RXNE中断响应过程方法(1)
在USART1串口控制流水灯的实验中,https://www.jianshu.com/p/48817b329231,串口助手发送“mode_1#”命令字后,STM32的USART1 DR寄存器接收到1个字符之后,就会进入到中断服务函数;总结中断执行的过程:
(1)使能中断标志位,CR1寄存器的标志位RXNEIE被置1; 此函数调用之后,会直接配置CR1寄存器的RXNEIE=1;
__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
(2)响应中断的入口函数
在使能了中断之后,DR数据寄存器只要接收到1个字符,例如,发送命令字“mode_1#”,DR寄存器先接收到一个字符“m”,就会产生中断;USART1_IRQHandler()函数是USART1中断服务的入口,其中&huart1是访问串口句柄UART_HandleTypeDef,可以通过huart1.TxXferSize,调用结构体定义的成员,
void USART1_IRQHandler(void)//中断响应的入口
{
USER_UART_IRQHandler(&huart1);
}
串口句柄的定义内容,包含了(发送或接收的)数据缓存、数据指针、串口 DMA 相关的变量、各种标志位等等要在整个项目流程中都要设置的各个成员。还有中断服务程序中的回调函数;这样我们在整个程序中就可以方便调用;
UART_HandleTypeDef huart1;
typedef struct __UART_HandleTypeDef
{
USART_TypeDef *Instance; /*!< UART registers base address */
UART_InitTypeDef Init; /*!< UART communication parameters */
uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */
uint16_t TxXferSize; /*!< UART Tx Transfer size */
__IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
uint16_t RxXferSize; /*!< UART Rx Transfer size */
__IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */
DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */
HAL_LockTypeDef Lock; /*!< Locking object */
__IO HAL_UART_StateTypeDef gState; /*!< UART state information related to global Handle management
and also related to Tx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */
__IO HAL_UART_StateTypeDef RxState; /*!< UART state information related to Rx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */
__IO uint32_t ErrorCode; /*!< UART Error code */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Half Complete Callback */
void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Complete Callback */
void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Half Complete Callback */
void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Complete Callback */
void (* ErrorCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Error Callback */
void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Complete Callback */
void (* MspInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp Init callback */
void (* MspDeInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp DeInit callback */
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
} UART_HandleTypeDef;
(3)用户重写中断服务函数
此次项目中,我们注释掉了HAL库原本的HAL_UART_IRQHandler(&huart1);然后重新定义了USER_UART_IRQHandler(&huart1);在中断入口响应函数 USART1_IRQHandler(void)执行以后,我们就直接调用USER_UART_IRQHandler(&huart1);在main.c中对函数进行定义;
void USER_UART_IRQHandler(UART_HandleTypeDef *huart)
{
if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE) != RESET))//接收一个字节就会产生中断
{
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
uart1RxBuff[uart1RxCounter] = (uint8_t)(huart1.Instance->DR & (uint8_t)0x00ff);
uart1RxCounter++;
__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE);
}
if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET))//接收一组数据/一帧数据就会中断
{
__HAL_UART_DISABLE_IT(&huart1,UART_IT_IDLE);
uart1RxState = 1;
}
}
这种方式与我们之前学习的标准库方法比较相似,在中断服务入口函数里,判断标志位,然后做读/写操作,最后清除标志位;没有使用调用HAL库的回调函数,程序代码思路比较简洁;
(4)判断标志位,将数据寄存器DR中接收的低8位字符读取到自己定义的数组中;同时,完成清除标志位RXNE;代码中uart1RxBuff是我们在C语言代码中定义好的数组,准备将DR寄存器的数值读入到数组中;通过在线调试,我们可以看到变化;
从DR寄存器读取数据之后,RXNE会自动被清零;
if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE) != RESET))
uart1RxBuff[uart1RxCounter] = (uint8_t)(huart1.Instance->DR & (uint8_t)0x0i0ff);
uart1RxCounter++;
当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位被硬件置位。如果USART_CR1寄存器中的RXNEIE为1,则产生中断。对USART_DR的读操作可以将该位清零。
(5)使能IDLE中断,本次项目在RXNE中断响应函数中,开启了IDLE中断,IDLE是空闲线路检测标志位,当一帧数据发送完,下一帧数据还没有发送之前,会有空闲状态;IDLE标志位用于检测一帧数据是否发送完成;在本例中,IDLE使能的代码是:
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
在中断服务函数中又使能了一个新的中断,可能对于初学者有很多疑问;这里注意不属于中断嵌套,IDLE标志位也只有在接收数据的状态下,才可能发生,因为RXNE接收一个字符就会发生中断,而IDLE是接收一帧数据才会发生中断,这里两个状态是可以在一次接收数据的过程中按代码顺序先后发生;所以不存在中断嵌套问题;如果初学者对这里觉得难以理解,也可以将IDLE的使能,在main.c全局变量中进行使能;
(6)产生IDLE中断
当检测到总线空闲时,IDLE位被硬件置位,因为已经开启了USART_CR1中的IDLEIE为’1’,则产生中断;条件判断成立,接收数据状态字置uart1RxState=1;
if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET))//接收一组数据/一帧数据就会中断
{
__HAL_UART_DISABLE_IT(&huart1,UART_IT_IDLE);
uart1RxState = 1;
}
IDLE的状态位受到RXNE状态位的影响,先有RXNE状态位置1,检测到IDLE状态情况下,才会进入IDLE的中断响应;表示一帧数据已经接收完成;
(7)禁用IDLE中断;__HAL_UART_DISABLE_IT(&huart1,UART_IT_IDLE);
总结:通过此篇分析,是帮助同学们理解串口接收中断函数的执行流程;使能函数__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE) 与用户自定义的中断服务函数 USER_UART_IRQHandler(UART_HandleTypeDef *huart),配合使用。完成数据接收;此篇没有用到接收中断的回调函数。我们在下篇方法二中进行介绍。