该文章是针对于串口通讯过程中快速定义命令而写的,算是我自己的一个通用化的平台,专门用来进行串口调试用,莫要取笑

要处理串口数据首先是要对单片机的串口中断进行处理,我的方法是正确的命令必须要在命令的结尾处同时带有回车和换行,处理过程如下

//串口接收缓冲区
u8 serial_Buffer[SERIAL_MAX_LENGTH] = {};
//串口接收数据长度
u16 serial_Buffer_Length = ; static void SerialRecv(u8 ch)
{
if((serial_Buffer_Length&0x8000) == 0x8000)//已经接收完成,系统还没处理
{
serial_Buffer_Length |= 0x8000;//退出
}
else if((serial_Buffer_Length&0x4000) == 0x4000)//接收到回车还没接收到换行
{
if(ch == '\n')serial_Buffer_Length |= 0x8000;
else
{
//一帧接受失败
serial_Buffer_Length = ;
}
}
else
{
if((serial_Buffer_Length&0xff) < SERIAL_MAX_LENGTH)
{
if(ch == '\r')serial_Buffer_Length |= 0x4000;
else
{
serial_Buffer[(serial_Buffer_Length&0xff)] = ch;
serial_Buffer_Length++;
}
}
else
{
//一帧接受失败
serial_Buffer_Length = ;
}
}
} void USART1_IRQHandler(void)
{
u8 ch = ;
if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)//检查中断发生
{
ch = (u8)USART_ReceiveData(USART1);
USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除中断
// Debug_Serial_Send_Char(ch); //将收到的数据发送出去
SerialRecv(ch); //处理中断数据
}
}

这一帧数据接收到了之后就会阻塞串口,不再接受新数据,这时候我们就要定时的调用命令处理任务,将接收到的数据提取出来,如下

//扫描命令字符串,并调用相应处理函数
void CommandScan(void)
{
u8 commandLength1;
u8 commandLength2;
u8 i = ,j = ;
//数据满
if((serial_Buffer_Length & 0x8000) == 0x8000)
{
//检测命令不是全为空格
if(Command_Is_Vailed())
{
Command_Copy();//copy命令字符串等待处理
//去除命令头上的空白
Command_Remove_Space_Head();
//去除命令尾巴上的空格
Command_Remove_Space_End();
//去除中间的重复空格
Command_Remove_Space_Inner();
commandLength1 = Command_Find_Space_Postion();//获取长度
if(commandLength1 == )commandLength1 = commandStringLength;//当第二个空格获取返回0的时候,说明没有参数,纯命令,所以没有空格
for(i = ; i < COMMAND_NUM; i++)
{
commandLength2 = StringGetLength(commandStringList[i]);
if(commandLength1 == commandLength2)
{
//长度相同,比对每个字符
for(j = ; j < commandLength1; j++)
{
if(commandStringBuffer[j] == commandStringList[i][j])continue;
else break;
}
if(j == commandLength1)//比对成功
{
//调用函数
Command_Proc_Func_Table[i]();
return;
}
}
else
{
//直接长度不同,不需要比对了
continue;
}
}
if(i == COMMAND_NUM)
{
//没找到对应命令
printf("not find command\r\n");
} }
else
{
printf("command can't all space\r\n");
serial_Buffer_Length = ;
} }
}

先去除发送过来的数据头尾的空格,然后去除中间的空格,这样就能保证一定的数据纠错能力去除空白的代码段如下

