我们再cubemx中配置USART的时候选择开启USART1全局中断,对应到代码当中,只是开启了中断通道,配置了此中断通道的两个优先级。这一步的目的是当某个中断源对应的中断事件发生时,我们可以通过此通道向内核发出请求,即满足条件的时候可以进入中断服务函数。
然而一个中断通道,对应着的是一个外设,而一个外设有好多中断源。所以当我们使用发送和接受中断时,必须开启它们的中断源。而且发送中断搭配了发送完成中断。
我们先贴出使用的中断标志位和中断使能位
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
上面是使能接受中断的函数,我们把代码主要的部分拿出来分析。
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
/* Enable the UART Parity Error Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
主要是把传入的地址赋值给句柄的接受缓冲区的指针。把接受的字节数赋值给句柄的计数和大小。然后是开启了一系列错误中断,最后开启了接受中断,即RXNEIE置1。
当有数据从RX引脚进来后,根据采样把数据接受到RDR移位寄存器,硬件把数据转移到DR寄存器后,硬件把RXNE置1,前面RXNEIE置1,所以产生接受中,进入中断服务函数。我们把几个需要分析的函数列到下面。
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t tmp_flag = 0, tmp_it_source = 0;
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_PE);
tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_PE);
/* UART parity error interrupt occurred ------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_PE;
}
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_FE);
tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR);
/* UART frame error interrupt occurred -------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_FE;
}
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_NE);
/* UART noise error interrupt occurred -------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_NE;
}
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_ORE);
/* UART Over-Run interrupt occurred ----------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_ORE;
}
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE);
tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE);
/* UART in mode Receiver ---------------------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_Receive_IT(huart);
}
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_TXE);
tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TXE);
/* UART in mode Transmitter ------------------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_Transmit_IT(huart);
}
tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_TC);
tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TC);
/* UART in mode Transmitter end --------------------------------------------*/
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_EndTransmit_IT(huart);
}
if(huart->ErrorCode != HAL_UART_ERROR_NONE)
{
/* Clear all the error flag at once */
__HAL_UART_CLEAR_PEFLAG(huart);
/* Set the UART state ready to be able to start again the process */
huart->State = HAL_UART_STATE_READY;
HAL_UART_ErrorCallback(huart);
}
}
产生接受中断后,进入中断服务函数,中断服务函数调用了一个HAL自己编写的中断处理函数,这个函数根据判断发生的中断类型,进行对应的操作。这里我们的中断是接受中断,所以判断后满足接受中断的条件后调用UART_Receive_IT(huart),下面我们把被调函数的关键内容列出。
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
if(--huart->RxXferCount == 0)
{
__HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);
/* Check if a transmit process is ongoing or not */
if(huart->State == HAL_UART_STATE_BUSY_TX_RX)
{
huart->State = HAL_UART_STATE_BUSY_TX;
}
else
{
/* Disable the UART Parity Error Interrupt */
__HAL_UART_DISABLE_IT(huart, UART_IT_PE);
/* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_DISABLE_IT(huart, UART_IT_ERR);
huart->State = HAL_UART_STATE_READY;
}
HAL_UART_RxCpltCallback(huart);
return HAL_OK;
}
此函数首先是从DR寄存器读出一字节内容,并且将这字节内容储存到我们之前传入的地址(每次地址会自增),根据寄存器RXNE我们得知,读取DR寄存器,此时RXNE位变成0。清除了接受中断标志位。接下来判断计数自减是否为0,不等于0重复上述过程,等于0表示需要接受特点的长度字节接受储存完毕,此时我们失能之前使能的中断。然后调用一个接受中断回调函数,这个中断回调函数需要我们再用户文件中重定向,目的是接受完指定操作去执行某些动作。
理解玩接受中断,发送中断就好理解多了,
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
huart->pTxBuffPtr = pData;
huart->TxXferSize = Size;
huart->TxXferCount = Size;
/* Enable the UART Transmit data register empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_TXE);
这和上面接受中断是一样的,赋值一些变量后使能发送中断。我们根据手册寄存器得知,复位后,TXE,TC缺省值为1。前面使能了发送中断,所以进入中断函数,之后调用HAL中断处理,判断类型后执行下面的函数
static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)
我们把这个函数的关键内容列出来分析。
huart->Instance->DR = (uint8_t)(*huart->pTxBuffPtr++ & (uint8_t)0x00FF);
if(--huart->TxXferCount == 0)
{
/* Disable the UART Transmit Complete Interrupt */
__HAL_UART_DISABLE_IT(huart, UART_IT_TXE);
/* Enable the UART Transmit Complete Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_TC);
}
首先对DR寄存器有一个写操作,把数据从之前传入的地址取出写入DR寄存器。(指定的地址每次自增),根据寄存器可得对DR写操作,TXE置0,然后计数自减判断,不等于0,等待硬件把数据从DR寄存器专一到移位寄存器发出,此时TXE也被硬件置1触发了此次中断,重复几次,当计数自减为0后代表指定发送长度的数据发送完毕,此时失能发送中断,使能发送完成中断,由于在发送字节时,当数据转移到移位寄存器后TC置1,加上使能发送完成中断,此时触发中断,。。。根据判断执行static HAL_StatusTypeDef UART_EndTransmit_IT(UART_HandleTypeDef *huart),我们还是只看关键部分。
__HAL_UART_DISABLE_IT(huart, UART_IT_TC);
HAL_UART_TxCpltCallback(huart);
触发发送完成中断进入此函数后,失能发送完成中断,然后调用发送中断回调函数,这个函数也是需要用户重定向。目的也是执行特点的动作。