linux串口示例

LINUX线程初探,linux初探

LINUX程序设计最重要的当然是进程与线程。本文主要以uart程序结合键盘输入控制uart的传输。

硬件平台:树莓派B

软件平台:raspberry

需要工具:USB转TTL(PL2303) GCC

最近看了下linux的串口,发现还是蛮容易的

程序设计

 首先声明,在LINUX中已经集成了PL2303的驱动,不用装驱动。

做一些总结和记录

串口简介

串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用。常用的串口是 RS-232-C 接口(又称 EIA RS-232-C)它是在 1970 年由美国电子工业协会(EIA)联合贝尔系统、 调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。它的全名是"数据终端设备(DTE)和数据通讯设备(DCE)之间串行二进制数据交换接口技术标准"该标准规定采用一个 25 个脚的 DB25 连接器,对连接器的每个引脚的信号内容加以规定,还对各种信号的电平加以规定。传输距离在码元畸变小于 4% 的情况下,传输电缆长度应为 50 英尺。

Linux 操作系统从一开始就对串行口提供了很好的支持,本文就 Linux 下的串行口通讯编程进行简单的介绍,如果要非常深入了解,建议看看本文所参考的 《Serial Programming Guide for POSIX Operating Systems》

 串口操作

串口操作需要的头文件

太阳集团所有网址16877 1#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> /*错误号定义*/ View Code

 

【这篇文章也重在备份和记录,代码都是套用别人的 ,所以基本只是罗列了些代码,但保证代码可用】

打开串口

在 Linux 下串口文件是位于 /dev 下的

串口一 为 /dev/ttyUSB0

串口二 为 /dev/ttyUSB1

打开串口是通过使用标准的文件打开函数操作:

太阳集团所有网址16877 2int fd; /*太阳集团所有网址16877,以读写方式打开串口*/ fd = open( "/dev/ttyS0", O_RDWR); if (-1 == fd){ /* 不能打开串口一*/ perror(" 提示错误!"); } View Code

 

其实串口操作也就那么几步

设置串口

最基本的设置串口包括波特率设置,效验位和停止位设置。

串口的设置主要是设置 struct termios 结构体的各成员值。

太阳集团所有网址16877 3struct termio { unsigned short c_iflag; /* 输入模式标志 */ unsigned short c_oflag; /* 输出模式标志 */ unsigned short c_cflag; /* 控制模式标志*/ unsigned short c_lflag; /* local mode flags */ unsigned char c_line; /* line discipline */ unsigned char c_cc[NCC]; /* control characters */ }; View Code

 

设置这个结构体很复杂,我这里就只说说常见的一些设置:

波特率设置

下面是修改波特率的代码:

太阳集团所有网址16877 4struct termios Opt; tcgetattr(fd, &Opt); cfsetispeed(&Opt,B19200); /*设置为19200Bps*/ cfsetospeed(&Opt,B19200); tcsetattr(fd,TCANOW,&Opt); View Code

设置波特率的例子函数:

/**
*@brief  设置串口通信速率
*@param  fd     类型 int  打开串口的文件句柄
*@param  speed  类型 int  串口速度
*@return  void
*/
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(fd1, TCSANOW, &Opt);  
      if  (status != 0) {        
        perror("tcsetattr fd1");  
        return;     
      }    
      tcflush(fd,TCIOFLUSH);   
    }  
  }
}

 

效验位和停止位的设置:

无效验 8位 Option.c_cflag &= ~PARENB;  Option.c_cflag &= ~CSTOPB;  Option.c_cflag &= ~CSIZE;  Option.c_cflag |= ~CS8;
奇效验(Odd) 7位 Option.c_cflag |= ~PARENB;  Option.c_cflag &= ~PARODD;  Option.c_cflag &= ~CSTOPB;  Option.c_cflag &= ~CSIZE;  Option.c_cflag |= ~CS7;
偶效验(Even) 7位 Option.c_cflag &= ~PARENB;  Option.c_cflag |= ~PARODD;  Option.c_cflag &= ~CSTOPB;  Option.c_cflag &= ~CSIZE;  Option.c_cflag |= ~CS7;
Space效验 7位 Option.c_cflag &= ~PARENB;  Option.c_cflag &= ~CSTOPB;  Option.c_cflag &= &~CSIZE;  Option.c_cflag |= CS8;

设置效验的函数:

太阳集团所有网址16877 5/** *@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 sizen"); 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 parityn"); return (FALSE); } /* 设置停止位*/ switch (stopbits) { case 1: options.c_cflag &= ~CSTOPB; break; case 2: options.c_cflag |= CSTOPB; break; default: fprintf(stderr,"Unsupported stop bitsn"); return (FALSE); } /* Set input parity option */ if (parity != 'n') options.c_iflag |= INPCK; tcflush(fd,TCIFLUSH); options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/ options.c_cc[VMIN] = 0; /* Update the options and do it NOW */ if (tcsetattr(fd,TCSANOW,&options) != 0) { perror("SetupSerial 3"); return (FALSE); } return (TRUE); } set_Parity

需要注意的是:

如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用原始模式(Raw Mode)方式来通讯,设置方式如下:

options.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
options.c_oflag  &= ~OPOST;   /*Output*/

 

1.打开串口

读写串口

设置好串口之后,读写串口就很容易了,把串口当作文件读写就是。

  • 发送数据

    char  buffer[1024];int    Length;int    nByte;nByte = write(fd, buffer ,Length)
    
  • 读取串口数据

    使用文件操作read函数读取,如果设置为原始模式(Raw Mode)传输数据,那么read函数返回的字符数是实际串口收到的字符数。

    可以使用操作文件的函数来实现异步读取,如fcntl,或者select等来操作。

    char  buff[1024];int    Len;int  readByte = read(fd,buff,Len);
    

编程例子:

    int fd,fd1;
    int flag11;
    int nread;
    char buff[512]={};
    char *dev ="/dev/ttyUSB0";
        /*打开串口0*/
    fd = OpenDev(dev);    //open uart dev

    if (fd>0)
    {
            set_speed(fd,57600); //set uart baud speed 
        printf("success Open Serial Portn");
    }
    else
    {
        printf("Can't Open Serial Port!n");
        exit(0);
    }
    if (set_Parity(fd,8,1,'N')== FALSE)//set uart odd/even fomat 
    {
        printf("Set Parity Errorn");
        exit(1);
    }
    printf("Set Parity successn");
    printf("open keyboard  successn");


        /*向串口中写数据*/
        write(fd, "1,SVS,2,200rn", 16);  
        write(fd, "1,SVS,1,200rn", 16);  


         printf ("**********Now into while(1)**********n");
         while(1)
         {    
            /*循环读取其他设备传入的数据*/
           while((nread = read(fd,buff,512))>0)
           {    
                  //write(fd, "I am writo back", 16);
                  printf("nLen %dn",nread);
                  buff[nread 1]='\0';
                  printf("n%s",buff);
            }
          }       

 

2.设置参数

关闭串口

关闭串口就是关闭文件。

close(fd);

3.发送接收

 scanf线程

 由于scanf是阻塞程序,也就是在程序中出现了scanf的地方那么就要等待用户的输入,而不能执行其他的函数,如果我们加入线程处理,那么可以很好的解决这个问题。

 线程处理函数

/*线程执行函数*/
void *create(void *arg)
{
    unsigned char *a=0;
    char pet[20];//字符串
    while(1)
    {
        printf("please input the petn");
        scanf("%s",pet);
        printf("thread : pet = %sn",pet);
    }
       return (void *)0;
}

main函数

int main( int argc, char** argv )
{
    pthread_t tidp;
        int error;
        struct menber b={0};
    printf("I am start n");

    unsigned char a;
    /*创建线程并运行线程执行函数*/
    error = pthread_create(&tidp, NULL, create, (void *)&b);
            if( error )
            {
                printf("phread is not created...n");
                return -1;
            }

    while(1)
    {

        /*阻塞等待线程退出*/
            //pthread_join(tidp, NULL);
        printf("main functionn"); //住函数运行,不影响线程
        sleep(1);

    }


    return 0;
}

 

4.按需关闭

参考

  

    LINUX下串口编程入门:

    

    APUE第11章  

   

而根据函式提供的形式,

Linux C编程一站式学习中对于线程的一到思考题,高人指点

配置文件为 conf.txt
测试代码如下,注意链接的时候加上 -lpthread 这个参数

#include <stdio.h>
#include <errno.h> //perror()
#include <pthread.h>

#include <unistd.h> //sleep()
#include <time.h> // time()
#include <stdlib.h> //rand()

#define FD "conf.txt"

typedef void *(*fun)(void *);

struct my_struct
{
unsigned time_to_wait;
int n;
};

void *test_thread(struct my_struct *);

