Linux驱动开发 - LED驱动

news/2024/12/21 23:04:48/

驱动程序

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define LED_MAJOR 200  /* 主设备号 */
#define LED_NAME "led" /* 设备名字 */#define LEDOFF 0 /* 关灯 */
#define LEDON 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;/** @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 << 3);writel(val, GPIO1_DR);}else if (sta == LEDOFF){val = readl(GPIO1_DR);val |= (1 << 3);writel(val, GPIO1_DR);}
}/** @description		: 打开设备* @param - inode 	: 传递给驱动的inode* @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量* 					  一般在open的时候将private_data指向设备结构体。* @return 			: 0 成功;其他 失败*/
static int led_open(struct inode *inode, struct file *filp)
{return 0;
}/** @description		: 从设备读取数据* @param - filp 	: 要打开的设备文件(文件描述符)* @param - buf 	: 返回给用户空间的数据缓冲区* @param - cnt 	: 要读取的数据长度* @param - offt 	: 相对于文件首地址的偏移* @return 			: 读取的字节数,如果为负值,表示读取失败*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}/** @description		: 向设备写数据* @param - filp 	: 设备文件,表示打开的文件描述符* @param - buf 	: 要写给设备写入的数据* @param - cnt 	: 要写入的数据长度* @param - offt 	: 相对于文件首地址的偏移* @return 			: 写入的字节数,如果为负值,表示写入失败*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[1];unsigned char ledstat;retvalue = copy_from_user(databuf, buf, cnt);if (retvalue < 0){printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 获取状态值 */if (ledstat == LEDON){led_switch(LEDON); /* 打开LED灯 */}else if (ledstat == LEDOFF){led_switch(LEDOFF); /* 关闭LED灯 */}return 0;
}/** @description		: 关闭/释放设备* @param - filp 	: 要关闭的设备文件(文件描述符)* @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,.read = led_read,.write = led_write,.release = led_release,
};/** @description	: 驱动出口函数* @param 		: 无* @return 		: 无*/
static int __init led_init(void)
{int retvalue = 0;u32 val = 0;/* 初始化LED *//* 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); /* 清楚以前的设置 */val |= (3 << 26);  /* 设置新值 */writel(val, IMX6U_CCM_CCGR1);/* 3、设置GPIO1_IO03的复用功能,将其复用为*    GPIO1_IO03,最后设置IO属性。*/writel(5, SW_MUX_GPIO1_IO03);/*寄存器SW_PAD_GPIO1_IO03设置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_GPIO1_IO03);/* 4、设置GPIO1_IO03为输出功能 */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、注册字符设备驱动 */retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);if (retvalue < 0){printk("register chrdev failed!\r\n");return -EIO;}return 0;
}/** @description	: 驱动出口函数* @param 		: 无* @return 		: 无*/
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("lk");

