среда, 12 августа 2020 г.

Прием по uart на ассемблере для ATtiny2313

 Интерфейс uart (универсальный асинхронный приемник передатчик) часто используется для связи разных устройств друг с другом. Например для связи компьютера с микроконтроллером или микроконтроллера с например радиомодулем или другим микроконтроллером, модулем или микросхемой. Изучив uart микроконтроллера ATtiny2313 можно будет заменить Ардуино, в некоторых случаях, этим микроконтроллером. Чтобы изучить uart можно например поставить какую нибудь задачу например зажечь светодиод подключенный к микроконтроллеру передав ему какой либо код по uartу тогда в процессе решения этой задачи придет понимание того как использовать этот интерфейс. Светодиод, в таком случае, нельзя подключить к нулевому или первому пину порта D т.к. эти пины являются пинами uartа микроконтроллера ATtiny2313. Его можно подключить например к нулевому пину порта B


Рисунок 1 - Схема

В схему добавлены разъемы для того чтобы микроконтроллер можно было соединить с программатором чтобы зампрограммировать а потом отсоединить от программатора и соединить с usb-uart переходником для передачи информации на микроконтроллер от компьютера. Кварцевого резонатора в схеме нет. Практика показала что uart может работать и без него. uart - это асинхронный интерфейс т.е. передатчик передает биты через какие то определенные промежутки времени а приемник должен принимать биты через промежутки времени такие же. Иначе приемник будет принимать не то что нужно. К счастью некотарая доля синхронности в этом интерфейсе есть т.к. запускается прием только после возникновения запускающего импульса и асинхронность присутствует только в течении приема одного байта информации (если uart настроен на один байт т.е. 8 бит). Поэтому uart может работать при некотором небольшом несовпадении частот. Чем меньше скорость передачи тем больше может быть это несовпадение. Чтобы это несовпадение было почти нулевым и микроконтроллер мог работать по uartу на очень больших скоростях и очень стабильно и наверняка надежно, нужны специальные кварцевые резонаторы, например не на 1 или 2 МГц а на 1.8432 МГц для того чтобы время тактового импульса было кратным времени одного импульса по стандартам для uart. Естественно uart будет работать и при обычных кварцевых резонаторах (на Ардуине же он как то работает при кварцевых резонаторах с резонансной частотой 16 МГц). В данном случае для простоты мы не будем использовать такие редкие кварцевые резонаторы а используем внутренний rc генератор микроконтроллера с установленной по умолчанию на заводе частотой 1 МГц. Практика показала что uart при этом работает а также при такой частоте микроконтроллер может работать и при пониженном напряжении что позволяет его использовать например при питании от одной ячейки LiFePO4 аккумулятора что удобно для изготовления каких нибудь маленьких радиоуправляемых игрушек или роботов. Рассмотрим ассемблерный код программы:



Первые 4 команды а также инициализация стека, настройка пина на выход были рассмотрены в предыдущих уроках. Адрес ячейки памяти на которую переводиться выполнение программы после того как сработало прерывание по приему байта называется URXCaddr. После того как это прерывание возникло команда RJMP переводит выполнение программы на подпрограмму обработки прерывания. Но чтобы это смогло произойти сначала нужно настроить uart микроконтроллера для этого (а точнее для нашей текущей задачи) есть 5 регистров. Причем в данном случае 2 из них можно даже не трогать. Для начала можно настроить скорость. Слишком большую выбирать нельзя чтобы uart нормально работал. Можно выбрать например 9600 - эта скорость часто используется и подходит во многих случаях. Настраивается скорость через регистры UBRRL и UBRRH. В эти регистры нужно записать число соответствующее нужной нам скорости. Если это число больше 255 то надо младшие разряды этого числа записать в регистр UBRRL а старшие в UBRRH. Если меньше то можно это число записать в UBRRL а UBRRH не трогать вообще т.к. там нули по умолчанию. Чтобы определить это число не обязательно его рассчитывать по формулам. Можно просто найти его из таблицы в документации на микроконтроллер:

Также из этой таблицы видно что в нашем случае можно получить меньшую ошибку если установить бит удвоения скорости в единицу. Находиться этот бит в регистре UCSRA там он под номером 1 (самый первый это нулевой). После того как 3 регистра настроены осталось ещё 2. Разрешить прием байта и прерывание по завершению приема можно настройкой UCSRB. Чтобы разрешить прием, в единицу устанавливается бит 4 этого регистра, чтобы разрешить прерывание по приему, в единицу устанавливается бит 7 этого регистра. Последний регистр UCSRC надо настроить так чтобы принимать по 8 бит. Обычно так всегда и делается но в некоторых случая можно сделать по другому. Чтобы настроить прием 8ми бит нужно в записать единицы в бит 1 и бит 2 регистра UCSRC. Хотя этого можно было бы и не делать т.к. там по умолчанию и так имеются единицы:

