led驱动编写——不使用驱动框架

基于led框架的驱动分析 本文的led驱动使用了内核提供的led框架接口,这种驱动实现与普通字符设备驱动有着本质的区别。此外还融合了platform和gpiolib,需要结合这两者来分析本驱动。 阅读详情

1 代码片段详解

static dev_t mydev;
声明 dev_t结构体变量,用来存储主次设备号。

static struct cdev test_cdev;
声明cdev结构体变量,用来绑定file_operations test_fops这个驱动的具体操作函数,test_cdev.ops = test_fops。

mydev = MKDEV(200, 0);
分配设备号,主设备号:200,次设备号:从0开始

cdev_init(&test_cdev, &test_fops);
出始化cdev类声明的对象test_cdev中的ops成员变量,把第二个形参赋值给test_cdev.ops。

cdev_add(&test_cdev, mydev, MYCNT);
第一个参数:表示被注册的设备的file ops等,第二个:主次设备号,第三:注册数量,数量在 MKDEV(x, y); y的基础上递增。

mknod /dev/名字 c 250 0
创建设备,操作主设备号250,次设备号:0的驱动

2 代码展示

2.1 静态注册主次设备号

//以下代码为使用新版静态字符设备注册函数,实现可使用的LED驱动

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>		// arch/arm/mach-s5pv210/include/mach/gpio-bank.h
#include <linux/string.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/cdev.h>



#define MYMAJOR		200
#define MYCNT		1
#define MYNAME		"testchar"

#define GPJ0CON		S5PV210_GPJ0CON
#define GPJ0DAT		S5PV210_GPJ0DAT

#define rGPJ0CON	*((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT	*((volatile unsigned int *)GPJ0DAT)

#define GPJ0CON_PA	0xe0200240
#define GPJ0DAT_PA 	0xe0200244

unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;


int mymajor;
static dev_t mydev;
static struct cdev test_cdev;

char kbuf[100];			// 内核空间的buf


static int test_chrdev_open(struct inode *inode, struct file *file)
{
	// 这个函数中真正应该放置的是打开这个设备的硬件操作代码部分
	// 但是现在暂时我们写不了这么多,所以用一个printk打印个信息来做代表。
	printk(KERN_INFO "test_chrdev_open\n");
	
	rGPJ0CON = 0x11111111;
	rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));		// 亮
	
	return 0;
}

static int test_chrdev_release(struct inode *inode, struct file *file)
{
	printk(KERN_INFO "test_chrdev_release\n");
	
	rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
	
	return 0;
}

ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
{
	int ret = -1;
	
	printk(KERN_INFO "test_chrdev_read\n");
	
	ret = copy_to_user(ubuf, kbuf, count);
	if (ret)
	{
		printk(KERN_ERR "copy_to_user fail\n");
		return -EINVAL;
	}
	printk(KERN_INFO "copy_to_user success..\n");
	
	
	return 0;
}

// 写函数的本质就是将应用层传递过来的数据先复制到内核中,然后将之以正确的方式写入硬件完成操作。
static ssize_t test_chrdev_write(struct file *file, const char __user *ubuf,
	size_t count, loff_t *ppos)
{
	int ret = -1;
	
	printk(KERN_INFO "test_chrdev_write\n");

	// 使用该函数将应用层传过来的ubuf中的内容拷贝到驱动空间中的一个buf中
	//memcpy(kbuf, ubuf);		// 不行,因为2个不在一个地址空间中
	memset(kbuf, 0, sizeof(kbuf));
	ret = copy_from_user(kbuf, ubuf, count);
	if (ret)
	{
		printk(KERN_ERR "copy_from_user fail\n");
		return -EINVAL;
	}
	printk(KERN_INFO "copy_from_user success..\n");
	
	if (kbuf[0] == '1')
	{
		rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
	}
	else if (kbuf[0] == '0')
	{
		rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
	}
	return 0;
}


