Using STM32 Timer->GPIO transfer to handle WS2812B LED timing data - arm

I've been working on a lighting controller, but haven't had any luck in transferring data through GPIO. I'm using the STM32F303VCT6, and HAL library. I used the WS2812B Library for STM32F3 by Hubmartin as reference, modifying it to handling LED timing data transfer in a single DMA transfer as opposed to bitbanding timing data into a two LED length buffer through circular DMA.
My plan has been to use DMA to start every GPIO that's set to LED output, to output 'high' on timer update event. Then on event CC1 to write given pins to 'low' through buffer data. And then on event CC2 to set all pins 'low'. This should allow me to handle the timing data for multiple strips in parallel. Here is my implementation:
To initialize timer:
static void TIM2_init(void){
__HAL_RCC_TIM2_CLK_ENABLE();
tim_period = SystemCoreClock / 800000;
uint32_t cc1 = (10 * tim_period) / 36;
uint32_t cc2 = (10 * tim_period) / 15;
Tim2Handle.Instance = TIM2;
Tim2Handle.Init.Period = tim_period;
Tim2Handle.Init.RepetitionCounter = 0;
Tim2Handle.Init.Prescaler = 0;
Tim2Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
Tim2Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_PWM_Init(&Tim2Handle);
tim2OC1.OCMode = TIM_OCMODE_PWM1;
tim2OC1.OCPolarity = TIM_OCPOLARITY_HIGH;
tim2OC1.Pulse = cc1;
tim2OC1.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&Tim2Handle, &tim2OC1, TIM_CHANNEL_1);
tim2OC2.Pulse = cc2;
HAL_TIM_PWM_ConfigChannel(&Tim2Handle, &tim2OC2, TIM_CHANNEL_2);
}
To initialize DMA:
static void DMA_init(void){
__HAL_RCC_DMA1_CLK_ENABLE();
dmaUpdate.Init.Direction = DMA_MEMORY_TO_PERIPH;
dmaUpdate.Init.PeriphInc = DMA_PINC_DISABLE;
dmaUpdate.Init.MemInc = DMA_MINC_DISABLE;
dmaUpdate.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
dmaUpdate.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
dmaUpdate.Init.Mode = DMA_NORMAL;
dmaUpdate.Init.Priority = DMA_PRIORITY_VERY_HIGH;
dmaUpdate.Instance = DMA1_Channel2;
HAL_DMA_Init(&dmaUpdate);
__HAL_LINKDMA(&Tim2Handle, hdma[TIM_DMA_ID_UPDATE], dmaUpdate);
dmaCC1.Init.Direction = DMA_MEMORY_TO_PERIPH;
dmaCC1.Init.PeriphInc = DMA_PINC_DISABLE;
dmaCC1.Init.MemInc = DMA_MINC_ENABLE;
dmaCC1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
dmaCC1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
dmaCC1.Init.Mode = DMA_NORMAL;
dmaCC1.Init.Priority = DMA_PRIORITY_VERY_HIGH;
dmaCC1.Instance = DMA1_Channel5;
HAL_DMA_Init(&dmaCC1);
__HAL_LINKDMA(&Tim2Handle, hdma[TIM_DMA_ID_CC1], dmaCC1);
dmaCC2.Init.Direction = DMA_MEMORY_TO_PERIPH;
dmaCC2.Init.PeriphInc = DMA_PINC_DISABLE;
dmaCC2.Init.MemInc = DMA_MINC_DISABLE;
dmaCC2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
dmaCC2.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
dmaCC2.Init.Mode = DMA_NORMAL;
dmaCC2.Init.Priority = DMA_PRIORITY_VERY_HIGH;
dmaCC2.Instance = DMA1_Channel7;
HAL_DMA_Init(&dmaCC2);
__HAL_LINKDMA(&Tim2Handle, hdma[TIM_DMA_ID_CC2], dmaCC2);
dmaCC2.XferCpltCallback = DMA_TransferCompleteHandler;
HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
}
To begin a transfer:
static void WS2812_sendbuf(){
ws2812b.transferComplete = 0;
HAL_DMA_Start(&dmaUpdate,(uint32_t)WS2812_IO_High, (uint32_t)&WS2812B_PORT->BSRR, BUFFER_SIZE);
HAL_DMA_Start(&dmaCC1,(uint32_t)ws2812bDmaBitBuffer, (uint32_t)&WS2812B_PORT->BRR, BUFFER_SIZE);
HAL_DMA_Start_IT(&dmaCC2,(uint32_t)WS2812_IO_Low, (uint32_t)&WS2812B_PORT->BSRR, BUFFER_SIZE);
__HAL_TIM_ENABLE_DMA(&Tim2Handle, TIM_DMA_UPDATE);
__HAL_TIM_ENABLE_DMA(&Tim2Handle, TIM_DMA_CC1);
__HAL_TIM_ENABLE_DMA(&Tim2Handle, TIM_DMA_CC2);
__HAL_TIM_ENABLE(&Tim2Handle);
}
To callback on completion of DMA output:
void DMA1_Channel7_IRQHandler(void){
HAL_DMA_IRQHandler(&dmaCC2);
}
void DMA_TransferCompleteHandler(DMA_HandleTypeDef *DmaHandle){
WS2812B_PORT->BSRR = WS2812_IO_Low[0];
__HAL_TIM_DISABLE_DMA(&Tim2Handle, TIM_DMA_UPDATE);
__HAL_TIM_DISABLE_DMA(&Tim2Handle, TIM_DMA_CC1);
__HAL_TIM_DISABLE_DMA(&Tim2Handle, TIM_DMA_CC2);
__HAL_TIM_DISABLE(&Tim2Handle);
ws2812b.transferComplete = 1;
}
Although I can verify that the initial implementation works as intended, there's some glitchy behavior in the current HAL driver, which I am attempting to overcome through having all my data available before the timing data transmission begins.
In my current implementation, I send a single data buffer and begin the timing data transmission, and then delay 100 milliseconds before starting the process again. I am confused to find that my implementation doesn't appear to send out any data at all through GPIO, and I assume that I've made mistakes in my state logic / timer start. Does anyone see a mistake that I've made to cause no output?

