Обсуждаем контроллеры компании Atmel.
Ответить

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сб авг 29, 2020 08:44:01

Первое что нужно. Читать ВНИМАТЕЛЬНО соответствующий раздел архитектуры. На каждый камень дополнительно читать даташит. Это базис. Отталкиваемся только от ЭТОГО. Если в студии соответствующий элемент периферии ведёт себя некорректно, учитывать это, и если требуется, ставить тестовые программные затычки. И да, если требуется, вручную делать телодвижения, где порт, где регистры ввода-вывода.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сб авг 29, 2020 08:56:11

Своё скромное мнение выскажу: добившись корректной работы в симуляторах можно не запустить в железе.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сб авг 29, 2020 09:39:18

ГЛАВНЫЙ СИМУЛЯТОР - МАКЕТ.
8)

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сб авг 29, 2020 10:09:40

Господа оппоненты. Я с AVR работаю с 2007 года. Не буду очередной срач поднимать про протеус. Его убираем. Кто желает, пусть протеус использует. Все эти годы я отлаживаю все свои проекты в студии. И я знаю о чем говорю. Так же я знаю, о чем я говорю, когда говорю: RTFM, мать вашу. Вы, новички теряете время, когда бежите на форумы, вместо того, чтобы прочитать книги по архитектуре МК AVR.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сб авг 29, 2020 13:46:28

Доброго времени суток!
Изучаю внешние прерывания. При прогоне программы в AVRStudio4 и эмуляции внешнего прерывания на выводе PB1(INT0) (прерывание настроено по изменению уровня) иногда программа уходит в прерывание и возвращается из прерывания однократно, а иногда прерывание обрабатывается два раза, при этом в дополнительно устанавливается бит INTF0 в активное состояние, который при первом заходе в прерывание не сбрасывается, а сбрасывается при втором заходе в прерывание. Подскажите, может у меня ошибка в коде где то? Что не так я делаю. Далее программа .Сама программа написана только для понимания работы прерывания
Спойлер.DEVICE ATtiny13
.INCLUDE "tn13def.inc"
.DSEG
.ESEG
.CSEG
;****** INTERRUPT VECTORS **************************************************
.ORG $000
RJMP RESET ; Reset Handler
.ORG $001
RJMP EXT_INT0 ; EXT_INT0 ; IRQ0 Handler
.ORG $002
RJMP PCINT_0 ; PCINT0 ; PCINT0 Handler

;****** INTERRUPTS **************************************************

;****** ВНЕШНЕЕ ПРЕРЫВАНИЕ **********************************
EXT_INT0:
NOP
RETI

PCINT_0:
NOP
RETI

RESET:
LDI R16, RAMEND
OUT SPL, R16

LDI R16, 0b00000001
OUT DDRB, R16
LDI R16, 0b00000001
OUT PORTB, R16


;***** ИНИЦИАЛИЗАЦИЯ ВНЕШНЕГО ПРЕРЫВАНИЯ *******************************************
;*** MCUCR – MCU Control Register Внешний регистр управления прерываниями A содержит биты управления для контроля значения прерываний
LDI R16, 0<<ISC01 | 1<<ISC00 ;00- прерывание по низкому уровню, 01- прерывание по логическому изменению, 10- прерывание по ниспадающему фронту, 11- прерывание по нарастающему фронту
OUT MCUCR, R16
;*** GIMSK– General Interrupt Mask Register Реестр масок прерываний
LDI R16, 1<<INT0 | 1<<PCIE ;- INT0: запрос внешнего прерывания 0 разрешен;- PCIE: разрешение прерывания при смене вывода
OUT GIMSK, R16
;*** GIFR – General Interrupt Flag Register Общий регистр флагов прерываний
LDI R16, 1<<INTF0 | 1<<PCIF ;INTF0: флаг внешнего прерывания; - PCIF: флаг прерывания смены вывода
OUT GIFR, R16
;*** PCMSK – Pin Change Mask Register
LDI R16, 0<<PCINT5 | 1<<PCINT4 | 1<<PCINT3| 1<<PCINT2| 0<<PCINT1| 0<<PCINT0
OUT PCMSK, R16


SEI ; глобально разрешаем прерывания


MAIN:
NOP
RJMP MAIN

Скриншот AVRStudio
СпойлерИзображение

Спасибо

При изучении внешних прерываний отталкивался исключительно от даташита и Евстифеева. Не думал что в Студии могут глюки появлятся при прогонке программы. А так как опыта у меня нет, то вновь и вновь искал косяк в коде и не находил.
Кстати проблему решил введением в обработчик прерывания сброса регистра INTF0 путем записи туда 1

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сб авг 29, 2020 15:51:29

