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

目录

一、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;
}


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

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

 

  • 9
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
ESP8266是一种常用的物联网开发板,通过它能够轻松实现与其他外部设备的通信。在基础的第五部分,我们将学习如何使用SPI和I2C协议来实现通信。 首先是SPI通信。SPI是一种串行通信协议,它通过四根线(SCLK、MISO、MOSI和SS)将数据发送到其他设备。在ESP8266上,我们可以使用其GPIO来模拟这些SPI线。 要使用SPI通信,我们需要设置一些配置参数,例如时钟频率、数据位顺序和传输模式等。然后,通过编写代码来初始化SPI,并使用SPI传输数据。在发送数据时,我们需要同时发送和接收数据,以确保通信的准确性。 接下来是I2C通信。I2C是一种双线制串行通信协议,使用SDA(数据线)和SCL(时钟线)进行通信。与SPI不同,I2C可以连接多个设备到同一个总线上。 在ESP8266上,我们同样可以使用GPIO来模拟I2C通信。我们需要设置一些配置参数,例如时钟频率和I2C地址等。然后,我们可以通过编写代码来初始化I2C,并发送和接收数据。 无论是SPI还是I2C通信,我们都可以通过连接其他传感器或外部设备来扩展ESP8266的功能。例如,我们可以使用SPI通信来连接一个SD卡模块,以实现数据存储和读取的功能。而使用I2C通信,我们可以连接温度传感器或加速度计等外设,以获取环境数据或运动数据。 总之,学习ESP8266的SPI和I2C通信对于扩展其功能和与其他设备进行交互非常重要。通过使用这些通信协议,我们可以轻松地实现与外设的数据传输和控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AF_INET6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值