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

Обсуждаем контроллеры компании Atmel.
Alexeyslav
Друг Кота
Сообщения: 4550
Зарегистрирован: Чт май 05, 2011 21:26:34
Откуда: Украина, Славутич
Контактная информация:

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

Сообщение Alexeyslav »

Если используешь 8 бит, то не 1024 надо использовать а константу 256 в формулах. Но и это не нужно - умножать, получать вольты средствами МК чтобы сравнить с константой - это лишняя работа, лучше константу привести к виду кода выходящего с АЦП - т.е. к целому числу 0..255 соответствующему нужному напряжению. Тогда эту константу посчитает компилятор, а контроллеру останется только сравнивать байт в регистре с константой - а это уже совершенно другой уровень математики.
Если же все-таки надо вывести куда-то значение напряжения в миливольтах, то делается такой ход конём - подбирается опорное напряжение, делитель на входе таким образом чтобы сдвинув(умножив результат измерения на 2-4-8-16-32) результат измерения получить целое число представляющее напряжение в кратных 10 единицах(1мв-10мв-100мв) - нолики можно дорисовать при выводе и запятую поставить в нужном месте...
Да, это целое потом при помощи BIN2BCD преобразования можно разложить на цифры и вывести как текст.
Аватара пользователя
Мikа
Потрогал лапой паяльник
Сообщения: 343
Зарегистрирован: Пн апр 01, 2013 15:13:40
Откуда: Москва

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

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

Привет! Спасибо за кучу ответов :) Вчера я правда чё-то не подумал о том, что надо просто посчитать заранее, какому значению в диапазоне 0...255 будет что соответствовать :) Спасибо за ссылку на материал про помехи, щас обязательно ознакомлюсь. По поводу раскуривания ДШ и noise canceller - это я всё вчера смотрел, пока конфигурировал АЦП. Почти всё понял, но учитывая то, что моя задача не требует ни скорости ни точности,Ю сильно заострять внимание на этом не стал.

UPD:

Вот такое насчитал, если кому-то не лень, пробегитесь, может что-то напутал :?

Uref 1.1V = 255
Ubat 4.2V -> voltage devider ->1.05V

1.1V/255 = 0,0043137254901961
1.05/0,0043137254901961 = 243,4090909090897 = 243

4.2V-3.3V = 0.9V Рабочий диапазон
4.2V/0.9 = 4.6
243/4.6 = 53 - эквивалент 0.9V

243-53=190 - Минимально допустимое значение с АЦП
53/4 = 13.25 = 13 Шаг индикатора разряда ( - - - - )

243-230-217-204-191

*** *** *** *** Полная
___ *** *** *** 3/4
___ ___ *** *** 2/4
___ ___ ___ *** 1/4

Вот примерно так :)
Почему я здесь и задаю тупые вопросы?
Потому что хочу научиться.
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

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

Сообщение WiseLord »

Я делал вольтметр для автомобиля и сильно не заморачивался по поводу вычислений.

Сделал простой делитель 20к - 1к(перем) - 1к. И подстроил переменный резистор так, чтобы при 25.5В напряжение на движке резистора было около 1.1В - то есть, при 8-битном преобразовании получалось (выравнивание ADLAR вправо) ADCL = 255.

Для вывода на семисегментник, таким образом, даже расчёты не нужны. Просто точку не забыть поставить во втором разряде.
BGert
Первый раз сказал Мяу!
Сообщения: 24
Зарегистрирован: Вс ноя 25, 2012 13:57:12

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

Сообщение BGert »

Помогите пожалуйста!!! Я сейчас начал изучать Таймеры и столкнулся с такой проблемой.
При настройке Таймер Т0 на Mega8 все что заношу (до 255)в регистр TCNT0, в результате не изменяеться частота мигание LED. Пожалуйста укажите на мою ошибку.

