PKGBUILD/vendor/periph.io/x/periph/host/bcm283x/pwm.go

273 lines
12 KiB
Go
Raw Normal View History

2018-12-04 18:11:50 +00:00
// 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
}