linux台式机 ttys0 驱动程序,Linux下串口通讯 - Linux设备驱动编程总结_Linux编程_Linux公社-Linux系统门户网站...

用过开发板的朋友们都知道,UART在一个芯片中是很珍贵的资源,它可以被配置为485功能,也可以配置232功能,而在工业通讯中,485,232都是常用的通讯方式。

这里多说一句题外话:一直没有找到linux比较好的编辑工具,vim虽然功能强大,但是没有鼠标,总感觉不爽,gedit虽然有鼠标,但是缩进功能实在是不行,这里推荐一款本人一直用的,感觉用的不错,就是大家熟悉的eclipse for c++,这个有Linux版本,用起来虽然没有windows下那么爽,但是比大多数(至少笔者用过的)都好用。

首先说,Linux的串口通讯和我们逻辑编程的编程方法是不同的:

裸机下需要配置寄存器,然后如果中断方式,设置中断函数,如果查询方式,就查询状态位,判断是否有数据过来,如果要发送数据则就是往发送寄存器发送数据,然后就自动发出去了。

Linux下可不同,那些所谓的驱动都已经写好了,我们需要调用的是Linux统一封装的数据结构和函数来操作串口,对于不同的芯片,只要是Linux操作系统,都可以用这一类型的东西操作串口。

在Linux中所有串口配置数据都可以用struct termios来描述,里面有许多的成员函数,像我们配置波特率,数据位长度和奇偶校验,具体配置方法可以参考这篇程:http://www.linuxidc.com/Linux/2007-12/9815p3.htm

下面是笔者写的程序,大家可以拿来当做模板来使用,主要实现对UART0的操作:

#include 

#include 

#include 

#include 

#include 

#include 

//为了保证用户输入的波特率是个正确的值,所以需要这两个数组验证,对于设置波特率时候,前面要加个B

intspeed_arr[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300,

B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300, };

intname_arr[] = {115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300,

115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300, };

/*-----------------------------------------------------------------------------

函数名:      set_speed

参数:        int fd ,int speed

返回值:      void

描述:        设置fd表述符的串口波特率

*-----------------------------------------------------------------------------*/

voidset_speed(intfd ,intspeed)

{

structtermios opt;

inti;

intstatus;

tcgetattr(fd,&opt);

for(i = 0;i 

{

if(speed == name_arr[i])//找到标准的波特率与用户一致

{

tcflush(fd,TCIOFLUSH);//清除IO输入和输出缓存

cfsetispeed(&opt,speed_arr[i]);//设置串口输入波特率

cfsetospeed(&opt,speed_arr[i]);//设置串口输出波特率

status = tcsetattr(fd,TCSANOW,&opt);//将属性设置到opt的数据结构中,并且立即生效

if(status != 0)

perror("tcsetattr fd:");//设置失败

return;

}

tcflush(fd,TCIOFLUSH);//每次清除IO缓存

}

}

/*-----------------------------------------------------------------------------

函数名:      set_parity

参数:        int fd

返回值:      int

描述:        设置fd表述符的奇偶校验

*-----------------------------------------------------------------------------*/

intset_parity(intfd)

{

structtermios opt;

if(tcgetattr(fd,&opt) != 0)//或许原先的配置信息

{

perror("Get opt in parity error:");

return-1;

}

/*通过设置opt数据结构,来配置相关功能,以下为八个数据位,不使能奇偶校验*/

opt.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP

| INLCR | IGNCR | ICRNL | IXON);

opt.c_oflag &= ~OPOST;

opt.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);

opt.c_cflag &= ~(CSIZE | PARENB);

opt.c_cflag |= CS8;

tcflush(fd,TCIFLUSH);//清空输入缓存

if(tcsetattr(fd,TCSANOW,&opt) != 0)

{

perror("set attr parity error:");

return-1;

}

return0;

}

/*-----------------------------------------------------------------------------

函数名:      serial_init

参数:        char *dev_path,int speed,int is_block

返回值:      初始化成功返回打开的文件描述符

描述:        串口初始化,根据串口文件路径名,串口的速度,和串口是否阻塞,block为1表示阻塞

*-----------------------------------------------------------------------------*/

intserial_init(char*dev_path,intspeed,intis_block)

