嵌入式:驱动开发 Day4

2023-11-14

作业:通过字符设备驱动分步注册方式编写LED驱动,完成设备文件和设备的绑定

驱动程序:myled.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/io.h>
#include "head.h"

char kbuf[128] = "";
struct cdev *cdev;
unsigned int major = 0;
unsigned int minor = 0;
dev_t devno;
module_param(major, uint, 0664); //方便命令行传递major的值
struct class *cls;
struct device *dev;

gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;

//封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    int min = MINOR(inode->i_rdev); //根据打开的文件对应的设备号获取次设备号
    file->private_data = (void *)min;
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int min = (int)file->private_data; //传递次设备号

    //设备文件和设备的绑定,根据次设备号决定打开的设备文件,根据打开的设备文件,控制相应的设备
    switch (min)
    {
    case 0: //控制LED1
        switch (cmd)
        {
        case LED_ON: //开灯
            vir_led1->ODR |= (0x1 << 10);
            break;
        case LED_OFF: //关灯
            vir_led1->ODR &= (~(0x1 << 10));
            break;
        }
        break;
    case 1: //控制LED2
        switch (cmd)
        {
        case LED_ON: //开灯
            vir_led2->ODR |= (0x1 << 10);
            break;
        case LED_OFF: //关灯
            vir_led2->ODR &= (~(0x1 << 10));
            break;
        }
        break;
    case 2: //控制LED3
        switch (cmd)
        {
        case LED_ON: //开灯
            vir_led3->ODR |= (0x1 << 8);
            break;
        case LED_OFF: //关灯
            vir_led3->ODR &= (~(0x1 << 8));
            break;
        }
        break;
    }
    return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{

    return 0;
}

ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{

    return 0;
}

int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

//定义一个操作方法结构体对象并初始化
struct file_operations fops = {
    .open = mycdev_open,
    .unlocked_ioctl = mycdev_ioctl,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};

int all_led_init(void)
{

	//进行相关寄存器的内存映射
	//GPIOE组寄存器内存映射
	vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
	if (vir_led1 == NULL)
	{
		printk("物理内存映射失败%d\n", __LINE__);
		return -EFAULT;
	}

	vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
	if (vir_led2 == NULL)
	{
		printk("物理内存映射失败%d\n", __LINE__);
		return -EFAULT;
	}

	vir_led3 = vir_led1;

	vir_rcc = ioremap(PHY_RCC_ADDR, 4);
	if (vir_rcc == NULL)
	{
		printk("物理内存映射失败%d\n", __LINE__);
		return -EFAULT;
	}

	printk("寄存器内存映射成功\n");

	//硬件寄存器的初始化
	//RCC使能
	(*vir_rcc) |= (0x3 << 4); //GPIOE(PE10 PE8)使能 GPIOF(PF10)使能

	//设置PE10为输出模式 --> LED1
	(vir_led1->MODER) &= (~(0x3 << 20));
	(vir_led1->MODER) |= (0x1 << 20);
	//设置PF10为输出模式 --> LED2
	(vir_led2->MODER) &= (~(0x3 << 20));
	(vir_led2->MODER) |= (0x1 << 20);
	//设置PE8为输出模式 --> LED3
	(vir_led3->MODER) &= (~(0x3 << 16));
	(vir_led3->MODER) |= (0x1 << 16);

	//默认LED1关灯
	(vir_led1->ODR) &= (~(0x1 << 10));
	//默认LED2关灯
	(vir_led2->ODR) &= (~(0x1 << 10));
	//默认LED1关灯
	(vir_led3->ODR) &= (~(0x1 << 8));
	printk("寄存器初始化成功\n");

	return 0;
}

