amoBBS 阿莫电子论坛

 找回密码
 注册
搜索
bottom↓
查看: 602|回复: 23

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

[复制链接]
发表于 2019-4-8 10:13:50 | 显示全部楼层 |阅读模式
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,关于如何构建工程,请参考嵌入式Linux应用程序开发-(1)第一个嵌入式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,这个类包含了打开和关闭串口的方法,读/写串口缓冲区的方法,类的具体内容如下图所示。
  1. class Uart_Test : public QWidget
  2. {
  3.     Q_OBJECT

  4. public:
  5.     Uart_Test();
  6.     ~Uart_Test();

  7.     bool open_serial_port(QString port,QString baud);
  8.     bool close_serial_port(void);
  9.     void write_serial_port(char *p_data,int len);
  10.     void write_serial_port(QByteArray arr);

  11. signals:
  12.     void read_serial_signals(QByteArray arr);

  13. private slots:
  14.     void slot_read_serial_port();

  15. private:
  16.     QString port_name;
  17.     BaudRateType baudrate;
  18.     QTimer *recv_timeout_timer;
  19.     Posix_QextSerialPort *serial_port;
  20.     QByteArray recv_data;
  21.     BaudRateType get_baudrate(QString baudrate);
  22.     unsigned int serial_port_recv_len;
  23. };
复制代码


4、串口类中的bool open_serial_port(QString port,QString baud),具体实现如下:
  1. bool Uart_Test::open_serial_port(QString port,QString baud)
  2. {
  3.     this->port_name = QString("/dev/")+port;
  4.     this->baudrate = get_baudrate(baud);

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

  7.     if(serial_port->open(QIODevice::ReadWrite))
  8.     {
  9.         serial_port->setBaudRate(baudrate);
  10.         serial_port->setDataBits(DATA_8);
  11.         serial_port->setParity(PAR_NONE);
  12.         serial_port->setStopBits(STOP_1);
  13.         serial_port->setFlowControl(FLOW_OFF);
  14.         serial_port->setTimeout(1);

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

  21.     return false;
  22. }
复制代码

重点:由于第三方的串口类库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)的具体实现如下所示:
  1. void Uart_Test::write_serial_port(QByteArray arr)
  2. {
  3.     if(serial_port->isOpen())
  4.     {
  5.         serial_port->write(arr);
  6.     }
  7. }

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

  14.         emit read_serial_signals(recv_data); //发送信号,这个信号会在Widget类的构造函数中,与数据处理函数绑定
  15.     }
  16. }
复制代码

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

6、在Widget类的构造函数中,我们定义一个Uart_Test的类对象uart_test。并且通过connect函数把对象uart_test里面的信号void read_serial_signals(QByteArray arr) 与串口数据处理的槽函数void slot_serialport_data_process(QByteArray arr)进行绑定。当串口有数据到达时,可以通过该槽函数进行处理。
  1. Widget::Widget(QWidget *parent) :
  2.     QWidget(parent),
  3.     ui(new Ui::Widget)
  4. {
  5.     ui->setupUi(this);

  6.     ui->pushButton_open_uart->setCheckable(false);

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

  8.     connect(uart_test, SIGNAL(read_serial_signals(QByteArray)), this, SLOT(slot_serialport_data_process(QByteArray)));   //绑定串口数据处理的槽函数
  9. }
复制代码


7、在槽函数void slot_serialport_data_process(QByteArray arr)里,我们把串口接收的数据显示出来,当然,也可以处理其他事务。
  1. void Widget::slot_serialport_data_process(QByteArray arr)
  2. {
  3.     char data_out[1024];

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

  6.     display_uart_rx_data(QString(data_out));
  7. }
复制代码


8、点击send_data按钮,则通过串口发送固定数据,send_data按钮的具体实现如下所示:
  1. void Widget::on_pushButton_send_data_clicked()
  2. {
  3.     uart_test->write_serial_port((char*)"helloworld\n",sizeof("helloworld\n"));

  4.     display_uart_tx_data(QString("helloworld"));
  5. }
复制代码


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


点击这里,下载工程源码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
发表于 2019-4-8 10:20:59 | 显示全部楼层
为啥一开始就说应用,不说启动原理,uboot,内核啥的,求推荐学习路线
 楼主| 发表于 2019-4-8 10:28:35 | 显示全部楼层
nade 发表于 2019-4-8 10:20
为啥一开始就说应用,不说启动原理,uboot,内核啥的,求推荐学习路线

其实,如果真正想学习嵌入式,驱动、应用、系统,三者都需要学习,但是,因为Linux系统实在太庞大和太复杂,如果一开始就学习内核里面的情景分析,分析u-boot源码,如何引导kernel,加载文件系统,相信很多初学者都会望而却步。我比较喜欢从应用 -> 驱动的路线进行学习,先弄懂如何用,再深入学习为什么这样用。当然了,学习方法因人而异,也有些同学喜欢研究kernel源码。
 楼主| 发表于 2019-4-8 10:30:08 | 显示全部楼层
nade 发表于 2019-4-8 10:20
为啥一开始就说应用,不说启动原理,uboot,内核啥的,求推荐学习路线

如果你需要内核驱动的学习路线,推荐:点击这里
 楼主| 发表于 2019-4-9 13:54:35 | 显示全部楼层
