韦东山的led硬件和驱动分类框架

  • Makefile
  • led_opr.h抽象出了led的操作
  • board_demo.c单板资源文件提供了实现
    • demo的具体实现
  • leddrv.c设备驱动文件
  • ledtest.c测试文件
  • 总结

Makefile

# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册KERN_DIR = /home/book/100ask_roc-rk3399-pc/linux-4.4all:make -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o ledtest ledtest.c clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderrm -f ledtest# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o# leddrv.c board_demo.c 编译成 100ask.ko
100ask_led-y := leddrv.o board_demo.o
obj-m   += 100ask_led.o

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

led_opr.h抽象出了led的操作

#ifndef _LED_OPR_H
#define _LED_OPR_Hstruct led_operations {int (*init) (int which); /* 初始化LED, which-哪个LED */       int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
};struct led_operations *get_board_led_opr(void);#endif

board_demo.c单板资源文件提供了实现

这里是个框架,填充对应寄存器的内存映射就能实现功能

#include <linux/module.h>#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include "led_opr.h"static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */
{printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);return 0;
}static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");return 0;
}static struct led_operations board_demo_led_opr = {.init = board_demo_led_init,.ctl  = board_demo_led_ctl,
};struct led_operations *get_board_led_opr(void)
{return &board_demo_led_opr;
}

demo的具体实现

以6ull_pro为例,不重要

#include <linux/module.h>#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <asm/io.h>#include "led_opr.h"static volatile unsigned int *CCM_CCGR1                              ;
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
static volatile unsigned int *GPIO5_GDIR                             ;
static volatile unsigned int *GPIO5_DR                               ;static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */
{unsigned int val;//printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);if (which == 0){if (!CCM_CCGR1){CCM_CCGR1                               = ioremap(0x20C406C, 4);IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x2290014, 4);GPIO5_GDIR                              = ioremap(0x020AC000 + 0x4, 4);GPIO5_DR                                = ioremap(0x020AC000 + 0, 4);}/* GPIO5_IO03 *//* a. 使能GPIO5* set CCM to enable GPIO5* CCM_CCGR1[CG15] 0x20C406C* bit[31:30] = 0b11*/*CCM_CCGR1 |= (3<<30);/* b. 设置GPIO5_IO03用于GPIO* set IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3*      to configure GPIO5_IO03 as GPIO* IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3  0x2290014* bit[3:0] = 0b0101 alt5*/val = *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;val &= ~(0xf);val |= (5);*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = val;/* b. 设置GPIO5_IO03作为output引脚* set GPIO5_GDIR to configure GPIO5_IO03 as output* GPIO5_GDIR  0x020AC000 + 0x4* bit[3] = 0b1*/*GPIO5_GDIR |= (1<<3);}return 0;
}static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{//printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");if (which == 0){if (status) /* on: output 0*/{/* d. 设置GPIO5_DR输出低电平* set GPIO5_DR to configure GPIO5_IO03 output 0* GPIO5_DR 0x020AC000 + 0* bit[3] = 0b0*/*GPIO5_DR &= ~(1<<3);}else  /* off: output 1*/{/* e. 设置GPIO5_IO3输出高电平* set GPIO5_DR to configure GPIO5_IO03 output 1* GPIO5_DR 0x020AC000 + 0* bit[3] = 0b1*/ *GPIO5_DR |= (1<<3);}}return 0;
}static struct led_operations board_demo_led_opr = {.num  = 1,.init = board_demo_led_init,.ctl  = board_demo_led_ctl,
};struct led_operations *get_board_led_opr(void)
{return &board_demo_led_opr;
}

leddrv.c设备驱动文件

在设备驱动文件中,不用关注怎么实现对寄存器的具体操作,只要调用led_opr.h提供的接口,将关注的重心放在设备驱动本身,比如申请释放设备号、注册设备驱动等

