Программирование STM32. Часть 3: Система тактирования
Первое, с чем необходимо разобраться перед дальнейшим изучением микроконтроллеров STM32, является система управления тактированием и сбросом, именуемая RCC. В этой статье мы рассмотрим, как правильно настроить микроконтроллер на работу от внешнего кварцевого резонатора на 8 МГц. Все статьи цикла можно посмотреть тут: http://dimoon.ru/category/obuchalka/stm32f1
Система тактирования в STM32 в сравнении с микроконтроллерами AVR выполнена довольно замысловато. Давайте разбираться.
Содержание:
- Шины
- Генераторы
- Тактирование периферии
- Источники сигнала SYSCLK
- HSE и PLL
- Что еще?
- Заключение
У микроконтроллеров STM32 все периферийные устройства (порты ввода-вывода, таймеры, интерфейсы SPI, и т.д.) подключены к так называемым шинам, через которые периферия получает тактовый сигнал и обменивается данными с ведущими устройствами шины (например, с процессором).
В STM32F103x8 три основных шины: AHB, APB1 и APB2. На каждой из шин висит определенная группа устройств:
- AHB: процессорное ядро, память и DMA;
- APB1: USART2, USART3, I2C1/2, CAN, таймеры TIM2..4;
- APB2: порты GPIO, АЦП, USART1, TIM1, SPI1.
В даташите на STM32F103x8 есть блок-схема, в которой указано, какая периферия куда подключена:
Рис. 1. Блок-схема микроконтроллеров STM32F103x8 и STM32F103xB
Схема на рис. 1 поначалу может казаться сложной и непонятной, это нормально, со временем все в голове уложится и ощущение непонимания исчезнет.
А еще есть вот такая таблица, в которой так же указаны периферийные устройства и шины, к которым они подключены:

Рис. 2. Таблица шин и периферийных устройств
Можно заметить, что на рис. 2 возле названия шины (AHB, APB1 и APB2) в скобках указана ее максимальная частота. Так как периферийные устройства получают тактовый сигнал от шины, ее частота задает скорость работы подключенных к данной шине устройств. Далее мы рассмотрим, как настроить частоту каждой из шин микроконтроллера.
Еще одной особенностью системы тактирования STM32 является то, что после сигнала сброса микроконтроллера вся периферия находится в отключенном состоянии и на нее не подается тактовый сигнал. Это сделано с целью снижения энергопотребления всего микроконтроллера. Перед началом работы с любым периферийным устройством необходимо разрешить подачу на него тактового сигнала. Как это сделать рассмотрим далее.
Итак, вот основные тезисы, которые необходимо запомнить:
- Все периферийные устройства в микроконтроллерах STM32 подключены к шинам (AHB, APB1 и APB2), через которые производится взаимодействие с устройствами и подача на них тактовых сигналов;
- Шины микроконтроллера STM32 могут иметь разные частоты тактирования;
- Перед началом работы с периферийным устройством необходимо разрешить подачу на него тактового сигнала.
Генераторы
В микроконтроллерах STM32F103x8/B присутствует несколько генераторов тактового сигнала:

Рис. 3. Блок-схема системы тактирования, красными прямоугольниками выделены генераторы тактовых сигналов
Первый из них — встроенный RC-генератор на 8 МГц, который называется High-speed internal (HSI) RC oscillator. После сброса микроконтроллер по-умолчанию тактируется как раз от этого генератора. Основным его плюсом является то, что для работы генератора не нужны ни какие дополнительные внешние компоненты. Однако его минус — плохая стабильность генерируемой частоты: при изменении температуры окружающей среды его частота в 8 МГц будет немного плыть. Для нетребовательных ко временнЫм интервалам устройств это может быть не критично, но в некоторых случаях данная особенность является недопустимой.
Следующий — High-speed external (HSE). Этот генератор является альтернативой HSI. Для его работы нужен внешний кварцевый резонатор на частоту 4-16 МГц. Его главным преимуществом в сравнении с HSI является стабильность генерируемой частоты. Так же, при определенной настройке, вывод OSC_IN можно подключить к источнику готового прямоугольного тактового сигнала без использования резонатора.
Далее Low-speed external (LSE). Этот генератор так же требует внешнего кварцевого резонатора, но только на 32768 Гц. LSE используется только для тактирования встроенных часов реального времени RTC, с помощью которых можно вести отсчет текущего времени, если это нужно.
Последний генератор — это Low-speed internal (LSI) RC oscillator. Это встроенный RC-генератор на 40 КГц. Он не отличается особой точностью, однако у него есть очень важная задача: генерация тактового сигнала для сторожевого таймера МК, который перезапустит систему в случае зависания. А еще от LSI можно тактировать RTC, но скорее всего это ни кто делать не будет
Тактирование периферии
Процессорное ядро и основная часть периферии использует тактовый сигнал SYSCLK.

Рис. 4. Распределение тактового сигнала SYSCLK
После делителя AHB Prescaler тактовый сигнал распределяется между шинами микроконтроллера. Сигнал HCLK поступает в процессорное ядро, память и периферию шины AHB. FCLK так же идет в ядро. Через фиксированный делитель на 8 тактирование подается на системный таймер Cortex System timer. Делитель APB1 Prescaler формирует сигнал тактирования устройств шины APB1, а APB2 Prescaler для устройств APB2.
Тут правда есть небольшая особенность формирования тактового сигнала для таймеров и АЦП.

Рис. 5. Распределение тактового сигнала шины APB1 между устройствами

Рис. 6. Распределение тактового сигнала шины APB2 между устройствами
Тактовый сигнал на таймеры подается следующим образом. Если делитель шины (APB1 Prescaler или APB2 Prescaler) установлен в единицу, то частота тактирования тактирования таймеров (TIMXCLK или TIM1CLK) будет равна частоте шины. Но, если делитель не равен единице, то частота тактирования таймеров будет в 2 раза больше частоты шины (см. рис. 5, 6). Вот так А для АЦП есть свой собственный делитель, который из частоты тактирования шины APB2 формирует сигнал ADCCLK (рис. 6).
Думаю, следует еще обратить внимание на вот эти элементы блок-схемы:

Это есть ни что иное, как устройства подачи тактового сигнала на конкретную периферию (логические элементы 2И). Попробую перерисовать один из них так, чтоб было понятнее, что это и как оно работает:

У каждого периферийного модуля в специальном регистре есть свой бит (SPI1EN, IOPAEN, IOABEN и так далее), при установке которого в единицу разрешается подача на него тактового сигнала. На рис. 8 я привел пример только для тактового сигнала PCLK2 шины APB2, для остальных сигналов (HCLK, PCLK1, TIMXCLK, TIM1CLK) все то же самое.
Источники сигнала SYSCLK
Итак, теперь мы знаем, что основным тактовым сигналом в микроконтроллерах STM32 является SYSCLK. Давайте теперь разберемся, как его получить. В нашем распоряжении 3 варианта: генераторы HSI, HSE и модуль PLL:

Рис. 9. Источники сигнала SYSCLK
После сброса микроконтроллера в качестве источника сигнала SYSCLK по-умолчанию устанавливается встроенный RC-генератор HSI. Прохождение тактового сигнала для этого случая представлено на рис. 10, значения по-умолчанию всех делителей обвел кружочком:

Рис. 10. Конфигурация системы тактирования по-умолчанию
А теперь давайте посчитаем значения всех частот в конфигурации по-умолчанию. Частоты HCLK, FCLK, PCLK1, TIMXCLK, PCLK2, TIM1CLK будут равны 8 МГц, частота Cortex System timer равна 1 МГц, а ADCCLK 4 Мгц.
Если мы хотим задействовать HSE-генератор, то картина будет следующей:

Рис. 11. Выбор генератора HSE в качестве источника тактирования SYSCLK
При использовании кварцевого резонатора на 8 МГц все системные частоты будут такими же, что и в предыдущем случае. Разница только в одном: при использовании генератора HSE стабильность частот лучше, чем при использовании HSI. Однако, если мы хотим получить максимальную производительность всей системы, то нужно в качестве источника SYSCLK использовать блок умножения частоты PLL.
HSE и PLL
В микроконтроллерах STM32 модуль PLL может тактироваться как от HSI генератора, так и от HSE. Существует огромное количество вариантов настройки тактирования системы от PLL. Мы остановимся только на одном, в котором используется HSE и все коэффициенты настроены на максимальную производительность системы:

