USART1: связь с компьютером
Отладчик показывает переменные, но удобнее, когда программа сама пишет, что происходит. Настроим USART1 на 115 200 бит/с, научимся отправлять строки и числа и принимать символы с компьютера через преобразователь USB-UART.
Обновлено Редакция GAW
STM32 с нуляурок 5 из 835 мин
Цель урока: Настроить USART1 STM32F103 на 115 200 бит/с 8N1, посчитать BRR, отправлять строки и числа и принимать символы с ожиданием флагов TXE и RXNE.
USART — универсальный синхронно-асинхронный приёмопередатчик. В асинхронном режиме это тот самый UART: линия TX, линия RX, общий провод и договорённость о скорости. Каждый кадр — стартовый бит с низким уровнем, 8 бит данных младшим битом вперёд и стоп-бит (RM0008, разд. 27.3.2). Как устроен протокол и чем UART отличается от RS-232 и RS-485, разобрано в статье «UART»; здесь — регистры STM32F103.
Подключение
У STM32F103C8 три USART. Возьмём USART1: по таблице 5 DS5319 его TX — PA9, RX — PA10, и без переназначения (бит USART1_REMAP регистра AFIO_MAPR равен 0 после сброса) они там и остаются. Оба вывода отмечены FT — выдерживают 5 В на входе. Через USART1 работает и встроенный загрузчик ST, так что это ещё и запасной путь прошивки.
| Blue Pill | Преобразователь USB-UART (3,3 В) |
|---|---|
| A9 (TX) | RXD |
| A10 (RX) | TXD |
| G | GND |
TX одной стороны соединяют с RX другой. Питание платы — по-прежнему от ST-LINK или USB, а земли преобразователя и платы должны быть общими. На компьютере откройте любой терминал на порту преобразователя: 115 200 бит/с, 8 бит данных, без чётности, 1 стоп-бит (8N1). Как работают такие преобразователи, рассказано в статье глоссария «Мост USB-UART».
Выводы: альтернативная функция
Пока выводом управляет USART, а не программа через ODR, его режим — альтернативная функция. По таблице 24 RM0008 (с. 167) TX в полнодуплексном режиме — альтернативная функция push-pull, RX — плавающий вход или вход с подтяжкой вверх. Возьмём подтяжку: если преобразователь отключат, вход не будет ловить помехи как случайные байты.
- PA9: CNF = 10 (альтернативная функция, push-pull), MODE = 11 (50 МГц) — биты 7:4 регистра
GPIOA->CRH; - PA10: CNF = 10 (вход с подтяжкой), MODE = 00, бит ODR10 = 1 — подтяжка вверх; биты 11:8.
Скорость: регистр BRR
По разделу 27.3.4 RM0008 (с. 798) скорость равна fCK / (16 × USARTDIV), где fCK — PCLK2 для USART1 и PCLK1 для USART2 и USART3. Число USARTDIV хранится в USART_BRR с фиксированной точкой: целая часть (мантисса) — в битах 15:4, дробная часть в шестнадцатых — в битах 3:0. Значит, само содержимое BRR — это 16 × USARTDIV = fCK / скорость, округлённое до целого.
Для USART1 на PCLK2 = 72 МГц и 115 200 бит/с: 72 000 000 / 115 200 = 625 = 0x271 — мантисса 39, дробная часть 1/16, USARTDIV = 39,0625. Это значение и ошибку 0 % даёт таблица 192 RM0008. Для 9600 бит/с: 72 000 000 / 9600 = 7500 = 0x1D4C, USARTDIV = 468,75 — тоже точное значение из таблицы.
Если дробная часть при округлении доходит до 16, она переносится в мантиссу — RM0008 разбирает такой пример: USARTDIV 50,99 даёт BRR = 0x330. Деление с округлением (fck + baud / 2) / baud делает это само. Насколько можно ошибиться: по таблицам 193 и 194 (с. 800) приёмник терпит расхождение скоростей до 3,75 % при нулевой дробной части и до 3,33 % при ненулевой (8 бит данных, шум считается ошибкой) — и это суммарный допуск на обе стороны. BRR и ошибку для любой частоты и скорости посчитает калькулятор скорости UART.
uart.c и uart.h
/* uart.h */
#ifndef UART_H
#define UART_H
#include <stdint.h>
void uart1_init(uint32_t pclk2_hz, uint32_t baud);
void uart1_putc(char c);
void uart1_puts(const char *s);
void uart1_put_uint(uint32_t v);
int uart1_getc(void); /* -1 when nothing has been received */
#endif
/* uart.c: USART1 on PA9 (TX) and PA10 (RX), 8N1, polling */
#include "stm32f1xx.h"
#include "uart.h"
void uart1_init(uint32_t pclk2_hz, uint32_t baud)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN;
/* PA9 = TX: AF push-pull, 50 MHz; PA10 = RX: input with pull */
GPIOA->CRH = (GPIOA->CRH & ~(GPIO_CRH_CNF9 | GPIO_CRH_MODE9
| GPIO_CRH_CNF10 | GPIO_CRH_MODE10))
| (0x2u << GPIO_CRH_CNF9_Pos) | (0x3u << GPIO_CRH_MODE9_Pos)
| (0x2u << GPIO_CRH_CNF10_Pos);
GPIOA->BSRR = GPIO_BSRR_BS10; /* ODR10 = 1: pull-up */
USART1->BRR = (pclk2_hz + baud / 2u) / baud; /* 16 x USARTDIV, rounded */
USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; /* 8N1 */
}
void uart1_putc(char c)
{
while (!(USART1->SR & USART_SR_TXE)) { } /* data register empty */
USART1->DR = (uint8_t)c; /* clears TXE */
}
void uart1_puts(const char *s)
{
while (*s) {
if (*s == '\n')
uart1_putc('\r'); /* terminals want CR LF */
uart1_putc(*s++);
}
}
void uart1_put_uint(uint32_t v)
{
char buf[10]; /* 4294967295 has 10 digits */
int n = 0;
do {
buf[n++] = (char)('0' + v % 10u);
v /= 10u;
} while (v != 0u);
while (n > 0)
uart1_putc(buf[--n]);
}
int uart1_getc(void)
{
if (!(USART1->SR & USART_SR_RXNE))
return -1;
return (int)(USART1->DR & 0xFFu); /* reading DR clears RXNE */
}
Бит M регистра CR1 оставлен нулевым — 8 бит данных, PCE = 0 — без чётности, а поле STOP регистра CR2 после сброса означает 1 стоп-бит. После установки TE передатчик сначала посылает «пустой» кадр (idle frame, разд. 27.3.2) — это нормально, терминал его не покажет.
Флаги TXE, TC, RXNE и ORE
Регистр данных USART_DR один, но за ним два буфера: передатчика и приёмника. О состоянии говорят флаги регистра USART_SR (разд. 27.6.1, с. 818–819):
| Флаг | Когда ставится | Как сбрасывается |
|---|---|---|
| TXE (бит 7) | байт перешёл из регистра данных в сдвиговый регистр, можно писать следующий | записью в DR |
| TC (бит 6) | кадр передан полностью, и TXE тоже стоит | чтением SR и затем записью в DR |
| RXNE (бит 5) | принятый байт лежит в DR | чтением DR |
| ORE (бит 3) | пришёл новый байт, а предыдущий ещё не прочитан (RXNE = 1) | чтением SR, затем DR |
Функция uart1_putc ждёт TXE, а не TC: так следующий байт уходит в буфер, пока предыдущий ещё передаётся, и линия работает без пауз. TC нужен, когда важно, что передача закончилась совсем: перед сном, выключением USART или переключением направления драйвера RS-485 (разд. 27.3.2, п. 8).
При опросе с ожиданием легко потерять входящие байты: на 115 200 бит/с кадр из 10 бит длится 86,8 мкс (расчёт наш), и если программа в это время занята чем-то дольше, придёт следующий байт, а предыдущий будет потерян с флагом ORE. Наша uart1_getc читает SR, а затем DR — и тем самым заодно сбрасывает ORE. Надёжный приём через прерывание и кольцевой буфер разберём в уроке 6.
Программа
/* main.c, lesson 5: hello, echo and uptime over USART1 */
#include "stm32f1xx.h"
#include "clock.h"
#include "uart.h"
int main(void)
{
uint32_t hclk = clock_init();
systick_init(hclk);
uart1_init(hclk, 115200u); /* APB2 is not divided: PCLK2 = HCLK */
uart1_puts("STM32F103: hello, HCLK = ");
uart1_put_uint(hclk);
uart1_puts(" Hz\n");
uint32_t next = g_ticks + 1000u;
for (;;) {
int c = uart1_getc();
if (c >= 0)
uart1_putc((char)c); /* echo */
if ((int32_t)(g_ticks - next) >= 0) {
next += 1000u;
uart1_puts("uptime, s: ");
uart1_put_uint(g_ticks / 1000u);
uart1_puts("\n");
}
}
}
В Makefile: SRCS = startup.c main.c clock.c uart.c. После прошивки терминал покажет приветствие с частотой HCLK, раз в секунду — время работы, а набранные символы вернутся эхом. Сравнение (int32_t)(g_ticks - next) >= 0 остаётся правильным и при переполнении счётчика — тот же приём, что в delay_ms из урока 4.
На STM32F4
У STM32F411 USART устроен похоже: те же регистры SR, DR, BRR и CR1 с битами TXE, RXNE, UE, TE, RE, и USART1, как и у F1, тактируется от PCLK2 (у F411 вместе с USART6, RM0383, с. 522). Отличий три:
- выводу задают номер альтернативной функции: для USART1_TX на PA9 это AF7 (данные STM32_open_pin_data), поле
AFRH, режимMODER= 10; - порт A включается в
RCC->AHB1ENR, а не в APB2ENR; - в CR1 есть бит OVER8: скорость = fCK / (8 × (2 − OVER8) × USARTDIV) (RM0368, разд. 19.3.4, с. 519). При OVER8 = 0 формула и BRR совпадают с F1, при OVER8 = 1 дробная часть занимает 3 бита, а бит 3 BRR должен оставаться нулевым.
Задание
- Какое значение BRR получится для USART2 на 115 200 бит/с при наших настройках тактирования? Какая будет ошибка скорости?
- Допишите функцию
uart1_put_hex(uint32_t v), которая печатает число в виде0xи восьми шестнадцатеричных цифр, и выведите ею значениеRCC->CFGRпослеclock_init().
Ответ и решение
-
USART2 тактируется от PCLK1 = 36 МГц. 36 000 000 / 115 200 = 312,5 — округление даёт 313 = 0x139, то есть USARTDIV = 19,5625. Скорость 36 000 000 / 313 = 115 016 бит/с, ошибка −0,16 % (расчёт наш). RM0008 в таблице 192 для 36 МГц приводит USARTDIV 19,5 (BRR 312) и скорость 115 384 бит/с, ошибку +0,15 %: из-за ничьей при 312,5 оба значения одинаково далеки от идеала, и оба укладываются в допуск приёмника.
-
Один из вариантов:
void uart1_put_hex(uint32_t v)
{
static const char digits[] = "0123456789ABCDEF";
uart1_puts("0x");
for (int shift = 28; shift >= 0; shift -= 4)
uart1_putc(digits[(v >> shift) & 0xFu]);
}
При удачном запуске PLL поле SWS (биты 3:2) будет равно 10, а SW (биты 1:0) — тоже 10, то есть младшая цифра — 0xA. В битах 21:18 будет PLLMUL = 0111, в бите 16 — PLLSRC = 1, в битах 10:8 — PPRE1 = 100, в битах 15:14 — ADCPRE = 10: всего 0x001D840A (расчёт наш).
Что дальше
В уроке 6 включим прерывания: кнопку обработаем через EXTI, а приём USART1 — в обработчике прерывания. Все регистры USART STM32F1 с битами — на странице атласа регистров USART STM32F1, а DMA, приём по IDLE и полудуплекс — в статье «UART STM32». Формат кадра и ошибки приёма помогает понять калькулятор кадра UART, а скорость — статья глоссария «Скорость передачи».
Частые вопросы
Почему в терминале вместо текста мусор?
Почти всегда не совпадает скорость: программа считает BRR от одной частоты, а шина на самом деле работает на другой. Если clock_init() не смогла запустить кварц и осталась на 8 МГц, а BRR посчитан для 72 МГц, скорость выйдет в 9 раз ниже. Передавайте в uart1_init() частоту, которую вернула clock_init(), и проверьте, что в терминале выбраны те же 115 200 бит/с, 8 бит, без чётности, 1 стоп-бит.
Можно ли подключить к Blue Pill преобразователь USB-UART на 5 В?
Выводы PA9 и PA10 по таблице 5 DS5319 отмечены как FT и выдерживают 5 В на входе, так что приём от 5-вольтового преобразователя их не повредит. Но STM32 отвечает уровнем 3,3 В, и не каждый 5-вольтовый приёмник его надёжно распознает. Проще взять преобразователь, переключаемый на 3,3 В, и обязательно соединить земли.
Чем отличаются флаги TXE и TC?
TXE означает, что байт ушёл из регистра данных в сдвиговый регистр и можно писать следующий, хотя предыдущий ещё передаётся. TC ставится, когда последний кадр передан полностью, включая стоп-бит. Для потока байтов ждут TXE, а TC — перед выключением USART, сменой скорости или переключением направления драйвера RS-485, иначе последний байт оборвётся.
Источники
- STMicroelectronics. RM0008 Rev 21. Reference manual STM32F101xx–STM32F107xx — табл. 24 (с. 167): TX в полнодуплексном режиме — альтернативная функция push-pull, RX — плавающий вход или вход с подтяжкой вверх; разд. 9.4.2: USART1_REMAP = 0 — TX на PA9, RX на PA10; разд. 27.3.2 (с. 791–792): младший бит первым, порядок настройки передатчика, после последнего байта ждать TC; разд. 27.3.4 (с. 798–799): скорость = fCK / (16 × USARTDIV), fCK — PCLK2 у USART1 и PCLK1 у остальных, примеры 0x1BC = 27,75 и 50,99 → 0x330; табл. 192: при 72 МГц 115,2 кбит/с — USARTDIV 39,0625, ошибка 0 %, 9,6 кбит/с — 468,75; разд. 27.3.5, табл. 193–194 (с. 800): допуск приёмника 3,75 % (M = 0, дробная часть 0) и 3,33 % (дробная часть не 0) при NF как ошибке; разд. 27.6.1 (с. 818–819): TXE сбрасывается записью в DR, TC — чтением SR и записью DR, RXNE — чтением DR, ORE — чтением SR, затем DR; разд. 27.6.3 и 27.6.4 (с. 820–821): BRR — мантисса в битах 15:4, дробная часть в 3:0; CR1 — UE (13), M (12), TE (3), RE (2), RXNEIE (5)
- STMicroelectronics. DS5319 Rev 20. STM32F103x8, STM32F103xB datasheet — табл. 5: PA9 — USART1_TX, PA10 — USART1_RX, оба вывода FT (выдерживают 5 В); выводы 30 и 31 корпуса LQFP48
- STMicroelectronics. cmsis-device-f1 v4.3.5: Include/stm32f103xb.h — RCC_APB2ENR_USART1EN, USART_SR_TXE, USART_SR_TC, USART_SR_RXNE, USART_SR_ORE, USART_CR1_UE, USART_CR1_TE, USART_CR1_RE; GPIO_CRH_CNF9/MODE9/CNF10 и позиции полей
- STMicroelectronics. RM0368 Rev 6 (STM32F401) и RM0383 Rev 4 (STM32F411); STM32_open_pin_data — RM0368 разд. 19.3.4 (с. 519): скорость = fCK / (8 × (2 − OVER8) × USARTDIV), при OVER8 = 1 дробная часть 3 бита; с. 552: OVER8 — бит 15 CR1; RM0383 с. 522: USART1 и USART6 на PCLK2; STM32_open_pin_data для STM32F411: USART1_TX на PA9 — AF7
Нашли ошибку?
Смотрите также
- UART STM32BRR и OVER8, допуск по частоте, флаги, прерывания, DMA с IDLE, полудуплекс, RS-485
- Скорость UARTUBRR, BRR, TH1 и ошибка скорости
- UARTКадр 8N1, скорость и допуск, TX и RX, уровни, USB–UART, AVR и STM32
- Мост USB–UARTМикросхема, превращающая USB в последовательный порт UART и виртуальный COM-порт
- Скорость передачи (бод)Бит/с и бод: стандартные скорости и допуск UART