0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【i.MX6ULL】驱动开发4——点亮LED(寄存器版)

码农爱学习 来源:码农爱学习 作者:码农爱学习 2022-05-21 21:26 次阅读

上篇文章(【i.MX6ULL】驱动开发3--GPIO寄存器配置原理),介绍了i.MX6ULL芯片的GPIO的工作原理与寄存器配置。

本篇,就要来实际操作一下GPIO,实现板子上LED灯的亮灭控制。

在介绍如何通过寄存器来控制LED之前,需要先来了解一下有关Linux地址映射相关的知识。

1 地址映射

Linux或是STM32,对于硬件的控制,本质都是操作寄存器,在对应的地址进行数据的读写。若是在裸机开发中,可以控制CPU直接操作寄存器的地址,实现相应的功能,其过程是这样的:

poYBAGKI5cqAbBZXAAArtF3Tv-A952.png

linux环境,一般是不会直接访问物理内存,因为如果用户不小心修改了内存中的数据,很有可能造成错误甚至系统崩溃。为了避免这些问题,linux内核便引入了MMU和TLB进行内存地址映射,通过访问虚拟地址实现对实际物理地址的读写:

pYYBAGKI5dSAAeTnAAA0oU74bLY109.png

1.1 MMU介绍

MMU,Memory Manage Unit,即内存管理单元,它提供统一的内存空间抽象,程序通过访问虚拟内存中的地址,MMU将虚拟地址(Virtual Address)翻译成实际的物理地址(Physical Address) ,之后CPU即可操作实际的物理地址。

MMU具有如下功能:

保护内存: MMU给一些指定的内存块设置了读、写以及可执行的权限,这些权限存储在页表当中,MMU会检查CPU当前所处的是特权模式还是用户模式,只有和操作系统所设置的权限匹配才可以访问。

提供方便统一的内存空间抽象,实现虚拟地址到物理地址的转换:CPU可以运行在虚拟的内存当中,虚拟内存一般要比实际的物理内存大很多,使得CPU可以运行比较大的应用程序。

1.2 TLB介绍

TLB,Translation Lookaside Buffer,即转译后备缓冲器,也称页表缓存,里面存放的是一些页表文件(虚拟地址到物理地址的转换表),又称为快表技术。

当CPU第一次查找一个虚拟地址时,硬件通过3级页表(page table)得到最终的PPN(Physical Page Number),TLB会保存虚拟地址到物理地址的映射关系。这样在下一次访问同一个虚拟地址时,处理器通过查看TLB来直接返回物理地址,而不需要通过page table得到结果,从而提高地址转换的效率。

1.3 I/O映射函数

Linux内核启动的时候会初始化MMU,设置好内存映射,设置好以后CPU访问的都是虚拟地址。

那在程序编写的时候,如何进行物理内存和虚拟内存之间的转换呢?这就需要用到两个函数:ioremap和iounmap。

ioremap()

ioremap函数用将物理地址映射为虚拟地址。

#define ioremap(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE) 

/**
 * paddr: 被映射的 IO 起始地址(物理地址)
 * size: 需要映射的空间大小,以字节为单位
 * return: 一个指向__iomem类型的指针,映射成功后便返回一段虚拟地址空间的起始地址
 */
void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype)
{ 
	return arch_ioremap_caller(phys_addr, size, mtype, __builtin_return_address(0)); 
} 

iounmap()

iounmap函数的作用是释放掉ioremap函数所做的映射,即反向操作,在卸载驱动的时候需要调用。

/**
 * addr: 要取消映射的虚拟地址空间首地址
 * return: void
 */
void iounmap (volatile void __iomem *addr) 

1.4 I/O内存访问函数

在使用ioremap函数将物理地址转换成虚拟地址之后,理论上我们便可以直接读写 I/O 内存,但是为了符合驱动的跨平台以及可移植性,我们应该使用 linux 中指定的函数(如:iowrite8()、iowrite16()、iowrite32()、ioread8()、ioread16()、ioread32() 等)去读写 I/O 内存,而非直接通过映射后的指向虚拟地址的指针进行访问。读写 I/O 内存的函数如下:

unsigned int ioread8(void __iomem *addr);  /*读取一个字节*/
unsigned int ioread16(void __iomem *addr); /*读取一个字*/
unsigned int ioread32(void __iomem *addr); /*读取一个双字*/