//去除命令字符串的前面的空格字符
void Command_Remove_Space_Head(void)
{
u8 index = ;
u8 i = ;
for(index = ; index < commandStringLength; index++)
{
if(commandStringBuffer[index] == ' ')continue;
else break;
}
if(index == )//前面没有空格
{
return;
}
else
{
//删除空格
for(i = ; i < (commandStringLength-index);i++)
{
commandStringBuffer[i] = commandStringBuffer[index+i];
}
commandStringLength -= index;
}
}
//去除命令字符串后面的空格
void Command_Remove_Space_End(void)
{
u8 i = ;
//寻找字符串最尾巴上空格的位置
for(i = commandStringLength; i > ; i--)
{
if(commandStringBuffer[i-] == ' ')continue;//如果这个是空格,继续下一次寻找
else break;//不是空格,到此为止
}
if(i == commandStringLength)//尾上没有空格
{
return;
}
else //尾上有空格
{
commandStringBuffer[i] = '\0';
commandStringLength = i;
return;
} }
//去除命令字符串中间的空格,将连续两个的空格合并成一个
void Command_Remove_Space_Inner(void)
{
u8 spaceCount;
u8 i = ;
u8 j = ;
for(i = ; i < commandStringLength; i++)
{
//此时检测到一个空格
if(commandStringBuffer[i] == ' ')
{
//立刻查看下一个是不是空格
if(commandStringBuffer[i+] == ' ')
{
spaceCount = ;
//下一个也是空格,此时说明已经有了两个连续的空格了必须立刻查找到结束的空格在哪
for(j = i+; j < commandStringLength; j++)
{
//当不是空格的时候跳出来,是空格就一直加
if(commandStringBuffer[j] == ' ')spaceCount++;
else break;
}
//跳出来根据space的值来移动数组,同时减小长度
//i是第一个空格,i+1是第二个空格,最后一个空格是spaceCount-2
for(j = i+;j < commandStringLength-spaceCount+;j++)
{
//要跳过spacecount-1的数量,来拷贝有效字符
commandStringBuffer[j] = commandStringBuffer[j+spaceCount-];
}
//最后修改长度,长度缩减量是空格数-1,因为保留了一个空格
commandStringLength -= (spaceCount-);
}
else
{
//下一个不是空格,说明是只有一个空格的环境,不用操心,进行下一次循环
continue;
}
} }
}

去除空格之后,可能这一次的命令是带参数的,那么我们去除第一个连续的字符串当成命令,所以这个特性就规定了命令本身(不包括参数)必须是连续的字符串,取出命令的函数如下

commandLength1 = Command_Find_Space_Postion();//获取长度
if(commandLength1 == )commandLength1 = commandStringLength;//当第二个空格获取返回0的时候,说明没有参数,纯命令,所以没有空格

获取命令中第一个空格的位置,那就是命令字符串的结尾,接下来需要和命令数组进行比对,命令数组如下

//命令列表,命令最长50字节
u8 commandStringList[][] = \
{
"help",\
"list",\
"iap_down",\
"iap_jump_app",\
"iap_over",\
"iap_set_flag",\
"iap_clear_flag"
};

每个命令最大不超过49个字节,命令个数可以由实际情况编译器自行处理,当比对成功之后,自动的按照命令在命令序列中的序列号调用相应的函数,这里使用指针回调机制,如下

//回调函数数组定义
Command_Proc_Func Command_Proc_Func_Table[] =
{
Help_Proc_Func,
List_Proc_Func,
iap_down_s,
iap_jump_app_s,
iap_over_s,
iap_set_flag_s,
iap_clear_flag
};
typedef void (*Command_Proc_Func)(void);

extern u8 commandStringList[][] ;

extern Command_Proc_Func Command_Proc_Func_Table[];

这就要求命令和命令响应函数在数组中的位置必须是对应的,这样就能实现一个简单的shell了,为了解析参数,我做了几个函数可以解析十进制和十六进制的数据,如下

/*******************************字符串参数转换接口***********************/

