驱动开发,stm32mp157a开发板的led灯控制实验(再优化),使用ioctl函数,通过字符设备驱动分步注册方式编写LED驱动,完成设备文件和设备的绑定

2023-11-01

 1.实验目的

        编写LED灯的驱动,在应用程序中编写控制LED灯亮灭的代码逻辑实现LED灯功能的控制;

 

 2. LED灯相关寄存器分析

 

LED1->PE10 LED1亮灭:

RCC寄存器[4]->1 0X50000A28

GPIOE_MODER[21:20]->01 (输出) 0X50006000

GPIOE_ODR[10]->1(输出高电平) 0(输出低电平)0X50006014

LED2->PF10 LED2亮灭:

RCC寄存器[5]->1 0X50000A28

GPIOE_MODER[21:20]->01 (输出) 0X50006000

GPIOE_ODR[10]->1(输出高电平) 0(输出低电平)0X50006014

LED3->PE8 LED3亮灭:

RCC寄存器[4]->1 0X50000A28

GPIOE_MODER[17:16]->01 (输出) 0X50006000

GPIOE_ODR[8]->1(输出高电平) 0(输出低电平)0X50006014

GPIOE_OTYPER默认为00

GPIOE_PUPDR默认为0

GPIOE_OSPEEDR默认为00

 

 3.字符设备驱动内部注册过程 

  • 分配struct cdev对象空间
  • 初始化struct cdev对象
  • 设备号的申请(静态/动态申请)
  • 注册cdev对象

 

 4.编写代码

---Makefile---工程管理文件

modname?=demo
arch?=arm
ifeq ($(arch),arm)
KERNELDIR:= /home/ubuntu/FSMP1A/linux-stm32mp-5.10.61-stm32mp-r2-r0/linux-5.10.61 #编译生成ARM架构
else
KERNELDIR:=/lib/modules/$(shell uname -r)/build #编译生成X86架构
endif

PWD:=$(shell pwd) #模块化编译文件路径
all:
	make -C $(KERNELDIR) M=$(PWD) modules
clean:
	make -C $(KERNELDIR) M=$(PWD) clean

obj-m:=$(modname).o

---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;

//LED1和LED3寄存器地址
#define LED1_ADDR 0x50006000
#define LED2_ADDR 0x50007000
#define LED3_ADDR 0x50006000
#define RCC_ADDR 0x50000A28

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


#endif

---cdev.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 "head.h"
#include <linux/io.h>

struct cdev *cdev = NULL;
unsigned major = 0;
unsigned minor = 0;
module_param(major, uint, 0664); // 方便再命令行传递major的值
dev_t devno;
struct class *cls;
struct device *dev;
char kbuf[128] = {0};
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 2: // 控制LED2
        switch (cmd)
        {
        case LED_ON: // 开灯
            vir_led2->ODR |= (0x1 << 10);
            break;
        case LED_OFF: // 关灯
            vir_led2->ODR &= (~(0x1 << 10));
            break;
        }
        break;

    case 3: // 控制LED3
        switch (cmd)
        {
        case LED_ON: // 开灯
            vir_led3->ODR |= (0x1 << 8);
            break;
        case LED_OFF: // 关灯
            vir_led3->ODR &= (~(0x1 << 8));
            break;
        }
        break;
    }

    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,
    .release = mycdev_close,
};

// 相关寄存器地址映射及初始化
int all_led_init(void)
{
    // 相关寄存器的内存映射
    vir_led1 = ioremap(LED1_ADDR, sizeof(gpio_t));
    if (vir_led1 == NULL)
    {
        printk("物理内存映射失败%d\n", __LINE__);
        return -ENOMEM;
    }

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

    vir_led3 = vir_led1;

    vir_rcc = ioremap(RCC_ADDR, 4);
    if (vir_rcc == NULL)
    {
        printk("物理内存映射失败%d\n", __LINE__);
        return -ENOMEM;
    }
    printk("寄存器内存映射成功\n");

    // 硬件寄存器的初始化
    (*vir_rcc) |= (0x3 << 4);

    // LED1
    vir_led1->MODER &= (~(0x3 << 20));
    vir_led1->MODER |= (0x1 << 20);
    vir_led1->ODR &= (~(0x1 << 10));

    // LED2
    vir_led2->MODER &= (~(0x3 << 20));
    vir_led2->MODER |= (0x1 << 20);
    vir_led2->ODR &= (~(0x1 << 10));

    // LED3
    vir_led3->MODER &= (~(0x3 << 16));
    vir_led3->MODER |= (0x1 << 16);
    vir_led3->ODR &= (~(0x1 << 8));

    printk("寄存器初始化成功\n");
    return 0;
}


