【Linux 驱动基础】IMX6ULL LED基础驱动

        本机使用的是正点原子的IMX6ULL开发板

# 前置知识

        IMX6ULL GPIO控制框图:

GPIO控制代码大概分为几个流程:开启时钟、设置IO复用、设置IO属性、配置IO方向、设置IO输出电平,下面以IMX6ULL为例,

1. 开启时钟

参考资料:芯片手册《Chapter 18: Clock Controller Module (CCM)》
关于CCM_CCGRx主要是由如下介绍:

00:该 GPIO 模块全程被关闭 

01:该 GPIO 模块在 CPU run mode 情况下是使能的;在 WAIT 或 STOP模式下,关闭

10:保留

11:该 GPIO 模块全程使能

IMX6ULL 使用的LED引脚为GPIO1_IO03,GPIO1时钟控制:

 2. 设置IO复用

参考资料:芯片手册《Chapter 32: IOMUX Controller (IOMUXC)》

选择功能:

        a) IOMUXC_SW_MUX_CTL_PAD_<PADNAME> : Mux pad xxx,选择某个 pad 的功能

        b) IOMUXC_SW_MUX_CTL_GRP_<GROUP NAME>: Mux grp xxx,选择某组引脚的功能

比如我们需要选择的LED灯引脚为GPIO1_IO03,那么选择对应的寄存器,设置复用模式为ALT5即可:

 3. 设置IO属性

参考资料:芯片手册《Chapter 32: IOMUX Controller (IOMUXC)》

设置IO属性等参数:

        a) IOMUXC_SW_PAD_CTL_PAD_<PAD_NAME>: pad pad xxx,设置某个 pad 的参数

        b) IOMUXC_SW_PAD_CTL_GRP_<GROUP NAME>: pad grp xxx,设置某组引脚的参数

比如我们需要选择的LED灯引脚为GPIO1_IO03,那么选择对应的寄存器:

 

 4. 设置IO方向

参考资料:芯片手册《Chapter Chapter 28 General Purpose Input/Output (GPIO)》

GPIO的框图如下:

GPIO模块一共有8个寄存器:

  • 设置方向寄存器:GPIO_GDIR
  • 设置输出电平寄存器:GPIO_DR
  • 读取输入电平寄存器:GPIO_PSR
  • 中断控制寄存器:GPIO_ICR1, GPIO_ICR2
  • 边沿选择寄存器:GPIO_EDGE_SEL
  • 中断掩码寄存器:GPIO_IMR
  • 中断状态寄存器:GPIO_ISR

我们需要控制LED灯打开或者关闭,那么就需要以下步骤:

  • 设置GPIO为输出

  • 控制GPIO输出

# 驱动程序 

1. 驱动代码

#include <linux/module.h>
#include <linux/major.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/delay.h>	/* guess what */
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>	/* For put_user and get_user */#include "atk_led_op.h"struct pri_led_TypeDef
{char drv_name[50];  /* 驱动名称 */int major;      /* 主设备号 */int minor;      /* 次设备号 */dev_t devt;     /* 设备号 */struct device *device;   /* 设备 */char device_name[50];    /* 设备名称 */struct class *class;    /* 类 */char class_name[50];    /* 类名称 */
};static struct pri_led_TypeDef pri_led = {.drv_name = "led_drv",.major  = 0,.minor  = 0,.devt   = 0,.device = NULL,.device_name = "led_dev",.class  = NULL,.class_name  = "led_class",
};struct led_operations *atk_led;static int led_open(struct inode *inode, struct file *file)
{atk_led->init();return 0;
}static int led_release(struct inode *inode, struct file *file)
{atk_led->deInit();return 0;
}static ssize_t led_write(struct file *file, const char __user *buff, size_t size, loff_t *ppos)
{int err;unsigned char data[1];err = copy_from_user(data, buff, 1);if(data[0] == 1){atk_led->ctrl(1);}else{atk_led->ctrl(0);}return 1;
}static const struct file_operations led_op = {.owner      = THIS_MODULE,.open       = led_open,.release    = led_release,.write      = led_write,
};static int __init led_init(void)
{int err;printk("led_init\r\n");pri_led.major = register_chrdev(0, pri_led.drv_name, &led_op);pri_led.devt = MKDEV(pri_led.major, pri_led.minor);pri_led.class = class_create(THIS_MODULE, pri_led.class_name);if(IS_ERR(pri_led.class)){printk("class_create error\r\n");err = PTR_ERR(pri_led.class);goto err_class_create_out;}pri_led.device = device_create(pri_led.class, NULL, pri_led.devt, NULL, pri_led.device_name);if(IS_ERR(pri_led.device)){printk("device_create error\r\n");err = PTR_ERR(pri_led.device);goto err_device_create_out;}atk_led = atk_led_op_register();return 0;err_device_create_out:class_destroy(pri_led.class);
err_class_create_out:unregister_chrdev(pri_led.major, pri_led.drv_name);return err;
} static void __exit led_exit(void)
{printk("led_exit\r\n");device_destroy(pri_led.class, pri_led.devt);class_destroy(pri_led.class);unregister_chrdev(pri_led.major, pri_led.drv_name);
}module_init(led_init);
module_exit(led_exit);MODULE_LICENSE("GPL");