#include <linux/module.h>#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>#include "led_opr.h"#define LED_NUM 2/* 1. 确定主设备号                                                                 */
static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;#define MIN(a, b) (a < b ? a : b)/* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{int err;char status;struct inode *inode = file_inode(file);int minor = iminor(inode);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);err = copy_from_user(&status, buf, 1);/* 根据次设备号和status控制LED */p_led_opr->ctl(minor, status);return 1;
}static int led_drv_open (struct inode *node, struct file *file)
{int minor = iminor(node);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/* 根据次设备号初始化LED */p_led_opr->init(minor);return 0;
}static int led_drv_close (struct inode *node, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/* 2. 定义自己的file_operations结构体                                              */
static struct file_operations led_drv = {.owner     = THIS_MODULE,.open    = led_drv_open,.read    = led_drv_read,.write   = led_drv_write,.release = led_drv_close,
};/* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init led_init(void)
{int err;int i;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);major = register_chrdev(0, "100ask_led", &led_drv);  /* /dev/led */led_class = class_create(THIS_MODULE, "100ask_led_class");err = PTR_ERR(led_class);if (IS_ERR(led_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "100ask_led");return -1;}for (i = 0; i < LED_NUM; i++)device_create(led_class, NULL, MKDEV(major, i), NULL, "100ask_led%d", i); /* /dev/100ask_led0,1,... */p_led_opr = get_board_led_opr();return 0;
}/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit led_exit(void)
{int i;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);for (i = 0; i < LED_NUM; i++)device_destroy(led_class, MKDEV(major, i)); /* /dev/100ask_led0,1,... */device_destroy(led_class, MKDEV(major, 0));class_destroy(led_class);unregister_chrdev(major, "100ask_led");
}/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */module_init(led_init);
module_exit(led_exit);MODULE_LICENSE("GPL");

ledtest.c测试文件


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>/** ./ledtest /dev/100ask_led0 on* ./ledtest /dev/100ask_led0 off*/
int main(int argc, char **argv)
{int fd;char status;/* 1. 判断参数 */if (argc != 3) {printf("Usage: %s <dev> <on | off>\n", argv[0]);return -1;}/* 2. 打开文件 */fd = open(argv[1], O_RDWR);if (fd == -1){printf("can not open file %s\n", argv[1]);return -1;}/* 3. 写文件 */if (0 == strcmp(argv[2], "on")){status = 1;write(fd, &status, 1);}else{status = 0;write(fd, &status, 1);}close(fd);return 0;
}

总结

用UML图来表示硬件和驱动分层的思想

#mermaid-svg-H62fL9k7v8xq2HjH {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-H62fL9k7v8xq2HjH .error-icon{fill:#552222;}#mermaid-svg-H62fL9k7v8xq2HjH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-H62fL9k7v8xq2HjH .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-H62fL9k7v8xq2HjH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-H62fL9k7v8xq2HjH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-H62fL9k7v8xq2HjH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-H62fL9k7v8xq2HjH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-H62fL9k7v8xq2HjH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-H62fL9k7v8xq2HjH .marker.cross{stroke:#333333;}#mermaid-svg-H62fL9k7v8xq2HjH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-H62fL9k7v8xq2HjH .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-H62fL9k7v8xq2HjH text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-H62fL9k7v8xq2HjH .actor-line{stroke:grey;}#mermaid-svg-H62fL9k7v8xq2HjH .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-H62fL9k7v8xq2HjH .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-H62fL9k7v8xq2HjH #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-H62fL9k7v8xq2HjH .sequenceNumber{fill:white;}#mermaid-svg-H62fL9k7v8xq2HjH #sequencenumber{fill:#333;}#mermaid-svg-H62fL9k7v8xq2HjH #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-H62fL9k7v8xq2HjH .messageText{fill:#333;stroke:#333;}#mermaid-svg-H62fL9k7v8xq2HjH .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-H62fL9k7v8xq2HjH .labelText,#mermaid-svg-H62fL9k7v8xq2HjH .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-H62fL9k7v8xq2HjH .loopText,#mermaid-svg-H62fL9k7v8xq2HjH .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-H62fL9k7v8xq2HjH .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-H62fL9k7v8xq2HjH .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-H62fL9k7v8xq2HjH .noteText,#mermaid-svg-H62fL9k7v8xq2HjH .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-H62fL9k7v8xq2HjH .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-H62fL9k7v8xq2HjH .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-H62fL9k7v8xq2HjH .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-H62fL9k7v8xq2HjH .actorPopupMenu{position:absolute;}#mermaid-svg-H62fL9k7v8xq2HjH .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-H62fL9k7v8xq2HjH .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-H62fL9k7v8xq2HjH .actor-man circle,#mermaid-svg-H62fL9k7v8xq2HjH line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-H62fL9k7v8xq2HjH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}board_demo.cled_opr.hleddrv.cledtest.c提供了抽象led的具体实现抽象出了led的操作调用抽象的led的接口提供API给应用层调用API接口测试驱动程序leddrv.c主要关注驱动本身比如申请释放设备号、注册字符设备board_demo.cled_opr.hleddrv.cledtest.c

