一.简介

  串口( UART)是一种非常常见的外设, 串口在嵌入式开发领域当中一般作为一种调试手段,通过串口将调试信息打印出来,或者通过串口发送指令给主机端进行处理;当然除了作为基本的调试手段之外,还可以通过串口与其他设备或传感器进行通信, 譬如有些 sensor 就使用了串口通信的方式与主机端进行数据交互。
根据电平标准的不同,串口可以分为 TTL, RS232,RS485, RS422等这些,虽然它们的电平标准不同,但是却都遵循相同的通信时序协议,所以呢,不管它是什么样的电平标准,对于我们软件而言其驱动程序都是一样的;一般主机端直接出来的电平信号就是 TTL
对于 ZYNQ 来说,其 PS 端只提供了两个串口外设 UART0 和 UART1,很明显对于大一点的工程而言是不够用的,此时可以使用 PL 端串口软核外设, xilinx 同样提供了相应的 IP核 可供调用如这次使用的UART16550

二.基础知识准备

  串口在嵌入式 Linux 系统当中经常作为系统的标准输入、输出设备,而提到串口,那么就不得不引出另外两个概念:终端与控制台。关于这两个概念,很多人估计是对此傻傻分不清!
  这里参考了原子文档里对终端的理解
  1、 什么是终端 Terminal
  终端就是处理主机输入、 输出的一套设备,它用来显示主机运算的输出,并且接受主机要求的输入。 典型的终端包括显示器键盘套件,打印机打字机套件等。 其实本质上也就一句话,能接受输入、能显示输出,这就够了,不管到了什么时代,终端始终扮演着人机接口的角色, 所谓 Terminal,即机器的边缘!
只要能提供给计算机输入和输出功能,它就是终端,而与其所在的位置无关。
  2、终端的分类
  ⚫ 本地终端:例如对于我们的个人 PC 机来说, PC 机连接了显示器、键盘以及鼠标等设备, 这样的一个显示器/键盘组合就是一个本地终端;同样对于开发板来说也是如此,开发板也可以连接一个LCD 显示器、键盘和鼠标等,同样可以构成本地终端。
  ⚫ 用串口连接的远程终端:对于嵌入式 Linux 开发来说,这是最常见的终端—串口终端。 譬如我们的开发板通过串口线连接到一个带有显示器和键盘的 PC 机, 在 PC 机通过运行一个终端模拟程序,譬如 Windows 超级终端、 putty、 MobaXterm、 SecureCRT 等来获取并显示开发板通过串口发出的数据、同样还可以通过这些终端模拟程序将用户数据通过串口线发送给开发板。
  ⚫ 基于网络的远程终端:譬如我们可以通过 ssh、 Telnet 这些协议登录到一个远程主机。以上列举的这些都是终端,前两类称之为物理终端; 最后一个称之为伪终端。 前两类都是在本地就直接关联了物理设备的, 譬如显示器、鼠标键盘、 串口等之类的,这种终端叫做物理终端,而第三类在本地则没有关联任何物理设备,注意,不要把物理网卡当成终端关联的物理设备,它们与终端并不直接相关,所以这类不直接关联物理设备的终端叫做伪终端。
  3、什么是控制台 Console
能够显示系统信息的终端就叫控制台。 控制台的概念与终端含义非常相近,其实现在我们经常用它们表示相同的东西, linux 中基本也已经淡化了控制台和终端的区别。虽然说它们之间的含义非常相近,但还是有一些区别:
  ⚫ 能够显示系统信息的终端就叫控制台,这说明它们之间是一个包含关系,控制台一定是终端,而终端则不一定是控制台,也就是说控制台是终端的真子集。
  ⚫ 控制台只有一个。看到这里大家可能就有疑问了,我们使用的开发板可以通过串口终端打印信息,同样我们也可以通过 ssh 协议登录连接到开发板,同样也会打开一个伪终端,并且也可以在伪终端显示打印信息,那么它俩不都可以认为是控制台吗?其实并非如此,上面说到的显示系统信息指的是开发板启动的时候,所有的打印系统信息都会显示到这个终端上,那么这个终端才叫做控制台,所有由此可以知道,譬如我们的开发板在启动时,所有的打印信息都会通过串口输出,所以我们的串口终端就是控制台, 而通过 ssh 远程登录开发板打开的伪终端并不是控制台,因为启动时的打印信息是不可能输出到这个伪终端上的。
  ⚫ 控制台是计算机本身的设备,一个计算机只有一个控制台。譬如开发板的串口这就是开发板本身的设备。
