Показаны сообщения с ярлыком Ассемблер. Показать все сообщения
Показаны сообщения с ярлыком Ассемблер. Показать все сообщения

четверг, 12 сентября 2024 г.

AVR ASM RAM

 У микроконтроллера ATtiny2313 есть 3 типа памяти (если не считать память регистров общего назначения и специализированную вроде регистров данных и т.д.). Это постоянная или flash память, она самая большая и в ней обычно храниться код программы. Оперативная (ОЗУ, RAM), информация с которой удаляется после выключения или перезагрузки, обычно содержит данные с которыми требуются частые манипуляции. Также есть EEPROM для длительного хранения данных при выключенном питании. В ассемблерных программах ранее рассмотренных в данном блоге обычно применялись регистры общего назначения для хранения данных с которыми требуются частые манипуляции. Однако регистров общего назначения не так много как ячеек оперативной памяти, поэтому не всегда можно обойтись одними только регистрами. Чтобы научиться использовать оперативную память, записывать и читать из неё данные. Можно в качестве тренировки сделать например "бегущий светодиод" схема которого приведена на рисунке:

Рисунок 1 - Схема "бегущего светодиода"

Светодиоды соединены с пинами порта D в который будут поочередно записываться состояния при которых горит один из светодиодов по порядку на линии. Код получился не сложный, давйте его рассмотрим:

Многое из данного кода знакомо по предыдущим статьям об ассемблере в данном блоге. Но есть некоторые новые команды.

Директива .DSEG указывает на начало сегмента данных. В данном сегменте резервируюется память под переменные. Переменной можно дать произвольное название и выделить для неё произвольное количество байт. В данном случае, каждой переменной выделено по 1 байт. Для записи, в переменную, используется команда STS. Запись в переменную осуществляется из регистра общего назначения (в данном случае R16) а в регистр общего назначения, значение записывается командой LDI. Чтение из переменной происходит только в регистр общего назначения, командой LDS. 

Посмотреть работу "бегущего светодиода" можно на видео




Адрес биткоин кошелька для поддержки канала - bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g

воскресенье, 9 января 2022 г.

USI в режиме SPI slave на ATtiny2313

 SPI - это полнодуплексный последовательный синхронный интерфейс передачи данных. Полнодуплексный означает что прием данных и их передача может происходить одновременно. Для этого в данном интерфейсе есть 2 провода один из которых называется MOSI (M-мастера O-выход S-слейва I-вход) а второй MISO (M-мастера I-вход S-слейва O-выход). Если по SPI соединяются два устройства то один из них является мастером а другой слейвом. Мастер управляет процессом передачи и приема а также генерирует синхроимпульсы. Синхроимпульсы нужны для того чтобы точно задавать моменты времени в которые на проводах MOSI и MISO замеряются уровни напряжений чтобы быть записанными. Синхронные интерфейсы надежнее асинхронных т.к. в асинхронных приемник и передатчик должны работать с одинаковой (чего практически никогда не бывает) или очень близкой частотой чтобы приемник записывал 0 или 1 именно тогда когда это нужный по счету 0 или 1. Неточность частот также влияет на скорость передачи данных в худшую сторону. Поэтому синхронные интерфейсы также быстрее асинхронных. Из за полного дуплекса и синхронности возможно что это самый быстрый последовательный интерфейс который только можно придумать. Быстрее могут быть только параллельные интерфейсы. Синхроимпульсы идут по проводу CSK. Итого, как минимум и не учитывая земли, для данного интерфейса нужно 3 провода. Если необходимо соединить по SPI более 2х устройств то задействуются дополнительные провода SSx где x-номер слейва. Из за этого количество проводов увеличивается на 1 при добавлении одного нового устройства на шину. Т.е. количество проводов SS равно количеству слейвов если оно больше 1. Если слейв 1 то провод SS можно не использовать. Поэтому SPI плох в плане экономии проводов. Если требуется передача, с небольшой скоростью, данных по небольшому количеству проводов то лучше использовать например I2C. Если нужна большая скорость передачи данных с небольшим количеством устройств то SPI лучше подойдет чем I2C. Из за того что данный интерфейс синхронный, стабильность частоты его работы не важна, настолько что если например микроконтроллер является слейвом то мастером может быть человек с кнопкой подключенной к SCK, переключателем подключенным к MOSI и светодиодом подключенным к MISO. С таким подключением человек-мастер может протестировать слейва. Чтобы подключить два устройства по SPI нужно соединить MOSI вывод мастера с MOSI выводом слейва, MISO мастера с MISO слейва а также CSK мастера с CSK слейва. Мастер и слейв имеют внутри себя сдвиговые регистры и после соединения, их описанным выше способом, получается кольцевой сдвиговый регистр при сдвиге битов столько раз сколько битов в одном из регистров (мастера или слейва) происходит обмен данными между мастером и слейвом:




