TA的每日心情 | 开心 2016-10-12 17:42 |
---|
签到天数: 1 天 连续签到: 1 天 [LV.1]初来乍到
|
0 前言
上一期,我们讲的是搭建开发环境及程序演示。讲真,上次真的花了我不少功夫,主要是对虚拟机的使用比较陌生,关于文件共享、网络配置方面比较繁琐,一直在学着怎么使用。好在基本上搞定了,其实还有个问题:win10系统下,Virtual Box(版本4.1.18)中的虚拟机网络桥接宿主机一直没有搞定,这就意味着虚拟机无法与宿主机、开发板同处一个局域网中,传输文件会比较麻烦,如果大家有好的解决方案,请告诉我。现在这一期,开始在上面编程了。我的想法是,利用已经写好的硬件驱动,写几个应用程序。比如基本的按键key、LED灯、串口通信方面的。好了,现在就开始正式的征程吧。
写应用程序,在我看来,应该是调用Linux系统提供的应用程序接口(API),通过内核,来控制硬件。那么就需要找到这些API,那这些API到底在哪儿呢?比如说LED灯的操作,如何完成初始化,然后指定某个引脚为输出,再指定输出高或低电平,这些应该都是API中给定的。后来事实证明,我想的有点简单,因为Linux文件系统很复杂,而我又是新手,好多文件我都是第一次接触的。
在手册中,并没有指出这些接口所在的文件,可能对于老手来说,这些都不是事儿。然后对我们这种刚入门的菜鸟来说,就是很大的困难。这里先推荐一下大家上米尔官网上的论坛:https://www.myir-tech.com/bbs/forum.php,AM437X系列芯片比较新,资料比较少,大家可以参照AM335X系列的。里面有一篇是介绍Linux设备驱动源码文件列表的。
大家如果有兴趣可以打开去看看哦。其实Linux系统已经把很多接口通用化了。从后面的内容就可以明显感觉到,操作GPIO就像操作文件一样,有open, read, write, close等。这里放一张图,说的是用户程序、操作系统、驱动和硬件之间的关系。
1 LED灯玩法
上面说了这么多,是不是有点晕乎乎的。好的,下面开始结合LED灯讲讲硬件接口编程。开发板上有4个LED灯,写着D23~D25,打开开发板原理图,找到D23~D25;
再找到对应的芯片引脚,
对应的是GPIO1_24~GPIO1_27,这里说一下GPIO的编号方式。举个例子,GPIO1_24意味着是1*32+24=56,对应的GPIO56。
怎么来控制它们呢?一种方式是通过虚拟文件系统sysfs:
cd /sys/class/leds/status_led1/
然后你会发现有一些基本操作:
比如说把非零值传给brightness文件(在Linux中,设备皆文件,LED灯也是这样啊。),
echo 1 > brightness
就可以点亮小灯;
把零值传给brightness文件,
echo 0 > brightness
就可以关闭小灯;
把heartbeat传给trigger,
echo heartbeat > trigger
就可以让小灯闪烁(即心跳灯)。
比如说系统运行时D23小灯一直在闪烁,就是以下这句话的效果:
echo heartbeat > /sys/class/leds/status_led0/trigger
是不是很神奇。
2 在C程序中操作LED
上面的一种方法,讲的是在应用层通过shell方式控制LED,下面介绍的这种,是通过C语言来实现的。下面贴上我写的代码。思路:在C程序中操作LED,首先要设置trigger属性为nono,将指示灯设置为用户控制。然后操作brightness属性0和1每隔1s交替变换,设置LED点亮或熄灭。
#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <string.h>#define TRIGGER "trigger"#define LED_PATH "/sys/class/leds/"#define LED_STATUS "brightness"#define TRIGGER_NONE "none"int main(int argc, char **argv){ char path[40], data[2]; int fd, ret, flag = 0; if (argv[1] == NULL) { printf("Usage: ./led status_led3"); return 0; } strcpy(path, LED_PATH); strcat(path, argv[1]); strcat(path, "/" TRIGGER); fd = open(path, O_RDWR); if (fd < 0) { perror("open"); return -1; } ret = write(fd, TRIGGER_NONE, strlen(TRIGGER_NONE)); if (ret < 0) { perror("write"); return -1; } close(fd); stpcpy(path, LED_PATH); strcat(path, argv[1]); strcat(path,"/" LED_STATUS); fd = open(path, O_WRONLY); if (fd < 0) { perror("open"); return -1; } for (;;) { data[0] = flag?'0':'1'; ret = write(fd, data, 1); if (ret < 0) { perror("write"); return -1; } flag =!flag; sleep(1); } return 0;} 写完代码后,修改下Makefile文件里的变量,然后编译,没有错误后,将执行文件拷贝到目标板中,增加权限后,输入以下指令执行:
$./led status_led3
3 GPIO玩法
介绍完LED灯的两种玩法,下面讲一下GPIO的。
科普一下,Linux下用文件IO的方式操作GPIO(/sys/class/gpio),通过sysfs方式控制GPIO,先访问/sys/class/gpio目录,向export文件写入GPIO编号,使得该GPIO的操作接口从内核空间暴露到用户空间,GPIO的操作接口包括direction和value等,direction控制GPIO方向,而value可控制GPIO输出或获得GPIO输入。文件IO方式操作GPIO,使用到了4个函数open、close、read、write。
ARM Linux开发板GPIO输入输出接口操作,对于大部分的 ARM Linux 开发板来说,其实都已经把所有的GPIO驱动做好了,我们要做的就是去使用它。
(1)、GPIO目录
在ARM Linux中,GPIO驱动的目录是:/sys/class/gpio/,所以,要首先进入这个目录,命令:
$ cd /sys/class/gpio/
(2)、目录解析
使用ls命令查看目录详情:
$ ls
export gpiochip128 gpiochip32 gpiochip96
gpiochip0 gpiochip160 gpiochip64 unexport
如上列出了6个gpio目录、export、unexport文件。gpio目录对应基地址,比如芯片的IO定义为GPIO0_0~GPIO0_31、GPIO1_0~GPIO1_31等等,那么GPIO0对应的base就是gpiochip0,GPIO1对应的base就是gpiochip32,以此类推。大家有兴趣可以可以进入其中一个目录看看,
$cd gpiochip32
$ls
base label ngpio power subsystem uevent
具体内容就不一一介绍了,如果有需要,我可以再写个介绍。
(3)、控制实例
比如要实现控制 GPIO5_9(5*32+9=169) 口,在/sys/class/gpio目录下先生成一个gpio169 目录:
$ echo 169 > export
$ cd gpio169
$ ls
active_low direction edge power subsystem uevent
(4)、控制
$ cat direction #查看方向
in
$ echo out > direction #设置为输出
$ cat value #获取值
0
$ echo 1 > value #设置为1
这时候,你可以用万用表去测一下J11上12号引脚(GPIO5_9)的电平;
关于GPIO的使用,还有另外两种方式,可以参考这篇文章,写得比较详细:https://www.myir-tech.com/bbs/thread-7220-1-1.html。
4 小结
这一期的内容,需要有一定的嵌入式Linux开发基础。因为之前没有做过嵌入式Linux开发项目,都是基于Cortex-M系列的应用开发,所以我还是下了不少功夫,Cortex-A系列的复杂度比Cortex-M系列高出几个数量级,何况米尔公司又没有提供非常详细的入门引导教程。建议大家可以找几本嵌入式Linux方面的书来看看。嵌入式Linux开发还有一个重要内容需要去学习,就是Qt嵌入式开发。所以,我准备将更多的时间精力放在Qt开发上。下一期就是关于这个主题的,我希望能将更多有价值的内容分享给大家。 |
|