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

PWM (ШИМ) на STM32F103, Си и CMSIS

 Широтно импульсная модуляция (ШИМ, PWM) напряжения - это чрезвычайно полезная функция микроконтроллера т.к. такой тип модуляции чаще других применяется на практике. Можно сказать что это очень популярный тип модуляции и поэтому в популярных микроконтроллерах ШИМ модулятор часто присутствует на аппаратном уровне в виде периферийного устройства или их совокупности. В микроконтроллере stm32f103 есть некоторое количество каналов с которых можно получить ШИМ. Пины с которых можно его получить д.б. связаны с таймерами которые могут работать в режиме широтно импульсной модуляции. Таймер общего назначения TIM2 может работать в таком режиме. Из таблицы в даташите можно выбрать пин связанный с этим таймером:


Этот таймер надо настроить (об этом есть отдельная статья ->https://electe.blogspot.com/2021/12/stm32f103c8t6-cmsis_24.html). Также надо включить тактирование выбранного таймера и порта на котором выбранный пин который также надо настроить на выход. К выбранному пину можно подключить светодиод например как на схеме:


После этих настроек:

Будут ещё минимум 4 регистра которые надо настроить чтобы получить минимальный проект использующий ШИМ. С настройками, как на картинке выше, таймер считает от 0 до 10000 после чего счетчик сбрасывается и счет начинается заново. В регистр CCR1 можно записать число от 0 до 10000 которое будет определять коэффициент заполнения ШИМ. Т.е. при достижении счетчиком данного числа будет происходить переключение напряжения на выбранном пине. Чтобы включить эту опцию надо записать  1 в первый бит регистра CCER:
Есть регистр CCMR1 чтобы настроить режим захвата/сравнения таймера TIM2. В биты с 4 по 6 записывается код определяющий режим работы. Если записать в них 110 то будет режим "ШИМ1" который описан в мануале:


В конце можно записать 1 в нулевой бит регистра CR1 и тем самым запустить таймер и ШИМ на выбранном пине. Полный код может выглядеть например так:
По теме ШИМа на STM32F103C8 можно посмотреть видео:

пятница, 24 декабря 2021 г.

stm32f103c8t6 cmsis мигалка на таймере

Периферийные устройства микроконтроллера дополняют его функционал а также снижают нагрузку на центральный процессор. Аппаратный таймер микроконтроллера является периферийным устройством отмеряющим заданные промежутки времени и сигнализирующим об их окончании обычно путем установки определённых битов статусных регистров и вызова прерываний. Написать программный код в котором определенные его участки выполняются через определенные временные промежутки проще если для замера этих промежутков будут использоваться аппаратные таймеры чем если для этого будет использоваться только процессор т.к. во втором случае придется считать сколько тактов занимает каждая инструкция которая может повлиять на отмеряемые промежутки времени. В микроконтроллерах STM32 обычно имеется некоторое количество аппаратных таймеров (т.е. больше одного). Они бывают простыми, сложными и "общего назначения" (general purpose). Простые присутствуют не во всех микроконтроллерах а сложные имеют избыточный функционал для простой мигалки, поэтому для изучения работы с таймерами лучше использовать таймер общего назначения, например таймер TIM2 микроконтроллера STM32F103C8. Данный таймер тактируется от шины APB1

Включить его тактирование можно записью 1 в бит 0 регистра APB1ENR



Для разблокирования прерываний, в данном микроконтроллере от данного таймера, в библиотеке CMSIS имеется команда: NVIC_EnableIRQ (TIM2_IRQn);
Чтобы задать отмеряемый промежуток времени, имеются два регистра: PCR и ARR. В регистр PCR записывается число на единицу меньшее того на которое делиться тактовая частота шины APB1 (которая по умолчанию равна 72МГц) чтобы задать минимальный промежуток времени который сможет отсчитать таймер. Т.е. если например в этот регистр записать число 7200-1=7199 то тактовая частота таймера будет равна = 72 000 000 / 7 200 =  10 000 Гц. Следовательно минимальный промежуток времени который сможет отсчитать таймер равен = 1 / 10 000 = 0.0001c. Т.е эта установка делает так что таймер считает один раз в 0.0001с. В регистр ARR записывается число на единицу меньшее числа до которого считает таймер прежде чем вызвать прерывание. Т.е. если например записать в этот регистр число 10 000 - 1 = 9 999 то прерывание будет вызываться 1 раз в секунду т.к. 72 000 000 / (7 200 * 10 000) = 1с. В каждый из этих регистров нельзя помещать число большее чем 65535. Поэтому нельзя например записать в один из них 72 000 000 а в другой 1 и получить задержку в 1 секунду. 
Чтобы разрешить прерывание от таймера есть бит 0 регистра разрешения прерываний DIER:
Чтобы запустить таймер и он начал работать есть бит 0 регистра CR1 таймера:


Теперь можно вне функции "main" написать функцию "void TIM2_IRQHandler();" которая будет вызываться каждый раз когда таймер досчитает до 10 000. В теле этой функции надо прописать код который должен выполниться после того как это произойдет. В нем обязательно надо сбросить бит 0 статаус-регистра SR т.к. этот бит является программно очищаемым флагом обновления прерывания. 


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



Светодиод к микроконтроллеру, естественно, подключается также как описано в статье про мигалку на задержках -> https://electe.blogspot.com/2021/12/stm32f103c8t6-cmsis.html т.к. пин к которому он подключается настроен на выход с открытым стоком. В основном цикле программы можно ничего не писать. По данной теме можно посмотреть видео:


пятница, 17 декабря 2021 г.

Работа с кнопкой на STM32, языке Си и библиотеке CMSIS

 Работа с кнопкой подключенной к выводу микроконтроллера обычно является вторым этапом в его изучении, после мигалки. Умея работать с портами ввода-вывода общего назначения уже можно программно реализовывать связь по разным интерфейсам вроде SPI, UART и т.д (аппаратная реализация их возможно лучше и проще но это не исключает возможность их программной реализации) с внешними устройствами а также реализовывать ШИМ и ещё много чего. Проблемы могут быть с реализацией например АЦП, (т.к. для него нужен как минимум компаратор поэтому определенно лучше использовать встроенный АЦП) ЦАП и возможно некоторых других периферийных устройств но в целом, порты ввода-вывода общего назначения это основной способ взаимодействия микроконтроллера с "внешним миром". Кнопку можно подключить к любому свободному пину ввода-вывода общего назначения микроконтроллера STM32:

При этом не обязательно "подтягивать" этот пин к плюсу или минусу питания внешним резистором т.к. cделать это можно программно в коде. За основу можно взять например код из предыдущего урока ->https://electe.blogspot.com/2021/12/stm32f103c8t6-cmsis.html. Т.к. умения мигать светодиодом уже имеются то можно например реализовать мигания с разной частотой при нажатой и отпущенной кнопке. Чтобы настроить пин на выход, как обычно для данного семейства микроконтроллеров, сначала надо включить тактирование порта связанного с этим пином. Т.к. на схеме выше, кнопка подключена к пину порта B то взяв код из предыдущего урока, можно ничего не добавлять. Если кнопку подключить к пину другого порта то надо будет включить тактирование соответствующего порта. Чтобы пин PB8 был входом нужно чтобы в бите 0 и бите 1 регистра GPIOB_CHR были нули. Т.к. там нули по умолчанию то для настройки данного пина на вход можно не делать ничего. Аналогичная ситуация и с другими пинами. Обычно по умолчанию они все настроены на вход и это стандартная ситуация для большинства микроконтроллеров. Чтобы можно было сделать пину программную "подтяжку" и не подключать внешний резистор нужно в бит 3 записать единицы и не забыть убрать единицу из бита 2 т.к. там она по умолчанию и если её не убрать то получим ситуацию при которой пин не работает как было запланировано изначально:

В коде это может выглядеть например так:


Для "подтяжки" к плюсу можно использовать, уже знакомую, команду установки единицы на пине:


Только на этот раз единицу надо записать в бит 8:

Чтобы проверять нажата кнопка или нет, используется регистр IDR:
Логическими операторами языка си можно получать ноль или не ноль в зависимости от напряжения на пине и использовать ветвления чтобы в зависимости от этого напряжения выполнялись разные участки кода. Так например можно реализовать мигалку с разной частотой в зависимости от того нажата кнопка или нет:
Полный код можно скопипастить из текстового поля:



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


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


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

Мигалка на микроконтроллере stm32f103c8t6, языке си и библиотеке CMSIS

 Создание светодиодной мигалки на микроконтроллере является аналогом созданию "Hello World!" при написании первой программы выполняемой на компьютере при изучении какого нибудь нового языка программирования, библиотеки, фреймворка или чего то подобного. Т.к. это обычно самое простое устройство которое можно сделать а чтобы подключить дисплей к микроконтроллеру и вывести на нем "Hello World!" нужно уже неплохо разбираться в программировании данного микроконтроллера. Микроконтроллеры STM32 неплохо продуманны и весьма популярны поэтому уроков по их программированию в сети интернет очень много. Особенно много уроков по мигалкам т.к. это, по традиции, первое с чего следует начать. Тем не менее, это не повод отказаться от изучения данных микроконтроллеров и поделиться опытом с другими по тому что любой опыт может оказаться полезным. О том как установить и настроить среду разработки для STM32 а также о том как откомпилировать и загрузить программу в микроконтроллер есть отдельная статья -> https://electe.blogspot.com/2019/06/stm32.html. Однако не обязательно использовать именно такую среду разработки как в той статье т.к. со времени её создания появились более новые и такие же бесплатные как Coocox CoIDE среды, например STM32CubeIDE. Есть также платные среды вроде Keil, IAR и т.д. Также есть возможность адаптировать Arduino IDE для работы с STM32F103C8T6, использовать генератор кода для инициализации STM32CubeMX. Что даст более быстрый результат но меньшее понимание микроконтроллера и работы с ним. Помимо этого можно также выбрать более продуманный путь и не использовать IDE или написать свою IDE однако такой путь, в данном случае, будет весьма трудоемким и выбрав его есть риск "увязнуть" в подготовке и не начать программировать микроконтроллер никогда или не скоро. STM32CubeIDE и STM32CubeMX - это возможно лучшие варианты которые стоит использовать для постоянной работы с микроконтроллерами STM32. Однако Coocox CoIDE не изобилует большим количеством открытых окон (от которых рябит в глазах) после запуска и имеет системные требования подходящие для не крутых компьютеров а также про его установку уже есть статья. Также для лучшего изучения микроконтроллера желательно уметь настраивать его в коде по тому что это основной обычный способ настройки для большинства микроконтроллеров и в случае возникновения бага к нему непременно придется вернуться. Конечно это будет проблематично и возможно не оправданно для сложной периферии вроде USB но для простой мигалки и подобных вещей генератор кода не дает существенных преимуществ (если конечно вообще их дает). В качестве языка для создания первой программы на новом микроконтроллере конечно более предпочтителен Си т.к. на ассемблере простая мигалка уже будет не такой простой чтобы не вызвать затруднения отбивающие желание продолжать обучение. Ассемблер, само собой, очень важный язык к которому приходиться обращаться для оптимизации, устранения багов или реверс иженеринге (возможно есть и другие причины о которых можно написать в комментариях) но в большинстве случаев такой необходимости нет. В качестве библиотеки для первой программы выбрана CMSIS т.к. она делает минимальные начальные настройки и доступ к регистрам без сложных высокоуровневых "оберток отделяющих программиста от этих регистров". Чтобы подключить CMSIS при создании проекта в среде Coocox CoIDE нужно поставить галочку в чекбоксе с надписью "CMSIS_Boot" (или с аналогичной надписью) галочка с надписью "CMSIS_Core" при этом, установиться автоматически.


А также включить заголовочный файл, данной библиотеки, использовав директиву "include" в исходном коде который появиться после создания проекта.


Чтобы писать программу для микроконтроллера нужен документ с описанием всех его регистров. Для stm32f103c8t6 есть reference manual -  https://www.st.com/resource/en/reference_manual/cd00171190-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf.

В микроконтроллерах STM32, в отличии от AVR, прежде чем использовать какое либо периферийное устройство, нужно включить его тактирование. В данном микроконтроллере для этого есть два регистра APB1 и APB2. Чтобы помигать светодиодом нужно его сначала правильно подключить к какому нибудь пину на порту ввода-вывода общего назначения. Т.к. в микроконтроллерах STM32 есть замечательная возможность настроить пин как выход с открытым стоком то стоит испытать данный вариант. Его преимуществом перед обычным пушпульным выходом можно также назвать то что он более безопасен в случае например короткого замыкания между пинами (хотя если второй пин будет пушпульным и выдавать лог.1 то это не поможет) или пином и землей. Подключить светодиод к такому выходу можно например способом показанным на рисунке:


Можно также подключить светодиод между пином и землей а резистор между пином и плюсом питания и так он будет загораться при лог.1 на выходе но такой способ более энергозатратный и менее безопасный в случае случайного конфигурирования пина как пушпульный выход. Из рисунка выше видно что светодиод подключен к пину 9 порта B следовательно надо настроить тактирование порта B. Данная настройка осуществляется через регистр APB2:


-Это картинка из reference manual (см. ссылку выше) с выделенным (красным прямоугольником со скругленными краями) битом конфигурации тактирования порта B.

Т.е. для того чтобы включить тактирование порта B нужно записать 1 в бит 3. По умолчанию в этом бите находиться 0 но так бывает не всегда поэтому за этим надо следить чтобы делать правильные настройки. Чтобы сделать это в коде, можно использовать операцию присвоения с логическим или |= так в тех битах в которых уже установлена единица она останется а те в которых её нет она запишется. Можно также использовать простое присвоение и учитывать все единицы и нули сразу в 32ух разрядных микроконтроллерах это это делать проблематично ввиду большого количества битов за которыми надо следить не игнорируя те которые для решения текущей задачи не важны. После присвоения с логическим или можно использовать простые и наглядные двоичные числа например так:


Но из за того что битов 32 числа получаются длинными. Для сокращения можно использовать оператор побитового сдвига влево например так:


Это, всё ещё, не последний способ записать единицу в нужный бит но именно такой способ используется чаще всего, когда нужно видеть какой по счету бит устанавливается, из за его наглядности. В  строке после комментария "включить тактирование порта B" RCC - это указатель на структуру а APB2ENR - это поле данной структуры. Теперь чтобы настроить пин 9 порта B на выход с открытым коллектором нужен регистр GPIOB_CRH. В reference manual он обозначен как GPIOx_CRH где x - это буква порта (в нашем случае B):


Есть также регистр GPIOB_CRL где буква L указывает на то что этот регистр относиться к пинам с 0 по 7. Регистр же GPIOB_CRH относиться к пинам с 8 по 15 поэтому мы настраиваем его. Из мануала видно какие биты надо установить в единицу чтобы пин был с выходом с максимальной частотой 2МГц и открытым стоком (эти биты указаны стрелочками на картинке выше). При этом следует учитывать что не все биты по умолчанию установлены в 0. Это можно использовать и например не делать установку единицы бит 6 но если например нужно сделать пушпульный выход то нужно не забыть затереть этот бит записав в него ноль. В коде настройка выглядит так:


Теперь осталось сделать собственно мигание светодиодом. Т.е. установку в 1 пина 9 порта B (при этом светодиод гаснет т.к. верхний транзистор кмоп полумоста не работает и всегда закрыт а нижнему закрытым быть положено). Потом задержку. Специальной функции для задержки в библиотеке CMSIS нет, поэтому задержку нужно делать самому, для этого можно использовать пустой цикл. После чего сброс до 0 пина 9 порта B (светодиод при этом светиться т.к. нижний транзистор кмоп полумоста открывается). Для управления пином, в режиме выхода, есть три регистра, первые два из них можно использовать для установки в 0 или 1 а последний нужен только для сброса до 0 поэтому он не интересен. Первый называется GPIOx_ODR где x-буква порта (в нашем случае B). В этом регистре номер бита соответствует номеру пина (что очень удобно) поэтому для управления пиами можно даже не смотреть в мануал:


В коде мигалка будет выглядеть так:

Для сброса до нуля используется присвоение с логическим и которое выглядит так 
&=
также сдвиг в лево но на этот раз все биты инвертируются оператором "волнистая черта"
~
Можно также сделать это простым присваиванием но на, первых порах, важно рассмотреть базовый вариант установки конкретного бита. 
Второй регистр для управления пинами порта B называется GPIOB_BSRR. С ним работу следует вести по другому. Биты от 0 по 15 нужны для установки в 1 а оставшиеся для сброса до 0. Для установки 1 на пине 9 нужно в 9й бит записать единицу а если нужно установить лог. 0 на пине 9 то нужно в пин по номером 9+16=25 записать 1. Управление пином через данный регистр происходит быстрее чем через предыдущий. Мигалка, с использованием данного регистра, в коде может выглядеть так:

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



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



вторник, 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=

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