讲到这里相信大家都应该清楚了,它们之间的共同点和小小的区别,其实我们也不用去刻意区分它们之间的异同,因为 Linux 中它们之间的区别基本完全淡化了。
  4、 Linux 下终端对应的设备文件
在 Linux 当中,一切皆是文件。当然,终端也不例外,每一个终端设备在/dev 目录下都
有一个对应的设备文件。
  ⚫ /dev/ttyX 设备文件: tty( teletype 的简称) 是最令人熟悉的了,在 Linux 中, /dev/ttyX 代表的都是上述的物理终端,其中, /dev/tty1~/dev/tty63 代表的是本地终端,也就是接到本机的键盘显示器可以操作的终端。事实上 Linux 内核在初始化时会生成 63 个本地终端。
  ⚫ /dev/console 设备文件:这个设备文件表示当前系统的控制台,如果我们往这个设备文件输入信息,它一定会显示在我们的控制台终端中,譬如“echo Hello > /dev/console”。
  ⚫ 串口终端 ttyPSX:对于我们的调试版来说,有一个ps的串口,12个PL的串口所以也对应了13串口终端设备文件,如下所示:
【图片】
当然这里的名字 ttyPS0,ttyS0不是固定的,这个具体的命名跟串口的驱动程序有关,名字不是统一的,但是名字前缀一般都以“ tty”开头,以表明它是一个终端设备

三.Linux下的串口调用

3.1介绍

  串口的工作原理肯定是跟之前裸机中介绍到的谁一样的,重点在编程时linux对串口不同的调用方式
  Linux 系统中 UART 串口驱动框架结构图如下所示

  简单地说可以分为两层: UART 驱动层和 tty 驱动层。 从图中可以看到,下层 UART 驱动层直接与硬件相接触,也就是说它才是真正的 UART驱动程序,它提供了 UART 硬件操作相关函数集 uart_ops;而上层 tty 驱动层则会将 UART 设备描述成一个 tty(终端)设备, 并向内核注册 tty 字符设备,提供字符设备操作函数集ops,其实 ops 函数集中经过层层跳转最终执行的就是 uart 驱动层的 uart_ops。
首先对于驱动开发工程师来说, tty 驱动层并不需要我们去实现,它会在我们注册 UART
驱动的过程中自动构建出来,对于我们所使用的内核源码来说, xilinx 官方已经提供了串口驱动程序,所以只需要在应用层封装一层驱动了(真实令人感到偷懒和开心的消息)
(如果需要自己写的话,自行查找原子的免费开发文档吧,有很详细的讲解)

3.2 命令行调用测试

  老规矩,还是先使用命令行对串口进行一个简单的测试吧
第一步先要看我们有没有生成的tty串口设备,不确定是xilinx的默认设置还是怎么的,不论是交叉编译menuconfig还是官方编译的config kernel都对最大支持设备数有限制,我这边独到的最大值为4,所以需要改,配置内核时找到如下路径,对下面的最大支持设备改为自己的设备数,(没用官方推荐模式的可能默认连8250的设备树都没有支持,往下翻,找到并勾选)

  stty查看串口参数
  stty -F /dev/ttyS0 -a
  查看串口1(/dev/ttyS0)当前的参数,包括波特率、数据位等。
  stty设置串口参数
  stty -F /dev/ttyS0 ispeed 115200 ospeed 115200 cs8
  该命令将串口1(/dev/ttyS0)设置成115200波特率,8位数据模式。一般情况下设置这两个参数就可以了,如果显示数据乱码,可能还需要设置其它参数,使用man  查看stty其它设置选项
  cat打印串口数据
  cat /dev/ttyS0
  串口数据就可以在终端上显示了。
  发送数据
  echo “HelloWorld” >/dev/ttyS0

3.3测试代码

然后就是编写代码了,下面是增改可用的串口驱动头文件:

#ifndef _SERIAL_H_
#define _SERIAL_H_#include<stdio.h>      /*Standard input/output definitions*/
#include<stdlib.h>     /*Standard function library definitions*/
#include<unistd.h>     /*Unix Standard function definition*/
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>      /*File control definition*/
#include<termios.h>    /*PPSIX Terminal control definition*/    #include<errno.h>      /*Error number definition*/
#include<string.h>
#include<sys/time.h>#define FALSE  -1
#define TRUE   0  #define   B76800  0010020
#define   B153600 0010021
#define   B307200 0010022
#define   B614400 0010023
//
/*******************************************************************
* name:           serial_open
* function:       Opens the serial port and returns the serial device file description
* @param[in]:     port :serial number(ttyS0,ttyS1)
* @return:        Correct returns fd, error returns -1
*******************************************************************/
int serial_open(char* port);
/*******************************************************************
* name:           serial_init()
* function:       serial_init
* @param[in]:     fd     :   File descriptor
* @param[in]       speed  :   Serial speed
* @param[in]       flow_ctrl  Data flow control
* @param[in]       databits   The data bits , either 7 or 8
* @param[in]       stopbits   The stop bit , either 1 or 2
* @param[in]       parity     The value of effect type ,choose N,E,O, S
*
* return:        Correct returns 0, error returns -1
*******************************************************************/
int serial_init(int fd, int speed,int flow_ctrl,int databits,int stopbits,int parity);
/*******************************************************************
* name:           serial_recv
* function:       Receive serial data
* @param[in]:     fd:          File descriptor
*                 rcv_buf   :  The data from the receiving serial port is stored in the rcv_buf buffer
*                 data_len  :  The length of the data to read
*
* return:        The actual length of the data read
*******************************************************************/
int serial_recv(int fd, char *rcv_buf,int data_len);
/********************************************************************
* name:           serial_send
* function:       To send data
* @param[in]:     fd  :        File descriptor
*                 send_buf    :Stores serial port to send data
*                 data_len    :The length of the data to send
* return:        Correct returns 0, error returns -1
*******************************************************************/
int serial_send(int fd, char *send_buf,int data_len);
/*******************************************************************
* name:           serial_set
* function:       Set serial port data bit, stop bit and effect check bit
* @param[in]:   fd            File descriptor
*                speed         serial speed
*                flow_ctrl     Data flow control
*                databits      The data bits , either 7 or 8
*                topbits       The stop bit , either 1 or 2
*                parity        The value of effect type ,choose N,E,O, S
*return:         Correct returns 0, error returns -1
*******************************************************************/
int serial_set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity);
/*******************************************************************
* name:           serial_close
* function:       Closes the serial port and returns the serial device file description
* @param[in]:     fd    :File descriptor
* @param[in]:     port :serial number(ttyS0,ttyS1)
* @return:        void
*******************************************************************/  void serial_close(int fd);#endif

再然后是包含的库文件

#ifndef _PACKET_H
#define _PACKET_H#include <stdint.h>// #define DEBUG
#ifdef DEBUG
#define PRINT(fmt, ...)   printf(fmt, ##__VA_ARGS__)
#else
#define PRINT(fmt, ...)
#endiftypedef enum {UART_SND_REQ            = 0x11,UART_UPLOAD             = 0x22,UART_ATTR_SET_REQ       = 0x33,UART_ATTR_GET_REQ       = 0x44,UART_ATTR_GET_ACK       = 0x44,JTAG_CONNECT_SET_REQ    = 0x55,JTAG_CONNECT_GET_REQ    = 0x66,JTAG_CONNECT_GET_ACK    = 0x66,IP_ADDR_SET_REQ         = 0x77,IP_ADDR_GET_REQ         = 0x71,IP_ADDR_GET_ACK         = 0x71,SYSTEM_RESET            = 0x72
} MANAGE_TYPE;typedef struct {uint32_t    PackageLen;uint8_t     ManageType;
} __attribute__((__packed__)) HEADER;typedef struct {HEADER      header;uint8_t     PortID;uint8_t     Data[];
} __attribute__((__packed__)) UART_SND;typedef struct {HEADER      header;uint8_t   PortID;uint8_t  Baudrate;uint8_t        DataBit;uint8_t     StopBit;uint8_t     Pirity;uint8_t      FlowCtrl;uint8_t        CheckSum;
} __attribute__((__packed__)) UART_SET_REQ;typedef struct {HEADER      header;uint8_t   PortID;uint8_t      CheckSum;
} __attribute__((__packed__)) UART_GET_REQ;#define UART_GET_ACK UART_SET_REQ
#define JTAG_SET_REQ UART_GET_REQ
#define JTAG_INQ_REQ UART_GET_REQ   //Jtag connect status inquire request
#define JTAG_INQ_ACK UART_GET_REQ   //Jtag connect status inquire answer
#define IP_INQ_REQ   UART_GET_REQ
#define COMMON_ACK   UART_GET_REQ
#define IP_INQ_ACK   IP_SET_REQtypedef struct {HEADER      header;uint8_t       IpAddr[4];uint8_t       NetMask[4];uint8_t      GateWay[4];uint8_t      CheckSum;
} __attribute__((__packed__)) IP_SET_REQ;typedef struct {int sock;char status;uint8_t ip[20];
} TCP_CLIENT;#endif

