《Linux驱动开发学习笔记记录》 0-1 led模块

学习记录—基于韦东山老师教程

  1. 字符串驱动开发流程
    (1)确定主设备号
    (2)定义自己的file_operation结构体
    (3)实现对应的drv_open、drv_write等函数
    (4)编写一个入口函数,用来注册驱动程序
    (5)相应的编写一个出口函数,用于卸载驱动程序
  2. led 开发的步骤
    (1)看原理图,确定GPIO端口和输出高低电平时led的亮灭情况
    (2)使能:CCM用于设置向GPIO模块提供时钟,Clock Controller Module (时钟控制模块),找到对应的芯片手册,改变对应寄存器相应位的值
    (3)设置引脚工作模式为GPIO模式
    (4)设置引脚方向(GPIOx_GDIR):每位对应一个引脚,1-output,0-input
    (5)设置输出引脚电平(GPIOx_DR):每位对应一个引脚,1-高电平,0-低电平
    (6)读取引脚的电平(GPIOx_PSR):每位对应一个引脚,1-高电平,0-低电平
  3. 驱动程序的编写
    (1)加入头文件,打开Linux内核的源码,随便打开一个驱动程序,将其包含的头文件复制进去;
    (2)确定主设备号,可以先定为零,后调用register_chrdev()函数,返回一个合法的组设备好;
    (3)定义file_operation结构体:同理去找一个驱动程序,模仿着定义自己需要的结构体。
static const struct file_operations led_fops = {
	.owner		= THIS_MODULE,
	.write		= led_write,
	.open		= led_open,
};

(4)实现相应的驱动函数:

/* write函数 */
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
	char val;
	int retu;
	/* copy_from_user: get data from app */
	retu = copy_from_user(&val, buf, 1);
	/* to set gpio register: 1/0 */
	if (val){
		/* set gpio to let led on */
		*GPIO5_DR &= ~(1<<3);
	}
	else {
		/*set gpio to let led off */
		*GPIO5_DR |= (1<<3);
	}
	 
	return 0;
}

/* open函数 */
static int led_open(struct inode *inode, struct file *file) {
	/* 
	* enable gpio 
	* configure gpio5_io3 as gpio
	* configure gpio5_io3 as output
	*/
	*IOMUXC_GPIO5_IO03 &= ~0xf;
	*IOMUXC_GPIO5_IO03 |= 0x5;
	
	*GPIO5_GDIR |= (1<<3);
	
	return 0;
}


(5)在驱动开发中不能直接使用硬件地址,而是需要用ioremap()函数映射实际的地址再使用,此例子中因为此GPIO模块默认使能,且不需要查看电平高低,便省去对应两步。

/* register */
// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 Address: 229_0000h base + 14h offset = 229_0014h   0x2290000+0x14
static volatile unsigned int *IOMUXC_GPIO5_IO03;
// gpio5 020A_C000 
// GPIO direction register     input/output   Address: Base address + 4h offset   0x20AC004
static volatile unsigned int *GPIO5_GDIR;
// GPIO data register          0/1       Address: Base address + 0h offset   0X20AC000
static volatile unsigned int *GPIO5_DR;

	/* ioremap 映射实际地址 */
	// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 Address: 229_0000h base + 14h offset = 229_0014h   0x2290000+0x14
	IOMUXC_GPIO5_IO03 = ioremap(0x2290000+0x14, 4);
	// gpio5 020A_C000 
	// GPIO direction register	   input/output   Address: Base address + 4h offset   0x20AC004
	GPIO5_GDIR = ioremap(0x20AC004, 4);
	// GPIO data register		   0/1		 Address: Base address + 0h offset	 0X20AC000
	GPIO5_DR = ioremap(0X20AC000, 4);

(6)编写入口函数‘

/* 入口函数 */
static int __init led_init(void) {

	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 注册驱动程序 */
	major = register_chrdev(0, "yuelong_led", &led_fops);  //接收传回的主设备号,用于出口函数销毁

	/* ioremap 映射实际地址 */
	// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 Address: 229_0000h base + 14h offset = 229_0014h   0x2290000+0x14
	IOMUXC_GPIO5_IO03 = ioremap(0x2290000+0x14, 4);
	// gpio5 020A_C000 
	// GPIO direction register	   input/output   Address: Base address + 4h offset   0x20AC004
	GPIO5_GDIR = ioremap(0x20AC004, 4);
	// GPIO data register		   0/1		 Address: Base address + 0h offset	 0X20AC000
	GPIO5_DR = ioremap(0X20AC000, 4);


	/* 创建类和节点 */
	led_class = class_create(THIS_MODULE, "myled");
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "myled"); // 创建了 /dev/myled的设备节点

	
	return 0;
}

(7)编写出口函数

/* 出口函数 */
static void __exit led_exit(void) {

	/* 清楚映射实际地址 */
	iounmap(IOMUXC_GPIO5_IO03);
	iounmap(GPIO5_GDIR);
	iounmap(GPIO5_DR);
	/* 销毁节点和类 */
	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);

	/* 注销驱动程序 */
	unregister_chrdev(major, "yuelong_led");

}

(8)声明上述的入口与出口函数,才算真正的实现注册与卸载驱动程序,并且声明GPL协议:

// 声明上诉的入口和出口函数
module_init(led_init);
module_exit(led_exit);
// GPL协议
MODULE_LICENSE("GPL");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值