First implementation
This commit is contained in:
7
vendor/periph.io/x/periph/host/bcm283x/bcm283x_arm.go
generated
vendored
Normal file
7
vendor/periph.io/x/periph/host/bcm283x/bcm283x_arm.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package bcm283x
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/periph/host/bcm283x/bcm283x_arm64.go
generated
vendored
Normal file
9
vendor/periph.io/x/periph/host/bcm283x/bcm283x_arm64.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build arm64
|
||||
|
||||
package bcm283x
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/periph/host/bcm283x/bcm283x_other.go
generated
vendored
Normal file
9
vendor/periph.io/x/periph/host/bcm283x/bcm283x_other.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm,!arm64
|
||||
|
||||
package bcm283x
|
||||
|
||||
const isArm = false
|
330
vendor/periph.io/x/periph/host/bcm283x/clock.go
generated
vendored
Normal file
330
vendor/periph.io/x/periph/host/bcm283x/clock.go
generated
vendored
Normal file
@ -0,0 +1,330 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package bcm283x
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"periph.io/x/periph/conn/physic"
|
||||
)
|
||||
|
||||
// errClockRegister is returned in a situation where the clock memory is not
|
||||
// working as expected. It is mocked in tests.
|
||||
var errClockRegister = errors.New("can't write to clock divisor CPU register")
|
||||
|
||||
// Clock sources frequency in hertz.
|
||||
const (
|
||||
clk19dot2MHz = 19200 * physic.KiloHertz
|
||||
clk500MHz = 500 * physic.MegaHertz
|
||||
)
|
||||
|
||||
const (
|
||||
// 31:24 password
|
||||
clockPasswdCtl clockCtl = 0x5A << 24 // PASSWD
|
||||
// 23:11 reserved
|
||||
clockMashMask clockCtl = 3 << 9 // MASH
|
||||
clockMash0 clockCtl = 0 << 9 // src_freq / divI (ignores divF)
|
||||
clockMash1 clockCtl = 1 << 9
|
||||
clockMash2 clockCtl = 2 << 9
|
||||
clockMash3 clockCtl = 3 << 9 // will cause higher spread
|
||||
clockFlip clockCtl = 1 << 8 // FLIP
|
||||
clockBusy clockCtl = 1 << 7 // BUSY
|
||||
// 6 reserved
|
||||
clockKill clockCtl = 1 << 5 // KILL
|
||||
clockEnable clockCtl = 1 << 4 // ENAB
|
||||
clockSrcMask clockCtl = 0xF << 0 // SRC
|
||||
clockSrcGND clockCtl = 0 // 0Hz
|
||||
clockSrc19dot2MHz clockCtl = 1 // 19.2MHz
|
||||
clockSrcTestDebug0 clockCtl = 2 // 0Hz
|
||||
clockSrcTestDebug1 clockCtl = 3 // 0Hz
|
||||
clockSrcPLLA clockCtl = 4 // 0Hz
|
||||
clockSrcPLLC clockCtl = 5 // 1000MHz (changes with overclock settings)
|
||||
clockSrcPLLD clockCtl = 6 // 500MHz
|
||||
clockSrcHDMI clockCtl = 7 // 216MHz; may be disabled
|
||||
// 8-15 == GND.
|
||||
)
|
||||
|
||||
// clockCtl controls the clock properties.
|
||||
//
|
||||
// It must not be changed while busy is set or a glitch may occur.
|
||||
//
|
||||
// Page 107
|
||||
type clockCtl uint32
|
||||
|
||||
func (c clockCtl) String() string {
|
||||
var out []string
|
||||
if c&0xFF000000 == clockPasswdCtl {
|
||||
c &^= 0xFF000000
|
||||
out = append(out, "PWD")
|
||||
}
|
||||
switch c & clockMashMask {
|
||||
case clockMash1:
|
||||
out = append(out, "Mash1")
|
||||
case clockMash2:
|
||||
out = append(out, "Mash2")
|
||||
case clockMash3:
|
||||
out = append(out, "Mash3")
|
||||
default:
|
||||
}
|
||||
c &^= clockMashMask
|
||||
if c&clockFlip != 0 {
|
||||
out = append(out, "Flip")
|
||||
c &^= clockFlip
|
||||
}
|
||||
if c&clockBusy != 0 {
|
||||
out = append(out, "Busy")
|
||||
c &^= clockBusy
|
||||
}
|
||||
if c&clockKill != 0 {
|
||||
out = append(out, "Kill")
|
||||
c &^= clockKill
|
||||
}
|
||||
if c&clockEnable != 0 {
|
||||
out = append(out, "Enable")
|
||||
c &^= clockEnable
|
||||
}
|
||||
switch x := c & clockSrcMask; x {
|
||||
case clockSrcGND:
|
||||
out = append(out, "GND(0Hz)")
|
||||
case clockSrc19dot2MHz:
|
||||
out = append(out, "19.2MHz")
|
||||
case clockSrcTestDebug0:
|
||||
out = append(out, "Debug0(0Hz)")
|
||||
case clockSrcTestDebug1:
|
||||
out = append(out, "Debug1(0Hz)")
|
||||
case clockSrcPLLA:
|
||||
out = append(out, "PLLA(0Hz)")
|
||||
case clockSrcPLLC:
|
||||
out = append(out, "PLLD(1000MHz)")
|
||||
case clockSrcPLLD:
|
||||
out = append(out, "PLLD(500MHz)")
|
||||
case clockSrcHDMI:
|
||||
out = append(out, "HDMI(216MHz)")
|
||||
default:
|
||||
out = append(out, fmt.Sprintf("GND(%d)", x))
|
||||
}
|
||||
c &^= clockSrcMask
|
||||
if c != 0 {
|
||||
out = append(out, fmt.Sprintf("clockCtl(0x%0x)", uint32(c)))
|
||||
}
|
||||
return strings.Join(out, "|")
|
||||
}
|
||||
|
||||
const (
|
||||
// 31:24 password
|
||||
clockPasswdDiv clockDiv = 0x5A << 24 // PASSWD
|
||||
// Integer part of the divisor
|
||||
clockDiviShift = 12
|
||||
clockDiviMax = (1 << 12) - 1
|
||||
clockDiviMask clockDiv = clockDiviMax << clockDiviShift // DIVI
|
||||
// Fractional part of the divisor
|
||||
clockDivfMask clockDiv = (1 << 12) - 1 // DIVF
|
||||
)
|
||||
|
||||
// clockDiv is a 12.12 fixed point value.
|
||||
//
|
||||
// The fractional part generates a significant amount of noise so it is
|
||||
// preferable to not use it.
|
||||
//
|
||||
// Page 108
|
||||
type clockDiv uint32
|
||||
|
||||
func (c clockDiv) String() string {
|
||||
i := (c & clockDiviMask) >> clockDiviShift
|
||||
c &^= clockDiviMask
|
||||
if c == 0 {
|
||||
return fmt.Sprintf("%d.0", i)
|
||||
}
|
||||
return fmt.Sprintf("%d.(%d/%d)", i, c, clockDiviMax)
|
||||
}
|
||||
|
||||
// clock is a pair of clockCtl / clockDiv.
|
||||
//
|
||||
// It can be set to one of the sources: clockSrc19dot2MHz(19.2MHz) and
|
||||
// clockSrcPLLD(500Mhz), then divided to a value to get the resulting clock.
|
||||
// Per spec the resulting frequency should be under 25Mhz.
|
||||
type clock struct {
|
||||
ctl clockCtl
|
||||
div clockDiv
|
||||
}
|
||||
|
||||
// findDivisorExact finds the clock divisor and wait cycles to reduce src to
|
||||
// desired hz.
|
||||
//
|
||||
// The clock divisor is capped to clockDiviMax.
|
||||
//
|
||||
// Returns clock divisor, wait cycles. Returns 0, 0 if no exact match is found.
|
||||
// Favorizes high clock divisor value over high clock wait cycles. This means
|
||||
// that the function is slower than it could be, but results in more stable
|
||||
// clock.
|
||||
func findDivisorExact(src, desired physic.Frequency, maxWaitCycles uint32) (uint32, uint32) {
|
||||
if src < desired || src%desired != 0 || src/physic.Frequency(maxWaitCycles*clockDiviMax) > desired {
|
||||
// Can't attain without oversampling (too low) or desired frequency is
|
||||
// higher than the source (too high) or is not a multiple.
|
||||
return 0, 0
|
||||
}
|
||||
factor := uint32(src / desired)
|
||||
// TODO(maruel): Only iterate over valid divisors to save a bit more
|
||||
// calculations. Since it's is only doing 32 loops, this is not a big deal.
|
||||
for wait := uint32(1); wait <= maxWaitCycles; wait++ {
|
||||
if rest := factor % wait; rest != 0 {
|
||||
continue
|
||||
}
|
||||
clk := factor / wait
|
||||
if clk == 0 {
|
||||
break
|
||||
}
|
||||
if clk <= clockDiviMax {
|
||||
return clk, wait
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// findDivisorOversampled tries to find the lowest allowed oversampling to make
|
||||
// desiredHz a multiple of srcHz.
|
||||
//
|
||||
// Allowed oversampling depends on the desiredHz. Cap oversampling because
|
||||
// oversampling at 10x in the 1Mhz range becomes unreasonable in term of
|
||||
// memory usage.
|
||||
func findDivisorOversampled(src, desired physic.Frequency, maxWaitCycles uint32) (uint32, uint32, physic.Frequency) {
|
||||
//log.Printf("findDivisorOversampled(%s, %s, %d)", src, desired, maxWaitCycles)
|
||||
// There are 2 reasons:
|
||||
// - desired is so low it is not possible to lower src to this frequency
|
||||
// - not a multiple, there's a need for a prime number
|
||||
// TODO(maruel): Rewrite without a loop, this is not needed. Leverage primes
|
||||
// to reduce the number of iterations.
|
||||
for multiple := physic.Frequency(2); ; multiple++ {
|
||||
n := multiple * desired
|
||||
if n > 100*physic.KiloHertz && multiple > 10 {
|
||||
break
|
||||
}
|
||||
if clk, wait := findDivisorExact(src, n, maxWaitCycles); clk != 0 {
|
||||
return clk, wait, n
|
||||
}
|
||||
}
|
||||
return 0, 0, 0
|
||||
}
|
||||
|
||||
// calcSource choose the best source to get the exact desired clock.
|
||||
//
|
||||
// It calculates the clock source, the clock divisor and the wait cycles, if
|
||||
// applicable. Wait cycles is 'div minus 1'.
|
||||
func calcSource(f physic.Frequency, maxWaitCycles uint32) (clockCtl, uint32, uint32, physic.Frequency, error) {
|
||||
if f < physic.Hertz {
|
||||
return 0, 0, 0, 0, fmt.Errorf("bcm283x-clock: desired frequency %s must be >1hz", f)
|
||||
}
|
||||
if f > 125*physic.MegaHertz {
|
||||
return 0, 0, 0, 0, fmt.Errorf("bcm283x-clock: desired frequency %s is too high", f)
|
||||
}
|
||||
// http://elinux.org/BCM2835_datasheet_errata states that clockSrc19dot2MHz
|
||||
// is the cleanest clock source so try it first.
|
||||
div, wait := findDivisorExact(clk19dot2MHz, f, maxWaitCycles)
|
||||
if div != 0 {
|
||||
return clockSrc19dot2MHz, div, wait, f, nil
|
||||
}
|
||||
// Try 500Mhz.
|
||||
div, wait = findDivisorExact(clk500MHz, f, maxWaitCycles)
|
||||
if div != 0 {
|
||||
return clockSrcPLLD, div, wait, f, nil
|
||||
}
|
||||
|
||||
// Try with up to 10x oversampling. This is generally useful for lower
|
||||
// frequencies, below 10kHz. Prefer the one with less oversampling. Only for
|
||||
// non-aliased matches.
|
||||
div19, wait19, f19 := findDivisorOversampled(clk19dot2MHz, f, maxWaitCycles)
|
||||
div500, wait500, f500 := findDivisorOversampled(clk500MHz, f, maxWaitCycles)
|
||||
if div19 != 0 && (div500 == 0 || f19 < f500) {
|
||||
return clockSrc19dot2MHz, div19, wait19, f19, nil
|
||||
}
|
||||
if div500 != 0 {
|
||||
return clockSrcPLLD, div500, wait500, f500, nil
|
||||
}
|
||||
return 0, 0, 0, 0, errors.New("failed to find a good clock")
|
||||
}
|
||||
|
||||
// set changes the clock frequency to the desired value or the closest one
|
||||
// otherwise.
|
||||
//
|
||||
// f=0 means disabled.
|
||||
//
|
||||
// maxWaitCycles is the maximum oversampling via an additional wait cycles that
|
||||
// can further divide the clock. Use 1 if no additional wait cycle is
|
||||
// available. It is expected to be dmaWaitcyclesMax+1.
|
||||
//
|
||||
// Returns the actual clock used and divisor.
|
||||
func (c *clock) set(f physic.Frequency, maxWaitCycles uint32) (physic.Frequency, uint32, error) {
|
||||
if f == 0 {
|
||||
c.ctl = clockPasswdCtl | clockKill
|
||||
for c.ctl&clockBusy != 0 {
|
||||
}
|
||||
return 0, 0, nil
|
||||
}
|
||||
ctl, div, div2, actual, err := calcSource(f, maxWaitCycles)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return actual, div2, c.setRaw(ctl, div)
|
||||
}
|
||||
|
||||
// setRaw sets the clock speed with the clock source and the divisor.
|
||||
func (c *clock) setRaw(ctl clockCtl, div uint32) error {
|
||||
if div < 1 || div > clockDiviMax {
|
||||
return errors.New("invalid clock divisor")
|
||||
}
|
||||
if ctl != clockSrc19dot2MHz && ctl != clockSrcPLLD {
|
||||
return errors.New("invalid clock control")
|
||||
}
|
||||
// Stop the clock.
|
||||
// TODO(maruel): Do not stop the clock if the current clock rate is the one
|
||||
// desired.
|
||||
for c.ctl&clockBusy != 0 {
|
||||
c.ctl = clockPasswdCtl | clockKill
|
||||
}
|
||||
d := clockDiv(div << clockDiviShift)
|
||||
c.div = clockPasswdDiv | d
|
||||
Nanospin(10 * time.Nanosecond)
|
||||
// Page 107
|
||||
c.ctl = clockPasswdCtl | ctl
|
||||
Nanospin(10 * time.Nanosecond)
|
||||
c.ctl = clockPasswdCtl | ctl | clockEnable
|
||||
if c.div != d {
|
||||
// This error is mocked out in tests, so the code path of set() callers can
|
||||
// follow on.
|
||||
return errClockRegister
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *clock) String() string {
|
||||
return fmt.Sprintf("%s / %s", c.ctl, c.div)
|
||||
}
|
||||
|
||||
// clockMap is the memory mapped clock registers.
|
||||
//
|
||||
// The clock #1 must not be touched since it is being used by the ethernet
|
||||
// controller.
|
||||
//
|
||||
// Page 107 for gp0~gp2.
|
||||
// https://scribd.com/doc/127599939/BCM2835-Audio-clocks for PCM/PWM.
|
||||
type clockMap struct {
|
||||
reserved0 [0x70 / 4]uint32 //
|
||||
gp0 clock // CM_GP0CTL+CM_GP0DIV; 0x70-0x74 (125MHz max)
|
||||
gp1ctl uint32 // CM_GP1CTL+CM_GP1DIV; 0x78-0x7A must not use (used by ethernet)
|
||||
gp1div uint32 // CM_GP1CTL+CM_GP1DIV; 0x78-0x7A must not use (used by ethernet)
|
||||
gp2 clock // CM_GP2CTL+CM_GP2DIV; 0x80-0x84 (125MHz max)
|
||||
reserved1 [(0x98 - 0x88) / 4]uint32 // 0x88-0x94
|
||||
pcm clock // CM_PCMCTL+CM_PCMDIV 0x98-0x9C
|
||||
pwm clock // CM_PWMCTL+CM_PWMDIV 0xA0-0xA4
|
||||
}
|
||||
|
||||
func (c *clockMap) GoString() string {
|
||||
return fmt.Sprintf(
|
||||
"{\n gp0: %s,\n gp1: %s,\n gp2: %s,\n pcm: %sw,\n pwm: %s,\n}",
|
||||
&c.gp0, &clock{clockCtl(c.gp1ctl), clockDiv(c.gp1div)}, &c.gp2, &c.pcm, &c.pwm)
|
||||
}
|
1224
vendor/periph.io/x/periph/host/bcm283x/dma.go
generated
vendored
Normal file
1224
vendor/periph.io/x/periph/host/bcm283x/dma.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
42
vendor/periph.io/x/periph/host/bcm283x/doc.go
generated
vendored
Normal file
42
vendor/periph.io/x/periph/host/bcm283x/doc.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package bcm283x exposes the BCM283x GPIO functionality.
|
||||
//
|
||||
// This driver implements memory-mapped GPIO pin manipulation and leverages
|
||||
// sysfs-gpio for edge detection.
|
||||
//
|
||||
// If you are looking for the actual implementation, open doc.go for further
|
||||
// implementation details.
|
||||
//
|
||||
// GPIOs
|
||||
//
|
||||
// Aliases for GPCLK0, GPCLK1, GPCLK2 are created for corresponding CLKn pins.
|
||||
// Same for PWM0_OUT and PWM1_OUT, which point respectively to PWM0 and PWM1.
|
||||
//
|
||||
// Datasheet
|
||||
//
|
||||
// https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
|
||||
//
|
||||
// Its crowd-sourced errata: http://elinux.org/BCM2835_datasheet_errata
|
||||
//
|
||||
// BCM2836:
|
||||
// https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
|
||||
//
|
||||
// Another doc about PCM and PWM:
|
||||
// https://scribd.com/doc/127599939/BCM2835-Audio-clocks
|
||||
//
|
||||
// GPIO pad control:
|
||||
// https://scribd.com/doc/101830961/GPIO-Pads-Control2
|
||||
package bcm283x
|
||||
|
||||
// Other implementations details
|
||||
//
|
||||
// mainline:
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/dma/bcm2835-dma.c
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/gpio
|
||||
//
|
||||
// Raspbian kernel:
|
||||
// https://github.com/raspberrypi/linux/blob/rpi-4.11.y/drivers/dma
|
||||
// https://github.com/raspberrypi/linux/blob/rpi-4.11.y/drivers/gpio
|
1361
vendor/periph.io/x/periph/host/bcm283x/gpio.go
generated
vendored
Normal file
1361
vendor/periph.io/x/periph/host/bcm283x/gpio.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
234
vendor/periph.io/x/periph/host/bcm283x/pcm.go
generated
vendored
Normal file
234
vendor/periph.io/x/periph/host/bcm283x/pcm.go
generated
vendored
Normal file
@ -0,0 +1,234 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// pcm means I2S.
|
||||
|
||||
package bcm283x
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"periph.io/x/periph/conn/physic"
|
||||
)
|
||||
|
||||
type pcmCS uint32
|
||||
|
||||
// Pages 126-129
|
||||
const (
|
||||
// 31:26 reserved
|
||||
pcmStandby pcmCS = 1 << 25 // STBY Allow at least 4 PCM clock cycles to take effect
|
||||
pcmSync pcmCS = 1 << 24 // SYNC Two PCM clocks have occurred since last write
|
||||
pcmRXSignExtend pcmCS = 1 << 23 // RXSEX Sign extend RXZ data
|
||||
pcmRXFull pcmCS = 1 << 22 // RXF RX FIFO is full
|
||||
pcmTXEmpty pcmCS = 1 << 21 // TXE TX FIFO is empty
|
||||
pcmRXData pcmCS = 1 << 20 // RXD RX FIFO contains data
|
||||
pcmTXData pcmCS = 1 << 19 // TXD TX FIFO ready to accept data
|
||||
pcmRXR pcmCS = 1 << 18 // RXR RX FIFO needs reading
|
||||
pcmTXW pcmCS = 1 << 17 // TXW TX FIFO needs writing
|
||||
pcmRXErr pcmCS = 1 << 16 // RXERR RX FIFO error
|
||||
pcmTXErr pcmCS = 1 << 15 // TXERR TX FIFO error
|
||||
pcmRXSync pcmCS = 1 << 14 // RXSYNC RX FIFO is out of sync
|
||||
pcmTXSync pcmCS = 1 << 13 // TXSYNC TX FIFO is out of sync
|
||||
// 12:10 reserved
|
||||
pcmDMAEnable pcmCS = 1 << 9 // DMAEN Generate TX&RX DMA DREQ
|
||||
// 8:7 RXTHR controls when pcmRXR is set
|
||||
pcmRXThresholdOne pcmCS = 0 << 7 // One sample in RX FIFO
|
||||
pcmRXThreshold1 pcmCS = 1 << 7 // RX FIFO is at least (?) full
|
||||
pcmRXThreshold2 pcmCS = 2 << 7 // ?
|
||||
pcmRXThresholdFull pcmCS = 3 << 7 // RX is full
|
||||
// 6:5 TXTHR controls when pcmTXW is set
|
||||
pcmTXThresholdEmpty pcmCS = 0 << 5 // TX FIFO is empty
|
||||
pcmTXThresholdNotFull1 pcmCS = 1 << 5 // At least one sample can be put
|
||||
pcmTXThresholdNotFull2 pcmCS = 2 << 5 // At least one sample can be put
|
||||
pcmTXThresholdOne pcmCS = 3 << 5 // One sample can be put
|
||||
pcmRXClear pcmCS = 1 << 4 // RXCLR Clear RX FIFO; takes 2 PCM clock to take effect
|
||||
pcmTXClear pcmCS = 1 << 3 // TXCLR Clear TX FIFO; takes 2 PCM clock to take effect
|
||||
pcmTXEnable pcmCS = 1 << 2 // TXON Enable TX
|
||||
pcmRXEnable pcmCS = 1 << 1 // RXON Enable FX
|
||||
pcmEnable pcmCS = 1 << 0 // EN Enable the PCM
|
||||
)
|
||||
|
||||
type pcmMode uint32
|
||||
|
||||
// Page 129-131
|
||||
const (
|
||||
// 31:29 reserved
|
||||
pcmClockDisable pcmMode = 1 << 28 // CLK_DIS Cleanly disable the PCM clock
|
||||
pcmDecimation32 pcmMode = 1 << 27 // PDMN; 0 is factor 16, 1 is factor 32
|
||||
pcmRXPDMFilter pcmMode = 1 << 26 // PDME Enable input CIC filter on PDM input
|
||||
pcmRXMerge pcmMode = 1 << 25 // FRXP Merge both channels as single FIFO entry
|
||||
pcmTXMerge pcmMode = 1 << 24 // FTXP Merge both channels as singe FIFO entry
|
||||
pcmClockSlave pcmMode = 1 << 23 // CLKM PCM CLK is input
|
||||
pcmClockInverted pcmMode = 1 << 22 // CLKI Inverse clock signal
|
||||
pcmFSSlave pcmMode = 1 << 21 // FSM PCM FS is input
|
||||
pcmFSInverted pcmMode = 1 << 20 // FSI Invese FS signal
|
||||
pcmFrameLengthShift = 10 //
|
||||
pcmFrameLenghtMask pcmMode = 0x3F << pcmFrameLengthShift // FLEN Frame length + 1
|
||||
pcmFSLenghtMask pcmMode = 0x3F << 0 // FSLEN FS pulse clock width
|
||||
)
|
||||
|
||||
type pcmRX uint32
|
||||
|
||||
// Page 131-132
|
||||
const (
|
||||
pcmRX1Width pcmRX = 1 << 31 // CH1WEX Legacy
|
||||
pcmRX1Enable pcmRX = 1 << 30 // CH1EN
|
||||
pcmRX1PosShift = 20
|
||||
pcmRX1PosMask pcmRX = 0x3F << pcmRX1PosShift // CH1POS Clock delay
|
||||
pcmRX1Channel16 pcmRX = 8 << 16 // CH1WID (Arbitrary width between 8 and 16 is supported)
|
||||
pcmRX2Width pcmRX = 1 << 15 // CH2WEX Legacy
|
||||
pcmRX2Enable pcmRX = 1 << 14 // CH2EN
|
||||
pcmRX2PosShift = 4
|
||||
pcmRX2PosMask pcmRX = 0x3F << pcmRX2PosShift // CH2POS Clock delay
|
||||
pcmRX2Channel16 pcmRX = 8 << 0 // CH2WID (Arbitrary width between 8 and 16 is supported)
|
||||
)
|
||||
|
||||
type pcmTX uint32
|
||||
|
||||
// Page 133-134
|
||||
const (
|
||||
pcmTX1Width pcmTX = 1 << 31 // CH1WX Legacy
|
||||
pcmTX1Enable pcmTX = 1 << 30 // CH1EN Enable channel 1
|
||||
pcmTX1PosShift = 20
|
||||
pcmTX1PosMask pcmTX = 0x3F << pcmTX1PosShift // CH1POS Clock delay
|
||||
pcmTX1Channel16 pcmTX = 8 << 16 // CH1WID (Arbitrary width between 8 and 16 is supported)
|
||||
pcmTX2Width pcmTX = 1 << 15 // CH2WEX Legacy
|
||||
pcmTX2Enable pcmTX = 1 << 14 // CH2EN
|
||||
pcmTX2PosShift = 4
|
||||
pcmTX2PosMask pcmTX = 0x3F << pcmTX2PosShift // CH2POS Clock delay
|
||||
pcmTX2Channel16 pcmTX = 8 << 0 // CH2WID (Arbitrary width between 8 and 16 is supported)
|
||||
)
|
||||
|
||||
type pcmDreq uint32
|
||||
|
||||
// Page 134-135
|
||||
const (
|
||||
// 31 reserved
|
||||
pcmDreqTXPanicShift = 24
|
||||
pcmDreqTXPanicMask pcmDreq = 0x7F << pcmDreqTXPanicShift // TX_PANIC Panic level
|
||||
// 23 reserved
|
||||
pcmDreqRXPanicShift = 16
|
||||
pcmDreqRXPanicMask pcmDreq = 0x7F << pcmDreqRXPanicShift // RX_PANIC Panic level
|
||||
// 15 reserved
|
||||
pcmDreqTXLevelShift = 8
|
||||
pcmDreqTXLevelMask pcmDreq = 0x7F << pcmDreqTXPanicShift // TX Request Level
|
||||
// 7 reserved
|
||||
pcmDreqRXLevelShift = 0
|
||||
pcmDreqRXLevelMask pcmDreq = 0x7F << pcmDreqRXPanicShift // RX Request Level
|
||||
)
|
||||
|
||||
type pcmInterrupt uint32
|
||||
|
||||
// Page 135
|
||||
const (
|
||||
// 31:4 reserved
|
||||
pcmIntRXErr pcmInterrupt = 1 << 3 // RXERR RX error interrupt enable
|
||||
pcmIntTXErr pcmInterrupt = 1 << 2 // TXERR TX error interrupt enable
|
||||
pcmIntRXEnable pcmInterrupt = 1 << 1 // RXR RX Read interrupt enable
|
||||
pcmIntTXEnable pcmInterrupt = 1 << 0 // TXW TX Write interrupt enable
|
||||
)
|
||||
|
||||
type pcmIntStatus uint32
|
||||
|
||||
// Page 135-136
|
||||
const (
|
||||
// 31:4 reserved
|
||||
pcmIntStatRXErr pcmIntStatus = 1 << 3 // RXERR RX error occurred / clear
|
||||
pcmIntStatTXErr pcmIntStatus = 1 << 2 // TXERR TX error occurred / clear
|
||||
pcmIntStatRXEnable pcmIntStatus = 1 << 1 // RXR RX Read interrupt occurred / clear
|
||||
pcmIntStatTXEnable pcmIntStatus = 1 << 0 // TXW TX Write interrupt occurred / clear
|
||||
pcmIntStatusClear pcmIntStatus = 0xF
|
||||
)
|
||||
|
||||
// pcmGray puts it into a special data/strobe mode that is under 'best effort'
|
||||
// contract.
|
||||
type pcmGray uint32
|
||||
|
||||
// Page 136-137
|
||||
const (
|
||||
// 31:22 reserved
|
||||
pcmGrayRXFIFOLevelShift = 16
|
||||
pcmGrayRXFIFOLevelMask pcmGray = 0x3F << pcmGrayRXFIFOLevelShift // RXFIFOLEVEL How many words in RXFIFO
|
||||
pcmGrayFlushShift = 10
|
||||
pcmGrayFlushMask = 0x3F << pcmGrayFlushShift // FLUSHED How many bits were valid when flush occurred
|
||||
pcmGrayRXLevelShift = 4
|
||||
pcmGrayRXLevelMask pcmGray = 0x3F << pcmGrayRXLevelShift // RXLEVEL How many GRAY coded bits received
|
||||
pcmGrayFlush pcmGray = 1 << 2 // FLUSH
|
||||
pcmGrayClear pcmGray = 1 << 1 // CLR
|
||||
pcmGrayEnable pcmGray = 1 << 0 // EN
|
||||
)
|
||||
|
||||
// Page 119
|
||||
type pcmMap struct {
|
||||
cs pcmCS // CS_A Control Status
|
||||
fifo uint32 // FIFO_A FIFO register
|
||||
mode pcmMode // MODE_A Operation mode
|
||||
rxc pcmRX // RXC_A RX control
|
||||
txc pcmTX // TXC_A TX control
|
||||
dreq pcmDreq // DREQ_A DMA control
|
||||
inten pcmInterrupt // INTEN_A Interrupt enable
|
||||
intstc pcmIntStatus // INTSTC_A Interrupt status
|
||||
gray pcmGray // GRAY Gray mode input processing
|
||||
}
|
||||
|
||||
func (p *pcmMap) GoString() string {
|
||||
return fmt.Sprintf(
|
||||
"{\n cs: 0x%x,\n mode: 0x%x,\n rxc: 0x%x,\n txc: 0x%x,\n dreq: 0x%x,\n inten: 0x%x,\n intstc: 0x%x,\n gray: 0x%x,\n}",
|
||||
p.cs, p.mode, p.rxc, p.txc, p.dreq, p.inten, p.intstc, p.gray)
|
||||
}
|
||||
|
||||
func (p *pcmMap) reset() {
|
||||
p.cs = 0
|
||||
// In theory need to wait the equivalent of 2 PCM clocks.
|
||||
// TODO(maruel): Use pcmSync busy loop to synchronize.
|
||||
Nanospin(time.Microsecond)
|
||||
// Hard reset
|
||||
p.fifo = 0
|
||||
p.mode = 0
|
||||
p.rxc = 0
|
||||
p.txc = 0
|
||||
p.dreq = 0
|
||||
p.inten = 0
|
||||
p.intstc = pcmIntStatusClear
|
||||
p.gray = 0
|
||||
|
||||
// Clear pcmStandby / pcm
|
||||
}
|
||||
|
||||
// set initializes 8 bits stream via DMA with no delay and no FS.
|
||||
func (p *pcmMap) set() {
|
||||
p.cs |= pcmEnable
|
||||
p.txc = pcmTX1Width | pcmTX1Channel16 | pcmTX1Enable // 32bit TX
|
||||
p.mode = (32 - 1) << pcmFrameLengthShift
|
||||
p.cs |= pcmTXClear | pcmRXClear
|
||||
// In theory need to wait the equivalent of 2 PCM clocks.
|
||||
// TODO(maruel): Use pcmSync busy loop to synchronize.
|
||||
Nanospin(time.Microsecond)
|
||||
p.dreq = 0x10<<pcmDreqTXPanicShift | 0x30<<pcmDreqTXLevelShift
|
||||
p.cs |= pcmDMAEnable
|
||||
// pcmTXThresholdOne ?
|
||||
p.cs |= pcmTXEnable
|
||||
}
|
||||
|
||||
// setPCMClockSource sets the PCM clock.
|
||||
//
|
||||
// It may select an higher frequency than the one requested.
|
||||
//
|
||||
// Other potentially good clock sources are PWM, SPI and UART.
|
||||
func setPCMClockSource(f physic.Frequency) (physic.Frequency, uint32, error) {
|
||||
if drvDMA.pcmMemory == nil {
|
||||
return 0, 0, errors.New("subsystem PCM not initialized")
|
||||
}
|
||||
if drvDMA.clockMemory == nil {
|
||||
return 0, 0, errors.New("subsystem Clock not initialized")
|
||||
}
|
||||
actual, divs, err := drvDMA.clockMemory.pcm.set(f, 1)
|
||||
if err == nil {
|
||||
drvDMA.pcmMemory.cs = 0
|
||||
}
|
||||
// Convert divisor into wait cycles.
|
||||
return actual, divs, err
|
||||
}
|
272
vendor/periph.io/x/periph/host/bcm283x/pwm.go
generated
vendored
Normal file
272
vendor/periph.io/x/periph/host/bcm283x/pwm.go
generated
vendored
Normal file
@ -0,0 +1,272 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package bcm283x
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"periph.io/x/periph/conn/physic"
|
||||
)
|
||||
|
||||
// PWENi is used to enable/disable the corresponding channel. Setting this bit
|
||||
// to 1 enables the channel and transmitter state machine. All registers and
|
||||
// FIFO is writable without setting this bit.
|
||||
//
|
||||
// MODEi bit is used to determine mode of operation. Setting this bit to 0
|
||||
// enables PWM mode. In this mode data stored in either PWM_DATi or FIFO is
|
||||
// transmitted by pulse width modulation within the range defined by PWM_RNGi.
|
||||
// When this mode is used MSENi defines whether to use PWM algorithm. Setting
|
||||
// MODEi to 1 enables serial mode, in which data stored in either PWM_DATi or
|
||||
// FIFO is transmitted serially within the range defined by PWM_RNGi. Data is
|
||||
// transmitted MSB first and truncated or zeropadded depending on PWM_RNGi.
|
||||
// Default mode is PWM.
|
||||
//
|
||||
// RPTLi is used to enable/disable repeating of the last data available in the
|
||||
// FIFO just before it empties. When this bit is 1 and FIFO is used, the last
|
||||
// available data in the FIFO is repeatedly sent. This may be useful in PWM
|
||||
// mode to avoid duty cycle gaps. If the FIFO is not used this bit does not
|
||||
// have any effect. Default operation is do-notrepeat.
|
||||
//
|
||||
// SBITi defines the state of the output when no transmission takes place. It
|
||||
// also defines the zero polarity for the zero padding in serialiser mode. This
|
||||
// bit is padded between two consecutive transfers as well as tail of the data
|
||||
// when PWM_RNGi is larger than bit depth of data being transferred. this bit
|
||||
// is zero by default.
|
||||
//
|
||||
// POLAi is used to configure the polarity of the output bit. When set to high
|
||||
// the final output is inverted. Default operation is no inversion.
|
||||
//
|
||||
// USEFi bit is used to enable/disable FIFO transfer. When this bit is high
|
||||
// data stored in the FIFO is used for transmission. When it is low, data
|
||||
// written to PWM_DATi is transferred. This bit is 0 as default.
|
||||
//
|
||||
// CLRF is used to clear the FIFO. Writing a 1 to this bit clears the FIFO.
|
||||
// Writing 0 has no effect. This is a single shot operation and reading the bit
|
||||
// always returns 0.
|
||||
//
|
||||
// MSENi is used to determine whether to use PWM algorithm or simple M/S ratio
|
||||
// transmission. When this bit is high M/S transmission is used. This bit is
|
||||
// zero as default. When MODEi is 1, this configuration bit has no effect.
|
||||
//
|
||||
// See page 139-140 for the description of the PWM and M/S ratio algorithms.
|
||||
const (
|
||||
// 31:16 reserved
|
||||
pwm2MS pwmControl = 1 << 15 // MSEN2; 0: PWM algorithm is used; 1: M/S transmission is used
|
||||
// 14 reserved
|
||||
pwm2UseFIFO pwmControl = 1 << 13 // USEF2; 0: Data register is transmitted; 1: Fifo is used for transmission
|
||||
pwm2Polarity pwmControl = 1 << 12 // POLA2; 0: 0=low 1=high; 1: 1=low 0=high
|
||||
pwm2SilenceHigh pwmControl = 1 << 11 // SBIT2; Defines the state of the output when no transmission takes place
|
||||
pwm2RepeatLastData pwmControl = 1 << 10 // RPTL2; 0: Transmission interrupts when FIFO is empty; 1: Last data in FIFO is transmitted repetedly until FIFO is not empty
|
||||
pwm2Serialiser pwmControl = 1 << 9 // MODE2; 0: PWM mode; 1: Serialiser mode
|
||||
pwm2Enable pwmControl = 1 << 8 // PWEN2; Enable channel 2
|
||||
pwm2Mask pwmControl = pwm2MS | pwm2UseFIFO | pwm2Polarity | pwm2SilenceHigh | pwm2RepeatLastData | pwm2Serialiser | pwm2Enable
|
||||
pwm1MS pwmControl = 1 << 7 // MSEN1; 0: PWM algorithm is used; 1: M/S transmission is used
|
||||
pwmClearFIFO pwmControl = 1 << 6 // CLRF1; Clear the fifo
|
||||
pwm1UseFIFO pwmControl = 1 << 5 // USEF1; 0: Data register is transmitted; 1: Fifo is used for transmission
|
||||
pwm1Polarity pwmControl = 1 << 4 // POLA1; 0: 0=low 1=high; 1: 1=low 0=high
|
||||
pwm1SilenceHigh pwmControl = 1 << 3 // SBIT1; Defines the state of the output when no transmission takes place
|
||||
pwm1RepeatLastData pwmControl = 1 << 2 // RPTL1; 0: Transmission interrupts when FIFO is empty; 1: Last data in FIFO is transmitted repetedly until FIFO is not empty
|
||||
pwm1Serialiser pwmControl = 1 << 1 // MODE1; 0: PWM mode; 1: Serialiser mode
|
||||
pwm1Enable pwmControl = 1 << 0 // PWEN1; Enable channel 1
|
||||
pwm1Mask pwmControl = pwm1MS | pwm1UseFIFO | pwm1Polarity | pwm1SilenceHigh | pwm1RepeatLastData | pwm1Serialiser | pwm1Enable
|
||||
)
|
||||
|
||||
// Pages 141-143.
|
||||
type pwmControl uint32
|
||||
|
||||
const (
|
||||
// 31:13 reserved
|
||||
// STAi bit indicates the current state of the channel which is useful for
|
||||
// debugging purposes. The bit set means the channel is currently
|
||||
// transmitting data.
|
||||
pwmSta4 pwmStatus = 1 << 12 // STA4
|
||||
pwmSta3 pwmStatus = 1 << 11 // STA3
|
||||
pwmSta2 pwmStatus = 1 << 10 // STA2
|
||||
pwmSta1 pwmStatus = 1 << 9 // STA1
|
||||
// BERR sets to high when an error has occurred while writing to registers
|
||||
// via APB. This may happen if the bus tries to write successively to same
|
||||
// set of registers faster than the synchroniser block can cope with.
|
||||
// Multiple switching may occur and contaminate the data during
|
||||
// synchronisation. Software should clear this bit by writing 1. Writing 0
|
||||
// to this bit has no effect.
|
||||
pwmBusErr pwmStatus = 1 << 8 // BERR Bus Error flag
|
||||
// GAPOi. bit indicates that there has been a gap between transmission of two
|
||||
// consecutive data from FIFO. This may happen when FIFO gets empty after
|
||||
// state machine has sent a word and waits for the next. If control bit RPTLi
|
||||
// is set to high this event will not occur. Software must clear this bit by
|
||||
// writing 1. Writing 0 to this bit has no effect.
|
||||
pwmGapo4 pwmStatus = 1 << 7 // GAPO4 Channel 4 Gap Occurred flag
|
||||
pwmGapo3 pwmStatus = 1 << 6 // GAPO3 Channel 3 Gap Occurred flag
|
||||
pwmGapo2 pwmStatus = 1 << 5 // GAPO2 Channel 2 Gap Occurred flag
|
||||
pwmGapo1 pwmStatus = 1 << 4 // GAPO1 Channel 1 Gap Occurred flag
|
||||
// RERR1 bit sets to high when a read when empty error occurs. Software must
|
||||
// clear this bit by writing 1. Writing 0 to this bit has no effect.
|
||||
pwmRerr1 pwmStatus = 1 << 3 // RERR1
|
||||
// WERR1 bit sets to high when a write when full error occurs. Software must
|
||||
// clear this bit by writing 1. Writing 0 to this bit has no effect.
|
||||
pwmWerr1 pwmStatus = 1 << 2 // WERR1
|
||||
// EMPT1 bit indicates the empty status of the FIFO. If this bit is high FIFO
|
||||
// is empty.
|
||||
pwmEmpt1 pwmStatus = 1 << 1 // EMPT1
|
||||
// FULL1 bit indicates the full status of the FIFO. If this bit is high FIFO
|
||||
// is full.
|
||||
pwmFull1 pwmStatus = 1 << 0 // FULL1
|
||||
pwmStatusMask = pwmSta4 | pwmSta3 | pwmSta2 | pwmSta1 | pwmBusErr | pwmGapo4 | pwmGapo3 | pwmGapo2 | pwmGapo1 | pwmRerr1 | pwmWerr1 | pwmEmpt1 | pwmFull1
|
||||
)
|
||||
|
||||
// Pages 144-145.
|
||||
type pwmStatus uint32
|
||||
|
||||
const (
|
||||
pwmDMAEnable pwmDMACfg = 1 << 31 // ENAB
|
||||
// 30:16 reserved
|
||||
pwmPanicShift = 16
|
||||
pwmPanicMask pwmDMACfg = 0xFF << pwmPanicShift // PANIC Default is 7
|
||||
pwmDreqMask pwmDMACfg = 0xFF // DREQ Default is 7
|
||||
)
|
||||
|
||||
// Page 145.
|
||||
type pwmDMACfg uint32
|
||||
|
||||
// pwmMap is the block to control the PWM generator.
|
||||
//
|
||||
// Note that pins are named PWM0 and PWM1 but the mapping uses channel numbers
|
||||
// 1 and 2.
|
||||
// - PWM0: GPIO12, GPIO18, GPIO40, GPIO52.
|
||||
// - PWM1: GPIO13, GPIO19, GPIO41, GPIO45, GPIO53.
|
||||
//
|
||||
// Each channel works independently. They can either output a bitstream or a
|
||||
// serialised version of up to eight 32 bits words.
|
||||
//
|
||||
// The default base PWM frequency is 100Mhz.
|
||||
//
|
||||
// Description at page 138-139.
|
||||
//
|
||||
// Page 140-141.
|
||||
type pwmMap struct {
|
||||
ctl pwmControl // CTL
|
||||
status pwmStatus // STA
|
||||
dmaCfg pwmDMACfg // DMAC
|
||||
// This register is used to define the range for the corresponding channel.
|
||||
// In PWM mode evenly distributed pulses are sent within a period of length
|
||||
// defined by this register. In serial mode serialised data is transmitted
|
||||
// within the same period. If the value in PWM_RNGi is less than 32, only the
|
||||
// first PWM_RNGi bits are sent resulting in a truncation. If it is larger
|
||||
// than 32 excess zero bits are padded at the end of data. Default value for
|
||||
// this register is 32.
|
||||
dummy1 uint32 // Padding
|
||||
rng1 uint32 // RNG1
|
||||
// This register stores the 32 bit data to be sent by the PWM Controller when
|
||||
// USEFi is 0. In PWM mode data is sent by pulse width modulation: the value
|
||||
// of this register defines the number of pulses which is sent within the
|
||||
// period defined by PWM_RNGi. In serialiser mode data stored in this
|
||||
// register is serialised and transmitted.
|
||||
dat1 uint32 // DAT1
|
||||
// This register is the FIFO input for the all channels. Data written to this
|
||||
// address is stored in channel FIFO and if USEFi is enabled for the channel
|
||||
// i it is used as data to be sent. This register is write only, and reading
|
||||
// this register will always return bus default return value, pwm0.
|
||||
// When more than one channel is enabled for FIFO usage, the data written
|
||||
// into the FIFO is shared between these channels in turn. For example if the
|
||||
// word series A B C D E F G H I .. is written to FIFO and two channels are
|
||||
// active and configured to use FIFO then channel 1 will transmit words A C E
|
||||
// G I .. and channel 2 will transmit words B D F H .. . Note that
|
||||
// requesting data from the FIFO is in locked-step manner and therefore
|
||||
// requires tight coupling of state machines of the channels. If any of the
|
||||
// channel range (period) value is different than the others this will cause
|
||||
// the channels with small range values to wait between words hence resulting
|
||||
// in gaps between words. To avoid that, each channel sharing the FIFO should
|
||||
// be configured to use the same range value. Also note that RPTLi are not
|
||||
// meaningful when the FIFO is shared between channels as there is no defined
|
||||
// channel to own the last data in the FIFO. Therefore sharing channels must
|
||||
// have their RPTLi set to zero.
|
||||
//
|
||||
// If the set of channels to share the FIFO has been modified after a
|
||||
// configuration change, FIFO should be cleared before writing new data.
|
||||
fifo uint32 // FIF1
|
||||
dummy2 uint32 // Padding
|
||||
rng2 uint32 // RNG2 Equivalent of rng1 for channel 2
|
||||
dat2 uint32 // DAT2 Equivalent of dat1 for channel 2
|
||||
}
|
||||
|
||||
// reset stops the PWM.
|
||||
func (p *pwmMap) reset() {
|
||||
p.dmaCfg = 0
|
||||
p.ctl |= pwmClearFIFO
|
||||
p.ctl &^= pwm1Enable | pwm2Enable
|
||||
Nanospin(100 * time.Microsecond) // Cargo cult copied. Probably not necessary.
|
||||
p.status = pwmBusErr | pwmGapo1 | pwmGapo2 | pwmGapo3 | pwmGapo4 | pwmRerr1 | pwmWerr1
|
||||
Nanospin(100 * time.Microsecond)
|
||||
// Use the full 32 bits of DATi.
|
||||
p.rng1 = 32
|
||||
p.rng2 = 32
|
||||
}
|
||||
|
||||
// setPWMClockSource sets the PWM clock for use by the DMA controller for
|
||||
// pacing.
|
||||
//
|
||||
// It may select an higher frequency than the one requested.
|
||||
//
|
||||
// Other potentially good clock sources are PCM, SPI and UART.
|
||||
func setPWMClockSource() (physic.Frequency, error) {
|
||||
if drvDMA.pwmMemory == nil {
|
||||
return 0, errors.New("subsystem PWM not initialized")
|
||||
}
|
||||
if drvDMA.clockMemory == nil {
|
||||
return 0, errors.New("subsystem Clock not initialized")
|
||||
}
|
||||
if drvDMA.pwmDMACh != nil {
|
||||
// Already initialized
|
||||
return drvDMA.pwmDMAFreq, nil
|
||||
}
|
||||
|
||||
// divs * div must fit in rng1 registor.
|
||||
div := uint32(drvDMA.pwmBaseFreq / drvDMA.pwmDMAFreq)
|
||||
actual, divs, err := drvDMA.clockMemory.pwm.set(drvDMA.pwmBaseFreq, div)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if e := actual / physic.Frequency(divs*div); drvDMA.pwmDMAFreq != e {
|
||||
return 0, fmt.Errorf("unexpected DMA frequency %s != %s (%d/%d/%d)", drvDMA.pwmDMAFreq, e, actual, divs, div)
|
||||
}
|
||||
// It acts as a clock multiplier, since this amount of data is sent per
|
||||
// clock tick.
|
||||
drvDMA.pwmMemory.rng1 = divs * div
|
||||
Nanospin(10 * time.Microsecond)
|
||||
// Periph data (?)
|
||||
|
||||
// Use low priority.
|
||||
drvDMA.pwmMemory.dmaCfg = pwmDMAEnable | pwmDMACfg(15<<pwmPanicShift|15)
|
||||
Nanospin(10 * time.Microsecond)
|
||||
drvDMA.pwmMemory.ctl |= pwmClearFIFO
|
||||
Nanospin(10 * time.Microsecond)
|
||||
old := drvDMA.pwmMemory.ctl
|
||||
drvDMA.pwmMemory.ctl = (old & ^pwmControl(0xff)) | pwm1UseFIFO | pwm1Enable
|
||||
|
||||
// Start DMA
|
||||
if drvDMA.pwmDMACh, drvDMA.pwmDMABuf, err = dmaWritePWMFIFO(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return drvDMA.pwmDMAFreq, nil
|
||||
}
|
||||
|
||||
func resetPWMClockSource() error {
|
||||
if drvDMA.pwmDMACh != nil {
|
||||
drvDMA.pwmDMACh.reset()
|
||||
drvDMA.pwmDMACh = nil
|
||||
}
|
||||
if drvDMA.pwmDMABuf != nil {
|
||||
if err := drvDMA.pwmDMABuf.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
drvDMA.pwmDMABuf = nil
|
||||
}
|
||||
_, _, err := drvDMA.clockMemory.pwm.set(0, 0)
|
||||
return err
|
||||
}
|
134
vendor/periph.io/x/periph/host/bcm283x/streams.go
generated
vendored
Normal file
134
vendor/periph.io/x/periph/host/bcm283x/streams.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package bcm283x
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"periph.io/x/periph/conn/gpio/gpiostream"
|
||||
)
|
||||
|
||||
// uint32ToBitLSBF packs a bit offset found on slice `d` (that is actually
|
||||
// uint32) back into a densely packed Bits stream.
|
||||
func uint32ToBitLSBF(w []byte, d []uint8, bit uint8, skip int) {
|
||||
// Little endian.
|
||||
x := bit / 8
|
||||
d = d[x:]
|
||||
bit -= 8 * x
|
||||
mask := uint8(1) << bit
|
||||
for i := range w {
|
||||
w[i] = ((d[0]&mask)>>bit<<0 |
|
||||
(d[skip*1]&mask)>>bit<<1 |
|
||||
(d[skip*2]&mask)>>bit<<2 |
|
||||
(d[skip*3]&mask)>>bit<<3 |
|
||||
(d[skip*4]&mask)>>bit<<4 |
|
||||
(d[skip*5]&mask)>>bit<<5 |
|
||||
(d[skip*6]&mask)>>bit<<6 |
|
||||
(d[skip*7]&mask)>>bit<<7)
|
||||
d = d[skip*8:]
|
||||
}
|
||||
}
|
||||
|
||||
func getBit(b byte, index int, msb bool) byte {
|
||||
var shift uint
|
||||
if msb {
|
||||
shift = uint(7 - index)
|
||||
} else {
|
||||
shift = uint(index)
|
||||
}
|
||||
return (b >> shift) & 1
|
||||
}
|
||||
|
||||
func raster32Bits(s gpiostream.Stream, skip int, clear, set []uint32, mask uint32) error {
|
||||
var msb bool
|
||||
var bits []byte
|
||||
switch b := s.(type) {
|
||||
case *gpiostream.BitStream:
|
||||
msb = !b.LSBF
|
||||
bits = b.Bits
|
||||
default:
|
||||
return fmt.Errorf("unsupported type %T", b)
|
||||
}
|
||||
m := len(clear) / 8
|
||||
if n := len(bits); n < m {
|
||||
m = n
|
||||
}
|
||||
index := 0
|
||||
for i := 0; i < m; i++ {
|
||||
for j := 0; j < 8; j++ {
|
||||
if getBit(bits[i], j, msb) != 0 {
|
||||
for k := 0; k < skip; k++ {
|
||||
set[index] |= mask
|
||||
index++
|
||||
}
|
||||
} else {
|
||||
for k := 0; k < skip; k++ {
|
||||
clear[index] |= mask
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// raster32 rasters the stream into a uint32 stream with the specified masks to
|
||||
// put in the correctly slice when the bit is set and when it is clear.
|
||||
//
|
||||
// `s` must be one of the types in this package.
|
||||
func raster32(s gpiostream.Stream, skip int, clear, set []uint32, mask uint32) error {
|
||||
if mask == 0 {
|
||||
return errors.New("bcm283x: mask is 0")
|
||||
}
|
||||
if len(clear) == 0 {
|
||||
return errors.New("bcm283x: clear buffer is empty")
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return errors.New("bcm283x: set buffer is empty")
|
||||
}
|
||||
if len(clear) != len(set) {
|
||||
return errors.New("bcm283x: clear and set buffers have different length")
|
||||
}
|
||||
switch x := s.(type) {
|
||||
case *gpiostream.BitStream:
|
||||
// TODO
|
||||
return raster32Bits(x, skip, clear, set, mask)
|
||||
case *gpiostream.EdgeStream:
|
||||
return errors.New("bcm283x: EdgeStream is not supported yet")
|
||||
case *gpiostream.Program:
|
||||
return errors.New("bcm283x: Program is not supported yet")
|
||||
default:
|
||||
return errors.New("bcm283x: unknown stream type")
|
||||
}
|
||||
}
|
||||
|
||||
// PCM/PWM DMA buf is encoded as little-endian and MSB first.
|
||||
func copyStreamToDMABuf(w gpiostream.Stream, dst []uint32) error {
|
||||
switch v := w.(type) {
|
||||
case *gpiostream.BitStream:
|
||||
if v.LSBF {
|
||||
return errors.New("TODO(simokawa): handle BitStream.LSBF")
|
||||
}
|
||||
// This is big-endian and MSB first.
|
||||
i := 0
|
||||
for ; i < len(v.Bits)/4; i++ {
|
||||
dst[i] = binary.BigEndian.Uint32(v.Bits[i*4:])
|
||||
}
|
||||
last := uint32(0)
|
||||
if mod := len(v.Bits) % 4; mod > 0 {
|
||||
for j := 0; j < mod; j++ {
|
||||
last |= (uint32(v.Bits[i*4+j])) << uint32(8*(3-j))
|
||||
}
|
||||
dst[i] = last
|
||||
}
|
||||
return nil
|
||||
case *gpiostream.EdgeStream:
|
||||
return errors.New("TODO(simokawa): handle EdgeStream")
|
||||
default:
|
||||
return errors.New("unsupported Stream type")
|
||||
}
|
||||
}
|
60
vendor/periph.io/x/periph/host/bcm283x/timer.go
generated
vendored
Normal file
60
vendor/periph.io/x/periph/host/bcm283x/timer.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package bcm283x
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"periph.io/x/periph/host/cpu"
|
||||
)
|
||||
|
||||
// ReadTime returns the time on a monotonic 1Mhz clock (1µs resolution).
|
||||
//
|
||||
// It only works if bcm283x-dma successfully loaded. Otherwise it returns 0.
|
||||
func ReadTime() time.Duration {
|
||||
if drvDMA.timerMemory == nil {
|
||||
return 0
|
||||
}
|
||||
return (time.Duration(drvDMA.timerMemory.high)<<32 | time.Duration(drvDMA.timerMemory.low)) * time.Microsecond
|
||||
}
|
||||
|
||||
// Nanospin spins the CPU without calling into the kernel code if possible.
|
||||
func Nanospin(t time.Duration) {
|
||||
start := ReadTime()
|
||||
if start == 0 {
|
||||
// Use the slow generic version.
|
||||
cpu.Nanospin(t)
|
||||
return
|
||||
}
|
||||
// TODO(maruel): Optimize code path for sub-1µs duration.
|
||||
for ReadTime()-start < t {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const (
|
||||
// 31:4 reserved
|
||||
timerM3 = 1 << 3 // M3
|
||||
timerM2 = 1 << 2 // M2
|
||||
timerM1 = 1 << 1 // M1
|
||||
timerM0 = 1 << 0 // M0
|
||||
)
|
||||
|
||||
// Page 173
|
||||
type timerCtl uint32
|
||||
|
||||
// timerMap represents the registers to access the 1Mhz timer.
|
||||
//
|
||||
// Page 172
|
||||
type timerMap struct {
|
||||
ctl timerCtl // CS
|
||||
low uint32 // CLO
|
||||
high uint32 // CHI
|
||||
c0 uint32 // 0
|
||||
c1 uint32 // C1
|
||||
c2 uint32 // C2
|
||||
c3 uint32 // C3
|
||||
}
|
Reference in New Issue
Block a user