【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.hqwc.cn/news/564221.html

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

相关文章

qt Qt Remote Object(QtRO)实现进程间通信

简介 Qt Remote Object简称QtRO&#xff0c;这是Qt5.9以后官方推出来的新模块&#xff0c;专门用于进程间通信&#xff08;IPC&#xff09;。是基于Socket来封装的&#xff0c;兼容LPC和RPC。LPC即Local Process Communication&#xff0c;而RPC是指Remote Process Communicat…

计算机软件安全

一、软件安全涉及的范围 1.1软件本身的安全保密 软件的本质与特征&#xff1a; 可移植性 寄生性 再生性 可激发性 攻击性 破坏性 …… 知识产权与软件盗版 软件商品交易形式不透明&#xff0c;方式多样&#xff0c;传统商标标识方法不适用&#xff1b; 盗版方法简捷…

基于java+springboot+vue实现的研究生志愿填报辅助系统(文末源码+Lw+ppt)23-600

摘 要 二十一世纪我们的社会进入了信息时代&#xff0c;信息管理系统的建立&#xff0c;大大提高了人们信息化水平。传统的管理方式对时间、地点的限制太多&#xff0c;而在线管理系统刚好能满足这些需求&#xff0c;在线管理系统突破了传统管理方式的局限性。于是本文针对这…

Verilog刷题笔记45

题目&#xff1a;Given the finite state machine circuit as shown, assume that the D flip-flops are initially reset to zero before the machine begins. Build this circuit. 解题&#xff1a; module top_module (input clk,input x,output z ); wire [2:0]size;dtou…

CTF-辨别细菌

题目描述&#xff1a;try your best to find the flag. 进入靶场后发现是一个游戏&#xff0c;需要全部答对才可以得到最后的flag 查看了一下源码&#xff0c;发现有一个答案模板的模块 尝试解释一下代码 <!-- 答案模版 --> <script id"template_game_pi…

2024.3.9|第十五届蓝桥杯模拟赛(第三期)

2024.3.9|十五届蓝桥杯模拟赛&#xff08;第三期&#xff09; 第一题 第二题 第三题 第四题 第五题 第六题 第七题 第八题 第九题 第十题 心有猛虎&#xff0c;细嗅蔷薇。你好朋友&#xff0c;这里是锅巴的C\C学习笔记&#xff0c;常言道&#xff0c;不积跬步无以至千里&…

自媒体洗稿软件文心一言api洗稿软件介绍

大家好&#xff0c;我是淘小白~ 这几天给一个客户写了一个文心一言api洗稿的软件。 一、客户要求&#xff1a; 1、采集头条文章&#xff08;软件内置可采集头条文章网址、微信文章网址、搜狐文章网址&#xff09; 2、调用文心一言api 3、多线程并发 4、逐段改写文章 5、…

inputStream.avaliable()方法网络操作读取不全BUG

一、问题描述 公司有个需求&#xff0c;就是调用方&#xff08;我&#xff09;需要把pdf文件转为Base64字符串作为参数传递为被调用方&#xff0c;以下是大致转换过程&#xff1a; URL url new URL("http://xxxx.pdf");HttpURLConnection uc (HttpURLConnection) …

ideaSSM 高校公寓交流员管理系统bootstrap开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 idea 开发 SSM 高校公寓交流管理系统是一套完善的信息管理系统&#xff0c;结合SSM框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&…

python--list容器、列表

1.python官方内置的容器 list: set: tuple: dict: 弱数据类语言通通没有数组&#xff0c;因为数组指的是 类型固定、大小固定、连续的内存空间。 2.链表&#xff1a; 非连续内存空间 python用的是双向链表 单向链表&#xff1a;优点&#xff1a;不浪费内存&#xf…

基于springboot+vue的码头船只货柜管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

01.数据归档工具的选择-Percona Toolkit,并centos7.9中安装

1.需求 1.1.在实际的业务使用过程中&#xff0c;我们既要考虑服务器硬件的成本&#xff0c;也要考虑系统的稳定性。所以就有了数据归档的这个业务需求了。我们需要把一些老的数据&#xff0c;比如两年前的数据移出去。增强数据库的性能。 1.2.在进行数据归档的过程中&#xf…