STM32串口通信(串口中断、FIFO机制)之安富莱代码学习笔记

创建串口设备结构体

typedef struct
{
  USART_TypeDef *uart;/*STM32内部串口设备指针*/
	uint8_t *pTxBuf;/*发送缓冲区*/
	uint8_t *pRxBuf;/*接收缓冲区*/
	uint16_t usTxBufsize;/*发送缓冲区大小*/
	uint16_t usRxBufsize;/*接收缓冲区大小*/
	uint16_t usTxWrite;/*发送缓冲区写指针(FIFO机制)*/
	uint16_t usTxRead;/*发送缓冲区读指针*/
	uint16_t usTxCount;/*等待发送的数据个数*/
	
	uint16_t usRxWrite;/*接收缓冲区写指针*/
	uint16_t usRxRead;/*接收缓冲区读指针*/
	uint16_t usRxCount;/*还未读取的新数据的个数*/
	
	void (*SendBefor)(void);/*开始发送之前的回调函数*/
	void (*SendOver)(void);/*发送完毕的回调函数指针*/
	void (*ReciveNew)(void);/*串口收到数据的回调函数指针*/
}UART_T;

上述的接收缓冲区读写指针和发送缓冲区读写指针虽然是uint16_t的数据类型,但是在这里称之为指针,是因为其在队列中反应了数据读写时位置的变化。

static void UartVarInit(void)
{
	
	g_tUart1.uart = USART1;/*串口设备*/
	g_tUart1.pTxBuf = g_TxBuf1;/*发送缓冲区指针*/
	g_tUart1.pRxBuf = g_RxBuf1;/*接收缓冲区指针*/
	g_tUart1.usTxBufsize = UART1_TX_BUF_SIZE;/*发送缓冲区大小*/
    g_tUart1.usRxBufsize = UART1_RX_BUF_SIZE;/*发送缓冲区大小*/
    g_tUart1.usTxWrite = 0;/*发送缓冲区写指针初始化为0*/
	g_tUart1.usTxRead = 0;/*发送缓冲区读指针初始化为0*/
	g_tUart1.usRxWrite = 0;/*接收缓冲区写指针初始化为0*/
	g_tUart1.usRxRead = 0;/*接收缓冲区读指针初始化为0*/
	g_tUart1.usRxCount = 0;/*接收到的新数据个数*/
	g_tUart1.usTxCount = 0;/*待发送的数据个数*/
	g_tUart1.SendBefor = 0;/*发送数据前的回调函数*/
	g_tUart1.SendOver = 0;/*发送完成的回调函数*/
	g_tUart1.ReciveNew = 0;/*接收到新数据的回调函数*/
}

上述的初始化过程将读写指针初始化为0,表明最开始缓冲区中没有数据,也就是处于队列的最底部。
下面是初始化串口的硬件部分

static void InitHardUart(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	USART_ClockInitTypeDef USART_ClockInitStructure;
	
	/*第一步:配置GPIO*/
	/*使能串口1,PA,AFIO总线*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_USART1, ENABLE);
	
	/*A9 USART1-Tx*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	/*A10 USART1-Rx*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	/*第2步:配置串口硬件参数*/
	USART_InitStructure.USART_BaudRate = UART1_BAUD;
	USART_InitStructure.USART_WordLength = UART1_WORD_LENGTH;
	USART_InitStructure.USART_StopBits = UART1_STOP_BIT;
	USART_InitStructure.USART_Parity = UART1_PARITY;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	
	USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
	USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
	USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;
	USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
	
	USART_ClockInit(USART1, &USART_ClockInitStructure);
	USART_Init(USART1, &USART_InitStructure);
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);  /*使能接收中断*/
	/*不能在此处打开发送中断*/
	
	USART_Cmd(USART1, ENABLE);
	
	/*小缺陷:串口配置好,如果直接send,则第一个字节发送不出去*/
	USART_ClearFlag(USART1, USART_FLAG_TC);/*清发送完成标志,可以解决第一个字节无法正确发送的问题*/

}

上述过程是程序化步骤,没有思维难点,接下来配置串口中断

static void ConfigUartNVIC(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
	
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
	
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

在发送数据的时候,采用UartSend();这个函数进行发送
具体的代码如下所示:

static void UartSend(UART_T *_pUart, uint8_t *_ucaBuf, uint16_t _usLen)
{
  uint16_t i;

  for(i = 0;i < _usLen;i++)
  {
    while(1)
    {
      __IO uint16_t usCount;
      DISABLE_INT();
      usCount = _pUart->usTxCount;
      ENABLE_INT();
      if(usCount < _pUart->usTxBufSize)
      {
         break;
      }
    }
   _pUart->pTxBuf[_pUart->usTxWrite] = _ucaBuf[i]; 
   DISABLE_INT();
   if (++_pUart->usTxWrite >= _pUart->usTxBufSize)
	{
	  _pUart->usTxWrite = 0;
	}
	_pUart->usTxCount++;/*等待发送的数据个数*/
   ENABLE_INT();
  }
  USART_ITConfig(_pUart->uart, USART_IT_TXE, ENABLE);/*使能发送中断*/
}
  if(usCount < _pUart->usTxBufSize)
  {
     break;
  }

通过上述的这句话保证每次写入缓冲区时都能保证等待发送的个数都小于缓冲区本身的大小,当等待发送的数据个数超过缓冲区本身的大小时,程序将陷入死循环。
_pUart->pTxBuf[_pUart->usTxWrite] = _ucaBuf[i]; 通过此条语句将要发送的数据送入缓冲区。
if (++_pUart->usTxWrite >= _pUart->usTxBufSize)
{
_pUart->usTxWrite = 0;
}
上述语句即表示如果当前的写指针已经移动到FIFO的顶端时,对指针进行复位。

同时在将要发送的数据存储到缓冲区之后,就可以通过触发中断发送数据
具体的发送中断的函数如下所示:

if (USART_GetITStatus(_pUart->uart, USART_IT_TXE) != RESET)
{
  if(_pUart->usTxCount == 0)
  {
    USART_ITConfig(_pUart->uart, USART_IT_TXE, DISABLE);/*发送缓冲区的数据已经取完        时,禁止发送缓冲区空中断*/
    USART_ITConfig(_pUart->uart, USART_IT_TC, ENABLE);/*使能数据发送完毕中断*/
  }
  else
  {
   /*从发送FIFO取一个字节写入发送数据寄存器*/
   USART_SendData(_pUart->uart, _pUart->pTxBuf[_pUart->usTxRead]);/*此条语句通过调用库函数实现*/
   if (++_pUart->usTxRead >= _pUart->usTxBufSize)
   {
	  _pUart->usTxRead = 0;
	}
	  _pUart->usTxCount--;/*每发送一个字节之后待发送数据个数减一*/
    }
  }
  else if (USART_GetITStatus(_pUart->uart, USART_IT_TC) != RESET)
  {
    if (_pUart->usTxCount == 0)
    {
      /*如果发送的数据全部发送完毕时,禁止数据发送中断*/
      USART_ITConfig(_pUart->uart, USART_IT_TC, DISABLE)}
    else
    {
       /*正常情况下,不会进入这个分支*/
       USART_SendData(_pUart->uart, _pUart->pTxBuf[_pUart->usTxRead]);
       /*进入此分支后,再次发送缓冲区的值*/
       if (++_pUart->usTxRead >= _pUart->usTxBufSize)
	   {
		  _pUart->usTxRead = 0;
	  }
	  _pUart->usTxCount--;
     }
  }

上述代码完成了一个完整的数据发送的过程
下面阐述关于接收数据的过程,接收数据通过触发中断接收数据。
中断处理函数如下:

if (USART_GetITStatus(_pUart->uart, USART_IT_RXNE) != RESET)
{
  uint8_t ch;

  ch = USART_ReceiveData(_pUart->uart);/*通过库函数从接收移位寄存器中读取收到的数据*/
  _pUart->pRxBuf[_pUart->usRxWrite] = ch;/*将数据存入接收缓冲区,每进入中断一次存取一次*/		
  if (++_pUart->usRxWrite >= _pUart->usRxBufSize)
  {
	_pUart->usRxWrite = 0;
  }
  if (_pUart->usRxCount < _pUart->usRxBufSize)
  {
	_pUart->usRxCount++;
  }
}

在接收中断里面读取到接收移位寄存器的值并将值存储到FIFO中,然后通过UartGetChar()函数读取缓冲区的值。

static uint8_t UartGetChar(UART_T *_pUart, uint8_t *_pByte)
{
  uint16_t usCount;

 DISABLE_INT();
 usCount = _pUart->usRxCount;
 ENABLE_INT();

 if(usCount == 0)/*沒有数据则返回*/
 {
    return 0;
 }
 else
 {
   *_pByte = _pUart->pRxBuf[_pUart->usRxRead];	
  }  
}

通过上述函数*_pByte便是从缓冲区读出的值
最后,再指出一点,再发送数据的时候,将要发送的数据送入缓冲区时,用的是发送写指针,在中断中将发送缓冲区中的数据填入发送数据寄存器中用的是发送读指针。
在中断接收时用的是接收写指针,读取程序中用的是接收读指针。

发布了19 篇原创文章 · 获赞 6 · 访问量 1737

猜你喜欢

转载自blog.csdn.net/weixin_42616791/article/details/99655626