Related

SPI transmission errors. RX buffer full and data mismatch

I'm transmitting my data in SPI. uint8_t data[5] = {0xa,0xb,0xc,0xd,0xe}; When I see the output in logic analyzer, the data is mis matched. I dont know what might be the root of the problem. I will share my code and configuration here. Please have a look.
void SPI_Transmit (uint8_t *data, int size)
{
SPI1->CR1 |= (1<<6);
int i=0;
while (i<size)
{
while (!((SPI1->SR)&(1<<1))) {}; // wait for TXE bit to set -> This will indicate that the buffer is empty
SPI1->DR = data[i]; // load the data into the Data Register
i++;
}
// Clear the Overrun flag by reading DR and SR
while (!((SPI1->SR)&(1<<1))) {}; // wait for TXE bit to set -> This will indicate that the buffer is empty
while (((SPI1->SR)&(1<<7))) {}; // wait for BSY bit to Reset -> This will indicate that SPI is not busy in communication
while (((SPI1->SR)&(1<<0)))
{
uint8_t temp = SPI1->DR;
};
uint8_t temp1 = SPI1->SR;
SPI1->CR1 &= ~(1<<6);
}
Here I'm transmitting my data and waiting for my TX empty flag to set and Busy flag to reset. Also, after I transmittting my data, my RX buffer (RXFIFO) is full and RX not empty flag is set. So I have to read data register until RXNE is reset.
I understand SPI communicate through register shifting, for every data sent there will be some received data. But my communication is in Master transmit only mode. I'm only using MOSI not MISO. So I dont understand why there is data in my RX buffer even when I'm using only MOSI wire. I also tested transmitting data without any slave device, still I receive data in RX buffer. I dont understand where this RX data is coming from. The status registe shows RXNE=1 and FRLVL=11.
After programming my SPI transmit function according to my conditions, I transmit the data. When I see output in logic analyzer, the data is mismatch. The Baud Rate is 625.0KBits/s and sampling rate is 2MS/s.
Here is my SPI configuration
static void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}
One of the most important thing to work with SPI is phasing. With two parameters CPOL and CPHA the phasing should be configured. In your problem in these two are named as CLKPolarity and CLKPhase in hspi1.Init respectively. These two parameters includes 4 states (0,0 - 0,1 - 1,0 - 1,1). It's better to try each state. For more information please check Here, section: Clock Polarity and Clock Phase.

Using DCMI Interface but only receiving 0xA603A603 before hard fault

