STM32CubeMX_UART_printf_接收中断_DMA空闲中断_LPUART

前言

STM32CubeMX_环境搭建_GPIO_外部中断
STM32CubeMX_定时器中断_PWM

前两节简单的总结了GPIO, EXTI, TIMER的相关用法, 本节总结一下STM32串口的用法, 依然是原理性的东西不多讲, 直接开干.

先看下缩写:

  • UART(Universal Asynchronous Receiver/Transmitter, 通用异步收发器)
  • USART(Universal Synchronous/Asynchronous Receiver/Transmitter, 通用同步/异步收发器)
  • LPUART(Low Power Universal Asynchronous Receiver/Transmitter, 低功耗串行异步收发器)

USART比UART多了个同步, 选择作为同步时, 硬件表现上多了USART_CK的时钟线, 选择作为异步时, 和UART差别不大, 同步方式很少用到, 我们就只用异步方式. 仍然使用NUCLEO-F767ZI板子, 串口3连接到了板载的ST-Link的虚拟串口, 可以通过USB直接与PC通信:
在这里插入图片描述

LPUART是新出的 STM32G/H/WB系列部分STM32L系列 才有的, 顾名思义, 低功耗, 允许只使用32.768K的LSE作为时钟达到9600bps通讯速率, 超级省电; 但当时钟源是100MHz时, 可以达到33.3Mbps通讯速率, 可谓上限极高. 此外, 支持多机通信, 单线半双工通信等. 详细可参考 en.STM32G4-Peripheral-LPUART_interface_LPUART.pdf . LPUART的例子暂略, 通用用法大致一样.

HAL库函数的操作方式有轮询, 中断DMA3种, 轮询是Blocking mode, 中断和DMA是 Non-Blocking mode. 注意后缀很好区分, 如发送函数:

HAL_UART_Transmit()
HAL_UART_Transmit_IT()
HAL_UART_Transmit_DMA()

补充一点, 最近几年出的片子的UART/USART功能也越来越完善, 增加了诸如自动波特率, 数据反转, TX/RX引脚互换等亮眼操作. 暂且不表, 先从 NUCLEO-F767ZI 的串口开始吧.

STM32CubeMX新建F767工程

步骤如下:

  • MCU选择: 打开 STM32CubeMX, 点击 ACCESS TO MCU SELECTOR, 选择 STM32F767ZI
  • 调试端口配置为SWD: Pinout & Configuration -> System Core -> SYS -> Debug 选择 Serial Wire
  • Pinout & Configuration -> System Core -> RCC -> HSE 选择 Crystal/Ceramic Resonator
  • Clock Configuration:
    在这里插入图片描述

以上步骤不太熟悉的话, 可以参考 STM32CubeMX_环境搭建_GPIO_外部中断 一节.

UART配置

Pinout & Configuration -> Connectivity -> USART3 -> Mode选择异步Asynchronous, 波特率之类的就默认115200-8-N-1:
在这里插入图片描述
然后发现图中的引脚居然是PB10, PB11, 而不是原理图的PD8/PD9:
在这里插入图片描述
不要慌, 鼠标悬停提示 <Ctrl+Click> To Show Alternate(s), 我们按住Ctrl, 单击PB10, 查看USART3_TX都可以映射到哪些引脚, 发现PD8PC10变蓝, 那接着就松开Ctrl, 单击PD8, 选择USART3_TX, 同样单击PD9, 选择USART3_RX, 这样, USART3就挪到了PD8/PD9:
在这里插入图片描述

生成代码

Project Manager -> Project -> Browse 选择工程位置(Project Location), 填入工程名(Project Name), Toolchain/IDE 选择 MDK-ARM.

Project Manager -> Code Generator -> 勾选Copy only the necessary library files, 还有Generate peripheral initialization as a pair of .c/.h files per periphral

点击右上角 GENERATE CODE 按钮生成代码, 打开工程.

Keil 点击魔术棒或者Project -> Options for Target ..., 默认配置DebugST-link Debugger, 点击Setting -> Flash Download -> 勾选Reset and Run, 这样下载后可以自动复位运行.

如有疑问, 仍然去参考 STM32CubeMX_环境搭建_GPIO_外部中断 一节.

串口发送

主要是用HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) 这个函数进行发送, main.c中补充代码发送字符串和十六进制:

  /* USER CODE BEGIN 2 */
		uint8_t str_arr[] = "UART Example!\n\r";
		uint8_t hex_arr[] = {0x00, 0x02, 0x03, 0x04, 0x05, 0x06};

		uint8_t arr_length = 0;

		arr_length = sizeof(str_arr) / sizeof(*str_arr);
		HAL_UART_Transmit(&huart3, str_arr, arr_length, 100);
		HAL_Delay(1000);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		++hex_arr[0];
		arr_length = sizeof(hex_arr) / sizeof(*hex_arr);
		HAL_UART_Transmit(&huart3, hex_arr, arr_length, 100);
		
		HAL_Delay(1000);

  }
  /* USER CODE END 3 */

选择板子的虚拟串口, 115200波特率, 打开串口调试助手, 编译下载程序, 发现接收到UART Example 信息, 切换HEX显示, 符合预期:
在这里插入图片描述

printf支持

随便找个地方添加以下代码:

