//go:build nrf52 || nrf52840 || nrf52833 package machine import ( "device/nrf" "runtime/volatile" "unsafe" ) func CPUFrequency() uint32 { return 64000000 } // InitADC initializes the registers needed for ADC. func InitADC() { return // no specific setup on nrf52 machine. } // Configure configures an ADC pin to be able to read analog data. func (a ADC) Configure(config ADCConfig) { // Enable ADC. // The ADC does not consume a noticeable amount of current simply by being // enabled. nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos) // Use fixed resolution of 12 bits. // TODO: is it useful for users to change this? nrf.SAADC.RESOLUTION.Set(nrf.SAADC_RESOLUTION_VAL_12bit) var configVal uint32 = nrf.SAADC_CH_CONFIG_RESP_Bypass<= 8000000: freq = nrf.SPIM_FREQUENCY_FREQUENCY_M8 case config.Frequency >= 4000000: freq = nrf.SPIM_FREQUENCY_FREQUENCY_M4 case config.Frequency >= 2000000: freq = nrf.SPIM_FREQUENCY_FREQUENCY_M2 case config.Frequency >= 1000000: freq = nrf.SPIM_FREQUENCY_FREQUENCY_M1 case config.Frequency >= 500000: freq = nrf.SPIM_FREQUENCY_FREQUENCY_K500 case config.Frequency >= 250000: freq = nrf.SPIM_FREQUENCY_FREQUENCY_K250 default: // below 250kHz, default to the lowest speed available freq = nrf.SPIM_FREQUENCY_FREQUENCY_K125 } spi.Bus.FREQUENCY.Set(freq) var conf uint32 // set bit transfer order if config.LSBFirst { conf = (nrf.SPIM_CONFIG_ORDER_LsbFirst << nrf.SPIM_CONFIG_ORDER_Pos) } // set mode switch config.Mode { case 0: conf &^= (nrf.SPIM_CONFIG_CPOL_ActiveHigh << nrf.SPIM_CONFIG_CPOL_Pos) conf &^= (nrf.SPIM_CONFIG_CPHA_Leading << nrf.SPIM_CONFIG_CPHA_Pos) case 1: conf &^= (nrf.SPIM_CONFIG_CPOL_ActiveHigh << nrf.SPIM_CONFIG_CPOL_Pos) conf |= (nrf.SPIM_CONFIG_CPHA_Trailing << nrf.SPIM_CONFIG_CPHA_Pos) case 2: conf |= (nrf.SPIM_CONFIG_CPOL_ActiveLow << nrf.SPIM_CONFIG_CPOL_Pos) conf &^= (nrf.SPIM_CONFIG_CPHA_Leading << nrf.SPIM_CONFIG_CPHA_Pos) case 3: conf |= (nrf.SPIM_CONFIG_CPOL_ActiveLow << nrf.SPIM_CONFIG_CPOL_Pos) conf |= (nrf.SPIM_CONFIG_CPHA_Trailing << nrf.SPIM_CONFIG_CPHA_Pos) default: // to mode conf &^= (nrf.SPIM_CONFIG_CPOL_ActiveHigh << nrf.SPIM_CONFIG_CPOL_Pos) conf &^= (nrf.SPIM_CONFIG_CPHA_Leading << nrf.SPIM_CONFIG_CPHA_Pos) } spi.Bus.CONFIG.Set(conf) // set pins if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 { config.SCK = SPI0_SCK_PIN config.SDO = SPI0_SDO_PIN config.SDI = SPI0_SDI_PIN } spi.Bus.PSEL.SCK.Set(uint32(config.SCK)) spi.Bus.PSEL.MOSI.Set(uint32(config.SDO)) spi.Bus.PSEL.MISO.Set(uint32(config.SDI)) // Re-enable bus now that it is configured. spi.Bus.ENABLE.Set(nrf.SPIM_ENABLE_ENABLE_Enabled) return nil } // Transfer writes/reads a single byte using the SPI interface. func (spi SPI) Transfer(w byte) (byte, error) { buf := spi.buf[:] buf[0] = w err := spi.Tx(buf[:], buf[:]) return buf[0], err } // Tx handles read/write operation for SPI interface. Since SPI is a syncronous // write/read interface, there must always be the same number of bytes written // as bytes read. Therefore, if the number of bytes don't match it will be // padded until they fit: if len(w) > len(r) the extra bytes received will be // dropped and if len(w) < len(r) extra 0 bytes will be sent. func (spi SPI) Tx(w, r []byte) error { // Unfortunately the hardware (on the nrf52832) only supports up to 255 // bytes in the buffers, so if either w or r is longer than that the // transfer needs to be broken up in pieces. // The nrf52840 supports far larger buffers however, which isn't yet // supported. for len(r) != 0 || len(w) != 0 { // Prepare the SPI transfer: set the DMA pointers and lengths. // read buffer nr := uint32(len(r)) if nr > 0 { if nr > 255 { nr = 255 } spi.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0])))) r = r[nr:] } spi.Bus.RXD.MAXCNT.Set(nr) // write buffer nw := uint32(len(w)) if nw > 0 { if nw > 255 { nw = 255 } spi.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0])))) w = w[nw:] } spi.Bus.TXD.MAXCNT.Set(nw) // Do the transfer. // Note: this can be improved by not waiting until the transfer is // finished if the transfer is send-only (a common case). spi.Bus.TASKS_START.Set(1) for spi.Bus.EVENTS_END.Get() == 0 { } spi.Bus.EVENTS_END.Set(0) } return nil } // PWM is one PWM peripheral, which consists of a counter and multiple output // channels (that can be connected to actual pins). You can set the frequency // using SetPeriod, but only for all the channels in this PWM peripheral at // once. type PWM struct { PWM *nrf.PWM_Type channelValues [4]volatile.Register16 } // Configure enables and configures this PWM. // On the nRF52 series, the maximum period is around 0.26s. func (pwm *PWM) Configure(config PWMConfig) error { // Enable the peripheral. pwm.PWM.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos) // Use up counting only. TODO: allow configuring as up-and-down. pwm.PWM.MODE.Set(nrf.PWM_MODE_UPDOWN_Up << nrf.PWM_MODE_UPDOWN_Pos) // Indicate there are four channels that each have a different value. pwm.PWM.DECODER.Set(nrf.PWM_DECODER_LOAD_Individual< maxTop { return ErrPWMPeriodTooLong } } pwm.PWM.COUNTERTOP.Set(uint32(top)) // Apparently this is needed to apply the new COUNTERTOP. pwm.PWM.TASKS_SEQSTART[0].Set(1) return nil } // Top returns the current counter top, for use in duty cycle calculation. It // will only change with a call to Configure or SetPeriod, otherwise it is // constant. // // The value returned here is hardware dependent. In general, it's best to treat // it as an opaque value that can be divided by some number and passed to // pwm.Set (see pwm.Set for more information). func (pwm *PWM) Top() uint32 { return pwm.PWM.COUNTERTOP.Get() } // Channel returns a PWM channel for the given pin. func (pwm *PWM) Channel(pin Pin) (uint8, error) { config := uint32(pin) for ch := uint8(0); ch < 4; ch++ { channelConfig := pwm.PWM.PSEL.OUT[ch].Get() if channelConfig == 0xffffffff { // Unused channel. Configure it. pwm.PWM.PSEL.OUT[ch].Set(config) // Configure the pin (required by the reference manual). pin.Configure(PinConfig{Mode: PinOutput}) // Set channel to zero and non-inverting. pwm.channelValues[ch].Set(0x8000) return ch, nil } else if channelConfig == config { // This channel is already configured for this pin. return ch, nil } } // All four pins are already in use with other pins. return 0, ErrInvalidOutputPin } // SetInverting sets whether to invert the output of this channel. // Without inverting, a 25% duty cycle would mean the output is high for 25% of // the time and low for the rest. Inverting flips the output as if a NOT gate // was placed at the output, meaning that the output would be 25% low and 75% // high with a duty cycle of 25%. func (pwm *PWM) SetInverting(channel uint8, inverting bool) { ptr := &pwm.channelValues[channel] if inverting { ptr.Set(ptr.Get() &^ 0x8000) } else { ptr.Set(ptr.Get() | 0x8000) } } // Set updates the channel value. This is used to control the channel duty // cycle. For example, to set it to a 25% duty cycle, use: // // ch.Set(ch.Top() / 4) // // ch.Set(0) will set the output to low and ch.Set(ch.Top()) will set the output // to high, assuming the output isn't inverted. func (pwm *PWM) Set(channel uint8, value uint32) { // Update the channel value while retaining the polarity bit. ptr := &pwm.channelValues[channel] ptr.Set(ptr.Get()&0x8000 | uint16(value)&0x7fff) // Start the PWM, if it isn't already running. pwm.PWM.TASKS_SEQSTART[0].Set(1) }