当前位置:首页 > 公众号精选 > 嵌入式云IOT技术圈
[导读]关于友善之臂出的这款contexA9开发板,目前在网络上的资源较少,特别是内核的,非常之少,鉴于这种情况,我将会写一个系列的驱动来做关于tiny4412这款板子开发的总结。      简单介绍一下: Tiny4412是一款高性能的四核Cortex-A9核心板,由广州友善之臂设计、

关于友善之臂出的这款contexA9开发板,目前在网络上的资源较少,特别是内核的,非常之少,鉴于这种情况,我将会写一个系列的驱动来做关于tiny4412这款板子开发的总结。

     简单介绍一下:

Tiny4412是一款高性能的四核Cortex-A9核心板,由广州友善之臂设计、生产和发行销售。它采用三星Exynos4412作为主处理器,运行主频可高达1.5GHz,Exynos4412内部集成了Mali-400 MP高性能图形引擎,支持3D图形流畅运行,并可播放1080P大尺寸高清视频。三星旗舰智能手机Galaxy S3即是采用此CPU设计。

我用的是普通版.也就是只有一个串口的.但是核心板是一样的。

好了,介绍完毕,前面的文章我们已经说过了如何编写一个字符设备的驱动程序,这里就不再继续扯字符驱动怎么写,非常简单了,看看就懂了。

我们进入整题,今天,我们需要实现一个LED的驱动程序。在友善之臂的核心板上,有4颗LED灯,如何编写一个驱动程序,让它亮起来,首先我们来看看核心板:

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

对应的原理图:



从这里我们可以得出一个结论,LED灯是低电平点亮的,也就是往对应的端口里写0,LED灯就亮了。从最下面一幅图可以知道,我们要找的寄存器是GPIO的GPM4开头的这个寄存器,现在我们进入查数据手册的阶段.

查手册:

我们找到手册的第288页GPIO章节的GPMCON这里:




这是我们要配置端口的模式的IO口,端口有以上的一些状态,在这里我们只考虑输出,也就是只要配置Output那一项就可以了。

我们要配的寄存器有GPM4CON[0],GPM4CON[1],GPM4CON[2],GPM4CON[3],这四位,分别配置成output输出模式.

接下来再看一个GPM4DAT,这个是端口的状态寄存器,对状态寄存器就是写0或者写1,那么LED就被驱动了,我们来看看:


好了,寄存器我们已经找到了,接下来,可以进入写代码的阶段了:

首先编写LED驱动程序:


[cpp] view plain copy print?

  1. #include <linux/init.h>  

  2. #include <linux/module.h>  

  3. #include <linux/kernel.h>  

  4. #include <linux/fs.h>  

  5. #include <linux/io.h>  

  6. #include <asm/uaccess.h>  

  7. #include <asm/irq.h>  

  8. #include <asm/io.h>  

  9. //这个是设备的名称,也就是对应在/dev/test-dev  

  10. #define DEV_NAME    "test-dev"  

  11. //LED灯IO口的地址,也就是刚刚我们在上面的芯片手册看到的Address  

  12. #define GPM4COM     0x110002E0  

  13. //定义配置模式的指针变量  

  14. volatile unsigned long *led_config = NULL ;   

  15. //定义配置状态的指针变量  

  16. volatile unsigned long *led_dat = NULL ;   

  17. //open方法,对LED灯进行初始化  

  18. int led_open(struct inode *inode, struct file *filp)  

  19. {  

  20.     printk("led_open\n");//上层程序对LED进行Open操作的时候会执行这个函数  

  21.     //先对LED的端口进行清0操作  

  22.     *led_config &= ~(0xffff);  

  23.     //将4个IO口16位都设置为Output输出状态  

  24.     *led_config |= (0x1111);  

  25.     return 0;  

  26. }  

  27. //write方法  

  28. int led_write(struct file *filp , const char __user *buf , size_t count , loff_t *f_pos)  

  29. {  

  30.     int val ;   

  31.     //注意,这里是在内核中进行操作,我们需要使用copy_from_user这个函数将用户态的内容拷贝到内核态  

  32.     copy_from_user(&val , buf , count);   

  33.     //以下就是当val是哪个值的时候,led就执行相应的操作,这里不多说  

  34.     switch(val)  

  35.     {  

  36.         case 0 :   

  37.                 //对状态寄存器进行赋值,以下雷同  

  38.                 printk(KERN_EMERG"led1_on\n");  

  39.                 *led_dat &= ~0x1 ;  

  40.                 break ;  

  41.         case 1 :  

  42.                 printk(KERN_EMERG"led2_on\n");  

  43.                 *led_dat &= ~0x2 ;  

  44.                 break ;  

  45.         case 2 :  

  46.                 printk(KERN_EMERG"led3_on\n");  

  47.                 *led_dat &= ~0x4 ;  

  48.                 break ;  

  49.         case 3 :  

  50.                 printk(KERN_EMERG"led4_on\n");  

  51.                 *led_dat &= ~0x8 ;   

  52.                 break ;  

  53.         case 4 :  

  54.                 printk(KERN_EMERG"ledall_on\n");  

  55.                 *led_dat &= ~0xf ;  

  56.                 break ;  

  57.         case 5 :   

  58.                 printk(KERN_EMERG"ledall_off\n");  

  59.                 *led_dat |= 0xf ;  

  60.                 break ;  

  61.   

  62.     }  

  63. }  

  64. //close方法  

  65. int led_close(struct inode *inode, struct file *filp)  

  66. {  

  67.     printk("led_close\n");  

  68.     *led_dat |= 0xf ;  //全灭,因为高电平是灭的,0xf ----> 1111  

  69.     return 0;  

  70. }  

  71. //用ioctl这个方法也可以实现LED的操作的,自己去实现吧  

  72. #if 0  

  73. long led_ioctl(struct file *filp, unsigned int request, unsigned long arg)  

  74. {  

  75.     switch(request)  

  76.     {  

  77.         case 0:  

  78.             printk(KERN_EMERG"led1 on\n");  

  79.             *led_dat &=~0x1 ;  

  80.             break;  

  81.   

  82.         case 1:  

  83.             printk(KERN_EMERG"led2 on\n");  

  84.             *led_dat &=~0x2 ;  

  85.             break;  

  86.   

  87.         case 3:  

  88.             printk(KERN_EMERG"led3 on\n");  

  89.             *led_dat &=~0xf ;  

  90.             break;  

  91.   

  92.         case 4:  

  93.             printk(KERN_EMERG"led4 on\n");  

  94.             *led_dat &=~0x8 ;  

  95.             break ;  

  96.         default :   

  97.             *led_dat |= 0xf ;  

  98.     }     

  99. }  

  100. #endif  

  101. //对方法进行初始化  

  102. struct file_operations fops = {  

  103.     .owner = THIS_MODULE ,  

  104.     .open = led_open,  

  105.     .release = led_close,  

  106. //  .unlocked_ioctl = led_ioctl,  

  107.     .write = led_write,  

  108. };  

  109. //主设备号  

  110. int major ;  

  111. //启动函数  

  112. static __init int test_init(void)  

  113. {  

  114.     printk("led_init\n");  

  115.     major = register_chrdev(major, DEV_NAME, &fops);  

  116.     led_config = (volatile unsigned long *)ioremap(GPM4COM , 16);  

  117.     led_dat = led_config + 1 ;    

  118.     return 0;  

  119. }  

  120. //注销函数  

  121. static __exit void test_exit(void)  

  122. {  

  123.     printk("led_exit\n");  

  124.     unregister_chrdev(major, DEV_NAME);  

  125.     iounmap(led_config);  

  126. }  

  127.   

  128. module_init(test_init);  

  129. module_exit(test_exit);  

  130.   

  131. MODULE_LICENSE("GPL");  

  132. MODULE_AUTHOR("Y.X.YANG");  

  133. MODULE_VERSION("2016.1.15");</span>  