void iowrite8(u8 b, void __iomem *addr);   /*写入一个字节*/
void iowrite16(u16 b, void __iomem *addr); /*写入一个字*/
void iowrite32(u32 b, void __iomem *addr); /*写入一个双字*/

对于读I/O而言,他们都只有一个 __iomem 类型指针的参数,指向被映射后的地址,返回值为读取到的数据;

对于写I/O而言他们都有两个参数,第一个为要写入的数据,第二个参数为要写入的地址,返回值为空。

与这些函数相似的还有writeb、writew、writel、readb、readw、readl 等

u8  readb(const volatile void __iomem *addr);
u16 readw(const volatile void __iomem *addr); 
u32 readl(const volatile void __iomem *addr);
    
void writeb(u8 value,  volatile void __iomem *addr); 
void writew(u16 value, volatile void __iomem *addr); 
void writel(u32 value, volatile void __iomem *addr);     

在 ARM 架构下,writex(readx)函数与 iowritex(ioreadx)有一些区别,writex(readx)不进行端序的检查,而 iowritex(ioreadx)会进行端序的检查。

2 程序编写

2.1 LED驱动程序

led驱动也是属于字符设备驱动的,之前介绍了新旧两种字符驱动的写法,本篇led驱动就按照新字符设置驱动的写法来编写。

关于新字符设备的驱动模块,可参考之前的文章:【i.MX6ULL】驱动开发2--新字符设备开发模板

这里再放一张新字符设备开发的模板框架

pYYBAGIyAdiAMiETAAEM8HQykVI076.png

2.1.1 字符设备的基本框架

//字符设备结构体
struct newchrled_dev{
	dev_t         devid;	/* 设备号   */
	struct cdev   cdev;		/* cdev     */
	struct class  *class;	/* 类       */
	struct device *device;	/* 设备     */
	int           major;	/* 主设备号 */
	int           minor;	/* 次设备号 */
};
struct newchrled_dev chrdevled; /* led设备 */

//打开 读取 写入 释放, 基础文件操作函数
static int chrdevled_open(struct inode *inode, struct file *filp)
{
    /*设置chrdevled为私有数据*/
    return 0;
}
static ssize_t chrdevled_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}
static ssize_t chrdevled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}
static int chrdevled_release(struct inode *inode, struct file *filp)
{
	return 0;
}

//设备操作函数结构体
static struct file_operations chrdevled_fops = {
	.owner   = THIS_MODULE,	
	.open    = chrdevled_open,
	.read    = chrdevled_read,
	.write   = chrdevled_write,
	.release = chrdevled_release,
};

//驱动入口函数
static int __init chrdevled_init(void)
{
    /* 初始化LED */
    /* 注册字符设备驱动(操作chrdevled_fops) */
    return 0;
}

//驱动出口函数
static void __exit chrdevled_exit(void)
{
    /* 取消IO映射 */
    /* 注销字符设备驱动 */
}

//驱动的入口和出口函数 
module_init(chrdevled_init);
module_exit(chrdevled_exit);

//LICENSE和作者信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxpcb");

2.1.2 具体完善

1)GPIO寄存器宏定义

需要配置相关的寄存器,就要对照着LED这个GPIO的硬件按需配置。

有关GPIO的各种寄存器的使用原理介绍,请参考上篇文章的介绍。

poYBAGIzTTGAWCbZAACbd73nSec681.png
/* 寄存器物理地址 */
#define CCM_CCGR1_BASE				(0X020C406C)	
#define SW_MUX_SNVS_TAMPER3_BASE    (0X02290014)
#define SW_PAD_SNVS_TAMPER3_BASE    (0X02290058)
#define GPIO5_DR_BASE				(0X020AC000)
#define GPIO5_GDIR_BASE				(0X020AC004)

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_SNVS_TAMPER3;
static void __iomem *SW_PAD_SNVS_TAMPER3;
static void __iomem *GPIO5_DR;
static void __iomem *GPIO5_GDIR;

CCM 是用来进行时钟的使能,其寄存器包括CCGR0~CCGR6,因为LED用到GPIO属于GPIO5,它对应的时钟配置寄存器就是CCM_CCGR1