Спойлер;---------- Псевдокоманды управления
.include "m8Adef.inc" ; Присоединение файла описаний
.def temp = r16 ; Определение главного рабочего регистра
; RAM =================
.DSEG ; Сегмент ОЗУ
; FLASH ===============
.CSEG ; Кодовый сегмент
.org 0 ;Установка текущего адреса на ноль
;........ Переопределение векторов прерываний
start: rjmp reset ; Reset Handler ;Reset Переход на начало программы
reti ;EXT_INT0 ; IRQ0 Handler
reti ;EXT_INT1 ; IRQ1 Handler
reti ;TIM2_COMP ; Timer2 Compare Handler
reti ;TIM2_OVF ; Timer2 Overflow Handler
reti ;TIM1_CAPT ; Timer1 Capture Handler
reti ;TIM1_COMPA ; Timer1 CompareA Handler
reti ;TIM1_COMPB ; Timer1 CompareB Handler
reti ;TIM1_OVF ; Timer1 Overflow Handler
rjmp TIM0_OVF ; Timer0 Overflow Handler
reti ;SPI_STC ; SPI Transfer Complete Handler
reti ;USART_RXC ; USART RX Complete Handler
reti ;USART_UDRE ; UDR Empty Handler
reti ;USART_TXC ; USART TX Complete Handler
reti ;ADC ; ADC Conversion Complete Handler
reti ;EE_RDY ; EEPROM Ready Handler
reti ;ANA_COMP ; Analog Comparator Handler
reti ;TWSI ; Two-wire Serial Interface Handler
reti ;SPM_RDY ; Store Program Memory Ready Handler
;---------- стека
reset: ldi r16,high(RAMEND) ; Main program start
out SPH,temp ; Set Stack Pointer to top of RAM
ldi r16,low(RAMEND)
out SPL,temp
;=======
;---------- портов
ldi r17,(1<<4) ;
out DDRB, r17 ;Копирование из r17 в DDRB (РВ4 - выход)
;---------- 8-разрядный таймер-счетчик 0 ВАРИАНТ №1

ldi r16, (1<<CS00)|(1<<CS02) ;Пределитель 1024
out TCCR0,r16 ;

ldi r16, (1<<TOIE0) ;бит 0 – TOIE0: Включение Прерывания Переполнения Таймер/Счетчика 0
out TIMSK,r16 ;

ldi r16, 0b11001000 ;11001000 =200,
out TCNT0,r16 ;начальное значение счетчика

sei ;Глобальное разрешение прерываний
;----------
;---------- Основной цикл
;---------- Начало основного цикла ВАРИАНТ №1
main:
rjmp main ; К началу цикла
;---------- Преривания по Переполнение таймера-счетчика 0
TIM0_OVF:
;---------- Мигание светодиода
eor r18,r17 ;Исключающее ИЛИ регистров r16 и r17
out PORTB,r18 ;Копирование из r16 в PORTB
reti

; EEPROM ==============
.ESEG ; Сегмент EEPROM
Alexeyslav
Друг Кота
Сообщения: 4550
Зарегистрирован: Чт май 05, 2011 21:26:34
Откуда: Украина, Славутич
Контактная информация:

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

Сообщение Alexeyslav »

А почитать даташит? Ну, первую итерацию он у тебя отработал короче последующих и это все действие которое ты определил загрузкой значения в таймер. либо настраивать надо его в другой режим работы, где период таймера задаётся регистром сравнения, либо сбрасывать на указанное значение КАЖДОЕ переполнение таймера.
BGert
Первый раз сказал Мяу!
Сообщения: 24
Зарегистрирован: Вс ноя 25, 2012 13:57:12

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

Сообщение BGert »

Alexeyslav писал(а):либо сбрасывать на указанное значение КАЖДОЕ переполнение таймера.

Большое Спасибо, Ваши слова мне очень помогли!!! я добавил "начальное значение счетчика" в преривание. И все заработало !
Я чувствовал что я что та не дописал, но не мог понять что именно. Спасибо еще раз. Буду дальше изучать таймеры.
СпойлерTIM0_OVF:
;---------- Мигание светодиода
ldi r16, 0b1100100 ;11001000 =200,
out TCNT0,r16 ;начальное значение счетчика


