Stm32 usart обработка ошибок

Универсальные асинхронные последовательные средства связи

231

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if(RingBuffer_GetDataLength(&txBuf) > 0) {

RingBuffer_Read(&txBuf, &txData, 1); HAL_UART_Transmit_IT(huart, &txData, 1);

}

}

Функция RingBuffer_Read() является не очень быстрой, какой могла бы быть ее более производительная реализация. В некоторых реальных ситуациях общая нагрузка на процедуру HAL_UART_TxCpltCallback() (которая вызывается из процедуры ISR) может быть слишком высокой. Если это ваш случай, вы можете создать такую функцию:

void processPendingTXTransfers(UART_HandleTypeDef *huart) { if(RingBuffer_GetDataLength(&txBuf) > 0) {

RingBuffer_Read(&txBuf, &txData, 1); HAL_UART_Transmit_IT(huart, &txData, 1);

}

}

Затем вы могли бы вызвать данную функцию из основного кода приложения или из задачи с более низким уровнем привилегий, если используете ОСРВ.

При работе с внешними подключениями обработка ошибок является аспектом, который мы должны строго учитывать. Периферийное устройство UART в STM32 предлагает несколько флагов ошибок, относящихся к ошибкам обмена данными. Более того, возможно, что соответствующее ошибке прерывание не будет обработано при ее возникновении.

CubeHAL предназначен для автоматического обнаружения условий ошибок и предупреждения нас о них. Нам нужно только реализовать функцию HAL_UART_ErrorCallback() внутри кода нашего приложения. HAL_UART_IRQHandler() автоматически вызовет ее в случае возникновения ошибки. Чтобы понять, какая именно произошла ошибка, мы можем проверить значение поля UART_HandleTypeDef->ErrorCode. Список кодов ошибок приве-

ден в таблице 7.

Таблица 7: Список возможных значений UART_HandleTypeDef->ErrorCode

Код ошибки UART

Описание

HAL_UART_ERROR_NONE

Ошибка не произошла

HAL_UART_ERROR_PE

Ошибка при проверке четности

HAL_UART_ERROR_NE

Ошибка вследствие зашумления

HAL_UART_ERROR_FE

Ошибка кадрирования данных

HAL_UART_ERROR_ORE

Ошибка вследствие переполнения

HAL_UART_ERROR_DMA

Ошибка передачи посредством DMA

HAL_UART_IRQHandler() разработан таким образом, что нам не нужно вдаваться в подробности реализации обработки ошибок UART. Код HAL автоматически выполнит все необходимые шаги для обработки ошибки (например, сброс флагов событий, бита отложенного состояния и т. д.), оставляя нам ответственность за обработку ошибки на уровне

HAL_UART_ErrorCallback()

Универсальные асинхронные последовательные средства связи

232

приложения (например, мы можем попросить другой узел повторно отправить поврежденный кадр данных).

Прочитайте внимательно

Во время написания данной главы, 2 декабря 2015 года, едва уловимый баг не позволял правильно контролировать ошибку переполнения Overrun error. Вы можете прочитать больше о нем на официальном форуме ST17. Этот баг можно воспроизвести даже со вторым примером данной главы. Запустите пример на Nucleo и нажмите клавишу «3» на клавиатуре, оставив ее нажатой. Через некоторое время микропрограмма зависнет. Это происходит потому, что после возникновения ошибки переполнения HAL вновь не перезапускает процесс получения. Вы можете устранить данный баг с помощью функции

следующим образом:

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { if(huart->ErrorCode == HAL_UART_ERROR_ORE)

HAL_UART_Receive_IT(huart, readBuf, 1);

}

}

8.6. Перенаправление ввода-вывода

В Главе 5 мы узнали, как использовать функцию полухостинг для отправки отладочных сообщений на консоль OpenOCD с помощью функции Си printf(). Если вы уже использовали данную функцию, то вы знаете, что существует два сильных ограничения:

полухостинг значительно замедляет выполнение микропрограммы;

он также не позволяет вашей микропрограмме работать, если она выполняется без сеанса отладки (из-за того, что полухостинг реализован с использованием программных точек останова).

Теперь, когда мы знакомы с управлением UART, мы можем переопределить необходимые системные вызовы (_write(), _read() и т. д.), чтобы перенаправить стандартные потоки STDIN, STDOUT и STDERR на USART2 Nucleo. Это можно легко сделать следующим образом:

Имя файла: system/src/retarget/retarget.c

14 #if !defined(OS_USE_SEMIHOSTING)

15

16#define STDIN_FILENO 0

17#define STDOUT_FILENO 1

18#define STDERR_FILENO 2

20UART_HandleTypeDef *gHuart;

22void RetargetInit(UART_HandleTypeDef *huart) {

23gHuart = huart;

25/* Отключение буферизации ввода/вывода для потока STDOUT,

17 https://community.st.com/s/question/0D50X00009XkflRSAR/haluartirqhandler-bug

Универсальные асинхронные последовательные средства связи

233

26* чтобы символы отправлялись сразу после их печати. */

27setvbuf(stdout, NULL, _IONBF, 0);

28}