// 自定义一个file_operations结构体变量,并且去填充
static const struct file_operations test_fops = {
	.owner		= THIS_MODULE,				// 惯例,直接写即可
	
	.open		= test_chrdev_open,			// 将来应用open打开这个设备时实际调用的
	.release	= test_chrdev_release,		// 就是这个.open对应的函数
	.write 		= test_chrdev_write,
	.read		= test_chrdev_read,
};


// 模块安装函数
static int __init chrdev_init(void)
{	
	int retval;
	
	printk(KERN_INFO "chrdev_init helloworld init\n");

/*
	// 在module_init宏调用的函数中去注册字符设备驱动
	// major传0进去表示要让内核帮我们自动分配一个合适的空白的没被使用的主设备号
	// 内核如果成功分配就会返回分配的主设备好;如果分配失败会返回负数
	mymajor = register_chrdev(0, MYNAME, &test_fops);
	if (mymajor < 0)
	{
		printk(KERN_ERR "register_chrdev fail\n");
		return -EINVAL;
	}
	printk(KERN_INFO "register_chrdev success... mymajor = %d.\n", mymajor);
*/	

	// 使用新的cdev接口来注册字符设备驱动
	// 新的接口注册字符设备驱动需要2步
	
	// 第1步:注册/分配主次设备号
	mydev = MKDEV(MYMAJOR, 0);  //分配设备号,主设备号:200,次设备号:从0开始
	retval = register_chrdev_region(mydev, MYCNT, MYNAME);  //注册字符设备,主设备号从mydev获取,注册设备数量1,设备名称myname
	if (retval) {
		printk(KERN_ERR "Unable to register minors for %s\n", MYNAME);
		return -EINVAL;
	}
	printk(KERN_INFO "register_chrdev_region success\n");
	// 第2步:注册字符设备驱动
	cdev_init(&test_cdev, &test_fops);   //出始化cdev类声明的对象test_cdev中的ops成员变量,把第二个形参赋值给test_cdev.ops
	retval = cdev_add(&test_cdev, mydev, MYCNT); //第一个参数:表示被注册的设备的file ops等,第二个:主次设备号,第三:注册数量
	if (retval) {
		printk(KERN_ERR "Unable to cdev_add\n");
		return -EINVAL;
	}
	printk(KERN_INFO "cdev_add success\n");

	
	// 使用动态映射的方式来操作寄存器
	if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))
		return -EINVAL;
	if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON"))
		return -EINVAL;
	
	pGPJ0CON = ioremap(GPJ0CON_PA, 4);
	pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);
	
	*pGPJ0CON = 0x11111111;
	*pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));		// 亮
	

	return 0;
}

// 模块下载函数
static void __exit chrdev_exit(void)
{
	printk(KERN_INFO "chrdev_exit helloworld exit\n");

	*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));	
	
	// 解除映射
	iounmap(pGPJ0CON);
	iounmap(pGPJ0DAT);
	release_mem_region(GPJ0CON_PA, 4);
	release_mem_region(GPJ0DAT_PA, 4);

/*	
	// 在module_exit宏调用的函数中去注销字符设备驱动
	unregister_chrdev(mymajor, MYNAME);
*/	

	// 使用新的接口来注销字符设备驱动
	// 注销分2步:
	// 第一步真正注销字符设备驱动用cdev_del
	cdev_del(&test_cdev);
	// 第二步去注销申请的主次设备号
	unregister_chrdev_region(mydev, MYCNT);
}


