驱动——串口工具点灯实验

news2025/7/6 14:19:15

 通过串口工具输入命令,操作LED灯的点亮与熄灭

要求:

1)分部实现注册字符设备驱动

2)自动创建设备节点

3)通过结构体对led灯地址进行映射

4)次设备号完成私有数据传参

代码实现:

1、头文件代码的编写:

①对GPIO寄存器进行结构体的封装

②对寄存器的地址进行宏定义

③通过枚举给LED灯进行赋值

 2、编写功能代码:

open函数:

通过inode结构体获取次设备号,并通过私有数据进行传参

入口函数:

①分布实现字符设备驱动

a:分配字符设备驱动(struct cdev *cdev_alloc(void))

b:完成设备驱动的初始化(void cdev_init(struct cdev *cdev, const struct file_operations *fops))

c:申请设备号(静态指定:int register_chrdev_region(dev_t from, unsigned count, const char *name);动态申请:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name))

d:注册字符设备驱动(int cdev_add(struct cdev *p, dev_t dev, unsigned count))

②完成LED灯相应寄存器的初始化

③自动创建设备节点(class_create提交目录信息,device_create提交设备节点信息)

出口函数:

a:销毁设备节点信息

b:释放驱动

c:释放设备号

d:注销字符设备驱动

许可证

 附:实现代码

头文件:

#ifndef __MYLED_H__
#define __MYLED_H_

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

#define GPIOE_ADDR 0x50006000
#define GPIOF_ADDR 0x50007000
#define RCC_ADDR 0x50000A28

typedef enum{
    LED1,
    LED2,
    LED3,
}leds_t;

#endif

 makefile代码:

ARCH ?= x86
FILE ?= led
ARM:=arm
X86:=x86
ifeq ($(ARCH),$(ARM))
KERNEDIR:=/home/ubuntu/linux-5.10.61
endif
ifeq ($(ARCH),$(X86))
KERNEDIR:=/lib/modules/$(shell uname -r)/build
endif
PWD:=$(shell pwd)
KBUILD_EXTRA_SYMBOLS:=/home/ubuntu/ww/driver/01_linux/03_sym/01_demo/Module.symvers
all:
	make -C $(KERNEDIR) M=$(PWD) modules
clean:
	make -C $(KERNEDIR) M=$(PWD) clean
obj-m:=$(FILE).o

 功能代码:

#include<linux/init.h>
#include<linux/module.h>
#include<linux/device.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/slab.h>
#include<linux/uaccess.h>
#include<linux/io.h>
#include"./led.h"
#define GNAME "mydev"

volatile gpio_t* VIRT_GPIOE;
volatile gpio_t* VIRT_GPIOF;
volatile unsigned int* VIRT_RCC;

unsigned int major =0;
char kbuf[128]={0};
struct cdev* cdev;
struct class * cls;
struct device *devic;
dev_t dev1;
int minor = 0;
unsigned count=3;

int mydev_open(struct inode *inode, struct file *file)
{
    int min;
    printk("%s:%s:%d",__FILE__,__func__,__LINE__);
    
    min=MINOR(inode->i_rdev);
    file->private_data=(void*)min;
    return 0;
}
ssize_t mydev_read(struct file *file, char __user *ubuf, size_t size, loff_t * loff)
{
    printk("%s:%s:%d",__FILE__,__func__,__LINE__);
    return 0;
}
ssize_t mydev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
    int ret;
    int min;
    //printk("%s:%s:%d",__FILE__,__func__,__LINE__);
    if(size > sizeof(kbuf)) size = sizeof(kbuf);
    ret = copy_from_user(kbuf,ubuf,size);
    if(ret)
    {
        printk("copy from user is error\n");
        return -EIO;
    }
    //printk("copy from user kbuf = %s\n",kbuf);
    min=(int)file->private_data;
    switch(min)
    {
        case 0:
                if(kbuf[0]=='1')
                {
                    VIRT_GPIOE->ODR |= (0x1<<10);
                }
                else if(kbuf[0]=='0')
                {
                    VIRT_GPIOE->ODR &= (~(0x1<<10));
                }
                break;
        case 2:
                if(kbuf[0]=='1')
                {
                    VIRT_GPIOE->ODR |= (0x1<<8);
                }
                else if(kbuf[0]=='0')
                {
                    VIRT_GPIOE->ODR &= (~(0x1<<8));
                }
                break;
        case 1:
                if(kbuf[0]=='1')
                {
                    VIRT_GPIOF->ODR |= (0x1<<10);
                }
                else if(kbuf[0]=='0')
                {
                    VIRT_GPIOF->ODR &= (~(0x1<<10));
                }
                break;
    }
    return 0;
}

