串口的接收与发送

1、等待查询等待方式

void Usart_Send_Char(unsigned char *c,uint32_t cnt)//把发送数据的指针和个数传入,然后直到发送完才退出
{
    while(cnt--)
    {
    USART_SendData(USART1, *c++);
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET );//死等发送完成,TXE置1表示发送寄存器空,然后发送下一个
  }
}

STM32 HAL库对应的查询发送函数:

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)//在给定的时间内发送一帧数据,直到发送完成或超出时间,才会退出

对应的查询接收函数:

HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)//接收指定长度个数据,如果没有接收够就不退出,除非超时

以上2个函数都需要输入4个参数:
1、串口号
2、要发送数据的位置 或 接收的数据存放的位置
3、数据长度,多少个字节
4、规定的时间,单位一般是ms, (uwTick的单位)

2、中断方式发送

中断方式就是把数据 地址放到指定的指针变量,然后打开中断。发送完一个字节后,会发生中断,中断会自动从指定的地址取下一个数来发送,直到发送完成指定长度,关中断。

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  /* Check that a Tx process is not already ongoing */
  if(huart->gState == HAL_UART_STATE_READY)
  {
    if((pData == NULL) || (Size == 0U)) 
    {
      return HAL_ERROR;
    }
    /* Process Locked */
    __HAL_LOCK(huart);
    huart->pTxBuffPtr = pData;//把要发送的数据指针放到指定位置
    huart->TxXferSize = Size;//数据长度
    huart->TxXferCount = Size;
    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->gState = HAL_UART_STATE_BUSY_TX;

    /* Process Unlocked */
    __HAL_UNLOCK(huart);
    /* Enable the UART Transmit data register empty Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_TXE);//打开中断

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

中断接收:

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

另外还有一个,处理串口中断请求的函数,就是中断后调用,该函数会判断是接收中断,还是发送中断…

HAL_UART_IRQHandler(UART_HandleTypeDef *huart)

3、环形队列

1、如下建立三个数组,第1个要定义为volatile可变的,即每个读取的时候都要从实际地址中读取,防止keil优化。再定义3个全局变量

uint8_t  volatile wifi_queue_buf[20];
uint8_t  wifi_uart_rx_buf[30];         
uint8_t  wifi_uart_tx_buf[30]; 

volatile uint8_t *queue_in;
volatile uint8_t *queue_out;
volatile uint8_t queue_total_data;  

2、在串口中断中,判断如果是接收中断,调用uart_receive_input(ch);将接收到的字节入队wifi_queue_buf[20],并将全局变量queue_total_data++,记录队列中的字节数
当然main.c初始化的时候要把queue_in,queue_out都指向队列的头部

void USART1_IRQHandler(void)
{
  uint8_t ch;
  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
  {
    ch = USART_ReceiveData(USART1);
    uart_receive_input(ch);
  }
}
/***********************************************/
  void uart_receive_input(unsigned char value)
    {
      if(queue_total_data < sizeof(wifi_queue_buf))
      {
        if(queue_in >= (unsigned char *)(wifi_queue_buf + sizeof(wifi_queue_buf)))//溢出,返回到开头
        {
          queue_in = (unsigned char *)(wifi_queue_buf);
        }
        *queue_in ++ = value;
        queue_total_data ++;
      }
      else
      {
        //队列已满
      }
    }

以上完成了把数据接收,并放入到环形队列wifi_queue_buf[20]中,而且queue_total_data记录了队列里有多少数据

3、出队
在main.c的while(1)循环中,调用void wifi_uart_service(void)处理接收到的数据。

定义一个static uint8_t rx_in用于记录出队数量,必须定义为static,相当于全局变量,但作用范围只是本函数

void wifi_uart_service(void)
{
  static uint8_t rx_in = 0;
  uint8_t offset = 0;
  uint8_t rx_value_len = 0;   
  while((rx_in < sizeof(wifi_uart_rx_buf)) && get_queue_total_data() > 0)//
  {    //只要队列wifi_queue_buf中有数据,并且wifi_uart_rx_buf队列中有空位置,就把数据复制过来
    wifi_uart_rx_buf[rx_in ++] = Queue_Read_Byte();
  }

高用下面的函数复制队列数据,然后总数减1

unsigned char Queue_Read_Byte(void)
{
  unsigned char value;
  if(queue_total_data > 0)
  {
    //有数据
    if(queue_out >= (unsigned char *)(wifi_queue_buf + sizeof(wifi_queue_buf)))
    {
      //数据已经到末尾
      queue_out = (unsigned char *)(wifi_queue_buf);
    }
    value = *queue_out ++; //  
    queue_total_data --;
  }
  return value;
}

数据复制到wifi_uart_rx_buf[]后,我们就可以判断是否够一条指令,如果够,就处理。然后rx_in需要减去一条指令的长度,剩下如果还有数据,下次处理。

以上完成了接收处理。

4、发送,
把指令先存入到wifi_uart_tx_buf数组,然后调用发送函数发送。

以上 环形队列 参考了涂鸦科技的例程:http://www.tuya.com

4、DMA 发送方式

1、接收完一半数据的时候会中断,接收完再中断一次。每个DMA通道都有3个事件标志(DMA半传输,DMA传输完成,DMA传输出错);这三个事件标志或成为单独 中断源。DMA可以在传输过半,传输完成,传输错误时产生中断
在这里插入图片描述
在这里插入图片描述
DMA发送,把要发送的数据指针,字节数传入,即可自动发送。

HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

这个函数实际是调用下面这个函数来启动发送的

HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t` DstAddress, uint32_t DataLength)

DMA接收,也是要指定接收的长度:

HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

这个函数同样是调用

HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);

只不过源,目标 地址反过来
在这里插入图片描述
DMA还有暂停 、恢复函数 和 中止函数

5、freeRTOS+DMA+先进先出队列+动态内在申请
这个搞了几天,另个写。

猜你喜欢

转载自blog.csdn.net/liangbin414/article/details/88422110