嵌入式Linux应用程序开发-(4)i.MX6UL RS232串口通信程序

i.MX6UL RS232串口通信程序

目标:了解i.MX6UL如何使用串口进行数据通信。
功能:使用串口进行自定义的数据收发,并把收发数据实时在显示屏上显示,实现一个嵌入式上运行的,简单的串口调试助手。

RS232是工业控制上用得比较多的一种通信方式,TQ-i.MX6UL底板引出了8个串口(含命令调试口),各个串口的硬件电路图,请查看官方开发资料。以下是各个串口的描述。
UART1:调试串口,Debug口,三线(RX, TX, GND),RS232电平。
UART2:与RS485复用,默认RS485通信,用作串口时,4线(5V, TXD, RXD, GND)TTL电平。
UART3:无复用,可选3线(RX, TX, GND)RS232电平或4线(5V, TXD, RXD, GND)TTL电平。
UART4:无复用,可选3线(RX, TX, GND)RS232电平或4线(5V, TXD, RXD, GND)TTL电平。
UART5:无复用,可选3线(RX, TX, GND)RS232电平或4线(5V, TXD, RXD, GND)TTL电平。
UART6:无复用,仅支持4线(5V, TXD, RXD, GND)TTL电平。
UART7:与网口2复用,默认为网口2,用作串口时,4线(5V, TXD, RXD, GND)TTL电平。
UART8:与网口2复用,默认为网口2,用作串口时,4线(5V, TXD, RXD, GND)TTL电平。

由此可见,TQ-i.MX6UL某些串口与其他外设接口进行了复用设计,除了调试串口(UART1)外,其他所有串口均支持TTL电平输出(需要更改某些电阻)。我们选择无复用功能的RS232串口(UART3, UART4, UART5)进行实验。

软件开发篇:
由于 i.MX6UL开发板运行的是QT4.8,不支持QT自带的串口类库,QT5以上才支持自带串口类。因此,开发QT5以下的串口应用时,需要借助第三方的串口类,可以通过以下的链接下载:https://sourceforge.net/projects/qextserialport/files/
最新的版本为:qextserialport-1.2win-alpha.zip
在Linux下进行串口应用开发,需要用到以下6个文件:

如果在Windows下只需将posix_qextserialport.cpp/posix_qextserialport.h 换为 win_qextserialport.cpp/win_qextserialport.h即可。
 

1、先用Qt Creator构建一个工程,命名为:004_uart_test,关于如何构建工程,请参考“第一个嵌入式QT应用程序”的具体内容。
2、双击打开“widget.ui”文件,构建界面,构建后的界面如下图所示:

界面描述:
PORT:指定需要打开的串口,目前提供 ttySAC1 - ttySAC5。表示UART2 - UART6
BAUDRATE:提供 2400/4800/9600/19200/38400/115200 这几种波特率。
【程序默认8位数据位,1位停止位,无校验位,无流控的设置方式(可通过代码修改)。】
OPEN:设置好串口的工作参数后,点击“OPEN”打开串口。
TX_CLEAR按钮和RX_CLEAR按钮:清空接收和发送的显示区域。
send_data按钮:点击一次,则通过串口发送一次固定数据。

3、为了方便配置和操作串口读写,我们可以把串口相关的操作(配置,读/写串口缓冲区)封装成一个类:Uart_Test,这个类包含了打开和关闭串口的方法,读/写串口缓冲区的方法,类的具体内容如下所示。

class Uart_Test : public QWidget
{
    Q_OBJECT

public:
    Uart_Test();
    ~Uart_Test();

    bool open_serial_port(QString port,QString baud);
    bool close_serial_port(void);
    void write_serial_port(char *p_data,int len);
    void write_serial_port(QByteArray arr);

signals:
    void read_serial_signals(QByteArray arr);

private slots:
    void slot_read_serial_port();

private:
    QString port_name;
    BaudRateType baudrate;
    QTimer *recv_timeout_timer;
    Posix_QextSerialPort *serial_port;
    QByteArray recv_data;
    BaudRateType get_baudrate(QString baudrate);
    unsigned int serial_port_recv_len;
};

4、串口类中的bool open_serial_port(QString port,QString baud),具体实现如下:

bool Uart_Test::open_serial_port(QString port,QString baud)
{
    this->port_name = QString("/dev/")+port;
    this->baudrate = get_baudrate(baud);

    //以查询的方式打开串口
    serial_port = new Posix_QextSerialPort(port_name,QextSerialBase::Polling);

    if(serial_port->open(QIODevice::ReadWrite))
    {
        serial_port->setBaudRate(baudrate);
        serial_port->setDataBits(DATA_8);
        serial_port->setParity(PAR_NONE);
        serial_port->setStopBits(STOP_1);
        serial_port->setFlowControl(FLOW_OFF);
        serial_port->setTimeout(1);

        recv_timeout_timer = new QTimer();
        //设置100ms的定时器,以查询的方式去读取串口数据
        connect( recv_timeout_timer, SIGNAL(timeout()), this, SLOT(slot_read_serial_port()));
        recv_timeout_timer->start(100);
        return true;
    }

    return false;
}

重点:由于第三方的串口类库qextserialport在Linux环境下,不支持以事件方式(EventDriven)去读取串口数据(windows下则同时支持EventDriven和Polling)。所以,Linux环境下,串口需要配置为Polling的工作方式,并且开启一个周期定时器,去读取串口数据。
(最新的QT5版本添加了串口的操作类QSerialPort,支持事件触发。但目前TQ-i.MX6UL仅支持QT4.8,后续待开发板的QT版本更新后,会同步更新串口通信程序。)

5、串口数据读写函数void slot_read_serial_port() 和 void write_serial_port(QByteArray arr)的具体实现如下所示:

void Uart_Test::write_serial_port(QByteArray arr)
{
    if(serial_port->isOpen())
    {
        serial_port->write(arr);
    }
}

void Uart_Test::slot_read_serial_port()
{
    if(serial_port->bytesAvailable() > 0)
    {
        recv_data.clear();
        recv_data = serial_port->readAll(); //读取串口缓冲区的所有数据

        emit read_serial_signals(recv_data); //发送信号,这个信号会在Widget类的构造函数中,与数据处理函数绑定
    }
}

重点:由于串口数据是通过定时器查询的方式读取,并且一次性读取所有数据。因此,每次串口有数据到达,可能会出现数据粘包或分包的情况。对于此类情况,建议使用自定义报文的方式,定义数据报文的帧头和帧尾,并使用环形队列处理方式。每次保证接收到完整的数据报文后,再进行数据处理。实验中为了简化工程量,所以并没有采用以上方式。

6、在Widget类的构造函数中,我们定义一个Uart_Test的类对象uart_test。并且通过connect函数把对象uart_test里面的信号void read_serial_signals(QByteArray arr) 与串口数据处理的槽函数void slot_serialport_data_process(QByteArray arr)进行绑定。当串口有数据到达时,可以通过该槽函数进行处理。

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    ui->pushButton_open_uart->setCheckable(false);

    uart_test = new Uart_Test(); //构建一个uart_test对象

    connect(uart_test, SIGNAL(read_serial_signals(QByteArray)), this, SLOT(slot_serialport_data_process(QByteArray)));   //绑定串口数据处理的槽函数
}

7、在槽函数void slot_serialport_data_process(QByteArray arr)里,我们把串口接收的数据显示出来,当然,也可以处理其他事务。

void Widget::slot_serialport_data_process(QByteArray arr)
{
    char data_out[1024];

    memset(data_out,0x00,sizeof(data_out));
    byte_to_str(arr.data(),data_out,arr.length());

    display_uart_rx_data(QString(data_out));
}


8、点击send_data按钮,则通过串口发送固定数据,send_data按钮的具体实现如下所示:

void Widget::on_pushButton_send_data_clicked()
{
    uart_test->write_serial_port((char*)"helloworld\n",sizeof("helloworld\n"));

    display_uart_tx_data(QString("helloworld"));
}

所有代码编写完成,下载到TQ-i.MX6UL,运行应用程序,可以看到如下效果。我们使用UART3(ttySAC2)与电脑进行串口数据收发,其他串口操作类似。

点击这里,查看实验现象

 

点击这里,获取源码

 

 

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

工程师进阶笔记

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

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

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

打赏作者

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

抵扣说明:

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

余额充值