Первые шаги с STM32 и компилятором mikroC для ARM архитектуры — Часть 3 — UART и GSM модуль
Сейчас немного научившись программировать под наш микроконтроллер, попробуем связать его с внешним миром. Аппаратные интерфейсные модули STM32 поддерживают много много различных внешних интерфейсов. Начнем с самого часто используемого интерфейса UART. Что это за интерфейс можно прочитать здесь и здесь.
Попробуем подключить наш МК к компьютеру посредством UART. Для этого я использую простой USB — UART TTL (3.3V) (не забываем что у нашего МК уровни — 3.3 V ) конвертер на базе микросхемы PL2303. 
В STM32 имеется 3 UART интерфейса. Перед тем как его использовать необходимо сконфигурировать наш последовательный приемопередатчик в МК. Для этого используем команду:
Эта команда запускает наш UART на скорости baud_rate (например 9600 или 19200 kbs) со стандартными параметрами (8 бит данных, без аппаратного контроля четности.)
Если нужно запустить UART в каком-то нестандартном режиме, то используем:
или для массива данных (не важно текст или набор значений) передаем функции:
используя указатель на маcсив.
Напишем простую программу:
Подключаем RX STM32 к TX конвертера и ТX STM32 к RX конвертера и вставляем его в USB порт нашего компьютера. Открываем терминальную программу (Я использую PuTTY), выбираем режим SERIAL, скорость порта, например, 19200 kbs и COM порт который появился при подключении нашего USB-TTL конвертера (можно посмотреть в диспетчере устройств в ветке «Порты COM и LPT») и нажимаем «OPEN». Видим наш текст в окне терминала.
Передавая последовательно несколько байт данных, необходимо контролировать закончилась ли передача предыдущего. У каждого UART есть свой статусный регистр.
USART1_SR. За окончание передачи отвечает бит USART1_SRbits.TC Функция UARTx_Write_Text (char * UART_text) сама контролирует, перед передачей следующего, завершение передачи предыдущего байта.
Для чтения данных с UART микроконтроллера используется функция:
Чтобы знать что в буфере нашего UARTа есть принятый байт используем функцию:
возвращающую 1 если принятый байт есть в буфере и 0 если там его нет. В данном случае нам необходимо постоянно контролировать принял ли что-либо UART нашего контроллера. Давайте попробуем при приеме, например, буквы «А» (ASCII код 65) зажечь светодиод на плате:
Чтобы постоянно не занимать МК опросом состояния регистра UART можно (и нужно) использовать прерывание. При приеме очередного байта вызовется аппаратное прерывание от UART, в котором мы можем обработать данные, принятые поп последовательному интерфейсу. Для включения прерывания по событию «RX UART» нам нужно записать в бит RXNEIE регистра CR1 используемого модуля UART логическую 1.
как и в случае с таймером, при возникновении прерывания вызывается подпрограмма:
При помощи UART можно связывать наш МК не только с компьютером, но и с многими периферийными устройствами. Для примера рассмотрим широкодоступный GSM модуль SIM800L. Мне достался данный модуль уже распаянный на небольшой плате. Такой недорого можно заказать в Китае. На штырьки этой платы выведены все необходимые для работы с модулем сигналы, к тому же плата имеет держатель под СИМ карту.
Единственное что не удобно, это необходимость для питания данной платы напряжения от 3,7 до 4,2 В. Так что придется или подключать внешнюю литиевую батарею, или городить преобразовать с выходным напряжением 4,1 В. Учитывая широкое распостранение регулируемых DC-DC это, думаю, не составит особой проблемы.
Подключим наш модуль у МК, не забывая что RX модуля идет на TX МК и наоборот. Для Maple mini модуль подключаем RX к 26 пину (PA9 / UART1TX), TX к 25 (PA10 / UART1RX).
Для общения с модулем используются, так званые, AT команды. Это набор терминальных команд, которые понимает модем и выполняет в ответ на них определенные действия. С командами нашего модуля можно ознакомится в даташите, к тому же есть неплохая статья где неплохо расписана работа с данным модулем.
Иннициализируем наши UARTы
Наша программа потребует относительно большого количества функций, поэтому вынесем их в отдельный с-файл. Для этого нажмем Ctrl+N в Microc, создадим в проекте еще один файл, например «GSM_SIM800L.с». Для того чтоб наш компилятор понял, что часть функций нужно искать в другом файле, допишем в начале основного с-файла строку (аналогично можно и подключать библиотеки.).
Для начала, в файле «GSM_SIM800L.с» напишем функцию передающую модулю АТ команду и принимающую от него ответ.
В MicroC строка это массив символов char который заканчивается значением 00 (NULL) означающем конец строки. Строка как аргумент функции задается указателем на тип char (например char *nasha_stroka). MicroC поддерживает функции стандартной Си — шной библиотеки С_String.h. Например выше использованна функция
Для начала непроходимо дать модулю простую команду «AT\r» — Модуль на нее должен ответить «OK \r\n» и согласовать скорость своего UART. Любая АТ команда заканчивается символом перевода строки «\r». Ответ модуля на любую команду может состоять из нескольких строк и всегда заканчивается «OK\r\n» или «ERROR\r\n». Сообщение модуля, как то сообщение о входящем звонке или СМС всегда заканчивается «\r\n», причем жизнь упростило то, что информативна обычно только последняя строка. Прием таких строк, я называю асинхронным ответом (не совсем корректно, но пускай так).
Выполним несколько команд, необходимых для конфигурации модуля. Для простоты не будем контролировать ответы модуля, хотя несложно при выполнении каждой команды проверять статус на равенство 1;
Напишем еще одну функцию которая вернет ответ модуля (а не просто статус запроса, при чем максимально в нужном нам виде. В даташите к модулю описаны ответы на АТ команды, используя регулярные выражения о особенности ответа практически всегда нужную нам часть строки можно ограничить с 2 сторон заранее известными символами).
Например «AT+GMM \r\n» вернет название модуля в виде «\r\n SIM_800L \r\n\r\nOK\r\n». «Откусим» начальные «\r\n» и конец «\r\n\r\nOK\r\n»
Среди АТ команд модуля существует команда «AT+CREG?\r». Данная команда вернет состояние модуля о регистрации в сети. Если все ОК и модуль зарегистрирован ответ должен быть таким: «\r\n+CREG: 0,1\r\n\r\n\OK\r\n». 0,0 — модуль не зарегистрирован и нет поиска сети (например не вставлена SIM карта), 0,2 — модуль не зарегистрирован, идет поиск сети. Напишем функцию проверки состояния сети модуля, кроме этого вернем уровень сигнала GSM сети (для этого есть АТ команда «AT+CSQ\r»).
Для отправки СМС используется АТ команда «AT+CMGS=»+380XXXXXXXX\r». Эта команда в ответ выдает символ «\r\n>» — приглашение к вводу. Можно ловить это приглашение в ответе модуля, я же просто даю задержку в 100 мс. и отправляю в UART текст смс который должен окончится символом с ASCII кодом 26
В статье описана только малая часть возможностей как GSM модуля так и UART интерфейса МК STM32. Тем не менее, я надеюсь, что изложенная выше информация будет полезна для понимания работы с последовательным интерфейсом реализованном в микроконтроллере.
В следующей статье я расскажу что такое шина I2C, как работать с ней и как подключить LCD индикатор на основе контроллера HD44780 к STM32 по параллельной шине и через I2C расширитель линий ввода/вывода.
UART (USART) на STM32L (STM32)
Введение
На момент написания данной статьи в сети существовало множество примеров по работе с UART’ом на микроконтроллерах серии STM32. В основном данные примеры сводятся к приему/передачи одного байта без использования прерываний. Основной акцент в статьях делается на инициализации портов ввода/вывода и UART’а, с небольшими примерами. В некоторых статьях описывается работа прерываний, но не создаются полноценные функции приема/передачи.
Однако все эти примеры достаточно разрознены и мне не удалось найти одного исчерпывающего примера по работе с UART’ом. Поэтом, в данной статье ставиться целю объединение воедино накопившегося опыта и создание полноценных функций для работы с UART с использованием прерываний, для чего детально разбирается устройство интерфейса применительно к микроконтроллерам STM32L.
1. Обзор существующих статей:
[1] Статья уважаемого DI HALT на сайте easyelectronics.ru.
Подробно рассматривается пример работы с UART’ом с использованием прерываний, без использования стандартной библиотеки. Также внутри статьи есть полезные ссылки:
Рассматривается пример работы с UART’ом с использованием прерываний, без написания специальных функций.
Небольшие общие слова об интерфейсе UART и сопутствующих интерфейсах (LIN, IrDA, ISO/IEC 7816). Пример подключения FT232L. Описание регистров (USART_SR, USART_DR, USART_BRR, USART_CR1, USART_CR2, USART_CR3). Приведен пример кода без использования SPL.
Изменения к части 1, касающиеся STM32L
Дополнение к части 1,2. Использование стандартной библиотеки.
Пример кода двустороннего обмена данными
Подключение STM32F4-Discovery к компьютеру через COM порт. Схема переходника на MAX3232. Использование PuTTY. Пример кода с написанием функции send_to_uart, и приемом данных с использованием прерывания.
Подключение STM32VL-Discovery к компьютеру. Схема переходника USP-UART на CP2102. Пример кода с написанием функции send_Uart, getch_Uart, send_Uart_str.Без использования прерываний.
Общие сведения о UART’е. Пример осциллограммы. Переходники на CP2102 и MAX3232.
Последовательность настройки. Описание регистров (USART_DR, USART_BRR, USART_CR1, USART_CR2). Пример отправки без прерываний.
Пример приема данных без прерываний.
Рассмотрена работа UART’а с использованием прерываний применительно к STM32. Приводиться пример файла инициализации startup.c и makefile’а.
Статья посвящена портированию буфера FIFO из библиотек STM8L
Краткое описание регистров (USART_SR, USART_DR, USART_BRR, USART_CR1, USART_CR2, USART_CR3, USART_GTPR). Пример обмена данными с компьютером. Отправка без прерываний. Прием по прерыванию.
Вот вроде бы и все, что выдается в первых результатах googl’а. Стоит отметить, что в STM32L регистры немного отличаются от STM32F. Будьте внимательны!
2. Что необходимо сделать, чтобы все заработало.
Для того чтобы воспользоваться всеми удобствами, которые предоставляет нам интерфейс UART необходимо проделать ряд действий. А именно:
Далее рассмотрим по порядку перечисленные действия.
3. Подключение микроконтроллера и минимальная обвязка.
Основной особенностью при проектировании схемы с использованием UART’а является наличие внешнего кварца. Дело в том, что при работе в асинхронном режиме интервалы стробирования бит данных определяются на основе тактовой частотой процессора, и если частоты приемника и передатчика за время передачи посылки разойдутся более чем на 2%, то правильность приема данных не гарантирована. Поэтому при использовании внутреннего источника тактирования могут возникнуть проблемы, по причине не высокой стабильности его частоты. Эти проблемы могут и не возникнуть, однако, я лично предпочитаю для надежности устанавливать кварц. А то потом сиди и цепляйся осциллографом за все ножки, с задумчивым взглядом «Так вот только что работало же!»
В остальном, нет ничего специфичного в подключении микроконтроллера для работы с UART’ом.
4. Согласование уровней, настройка параметров передачи.
В общем и целом схема подключения по UART’у выглядит следующем образом: 
Данные от передающего пина (Tx) одного модуля поступают на вход принимающего пина (Rx) другого модуля. Необходимо наличие общей земли.
Однако при работе с UART’ом не стоит забывать, что разные устройства, использующие данный интерфейс, могут иметь различные логические уровни. А подключение устройств без согласования уровней может привести к поломке. Так в документации на STM32L приводиться следующая информация относительно логических уровней (информация приводиться в datasheet’е на конкретный чип):
Из таблиц 42 и 43 видно, что низкий уровень входного сигнала должен быть ниже 0.3*Vdd, а высокий уровень должен быть выше 0.7*Vdd, но не выше Vdd+0.3 (5.25 для толерантных входов). Также видно, что производители обещают, что выходной сигнал не будет отличаться от 0/Vdd более чем на 0.45 вольта.
Лучше всего согласование уровней делать с применением специальных микросхем, например 74LVC2T, MAX232 и т.п., главное обращать внимание на максимальную скорость работы. Например, для MAX232 она составляет 120 kbit/s, то есть можно установить типичный битрейт в 115,2 кбод, не больше. У микросхемы 74LVC2T максимальная скорость не указана, но указано время задержки прохождения сигнала DR данными, предварительно проверив, завершена ли предыдущая передача.
Пример
или с использованием стандартной библиотеки командами
Прием данных осуществляется аналогично: считыванием данных из регистра USART2->DR, предварительно проверив, пришли ли данные.
или с использованием стандартной библиотеки командами
Не нужно пугаться, что мы и пишем и читаем из одного и того же регистра USART2->DR. На самом деле за одним именем скрываются два различных регистра, поэтому чтение и запись происходят в разных местах и не влияют на данные друг друга.
Такой способ отправки и приема очень прост в реализации, но имеет существенный недостаток: необходимость ожидания установления соответствующего флага. Пока флаг не установился – программа висит в ожидании, что не есть хорошо.
Для того чтобы избавиться от длительного ожидания факта поступления/передачи данных используют прерывания
7. Настройка прерываний.
Настройку прерываний также произведем с использованием стандартной библиотеки.
Первым делом не забываем подключить библиотечные файлы (misc.x).
Далее в файле stm32l1xx_conf.h раскоментим строчку #include «misc.h»
Настройка выглядит следующим образом
Данным участком программы была разрешена генерация прерываний от UART’а в принципе, в частности же, прерывания от UART’а могут генерироваться по факту наступления одного из событий:
Необходимо разрешить одно из этих событий. Разрешим генерировать прерывание по приему и передачи
USART_ITConfig(USART2, USART_IT_RXNE| USART_IT_TXE, ENABLE);
UPD:
Спасибо комментариям, так делать нельзя, правильно
Стоит отметить отличие между флагами USART_IT_TXE и USART_IT_TС. Первый срабатывает, как только освободился буфер и мы можем смело кидать еще данных, второй — когда передача полностью завершилась и мы можем смело отключать UART.
На последок разрешим использовать UART.
С настройкой прерываний все. Осталось написать обработчики прерываний.
8. Написание необходимых функций
Общая идея работы с UART’ом будет следующая: Создадим два буфера – буфер приема и буфер передачи. В программе вызываются привычные функции: put_char(), put_string(), get_char() которые работают с этими буферами. Работа с буферами не требует циклов ожидания.
Далее как только прилетели какие-нибудь данные, обработчик прерывания складывает данные в буфер, а программа уже по необходимости их от туда забирает. Никакие данные не теряются. В случае передачи обработчик при первом удобном случае сам возмет очередной байт из буфера и самостоятельно отправит его.
Будем организовывать два кольцевых буфера (на прием и передачу). Хорошо про реализацию кольцевого буфера написано в статье «Учебный курс. Организация обмена по USART `у с использованием кольцевого буфера» (http://chipenable.ru/index.php/programming-avr/item/44-uchebnyy-kurs-organizatsiya-obmena-po-usart-u-s-ispolzovaniem-koltsevogo-bufera.html)
Напишем обработчик прерывания в следующем виде.
Поясню, что команда USART_ReceiveData(USART2)& 0xFF введена для того, что существует режим работы UART’а в котором передаются 9 бит, в данном случае (поскольку буферы 8 битные) старший бит теряется. Если мы не хотим его терять, то необходимо изменить тип буферов.
Отдельно необходимо прокомментировать поведение флагов прерывания. Многие могут заметить, что необходимо сбрасывать флаги прерываний командой USART_ClearITPendingBit(). Однако некоторые флаги прерывания сбрасываться автоматически после выполнения определенной последовательности комманд. В частности, флаг приема данных USART_IT_RXNE сбрасывается, как только происходит чтение из регистра USART2->DR командой USART_ReceiveData. UPD: Если чтение не произошло, то его необходимо сбросить вручную. Флаг успешной передачи данных USART_IT_TС сбрасывается после последовательного чтения регистра USART2->SR и записи в регистр USART2->DR командами USART_GetITStatus иUSART_SendData. Флаг USART_IT_TXE сбрасывается только записью в регистр USART2->DR командой USART_SendData. Для удобства существует команда USART_ClearITPendingBit(), которая позволяет вручную сбрасывать флаги прерываний, но она не может сбрасывать флаг USART_IT_TXE, который всегда установлен, если буфер пуст. Поэтому нам необходимо запретить генерацию прерывания командой USART_ITConfig(USART2, USART_IT_TXE, DISABLE). В противном случае, сразу же после выхода из прерывания мы бы попали туда снова и программа бы зациклилась.
Рассмотрим более подробно флаги прерываний. Поскольку, как уже отмечалось выше, прерывание для USART только одно, внутри обработчика используются флаги, для того, чтобы определить источник прерывания. Также флаги используются еще и для сигнализирования об ошибках. Для определения источника прерывания в стандартной библиотеке используется команда USART_GetITStatus с параметрами
USART_IT_CTS: прерывание по изменению CTS (не доступно для UART4 и UART5)
USART_IT_LBD: LIN Break detection interrupt
USART_IT_TXE: прерывание по опустошению буфера передачи
USART_IT_TC: прерывание по завершению передачи
USART_IT_RXNE: прерывание по наличию данных в приемном буфере
USART_IT_IDLE: Idle line detection interrupt
USART_IT_ORE_RX: Прерывание по переполнении при установленном бите RXNEIE
USART_IT_ORE_ER: Прерывание по переполнении при установленном бите EIE
USART_IT_NE: прерывание из-за «шума» на линии
USART_IT_FE: прерывание из-за ошибки пакета
USART_IT_PE: прерывание из-за ошибки четности
Данная команда проверяет наличие флага прерывания, а также наличие разрешающего данное прерывания бита в соответствующем регистре (Поскольку возможна ситуация, когда флаг установлен, а данный источник прерывания запрещен. Следовательно прерывание вызвал кто-то другой.) Флаг прерывания необходимо сбросить в обработчике прерывания. Это можно сделать либо явно вызвав функцию USART_ClearITPendingBit(), либо неявно выполнив необходимую последовательность действий, например, чтение USART2->SR и USART2->DR.
Что касается флагов, сообщающих об ошибках, то к ним относятся флаги
USART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5).
USART_FLAG_LBD: LIN Break detection flag.
USART_FLAG_TXE: Transmit data register empty flag.
USART_FLAG_TC: Transmission Complete flag.
USART_FLAG_RXNE: Receive data register not empty flag.
USART_FLAG_IDLE: Idle Line detection flag.
USART_FLAG_ORE: OverRun Error flag.
USART_FLAG_NE: Noise Error flag.
USART_FLAG_FE: Framing Error flag.
USART_FLAG_PE: Parity Error flag.
состояние которых можно узнать функцией USART_GetFlagStatus, либо напрямую в чтением регистра USART2->SR.
Нас интересуют ошибки по приему данных
USART_FLAG_ORE: Переполнение буфера.
USART_FLAG_NE: ишибка из-за «шумов» на линии.
USART_FLAG_FE: ошибка пакета.
USART_FLAG_PE: ошибка четности.
Данные ошибки мы проверяем и, по-хорошему, обрабатываем. (В примере, битый байт просто пропускается). Стоит отметить, что данные флаги нельзя сбросить явно с помошью функции USART_ClearFlag(), можно только неявно прочитав сначала USART2->SR затем USART2->DR.
Далее отмечу, что разрешая прерывание по приему данных, автоматически разрешается прерывание по переполнению буфера. Его тоже необходимо обработать. Флаг о переполнении сбрасывается последовательностью чтения регистров USART2->SR и USART2->DR командами USART_GetITStatus и USART_ReadData. Поскольку флаг о переполнении буфера устанавливается, только если уже установлен флаг RXNE, то в большинстве случаев сброс флага данного прерывания произойдет в обработчике прерывания по приему. Однако существует вероятность, что переполнение произошло в момент между чтением регистра USART2->SR и регистра USART2->DR. В таком случае прерывание о переполнении произойдет отдельно от прерывания по приему и именно такой случай нужно обработать отдельно.
Теперь напишем функции для передачи/приема одного байта
Здесь стоит обратить внимание на строчки:
В данном случае все же была организована преднамеренная задержка. В первом случае необходимо ждать, пока данные не поступили, во втором — пока буфер переполнен. Важно отметить, что переменные rx_counter и tx_counter должны быть volatile. Иначе компилятор может провести оптимизацию этих циклов в бесконечные, не подозревая, что переменные могут быть изменены в обработчике прерываний. И вообще, все указатели на индексы буфера и сам буфер лучше сделать volatile, чтобы не перечитывать потом ассемблеровский код, с выражением глубокого удивления не лице: «А как это компилятор счел этот цикл ненужным?» избавить себя от неожиданных ошибок.
Для удобства можно написать еще функцию передачи строки
И функцию передачи целого числа
После этого можно полностью насладиться работой UART’а.
Теперь коротко пробежимся по основным этапам:
1. Собираем схему, желательно с внешним кварцем
2. Согласовываем логические уровни USART.
3. Настраиваем ноги, USART, вектор прерываний
a. Подключаем библиотечные файлы stm32l1xx_rcc.x, stm32l1xx_gpio.x, stm32l1xx_usart.x, misc.x, stm32l1xx_conf.h в последнем файле комментируем подключение лишних заголовочных фалов, в настройках компилятора объявляем USE_STDPERIPH_DRIVER, прописываем пути к файлам. В main() добавляем #include «stm32l1xx_conf.h»
b. Настраиваем ноги по аналогии с примером
4. Пишем обработчик прерываний
5. Пишем необходимые функции для работы
Добавляем в main() функцию USART_init(). Наслаждаемся!
Одно замечание, если вы создаете файл, в котором написан код, в формате C++ (.cpp), а не в формате CИ (.c) то перед объявлением обработчика прерывания необходимо вставить extern «C», получиться так:
Для удобства полный код файла main.c
В приложении собранный проект.
Внимание. По ходу комментариев обнаружились некоторые ошибки, которые были исправлены в статье, в приложенном файле еще не поправил. Как поправлю сотру эту надпись.
ОБОРУДОВАНИЕ
ТЕХНОЛОГИИ
РАЗРАБОТКИ
Блог технической поддержки моих разработок
Урок 20. Интерфейс UART в STM32. Работа с ним через регистры CMSIS. Использование прерывания UART.
Получим необходимый минимум информации об интерфейсах UART микроконтроллеров STM32. Научимся управлять ими через регистры библиотеки CMSIS.
UART – это наиболее распространенный последовательный интерфейс передачи данных. Через него к микроконтроллеру могут быть подключены самые разные устройства: от миниатюрного цифрового датчика, до компьютера. На базе UART могут создаваться даже локальные сети. Нам этот интерфейс необходим как минимум для отладки программ.
Протокол интерфейса UART.
Данные передаются асинхронным последовательным кодом.
Интерфейс называется асинхронным. Это означает, что данные могут быть переданы в любой момент, без использования между приемником и передатчиком синхронизирующих импульсов. В одну сторону данные предаются с помощью одного сигнала. Сам сигнал содержит как данные, так и синхронизирующую информацию.
Для того, чтобы приемник понял, что начинается передача данные сопровождаются специальными битами: стартовым и стоповым.
В режиме ожидания сигнал на входе приемника находится в высоком уровне. Первым передается стартовый бит. Он всегда имеет низкий уровень. По первому перепаду сигнала в низкий уровень приемник начинает отсчитывать внутренние синхроимпульсы. Зная скорость передачи, а значит и период поступления входных битов, приемник считывает состояние следующих 8 битов. В конце передатчик формирует стоп-бит, всегда высокого уровня.
Все биты передаются за одинаковые промежутки времени. Время передачи одного бита определяет скорость передачи интерфейса. Часто она указывается в бодах (бит в секунду). Как я писал выше, кроме собственно данных в поток добавляются биты синхронизации. Таким образом, для передачи байта требуется не 8, а 10 битов.
Погрешность скорости передачи не должна превышать 5% (рекомендуется не более 1,5 %.)
Я рассказываю о самом распространенном формате протокола UART. Бывают варианты с различными количествами битов данных, обычно от 5 до 9. Могут использоваться форматы с двумя стоповыми битами. Иногда добавляется бит контроля четности. Но такие варианты используются редко.
Главное, что надо знать:
Существуют стандартные скорости передачи интерфейса UART. Наиболее распространены следующие.
| Скорость передачи, бод | Время передачи одного бита, мкс | Время передачи байта, мкс |
| 4800 | 208 | 2083 |
| 9600 | 104 | 1042 |
| 19200 | 52 | 521 |
| 38400 | 26 | 260 |
| 57600 | 17 | 174 |
| 115200 | 8,7 | 87 |
Обмен данными через UART происходит в дуплексном режиме. Т.е. передача и прием данных могут происходить одновременно.
У интерфейса UART существуют 2 сигнала RX и TX. Иногда их маркируют RXD и TXD, подчеркивая, что это сигналы передачи данных.
При подключении двух UART устройств сигналы RX соединяются с сигналами TX. Используется перекрестная схема соединения.
UART в микроконтроллерах STM32.
У микроконтроллеров STM32F103C8T6 целых 3 интерфейса UART. Они имеют одинаковые структуры и функциональные возможности.
Не повторяя информацию об общепринятых функциях UART, я бы выделил следующие возможности этого интерфейса в микроконтроллерах STM32.
Я коротко, упрощенно расскажу, как работает UART STM32.
Вот часть функциональной схемы интерфейса, через которую происходит передача и прием данных. Все остальное – это управляющая часть. Я ее не привел.
Передающая и приемная часть работают совершенно независимо друг от друга. Из общего у них только тактовый генератор. Т.е. могут быть задействован только приемник, или только передатчик. Приемник и передатчик могут работать с разными устройствами, разными протоколами верхнего уровня и т.п. Единственно у них должна быть одинаковая скорость и формат данных.
Собственно передатчик состоит из 2 регистров: регистра сдвига (Transmit Shift Register) и буферного регистра (TDR).
Данное загружается в буферный регистр передатчика. Программно доступен только он. Если передача предыдущего данного закончена и регистр сдвига пуст, то данное перегружается в него, сдвигается и побитно поступает на выход TX. Как только данное перегружено в регистр сдвига, буферный регистр освобождается и в него может быть загружено новое слово. Оно будет ждать окончания передачи и автоматически перегрузится в сдвиговый регистр.
Таким образом, происходит буферизация данных передатчика. Это позволяет реализовывать передачу данных без пауз между словами. После загрузки буферного регистра есть время на запись нового данного, равное времени передачи. Если успевать заполнять буферный регистр, то данные будут передаваться сплошным потоком.
Об окончании передачи данных регистром сдвига и освобождении буферного регистра сообщают специальные флаги. По ним могут быть сформированы прерывания.
Приемная часть устройства также состоит из двух регистров: регистра сдвига (Receive Shift Register) и буферного регистра (RDR). С входа RX данные поступают на сдвиговый регистр, сдвигаются и, после формирования полного слова, перегружаются в буферный регистр. Буферный регистр доступен программно. Из него считывается полученное данное. Если данные поступают сплошным потоком, без пауз, то после приема данного есть время, равное времени передачи слова, на то, чтобы считать его из буферного регистра. Иначе придет новое данное, а старое будет потеряно.
Регистры UART.
Регистров много, битов еще больше. Я не претендую на подробное описание регистров UART. Приведу только информацию, необходимую нам в ближайших уроках.
Прежде всего, нам интересны флаги, сообщающие о состоянии приема и передачи.
UART способен определить некоторые ошибки приема данных, возникающие из-за искажения сигнала при передаче или за счет неправильных программных операций с ним. Об ошибках сигнализируют следующие 4 бита.
Все биты сбрасываются программно последовательным чтением сначала регистра USART_SR, затем USART_DR.
Используется для записи данных в буферный регистр передатчика и чтения данных из буферного регистра приемника. На самом деле это 2 регистра, для обращения к которым используется один адрес.
Регистр содержит значение делителя частоты (USARTDIV), который определяет скорость передачи и приема данных.
Скорость вычисляется по формуле:
BAUD = Fck / (16 * USARTDIV), где
USARTDIV задано в формате с фиксированной запятой с дробной частью.
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| Целая часть (12 разрядов) | Дробная часть (4 разр) | ||||||||||||||
| 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | -1 | -2 | -3 | -4 |
В верхней строке – разряды регистра USART_BRR.
Десятичное значение USARTDIV = целая часть + (дробная часть / 16).
Например, если целая часть равна 78, а дробная 5, то
USARTDIV = 78 + (5 / 16) = 78,3125.
Можно перевести в десятичный код содержимое всего регистра (разряды 0…15) и разделить его на 16.
Скорость при частоте тактирования 72 Мгц
BAUD = 72000000 / (16 * 78,3125) = 57462 бод.
Для обратного вычисления USARTDIV по скорости BAUD можно использовать формулу:
USARTDIV = Fck / (16 * BAUD)
USART_CR1 – управляющий регистр 1.
У всех разрядов активный уровень единица.
USART_CR2 – управляющий регистр 2.
Нам интересно только одно поле.
USART_CR3 – управляющий регистр 3.
Пока при инициализации UART запишем в этот регистр 0.
Работа с UART через регистры CMSIS.
Большей частью мы будем управлять UART с помощью HAL- функций. Но бывают задачи, когда без прямого обращения к регистрам UART не обойтись.
Например, сейчас я разрабатываю центральный контроллер для системы управления шаговыми двигателями. Передача данных происходит со скоростью 1 Мбит/сек, и операции обмена крайне критичны ко времени выполнения. Тратить время на вызовы функций библиотеки HAL при такой задаче очень расточительно. Все удачно реализовывается при прямом обращении к регистрам UART.
Поэтому, я решил, коротко рассказать о таком способе управления передачей данных.
Поставим задачу – реализовать эхо-терминал. Т.е. устройство, которому мы посылаем данные и получаем их в ответ. Будем использовать UART1. В нашей системе он уже подключен к компьютеру.
Создадим проект Lesson20_1. В нем настроим только систему тактирования на частоту 72 мГц.
В файле main.c создаем блок инициализации UART.
/* USER CODE BEGIN SysInit */
Включаем тактирование UART1. Он подключен к шине APB2.
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // включаем тактирование UART1
UART1 использует выводы: PA9 (для сигнала TX) и PA10 (сигнал RX). Надо задать конфигурацию для них.
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // разрешаем тактирование порта GPIOA
GPIO_CRH_CNF9_0);
GPIOA->CRH |= (GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9);
GPIO_CRH_CNF10_0);
GPIOA->CRH |= GPIO_CRH_CNF10_1;
GPIOA->CRH &= (
(GPIO_CRH_MODE10));
GPIOA->BSRR |= GPIO_ODR_ODR10;
Теперь конфигурация самого UART.
// конфигурация UART1
USART1->CR1 = USART_CR1_UE; // разрешаем USART1, сбрасываем остальные биты
Устанавливаем скорость обмена.
Частота тактирования UART1 72 мГц, нам нужна скорость 9600 бод. Вычисляем значение USARTDIV.
USARTDIV = Fck / (16 * BAUD) = 72000000 / (16 * 9600) = 468,75
Значение регистра USART_BRR = 468,75 * 16 = 7500.
USART1->BRR = 7500; // скорость 9600 бод
Разрешаем работу приемника и передатчика. Прерывания по событиям UART не разрешаем.
USART1->CR1 |= USART_CR1_TE | USART_CR1_RE ; // разрешаем приемник и передатчик
USART1->CR2 = 0;
USART1->CR3 = 0;
Теперь для передачи необходимо дождаться, когда бит TXE станет равным 1 и загрузить в регистр данных передаваемое число.
Для приема надо дождаться, когда бит RXNE станет равным 1 и считать из регистра данных принятое число.
Для реализации эхо-терминала поместим такой блок в цикл while:
/* Infinite loop */
/* USER CODE BEGIN WHILE */
/* USER CODE END WHILE */
Загрузить проект можно по ссылке:
Зарегистрируйтесь и оплатите. Всего 60 руб. в месяц за доступ ко всем ресурсам сайта!
Загружаем программу в микроконтроллер.
Запускаем программу CoolTerm.
Нажимаем Options, выбираем COM-порт, скорость обмена.
Нажимаем кнопку Connect.
В верхнем меню, в закладке Connection выбираем Send String (Послать строку).
Набираем текстовую информацию, нажимаем Send и посланная строка появляется в окне принятых данных.
Программу CoolTerm можно не закрывать. Если необходимо загрузить программу в STM32, то можно нажать кнопку Disconnect. CoolTerm освободит COM-порт и даст возможность запрограммировать микроконтроллер. Для возобновления работы CoolTerm достаточно нажать Connect.
Использование прерываний UART.
В предыдущей программе в основном цикле while постоянно происходила проверка состояния флага RXNE. На остальные задачи времени не оставалось. И это притом, что флаг становился активным не чаще чем с периодом 1 мс. Заставить микроконтроллер тратить минимум вычислительных ресурсов на работу с UART можно за счет применения прерываний.
Реализуем ту же задачу с использованием прерываний.
Я создал новый проект Lesson20_2, в котором настроена только система тактирования на 72 мГц.
Открываем урок 18 и кто забыл, повторяем необходимые действия для работы с прерываниями.
Копируем блок инициализации UART из предыдущей программы. В нем делаем изменения только в одной строке. В регистре CR1 разрешаем прерывание по флагу RXNE.
USART1->CR1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; // разрешаем приемник, передатчик и прерывание по приему
Разрешаем прерывание в контроллере прерываний.
// разрешения прерывания UART1 в контроллере прерываний
NVIC_EnableIRQ (USART1_IRQn);
В конце файла stm32f1xx_it.c размещаем функцию обработки прерывания UART1.
void USART1_IRQHandler(void) <
Не забываем добавить в файл stm32f1xx_it.h прототип функции обработки прерывания.
Остается заполнить функцию обработки прерывания.
void USART1_IRQHandler(void) <
uint8_t d = USART1->DR; // получить данное
USART1->DR = d; // отослать данное назад
>
Загрузить проект можно по ссылке:
Зарегистрируйтесь и оплатите. Всего 60 руб. в месяц за доступ ко всем ресурсам сайта!
Теперь цикл while пустой. В нем можно выполнять любые задачи. Микроконтроллер будет прерывать его только на короткое время при приеме данного.
В следующем уроке будем работать с UART через HAL-функции. Поговорим об отладке программ с помощью UART.
