2. 板级驱动代码

#ifndef __ATK_LED_OP_H__
#define __ATK_LED_OP_H__#include <linux/io.h>
#include <linux/module.h>
#include <linux/major.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/delay.h>	/* guess what */
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>	/* For put_user and get_user */struct led_operations
{void (*init)(void);void (*deInit)(void);int (*ctrl)(int status);
};struct led_operations *atk_led_op_register(void);#endif /* __ATK_LED_CTRL_H__ */
#include "atk_led_op.h"/*** 1. 查看原理图,LED0 --> GPIO1_IO03* 2. 开启时钟,CCM_CCGR1->CG13     //Address: 20C_4000h base + 6Ch offset = 20C_406Ch* 3. 设置IO复用,IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03      //Address: 20E_0000h base + 68h offset = 20E_0068h* 4. 设置IO属性,IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03      //Address: 20E_0000h base + 2F4h offset = 20E_02F4h* 5. GPIO1寄存器:  GPIO1_DR           //Address: 209_C000*                  GPIO1_GDIR          //Address: 209_C004*                  GPIO1_PSR           //Address: 209_C008*                  GPIO1_ICR1          //Address: 209_C00C*                  GPIO1_ICR2          //Address: 209_C010*                  GPIO1_IMR           //Address: 209_C014*                  GPIO1_ISR           //Address: 209_C018*                  GPIO1_EDGE_SEL      //Address: 209_C01C             
*/#define CCM_CCGR1_ADDR                              0x20C406C
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03_ADDR       0x20E0068
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03_ADDR       0x20E02F4
#define GPIO1_BASE_ADDR                             0x209C000           typedef struct
{volatile uint32_t GPIO_DR;volatile uint32_t GPIO_GDIR;volatile uint32_t GPIO_PSR;volatile uint32_t GPIO_ICR1;volatile uint32_t GPIO_ICR2;volatile uint32_t GPIO_IMR;volatile uint32_t GPIO_ISR;volatile uint32_t GPIO_EDGE_SEL;
}GPIO_TypeDef;static GPIO_TypeDef *GPIO1;static volatile uint32_t *CCM_CCGR1;
static volatile uint32_t *IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03;
static volatile uint32_t *IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03;void atk_led_init(void)
{uint32_t val;/*1. 地址映射 */GPIO1 = ioremap(GPIO1_BASE_ADDR, sizeof(GPIO_TypeDef));CCM_CCGR1 = ioremap(CCM_CCGR1_ADDR, 4);IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = ioremap(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03_ADDR, 4);IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 = ioremap(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03_ADDR, 4);/* 2. 使能GPIO1时钟 */*CCM_CCGR1 |= (3 << 26);/* 3. 设置IO复用 */val = *IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03;val &= ~(0x0F);val |= 5;*IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = val;/* 4. 设置IO属性 *//* 5. 设置IO方向,设置GPIO1_IO03为输出 */GPIO1->GPIO_GDIR |= (1 << 3);
}void atk_led_deInit(void)
{/* 取消地址映射 */iounmap(GPIO1);iounmap(CCM_CCGR1);iounmap(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03);iounmap(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03);
}int atk_led_ctrl(int status)
{if(status == 1){GPIO1->GPIO_DR &= ~(1<<3);}else{GPIO1->GPIO_DR |= (1<<3);}return 1;
}static struct led_operations led_op = {.init   = atk_led_init,.deInit = atk_led_deInit,.ctrl   = atk_led_ctrl,
};struct led_operations *atk_led_op_register(void)
{return &led_op;
}

 3. 测试APP

