Your approach sounds right at the first glance. The problem seemingly is libraries and autogeneration which claim to make things easier but apparently makes everything harder because without them, you'd have tested this out in less time than it took to write the forum post.
So open up the reference manual. As for DMA, assuming the F303 has the same DMA engine as the F3, F4, F7, H7 MCUs I have worked with (F0 devices have a different, even simpler DMA), there is not much to do to configure it: set M0AR, PAR, NDTR, CR, clear interrupt flags through LIFCR, and enable the channel through CR.
DMA2_Stream1->M0AR = (uint32_t)&databuf[0];
DMA2_Stream1->PAR = (uint32_t)&SPIwhatever->RXDR;
DMA2_Stream1->NDTR = 1234;
DMA2_Stream1->CR = 0UL<<25 /* the channel number, see the table in refman*/ |
0b01UL<<16 /*med prio*/ | 0b01UL<<13 /*16-bit mem*/ | 0b01UL<<11 /*16-bit periph*/ |
1UL<<10 /*mem increment*/ | 0b00UL<<6 /*periph-to-mem*/ | 0b110 /*err interrupts*/ | 1UL<<8 /*circular*/;
DMA_CLEAR_INTFLAGS(DMA2, 1);
DMA2_Stream1->CR |= 1UL; // enable it!
where DMA_CLEAR_INTFLAGS() is a helper I created once:
#define DMA_CLEAR_INTFLAGS(_dma_, _stream_) do{ \
if ((_stream_) == 0) (_dma_)->LIFCR = 0b111101UL<<0; \
else if((_stream_) == 1) (_dma_)->LIFCR = 0b111101UL<<6; \
else if((_stream_) == 2) (_dma_)->LIFCR = 0b111101UL<<16; \
else if((_stream_) == 3) (_dma_)->LIFCR = 0b111101UL<<22; \
else if((_stream_) == 4) (_dma_)->HIFCR = 0b111101UL<<0; \
else if((_stream_) == 5) (_dma_)->HIFCR = 0b111101UL<<6; \
else if((_stream_) == 6) (_dma_)->HIFCR = 0b111101UL<<16; \
else if((_stream_) == 7) (_dma_)->HIFCR = 0b111101UL<<22; \
}while(0)
Don't forget to
* Enable clock to DMA
* Enable clock to SPI
* Set SPI pins in Alternative Function mode with the right AF number
* Look up the correct DMA channel number for DMA CR register, to map the right SPI to the DMA
* Enable the DMA mode bit in the peripheral (in this case, SPI) config
As for SPI, most STM32 devices are still broken beyond laughable in how they handle hardware nCS. But if you have truly constant length communication and are not expecting any communication errors like a missed clock, it will work. For variable-length messaging (or a robust system), to make the rising nCS reset the SPI state machine, which is the basics of the basics, you need to write an interrupt handler triggered by rising nCS through EXTI, then on that handler, reset the whole SPI peripheral through the RCC reset register! On the newest STM32 models, they have semi-fixed this issue so that you can now reset the SPI by using the peripheral's own enable bit, still a long way to go to do it right.
Finally, STM32F303 has SPI FIFOs. It may be worth considering not using DMA, especially if your communication packet is short enough to fit in SPI FIFOs completely. In this case, you can enable the SPI peripheral in nCS falling ISR, and read out everything in 32-bit bus reads in nCS rising ISR; especially if you would process the data anyway after full reception. Though, in your pattern, make the SPI automagically update memory region with no sync/atomicity concerns, this would be extra overhead.