Что такое регистры в микроконтроллерах STM32. Как настроить и использовать регистры для управления периферией. Какие основные типы регистров существуют в STM32. Как работать с регистрами с помощью библиотеки CMSIS.
Что такое регистры в микроконтроллерах STM32
Регистры в микроконтроллерах STM32 представляют собой специальные ячейки памяти, которые используются для управления различными функциями и периферийными устройствами. Каждый регистр имеет размер 32 бита и свой уникальный адрес в памяти микроконтроллера.
Основные характеристики регистров STM32:
- Размер 32 бита
- Уникальный адрес в памяти
- Используются для настройки и управления периферией
- Позволяют считывать состояние и записывать управляющие значения
- Группируются по функциональным блокам (GPIO, USART, TIM и т.д.)
Основные типы регистров в микроконтроллерах STM32
В STM32 можно выделить следующие основные типы регистров:
- Регистры настройки — используются для конфигурирования периферийных устройств
- Регистры управления — позволяют управлять работой периферии
- Регистры состояния — содержат информацию о текущем состоянии
- Регистры данных — хранят передаваемые/принимаемые данные
- Регистры прерываний — настройка и управление прерываниями
Каждый функциональный блок микроконтроллера (GPIO, USART, ADC и т.д.) имеет свой набор регистров для настройки и управления.

Как получить доступ к регистрам STM32
Существует несколько способов обращения к регистрам STM32 в программе:
- Прямой доступ по адресу регистра
- Использование структур и макросов из CMSIS
- Применение драйверов периферии от производителя
Рассмотрим подробнее каждый из этих методов.
Прямой доступ к регистрам по адресу
При прямом доступе используется числовой адрес регистра в памяти микроконтроллера. Например:
// Установка бита 13 в регистре GPIOC_ODR (включение светодиода на PC13)
*(volatile uint32_t *)0x4001100C |= (1 << 13);
Этот метод не рекомендуется использовать, так как снижает читаемость кода и может привести к ошибкам.
Использование структур и макросов CMSIS
Библиотека CMSIS предоставляет удобные структуры и макросы для доступа к регистрам:
// Включение тактирования GPIOC
RCC->AHBENR |= RCC_AHBENR_GPIOCEN;
// Настройка PC13 на выход
GPIOC->MODER |= GPIO_MODER_MODER13_0;
// Включение светодиода на PC13
GPIOC->ODR |= GPIO_ODR_13;
Этот способ более удобен и менее подвержен ошибкам.

Использование драйверов периферии
Производители микроконтроллеров часто предоставляют готовые драйверы для работы с периферией, которые инкапсулируют работу с регистрами:
// Инициализация GPIO GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); // Включение светодиода HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
Этот метод наиболее прост в использовании, но может иметь избыточность для простых задач.
Основные регистры для работы с GPIO в STM32
Рассмотрим основные регистры, используемые для работы с портами ввода-вывода общего назначения (GPIO):
- MODER — настройка режима работы выводов (вход/выход/альтернативная функция)
- OTYPER — настройка типа выхода (push-pull/open-drain)
- OSPEEDR — настройка скорости работы выводов
- PUPDR — подключение подтягивающих резисторов
- IDR — регистр входных данных (только чтение)
- ODR — регистр выходных данных
- BSRR — регистр установки/сброса битов
- LCKR — блокировка конфигурации выводов
Каждый из этих регистров выполняет определенную функцию при настройке и управлении портами ввода-вывода.

Порядок настройки GPIO на примере управления светодиодом
Рассмотрим последовательность действий для управления светодиодом, подключенным к выводу PC13:
- Включение тактирования порта GPIOC
- Настройка вывода PC13 на выход
- Управление состоянием светодиода
Код для выполнения этих действий с использованием CMSIS:
// Включение тактирования GPIOC
RCC->AHBENR |= RCC_AHBENR_GPIOCEN;
// Настройка PC13 на выход
GPIOC->MODER |= GPIO_MODER_MODER13_0;
// Включение светодиода
GPIOC->ODR |= GPIO_ODR_13;
// Выключение светодиода
GPIOC->ODR &= ~GPIO_ODR_13;
Этот пример демонстрирует базовую последовательность действий при работе с регистрами GPIO.
Особенности работы с регистрами в STM32
При работе с регистрами STM32 следует учитывать некоторые особенности:
- Атомарность операций — некоторые регистры требуют атомарной записи
- Регистры только для чтения — некоторые регистры нельзя модифицировать
- Сброс регистров — при сбросе микроконтроллера регистры принимают значения по умолчанию
- Зарезервированные биты — некоторые биты в регистрах зарезервированы и не должны изменяться
Учет этих особенностей позволяет избежать ошибок при программировании микроконтроллеров STM32.

