项目名称:驱动开发(控LED灯,控制蜂鸣器)

news2024/11/26 21:34:06

一,简述

(1)Linux系统组成

()app:                                                [0-3G]

---------------------------------系统调用(软中断)---------------------

kernel:                                         【3-4G】

5种功能:

进程管理:进程的创建、销毁、调度等功能

文件管理:通过文件系统ext2/ext3/ext4  yaff  jiffs等来组织管理文件

网络管理 :通过网络协议栈对数据进程封装和拆解的过程

内存管理 :通过内存管理器对用户空间和内核空间内存的申请和释放

设备管理:设备驱动的管理

字符设备驱动:

  1. 按照字节为单位进行访问,顺序访问
  2. 会创建设备文件,open read write close来访问

       块设备驱动:

  1. 按照块(512字节)(扇区)来访问,可以顺序访问,可以无序访问
  2. 会创建设备文件,open read write close来访问

       网卡设备驱动:按照网络数据包来收发的

(2)驱动移植

  1. 需要有一个驱动对应的 .c代码
  2. 把.c文件放入到对应的文件夹内(char)
  3. 修改Makefile-》添加上自己代码编译生成的.o文件-》保存退出
  4. 修改Kconfig生成自己的菜单  (3和4根据其他的仿写)-》保存退出
  5. 到顶层目录执行make menuconfig-》配置自己的驱动(M)
  6. 编译-》make modules
  7. 找到生成的.ko文件安装(insmod lcd.ko )

编译:

make uImage-->uImage(包含了新的驱动的内核)

make modules -->demo.ko

Makefile  modules:

编译模块的规则

    Y(要编译到内核中)  M(编译生成模块)  N(不编译驱动)

sudo insmod demo.ko  安装驱动

sudo rmmod  demo     卸载驱动

静态编译:编译之后生成的可执行程序可以单独执行

动态编译:编译之后生成的可执行程序需要依赖其他内核才能执行

内部编译:在内核源码树中编译

外部编译:在内核源码树外编译

(3)驱动模块

入口(安装):资源的申请

出口(卸载):资源的释放

许可证:GPL

#include <linux/init.h>
#include <linux/module.h>                                                                          
static int __init  hello_init(void) 
//__init将hello_in it放到.init.text段中
{
	return 0;
} 
static void __exit hello_exit(void)
		//__exit将hello_exit放到.exit.text段中
{
}
module_init(hello_init);
//告诉内核驱动的入口地址
module_exit(hello_exit);
//告诉内核驱动的出口地址
MODULE_LICENSE("GPL");
//许可证

(4)字符设备驱动

int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)

功能:注册一个字符设备驱动

参数:

@major:主设备号  

 :如果你填写的值大于0,它认为这个就是主设备号

 :如果你填写的值为0,操作系统给你分配一个主设备号   

@name :名字    cat /proc/devices 查看设备名和主设备号

@fops :操作方法结构体

返回值:major>0 ,成功返回0,失败返回错误码(负数) (vi -t EIO 可以查看错误码)

major=0,成功主设备号,失败返回错误码(负数)      

void unregister_chrdev(unsigned int major, const char *name)

功能:注销一个字符设备驱动

参数:

major:主设备号

name:名字

返回值:无

二,相关知识点

应用程序如何将数据传递给驱动(读写方向站在用户角度)

int copy_from_user(void *to, const void __user *from, int n)

功能:从用户空间拷贝数据到内核空间

参数:

  to:内核中内存的首地址  from:用户空间的首地址  n:拷贝数据的长度

返回值:成功返回0,失败返回未拷贝的字节个数

int copy_to_user(void __user *to,const void *from,int n)

功能:从内核空间拷贝数据到用户空间

参数:

to:用户空间内存的首地址 from:内核空间的首地址 n:拷贝数据的长度

返回值:成功返回0,失败返回未拷贝的字节个数

控制LED灯:

驱动如何操作寄存器

rgb_led灯的寄存器是物理地址,在linux内核启动之后,

在使用地址的时候,操作的全是虚拟地址。需要将物理地址

转化为虚拟地址。在驱动代码中操作的虚拟地址就相当于

操作实际的物理地址。

物理地址<------>虚拟地址

void * ioremap(phys_addr_t offset, unsigned long size)

功能:将物理地址映射成虚拟地址

参数:

@offset :要映射的物理地址

@size   :大小(字节)

返回值:成功返回虚拟地址,失败返回NULL; 

void iounmap(void  *addr)