Рис. 12. Схема прохождения тактового сигнала при использовании PLL совместно с HSE
Кварцевый резонатор выбираем на 8 МГц. Далее, сигнал с HSE без деления (настраивается битом PLLXTPRE) поступает на селектор PLLSRC и потом на PLL. Для того, чтобы из 8-и МГц получить 72 МГц, коэффициент умножения PLL должен быть равен PLLMUL=9. Далее, сигнал с PLL частотой 72 МГц через селектор SW поступает на SYSCLK. Так как процессорное ядро мы хотим тактировать максимальной частотой в 72 МГц, AHB Prescaler устанавливаем равный единице (без деления). Для получения частоты шины APB1, равной 36 МГц, APB1 Prescaler ставим равным 2. Шина APB2 имеет максимальную частоту 72 МГц, следовательно, APB2 Prescaler можно установить в 1.
- Кварц HSE на 8 МГц
- PLLXTPRE: без деления
- PLLSRC: HSE генератор
- PLLMUL = 9
- SW = PLLCLK
- AHB Prescaler = 1
- APB1 Prescaler = 2
- APB2 Prescaler = 1
Что еще?
Здесь мы не рассмотрели еще некоторые блоки системы тактирования, о которых хочется упомянуть.
Clock security system (CSS) — переводится примерно как «система безопасности тактирования». Если, при использовании генератора HSE в качестве источника тактового сигнала для SYSCLK или PLL, произойдет срыв генерации HSE, то CSS автоматически переключит всю систему на работу от встроенного RC-генератора HSI. Таким образом, если что-то случится с кварцем, система не зависнет намертво в неопределенном состоянии, а сможет выполнить какие-то действия, например, перевести объект управления в безопасное состояние (закрыть все вентили, отключить силовые установки, и т.д.)
Модуль часов реального времени RTC может тактироваться от встроенного LSI генератора на 40 КГц, от HSE через делитель на 128, либо от LSE с внешним кварцем на 32768 Гц. Источник тактовых импульсов выбирается с помощью RTCSEL.
Модуль USB получает тактовый сигнал от PLL, причем при частоте на выходе PLL равной 72 МГц есть возможность активировать USB Prescaler с коэффициентом деления 1.5 для получения необходимой частоты 48 МГц.
Microcontroller clock output (MCO) — вывод микроконтроллера, на который можно вывести частоту от одного из источников сигнала: SYSCLK, HSE, HSI либо сигнал с выхода PLL, поделенный пополам. Нужный источник выбирается с помощью битов MCO.
Заключение
Итак, мы рассмотрели основные моменты в системе тактирования микроконтроллеров STM32 на примере STM32F103x8 и STM32F103xB. В других микроконтроллерах STM32 примерно все то же самое, за исключением некоторых нюансов. В следующей части мы познакомимся с регистрами системы тактирования и сброса RCC и рассмотрим пример инициализации RCC.
Ассемблер для микроконтроллера с нуля. Часть 4. Система адресации памяти, назначение выводов, тактирование и прерывания МК

↑ Система адресации регистров памяти данных
В предыдущих частях статьи мы время от времени, оформляя код примеров, записывали в шаблонные хидер-файлы те или иные макроопределения. Начиная с этой части статьи, процесс наполнения указанных файлов пойдёт особенно активно. В конце концов вам придётся записать в указанные файлы макроопределения всех регистров и номеров битов для выбранного МК. Учитывая их количество (особенно, в случае с Cortex M-4), необходимо определиться, как именно будут оформляться макроопределения с тем, чтобы содержимое хидер-файлов не превратилось в неудобочитаемое месиво.
Самый простой и естественный путь — взять за основу систему адресации регистров памяти данных, принятую в документации микроконтроллера.
↑ Система адресации регистров памяти данных AVR-8
Здесь всё просто: берём данные из Таблицы «Registers Summary» даташита (страница 200 и 309 для ATtiny85 и ATmega8, соответственно) и переносим их в хидер-файл.
Единственное, с чем вы должны определиться, какие именно адреса регистров будете использовать — абсолютные или относительные — поскольку дважды прописывать одно и то же имя макроопределения запрещено правилами.
С учётом того, что в последующих примерах и далее будут использоваться макросы setBit и clearBit, в рамках данной статьи в хидер-файлы AVR-8 будут вноситься макроопределения абсолютных адресов.
Например, записи для регистра ADCSRA модуля АЦП обоих МК будут выглядеть так:
↑ Система адресации регистров памяти данных STM32F401
Тема организации памяти STM32F401 отражена в Разделе «Memory and bus architecture» на странице 36 Reference manual.
Адресация регистров этого МК основана на системе граничных (boundary) адресов и смещений (offset).
Поясню, что это означает.
На каждый модуль в памяти данных выделен сектор, границы которого приведены в Таблице 1 «STM32F401x register boundary addresses» из указанного выше раздела. Чтобы понять, где именно внутри этого сектора находится адрес того или иного регистра, необходимо суммировать значения нижней границы сектора и смещения, определённого для данного регистра. Сводная информация по всем регистрам модуля, включая значение offset, приводится в Таблице «Register map», располагающейся, как правило, в самом конце раздела, посвящённого данному модулю.
В качестве примера на Рисунке 1 приведены выдержки из таблиц «STM32F401x register boundary addresses» и «Register map» (страница 164 Reference manual) для портов ввода-вывода.

Как видите, на каждый из шести портов ввода-вывода GPIO выделен сектор памяти данных, ограниченный адресами из столбца «Boundary address» верхней таблицы.
Режим работы порт вывода-вывода определяют несколько регистров настройки, одинаковых для всех GPIO, три из которых представлены в нижней части Рисунка 1.
Нетрудно понять, что значение адреса регистра MODER составляет:
• 0×40020000 + 0×00 = 0×40020000 для GPIOA,
• 0×40020400 + 0×00 = 0×40020400 для GPIOB,
а младшего AFRL из двух регистров AFR:
• 0×40021000 + 0×20 = 0×40021020 для GPIOE,
• 0×40021C00 + 0×20 = 0×40021C20 для GPIOH.
Однако, в вычислениях абсолютного адреса того или иного регистра нет необходимости, поскольку мы можем прописывать его в коде как сумму двух макроопределений: адреса порта и смещения регистра.
Подобный подход справедлив по отношению и к другим модулям МК.
С учетом изложенного, макроопределения для модуля GPIO будут оформляться нами в stm32f401.h нижеприведённым способом:
Как вы понимаете, заполнить пробелы, обозначенные многоточием, вам придётся самостоятельно.
Обратите внимание, что в регистрах MODER и AFR за ту или иную настройку отвечает не один бит, а группа из двух или даже четырёх битов.
Учитывая, что такое возможно и для других регистров, вы должны выбрать для себя один из двух вариантов макроопределений для битов группы:
1. Наделить макроопределением каждый бит группы (MODER_1_1, MODER_1_0, AFRL_5_3, AFRL_5_2, AFRL_5_1, AFRL_5_0 и т. п.) и работать с ними по-отдельности посредством макросов setBit и clearBit из macro.h.
При таком подходе запись единиц в биты 22 и 20 регистра AFRL порта B осуществляется следующим образом:
2. Дать макроопределение младшему биту группы битов, как мы это сделали выше, и обращаться к группе с помощью маски из двоичных чисел.
В этом случае пример выше будет выглядеть так:
↑ Система адресации регистров памяти данных nRF52832
Для nRF52832, аналогично с STM32F401, принята система адресации, согласно которой адрес регистра памяти данных складывается из двух составляющих: значения базового адреса модуля, к которому принадлежит регистр, и значения смещения, определённого для данного регистра.
На Рисунке 2 приведены выдержки из Таблиц 20, 30 и расшифровка назначения битов регистра PIN_CNF[0], ответственного за настройку пина P0.00 модуля GPIO (Раздел «GPIO» на странице 111 Product specification).

