嵌入式系统内核镜像相关(十)

文章目录

  • 前言
  • 一、点亮多个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》。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

所以使用0x00000284GPIO方向寄存器和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个基于EMIOGPIO-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采用的是基于MIOGPIO引脚,因此没法儿直接用。我还是参照1.1.1.1中的流程修改!

不难看出来,裸机开发的流程中,得益于xparameters.h的存在,避免了自己去找文档,只需要关注EMIO的起始序号和范围即可。然而,驱动开发需要自己确认某些寄存器的引脚以实现正常功能。

所以,相比之下,从这一方面来看,驱动开发的流程更为复杂。

1.1.1.3 “AMBA外设时钟使能寄存器”是怎么回事儿?

alinx的教程提到这个名词的时候,我不禁愣了一下,因为这个在裸机开发中压根儿不存在。

但还是去花了时间去确认这是个啥!回到《ug585-Zynq-7000-TRM.pdf》。

在这里插入图片描述
在这里插入图片描述
可以看到这里有个APER_CLK_CTRL寄存器,详细看下该寄存器:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以,这个寄存器的各个bit用于控制AMBA时钟,第23bit用于控制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.c1.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 设备树和驱动程序的优先级与延申思考

现在我把之前嵌入式系统内核镜像相关(二)里面的设备树替换掉本文一开始提到的设备树!

注意设备树中的compatiblegpio-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驱动进宏内核时,可能因为宏内核判断gpioemio引脚被占用,即使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的过程我并没有展开,但从上文我怎么改进中可窥一角。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/89886.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/89886.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Word和Excel批量转PDF新方法,操作简单

PDF是一种跨平台的文档格式&#xff0c;无论在任何设备上查看&#xff0c;其排版、字体和图像都不会发生变化。这确保了文档的一致性&#xff0c;避免了由于不同软件版本或操作系统引起的显示问题。这款小巧的工具大小不到2MB&#xff0c;使用起来异常简单。只需要把需要转换的…

AI搜索 MCP最佳实践

背景 那些 LLM 不知道的事 尝试直接询问LLM“今天天气如何”时&#xff0c;会发现LLM无法回答——它既不知道“今天”是哪天&#xff0c;也无法获取地理位置信息。这揭示了LLM的局限&#xff1a;缺乏与外部工具和实时数据的交互能力。 为解决这一问题&#xff0c;MCP&#x…

JVM 简介与作用

&#x1f680; JVM 简介与作用 &#x1f4da; 深入理解 Java 虚拟机的核心概念与重要作用 &#x1f4d6; 目录 &#x1f914; 什么是 Java 虚拟机&#xff08;JVM&#xff09;&#x1f310; JVM 在 Java 生态中的核心地位&#x1f500; JVM 跨平台原理剖析&#x1f4dd; 总结 …

✨ OpenAudio S1:影视级文本转语音与语音克隆Mac整合包

✨ OpenAudio S1&#xff1a;影视级文本转语音与语音克隆Mac整合包 &#x1f680; OpenAudio S1 简介 OpenAudio S1 是由 Fish Audio 开发的 Fish Speech 系列的最新一代人工智能语音生成模型。该模型旨在大幅提升 AI 语音生成的技术水平&#xff0c;为用户提供更加自然、富有表…

spring加载外部properties文件属性时,读取到userName变量值和properties文件的值不一致

问题 使用spring DI注入外部properties文件属性时&#xff0c;读取到userName变量值和properties文件的值不一致。 bean属性注入&#xff1a; <!--加载配置文件--> <context:property-placeholder location"classpath:*.properties"/><bean id"…

黑马点评系列问题之基础篇p7 06初识redis无法在虚拟机查到图形化界面存进去的键

问题描述 在RESP中输入了一些键(name,age等这些) 但是在图形化界面里面输入的&#xff0c;在非图形化界面就找不到&#xff0c;在非图形化界面里输入的&#xff0c;在图形化界面里就可以查到。 原因分析及解决 经过多次实验&#xff0c;发现是因为在添加键名的时候&#xff0…

在VMware虚拟机中安装Windows 98时,Explorer提示“该程序执行了非法操作,即将关闭”的解决办法

在使用iso文件&#xff08;MD5: 0E496B5DCC519F550AAF0BCFBB4A11EA&#xff09;安装Windows98时&#xff0c;遇到此提示。 虽然原因未知&#xff0c;也无需深入探究&#xff0c;但是根据网友在 https://www.bilibili.com/opus/435866522585702782 中给出的相似经验&#xff…

在浏览器中使用SQLite(官方sqlite3.wasm)

有人可能会问&#xff1a;既然浏览器里又内置得IndexedDB&#xff0c;而且在IndexedDB里存数据&#xff0c;关了浏览器数据也不会丢&#xff0c;为什么还要在浏览器里用SQLite? 实际上&#xff0c;当 IndexedDB 内的数据量增多&#xff0c;数据和数据之间的关系变得复杂&…

数据结构(Java)--位运算

