驱动开发 CoetexA7核 字符设备驱动(LED亮灯)(单独映射寄存器实现+封装结构体映射实现)

news2024/11/18 11:26:50

一、单独映射寄存器实现

可参考arm点灯C语言 cortex-A7核 点LED灯 (附 汇编实现、使用C语言 循环实现、使用C语言 封装函数实现【重要、常用】)-CSDN博客

1 应用程序 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, char const *argv[])
{
    char buf[128]={0};
    int fd = open("/dev/mychrdev",O_RDWR);
    if(fd < 0)
    {
        printf("打开设备文件失败\n");
        return -1;
    }
    printf("打开设备文件成功\n");   
    while(1)
    {
        printf("请输入要进行的操作:0(关灯) 1(开灯) >>> ");
        fgets(buf,sizeof(buf),stdin);//在终端读一个字符串
        buf[strlen(buf) - 1] = '\0';
        write(fd, buf, sizeof(buf));//将数据传递给内核
    }
    close(fd);
    return 0;
}

2 头文件head.h

#ifndef __HEAD_H__
#define __HEAD_H__
//GPIOE
#define PHY_LED1_MODER 0X50006000
#define PHY_LED1_ODR 0x50006014
//GPIOF
#define PHY_LED2_MODER 0X50007000
#define PHY_LED2_ODR 0x50007014
//RCC
#define PHY_RCC 0x50000A28

#endif

3 驱动程序 demo.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include "head.h"
unsigned int major;
char kbuf[128] = {};
//定义三个指针指向映射后的虚拟内存
unsigned int *vir_moder_E;
unsigned int *vir_odr_E;
unsigned int *vir_moder_F;
unsigned int *vir_odr_F;
unsigned int *vir_rcc;
//封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size,loff_t *lof)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    int ret;
    ret = copy_to_user(ubuf,kbuf,size);
    if (ret)
    {
        printk("copy_to_user filed\n");
        return -EIO;
    }
    return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size,loff_t *lof)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    int ret;
    ret = copy_from_user(kbuf,ubuf,size);
    if (ret)
    {
        printk("copy_from_user filed\n");
        return -EIO;
    }
    if(kbuf[0] == '0') //关灯
    {
        //关灯逻辑
        (*vir_odr_E) &= (~(0x1 << 10)); //LED1默认关灯
        (*vir_odr_F) &= (~(0x1 << 10)); //LED2默认关灯
        (*vir_odr_E) &= (~(0x1 << 8)); //LED3默认关灯
    }
    else if(kbuf[0] == '1') //开灯
    {
        //开灯逻辑
        (*vir_odr_E) |= (0x1 << 10); //LED1
        (*vir_odr_F) |= (0x1 << 10); //LED2
        (*vir_odr_E) |= (0x1 << 8); //LED3
    }
    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,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};