Как следует из рисунка, обращение к регистру PIN_CNF[0] осуществляется по адресу 0×50000000 + 0×700 = 0×50000700.
Оформим в хидер-файле nrf52832.h макроопределения представленных на Рисунке 2 регистров и битов модуля GPIO.
Вы наверняка заметили, что за некоторые настройки отвечает не один бит, а их группа. Кроме того, для группы битов с одним назначением в документации nRF52832 приводится не комбинация 0 и 1 значений этих битов, как это принято для AVR-8 и STM32F401, а десятичное число, которое выражает указанную комбинацию. В частности, для битов группы PULL столбец «Value» нижней таблицы из Рисунка 2 предусматривает для варианта «Pullup» запись числа 3 (т.е. комбинации 11) в указанные биты.
Всё это ставит вас, как и в случае с STM32F401, перед выбором порядка записи макроопределений для группы битов:
• либо дать макроопределение каждому биту (например, PULL_1, PULL_0) и устанавливать/сбрасывать его в коде с помощью макросов setBit/clearBit, соответственно:
• либо, как мы и поступили выше, прописать макроопределение младшего бита группы и записывать в него число из столбца «Value»:
↑ Назначение выводов микроконтроллера
В связи с тем, что число выводов микроконтроллера ограничено, каждый из них (за исключением специальных, вроде выводов питания для всех МК или антенны в случае с nRF52832) может иметь несколько назначений: дефолтное (т.е. — по умолчанию), определённое производителем, и альтернативные.
Нюансы настроек выводов при исполнении ими тех или иных функций будут рассмотрены в следующей части статьи, а пока выясним в общих чертах, каково их назначение (или распиновка) по умолчанию и как происходит его смена.
↑ Назначение выводов AVR-8
Подробно о назначении выводов обоих МК можно узнать из Раздела «Pin Configurations» на странице 2 даташита.
В случае с AVR-8 дефолтные и альтернативные функции жёстко привязаны производителем к определённым выводам.
На Рисунке 3 представлена распиновка ATtiny85 и ATmega8 с указанием дефолтного и некоторых дополнительных (в скобках) назначений выводов.

Как видите:
1. Вывод 1 обоих МК по умолчанию исполняет функцию входа Reset для подключения активно-низкой кнопки сброса.
2. Дефолтная функция всех остальных не специальных выводов — пин порта ввода-вывода (PB — пин порта B, РС — пин порта С, PD — пин порта D) для обмена цифровыми данными (логические 1 и 0) с внешними устройствами.
В большинстве случаев переход вывода в альтернативное состояние автоматически происходит после подключения и настройки того или иного модуля периферии МК. К примеру, выводы 7 ATtiny85 и 24 ATmega8, являясь по умолчанию пинами PB2 порта В и РС1 порта С, соответственно, при включении канала ADC1 модуля АЦП автоматически становятся входом для измерения аналогового напряжения. Отключение канала ADC1 АЦП вновь возвращает указанным выводам функцию пина порта.
Из всего многообразия функций выводов рассматриваемых нами МК есть несколько, реализация которых потребует доступа к так называемым фьюз-байтам, подробнее о которых вы узнаете в следующей главе:
а) Функция XTAL, когда выводы 2/3 ATtiny85 и 9/10 ATmega8 должны работать как входы для подключения внешнего кварцевого резонатора.
б) Альтернативные функции выводов 1 обоих МК.
↑ Назначение выводов STM43F401
Информация о дефолтных, альтернативных и дополнительных функциях выводов МК STM32F401 сведена в Таблицы 8 «Pin definitions» и 9 «Alternate functions mapping» на страницах 37 и 44 даташита, выдержки из которых для одного из выводов МК приведены на Рисунке 4.

Как следует из верхней таблицы Рисунка 4 вывод, который в зависимости от типа корпуса МК может иметь номер 13, E4, 17, 26 или L3, по сбросу/подаче питания становится пином РА3 порта А.
В качестве альтернативных для этого вывода могут быть следующие функции:
а) Линия RX модуля протокола UART.
б) Вход для внешнего сигнала тактирования таймеров 2, 5 и 9.
в) Выход для генерируемых FPU событий (EVENT).
В дополнение ко всему этому, указанный вывод может работать и как вход третьего канала аналого-цифрового преобразователя ADC1.
Так же, как и в случае с AVR-8, альтернативные и дополнительные функции жёстко привязаны к конкретным выводам STM32F401 и выбираются путём записи требуемых чисел в регистры MODER и один из двух AFR (AFRL или AFRH) модуля GPIO, (страница 158 Reference manual), представленных на Рисунке 5.

Как видите, основной выбор назначения осуществляется посредством записи того или иного числа в биты регистра MODER. Кроме того, в случае с альтернативной функцией необходимо через регистр AFRL выбрать требуемую, исходя из данных нижней таблицы на Рисунке 4.
Например, чтобы упомянутый выше 3-й пин порта A стал входом для измерения аналогового напряжения, необходимо записать в биты MODER3 (6 и 7) комбинацию 11.
Выбор альтернативного назначения для того же вывода обуславливается комбинацией 10 указанных битов регистра MODER. При этом, согласно Рисунка 4, для выбора функции входа тактирования таймера 2 (функция AF01) потребуется записать комбинацию 0001 в биты AFRL3 (12-15) регистра AFRL, а выбор функции линии RX протокола UART (функция AF07) осуществляется комбинацией 0111 тех же битов регистра AFRL.
↑ Назначение выводов nRF52832
Таблица «Pin assignment» назначений выводов этого МК представлена в разделе с одноимённым названием на странице 13 Product specification.
За исключением выводов специального назначения (питание, антенна, внешнее программирование и др.), оставшиеся 32 вывода по умолчанию исполняют роль пинов P0.00–P0.31 порта ввода-вывода. Помимо этого, большинство из указанных выводов могут быть использованы и для реализации альтернативных функций. К примеру, как видно из Рисунка 6, вывод 5 МК, работающий по умолчанию как пин Р0.03 порта ввода-вывода, автоматически переходит в режим аналогового входа AIN1 при включении модуля АЦП.
![]()
Если функция входа АЦП жёстко привязана к определённым выводам (4—7 и 40—43), то в отношении других альтернативных функций nRF52832 отличается от других рассматриваемых нами МК очень полезной особенностью: выбор вывода для реализации такой функции (например линии протоколов UART и SPI) — произвольный и производится через регистр PSEL соответствующего модуля.
↑ Тактирование микроконтроллера
Как вы помните из первой части статьи одним из важных узлов МК является генератор тактовых импульсов (ГТИ), частота которых определяет скорость работы процессора и модулей периферии.
В целом, системы тактирования рассматриваемых нами МК очень похожи:
1. Формируются тактовые импульсы базовой частоты FCPU, которые и подаются на процессор. В качестве элемента, определяющего FCPU могут выступать:
а) Внутренняя RC-цепочка.
б) Внешний кварцевый резонатор. Эта схема, по сравнению с предыдущей, обеспечивает менее зависимую от напряжения питания и температуры частоту тактовых импульсов, вследствие чего предпочтительна при разработке устройств, требующих высокой точности по времени.
2. Базовая частота путём деления уменьшается до частоты FPERIPHERAL тактирования периферии. Для одних МК такое деление происходит автоматически, а в случае с другими осуществляется посредством записи соответствующего числа в один из регистров микроконтроллера.
Зная частоту тактирования процессора вы можете рассчитать время исполнения той или иной инструкции (а следовательно — и всей программы), для чего необходимо:
а) Вычислить период одного тактового импульса, разделив единицу на FCPU.
б) Умножить полученное значение на количество тактов, которое требуется для исполнения той или иной инструкции и приведено в соответствующей графе Таблицы «Instructions Set Summary» (графа «Clocks» на странице 202/311 — для ATtiny85/ATmega8, соответственно, графа «Cycles» на странице 3-30 Technical Reference Manual — для Cortex M-4).
В отношении периферии так же полезно понимать, какова текущая частота её тактирования. К примеру, учитывая, что таймер увеличивает значение своего счётчика на 1 с каждым тактовым импульсом, несложно определить время, в течении которого он досчитает до заданного значения, умножив период тактирования таймера 1/FPERIPHERAL на указанное значение. Следовательно, мы можем с помощью таймера реализовать отсчёт временных интервалов с точностью до одного периода его тактирования.
Выясним нюансы настройки тактовой частоты для рассматриваемых нами МК.
↑ Тактирование для AVR-8
Для ATtiny85 и ATmega8 подробности работы и настройки системы тактирования можно узнать из Раздела «System Clock and Clock Options» на странице даташита 23 и 25, соответственно.
Производитель МК предлагает несколько источников тактирования (как внутренних, так и внешних), полный перечень которых можно найти в пункте «Clock Sources» вышеуказанного раздела.
Выбор схемы тактирования процессора и значении частоты FCPU тактовых импульсов осуществляется через CKSEL биты младшего из упоминавшихся выше фьюз-байтов, обращение к которым производится извне с помощью программы загрузки. В нашем случае это — avrdude.exe.
Добавим в шаблонные Makefile ещё несколько строк, приведя их к следующему виду:
Теперь ход работы make определяет одна из трёх комбинаций клавиш, набранных в консоли:
1. make/Enter будет по-прежнему запускать компиляцию программы и загрузку её в МК.
2. make rf/Enter создаст в папке проекта файлы lfuse.hex и hfuse.hex и запишет в них текущие значения младшего и старшего фьюз-байтов, соответственно, в шестнадцатеричном формате.
3. make wf/Enter запишет в младший и старший фьюз-байты значения LF_VALUE и HF_VALUE, соответственно.
Запустим посредством комбинации make rf/Enter чтение фьюз-байтов и получим дефолтные значения 0×62/0xdf и 0xe1/0xd9 младшего/старшего байтов для ATtiny85 и ATmega8, соответственно. Чтобы понять смысл этих чисел, можно пройти к Разделу «Memory Programming» даташита (страница 147 — для ATtiny85, страница 215 — для ATmega8) и разобраться в назначении каждого бита указанных байтов. Однако, гораздо проще и быстрее открыть калькулятор фьюзов , выбрать свой МК, ввести вышеприведённые числа в соответствующие окошки внизу и нажать кнопу «Apply values», получив в результате следующее:

Как видите:
1. В качестве ГТИ по умолчанию для обоих МК производителем выбран внутренний источник тактирования. Среди прочего, это означает, что выводы 3 ATtiny85 и 10 ATmega8 в качестве входов для подключения кварцевого резонатора не требуются и могут исполнять свою дефолтную или другие дополнительные функции.
2. В случае с ATmega8 частота FCPU тактирования процессора составляет 1МГц, а с ATtiny85 — 8МГц, но с делением на 8 через бит CKDIV8 младшего фьюз-байта. Следует отметить, что указанное деление для ATtiny85 является не преобразованием FCPU в FPERIPHERAL, а именно уменьшением значения самой частоты тактирования процессора. Возможность такого деления — особенность ATtiny85, которая может быть реализована не только через бит CKDIV8 фьюз-байта, но и программным путём через регистр CLKPR.
3. Галочка напротив бита RSTDISBL старшего фьюз-байта отсутствует, поэтому вывод 1 обоих МК будет работать в режиме входа Reset.
Чтобы изменить тип источника и частоту тактирования, необходимо выбрать их в калькуляторе фьюзов и появившимися в окошках снизу числами заменить LF_VALUE и HF_VALUE в Makefile, а затем набрать в консоли комбинацию make wf/Enter. Предположим, мы выбрали для обоих МК вариант тактирования с внешним кварцевым резонатором и частотой 8 МГц. Тогда, искомыми значениями младшего/старшего фьюз-байтов будут 0xcf/0xdf и 0xff/0xd9 для ATtiny85 и ATmega8, соответственно:

Обратите внимание, что для Attiny85 галочка напротив бита CKDIV8 снята, чтобы исключить деление FCPU на 8.
Поскольку в качестве источника тактирования выбрана схема с подключением внешнего кварцевого резонатора, то выводы 3 ATtiny85 и 10 ATmega8 теперь могут функционировать только как входы для подключения последнего. Вернуть им способность исполнять другие функции можно, вновь перейдя к внутреннему источнику тактирования.
Если выставить галочку напротив бита RSTDISBL, то после записи итоговых значений фьюз-байтов в МК вывод 1 обоих микроконтроллеров перестанет быть входом Reset и сможет выполнять свои дополнительные функции (пин порта и другие). Однако, не торопитесь это делать, если в вашем распоряжении нет высоковольтного программатора. Дело в том, что низковольтные программаторы, к коим относится и используемый нами USBasp, для общения с МК используют функцию сброса, отсутствие которой превратит микроконтроллер для USBasp в «кирпич», реанимировать который можно только с помощью упомянутого высоковольтного программатора. Поэтому, выставлять галочку напротив бита RSTDISBL следует только, если ваша программа, использующая дополнительные функции вывода 1, полностью отлажена и готова к окончательной загрузке в МК.
В следующей части статьи, на примере отдельных модулей МК, будет показано, как именно нужно делить FCPU для получения требуемого значения FPERIPHERAL.
↑ Тактирование для STM32F401
Система тактирования данного МК подробно описана в Разделе 6 «Reset and clock control (RCC)» на странице 91 Reference Manual и наглядно отображена на Рисунке 12 «Clock tree» из того же раздела.
В распоряжение программиста предоставлены три ГТИ:
1. HSI (High Speed Internal) с частотой FCPU 16 Мгц на базе внутреннего осциллятора.
2. HSE (High Speed External) от внешнего кварцевого осциллятора с частотой FCPU от 4 до 26 МГц.
3. Разогнанный до 84 МГц посредством ФАПЧ HSE под именем PLLCLK.
Выбор и настройка источников тактирования процессора и периферии осуществляется через регистры модуля RCC, для которого согласно упомянутой выше Таблицы 1 Раздела «Memory and bus architecture» Reference manual выделен сектор памяти данных с адресами 0×40023800 — 0×40023BFF.
Для выбора того или иного ГТИ необходимо записать соответствующее число в биты SW регистра RCC_CFGR, представленного на Рисунке 9.

