一、寄存器初始化

led的引脚是
在这里插入图片描述
LED使用GPIO1_IO03引脚。下面是初始化的步骤。

1.打开引脚的时钟
在这里插入图片描述在这里插入图片描述
打开gpio1的时钟,先清0 在写1

	val = readl(IMX6U_CCM_CCGR1);//注意 这个地址是虚拟地址 ioremap
    val &= ~(3<<26);/* bit26 27清零 */
    val |= (3<<26);/* 打开gpio1时钟 */
    writel(val,IMX6U_CCM_CCGR1);

2.复用模式
在这里插入图片描述
在这里插入图片描述
引脚上电默认就是复用为GPIO1_IO03

writel(5,SW_MUX_GPIO1_IO03);//注意 这个地址是虚拟地址 ioremap

3.引脚电气属性寄存器
在这里插入图片描述

	HYS(bit16):使能迟滞比较器,当 IO 作为输入功能的时候有效,用于设置输入接收器的施密特触发器是否使能。如果需要对输入波形进行整形的话可以使能此位。此位为 0 的时候禁止迟滞比较器,为 1 的时候使能迟滞比较器。 这里不需要使用

在这里插入图片描述

PUS(bit15:14):设置上下拉电阻,100k下拉,47k上拉,100k上拉,22k上拉
PUE(bit13):当 IO 作为输入的时候,这个位用来设置 IO 使用上下拉还是状态保持器。当为 0的时候使用状态保持器,当为 1 的时候使用上下拉。状态保持器在IO 作为输入的时候才有用,顾名思义,就是当外部电路断电以后此 IO 口可以保持住以前的状态。
PKE(bit12):此为用来使能或者禁止上下拉/状态保持器功能,为0 时禁止上下拉/状态保持器,为 1 时使能上下拉和状态保持器。

在这里插入图片描述

ODE(bit11):当 IO 作为输出的时候,此位用来禁止或者使能开路输出,此位为 0 的时候禁止开路输出,当此位为 1 的时候就使能开路输出功能。

在这里插入图片描述

SPEED(bit7:6):当 IO 用作输出的时候,此位用来设置 IO 速度
DSE(bit5:3):当 IO 用作输出的时候用来设置 IO 的驱动能力。3.3V 下 R0 是 260Ω,1.8V 下 R0 是 150Ω,接 DDR 的时候是 240Ω
SRE(bit0):当此位为 0 的时候是低压摆率,当为 1 的时候是高压摆率。这里的压摆率就是 IO 电平跳变所需要的时间,比如从 0 到 1 需要多少时间,时间越小波形就越陡,说明压摆率越高;反之,时间越多波形就越缓,压摆率就越低。如果你的产品要过 EMC 的话那就可以使用小的压摆率,因为波形缓和,如果你当前所使用的 IO做高速通信的话就可以使用高压摆率。
writel(0x10B0,SW_PAD_GPIO1_IO03);//注意 地址是虚拟地址 ioremap

4.引脚方向
在这里插入图片描述

	val = readl(GPIO1_GDIR);//注意是虚拟地址
    val &= ~(1<<3);/* 清零 */
    val |= (1<<3);/* 设置为输出 */
    writel(val,GPIO1_GDIR);

二、驱动程序编写

#include <linux/ide.h>
#include <linux/module.h>
//#include <linux/kernel.h>
//#include <linux/delay.h>
// #include <linux/init.h>
// #include <linux/types.h>    //dev_t 
// #include <linux/kdev_t.h>   //设备号头文件
// #include <linux/kern_levels.h> //printk 消息级别
// #include <linux/errno.h> 
// #include <linux/gpio.h> 
// #include <asm/mach/map.h>
// #include <asm/uaccess.h>
// #include <asm/io.h>


/********************************************************************
 * led.c
 * 作者:张亚胜
 * 版本:V1.0
 * 描述:led驱动文件
 * 其他:无
 * 网站:www.s123.xyz
 * 日志:出版V1.0 2020/7/26 张亚胜创建
 * ******************************************************************/

#define LED_MAJOR 200   /* 主设备号 */
#define LED_NAME "led"  /* 设备名字 */ 

#define LED_OFF 0       /* 关灯 */ 
#define LED_ON  1       /* 开灯 */ 

/* 寄存器的物理地址 */
#define CCM_CCGR1_BASE           (0x020c406c)   //时钟
#define SW_MUX_GPIO1_IO03_BASE   (0x020E0068)   //复用
#define SW_PAD_GPIO1_IO03_BASE   (0x020E02F4)   //电气属性
#define GPIO1_DR_BASE            (0x0209C000)   //数据
#define GPIO1_GDIR_BASE          (0x0209C004)   //方向