29

30int _isatty(int fd) {

31if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)

32return 1;

33

34errno = EBADF;

35return 0;

36}

37

38int _write(int fd, char* ptr, int len) {

39HAL_StatusTypeDef hstatus;

40

41if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {

42hstatus = HAL_UART_Transmit(gHuart, (uint8_t *) ptr, len, HAL_MAX_DELAY);

43if (hstatus == HAL_OK)

44return len;

45else

46return EIO;

47}

48errno = EBADF;

49return -1;

50}

51

52int _close(int fd) {

53if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)

54return 0;

55

56errno = EBADF;

57return -1;

58}

59

60int _lseek(int fd, int ptr, int dir) {

61(void) fd;

62(void) ptr;

63(void) dir;

64

65errno = EBADF;

66return -1;

67}

68

69int _read(int fd, char* ptr, int len) {

70HAL_StatusTypeDef hstatus;

71

72if (fd == STDIN_FILENO) {

73hstatus = HAL_UART_Receive(gHuart, (uint8_t *) ptr, 1, HAL_MAX_DELAY);

74if (hstatus == HAL_OK)

75return 1;

Универсальные асинхронные последовательные средства связи

234

76else

77return EIO;

78}

79errno = EBADF;

80return -1;

81}

82

83int _fstat(int fd, struct stat* st) {

84if (fd >= STDIN_FILENO && fd <= STDERR_FILENO) {

85st->st_mode = S_IFCHR;

86return 0;

87}

88

89errno = EBADF;

90return 0;

91}

92

93 #endif //#if !defined(OS_USE_SEMIHOSTING)

Чтобы перенаправить стандартные потоки в вашей микропрограмме, вы должны удалить макрос OS_USE_SEMIHOSTING на уровне проекта и инициализировать библиотеку, вызывая RetargetInit(), передавав указатель на экземпляр UART2 UART_HandleTypeDef. Например, следующий код показывает, как использовать функции printf()/scanf() в вашей микропрограмме:

int main(void) { char buf[20]; HAL_Init();

SystemClock_Config();

MX_GPIO_Init();

MX_USART2_UART_Init();

RetargetInit(&huart2);

printf(«Write your name: «); scanf(«%s», buf); printf(«rnHello %s!rn«, buf); while(1);

}

Если вы собираетесь использовать функции printf()/scanf() для печати/чтения типов данных с плавающей точкой float в консоль последовательного порта (также как и если вы собираетесь использовать sprintf() и аналогичные процедуры), вам нужно явно включить поддержку чисел формата float в newlib-nano – более компактной версии библиотеки среды выполнения Си для встраиваемых систем. Для этого перейдите в меню

Project → Properties…, затем перейдите в C/C++ Build → Settings → Cross ARM C++ Linker → Miscellaneous и установите флажок Use float with nano printf/scanf в соот-

ветствии с нужной вам функцией, как показано на рисунке 13. Это увеличит размер бинарного файла микропрограммы.

Универсальные асинхронные последовательные средства связи

235

Рисунок 13: Как включить поддержку чисел формата float в printf() и scanf()

Форум РадиоКот • Просмотр темы — Проблемы обработки ошибок по UART в stm32

Сообщения без ответов | Активные темы

ПРЯМО СЕЙЧАС:

Автор Сообщение

Не в сети

Заголовок сообщения: Проблемы обработки ошибок по UART в stm32

СообщениеДобавлено: Вт окт 28, 2014 20:27:17 

Родился

Зарегистрирован: Пн мар 11, 2013 01:46:59
Сообщений: 19

Рейтинг сообщения: 0

Добрый вечер. Есть плата STM32VL Discovery. Делаю на ней мост.По UART передаю с компа данные, данные принимаются и передаются по SPI дальше и соответственно ответ от устройства принимаю по SPI и передаю по UART в компьютер.
UARTработает на 115200. В принципе программа работает при идеальных условиях(если посылка идет с компа на правильной скорости).Решил сделать обработчик ошибок по UART, если ошибка то просто пропускается байт и соответственно он не запихивается в SPI.
Возникла проблема, что при передаче например на другой скорости, умышленно передаю на 19200 например, то обработка работает как то через раз и не все байты отсекаются.Хотя ,например , на скорости 460800 все прекрасно отрабатывается.
Проект собран IAR 6.21.1. Использую UART1 прерывание по приему(данные в нем отправляю в SPI), SPI1(прерывание по приему,данные отправляются в UART). Может кто увидит где я накосячил. Спасибо

Вложения:


Repeater.rar [205.82 KiB]

Скачиваний: 179

Вернуться наверх
 

ПрофильПрофиль

 

Реклама

dosikus

Не в сети

Заголовок сообщения: Re: Проблемы обработки ошибок по UART в stm32

СообщениеДобавлено: Вт окт 28, 2014 20:43:39 

Друг Кота
Аватар пользователя

Карма: 30

Рейтинг сообщений: 155

Зарегистрирован: Пн июл 28, 2008 22:12:01
Сообщений: 3604

