建设网站主题,网站开发实用吗,平台是什么意思有哪些,在线网站代码生成器前言
今天是儿童节#xff0c;挣个奖牌给小孩玩玩。 在 linux 驱动大家庭中#xff0c;LED 驱动算是个儿童#xff0c;今天就写写他吧。正好之前写过他的婴儿时期《i.MX6ULL 裸机点亮 LED》#xff0c;记得那时候他还穿着开裆裤呢#xff0c;裸鸡嘛。
ioremap()
裸机程…前言
今天是儿童节挣个奖牌给小孩玩玩。 在 linux 驱动大家庭中LED 驱动算是个儿童今天就写写他吧。正好之前写过他的婴儿时期《i.MX6ULL 裸机点亮 LED》记得那时候他还穿着开裆裤呢裸鸡嘛。
ioremap()
裸机程序也好、linux 驱动程序也好最终都是要操作真实设备的那就要操作物理地址设备寄存器。之前写裸机程序由于没有开启 MMUCPU 操作的地址就是物理地址。现在写 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/ledstep3执行测试程序
# ./led_app /dev/led 1
# ./led_app /dev/led 0看到 led 亮、灭