Qt车载系统有的功能:音视频播放、天气显示、地图显示、起点搜索和终点搜索,路线导航,倒车影像,倒车障碍实时距离显示,温湿度曲线绘制,交叉编译,程序优美界面。

 

使用技术:  Qt应用程序开发,Linux 驱动程序开发,HC-SR04超声波模块驱动,Linux C嵌入式应用,JavaScript,Qt WebEngine(内嵌HTML),HTTP,JSON。

 

基于I.MX6ULL + LINUX驱动程序 Qt车载系统

 

 

 

 

 

44151a6634f1a34846fd8193edbc589d.png

 

 

HC-SR04超声波模块驱动:

使用新字符设备驱动开发框架  (配置寄存器方式)  编写好HC-SR04超声波模块驱动并提供应用层控制接口,然后将 .ko 文件拷入开发板加载入内核。每次应用空间对驱动文件读操作,将返回超声波模块一次声波来回的间隔时间(纳秒),根据公式将此数据转换为秒,然后带入L=V * T / 2,得出倒车距离。   这里用让驱动层返回纳秒的而不是距离的原因是:Linxu内核对浮点数操作挺麻烦的就直接传给应用层让QT去搞简简单单。

驱动源码:初始化两个GPIO引脚

#ifndef _MAIN_
#define _MAIN_

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/ktime.h>

#include<linux/types.h>

/* 寄存器物理地址 */
#define CCM_CGPR1_BASE  (0x020C406C)  //GPIO1


#define MUX_CTL_PAD_GPIO1_IO04_BASE   (0x020E006C)   
#define MUX_CTL_PAD_GPIO1_IO09_BASE   (0x020E0080)

#define PAD_CTL_PAD_GPIO1_IO04_BASE      (0x020E02F8)
#define PAD_CTL_PAD_GPIO1_IO09_BASE       (0x020E030C)

#define GPIO1_DR_BASE (0x0209C000)
#define GPIO1_GDIR_BASE (0x0209C004)

/* 映射后的寄存器虚拟地址指针 */
static void __iomem * CCM_CGPR1;

static void __iomem *MUX_CTL_PAD_GPIO1_IO04;
static void __iomem *MUX_CTL_PAD_GPIO1_IO09;

static void __iomem *PAD_CTL_PAD_GPIO1_IO04;
static void __iomem *PAD_CTL_PAD_GPIO1_IO09;

static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

void register_init(void);
void HC_SR04_Init(void);
void myexit(void);
unsigned long long Test_Distance(void);
void SetBits(void);     //25
void ResetBits(void);
unsigned int ReadInputDataBit(void);    //24


void register_init(void)
{
   printk("register_init\r\n");

  CCM_CGPR1=ioremap(CCM_CGPR1_BASE,4);
	MUX_CTL_PAD_GPIO1_IO04=ioremap(MUX_CTL_PAD_GPIO1_IO04_BASE, 4);
	MUX_CTL_PAD_GPIO1_IO09=ioremap(MUX_CTL_PAD_GPIO1_IO09_BASE,4);
	PAD_CTL_PAD_GPIO1_IO04=ioremap(PAD_CTL_PAD_GPIO1_IO04_BASE,4);
	PAD_CTL_PAD_GPIO1_IO09=ioremap(PAD_CTL_PAD_GPIO1_IO09_BASE,4);
	GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
	GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);
}


unsigned long long Test_Distance(void)
{
   unsigned long long start_time, end_time, diff_time;

    SetBits(); //25
		udelay(30);
    ResetBits(); //25

	 //read 24
    while(ReadInputDataBit()==0);

    start_time = ktime_get_ns();

    while(ReadInputDataBit()==1);

    end_time = ktime_get_ns();
		diff_time = end_time - start_time;
		mdelay(100);

    return diff_time;
}