Как видите, дефолтное значение регистра RCC_CFGR — нулевое, т. е. источником тактирования по умолчанию производитель определил внутренний HSI.
Здесь же, в RCC_CFGR, частоту FCPU можно разделить, записав требуемые числа в биты HPRE и PPRE, и уже после этого направить тактовые импульсы пониженной частоты FPERIPHERAL на шину тактирования модулей периферии. В свою очередь, для некоторых из модулей периферии частоту FPERIPHERAL можно дополнительно уменьшить делением. По умолчанию значение всех делителей равно 1, то есть FPERIPHERAL = FCPU = 16 МГц.
После того, как источник тактирования выбран, следует ещё и включить его, записав 1 в соответствующий бит регистра RCC_CR, изображённого на Рисунке 10.
![]()
Обратите внимание, что дефолтное значение регистра содержит 1 в младшем бите HSION, следовательно внутренний источник тактирования HSI не только выбирается по сбросу/подаче питания, но и автоматически включается.
В следующей части мы рассмотрим вопрос настройки периферии посредством записи требуемых чисел в тот или иной регистр. Однако, ни один из этих регистров не будет доступен для записи до тех пор, пока вы не включите тактирование самого модуля периферии. Осуществляется такое включение записью 1 в соответствующий бит одного из регистров всё того же модуля RCC.
В частности, за включение тактирования портов ввода-вывода A–E и H ответственны биты 0–4 и 7, соответственно, регистра RCC_AHB1ENR, как показано на Рисунке 11.
![]()
Тактирование таймеров общего назначения TIM2–TIM5 обеспечивается, как видно из Рисунка 12, записью 1 в биты 0-3, соответственно, регистра RCC_APB1ENR.
![]()
Включение же тактирования модуля аналого-цифрового преобразователя ADC1 обуславливается, как следует из Рисунка 13, битом 8 регистра RCC_APB2ENR.
![]()
Уверен, вы догадались, что по умолчанию тактирование всех упомянутых модулей периферии отключено, поскольку дефолтные значения ответственных регистров равны нулю.
Внесём в шаблонный файл stm32f401.h макроопределения адресов и номеров битов вышеупомянутых регистров модуля RCC:
и оформим код включения тактирования отдельных модулей периферии:
↑ Тактирование для nRF52832
Вся информация о тактировании этого МК представлена в Разделе «Clock — clock control» на странице 101 Product Specification.
В nRF52832 доступными для выбора программиста являются следующие источники тактирования процессора:
1. Внутренний осциллятор с частотой FCPU 64 МГц, являющийся ГТИ по умолчанию.
2. Внешний кварцевый резонатор с частотой 32 МГц, которая автоматически разгоняется до FCPU 64 Мгц.
Тактовый сигнал ГТИ с частотой FCPU поступает на контроллер HFCLCK, откуда перенаправляется на процессор. Кроме того, контроллер HFCLCK преобразует входящий сигнал в тактовые импульсы c тремя разными частотами FPERIPHERAL (1, 16 и 32 МГц), которые автоматически распределяет между модулями периферии. В частности, частота тактирования таймера составляет 16 МГц и при этом может дополнительно делиться уже через один из регистров самого таймера, о чём — в следующей части статьи.
Значения базового адреса модуля тактирования CLOCK и смещений некоторых задач и событий представлены на Рисунке 14.
![]()
Для выбора, например, схемы тактирования от внешнего источника необходимо запустить задачу TASK_HFCLKSTART посредством записи 1 по соответствующему адресу, а затем дождаться события EVENTS_HFCLKSTARTED, когда регистр со смещением 0×100 принимает значение 1, после чего необходимо обнулить указанный регистр.
Чтобы вновь вернуть внутренний осциллятор на роль ГТИ, потребуется запустить задачу TASK_HFCLKSTOP, записав 1 по адресу этой задачи.
Макроопределения вышеприведённых задач и событий, а также код запуска/выключения внешнего ГТИ будут выглядеть так:
nrf52832.h
↑ Прерывания микроконтроллера
Важным свойством микроконтроллера является способность его модулей генерировать прерывания по наступлению определённых событий.
В частности, это может быть прерывание:
а) свидетельствующее, что определённый пин порта ввода-вывода перешёл из одного логического состояния в другое (из 0 в 1 или наоборот). Такое прерывание обычно называют внешним, поскольку инициируется оно внешними по отношению к МК устройствами (кнопкой, цифровым датчиком и пр.).
б) генерируемое АЦП по завершению измерения. Как и следующее, это прерывание является внутренним в связи с тем, что его источник — внутренний модуль МК.
в) исходящее от таймера по достижении его счётчиком заданного значения.
Чтобы понять пользу от применения прерываний, представим устройство на базе микроконтроллера, которое должно:
• Включать/выключать красный светодиод в зависимости от состояния кнопки, подключённой к одному из выводов МК.
• Включать/выключать синий светодиод в зависимости от положения ручки потенциометра, подключённого к другому выводу МК.
• Обрабатывать массив каких-то данных.
Алгоритм основного цикла программы такого устройства без использования прерываний будет выглядеть следующим образом:
Подобная структура программы несёт в себе ряд недостатков:
1. Включение/выключение красного светодиода происходит без учёта его текущего состояния, следовательно будут иметь случаи включения уже включенного и выключения выключенного светодиода, т. е. бесполезной траты времени процессора.
2. Измерение аналогового напряжения не происходит мгновенно, следовательно на ожидание его завершения процессор так же непродуктивно будет расходовать какое-то конечное время.
3. Вследствие того, что все этапы процесса исполняются последовательно, неизбежна задержка реакции устройства на нажатие кнопки и поворот ручки потенциометра, величина которой будет зависеть от размера массива: чем дольше происходит его обработка, тем реже за единицу времени опрашиваются кнопка и потенциометр. При достижении же массивом критического размера процессор, будучи занятым его обработкой, может вообще пропустить факт нажатия кнопки.
Внесём в программу следующие дополнения:
а) Настройку внешнего прерывания от изменения состояния вывода, к которому подключена кнопки. Алгоритм кода обработчика этого прерывания будет следующим:
• Если произошёл переход состояния вывода с 0 на 1, включить красный светодиод.
• Если произошёл переход состояния вывода с 1 на 0, выключить красный светодиод.
б) Настройку таймера с тем, чтобы он выдавал прерывания с заданным периодом, к примеру, каждые 20 мс. В обработчике прерывания таймера пропишем запуск измерения АЦП.
в) Настройку АЦП на генерацию прерывания по завершению измерения, в обработчик которого перенесём действия устройства по включению/выключению синего светодиода.
Тогда, структура программы примет следующий вид:
Как вы помните из первой части статьи, при возникновении прерывания ЦПУ приостанавливает исполнение основной программы и отправляется по адресу памяти программ, где располагается соответствующий вектор прерывания, откуда перенаправляется к обработчику прерывания. Выполнив инструкции последнего, процессор возвращается к тому месту основной программы, где его застигло прерывание.
Учитывая изложенное, прикинем выигрыш от нововведений в программу:
1. Переход к обработчику внешнего прерывания происходит сразу по факту нажатия/отпускания кнопки, следовательно исключается риск пропуска этих событий, а величина задержки включения/выключения красного светодиода сводится к нескольким тактовым периодам, которые требуются процессору для перехода от основной программы к обработчику. Кроме того, применение прерывания избавляет нас от холостых включений/выключений красного светодиода.
2. Поскольку АЦП тактируется автономно, измерение аналогового напряжения происходит параллельно с обработкой массива процессором, не занимая попусту время последнего на ожидание завершения измерения.
3. Если время обработки массива не ограничено какими-либо рамками, т. е. позволяет прерываться на запуск измерений АЦП, можно уменьшить период счёта таймера, увеличив тем самым частоту измерений уровня аналогового напряжения, что, в свою очередь, уменьшит задержку реакции устройства на поворот ручки потенциометра.
Важно помнить, что, пока ЦПУ исполняет инструкции обработчика прерывания, все процессы, прописанные в основной программе приостанавливаются. Поэтому, при оформлении обработчика следует ограничиваться минимально возможным объёмом кода. Тем не менее, бывают ситуации, когда увеличение размера обработчика прерывания неизбежно. В частности, реализация приёма байта в программном варианте протокола UART, которым мы пользовались в предыдущих частях статьи, вряд ли возможна без применения внешних прерываний. При этом, обработчик должен содержать полный код приёма одного байта, иначе вы рискуете получить искажённые данные.
Для включение механизма прерываний требуется, как правило, разрешение на двух уровнях: глобальном и локальном.
В следующей части статьи мы подробно поговорим о работе наиболее часто используемых на начальных этапах обучения модулей микроконтроллера — порта ввода-вывода, таймера и АЦП, а пока остановимся лишь на вопросах настройки и оформления в коде прерываний указанных модулей для рассматриваемых нами МК.
↑ Настройка и оформление прерываний для AVR-8
Полная информация о прерываниях изложена в Разделах «Interrupts» и «External interrupts» даташита (страница 48 — для ATtiny85 и 46, 66 — для ATmega8).
Выберем к рассмотрению одинаковые для обоих МК прерывания:
а) Внешнее прерывание от состояния вывода INT0 (7 и 4 для ATtiny85 и ATmega8, соответственно). Следует отметить, что все внешние прерывания AVR-8 генерируются не зависимо от того, настроен соответствующий вывод как вход или выход.
б) Прерывания таймера TIMER0 по переполнению счётчика.
в) Прерывание АЦП по завершению измерения.
Начать следует с того, что глобальное разрешение — единое для всех прерываний AVR-8, включая и вышеперечисленные. Реализуется оно установкой в 1 бита I спец-регистра SREG посредством применения инструкции SEI, которую принято прописывать перед самым входом в цикл main_loop основной функции.
В случае, если по каким-то соображениям необходимо глобально приостановить механизм прерываний, используется инструкция CLI.
Локальные разрешение или запрет рассматриваемых нами прерываний обеспечиваются записью 1 или 0, соответственно, в:
а) одноимённый бит INT0 регистров GIMSK (ATtiny85) и GICR (ATmega8) в случае с прерыванием INT0,
б) бит TOIE0 регистра TIMSK в случае с прерыванием таймера TIMER0 по переполнению счётчика,
в) бит ADIE регистра ADCSRA в случае с прерыванием АЦП.
Для некоторых прерываний предусмотрены дополнительные настройки.
К примеру, комбинация битов ISC01 и ISC00 регистра MCUCR определяет условие генерации внешнего прерывания:
![]()
Вместе с генерацией каждого из рассматриваемых нами прерываний устанавливается в 1 соответствующий бит одного из регистров:
а) бит INTF0 регистра GIFR в случае внешнего прерывания,
а) бит TOV0 регистра TIFR для прерывания таймера TIMER0 по переполнению счётчика,
в) бит ADIF регистра ADCSRA по прерыванию АЦП.
Все указанные биты сбрасываются автоматически при входе в обработчик соответствующего прерывания либо вручную посредством записи в них единицы.
Перейдём к оформлению прерываний в коде.
Добавим в шаблонные хидер-файлы макроопределения:
• векторов рассматриваемых нами прерываний в соответствии с Таблицей «Reset and Interrupt Vectors» даташита,
• абсолютных адресов регистров и номеров их битов, ответственных за разрешение и настройку прерываний, ориентируясь на Таблицу «Register Summary» даташита.
Далее, по аналогии с вектором сброса, необходимо посредством директивы .org разместить в нужном месте памяти программ инструкцию перехода RJMP с именем обработчика прерывания в качестве аргумента.
И здесь в случае с AVR-8 есть один важный нюанс.
В вышеупомянутой таблице векторов прерываний даташита указаны адреса регистров памяти программ, т. е. двух-байтных слов, в то время как директива .org в GCC оперирует адресами байтов и в качестве аргумента ей требуется значение адреса младшего байта вектора прерывания. Взаимосвязь между двумя вариантами адресации продемонстрирована на Рисунке 16.

