Научимся работать с регистрами микроконтроллера. Получим минимальные сведения о библиотеках CMSIS и HAL.
Предыдущий урок Список уроков Следующий урок
Будем считать, что язык C мы знаем. Умеем работать с IDE. Конфигуратор STM32CubeMX нам создал проект, структурно выделил модули программы, подсказал, где что писать в исходном тексте.
Но программирование любого микроконтроллера – это работа с регистрами. Через регистры происходит управление всеми ресурсами контроллера. Через регистры мы работаем с коммуникационными интерфейсами и выводами, а значит, и связываем микроконтроллер с внешним миром.
Работа с регистрами STM32 через библиотеку CMSIS.
Первое что мы сделали при конфигурации микроконтроллера в предыдущем уроке – выбрали в качестве источника тактирования кварцевый резонатор. Давайте, для примера, сделаем эту операцию через прямое обращение к регистрам микроконтроллера.
Не вникаем, что за регистр, зачем он нужен. Просто учимся обращаться к регистрам.
В технической документации взятый нами для примера регистр RCC_CR описан так. Я перевел на русский язык и оставил описание только двух разрядов.
Регистр управления тактированием (RCC_CR).
Адрес: 0x00
Значение после сброса: 0x0000 XX83
Доступ: без ожидания, доступ к слову, полуслову, байту.
Бит 31: . . . . . . . . . . . . . . . . . . . .
Бит 18 HSEBYP: Выбор в качестве источника высокочастотных тактовых импульсов внешнего сигнала.
Бит 16 HSEON: Разрешение работы высокочастотного генератора HSE.
Бит 0: . . . . . . . . . . . . . . . . . . . .
Указан адрес регистра, дополнительная информация, формат распределения битов в 32 разрядном слове и описание каждого бита.
Если прочитать предыдущие 8 страниц документации, то станет понятно, что
- для того чтобы включить режим тактирования от кварцевого резонатора надо установить в единицу бит HSEON;
- а для использования в качестве источника тактирования сигнала внешнего генератора надо установить в единицы биты HSEON и HSEBYP.
На уровне команд микроконтроллера - это операция записи кода в пространство устройств ввода-вывода. Но мы программируем на языке высокого уровня.
Когда мы устанавливали программное обеспечение, то, сами того не зная, установили библиотеку CMSIS (Cortex Microcontroller Software Interface Standard). Это стандартная библиотека содержит обозначение регистров микроконтроллера, областей памяти, битов, векторов прерываний и т.п. Для нас важно, что она переводит числовые адреса регистров в символьные имена.
Библиотека видна в нашем проекте из предыдущего урока.
В файле stm32f103xb.h регистры микроконтроллера объявлены как указатели на структуры.
#define TIM1 ((TIM_TypeDef *)TIM1_BASE)
#define SPI1 ((SPI_TypeDef *)SPI1_BASE)
#define USART1 ((USART_TypeDef *)USART1_BASE)
#define SDIO ((SDIO_TypeDef *)SDIO_BASE)
#define DMA1 ((DMA_TypeDef *)DMA1_BASE)
#define RCC ((RCC_TypeDef *)RCC_BASE)
#define CRC ((CRC_TypeDef *)CRC_BASE)
Поэтому обращаться к регистрам надо как к элементам структуры через указатели. Для тех, кто забыл - обращение через указатели происходит с помощью оператора стрелка (->).
Следующая строчка записывает число в регистр RCC_CR:
RCC->CR = 0x00010000;
Команда установила 1 в бите 16, т.е. включила кварцевый резонатор. Правда, мы сбросили все остальные биты, не зная их назначение. Если нам необходимо установить только один 16й бит надо сделать логическое ИЛИ с содержимым регистра.
RCC->CR |= 0x00010000;
Для битов регистров в файле stm32f103xb.h тоже есть имена. Например, что касается наших двух битов HSEON и HSEBYP.
#define RCC_CR_HSEON_Pos (16U)
#define RCC_CR_HSEON_Msk (0x1U << RCC_CR_HSEON_Pos) /*!< 0x00010000 */
#define RCC_CR_HSEON RCC_CR_HSEON_Msk /*!< External High Speed clock enable */
#define RCC_CR_HSEBYP_Pos (18U)
#define RCC_CR_HSEBYP_Msk (0x1U << RCC_CR_HSEBYP_Pos) /*!< 0x00040000 */
#define RCC_CR_HSEBYP RCC_CR_HSEBYP_Msk /*!< External High Speed clock Bypass */
Для бита HSEON указана позиция в слове (16й бит).
Через имя RCC_CR_HSEON_Msk определена битовая маска
#define RCC_CR_HSEON_Msk (0x1U << RCC_CR_HSEON_Pos)
Единицу сдвинули на позицию (16) и получили число 0x00010000. С учетом этого установить наш бит можно так:
RCC->CR |= RCC_CR_HSEON_Msk;
Для установки нескольких битов надо использовать операцию ИЛИ.
RCC->CR |= RCC_CR_HSEON_Msk | RCC_CR_HSEBYP_Msk;
Сбросить бит можно командой:
RCC->CR &= ~RCC_CR_HSEON_Msk;
Т.е. сделать логическое И с инверсным значением маски.
Два бита сбросятся командой:
RCC->CR &= ~RCC_CR_HSEON_Msk & ~RCC_CR_HSEBYP_Msk;
В результате работы с регистрами таким образом, т.е. прямым обращением через имена библиотеки CMSIS, получаются самые компактные, быстрые программы. Можно сказать, что это профессиональный способ создания приложений.
Но это и самый сложный способ. Приходится разбираться с технической документацией, с форматами регистров, пересчитывать коды, числа, биты. Легко ошибиться. Все это невозможно удержать в голове. Поэтому при малейшем изменении конфигурации приходится все читать и осмысливать заново. Использование символьных имен, а не чисел ничего не меняет.
Чтобы облегчить работу программиста была создана низкоуровневая библиотека SPL (Standard Peripherals Library).
А затем вместе с конфигуратором STM32CubeMX вышла библиотека HAL, значительно упрощающая создание программ для STM32.
Библиотека HAL.
Библиотека HAL (Hardware Acess Level) – это набор драйверов для работы с периферийными узлами микроконтроллера (USART, АЦП, SPI и др.). Программист работает с функциями этих драйверов. Регистры уровня CMSIS он не использует.
HAL является своеобразным промежуточным уровнем между приложением и уровнем CMSIS регистров.
При таком подходе не только значительно проще разрабатывать программы, но и приложения становятся независимыми от типа микроконтроллера. Код легко переносится на другие микроконтроллеры STM32.
HAL драйверы предоставляют программисту набор API (интерфейс прикладного программирования) для работы с периферийными устройствами STM32. Драйверы являются функционально-ориентированными, а не ориентированными на конкретные периферийные узлы.
Например, когда мы работаем с последовательным портом UART, нам часто необходимо использовать прерывания и контроллер прямого доступа к памяти. HAL драйвер для работы с UART управляет всеми этими устройствами в комплексе.
В предыдущем уроке мы с помощью STM32CubeMX конфигурировали микроконтроллер и создали проект. Режимы работы микроконтроллера и его периферийных устройств были заданы с помощью библиотеки HAL.
В тексте нашей первой программы встречаются строки:
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED13_GPIO_Port, LED13_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : LED13_Pin */
GPIO_InitStruct.Pin = LED13_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED13_GPIO_Port, &GPIO_InitStruct);
Нетрудно понять, что это установка начального состояния и режима вывода LED13 посредством HAL библиотеки.
Бок управления светодиодом состоит из 2 строк вызова функций HAL драйверов.
HAL_GPIO_TogglePin(LED13_GPIO_Port,LED13_Pin);
HAL_Delay(250);
Опять же нетрудно понять, что это инверсия состояния вывода 13 и временная задержка. И нам не пришлось часами копаться в документации на STM32, разбираться в форматах регистров.
В дальнейших уроках при использовании функций HAL библиотеки я постараюсь описывать их подробно, с примерами. Думаю завести отдельную страницу со ссылками на материалы сайта для каждой функции.
Как лучше разрабатывать программы? Используя прямое обращение к регистрам CMSIS или с библиотекой HAL?
В принципе я уже ответил на этот вопрос.
- В первом случае код программы будет короче, программа работать быстрее. Но разработка приложения значительно усложнится. В STM32 очень много регистров, много режимов работы, сложная периферия.
- При использовании библиотеки HAL все наоборот. Код длиннее, программа медленнее. А разрабатывать проще.
Но микроконтроллер STM32 обладает очень мощными ресурсами. У него 32 разрядная архитектура, большой объем памяти, высокое быстродействие. Многие задачи могут быть решены с использованием HAL уровня. На современных персональных компьютерах при разработке программного обеспечения вообще не обращают внимания на ресурсоемкость программ. Компьютер все вытянет.
Так что надо выбирать исходя из конкретной задачи.
Есть еще вариант использовать HAL драйверы для начальной установки периферийных устройств. С точки зрения работы с документацией это самый трудоемкий процесс. Большая часть регистров устанавливается именно при инициализации.
К тому же процесс начальной установки состояния регистров совершенно не критичен ко времени. Завершится инициализация периферии микроконтроллера на 10 мкс раньше или позже после включения питания вряд ли имеет значение.
Т.е. можно применять конфигуратор STM32CubeMX и библиотеку HAL для инициализации регистров, а при работе программы использовать прямое обращение к ним. Или выделить отдельные блоки программы, критичные ко времени, и в них использовать нижний уровень доступа к регистрам.
Я постараюсь в уроках представлять и такие варианты.
В следующем уроке будем изучать систему тактирования STM32.
Здравствуйте. Мне, начинающему программисту ещё не знакомы подобные конструкции: «RCC->CR = 0x00010000;» Конкретно не знакома запись в регистр вот этого: » 0x00010000″. Где можно более детально познакомиться с этим синтаксисом, может у вас есть уроки, спасибо!
Здравствуйте!
Это запись числа в шестнадцатеричном виде. Начинается с 0x.
если перевести в двоичную систему, это получится 10000000000000000, т.е. 1 оказывается на 17й позиции, что с учетом отсчета от 0 является 16м битом в регистре…, т.е. значение, к примеру 0x000F0000 в бинарном представлении являлось бы числом 1111 0000 0000 0000 0000 и установило бы в 16, 17, 18, 19 регистры еденицы, остальное нули.. ну и т.д. через калькулятор виндовс)
А ещё в дополнение к ответам: поиграйтесь со стандартным калькулятором в Windows в «программистском» виде, там можно переключать из шестнадцатеричного вида в двоичный, десятичный и т. д., переключать биты в двоичном виде и смотреть какое число получится в шестнадцатеричном, например.. в общем сразу станет понятно многое
Здравствуйте.
Подскажите пожалуйста а как прочитать содержимое регистров или ячейки памяти микроконтроллера с заданным адресом?
Здравствуйте!
Через указатель.
* (uint32_t *)(адрес)
Например, строка:
GPIOB -> BSRR = GPIO_BSRR_BR13; // сброс бита
эквивалентна строке:
* (uint32_t *)(0x40010c10) = GPIO_BSRR_BR13; // сброс бита
Если непонятно, посмотрите урок 15 Ардуино.
(*(int32_t*)адрес) действия.
Например:
(*(int32_t*)0) |= 0x00010000;
Эдуард спасибо, хорошая подача материала, от меня так же благодарность была и будет. Главное не бросайте статьи. Я нашел сайт в гугле по запросу stm32 ll.
Спасибо за отзыв. Бросать не собираюсь. Сейчас погода окончательно испортится и продолжу.
Большое спасибо за ваши уроки.
Если можно два вопроса.
1. Почему то ни могу найти у себя файлик stm32f103xb.h (у меня версия тру студио 9.3.0)?
2. И еще вопрос файлики stm32f103xb.h и stm32f10x.h и тот и другой определяют структуры регистров МК, то есть можно использовать либо один либо другой?
Здравствуйте!
У меня версия 9.2. Попробуйте использовать stm32f10x.h. Будет что-то не так — компилятор выдаст сообщения об ошибках.
Здравствуйте. Подскажите, для чего в библиотеке такая запись:
#define RCC_CR_HSEON_Pos (16U)
#define RCC_CR_HSEON_Msk (0x1U << RCC_CR_HSEON_Pos)
#define RCC_CR_HSEON RCC_CR_HSEON_Msk
Почему нельзя было записать сразу:
#define RCC_CR_HSEON (0x1U << 16U)
Здравствуйте!
Заданы константы для разных вариантов работы с регистрами. Кому-то удобнее работать с позицией бита, кому-то с маской.
Эдуард, здравствуйте!
Подскажите пожалуйста, где скачать документацию на CMSIS для STM32f1xx? ни где не могу найти описание регистров.
Помогите пожалуйста:-)
Здравствуйте!
Это имена регистров микроконтроллера. Поэтому их описание в технической документации STM32. Я даю ссылки в начальных уроках.