串口通信实验
一、实验目的
从电脑串口助手发送长度为200以内任意长度的字符串给STM32串口1(字符串以回车换行标识结束),STM32接收到字符串之后,一次性通过串口1把所有数据返回给电脑。
二、硬件连接
引脚 | 连接 |
PA9(USART1_TX) | USB-TTL串口RXD |
PA10(USART1_RX) | USB-TTL串口TXD |
三、代码及分析
( 1 )定义函数uart_init用来调用HAL_UART_Init初始化串口参数配置:
UART_HandleTypeDef UART1_Handler;//UART句柄
void uart_init(u32 bound)
{
//UART 初始化设置
UART1_Handler.Instance=USART1; //USART1
UART1_Handler.Init.BaudRate=bound; //波特率
UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为8位数据格式
UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能UART1
HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量。
}
( 2 ) 串口MSP函数HAL_UART_MspInit函数我们自定义了其内容。
在串口初始化函数HAL_UART_Init内部,会调用串口MSP函数HAL_UART_MspInit来设置与MCU相关的配置。函数HAL_UART_Init主要用来初始化与串口相关的参数(这些参数与MCU无关),包括波特率,停止位等。而串口MSP函数HAL_UART_MspInit用来设置GPIO初始化,NVIC配置等于MCU相关的配置。
代码如下:
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_Initure;
if(huart->Instance==USART1) //如果是串口1,进行串口1 MSP初始化
{
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟
GPIO_Initure.Pin=GPIO_PIN_9; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速
GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9
GPIO_Initure.Pin=GPIO_PIN_10; //PA10
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10
#if EN_USART1_RX
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道
HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级3,子优先级3
#endif
}
}
( 3 )函数HAL_UART_IRQHandler实现串口1中断服务程序
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&UART1_Handler); //调用HAL库中断处理公用函数
}
串口接收中断流程图
串口接收中断的一般流程进行概括:当接收到一个字符之后,在函数UART_Receive_IT()中会把数据保存在串口句柄的成员变量pRxBuffPtr缓存中,同时RxXferCount计数器减1。如果我们设置RxXferSize=10,那么当接收到10个字符之后,RxXferCount会由10减到0(RxXferCount 初始值等于RxXferSize),这个时候再调用接收完成回调函数HAL_UART_RxCpltCallback进行处理。
首先,我们回到用户函数uart_init定义可以看到,在uart_init 函数中调用完HAL_UART_Init后我们还调用了HAL_UART_Receive_IT开启接收中断,并且初始化串口句柄的缓存相关参数。
代码如下:
HAL_UART_Receive_IT(&UART1_Handler,(u8*)aRxBuff,RXBUFFERSIZE);而aRxBuffer是我们定义的一个全局数组变量,RXBUFFERSIZE是我们定义的一个标识符: #define RXBUFFERSIZE 1
u8 aRxBuffer[RXBUFFERSIZE];所以,调用HAL_UART_Receive_IT函数后,除了开启接收中断外还确定了每次接收RXBUFFERSIZE个字符后标示接收结束从而进入回调函数HAL_UART_RxCpItCalback进行相应处理。
( 4 ) HAL_UART_RxCpltCallback函数定义:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)//如果是串口1
{
if((USART_RX_STA&0x8000)==0)//接收未完成,判断USART_RX_STA的最高位bit15是否为1 ,若为1,则接收完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
( 5 ) 主函数
int main(void)
{
u8 len;
u16 times=0;
Cache_Enable(); //打开L1-Cache
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz
delay_init(216); //延时初始化
uart_init(115200); //串口初始化
LED_Init(); //初始化LED
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n您发送的消息为:\r\n");
HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000); //发送接收到的数据
while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET); //等待发送结束
printf("\r\n\r\n"); //插入换行
USART_RX_STA=0;
}else
{
times++;
if(times%5000==0)
{
printf("\r\nALIENTEK STM32F7开发板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\r\n");
if(times%30==0)LED0_Toggle;//闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}