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

Обсуждаем контроллеры компании Atmel.
Аватара пользователя
dr.doc
Это не хвост, это антенна
Сообщения: 1368
Зарегистрирован: Вс мар 28, 2010 12:52:22
Откуда: Беларусь

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

Сообщение dr.doc »

Термометр в proteus работает и именно со 2-м и 0-м байтами. В спойлере участок кода приемника. Частота - 8 МГц.
Спойлерreceive:
/*
Операция чтения бита: Вывод микроконтроллера устанавливается
в режим выхода и на нем устанавливается логический ноль.
Выдерживается определенная пауза, вывод переводится в режим
входа в состоянии Hi-z, выдерживается пауза, а затем микроконтроллер
считывает потенциал вывода.
*/
ldi r19,8 ; Count bit
ldi r18,9 ; Count byte
clr r16 ; Очистим регистр приемника
receive_in:
; Выдадим старт-бит
rcall init_out
cbi Dataport,DATA ; Clear pin
rcall Delay_3uS ; Start slot
rcall init_in ; Вывод на вход
rcall Delay_20uS ; Прочитаем значение бита:
clc ; Clear Carry
sbic Datapin,DATA ; Пропустим команду при лог. 0
sec ; Установим Carry в 1
ror r16 ; Carry в бит 7

rcall Delay_60uS ; Ждем окончания слота времени

dec r19
brne receive_in ; next bit
; Байт прочитан. Запишем его в ячейку
cpi r18,9
brne _8_1
sts rec+0,r16
_8_1:
cpi r18,8
brne _7_1
sts rec+1,r16
_7_1:
cpi r18,7
brne _6_1
sts rec+2,r16
_6_1:
cpi r18,6
brne _5_1
sts rec+3,r16
_5_1:
cpi r18,5
brne _4_1
sts rec+4,r16
_4_1:
cpi r18,4
brne _3_1
sts rec+5,r16
_3_1:
cpi r18,3
brne _2_1
sts rec+6,r16
_2_1:
cpi r18,2
brne _1_1
sts rec+7,r16
_1_1:
cpi r18,1
brne _0_1
sts rec+8,r16
_0_1:
clr r16 ; Очистим приемник

dec r18
cpi r18,0
brne receive_in
ret

Вот - дальнейшая обработка:
Спойлер; Для корректного отображения нужно поделить значение на 16
; Но, сначала определимся со знаком

lds r16,rec+0
lds r17,rec+2
sbrs r17,6
rjmp positiv

Появится время - проверю код в железе и отпишусь. Пока нет индикатора, поэтому придется передать данные на комп...
«Еще я хотел бы, чтобы наши ученые изобрели какой-то новый источник энергии, чтобы мы на коленях не ползали даже перед нашими братьями, умоляя их и выпрашивая тонну нефти или кубометр газа», — рассказал белорусский президент.
Аватара пользователя
Gudd-Head
Друг Кота
Сообщения: 20091
Зарегистрирован: Чт сен 18, 2008 12:27:21
Откуда: Столица Мира Санкт-Петербург

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

Сообщение Gudd-Head »

dr.doc писал(а):Пока нет индикатора

Тут камрад как-то давно (может даже в статьях) выкладывал код (?) с выводом трёх (?) значащих цифр (напряжение свинцового аккума вроде) на один светодиод :idea:
Количество длинных миганий — десятки, мигания покороче — единицы и совсем короткие — десятые доли вольта. :)
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Alexeyslav
Друг Кота
Сообщения: 4550
Зарегистрирован: Чт май 05, 2011 21:26:34
Откуда: Украина, Славутич
Контактная информация:

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

Сообщение Alexeyslav »

Тогдауж, морзянкой... так можно тексты целые выводить.
Аватара пользователя
Gudd-Head
Друг Кота
Сообщения: 20091
Зарегистрирован: Чт сен 18, 2008 12:27:21
Откуда: Столица Мира Санкт-Петербург

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

Сообщение Gudd-Head »

Alexeyslav писал(а):Тогдауж, морзянкой...

