您的位置:首页 > 娱乐 > 八卦 > stm32串口通信协议(标准库 and HAL库)

stm32串口通信协议(标准库 and HAL库)

2024/10/7 2:32:25 来源:https://blog.csdn.net/2301_81374681/article/details/140595759  浏览:    关键词:stm32串口通信协议(标准库 and HAL库)

刚入门32,接触的第一个通信协议就是串口通信,所谓通信协议,就是指两个设备之间互联,将两个设备关联起来,使其可以相互之间传输数据,利用串口通信最常见的例子就是蓝牙模块。

下面为学习串口之后的一些简单的小总结:

1.串口一般都有固定的引脚,比如c8t6上USART1的TX为PA9,RX为PA10,当然,也可以将某些特定的引脚复用成USART1的TX,RX,两个设备之间进行串口通信时,TX与RX要互相交叉连接,且一定要共地,如果两个设备有单独供电的话,可以不用连接VCC

2.使用串口的时候,如果要与电脑端通信将数据打印再串口调试助手上,则电脑端要安装CH340驱动,且stm32端如果没有内置CH340模块的话,则必须用CH340将32与电脑端连接

3.串口基本结构:

4.串口初始化:

开启时钟,将所需的USART与GPIO的时钟打开,然后GPIO初始化,将TX配置为复用输出,RX为输入,最后利用USART的初始化结构体,将其余参数全部配置好,如果选择通过中断的方式接受,则需要配置NVIC中断优先级与开启中断

void USART_Init(void){//GPIO初始化结构体GPIO_InitTypeDef GPIO_InitStructure;//USART初始化结构体USART_InitTypeDef USART_InitStructure;// 打开串口GPIO的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);// 打开串口外设的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 将USART Tx的GPIO配置为推挽复用模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);// 将USART Rx的GPIO配置为浮空输入模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA, &GPIO_InitStructure);// 配置串口的工作参数// 配置波特率USART_InitStructure.USART_BaudRate = 115200;// 配置 针数据字长USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 配置停止位USART_InitStructure.USART_StopBits = USART_StopBits_1;// 配置校验位USART_InitStructure.USART_Parity = USART_Parity_No ;// 配置硬件流控制USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 配置工作模式,收发一起USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 完成串口的初始化配置USART_Init(DEBUG_USARTx, &USART_InitStructure);/*//当需要中断的时候// 串口中断优先级配置NVIC_Configuration();// 使能串口接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
*/	// 使能串口USART_Cmd(USART1, ENABLE);	    
}

5.串口发送一个字节

void Usart_SendByte(USART_TypeDef* pUSARTx,uint8_t data){//调用库函数发送一个字节的数据USART_SendData(pUSARTx, data);//判断发送标志位,若数据没来得及发送,下一个数据就过来了,则会产生数据覆盖,所以要用标志位来 //判断是否能进行下一次发送while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET);
}

6.将fputc函数重定向,使用print打印数据

int fgetc(FILE *f){/* 等待串口输入数据 */while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);return (int)USART_ReceiveData(USART1);
}

使用print时,注意,需要在魔术棒里面勾选使用微库,并在USART头文件里引用头文件<stdio.h>

只能重定向fputc到一个串口,若多个串口需要使用print,则可以使用sprint

char String[100];
sprintf(String,"Num=%d\r\n",666);
Usart_SendString(String);

7.利用阻塞的方式,读取串口数据(没成功读取数据就一直占用CPU)

while(1){if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET){RxData = USART_ReceiveData(USART1);}
}

读完DR之后不用手动清除标志位

8.通过中断的方式读取串口数据

在初始化USART时,配置NVIC,开启中断

void NVIC_Configuration(void){NVIC_InitTypeDef NVIC_InitStructure;/* 嵌套向量中断控制器组选择 *//* 提示 NVIC_PriorityGroupConfig() 在整个工程只需要调用一次来配置优先级分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);/* 配置USART为中断源 */NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;/* 抢断优先级*/NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;/* 子优先级 */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;/* 使能中断 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/* 初始化配置NVIC */NVIC_Init(&NVIC_InitStructure);
}
// 串口中断优先级配置NVIC_Configuration();// 使能串口接收中断(USART_IT_RXNE标志位一旦置1,就会向NVIC申请中断,然后在中断服务函数里面接收数据)USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

之后在中断服务函数里面执行用户所需代码

void USART1_IRQHandler(void){if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET){//执行用户所需中断代码(可以直接读取DR)//RxData = USART_ReceiveData(USART1);//清除标志位USART_ClearITPendingBit(USART1,USART_IT_RXNE);}	 
}

9.通过将数据打包进行发送,和将打包的数据解码出来

