标签:
--------------------------- 串口芯片, 配置, 使用 ---------------
串口芯片集成到了主芯片内部, 串口芯片一般包括, 接收缓冲区, 发送缓冲区. 这是硬件缓冲区. 主芯片一般有三条串口线, 就像可以有多条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也支持硬件流控.
分包大小要受对方缓冲区限制, 最大不能超过对方接收缓冲区.
----- 串口速率 ---------
串口传输速率, 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设备
adb shell
setprop persist.uartconsole.enable 1
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.
--------------------------
------------------------
标签:
原文地址:http://www.cnblogs.com/tchz/p/3377750.html