int mydev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d",__FILE__,__func__,__LINE__);
    return 0;
}
struct file_operations fops={
    .open=mydev_open,
    .read=mydev_read,
    .write=mydev_write,
    .release=mydev_close,
};
static int __init myled_init(void)
{
    int i;
    int ret;
    //分配字符设备驱动
    cdev=cdev_alloc();
    if(NULL==cdev)
    {
        printk("cdev alloc error\n");
        goto ERR1;
    }
    //设备驱动初始化
    cdev_init(cdev,&fops);
    //申请设备号
    if(major>0)
    {
        ret=register_chrdev_region(MKDEV(major,minor),count,GNAME);
        if(ret!=0)
        {
            printk("register chrdev region error\n");
            ret = -ENOMEM;
            goto ERR2;
        }
    }
    else
    {
        ret=alloc_chrdev_region(&dev1,0,count,GNAME);
        if(ret!=0)
        {
            printk("alloc chrdev region error\n");
            ret = -ENOMEM;
            goto ERR2;
        }
        major = MAJOR(dev1);
        minor = MINOR(dev1);
    }

    //驱动的注册
    ret = cdev_add(cdev,MKDEV(major,minor),count);
    if(ret!=0)
    {
        printk("cdev add error\n");
        ret = -EIO;
        goto ERR3;
    }
    //寄存器初始化
    VIRT_RCC = ioremap(RCC_ADDR,4);
    if(NULL == VIRT_RCC)
    {
        printk("VIRT_RCC error\n");
        return -ENXIO;
    }
    VIRT_GPIOE = ioremap(GPIOE_ADDR,sizeof(gpio_t));
    if(NULL == VIRT_GPIOE)
    {
        printk("VIRT_GPIOE error\n");
        return -ENXIO;
    }
    VIRT_GPIOF = ioremap(GPIOF_ADDR,sizeof(gpio_t));
    if(NULL == VIRT_GPIOF)
    {
        printk("VIRT_GPIOF error\n");
        return -ENXIO;
    }
    *VIRT_RCC |= (0x3<<4);

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

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

    VIRT_GPIOF->MODER &= (~(0x3<<20));
    VIRT_GPIOF->MODER |= (0x1<<20);
    VIRT_GPIOF->ODR &= (~(0x1<<10));
    //自动创建设备节点
   
    cls = class_create(THIS_MODULE,GNAME);
    if(IS_ERR(cls))
    {
        ret = PTR_ERR(cls);
        goto ERR4;
    }
    for(i=0;i<count;i++)
    {
    devic = device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
    if(IS_ERR(devic))
    {
        ret = PTR_ERR(devic);
        goto ERR5;
    }
    }
    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 -EIO;
  

}

static void __exit myled_exit(void)
{
    int i;
    //销毁设备节点信息
    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);
}

module_init(myled_init);
module_exit(myled_exit);

MODULE_LICENSE("GPL");

点灯测试现象:

1.在串口工具进行输入:

echo 1 > /dev/myled0 ---->led1灯点亮

echo 0 > /dev/myled0 ---->led1灯熄灭

