无论是蓝牙、WiFi,还是4G、5G,亦或是其它模组,都支持AT指令+透传模式。
AT指令模式下,执行查询指令和操作(设置)指令。
响应速度快,逻辑交互明确,不需要复杂的处理代码。
比如查询MAC信息指令、设置波特率指令等等操作,可以只管发,然后等待中断处进行数据处理,中间只需要一个全局变量传递状态,和一个缓冲区指针缓存结果,用掉之后再释放掉。
主控与模组之间的交互
到实际工作场景中,往往需要进入透传模式。
模组收到的数据,直接传递给MCU;MCU需要发数据时,直接将元数据扔给模组。
透传模式下一般考虑的就两点:
一是 透 传 的协议格式
可以相互之间直接传递原始数据,也可以在头或尾(或头尾)添加固定封包。这主要取决于双方通信是否可靠,以及通信的复杂度。
二是 链 接 状态的传递
进入透传模式,前提肯定是连接建立成功。如果连接断开,要及时反馈,对端同时要及时做好逻辑处理。
其中1对1下,状态传递最好的方式就是检测IO状态(硬件连接)。通过软件方式判断,总有不可避免的bug出现。
主控端的数据处理
主控端一是数据的发送,二是数据的接收。
发送一般不会有什么问题。
主要是如何高效的接收数据?
以蓝牙传输文件为例,采用BR2x51e(-s)模块,串口通讯波特率921600,由平板或手机下发文件,一次一包10K数据大小,总文件大小最大20M左右。
遇到的问题
1、DMA Normal/循环模式下接收数据存在丢数据现象
115200波特率下256KB甚至到1K,基本只会触发一次空闲中断;
921600波特率下即使是256KB的一包数据也会触发多次串口空闲中断;
即波特率越高,由于时钟不同步或者模组定时不精准等原因,串口空闲中断触发的几率更大。
2、DMA循环模式+DMA传输完成中断下出现数据被覆盖
不同于上一个导致硬件中断丢数据,这次是线程中数据处理不过来,导致数据被覆盖。
添加二级缓冲区机制后解决。
总结如下
2种常用的串口接收方式
1、DMA Normal模式+空闲中断接收
这种方式适用于,单次传输模式,即一收一发形式的交互。
因为Normal模式下,
1、接收数据溢出时会从头开始覆盖原有数据,有数据丢失的风险;
2、这种模式下要求DMA接收buf的大小,要大于可能出现的数据最大长度,浪费内存;
但是非常适合处理AT指令模式下的数据交互,buf区只需要很小的长度。
//典型代码
uint8_t g_logRxBuf[LOG_RX_BUF_NUM_MAX];//定义DMA接收缓冲区
void LogInit(void)
{
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);//使能空闲中断
HAL_UART_Receive_DMA(&huart2, g_logRxBuf, LOG_RX_BUF_NUM_MAX);//配置DMA接收地址和长度
}
__weak void ConsoleRxCallback(uint8_t *pData,uint16_t unDataLen) { }//回调函数
void LogRxIdleCallBack(void)
{
if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE)!=RESET)
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2);//清中断标志位
HAL_UART_DMAStop(&huart2);//关闭DMA传输
__HAL_UNLOCK(&huart2);
ConsoleRxCallback(g_logRxBuf,LOG_RX_BUF_NUM_MAX - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx));//数据处理
HAL_UART_Receive_DMA(&huart2, g_logRxBuf, LOG_RX_BUF_NUM_MAX);//重新配置DMA传输
}
}
2、DMA循环模式+串口DMA完成中断/串口DMA半完成中断/串口空闲中断
这种模式下,相当于用了3个中断源触发数据的处理。
这样利用DMA传输的半完成中断和完全中断,只需要很小的buf就能处理很大的数据,这里用1K的buf去接收16K的数据是完全没问题的,处理这个16K的数据接收时,总是先进入半完成中断处理前一半数据,再进入完全中断处理后一半数据,这样处理数据时不影响数据的接收。
硬件层面不会导致数据丢失。
static uint8_t g_bt_dma_buf[BT_BUF_SIZE];//DMA接收buf,1K大小
static volatile uint16_t unBtDmaReadOffset;//接收偏移量(volatile防止被优化)
void BlueToothInit(void)
{
//BlueToothConfig(921600);
__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart3,g_bt_dma_buf,BT_BUF_SIZE);
}
__weak void BTUartRxCallBack(uint8_t *pData,uint16_t unDataLen) {}
void BtUartRxIdleCallback(void)
{
uint32_t ulLen;
if((__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) != RESET))
{
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
ulLen = BT_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx) - unBtDmaReadOffset;
BTUartRxCallBack(&g_bt_dma_buf[unBtDmaReadOffset], ulLen);
//空闲中断:累加DMA接收偏移量
unBtDmaReadOffset += ulLen;
}
}
void BtUartDmaRxHalfCpltCallback(void)
{
uint16_t unLen = 0;
//半完成中断:DMA接收偏移量为buf的一半大小
unLen = BT_BUF_SIZE/2 - unBtDmaReadOffset;
BTUartRxCallBack(&g_bt_dma_buf[unBtDmaReadOffset], unLen);
unBtDmaReadOffset = BT_BUF_SIZE/2;
}
void BtUartDmaRxCpltCallback(void)
{
uint16_t unLen = 0;
//完成中断:DMA接收偏移量就等于buf的大小,即0
unLen = BT_BUF_SIZE - unBtDmaReadOffset;
BTUartRxCallBack(&g_bt_dma_buf[unBtDmaReadOffset], unLen);
unBtDmaReadOffset = 0;
}
二级缓冲区
利用DMA完成中断传输数据时,可以解决硬件中断的数据丢失问题,但是当数据持续高速传输时,会导致应用层处理不过来,这时可以再加一层缓冲区来解决。
static uint8_t pBtRxDataBuff[BT_UART_RX_BUFF_MAX_LEN];//接收缓冲区,4K大小
static volatile uint16_t unBtRxBuffDealOffset = 0;//缓冲区读取偏移量
static volatile uint16_t unBtRxBuffRxOffset = 0;//缓冲区写入偏移量
static volatile uint16_t unBtRxBuffNeedDealLen = 0;//缓冲区写入总长度
void BTUartRxCallBack(uint8_t *pData,uint16_t unDataLen)
{
WriteBlueToothRxData(pData, unDataLen);
OSSemPost(UpperRxSem);
}
//将数据写入“循环”缓冲区
void WriteBlueToothRxData(uint8_t *pData,uint16_t unDataLen)
{
uint16_t unWriteLen = 0,unWriteOffset = 0;
while(1)
{
if(unWriteOffset == unDataLen)
break;
unWriteLen = unDataLen - unWriteOffset;
//当写入长度大于剩余长度时,1、先写入剩余长度
//2、再将多余长度从buf首地址开始写入
if(unWriteLen >= (BT_UART_RX_BUFF_MAX_LEN - unBtRxBuffRxOffset))
unWriteLen = BT_UART_RX_BUFF_MAX_LEN - unBtRxBuffRxOffset;
//拷贝数据
memcpy(&pBtRxDataBuff[unBtRxBuffRxOffset],pData + unWriteOffset,unWriteLen);
unWriteOffset += unWriteLen;
unBtRxBuffNeedDealLen += unWriteLen;
unBtRxBuffRxOffset += unWriteLen;
//写满时将写入位置拉到buf首地址
if(unBtRxBuffRxOffset >= BT_UART_RX_BUFF_MAX_LEN)
unBtRxBuffRxOffset = 0;
}
}
//获取缓冲区数据长度
//unBtRxBuffDealOffset buf读取的偏移量,用来追接收偏移量
uint16_t ReadBlueToothRxDataLen(void)
{
if(unBtRxBuffDealOffset != unBtRxBuffRxOffset)
{
if(unBtRxBuffRxOffset > unBtRxBuffDealOffset)
return unBtRxBuffRxOffset - unBtRxBuffDealOffset;
else
return unBtRxBuffRxOffset + BT_UART_RX_BUFF_MAX_LEN - unBtRxBuffDealOffset;
}
else
return 0;
}
//读取缓冲区数据
uint8_t ReadBlueToothRxData(void)
{
uint8_t byData;
unBtRxBuffNeedDealLen--;
byData = pBtRxDataBuff[unBtRxBuffDealOffset++];
if(unBtRxBuffDealOffset >= BT_UART_RX_BUFF_MAX_LEN)
unBtRxBuffDealOffset = 0;
return byData;
}
void ReadBlueToothRxDataStr(uint8_t *pBuf,uint16_t len)
{
uint16_t i;
for(i=0; i<len; i++)
pBuf[i] = ReadBlueToothRxData();
return;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)