Как видите, чтобы получить значение адреса младшего байта двух-байтного слова, необходимо значение адреса самого слова умножить на 2.
С учётом изложенного, в качестве аргумента директивы .org следует использовать удвоенное значение макроопределения вектора прерывания. При оформлении вектора сброса мы не использовали умножение, поскольку значение его адреса — 0×00 и указанная операция не имеет смысла.
Прописываются директива .org и инструкция RJMP в main.S после таковых для вектора сброса в последовательности, с которой вектора следуют в Таблице «Reset and Interrupt Vectors».
Обработчик прерывания размещается ниже основной функции или в отдельном ассемблер-файле, вместе с функциями настройки и работы модуля, генерирующего данное прерывание.
К оформлению обработчика предъявляются те же требования, что и для обычной функции, за одним исключением: возврат из него осуществляется посредством инструкции RETI, а не RET.
Название обработчика — произвольно, однако, придерживаясь общепринятых негласных правил, будем комбинировать его из имени вектора и слова «Handler»: INT0_Handler, ADC_Handler и т. п.
Чтобы не дублировать примеры кода, применим по отношению к регистрам GIMSK и GICR двойное макроопределение (когда одно макроопределение выступает аргументом второго), прописав его в самом верху main.S.
Тогда, настройка и разрешение рассматриваемых нами прерываний, а также их обработчики, оформленные в main.S, будут выглядеть следующим образом:
Как видите, в примере представлен вариант, когда в обработчиках прерываний лишь устанавливается в 1 соответствующий бит переменной flags, объявленной в секции данных, а уже в цикле main_loop состояние указанных битов должно анализироваться с принятием соответствующего решения по каждому случаю.
↑ Настройка и оформление прерываний для Cortex M-4
Прежде, чем перейти к особенностям каждого МК в вопросах настройки и оформления прерываний, обсудим общие для них моменты.
Глобальный контроль за прерываниями для STM32F401 и nRF52832 осуществляется через модуль ядра, именуемый Nested vectored interrupt controller (NVIC), информацию о котором вы найдёте в разделе с одноимённым названием на странице 6-60 Cortex-M4 Technical Reference Manual.
В отличии от AVR-8, глобальные разрешение и запрет в Cortex M-4 осуществляются не оптом, а персонально для каждого отдельного прерывания или, в крайнем случае, группы однотипных прерываний (например, внешние прерывания nRF52832) через соответствующий бит 32-битных регистров ISER и IСER, соответственно, контроллера NVIC, коих — по восемь каждых:
• ISER0 — ISER7 с адресами от 0xE000E100 по 0xE000E11C,
• IСER0 — IСER7 с адресами от 0xE000E180 по 0xE000E19C.
Теоретически, такое количество регистров позволяет контролировать 32×8 = 256 прерываний, хотя в рассматриваемых нами МК их намного меньше.
Для STM32F401 номера битов указанных регистров связаны со значением числа из столбца «Position» Таблицы 38 «Vector table» на странице 202 Reference manual, выдержка из которой приведена на Рисунке 17.
![]()
Например, глобальные разрешение/запрет внешнего прерывания EXTI0 порта ввода-вывода и прерывание АЦП реализуются записью единицы в биты 6 и 18 регистров ISER0/IСER0, соответственно.
Число 50 превышает количество битов в регистрах ISER0/IСER0, поэтому прерывание таймера TIM5 глобально контролируется битом 50 — 32 = 18 регистров ISER1/IСER1.
Для nRF52832 подобная связь существует между битами регистров ISER/IСER контроллера NVIC и идентификационным номером ID модуля периферии. Полный перечень указанных номеров представлен в Таблице 11 «Instantiation table» на странице 24 Product specification, выдержку их которой вы можете наблюдать на Рисунке 18.
![]()
Как видите, значения ID всех трёх модулей — порта ввода-вывода, АЦП и таймера TIMER2 — меньше 32, поэтому контроль за их прерываниями осуществляется через биты регистров ISER0/IСER0, а именно 6-й, 7-й и 10-й.
Номер позиции прерывания в STM32F401 и ID в nRF52832 можно использовать для вычисления адреса вектора прерывания, что особенно актуально в случае с МК nRF52832, в документации которого эти данные по какой-то причине отсутствуют.
Дело в том, ARM зарезервировала под прерывания и исключения ядра шестнадцать младших адресов пространства векторов. Учитывая, что длина адресов в Cortex M-4 составляет 4 байта, адреса остальных векторов прерываний рассчитываются по формуле:
где, n — номер позиции прерывания и ID для STM32F401 и nRF52832, соответственно.
Например, адрес вектора прерывания EXTI0 STM32F401 равен (16 + 6) х 4 = 88 или 0×58 в шестнадцатеричной системе счисления, что подтверждается столбцом «Address» таблицы из Рисунка 17.
Адреса же прерываний nRF52832 из Рисунка 18 согласно формуле выше составляют:
• 0×58 — для внешних прерываний от GPIO,
• 0×5C — для прерывания АЦП,
• 0×68 — для прерываний таймера TIMER2.
Генерация любого прерывания в обоих МК сопровождается установкой в 1 определённого бита одно из регистров. Сброс указанного бита осуществляется не автоматически, как в случае с AVR-8, а вручную, посредством записи единицы или нуля (в зависимости от регистра и МК) в тот же бит при входе в обработчик прерывания. Делать это надо обязательно, иначе ЦПУ зациклится между вектором и обработчиком прерывания.
Оформление векторов прерываний для обоих МК производится точно так же, как мы это делали во второй части статьи для вектора сброса: посредством директив .org с адресом вектора прерывания в качестве аргумента и .word с аргументом в виде имени обработчика прерывания и приплюсованной к нему единицы.
Размещаться вектора должны в main.S вслед за вектором сброса в том же порядке, в котором следуют позиции или ID на Рисунке 17 или 18, соответственно.
Требования к оформлению обработчика прерывания полностью совпадают с описанными для обычной функции в предыдущей части статьи. Как и в случае с AVR-8, в названии обработчика будем использовать комбинацию из имени соответствующего вектора и слова «Handler»: EXTI0_Handler, TIMER2_Handler и т. д.
Добавим в шаблонные хидер-файлы макроопределения вышеупомянутых регистров модуля NVIC и их битов, а также адреса векторов представленных на Рисунках 17, 18 прерываний:
Тогда, вектора прерываний с пустыми обработчиками и их глобальное разрешение/запрет, оформленные в main.S, будут выглядеть следующим образом:
В отличие от AVR-8, для работы внешних прерываний STM32F401 и nRF52832 необходимо настроить соответствующий пин порта ввода-вывода как вход. Однако, поскольку по умолчанию все не специальные выводы обоих МК и без того являются входами, в нижеследующих примерах код соответствующей настройки будет опущен тем более, что вопросы конфигурирования портов ввода-вывода будут подробно рассмотрены в следующей части статьи.
↑ Внешние прерывания STM32F401
Для всех шести портов ввода-вывода этого МК, предусмотрены внешние прерывания EXTI0–EXTI15 от пинов 0–15, соответственно, в настройке которых принимают участие два модуля:
1. Модуль External interrupt/event controller (EXTI), подробно описанный в Разделе «Interrupts and events» на странице 202 Reference manual.
Младший адрес выделенного под этот модуль сектора памяти данных — 0×40013C00.
Для доступа к регистрам модуля EXTI включение тактирования через модуль RCC не требуется.
2. Модуль System configuration controller (SYSCFG) с младшим адресом 0×40013800 в памяти данных, информацию о котором вы найдёте в разделе с одноимённым названием на странице 140 Reference manual.
Запись в регистры SYSCFG невозможна без предварительного включения его тактирования посредством четырнадцатого бита SYSCFGEN ранее упоминавшегося регистра APB2ENR модуля RCC.
Выдержка из таблиц расшифровки назначений битов для регистров указанных модулей приведена на Рисунке 19.

