第三篇 香橙派的外设开发基础(中)— 串口篇

这篇具有很好参考价值的文章主要介绍了第三篇 香橙派的外设开发基础(中)— 串口篇。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、OrangePi PC Plus的串口

1.开启OrangePi PC+用于通信的串口

 🔖方法一 :修改/boot/orangepiEnv.txt

 🔖方法二:通过orangepi-config

2.基于wiringPi的串口通信Demo1.c

二、Linux下的串口开发基础 

1.Linux下的串口配置

2.Linux串口通信Demo2.c


一、OrangePi PC Plus的串口

        根据官方的用户手册所描述,OrangePi PC+可用于设备通信的串口有三个,分别对应的设备文件是:/dev/ttyS1、/dev/ttyS2、/dev/ttyS3,且默认未开启(zero plus据说默认开启,总之测试即可得知)。之前提到到调试串口应该是对于/dev/ttyS0(也可做通信串口,效果较差不推荐)。

1.开启OrangePi PC+用于通信的串口

        这里使用官方提供的examples里的serialTest.c程序来测试。默认未开启情况下,直接编译运行该程序,是满屏的-1和→。我们可以通过修改/boot/orangepiEnv.txt这个文件,或者orange-config打开配置菜单去开启ttyS1、ttyS2或ttyS3。

        特别说明:这里使用的系统是官方提供的ubuntu系统。如果是Armbian系统,应该修改的文件是/boot/armbianEnv.txt,同时调出配置菜单命令是armbian-config

        对应的针脚接线,可通过gpio readall查看。