前言 本文为本小白学习数据结构的笔记&#xff0c;将以算法题为导向&#xff0c;向大家更清晰的介绍数据结构相关知识&#xff08;算法题都出自B站马士兵教育——左老师的课程&#xff0c;讲的很好&#xff0c;对于想入门刷题的人很有帮助&#xff09; 为什么要使用为位运算 位…

秋招Day14 - Redis - 应用

Redis如何实现异步消息队列&#xff1f; List配合LPUSH和RPOP。 另外就是用 Redis 的 Pub/Sub 来实现简单的消息广播和订阅。 但是这两种方式都是不可靠的&#xff0c;因为没有 ACK 机制所以不能保证订阅者一定能收到消息&#xff0c;也不支持消息持久化。 Redis如何实现延时…

因果语言模型、自回归语言模型、仅解码器语言模型都是同一类模型

因果语言模型、自回归语言模型、仅解码器语言模型都是同一类模型 flyfish 因果语言模型&#xff08;causal Language Models&#xff09; 自回归语言模型&#xff08;autoregressive language models&#xff09; 仅解码器语言模型&#xff08;decoder-only language models&am…

jvm架构原理剖析篇

简单题&#xff08;5道&#xff09; 考查内容&#xff1a;JVM运行时数据区域 题干&#xff1a;Java虚拟机栈的主要作用是&#xff1f; A. 存储对象实例 B. 存储方法调用和局部变量 C. 存储静态字段 D. 存储字节码指令 正确答案&#xff1a;B 解析&#xff1a;虚拟机栈用于存储方…

智链万物:人工智能驱动的产业智能化革命

当生成式AI在艺术与创意领域掀起风暴&#xff0c;大型语言模型重塑信息交互方式时&#xff0c;一场更为基础、影响更为深远的变革&#xff0c;正在全球实体经济的根基处悄然发生并加速推进——这就是产业智能化。它并非简单的“机器换人”&#xff0c;而是人工智能&#xff08;…

python中上下文管理器 与 try finally有什么区别

目录 主要区别代码对比何时使用哪种方式 主要区别 语法简洁性 上下文管理器使用 with 语句&#xff0c;语法更简洁优雅try-finally 需要显式编写异常处理代码&#xff0c;更冗长 代码复用性 上下文管理器可以封装为类或函数&#xff0c;便于在多处复用try-finally 通常需要在每…

人体属性识别+跌倒检测:儿童行为监测与安全升级

智慧幼儿园的AI智能检测盒应用实践 背景&#xff1a;传统园区管理的三大痛点 传统幼儿园管理长期面临三大核心挑战&#xff1a;一是安全监控依赖人工巡查&#xff0c;存在视觉盲区与响应延迟&#xff0c;如某连锁幼儿园曾因人工巡查疏漏&#xff0c;导致3起儿童跌倒事故未能及…

【ESP32-IDF笔记】09-UART配置和使用

环境配置 Visual Studio Code &#xff1a;版本1.98.2 ESP32&#xff1a;ESP32-S3 ESP-IDF&#xff1a;V5.4 支持型号&#xff1a;ESP32、ESP32-C2、ESP32-C3、ESP32-C5、ESP32-C6、ESP32-C61、ESP32-H2、ESP32-P4、 ESP32-S2、ESP32-S3 简介 通用异步接收器/发送器 (UART) …

在 .NET Core 和 React 中使用 WebSockets 和 SignalR 进行实时数据传输

对于需要即时更新和通知的应用程序来说&#xff0c;实时数据传输至关重要。在 .NET Core 中&#xff0c;WebSocket 和 SignalR 提供了强大的工具来实现客户端和服务器之间的实时通信。在本指南中&#xff0c;我们将探讨如何在 .NET Core 应用程序中使用 WebSocket 和 SignalR 实…

第八十六篇 大数据排序算法:从厨房整理到分布式排序的智慧

目录一、基础排序算法&#xff1a;生活场景中的计算智慧1.1 冒泡排序&#xff1a;图书馆的书籍整理1.2 插入排序&#xff1a;厨房调料的整理艺术二、高效排序算法&#xff1a;大数据处理的利器2.1 快速排序&#xff1a;音乐APP的智能歌单2.2 归并排序&#xff1a;学校成绩单的合…

开源 | V3.1.1慧知开源重卡运营充电桩平台 - 重卡运营充电桩平台管理解决方案;企业级完整代码 多租户、模拟器、多运营商、多小程序;

【开源免费版】推荐一套企业级开源充电桩平台&#xff1a;完整代码包含多租户、硬件模拟器、多运营商、多小程序&#xff0c;汽车 电动自行车、云快充协议&#xff1b;——(慧哥)慧知开源充电桩平台&#xff1b;https://liwenhui.blog.csdn.net/article/details/148242725?spm…

ONLYOFFICE 协作空间 企业版使用秘籍-8.使用虚拟数据房间,处理机密文档更安全

在当今快节奏的社会中&#xff0c;信息已成为极其关键的资源&#xff0c;因此&#xff0c;保护敏感数据至关重要。ONLYOFFICE 协作空间中的虚拟数据房间&#xff08;VDR&#xff09;提供了一个安全便捷的工作空间&#xff0c;确保文档受到严密保护的同时&#xff0c;也能实现轻…