static int __init mycdev_init(void)
{
    //寄存器内存地址映射以及初始化
	all_led_init();
    
    int ret;
    //1.为字符设备驱动对象申请空间
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("申请字符设备驱动对象空间失败\n");
        ret = -EFAULT;
        goto out1;
    }
    printk("申请字符设备驱动对象空间成功\n");

    //2.初始化字符设备驱动对象
    cdev_init(cdev, &fops);
    printk("初始化字符设备驱动对象成功\n");

    //3.申请设备号
    if (major > 0) //静态指定设备号
    {
        ret = register_chrdev_region(MKDEV(major, minor), 3, "myled");
        if (ret)
        {
            printk("静态申请设备号失败\n");
            goto out2;
        }
    }
    else if (major == 0)
    { //动态申请设备号
        ret = alloc_chrdev_region(&devno, minor, 3, "myled");
        if (ret)
        {
            printk("动态申请设备号失败\n");
            goto out2;
        }
        major = MAJOR(devno); //获取主设备号
        minor = MINOR(devno); //获取次设备号
    }
    printk("申请设备号成功 major= %d\n", major);

    //4.注册字符设备驱动对象
    ret = cdev_add(cdev, MKDEV(major, minor), 3);
    if (ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out3;
    }
    printk("注册字符设备驱动对象成功\n");

    //向上提交目录信息
    cls = class_create(THIS_MODULE, "myled");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        ret = -PTR_ERR(cls);
        goto out4;
    }
    printk("向上提交目录成功\n");

    //向上提交设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            ret = -PTR_ERR(dev);
            goto out5;
        }
    }
    printk("向上提交设备信息成功\n");
    return 0;

out5:
    //释放前一次提交成功的设备信息
    for (--i; i >= 0; i--)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls); //释放目录
out4:
    //注销字符设备驱动对象
    cdev_del(cdev);
out3:
    //释放设备号
    unregister_chrdev_region(MKDEV(major, minor), 3);
out2:
    //释放设备驱动对象空间
    kfree(cdev);
