// 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<