《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,
},
};