以上就是led这个设备驱动的编写框架。看不懂的可以去学学linux内核设备驱动再来看就很简单了。其实跟单片机的编程差不了多少的,只不过内核驱动是按照框架来编写的,有所驱动就在这里。


驱动程序编写完了,接下来我们编写上层应用层的程序:


[cpp] view plain copy print?

  1. #include <stdio.h>  

  2. #include <sys/types.h>  

  3. #include <sys/stat.h>  

  4. #include <fcntl.h>  

  5.   

  6. int main(int argc, char **argv)  

  7. {  

  8.     int fd;  

  9.     int val = 0 ;  

  10.     //打开对应的设备  

  11.     fd = open("/dev/test-dev",O_RDWR) ;  

  12.     if(-1 == fd)  

  13.     {  

  14.         printf("open fair!\n");  

  15.         return -1 ;  

  16.     }  

  17.     while(1){  

  18.         val = 0 ;  

  19.         //写write方法就会调用到驱动程序的led_write  

  20.         //最后我们能看到的结果是led灯做流水灯的实现,然后全灭,再周而复始  

  21.         write(fd , &val , 4);  

  22.         sleep(1);  

  23.         val = 1 ;  

  24.         write(fd , &val , 4);  

  25.         sleep(1);  

  26.         val = 2 ;  

  27.         write(fd , &val , 4);  

  28.         sleep(1);  

  29.         val = 3 ;  

  30.         write(fd , &val , 4);  

  31.         sleep(1);  

  32.         val = 5 ;  

  33.         write(fd , &val , 4);  

  34.         sleep(1);  

  35.     }  

  36.     return 0;  

  37. }</span>  




好了,程序已经写完了,我们来看看makefile怎么写.


[cpp] view plain copy print?

  1. #将你所写的驱动程序编译成模块形式  

  2. obj-m   += leds.o  

  3. #你需要的文件系统  

  4. ROOTFS = /disk/A9/filesystem  

  5. #你需要的内核  

  6. KERNEL = /disk/A9/linux-3.5/  

  7. #模块编译  

  8. all:  

  9.     make -C $(KERNEL) M=`pwd` modules  

  10. #模块清除  

  11. clean:  

  12.     make -C $(KERNEL) M=`pwd` clean  

  13.     rm -rf my_led  

  14. #模块更新  

  15. install:  

  16.     make -C $(KERNEL) M=`pwd` modules_install INSTALL_MOD_PATH=$(ROOTFS)  

  17. #编译上层app应用程序  

  18. my_led:  

  19.     arm-linux-gcc my_led.c -o my_led</span>  


好了,所有的一切都编写完成,我们来看看接下来的操作:


1、先编译整个工程:执行make命令

2、编译app


3、启动minicom,打开开发板的电源,开发板bootload开始启动

4、开发板内核启动


5、进入文件系统,执行insmod插入模块和显示插入后的模块的操作


6、看看主设备号


从这里可以看到,我们的设备test-dev的主设备号是250

所以我们创建设备节点 : mknod /dev/test-dev c 250 0 ,创建完成后

7、执行app应用程序


在这里,我们可以看到程序开始跑起来了,我们来看看开发板上的led是怎么变化的:






好,写了好多,也整理了不少东西出来了,有点累了,以后还要慢慢写.....今天就到这里...


免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
关闭
关闭