一.回顾总结
昨天作者已经写好了环形缓冲区的代码,经测试无误。但是作者最后也说,读取USART1的数据一定不能死等待,为了解决这个问题我们先思考一下WIFI模块层面的代码怎么设计。WIFI模块层面实际就是发送AT指令和接收返回指令的程序,这里会有两个函数(任务):
int ATSendCmd(char *buf, int len, char *resp, int resp_len, int timeout);
void ATRecvParser( void * params);
ATSendCmd负责发送AT指令并读取返回状态,对于MCU来说,串口发送完数据然后等待ESP-12F返回是一个很漫长的过程,为了有效利用MCU,我们需要让其阻塞,不要占用处理器。因此,在ATSendCmd发送完AT指令后就上锁(通过FreeRTOS的信号量实现)阻塞,等待ATRecvParser解析完毕返回后将其唤醒。
二.编写WIFI模块的程序
int8_t ATSendCmd(uint8_t* buf, int len, int timeout)
{
int8_t ret;
int8_t err;
hal_at_send(buf, len);
hal_at_send("\r\n", 2);
/* 发送完AT命令就要等待WIFI模块的返回值,但这里一定不能死等待,要使用互斥锁 */
/* 1--成功获得锁;0--失败*/
ret = platform_mutex_lock_timeout(&at_ret_mutex, timeout);
if (ret)
{
err = getATState();
if (err == AT_OK)
{
return AT_OK;
}
else if (err == AT_ERROR)
{
return AT_ERROR;
}
else
{
return AT_TIMEOUT;
}
}
return AT_TIMEOUT;
}
void ATRecvParser(void* params) // void (*ptr) (void* p) 函数类型
{
uint8_t buf[AT_RESP_LEN];
int index = 0;
while (1)
{
if (index < AT_RESP_LEN) //USART1读不到数据就阻塞,不会空转
{
hal_at_recv(buf + index, (int)portMAX_DELAY);
}
/* 只有读到了换行才去解析数据 */
if (index && buf[index] == '\n' && buf[index - 1] == '\r')
{
buf[index + 1] = '\0'; //先给出结束字符方便使用string.h库函数
if (strstr("OK", buf)) //case1,接收到OK
{
setATState(AT_OK);
platform_mutex_unlock(&at_ret_mutex); //成功接收到数据就解锁
memcpy(g_at_resp, buf, index + 1); /* len?*/
}
else if (strstr("ERROR", buf)) //case2,接收到ERROR
{
setATState(AT_ERROR);
platform_mutex_unlock(&at_ret_mutex);
}
else if (isSpecialAT(buf)) //case3,接收到特殊字符串
{
procSpecialAT(buf);
}
}
index ++; //只要读到了数据,写指针index都++
}
}
以下为改进后的usart_read函数和中断函数
void myUSART1_Read(uint8_t* c, int timeout)
{
while (1)
{
if (ring_buffer_read(c, &USART1_buffer) == 0)
{
return;
}
else
{
platform_mutex_lock_timeout(&USART1_Rcev_Mutex, timeout);
}
}
}
/* 使用环形缓冲区的中断函数 */
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
RxData = USART_ReceiveData(USART1);
ring_buffer_write(RxData, &USART1_buffer);
platform_mutex_unlock(&USART1_Rcev_Mutex); /*<!中断函数内部解锁?*/
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
因为该程序作者还没有调试,而且对AT返回值的解析肯定还不完善,因此子函数暂不展示。
三.模拟接受AT返回值的过程
ATSendCmd函数发送完AT指令后就阻塞了,一段时间后串口接收到数据,中断函数将数据写入环形缓冲区后唤醒myUSART1_Read,同时也唤醒了 ATRecvParser。ATRecvParser将数据保存到局部数组buf中,当读到"\r\n"时就开始解析,假如接收到"OK",那么解析后就设置ATState为OK,并且唤醒ATSendCmd。ATSendCmd读取ATState并返回状态值表示OK, ERROR, TIMEOUT,至此ATSendCmd的任务就完成了。
但是今天写的程序肯定有一些问题,比如能否在中断函数中解锁unlock?对特殊AT返回值的解析肯定不完善,对于这些问题作者明天努力解决@.@