int main (int argc, char const *argv[])
{
FILE *fp = fopen(FD, "r");
if (fp == NULL)
{
perror(FD);
return -1;
}

srand((unsigned)time(NULL)); //初始化随机种子

int thread_count;
fscanf(fp, "%d", &thread_count);
fclose(fp);

if (thread_count <= 0)
{
printf("线程数<1,退出程序。n");
return -1;
}

pthread_t *ptid = (pthread_t *)malloc(sizeof(pthread_t) * thread_count); //保存线程ID

int i;
for (i = 0; i < thread_count; i )
{
int tw = rand() % thread_count 1; //随机等待时间

struct my_struct * p = (struct my_struct *)malloc(sizeof(struct my_struct));
if (p == NULL)
{
perror("内存分配错误");
goto ERROR;
}
p->time_to_wait = tw;
p->n = i 1;

int rval = pthread_create(ptid i, NULL, (fun) test_thread, (void *)(p)); //注意这里的强制转换(两个)
if (rval != 0)
{
per......余下全文>>  

一般设置参数分两步进行【其实就是那么配置下,分几步都行,只是配合后面的代码了】

浅谈linux进程之间是怎通信的

有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点;【java培训】 2、管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信; 3、套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字;【学习java】 4、信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数); 5、共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥;【java学习】 6、信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。以上就是linux操作系统下进程之间的通信讲解,希望对广大学习者有所帮助。腾科IT教育软件学院是腾科IT教育集团旗下的全资子公司,是一家专业从事Java, Android, Oracle Java, Linux Java, 系统架构师等高端IT技能培训的专业机构。也是甲骨文授权的广州唯一一家正版Oracle Java学习中心!秉承六年办学理念,致力于提供全球领先的培训方案,着力打造IT行业精英人才。学校位于广州最繁华的金融商业中心天河区,地理位置优越,学校环境优美,教学设施完备,师资力量雄厚。  

LINUX程序设计最重要的当然是进程与线程。本文主要以uart程序结合键盘输入控制uart的传输。 硬件平台:树莓派B ...

[1]设置波特率

[2]设置数据格式

下面还是罗列一些代码

打开串口

int OpenDev(char *Dev)

{

int fd = open( Dev, O_RDWR );

if (-1 == fd)

{

perror("Can't Open Serial Port");

return -1;

}

else

return fd;

}

O_RDWR就是可读写的意思设置波特率

Code

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);

}

}

}

设置参数是用到了一个专用的结构体struct termios其实也没啥说的,就是通过它去配置串口参数罢了

注意tcflush,他是清空buffer用的,关于buffer这东西,里面其实挺饶,这里不多说明,只是要注意它清空的buffer并不是printf那类函式中所谓的缓冲。

另外那两个数组,其实可以弄的简单些,只不过懒得改了

有点像画刷的使用,旧的一般都爱保存起来,最后还要还原。

设置数据格式

int set_Parity(int fd,int databits,int stopbits,int parity)

{

struct termios options;

if ( tcgetattr( fd,&options) != 0) {

perror("SetupSerial 0");

return -1;

}

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 sizen");

return -1;

}

switch (parity)

{

case 'n':

case 'N':

options.c_cflag &= ~PARENB;

options.c_iflag &= ~INPCK;

break;

case 'o':

case 'O':

options.c_cflag |= (PARODD | PARENB);

options.c_iflag |= INPCK;

break;

case 'e':

case 'E':

options.c_cflag |= PARENB;

options.c_cflag &= ~PARODD;

options.c_iflag |= INPCK;

break;

case 'S':

case 's':

options.c_cflag &= ~PARENB;

options.c_cflag &= ~CSTOPB;break;

default:

fprintf(stderr,"Unsupported parityn");

return -1;

}

switch (stopbits)

{

case 1:

options.c_cflag &= ~CSTOPB;

break;

case 2:

options.c_cflag |= CSTOPB;

break;

default:

fprintf(stderr,"Unsupported stop bitsn");

return -1;

}

if (parity != 'n')

options.c_iflag |= INPCK;

tcflush(fd,TCIFLUSH);

options.c_cc[VTIME] = 150;

options.c_cc[VMIN] = 0;

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

{

perror("SetupSerial 3");

return -1;

}

return 0;

}

和前面的函式不同这里直接对struct termios进行操作,进而配置了数据位长度,校验位,停止位,超时等信息

最后整合下,列出一个测试用例

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

本文由太阳集团所有网址16877发布于www.16877.com,转载请注明出处:linux串口示例

您可能还会对下面的文章感兴趣: