【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;
}
相关推荐
代码游侠2 分钟前
应用——Linux Framebuffer 图形库显示
linux·运维·服务器·数据库·笔记·算法
会飞的小新4 分钟前
Linux PCI 设备查看工具 lspci 的工作机制与使用方法
linux
xingzhemengyou15 分钟前
LINUX modprobe 智能加载和卸载内核模块
linux·服务器·前端
xingzhemengyou19 分钟前
Linux who指令查询和显示当前登录系统的用户信息
linux·服务器·网络
wadesir11 分钟前
Linux MySQL Sysbench一键部署与压测实战教程
linux·mysql·adb
米高梅狮子14 分钟前
6. Linux 硬盘分区管理
linux·运维·服务器
石头明月19 分钟前
基于XC7A35T的DDR3 IP核使用
嵌入式硬件·fpga开发
食咗未22 分钟前
Linux lrzsz文件传输工具的使用
linux·测试工具
HalvmånEver23 分钟前
Linux:基于匿名管道创建出简易进程池(进程间通信五)
linux·运维·服务器·c++·进程池·管道pipe
工程师老罗25 分钟前
龙芯2k0300 U盘烧录Linux系统,从Ubuntu到PMON自动启动
linux·运维·ubuntu