Это для совсем тру радиолюбителей :)))
[ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ]
Аватара пользователя
Jack_A
Друг Кота
Сообщения: 6307
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

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

Сообщение Jack_A »

Alexeyslav писал(а):Тогдауж, морзянкой... так можно тексты целые выводить.

Причем на слух :) "ЗА ЗАЙ-ца-ми" - так на слух нас учили принимать букву 'З' : _ _ . .
Аватара пользователя
dr.doc
Это не хвост, это антенна
Сообщения: 1368
Зарегистрирован: Вс мар 28, 2010 12:52:22
Откуда: Беларусь

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

Сообщение dr.doc »

Да хватит стебаться. Написать вывод на семисегментник, по кнопке в порт, по UART в комп не проблема. Проблема, что на железо пока нет времени.
«Еще я хотел бы, чтобы наши ученые изобрели какой-то новый источник энергии, чтобы мы на коленях не ползали даже перед нашими братьями, умоляя их и выпрашивая тонну нефти или кубометр газа», — рассказал белорусский президент.
Аватара пользователя
B@R5uk
Собутыльник Кота
Сообщения: 2896
Зарегистрирован: Сб ноя 13, 2010 12:53:25
Откуда: приходит весна?

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

Сообщение B@R5uk »

Подскажите, пожалуйста, как правильно использовать ветвление в макросах?
Аватара пользователя
Kavka
Мудрый кот
Сообщения: 1810
Зарегистрирован: Чт июн 10, 2010 08:55:35
Откуда: Сибирские Афины

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

Сообщение Kavka »

Как обычно - делаешь метку в тексте макроса и JMP/BRxx на неё из кода макроса.
Когда уже ничего не помогает - прочтите, наконец, инструкцию.
Лучший оптимизатор находится у вас между ушей. (Майкл Абраш, программист Quake и QuakeII)
Избыток информации ведёт к оскудению души - Леонтьев А. (сказано в 1965 г.)
Аватара пользователя
B@R5uk
Собутыльник Кота
Сообщения: 2896
Зарегистрирован: Сб ноя 13, 2010 12:53:25
Откуда: приходит весна?

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

Сообщение B@R5uk »

А при многократном использовании макроса проблем не возникнет? Как компилятор разрешает дублирование меток в этом случае?
Аватара пользователя
Kavka
Мудрый кот
Сообщения: 1810
Зарегистрирован: Чт июн 10, 2010 08:55:35
Откуда: Сибирские Афины

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

Сообщение Kavka »

Нет, проблем не возникает. Макрос обрабатывается особым образом. Метки объявленные в макросе видны только в коде макроса. Соответственно, можно делать одинаковые метки в разных макросах, и метку в макросе одинаковую с меткой в основной программе и это не будет вызывать ошибок и будет работать как надо. Для AvrAssembler2, который в 4ой, 5ой и 6ой Студии, вроде как, тут уже это обсуждалось и на практике подтверждали.
Когда уже ничего не помогает - прочтите, наконец, инструкцию.
Лучший оптимизатор находится у вас между ушей. (Майкл Абраш, программист Quake и QuakeII)
Избыток информации ведёт к оскудению души - Леонтьев А. (сказано в 1965 г.)
nirq
Опытный кот
Сообщения: 758
Зарегистрирован: Вс фев 10, 2013 15:26:00

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

Сообщение nirq »

А скачать AVR Studio, установить, скомпилировать и посмотреть на файл *.lst?
Микросхема не знает никаких меток, у неё только инструкции, машинные коды. А в машинных кодах переходы выполняются как "перейти на адрес 1234 на плюс Х адресов или на минус Х адресов от самой команды перехода" - т.е. при компиляции выражение BRNE ERROR будет понято как BRNE ERROR-PC, где ERROR-PC = автоматически рассчитанная компилятором и подставленная в машинный код константа.
Или смысл в общении.
Аватара пользователя
B@R5uk
Собутыльник Кота
Сообщения: 2896
Зарегистрирован: Сб ноя 13, 2010 12:53:25
Откуда: приходит весна?

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

