天天看點

linux下序列槽的使用

《ARM Linux開發-warewin 2G/3G無線傳輸(DTU)和路由器—筆記》

1、序列槽定義

串行接口簡稱序列槽,也稱串行通信接口(UART),是采用串行通信方式的擴充接口。

2、Linux下的使用

在Linux下操作、控制序列槽是通過操作裝置檔案進行的,可在/dev目錄下看到序列槽裝置檔案,如ttyS0、ttyS1等。在應該程式中操作序列槽可進行以下步驟:

l 打開序列槽

int comfd;

comfd = open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NONBLOCK);

l 設定序列槽波特率

set_baud(comfd , 9600); //設定波特率為9600

set_baud()函數定義如下:

int baud_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,

                    B38400, B19200, B9600, B4800, B2400, B1200, B300, };

int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, 

                                 19200, 9600, 4800, 2400, 1200, 300, };

void set_baud(int fd, int baud){

      int  i;

      int  status;

      struct termios  Opt;

      tcgetattr(fd, &Opt);

      for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) {

             if (baud== name_arr[i]) {    

                    tcflush(fd, TCIOFLUSH);    

                    cfsetispeed(&Opt, baud_arr[i]); 

                    cfsetospeed(&Opt, baud_arr[i]);  

                    status = tcsetattr(fd, TCSANOW, &Opt); 

                    if (status != 0) {       

                           perror("tcsetattr fd"); 

                           return;    

                    }   

                    tcflush(fd,TCIOFLUSH);  

             } 

      }

}

l 設定序列槽資訊

struct termios tio;

if ( tcgetattr( fd,&options) != 0) {

perror("SetupSerial 1");    

return 0; 

}

set_Parity(&tio,8,1,'N'); //設定資料位為8,停止位為1,無校驗

set_Flag(&tio,10,0,0);//設定VTIME時間為10,VMIN大小為0,無流控

set_Parity函數如下:

void set_Parity(struct termios *tio,uint32_t databit,uint32_t stopbit,char parity)

{

tio->c_cflag &= ~CSIZE;

switch (databit) 

{

case 7:

   tio->c_cflag |= CS7;

   break;

case 8:

   tio->c_cflag |= CS8;

   break;

default:

   fprintf(stderr,"Unsupported data size%d\n",databit);

   return;

}

switch (parity)

{

case 'n':

case 'N':

   tio->c_cflag &= ~PARENB;   

   tio->c_iflag &= ~INPCK;     

   break;

case 'o':

case 'O':

   tio->c_cflag |= (PARODD | PARENB);  

   tio->c_iflag |= INPCK;             

   break;

case 'e':

case 'E':

   tio->c_cflag |= PARENB;     

   tio->c_cflag &= ~PARODD;    

   tio->c_iflag |= INPCK;       

   break;

case 's':

case 'S': 

   tio->c_cflag &= ~PARENB;

   tio->c_cflag &= ~CSTOPB;

   break;

default:

   fprintf(stderr,"Unsupported parity\n");

   return;

   }

switch (stopbit)

{

case 1:

   tio->c_cflag &= ~CSTOPB;

   break;

case 2:

   tio->c_cflag |= CSTOPB;

   break;

default:

   fprintf(stderr,"Unsupported stop bits\n");

   return;

}

}

set_Flag()函數如下:

void set_Flag(struct termios *tio,uint32_t timeout,uint32_t bufout,uint32_t fcon)

{

switch (fcon) 

{

case 0:

   tio->c_cflag&=~CRTSCTS;

   break;

case 1:

   tio->c_cflag |= CRTSCTS;

   break;

default:

   fprintf(stderr,"Unsupported FlowControl\n");

   return;

}  

tio->c_lflag=0;

tio->c_cflag|=CREAD|CLOCAL;

tio->c_iflag|=IGNPAR;

tio->c_cc[VTIME]=timeout;

tio->c_cc[VMIN]=bufout;

}

注:函數中的VTIME指定了等待的時間,VMIN指定了讀取字元的最小數量。它們不同組合地取值會得到不同的結果,分别如下:

(1)當VTIME>0,VMIN>0時。read調用将保持阻塞直到讀取到第一個字元,讀到了第一個字元之後開始計時,此後若時間到了VTIME或者時間未到但已讀夠了VMIN個字元則會傳回;若在時間未到之前又讀到了一個字元(但此時讀到的總數仍不夠VMIN)則計時重新開始。

(2)當VTIME>0,VMIN=0時。read調用讀到資料則立即傳回,否則将為每個字元最多等待VTIME時間。

(3) 當VTIME=0,VMIN>0時。read調用一直阻塞,直到讀到VMIN個字元後立即傳回。

(4)若在open或fcntl設定了O_NDELALY或O_NONBLOCK标志,read調用不會阻塞而是立即傳回,那麼VTIME和VMIN就沒有意義,效果等同于與把VTIME和VMIN都設為了0。

l 使用序列槽讀寫資料:

    char iobuf[100];

read(comfd, iobuf, 50);//讀50個子節資料

write(comfd, iobuf, 50);//寫50個子節資料

l 關閉序列槽

close(comfd);

3、注冊多個序列槽

以核心版本為Linux-2.6.36、AT91SAM9260平台為例,核心預設注冊了3個序列槽,一個終端調試序列槽ttyS0,兩個232序列槽ttyS1、ttyS2,現在再注冊ttyS3、ttyS4這兩個序列槽,在核心源碼/linux-2.6.36/arch/arm/mach-at91/board-sam9260ek.c中的__init ek_map_io()函數中添加以下紅色字型:

static void __init ek_map_io(void)

{

at91sam9260_initialize(18432000);

at91_register_uart(0, 0, 0);

at91_register_uart(AT91SAM9260_ID_US0,1,ATMEL_UART_CTS| ATMEL_UART_RTS | ATMEL_UART_DTR | ATMEL_UART_DSR | ATMEL_UART_DCD | ATMEL_UART_RI);

at91_register_uart(AT91SAM9260_ID_US1,2,ATMEL_UART_CTS| ATMEL_UART_RTS);

at91_register_uart(AT91SAM9260_ID_US2, 3, 0);

at91_register_uart(AT91SAM9260_ID_US3, 4, ATMEL_UART_RTS);

at91_set_serial_console(0);

}

    重新編譯核心,下載下傳到裝置上,進入系統後,在/dev目錄下會看到新生成的ttyS3、ttyS4這兩個裝置節點,在應用程式中便可使用這兩個序列槽。

4、設定485序列槽

   以設定ttyS4為485序列槽為例,在核心源碼/linux-2.6.36/drivers/serial/atmel_serial.c的atmel_set_termios()函數中添加以下紅色字型:

static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,

      struct ktermios *old)

{

unsigned long flags;

unsigned int mode, imr, quot, baud;

mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL

| ATMEL_US_NBSTOP | ATMEL_US_PAR);

if (port->line==4){

mode = UART_GET_MR(port) & ~(ATMEL_US_USMODE);

mode |= ATMEL_US_USMODE_RS485;

}

baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);

quot = uart_get_divisor(port, baud);

.........................................................................

.........................................................................

}

    在使用485序列槽傳輸資料中,測試發現有亂碼的出現,需要把485序列槽DMA模式去掉,在/linux-2.6.36/arch/arm/mach-at91/at91sam9260_devices.c檔案中把atmel_uart_data uart3_data結構中use_dma_tx、use_dma_rx的值由1改為0,另外添加以下紅色字型代碼、在發送資料時增加延時處理,如以下代碼所示:

static struct atmel_uart_data uart3_data = {

.use_dma_tx = 0,

.use_dma_rx = 0,

//20120301add

.rs485= {

.flags = SER_RS485_ENABLED|SER_RS485_RTS_AFTER_SEND|SER_RS485_RTS_BEFORE_SEND,

.delay_rts_before_send=5,

.delay_rts_after_send= 5,

},

};

繼續閱讀