功能:取消映射

参数:

@addr :虚拟地址

返回值:无

RGB_led 

red  :gpioa28

GPIOXOUT   :控制高低电平的   0xC001A000

GPIOxOUTENB:输入输出模式    0xC001A004

GPIOxALTFN1:function寄存器  0xC001A024

green:gpioe13

0xC001e000

blue :gpiob12

0xC001b000

指针类型加1是加的他的类型大小

驱动控制灯

 三,代码开发

驱动层

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/string.h>
#include <linux/device.h>

#define RED_BASE 0xC001A000 //红灯基地址
#define BLUE_BASE 0xC001B000 //蓝灯基地址
#define GREEN_BASE 0xC001E000 //绿灯基地址
#define FMQ_BASE 0xC001C000 //蜂鸣器基地址

unsigned int *red_base = NULL;//申请的红灯虚拟地址
unsigned int *blue_base = NULL;//申请的蓝灯虚拟地址
unsigned int *green_base = NULL;//申请的绿灯虚拟地址
unsigned int *fmq_base = NULL;//申请的蜂鸣器虚拟地址

#define CNAME "hello"//设备名称
int major = 0;//设备号
int r_led=0,b_led=0,g_led=0;
char kbuf[128] = {0};//数据缓存区
struct class *cls;//自动创建设备节点 目录返回
struct device *dev;//自动创建设备节点 信息返回
int mycdev_open(struct inode *inode, struct file *file)//自己的open函数
{
	printk("open\n");//应用层使用open函数时会打印这句信息
	return 0;
}
ssize_t mycdev_read(struct file *file, char __user *ubuf,//自己写的read函数
	size_t size, loff_t *offs)
{
	memset(kbuf,0,sizeof(kbuf));
	int ret;
	printk("read is 111\n");//应用层使用read函数时会打印这句信息
//红灯
	if(r_led){
		strcat(kbuf,"red_led_open;");
	}else{
		strcat(kbuf,"red_led_close;");
	}

//蓝灯
	if(b_led){
		strcat(kbuf,"blue_led_open;");
	}else{
		strcat(kbuf,"blue_led_close;");
	}
//绿灯
	if(g_led){
		strcat(kbuf,"green_led_open;");
	}else{
		strcat(kbuf,"green_led_close;");
	}
	if(size > sizeof(kbuf))
		size = sizeof(kbuf);//判断应用层传来的数据是不是大于咱们自己声明的数组长度,如果大于给转成128

	ret=copy_to_user(ubuf,kbuf,size);//将驱动层kbuf内数据读到应用层
	if(ret){ //如果错误进入
		printk("copy  to user error\n");
		return -EINVAL;//返回错误提示
	}

	return size;
}
ssize_t mycdev_write(struct file *file, const char __user *ubuf,
	size_t size, loff_t *offs)
{
	int ret;
	printk("this is write\n");//应用层使用write函数时会打印这句信息
	if(size > sizeof(kbuf)) 
		size = sizeof(kbuf);//判断应用层传来的数据是不是大于咱们自己声明的数组长度,如果大于给转成128
	ret=copy_from_user(kbuf,ubuf,size);//将应用层ubuf内数据写到驱动层
	if(ret){
		printk("copyfrom user error\n");
		return -EINVAL;
	}
//红灯
	if(strncmp(kbuf,"11",2)==0){//灭灯
		
		*red_base &= ~(1<<28);
		r_led=0;
	}
	if(strncmp(kbuf,"22",2)==0)//亮灯
	{
		
		*red_base |= (1<<28);
		r_led=1;
	}
//蓝灯
	if(strncmp(kbuf,"33",2)==0){
		//灭灯
		*blue_base &= ~(1<<12);
		b_led=0;
	}
	if(strncmp(kbuf,"44",2)==0)
	{
		//亮灯
		*blue_base |= (1<<12);
		b_led=1;
	}
//绿灯
	if(strncmp(kbuf,"55",2)==0){
		//灭灯
		*green_base &= ~(1<<13);
		g_led=0;
	}
	if(strncmp(kbuf,"66",2)==0)
	{
		//亮灯
		*green_base |= (1<<13);
		g_led=1;
	}
//蜂鸣器
	if(strncmp(kbuf,"77",2)==0){
		//蜂鸣器关
		*fmq_base &= ~(1<<14);
	}
	if(strncmp(kbuf,"88",2)==0)
	{
		//蜂鸣器开
		*fmq_base |= (1<<14);

	}
	return size;
}
int mycdev_close(struct inode *inode, struct file *file)
{
	printk("close");
	//应用层使用close函数时会打印这句信息
	return 0;
}