Обратите внимание, что под названием каждого регистра указано смещение его адреса относительно нижней границы сектора памяти данных, выделенного под данный модуль согласно Таблицы 1 «STM32F401x register boundary addresses».
Как следует из Рисунка 19, для настройки генерации внешнего прерывания EXTI0 от ниспадающего фронта сигнала на пине PB0 порта B необходимо как минимум:
а) Включить тактирование модуля SYSCFG и записать комбинацию 0001 в биты 0—3 его регистра EXTICR1, выбрав тем самым пин PB0 как вход для прерывания.
б) Выбрать в качестве триггера прерывания ниспадающий фронт сигнала, установив в 1 бит TR0 регистра EXTI_FTSR модуля EXTI. Следует отметить, что в число регистров этого модуля входит также и EXTI_RTSR, посредством которого можно выбрать в качестве триггера прерывания нарастающий фронт сигнала на пине МК.
в) Записать 1 в бит MR0 регистра EXTI_IMR модуля EXTI, сбросив тем самым маску прерывания и, по сути, разрешив его локально.
Далее необходимо разрешить прерывание глобально так, как было показано выше.
С генерацией прерывания EXTI0 устанавливается в 1 бит PR0 регистра EXTI_PR модуля EXTI, который, как вы помните, должен быть сброшен в обработчике прерывания записью в него единицы.
Добавим к уже имеющимся макроопределения впервые упомянутых регистров и их битов в stm32f401.h:
и с учётом вышеизложенного, оформим фрагменты настройки внешнего прерывания EXTI0 и сброса в обработчике прерывания бита PR0:
↑ Прерывания таймера STM32F401
В качестве примера рассмотрим настройку прерывания по достижению заданного значения счётчиком таймера общего назначения TIM5, описанного в Разделе «General-purpose timers (TIM2 to TIM5)» на странице 316 Reference manual.
Для модуля таймеров общего назначения TIM2—TIM5 в памяти данных STM2F401 выделены сектора, граничные значения адресов которых вы можете увидеть в верхней части Рисунка 20.

Регистры, ответственные за настройку прерываний указанных таймеров, представлены выдержкой из Таблицы «Register map» (страница 374 Reference manual) в нижней части Рисунка 20.
Как вы помните из п. 3.2 настоящей части статьи, первым делом необходимо через модуль RCC включить тактирование таймера, чтобы иметь доступ к его регистрам.
Далее следует:
1. Записать в регистр ARR значение счётчика (например, 2500), по достижению которого будет генерироваться прерывание.
2. Разрешить обновление содержимого ARR после каждого прерывания, установив в 1 бит UG регистра EGR.
3. Разрешить прерывание локально посредством записи 1 в бит UIE регистра DIER.
4. Разрешить прерывание глобально.
Кроме того, в обработчике прерывания необходимо обеспечить сброс бита UIF регистра SR через запись нуля в указанный бит.
↑ Прерывания АЦП STM32F401
Заглянув в Таблицу «STM32F401x register boundary addresses», вы увидите, что под модуль аналого-цифрового преобразователя ADC1 этого МК выделен сектор памяти данных в границах адресов 0×40012000—0×400123FF.
На Рисунке 21 представлены регистры, имеющие отношение к прерыванию АЦП по завершению измерения (подробнее, смотрите в Разделе «Analog-to-digital converter (ADC)» на странице 213 Reference manual).
![]()
Для настройки указанного прерывания помимо описанного выше глобального, необходимо обеспечить локальное разрешение через запись единицы в бит EOCIE регистра CR1, не забыв предварительно включить тактирование АЦП.
Бит EOC регистра SR сбрасывается в обработчике прерывания посредством записи в него нуля.
↑ Внешние прерывания nRF52832
Внешние прерывания в nRF52832 организуются через так называемые события (events) порта, подробнее о чём можно узнать из Раздела «GPIOTE — GPIO tasks and events» на странице 157 Product specification.
Всего, можно реализовать восемь внешних прерываний (с номерами от 0 по 7), выбрав для каждого в качестве входа любой из 32 пинов порта ввода-вывода.
На Рисунке 22 отображена информация о базовом адресе модуля GPIOTE и регистрах, ответственных за настройку внешних прерываний.

Чтобы, к примеру, настроить внешнее прерывание с номером 0 по ниспадающему фронту сигнала на пине P0.2, необходимо:
1. Через регистр CONFIG[0]:
а) выбрать режим «Event», записав число 1 в группу битов MODE,
б) выбрать указанный пин как вход прерывания, записав число 2 в группу битов PSEL,
в) выбрать в качестве триггера прерывания ниспадающий фронт сигнала, записав число 2 в группу битов POLARITY.
2. Разрешить прерывание локально, установив в 1 бит IN0 регистра INTENSET. Локальный запрет на прерывание обеспечивается записью 1 в такой же бит регистра INTENCLR.
3. Разрешить прерывание глобально.
Сброс флага прерывания обеспечивается записью нуля по адресу события EVENTS_IN[0].
↑ Прерывания таймера nRF52832
Информация о таймерах nRF52832 и настройке их прерываний изложена в Разделе «TIMER — Timer/counter» на странице 234 Product specification.
Все пять таймеров этого МК могу генерировать от 4 до 6 прерываний по достижению счётчиком каждого из четырёх (или шести) заданных значений.
Выдержки из таблиц указанного выше раздела для таймера TIMER2 приведены на рисунке 23.

Настройка одного из четырёх возможных для TIMER2 прерываний по совпадению осуществляется следующим образом:
1. В регистр CC[0] Записывается значение совпадения (например, 2500).
2. Прерывание локально разрешается посредством записи 1 в бит COMPARE0 регистра INTENSET. Запретить данное прерывание локально можно записав 1 в тот же бит регистра INTENCLR.
3. Через модуль NVIC обеспечивается глобальное разрешение прерывания.
Для корректной работы прерывания необходимо в обработчике прерывания сбрасывать флаг события EVENTS_COMPARE[0] записью нуля по соответствующему адресу.
↑ Прерывания АЦП nRF52832
Подробности работы аналого-цифрового преобразователя, включая настройку прерываний, приведены в Разделе «SAADC» Product specification на странице 357.
Этот модуль nRF52832 может генерировать несколько прерываний, в т. ч. по:
• запуску АЦП,
• переполнению буфера для хранения результатов измерений,
• завершению измерения,
• записи результата измерений в ОЗУ,
• завершению калибровки АЦП.
• остановке АЦП.
Информация об адресах и регистрах, связанных с одним из вышеперечисленных прерываний — по записи результата измерений в ОЗУ — приведена на Рисунке 24.

