当前位置: 首页 > news >正文

linux led 驱动

前言

今天是儿童节,挣个奖牌给小孩玩玩。
在这里插入图片描述
在 linux 驱动大家庭中,LED 驱动算是个儿童,今天就写写他吧。正好之前写过他的婴儿时期《i.MX6ULL 裸机点亮 LED》,记得那时候他还穿着开裆裤呢,裸鸡嘛在这里插入图片描述在这里插入图片描述在这里插入图片描述

ioremap()

裸机程序也好、linux 驱动程序也好,最终都是要操作真实设备的,那就要操作物理地址(设备寄存器)。之前写裸机程序,由于没有开启 MMU,CPU 操作的地址就是物理地址。现在写 linux 驱动程序不一样了,内核(包括驱动)操作的都是虚拟地址而无法直接操作物理地址。要想操作物理地址,就要上一个大杀器:ioremap(),可以参考我之前的一篇文章《ioremap()》,这里就不多介绍了。

代码

led.c

#include <asm/io.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>#define LED_MAJOR 200  /* 主设备号 */
#define LED_NAME "led" /* 字符设备名称: cat /proc/devices 显示的字符设备名称 */#define LEDOFF 0	   /* 关灯 */
#define LEDON 1		   /* 开灯 *//* 寄存器物理地址 */
#define CCM_CCR_BASE 0x020C4000	 // Clock Controller Module(CCM)
#define CCM_CCGR1 (CCM_CCR_BASE + 0x6C)
#define GPIO1_BASE 0x0209C000
#define GPIO1_DR (GPIO1_BASE + 0x0)
#define GPIO1_GDIR (GPIO1_BASE + 0x4)
#define SW_MUX_CTL_BASE 0x020E0000	// software mux control registers
#define SW_MUX_CTL_PAD_GPIO1_IO03 (SW_MUX_CTL_BASE + 0x68)
#define SW_PAD_CTL_PAD_GPIO1_IO03 (SW_MUX_CTL_BASE + 0x2F4)/* 映射后的寄存器虚拟地址指针 */
static void __iomem *V_CCM_CCGR1;
static void __iomem *V_SW_MUX_GPIO1_IO03;
static void __iomem *V_SW_PAD_GPIO1_IO03;
static void __iomem *V_GPIO1_DR;
static void __iomem *V_GPIO1_GDIR;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;
}void led_switch(u8 sta)
{u32 val;if (sta == LEDON) {val = readl(V_GPIO1_DR);val &= ~(1 << 3);writel(val, V_GPIO1_DR);} else if (sta == LEDOFF) {val = readl(V_GPIO1_DR);val |= (1 << 3);writel(val, V_GPIO1_DR);}
}/** @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!\n");return -EFAULT;}ledstat = databuf[0]; /* 获取状态值 */if (ledstat == LEDON) {led_switch(LEDON);	/* 打开LED灯 */} else if (ledstat == LEDOFF) {led_switch(LEDOFF); /* 关闭LED灯 */}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,
};static int __init led_init(void)
{int retvalue = 0;u32 val = 0;/* 1. 寄存器地址映射 */V_CCM_CCGR1 = ioremap(CCM_CCGR1, 4);V_SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_CTL_PAD_GPIO1_IO03, 4);V_SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_CTL_PAD_GPIO1_IO03, 4);V_GPIO1_DR = ioremap(GPIO1_BASE, 4);V_GPIO1_GDIR = ioremap(GPIO1_GDIR, 4);/* 2. 使能GPIO1时钟 */val = readl(V_CCM_CCGR1);val |= (3 << 26);writel(val, V_CCM_CCGR1);/* 3. 设置GPIO1_IO03的复用功能,将其复用为 GPIO1_IO03,最后设置IO属性 */writel(5, V_SW_MUX_GPIO1_IO03);/* 4. 设置GPIO1_IO03为输出功能 */val = readl(V_GPIO1_GDIR);val |= (1 << 3);writel(val, V_GPIO1_GDIR);/* 5. 配置引脚属性,驱动能力、速度、上下拉 */writel(0x10B0, V_SW_PAD_GPIO1_IO03);/* 6. 默认关闭 LED */val = readl(V_GPIO1_DR);val |= (1 << 3);writel(val, V_GPIO1_DR);/* 7. 注册字符设备驱动 */retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);if (retvalue < 0) {printk("register chrdev failed!\n");return -EIO;}return 0;
}static void __exit led_exit(void)
{/* 取消映射 */iounmap(V_CCM_CCGR1);iounmap(V_SW_MUX_GPIO1_IO03);iounmap(V_SW_PAD_GPIO1_IO03);iounmap(V_GPIO1_DR);iounmap(V_GPIO1_GDIR);/* 注销字符设备驱动 */unregister_chrdev(LED_MAJOR, LED_NAME);
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liyongjun");

led_app.c

#include "fcntl.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "sys/stat.h"
#include "sys/types.h"
#include "unistd.h"#define LEDOFF 0
#define LEDON 1int main(int argc, char *argv[])
{int fd, retvalue;char *filename;unsigned char databuf[1];if (argc != 3) {printf("Usage: %s devfile 0/1\n", argv[0]);return -1;}filename = argv[1];/* 打开设备文件驱动 */fd = open(filename, O_RDWR);if (fd < 0) {printf("file %s open failed!\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!\n");close(fd);return -1;}/* 关闭文件 */retvalue = close(fd);if (retvalue < 0) {printf("file %s close failed!\n", argv[1]);return -1;}return 0;
}

Makefile

KERNELDIR := ../../../linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/
CURRENT_PATH := $(shell pwd)obj-m := led.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) cleanrm led_appCROSS_COMPILE = ../../../tool/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gccapp:$(CROSS_COMPILE) led_app.c -o led_appinstall:cp led.ko led_app ../../../rootfs/home/root/

