Linux学习笔记之用QT界面操作板子LED

2019-07-13 09:19发布

Linux学习笔记之小目标一:用QT界面操作板子LED 一、目标:用QT绘制一个界面,点击开按钮,板子LED点亮,点击关按钮,LED熄灭
二、设计知识点:Linux底层IO驱动,内核编程,QT编程
三、代码部分 1、驱动代码 qt-led.c /**************************************
版本记录: chen :2017-11-11 V1.0
linux内核:2.6
硬件接法:
LED1 –> GPX1_0
LED2 –> GPK1_1
高电平点亮
驱动用法:
设备名称:qt-led
点亮一个灯:LED_ON
熄灭一个灯:LED_OFF
点亮所有灯:ALL_LED_ON
熄灭所有灯:ALL_LED_OFF
说明:
***************************************/ //Linux通用的头文件 #include #include #include #include #include #include //针对板子的头文件 #include #include #include #define DEVICE_NAME "qt-led" /* 设备名称 */ static int LED_Major = 0; /* 主设备号 */ #define LED_ON 1 #define LED_OFF 0 #define FIRST_LED 0 #define SECOND_LED 1 #define ALL_LED 2 /* 打开设备函数,打开的时候仅仅做了GPIO申请 */ static int qt_led_open(struct inode *inode,struct file *file) { int ret; //打印KERN_EMERG是为了区分打印信息 printk(KERN_EMERG"KERN_EMERG:qt_led_open_success! "); //先释放GPIO gpio_free(EXYNOS4_GPL2(0)); //申请GPIO ret = gpio_request(EXYNOS4_GPL2(0),"LED1"); if(ret < 0){ printk(KERN_EMERG"KERN_EMERG:gpio_request EXYNOS4_GPX1_0 fail! "); return -1; } //设置GPIO模式,为output s3c_gpio_cfgpin(EXYNOS4_GPL2(0),S3C_GPIO_OUTPUT); //给GPIO赋初值,相当于是LED默认关 gpio_set_value(EXYNOS4_GPL2(0),0); //第二个LED GPIO,跟第一个操作一模一样 gpio_free(EXYNOS4_GPK1(1)); ret = gpio_request(EXYNOS4_GPK1(1),"LED2"); if(ret < 0){ printk(KERN_EMERG"KERN_EMERG:gpio_request EXYNOS4_GPK1_1 fail! "); return -1; } s3c_gpio_cfgpin(EXYNOS4_GPK1(1),S3C_GPIO_OUTPUT); gpio_set_value(EXYNOS4_GPK1(1),0); return 0; } /* 关闭设备函数,关闭的时候没做啥,释放GPIO */ static int qt_led_release(struct inode *inode,struct file *file){ gpio_free(EXYNOS4_GPL2(0)); gpio_free(EXYNOS4_GPK1(1)); printk(KERN_EMERG"KERN_EMERG:qt_led_release_success! "); return 0; } /* GPIO控制函数 */ static long qt_led_ioctl(struct file *file,unsigned int cmd,unsigned long arg){ printk(KERN_EMERG"KERN_EMERG:qt_led_ioctl! "); //由于CMD组合比较复杂,达到目标我只用0.1就可以了,所以没有组合 //arg可以是很多值,所以用cmd表示开还是关,arg表示哪一路 switch(arg){ case FIRST_LED:{ gpio_set_value(EXYNOS4_GPL2(0),cmd); printk(KERN_EMERG"KERN_EMERG:cmd is :%d,arg is :%ld ",cmd,arg); return 0; break; } case SECOND_LED:{ gpio_set_value(EXYNOS4_GPK1(1),cmd); printk(KERN_EMERG"KERN_EMERG:cmd is :%d,arg is :%ld ",cmd,arg); return 0; break; } case ALL_LED:{ gpio_set_value(EXYNOS4_GPL2(0),cmd); gpio_set_value(EXYNOS4_GPK1(1),cmd); printk(KERN_EMERG"KERN_EMERG:cmd is :%d,arg is :%ld ",cmd,arg); return 0; break; } default: return -EINVAL; break; } } //由于用的是字符设备,不是平台设备,需要class结构体 static struct class *qt_led_class; //file_operations结构体,无论字符设备还是平台设备都需要 static struct file_operations qt_led_fs = { .owner = THIS_MODULE, .open = qt_led_open, .release = qt_led_release, .unlocked_ioctl = qt_led_ioctl, }; static int qt_led_init(void){ printk(KERN_EMERG"Led_Drv_Module_Init! "); //平台设备,要注册主设备号 LED_Major = register_chrdev(0,DEVICE_NAME,&qt_led_fs); if(LED_Major < 0){ printk(KERN_EMERG"Led_Drv_Module_Init_Major_error! "); return LED_Major; } printk(KERN_EMERG"Led_Drv_Module_Init_Major_Success! "); //创建class qt_led_class = class_create(THIS_MODULE,DEVICE_NAME); if(IS_ERR(qt_led_class)){ printk(KERN_EMERG"Err: failed in qt_led_class.! "); return -1; } //创建device device_create(qt_led_class,NULL,MKDEV(LED_Major,0),NULL,DEVICE_NAME); printk(DEVICE_NAME"initialized.! "); return 0; } static void qt_led_exit(void){ //卸载设备,与注册要相反的操作 printk(KERN_EMERG"Led_Drv_Module_exit! "); unregister_chrdev(LED_Major,DEVICE_NAME); device_destroy(qt_led_class,MKDEV(LED_Major,0)); class_destroy(qt_led_class); } MODULE_AUTHOR("Chenxw"); MODULE_DESCRIPTION("qt LED Driver"); MODULE_LICENSE("Dual BSD/GPL"); module_init(qt_led_init); module_exit(qt_led_exit); 2、驱动代码Makefile #!/bin/bash #通知编译器我们要编译模块的哪些源码 #这里是编译qt-led.c这个文件编译成中间文件qt-led.o obj-m += qt-led.o #源码目录变量,这里用户需要根据实际情况选择路径 #作者是将Linux的源码拷贝到目录/home/topeet/workstation下并解压的 KDIR := /home/topeet/workstation/iTop4412_Kernel_3.0 #当前目录变量 PWD ?= $(shell pwd) #make命名默认寻找第一个目标 #make -C就是指调用执行的路径 #$(KDIR)Linux源码目录,作者这里指的是/home/topeet/workstation/iTop4412_Kernel_3.0 #$(PWD)当前目录变量 #modules要执行的操作 all: make -C $(KDIR) M=$(PWD) modules #make clean执行的操作是删除后缀为o的文件 clean: rm -rf *.o *.ko *.mod.c *.symvers *.order 3、由于驱动C语言写的,QT是C++写的,需要写一个给QT的C语言接口文件,给C++提供操作驱动的接口函数 4、内核接口文件led-c.c #include "led-c.h" #include #include #include #include #include //文件句柄申明和初始化 int qt_led_fd = 0; //qt调用的文件打开函数 int qt_led_open(const char *devname){ //devname 是传进来的文件路径,O_RDWR是可读可写 qt_led_fd = open(devname,O_RDWR); if(qt_led_fd < 0){ printf("Open device %s failed! ",devname); return -1; } printf("Qt-led driver is ok,open success! "); return 0; } //qt调用的文件操作函数 int qt_led_ioctl(unsigned int cmd, unsigned long led_num){ //cmd是控制开还是关,led_num就是arg,控制哪一个灯 ioctl(qt_led_fd,cmd,led_num); return 0; } int qt_led_close(void){ //如果有文件打开,就去关闭打开的那个文件 if(qt_led_fd){ close(qt_led_fd); } printf("Dev qt led closed! "); return 0; } 5、内核接口文件的头文件led-c.h #ifndef LEDC_H #define LEDC_H //这是一段cpp的代码,加入extern "C"{和}处理其中的代码 //C++和C对产生的函数名字的处理是不一样的,C++为了兼容C #ifdef __cplusplus extern "C"{ #endif extern int qt_led_open(const char *devname); int qt_led_ioctl(unsigned int cmd,unsigned long led_num); extern int qt_led_close(); extern int qt_led_fd; #ifdef __cplusplus } #endif #endif // LEDC_H 6、窗体文件mainwindow.cpp //窗体设计可以自己一行代码一行代码敲 #include "mainwindow.h" //窗体设计还可以用QT自带的UI设计,区别在于自动生成代码,文件前面自动添加ui标识 #include "ui_mainwindow.h" //add by chen #include "led-c.h" //cmd取值 #define LED_ON 1 #define LED_OFF 0 //arg取值 #define FIRST_LED 0 #define SECOND_LED 1 #define ALL_LED 2 //设备文件位置,名字要跟驱动里面保持一致 #define LED_DEVICE "/dev/qt-led" //add by chen end MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); //add by chen //设置窗口名字,还可以设置图片,这里就不加了 this->setWindowTitle("Chen qt led test!"); //实例化一个按钮 button_led_on = new QPushButton(this); //设置按钮位置,大小 button_led_on->setGeometry(QRect(20,20,100,100)); //设置按钮显示文字 button_led_on->setText("led_on"); //设置点击事件,信号是released,槽是灯亮函数 connect(button_led_on,SIGNAL(released()),this,SLOT(ctl_led_on())); /*下面代码跟上面一样了*/ button_led_off = new QPushButton(this); button_led_off->setGeometry(QRect(140,20,100,100)); button_led_off->setText("led_off"); connect(button_led_off,SIGNAL(released()),this,SLOT(ctl_led_off())); button_led_all_on = new QPushButton(this); button_led_all_on->setGeometry(QRect(20,140,100,100)); button_led_all_on->setText("led_all_on"); connect(button_led_all_on,SIGNAL(released()),this,SLOT(ctl_led_all_on())); button_led_all_off = new QPushButton(this); button_led_all_off->setGeometry(QRect(140,140,100,100)); button_led_all_off->setText("led_all_off"); connect(button_led_all_off,SIGNAL(released()),this,SLOT(ctl_led_all_off())); button_close_led_dev = new QPushButton(this); button_close_led_dev->setGeometry(QRect(260,140,100,100)); button_close_led_dev->setText("close_dev"); /*由于要调试,在手动退出后文件未被关闭,增加一个按钮主动关闭文件*/ connect(button_close_led_dev,SIGNAL(released()),this,SLOT(ctl_close_led_dev())); qt_led_open(LED_DEVICE); printf("C++ Open %s ",LED_DEVICE); //add by chen finished } //add by chen //控制灯亮函数 void MainWindow::ctl_led_on(){ qt_led_ioctl(LED_ON,FIRST_LED); printf("Qt ctl_led_on IO here! "); printf("Qt cmd is:%d,arg is :%d ",LED_ON,FIRST_LED); } void MainWindow::ctl_led_off(){ qt_led_ioctl(LED_OFF,FIRST_LED); printf("Qt ctl_led_off IO here! "); printf("Qt cmd is:%d,arg is :%d ",LED_OFF,FIRST_LED); } void MainWindow::ctl_led_all_on(){ qt_led_ioctl(LED_ON,ALL_LED); printf("Qt ctl_led_all_on IO here! "); printf("Qt cmd is:%d,arg is :%d ",LED_ON,ALL_LED); } void MainWindow::ctl_led_all_off(){ qt_led_ioctl(LED_OFF,ALL_LED); printf("Qt ctl_led_all_off IO here! "); printf("Qt cmd is:%d,arg is :%d ",LED_OFF,ALL_LED); } void MainWindow::ctl_close_led_dev(){ qt_led_close(); } //add by chen finished MainWindow::~MainWindow() { delete ui; /*关闭窗体后要记得关闭文件*/ qt_led_close();//add by chen } 7、窗体文件头文件mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include //add by chen #include #include #include #include #include #include #include //add by chen end namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: Ui::MainWindow *ui; //add by chen //私有成员,声明按钮 private: QPushButton *button_led_on; QPushButton *button_led_off; QPushButton *button_led_all_on; QPushButton *button_led_all_off; QPushButton *button_close_led_dev; //公有成员,声明事件实现槽函数 public slots: void ctl_led_on(); void ctl_led_off(); void ctl_led_all_on(); void ctl_led_all_off(); void ctl_close_led_dev(); //add by chen end }; #endif // MAINWINDOW_H 四、效果图
Qt界面效果图
Qt界面效果图 五、实际效果 先加载模块 insmod qt-led.ko
再运行Qt程序 qt-led -qws & 我的板子是有触摸,可以接鼠标。程序运行后两个灯都是出于灭的状态,点击led_on,第一个灯亮了,点击led_off第一个灯灭了。点击led_all_on,两个灯都亮了,点击led_all_off,两个灯都灭了。点击close_dev,然后再重复上面的操作,灯无变化,达到效果。 六、总结 买了书也两个礼拜了,本人从13年毕业到现在一直从事于硬件工作,从方案选型到原理图设计和PCB layout乃至量产等经验倒是一大堆。一直想学习linux部分内容,自己倒腾了这么久总算是完成第一个小目标。通过界面操作LED,Android部分还没去看。不得不说,linux学起来还有点吃力,理解和要记忆的一大堆,自我感觉还没入门,再接再厉。留下记录博客,以供自勉,如有同道之人愿意一起交流,无比乐意。