eor r18,r17 ;Исключающее ИЛИ регистров r16 и r17
out PORTB,r18 ;Копирование из r16 в PORTB
reti
Аватара пользователя
Мikа
Потрогал лапой паяльник
Сообщения: 343
Зарегистрирован: Пн апр 01, 2013 15:13:40
Откуда: Москва

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

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

Всем привет!
Вопрос такой, по выходу из подпрограммы.

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

cycle_1:
Push R17
NOP;1
NOP;2
NOP;3

rcall programm

NOP;4
NOP;5
NOP;6
rjmp cycle_1

cycle_2:
NOP7
NOP8
NOP9
rjmp cycle_2

programm:
NOP
NOP
NOP
Pop R16
Pop R16

LDI    16,low(cycle_2)
Push R16
LDI    16,High(cycle_2)
Push R16
ret


Правильно ли я понимаю, в ходе выполнения подпррограммы programm, 2 команды Pop вынут из стека адрес возврата, который попал туда при выполнении команды rcall, что в свою очередь поставит на край стека содержимое R18, которое было туда положено в самом начале cycle_1. После чего мы кладём в стек адрес метки cycle_2 и по команде ret извлекаем оттуда этот адрес, переходим на cycle_2, а в стеке готовым к извлечению лежит то, что было в R18. Я ничего не упускаю?

И если всё так, то чтобы перейти по другому адресу из подпрограммы, и сохранить стек таким, каким он был до того, как мы попали в подпрограмму, можно просто дважды извлечь значения из стека, после чего сделав jmp или rjmp по метке?
Почему я здесь и задаю тупые вопросы?
Потому что хочу научиться.
Alexeyslav
Друг Кота
Сообщения: 4550
Зарегистрирован: Чт май 05, 2011 21:26:34
Откуда: Украина, Славутич
Контактная информация:

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

Сообщение Alexeyslav »

У тебя стек разрушится. У тебя постоянно с каждым циклом будет расти стек и когда он дойдет до начала памяти начнет затирать предыдущие значения.

Надо всегда держаться правила что на каждый PUSH должен быть свой POP, так же как на каждый CALL должен быть свой RET в пределах одного логического блока(внутри тела цикла, внутри тела условного оператора). Иначе путаницы и переполнения стека не избежать.
Аватара пользователя
Мikа
Потрогал лапой паяльник
Сообщения: 343
Зарегистрирован: Пн апр 01, 2013 15:13:40
Откуда: Москва

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

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

А как понять разрушится?
Ведь в примере, который я написал, на каждый (условно) Push, есть Pop, смотрите:

cycle_1:
Push R17 PUSH НОМЕР 1
NOP;1
NOP;2
NOP;3

rcall programmPUSH НОМЕР 2 и 3

NOP;4
NOP;5
NOP;6
rjmp cycle_1

cycle_2:
NOP7
NOP8
NOP9
rjmp cycle_2

programm:
NOP
NOP
NOP
Pop R16POP для PUSH НОМЕР 2 и 3
Pop R16

LDI 16,low(cycle_2)
Push R16PUSH НОМЕР 4
LDI 16,High(cycle_2)
Push R16PUSH НОМЕР 5
retPOP для PUSH НОМЕР 4 и 5

И по выходу у нас на готове в стеке байт для Pop для Push номер 1.
P.S. Не выделил код тегом, чтобы жирным выделить комменты :)
И да, я, видимо, неудачно выразился с циклами. Я тут подразумеваю рассмотрение при однократном проходе программы до команды ret.
Почему я здесь и задаю тупые вопросы?
Потому что хочу научиться.
Аватара пользователя
BOB51
Друг Кота
Сообщения: 15541
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

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

Сообщение BOB51 »

