当前位置: 首页 > news >正文

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

文章目录

  • 前言
  • 零、驱动和操作系统镜像编译流程
    • 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.ubBOOT.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.BINimage.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.BINimage.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!自我感觉如果可以通过printkprintf定位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处和pinctrlgpio子系统密切相关。

第一是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)的替代较为容易,不容易的是设备树的中断语法编写可能牵扯甚多。

骤然意识到需要花费时间学习设备树的中断语法和大量学习样例。

因此,还是决定单独写一篇。

另外,后续的阻塞非阻塞异步IOinput子系统都涉及gpio_to_irq函数,因此后续这几章将会放到一起!


总结

本文对alinx中的gpio输入定时器两部分进行移植,花费精力和时间巨大,但得到的经验也是相当多的!另外,也对中断部分进行了移植尝试,但为实现移植思路,需要花费大量时间去学习设备树的中断语法和学习样例,因此关于中断的移植打算单独写一篇。

http://www.dtcms.com/a/267420.html

相关文章:

  • Flink-Source算子点位提交问题(Earliest)
  • 力扣 hot100 Day35
  • STM32中实现shell控制台(命令解析实现)
  • MySQL回表查询深度解析:原理、影响与优化实战
  • 从UI设计到数字孪生实战部署:构建智慧城市的智慧照明系统
  • 【项目笔记】高并发内存池项目剖析(三)
  • NX二次开发——NX二次开发-检查点是否在面上或者体上
  • MPLS 多协议标签交换
  • Python实例题:基于 Python 的简单聊天机器人
  • springsecurity5配置之后启动项目报错:authenticationManager cannot be null
  • LangChain4j 框架模仿豆包实现智能对话系统:架构与功能详解
  • windows 安装 wsl
  • 基于matlab卡尔曼滤波器消除噪声
  • 点击方块挑战小游戏流量主微信小程序开源
  • Java+Vue开发的进销存ERP系统,集采购、销售、库存管理,助力企业数字化运营
  • 浏览器与服务器的交互
  • 深度学习图像分类数据集—百种鸟类识别分类
  • STM32中实现shell控制台(shell窗口输入实现)
  • 结构型智能科技的关键可行性——信息型智能向结构型智能的转变(修改提纲)
  • rk3128 emmc显示剩余容量为0
  • kubectl exec 遇到 unable to upgrade connection Forbidden 的解决办法
  • 浅度解读-(未完成版)浅层神经网络-多个隐层神经元
  • 解决el-select数据类型相同但是显示数字的问题
  • Python-函数、参数及参数解构-返回值作用域-递归函数-匿名函数-生成器-学习笔记
  • 从数据洞察到设计创新:UI前端如何利用数字孪生提升用户体验?
  • 【算法笔记】4.LeetCode-Hot100-数组专项
  • 操作系统---I/O核心子系统与磁盘
  • Linux操作系统之文件(四):文件系统(上)
  • pyspark大规模数据加解密优化实践
  • NVMe高速传输之摆脱XDMA设计13:PCIe初始化状态机设计