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

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!\r\n");
    //先释放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!\r\n");
        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!\r\n");
        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!\r\n");
    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!\r\n");

    //由于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\r\n",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\r\n",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\r\n",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!\r\n");
    //平台设备,要注册主设备号
    LED_Major = register_chrdev(0,DEVICE_NAME,&qt_led_fs);
    if(LED_Major < 0){
        printk(KERN_EMERG"Led_Drv_Module_Init_Major_error!\r\n");
        return LED_Major;
    }
    printk(KERN_EMERG"Led_Drv_Module_Init_Major_Success!\r\n");
    //创建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.!\r\n");
        return -1;
    }
    //创建device
    device_create(qt_led_class,NULL,MKDEV(LED_Major,0),NULL,DEVICE_NAME);
    printk(DEVICE_NAME"initialized.!\r\n");
    return 0;
}

static void qt_led_exit(void){
    //卸载设备,与注册要相反的操作
    printk(KERN_EMERG"Led_Drv_Module_exit!\r\n");
    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!\r\n",devname);
        return -1;
    }
    printf("Qt-led driver is ok,open success!\r\n");
    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!\r\n");
    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 \r\n",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!\r\n");
    printf("Qt cmd is:%d,arg is :%d\r\n",LED_ON,FIRST_LED);
}

void MainWindow::ctl_led_off(){
    qt_led_ioctl(LED_OFF,FIRST_LED);
    printf("Qt ctl_led_off IO here!\r\n");
    printf("Qt cmd is:%d,arg is :%d\r\n",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!\r\n");
    printf("Qt cmd is:%d,arg is :%d\r\n",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!\r\n");
    printf("Qt cmd is:%d,arg is :%d\r\n",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界面效果图
Linux学习笔记之用QT界面操作板子LED_第1张图片

五、实际效果

先加载模块 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学起来还有点吃力,理解和要记忆的一大堆,自我感觉还没入门,再接再厉。留下记录博客,以供自勉,如有同道之人愿意一起交流,无比乐意。

你可能感兴趣的:(Linux学习之旅)