MUX 是用来将IO复用为GPIO

PAD 是用来配置IO的基本参数(驱动能力、压摆率、上下拉等)

GPIO5_DR 数据寄存器,当GPIO为输出模式时,用来设置对应的高低电平

GPIO5_GDIR 方向寄存器,用来设置输入还是输出

以上是先对这些需要使用的寄存器的地址声明宏定义(这些寄存器的地址可通过查阅i.MX6ULL数据手册得到),然后再声明对应的虚拟地址的指针,因为Linux开始MMU后,就不能直接对寄存器的地址直接操作了,需要使用映射后的虚拟地址。

2)GPIO硬件初始化

主要包括以下几步:

寄存器地址映射:将需要用的寄存器的物理地址映射为虚拟地址

使能GPIO1时钟:就是配置CCM_CCGR1寄存器

设置GPIO5_IO03的复用功能:配置MUX和PAD寄存器

设置GPIO5_IO03为输出功能:配置GPIO5_GDIR方向寄存器

初始默认关闭LED:配置GPIO5_DR数据寄存器

具体配置过程如下,主要这里使用"与"和"或"的位运算操作,来配置寄存器中对应位的值。

static void led_hardware_init(void)
{
    u32 val = 0;
        
    /* 1、寄存器地址映射 */
    IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
    SW_MUX_SNVS_TAMPER3 = ioremap(SW_MUX_SNVS_TAMPER3_BASE, 4);
    SW_PAD_SNVS_TAMPER3 = ioremap(SW_PAD_SNVS_TAMPER3_BASE, 4);
    GPIO5_DR = ioremap(GPIO5_DR_BASE, 4);
    GPIO5_GDIR = ioremap(GPIO5_GDIR_BASE, 4);

    /* 2、使能GPIO1时钟 */
    val = readl(IMX6U_CCM_CCGR1);
    val &= ~(3 << 26);    /* 清除以前的设置 */
    val |= (3 << 26);    /* 设置新值 */
    writel(val, IMX6U_CCM_CCGR1);

    /* 3、设置GPIO5_IO03的复用功能,将其复用为GPIO5_IO03,最后设置IO属性 */
    writel(5, SW_MUX_SNVS_TAMPER3);
    
    /*寄存器SW_PAD_SNVS_TAMPER3设置IO属性
     *bit 16:0 HYS关闭
     *bit [15:14]: 00 默认下拉
     *bit [13]: 0 kepper功能
     *bit [12]: 1 pull/keeper使能
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 110 R0/6驱动能力
     *bit [0]: 0 低转换率
     */
    writel(0x10B0, SW_PAD_SNVS_TAMPER3);

    /* 4、设置GPIO5_IO03为输出功能 */
    val = readl(GPIO5_GDIR);
    val &= ~(1 << 3);    /* 清除以前的设置 */
    val |= (1 << 3);    /* 设置为输出 */
    writel(val, GPIO5_GDIR);

    /* 5、默认关闭LED */
    val = readl(GPIO5_DR);
    val |= (1 << 3);    
    writel(val, GPIO5_DR);
}

3)字符设备初始化

需要定义led字符设备结构体,来管理这个led设备。

/*newchr设备结构体 */
struct newchrled_dev{
    dev_t         devid;    /* 设备号   */
    struct cdev   cdev;     /* cdev     */
    struct class  *class;   /* 类       */
    struct device *device;  /* 设备     */
    int           major;    /* 主设备号 */
    int           minor;    /* 次设备号 */
};

struct newchrled_dev chrdevled; /* led设备 */

具体的led字符设备初始化流程:

初始化LED的GPIO(上面刚介绍)

创建设备号

初始化cdev字符设备

添加cdev字符设备

创建类

创建设备