Но зато теперь если вдруг понадобиться изменить количество принимаемых бит известно как это сделать. Далее в программе настраивается пин к которому подключен светодиод и разрешаются все прерывания. В основном цикле опять ничего интересного (хотя можно его заполнить например динамической индикацией или ещё чем нибудь). Когда бит приходит по uartу и возникает прерывание нужно вытащить из регистра UDR этот бит и поместить в какой либо регистр общего назначения например R18. Далее командой сравнения CPI и командой условного перехода если не равно BRNE сделать ветвление и организовать зажигание светодиода когда пришел ASCII код символа "a" и гашение светодиода когда пришел ASCII код символа "b". ASCII коды всех символов можно узнать из таблицы ASCII кодов всех символов:

Команда RETI делает выход из подпрограммы обработки прерывания. О том как откомпилировать программы и загрузить её в микроконтроллер можно узнать из предыдущих уроков. Если компиляция и загрузка прошли успешно то можно подключить микроконтроллер к компьютеру через usb-uart переходник и попробовать зажечь светодиод. Отправить какой либо символ с компьютера через переходник можно используя например Arduino IDE или же это можно сделать через командную строку. Если на компьютере обычная ОС Winows напр. 7 то например командой 

mode COM3 BAUD=9600 PARITY=n DATA=8

Можно настроить этот переходник на нужную скорость и количество бит. Вместо COM3 нужно вписать название своего com порта который появляется после втыкания переходника в usb разъем. Отправить символ а можно командой

echo a > COM3

символ b соответственно командой

echo b > COM3

Вместо COM3 нужно вписать название своего com порта который появляется после втыкания переходника в usb разъем. После написания команды надо нажимать Enter чтобы символ отправился. В моем случае всё сработало, можно убедиться в этом посмотрев видео:


воскресенье, 9 августа 2020 г.

Прерывание по переполнению таймера на ассемблере для ATtiny2313

Таймеры/счетчики есть в большинстве современных микроконтроллеров. Они используются для множества разнообразных целей. Например для каких нибудь часов, широтно-импульсной модуляции (ШИМ), динамической индикации и ещё очень много для чего. В микроконтроллере ATtiny2313 их два. Один из них 8ми битный другой 16ти битный. Тот что 8ми битный может считать только до 255 т.к. для большего числа нужно большее количество бит. На основе 8ми битного счетчика можно делать что либо с малыми промежутками времени напр. ШИМ, динамическую индикацию, какие либо короткие импульсы напр. для каких нибудь протоколов связи по какому нибудь редкому интерфейсу или для чего нибудь ещё для чего нужны не долгие импульсы. На основе 16ти битного таймера счетчика можно делать длинные задержки (но короткие делать тоже можно). 16ти битный таймер/счетчик более универсальный поэтому его стоит изучить первым. На его основе можно сделать светодиодную мигалку

Рассмотрим ассемблерный код светодиодной мигалки на таймере:


Вначале подключаем специальный файл а также указываем директиву начала сегмента кода (об этом подробно написано в предыдущем уроке). Далее после директивы ORG с адресом с которого начинается выполнение программы ставим безусловный переход для того чтобы код изменения состояния светодиода (зажечь когда погашен или погасить когда зажжен) не выполнялся сразу после запуска микроконтроллера или после сброса. Далее инициализация стека (см. предыдущий урок). После инициализации стека идет инициализация таймера. Для настройки таймера есть некоторое количество регистров. В простейшем варианте (как сейчас) достаточно настроить два регистра TCCR1B - для запуска таймера и настройки его тактирования а также TIMSK - для разрешения прерывания. У разных микроконтроллеров регистры настраиваются по разному поэтому при переходе на другой микроконтроллер надо смотреть документацию и возможно устанавливать другие биты. В данном случае в регистре TCCR1B наиболее интересны первые 3 бита которыми устанавливается тактирование таймера. Если все три этих бита нули то раймер не тактируется и не работает. Если нулевой бит с единицей а остальные с нулем то таймер тактируется от того же генератора что и центрайльный процессор микроконтроллера и без предделителя. Если первый бит с единицей а остальные с нулями то тактирование также от генератора для центрального процессора но добавляется предделитель на 8. Более наглядно все опции представлены в таблице в документации:

Чтобы разрешить прерывание по переполнению 16ти битного таймера нужно в 7ой бит регистра TIMSK записать 1. Этот регистр в ATtiny2313 отличается от того же регистра в например ATmega8 т.е. TIMSK ATtiny2313 не TIMSK ATmega8 и настраивать их нужно по разному. 

Далее в коде пин со светодиодом настраивается на выход (об этом в предыдущих уроках). 

Командой SEI разрешаются все прерывания в том числе и то что было настроено для таймера.

