在现有同步实现的基础上进行异步实现,需要将同步代码转换成异步代码。
mingdu.zheng at gmail dot com
基本方法:分开请求和结果
I/O的基本操作可以细分为两个部分:
- 发起请求
- 检查结果
同步编程模型中,这两部分是在一个函数中的,在没有操作系统的情况下,检查结果部分可能是一个繁忙等待式的轮询,在有操作系统的情况下,检查结果部分可能首先是一个等待信号量的操作,该操作会将线程挂起直到得到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;
}
}