第三篇 香橙派的外设开发基础(中)— 串口篇


  🔖方法一 :修改/boot/orangepiEnv.txt

        以开启ttyS1为例,修改如下内容:(保存修改后需要reboot命令重启

        第三篇 香橙派的外设开发基础(中)— 串口篇

         修改好重启后,接上TTL转USB模块到UART1。再用测试程序测试,正确的运行结果如下

        第三篇 香橙派的外设开发基础(中)— 串口篇

 🔖方法二:通过orangepi-config

        orangepi-config命令调出配置菜单来开启通信串口,按照以下步骤

        System → Hardware → 选择对应串口,空格选中 → Save → reboot重启

第三篇 香橙派的外设开发基础(中)— 串口篇

2.基于wiringPi的串口通信Demo1.c

#include <stdio.h>
#include <string.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

int fd;

/* 发送数据线程 */
void *sendDatas(){
    char *sendBuffer = (char *)malloc(128);
    while(1){
        memset(sendBuffer,'\0',sizeof(sendBuffer));
        printf("Send -> ");
        scanf("%s",sendBuffer);
        serialPuts(fd,sendBuffer);
    }
}

int main ()
{
    fd = serialOpen("/dev/ttyS1",115200);
    if (fd < 0){
        printf("serial open error\n");
        return -1;
    }

    if (wiringPiSetup () == -1){
        perror("wiringPi setup");
        return -1;
    }

    pthread_t sendThread;
    char recvBuffer[128];
    int cnt;

    pthread_create(&sendThread,NULL,sendDatas,NULL);

    /* 主线程,接收数据 */
    while(1){
        cnt = serialDataAvail(fd);
        if(cnt > 0){
            memset(recvBuffer,'\0',128);
            read(fd,recvBuffer,cnt);
            printf("receive -> %s\n",recvBuffer);
        }
    }
}

二、Linux下的串口开发基础 

1.Linux下的串口配置

        在demo1中,仅是调用了wiringPi库提供打开串口的API,下面要在demo1的基础上,自己编写Linux应用层的串口通信代码,也就是不用调库实现类似serialOpen()的接口。在serialOpen()这一接口中,主要完成设置波特率(输入和输出)、设置奇偶校验位、停止位、数据位。(也就是调用Linux标准C库的接口,向内核传递串口通信必要的参数信息。)

        首先需要大致了解Linux下串口配置需要做哪些事,这里参考wiringpi源码来了解Linux配置串口基本流程。(以下源码摘自官方wiringPi库,注释为自行添加,可能有误仅供参考):

int serialOpen (const char *device, const int baud)
{
  struct termios options ;
  speed_t myBaud ;
  int     status, fd ;
  switch (baud)
  {
    case      50:   myBaud =      B50 ; break ;
    case      75:   myBaud =      B75 ; break ;
    case     110:   myBaud =     B110 ; break ;
    case     134:   myBaud =     B134 ; break ;
    case     150:   myBaud =     B150 ; break ;
    case     200:   myBaud =     B200 ; break ;
    case     300:   myBaud =     B300 ; break ;
    case     600:   myBaud =     B600 ; break ;
    case    1200:   myBaud =    B1200 ; break ;
    case    1800:   myBaud =    B1800 ; break ;
    case    2400:   myBaud =    B2400 ; break ;
    case    4800:   myBaud =    B4800 ; break ;
    case    9600:   myBaud =    B9600 ; break ;
    case   19200:   myBaud =   B19200 ; break ;
    case   38400:   myBaud =   B38400 ; break ;
    case   57600:   myBaud =   B57600 ; break ;
    case  115200:   myBaud =  B115200 ; break ;
    default:
    return -2 ;
  }

  if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)
    return -1 ;

  fcntl (fd, F_SETFL, O_RDWR) ; //设置串口阻塞办法

// Get and modify current options: 获取和修改当前选项

  tcgetattr (fd, &options) ;
  cfmakeraw   (&options) ;            //将终端设置为原始模式8N1无流控

  cfsetispeed (&options, myBaud) ;    //设置输入波特率
  cfsetospeed (&options, myBaud) ;    //设置输出波特率
  /* 据说linux并没有将输入波特率和输出波特率分开
       调用cfsetispeed和cfsetospeed任何一个会同时修改输入和输出波特率
       但是也许传入要修改的波特率为0的时候这两个函数会有区别 */

 options.c_cflag |= (CLOCAL | CREAD); //CLOCAL忽略modem状态线,CREAD使能设备接收(某教材所述)
 options.c_cflag &= ~PARENB ;        //设置奇偶校验位——这里是无校验位

 /* 三种校验位的设置 */
 /* 1.奇校验位设置 options.c_cflag |= (PARODD | PARENB);
                      options.c_cflag |= INPCK;
    2.偶校验位设置 options.c_cflag |= PARENB;
                      options.c_cflag &= PARODD;
                      options.c_cflag |= INPCK;
    3.无校验位设置 options.c_cflag &= ~PARENB;                 */

  options.c_cflag &= ~CSTOPB;     //设置1位的停止位
/*options.c_cflag |= CSTOPB;        设置2位的停止位*/
    
  options.c_cflag &= ~CSIZE ;         //用数据位掩码清空设置
  options.c_cflag |= CS8 ;            //设置8位的数据位

  /* 设置输出模式*/
  options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 

  /*  设置等待时间和最小接收字符 */
  options.c_cc [VMIN]  =   0 ;    //读取字符最少个数
  options.c_cc [VTIME] = 100 ;    //读取一个字符等待100分秒

  tcsetattr (fd, TCSANOW, &options) ;   //使上面新的设置生效

  ioctl (fd, TIOCMGET, &status);    //获取(具体未查到,不深究)
  status |= TIOCM_DTR ;
  status |= TIOCM_RTS ;
  ioctl (fd, TIOCMSET, &status);    //设置(具体未查到,不深究)
  usleep (10000) ;  // 10mS
  return fd ;
}

2.串口通信Demo2.c

        demo2主要是在demo1的基础上,自己实现了wiringPi库中的serialOpen()这一接口,以及用Linux下标准的文件I/O操作函数read()、write()实现串口通信。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>