Для настройки этого прерывания достаточно:
1. Разрешить его локально, установив в 1 бит RESULTDONE регистра INTENSET. Запись 1 в такой же бит регистра INTENCLR обуславливает локальный запрет на данное прерывание.
2. Обеспечить глобальное разрешение прерывания.
Флаг прерывания сбрасывается записью нуля по адресу события EVENTS_ RESULTDONE.
↑ Файлы
Наш файловый сервис предназначен для полноправных участников сообщества «Datagor Electronics».
Тактирование микроконтроллера
Тактовая частота микроконтроллера это количество тактов в секунду которые выполняет микроконтроллер. Получается чем больше тактовая частота МК тем большее количество операций он может выполнить за 1 секунду.
К примеру тактовая частота в 8 МГц означает что МК за 1 секунду выполнить 8000000 различных операций таких как сложение, вычитание, запись данных в регистры и чтение данных т.д. Такая частота кажется довольно большой но как показывает практика это далеко не так, к примеру частота тактирования современных процессора компьютера составляет уже от 2 ГГц, а некоторые модели уже имеют частоту в 4 ГГц.
- использовать внутренний RC-генератор;
- использовать внешний кварц;
- использовать внешний генератор;
- использовать RC-цепочку.
Давайте в вкратце рассмотрим каждый из этих способов тактирования микроконтроллера. Выбор того или иного способа осуществляется путем выставления фьюз битов микроконтроллера.
Тактирование микроконтроллера от внутреннего RC генератора
При этом способе нам не нужно подключать какие либо внешние детали. Тактирование осуществляется от RC генератора который находится внутри и который может работать на частотах 1,2,4,8 МГц. А если вам требуется какая нибудь другая частота, то этот способ не подходит. Так же этот способ не подойдет если вам нужны точные интервалы времени, дело в том что задающая частота RC генератора плавает в зависимости от температуры.
Тактирование микроконтроллера от внешнего кварцевого резонатора
Этот способ несколько сложнее, для того чтобы его подключить нам нужно знать расположение выводов микроконтроллера и их название, далее находим выводы «XTAL1» и «XTAL2».
Схема подключения кварцевого резонатора к микроконтроллеру
Емкость конденсаторов должана быть в интервале от 15 до 22 пФ, один вывод подсоединятеся к резонатору а другой к земле.
Тактирование микроконтроллера от RC-цепочки
В представленной схеме тактирования конденсатор берется емкостью не менее 22 пФ, а резистор от 10 Ом до 100 кОм. Внешний RC генератор так же как и внутренний имеет нестабильную частоту которая «плавает » в зависимости от температуры. Но если в проектах не нужна стабильная частота то можно использовать эти два способа.
Частота тактирования рассчитывает по формуле
F-это частота в Герцах;
R- номинал сопротивления в Ом;
C — емкость конденсатора в Фарадах.
По дефолту все МК AVR (за исключением страх AT90S2313 и AT90S8535 и другие) настроены так чтобы испольщовать внутренний тактовый генератор. Получается как только мы подаем на него напряжение то МК сразу начинает работать.
Тактирование системы
Если вы проектировали схемы тактовых генераторов для микропроцессоров, то вы будете приятно удивлены, когда начнете разрабатывать такие схемы для микроконтроллеров. Микроконтроллеры построены так, что требуют минимального числа внешних элементов для генерации тактовых импульсов.
Большинство микроконтроллеров способны работать в очень широком диапазоне частот: от нуля до десятков мегагерц. Это возможно благодаря использованию полностью статической логике. Некоторые разработчики используют тактовую частоту в 1 Гц и менее при отладке программного обеспечения. Широкий диапазон возможных рабочих частот позволяет разработчику лучше настроить микроконтроллер на выполнение конкретных заданных функций.
Существует три способа задания тактовой частоты микроконтроллера, каждый из которых имеет свои достоинства и недостатки. Первый способ — использование кварцевого резонатора, подключенного согласно схеме на рис. 2.12. Этот способ позволяет очень точно задать тактовую частоту микроконтроллера (разброс частот обычно составляет не более 0,01%). Такой уровень точности требуется для организации интерфейса микроконтроллера с другими устройствами или обеспечения точного хода часов реального времени.
Номиналы емкостей конденсаторов в данной схеме определяются производителем микроконтроллера для конкретной резонансной частоты кварца. Иногда требуется включить резистор большого номинала (порядка нескольких МОм) между выводами Clk0 и Clk1, чтобы генератор работал стабильно. Часто производитель советует использовать переменный конденсатор, подключаемый к выводу Clkl, чтобы обеспечить возможность точной подстройки частоты.

Рис. 2.12 — Тактирование с использованием кварцевого резонатора.
Многие разработчики не любят использовать в своих схемах какие-либо переменные элементы. Если разброс частот не соответствует заданным требованиям, то можно использовать другие схемы синхронизации.
При использовании малых емкостей тактовые импульсы будут иметь лучшую форму. Если вы включите слишком большие емкости, то это приведет к деградации формы импульсов, и микроконтроллер не будет запускаться. Если вы не уверены в правильности выбранных значений емкостей, то проверьте форму тактовых импульсов при помощи осциллографа. Их форма должна быть похожа на верхний рисунок 2.13.
Если схема перегружена из-за включения слишком большой емкости, то форма импульсов будет похожа на нижний рисунок 2.13, или вместо импульсов вообще будет постоянное напряжение на уровне примерно Vdd/2.

Рис. 2.13 — Хорошая и плохая формы импульсов для микроконтроллера PICMicro.
Два основных недостатка этого способа синхронизации – необходимость подключения дополнительных компонентов и хрупкость кристаллов кварца Оба этих недостатка могут быть устранены, если использовать керамический резонатор. Керамические резонаторы существенно более стойки к ударной нагрузке, и многие из них имеют встроенные конденсаторы, вследствие чего количество требуемых внешних компонентов уменьшается с трех до одного. Керамические резонаторы обычно имеют разброс частот порядка нескольких десятых процента (около 0,5%).
Следующий способ синхронизации — использование RC-генератора. В этом случае необходимая частота тактовых импульсов задается путем соответствующего выбора постоянной времени RC-цепи (рис 2.14). Это самый дешевый способ задания частоты, но, к сожалению, наименее точный. Если кварц обеспечивает поддержание частоты с точностью в тысячные доли процента, керамический резонатор — в десятые доли процента, то RC-цепь дает точность порядка десятков процентов. Так, при экспериментах с микроконтроллерами PIC было установлено, что точность RC-генератора составляет примерно 20%. Очевидно, что это неприемлемо для многих приложений, где требуется точный подсчет времени. Однако имеются области применения, где такая точность является вполне достаточной.

Рис. 2.14 — Подключение RC-цепи для генерации тактовых импульсов.
Нет общей формулы расчета номиналов резистора и конденсатора для этого типа генератора. Причиной этого являются нелинейные внутренние цепи микроконтроллера. Значения R и С, обеспечивающие требуемую частоту синхронизации, можно найти в руководстве по применению микроконтроллера.
Большое преимущество этого способа синхронизации частоты — низкая цена. Точность задания тактовой частоты можно повысить, используя переменные резистор или конденсатор. Однако при этом сложность и цена генератора увеличатся.
Другой способ синхронизации — это подача тактовых импульсов от внешнего генератора. Как указано выше, микроконтроллеры работают в широком диапазоне частот. При помощи внешнего тактового генератора можно задать любую частоту синхронизации.
Некоторые микроконтроллеры содержат встроенные RC или кольцевые генераторы, которые позволяют микроконтроллеру работать без внешних схем синхронизации. Работа внутреннего генератора обычно разрешается путем соответствующего программирования регистра конфигурации, которое производится вместе с программированием памяти программ микроконтроллера.
