一个简单的驱动开发例程——GPIO流水灯(Petalinux部分)

前言

本例程基于Xilinx公司的zynq系列zedboard开发板。(其他芯片类似,只需简单修改)
使用Vivado、XSDK与Petalinux工具。
最终效果是:在串口终端运行自己写的app,实现开发板上的流水灯控制。

【一个简单的驱动开发例程——GPIO流水灯(vivado工程)】(本例程分为两部分)
https://blog.csdn.net/u013029731/article/details/86652050

使用Petalinux生成内核镜像

0、Petalinux的安装及启动见另一博客。https://blog.csdn.net/u013029731/article/details/84957686

1、创建petalinux工程,petalinux-create --type project --template zynqMP --name zedtest

2、导入硬件描述文件。首先将vivado工程生成的硬件描述文件(.hdf)放到petalinux工程文件夹下,在本例中即是/opt/zedtest文件夹,然后在文件夹内打开终端输入petalinux-config --get-hw-description=/opt/zedtest (即.hdf所在文件夹)
  这一步会打开配置界面,通过方向键移动光标。(界面可能会略有不同):
一个简单的驱动开发例程——GPIO流水灯(Petalinux部分)_第1张图片
  选中第一行Linux Components Selection和下方的select,回车。出现下图:
一个简单的驱动开发例程——GPIO流水灯(Petalinux部分)_第2张图片
  由于我的电脑(6核8G内存)无法成功生成fsbl文件,所以需要取消在petalinux中生成fsbl的选项。选中第一行First Stage Bootloader和下方的select,按键盘上的N,取消选中;本例程没有PMU Fireware这一行,不用管。需要的fsbl文件可以通过XSDK生成,具体步骤见vivado工程部分的附A。
  右方向键选择Save,ok然后Exit返回到初始界面。
  向下选择U-boot Configuration,选择第二项,修改u-boot模板,如下:
一个简单的驱动开发例程——GPIO流水灯(Petalinux部分)_第3张图片
  按回车键,修改为:zynq_zed_defconfig ,关于不同平台对应的u-boot模板,可以参考xilinx wiki: https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841973/Build+U-Boot。
  接下来还可以进入Yocto Settings选项来配置rootfs的源,参考另一博客:Yocto Settings设置。
  选择Save保存并返回初始界面。选择Exit退出配置。
  等待一段时间,配置文件生成。
注意: 如果要修改配置,可以输入命令petalinux-config,会重新出现配置界面,重新配置。

3、建立应用程序模板,petalinux-create -t apps --template c --name myapp --enable
  注意:如果程序名字中含有_,则必须是myapp_1这样的形式,即_后跟数字,代表版本号。如果是myapp_led,则会报错。发生错误后,必须删除整个程序文件夹,通常是/project-spec/meta-user/recipes-apps/myapp这个文件夹;然后打开/project-spec/meta-user/recipes-core/images下的petalinux-image.bbappend文件,删除IMAGE_INSTALL_append = " myapp_led"这一行。再重新修改编译。
  删除应用程序必须做两件事:1、删除程序文件夹,2、删除petalinux-image.bbappend文件对应行。
  
4、调整应用程序
  上一步完成后,会出现/project-spec/meta-user/recipes-apps/myapp这个文件夹,文件夹下有Makefile、README、myapp.bb和myapp.c(或cpp)文件。我们可以直接修改myapp.c,也可以用已写好的.c文件替换,同时要把头文件放到.c文件同一个文件夹内。修改程序后,需要修改.bb文件,通常是添加对应的.c文件及头文件。
  
5、添加相应驱动(LKM)(根据需要选择添加),petalinux-create -t modules --name mymodule --enable
  如果名字中含有_,处理方法同3。建好的驱动文件夹是/project-spec/meta-user/recipes-modules/mymodule
  
6、调整驱动程序
  文件夹下有Makefile、README、mymodule.bb和mymodule.c(或cpp)文件。修改如4。也需要修改.bb文件。
  
