RTEMS 的 AT91SAM9260 移植(5): 调试串口驱动
其實到這里,只要能保證編譯通過,再修改一下鏈接腳本,其實已經可以跑在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的源代碼:
/* * Console driver for AT91SAM9260 DBGU port * */ #include <bsp.h> #include <rtems/libio.h> #include <termios.h> #include <at91sam9260.h> #include <rtems/bspIo.h> #include <libchip/serial.h> #include <libchip/sersupp.h> #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 driver for AT9260 * * This driver uses the shared console driver in * ...../libbsp/shared/console.c * */ #include <bsp.h> #include <rtems/libio.h> #include <termios.h> #include <rtems/bspIo.h> #include <at91sam9260.h> #include <libchip/serial.h> #include <libchip/sersupp.h> /* How many serial ports? */ #define NUM_DEVS 1 /* These are used by code in console.c */ unsigned long Console_Port_Count = NUM_DEVS; console_data Console_Port_Data[NUM_DEVS]; /* rtems console uses the following minor number */ rtems_device_minor_number Console_Port_Minor = 0; extern console_fns dbgu_fns; /* * There's one item in array for each UART. * * Some of these fields are marked "NOT USED". They are not used * by console.c, but may be used by drivers in libchip * * when we add other types of UARTS we will need to move this * structure to a generic uart.c file with only this in it */ console_tbl Console_Port_Tbl[] = { { "/dev/console", /* sDeviceName */ SERIAL_CUSTOM, /* deviceType */ &dbgu_fns, /* pDeviceFns */ NULL, /* deviceProbe */ NULL, /* pDeviceFlow */ 0, /* ulMargin - NOT USED */ 0, /* ulHysteresis - NOT USED */ NULL, /* pDeviceParams */ AT91C_BASE_DBGU, /* ulCtrlPort1 - Pointer to DBGU regs */ 0, /* ulCtrlPort2 - NOT USED */ 0, /* ulDataPort - NOT USED */ NULL, /* getRegister - NOT USED */ NULL, /* setRegister - NOT USED */ NULL, /* getData - NOT USED */ NULL, /* setData - NOT USED */ 0, /* ulClock - NOT USED */ 0 /* ulIntVector - NOT USED */ }}; console_tbl *BSP_get_uart_from_minor(int minor) { return &Console_Port_Tbl[minor]; }?
?
?
至此,搞定調試串口驅動。
?
?
轉載于:https://blog.51cto.com/coolbacon/1280034
總結
以上是生活随笔為你收集整理的RTEMS 的 AT91SAM9260 移植(5): 调试串口驱动的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL Server中如何取得刚插入的标
- 下一篇: 数据恢复西藏之旅--硬盘也有高原反应