static int __init mycdev_init(void)
{
    //注册字符设备驱动
    major = register_chrdev(0,"mychrdev",&fops);
    if(major < 0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功major=%d\n",major);
    //进行寄存器的地址映射
    //GPIOE
    vir_moder_E = ioremap(PHY_LED1_MODER,4);
    if(vir_moder_E == NULL)
    {
        printk("物理内存地址映射失败%d\n",__LINE__);
        return -EFAULT;
    }
    vir_odr_E = ioremap(PHY_LED1_ODR,4);
    if(vir_odr_E == NULL)
    {
        printk("物理内存地址映射失败%d\n",__LINE__);
        return -EFAULT;
    }
    //GPIOF
    vir_moder_F = ioremap(PHY_LED2_MODER,4);
    if(vir_moder_F == NULL)
    {
        printk("物理内存地址映射失败%d\n",__LINE__);
        return -EFAULT;
    }
    vir_odr_F = ioremap(PHY_LED2_ODR,4);
    if(vir_odr_F == NULL)
    {
        printk("物理内存地址映射失败%d\n",__LINE__);
        return -EFAULT;
    }
    //RCC
    vir_rcc = ioremap(PHY_RCC,4);
    if(vir_rcc == NULL)
    {
        printk("物理内存地址映射失败%d\n",__LINE__);
        return -EFAULT;
    }
    printk("寄存器内存映射成功\n");
    //LED1寄存器初始化
    (*vir_rcc) |= (0x1 << 4); //GPIOE控制器时钟使能
    (*vir_moder_E) &= (~(0x3 << 20)); //MODER[21:20]->00
    (*vir_moder_E) |= (0x1 << 20); //MODER[21:20]->01
    (*vir_odr_E) &= (~(0x1 << 10)); //默认关灯
    //LED2寄存器初始化
    (*vir_rcc) |= (0x1 << 5); //GPIOF控制器时钟使能
    (*vir_moder_F) &= (~(0x3 << 20)); //MODER[21:20]->00
    (*vir_moder_F) |= (0x1 << 20); //MODER[21:20]->01
    (*vir_odr_F) &= (~(0x1 << 10)); //默认关灯
    //LED3寄存器初始化
    (*vir_rcc) |= (0x1 << 4); //GPIOE控制器时钟使能
    (*vir_moder_E) &= (~(0x3 << 16)); //MODER[17:16]->00
    (*vir_moder_E) |= (0x1 << 16); //MODER[17:16]->01
    (*vir_odr_E) &= (~(0x1 << 8)); //默认关灯
    return 0;
}
static void __exit mycdev_exit(void)
{
    //取消内存映射
    iounmap(vir_moder_E);
    iounmap(vir_odr_E);
    iounmap(vir_moder_F);
    iounmap(vir_odr_F);
    iounmap(vir_rcc);
    //注册字符设备驱动
    unregister_chrdev(major,"mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

编写好代码后,make成arm架构

make arch=arm modname=demo

使用交叉编译工具链

arm-linux-gnueabihf-gcc test.c 

将文件通过tftp传输到开发板中

cp demo.ko ~/nfs/rootfs/
cp a.out ~/nfs/rootfs/

4 效果呈现

二、封装结构体映射实现

kbuf[0] 控制灯

kbuf[1] 控制状态

1 头文件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
#endif 

2 驱动程序 demo.c

#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include"head.h"

int major;
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)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
ssize_t mycdev_read(struct file *file, char  *ubuf, size_t size, loff_t *lof)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
     unsigned long ret;
    //向用户空间读取拷贝
    if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
        size=sizeof(kbuf);
    ret=copy_to_user(ubuf,kbuf,size);
    if(ret)//拷贝失败
    {
        printk("copy_to_user filed\n");
        return ret;
    }
    return 0;
}
ssize_t mycdev_write(struct file *file, const char  *ubuf, size_t size, loff_t *lof)
{
    unsigned long ret;
    //从用户空间读取数据
    if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
        size=sizeof(kbuf);
    ret=copy_from_user(kbuf,ubuf,size);
    if(ret)//拷贝失败
    {
        printk("copy_to_user filed\n");
        return ret;
    }
    switch(kbuf[0]){
        case '1'://LED1
            if(kbuf[1]=='0')//关灯
                vir_led1->ODR &= (~(1<<10));
            else//开灯
                vir_led1->ODR |= 1<<10;
            break;
        case '2'://LED2
            if(kbuf[1]=='0')//关灯
                vir_led2->ODR &= (~(1<<10));
            else//开灯
                vir_led2->ODR |= 1<<10;
            break;
        case '3'://LED3
            if(kbuf[1]=='0')//关灯
                vir_led3->ODR &= (~(1<<8));
            else//开灯
                vir_led3->ODR |= 1<<8;
            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,
    .read=mycdev_read,
    .write=mycdev_write,
    .release=mycdev_close,
};

int all_led_init(void)
{
    //寄存器地址的映射
    vir_led1=ioremap(PHY_LED1_ADDR,sizeof(gpio_t));
    if(vir_led1==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
     vir_led2=ioremap(PHY_LED2_ADDR,sizeof(gpio_t));
    if(vir_led2==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
     vir_led3=vir_led1;
    vir_rcc=ioremap(PHY_RCC_ADDR,4);
    if(vir_rcc==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
    printk("物理地址映射成功\n");
    //寄存器的初始化
    //rcc
    (*vir_rcc) |= (3<<4);
    //led1
    vir_led1->MODER &= (~(3<<20));
    vir_led1->MODER |= (1<<20);
    vir_led1->ODR &= (~(1<<10));
    //led2
    vir_led2->MODER &= (~(3<<20));
    vir_led2->MODER |= (1<<20);
    vir_led2->ODR &= (~(1<<10));
    //led3
    vir_led3->MODER &= (~(3<<16));
    vir_led1->MODER |= (1<<16);
    vir_led1->ODR &= (~(1<<8));
    printk("寄存器初始化成功\n");

    return 0;
}

static int __init mycdev_init(void)
{
    //字符设备驱动注册
    major=register_chrdev(0,"mychrdev",&fops);
    if(major<0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n",major);

    //寄存器映射以及初始化
    all_led_init();

    return 0;
}
static void __exit mycdev_exit(void)
{
    //取消地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);
    //注销字符设备驱动
    unregister_chrdev(major,"mychrdev");


}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

3 应用程序 test.c

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


int main(int argc, char const *argv[])
{
    char buf[128]={0};
    int fd=open("/dev/mychrdev",O_RDWR);
    if(fd<0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    while(1)
    {
        //从终端读取
        printf("请输入两个字符\n");
        printf("第一个字符:1(LED1) 2(LED2) 3(LED3)\n");
        printf("第二个字符:0(关灯) 1(开灯)\n");
        printf("请输入>");
        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1]='\0';
        //向设备文件中写
        write(fd,buf,sizeof(buf));
    }

    
    close(fd);

    return 0;
}

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

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

相关文章

我的电子萝卜刀火了吗?

引言 大家好&#xff0c;我是亿元程序员&#xff0c;一位有着8年游戏行业经验的主程。 笔者在上一篇文章《萝卜刀真的太危险了,于是我用Cocos做了一个》中说到因女儿从学校回来之后想要我给她买一把萝卜刀被我拒绝&#xff0c;但是又想要让她体验一下&#xff0c;因此用Cocos…

【广州华锐互动】建筑安全事故VR沉浸式体验系统

在建筑行业中&#xff0c;安全永远是首要的考虑因素。传统的安全培训方法&#xff0c;如书本教学、现场演示等&#xff0c;虽然能在一定程度上提高员工的安全意识&#xff0c;但这些方法往往缺乏实际体验&#xff0c;员工在真正面临危险时可能无法做出正确的判断和反应。近年来…

nvm管理不同版本nodejs

文章目录 nvm下载卸载本地node安装nvm安装nodejsnvm查看已安装版本nvm切换nodejs版本nvm删除nodejs版本 nvm下载 nvm github下载链接 nvm 1.1.7-setup.zip&#xff1a;安装版&#xff0c;推荐使用 卸载本地node 打开cmd where node 找到上面找到的路径&#xff0c;将node.…

gulp打包vue3+jsx+less插件

最终转换结果如下 在根目录下添加gulpfile.js文件&#xff0c;package.json添加命令npm run gulp var gulp require(gulp) var babel require(gulp-babel) var less require(gulp-less) var del require(del); var spawn require(child_process).spawn;const outDir &…

亚马逊测评,买家号支付不了、砍单率高是什么问题,需要怎么解决

下半年旺季很多卖家都在使用自养号测评给产品冲一波权重&#xff0c;但是很多朋友会遇到下不了单或者砍单率过高等问题。有人以为是支付卡的问题&#xff0c;也有人觉得是IP被关联了。其实他们讲的也没错&#xff0c;但是&#xff0c;亚马逊风控不会针对某个点去进行检测&#…

中小型企网搭建

企业网项目建设实践 目录 企业网项目建设实践 一、 项目背景 二、 需求分析 三、 项目拓扑规划 四、 规划表 1. vlan规划 2. 设备管理规划 3. 端口互联规划 4. IP规划 5. SSH服务规划 五、 仿真拓扑 六、 项目实践&#xff08;配置过程&#xff09; 七…

复杂的菱形继承及菱形虚拟继承(详解)

复杂的菱形继承及菱形虚拟继承 复杂的菱形继承及菱形虚拟继承虚拟继承解决数据冗余和二义性的原理笔试面试题 复杂的菱形继承及菱形虚拟继承 单继承&#xff1a;一个子类只有一个直接父类时称这个继承关系为单继承 多继承&#xff1a;一个子类有两个或以上直接父类时称这个继…

计算机算法分析与设计(15)---贪心算法(虚拟汽车加油问题和最优分解问题)

文章目录 一、虚拟汽车加油问题1.1 问题描述1.2 思路分析1.3 代码编写 二、最优分解问题2.1 问题描述2.2 思路分析2.3 代码编写 一、虚拟汽车加油问题 1.1 问题描述 一辆虚拟汽车加满油后可行驶 n n n km。旅途中有若干加油站。设计一个有效算法&#xff0c;指出应在哪些加油…

【STL】bitset位图的介绍和使用

⭐博客主页&#xff1a;️CS semi主页 ⭐欢迎关注&#xff1a;点赞收藏留言 ⭐系列专栏&#xff1a;C进阶 ⭐代码仓库&#xff1a;C进阶 家人们更新不易&#xff0c;你们的点赞和关注对我而言十分重要&#xff0c;友友们麻烦多多点赞&#xff0b;关注&#xff0c;你们的支持是我…

优思学院|精益管理涵盖哪些内容?

精益生产管理涵盖哪些内容&#xff1f;精益生产是一种以客户需求为引导、以消除浪费和持续改进为核心的生产管理模式&#xff0c;有效提升了公司的效率和利润&#xff0c;投入却极少。它包含哪些具体要素呢&#xff1f; 准时化生产&#xff08;JIT&#xff09; JIT&#xff0…

idea调教-全键盘操作

先转个vim&#xff0c;现在代码编辑可以使用全部键盘 接下来键盘使用目录 现在需要在项目文件中进行跳转&#xff0c;idea在左边目录等进行切换使用alt1等可以切换左右目录等&#xff0c;用方向建可以选中对应的文件&#xff0c;使用shiftenter可以在右边打开新的标签页,使用a…

日常--windows11右键切换回win10

文章目录 一&#xff0e;前言二&#xff0e;方法1.一键切换2.恢复回Win11右键菜单&#xff1a; 一&#xff0e;前言 从win10更新成win11后&#xff0c;很多地方不适应&#xff0c;这里演示如何将windows11右键切换回win10 二&#xff0e;方法 1.一键切换 winr打开运行 输入…

C++ 友元函数和友元类

前言 在本文中&#xff0c;您将学习在C 中创建友元函数和友元类&#xff0c;并在程序中有效地使用它们。OOP的重要概念之一是数据隐藏&#xff0c;即非成员函数无法访问对象的私有或受保护的数据。但是&#xff0c;有时这种限制可能迫使程序员编写冗长而复杂的代码。因此&#…

反向传播back propagation

深度学习概述 决定要怎么连接这些neuron的时候 就已经确定了function set 相比于之前做logistic regression&#xff0c;linear regression的时候&#xff0c;换一个方式来决定function set 比较大&#xff0c;包含了logistic regression&#xff0c;linear regression没法包含…

【C++入门 一 】学习C++背景、开启C++奇妙之旅

目录 1.什么是C2. C的发展史3. C的重要性3.1 语言的使用广泛度3.2 在工作领域1. 操作系统以及大型系统软件开发2. 服务器端开发3. 游戏开发4. 嵌入式和物联网领域5. 数字图像处理6. 人工智能7. 分布式应用 3.3 在校招领域3.3.1 岗位需求3.3.2 笔试题 4. 如何学习C4.1 别人怎么学…

工业电子中的深力科分享一款PWM控制器 KA3525A

关于PWM控制器&#xff1a; PWM控制器是一种用于控制电机或其他设备的电路&#xff0c;它通过改变脉冲宽度调制&#xff08;PWM&#xff09;信号的占空比来控制设备的输出。PWM控制器可以使用单片机或开发板等设备来实现&#xff0c;通过设定占空比&#xff0c;可以轻松地控制…

LeetCode —— dfs和bfs

797. 所有可能的路径 给你一个有 n 个节点的 有向无环图&#xff08;DAG&#xff09;&#xff0c;请你找出所有从节点 0 到节点 n-1 的路径并输出&#xff08;不要求按特定顺序&#xff09;。 graph[i] 是一个从节点 i 可以访问的所有节点的列表&#xff08;即从节点 i 到节点…

【斗罗二】天梦哥告白冰帝,唐三再返场,雨浩通过冰帝考验,觉醒新武魂

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析斗罗大陆2绝世唐门国漫资讯。 斗罗大陆动画第二部绝世唐门第19集已经更新了&#xff0c;全都是霍雨浩与天梦哥在极北之地&#xff0c;吸引冰帝加入造神计划的过程。不仅有天梦哥的爱情宣言告白&#xff0c;唐三也再次限时…

设计模式_备忘录模式

备忘录模式 介绍 设计模式定义案例问题堆积在哪里解决办法备忘录模式行为型模式&#xff0c; 保存了数据某一个时间点的状态 在需要的时候进行回档单机游戏的角色 数据保存并且回档保存和回档加一个状态管理类 类图 代码 MomentData using UnityEngine;public class MomentD…

SLAM从入门到精通(从仿真到实践)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 之前花了40多篇文章&#xff0c;弄清了什么是slam&#xff0c;怎么学习slam&#xff0c;什么是ros&#xff0c;怎么利用ros来学好slam等等。不仅如…