本帖最后由 tjCFeng 于 2015-4-23 17:33 编辑
Go是Google开发的一种编译型,可平行化,并具有垃圾回收功能的编程语言。 Go是有表达力、简洁、清晰和有效率的。它的并行机制使其很容易编写多核和网络应用,而新的类型系统 允许构建有性的模块化程序。Go编译到机器码非常快 速,同时具有便利的垃圾回收和强大的运行时反射。 GO快速的、静态类型编译语言,但是感觉上是动态 类型的,解释型语言,易于编写。而且它的特点和C、pascal一样,可以编译为本地原生代码,生成的可直行文件可以直接拷贝到其他同种系统中运行,开发和运行效率都很高,甚至可以达到C语言的80%。
在大概熟悉了go的语法后,打算在arm平台上尝试一下直接操作底层寄存器的开发,大体思路应该是一样的,但是go已经封装了很多功能,不能直接调用系统的API函数,如果调用的话需要通过嵌入C来实现。从网上找了几个例子看了下也是这样的。不过我还是不死心,进过两天的摸索,查看了相关的例子之后终于初步实现了寄存器的操作,可以算做是学习go的第一步吧。
本次开发的环境是在Firefly-RK3288开发板的Lubuntu下进行的,直接编译为本地可执行文件。go的开发环境就不详细说明了,从github上下载源代码后直行all.bash可以自动完成安装。目前是1.4.2版本,修复了许多bug。 以前用pascal封装过Allwinner A20的寄存器类库,估计go大同小异,都是先找到地址,映射到内存,再根据寄存器来赋值完成功能。 不过一上来就给我难住了,首先是打开/dev/mem文件,返回的应该是个int的文件句柄,而go返回的是个结构体不能直接用。在查看了手册后发现,其中的Fd()是这个句柄,可以拿来使用,第一个问题解决。
接下来就是映射内存地址了。先看原理图,找到需要用到的IO口
板子上的WORK_LED是我要控制的,而它对应的是GPIO8_A1这个Pin,并且的高电平是关闭,低电平是点亮。 这个GPIO8的地址是多少呢?从内核代码里和手册上都能找到
物理地址是0xFF7F0000。在pascal中是要除以PAGE_SIZE来计算的,而到了go里,不知道是不是因为go把mmap封装了,这样调用不成功,很是奇怪,因为在c和pascal里都是这样的,没道理会改变吧。 经过多次试验发现,这里是不能除PAGE_SIZE的,估计是跟go的封装有关。映射成功,稀里糊涂第二个问题解决,至少不报错了。
在go里mmap之后返回的是一个数组,而不是一个指针,并且go的数组指针指向的并不是数组的首地址,所以不能直接对指针赋值来改变寄存器的值,而是直接对数组赋值达到这个效果。映射后的数组是8位的,为了和手一致,需要转换成32位的,经过学习(抄的代码:-)),又用到了unsafe类型,转换后可以打印出数组的内容了,看来第三个问题也解决了。
赶紧来操作寄存器试一试,GOIO_A0是第0位,那么GPIO_A1理所当然是第1位了。所以直接对这个数组[1]进行赋1和清0,果然那个Work_LED能够控制了。
大功告成,终于可以控制GPIO了。不过真的对吗?感觉有些地方想不通,手册里标明的第一个寄存器是高低电平,第二个是输入输出,那么我对着数组的[1]赋值算是什么呢?
经过几次的调试,最后得到确认,数组的[0]才是对应的DR,[1]是DDR,因为数据已经从8位转换为32位了,所以数组[0]中的[1]才是我要控制的GPIO_A1,前面操作的原来是方向寄存器。 到此才算基本搞清楚了go操作寄存器的方法。第一步至关重要,只要成功了后面的就好办了。
附上go的全部程序,就是这么一点语句,折腾了两个晚上的时间。
|