嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3

(本人业余时间接相关外包,欢迎联系!)

linux内核版本:3.4.2

qt 版本:5.6.0

交叉编译工具:arm-linuxgcc 4.4.3

开发板:JZ2440V3

linux驱动编程环境:vscode (环境搭建可参考博客:https://blog.csdn.net/Ningjianwen/article/details/88075042)

如何交叉编译qt应用程序,并在开发板上运行,可参考博客:https://blog.csdn.net/Ningjianwen/article/details/87879668

该博客源码分享: https://download.csdn.net/download/ningjianwen/10998347(内含可执行文件,可直接运行)

目标

最终效果一个主屏幕,有一个LED控制按钮, 通过该按钮进入led控制界面,通过该界面控制led灯的亮灭,如下图所示.

嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第1张图片嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第2张图片

1. 学会编写一个简单的嵌入式Linux字符设备驱动

2.  学会编写一个简单的linux 文件io应用程序.

3. 学会使用qt creator建立嵌入式qt工程.

4. 学会qt5.6设置背景图片.

5. 学会qt5.6进行界面切换.

6. 学会如何使用qt程序操作jz2440的GPIO.

 

一  驱动程序编写

1.硬件电路如下图所示,可以看出三个LED灯与GPF4,5,6相连接。且当io口输出为高电平时,led灯熄灭,当io输出低电平时,led灯点亮.

嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第3张图片

 

2.字符设备驱动编写思路:

1.编写模块的入口函数与出口函数.(module_init,module_exit)
2.定义file_operations结构体,确定需要实现哪些函数.最基本的有open ,read ,write函数,其他根据需要添加
3.编写open,read,write等函数.

驱动的write函数需要方便单独控制每一个LED灯,也需要方便同时控制所有LED灯.

3.代码实现:

#include   //定义了THIS_MODULE宏
#include       //定义了file_operations结构体
#include    //定义了copy_to_user函数
#include         //定义了ioremap 与iounremap函数
#include   //定义了class_create/device_create/class_destory/device_destory函数
                           //定义了class 与 class_device结构体
#include //包含了GPIO相关宏
#include     //包含了s3c2410_gpio_cfgpin等io操作函数
#define LED_NUM 3
static struct class *leddrv_class;
static struct class_device	*leddrv_class_dev;

/**应用程序的open函数时,最终会调用该函数,配置LED控制引脚为输出*/
static int led_drv_open(struct inode *inode, struct file *file)
{
	s3c2410_gpio_cfgpin(S3C2410_GPF(4),S3C2410_GPIO_OUTPUT);
	s3c2410_gpio_cfgpin(S3C2410_GPF(5),S3C2410_GPIO_OUTPUT);
	s3c2410_gpio_cfgpin(S3C2410_GPF(6),S3C2410_GPIO_OUTPUT);
	return 0;
}

/**
* 应用程序的read函数时,最终会调用该函数
* 返回的buf中有3个数据, 第一个数据对应led1的亮灭状态,一次类推.
*/
ssize_t led_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	unsigned char led_vals[LED_NUM];
	if (size > LED_NUM || size < 0)
		return -EINVAL;
	led_vals[0] = s3c2410_gpio_getpin(S3C2410_GPF(4));
	led_vals[1] = s3c2410_gpio_getpin(S3C2410_GPF(5));
	led_vals[2] = s3c2410_gpio_getpin(S3C2410_GPF(6));
	copy_to_user(buf, led_vals, size);
	return size;
}

/**
 * 应用程序的write函数时,最终会调用该函数,调用一次只能控制一个led灯的两灭
 * buf:第一个byte表示控制第几个led灯范围0~3(0表示控制所有灯), 第2个byte取值1=亮灯,0=灭灯
 * size:只能等于2
 */
ssize_t led_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos)
{
	unsigned char led_vals[2];
	unsigned char value = 0,led_num;
	if ( size != 2)//
			return -EINVAL;

	copy_from_user(led_vals, buf, size);
	value = led_vals[1] ? 0: 1;
	led_num = led_vals[0];
	if(led_num == 0){//同时控制所有led
		s3c2410_gpio_setpin(S3C2410_GPF(4),value);
		s3c2410_gpio_setpin(S3C2410_GPF(5),value);
		s3c2410_gpio_setpin(S3C2410_GPF(6),value);
	}else{//单独控制单个led
		s3c2410_gpio_setpin(S3C2410_GPF(3+led_num),value);
	}
	return 0;
}

static struct file_operations led_drv_fops = {
    .owner  =   THIS_MODULE,
    .open   =   led_drv_open,
	.read	=	led_drv_read,
	.write  =   led_drv_write,
};


int major;
dev_t led_dev;
/**
 *初始化函数在加载该模块时就会调用,
 *注册设备
 */
static int led_drv_init(void)
{
	// alloc_chrdev_region()
	major = register_chrdev(0, "led_drv", &led_drv_fops);
	leddrv_class = class_create(THIS_MODULE, "led_drv");
	if(IS_ERR(leddrv_class))
		return PTR_ERR(leddrv_class);

	/* 加载模块后,/dev目录下会新增 leds */
	leddrv_class_dev = device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "leds");
	 if(unlikely(IS_ERR(leddrv_class_dev)))
	  return PTR_ERR(leddrv_class_dev);

	printk("led_drv_init\n");
	return 0;
}

