1 配置petaLinux环境变量
在Linuxproject目录下,打开终端,输入命令source /opt/pkg/petalinux/2018.3/settings.sh
2 新建petaLinux工程
petalinux-create -t project --template zynq -n ZYNQ7010_LED
3 配置petaLinux工程
输入cd ZYNQ7010_LED
,进入刚刚创建的工程文件;
输入petalinux-config --get-hw-description /mnt/hgfs/ShareFile/project_File/Class_text/
,【此命令指向一个硬件描述文件的目录,HDF 文件包含了 Zynq 硬件设计的配置信息(如处理器核、外设等),供 PetaLinux 使用】,稍等一会就能进入配置界面
3.1 配置为从QSPI Flash中启动
3.1.1 进入Subsystem AUTO Hardware Settings
3.1.2 进入Advanced bootable images storage Settings
3.1.3 进入boot image settings
修改为primary flash
3.1.4 进入kernel image settings
也修改为primary flash
3.2 保存并退出
等待petaLinux配置完成
4 修改自定义设备树文件
设备树文件在该工程路径下
/include/ "system-conf.dtsi"
#include <dt-bindings/gpio/gpio.h>
#define GPIO_ACTIVE_HIGH 0
#define GPIO_ACTIVE_LOW 1
/ {
model = "Navigator Development Board";
compatible = "zynq7010,zynq-7020","xlnx,zynq-7000";
leds {
compatible = "gpio-leds";
gpio-led1 {
label = "led1";
gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
gpio-led2{
label = "led2";
gpios = <&gpio0 54 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
gpio-led3{
label = "led3";
gpios = <&gpio0 55 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
gpio-led4{
label = "led4";
gpios = <&gpio0 56 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
gpio-led5{
label = "led5";
gpios = <&gpio0 57 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
};
keys {
compatible = "gpio-keys";
autorepeat;
gpio-key1 {
label = " key1";
gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
linux,code = <105>; // Right
debounce-interval = <20>; // 20ms
};
gpio-key2 {
label = " key2";
gpios = <&gpio0 58 GPIO_ACTIVE_LOW>;
linux,code = <106>; // Left
debounce-interval = <20>; // 20ms
};
gpio-key3 {
label = " key3";
gpios = <&gpio0 59 GPIO_ACTIVE_LOW>;
linux,code = <28>; // Enter
debounce-interval = <20>; // 20ms
};
};
};
&i2c0 {
status = "okay";
clock-frequency = <100000>;
myiic@50 {
compatible = "cdns,i2c-r1p10";
reg = <0x50>;
};
};
&PWM_0 {
compatible = "digilent,axi-pwm";
clock-names = "pwm";
npwm = <1>;
};
设备树中描述了5个LED接口,3个按键接口,1个IIC接口和1个PWM接口
5 配置内核
输入petalinux-config -c kernel
,回车等待…
不用修改,直接保存退出
等待配置完成
6 创建驱动模块
输入petalinux-create -t modules --name leddrive --enable
,创建一个名为leddrive
的驱动模块【驱动模块的名称不要大写,也不要出现下划线】
找到该驱动模块的路径,并对leddrive.c
文件进行修改
//#include <linux/typlinuxkes.h>
#include <linux/kernel.h>
#include <linux/delay.h>
//#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#define LED_MAJOR 200 /* 主设备号 */
#define LED_NAME "led" /* 设备名字 */
/* GPIO 相关寄存器地址定义 */
#define ZYNQ_GPIO_REG_BASE 0xE000A000
#define DATA_OFFSET 0x00000040
#define DIRM_OFFSET 0x00000204
#define OUTEN_OFFSET 0x00000208
#define INTDIS_OFFSET 0x00000214
#define APER_CLK_CTRL 0xF800012C
/* 映射后的寄存器虚拟地址指针 */
static void __iomem *data_addr;
static void __iomem *dirm_addr;
static void __iomem *outen_addr;
static void __iomem *intdis_addr;
static void __iomem *aper_clk_ctrl_addr;
static int led_open(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf,
size_t cnt, loff_t *offt)
{
int ret;
int val;
char kern_buf[1];
ret = copy_from_user(kern_buf, buf, cnt); // 得到应用层传递过来的数据
if (0 > ret)
{
printk(KERN_ERR "kernel write failed!\r\n");
return -EFAULT;
}
val = readl(data_addr);
if (0 == kern_buf[0])
val &= ~(0x1U << 9); // 如果传递过来的数据是 0 则关闭 led
else if (1 == kern_buf[0])
val |= (0x1U << 9); // 如果传递过来的数据是 1 则点亮 led
writel(val, data_addr);
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 int __init led_init(void)
{
u32 val;
int ret;
/* 1.寄存器地址映射 */
data_addr = ioremap(ZYNQ_GPIO_REG_BASE + DATA_OFFSET, 4);
dirm_addr = ioremap(ZYNQ_GPIO_REG_BASE + DIRM_OFFSET, 4);
outen_addr = ioremap(ZYNQ_GPIO_REG_BASE + OUTEN_OFFSET, 4);
intdis_addr = ioremap(ZYNQ_GPIO_REG_BASE + INTDIS_OFFSET, 4);
aper_clk_ctrl_addr = ioremap(APER_CLK_CTRL, 4);
/* 2.使能 GPIO 时钟 */
val = readl(aper_clk_ctrl_addr);
val |= (0x1U << 22);
writel(val, aper_clk_ctrl_addr);
/* 3.关闭中断功能 */
val |= (0x1U << 9);
writel(val, intdis_addr);
/* 4.设置 GPIO 为输出功能 */
val = readl(dirm_addr);
val |= (0x1U << 9);
writel(val, dirm_addr);
/* 5.使能 GPIO 输出功能 */
val = readl(outen_addr);
val |= (0x1U << 9);
writel(val, outen_addr);
/* 6.默认关闭 LED */
val = readl(data_addr);
val &= ~(0x1U << 9);
writel(val, data_addr);
/* 7.注册字符设备驱动 */
ret = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
if (0 > ret)
{
printk(KERN_ERR "Register LED driver failed!\r\n");
return ret;
}
return 0;
}
static void __exit led_exit(void)
{
/* 1.卸载设备 */
unregister_chrdev(LED_MAJOR, LED_NAME);
/* 2.取消内存映射 */
iounmap(data_addr);
iounmap(dirm_addr);
iounmap(outen_addr);
iounmap(intdis_addr);
iounmap(aper_clk_ctrl_addr);
}
/* 驱动模块入口和出口函数注册 */
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("zxr");
MODULE_DESCRIPTION("ZYNQ GPIO LED Driver");
MODULE_LICENSE("GPL");
7 配置Linux根文件系统
输入petalinux-config -c rootfs
,等待…
检查刚刚创建的leddrive
模块是否使能
leddrive
有星号就说明已经使能,直接保存退出即可
8 编译
输入petalinux-build -c leddrive
,先编译刚刚创建的leddrive
模块,等待…
输入petalinux-build
,编译整个工程,等待…
9 打包制作boot.bin启动文件
输入petalinux-package --boot --fsbl --fpga --u-boot --force
,等待…
然后将镜像文件image复制到共享文件夹下,以便在Windows端进行镜像烧录
10 SDK烧录flash
打开烧录界面
在image文件夹中找到对应的文件
然后将开发板boot拨码开关置于JATG模式进行烧录程序
然后,再将image里面的image.ub
修改为image.ub.bin
文件,烧录进开发板,offset修改为0x520000
11 SDK开发应用程序
11.1 创建应用工程
就基于模板创建
对模板工程重新命名
11.2 添加应用程序代码
打开模板工程,自行修改为
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd, ret;
unsigned char buf[1];
if (3 != argc)
{
printf("Usage:\n"
"\t./ledApp /dev/led 1 @ close LED\n"
"\t./ledApp /dev/led 0 @ open LED\n");
return -1;
}
/* 打开设备 */
fd = open(argv[1], O_RDWR);
if (0 > fd)
{
printf("file %s open failed!\r\n", argv[1]);
return -1;
}
/* 将字符串转换为 int 型数据 */
buf[0] = atoi(argv[2]);
/* 向驱动写入数据 */
ret = write(fd, buf, sizeof(buf));
if (0 > ret)
{
printf("LED Control Failed!\r\n");
close(fd);
return -1;
}
/* 关闭设备 */
close(fd);
return 0;
}
保存应用程序代码,sdk会自动编译,且生成一个elf的可执行文件
该执行文件处于SDK的工作空间路径下
12 功能验证
烧录成功后,将开发板的boot拨码开关至于QSPI模式进行启动,打开串口,点击POR
复位,串口开始打印信息
其中有个联网的等待过程
直接ctrl+c
打断,进入下一步,等待串口继续打印信息
直到显示了<工程名称> login:
,才说明工程烧录成功
12.1 Linux登录
在Class_ZYNQ7010 login:
后面输入root
,登录,然后提示输入密码,继续输入root
,即可登录成功
12.2 网口连接
输入ping 192.168.50.7
,连接Windows电脑网口
12.3 设备树硬件测试
12.3.1 LED测试
输入cd /sys/class/leds/
,进入这个路径,再输入ls
展开该路径下的内容
这五个led,刚好对应着自定义设备树system-conf.dtsi
中的信息,输入cd led1
,进入led1,再输入ls
,展开led1中的配置信息
其中brightness
用于控制led的亮灭程度,默认情况下,echo 1 > brightness
点亮LED,echo 0 > brightness
熄灭LED
12.3.2 按键测试
输入cd /dev/input/
,再输入ls
,展开/dev/input/ 下的内容
输入cat event0 | hexdump
,即可进入按键的检测
检测完后,通过ctrl+c
退出检测
12.4 驱动程序和应用程序测试
12.4.1 检查驱动程序和设备节点
输入cd /lib/modules/4.14.0-xilinx-v2018.3/extra/
,再输入ls
,检查该路径下是否存在对应的驱动文件(以.ko为后缀)
输入cd /dev/
,ls
展开,检查是否存在驱动节点
发现不存在对应的led节点,继续执行以下命令:
cd
回到主目录
depmod
生成模块的依赖关系
modprobe leddrive.ko
加载驱动模块并自动处理模块依赖
mknod /dev/led c 200 0
创建设备节点
再输入cd /dev/
,ls
展开,检查是否存在驱动节点
此时,dev目录下便有了一个led设备节点
12.4.2 挂载NFS服务
回到主目录,输入mkdir /mnt/nfs
,在开发板的Linux系统中新建一个nfs文件夹,用以存放Windows端nfs挂载的共享文件
然后输入mount -t nfs -o nolock 192.168.50.7:/nfs /mnt/nfs/
进行挂载
输入cd /mnt/nfs
,进入开发板Linux系统刚刚创建的nfs路径中,ls
展开,便能看到Windows端nfs服务所共享的文件
12.4.3 复制应用程序文件
输入cp LED_APP.elf /home/root/
,将应用程序的可执行文件复制到开发板的root目录下,输入cd /home/root/
进入该目录,再输入ls
展开,检查文件是否复制成功
12.4.4 赋予权限
输入chmod a+x LED_APP.elf
,给文件添加可执行权限
12.4.5 运行应用程序
输入./LED_APP.elf /dev/led 1
,开发板LED亮
;输入./LED_APP.elf /dev/led 0
,开发板LED灭
到此,成功通过petaLinux编译驱动程序和SDK编译应用程序并在ZYNQ7010开发板上实现LED的控制