在CubeMX中配置好后,自动生成的代码只需要再添加少量的代码就可以运行了。比如上一篇的点灯,只需要在main函数的大循环中添加几个控制引脚的代码就可以了。HAL库通过systick帮我们实现了delay的功能,只需要调用就行了。
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
CubeMX生成的代码,像上面这样有很多USER CODE BEGIN,USER CODE END的代码,据传这样的代码是为了让CubeMX在原来工程的基础上生成新代码的时候,可以保留这样的注释对中的代码。对于这个功能我没实践过,我只是用CubeMX来生成初始化的代码,还没有试过在代码已经有修改的情况下再次使用CubeMX生成代码。因为CubeMX重新生成代码的时候会将所有的库都重新复制一次,并且修改工程文件,导致所有代码都得重新编译,不是很方便。
为了让后续的rtthread跑起来,还得加入对串口的支持。
CubeMX这样的设计方式是瀑布型的,需要先对整个工程有个全局的考虑,设置好管脚之后生成代码。当你需要调整引脚或是功能的时候,需要回到CubeMX中,重新生成代码,重新走一次之前的流程。
/* USART3 init function */
static void MX_USART3_UART_Init(void)
{
huart3.Instance = USART3;
huart3.Init.BaudRate = 115200;
huart3.Init.WordLength = UART_WORDLENGTH_7B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
比如上面这段代码中,我配置错了UART的比特长度,为了改成正确的8b,按照CubeMX的方式,我需要回到图形化界面,重新生成代码,如果是在一个工程中做到了中期,发现了这个问题,还需要重新生成代码的话,可能会造成意想不到的结果。所以这里我决定直接在源代码中修改,不去理CubeMX了。
在做了上述的修改之后,我们的CubeMX工程和源代码工程就“失步”了。如何能把代码的修改反馈到CubeMX中,我觉得这是CubeMX从一个初始化玩具进化到配置管理工具要解决的问题。图形UI与代码交互式的设计基本上是现代程序设计的主流风格,因为很难有项目能够一开始就把所有的细节都考虑好,交互式的开发风格更符合实际的开发流程。CubeMX可以设计一个编辑器来管理初始化相关的代码,在这个编辑器中分析出用户的修改并反馈到图形界面中。或者是直接用外设初始化的源代码来做为CubeMX的配置管理文件,通过分析源代码来展示UI元素。
前面我们用CubeMX生成了基本的初始化代码,只添加了几行代码就完成了闪灯的功能。后续我们为其添加串口输入输出的功能。串口作为单片机上最为常见的接口,基本上玩不出什么特别的花样了,HAL也是如此。
学习一个新的库最好是先去看他的头文件,通过头文件我们能够知道这个库能提供哪些功能给我们用。通过观察HAL串口库的头文件,前面是一堆的类型和宏定义,这个先不管他。直接看后面对外提供的函数声明。HAL写得还是比较科学,帮我们把这些函数归了类。
/** @addtogroup UART_Exported_Functions_Group1 Initialization and de-initialization functions
* @{
*/
/* Initialization and de-initialization functions ****************************/
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_HalfDuplex_Init(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_LIN_Init(UART_HandleTypeDef *huart, uint32_t BreakDetectLength);
HAL_StatusTypeDef HAL_MultiProcessor_Init(UART_HandleTypeDef *huart, uint8_t Address, uint32_t WakeUpMethod);
HAL_StatusTypeDef HAL_RS485Ex_Init(UART_HandleTypeDef *huart, uint32_t Polarity, uint32_t AssertionTime, uint32_t DeassertionTime);
HAL_StatusTypeDef HAL_UART_DeInit (UART_HandleTypeDef *huart);
void HAL_UART_MspInit(UART_HandleTypeDef *huart);
void HAL_UART_MspDeInit(UART_HandleTypeDef *huart);
最开始就是initialization和de-initialization类的函数声明,这些函数CubeMX应该会帮我们做好,不然对不起他那么大的名头,这样初始化和资源回收的函数就暂时跳过不管。再看下面,是IO类的函数。
/** @addtogroup UART_Exported_Functions_Group2 IO operation functions
* @{
*/
/* IO operation functions *****************************************************/
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_DMAPause(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_DMAResume(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart);
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);
前面是各种后缀不同的发送和接收,然后是irq中断处理函数,最后是一堆的callback。收发类的函数有三种,不带后缀的,IT后缀,DMA后缀。如果没猜错分别对应的是:普通收发,阻塞式的;中断的收发,调用完可以不管,结果会通过中断来返回;DMA就是用DMA收发的,不用mcu参与数据搬移。这些收发类函数的数据都是一个指针加长度的形式,ST的库也延续了他一贯的坚决不用const修饰的风格,如果想发字符串常量,继续cast(强制转换)吧。普通形式的收发多了个timeout参数,看来的确是阻塞式的。后面的callback函数看起来不像是被别人调用的,倒像是什么事情被调用了,我的用这个函数来处理的意思。一般设计的callback函数都有一个注册的过程,这里的callback直接声明成一个函数是几个意思。看来光看头文件有点理解不了st得设计风格了,那就去看看c文件吧。虽然我们一般情况下只看头文件来分析别人提供的代码,对于不一般的代码,当然得用不一般的方法。
/**
* @brief Tx Transfer completed callbacks
* @param huart: uart handle
* @retval None
*/
__weak void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_UART_TxCpltCallback can be implemented in the user file
*/
}
/**
* @brief Tx Half Transfer completed callbacks.
* @param huart: UART handle
* @retval None
*/
__weak void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
/* NOTE: This function should not be modified, when the callback is needed,
the HAL_UART_TxHalfCpltCallback can be implemented in the user file
*/
}
C文件中的callback函数很简单,全是空的,定义的前面有一个__weak标示。不同的编译器对__weak有不同的理解,我用的keil对其的理解是,如果有一个非weak的函数定义,就用这个非weak的定义来替换他。按照这样的写法,我们要用callback的时候直接写一个相应callback的非weak的定义就行了,看这里写的注释也是这么个意思。再回到头文件中,看起来我们了解的函数们已经足以让我们把串口用起来了,那就去代码中试试看。
这个例子中,我用中断方式来收发数据,准备好数据后后台会自动在中断中进行处理。
上面的代码也很简单,将接收到的数据再转发出去。
HAL库的串口和以前标准库的不大一样,标准库没有提供这样的能够收发一段数据的接口,只提供了一个单字节读写的接口,HAL库提供了一个更接近“实际”应用的接口。通常在用中断实现的发送代码中,把要需要发送的数据放到一个缓存中,用一个变量来标记当前已经发送的数据位置,在发送完成或是发送寄存器空的中断中填入新数据,移动标记变量。HAL库也应该会采用这样的思路来设计,下面进入到HAL库的代码中一探究竟。
/**
* @brief Send an amount of data in interrupt mode.
* @param huart: UART handle.
* @param pData: pointer to data buffer.
* @param Size: amount of data to be sent.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
/* Check that a Tx process is not already ongoing */
if(huart->gState == HAL_UART_STATE_READY)
{
if((pData == NULL ) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pTxBuffPtr = pData;
huart->TxXferSize = Size;
huart->TxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->gState = HAL_UART_STATE_BUSY_TX;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Transmit Data Register Empty Interrupt */
SET_BIT(huart->Instance->CR1, USART_CR1_TXEIE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
在HAL_UART_Transmit_IT函数中,代码还是挺简单的,设置好要发送数据的指针,要发送数据的数量,开启发送中断。代码中会对串口当前的状态进行判断。通常在设计代码的时候,对于可能同时发生的状态会用不同的位来表示。st的库经历了几代变迁后,把接受状态用了一个叫rxstate的变量来表示。
/**
* @brief Receive an amount of data in interrupt mode.
* @param huart: UART handle.
* @param pData: pointer to data buffer.
* @param Size: amount of data to be received.
* @retval HAL status
*/
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;
/* Computation of UART mask to apply to RDR register */
UART_MASK_COMPUTATION(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Enable the UART Parity Error and Data Register not empty Interrupts */
SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
HAL_UART_Receive_IT函数和发送函数差不多,也是把数据缓存备好后打开接收中断。既然中断收发函数都很简单,那复杂的代码一定在中断响应函数中。
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
...........
/* If no error occurs */
errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE));
if (errorflags == RESET)
{
/* UART in mode Receiver ---------------------------------------------------*/
if(((isrflags & USART_ISR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{
UART_Receive_IT(huart);
return;
}
}
...........
/* UART in mode Transmitter ------------------------------------------------*/
if(((isrflags & USART_ISR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
}
/* UART in mode Transmitter (transmission end) -----------------------------*/
if(((isrflags & USART_ISR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
{
UART_EndTransmit_IT(huart);
return;
}
}
中断响应函数就是对一堆状态的判断,然后执行对应的代码。这里没有分析HAL库对错误的处理,只关注他对接收和发送的处理。HAL库为了通用,对所有的状态都进行了判断,实际在很多的应用中只需要接收和发送中断。在中断响应函数中,发送空中断到来时,调用UART_Transmit_IT函数,接收中断到来时,调用UART_Receive_IT函数。
static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)
{
uint16_t* tmp;
/* Check that a Tx process is ongoing */
if (huart->gState == HAL_UART_STATE_BUSY_TX)
{
if(huart->TxXferCount == 0U)
{
/* Disable the UART Transmit Data Register Empty Interrupt */
CLEAR_BIT(huart->Instance->CR1, USART_CR1_TXEIE);
/* Enable the UART Transmit Complete Interrupt */
SET_BIT(huart->Instance->CR1, USART_CR1_TCIE);
return HAL_OK;
}
else
{
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
{
tmp = (uint16_t*) huart->pTxBuffPtr;
huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);
huart->pTxBuffPtr += 2U;
}
else
{
huart->Instance->TDR = (uint8_t)(*huart->pTxBuffPtr++ & (uint8_t)0xFFU);
}
huart->TxXferCount--;
return HAL_OK;
}
}
else
{
return HAL_BUSY;
}
}
static HAL_StatusTypeDef UART_EndTransmit_IT(UART_HandleTypeDef *huart)
{
/* Disable the UART Transmit Complete Interrupt */
CLEAR_BIT(huart->Instance->CR1, USART_CR1_TCIE);
/* Tx process is ended, restore huart->gState to Ready */
huart->gState = HAL_UART_STATE_READY;
HAL_UART_TxCpltCallback(huart);
return HAL_OK;
}
UART_Transmit_IT和UART_EndTransmit_IT是发送中断的处理函数。在UART_Transmit_IT函数中,判断是否还有数据待发送,如果没有则关闭发送empty中断TXEIE,打开发送complete中断TCIE,这样当数据发送完成后会进入到UART_EndTransmit_IT 函数中。如果还有待发数据,那就继续发送。这里的处理逻辑还是挺简单的,发送时先准备好数据,然后打开发送empty中断,进入empty中断后,有数据就发数据,没数据开complete中断,后续进入complete中断后,调用HAL_UART_TxCpltCallback函数。这个cplt函数是weak的,用户可以在自己的代码中重写它,有点虚函数的意味。
static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
uint16_t* tmp;
uint16_t uhMask = huart->Mask;
/* Check that a Rx process is ongoing */
if(huart->RxState == HAL_UART_STATE_BUSY_RX)
{
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
{
tmp = (uint16_t*) huart->pRxBuffPtr ;
*tmp = (uint16_t)(huart->Instance->RDR & uhMask);
huart->pRxBuffPtr +=2;
}
else
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
}
if(--huart->RxXferCount == 0)
{
/* Disable the UART Parity Error Interrupt and RXNE interrupt*/
CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
/* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Rx process is completed, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
HAL_UART_RxCpltCallback(huart);
return HAL_OK;
}
return HAL_OK;
}
else
{
/* Clear RXNE interrupt flag */
__HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);
return HAL_BUSY;
}
}
UART_Receive_IT是接收中断的处理函数,逻辑与tx的类似,只不过方向是接收。这个函数接收到指定的数据长度后会关闭接收中断并调用HAL_UART_RxCpltCallback函数。这样的接收机制几乎没什么用处,一般应用场景下接收数据是不可预知的,即使协议长度固定,在线路上也会因为干扰而出现错误包,导致包长变化。如果将每次接收设置为1个字节就退化到了单字节接收模式。如果增加超时机制,可以按照多字节接收,但是会引入定时器,这个和应用密切相关,不好在库中定义。不知道st出于什么原因设计了这样的接收机制,导致串口库的接收部分代码基本上不会在应用中使用。
CubeMXC语言STM32
RTThread与CubeMX – (2)修改CubeMX生成的代码
2017-10-09
CubeMX, rtthread
No Comments
xtoolbox
在CubeMX中配置好后,自动生成的代码只需要再添加少量的代码就可以运行了。比如上一篇的点灯,只需要在main函数的大循环中添加几个控制引脚的代码就可以了。HAL库通过systick帮我们实现了delay的功能,只需要调用就行了。
CubeMX生成的代码,像上面这样有很多USER CODE BEGIN,USER CODE END的代码,据传这样的代码是为了让CubeMX在原来工程的基础上生成新代码的时候,可以保留这样的注释对中的代码。对于这个功能我没实践过,我只是用CubeMX来生成初始化的代码,还没有试过在代码已经有修改的情况下再次使用CubeMX生成代码。因为CubeMX重新生成代码的时候会将所有的库都重新复制一次,并且修改工程文件,导致所有代码都得重新编译,不是很方便。
为了让后续的rtthread跑起来,还得加入对串口的支持。
CubeMX这样的设计方式是瀑布型的,需要先对整个工程有个全局的考虑,设置好管脚之后生成代码。当你需要调整引脚或是功能的时候,需要回到CubeMX中,重新生成代码,重新走一次之前的流程。
比如上面这段代码中,我配置错了UART的比特长度,为了改成正确的8b,按照CubeMX的方式,我需要回到图形化界面,重新生成代码,如果是在一个工程中做到了中期,发现了这个问题,还需要重新生成代码的话,可能会造成意想不到的结果。所以这里我决定直接在源代码中修改,不去理CubeMX了。
在做了上述的修改之后,我们的CubeMX工程和源代码工程就“失步”了。如何能把代码的修改反馈到CubeMX中,我觉得这是CubeMX从一个初始化玩具进化到配置管理工具要解决的问题。图形UI与代码交互式的设计基本上是现代程序设计的主流风格,因为很难有项目能够一开始就把所有的细节都考虑好,交互式的开发风格更符合实际的开发流程。CubeMX可以设计一个编辑器来管理初始化相关的代码,在这个编辑器中分析出用户的修改并反馈到图形界面中。或者是直接用外设初始化的源代码来做为CubeMX的配置管理文件,通过分析源代码来展示UI元素。
前面我们用CubeMX生成了基本的初始化代码,只添加了几行代码就完成了闪灯的功能。后续我们为其添加串口输入输出的功能。串口作为单片机上最为常见的接口,基本上玩不出什么特别的花样了,HAL也是如此。
学习一个新的库最好是先去看他的头文件,通过头文件我们能够知道这个库能提供哪些功能给我们用。通过观察HAL串口库的头文件,前面是一堆的类型和宏定义,这个先不管他。直接看后面对外提供的函数声明。HAL写得还是比较科学,帮我们把这些函数归了类。
最开始就是initialization和de-initialization类的函数声明,这些函数CubeMX应该会帮我们做好,不然对不起他那么大的名头,这样初始化和资源回收的函数就暂时跳过不管。再看下面,是IO类的函数。
前面是各种后缀不同的发送和接收,然后是irq中断处理函数,最后是一堆的callback。收发类的函数有三种,不带后缀的,IT后缀,DMA后缀。如果没猜错分别对应的是:普通收发,阻塞式的;中断的收发,调用完可以不管,结果会通过中断来返回;DMA就是用DMA收发的,不用mcu参与数据搬移。这些收发类函数的数据都是一个指针加长度的形式,ST的库也延续了他一贯的坚决不用const修饰的风格,如果想发字符串常量,继续cast(强制转换)吧。普通形式的收发多了个timeout参数,看来的确是阻塞式的。后面的callback函数看起来不像是被别人调用的,倒像是什么事情被调用了,我的用这个函数来处理的意思。一般设计的callback函数都有一个注册的过程,这里的callback直接声明成一个函数是几个意思。看来光看头文件有点理解不了st得设计风格了,那就去看看c文件吧。虽然我们一般情况下只看头文件来分析别人提供的代码,对于不一般的代码,当然得用不一般的方法。
C文件中的callback函数很简单,全是空的,定义的前面有一个__weak标示。不同的编译器对__weak有不同的理解,我用的keil对其的理解是,如果有一个非weak的函数定义,就用这个非weak的定义来替换他。按照这样的写法,我们要用callback的时候直接写一个相应callback的非weak的定义就行了,看这里写的注释也是这么个意思。再回到头文件中,看起来我们了解的函数们已经足以让我们把串口用起来了,那就去代码中试试看。
这个例子中,我用中断方式来收发数据,准备好数据后后台会自动在中断中进行处理。
上面的代码也很简单,将接收到的数据再转发出去。
HAL库的串口和以前标准库的不大一样,标准库没有提供这样的能够收发一段数据的接口,只提供了一个单字节读写的接口,HAL库提供了一个更接近“实际”应用的接口。通常在用中断实现的发送代码中,把要需要发送的数据放到一个缓存中,用一个变量来标记当前已经发送的数据位置,在发送完成或是发送寄存器空的中断中填入新数据,移动标记变量。HAL库也应该会采用这样的思路来设计,下面进入到HAL库的代码中一探究竟。
在HAL_UART_Transmit_IT函数中,代码还是挺简单的,设置好要发送数据的指针,要发送数据的数量,开启发送中断。代码中会对串口当前的状态进行判断。通常在设计代码的时候,对于可能同时发生的状态会用不同的位来表示。st的库经历了几代变迁后,把接受状态用了一个叫rxstate的变量来表示。
HAL_UART_Receive_IT函数和发送函数差不多,也是把数据缓存备好后打开接收中断。既然中断收发函数都很简单,那复杂的代码一定在中断响应函数中。
中断响应函数就是对一堆状态的判断,然后执行对应的代码。这里没有分析HAL库对错误的处理,只关注他对接收和发送的处理。HAL库为了通用,对所有的状态都进行了判断,实际在很多的应用中只需要接收和发送中断。在中断响应函数中,发送空中断到来时,调用UART_Transmit_IT函数,接收中断到来时,调用UART_Receive_IT函数。
UART_Transmit_IT和UART_EndTransmit_IT是发送中断的处理函数。在UART_Transmit_IT函数中,判断是否还有数据待发送,如果没有则关闭发送empty中断TXEIE,打开发送complete中断TCIE,这样当数据发送完成后会进入到UART_EndTransmit_IT 函数中。如果还有待发数据,那就继续发送。这里的处理逻辑还是挺简单的,发送时先准备好数据,然后打开发送empty中断,进入empty中断后,有数据就发数据,没数据开complete中断,后续进入complete中断后,调用HAL_UART_TxCpltCallback函数。这个cplt函数是weak的,用户可以在自己的代码中重写它,有点虚函数的意味。
UART_Receive_IT是接收中断的处理函数,逻辑与tx的类似,只不过方向是接收。这个函数接收到指定的数据长度后会关闭接收中断并调用HAL_UART_RxCpltCallback函数。这样的接收机制几乎没什么用处,一般应用场景下接收数据是不可预知的,即使协议长度固定,在线路上也会因为干扰而出现错误包,导致包长变化。如果将每次接收设置为1个字节就退化到了单字节接收模式。如果增加超时机制,可以按照多字节接收,但是会引入定时器,这个和应用密切相关,不好在库中定义。不知道st出于什么原因设计了这样的接收机制,导致串口库的接收部分代码基本上不会在应用中使用。
CubeMXC语言STM32