验证

step1:安装驱动程序

insmod led.ko

# cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
200 led
207 ttymxc

step2:创建字符设备文件

# mknod /dev/led c 200 0
#
# ls -lh /dev/led
crw-r--r-- 1 root root 200, 0 May 31 14:06 /dev/led

step3:执行测试程序

# ./led_app /dev/led 1
# ./led_app /dev/led 0

看到 led 亮、灭

http://www.lryc.cn/news/90828.html

相关文章:

  • 平面最近点对(分治算法)
  • 【基于前后端分离的博客系统】Servlet版本
  • 在线Excel绝配:SpreadJS 16.1.1+GcExcel 6.1.1 Crack
  • 一个轻量的登录鉴权工具Sa-Token 集成SpringBoot简要步骤
  • day 44 完全背包:518. 零钱兑换 II;377. 组合总和 Ⅳ
  • K8s in Action 阅读笔记——【5】Services: enabling clients to discover and talk to pods
  • 牛客网DAY2(编程题)
  • Java经典笔试题—day14
  • 一个帮助写autoprefixer配置的网站
  • C语言中的类型转换
  • String底层详解(包括字符串常量池)
  • C++ 里面lambda和函数指针的转换
  • 前端Rust开发WebAssembly与Swc插件快速入门
  • 【C++ 学习 ⑧】- STL 简介
  • 论文笔记--Deep contextualized word representations
  • 【MySQL高级篇笔记-性能分析工具的使用 (中) 】
  • 大学生数学建模题论文
  • 论文阅读 —— 滤波激光SLAM
  • JavaScript键盘事件
  • opengl灯光基础:2.1 光照基础知识
  • 大屏时代:引领信息可视化的新潮流
  • ChatGTP全景图 | 背景+技术篇
  • 计算机专业学习的核心是什么?
  • 基于springboot地方旅游系统的设计与实现
  • 一些学习资料链接
  • Webpack打包图片-JS-Vue
  • 进程控制(Linux)
  • C Primer Plus第十四章编程练习答案
  • 又名管道和无名管道
  • 操作系统复习4.1.0-文件管理结构