Рисунок 1 - Диаграмма обмена данными межу слейвом и мастером по SPI

Сдвиг на один бит происходит при переднем фронте синхроимпульса или при и заднем спаде синхроимпульса. В микроконтроллерах обычно можно настроить данный параметр. Обычно обмен по SPI происходит по 8 бит но в микроконтроллерах часто имеется возможность настроить обмен на другое количество бит за раз, однако чаще всего используется 8 бит т.е. 1 байт. После того как обмен произошел мастер может забрать байт данных данных из регистра и поместить новый для обмена. Слейв может поступить аналогично. В обоих устройствах д.б. счетчик который указывает на то когда можно забирать байт данных и записывать новый и отсчитывать он должен одинаковое количество раз т.е. 8 если передается 1 байт за раз. Обычно в микроконтроллерах имеются специальные флаги сигнализирующие об окончании обмена. Схема со ATtiny2313 слейвом для взаимодействия с человеком мастером м.б. например такой:
Рисунок 2 - Схема SPI слейва на ATtiny2313 для взаимодействия с человеком

Кнопка S2 обязательно должна быть бездребезговой т.к. иначе одно нажатие на такую кнопку может создать (а скорее почти наверняка создаст) количество синхроимпульсов больше одного. О бездребезговой кнопке есть статья -> https://electe.blogspot.com/2019/04/blog-post.html практика показала (см. видео ниже) что такая кнопка может успешно работать как источник синхроимпульсов. В микроконтроллере ATtiny2313 на SPI. Зато есть USI (универсальный синхронный интерфейс) который может работать в режиме SPI мастера или слейва. Для настройки USI на режим SPI slave есть регистр USICR. Но прежде нужно настроить пины микроконтроллера соответствующим образом. Т.е. Пин SCK д.б. настроен на вход (т.к. если микроконтроллер слейв то синхроимпульсы на него подаются от мастера),  пин MOSI тоже на вход а пин MISO на выход. На входные пины также следует поставить внутреннюю подтяжку к плюсу питания. Режим SPI настраивается битами 4 и 5 регистра USICR, битами 1...3, этого регистра, настраивается источник тактирования сдвигового регистра а также счетчика. Если сделать источником тактирования сдвигового регистра внешний сигнал то микроконтроллер будет работать как слейв. Для счетчтка также нужно установить внешний сигнал. В общем чтобы настроить USI как SPI slave нужно установить биты 3 и 4 регистра USICR:

Чтобы мастер смог забрать байт данных от слейва, нужно поместить этот байт в регистр USIDR. после того как обмен произойдет, из этого регистра можно извлечь байт данных пришедший от мастера. Об окончании обмена свидетельствует бит 6 регистра USISR. Его можно проверять в цикле прежде чем забирать байт от мастера но прежде его нужно программно сбросить записав в регистр число 0b01000000 т.е. именно присвоить это число этому регистру а не выполнить операцию побитового или с данным числом. На схеме на рисунке 2 есть светодиод VD1. Этот светодиод нужен для того чтобы сигнализировать об окончании обмена с мастером т.к. если считать синхроимпульсы слишком долго то можно сбиться со счета а данный светодиод упрощает тестирование слейва вручную. Полный код программы приведен в текстовом поле ниже:


Видео:

вторник, 7 декабря 2021 г.

