原文地址:http://blog.chinaunix.net/uid-24148050-id-170849.html
1.UART
UART(Universal Asynchronous Receiver and Transmitter)通用异步收发器(异步串行通信口),是一种通用的数据通信协议,它包括了RS232、RS499、RS423、RS422和RS485等接口标准规范和总线标准规范,即UART是异步串行通信口的总称。
而RS232、RS499、RS423、RS422和RS485等,是对应各种异步串行通信口的接口标准和总线标准,它规定了通信口的电气特性、传输速率、连接特性和接口的机械特性等内容,这些东东都是物理层的概念。
通信协议,是属于通信网络中的数据链路层的概念。
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 (int file, const char *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(const char *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()