Рейтинг сообщения: 0

Код:

if(USART1->SR & (USART_SR_PE | USART_SR_FE | USART_SR_NE | USART_SR_ORE)){
        temp = USART1->SR;
        temp = USART1->DR;
        temp = USART1->DR;
        USART1->SR &= ~USART_SR_RXNE;

PE,FE,NE,ORE чистятся вычиткой SR с последующей вычиткой DR.
Вычитка SR уже происходит в if ( …) .
RXNE чистится вычиткой DR .
Итого :

Код:

if(USART1->SR & (USART_SR_PE | USART_SR_FE | USART_SR_NE | USART_SR_ORE)){
        temp = USART1->DR;

Вернуться наверх
Реклама

halisi

Не в сети

Заголовок сообщения: Re: Проблемы обработки ошибок по UART в stm32

СообщениеДобавлено: Вт окт 28, 2014 20:51:31 

Зарегистрирован: Пн мар 11, 2013 01:46:59
Сообщений: 19

Рейтинг сообщения: 0

Спасибо.То я подстраховаться так пытался. Поправил, но эффекта нет.Заметил что ставлю скорость больше передачи- все отлично, меньше чем 115200 — проскакивают байты(

Вернуться наверх

dosikus

Не в сети

Заголовок сообщения: Re: Проблемы обработки ошибок по UART в stm32

СообщениеДобавлено: Вт окт 28, 2014 20:56:25 

Друг Кота
Аватар пользователя

Карма: 30

Рейтинг сообщений: 155

Зарегистрирован: Пн июл 28, 2008 22:12:01
Сообщений: 3604

Рейтинг сообщения: 1

ХЗ я подобными извращениями не занимался .
Может стоит перенести на F0 , там USART более навороченный с Auto baud rate detection.
И еще — у тебя CMSIS старая , в новой одни хэдеры …

Вернуться наверх
Реклама

ИБП MEAN WELL серии DRC-180 на DIN-рейку – новое решение для пожарно-охранных систем

Компания MEAN WELL расширила семейство DRC-40/60/100 – недорогих ИБП (UPS) 2-в-1 (ИП и контроллер заряда/разряда АКБ в одном корпусе) с креплением на DIN-рейку. Теперь доступны модели мощностью 180 Вт новой серии DRC-180.
Источник питания DRC-180 предназначен для создания систем бесперебойного питания с внешней АКБ и может использоваться в охранно-пожарных системах, системах аварийной сигнализации, контроля доступа и в других приложениях, где требуется простая, недорогая и в то же время качественная система бесперебойного питания соответствующей мощности.

Подробнее>>

halisi

Не в сети

Заголовок сообщения: Re: Проблемы обработки ошибок по UART в stm32

СообщениеДобавлено: Вт окт 28, 2014 21:09:32 

Зарегистрирован: Пн мар 11, 2013 01:46:59
Сообщений: 19

Рейтинг сообщения: 0

Спасибо.Шаблон с этим CMSIS как сделал давно, так и пользуюсь. Контроллер возможности нет менять( Буду пыхтеть дальше.

Вернуться наверх
Реклама

menzoda

Не в сети

Заголовок сообщения: Re: Проблемы обработки ошибок по UART в stm32

СообщениеДобавлено: Вт окт 28, 2014 21:19:22 

Вымогатель припоя
Аватар пользователя

Карма: 7

Рейтинг сообщений: 56

Зарегистрирован: Вт авг 28, 2012 22:21:33
Сообщений: 535

Рейтинг сообщения: 0

При передаче на скорости отличной от ожидаемой некоторые символы (последовательности символов) вполне могут быть распознаны как нормальные, значение у них будет уже другое, но тем не менее. Введи понятие кадра, определи его границы, используй контрольную сумму и будет тебе счастье.

Вернуться наверх

halisi

Не в сети

Заголовок сообщения: Re: Проблемы обработки ошибок по UART в stm32

СообщениеДобавлено: Вт окт 28, 2014 21:30:32 

Зарегистрирован: Пн мар 11, 2013 01:46:59
Сообщений: 19

Рейтинг сообщения: 0

Хотелось бы просто байт принял — байт отослал.Неужели мк не может отследить например ошибку кадра 100%?. Спасибо за совет.

Вернуться наверх

menzoda

Не в сети

Заголовок сообщения: Re: Проблемы обработки ошибок по UART в stm32

СообщениеДобавлено: Вт окт 28, 2014 21:42:49 

Вымогатель припоя
Аватар пользователя

Карма: 7

Рейтинг сообщений: 56

Зарегистрирован: Вт авг 28, 2012 22:21:33
Сообщений: 535

Рейтинг сообщения: 0

Зависит от конкретной аппаратной реализации, но 100% никогда не будет. Лень рисовать картинки, к примеру, значение 0xFF, переданное на скорости 9600, распознается как 0x7F приемником настроенным на скорость 19200. Включение проверки четности позволит отсеять некоторые «коллизии», но не все.

Так что если нужна близкая к 100% вероятность распознавания ошибки, то без усложнений не обойтись (довольно значительных по сравнению с «принял байт, передал байт», но тем не менее вполне реализуемых).

Вернуться наверх

halisi

Не в сети

Заголовок сообщения: Re: Проблемы обработки ошибок по UART в stm32

СообщениеДобавлено: Вт окт 28, 2014 21:54:11 

Зарегистрирован: Пн мар 11, 2013 01:46:59
Сообщений: 19

Рейтинг сообщения: 0

Значит буду допиливать…

Вернуться наверх

Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Последовательный порт stm32

Получим необходимый минимум информации об интерфейсах UART микроконтроллеров STM32. Научимся управлять ими через регистры библиотеки CMSIS.

Предыдущий урок     Список уроков     Следующий урок

UART – это наиболее распространенный последовательный интерфейс передачи данных. Через него к микроконтроллеру могут быть подключены самые разные устройства: от миниатюрного цифрового датчика, до компьютера. На базе UART могут создаваться даже локальные сети. Нам этот интерфейс необходим как минимум для отладки программ.

Протокол интерфейса UART.

Данные передаются асинхронным последовательным кодом.

Протокол UART

Интерфейс называется асинхронным. Это означает, что данные могут быть переданы в любой момент, без использования между приемником и передатчиком синхронизирующих импульсов. В одну сторону данные предаются с помощью одного сигнала. Сам сигнал содержит как данные, так и синхронизирующую информацию.

Для того, чтобы приемник понял, что начинается передача данные сопровождаются специальными битами: стартовым и стоповым.

В режиме ожидания сигнал на входе приемника находится в высоком уровне. Первым передается стартовый бит. Он всегда имеет низкий уровень. По первому перепаду сигнала в низкий уровень приемник начинает отсчитывать внутренние синхроимпульсы. Зная скорость передачи, а значит и период поступления входных битов, приемник считывает состояние следующих 8 битов. В конце передатчик формирует стоп-бит, всегда высокого уровня.

Все биты передаются за одинаковые промежутки времени. Время передачи одного бита определяет скорость передачи интерфейса. Часто она указывается в бодах (бит в секунду). Как я писал выше, кроме собственно данных в поток добавляются биты синхронизации. Таким образом, для передачи байта требуется не 8, а 10 битов.

Погрешность скорости передачи не должна превышать 5% (рекомендуется не более 1,5 %.)

Я рассказываю о самом распространенном формате протокола UART. Бывают варианты с различными количествами битов данных, обычно от 5 до 9. Могут использоваться форматы с двумя стоповыми битами. Иногда добавляется бит контроля четности. Но такие варианты используются редко.

Главное, что надо знать:

  • в неактивном режиме (состоянии ожидания) выход передатчика UART находится в высоком состоянии;
  • передача байта начинается со стартового бита, который равен 0 (низкий уровень сигнала);
  • передача байта заканчивается стоповым битом, который равен 1 (высокий уровень сигнала);
  • данные передаются, начиная с младшего бита;
  • для передачи байта требуется 10 битов;
  • время передачи одного байта вычисляется исходя из скорости обмена и количества битов  в пакете (10 бит при передаче байта).

Существуют стандартные скорости передачи интерфейса 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.

  • При частоте тактирования 72 МГц скорость передачи данных до 4,5 Мбит/сек.
  • Размерность передаваемых данных 8 или 9 бит.
  • Могут быть заданы форматы с 1 или 2 стоповыми битами.
  • Интерфейс поддерживает физический уровень протокола инфракрасного порта (IRDA).
  • Также поддерживается интерфейс контактных смарт-карт ISO 7816.
  • Передача и прием данных могут происходить с использованием контроллера прямого доступа к памяти (DMA).
  • Интерфейс может работать в синхронном режиме.

Я коротко, упрощенно расскажу, как работает UART STM32.

Вот часть функциональной схемы интерфейса, через которую происходит передача и прием данных. Все остальное – это управляющая часть. Я ее не привел.

Структурная схема

Передающая и приемная часть работают совершенно независимо друг от друга. Из общего у них только тактовый генератор. Т.е. могут быть задействован только приемник, или только передатчик. Приемник и передатчик могут работать с разными устройствами, разными протоколами верхнего уровня и т.п. Единственно у них должна быть одинаковая скорость и формат данных.

Собственно передатчик состоит из 2 регистров: регистра сдвига (Transmit Shift Register) и буферного регистра (TDR).

Данное загружается в буферный регистр передатчика. Программно доступен только он. Если передача предыдущего данного закончена и регистр сдвига пуст, то данное перегружается в него, сдвигается и побитно поступает на выход TX. Как только данное перегружено в регистр сдвига, буферный регистр освобождается и в него может быть загружено новое слово.  Оно будет ждать окончания передачи и автоматически перегрузится в сдвиговый регистр.

Таким образом, происходит буферизация данных передатчика. Это позволяет реализовывать передачу данных без пауз между словами. После загрузки буферного регистра есть время на запись нового данного, равное времени передачи. Если успевать заполнять буферный регистр, то данные будут передаваться сплошным потоком.

Об окончании передачи данных регистром сдвига и освобождении буферного регистра сообщают специальные флаги. По ним могут быть сформированы прерывания.

Приемная часть устройства также состоит из двух регистров:  регистра сдвига (Receive Shift Register) и буферного регистра (RDR). С входа RX данные поступают на сдвиговый регистр, сдвигаются и, после формирования полного слова, перегружаются в буферный регистр. Буферный регистр доступен программно. Из него считывается полученное данное. Если данные поступают сплошным потоком, без пауз, то после приема данного есть время, равное времени передачи слова, на то, чтобы считать его из буферного регистра. Иначе придет новое данное, а старое будет потеряно.

Регистры UART.

Регистров много, битов еще больше. Я не претендую на подробное описание регистров UART. Приведу только информацию, необходимую нам в ближайших уроках.

USART_SR  — регистр состояния.

Формат SR

Прежде всего, нам интересны флаги, сообщающие о состоянии приема и передачи.

  • Бит 7 TXE. Буферный регистр передатчика пуст. Флаг становится равным 1 после того, как данное перегружается в регистр сдвига. Т.е. флаг сообщает о том, что буферный регистр пуст, может быть загружено новое данное. Флаг устанавливается аппаратно и сбрасывается после записи байта в буферный регистр USART_DR.
  • Бит 6 TC. Флаг устанавливается аппаратно и сообщает о том, что передача данного закончена, сдвиговый регистр пуст. Если предыдущий флаг (TXE) говорит о том, что можно в буферный регистр загружать новое данное. Физическая передача может продолжаться, на выходе TX возможно идет поток битов. То флаг TC сообщает, что все биты переданы. Часто этот момент необходимо определять для отключения передатчика шинного интерфейса сети, например, в интерфейсе RS-485. Сбрасывается флаг последовательным чтением регистров USART_SR и USART_DR.
  • Бит 5 RXNE. Буферный регистр приема не пуст. Флаг сообщает, что в буферном регистре приемника есть данное. Данное должно быть считано до прихода следующего, иначе оно будет потеряно. Сбрасывается флаг при чтении регистра USART_DR.

UART способен определить некоторые ошибки приема данных, возникающие из-за искажения сигнала при передаче или за счет неправильных программных операций с ним. Об ошибках сигнализируют следующие 4 бита.

  • Бит 3 ORE. Ошибка переполнения. Флаг устанавливается в случае, если в приемный буферный регистр поступило новое данное, а предыдущее считано не было. Т.е. при потере данного.
  • Бит 2 NE. Флаг устанавливается при выделении шума во входном сигнале. Наличие шума определяется как слишком частое переключение входного сигнала.
  • Бит 1 FE. Ошибка приема фрейма (кадра). Возникает, когда не был выделен стоп-бит. Т.е. после приема стартового бита и  8 битов данных на месте стопового бита UART считал сигнал низкого уровня.
  • Бит 0 PE.  Ошибка паритета. Сигнализирует об ошибке при включенном контроле четности.

Все биты сбрасываются программно последовательным чтением сначала регистра USART_SR, затем USART_DR.

USART_DR  — регистр данных.

Формат DR

Используется для записи данных в буферный регистр передатчика и чтения данных из буферного регистра приемника. На самом деле это 2 регистра, для обращения к которым используется один адрес.

USART_BRR — регистр, задающий скорость обмена.

Формат BRR

Регистр содержит значение делителя частоты (USARTDIV), который определяет скорость передачи и приема данных.

Скорость вычисляется по формуле:

BAUD = Fck / (16 * USARTDIV), где

  • BAUD – скорость, бод.
  • Fck – входная частота тактирования UART:
    • сигнал PCLK2 для UART1 (шина APB2);
    • сигнал PCLK1 для UART2 и 3 (шина APB1).
  •  USARTDIV – значение регистра USART_BRR.

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. Отрицательные – это дробные разряды. Разряд – 1 это ½ или в десятичном виде 0,5. -2 это ¼ или 0,25. И т.д. Это стандартная форма представления дробного числа с фиксированной запятой. Для перевода в привычную десятичную форму можно воспользоваться формулой:

Десятичное значение 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.

Формат CR1

  • Бит 13 UE. Разрешение работы UART. Отключение UART (UE=0) уменьшает ток потребления микроконтроллера.
  • Бит 12 M. Задает длину слова 8 бит (M=0) или 9 бит (M=1).
  • Бит 10 PCE. Разрешение контроля четности (PCE=1).
  • Бит 7 TXEIE. Разрешение прерывания по флагу TXE, т.е. когда буферный регистр передатчика пуст.
  • Бит 6 TCIE. Разрешение прерывания по флагу TC, т.е. когда пуст сдвиговый регистр передатчика.
  • Бит 5 RXNEIE. Разрешение прерывания по флагу RXNE, т.е. когда в буферном регистре приемника есть непрочитанное данное.
  • Бит 3 TE. Разрешение работы передатчика.
  • Бит2 RE. Разрешение работы приемника.

У всех разрядов активный уровень единица.

USART_CR2 – управляющий регистр 2.

Формат CR2

Нам интересно только одно поле.

  • Биты 13:12 STOP. Биты задают формат стопового признака слова:
    • 00 – 1 стоп-бит;
    • 01 – 0,5 стоп-бита;
    • 10 – 2 стоп-бита;
    • 11 – 1,5 стоп бита.

USART_CR3 – управляющий регистр 3.

Формат CR3

Пока при инициализации UART запишем в этот регистр 0.

Работа с UART через регистры CMSIS.

Большей частью мы будем управлять UART с помощью HAL- функций. Но бывают задачи, когда без прямого обращения к регистрам UART не обойтись.

Например, сейчас я разрабатываю центральный контроллер для системы управления шаговыми двигателями. Передача данных происходит со скоростью 1 Мбит/сек, и операции обмена крайне критичны ко времени выполнения. Тратить время на вызовы функций библиотеки HAL при такой задаче очень расточительно. Все удачно реализовывается при прямом обращении к регистрам UART.

Поэтому, я решил, коротко рассказать о таком способе управления передачей данных.

Поставим задачу – реализовать эхо-терминал. Т.е. устройство, которому мы посылаем данные и получаем их в ответ. Будем использовать UART1. В нашей системе он уже подключен к компьютеру.

Создадим проект Lesson20_1. В нем настроим только систему тактирования на частоту 72 мГц.

В файле main.c создаем блок инициализации UART.

  /* USER CODE BEGIN SysInit */

  // инициализация UART1

Включаем тактирование UART1. Он подключен к шине APB2.

RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // включаем тактирование UART1

UART1 использует выводы: PA9 (для сигнала TX) и PA10 (сигнал RX). Надо задать конфигурацию для них.

RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // разрешаем тактирование порта GPIOA

// настройка вывода PA9 (TX1) на режим альтернативной функции с активным выходом
// Биты CNF = 10, ,биты MODE = X1
GPIOA->CRH &= (~GPIO_CRH_CNF9_0);
GPIOA->CRH |= (GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9);

// настройка вывода PA10 (RX1) на режим входа с подтягивающим резистором
// Биты CNF = 10, ,биты MODE = 00, ODR = 1
GPIOA->CRH &= (~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 и загрузить в регистр данных передаваемое число.

while ((USART1->SR & USART_SR_TXE) == 0) {}
USART1->DR = d;

Для приема надо дождаться, когда бит RXNE станет равным 1 и считать из регистра данных принятое число.

while ((USART1->SR & USART_SR_RXNE) == 0) {}
d = USART1->DR;

Для реализации эхо-терминала поместим такой блок в цикл while:

/* Infinite loop */
  /* USER CODE BEGIN WHILE */

  while (1)
  {

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

      // получить данное
      while ((USART1->SR & USART_SR_RXNE) == 0) {}
      uint8_t d = USART1->DR;

      // отослать данное назад
      while ((USART1->SR & USART_SR_TXE) == 0) {}
      USART1->DR = d;
  }

  /* USER CODE END 3 */

Загрузить проект можно по ссылке:

Зарегистрируйтесь и оплатитеВсего 60 руб. в месяц за доступ ко всем ресурсам сайта!

 

Проверяем.

Загружаем программу в микроконтроллер.

Запускаем программу CoolTerm.

Окно CoolTerm

Нажимаем Options, выбираем COM-порт, скорость обмена.

Окно CoolTerm

Нажимаем кнопку Connect.

В верхнем меню, в закладке Connection выбираем Send String (Послать строку).

Набираем текстовую информацию, нажимаем Send и посланная строка появляется в окне принятых данных.

Окно терминала CoolTerm

Эхо-терминал работает.

Программу 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.

/* USER CODE BEGIN 1 */

void USART1_IRQHandler(void) {

}

Не забываем добавить в файл stm32f1xx_it.h прототип функции обработки прерывания.

/* Exported functions prototypes ———————————————*/
void USART1_IRQHandler(void);

Остается заполнить функцию обработки прерывания.

/* USER CODE BEGIN 1 */

void USART1_IRQHandler(void) {

uint8_t d = USART1->DR; // получить данное
USART1->DR = d; // отослать данное назад
}

/* USER CODE END 1 */

Загрузить проект можно по ссылке:

Зарегистрируйтесь и оплатитеВсего 60 руб. в месяц за доступ ко всем ресурсам сайта!

 

Теперь цикл while пустой. В нем можно выполнять любые задачи. Микроконтроллер будет прерывать его только на короткое время при приеме данного.

В следующем уроке будем работать с UART через HAL-функции. Поговорим об отладке программ с помощью UART.

Предыдущий урок     Список уроков     Следующий урок

Автор публикации


265

Комментарии: 1906Публикации: 195Регистрация: 13-12-2015

В данном занятии мы поработаем с механизмом обработки прерываний от шины USART.

С прерываниями данными мы уже с вами знакомы из урока 14, только использовали мы при этом библиотеку HAL. Теперь нам предстоит работа с библиотекой LL, что не является сильно простой задачей, но так как в уроке 161 мы полностью познакомились с аппаратной частью шины USART, со всеми битами всех её регистров, то мы с нашей задачей обязательно справимся.

Схема наша также не изменилась, поэтому можем смело приступить к практической части урока.

Проект занятия был сделан из проекта прошлого урока с именем LL_USART_RX и имя ему было присвоено LL_USART_INT.

Откроем проект в Cube MX и включим прерывания на шине USART1

Сгенерируем проект, откроем его в Keil, подключим библиотеку для драйвера индикатора max7219.c, настроим программатор на автоперезагрузку и уберём оптимизацию.

Зайдём в тело функции MX_USART1_UART_Init и увидим, что у там выставился уровень приоритета глобальных прерываний USART1, а также включились данные глобальные прерывания

  /* USART1 interrupt Init */

  NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));

  NVIC_EnableIRQ(USART1_IRQn);

Добавим две глобальные переменные

char rx_str[30], tx_str[30], tmp_str[10];

uint8_t fl=0;

uint8_t dt1;

Добавим функцию для обработки прерываний, в которой установим пользовательский флаг и заберём данные из регистра DT

/* USER CODE BEGIN 4 */

void  USART1_RX_Callback(void)

{

  dt1 = LL_USART_ReceiveData8(USART1);

  fl=1;

}

Как мы помним, что для того, чтобы сбросить флаг прерывания по заполнению буфера приёма, мы должны считать регистр DR. Поэтому мы здесь его и читаем, заодно сохраняя данные для последующего использования.

Перейдём в файл stm32f1xx_it.c и добавим прототип на нашу пользовательскую функцию обработки прерывания

/* USER CODE BEGIN PFP */

void  USART1_RX_Callback(void);

В обработчике прерываний USART1_IRQHandler обработаем установки всех нужных флагов

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

  /* USER CODE BEGIN USART1_IRQn 0 */

  if(LL_USART_IsActiveFlag_RXNE(USART1) && LL_USART_IsEnabledIT_RXNE(USART1))

  {

    USART1_RX_Callback();

  }

  else

  {

    if(LL_USART_IsActiveFlag_ORE(USART1))

    {

      (void) USART1->DR;

    }

    else if(LL_USART_IsActiveFlag_FE(USART1))

    {

      (void) USART1->DR;

    }

    else if(LL_USART_IsActiveFlag_NE(USART1))

    {

      (void) USART1->DR;

    }

  }

При обнаружении установленных флагов различных ошибок мы читаем регистр DR, что является условием их сброса. В случае использования (void) данные считаются в регистр АЛУ и никуда дальше не пойдут, что обеспечит меньше машинных циклов для обработки прерывания

Если бы мы писали данную программу на ассемблере, то, скорее всего, достаточно было бы одной инструкции.

А при обнаружении установленного флага RXNE мы отправляемся в пользовательский обработчик.

Вернёмся в файл main.c и в функции main() включим прерывания по заполнению буфера, а также при обнаружении тех или иных ошибок в работе шины

  Number_7219(87654321);

  LL_USART_EnableIT_RXNE(USART1);

  LL_USART_EnableIT_ERROR(USART1);

Также нам надо будет немного переделать функцию приёма и передачи USART_RX_TX_Str.

Удалим оттуда код чтения буфера приёма, причём в обоих местах

while (!LL_USART_IsActiveFlag_RXNE(USART1)) {}

rx_dt[ind] = (uint8_t)(USART1->DR & 0x00FF);

И заменим вот на это (также в обоих местах)

  uint16_t ind = 0;

  while (!fl) {}

  fl=0;

  rx_dt[ind] = dt1;

    ind++;

    while (!fl) {}

    fl=0;

    rx_dt[ind] = dt1;

Мы дождались установки флага, провалились вниз, сбросили его и сохранили считанные данные в наш буфер.

Больше в коде ничего не меняется.

Соберём код, прошьём контроллер, включим обмен в терминальной программе и запустим наш скрипт, который мы писали в прошлом уроке.

А вот и результат работы кода

Всё прекрасно работает!

Итак, на данном уроке мы научились пользоваться механизмом обработки прерываний от шины USART. Какие достоинства прерываний существуют для работы программы, думаю, объяснять не нужно.

Всем спасибо за внимание!

Предыдущий урок Программирование МК STM32 Следующий урок

Исходный код

Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6

Программатор недорогой можно купить здесь ST-Link V2

Переходник USB to TTL можно приобрести здесь ftdi ft232rl

Индикатор светодиодный семиразрядный с драйвером MAX7219

Логический анализатор 16 каналов можно приобрести здесь

Смотреть ВИДЕОУРОК (нажмите на картинку)

STM LL. USART. Interrupt


Post Views:
1 867

Итак, совсем недавно мы познакомились с приемо-передатчиком USART в микроконтроллерах STM32F4 и создали пример для передачи данных в окружающий мир (вот). Как и обещал, сегодня разберемся с приемом данных. Теории не будет, все уже вроде обсудили при работе с передачей, так что без лишних прелюдий переходим сразу к написанию программы )

Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там — STM32CubeMx. Кроме того, вот глобальная рубрика по STM32, а также статья на смежную тему из нового курса: STM32 UART. Прием и передача данных по UART в STM32CubeMx.

Для начала рассмотрим общую последовательность действий, необходимых для запуска USART в STM32F4 и настройки его для работы в качестве приемника:

  • Работаем с тактированием – включаем тактирование приемо-передатчика, а заодно и тактирование порта, который отвечает за ножки Rx/Tx.
  • Сначала настраиваем нужные ножки микроконтроллера, а затем и сам USART.
  • Включаем прерывания – функция NVIC_EnableIRQ() и запускаем USART — USART_Cmd().
  • Наконец включаем прерывание, которое нам в данном случае понадобится, то есть прерывание по приему данных — для этого нам пригодится функция USART_ITConfig().

Вот в принципе и все. Вся работа будет в прерывании, соответственно необходимо будет реализовать соответствующий обработчик. Будем использовать, как и при передаче данных, модуль USART1, так что многое будет точно таким же, как и в том примере.

/***************************************************************************************/
// Подключаем файлы
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_usart.h"


/***************************************************************************************/
// Объявляем переменные
GPIO_InitTypeDef gpio;
USART_InitTypeDef usart;
// Будем принимать 16 байт, к примеру
uint8_t receivedData[16];
uint8_t bytesToReceive = 16;
// Счетчик принятых байт
uint8_t receivedDataCounter = 0;
// Пока все идет точно также, как и в случае с передачей


/***************************************************************************************/
// Инициализируем все подряд
void initAll()
{
	// Глобальное разрешение прерываний
	__enable_irq();

	// Тактирование, куда ж без него-то
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

	// Настраиваем ножки  контроллера, тут все понятно
	GPIO_StructInit(&gpio);

	gpio.GPIO_Mode = GPIO_Mode_AF;
	gpio.GPIO_Pin = GPIO_Pin_9;
	gpio.GPIO_Speed = GPIO_Speed_50MHz;
	gpio.GPIO_OType = GPIO_OType_PP;
	gpio.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(GPIOA, & gpio);

	gpio.GPIO_Mode = GPIO_Mode_AF;
	gpio.GPIO_Pin = GPIO_Pin_10;
	gpio.GPIO_Speed = GPIO_Speed_50MHz;
	gpio.GPIO_OType = GPIO_OType_PP;
	gpio.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(GPIOA, & gpio);

	// Обязательно вызываем эту функцию
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);

	// Настраиваем модуль USART
	USART_StructInit(&usart);
	usart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	usart.USART_BaudRate = 9600; 
	USART_Init(USART1, &usart); 

	// Включаем прерывания и запускаем USART
	NVIC_EnableIRQ(USART1_IRQn);
	USART_Cmd(USART1, ENABLE);
}


/***************************************************************************************/
// Функция main()
int main()
{
	// Тут нам нужно лишь вызвать функцию инициализации и запустить процесс – то есть включить прерывание по приему данных
	initAll();
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	while(1)
	{
		__NOP();
	}
}


/***************************************************************************************/
// Прерывание
void USART1_IRQHandler()
{
	// Убеждаемся, что прерывание вызвано новыми данными в регистре данных
	if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	{
		// Чистим флаг прерывания
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);

		// То ради чего все затеяли – принимаем данные )
		receivedData[receivedDataCounter]  = USART_ReceiveData(USART1);

		// Приняли? Увеличиваем значение счетчика!
		receivedDataCounter ++;

		// Приняли 16 байт – выключаем
		if  (receivedDataCounter == bytesToReceive)
		{
			USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
		}
	}
}  


