【轉】Linux C下非特定波特率的配置和使用
阿新 • • 發佈:2019-01-14
https://blog.csdn.net/jinhongdu/article/details/43413071
對於非標準的任意波特率需要用ioctl(fd, TIOCGSERIAL, p)和ioctl(fd, TIOCSSERIAL, p)的配合,ioctl的最後一個引數是struct serial_struct *型別,在linux/serial.h中定義。
其中baud_base是基準晶振頻率/16,通常是115200,你需要設的是custom_divisor這個值,最終的波特率為baud_base/custom_divisor,比如你需要28800,因為115200/4=28800,所以要設定custom_divisor= 4,。
具體過程為,先設定波特率設為38400(tcsetattr),然後用TIOCGSERIAL得到當前的設定,將flags設定ASYNC_SPD_CUST位,設定custom_divisor,最後用TIOCSSERIAL設定。
使用setserial其實就是利用上述方法,來設定baud_base, custom_divisor等, 其內部實現就是使用ioctl來進行設定。
網上的東西真的是參差不齊,希望能呈現完善的正確的Blog給大家。
由於是測試程式碼,只是保證可以執行。另外推薦一個串列埠除錯助手AccessPort,可以提供28800的串列埠位元率作為測試。
#include <termios.h>
#include <sys/ioctl.h>
#include <stdio.h> /*標準輸入輸出定義*/
#include <stdlib.h> /*標準函式庫定義*/
#include <unistd.h> /*Unix標準函式定義*/
#include <sys/types.h> /**/
#include <sys/stat.h> /**/
#include <fcntl.h> /*檔案控制定義*/
#include <termios.h> /*PPSIX終端控制定義*/
#include <errno.h> /*錯誤號定義*/
#include <linux/serial.h>
#define TRUE 1
#define FALSE 0
/*
*功能:用於測試非標準波特率串列埠。
*此程式碼僅限於執行在X86架構的環境下,其他架構並未測試。在arm下未測試
*請在root下編譯此程式碼
*如有問題,聯絡我:靳小都 hellojinhongdu#126.com
*/
struct serial_t {
int fd;
char *device;/*/dev/ttyS0,...*/
int baud;
int databit;/*5,6,7,8*/
char parity;/*O,E,N*/
int stopbit;/*1,2*/
int startbit;/*1*/
struct termios options;
};
int speed_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_speed(int fd, int speed)
{
int i;
int status;
struct termios Opt;
tcgetattr(fd, &Opt);
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++)
{
if (speed == name_arr[i])
{
tcflush(fd, TCIOFLUSH);
cfsetispeed(&Opt, speed_arr[i]);
cfsetospeed(&Opt, speed_arr[i]);
status = tcsetattr(fd, TCSANOW, &Opt);
if (status != 0)
perror("tcsetattr fd1");
return;
}
tcflush(fd,TCIOFLUSH);
}
}
//設定為特訴波特率,比如28800
int serial_set_speci_baud(struct serial_t *tty,int baud)
{
struct serial_struct ss,ss_set;
tcgetattr(tty->fd,&tty->options);
cfsetispeed(&tty->options,B38400);
cfsetospeed(&tty->options,B38400);
tcflush(tty->fd,TCIFLUSH);/*handle unrecevie char*/
tcsetattr(tty->fd,TCSANOW,&tty->options);
if((ioctl(tty->fd,TIOCGSERIAL,&ss))<0){
printf("BAUD: error to get the serial_struct info:%s\n",strerror(errno));
return -1;
}
ss.flags = ASYNC_SPD_CUST;
ss.custom_divisor = ss.baud_base / baud;
printf("ss.custom_divisor = %d \r\n",ss.custom_divisor);
if((ioctl(tty->fd,TIOCSSERIAL,&ss))<0){
printf("BAUD: error to set serial_struct:%s\n",strerror(errno));
//return -2;
}
ioctl(tty->fd,TIOCGSERIAL,&ss_set);
printf("BAUD: success set baud to %d,custom_divisor=%d,baud_base=%d\n",
baud,ss_set.custom_divisor,ss_set.baud_base);
return 0;
}
/*get serial's current attribute*/
static int serial_get_attr(struct serial_t *tty)
{
if(tcgetattr(tty->fd,&tty->options) != 0){
printf("SERIAL: can't get serial's attribute\n");
return -1;
}
return 0;
}
/*update serial's attrbute*/
static int serial_attr_update(struct serial_t *tty)
{
tcflush(tty->fd,TCIFLUSH);/*handle unrecevie char*/
if((tcsetattr(tty->fd,TCSANOW,&tty->options)) < 0){
return -1;
}
return 0;
}
static int serial_init_databit(struct serial_t *tty)
{
if(serial_get_attr(tty)<0)
return -1;
tty->options.c_cflag &= ~CSIZE;
switch(tty->databit){
case 5: tty->options.c_cflag |= CS5;break;
case 6: tty->options.c_cflag |= CS6;break;
case 7: tty->options.c_cflag |= CS7;break;
case 8: tty->options.c_cflag |= CS8;break;
default:
printf("SERIAL: unsupported databit %d\n",tty->databit);
return -2;
}
if(serial_attr_update(tty) < 0)
return -3;
printf("SERIAL: set databit to %d\n",tty->databit);
return 0;
}
static int serial_init_parity(struct serial_t *tty)
{
if(serial_get_attr(tty)<0)
return -1;
/*ignore framing and parity error*/
tty->options.c_iflag = IGNPAR;
switch (tty->parity){
case 'n':
case 'N':
/* Clear parity enable */
tty->options.c_cflag &= ~PARENB;
/* Enable parity checking */
tty->options.c_iflag &= ~INPCK;
break;
case 'o':
case 'O':
/* 設定為奇校檢*/
tty->options.c_cflag |= (PARODD|PARENB);
/* Disnable parity checking */
tty->options.c_iflag |= (INPCK|ISTRIP);
break;
case 'e':
case 'E':
/* Enable parity */
tty->options.c_cflag |= PARENB;
/* 轉換為偶效驗*/
tty->options.c_cflag &= ~PARODD;
/* Disnable parity checking */
tty->options.c_iflag |= (INPCK|ISTRIP);
break;
default:
printf("SERIAL: unsupported parity %c\n",tty->parity);
return -2;
}
if(serial_attr_update(tty) < 0)
return -3;
printf("SERIAL: set parity to %c\n",tty->parity);
return 0;
}
static int serial_init_stopbit(struct serial_t *tty)
{
if(serial_get_attr(tty)<0)
return -1;
switch(tty->stopbit){
case 1:
tty->options.c_cflag &= ~CSTOPB;break;
case 2:
tty->options.c_cflag |= CSTOPB;break;
default:
printf("SERIAL: unsupported stopbit %d\n",tty->stopbit);
return -2;
}
if(serial_attr_update(tty) < 0)
return -3;
printf("SERIAL: set stopbit to %d\n",tty->stopbit);
return 0;
}
/**
*@brief 設定串列埠資料位,停止位和效驗位
*@param fd 型別 int 開啟的串列埠檔案控制代碼*
*@param databits 型別 int 資料位 取值 為 7 或者8*
*@param stopbits 型別 int 停止位 取值為 1 或者2*
*@param parity 型別 int 效驗型別 取值為N,E,O,,S
*/
int set_Parity(int fd,int databits,int stopbits,int parity)
{
struct termios options;
if ( tcgetattr( fd,&options) != 0)
{
perror("SetupSerial 1");
return(FALSE);
}
options.c_cflag &= ~CSIZE;
switch (databits) /*設定資料位數*/
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unsupported data size\n");
return (FALSE);
}
switch (parity)
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 設定為奇效驗*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 轉換為偶效驗*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
fprintf(stderr,"Unsupported parity\n");
return (FALSE);
}
/* 設定停止位*/
switch (stopbits)
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr,"Unsupported stop bits\n");
return (FALSE);
}
/* Set input parity option */
if (parity != 'n')
options.c_iflag |= INPCK;
options.c_cc[VTIME] = 150; // 15 seconds
options.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return (FALSE);
}
return (TRUE);
}
//用法:只要指定serial_t的baud就可以了
static struct serial_t __seri_conf[] = {
[0] = {//connect with b board, ttyS0
.device = "/dev/ttyS1",
.baud = 28800,
.databit = 8,
.parity = 'N',
.stopbit = 1,
},
};
/**
*@breif main()
*/
int main(int argc, char **argv) {
int fd;
int nread;
char buff[512];
fd = open(__seri_conf->device, O_RDWR | O_NOCTTY | O_NONBLOCK);
__seri_conf->fd = fd;
serial_set_speci_baud(__seri_conf, __seri_conf->baud);
/*
if (set_Parity(__seri_conf->fd, 8, 1, 'N') == FALSE)
{
printf("Set Parity Error\n");
}
*/
if(serial_init_databit(__seri_conf)<0)
printf("serial_init_databit error\n");
if(serial_init_parity(__seri_conf)<0)
printf("serial_init_parity error\n");
if(serial_init_stopbit(__seri_conf)<0)
printf("serial_init_stopbit error\n");
//struct termios opt;
tcgetattr(__seri_conf->fd,&__seri_conf->options);
__seri_conf->options.c_iflag &=~(BRKINT|ICRNL|INPCK|ISTRIP|IXON);
__seri_conf->options.c_lflag &=~(ICANON|ECHO|ECHOE|ECHONL|ISIG|IEXTEN);
__seri_conf->options.c_oflag &=~(OPOST);
if(tcsetattr(__seri_conf->fd,TCSANOW,&__seri_conf->options)!=0)
printf("error");
while (1) {
char ct[10] = "hello";
write(__seri_conf->fd, ct, 10);
puts("hello world 28800 test 8888!\n");
sleep(1);
nread = read(__seri_conf->fd,buff,256);
if(nread>0)
{
printf("recv :%d ***%s\r\n",nread,buff);
}
}
close(fd);
}
對於非標準的任意波特率需要用ioctl(fd, TIOCGSERIAL, p)和ioctl(fd, TIOCSSERIAL, p)的配合,ioctl的最後一個引數是struct serial_struct *型別,在linux/serial.h中定義。
其中baud_base是基準晶振頻率/16,通常是115200,你需要設的是custom_divisor這個值,最終的波特率為baud_base/custom_divisor,比如你需要28800,因為115200/4=28800,所以要設定custom_divisor=
具體過程為,先設定波特率設為38400(tcsetattr),然後用TIOCGSERIAL得到當前的設定,將flags設定ASYNC_SPD_CUST位,設定custom_divisor,最後用TIOCSSERIAL設定。
使用setserial其實就是利用上述方法,來設定baud_base, custom_divisor等, 其內部實現就是使用ioctl來進行設定。
網上的東西真的是參差不齊,希望能呈現完善的正確的Blog給大家。
由於是測試程式碼,只是保證可以執行。另外推薦一個串列埠除錯助手AccessPort,可以提供28800的串列埠位元率作為測試。
#include <termios.h>
#include <sys/ioctl.h>
#include <stdio.h> /*標準輸入輸出定義*/
#include <stdlib.h> /*標準函式庫定義*/
#include <unistd.h> /*Unix標準函式定義*/
#include <sys/types.h> /**/
#include <sys/stat.h> /**/
#include <fcntl.h> /*檔案控制定義*/
#include <termios.h> /*PPSIX終端控制定義*/
#include <errno.h> /*錯誤號定義*/
#include <linux/serial.h>
#define TRUE 1
#define FALSE 0
/*
*功能:用於測試非標準波特率串列埠。
*此程式碼僅限於執行在X86架構的環境下,其他架構並未測試。在arm下未測試
*請在root下編譯此程式碼
*如有問題,聯絡我:靳小都 hellojinhongdu#126.com
*/
struct serial_t {
int fd;
char *device;/*/dev/ttyS0,...*/
int baud;
int databit;/*5,6,7,8*/
char parity;/*O,E,N*/
int stopbit;/*1,2*/
int startbit;/*1*/
struct termios options;
};
int speed_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_speed(int fd, int speed)
{
int i;
int status;
struct termios Opt;
tcgetattr(fd, &Opt);
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++)
{
if (speed == name_arr[i])
{
tcflush(fd, TCIOFLUSH);
cfsetispeed(&Opt, speed_arr[i]);
cfsetospeed(&Opt, speed_arr[i]);
status = tcsetattr(fd, TCSANOW, &Opt);
if (status != 0)
perror("tcsetattr fd1");
return;
}
tcflush(fd,TCIOFLUSH);
}
}
//設定為特訴波特率,比如28800
int serial_set_speci_baud(struct serial_t *tty,int baud)
{
struct serial_struct ss,ss_set;
tcgetattr(tty->fd,&tty->options);
cfsetispeed(&tty->options,B38400);
cfsetospeed(&tty->options,B38400);
tcflush(tty->fd,TCIFLUSH);/*handle unrecevie char*/
tcsetattr(tty->fd,TCSANOW,&tty->options);
if((ioctl(tty->fd,TIOCGSERIAL,&ss))<0){
printf("BAUD: error to get the serial_struct info:%s\n",strerror(errno));
return -1;
}
ss.flags = ASYNC_SPD_CUST;
ss.custom_divisor = ss.baud_base / baud;
printf("ss.custom_divisor = %d \r\n",ss.custom_divisor);
if((ioctl(tty->fd,TIOCSSERIAL,&ss))<0){
printf("BAUD: error to set serial_struct:%s\n",strerror(errno));
//return -2;
}
ioctl(tty->fd,TIOCGSERIAL,&ss_set);
printf("BAUD: success set baud to %d,custom_divisor=%d,baud_base=%d\n",
baud,ss_set.custom_divisor,ss_set.baud_base);
return 0;
}
/*get serial's current attribute*/
static int serial_get_attr(struct serial_t *tty)
{
if(tcgetattr(tty->fd,&tty->options) != 0){
printf("SERIAL: can't get serial's attribute\n");
return -1;
}
return 0;
}
/*update serial's attrbute*/
static int serial_attr_update(struct serial_t *tty)
{
tcflush(tty->fd,TCIFLUSH);/*handle unrecevie char*/
if((tcsetattr(tty->fd,TCSANOW,&tty->options)) < 0){
return -1;
}
return 0;
}
static int serial_init_databit(struct serial_t *tty)
{
if(serial_get_attr(tty)<0)
return -1;
tty->options.c_cflag &= ~CSIZE;
switch(tty->databit){
case 5: tty->options.c_cflag |= CS5;break;
case 6: tty->options.c_cflag |= CS6;break;
case 7: tty->options.c_cflag |= CS7;break;
case 8: tty->options.c_cflag |= CS8;break;
default:
printf("SERIAL: unsupported databit %d\n",tty->databit);
return -2;
}
if(serial_attr_update(tty) < 0)
return -3;
printf("SERIAL: set databit to %d\n",tty->databit);
return 0;
}
static int serial_init_parity(struct serial_t *tty)
{
if(serial_get_attr(tty)<0)
return -1;
/*ignore framing and parity error*/
tty->options.c_iflag = IGNPAR;
switch (tty->parity){
case 'n':
case 'N':
/* Clear parity enable */
tty->options.c_cflag &= ~PARENB;
/* Enable parity checking */
tty->options.c_iflag &= ~INPCK;
break;
case 'o':
case 'O':
/* 設定為奇校檢*/
tty->options.c_cflag |= (PARODD|PARENB);
/* Disnable parity checking */
tty->options.c_iflag |= (INPCK|ISTRIP);
break;
case 'e':
case 'E':
/* Enable parity */
tty->options.c_cflag |= PARENB;
/* 轉換為偶效驗*/
tty->options.c_cflag &= ~PARODD;
/* Disnable parity checking */
tty->options.c_iflag |= (INPCK|ISTRIP);
break;
default:
printf("SERIAL: unsupported parity %c\n",tty->parity);
return -2;
}
if(serial_attr_update(tty) < 0)
return -3;
printf("SERIAL: set parity to %c\n",tty->parity);
return 0;
}
static int serial_init_stopbit(struct serial_t *tty)
{
if(serial_get_attr(tty)<0)
return -1;
switch(tty->stopbit){
case 1:
tty->options.c_cflag &= ~CSTOPB;break;
case 2:
tty->options.c_cflag |= CSTOPB;break;
default:
printf("SERIAL: unsupported stopbit %d\n",tty->stopbit);
return -2;
}
if(serial_attr_update(tty) < 0)
return -3;
printf("SERIAL: set stopbit to %d\n",tty->stopbit);
return 0;
}
/**
*@brief 設定串列埠資料位,停止位和效驗位
*@param fd 型別 int 開啟的串列埠檔案控制代碼*
*@param databits 型別 int 資料位 取值 為 7 或者8*
*@param stopbits 型別 int 停止位 取值為 1 或者2*
*@param parity 型別 int 效驗型別 取值為N,E,O,,S
*/
int set_Parity(int fd,int databits,int stopbits,int parity)
{
struct termios options;
if ( tcgetattr( fd,&options) != 0)
{
perror("SetupSerial 1");
return(FALSE);
}
options.c_cflag &= ~CSIZE;
switch (databits) /*設定資料位數*/
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unsupported data size\n");
return (FALSE);
}
switch (parity)
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 設定為奇效驗*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 轉換為偶效驗*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
fprintf(stderr,"Unsupported parity\n");
return (FALSE);
}
/* 設定停止位*/
switch (stopbits)
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr,"Unsupported stop bits\n");
return (FALSE);
}
/* Set input parity option */
if (parity != 'n')
options.c_iflag |= INPCK;
options.c_cc[VTIME] = 150; // 15 seconds
options.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return (FALSE);
}
return (TRUE);
}
//用法:只要指定serial_t的baud就可以了
static struct serial_t __seri_conf[] = {
[0] = {//connect with b board, ttyS0
.device = "/dev/ttyS1",
.baud = 28800,
.databit = 8,
.parity = 'N',
.stopbit = 1,
},
};
/**
*@breif main()
*/
int main(int argc, char **argv) {
int fd;
int nread;
char buff[512];
fd = open(__seri_conf->device, O_RDWR | O_NOCTTY | O_NONBLOCK);
__seri_conf->fd = fd;
serial_set_speci_baud(__seri_conf, __seri_conf->baud);
/*
if (set_Parity(__seri_conf->fd, 8, 1, 'N') == FALSE)
{
printf("Set Parity Error\n");
}
*/
if(serial_init_databit(__seri_conf)<0)
printf("serial_init_databit error\n");
if(serial_init_parity(__seri_conf)<0)
printf("serial_init_parity error\n");
if(serial_init_stopbit(__seri_conf)<0)
printf("serial_init_stopbit error\n");
//struct termios opt;
tcgetattr(__seri_conf->fd,&__seri_conf->options);
__seri_conf->options.c_iflag &=~(BRKINT|ICRNL|INPCK|ISTRIP|IXON);
__seri_conf->options.c_lflag &=~(ICANON|ECHO|ECHOE|ECHONL|ISIG|IEXTEN);
__seri_conf->options.c_oflag &=~(OPOST);
if(tcsetattr(__seri_conf->fd,TCSANOW,&__seri_conf->options)!=0)
printf("error");
while (1) {
char ct[10] = "hello";
write(__seri_conf->fd, ct, 10);
puts("hello world 28800 test 8888!\n");
sleep(1);
nread = read(__seri_conf->fd,buff,256);
if(nread>0)
{
printf("recv :%d ***%s\r\n",nread,buff);
}
}
close(fd);
}