I.MX6U嵌入式Linux驱动开发(2)led实验

通过应用程序对 I.MX6U-ALPHA 开发板上的LED 灯进行开关操作。
先编写led的驱动程序,再编写应用程序。

1、驱动程序的编写

1.1、准备工作

在Ubuntu中新建2_led文件夹,输入如下命令:

cd /linux/IMX6ULL/Linux_Drivers
mkdir 2_led
cp 1_chrdevbase/ * ./2_led/       
cd 2_led/
rm chrdevbase.code.workspace chrdevbaseAPP
mv chrdevbaseAPP.c ledAPP.c
mv chrdevbase.c led.c

1.2、编写框架

在vscode中打开文件夹,打开led.c文件,头文件不变,其余的都删除,自己敲写驱动框架。
可以参考Linux源码中的驱动来编写。

//led.c文件
#define LED_MAJOR 200/* 主设备号 */
#define LED_NAME "led" /* 设备名称 */

static int led_open(struct inode *inode, struct file *filp)
{
	return 0;
}

static ssize_t led_read(struct file *filp, chr  __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

static ssize_t led_write(struct file *filp, chr  __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

static int led_relaese(struct inode *inode, struct file *filp)
{
	return 0;
}

/* 设备操作函数 */
static const struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = led_release,
};

/* 入口 */
static int __int led_init(void)
{
    int ret = 0;
	/* 注册驱动 */
	ret = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
	if(ret < 0)
	{
		printk("register chrdev failed!\r\n");
		return -EIO;
	}
	printk("led_init\r\n");//在编写框架的时候先用一个输出来验证一下
	return 0;
}
/* 出口 */
static void __exit led_exit(void)
{
	/* 注销设备 */
	unregister_chrdev(LED_MAJOR, LED_NAME);
	printk("led_exit\r\n");
}
/* 注册驱动加载和卸载 */
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yang");

框架搭建完了之后,编译.c文件,可以使用Makefile,只需要将上一节中的Makefile中的文件名字改成led.o就行。

在vscode终端中输入“make”即可编译。

测试:
(1)将生成的.ko文件拷贝到我们挂在的文件系统下面:

sudo cp led.ko /home/yang/linux/nfs/rootfs/lib/modules/4.1.15 -f

(2)在串口中挂载led.ko模块
在挂载的时候出现了下图的这种情况:
在这里插入图片描述

挂载之前写的驱动模块时,是正常的,然后挂载新的.ko文件会报错,输入depmod命令即可。

1.3、驱动程序编写

参考裸机程序代码,我们将初始化内容放在加载设备驱动这块代码里。

1.3.1、地址映射

MMU全称叫做 Memory Manage Unit,也就是内存管理单元。完成虚拟空间到物理空间的映射。对于 32 位的处理器来说,虚拟地址范围是 2^32=4GB,我们的开发板上有 512MB 的 DDR3,这 512MB 的内存就是物理内存,经过 MMU 可以将其映射到整个 4GB 的虚拟空间.

Linux 内核启动的时候会初始化 MMU,设置好内存映射,设置好以后 CPU 访问的都是虚拟地址。

GPIO1_IO03 引脚的复用寄存器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 的地址为 0X020E0068。

如果没有开启 MMU 的话直接向 0X020E0068 这个寄存器地址写入数据就可以配置 GPIO1_IO03 的复用功能。现在开启了 MMU,并且设置了内存映射,因此就不能直接向 0X020E0068 这个地址写入数据了。我们必须得到 0X020E0068 这个物理地址在 Linux 系统里面对应的虚拟地址,这里就涉及到了物理内存和虚拟内存之间的转换,需要用到两个函数:ioremap 和 iounmap。

//led.c文件
/* 寄存器物理地址 */
#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;

static int __init led_init(viod)
{
/* 初始化LED灯,地址映射 */
	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);
}

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 );
}

1.3.2、 I/O 内存访问函数

在初始化了LED灯后,我们需要访问这些地址,当外部寄存器或内存映射到内存空间时,称为 I/O 内存。Linux 内核推荐使用一组操作函数来对映射后的内存进行读写操作。

/* 初始化 */
/* 时钟配置 */
val = readl(IMX6U_CCM_CCGR1 );
val &= ~(3 << 26);/* 清除以前的配置bit26,27 */
value|= 3 << 26;/* bit26 27置1 */
writel(val,IMX6U_CCM_CCGR1 );

writel(0x5, SW_MUX_GPIO1_IO03);/* 设置复用 */
writel(0x10B0, SW_PAD_GPIO1_IO03);/* 设置电气属性 */

val = readl(GPIO1_GDIR);
val |= 1<<3;//bit3置1,设置为输出
writel(val, GPIO1_GDIR);

val = readl(GPIO1_DR);
val &= ~(1<<3);//bit3置0,打开led
writel(val, GPIO1_DR );

1.3.3、 测试

在Ubuntu中的VSCode的终端输入“make"命令进行编译。然后将生成的.ko文件复制到4.1.15文件中:

sudo cp led.ko /home/yang/linux/nfs/rootfs/lib/modules/4.1.15/ -f

此时的灯,不会亮,在串口界面,切换到4.1.15文件夹中,加载模块

modprobe led.ko //等亮
rmod led.ko    //卸载灯没有灭,因为关闭的时候没有加载灯关闭的代码

在取消地址映射前面加载:灯关闭的代码

static void __exit led_exit(void)
{
	int val = 0;
	
	val = readl(GPIO1_DR);
	val |= (1<<3);//bit3置1,关闭led
	writel(val, GPIO1_DR );
	/* 取消地址映射 */
}

2、应用程序编写

我们通过应用程序写0,1来控制灯的亮灭。

2.1、led.c编写

在led.c的函数static int led_release()添加关闭灯的代码。

static int led_release(struct inode *inode, struct file *file)
{
	unsigned int val = 0;
	val = readl(GPIO1_DR);
	val |= (1 << 3);
	writel(val, GPIO1_DR);
	return 0;
}

在led_write()中添加

int retvalue;
unsigned char databuf[1];
retvalue = copy_from_user(databuf, buf, count);
if(retvalue < 0)
{
	printk("kernel write failed!\r\n");
	return -EFAULT;
}
/* 判断开灯还是关灯 */
led_switch(databuf[0]);

判断是开灯还是关灯,可以写个函数

#define LEDOFF 0
#define LEDON 1
static void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON){
		val = readl(GPIO1_DR);
		val &= ~(1<<3);//bit3置0,打开led
		writel(val, GPIO1_DR );
	}else if(sta == LEDOFF){
		val = readl(GPIO1_DR);
		val |= (1 << 3);
		writel(val, GPIO1_DR);
	}
	
}

编译一下。

2.2、ledAPP.c编写

/*argc:应用程序参数个数
 *argv[]:具体的参数内容,字符串形式
 *./ledAPP <filename> <0:1> 0表示关灯,1表示开灯
 *./ledAPP /dev/led 0 关灯
 *./ledAPP /dev/led 1 开灯
 */
 #define LEDOFF 0
 #define LEDON 1
 int main(int argc, char *argv[])
 {
 	int fd,ret;
 	char *filename;
 	unsigned char databuf[1];//定义一个数据缓冲区
 	/* 检查命令是否输入正确 */
	if(argc != 3){
		printf("Error Usage!\r\n");
		return -1;
	}
	
 	filename = argv[];
 	fd = open(filename, O_RDWR);
 	if(fd < 0){
 		printf("file %s open failed!\r\n", filename);
 		return -1;
 	}
 	databuf[0] = atoi(argv[2]);//将字符转换为数字
	ret = write(fd, databuf,sizeof(databuf));
	if(ret < 0){
		printf("LED Control failed!\r\n");
		close(fd);
		return -1;
	}
	close(fd);
	
 }

检查一下驱动程序,在led_release()中不能把灯关了,因为应用程序有关闭文件的功能,如果在led.c中把灯关了,则刚打开灯,灯就关了。
编译驱动程序、应用程序:

make
arm-linux-gnueabihf-gcc ledAPP.c -o ledAPP
sudo cp led.ko ledAPP /home/yang/linux/nfs/rootfs/lib/modules/4.1.15/ -f

3、测试

在串口中:
进入/lib/modules/4.1.15/

/lib/modules/4.1.15/ # lsmod
/lib/modules/4.1.15/ # modprobe led.ko //加载驱动
/lib/modules/4.1.15/ # cat /proc/devices
/lib/modules/4.1.15/ # mknod /dev/led.c 200 0  //创建设备节点
/lib/modules/4.1.15/ # ls /dev/led -l
/lib/modules/4.1.15/ # ./ledAPP /dev/led 0
/lib/modules/4.1.15/ # ./ledAPP /dev/led 1
/lib/modules/4.1.15/ # rmmod led.ko
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值