For my master thesis I am trying to receive data over DCMI with the STM32H742. As an image sensor I am using the NOIV2SN1300A. It sends data over 10 data lines, this data differs (so not always 0xA603A603). To reduce data size I have activated binning and subsampling.
I am using DMA to transfer the data received by DCMI into the internal SRAM at address 0x24000000. After a few telegrams (of 0xA603A603) the uC goes into hard fault and infinite loop. I am not sure why this error occurs. Here is my config for DMA and DCMI:
/* DCMI DMA Init */
/* DCMI Init */
hdma_dcmi.Instance = DMA1_Stream0;
hdma_dcmi.Init.Request = DMA_REQUEST_DCMI;
hdma_dcmi.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_dcmi.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_dcmi.Init.MemInc = DMA_MINC_ENABLE;
hdma_dcmi.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;//WORD;
hdma_dcmi.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_dcmi.Init.Mode = DMA_DOUBLE_BUFFER_M0;
hdma_dcmi.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_dcmi.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
hdma_dcmi.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_dcmi.Init.MemBurst = DMA_MBURST_SINGLE;
//hdma_dcmi.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_dcmi) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hdcmi,DMA_Handle,hdma_dcmi);
HAL_NVIC_SetPriority(DCMI_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DCMI_IRQn);
HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
hdcmi.Instance = DCMI;
hdcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
hdcmi.Init.PCKPolarity = DCMI_PCKPOLARITY_FALLING; //Todo:???
hdcmi.Init.VSPolarity = DCMI_VSPOLARITY_HIGH;
hdcmi.Init.HSPolarity = DCMI_HSPOLARITY_HIGH;
hdcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME;
hdcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_10B;
hdcmi.Init.JPEGMode = DCMI_JPEG_DISABLE;
hdcmi.Init.ByteSelectMode = DCMI_BSM_OTHER;//DCMI_BSM_ALL;
hdcmi.Init.ByteSelectStart = DCMI_OEBS_ODD;
hdcmi.Init.LineSelectMode = DCMI_LSM_ALL;
hdcmi.Init.LineSelectStart = DCMI_OELS_ODD;
if (HAL_DCMI_Init(&hdcmi) != HAL_OK)
{
Error_Handler();
}
uint32_t DataSizeImageSensor = 0xB160;
DMA_BUFFER uint32_t AddressFrameBuffer[0xB160];
HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_SNAPSHOT, (uint32_t)AddressFrameBuffer, DataSizeImageSensor);
void MPU_Config (void)
{
MPU_Region_InitTypeDef MPU_InitStruct;
/* Disable the MPU */
HAL_MPU_Disable();
/* Configure the MPU attributes for SDRAM */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; //Cache is incoherent with CPU for DMA usage
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enable the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
Any idea why this error occurs? Like I said, I receive data, I am sure the sensor sends viable data (so something else then 0xA603A603), but I get only the same data by DCMI before hard fault. At this current setup I transfer 64 telegrams, then the hard fault occurs. Furthermore I disabled the cache for the SRAM1, as there are problems regarding cache/DMA/CPU coherence. I am not quite sure how to proceed, as it looks to me like it is a setup error. Maybe an overrun by DCMI?
Note: The code above doesn't actually occur in this order. That are just the relevant parts (at least from what I expect to be relevant).
I found other posts about this problem, where the error was a slow system clock. As my system clock is 475 MHz and the HCLK3 Clock (which is used by DCMI for sampling) is 237.5 MHz I can rule out a Clock problem. The datasheet says the HCLK Clock has to be 2.5 times faster then DCMI Clock (which is set by the image sensor with 62 Mhz).

STM32H7 SPI Nor Flash Device is always busy IS25LP128F

I am working on SPI Nor Flash IC IS25LP128F using Little FS as my file system. I Set 400Mhz as SPI kernel Clock, and my Prescaler value at 8. Hence 50 MHz SPI clock runs. At this case, my Filesystem operation takes places successfully at time, some times the code hangs in the SPI routine for a long time and states, NOR Device is busy.
According to Datasheet 80Mhz SPI clock, normal Read and 166Mhz SPI clock Fast read is given for IS25LP128F. I changed the kernel clk, to get respective output clock. Even then, the problem remains the same.
At 50Mhz sometimes to write a single line(64 byte) takes 4 minutes to complete. I have tried with 100Mhz, 80Mhz, 50Mhz, all the prescaler values, but only for 50 Mhz runs successfully sometimes (not all the time success)
Without device busy(txData=0 has to come), the filesystem, have to be taken place.
Pls help me out.
Datasheet
Output
/*Received Txdata always busy*/
uint8_t readStatusReg(void)
{
uint8_t txData;
CS_ACTIVE;
txData=CMD_RDSR;
HAL_SPI_Transmit(&hspi1, &txData, 1, 1000);
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
HAL_SPI_Receive(&hspi1, &txData, 1, 1000);
CS_DEACTIVE;
return txData;
}
/*Clock Config */
static void HWI_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
}

