## 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编译器
```C
int _write(int fd, char *ch, int len)
{
  HAL_UART_Transmit(&huart2, (uint8_t*)ch, len, 0xFFFF);
  return len;
}

```
在mdk下的printf函数重定向：只需要重写fputc
```C
#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); //关闭接收完成中断

在中断函数中直接判断
```C
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 函数实例
```C
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
#### 轮询
发送
```c
// 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); // 需要超时参数
```

接收 -- 一般用于指定长度
```c
// 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));

重写函数

```c
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
```C
void USART1_IRQHandler(void)
{
    
    HAL_UART_IRQHandler(&huart2);
    
    /* USER CODE BEGIN USART1_IRQn 1 */
    USER_UART_IRQHandler(&huart2);       //新添加的函数，用来处理串口空闲中断
    /* USER CODE END USART1_IRQn 1 */
}

```

自定义中断处理函数
```C
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);                          //调用中断处理函数
        }
    }
}

```

最终处理方法
```C
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收发函数也可以编写回调函数。

```c
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);//传输出错回调函数