static int __init mycdev_init(void)
{
    int ret;
    // 分配字符设备驱动对象空间
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("字符设备驱动对象申请空间失败\n");
        ret = -EFAULT;
        goto out1;
    }
    printk("字符设备驱动对象申请空间成功\n");

    // 字符设备驱动对象初始化
    cdev_init(cdev, &fops);

    // 设备号的申请
    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("申请设备号成功\n");

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

     相关寄存器地址映射及初始化
    all_led_init();

    // 向上提交目录信息
    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 < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    // 销毁目录
    class_destroy(cls);
    // 注销驱动对象
    cdev_del(cdev);
    // 释放申请的设备号和设备资源
    unregister_chrdev_region(MKDEV(major, minor), 3);
    // 释放字符设备驱动对象空间
    kfree(cdev);

    // 取消物理内存的映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);
}
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 "head.h"
#include <sys/ioctl.h>

int main(int argc, char const *argv[])
{
    int a;
    char buf[128] = {0};
    int fd = open("/dev/myled0", O_RDWR);

    if (fd < 0)
    {
        printf("设备文件打开失败\n");
        exit(-1);
    }
    while (1)
    {
        printf("请输入对LED灯的控制:1(开灯) 0(关灯)>> ");
        scanf("%d",&a);
        getchar();

        switch (a)
        {
        case 1:
            ioctl(fd, LED_ON); // 第三个参数为指针
            break;
        case 0:
            ioctl(fd, LED_OFF);
            break;
        }
    }

    close(fd);

    return 0;
}

 

5.测试现象

         int fd = open("/dev/myled0", O_RDWR);

        测试程序中只打开一个设备文件,对应LED1的次设备号,所以只控制LED1灯的亮灭

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

驱动开发,stm32mp157a开发板的led灯控制实验(再优化),使用ioctl函数,通过字符设备驱动分步注册方式编写LED驱动,完成设备文件和设备的绑定 的相关文章