const struct file_operations fops = {//这个结构体为咱们自己写的,在注册驱动时被调用,这是APP层能调用驱动层上面咱们自己的的程序的关键
	.open    = mycdev_open,//将自己写的open函数给到API内
	.read    = mycdev_read,//将自己写的read函数给到API内
	.write   = mycdev_write,//将自己写的write函数给到API内
	.release = mycdev_close,//将自己写的close函数给到API内
};

static int __init mycdev_init(void)
{
	//注册字符设备驱动
	major = register_chrdev(major,CNAME,&fops);
	if(major < 0){
		printk("register device error\n");//注册失败打印
		return major;
	}

	red_base = ioremap(RED_BASE,36);//物理地址转换虚拟地址 红灯GPIOa28
	if(red_base == NULL){//转换失败提示
		printk("red ioremap error\n");
		return -ENOMEM;
	}
	blue_base = ioremap(BLUE_BASE,36);//物理地址转换虚拟地址 
	if(blue_base == NULL){//转换失败提示
		printk("blue ioremap error\n");
		return -ENOMEM;
	}
	green_base = ioremap(GREEN_BASE,36);//物理地址转换虚拟地址
	if(green_base == NULL){//转换失败提示
		printk("green ioremap error\n");
		return -ENOMEM;
	}
	fmq_base = ioremap(FMQ_BASE,36);//物理地址转换虚拟地址
	if(fmq_base == NULL){//转换失败提示
		printk("fmq ioremap error\n");
		return -ENOMEM;
	}
//红灯部分配置
	*red_base &= ~(1<<28);//配置为灭灯
	*(red_base+1) |= (1<<28);//配置为输出模式
	*(red_base+9) &= ~(3<<24);//配置为GPIO功能,基地址+9,16进制36是24
//蓝灯部分配置
	*blue_base &= ~(1<<12);
	*(blue_base+1) |= (1<<12);
	*(blue_base+8) &= ~(3<<24);
	*(blue_base+8) |= (1<<25);
//绿灯部分配置
	*green_base &= ~(1<<13);
	*(green_base+1) |= (1<<13);
	*(green_base+8) &= ~(3<<26);
//蜂鸣器部分配置
	*fmq_base &= ~(1<<14);
	*(fmq_base+1) |= (1<<14);
	*(fmq_base+8) &= ~(3<<28);
	*(fmq_base+8) |= (1<<28);

	//自动创建设备节点
	cls = class_create(THIS_MODULE,CNAME);//自动创建设备节点目录
	if(IS_ERR(cls)){//IS_ERR可以将cls 错误码指针转换成错误码
		printk("class create error\n");
		return PTR_ERR(cls);//失败返回错误码
	}

	dev = device_create(cls,NULL,MKDEV(major,0),NULL,CNAME);//自动创建设备节点信息
	if(IS_ERR(dev)){
		printk("device create error\n");
		return PTR_ERR(dev);//失败返回错误码
	}
	
	return 0;
}

