“命令终端”的实现1-准备篇
李迟注:
这几篇文章写于2012年底,因故未发表,前不久,音视频群里的树哥询问一个技术方案,想到以前曾经实现过,就把工程发给他。现在发表出来,除修正个别严重的病句外,其它没有修改。从行文看,还有很大提高水平,但文中的编码风格却一直保持着。
本系列文章将完成一个类似DOS或Linux或busybox或u-boot的命令终端。题目的“命令终端”之所以加引号,一来表示它不是真正意义上的终端,二来也可以说明并非自己一字一字写出来的代码。——本程序所用的原型来自u-boot2010.09,这个版本陪了我很久,使我一直不能忘怀。如今重拾代码,也了却心头所念。
所谓工欲善其事,必先利其器,本文便是该工程的前期准备。包括如下内容:检测按键,接收终端字符,将字符发送到终端,printf函数的实现,等等。
以下分别给出与“终端”交互的各个函数。由于u-boot面向的是串口终端,而自己的实现的程序是操作系统中的终端。所以下面先介绍了u-boot中的实现(以SMDK2440为例),再介绍自己的实现。
一、检测按键
u-boot 的实现如下(注:已作了修改,下同):
int tstc(void)
{return serial_tstc();
}int serial_tstc()
{struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);return readl(&uart->UTRSTAT) & 0x1;
}
可以看到,这个实现最终是读取串口的状态寄存的某个位。具体可以查看芯片手册。
自己的实现如下:
/* implement of getch() */
#ifdef WIN32
#include <conio.h>/** * return non-zero if a key pressed, zero if not.**/
int mytstc(void)
{return kbhit();
}#else#include <termios.h> /* for tcxxxattr, ECHO, etc */
#include <unistd.h> /* for STDIN_FILENO */
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>int mytstc(void)
{struct timeval tv;fd_set rdfs;int ch;struct termios oldt, newt;// get terminal input's attribute tcgetattr(STDIN_FILENO, &oldt);newt = oldt;//set termios' local mode newt.c_lflag &= ~(ECHO|ICANON);tcsetattr(STDIN_FILENO, TCSANOW, &newt);tv.tv_sec = 0;tv.tv_usec = 100;FD_ZERO(&rdfs);FD_SET (STDIN_FILENO, &rdfs);select(STDIN_FILENO+1, &rdfs, NULL, NULL, &tv);ch = FD_ISSET(STDIN_FILENO, &rdfs);//recover terminal's attribute tcsetattr(STDIN_FILENO, TCSANOW, &oldt);return ch;
}
二、接收终端字符
u-boot实现如下:
int getc(void)
{return serial_getc();
}
int serial_getc()
{struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);while (!(readl(&uart->UTRSTAT) & 0x1))/* wait for character to arrive */ ;return readb(&uart->URXH) & 0xff;
}
原理很简单,就是判断接收缓冲区是否有数据,没有的话就一直等,否则就返回数据接收寄存中的值。
在Windows中有getch可用,但Linux无此函数,在curses库中倒是有一个,但这里没必要使用这个库,网上有相应的实现函数,这里按“拿来主义”,为保其完整性,连注释也不修改。如下:
/* implement of getch() */
#ifdef WIN32
#include <conio.h>int mygetc(void)
{return getch();
}#else#include <termios.h> /* for tcxxxattr, ECHO, etc */
#include <unistd.h> /* for STDIN_FILENO *//*simulate windows' getch(), it works!!*/
int mygetc(void)
{int ch;struct termios oldt, newt;// get terminal input's attribute tcgetattr(STDIN_FILENO, &oldt);newt = oldt;//set termios' local mode newt.c_lflag &= ~(ECHO|ICANON);tcsetattr(STDIN_FILENO, TCSANOW, &newt);//read character from terminal input ch = getchar();//recover terminal's attribute tcsetattr(STDIN_FILENO, TCSANOW, &oldt);return ch;
}
#endif
三、发送字符到终端
u-boot的实现如下:
void putc(const char c)
{serial_putc(c);
}void serial_putc(const char c, const int dev_index)
{struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);while (!(readl(&uart->UTRSTAT) & 0x2))/* wait for room in the tx FIFO */ ;writeb(c, &uart->UTXH);/* If \n, also do \r */if (c == '\n')serial_putc('\r');
}
上面提到的是发送单个字符,发送字符串就循环调用该函数,如下:
void serial_puts(const char * s)
{while (*s) {serial_putc(*s++);}
}
标准C库里面有putchar函数,直接使用之,如下:
void myputc(const char c)
{if (c == '\n')myputc('\r');putchar(c);
}void myputs(const char *s)
{while (*s)myputc(*s++);
}
四、printf实现
printf的实现主要调用vsprintf,该函数实现如下:
int myprintf(const char *fmt, ...)
{va_list args;int i;char printbuffer[512];va_start(args, fmt);/* For this to work, printbuffer must be larger than* anything we ever want to print.*/i = myvsprintf(printbuffer, fmt, args);va_end(args);/* Print the string */myputs(printbuffer);return i;
}/**简单版本仅支持:%d %x %s %c
*/
int myvsprintf1(char *buf, const char *fmt, va_list args)
{char* s = NULL;char* p = NULL;int d = 0;int len = 0;int i = 0;s = buf;for (; *fmt; ++fmt){if (*fmt != '%'){// string within there *s++ = *fmt;continue; }++fmt;// no flags // no field width // no precision // no length // specifier switch (*fmt){case 's':p = va_arg(args, char*);break;case 'x':d = va_arg(args, int);p = myitoa(d, 16);while (*p)*s++ = *p++;break;case 'd':d = va_arg(args, int);p = myitoa(d, 10);while (*p)*s++ = *p++;break;case 'c':d = va_arg(args, int); // cannot be 'char' *s++ = d;break;default:*s++ = *fmt;break;}}*s = '\0';return (int)(s - buf);
}
其中myitoa函数是itoa的实现,可参考笔者前段时间写的文章。vsprintf函数如其注释所示,支持的格式化十分有限。当然,网上已经有人实现了功能十分强大的vsprintf,可参阅文后链接。
注:
关于串口的操作,大部分芯片原理是相似的,具体到某款芯片,只要按照其数据手册中寄存器说明来编写代码就OK了。笔者就是参考u-boot的代码,将这一套“终端”应用于某芯片平台上的。
参考资料:
http://blog.csdn.net/summerhust/article/details/6615785
http://www.jbox.dk/sanos/source/lib/vsprintf.c.html
李迟 2020.9.30
“命令终端”的实现1-准备篇相关推荐
- Ubuntu命令终端查看使用过的命令
使用history命令 cyf@ubuntu:~$ history 但是这样会显示出所有使用过的命令,可以在history后加上less cyf@ubuntu:~$ history | less 会显 ...
- Win7命令终端基础配色指南
微软对控制台字体的元数据有严格的限制,https://support.microsoft.com/zh-cn/help/247815/necessary-criteria-for-fonts-to-b ...
- Windows 如何在命令终端(CMD)使用命令来访问本地/远程的 Oracle 数据库呢?
打开命令窗口后直接输入 sqlplus sys/123@orcl as sysdba 其中 sys 是用户名,123 是密码,orcl 是数据库实例名,as sysdba 表示用户 sys 是数据库管 ...
- Windows 如何用命令终端(CMD)启动和停止 MySQL 数据库服务
当安装完 MySql 后,默认每次 Windows 启动的时候都会将 MySql 服务启动起来.那么如何通过命令方式来启动和停止 MySQL 服务呢? Windows XP 如果是 Windows X ...
- Linux 的命令终端(CMD)的快捷键(Keyboard of MacBook)
文章目录 常用 移动光标 编辑命令 查找历史命令 控制命令 命令终端界面滚屏 命令终端页签切换 奇葩 常用 快捷键 说明 Ctrl + A 光标跳到本行的行首 Ctrl + E 光标跳到本行的行尾 C ...
- Windows 命令终端(CMD)的快捷键
这些快捷键只在Windows系统操作有效,连接远程Linux主机,再操作这些快捷键是无效的,因为连接远程的Linux主机后,你用的是Linux命令终端. 快捷键: F1:按F1逐字显示最后一次执行的命 ...
- 如何通过命令终端访问本地/局域网/远程的MySQL数据库_访问数据库_连接数据库_登录数据库
文章目录 Windows系统下 访问本地MySQL数据库 访问远程主机的MySQL数据库 本地安装了MySQL数据库 本地没有安装MySQL Linux系统下 退出数据库登录 Windows系统下 访 ...
- Debian11镜像更新为阿里巴巴开源镜像站镜像,切换root用户,解决用户名不在sudoers文件中此事将被报告,Debian11 文件夹对话框、火狐浏览器、命令终端等没有最大化和最小化
选择Debian作为编程开发最佳Linux的理由: Debian是面向程序员的最古老,最出色的Linux发行版之一.Debian提供了具有.deb软件包管理兼容性的超稳定发行版.Debian为程序员提 ...
- “命令终端”的实现4-优化之解耦
这段时间一直在做测试的工程(不是测试的工作),为了应付不同的测试场景,代码使用了解释器风格,至于实现,则使用了多年前写的命令终端代码.那会刚毕业不久,写的代码还是有提升空间.现在重新拾起,打破一般认知 ...
最新文章
- 你不知道的对称密钥与非对称密钥
- 数字图象处理之二维码图像提取算法(九)
- mac14.5 mojave安装错误
- Qt中应用程序的打包与发布
- 预处理指令的开始和结束
- opencv进阶学习笔记11:cannny边缘检测,直线检测,圆检测
- Mac OS X下64位汇编与Linux下64位汇编的一些不同
- 我们的实践: 400万全行业动态事理图谱Demo
- python语言中包含的标准数据类型_Python对象——标准类型的分类
- 控制台程序转化为windows服务
- ViBe(Visual Background extractor)背景建模或前景检测
- 白帽SQL注入实战过程记录(2)——根据information_schema组装SQL注入语句
- 关于团队项目的一些思考和理解
- java简单代码练习
- Android--使用开源vitamio做万能视频播放器
- 计算机用户密码查看器,电脑开机密码查看工具
- PackageManager hasSystemFeature
- 不怕崩溃 Ghost令机房管理化繁为简
- jsp:使用request为页面添加静态数据。
- 低腰产品的软文营销之道 如何利用软文引流宣传产品利用软文营销提升低腰产品的知名度 如何打造有效的软文引流策略