函数说明

  • led_switch

    • 这个函数的功能是实际去调用readl和writel函数

    • readl–>从内存映射的 I/O 空间读取数据,readl 从 I/O 读取 32 位数据 ( 4 字节 )

    • writel–>往内存映射的 I/O 空间上写数据,wirtel I/O 上写入 32 位数据 (4字节)

      //读操作函数一共有三个
      u8 readb(const volatile void __iomem *addr)
      u16 readw(const volatile void __iomem *addr)
      u32 readl(const volatile void __iomem *addr)
      //readb、 readw 和 readl 这三个函数分别对应 8bit、 16bit 和 32bit 读操作,参数 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)
      //writeb、 writew 和 writel 这三个函数分别对应 8bit、 16bit 和 32bit 写操作,参数 value 是要写入的数值, addr 是要写入的地址。
      
  • led_open

    • 打开设备(对于驱动来说传递给驱动的inode)
  • led_read

    • 从设备读取数据(这里LED只涉及下发数据)
  • led_write

    • 向设备写数据(这里就是接收到应用程序写入的命令之后,将值传给led_switch
  • led_release

    • 关闭/释放设备
  • led_fops

    • 相当于映射函数吧
  • led_init

    • 先对寄存器地址映射(根据原理图及数据手册确定I/O和地址)
    • 使能GPIO1时钟(相关寄存器功能设置)
    • 设置GPIO1_IO03为输出功能
    • 注册字符设备驱动(向内核注册一个字符设备)完成注册后,在 /proc/devices 中的第一个字符设备我们就看到了
  • led_exit

    • 取消映射
    • 注销字符设备驱动

应用程序

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"#define LEDOFF 	0
#define LEDON 	1/** @description		: main主程序* @param - argc 	: argv数组元素个数* @param - argv 	: 具体参数* @return 			: 0 成功;其他 失败*/
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("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]);	/* 要执行的操作:打开或关闭 *//* 向/dev/led文件写入数据 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 关闭文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}

对 led 的驱动文件进行最基本的打开、关闭、写操作

运行效果

led.ko和 ledApp这两个文件拷贝到 /lib/modules/4.1.15-g0423506 目录中

insmod led.ko //加载驱动

创建“/dev/led”设备节点

mknod /dev/led c 200 0

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

./ledApp /dev/led 0 // 关闭LED


http://www.ppmy.cn/news/364240.html

相关文章

Linux驱动_新LED驱动

一、新字符驱动原理 1、以前的字符驱动使用register_chedev注册字符设备&#xff0c;需要手动指定主设备号 2、新版字符型驱动有两种方式注册字符设备&#xff1a;alloc_chrdev_region(自动申请设备号&#xff09;和register_chrdev_region(手动申请主设备号)。两种申请方式的注…

一、LED驱动电路

该专栏笔记整理于哔哩哔哩up主 硬件工程师桥 &#xff0c;个人感觉up主视频质量还是很高的&#xff0c;对硬件感兴趣的小伙伴可以戳蓝字关注一波~ 文档中原理图部分&#xff08;不包含对原视频截图中涉及的原理图&#xff09;统一使用 立创EDA 进行绘制 同时&#xff0c;由于个…

linux设备驱动--LED驱动

linux设备驱动--LED驱动 最近正在学习设备驱动开发&#xff0c;因此打算写一个系列博客&#xff0c;即是对自己学习的一个总结&#xff0c;也是对自己的一个督促&#xff0c;有不对&#xff0c;不足&#xff0c;需要改正的地方还望大家指出&#xff0c;而且希望结识志同道合的朋…

【立创开源】RY3730-带充电的恒流驱动LED照明灯

如题所示&#xff0c;作者为了把手上的RY3730恒流驱动芯片和IP2312锂电池充电芯片用完&#xff0c;就做了这么一个板子&#xff0c;甚至顺便把之前打的亚克力板也用完了( 开源地址 RY3730-带充电的恒流驱动LED照明灯 - 嘉立创EDA开源硬件平台 嘉立创EDA开源硬件平台&#xff…

LED驱动程序--可拓展的LED驱动程序

LED驱动程序–可拓展的LED驱动程序 一.把驱动拆分成通用的框架&#xff08;leddrv.c&#xff09;&#xff0c;具体的硬件操作&#xff08;board_X.c&#xff09; 实现一个通用的框架leddrv.c和 具体的硬件操作。想要操作那个硬件就将相应的框架和硬件操作一起编译成对应的驱动…

i.MX 6ULL 驱动开发 五:LED 驱动

一、原理分析 i.MX6ULL裸机开发 一&#xff1a;LED_lqonlylove的博客-CSDN博客 二、pinctrl 子系统和gpio子系统基本概念 Linux 驱动开发 二十一&#xff1a;pinctrl子系统和gpio子系统基本概念_lqonlylove的博客-CSDN博客_设备树gpio 三、linux 源码对于 pinctrl 子系统的…

LED灯驱动介绍

一&#xff1a;简单字符设备驱动开发一般分为下述几个步骤&#xff1a; 第一步&#xff1a; 先调用module_init注册加载与module_exit卸载驱动模块的函数。 编写出驱动模块入口&#xff08;加载&#xff09;函数与驱动模块出口&#xff08;卸载&#xff09;函数。 调用MODULE_L…

公司招人面了一个00后测试,可以说是内卷届的天花板.....

公司前段缺人&#xff0c;也面了不少测试&#xff0c;结果竟然没有一个合适的。一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资也不低&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。令我印象最深的是一个00后测试员&#xff0c;他…