3242|2

1239

帖子

68

TA的资源

纯净的硅(中级)

楼主
 

【米尔MYD-YA15XC-T评测】+ epoll串口通信 [复制链接]

 
本帖最后由 dql2016 于 2021-12-15 20:09 编辑

串口是嵌入式系统中最常用的接口之一,系统终端通常都是串口。除了终端功能之外,实际应用中,Linux系统也经常通过串口与其它设备进行通信和数据传递,如232、485等接口传感器通常底层都是串口。在Linux下的串口编程不像微控制器上那么简单。本帖以米尔MYD-YA15XC-T为例,实现串口基本操作、串口属性设置、如串口数据读写。米尔MYD-YA15XC-T开发板扩展排针接口引出了串口3供我们使用。位置如下图所示:

终端使用ls /dev/tty*命令查看设备文件:

Linux的串口表现为设备文件,Linux的串口设备文件命名一般为/dev/tty*,米尔MYD-YA15XC-T开发板串口设备命名为/dev/ttySTM*

可以看到串口3对应的设备文件是/dev/ttySTM3,使用一个TTL串口调试助手连接到串口3:

首先使用echo命令测试发送数据,可以看到串口3收到了数据:

接下来通过编写代码的方式实现串口的数据收发。

 

首先配置网络,因为后面拷贝文件到开发板需要用到。

 

在编写Linux串口的C程序代码时,需要包含termios.h头文件。主要流程为:

打开串口:用open()函数打开它所对应的设备文件。

关闭串口:用close()函数关闭串口。

发送数据:使用write()函数可以发送数据。

读取数据:使用read()函数可以读取接收到的数据。

此外,如果对串口属性进行设置需要包含<termios.h>头文件,该文件包含了POSIX终端属性描述结构struct termios。

完整的代码如下所示:

#include <iostream>

using namespace std;

#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>

#include <chrono>
#include <thread>   

class MySerial
{
private:
    int fd = -1;
    string serial_dev;
    int serial_baudrate;
    string serial_parity;
    int serial_databits;
    int serial_stopbits;
    void* (*rx_cb_fun)(void*);

    int epfd;
    struct epoll_event event;	// 告诉内核要监听什么事件
    struct epoll_event wait_event;
public:
    MySerial(string serial_dev, int serial_baudrate, string serial_parity, int serial_databits, int serial_stopbits,void* (*rx_cb_fun)(void*))
    {
       this-> serial_dev= serial_dev;
        this-> serial_baudrate= serial_baudrate;
        this-> serial_parity= serial_parity;
        this-> serial_databits= serial_databits;
        this-> serial_stopbits= serial_stopbits;

        this->rx_cb_fun = rx_cb_fun;

        epfd = epoll_create(10); // 创建一个 epoll 的句柄,参数要大于 0, 没有太大意义

        if( -1 == epfd )
        {
            perror ("epoll_create");
        }

    };
    int OpenSerial()
    {
        struct termios tios;
        int speed;
        fd = open(serial_dev.c_str(), O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL);
        if (fd < 0)
        {
            perror("open");
        }
        memset(&tios, 0, sizeof(struct termios));
        switch (serial_baudrate)
        {
            case 50:
                speed = B50;
                break;
            case 75:
                speed = B75;
                break;
            case 110:
                speed = B110;
                break;
            case 134:
                speed = B134;
                break;
            case 150:
                speed = B150;
                break;
            case 200:
                speed = B200;
                break;
            case 300:
                speed = B300;
                break;
            case 600:
                speed = B600;
                break;
            case 1200:
                speed = B1200;
                break;
            case 1800:
                speed = B1800;
                break;
            case 2400:
                speed = B2400;
                break;
            case 4800:
                speed = B4800;
                break;
            case 9600:
                speed = B9600;
                break;
            case 19200:
                speed = B19200;
                break;
            case 38400:
                speed = B38400;
                break;
            case 57600:
                speed = B57600;
                break;
            case 115200:
                speed = B115200;
                break;
            case 230400:
                speed = B230400;
                break;
            case 460800:
                speed = B460800;
                break;
            case 500000:
                speed = B500000;
                break;
            case 576000:
                speed = B576000;
                break;
            case 921600:
                speed = B921600;
                break;
            case 1000000:
                speed = B1000000;
                break;
            case 1152000:
                speed = B1152000;
                break;
            case 1500000:
                speed = B1500000;
                break;
            case 2000000:
                speed = B2000000;
                break;
            case 2500000:
                speed = B2500000;
                break;
            case 3000000:
                speed = B3000000;
                break;
            case 3500000:
                speed = B3500000;
                break;
            case 4000000:
                speed = B4000000;
                break;
            default:
                speed = B9600;
                break;
        }
        if ((cfsetispeed(&tios, speed) < 0) || (cfsetospeed(&tios, speed) < 0))
        {
            close(fd);
            fd = -1;
            perror("cfsetispeed or cfsetospeed");
        }
        tios.c_cflag |= (CREAD | CLOCAL);
        tios.c_cflag &= ~CSIZE;
        switch (serial_databits)
        {
            case 5:
                tios.c_cflag |= CS5;
                break;
            case 6:
                tios.c_cflag |= CS6;
                break;
            case 7:
                tios.c_cflag |= CS7;
                break;
            case 8:
            default:
                tios.c_cflag |= CS8;
                break;
        }
        if (serial_stopbits == 1)
        {
            tios.c_cflag &= ~CSTOPB;
        }
        else
        {
            tios.c_cflag |= CSTOPB;
        }
        if (serial_parity == "none")
        {
            tios.c_cflag &= ~PARENB;
        }
        else if (serial_parity == "even")
        {
            tios.c_cflag |= PARENB;
            tios.c_cflag &= ~PARODD;
        }
        else if (serial_parity == "odd")
        {
            tios.c_cflag |= PARENB;
            tios.c_cflag |= PARODD;
        }
        tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
        if (serial_parity == "none")
        {
            tios.c_iflag &= ~INPCK;
        }
        else
        {
            tios.c_iflag |= INPCK;
        }
        tios.c_iflag &= ~(IXON | IXOFF | IXANY);
        tios.c_oflag &= ~OPOST;
        tios.c_cc[VMIN] = 0;
        tios.c_cc[VTIME] = 0;
        if (tcsetattr(fd, TCSANOW, &tios) < 0)
        {
            close(fd);
            fd = -1;
            perror("tcsetattr");
        }

        event.data.fd = fd; 	// 串口描述符
        event.events = EPOLLIN; // 表示对应的文件描述符可以读
        // 事件注册函数,将描述符fd加入监听事件
        int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
        if(-1 == ret)
        {
            perror("epoll_ctl");
        }
        return fd;
    }