echo 1 > /dev/myled1 ---->led1灯点亮

echo 0 > /dev/myled1 ---->led1灯熄灭

echo 1 > /dev/myled2 ---->led1灯点亮

echo 0 > /dev/myled2 ---->led1灯熄灭

点灯实验现象

 

串口点灯实验现象

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

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

相关文章

如何使用闲置的云服务器搭建一个属于自己的私人云网盘(可道云kodbox)

你是否有过网盘下载速度只有十几KB&#xff0c;时不时出现网盘的文件被删除的问题&#xff0c;不如自己搭建一个云网盘吧&#xff0c;只需要一云服务器&#xff0c;即可搭建一个跟某度云一样的云盘。可以自由下载&#xff0c;不限制网速&#xff0c;随时都可上传下载。这篇文章…

[附源码]SSM计算机毕业设计在线文献查阅系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Redis应用问题解决(缓存穿透、击穿、雪崩、分布式锁)

Redis应用问题解决(缓存穿透、击穿、雪崩、分布式锁) 缓存穿透 问题描述 当系统中引入redis缓存后&#xff0c;一个请求进来后&#xff0c;会先从redis缓存中查询&#xff0c;缓存有就直接返回&#xff0c;缓存中没有就去db中查询&#xff0c;db中如果有就会将其丢到缓存中&…

Dockerfile构建SpringBoot项目

【1】将SpringBoot项目打包 jar包-->用JDK运行(我们这里打成jar包)war包-->用Tomcat运行 MySQL的url配置 1.useSSLfalse MySQL 8.0 以上版本不需要建立 SSL 连接的&#xff0c;需要显示关闭 2.allowPublicKeyRetrievaltrue 允许客户端从服务器获取公钥。 3.serverTime…

git3:github的使用

1.github创建远程库 创建远程库 名字一般与本地库的名字相同推送远程库push poll https://github.com/likejin123/gitdemo.git 可以创建别名&#xff0c;因为连接太长git remote -v 查看别名 当前没有别名 git remote add gitdemo https://github.com/likejin123/gitdemo.gitg…

初阶数据结构学习记录——여덟 二叉树

树 顾名思义&#xff0c;结构即为树&#xff0c;由一个根节点分出多个节点&#xff0c;这几个节点再继续往下连接其他节点形成一个个子树。不过这棵树是根朝上&#xff0c;叶朝下的。一个根不限制连接多少个节点&#xff0c;把第二层的几个节点也看成根节点&#xff0c;最终形…

2011年408大题总结

2011年408大题第41题第42题第43题第44题第45题第46题第47题第41题 关键信息&#xff1a;有向带权、上三角、行为主序 就可以解决第一二小问 关键路径&#xff1a;最长 0 1 2 3 5&#xff0c;长度为16 第42题 一如既往的暴力 最简单的思路&#xff0c;合并取中位数 所以用数组就…

[附源码]java毕业设计拾穗在线培训考试系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Android App开发动画特效中帧动画和电影淡入淡出动画的讲解及实战(附源码和演示视频 简单易懂)

需要图片集和源码请点赞关注收藏后评论区留言~~~ 一、帧动画 Android的动画分为三类&#xff0c;帧动画&#xff0c;补间动画和属性动画。其中帧动画是实现原理最简单的一种&#xff0c;跟现实生活中的电影胶卷类似&#xff0c;都是在短时间内连续播放多张图片&#xff0c;从而…

Request和Response

目录 1、Request和Response的概述 2、Request对象 2.1、Request继承体系 2.2、Request获取请求数据 2.2.1 获取请求行数据 2.2.2 获取请求头数据 2.2.3 获取请求体数据 2.2.4、获取请求参数的通用方式 2.3 IDEA快速创建Servlet 2.4、请求参数中文乱码问题 2.4.1、POS…

认识Spring

