From 631591fc014b4efa91f9a2dd975eebfa26ee4697 Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Sat, 1 Dec 2018 05:25:08 +0100 Subject: [PATCH 1/2] Add IRQ enable/disable --- rpio.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/rpio.go b/rpio.go index f4aeef3..7e8f28d 100644 --- a/rpio.go +++ b/rpio.go @@ -90,6 +90,7 @@ const ( clkOffset = 0x101000 pwmOffset = 0x20C000 spiOffset = 0x204000 + intrOffset = 0x00B000 memLength = 4096 ) @@ -99,6 +100,9 @@ var ( clkBase int64 pwmBase int64 spiBase int64 + intrBase int64 + + irqsBackup uint64 ) func init() { @@ -107,6 +111,7 @@ func init() { clkBase = base + clkOffset pwmBase = base + pwmOffset spiBase = base + spiOffset + intrBase = base + intrOffset } // Pin mode, a pin can be set in Input or Output, Clock or Pwm mode @@ -146,10 +151,12 @@ var ( clkMem []uint32 pwmMem []uint32 spiMem []uint32 + intrMem []uint32 gpioMem8 []uint8 clkMem8 []uint8 pwmMem8 []uint8 spiMem8 []uint8 + intrMem8 []uint8 ) // Set pin as Input @@ -584,6 +591,32 @@ func StartPwm() { pwmMem[pwmCtlReg] |= pwen<<8 | pwen } +// Enables given IRQs (by setting bit to 1 at intended position). +// See 'ARM peripherals interrupts table' in pheripherals datasheet. +// WARNING: you can corrupt your system, only use this if you know what you are doing. +func EnableIRQs(irqs uint64) { + const irqEnable1 = 0x210 / 4 + const irqEnable2 = 0x214 / 4 + intrMem[irqEnable1] = uint32(irqs) // IRQ 0..31 + intrMem[irqEnable2] = uint32(irqs >> 32) // IRQ 32..63 +} + +// Disables given IRQs (by setting bit to 1 at intended position). +// See 'ARM peripherals interrupts table' in pheripherals datasheet. +// WARNING: you can corrupt your system, only use this if you know what you are doing. +func DisableIRQs(irqs uint64) { + const irqDisable1 = 0x21C / 4 + const irqDisable2 = 0x220 / 4 + intrMem[irqDisable1] = uint32(irqs) // IRQ 0..31 + intrMem[irqDisable2] = uint32(irqs >> 32) // IRQ 32..63 +} + +func backupIRQs() { + const irqEnable1 = 0x210 / 4 + const irqEnable2 = 0x214 / 4 + irqsBackup = uint64(intrMem[irqEnable2])<<32 | uint64(intrMem[irqEnable1]) +} + // Open and memory map GPIO memory range from /dev/mem . // Some reflection magic is used to convert it to a unsafe []uint32 pointer func Open() (err error) { @@ -627,6 +660,14 @@ func Open() (err error) { return } + // Memory map interruption registers to slice + intrMem, intrMem8, err = memMap(file.Fd(), intrBase) + if err != nil { + return + } + + backupIRQs() // back up enabled IRQs, to restore it later + return nil } @@ -653,18 +694,12 @@ func memMap(fd uintptr, base int64) (mem []uint32, mem8 []byte, err error) { func Close() error { memlock.Lock() defer memlock.Unlock() - if err := syscall.Munmap(gpioMem8); err != nil { - return err - } - if err := syscall.Munmap(clkMem8); err != nil { - return err - } - if err := syscall.Munmap(pwmMem8); err != nil { - return err - } - if err := syscall.Munmap(spiMem8); err != nil { - return err + for _, mem8 := range [][]uint8{gpioMem8, clkMem8, pwmMem8, spiMem8, intrMem8} { + if err := syscall.Munmap(mem8); err != nil { + return err + } } + EnableIRQs(irqsBackup) // Return IRQs to state where it was before - just to be nice return nil } From 84326092ab1b1d64cfac206cefcc9eeff380069a Mon Sep 17 00:00:00 2001 From: Drahoslav Date: Sat, 1 Dec 2018 05:25:37 +0100 Subject: [PATCH 2/2] Disable gpio IRQ on Detect edge --- rpio.go | 13 +++++++++++-- rpio_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/rpio.go b/rpio.go index 7e8f28d..cd1afb1 100644 --- a/rpio.go +++ b/rpio.go @@ -372,12 +372,20 @@ func TogglePin(pin Pin) { // // Note that using this function might conflict with the same functionality of other gpio library. // -// It also clears previously detected event of this pin if any. +// It also clears previously detected event of this pin if there was any. // // Note that call with RiseEdge will disable previously set FallEdge detection and vice versa. // You have to call with AnyEdge, to enable detection for both edges. // To disable previously enabled detection call it with NoEdge. +// +// WARNING: this might make your Pi unresponsive, if this happens, you should either run the code as root, +// or add `dtoverlay=gpio-no-irq` to `/boot/config.txt` and restart your pi, func DetectEdge(pin Pin, edge Edge) { + if edge != NoEdge { + // disable GPIO event interruption to prevent freezing in some cases + DisableIRQs(1<<49 | 1<<52) // gpio_int[0] and gpio_int[3] + } + p := uint8(pin) // Rising edge detect enable register (19/20 depending on bank) @@ -692,6 +700,8 @@ func memMap(fd uintptr, base int64) (mem []uint32, mem8 []byte, err error) { // Close unmaps GPIO memory func Close() error { + EnableIRQs(irqsBackup) // Return IRQs to state where it was before - just to be nice + memlock.Lock() defer memlock.Unlock() for _, mem8 := range [][]uint8{gpioMem8, clkMem8, pwmMem8, spiMem8, intrMem8} { @@ -699,7 +709,6 @@ func Close() error { return err } } - EnableIRQs(irqsBackup) // Return IRQs to state where it was before - just to be nice return nil } diff --git a/rpio_test.go b/rpio_test.go index d534f5f..9797a9d 100644 --- a/rpio_test.go +++ b/rpio_test.go @@ -1,6 +1,7 @@ package rpio import ( + "fmt" "os" "testing" "time" @@ -15,6 +16,21 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } +func TestInterrupt(t *testing.T) { + logIrqRegs(t) + EnableIRQs(1 << 49) + EnableIRQs(1 << 50) + EnableIRQs(1 << 51) + EnableIRQs(1 << 52) + logIrqRegs(t) + DisableIRQs(1 << 49) + DisableIRQs(1 << 50) + DisableIRQs(1 << 51) + DisableIRQs(1 << 52) + logIrqRegs(t) + EnableIRQs(irqsBackup) +} + func TestEvent(t *testing.T) { src := Pin(3) src.Mode(Output) @@ -118,6 +134,33 @@ func TestEvent(t *testing.T) { }) + // If this frā½eezes your pi, + // add `dtoverlay=gpio-no-irq` to `/boot/config.txt` and restart your pi, + // or run as root. + t.Run("multiple edges", func(t *testing.T) { + EnableIRQs(15 << 49) // all 4 gpio_int[] + logIrqRegs(t) + src.High() + pin.Detect(FallEdge) + + logIrqRegs(t) + + for i := 0; i < 10000; i++ { + time.Sleep(time.Millisecond) + src.High() + time.Sleep(time.Millisecond) + src.Low() + } + logIrqRegs(t) + if !pin.EdgeDetected() { + t.Errorf("Edge not detected") + } + logIrqRegs(t) + pin.Detect(NoEdge) + logIrqRegs(t) + EnableIRQs(irqsBackup) + }) + } func BenchmarkGpio(b *testing.B) { @@ -191,3 +234,12 @@ func BenchmarkGpio(b *testing.B) { }) } + +func logIrqRegs(t *testing.T) { + fmt.Printf("PENDING(% X) FIQ(% X) ENAB(% X) DISAB(% X)\n", + intrMem8[0x200:0x20C], + intrMem8[0x20C:0x210], + intrMem8[0x210:0x21C], + intrMem8[0x21C:0x228], + ) +}