STM32 odd timer1 behavior in Master-Slave Configuration - mb code issue

I'm currently working on an embedded system which is meant to output a specific pulse sequence at equidistant timings. Therefore, I use the STM32 - DISCO board with the FreeRTOS kernel, as a first test.
I configured TIM3 as Master triggering TIM1. TIM1 is triggered with a frequency of 1Hz or every second. TIM3 then generates a pulse sequence on it's output.
I configured the TIM3 output to PB4, and TIM1 to PA9. This configuration works as expected, but now I wanted to change the configuration on the fly with a structure that stores my settings and can be passed as pointer to a function that configures both timers.
As a first step, I generated a data structure and initialized it in my timer function to configure TIM3.
PG_ERR TIM_Master_Init(void){
PG_HandleTypeDef hPG_timer;
hPG_timer.PLS.Prescaler = 139;
hPG_timer.PLS.Period = 60000;
hPG_timer.PLS.DutyCycle = 30000;
hPG_timer.PLS.RepetitionCounter = 5;
hPG_timer.PLS.PercentChange = 0;
/* Timer3 handler declaration: Master */
TIM_HandleTypeDef TimMasterHandle;
/* Master configuration structure */
TIM_MasterConfigTypeDef sMasterConfig;
/* Output compare structure */
TIM_OC_InitTypeDef sOCConfig;
__TIM3_CLK_ENABLE();
PG_ERR xPGERR = PG_ERR_NONE;
/* Compute the prescaler value to have TIM3 counter clock equal to 60 KHz */
/* Set TIMx instance */
TimMasterHandle.Instance = MASTER_TIM;
/* Master configuration: TIM3 */
TimMasterHandle.Init.Period = 60000 - 1;
TimMasterHandle.Init.Prescaler = 1399;
TimMasterHandle.Init.ClockDivision = 0;
TimMasterHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
if (HAL_TIM_PWM_Init(&TimMasterHandle) != HAL_OK){
xPGERR = PG_ERR_TIM;
}
/* Configure the PWM_channel_1 */
sOCConfig.OCMode = TIM_OCMODE_PWM1;
sOCConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
sOCConfig.OCFastMode = TIM_OCFAST_ENABLE;
sOCConfig.Pulse = 30000;
if (HAL_TIM_PWM_ConfigChannel(&TimMasterHandle, &sOCConfig, TIM_CHANNEL_1) != HAL_OK){
xPGERR = PG_ERR_TIM;
}
/* Configure TIM3 as master & use the update event as Trigger Output (TRGO) */
sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
if(HAL_TIMEx_MasterConfigSynchronization(&TimMasterHandle, &sMasterConfig) != HAL_OK){
xPGERR = PG_ERR_TIM;
}
/* Start PWM Timer3*/
if(HAL_TIM_PWM_Start(&TimMasterHandle, TIM_CHANNEL_1) != HAL_OK){
xPGERR = PG_ERR_TIM;
}
if(HAL_TIM_Base_Start_IT(&TimMasterHandle) != HAL_OK){
xPGERR = PG_ERR_TIM;
}
return xPGERR;
}
1) With this configuration i get some really odd behaviour. After including the data structure:
PG_HandleTypeDef hPG_timer;
hPG_timer.PLS.Prescaler = 139;
hPG_timer.PLS.Period = 60000;
hPG_timer.PLS.DutyCycle = 30000;
hPG_timer.PLS.RepetitionCounter = 5;
hPG_timer.PLS.PercentChange = 0;
After that code snipped, I don't get any output on PIN PB4 (Master - TIM3) which should still be toggled at 1Hz.
2) To get even more confusing, when I substitute the code block with a handle:
PG_HandleTypeDef hPG_timer;
PG_HandleTypeDef *hPG_timer;
hPG_timer = &hPG_timer_config;
hPG_timer.PLS.Prescaler = 139;
hPG_timer.PLS.Period = 60000;
hPG_timer.PLS.DutyCycle = 30000;
hPG_timer.PLS.RepetitionCounter = 5;
hPG_timer.PLS.PercentChange = 0;
Now I can see the output on PB4 (Master - TIM3) with 1Hz but the output polarity of PA9 (Slave - TIM1) is reversed.
I tried to investigate the problem, I focused on the stack/heap of the FreeRTOS. I tested the system with large heap/stack = (uint32_t) 65535; I couldn't observe any changes in the behaviour.
I hope somebody came across a similar problem or has an idea how to resolve this. I'm thankful for any input in this, I'm at the end of my knowledge unfortunately.
Edit:
I spend some more time with the problem, and I think I can be more specific. In case of the pointer use, the TimMasterHandle is locked right after the initialization. If I unlock the handle TimMasterHandle.lock = HAL_UNLOCK; all works well, but that is just masking the problem and i would like to know where this is coming from.
It some how looks still like an heap or stack problem. Is there any way, I can check for heap or stack overflow. I am using Keil uVision 5.10.
Thank you for your time and help,
eimer
So I contacted the E-Mail support of ST.
The rather simple answer I got was -
Initialize your timer handles e.g.:
PG_HandleTypeDef hPG_timer = {0};
Seems to resolve the odd behaviour.
I hope somebody finds this usefull.
enjoy your day
eimer

