在嵌入式應用領域中,串口是最為常見的一種硬體通信介面。因為其具備協議簡單,硬體電路精簡等優勢使得串口基本成為MCU、電腦或嵌入式產品的標配介面。本文僅介紹在Linux系統下串口編程需要使用的API和一些應用技巧,關於串口的背景知識介紹,以及Windows系統下串口編程讀者可以移步至其他文章。 Li ...
在嵌入式應用領域中,串口是最為常見的一種硬體通信介面。因為其具備協議簡單,硬體電路精簡等優勢使得串口基本成為MCU、電腦或嵌入式產品的標配介面。本文僅介紹在Linux系統下串口編程需要使用的API和一些應用技巧,關於串口的背景知識介紹,以及Windows系統下串口編程讀者可以移步至其他文章。
Linux系統下串口的操作主要分為如下部分:
- 串口打開、關閉
- 串口參數設置
- 串口數據發送與接收
- 串口MODEM信號設置與讀取
- 串口Break信號發送
可以熟練掌握並應用以上串口功能已經可以應對Linux系統上串口應用的大多數場景了,針對更高級的串口用法可以閱讀《Linux串口編程-進階篇》,包含Linux系統使用非標準波特率、同步等待Modem信號變化、串口參數VTIME和VMIN的作用、RS485串口功能開關等。為方便用戶使用我們將以上串口操作均封裝成了獨立的函數,可以極大的節約開發時間。
1、串口打開
/**
* libtty_open - open tty device
* @devname: the device name to open
*
* In this demo device is opened blocked, you could modify it at will.
*/
static int libtty_open(const char *devname)
{
int fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY);
int flags = 0;
if (fd < 0) {
perror("open device failed");
return -1;
}
/* 恢復串口為阻塞狀態 */
flags = fcntl(fd, F_GETFL, 0);
flags &= ~O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0) {
printf("fcntl failed.\n");
return -1;
}
/* 測試該設備是否為tty設備 */
if (isatty(fd) == 0) {
printf("not tty device.\n");
return -1;
} else
printf("tty device test ok.\n");
return fd;
}
Note:
- devname 參數為設備絕對路徑,如:“/dev/ttyUSB0”
- O_NOCTTY標誌用於通知系統,這個程式不會成為對應這個設備的控制終端。如果沒有指定這個標誌,那麼任何一個輸入(如SIGINT等)都將會影響用戶的進程;
- O_NDELAY標誌與O_NONBLOCK 等效,但這裡不僅僅是設置為非阻塞,還用於通知系統,這個程式不關心 DCD 信號線所處的狀態(即與設備相連的另一端是否激活或者停止)。如果用戶指定了這一標誌,則進程將會一直處在休眠狀態,直到 DCD 信號線被激活;
2、串口關閉
/**
* libtty_close - close tty device
* @fd: the device handle
*
* The function return 0 if success, others if fail.
*/
static int libtty_close(int fd)
{
return close(fd);
}
3、串口設置
/**
* libtty_setopt - config tty device
* @fd: device handle
* @speed: baud rate to set
* @databits: data bits to set
* @stopbits: stop bits to set
* @parity: parity to set
* @hardflow: hardflow to set
*
* The function return 0 if success, or -1 if fail.
*/
static int libtty_setopt(int fd, int speed, int databits, int stopbits, char parity, char hardflow)
{
struct termios newtio;
struct termios oldtio;
int i;
bzero(&newtio, sizeof(newtio));
bzero(&oldtio, sizeof(oldtio));
/* 先保存之前配置,以防後續步驟出錯無法恢復 */
if (tcgetattr(fd, &oldtio) != 0) {
perror("tcgetattr");
return -1;
}
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
/* 串口波特率設置*/
switch (speed): {
case 1200:
cfsetspeed(&newtio, B1200);
break;
case 2400:
cfsetspeed(&newtio, B2400);
break;
case 4800:
cfsetspeed(&newtio, B4800);
break;
case 9600:
cfsetspeed(&newtio, B9600);
break;
case 19200:
cfsetspeed(&newtio, B19200);
break;
case 38400:
cfsetspeed(&newtio, B38400);
break;
case 57600:
cfsetspeed(&newtio, B57600);
break;
case 115200:
cfsetspeed(&newtio, B115200);
break;
case 230400:
cfsetspeed(&newtio, B230400);
break;
case 460800:
cfsetspeed(&newtio, B460800);
break;
case 921600:
cfsetspeed(&newtio, B921600);
break;
default:
break;
}
for (