Вообще-то непонятно. В ДШ про этот INTF0 открытым текстом сказано
The flag is cleared when the interrupt routine is executed.
Alternatively, the flag can be cleared by writing a logical one to it. This flag is always cleared when INT0 is configured as a level interrupt.
То же самое сказано про биты 0-5 регистра PCMSK.

О! Посмотрел внимательнее. Djonny, ты же разрешил ОБА прерывания, и INT0, и PCINT1 от несчастной ножки PB1. Не знаю, как реальное железо, а симулятор из-за этого, похоже, сходит с ума, одно прерывание отработал, про второе забыл. Не удивлюсь, если железо тоже сойдет с ума...

Или отдельно исследуй PCINTn и INT0 (разными программами), или исключи из PCINTn ножку PCINT1 (PB1).

Добавлено after 4 minutes 7 seconds:
Да, еще момент. То, что ты взял код под спойлер - это правиоьно, однако код под спойлером надо еще взять в [code][/code], тогда более-менее сохранится форматирование исходника, не будет той кучи, в которую и смотреть не хочется...

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сб авг 29, 2020 16:33:26

Djonny. Добро пожаловать.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Сб авг 29, 2020 19:23:03

Или отдельно исследуй PCINTn и INT0 (разными программами), или исключи из PCINTn ножку PCINT1 (PB1).

Добрый день,
Я бит PCINT1 (PB1) не задействовал, выставлен 0.
Код:
LDI R16, 0<<PCINT5 | 1<<PCINT4 | 1<<PCINT3| 1<<PCINT2| 0<<PCINT1| 0<<PCINT0


Да, еще момент. То, что ты взял код под спойлер - это правиоьно, однако код под спойлером надо еще взять в
Код:
, тогда более-менее сохранится форматирование исходника, не будет той кучи, в которую и смотреть не хочется...

Спасибо, учту.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Пн авг 31, 2020 13:54:31

Неоднократно замечал что при старте проекта в студии имеет место быть 1 холостое прерывание от внешних источников если они разрешены и если настроено на фронт или спад сразу после SEI. В железе его нет, но я делаю свои подпрограммы такими, что-б им это было до светодиода....

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Пн авг 31, 2020 17:27:50

Это говорит о том, что всегда нужно сначала настроить режим работы, и только потом включать прерывания. И сбрасывать флаги, если требуется. А на устройстве всегда учитывать такие особенности.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Вт сен 01, 2020 05:31:20

Djonny Попробуйте в студии simulator, который более приближён к железу, вместо simulator 2.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Вт сен 01, 2020 05:48:58

Да, но в нем не все МК.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Вт окт 06, 2020 20:56:29

Это говорит о том, что всегда нужно сначала настроить режим работы, и только потом включать прерывания. И сбрасывать флаги, если требуется. А на устройстве всегда учитывать такие особенности.


Я всегда так делал - однако факт остается фактом. Версию студии не помню уже. Но на симуляторе.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Ср окт 07, 2020 11:48:50

Wladimir_TS писал(а):Неоднократно замечал что при старте проекта в студии имеет место быть 1 холостое прерывание от внешних источников если они разрешены и если настроено на фронт или спад сразу после SEI. В железе его нет
когда ты в студии запустил симуляцию и курсор встал на нулевой адрес, сначала пины соответствующих входов нужно поставить в нужное состояние. тогда не будет возникать ложного запуска прерывания.
изначально студия на всех пинах всех портов ставит нули.
а в железе пины при запуске стоят в нужном состоянии, потому в железе ложного запуска прерывания при старте не происходит.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Ср окт 07, 2020 20:50:48

Видимо так оно и есть.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Вс окт 18, 2020 20:49:32

Всем привет!
В ходе изучения ASM в учебных целях решил сделать частотомер на 50 Гц. Принцип работы - измерение периода. Камень - atmega8, частота 8 МГц. Напяжение через диод и резисторный делитель подаю на ICP1. Для начала был написан такой код.
Спойлер
Код:
            
;=============
; Сегмент FLASH памяти
.CSEG
;=============
; Таблица прерываний
    .ORG 0x00        RJMP   RESET
    .ORG 0x05        RJMP   TIM1_CAPT         