module_init(chrdev_init);
module_exit(chrdev_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");				// 描述模块的许可证
MODULE_AUTHOR("aston");				// 描述模块的作者
MODULE_DESCRIPTION("module test");	// 描述模块的介绍信息
MODULE_ALIAS("alias xxx");			// 描述模块的别名信息

2.2 动态注册主次设备号

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>		// arch/arm/mach-s5pv210/include/mach/gpio-bank.h
#include <linux/string.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/cdev.h>

//#define MYMAJOR		200
#define MYCNT		1
#define MYNAME		"testchar"

#define GPJ0CON		S5PV210_GPJ0CON
#define GPJ0DAT		S5PV210_GPJ0DAT

#define rGPJ0CON	*((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT	*((volatile unsigned int *)GPJ0DAT)

#define GPJ0CON_PA	0xe0200240
#define GPJ0DAT_PA 	0xe0200244

unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;


//int mymajor;
static dev_t mydev;
static struct cdev test_cdev;

char kbuf[100];			// 内核空间的buf


static int test_chrdev_open(struct inode *inode, struct file *file)
{
	// 这个函数中真正应该放置的是打开这个设备的硬件操作代码部分
	// 但是现在暂时我们写不了这么多,所以用一个printk打印个信息来做代表。
	printk(KERN_INFO "test_chrdev_open\n");
	
	rGPJ0CON = 0x11111111;
	rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));		// 亮
	
	return 0;
}

static int test_chrdev_release(struct inode *inode, struct file *file)
{
	printk(KERN_INFO "test_chrdev_release\n");
	
	rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
	
	return 0;
}

ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
{
	int ret = -1;
	
	printk(KERN_INFO "test_chrdev_read\n");
	
	ret = copy_to_user(ubuf, kbuf, count);
	if (ret)
	{
		printk(KERN_ERR "copy_to_user fail\n");
		return -EINVAL;
	}
	printk(KERN_INFO "copy_to_user success..\n");
	
	
	return 0;
}

// 写函数的本质就是将应用层传递过来的数据先复制到内核中,然后将之以正确的方式写入硬件完成操作。
static ssize_t test_chrdev_write(struct file *file, const char __user *ubuf,
	size_t count, loff_t *ppos)
{
	int ret = -1;
	
	printk(KERN_INFO "test_chrdev_write\n");

	// 使用该函数将应用层传过来的ubuf中的内容拷贝到驱动空间中的一个buf中
	//memcpy(kbuf, ubuf);		// 不行,因为2个不在一个地址空间中
	memset(kbuf, 0, sizeof(kbuf));
	ret = copy_from_user(kbuf, ubuf, count);
	if (ret)
	{
		printk(KERN_ERR "copy_from_user fail\n");
		return -EINVAL;
	}
	printk(KERN_INFO "copy_from_user success..\n");
	
	if (kbuf[0] == '1')
	{
		rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
	}
	else if (kbuf[0] == '0')
	{
		rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
	}
	return 0;
}


// 自定义一个file_operations结构体变量,并且去填充
static const struct file_operations test_fops = {
	.owner		= THIS_MODULE,				// 惯例,直接写即可
	
	.open		= test_chrdev_open,			// 将来应用open打开这个设备时实际调用的
	.release	= test_chrdev_release,		// 就是这个.open对应的函数
	.write 		= test_chrdev_write,
	.read		= test_chrdev_read,
};


// 模块安装函数
static int __init chrdev_init(void)
{	
	int retval;
	
	printk(KERN_INFO "chrdev_init helloworld init\n");

	// 使用新的cdev接口来注册字符设备驱动
	// 新的接口注册字符设备驱动需要2步
	
	// 第1步:分配主次设备号
	retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME);
	if (retval < 0) 
	{
		printk(KERN_ERR "Unable to alloc minors for %s\n", MYNAME);
		goto flag1;
	}
	printk(KERN_INFO "alloc_chrdev_region success\n");
	printk(KERN_INFO "major = %d, minor = %d.\n", MAJOR(mydev), MINOR(mydev));
	
	
	// 第2步:注册字符设备驱动
	cdev_init(&test_cdev, &test_fops);
	retval = cdev_add(&test_cdev, mydev, MYCNT);
	if (retval) {
		printk(KERN_ERR "Unable to cdev_add\n");
		goto flag2;
	}
	printk(KERN_INFO "cdev_add success\n");

	
	// 使用动态映射的方式来操作寄存器
	if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))
//		return -EINVAL;
		goto flag3;
	if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON"))
//		return -EINVAL;
		goto flag3;
	
	pGPJ0CON = ioremap(GPJ0CON_PA, 4);
	pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);
	
	*pGPJ0CON = 0x11111111;
	*pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));		// 亮
	
	//goto flag0:
	return 0;
	
