我所知道的EC====>Uart
我所知道的EC====>Uart1. Introduction
“没有输出之前调试是一门艺术,有了输出以后调试就是一门技术!”这句话准确的道出了调试程序时能够查看运行状况和环境信息的重要性;尤其在FW环境下debug手段非常有限,uart是几乎所有FW都会采用的debug方式。Uart全称是 Universal Asynchronous Receiver/Transmitter即通用异步收发器(异步串行通信口)它包括了RS232、RS485等接口和总线标准规范,它作为一种低速通信协议广泛应用于通信领域。
2. Hardware Interface
Uart spec定义了非常多的信号,但debug过程中通常只需要接三根信号RX、TX、GND就可以了,故常见串口debug线路如下图1所示:
串口读写数据时数据送往内部的fifo,如果fifo满了数据就会按照顺讯逐个bit送往总线。数据读写的时序和格式如下图2所示:
串口通信常见的波特率有19200,115200,9600,4800,2400,1200 Bps,波特率可以通过寄存器配置。
3. How to Implement
前一段时间有同事问我一个问题:”为什么我们EC之中使用printf、puts、putchar这些函数输出字符,字符会送给串口呢?以前写的DOS下的程序是输出到终端的呀。”,他的困惑不无道理,为什么呢?不要着急,让我来告诉你其中的奥秘。若干年以前,我曾经在arm9上面portting过bootloader(u-boot),u-boot也有一个串口debug的部分,所以我就碰巧大致翻了翻这部分的代码,关注过这个问题J.先来看看printf的实现吧,下述代码是u-boot 中的printf code:
void printf (const char *fmt, ...)
{
va_list args;
uint i;
char printbuffer;
va_start (args, fmt);
/* For this to work, printbuffer must be larger than
* anything we ever want to print.
*/
i = vsprintf (printbuffer, fmt, args);
va_end (args);
/* Print the string */
puts (printbuffer);
}
注意看红色部分的代码,printf先使用vsprintf处理输入的参数,并将处理后的结果存入printbuffer,随后调用puts输出。那么我们看一下vsprintf的实现吧,下面的代码是vsprintf的code。
int vsprintf(char *buf, const char *fmt, va_list args)
{
int len;
unsigned long num;
int i, base;
char * str;
const char *s;
int flags;
/* flags to number() */
int field_width;
/* width of output field */
int precision;
/* min. # of digits for integers; max
number of chars for from string */
int qualifier;
/* 'h', 'l', or 'q' for integer fields */
for (str=buf ; *fmt ; ++fmt) {
if (*fmt != '%') {
*str++ = *fmt;
continue;
}
/* process flags */
flags = 0;
repeat:
++fmt;
/* this also skips first '%' */
switch (*fmt) {
case '-': flags |= LEFT; goto repeat;
case '+': flags |= PLUS; goto repeat;
case ' ': flags |= SPACE; goto repeat;
case '#': flags |= SPECIAL; goto repeat;
case '0': flags |= ZEROPAD; goto repeat;
}
/* get field width */
field_width = -1;
if (is_digit(*fmt))
field_width = skip_atoi(&fmt);
else if (*fmt == '*') {
++fmt;
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
}
/* get the precision */
precision = -1;
if (*fmt == '.') {
++fmt;
if (is_digit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == '*') {
++fmt;
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0)
precision = 0;
}
/* get the conversion qualifier */
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
*fmt == 'Z' || *fmt == 'z' || *fmt == 't' ||
*fmt == 'q' ) {
qualifier = *fmt;
if (qualifier == 'l' && *(fmt+1) == 'l') {
qualifier = 'q';
++fmt;
}
++fmt;
}
/* default base */
base = 10;
switch (*fmt) {
case 'c':
if (!(flags & LEFT))
while (--field_width > 0)
*str++ = ' ';
*str++ = (unsigned char) va_arg(args, int);
while (--field_width > 0)
*str++ = ' ';
continue;
case 's':
s = va_arg(args, char *);
if (!s)
s = "<NULL>";
len = strnlen(s, precision);
if (!(flags & LEFT))
while (len < field_width--)
*str++ = ' ';
for (i = 0; i < len; ++i)
*str++ = *s++;
while (len < field_width--)
*str++ = ' ';
continue;
case 'p':
if (field_width == -1) {
field_width = 2*sizeof(void *);
flags |= ZEROPAD;
}
str = number(str,
(unsigned long) va_arg(args, void *), 16,
field_width, precision, flags);
continue;
case 'n':
if (qualifier == 'l') {
long * ip = va_arg(args, long *);
*ip = (str - buf);
} else {
int * ip = va_arg(args, int *);
*ip = (str - buf);
}
continue;
case '%':
*str++ = '%';
continue;
/* integer number formats - set up the flags and "break" */
case 'o':
base = 8;
break;
case 'X':
flags |= LARGE;
case 'x':
base = 16;
break;
case 'd':
case 'i':
flags |= SIGN;
case 'u':
break;
default:
*str++ = '%';
if (*fmt)
*str++ = *fmt;
else
--fmt;
continue;
}
if (qualifier == 'l') {
num = va_arg(args, unsigned long);
} else if (qualifier == 'Z' || qualifier == 'z') {
num = va_arg(args, size_t);
} else if (qualifier == 't') {
num = va_arg(args, ptrdiff_t);
} else if (qualifier == 'h') {
num = (unsigned short) va_arg(args, int);
if (flags & SIGN)
num = (short) num;
} else if (flags & SIGN)
num = va_arg(args, int);
else
num = va_arg(args, unsigned int);
str = number(str, num, base, field_width, precision, flags);
}
*str = '\0';
return str-buf;
}
上述代码大致的意思就是解析fmt字符串,并据此分析计算args,最后将结果写入buf之中,有兴趣可以自己单步debug玩一玩。下面我们来看一看puts的实现代码:
void puts (const char *s)
{
#ifdef CONFIG_SILENT_CONSOLE
if (gd->flags & GD_FLG_SILENT)
return;
#endif
if (gd->flags & GD_FLG_DEVINIT)
{
/* Send to the standard output */
fputs (stdout, s);
}
else
{
/* Send directly to the handler */
serial_puts (s);
}
}
这段code一出问题就明了了J,puts函数会先检查GD_FLG_SILENT flag如置起就什么都不干直接返回,silent就是安静的意思嘛J,而后会看GD_FLG_DEVINIT flag如置起,那么输出到stdout(也就是通常所说的终端),否则就是红色部分的代码了,这里就是问题的关键,它会调用serial_puts输出到串口了,到serial_puts这里就已经很明了了,反正一不做二不休,我们一追到底看个明白(我参考的是s3c24x0的code):
void serial_puts (const char *s)
{
_serial_puts(s, UART_NR);
}
void _serial_puts(const char *s, const int dev_index)
{
while (*s)
{
_serial_putc (*s++, dev_index);
}
}
/*
* Output a single byte to the serial port.
*/
void _serial_putc (const char c, const int dev_index)
{
S3C24X0_UART * const uart = S3C24X0_GetBase_UART(dev_index);
#ifdef CONFIG_MODEM_SUPPORT
if (be_quiet)
return;
#endif
/* wait for room in the tx FIFO */
while (!(uart->UTRSTAT & 0x2));
#ifdef CONFIG_HWFLOW
/* Wait for CTS up */
while(hwflow && !(uart->UMSTAT & 0x1))
;
#endif
uart->UTXH = c;
/* If \n, also do \r */
if (c == '\n')
serial_putc ('\r');
}
图穷匕见,秘密终于揭开,红色部分的code已经说明一切了,最终是通过操纵s3c24x0 uart register将数据抛出去的。经这一追printf、puts、putchar都解决了看完上面的分析之后,大家应该不会再困惑了吧!
That’s all!
Peter
[ 本帖最后由 peterhu 于 2009-5-27 07:45 编辑 ]
请教一下
这个贴子写的很高深啊!看了后有点晕!我就想请教您一下,我们从EC中引出三根线”rx,tx,地线“后在串行接口上和计算机相连就行吗!总感觉不对,是不是还要不着三根信号接在一个专门的IC上经过转化之后在连到计算机上啊! to zhanghmjm: 我想你指的应该是还要经过TTL与CMOS电平转换. to zhanghmjm:如果要接到PC端就需要一个转接卡了,作用如bini所说的做TTL的电平转化。
PC端使用RS-232电平是12v电压,单片机使用TTL电平为0~+5v电压,所以需要做一个电平的转化,
如果是单片机之间通信就没有必要了。
[ 本帖最后由 peterhu 于 2009-6-11 08:10 编辑 ] 2#,
ADM3202, MAX232之类的都可以完成转换的动作,可以参考附件的图,它是51<-->PC的,用在EC上也一样
P30--> RX,P31--> TX,然后将51/EC上的地连过来
[ 本帖最后由 amty.wang 于 2009-6-11 08:50 编辑 ] 看过watcom的运行库源码,很多都差不多啊。
只是最下面不大一样。 感谢bini 和peterhu的回答,我想问的就是这个,我们以前调试EC没有用这个功能,现在发现很不方便,我现在想弄弄这个。对了还想请教您一下关于winbond的watchdog的使用时现在不清楚怎么喂狗!发现有时一喂就会关机,不喂的话还能挺到我设置的时间才关机,请指教一下,谢谢了!! 留个 记号 这几天很忙没有时间来看贴了!现在watchdog的问题已经解决了,其实早就弄好了就是差一个小小的问题。主要是自己马虎!不过以后还是要插常来的! 请教是否ENE上写的ISP mode就是从这接口进行download?
谢谢 请教楼主一个问题
我现在用的是Winbond的WPCE775L的片子,我也习惯于用串口打印。翻了翻源码发现本身有Message函数用于debug,看了它的实现是采用POSIX库中的write完成系统调用,但是我直接调用write函数观察串口并没有任何数据输出(电平转换已经做好)。而且我在源码中也看不到任何的UART初始化并输出的function,不清楚是它封装成库还是怎么样,请您指点一下啊
页:
[1]