`使用QT程序控制Linux开发板上的继电器(一)`

  • 测试平台介绍
  • 测试系统接线
  • 编写PlatformIO驱动程序
    • 修改设备树
    • 烧录设备树
    • Platform字符设备驱动设备框架
    • 完善驱动框架
  • 编写测试程序
  • 应用程序测试

测试平台介绍

系统为Ubuntu16.04LTS,QT版本为5.12,开发板是正点原子的IMX6ULL.ALPHA(NAND)开发板,使用的是正点原子出厂系统。最终要实现的效果为:通过点击QT程序上的按钮来控制连接在开发板IO上继电器的通断,进而控制连接在继电器上的LED的点亮与熄灭。

测试系统接线

系统接线如图所示,继电器控制端接到UART3_RX引脚:

编写PlatformIO驱动程序

修改设备树

首先需要修改设备树,添加对应的设备节点,重点是设置compatible以及引脚定义pinctrl-name以及pinctrl-0,因为platform总线需要通过compatible值来匹配设备和驱动,pinctrl-name和pinctrl-0对应实际用于控制的IO。
需要注意的是,UART3_RX在原来的设备树里,已经被使用了。需要将使用这个引脚的设备默认状态设置为disabled,或是直接将其它地方用到这个引脚的代码注释掉,否则后面加载模块的时候会加载不上,后面编写驱动程序的部分会提到。


修改后使用make dtbs编译设备树文件,生成设备树文件imx6ull-14x14-nand-4.3-480x272-c.dtb、imx6ull-14x14-nand-4.3-800x480-c.dtb、imx6ull-14x14-nand-7-800x480-c.dtb、imx6ull-14x14-nand-7-1024x600-c.dtb、
imx6ull-14x14-nand-10.1-1280x800-c.dtb、imx6ull-14x14-nand-hdmi.dtb、imx6ull-14x14-nand-vga.dtb。

烧录设备树

通过U盘或是网络将生成的设备树文件拷贝到开发板上后,根据自己显示屏的尺寸和分辨率的情况,使用 nandwrite 指令将对应的设备树文件烧写到
NAND对应的地址中。完成后重启,在启动时,系统会根据屏幕类型自动选择设备树。

flash_erase /dev/mtd3 0 0
nandwrite -p /dev/mtd3 $Cur_Dir/imx6ull-14x14-nand-4.3-480x272-c.dtb
nandwrite -s 0x20000 -p /dev/mtd3 $Cur_Dir/imx6ull-14x14-nand-4.3-800x480-c.dtb
nandwrite -s 0x40000 -p /dev/mtd3 $Cur_Dir/imx6ull-14x14-nand-7-800x480-c.dtb
nandwrite -s 0x60000 -p /dev/mtd3 $Cur_Dir/imx6ull-14x14-nand-7-1024x600-c.dtb
nandwrite -s 0x80000 -p /dev/mtd3 $Cur_Dir/imx6ull-14x14-nand-10.1-1280x800-c.dtb
nandwrite -s 0xa0000 -p /dev/mtd3 $Cur_Dir/imx6ull-14x14-nand-hdmi.dtb
nandwrite -s 0xc0000 -p /dev/mtd3 $Cur_Dir/imx6ull-14x14-nand-vga.dtb
sync
reboot

如果使用的是EMMC版本的开发板,则使用以下命令(根据屏幕尺寸选择对应的设备树,以4.3寸800x480RGB屏为例)

cp imx6ull-14x14-emmc-4.3-800x480-c.dtb /run/media/mmcblk1p1
sync
reboot

这样,设备树就成功烧录到系统中了,烧录设备树后进入/proc/device-tree查看创建的节点是否存在,如果设备树存在就说明设备节点添加成功了。

Platform字符设备驱动设备框架

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
......
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/delay.h>/*设备匹配后probe函数执行*/
static int relays_probe(struct platform_device *dev)
{printk("relays module probe\r\n");return 0;
}/*移除platform设备的时候执行*/
static int relays_remove(struct platform_device *dev)
{printk("relays module remove\r\n");return 0;
}/*匹配列表*/
static const struct of_device_id relays_of_match[] = {{.compatible = "fzu,relays"},{ }
};/*platform驱动结构体*/
static struct platform_driver relays_driver = {.driver = {.name = "fzu,relays",.of_match_table = relays_of_match,},.probe = relays_probe,.remove = relays_remove,
};static int __init relays_init(void)
{printk("relays module init\r\n");return platform_driver_register(&relays_driver);
}static void __exit relays_exit(void)
{platform_driver_unregister(&relays_driver);printk("relays module exit\r\n");
}module_init(relays_init);
module_exit(relays_exit);MODULE_AUTHOR("xxxxxxx");
MODULE_LICENSE("GPL");

