串口中断方式的特点:
串口中断方式发送函数:HAL_UART_Transmit_IT
函数原型 | HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_Handle TypeDef *huart, uint 8_t *pData, uint 16_t Size) |
---|---|
功能描述 | 在中断方式下发送一定数量的数据 |
入口参数1 | huart:串口句柄的地址 |
入口参数 | pData:待发送数据的首地址 |
入口参数3 | Size:待发送数据的个数 |
入口参数4 | Timeout:超时等待时间, 以ms为单位, HAL MAX DELAY表示无限等待 |
返回值 | HAL状态值:HAL_OK表示发送成功;HAL_ERROR表示参数错误;HAL_BUSY表示串口被占用; |
注意事项 | 1. 函数将使能串口发送中断2. 函数将置位TXEIE和TCIE,使能发送数据寄存器空中断和发送完成中断。完成指定数量的数据发送后,将会关闭发送中断,即清零TXEIE和TCIE。因此用户采用中断方式连续发送数据时,需要重复调用该函数,以便重新开启发送中断3. 当指定数量的数据发送完成后,将调用发送中断回调函数HAL_UART_TxCpltCallback进行后续处理4. 该函数由用户调用 |
串口中断方式接收函数:HAL_UART_Receive_IT
函数原型 | HAL_StatusTypeDef HAL_UART_Receive_IT(UART_Handle TypeDef *huart, uint 8_t *pData, uint 16_t Size, uint 32_t Timeout) |
---|---|
功能描述 | 在中断方式下接收一定数量的数据 |
入口参数1 | huart:串口句柄的地址 |
入口参数2 | pData:存放数据的首地址 |
入口参数3 | Size:待接收数据的个数 |
入口参数4 | Timeout:超时等待时间, 以ms为单位, HAL MAX DELAY表示无限等待 |
返回值 | HAL状态值:HAL_OK表示发送成功;HAL_ERROR表示参数错误;HAL_BUSY表示串口被占用; |
注意事项 | 1. 函数将使能串口接收中断2. 函数将置位RXNEIE,使能接收数据寄存器非空中断RXNE。完成指定数量的数据接收后,将会关闭接收中断,即清零RXNEIE。因此用户采用中断方式连续接收数据时,要重复调用该函数,以重新开启接收中断3. 当指定数量的数据接收完成后,将调用接收中断回调函数HAL_UART_RxCpltCallback进行后续处理4. 该函数由用户调用 |
串口中断通用处理函数:HAL_UART_IRQHandler
函数原型 | void HAL_UART_IRQHandler(UART_HandleTypeDef *huart) |
---|---|
功能描述 | 作为所有串口中断发生后的通用处理函数 |
入口参数 | htim:定时器句柄的地址 |
返回值 | 无 |
注意事项 | 1. 函数内部先判断中断类型,并清除对应的中断标志,最后调用回调函数完成对应的中断处理2. 该函数由CubeMX自动生成 |
串口发送中断回调函数:HAL_UART_TxCpltCallback
函数原型 | void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) |
---|---|
功能描述 | 回调函数,用于处理所有串口的发送中断,用户在该函数内编写实际的任务处理程序 |
入口参数 | htim:定时器句柄的地址 |
返回值 | 无 |
注意事项 | 1. 函数由串口中断通用处理函数HAL_UART_IRQHandler调用,完成所有注意事项2.串口的发送中断任务处理函数内部需要根据串口句柄的实例来判断是哪一个串口产生的发送中断3. 函数由用户根据具体的处理任务编写 |
串口接收中断回调函数:HAL_UART_RxCpltCallback
函数原型 | void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) |
---|---|
功能描述 | 回调函数,用于处理所有串口的接收中断,用户在该函数内编写实际的任务处理程序 |
入口参数 | htim:定时器句柄的地址 |
返回值 | 无 |
注意事项 | 1. 函数由串口中断通用处理函数HAL_UART_IRQHandler调用,完成所有注意事项2.串口的发送中断任务处理函数内部需要根据串口句柄的实例来判断是哪一个串口产生的接收中断3. 函数由用户根据具体的处理任务编写 |
串口中断使能函数:__HAL_UART_ENABLE_IT
函数原型 | __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__) |
---|---|
功能描述 | 使能对应的串口中断类型 |
入口参数 | __INTERRUPT __ :串口中断类型,该参数几个常用的取值如下UART_IT_TXE :发送数据寄存器空中断UART_IT_TC :发送完成中断UART_IT_RXNE:接收数据寄存器非空中断UART_IT_IDLE :线路空闲中断 |
返回值 | 无 |
注意事项 | 1. 该函数是宏函数,进行宏替换,不发生函数调用2. 函数需要由用户调用,用于使能对应的串口中断类型 |
串口中断标志查询函数:__HAL_UART_GET_FLAG
函数原型 | __HAL_UART_GET_FLAG (__HANDLE__, __INTERRUPT__) |
---|---|
功能描述 | 查询对应的串口中断类型 |
入口参数 | __INTERRUPT __ :串口中断类型,该参数几个常用的取值如下UART_IT_TXE :发送数据寄存器空中断UART_IT_TC :发送完成中断UART_IT_RXNE:接收数据寄存器非空中断UART_IT_IDLE :线路空闲中断 |
返回值 | 中断标志的状态值:SET表示中断标志置位;RESET表示中断标志没有置位 |
注意事项 | 1. 该函数是宏函数,进行宏替换,不发生函数调用2. 函数需要由用户调用,用于查询对应的串口中断类型 |
空闲中断标志清除函数:__HAL_UART_CLEAR_IDLEFLAG
函数原型 | __HAL_UART_CLEAR_IDLEFLAG |
---|---|
功能描述 | 清除串口的空闲中断标志 |
入口参数 | HANDLE :串口句柄的地址 |
返回值 | 无 |
注意事项 | 1. 该函数是宏函数,进行宏替换,不发生函数调用2. 函数需要由用户调用,用于清除对应的串口空闲中断标志 |
HAL_UART_Receive_IT
:开启中断,在中断方式下接收一定数量的数据。USART2_IRQHandler
:串口2的中断服务程序,调用串口中断通用处理函数HAL_UART_IRQHandler
。HAL_UART_IRQHandler
:在函数 HAL_UART_IRQHandler
内部通过判断中断类型是否为接收完成中断,确定是否调用UART_Receive_IT
。函数
UART_Receive_IT
的作用是把每次中断接收到的字符保存在串口句柄的缓存指针pRxBuffPtr
中,同时每次接收一个字符,其计数器RxXferCount
减 1,直到接收完成RxXferSize
个字符之后RxXferCount
设置为0,同时调用接收完成回调函数HAL_UART_RxCpltCallback
进行处理。
HAL_UART_RxCpltCallback
:函数由串口中断通用处理函数UART_Receive_IT
调用,完成所有串口的接收中断任务处理,函数内部需要根据串口句柄的实例来判断是哪一个串口产生的接收中断,函数由用户根据具体的处理任务编写。利用串口调试助手,从PC上发送10个字符到开发板,开发板收到后原样发回到PC。
前后台编程模式: 前台程序为中断服务程序,一旦数据接收完成,则设置一个标志位为1;后台程序为while(1)的死循环,在循环中不断检测标志位是否为1。如果为1,表明数据接收完成,并存放在接收缓冲区中。然后进行后续处理:先清除标志位,再把接收的数据原样发回。
串口外设配置
编写代码
printf和scanf重定向:略
// -----------------------------------------------------------------------//
/* USER CODE BEGIN PD */
#define LENGTH 10 // 接收数据缓冲区大小
/* USER CODE END PD */
// -----------------------------------------------------------------------//
/* USER CODE BEGIN PV */
uint8_t RxBuffer[LENGTH]; // 接收缓冲区
uint8_t RxFlag = 0; // 接收完成标志,0未完成,1完成
/* USER CODE END PV */
// -----------------------------------------------------------------------//
/* USER CODE BEGIN 2 */
// 打印提示信息
printf("UART Communication Using ITn");
printf("Please enter 10 characters:n");
HAL_UART_Receive_IT(&huart1, (uint8_t *)RxBuffer, LENGTH); // 使能接收中断
/* USER CODE END 2 */
// -----------------------------------------------------------------------//
while (1)
{
/* USER CODE BEGIN 3 */
if (RxFlag == 1) // 判断接收是否完成
{
RxFlag = 0; // 接收完成,清除标志位
printf("Receive Successfully!n"); // 打印提示信息
HAL_UART_Transmit_IT(&huart1, (uint8_t*)RxBuffer, LENGTH); // 将接收的字符原样发回
}
}
/* USER CODE END 3 */
// -----------------------------------------------------------------------//
/* USER CODE BEGIN 4 */
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart- >Instance == USART1) // 判断发生接收中断的串口
{
RxFlag = 1; // 置位接收完成b标志
HAL_UART_Receive_IT(&huart1, (uint8_t*)RxBuffer, LENGTH); // 使能接收中断
}
}
/* USER CODE END 4 */
// -----------------------------------------------------------------------//
实验现象
实现简单的帧格式通信:PC按照自定义的帧格式发送指令开启或关闭开发板上的LED1。
帧格式的概念:
Modbus消息帧格式:
起始符 | 设备地址 | 功能代码 | 数据 | 校验 | 结束符 |
---|---|---|---|---|---|
1个字符 | 2个字符 | 1个字符 | n个字符 | 2个字符 | 1个字符 |
自定义的帧格式设定:
帧头 | 设备码 | 功能码 | 帧尾 |
---|---|---|---|
0xaa | 1个字符(8bit) | 1个字符(8bit) | 0x55 |
串口配置同任务实践1
配置PA1为GPIO_Output模式
编写代码
// -----------------------------------------------------------------------//
/* USER CODE BEGIN PV */
uint8_t RxBuffer[4]; // 接收缓冲区
uint8_t RxFlag = 0; // 接收完成标志,0位完成,1完成
uint8_t ErrFlag = 0; // 指令错误标志,0正确,1错误
/* USER CODE END PV */
// -----------------------------------------------------------------------//
/* USER CODE BEGIN 2 */
// 打印提示信息
printf("***** Communication Frame *****n");
printf("Please enter instruction:n");
printf("Head- >0xaa, Device- >0x01, Operation- >0x00/0x10, Tail- >0x55n");
HAL_UART_Receive_IT(&huart1, (uint8_t*)RxBuffer, 4); // 使能接收中断
/* USER CODE END 2 */
// -----------------------------------------------------------------------//
while(1)
{
/* USER CODE BEGIN 3 */
// Determine if reception is complete
if (RxFlag == 1) // 判断接收是否完成
{
RxFlag = 0; // 完成,清除标志位
// 帧格式解析
printf("head = RxBuffer[0] = %xn", RxBuffer[0]);
printf("tail = RxBuffer[3] = %xn", RxBuffer[3]);
printf("device = RxBuffer[1] = %xn", RxBuffer[1]);
printf("function = RxBuffer[2] = %xn", RxBuffer[2]);
if (RxBuffer[0] == 0xaa && RxBuffer[3] == 0x55) // 判断帧头帧尾
{
if (RxBuffer[1] == 0x01) // 判断设备码
{
if (RxBuffer[2] == 0x00) // 判断功能码
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
printf("LED1 is close!n");
}
else if (RxBuffer[2] == 0x01) // 判断功能码
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
printf("LED1 is open!n");
}
else // 功能码错误
{
ErrFlag = 3;
}
}
else // 设备码错误
{
ErrFlag = 2;
}
}
else // 帧头帧尾错误
{
ErrFlag = 1;
}
// 发送错误提示信息
switch (ErrFlag)
{
case 1:
printf("Head and tail error! Please send again!n");
break;
case 2:
printf("Device code error! Please send again!n");
break;
case 3:
printf("Function code error! Please send again!n");
break;
default:
break;
}
// 清除接收缓冲区和错误标志,准备下一次接收
ErrFlag = 0;
RxBuffer[0] = 0;
RxBuffer[1] = 0;
RxBuffer[2] = 0;
RxBuffer[3] = 0;
}
}
/* USER CODE END 3 */
// -----------------------------------------------------------------------//
/* USER CODE BEGIN 4 */
int fputc (int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart- >Instance == USART1) // 判断发生接收中断的串口
{
RxFlag = 1; // 置位接收完成标志
HAL_UART_Receive_IT(&huart1 , (uint8_t*)RxBuffer, 4); // 使能串口中断
}
}
/* USER CODE END 4 */
实验现象
全部0条评论
快来发表一下你的评论吧 !