Подстановку перенаправления выхода через подмену адреса возврата в верхушке стека что-ли содеять хочется?
8)
Так учти особенности адресации памяти у АВРок - там не метка подставляться должна, а метка,смещенная на бит влево с младшим битом = 0.
и сам указатель стека скорректировать для переустановки надо:
для начала сделаем sp=sp+2 согласно имеющемуся МК (скоко там регистро - два или один - допустим POPами тоже можно "гасить")
затем грузим
ldi rn,low(metka*2)
push rn
ldi rn,high(metka*2)
push rn
:tea:
Аватара пользователя
ANALOG
Мучитель микросхем
Сообщения: 444
Зарегистрирован: Вс ноя 28, 2010 15:18:52
Откуда: Минск

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

Сообщение ANALOG »

Mika, все правильно, в итоге прога застрянет в cycle_2
Только единственное, как уже сказали, надо адреса флеша умножать на 2, прежде чем пихать в стек.
А вообще, таких трюков, конечно, лучше стараться избегать :) Ну а для обучения полезно конечно, да :tea:
Аватара пользователя
Seriyvolk
Друг Кота
Сообщения: 4961
Зарегистрирован: Сб май 05, 2012 20:19:55
Откуда: Минск

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

Сообщение Seriyvolk »

Есть вопрос: с одного таймера в меге можно ли получить 2 независимых аппаратных канала ШИМ? Что-то ответа не нагуглил...
Прибор, защищённый предохранителем, сгорает первым, защитив предохранитель. Закон Мерфи.
Аватара пользователя
ANALOG
Мучитель микросхем
Сообщения: 444
Зарегистрирован: Вс ноя 28, 2010 15:18:52
Откуда: Минск

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

Сообщение ANALOG »

Да, используйте регистры сравнений A и B
ну, каналы будут независимы в плане значений ШИМ, а частота уж у них будет одна общая
Аватара пользователя
Seriyvolk
Друг Кота
Сообщения: 4961
Зарегистрирован: Сб май 05, 2012 20:19:55
Откуда: Минск

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

Сообщение Seriyvolk »

Спасибо, буквально только что уже и сам в даташите это откопал. Раньше мне казалось, что регистр сравнения у таймера только один... :facepalm:
Прибор, защищённый предохранителем, сгорает первым, защитив предохранитель. Закон Мерфи.
Аватара пользователя
Мikа
Потрогал лапой паяльник
Сообщения: 343
Зарегистрирован: Пн апр 01, 2013 15:13:40
Откуда: Москва

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

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

BOB51 писал(а):Подстановку перенаправления выхода через подмену адреса возврата в верхушке стека что-ли содеять хочется?
8)
Так учти особенности адресации памяти у АВРок - там не метка подставляться должна, а метка,смещенная на бит влево с младшим битом = 0.
и сам указатель стека скорректировать для переустановки надо:
для начала сделаем sp=sp+2 согласно имеющемуся МК (скоко там регистро - два или один - допустим POPами тоже можно "гасить")
затем грузим
ldi rn,low(metka*2)
push rn
ldi rn,high(metka*2)
push rn
:tea:


Если честно, мне кажется, что я толком нифига не понял, что вы сказали. Но рассуждения такие:

sp=sp+2 - это мы передвигаем указатель стека, то есть место, куда будет произведена следующая запись или откуда следующее чтение.

ldi rn,low(metka*2)
push rn
ldi rn,high(metka*2)
push rn


А вот прочитав это, я вспомнил, что ещё в обучалке динамической индикации, в подпрограмме, которая достаёт из матрицы код символа, который надо вывести на индикатор, там тоже адрес матрицы умножался на 2.

Спойлер

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

Decoder:
;преобразование двоичного числа
;в код 7-сегментного индикатора

              ldi ZL,Low(DcMatrix*2)   ;инициализация массива
              ldi ZH,High(DcMatrix*2)

              ldi Temp2,0             ;прибавление переменной
              add ZL,Temp1            ;к 0-му адресу массива
              adc ZH,Temp2

              lpm                     ;загрузка значения
              mov Temp1,r0
              ret


И тут меня посещает некое удивление, т.к. в статье DI HALT'a адрес на 2 не умножается.

Спойлер

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

LDI   R17,low(M1)
   PUSH   R17
   LDI   R17,High(M1)
   PUSH   R17


Думаю, щас уберу в подпрограмме с массивом кодов символов умножение адреса на 2 и посмотрю, что будет :) Просто хочется это сделать, чтобы увидеть самому.

А моя программа в данный момент имеет следующий принцип и код у неё такой:

1).Работает основной цикл, по нажатию кнопки мы переходим во второстепенный цикл, из которого программа сама вернётся через некоторое время.
2).Внутри второстепенного цикла мы переходим в подпрограмму и делаем то, что в ней написано.
3).Но если нам по UART приходит команда, надо срочно выходить из подпрограммы и идти не туда, откуда мы сюда пришли, а в основной цикл.

А в коде сделано так:

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

;Проверяем, пришёл ли байт по UART
                     LDS UARTTemp, UCSR0A ; Проверить, пришло ли что-нибудь по UART
                     SBRS   UARTTemp,7    ;
                     rjmp   nocom3    ; Не пришло - выходим отсюда
                     LDS   UARTTemp, UDR0    ; Пришло - читаем полученный байт.
                     ;********************************************
                     SUBI UARTTemp,0x30 ;Это чтобы облегчить общение с Термитом и присланная с компа цифра, на самом деле была этой цифрой в понимании МК.
                     cpi UARTTemp,7 ;Сравниваем пришедшую команду с тем, что значит срочно выходить обратно в основной цикл.
                     brne nocom3      ;Если не то, то ничего не делаем, идём дальше.
                     Pop UARTTemp   ;Или забираем из стека адрес возврата, чтобы вернуть SP к тому, что там изначально было положено до вызова подпрограммы.
                     Pop UARTTemp
                     jmp actioncommand ; Перепрыгиваем в цикл основной программы.


В этом коде вообще нет ret, но насколько я помню, это просто возврат по адресу, лежащему в 2 последних байтах стека (вернее тех, где находится SP), в отличии от reti, где ещё снимается флаг, а раз мы почистили стек через Pop, то ничего криминального не произошло. Естесственно, если в начале обработки этой подпрограммы в стек не было заPushено что-то ещё (а у меня не было).

И да, я знаю, что есть прерывание по получению байта по UART, но пока что меня устраивает такой вариант проверки прихода команды.
Почему я здесь и задаю тупые вопросы?
Потому что хочу научиться.
Alexeyslav
Друг Кота
Сообщения: 4550
Зарегистрирован: Чт май 05, 2011 21:26:34
Откуда: Украина, Славутич
Контактная информация:

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

Сообщение Alexeyslav »

Все эти игры со стеком до добра не доведут. Нельзя изменять поведение вызываемой подпрограммы, иначе потом очень сложно будет ловить глюки. Если даже простая программа требует держать в уме столько сложностей, то что будет если она станет больше?

Адрес на два умножать не надо! RCALL и RET оперируют с адресами слов, и только когда нам надо что-то считать с FLASH-памяти побайтно при помощи команды LPM необходимо адреса меток умножать на два. В остальных случаях этого делать не нужно.
Аватара пользователя
BOB51
Друг Кота
Сообщения: 15541
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

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

Сообщение BOB51 »

