1022|6

441

帖子

3

TA的资源

纯净的硅(高级)

楼主
 

okmx6q -- led驱动程序开发实验 [复制链接]

使用寄存器方式测试LED驱动。

 

一、硬件部分

 

测试使用引脚EIM_A17

 

二、软件部分

 

2.1、led.c

#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 IOMUXC_SW_MUX_CTL_PAD_EIM_ADDR17_BASE 	(0X020E00F0)
#define IOMUXC_SW_PAD_CTL_PAD_EIM_ADDR17_BASE 	(0X020E0404)
#define GPIO2_DR_BASE							(0X020A0000)
#define GPIO2_GDIR_BASE							(0X020A0004)

/* 映射后的寄存器虚拟地址指针 */
//static void __iomem *IMX6Q_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO2_IO21;
static void __iomem *SW_PAD_GPIO2_IO21;
static void __iomem *GPIO2_DR;
static void __iomem *GPIO2_GDIR;

/*
 * [url=home.php?mod=space&uid=1068364]@description[/url] : LED打开/关闭
 * @param - sta 	: LEDON(0) 打开LED,LEDOFF(1) 关闭LED
 * [url=home.php?mod=space&uid=784970]@return[/url] : 无
 */
void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON) {
		val = readl(GPIO2_DR);
		val &= ~(1 << 21);	
		writel(val, GPIO2_DR);
	}else if(sta == LEDOFF) {
		val = readl(GPIO2_DR);
		val|= (1 << 21);	
		writel(val, GPIO2_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、寄存器地址映射 */
	SW_MUX_GPIO2_IO21 = ioremap(IOMUXC_SW_MUX_CTL_PAD_EIM_ADDR17_BASE, 4);
  	SW_PAD_GPIO2_IO21 = ioremap(IOMUXC_SW_PAD_CTL_PAD_EIM_ADDR17_BASE, 4);
	GPIO2_DR = ioremap(GPIO2_DR_BASE, 4);
	GPIO2_GDIR = ioremap(GPIO2_GDIR_BASE, 4);

	/* 3、设置GPIO2_IO21的复用功能,将其复用为
	 *    GPIO2_IO21,最后设置IO属性。
	 */
	writel(5, SW_MUX_GPIO2_IO21);
	
	/*寄存器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_GPIO2_IO21);

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

	/* 5、默认关闭LED */
	val = readl(GPIO2_DR);
	//val |= (1 << 21);	
	val &= ~(1 << 21);
	writel(val, GPIO2_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)
{
	u32 val = 0;
	//关闭LED
	val = readl(GPIO2_DR);
	val |= (1 << 21);	
	writel(val, GPIO2_DR);

	/* 取消映射 */
	//iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO2_IO21);
	iounmap(SW_PAD_GPIO2_IO21);
	iounmap(GPIO2_DR);
	iounmap(GPIO2_GDIR);

	/* 注销字符设备驱动 */
	unregister_chrdev(LED_MAJOR, LED_NAME);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZZK");

 

2.2、ledApp.c

#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 i=0;
	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;
	}*/

	for(i=0;i<10;i++)
	{
		printf("led  on!\r\n");
		databuf[0] = 1;
		retvalue = write(fd, databuf, sizeof(databuf));
		if(retvalue < 0){
			printf("LED Control Failed!\r\n");
			close(fd);
			return -1;
		}
		sleep(1);

		printf("led  off!\r\n");
		databuf[0] = 0;
		retvalue = write(fd, databuf, sizeof(databuf));
		if(retvalue < 0){
			printf("LED Control Failed!\r\n");
			close(fd);
			return -1;
		}
		sleep(1);
	}



	retvalue = close(fd); /* 关闭文件 */
	if(retvalue < 0){
		printf("file %s close failed!\r\n", argv[1]);
		return -1;
	}
	return 0;
}

 

2.3、Makefile

KERNELDIR := ~/okmx6q-c/linux-4.1.15
CURRENT_PATH := $(shell pwd)

obj-m := led.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

 

三、编译及运行

 

3.1、编译驱动程序

root@ThinkPad:~/okmx6q-c/app/led# make ARCH=arm
root@ThinkPad:~/okmx6q-c/app/led# cp led.ko /nfs_rootfs/imx6qdl-rootfs/lib/modules/4.1.15

 

3.2、编译应用程序

root@ThinkPad:~/okmx6q-c/app/led# $CC ledApp.c -o ledApp
root@ThinkPad:~/okmx6q-c/app/led# cp ledApp /nfs_rootfs/imx6qdl-rootfs/lib/modules/4.1.15

3.3、加载驱动程序

 

3.4、创建设备节点

root@imx6qsabresd:/lib/modules/4.1.15# mknod /dev/led c 200 0

 

3.5、测试应用程序


 

 

此帖出自ARM技术论坛

最新回复

感谢楼主的技术信息分享,内容丰富,图文并茂,受益匪浅   详情 回复 发表于 2024-4-21 21:07
点赞 关注
 

回复
举报

7244

帖子

2

TA的资源

版主

沙发
 

寄存器操作就会涉及到位运算了。

此帖出自ARM技术论坛

点评

是呀,涉及到底层操作。  详情 回复 发表于 2024-4-19 20:33
 
 
 

回复

441

帖子

3

TA的资源

纯净的硅(高级)

板凳
 
wangerxian 发表于 2024-4-19 16:17 寄存器操作就会涉及到位运算了。

是呀,涉及到底层操作。

此帖出自ARM技术论坛
 
 
 

回复

1286

帖子

4

TA的资源

版主

4
 
请问用的是哪一块开发板?
此帖出自ARM技术论坛

点评

飞凌的OKMX6Q-C这款  详情 回复 发表于 2024-4-21 16:54
 
 
 

回复

441

帖子

3

TA的资源

纯净的硅(高级)

5
 
beyond_笑谈 发表于 2024-4-21 13:10 请问用的是哪一块开发板?

飞凌的OKMX6Q-C这款

此帖出自ARM技术论坛
 
 
 

回复

1286

帖子

4

TA的资源

版主

6
 

设备节点是不是可以在设备树里直接增加,然后编译设备树,不用单独的创建设备节点?

此帖出自ARM技术论坛
 
 
 

回复

755

帖子

4

TA的资源

纯净的硅(高级)

7
 

感谢楼主的技术信息分享,内容丰富,图文并茂,受益匪浅

此帖出自ARM技术论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表