2021/07/20 悍匠暑假集训第三天

2021/07/20 悍匠暑假集训第三天


单个串口收发

1、串口在cubeMX中配置

串口在cubeMX中的配置过程如下:

  1. 首先在Connectivity标签页下将USART6打开,将其Mode设置为Asynchronous异步通讯方式。异步通讯即发送方和接收方间不依靠同步时钟信号的通讯方式。
  2. 接着将其波特率设置为115200,数据帧设置为8位数据位,无校验位,1位停止位。
    2021/07/20 悍匠暑假集训第三天
  3. 接着前往NVIC标签页下,开启USART6的中断。
  4. 点击Generate Code生成工程。

2、串口接收中断与空闲中断

串口的接收中断与空闲中断,这两种中断都是在串口进行接收时可能会发生的中断。
2021/07/20 悍匠暑假集训第三天
串口接收中断即每当串口完成一次接收之后触发一次中断。在STM32中相应的中断处理函数为USARTx_IRQHandler,中断回调函数为HAL_UART_RxCpltCallback。可以通过USART状态寄存器中的UART_FLAG_RXNE位判断USART是否发生了接收中断。
串口空闲中断即每当串口接收完一帧数据后又过了一个字节的时间没有接收到任何数据则触发一次中断,中断处理函数同样为USARTx_IRQHandler,可以通过USART状态寄存器中的UART_FLAG_IDLE判断是否发生了空闲中断。

3、串口发送数据

HAL库提供了串口发送函数HAL_UART_Transmit,通过这个函数可以从指定的串口发送数据。

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, 
				uint8_t *pData, uint16_t Size, uint32_t Timeout)

2021/07/20 悍匠暑假集训第三天
例:HAL_UART_Transmit(&huart6, "Hello World!\r\n", sizeof("Hello World!\r\n"), 100);
使能发送完成中断后,每当完成一次串口发送,串口会触发一次发送完成中断,对应的中断回调函数为HAL_UART_TxCpltCallback。

4、串口中断接收字符串数据

串口中断接收数据的实现可以通过在中断回调函数HAL_UART_TxCpltCallback或者重载USARTX_IRQHandler中实现字符串接收。

自定义全局变量

uint8_t message[200] = {0}; //接收字符串缓冲区
uint8_t offset; //接收字符串缓冲区的下标及大小
uint8_t mesg; //用于中断时,接收单个字符
uint8_t RX_Flag; //发生中断的标志

重载USARTX_IRQHandler

void USART6_IRQHandler(void){
	//该函数会清空中断标志,取消中断使能,并间接调用回调函数
	HAL_UART_IRQHandler(&huart6); 
	
	HAL_GPIO_WritePin(GPIOH, GPIO_PIN_10, GPIO_PIN_SET);

	if(offset == 200){ //以防溢出
		offset = 0;
	}	

	//将接收到的单个字符扔到自定义字符串缓冲中,形成字符串
	message[offset++] = mesg; 
	
	RX_Flag = 1;

	//实现多次数据返回
	HAL_UART_Receive_IT(&huart6, (uint8_t *)&mesg, 1);
	
}

注意:每次中断只能接收一个字符!!!为了实现多次数据返回,我们要在中断处理函数中添加HAL_UART_Receive_IT;另外,main函数在进入while循环前要调用以下代码

void main(){
	......
	MX_USART6_UART_Init();
	......
	//进入循环前需调用的函数,有中断使能的作用
	HAL_UART_Receive_IT(&huart6, (uint8_t *)&mesg, 1);
	......
	while(1){
		/* USER CODE END WHILE */

    	/* USER CODE BEGIN 3 */
		if(RX_Flag == 1){
			HAL_UART_Transmit(&huart6, message, offset, 100);
			memset(message, '\0', sizeof(message));
			HAL_Delay(100);
			RX_Flag = 0;
			HAL_GPIO_WritePin(GPIOH, GPIO_PIN_10, GPIO_PIN_RESET);
		}
		else{
			HAL_UART_Transmit(&huart6, "Hello World!\r\n", sizeof("Hello World!\r\n"), 100);
		}
		HAL_Delay(200);
  	}
  	/* USER CODE END 3 */
}

5、总体代码的效果

当未接收到数据时,每200ms发送 “Hello World!” 。当接收到字符串时,多次触发中断,利用mesg接收每次中断的单个字符,再将接收到的字符扔到message缓冲区中,从而实现字符串的接收,然后闪烁LED灯,再把接收到的字符串又发送回去。

6、APB时钟计算串口波特率

计算串口的波特率时首先要通过在定时器章节介绍的方法,通过datasheet查找到使用的串口对应的总线是APB1还是APB2,然后再在cube中找到对应总线的速率。
在获取总线速率后,可以通过USART_BRR寄存器中的USARTDIV值来计算串口的通讯速率。在该寄存器中,前12位用来表示USARTDIV的整数部分,后4位用来表示小数部分,小数部分乘以16后取整,然后存储在后4位中。
使用USARTDIV计算波特率的公式为:
2021/07/20 悍匠暑假集训第三天
在数据手册中查出USART1连接在APB1总线上, APB1速率PCLK1为42MHz。而期望的USART1的串口通讯速率为115200。

  1. 通过上述的波特率计算公式,计算出USARTDIV值为42MHz/115200/16=22.786;
  2. 将整数22转化为16进制0x16,小数0.786*16=12.576(取整12)用16进制表示为0x0C,所以存储在USART_BRR中的值最终为0x16C。
    由于这里采取了一些取整和舍弃尾数的近似手段,所以通过寄存器产生的波特率和最终期望的波特率之间是存在误差的,一般较小的误差不会影响最终的串口通讯,但是如果通讯时出现问题的话,要记得检查是否与通讯速率的实际值与理想值相差较大有关。
    2021/07/20 悍匠暑假集训第三天
上一篇:Conmi的正确答案——STM32CUBEMX创建USART1的DMA怎么实现1秒发送一次(HAL)


下一篇:基于HAL库的STM32H7工程开发系列教程一《手动配置基本工程》