韦东山的led硬件和驱动分类框架相关推荐

  1. 韦东山:驱动和APP,根本不应该上升到互相鄙视的地步

    在线课堂:https://www.100ask.net/index(课程观看) 论  坛:http://bbs.100ask.net/(学术答疑) 开 发 板:https://100ask.taoba ...

  2. IMX6ULL裸机学习----LED正点原子在韦东山开发板上实现

    前言 一.IMX6ULL 启动流程? 二.LED代码移植 1.硬件介绍 2.代码移植 总结 前言 最近在学习IMX6ULL这个开发板,手边有韦东山老师的imx6ull全功能板子,为了练习学习的初步效果 ...

  3. 【韦东山】嵌入式全系统:单片机-linux-Android对硬件操作的不同侧重点

    在线课堂:https://www.100ask.net/index(课程观看) 论  坛:http://bbs.100ask.net/(学术答疑) 开 发 板:https://100ask.taoba ...

  4. 韦东山二期驱动视频-热拔插驱动——RK3399自制linux系统不支持HDMI热拔插问题分析

    背景: 公司的板子,对于HDMI的显示器热拔插不支持,只能在插入HDMI时启动才能输出,而当开机之后,再插入HDMI显示器则无输出,不知道原因. 推测如下: 1.设备树的引脚配置有误,导致插入HDMI ...

  5. imx6ull 正点原子设备树适配韦东山的开发板 (一)顺利启动,配置led,button

    设备树在公司经常用到 有时候很多驱动编写也就是替换设备树,所以拿韦老师的板子练手设备树 这次尝试直接拿正点的设备书改成韦老师的板子能用 对比 正点原子的设备树结构图 韦东山的设备树结构图 从因为蓝色的 ...

  6. 【韦东山】7天物联网智能家居实战训练营基础班-DAY1

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.学习路线 二.学习的基础 三.开发流程 四.开发板的介绍 1.ARM分类 2.STM31F103_MINI开发板 ...

  7. 韦东山:嵌入式Linux学习路线图

    我是1999年上的大学,物理专业.在大一时,我们班里普遍弥漫着对未来的不安,不知道学习了物理后出去能做什么.你当下的经历.当下的学习,在未来的一天肯定会影响到你.毕业后我们也各自找到了自己的职业:出国 ...

  8. 韦东山第一二期衔接课程内容概要

    韦东山第一二期衔接课程内容概要 0 使得一个裸板Jz2440能运行linux应用程序的过程 1 uboot启动内核总结 1.1 u-boot分析之编译体验 1.2 u-boot分析之Makefile结 ...

  9. 韦东山第一期课程内容概要

    韦东山第一期课程内容概要 1一个嵌入式程序要运行所需的东西 1.1第一条指令:b reset 1.2 reset要完成的事件 1.2.1设置开门狗 1.2.2设置时钟 1.2.3判断启动方式并设置堆栈 ...

最新文章

  1. bugku 闪得好快
  2. php安全编程—sql注入攻击
  3. const char* p 、char* const p、const (char*) p 理解记忆
  4. easyui 修改单元格内容_初学Excel办公软件快速修改文字的方法
  5. lol战绩查询接口_LOL:莫名其妙被封号?3把躺赢局被说是代打
  6. 前端学习(218):属性选择器
  7. python通信模块_基于Python的电路故障诊断系统通信模块的实现
  8. Delphi 中的 XMLDocument 类详解(13) - 关于 XML 属性
  9. LeetCode 852. Peak Index in a Mountain Array
  10. string 转换int
  11. Fluentd日志处理-tail拉取(三)
  12. 从零学会SQL:入门(实操演示)
  13. 分享一个百度云加速下载工具
  14. GEE、USGS、地理空间数据云上下载武汉地区的影像数据
  15. codesmith mysql 模板_CodeSmith代码自动生成器 JAVA模版的制作---CodeSmith+MySQL+MyEclipse 10...
  16. 2005世界五百强排名
  17. 【二维码案例】“码”出行,交通运输领域二维码应用
  18. ftp服务器项目,ftp服务器项目手册.doc
  19. Java实现生产者消费者的PV操作信号量操作
  20. WHERE语句中BETWEEN与IN的使用教程-MySQL

热门文章

  1. 就这一次,带你彻底搞清 MySQL行级锁的加锁规则
  2. Java后端--53--Mybatis2:Mybatis的查询和新增操作
  3. Matlab数形结合求解不等式
  4. useRequest使用日志
  5. 半高半长 PCIe 20G 以太网实 存储卡 K7
  6. 中国超级计算机城市,刚刚传来!2021中国十大城市排名!结果大吃—惊!快看看~...
  7. 27Vert.X框架学习笔记
  8. Android Studio 技巧之【Move Lines Up Down】
  9. Hadoop HA高可用集群搭建(2.7.2)
  10. excel工具类 支持2003、2007、2010、2013