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

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

3天内不再提示

基于i.MX6ULL点亮LED

玩转单片机 来源:玩转单片机 2023-03-06 09:09 次阅读

都说入门一款芯片的第一步是点亮LED,但是i.MX6ULL入门门槛比较高,特别是通过自学入门的,这个系列已经写了好久了,最近打算在项目不急的时候加快一下学习进度,现在就开始学习一下怎么点亮一个LED,前边学的框架就是为了点亮LED做基础,点亮LED主要是学习怎么操作实际地址,都知道内核是不能直接操作地址的,就需要通过内核提供的API去操作实际地址;

|测试写入

在点亮LED前先测试一下APP往驱动写入数据,避免写入数据阶段就出现数据异常,再往下写就没多大意义;

chrdevbaseApp.c文件

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"


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


  if(argc != 3){
    printf("[APP]Error Usage!
");
    return -1;
  }


  filename = argv[1];


  /* 打开驱动文件 */
  fd  = open(filename, O_RDWR);
  if(fd < 0){
    printf("[APP]Can't open file %s
", filename);
    return -1;
  }


  /* 把第三个参数赋值给databuf */
  databuf[0] = atoi(argv[2]);


  /* 向设备驱动写数据 */
  memcpy(writebuf, databuf, sizeof(databuf));
  retvalue = write(fd, writebuf, sizeof(databuf));
  if(retvalue < 0){
    printf("[APP]write file %s failed!
", filename);
  }


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


  return 0;
}

chrdevbase.c文件

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define CHRDEVBASE_CNT      1    /* 设备号个数 */
#define CHRDEVBASE_NAME   "chrdevbase"  /* 名字 */


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


struct newchr_dev chrdevbase;/* 自定义字符设备 */


/*
 * @description    : 打开设备
 * @param - inode   : 传递给驱动的inode
 * @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量
 *             一般在open的时候将private_data指向设备结构体。
 * @return       : 0 成功;其他 失败
 */
static int chrdevbase_open(struct inode *inode, struct file *filp)
{
  printk("[BSP]chrdevbase open!
");
  filp->private_data = &chrdevbase; /* 设置私有数据 */
  return 0;
}


/*
 * @description    : 从设备读取数据 
 * @param - filp   : 要打开的设备文件(文件描述符)
 * @param - buf   : 返回给用户空间的数据缓冲区
 * @param - cnt   : 要读取的数据长度
 * @param - offt   : 相对于文件首地址的偏移
 * @return       : 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
  printk("chrdevbase read!
");
  return 0;
}


/*
 * @description    : 向设备写数据 
 * @param - filp   : 设备文件,表示打开的文件描述符
 * @param - buf   : 要写给设备写入的数据
 * @param - cnt   : 要写入的数据长度
 * @param - offt   : 相对于文件首地址的偏移
 * @return       : 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
  int retvalue = 0;
  char writebuf[1];


  /* 接收用户空间传递给内核的数据并且打印出来 */
  retvalue = copy_from_user(writebuf, buf, cnt);
  printk("[BSP]kernel recevdata data:%d!
",writebuf[0]);


  // printk("chrdevbase write!
");
  return 0;
}


/*
 * @description    : 关闭/释放设备
 * @param - filp   : 要关闭的设备文件(文件描述符)
 * @return       : 0 成功;其他 失败
 */
static int chrdevbase_release(struct inode *inode, struct file *filp)
{
  printk("[BSP]release!
");
  return 0;
}


/*
 * 设备操作函数结构体
 */
static struct file_operations chrdevbase_fops = {
  .owner = THIS_MODULE,  
  .open = chrdevbase_open,
  .read = chrdevbase_read,
  .write = chrdevbase_write,
  .release = chrdevbase_release,
};


/*
 * @description  : 驱动入口函数 
 * @param     : 无
 * @return     : 0 成功;其他 失败
 */
static int __init chrdevbase_init(void)
{
  /* 注册字符设备驱动 */
  /* 1、创建设备号 */
  if (chrdevbase.major) { /* 定义了设备号 */
    chrdevbase.devid = MKDEV(chrdevbase.major, 0);
    register_chrdev_region(chrdevbase.devid, CHRDEVBASE_CNT, CHRDEVBASE_NAME);
  } else { /* 没有定义设备号 */
    alloc_chrdev_region(&chrdevbase.devid, 0, CHRDEVBASE_CNT,CHRDEVBASE_NAME); /* 申请设备号 */
    chrdevbase.major = MAJOR(chrdevbase.devid); /* 获取主设备号 */
    chrdevbase.minor = MINOR(chrdevbase.devid); /* 获取次设备号 */
  }
  printk("newcheled major=%d,minor=%d
",chrdevbase.major,chrdevbase.minor);


  /* 2、初始化 cdev */
  chrdevbase.cdev.owner = THIS_MODULE;
  cdev_init(&chrdevbase.cdev, &chrdevbase_fops);


  /* 3、添加一个 cdev */
  cdev_add(&chrdevbase.cdev, chrdevbase.devid, CHRDEVBASE_CNT);


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


  /* 5、创建设备 */
  chrdevbase.device = device_create(chrdevbase.class, NULL,chrdevbase.devid, NULL, CHRDEVBASE_NAME);
  if (IS_ERR(chrdevbase.device)) {
    return PTR_ERR(chrdevbase.device);
  }


  return 0;
}


/*
 * @description  : 驱动出口函数
 * @param     : 无
 * @return     : 无
 */
static void __exit chrdevbase_exit(void)
{
  /* 注销字符设备 */
  cdev_del(&chrdevbase.cdev);/* 删除 cdev */
  unregister_chrdev_region(chrdevbase.devid, CHRDEVBASE_CNT);/* 注销设备号 */


  device_destroy(chrdevbase.class, chrdevbase.devid);/* 销毁设备 */
  class_destroy(chrdevbase.class);/* 销毁类 */


  printk("[BSP]chrdevbase exit!
");
}


/* 
 * 将上面两个函数指定为驱动的入口和出口函数 
 */
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);


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

实测操作如下图所示,没有问题就可以继续编写!

14dc525a-bb71-11ed-bfe3-dac502259ad0.png

|寄存器点亮LED

测试写入没有问题就继续补充驱动,通过配置寄存器来实现点亮LED,不同板子LED灯在不同的GPIO上,根据实际配置,下方是补充完整的驱动:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define CHRDEVBASE_CNT      1    /* 设备号个数 */
#define CHRDEVBASE_NAME   "chrdevbase"  /* 名字 */


#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */


/* 寄存器物理地址 */
// GPIO1时钟
#define CCM_CCGR1_BASE (0X020C406C)
// GPIO1 多路选择
#define SW_MUX_GPIO1_IO04_BASE  (0X020E006C)
// GPIO模式配置
#define SW_PAD_GPIO1_IO04_BASE  (0X020E02F8)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)


/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO04;
static void __iomem *SW_PAD_GPIO1_IO04;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;


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


struct newchr_dev chrdevbase;/* 自定义字符设备 */


/*
* @description : LED 打开/关闭
* @param - sta : LEDON(0) 打开 LED,LEDOFF(1) 关闭 LED
* @return : 无
*/
void led_switch(u8 sta)
{
  u32 val = 0;
  if(sta == LEDON) {
    val = readl(GPIO1_DR);
    val &= ~(1 << 4); 
    writel(val, GPIO1_DR);
  }else if(sta == LEDOFF) {
    val = readl(GPIO1_DR);
    val|= (1 << 4);
    writel(val, GPIO1_DR);
  } 
}


/*
* @description : LED 硬件初始化
* @param : 无
* @return : 无
*/
void led_hal_init(void)
{
  u32 val = 0;


  /* 初始化 LED */
  /* 1、寄存器地址映射 */
  IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
  SW_MUX_GPIO1_IO04 = ioremap(SW_MUX_GPIO1_IO04_BASE, 4);
  SW_PAD_GPIO1_IO04 = ioremap(SW_PAD_GPIO1_IO04_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); /* 清楚以前的设置 */
  val |= (3 << 26); /* 设置新值 */
  writel(val, IMX6U_CCM_CCGR1);


  /* 3、设置 GPIO1_IO04 的复用功能,将其复用为
  * GPIO1_IO04,最后设置 IO 属性。
  */
  writel(5, SW_MUX_GPIO1_IO04);


  /* 寄存器 SW_PAD_GPIO1_IO04 设置 IO 属性 */
  writel(0xD0B1, SW_PAD_GPIO1_IO04);


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


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


/*
* @description : 取消映射
* @param : 无
* @return : 无
*/
void led_hal_exit(void)
{
  iounmap(IMX6U_CCM_CCGR1);
  iounmap(SW_MUX_GPIO1_IO04);
  iounmap(SW_PAD_GPIO1_IO04);
  iounmap(GPIO1_DR);
  iounmap(GPIO1_GDIR);
}


/*
 * @description    : 打开设备
 * @param - inode   : 传递给驱动的inode
 * @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量
 *             一般在open的时候将private_data指向设备结构体。
 * @return       : 0 成功;其他 失败
 */
static int chrdevbase_open(struct inode *inode, struct file *filp)
{
  printk("[BSP]chrdevbase open!
");
  filp->private_data = &chrdevbase; /* 设置私有数据 */
  return 0;
}


/*
 * @description    : 从设备读取数据 
 * @param - filp   : 要打开的设备文件(文件描述符)
 * @param - buf   : 返回给用户空间的数据缓冲区
 * @param - cnt   : 要读取的数据长度
 * @param - offt   : 相对于文件首地址的偏移
 * @return       : 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
  printk("chrdevbase read!
");
  return 0;
}


/*
 * @description    : 向设备写数据 
 * @param - filp   : 设备文件,表示打开的文件描述符
 * @param - buf   : 要写给设备写入的数据
 * @param - cnt   : 要写入的数据长度
 * @param - offt   : 相对于文件首地址的偏移
 * @return       : 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
  int retvalue = 0;
  char writebuf[1];


  /* 接收用户空间传递给内核的数据并且打印出来 */
  retvalue = copy_from_user(writebuf, buf, cnt);
  printk("[BSP]kernel recevdata data:%d!
",writebuf[0]);


  if(writebuf[0] == LEDON) { 
    led_switch(LEDON); /* 打开 LED 灯 */
  } else if(writebuf[0] == LEDOFF) {
    led_switch(LEDOFF); /* 关闭 LED 灯 */
  }


  // printk("chrdevbase write!
");
  return 0;
}


/*
 * @description    : 关闭/释放设备
 * @param - filp   : 要关闭的设备文件(文件描述符)
 * @return       : 0 成功;其他 失败
 */
static int chrdevbase_release(struct inode *inode, struct file *filp)
{
  printk("[BSP]release!
");
  return 0;
}


/*
 * 设备操作函数结构体
 */
static struct file_operations chrdevbase_fops = {
  .owner = THIS_MODULE,  
  .open = chrdevbase_open,
  .read = chrdevbase_read,
  .write = chrdevbase_write,
  .release = chrdevbase_release,
};


/*
 * @description  : 驱动入口函数 
 * @param     : 无
 * @return     : 0 成功;其他 失败
 */
static int __init chrdevbase_init(void)
{
  /* 初始化硬件 */
  led_hal_init();


  /* 注册字符设备驱动 */
  /* 1、创建设备号 */
  if (chrdevbase.major) { /* 定义了设备号 */
    chrdevbase.devid = MKDEV(chrdevbase.major, 0);
    register_chrdev_region(chrdevbase.devid, CHRDEVBASE_CNT, CHRDEVBASE_NAME);
  } else { /* 没有定义设备号 */
    alloc_chrdev_region(&chrdevbase.devid, 0, CHRDEVBASE_CNT,CHRDEVBASE_NAME); /* 申请设备号 */
    chrdevbase.major = MAJOR(chrdevbase.devid); /* 获取主设备号 */
    chrdevbase.minor = MINOR(chrdevbase.devid); /* 获取次设备号 */
  }
  printk("newcheled major=%d,minor=%d
",chrdevbase.major,chrdevbase.minor);


  /* 2、初始化 cdev */
  chrdevbase.cdev.owner = THIS_MODULE;
  cdev_init(&chrdevbase.cdev, &chrdevbase_fops);


  /* 3、添加一个 cdev */
  cdev_add(&chrdevbase.cdev, chrdevbase.devid, CHRDEVBASE_CNT);


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


  /* 5、创建设备 */
  chrdevbase.device = device_create(chrdevbase.class, NULL,chrdevbase.devid, NULL, CHRDEVBASE_NAME);
  if (IS_ERR(chrdevbase.device)) {
    return PTR_ERR(chrdevbase.device);
  }


  return 0;
}


/*
 * @description  : 驱动出口函数
 * @param     : 无
 * @return     : 无
 */
static void __exit chrdevbase_exit(void)
{
  /* 取消映射 */
  led_hal_exit();


  /* 注销字符设备 */
  cdev_del(&chrdevbase.cdev);/* 删除 cdev */
  unregister_chrdev_region(chrdevbase.devid, CHRDEVBASE_CNT);/* 注销设备号 */


  device_destroy(chrdevbase.class, chrdevbase.devid);/* 销毁设备 */
  class_destroy(chrdevbase.class);/* 销毁类 */


  printk("[BSP]chrdevbase exit!
");
}


/* 
 * 将上面两个函数指定为驱动的入口和出口函数 
 */
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);


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

| 细节剖析

1、先看地址定义,包括物理地址和映射后的地址指针:

1504c5e6-bb71-11ed-bfe3-dac502259ad0.png

2、再看具体的配置,封装在led_hal_init函数中:

void led_hal_init(void)
{
  // LED 硬件初始化
}

3、再看输出电平配置,封装在led_switch函数中:

void led_switch(u8 sta)
{
//电平配置
}

4、再看调用位置,一般在加载驱动的时候就初始化寄存器,在写函数中判断参数来进行亮灭切换;

5、最后注销驱动的时候,需要把映射地址进行取消,一般都是初始化申请了什么资源,注销的时候就需要释放什么资源;

6、多次注销和加载驱动,看看效果是否正常,注意野火的板子是多彩灯,只操作一个gpio效果不是很明显,上面的代码就是控制R灯的亮灭;

152b3e2e-bb71-11ed-bfe3-dac502259ad0.png

又学会了一种点灯方式,在点灯的道路上越走越远,哈哈哈!

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

    关注

    237

    文章

    22450

    浏览量

    645879
  • 芯片
    +关注

    关注

    447

    文章

    47795

    浏览量

    409153
  • 内核
    +关注

    关注

    3

    文章

    1309

    浏览量

    39848
  • 函数
    +关注

    关注

    3

    文章

    3880

    浏览量

    61309
  • IMX6ULL
    +关注

    关注

    3

    文章

    15

    浏览量

    3906

原文标题:i.MX6ULL|点亮LED

文章出处:【微信号:玩转单片机,微信公众号:玩转单片机】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    i.MX6UL/i.MX6ULL开发常见问题】单独编译内核,uboot生成很多文件,具体用哪一个?

    i.MX6UL/i.MX6ULL开发常见问题》基于米尔电子 i.MX6UL/i.MX6ULL产品(V.10)2.3单独编译内核,uboot生成很多文件,具体用哪一个?答:内核编译出来
    发表于 07-01 17:50

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

    I.MX6ULL‘终结者’开发板预留了JTAG仿真接口,并给出了开发文档,可以实现在JLINK仿真器条件下的单步跟踪、断点调试等功能,使得开发研究i.MX6ULL处理器的相关技术更加直观便利。
    发表于 07-07 10:56

    i.MX6ULL开发板硬件资源

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

    迅为i.MX6ULL终结者线程基础线程终止与回收解析

    迅为i.MX6ULL终结者线程基础线程终止与回收
    发表于 04-02 06:43

    i.MX6ULL核心板资源

    `STAMP-IMX6ULL-CM是浙江启扬智能科技有限公司推出的基于 NXP i.MX6ULL 处理器的产品;i.MX 6ULL 是 NXP 推出的 ARM Cortex-A7 系列
    发表于 07-12 17:50

    Linux内核在i.mx6ull中的编译运行

    Linux内核在i.mx6ull的编译运行编译Linux Kernel需要使用lzop库,所以需要安装,否则编译内核会失败!!!sudo apt-get install lzop一、Linux
    发表于 11-05 07:14

    stm32f0717bt6i.mx6ull启动方式的区别是什么?

    stm32f0717bt6i.mx6ull启动方式的区别是什么?
    发表于 11-29 07:51

    初识 i.MX6ULL 寄存器

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

    ARM裸机篇之i.MX6ULL处理器资料分享

    1、i.MX6ULL处理器启动过程i.MX6ULL是NXP基于ARM Cortex-A7内核的单核处理器家族,主频可以高900MHz。i.MX6ULL应用处理器包含了电源管理模块,可以降低外部电源
    发表于 04-14 16:42

    关于i.MX6ULL配置GPIO

    正如学习C语言时写的第一段代码都是“HelloWorld!”,接触一款新的处理器时往往是从点亮一个LED开始;而点亮一个LED,则需要操作这款芯片的GPIO外设。那么作为广受欢迎的
    发表于 08-05 10:37

    I.MX6ULL无法枚举USB2514是为什么?

    你好目前,I.MX6ULL开发存在一些问题。其中之一是OTG USB2无法正常挂载USB2514,无法正确枚举下游设备,只显示设备id。usb设计要注意什么。
    发表于 04-03 06:55

    I.MX6ULL UART传输问题求解

    I.MX6ULL UART传输问题
    发表于 04-21 08:09

    如何使用Linux版本在i.mx6ull上启用USB网络共享?

    我从 lf-5.10-y 分支为 i.mx6ull evk 构建了 Linux。我将我的 android 手机连接到 usb otg 端口并在我的手机上启用网络共享。但是我没有看到 USB0 接口
    发表于 05-09 08:06

    已知的HAB漏洞是否会影响i.MX6ULL版本1.1?

    我有一个 NXP iMX6ULL rev 1.1,发现i.MX6ULL 的 HAB 机制中存在两个已知漏洞。但是,我所指的文档并未指定芯片版本。因此,我不确定这些漏洞是否会影响我的设备。你能帮我澄清一下吗?
    发表于 06-02 09:07

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

    本篇主要介绍了如何通过操作寄存器来点亮i.MX6ULL开发板上的led,通过编写LED对应的驱动程序和应用程序,实现程序设计的分层。
    的头像 发表于 05-21 21:26 2650次阅读
    【<b class='flag-5'>i.MX6ULL</b>】驱动开发4——<b class='flag-5'>点亮</b><b class='flag-5'>LED</b>(寄存器版)