Сообщение B@R5uk »

Kavka, спасибо за подробный ответ. Вы развеяли все мои опасения.
nirq писал(а):Или смысл в общении.
Да. Изображение
Аватара пользователя
ANALOG
Мучитель микросхем
Сообщения: 444
Зарегистрирован: Вс ноя 28, 2010 15:18:52
Откуда: Минск

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

Сообщение ANALOG »

Сегодня столкнулся с таким багом при использовании avra на Linux:
Компилятор не проверяет значение параметров, передаваемых командам in/out lds/sts
Так, например, вот этот код (tiny2313)

Код: Выделить всё

sts TIMSK, R16

не вызвал у компилятора никаких подозрений, хотя программа, естественно, не работала. (TIMSK = 0x39, что меньше, чем 0x3F, так что тут нужен out) ушел почти день на отловку этого бага. :evil:
Пользуюсь avra уже давно, но на эту штуку наткнулся в первый раз, т.к. обычно использую макрос, который сам подстаяляет нужный out или sts, а тут понадобилось сделать вручную.
Ну зато пока ловил этот баг, повычищал прогу от всех остальных :)
Такие дела. :tea:
Alexeyslav
Друг Кота
Сообщения: 4550
Зарегистрирован: Чт май 05, 2011 21:26:34
Откуда: Украина, Славутич
Контактная информация:

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

Сообщение Alexeyslav »

а с чего это он должен проверять? константа имеет допустимое для STS значение... просто данные тулишь в какую-то ячейку памяти.
Аватара пользователя
ANALOG
Мучитель микросхем
Сообщения: 444
Зарегистрирован: Вс ноя 28, 2010 15:18:52
Откуда: Минск

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

Сообщение ANALOG »

А, да, это я туплю :oops:
а уже компилятор заподозрил :roll:

Надо было все-таки все макросами писать :)
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

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

Сообщение ARV »

ANALOG писал(а): TIMSK = 0x39, что меньше, чем 0x3F, так что тут нужен out
можно и STS, но с другим адресом, естественно
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Аватара пользователя
НАПАЛМ
Это не хвост, это антенна
Сообщения: 1314
Зарегистрирован: Пт ноя 27, 2009 19:47:13
Откуда: Казань

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

Сообщение НАПАЛМ »

Здравствуйте.
Изучаю АЦП, камень - ATmega88PA. К 0-евому каналу подключаю переменник.
Сам код:
Спойлер

Код: Выделить всё

.include "m88PAdef.inc" ; Используем ATmega88PA
.include "macrobaselib.inc" ; Макроопределения

 
;======== RAM ============
      .DSEG         ; Сегмент ОЗУ
     
StrPtr:      .byte   2
UCSR0B_temp: .byte   1
        .equ MAXBUFF_IN    =   10   ; Размер в байтах
      .equ MAXBUFF_OUT    =    10
 
IN_buff:   .byte   MAXBUFF_IN   ; Буфер приема
IN_PTR_S:   .byte   1      ; Указатель начала
IN_PTR_E:   .byte   1      ; Указатель конца
IN_FULL:   .byte   1      ; Флаг переполнения
 
OUT_buff:   .byte   MAXBUFF_OUT   ; Буфер передачи
OUT_PTR_S:   .byte   1      ; Указатель начала
OUT_PTR_E:   .byte   1      ; Указатель конца
OUT_FULL:   .byte   1      ; Флаг переполнения.

ADC_state:  .byte 1 ;Состояние АЦП (вкл/выкл)
RX_sel:      .byte 1   ;Переменная состояния отправки данных
ADCH_sel:   .byte 1   ;Переменная текущего канала АЦП
ADCCH:      .byte 8   ;Восемь байт под хранение результатов АЦП. По байту на канал.
 
;======= FLASH ===========
      .CSEG         ; Кодовый сегмент

.ORG 0x0000        ; (RESET)
         RJMP   Reset
.include "ivectors.inc" // Подключим файл с таблицой прерываний
;----------
; Это обработчик прерывания. Тут, на просторе, можно наворотить сколько
; угодно кода.

