关于友善之臂出的这款contexA9开发板,目前在网络上的资源较少,特别是内核的,非常之少,鉴于这种情况,我将会写一个系列的驱动来做关于tiny4412这款板子开发的总结。file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps581D.tmp.png 简单介绍一下: Tiny4412是一款高性能的四核Cortex-A9核心板,由广州友善之臂设计、生产和发行销售。它采用三星Exynos4412作为主处理器,运行主频可高达1.5GHz,Exynos4412内部集成了Mali-400 MP高性能图形引擎,支持3D图形流畅运行,并可播放1080P大尺寸高清视频。三星旗舰智能手机Galaxy S3即是采用此CPU设计。 我用的是普通版.也就是只有一个串口的.但是核心板是一样的。 file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps581E.tmp.jpg 好了,介绍完毕,前面的**我们已经说过了如何编写一个字符设备的驱动程序,这里就不再继续扯字符驱动怎么写,非常简单了,看看就懂了。 我们进入整题,今天,我们需要实现一个LED的驱动程序。在友善之臂的核心板上,有4颗LED灯,如何编写一个驱动程序,让它亮起来,首先我们来看看核心板: file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps583F.tmp.jpg LED灯就位于右上角,第一个和第二个都是电源指示灯,我们不需要管它,我们只管后面那4个LED灯。 如何编写? 1、首先找到板子的原理图,找到对应的引脚。 2、接着打开数据手册,找到对应的寄存器。 3、开始编写LED驱动程序 4、编写makefile 5、插入模块insmod xxx.ko 6、查询主设备号 cat /proc/devices 7、创建设备节点 mknod /dev/xxx c x x 8、执行应用程序app 对应的原理图: file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps5840.tmp.jpg file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps5841.tmp.jpg 从这里我们可以得出一个结论,LED灯是低电平点亮的,也就是往对应的端口里写0,LED灯就亮了。从最下面一幅图可以知道,我们要找的寄存器是GPIO的GPM4开头的这个寄存器,现在我们进入查数据手册的阶段. 查手册: 我们找到手册的第288页GPIO章节的GPMCON这里: file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps5842.tmp.jpg file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps5852.tmp.pngfile:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps5853.tmp.jpg 这是我们要配置端口的模式的IO口,端口有以上的一些状态,在这里我们只考虑输出,也就是只要配置Output那一项就可以了。 我们要配的寄存器有GPM4CON[0],GPM4CON[1],GPM4CON[2],GPM4CON[3],这四位,分别配置成output输出模式. 接下来再看一个GPM4DAT,这个是端口的状态寄存器,对状态寄存器就是写0或者写1,那么LED就被驱动了,我们来看看: file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps5854.tmp.jpg 好了,寄存器我们已经找到了,接下来,可以进入写代码的阶段了: 首先编写LED驱动程序: [cpp] view plain copy print?file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps5855.tmp.png 1. #include <linux/init.h> 2. 3. #include <linux/module.h> 4. 5. #include <linux/kernel.h> 6. 7. #include <linux/fs.h> 8. 9. #include <linux/io.h> 10. 11. #include <asm/uaccess.h> 12. 13. #include <asm/irq.h> 14. 15. #include <asm/io.h> 16. 17. //这个是设备的名称,也就是对应在/dev/test-dev 18. 19. #define DEV_NAME "test-dev" 20. 21. //LED灯IO口的地址,也就是刚刚我们在上面的芯片手册看到的Address 22. 23. #define GPM4COM 0x110002E0 24. 25. //定义配置模式的指针变量 26. 27. volatile unsigned long *led_config = NULL ; 28. 29. //定义配置状态的指针变量 30. 31. volatile unsigned long *led_dat = NULL ; 32. 33. //open方法,对LED灯进行初始化 34. 35. int led_open(struct inode *inode, struct file *filp) 36. 37. { 38. 39. printk("led_open\n");//上层程序对LED进行Open操作的时候会执行这个函数 40. 41. //先对LED的端口进行清0操作 42. 43. *led_config &= ~(0xffff); 44. 45. //将4个IO口16位都设置为Output输出状态 46. 47. *led_config |= (0x1111); 48. 49. return 0; 50. 51. } 52. 53. //write方法 54. 55. int led_write(struct file *filp , const char __user *buf , size_t count , loff_t *f_pos) 56. 57. { 58. 59. int val ; 60. 61. //注意,这里是在内核中进行操作,我们需要使用copy_from_user这个函数将用户态的内容拷贝到内核态 62. 63. copy_from_user(&val , buf , count); 64. 65. //以下就是当val是哪个值的时候,led就执行相应的操作,这里不多说 66. 67. switch(val) 68. 69. { 70. 71. case 0 : 72. 73. //对状态寄存器进行赋值,以下雷同 74. 75. printk(KERN_EMERG"led1_on\n"); 76. 77. *led_dat &= ~0x1 ; 78. 79. break ; 80. 81. case 1 : 82. 83. printk(KERN_EMERG"led2_on\n"); 84. 85. *led_dat &= ~0x2 ; 86. 87. break ; 88. 89. case 2 : 90. 91. printk(KERN_EMERG"led3_on\n"); 92. 93. *led_dat &= ~0x4 ; 94. 95. break ; 96. 97. case 3 : 98. 99. printk(KERN_EMERG"led4_on\n"); 100. 101. *led_dat &= ~0x8 ; 102. 103. break ; 104. 105. case 4 : 106. 107. printk(KERN_EMERG"ledall_on\n"); 108. 109. *led_dat &= ~0xf ; 110. 111. break ; 112. 113. case 5 : 114. 115. printk(KERN_EMERG"ledall_off\n"); 116. 117. *led_dat |= 0xf ; 118. 119. break ; 120. 121. 122. 123. } 124. 125. } 126. 127. //close方法 128. 129. int led_close(struct inode *inode, struct file *filp) 130. 131. { 132. 133. printk("led_close\n"); 134. 135. *led_dat |= 0xf ; //全灭,因为高电平是灭的,0xf ----> 1111 136. 137. return 0; 138. 139. } 140. 141. //用ioctl这个方法也可以实现LED的操作的,自己去实现吧 142. 143. #if 0 144. 145. long led_ioctl(struct file *filp, unsigned int request, unsigned long arg) 146. 147. { 148. 149. switch(request) 150. 151. { 152. 153. case 0: 154. 155. printk(KERN_EMERG"led1 on\n"); 156. 157. *led_dat &=~0x1 ; 158. 159. break; 160. 161. 162. 163. case 1: 164. 165. printk(KERN_EMERG"led2 on\n"); 166. 167. *led_dat &=~0x2 ; 168. 169. break; 170. 171. 172. 173. case 3: 174. 175. printk(KERN_EMERG"led3 on\n"); 176. 177. *led_dat &=~0xf ; 178. 179. break; 180. 181. 182. 183. case 4: 184. 185. printk(KERN_EMERG"led4 on\n"); 186. 187. *led_dat &=~0x8 ; 188. 189. break ; 190. 191. default : 192. 193. *led_dat |= 0xf ; 194. 195. } 196. 197. } 198. 199. #endif 200. 201. //对方法进行初始化 202. 203. struct file_operations fops = { 204. 205. .owner = THIS_MODULE , 206. 207. .open = led_open, 208. 209. .release = led_close, 210. 211. // .unlocked_ioctl = led_ioctl, 212. 213. .write = led_write, 214. 215. }; 216. 217. //主设备号 218. 219. int major ; 220. 221. //启动函数 222. 223. static __init int test_init(void) 224. 225. { 226. 227. printk("led_init\n"); 228. 229. major = register_chrdev(major, DEV_NAME, &fops); 230. 231. led_config = (volatile unsigned long *)ioremap(GPM4COM , 16); 232. 233. led_dat = led_config + 1 ; 234. 235. return 0; 236. 237. } 238. 239. //注销函数 240. 241. static __exit void test_exit(void) 242. 243. { 244. 245. printk("led_exit\n"); 246. 247. unregister_chrdev(major, DEV_NAME); 248. 249. iounmap(led_config); 250. 251. } 252. 253. 254. 255. module_init(test_init); 256. 257. module_exit(test_exit); 258. 259. 260. 261. MODULE_LICENSE("GPL"); 262. 263. MODULE_AUTHOR("Y.X.YANG"); 264. 265. MODULE_VERSION("2016.1.15");</span> 266. 以上就是led这个设备驱动的编写框架。看不懂的可以去学学linux内核设备驱动再来看就很简单了。其实跟单片机的编程差不了多少的,只不过内核驱动是按照框架来编写的,有所驱动就在这里。file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps5866.tmp.png 驱动程序编写完了,接下来我们编写上层应用层的程序: [cpp] view plain copy print?file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps5867.tmp.png 1. #include <stdio.h> 2. 3. #include <sys/types.h> 4. 5. #include <sys/stat.h> 6. 7. #include <fcntl.h> 8. 9. 10. 11. int main(int argc, char **argv) 12. 13. { 14. 15. int fd; 16. 17. int val = 0 ; 18. 19. //打开对应的设备 20. 21. fd = open("/dev/test-dev",O_RDWR) ; 22. 23. if(-1 == fd) 24. 25. { 26. 27. printf("open fair!\n"); 28. 29. return -1 ; 30. 31. } 32. 33. while(1){ 34. 35. val = 0 ; 36. 37. //写write方法就会调用到驱动程序的led_write 38. 39. //最后我们能看到的结果是led灯做流水灯的实现,然后全灭,再周而复始 40. 41. write(fd , &val , 4); 42. 43. sleep(1); 44. 45. val = 1 ; 46. 47. write(fd , &val , 4); 48. 49. sleep(1); 50. 51. val = 2 ; 52. 53. write(fd , &val , 4); 54. 55. sleep(1); 56. 57. val = 3 ; 58. 59. write(fd , &val , 4); 60. 61. sleep(1); 62. 63. val = 5 ; 64. 65. write(fd , &val , 4); 66. 67. sleep(1); 68. 69. } 70. 71. return 0; 72. 73. }</span> 74. 好了,程序已经写完了,我们来看看makefile怎么写.file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps5877.tmp.png [cpp] view plain copy print?file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps5878.tmp.png 1. #将你所写的驱动程序编译成模块形式 2. 3. obj-m += leds.o 4. 5. #你需要的文件系统 6. 7. ROOTFS = /disk/A9/filesystem 8. 9. #你需要的内核 10. 11. KERNEL = /disk/A9/linux-3.5/ 12. 13. #模块编译 14. 15. all: 16. 17. make -C $(KERNEL) M=`pwd` modules 18. 19. #模块清除 20. 21. clean: 22. 23. make -C $(KERNEL) M=`pwd` clean 24. 25. rm -rf my_led 26. 27. #模块更新 28. 29. install: 30. 31. make -C $(KERNEL) M=`pwd` modules_install INSTALL_MOD_PATH=$(ROOTFS) 32. 33. #编译上层app应用程序 34. 35. my_led: 36. 37. arm-linux-gcc my_led.c -o my_led</span> 38. 好了,所有的一切都编写完成,我们来看看接下来的操作: file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps5879.tmp.png 2、编译app file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps587A.tmp.png 3、启动minicom,打开开发板的电源,开发板bootload开始启动 file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps588B.tmp.png 4、开发板内核启动 file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps588C.tmp.png 5、进入文件系统,执行insmod插入模块和显示插入后的模块的操作 file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps588D.tmp.png 6、看看主设备号 file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps588E.tmp.png 从这里可以看到,我们的设备test-dev的主设备号是250 所以我们创建设备节点 : mknod /dev/test-dev c 250 0 ,创建完成后 7、执行app应用程序 file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps589F.tmp.png 在这里,我们可以看到程序开始跑起来了,我们来看看开发板上的led是怎么变化的: file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps58AF.tmp.png file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps58C0.tmp.png
|