随机推荐

  • 10.12黄金原油资讯直通车,黄金原油区间震荡后市操作建议

    黄金消息面与技术面解析 消息面 本周显然又是一个 超级周 数据方面 将迎来中国CPI PPI数据和进出口数据 美国将公布CPI PPI 零售销售等重磅经济数据 风险事件方面 OPEC EIA和IEA都将公布原油市场月度报告 美联储多位票委和
  • eslint+prettier+vue3格式化

    项目里面安装并配置eslint 参考官网执行如下命令 npm init eslint config 等价于 npm install eslint D 安装eslint npx eslint init 初始化配置eslint 执行后会有一些配
  • 【论文笔记】:UnitBox

    Title 2016 ACM MM UnitBox An Advanced Object Detection Network Abstract 传统的目标框含有四个独立的坐标变量 丢失了相互之间的信息 导致AP下降 Unit Box 提出了
  • java操作RabbitMQ

    1 创建虚拟主机 交换机 队列 RabbitMQ提供了自己的管理界面 可以通过管理界面来完成VirtualHost Exchange queue的创建 1 1创建VirtualHost 1 2创建交换机 创建交换机的时候需要指定虚拟主机以及
  • 切换默认python版本(解决ROS中python默认版本为python2的问题)

    1 前言 许多小伙伴在安装完ROS以后 需要基于python3写ROS程序 尤其是部署深度学习算法 但是ROS默认的python版本为python2 导致无法兼容一些基于python3写的算法 有的小伙伴会选择利用anaconda来创建py
  • 蓝桥杯单片机之AT24C02模块的使用

    蓝桥杯单片机之AT24C02时钟模块的使用 简介部分 EEPROM AT24C02 引脚示意 设备地址 Device Address 基本操作 字节写入 分析手册 字节读取 随机读取 根据需要读取的地址进行读取 分析手册 读与写函数代码 实
  • 计算并输出给定正整数n的所有因子(不包括1和自身)之和

    国二有题目 请编写函数fun 该函数的功能是 计算并输出给定正整数n的所有因子 不包括1和自身 之和 规定n的值不大于1000 例如 在主函数中从键盘给n输入的值为856 则输出为 sum 763 代码如何完成呢 分析 1 输入的数字要是整
  • 内网渗透—红日靶场三

    文章目录 0x01 环境配置 0x02 Centos getshell 0x03 Centos提权 0x04 内网穿透 设置路由 0x05 内网穿透 设置代理 0x06 获取内网目标shell 通过smb拿shell 或者本地挂代理使用k8
  • Windows环境下编译C++版的MXNet问题处理

    最近涉及要在c 上部署人脸检测的算法 要在Windows环境下编译运行MXNet 对于不熟悉c 的小白的我真是一件又让人抓狂又掉头发的事情 网上关于c 的部署的帖子少之又少 加上又是第一次摸这些东西 所以出现的bug真的数不胜数 写这个bl
  • 数据结构与算法之二叉树: Leetcode 145. 二叉树的后序遍历 (Typescript版)

    二叉树的后序遍历 https leetcode cn problems binary tree postorder traversal 描述 给你一棵二叉树的根节点 root 返回其节点值的 后序遍历 示例 1 输入 root 1 null
  • 适合普通大学生的前端开发学习路线

    大家好 我是帅地 假如你没有明确的目标 或许可以按照我说的学习路线来学习一波 我写的每一份学习路线 不会很全面 因为我认为 东西列的太多 反而不利于新手的学习 所以我列举的 都是比较必要的知识 当你把这些知识学了的时候 我相信你不需要别人的
  • 前端基础_使用moveTo与lineTo路径绘制火柴人

    使用moveTo与lineTo路径绘制火柴人 接下来看一下除了arc方法以外 其他使用路径绘制图形时会使用到的方法 moveTo x y 不绘制 只是将当前位置移动到新的目标坐标 x y lineTo x y 不仅将当前位置移动到新的目标坐
  • 工控CTF(wp)

    GUET工控CTF 所见非真 异常的流量分析 黑客的攻击 黑客的大意 丢失的数据 凯撒的秘密 工程的秘密 S7协议分析 轻松时刻 打不开的压缩包 失控的遥控 病毒文件分析 OPC协议分析 sign in 随意记录一下这次CTF的解题步骤 比
  • 文章详情页 - 评论功能的实现

    目录 1 准备工作 1 1 创建评论表 1 2 创建评论实体类 1 3 创建 mapper 层评论接口和对应的 xml 实现 1 4 准备评论的 service 层 1 5 准备评论的 controller 层 2 总的初始化详情页 2 1
  • The Open Group 参考卡|《ArchiMate® 3.1 规范》中文版正式发布

    ArchiMate 规范 3 1 版在 3 0 1 版之间做了如下改动 增加了新的策略元素 价值流 为关联关系添加了可选的指示标记 改进了元模型和相关图形的组织结构 进一步完善和规范了关系的推导 除此之外 ArchiMate 规范 3 1
  • 全球及中国微电网市场规模容量及建设运营模式分析报告2021年版

    全球及中国微电网市场规模容量及建设运营模式分析报告2021年版 HS HS HS HS HS HS HS HS HS HS HS HS 修订日期 2021年11月 搜索鸿晟信合研究院查看官网更多内容 第一章 微电网相关概述 1 1 微电网概
  • pandas 取excel 中的某一列_别找了,这是 Pandas 最详细教程了

    点击上方 菜鸟编程大本营 选择 星标 公众号 重磅干货 第一时间送达 来源 机器之心 Python 是开源的 它很棒 但是也无法避免开源的一些固有问题 很多包都在做 或者在尝试做 同样的事情 如果你是 Python 新手 那么你很难知道某个
  • MySQL 如何优化慢查询?

    一 前言 在日常开发中 我们往往会给表加各种索引 来提高 MySQL 的检索效率 但我们有时会遇到明明给字段加了索引 并没有走索引的Case 进而导致 MySQL 产生慢查询 严重场景下 甚至出现主从延迟 数据库拖垮的极端事故 本文梳理出索
  • 用Python采集Notion数据

    使用 Python 采集 Notion 数据的方法 首先 你需要在 Notion 网站上申请一个 API key 具体方法可以参考这篇文章 https developers notion com docs getting started s
  • 驱动开发,stm32mp157a开发板的led灯控制实验(再优化),使用ioctl函数,通过字符设备驱动分步注册方式编写LED驱动,完成设备文件和设备的绑定

    1 实验目的 编写LED灯的驱动 在应用程序中编写控制LED灯亮灭的代码逻辑实现LED灯功能的控制 2 LED灯相关寄存器分析 LED1 gt PE10 LED1亮灭 RCC寄存器 4 gt 1 0X50000A28 GPIOE MODER