#ifdef __GNUC__
/* With GCC, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xFFFF);

  return ch;
}

要问哪来的, 官方的Example -> UART_Printf 复制过来的, 同时, 别忘添加头文件:

#include <stdio.h>

不然可能报错 identifier "FILE" is undefined.

然后就可以愉快的使用printf了:

	uint8_t str_arr[] = "print Example!";
	uint8_t hex_arr[] = {0x00, 0x02, 0x03, 0x04, 0x05, 0x06};
	printf("printf test: \n\r");
	printf("string array: %s\n\r", str_arr);
	printf("hex: hex_arr[1] = 0x%02x\n\r", hex_arr[1]);
	printf("num one: %d\n\r", 1u);
	printf("pi = %.3f\n\r", 3.1415926f);

打印结果:
在这里插入图片描述

串口接收中断

关闭Keil, 回到STM32CubeMX, NVIC中勾选 USART3 global interrupt, 优先级不设置么, 默认(0, 0):
在这里插入图片描述
点击 GENERATE CODE 重新生成代码, 打开工程, 补充代码:

/* USER CODE BEGIN PD */
#define RXBUFFERSIZE	28
/* USER CODE END PD */

/* USER CODE BEGIN PV */
uint8_t aRxBuffer[RXBUFFERSIZE];	//Buffer used for reception
__IO ITStatus g_uart3_rx_status = RESET;
/* USER CODE END PV */

/* USER CODE BEGIN 0 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
	if(huart->Instance == huart3.Instance) {	//if(huart == &huart3)
		g_uart3_rx_status = SET;
	}
}
/* USER CODE END 0 */

  /* USER CODE BEGIN 2 */
	HAL_UART_Receive_IT(&huart3, (uint8_t *)aRxBuffer, RXBUFFERSIZE);
  /* USER CODE END 2 */

  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		if(g_uart3_rx_status == SET) {
			g_uart3_rx_status = RESET;
			HAL_UART_Transmit(&huart3, aRxBuffer, RXBUFFERSIZE, 20);
			HAL_UART_Receive_IT(&huart3, (uint8_t *)aRxBuffer, RXBUFFERSIZE);
		}
  }
  /* USER CODE END 3 */

100ms间隔发送26个英文字母加回车换行是28字节, 发送间隔20ms以上10字节收发没有什么问题, 再快就可能出问题卡死了:
在这里插入图片描述
这种对于 定长字符串的接收 还是可以用的, 对于 不定长字符串的接收, 一般会有明显的结束标志, 比如回车换行, 典型的就是AT指令的接收, 可以设置1字节接收中断, 来一个字节存一个字节到一个数组里, 然后在中断里判断是否是结束标志如回车换行或者特殊字符之类的即可 .

DMA空闲中断

上面的接收方式使用起来还是很抓狂的, 可能会出现卡死之类, 是没有调校好的结果. 好在STM32提供了空闲中断, 当字节流不连续时, 总会出现串口总线空闲的时候, 出现空闲就表示一帧数据接收完毕, 这个时候来个中断岂不美哉, 嗯, DMA空闲中断就是这么好使. 对于定长不定长字符串的接收通通好用.

关掉Keil, 回到STM32CubeMX, 原来的工程不要了, 新建一个工程, 时钟引脚配置之类的步骤略, 到USART3的界面, 找到DMA Setting, 点击 Add 按钮添加USART3_RX的DMA, 同样添加USART3_TX的DMA: 在这里插入图片描述
然后到NVIC里面勾选中断, 优先级可以设置低一点:
在这里插入图片描述
点击生成代码, 打开工程, keil别忘记Debug里面勾选Reset and Run.

main.h添加代码:

/* USER CODE BEGIN Private defines */
#define RXBUFFERSIZE 50
/* USER CODE END Private defines */

main.c添加代码:

/* USER CODE BEGIN PV */
uint8_t aRxBuffer[RXBUFFERSIZE];
/* USER CODE END PV */

  /* USER CODE BEGIN 2 */
	__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
	HAL_UART_Receive_DMA(&huart3, (uint8_t *)aRxBuffer, RXBUFFERSIZE);
  /* USER CODE END 2 */

stm32f7xx_it.c中:

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern uint8_t aRxBuffer[];
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
void UART_RxIdleCallback(UART_HandleTypeDef *huart);
/* USER CODE END PFP */

//void USART3_IRQHandler(void)
  /* USER CODE BEGIN USART3_IRQn 1 */
	UART_RxIdleCallback(&huart3);
  /* USER CODE END USART3_IRQn 1 */

/* USER CODE BEGIN 1 */
void UART_RxIdleCallback(UART_HandleTypeDef *huart) {
	uint32_t temp;
	
	if(huart == &huart3) {
		if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) != RESET ) {
				__HAL_UART_CLEAR_IDLEFLAG(&huart3);
				temp = huart3.Instance->ISR;
				temp = huart3.Instance->ICR;
				temp = huart3.Instance->RDR; 
				HAL_UART_DMAStop(&huart3);
				temp  = hdma_usart3_rx.Instance->NDTR;	//DMA stream x number of data register
				HAL_UART_Transmit_DMA(&huart3, aRxBuffer, RXBUFFERSIZE-temp);
				HAL_UART_Receive_DMA(&huart3, aRxBuffer, RXBUFFERSIZE);
		}
	}
}
/* USER CODE END 1 */

编译下载, 打开串口调试助手, 可以看到只要长度不超过RXBUFFERSIZE定义的字节, 都会自动回传:
在这里插入图片描述

工程代码

https://download.csdn.net/download/weifengdq/11962932

微信公众号

欢迎扫描二维码关注本人微信公众号, 及时获取或者发送给我最新消息:
在这里插入图片描述

  • 3
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值