{

intfd;

intflag;

flag = 0;

flag |= O_RDWR;//设置为可读写的串口属性文件

if(is_block == 0)

flag |=O_NONBLOCK;//若为0则表示以非阻塞方式打开

fd = open(dev_path,flag);//打开设备文件

if(fd 

{

perror("Open device file err:");

close(fd);

return-1;

}

/*打开设备文件后,下面开始设置波特率*/

set_speed(fd,speed);//考虑到波特率可能被单独设置,所以独立成函数

/*设置奇偶校验*/

if(set_parity(fd) != 0)

{

perror("set parity error:");

close(fd);//一定要关闭文件,否则文件一直为打开状态

return-1;

}

returnfd;

}

/*-----------------------------------------------------------------------------

函数名:      serial_send

参数:        int fd,char *str,unsigned int len

返回值:      发送成功返回发送长度,否则返回小于0的值

描述:        向fd描述符的串口发送数据,长度为len,内容为str

*-----------------------------------------------------------------------------*/

intserial_send(intfd,char*str,unsignedintlen)

{

intret;

if(len > strlen(str))//判断长度是否超过str的最大长度

len = strlen(str);

ret = write(fd,str,len);

if(ret 

{

perror("serial send err:");

return-1;

}

returnret;

}

/*-----------------------------------------------------------------------------

函数名:      serial_read

参数:        int fd,char *str,unsigned int len,unsigned int timeout

返回值:      在规定的时间内读取数据,超时则退出,超时时间为ms级别

描述:        向fd描述符的串口接收数据,长度为len,存入str,timeout 为超时时间

*-----------------------------------------------------------------------------*/

intserial_read(intfd,char*str, unsignedintlen, unsignedinttimeout)

{

fd_set rfds;

structtimeval tv;

intret;//每次读的结果

intsret;//select监控结果

intreadlen = 0;//实际读到的字节数

char* ptr;

ptr = str;//读指针,每次移动,因为实际读出的长度和传入参数可能存在差异

FD_ZERO(&rfds);//清除文件描述符集合

FD_SET(fd,&rfds);//将fd加入fds文件描述符,以待下面用select方法监听

/*传入的timeout是ms级别的单位,这里需要转换为struct timeval 结构的*/

tv.tv_sec  = timeout / 1000;

tv.tv_usec = (timeout%1000)*1000;

/*防止读数据长度超过缓冲区*/

//if(sizeof(&str) 

//  len = sizeof(str);

/*开始读*/

while(readlen 

{

sret = select(fd+1,&rfds,NULL,NULL,&tv);//检测串口是否可读

if(sret == -1)//检测失败

{

perror("select:");

break;

}

elseif(sret > 0)                         //检测成功可读

{

ret = read(fd,ptr,1);

if(ret 

{

perror("read err:");

break;

}

elseif(ret == 0)

break;

readlen += ret;//更新读的长度

ptr     += ret;//更新读的位置

}

else//超时

{

printf("timeout!\n");

break;

}

}

returnreadlen;

}

intmain()

{

intfd;

intret;

charstr[]="hello linux serial!";

charbuf[100];

fd =  serial_init("/dev/ttyS1",115200,1);

if(fd 

{

perror("serial init err:");

return-1;

}

ret = serial_send(fd,str,22);

printf("send %d bytes!\n",ret);

serial_read(fd,buf,100,5000);

printf("the buf is :%s\n",buf);

close(fd);

return0;

}

做几点简要的说明:

1.高级的一点芯片,如ARM9一般有个UART口专门用于打印调试信息,其对应的设备文件是ttyS0,然后UART0对应    ttyS1,以此类推,笔者这里打开的是ttyS1那就是UART0

2.可以看到一点,我定义了一个struct termios opt,对UART的所有配置信息都是在这个结构体中完成。

3.还是那句,发送和接受数据就和读写文件一样简单,发送就是write操作,读就是read操作

4.在读的时候,笔者用的是循环查询的方式,在Linux中,一般用的方法可以创建一个线程,用这个线程来循环调用select方法,来反复查询UART对应的设备文件是否可读(对应于是否有数据从串口传入),这种阻塞方式不是盲目等待,后来有调度机制,可以代替裸机编程中的中断方式。

下面是运行结果:

5f9c34e60ff6d63e8faac17f906db459.png

e9b6caf3a53db3cd6ed4c3757bec4cbf.png0b1331709591d260c1c78e86d0c51c18.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值