文章目錄
- 前言
- 一、點亮多個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
的過程我并沒有展開,但從上文我怎么改進中可窺一角。