前言
串口是我们特别常用的一个通讯接口,在Linux下的串口编程使用的就是文件IO的操作,所以说这次来进行串口的应用编程,实现发送接收功能
实战Linux串口编程
在单片机串口编程中,需要用户直接对寄存器以及中断进行控制
而在Linux串口编程中,无论是官方直接下载的原生内核还是厂家提供的Linux内核都会将串口驱动写好,所以不需要自己写串口驱动
Linux串口应用编程使用原厂提供的接口进行初始化配置以及发送和接收
串口基本介绍
一次只传送一个数据位,以单个bit的方式传输
一般特指RS232
流控
两个串行接口之间的传输数据流通常需要协调一致才行,这可能是由于用于通信的某个串行接口或者某些存介质的中间通信链路的限制造成的
软件流控制:采用特殊字符开始和结束,开始(XON,DC1,八进制数021),结束(XOFF,DC3,八进制数023),这些字符都在ASCII中定义好了,对于传输文本信息非常有用,不能用于在特殊程序中的其他类型信息
硬件流控制:使用RS-232标准的CTS和RTS信号来取代之前的特殊字符,当准备就绪时接收方会将CTS设置为space电压,尚未就绪时设置为mark电压;相应的发送方会在准备就绪时将RTS设置为space电压。
软件流控的速度更快,但并不是所有的硬件和操作系统都支持CTS/RTS流控制
串口编程的流程
打开串口:一般使用open函数,打开后返回句柄,这个句柄就可以提供给发送和接收函数使用
初始化串口:需要设置波特率、校验位、数据位、停止位等一系列的参数,初始化比较麻烦,要知道如何进行研制和配置
发送和接收数据:属于字符设备,可以使用read函数和write函数实现
关闭:使用函数close即可关闭串口
设置开机启动
在/etc/init.d/rcS最后添加要执行的程序
打开串口
确认设备节点
在4412开发板中设备节点使用的是ttySAC*系列
有四个串口
uartopen.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
main(){
int fd;
char *leds = "/dev/leds";
char *test1 = "/bin/test1";
char *test2 = "/bin/test2";
if((fd = open(leds,O_RDWR|O_NOCTTY|O_NDELAY))<0){
printf("open %s failed!\n",leds);
}
printf("\n%s fd is %d\n",leds,fd);
if((fd = open(test1,O_RDWR,0777))<0){
printf("open %s failed!\n",test1);
}
printf("%s fd is %d\n",test1,fd);
if((fd = open(test2,O_RDWR|O_CREAT,0777))<0){
printf("open %s failed!\n",test2);
}
printf("%s fd is %d\n",test2,fd);
}
运行结果
串口初始化参数介绍
常用的:串口号、波特率、数据位、停止位、校验位、流控
查看/arch/arm/include/asm中的termios.h
struct termio {
unsigned short c_iflag; /* input mode flags */
unsigned short c_oflag; /* output mode flags */
unsigned short c_cflag; /* control mode flags */
unsigned short c_lflag; /* local mode flags */
unsigned char c_line; /* line discipline */
unsigned char c_cc[NCC]; /* control characters */
};
c_iflag输入模式标志
c_oflag输出模式标志
c_cflag控制模式标志
c_lflag本地模式标志
串口初始化常用函数
头文件<termios.h><unistd.h>
函数tcgetattr
读取当前穿裤的参数值,一般用于先确认该串口是否能配置,做检测用
int tcgetattr(int fd,struct termios *termios_p)
先创建一个termios结构体来存储旧的参数
波特率相关函数
函数cfsetisspeed和cdsetosspeed用于修改串口的波特率,函数cfgetisspeed和cfgetosspeed用于获取当前的波特率
设置函数
int cfsetisspeed(struct termios *termios_p,speed_t speed);
speed:波特率,常用的B2400,B4800,B9600,B115200等,
int cdsetosspeed(struct termios *termios_p,speed_t speed);
获取函数
speed_t cfgetisspeed(const struct termios *termios_p)
speed_t cfgetosspeed(const struct termios *termios_p)
函数tcflush
清空缓存寄存器,清空串口中没有完成的输入或者输出数据,在接收和发送数据时,串口寄存器会缓存数据
int tcflush(int fd,int queue_selector)
fd是open返回的文件句柄
queue_selector控制tcflush的操作
三个常用的数值,TCIFLUSH清除正受到的数据,且不会读取出来;TCOFLUSH清除正写入的数据,且不会发送至终端;TCIOFLUSH清除所有正在发生的IO数据
函数tcsetattr
int tcsetattr(int fd,int optional_actions,const struct termios*termios_p)
optional_actions:参数生效时间
三个常用的值:TCSANOW,不等数据传输完毕就立即改变属性;TCSADRAIN,等待所有数据传输结束才改变属性;TCSAFLUSH,清空输入输出缓冲区才改变属性
termios_p在旧的参数基础上修改后的参数
初始化流程分析
串口发送
uartwrite.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
int set_opt(int,int,int,char,int);
void main()
{
int fd,wr_static,i=10;
char *uart3 = "/dev/ttySAC3";
char *buffer = "hello world!\n";
printf("\r\nitop4412 uart3 writetest start\r\n");
if((fd = open(uart3, O_RDWR|O_NOCTTY|O_NDELAY))<0){
printf("open %s is failed",uart3);
}
else{
printf("open %s is success\n",uart3);
set_opt(fd, 115200, 8, 'N', 1);
//发送十次数据
while(i--)
{
wr_static = write(fd,buffer, strlen(buffer));
if(wr_static<0)
printf("write failed\n");
else{
printf("wr_static is %d\n",wr_static);
}
sleep(1);
}
}
close(fd);
}
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newtio,oldtio;//定义结构体newtio和oldtio
//测试串口能否运行并将原串口的数据取到oldtio
if ( tcgetattr( fd,&oldtio) != 0) {
perror("SetupSerial 1");
return -1;
}
//将newio清零和设置c_cflag
bzero( &newtio, sizeof( newtio ) );
newtio.c_cflag |= CLOCAL | CREAD;//使能接收和忽略控制线
newtio.c_cflag &= ~CSIZE;
//设置数据位
switch( nBits )
{
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
}
//设置校验位
switch( nEvent )
{
//偶校验
case 'O':
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;
case 460800:
cfsetispeed(&newtio, B460800);
cfsetospeed(&newtio, B460800);
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[VTIME] = 0;//不设置读取超时
newtio.c_cc[VMIN] = 0;//读取最小字符数为0
tcflush(fd,TCIFLUSH);//清空缓冲区
//进行初始化设置
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("com set error");
return -1;
}
// printf("set done!\n\r");
return 0;
}
运行结果(使用串口助手)
串口接收
uartread.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
int set_opt(int,int,int,char,int);
//"/dev/ttySAC3"是con2,靠近耳机接口的串口
void main()
{
int fd,nByte;
char *uart3 = "/dev/ttySAC3";
char buffer[512];
char *uart_out = "please input\r\n";
memset(buffer, 0, sizeof(buffer));
if((fd = open(uart3, O_RDWR|O_NOCTTY))<0)
printf("open %s is failed",uart3);
else{
set_opt(fd, 115200, 8, 'N', 1);
//发送输入提醒
write(fd,uart_out, strlen(uart_out));
while(1){
//如果串口读到数据
while((nByte = read(fd, buffer, 512))>0){
buffer[nByte+1] = '\0';//在数据后加结束符
write(fd,buffer,strlen(buffer));//把接收到的发送回去
memset(buffer, 0, strlen(buffer));//将接受buffer清空
nByte = 0;
}
}
}
}
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;
switch( nBits )
{
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
}
switch( nEvent )
{
case 'O':
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;
case 460800:
cfsetispeed(&newtio, B460800);
cfsetospeed(&newtio, B460800);
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[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH);
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("com set error");
return -1;
}
// printf("set done!\n\r");
return 0;
}
运行结果