根据内容,数据包通常分为HEX数据包与文本数据包;根据长度,数据包分为固定包长的数据包与可变包长的数据包。

发送HEX数据包:

//已经打包好的数据包
uint8_t USART_TxPacket[4];void USART_SendPacket(){//发送帧头Usart_SendByte(0xFF);//发送数据包Usart_SendArray(USART_TxPacket,4);//发送帧尾Usart_SendByte(0xFE);
}

接受HEX数据包(定义中断服务函数):

void USART1_IRQHandler(){//定义静态变量,使用状态机,来控制数据的接受static uint8_t RxState =0;static uint8_t pRxPacket =0;if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)){uint8_t RxData = USART_ReceiveData(USART1);//判断帧头if(RxState == 0 && RxData == 0xFF){RxState = 1;pRxPacket = 0;}//接收数据else if(RxState == 1){USART_RxPacket[pRxPacket] = RxData;pRxPacket++;if(pRxPacket >= 4)RxState = 2;}//判断帧尾else if(RxState == 2 && RxData == 0xFE){RxState = 0;}//清除标志位USART_ClearITPendingBit(USART1,USART_IT_RXNE);}
}

接受文本数据包(与接受HEX数据包类似):

void USART1_IRQHandler(){//定义静态变量,使用状态机,来控制数据的接受static uint8_t RxState =0;static uint8_t pRxPacket =0;if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)){uint8_t RxData = USART_ReceiveData(USART1);//判断帧头if(RxState == 0 && RxData == '@'){RxState = 1;pRxPacket = 0;}//接收数据else if(RxState == 1){if(RxData == '\r'){RxState = 2;}else{USART_RxPacket[pRxPacket] = RxData;pRxPacket++;}}//判断帧尾else if(RxState == 2 && RxData == '\n'){RxState = 0;//在数据末尾加上'\0',将数据处理成完整的字符串USART_RxPacket[pRxPacket] = '\0';}//清除标志位USART_ClearITPendingBit(USART1,USART_IT_RXNE);}
}

HAL库外设初始化MSP回调机制:

在HAL库里,一般采用HAL_USART_Init()来初始化某一个具体的外设,HAL_USART_Init()待传入的参数是一个USART的句柄结构体,其最主要的部分就是Instance成员和Init成员,Instance作为一个指针指向一个具体的外设,比如

UartHandle.Instance = USART1;

而Init就是USART初始化结构体,将参数配置好后,调用Init函数即可完成初始化。

void USART_Config(void){ UartHandle.Instance          = USART1;UartHandle.Init.BaudRate     = 115200;UartHandle.Init.WordLength   = UART_WORDLENGTH_8B;UartHandle.Init.StopBits     = UART_STOPBITS_1;UartHandle.Init.Parity       = UART_PARITY_NONE;UartHandle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;UartHandle.Init.Mode         = UART_MODE_TX_RX;HAL_UART_Init(&UartHandle);/*使能串口接收断 */__HAL_UART_ENABLE_IT(&UartHandle,UART_IT_RXNE);  
}

在HAL库里调用HAL_UART_Init()之后,程序会自动调用HAL_UART_MspInit()(在HAL库被__weak修饰,用户可自行选择是否定义),用户可在该函数里面配置UART外设所用到的硬件:GPIO,NVIC以及时钟

void HAL_UART_MspInit(UART_HandleTypeDef *huart){  GPIO_InitTypeDef  GPIO_InitStruct;__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/**USART1 GPIO Configuration    PA9     ------> USART1_TXPA10    ------> USART1_RX *//* 配置Tx引脚为复用功能  */GPIO_InitStruct.Pin = GPIO_PIN_9;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_PULLUP;GPIO_InitStruct.Speed =  GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* 配置Rx引脚为复用功能 */GPIO_InitStruct.Pin = GPIO_PIN_10;GPIO_InitStruct.Mode=GPIO_MODE_AF_INPUT;	//模式要设置为复用输入模式!	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); HAL_NVIC_SetPriority(USART1_IRQn ,0,1);	//抢占优先级0,子优先级1HAL_NVIC_EnableIRQ(USART1_IRQn );		    //使能USART1中断通道  
}

HAL库中断回调机制:

HAL_UART_IRQHandler():用户在中断服务函数中调用HAL库共用中断函数,然后再调用一系列中断回调函数(都被__weak修饰,用户自行选择定义与否)

HAL库配置串口步骤:

开启串口接受中断:

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef * huart,uint8_t * pData,uint16_t Size)

发送数据(以阻塞的方式,占用CPU):

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

第一个参数为一个串口句柄结构体,第二个参数为指向发送数据缓冲区,第三个参数为要发送的数据大小,以字节为单位,第四个参数为超时时间,以ms为单位

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com