/***************************************************************************************/

Вот такой получился пример. За исключением обработчика прерывания все также как и с передачей данных, можно было, в принципе, и в одну статью все уместить. Отмечу, кстати вызов функции GPIO_PinAFConfig():

GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);

Я когда первый раз работал в STM32F4 с USART’ом, упустил из виду то, что надо вызывать эту функцию и некоторое время не мог понять, почему ничего не работает… После STM32F10x казалось, что достаточно вот этого:

gpio.GPIO_Mode = GPIO_Mode_AF;

Но нет, тут все чуть иначе. Так что при работе с любой периферией в STM32F4 не забываем про вызов функции GPIO_PinAFConfig(). Что еще можно сказать про USART? Да вроде бы все… Так что, если возникли какие-нибудь вопросы, обязательно спрашивайте в комментариях, а на сегодня это пожалуй все, до скорого!

P. S. Я тут кстати себе сделал небольшую библиотеку для ускорения работы с USART’ом, на днях обязательно выложу, буду рад, если кому-нибудь пригодится.

Понравилась статья? Поделить с друзьями:
  • Stop fehler fahrwerk ошибка туарег
  • Stm32 can обработка ошибок
  • Steam ошибка чтения диска дота 2
  • Stk500 ошибка ардуино
  • Still погрузчик ошибка