不含stm32 底层的代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
MyStm32Code/DOC/04_uart.md

8.2 KiB

uart

实现重定向, printf

添加 usart.h usart.c

在调用的初始化函数 HAL_UART_Init 内部,会先调用 MSP 初始化回调函数进行 MCU 相关的初始化,函数为: void HAL_UART_MspInit(UART_HandleTypeDef *huart) 以上函数在 stm32f1xx_hal_msp.c 定义,无需重复定义 (CubeMX会自己定义) 操作过程中,只需要开启何种类型中断,关闭何种类型中断

HAL_NVIC_EnableIRQ(USART1_IRQn); //使能 USART1 中断通道 HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级 3,子优先级 3

__HAL_UART_ENABLE_IT(huart,UART_IT_RXNE); //开启接收完成中断 __HAL_UART_DISABLE_IT(huart,UART_IT_RXNE); //关闭接收完成中断

编写中断服务函数 USART1_IRQHandler 以上函数在 stm32f1xx_hal_it.c 定义, 其引用了实际的中断服务函数HAL_UART_IRQHandler(), 该函数非弱函数 用户可以注销 stm32f1xx_hal_it.c 中的定义,自己在自己定义的usart.c 中处理 如使用系统默认的, 需要在不同中端回调函数处理中断 如果不使用系统默认中断服务函数,需要自己定义

中断回调函数 例如: 接收完成回调 HAL_UART_RxCpltCallback() 该函数定义的时候是__weak ,可以直接重写,无需去原文件注销

开启中断 用__HAL_UART_ENABLE_IT()

printf

uart1 为例子 引入 stdio.h 使用printf处也需#include "stdio.h"

重写_write()函数,正对arm-gcc编译器

int _write(int fd, char *ch, int len)
{
  HAL_UART_Transmit(&huart2, (uint8_t*)ch, len, 0xFFFF);
  return len;
}

在mdk下的printf函数重定向:只需要重写fputc

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

调用

extern UART_HandleTypeDef huart2; MX_USART1_UART_Init(115200); MX_USART2_UART_Init(115200); MX_USART3_UART_Init(115200);

测试发送

注意后面要有 \n, 否则可能需要等到换行或缓冲区满后一起输出,不能及时看到数据 uint8_t Data[] = "HELLO WORLD !\n";

//参数1:使用的串口,2:要发送的数据,3:数据大小,4:发送的超时时间 HAL_UART_Transmit(&huart1,Data,sizeof(Data),1000);

测试接收

https://blog.csdn.net/qq_42652838/article/details/107776265?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-107776265-blog-120872001.235%5Ev36%5Epc_relevant_default_base3&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-107776265-blog-120872001.235%5Ev36%5Epc_relevant_default_base3&utm_relevant_index=1 中断类型及开启方法 开启中断 __HAL_UART_ENABLE_IT(huart,UART_IT_RXNE); //开启接收完成中断 __HAL_UART_DISABLE_IT(huart,UART_IT_RXNE); //关闭接收完成中断

在中断函数中直接判断

void USART3_IRQHandler(void){
      uint8_t temp;
    if(huart3.Instance->SR & UART_FLAG_RXNE){
         Rx_uart3_signal=2;
         temp=huart3.Instance->DR;
        Rx_uart3_buf[Rx_uart3_cnt++]=temp; 
    }
    
    else if(huart3.Instance->SR & UART_FLAG_IDLE){
         temp=huart3.Instance->DR;
        Rx_uart3_signal=0;
    } 
}

五种回调函数 void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);//发送完成回调函数 void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);//发送完成过半 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//接收完成回调函数 void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//接收完成过半 void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);//错误处理回调函数

main 函数实例

while(1)
{
  if(USART_RX_STA&0x8000)  // 接收区是否有数据
    { 
      len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
      printf("\r\n 您发送的消息为:\r\n");
      HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000);
      //发送接收到的数据
      while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);  //等待发送结束
      printf("\r\n\r\n");//插入换行
      USART_RX_STA=0;
    }else
    {
      times++;
      if(times%5000==0)
      {
      printf("\r\nALIENTEK 战舰 STM32 开发板 串口实验\r\n");
      printf("正点原子@ALIENTEK\r\n\r\n\r\n");
      }
      if(times%200==0)printf("请输入数据,以回车键结束\r\n"); 
      if(times%30==0)LED0=!LED0;//闪烁 LED,提示系统正在运行.
      delay_ms(10); 
    } 
} 

