В предыдущей части нашего занятия мы познакомились с блок-схемой шины USART в контроллере STM32F1, а также познакомились с регистрами данной шины и с их битами по-отдельности.
Думаю, на этой ноте можно уже смело переходить к практической части нашего урока. Работать на данном уроке с шиной USART мы будем пока только на передачу, без всяких прерываний и DMA.
Начнем со схемы. Контроллер у нас будет STM32F103C8T6, расположенный на недорогой отладочной плате. Также для мониторинга мы подключим к нему индикатор с драйвером MAX7219, которым мы также постоянно пользуемся. Подключим мы его к шине SPI2, ножки мы увидим, когда будем настраивать шину в Cube MX
Для работы с интерфейсом USART мы подключим переходник USB-TTL к шине USART1, не забывая, что ножка RX переходника подключается к ножке TX контроллера, а ножка TX, наоборот, — к RX
Также для оценки процесса обмена подключим к шине USART логический анализатор
Подключим к ПК, анализатор, переходник и программатор
Перейдём к проекту. Создадим новый проект в Cube MX и выберем наш контроллер
Выберем программатор
Подключим кварцевый резонатор
Произведём настройки тактирования в Clock Configuration
Включим USART1 для работы в асинхронном режиме, настройки все оставим по умолчанию
Посмотрим, какие ножки у нас включились для шины USART1
Для работы с драйвером индикатора включим шину SPI2 произведём её некоторые настройки
Посмотрим, какие ножки контроллера работают с шиной SPI2, а также включим ещё одну на выход для линии SS
Добавим ей немного скорости
Далее перейдём в раздел Project Manager и задействуем на всё библиотеку LL
Придумаем имя нашему проекту и выберем среду программирования
Соберём проект и откроем его в Keil. Файлы для работы с драйвером индикатора max7219.c и max7219.h возьмём, например из проекта прошлого урока LL_SPI_MASTER_DMA и скопируем их в соответствующие папки нового проекта.
Подключим файл max7219.c к дереву проекта, настроим программатор на автоперезагрузку и уберём оптимизацию.
Подключим библиотеку для драйвера индикатора, а также ещё одну стандартную библиотеку, в файле main.c
1 2 3 |
/* USER CODE BEGIN Includes */ #include <stdio.h> #include "max7219.h" |
А теперь в соответствии с традицией посмотрим, как нам проектогенератор сгенерировал инициализацию шины USART1.
Для этого зайдём в тело функции MX_SPI2_Init и посмотрим, что там происходит.
Как обычно, сначала включается тактирование, также настраиваются ножки шины, а затем данными заполняется структура USART_InitStruct, поля которой затем используется для настройки битов тех или иных регистров шины.
Далее уже вызывается более низкоуровневая функция LL_USART_Init, параметрами которой служат USART1 и вышеназванная структура.
Проследуем в тело данной функции, в которой, как обычно, сначала идёт проверка наличия параметров.
В условие включения 9-битного режима передачи мы не попадаем, а попадаем в ELSE. где сначала отключаются биты M, PCE, PS, TE и RE регистра CR1 с помощью второго параметра макроса MODIFY_REG, а в третьем параметре мы настраиваем биты с помощью полей нашей переданной структуры. В нашем случае включатся только биты TE и RE, так как мы включаем шину и на передачу и на приём, хотя приёмом пользоваться пока не будем.
Далее с помощью функции LL_USART_SetStopBitsLength настраивается количество стоповых бит. Так как у нас 1 стоповый бит, то у нас соответствующее битовое поле STOP регистра CR2 будет нулевое.
Затем с помощью функции LL_USART_SetHWFlowCtrl настраиваются биты RTSE и CTSE регистра CR3, отвечающие за управление аппаратным потоком, а так как мы им не будем управлять, то данные биты просто сбросятся.
Затем идёт настройка скорости обмена по шине.
Сначала с помощью функции LL_RCC_GetSystemClocksFreq заполняются поля структуры rcc_clocks типа LL_RCC_ClocksTypeDef, в которые заносятся частоты тактирования различных видов периферии контроллера. Так как у нас включен USART1, то нас будет интересовать поле PCLK2_Frequency, которое заполнится значением частоты PCLK2. Значение данного поля затем присваивается переменной periphclk, которой мы затем и пользуемся в расчётах делителя. Значение данной переменной затем передаётся в качестве параметра функции LL_USART_SetBaudRate, которой в качестве третьего параметра также передаётся и значение битрейта, которое нам желательно получить.
Войдём в тело этой функции и посмотрим, как настраивается битрейт.
Здесь регистру BRR присваивается возвращённое значение ещё одной функции (а вернее макроса) __LL_USART_DIV_SAMPLING16, преобразованный в 16-битное значение. В данную функцию передаются те же параметры.
Здесь стоит задача — получить значение делителя — его целую и дробную часть и уложить в регистр BRR.
Делитель рассчитывается по той же формуле, только для этого надо его из неё выразить
Вот этим и занимается наш макрос. Только разбирать, как здесь всё происходит мы не будем, так как здесь используется ещё ряд вспомогательных макросов, чтобы избежать работы с плавающей точкой.
На этом наша функция LL_USART_Init заканчивается, поэтому вернёмся в функцию MX_USART1_UART_Init, в которой дальше с помощью функции LL_USART_ConfigAsyncMode сбрасываются биты LINEN и CLKEN в регистре CR2, а также сбрасываются биты SCEN, IREN и HDSEL, назначение которых мы теперь уже знаем.
Затем при помощи функции LL_USART_Enable устанавливается бит UE регистра CR1, который включит нашу шину.
На этом инициализация закончена. Можно приступать к написанию кода.
Объявим глобальный строковый массив
1 2 |
/* USER CODE BEGIN PV */ char str1[30]; |
В функции main() произведём инициализацию драйвера индикатора, проверим его, выведя на него число, подождём 2 секунды и очистим индикатор
1 2 3 4 5 6 7 8 |
/* USER CODE BEGIN 2 */ LL_mDelay(100); LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_12); LL_SPI_Enable(SPI2); Init_7219(); Number_7219(87654321); LL_mDelay(2000); Clear_7219(); |
Добавим функцию. которая будет заниматься передачей в шину USART байтов в определённом количестве
1 2 3 4 |
/* USER CODE BEGIN 0 */ void USART_TX (uint8_t* dt, uint16_t sz) { } |
Займёмся телом функции. Добавим целочисленную переменную для перемещения указателя по массиву
1 2 3 |
void USART_TX (uint8_t* dt, uint16_t sz) { uint16_t ind = 0; |
Добавим цикл, в котором будем следить за окончанием массива
1 2 3 4 |
uint16_t ind = 0; while (ind<sz) { } |
В данном цикле дождёмся установки флага TXE в регистре SR
1 2 3 |
while (ind<sz) { while (!LL_USART_IsActiveFlag_TXE(USART1)) {} |
Запишем передаваемый байт в регистр DR, тем самым шина получит команду передавать данные и затем увеличим индекс
1 2 3 |
while (!LL_USART_IsActiveFlag_TXE(USART1)) {} LL_USART_TransmitData8(USART1,*(uint8_t*)(dt+ind)); ind++; |
Вот, в принципе, и вся функция.
Вернёмся в функцию main() и добавим переменную для счёта
1 2 |
/* USER CODE BEGIN 1 */ uint16_t i=0; |
В бесконечном цикле сначала сбросим счётчик по достижению какого-нибудь порога
1 2 |
/* USER CODE BEGIN 3 */ if(i>1023) i=0; |
Сформируем строку со значением счётчика и передадим её в шину USART
1 2 3 |
if(i>1023) i=0; sprintf(str1,"String %04drn",i); USART_TX((uint8_t*)str1,13); |
Отобразим значение счётчика на индикаторе, немного подождём и увеличим значение счётчика на 1
1 2 3 4 |
USART_TX((uint8_t*)str1,13); NumberR_7219(i); LL_mDelay(100); i++; |
Вот и весь наш код. Зная теорию, намного легче его писать.
Соберём код, прошьём контроллер. Посмотрим сначала результат работы кода на светодиодном индикаторе
Индикатор исправно отсчитывает циферки.
Далее настроим терминальную программу, и посмотрим, как приходят строки в ПК
Также давайте посмотрим, как отображается трафик в программе логического анализа. Для этого включим 2 канала. Затем добавим следующий анализатор
Настроим анализатор
Добавим ещё один такой анализатор, то только на другой канал, настроив его аналогичным образом
Установим триггер на канал 0
Вот так передаётся наша строка
Также мы видим, что передача байтов идёт непрерывно, без каких-либо пропусков
Итак, на данном уроке мы изучили подробно аппаратную реализацию шины USART в контроллере STM32, и, используя библиотеку LL закрепили свои знания на практике.
Всем спасибо за внимание!
Предыдущая часть Программирование МК STM32 Следующий урок
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Индикатор светодиодный семиразрядный с драйвером MAX7219
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий