代码链接:https://download.csdn.net/download/qq_55813245/85594338
资源 :imx6ull开发板
系统:正点原子出厂系统(Uboot 版本为 2016.03,内核版本为 4.1.15 ,根文件系统 rootfs为yocto。)
交叉编译:
应用程序:arm-linux-gnueabihf-gcc编译
驱动程序:对应的Makefile
QT应用程序:出厂系统的QT源码使用的arm-poky-linux-gnueabihf-gcc编译得到的,QT应用程序要用一样的编译器,在qt中设置对应的编译套件。
基于新字符设备驱动,可以自动创建设备节点
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LEDOFF 0
#define LEDON 1
#define LED_CNT 1 //设备号个数
#define LED_NAME "led" //名字
//寄存器物理地址
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)
//映射后的寄存器虚拟地址指针
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
// led设备结构体
struct led_dev{
dev_t devid; //设备号
int major; //主设备号
int minor; //次设备号
struct cdev cdev_led; //字符设备
struct device *device; //设备
struct class *class; //类
};
struct led_dev led; //led设备结构体
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &led; //设置私有数据
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt)
{
return 0;
}
void delay_short(volatile unsigned int n)
{
while(n--)
{
while(0x7ff){} //在 396Mhz 的主频下延时时间大约为 1ms
}
}
static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{ u32 val_set=0;
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
struct led_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt); //拷贝来自用户层的数据到databuf
if(retvalue < 0)
{
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0];
if(ledstat == LEDON)
{
val_set = readl(GPIO1_DR);
val_set &= ~(1 << 3);
writel(val_set, GPIO1_DR); //打开
}
else if(ledstat == LEDOFF)
{
val_set = readl(GPIO1_DR);
val_set |= (1 << 3);
writel(val_set, GPIO1_DR); //关闭
}
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
//设备操作函数
static struct file_operations led_fops =
{
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
//设备的注册
static void __exit led_init(void)
{ //初始化led
u32 val=0;
//1.获取映射后的寄存器地址
IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);
//2.使能时钟
val=readl(IMX6U_CCM_CCGR1);
val &=~(3<<26); //清除以前的值
val |= (3<<26);
writel(val,IMX6U_CCM_CCGR1);
//3. 设置复用功能和IO电气属性
writel(5, SW_MUX_GPIO1_IO03);
writel(0x10B0, SW_PAD_GPIO1_IO03);
//4.设置 GPIO1_IO03 为输出功能
val = readl(GPIO1_GDIR);
val &=~(1<<3); //清除以前的值
val |= (1<<3);
writel(val, GPIO1_GDIR);
//5.默认关闭led
val = readl(GPIO1_DR);
val |= (1 << 3);
writel(val, GPIO1_DR);
//led字符设备的注册
//1.创建设备号
led.major=0; //设置为默认没有定义设备号,后面自动申请设备号
alloc_chrdev_region(&led.devid,0,LED_CNT,LED_NAME); //申请设备号
led.major = MAJOR(led.devid); // 获取分配号的主设备号
led.minor = MINOR(led.devid); //获取分配号的次设备号
printk("led major=%d,minor=%d\r\n",led.major,led.minor);
//2初始化 cdev_led
led.cdev_led.owner=THIS_MODULE;
cdev_init(&led.cdev_led,&led_fops);
//3.添加cedv
cdev_add(&led.cdev_led,led.devid,LED_CNT);
//4.自动创建设备节点-创建类
led.class=class_create(THIS_MODULE,LED_NAME);
if(IS_ERR(led.class))
{
return ;
}
else
{
}
//创建设备
led.device=device_create(led.class,NULL,led.devid,NULL,LED_NAME);
if (IS_ERR(led.device))
{
return ;
}
else
{
printk("led驱动加载成功!\r\n");
}
return ;
}
//设备的注销
static void __exit led_exit(void)
{
//取消映射
iounmap(IMX6U_CCM_CCGR1);
iounmap(SW_MUX_GPIO1_IO03);
iounmap(SW_PAD_GPIO1_IO03);
iounmap(GPIO1_DR);
iounmap(GPIO1_GDIR);
cdev_del(&led.cdev_led); //删除
unregister_chrdev_region(led.devid,LED_CNT); //注销
device_destroy(led.class,led.devid);
class_destroy(led.class);
}
// 驱动模块加载
module_init(led_init);
// 驱动模块卸载
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LED");
KERNELDIR :=/home/zhw/linux/orign_code/linux-imx-4.1.15-2.1.0
CURRENT_PATH := $(shell pwd)
obj-m := led_mmu.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
利用tftp下载到开发板上,开发板,虚拟机,window三者在同一网段内
注意:
包含的头文件路径的内核源码目录版本一定与开发板使用的内核版本一样,且一定是用arm-linux-gnueabihf-交叉编译得到的内核源码。
if(argc != 3){
printf("Error Usage!\r\n");
return -1;
}
else{}
filename = argv[1];
//获取led设备对应的文件描述符
fd = open(filename, O_RDWR);
if(fd < 0){
printf("file %s open failed!\r\n", argv[1]);
return -1;
}
else{}
//获取命令行的打开或者关闭
databuf[0] = atoi(argv[2]);
//向/dev/led文件写入数据
retvalue = write(fd, databuf, sizeof(databuf));
if(retvalue < 0){
printf("LED Control Failed!\r\n");
close(fd);
return -1;
}
else{}
//关闭文件
retvalue = close(fd);
if(retvalue < 0){
printf("file %s close failed!\r\n", argv[1]);
return -1;
}
else{}
return 0;
}
利用arm-linux-gnueabihf-gcc ledApp.c -o ledApp 交叉编译器编译为可执行程序
实验:正点原子出厂系统红灯是Linux内核自带的 LED,挂在设备树上,默认打开的,首先要先将其关闭。
Beep 基于设备树的pinctrl和gpio子系统
使用的出厂系统自带的设备树,所以pinctrl节点和gpio就不用自己添加了。
gpios属性是设置对应设备所使用的GPIO,如led就是gpio1的IO03,低电平有效。
蜂鸣器驱动程序是直接使用的正点原子的教程源码,基于设备树驱动的不同点在于,我们不用关心相应的寄存器使能驱动具体是怎么去实现的,这是非常底层的驱动,NXP已经帮我们写好了。只需要在设备树中配置好相应节点(pinctrl节点和gpio)就行了。后面还有更为分离的platform 设备驱动。
/* 设置BEEP所使用的GPIO */
/* 1、获取设备节点:beep */
beep.nd = of_find_node_by_path("/leds/beep");
if(beep.nd == NULL) {
printk("beep node not find!\r\n");
return -EINVAL;
} else {
printk("beep node find!\r\n");
}
/* 2、 获取设备树中的gpio属性,得到BEEP所使用的BEEP编号 */
beep.beep_gpio = of_get_named_gpio(beep.nd, "gpios", 0);
if(beep.beep_gpio < 0) {
printk("can't get beep-gpio");
return -EINVAL;
}
printk("beeps-gpio num = %d\r\n", beep.beep_gpio);
/* 3、设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP */
ret = gpio_direction_output(beep.beep_gpio, 1);
printk("close beep\r\n");
if(ret < 0) {
printk("can't set gpio!\r\n");
}
与led驱动不同之处在于,在注册字符设备前,这里是先获取beep在设备树中的设备节点,和beep使用的GPIO编号,最后再调用相应的gpio驱动函数使能相应寄存器。调用beep的应用程序说明加载的beep驱动是没有错误的。
rcync远程部署调试,QT源码是来自于b站黑马QT视频的案例,与原本不同的是,这次的平台是基于ARM架构的linux系统,添加了led和beep控制程序。在开始游戏时,红灯亮;每一关卡胜利超时则蜂鸣器使能;回到其余界面蜂鸣器关闭。
ledcontrol.h
#ifndef LEDCONTROL_H
#define LEDCONTROL_H
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include
class LedControl
{
public:
LedControl();
~LedControl();
int fd;
int ret;
int databuf[1];
void open_file();
void led_open();
void led_off();
};
#endif // LEDCONTROL_H
ledcontrol.cpp
LedControl::LedControl()
{
this->led_off();
}
void LedControl::led_open()
{
this->open_file();
this->databuf[0]=1;
this->ret=write(fd,databuf,sizeof(databuf));
if(this->ret<0)
{
printf("LED Control Failed!\r\n");
close(fd);
return ;
}
close(fd);
}
void LedControl::led_off()
{
this->open_file();
this->databuf[0]=0;
this->ret=write(fd,databuf,sizeof(databuf));
if(this->ret<0)
{
printf("LED Control Failed!\r\n");
close(fd);
return ;
}
close(fd);
}
void LedControl::open_file()
{
this->fd=open("/dev/led", O_RDWR);
if(fd<0){
printf("can’t open!\n");
return ;
}
}
LedControl::~LedControl()
{
this->led_off();
}
beep控制程序与led一样,打开文件路径不同。
connect(startbtn,&MypushButton::clicked,this,[=](){
//弹跳特效
startbtn->zoom1(200,20);
startbtn->zoom2(200,20);
//延时进入开始场景
QTimer::singleShot(500,this,[=](){
led->led_open();
this->hide();
choose_scene->show();
});
});
//监听选择关卡的返回
connect(choose_scene,&ChooseLevelScene::chooseback,this,[=](){
QTimer::singleShot(150,this,[=](){
this->show();
led->led_off();
choose_scene->hide();
});
});
Mainscene::~Mainscene()
{
delete this->led;
led=NULL;
delete ui;
}
在主界面点开始按钮后,红灯亮,进入选择关卡界面,返回到开始界面,红灯熄灭,且因为beep和led没有父对象,结束时要手动释放。
//监听每个按钮的点击事件
connect(menubtn,&MypushButton::clicked,this,[=]()
{
//进入游戏场景
PlayScene * play=new PlayScene(i+1);
//play->setParent(this);不要依赖这个场景,场景消失,游戏界面也会
this->hide();
play->show();
this->beep_flag=true;
//蜂鸣器定时
QTimer::singleShot(10000+(i+1)*(i+1)*5000,this,[=]()
{
if(this->beep_flag){
beep->beep_open();
}
});
//胜利时,蜂鸣器定时无效
connect(play,&PlayScene::winsignal,this,[=]()
{
this->beep_flag=false;
} );
//监听游戏关卡的返回
connect(play,&PlayScene::returnsignal,this,[=]()
{
QTimer::singleShot(150,this,[=]()
{
this->show();
this->beep_flag=false;
delete play;
beep->beep_off();
});
});
});
用一个状态表示选择页面的打开和关闭,打开时,蜂鸣器定时无效;在游戏程序中添加一个信号函数,在一定内时间胜利,告诉蜂鸣器定时无效;进入游戏界面,蜂鸣器开始定时,返回到选择界面,蜂鸣器关闭。
利用远程调试可以正常运行,通过tftp下载到开发板上运行就会发生段错误,现在也还没找到问题;可以通过QFile直接对文件进行读写,后期可以lcd、触摸屏等驱动加载到自己移植的系统上,再进行可视化编程,可以在windows上编写一个服务端,接受来自开发板的信息等。