前一小節(jié)通過了調(diào)用github上某位同道中人寫好的庫(kù),實(shí)現(xiàn)了對(duì)GPIO的操作,這里從原理上分析如何操作 樹莓派3B 的寄存器逾雄,也是從最簡(jiǎn)單的例子開始罪裹,點(diǎn)亮第二個(gè)LED燈。
所以我們今天的任務(wù):通過操作寄存器的方式點(diǎn)亮第二個(gè)LED燈(板子上的第10腳,對(duì)應(yīng)BCM.GPIO15,也是之前圖中的RXD)。
本文源碼地址參見 github
一、分析下樹莓派硬件寄存器
這里因?yàn)橹安殚喠斯倬W(wǎng)的說(shuō)明徐绑,BCM2837 和 BCM2835 在外設(shè)這一塊是沒有變化的,所以我們可以直接參考 BCM2835 的數(shù)據(jù)手冊(cè)莫辨。我們直接翻閱到 89 頁(yè)左右傲茄,這里就是我們的目標(biāo)了毅访,GPIO 主要在第六章,這里找到下面這個(gè)表格:(圖片部分截忍棠弧)
可以看到在芯片地址中 GPIO 主要分布在 0x7E200000 這個(gè)地址往后走的一部分俺抽,最大的地址是 0x7E200B0。我們接著查閱芯片手冊(cè)中關(guān)于每個(gè)寄存器作用较曼。最后確認(rèn)下來(lái)磷斧,如果我們要點(diǎn)亮那個(gè)LED燈,需要將 BCM.GPIO15設(shè)置為輸出捷犹,且輸出一個(gè)高電平就可以了弛饭。通過找尋寄存器對(duì)應(yīng)的區(qū)域,判斷到需要將下面圖2中15位設(shè)置為1(BCM.GPIO15設(shè)置為輸出模式)萍歉。
進(jìn)一步設(shè)置中需要對(duì)輸出寄存器相關(guān)位進(jìn)行賦值侣颂,可以將 BCM.GPIO15 設(shè)置為高,也就是下圖3中寄存器相關(guān)位枪孩。
當(dāng)時(shí)設(shè)置了輸出位為高電平可以點(diǎn)亮LED燈憔晒,同時(shí)也需要輸出位為低電平以關(guān)閉LED燈,也就是下面圖4這個(gè)寄存器蔑舞。
了解清楚我們要操作的寄存器后拒担,我們需要進(jìn)一步確定這個(gè)地址到底是多少,通過手冊(cè)第5頁(yè) 圖5 我們可以看出實(shí)際上ARM的MMU把上面的 0x7E200000 這種實(shí)際地址映射到了0x20000000 到 0x40000000 這個(gè)地址上去攻询,但是具體地址是多少也不是很清楚从撼,這里就去找到了文檔 bcm2835 的c語(yǔ)言程序找尋蛛絲馬跡。后來(lái)發(fā)現(xiàn)了可以通過讀取 /proc/device-tree/soc/ranges 找到具體的外設(shè)地址和范圍钧栖,按照實(shí)際的偏移進(jìn)行映射就可以使用了低零。
二、實(shí)戰(zhàn)
經(jīng)過了上面一圈的查資料拯杠、分析掏婶,發(fā)現(xiàn)思路越來(lái)越明朗了,這里就開始這幾實(shí)戰(zhàn)了潭陪。Go 語(yǔ)言對(duì)于指針操作會(huì)比C語(yǔ)言要求更為嚴(yán)格一點(diǎn)雄妥,也沒有宏定義可以使用,這里就直接定義到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
/*! 靈感部分來(lái)自 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)
}
}
如有朋友感興趣可以簡(jiǎn)信我茎芭。