stm32串口收发总结

stm32串口的使用过程:
1.使能串口时钟,同时使能串口对应的GPIO的时钟;
2.设置串口引脚的输入输出模式、速率,并初始化GPIO引脚;
3.对于需要接收数据的串口,配置其中断,并使能;
4.设置串口的波特率、数据位、停止位、奇偶校验位、流控方式、收发模式;
5.初始化串口;
6.开串口接收中断;
7.使能串口;
8.编写串口中断服务函数;
9.发送数据

补充:STM32中与串口相关的寄存器主要是状态寄存器SR和数据寄存器寄存器DR。
附图如下:
在这里插入图片描述
状态寄存器比较重要的两位是第六位RXNE和第七位TC,使用方法USARTx->SR。
TC:发送完成 (Transmission complete)
当包含有数据的一帧发送完成后,并且TXE=1时,由硬件将该位置’1’。如果USART_CR1中的
TCIE为’1’,则产生中断。由软件序列清除该位(先读USART_SR,然后写入USART_DR)
。TC
位也可以通过写入’0’来清除,只有在多缓存通讯中才推荐这种清除程序。
0:发送还未完成;
1:发送完成。

RXNE:读数据寄存器非空 (Read data register not empty)
当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位被硬件置位。如果
USART_CR1寄存器中的RXNEIE为1,则产生中断。对USART_DR的读操作可以将该位清
。RXNE位也可以通过写入0来清除,只有在多缓存通讯中才推荐这种清除程序。
0:数据没有收到;
1:收到数据,可以读出。

所以常用SR的第六位RXNE来判断是否接收到数据,若接收到数据,就将数据从DR中读取出来,以便DR接收下个数据。

在这里插入图片描述
DR[8:0]:数据值 (Data value)
包含了发送或接收的数据。由于它是由两个寄存器组成的,一个给发送用(TDR),一个给接收
用(RDR),该寄存器兼具读和写的功能。TDR寄存器提供了内部总线和输出移位寄存器之间的
并行接口(参见图248)。RDR寄存器提供了输入移位寄存器和内部总线之间的并行接口。
当使能校验位(USART_CR1中PCE位被置位)进行发送时,写到MSB的值(根据数据的长度不
同,MSB是第7位或者第8位)会被后来的校验位该取代。
当使能校验位进行接收时,读到的MSB位是接收到的校验位。

代码如下:

	//定义初始化变量
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	//使能串口2的时钟和GPIOA的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);//PA2-TX,PA3-RX
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//配置串口对应引脚的收发模式
	//USART2_RX    GPIOA.3初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);/初始化GPIOA.3	
	//USART2_TX	  GPIOA.2初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.2	
	//注意:此处TX和RX引脚都要配置,否则可能无法通信

	//USART2 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//初始化NVIC寄存器

	//USART  初始化设置
	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;//收发模式

	//初始化USART2
	USART_Init(USART2, &USART_InitStructure);
	//开启USART2接收中断
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
	//使能串口2
	USART_Cmd(USART2, ENABLE); 
	
	//编写USART2的中断服务函数
	void USART2_IRQHandler(void) 
	{
		u8 Res;
		USART_PRINTF_FLAG=2;
		
		if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断,判断读寄存器是否为空
		{
			Res =USART_ReceiveData(USART2);	//读取接收到的数据
			
			if((USART2_RX_STA&0x8000)==0)//接收未完成
			{
				if(USART2_RX_STA&0x4000)//接收到了0x0d
				{
					if(Res!=0x0a)
						USART2_RX_STA=0;//接收错误重新开始
					else 	
					{
						if(crc_check(USART2_RX_BUF,17))
							USART2_RX_STA|=0x8000;	//接收完成
						else
							USART2_RX_STA=0;//接受错误,重新开始
					}
				}
				else //未接收到0x0d
				{	
					if(Res==0x0d)
						USART2_RX_STA|=0x4000;
					else
					{
						USART2_RX_BUF[USART2_RX_STA&0X3FFF]=Res ;
						USART2_RX_STA++;
						if(USART2_RX_STA>(USART_REC_LEN-1))
							USART2_RX_STA=0;
					}		 
				}
			}   		 
	    } 
	} 
	//注:此处参考了正点原子所提供的源码,根据自己的需要对接收完成的判断进行了修改

	//发送数据
	USART_SendData(UART4, USART2_TX_BUF[t]);//向UART4发送数据
	while(USART_GetFlagStatus(UART4,USART_FLAG_TC)!=SET);//等待发送结束
	//注while语句必须存在,否则可能出现接收字符不完整的情况,且USART_SendData一次只能发送一个数据,对于数组需要用循环的方式进行发送
	//补充:如何用printf进行发送
	//参照正点原子,重定义fputc函数
	int fputc(int ch, FILE *f)
	{      
		if(USART_PRINTF_FLAG==2)
		{
			while((USART2->SR&0X40)==0){};
			USART2->DR = (u8) ch; 
		}
		else
		{
			while((USART3->SR&0X40)==0){};
			USART3->DR = (u8) ch; 		
		}
	     
		return ch;
	}
	//注意:与正点原子不同的是补充了一个发送标志(全局变量),判断是用哪个串口进行发送,用于处理多串口收发

猜你喜欢

转载自blog.csdn.net/weixin_44941350/article/details/123167637