static int __init chrdevled_init(void)
{
    /* 初始化LED */
    led_hardware_init();

    /* 注册字符设备驱动 */
    /* 1、创建设备号 */
    if (chrdevled.major) /* 定义了设备号 */
    {
        chrdevled.devid = MKDEV(chrdevled.major, 0);
        register_chrdev_region(chrdevled.devid, chrdevled_CNT, chrdevled_NAME);
    } 
    else /* 没有定义设备号 */
    {
        alloc_chrdev_region(&chrdevled.devid, 0, chrdevled_CNT, chrdevled_NAME);    /* 申请设备号 */
        chrdevled.major = MAJOR(chrdevled.devid);    /* 获取分配号的主设备号 */
        chrdevled.minor = MINOR(chrdevled.devid);    /* 获取分配号的次设备号 */
    }
    printk("chrdevled major=%d,minor=%d\n",chrdevled.major, chrdevled.minor);    
    
    /* 2、初始化cdev */
    chrdevled.cdev.owner = THIS_MODULE;
    cdev_init(&chrdevled.cdev, &chrdevled_fops);
    
    /* 3、添加一个cdev */
    cdev_add(&chrdevled.cdev, chrdevled.devid, chrdevled_CNT);

    /* 4、创建类 */
    chrdevled.class = class_create(THIS_MODULE, chrdevled_NAME);
    if (IS_ERR(chrdevled.class)) 
    {
        return PTR_ERR(chrdevled.class);
    }

    /* 5、创建设备 */
    chrdevled.device = device_create(chrdevled.class, NULL, chrdevled.devid, NULL, chrdevled_NAME);
    if (IS_ERR(chrdevled.device)) 
    {
        return PTR_ERR(chrdevled.device);
    }
    
    printk("chrdevled init done!\n");
    return 0;
}

4)LED亮灭控制

驱动程序中,对于LED的控制,可以分为两步。

第一步是接收和解析应用层发来的控制数据(0或1来控制亮灭),将控制参数传递给具体的开关led的函数:

static ssize_t chrdevled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    unsigned char databuf[1];
    unsigned char ledstat;
    
    /* 接收用户空间传递给内核的数据并且打印出来 */
    if(0 != copy_from_user(databuf, buf, cnt))
    {
        printk("kernel recevdata failed!\n");
        return -EFAULT;
    }
    
    ledstat = databuf[0];        /* 获取状态值 */

    if(ledstat == LEDON) 
    {    
        led_switch(LEDON);        /* 打开LED灯 */
        printk("led on!\n");
    } 
    else if(ledstat == LEDOFF) 
    {
        led_switch(LEDOFF);       /* 关闭LED灯 */
        printk("led off!\n");
    }
    
    return 0;
}

第二步就是根据指令参数,通过控制数据寄存器GPIO5_DR来实现GPIO的高低电平输出,从而实现LED的亮灭:

void led_switch(u8 sta)
{
    u32 val = 0;
    if(sta == LEDON) 
    {
        val = readl(GPIO5_DR);
        val &= ~(1 << 3);    
        writel(val, GPIO5_DR);
    }
    else if(sta == LEDOFF) 
    {
        val = readl(GPIO5_DR);
        val|= (1 << 3);    
        writel(val, GPIO5_DR);
    }    
}

5)驱动退出

驱动不再使用时,需要注销相关的设备:

static void led_hardware_exit(void)
{
    iounmap(IMX6U_CCM_CCGR1);
    iounmap(SW_MUX_SNVS_TAMPER3);
    iounmap(SW_PAD_SNVS_TAMPER3);
    iounmap(GPIO5_DR);
    iounmap(GPIO5_GDIR);
}

首先释放掉这些地址映射:

static void __exit chrdevled_exit(void)
{
    /* 取消IO映射 */
    led_hardware_exit();
    
    /* 注销字符设备驱动 */
    cdev_del(&chrdevled.cdev);/*  删除cdev */
    unregister_chrdev_region(chrdevled.devid, chrdevled_CNT); /* 注销设备号 */

    device_destroy(chrdevled.class, chrdevled.devid);
    class_destroy(chrdevled.class);
    
    printk("chrdevled exit done!\n");
}

驱动程序基本就是这些,完整的程序见我的gitee仓库:https://gitee.com/xxpcb/imx6ull

2.2 LED应用程序

写完了驱动程序(BSP),还要写对应的应用程序(APP)。

目前的应用程序比较简短,因为在Linux中,一切皆文件,所以,对于LED的控制,就是通过向文件中写入0或1来实现LED的亮灭。

先来对0和1进行宏定义:

#define LEDOFF  0 /*长灭*/
#define LEDON   1 /*长亮*/

然后就是main函数了:

int main(int argc, char *argv[])
{
    int fd, retvalue;
    char *filename;
    unsigned char databuf[1];

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

    filename = argv[1];

    /* 打开led驱动文件 */
    fd  = open(filename, O_RDWR);
    if(fd < 0)
    {
        printf("Can't open file %s\r\n", filename);
        return -1;
    }
    
    /* 要执行的操作:打开或关闭 */
    databuf[0] = atoi(argv[2]); 
    
    /* 向设备驱动(/dev/chrdevled)写数据 */
    retvalue = write(fd, databuf, sizeof(databuf));
    if(retvalue < 0)
    {
        printf("write file %s failed!\r\n", filename);
        close(fd);
        return -1;
    }

    /* 关闭设备 */
    retvalue = close(fd);
    if(retvalue < 0)
    {
        printf("Can't close file %s\r\n", filename);
        return -1;
    }

    return 0;
}

3 实验测试

3.1 程序编译与下载

再来复习一下基本步骤:

ubuntu中通过gcc交叉编译器编译出led的驱动程序和应用程序

搭建局域网环境(电脑和linux板子连接到同一个路由器下,Linux板子以及烧录了镜像文件,能够正常运行)

通过tftp服务将两个文件发送到linux板子的对应目录中(/lib/modules/4.1.15目录)

进行字符设备的加载,以及文件读写测试(控制led亮灭)

poYBAGIx__mAJAMxAAFqlflsgR8688.png

程序的具体编译过程与之前的类似,这里不再赘述,可参考之前的文章(如这篇:【i.MX6ULL】驱动开发2--新字符设备开发模板)

3.2 实验现象

首先来看一下板子上LED的位置,如下图的电路上的标号D14处:

poYBAGKI58-AARrbAAvgSp2DtwI988.png

然后在串口中,按照之前介绍字符设备的加载流程,先加载led字符设备,然后就可以下向应用程序写1或0来控制led的亮灭了。

pYYBAGKI59qAKrYyAAC-dufz6w8036.png

led点亮的效果如下:

poYBAGKI5-GAEdbJABaJTzrkbIo962.png

4 总结

本篇主要介绍了如何通过操作寄存器来点亮i.MX6ULL开发板上的led,通过编写LED对应的驱动程序和应用程序,实现程序设计的分层。

因为Linux使用了MMU进行虚拟地址管理,因此在操作寄存器时,要进行地址映射后再操作。最后通过程序的实际测试,验证了led的亮灭功能。

审核编辑:符乾江

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    5056

    文章

    18950

    浏览量

    301468
  • 驱动
    +关注

    关注

    12

    文章

    1816

    浏览量

    85089
  • Linux
    +关注

    关注

    87

    文章

    11191

    浏览量

    208623
