SPI DMA problems #16455
-
IntentionSo I have been trying to see whether I could use the new DMA micropython module to interface a MAX31865 SPI RTD sensing chip to a Pico 2, in such a way that I can periodically read temperature values from the chip and write them to a memory buffer, all with essentially no intervention with the main processor. CodeAfter a fair bit of reading of the RP2350 datasheet, I was able to come up with the following code: # Constants
SPI1_BASE = 0x40088000
SSPDR_OFFSET = 0x008
SPI1_SSPDR = SPI1_BASE + SSPDR_OFFSET # SPI1 data register
DREQ_SPI_RX = 27 # Receive DREQ for SPI->DMA
DREQ_SPI1_TX = 26 # Transmit DREQ for SPI->DMA
# Read and write registers
tx_data = bytearray([0x01, 0x00, 0x00, 0x00]) # Address to read on MAX31865 chip
rx_data = bytearray(4) # Buffer to write temperature data to
# Setup DMA channels
tx_dma = rp2.DMA() # DMA controlling output of SPI MOSI
rx_dma = rp2.DMA() # DMA receiving input of SPI MISO
# SPI chip select pin
cs_1 = Pin(SPI_CS_PIN_1, mode=Pin.PULL_UP)
cs_1.value(1)
# Configure DMA channels
tx_config = tx_dma.pack_ctrl(
size = 0, # 8-bit transfers
inc_read = False, # always read tx_data
inc_write = False, # always write to SPI1_SSPDR
treq_sel = DREQ_SPI1_TX # pace transfers according to when byte is sent on SPI bus
)
rx_config = rx_dma.pack_ctrl(
size = 0,
inc_read = False, # always read from SPI1_SSPDR
inc_write = True, # update each byte of rx_data
ring_size = 2, # wrap back to start of rx_data after 4th bit,
ring_sel = True, # apply ring address mask to write address
treq_sel = DREQ_SPI1_RX # pace transfers according to when byte is received on SPI bus
)
tx_dma.config(
read = tx_data, # Address of register to read on MAX31865
write = SPI1_SSPDR, # SPI1 data register => SPI1 TX FIFO
count = 4, # transfer 4 bytes
ctrl = tx_config, # DMA control register data
trigger = False # Don't start SPI transaction yet
)
rx_dma.config(
read = SPI1_SSPDR, # SPI1 data register <= SPI1 RX FIFO
write = rx_data, # Received temperature value
count = 4, # transfer 4 bytes
ctrl = rx_config, # DMA control register data
trigger = False # Don't start SPI transaction yet
)
def write_spi():
cs_1.value(0) # Low enable chip select
tx_dma.active(1) # Send address bytes to SPI controller
cs_1.value(1) # Stop transaction
# SPI transaction
rx_dma.active(1) # Start listening for bytes to be read in by SPI controller
write_spi() # Start SPI transaction which I was very happy to see would result in the appropriate temperature (resistance, actually) value appearing in ProblemThe only problem is, after the first transaction, attempting to restart # First transaction:
rx_dma.active(1) # Returns True (as expected)
rx_dma.active() # Returns True (as expected)
rx_dma.count # Returns 4 (as expected)
write_spi() # actual data: v v
rx_data # Returns correct data e.g [0x00, 0x40, 0xbe, 0x00] (as expected)
rx_dma.active() # Returns False (as expected)
rx_dma.count # Returns 0 (as expected)
tx_dma.active() # Returns False (as expected)
tx_dma.count # Returns 0 (as expected)
# Attempt second transaction:
rx_dma.active(1) # Returns False (unexpected)
rx_dma.active() # Returns False (unexpected)
rx_dma.count # Returns 0 (unexpected)
rx_data # Returns [0x00, 0x00, 0x00, 0x00] (unexpected) and I am unable to re-activate the receive DMA channel (or, when it is activated, it immediately thinks it has received 4 Funnily enough, I found that if I used the Micropython spi = SPI(...)
cs_1.value(0)
spi.write(0x01)
spi.read_into(rx_data)
cs_1.value(1) then rx_dma.active(1) # Returns True once again. However, if I call The issue?Having looked at the C code in My hunch, having read the RP2350 datasheet, was that this might have something to do with IRQs being generated by the SPI controller which are cleared when the Investigating the status registers for the SPI 1 controller whilst it was in its 'working state', I noticed that the following two registers:
had different values when in the 'unresponsive state': # Working state
bin(machine.mem32[SPI1_SSPSR]) # Returns 0b11
bin(machine.mem32[SPI1_SSPRIS]) # Returns 0b1000
# Unresponsive state
bin(machine.mem32[SPI1_SSPSR]) # Returns 0b111
bin(machine.mem32[SPI1_SSPRIS]) # Returns 0b1110 I wasn't sure if this is the reason I'm not able to reactivate the DMA channels - because the SSPRXINTR/SSPRTINTR interrupts remain asserted? The thing is, I'm not sure how to de-assert them; writing I should note that both DMA channels are set to |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
Ok, I think I've worked out the problem here. I think my use of the standard After deleting all the python objects instantiated from the Testing this theory a bit further, I tried creating and deleting the It is a little surprising that the rx_dma = rp2.DMA(5) # Use DMA channel 5 in order to avoid these kind of conflicts. |
Beta Was this translation helpful? Give feedback.
-
A couple of queries (from a non-expert) tx_config = tx_dma.pack_ctrl(
size = 0, # 8-bit transfers
inc_read = False, # always read tx_data
inc_write = False, # always write to SPI1_SSPDR
treq_sel = DREQ_SPI1_TX # pace transfers according to when byte is sent on SPI bus
) Shouldn't There may be a race condition here: def write_spi():
cs_1.value(0) # Low enable chip select
tx_dma.active(1) # Send address bytes to SPI controller
cs_1.value(1) # Stop transaction If |
Beta Was this translation helpful? Give feedback.
Ok, I think I've worked out the problem here.
I think my use of the standard
SPI
module together with the direct DMA configuration may have caused a conflict, withSPI
trying to use the same DMA channels as my code above (channels 0 and 1).After deleting all the python objects instantiated from the
SPI
module, when I created two newrp2.DMA
objects, they were assigned to DMA channels 4 and 5. Using these channels, the unresponsive state doesn't appear and I was able to reactivaterx_dma
multiple times without any problems.Testing this theory a bit further, I tried creating and deleting the
rp2.DMA
instances assigned to channel 0 and 1 and then creating two new ones, assigned to channel …