测试DMA

半满中断

接收发送

轮询模式要采用轮询模式接收, 中断模式要采用中断模式接收,DMA同理 HAL_UART_Transmit();串口发送数据,使用超时管理机制 HAL_UART_Receive();串口接收数据,使用超时管理机制 HAL_UART_Transmit_IT();串口中断模式发送
HAL_UART_Receive_IT();串口中断模式接收 HAL_UART_Transmit_DMA();串口DMA模式发送 HAL_UART_Transmit_DMA();串口DMA模式接收

HAL_UART_GetState(); 串口状态查询函数, 判断UART的接收是否结束,或者发送数据是否忙碌 代替方法 (&huart1)->gState == HAL_UART_STATE_READY

轮询

发送

// main
uint8_t Data[] = "HELLO  WORLD !\n";
uint8_t Cmd[] = "\x23\x00\x00\x80\xA8\x00\x81\x01";
HAL_UART_Transmit(&huart2,(uint8_t*)Data,len,1000); // 需要超时参数

接收 -- 一般用于指定长度

// main
uint8_t Rxbuf[4] ={0};

  while (1)
  {
    if ( HAL_UART_Receive(&huart2, Rxbuf, sizeof(Rxbuf), 1000) == HAL_OK)
    {
      HAL_UART_Transmit(&huart2, Rxbuf,sizeof(Rxbuf),100);
    }
  }

中断模式

main while循环之外 开启中断接收 HAL_UART_Receive_IT(&huart2, Rxbuf, sizeof(Rxbuf));

重写函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 
{
    if(huart->Instance==USART2) 
    {
      HAL_UART_Transmit_IT(&huart2, ACKbuf, sizeof(ACKbuf));
      HAL_UART_Receive_IT(&huart2, Rxbuf, sizeof(Rxbuf));  // 重新使用串口接收中断
    }
}

处理空闲中断

中断里面增加用户自定义中断处理 ****_it.h

void USART1_IRQHandler(void)
{
    
    HAL_UART_IRQHandler(&huart2);
    
    /* USER CODE BEGIN USART1_IRQn 1 */
    USER_UART_IRQHandler(&huart2);       //新添加的函数,用来处理串口空闲中断
    /* USER CODE END USART1_IRQn 1 */
}

自定义中断处理函数

void USER_UART_IRQHandler(UART_HandleTypeDef *huart)
{
    if(USART2 == huart2.Instance)                                   //判断是否是串口1
    {
        if(RESET != __HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE))   //判断是否是空闲中断
        {
            __HAL_UART_CLEAR_IDLEFLAG(&huart2);                     //清楚空闲中断标志(否则会一直不断进入中断)
            USER_UART_IDLECallback(huart);                          //调用中断处理函数
        }
    }
}

最终处理方法

void USAR_UART_IDLECallback(UART_HandleTypeDef *huart)
{
    HAL_UART_DMAStop(&huart2);                                                     //停止本次DMA传输
    
    uint8_t data_length  = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);   //计算接收到的数据长度
	  HAL_UART_Transmit_DMA(&huart2,receive_buff,data_length);                     //DMA发送函数:将接收到的数据打印出去
    data_length = 0;
}

DMA方式

HAL_UART_Transmit_DMA();串口DMA模式发送 HAL_UART_Transmit_DMA();串口DMA模式接收

HAL_UART_Receive_DMA(&huart2, Rxbuf, sizeof(Rxbuf)); // 不需要将接收函数放到 whilel里面,自动在后台干活 UART一旦开启DMA之后,DMA收发中断都是强制开启的,所以DMA收发函数也可以编写回调函数。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 
{
    if(huart->Instance==USART2) 
    {
      HAL_UART_Transmit_DMA(&huart2, ACKbuf, sizeof(ACKbuf));
      // HAL_UART_Receive_IT(&huart2, Rxbuf, sizeof(Rxbuf));  // 重新使用串口接收中断
    }
}

DMA 回调函数

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);//发送完成回调函数 void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);/发送一半回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//接收完成回调函数 void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//接收一半回调函数 void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);//传输出错回调函数