之后就是驱动c文件了

#include "serial.h"
#include "packet.h"/*******************************************************************
* 名称:           serial_open
* 功能:           打开串口并返回串口设备文件描述
* 入口参数:        fd    :文件描述符     port :串口号(ttyS0,ttyS1)
* 出口参数:        正确返回为fd,错误返回为-1
*******************************************************************/
int serial_open(char* port)
{    int fd;fd = open( port, O_RDWR|O_NOCTTY);// fd = open( port, O_RDWR|O_NOCTTY|O_NDELAY);if (FALSE == fd)    {    perror("Can't Open Serial Port");    return(FALSE);    }    //恢复串口为阻塞状态                                   // if(fcntl(fd, F_SETFL, 0) < 0)    // {    //     PRINT("fcntl failed!\n");    //     return(FALSE);    // }         // else    // {    //     // PRINT("fcntl=%d\n",fcntl(fd, F_SETFL,0));    // }    // // //测试是否为终端设备        // // if(0 == isatty(STDIN_FILENO))    // // {    // //     PRINT("standard input is not a terminal device\n");    // //     return(FALSE);    // // }    // // else    // // {    // //     // PRINT("%s is a terminal device!\n", port);    // // }                  return fd;
}
/*******************************************************************
* 名称:           serial_close
* 功能:           关闭串口并返回串口设备文件描述
* 入口参数:        fd    :文件描述符     port :串口号(ttyS0,ttyS1)
* 出口参数:        void
*******************************************************************/    void serial_close(int fd)
{    close(fd);
}    /*******************************************************************
* 名称:           serial_set
* 功能:           设置串口数据位,停止位和效验位
* 入口参数:        fd        串口文件描述符
*                 speed     串口速度
*                 flow_ctrl   数据流控制
*                 databits   数据位   取值为 7 或者8
*                 topbits   停止位   取值为 1 或者2
*                 parity     效验类型 取值为N,E,O,,S
*出口参数:         正确返回为1,错误返回为0
*******************************************************************/
int serial_set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity)
{    int   i;    //int   status;    int   speed_arr[] = { B115200, B38400, B19200, B9600, B4800, B2400, B1200, B614400,B76800};    //int   name_arr[] = {115200, 38400, 19200,  9600,  4800,  2400,  1200,  300};  // int   name_arr[] = {4,  3, 2,  1,  0,  2400,  1200,  300};   int   name_arr[] = {115200, 38400, 19200,  9600,  4800,  2400,  1200,  614400,76800};struct termios options;/*tcgetattr(fd,&options)得到与fd指向对象的相关参数,并将它们保存于options,该函数还可以测试配置是否正确,该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1.  */    if( tcgetattr( fd,&options)  !=  0)    {    perror("SetupSerial 1");        return(FALSE);     }    //设置串口输入波特率和输出波特率    for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)    {    if  (speed == name_arr[i])    {                 cfsetispeed(&options, speed_arr[i]);   // printf("ispeed is %d----------------------------\n",speed_arr[i]);  cfsetospeed(&options, speed_arr[i]);   // printf("ospeed is %d----------------------------\n",speed_arr[i]);     }    }         //修改控制模式,保证程序不会占用串口    options.c_cflag |= CLOCAL;    //修改控制模式,使得能够从串口中读取输入数据    options.c_cflag |= CREAD;//清bit位 关闭字符映射 0x0a 0x0doptions.c_iflag &= ~(INLCR|ICRNL);//清bit位 关闭流控字符 0x11 0x13options.c_iflag &= ~(IXON);options.c_cflag |= CBAUDEX; //设置特定波特率的标志位.//设置数据流控制    switch(flow_ctrl)    {    case 0 ://不使用流控制    options.c_cflag &= ~CRTSCTS;    break;       case 1 ://使用硬件流控制    options.c_cflag |= CRTSCTS;    break;case 2 ://使用硬件流控制DTR/DSRoptions.c_cflag &= ~CRTSCTS;break;case 3 ://使用软件流控制    options.c_cflag |= IXON | IXOFF | IXANY;    break;    }    //设置数据位    //屏蔽其他标志位    options.c_cflag &= ~CSIZE;    switch (databits)    {      case 5    :    options.c_cflag |= CS5;    break;    case 6    :    options.c_cflag |= CS6;    break;    case 7    :        options.c_cflag |= CS7;    break;    case 8:        options.c_cflag |= CS8;    break;      default:       fprintf(stderr,"Unsupported data size\n");    return (FALSE);     }    //设置校验位    switch (parity)    {      case 0:    case 'N': //无奇偶校验位。    options.c_cflag &= ~PARENB;     options.c_iflag &= ~INPCK;        break;     case 1:      case 'O'://设置为奇校验        options.c_cflag |= (PARODD | PARENB);     options.c_iflag |= INPCK;                 break;     case 2:     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 parity\n");        return (FALSE);     }     // 设置停止位     switch (stopbits)    {      case 1:       options.c_cflag &= ~CSTOPB; break;     case 2:       options.c_cflag |= CSTOPB; break;    default:       fprintf(stderr,"Unsupported stop bits\n");     return (FALSE);    }    //修改输出模式,原始数据输出    options.c_oflag &= ~OPOST;    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);    //options.c_lflag &= ~(ISIG | ICANON);    //设置等待时间和最小接收字符    options.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */      options.c_cc[VMIN] = 32; /* 读取字符的最少个数为1 */    //如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读    tcflush(fd,TCIFLUSH);    //激活配置 (将修改后的termios数据设置到串口中)    if (tcsetattr(fd,TCSANOW,&options) != 0)      {    perror("com set error!\n");      return (FALSE);     }    return (TRUE);
}
/*******************************************************************
* 名称:         serial_init()
* 功能:         串口初始化
* 入口参数:      fd       :  文件描述符
*               speed  :  串口速度
*               flow_ctrl  数据流控制
*               databits   数据位   取值为 7 或者8
*               stopbits   停止位   取值为 1 或者2
*               parity     效验类型 取值为N,E,O,,S
*
* 出口参数:        正确返回为0,错误返回为-1
*******************************************************************/
int serial_init(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)
{       //设置串口数据帧格式    //if (serial_set(fd,19200,0,8,1,'N') == FALSE)if((speed < 0) || (speed > 10000000))return 1;if (serial_set(fd,speed,flow_ctrl,databits,stopbits,parity) == FALSE){return FALSE;}else    {    return  TRUE;    }
}    /*******************************************************************
* 名称:            serial_recv
* 功能:            接收串口数据
* 入口参数:         fd       :  文件描述符
*                  rcv_buf   :接收串口中数据存入rcv_buf缓冲区中
*                  data_len   :数据的长度
*
* 出口参数:        实际读到的长度
*******************************************************************/
int serial_recv(int fd, char *rcv_buf,int data_len)
{  int len,fs_sel;  fd_set fs_read;  struct timeval time;  FD_ZERO(&fs_read);  FD_SET(fd,&fs_read);  time.tv_sec = 10;  time.tv_usec = 0;  len = read(fd,rcv_buf,data_len);  // printf("I am right!(version1.2) len = %d fs_sel = %d\n",len,fs_sel);  return len;  //使用select实现串口的多路通信  /* 入口参数:①:ndfs:select() 中监视的文件句柄,一般设为要监视的文件中的最大文件号加一。②:rdfds:select()监视的可读文件句柄集合,当rdfds映象的文件句柄状态变成可读时系统告诉select函数返回。这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值,可以传入NULL值,表示不关心任何文件的读变化;③:wtfds: select()监视的可写文件句柄集合,当wtfds映象的文件句柄状态变成可写时系统告诉select函数返回。如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值,可以传入NULL值,表示不关心任何文件的写变化。④:exfds:select()监视的异常文件句柄集合,当exfds映象的文件句柄上有特殊情况发生时系统会告诉select函数返回。⑤:timeout:select()的超时结束时间 *//* FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。FD_CLR(int fd, fd_set *fdset):清除文件句柄fd与fdset的联系。FD_ISSET(int fd, fdset *fdset):检查fdset联系的文件句柄fd是否可读写,>0表示可读写。*/// fs_sel = select(fd+1,&fs_read,NULL,NULL,&time);  // // printf("fs_sel = %d\n",fs_sel);  // if(fs_sel)  // {  //     len = read(fd,rcv_buf,data_len);  //     // printf("I am right!(version1.2) len = %d fs_sel = %d\n",len,fs_sel);  //     return len;  // }  // else  // {  //     // printf("Sorry,I am wrong!");  //     return FALSE;  // }
}
/********************************************************************
* 名称:           serial_send
* 功能:           发送数据
* 入口参数:        fd                  :文件描述符
*                 send_buf    :存放串口发送数据
*                 data_len    :一帧数据的个数
* 出口参数:        正确返回为0,错误返回为-1
*******************************************************************/
int serial_send(int fd, char *send_buf,int data_len)
{    int len = 0;    len = write(fd,send_buf,data_len);    if (len == data_len )    {    PRINT("send data is %s\n",send_buf);  return TRUE;    }         else       {    tcflush(fd,TCOFLUSH);    return FALSE;    }    }

