news 2026/5/5 19:22:18

点亮 LED 灯实验(平台总线)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
点亮 LED 灯实验(平台总线)

1. platform_device.c - 平台设备定义

#include <linux/module.h> #include <linux/platform_device.h> #include <linux/io.h> #define DRIVER_NAME "simple_led" // LED资源:寄存器地址和大小 #define PMU_GRF_BASE (0xFDC20000) #define PMU_GRF_GPIO0C_IOMUX_L (PMU_GRF_BASE + 0x0010) #define PMU_GRF_GPIO0C_DS_0 (PMU_GRF_BASE + 0x0090) #define GPIO0_BASE (0xFDD60000) #define GPIO0_SWPORT_DR_H (GPIO0_BASE + 0x0004) #define GPIO0_SWPORT_DDR_H (GPIO0_BASE + 0x000C) // 定义LED设备资源(寄存器内存区域) static struct resource led_resources[] = { [0] = { .start = PMU_GRF_GPIO0C_IOMUX_L, .end = PMU_GRF_GPIO0C_IOMUX_L + 0x3, // 4字节 .flags = IORESOURCE_MEM, .name = "pmu_iomux", }, [1] = { .start = PMU_GRF_GPIO0C_DS_0, .end = PMU_GRF_GPIO0C_DS_0 + 0x3, .flags = IORESOURCE_MEM, .name = "pmu_ds", }, [2] = { .start = GPIO0_SWPORT_DR_H, .end = GPIO0_SWPORT_DR_H + 0x3, .flags = IORESOURCE_MEM, .name = "gpio_dr", }, [3] = { .start = GPIO0_SWPORT_DDR_H, .end = GPIO0_SWPORT_DDR_H + 0x3, .flags = IORESOURCE_MEM, .name = "gpio_ddr", }, }; // 平台设备私有数据(可选) struct led_platform_data { const char *name; int default_state; // 0=off, 1=on }; static struct led_platform_data led_pdata = { .name = "rk3568_led", .default_state = 0, // 默认关闭 }; // 平台设备定义 static struct platform_device led_platform_device = { .name = DRIVER_NAME, .id = -1, // 只有一个设备 .dev = { .platform_data = &led_pdata, // 传递私有数据 }, .resource = led_resources, .num_resources = ARRAY_SIZE(led_resources), }; // 模块初始化:注册平台设备 static int __init led_device_init(void) { int ret; ret = platform_device_register(&led_platform_device); if (ret) { printk(KERN_ERR "Failed to register LED platform device\n"); return ret; } printk(KERN_INFO "LED platform device registered successfully\n"); return 0; } // 模块退出:注销平台设备 static void __exit led_device_exit(void) { platform_device_unregister(&led_platform_device); printk(KERN_INFO "LED platform device unregistered\n"); } module_init(led_device_init); module_exit(led_device_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("RK3568 LED Platform Device");

2. platform_driver.c - 平台驱动实现

