文章目录
- 前言
- 一、点亮多个led灯的基础实验以及其中的问题
- 1.1 基础流程
- 1.1.1 alinx教程的问题
- 1.1.1.1 驱动程序中的亮/灭逻辑修改!
- 1.1.1.1.1 逻辑错误的修改
- 1.1.1.1.2 多灯亮/灭
- 1.1.1.2 驱动程序中引脚的问题以及与裸机开发的区别(重要)
- 1.1.1.3 “AMBA外设时钟使能寄存器”是怎么回事儿?
- 1.1.2 全部流程
- 1.1.2.1 编辑和编译驱动
- 1.1.2.2 编辑和编译测试程序
- 1.1.2.3 测试结果
- 1.2 设备树和驱动程序的优先级与延申思考
- 1.2.1 insmod驱动以后cat /proc/devices显示什么(回答优先级的问题)?
- 1.2.2 延申思考——4个led的控制驱动就只有1个。
- 二、后续魔改
- 总结
前言
本文补全嵌入式系统内核镜像相关(二)中提到的驱动开发和测试流程,并且也进一步探索存在的问题,并进行了额外的思考和推敲。
一、点亮多个led灯的基础实验以及其中的问题
1.1 基础流程
依旧沿用嵌入式系统内核镜像相关(二)的PS
最小系统,至于设备树,就如下:
/include/ "system-conf.dtsi"/ { model ="ZYNQ7035"; compatible = "xlnx,zynq-7000"; usb_phy0: phy0@e0002000 {compatible = "ulpi-phy";#phy-cells = <0>;reg = <0xe0002000 0x1000>;view-port = <0x0170>;drv-vbus;};
};&usb0 {status = "okay";dr_mode = "host";usb-phy = <&usb_phy0>;
};&uart0 {
u-boot,dm-pre-reloc;
};&uart1 {
u-boot,dm-pre-reloc;
};
通过上述设备树和petalinux-config
的配置得到启动镜像后,略去不表,咱接着研究驱动。
开发板还是使用璞致的7035!
1.1.1 alinx教程的问题
本来大体上参考alinx的字符设备教程,但琢磨发现一些问题和疑惑了。
alinx提供的驱动程序如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/ide.h>
#include <linux/types.h>/* 驱动名称 */
#define DEVICE_NAME "gpio_leds"
/* 驱动主设备号 */
#define GPIO_LED_MAJOR 200/* gpio 寄存器虚拟地址 */
static unsigned int gpio_add_minor;
/* gpio 寄存器物理基地址 */
#define GPIO_BASE 0xE000A000
/* gpio 寄存器所占空间大小 */
#define GPIO_SIZE 0x1000
/* gpio 方向寄存器 */
#define GPIO_DIRM_0 (unsigned int *)(0xE000A204 - GPIO_BASE + gpio_add_minor)
/* gpio 使能寄存器 */
#define GPIO_OEN_0 (unsigned int *)(0xE000A208 - GPIO_BASE + gpio_add_minor)
/* gpio 控制寄存器 */
#define GPIO_DATA_0 (unsigned int *)(0xE000A040 - GPIO_BASE + gpio_add_minor)/* 时钟使能寄存器虚拟地址 */
static unsigned int clk_add_minor;
/* 时钟使能寄存器物理基地址 */
#define CLK_BASE 0xF8000000
/* 时钟使能寄存器所占空间大小 */
#define CLK_SIZE 0x1000
/* AMBA 外设时钟使能寄存器 */
#define APER_CLK_CTRL (unsigned int *)(0xF800012C - CLK_BASE + clk_add_minor)/* open 函数实现, 对应到 Linux 系统调用函数的 open 函数 */
static int gpio_leds_open(struct inode *inode_p, struct file *file_p)
{
/* 把需要修改的物理地址映射到虚拟地址 */
gpio_add_minor = (unsigned int)ioremap(GPIO_BASE, GPIO_SIZE);
clk_add_minor = (unsigned int)ioremap(CLK_BASE, CLK_SIZE);/* MIO_0 时钟使能 */
*APER_CLK_CTRL |= 0x00400000;
/* MIO_0 设置成输出 */
*GPIO_DIRM_0 |= 0x00000001;
/* MIO_0 使能 */
*GPIO_OEN_0 |= 0x00000001;printk("gpio_test module open\n");return 0;
}/* write 函数实现, 对应到 Linux 系统调用函数的 write 函数 */
static ssize_t gpio_leds_write(struct file *file_p, const char __user *buf, size_t len, loff_t *loff_t_p)
{
int rst;
char writeBuf[5] = {0};printk("gpio_test module write\n");rst = copy_from_user(writeBuf, buf, len);
if(0 != rst)
{
return -1;
}if(1 != len)
{
printk("gpio_test len err\n");
return -2;
}
if(1 == writeBuf[0])
{
*GPIO_DATA_0 &= 0xFFFFFFFE;
printk("gpio_test ON\n");
}
else if(0 == writeBuf[0])
{
*GPIO_DATA_0 |= 0x00000001;
printk("gpio_test OFF\n");
}
else
{
printk("gpio_test para err\n");
return -3;
}return 0;
}/* release 函数实现, 对应到 Linux 系统调用函数的 close 函数 */
static int gpio_leds_release(struct inode *inode_p, struct file *file_p)
{
printk("gpio_test module release\n");
return 0;
}/* file_operations 结构体声明, 是上面 open、write 实现函数与系统调用函数对应的关键 */
static struct file_operations gpio_leds_fops = {
.owner = THIS_MODULE,
.open = gpio_leds_open,
.write = gpio_leds_write,
.release = gpio_leds_release,
};/* 模块加载时会调用的函数 */
static int __init gpio_led_init(void)
{
int ret;/* 通过模块主设备号、名称、模块带有的功能函数(及 file_operations 结构体)来注册模块 */
ret = register_chrdev(GPIO_LED_MAJOR, DEVICE_NAME, &gpio_leds_fops);
if (ret < 0)
{
printk("gpio_led_dev_init_ng\n");
return ret;
}
else
{
/* 注册成功 */
printk("gpio_led_dev_init_ok\n");
}
return 0;
}/* 卸载模块 */
static void __exit gpio_led_exit(void)
{
/* 释放对虚拟地址的占用 */
iounmap((unsigned int *)gpio_add_minor);
iounmap((unsigned int *)clk_add_minor);
/* 注销模块, 释放模块对这个设备号和名称的占用 */
unregister_chrdev(GPIO_LED_MAJOR, DEVICE_NAME);printk("gpio_led_dev_exit_ok\n");
}/* 标记加载、卸载函数 */
module_init(gpio_led_init);
module_exit(gpio_led_exit);/* 驱动描述信息 */
MODULE_AUTHOR("Alinx");
MODULE_ALIAS("gpio_led");
MODULE_DESCRIPTION("GPIO LED driver");
MODULE_VERSION("v1.0");
MODULE_LICENSE("GPL");
1.1.1.1 驱动程序中的亮/灭逻辑修改!
主要指的是控制灯亮灭的逻辑错误。
1.1.1.1.1 逻辑错误的修改
观察其中的:
if(1 == writeBuf[0])
{
*GPIO_DATA_0 &= 0xFFFFFFFE;
printk("gpio_test ON\n");
}
else if(0 == writeBuf[0])
{
*GPIO_DATA_0 |= 0x00000001;
printk("gpio_test OFF\n");
}
根据printk
的提示,*GPIO_DATA_0 &= 0xFFFFFFFE;
应该预期亮灯,但实际上观察GPIO_DATA_0
指针指向的结果末位是0
。根据《ug585-Zynq-7000-TRM.pdf》,结果应该是1
。对于灭灯的控制同理,因此需要修改为如下:
if(1 == writeBuf[0])
{
*GPIO_DATA_0 |= 0x00000001;
printk("gpio_test ON\n");
}
else if(0 == writeBuf[0])
{
*GPIO_DATA_0 &= 0xFFFFFFFE;
printk("gpio_test OFF\n");
}
实验结果和预期一致。
1.1.1.1.2 多灯亮/灭
多灯的亮/灭控制其实和当初的裸机开发一致了。由于当初裸机开发使用了基于EMIO
引脚的GPIO-led
,所以咱肯定不能接着沿用alinx
提供的驱动程序。
我先说说为什么我们不能接着用基于MIO
引脚的GPIO-led
,因为道理很简单,早在制作PS
最小子系统的时候,led
灯全被绑定到EMIO
引脚的GPIO
上,所以即使照着alinx
的作业抄,也不会有啥效果(已经验证过了,实验结果也确实如此)!
那么,怎么改?很简单,先回到璞致提供的用户手册:
明明白白说了EMIO
属于bank2
,然后回到《ug585-Zynq-7000-TRM.pdf》。
所以使用0x00000284
的GPIO
方向寄存器和0x00000288
的使能寄存器,以及0x00000048
的控制寄存器。关于控制寄存器/方向寄存器/使能寄存器,每一个bit都对应相应的EMIO
,因此,为实现多灯亮灭控制,很显然需要从低到高依次写入数值。我们就以4个灯为例!
在方向/使能寄存器上:
/* MIO_0 时钟使能 */
*APER_CLK_CTRL |= 0x00400000;
/* MIO_0 设置成输出 */
*GPIO_DIRM_0 |= 0x0000000F;
/* MIO_0 使能 */
*GPIO_OEN_0 |= 0x0000000F;
在亮灭实现(控制寄存器)上:
if(1 == writeBuf[0])
{
*GPIO_DATA_0 |= 0x0000000F;
printk("gpio_test ON\n");
}
else if(0 == writeBuf[0])
{
*GPIO_DATA_0 &= 0xFFFFFFF0;
printk("gpio_test OFF\n");
}
因此,可以控制4个基于EMIO
的GPIO-led
亮灭的驱动代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/ide.h>
#include <linux/types.h>/* 驱动名称 */
#define DEVICE_NAME "gpio_leds_our"
/* 驱动主设备号 */
#define GPIO_LED_MAJOR 200/* gpio 寄存器虚拟地址 */
static unsigned int gpio_add_minor;
/* gpio 寄存器物理基地址 */
#define GPIO_BASE 0xE000A000
/* gpio 寄存器所占空间大小 */
#define GPIO_SIZE 0x1000
/* gpio 方向寄存器 */
#define GPIO_DIRM_0 (unsigned int *)(0xE000A284 - GPIO_BASE + gpio_add_minor)
/* gpio 使能寄存器 */
#define GPIO_OEN_0 (unsigned int *)(0xE000A288 - GPIO_BASE + gpio_add_minor)
/* gpio 控制寄存器 */
#define GPIO_DATA_0 (unsigned int *)(0xE000A048 - GPIO_BASE + gpio_add_minor)/* 时钟使能寄存器虚拟地址 */
static unsigned int clk_add_minor;
/* 时钟使能寄存器物理基地址 */
#define CLK_BASE 0xF8000000
/* 时钟使能寄存器所占空间大小 */
#define CLK_SIZE 0x1000
/* AMBA 外设时钟使能寄存器 */
#define APER_CLK_CTRL (unsigned int *)(0xF800012C - CLK_BASE + clk_add_minor)/* open 函数实现, 对应到 Linux 系统调用函数的 open 函数 */
static int gpio_leds_open(struct inode *inode_p, struct file *file_p)
{
/* 把需要修改的物理地址映射到虚拟地址 */
gpio_add_minor = (unsigned int)ioremap(GPIO_BASE, GPIO_SIZE);
clk_add_minor = (unsigned int)ioremap(CLK_BASE, CLK_SIZE);/* MIO_0 时钟使能 */
*APER_CLK_CTRL |= 0x00400000;
/* MIO_0 设置成输出 */
*GPIO_DIRM_0 |= 0x0000000F;
/* MIO_0 使能 */
*GPIO_OEN_0 |= 0x0000000F;printk("gpio_test module open\n");return 0;
}/* write 函数实现, 对应到 Linux 系统调用函数的 write 函数 */
static ssize_t gpio_leds_write(struct file *file_p, const char __user *buf, size_t len, loff_t *loff_t_p)
{
int rst;
char writeBuf[5] = {0};printk("gpio_test module write\n");rst = copy_from_user(writeBuf, buf, len);
if(0 != rst)
{
return -1;
}if(1 != len)
{
printk("gpio_test len err\n");
return -2;
}
if(1 == writeBuf[0])
{
*GPIO_DATA_0 |= 0x0000000F;
printk("gpio_test ON\n");
}
else if(0 == writeBuf[0])
{
*GPIO_DATA_0 &= 0xFFFFFFF0;
printk("gpio_test OFF\n");
}
else
{
printk("gpio_test para err\n");
return -3;
}return 0;
}/* release 函数实现, 对应到 Linux 系统调用函数的 close 函数 */
static int gpio_leds_release(struct inode *inode_p, struct file *file_p)
{
printk("gpio_test module release\n");
return 0;
}/* file_operations 结构体声明, 是上面 open、write 实现函数与系统调用函数对应的关键 */
static struct file_operations gpio_leds_fops = {
.owner = THIS_MODULE,
.open = gpio_leds_open,
.write = gpio_leds_write,
.release = gpio_leds_release,
};/* 模块加载时会调用的函数 */
static int __init gpio_led_init(void)
{
int ret;/* 通过模块主设备号、名称、模块带有的功能函数(及 file_operations 结构体)来注册模块 */
ret = register_chrdev(GPIO_LED_MAJOR, DEVICE_NAME, &gpio_leds_fops);
if (ret < 0)
{
printk("gpio_led_dev_init_ng\n");
return ret;
}
else
{
/* 注册成功 */
printk("gpio_led_dev_init_ok\n");
}
return 0;
}/* 卸载模块 */
static void __exit gpio_led_exit(void)
{
/* 释放对虚拟地址的占用 */
iounmap((unsigned int *)gpio_add_minor);
iounmap((unsigned int *)clk_add_minor);
/* 注销模块, 释放模块对这个设备号和名称的占用 */
unregister_chrdev(GPIO_LED_MAJOR, DEVICE_NAME);printk("gpio_led_dev_exit_ok\n");
}/* 标记加载、卸载函数 */
module_init(gpio_led_init);
module_exit(gpio_led_exit);/* 驱动描述信息 */
MODULE_AUTHOR("Alinx");
MODULE_ALIAS("gpio_led");
MODULE_DESCRIPTION("GPIO LED driver");
MODULE_VERSION("v1.0");
MODULE_LICENSE("GPL");
1.1.1.2 驱动程序中引脚的问题以及与裸机开发的区别(重要)
关于引脚问题,见1.1.1.1。注意,alinx
采用的是基于MIO
的GPIO
引脚,因此没法儿直接用。我还是参照1.1.1.1中的流程修改!
不难看出来,裸机开发的流程中,得益于xparameters.h
的存在,避免了自己去找文档,只需要关注EMIO
的起始序号和范围即可。然而,驱动开发需要自己确认某些寄存器的引脚以实现正常功能。
所以,相比之下,从这一方面来看,驱动开发的流程更为复杂。
1.1.1.3 “AMBA外设时钟使能寄存器”是怎么回事儿?
alinx
的教程提到这个名词的时候,我不禁愣了一下,因为这个在裸机开发中压根儿不存在。
但还是去花了时间去确认这是个啥!回到《ug585-Zynq-7000-TRM.pdf》。
可以看到这里有个APER_CLK_CTRL
寄存器,详细看下该寄存器:
所以,这个寄存器的各个bit用于控制AMBA
时钟,第23
bit用于控制GPIO
相关的时钟。那好像也不奇怪了!
1.1.2 全部流程
假设启动镜像已经制作结束,那么就从驱动开发开始!
1.1.2.1 编辑和编译驱动
首先使用petalinux-create -t modules --name ax-led-drv
创建驱动模板文件,如下:
dention@ubuntu:~/petalinux_proj/test_peta/proj_peta$ petalinux-create -t modules --name ax-led-drv
INFO: Create modules: ax-led-drv
INFO: New modules successfully created in /home/dention/petalinux_proj/test_peta/proj_peta/project-spec/meta-user/recipes-modules/ax-led-drv
修改路径为/home/dention/petalinux_proj/test_peta/proj_peta/project-spec/meta-user/recipes-modules/ax-led-drv/files/
下的驱动模板文件ax-led-drv.c
为1.1.1.1.2
中的驱动程序。
随后petalinux-config -c rootfs
选中驱动模块,如下:
dention@ubuntu:~/petalinux_proj/test_peta/proj_peta$ petalinux-config -c rootfs
[INFO] sourcing bitbake
[INFO] generating plnxtool conf
[INFO] generating meta-plnx-generated layer
[INFO] generating user layers
[INFO] generating machine configuration
[INFO] configuring: rootfs
[INFO] generating kconfig for Rootfs
[INFO] menuconfig rootfs*** End of the configuration.
*** Execute 'make' to start the build or try 'make help'.[INFO] generating petalinux-user-image.bb
[INFO] successfully configured rootfs
选中之后,使用petalinux-build
编译得到.ko
驱动文件,如下:
dention@ubuntu:~/petalinux_proj/test_peta/proj_peta$ petalinux-build
[INFO] building project
[INFO] sourcing bitbake
[INFO] generating user layers
INFO: bitbake petalinux-user-image
Loading cache: 100% |########################################################################################################################| Time: 0:00:02
Loaded 3811 entries from dependency cache.
Parsing recipes: 100% |######################################################################################################################| Time: 0:00:08
Parsing of 2778 .bb files complete (2774 cached, 4 parsed). 3813 targets, 163 skipped, 0 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies
Initialising tasks: 100% |###################################################################################################################| Time: 0:00:07
Checking sstate mirror object availability: 100% |###########################################################################################| Time: 0:00:22
Sstate summary: Wanted 134 Found 20 Missed 228 Current 753 (14% match, 87% complete)
NOTE: Executing SetScene Tasks
NOTE: Executing RunQueue Tasks
NOTE: Tasks Summary: Attempted 3199 tasks of which 2926 didn't need to be rerun and all succeeded.
INFO: Copying Images from deploy to images
NOTE: Failed to copy built images to tftp dir: /tftpboot
[INFO] successfully built project
可以找一下.ko
文件:
dention@ubuntu:~/petalinux_proj/test_peta/proj_peta$ find ./ -name "ax-led-drv.ko"
./build/tmp/sysroots-components/plnx_zynq7/ax-led-drv/lib/modules/4.19.0-xilinx-v2019.1/extra/ax-led-drv.ko
到此为止,驱动开发部分结束!接下来编辑和编译测试程序!
1.1.2.2 编辑和编译测试程序
按照下述存放文件:
dention@ubuntu:~/qt_proj$ tree
.
├── axleddev_test
│ └── main.c
└── build-axleddev_test-ZYNQ-Debug└── Makefile2 directories, 2 files
为什么是这么放的,可以参考嵌入式系统内核镜像相关(九)。
其中main.c
是:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>int main(int argc, char **argv)
{
int fd;
char buf;if(3 != argc)
{
printf("none para\n");
return -1;
}fd = open(argv[1], O_RDWR);
if(fd < 0)
{
printf("Can't open file %s\r\n", argv[1]);
return -1;
}if(!strcmp("on",argv[2]))
{
printf("ps_led1 on\n");
buf = 1;
write(fd, &buf, 1);
}
else if(!strcmp("off",argv[2]))
{
printf("ps_led1 off\n");
buf = 0;
write(fd, &buf, 1);
}
else
{
printf("wrong para\n");
return -2;
}close(fd);
return 0;
}
其中Makefile
是:
CC = arm-linux-gnueabihf-gcc
CFLAGS = -Wall -gall: axleddev_testaxleddev_test: main.o$(CC) $(CFLAGS) -o axleddev_test main.omain.o: ../axleddev_test/main.c$(CC) $(CFLAGS) -c ../axleddev_test/main.cclean:rm -f axleddev_test main.o
然后make all
即可得到驱动模块测试可执行文件。
1.1.2.3 测试结果
参照嵌入式系统内核镜像相关(八)将驱动模块文件和测试的可执行文件放到开发板侧!
参考下述运行:
root@proj_peta:~# ls
ax-led-drv.ko axleddev_test
root@proj_peta:~# chmod 777 -R axleddev_test
root@proj_peta:~# insmod ax-led-drv.ko
ax_led_drv: loading out-of-tree module taints kernel.
gpio_led_dev_init_ok
root@proj_peta:~# mknod /dev/alinx-led c 200 0
root@proj_peta:~# ls /dev/
alinx-led mtab ram6 tty27 tty55
block mtd0 ram7 tty28 tty56
char mtd0ro ram8 tty29 tty57
console mtd1 ram9 tty3 tty58
cpu_dma_latency mtd1ro random tty30 tty59
disk mtd2 shm tty31 tty6
fd mtd2ro snd tty32 tty60
full mtd3 stderr tty33 tty61
gpiochip0 mtd3ro stdin tty34 tty62
iio:device0 mtdblock0 stdout tty35 tty63
initctl mtdblock1 tty tty36 tty7
kmsg mtdblock2 tty0 tty37 tty8
log mtdblock3 tty1 tty38 tty9
loop-control network_latency tty10 tty39 ttyPS0
loop0 network_throughput tty11 tty4 ttyPS1
loop1 null tty12 tty40 udev_network_queue
loop2 port tty13 tty41 urandom
loop3 ptmx tty14 tty42 vcs
loop4 pts tty15 tty43 vcs1
loop5 ram0 tty16 tty44 vcsa
loop6 ram1 tty17 tty45 vcsa1
loop7 ram10 tty18 tty46 vcsu
mem ram11 tty19 tty47 vcsu1
memory_bandwidth ram12 tty2 tty48 vga_arbiter
mmcblk0 ram13 tty20 tty49 watchdog
mmcblk0p1 ram14 tty21 tty5 watchdog0
mmcblk0p2 ram15 tty22 tty50 zero
mmcblk1 ram2 tty23 tty51
mmcblk1boot0 ram3 tty24 tty52
mmcblk1boot1 ram4 tty25 tty53
mmcblk1rpmb ram5 tty26 tty54
root@proj_peta:~# ls
ax-led-drv.ko axleddev_test
root@proj_peta:~# ./axleddev_test /dev/alinx-led on
gpio_test module open
ps_led1 on
gpio_test module write
gpio_test ON
gpio_test module release
root@proj_peta:~# ./axleddev_test /dev/alinx-led off
gpio_test module open
ps_led1 off
gpio_test module write
gpio_test OFF
gpio_test module release
初始情况如下:
其中亮灯的时候,如下:
灭灯的时候,如下:
到此为止!
1.2 设备树和驱动程序的优先级与延申思考
现在我把之前嵌入式系统内核镜像相关(二)里面的设备树替换掉本文一开始提到的设备树!
注意设备树中的compatible
是gpio-leds
,而驱动中的设备名称是gpio_leds_our
。
1.2.1 insmod驱动以后cat /proc/devices显示什么(回答优先级的问题)?
显示结果如下:
root@proj_peta:~# chmod 777 -R axleddev_test
root@proj_peta:~# insmod ax-led-drv.ko
ax_led_drv: loading out-of-tree module taints kernel.
gpio_led_dev_init_ok
root@proj_peta:~# cat /proc/devices
Character devices:1 mem4 /dev/vc/04 tty5 /dev/tty5 /dev/console5 /dev/ptmx7 vcs10 misc13 input21 sg29 fb81 video4linux89 i2c90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
200 gpio_leds_our
226 drm
244 rpmb
245 uio
246 watchdog
247 iio
248 ptp
249 pps
250 media
251 rtc
252 ttyPS
253 ttyPS
254 gpiochipBlock devices:1 ramdisk7 loop8 sd31 mtdblock65 sd66 sd67 sd68 sd69 sd70 sd71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
259 blkext
根据编号200
,可以证明即使设备树变更了,但是驱动还是顺利加载!
但是下一步的驱动测试失败了,灯的状态是10个灯按照heartbeat
模式闪烁。
但沿用嵌入式系统内核镜像相关(二)里面的内置测试可以成功!
不能排除的一点是petalinux-build
将设备树的内容编译进内核,并且启用了gpio-leds
的内置驱动模块。这就导致再使用insmod
驱动进宏内核时,可能因为宏内核判断gpio
的emio
引脚被占用,即使insmod
驱动模块,无法通过测试。
可能的解决办法是rmmod
内核自带的gpio-leds
模块,或者,单独编译设备树文件并加载进入内核。但是,petalinux
平台并不支持单独编译设备树文件(我确实没找到),但是存在单独编译设备树文件和设备树插件文件的方式(可以参考野火的教程)。
这个话题值得后续研究,我在博客中标记一下!
1.2.2 延申思考——4个led的控制驱动就只有1个。
现在4
个led灯的控制是由1个驱动完成的!
因此,可以确认的一点是被控制外设的数量并不绝对等于驱动数量。
此外,驱动可以控制4
个led灯,也就意味着可以控制10
个led灯。但是控制的逻辑被限制在驱动程序里面,导致需要控制10
个led灯时,不得不重新编译驱动模块,这在petalinux
中还是挺浪费时间的!
怎么办?这也是后续接着研究的内容!
(很幸运的是,自己从4
个led灯的例子,cover掉以往讲解驱动教程中提到为什么要发展设备驱动平台和设备树的原因!)
二、后续魔改
这一节放在下一篇文章了,本篇内容够多了!
另外后面几篇就不一定会那么详细地展开步骤,只会提及教程中需要注意的点。
总结
对alinx
教程中与我开发板不符的点进行详细描述,并深度思考一些从未被教程提到的问题。
此外,这次开发将Vivado
最小ps
子系统、Vitis SDK
上的xparameters.h
和驱动开发结合在一起,相互辅助判断驱动开发的bug
究竟出在哪里,尽管怎么遇到bug
和怎么解决bug
的过程我并没有展开,但从上文我怎么改进中可窥一角。