嵌入式系统内核镜像相关(十二)
文章目录
- 前言
- 零、驱动和操作系统镜像编译流程
- 0.1 驱动和操作系统镜像的开发流程
- 0.1.1 制作操作系统镜像
- 0.1.2 开发驱动
- 0.1.2.1 开发驱动模块
- 0.1.2.2 开发驱动测试程序
- 0.1.3 测试
- 0.2 修改驱动和设备树后的重新编译流程
- 0.3 仅修改驱动后的重新编译流程
- 五、gpio输入(移植成功)
- 5.1 移植的难点
- 5.2 移植的程序
- 5.2.1 2个驱动模块的代码
- 5.2.2 gpio输入测试用的驱动测试代码与makefile
- 5.2.3 设备树
- 5.3 移植的结果
- 5.4 感悟
- 六、定时器(移植成功)
- 6.1 移植的难点
- 6.2 移植的程序
- 6.3 移植的结果
- 七、中断(单独放一篇)
- 总结
前言
接着将alinx
的驱动开发移植到璞致板子上。
零、驱动和操作系统镜像编译流程
0.1 驱动和操作系统镜像的开发流程
这个流程重新提及一下,开发流程首先制作操作系统镜像,随后开发驱动。
0.1.1 制作操作系统镜像
全流程如下:
# source工具
source /opt/pkg/petalinux/settings.sh
source /tools/Xilinx/Vivado/2019.1/settings64.sh# 创建项目,并以zynq作为模板
petalinux-create -t project -n proj_peta --template zynq
# 配置config,其中需要修改Yocto以实现离线编译,修改串口为ps_uart_0
petalinux-config --get-hw-description=../hardware/
# 编译
petalinux-build
# 打包
petalinux-package --boot --fsbl --u-boot --fpga --force
在./images/linux/
中有image.ub
和BOOT.BIN
,拷贝进sd卡。
0.1.2 开发驱动
分为两个步骤,第一是驱动模块开发,第二是驱动测试程序开发。
0.1.2.1 开发驱动模块
# 开发项目名称为ourhelloworld的驱动
petalinux-create -t modules --name ourhelloworld
# 配置并选中modules中的ourhelloworld
petalinux-config -c rootfs
# 编译驱动
petalinux-build
在./build/tmp/sysroots-components/plnx_zynq7/ax-led-devicetree/lib/modules/4.19.0-xilinx-v2019.1/extra/
可以找到ourhelloworld.ko
。
0.1.2.2 开发驱动测试程序
# 撰写测试程序main.c
...
# 编译
...
可以参考嵌入式系统内核镜像相关(十)中的1.1.2
。
0.1.3 测试
将BOOT.BIN
和image.ub
拷贝到sd卡上,同时将.ko
文件与驱动模块测试文件移动到开发板上进行测试。参考嵌入式系统内核镜像相关(十)中的1.1.2
。
0.2 修改驱动和设备树后的重新编译流程
如果需要修改驱动和设备树,则需要在修改完驱动和设备树之前完成:
# 清除镜像文件,但无法清除驱动的编译结果文件(因为petalinux无法单独编译设备树,只能将设备树编译进内核)
petalinux-build -x distclean
# 清除驱动的编译结果文件,其中比如驱动项目为ax-key-dev
petalinux-build -c ax-key-dev -x do_cleansstate# 可以查找ax-key-dev.ko是否存在即可确认清除是否成功
find ./ -name "ax-key-led.ko"
在完成驱动和设备树修改后,重新编译:
# 可以得到驱动的.ko文件和image.ub
petalinux-build# 打包得到BOOT.BIN
petalinux-package --boot --fsbl --u-boot --fpga --force
随后将BOOT.BIN
和image.ub
拷贝到sd卡上,同时通过find
命令搜索得到.ko
文件,并将其与驱动模块测试文件移动到开发板上进行测试。
0.3 仅修改驱动后的重新编译流程
如果仅需要修改驱动,则需要在修改完驱动之前完成:
# 清除驱动的编译结果文件,其中比如驱动项目为ax-key-dev
petalinux-build -c ax-key-dev -x do_cleansstate# 可以查找ax-key-dev.ko是否存在即可确认清除是否成功
find ./ -name "ax-key-led.ko"
在完成驱动和设备树修改后,重新编译:
# 可以得到驱动的.ko文件和image.ub
petalinux-build
随后通过find
命令搜索得到.ko
文件,并将其与驱动模块测试文件移动到开发板上进行测试。
五、gpio输入(移植成功)
后来仔细思考了,也许可以修改,于是打算试试!
我必须说这一篇耗费了我大量精力和时间,一下午+一晚上+一上午,但功夫不负有心人,总算是移植成功了!
关于PS
最小子系统和Vitis SDK
裸机测试部分见嵌入式系统内核镜像相关(十一)的五、gpio输入(受限于资源而无法移植)
。
另外注意按键的有效逻辑,璞致开发板提供的信息如下:
5.1 移植的难点
核心难点正如嵌入式系统内核镜像相关(十一)的五、gpio输入(受限于资源而无法移植)
提到的那样:
理由是因为alinx_key_gpio这样的gpio号和四、并发的处理一样,按键也是被EMIO驱动,因此无法获取。尤其是我需要通过这个变量以gpio_get_value(dev->alinx_key_gpio)的方式获取按键是否按下,但gpio_get_value是gpio子系统下的函数,因此我无法接着改动这段代码了,改完也无法完成测试!
但后来想到既然有GPIO_DATA_0
这个控制寄存器指针存在,没道理获取不了key
的按键状态。因此,找个平替的函数替代gpio_get_value
函数即可。
我后续在这个实验中犯下了一个比较明显的错误,就是设置了4个按键,通过4个按键控制led的亮灭。这会导致一个情况就是4个按键按下的先后以及是否按到底/弹跳的一致性我很难控制,相比之下,控制1个按键的难度最小。因此后续的驱动模块使用1个按键来进行控制。
回到嵌入式系统内核镜像相关(十一)的五、gpio输入(受限于资源而无法移植)
提到的sdk
开发,可以确定的是,GPIO_DATA_0
指针指向的值从最右边的最低位从右往左依次是5个EMIO-LED
和5个key
(序号依次为54、55、56、57、58、59、60、61、62、63)。我只使用59编号位的key
,使其作为输入,从60~63编号位的key
不接受数据。因此设计如下:
static int extract_and_or_bits(uint32_t *gpio_data) {// 从GPIO_DATA_0寄存器中读取值uint32_t reg_value = *gpio_data;// 提取第6位uint8_t bits_to_or = (reg_value >> 5) & 0x1;// 返回结果return bits_to_or;
}
并在按键驱动的下述模块中完成替代(其中被注释的内容包括了gpio_get_value
函数):
/* read函数实现, 对应到Linux系统调用函数的read函数 */
static ssize_t gpio_key_read(struct file *file_p, char __user *buf, size_t len, loff_t *loff_t_p)
{int ret = 0;unsigned int key_value = 0;struct alinx_char_dev *dev = file_p->private_data;printk("extract_and_or_bits(GPIO_DATA_0) at start is : %d\r\n", extract_and_or_bits(GPIO_DATA_0));/* 检查按键是否被按下 *///if (0 == gpio_get_value(dev->alinx_key_gpio)) {if (0 == extract_and_or_bits(GPIO_DATA_0)) {/* 按键被按下 */mdelay(50); /* 防抖 */printk("mdelay is okay!\r\n");/* 等待按键抬起 *///while (!gpio_get_value(dev->alinx_key_gpio));while (extract_and_or_bits(GPIO_DATA_0));printk("extract_and_or_bits(GPIO_DATA_0) at end is : %d\r\n", extract_and_or_bits(GPIO_DATA_0));key_value = 1;} else {/* 按键未被按下 */}/* 返回按键状态 */ret = copy_to_user(buf, &key_value, sizeof(key_value));return ret;
}
关于设备树的按键部分我设置如下:
alinxkey {compatible = "alinxkey";reg = <0xE000A284 0x04 /* gpio 方向寄存器 */0xE000A048 0x04 /* gpio 控制寄存器 */0xF800012C 0x04 /* AMBA 外设时钟使能寄存器 */>;};
5.2 移植的程序
开发流程参考alinx
的gpio输入,因此本文需要包括2个驱动模块,分别是led
驱动和key
驱动。led
驱动使用嵌入式系统内核镜像相关(十一)的二、设备树和of函数(移植成功)
。但为了方便起见,在这里放一下驱动模块代码。
5.2.1 2个驱动模块的代码
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>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/of.h>#include <linux/device.h>
#include <asm/uaccess.h>/* 设备节点名称 */
#define DEVICE_NAME "gpio_leds_our3"
/* 设备号个数 */
#define DEVID_COUNT 1
/* 驱动个数 */
#define DRIVE_COUNT 1
/* 主设备号 */
#define MAJOR
/* 次设备号 */
#define MINOR 0/* gpio 寄存器虚拟地址 */
static u32 *GPIO_DIRM_0;
/* gpio 使能寄存器 */
static u32 *GPIO_OEN_0;
/* gpio 控制寄存器 */
static u32 *GPIO_DATA_0;
/* AMBA 外设时钟使能寄存器 */
static u32 *APER_CLK_CTRL;/* 把驱动代码中会用到的数据打包进设备结构体 */
struct alinx_char_dev{
dev_t devid; //设备号
struct cdev cdev; //字符设备
struct class *class; //类
struct device *device; //设备
struct device_node *nd; //设备树的设备节点
};
/* 声明设备结构体 */
static struct alinx_char_dev alinx_char = {
.cdev = {
.owner = THIS_MODULE,
},
};/* open 函数实现, 对应到 Linux 系统调用函数的 open 函数 */
static int gpio_leds_open(struct inode *inode_p, struct file *file_p)
{
/* 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 ax_char_fops = {
.owner = THIS_MODULE,
.open = gpio_leds_open,
.write = gpio_leds_write,
.release = gpio_leds_release,
};/* 模块加载时会调用的函数 */
static int __init gpio_led_init(void)
{
/* 用于接受返回值 */
u32 ret = 0;
/* 存放 reg 数据的数组 */
u32 reg_data[10];/* 通过节点名称获取节点 */
alinx_char.nd = of_find_node_by_name(NULL, "alinxled");
/* 4、获取 reg 属性内容 */
ret = of_property_read_u32_array(alinx_char.nd, "reg", reg_data, 8);
if(ret < 0)
{
printk("get reg failed!\r\n");
return -1;
}
else
{
/* do nothing */
}/* 把需要修改的物理地址映射到虚拟地址 */
GPIO_DIRM_0 = ioremap(reg_data[0], reg_data[1]);
GPIO_OEN_0 = ioremap(reg_data[2], reg_data[3]);
GPIO_DATA_0 = ioremap(reg_data[4], reg_data[5]);
APER_CLK_CTRL = ioremap(reg_data[6], reg_data[7]);/* 注册设备号 */
alloc_chrdev_region(&alinx_char.devid, MINOR, DEVID_COUNT, DEVICE_NAME);/* 初始化字符设备结构体 */
cdev_init(&alinx_char.cdev, &ax_char_fops);/* 注册字符设备 */
cdev_add(&alinx_char.cdev, alinx_char.devid, DRIVE_COUNT);/* 创建类 */
alinx_char.class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(alinx_char.class))
{
return PTR_ERR(alinx_char.class);
}/* 创建设备节点 */
alinx_char.device = device_create(alinx_char.class, NULL,
alinx_char.devid, NULL, DEVICE_NAME);
if (IS_ERR(alinx_char.device))
{
return PTR_ERR(alinx_char.device);
}return 0;
}/* 卸载模块 */
static void __exit gpio_led_exit(void)
{
/* 注销字符设备 */
cdev_del(&alinx_char.cdev);/* 注销设备号 */
unregister_chrdev_region(alinx_char.devid, DEVID_COUNT);/* 删除设备节点 */
device_destroy(alinx_char.class, alinx_char.devid);/* 删除类 */
class_destroy(alinx_char.class);/* 释放对虚拟地址的占用 */
iounmap(GPIO_DIRM_0);
iounmap(GPIO_OEN_0);
iounmap(GPIO_DATA_0);
iounmap(APER_CLK_CTRL);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("DEVICE TREE GPIO LED driver");
MODULE_VERSION("v1.0");
MODULE_LICENSE("GPL");
key
驱动模块代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>
#include <asm/io.h>
#include <linux/gpio.h>/* 设备节点名称 */
#define DEVICE_NAME "gpio_key_our6"
/* 设备号个数 */
#define DEVID_COUNT 1
/* 驱动个数 */
#define DRIVE_COUNT 1
/* 主设备号 */
#define MAJOR
/* 次设备号 */
#define MINOR 0/* gpio 寄存器虚拟地址 */
static u32 *GPIO_DIRM_0;
/* gpio 控制寄存器 */
static u32 *GPIO_DATA_0;
/* AMBA 外设时钟使能寄存器 */
static u32 *APER_CLK_CTRL;/*
static int extract_and_or_bits(uint32_t *gpio_data) {// 从GPIO_DATA_0寄存器中读取值uint32_t reg_value = *gpio_data;// 提取第6到第9位uint8_t bits_to_or = (reg_value >> 5) & 0xF;// 执行位或操作bits_to_or |= (1 << 3); // 对应于第9位bits_to_or |= (1 << 2); // 对应于第8位bits_to_or |= (1 << 1); // 对应于第7位bits_to_or |= (1 << 0); // 对应于第6位// 返回结果return bits_to_or;
}*/static int extract_and_or_bits(uint32_t *gpio_data) {// 从GPIO_DATA_0寄存器中读取值uint32_t reg_value = *gpio_data;// 提取第6位uint8_t bits_to_or = (reg_value >> 5) & 0x1;// 返回结果return bits_to_or;
}/* 把驱动代码中会用到的数据打包进设备结构体 */
struct alinx_char_dev {dev_t devid; //设备号struct cdev cdev; //字符设备struct class *class; //类struct device *device; //设备struct device_node *nd; //设备树的设备节点//int alinx_key_gpio; //gpio号
};/* 声明设备结构体 */
static struct alinx_char_dev alinx_char = {.cdev = {.owner = THIS_MODULE,},
};/* open函数实现, 对应到Linux系统调用函数的open函数 */
static int gpio_key_open(struct inode *inode_p, struct file *file_p)
{/* MIO_0 时钟使能 */*APER_CLK_CTRL |= 0x00400000;/* MIO_0 设置成input */*GPIO_DIRM_0 &= 0xFFFFFFDF;/* 设置私有数据 */file_p->private_data = &alinx_char;printk("gpio_key module open\n");return 0;
}/* read函数实现, 对应到Linux系统调用函数的read函数 */
static ssize_t gpio_key_read(struct file *file_p, char __user *buf, size_t len, loff_t *loff_t_p)
{int ret = 0;unsigned int key_value = 0;struct alinx_char_dev *dev = file_p->private_data;printk("extract_and_or_bits(GPIO_DATA_0) at start is : %d\r\n", extract_and_or_bits(GPIO_DATA_0));/* 检查按键是否被按下 *///if (0 == gpio_get_value(dev->alinx_key_gpio)) {if (0 == extract_and_or_bits(GPIO_DATA_0)) {/* 按键被按下 */mdelay(50); /* 防抖 */printk("mdelay is okay!\r\n");/* 等待按键抬起 *///while (!gpio_get_value(dev->alinx_key_gpio));while (extract_and_or_bits(GPIO_DATA_0));printk("extract_and_or_bits(GPIO_DATA_0) at end is : %d\r\n", extract_and_or_bits(GPIO_DATA_0));key_value = 1;} else {/* 按键未被按下 */}/* 返回按键状态 */ret = copy_to_user(buf, &key_value, sizeof(key_value));return ret;
}/* release函数实现, 对应到Linux系统调用函数的close函数 */
static int gpio_key_release(struct inode *inode_p, struct file *file_p)
{printk("gpio_key module release\n");return 0;
}/* file_operations结构体声明, 是上面open、read实现函数与系统调用函数对应的关键 */
static struct file_operations ax_char_fops = {.owner = THIS_MODULE,.open = gpio_key_open,.read = gpio_key_read,.release = gpio_key_release,
};/* 模块加载时会调用的函数 */
static int __init gpio_key_init(void)
{/* 用于接受返回值 */u32 ret = 0;/* 存放 reg 数据的数组 */u32 reg_data[10];/* 通过节点名称获取节点 */alinx_char.nd = of_find_node_by_name(NULL, "alinxkey");/* 4、获取 reg 属性内容 */ret = of_property_read_u32_array(alinx_char.nd, "reg", reg_data, 6);if(ret < 0){printk("get reg failed!\r\n");return -1;}else{/* do nothing */}/* 把需要修改的物理地址映射到虚拟地址 */GPIO_DIRM_0 = ioremap(reg_data[0], reg_data[1]);GPIO_DATA_0 = ioremap(reg_data[2], reg_data[3]);APER_CLK_CTRL = ioremap(reg_data[4], reg_data[5]);alloc_chrdev_region(&alinx_char.devid, MINOR, DEVID_COUNT, DEVICE_NAME);cdev_init(&alinx_char.cdev, &ax_char_fops);cdev_add(&alinx_char.cdev, alinx_char.devid, DRIVE_COUNT);alinx_char.class = class_create(THIS_MODULE, DEVICE_NAME);if (IS_ERR(alinx_char.class)) {return PTR_ERR(alinx_char.class);}alinx_char.device = device_create(alinx_char.class, NULL, alinx_char.devid, NULL, DEVICE_NAME);if (IS_ERR(alinx_char.device)) {return PTR_ERR(alinx_char.device);}printk("init gpio_key successfully!\r\n");return 0;
}/* 卸载模块 */
static void __exit gpio_key_exit(void)
{/* 注销字符设备 */cdev_del(&alinx_char.cdev);/* 注销设备号 */unregister_chrdev_region(alinx_char.devid, DEVID_COUNT);/* 删除设备节点 */device_destroy(alinx_char.class, alinx_char.devid);/* 删除类 */class_destroy(alinx_char.class);/* 释放对虚拟地址的占用 */iounmap(GPIO_DIRM_0);iounmap(GPIO_DATA_0);iounmap(APER_CLK_CTRL);printk("gpio_key_dev_exit_ok\n");
}module_init(gpio_key_init);
module_exit(gpio_key_exit);MODULE_AUTHOR("Alinx");
MODULE_ALIAS("gpio_key");
MODULE_DESCRIPTION("GPIO KEY driver");
MODULE_VERSION("v1.0");
MODULE_LICENSE("GPL");
5.2.2 gpio输入测试用的驱动测试代码与makefile
驱动测试代码是:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>int main(int argc, char *argv[])
{int fd, fd_l ,ret;char *filename, led_value = 0;//char *filename, led_value = 1;unsigned int key_value;/* 验证输入参数个数 */if(argc != 2){printf("Error Usage\r\n");return -1;}/* 打开输入的设备文件, 获取文件句柄 */filename = argv[1];fd = open(filename, O_RDWR);if(fd < 0){/* 打开文件失败 */printf("file %s open failed\r\n", argv[1]);return -1;}else {printf("file %s open successfully\r\n", argv[1]);}while(1){/* 读取按键状态 */ret = read(fd, &key_value, sizeof(key_value));if(ret < 0){printf("read failed\r\n");break;}/* 按键被按下 */if (key_value == 0){printf("Key value is 0\r\n");}else{printf("Key value is is is is is is %d\r\n", key_value);}if(1 == key_value){printf("ps_key1 press\r\n");led_value = !led_value;/* 用设备节点/dev/gpio_leds, 点亮led */fd_l = open("/dev/gpio_leds_our3", O_RDWR);if(fd_l < 0){printf("file /dev/gpio_leds_our3 open failed\r\n");break;}printf("led value is %d\r\n", led_value);ret = write(fd_l, &led_value, sizeof(led_value));if(ret < 0){printf("write failed\r\n");break;}ret = close(fd_l);if(ret < 0){printf("file /dev/gpio_leds_our3 close failed\r\n");break;}}}ret = close(fd);if(ret < 0){printf("file %s close failed\r\n", argv[1]);return -1;}return 0;
}
makefile是:
CC = arm-linux-gnueabihf-gcc
CFLAGS = -Wall -gall: axledkey_testaxledkey_test: main.o$(CC) $(CFLAGS) -o axledkey_test main.omain.o: main.c$(CC) $(CFLAGS) -c main.cclean:rm -f axledkey_test main.o
5.2.3 设备树
设备树如下:
/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;};alinxled {compatible = "alinxled";reg = <0xE000A284 0x04 /* gpio 方向寄存器 */0xE000A288 0x04 /* gpio 使能寄存器 */0xE000A048 0x04 /* gpio 控制寄存器 */0xF800012C 0x04 /* AMBA 外设时钟使能寄存器 */>;};alinxkey {compatible = "alinxkey";reg = <0xE000A284 0x04 /* gpio 方向寄存器 */0xE000A048 0x04 /* gpio 控制寄存器 */0xF800012C 0x04 /* AMBA 外设时钟使能寄存器 */>;};
};&usb0 {status = "okay";dr_mode = "host";usb-phy = <&usb_phy0>;
};&uart0 {
u-boot,dm-pre-reloc;
};&uart1 {
u-boot,dm-pre-reloc;
};
5.3 移植的结果
移植做了很多debug
,因此可以看到上述测试代码和key
驱动代码都设置了很多printk(t)
语句以确定哪里失败!
在开发板侧的打印结果如下:
root@proj_peta:~# ls
ax-key-dev.ko ax-led-devicetree.ko axleddev_test axledkey_test
root@proj_peta:~# chmod 777 -R axled*
root@proj_peta:~# insmod ax-led-devicetree.ko
ax_led_devicetree: loading out-of-tree module taints kernel.
root@proj_peta:~# insmod ax-key-dev.ko
init gpio_key successfully!
root@proj_peta:~# ./axleddev_test /dev/gpio_leds_our3 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/gpio_leds_our3 off
gpio_test module open
ps_led1 off
gpio_test module write
gpio_test OFF
gpio_test module release
root@proj_peta:~# ./axledkey_test /dev/gpio_key_our6
gpio_key module open
file /dev/gpio_key_our6 open successfully
extract_and_or_bits(GPIO_DATA_0) at start is : 1
Key value is 0
extract_and_or_bits(GPIO_DATA_0) at start is : 1
Key value is 0
extract_and_or_bits(GPIO_DATA_0) at start is : 1
Key value is 0
extract_and_or_bits(GPIO_DATA_0) at start is : 1
Key value is 0
extract_and_or_bits(GPIO_DATA_0) at start is : 1
mdelay is okay!
extract_and_or_bits(GPIO_DATA_0) at end is : 0
Key value is is is is is is 1
ps_key1 press
gpio_test module open
led value is 1
gpio_test module write
gpio_test ON
gpio_test module release
extract_and_or_bits(GPIO_DATA_0) at start is : 0
mdelay is okay!
extract_and_or_bits(GPIO_DATA_0) at end is : 0
Key value is is is is is is 1
ps_key1 press
gpio_test module open
led value is 0
gpio_test module write
gpio_test OFF
gpio_test module release
从上面的测试结果中不难看出,我使用了led
灯的测试结果去验证key
的测试结果。led
灯的测试结果有2种:
root@proj_peta:~# ./axleddev_test /dev/gpio_leds_our3 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/gpio_leds_our3 off
gpio_test module open
ps_led1 off
gpio_test module write
gpio_test OFF
gpio_test module release
以上都可以在./axledkey_test /dev/gpio_key_our6
和按键按下后输出中找到痕迹,说明实验成功了!
5.4 感悟
这次真的很耗费精力,查了很多次bug
!自我感觉如果可以通过printk
和printf
定位bug
的存在,那这样的bug
相对比较容易攻克。但,也总能碰到很难定位的bug
,比如我碰到的这个就是:
ax_led_devicetree: loading out-of-tree module taints kernel.
root@proj_peta:~# ./axleddev_test /dev/gpio_leds_our3 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/gpio_leds_our3 off
gpio_test module open
ps_led1 off
gpio_test module write
gpio_test OFF
gpio_test module release
root@proj_peta:~# rmmod ax-led-devicetree.ko
gpio_led_dev_exit_ok
root@proj_peta:~# ls
ax-key-dev.ko ax-led-devicetree.ko axleddev_test axledkey_test
root@proj_peta:~# chmod 777 -R axledkey_test
root@proj_peta:~# insmod ax-key-dev.ko
root@proj_peta:~#
在petalinux
平台上的一切行为都不报错,但是上板没有反应!
仔细排查后,仔细看之后发现ax-key-dev.c中:
/* 通过节点名称获取节点 */alinx_char.nd = of_find_node_by_name(NULL, "alinxled");
并未改成
/* 通过节点名称获取节点 */alinx_char.nd = of_find_node_by_name(NULL, "alinxkey");
很感慨软硬件调试难度还是挺大,调试需要考虑的因素远远超过软件!
六、定时器(移植成功)
知识部分可以参考alinx
的定时器,本节给出移植的难点和移植后的结果。
6.1 移植的难点
alinx
的定时器一节依旧沿用了gpio
子系统,因此需要格外注意alinx
教程中的:
gpio_set_value(alinx_char.alinx_led_gpio, alinx_char.led_status);
其含义就是根据alinx_led_gpio
节点设置led_status
,注意,该函数有2处和pinctrl
与gpio
子系统密切相关。
第一是alinx_led_gpio
节点,来自于pinctrl
,但是璞致的开发板不支持。
第二是gpio_set_value
函数,来自于gpio
子系统,但是璞致的开发板不支持。
因此目标就是自己写一段程序替换gpio_set_value
函数以实现相同的功能。我修改后的替代如下:
//gpio_set_value(alinx_char.alinx_led_gpio, alinx_char.led_status);if (1 == alinx_char.led_status) {*GPIO_DATA_0 |= 0x0000000F;printk("gpio_test ON\n");}else if (0 == alinx_char.led_status){*GPIO_DATA_0 &= 0xFFFFFFF0;printk("gpio_test OFF\n");} else{printk("gpio_test para err\n");}
6.2 移植的程序
设备树程序如下:
/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;};alinxled {compatible = "alinxled";reg = <0xE000A284 0x04 /* gpio 方向寄存器 */0xE000A288 0x04 /* gpio 使能寄存器 */0xE000A048 0x04 /* gpio 控制寄存器 */0xF800012C 0x04 /* AMBA 外设时钟使能寄存器 */>;};
};&usb0 {status = "okay";dr_mode = "host";usb-phy = <&usb_phy0>;
};&uart0 {
u-boot,dm-pre-reloc;
};&uart1 {
u-boot,dm-pre-reloc;
};
驱动模块如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/uaccess.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>
#include <asm/io.h>/* 设备节点名称 */
#define DEVICE_NAME "gpio_leds_our7"
/* 设备号个数 */
#define DEVID_COUNT 1
/* 驱动个数 */
#define DRIVE_COUNT 1
/* 主设备号 */
#define MAJOR_U
/* 次设备号 */
#define MINOR_U 0/* gpio 寄存器虚拟地址 */
static u32 *GPIO_DIRM_0;
/* gpio 使能寄存器 */
static u32 *GPIO_OEN_0;
/* gpio 控制寄存器 */
static u32 *GPIO_DATA_0;
/* AMBA 外设时钟使能寄存器 */
static u32 *APER_CLK_CTRL;/* 把驱动代码中会用到的数据打包进设备结构体 */
struct alinx_char_dev{dev_t devid; //设备号struct cdev cdev; //字符设备struct class *class; //类struct device *device; //设备struct device_node *nd; //设备树的设备节点//int alinx_led_gpio; //gpio号char led_status; //gpio状态unsigned int time_count; //定时器时间struct timer_list timer; //定时器
};
/* 声明设备结构体 */
static struct alinx_char_dev alinx_char = {.cdev = {.owner = THIS_MODULE,},
};void timer_function(struct timer_list *timer)
{/* 反转led状态 */alinx_char.led_status = !alinx_char.led_status;/* 设置led *///gpio_set_value(alinx_char.alinx_led_gpio, alinx_char.led_status);if (1 == alinx_char.led_status) {*GPIO_DATA_0 |= 0x0000000F;printk("gpio_test ON\n");}else if (0 == alinx_char.led_status){*GPIO_DATA_0 &= 0xFFFFFFF0;printk("gpio_test OFF\n");} else{printk("gpio_test para err\n");}/* 重新开始计时 */mod_timer(&alinx_char.timer, jiffies + msecs_to_jiffies(alinx_char.time_count));
}/* open函数实现, 对应到Linux系统调用函数的open函数 */
static int gpio_leds_open(struct inode *inode_p, struct file *file_p)
{/* 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);rst = copy_from_user(&alinx_char.time_count, buf, len);/* 设置好timer后先点亮led */alinx_char.led_status = 1;if(0 != rst){return -1;}//if(1 != len)//{// printk("gpio_test len err\n");// return -2;//}if(1 == alinx_char.led_status){*GPIO_DATA_0 |= 0x0000000F;printk("gpio_test ON\n");}else if(0 == alinx_char.led_status){*GPIO_DATA_0 &= 0xFFFFFFF0;printk("gpio_test OFF\n");}else{printk("gpio_test para err\n");return -3;}/* 开启timer */mod_timer(&alinx_char.timer, jiffies + msecs_to_jiffies(alinx_char.time_count));return 0;
}/* release函数实现, 对应到Linux系统调用函数的close函数 */
static int gpio_leds_release(struct inode *inode_p, struct file *file_p)
{printk("gpio_test module release\n");/* 删除定时器 */del_timer_sync(&alinx_char.timer);return 0;
}/* file_operations 结构体声明, 是上面 open、write 实现函数与系统调用函数对应的关键 */
static struct file_operations ax_char_fops = {.owner = THIS_MODULE,.open = gpio_leds_open,.write = gpio_leds_write,.release = gpio_leds_release,
};/* 模块加载时会调用的函数 */
static int __init gpio_led_init(void)
{/* 用于接受返回值 */u32 ret = 0;/* 存放 reg 数据的数组 */u32 reg_data[10];/* 通过节点名称获取节点 */alinx_char.nd = of_find_node_by_name(NULL, "alinxled");/* 4、获取 reg 属性内容 */ret = of_property_read_u32_array(alinx_char.nd, "reg", reg_data, 8);if(ret < 0){printk("get reg failed!\r\n");return -1;}else{/* do nothing */}/* 把需要修改的物理地址映射到虚拟地址 */GPIO_DIRM_0 = ioremap(reg_data[0], reg_data[1]);GPIO_OEN_0 = ioremap(reg_data[2], reg_data[3]);GPIO_DATA_0 = ioremap(reg_data[4], reg_data[5]);APER_CLK_CTRL = ioremap(reg_data[6], reg_data[7]);/* 注册设备号 */alloc_chrdev_region(&alinx_char.devid, MINOR_U, DEVID_COUNT, DEVICE_NAME);/* 初始化字符设备结构体 */cdev_init(&alinx_char.cdev, &ax_char_fops);/* 注册字符设备 */cdev_add(&alinx_char.cdev, alinx_char.devid, DRIVE_COUNT);/* 创建类 */alinx_char.class = class_create(THIS_MODULE, DEVICE_NAME);if(IS_ERR(alinx_char.class)){return PTR_ERR(alinx_char.class);}/* 创建设备节点 */alinx_char.device = device_create(alinx_char.class, NULL,alinx_char.devid, NULL,DEVICE_NAME);if (IS_ERR(alinx_char.device)){return PTR_ERR(alinx_char.device);}/* 初始化定时器 */__init_timer(&alinx_char.timer, timer_function, 0);return 0;
}/* 卸载模块 */
static void __exit gpio_led_exit(void)
{/* 注销字符设备 */cdev_del(&alinx_char.cdev);/* 注销设备号 */unregister_chrdev_region(alinx_char.devid, DEVID_COUNT);/* 删除设备节点 */device_destroy(alinx_char.class, alinx_char.devid);/* 删除类 */class_destroy(alinx_char.class);/* 释放对虚拟地址的占用 */iounmap(GPIO_DIRM_0);iounmap(GPIO_OEN_0);iounmap(GPIO_DATA_0);iounmap(APER_CLK_CTRL);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("DEVICE TREE GPIO LED driver");
MODULE_VERSION("v1.0");
MODULE_LICENSE("GPL");
驱动测试程序如下:
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"int main(int argc, char *argv[])
{int fd, ret;char *filename;unsigned int interval_new, interval_old = 0;/* 验证输入参数个数 */if(argc != 2){printf("Error Usage!\r\n");return -1;}/* 打开输入的设备文件, 获取文件句柄 */filename = argv[1];fd = open(filename, O_RDWR);if(fd < 0){/* 打开文件失败 */printf("can not open file %s\r\n", filename);return -1;}while(1){/* 等待输入led闪烁的时间间隔 */printf("Input interval:");scanf("%d", &interval_new);/* 时间间隔更新了 */if(interval_new != interval_old){interval_old = interval_new;/* 写入新的时间间隔 */ret = write(fd, &interval_new, sizeof(interval_new));if(ret < 0){printf("write failed\r\n");}else{printf("interval refreshed!\r\n");}}else{printf("same interval!");}}close(fd);
}
以上代码可以直接使用!
6.3 移植的结果
应该说并非一帆风顺,得到6.2
的代码还是历经bug
的!一个有意思的bug
如下:
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:01
Loaded 3813 entries from dependency cache.
Parsing recipes: 100% |#############################################################################################################################################################| Time: 0:00:08
Parsing of 2778 .bb files complete (2777 cached, 1 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:39
Sstate summary: Wanted 151 Found 16 Missed 270 Current 736 (10% match, 84% complete)
NOTE: Executing SetScene Tasks
NOTE: Executing RunQueue Tasks
ERROR: ax-timer-dev-1.0-r0 do_compile: oe_runmake failed
ERROR: ax-timer-dev-1.0-r0 do_compile: Function failed: do_compile (log file is located at /home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work/plnx_zynq7-xilinx-linux-gnueabi/ax-timer-dev/1.0-r0/temp/log.do_compile.70063)
ERROR: Logfile of failure stored in: /home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work/plnx_zynq7-xilinx-linux-gnueabi/ax-timer-dev/1.0-r0/temp/log.do_compile.70063
Log data follows:
| DEBUG: Executing shell function do_compile
| NOTE: make -j 2 KERNEL_SRC=/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work-shared/plnx-zynq7/kernel-source KERNEL_PATH=/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work-shared/plnx-zynq7/kernel-source KERNEL_VERSION=4.19.0-xilinx-v2019.1 CC=arm-xilinx-linux-gnueabi-gcc -mno-thumb-interwork -marm -fuse-ld=bfd -fdebug-prefix-map=/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work/plnx_zynq7-xilinx-linux-gnueabi/ax-timer-dev/1.0-r0=/usr/src/debug/ax-timer-dev/1.0-r0 -fdebug-prefix-map=/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work/plnx_zynq7-xilinx-linux-gnueabi/ax-timer-dev/1.0-r0/recipe-sysroot= -fdebug-prefix-map=/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work/plnx_zynq7-xilinx-linux-gnueabi/ax-timer-dev/1.0-r0/recipe-sysroot-native= -fdebug-prefix-map=/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work-shared/plnx-zynq7/kernel-source=/usr/src/kernel LD=arm-xilinx-linux-gnueabi-ld.bfd AR=arm-xilinx-linux-gnueabi-ar O=/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work-shared/plnx-zynq7/kernel-build-artifacts KBUILD_EXTRA_SYMBOLS=
| make -C /home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work-shared/plnx-zynq7/kernel-source M=/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work/plnx_zynq7-xilinx-linux-gnueabi/ax-timer-dev/1.0-r0
| make[1]: Entering directory '/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work-shared/plnx-zynq7/kernel-source'
| make[2]: Entering directory '/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work-shared/plnx-zynq7/kernel-build-artifacts'
| CC [M] /home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work/plnx_zynq7-xilinx-linux-gnueabi/ax-timer-dev/1.0-r0/ax-timer-dev.o
| /home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work/plnx_zynq7-xilinx-linux-gnueabi/ax-timer-dev/1.0-r0/ax-timer-dev.c: In function 'gpio_leds_write':
| /home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work/plnx_zynq7-xilinx-linux-gnueabi/ax-timer-dev/1.0-r0/ax-timer-dev.c:110:11: error: implicit declaration of function 'copy_from_user'; did you mean 'raw_copy_from_user'? [-Werror=implicit-function-declaration]
| rst = copy_from_user(&alinx_char.time_count, buf, len);
| ^~~~~~~~~~~~~~
| raw_copy_from_user
| cc1: some warnings being treated as errors
| ERROR: oe_runmake failed
| make[3]: *** [/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work-shared/plnx-zynq7/kernel-source/scripts/Makefile.build:312: /home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work/plnx_zynq7-xilinx-linux-gnueabi/ax-timer-dev/1.0-r0/ax-timer-dev.o] Error 1
| make[2]: *** [/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work-shared/plnx-zynq7/kernel-source/Makefile:1517: _module_/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work/plnx_zynq7-xilinx-linux-gnueabi/ax-timer-dev/1.0-r0] Error 2
| make[2]: Leaving directory '/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work-shared/plnx-zynq7/kernel-build-artifacts'
| make[1]: *** [Makefile:146: sub-make] Error 2
| make[1]: Leaving directory '/home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work-shared/plnx-zynq7/kernel-source'
| make: *** [Makefile:6: all] Error 2
| WARNING: exit code 1 from a shell command.
| ERROR: Function failed: do_compile (log file is located at /home/dention/petalinux_proj/test_peta/proj_peta/build/tmp/work/plnx_zynq7-xilinx-linux-gnueabi/ax-timer-dev/1.0-r0/temp/log.do_compile.70063)
ERROR: Task (/home/dention/petalinux_proj/test_peta/proj_peta/project-spec/meta-user/recipes-modules/ax-timer-dev/ax-timer-dev.bb:do_compile) failed with exit code '1'
NOTE: Tasks Summary: Attempted 3153 tasks of which 2851 didn't need to be rerun and 1 failed.Summary: 1 task failed:/home/dention/petalinux_proj/test_peta/proj_peta/project-spec/meta-user/recipes-modules/ax-timer-dev/ax-timer-dev.bb:do_compile
Summary: There were 2 ERROR messages shown, returning a non-zero exit code.
ERROR: Failed to build project
错误信息显示implicit declaration of function 'copy_from_user'; did you mean 'raw_copy_from_user'? [-Werror=implicit-function-declaration]
表示copy_from_user
函数在使用前没有声明。这是因为缺少了必要的头文件包含。copy_from_user
函数是在<linux/uaccess.h>
头文件中声明的。
有点惊讶!因为之前使用<asm/uaccess.h>
可以编译成功!但还是添加了<linux/uaccess.h>
。
那在6.2
的程序下最终顺利运行,终端输出如下:
root@proj_peta:~# ls
ax-timer-dev.ko axledtimer_test
root@proj_peta:~# chmod 777 -R axledtimer_test
root@proj_peta:~# insmod ax-timer-dev.ko
ax_timer_dev: loading out-of-tree module taints kernel.
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
226 drm
243 gpio_leds_our7
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
root@proj_peta:~# cd /proc/device-tree
root@proj_peta:/proc/device-tree# ls
#address-cells alinxkey chosen fixedregulator model pmu@f8891000
#size-cells alinxled compatible fpga-full name
aliases amba cpus memory phy0@e0002000
root@proj_peta:/proc/device-tree# ls
#address-cells alinxkey chosen fixedregulator model pmu@f8891000
#size-cells alinxled compatible fpga-full name
aliases amba cpus memory phy0@e0002000
root@proj_peta:/proc/device-tree# cd ~
root@proj_peta:~# cd /dev/
root@proj_peta:/dev# ls
block mmcblk0p2 ram10 tty13 tty37 tty60
char mmcblk1 ram11 tty14 tty38 tty61
console mmcblk1boot0 ram12 tty15 tty39 tty62
cpu_dma_latency mmcblk1boot1 ram13 tty16 tty4 tty63
disk mmcblk1rpmb ram14 tty17 tty40 tty7
fd mtab ram15 tty18 tty41 tty8
full mtd0 ram2 tty19 tty42 tty9
gpio_leds_our7 mtd0ro ram3 tty2 tty43 ttyPS0
gpiochip0 mtd1 ram4 tty20 tty44 ttyPS1
iio:device0 mtd1ro ram5 tty21 tty45 udev_network_queue
initctl mtd2 ram6 tty22 tty46 urandom
kmsg mtd2ro ram7 tty23 tty47 vcs
log mtd3 ram8 tty24 tty48 vcs1
loop-control mtd3ro ram9 tty25 tty49 vcsa
loop0 mtdblock0 random tty26 tty5 vcsa1
loop1 mtdblock1 shm tty27 tty50 vcsu
loop2 mtdblock2 snd tty28 tty51 vcsu1
loop3 mtdblock3 stderr tty29 tty52 vga_arbiter
loop4 network_latency stdin tty3 tty53 watchdog
loop5 network_throughput stdout tty30 tty54 watchdog0
loop6 null tty tty31 tty55 zero
loop7 port tty0 tty32 tty56
mem ptmx tty1 tty33 tty57
memory_bandwidth pts tty10 tty34 tty58
mmcblk0 ram0 tty11 tty35 tty59
mmcblk0p1 ram1 tty12 tty36 tty6
root@proj_peta:/dev# cd ~
root@proj_peta:~# ls
ax-timer-dev.ko axledtimer_test
root@proj_peta:~# ./axledtimer_test /dev/gpio_leds_our7
gpio_test module open
Input interval:3
gpio_test module write
gpio_test ON
interval refreshed!
Input interval:gpio_test OFF
gpio_test ON
gpio_test OFF
gpio_test ON
gpio_test OFF
gpio_test ON
gpio_test OFF
结果就是4盏灯闪烁。
七、中断(单独放一篇)
这个移植替换难度比较大,已经尝试了一部分,知道移植思路。完成基于gpio子系统
的中断号获取函数(gpio_to_irq
)的替代较为容易,不容易的是设备树的中断语法编写可能牵扯甚多。
骤然意识到需要花费时间学习设备树的中断语法和大量学习样例。
因此,还是决定单独写一篇。
另外,后续的阻塞
、非阻塞
、异步IO
、input子系统
都涉及gpio_to_irq
函数,因此后续这几章将会放到一起!
总结
本文对alinx
中的gpio输入
和定时器
两部分进行移植,花费精力和时间巨大,但得到的经验也是相当多的!另外,也对中断部分进行了移植尝试,但为实现移植思路,需要花费大量时间去学习设备树的中断语法和学习样例,因此关于中断
的移植打算单独写一篇。