嵌入式软件异步编程:同步转为异步

时间:2022-11-18 07:58:11


在现有同步实现的基础上进行异步实现,需要将同步代码转换成异步代码。

mingdu.zheng at gmail dot com

基本方法:分开请求和结果

I/O的基本操作可以细分为两个部分:

  1. 发起请求
  2. 检查结果

同步编程模型中,这两部分是在一个函数中的,在没有操作系统的情况下,检查结果部分可能是一个繁忙等待式的轮询,在有操作系统的情况下,检查结果部分可能首先是一个等待信号量的操作,该操作会将线程挂起直到得到I/O的通知。要将同步编程转换成异步编程就要将这两个基本部分分开,若采用轮询式异步编程,只要系统空闲就会调用检查结果部分,若采用回调式异步编程,检查结果部分则可以作为回调函数。

以轮询异步串口发送为例

同步发送

HAL_StatusTypeDef syncUART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
huart->TxXferCount = Size;
while (huart->TxXferCount > 0U)
{
huart->TxXferCount--;
huart->Instance->TDR = *pData++;
while(__HAL_UART_GET_FLAG(huart, UART_FLAG_TXE) == RESET){}
}

return HAL_OK;
}

异步发送

异步发送将syncUART_Transmit分割为两个函数:asyncUART_Transmit和asyncUART_Process。asyncUART_Transmit是I/O请求发起函数,asyncUART_Process检查I/O请求的完成情况。asyncUART_Transmit函数在需要发送数据时调用一次,而asyncUART_Process则需要在主函数循环中不断地调用以检查I/O完成请求。

发起请求

  • asyncUART_Transmit发起I/O请求成功后立即返回,并不会等待I/O完成。
HAL_StatusTypeDef asyncUART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
if(huart->gState != HAL_UART_STATE_READY)
{
return HAL_BUSY;
}

huart->TxXferCount = Size;
huart->pTxBuffPtr = pData;
if (huart->TxXferCount > 0U)
{
huart->TxXferCount--;
huart->Instance->TDR = *huart->pTxBuffPtr++;
huart->gState = HAL_UART_STATE_BUSY_TX;
}

return HAL_OK;
}

检查结果

  • asyncUART_Process检查I/O的完成情况,如果I/O未完成,那么立即退出,等下一次主循环再次调用。
  • 这个例子在检查到上一个I/O请求已经完成的情况下会发起下一个I/O请求。
  • asyncUART_Process使用了状态机,因为asyncUART_Process不停地被调用,需要根据外设的状态执行不同的检查或者什么都不做。
void asyncUART_Process(UART_HandleTypeDef *huart)
{
switch(huart->gState)
{
case HAL_UART_STATE_BUSY_TX:
if(__HAL_UART_GET_FLAG(huart, UART_FLAG_TXE) == SET)
{
if (huart->TxXferCount > 0U)
{
huart->TxXferCount--;
huart->Instance->TDR = *huart->pTxBuffPtr++;
}
else
{
huart->gState = HAL_UART_STATE_READY;
}
}
break;

default:
break;
}
}