void HC_SR04_Init(void)
{
	    unsigned int val;

  /*开启GPIO1时钟*/
	 val = readl(CCM_CGPR1);  
	 val &= ~(3 << 26);	/* 清除以前的设置 */
   val |= (3 << 26);	/* 设置新值 */
	 writel(val, CCM_CGPR1);


	/*将引脚复用为GPIO1_IO1 2*/
	writel(5, MUX_CTL_PAD_GPIO1_IO04);  //4
	writel(5, MUX_CTL_PAD_GPIO1_IO09);  //9

	//设置电气属性
	writel(0xF080, PAD_CTL_PAD_GPIO1_IO04);  //4输入模式
	writel(0x10B0, PAD_CTL_PAD_GPIO1_IO09);  //9输出模式


	/* 4、设置GPIO1_IO4为输入功能 */	
	val = readl(GPIO1_GDIR);
	val &= ~(1 << 4);	/* 输入*/
	writel(val, GPIO1_GDIR);

	/* 4、设置GPIO1_IO9为输出功能 */	
	val = readl(GPIO1_GDIR);
	val |= (1 << 9);	/* 输出*/
	writel(val, GPIO1_GDIR);
   
}

void SetBits(void)     //9
{
	u32 val;
	val = readl(GPIO1_DR);
	val |= (1 << 9);	           /*9*/
	writel(val, GPIO1_DR);
}

void ResetBits(void)
{
  u32 val;
	val = readl(GPIO1_DR);
	val &= ~(1 <<9);	       /* 9*/
	writel(val, GPIO1_DR);
}

unsigned int ReadInputDataBit(void)    //4
{
	u32 val;
	val=readl(GPIO1_DR);
	return (((val) >> 4) & 0x1);
}

void myexit(void)
{
	iounmap(CCM_CGPR1);
  //.......  
}

#endif // !_MAIN_

 Qt计算距离

           int fd= open("/dev/newchriobee",O_RDWR);//打开文件
           if(fd<0)
            return;
           read(fd,buffer,sizeof(buffer));
           if(buffer[0]=='\0')return;

           QString str=buffer;  //时间 str  纳
           float re_ms=str.toDouble()/1000000000;   //得到秒
           float distance=(re_ms*340)/2;  //距离公式
          ui->label->setText("倒车障碍距离:"+QString::number(distance,'f',2));

地图显示、起点搜索和终点搜索,路线导航:

这个需要用到模块 Qt WebEngine;基于Qt与JavaScript的交互更新HTML。高德地图官网有web API接口,直接复制JS的案例,就可以创建出地图对象显示在HTML上,Qt用 Qt WebEngine 显示本地网页内嵌HTML,Qt然后创建HTML对象,定义需要暴露的槽函数和信号,设置channel对象,这样HTML客户端就可以通过channel.objects属性访问所有已发布的对象,完成交互。JS给Qt的数据都JSON格式,需要解析,然后根据数据发送信号给主窗口来更改ui界面

<script>

    //高德地图API,创建map对象,创建出来就有了地图
    var map = new AMap.Map('container', {
        viewMode: '2D', // 默认使用 2D 模式,如果希望使用带有俯仰角的 3D 模式,请设置 viewMode: '3D'
        zoom: 11, // 初始化地图层级

    });

    //加载这个地图的自动补全插件
    AMap.plugin(["AMap.Autocomplete"], function () {
    });


    //QT与HTML交互       
    var mchannel;
    window.onload = function () {
        if (typeof qt != 'undefined') {
            new QWebChannel(qt.webChannelTransport, function (channel) {
                // 与QT MyChannel的信号绑定
                channel.objects.qtChannel.qiDianChanged.connect(_qiDianChanged);    //qiDianChanged是QT的信号,然后绑定_qiDianChanged函数 
                channel.objects.qtChannel.destChanged.connect(_destChanged);
                channel.objects.qtChannel.sendQiLocation.connect(setQiLocation);
                channel.objects.qtChannel.sendDestLocation.connect(setDestLocation);
                channel.objects.qtChannel.selectRoute.connect(setRoute);
                mchannel = channel;
            });
        }
        else {
            alert("qt对象获取失败!");
        }
    }
    var autoComplete = new AMap.Autocomplete({});

    //QT的起点搜索栏 内容变化时就会触发该信号,然后再调用高德地图的api搜索,然后把搜索的内容发送给QT
    function _qiDianChanged(pos) {

        autoComplete.search(pos, function (status, result) {
            // 搜索成功时,result即是对应的匹配数据
            mchannel.objects.qtChannel.qiAutocomplete(result);
        });
    }