RX_OK:   
   
        PUSHF            ; Макрос, пихающий в стек SREG и R16
      PUSH   R17
      PUSH   R18
      PUSH   XL
      PUSH   XH
 
      LDI   XL,low(IN_buff)      ; Берем адрес начала буффера
      LDI   XH,high(IN_buff)
      LDS   R16,IN_PTR_E      ; Берем смещение точки записи
      LDS   R18,IN_PTR_S      ; Берем смещение точки чтения
 
      ADD   XL,R16         ; Сложением адреса со смещением
      CLR   R17             ; получаем адрес точки записи
      ADC   XH,R17
 
      LDS   R17,UDR0      ; Забираем данные
      ST   X,R17         ; сохраняем их в кольцо
 
      INC   R16         ; Увеличиваем смещение
 
      CPI   R16,MAXBUFF_IN      ; Если достигли конца
      BRNE   NoEnd
      CLR   R16         ; переставляем на начало
 
NoEnd:   
        CP   R16,R18         ; Дошли до непрочитанных данных?
      BRNE   RX_OUT      ; Если нет, то просто выходим
 
 
RX_FULL:
       LDI   R18,1         ; Если да, то буффер переполнен.
      STS   IN_FULL,R18      ; Записываем флаг наполненности
 
RX_OUT:      
        STS   IN_PTR_E,R16   ; Сохраняем смещение. Выходим
       

      POP   XH
      POP   XL
      POP   R18
      POP   R17
      POPF            ; Достаем SREG и R16

      RETI

;***********************************
TX_OK:
        RETI
;***********************************

UD_OK:      
        PUSHF         ; Пихаем в стек SPEG, R16, R17, R18, R19
      PUSH   R17    ; и пару XL:XH   
      PUSH   R18
      PUSH   R19
      PUSH   XL
      PUSH   XH
 
 
      LDI   XL,low(OUT_buff)   ; Берем адрес начала буффера
      LDI   XH,high(OUT_buff)
      LDS   R16,OUT_PTR_E      ; Берем смещение точки записи
      LDS   R18,OUT_PTR_S      ; Берем смещение точки чтения         
      LDS   R19,OUT_FULL      ; Берем флаг переполнения
 
      CPI   R19,1         ; Если буффер переполнен, то указатель начала
      BREQ   NeedSend   ; Равен указателю конца. Это надо учесть.
 
      CP   R18,R16         ; Указатель чтения достиг указателя записи?
      BRNE   NeedSend   ; Нет! Буффер не пуст. Надо слать дальше
 
      LDI    R16,(1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0)|(0<<UDRIE0)
      STS    UCSR0B, R16      ; Запрет прерывания По пустому UDR
      RJMP   TX_OUT         ; Выходим
 
NeedSend:   
        CLR   R17             ; Получаем ноль
      STS   OUT_FULL,R17   ; Сбрасываем флаг переполнения
 
      ADD   XL,R18         ; Сложением адреса со смещением
      ADC   XH,R17         ; получаем адрес точки чтения
 
      LD   R17,X         ; Берем байт из буффера
      STS   UDR0,R17      ; Отправляем его в USART
 
      INC   R18         ; Увеличиваем смещение указателя чтения
 
      CPI   R18,MAXBUFF_OUT      ; Достигли конца кольца?
      BRNE   TX_OUT         ; Нет?
 
      CLR   R18         ; Да? Сбрасываем, переставляя на 0
 
TX_OUT:      
        STS   OUT_PTR_S,R18      ; Сохраняем указатель
 
      POP   XH
      POP   XL
      POP   R19
      POP   R18
      POP   R17
      POPF            ; Выходим, достав все из стека

      RETI