// 如果第4步才出错跳转到这里来	
flag4:
	release_mem_region(GPJ0CON_PA, 4);
	release_mem_region(GPJ0DAT_PA, 4);

// 如果第3步才出错跳转到这里来
flag3:
	cdev_del(&test_cdev);

// 如果第2步才出错跳转到这里来
flag2:
	// 在这里把第1步做成功的东西给注销掉
	unregister_chrdev_region(mydev, MYCNT);
// 如果第1步才出错跳转到这里来
flag1:	
	return -EINVAL;
//flag0:	
//	return 0;
}

// 模块下载函数
static void __exit chrdev_exit(void)
{
	printk(KERN_INFO "chrdev_exit helloworld exit\n");

	*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));	
	
	// 解除映射
	iounmap(pGPJ0CON);
	iounmap(pGPJ0DAT);
	release_mem_region(GPJ0CON_PA, 4);
	release_mem_region(GPJ0DAT_PA, 4);

/*	
	// 在module_exit宏调用的函数中去注销字符设备驱动
	unregister_chrdev(mymajor, MYNAME);
*/	

	// 使用新的接口来注销字符设备驱动
	// 注销分2步:
	// 第一步真正注销字符设备驱动用cdev_del
	cdev_del(&test_cdev);
	// 第二步去注销申请的主次设备号
	unregister_chrdev_region(mydev, MYCNT);
}