#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>/** ./test_app  /dev/test_drv on/off*/
int main(int argc, char **argv)
{int fd;unsigned char buff[1];int len;/* 1. 判断输入参数 */if(argc < 3){printf("Usage: %s on/off\n", argv[0]);printf("       %s -r\n", argv[0]);return -1;}/* 2. 打开文件 */fd = open(argv[1], O_RDWR);if(fd == -1){printf("can not open file %s\n", argv[1]);return -1;}/* 3. 读写文件 */if((strcmp(argv[2], "on") == 0)){buff[0] = 1;write(fd, buff, 1);}else{buff[0] = 0;write(fd, buff, 1);}close(fd);return 0;
}

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

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

相关文章

深入理解 Docker 镜像

1. Docker 镜像的底层原理 1.1 分层的镜像 以我们的pull 命令为例&#xff0c;在下载的过程中我们可以看到docker的镜像好像是一层一层的在下载。 1.2 UnionFS(联合文件系统) 联合文件系统是一种分层、轻量级并且高性能的文件系统&#xff0c;它支持对文件系统的修改作为一次…

MPI4.1文档翻译(持续更新)

本博客参考官方文档进行介绍&#xff0c;全网仅此一家进行中文翻译&#xff0c;走过路过不要错过。 官方网址&#xff1a;https://www.mpi-forum.org/ 参考文档&#xff1a;https://www.mpi-forum.org/docs/mpi-4.1/mpi41-report.pdf 引用官方4.1文档方法&#xff1a; manu…

linux下使用迅雷的完美办法(网络版免费),其他下载工具

迅雷有自家服务器的支持&#xff0c;因此&#xff0c;其他下载器&#xff0c;可能难以匹敌 &#xff1f; linux下使用迅雷的完美办法&#xff08;免费&#xff09; https://blog.csdn.net/lqrensn/article/details/8853949 网络版 Linux下安装并使用迅雷 https://www.lxlin…

牛客题霸-SQL进阶篇(刷题记录一)

本文基于前段时间学习总结的 MySQL 相关的查询语法&#xff0c;在牛客网找了相应的 MySQL 题目进行练习&#xff0c;以便加强对于 MySQL 查询语法的理解和应用。 由于涉及到的数据库表较多&#xff0c;因此本文不再展示&#xff0c;只提供 MySQL 代码与示例输出。 部分题目因…

化工企业能源在线监测管理系统,智能节能助力生产

化工企业能源消耗量极大&#xff0c;其节能的空间也相对较大&#xff0c;所以需要控制能耗强度&#xff0c;保持更高的能源利用率。 化工企业能源消耗现状 1、能源管理方面 计量能源消耗时&#xff0c;计量器具存在问题&#xff0c;未能对能耗情况实施完全计量&#xff0c;有…

P - Beat

题目分析 1.看数据范围&#xff0c;大概知道dfs能做 2.自0问题开始查找&#xff0c;确保之后每次查找到的问题的困难度均大于上一次 3.遍历所有情况再记录cnt即可 代码 #include <iostream> #include <algorithm> #include <cstdio> #include <cstring&…

蓝桥集训之矩形牛棚

蓝桥集训之矩形牛棚 核心思想&#xff1a;单调队列 模板&#xff1a;Acwing.131.直方图矩形面积首先遍历所有下界 然后确定以该下界为底的直方图 求最大矩形 #include <iostream>#include <cstring>#include <algorithm>using namespace std;const int N 30…

【13】vue2和vue3对比

vite: https://github.com/vitejs/vite 面试题:谈谈你对 vite 的理解,最好对比 webpack 说明 webpack 原理图 vite 原理图 面试题答案: webpack 会先打包,然后启动开发服务器,请求服务器时直接给予打包结果。 而 vite 是直接启动开发服务器,请求哪个模块再对该模块进行实…

