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

2019-07-13 04:28发布

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 ",sizeof("helloworld ")); display_uart_tx_data(QString("helloworld")); } 所有代码编写完成,下载到TQ-i.MX6UL,运行应用程序,可以看到如下效果。我们使用UART3(ttySAC2)与电脑进行串口数据收发,其他串口操作类似。 点击这里,查看实验现象   点击这里,下载源码 点击这里,学习更多Embeded IoT Linux开发