#include <linux/module.h> #include <linux/platform_device.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/uaccess.h> #include <linux/io.h> #include <linux/slab.h> #define DRIVER_NAME "simple_led" #define LED_ON_VAL 0x00010001 #define LED_OFF_VAL 0x00010000 #define DEVICE_NAME "simple_led" // 设备私有结构体 struct led_private { struct cdev cdev; dev_t dev_num; struct class *class; struct device *device; void __iomem *vir_pmu_iomux; void __iomem *vir_pmu_ds; void __iomem *vir_gpio_dr; void __iomem *vir_gpio_ddr; char kbuf; struct platform_device *pdev; }; // 文件操作函数 static int led_open(struct inode *inode, struct file *filp) { struct led_private *priv = container_of(inode->i_cdev, struct led_private, cdev); filp->private_data = priv; printk(KERN_INFO "LED device opened\n"); return 0; } static ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { struct led_private *priv = (struct led_private *)filp->private_data; int ret; if (count != 1) { return -EINVAL; } ret = copy_from_user(&priv->kbuf, buf, 1); if (ret) { return -EFAULT; } if (priv->kbuf == '1') { writel(LED_ON_VAL, priv->vir_gpio_dr); printk(KERN_INFO "LED ON (reg: 0x%x)\n", LED_ON_VAL); } else if (priv->kbuf == '0') { writel(LED_OFF_VAL, priv->vir_gpio_dr); printk(KERN_INFO "LED OFF (reg: 0x%x)\n", LED_OFF_VAL); } else { return -EINVAL; } return 1; } static int led_release(struct inode *inode, struct file *filp) { printk(KERN_INFO "LED device released\n"); return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .write = led_write, .release = led_release, }; // 平台驱动的probe函数 static int led_probe(struct platform_device *pdev) { struct led_private *priv; struct resource *res; int ret; u32 val; // 分配设备私有结构体 priv = devm_kzalloc(&pdev->dev, sizeof(struct led_private), GFP_KERNEL); if (!priv) { return -ENOMEM; } priv->pdev = pdev; platform_set_drvdata(pdev, priv); // 获取并映射寄存器资源 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "Failed to get PMU IOMUX resource\n"); return -ENODEV; } priv->vir_pmu_iomux = devm_ioremap(&pdev->dev, res->start, resource_size(res)); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); priv->vir_pmu_ds = devm_ioremap(&pdev->dev, res->start, resource_size(res)); res = platform_get_resource(pdev, IORESOURCE_MEM, 2); priv->vir_gpio_dr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); res = platform_get_resource(pdev, IORESOURCE_MEM, 3); priv->vir_gpio_ddr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!priv->vir_pmu_iomux || !priv->vir_pmu_ds || !priv->vir_gpio_dr || !priv->vir_gpio_ddr) { dev_err(&pdev->dev, "Failed to ioremap resources\n"); return -ENOMEM; } // 初始化硬件 // 1. 配置GPIO0_C0为GPIO功能 val = 0x00070000; writel(val, priv->vir_pmu_iomux); // 2. 配置驱动能力 val = 0x003F003F; writel(val, priv->vir_pmu_ds); // 3. 配置为输出模式 val = 0x00010001; writel(val, priv->vir_gpio_ddr); // 4. 默认关闭LED writel(LED_OFF_VAL, priv->vir_gpio_dr); // 注册字符设备 ret = alloc_chrdev_region(&priv->dev_num, 0, 1, DEVICE_NAME); if (ret < 0) { dev_err(&pdev->dev, "Failed to allocate device number\n"); return ret; } cdev_init(&priv->cdev, &led_fops); priv->cdev.owner = THIS_MODULE; ret = cdev_add(&priv->cdev, priv->dev_num, 1); if (ret) { dev_err(&pdev->dev, "Failed to add cdev\n"); goto err_cdev; } // 创建设备节点 priv->class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(priv->class)) { ret = PTR_ERR(priv->class); goto err_class; } priv->device = device_create(priv->class, NULL, priv->dev_num, NULL, DEVICE_NAME); if (IS_ERR(priv->device)) { ret = PTR_ERR(priv->device); goto err_device; } dev_info(&pdev->dev, "LED driver probed successfully (major: %d)\n", MAJOR(priv->dev_num)); return 0; err_device: class_destroy(priv->class); err_class: cdev_del(&priv->cdev); err_cdev: unregister_chrdev_region(priv->dev_num, 1); return ret; } // 平台驱动的remove函数 static int led_remove(struct platform_device *pdev) { struct led_private *priv = platform_get_drvdata(pdev); if (priv) { // 删除设备节点 device_destroy(priv->class, priv->dev_num); class_destroy(priv->class); // 注销字符设备 cdev_del(&priv->cdev); unregister_chrdev_region(priv->dev_num, 1); // 注意:使用devm_ioremap的资源会自动释放,无需手动iounmap dev_info(&pdev->dev, "LED driver removed\n"); } return 0; } // 平台驱动结构体 static struct platform_driver led_platform_driver = { .probe = led_probe, .remove = led_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, }, }; // 模块初始化 static int __init led_driver_init(void) { int ret; ret = platform_driver_register(&led_platform_driver); if (ret) { printk(KERN_ERR "Failed to register LED platform driver\n"); return ret; } printk(KERN_INFO "LED platform driver registered\n"); return 0; } // 模块退出 static void __exit led_driver_exit(void) { platform_driver_unregister(&led_platform_driver); printk(KERN_INFO "LED platform driver unregistered\n"); } module_init(led_driver_init); module_exit(led_driver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("RK3568 LED Platform Driver"); MODULE_ALIAS("platform:simple_led");