int uartOpen (const char *device, const int baud){
    struct termios options ;
    speed_t myBaud ;
    int     status, fd ;
    switch (baud){
        case    9600:   myBaud =    B9600; break;
        case  115200:   myBaud =  B115200; break;
        default:
                        return -2 ;
    }

    fd = open(device,O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
    if(fd == -1){
        perror("open");
        return -1;
    }

    fcntl (fd, F_SETFL, O_RDWR) ;   //设置串口阻塞办法

    //获取和修改当前选项
    tcgetattr (fd, &options) ;
    cfmakeraw   (&options) ;            //将终端设置为原始模式8N1无流控

    cfsetispeed (&options, myBaud) ;    //设置输入波特率
    cfsetospeed (&options, myBaud) ;    //设置输出波特率

    options.c_cflag |= (CLOCAL | CREAD); //CLOCAL 忽略modem状态线,CREAD使能设备接收(某教材所述)
    options.c_cflag &= ~PARENB ;        //设置奇偶校验位——这里是无校验位

    options.c_cflag &= ~CSTOPB;     //设置1位的停止位

    options.c_cflag &= ~CSIZE ;         //用数据位掩码清空设置
    options.c_cflag |= CS8 ;            //设置8位的数据位

    /* 设置输出模式*/
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 
    /*  设置等待时间和最小接收字符 */
    options.c_cc [VMIN]  =   0 ;    //读取字符最少个数
    options.c_cc [VTIME] = 100 ;    //读取一个字符等待100分秒

    tcsetattr (fd, TCSANOW, &options) ; //使上面新的设置生效

    ioctl (fd, TIOCMGET, &status);  
    status |= TIOCM_DTR ;
    status |= TIOCM_RTS ;
    ioctl (fd, TIOCMSET, &status);
    usleep (10000) ;    //10ms
    return fd ;
}

int uartfd;

/* 向串口发送数据的线程 */
void *sendDatas(){
    int cnt;
    while(1){
        char *buffer = (char *)malloc(64);
        memset(buffer,'\0',sizeof(buffer));
        printf("send -> ");
        scanf("%s",buffer);
        /*向串口1对应的设备文件写入buffer的数据*/
        cnt = write(uartfd,buffer,strlen(buffer));
        if(cnt < 0)printf("Serial send datas error\n");
    }
}

/* 读取串口数据的线程 */
void *recvDatas(){
    int cnt,readSize;
    char *buffer = (char *)malloc(64);
    while(1){
        /* 判断串口是否有数据 */
        if(ioctl(uartfd,FIONREAD,&cnt) == -1){
            perror("ioctl");
            return 0;
        }else{
            readSize = read(uartfd,buffer,cnt); //读取数据到buffer中
            if(readSize > 0)printf("recv -> %s",buffer);//读取成功再打印
        }
        memset(buffer,'\0',sizeof(buffer));
    }
}
int main(int argc,char **argv){

    pthread_t sendThread,recvThread;
    if(argc < 2){
        printf("syntax error.Usage:%s /dev/ttyS*\n",argv[0]);
        return -1;
    }
    /* 以波特率115200打开串口,其他相关配置已在uartOpen接口中已默认 */
    uartfd = uartOpen(argv[1],115200);
    if(uartfd == -1){
        printf("%s open error\n",argv[1]);
        return -1;
    }else{
        printf("open %s succeed.\n",argv[1]);
    }
    /* 主函数中定义出两个线程用于接收和发送数据 */
    pthread_create(&sendThread,NULL,sendDatas,NULL);
    pthread_create(&recvThread,NULL,recvDatas,NULL);

    /* 主线程每10秒发送心跳包 */
    while(1){
        char alive[] = "I am alive\r\n";
        write(uartfd,alive,strlen(alive));
        sleep(10);
    }
    return 0;
}


说明:由于笔者水平有限,文中难以避免有所错漏,敬请各读者斧正

版权声明:转载请附上原文出处链接及本声明。

 文章来源地址https://www.toymoban.com/news/detail-402551.html

到了这里,关于第三篇 香橙派的外设开发基础(中)— 串口篇的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Oracle之 第三篇 PL/SQL基础

    目录 Oracle之  第三篇  PL/SQL基础 PL/SQL程序块  PL/SQL语言 PL/SQL的基本结构   PL/SQL块分类    一、PL/SQL语言 二、PL/SQL 常量 、变量    合法字符 数据类型 LOB  数据类型  属性类型   运算符 常量    PL/SQL常量 1 、变量的声明        2、属性类型 % type 变量赋值 %type和%rowtype区

    2024年02月02日
    浏览(58)
  • Java零基础教学文档第三篇:JDBC

    今日新篇章 【JDBC】 【主要内容】 JDBC概述 使用JDBC完成添加操作 使用JDBC完成更新和删除 DBUtils的简单封装 使用JDBC完成查询 使用JDBC完成分页查询 常用接口详解 JDBC批处理 SQL注入问题 事务处理解决转账问题 连接池 使用反射对DBUtils再次的封装 BaseDAO的封装 【学习目标】 1.1

    2024年01月19日
    浏览(47)
  • 【MySQL基础|第三篇】--- 详谈SQL中的DQL语句

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【MySQL学习专栏】🎈 本专栏旨在分享学习MySQL的一点学习心得,欢迎大家在评论区讨论💌 DQL(Data Query Language) 是 数据库查询语言 ,用来查询数据库表中的记录。查询

    2024年02月07日
    浏览(44)
  • Groovy开发语言系列(第三篇):DSL的强大能力

    欢迎阅读本系列的第三篇文章!在前两篇中,我们已经介绍了Groovy语言的发展历史、特点以及安装和基本语法。今天,我们将聚焦于Groovy的强大能力之一:领域特定语言(Domain-Specific Language,简称DSL)。 让我们先通过一个简单的示例来吸引大家对DSL的兴趣: 这段代码执行后

    2024年02月12日
    浏览(31)
  • 第三篇【传奇开心果系列】Vant开发移动应用:财务管理应用

    使用vant实现财务管理应用:创建一个简单的财务管理应用,用户可以记录和跟踪他们的收入和支出,并生成报表和图表展示财务状况。 1. 首先,安装并引入Vant组件库,以便使用Vant提供的丰富组件来构建财务管理应用界面。 创建一个首页,包括收入、支出、报表和图表四个

    2024年01月22日
    浏览(58)
  • c++类开发的第三篇(讲明白友元函数和this指针)

    c++实现了 封装 , 数据 和 处理数据的操作(函数) 是分开存储的。 c++中的 非静态数据成员 直接内含在类对象中,就像c语言的struct一样。 成员函数并不会出现在对象中,而是作为类的一部分存储在代码段中,需要通过对象或对象指针进行调用。成员函数可以访问类的所有成员

    2024年02月21日
    浏览(47)
  • AI大模型运维开发探索第三篇:深入浅出运维智能体

    作者:炯思、玦离 大模型出现伊始,我们就在SREWorks开源社区征集相关的实验案例。 玦离同学提供了面向大数据HDFS集群的智能体案例,非常好地完成了运维诊断的目标。于是基于这一系列的实验和探索,就有了本篇文章。 读者思路: 介绍什么是智能体 基于智能体的运维诊

    2024年04月24日
    浏览(34)
  • firefly开发板RK3588非默认外设使能(串口uart、IIC、adc等)设备树修改详细步骤

    sdk获取和内核编译,参考上一篇博文:rk3588内核裁剪 文件1: 此文件是针对firefly的板级设备树文件。 文件2: 此文件是关于io复用的设备树文件。 文件3: 此文件是所有具有复用功能的gpio,例如:#define RK_PA4 4 代表的是GPIO_PA_4即A组的GPIO4。 官方的内核和设备树,默认打开了串

    2024年02月09日
    浏览(52)
  • C++练级之初级:第三篇

    🤔首先我们先解决一下为什么C++支持函数重载,而C语言不支持? 这里就不得不提起编译链接了😁; 👉这是编译链接篇 以这三个简单的文件为例: 预处理阶段: 头文件的展开,条件编译,宏的替换,注释的删除等,最终处理完这些后test.c就会变成test.i,add.c就会变成add.i;

    2023年04月23日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包