MyTetra Share
Делитесь знаниями!
Структура программы на Ассемблере в AVR Studio
Время создания: 02.12.2021 17:33
Раздел: Компьютер - Программирование - Ассемблеры - Архитектура AVR - Ассемблер в AVR Studio
Запись: xintrea/mytetra_syncro/master/base/1638455582remgd4m5u3/text.html на raw.github.com

Рассмотрим синтаксические особенности ассемблера AVR на примере небольшой тестовой программы для микроконтроллера ATmega8, приведенной ниже. Программа формирует на выводе PB2 импульсы с частотой следования ≈ 2.5 Гц при частоте внутреннего RC-генератора 1 МГц.


1. ; Тестовая программа для ATmega8.

2. // Светодиод подключен к выводу PB0 микроконтроллера.

3. /* Биты конфигурации:

Low Fuse High Fuse

BODLEVEL = 1 RSTDISBL = 1

BODEN = 1 WDTON = 1

SUT1 = 1 SPIEN = 1

SUT0 = 0 CKOPT = 1

CKSEL3 = 0 | EESAVE = 1

CKSEL2 = 0 |_ RC-генератор BOOTSZ1 = 1

CKSEL1 = 0 | 1 МГц BOOTSZ0 = 1

CKSEL0 = 1 | BOOTRST = 1 */

4. .nolist ;подключение стандартного заголовочного файла

5. .include "m8def.inc"

6. .list

7. .equ PAUSE = 50000 ;задержка времени

8. .equ LED = PB2 ;вывод для подключения светодиода

9. .def temp = R16 ;регистр для промежуточных операций

10. .def buffer = R17 ;регистр для чтения порта

11. .cseg

12. .org 0

13. rjmp initial ;0xC01F

14. rjmp 0 ;rjmp service_INT0 ;внешнее прерывание 0 0xCFFE

15. rjmp 0 ;rjmp service_INT1 ;внешнее прерывание 1 0xCFFD

16. rjmp 0 ;rjmp service_OC2 ;совпадение TCNT2 и OCR2 0xCFFC

17. rjmp 0 ;rjmp service_OVF2 ;переполнение TCNT2 0xCFFB

18. rjmp 0 ;rjmp service_ICP1 ;захват в ICP1 0xCFFA

19. rjmp 0 ;rjmp service_OC1A ;совпадение TCNT1 и OCR1A 0xCFF9

20. rjmp 0 ;rjmp service_OC1B ;совпадение TCNT1 и OCR1B 0xCFF8

21. rjmp 0 ;rjmp service_OVF1 ;переполнение TCNT1 0xCFF7

22. rjmp 0 ;rjmp service_OVF0 ;переполнение TCNT0 0xCFF6

23. rjmp 0 ;rjmp service_SPI ;прерывание от модуля SPI 0xCFF5

24. rjmp 0 ;rjmp service_URXC ;получение байта по USART 0xCFF4

25. rjmp 0 ;rjmp service_UDRE ;опустошение UDR в USART 0xCFF3

26. rjmp 0 ;rjmp service_UTXC ;передача байта по USART 0xCFF2

27. rjmp 0 ;rjmp service_ADCC ;прерывание от АЦП 0xCFF1

28. rjmp 0 ;rjmp service_ERDY ;завершение записи в EEPROM 0xCFF0

29. rjmp 0 ;rjmp service_ACI ;прерывание от компаратора 0xCFEF

30. rjmp 0 ;rjmp service_TWI ;прерывание от модуля TWI 0xCFEE

31. rjmp 0 ;rjmp service_SPMR ;завершение выполнения spm 0xCFED

32. .org 0x20

33. initial: ldi temp,low(RAMEND) ;0xE50F

34. out SPL,temp ;0xBF0D

35. ldi temp,high(RAMEND) ;0xE004

36. out SPH,temp ;0xBF0E

37. cbi PORTB,LED ;0x98C2

38. sbi DDRB,LED ;0x9ABA

39. ldi temp,1«LED ;0xE004

40. main: in buffer,PORTB ;0xB318

41. eor buffer,temp ;0x2710

42. out PORTB,buffer ;0xBB18

43. rcall delay ;0xD001

44. rjmp main ;0xCFFB

45. delay: ldi XH,high(PAUSE) ;0xECB3

46. ldi XL,low(PAUSE) ;0xE5A0

47. sbiw XH:XL,1 ;0x9711

48. brne PC-1 ;0xF7F1

49. ret

В строках 1…3 приведено 3 возможных варианта оформления комментариев. Комментарий в строке 1, начинающийся со знака “;”, распознается любым ассемблером и поэтому является наиболее предпочтительным. Комментарии в строках 2 и 3 подобны тем, которые используются в нотациях языков высокого уровня. Последний из них дает возможность выделить сразу несколько строк (маркерами начала и конца фрагмента текста является “/*” и “*/” соответственно).

