Page 3 of 3
Re: DMA for SPI
Posted: Sat Feb 01, 2025 5:41 am
by ag123
updated SPI.h
https://github.com/ag88/stm32duino_spi_ ... /SPI/SPI.h
- added externs to SPI instance (this defaults to SPI 1)
- SPIDMA classes are different for each series and SPIDMA uses a different/specific class for each SPI peripheral.
Typedefs are defined for SPIClass1, SPIClass2, SPIClass3 in spi.h
This is so that codes like
Code: Select all
SPIClass1 spi1;
SPIClass2 spi2;
SPIClass3 spi3;
should work, and does not need to reference the specific class name for each series and peripheral.
Note that each SPIClassN is actually derived from SPIClass, hence they can be passed to functions / methods
that use SPIClass for the SPI api.
each SPIClass1, SPIClass2, SPIClass3 connects to the specific SPI peripheral 1, 2, 3 respectively
This is still untested codes, but just compiled.
If you do test that, do drop a comment about it here or in github discussions etc.
edit:
there is a 'catch' in that actually for series that do not have a SPIDMA class, it uses SPIBasic which is generic (across SPI 1, 2, 3) and follows the original SPIClass definition instead. But that if no pin parameters are given, it defaults to SPI1.
I'd need to figure out how to make the declarations similar.
edit2:
this is done, SPIClass2 spi2; and SPIClass3 spi3; now works for SPIBasic based class as well, it is done by deriving a class and passing default pins to the constructor.
SPIBasic remains as a generic SPIClass implementation that can be used for any SPI peripherals.
SPIDMA implementations are specific to each SPI peripheral, i.e. it isn't possible to use SPIClass2 for SPIClass1 and vice versa.
I'm also thinking about in the constructor to add a parameter to specify the particular SPI peripheral used (e.g. using the SPI_BASE symbol for the register base address). This would deviate from the current constructor, but that it could make defaulting behaviour simpler to implement.
Such an implementation can enable specific pins tied to the SPI peripheral by default and helps avoid usage errors binding the incorrect pins,
Re: DMA for SPI
Posted: Mon Feb 03, 2025 2:12 pm
by ag123
refactor getClkFreq, SPIDMA use generic getClkFreq from SPIBasic
https://github.com/ag88/stm32duino_spi_ ... A.cpp#L300
Re: DMA for SPI
Posted: Tue Feb 04, 2025 10:58 am
by ag123
added stm32f7xx
https://github.com/ag88/stm32duino_spi_ ... DMA_F7XX.h
https://github.com/ag88/stm32duino_spi_ ... A_F7XX.cpp
this is based on stm32f722rc generic variant, this is still untested codes, just compiled
stm32f7(22rc) is practically similar to/same as f4xx for the DMA
it has both channels and streams, but unlike H7, H5, it does not use a DMA MUX.
Hence, the channels/streams are pre-bound to DMA specifically as like in F4.
stm32f7(22rc) has an SPI with FIFO, but that instead of using a different structure / register as like H5, H7 it uses DR (data register),
and similar flags as TXE (transmit buf empty), RXNE (receive buf not empty), to flag the status.
Re: DMA for SPI
Posted: Tue Feb 04, 2025 3:20 pm
by ag123
added stm32L4xx
https://github.com/ag88/stm32duino_spi_ ... DMA_L4XX.h
https://github.com/ag88/stm32duino_spi_ ... A_L4XX.cpp
this is based on stm32L431cc generic variant, this is still untested codes, just compiled
stm32L4(31cc) is again different, it looks a little like stm32f3xx, but different. There is a mux at each channel, which CubeMX generaged codes call it a 'request', yet this is different from streams as like those in F4xx which are parallel. But in L4xx, I think it is a single DMA thread, just that the priorities are different. I'd guess it is partly as it is after all a 'low power' mcu. Hence, using parallel streams would likely consume more power.
stm32L4(31cc) SPI peripheral is similar to F7, in that it has a FIFO and unlike H5, H7 which uses different register design for FIFO.
stm32L4(31cc) SPI uses the 'old' F4 style DR register and TXE (transmit buf empty), RXNE (receive buf not empty) flags.
I'd guess the FIFO if used correctly could help with latency issues. But that the current implementation is 'simple' in a sense that it is just reading the byte (buffer) in the same way as if it is f4/f3 etc.
Re: DMA for SPI
Posted: Tue Feb 04, 2025 3:39 pm
by ag123
edit:
updated Readme.md
https://github.com/ag88/stm32duino_spi_dma
--
did a round of spihandle bug fixes
https://github.com/ag88/stm32duino_spi_ ... 0436f19a90
another round of bug fixes
https://github.com/ag88/stm32duino_spi_ ... d94fa76925
^ well, to get differentially faster speeds, I hit the registers this time round, and all the hairy bummers surface
avoiding stacking and unstacking by avoiding function calls can save quite a bit of cpu cycles when transmitting / receiving a whole buffer of data.
I'd stop changing or adding things (e.g. other series) at least for a while as after all, I've yet to test anything.
And that stm32{F,G,H,L}xxyy are 'cherry picked' single soc variants used to build and compile the codes.
It is unknown if within the series if they may vary.
The assumption is that 'cherry picking' a smaller mcu in the series, for the build, and 'bigger' chips would likely have same/similar architecture for the spi and dma peripherals, but still it is a guess.
Re: DMA for SPI
Posted: Sat Jul 12, 2025 7:49 am
by trimarco232
Hi,
I recently wrote a piece of code using DMA and SPI , the goal is to command WS2812b chips , using just a MOSI output of a BluePill+ (WeAct , hope this is an original STM32F103C8T6)
I am writing directly into the registers , so I know exactly what I am doing , at least I hope so.
But here , I had to set the SPI_CR2_SSOE bit , in order to finally make this to work ... and I don't know why ?
Code: Select all
/* spi_dma_ws.h */
void spi_dma_transmit();
extern uint16_t adc_pa5;
// some colours
const uint8_t ws_off[3] = { 0, 0, 0 };
const uint8_t ws_green[3] = { 25, 0, 0 };
const uint8_t ws_red[3] = { 0, 25, 0 };
const uint8_t ws_blue[3] = { 0, 0, 25 };
const uint8_t ws_white[3] = { 25, 25, 25 };
const uint8_t ws_purple[3] = { 0, 25, 25 };
const uint8_t ws_yell[3] = { 25, 25, 0 };
const uint8_t ws_turc[3] = { 25, 0, 25 };
uint8_t ws3[nb_ws][3]; // has the 3 colours of each RGB chip
uint8_t ws33[nb_ws * 3 * 3]; // has the 3 SPI bytes for each the 3 colours of each RGB chip
/// example with some colours
/*
void ws_3() {
for (int c = 0; c < 3; c++) {
ws3[0][c] = ws_green[c];
ws3[1][c] = ws_red[c];
ws3[2][c] = ws_blue[c];
ws3[3][c] = ws_white[c];
ws3[4][c] = ws_off[c];
ws3[5][c] = ws_yell[c];
ws3[6][c] = ws_turc[c];
ws3[7][c] = ws_purple[c];
}
}
*/
void ws3_green(uint8_t ws_chip) {
for (int c = 0; c < 3; c++) {
ws3[ws_chip-1][c] = ws_green[c];
}
}
void ws3_red(uint8_t ws_chip) {
for (int c = 0; c < 3; c++) {
ws3[ws_chip-1][c] = ws_red[c];
}
}
void ws3_blue(uint8_t ws_chip) {
for (int c = 0; c < 3; c++) {
ws3[ws_chip-1][c] = ws_blue[c];
}
}
void ws3_white(uint8_t ws_chip) {
for (int c = 0; c < 3; c++) {
ws3[ws_chip-1][c] = ws_green[c];
}
}
void ws3_off(uint8_t ws_chip) {
for (int c = 0; c < 3; c++) {
ws3[ws_chip-1][c] = 0;
}
}
void ws3_yell(uint8_t ws_chip) {
for (int c = 0; c < 3; c++) {
ws3[ws_chip-1][c] = ws_yell[c];
}
}
void ws3_turc(uint8_t ws_chip) {
for (int c = 0; c < 3; c++) {
ws3[ws_chip-1][c] = ws_turc[c];
}
}
void ws3_purple(uint8_t ws_chip) {
for (int c = 0; c < 3; c++) {
ws3[ws_chip-1][c] = ws_purple[c];
}
}
// 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
// |‾|_._|‾|_._|‾|_._|‾|_._|‾|_._|‾|_._|‾|_._|‾|_._ // 146 73 36
// 128 64 32 16 8 4 2 1
// |‾.‾|_|‾.‾|_|‾.‾|_|‾.‾|_|‾.‾|_|‾.‾|_|‾.‾|_|‾.‾|_ // 219 109 182
void ws_33() {
uint8_t p = 0, w;
///ws_3();
///ws_green_adc[0] = adc_pa5 / 4;
for (int ws = 0; ws < sizeof(ws3) / 3; ws++) {
for (int c = 0; c < 3; c++) {
w = ws3[ws][c];
ws33[p] = 0b10010010;
if (w & 128) ws33[p] += 0b01000000;
if (w & 64) ws33[p] += 0b00001000;
if (w & 32) ws33[p] += 1;
p++;
ws33[p] = 0b01001001;
if (w & 16) ws33[p] += 0b00100000;
if (w & 8) ws33[p] += 0b00000100;
p++;
ws33[p] = 0b00100100;
if (w & 4) ws33[p] += 0b10000000;
if (w & 2) ws33[p] += 0b00010000;
if (w & 1) ws33[p] += 0b00000010;
p++;
}
}
}
void send_ws() {
ws_33();
spi_dma_transmit();
}
void spi_dma_init() {
// Enable Port A clock // Enable SPI Clock
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_SPI1EN;
// Mode: Output, Speed: 10MHz // Alternate function push-pull
GPIOA->CRL &= ~(GPIO_CRL_CNF7 | GPIO_CRL_MODE7); // MOSI = PA7
GPIOA->CRL |= (GPIO_CRL_CNF7_1 | GPIO_CRL_MODE7_0); // 10.01
// 100: BaudRate = fPCLK/32 // + CPOL + CPHA
SPI1->CR1 |= SPI_CR1_BR_2 | SPI_CR1_CPOL | SPI_CR1_CPHA; ///
// Set TXDMA bit in CR2 → enable DMA transmit
SPI1->CR2 |= SPI_CR2_TXDMAEN | SPI_CR2_SSOE; /// pourquoi le SSOE ??
// SPI Mode: Master // SPI Enable
SPI1->CR1 |= SPI_CR1_MSTR | SPI_CR1_SPE;
// DMA Configuration // Enable DMA Clock
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
// Priority Level = 0b10 → High
DMA1_Channel3->CCR |= DMA_CCR_PL_1;
// Memory Increment mode + Direction Read from Memory
DMA1_Channel3->CCR |= DMA_CCR_MINC | DMA_CCR_DIR; // no need for interrupt
// Set Memory Address → Peripheral Address
DMA1_Channel3->CMAR = (uint32_t)ws33;
DMA1_Channel3->CPAR = (uint32_t)&SPI1->DR;
}
void spi_dma_transmit() {
// stop DMA to refill CNDTR
DMA1_Channel3->CCR &= ~DMA_CCR_EN;
DMA1_Channel3->CNDTR = sizeof(ws33);
// launch DMA
DMA1_Channel3->CCR |= DMA_CCR_EN;
///Serial.print(sizeof(ws33));
}
Re: DMA for SPI
Posted: Sat Jul 12, 2025 10:01 am
by ag123
I googled for that, gemini chipped in and this is the response:
The SSOE bit (Slave Select Output Enable) in the SPI_CR2 register of an STM32 microcontroller is used to enable or disable the automatic management of the NSS (Slave Select) pin by the hardware in Master mode. When enabled (SSOE = 1), the NSS pin is driven low when the SPI is enabled and remains low until the SPI is disabled. This behavior is specific to master mode.
normally /CS can be manually managed, e.g. use a gpio pin (which can even be the NSS pin), I tend to have codes that works without SSOE, and I manually control the /CS using gpio e.g. digitalWrite() etc. but that SSOE could be 'more convenient' if it is appropriate.
Re: DMA for SPI
Posted: Sat Jul 12, 2025 10:52 am
by trimarco232
but in this case , I don't use NSS , nor MISO , nor CLK
What happened , is that without setting SPI_CR2_SSOE , my MOSI did not work , and even the SPI peripheral lasts its config !
I don't know what happens :
- undocumented silicon error
- no original chip
- me living in 5th dimension
(AI can't actually answer this)