本章节pdf下载:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
发表于 2019-4-9 14:00:59 | 显示全部楼层
我觉得很有道理,熟悉应用后做几个小项目,有时间和精力后再慢慢深入研究底层。
 楼主| 发表于 2019-4-9 14:06:25 | 显示全部楼层
wt19891114 发表于 2019-4-9 14:00
我觉得很有道理,熟悉应用后做几个小项目,有时间和精力后再慢慢深入研究底层。 ...

嗯嗯,每个人都有自己的学习方法,驱动与应用都同等重要,需要不停地学习
发表于 2019-4-9 14:59:10 | 显示全部楼层
本帖最后由 kinsno 于 2019-4-9 15:06 编辑

鼓掌,我为楼主鼓掌贺彩,我想楼主的这套资料,应该就是我的敲门砖了。。
楼主是天嵌开发板的官方吗?
楼主,在文章开头的索引,非常好。。
感谢感谢。


 楼主| 发表于 2019-4-9 15:19:47 | 显示全部楼层
kinsno 发表于 2019-4-9 14:59
鼓掌,我为楼主鼓掌贺彩,我想楼主的这套资料,应该就是我的敲门砖了。。
楼主是天嵌开发板的官方吗?
楼主 ...

感谢您的支持,如果需要Linux开发板学习,可以联系我,一起学习,一起进步
发表于 2019-4-9 15:33:43 | 显示全部楼层
广轻电气091 发表于 2019-4-9 15:19
感谢您的支持,如果需要Linux开发板学习,可以联系我,一起学习,一起进步 ...

等你这个连载完了,我把所有PDF下载下来,合个集。。然后。。再寻摸着弄一块最小核心板耍耍吧。。

 楼主| 发表于 2019-4-9 15:37:15 | 显示全部楼层
kinsno 发表于 2019-4-9 15:33
等你这个连载完了,我把所有PDF下载下来,合个集。。然后。。再寻摸着弄一块最小核心板耍耍吧。。

...

哈哈,不知道会更新到什么程度,我也是边学习边总结。
发表于 2019-4-9 16:23:43 | 显示全部楼层
不错。其实学linux就应该从应用开始。
直接搞驱动得大部分都放弃掉了。
发表于 2019-4-9 17:00:39 | 显示全部楼层
honami520 发表于 2019-4-9 16:23
不错。其实学linux就应该从应用开始。
直接搞驱动得大部分都放弃掉了。

话又说回来,LINUX是加分项,虽然不涨工资啊,老铁。。。

 楼主| 发表于 2019-4-9 17:01:17 | 显示全部楼层
honami520 发表于 2019-4-9 16:23
不错。其实学linux就应该从应用开始。
直接搞驱动得大部分都放弃掉了。

感谢支持。其实每个人的学习方法都不一样。应用程序接近用户业务,比较多样化。Linux驱动在固有的框架内开发,可能有些人会觉得枯燥
 楼主| 发表于 2019-4-9 17:33:43 | 显示全部楼层
kinsno 发表于 2019-4-9 17:00
话又说回来,LINUX是加分项,虽然不涨工资啊,老铁。。。

工资水平跟行业有关,但如果基本功练好的话,相信不会过得太差
发表于 2019-4-9 17:35:58 | 显示全部楼层
nade 发表于 2019-4-8 10:20
为啥一开始就说应用,不说启动原理,uboot,内核啥的,求推荐学习路线

如果一来就学这些 很容易放弃啊  哈哈
 楼主| 发表于 2019-4-9 17:56:20 | 显示全部楼层
浮华一生 发表于 2019-4-9 17:35
如果一来就学这些 很容易放弃啊  哈哈

从入门到放弃的最快途径
发表于 2019-4-28 17:48:26 | 显示全部楼层
是应该从应用入手,才知道系统的运行机制,才对后面驱动开发有更深理解。不了解应用就搞驱动,做出来的驱动也不一定是最适合应用的。
 楼主| 发表于 2019-4-29 08:16:29 | 显示全部楼层
hmsfeng 发表于 2019-4-28 17:48
是应该从应用入手,才知道系统的运行机制,才对后面驱动开发有更深理解。不了解应用就搞驱动,做出来的驱动 ...

每个人都有适合自己的学习方法啦,我选择从应用 -> 驱动的路线,先学会用,再了解为什么这样用。Linux里面集合了全世界最优秀的思想和智慧,很值得学习
发表于 2019-4-29 10:20:49 | 显示全部楼层
楼主挺厉害的啊!
 楼主| 发表于 2019-4-29 10:50:11 | 显示全部楼层

菜鸟一名,在众多大神面前不敢造次
发表于 2019-4-29 12:44:42 来自手机 | 显示全部楼层
楼主威武!
 楼主| 发表于 2019-4-29 13:54:06 | 显示全部楼层

感谢关注,谢谢支持!
 楼主| 发表于 2019-4-29 13:55:20 | 显示全部楼层
源码下载路径已更改:点击这里
友情提示:标题不合格、重复发帖,将会被封锁ID。详情请参考:论坛通告:封锁ID、获得注册邀请码、恢复被封ID、投诉必读
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|阿莫电子论坛(原ourAVR/ourDEV) ( 公安备案:44190002001997(交互式论坛) 工信部备案:粤ICP备09047143号 )

GMT+8, 2019-10-18 01:03

阿莫电子论坛, 原"中国电子开发网"

© 2004-2018 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表