3. test_led.c - 测试应用程序

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> int main(int argc, char *argv[]) { int fd; char buf[2]; if (argc != 2) { printf("Usage: %s <0|1>\n", argv[0]); printf(" 0: turn LED off\n"); printf(" 1: turn LED on\n"); return -1; } // 打开设备 fd = open("/dev/simple_led", O_WRONLY); if (fd < 0) { perror("Failed to open device"); return -1; } // 写入控制命令 buf[0] = argv[1][0]; buf[1] = '\0'; if (write(fd, buf, 1) < 0) { perror("Failed to write to device"); close(fd); return -1; } printf("LED %s\n", (buf[0] == '1') ? "ON" : "OFF"); close(fd); return 0; }
# 1. 加载平台设备模块 insmod led_device.ko # 2. 加载平台驱动模块(会自动匹配设备并调用probe函数) insmod led_driver.ko # 3. 检查设备是否创建 ls -l /dev/simple_led # 4. 测试LED控制 ./test_led 1 # 打开LED ./test_led 0 # 关闭LED # 5. 查看内核日志 dmesg | tail # 6. 卸载模块 rmmod led_driver rmmod led_device

加载platform_device.ko阻塞 待解决

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/4 11:21:43

全网首发!万字硬核解析:如何用向量引擎API构建企业级GPT-5.2与Sora2双模态中台(附完整源码+架构图)

摘要本文不仅仅是一篇教程。 更是一次对当下AI开发模式的深度反思与重构。 随着GPT-5.2、Sora2、Veo3等核弹级模型的相继发布。 传统的“单点直连”开发模式已经彻底崩塌。 面对高昂的维护成本、复杂的网络环境以及碎片化的接口标准。 我们迫切需要一种新的架构思想。 本文将通…

作者头像 李华
网站建设 2026/5/4 11:22:29

冥想第一千七百七十七天(1777)

1.周三&#xff0c;今天还是特别忙&#xff0c;中午忙工作休息的时间特别短。天气比前两天好多了。 2.感谢父母&#xff0c;感谢朋友&#xff0c;感谢家人&#xff0c;感谢不断进步的自己。

作者头像 李华
网站建设 2026/5/4 13:37:11

状态观测器深入浅出:从“为何需要”到“如何设计”

今天补充一下观测器的理论知识。 引言:我们为何需要“猜测”系统内部? 在理想的控制系统设计中,我们通常假设所有需要的状态变量(比如位置、速度、温度、化学浓度等)都可以通过传感器直接、精确地测量。然而,现实很骨感,很多时候我们无法直接获取这些关键状态: 物理限…

作者头像 李华
网站建设 2026/5/1 10:01:56

Flutter 三端应用实战:OpenHarmony 简易文本首尾字符对比器开发指南

一、为什么需要“简易文本首尾字符对比器”&#xff1f; 在 OpenHarmony 的文本校验、格式验证与结构分析场景中&#xff0c;同时观察首尾字符能提供独特的上下文洞察&#xff1a; 程序员&#xff1a;检查字符串是否被正确包裹&#xff08;如 "..."、...、[...]、{..…

作者头像 李华
网站建设 2026/5/5 17:58:26

CMake工程指南(一):基础概念与核心优势

引言 在现代C/C开发中&#xff0c;构建系统是项目管理的核心。传统的构建方式往往面临跨平台兼容性差、配置复杂等问题。本教程将带你深入了解CMake——这个C/C开发的事实标准构建工具。 什么是构建系统&#xff1f; 构建&#xff08;Build&#xff09; 编译&#xff08;Compil…

作者头像 李华
网站建设 2026/5/4 12:48:03

【算法分享】R树索引

在车联网、POI 分析、地理围栏、行政区划判断等业务中&#xff0c;我们经常会遇到一个非常基础、但极其高频的问题&#xff1a; &#x1f449; 给定一个经纬度点&#xff0c;它属于哪个&#xff08;或哪些&#xff09;多边形&#xff1f; 比如&#xff1a; 一个 GPS 点属于哪个…

作者头像 李华