diff --git a/src/machine/machine_nrf.go b/src/machine/machine_nrf.go index ac01fd0bcb..f2e6777ebe 100644 --- a/src/machine/machine_nrf.go +++ b/src/machine/machine_nrf.go @@ -328,163 +328,3 @@ func (i2c I2C) readByte() (byte, error) { i2c.Bus.EVENTS_RXDREADY.Set(0) return byte(i2c.Bus.RXD.Get()), nil } - -// SPI on the NRF. -type SPI struct { - Bus *nrf.SPI_Type -} - -// There are 2 SPI interfaces on the NRF5x. -var ( - SPI0 = SPI{Bus: nrf.SPI0} - SPI1 = SPI{Bus: nrf.SPI1} -) - -// SPIConfig is used to store config info for SPI. -type SPIConfig struct { - Frequency uint32 - SCK Pin - SDO Pin - SDI Pin - LSBFirst bool - Mode uint8 -} - -// Configure is intended to setup the SPI interface. -func (spi SPI) Configure(config SPIConfig) { - // Disable bus to configure it - spi.Bus.ENABLE.Set(nrf.SPI_ENABLE_ENABLE_Disabled) - - // set frequency - var freq uint32 - - if config.Frequency == 0 { - config.Frequency = 4000000 // 4MHz - } - - switch { - case config.Frequency >= 8000000: - freq = nrf.SPI_FREQUENCY_FREQUENCY_M8 - case config.Frequency >= 4000000: - freq = nrf.SPI_FREQUENCY_FREQUENCY_M4 - case config.Frequency >= 2000000: - freq = nrf.SPI_FREQUENCY_FREQUENCY_M2 - case config.Frequency >= 1000000: - freq = nrf.SPI_FREQUENCY_FREQUENCY_M1 - case config.Frequency >= 500000: - freq = nrf.SPI_FREQUENCY_FREQUENCY_K500 - case config.Frequency >= 250000: - freq = nrf.SPI_FREQUENCY_FREQUENCY_K250 - default: // below 250kHz, default to the lowest speed available - freq = nrf.SPI_FREQUENCY_FREQUENCY_K125 - } - spi.Bus.FREQUENCY.Set(freq) - - var conf uint32 - - // set bit transfer order - if config.LSBFirst { - conf = (nrf.SPI_CONFIG_ORDER_LsbFirst << nrf.SPI_CONFIG_ORDER_Pos) - } - - // set mode - switch config.Mode { - case 0: - conf &^= (nrf.SPI_CONFIG_CPOL_ActiveHigh << nrf.SPI_CONFIG_CPOL_Pos) - conf &^= (nrf.SPI_CONFIG_CPHA_Leading << nrf.SPI_CONFIG_CPHA_Pos) - case 1: - conf &^= (nrf.SPI_CONFIG_CPOL_ActiveHigh << nrf.SPI_CONFIG_CPOL_Pos) - conf |= (nrf.SPI_CONFIG_CPHA_Trailing << nrf.SPI_CONFIG_CPHA_Pos) - case 2: - conf |= (nrf.SPI_CONFIG_CPOL_ActiveLow << nrf.SPI_CONFIG_CPOL_Pos) - conf &^= (nrf.SPI_CONFIG_CPHA_Leading << nrf.SPI_CONFIG_CPHA_Pos) - case 3: - conf |= (nrf.SPI_CONFIG_CPOL_ActiveLow << nrf.SPI_CONFIG_CPOL_Pos) - conf |= (nrf.SPI_CONFIG_CPHA_Trailing << nrf.SPI_CONFIG_CPHA_Pos) - default: // to mode - conf &^= (nrf.SPI_CONFIG_CPOL_ActiveHigh << nrf.SPI_CONFIG_CPOL_Pos) - conf &^= (nrf.SPI_CONFIG_CPHA_Leading << nrf.SPI_CONFIG_CPHA_Pos) - } - spi.Bus.CONFIG.Set(conf) - - // set pins - spi.setPins(config.SCK, config.SDO, config.SDI) - - // Re-enable bus now that it is configured. - spi.Bus.ENABLE.Set(nrf.SPI_ENABLE_ENABLE_Enabled) -} - -// Transfer writes/reads a single byte using the SPI interface. -func (spi SPI) Transfer(w byte) (byte, error) { - spi.Bus.TXD.Set(uint32(w)) - for spi.Bus.EVENTS_READY.Get() == 0 { - } - r := spi.Bus.RXD.Get() - spi.Bus.EVENTS_READY.Set(0) - - // TODO: handle SPI errors - return byte(r), nil -} - -// 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. -// The Tx method knows about this, and offers a few different ways of calling it. -// -// This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer. -// Note that the tx and rx buffers must be the same size: -// -// spi.Tx(tx, rx) -// -// This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros -// until all the bytes in the command packet have been received: -// -// spi.Tx(tx, nil) -// -// This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet": -// -// spi.Tx(nil, rx) -// -func (spi SPI) Tx(w, r []byte) error { - var err error - - switch { - case len(w) == 0: - // read only, so write zero and read a result. - for i := range r { - r[i], err = spi.Transfer(0) - if err != nil { - return err - } - } - case len(r) == 0: - // write only - spi.Bus.TXD.Set(uint32(w[0])) - w = w[1:] - for _, b := range w { - spi.Bus.TXD.Set(uint32(b)) - for spi.Bus.EVENTS_READY.Get() == 0 { - } - spi.Bus.EVENTS_READY.Set(0) - _ = spi.Bus.RXD.Get() - } - for spi.Bus.EVENTS_READY.Get() == 0 { - } - spi.Bus.EVENTS_READY.Set(0) - _ = spi.Bus.RXD.Get() - - default: - // write/read - if len(w) != len(r) { - return ErrTxInvalidSliceSize - } - - for i, b := range w { - r[i], err = spi.Transfer(b) - if err != nil { - return err - } - } - } - - return nil -} diff --git a/src/machine/machine_nrf51.go b/src/machine/machine_nrf51.go index 464a1a2c34..bbb4f76314 100644 --- a/src/machine/machine_nrf51.go +++ b/src/machine/machine_nrf51.go @@ -29,18 +29,169 @@ func (i2c I2C) setPins(scl, sda Pin) { i2c.Bus.PSELSDA.Set(uint32(sda)) } -// SPI -func (spi SPI) setPins(sck, sdo, sdi Pin) { - if sck == 0 { - sck = SPI0_SCK_PIN +// SPI on the NRF. +type SPI struct { + Bus *nrf.SPI_Type +} + +// There are 2 SPI interfaces on the NRF51. +var ( + SPI0 = SPI{Bus: nrf.SPI0} + SPI1 = SPI{Bus: nrf.SPI1} +) + +// SPIConfig is used to store config info for SPI. +type SPIConfig struct { + Frequency uint32 + SCK Pin + SDO Pin + SDI Pin + LSBFirst bool + Mode uint8 +} + +// Configure is intended to setup the SPI interface. +func (spi SPI) Configure(config SPIConfig) { + // Disable bus to configure it + spi.Bus.ENABLE.Set(nrf.SPI_ENABLE_ENABLE_Disabled) + + // set frequency + var freq uint32 + + if config.Frequency == 0 { + config.Frequency = 4000000 // 4MHz + } + + switch { + case config.Frequency >= 8000000: + freq = nrf.SPI_FREQUENCY_FREQUENCY_M8 + case config.Frequency >= 4000000: + freq = nrf.SPI_FREQUENCY_FREQUENCY_M4 + case config.Frequency >= 2000000: + freq = nrf.SPI_FREQUENCY_FREQUENCY_M2 + case config.Frequency >= 1000000: + freq = nrf.SPI_FREQUENCY_FREQUENCY_M1 + case config.Frequency >= 500000: + freq = nrf.SPI_FREQUENCY_FREQUENCY_K500 + case config.Frequency >= 250000: + freq = nrf.SPI_FREQUENCY_FREQUENCY_K250 + default: // below 250kHz, default to the lowest speed available + freq = nrf.SPI_FREQUENCY_FREQUENCY_K125 + } + spi.Bus.FREQUENCY.Set(freq) + + var conf uint32 + + // set bit transfer order + if config.LSBFirst { + conf = (nrf.SPI_CONFIG_ORDER_LsbFirst << nrf.SPI_CONFIG_ORDER_Pos) } - if sdo == 0 { - sdo = SPI0_SDO_PIN + + // set mode + switch config.Mode { + case 0: + conf &^= (nrf.SPI_CONFIG_CPOL_ActiveHigh << nrf.SPI_CONFIG_CPOL_Pos) + conf &^= (nrf.SPI_CONFIG_CPHA_Leading << nrf.SPI_CONFIG_CPHA_Pos) + case 1: + conf &^= (nrf.SPI_CONFIG_CPOL_ActiveHigh << nrf.SPI_CONFIG_CPOL_Pos) + conf |= (nrf.SPI_CONFIG_CPHA_Trailing << nrf.SPI_CONFIG_CPHA_Pos) + case 2: + conf |= (nrf.SPI_CONFIG_CPOL_ActiveLow << nrf.SPI_CONFIG_CPOL_Pos) + conf &^= (nrf.SPI_CONFIG_CPHA_Leading << nrf.SPI_CONFIG_CPHA_Pos) + case 3: + conf |= (nrf.SPI_CONFIG_CPOL_ActiveLow << nrf.SPI_CONFIG_CPOL_Pos) + conf |= (nrf.SPI_CONFIG_CPHA_Trailing << nrf.SPI_CONFIG_CPHA_Pos) + default: // to mode + conf &^= (nrf.SPI_CONFIG_CPOL_ActiveHigh << nrf.SPI_CONFIG_CPOL_Pos) + conf &^= (nrf.SPI_CONFIG_CPHA_Leading << nrf.SPI_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.PSELSCK.Set(uint32(config.SCK)) + spi.Bus.PSELMOSI.Set(uint32(config.SDO)) + spi.Bus.PSELMISO.Set(uint32(config.SDI)) + + // Re-enable bus now that it is configured. + spi.Bus.ENABLE.Set(nrf.SPI_ENABLE_ENABLE_Enabled) +} + +// Transfer writes/reads a single byte using the SPI interface. +func (spi SPI) Transfer(w byte) (byte, error) { + spi.Bus.TXD.Set(uint32(w)) + for spi.Bus.EVENTS_READY.Get() == 0 { } - if sdi == 0 { - sdi = SPI0_SDI_PIN + r := spi.Bus.RXD.Get() + spi.Bus.EVENTS_READY.Set(0) + + // TODO: handle SPI errors + return byte(r), nil +} + +// 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. +// The Tx method knows about this, and offers a few different ways of calling it. +// +// This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer. +// Note that the tx and rx buffers must be the same size: +// +// spi.Tx(tx, rx) +// +// This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros +// until all the bytes in the command packet have been received: +// +// spi.Tx(tx, nil) +// +// This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet": +// +// spi.Tx(nil, rx) +// +func (spi SPI) Tx(w, r []byte) error { + var err error + + switch { + case len(w) == 0: + // read only, so write zero and read a result. + for i := range r { + r[i], err = spi.Transfer(0) + if err != nil { + return err + } + } + case len(r) == 0: + // write only + spi.Bus.TXD.Set(uint32(w[0])) + w = w[1:] + for _, b := range w { + spi.Bus.TXD.Set(uint32(b)) + for spi.Bus.EVENTS_READY.Get() == 0 { + } + spi.Bus.EVENTS_READY.Set(0) + _ = spi.Bus.RXD.Get() + } + for spi.Bus.EVENTS_READY.Get() == 0 { + } + spi.Bus.EVENTS_READY.Set(0) + _ = spi.Bus.RXD.Get() + + default: + // write/read + if len(w) != len(r) { + return ErrTxInvalidSliceSize + } + + for i, b := range w { + r[i], err = spi.Transfer(b) + if err != nil { + return err + } + } } - spi.Bus.PSELSCK.Set(uint32(sck)) - spi.Bus.PSELMOSI.Set(uint32(sdo)) - spi.Bus.PSELMISO.Set(uint32(sdi)) + + return nil } diff --git a/src/machine/machine_nrf52.go b/src/machine/machine_nrf52.go index 880126a11a..189f0a32a8 100644 --- a/src/machine/machine_nrf52.go +++ b/src/machine/machine_nrf52.go @@ -4,17 +4,48 @@ package machine import ( "device/nrf" - "unsafe" +) + +// Hardware pins +const ( + P0_00 Pin = 0 + P0_01 Pin = 1 + P0_02 Pin = 2 + P0_03 Pin = 3 + P0_04 Pin = 4 + P0_05 Pin = 5 + P0_06 Pin = 6 + P0_07 Pin = 7 + P0_08 Pin = 8 + P0_09 Pin = 9 + P0_10 Pin = 10 + P0_11 Pin = 11 + P0_12 Pin = 12 + P0_13 Pin = 13 + P0_14 Pin = 14 + P0_15 Pin = 15 + P0_16 Pin = 16 + P0_17 Pin = 17 + P0_18 Pin = 18 + P0_19 Pin = 19 + P0_20 Pin = 20 + P0_21 Pin = 21 + P0_22 Pin = 22 + P0_23 Pin = 23 + P0_24 Pin = 24 + P0_25 Pin = 25 + P0_26 Pin = 26 + P0_27 Pin = 27 + P0_28 Pin = 28 + P0_29 Pin = 29 + P0_30 Pin = 30 + P0_31 Pin = 31 ) var ( UART0 = NRF_UART0 ) -func CPUFrequency() uint32 { - return 64000000 -} - // Get peripheral and pin number for this GPIO pin. func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) { return nrf.P0, uint32(p) @@ -30,165 +61,9 @@ func (i2c I2C) setPins(scl, sda Pin) { i2c.Bus.PSELSDA.Set(uint32(sda)) } -// SPI -func (spi SPI) setPins(sck, sdo, sdi Pin) { - if sck == 0 { - sck = SPI0_SCK_PIN - } - if sdo == 0 { - sdo = SPI0_SDO_PIN - } - if sdi == 0 { - sdi = SPI0_SDI_PIN - } - spi.Bus.PSEL.SCK.Set(uint32(sck)) - spi.Bus.PSEL.MOSI.Set(uint32(sdo)) - spi.Bus.PSEL.MISO.Set(uint32(sdi)) -} - -// 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() { - return // no pin specific setup on nrf52 machine. -} - -// Get returns the current value of a ADC pin in the range 0..0xffff. -func (a ADC) Get() uint16 { - var pwmPin uint32 - var value int16 - - switch a.Pin { - case 2: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput0 - - case 3: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput1 - - case 4: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput2 - - case 5: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput3 - - case 28: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput4 - - case 29: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput5 - - case 30: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput6 - - case 31: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput7 - - default: - return 0 - } - - nrf.SAADC.RESOLUTION.Set(nrf.SAADC_RESOLUTION_VAL_12bit) - - // Enable ADC. - nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos) - for i := 0; i < 8; i++ { - nrf.SAADC.CH[i].PSELN.Set(nrf.SAADC_CH_PSELP_PSELP_NC) - nrf.SAADC.CH[i].PSELP.Set(nrf.SAADC_CH_PSELP_PSELP_NC) - } - - // Configure ADC. - nrf.SAADC.CH[0].CONFIG.Set(((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESP_Pos) & nrf.SAADC_CH_CONFIG_RESP_Msk) | - ((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESN_Pos) & nrf.SAADC_CH_CONFIG_RESN_Msk) | - ((nrf.SAADC_CH_CONFIG_GAIN_Gain1_5 << nrf.SAADC_CH_CONFIG_GAIN_Pos) & nrf.SAADC_CH_CONFIG_GAIN_Msk) | - ((nrf.SAADC_CH_CONFIG_REFSEL_Internal << nrf.SAADC_CH_CONFIG_REFSEL_Pos) & nrf.SAADC_CH_CONFIG_REFSEL_Msk) | - ((nrf.SAADC_CH_CONFIG_TACQ_3us << nrf.SAADC_CH_CONFIG_TACQ_Pos) & nrf.SAADC_CH_CONFIG_TACQ_Msk) | - ((nrf.SAADC_CH_CONFIG_MODE_SE << nrf.SAADC_CH_CONFIG_MODE_Pos) & nrf.SAADC_CH_CONFIG_MODE_Msk)) - - // Set pin to read. - nrf.SAADC.CH[0].PSELN.Set(pwmPin) - nrf.SAADC.CH[0].PSELP.Set(pwmPin) - - // Destination for sample result. - nrf.SAADC.RESULT.PTR.Set(uint32(uintptr(unsafe.Pointer(&value)))) - nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample - - // Start tasks. - nrf.SAADC.TASKS_START.Set(1) - for nrf.SAADC.EVENTS_STARTED.Get() == 0 { - } - nrf.SAADC.EVENTS_STARTED.Set(0x00) - - // Start the sample task. - nrf.SAADC.TASKS_SAMPLE.Set(1) - - // Wait until the sample task is done. - for nrf.SAADC.EVENTS_END.Get() == 0 { - } - nrf.SAADC.EVENTS_END.Set(0x00) - - // Stop the ADC - nrf.SAADC.TASKS_STOP.Set(1) - for nrf.SAADC.EVENTS_STOPPED.Get() == 0 { - } - nrf.SAADC.EVENTS_STOPPED.Set(0) - - // Disable the ADC. - nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Disabled << nrf.SAADC_ENABLE_ENABLE_Pos) - - if value < 0 { - value = 0 - } - - // Return 16-bit result from 12-bit value. - return uint16(value << 4) -} - // PWM var ( pwmChannelPins = [3]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF} pwms = [3]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2} pwmChannelSequence [3]uint16 ) - -// InitPWM initializes the registers needed for PWM. -func InitPWM() { - return -} - -// Configure configures a PWM pin for output. -func (pwm PWM) Configure() error { - return nil -} - -// Set turns on the duty cycle for a PWM pin using the provided value. -func (pwm PWM) Set(value uint16) { - for i := 0; i < 3; i++ { - if pwmChannelPins[i] == 0xFFFFFFFF || pwmChannelPins[i] == uint32(pwm.Pin) { - pwmChannelPins[i] = uint32(pwm.Pin) - pwmChannelSequence[i] = (value >> 2) | 0x8000 // set bit 15 to invert polarity - - p := pwms[i] - - p.PSEL.OUT[0].Set(uint32(pwm.Pin)) - p.PSEL.OUT[1].Set(uint32(pwm.Pin)) - p.PSEL.OUT[2].Set(uint32(pwm.Pin)) - p.PSEL.OUT[3].Set(uint32(pwm.Pin)) - p.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos) - p.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_2) - p.MODE.Set(nrf.PWM_MODE_UPDOWN_Up) - p.COUNTERTOP.Set(16384) // frequency - p.LOOP.Set(0) - p.DECODER.Set((nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos)) - p.SEQ[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&pwmChannelSequence[i])))) - p.SEQ[0].CNT.Set(1) - p.SEQ[0].REFRESH.Set(1) - p.SEQ[0].ENDDELAY.Set(0) - p.TASKS_SEQSTART[0].Set(1) - - break - } - } -} diff --git a/src/machine/machine_nrf52840.go b/src/machine/machine_nrf52840.go index 7afb9286ed..157495dc15 100644 --- a/src/machine/machine_nrf52840.go +++ b/src/machine/machine_nrf52840.go @@ -4,13 +4,8 @@ package machine import ( "device/nrf" - "unsafe" ) -func CPUFrequency() uint32 { - return 64000000 -} - // Hardware pins const ( P0_00 Pin = 0 @@ -82,164 +77,9 @@ func (i2c I2C) setPins(scl, sda Pin) { i2c.Bus.PSEL.SDA.Set(uint32(sda)) } -// SPI -func (spi SPI) setPins(sck, sdo, sdi Pin) { - if sck == 0 { - sck = SPI0_SCK_PIN - } - if sdo == 0 { - sdo = SPI0_SDO_PIN - } - if sdi == 0 { - sdi = SPI0_SDI_PIN - } - spi.Bus.PSEL.SCK.Set(uint32(sck)) - spi.Bus.PSEL.MOSI.Set(uint32(sdo)) - spi.Bus.PSEL.MISO.Set(uint32(sdi)) -} - -// InitADC initializes the registers needed for ADC. -func InitADC() { - return // no specific setup on nrf52840 machine. -} - -// Configure configures an ADC pin to be able to read analog data. -func (a ADC) Configure() error { - return nil // no pin specific setup on nrf52840 machine. -} - -// Get returns the current value of a ADC pin in the range 0..0xffff. -func (a ADC) Get() uint16 { - var pwmPin uint32 - var value int16 - - switch a.Pin { - case 2: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput0 - - case 3: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput1 - - case 4: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput2 - - case 5: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput3 - - case 28: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput4 - - case 29: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput5 - - case 30: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput6 - - case 31: - pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput7 - - default: - return 0 - } - - nrf.SAADC.RESOLUTION.Set(nrf.SAADC_RESOLUTION_VAL_12bit) - - // Enable ADC. - nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos) - for i := 0; i < 8; i++ { - nrf.SAADC.CH[i].PSELN.Set(nrf.SAADC_CH_PSELP_PSELP_NC) - nrf.SAADC.CH[i].PSELP.Set(nrf.SAADC_CH_PSELP_PSELP_NC) - } - - // Configure ADC. - nrf.SAADC.CH[0].CONFIG.Set(((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESP_Pos) & nrf.SAADC_CH_CONFIG_RESP_Msk) | - ((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESN_Pos) & nrf.SAADC_CH_CONFIG_RESN_Msk) | - ((nrf.SAADC_CH_CONFIG_GAIN_Gain1_5 << nrf.SAADC_CH_CONFIG_GAIN_Pos) & nrf.SAADC_CH_CONFIG_GAIN_Msk) | - ((nrf.SAADC_CH_CONFIG_REFSEL_Internal << nrf.SAADC_CH_CONFIG_REFSEL_Pos) & nrf.SAADC_CH_CONFIG_REFSEL_Msk) | - ((nrf.SAADC_CH_CONFIG_TACQ_3us << nrf.SAADC_CH_CONFIG_TACQ_Pos) & nrf.SAADC_CH_CONFIG_TACQ_Msk) | - ((nrf.SAADC_CH_CONFIG_MODE_SE << nrf.SAADC_CH_CONFIG_MODE_Pos) & nrf.SAADC_CH_CONFIG_MODE_Msk)) - - // Set pin to read. - nrf.SAADC.CH[0].PSELN.Set(pwmPin) - nrf.SAADC.CH[0].PSELP.Set(pwmPin) - - // Destination for sample result. - nrf.SAADC.RESULT.PTR.Set(uint32(uintptr(unsafe.Pointer(&value)))) - nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample - - // Start tasks. - nrf.SAADC.TASKS_START.Set(1) - for nrf.SAADC.EVENTS_STARTED.Get() == 0 { - } - nrf.SAADC.EVENTS_STARTED.Set(0x00) - - // Start the sample task. - nrf.SAADC.TASKS_SAMPLE.Set(1) - - // Wait until the sample task is done. - for nrf.SAADC.EVENTS_END.Get() == 0 { - } - nrf.SAADC.EVENTS_END.Set(0x00) - - // Stop the ADC - nrf.SAADC.TASKS_STOP.Set(1) - for nrf.SAADC.EVENTS_STOPPED.Get() == 0 { - } - nrf.SAADC.EVENTS_STOPPED.Set(0) - - // Disable the ADC. - nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Disabled << nrf.SAADC_ENABLE_ENABLE_Pos) - - if value < 0 { - value = 0 - } - - // Return 16-bit result from 12-bit value. - return uint16(value << 4) -} - // PWM var ( pwmChannelPins = [4]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF} pwms = [4]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2, nrf.PWM3} pwmChannelSequence [4]uint16 ) - -// InitPWM initializes the registers needed for PWM. -func InitPWM() { - return -} - -// Configure configures a PWM pin for output. -func (pwm PWM) Configure() { -} - -// Set turns on the duty cycle for a PWM pin using the provided value. -func (pwm PWM) Set(value uint16) { - for i := 0; i < 4; i++ { - if pwmChannelPins[i] == 0xFFFFFFFF || pwmChannelPins[i] == uint32(pwm.Pin) { - pwmChannelPins[i] = uint32(pwm.Pin) - pwmChannelSequence[i] = (value >> 2) | 0x8000 // set bit 15 to invert polarity - - p := pwms[i] - - p.PSEL.OUT[0].Set(uint32(pwm.Pin)) - p.PSEL.OUT[1].Set(uint32(pwm.Pin)) - p.PSEL.OUT[2].Set(uint32(pwm.Pin)) - p.PSEL.OUT[3].Set(uint32(pwm.Pin)) - p.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos) - p.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_2) - p.MODE.Set(nrf.PWM_MODE_UPDOWN_Up) - p.COUNTERTOP.Set(16384) // frequency - p.LOOP.Set(0) - p.DECODER.Set((nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos)) - p.SEQ[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&pwmChannelSequence[i])))) - p.SEQ[0].CNT.Set(1) - p.SEQ[0].REFRESH.Set(1) - p.SEQ[0].ENDDELAY.Set(0) - p.TASKS_SEQSTART[0].Set(1) - - break - } - } -} diff --git a/src/machine/machine_nrf528xx.go b/src/machine/machine_nrf528xx.go new file mode 100644 index 0000000000..76409522ca --- /dev/null +++ b/src/machine/machine_nrf528xx.go @@ -0,0 +1,296 @@ +// +build nrf52 nrf52840 + +package machine + +import ( + "device/nrf" + "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() { + return // no pin specific setup on nrf52 machine. +} + +// Get returns the current value of a ADC pin in the range 0..0xffff. +func (a ADC) Get() uint16 { + var pwmPin uint32 + var value int16 + + switch a.Pin { + case 2: + pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput0 + + case 3: + pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput1 + + case 4: + pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput2 + + case 5: + pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput3 + + case 28: + pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput4 + + case 29: + pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput5 + + case 30: + pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput6 + + case 31: + pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput7 + + default: + return 0 + } + + nrf.SAADC.RESOLUTION.Set(nrf.SAADC_RESOLUTION_VAL_12bit) + + // Enable ADC. + nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos) + for i := 0; i < 8; i++ { + nrf.SAADC.CH[i].PSELN.Set(nrf.SAADC_CH_PSELP_PSELP_NC) + nrf.SAADC.CH[i].PSELP.Set(nrf.SAADC_CH_PSELP_PSELP_NC) + } + + // Configure ADC. + nrf.SAADC.CH[0].CONFIG.Set(((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESP_Pos) & nrf.SAADC_CH_CONFIG_RESP_Msk) | + ((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESN_Pos) & nrf.SAADC_CH_CONFIG_RESN_Msk) | + ((nrf.SAADC_CH_CONFIG_GAIN_Gain1_5 << nrf.SAADC_CH_CONFIG_GAIN_Pos) & nrf.SAADC_CH_CONFIG_GAIN_Msk) | + ((nrf.SAADC_CH_CONFIG_REFSEL_Internal << nrf.SAADC_CH_CONFIG_REFSEL_Pos) & nrf.SAADC_CH_CONFIG_REFSEL_Msk) | + ((nrf.SAADC_CH_CONFIG_TACQ_3us << nrf.SAADC_CH_CONFIG_TACQ_Pos) & nrf.SAADC_CH_CONFIG_TACQ_Msk) | + ((nrf.SAADC_CH_CONFIG_MODE_SE << nrf.SAADC_CH_CONFIG_MODE_Pos) & nrf.SAADC_CH_CONFIG_MODE_Msk)) + + // Set pin to read. + nrf.SAADC.CH[0].PSELN.Set(pwmPin) + nrf.SAADC.CH[0].PSELP.Set(pwmPin) + + // Destination for sample result. + nrf.SAADC.RESULT.PTR.Set(uint32(uintptr(unsafe.Pointer(&value)))) + nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample + + // Start tasks. + nrf.SAADC.TASKS_START.Set(1) + for nrf.SAADC.EVENTS_STARTED.Get() == 0 { + } + nrf.SAADC.EVENTS_STARTED.Set(0x00) + + // Start the sample task. + nrf.SAADC.TASKS_SAMPLE.Set(1) + + // Wait until the sample task is done. + for nrf.SAADC.EVENTS_END.Get() == 0 { + } + nrf.SAADC.EVENTS_END.Set(0x00) + + // Stop the ADC + nrf.SAADC.TASKS_STOP.Set(1) + for nrf.SAADC.EVENTS_STOPPED.Get() == 0 { + } + nrf.SAADC.EVENTS_STOPPED.Set(0) + + // Disable the ADC. + nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Disabled << nrf.SAADC_ENABLE_ENABLE_Pos) + + if value < 0 { + value = 0 + } + + // Return 16-bit result from 12-bit value. + return uint16(value << 4) +} + +// SPI on the NRF. +type SPI struct { + Bus *nrf.SPIM_Type +} + +// There are 3 SPI interfaces on the NRF528xx. +var ( + SPI0 = SPI{Bus: nrf.SPIM0} + SPI1 = SPI{Bus: nrf.SPIM1} + SPI2 = SPI{Bus: nrf.SPIM2} +) + +// SPIConfig is used to store config info for SPI. +type SPIConfig struct { + Frequency uint32 + SCK Pin + SDO Pin + SDI Pin + LSBFirst bool + Mode uint8 +} + +// Configure is intended to setup the SPI interface. +func (spi SPI) Configure(config SPIConfig) { + // Disable bus to configure it + spi.Bus.ENABLE.Set(nrf.SPIM_ENABLE_ENABLE_Disabled) + + // Pick a default frequency. + if config.Frequency == 0 { + config.Frequency = 4000000 // 4MHz + } + + // set frequency + var freq uint32 + switch { + case config.Frequency >= 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) +} + +// Transfer writes/reads a single byte using the SPI interface. +func (spi SPI) Transfer(w byte) (byte, error) { + var wbuf, rbuf [1]byte + wbuf[0] = w + err := spi.Tx(wbuf[:], rbuf[:]) + return rbuf[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. + if len(r) != 0 { + spi.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0])))) + n := uint32(len(r)) + if n > 255 { + n = 255 + } + spi.Bus.RXD.MAXCNT.Set(n) + r = r[n:] + } + if len(w) != 0 { + spi.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0])))) + n := uint32(len(w)) + if n > 255 { + n = 255 + } + spi.Bus.TXD.MAXCNT.Set(n) + w = w[n:] + } + + // 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 +} + +// InitPWM initializes the registers needed for PWM. +func InitPWM() { + return +} + +// Configure configures a PWM pin for output. +func (pwm PWM) Configure() { +} + +// Set turns on the duty cycle for a PWM pin using the provided value. +func (pwm PWM) Set(value uint16) { + for i := 0; i < len(pwmChannelPins); i++ { + if pwmChannelPins[i] == 0xFFFFFFFF || pwmChannelPins[i] == uint32(pwm.Pin) { + pwmChannelPins[i] = uint32(pwm.Pin) + pwmChannelSequence[i] = (value >> 2) | 0x8000 // set bit 15 to invert polarity + + p := pwms[i] + + p.PSEL.OUT[0].Set(uint32(pwm.Pin)) + p.PSEL.OUT[1].Set(uint32(pwm.Pin)) + p.PSEL.OUT[2].Set(uint32(pwm.Pin)) + p.PSEL.OUT[3].Set(uint32(pwm.Pin)) + p.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos) + p.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_2) + p.MODE.Set(nrf.PWM_MODE_UPDOWN_Up) + p.COUNTERTOP.Set(16384) // frequency + p.LOOP.Set(0) + p.DECODER.Set((nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos)) + p.SEQ[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&pwmChannelSequence[i])))) + p.SEQ[0].CNT.Set(1) + p.SEQ[0].REFRESH.Set(1) + p.SEQ[0].ENDDELAY.Set(0) + p.TASKS_SEQSTART[0].Set(1) + + break + } + } +}