测试demo

仅仅是修改中,read是阻塞型的,所以测试的话直接while(1)吧,先写了一路,没开多线程

#include "packet.h"
#include "serial.h"
#include<stdio.h>      /*标准输入输出定义*/
#include<stdlib.h>     /*标准函数库定义*/
#include<unistd.h>     /*Unix 标准函数定义*/
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>      /*文件控制定义*/
#define __USE_MISC
#include<termios.h>    /*PPSIX 终端控制定义*/
#include<errno.h>      /*错误号定义*/
#include<string.h>
#include <sys/mman.h>
#include <poll.h>#define FALSE  -1
#define TRUE   0
#define UART_NUM 12void Uart_Out32(unsigned int * Addr, unsigned int Value)
{volatile unsigned int *LocalAddr = (volatile unsigned int *)Addr;*LocalAddr = Value;
}unsigned int * Uart_In32(unsigned int * Addr)
{return *(volatile unsigned int *) Addr;
}uint8_t *uartdev[UART_NUM] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS2", "/dev/ttyS3", "/dev/ttyS4", "/dev/ttyS5", "/dev/ttyS6", "/dev/ttyS7", "/dev/ttyS8", "/dev/ttyS9", "/dev/ttyS10",    "/dev/ttyS11",    };int main(int argc, char **argv)
{  int fd;                            //文件描述符  int err;                           //返回调用函数的状态  int len;                          int i;  char rcv_buf[0x100];         char send_buf[20]="HelloWorld";fd = serial_open(uartdev[0]); //打开串口,返回文件描述符  err = serial_init(fd,614400,0,8,1,'N');  printf("Set Port Exactly!\n");  for(i = 0;i < 3;i++)  {  len = serial_send(fd,send_buf,10);  if(len > 0)  printf(" %d time send %d data successful\n",i,len);  else  printf("send data failed!\n");  sleep(2);  }  // serial_close(fd);               while (1) //循环读取数据  {    memset(rcv_buf,0,256);len = serial_recv(fd, rcv_buf,100);  if(len > 0)  {  // rcv_buf[len] = '\0';  for(i = 0;i < len;i++)printf("receive data is 0x%x\n",rcv_buf[i]);  printf("len = %d\n",len);  }  else  {  printf("cannot receive data\n");  }  // sleep(2);  }              // serial_close(fd);   }

简单好用,对于波特率的修改和进阶在补充篇讲吧

zynq操作系统: Linux驱动开发串口篇相关推荐

  1. ip核在linux的驱动,Linux驱动开发笔记:对zynq PL部分IP核的驱动开发过程

    reg_addr = (((hopcount+1) #include * Xilinx RapidIO 3. 驱动设计 } #include Amba_pl对应PL部分的amba,devicetree ...

  2. linux驱动开发篇(三)—— 总线设备驱动模型

    linux系列目录: linux基础篇(一)--GCC和Makefile编译过程 linux基础篇(二)--静态和动态链接 ARM裸机篇(一)--i.MX6ULL介绍 ARM裸机篇(二)--i.MX6 ...

  3. linux驱动开发篇(四)—— platform平台设备驱动

    linux系列目录: linux基础篇(一)--GCC和Makefile编译过程 linux基础篇(二)--静态和动态链接 ARM裸机篇(一)--i.MX6ULL介绍 ARM裸机篇(二)--i.MX6 ...

  4. 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)

    这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...

  5. Linux nor flash分区,Linux驱动开发笔记:NOR FLASH编写实例

    1. 背景介绍 板子上的zynq通过emc外接一块nor flash,地址分配如下: Nor flash的起始地址为0x80000000.当zynq上运行Linux后可以通过对该地址起始的区域进行擦除 ...

  6. 使用Petalinux实现ZYNQ的linux程序开发(实现简单的socket通信程序)

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 (一)实现功能及开发环境 一.实现功能 二.开发环境 (二)实现步骤 一.开发环境搭建 1.设置Ubuntu 和 Windo ...

  7. 嵌入式 Linux 驱动开发你想知道的都在这

    最近看到公众号上写的一篇文章,关于嵌入式 Linux 驱动开发的方方面面,感觉提供不错,此处特意贴出来供大家参考借鉴. 1.嵌入式驱动开发到底学什么 嵌入式大体分为以下四个方向: 嵌入式硬件开发:熟悉 ...

  8. 最全Linux驱动开发全流程详细解析(持续更新)

    Linux驱动开发详细解析 一.驱动概念 驱动与底层硬件直接打交道,充当了硬件与应用软件中间的桥梁. 具体任务 读写设备寄存器(实现控制的方式) 完成设备的轮询.中断处理.DMA通信(CPU与外设通信 ...

  9. 【正点原子Linux连载】第三十八章 根文件系统构建 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  10. 我要转行Linux驱动开发了,驱动开发的工作是什么样的情况,希望大神给点建议,也希望大家给我一点鼓励

    我刚大学毕业的时候是2010年,什么都不会,只会点c,后来介绍去做JavME,做了一年,后又做了一阵子Java企业级开发.感觉这不是我想要的工作,在这段时间里面,除了实际编程能力有了提高,其他的没学会 ...

最新文章

  1. 【剑指offer-Java版】31连续子数组的最大和
  2. 面到阿里月薪 27k 数据岗,很难?
  3. Matlab程序中调用其他程序
  4. svpwm c语言程序,SVPWM的实现过程附C代码.doc
  5. Activiti6驳回上一节点
  6. 三菱伺服自动调谐_三菱伺服mr-j3与mr-j4参数有什么差别
  7. dns 性能测试 dnsperf
  8. android内存dump分析,闭眼能敲,Android内存分析command
  9. 快速排序的时间复杂度和空间复杂度
  10. Tomcat+Servlet面试题都在这里(修订版)
  11. Mac系统Safari浏览器快捷键大全
  12. Qt 5.6.0-VS2015 版本 安装配置图文教程
  13. 简易防火墙建置与流量统计
  14. java 基础练习(1-5)
  15. 岁月让我们学会了选择与放弃
  16. STM32HAL库学习之路(八):MG90S舵机应用
  17. 【leetcode】剑指 Offer 16. 数值的整数次方(shu-zhi-de-zheng-shu-ci-fang-lcof)(快速幂)[中等]
  18. OpenGLES—API(gl2.h)
  19. 民警同志立刻往女同志那儿走来
  20. 深圳内推 | 华为诺亚方舟实验室招聘计算机视觉算法研究员/实习生

热门文章

  1. 常用的SQL多表连接查询
  2. 快手视频大量下载无水印软件 怎样从快手下载无水印软件 说说如何在快手批量下载高清不含水印...
  3. 音频测试方法(tiny)
  4. SM3密码杂凑算法原理
  5. 解决 Win 10 输入法(仅桌面)的问题
  6. (附源码)springboot自律健身房会员管理系统 毕业设计456466
  7. 2、宽带Doherty放大器ADS仿真(带版图)
  8. ADS仿真遇到error如何查找原因
  9. oracle查询语句查询增加一列内容
  10. 2021最新一线互联网大厂常见高并发面试题解析,快手Java面试算法题