linux整合day1 (裸机加设备驱动)

it2024-07-15  40

imx6ull 点灯(各种版本)

买了imx6ull正点原子的断断续续的在学 最近打算系统的学习一下 但是不想从头开始 所以我想的是分模块的进行学习 比如点灯那么我就把寄存器版和驱动版一起拿来学简单就一天一个 类stm32比较 stm32在使用gpio的时候步骤基本都是

使能gpio的时钟(心脏嘛)配置电器属性配置io复用设置电平

了解imx6中IOMUX以及配置的方法

PAD控制寄存器 这个一般进行电气属性的控制MUX控制寄存器 这个一般就是控制IO复用的Select Input控制寄存器

上图得知 IOMUXC_SW_MUX_CTL_PAD_XX_XX IOMUXC_SW_PAD_CTL_PAD_XX_XX 主要就是这两个寄存器配置IO imx一共有5组io 每一组io 有最多有32个引脚 io复用为gpio之后要设置的八大寄存器 DR(data register):32位数据寄存器 最多对应32个GPIO 配置io后可以进行io电平的控制 GDIR(GPIO direction register) :32位寄存器 控制IOMUXC处于gpio模式的时候的方向控制 PSR(GPIO pad status register) :只读寄存器 32位 功能和输入状态下的 DR寄存器一样。 ICR1(interrupt configuration register1):配置低16位 ICR2(interrupt configuration register2):配置高16位 上面两个都是中断控制器两个位控制一个io 决定了什么方式触发中断 EDGE_SEL(edge select register) :设置边沿触发中断 比ICR寄存器优先级高 IMR(interrupt mask register):中断屏蔽寄存器 32位 控制gpio中断的使能 ISR(interrupt status register) :就是中断标志位 我们处理完中断后必须要往里面写零 清除标志位 直接操作寄存器

查看原理图得知 led由GPIO1_IO03控制

使能时钟 gpio相关的时钟主要是由CCM_CCGR(0~3)决定的 通过查数据手册得出 GPIO1_IO03 由 CCM_CCGR1(地址0x020C406C)的26,27位控制 00 所有模式均关闭外设时钟01 在运行模式下打开10 保留11 除了停止模式外 外设时钟全部使能 引脚复用 查询数据手册找到IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的地址 0x020E0068这个 0101 ALT5 — Select mux mode: ALT5 mux port: GPIO1_IO03 of instance: gpio1配置电气属性 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03的地址是0x020E02F4 电气属性常规配置 -配置八个GPIO寄存器 这个是点灯 所以设置 GDIR(GPIO direction register)设置为输出 查手册 地址0x020A4004 //此代码是地址都映射好了 #include "main.h" void clk_enable(void) { CCM_CCGR1 = 0x0C00 0000; //使能时钟 } void led_init(void) { //io复用 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03=0x5; //配置电气属性 /*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 低转换率*/ IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03=0X10B0; //设置DIR寄存器为输出 GPIO1_GDIR = 0X0000008; //设施DR寄存器输出的电平 GPIO1_DR = 0X0; } void led_on(void) { //改变DR寄存器的值 GPIO1_DR &= ~(1<<3); //原理图得 拉低灯亮 } void led_off(void) { //改变DR寄存器 GPIO1_DR |= (1<<3); } //在396Mhz主频下的延时函数 ms级 void delay_short(volatile unsigned int n) { while(n--){} } void delay(volatile unsigned int n) { while(n--) { delay_short(0x7ff); } } int main(void) { clk_enable(); led_init(); while(1) { led_off(); delay(500); led_on(); delay(500); } return 0; }

到这里直接操作寄存器的 c语言版本就好了 下面我们来写使用字符设备驱动来点灯 有了前面的基础 这里我们很简单,首先我们对驱动有个基本的认识话不多说上图

从图上可以看出来内核需要设备驱动给的封装的函数函数来操作硬件和获取硬件传输的数据,设备驱动模块具有规定的格式如下

