当前位置: 首页 > news >正文

图片无版权网站上海网站建设科技公司

图片无版权网站,上海网站建设科技公司,办公家具网站模版,竞价单页模板Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 在人类的发展进化中#xff0c;时间是一个非常重要神秘的物质量。任何事物都是在时间的长河中流淌发生、发展、变化。我们进行驱动开发中对时间的定义和使用也是…Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长  在人类的发展进化中时间是一个非常重要神秘的物质量。任何事物都是在时间的长河中流淌发生、发展、变化。我们进行驱动开发中对时间的定义和使用也是必须要掌握的重要知识点。 本节笔记主要学习Linux内核定时器的驱动开发主要包括内核时间管理和定时器、硬件原理图分析【LED】、驱动开发和测试。最重要的内容为定时器驱动开发部分。 本笔记的脑图如下 一、Linux时间管理和内核定时器 1.内核时间管理 作为一个应用者不需要研究太深的具体实现。会用相应的API函数即可。 图形化配置界面可以设置系统节拍率。具体路径为 Kernel Features Timer frequency(choice[y]) 使用CONFIG_HZ来设置自己的系统时钟。 HZ表示一秒的节拍数也就是频率。 使用jiffies来记录系统从启动以来的系统节拍数。 jiffies/HZ就是系统运行时间单位为秒。 处理32位jiffies的绕回用到的几个函数。如下 函数        描述time_after(unknown,known)unkonwn超过known时为真time_before(unknown,known)unkonwn没有超过known时为真time_after_eq(unknown,known)unkonwn超过或等于known时为真time_before_eq(unknown,known)unkonwn没有超过或等于known时为真 2.内核定时器 需要周期性处理的工作都要用到定时器。Linux定时器采用系统时钟来实现只需要提供超时时间和定时处理函数即可。 内核定时器超时会自动关闭。 使用timer_list结构体表示内核定时器。该结构体的定义如下 struct timer_list { struct list_head entry; unsigned long expires; /* 定时器超时时间单位是节拍数 */ struct tvec_base *base; void (*function)(unsigned long); /* 定时处理函数 */ unsigned long data; /* 要传递给 function 函数的参数 */ int slack; }; 定义好定时器以后需要以下函数对其进行初始化 函数说明init_timer初始化add_timer注册del_timer删除del_timer_sync等待其他处理器使用完定时器后删除mod_timer修改定时值 内核定时器的使用流程如下 1 struct timer_list timer; /* 定义定时器 */ 2 3 /* 定时器回调函数 */ 4 void function(unsigned long arg) { 6 /* 7 * 定时器处理代码 8 */ 9 10 /* 如果需要定时器周期性运行的话就使用 mod_timer 11 * 函数重新设置超时值并且启动定时器。 12 */ 13 mod_timer(dev-timertest, jiffies msecs_to_jiffies(2000)); 14 } 15 16 /* 初始化函数 */ 17 void init(void) 18 { 19 init_timer(timer); /* 初始化定时器 */ 20 21 timer.function function; /* 设置定时处理函数 */ 22 timer.expiresjffies msecs_to_jiffies(2000);/* 超时时间 2 秒 */ 23 timer.data (unsigned long)dev; /* 将设备结构体作为参数 */ 24 25 add_timer(timer); /* 启动定时器 */ 26 } 27 28 /* 退出函数 */ 29 void exit(void) 30 { 31 del_timer(timer); /* 删除定时器 */ 32 /* 或者使用 */ 33 del_timer_sync(timer); 34 } 3.Linux内核短延时函数 函数 描述 void ndelay(unsigned long nsecs)      纳秒 void udelay(unsigned long usecs)      微秒 void mdelay(unsigned long mseces)  毫秒 二、硬件原理图分析 LED0 接到了 GPIO_3 上 GPIO_3 就是 GPIO1_IO03当 GPIO1_IO03输出低电平(0)的时候发光二极管 LED0 就会导通点亮当 GPIO1_IO03 输出高电平(1)的时候发光二极管 LED0 不会导通因此 LED0 也就不会点亮。所以 LED0 的亮灭取决于 GPIO1_IO03的输出电平输出 0 就亮输出 1 就灭。 三、驱动程序开发 1.修改设备树文件 使用已有的即可。 2.定时器驱动程序开发 38 struct timer_dev{ 39 dev_t devid; /* 设备号 */ 40 struct cdev cdev; /* cdev */ 41 struct class *class; /* 类 */ 42 struct device *device; /* 设备 */ 43 int major; /* 主设备号 */ 44 int minor; /* 次设备号 */ 45 struct device_node *nd; /* 设备节点 */ 46 int led_gpio; /* key 所使用的 GPIO 编号 */ 47 int timeperiod; /* 定时周期,单位为 ms */ 48 struct timer_list timer; /* 定义一个定时器 */ 49 spinlock_t lock; /* 定义自旋锁 */ 50 }; 定时器设备结构体在 48 行定义了一个定时器成员变量 timer。 60 static int led_init(void) 61 { 62 int ret 0; 63 64 timerdev.nd of_find_node_by_path(/gpioled); 65 if (timerdev.nd NULL) { 66 return -EINVAL; 67 } 68 69 timerdev.led_gpio of_get_named_gpio(timerdev.nd ,led-gpio, 0); 70 if (timerdev.led_gpio 0) { 71 printk(cant get led\r\n); 72 return -EINVAL; 73 } 74 75 /* 初始化 led 所使用的 IO */ 76 gpio_request(timerdev.led_gpio, led); /* 请求 IO */ 77 ret gpio_direction_output(timerdev.led_gpio, 1); 78 if(ret 0) { 79 printk(cant set gpio!\r\n); 80 } 81 return 0; 82 } LED 灯初始化函数从设备树中获取 LED 灯信息然后初始化相应的 IO。 91 static int timer_open(struct inode *inode, struct file *filp) 92 { 93 int ret 0; 94 filp-private_data timerdev; /* 设置私有数据 */ 95 96 timerdev.timeperiod 1000; /* 默认周期为 1s */ 97 ret led_init(); /* 初始化 LED IO */ 98 if (ret 0) { 99 return ret; 100 } 101 return 0; 102 } 函数 timer_open对应应用程序的 open 函数应用程序调用 open 函数打开/dev/timer 驱动文件的时候此函数就会执行。此函数设置文件私有数据为 timerdev并且初始化定时周期默认为 1 秒最后调用 led_init 函数初始化 LED 所使用的 IO。 111 static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 112 { 原子哥在线教学:www.yuanzige.com 论坛:www.openedv.com 1228 I.MX6U 嵌入式 Linux 驱动开发指南 113 struct timer_dev *dev (struct timer_dev *)filp-private_data; 114 int timerperiod; 115 unsigned long flags; 116 117 switch (cmd) { 118 case CLOSE_CMD: /* 关闭定时器 */ 119 del_timer_sync(dev-timer); 120 break; 121 case OPEN_CMD: /* 打开定时器 */ 122 spin_lock_irqsave(dev-lock, flags); 123 timerperiod dev-timeperiod; 124 spin_unlock_irqrestore(dev-lock, flags); 125 mod_timer(dev-timer, jiffies msecs_to_jiffies(timerperiod)); 126 break; 127 case SETPERIOD_CMD: /* 设置定时器周期 */ 128 spin_lock_irqsave(dev-lock, flags); 129 dev-timeperiod arg; 130 spin_unlock_irqrestore(dev-lock, flags); 131 mod_timer(dev-timer, jiffies msecs_to_jiffies(arg)); 132 break; 133 default: 134 break; 135 } 136 return 0; 137 } 函数 timer_unlocked_ioctl对应应用程序的 ioctl 函数应用程序调用 ioctl函数向驱动发送控制信息此函数响应并执行。此函数有三个参数 filp cmd 和 arg其中 filp是对应的设备文件 cmd 是应用程序发送过来的命令信息 arg 是应用程序发送过来的参数在本章例程中 arg 参数表示定时周期。 CLOSE_CMD 关闭定时器命令 调用 del_timer_sync 函数关闭定时器。OPEN_CMD打开定时器命令调用 mod_timer 函数打开定时器定时周期为 timerdev 的 timeperiod 成员变量定时周期默认是 1 秒。SETPERIOD_CMD设置定时器周期命令参数 arg 就是新的定时周期设置 timerdev 的 timeperiod 成员变量为 arg 所表示定时周期指。并且使用 mod_timer 重新打开定时器使定时器 以新的周期运行。   139 /* 设备操作函数 */ 140 static struct file_operations timer_fops { 141 .owner THIS_MODULE, 142 .open timer_open, 143 .unlocked_ioctl timer_unlocked_ioctl, 144 }; 定时器驱动操作函数集 timer_fops。   146 /* 定时器回调函数 */ 147 void timer_function(unsigned long arg) 148 { 149 struct timer_dev *dev (struct timer_dev *)arg; 150 static int sta 1; 151 int timerperiod; 152 unsigned long flags; 153 154 sta !sta; /* 每次都取反实现 LED 灯反转 */ 155 gpio_set_value(dev-led_gpio, sta); 156 157 /* 重启定时器 */ 158 spin_lock_irqsave(dev-lock, flags); 159 timerperiod dev-timeperiod; 160 spin_unlock_irqrestore(dev-lock, flags); 161 mod_timer(dev-timer, jiffies msecs_to_jiffies(dev-timeperiod)); 162 } 函数 timer_function定时器服务函数此函有一个参数 arg在本例程中arg 参数就是 timerdev 的地址这样通过 arg 参数就可以访问到设备结构体。当定时周期到了以后此函数就会被调用。在此函数中将 LED 灯的状态取反实现 LED 灯闪烁的效果。因为内核定时器不是循环的定时器执行一次以后就结束了因此在 161 行又调用了 mod_timer 函数重新开启定时器。 169 static int __init timer_init(void) 170 { 171 /* 初始化自旋锁 */ 172 spin_lock_init(timerdev.lock); 173 174 /* 注册字符设备驱动 */ 175 /* 1、创建设备号 */ 176 if (timerdev.major) { /* 定义了设备号 */ 177 timerdev.devid MKDEV(timerdev.major, 0); 178 register_chrdev_region(timerdev.devid, TIMER_CNT, TIMER_NAME); 179 } else { /* 没有定义设备号 */ 180 alloc_chrdev_region(timerdev.devid, 0, TIMER_CNT, TIMER_NAME); 181 timerdev.major MAJOR(timerdev.devid); /* 获取主设备号 */ 182 timerdev.minor MINOR(timerdev.devid); /* 获取次设备号 */ 183 } 184 185 /* 2、初始化 cdev */ 186 timerdev.cdev.owner THIS_MODULE; 187 cdev_init(timerdev.cdev, timer_fops); 188 189 /* 3、添加一个 cdev */ 190 cdev_add(timerdev.cdev, timerdev.devid, TIMER_CNT); 191 192 /* 4、创建类 */ 193 timerdev.class class_create(THIS_MODULE, TIMER_NAME); 194 if (IS_ERR(timerdev.class)) { 195 return PTR_ERR(timerdev.class); 196 } 197 198 /* 5、创建设备 */ 199 timerdev.device device_create(timerdev.class, NULL, timerdev.devid, NULL, TIMER_NAME); 200 if (IS_ERR(timerdev.device)) { 201 return PTR_ERR(timerdev.device); 202 } 203 204 /* 6、初始化 timer设置定时器处理函数,还未设置周期所有不会激活定时器 */ 205 init_timer(timerdev.timer); 206 timerdev.timer.function timer_function; 207 timerdev.timer.data (unsigned long)timerdev; 208 return 0; 209 } 第 169~209 行函数 timer_init驱动入口函数。在第 205~207 行初始化定时器设置定时器的定时处理函数为 timer_function另外设置要传递给 timer_function 函数的参数为 timerdev的地址。在此函数中并没有调用 timer_add 函数来开启定时器因此定时器默认是关闭的除非应用程序发送打开命令。 216 static void __exit timer_exit(void) 217 { 218 219 gpio_set_value(timerdev.led_gpio, 1); /* 卸载驱动的时候关闭 LED */ 220 del_timer_sync(timerdev.timer); /* 删除 timer */ 221 #if 0 222 del_timer(timerdev.tiemr); 223 #endif 224 225 /* 注销字符设备驱动 */ 226 cdev_del(timerdev.cdev); /* 删除 cdev */ 227 unregister_chrdev_region(timerdev.devid, TIMER_CNT); 228 229 device_destroy(timerdev.class, timerdev.devid); 230 class_destroy(timerdev.class); 231 } 驱动出口函数在 219 行关闭 LED也就是卸载驱动以后 LED 处于熄灭状态。第 220 行调用 del_timer_sync 函数删除定时器也可以使用 del_timer 函数。   3.编写测试APP 21 /* 命令值 */ 22 #define CLOSE_CMD (_IO(0XEF, 0x1)) /* 关闭定时器 */ 23 #define OPEN_CMD (_IO(0XEF, 0x2)) /* 打开定时器 */ 24 #define SETPERIOD_CMD (_IO(0XEF, 0x3)) /* 设置定时器周期命令 */ 25 26 /* 27 * description : main 主程序 28 * param - argc : argv 数组元素个数 29 * param - argv : 具体参数 30 * return : 0 成功;其他 失败 31 */ 32 int main(int argc, char *argv[]) 33 { 34 int fd, ret; 35 char *filename; 36 unsigned int cmd; 37 unsigned int arg; 38 unsigned char str[100]; 39 40 if (argc ! 2) { 41 printf(Error Usage!\r\n); 42 return -1; 43 } 44 45 filename argv[1]; 46 47 fd open(filename, O_RDWR); 48 if (fd 0) { 49 printf(Cant open file %s\r\n, filename); 50 return -1; 51 } 52 53 while (1) { 54 printf(Input CMD:); 55 ret scanf(%d, cmd); 56 if (ret ! 1) { /* 参数输入错误 */ 57 gets(str); /* 防止卡死 */ 58 } 59 60 if(cmd 1) /* 关闭 LED 灯 */ 61 cmd CLOSE_CMD; 62 else if(cmd 2) /* 打开 LED 灯 */ 63 cmd OPEN_CMD; 64 else if(cmd 3) { 65 cmd SETPERIOD_CMD; /* 设置周期值 */ 66 printf(Input Timer Period:); 67 ret scanf(%d, arg); 68 if (ret ! 1) { /* 参数输入错误 */ 69 gets(str); /* 防止卡死 */ 70 } 71 } 72 ioctl(fd, cmd, arg); /* 控制定时器的打开和关闭 */ 73 } 74 close(fd); 75 } ①、运行 APP 以后提示我们输入要测试的命令输入 1 表示关闭定时器、输入 2 表示打开定时器输入 3 设置定时器周期。 ②、如果要设置定时器周期的话需要让用户输入要设置的周期值单位为毫秒。 第 22~24 行命令值。 第 53~73 行 while(1)循环让用户输入要测试的命令然后通过第 72 行的 ioctl 函数发送给驱动程序。如果是设置定时器周期命令 SETPERIOD_CMD那么 ioctl 函数的 arg 参数就是用户输入的周期值。 四、运行测试 1.编译驱动程序和测试AP 编译成功以后就会生成一个名为“ timer.ko”的驱动模块文件。 编译成功以后就会生成 timerApp 这个应用程序。 2.运行测试 根据提示输入指令及周期观察LED的交替亮变化即可。 五、总结 本节笔记主要学习Linux内核定时器的驱动开发主要包括内核时间管理和定时器、硬件原理图分析【LED】、驱动开发和测试。最重要的内容为定时器驱动开发部分。 本文为参考正点原子开发板配套教程整理而得仅用于学习交流使用不得用于商业用途。
http://www.hkea.cn/news/14553338/

