写在前面
我已经在我的另外一篇博客中写到了如何进行芯片与树莓派的通信,传送门在下面:
STM32F427IIH6芯片通过DMA+USART(UART)与树莓派进行双工通信
那么我们接收到串口的数据之后如何进行解析处理呢?今天这篇博客主要就是解决这个问题啦~
程序处理逻辑
流程图
自然语言描述
自然语言描述起来的话还是比较简单的,既然要处理数据,那么最开始肯定先要把数据通过串口接收下来,这个时候就用到了HAL_UART_Receive_DMA(&huart8, uart8Rx, buffer_size);
这个函数,当接收到了数据之后我们要判断帧头是否正确,如果正确的话我们就将数据复制到相应数组里面去,用的就是这个函数SERDEB_PushCmd(uart8Rx, uart8RxLength);
,如果帧头错误就需要等待下一次串口接收数据,数据接收部分完成之后就开始对数据包开始做解析了,解析帧头后面的数据包内容用的是这两个函数DebugCmdProceed();
和GetDebugCmd(uint8_t *cmd, uint16_t len)
,最后根据数据包里面的信息更改芯片内的变量数值,从而实现了树莓派对于底盘运动的控制。
程序分析
接下来我将结合代码具体分析整个流程~
数据接收
首先是数据接收部分了,详细代码在下面:
#define buffer_size 1024
if( huart == &huart8)
{
test3++;
HAL_UART_Receive_DMA(&huart8, uart8Rx, buffer_size);
uart8RxLength = buffer_size-__HAL_DMA_GET_COUNTER(&hdma_uart8_rx);
if(uart8Rx[0]=='@'
&&uart8Rx[1]=='c'
&&uart8Rx[2]=='m'
&&uart8Rx[3]=='d')
{
if(!SERDEB_CmdValid())
SERDEB_PushCmd(uart8Rx, uart8RxLength);
}
__HAL_DMA_DISABLE(&hdma_uart8_rx);
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_8);
}
在源码中可以很清楚的看到程序是先用HAL_UART_Receive_DMA(&huart8, uart8Rx, buffer_size);
来接收串口数据,接收到数据之后通过前几个字符的帧头来判断其与命令帧头是否相同,如果相同的话说明芯片正确接收到了树莓派的指令,即可进行相应的动作。
我再放一下SERDEB_PushCmd(uart8Rx, uart8RxLength);
的源码,这个函数实现的目标就是内存复制
void SERDEB_PushCmd(uint8_t *cmd, uint16_t len)
{
memcpy((uint8_t *)(debugCmd.cmd), cmd, len);
debugCmd.cmd[len] = '\0'; // end of string;
debugCmd.valid = TRUE;
debugCmd.cmdLen = len;
}
但是不知道大家发现了一个问题没有,HAL_UART_Receive_DMA(&huart8, uart8Rx, buffer_size);
和SERDEB_PushCmd(uart8Rx, uart8RxLength);
都是进行了内存复制,那为什么要2个这样相同功能的函数呢?
玄机就是这个语句:debugCmd.cmd[len] = '\0';
。这个关键点就相当于是给数组添加了一个结束字节,这样可以极大程度上方便之后的数据处理。
也就是说HAL_UART_Receive_DMA(&huart8, uart8Rx, buffer_size);
函数是从串口缓冲区拿到数据,SERDEB_PushCmd(uart8Rx, uart8RxLength);
是给取到的数据末尾加上停止标志位,方便之后的数据处理。
因为函数SERDEB_PushCmd(uart8Rx, uart8RxLength);
需要复制内存的字节数,所以需要uart8RxLength = buffer_size-__HAL_DMA_GET_COUNTER(&hdma_uart8_rx);
来计算得到 串口接收到的字节数。
__HAL_DMA_GET_COUNTER(&hdma_uart8_rx);
DMA接收中断使能时该宏将会返回当前接收内存的剩余字节数(总字节数-剩余字节数=串口接受字节数) 。
数据处理
当数据接收完成并且添加了结束字节之后就开始数据处理啦~
首先对数据做一个粗处理,就是先把数据数组里的数据包进行分解,分到了3个不同的数组里,分别是debArray[0],debArray[1],debArray[2]
,并且将数组里面的数值分别赋值到mov_sta,move_mode,step
三个变量里
因为接收到的串口数据是字符型的,所以需要将其转换成数字,所以就用了这个static uint8_t atoi(uint8_t *str, uint16_t *num)
函数将接收到的字符转换成数字
static uint8_t atoi(uint8_t *str, uint16_t *num)
{
#define MAX_DIG 10
uint8_t dig = 0, cnt;
uint8_t numTmp[MAX_DIG];
memset((uint8_t *)numTmp, ' ', MAX_DIG);
*num = 0;
if(*str=='0'&&(*(str+1)=='x'||*(str+1)=='X'))
{
str+=2;
do
{
if(*str>='0'&&*str<='9')
{
numTmp[dig] = *str - '0';
}
else if((*str>='a'&&*str<='f'))
{
numTmp[dig] = *str - 'a' + 10;
}
else if((*str>='A'&&*str<='F'))
{
numTmp[dig] = *str - 'A' + 10;
}
str++; dig++;
if(dig>MAX_DIG-2)
{
break;
}
}
while(
(*str>='0'&&*str<='9')
||(*str>='a'&&*str<='f')
||(*str>='A'&&*str<='F')
);
for(cnt=0; cnt<dig; cnt++)
{
*num += (numTmp[cnt]*power(16, (dig-cnt-1)));
}
dig += 2;
}
else
{
do
{
numTmp[dig] = *str - '0';
str++; dig++;
if(dig>MAX_DIG-1)
break;
}while(*str>='0'&&*str<='9');
for(cnt=0; cnt<dig; cnt++)
{
*num += (numTmp[cnt]*power(10, (dig-cnt-1)));
}
}
return dig;
#undef MAX_DIG
}
static uint32_t power(uint8_t base, uint8_t exponent)
{
uint32_t value;
uint8_t cnt;
if(exponent==0)
return 1;
value = 1;
for(cnt=0; cnt<exponent; cnt++)
{
value *= base;
}
return value;
}
#define MAX_DEBBUF (1024)
#define mov_sta debArray[0]
#define move_mode debArray[1]
#define step debArray[2]
static uint8_t GetDebugCmd(uint8_t *cmd, uint16_t len)
{
uint8_t *pCmd;
uint8_t index;
uint8_t u8Temp = 0;
pCmd = cmd;
if(cmd[0]!='@'
&&cmd[1] != 'c'
&&cmd[2] != 'm'
&&cmd[3] != 'd'
&&cmd[4] != ' ')
{
printf("GetDebugCmd: error cmd format. It must be @cmd n1 n2 n3... \r\n");
return FALSE;
}
pCmd += 5; // the first num
index = 0; // index of debArray, ==0 the first num ==1 the second num ==2... ...
while(1)
{
if(*pCmd=='\r'
||*pCmd=='\n'
||*pCmd=='\0'
||len==0)
{
break;
}
u8Temp = atoi(pCmd, (uint16_t *)(debArray+index));
pCmd+=u8Temp; len-=u8Temp;
pCmd+=1; len-=1;
index++;
}
return TRUE;
}
当我把数据放到这三个变量mov_sta,move_mode,step
里面之后就可以进行最终的细数据处理啦~
static void DebugCmdProceed(void)
{
switch(mov_sta){
case STOP:
// printf("I'm stop\n");
MOV_STA = STOP;
speed=0;
break;
case RUN:
//printf("start running\n");
MOV_STA = RUN;
cross_sta=mid_cnt;
speed=0;
break;
case LEFT:
// printf("start turning left\n");
MOV_STA = LEFT ;
cross_sta=mid_cnt;
speed =0;
break;
case RIGHT:
// printf("start turning right\n");
MOV_STA = RIGHT ;
cross_sta=mid_cnt;
speed =0;
break;
case BACK:
// printf("start going back\n");
MOV_STA = BACK ;
cross_sta=mid_cnt;
speed =0;
break;
case GET_WALL:
//printf("start running to wall\n");
MOV_STA = GET_WALL ;
cross_sta=mid_cnt;
speed =0;
break;
}
switch(move_mode){
case MODE_A:
MOV_MODE = MODE_A;
//printf(",MODE : A");
break;
case MODE_B:
MOV_MODE = MODE_B;
//printf(",MODE : B");
break;
}
printf("cross_sta is %d\n",cross_sta);
switch(step){
case 0:
STEP = 0;
//printf(" STEP: half ");
break;
case 1:
STEP = 1;
// printf(" STEP:1 ");
break;
case 2:
STEP = 2;
// printf(" STEP:2 ");
break;
case 3:
STEP = 3;
//printf(" STEP:3");
break;
case 4:
STEP = 4;
// printf(" STEP:4 ");
break;
case 5:
STEP = 5;
//printf(" STEP:5 ");
break;
}
}
(づ ̄3 ̄)づ╭❤~一键三连,这次一定(๑•̀ㅂ•́)و✧