1.1 Spring的历程 早期的 Java EE 使用 EJB 为核心的开发方式,但是这种开发方式在实际开发环境中存在诸多问题: 使用复杂, 代码臃肿, 移植性差等.于是"Spring 之父" Rod Johnson 在其畅销书《Expert One-on-One J2EE Design and Development》中使用一个3万行代码的…

MySQL8.0优化 - 锁 - 按加锁的方式划分:显示锁、隐式锁

文章目录学习资料锁的不同角度分类锁的分类图如下按加锁的方式划分&#xff1a;显示锁、隐式锁隐式锁显式锁学习资料 【MySQL数据库教程天花板&#xff0c;mysql安装到mysql高级&#xff0c;强&#xff01;硬&#xff01;-哔哩哔哩】 【阿里巴巴Java开发手册】https://www.w3…

[附源码]计算机毕业设计JAVA基于Java的快递驿站管理系统

[附源码]计算机毕业设计JAVA基于Java的快递驿站管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; S…

车联网解决方案-最新全套文件

车联网解决方案-最新全套文件一、建设背景面临的挑战1、平台难以支撑高并发接入2、海量数据难以挖掘价值3、缺乏使能套件&#xff0c;开发效率低4、车联网的安全难以保证二、建设架构三、建设方案四、获取 - 车联网全套最新解决方案合集一、建设背景 面临的挑战 1、平台难以支…

Teams Tab App 代码深入浅出 - 配置页面

上一篇文章我们使用Teams Toolkit 来创建、运行 tab app。这篇文章我们深入来分析看一下tab app 的代码。 先打开代码目录&#xff0c;可以看到在 src 目录下有入口文件 index.tsx&#xff0c;然后在 components 目录下有更多的一些 tsx 文件&#xff0c;tsx 是 typescript的一…

实战十二:基于FM算法针对用户商品购买和浏览记录预测用户的行为 代码+数据

1.案例知识点 推荐系统任务描述:通过用户的历史行为(比如浏览记录、购买记录等等)准确的预测出用户未来的行为;好的推荐系统不仅如此,而且能够拓展用户的视野,帮助他们发现可能感兴趣的却不容易发现的item;同时将埋没在长尾中的好商品推荐给可能感兴趣的用户。FM推荐方法…

Qt5开发从入门到精通——第十一篇二节(Qt5 事件处理及实例——键盘事件及实例)

提示&#xff1a;欢迎小伙伴的点评✨✨&#xff0c;相互学习c/c应用开发。&#x1f373;&#x1f373;&#x1f373; 博主&#x1f9d1;&#x1f9d1; 本着开源的精神交流Qt开发的经验、将持续更新续章&#xff0c;为社区贡献博主自身的开源精神&#x1f469;‍&#x1f680; 文…

基于DJYOS的UART驱动编写指导手册

1.概述 DJYOS设计通用的串口驱动模型&#xff0c;在此模型的基础上&#xff0c;移植到不同硬件平台时&#xff0c;只需提供若干硬件操作函数&#xff0c;即可完成串口驱动开发&#xff0c;使开发工作变得简单而快速执行效率高。 DJYOS源代码都有特定的存放位置&#xff0c; 建…

记录为小电机安装一个5012编码器(多摩川协议)的过程

目录 1. 编码器接口板介绍 2. 编码器接口板安装 3. 角度读取软件实现 4.总结 为了能得到更精确的角度&#xff0c;于是想要通过在测试电机上安装一个编码器来获取更精确的角度&#xff0c;方便日后调试或者校验使用&#xff0c;这里记录下操作的步骤。 1. 编码器接口板介绍…

Github Actions 自动同步到 Gitee

目录引言具体方案引言 平时开源代码一般已Github为主&#xff0c;但是会遇到网不好的情况&#xff0c;于是考虑将Github仓库自动同步到Gitee上&#xff0c;作为备份。考虑不能手动做这个事情&#xff0c;于是想到了Github Actions 自动化同步到Gitee中 具体方案 假设Github已…