其实到这里,只要能保证编译通过,再修改一下链接脚本,其实已经可以跑在at9260的系统上了。因为我没有仿真器,不知道程序的状态,所以必须撰写调试串口的驱动。
RTEMS 的调试串口并没有采用中断输出的方式,而是采用查询输出的方式。相对来说比较容易。
RTEMS里有两个打印函数:
printf 和 printk
printf是由库支持的打印函数,内部比较复杂,主要用于应用程序级别的打印,不能用于内核和中断的打印,特别是中断中不能使用该函数,否则会引起死机。
printk是由rtems的调试打印函数,主要用于内核和中断的打印。任务级别也可以调用,但是最好不要这样做。
printk是调用 BSP_output_char 这个函数指针完成字符的输出。
可以在c/src/lib/lbicpu/arm/at91sam9260/dbgu/Dbgu.c中的末尾的
static void _BSP_put_char( char c ) {
dbgu_write_polled(0, c);
}
BSP_output_char_function_type BSP_output_char = _BSP_put_char;
那么就是说,printk实际上是调用调试串口驱动完成字符的输出。
既然调用调试串口输出,必须等调试串口初始化完成以后才能输出。
start.S执行完毕后,是跳转到 bootcard 函数中继续执行,bootcard 要完成对内核数据的初始化后,才调用IO的初始化,这时串口的驱动才能工作。也就是说,想在bootcard运行调试串口初始化以前,看到printk的输出,基本上是不可能的。
如果你真的想这个时候也能看到printk的输出,只能提前初始化串口驱动了。
我的做法是,dbgu_write 函数内部会检查调试串口有没有被初始化,如果没有被初始化,先初始化再输出字符。那么这样,即使在start.S汇编文件里调用printk,也是能看到输出的。
这里还要注意一个问题就是调试串口的波特率,调试串口的波特率是通过函数BSP_get_baud获得,
这个函数在c/src/lib/libbsp/arm/at9260/include/bsp.h中。
另外就是修改 c/src/lib/libbsp/arm/at9260/console/uarts.c的代码。
这里的改动主要是一些配置和寄存器,没有什么好讲的。
下面是dbgu.c的源代码:
#define AT91SAM9260_DBGU_NUM 1
static volatile int _gIsInitDbgu[AT91SAM9260_DBGU_NUM] = {0};
volatile int dbg_dly;
/* static function prototypes */
static int dbgu_first_open(int major, int minor, void *arg);
static int dbgu_last_close(int major, int minor, void *arg);
static int dbgu_read(int minor);
static int dbgu_write(int minor, const char *buf, int len);
static void dbgu_init(int minor);
static void dbgu_write_polled(int minor, char c);
static int dbgu_set_attributes(int minor, const struct termios *t);
/* Pointers to functions for handling the UART. */
console_fns dbgu_fns =
{
libchip_serial_default_probe,
dbgu_first_open,
dbgu_last_close,
dbgu_read,
dbgu_write,
dbgu_init,
dbgu_write_polled, /* not used in this driver */
dbgu_set_attributes,
FALSE /* TRUE if interrupt driven, FALSE if not. */
};
/*********************************************************************/
/* Functions called via callbacks (i.e. the ones in uart_fns */
/*********************************************************************/
/*
* This is called the first time each device is opened. Since
* the driver is polled, we don't have to do anything. If the driver
* were interrupt driven, we'd enable interrupts here.
*/
static int dbgu_first_open(int major, int minor, void *arg)
{
return 0;
}
/*
* This is called the last time each device is closed. Since
* the driver is polled, we don't have to do anything. If the driver
* were interrupt driven, we'd disable interrupts here.
*/
static int dbgu_last_close(int major, int minor, void *arg)
{
return 0;
}
/*
* Read one character from UART.
*
* return -1 if there's no data, otherwise return
* the character in lowest 8 bits of returned int.
*/
static int dbgu_read(int minor)
{
char c;
console_tbl *console_entry;
AT91S_DBGU *dbgu;
if ((minor < AT91SAM9260_DBGU_NUM) && (0 == _gIsInitDbgu[minor]))
dbgu_init(minor);
console_entry = BSP_get_uart_from_minor(minor);
if (console_entry == NULL) {
return -1;
}
dbgu = (AT91S_DBGU *)console_entry->ulCtrlPort1;
if (!(dbgu->DBGU_CSR & AT91C_US_RXRDY)) {
return -1;
}
c = dbgu->DBGU_RHR & 0xff;
return c;
}
/*
* Write buffer to UART
*
* return 1 on success, -1 on error
*/
static int dbgu_write(int minor, const char *buf, int len)
{
int i, x;
char c;
console_tbl *console_entry;
AT91S_DBGU *dbgu;
if ((minor < AT91SAM9260_DBGU_NUM) && (0 == _gIsInitDbgu[minor]))
dbgu_init(minor);
console_entry = BSP_get_uart_from_minor(minor);
if (console_entry == NULL) {
return -1;
}
dbgu = (AT91S_DBGU *)console_entry->ulCtrlPort1;
for (i = 0; i < len; i++) {
/* Wait for fifo to have room */
while(1) {
if (dbgu->DBGU_CSR & AT91C_US_TXRDY) {
break;
}
}
c = (char) buf[i];
dbgu->DBGU_THR = c;
/* the TXRDY flag does not seem to update right away (is this true?) */
/* so we wait a bit before continuing */
for (x = 0; x < 10; x++) {
dbg_dly++; /* using a global so this doesn't get optimized out */
}
}
return 1;
}
/* Set up the UART. */
static void dbgu_init(int minor)
{
console_tbl *console_entry;
AT91S_DBGU *dbgu;
if ((minor >= AT91SAM9260_DBGU_NUM) || (_gIsInitDbgu[minor] > 0))
return ;
console_entry = BSP_get_uart_from_minor(minor);
if (console_entry == NULL) {
return;
}
_gIsInitDbgu[minor] = 1;
dbgu = (AT91S_DBGU *)console_entry->ulCtrlPort1;
AT91C_BASE_PIOB->PIO_IDR = (1<<14) | (1<<15);
AT91C_BASE_PIOB->PIO_ASR = (1<<14) | (1<<15);
AT91C_BASE_PIOB->PIO_BSR = 0;
AT91C_BASE_PIOB->PIO_PDR = (1<<14) | (1<<15);
AT91C_BASE_PIOB->PIO_PPUDR = (1<<14) | (1<<15);
/* Clear error bits, and reset */
dbgu->DBGU_CR = (AT91C_US_RSTSTA | AT91C_US_RSTTX | AT91C_US_RSTRX);
/* Clear pending interrupts */
dbgu->DBGU_IDR = (AT91C_US_RXRDY | AT91C_US_TXRDY | AT91C_US_ENDRX |
AT91C_US_ENDTX | AT91C_US_OVRE | AT91C_US_FRAME |
AT91C_US_PARE | AT91C_US_TXEMPTY | AT91C_US_TXBUFE |
AT91C_US_RXBUFF | AT91C_US_COMM_TX | AT91C_US_COMM_RX);
dbgu->DBGU_IMR = 0;
/* Set port to no parity, no loopback */
dbgu->DBGU_MR = AT91C_US_PAR_NONE | AT91C_US_CHMODE_NORMAL;
/* Set the baud rate */
dbgu->DBGU_BRGR = (at91sam9260_get_mck() / 16) / BSP_get_baud();
/* Enable the DBGU */
dbgu->DBGU_CR = (AT91C_US_RXEN | AT91C_US_TXEN);
}
/* I'm not sure this is needed for the shared console driver. */
static void dbgu_write_polled(int minor, char c)
{
dbgu_write(minor, &c, 1);
}
/* This is for setting baud rate, bits, etc. */
static int dbgu_set_attributes(int minor, const struct termios *t)
{
return 0;
}
/***********************************************************************/
/*
* The following functions are not used by TERMIOS, but other RTEMS
* functions use them instead.
*/
/***********************************************************************/
/*
* Read from UART. This is used in the exit code, and can't
* rely on interrupts.
*/
int dbgu_poll_read(int minor)
{
return dbgu_read(minor);
}
/*
* Write a character to the console. This is used by printk() and
* maybe other low level functions. It should not use interrupts or any
* RTEMS system calls. It needs to be very simple
*/
static void _BSP_put_char( char c ) {
dbgu_write_polled(0, c);
}
BSP_output_char_function_type BSP_output_char = _BSP_put_char;
下面是uarts.c的代码:
console_tbl *BSP_get_uart_from_minor(int minor)
{
return &Console_Port_Tbl[minor];
}
至此,搞定调试串口驱动。