STM32串口相关(一)--发送接收基础性问题

文章介绍了STM32的串口通信基础知识,包括中断接收模式下如何读取数据,以及中断发送的实现。在中断接收中,强调了数据帧尾0X0D0X0A的重要性,并展示了正点原子的中断接收程序。中断发送部分解释了TXE和TC标志位的意义,指出何时开启和关闭中断。
摘要由CSDN通过智能技术生成

STM32串口相关(一)–发送接收基础性问题

【注】STM32串口相关,总共分为三个部分:①发送接收基础性问题。②最优的串口使用方式及说明。③串口发送接收数据的一般算法。

1、普通的中断接收模式

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_GetITStatus(USART1, USART_IT_RXNE)

打开接收中断后,当接收到数据式,中断标志位就会置为1,从而进入中断。

R_Dat =USART_ReceiveData(USART2);	//读取接收到的数据//(USART1->DR)读取DR

uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  
  /* Receive Data */
  return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);
}

进入中断后,我们通过USART_ReceiveData函数读取数据,而从USART_ReceiveData函数内部实现方式可以知道,这个数据存储在DR寄存器中的。

void USART2_IRQHandler(void)  //这个函数是接收不定长数据自己写的程序,非寄存器标志
{  
    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
		R_Dat =USART_ReceiveData(USART2);	//读取接收到的数据//(USART1->DR)读取DR
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			{
			if(USART_RX_STA&0x4000)//接收到了0x0d
				{
				if(R_Dat!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //还没收到0X0D
				{	
				if(R_Dat==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=R_Dat ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
					}		 
				}
			}   		 
     }
}

这里我们贴了一块正点原子的中断接收程序,这个程序如果只是“单字节”数据传输,那么可以沈略帧尾0X0D 0X0A,直接进入中断读取DR寄存器即可,如果接收数据为不定长的数组,那么我们使用正点原子这个程序可以实现,不过帧尾一定要是0X0D 0X0A。同时不定长的数组,最后接收存储在USART_RX_BUF[]中,当然这个数组也是有限数组,如下,长度可以根据实际情况改变。

#define USART_REC_LEN       200//定义最大接收字节数 200
#define EN_USART1_RX        1   //使能(1)/禁止(0)串口1接收
    
extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART_RX_STA;         		//接收状态标记	

当不定长时,我们读取数据的代码如下:

if(USART_RX_STA&0x8000)
{					   
	len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
	printf("\r\n您发送的消息为:\r\n");
	for(t=0;t<len;t++)
	{
			buf[t]=USART_RX_BUF[t];
		}
}

我们在程序中可以看到,进入中断之后,中断标志位在程序的最后并没有清除,这是为什么呢?
这里我们说明一点知识点:
串口接收发送的一般过程为:
①接收:RX -> 接收移位寄存器(SR) -> 接收数据寄存器(DR)-> CPU
②发送:CUP -> 发送数据寄存器(DR)-> 发送移位寄存器(SR)-> TX

其中,DR是我们需要读取或者写入的寄存器区。写入后移位寄存器会硬件自动发送,同样,接收到数据后,移位寄存器也会硬件自动将数据存到DR寄存器中供我们读取。

有些中断标志位需要我们软件清除,但是这个串口的接收标志位优点特殊:
TXE:写寄存器DR时会自动清零
RXNE:读寄存器DR时也会自动清零,也可以手动清零
TC:读/写DR寄存器,会自动清零,也可以手动清零

手动清零的代码:

 USART_ClearITPendingBit(USART2,USART_IT_RXNE);  //增加--清除RXNE标志位   
 USART_GetFlagStatus(USART2,USART_FLAG_TC);  

这里再说明一下,TC和TXE标志位,当DR中的数据转移到SR中时TXE置1,如果有数据写入DR时就能将TXE置0;如果SR中的数据全部通过TXDpin移出并且没有数据进入DR,则TC置1。并且需要注意TXE只能通过写DR来置0,不能直接将其清零,而TC可以直接将其写1清零。也就是:

TXE = 1:表示DR寄存器已经清空,这个时候可以将下一帧数据写入
TXE = 0:表示DR寄存器仍然有数据,不可以写数据,若忽略这个标志位,很可能,上一个数据还未发送,新值已经送进来,使得上一个数据被覆盖丢失,使得通信错误。
TC = 1:表示SR寄存器中的值已经完全移出,此时数据传输完成。
TC = 0:表示上一个数据还未传输完成。

那么该如何看待这两个标志位的应用场景呢?其实仔细看我上面关于这两个标志位的说明就可以找到答案。显然,对于USART_FLAG_TXE来说,只是说明数据寄存器中的数据已经被发送移位寄存器取走了(但发送移位寄存器中可能还没有启动发送过程),通过中断就可以提醒CPU可以往数据寄存器中填充数据了,发送移位寄存器中的数据往外发送的过程其实还是比较耗时的,相对于C语言代码执行时间来说,这个过程的耗时完全算得上是一个宏观的数据,所以每次发送数据寄存器中的数据被发送移位寄存器取走后,都应该产生中断来提醒CPU对该寄存器更新数据;而对于USART_FLAG_TC来说,没必要每次当发送移位寄存器中的数据发送完成后都发生中断,而应该是整个串口数据帧全部发送完毕,包括最后一个字节也发送出去之后才应该开中断,这代表的就是一个数据帧发送完成事件了。
我们在实际的使用过程中,可以直接利用TC = 1来判断是否给发送数据寄存器DR赋新值。

2、中断发送

我们一般在串口发送的时候,如果是调试的话,直接用printf就行,如果是设备间通信,发送单个字符直接以查询的方式发送。

	USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
/*******************************************************/
void USART1_IRQHandler(void)
{
  unsigned int i;
  /*接收中断*/
  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
  {   
    /* Read one byte from the receive data register */
    RxBuffer1[RxCounter1++] = USART_ReceiveData(USART1);
    if(RxCounter1 == NbrOfDataToRead1)  //接收数据达到需要长度,则将数据复制到发送数组中,并置标志
    {                     
      /* Disable the USART1 Receive interrupt */
      //USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
        for(i=0; i< RxCounter1; i++) TxBuffer1[i]  = RxBuffer1[i];
        rec_f=1;
        RxCounter1=0;
        TxCounter1=0;
        USART_ITConfig(USART1, USART_IT_TXE, ENABLE);  //打开发送中断,这句是关键
    }
  }
  /*发送中断*/
  if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
  {  
  
    USART_SendData(USART1, TxBuffer1[TxCounter1++]);
 
    if(TxCounter1 == NbrOfDataToTransfer1)//发送数据完成
    {   
      USART_ITConfig(USART1, USART_IT_TXE, DISABLE); //关闭发送中断
    }   
  }   
}

移位状态寄存器SR,复位值为0X00C0H,也就是第七位TXE、第六位TC为1,TXE = 1,表示发送寄存器为空,TC = 1,表示发送已经完成。而在配置时,只要使能IT_txe,此时TXE = 1就会进入中断,发送相关的数据。所以我们一般在函数初始化时不要启动TXE中断,

USART_ITConfig(USART1, USART_IT_TXE, ENABLE);  

而是在主程序,什么时候需要发送数据,再去打开,发送完成之后,再关闭中断。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值