Умножение чисел на ассемблере на микроконтроллерах ATtiny

 Несмотря на то что в интернете можно найти команду MUL для умножения на микроконтроллерах AVR. В документациях на ATtiny2313 и ATtiny13 такой команды нет. Микроконтроллеры семейства "Tiny" имеют сокращенный набор команд. Но умножение может понадобиться даже на таких микроконтроллерах. Несмотря на скромные, относительно других современных микроконтроллеров вроде STM32, возможности, микроконтроллеры семейства Tiny всё равно могут выполнять сложные операции если бережно распоряжаться их ресурсами создавая программы для них на ассемблере. В среде WinAVR или AVR Studio на языке си можно использовать знак умножения но компилятор наверняка превратит конструкцию с умножением в большой кусок машинного кода. Который для какой нибудь ATtiny будет гораздо больше чем для какой нибудь ATmega. Одним из выходов может быть - стараться не использовать умножение там где без него можно обойтись. Если нужно умножить на 2,4,8,16,... и т.д. то умножение можно заменить побитовым сдвигом влево. Эта операция выполняется быстро однако подходит только для частных случаев. Самый простой и, при этом, являющийся определением умножения способ перемножить два числа между собой - это сложить первое число с самим собой столько раз сколько указывает второе число (далее первое число - это множимое, второе число - множитель). Например чтобы умножить 10 на 2 нужно 10 сложить с самим собой 2 раза т.е.

10*2=10+10=20

Операция умножения простых чисел обладает свойством коммутативности т.е. "от перемены мест множителей произведение (результат умножения) не меняется". Однако если сделать это в предыдущем примере то процессору будет нужно выполнить гораздо больше действий:

2*10=2+2+2+2+2+2+2+2+2+2=20

Отсюда видно что на производительность сильно влияет множитель а не множимое. Если множитель будет велик то будет велика и задержка на выполнение этой операции что может негативно сказаться на работе устройства. Например если микроконтроллер планируется использовать для векторного управления электродвигателем то этот микроконтроллер может не успевать умножать числа. Для ускорения можно использовать например таблицу умножения но для её хранения нужно некоторое количество памяти а например на ATtiny2313 flash памяти всего 2кб. Существует способ умножения не требующий большого количества памяти. Он известен как "умножение в столбик". Умножать этим способом учат в школах и, на листке бумаге, он выполняется (в большинстве случаев (если не надо умножать например на 2)) явно быстрее способа с использованием одного только сложения. Однако для данного способа необходимо знать таблицу умножения что может привести к мысли о том что микроконтроллер тоже должен её "знать" т.е. хранить во flash памяти. Однако, к счастью, таблица умножения в двоичном виде полностью совпадает с таблицей истинности логического "И". Ещё одним немаловажным упрощением является то что при применении логического "И" между 0 и множимым, по всем разрядам, получится 0. При применении логического "И" между 1 и множимым, по всем разрядам, получится множимое. Это упрощает написание кода т.к. с учетом этого нет необходимости проходиться по всем разрядам множимого во вложенном цикле. Однако по всем разрядам множителя всё рано придется пройтись. Отсутствие вложенного цикла также уменьшит количество операций для выполнения процессором что ускорит операцию умножения в целом. Чтобы проверить "такой облегченный алгоритм умножения столбиком" на микроконтроллере, можно например использовать UART. Т.к. произведение двух чисел имеет разрядов больше чем множитель то желательно не делать его большим чтобы произведение не вышло за пределы одного байта. Для этого можно послать по UARTу два числа в одном символе, после чего микроконтроллер их выделит, перемножит и пришлет результат. Так можно проверить работает ли алгоритм прежде чем начать использовать его для умножения. При проверке, указанным способом, микроконтроллер должен будет выдать символ '8' (без кавычек) при отправке ему символа 'x' (тоже без кавычек). Код программы для ATtiny2313:


К пину 4 порта D надо приделать кнопку которая делает 0В на этом пине при её нажатии. При нажатии на эту кнопку микроконтроллер присылает произведение по UARTу. Программа не идеальная но она нужна только для проверки алгоритма умножения. Вложенный цикл всё таки есть но он нужен для побитового сдвига на некоторое количество разрядов. Также можно заметить что 0 также будет сдвигаться в чем нет необходимости. Данную проблему можно решить перенеся цикл сдвига в пропускаемый участок кода перед меткой "L2". Возможно существуют и более быстрые и экономные способы умножения (о них можно написать в комментариях внизу или в комментариях к видео) однако данный тоже не плох по сравнению со способом на одном сложении.

Видео по данной теме:



КАРТА БЛОГА (содержание)

четверг, 2 декабря 2021 г.

