Nucleo H563ZI SPI Comms with an ADC: Works with HAL Interrupt APIs, Not with DMA APIs
Hi all - I have a problem getting DMA working with SPI, for communication with an MCP3564R ADC as one task in a complex project running on FreeRTOS. I have made a stripped-down version of the project that just does the ADC reading task. It runs on a bare Nucleo board and illustrates the basic problem which shows up from the first transmit, so a connected ADC isn’t needed to see the problem.
The code uses SPI3 at 20MHz (although the problem occurs at lower speeds as well)

The GPIO pins I’m using are not the defaults:

GPDMA1 is configured with two channels for SPI3 TX and RX:

My code can be configured to use the HAL SPI DMA APIs or the interrupt APIs like so:
static uint8_t write_register(const uint8_t * reg_write_cmd_p, uint8_t num_bytes)
{
HAL_StatusTypeDef hal_status;
BaseType_t wait_ret_val;
uint32_t notify_bits;
uint8_t ret_val = 0; /* Failure */
#if (MCP3564_HAL_SPI_MODE == MCP3564_USE_HAL_SPI_DMA_MODE)
hal_status = HAL_SPI_Transmit_DMA(&ADC_SPI_PERIPH_HANDLE, reg_write_cmd_p, num_bytes);
#elif (MCP3564_HAL_SPI_MODE == MCP3564_USE_HAL_SPI_INT_MODE)
hal_status = HAL_SPI_Transmit_IT(&ADC_SPI_PERIPH_HANDLE, reg_write_cmd_p, num_bytes);
#else
#error "unexpected MCP3564_HAL_SPI_MODE"
#endif
if (HAL_OK == hal_status)
{
wait_ret_val = xTaskNotifyWait(0x0, ADC_TASK_NOTIFY_TX_COMPLETE, ¬ify_bits,
pdMS_TO_TICKS(ADC_HAL_SPI_TIMEOUT_MSEC));
if ((pdTRUE == wait_ret_val) && (notify_bits & ADC_TASK_NOTIFY_TX_COMPLETE))
{
ret_val = 1;
}
}
return ret_val;
}
When I have it building for the interrupt APIs, the first transmission looks just like I expect (note that the pulse edges look more sawtooth than square because I’m getting close to the maximum time resolution of my Analog Discovery USB scope, but it still decodes OK):

When I have it building for the DMA APIs, I’m only seeing zero go out:

I had all this code (and much more) working quite nicely at one time but then the hardware design I’m targeting changed and I had to relocate some pins. DMA hasn’t worked right since despite trying to build an .ioc file back up starting with the basic Nucleo board.
I’ve grilled Google AI about this problem and it has had many suggestions but most of them turned out to be mistaken - for example, it insists that the problem is likely the data cache, although on the STM32H563 family parts my understanding is this could not be an issue due to a difference in the DMA architecture. In any case the cache is not enabled. I worked through many more suggestions from Google AI and they became increasingly far-fetched and incorrect and it stopped answering me.
If anyone has this Nucleo board and would like to look at the project, or step through the code, I have placed the project on GitHub here: https://github.com/paulrpotts/nucleo-h563zi-adc-MCP3564R
Without hardware connected like my hand-built prototype board with the ADC, of course the ADC will not answer and the whole ADC monitoring task will not run, but the failure happens right with the first call to write_register() in task_adc_MCP3564R_test.c line 317. The #define to build for DMA APIs or interrupt APIs is in adc_MCP3564R_config.h at line 100.
Any suggestions for how to get past this non-working DMA appreciated, even just getting one initial write working and seeing the expected data on the SPI bus would be progress.
Thanks,
Paul R. Potts