;**************************************
ADC_OK:
        PUSHF
      PUSH   ZL
      PUSH   ZH
      PUSH   R17
 
      LDS    R16,ADMUX ;Берем ADMUX
      ANDI R16,0b0001111 ;Маской отрезаем лишние биты. Получаем номер канала
                      ;С которого было снято измерение.
 
      ;MOV   R17,R16      ; Cохранили копию номера канала         
 
      LDI   ZL,low(ADCCH)   ; Берем адрес начала массива с будущими данными.
      LDI   ZH,High(ADCCH)
 
      ADD   ZL,R16      ; Прибавляем к адресу наш номер канала.
               ; Если было переполнение, то будет флаг С
      CLR   R16      ; Флаг важен, а значение в R16 уже нет. Но нам нужен ноль
               ; Возьмем и сделаем его из R16.
      ADC   ZH,R16   ; Сложим флаг возможного переполнения с ZH
               ; Т.о. у нас получается в Z = адрес (ADCCH+номер канала)
               ; И значения из разных каналов ложатся в разные переменные массива
               ; с адресом базы ADCCH
 
      LDS   R16,ADCL   ; Младшее значение нам не надо. Но считать его нужно.
      LDS   R16,ADCH   ; Берем в R16 значение из АЦП            
      ST   Z,R16      ; Сохраняем его в массив по нужному адресу
               
 
        /*
      LDS   R16,ADMUX   ; Опять взяли ADMUX
      ANDI   R16,0xF8   ; На этот раз обнулили номер канала. Оставив остальные биты нетронутыми
 
      INC   R17      ; Увеличили на 1 заныченный ранее номер канала
      ANDI   R17,0x07   ; Обрезали лишние биты, чтобы не было переполнения.
               ; Число по маске 0х07 в принципе не может быть больше 7.
               ; А каналов у нас от 0 до 7. То что надо.
 
      OR   R16,R17      ; Слепили старое значение из ADMUX c новым значением номера
      OUT   ADMUX,R16   ; Канала. Т.е. по факту сделали MUX = MUX+1 выбрав следующий канал
               ; Спихнули его в регистр ADMUX. Все, следующий канал выбран. Можно запускать
               ; Следующее преобразование. */
 
      OUTI   ADCSRA,(1<<ADEN)|(1<<ADIE)|(1<<ADSC)|(0<<ADATE)|(3<<ADPS0)   ; Запустили
 
      POP   R17      ; Корректный выход из прерывания.
      POP   ZH
      POP   ZL
      POPF
      RETI


;----------
 // Теперь инициализируем всё, что нам нужно.
 
Reset:
 STACKINIT // Инициализация стека
 RAMFLUSH  // Очистка ОЗУ
 GPRFLUSH  // Очистка РОН (Регистров Общего Назначения)


.equ    XTAL = 16000000   
.equ    baudrate = 9600 
.equ    bauddivider = XTAL/(16*baudrate)-1
 
 
 //Инициализация USART
   LDI    R16, low(bauddivider)    ;Записываем в UBRR0H:UBRR0L
   STS    UBRR0L,R16               ;делитель для настройки скорости передачи
   LDI    R16, high(bauddivider)
   STS    UBRR0H,R16
 
   LDI    R16, 0
   STS    UCSR0A, R16
 
; Прерывания прием-передачи разрешены, прием-передача разрешена
   LDI    R16, (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0)|(0<<UDRIE0)
   STS    UCSR0B, R16   
 
; Формат кадра - 8 бит
   LDI    R16, (1<<UCSZ00)|(1<<UCSZ01)
   STS    UCSR0C, R16
   

 //Инициализация АЦП   
   OUTI   ADMUX,(1<<REFS0)|(1<<ADLAR)|(0<<MUX0)
   ;Опорное напряжение с AVCC, выравнивание влево, канал 0.


 //Мигание светодиодом для зрительного контроля рестарта микроконтроллера
   OUTI    DDRB,  0b11111101 //Инициализация порта B, PB0 - на выход
   RCALL LED_flashing
   
   SEI // Глобально разрешаем прерывания


Main:
   
   rcall Delay
   rcall Delay
   RCALL Buff_Pop ; Вызываем подпрограмму чтения из буфера приема USART
                  ; В R19 - код ошибки (1 - пустой буфер), R17 - данные.
   CPI R19, 1     ; Проверяем, пустой ли буфер.
   BREQ M1        ; Если буфер пуст, то идем на М1.

   RCALL Processing_of_external_commands
                  ; Процедура обработки команд, принимаемых по USART
      

               
