воскресенье, 6 сентября 2020 г.

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

Иногда при работе с микроконтроллерами возникает необходимость сделать так чтобы микроконтроллер сам передавал какую нибудь информацию другим устройствам. Часто для этих целей используется интерфейс uart. В прошлом уроке https://electe.blogspot.com/2020/08/uart-attiny2313.html рассматривался способ сделать прием по интерфейсу uart символа и отреагировать на этот символ светодиодом подключенным к микроконтроллеру. Вторым закономерным этапом в изучении интерфейса uart для микроконтроллера должен быть этап изучения передачи информации из микроконтроллера во внешнее устройство. Как и ранее изучение можно начать поставив какую нибудь задачу. Т.к. работа с одним символом уже изучена то теперь можно усложнить задачу и попытаться сделать например передачу не одного символа а целой строки. Схему нужно немного изменить, надо добавить конденсатор по питанию (практика показала что без него uart плохо работает) а также можно убрать светодиод т.к. в нем нет необходимости в данном случае:
Рисунок 1 - Схема uart передатчика на ATtiny2313
Рисунок 1 - Схема uart передатчика на ATtiny2313

Теперь давайте рассмотрим код программы на ассемблере для отправки строки по интерфейсу uart от микроконтроллера:


На этот раз можно обойтись без прерываний и не писать строки необходимые для них. В начале, как обычно, подключается .inc файл с константами и макросами для ATtiny2313 (специально урезанный для данного компилятора (см. урок про стек и .inc файл)) для удобства написания программы. Далее указывается начало сегмента кода, инициализация стека. Следующие 4 команды были рассмотрены в предыдущем уроке они нужны для настройки скорости UARTа и остаются без изменений. Далее регистр UCSRB нужно настроить по другому нежели ранее. Теперь нужно установить в единицу бит разрешающий передачу по UART. Биты разрешающие прием и прерывания можно не устанавливать. На этом можно закончить настройку UARTа т.к. всё минимально необходимое для передачи по UART уже настроено. Далее в регистр R17 записывается ноль. Этого конечно можно было и не делать но написав эту строку и добавив комментарий можно будет потом понять для чего этот регистр будет использоваться. Следующие две команды очень интересные. Они нужны для того чтобы использовать массив строки символов который мы будем отправлять по интерфейсу UART. Регистры ZL и ZH хранят адрес ячейки FLASH памяти микроконтроллера из которой можно извлечь байт информации командой LPM. Т.е. мы можем записать какую нибудь информацию на FLASH память микроконтроллера а затем извлекать её от туда и использовать. FLASH память обычно используется для хранения команд но для хранения данных её тоже можно использовать но при этом надо учитывать что в отличии от оперативной памяти у FLASH памяти ограниченное количество циклов записи/стирания. Хотя есть и преимущества напр. больший размер. А также эта возможность используется для записи в микроконтроллер загрузчика Ардуины и она же может использоваться для загрузки например своих самодельных загрузчиков или даже для программ которые могут менять сами себя! Эта возможность присутствует хотя микроконтроллеры строятся по гарвардской архитектуре. Т.о. в этом микроконтроллере сочетается быстродействие даваемое данной архитектурой и возможность создавать самопрограммирующиеся программы которые могут значительно расширить возможности микроконтроллера, если конечно хорошо уметь использовать эту возможность. Для того чтобы записать в регистры ZL и ZH адрес ячейки флеш памяти было удобно, можно использовать такой способ как в коде выше. Указать где нибудь в коде какую нибудь метку напр. str: потом через директивы LOW и HIGH и умножение на два получить в эти регистры адрес того места на который указывает данная метка. Его естественно просто так не видно программисту но в данном случае это и не обязательно, главное разместить метку где нибудь так чтобы при вытаскивании из FLASH памяти информации случайно не попасть в область где находятся команды или за границу памяти. Для избежания первой проблемы метку можно располагать под основным циклом и всем остальным кодом (как и было сделано в коде выше). После этой метки можно через директиву .db разместить массив строки так как в коде выше. Через запятую пишутся байты информации которые можно написать символами в одинарных кавычках либо числами в десятичном или каком либо другом допустимом виде. В начале основного цикла проверяется бит UDRE регистра UCSRA который указывает на то свободен ли регистр для отправки байта по UART или нет. Если он не свободен то надо подождать когда он освободиться чтобы записать в него бит который нужно отправить. Это и происходит в первых двух строках основного цикла. Когда регистр для отправки байта освобождается, происходит выход из этого, вложенного в основной, цикла и первый символ массива строки которую мы хотим передать записывается в регистр R18 а также адрес в регистрах ZL и ZH инкрементируется т.е. увеличивается на единицу чтобы в следующий раз можно было взять следующий символ. После того как символ получен в регистр. Он отправляется по UARTу путем записи этого символа в регистр UDR. Далее инкрементируется счетчик конца строки т.е. увеличивается число в регистре R17 который мы сделали счетчиком. Если счетчик не досчитал до конца строки то происходит переход на начало основного цикла и все повторяется заново. Если досчитал то в регистры ZL и ZH опять записывается адрес начала массива строки потом счетчик конца строки обнуляется. Потом происходит задержка, чтобы строка не присылалась слишком часто. И возврат на начало основного цикла.