//QT的终点搜索栏 内容变化时就会触发该信号,然后再调用高德地图的api搜索,然后把搜索的内容发送给QT
    function _destChanged(pos) {
        autoComplete.search(pos, function (status, result) {
            // 搜索成功时,result即是对应的匹配数据
            mchannel.objects.qtChannel.destAutocomplete(result);
        });
    }


    var startIcon = new AMap.Icon({
        // 图标尺寸
        size: new AMap.Size(25, 34),
        // 图标的取图地址
        image: 'http://a.amap.com/jsapi_demos/static/demo-center/icons/dir-marker.png',
        // 图标所用图片大小
        imageSize: new AMap.Size(135, 40),
        // 图标取图偏移量
        imageOffset: new AMap.Pixel(-9, -3)
    });


    var startMarker;
    var startLocation;
    //QT中QListView选中地点后,会发送信号,并把位置信息带上,然后这里调用高德地图API设置起点的标志,并把坐标放在startLocation
    function setQiLocation(location) {

        if (startMarker != null) {
            map.remove(startMarker);
        }
        var pos = location.split(",");
        startLocation = pos;
        map.setCenter(pos);
        startMarker = new AMap.Marker({
            position: pos,
            icon: startIcon,
            offset: new AMap.Pixel(-13, -30)
        });
        map.add(startMarker);
    }

    // 创建一个 icon
    var endIcon = new AMap.Icon({
        size: new AMap.Size(25, 34),
        image: 'http://a.amap.com/jsapi_demos/static/demo-center/icons/dir-marker.png',
        imageSize: new AMap.Size(135, 40),
        imageOffset: new AMap.Pixel(-95, -3)
    });
    var endMarker;
    var endLocation;
     //QT中QListView选中地点后,会发送信号,并把位置信息带上,然后这里调用高德地图API设置终点的标志,并把坐标放在endLocation
    function setDestLocation(location) {

        if (endMarker != null) {
            map.remove(endMarker);
        }
        var pos = location.split(",");
        endLocation = pos;
        map.setCenter(pos);
        endMarker = new AMap.Marker({
            position: pos,
            icon: endIcon,
            offset: new AMap.Pixel(-13, -30)
        });
        map.add(endMarker);
    }


    var driving = new AMap.Driving({
        map: map,
        panel: "panel"
    });
    // 点击导航按钮后,发送信号,然后这里判断endLocation和startLocation是否为NULL,不为空就说明设置了起点,然后调用高德地图API进行导航
    function setRoute() {

        if (endLocation != null && startLocation != null) {
            driving.search(startLocation, endLocation, function (status, result) {
                if (status === 'complete') {
                    log.success('绘制驾车路线完成')
                    map.setCenter(startLocation)
                } else {
                    log.error('获取驾车数据失败:' + result)
                }
            });
        }
        else {
            alert("请输入起点和终点");
        }

    }
</script>

 

 

天气显示

天气显示通过HTTP协议访问API接口,得到一个JSON字符串,然后将其解析。API接口 http://t.weather.itboy.net/api/weather/city/+城市编号

 

 

音视频播放

通过QMediaPlayer,QMediaPlaylist 创建媒体对象和媒体列表,媒体列表会自动扫描当前目录下的music文件夹自动更新,切换上一首,下一首功能根据媒体列表对象来实现,播放模式,进度条扫描的都i有相对于API。视频的话和上面基本一样,需要设置一个播放视频的容器QVideoWidget

 

温湿度曲线绘制

正点原子官方已经写好了DTH11的驱动程序,我们直接加载到内核,用Qt定时器定时去读该文件获取温湿度数据,然后绘制就好了。

 

总结:

Qt车载系统都是基本功和API调用,硬件也涉及的少,交叉编译的话WEB模块是编译不过去的,当时我问了正点原子官方   他们原话:(6ull不支持webengewidgets。就算你能交叉编译出来,6ull估计跑不动。6ull上有webview。webengine编译相关源码500MB。mp157和rk3568上有webengine,如果你有这两板子)

    还有一个bug  板子上Qt程序对两个驱动文件读数据时会直接卡死了,我查了好久资料都查不到原因,不管了都无所谓,能跑就行。

 

 

 

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