static void __exit mycdev_exit(void)//出口
{
	device_destroy(cls,MKDEV(major,0));//注释掉自动创建节点 信息
	class_destroy(cls);//注释掉自动创建节点 目录
	iounmap(green_base);//注销掉虚拟地址,注意后申请的先注销
	iounmap(blue_base);
	iounmap(red_base);
	
	//注销字符设备驱动
	unregister_chrdev(major,CNAME);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");


应用层(控制LED灯,蜂鸣器)


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

char buf[6] = {0};//数据缓存

int main(int argc, const char *argv[])
{
	int fd;
	
	fd = open("./hello",O_RDWR);//打开文件
	if(fd == -1){
		perror("open error");//打开错误提示
		return -1;
	}
	while(1){
		write(fd,buf,sizeof(buf));//将buf写入驱动中用于控制LED亮灭
		sleep(1);//延时
		
		//buf[0] = buf[0]?0:1;//如果buf[0]为0则赋值1,不为赋值0
//红灯
	if(strncmp(buf,"11",2)==0){//灭灯
		
		printf("红灯灭");
		
	}
	else if(strncmp(buf,"22",2)==0)//亮灯
	{
		
		printf("红灯亮");
	}
//蓝灯
	else if(strncmp(buf,"33",2)==0){
		//灭灯
		printf("蓝灯灭")
	}
	else if(strncmp(buf,"44",2)==0)
	{
		//亮灯
		printf("蓝灯亮");
	}
//绿灯
	else if(strncmp(buf,"55",2)==0){
		//灭灯
		printf("绿灯灭");
	}
	else if(strncmp(buf,"66",2)==0)
	{
		//亮灯
		printf("绿灯亮");
	}
//蜂鸣器
	else if(strncmp(buf,"77",2)==0){
		
		printf("蜂鸣器关");
		
	}
	else if(strncmp(buf,"88",2)==0)
	{
		printf("蜂鸣器开");

	}
	}

	close(fd);//关闭

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/780476.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python补充笔记3-bug问题

目录 一、Bug 粗心导致的语法错误​ ​编辑 知识不熟练导致的错误​ 思路不清晰导致的问题​ 被动掉坑​ 二、try…except…else结构​ 三、try…except…else…finally结构​ 四、常见异常类型​编辑traceback模块 pycharm调试 一、Bug 粗心导致的语法错误 知识不熟练导致的…

C++进阶:c++11

C11 相比于C98&#xff0c;C11则带来了数量可观的变化&#xff0c;以及对C03缺陷的修正。C11语法更加泛化简单化、更加稳定安全&#xff0c;功能更强大&#xff0c;提升开发效率。 cpp11 1. 列表初始化 C11扩大了用{}&#xff08;初始化列表&#xff09;的使用范围&#xff…

Spring Cloud+Uniapp+企业工程管理系统源码之提高工程项目管理软件的效率

高效的工程项目管理软件不仅能够提高效率还应可以帮你节省成本提升利润 在工程行业中&#xff0c;管理不畅以及不良的项目执行&#xff0c;往往会导致项目延期、成本上升、回款拖后&#xff0c;最终导致项目整体盈利下降。企企管理云业财一体化的项目管理系统&#xff0c;确保…

Mac端虚拟定位 AnyGo mac中文 6.2.1

AnyGo for Mac是一款一键将iPhone的Gps位置更改为任何位置的强大软件&#xff01;使用AnyGo在其iOS或Android设备上改变其Gps位置&#xff0c;并在任何想要的地方显示自己的位置。这对那些需要测试应用程序、游戏或其他依赖于地理位置信息的应用程序的开发人员来说非常有用&…

C# List 详解六

目录 35.MemberwiseClone() 36.Remove(T) 37.RemoveAll(Predicate) 38.RemoveAt(Int32) 39.RemoveRange(Int32, Int32) 40.Reverse() 41.Reverse(Int32, Int32) C# List 详解一 1.Add(T)&#xff0c;2.AddRange(IEnumerable)&#xff0c;3…

【SpringCloud Alibaba】(一)微服务介绍

此专栏内容皆来自于【冰河】的《SpringCloud Alibaba 实战》文档。 1. 专栏介绍 我们先来看看《SpringCloud Alibaba实战》专栏的整体结构吧&#xff0c;先上图 从上图&#xff0c;大家可以看到&#xff0c;专栏从整体上分为十个大的篇章&#xff0c;分别为 专栏设计、微服务…

MGER-OSPF的LSA-OSPF的优化 综合实验报告

题目&#xff1a; 步骤一&#xff1a;拓扑设计&#xff0c;地址规划 地址规划&#xff1a; 有题意知&#xff1a;整个OSPF环境基于172.16.0.0/16划分。则据提意划分出子网掩码长度为20的&#xff0c;十六个网段&#xff0c;如下&#xff1a; 骨干链路&#xff1a;使用172.16.…

Appium+python自动化(十九)- Monkey(猴子)参数(超详解)

前边几篇介绍了Monkey以及Monkey的事件&#xff0c;今天就给小伙伴们介绍和分享一下Monkey的参数。 首先我们看一下这幅图来大致了解一下&#xff1a; 1、Monkey 命令 基本参数介绍 -p <允许的包名列表> 用此参数指定一个或多个包。指定包之后&#xff0c;mon…

18 常用控件--按钮组

QPushButton 可以显示图标QToolButton 工具按钮 可以显示图标 可以设置透明效果QRadioButton 单选按钮QCheckBox 多选按钮 多个单选按钮可以用GroupBox分组 按钮可以设置默认选中状态&#xff0c;多选按钮可以设置半选状态 代码&#xff1a; //widget.h #ifndef WIDGET_H #def…

Llama2开源大模型的新篇章以及在阿里云的实践

Llama一直被誉为AI社区中最强大的开源大模型。然而&#xff0c;由于开源协议的限制&#xff0c;它一直不能被免费用于商业用途。然而&#xff0c;这一切在7月19日发生了改变&#xff0c;当Meta终于发布了大家期待已久的免费商用版本Llama2。Llama2是一个由Meta AI开发的预训练大…

前端工程师的岗位职责(合集)

篇一 岗位职责&#xff1a; 1、负责网站前端开发&#xff0c;实现产品的页面交互及功能实现; 2、与程序开发人员紧密合作&#xff0c;制作前端及后端程序接口标准; 3、完成产品的设计、开发、测试、修改bug等工作&#xff0c;包括业务需求的沟通&#xff0c;功能模块详细设计…

防火墙入门指南:了解防火墙的基础知识

目录 防火墙&#xff08;四层设备&#xff09; 1.1防火墙是什么 1.2 防火墙是如何诞生的 1.2.1包过滤防火墙----访问控制列表技术---三层技术 1.2.2代理防火墙----中间人技术---应用层 1.2.3状态防火墙---会话追踪技术---三层、四层 1.2.4UTM---深度包检查技术----应用层…

element-ui动态编辑标签

点击叉叉&#xff0c;标签消失&#xff0c;点击New Tag&#xff0c;显示输入框&#xff0c;输入完成后生成标签&#xff0c;并且出现New Tag标签。 代码&#xff1a; <el-tag:key"tag"v-for"tag in dynamicTags"closable:disable-transitions"fa…

【Hive 01】简介、安装部署、高级函数使用

1 Hive简介 1.1 Hive系统架构 Hive是建立在 Hadoop上的数据仓库基础构架&#xff0c;它提供了一系列的工具&#xff0c;可以进行数据提取、转化、加载&#xff08; ETL &#xff09;Hive定义了简单的类SQL查询语言&#xff0c;称为HQL&#xff0c;它允许熟悉SQL的用户直接查询…

redux源码阅读总结(一)- createStore.js详细解析与思考

redux数据流分析 在阅读redux源码之前&#xff0c;先整理一下redux的数据流&#xff0c;官网的数据流程图如下所示。该图十分清晰明了的展示了redux的数据流&#xff1a; 点击UI&#xff0c;发起一个存钱的点击事件。在点击事件处理函数中&#xff0c;dispatch分发一个action…

AE 3D粒子插件trapcode particular 新版本

Trapcode Particular for Mac是目前AE系列的插件中最火爆最流行的一款三维粒子插件&#xff0c;是属于Red Giant Trapcode Suite&#xff08;红巨人粒子特效套装插件&#xff09;中的一款粒子插件。该软件提供了多达一百余种粒子效果供用户使用&#xff0c;可以产生各种各样的自…

【算法基础:搜索与图论】3.4 求最短路算法(Dijkstrabellman-fordspfaFloyd)

文章目录 求最短路算法总览Dijkstra朴素 Dijkstra 算法&#xff08;⭐原理讲解&#xff01;⭐重要&#xff01;&#xff09;&#xff08;用于稠密图&#xff09;例题&#xff1a;849. Dijkstra求最短路 I代码1——使用邻接表代码2——使用邻接矩阵 补充&#xff1a;稠密图和稀疏…

(Chrome Ext)谷歌扩展程序-谷歌插件渗透测试方法记录

文章目录 前言一、本地获取谷歌插件/扩展程序源码二、工具化信息收集总结 前言 在工作岗位变更之后&#xff0c;越来越多“奇奇怪怪”的东西要去渗透和测试&#xff0c;在我之前干安服的时候&#xff0c;最多的就是测一下web&#xff0c;极少情况下测测app&#xff0c;但是现在…

信息与通信工程学科面试准备——信息论与编码|保研推免面试题

目录 第一章 绪论 1 信息的概念 1.1 香农对信息的定义 1.2 信息与消息之间的关系&#xff1f; 2 信息的性质 3 信息的分类 4 信息论与编码研究的主要内容 (1)狭义信息论 (2)一般信息论 (3)广义信息论 5 信息论与编码的发展和应用 6 通信系统模型构成 (1)信源 (2)…

微信小程序-----input数据双向绑定

简介&#xff1a; 这里介绍两种获取的方式&#xff1a; 1、用户每输入一个字节就获取一个字节 2、用户全部输入结束了之后&#xff0c;再一起获取整个input输入框的值 注意&#xff1a;第二种方式会比较节省内存资源 第一种方式: 原理&#xff1a;我们使用bindinput事件来获取…