串口 终端(原创)

Posted on 2015-01-20 15:27  tongchuhuozhai  阅读(4104)  评论(0编辑  收藏  举报

----------- 使 串口在进入idle后继续输出log ------------ 

默认是不能的, 只要下面的, 就能不断的输出串口log: 

adb shell
setprop persist.uartconsole.enable 1

 

  串口线标准不一样, 有一根串口线能打出 feature phone的串口数据。 但6795的数据就打不出来。 有1.8伏的,和2.8伏的。要确定串口线用的对不对。 里面是转换芯片的。 

  --------------------------- 串口芯片, 配置, 使用 --------------- 
  串口芯片集成到了主芯片内部, 串口芯片一般包括, 接收缓冲区, 发送缓冲区. 这是硬件缓冲区.  主芯片一般有三条串口线, 就像可以有多条i2c总线.
 
  相应的gpio可以选择配置成串口, 并不是每个gpio都能配置成串口, 从gpio配置表dws文件, 可以看到gpio是否支持串口模式. 
    
  从feature phone调试来看, 串口只能被一个模块使用, 可以用setowner函数来切换被哪个模块使用. 有一个nv项会决定, 串口给哪个模块用. 

  每次串口在接收到数据时, 都会发送串口消息, 在feature phone中, 消息中都会指定发给哪个task. task中都会有一个循环, 来接收task.  循环中会相应的receive函数, 接收消息, 如果没消息过来, 则会阻塞. 
 
  实际中遇到的问题:
 
  1.  在打开串口时, 本来要打开串口1, 而打开了串口3, 结果Task循环中, 一直没收到消息.
 
  2. 配置串口时, 没调setowner, 导致open串口时指定的模块, 与串口的owner不一致, 导致电流一直在巨大跳变. 一插上usb, 电脑的services进程占用40%
 
  3. uart_getbytes函数的指定的软件缓冲区length大小为0, 导致硬件缓冲区的数据没有被接收, 串口驱动的接收标志没有被置位,
     导致下一次串口接收数据时, 没有发串口消息. 把length改大即可.  
    
  4. 串口的owner切换, 默认配置的是给at模块用. 为了给2502传送数据, 添加一个at命令, 完成模块切换. 即将串口由at模块转给自定义的task模块.
 
  5.  task 配置,  在hal_task_config.h配置,  编译不过. 要在app_task_config.h配置, task所做的事情, 应该都属于mmi层. 对应linux, 应该是串口用户空间编程.
 
  6. 因持续收到的串口消息没有释放, 导致收到第600个左右的字节时, 系统挂掉.
  先说一下消息的一般机制:
  java, c, c++, 消息架构都是, 消息对象里都会指定消息的接收者, 消息的接收者, 一般都是一个循环,
  循环里有个函数会等待消息, 如果消息没来, 就会阻塞在那, 如果消息过来, 才会继续执行.
  mtk Task接收uart消息, 就是使用这个机制.
  串口有数据发来, 就会有个串口消息发出来, 通过对串口设置owner, 即该task的mod id, 这样对消息体里id就指定这个task. 消息发送这个task.
 
  task while 循环接收到消息, 实际接收的消息的对象, 一般的机制都是由接收者来释放这个消息在内存中申请的空间.
 
  若没有释放, 这里碰到一个问题, 由于空间不断泄漏, 到收到第600多个字节, 消息对象数量不断增多, 泄漏的内存不断增大, 导致系统挂住.
 
  串口工具有时at命令无响应, 串口工具重新打开就好

 

------------------ cts rts配置 -----------------
  解释cts/rts流控,要从接收缓冲区说起. 当初遇到了什么问题, 而想出了这个东西. 

  流控是为了接收缓冲区没有准备好, 发送方还在发数据的问题, 数据丢失的问题. 

  解决的办法, 就是多连一条线, 接收方通过控制这根线的高低电平, 来告诉发送方, 接收缓冲区是否准备好. 

   接收缓冲区如果没准备好, 会把这根线拉高,  如果准备好, 就把这根线拉低.
  从术语上讲, 这根线在rx这边, 对应引脚是rts,  在tx那边, 接引脚cts.  在数据没准备好时,   rts拉低. 默认rts配置成内部拉高.

 

接线:

rx要接到对方的tx, tx要接到对方的rx. 在调ble与2502通讯时, ble要配成与2502相反. 

rts要接到对方的cts.  rts电位由接收方控制.

 

通过控制电平, 通知对方:

当接收方还没准备好, 接收方会把rts拉高, 即发送方的cts被拉高, 这样发送方就知道接收发没有准备好, 就不发送了.
当接收方准备好时, 接收方会把rts拉低, 即发送方的cts被拉低, 这样发送方就知道接收发准备好, 就发送数据.

 

gpio配置:

从Datashee可确认, 打开dws, 可看到支持硬件流控的gpio. 2502只有两个gpio支持硬件流控,  从gpio选项中可确认.

 

测试硬件是否起作用:

 

 

对于ble与2502, 因为2502给ble发送大数据, ble是接收方, 所以rts信号由接收方控制, 这就是ble支持硬件流控.  而2502的cts收到这个信号, 能不发送数据, 说明2502也支持硬件流控.

 

分包大小要受对方缓冲区限制, 最大不能超过对方接收缓冲区. 

 

--------------------  反接, 看另一方数据  --------------------

uart , 串口线的白线接到板子上的Tx, 即接到板子的发she点, 即看板子的串口输出信息.

 

ble到2502的串口总线, 引出的tx, rx测试点, 是以2502来命名的,

如果想接收2502的串口发出信息, 就接点tx点.
如果想看ble的串口发出发出信息, 就接到rx点.
调试时经常要看ble的发出信息. 所以uart反接一下.
一语击中要点, 又不卖nong.
可以从插头, 直接引出针来.

修行需要积点滴以成大海的yi力

 

 

-----  串口速率 ---------

   
   串口传输速率, 50K, 20K分别对应哪个档. 115200 对应多少.  除以8, 得14402, 即15K. 是吧
   串口传输速率有两种表示方法, 一种bps, 如115200. 一种是KBps, 一般一个数据占8位.  所以115200对应的是14.4KBps. 理论上每秒传14K.   传输速率是否受接收方的限制. 

 

---------- 协调串口数据传输的信号线 ----------------

uart可以有18根信号线, unix环境只用了6根.


GND - Logic Ground 地线, 提供了一个参考电压, 基于这个参考电压, 有两种电压, space电压, mark电压.
TXD - Transmitted Data发送信号线
RXD - Received Data接收信号线
DCD - Data Carrier Detect表示另一端已经连接好的信号线, space电压表示另一端已经连接, mark电压表示没准备好
DTR - Data Terminal Ready表示另一端是否已经准备好的信号线, space电压表示已经准备好
CTS - Clear To Send space电压表示本地这端可以送出更多数据
RTS - Request To Send space电压表示本地已经准备好了数据可以传送.

以后再看到这些字母就不犯迷糊了. 这些信号线都是来协调数据传送的, 就是为了给你百分之百的保证. 才有了这么多信号线.

芯片从串行总线接到数据, 数据都是一个字节一个字节传送的, 必须知道字节的开始位置, 与结束位置.
串行线在没有数据传送时, 都是mark电压.
在变到space电压, 说明有数据来了,
每个字节前都有一个space电压的start位, 表示字节的开始
在每个字节的最后都一个parity校验位, 和一个停止位.

数据一般表示为8N1, 表示8个数据位, 无校验位, 有一个停止位.
7E1 表示7个数据位, Even偶数校验位, 和一个停止位.


-------------
双工之双字,就表示双向, 接收与发送两个方向
全双工,  就是同一时刻, 可以同时接收与发送数据,  一个发送线, 一个接收线
半双工, 在同一时刻, 只能发送, 或只能接收.

----------------
为了协调串行数据流, 有软件和硬件两种方法,
软件方式即是用特殊字符来开始或结束数据流, 这些特殊字符有: 开始的有XON, DC1, 结束的有XOFF, DC3.
硬件方式, 接收方准备好时, 将CTS置为space电压, 没有准备好就置为mark电压.  这样发送方就知道可以发送数据了.  CLEAR to send, 即告诉发送方可以发送了.
发送方准备好时,  将RTS置为space电压, 这样接收方就知道可以接收数据了.   Request to send, 即发送请求发送数据, 让接收方接收.
这就是CTS/RTS通知机制.  

有数据正在传输时, 接收信号线和发送信号线, 都会保持在mark电压. 如果传着传着, 从mark电压掉到space电压,  并且持续了1/4秒, 说明传输break了, 这时break条件就存在了.
break一般被用来重置数据线.

 

---------------  串口线 -------------
串口线的地线是必须连接的, 若只看log, 则只连tx和地线, tx对应白线. 若要通过串口发at命令, 则要tx, rx, 地线三根线.

电源引出的线有可能烧掉, 用万用表量一下电阻, 正常的会有一个值.

2502引出的uart1, 与蓝牙通信, 接收蓝牙发来的At命令, (应该是字节), 然后2502向蓝牙通过串返回数据.

 

--------------  串口log  --------------------

串口线的发送或接收, 都是基于板子命名的, 串口数据从板子出去的, 就叫发送。 串口线里面,有一条线专门是发送的, 这条线叫发送线, 原理图上的焊点叫tx。
t (transmit)表示发送,从板子到电脑,
r (receive)接收,从电脑到板子,接收电脑发来的命令。
原理图上有注释,uar1 for ap log, 从原理图对应的pcb图上找到测试的uart点。


串口线里有个转唤器, 白色线表示发送, 绿色线表示接收,黑线表示接地。 一般看串口log,  只用发送,即只连白线与黑线。 如果黑线接到了rx上,即接收点,串口log就是乱码。

 

lk传过disable_uart参数决定了,串口是否吐log.  如果lk后面的阶段,如kernel, recover模式,如果不吐log. 就要检查这个参数。

串口如果想吐android log, 就必须在命令行下,输入logcat。 如果没有这个命令, 串口是不会吐android log 的。

 

串口可以看到初始化log, preloader, lk, 内核的,串口日志是非常需要的。usb无法看到初始化日志。

pcb一般只选pads, vias两项, 从sch对应找到pcb钟的测试点

 

需要一个usb转串口驱动的软件,如果环境变量有修改,这个软件就安装失败。

用secureCRT看串口log输出:
从设备管理器上,找到端口,这里10
连接速率选最大,其它默认
如果有光标,说明连上了,可以看串口log了。


------------- 将串口注册为一个终端 ------------
tty_io.c 注册tty设备

 

uart0:ap 侧log
uart1: modem侧log

如果不接rx拿掉,则无此问题
终端是与外部与计算机交互的一个窗口

------------------------


pty是虚拟终端,
pts, ptmx是pty的实现
每打开一个终端, /dev/pts/ 下就有一个文件


先从外部特性认识一下
一个串口(串行端口),就是一个终端,对应/dev/ttySn
在图形界面下,打开终端实际是打开依终端,对应/dev/pts/ 下面的一个文件,可以打开终端,看一下pts目录下多了一个文件。
用echo "1" > /dev/pts/n 可以看到终端下面出现相应字符。
查看当前终端, 用tty, 如可看到/dev/pts/2
对于/dev/console, 只有单用户模式下,才可登录到控制台
用who, 可看到打开了多少个虚拟终端。
/dev/tty始终代表当前终端,如果当前终端为/dev/pts/2, 则/dev/tty/就代表/dev/pts/2
echo "1" > /dev/tty等同于 echo "1" > /dev/pts/2

ps -aux 的TTY列,显示在哪个终端上运行


再从代码上分析实现:
从上层到驱动层:
tty_io.c read
线路层:tty_ldisc.c read 等待队列机制进入睡眠
驱动层:以串口为例,则应为 serial_core.c


fs_initcall与module_initcall, 都是define_initcall宏函数,被放在vmlinux.lds.s链接脚本的INIT_CALLS宏指定的init段中,该宏在vmlinux.lds.h中定义。

调用过程: start_kernel-> init-> init线程函数调用do_initcalls来调用这些init函数。

字符设备初始化(drivers/char/mem.c)主要就是tty初始化,chr_dev_init->tty_init,完成tty设备初始化,总算看到字符设备的应用示例了, 设备初始化一定会创建设备模型,

--------------------
串口线中:白线为tx, 即发送线。  绿线为rx. 即接收线。

接上rx线,不会有机器挂掉的问题,把软件重新firmware upgrade一下,就解决休眠后挂掉的问题,

用rx,向机器发送命令,需要机器亮屏,可使用tab键,在usb线不够用,或usb口不能用时,用rx串口, 一样看到adb shell所能看到的文件信息,调用otg,usb被占用,这时用串最合适。
若想看log, 可以直接aee -k 6就显示log.
选中后,可以直接查找

control+f 进入debug模式, 这个模式能看到arm各寄存器的值,中断号信息,

   

开机初始化log只能看串口,因为adb shell就内核完全启动后的用户空间的一个进程,所以adb shell 看不到内核初始化的log, 这包括各个驱动的probe初始化log. mtk log建立在文件系统上,所以也没有内核初始化log.

preloader lk 内核初始化log都可通过串口看到。 

 有乱码时,重启secure  uart就好了。

 

-----------   串行总线基本原理  -------------

RS-232是串行总线标准.该总线按位传送.
串行总线由发送线tx,接收线rx,地线组成.
发送线在发送的同时,接收线可以接收,即异步通信
波特率是每秒传送的位数.单位是bps. 一般一个字符有10位,1个起始位.1个结束位,8个数据位. 每秒字符数(即信息包数,一个信息包含一个字符)乘上字符的位数,即是波特率.
数据位,一个信息包有一个开始位,一个结束位.数据位有的7位,有的是8位.这个要根据协议确定.

停止位一方面表示一个信息包的结束,一方面提供校正同步时钟

校验用奇偶校验.如果是偶校验,数据1的个数为偶数,校验位就为0. 可以判断是否有噪声干扰了信号.

uart连接的串行设备, i2c连接的并行设备. 
uart一般用于计算机与外部设备的通信,如键盘等. i2c一般用于内部模块通信.sensor都是用i2c. gps, 蓝牙等射频相关的都走串口.

显示器也用串口,有9针串口, 也有25针串口. 

所谓协议,接收方与发送方确定,一个信息包的每个位表示的含义.双方对此的理解必须是一致的.

接收过程:由波特率和接收时钟,确定每一位的时间长度.
当信号线从1跳到0,表示开始接收.
一侦一帧的接收. 在一帧之内,用时钟确定接收特定的位.
每隔8个时钟,来接收一位.

把数据放到寄存器中,在发送时钟的控制下,将寄存器的数据移位输出.

在接收端,在接收上升沿的那个时刻,对数据采样,把数据放到移位寄存器中,组成并行数据取出.

波特率是一秒钟发送的位数.波特率因子发送或接收一个数据位需要的时钟数. 
显示屏25针的串口有4条数据线,11条控制线,其余备用线.

 

-----------------------------

串口也可以同步通讯, 这时需要多出一个时钟线, 因为同步通讯需要硬件上额外支持, 一般不用同步通讯.

访问串口设备文件的方法:
改变串口设备文件的权限, 以让一般程序可以访问

一切皆是文件,  打开串口设备, 就中打开这个文件, 即
open("/dev/ttyS0", );

进程打开一个串口设备, 默认,进程就是这个串口设备的终端, 控制终端,  串口设备上的信号都会影响这个进程, 如ctrl+C这个信号就会终止这个进程.

如果不想接收这个串口的信号, 使进程不成为这个串口的终端, 就不会收到串口的信号了. 用O_NOCTTY.

如果进程不关心另一端是否连接,  即不关心dcd信号的状态, 就用O_NODELAY.

向串口可以写入数据, 发at命令就是写数据, 用write系统调用. 

从串口读数据, 就是从串口缓冲区上读数据,  如果得不到数据, read就不返回.

如果想让read立即返回, 就要用fcntl设置一下, fcntl(fd, F_SETFL, FNDELAY);

关闭串口, 直接关闭这个设备文件即可, 就会将dtr信号拉低.

CLOCAL与CREAD, 保证程序不会成为端口的所有者, 所有者必须处理各种信号.
options.c_cflag |= (CLOCAL | CREAD);

控制选项还可设置奇偶校验.
没有奇偶校验:
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
奇数校验:

options.c_cflag |= PARENB  // 使能校验位
options.c_cflag &= ~PARODD
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;

即c_cflag设置成7E1

偶数校验:
options.c_cflag |= PARENB
options.c_cflag |= PARODD
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
即c_cflag设置成7O1

 

控制选项中设置了奇偶校验,  在输入选项就要把奇偶项剥去, 设置为:

options.c_iflag |= (INPCK | ISTRIP);

如果发生了奇偶校验错误, DEL字符和NUL字符和出错的字符一起发出.

 

如果打开了使能了奇偶校验, 又要做一些特别试验, 如在发生奇偶校验错误, 也要忽略, 放行数据, 可用

options.c_iflag |=IGNPAR;

这时只有NUL字符送出

 

如果硬件上支持cts/rts, 就要设置:
options.c_cflag |= CNEW_RTSCTS
如果不支持:
options.c_cflag &= ~CNEW_RTSCTS;

 

旧的接口没有c_ispeed,和c_ospeed两个成员.  所以就用了CBAUD,B9600等波特率常数

常量TCSANOW标志所有改变必须立刻生效而不用等到数据传输结束

tcsetattr 可以设置的有:
TCSANOW标志所有改变必须立刻生效而不用等到数据传输结束
TCSADRAIN  等待传输完之后再生效.

串口连接两台机器, 波特率应为两个中较小的那个.

设置一个字符大小:
options.c_flag &= ~CSIZE;
options.c_flag |= CS8; 也可以CS8.

 

 

 

--------------------------

------------------------