相关文章:

  • 3733手游网站在哪里做的百度热词搜索指数
  • 福州网站建设要找嘉艺网络高校建设网站的特色
  • 网站建设制作费用预算表怎么搞免费的网站
  • 河南做网站公司汉狮洛阳网站设计公司
  • 东营智能网站设计seo北京公司
  • 菏泽做网站建设找哪家建设专业网站网络
  • 金泉网做网站找谁sae安装WordPress4.4
  • 小企业怎么做网站公司网站建设应注意事项
  • 系统花钱做任务的小说魅网站外贸网站建设 东莞
  • 用flash做网站超链接百度用户服务中心
  • 如何创办视频网站网站获得流量最好的方法是什么 ( )
  • 娱乐新闻做的好的网站优化课程
  • 自己建网站做推广中山祥云做的网站怎么样百度百科
  • html电影网站模板网站的ftp管理权限是什么意思
  • 商务网站建设毕业设计家教
  • 西安网站推广排名国际采购平台有哪些
  • 怎么做网站最便宜山东恒正建设有限公司 网站
  • 做境外碎片化旅游的网站网站的内链怎么做
  • 上传文件网站广告公司网络推广计划
  • 哪个网站做二微码做外贸的有哪些网站有哪些
  • 怎样才能把网站宣传做的更好什么是百度竞价
  • 公司网站开发的流程网站开发代码用什么软件
  • 安庆网站建设专业自己做的网站怎么绑定域名
  • 黑龙江两学一做网站深圳it外包公司
  • 模板网站首页设计做美工好的网站
  • 手机建设网站赚钱深圳网站制作服
  • 精品课程网站设计与实现开题报告教育网站官网入口
  • 馆陶专业做网站上海网站排名seo公司哪家好
  • 广元如何做百度的网站广州越秀最新通知
  • 湖南商城网站建设网站开发工具 比较