Генерация звука на ассемблере и ATtiny2313

Помимо дисплеев, светодиодов и прочих средств вывода информации воспринимаемой глазами, микроконтроллеры также способны управлять устройствами вывода звука. Полноценный mp3 плеер сделать на таком простом микроконтроллере как ATtiny2313 будет не просто т.к. он имеет не много памяти (2кб flash памяти) для хранения звуковой информации. Для решения данной проблемы могут быть использованы дополнительные микросхемы памяти связанные с микроконтроллером по интерфейсу например SPI но при этом программисту придется заняться алгоритмами декодирования звука формата mp3 в импульсы на пинах микроконтроллера для вывода звука. Самый простой способ сделать звук используя микроконтроллер - это подключить к его пину маломощный пьезодинамик и подавать на этот пин прямоугольные импульсы. Звуки, при этом, будут обогащены высшими гармониками и простое добавление фильтра не решит проблему полностью т.к. звуки могут быть разных частот и также могут сильно отличаться друг от друга частотами. Однако простые сигналы или простые мелодии, как например в старых часах или игрушках, таким способом генерировать можно. Так можно сделать например сирену для радиоуправляемой игрушки. Ещё звуки в таком формате не занимают много места в памяти. Чтобы генерировать звук определенной частоты, на микроконтроллере, лучше использовать аппаратный таймер т.к. используя его проще рассчитать результат. В ATtiny3213 есть 8ми битный таймер и 16ти битный. Используя второй, без предделителя, можно создавать импульсы с частотой от примерно 7 Гц до примерно 500кГц. 7 Гц - это уже частота инфразвука который человеческая слуховая система не воспринимает а 500кГц - это частота которая намного больше верхнего порога слышимости, поэтому данный диапазон частот подходит и следовательно второй таймер подходит для генерации звука. Если используется таймер то с ним лучше использовать какое нибудь прерывание (т.к. с флагами в основном цикле работать не очень удобно). Можно использовать например прерывание по совпадению или прерывание по переполнению. В первом случае, таймер считает до определенного числа после чего происходит прерывание. Во втором случае таймеру устанавливается определенное число от которого он считает до переполнения после чего происходит прерывание по переполнению. В обработчике прерывания происходит установка нового (или опять старого) числа а также изменение напряжения на пине. Большой разницы в том какой способ использовать нет. Выберем второй т.к. он уже ранее рассматривался в статье https://electe.blogspot.com/2020/08/attiny2313.html. Чтобы генерировать звуки можно например использовать массив в котором будут частоты и длительности. Программа будет перебирать данный массив делать импульсы с определенной частотой определенное время после чего делать импульсы с другой частотой и продолжать это делать в течении другого промежутка времени и так пока массив не закончиться после чего либо повторить заново либо ждать например нажатия кнопки. Для начала можно реализовать циклическое повторение, проверить результат а потом переделать для генерации звука в ответ на какие либо события будет не трудно. Чтобы определить число от которого должен считать таймер для создания импульсов определенной частоты а также определить количество импульсов которое должен пропустить микроконтроллер для формирования задержки, для данного случая, можно использовать формулы:
Где val - число от которого должен считать таймер, delay_counter_max - количество импульсов которое надо пропустить чтобы получилась задержка delay в секундах. Т.к. таймер 16ти битный а микроконтроллер 8ми разрядный то для хранения val в микроконтроллере есть два регистра и следовательно это число надо разделять на две части. Лучше всего это сделать "снаружи" микроконтроллера (чтобы его не перегружать и можно было использовать более простые языки вроде питона или js на которых решить данную проблему проще чем на ассемблере) а массив в микроконтроллер записать в готовом виде. Для формирования такого массива можно использовать программу внизу данной статьи. В верхнее поле вводятся частоты через запятую а в нижнее длительности. После нажатия кнопки "play" проиграется звук и выведется массив а также колличество элементов в нем деленое на 3. Имея массив можно организовать его перебор, в обработчике прерывания, используя операторы условного перехода и пропуска команды и написать программу:

Массив записывается в самом низу после директивы ".db". Eсли одной строки не хватает то на следующей строке, в начале, пишется ".db" (без кавычек) и продолжение массива. В строке 
CPI R17, 46
вместо 46 пишется число которое выдала программа внизу после массива. При этом надо помнить что у ATtiny2313 всего 2кб flash памяти а в регистр нельзя записать больше 255.
Видео по данной теме с тестом устройства:

Программа для генерации массива для генерации звука:
частоты через запятую->
длительности через запятую->

массив генерации звука:
delay_counter_max=

КАРТА БЛОГА (содержание)

вторник, 29 декабря 2020 г.

Внешние прерывания и счетчик дребезга контактов на attiny2313

Внешними прерываниями называются такие прерывания основного цикла программы которые возникают в результате воздействия на специальные пины, электрических сигналов определенного вида. Для attiny2313 есть 4 таких вида:

 1) передний фронт импульса (переход напряжения от низкого уровня до высокого),

2) задний фронт импульса (или спад (переход напряжения от высокого уровня к низкому)),

3) и фронт и спад, 

4) низкий уровень напряжения. 

То какой из них будет вызывать прерывание настраивается регистром MCUCR данного микроконтроллера. Реакция микроконтроллера на внешние прерывания гораздо быстрее чем его реакция на опрос пина порта командами SBIS и SBIC в основном цикле т.к. прежде чем пин опросится этими командами должны будут выполнится команды в основном цикле а это как минимум безусловный переход который делает этот основной цикл. С внешними прерываниями программа может работать настолько быстро что даже сможет посчитать дребезг контактов кнопки подключенной к пину внешнего прерывания. У attiny2313 есть 2 пина внешних прерываний, на схеме они помечаются как INT0 и INT1:

Счетчик дребезга контактов - это интересное устройство которое можно использовать для примерной оценки того насколько проверяемые контакты плохи своим дребезгом. Схема счетчика дребезга контактов на attiny2313:


Самым простым способом показать колличество дребезга является светодиод. Светодиод может мигнуть столько раз сколько кнопка сделала паразитных переключений своим дребезгом. От программатора, при этом, микроконтроллер м.б. отсоединен. Теперь давайте рассмотрим код программы на ассемблере: 

В начале, как всегда, подключается файл с константами и макросами для attiny2313, далее директивой .CSEG указывается начало сегмента кода а после директивы .ORG 0x0000 происходит "перепрыгивание" через фрагмент когда который перенаправляет выполнение программы на подпрограмму обработки прерывания. Это нужно чтобы эта подпрограмма не сработала сразу после старта. Когда после того как произошло внешнее прерывание INT0 происходит переход по метке i0 в самый низ этого кода где число в регистре R17 увеличивается на единицу т.е. инкрементируется, после чего в регистр r20 записывается число 250 для того чтобы после возврата в основной цикл, задержка начиналась заново. Команда RETI делает выход из подпрограммы обработки прерывания обратно в основной цикл. Перед основным циклом происходит инициализация стека для того чтобы прерывание срабатывало как при фронте так и при спаде в нулевой бит регистра MCUCR записывается единица. Такой вид внешнего прерывания выбран по тому что светодиод, в основном цикле, также будет менять свое состояния на каждой итерации основного цикла т.е. переходить в светящееся состояние если он не светиться и переходить в не светящееся состояние если он светиться т.о. надо инкрементировать счетчик дребезга два раза при одном импульсе для того чтобы был подсчет полных импульсов. Записью единицы в 6й бит регистра GIMSK происходит разрешение внешнего прерывания INT0. Далее пин к которому подключен светодиод настраивается на выход и командой SEI разрешаются прерывания вообще. После чего начинается основной цикл в котором имеется задержка, для того чтобы глаз человека успевал замечать мигания светодиода, после которой происходит переход в начало основного цикла если в регистре с количеством дребезга пусто. Если же там не пусто то в атомарном блоке (блоке между запретом прерываний CLI и их разрешением SEI) происходит изменение состояния светодиода вместе с декркментированием (уменьшением на единицу колличества дребезга) после этого атомарного блока происходит возврат на начало основного цикла. Программа простая и не без недостатков но тем не менее её написание является хорошей тренировкой по ассемблеру в образовательных целях и собранное устройство даже имеет некоторую практическую ценность т.к. используя его можно убедиться в наличии дребезга контактов а также примерно оценить насколько контакты плохи в плане наличия этого дребезга. Посмотреть как работает счетчик дребезга и послушать описание работы программы можно на видео:

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

