一、UART_Receive_IT
对于CubeMX生成的代码,USART1_IRQHandler(void)函数为了提高中断效率采用了回调机制。(业务代码可以等中断关闭了再去处理,这样中断处理不会占用太多时间影响程序的执行效率)
HAL库将函数都已封装完整,回调函数完好地提供一个API接口,供用户使用
USART1_IRQHandler(void)函数中只调用了HAL_UART_IRQHandler(&huart1)(可以在STM32f1xx_it.c中找到),参数为uart1的句柄huart1(本质就是个结构体指针),可以通过huart1访问到uart1的各种寄存器和数据类型。
static HAL_StatusTypeDe(UART_HandleTypeDef *huart)
{
uint16_t *tmp; //定义了一个指针tmp 指向一个地址(由于还没有初始化还不知道指向哪个地址)地址里面装着16位的整型数据
/* Check that a Rx process is ongoing */
if (huart->RxState == HAL_UART_STATE_BUSY_RX)
{
if (huart->Init.WordLength == UART_WORDLENGTH_9B)//判断CR寄存器的第12位W是否为1 ,为1则代表设置为一个起始位, 9个数据位, n个停止位
{
tmp = (uint16_t *) huart->pRxBuffPtr; //将pRxBuffPtr强制转换为一个指向缓冲区A首地址的指针,这个缓冲区A里放的内容为16位整型,把这个首地址赋值给tmp
if (huart->Init.Parity == UART_PARITY_NONE)
{
*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);将数据寄存器DR的值赋给temp指向地址里
huart->pRxBuffPtr += 2U;//指向下两个地址 因为pRxBuffPtr指针指向的地址一个里面只能装8bit,这里有9bit所以需要两个地址位置
}
else
{
*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
huart->pRxBuffPtr += 1U;
}
}
else
{
if (huart->Init.Parity == UART_PARITY_NONE)
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);//将数据寄存器DR的值载入到huart的缓冲区指针所指向的位置
}//*(huart->pRxBuffPtr)++ =
//huart->pRxBuffPtr指向的地址里的内容被附上DR里的东西
//++在后 优先级和等号比起来更低 所以DR的内容先赋值给*huart->pRxBuffPtr,再让huart->pRxBuffPtr这个地址加1
else
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
}
}
if (--huart->RxXferCount == 0U)
//--放前面 优先级高 也就是减1后再执行if判断操作
{ //中断已使用 然后让中断失能
/* Disable the UART Data Register not empty Interrupt */
__HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);
/* 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);
/* Rx process is completed, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*Call registered Rx complete callback*/
huart->RxCpltCallback(huart);
#else
/*Call legacy weak Rx complete callback*/
HAL_UART_RxCpltCallback(huart); //用户接口 用户在这个里面写回调函数的内容
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
return HAL_OK;
}
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
为什么要定义tmp?不可以直接把值放在pRxBuffPtr指向的地址里吗?
tmp的存在是必须的,DR必须做一次读操作才能动作,没有一个赋值语句,DR那句就没办法执行,所以必须有一个临时变量,用来操作DR(这句话抄的 不知道什么意思)
这个函数是把所有输入的数据一个一个存放到缓存区中,也就是,一个数据对应一次中断,进入中断处理函数,调用UART_Receive_IT,根据RxXferSize设置你要放多少数据在缓存区,直到确认所有的数据都存放到缓存区中,huart->RxXferCount对应的值也会自减为0,此时会执行3个__HAL_UART_DISABLE_IT函数来关闭中断(我也不知道为什么HAL库要这样设置),之后会进入回调函数,我们只需在回调函数中写入我们的用户代码即可。但是其中一定要包含打开中断的函数,因为__HAL_UART_DISABLE_IT这个函数已经关闭中断。
二、HAL_UART_Receive
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint16_t *tmp;
uint32_t tickstart = 0U;
/* Check that a Rx process is not already ongoing */
if (huart->RxState == HAL_UART_STATE_READY)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* Init tickstart for timeout managment */
tickstart = HAL_GetTick();
huart->RxXferSize = Size;
huart->RxXferCount = Size;
/* Check the remain data to be received */
while (huart->RxXferCount > 0U)
{
huart->RxXferCount--;
if (huart->Init.WordLength == UART_WORDLENGTH_9B)
{
if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT;
}
tmp = (uint16_t *) pData;
if (huart->Init.Parity == UART_PARITY_NONE)
{
*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);
pData += 2U;
}
else
{
*tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
pData += 1U;
}
}
else
{
if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT;
}
if (huart->Init.Parity == UART_PARITY_NONE)
{
*pData++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
}
else
{
*pData++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
}
}
}
/* At end of Rx process, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(huart);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
这个函数和上一个相比,多了给RxXferCount等赋值的操作,但是少了回调函数,来一个数据存一个,存在缓存区,直到RxXferCount为0
三 HAL_UART_Receive_IT
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
/* Check that a Rx process is not already ongoing */
if (huart->RxState == HAL_UART_STATE_READY)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* 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);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
记住!这个函数不是用来接收数据的!这个函数不是用来接收数据的!这个函数不是用来接收数据的!他是用来打开中断,配置串口中断的!不是真正的接收数据的中断函数,很容易把它和其他两个函数混淆。看了上面两个函数的解释,这个函数的内容应该不难理解,这也就是,我们要手动打开串口中断,就要在main函数里面首先写下它,否则无法进入串口中断(亲测如此),其次还要在回调函数里面添加这个函数(因为之前就说过一旦进入回调函数,串口中断就会关闭),为了下一次接收数据考虑,需要这么做