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.
253 lines
8.2 KiB
253 lines
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编译器
|
|
```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);//传输出错回调函数
|
|
|