一、 串口API
在Linux系统中,操作设备的统一接口就是:open/ioctl/read/write。
对于UART,又在ioctl之上封装了很多函数,主要是用来设置行规程。
所以对于UART,编程的套路就是:
- open
- 设置行规程,比如波特率、数据位、停止位、检验位、RAW模式、一有数据就返回
- read/write
怎么设置行规程?行规程的参数用结构体termios来表示,可以参考Linux串口—struct termios结构体
typedef unsigned char cc_t;
typedef unsigned int speed_t;
typedef unsgined int tcflag_t;
#define NCCS 19
struct termios {
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
};
这些函数在名称上有一些惯例:
- tc: terminal control
- cf: control flag
函数名 | 作用 |
---|---|
tcgetattr | get terminal attributes,获得终端的属性 |
tcsetattr | set terminal attributes,修改终端参数 |
tcflush | 清空终端未完成的输入/输出请求及数据 |
cfsetispeed | sets the input baud rate,设置输入波特率 |
cfsetospeed | sets the output baud rate,设置输出波特率 |
cfsetspeed | 同时设置输入、输出波特率 |
函数不多,主要是需要设置好termios中的参数,这些参数很复杂,可以参考Linux串口—struct termios结构体。
二、编程
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
int set_opt(int fd, int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newtio, oldtio;
if(tcgetattr(fd, &oldtio) != 0) {
perror("SetupSerial 1");
return -1;
}
bzero(&newtio, sizeof(newtio));
newtio.c_cflag |= CLOCAL|CREAD;
newtio.c_cflag &= ~CSIZE;
newtio.c_lflag &= ~(ICANON|ECHO|ECHOE|ISIG);
newtio.c_oflag &= ~OPOST;
switch(nBits) {
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
}
switch(nEvent) {
case 0:
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
break;
case 'E':
newtio.c_iflag |= (INPCK|ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
break;
case 'N':
newtio.c_cflag &= ~PARENB;
break;
}
switch(nSpeed) {
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
default:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
}
if(nStop == 1)
newtio.c_cflag &= ~CSTOPB;
else if(nStop == 2)
newtio.c_cflag |= CSTOPB;
newtio.c_cc[VMIN] = 1;
newtio.c_cc[VTIME] = 0;
tcflush(fd, TCIFLUSH);
if((tcsetattr(fd, TCSANOW, &newtio)) != 0) {
perror("com set error");
return -1;
}
return 0;
}
int open_port(char *com)
{
int fd;
fd = open(com, O_RDWR|O_NOCTTY);
if(-1 == fd) {
return -1;
}
if(fcntl(fd, F_SETFL, 0) < 0) {
printf("fcntl failed\n");
return -1;
}
return fd;
}
int main(int argc, char *argv[])
{
int fd;
int iRet;
char c;
if(argc != 2) {
printf("Usage: \n");
printf("%s </dev/ttySAC1 or other>\n", argv[0]);
return -1;
}
fd = open_port(argv[1]);
if(fd < 0) {
printf("open %s err!\n", argv[1]);
return -1;
}
iRet = set_opt(fd, 115200, 8, 'N', 1);
if(iRet) {
printf("set port err!\n");
return -1;
}
printf("Enter a char: ");
while(1) {
scanf("%c", &c);
iRet = write(fd, &c, 1);
iRet = read(fd, &c, 1);
if(iRet == 1) {
printf("get: %02x %c\n", c, c);
} else {
printf("can not get data\n");
}
}
return 0;
}
三、上机实验
短接串口的RX和TX
root@npi:~/test# ./a.out /dev/ttymxc2
Enter a cahr: a
get: 61 a
get: 0a
get: 0a
get: 0a
a
get: 61 a
get: 0a