做网站买什么服务器,潜江资讯网招聘信息最新,网站做成响应式的有什么弊端,百度查重若该文为原创文章#xff0c;转载请注明原文出处。 记录使用设备树编写一个简单的 LED 灯驱动程序 一、编程思路
程序编写的主要内容为添加 LED 灯的设备树节点、在驱动程序中使用 of 函数获取设备节点中的 属性#xff0c;编写测试应用程序。
• 首先向设备树添加 LED 设备…若该文为原创文章转载请注明原文出处。 记录使用设备树编写一个简单的 LED 灯驱动程序 一、编程思路
程序编写的主要内容为添加 LED 灯的设备树节点、在驱动程序中使用 of 函数获取设备节点中的 属性编写测试应用程序。
• 首先向设备树添加 LED 设备节点。
• 其次编写平台设备驱动框架主要包驱动入口函数、驱动注销函数、平台设备结构体定义 三部分内容。
• 实现.probe 函数对 LED 进行设备注册和初始化。
• 实现字符设备操作函数集这里主要实现.write 操作。
• 编写测试应用程序对于输入不同的值控制 LED 亮灭
二、编写设备树
RK3568支持设备树设备树相关知识执行了解。
设备树目录在arch/arm64/boot/dts/rockchip下。
修改 rk3568-atk-evb1-ddr4-v10.dtsi文件在根目录 /下建立一个节点
led_test {compatibleyifeng,led_test;statusokay;reg 0x0 0xFDC20010 0x0 0x08 /* PMU_GRF_GPIO0C_IOMUX_L */ 0x0 0xFDC20090 0x0 0x08 /* PMU_GRF_GPIO0C_DS_0 */ 0x0 0xFDD60004 0x0 0x08 /* GPIO0_SWPORT_DR_H */ 0x0 0xFDD6000C 0x0 0x08 ; /* GPIO0_SWPORT_DDR_H */};
reg里的值参考LED驱动开发比如“0x0 0xFDC20010 0x0 0x08”表示 RK3568 的 PMU_GRF_GPIO0C_IOMUX_L 寄存器其中寄存器地址为 0xFDC20010长度为 8 个字节。
修改如下 设备树修改完成以后在 SDK 顶层目录输入如下命令重新编译一下内核
# 指定 SDK 的板级配置文件
./build.sh lunch
# 编译内核
./build.sh kernel 编译完成以后得到 boot.img, boot.img 就是编译出来的内核设备树打包在一起的文件 只需要重新烧写boot.img。 烧写完成以后启动开发板。Linux 启动成功以后进入到/proc/device-tree/目录中查看是否有 “led_test”这个节点. 三、编写驱动
led_test.c
#include linux/types.h
#include linux/kernel.h
#include linux/delay.h
#include linux/ide.h
#include linux/init.h
#include linux/module.h
#include linux/errno.h
#include linux/gpio.h
#include linux/cdev.h
#include linux/device.h
#include linux/of.h
#include linux/of_address.h
//#include asm/mach/map.h
#include asm/uaccess.h
#include asm/io.h#define DTSLED_CNT 1 /* 设备号个数 */
#define DTSLED_NAME led_test /* 名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 *//* 映射后的寄存器虚拟地址指针 */
static void __iomem *PMU_GRF_GPIO0C_IOMUX_L_PI;
static void __iomem *PMU_GRF_GPIO0C_DS_0_PI;
static void __iomem *GPIO0_SWPORT_DR_H_PI;
static void __iomem *GPIO0_SWPORT_DDR_H_PI;/* dtsled设备结构体 */
struct dtsled_dev{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */int minor; /* 次设备号 */struct device_node *nd; /* 设备节点 */
};struct dtsled_dev dtsled; /* led设备 *//** description : LED打开/关闭* param - sta : LEDON(0) 打开LEDLEDOFF(1) 关闭LED* return : 无*/
void led_switch(u8 sta)
{u32 val 0;if(sta LEDON) {val readl(GPIO0_SWPORT_DR_H_PI);val ~(0X1 0); /* bit0 清零*/val | ((0X1 16) | (0X1 0)); /* bit16 置1允许写bit0bit0高电平*/writel(val, GPIO0_SWPORT_DR_H_PI);}else if(sta LEDOFF) { val readl(GPIO0_SWPORT_DR_H_PI);val ~(0X1 0); /* bit0 清零*/val | ((0X1 16) | (0X0 0)); /* bit16 置1允许写bit0bit0低电平 */writel(val, GPIO0_SWPORT_DR_H_PI);}
}/** description : 取消映射* return : 无*/
void led_unmap(void)
{/* 取消映射 */iounmap(PMU_GRF_GPIO0C_IOMUX_L_PI);iounmap(PMU_GRF_GPIO0C_DS_0_PI);iounmap(GPIO0_SWPORT_DR_H_PI);iounmap(GPIO0_SWPORT_DDR_H_PI);
}/** description : 打开设备* param - inode : 传递给驱动的inode* param - filp : 设备文件file结构体有个叫做private_data的成员变量* 一般在open的时候将private_data指向设备结构体。* return : 0 成功;其他 失败*/
static int led_open(struct inode *inode, struct file *filp)
{filp-private_data dtsled; /* 设置私有数据 */return 0;
}/** description : 从设备读取数据 * param - filp : 要打开的设备文件(文件描述符)* param - buf : 返回给用户空间的数据缓冲区* param - cnt : 要读取的数据长度* param - offt : 相对于文件首地址的偏移* return : 读取的字节数如果为负值表示读取失败*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}/** description : 向设备写数据 * param - filp : 设备文件表示打开的文件描述符* param - buf : 要写给设备写入的数据* param - cnt : 要写入的数据长度* param - offt : 相对于文件首地址的偏移* return : 写入的字节数如果为负值表示写入失败*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[1];unsigned char ledstat;retvalue copy_from_user(databuf, buf, cnt);if(retvalue 0) {printk(kernel write failed!\r\n);return -EFAULT;}ledstat databuf[0]; /* 获取状态值 */if(ledstat LEDON) { led_switch(LEDON); /* 打开LED灯 */} else if(ledstat LEDOFF) {led_switch(LEDOFF); /* 关闭LED灯 */}return 0;
}/** description : 关闭/释放设备* param - filp : 要关闭的设备文件(文件描述符)* return : 0 成功;其他 失败*/
static int led_release(struct inode *inode, struct file *filp)
{return 0;
}/* 设备操作函数 */
static struct file_operations dtsled_fops {.owner THIS_MODULE,.open led_open,.read led_read,.write led_write,.release led_release,
};/** description : 驱动出口函数* param : 无* return : 无*/
static int __init led_init(void)
{u32 val 0;int ret;u32 regdata[16];const char *str;struct property *proper;/* 获取设备树中的属性数据 *//* 1、获取设备节点led_test */dtsled.nd of_find_node_by_path(/led_test);if(dtsled.nd NULL) {printk(led_test node not find!\r\n);goto fail_find_node;} else {printk(led_test node find!\r\n);}/* 2、获取compatible属性内容 */proper of_find_property(dtsled.nd, compatible, NULL);if(proper NULL) {printk(compatible property find failed\r\n);} else {printk(compatible %s\r\n, (char*)proper-value);}/* 3、获取status属性内容 */ret of_property_read_string(dtsled.nd, status, str);if(ret 0){printk(status read failed!\r\n);} else {printk(status %s\r\n,str);}/* 4、获取reg属性内容 */ret of_property_read_u32_array(dtsled.nd, reg, regdata, 16);if(ret 0) {printk(reg property read failed!\r\n);} else {u8 i 0;printk(reg data:\r\n);for(i 0; i 16; i)printk(%#X , regdata[i]);printk(\r\n);}/* 初始化LED *//* 1、寄存器地址映射 */PMU_GRF_GPIO0C_IOMUX_L_PI of_iomap(dtsled.nd, 0);PMU_GRF_GPIO0C_DS_0_PI of_iomap(dtsled.nd, 1);GPIO0_SWPORT_DR_H_PI of_iomap(dtsled.nd, 2);GPIO0_SWPORT_DDR_H_PI of_iomap(dtsled.nd, 3);/* 2、设置GPIO0_C0为GPIO功能。*/val readl(PMU_GRF_GPIO0C_IOMUX_L_PI);val ~(0X7 0); /* bit2:0清零 */val | ((0X7 16) | (0X0 0)); /* bit18:16 置1允许写bit2:0bit2:00用作GPIO0_C0 */writel(val, PMU_GRF_GPIO0C_IOMUX_L_PI);/* 3、设置GPIO0_C0驱动能力为level5 */val readl(PMU_GRF_GPIO0C_DS_0_PI);val ~(0X3F 0); /* bit5:0清零*/val | ((0X3F 16) | (0X3F 0)); /* bit21:16 置1允许写bit5:0bit5:00用作GPIO0_C0 */writel(val, PMU_GRF_GPIO0C_DS_0_PI);/* 4、设置GPIO0_C0为输出 */val readl(GPIO0_SWPORT_DDR_H_PI);val ~(0X1 0); /* bit0 清零*/val | ((0X1 16) | (0X1 0)); /* bit16 置1允许写bit0bit0高电平 */writel(val, GPIO0_SWPORT_DDR_H_PI);/* 5、设置GPIO0_C0为低电平关闭LED灯。*/val readl(GPIO0_SWPORT_DR_H_PI);val ~(0X1 0); /* bit0 清零*/val | ((0X1 16) | (0X0 0)); /* bit16 置1允许写bit0bit0低电平 */writel(val, GPIO0_SWPORT_DR_H_PI);/* 注册字符设备驱动 *//* 1、创建设备号 */if (dtsled.major) { /* 定义了设备号 */dtsled.devid MKDEV(dtsled.major, 0);ret register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);if(ret 0) {pr_err(cannot register %s char driver [ret%d]\n,DTSLED_NAME, DTSLED_CNT);goto fail_devid;}} else { /* 没有定义设备号 */ret alloc_chrdev_region(dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME); /* 申请设备号 */if(ret 0) {pr_err(%s Couldnt alloc_chrdev_region, ret%d\r\n, DTSLED_NAME, ret);goto fail_devid;}dtsled.major MAJOR(dtsled.devid); /* 获取分配号的主设备号 */dtsled.minor MINOR(dtsled.devid); /* 获取分配号的次设备号 */}printk(dtsled major%d,minor%d\r\n,dtsled.major, dtsled.minor); /* 2、初始化cdev */dtsled.cdev.owner THIS_MODULE;cdev_init(dtsled.cdev, dtsled_fops);/* 3、添加一个cdev */ret cdev_add(dtsled.cdev, dtsled.devid, DTSLED_CNT);if(ret 0)goto del_unregister;/* 4、创建类 */dtsled.class class_create(THIS_MODULE, DTSLED_NAME);if (IS_ERR(dtsled.class)) {goto del_cdev;}/* 5、创建设备 */dtsled.device device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);if (IS_ERR(dtsled.device)) {goto destroy_class;}return 0;destroy_class:class_destroy(dtsled.class);
del_cdev:cdev_del(dtsled.cdev);
del_unregister:unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
fail_devid:led_unmap();
fail_find_node:return -EIO;
}/** description : 驱动出口函数* param : 无* return : 无*/
static void __exit led_exit(void)
{/* 取消映射 */led_unmap();/* 注销字符设备驱动 */cdev_del(dtsled.cdev);/* 删除cdev */unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /* 注销设备号 */device_destroy(dtsled.class, dtsled.devid);class_destroy(dtsled.class);
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE(GPL);
MODULE_AUTHOR(ALIENTEK);
MODULE_INFO(intree, Y);
makefile
KERNELDIR : /home/alientek/rk3568_linux_sdk/kernel
ARCHarm64
CROSS_COMPILE/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu-export ARCH CROSS_COMPILECURRENT_PATH : $(shell pwd)
obj-m : led_test.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M$(CURRENT_PATH) clean四、编写应用
test_app.c
#include stdio.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include stdlib.h
#include string.h#define LEDOFF 0
#define LEDON 1/** description : main主程序* param - argc : argv数组元素个数* param - argv : 具体参数* return : 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd, retvalue;char *filename;unsigned char databuf[1];if(argc ! 3){printf(Error Usage!\r\n);return -1;}filename argv[1];/* 打开led驱动 */fd open(filename, O_RDWR);if(fd 0){printf(file %s open failed!\r\n, argv[1]);return -1;}databuf[0] atoi(argv[2]); /* 要执行的操作打开或关闭 *//* 向/dev/led文件写入数据 */retvalue write(fd, databuf, sizeof(databuf));if(retvalue 0){printf(LED Control Failed!\r\n);close(fd);return -1;}retvalue close(fd); /* 关闭文件 */if(retvalue 0){printf(file %s close failed!\r\n, argv[1]);return -1;}return 0;
}
编译
/opt/atk-dlrk356x-toolchain/bin/aarch64-buildroot-linux-gnu-gcc ledApp.c -o ledApp 五、测试
1、关闭 LED 的心跳灯
echo none /sys/class/leds/work/trigger
2、加载和卸载驱动模块
depmod //第一次加载驱动的时候需要运行此命令
modprobe dtsled //加载驱动或
insmod led_test.ko卸载
rmmod led_test
3、测试 ./ledApp /dev/led_test 1 //打开 LED 灯 ./ledApp /dev/led_test 0 // 关闭 LED 灯 如有侵权或需要完整代码请及时联系博主。