串口数据框架
一、串口初始化配置
(串口任务函数)
unsigned int uart_Task_init(struct tasks* urt)
函数内容:定义了串口名称,分配内存空间。
unsigned int uart_Task_init(struct tasks* urt)
{
#if UART_TASK_FLAG
urt->task_Name = "Task_uart";
urt->task_Stack_Size = UART_TASK_STACK_SIZE;
urt->task_Value = NULL;
urt->task_Prio = UART_TASK_PRIO;
InitQueue(rx_queue);
return 1;
#else
return 0;
#endif
}
InitQueue函数完成了队列的初始化操作
void InitQueue(SqQueue *Q)
{
Q->rear = Q->front = 0;
}
( 串口初始化函数)
void SerialInit(uint32_t MCU_Baudrate, UART_TypeDef *UARTx)
函数内容:用于串口GPIO及波特率等一些参数的设置。
- 配置了uart0的GPIO输入输出脚
- 定义了数据位,奇偶校验位,停止位的长度与形式
- 设置了触发中断时长
#if (THIS_PROJECT_IS_BM01 || THIS_PROJECT_IS_VC002||THIS_PROJECT_IS_VC002B)
PORT_Init(PORTM, PIN2, FUNMUX0_UART0_RXD, 1);
PORT_Init(PORTM, PIN3, FUNMUX1_UART0_TXD, 0);
UART_initStruct.DataBits = UART_DATA_8BIT;
UART_initStruct.Parity = UART_PARITY_NONE;
UART_initStruct.StopBits = UART_STOP_1BIT;
UART_initStruct.RXThreshold = 3;
UART_initStruct.RXThresholdIEn = 1;
UART_initStruct.TXThreshold = 3;
UART_initStruct.TXThresholdIEn = 0;
UART_initStruct.TimeoutTime = 10;
UART_initStruct.TimeoutIEn = 1;
二、底层数据接收
UART0_Handler(void)
调用EnRxQueue函数接收串口数据
uint32_t chr;
char data;
if (UART_INTRXThresholdStat(UART0) || UART_INTTimeoutStat(UART0))
{
while (UART_IsRXFIFOEmpty(UART0) == 0)
{
if (UART_ReadByte(UART0, &chr) == 0)
{
usleep(3);
EnRxQueue(&rx_queue[MCU_UART0], chr);
}
}
}
bool EnRxQueue(SqQueue *Q, ElemType x)
{
if ((Q->rear + 1) % MaxSize == Q->front)
return false;
Q->rear = (Q->rear + 1) % MaxSize;
Q->data[Q->rear] = x;
return true;
}
三、出队数据接收
FR_data_read(unsigned char *pDe_buf, unsigned char *pDe_cnt, unsigned char *pOld_buf, unsigned char pOld_cnt, UART_STATUS uart_sta)
函数内容:将串口接收的数据进行出队操作,取出队列数据,此外,为了防止出现数据与数据的重叠,函数体也相应地设置了参数用于判断每帧数据的断点
- param {unsigned char} *pDe_buf : 接收到的数据buf
- param {unsigned char} *pDe_cnt : 接收到的数据的长度
- param {unsigned char} *pOld_buf : 上次接收的没处理完的旧数据。
- param {unsigned char} pOld_cnt : 旧数据长度。
- param {UART_STATUS} uart_sta : 串口状态参数,可通过改状态判断串口当前处于哪一个状态。
while (dt_uart_receive_char(0, &ch))
{
if (pOld_cnt > 0)
{
printf("has old_cnt %d\r\n", pOld_cnt);
}
if (now() - pre_uart0_time > 200)
{
printf("new uart frame Head is %02x \r\n", (uint8_t)ch);
if (ch == 0x7e)
{
pOld_cnt = 0;
De_len = 0;
}
}
printf("%02x ", (unsigned char)ch);
if (0x7d == (unsigned char)ch)
{
escape_num++;
}
if (0 < pOld_cnt)
{
memset(pOld_buf, 0, pOld_cnt);
pDe_buf[De_len + pOld_cnt] = ch;
De_len++;
msleep(3);
*pDe_cnt = De_len + pOld_cnt;
}
else
{
pOld_cnt = 0;
pDe_buf[De_len++] = ch;
msleep(3);
*pDe_cnt = De_len;
}
pre_uart0_time = now();
}
if (De_len > 0 && ((pDe_buf[1] + escape_num) == De_len))
{
printf("R[%d][%d]:Uart 0\r\n", pDe_buf[1], De_len);
De_len = 0;
escape_num = 0;
}
}
主要使用dt_uart_receive_char(…)来调用出队操作函数,即DeRxQueue(…),读取接收
bool DeRxQueue(SqQueue *Q, ElemType *x)
{
if (Q->rear == Q->front)
return false;
Q->front = (Q->front + 1) % MaxSize;
*x = Q->data[Q->front];
return true;
}
四、数据解析
int parseData(unsigned char *dst, int *dstLen, unsigned char *src, int srcLen, int *RescapeNum)
数据解析函数。依据串口协议解析数据,即首先去掉帧头帧尾(同时存在帧头帧尾)
int flag = 0;
unsigned char checkSum = 0;
int dataLen = 0;
unsigned char tmpDst[100] = {0};
int tmpDstIdx = 0;
寻找帧头
for (i = 0; i < srcLen; i++)
{
if (0x7E == src[i])
break;
}
if (0 != i)
{
log_abort("无效数据:%d\r\n", i);
return i;
}
寻找帧尾
for (i = 1; i < srcLen; i++)
{
if (src[i] != 0x7E && flag == 1)
{
tmpDst[tmpDstIdx++] = src[i];
log_abort(" tmpDst1[%d]: %02X\r\n", tmpDstIdx - 1, tmpDst[tmpDstIdx - 1]);
}
else if (src[i] == 0x7E && flag == 1)
{
log_abort(" tmpDst2[%d]: %02X\r\n", tmpDstIdx, tmpDst[tmpDstIdx]);
flag = 2;
break;
}
log_abort(" tmpDst3[%d]: %02X\r\n", tmpDstIdx - 1, tmpDst[tmpDstIdx - 1]);
}
反转义:dedivertFrame(…)主要将转化后数据中的0x01,0x02还原为原数据的0x7D,0x7E
if (src[srcIdx] == 0x7D)
{
if (src[srcIdx + 1] == 0x02)
{
dst[dstIdx++] = 0x7E;
deLen++;
}
else if (src[srcIdx + 1] == 0x01)
{
dst[dstIdx++] = 0x7D;
deLen++;
}
校验和:checkSumFun(…)主要用于计算实际数据的校验和
for (i = 0; i < len; i++)
{
sum = (unsigned char)((sum + dst[i]) & 0xFF);
}
验证长度:累计计算数据内容长度,使用memmove(…)字符串库函数进行数据数据的读取
if ((*dstLen - 1 - 1) != dataLen)
{
log_error("Error:dataLenErr:%d\r\n", dataLen);
return (tmpDstIdx - 1);
}
*dstLen = dataLen;
memmove(dst, dst + 1, dataLen);
五、数据分离
app_uart_Start(…)通过返回的app_uart_recvCmd(…)来判断是协议数据还是OTA数据
int app_uart_Start(unsigned char *Data_buff, int Data_buff_Len, UART_STATUS uart_sta)
协议数据:进行数据解析处理,EventDataParse(…),处理完成数据,入消息队列给UI或其他需要接受数据的位置
OTA数据:
- CAN_CMD(ota复位升级请求,mcu进入升级状态)
case CAN_CMD:
#else
case cmdUartVersionInquire:
#endif
#if TEST_TASK_FLAG
State_info.ManufacturerCode[0] = Data_buff[2];
State_info.ManufacturerCode[1] = Data_buff[3];
State_info.HardwareVersion[0] = Data_buff[4];
State_info.HardwareVersion[1] = Data_buff[5];
State_info.ControlBoardVersion[0] = Data_buff[6];
State_info.ControlBoardVersion[1] = Data_buff[7];
State_info.CommunicationProtocolVersion = Data_buff[8];
State_info.TypeOfEquipment = Data_buff[9];
- CAN_DATA(ota升级准备,擦除flag固件标志位,flash空间)
case CAN_DATA:
#else
case cmdUartStateHandshake:
#endif
#if TEST_TASK_FLAG
vers_info.ScreenSwitch = Data_buff[2];
vers_info.UIshow = Data_buff[3];
vers_info.WorkingMode = Data_buff[4];
vers_info.ContinuousWorkingMode = Data_buff[5];
vers_info.CurrentLanguage = Data_buff[6];
vers_info.LampRingState = Data_buff[7];
vers_info.WorkingState = Data_buff[8];
vers_info.AbnormalInformation = Data_buff[9];
vers_info.ElectricityValue = Data_buff[10];
六、数据处理
EventDataParse(…)串口数据统一处理框架,通过该函数将解析完的数据统一进行处理,转化为UI框架能够识别的数据格式,统一通过消息队列依次发送出去。
int EventDataParse(char *data, int dstLen, char *src)
- param {char} *data : 实际处理的数据内容
- param {int} dstLen : 实际处理数据长度
- param {char} *src :转化完成后的数据内容。
int mq_send(mqd_t msgid, const char *msg, size_t msg_len, unsigned int msg_prio)
将处理完的数据入到消息队列
q = (mqQueue *)msgid;
q->msg_size = msg_len;
xTicksToWait = (q->oflag & O_NONBLOCK) ? 0 : portMAX_DELAY;
if (xQueueSend(q->xQueue, (const void *)msg, xTicksToWait) != pdTRUE)
{
return -1;
}
七、下行数据入队
app_uart_Start(…)主要使用下行数据组合函数cmdtomcu(…)和数据组包函数FR_Combination(…)进行入队前的数据处理
int app_uart_Start(unsigned char *Data_buff, int Data_buff_Len, UART_STATUS uart_sta)
arm主动发送指令
int cmdtomcu(unsigned char *preaddata, unsigned char *pCmdData, unsigned char len)
{
for (int i = 0; i < len; i++)
{
pCmdData[i] = preaddata[i];
}
return len;
}
下行数据组合:帧头+长度+数据+校验和+帧尾
int FR_Combination(unsigned char Rec_CmdData[], unsigned char nRecCmdCnt)
{
unsigned char pSen_CmdData[nRecCmdCnt + 4];
unsigned char Sen_CmdData[UART_DATA_BUFF_SIZE];
unsigned char crc, pSen_datalen = 0;
unsigned int crcSunm = 0;
pSen_CmdData[0] = HEAD_END;
pSen_CmdData[1] = 4 + nRecCmdCnt;
for (unsigned char j = 0; j < nRecCmdCnt; j++)
{
pSen_CmdData[2 + j] = Rec_CmdData[j];
}
for (unsigned char i = 1; i < 2 + nRecCmdCnt; i++)
{
crcSunm += pSen_CmdData[i];
}
crc = crcSunm & 0xff;
pSen_CmdData[2 + nRecCmdCnt] = crc;
pSen_CmdData[3 + nRecCmdCnt] = HEAD_END;
escape(pSen_CmdData, sizeof(pSen_CmdData), Sen_CmdData, &pSen_datalen);
for (unsigned char j = 0; j < pSen_datalen; j++)
{
bl_TX_com1(MCU_UART0, Sen_CmdData[j]);
}
return 0;
}
其中escape函数将7E转换成7D02,进行转义操作
for (i = 1; i < rlen - 1; i++, j++)
{
if (realACK[i] == 0x7e)
{
trunACK[j] = 0x7d;
j++;
trunACK[j] = 0x02;
}
else if (realACK[i] == 0x7d)
{
trunACK[j] = 0x7d;
j++;
trunACK[j] = 0x01;
}
八、发送数据
int ota_data_send_Uart(void)
将写入bl_tx_q_com1循环队列里面的数据通过串口发送出去
unsigned char sen_buff[UART_BUFF_SIZE];
unsigned short int sen_cnt = 0;
ElemType ch;
while (!isEmpty(&tx_queue[MCU_UART0]))
{
DeQueue(&tx_queue[MCU_UART0], &ch);
sen_buff[sen_cnt] = ch;
dt_uart_send_char(MCU_UART0,ch);
sen_cnt++;
if (sen_cnt > UART_BUFF_SIZE - 1)
{
sen_cnt = 0;
}
}
使用DeQueue(…)出队操作函数,将数据输出
bool DeQueue(SqQueue *Q, ElemType *x)
{
if (Q->rear == Q->front)
return false;
Q->front = (Q->front + 1) % MaxSize;
*x = Q->data[Q->front];
return true;
}
|