Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кармин Новиелло - Освоение STM32.pdf
Скачиваний:
2740
Добавлен:
23.09.2021
Размер:
47.68 Mб
Скачать

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

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);

}

}

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

8.5. Обработка ошибок

При работе с внешними подключениями обработка ошибок является аспектом, который мы должны строго учитывать. Периферийное устройство 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("\r\nHello %s!\r\n", 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()