module_init(chrdev_init);
module_exit(chrdev_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");				// 描述模块的许可证
MODULE_AUTHOR("aston");				// 描述模块的作者
MODULE_DESCRIPTION("module test");	// 描述模块的介绍信息
MODULE_ALIAS("alias xxx");			// 描述模块的别名信息

2.3 创建设备绑定主次设备号

//以上是关于驱动的代码,驱动安装成功后,使用下面的命令绑定设备

mknod /dev/名字 c 200 0

2.4 Linux应用层代码

绑定成功后,运行Linux应用程序,调用open、close、write、read等api接口函数就可以直接操作硬件寄存器。
代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>


#define FILE	"/dev/test"			// 刚才mknod创建的设备文件名,mknod /dev/test c 主设备号 次设备号

char buf[100];

int main(void)
{
	int fd = -1;
	int i = 0;
	
	fd = open(FILE, O_RDWR);
	if (fd < 0)
	{
		printf("open %s error.\n", FILE);
		return -1;
	}
	printf("open %s success..\n", FILE);

	while (1)
	{
		memset(buf, 0 , sizeof(buf));
		printf("请输入 on | off \n");
		scanf("%s", buf);
		if (!strcmp(buf, "on"))
		{
			write(fd, "1", 1);
		}
		else if (!strcmp(buf, "off"))
		{
			write(fd, "0", 1);
		}
		else if (!strcmp(buf, "flash"))
		{
			for (i=0; i<3; i++)
			{
				write(fd, "1", 1);
				sleep(1);
				write(fd, "0", 1);
				sleep(1);
			}
		}	
		else if (!strcmp(buf, "quit"))
		{
			break;
		}
	}

	
	// 关闭文件
	close(fd);
	
	return 0;
}
四.驱动框架入门之LED(上) 目录 一.何谓驱动框架(了解下即可) 1.1、驱动是谁写的 1.2、驱动编程协作要求 1.3、到底什么是驱动框架 二.内核驱动框架中LED的基本情况 2.1、相关文件 2.2、九鼎移植的内核中led驱动 2.3、案例分析驱动框架的使用 2.4、典型的驱动开发行业现状 三.初步分析led驱动框架源码1_2 3.1、涉及到的文件 3.2、subsys_initcal... 阅读详情

相关推荐

四.驱动框架入门之LED(中)

接上篇:https://blog.youkuaiyun.com/wangweijundeqq/article/details/101696862 目录 五.基于驱动框架写led驱动1 5.1、分析 5.2、动手写led驱动模块 六.基于驱动框架写led驱动2 6.1、代码实践 七.基于驱动框架写led驱动3 7.1、在驱动中将4个LED分开 7.2、和leds-s3c24xx.c的不同 7...

Mr.Wang的博客 1385

linux用户空间flash驱动,全面掌握Linux驱动框架——字符设备驱动、I2C驱动、总线设备驱动、NAND FLASH驱动...

原标题:全面掌握Linux驱动框架——字符设备驱动、I2C驱动、总线设备驱动、NAND FLASH驱动字符设备驱动哈~ 这几天都在发图,通过这种方式,我们希望能帮大家梳理学过的知识,全局的掌握Linux驱动框架,今天带来的是字符设备驱动,制图者是很久以前的答疑助手兰天王,原贴2012年5月20日发布于论坛(www.100ask.org)。众所周知,字符设备是Linux驱动基础也是重点,还没学到字符...

weixin_36437103的博客 192

嵌入式驱动编写-点亮LED驱动程序

在开发板上,有三个LED灯.如何通过应用程序点亮这三个灯如何编写驱动程序 操作硬件的时候,我们需要准备开发板的原理图和开发手册,,根据这两个文档来进行配置   在source insight 编写代码 1 第一个led驱动程序 #include #include #include #inclu

joshua_love的博客 1万+

imx6ull-驱动开发篇34——platform 设备驱动实验

本文介绍了基于Linux platform驱动框架编写LED灯驱动的方法,采用自定义platform_device方式而非设备树来描述设备信息。

m0_62228566的博客 721

led子系统之实战篇(实际led驱动编写)

led驱动应该是linux驱动中是最简单的一个,下面实际讲解关于led驱动编写的过程。 早期的led驱动实际上在/dev/led生成文件,当led成为一个单独的子系统时,我们要用到led的子系统以及相关的函数 #include #include #include #include #include #include #include #include 头文件不说了stat

感觉身体被掏空 4830

Linux 驱动开发基础知识——编写LED驱动程序(三)

我们基于 Hello 驱动程序先写出最简单的 LED 驱动程序

嵌入式技术开发 7513

设备驱动框架之LED

为了尽量降低驱动开发者难度以及接口标准化,就出现了设备驱动框架的概念;Linux 针对每个种类的驱动设计一套成熟的、标准的、典型的驱动实现, 然后把不同厂家的同类硬件驱动中相同的部分抽出来自己实现好,再把不同部分留出接口给具体的驱动开发工程师来实现,这就叫驱动框架。说白了,设备驱动框架就是对原始的驱动开发接口进行了封装,不同种类的设备有不同的封装和实现方法,驱动开发工程师根据这些封装好的接口来编写自己的驱动程序,这就是设备驱动框架。

qq_40514606的博客 1568

Linux 驱动开发基础知识—— LED 驱动程序框架(四)

内核驱动部分的维护者,往往为同类的设备(比如LED、LCD、蜂鸣器等等)设计了一个成熟的、标准的、典型的框架。他们将同类设备的驱动中通用的一些功能抽离出来,作为驱动框架中的核心层,然后设计好核心层与具体操作层的接口,让驱动开发者来实现具体操作层 ​

嵌入式技术开发 7987

驱动框架之LED

一、什么是驱动框架? 1、驱动是谁写的? (1)驱动开发工程师; (2)内核维护者; 2、驱动编程协作要求 (1)接口标准化; (2)内核开发者应该尽量降低驱动开发者难度; 3、到底什么是驱动框架? (1)驱动框架 内核中驱动部分维护者,针对每个种类(比如LED、LCD、蜂鸣器等等)的设备,都设计有一套成熟的、标准的、典型的驱动实现; 它是把不同厂家的同类硬件驱动中相同的部分抽...

lushoumin的博客 606

告别玄学调参:手把手教你用20元件模型搞定GaN HEMT高频仿真(附S参数提取避坑指南)

本文详细介绍了如何使用20元件模型优化GaN HEMT高频仿真,解决传统16元件模型在10GHz以上频段的误差问题。通过S参数提取避坑指南和实战案例,帮助工程师精准建模分布式电极效应和寄生反馈路径,提升射频电路设计效率。

weixin_42523693的博客 441

从原理图到PCB:手把手教你绘制DRV8301驱动板(附完整代码与避坑指南)

本文详细介绍了如何从原理图设计到PCB布局,手把手教你绘制DRV8301驱动板,特别适用于FOC(磁场定向控制)系统开发。内容涵盖元器件选型、电源树架构设计、栅极驱动电路、PCB布局技巧、焊接调试及代码配置,并附有完整代码与常见问题避坑指南,帮助工程师快速掌握DRV8301驱动板的设计与优化。

weixin_33716941的博客 394

基于led框架及gpiolib管理的驱动编写

leds-xxxx.c,这个文件是LED驱动框架的第2部分,是由不同厂商的驱动工程师编写添加的,厂商驱动工程师结合自己公司的硬件的不同情况来对LED进行操作,使用第一部分提供的接口来和驱动框架进行交互,最终实现驱动的功能。又譬如中断号也是一种资源,驱动在使用前也必须去申请。这也是驱动框架的组成部分。1、内核中驱动部分维护者针对每个种类的驱动设计一套成熟的、标准的、典型的驱动实现,然后把不同厂家的同类硬件驱动中相同的部分抽出来自己实现好,再把不同部分留出接口给具体的驱动开发工程师来实现,这就叫驱动框架。

2301_77136686的博客 559

数字电视中的 Closed Caption (CC) 字幕标准协议

本文将详细介绍 DVB中的 CC 字幕标准协议,包括数据结构格式、字段定义、字段占用比特数以及字段用途。此外,本文还将举例说明如何从 TS 传输流中解析 CC 字幕信息,并组织打包到电视节目中进行显示。

以实战经验介绍行业知识 670

三星6818LED驱动的编写

三星6818芯片LED驱动详解led驱动.c文件的编写驱动的编译和生成编写程序代码烧录驱动(.ko)和程序到板子 led驱动.c文件的编写 一般驱动编写顺序是这样的 1> 动态创建设备号 2> 字符设备初始化 3> 将设备添加到内核 4> 创建设备 5> 然后卸载驱动的时候记得释放资源就好 附上代码: #include <linux/init.h> #include <linux/kernel.h> #include <linux/module

a812417530的博客 1797

OpenHarmony电源管理驱动架构剖析:HDI框架下的低功耗实现

鸿蒙电源管理驱动模块是OpenHarmony操作系统中负责设备电源状态管理的核心组件。该模块基于HDI(Hardware Device Interface)框架实现,提供了完整的电源管理功能,包括系统休眠/唤醒、运行锁管理、休眠模式控制等核心功能。public:// 主要接口函数// 运行锁管理private:// 内部辅助函数// 成员变量public:// 运行锁控制接口// 回调管理private:// 内部辅助函数。

m0_67505153的博客 208

【2025软考高级架构师】基础知识部分——6、软件架构设计

(1)架构是对系统的抽象(2)架构由多个结构组成(3)任何软件都存在架构(4)元素及其行为的集合构成架构的内容(5)架构具有“基础”性(6)架构隐含有“决策”

qq837993702的博客 1万+

基于事件驱动的LED控制框架详解

在嵌入式系统开发中,LED控制是一个常见的需求,不同项目对LED的效果和功能要求各不相同。传统的LED控制方式需要针对不同的需求编写不同的代码,增加了开发的工作量和复杂度。为了解决这一问题,我开发了一种扩展性较强的LED框架,该框架脱离底层细节,只关注应用层,提供一种通用的LED控制方案,使得开发人员可以轻松实现各种LED效果,极大地提高了开发效率和灵活性。

wcynbl的博客 1776

leetcode134 加油站

题目如下 在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。 如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。 说明: 如果题目有解,该答案即为唯一答案。 输入数组均为非空数组,且长度相同。 输...

ayang818 's blog 233

【Linux驱动】最基本的驱动框架 | LED驱动

讲解了Linux程序的框架,包括file_operations结构体及初始化,入口函数及注册设备节点,提供设备信息,还有出口函数等等。最后用该框架实现了基于IMX6ULL单板的LED驱动程序。

weixin_63726869的博客 3139

Agisoft Metashape 基于RTK技术的无人机航测免像控技术流程

利用无人机进行航空摄影测量,其使用方便、快速获得成果,已经是测绘行业外业生产的主要方式。RTK定位系统的出现,其厘米级的定位系统,使得航测无人机在作业时,能够准确获取拍照时的GPS位置,能够大量减少像控点,即使不使用像控点也能满足规范对精度的要求。可以有效解决特殊地形(如山川、峡谷、河流、沼泽、水库等)外业人员很难布设像控点的情况。

penglu366366的博客 2188

基于Simulink的电力电子负载特性模拟仿真实例

电阻性负载是最简单的负载形式,其阻抗为纯电阻,电流和电压之间的关系符合欧姆定律。电阻性负载的特点是消耗能量,不会存储能量。本示例将以一个典型的直流电源供电系统为例,展示如何利用Simulink进行不同负载特性的仿真模拟。直流电源:提供固定电压输入。逆变器(可选):根据需要产生PWM调制信号,驱动负载。负载模块:模拟不同类型的负载特性,如电阻性负载、电感性负载和容性负载。测量模块:用于观察负载上的电压和电流波形。系统架构视图plaintext深色版本v| || |

xiaoheshang_123的博客 429

Linux LED设备驱动框架总结

文章目录Q1:到底什么是驱动框架?Q2:内核驱动框架中的LED位置在哪?Q3:leds目录的特点是什么?Q4:怎么在内核中使能LED驱动框架?Q5:新创建leds目录下,为什么不是空白?Q6:class-led.c的运作基本流程(1)subsys_initcall(leds_init);(2)static int __init leds_init入口函数 Q1:到底什么是驱动框架? (1)驱动框架由内核维护者设计,他们将每种类的驱动设计成一套成熟的、标准的、典型的驱动实现。将各个厂家相同的硬件驱动部分抽出来

weixin_38956024的博客 953

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

驱动框架入门之LED

1.1、驱动是谁写的 (1)驱动开发工程师 (2)内核维护者 1.2、驱动编程协作要求 (1)接口标准化 (2)尽量降低驱动开发者难度 1.3、到底什么是驱动框架 (1)内核中驱动部分维护者针对每个种类的驱动设计一套成熟的、标准的、典型的驱动实现,然后把不同厂家的同类硬件驱动中相同的部分抽出来自己实现好,再把不同部分留出接口给具体的驱动开发工程师来实现,这就叫驱动框架。 (2)内核维护者在内核中设计了一些统一管控系统资源的体系,这些体系让内核能够对资源在各个驱动之间的使用统一协调和分配,保证整个内核的稳

weixin_44705391的博客 1532

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

linux驱动开发(5、使用leds驱动框架点亮led灯)

文章目录使用驱动框架的优势leds驱动框架相关 在前面已经用创建通用字符设备的方式完成了led的操作,本次总结记录的是使用linux内核提供的leds驱动框架来点亮led灯。 使用驱动框架的优势 linux内核为各种硬件进行分类,然后针对各类硬件实现一套比较标准的驱动框架,其中每类硬件共有属性那一部分由内核开发者负责实现和维护,然后给驱动开发者留出接口用来完成具体硬件的特定操作部分。更加简化了驱动开发者的编码工作。 leds驱动框架相关 相关的源代码位于drivers/leds/目录内,头文件位于inclu

漫长当下 3805

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

led驱动编写-使用驱动框架

驱动开发框架由内核开发人员提供,SOC开发人员根据内核提供的驱动框架编写适合的硬件驱动,开发板厂商负责移植驱动到自己生产的开发板上,三者分工不同。

forecasts的博客 681
上一篇: secureCRT配置
下一篇: led驱动编写-使用驱动框架
金银588
博客等级 码龄7年 1210粉丝 30原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值