1.UART
UART(Universal Asynchronous Receiver and Transmitter)通用异步收发器(异步串行通信口),是一种通用的数据通信协议,它包括了RS232、RS499、RS423、RS422和RS485等接口标准规范和总线标准规范,即UART是异步串行通信口的总称。
而RS232、RS499、RS423、RS422和RS485等,是对应各种异步串行通信口的接口标准和总线标准,它规定了通信口的电气特性、传输速率、连接特性和接口的机械特性等内容,这些东东都是物理层的概念。
通信协议,是属于通信网络中的数据链路层的概念。
1.UART协议的工作特点
1.1数据采样
UART协议是实现设备之间低速数据通信的标准协议。因发送时不需同时发送时钟,故此协议为异步。UART链接典型为38400,9600波特 。
如图1,UART字符格式为1个起始位,5~8个数据位,1个地址位或奇偶位(可选),1个停止位。
由于接收器、发送器异步工作,无需联接接收和发送时钟。接收器采取对输入数据流高度采样方式,通常采样为16,并根据采样值确定位值。按惯例,使用16个采样值的中间三个值。
1.2 UART帧区分
UART一参数MAX-IDL,用来设置空闲字符的多少。一旦一字符在线上被接收,UART控制器开始计数接收到的空闲字符。若下一数据字符接收前,一MAX-IDL多个空闲字符被接收,则产生空闲时间,缓冲区被关闭。顺次对CPU32+核心发出一中断请求,要求从缓冲区接收数据。因此,MAX-IDL给UART模式提供一区分帧的便利方法。
空闲字符按以下公式计算其位数:1(起始)+数据长度(5,6,7,8)+1(若奇偶校验被使用)+停止位(1)。例如,1个(起始),8位数据,无校验,1个停止位,则空闲字符MAX-IDL为10位。
1.3 UART地址识别
多站系统中,网络上可能会有两个以上的站,每个站有一特定的地址。图2为此种结构的两个示例。由许多字符构成的帧可被广播,其第一字符做为目的地址。为实现此功能,UART帧被扩展一位,以区别地址字符和正常数据字符。
UART可被设置为操作于一多站环境,此环境下,支持以下两种模式:
自动多站模式 当地址于两个预置值之一相匹配时,UART控制器自动检查到来地址字符,接收随后的数据。
非自动多站模式 UART控制器接收所有数据。一地址字符总被写入一新缓冲区。
综上所述,UART协议采取一种通过数据采样来确定位值的机理,具有简单准确的定帧模式,而且广泛用于多站系统中,具有自动多站和非自动多站两种模式,来区分地址和数据。
1.1 RS232
COM口是PC(个人计算机)上,异步串行通信口的简写。由于历史原因,IBM的PC外部接口配置为RS232,成为实际上的PC界默认标准。所以,现在PC机的COM口均为RS232。
上图最右边的是串口接口,统称为RS232接口(封装DB9)
通信过程中实际只有两个管脚参与通信
2脚:电脑的输入RXD
3脚:电脑的输出TXD
5脚:接地
通过2,3脚就实现全双工(可同时收发)的串行异步通信
1.2 UART通信协议
UART使用的是 异步,串行通信。
串行通信是指利用一条传输线将资料一位位地顺序传送。特点是通信线路简单,利用简单的线缆就可实现通信,降低成本,适用于远距离通信,但传输速度慢的应用场合。
异步通信以一个字符为传输单位,通信中两个字符间的时间间隔多少是不固定的,然而在同一个字符中的两个相邻位间的时间间隔是固定的。
数据传送速率用波特率来表示,即每秒钟传送的二进制位数。例如数据传送速率为120字符/秒,而每一个字符为10位(1个起始位,7个数据位,1个校验位,1个结束位),则其传送的波特率为10×120=1200字符/秒=1200波特。
数据通信格式如下图:
其中各位的意义如下:
起始位:先发出一个逻辑”0”信号,表示传输字符的开始。
数据位:可以是5~8位逻辑”0”或”1”。如ASCII码(7位),扩展BCD码(8位)。小端传输
校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验)
停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。
空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。
注:异步通信是按字符传输的,接收设备在收到起始信号之后只要在一个字符的传输时间内能和发送设备保持同步就能正确接收。下一个字符起始位的到来又使同步重新校准(依靠检测起始位来实现发送与接收方的时钟自同步的)
3. Uboot 串口驱动
- board_init_r()
- --》devices_init()
- --》drv_system_init()
- 创建串口设备 serial,放到全局数组devlist[]中去
- struct device_t serial{
- .name = "serial"
- .putc= serial_putc; //这些函数是芯片相关的
- .puts= serial_puts;
- .getc= serial_getc;
- .gets= serial_gets;
- }
- --》console_init_r()
- --》设置钩子函数,这些函数是硬件相关的
- gd->jt[XF_getc]= serial_getc;
- gd->jt[XF_tstc]= serial_tstc;
- gd->jt[XF_putc]= serial_putc;
- gd->jt[XF_puts]= serial_puts;
- gd->jt[XF_printf]= serial_printf;
- --》在devlist[]中搜索,初始化标准输入,标准输出和错误输出的device_t指针
- inputdev = search_device(DEV_FLAGS_INPUT,"serial");
- outputdev = search_device(DEV_FLAGS_OUTPUT,"serial");
- errdev = search_device(DEV_FLAGS_OUTPUT,"serial");
- --》文件描述符file_no与设备device_t绑定,初始化stdio_devices[file_no]= dev
- console_setfile (stdout, outputdev);
- console_setfile (stderr, errdev);
- console_setfile (stdin, inputdev);
Uboot中,串口打印流程
- printf-->puts-->fputs
- void fputs (intfile,constchar*s){
- if (file< MAX_FILES)
- stdio_devices[file]->puts(s);
- //调用的函数为serial_puts,芯片相关不分析
- }
4. Linux串口驱动
- start_kernel()
- --> console_init()
- -->cpm_uart_console_init()
- -->register_console(&cpm_scc_uart_console)
- console->setup()
- //最终调用cpm_uart_cnsole_setup()
- //初始化uart控制器,并将console 结构加入到 console_drivers 列表中去
由于我们在console_init中调用cpm_uart_console_init时,
因为当时uart port的基址等基本参数都没有确立, 故其中调用console->setup 以失败返回,
即调用register_console 没有成功注册,
故后面在do_initcalls中调用 cpm_uart_init()进行初始化时,
其初始化中调用uart_configure_port会再次调用register_console
这次由于uart port的基址等参数得到初始化,故register_console成功
- do_initcalls()
- -->cpm_uart_init()
- -->uart_register_dirver(&cpm_reg)
- for( i=0; i< cpm_uart_nr; i++)
- con = cpm_uart_port_map[i]
- -->uart_add_one_port(&cpm_reg,&cpm_uart_ports[con].port)
- //行参(struct uart_driver *drv, struct uart_port *port)
- 注意这里有 port->cons= drv->cons;
- -->uart_configuare_port(drv, state, port)
- --> port->ops->config_port(port, flags)
- //这里port的ops是 cpm_uart_pops
- //即调用cpm_uart_config_port, 初始化buf,bd,controlller,enable rx/tx
- --> cpm_uart_request_port(port)
- cpm_uart_allocbuf(pinfo,0)
- cpm_uart_initbd(pinfo)
- cpm_uart_init_scc(pinfo)
- -->uart_report_port(drv,port) //打印串口相关信息
- -->register_console(port->cons) //形参(struct console *console)
- //加入到 console_drivers 列表中去
- -->uart_console(port)
printk在src/kernel/printk.c中实现
- int printk(constchar*fmt,...)
- static char printk_buf[1024];
- // 函数内申请了一块静态内存printk_buf[]作为format缓冲区,然后把缓冲区内容放到LOG_BUF中
- // 不管console是否存在,printk都成功返回。
- --> release_console_sem();
- --> call_console_drivers();
- -->_call_console_drivers(start_print, cur_index, msg_level);
- // 对console驱动中write的调用
- for (con = console_drivers; con; con= con->next){
- if ((con->flags& CON_ENABLED)&& con->write)
- con->write(con,&LOG_BUF(start), end- start);
- }
- con->write() 实际上是 cpm_scc_uart_console.write
- 即 cpm_uart_console_write()