Отладка программ с использованием регистров STM32
Для отладки программ, работающих с регистрами STM32, можно использовать следующие инструменты и методы:
- Встроенные отладчики в среды разработки (например, Keil uVision, STM32CubeIDE)
- Просмотр значений регистров в реальном времени
- Использование точек останова на обращениях к регистрам
- Логирование значений регистров
Эти инструменты позволяют эффективно отлаживать программы, работающие на низком уровне с регистрами микроконтроллера.
Заключение
Понимание принципов работы с регистрами STM32 является ключевым навыком для эффективного программирования микроконтроллеров этой серии. Правильное использование регистров позволяет точно настраивать периферийные устройства и максимально эффективно использовать возможности микроконтроллера.
Основные выводы:
- Регистры — это специальные ячейки памяти для управления микроконтроллером
- Существуют различные типы регистров для разных задач
- Библиотека CMSIS упрощает работу с регистрами
- Важно учитывать особенности работы с регистрами для избежания ошибок
- Отладка программ, работающих с регистрами, требует специальных инструментов
Освоение работы с регистрами открывает широкие возможности для создания эффективных и оптимизированных программ для микроконтроллеров STM32.

Что такое регистры? Как с ними работать? / Хабр
Продолжаем рассмотрение базовых вопросов
В предыдущем уроке мы рассмотрели работу с битовыми операциями и двоичными числами, тем самым заложив основу для рассмотрения новой темы. В этом уроке мы с Вами рассмотрим очередной вопрос: что такое регистры и как с ними работать?
Список статей:
- Начинаем изучать STM32 или Управляем светом по-умному
- Начинаем изучать STM32: битовые операции
- Начинаем изучать STM32: Что такое регистры? Как с ними работать?
Память и регистры
Одним из самых важных навыков необходимых при работе с микроконтроллерами является умение взаимодействовать с регистрами. Давайте для себя разберемся, что же это такое?
В целом, регистр — это особый вид памяти внутри микроконтроллера, который используется для управления процессором и периферийными устройствами. Каждый регистр в архитектуре ARM представляет собой ячейку памяти и имеет длину в 32 бита, где каждый бит можно представить в виде крошечного выключателя с помощью которого осуществляется управление тем или иным параметром микроконтроллера.
Каждый из регистров имеет свой порядковый номер – адрес. Адрес регистра обозначается 32-битным числом представленным в шестнадцатеричной системе счисления. Путём записи по адресу регистра определённой комбинации единиц и нулей, которые обычно представлены в шестнадцатеричном виде, осуществляется настройка и управление тем или иным узлом в МК. Вспомним, что в программе для работы с битовыми операциями, мы могли представить в виде шестнадцатеричного числа произвольный набор единиц и нулей. В целом стоит отметить, что существует два вида регистров: регистры общего назначения и специальные регистры. Первые расположены внутри ядра МК, а вторые являются частью RAM-памяти.
Так же стоит отметить, что Reference Manual, который мы скачивали в первом уроке, это один большой справочник по регистрам, содержащимся в целевом микроконтроллере, а библиотека CMSIS позволяет нам оперировать символьными именами регистров вместо числовых адресов.
Итак, обычно структура регистра описывается в виде небольшой таблицы с указанием:
- Названия регистра и описания его назначения
- Адреса регистра или смещением относительно базового адреса
- Значения по умолчанию после сброса
- Типа доступа к ячейкам регистра (чтение, запись, чтение/запись)
- Значения и описания параметров записываемых битов
Давайте рассмотрим пример работы с регистрами в конкретной ситуации, чтобы получить общее представление о принципах настройки микроконтроллера.
Разбор кода из первого занятия
Итак, давайте вспомним задачу, которую мы решили на первом уроке используя готовый код примера: нам было необходимо написать программу, которая бы обеспечила попеременное включение двух светодиодов на плате Discovery (возможно и не двух, если у вас другая версия платы Discovery) с временным интервалом.
Давайте еще разок взглянем на код программы, которую мы использовали для того, чтобы заставить наш МК дрыгать двумя ногами на которых расположены наши светодиоды:
Код main.c
/* Заголовочный файл для нашего семейства микроконтроллеров*/ #include "stm32f0xx.h" /* Тело основной программы */ int main(void) { /* Включаем тактирование на порту GPIO */ RCC->AHBENR |= RCC_AHBENR_GPIOCEN; /* Настраиваем режим работы портов PC8 и PC9 в Output*/ GPIOC ->MODER = 0x50000; /* Настраиваем Output type в режим Push-Pull */ GPIOC->OTYPER = 0; /* Настраиваем скорость работы порта в Low */ GPIOC->OSPEEDR = 0; while(1) { /* Зажигаем светодиод PC8, гасим PC9 */ GPIOC->ODR = 0x100; for (int i=0; i<500000; i++){} // Искусственная задержка /* Зажигаем светодиод PC9, гасим PC8 */ GPIOC->ODR = 0x200; for (int i=0; i<500000; i++){} // Искусственная задержка } }
Первым делом, при работе с STM32, даже для такой простой задачи как включение и выключение светодиода нам необходимо предварительно ответить на ряд вопросов:
- Куда подключены наши светодиоды? К какому выводу микроконтроллера?
- Как включить тактирование на нужный порт GPIO?
- Как настроить, нужные нам, пины порта GPIO для того чтобы можно было включить светодиод?
- Как включить и выключить светодиод?
Ответим на них по порядку.
Куда подключены наши светодиоды? К какому выводу микроконтроллера?
Для того, чтобы посмотреть где что находится на плате Discovery, а в частности, нужные нам светодиоды — нужно открыть Schematic-файл, либо тот который мы скачали с сайта ST, либо прямо из Keil:
Открыв Schematic мы увидим схему всего того, что есть на плате — схему ST-Link, обвязку всей периферии и многое другое. На текущий момент нас интересуют два светодиода, ищем их обозначение:
Как мы видим, наши светодиоды подключены к порту GPIOC на 8 и 9 пин.
Как включить тактирование на нужный порт GPIO?
В целом, любая работа с периферией в микроконтроллерах STM32 сводится к стандартной последовательности действий:
- Включение тактирования соответствующего периферийного модуля. Осуществляется это через регистр RCC путем подачи тактового сигнала напрямую с шины на которой находится данный модуль.
По умолчанию тактирование всей периферии отключено для минимизации энергопотребления.
- Настройка через управляющие регистры, путем изменения параметров специфичных для конкретного периферийного устройства
- Непосредственный запуск и использование результатов работы модуля
То есть, для начала работы нам нужно запустить тактирование на порт GPIOC. Это делается напрямую через обращение к регистру RCC отвечающему за тактирование всего и вся и включению тактового сигнала с шины, к которой подключен наш порт GPIO.
Внимание! Вопрос касательно системы тактирования, её настройки и использования мы подробно рассмотрим в отдельной статье.
Найти к какой шине подключен наш порт GPIOC можно найти в Datasheet’е на наш МК в разделе Memory Mapping в Таблице 16. STM32F051xx peripheral register boundary addresses.
Как вы уже успели заметить, необходимая нам шина именуется как AHB2. Для того чтобы подробнее ознакомиться с регистром, в котором включается тактирование на нужный нам порт GPIO на шине AHB, надо перейти в соответствующий раздел в Reference Manual. По названию регистров мы можем определить тот, который нужен нам:
Переходим в этот пункт, и мы видим наш 32-битный регистр, его адрес смещения, значение по умолчанию, способ доступа к регистру и перечисление того, за что отвечает каждый бит в регистре.
Смотрим на таблицу и видим нечто напоминающее опции включения тактирования на портах GPIO. Переходим к описанию и находим нужную нам опцию:
Соответственно если мы установим 19 бит в значение «1» то это обеспечит включение тактирования на порт I/O C – то есть на наш GPIOC. К тому же — нам нужно включить отдельно один бит из группы, не затрагивая остальные т.к. мы не должны мешать и изменять без надобности другие настройки.
Основываясь на материалах прошлого урока, мы знаем что для того чтобы выставить определенный бит нужно используя логическую операцию «ИЛИ» сложить текущее значение регистра с маской которая содержит те биты которые необходимо включить. Например, сложим значение регистра RCC->AHBENR по умолчанию, т.е. 0x14 и число 0x80000 тем самым включим тактирование GPIOC путем установки 19 бита:
Каким образом мы можем это сделать из программы? Всё достаточно просто. В данном случае у нас два варианта:
- Запись в регистр напрямую численного значения регистра напрямую через его адрес.
- Настройка с использованием библиотеки CMSIS
В записи значения в регистр напрямую нет особых проблем, но есть пара существенных недостатков. Во-первых, такой код становится не читабельным и во-вторых мы не можем сходу определить на какой регистр ссылается тот или иной адрес в памяти.
То есть, мы могли бы обращаться к адресам регистров напрямую по адресу и написать так:
__IO uint32_t * register_address = (uint32_t *) 0x40021014U; // Адрес нашего регистра в памяти *(__IO uint32_t *)register_address |= 0x80000; // Включаем 19 бит с нашим параметром
Второй вариант мне кажется наиболее привлекательным, т. к. библиотека CMSIS организована таким способом, что регистру можно обращаться, используя только его название. Препроцессор в ходе обработки текста программы перед компиляцией подставит все цифровые значения адреса регистра автоматически. Давайте разберем этот вопрос чуть подробнее.
Предлагаю открыть наш проект, который мы сделали в первом занятии, или скачайте предварительно подготовленый отсюда и удалите все содержимое программы оставив только подключенный заголовочный файл, функцию main() и инструкцию для включения тактирования (она нам понадобится для подробного разбора кода).
Наш код будет выглядеть следующим образом:
/* Заголовочный файл для нашего семейства микроконтроллеров*/ #include "stm32f0xx.h" /* Тело основной программы */ int main(void) { /* Включаем тактирование на порту GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN; }
Давайте для ознакомления копнём вглубь библиотеки CMSIS.
Для того, чтобы быстро перейти к месту где объявлена та или иная константа или переменная в Keil реализована удобная функция. Кликаем правой кнопкой по необходимой нам константе, например, на RCC:
И мы переносимся в глубины библиотеки CMSIS, в которой увидим, что все регистры доступные для управления программным способом имеют вид TypeDef-структур, в том числе и наш RCC:
Провалившись подобным образом в RCC_TypeDef мы увидим структуру в которой описаны все поля нашего регистра:
Соответственно, мы можем спокойно обращаться к нужному нам регистру записью вида PERIPH_MODULE->REGISTER и присваивать ему определенное значение.
Помимо мнемонического обозначения регистров есть так же обозначения конкретных битов. Если мы провалимся к объявлению параметра RCC_AHBENR_GPIOCEN из нашей программы, то так же увидим объявление всех параметров:
Таким образом, используя библиотеку CMSIS у нас получается лаконичная читаемая запись нужного нам параметра в регистр, через установку которого мы запускаем тактирование на нужный нам порт:
/* Включаем тактирование на порту GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN;
В качестве задания: определите используя возможности Keil, каким образом получился адрес регистра RCC->AHBENR как 0x40021014.
Как настроить нужные нам пины GPIO для того чтобы можно было включить светодиод?
Итак, мы знаем что нужные нам светодиоды подключены к порту GPIOC к пинам PC8 и PC9. Нам нужно настроить их в такой режим, чтобы загорался светодиод. Хотелось бы сразу же сделать оговорку, что порты GPIO мы рассмотрим подробнее в другой статье и тут мы сконцентрируемся именно на работе с регистрами.
Первым делом нам нужно перевести режим работы пинов PC8 и PC9 в режим Output. Остальные параметры порта можно оставить по умолчанию. Переходим в Reference Manual в раздел 9. General-purpose I/Os (GPIO) и открываем пункт отвечающий за режим работы пинов порта GPIO и видим что за этот параметр отвечает регистр MODER:
Судя по описанию, для установки пинов PC8 и PC9 в режим Output мы должны записать 01 в соответствующие поля регистра GPIOC.
Это можно сделать через прямую установку с помощью числовых значений:
- Формируем число для записи:
- Присваиваем это значение нашему регистру:
/* Настраиваем режим работы портов PC8 и PC9 в Output*/ GPIOC->MODER |= 0x50000;
Или через использование определений из библиотеки:
/* Включаем тактирование на порту GPIO */ GPIOC->MODER |= GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0;
После данной инструкции наши пины PC8 и PC9 перейдут в режим Output.
Как включить светодиод?
Если мы обратим внимание на список доступных регистров для управления портом GPIO то можем увидеть регистр ODR:
Каждый из соответствующих битов отвечает за один из пинов порта. Его структуру вы можете увидеть ниже:
Для того, чтобы обеспечить попеременную смену состояний светодиодов надо с определенным временным интервалом включать/выключать 8 и 9 биты. То есть попеременно присваивать регистру значение 0x100 и 0x200.
Сделать это мы можем через прямое присвоение значений регистру:
GPIOC->ODR = 0x100; // Зажигаем PC8, гасим PC9 GPIOC->ODR = 0x200; // Зажигаем PC9, гасим PC8
Можем через использование определений из библиотеки:
GPIOC->ODR = GPIO_ODR_8; // Зажигаем PC8, гасим PC9 GPIOC->ODR = GPIO_ODR_9; // Зажигаем PC9, гасим PC8
Но так как микроконтроллер работает очень быстро — мы не будем замечать смены состояний светодиодов и визуально будет казаться что они оба горят постоянно. Для того чтобы они действительно моргали попеременно мы внесем искусственную задержку в виде цикла который займет МК бесполезными вычислениями на некоторое время. Получится следующий код:
/* Зажигаем светодиод PC8, гасим PC9 */ GPIOC->ODR = GPIO_ODR_8; for (int i=0; i<500000; i++){} // Искусственная задержка /* Зажигаем светодиод PC9, гасим PC8 */ GPIOC->ODR = GPIO_ODR_9; for (int i=0; i<500000; i++){} // Искусственная задержка
На этом первоначальное знакомство с регистрами и методами работы с ними мы можем закончить.
Проверка результатов работы нашего кода
Небольшое приятное дополнение в конце статьи: в Keil имеется отличный Debug-инструмент с помощью которого мы можем пошагово выполнить нашу программу и просмотреть текущее состояние любого периферийного блока. Для этого после загрузки прошивки после компиляции мы можем нажать кнопку Start Debug Session:
Рабочая среда Keil переключится в режим отладки. Мы можем управлять ходом программы с помощью данных кнопок:
И есть еще одна удобная функция работы с периферией в режиме отладки, она позволяет просматривать текущее состояние регистров и менять их состояние простым кликом мышкой.
Для того чтобы ей воспользоваться — нужно перейти в соответствующий периферийный блок и справа откроется окно с указанием регистров и их значением.
Если вы кликните по одному из пунктов данного меню, вы увидите адрес регистра и его краткое описание. Так же можно просмотреть описание к каждому отдельному параметру регистра:
Попробуйте самостоятельно пошагово выполнить программу, включить/выключить светодиоды не используя программу, а используя данный режим работы с микроконтроллером. Простор для фантазии тут обширный. Так же попробуйте поиграться с длительностями задержек, сделайте одновременное моргание обоими светодиодами. В общем экспериментируйте! )
До встречи в следующих статьях!
Список статей:
- Начинаем изучать STM32 или Управляем светом по-умному
- Начинаем изучать STM32: битовые операции
- Начинаем изучать STM32: Что такое регистры? Как с ними работать?
Описание регистров DMA — DiMoon Electronics
Так как предыдущая статья про DMA оказалась довольно большой, то описание регистров я решил вынести отдельной частью. Предыдущая статья здесь, все статьи цикла можно посмотреть тут: http://dimoon.ru/category/obuchalka/stm32f1.
В микроконтроллере STM32F103C8 контроллер DMA имеет 6 типов регистров. Организованы они следующим образом.
Регистры, которые являются общими для всех каналов DMA:
- DMA_ISR — регистр статуса прерываний
- DMA_IFCR — регистр очистки флагов прерываний
Регистры, которые являются индивидуальными для каждого канала DMA:
- DMA_CCRx (x=1..7) — регистр конфигурации канала номер x DMA
- DMA_CNDTRx (x=1..7) — регистр количества передаваемых данных канала номер x DMA
- DMA_CPARx (x=1..7) — регистр адреса периферии канала номер x DMA
- DMA_CMARx (x=1..7) — регистр адреса памяти канала номер x DMA
Количество каналов у нас 7. Тогда общее число регистров DMA1 у нас будет 7*4 + 2 = 30 штук.
Регистры DMA
DMA interrupt status register (DMA_ISR) — регистр статуса прерываний от каналов DMAСодержит флаги прерываний от всех каналов DMA.
TEIFx: ошибка передачи канала DMA. Устанавливается аппаратно. Сбрасывается записью 1 в соответствующий бит регистра DMA_IFCR.
HTIFx: флаг передачи половины данных каналом DMA. Устанавливается аппаратно. Сбрасывается записью 1 в соответствующий бит регистра DMA_IFCR.
TCIFx: флаг конца передачи канала DMA. Устанавливается аппаратно. Сбрасывается записью 1 в соответствующий бит регистра DMA_IFCR.
GIFx: глобальный флаг прерывания канала DMA. Устанавливается в 1, когда произошло событие TE, HT или TC данного канала. Устанавливается аппаратно. Сбрасывается записью 1 в соответствующий бит регистра DMA_IFCR.
DMA interrupt flag clear register (DMA_IFCR) — регистр очистки флагов прерываний каналов DMA
Установка в единицу любого бита в регистре DMA_IFCR очищает флаг соответствующего прерывания в регистре DMA_ISR. Запись нуля в любой бит не оказывает ни какого эффекта.
CTEIFx: очистить флаг ошибки передачи канала DMA
CHTIFx: очистить флаг передачи половины данных
CTCIFx: очистить флаг завершения передачи
CGIFx: очистить глобальный флаг прерывания DMA
DMA channel x configuration register (DMA_CCRx) — регистр конфигурации канала номер x DMA
MEM2MEM: режим «из памяти в память»
- 0 — режим MEM2MEM отключен
- 1 — режим MEM2MEM включен
PL[1:0]: уровень приоритета канала DMA
- 00: Low (Низкий приоритет)
- 01: Medium (Средний приоритет)
- 10: High (Высокий приоритет)
- 11: Very high (Очень высокий приоритет)
MSIZE[1:0]: разрядность данных в памяти
- 00: 8 бит
- 01: 16 бит
- 10: 32 бита
- 11: не используется
PSIZE[1:0]: разрядность периферии
- 00: 8 бит
- 01: 16 бит
- 10: 32 бита
- 11: не используется
MINC: режим инкремента памяти
- 0 — режим инкремента памяти отключен
- 1 — режим инкремента памяти включен
PINC: режим инкремента периферии
- 0 — режим инкремента периферии отключен
- 1 — режим инкремента периферии включен
CIRC: кольцевой режим DMA (Circular mode)
- 0 — кольцевой режим отключен
- 1 — кольцевой режим включен
DIR: направление передачи данных
- 0 — чтение из периферии (направление из периферии в память)
- 1 — чтение из памяти (направление из памяти в периферию)
TEIE: разрешить прерывание ошибки передачи DMA
- 0 — прерывание запрещено
- 1 — прерывание разрешено
HTIE: разрешить прерывание передачи половины буфера
- 0 — прерывание запрещено
- 1 — прерывание разрешено
TCIE: разрешить прерывание об окончании передачи
- 0 — прерывание запрещено
- 1 — прерывание разрешено
EN: включить канал DMA
- 0 — канал отключен
- 1 — канал включен
DMA channel x number of data register (DMA_CNDTRx) — количество данных для передачи
NDT[15:0]: количество данных для передачи.
Количество данных для передачи может быть от 0 до 65535. Этот регистр может быть записан только если данный канал DMA отключен (EN=0 в регистре DMA_CCRx). Когда канал DMA включен, этот регистр является только для чтения (read-only) и показывает, сколько транзакций DMA еще осталось. Этот регистр уменьшается на единицу после каждой транзакции DMA. Как только передача завершена, этот регистр либо остается в нуле, либо автоматически перезагружается изначально записанным в него значением, если включен кольцевой режим. Если этот регистр равен нулю, ни одна транзакция DMA не может быть выполнена, вне зависимости от того, включен ли данный канал, или нет.
DMA channel x memory address register (DMA_CMARx) — регистр адреса памяти канала DMA
Этот регистр не может быть записан, когда данный канал DMA включен
MA[31:0]: адрес памяти.
Базовый адрес (указатель) области памяти, которую необходимо записать/прочитать. Когда MSIZE=01 (разрядность памяти 16 бит) бит MA[0] игнорируется. Доступ автоматически выравнивается с адресом в пол-слова. Когда MSIZE=10 (разрядность памяти 32 бита), биты MA[1:0] игнорируются. Доступ автоматически выравнивается с адресом в машинное слово.
Примечание. Получается, что при передаче массива данных по DMA по 16 или 32 бита, надо помнить, что элементы передаваемого массива в памяти должны быть выравнены по границе 16 или 32 бита соответственно. Если в коде на Си передаваемый массив объявлен как int16_t/uint16_t, и передача этого массива осуществляется по 16 бит, то проблем возникнуть не должно, так как компилятор по-умолчанию выравнивает данные массива по соответствующей границе (но это не точно). То же самое и для int32_t/uint32_t массивов и передачи данных по DMA по 32 бита. Так же проблем не должно быть при передаче массивов int32_t/uint32_t по 16 бит за раз. А вот если мы объявили int8_t/uint8_t, и хотим его передать куда-либо по 16 или 32 бита за раз, то в этом случае могут возникнуть проблемы. С регистрами периферийных устройств таких проблем нет, так как они все выравнены по границе 32 бита.
DMA channel x peripheral address register (DMA_CPARx) — регистр адреса периферии канала DMA
Этот регистр не может быть записан, когда данный канал DMA включен
PA[31:0]: адрес периферии
Базовый адрес (указатель) области памяти, которую необходимо записать/прочитать. Когда MSIZE=01 (разрядность памяти 16 бит) бит MA[0] игнорируется. Доступ автоматически выравнивается с адресом в пол-слова. Когда MSIZE=10 (разрядность памяти 32 бита), биты MA[1:0] игнорируются. Доступ автоматически выравнивается с адресом в машинное слово.
См. Примечание к регистру DMA_CMARx
Заключение
На этом описание регистров завершено, в следующей части мы перейдем к практике и научимся быстро копировать данные из памяти одной области памяти в другую, а так же разберемся с обменом данными через интерфейс SPI посредством DMA.
Продолжение следует! Продолжение
Микроконтроллер
. Почему периферийные регистры 16-битные только на 32-битных микроконтроллерах, таких как STM32 и GD32VF103?
Когда вы садитесь писать программу, вы всегда/сразу выбираете один язык, даже если это не имеет смысла. Нужны вычисления с мнимыми числами, а Фортран вам не подходит? Когда вы выбираете переменные цикла, которые считаются до 10, вы выбираете или предполагаете, что 8 бит лучше? (если да то почему???). Используете ли вы i,j,k как переменные цикла или у вас есть собственная привычка?
Во-первых, GD клонирует устройства несколько теневым образом, и их risc-v, который я пробовал, по крайней мере, очевидно, является устройством на основе cortex-m, которое они заменили купленным IP-адресом от ARM на какой-то другой купленный IP-адрес risc-v основной.
Да, в некоторых случаях вы видите поставщиков с многолетней историей, которые берут периферийные устройства из бывшего 8- или 16-битного (из-за отсутствия лучшего термина) ядра и повторно используют их. Хорошо проверенный, хорошо используемый, хорошо понятый, полностью оплаченный, зачем тратить десятки тысяч долларов на создание еще одного UART с теми же функциями, когда у нас уже есть два или три (конечно, вы можете задать этот вопрос ST, потому что у них есть два или три UART). многие из этих вещей).
Как и почему разработчик микросхемы выбирает количество регистров, как поля распределяются по регистрам, ширину регистров, адресное пространство и декодирование для этих регистров и т. д. Например, он может иметь только 8 бит или 16 бит в регистре, но находится на границе 4 байта и может быть доступен с помощью 32-битной транзакции.
Здесь нет ответа, вы можете узнать, можете ли вы связаться с инженерами напрямую, провести встречу в масштабе или что-то в этом роде и посмотреть, позволят ли они вам поковыряться в своих мозгах. (хороший шанс, что они ушли) (возможно, они были подрядчиками для начала, что очень часто встречается в бизнесе чипов, когда большая часть работы выполняется подрядчиками на этапе разработки, а затем они переходят к какой-либо другой компании / проектировщику, занимающемуся разработкой) фаза) Скорее всего, выбор был делом нескольких секунд, основанным на опыте, и в большинстве случаев это не то, на что они месяцами садились командой и решали.
С опытом лично вы, надеюсь, начнете работать против инженеров fpga или asic-инженеров и увидите это воочию, но вы уже знаете этот ответ, потому что, когда вы пишете программное обеспечение, вы не сидите с командой в течение недель или месяцев, чтобы выбрать имя и размер ваших переменных. Только если вы делаете что-то настолько болезненное для других, что кто-то упоминает об этом в рецензировании, произойдет ли этот разговор, в противном случае имена ваших функций, параметры, порядок параметров, имена переменных и т. д. в значительной степени являются вашим выбором. Аппаратное обеспечение больше не волшебство, чем программное обеспечение, и сегодня оно делается с использованием языков «программирования», которые очень напоминают языки программирования программного обеспечения. Как и ваше программное обеспечение, они выдают сотни/тысячи строк HDL…
Кроме того, поставщик чипов явно покупает интеллектуальную собственность, некоторый процент каждого чипа приходится на интеллектуальную собственность, а часть находится в доме, даже у Intel и других громких имен. В вашем x86 много купленных IP-адресов, которые Intel не создавала/не изобретала/не владела.
И купленный IP, по тем же причинам, что уже упоминались, будет иметь некоторую форму интерфейса, иногда они предлагают интерфейс на основе регистров и интерфейс на основе дискретных сигналов, или они предлагают только один или другой. После того, как вы были вовлечены, скажем, в приобретенный контроллер ip dram или контроллер pcie или что-то в этом роде, а затем посмотрите спецификации регистрации ваших конкурентов для их контроллера (или, что более вероятно, посмотрите их драйвер для Linux, поскольку часто вы не можете включить определенные части купленного информация об IP в документации из-за соглашений о неразглашении) иногда можно понять, ааа, они купили тот же контроллер, что и мы, и используют те же процедуры инициализации… (для некоторых технологий существует очень короткий список доступных IP-адресов, которые все используют большое имя или маленькое).
Таким образом, может случиться так, что конкретное периферийное устройство, о котором вы спрашиваете, либо было куплено и имело 16-битный интерфейс, либо, как вы предполагаете, могло быть STM8 или ST7 или другим IP из бывшей линейки продуктов, уже принадлежащей ST. И, основываясь на интерфейсе, разработчику, возможно, было проще просто сделать два отдельных доступа к регистрам, чем пытаться превратить один 32-битный в два отдельных на периферийном устройстве. Или оставить их как два, а также иметь один на случай, если какой-то внутренний или внешний код уже был написан для доступа к этому полю в двух меньших транзакциях. Вы тоже должны/будете это знать, вы создаете библиотеку, коллега использует ее, вы меняете ее и причиняете боль коллеге. Ну, я подумал, что так будет лучше, да, но у меня есть тысячи строк кода, которые я должен проверить, проверить, проверить и т. д. И вы можете выбрать, станете ли вы врагом коллеги, которого вы когда-нибудь вам придется полагаться на клиента, а не на клиента, которого вы никогда не видите и с которым не общаетесь, чью жизнь, по вашему мнению, вы могли бы улучшить.
В отношении того, что делают другие. Поскольку каждое семейство или продукт для каждого поставщика чипов — это отдельный проект разработки с реальными людьми, которые делают личный выбор на основе опыта и других факторов. Вы найдете продукты, которые делают это, и продукты, которые этого не делают. Вы обнаружите, что внутри компании или даже с одним чипом одно периферийное устройство делает что-то одним, а другое — другим. Это все очень ожидаемо и нормально. Есть некоторые микроконтроллеры на основе рук, которые используют 8-битный регистр только с 8-битными транзакциями для управления периферийным устройством, у них больше адресного пространства, чем они могут потреблять, почему бы не сделать все доступы 32-битными? Конечно, в отличие от памяти, меньший доступ (8 или 16 бит) к периферийным устройствам не обязательно приводит к снижению производительности. Просто означает, что мы, программисты, должны время от времени следить за тем, чтобы мы генерировали правильную инструкцию.
Остальные биты зарезервированы для языка 1) у нас может быть ошибка, и если вы немного измените значение сброса, периферийное устройство не будет работать 2) мы можем взять это периферийное устройство, добавить функции и поместить его в чип следующего поколения но если вы используете тот же код с нулями, он должен работать устаревшим способом 3) у нас есть биты, которые мы решили не документировать и т. д. Чип за чипом, регистр за регистром, вы поймете, насколько вам нужно заботиться. Возможно, лично я бы не стал предполагать, что мой код будет портирован, даже если он выглядит как то же самое периферийное устройство на следующем чипе, я бы хотя бы протестировал его, а затем понял, что он может быть неточным, может быть ноль, который должен быть где-то, или я возможно, придется сделать чтение-изменение-запись. Очень часто вы все равно должны выполнять чтение-изменение-запись, а не какой-то указатель и равенство, а затем отдельное или равное значение, которое вы имеете в виду, а чтение… изменение… запись. И, делая это по привычке (да, есть много исключений из этого, в частности, gpio — это много операций чтения-изменения-записи, uart обычно выполняет прямую запись), вы сохраните эти биты, помеченные как зарезервированные (должны быть равны нулю или не модифицировать).
В итоге, если кто-то на самом деле знает настоящий ответ для этих регистров, это, вероятно, будет нарушением их трудового договора или NDA, чтобы предоставить здесь много информации, если только компания не решила поделиться этой информацией публично. .. тогда эта информация уже был бы там. Тем не менее, на большинство вопросов «почему» в отношении дизайна нет ответа…
STM32 USART, лекция 3. Опции конфигурации и регистры USART
В начале файла usart_driver.h упомяните заголовочный файл конкретного устройства (рис. 1). .
Рисунок 1. Заголовок устройства в файле driver.h.
На рис. 2 показаны конфигурация и структура дескриптора периферийного устройства USART.
Рисунок 2. Конфигурация и структура дескриптора.
Все прототипы копируются в файл driver.h, как показано на рис. 3.
Рис. 3. Прототипы всех API, поддерживаемых этим драйвером.
Упомяните файл заголовка usart_driver.h в файле usart_driver.c (рис. 4).
Рисунок 4. Добавление заголовочного файла usart_driver.h в файл usart_driver.c.
Включите usart_driver.h в конец файла заголовка MCU (рис. 5).
Рисунок 5. Добавление файла usart_driver.h в специальный заголовочный файл MCU.
В файле driver. c необходимо реализовать управление периферийными устройствами, управление периферийными часами и другие API, как показано на Рисунке 6 и Рисунке 7 соответственно.
Рис. 7. API управления периферийными часами.
Теперь давайте перейдем к файлу usart_driver.h, чтобы обсудить параметры настройки структуры конфигурации.
USART_Mode: Для настройки режима USART пользовательское приложение может использовать макросы, показанные на рис. 8, которые можно получить из файла USART_API_prototype.h, прикрепленного в разделе ресурсов. 0, 1 и 2 — это коды для макросов USART_MODE_ONLY_TX, USART_MODE_ONLY_RX и USART_MODE_TXRX.
Рисунок 8. Макросы, используемые для настройки USART_Mode.
USART_Baud: Стандартные значения скорости передачи от 1200 бит/с до 3 Мбит/с (рис. 9) могут использоваться для определения значения скорости передачи. Макросы скорости передачи USART можно выбрать, просмотрев таблицу генерации скорости передачи периферийного устройства USART, упомянутую в справочном руководстве.
В справочном руководстве перейдите к разделу функционального описания периферийного устройства USART, там вы получите раздел генерации дробной скорости передачи данных (рис. 10).
Рисунок 10. Раздел генерации дробной скорости передачи данных.
При просмотре этого документа вы увидите таблицу, показанную на рис. 11. Поскольку частота наших периферийных устройств составляет 16 МГц, мы должны использовать эту таблицу для расчета скорости передачи данных.
На рисунке 11 видно, что если тактовая частота периферийного устройства составляет 16 МГц, то можно достичь скорости до 3 Мбит/с. Поскольку скорости передачи данных на рис. 11 являются стандартными, они кодируются как макросы C.
Рис. 11. Расчет погрешности для запрограммированных скоростей передачи данных при fPCLK = 16 МГц или fPCLK = 24 МГц, передискретизация на 16(1).
USART_ParityControl: Существует три варианта (рис. 12) контроля четности. Один — USART_PARITY_DISABLE, затем USART_PARITY_EN_EVEN, а другой — USART_PARITY_EN_ODD.
USART_WordLength: Существует два варианта длины слова: 8 или 9 бит. Таким образом, вы можете использовать два макроса, как показано на рис. 13, для настройки длины слова.
Рисунок 13. Макросы для настройки длины слова USART.
USART_NoOfStopBits: Существует четыре варианта количества стоповых битов, как показано на рис. 14. Это только один стоповый бит, 0,5 стоповых бита, 2 стоповых бита или 1,5 стоповых бита.
Рисунок 14. Макрос для настройки количества стоповых битов.
USART_HWFlowControl: Аппаратное управление потоком данных отсутствует, аппаратное управление потоком CTS, аппаратное управление потоком RTS и аппаратное управление потоком CTS и RTS — это макросы, используемые для настройки аппаратного управления потоком (рис. 15).
Где настроить USART_Mode?
Режим USART_Mode можно настроить, включив или отключив биты 2 и 3 регистра CR1 (рис. 16). Бит 2 означает включение приемника, а бит 3 — включение передатчика.
Если вы хотите настроить микроконтроллер только на передачу данных через периферийное устройство USART, то вам нужно включить передатчик, а не приемник, тем самым сэкономив немного энергии.
Если вы хотите только получать данные, вы можете использовать бит включения приемника, выключив передатчик. Если вы хотите, чтобы он участвовал и в передаче, и в приеме, то вы можете включить оба бита 2 и 3.
Рисунок 16. Регистр управления USART 1 (USART_CR1).
Где настроить USART_Baud?
Помните, что для настройки скорости передачи вы должны правильно настроить мантисса и дробную часть регистра USART_BRR, показанного на рис. 17, в соответствии со скоростью передачи, которую вы хотите использовать.
Где настроить контроль четности USART?
Вы должны настроить биты 9 и 10 регистра CR1 (рисунок 18) для контроля четности. Бит номер 10 отключен по умолчанию. Как только вы включите бит номер 10, контроль четности будет включен. Бит 9 может иметь смысл только при включенном контроле четности. Используя бит 9, вы можете настроить контроль четности или нечетности. По умолчанию это будет даже паритет.
Рисунок 18. Регистр управления USART 1 (USART_CR1).
Где настроить длину слова USART?
Длина слова может быть настроена в 12-м бите регистра CR1 (рис. 19). 12-й бит регистра CR1 определяет длину слова, он устанавливается и очищается программным обеспечением. Если этот бит равен нулю, это означает, что один стартовый бит, 8 битов данных и n стоповых битов. Здесь n будет определено позже с помощью другого управляющего бита. Если это 1, то это означает 1 стартовый бит, 9биты данных и n стоповых битов.
Где настроить количество стоповых битов?
Количество стоповых битов можно настроить в регистре CR2 (рис. 20). Существует 4 варианта настройки стоповых битов. Это:
- 1 стоповый бит.
- 0,5 стоповый бит.
- 2 стоповых бита.
- 1,5 стоповый бит.
Вы можете использовать любой из этих четырех вариантов.
Если вы используете более высокую скорость передачи данных (3 Мбит/с или 4 Мбит/с), то лучше использовать больше стоповых битов, чтобы дать приемнику время для правильной интерпретации данных. Обычно мы используем 2 стоповых бита в ситуации с более высокой скоростью передачи данных. Если скорость передачи данных меньше, например, менее 1 Мбит/с, вы можете использовать 1 стоповый бит. Когда скорость передачи меньше или равна 9600 бит/с, вы можете использовать стоповый бит 0,5.