STM32 DMA transfer error

I use STM32F407VTG6 controller and try to receive data from SPI using DMA. Then I want to process data on DMA Complete Transfer Interrupt. But when Complete Transfer Interrupt is occurred I see that TEIF (transfer error interrupt flag) is set. After this DMA can't be started. This is part of my code:
static void DmaInit()
{
DMA_InitTypeDef dma;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
// DMA for Rx
dma.DMA_Channel = DMA_Channel_3;
dma.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
dma.DMA_Memory0BaseAddr = 0; // will be set later
dma.DMA_DIR = DMA_DIR_PeripheralToMemory;
dma.DMA_BufferSize = 1; // will be set later
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
dma.DMA_Mode = DMA_Mode_Normal;
dma.DMA_Priority = DMA_Priority_High;
dma.DMA_FIFOMode = DMA_FIFOMode_Disable;
dma.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;
dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_DeInit(DMA2_Stream2);
DMA_Init(DMA2_Stream2, &dma);
// Enable DMA Interrupt on complete transfer
NVIC_EnableIRQ(DMA2_Stream2_IRQn);
DMA_ITConfig(DMA2_Stream2, DMA_IT_TC, ENABLE);
}
// It run on external interrupt
static void DmaStart(uint32_t bufferSize, uint32_t* rxBuffer)
{
// Start DMA for reading
DMA2_Stream2->NDTR = bufferSize;
DMA2_Stream2->M0AR = (uint32_t)rxBuffer;
DMA_Cmd(DMA2_Stream2, ENABLE);
}
static void SpiInit()
{
SPI_InitTypeDef spi;
// Enable clock for SPI
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
// SPI settings
SPI_StructInit(&spi);
spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
spi.SPI_Mode = SPI_Mode_Master;
spi.SPI_DataSize = SPI_DataSize_8b;
spi.SPI_CPOL = SPI_CPOL_Low;
spi.SPI_CPHA = SPI_CPHA_2Edge;
spi.SPI_NSS = SPI_NSS_Soft;
spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
spi.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI1, &spi);
SPI_Cmd(SPI1, ENABLE);
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);
}
I just finished my SDIO+DMA this days, some notes maybe useful for you:
Clear Flags before enabling the stream
DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_FEIF2|DMA_FLAG_DMEIF2|DMA_FLAG_TEIF2|DMA_FLAG_HTIF2|DMA_FLAG_TCIF2);
Clear EN bit in the DMA_SxCR Register, Wait Until the EN bit is read as 0 before DMA_Init()
DMA_Cmd(DMA2_Stream2, DISABLE);
while (DMA2_Stream2->CR & DMA_SxCR_EN);
When use DMA_FIFOMode_Disable (Direct Mode), data width is determined by DMA_PeripheralDataSize (PSIZE), DMA_MemoryDataSize (MSIZE) is ignored
Memory address must be aligned to your selected data width (HalfWord)
Reference:
STM32F407XX Reference Manual - Chapter 10 DMA Controller
STM32F4XX Standard Peripheral Library

Resources