//将十进制格式的字符串参数转换为数值,返回8位无符号整形
//value 最终转换值指针
//index 指示第几个参数,第一个参数为1 ......
//返回值 转换是否成功,失败返回1 成功返回0
u8 CommandGetParamToDecU8(u8* value,u8 index)
{
u8 result;
u32 valueResult = ;
u8 i = ;
u32 fac = ;
result = CommandGetParamStr(paramBuffer,PARAM_COVERT_MAX_LENGTH,&paramLength,index);
if(result == )return ;//找不到这么多参数
//找到之后根据长度计算系数
//系数计算
for(i = ; i < paramLength;i++)
{
fac *= ;
}
//校验每个参数字符值是否符合标准,十进制就必须在0-9之间
for(i = ;i<paramLength;i++)
{
if(paramBuffer[i] > '' || paramBuffer[i] < '')
{
return ;//参数错误
}
}
//开始计算
for(i = ; i < paramLength;i++)
{
valueResult += (paramBuffer[i]-'')*fac;
fac/=;
}
//检测最终结果是否大于限制值,如八位那么结果不能大于255
if(valueResult > 0xff)return ;//参数错误
else
{
*value = (u8)valueResult;
return ;
}
} //与上一个参数类似,检测十六进制参数
u8 CommandGetParamToDecU16(u16* value,u8 index)
{
u8 result;
u32 valueResult = ;
u8 i = ;
u32 fac = ;
result = CommandGetParamStr(paramBuffer,PARAM_COVERT_MAX_LENGTH,&paramLength,index);
if(result == )return ;//找不到这么多参数
//找到之后根据长度计算系数
//系数计算
for(i = ; i < paramLength;i++)
{
fac *= ;
}
//校验每个参数字符值是否符合标准,十进制就必须在0-9之间
for(i = ;i<paramLength;i++)
{
if(paramBuffer[i] > '' || paramBuffer[i] < '')
{
return ;//参数错误
}
}
//开始计算
for(i = ; i < paramLength;i++)
{
valueResult += (paramBuffer[i]-'')*fac;
fac/=;
}
//检测最终结果是否大于限制值,如八位那么结果不能大于255
if(valueResult > 0xffff)return ;//参数错误
else
{
*value = (u16)valueResult;
return ;
}
} u8 CommandGetParamToDecU32(u32* value,u8 index)
{
u8 result;
u32 valueResult = ;
u8 i = ;
u32 fac = ;
result = CommandGetParamStr(paramBuffer,PARAM_COVERT_MAX_LENGTH,&paramLength,index);
if(result == )return ;//找不到这么多参数
//找到之后根据长度计算系数
//系数计算
for(i = ; i < paramLength;i++)
{
fac *= ;
}
//校验每个参数字符值是否符合标准,十进制就必须在0-9之间
for(i = ;i<paramLength;i++)
{
if(paramBuffer[i] > '' || paramBuffer[i] < '')
{
return ;//参数错误
}
}
//开始计算
for(i = ; i < paramLength;i++)
{
valueResult += (paramBuffer[i]-'')*fac;
fac/=;
}
//检测最终结果是否大于限制值,如八位那么结果不能大于255
if(valueResult > 0xffffffff)return ;//参数错误
else
{
*value = (u32)valueResult;
return ;
}
} //从命令字符串中获取参数并转换参数,将0x格式的字符串转换为数值 成功返回0 失败返回1
//参数类型必须是0x开头的
u8 CommandGetParamToHexU8(u8* value,u8 index)
{
u8 result;
u32 valueResult = ;
u8 i = ;
u32 fac = ;
result = CommandGetParamStr(paramBuffer,PARAM_COVERT_MAX_LENGTH,&paramLength,index);
if(result == )return ;//找不到这么多参数
//检测参数长度,因为开头必须为0x,所以长度必须为3以上
if(paramLength <= )return ;//失败
//计算系数
for(i = ; i < paramLength; i++)
{
fac *= ; //因为0x占用了两个字节 第一未为1 所以乘法运算从第三个开始
}
//检测开头是否正确 0x
if(paramBuffer[] == '' &&(paramBuffer[] == 'x'||paramBuffer[] == 'X'))
{
//检测每一位数据是否正确并计算最终值
for(i = ; i < paramLength; i++)
{
if(paramBuffer[i] >= '' && paramBuffer[i] <= '')
{
result = paramBuffer[i] -'';
}
else if(paramBuffer[i] >= 'a' && paramBuffer[i] <= 'f')
{
result = paramBuffer[i] -'a'+;
}
else if(paramBuffer[i] >= 'A' && paramBuffer[i] <= 'F')
{
result = paramBuffer[i] -'A'+;
}
else
{
//出现范围之外的数据,返回1
return ;
}
valueResult += (u32)(result*fac);
fac /= ;
}
//计算完成,检测参数是否超过范围
if(valueResult > 0xff)return ;//参数错误
else
{
*value = (u8)valueResult;
return ;
}
}
else
{
//参数开头不对
return ;
}
} //从命令字符串中获取参数并转换参数,将0x格式的字符串转换为数值 成功返回0 失败返回1
u8 CommandGetParamToHexU16(u16* value,u8 index)
{
u8 result;
u32 valueResult = ;
u8 i = ;
u32 fac = ;
result = CommandGetParamStr(paramBuffer,PARAM_COVERT_MAX_LENGTH,&paramLength,index);
if(result == )return ;//找不到这么多参数
//检测参数长度,因为开头必须为0x,所以长度必须为3以上
if(paramLength <= )return ;//失败
//计算系数
for(i = ; i < paramLength; i++)
{
fac *= ;//因为0x占用了两个字节 第一未为1 所以乘法运算从第三个开始
}
//检测开头是否正确 0x
if(paramBuffer[] == '' &&(paramBuffer[] == 'x'||paramBuffer[] == 'X'))
{
//检测每一位数据是否正确并计算最终值
for(i = ; i < paramLength; i++)
{
if(paramBuffer[i] >= '' && paramBuffer[i] <= '')
{
result = paramBuffer[i] -'';
}
else if(paramBuffer[i] >= 'a' && paramBuffer[i] <= 'f')
{
result = paramBuffer[i] -'a'+;
}
else if(paramBuffer[i] >= 'A' && paramBuffer[i] <= 'F')
{
result = paramBuffer[i] -'A'+;
}
else
{
//出现范围之外的数据,返回1
return ;
}
valueResult += (u32)(result*fac);
fac /= ;
}
//计算完成,检测参数是否超过范围
if(valueResult > 0xffff)return ;//参数错误
else
{
*value = (u16)valueResult;
return ;
}
}
else
{
//参数开头不对
return ;
}
} //从命令字符串中获取参数并转换参数,将0x格式的字符串转换为数值 成功返回0 失败返回1
u8 CommandGetParamToHexU32(u32* value,u8 index)
{
u8 result;
u32 valueResult = ;
u8 i = ;
u32 fac = ;
result = CommandGetParamStr(paramBuffer,PARAM_COVERT_MAX_LENGTH,&paramLength,index);
if(result == )return ;//找不到这么多参数
//检测参数长度,因为开头必须为0x,所以长度必须为3以上
if(paramLength <= )return ;//失败
//计算系数
for(i = ; i < paramLength; i++)
{
fac *= ;//因为0x占用了两个字节 第一未为1 所以乘法运算从第三个开始
}
//检测开头是否正确 0x
if(paramBuffer[] == '' &&(paramBuffer[] == 'x'||paramBuffer[] == 'X'))
{
//检测每一位数据是否正确并计算最终值
for(i = ; i < paramLength; i++)
{
if(paramBuffer[i] >= '' && paramBuffer[i] <= '')
{
result = paramBuffer[i] -'';
}
else if(paramBuffer[i] >= 'a' && paramBuffer[i] <= 'f')
{
result = paramBuffer[i] -'a'+;
}
else if(paramBuffer[i] >= 'A' && paramBuffer[i] <= 'F')
{
result = paramBuffer[i] -'A'+;
}
else
{
//出现范围之外的数据,返回1
return ;
}
valueResult += (u32)(result*fac);
fac /= ;
}
//计算完成,检测参数是否超过范围
if(valueResult > 0xffffffff)return ;//参数错误
else
{
*value = (u32)valueResult;
return ;
}
}
else
{
//参数开头不对
return ;
}
}