Linux的一些基本指令

​​​​​​​ 目录 前言&#xff1a; 1.以指令的形式登录 2.ls指令 语法&#xff1a; 功能&#xff1a; 常用选项&#xff1a; 3.pwd指令 4.cd指令 4.1 绝对路径与相对路径 4.2 cd .与cd ..&#xff08;注意cd后先空格&#xff0c;然后两个点是连一起的&#xff0…

XUbuntu22.04之安装Plantuml(二百二十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

电脑不能读取移动硬盘,但是可以读取U盘解决方法

找到此电脑 右键设备管理器&#xff0c;找到其中的通用串行总线控制器。 注意&#xff0c;凡是插入到电脑当中不能读取的U盘或者移动硬盘&#xff0c;都会在通用串行总线控制器当中显示为USB大容量存储设备 鼠标选中“USB大容量存储设备”&#xff0c;右键卸载它。此时&#x…

地图定点热力图GeoJson

1.首先需要拿到地图&#xff0c;可以从不同的站点寻找&#xff0c;我这里是从hcharts里面找的 //国外地图数据地址&#xff1a; https://img.hcharts.cn/mapdata/ //国内地图数据地址&#xff1a; https://datav.aliyun.com/portal/school/atlas/area_selector2.在项目中引入e…

医院挂号系统设计与实现|jsp+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;…

路由器里如何设置端口映射?

在互联网时代&#xff0c;我们经常需要将内部网络的服务暴露到公网以便其他人访问。直接将内部网络暴露在公网上存在一定的安全风险。为了解决这个问题&#xff0c;我们可以利用路由器里设置端口映射来实现将特定端口的访问请求转发到内部网络的特定设备上。 端口映射的原理 端…

Matplotlib数据可视化实战-2绘制折线图(1)

函数plot是matplotlib.pyplot模块中的一个重要函数&#xff0c;用于在图形中绘制折线图。其基本用法是plot(x, y, [fmt], **kwargs)&#xff0c;其中x和y分别代表X轴和Y轴上的数据点&#xff0c;通常是以列表、数组或一维序列的形式给出。通常用的参数有&#xff1a; 基本参数…

Programming Abstractions in C阅读笔记:p331-p337

《Programming Abstractions in C》学习第79天&#xff0c;p331-p337&#xff0c;总计7页。 一、技术总结 /** File: stack.h* -------------* This interface defines an abstraction for stacks. In any* single application that uses this interface, the values in* the…

IEEE TRANSACTIONS ON INTELLIGENT TRANSPORTATION SYSTEMS (T-ITS) 投稿记录,欢迎大家评论~

投稿整个流程时间点&#xff1a;Submitted: 17 October 2023 Awaiting Reviewer Assignment: From 18 October 2023 to 6 November 2023 Under review: From 6 November 2023 to 30 November 2023 Awaiting reviewer scores: From 1 December 2023 to 13 January 2024 Aw…

Unity学习笔记 6.2D换帧动画

下载源码 UnityPackage 目录 1.导入图片 1.1. 图片的叠放顺序 2.图片切片 3.用动画控制器让马&#x1f40e;动起来 1.导入图片 直接拖拽进场景 检查 Texture Type&#xff08;纹理类型&#xff09;是否为 Sprite 创建2D精灵对象&#xff0c;拖拽图片到Sprite&#xff08…

6.4 Dropout正则化

1、Dropout Dropout是一种正则化技术&#xff0c;通过防止特征的协同适应&#xff0c;可用于减少神经网络中的过拟合。 Dropout的效果非常好&#xff0c;实现简单且不会降低网络速度&#xff0c;被广泛使用。 特征的协同适应指的是在训练模型时&#xff0c;共同训练的神经元…

【算法篇】逐步理解动态规划1(斐波那契数列模型)

目录 斐波那契数列模型 1. 第N个泰波那契数 2.使用最小花费爬楼梯 3.解码方法 学过算法的应该知道&#xff0c;动态规划一直都是一个非常难的模块&#xff0c;无论是状态转移方程的定义还是dp表的填表&#xff0c;都非常难找到思路。在这个算法的支线专题中我会结合很多力…