В строке 5 директивой .include к программе подключается стандартный заголовочный файл "m8def.inc". Необязательные директивы .nolist и .list (строки 4 и 6 соответственно) запрещают вывод содержимого подключаемого файла в файл листинга.

Все директивы допускается размещать в одной строке. Так, например, можно было бы записать
.nolist .include "m8def.inc" .list

Несмотря на это, желательно придерживаться правильного стиля программирования, в соответствии с которым в одной строке должна быть расположена только одна директива ассемблера.

Объявление констант находится в строках 7 и 8. Константе LED присваивается номер линии ввода-вывода PB2 = 2, описание которой находится в заголовочном файле. В строках 9 и 10 двум рабочим РОНам назначаются пользовательские имена.

Секция рабочего кода открывается директивой .cseg в строке 11. Директива .org 0 (строка 12) устанавливает начальный адрес в памяти программ. В строке 13 должна находиться инструкция перехода на метку начала основной программы initial.

Метка представляет собой адрес в пределах секции кода или данных. В ассемблере AVR она должна быть записана в начале строки и завершаться в конце двоеточием.

Конечно, адрес в команде может быть указан и явно. В данном примере его можно задать безошибочно (rjmp 0x20 вместо rjmp initial). Но, это возможно только потому, что директива .org 0x20 (строка 32) заставляет компилятор поместить команду в строке 33 по адресу 32-го слова FLASH-памяти программ. Однако, в большинстве случаев, адрес размещения той или иной команды заранее неизвестен и, кроме того, он может изменяться по мере того, как в программу будут вноситься изменения. Именно поэтому предпочтительней использовать метки. Назначение их адресов производится автоматически на этапе компиляции. Да и символьные имена меток, предоставляют об объектах намного больше информации, чем просо какие-то числа.

На месте каждого неиспользуемого в программе прерывания желательно поставить “заглушки” в виде команды возврата rjmp 0 или reti, как это сделано в строках 14…31.

В строках 33…39 производится инициализация ресурсов микроконтроллера. Любая программа обязательно должна начинаться с установки начального значения указателя стека (если он имеется). В строках 33…36 в SPH:SPL заносится значение RAMEND = 0x045F (вершина стека перемещается в самый верх SRAM). Для выделения младшего и старшего полубайтов константы RAMEND используются встроенные функции low(RAMEND)=0x5F и high(RAMEND)=0x04 соответственно.

Далее разряд LED в регистре данных порта B сбрасывается на 0 (стока 37), а сама линия управления светодиодом настраивается на вывод (стока 38). В строке 39 в регистр temp заносится константа 1<


1

2

ldi temp,(1«PB0)|(1«PB5) или ldi temp,(1«PB0)+(1«PB5) ;temp <- 0b00100001

out DDRB,temp ;DDRB <- temp

Код в строках 40…44 представляет собой тело основной программы. В регистр buffer считывается текущее состояние порта данных PORTB (строка 40). Далее между содержимым buffer и temp производится операция “Исключающее ИЛИ” (строка 41), после чего модифицированное содержимое buffer снова выводится в порт (строках 42). В результате такого действия логический уровень на линии PB2 изменится на противоположный. В строке 43 происходит вызов подпрограммы задержки времени delay (≈200 мс), а в строке 44 расположена инструкция перехода на метку main. Так образуется основной цикл программы, в котором происходит постоянное повторение операторов в строках 40…44: инвертирование уровня на выводе LED, задержка, затем снова инвертирование и т.д.

Внутри подпрограммы delay в регистровую пару XH:XL заносится число PAUSE = 50000, определяющее длительность задержки времени (строки 45, 46). В строке 47 из содержимого XH:XL вычитается 1. Команда условного перехода в строке 48 проверяет, значение флага Z из SREG. Если Z = 1 (результат предыдущей операции не равен нулю), то управление передается на строку 47. Так будет повторяться до тех пор, пока не выполнится условие XH:XL = 0 (т.е. пока не пройдет 50000 циклов вычитания). Команда выхода ret в строке 49 возвращает управление в то место основной программы, с которого произошел вызов delay.

Отдельно стоит обратить внимание на встроенную в ассемблер переменную PC в строке 48. Она представляет собой текущее содержимое программного счетчика. Адрес PC-1 будет указывать на предыдущее слово в памяти программ микроконтроллера. В данном случае в этом слове находится команда sbiw XH:XL,1. Использовать PC очень удобно для программных переходов в небольших пределах. Однако здесь всегда необходимо помнить, что у микроконтроллеров AVR имеются команды, которые имеют размер в 2 16-разрядных слова программ (lds Rd,k, jmp k и др.), из-за чего при вычислении смещения необходимо будет добавлять\отнимать к PC значение 2 вместо 1 на каждую такую инструкцию.

Справа, в листинге приведен машинный код, который будет сгенерирован после компиляции программы.


 
MyTetra Share v.0.65
Яндекс индекс цитирования