#define struct 10; //linux 中cdev结构体描述了一个字符设备 #define xxx_dev_no 100 ; struct cdev{ struct kobject kobj; struct moudle *owner; struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; } //linux内核提供了用于操作cdev的 函数 //cdev_init() 用于初始化一个成员 并且建立cdev和file_operatons之间的联系 void cdev_init(struct cdev *cdev ,struct file_operations *fops) { memset(cdev, 0,sizeof *cdev); INTI_LIST_HEAD(&cdev->kobj,&ktype_cdev_default); cdev->ops=fops; } //cdev_alloc() 用于动态申请一个cdev内存 struct cdev *cdev_alloc(void) { struct cdev *p=kzalloc(sizeof(struct cdev),GFP_KERNEL); if(p){ INIT_LIST_HEAD(&p->list); kobject_init(&p->kobj,&type_cdev_dynamic); } return p; } struct file_operations { structmodule *owner; loff_t(*llseek)(struct file *, loff_t, int); ssize_t(*read) (struct file *, char __user *, size_t, loff_t *); ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t(*read_iter) (struct kiocb *, struct iov_iter *); ssize_t(*write_iter) (struct kiocb *, struct iov_iter *); int(*iterate) (struct file *, struct dir_context *); unsignedint (*poll) (struct file *, struct poll_table_struct *); long(*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long(*compat_ioctl) (struct file *, unsigned int, unsigned long); int(*mmap) (struct file *, struct vm_area_struct *); int(*mremap)(struct file *, struct vm_area_struct *); int(*open) (struct inode *, struct file *); int(*flush) (struct file *, fl_owner_t id); int(*release) (struct inode *, struct file *); int(*fsync) (struct file *, loff_t, loff_t, int datasync); int(*aio_fsync) (struct kiocb *, int datasync); int(*fasync) (int, struct file *, int); int(*lock) (struct file *, int, struct file_lock *); ssize_t(*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsignedlong (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsignedlong, unsigned long); int(*check_flags)(int); int(*flock) (struct file *, int, struct file_lock *); ssize_t(*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t,unsigned int); ssize_t(*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t,unsigned int); int(*setlease)(struct file *, long, struct file_lock **, void **); long(*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); void(*show_fdinfo)(struct seq_file *m, struct file *f); #ifndef CONFIG_MMU unsigned(*mmap_capabilities)(struct file *); #endif }; //***********************常见的字符设备驱动模板****************************// //设备结构体 struct xxx_dev_t{ struct cdev cdev; .... }xxx_dev; //字符设备驱动文件模板 struct file_oprations xxx_fops={ .owner } //设备驱动模板加载函数 static int __init xxx_init(void) { .. cdev_init(&xxx_dev.cdev,&xxx_fops); /*初始化cdev 就是建立file_operations 和cdev之间的联系*/ xxx_dev.cdev.owner=THIS_MOUDLE; //获取字符设备号 if(xxx_major){ register_chrdev_region(xxx_dev_no,1,DEV_NAME); }else { alloc_chardev_region(&xxx_dev_no,0,1,DEV_NAME); } ret=cdev_add(&xxx_dev.cdev,xxx_dev_no,1); } //设备驱动模板卸载函数 static void __exit xxx_exit(void) { unregister_chrdev_region(xxx_dev_no,1); cdev_del(&xxx_dev.cdev);

同时我把file-operations结构放了进去 其实我们的设备驱动主要的任务就是实现file-operations结构体 驱动实现基本版

#define LED_MAJOR 200 /* 主设备号 */ #define LED_NAME "led" /* 设备名字 */ #define LEDOFF 0 /* 关灯 */ #define LEDON 1 /* 开灯 */ /* 寄存器物理地址 */ #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; //led控制 void led_switch(u8 sta) { u32 val = 0; if(sta == LEDON) { val = readl(GPIO1_DR); val &= ~(1 << 3); writel(val, GPIO1_DR); }else if(sta == LEDOFF) { val = readl(GPIO1_DR); val|= (1 << 3); writel(val, GPIO1_DR); } } //暂时不用 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; } //写入数据 修改灯的状态 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; } //注销设备 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; /* 初始化LED */ /* 1、寄存器地址映射 */ 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); /* 2、使能GPIO1时钟 */ val = readl(IMX6U_CCM_CCGR1); val &= ~(3 << 26); /* 清楚以前的设置 */ val |= (3 << 26); /* 设置新值 */ writel(val, IMX6U_CCM_CCGR1); /* 3、设置GPIO1_IO03的复用功能,将其复用为 * GPIO1_IO03,最后设置IO属性。 */ writel(5, SW_MUX_GPIO1_IO03); /*寄存器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_GPIO1_IO03); /* 4、设置GPIO1_IO03为输出功能 */ val = readl(GPIO1_GDIR); val &= ~(1 << 3); /* 清除以前的设置 */ val |= (1 << 3); /* 设置为输出 */ writel(val, GPIO1_GDIR); /* 5、默认关闭LED */ val = readl(GPIO1_DR); val |= (1 << 3); writel(val, GPIO1_DR); /* 6、注册字符设备驱动 */ retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops); if(retvalue < 0){ printk("register chrdev failed!\r\n"); return -EIO; } return 0; } //注销函数 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); /* 注销字符设备驱动 */ unregister_chrdev(LED_MAJOR, LED_NAME); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zzzz");

没写完 等更新

最新回复(0)