/* 映射后打寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

//led操作函数
void led_switch(u8 sta)
{
    u32 val=0;
    if(sta == LED_ON){
        val = readl(GPIO1_DR);
        val &= ~(1<<3);//GPIO1_IO03 bit3清零
        writel(val,GPIO1_DR);
    }
    else if(sta == LED_OFF)
    {
        val = readl(GPIO1_DR);
        val |= (1<<3);
        writel(val,GPIO1_DR);
    }
}


static int led_open(struct inode *inode,struct file *filp)
{
    return 0;
}

static ssize_t led_read(struct file *filp,char __user *buf,size_t cnt,loff_t *offt)
{
    return 0;
}

static ssize_t led_write(struct file *filp,const char __user *buf,size_t cnt,loff_t *offt)
{
    int retval=0;
    unsigned char databuf[1];
    unsigned char ledsta;

    retval = copy_from_user(databuf,buf,cnt);
    if(retval < 0){
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }

    ledsta = databuf[0];
    led_switch(ledsta);
    return 0;
}

static int led_release(struct inode *inode,struct file *filp)
{
    return 0;
}

static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .release = led_release,
    .read = led_read,
    .write = led_write,
};

static int __init led_init(void)
{
    int retval = 0;
    u32 val = 0;

    /* 1.获取寄存器的虚拟地址 */
    IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE,4);
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE,4);
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE,4);
    GPIO1_DR = ioremap(GPIO1_DR_BASE,4);
    GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE,4);

    /* 2.使能GPIO1时钟 */
    val = readl(IMX6U_CCM_CCGR1);
    val &= ~(3<<26);/* bit26 27清零 */
    val |= (3<<26);/* 打开gpio1时钟 */
    writel(val,IMX6U_CCM_CCGR1);

    /* 3.设置复用功能 */
    writel(5,SW_MUX_GPIO1_IO03);//GPIO1_IO03
    /* 设置io属性 */
    writel(0x10B0,SW_PAD_GPIO1_IO03);
    /* 4.设置输出功能 */
    val = readl(GPIO1_GDIR);
    val &= ~(1<<3);/* 清零 */
    val |= (1<<3);/* 设置为输出 */
    writel(val,GPIO1_GDIR);

    /* 5.默认关闭led */
    val = readl(GPIO1_DR);
    val |= (1<<3);
    writel(val,GPIO1_DR);

    /* 6.注册字符设备驱动 */
    retval = register_chrdev(LED_MAJOR,LED_NAME,&led_fops);
    if(retval < 0){
        printk("register chrdev failed!\r\n");
        return -EIO;
    }

    return retval;
}

static void __exit led_exit(void)
{
    /* 取消映射 */
    iounmap(IMX6U_CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);

    /* 注销字符设备驱动 */
    unregister_chrdev(LED_MAJOR,LED_NAME);
}


module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zys");

三、测试app编写

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
/********************************************************************
 * 文件名:ledApp.c
 * 作者:张亚胜
 * 版本:V1.0
 * 描述:ledApp测试文件
 * 其他:使用方法:./ledApp /dev/led 0 //写0 关灯
 *              ./ledApp /dev/led 1 //写1 开灯
 * 网站:www.s123.xyz
 * 日志:出版V1.0 2020/7/26 张亚胜创建
 * ******************************************************************/

/*
* @description : main 主程序
* @param - argc : argv 数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
    int fd,retval;
    char *filename;
    char buf[100];

    if(argc != 3){
        printf("params err!\r\n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename,O_RDWR);
    if(fd<0){
        printf("open err!\r\n");
        return -1;
    }
    buf[0] = atoi(argv[2]);
    write(fd,buf,1);
    
   retval = close(fd);
   return retval;
}

四、编译

编译驱动程序,Makefile如下,make生成led.ko

KERNELDIR := /home/zys/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)
obj-m := led.o

bulid: kernel_modules

kernel_modules: 
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean: 
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
 

编译应用程序ledApp

arm-linux-gnueabihf-gcc ledApp.c -o ledApp

五、测试

将编译出来的 led.ko和 ledApp这两个文件拷贝到 rootfs/lib/modules/4.1.15 目录中,
重启开发板,进入到目录 lib/modules/4.1.15 中,输入如下命令加载 led.ko 驱动模块:

depmod //第一次加载驱动的时候需要运行此命令
modprobe led.ko //加载驱动

驱动加载成功以后创建“/dev/led”设备节点,命令如下:

mknod /dev/led c 200 0

驱动节点创建成功以后就可以使用 ledApp 软件来测试驱动是否工作正常,输入如下命令打
开 LED 灯:

./ledApp /dev/led 1 //打开 LED 灯

如果要卸载驱动的话输入如下命令即可:

rmmod led.ko

在这里插入图片描述
可以正常开灯关灯

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