From 3e0491354cefa829b09c2ef999c9e2839a96af1c Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Tue, 14 Nov 2017 17:06:00 +0100 Subject: [PATCH 01/20] Do minor refactoring --- rpio.go | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/rpio.go b/rpio.go index 5fdc5dd..84d4de7 100644 --- a/rpio.go +++ b/rpio.go @@ -265,24 +265,19 @@ func Open() (err error) { var base int64 // Open fd for rw mem access; try gpiomem first - if file, err = os.OpenFile( - "/dev/gpiomem", - os.O_RDWR|os.O_SYNC, - 0); os.IsNotExist(err) { - file, err = os.OpenFile( - "/dev/mem", - os.O_RDWR|os.O_SYNC, - 0) - base = getGPIOBase() + file, err = os.OpenFile("/dev/gpiomem", os.O_RDWR|os.O_SYNC, 0) + if !os.IsNotExist(err) { + return } - + file, err = os.OpenFile("/dev/mem", os.O_RDWR|os.O_SYNC, 0) if err != nil { return } - // FD can be closed after memory mapping defer file.Close() + base = getGPIOBase() + memlock.Lock() defer memlock.Unlock() @@ -292,7 +287,8 @@ func Open() (err error) { base, memLength, syscall.PROT_READ|syscall.PROT_WRITE, - syscall.MAP_SHARED) + syscall.MAP_SHARED, + ) if err != nil { return From 12feac8555e59e4d798c4430ef2acc7337bd701f Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Tue, 14 Nov 2017 17:52:05 +0100 Subject: [PATCH 02/20] Add pin mode Clock --- rpio.go | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/rpio.go b/rpio.go index 84d4de7..75db597 100644 --- a/rpio.go +++ b/rpio.go @@ -4,7 +4,7 @@ Package rpio provides GPIO access on the Raspberry PI without any need for external c libraries (ex: WiringPI or BCM2835). Supports simple operations such as: -- Pin mode/direction (input/output) +- Pin mode/direction (input/output/clock) - Pin write (high/low) - Pin read (high/low) - Pull up/down/off @@ -66,7 +66,7 @@ import ( "unsafe" ) -type Direction uint8 +type Mode uint8 type Pin uint8 type State uint8 type Pull uint8 @@ -80,10 +80,11 @@ const ( pinMask uint32 = 7 // 0b111 - pinmode is 3 bits ) -// Pin direction, a pin can be set in Input or Output mode +// Pin mode, a pin can be set in Input or Output mode, or clock const ( - Input Direction = iota + Input Mode = iota Output + Clock ) // State of pin, High / Low @@ -116,6 +117,12 @@ func (pin Pin) Output() { PinMode(pin, Output) } + +// Set pin as Clock +func (pin Pin) Clock() { + PinMode(pin, Clock) +} + // Set pin High func (pin Pin) High() { WritePin(pin, High) @@ -131,9 +138,9 @@ func (pin Pin) Toggle() { TogglePin(pin) } -// Set pin Direction -func (pin Pin) Mode(dir Direction) { - PinMode(pin, dir) +// Set pin Mode +func (pin Pin) Mode(mode Mode) { + PinMode(pin, mode) } // Set pin state (high/low) @@ -166,22 +173,34 @@ func (pin Pin) PullOff() { PullMode(pin, PullOff) } -// PinMode sets the direction of a given pin (Input or Output) -func PinMode(pin Pin, direction Direction) { +// PinMode sets the mode (direction) of a given pin (Input, Output or Clock) +// Clock is possible only for some pins (bcm 4, 5, 6) +func PinMode(pin Pin, mode Mode) { // Pin fsel register, 0 or 1 depending on bank fsel := uint8(pin) / 10 shift := (uint8(pin) % 10) * 3 + f := uint32(0) memlock.Lock() defer memlock.Unlock() - if direction == Input { - mem[fsel] = mem[fsel] &^ (pinMask << shift) - } else { - mem[fsel] = (mem[fsel] &^ (pinMask << shift)) | (1 << shift) + switch mode { + case Input: + f = 0 // 000 + case Output: + f = 1 // 001 + case Clock: + switch pin { + case 4, 5, 6, 32, 34, 42, 43, 44: + f = 4 // 100 - alt0 + case 20, 21: + f = 2 // 010 - alt5 + default: + f = 1 // 001 - fallback to output + } } - + mem[fsel] = (mem[fsel] &^ (pinMask << shift)) | (f << shift) } // WritePin sets a given pin High or Low From aae3fc6504d119d6b425f1488850ca47dae545dd Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Tue, 14 Nov 2017 22:04:03 +0100 Subject: [PATCH 03/20] Implement setClock - using clock manager registers --- rpio.go | 156 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 116 insertions(+), 40 deletions(-) diff --git a/rpio.go b/rpio.go index 75db597..23c84f5 100644 --- a/rpio.go +++ b/rpio.go @@ -74,12 +74,26 @@ type Pull uint8 // Memory offsets for gpio, see the spec for more details const ( bcm2835Base = 0x20000000 - pi1GPIOBase = bcm2835Base + 0x200000 - memLength = 4096 + gpioOffset = 0x200000 + clkOffset = 0x101000 + + memLength = 4096 pinMask uint32 = 7 // 0b111 - pinmode is 3 bits ) +var ( + base int64 + gpioBase int64 + clkBase int64 +) + +func init () { + base = getBase() + gpioBase = base + gpioOffset + clkBase = base + clkOffset +} + // Pin mode, a pin can be set in Input or Output mode, or clock const ( Input Mode = iota @@ -103,8 +117,10 @@ const ( // Arrays for 8 / 32 bit access to memory and a semaphore for write locking var ( memlock sync.Mutex - mem []uint32 - mem8 []uint8 + gpioMem []uint32 + clkMem []uint32 + gpioMem8 []uint8 + clkMem8 []uint8 ) // Set pin as Input @@ -117,10 +133,10 @@ func (pin Pin) Output() { PinMode(pin, Output) } - -// Set pin as Clock -func (pin Pin) Clock() { +// Set pin as Clock with given freq +func (pin Pin) Clock(freq int) { PinMode(pin, Clock) + SetClock(pin, freq) } // Set pin High @@ -197,10 +213,10 @@ func PinMode(pin Pin, mode Mode) { case 20, 21: f = 2 // 010 - alt5 default: - f = 1 // 001 - fallback to output + return } } - mem[fsel] = (mem[fsel] &^ (pinMask << shift)) | (f << shift) + gpioMem[fsel] = (gpioMem[fsel] &^ (pinMask << shift)) | (f << shift) } // WritePin sets a given pin High or Low @@ -218,9 +234,9 @@ func WritePin(pin Pin, state State) { defer memlock.Unlock() if state == Low { - mem[clearReg] = 1 << (p & 31) + gpioMem[clearReg] = 1 << (p & 31) } else { - mem[setReg] = 1 << (p & 31) + gpioMem[setReg] = 1 << (p & 31) } } @@ -230,7 +246,7 @@ func ReadPin(pin Pin) State { // Input level register offset (13 / 14 depending on bank) levelReg := uint8(pin)/32 + 13 - if (mem[levelReg] & (1 << uint8(pin))) != 0 { + if (gpioMem[levelReg] & (1 << uint8(pin))) != 0 { return High } @@ -259,21 +275,72 @@ func PullMode(pin Pin, pull Pull) { switch pull { case PullDown, PullUp: - mem[pullReg] = mem[pullReg]&^3 | uint32(pull) + gpioMem[pullReg] = gpioMem[pullReg]&^3 | uint32(pull) case PullOff: - mem[pullReg] = mem[pullReg] &^ 3 + gpioMem[pullReg] = gpioMem[pullReg] &^ 3 } // Wait for value to clock in, this is ugly, sorry :( time.Sleep(time.Microsecond) - mem[pullClkReg] = 1 << shift + gpioMem[pullClkReg] = 1 << shift // Wait for value to clock in time.Sleep(time.Microsecond) - mem[pullReg] = mem[pullReg] &^ 3 - mem[pullClkReg] = 0 + gpioMem[pullReg] = gpioMem[pullReg] &^ 3 + gpioMem[pullClkReg] = 0 + +} + + +// Set clock speed for given pin +// +// freq should be in range 4688 - 19.2MHz to prevent unexpected behavior +// (for smaller frequencies implement custom software clock using output pin and sleep) +// +// Note that some pins share the same clock source that means +// that changing frequency for one pin will change it also for all pins within a group +// The groups are: clk0 (4, 20, 32, 34), clk1 (5, 21, 42, 43) and clk2 (6 and 43) +func SetClock(pin Pin, freq int) { + const source = 19200000 // oscilator frequency + const maxUint12 = 4095 + + divi := uint32(source / freq) + divf := uint32(((source % freq) << 12) / source) + + divi &= maxUint12 + divf &= maxUint12 + + clkCtl := 0x70 + clkDiv := 0x74 + switch pin { + case 4, 20, 32, 34: // clk0 + clkCtl += 0 + clkDiv += 0 + case 5, 21, 42, 44: // clk1 + clkCtl += 8 + clkDiv += 8 + case 6, 43: // clk2 + clkCtl += 16 + clkDiv += 16 + default: + return + } + + memlock.Lock() + defer memlock.Unlock() + + const PASSWORD = 0x5A000000 + const busy = 0x80 + const enab = 0x10 + const src = 0x01 // oscilator + + clkMem[clkCtl] = PASSWORD | src // stop gpio clock + for clkMem[clkCtl] & busy != 0 {} // ... and wait + + clkMem[clkDiv] = PASSWORD | (divi << 12) | divf // Set dividers + clkMem[clkCtl] = PASSWORD | enab | src // Start Clock } @@ -281,59 +348,68 @@ func PullMode(pin Pin, pull Pull) { // Some reflection magic is used to convert it to a unsafe []uint32 pointer func Open() (err error) { var file *os.File - var base int64 - // Open fd for rw mem access; try gpiomem first + // Open fd for rw mem access; try mem gpio first file, err = os.OpenFile("/dev/gpiomem", os.O_RDWR|os.O_SYNC, 0) - if !os.IsNotExist(err) { - return + if os.IsNotExist(err) { // try mem (need root) + file, err = os.OpenFile("/dev/mem", os.O_RDWR|os.O_SYNC, 0) } - file, err = os.OpenFile("/dev/mem", os.O_RDWR|os.O_SYNC, 0) if err != nil { return } // FD can be closed after memory mapping defer file.Close() - base = getGPIOBase() - memlock.Lock() defer memlock.Unlock() - // Memory map GPIO registers to byte array - mem8, err = syscall.Mmap( - int(file.Fd()), - base, - memLength, - syscall.PROT_READ|syscall.PROT_WRITE, - syscall.MAP_SHARED, - ) - + // Memory map GPIO registers to slice + gpioMem, gpioMem8, err = memMap(file.Fd(), base + gpioOffset) if err != nil { return } + // Memory map clock reisters to slice + clkMem, clkMem8, err = memMap(file.Fd(), base + clkOffset) + if err != nil { + return + } + + return nil +} + +func memMap (fd uintptr, offset int64) (mem []uint32, mem8 []byte, err error) { + mem8, err = syscall.Mmap( + int(fd), + base + clkOffset, + memLength, + syscall.PROT_READ|syscall.PROT_WRITE, + syscall.MAP_SHARED, + ) + if err != nil { + return + } // Convert mapped byte memory to unsafe []uint32 pointer, adjust length as needed header := *(*reflect.SliceHeader)(unsafe.Pointer(&mem8)) header.Len /= (32 / 8) // (32 bit = 4 bytes) header.Cap /= (32 / 8) - mem = *(*[]uint32)(unsafe.Pointer(&header)) - - return nil + return } // Close unmaps GPIO memory func Close() error { memlock.Lock() defer memlock.Unlock() - return syscall.Munmap(mem8) + syscall.Munmap(gpioMem8) + syscall.Munmap(clkMem8) + return nil } // Read /proc/device-tree/soc/ranges and determine the base address. // Use the default Raspberry Pi 1 base address if this fails. -func getGPIOBase() (base int64) { - base = pi1GPIOBase +func getBase() (base int64) { + base = bcm2835Base ranges, err := os.Open("/proc/device-tree/soc/ranges") defer ranges.Close() if err != nil { @@ -350,5 +426,5 @@ func getGPIOBase() (base int64) { if err != nil { return } - return int64(out + 0x200000) + return int64(out) } From b7b1bd8b813b1de7d2a4d68ed4725b62be5dc5bf Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Tue, 14 Nov 2017 22:16:15 +0100 Subject: [PATCH 04/20] Do minor renaming --- rpio.go | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/rpio.go b/rpio.go index 23c84f5..44506af 100644 --- a/rpio.go +++ b/rpio.go @@ -194,7 +194,7 @@ func (pin Pin) PullOff() { func PinMode(pin Pin, mode Mode) { // Pin fsel register, 0 or 1 depending on bank - fsel := uint8(pin) / 10 + fselReg := uint8(pin) / 10 shift := (uint8(pin) % 10) * 3 f := uint32(0) @@ -216,7 +216,7 @@ func PinMode(pin Pin, mode Mode) { return } } - gpioMem[fsel] = (gpioMem[fsel] &^ (pinMask << shift)) | (f << shift) + gpioMem[fselReg] = (gpioMem[fselReg] &^ (pinMask << shift)) | (f << shift) } // WritePin sets a given pin High or Low @@ -296,11 +296,11 @@ func PullMode(pin Pin, pull Pull) { // Set clock speed for given pin // -// freq should be in range 4688 - 19.2MHz to prevent unexpected behavior +// freq should be in range 4688Hz - 19.2MHz to prevent unexpected behavior // (for smaller frequencies implement custom software clock using output pin and sleep) // -// Note that some pins share the same clock source that means -// that changing frequency for one pin will change it also for all pins within a group +// Note that some pins share the same clock source, it means that +// changing frequency for one pin will change it also for all pins within a group // The groups are: clk0 (4, 20, 32, 34), clk1 (5, 21, 42, 43) and clk2 (6 and 43) func SetClock(pin Pin, freq int) { const source = 19200000 // oscilator frequency @@ -312,18 +312,18 @@ func SetClock(pin Pin, freq int) { divi &= maxUint12 divf &= maxUint12 - clkCtl := 0x70 - clkDiv := 0x74 + clkCtlReg := 0x70 + clkDivReg := 0x74 switch pin { case 4, 20, 32, 34: // clk0 - clkCtl += 0 - clkDiv += 0 + clkCtlReg += 0 + clkDivReg += 0 case 5, 21, 42, 44: // clk1 - clkCtl += 8 - clkDiv += 8 + clkCtlReg += 8 + clkDivReg += 8 case 6, 43: // clk2 - clkCtl += 16 - clkDiv += 16 + clkCtlReg += 16 + clkDivReg += 16 default: return } @@ -336,11 +336,11 @@ func SetClock(pin Pin, freq int) { const enab = 0x10 const src = 0x01 // oscilator - clkMem[clkCtl] = PASSWORD | src // stop gpio clock - for clkMem[clkCtl] & busy != 0 {} // ... and wait + clkMem[clkCtlReg] = PASSWORD | src // stop gpio clock + for clkMem[clkCtlReg] & busy != 0 {} // ... and wait - clkMem[clkDiv] = PASSWORD | (divi << 12) | divf // Set dividers - clkMem[clkCtl] = PASSWORD | enab | src // Start Clock + clkMem[clkDivReg] = PASSWORD | (divi << 12) | divf // set dividers + clkMem[clkCtlReg] = PASSWORD | enab | src // start clock } @@ -364,13 +364,13 @@ func Open() (err error) { defer memlock.Unlock() // Memory map GPIO registers to slice - gpioMem, gpioMem8, err = memMap(file.Fd(), base + gpioOffset) + gpioMem, gpioMem8, err = memMap(file.Fd(), gpioBase) if err != nil { return } // Memory map clock reisters to slice - clkMem, clkMem8, err = memMap(file.Fd(), base + clkOffset) + clkMem, clkMem8, err = memMap(file.Fd(), clkBase) if err != nil { return } From b604d031789d9b7119651fb6aad92afda751a0bc Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Tue, 14 Nov 2017 22:25:26 +0100 Subject: [PATCH 05/20] Minor fixes --- rpio.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/rpio.go b/rpio.go index 44506af..38ed04d 100644 --- a/rpio.go +++ b/rpio.go @@ -78,8 +78,6 @@ const ( clkOffset = 0x101000 memLength = 4096 - - pinMask uint32 = 7 // 0b111 - pinmode is 3 bits ) var ( @@ -198,9 +196,6 @@ func PinMode(pin Pin, mode Mode) { shift := (uint8(pin) % 10) * 3 f := uint32(0) - memlock.Lock() - defer memlock.Unlock() - switch mode { case Input: f = 0 // 000 @@ -216,6 +211,12 @@ func PinMode(pin Pin, mode Mode) { return } } + + memlock.Lock() + defer memlock.Unlock() + + const pinMask = 7 // 0b111 - pinmode is 3 bits + gpioMem[fselReg] = (gpioMem[fselReg] &^ (pinMask << shift)) | (f << shift) } @@ -246,7 +247,7 @@ func ReadPin(pin Pin) State { // Input level register offset (13 / 14 depending on bank) levelReg := uint8(pin)/32 + 13 - if (gpioMem[levelReg] & (1 << uint8(pin))) != 0 { + if (gpioMem[levelReg] & (1 << uint8(pin & 31))) != 0 { return High } @@ -401,8 +402,12 @@ func memMap (fd uintptr, offset int64) (mem []uint32, mem8 []byte, err error) { func Close() error { memlock.Lock() defer memlock.Unlock() - syscall.Munmap(gpioMem8) - syscall.Munmap(clkMem8) + if err := syscall.Munmap(gpioMem8) { + return err + } + if err := syscall.Munmap(clkMem8) { + return err + } return nil } From cdcd97f287dbd62173a4e9a65af1e6bc2e6d8d97 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Tue, 14 Nov 2017 22:33:04 +0100 Subject: [PATCH 06/20] go fmt --- rpio.go | 58 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/rpio.go b/rpio.go index 38ed04d..0aa6dff 100644 --- a/rpio.go +++ b/rpio.go @@ -74,19 +74,19 @@ type Pull uint8 // Memory offsets for gpio, see the spec for more details const ( bcm2835Base = 0x20000000 - gpioOffset = 0x200000 - clkOffset = 0x101000 + gpioOffset = 0x200000 + clkOffset = 0x101000 memLength = 4096 ) var ( - base int64 + base int64 gpioBase int64 - clkBase int64 + clkBase int64 ) -func init () { +func init() { base = getBase() gpioBase = base + gpioOffset clkBase = base + clkOffset @@ -114,11 +114,11 @@ const ( // Arrays for 8 / 32 bit access to memory and a semaphore for write locking var ( - memlock sync.Mutex - gpioMem []uint32 - clkMem []uint32 - gpioMem8 []uint8 - clkMem8 []uint8 + memlock sync.Mutex + gpioMem []uint32 + clkMem []uint32 + gpioMem8 []uint8 + clkMem8 []uint8 ) // Set pin as Input @@ -247,7 +247,7 @@ func ReadPin(pin Pin) State { // Input level register offset (13 / 14 depending on bank) levelReg := uint8(pin)/32 + 13 - if (gpioMem[levelReg] & (1 << uint8(pin & 31))) != 0 { + if (gpioMem[levelReg] & (1 << uint8(pin&31))) != 0 { return High } @@ -294,7 +294,6 @@ func PullMode(pin Pin, pull Pull) { } - // Set clock speed for given pin // // freq should be in range 4688Hz - 19.2MHz to prevent unexpected behavior @@ -316,17 +315,17 @@ func SetClock(pin Pin, freq int) { clkCtlReg := 0x70 clkDivReg := 0x74 switch pin { - case 4, 20, 32, 34: // clk0 - clkCtlReg += 0 - clkDivReg += 0 - case 5, 21, 42, 44: // clk1 - clkCtlReg += 8 - clkDivReg += 8 - case 6, 43: // clk2 - clkCtlReg += 16 - clkDivReg += 16 - default: - return + case 4, 20, 32, 34: // clk0 + clkCtlReg += 0 + clkDivReg += 0 + case 5, 21, 42, 44: // clk1 + clkCtlReg += 8 + clkDivReg += 8 + case 6, 43: // clk2 + clkCtlReg += 16 + clkDivReg += 16 + default: + return } memlock.Lock() @@ -338,10 +337,11 @@ func SetClock(pin Pin, freq int) { const src = 0x01 // oscilator clkMem[clkCtlReg] = PASSWORD | src // stop gpio clock - for clkMem[clkCtlReg] & busy != 0 {} // ... and wait + for clkMem[clkCtlReg]&busy != 0 { + } // ... and wait clkMem[clkDivReg] = PASSWORD | (divi << 12) | divf // set dividers - clkMem[clkCtlReg] = PASSWORD | enab | src // start clock + clkMem[clkCtlReg] = PASSWORD | enab | src // start clock } @@ -379,10 +379,10 @@ func Open() (err error) { return nil } -func memMap (fd uintptr, offset int64) (mem []uint32, mem8 []byte, err error) { +func memMap(fd uintptr, offset int64) (mem []uint32, mem8 []byte, err error) { mem8, err = syscall.Mmap( int(fd), - base + clkOffset, + base+clkOffset, memLength, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED, @@ -402,10 +402,10 @@ func memMap (fd uintptr, offset int64) (mem []uint32, mem8 []byte, err error) { func Close() error { memlock.Lock() defer memlock.Unlock() - if err := syscall.Munmap(gpioMem8) { + if err := syscall.Munmap(gpioMem8); err != nil { return err } - if err := syscall.Munmap(clkMem8) { + if err := syscall.Munmap(clkMem8); err != nil { return err } return nil From 9b13a0969b78be8dba0687038b9290c4d806a538 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Tue, 14 Nov 2017 23:16:39 +0100 Subject: [PATCH 07/20] Change clock public api --- rpio.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/rpio.go b/rpio.go index 0aa6dff..c6df11c 100644 --- a/rpio.go +++ b/rpio.go @@ -131,10 +131,9 @@ func (pin Pin) Output() { PinMode(pin, Output) } -// Set pin as Clock with given freq -func (pin Pin) Clock(freq int) { +// Set pin as Clock +func (pin Pin) Clock() { PinMode(pin, Clock) - SetClock(pin, freq) } // Set pin High @@ -152,6 +151,10 @@ func (pin Pin) Toggle() { TogglePin(pin) } +func (pin Pin) Freq(freq int) { + SetFreq(pin, freq) +} + // Set pin Mode func (pin Pin) Mode(mode Mode) { PinMode(pin, mode) @@ -302,7 +305,7 @@ func PullMode(pin Pin, pull Pull) { // Note that some pins share the same clock source, it means that // changing frequency for one pin will change it also for all pins within a group // The groups are: clk0 (4, 20, 32, 34), clk1 (5, 21, 42, 43) and clk2 (6 and 43) -func SetClock(pin Pin, freq int) { +func SetFreq(pin Pin, freq int) { const source = 19200000 // oscilator frequency const maxUint12 = 4095 @@ -350,7 +353,7 @@ func SetClock(pin Pin, freq int) { func Open() (err error) { var file *os.File - // Open fd for rw mem access; try mem gpio first + // Open fd for rw mem access; try gpiomem first file, err = os.OpenFile("/dev/gpiomem", os.O_RDWR|os.O_SYNC, 0) if os.IsNotExist(err) { // try mem (need root) file, err = os.OpenFile("/dev/mem", os.O_RDWR|os.O_SYNC, 0) From 0122f1902de645cecab765878d02cb2feb205825 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Wed, 15 Nov 2017 11:26:49 +0100 Subject: [PATCH 08/20] Fix divf computation --- rpio.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rpio.go b/rpio.go index c6df11c..30fab9c 100644 --- a/rpio.go +++ b/rpio.go @@ -191,7 +191,8 @@ func (pin Pin) PullOff() { } // PinMode sets the mode (direction) of a given pin (Input, Output or Clock) -// Clock is possible only for some pins (bcm 4, 5, 6) +// +// Clock is possible only for some pins (bcm 4, 5, 6, 20, 21) func PinMode(pin Pin, mode Mode) { // Pin fsel register, 0 or 1 depending on bank @@ -310,7 +311,7 @@ func SetFreq(pin Pin, freq int) { const maxUint12 = 4095 divi := uint32(source / freq) - divf := uint32(((source % freq) << 12) / source) + divf := uint32(((source % freq) << 12) / freq) divi &= maxUint12 divf &= maxUint12 From 8208ca5f0d45f657d8f049d456f30589303d7939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Drahoslav=20Bedn=C3=A1=C5=99?= Date: Wed, 15 Nov 2017 17:48:18 +0100 Subject: [PATCH 09/20] fix memMap base attr How could it even work before? --- rpio.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rpio.go b/rpio.go index 30fab9c..56d83ef 100644 --- a/rpio.go +++ b/rpio.go @@ -81,13 +81,12 @@ const ( ) var ( - base int64 gpioBase int64 clkBase int64 ) func init() { - base = getBase() + base := getBase() gpioBase = base + gpioOffset clkBase = base + clkOffset } @@ -383,10 +382,10 @@ func Open() (err error) { return nil } -func memMap(fd uintptr, offset int64) (mem []uint32, mem8 []byte, err error) { +func memMap(fd uintptr, base int64) (mem []uint32, mem8 []byte, err error) { mem8, err = syscall.Mmap( int(fd), - base+clkOffset, + base, memLength, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED, From fd0764b709bdd9cf8d53c932d03ab48d3fbf8de0 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Thu, 16 Nov 2017 01:12:13 +0100 Subject: [PATCH 10/20] Fix bad indexing of clk rigisters --- rpio.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rpio.go b/rpio.go index 56d83ef..f872178 100644 --- a/rpio.go +++ b/rpio.go @@ -315,18 +315,18 @@ func SetFreq(pin Pin, freq int) { divi &= maxUint12 divf &= maxUint12 - clkCtlReg := 0x70 - clkDivReg := 0x74 + clkCtlReg := 28 + clkDivReg := 29 switch pin { case 4, 20, 32, 34: // clk0 clkCtlReg += 0 clkDivReg += 0 case 5, 21, 42, 44: // clk1 - clkCtlReg += 8 - clkDivReg += 8 + clkCtlReg += 2 + clkDivReg += 2 case 6, 43: // clk2 - clkCtlReg += 16 - clkDivReg += 16 + clkCtlReg += 4 + clkDivReg += 4 default: return } From 95bb91625f3b01277deadda85a9fd6012e35cf7e Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Thu, 16 Nov 2017 01:14:01 +0100 Subject: [PATCH 11/20] Use mash filter if it makes sense --- rpio.go | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/rpio.go b/rpio.go index f872178..71915f4 100644 --- a/rpio.go +++ b/rpio.go @@ -306,11 +306,12 @@ func PullMode(pin Pin, pull Pull) { // changing frequency for one pin will change it also for all pins within a group // The groups are: clk0 (4, 20, 32, 34), clk1 (5, 21, 42, 43) and clk2 (6 and 43) func SetFreq(pin Pin, freq int) { - const source = 19200000 // oscilator frequency + // TODO: would be nice to choose best clock source depending on target frequency, oscilator is used for now + const sourceFreq = 19200000 // oscilator frequency const maxUint12 = 4095 - divi := uint32(source / freq) - divf := uint32(((source % freq) << 12) / freq) + divi := uint32(sourceFreq / freq) + divf := uint32(((sourceFreq % freq) << 12) / sourceFreq) divi &= maxUint12 divf &= maxUint12 @@ -331,21 +332,35 @@ func SetFreq(pin Pin, freq int) { return } + mash := uint32(1 << 9) // 1-stage MASH + if divi < 2 || divf == 0 { + mash = 0 + } + memlock.Lock() defer memlock.Unlock() const PASSWORD = 0x5A000000 - const busy = 0x80 - const enab = 0x10 - const src = 0x01 // oscilator + const busy = 1 << 7 + const enab = 1 << 4 + const src = 1 << 0 // oscilator clkMem[clkCtlReg] = PASSWORD | src // stop gpio clock for clkMem[clkCtlReg]&busy != 0 { - } // ... and wait + time.Sleep(time.Microsecond * 10) + } // ... and wait for not busy + clkMem[clkCtlReg] = PASSWORD | mash | src // set mash and source (without enabling clock) clkMem[clkDivReg] = PASSWORD | (divi << 12) | divf // set dividers - clkMem[clkCtlReg] = PASSWORD | enab | src // start clock + // mash and src can not be changed in same step as enab, to prevent lock-up and glitches + time.Sleep(time.Microsecond * 10) // ... so wait for them to take effect + + clkMem[clkCtlReg] = PASSWORD | mash | src | enab // finally start clock + + for clkMem[clkCtlReg]&busy == 0 { + time.Sleep(time.Microsecond * 10) + } // ... and wait for busy, just to be sure } // Open and memory map GPIO memory range from /dev/mem . From ec703e92e8818fad421655463c8e42195ea73067 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Thu, 16 Nov 2017 01:28:34 +0100 Subject: [PATCH 12/20] Use /mem/dev primarily - it is needed for clock settings to work --- rpio.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/rpio.go b/rpio.go index 71915f4..5fc9b58 100644 --- a/rpio.go +++ b/rpio.go @@ -358,9 +358,7 @@ func SetFreq(pin Pin, freq int) { clkMem[clkCtlReg] = PASSWORD | mash | src | enab // finally start clock - for clkMem[clkCtlReg]&busy == 0 { - time.Sleep(time.Microsecond * 10) - } // ... and wait for busy, just to be sure + // NOTE without root permission this changes will simply do nothing successfully } // Open and memory map GPIO memory range from /dev/mem . @@ -368,10 +366,10 @@ func SetFreq(pin Pin, freq int) { func Open() (err error) { var file *os.File - // Open fd for rw mem access; try gpiomem first - file, err = os.OpenFile("/dev/gpiomem", os.O_RDWR|os.O_SYNC, 0) - if os.IsNotExist(err) { // try mem (need root) - file, err = os.OpenFile("/dev/mem", os.O_RDWR|os.O_SYNC, 0) + // Open fd for rw mem access; try dev/mem first (need root) + file, err = os.OpenFile("/dev/mem", os.O_RDWR|os.O_SYNC, 0) + if os.IsPermission(err) { // try gpiomem otherwise (some extra functions like clock setting wont work) + file, err = os.OpenFile("/dev/gpiomem", os.O_RDWR|os.O_SYNC, 0) } if err != nil { return From 7e21224c6976a6f3e618e5ed502bf8f2503f6ee0 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Thu, 16 Nov 2017 01:47:08 +0100 Subject: [PATCH 13/20] Do not change mash while busy --- rpio.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpio.go b/rpio.go index 5fc9b58..c6778ce 100644 --- a/rpio.go +++ b/rpio.go @@ -345,7 +345,7 @@ func SetFreq(pin Pin, freq int) { const enab = 1 << 4 const src = 1 << 0 // oscilator - clkMem[clkCtlReg] = PASSWORD | src // stop gpio clock + clkMem[clkCtlReg] = PASSWORD | (clkMem[clkCtlReg] &^ enab) // stop gpio clock (without changing src or mash) for clkMem[clkCtlReg]&busy != 0 { time.Sleep(time.Microsecond * 10) } // ... and wait for not busy From c0dfd7a7e3b61c05bb392ab9b126e9663f96e6fc Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Thu, 16 Nov 2017 13:52:24 +0100 Subject: [PATCH 14/20] fixup! Use mash filter if it makes sense --- rpio.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpio.go b/rpio.go index c6778ce..4ebea04 100644 --- a/rpio.go +++ b/rpio.go @@ -311,7 +311,7 @@ func SetFreq(pin Pin, freq int) { const maxUint12 = 4095 divi := uint32(sourceFreq / freq) - divf := uint32(((sourceFreq % freq) << 12) / sourceFreq) + divf := uint32(((sourceFreq % freq) << 12) / freq) divi &= maxUint12 divf &= maxUint12 From 4a31f2cfa669b4d06736c5ab92a063d529bb63d2 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Fri, 17 Nov 2017 02:57:42 +0100 Subject: [PATCH 15/20] Add Pwm mode for pins --- rpio.go | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/rpio.go b/rpio.go index 4ebea04..8e6c9dc 100644 --- a/rpio.go +++ b/rpio.go @@ -91,11 +91,12 @@ func init() { clkBase = base + clkOffset } -// Pin mode, a pin can be set in Input or Output mode, or clock +// Pin mode, a pin can be set in Input or Output, Clock or Pwm mode const ( Input Mode = iota Output Clock + Pwm ) // State of pin, High / Low @@ -135,6 +136,11 @@ func (pin Pin) Clock() { PinMode(pin, Clock) } +// Set pin as Pwm +func (pin Pin) Pwm() { + PinMode(pin, Pwm) +} + // Set pin High func (pin Pin) High() { WritePin(pin, High) @@ -150,6 +156,7 @@ func (pin Pin) Toggle() { TogglePin(pin) } +// Set frequency of Clock pin func (pin Pin) Freq(freq int) { SetFreq(pin, freq) } @@ -189,9 +196,10 @@ func (pin Pin) PullOff() { PullMode(pin, PullOff) } -// PinMode sets the mode (direction) of a given pin (Input, Output or Clock) +// PinMode sets the mode (direction) of a given pin (Input, Output, Clock or Pwm) // -// Clock is possible only for some pins (bcm 4, 5, 6, 20, 21) +// Clock is possible only for pins 4, 5, 6, 20, 21 +// Pwm is possible only for pins 12, 13, 18, 19 func PinMode(pin Pin, mode Mode) { // Pin fsel register, 0 or 1 depending on bank @@ -199,6 +207,9 @@ func PinMode(pin Pin, mode Mode) { shift := (uint8(pin) % 10) * 3 f := uint32(0) + const alt0 = 4 // 100 + const alt5 = 2 // 010 + switch mode { case Input: f = 0 // 000 @@ -207,9 +218,18 @@ func PinMode(pin Pin, mode Mode) { case Clock: switch pin { case 4, 5, 6, 32, 34, 42, 43, 44: - f = 4 // 100 - alt0 + f = alt0 case 20, 21: - f = 2 // 010 - alt5 + f = alt5 + default: + return + } + case Pwm: + switch pin { + case 12, 13, 40, 41, 45: + f = alt0 + case 18, 19: + f = alt5 default: return } From cc678981b0d0d5a200a88323980922390341c177 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Fri, 17 Nov 2017 09:37:18 +0100 Subject: [PATCH 16/20] Mmap pwm memmory --- rpio.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/rpio.go b/rpio.go index 8e6c9dc..f10989a 100644 --- a/rpio.go +++ b/rpio.go @@ -76,6 +76,7 @@ const ( bcm2835Base = 0x20000000 gpioOffset = 0x200000 clkOffset = 0x101000 + pwmOffset = 0x20C000 memLength = 4096 ) @@ -83,12 +84,14 @@ const ( var ( gpioBase int64 clkBase int64 + pwmBase int64 ) func init() { base := getBase() gpioBase = base + gpioOffset clkBase = base + clkOffset + pwmBase = base + pwmOffset } // Pin mode, a pin can be set in Input or Output, Clock or Pwm mode @@ -117,8 +120,10 @@ var ( memlock sync.Mutex gpioMem []uint32 clkMem []uint32 + pwmMem []uint32 gpioMem8 []uint8 clkMem8 []uint8 + pwmMem8 []uint8 ) // Set pin as Input @@ -388,7 +393,7 @@ func Open() (err error) { // Open fd for rw mem access; try dev/mem first (need root) file, err = os.OpenFile("/dev/mem", os.O_RDWR|os.O_SYNC, 0) - if os.IsPermission(err) { // try gpiomem otherwise (some extra functions like clock setting wont work) + if os.IsPermission(err) { // try gpiomem otherwise (some extra functions like clock and pwm setting wont work) file, err = os.OpenFile("/dev/gpiomem", os.O_RDWR|os.O_SYNC, 0) } if err != nil { @@ -412,6 +417,11 @@ func Open() (err error) { return } + pwmMem, pwmMem8, err = memMap(file.Fd(), clkBase) + if err != nil { + return + } + return nil } From 7445bb69a5c243ec3daa577f79ee951f0ab47227 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Sun, 19 Nov 2017 17:23:48 +0100 Subject: [PATCH 17/20] Implement pwm methods --- rpio.go | 92 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 12 deletions(-) diff --git a/rpio.go b/rpio.go index f10989a..04314d2 100644 --- a/rpio.go +++ b/rpio.go @@ -4,7 +4,7 @@ Package rpio provides GPIO access on the Raspberry PI without any need for external c libraries (ex: WiringPI or BCM2835). Supports simple operations such as: -- Pin mode/direction (input/output/clock) +- Pin mode/direction (input/output/clock/pwm) - Pin write (high/low) - Pin read (high/low) - Pull up/down/off @@ -166,6 +166,11 @@ func (pin Pin) Freq(freq int) { SetFreq(pin, freq) } +// Set duty cycle for pwm pin +func (pin Pin) DutyCycle(dutyLen, cycleLen uint32) { + SetDutyCycle(pin, dutyLen, cycleLen) +} + // Set pin Mode func (pin Pin) Mode(mode Mode) { PinMode(pin, mode) @@ -295,9 +300,9 @@ func TogglePin(pin Pin) { func PullMode(pin Pin, pull Pull) { // Pull up/down/off register has offset 38 / 39, pull is 37 - pullClkReg := uint8(pin)/32 + 38 + pullClkReg := pin/32 + 38 pullReg := 37 - shift := (uint8(pin) % 32) + shift := pin % 32 memlock.Lock() defer memlock.Unlock() @@ -322,37 +327,44 @@ func PullMode(pin Pin, pull Pull) { } -// Set clock speed for given pin +// Set clock speed for given pin in Clock or Pwm mode // // freq should be in range 4688Hz - 19.2MHz to prevent unexpected behavior -// (for smaller frequencies implement custom software clock using output pin and sleep) +// (for smaller frequencies use Pwm pin with large cycle range or implement custom software clock using output pin and sleep) // // Note that some pins share the same clock source, it means that // changing frequency for one pin will change it also for all pins within a group // The groups are: clk0 (4, 20, 32, 34), clk1 (5, 21, 42, 43) and clk2 (6 and 43) +// Also all pwm pins (12, 13, 18, 19, 40, 41, 45) share same source clock, +// but final output frequency of pwm chanel can be adjusted individually with setDutyCycle func SetFreq(pin Pin, freq int) { // TODO: would be nice to choose best clock source depending on target frequency, oscilator is used for now const sourceFreq = 19200000 // oscilator frequency - const maxUint12 = 4095 + const divMask = 4095 // divi and divf have 12 bits each divi := uint32(sourceFreq / freq) divf := uint32(((sourceFreq % freq) << 12) / freq) - divi &= maxUint12 - divf &= maxUint12 + divi &= divMask + divf &= divMask clkCtlReg := 28 - clkDivReg := 29 + clkDivReg := 28 switch pin { case 4, 20, 32, 34: // clk0 clkCtlReg += 0 - clkDivReg += 0 + clkDivReg += 1 case 5, 21, 42, 44: // clk1 clkCtlReg += 2 - clkDivReg += 2 + clkDivReg += 3 case 6, 43: // clk2 clkCtlReg += 4 - clkDivReg += 4 + clkDivReg += 5 + case 12, 13, 40, 41, 45, 18, 19: // pwm_clk - shared clk for both pwm chanels + clkCtlReg += 12 + clkDivReg += 13 + StopPwm() + defer StartPwm() default: return } @@ -386,6 +398,62 @@ func SetFreq(pin Pin, freq int) { // NOTE without root permission this changes will simply do nothing successfully } +// Set cycle length (range) and duty length (data) for pwm in M/S mode +// +// |<- duty ->| +// __________ +// _/ \___________/ +// |<------ cycle ------->| +// +// Note that some pins share common pwm chanel, +// so calling this function will set same duty cycle for all pins belonig to chanel +// Its chanel pwm0 for pins 12, 18, 40, and pwm1 for pins 13, 19, 41, 45 +func SetDutyCycle(pin Pin, dutyLen, cycleLen uint32) { + const pwmCtlReg = 0 + var ( + pwmDatReg uint + pwmRngReg uint + shift uint // offset inside ctlReg + ) + + switch pin { + case 12, 18, 40: // chanel pwm0 + pwmDatReg = 4 + pwmRngReg = 5 + shift = 0 + case 13, 19, 41, 45: // chanel pwm1 + pwmDatReg = 8 + pwmDatReg = 9 + shift = 8 + default: + return + } + + const ctlMask = 255 // ctl setting has 8 bits for each chanel + const msen = 1 << 7 // use M/S transition instead of pwm algorithm + + // reset settings + pwmMem[pwmCtlReg] = pwmMem[pwmCtlReg]&^(ctlMask< Date: Mon, 20 Nov 2017 09:29:43 +0100 Subject: [PATCH 18/20] Fix pwmMem and some pwm Regs --- rpio.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/rpio.go b/rpio.go index 04314d2..388ff95 100644 --- a/rpio.go +++ b/rpio.go @@ -363,7 +363,7 @@ func SetFreq(pin Pin, freq int) { case 12, 13, 40, 41, 45, 18, 19: // pwm_clk - shared clk for both pwm chanels clkCtlReg += 12 clkDivReg += 13 - StopPwm() + StopPwm() // pwm clk busy wont go down without stopping pwm first defer StartPwm() default: return @@ -418,11 +418,11 @@ func SetDutyCycle(pin Pin, dutyLen, cycleLen uint32) { switch pin { case 12, 18, 40: // chanel pwm0 - pwmDatReg = 4 - pwmRngReg = 5 + pwmRngReg = 4 + pwmDatReg = 5 shift = 0 case 13, 19, 41, 45: // chanel pwm1 - pwmDatReg = 8 + pwmRngReg = 8 pwmDatReg = 9 shift = 8 default: @@ -430,14 +430,17 @@ func SetDutyCycle(pin Pin, dutyLen, cycleLen uint32) { } const ctlMask = 255 // ctl setting has 8 bits for each chanel + const pwen = 1 << 0 // enable pwm const msen = 1 << 7 // use M/S transition instead of pwm algorithm // reset settings - pwmMem[pwmCtlReg] = pwmMem[pwmCtlReg]&^(ctlMask< Date: Mon, 20 Nov 2017 10:22:14 +0100 Subject: [PATCH 19/20] Improve doc --- rpio.go | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/rpio.go b/rpio.go index 388ff95..98016d6 100644 --- a/rpio.go +++ b/rpio.go @@ -161,12 +161,12 @@ func (pin Pin) Toggle() { TogglePin(pin) } -// Set frequency of Clock pin +// Set frequency of Clock or Pwm pin (see doc of SetFreq) func (pin Pin) Freq(freq int) { SetFreq(pin, freq) } -// Set duty cycle for pwm pin +// Set duty cycle for Pwm pin (see doc of SetDutyCycle) func (pin Pin) DutyCycle(dutyLen, cycleLen uint32) { SetDutyCycle(pin, dutyLen, cycleLen) } @@ -329,14 +329,14 @@ func PullMode(pin Pin, pull Pull) { // Set clock speed for given pin in Clock or Pwm mode // -// freq should be in range 4688Hz - 19.2MHz to prevent unexpected behavior -// (for smaller frequencies use Pwm pin with large cycle range or implement custom software clock using output pin and sleep) +// Param freq should be in range 4688Hz - 19.2MHz to prevent unexpected behavior, +// hovewer output frequency of Pwm pins can be further adjusted with setDutyCycle. +// (Thus for smaller frequencies use Pwm pin with large cycle range or implement custom software clock using output pin and sleep.) // // Note that some pins share the same clock source, it means that // changing frequency for one pin will change it also for all pins within a group -// The groups are: clk0 (4, 20, 32, 34), clk1 (5, 21, 42, 43) and clk2 (6 and 43) -// Also all pwm pins (12, 13, 18, 19, 40, 41, 45) share same source clock, -// but final output frequency of pwm chanel can be adjusted individually with setDutyCycle +// The groups are: clk0 (4, 20, 32, 34), clk1 (5, 21, 42, 43) and clk2 (6 and 43). +// Also all pwm pins (12, 13, 18, 19, 40, 41, 45) share same source clock. func SetFreq(pin Pin, freq int) { // TODO: would be nice to choose best clock source depending on target frequency, oscilator is used for now const sourceFreq = 19200000 // oscilator frequency @@ -360,7 +360,7 @@ func SetFreq(pin Pin, freq int) { case 6, 43: // clk2 clkCtlReg += 4 clkDivReg += 5 - case 12, 13, 40, 41, 45, 18, 19: // pwm_clk - shared clk for both pwm chanels + case 12, 13, 40, 41, 45, 18, 19: // pwm_clk - shared clk for both pwm channels clkCtlReg += 12 clkDivReg += 13 StopPwm() // pwm clk busy wont go down without stopping pwm first @@ -400,14 +400,21 @@ func SetFreq(pin Pin, freq int) { // Set cycle length (range) and duty length (data) for pwm in M/S mode // -// |<- duty ->| -// __________ -// _/ \___________/ -// |<------ cycle ------->| +// |<- duty ->| +// __________ +// _/ \_____________/ +// |<------- cycle -------->| // -// Note that some pins share common pwm chanel, -// so calling this function will set same duty cycle for all pins belonig to chanel -// Its chanel pwm0 for pins 12, 18, 40, and pwm1 for pins 13, 19, 41, 45 +// Output frequency is computed as pwm clock frequency divided by cycle length +// So, to set Pwm pin to freqency 38kHz with duty cycle 1/4 use this combination: +// +// pin.Pwm() +// pin.DutyCycle(1, 4) +// pin.Freq(38000*4) +// +// Note that some pins share common pwm channel, +// so calling this function will set same duty cycle for all pins belongig to channel. +// It is channel 1 (pwm0) for pins 12, 18, 40, and channel 2 (pwm1) for pins 13, 19, 41, 45. func SetDutyCycle(pin Pin, dutyLen, cycleLen uint32) { const pwmCtlReg = 0 var ( @@ -417,11 +424,11 @@ func SetDutyCycle(pin Pin, dutyLen, cycleLen uint32) { ) switch pin { - case 12, 18, 40: // chanel pwm0 + case 12, 18, 40: // channel pwm0 pwmRngReg = 4 pwmDatReg = 5 shift = 0 - case 13, 19, 41, 45: // chanel pwm1 + case 13, 19, 41, 45: // channel pwm1 pwmRngReg = 8 pwmDatReg = 9 shift = 8 @@ -429,7 +436,7 @@ func SetDutyCycle(pin Pin, dutyLen, cycleLen uint32) { return } - const ctlMask = 255 // ctl setting has 8 bits for each chanel + const ctlMask = 255 // ctl setting has 8 bits for each channel const pwen = 1 << 0 // enable pwm const msen = 1 << 7 // use M/S transition instead of pwm algorithm @@ -443,14 +450,14 @@ func SetDutyCycle(pin Pin, dutyLen, cycleLen uint32) { // NOTE without root permission this changes will simply do nothing successfully } -// Stop pwm for both chanels +// Stop pwm for both channels func StopPwm() { const pwmCtlReg = 0 const pwen = 1 pwmMem[pwmCtlReg] = pwmMem[pwmCtlReg] &^ (pwen<<8 | pwen) } -// Start pwm for both chanels +// Start pwm for both channels func StartPwm() { const pwmCtlReg = 0 const pwen = 1 From ec49196c8ca3181812b5e630af0f2b11228b1668 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Mon, 20 Nov 2017 12:14:52 +0100 Subject: [PATCH 20/20] Update doc overview --- rpio.go | 99 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/rpio.go b/rpio.go index 98016d6..3d3ce71 100644 --- a/rpio.go +++ b/rpio.go @@ -1,13 +1,15 @@ /* - Package rpio provides GPIO access on the Raspberry PI without any need -for external c libraries (ex: WiringPI or BCM2835). +for external c libraries (eg. WiringPi or BCM2835). Supports simple operations such as: -- Pin mode/direction (input/output/clock/pwm) -- Pin write (high/low) -- Pin read (high/low) -- Pull up/down/off + - Pin mode/direction (input/output/clock/pwm) + - Pin write (high/low) + - Pin read (high/low) + - Pull up/down/off +And clock/pwm related oparations: + - Set Clock frequency + - Set Duty cycle Example of use: @@ -23,36 +25,40 @@ Example of use: } The library use the raw BCM2835 pinouts, not the ports as they are mapped -on the output pins for the raspberry pi +on the output pins for the raspberry pi, and not the wiringPi convention. - Rev 1 Raspberry Pi -+------+------+--------+ -| GPIO | Phys | Name | -+------+------+--------+ -| 0 | 3 | SDA | -| 1 | 5 | SCL | -| 4 | 7 | GPIO 7 | -| 7 | 26 | CE1 | -| 8 | 24 | CE0 | -| 9 | 21 | MISO | -| 10 | 19 | MOSI | -| 11 | 23 | SCLK | -| 14 | 8 | TxD | -| 15 | 10 | RxD | -| 17 | 11 | GPIO 0 | -| 18 | 12 | GPIO 1 | -| 21 | 13 | GPIO 2 | -| 22 | 15 | GPIO 3 | -| 23 | 16 | GPIO 4 | -| 24 | 18 | GPIO 5 | -| 25 | 22 | GPIO 6 | -+------+------+--------+ + Rev 2 and 3 Raspberry Pi Rev 1 Raspberry Pi (legacy) + +-----+---------+----------+---------+-----+ +-----+--------+----------+--------+-----+ + | BCM | Name | Physical | Name | BCM | | BCM | Name | Physical | Name | BCM | + +-----+---------+----++----+---------+-----+ +-----+--------+----++----+--------+-----+ + | | 3.3v | 1 || 2 | 5v | | | | 3.3v | 1 || 2 | 5v | | + | 2 | SDA 1 | 3 || 4 | 5v | | | 0 | SDA | 3 || 4 | 5v | | + | 3 | SCL 1 | 5 || 6 | 0v | | | 1 | SCL | 5 || 6 | 0v | | + | 4 | GPIO 7 | 7 || 8 | TxD | 14 | | 4 | GPIO 7 | 7 || 8 | TxD | 14 | + | | 0v | 9 || 10 | RxD | 15 | | | 0v | 9 || 10 | RxD | 15 | + | 17 | GPIO 0 | 11 || 12 | GPIO 1 | 18 | | 17 | GPIO 0 | 11 || 12 | GPIO 1 | 18 | + | 27 | GPIO 2 | 13 || 14 | 0v | | | 21 | GPIO 2 | 13 || 14 | 0v | | + | 22 | GPIO 3 | 15 || 16 | GPIO 4 | 23 | | 22 | GPIO 3 | 15 || 16 | GPIO 4 | 23 | + | | 3.3v | 17 || 18 | GPIO 5 | 24 | | | 3.3v | 17 || 18 | GPIO 5 | 24 | + | 10 | MOSI | 19 || 20 | 0v | | | 10 | MOSI | 19 || 20 | 0v | | + | 9 | MISO | 21 || 22 | GPIO 6 | 25 | | 9 | MISO | 21 || 22 | GPIO 6 | 25 | + | 11 | SCLK | 23 || 24 | CE0 | 8 | | 11 | SCLK | 23 || 24 | CE0 | 8 | + | | 0v | 25 || 26 | CE1 | 7 | | | 0v | 25 || 26 | CE1 | 7 | + | 0 | SDA 0 | 27 || 28 | SCL 0 | 1 | +-----+--------+----++----+--------+-----+ + | 5 | GPIO 21 | 29 || 30 | 0v | | + | 6 | GPIO 22 | 31 || 32 | GPIO 26 | 12 | + | 13 | GPIO 23 | 33 || 34 | 0v | | + | 19 | GPIO 24 | 35 || 36 | GPIO 27 | 16 | + | 26 | GPIO 25 | 37 || 38 | GPIO 28 | 20 | + | | 0v | 39 || 40 | GPIO 29 | 21 | + +-----+---------+----++----+---------+-----+ See the spec for full details of the BCM2835 controller: -http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf + +https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/BCM2835-ARM-Peripherals.pdf +and https://elinux.org/BCM2835_datasheet_errata - for errors in that spec */ - package rpio import ( @@ -208,8 +214,8 @@ func (pin Pin) PullOff() { // PinMode sets the mode (direction) of a given pin (Input, Output, Clock or Pwm) // -// Clock is possible only for pins 4, 5, 6, 20, 21 -// Pwm is possible only for pins 12, 13, 18, 19 +// Clock is possible only for pins 4, 5, 6, 20, 21. +// Pwm is possible only for pins 12, 13, 18, 19. func PinMode(pin Pin, mode Mode) { // Pin fsel register, 0 or 1 depending on bank @@ -330,13 +336,16 @@ func PullMode(pin Pin, pull Pull) { // Set clock speed for given pin in Clock or Pwm mode // // Param freq should be in range 4688Hz - 19.2MHz to prevent unexpected behavior, -// hovewer output frequency of Pwm pins can be further adjusted with setDutyCycle. -// (Thus for smaller frequencies use Pwm pin with large cycle range or implement custom software clock using output pin and sleep.) +// however output frequency of Pwm pins can be further adjusted with SetDutyCycle. +// So for smaller frequencies use Pwm pin with large cycle range. (Or implement custom software clock using output pin and sleep.) // // Note that some pins share the same clock source, it means that -// changing frequency for one pin will change it also for all pins within a group -// The groups are: clk0 (4, 20, 32, 34), clk1 (5, 21, 42, 43) and clk2 (6 and 43). -// Also all pwm pins (12, 13, 18, 19, 40, 41, 45) share same source clock. +// changing frequency for one pin will change it also for all pins within a group. +// The groups are: +// gp_clk0: pins 4, 20, 32, 34 +// gp_clk1: pins 5, 21, 42, 43 +// gp_clk2: pins 6 and 43 +// pwm_clk: pins 12, 13, 18, 19, 40, 41, 45 func SetFreq(pin Pin, freq int) { // TODO: would be nice to choose best clock source depending on target frequency, oscilator is used for now const sourceFreq = 19200000 // oscilator frequency @@ -398,23 +407,25 @@ func SetFreq(pin Pin, freq int) { // NOTE without root permission this changes will simply do nothing successfully } -// Set cycle length (range) and duty length (data) for pwm in M/S mode +// Set cycle length (range) and duty length (data) for Pwm pin in M/S mode // // |<- duty ->| // __________ // _/ \_____________/ // |<------- cycle -------->| // -// Output frequency is computed as pwm clock frequency divided by cycle length -// So, to set Pwm pin to freqency 38kHz with duty cycle 1/4 use this combination: +// Output frequency is computed as pwm clock frequency divided by cycle length. +// So, to set Pwm pin to freqency 38kHz with duty cycle 1/4, use this combination: // -// pin.Pwm() +// pin.Pwm() // pin.DutyCycle(1, 4) // pin.Freq(38000*4) // // Note that some pins share common pwm channel, -// so calling this function will set same duty cycle for all pins belongig to channel. -// It is channel 1 (pwm0) for pins 12, 18, 40, and channel 2 (pwm1) for pins 13, 19, 41, 45. +// so calling this function will set same duty cycle for all pins belonging to channel. +// The channels are: +// channel 1 (pwm0) for pins 12, 18, 40 +// channel 2 (pwm1) for pins 13, 19, 41, 45. func SetDutyCycle(pin Pin, dutyLen, cycleLen uint32) { const pwmCtlReg = 0 var (