【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,一经查实,立即删除!

相关文章

C 语言的关键字 static 和 C++ 的关键字 static 有什么区别 / C++中,a和a有什么区别?

一、C 语言的关键字 static 和 C 的关键字 static 有什么区别 C语言和C中的关键字static在用法和语义上有一些相似之处&#xff0c;但也存在一些细微的差别。 在C语言中&#xff0c;static关键字主要用于以下三个方面&#xff1a; 修饰全局变量或函数&#xff1a;使用static…

深入理解 Docker 镜像

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

自学python指导教程

要系统地自学Python&#xff0c;可以按照以下步骤进行&#xff1a; 学习基础知识&#xff1a; 从Python的基础语法开始学习&#xff0c;如变量、数据类型、运算符等。理解Python中的控制流程&#xff0c;如条件语句和循环结构。学习函数的定义和使用。了解Python中常用的数据结…

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…

2024最新华为OD机试试题库全 -【局域网中的服务器个数】- C卷

1. 🌈题目详情 1.1 ⚠️题目 在一个机房中,服务器的位置标识在 n*m 的整数矩阵网格中,1 表示单元格上有服务器,0 表示没有。如果两台服务器位于同一行或者同一列中紧邻的位置,则认为它们之间可以组成一个局域网。 请你统计机房中最大的局域网包含的服务器个数。 1.2 …

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&…

使用easyexcel生成动态头,实时生成头写入,加对应的列

测试类 参考链接&#xff1a;动态头&#xff0c;实时生成头写入 package org.springblade.modules.api.controller;import com.alibaba.excel.EasyExcel; import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import c…

蓝桥集训之矩形牛棚

蓝桥集训之矩形牛棚 核心思想&#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 是直接启动开发服务器,请求哪个模块再对该模块进行实…

30-2 越权漏洞

一、定义: 攻击者利用业务的设计缺陷&#xff0c;获取敏感信息或破坏业务完整性。本质是程序逻辑输入管控不严&#xff0c;未对用户数据进行严格把控&#xff0c;导致程序不能正常处理或处理错误。常见于登录注册、密码找回、信息查看、交易支付等场景。 二、类型: 未授权访…

常见的密码的分类和用途场景原理

一、按用途和管理分类 核心密码&#xff1a; 使用场景&#xff1a;专门用于保护国家最高等级的绝密信息&#xff0c;常见于政府、军队、外交等领域的重要通信及数据加密。特点&#xff1a;极高安全性&#xff0c;由国家密码管理部门统一严格管理&#xff0c;具体算法和密钥严格…

【C语言进阶篇】动态内存管理(一)

个人主页&#xff1a; 倔强的石头的博客 系列专栏 &#xff1a;C语言指南 C语言刷题系列 待补充完善

Linux的一些基本指令

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

华为od真题--2023C卷-地图寻宝华为OD真题题解

小华地图寻宝 题目描述 小华按照地图去寻宝&#xff0c;地图上被划分成m行和n列的方格&#xff0c;横纵坐标范围分别是[0,n-1]和[0,m-1]。在横坐标和纵坐标的数位之和不大于k的方格中存在黄金(每个方格中仅存在一克黄金)&#xff0c;但横坐标和纵坐标之和大于k的方格存在危险不…

编译执行篇

文章目录 11.1 compile()11.2 eval()11.3 exec()11.4 repr() 11.1 compile() 在Python中&#xff0c;compile()是一个内置函数&#xff0c;用于将字符串编译成字节码或AST&#xff08;抽象语法树&#xff09;对象&#xff0c;以便稍后被exec()或eval()函数执行。这对于执行动态…

2024最新华为OD机试试题库全 -【加密算法】- C卷

1. 🌈题目详情 1.1 ⚠️题目 有一种特殊的加密算法,明文为一段数字串,经过密码本查找转换,生成另一段密文数字串。 规则如下: 明文为一段数字串由 0~9 组成 密码本为数字 0~9 组成的二维数组 需要按明文串的数字顺序在密码本里找到同样的数字串,密码本里的数字串是…

YOLOv8跟踪分割+单目测距(python)

YOLOv8跟踪分割+单目测距(python) 1. 相关配置2. 测距原理和相机标定2.1 测距原理2.2 相机标定3. 相机测距3.1 测距模块3.2 测距添加4. 实验效果4.1 创建主代码4.2 实验效果相关链接 1. YOLOV7 + 单目测距(python) 2. YOLOV5 + 单目测距(python)