【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. 驱动代码

cpp 复制代码
#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. 板级驱动代码

cpp 复制代码
#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__ */
cpp 复制代码
#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

cpp 复制代码
#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;
}
相关推荐
冉佳驹1 小时前
Linux ——— 多线程编程中的核心概念与技术实现
linux·条件变量·互斥锁·自旋锁·线程的概念·线程的创建、等待、分离·生产者消费模型
梦..1 小时前
电路EMC问题(二)
嵌入式硬件·硬件架构·硬件工程·pcb工艺
Skilce1 小时前
HAProxy
linux·运维·负载均衡
有一个好名字1 小时前
claude code安装
linux·运维·前端
亮子AI1 小时前
【Linux】如何拷贝目录?
linux·运维·服务器
starvapour2 小时前
Ubuntu更换显卡驱动后网络消失的问题
linux·运维·ubuntu
风酥糖2 小时前
在Termux中运行Siyuan笔记服务
android·linux·服务器·笔记
哼?~2 小时前
Linux信号产生
linux
Je1lyfish2 小时前
CMU15-445 (2026 Spring) Project#2 - B+ Tree
linux·数据结构·数据库·c++·sql·spring·oracle
乐大师2 小时前
Linux普通用户设置开机自启服务
linux·服务器·开机自启动