    void loop()
    {
        int ret;
            // 监视并等待多文件(串口)描述符的属性变化(是否可读)
            // 没有属性变化,这个函数会阻塞,直到有变化才往下执行,这里没有设置超时
            ret = epoll_wait(epfd, &wait_event, 2, -1);
            if(ret == -1)  // 出错
            {
                close(epfd);
                perror("epoll");
            }
            else if(ret > 0)  // 准备就绪的文件描述符
            {
                //char buf[100] = {0};
                if((fd == wait_event.data.fd) && (EPOLLIN == wait_event.events & EPOLLIN))
                {
                    rx_cb_fun(&fd);
                }
            }
            else if(0 == ret)
            {
                printf("time out\n");
            }
    }
    ~MySerial()
    {
        close(fd);
        close(epfd);
    }
};

void *SerialRxCB(void* arg)
{
    int fd = *(int*)arg;
    char buf;
    read(fd, &buf, 1);
    printf("%c\n", buf);
    return NULL;
}

void thread_task(void* arg) 
{
	int fd = *(int*)arg;
	while(1)
	{
		char tx_buf[]="hello eeworld & mier";
		this_thread::sleep_for(chrono::seconds(1));
		write (fd, tx_buf, sizeof(tx_buf)); 
	}
}

int main(int argc,char* argv[])
{
    MySerial *ps = new MySerial("/dev/ttySTM3",9600,"none",8,1,SerialRxCB);
    int ret = ps->OpenSerial();
    if(ret < 0)
    {
        perror("open serial");
        return -1;
     }
    
    thread t(thread_task,&ret);
    t.detach();
    
    while(1)
    {
        ps->loop();
    }
    return 0;
}

程序中使用到了epoll,类似单片机里面的中断,可以实现异步数据的接收,程序中还使用到了c++11的线程库,米尔提供的交叉编译器版本很新,完全支持c++11的各种特性。首先在虚拟机终端执行命令,这样交叉编译环境变量就在该终端生效了:

. /opt/st/myir/3.1-snapshot/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi

可以看到CXX环境变量:

然后执行命令进行程序的编译:

$CXX stm32mp1_uart_test.cpp -o uart_test -lpthread

c++11的线程库底层使用了pthread实现,因此需要加链接参数-lpthread

交叉编译完成后,使用scp命令将程序拷贝到开发板上执行:

scp qldeng@192.168.1.131:/home/qldeng/stm32mp1_uart_test/uart_test ./

复制到板子上的效果:

最后看看实际效果吧:

最新回复

感觉还不错,代码给的比较全,是例程吗?   详情 回复 发表于 2021-12-17 10:38
点赞 关注
 
 

回复
举报

7608

帖子

2

TA的资源

五彩晶圆(高级)

沙发
 

串口读取等待休眠得驱动支持吧

个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 
 

回复

7159

帖子

2

TA的资源

版主

板凳
 

感觉还不错,代码给的比较全,是例程吗?

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表