;=============
; Прерывание по сбросу, стартовая инициализация
RESET:   
    ; Инициализация стека
    LDI    Temp1, LOW(RAMEND)
    OUT    SPL, Temp1
    LDI    Temp1, HIGH(RAMEND)   
    OUT    SPH, Temp1
    ; Очистка ОЗУ и регистров R0-R31
    LDI      ZL, LOW(SRAM_START)      ; Адрес начала ОЗУ в индекс
    LDI      ZH, HIGH(SRAM_START)
    CLR      Temp1            ; Очищаем R16
RAM_Flush:
    ST       Z+, Temp1            
    CPI      ZH, HIGH(RAMEND + 1)   
    BRNE   RAM_Flush         
    CPI      ZL, LOW(RAMEND + 1)   
    BRNE   RAM_Flush
    LDI      ZL, (0x1F-2)         ; Адрес регистра R29
    CLR      ZH
Reg_Flush:
    ST      Z, ZH
    DEC      ZL
    BRNE   Reg_Flush
    CLR      ZL
    CLR      ZH
    ; Регистры и SRAM полностью очищены (обнулены)
    ; Но регистры ввода-вывода (IO) НЕОБХОДИМО очищать
    ; Глобальный запрет прерываний
    CLI

        ;настройка порта D
   ldi Temp1, 0xFF // порт D на выход
   out DDRD, Temp1
   
   
        ;настройка порта B
   cbi   DDRB,0    ; ICP1 на вход
   
   ;настройка таймера1
   ldi Temp1, 0b00000000
   out TCCR1A, Temp1
   ldi Temp1, 0b01000011
   out TCCR1B, Temp1
   ldi Temp1, 0b00100000
   out TIMSK, Temp1
   out TIFR, Temp1

      
   SEI ;разрешаем прерывания
;***************************************************
LOOP:
   


rjmp   LOOP

;==============
TIM1_CAPT:            ;обработчик прерывания по захвату таймера 1
   
   SBIC PORTD,EXT
   rjmp   metka
   SBI  PORTD,EXT ;установить бит
   rjmp   EXIT
   metka:
   CBI  PORTD,EXT ;установить бит
EXIT:
RETI                 



Подключенный к PD0 логический анализатор выдал красивую картинку
Изображение
Следующий этап - получение данных с регистра захвата. В качестве индикатора применил модуль из 4 семисегментных индикаторов на TM1637. Что-бы число импульсов влазило в индикатор, решил ограничится 1 байтом. По сему коэфициент предделителя выбрал 1024, должно получаться что-то около 156. Вывод на TM1637 уже давно отработан и в коде не приводится.
Спойлер
Код:
            
;=============
; Сегмент FLASH памяти
.CSEG
;=============
; Таблица прерываний
    .ORG 0x00        RJMP   RESET
    .ORG 0x05        RJMP   TIM1_CAPT         



;=============
; Прерывание по сбросу, стартовая инициализация
RESET:   
    ; Инициализация стека
    LDI    Temp1, LOW(RAMEND)
    OUT    SPL, Temp1
    LDI    Temp1, HIGH(RAMEND)   
    OUT    SPH, Temp1
    ; Очистка ОЗУ и регистров R0-R31
    LDI      ZL, LOW(SRAM_START)      ; Адрес начала ОЗУ в индекс
    LDI      ZH, HIGH(SRAM_START)
    CLR      Temp1            ; Очищаем R16
RAM_Flush:
    ST       Z+, Temp1            
    CPI      ZH, HIGH(RAMEND + 1)   
    BRNE   RAM_Flush         
    CPI      ZL, LOW(RAMEND + 1)   
    BRNE   RAM_Flush
    LDI      ZL, (0x1F-2)         ; Адрес регистра R29
    CLR      ZH
Reg_Flush:
    ST      Z, ZH
    DEC      ZL
    BRNE   Reg_Flush
    CLR      ZL
    CLR      ZH
    ; Регистры и SRAM полностью очищены (обнулены)
    ; Но регистры ввода-вывода (IO) НЕОБХОДИМО очищать
    ; Глобальный запрет прерываний
    CLI
   
   ;настройка порта D
   ldi Temp1, 0xFF // Порт D на выход
   out DDRD, Temp1

   ;настройка порта B
   cbi   DDRB,0    ; ICP1 на вход
   
   ;настройка таймера1
   ldi Temp1, 0b00000000
   out TCCR1A, Temp1
   ldi Temp1, 0b01000101
   out TCCR1B, Temp1
   ldi Temp1, 0b00100000
   out TIMSK, Temp1
   out TIFR, Temp1


; ===========

   
   CLT  ;сбрасываем бит Т
   ORI R30, 0b00000001   ;устанасливаем бит 0 R30 в 1
   
   SEI ;разрешаем прерывания