编写好设备框架后,编译,将驱动文件放到IMX6ULL开发版上,加载模块。如果引脚已经被其它设备占用,就会出现这样的报错:

这是因为这个引脚已经被作为UART3的RX使用了,如果要将这个引脚复用为普通IO,就需要在设备树禁用UART3,将status修改为disabled,或是直接修改pinctrl_uart3,将UART3_RX引脚注释掉。

重新编译,烧录设备树后,成功加载模块。

完善驱动框架

接下来就是往驱动框架里添加驱动注册、自动添加设备节点等内容了

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/delay.h>#define RELAYS_CNT      1
#define RELAYS_NAME     "relays"     #define RELAYS_OFF      0
#define RELAYS_ON       1/*设备结构体*/
struct relays_dev {dev_t devid;struct cdev cdev;struct class *class;struct device *device;int major;int minor;struct device_node *nd;int relaysgpio;
};struct relays_dev relaysdev;int relays_open(struct inode *inode,struct file *filp)
{//printk("open\r\n");filp->private_data = &relaysdev;return 0;
}int relays_write (struct file *filp, const char __user *buf, size_t cnt, loff_t *ppos)
{//获取私有数据struct relays_dev *dev = (struct relays_dev*)filp->private_data;int retval;unsigned char datebuf[1];//printk("write\r\n");//从应用程序获取数据retval=copy_from_user(datebuf,buf,cnt);if(retval < 0){printk("kernel write failed\r\n");return -EFAULT;}   if(datebuf[0] == RELAYS_ON){gpio_set_value(dev->relaysgpio,0);//打开LED灯}else if(datebuf[0] == RELAYS_OFF){gpio_set_value(dev->relaysgpio,1);//关闭LED灯}return 0;
}int relays_release (struct inode *inode, struct file *filp)
{//struct dtsled_dev *dev = (struct dtsled_dev*)filp->private_data;return 0;
}struct file_operations relays_fops = {.owner = THIS_MODULE,.open = relays_open,.write = relays_write,.release = relays_release,
};/*设备匹配后probe函数执行*/
static int relays_probe(struct platform_device *dev)
{int ret = 0;printk("relays module probe\r\n");/*注册设备号*/relaysdev.major = 0;if(relaysdev.major){//设备号已指定relaysdev.devid = MKDEV(relaysdev.major,0);ret = register_chrdev_region(relaysdev.devid,RELAYS_CNT,RELAYS_NAME);}else{ret = alloc_chrdev_region(&relaysdev.devid,0,RELAYS_CNT,RELAYS_NAME);}if(ret < 0){printk("relays module fail devid\r\n");goto fail_devid;}else{relaysdev.major = MAJOR(relaysdev.devid);relaysdev.minor = MINOR(relaysdev.devid);printk("major:%d minor:%d \r\n",relaysdev.major,relaysdev.minor);}/*自动添加设备节点*//*注册设备*/relaysdev.cdev.owner = THIS_MODULE;cdev_init(&relaysdev.cdev,&relays_fops);cdev_add(&relaysdev.cdev,relaysdev.devid,RELAYS_CNT);/*注册类*/relaysdev.class = class_create(THIS_MODULE,RELAYS_NAME);if(IS_ERR(relaysdev.device)){ret = PTR_ERR(relaysdev.device);goto fail_class;}/*创建设备*/relaysdev.device = device_create(relaysdev.class,NULL,relaysdev.devid,NULL,RELAYS_NAME);if(IS_ERR(relaysdev.device)){ret = PTR_ERR(relaysdev.device);goto fail_device;}/*初始化IO*/relaysdev.nd = of_find_node_by_path("/relays");if(relaysdev.nd == NULL){ret = -EINVAL;printk("relays module fail node\r\n");goto fail_nd;}relaysdev.relaysgpio = of_get_named_gpio(relaysdev.nd,"gpios",0);if(relaysdev.relaysgpio < 0){ret = -EINVAL;printk("relays module fail gpio\r\n");goto fail_gpio;}gpio_request(relaysdev.relaysgpio,"gpios");gpio_direction_output(relaysdev.relaysgpio,0);return 0;fail_gpio:
fail_nd:device_destroy(relaysdev.class,relaysdev.devid);
fail_device:class_destroy(relaysdev.class);
fail_class:unregister_chrdev_region(relaysdev.devid,RELAYS_CNT);cdev_del(&relaysdev.cdev);
fail_devid:return ret;
}/*移除platform设备的时候执行*/
static int relays_remove(struct platform_device *dev)
{printk("relays module remove\r\n");//注销设备时关闭gpio_set_value(relaysdev.relaysgpio,0);/*删除cdev*/cdev_del(&relaysdev.cdev);/*注销设备号*/unregister_chrdev_region(relaysdev.devid,RELAYS_CNT);/*注销设备*/device_destroy(relaysdev.class,relaysdev.devid);/*注销类*/class_destroy(relaysdev.class);//释放iogpio_free(relaysdev.relaysgpio);return 0;
}/*匹配列表*/
static const struct of_device_id relays_of_match[] = {{.compatible = "fzu,relays"},{ }
};/*platform驱动结构体*/
static struct platform_driver relays_driver = {.driver = {.name = "fzu,relays",.of_match_table = relays_of_match,},.probe = relays_probe,.remove = relays_remove,
};static int __init relays_init(void)
{printk("relays module init\r\n");return platform_driver_register(&relays_driver);
}static void __exit relays_exit(void)
{platform_driver_unregister(&relays_driver);printk("relays module exit\r\n");
}module_init(relays_init);
module_exit(relays_exit);MODULE_AUTHOR("xxxxxxx");
MODULE_LICENSE("GPL");

编写完驱动后,重新编译驱动,将驱动拷贝到开发板上,加载驱动后,进入/dev查看,可以看到设备节点存在

接下啦就要写一个测试程序来测试驱动功能

编写测试程序

在Linux系统中,控制IO的高低电平,需要在应用层对IO对应的设备文件写入数据,驱动从应用层中读取数据,做出对应的处理。例如用户向/dev/relays文件写入一个0,那么relays对应的驱动文件就可以读取到这个数,并做出处理。
驱动部分已经完成,这部分要完成的就是从应用层发送一个正确的数据给驱动。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>/*
*argc:应用程序参数个数
*argv[]:参数内容字符串
*relaysAPP /dev/relays 0(1)
*/
int main(int argc,char *argv[])
{int fd;if(argc!=3){printf("usage error\r\n");return -1;}char *filename;unsigned char datebuf[1];filename = argv[1];fd = open(filename,O_RDWR);if(fd < 0){printf("open file %s failed\r\n",filename);return -1;}int ret;datebuf[0]=atoi(argv[2]);//将字符转换为数字ret=write(fd,datebuf,1);if(ret < 0){printf("led control failed\r\n");close(fd);return -1;}close(fd);
}

应用程序测试

编译完成后,使用交叉编译器编译应用程序

arm-linux-gnueabihf-gcc relaysAPP.c -o relaysAPP

编译完成后,将编译生成的可执行文件拷贝到开发板上运行

./relaysAPP /dev/relays 0
./relaysAPP /dev/relays 1

接下来编写QT程序
使用QT程序控制Linux开发板上的继电器(二)

使用QT程序控制Linux开发板上的继电器(一)相关推荐

  1. 嵌入式Linux开发板上NFS文件系统的使用【ZT】

    本文转载于http://www.dz863.com/RTOS/Embedded-linux/Linux-NFS.htm 本文描述了在开发嵌入式linux系统时调试程序的一个方法,本文以深圳远峰的YF2 ...

  2. 让kaldi在Linux开发板上运行起来~ (测试运行篇)

    [kaldi各文件解释] /egs:不同语料例子的执行脚本文件 /tools:存放asr过程中用到的库 /src:存放实际执行的c++算法 解码工具(src/onlinebin中): online-g ...

  3. linux开发板上程序如何调试,linux开发板调试典型方法

    tftpd sudo apt-get install tftpd openbsd-inetd 将/etc/inetd.conf中的最后一个路径设置成你希望让客户端存取文件的目录例如下面的"/ ...

  4. 我在这块牛X的A40i Linux开发板上点了个流水灯

    为啥要搞这个linux评估板? 小飞哥自毕业以来,工作5年了,一直从事的都是嵌入式MCU层面的开发工作,还从未涉足过linux开发相关的领域,最近的一次应该是翻过<鸟哥的linux私房菜> ...

  5. linux开发板 pc 通讯_从51单片机到Linux 开发板运行hello world(教程4)

    大家好,我是兔子. 是一个嵌入式软硬件工程师. 正在从单片机开发转岗Linux开发. 这次教大家如何在Linux开发板上运行可执行文件. 简单起见,不涉及到硬件.直接和教程2一样,能在PC虚拟机上运行 ...

  6. 【嵌入式Linux应用】初步移植MQTT到Ubuntu和Linux开发板

    1. 概述 ​ 本篇主要是记录将MQTT移植安装到百问网STM32MP157开发板上,并且是跑一下MQTT的一个例程来验证,要完成本次移植安装,必须要保证电脑和开发板都能上网.. 2. 软件平台 ​ ...

  7. qt 4.8.4 linux,Tslib和Qt 4.8.4与在开发板上的移植

    这篇博客算是接着Linux 下编译.安装.配置 QT这篇的,搞完PC平台的就来弄一下嵌入式平台的,Linux的东西搞起来麻烦的地方就在于太杂.太散,像QT,各版本之间都有很多差异,因此在网络上查找资料 ...

  8. linux开发板汉字显示,Linux Qt 及Arm开发板汉字显示

    作为一个优秀的开发人员,我就不说废话了. 很多人在 使用Qt做嵌入式开发时,会遇到一个问题: 汉字显示,网上有好多方法,并且针对 Qt5 以前的版本和Qt5 的版本有不同的方法,同样,我也遇到了这个问 ...

  9. 摄像头在liunx上的QT显示和OK6410 ARM开发板上的使用

    摄像头在liunx上的QT显示和OK6410 ARM开发板上的使用 发布者:旺旺雪饼   时间:2013-01-05 16:56:09 环境: Ubuntu10.04 arm linux OS: 3. ...

最新文章

  1. CNN收购Beme视频分享APP 11个员工也将加入
  2. 牛客网7-教417题解
  3. 【NLP】Transformer详解
  4. matlab 已知函数值纵坐标值(Y值)获得对应的横坐标
  5. 如何自定义Tableau 调色板
  6. 【CTR模型】TensorFlow2.0 的 xDeepFM 实现与实战(附代码+数据)
  7. 服务器物理内存总是九十几,服务器物理内存使用率90以上
  8. java dumpstack_Java获取执行进程的dump文件及获取Java stack
  9. pygame做的著名游戏_用Python和Pygame写游戏-从入门到放弃(1)
  10. P1830 轰炸III
  11. Pytest 自动化测试框架
  12. pg数据库客户端linux,PostgresSQL客户端pgAdmin4使用
  13. 基于TI DRV8424驱动步进电机实现调速和行程控制
  14. EBS系统打补丁(Patch)
  15. 山一程,水一程,身向榆关那畔行,夜深千帐灯。
  16. Python调用动态链接库DLL文件
  17. 基于FPGA的数字交通红绿灯Verilog开发Modelsim仿真
  18. Nat Nanotechnol | 朱涛/陈春英等合作发现碳纳米管呼吸暴露后的延迟毒性导致小鼠原位乳腺肿瘤的多发性广泛转移...
  19. spoolsv.exe占用100%的解决之道
  20. 美国征信巨头Equifax遭黑客入侵,1.43亿公民身份数据泄漏

热门文章

  1. 在Linux虚拟机安装MariaDB数据库
  2. 计算机远程桌面连接连接不上,解决远程桌面连接不上推荐方法
  3. python编写游戏加速器_Numba:用CUDA加速的高性能Python编译器
  4. 如何锁定计算机密码,如何设置电脑密码锁详细方法
  5. 【天光学术】哈佛传奇教授Whitesides的论文写作之“道”
  6. 上海大学计算机学院同等学力申硕,上海大学同等学力申硕通过率高不高?
  7. 设计模式应用有感总结
  8. echars 饼图环形图组件修改 自定义图例 重新渲染 显示对应颜色对应区域
  9. Vue3图片打点自定义标记颜色形状(完整教程可直接复制项目)
  10. web实训知识点_0312