Аналоговый компаратор и двухбитный АЦП

Многие современные микроконтроллеры, как правило, имеют в своем составе аналого цифровые преобразователи (АЦП) для измерения аналогового сигнала. Такая возможность часто бывает необходима. Однако не все микроконтроллеры имеют именно аппаратный встроенный АЦП. Например ATtiny2313 не имеет его. Зато он имеет аналоговый компаратор. Аналоговый компаратор, сам по себе, не может измерять аналоговое напряжение. Зато он может определить больше ли это напряжение некоторого опорного или нет. А также он работает гораздо быстрее чем полноценный АЦП. Бывают ситуации когда этого функционала достаточно, например когда надо определить превышение тока в нагрузке, включить защиту и сделать это быстро, в этом случае аналоговый компаратор будет гораздо лучше чем АЦП. Также этот компаратор может быть использован в каких нибудь преобразователях напряжения чтобы оперативно следить за напряжением. Или же небольшими внешними доработками можно сделать АЦП на основе имеющегося компаратора. Работа с аналоговым компаратором микроконтроллеров AVR достаточно проста. На основе компаратора  микроконтроллера ATtiny2313 можно сделать например двухбитный АЦП. Такой АЦП не очень точный но зато очень быстрый и его можно использовать например для какой нибудь шкалы уровня заряда или ещё чего нибудь подобного. Рассмотрим схему:

 




Рисунок 1 - АЦП на ATtiny2313

Резистором R9 задается напряжение которое измеряет АЦП, вместо этого резистора можно поставить какой либо другой источник напряжения в пределах от 0 до напряжения питания микроконтроллера. На резисторах R5-R8 собрана R-2R цепь которая является двухбитным цифро аналоговым преобразователем (ЦАПом). На светодиодах VD1-VD2 сделана шкала уровня напряжения. Резисторы R1-R4 нужны для ограничения тока светодиодов VD1-VD2. Вместо R-2R цепи можно было бы использовать какие либо другие способы задания напряжения например ШИМ+RC цепь и т.о добиться гораздо большей точности но при этом скорость измерения напряжения значительно снизиться. Теперь давайте рассмотрим код программы на ассемблере:


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




КАРТА БЛОГА (содержание)

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

Аппаратный ШИМ на ассемблере для ATtiny2313

Широтно импульсная модуляция (ШИМ) напряжения имеет множество применений. В электроприводах постоянного тока она используется для регулировки оборотов электродвигателя, в асинхронных электроприводах и инверторах такой модуляцией делаются синусоидальные напряжения, в источниках питания и DC-DC конвертерах для стабилизации и изменения напряжения, также этот тип модуляции может применяться для регулировки освещения электрическими лампами, температуры создаваемой электрическими нагревателями и использоваться во многих других случаях с электроприборами для которых подходит такой способ модуляции. В общем этот тип модуляции очень важен и поэтому во многих микроконтроллерах делают аппаратную поддержку ШИМа. ATtiny2313 - не исключение и в этом микроконтроллере есть 4 канала аппаратного ШИМа. Такой ШИМ можно получить с 4х специальных пинов:

Рисунок 1 - Пины аппаратного ШИМа микроконтроллера ATtiny2313

Для создания ШИМа используются таймеры счетчики которых в ATtiny2313 две штуки. На каждом из них есть два канала. Настроить аппаратный ШИМ на этом микроконтроллере не сложно. Сперва нужно настроить соответствующие выводы (см. рисунок 1) на выход (о том как это делать см. предыдущие статьи по ассемблеру). После чего есть два регистра TCCR0A и TCCR0B для 8ми битного таймера и два регистра TCCR1A и TCCR1B для 16ти битного таймера. Для настройки 8 ми битного таймера в режим FAST PWM (быстрый ШИМ) в нулевой и первый биты регистра TCCR0A записываются единицы. Для того чтобы ШИМ работал так:
В начале периода идет высокий уровень а по прошествии некоторого количества тактов он заменяется низким:


 И чтобы так было на обоих каналах. Нужно в 5й и 7ой биты регистра TCCR0A записать единицы.
в итоге должно получиться примерно так:

LDI R16, 0b10100011
OUT TCCR0A, R16

