4.11 驱动开发

作业

3.1 实验现象

crw------- 1 root root 236, 0 Apr 11 15:55 /dev/myled0
crw------- 1 root root 236, 1 Apr 11 15:55 /dev/myled1
crw------- 1 root root 236, 2 Apr 11 15:55 /dev/myled2在串口工具,输入如下命令实现控制对应的LED灯进行工作echo 1 > /dev/myled0 ========> led1灯点亮echo 0 > /dev/myled0 ========> led1灯熄灭echo 1 > /dev/myled1 ========> led2灯点亮echo 0 > /dev/myled1 ========> led2灯熄灭echo 1 > /dev/myled2 ========> led3灯点亮echo 0 > /dev/myled2 ========> led3灯熄灭

3.2 编程思路

    struct inode {dev_t          i_rdev; //设备号};int myled_open(struct inode *inode, struct file *file)
{//获取次设备的值,将次设备号的值,通过私有数据传参,传递给write函数return 0;
}
--------------------------------------------------------------------------------
ssize_t myled_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{//判断私有数据的值,也就是次设备号的值,决定操作哪一盏灯return 0;     
}

3.3 编程要求

  • 分步注册字符设备驱动
  • 自动创建设备节点
  • 三盏灯地址映射
  • 三盏灯初始化
  • 私有数据传参
  • 用户空间和内核空间数据传输
  • 控制灯亮灭

demo.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/device.h>
#include "myled.h"struct cdev *cdev;
#if 1
unsigned int major = 0;
#else
unsigned int major = 500;
#endif
unsigned int minor = 0;
unsigned int count = 3;
#define CNAME "myled"
struct class *cls;
struct device *device;char kbuf[128] = "";
unsigned int *rcc_virt = NULL;
gpio_t *gpioe_virt = NULL;
gpio_t *gpiof_virt = NULL;#define LED1_ON (gpioe_virt->ODR |= (0x1 << 10))
#define LED1_OFF (gpioe_virt->ODR &= (~(0x1 << 10)))#define LED2_ON (gpiof_virt->ODR |= (0x1 << 10))
#define LED2_OFF (gpiof_virt->ODR &= (~(0x1 << 10)))#define LED3_ON (gpioe_virt->ODR |= (0x1 << 8))
#define LED3_OFF (gpioe_virt->ODR &= (~(0x1 << 8)))struct inode
{dev_t i_rdev; // 设备号
};int myled_open(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}ssize_t myled_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}ssize_t myled_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);// 如果用户空间写的数据大小,大于内核空间大小,需要更正写的大小if (size > sizeof(kbuf))size = sizeof(kbuf);ret = copy_from_user(kbuf, ubuf, size); // 将用户空间的数据,拷贝到内核空间if (ret){printk("copy form user is error\n");return -EIO;}// kbuf[0] = 0 ==> 操作LED1 ==> kbuf[1] = 0 灯熄灭 kbuf[1] = 1 灯点亮// kbuf[0] = 1 ==> 操作LED2 ==> kbuf[1] = 0 灯熄灭 kbuf[1] = 1 灯点亮// kbuf[0] = 2 ==> 操作LED3 ==> kbuf[1] = 0 灯熄灭 kbuf[1] = 1 灯点亮switch (kbuf[0]){case LED1:kbuf[1] == 1 ? LED1_ON : LED1_OFF;break;case LED2:kbuf[1] == 1 ? LED2_ON : LED2_OFF;break;case LED3:kbuf[1] == 1 ? LED3_ON : LED3_OFF;break;}return size; //!!!!!!!!!!!return 0;
}int myled_close(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
// 操作方法结构体
const struct file_operations fops = {.open = myled_open,.read = myled_read,.write = myled_write,.release = myled_close,
};// 入口函数
static int __init demo_init(void)
{int ret;int i;dev_t devno;// 分配对象cdev = cdev_alloc();if (cdev == NULL){printk("cdev alloc is error\n");ret = -ENOMEM;goto ERR1;}// 对象初始化cdev_init(cdev, &fops);if (major > 0){ // 静态指定设备号ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);if (ret){printk("register chrdev region is error\n");ret = -EIO;goto ERR2;}}else{ // 动态指定设备号ret = alloc_chrdev_region(&devno, minor, count, CNAME);if (ret){printk("alloc chrdev region is error\n");ret = -EIO;goto ERR2;}major = MAJOR(devno); // 通过设备号,获取到主设备号的值minor = MINOR(devno); // 通过设备号,获取到次设备号的值}// 注册ret = cdev_add(cdev, MKDEV(major, minor), count);if (ret){printk("cdev add is error\n");ret = -EIO;goto ERR3;}// 向上层提交目录信息cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)){ret = PTR_ERR(cls);goto ERR4;}for (i = 0; i < count; i++){// 向上层提交设备节点信息device = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);if (IS_ERR(device)){ret = PTR_ERR(device);goto ERR5;}}// 自动创建设备节点// 注册字符设备驱动major = register_chrdev(0, CNAME, &fops);if (major < 0){printk("register chrdev is error\n");return -EIO;}printk("major = %d\n", major); // 打印主设备号的值// 向上层提交目录信息cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)){return PTR_ERR(cls);}// 向上层提交设备节点信息device = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);if (IS_ERR(device)){return PTR_ERR(device);}//// rcc物理地址映射rcc_virt = ioremap(RCC_MP_AHB4ENSETR_PHY, 4);if (rcc_virt == NULL){printk("rcc ioremap is error\n");return -EIO;}// GPIOE组寄存器物理地址映射gpioe_virt = ioremap(GPIOE, sizeof(gpio_t));if (gpioe_virt == NULL){printk("gpioe ioremap is error\n");return -EIO;}// GPIOF组寄存器物理地址映射gpiof_virt = ioremap(GPIOF, sizeof(gpio_t));if (gpiof_virt == NULL){printk("gpiof ioremap is error\n");return -EIO;}// LED1灯初始化 PE10*rcc_virt |= (0x1 << 4);             // 使能GPIOE组控制器gpioe_virt->MODER &= (~(0x3 << 20)); // 设置PE10引脚为输出模式gpioe_virt->MODER |= (0x1 << 20);gpioe_virt->ODR &= (~(0x1 << 10)); // 设置PE10引脚输出低电平// LED2灯初始化 PF10*rcc_virt |= (0x1 << 5);             // 使能GPIOF组控制器gpiof_virt->MODER &= (~(0x3 << 20)); // 设置PF10引脚为输出模式gpiof_virt->MODER |= (0x1 << 20);gpiof_virt->ODR &= (~(0x1 << 10)); // 设置PF10引脚输出低电平// LED3灯初始化 PE8gpioe_virt->MODER &= (~(0x3 << 16)); // 设置PE8引脚为输出模式gpioe_virt->MODER |= (0x1 << 16);gpioe_virt->ODR &= (~(0x1 << 8)); // 设置PE8引脚输出低电平return 0;return 0; //!!!!!!!!!!!!!!!!!!ERR5:// 创建三个设备节点时,第一个设备节点和第二个设备节点创建成功,第三个设备节点创建失败// 需要将第一个设备节点和第二个设备节点创建成功,需要进行释放for (--i; i >= 0; i--){device_destroy(cls, MKDEV(major, i));}class_destroy(cls);
ERR4:cdev_del(cdev);
ERR3:unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:kfree(cdev);
ERR1:return ret;
}// 出口
static void __exit demo_exit(void)
{int i = 0;for (i = 0; i < count; i++){device_destroy(cls, MKDEV(major, i));}class_destroy(cls);cdev_del(cdev);unregister_chrdev_region(MKDEV(major, minor), count);kfree(cdev);// 取消地址映射iounmap(rcc_virt);iounmap(gpioe_virt);iounmap(gpiof_virt);// 注销字符设备驱动unregister_chrdev(major, CNAME);
}module_init(demo_init); // 指定入口地址
module_exit(demo_exit); // 指定出口地址
MODULE_LICENSE("GPL");  // 遵循GPL协议

test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <unistd.h>#include <string.h>
int main(int argc, const char *argv[])
{int fd = -1;char buf[128] = {};fd = open("/dev/myled",O_RDWR);if(fd == -1){perror("open is error");return -1;}while(1){buf[0] = 0;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufbuf[1] = 1;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufsleep(1);buf[1] = 0;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufsleep(1);buf[0] = 1;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufbuf[1] = 1;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufsleep(1);buf[1] = 0;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufsleep(1);buf[0] = 2;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufbuf[1] = 1;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufsleep(1);buf[1] = 0;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufsleep(1);}close(fd);return 0;
}

test.h

#ifndef __MYLED_H__
#define __MYLED_H__#define RCC_MP_AHB4ENSETR_PHY 0x50000A28enum{LED1,LED2,LED3,
};typedef struct {volatile unsigned int MODER;   // 0x00volatile unsigned int OTYPER;  // 0x04volatile unsigned int OSPEEDR; // 0x08volatile unsigned int PUPDR;   // 0x0Cvolatile unsigned int IDR;     // 0x10                                                                        volatile unsigned int ODR;     // 0x14volatile unsigned int BSRR;    // 0x18volatile unsigned int LCKR;    // 0x1C volatile unsigned int AFRL;    // 0x20 volatile unsigned int AFRH;    // 0x24volatile unsigned int BRR;     // 0x28volatile unsigned int res;volatile unsigned int SECCFGR; // 0x30}gpio_t;#define  GPIOE   (0x50006000)
#define  GPIOF   (0x50007000)#endif

不足之处:

1.分步注册字符设备驱动后是否需要再搞一次自动创建设备节点,再搞一次是否有意义?

2.如何获取串口输入的内容并将其存储在容器中传输到主机?

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

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

相关文章

链表创建的陷阱与细节

链表是线性表的一种&#xff0c;它在逻辑结构上是连续的&#xff0c;在物理结构上是非连续的。 也就是说链表在物理空间上是独立的&#xff0c;可能是东一块西一块的。如下顺序表和链表在内存空间上的对比&#xff1a; 而链表的每一块空间是如何产生联系实现在逻辑结构上是连续…

移动应用安全合规动态:网信办、金管局发文强调数据安全;3月个人信息违规抽查结果出炉!(第五期)

一、监管部门动向&#xff1a;国家互联网信息办公室公布《促进和规范数据跨境流动规定》; 工信部发布《关于网络安全保险典型服务方案目录的公示》 二、安全新闻&#xff1a;恶意软件警报&#xff01;黑客利用软件即服务攻击印度安卓用户&#xff1b;Cerberus银行恶意软件的虚…

第十四届蓝桥杯题解:平方差 ,更小的数,买瓜,网络稳定性(货车运输)

目录 平方差 更小的数 买瓜 网络稳定性&#xff08;货车运输&#xff09; 货车运输 平方差 这道题就是数论的题&#xff0c;不难想到一个数m可以拆成(a-b)(ab)&#xff0c;其实(a-b)和(ab)就是m的一对因子&#xff0c;不妨设为x和y。 则有&#xff1a; abx; a-by; x*ym; 联…

JVM—jps、jstat、jinfo、jmap、jstack的使用

JVM—jps、jstat、jinfo、jmap、jstack的使用 jps jps全称&#xff1a;Java Virtual Machine Process Status Tool 可以查看Java进程&#xff0c;相当于Linux下的ps命令&#xff0c;只不过它只列出Java进程。 jps:列出Jav程序ID和Main函数名称 jps -q:只输出进程ID jps -m …

MATLAB4:数值计算

文章目录 一、实验目的二、实验内容三、仿真结果四、实践中遇到的问题及解决方法 一、实验目的 1. 熟悉根据已知数据进行回归法曲线拟合。   2. 熟悉根据已知数据进行多项式曲线拟合。   3. 熟悉根据已知数据利用指定方法进行数据插值&#xff08;临近插值、线性插值、立方…

小程序视频如何下载到电脑上

小程序视频如何下载到电脑上&#xff0c;很简单 1.利用Fiddler和Charles这些专业的抓包工具 2.利用录屏 3.利用专门抓取资源的工具(集成了抓取下载&#xff0c;而且对资源下载很友好) 工具我已经打包好了 下载高手链接&#xff1a;https://pan.baidu.com/s/1qJ81sNBzzzU0w…

leetcode2924--找到冠军II

1. 题意 给定一个有向无环图&#xff0c;方向表示胜负关系&#xff1b;求最后胜出的人。 2. 题解 将所有人标记为胜者&#xff0c;统计出度去掉对应胜者标记&#xff1b; 最后统计胜者数目&#xff0c;是否大于1&#xff0c;若大于1&#xff0c;则没有胜者&#xff0c;否则…

千云物流 -influxdb了解和应用

简介 InfluxDB Cluster 是一个开源的 时间序列数据库,没有外部依赖。它对于记录指标、事件和执行分析很有用。 InfluxDB Cluster 启发于 InfluxDB Enterprise、InfluxDB v1.8.10 和 InfluxDB v0.11.1,旨在替代 InfluxDB Enterprise。 InfluxDB Cluster 易于维护,可以与上游…

Linux的学习之路:6、Linux编译器-gcc/g++使用

摘要 本文主要是说一些gcc的使用&#xff0c;g和gcc使用一样就没有特殊讲述。 目录 摘要 一、背景知识 二、gcc如何完成 1、预处理(进行宏替换) 2、编译&#xff08;生成汇编&#xff09; 3、汇编&#xff08;生成机器可识别代码 4、链接&#xff08;生成可执行文件或…

【我的代码生成器】生成React页面类

有了数据表的结构信息&#xff0c;就能生成React 的页面类&#xff0c;快捷方便。 生成界面如下&#xff1a; 生成的React FrmUser.js页面如下&#xff1a; 只需再写里面的操作逻辑代码。

银河麒麟之PaddleOCR模型部署

一、PaddleOCR简介 PaddleOCR是一个基于飞桨框架开发的开源OCR工具&#xff0c;提供了一系列强大的文本识别功能。PaddleOCR支持多种文本识别任务&#xff0c;包括文字检测、文字识别、文本方向检测等。它具有高效、准确的特点&#xff0c;适用于多种场景下的文本识别需求&…

Selenium - java - 屏幕截图

文档地址 Selenium 浏览器自动化项目 | Selenium 安装 <dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.19.1</version></dependency>使用 创建WebDriver实例 …

IDP之Backstage - 环境搭建

0. 目录 1. 前言2. 环境准备&#xff08;Windows10下&#xff09;2.1 安装nvm2.2 git和docker安装 3. 创建模板项目3.1 典型错误: fails on the yarn install step3.2 再次启动3.3 验证 4. 相关 1. 前言 本不想写这篇&#xff0c;因为看着官网文档写着挺简单的&#xff0c;但实…

禅道登录/创建版本/编辑版本接口 【禅道版本 20.0.beta2】

登录获取token import requestsurl = "http://192.168.1.234:6061/api.php/v1/tokens" payload = {"account": "jenkins","password": "jenkins123" } headers = {}response = requests.request

UI设计的未来发展

UI 设计的未来发展&#xff0c;实际上是互联网行业未来发展的折射。毕竟&#xff0c;UI 设计始终是互联网行业的一部分&#xff0c;因此在互联网行业未来发展的可能性来看&#xff0c;UI 设计同样会跟随着互联网的部分稳步前进。曾经&#xff0c;在最初的图形化界面出现的时候&…

线上剧本杀小程序发展趋势如何?

随着几年的快速发展&#xff0c;剧本杀行业进入到了大众的视野&#xff0c;不过从2022年开始&#xff0c;行业就开始处于下滑趋势&#xff0c;但是行业反而变得更加的规范化。 经历过下滑发展后&#xff0c;剧本杀行业已经趋向专业化、精品化发展&#xff0c;行业正在复苏&…

高基报表是什么?高校各部门如何快速填报高基表?

高基报表——全称是《高等教育事业基层统计报表》&#xff08;以下简称《高基报表》&#xff09;&#xff0c;作为高等院校基本情况报表的代表&#xff0c;承载着学校办学实力的真实写照。填报高基报表是一项细致入微的工作&#xff0c;不仅关系到学校的科学管理和决策研究&…

第一节:什么是操作系统

什么是操作系统 一、一台计算机的组成部分1、计算机能干啥2、谈谈计算机硬件 二、什么是操作系统三、学习操作系统的层次 一、一台计算机的组成部分 如下图所示&#xff1a; 这就是就是构成一台计算机的组成部分 1、计算机能干啥 ∙ \bullet ∙计算机是我们专业吃饭的家伙&a…

前端知识学习笔记-六(vue)

简介 Vue是前端优秀框架是一套用于构建用户界面的渐进式框架 Vue优点 Vue是目前前端最火的框架之一 Vue是目前企业技术栈中要求的知识点 vue可以提升开发体验 Vue学习难度较低 Vue开发前准备 一、nodejs环境 Nodejs简介 Nodejs诞生于2009年&#xff0c;主攻服务器方向&#x…

Spring Message

EnableJms注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(JmsBootstrapConfiguration.class) public @interface EnableJms { } @Import(JmsBootstrapConfiguration.class) @Configuration @Role(BeanDefinition.ROLE_INFRASTRUC…