M1:

   LDS R16, ADC_state
   SBRS R16, 0 ; Если АЦП запущен, то с него нужно отправить данные по USART
   rjmp M2 ; А если не запущен, то идем дальше

   LDS R19, ADCCH  ; Берем данные с 0-ого канала АЦП
   RCALL Buff_Push ; Кладем их в буфер
   LDI    R16,(1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0)|(1<<UDRIE0)
   STS    UCSR0B, R16      ; Разрешаем прерывания По пустому UDR
M2:

   rjmp Main 






        /*----------
        | Начало сектора подпрограмм и функций |
         ----------*/ 


;---------- обработки команд внешнего управления-------*

Processing_of_external_commands:

   cpi R17, '0' ;Сравниваем R17 с нулем
   BREQ I1  ;Если пришел нуль, то гасим светодиод

   cpi R17, '1' ;Сравниваем R17 с единицей
   BREQ I2  ;Если пришла единица - зажигаем

   cpi R17, 'G' ;Сравниваем R17 c 'G'
   BREQ I3    ;Запускаем АЦП - Go

   cpi R17, 'S' ;Сравниваем R17 c 'S'
   BREQ I4    ;Выключаем АЦП - Stop

   rjmp Processing_of_external_commands_EXIT

I1:
   cbi  portb,0  ;Гасим светодиод
   rjmp Processing_of_external_commands_EXIT   
I2:
   sbi  portb,0  ;Зажигаем светодиод
   rjmp Processing_of_external_commands_EXIT
I3:
   cli
   LDI R16, 0b00000001
   STS ADC_state, R16
   sei
   OUTI   ADCSRA,(1<<ADEN)|(1<<ADIE)|(1<<ADSC)|(0<<ADATE)|(3<<ADPS0)
   
   rjmp Processing_of_external_commands_EXIT
I4:
   cli
   LDI R16, 0b00000000
   STS ADC_state, R16
   sei
   OUTI   ADCSRA,(0<<ADEN)|(0<<ADIE)|(0<<ADSC)|(0<<ADATE)|(3<<ADPS0)
   

Processing_of_external_commands_EXIT:   
 RET
;---------- подпрограммы-----------------------------------

               
;---------- задержки [Delay]----------*
Delay:
   LDI R20,0   

Loop:
   dec R20
   BRNE Loop

   dec R21
   BRNE Loop
     
   ret
;---------- подпрограммы-----------------------------------


;--------Подпрограмма чтения из буфера USART---------------------------*
; OUT:    R17 - Data,
;      R19 - ERROR CODE
Buff_Pop:
        LDS R16, UCSR0B      ; Берем в R16 значение UCSR0B
      STS UCSR0B_temp, R16 ; Сажаем предыдущее значение UCSR0B в ОЗУ 
        LDI R16, (0<<RXCIE0)|(0<<TXCIE0)|(0<<UDRIE0)
        STS UCSR0B, R16      ; Запрещаем прерывания от UART
                             ; для соблюдения атомарности.

      LDI   XL,low(IN_buff)    ; Берем адрес начала буффера
      LDI   XH,high(IN_buff)
      LDS   R16,IN_PTR_E    ; Берем смещение точки записи
      LDS   R18,IN_PTR_S    ; Берем смещение точки чтения         
      LDS   R19,IN_FULL       ; Берм флаг переполнения
 
      CPI   R19,1          ; Если буффер переполнен, то указатель начала
      BREQ   NeedPop       ; Равен указателю конца. Это надо учесть.
 
      CP   R18,R16          ; Указатель чтения достиг указателя записи?
      BRNE   NeedPop       ; Нет! Буффер не пуст. Работаем дальше
 
      LDI   R19,1          ; Код ошибки - пустой буффер!
 
      RJMP   _TX_OUT       ; Выходим
 
