(本人业余时间接相关外包,欢迎联系!)
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灯的亮灭,如下图所示.
1. 学会编写一个简单的嵌入式Linux字符设备驱动
2. 学会编写一个简单的linux 文件io应用程序.
3. 学会使用qt creator建立嵌入式qt工程.
4. 学会qt5.6设置背景图片.
5. 学会qt5.6进行界面切换.
6. 学会如何使用qt程序操作jz2440的GPIO.
1.编写模块的入口函数与出口函数.(module_init,module_exit)
2.定义file_operations结构体,确定需要实现哪些函数.最基本的有open ,read ,write函数,其他根据需要添加
3.编写open,read,write等函数.
驱动的write函数需要方便单独控制每一个LED灯,也需要方便同时控制所有LED灯.
#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");
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
先来看看这个工程最终包含了那些文件
注:
文件->新建文件或项目 (命名ledcontrol.ui)
通过以上步骤,主界面设计完成,效果图如下.
在主界面点击"LED控制按钮",进入LED控制界面, 在LED控制节目点击"返回主界面"进行返回.
#include "ledcontrol.h" //需要包含该头文件,才能引用LedControl
/**
* 该函数通过"转到槽功能自动生成"
*/
void Widget::on_led_ctl_entrance_clicked()
{
//函数内容由自己手动编写
LedControl led_dialog;
led_dialog.exec();
}
#include "widget.h" //Widget在该头文件声明
/**
* 该函数通过"转到槽功能自动生成"
*/
void LedControl::on_Button_Return_clicked()
{
this->hide();//必须执行,否则不会显示主界面
Widget w;
w.show();
}
/* 控制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);
}