恩,到这里基本就实现这个shell了,现在增加命令以及增加命令响应函数是不是就简单多了,反正我是觉得简单多了

项目demo位置

http://download.csdn.net/detail/dengrengong/8542891

这个项目里面使用了shell和之前使用的动态内存,可以参考

单片机裸机下写一个自己的shell调试器的更多相关文章

  1. 单片机裸机下写一个自己的shell调试器(转)

    源: 单片机裸机下写一个自己的shell调试器

  2. 如何写一个简单的shell

    如何写一个简单的shell 看完<UNIX环境高级编程>后我就一直想写一个简单的shell来作为练习,因为有事断断续续的写了好几个月,如今写了差不多来总结一下. 源代码放在了Github: ...

  3. a,b,c为3个整型变量,在不引入第四个变量的前提下写一个算法实现 a=b b=c c=a?(异或解决值互换问题)

    package com.Summer_0424.cn; /** * @author Summer * a,b,c为3个整型变量,在不引入第四个变量的前提下写一个算法实现 a=b b=c c=a? */ ...

  4. a,b为2个整型变量,在不引入第三个变量的前提下写一个算法实现 a与b的值互换

    package com.Summer_0424.cn; /** * @author Summer * a,b为2个整型变量,在不引入第三个变量的前提下写一个算法实现 a与b的值互换? */ publi ...

  5. Ubuntu16.04下写的Qt程序,调试时没问题,运行时偶现崩溃 (需要在运行时生成core dump文件,QMAKE_CC += -g)

    记录一下 Ubuntu16.04下写的Qt程序,调试时没问题,运行时偶现崩溃 需要在运行时生成core dump文件 首先在pro结尾里加入 QMAKE_CC += -g QMAKE_CXX += - ...

  6. 使用CLRMD编写一个自己的C#调试器

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:使用CLRMD编写一个自己的C#调试器.

  7. 在Linux下写一个简单的驱动程序

    本文首先描述了一个可以实际测试运行的驱动实例,然后由此去讨论Linux下驱动模板的要素,以及Linux上应用程序到驱动的执行过程.相信这样由浅入深.由具体实例到抽象理论的描述更容易初学者入手Linux ...

  8. 【转】在Linux下写一个简单的驱动程序

    转自:https://www.cnblogs.com/amanlikethis/p/4914510.html 本文首先描述了一个可以实际测试运行的驱动实例,然后由此去讨论Linux下驱动模板的要素,以 ...

  9. UNIX-LINUX编程实践教程->第八章->实例代码注解->写一个简单的shell

    一 分析 要实现一个shell,需包含3个步骤 1)读入指令 2)指令解析 3)执行指令 1 从键盘读入指令 从键盘读入指令的几个要点: 1)调用getc函数等待并获取用户键盘输入. 2)每一行命令的 ...