NeedPop:
       CLR   R17             ; Получаем ноль
      STS   IN_FULL,R17      ; Сбрасываем флаг переполнения
 
      ADD   XL,R18         ; Сложением адреса со смещением
      ADC   XH,R17         ; получаем адрес точки чтения
 
      LD   R17,X      ; Берем байт из буффера
      CLR   R19         ; Сброс кода ошибки
 
      INC   R18         ; Увеличиваем смещение указателя чтения
 
      CPI   R18,MAXBUFF_OUT      ; Достигли конца кольца?
      BRNE   _TX_OUT         ; Нет?
 
      CLR   R18         ; Да? Сбрасываем, переставляя на 0
 
_TX_OUT:   
        STS   IN_PTR_S,R18      ; Сохраняем указатель

      LDS R16, UCSR0B_temp   ;Загружаем в R16 предыдущее состояние UCSR0B
        STS UCSR0B, R16         ;Возвращаем обратно прерывания по USART

      RET
;---------- подпрограммы-----------------------------------------


;--------Подпрограмма записи в буфер USART-----------------------------*
; IN R19    - DATA
; OUT R19    - ERROR CODE
Buff_Push:
        PUSH ZL
      PUSH ZH
        LDS R16, UCSR0B      ; Берем в R16 значение UCSR0B
      STS UCSR0B_temp, R16 ; Сажаем предыдущее значение UCSR0B в ОЗУ 
        LDI R16, (0<<RXCIE0)|(0<<TXCIE0)|(0<<UDRIE0)
        STS UCSR0B, R16      ; Запрещаем прерывания от UART
                             ; для соблюдения атомарности.
                      
      LDI   XL,low(OUT_buff)   ; Берем адрес начала буффера
      LDI   XH,high(OUT_buff)
      LDS   R16,OUT_PTR_E      ; Берем смещение точки записи
      LDS   R18,OUT_PTR_S      ; Берем смещение точки чтения
 
      ADD   XL,R16      ; Сложением адреса со смещением
      CLR   R17         ; получаем адрес точки записи
      ADC   XH,R17
 
 
      ST   X,R19      ; сохраняем их в кольцо
      CLR   R19         ; Очищаем R19, теперь там код ошибки
                  ; Который вернет подпрограмма
 
      INC   R16         ; Увеличиваем смещение
 
      CPI   R16,MAXBUFF_OUT      ; Если достигли конца
      BRNE   _NoEnd
      CLR   R16         ; переставляем на начало
 
_NoEnd:   
       CP   R16,R18         ; Дошли до непрочитанных данных?
      BRNE   _RX_OUT      ; Если нет, то просто выходим
 
 
_RX_FULL:
       LDI   R19,1         ; Если да, то буффер переполнен.
      STS   OUT_FULL,R19   ; Записываем флаг наполненности
                      ; В R19 остается 1 - код ошибки переполнения
 
_RX_OUT:
       STS   OUT_PTR_E,R16      ; Сохраняем смещение. Выходим

      LDS R16, UCSR0B_temp   ;Загружаем в R16 предыдущее состояние UCSR0B
        STS UCSR0B, R16         ;Возвращаем обратно прерывания по USART

      POP ZH
      POP ZL

        RET
;---------- подпрограммы-----------------------------------------


;--------Подпрограмма стартового мигания светодиодом-------------------*
LED_flashing:
   
   OUTI    PORTB, 0b00000011 //Подтяжка на PB0 и PB1
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000010
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000011
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000010
   rcall Delay
   rcall Delay
    OUTI    PORTB, 0b00000011 //Подтяжка на PB0 и PB1
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000010
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000011
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000010
   rcall Delay
   rcall Delay
    OUTI    PORTB, 0b00000011 //Подтяжка на PB0 и PB1
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000010
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000011
   rcall Delay
   rcall Delay
   OUTI    PORTB, 0b00000010
   rcall Delay
   rcall Delay
   RET
;---------- подпрограммы-----------------------------------


String:      .db   "23C  ",0
;===== EEPROM ============
      .ESEG         ; Сегмент EEPROM (ППЗУ)


