From e7ba07dd5ad1760feaf392e788c0754bdc1a9e8c Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Tue, 17 Jan 2023 04:32:01 -0700 Subject: [PATCH] Add PDM support for circuitplay-bluefruit (#3359) machine/nrf52840: add PDM support Signed-off-by: Marcus Sorensen --- src/examples/pdm/pdm.go | 27 +++++++++ src/machine/board_circuitplay_bluefruit.go | 6 ++ src/machine/machine_nrf52840.go | 70 ++++++++++++++++++++++ src/machine/pdm.go | 7 +++ 4 files changed, 110 insertions(+) create mode 100644 src/examples/pdm/pdm.go create mode 100644 src/machine/pdm.go diff --git a/src/examples/pdm/pdm.go b/src/examples/pdm/pdm.go new file mode 100644 index 00000000..c48a0a17 --- /dev/null +++ b/src/examples/pdm/pdm.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + "machine" +) + +var ( + audio = make([]int16, 16) + pdm = machine.PDM{} +) + +func main() { + machine.BUTTONA.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) + err := pdm.Configure(machine.PDMConfig{CLK: machine.PDM_CLK_PIN, DIN: machine.PDM_DIN_PIN}) + if err != nil { + panic(fmt.Sprintf("Failed to configure PDM:%v", err)) + } + + for { + if machine.BUTTONA.Get() { + println("Recording new audio clip into memory") + pdm.Read(audio) + println(fmt.Sprintf("Recorded new audio clip into memory: %v", audio)) + } + } +} diff --git a/src/machine/board_circuitplay_bluefruit.go b/src/machine/board_circuitplay_bluefruit.go index d578760b..a133003c 100644 --- a/src/machine/board_circuitplay_bluefruit.go +++ b/src/machine/board_circuitplay_bluefruit.go @@ -73,6 +73,12 @@ const ( SPI0_SDI_PIN = P0_23 // SDI ) +// PDM pins +const ( + PDM_CLK_PIN = P0_17 // CLK + PDM_DIN_PIN = P0_16 // DIN +) + // USB CDC identifiers const ( usb_STRING_PRODUCT = "Adafruit Circuit Playground Bluefruit" diff --git a/src/machine/machine_nrf52840.go b/src/machine/machine_nrf52840.go index 005bf252..d38ebbe5 100644 --- a/src/machine/machine_nrf52840.go +++ b/src/machine/machine_nrf52840.go @@ -4,6 +4,8 @@ package machine import ( "device/nrf" + "errors" + "unsafe" ) // Get peripheral and pin number for this GPIO pin. @@ -32,3 +34,71 @@ var ( PWM2 = &PWM{PWM: nrf.PWM2} PWM3 = &PWM{PWM: nrf.PWM3} ) + +// PDM represents a PDM device +type PDM struct { + device *nrf.PDM_Type + defaultBuffer int16 +} + +// Configure is intended to set up the PDM interface prior to use. +func (pdm *PDM) Configure(config PDMConfig) error { + if config.DIN == 0 { + return errors.New("No DIN pin provided in configuration") + } + + if config.CLK == 0 { + return errors.New("No CLK pin provided in configuration") + } + + config.DIN.Configure(PinConfig{Mode: PinInput}) + config.CLK.Configure(PinConfig{Mode: PinOutput}) + pdm.device = nrf.PDM + pdm.device.PSEL.DIN.Set(uint32(config.DIN)) + pdm.device.PSEL.CLK.Set(uint32(config.CLK)) + pdm.device.PDMCLKCTRL.Set(nrf.PDM_PDMCLKCTRL_FREQ_Default) + pdm.device.RATIO.Set(nrf.PDM_RATIO_RATIO_Ratio64) + pdm.device.GAINL.Set(nrf.PDM_GAINL_GAINL_DefaultGain) + pdm.device.GAINR.Set(nrf.PDM_GAINR_GAINR_DefaultGain) + pdm.device.ENABLE.Set(nrf.PDM_ENABLE_ENABLE_Enabled) + + if config.Stereo { + pdm.device.MODE.Set(nrf.PDM_MODE_OPERATION_Stereo | nrf.PDM_MODE_EDGE_LeftRising) + } else { + pdm.device.MODE.Set(nrf.PDM_MODE_OPERATION_Mono | nrf.PDM_MODE_EDGE_LeftRising) + } + + pdm.device.SAMPLE.SetPTR(uint32(uintptr(unsafe.Pointer(&pdm.defaultBuffer)))) + pdm.device.SAMPLE.SetMAXCNT_BUFFSIZE(1) + pdm.device.SetTASKS_START(1) + return nil +} + +// Read stores a set of samples in the given target buffer. +func (pdm *PDM) Read(buf []int16) (uint32, error) { + pdm.device.SAMPLE.SetPTR(uint32(uintptr(unsafe.Pointer(&buf[0])))) + pdm.device.SAMPLE.MAXCNT.Set(uint32(len(buf))) + pdm.device.EVENTS_STARTED.Set(0) + + // Step 1: wait for new sampling to start for target buffer + for !pdm.device.EVENTS_STARTED.HasBits(nrf.PDM_EVENTS_STARTED_EVENTS_STARTED) { + } + pdm.device.EVENTS_END.Set(0) + + // Step 2: swap out buffers for next recording so we don't continue to + // write to the target buffer + pdm.device.EVENTS_STARTED.Set(0) + pdm.device.SAMPLE.SetPTR(uint32(uintptr(unsafe.Pointer(&pdm.defaultBuffer)))) + pdm.device.SAMPLE.MAXCNT.Set(1) + + // Step 3: wait for original event to end + for pdm.device.EVENTS_END.HasBits(nrf.PDM_EVENTS_STOPPED_EVENTS_STOPPED) { + } + + // Step 4: wait for default buffer to start recording before proceeding + // otherwise we see the contents of target buffer change later + for !pdm.device.EVENTS_STARTED.HasBits(nrf.PDM_EVENTS_STARTED_EVENTS_STARTED) { + } + + return uint32(len(buf)), nil +} diff --git a/src/machine/pdm.go b/src/machine/pdm.go new file mode 100644 index 00000000..e57ad0f6 --- /dev/null +++ b/src/machine/pdm.go @@ -0,0 +1,7 @@ +package machine + +type PDMConfig struct { + Stereo bool + DIN Pin + CLK Pin +}