7、编译生成镜像, petalinux-build
8、将镜像拷贝到SD卡上,启动。打开串口工具。
9、加载驱动到内核并运行程序:
  定位到/lib/modules/\/extra文件夹,输入命令: modprobe *.ko(*代表对应名字),目的是挂载驱动。然后串口打印出一系列信息。
  在串口输入myapp(app的名字),即可运行应用程序,然后看到开发板上的4个LED等开始做加法。

三、驱动代码如下:

#include      //定义了module_init
#include    //最基本的头文件,其中定义了MODULE_LICENSE这一类宏
#include        //file_operations结构体定义在该头文件中
#include 	//class、device结构体的定义位置
#include 	//printk头文件

#include 	//copy_from_user头文件
#include 			//ioremap头文件

#include 
#include 
#include 

//定义主设备号 
static int major_num;
//  定义设备文件名
#define DEVICE_NAME "leds"

//定义class、device结构体
#define CLASS_NAME "mygpio"	
static struct class* gpio_class;    
static struct device* gpio_device; 

#define LEDS_BASE_ADDR		(0x41200000)	//GPIO的Base addr,用于映射
 
static unsigned *leds;

static int leds_drv_open(struct inode *Inode, struct file *File)
{
	*leds = 0x0;				
	return 0;
}
 
 
static ssize_t leds_drv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	return 0;
}
 
static ssize_t leds_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	unsigned int ret = 0;
	unsigned int tmp_val;
	
	ret = copy_from_user(&tmp_val, buf, count);
	
	*leds = tmp_val & 0xf;	
	
	return ret;
}
 
//  描述与设备文件触发的事件对应的回调函数指针
static struct file_operations dev_fops =
{ 
	.owner = THIS_MODULE, 
	.open = leds_drv_open,
	.read = leds_drv_read, 
	.write = leds_drv_write,
};
 
//  初始化Linux驱动
static int __init leds_drv_init(void)
{
	int ret; 
 
	leds = ioremap(LEDS_BASE_ADDR, 0x100);
 
	//获取主设备号
	major_num = register_chrdev(0,DEVICE_NAME, &dev_fops);
	
	//创建设备类
	gpio_class = class_create(THIS_MODULE, CLASS_NAME);    

	if(IS_ERR(gpio_class))
	{                
    	unregister_chrdev(major_num, DEVICE_NAME);
    	printk(KERN_ALERT "Failed to register device class\n");
    	return PTR_ERR(gpio_class);          
    }
    //注册设备
	gpio_device = device_create(gpio_class, NULL, MKDEV(major_num, 0), NULL, DEVICE_NAME);
	
	if(IS_ERR(gpio_device))
	{               
    	class_destroy(gpio_class);           
    	unregister_chrdev(major_num, DEVICE_NAME);
    	printk(KERN_ALERT "Failed to create the device\n");
    	return PTR_ERR(gpio_device);
    }
    printk(KERN_INFO "LED_GPIO_init: device created correctly\n");
 
	return 0;
}
 
// 卸载Linux驱动
static void __exit leds_drv_exit(void)
{
	iounmap(leds);
	
	//  删除设备文件,后创建的先卸载  
	device_destroy(gpio_class, MKDEV(major_num, 0));  
	class_destroy(gpio_class); 

	unregister_chrdev(major_num,DEVICE_NAME);	//释放设备号

	//  输出日志信息
	printk("LED_GPIO_exit success!\n");
} 
 
//  注册初始化Linux驱动的函数
module_init(leds_drv_init);
//  注册卸载Linux驱动的函数
module_exit(leds_drv_exit);

MODULE_LICENSE("GPL");

四、应用程序app

#include 
#include 
#include 
#include 
#include 
 
 
 
int main(int argc, char** argv)
{
        int fd;
        
        fd = open("/dev/leds", O_RDWR);
        
        if(fd < 0)
        {
                printf("fd = %d open fialed!\n", fd);
        }

        unsigned int leds = 0;

        while(1)
        {
        	write(fd, &leds, 4);
        
        	leds++;
        	leds %= 0xf;
                sleep(1);
        }

        close(fd);

        return 0;
}

你可能感兴趣的:(Linux驱动,Xilinx)