Сначала программа не хотела работать как надо - не реагировала на байты с компьютера, начал дебажить, выяснилось, что процессор даже не заходит в обработчик прерывания по приему, т.к. светодиод не загорался (или загорался и очень быстро гас - тут я не уверен); код зажжения светодида в обработчике в данный момент убрал.
По итогу, добавил после метки Main две строчки

Код: Выделить всё

Main:
   
   rcall Delay
   rcall Delay

и всё заработало - байты принимаются и обрабатываются. АЦП работает и шлет показания.
И такой вопрос: почему без делея не работает?
Аватара пользователя
Мikа
Потрогал лапой паяльник
Сообщения: 343
Зарегистрирован: Пн апр 01, 2013 15:13:40
Откуда: Москва

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

Сообщение Мikа »

Привет, коты! На сей раз вопрос про АЦП. Гугл мне не очень помог, потому что все блоггерные умники, почему-тj напрочь влюблены в Си и пример на Ассемблере из быстро вылезающих сайтов мне ничего не попалось.
Вопрос у меня такой: как правильно пересчитать полученное значение их АЦП в напряжение?

Если я всё правильно понял, то формула такая:
(1.1* ADCH*4.03)/1024(или 1023?), где

1.1 - опорное U, берущееся внутри ATmega48
ADCH - то, что получилось с АЦП
4.03 - коэффициент делителя напряжения (10K и 3.3K)
1024 - выборки АЦП при 10битном АЦП (или нужно 1023?) Результат замера восьмибитный. 2 младших бита я не забираю.
Я думаю сделать так:

Код: Выделить всё

;Сначала 
.equ ADCco=(1.1*4.03)/1024 ;Просчёт с константами

;Потом уже после завершения преобразования:
Mov R16,ADCH             ;Загрузка результата АЦП в регистр
Ldi R17,ADCco             ;Загрузка результата вычисления констант
FMUL R16,R17             ;Беззнаковое умножение дробных чисел


Меня смущают дробные числа. Они очень весело описываются любителями си в бесполезных туториалах, мол "у нас точность измерения 0.0024 В!". Но, насколько я помню, дробные константы просчитает компилятор. А при вычислениях в МК там, вроде бы, всё округлится? А как тогда получить напряжение, скажем 2.7 В? Или представлять это как 2700 мВ?

Все это делается с целью следить за разрядом батареи, от которой питается все устройство, чтобы не дать ей переразрядиться.
Все это спрашиваю тут, потому что имею опыт сидеть целый день и не зная, что на самом деле происходит в регистрах? ломать голову. Не хочу потратить кучу времени зря. Помогите его спасти :) В принципе, куска программы с мало-мальскими комментариями, которая делает то, о чём я написал выше, будет достаточно. Я всё-таки хоть еще не радиокот, но я уже не радиокотёнок :D
Почему я здесь и задаю тупые вопросы?
Потому что хочу научиться.
Аватара пользователя
НАПАЛМ
Это не хвост, это антенна
Сообщения: 1314
Зарегистрирован: Пт ноя 27, 2009 19:47:13
Откуда: Казань

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

Сообщение НАПАЛМ »

Если собираетесь юзать все 10 битов АЦП, то вам придется заморочиться с грамотной разводкой платы и работой АЦП со спящим процессором.
Курите даташит: стр.248 24.6 ADC Noise Canceler
Про разводку, обратите внимание на землю: http://hamradio.tomsk.ru/2013/04/14/помехоустойчивые-устройства/
Аватара пользователя
ANALOG
Мучитель микросхем
Сообщения: 444
Зарегистрирован: Вс ноя 28, 2010 15:18:52
Откуда: Минск

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

Сообщение ANALOG »

Mika, в ADCco будет ноль, т.к. деление на 1024 даст меньше 1 и в итоге все занулится
А так да, лучше считать в милливольтах.
Конкретно для вашей задачи, если нужно просто контролировать, не опустилось ли напряжение ниже определенного предела, то я бы сказал, лучше вообще не трогать значения с АЦП (не переводить их в вольты), а просто сравнивать с заранее обсчитанной константой ;)
Ответить

Вернуться в «AVR»