李迟注:
这几篇文章写于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-准备篇相关推荐

  1. Ubuntu命令终端查看使用过的命令

    使用history命令 cyf@ubuntu:~$ history 但是这样会显示出所有使用过的命令,可以在history后加上less cyf@ubuntu:~$ history | less 会显 ...

  2. Win7命令终端基础配色指南

    微软对控制台字体的元数据有严格的限制,https://support.microsoft.com/zh-cn/help/247815/necessary-criteria-for-fonts-to-b ...

  3. Windows 如何在命令终端(CMD)使用命令来访问本地/远程的 Oracle 数据库呢?

    打开命令窗口后直接输入 sqlplus sys/123@orcl as sysdba 其中 sys 是用户名,123 是密码,orcl 是数据库实例名,as sysdba 表示用户 sys 是数据库管 ...

  4. Windows 如何用命令终端(CMD)启动和停止 MySQL 数据库服务

    当安装完 MySql 后,默认每次 Windows 启动的时候都会将 MySql 服务启动起来.那么如何通过命令方式来启动和停止 MySQL 服务呢? Windows XP 如果是 Windows X ...

  5. Linux 的命令终端(CMD)的快捷键(Keyboard of MacBook)

    文章目录 常用 移动光标 编辑命令 查找历史命令 控制命令 命令终端界面滚屏 命令终端页签切换 奇葩 常用 快捷键 说明 Ctrl + A 光标跳到本行的行首 Ctrl + E 光标跳到本行的行尾 C ...

  6. Windows 命令终端(CMD)的快捷键

    这些快捷键只在Windows系统操作有效,连接远程Linux主机,再操作这些快捷键是无效的,因为连接远程的Linux主机后,你用的是Linux命令终端. 快捷键: F1:按F1逐字显示最后一次执行的命 ...

  7. 如何通过命令终端访问本地/局域网/远程的MySQL数据库_访问数据库_连接数据库_登录数据库

    文章目录 Windows系统下 访问本地MySQL数据库 访问远程主机的MySQL数据库 本地安装了MySQL数据库 本地没有安装MySQL Linux系统下 退出数据库登录 Windows系统下 访 ...

  8. Debian11镜像更新为阿里巴巴开源镜像站镜像,切换root用户,解决用户名不在sudoers文件中此事将被报告,Debian11 文件夹对话框、火狐浏览器、命令终端等没有最大化和最小化

    选择Debian作为编程开发最佳Linux的理由: Debian是面向程序员的最古老,最出色的Linux发行版之一.Debian提供了具有.deb软件包管理兼容性的超稳定发行版.Debian为程序员提 ...

  9. “命令终端”的实现4-优化之解耦

    这段时间一直在做测试的工程(不是测试的工作),为了应付不同的测试场景,代码使用了解释器风格,至于实现,则使用了多年前写的命令终端代码.那会刚毕业不久,写的代码还是有提升空间.现在重新拾起,打破一般认知 ...

最新文章

  1. 你不知道的对称密钥与非对称密钥
  2. 数字图象处理之二维码图像提取算法(九)
  3. mac14.5 mojave安装错误
  4. Qt中应用程序的打包与发布
  5. 预处理指令的开始和结束
  6. opencv进阶学习笔记11:cannny边缘检测,直线检测,圆检测
  7. Mac OS X下64位汇编与Linux下64位汇编的一些不同
  8. 我们的实践: 400万全行业动态事理图谱Demo
  9. python语言中包含的标准数据类型_Python对象——标准类型的分类
  10. 控制台程序转化为windows服务
  11. ViBe(Visual Background extractor)背景建模或前景检测
  12. 白帽SQL注入实战过程记录(2)——根据information_schema组装SQL注入语句
  13. 关于团队项目的一些思考和理解
  14. java简单代码练习
  15. Android--使用开源vitamio做万能视频播放器
  16. 计算机用户密码查看器,电脑开机密码查看工具
  17. PackageManager hasSystemFeature
  18. 不怕崩溃 Ghost令机房管理化繁为简
  19. jsp:使用request为页面添加静态数据。
  20. 低腰产品的软文营销之道 如何利用软文引流宣传产品利用软文营销提升低腰产品的知名度 如何打造有效的软文引流策略

热门文章

  1. linux curses,Linux下利用curses库实现弹球游戏
  2. 【HTML】制作一个简单的浮动广告页面
  3. 什么是心智,如何占领用户心智
  4. 初识 Scrapy - Feed导出
  5. 提升技术认知,参加2021上海QCon技术大会
  6. 基于多因子模型,利用申万行业分类对量化公募基金进行分析
  7. Flutter之文字环绕控件效果
  8. 织梦mysql utf-8数据库_dede数据库编码UTF8与GBK转换方法
  9. 2017上半年技术文章集合—184篇文章分类汇总
  10. 加载JavaScript脚本方式