随机推荐

  1. win10怎么启用网络发现,网络发现已关闭怎么办

    脑和电脑之间传输文件的方式很多,其中一种就是使用局域网,在网络中我们的电脑应该可以被其他电脑发现是非常方便使用文件共享的,尤其是在使用家庭组网络的时候,那么win10里面怎么启用网络发现呢? 工具/原 ...

  2. Spring中实现监听的方法

    在未使用框架进行编程的时候,我们常常在web.xml中加上这样一段话 <listener> <listener-class>XXX</listener-class> ...

  3. oracle_一次移动数据库dbf文件的操作

    oracle数据库的dbf路径下面磁盘不足,需要把原始路径下面的dbf文件移动到另外一个磁盘路径下, 具体的操作有四步. 1.把整个表空间offline. 2.copy原始路径下的dbf文件到新的路径 ...

  4. sql 语句操作

    插入:insert into table1(field1,field2) values(value1,value2) db.execSQL(sql) db.execSQL(sql, bindArgs) ...

  5. C# 初步学习

    这学期有了C#开发这门课....先做了计算器,还有进制转换,别人看来似乎很强,其实在ACM中算是两个简单的水题了.....参加竞赛一年下来,发现学到的算法和数据结构都是十分有用的东西. 计算器最核心的 ...

  6. Android Studio的使用(六)

    本篇博文介绍如何更新Android Studio. 1.导航栏的Help下拉框可以找到更新的按钮. 2.接下来点击Update and Restart即可 3.有些人说网连不上,但我的就可以,不行的话 ...

  7. MYSQL启用日志,查看日志,利用mysqlbinlog工具恢复MySQL数据库【转载】

    转自 MYSQL启用日志,查看日志,利用mysqlbinlog工具恢复MySQL数据库 - _安静 - 博客园http://www.cnblogs.com/xionghui/archive/2012/ ...

  8. CentOS6.5 安装ntopng-1.2.0

    0.准备工作 安装libpcap:最好源码安装. yum install -y libpcap* 安装redis yum install -y redis* 1.安装 tar -zxvf ntopng ...

  9. mr本地运行的几种模式

    MR程序的几种提交运行模式 本地模型运行 1/在windows的eclipse里面直接运行main方法,就会将job提交给本地执行器localjobrunner执行 ----输入输出数据可以放在本地路 ...

  10. POJ 2991 Crane

    线段树+计算几何,区间更新,区间求和,向量旋转. /* *********************************************** Author :Zhou Zhentao Ema ...