嵌入式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)與電腦進行串口數據收發,其他串口操作類似。

點擊這裏,查看實驗現象

 

點擊這裏,下載源碼

點擊這裏,學習更多Embeded IoT Linux開發

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章