;***************************************************
LOOP:
   
   BRTC    LOOP          ;если Т=0 не делаем ничего
   CLI                   ;запрещаем прерывания

;***************************************************

; вывод значения Temp1 на TM1637

;***************************************************


; ===========
;    delay loop generator
;     8000000 cycles:
; ----------
; delaying 7999992 cycles:
          ldi  R17, $48
WGLOOP00:  ldi  R18, $BC
WGLOOP1:  ldi  R19, $C4
WGLOOP2:  dec  R19
          brne WGLOOP2
          dec  R18
          brne WGLOOP1
          dec  R17
          brne WGLOOP00
; ----------
; delaying 6 cycles:
          ldi  R17, $02
WGLOOP3:  dec  R17
          brne WGLOOP3
; ----------
; delaying 2 cycles:
          nop
          nop
; ===========

   SEI ;разрешаем прерывания
   CLT  ;сбрасываем бит Т

rjmp   LOOP

;==============
TIM1_CAPT:            ;обработчик прерывания по захвату таймера 1
        SBRS R30,0   
   rjmp ZAHVAT
   CLR Temp1         ;обнуляем счётчмк 
   out TCNT1H,Temp1
   out TCNT1L,Temp1
   ANDI R30, 0b11111110   ;устанасливаем бит 0 R30 в 0
   rjmp EXT
ZAHVAT:
   in Temp1,ICR1L    ;копируем значение регистра захвата 
        in Temp2,ICR1H
   ORI R30, 0b00000001   ;устанасливаем бит 0 R30 в 1
        SET  ;устанавливаем бит Т

EXT:
RETI                 



В кратце логика такая. До первого прерывания ничего не делаем. По первому прерыванию сбрасываем TCNT1 в ноль, по второму забираем в Temp1 младший байт регистра захвата и разрешаем его вывод на индикатор. Перед выводом запрещаем прерывания, выводим значение Temp1. Задержка 1 сек. Разрешаем прерывания и всё по новой. В теории красиво, а на деле на индикаторе бегают цифры от 3 до 200.
Где косяк?

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Пн окт 19, 2020 03:53:02

Неполная очистка таймера, т.к. задействован предделитель
Код:
TIM1_CAPT:            ;обработчик прерывания по захвату таймера 1
        SBRS R30,0   
   rjmp ZAHVAT
   CLR Temp1         ;обнуляем счётчмк
   out TCNT1H,Temp1
   out TCNT1L,Temp1
;================
   LDI   R16,1<<PSR10
   OUT   SFIOR,R16   ; обнулить предделитель таймера
;================

   ANDI R30, 0b11111110   ;устанасливаем бит 0 R30 в 0
   rjmp EXT
ZAHVAT:
   in Temp1,ICR1L    ;копируем значение регистра захвата
        in Temp2,ICR1H
   ORI R30, 0b00000001   ;устанасливаем бит 0 R30 в 1
        SET  ;устанавливаем бит Т

EXT:
RETI

Таймер при выходе из прерывания по окончании измерения не останавливается. За время вывода на индикацию и задержки входной сигнал на PB0/ICP1 установит флаг ICF1, т.е. при переходе на LOOP прерывание вызовется сразу, а не по фронту входного сигнала.
Спойлер
Код:
 .include "m8def.inc"   ; ATMega8

.equ   Fo=8000000

.def   temp1=R16
.def   temp2=R17
           
;=============
; Сегмент FLASH памяти
.CSEG
;=============
; Таблица прерываний
.ORG 0x00
   RJMP   RESET
.ORG 0x05   ;     RJMP   TIM1_CAPT         
TIM1_CAPT:            ;обработчик прерывания по захвату таймера 1
   SBRS R30,0   
   rjmp ZAHVAT
   OUT   TCCR1B,R22
;   CLR Temp1         ;обнуляем счётчмк
;   out TCNT1H,Temp1
;   out TCNT1L,Temp1
   CBR   R30,1<<0
;   ANDI R30, 0b11111110   ;устанасливаем бит 0 R30 в 0
   rjmp EXT
ZAHVAT:
   in Temp1,ICR1L    ;копируем значение регистра захвата
    in Temp2,ICR1H
   SBR   R30,1<<0
;   ORI R30, 0b00000001   ;устанасливаем бит 0 R30 в 1

   ldi Temp1,0b01000000
   out TCCR1B, Temp1
;   ldi Temp1, 0b01000101
;   out TCCR1B, Temp1

   ldi Temp1, 0b00100000
   out TIMSK, Temp1
   out TIFR, Temp1

   LDI   R16,1<<PSR10
   OUT   SFIOR,R16
        SET  ;устанавливаем бит Т
