【原创】树莓派3B开发Go语言(三)-寄存器版本GPIO

前一小节通过了调用github上某位同道中人写好的库,实现了对GPIO的操作,这里从原理上分析如何操作 树莓派3B 的寄存器,也是从最简单的例子开始,点亮第二个LED灯。

所以我们今天的任务:通过操作寄存器的方式点亮第二个LED灯(板子上的第10脚,对应BCM.GPIO15,也是之前图中的RXD)

BCM.GPIO15.png

本文源码地址参见 github

一、分析下树莓派硬件寄存器

BCM2835数据手册

这里因为之前查阅了官网的说明,BCM2837 和 BCM2835 在外设这一块是没有变化的,所以我们可以直接参考 BCM2835 的数据手册。我们直接翻阅到 89 页左右,这里就是我们的目标了,GPIO 主要在第六章,这里找到下面这个表格:(图片部分截取)

寄存器地址.png

可以看到在芯片地址中 GPIO 主要分布在 0x7E200000 这个地址往后走的一部分,最大的地址是 0x7E200B0。我们接着查阅芯片手册中关于每个寄存器作用。最后确认下来,如果我们要点亮那个LED灯,需要将 BCM.GPIO15设置为输出,且输出一个高电平就可以了。通过找寻寄存器对应的区域,判断到需要将下面图2中15位设置为1(BCM.GPIO15设置为输出模式)。

GPIOAFSR1.png

进一步设置中需要对输出寄存器相关位进行赋值,可以将 BCM.GPIO15 设置为高,也就是下图3中寄存器相关位。

GPSET0.png

当时设置了输出位为高电平可以点亮LED灯,同时也需要输出位为低电平以关闭LED灯,也就是下面图4这个寄存器。

GPCLR0.png

了解清楚我们要操作的寄存器后,我们需要进一步确定这个地址到底是多少,通过手册第5页 图5 我们可以看出实际上ARM的MMU把上面的 0x7E200000 这种实际地址映射到了0x200000000x40000000 这个地址上去,但是具体地址是多少也不是很清楚,这里就去找到了文档 bcm2835 的c语言程序找寻蛛丝马迹。后来发现了可以通过读取 /proc/device-tree/soc/ranges 找到具体的外设地址和范围,按照实际的偏移进行映射就可以使用了。

地址映射图.png

二、实战

经过了上面一圈的查资料、分析,发现思路越来越明朗了,这里就开始这几实战了。Go 语言对于指针操作会比C语言要求更为严格一点,也没有宏定义可以使用,这里就直接定义到const中。

package main

import (
    "os"
    "fmt"
    "bytes"
    "encoding/binary"
    "syscall"
    "unsafe"
    "time"
)

const(

    // define the device tree range
    BCM2837_PRI3B_DT_FILENAME                 = "/proc/device-tree/soc/ranges"
    BCM2837_PRI3_DT_PERI_BASE_ADDRESS_OFFSET  =  0x4
    BCM2837_PRI3_DT_PERI_SIZE_OFFSET          =  0X8
    BCM2837_GPIO_BASE                         =  0x00200000

    /*!  灵感部分来自 bcm2835 demo包
    GPIO register offsets from BCM2835_GPIO_BASE.
    Offsets into the GPIO Peripheral block in bytes per 6.1 Register View
    */
    BCM2837_GPFSEL0                 =    0x0000 /*!< GPIO Function Select 0 */
    BCM2837_GPFSEL1                 =    0x0004 /*!< GPIO Function Select 1 */
    BCM2837_GPSET0                  =    0x001c /*!< GPIO Pin Output Set 0 */
    BCM2837_GPSET1                  =    0x0020 /*!< GPIO Pin Output Set 1 */
    BCM2837_GPCLR0                  =    0x0028 /*!< GPIO Pin Output Clear 0 */
    BCM2837_GPCLR1                  =    0x002c /*!< GPIO Pin Output Clear 1 */
   
)

var  Bcm2837_peripherals_base   uint32
var  Bcm2837_peripherals_size   uint32
var  Bcm2837_gpio               uint32

func main(){
    // find the io peripheral base and range
    f,err:= os.OpenFile(BCM2837_PRI3B_DT_FILENAME,os.O_RDONLY,0)

    if err != nil {
        fmt.Println("open range file err")
    }
    defer f.Close()
    //read value and change []byte to uint32
    var buf []byte = make([]byte,4)
    f.ReadAt(buf , BCM2837_PRI3_DT_PERI_BASE_ADDRESS_OFFSET )
    bytesBuffer := bytes.NewBuffer(buf)
    binary.Read(bytesBuffer , binary.BigEndian , &Bcm2837_peripherals_base )

    f.ReadAt(buf , BCM2837_PRI3_DT_PERI_SIZE_OFFSET )
    bytesBuffer  = bytes.NewBuffer(buf)
    binary.Read(bytesBuffer , binary.BigEndian , &Bcm2837_peripherals_size )

    fmt.Printf("get peripherals base:%x size:%x\n" , Bcm2837_peripherals_base , Bcm2837_peripherals_size )

    //need su execute
    if os.Geteuid() == 0 {

        /* open the master /dev/mem device */
        f, err := os.OpenFile("/dev/mem",os.O_RDWR,0)
        if err != nil {
            fmt.Println("Open mem error")
        }

        p,err := syscall.Mmap(int(f.Fd()),int64(Bcm2837_peripherals_base),int(Bcm2837_peripherals_size),syscall.PROT_READ|syscall.PROT_WRITE,syscall.MAP_SHARED)

        if err != nil {
            fmt.Println("mmap error")
        }

        //strat find the gpio register
        Bcm2837_gpio = *(*uint32)(unsafe.Pointer(&p)) + uint32( BCM2837_GPIO_BASE )

        var test uintptr = uintptr( Bcm2837_gpio + BCM2837_GPFSEL1 )
        *(*uint32)(unsafe.Pointer(test)) = ( 0x1 << 15 )

        test = uintptr( Bcm2837_gpio + BCM2837_GPSET0 )
        *(*uint32)(unsafe.Pointer(test)) = ( 0x1 << 15 )

        time.Sleep( time.Second  * 2 )

        test = uintptr( Bcm2837_gpio + BCM2837_GPCLR0 )
        *(*uint32)(unsafe.Pointer(test)) = ( 0x1 << 15 )

    }else {
        fmt.Println("please use root execute")
        panic(err)
    }
}

如有朋友感兴趣可以简信我。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339

推荐阅读更多精彩内容