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 驱动程序

嵌入式技术开发 7512

设备驱动框架之LED

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

qq_40514606的博客 1568

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

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

嵌入式技术开发 7987

驱动框架之LED

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

lushoumin的博客 606

OpenVox 8FXO板卡驱动安装记录

OpenVox A800P和A1200P的板卡驱动安装及问题解决方法

clliu的专栏 2463

SAP ERP 物料主数据同步外围系统

w_input-beskz = i_marc_new-beskz."采购类型 " BESKZ--- ADD BY HX_FHJ 20210106。w_input-beskz = lw_marc-beskz."采购类型 " BESKZ--- ADD BY HX_FHJ 20210106。w_input-sobsl = i_marc_new-sobsl."特殊采购类型-虚拟物料标识。

ChampaignWolf的专栏 1744

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

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

2301_77136686的博客 559

跟着团子学SAP CO:熟练使用结果分析码实现各个场景下收入成本的匹配(一) KKA2/CJ88

PS模块中对财务顾问最难的点在于项目结算,特别是销售型的项目结算。针对客户型项目结算我列举了以下常见的收入成本匹配的一些场景: (1)在项目执行过程中会多次开票,但由于各期成本浮动很大(如前期可能只涉及一些人工、中期全是比较金额较大的材料费),所以希望开票的时候能通过合理的一些指标进行主营业务成本与主营业务收入的匹配。 (2)项目过程中会多次开票,开票确认主营业务收入时,直接将累计发生的实际成本结转为主营业务成本。 (3)项目过程中虽多次开票,但不确认收入,所有发生的实际成本月末计算为在制品,最后项目一次性

Lucas1211的博客 9533

三星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

六种光耦综合对比——《器件手册--光耦》

可控硅输出光耦(SCR Optocoupler)是一种特殊的光电耦合器,由红外发光二极管(LED)和双向可控硅(SCR)组成,用于实现输入和输出之间的电气隔离和信号传输。逻辑输出光耦是一种高效、可靠的数字信号隔离和电平转换器件,通过电气隔离和逻辑电平输出,确保了系统的安全性和信号的稳定性。固态继电器(可控硅输出,SSR)是一种无触点电子开关设备,利用可控硅(SCR或Triac)作为主要开关元件,通过光耦合器实现输入和输出之间的电气隔离。光耦作为一种成熟的电气隔离器件,在电子电路设计中具有重要的地位。

XU157303764的博客 1725

斗鱼直播间弹幕爬虫实战:WebSocket连接与实时数据采集

在数据驱动的时代,直播平台的弹幕数据蕴含着巨大的价值——用户行为分析、舆情监控、弹幕情感分析、直播热度评估等应用场景层出不穷。斗鱼作为国内领先的直播平台,其弹幕系统基于WebSocket协议实现实时双向通信。本文将带您从零开始,使用Python构建一个完整的斗鱼直播间弹幕爬虫系统。

2201_76125261的博客 135

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

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

wcynbl的博客 1776

Modbus通信从入门到精通_2_Modbus TCP通信详解及仿真(搭建ModbusTCP仿真环境:创建虚拟PLC并进行ModbusTCP通讯;寄存器与PLC中映射关系;适合理解如何编写上位机)

LocalPort”是PLC的端口,最终的设置如下图所示:(相关的设置都可以查看帮助文档)P#DB1.DBX0.0 BYTE28是以指针形式表示的DB1的DBX0.0开始(在使用的西门子Modbus tcp的程序块中设置的“MB_HOLD_REG”输出寄存器),其有28个字节(28个字节对应14个寄存器),4001是第一个寄存器,往后数14个即可。在上面的基础上,增加一个“DB1”的“组对象”,同时将“MW0”的组对象禁止(双击组对象,“Active”取消勾选),启动之后发现无法连接。

分享学习成长历程,Winter is coming! 1万+

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

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

weixin_63726869的博客 3139

目标跟踪与视频时序分析基础

两帧均有目标,却因快速移动导致检测框无重叠,仅靠交并比(IoU)无法维持身份关联。本文通过手算与纯Python实现最小跟踪流程,揭示IoU仅为位置线索,不能应对快速位移或遮挡。实验表明,即使目标真实存在,低重叠也会触发新编号。强调跟踪需结合运动预测、外观特征与阈值验证,避免误判。

Mr数据杨 2335

极验验证码: 一点即过验证(鼠标轨迹分析)

2、第一个验证接口分析(第一个w值还原) 3、第二个验证接口(第二个w值还原)这里定位w生成跟上篇文章一样,就不展开了;主要分析tt值(鼠标轨迹)的生成逻辑

qq_49268524的博客 1009

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

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

驱动框架入门之LED

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

weixin_44705391的博客 1532

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

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

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

漫长当下 3805

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

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、付费专栏及课程。

余额充值