让串口调试助手像命令行一样
说到嵌入式的实时系统,大家首先想到的是不是 闻名遐迩的 ucos 呢? ucos 简单,易移植,在关键部分移植汇编语言,是入坑 rtos [1]的不二选择.接触了一段时间之后,偶然在网上看到,原子哥(正点原子) 的开源教材里头也有介绍 rt-thread os 系统,然后开始了解和使用rt-thread. 它的优势是:商用无风险,不限任务个数,各种协议栈齐全(尤其在 iot方面),兼容部分posix接口(linux接口).
rt-thread 是一个中国人自己开发的开源的rtos ,借鉴了linux的部分想法.甚至可以复用linux的驱动接口.同时高度可裁剪和丰富的协议栈.当然现在的菜鸟小编都用不到这些. 可是有一个功能小编可是眼红的很,那就是它的 串口 shell 功能,让单片机跟电脑的命令行一样执行命令.
用过gcc 或者java 的小伙伴都知道,可以直接在命令行敲 gcc *.c 或者javac *.java 就可以直接编译文件, 这个 gcc javac 就是咱们的*.exe 文件,那么在单片机里,我定义了一个 printcircle (int r);这个函数,那么我们可不可以直接在串口助手里给单片机发信息: printcircle 30 \n 是不是他就可以执行printcirc这个函数并且r = 30 呢? 答案当然是可以的.
rt-thread 中的shell 就可以做到.并且更加完善.但是单片机支持这样的命令行操作,可是咱们的串口助手并不能像cmd窗口一样啊.著名的xcom 串口调试软件,不支持这个功能.于是我萌生了做一个上位机适配这个shell 的想法.
我的想法很简单,就是做一个窗口尽量让他像我们的命令行. 但是串口还是需要扫描,检查串口.所以我在最下面增加了几个按钮.同时尽量关联快捷键.尽量做到少操作鼠标.经过几天的打磨,思考,现在已经有点样子了.有需要的同学可以移步:
github: https://github.com/kimalittlestar/uart_shell
程序下载: https://github.com/kimalittlestar/uart_shell/releases
百度云: https://pan.baidu.com/s/12soblpsm5cuieg1eezep1q 提取码: hyhn
软件说明:按下回车就是发送消息.方向上键是发送消息的历史记录.有以下几个bug:
-
按方向下键不能看到下一条历史记录.
-
现在暂时不能发送 带有\n 的字符串,也不能发送 hex
-
如果光标在字符中间的时候按下了回车,那么接收到的消息是会从光标处开始插入
发送字符给单片机的时候,是发送最后一行的字符.文本太长会自动换行显示,但是仍然是一行.
以上的bug我还在修,因为现在是在实习工作,可能修bug 的速度会慢一些.ps:win10/win7 64bit 实测可用.
接下来是里面的一些具体的逻辑想法:
首先ui界面布局,这个不赘述,需要注意的就是使用*.ui文件的时候注意将你需要用到的对象的名称修改掉,不然你可能过了几天,你就不记得你的变量名那个是哪个了.没错我就经历过.
之后 开启串口.
void widget::openprot()
{
if(activeport->isopen()) //如果串口打开着就关闭
{
// activeport->clear();
activeport->close();
ui->pushbutton_2->settext("打开串口");
ui->tline_message->settext("串口"+ui->cbox_portlist->currenttext()+"已关闭");
}else{ //关闭着就打开
activeport->setportname(ui->cbox_portlist->currenttext());
if(activeport->open(qiodevice::readwrite)== true)
{
int a = ui->cbox_baudlist->currenttext().toint();
activeport->setbaudrate(a,qserialport::alldirections);//设置波特率和读写方向
activeport->setdatabits(qserialport::data8); //数据位为8位
activeport->setflowcontrol(qserialport::noflowcontrol);//无流控制
activeport->setparity(qserialport::noparity); //无校验位
activeport->setstopbits(qserialport::onestop); //一位停止位
ui->tline_message->settext(ui->cbox_portlist->currenttext()+"打开成功");
connect(activeport,signal(readyread()),this,slot(receiveinfo()));
ui->pushbutton_2->settext("关闭串口");
ui->tedit_shell->setfocus();
}else {
ui->tline_message->settext("打开失败,检查串口是否被占用");
ui->pushbutton_2->settext("打开串口");
}
}
}
监听按键,如果按键按到了 方向上 下 和enter键那么就执行相应的操作.同时我 也预留了一个 tab 键,为以后自定义关键字,做到字符按提示.
1 if (static_cast<qkeyevent *>(event)->key() == qt::key_up) { 2 qtextcursor tc = ui->tedit_shell->textcursor(); 3 4 if(history.size() == 0) return true;//如果没有历史记录,则不操作 5 6 if(historynum != history.size())//如果当前不是第一次按上键,则要删除上一次补全的历史记录 7 { 8 if(historynum == 0) 9 tc.moveposition(qtextcursor::left,qtextcursor::keepanchor,history[0].length()); 10 else { 11 tc.moveposition(qtextcursor::left,qtextcursor::keepanchor,history[historynum].length()); 12 } 13 tc.removeselectedtext(); 14 } 15 if(historynum == 0) 16 historynum = history.size(); 17 18 ui->tedit_shell->insertplaintext(history[historynum-1]); //插入上一个输入的历史记录 19 qout<<history[historynum-1]<<"history size "<< history.length(); 20 historynum--; 21 qout<<"使用上一个记录"<<endl; 22 23 return true;
如果按下了enter 把最后一行的文本获取到,用串口发送出去,将发出去的字符写入一个数组,记录下来以便提供历史记录
1 if(k->key() == qt::key_return || k->key() == qt::key_enter) 2 { 3 txtcur.moveposition(qtextcursor::end,qtextcursor::moveanchor); 4 txtcur.moveposition(qtextcursor::startofline,qtextcursor::moveanchor); 5 txtcur.moveposition(qtextcursor::endofline,qtextcursor::keepanchor); 6 qstring msg = txtcur.selectedtext(); 7 history.append(msg); 8 sendtext(msg); 9 10 historynum = history.size(); 11 ui->tedit_shell->append(""); 12 return true;
如果按下了方向上键,不上移光标,然后判断是不是最后一个的输入记录,如果是,直接在光标位置处插入上一次发送的文本,如果不是最后一个历史记录,删除当前历史的历史记录语句,插入更加前面一条历史语句.
如果删除时发现已经删除到最后一行的开始,则删除无效.
1 if (static_cast<qkeyevent *>(event)->key() == qt::key_backspace) { 2 qtextcursor tc = ui->tedit_shell->textcursor(); 3 int oldposition = tc.position(); 4 tc.moveposition(qtextcursor::startofline); 5 if((tc.blocknumber()+1) == maxline && oldposition == tc.position()) 6 { 7 return true; 8 }
恩,逻辑上还是比较简单的.每个人都可以做到.
然而,为了能用上好(装)用(逼)的 shell 串口,我们就要去移植一个rt-thread系统吗? no no no.小编也想到这个问题,于是乎,自己编写了一个.c.h 文件,只需一个声明,输入字符串进入执行函数,就会自动调用你想要调用的函数.具体请期待明日更新哦.
rtos [1] real time operating system 实时操作系统,. 名称是根据操作系统的工作特性而言的,实时是指物理进程的真实时间. 摘自百度百科 [rtos] 且rtos多为抢占式操作系统.
为了能够更好的交流,小编悄摸的申请了一个公众号: 奔跑的程序猿 ,感兴趣的可以关注一波.