out1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    int i;
    //释放设备节点信息
    for (i = 0; i < 0; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    //销毁目录
    class_destroy(cls);
    //注销字符设备驱动对象
    cdev_del(cdev);
    //释放设备号
    unregister_chrdev_region(MKDEV(major, minor), 3);
    //释放设备驱动对象空间
    kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

应用程序:test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include "head.h"

int main()
{
	int a, b;
	char buf[128] = "";
	int fd1, fd2, fd3;
	while (1)
	{
		printf("请输入要控制的灯:0(LED1) 1(LED2) 2(LED3) >");
		scanf("%d", &a);
		switch (a)
		{
		case 0:
			fd1 = open("/dev/myled0", O_RDWR);
			if (fd1 < 0)
			{
				printf("设备文件打开失败\n");
				exit(-1);
			}
			printf("请输入对LED的控制命令:0(关灯) 1(开灯)  >");
			scanf("%d", &b);
			switch (b)
			{
			case 0:
				ioctl(fd1, LED_OFF); //关灯
				close(fd1);
				break;
			case 1:
				ioctl(fd1, LED_ON); //开灯
				close(fd1);
				break;
			}
			break;

		case 1:
			fd2 = open("/dev/myled1", O_RDWR);
			if (fd2 < 0)
			{
				printf("设备文件打开失败\n");
				exit(-1);
			}
			printf("请输入对LED的控制命令:0(关灯) 1(开灯)  >");
			scanf("%d", &b);
			switch (b)
			{
			case 0:
				ioctl(fd2, LED_OFF); //关灯
				close(fd2);
				break;
			case 1:
				ioctl(fd2, LED_ON); //开灯
				close(fd2);
				break;
			}
			break;

		case 2:
			fd3 = open("/dev/myled2", O_RDWR);
			if (fd3 < 0)
			{
				printf("设备文件打开失败\n");
				exit(-1);
			}
			printf("请输入对LED的控制命令:0(关灯) 1(开灯)  >");
			scanf("%d", &b);
			switch (b)
			{
			case 0:
				ioctl(fd3, LED_OFF); //关灯
				close(fd3);
				break;
			case 1:
				ioctl(fd3, LED_ON); //开灯
				close(fd3);
				break;
			}
			break;
		}
	}

	close(fd1);
	close(fd2);
	close(fd3);

	return 0;
}

头文件:head.h

#ifndef __HEAD_H__
#define __HEAD_H__

typedef struct{
    unsigned int MODER;
    unsigned int OTYPER;
    unsigned int OSPEEDR;
    unsigned int PUPDR;
    unsigned int IDR;
    unsigned int ODR;
}gpio_t;

#define PHY_LED1_ADDR 0x50006000
#define PHY_LED2_ADDR 0x50007000
#define PHY_LED3_ADDR 0x50006000
#define PHY_RCC_ADDR 0x50000A28

//构建LED开关的功能码,不添加ioctl第三个参数
#define LED_ON _IO('l', 1)
#define LED_OFF _IO('l', 0)

#endif

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌入式:驱动开发 Day4 的相关文章

  • 驱动的调用

    目录 设备文件 编辑 测试驱动 读写回环测试 步骤 源文件 详细的讲解看注释即可 应用和驱动之间的数据交换 在应用层调用open来打开这个系统文件 在向这个设备文件使用read write等即可调用驱动的函数去工作 设备文件 设备文件连接着
  • 韦东山嵌入式教程第四篇Linux驱动基础知识学习笔记(1)——Hello驱动程序

    驱动入门 Hello驱动程序 1 怎么写出一个驱动程序 确定主设备号 定义自己的file operations结构体 实现对应的open read write函数 填入file operations结构体 实现入口函数 安装驱动程序时 就会
  • DDK(Driver Developer Kit)和WDK(Windows Driver Kit)的区别

    首先 先从基础的东西说起 开发WINDOWS下的驱动程序 需要一个专门的开发包 如 开发JAVA程序 我们可能需要一个JDK 开发WINDOWS应用程序 我们需要WINDOWS的SDK 现在开发WINDOWS下的驱动程序 我们需要一个DDK
  • 嵌入式linux驱动之路19:U-Boot 移植(2)

    uboot 的最终目的就是启动 Linux 内核 所以需要通过启动 Linux 内核来判断 uboot 移植是否成功 在启动 Linux 内核之前我们先来学习两个重要的环境变量 bootcmd 和 bootargs bootcmd 环境变量
  • 微星b560m mortar wifi 开机二检(开机灭了1秒后自动重启)

    微星b560m mortar wifi 开机二检 开机灭了1秒后自动重启 1 起因 为了照顾旧CPU 还想把内存扩容到64GB 就选择了DDR4的4条插槽的 B560M主板 都弄好之后 开机发现在按下电源键之后 通电自检后断电了大概2秒左右
  • OrangePIPC2---uboot flash的适配

    下载uboot源码 去我的github上下载源码 或者官方uboot都行 由于我还没装git所以先临时下载用用 解压 unzip XXX zip即可 编译 export CROSS COMPILE aarch64 linux gnu mak
  • Linux SPI 驱动实验

    目录 Linux 下SPI 驱动框架简介 SPI 主机驱动 SPI 设备驱动 SPI 设备和驱动匹配过程 I MX6U SPI 主机驱动分析 SPI 设备驱动编写流程 SPI 设备信息描述 SPI 设备数据收发处理流程 硬件原理图分析 试验
  • Linux驱动_多点电容触摸

    一丶Linux下多点电容触摸驱动框架 电容触摸屏IC是FT5426 为IIC协议芯片 因此需要编写IIC驱动 触摸IC会发出中断信号 并在中断服务函数中上报信息 因此需要编写中断框架 触摸屏向Linux内核上报的信息都属于Input子系统
  • STM32寄存器

    问题 什么是寄存器 什么是存储器映射 什么是寄存器映射 STM32架构 程序存放在FLASH中 const的常量存放在FLASH中 变量 全局 静态变量 存放在SRAM中 System总线主要读取寄存器 AHB 高速 总线上挂着SDIO 复
  • 异步通知实验(信号)

    目录 异步通知 异步通知简介 驱动中的信号处理 应用程序对异步通知的处理 硬件原理图分析 实验程序编写 修改设备树文件 程序编写 编写测试APP 运行测试 编译驱动程序和测试APP 运行测试 在前面使用阻塞或者非阻塞的方式来读取驱动中按键值
  • 驱动学习(六)ioctl

    驱动学习 六 ioctl 文章目录 驱动学习 六 ioctl 1 ioctl 2 命令码 2 1 自定义命令码 2 2 标准命令码 2 2 1 合成标准命令码的宏函数 3 测试ioctl linux内核给用户提供了两类系统调用函数 一类是数
  • 擎创技术流

    上期跟大家聊了下eBPF的发展历史还有特性 点击这里 擎创技术流 深入浅出运维可观测工具 一 聊聊eBPF的前世今生 一键回看上期精彩内容 这期主要跟大家分享下eBPF在应用过程中可能出现的问题 希望能帮到遇到类似问题的朋友 话不多说 我们
  • Android HAL 层框架分析(一)

    作为一个搞android驱动或者说搞底层的人 我觉得对于hal那是必须要掌握的 而且必须达到一定深度 于是我总结了一下 将整个自己的分析思路写下来 主要是看android源代码 根据源代码得到的思路 看源代码比看什么著作书籍都管用 andr
  • Linux驱动

    Linux驱动入门系列 Linux驱动入门 一 字符设备驱动基础 Linux驱动入门 二 操作硬件 Linux驱动入门 三 Led驱动 Linux驱动入门 四 非阻塞方式实现按键驱动 Linux驱动入门 五 阻塞方式实现按键驱动 Linux
  • STM32 CAN通信理解(是半双工还是全双工?)

    STM32F429 CAN通信 CAN 是控制器局域网络 Controller Area Network 的简称 它是由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的 并最终成为国际标准 ISO11519 是国际上应用最广泛的现场
  • windows内核驱动开发(WDK环境搭建)

    去官网下载WDK安装包和Visual Studio 下载 Windows 驱动程序工具包 WDK Windows drivers Microsoft Docs 首先安装Visual Studio 这个就不用我介绍了怎么安装了 下面直接下载步
  • <Linux开发>驱动开发 -之- Linux LCD 驱动

    Linux开发 驱动开发 之 Linux LCD 驱动 交叉编译环境搭建 Linux开发 linux开发工具 之 交叉编译环境搭建 uboot移植可参考以下 Linux开发 之 系统移植 uboot移植过程详细记录 第一部分 Linux开发
  • linux应用程序直接return与exit的区别

    在Linux应用程序中 可以使用 return 语句直接从 main 函数返回 这将导致程序终止并返回给操作系统 然而 有时候使用 exit 函数比直接使用 return 语句更有优势 以下是一些原因 清理资源 exit 函数可以确保在程序
  • linux ARM64 处理器内存屏障

    一 内存类型 ARMv8架构将系统中所有的内存 按照它们的特性 划分成两种 即普通内存和设备内存 并且它们是互斥的 也就是说系统中的某段内存要么是普通内存 要么是设备内存 不能都是 1 普通内存 Normal Memory 普通内存的特性是
  • OpenHarmony基于HDF简单驱动开发实例

    背景 OpenHarmony 3 0 LTS qemu small system demo liteos a qemu 添加配置 device qemu arm virt liteos a hdf config device info de

随机推荐

  • 基于Taro + 云开发 打造婚礼邀请函

    趣婚礼 基于Taro2 云开发 打造婚礼邀请函 项目名称 趣婚礼 基于Taro2 云开发 打造婚礼邀请函 Taro2 云开发 项目介绍 结婚的时候婚礼邀请函是一道必不可少的程序 但是没法去很好的留存我们的数据和回忆 除非有后端支持 最近刚好
  • java 面试的常用问题

    ArrayList 和 LinkedList 的区别 数据结构层面 ArrayList 是动态数组的数据结构 LinkedList是链表的数据结构 数据操作层面 对于随机访问get和set ArrayList优于LinkedList 对于新
  • PyQt(Python+Qt)学习随笔

    专栏 Python基础教程目录 专栏 使用PyQt开发图形界面Python应用 专栏 PyQt moviepy音视频剪辑实战 专栏 PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 PyQt学习随笔 PyQt Python Q
  • EBNF范式

    1 巴科斯范式 巴科斯范式 BNF Backus Naur Form 的缩写 是由 John Backus 和 Peter Naur 首先引入的用来描述计算机语言语法的符号集 现在 几乎每一位新编程语言书籍的作者都使用巴科斯范式来定义编程语
  • web前端入门到实战:CSS遮罩效果、阴影效果、毛玻璃效果

    一般遮罩 background 000 在body标签的最后加上div标签作为遮罩 如下 div class mask div css样式 mask position fixed top 0 left 0 bottom 0 right 0
  • Thrift之TProtocol类体系原理及源码详细解析之JSon协议类TJSONProtocol

    我的新浪微博 http weibo com freshairbrucewoo 欢迎大家相互交流 共同提高技术 JSON JavaScriptObjectNotation 是一种数据交换格式 是以JavaScript为基础的数据表示语言 是在
  • vant框架DropdownMenu 下拉菜单组件在小程序中的应用

    vant框架DropdownMenu 下拉菜单组件在小程序中的应用 官方文档实例
  • Grafana Kubernetes部署(rancher)

    1 相关资源导航 https blog csdn net zyj81092211 article details 122917786 2 环境介绍 kubernetes版本 v1 23 4 rancher版本 v2 6 3 容器相关环境配置
  • 获取服务器信息失效,获取服务器时间失败

    获取服务器时间失败 内容精选 换一换 安装完Mind Studio后 如果用户进行编译运行相关操作 则需要参见该章节 将硬件环境的lib库同步到Mind Studio安装服务器 已经完成安装 请确保DDK版本号与硬件环境所安装的软件包版本号
  • IO(输入/输出)

    用户态和内核态 用户态 用来运行应用程序 不能直接对操作系统进行调用 而是需要切换到内核态对操作系统进行操作 内核态 直接访问操作系统资源或运行操作系统程序 例如程序要保存一个文件到硬盘 在程序执行的用户态 是直接操作磁盘的 只有切换到内核
  • Socket编程之聊天室

    1 单线程模式 创建服务端 第一步 准备地址和端口 第二步 创建一个ServerSocket对象 第三步 等待客户端连接 最后一步 数据接收和发送 public class SingleThreadServer public static
  • Linux线程同步

    1 同步 同步即协同步调 按预定的先后次序运行 线程同步 指一个线程发出某一功能调用时 在没有得到结果之前 该调用不返回 同时其它线程为保证数据一致性 不能调用该函数 解决同步的问题 加锁 2 数据混乱原因 1 资源共享 独享资源则不会 2
  • ubuntu-16.04 安装虚拟机工具时报错

    2019独角兽企业重金招聘Python工程师标准 gt gt gt root alex virtual machine home alex Desktop vmware tools distrib vmware install pl ope
  • Mathtype公式编辑软件 安装教程

    文章目录 1 MathType公式编辑器 介绍 2 MathType 安装 2 1 下载包 2 2 安装源程序 2 3 安装补丁 4 验证是否安装成功 我们再写论文时 一般都明确要求 公式必须用MathType编辑 所有公式必须在MathT
  • 什么是软件外包公司?要不要去外包公司?

    关注后回复 进群 拉你进程序员交流群 作者丨土豆居士 来源丨一口Linux ID yikoulinux 一 什么是外包 软件外包分为 人力外包和项目外包两个方向 1 劳务派遣 指的是把员工外派到对应的用工企业打 短工 比如很多工程师虽然签约
  • SpringBoot总结

    一 SpringBoot简介 1 入门案例 SpringMVC的HelloWord程序大家还记得吗 SpringBoot是由Pivotal团队提供的全新框架 其设计目的是用来简化Spring应用的初始搭建以及开发过程 原生开发SpringM
  • 153个!PCB板上的字母符号都代表啥?一图带你搞懂!

    PCB板是基于电路设计图而生产的 看过电路设计图的小伙伴都会知道 上面有各种物理电学标准符号 通过分析电路设计图 可以得知将使用哪些电子元器件 各元器件之间的关系 以及该电路具备哪些性能 为此 小编在网络上搜集了一些电工电路图常用的字母符号
  • 石锤!谷歌排名第一的编程语言,死磕这点,程序员都收益

    日本最大的证券公司之一野村证券首席数字官马修 汉普森 在Quant Conference上发表讲话 用Excel的人越来越少 大家都在码Python代码 甚至直接说 Python已经取代了Excel 事实上 为了追求更高的效率和质量 他们开
  • 数据结构与算法——马踏棋盘(c++栈实现)

    马踏棋盘问题是旅行商 TSP 或哈密顿问题 HCP 的一个特例 在国际棋盘棋盘上 用一个马按照马步跳遍整个棋盘 要求每个格子都只跳到一次 最后回到出发点 这是一个 NP问题 通常采用回溯法或启发式搜索类算法求解 在此采用栈进行回溯法求解 i
  • 嵌入式:驱动开发 Day4

    作业 通过字符设备驱动分步注册方式编写LED驱动 完成设备文件和设备的绑定 驱动程序 myled c include