本例程基于Xilinx公司的zynq系列zedboard开发板。(其他芯片类似,只需简单修改)
使用Vivado、XSDK与Petalinux工具。
最终效果是:在串口终端运行自己写的app,实现开发板上的流水灯控制。
【一个简单的驱动开发例程——GPIO流水灯(vivado工程)】(本例程分为两部分)
https://blog.csdn.net/u013029731/article/details/86652050
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所在文件夹)
这一步会打开配置界面,通过方向键移动光标。(界面可能会略有不同):
选中第一行Linux Components Selection
和下方的select,回车。出现下图:
由于我的电脑(6核8G内存)无法成功生成fsbl文件,所以需要取消在petalinux中生成fsbl的选项。选中第一行First Stage Bootloader
和下方的select
,按键盘上的N,取消选中;本例程没有PMU Fireware
这一行,不用管。需要的fsbl文件可以通过XSDK生成,具体步骤见vivado工程部分的附A。
右方向键选择Save
,ok然后Exit
返回到初始界面。
向下选择U-boot Configuration
,选择第二项,修改u-boot模板,如下:
按回车键,修改为: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
,则会报错。发生错误后,必须删除整个程序文件夹,通常是
这个文件夹;然后打开
文件,删除IMAGE_INSTALL_append = " myapp_led"
这一行。再重新修改编译。
删除应用程序必须做两件事:1、删除程序文件夹,2、删除petalinux-image.bbappend文件对应行。
4、调整应用程序
上一步完成后,会出现
这个文件夹,文件夹下有Makefile、README、myapp.bb和myapp.c(或cpp)文件。我们可以直接修改myapp.c,也可以用已写好的.c文件替换,同时要把头文件放到.c文件同一个文件夹内。修改程序后,需要修改.bb文件,通常是添加对应的.c文件及头文件。
5、添加相应驱动(LKM)(根据需要选择添加),petalinux-create -t modules --name mymodule --enable
如果名字中含有_
,处理方法同3。建好的驱动文件夹是
。
6、调整驱动程序
文件夹下有Makefile、README、mymodule.bb和mymodule.c(或cpp)文件。修改如4。也需要修改.bb文件。
7、编译生成镜像, petalinux-build
8、将镜像拷贝到SD卡上,启动。打开串口工具。
9、加载驱动到内核并运行程序:
定位到/lib/modules/\
文件夹,输入命令: 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");
#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;
}