Со смещением в бит для адреса возврата в стеке, каюсь, сморозил "сусанина" - там метка без смещения ... :oops:
А вот в табличках, читаемых из памяти оно надобно (но опять же неприменимо при чтении строки данных). :)
Месячишку с аврками не вошкался - подзабылось. :facepalm:
Теперь о прерывании - это самостоятельный call adr вызов с собственной точкой возврата.
Основному циклу (какой-бы не выполнялся, если это простая подпрограмма в пределах допустимых вложений стека)это прерывание никак не помешает - просто в обработчике приемопередатчика предусмотреть буфер-накопитель данных, а в цикле основной программы опрос флажка готовности данных. Перевод точки возврата в данном случае несколько неоправдан.
Другое дело если процесс заканчивается по-разному в зависимости от имеющихся результатов (вычисляемый возврат)... и то можно за счет условных переходов реализовать - вопрос в быстродействии проверок всех условий и дополнительных требованиях к программе.
Или обработчик приемопередатчика имеет отличия в зависимости от того, в какой точке основной программы произошел его вызов...
:)
Аватара пользователя
Мikа
Потрогал лапой паяльник
Сообщения: 343
Зарегистрирован: Пн апр 01, 2013 15:13:40
Откуда: Москва

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

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

Всем привет, уважаемые :)
Сегодня решил просветиться в таком вопросе:

Под спойлером я написал живой пример использования, потом подумал, нафиг такие подробности, но удалять стало жалко и я его просто спрятал :)
СпойлерДопустим, как пример, мы делаем программу, которая мигает диодом с какой-то частотой.
Частоту мигания будет задавать прерывание по таймеру, в котором буде устанавливаться флаг, означающий, что временной интервал прошёл.

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

SBR R17,0b00000001 ;0 бит - флаг прохода временного интервала


Далее, в программе, пишем условие, благодаря которому мы будем 1 раз в одно поднятие этого флага попадать в ту часть кода, где будет происходить инверсия светодиода (вопрос как раз будет в той части).

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

SBRS R17,0 ; Если флаг установлен - то мы не перепрыгнем весь код
rjmp Do_Not_Do_Anything
;Код инверсии светодиода
Do_Not_Do_Anything:


Ну и сам код, где кроется вопрос:

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

CBR R17,0b00000001;Сброс флага

SBIS PinC,0;Проверка, включен ли диод
rjmp setLED

CBI PortC,0;Выключить диод, если он включен
rjmp over;Выход
 
setLED:
SBI PortC,0;Включить диод, если он выключен

over:


А вопрос, собственно в следующем:
Команды SBIS и SBIC пропускают следующую комманду, если бит в порту установлен или сброшен. А имеет ли принципиальное значение, как сконфигурирован указанный в этих командах пин контроллера? Я имею ввиду такую ситуацию: вы конфигурируете какой-то пин на выход. И за счёт того, что на нём висит, напряжение на нём, когда он запрограммирован на "1" меньше уровня логической единицы для вашего МК. В таком случае как поведёт себя команда?

На практике у меня были какие-то глюки и я просто добавил 1 байт оперативки, в котором я ставил флаги, одновременно с установкой бита в порту.
Почему я здесь и задаю тупые вопросы?
Потому что хочу научиться.
nirq
Опытный кот
Сообщения: 758
Зарегистрирован: Вс фев 10, 2013 15:26:00

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

Сообщение nirq »

SBIS и SBIC пропускают следующую комманду, если бит в порту регистре установлен или сброшен.

Не надо фантазировать, усложнять, предполагать и додумывать.
Представляем себя на месте человека, придумавшего вот эту вот волшебную чёрненькую штучку ЭМКА. И максимально буквально читаем то, что он про неё написал. В оригинале читаем, без посредников (а-ля "Мойша по телефону напел").



А чтоб не гадать, сколько букв "мэ" в комманде - называем её "инструкция", так получается даже наукообразнее.

И кстати да: этими сбисами не "инструкция пропускается", а лишний раз инкрементируется PC. Лишний один раз.

ldi R16, 0x00
out PORTA, R16
sbic PORTA, 0
lds R16, 0x10 ; опаньки... doc0856.pdf ???
пропускается именно что инструкция.


SBIS PinC,0;Проверка, включен ли диод
...
CBI PortC,0;Выключить диод, если он включен
А если подумать.
А если прочитать, потом прочитать внимательно, потом понять прочитанное и подумать ещё раз.
Ответить

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