我在研究NIOS的UART時有遇到一些問題,做一下分享。
Nios II在讀取周邊設備資訊時,可以用輪詢跟中斷方式兩種方式來讀取資料。
測試前的前置作業:
1.在Qsys中加入一個UART,並在Quartus中將新增的UART裝置的RX、TX對接。
問題一: 使用輪詢方式讀程式
當我用以下程式碼去輪詢時,會發現須等到第62筆資料傳完後,status暫存器的RRDY才會正常判斷。
1 /* 2 * UART test: polling test, design by Shih-An Li 3 * 4 */ 5 6 #include7 #include 8 #include "altera_avalon_uart.h" 9 #include "altera_avalon_uart_regs.h" 10 #include "alt_types.h" 11 #include "sys/alt_irq.h" 12 #include 13 #include 14 15 16 int main(void) 17 { 18 int rxdata=0,k=0,t=0; 19 unsigned int status; 20 21 IOWR_ALTERA_AVALON_UART_CONTROL(UART_TX_BASE, 0x1C0); // clear status 22 23 while(1) 24 { 25 status =IORD_ALTERA_AVALON_UART_STATUS(UART_TX_BASE); // read status register 26 if(status & ALTERA_AVALON_UART_STATUS_TRDY_MSK){ // check trdy flag 27 if(status & 0x040){// transmit shift register is empty 28 printf("Uart ready (0x%2x) and send %3d ", status, t); 29 IOWR(UART_TX_BASE,1, t++); // send tx data 30 } 31 } 32 33 usleep(100000); 34 status =IORD_ALTERA_AVALON_UART_STATUS(UART_TX_BASE); 35 printf(" status=0x%x %d\n", status,k++); 36 if (status & ALTERA_AVALON_UART_STATUS_RRDY_MSK) // check RRDY flag 37 { 38 rxdata = IORD(UART_TX_BASE,0); 39 printf("Your character rxd is:\t%d %d\n", rxdata, k); 40 } 41 42 } 43 return 0; 44 }
執行結果如下:
1 Uart ready (0x60) and send 59 status=0x60 59 2 Uart ready (0x60) and send 60 status=0x60 60 3 Uart ready (0x60) and send 61 status=0x60 61 4 Uart ready (0x60) and send 62 status=0xe0 62 5 Your character rxd is: 62 63 6 Uart ready (0x60) and send 63 status=0xe0 63 7 Your character rxd is: 63 64 8 Uart ready (0x60) and send 64 status=0xe0 64 9 Your character rxd is: 64 65 10 Uart ready (0x60) and send 65
讓我覺得很好奇的是去抓他硬體波形來看,發現只讀一次
status =IORD_ALTERA_AVALON_UART_STATUS(UART_TX_BASE);
結果硬體居然送出了好幾次的chip_select訊號,而Fig.1最後那次的chip_select居然是讀取addr 0 位置RXDATA暫存器,因此會把rx_char_ready訊號拉下,之後Fig2會在連續讀取幾次address 2位置的status暫存器,此時已經變成0x60了。
一直要到62筆之後才會正常收值。
Fig 1. UART波形
Fig 2. 接續Fig 1. 波形
方法二: 使用中斷方式
程式碼如下,使用Nios II HAL模式來讀寫資料。
此模式會用ISR函數來判斷RRDY旗標使否為1,是則去收值,因此判斷上比較準確。
1 /* 2 * Nios使用HAL指令,開啟以及讀寫UART設備, design by Shih-An Li 3 */ 4 5 #include6 #include 7 #include // define usleep() 8 #include // define NULL 9 #include // define O_NONBLOCK 10 #define BUF_SIZE 128 11 12 13 int main() 14 { 15 int iCount, iUart_rxcount; 16 int fdterm; // FILEDESCRIPTOR RETURNED BY OPEN 17 FILE *fpterm; // FILE pointer returned by fdopen 18 unsigned char uCUart1_rxbuffer[255], ucTX[128]; 19 // O_ACCMODE all access mode 20 fdterm = open("/dev/uart_tx", O_ACCMODE | O_NONBLOCK); 21 fpterm = fdopen(fdterm,"rw+"); 22 if(fpterm){ 23 fprintf(fpterm,"uart_tx STARTED\r\n"); // check initial output 24 iUart_rxcount = fread(&uCUart1_rxbuffer, 1, sizeof(uCUart1_rxbuffer), fpterm); 25 usleep(100000); 26 printf("received:%s\r\n",uCUart1_rxbuffer); 27 } 28 ucTX[0]=0; 29 while(1) { 30 write(fdterm, &ucTX[0], 1); // write tx value to uart 31 ucTX[0]++; // tx value + 1 32 // read uart data to rxbuffer and return receive data number 33 iUart_rxcount = read(fdterm, &uCUart1_rxbuffer, sizeof(uCUart1_rxbuffer)); 34 for(iCount=0; iCount ) { 35 // fprintf(fpterm,"received:%s\r\n",uart1_rxbuffer); 36 printf("%d\n",uCUart1_rxbuffer[iCount]); 37 } 38 usleep(10000); 39 } 40 return 0; 41 }
執行結果如下
received:uart_tx STARTED 0 1 2 3 4 5 6 7 8
因此推薦還是以HAL方式來讀取周邊資料比較不會有資料lost問題。
source code 下載