EXT:
RETI



;=============
; Прерывание по сбросу, стартовая инициализация
RESET:   
    ; Инициализация стека
    LDI    Temp1, LOW(RAMEND)
    OUT    SPL, Temp1
    LDI    Temp1, HIGH(RAMEND)   
    OUT    SPH, Temp1
    ; Очистка ОЗУ и регистров R0-R31
    CLI
   
   ;настройка порта D
   ldi Temp1, 0xFF // Порт D на выход
   out DDRD, Temp1

   ;настройка порта B
   cbi   DDRB,0    ; ICP1 на вход
   
   ;настройка таймера1
   ldi Temp1, 0b00000000
   out TCCR1A, Temp1

   ldi Temp1,0b01000000
   out TCCR1B, Temp1
;   ldi Temp1, 0b01000101
;   out TCCR1B, Temp1

   ldi Temp1, 0b00100000
   out TIMSK, Temp1
   out TIFR, Temp1

   LDI   R16,1<<PSR10
   OUT   SFIOR,R16
; ===========
   CLT  ;сбрасываем бит Т
   SBR   R30,1<<0
;   ORI R30, 0b00000001   ;устанасливаем бит 0 R30 в 1
   LDI   R22,0b01000101
   SEI ;разрешаем прерывания
;***************************************************
LOOP:
   
   BRTC    LOOP          ;если Т=0 не делаем ничего
   CLI                   ;запрещаем прерывания

;***************************************************

; вывод значения Temp1 на TM1637

;***************************************************


; ===========
;    delay loop generator
;     8000000 cycles:
; ----------
; delaying 7999992 cycles:
          ldi  R17, $48
WGLOOP00:  ldi  R18, $BC
WGLOOP1:  ldi  R19, $C4
WGLOOP2:  dec  R19
          brne WGLOOP2
          dec  R18
          brne WGLOOP1
          dec  R17
          brne WGLOOP00
; ----------
; delaying 6 cycles:
          ldi  R17, $02
WGLOOP3:  dec  R17
          brne WGLOOP3
; ----------
; delaying 2 cycles:
          nop
          nop
; ===========

;   SEI ;разрешаем прерывания
;   CLT  ;сбрасываем бит Т

   rjmp   RESET
;rjmp   LOOP


;==============
.EXIT
Последний раз редактировалось akl Пн окт 19, 2020 05:31:45, всего редактировалось 1 раз.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Пн окт 19, 2020 05:19:13

Замечание по комментариям. Комментарии должны быть информативными, нести смысловую нагрузку. А не информировать об очевидном.
Пример:
Код:
sbi LED_PORT, LED // Включение светодиода

Это очевидно. Масло масляное. Указывайте в комментариях непростые, неочевидные вещи. Алгоритм, пояснения. То, о чем другим будет непонятно, и что вы забудете через месяц.

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Пн окт 19, 2020 06:38:02

Далеко не очевидно.
Например, светик подключен через резистор анодом на плюс. Или для камня с переключением состояния через PIN
Код:
;.equ   LED_PORT=PORTD
.equ   LED_PORT=PIND
.equ   LED=0

   SER   ZH
   OUT   DDRD,ZH

   SBI   LED_PORT,LED ; переключить состояние светика

Re: Ассемблер (ASM) для AVR в вопросах и ответах

Пн окт 19, 2020 12:31:49

Неполная очистка таймера, т.к. задействован предделитель
...............
Таймер при выходе из прерывания по окончании измерения не останавливается. За время вывода на индикацию и задержки входной сигнал на PB0/ICP1 установит флаг ICF1, т.е. при переходе на LOOP прерывание вызовется сразу, а не по фронту входного сигнала.


Предделитель сильно не повлияет. Плюс-минус один такт. А по второму согласен на все сто. Пока будем выводить значения и висеть в задержке флаг ICF1 будет установлен и первое прерывание произойдёт не по фронту а в момент разрешения прерывания. Попробовал решить вопрос в лоб, сбрасывать ICF1 в конце LOOP перед установкой бита I. Показания немного стабилизировались. Буду разбираться с вашим кодом. Спасибо за совет.

Добавлено after 8 minutes 46 seconds:
Ещё вопрос в догонку. Установив биты предделителя CS12 – CS10 в "000" мы останавливаем таймер. При этом TCNT1 обнуляется? Если опять запустить счёт он продолжится со значения в момент остановки или начнётся с нуля?
Ответить