收藏 人收藏

    评论

    相关推荐

    i.MX6ULL 驱动开发7—按键输入捕获与GPIO输入配置与高低电平读取

    本篇主要介绍了i.MX6ULL的按键检测的使用,主要的知识点是设备树的修改,以及GPIO的输入配置与高低电平的读取。
    的头像 发表于 05-24 09:11 6060次阅读
    <b class='flag-5'>i.MX6ULL</b> <b class='flag-5'>驱动</b><b class='flag-5'>开发</b>7—按键输入捕获与GPIO输入配置与高低电平读取

    i.MX6ULL嵌入式Linux开发1-uboot移植初探

    本系列教程以i.MX6ULL处理的ARM开发板为实验基础,学习记录嵌入式Linux开发的各种知识与经验,主要内容包括嵌入式Linux移植,嵌入式Linux
    的头像 发表于 03-07 08:57 3821次阅读
    <b class='flag-5'>i.MX6ULL</b>嵌入式Linux<b class='flag-5'>开发</b>1-uboot移植初探

    使用i.MX6ULL开发板进行Linux根文件系统的完善

    上一篇推文讲了怎么移植根文件系统,并在i.MX6ULL开发板中运行起来,但是会出现一些提示,现在来进行根文件的完善。
    发表于 10-17 11:13 751次阅读

    移植NXP官方linux 5.4内核到i.MX6ULL开发

    本文描述移植NXP官方 linux 5.4 内核到i.MX6ULL开发板。
    发表于 12-19 11:10 1987次阅读

    I.MX6ULL终结者开发板裸机仿真jlink调试

    I.MX6ULL‘终结者’开发板预留了JTAG仿真接口,并给出了开发文档,可以实现在JLINK仿真条件下的单步跟踪、断点调试等功能,使得开发
    发表于 07-07 10:56

    i.MX6ULL开发板硬件资源

    迅为i.MX6ULL 终结者开发板硬件资源非常丰富,几乎将 i.MX6ULL 芯片的所有资源都扩展引出到底板上了,底板提供了丰富的外设接口,开发板的尺寸是 190mm*125mm,充分
    发表于 12-29 06:18

    初识 i.MX6ULL 寄存器

    裸机开发_L1_汇编LED实验0. 本节目标1. 硬件层电路2. 初识 i.MX6ULL 寄存器2.1 i.MX6ULL 时钟控制
    发表于 12-20 07:13

    关于i.MX6ULL配置GPIO

    正如学习C语言时写的第一段代码都是“HelloWorld!”,接触一款新的处理时往往是从点亮一个LED开始;而点亮一个LED,则需要操作这
    发表于 08-05 10:37

    飞凌i.MX6ULL开发板的评测,再次进阶拥有更高的性价比

    MCIMX6Y2开发设计,采用先进的ARMCortex-A7内核,运行速度高达800MHz。i.MX6ULL应用处理包括一个集成的电源管理模块,降低了外接电源的复杂性,并简化了上电时
    发表于 10-27 11:55 1441次阅读
    飞凌<b class='flag-5'>i.MX6ULL</b><b class='flag-5'>开发</b>板的评测,再次进阶拥有更高的性价比

    基于NXP i.MX6ULL处理的FETMX6ULL-C核心板

    合作伙伴,飞凌不负美誉,基于i.MX6ULL匠心打造的FETMX6ULL-S核心板一经问世便好评不断,且已有数百家来自工业、医疗、电力、物联网等行业的用户采用此款核心板快速完成了整机产品的开发上市。
    发表于 04-11 15:05 1125次阅读
    基于NXP <b class='flag-5'>i.MX6ULL</b>处理<b class='flag-5'>器</b>的FETMX<b class='flag-5'>6ULL</b>-C核心板

    i.MX6ULL驱动开发6——GPIO子系统点亮LED

    本篇介绍了使用**Pinctrl子系统与GPIO子系统**的方式来点亮LED,与之前的寄存器点亮LED与设备树版
    的头像 发表于 05-21 21:50 3122次阅读
    【<b class='flag-5'>i.MX6ULL</b>】<b class='flag-5'>驱动</b><b class='flag-5'>开发</b><b class='flag-5'>6</b>——GPIO子系统<b class='flag-5'>点亮</b><b class='flag-5'>LED</b>

    基于i.MX6ULL点亮LED

    都说入门一款芯片的第一步是点亮LED,但是i.MX6ULL入门门槛比较高,特别是通过自学入门的,这个系列已经写了好久了,最近打算在项目不急的时候加快一下学习进度,现在就开始学习一下怎么点亮
    的头像 发表于 03-06 09:09 758次阅读

    使用pinctrl和gpio子系统实现LED驱动

    前边已经学了两种点灯,本质依然还是通过配置寄存器;在学习STM32的时候除了学习配置一下寄存器,基本都是使用库来开发,那么在i.MX6ULL还使用
    的头像 发表于 04-03 10:17 1251次阅读

    【北京迅为】i.MX6ULL开发板移植 Debian 文件系统

    【北京迅为】i.MX6ULL开发板移植 Debian 文件系统
    的头像 发表于 02-10 15:34 1087次阅读
    【北京迅为】<b class='flag-5'>i.MX6ULL</b><b class='flag-5'>开发</b>板移植 Debian 文件系统

    基于i.MX6ULL的掉电检测设计与软件测试

    基于i.MX6ULL的掉电检测设计与软件测试基于i.MX6ULL平台设计实现掉电检测功能,首先选择一路IO,利用IO电平变化触发中断,在编写驱动时捕获该路GPIO的中断,然后在中断响应函数中发
    的头像 发表于 11-09 10:40 782次阅读
    基于<b class='flag-5'>i.MX6ULL</b>的掉电检测设计与软件测试