Далее идет основной цикл в котором ничего интересного не происходит. Когда таймер досчитывает до максимального числа которое может поместиться во все его 16 бит, происходит переполнение этого таймера и прерывание по этому переполнению. Выполнение прогрвммы переноситься из основного цикла на ячейку флешь памяти с адресом обозначаемым как OVF1addr. А в этой ячейке организован безусловный переход на подпрограмму обработки этого прерывания. В этой подпрограмме меняется состояние светодиода (с погашенного на горящий или с горящего на погашенный). Команда RETI осуществляет выход из подпрограммы обработки прерывания. Выход происходит обратно в основной цикл где ничего интересного. Таймер опять считает до максимального значения и далее все повторяется.



понедельник, 3 августа 2020 г.

Настройка стека на ассемблере для ATtiny2313 и подключение .inc файла

При написании программ для микроконтроллеров на языке C от программистов обычно не требуется такое действие как настройка стека, однако если программа пишется на ассемблере то настраивать его нужно в большинстве случаев а также нужно понимать зачем он нужен и как используется. Стеком (применительно к микроконтроллерам AVR) можно считать некоторую область в оперативной памяти (ОЗУ) которая может заполняться и опустошаться (т.е. как бы менять свои размеры) в процессе работы программы. В 8ми битных микроконтроллерах AVR в стек может быть помещено 8 бит информации за один раз в конец стека, извлечено также 8 бит информации за один раз из его конца. Заполнять и извлекать информацию из стека можно только с его конца (т.е. брать можно только последний элемент) (хотя на самом деле это ограничение можно обойти но суть стека в том что можно трогать только последний его элемент). Данные микроконтроллеры работают так что стек заполняется не от начала ОЗУ до его конца а наоборот т.е. если первый элемент помещен по какому то адресу в ОЗУ то следующий элемент будет помещен по адресу который на единицу меньше чем предыдущий и т.д. Настройка стека заключается в том чтобы указать с какого адреса начинается стек, путем помещения этого адреса в соответствующие регистры (для микроконтроллеров у которых максимальный адрес ОЗУ не помещается в один байт) или регистр (как в данном случае для ATtiny2313 т.к. этот микроконтроллер имеет небольшой размер ОЗУ). Для ATtiny2313 этот регистр называется SPL и чаще всего в него помещается самый большой адрес из ОЗУ чтобы для стека было как можно больше места чтобы "разростись" и не перекрывать те области ОЗУ которые заполняются другими данными. Стек можно использовать по разному но в основном он нужен чтобы использовать подпрограммы в программе на ассемблере. Подпрограммы - это набор инструкций который может быть размещен где нибудь во флешь памяти и выполнятся по мере необходимости после вызовов из основной программы. Подпрограмма может вызываться много раз и использование их позволяет экономить место во флешь памяти не заполняя его повторными одинаковыми инструкциями. После вызова такой подпрограммы в стек помещается адрес того места на котором остановилось выполнение основной программы чтобы позже в это место вернуться. Также стек можно использовать чтобы просто хранить в нем какую либо информацию. В общем стек - вещь нужная и поэтому её нужно настраивать. Если взять программу из предыдущей статьи и дополнить её настройкой стека то получиться так:

Данная программа заработает если её откомпилировать и загрузить в ATtiny2313 методами описанными в предыдущих уроках. Однако можно заметить что обращения к регистрам микроконтроллера осуществляются по их адресам написанным в шеснадцатеричном виде хотя у регистров есть свои буквенные имена по которым обращение к ним было бы более удобным. Для того чтобы реализовать такое более удобное обращение существуют специальные .inc файлы которые можно подключить директивой .INCLUDE. К сожалению те файлы что скачиваются вместе с компилятором avra не подходят для этого компилятора т.к. содержат инструкции которые им не поддерживаются. Если эти инструкции удалить то можно получить урезанный но подходящий файл. Скачать такой можно по ссылке https://yadi.sk/d/QUFvZ5JgAwyYpw. Далее этот файл надо поместить в папку где располагается .asm файл с кодом:

и перед компиляцией в CMD перейти в папку с данными файлами. Сделать это можно командой CD. После этого компилятор не должен выдавать предупреждение о том что он не опознал микроконтроллер т.к. в подключенном файле находиться директива которая указывает ему для какого микроконтроллера написана эта программа. 
После подключения данного файла и замены шестнадцатеричных адресов регистров их названиями код программы стал более понятным и удобным! Хотя всё же есть ньюансы которые стоит разяснить. Например Low(RAMEND) - означает что из двухбайтового числа которое теперь называется RAMEND мы возьмем только ту часть которая расположена в младших разрядах (как бы низкую половину возьмем). Low - это такой макрос который берет и выделяет эту половину. Т.к. ATtiny2313 это 8ми битный микроконтроллер и регистры в нем 8ми битные то поместить мы в них можем только 8 бит а адрес м.б. больше 8 ми бит, хотя в данном случае это не так и можно было бы сразу записать это число но для других микроконтроллеров этот макрос пригодиться.