static void led_drv_exit(void)
{
	unregister_chrdev(major, "led_drv");
	device_destroy(leddrv_class, MKDEV(major, 0));//卸载设备
	class_destroy(leddrv_class);//删除class类
}

module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");

4. Makefile文件实现:

KERN_DIR = /home/ningjw/linux-3.4.2

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= led_drv.o

二:Qt5应用程序编写

先来看看这个工程最终包含了那些文件

嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第4张图片

1. 使用qt creator新建工程:

嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第5张图片嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第6张图片嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第7张图片

注:

  • 只选取了关键的几个图片; 
  • 图2中的ARM-LINUX如何来的,可参考我开头提到的博客;   
  • 图2中选择了两个Kit,通过ARM-LINUX构建的程序可运行在jz2440开发板上,通过Desktop..构建的程序可在PC运行,方便调试.
  • 新建工程后,需要将工程中的nullptr指针改为0,才可以编译通过.

2. 往现有工程添加界面

文件->新建文件或项目 (命名ledcontrol.ui)

嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第8张图片                嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第9张图片

3. 设计主界面

  • 进入界面设计页面,并调整geometry属性为480 X 272 (对应JZ2440的长宽)

嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第10张图片嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第11张图片

  • 往界面放置一个Frame,同样设置geometry属性为480 X 272 (即铺满窗口),
  • 添加一个图片资源文件  (文件->新建文件或项目,调出下面窗口)嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第12张图片    嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第13张图片

      嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第14张图片

  • 为之前添加的Frame添加背景图片

   嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第15张图片         嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第16张图片

  • 添加一个50 X 50的PushButton控件,并为该按钮设置背景图片. (按钮的长宽通过设置geometry属性实现, 设置背景图片通过设置styleheet实现, 这里就不再贴图了,仿照上面frame的方式就行)
  • 选中PushButton控件,"鼠标右键->转到槽->clicked()->OK"

嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第17张图片

  • 添加一个label空间,显示"LED控制"放在PushButton下面.

通过以上步骤,主界面设计完成,效果图如下.

嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第18张图片

4 设计led控制界面

  • 设计led控制界面,如下图所示,这里都是控件,托过来摆放就行,就不在详细说明了.

嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第19张图片

  • 同时选中"LED1控制"按钮上的两个QRadioButton("开与关")控件, 通过操作  "鼠标右键->添加到按钮组->新建按钮组" 将这两个控件作为一组, 这样在同一时刻,LED1上的开关只能选择一个,且不与其他QRadioButton控件产生关系.

嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第20张图片

  • 对其他几组的QRadioButton控件进行同样的操作.
  • 选中"LED1控制"按钮,"鼠标右键->转到槽->clicked()->OK",  并对其他几个按钮做同样的操作. 

嵌入式Qt5.6应用编程之触摸屏控制LED灯 - JZ2440V3_第21张图片

5 实现界面跳转

在主界面点击"LED控制按钮",进入LED控制界面, 在LED控制节目点击"返回主界面"进行返回.

  • 在widget.cpp文件中编写"LED控制"按钮对应的槽函数如下:
#include "ledcontrol.h" //需要包含该头文件,才能引用LedControl

/**
 * 该函数通过"转到槽功能自动生成"
*/
void Widget::on_led_ctl_entrance_clicked()
{
    //函数内容由自己手动编写
    LedControl led_dialog;
    led_dialog.exec();
}
  • 在ledcontrol.cpp中编写"返回主界面"对应的槽函数如下:
#include "widget.h"   //Widget在该头文件声明
/**
 * 该函数通过"转到槽功能自动生成"
*/
void LedControl::on_Button_Return_clicked()
{
    this->hide();//必须执行,否则不会显示主界面
    Widget w;
    w.show();
}
  • 编写"LED1控制"按钮的槽函数如下,其他几个类似,就不贴处代码了.
/* 控制led亮灭的应用函数 */
void control_led(char num,bool value)
{
    int fd;
    char led_val[2];
    led_val[0] = num;//控制第几个led灯
    led_val[1] =value;//控制led灯亮灭:1=亮,2=灭
    fd = open("/dev/leds",O_RDWR);//打开jz2440板上的文件
    if (fd < 0){
        qDebug() << "can't open leds\n";
        return ;
    }
    write(fd, led_val, 2);//写文件,调用该函数,最终会调用驱动中的led_drv_write函数
}

/*"LED1控制"按钮对应的槽函数*/
void LedControl::on_button_led1_ctl_clicked()
{
    bool value = this->ui->radio_led1_open->isChecked();//判断是否选中"开"
    control_led(1,value);
}

六 测试

  • 通过make编译led_drv.c文件,生成内核模块led_drv.ko
  • 构建Qt5_JZ2440工程,生成可执行文件Qt5_JZ2440
  • 配置jz2440从网络启动nfs文件系统
  • 复制led_drv.ko/Qt5_JZ2440到文件系统
  • insmod led_drv.ko加载led驱动
  • ./Qt5_JZ2440 执行该软件.

 

你可能感兴趣的:(嵌入式linux)