Также нужно запустить таймер чтобы ШИМ начался. (об этом см. статью про таймер https://electe.blogspot.com/2020/08/attiny2313.html). Запускать надо естественно без предделителя чтобы режим действительно был FAST. В итоге должно получиться что то вроде этого:
 
LDI R16, 0b00000001 
OUT TCCR0B, R16 

Чтобы устанавливать какое либо напряжение на нужном пине есть регистры OCR0A и OCR0В для 8 ми битного таймера и OCR1A и OCR1В для 16ти битного таймера. Записывать в них можно от 0 до 255 (т.к. регистр 8ми битный) а напряжения на соответствующих выводах (см. рисунок 1) будет меняться пропорционально записанному числу. Например установить половину напряжения питания на пине 14 ( он же PB2 или OC0A или PCINT2) можно командами:

LDI R16, 127
OUT OCR0A, R16

На основе аппаратных ШИМов можно сделать например не обычную светодиодную мигалку а например псевдоплавную. Код такой мигалки приведен в поле:

Светодиоды с резисторами необходимо подключит к выводам 9 и 14 микроконтроллера ATtiny2313. К данной статье прилагается видео:
В котором также рассказано о подпрограммах на ассемблере для микроконтроллеров AVR.

воскресенье, 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 опять записывается адрес начала массива строки потом счетчик конца строки обнуляется. Потом происходит задержка, чтобы строка не присылалась слишком часто. И возврат на начало основного цикла.

среда, 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 ми бит, хотя в данном случае это не так и можно было бы сразу записать это число но для других микроконтроллеров этот макрос пригодиться.


понедельник, 27 июля 2020 г.

Работа с кнопкой на ассемблере для ATtiny2313

Для того чтобы опросить пин микроконтроллера AVR настроенный на ввод существует несколько команд. Команда SBIS делает пропуск следующей после неё команды если на опрашиваемом пине высокий уровень напряжения (+5В (логическая единица)) и не делает этот пропуск если на этом пине низкий уровень напряжения. Существует команда SBIC которая делает такой пропуск если на опрашиваемом пине низкий уровень и не делает его если высокий. Т.о. система микроконтроллера AVR позволяет опросить отдельный пин, какого либо порта, одной командой и сделать это за один такт. Как было показано ранее вывод логической единицы или нуля на пин настроенный на выход также можно одной командой за один такт. Есть команды для работы с портом целиком - это команда OUT для вывода и команда IN для ввода (т.е. опроса порта целиком). Чтобы проверить на практике ввод с кнопкой, можно использовать схему:
Рисунок 1 - Мигалка с кнопкой с подтягивающим резистором на ATtiny2313

Можно взять код из предыдущей статьи, дополнить его одной строкой и получить мигалку работающую при нажатой кнопке и не работающую при отпущенной кнопке:

здесь добавлена строка:
SBIS 0x10, 1 ; пропустить следующий команду если кнопка не нажата
0x10 - это адрес регистра для опроса порта D,
1 - это номер пина порта D к которому подключена кнопка.
Т.о. получается что светодиод может загораться только в случае если кнопка нажата т.к. только тогда команда SBIS будет позволять выполниться команде установки пина, к которому подключен светодиод, в единицу.
Можно упростить схему на рисунке 1 убрав резистор для подтягивания пина к плюсу питания когда кнопка не нажата.
Рисунок 2 - Мигалка с кнопкой на ATtiny2313

 Но чтобы схема работала также как раньше надо подключить внутренний подтягивающий резистор. Сделать это можно такой же командой какой мы устанавливали логическую единицу на пине настроенном на выход. Т.е. командой SBI но для пина к которому подключена кнопка.

Здесь добавлена строка:
SBI 0x12, 1 ; подача на пин 1 порта D высокого уровня
Прежде чем добавлять такую строку надо сначала убедиться что пин настроен на вход. Убедиться в этом можно посмотрев строки:

LDI R16, 0b00000001      ; поместим в регистр R16 число 1
OUT 0x11, R16            ; загрузим значение из регистра R16 в DDRD

в которых мы видим что первый пин порта D настроен на вход т.к. в соответствующий бит соответствующего регистра записан ноль. После того как программа написана её можно откомпилировать и загрузить в микроконтроллер (см. предыдущую статью).


КАРТА БЛОГА (содержание)