DMA Code not working on STM32F401 blackpill

Post here first, or if you can't find a relevant section!
Post Reply
hitachii
Posts: 22
Joined: Sat Jan 29, 2022 8:59 pm

DMA Code not working on STM32F401 blackpill

Post by hitachii »

Hello,

I got the DMA code for the timer based blinky from this site. It's the second code under the "Bonus Timer DMA" example down at the bottom:
https://ucexperiment.wordpress.com/2021 ... n-the-ide/

Unfortunately the code doesn't work. I'm using the STM32F401 black pill, so I used the reference manual and determined that the I should be using DMA2_Stream6 and DMA_Channel_0 instead of what's being used in the example code. The only thing that the code does correctly is light up the LED. It is not blinking at one second intervals as indicated in the code.

How do I need to modify this code further to make it work on my board? Thanks!

Code: Select all

#include <Arduino.h>

// TIM1 1Hz DMA Blinky.
TIM_HandleTypeDef htim1;
DMA_HandleTypeDef hdma_tim1;

void setup() {
  uint8_t data[] = { 0xFF, 0x0 };

  pinMode(LED_BUILTIN, OUTPUT);

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 9999;  // 84MHz/10000 = 8400 Hz
  htim1.Init.Period = 8399;     // 8400Hz / 8400 = 1Hz = 1.0s
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  __TIM1_CLK_ENABLE();

  HAL_TIM_Base_Init(&htim1);
  HAL_TIM_Base_Start(&htim1);

  __DMA2_CLK_ENABLE();
  hdma_tim1.Instance = DMA2_Stream6;
  hdma_tim1.Init.Channel = DMA_CHANNEL_0;
  hdma_tim1.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma_tim1.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_tim1.Init.MemInc = DMA_MINC_ENABLE;
  hdma_tim1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma_tim1.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_tim1.Init.Mode = DMA_CIRCULAR;
  hdma_tim1.Init.Priority = DMA_PRIORITY_LOW;
  hdma_tim1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  HAL_DMA_Init(&hdma_tim1);

  HAL_DMA_Start(&hdma_tim1, (uint32_t)&data, (uint32_t)&GPIOC->ODR, 2);
  __HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_UPDATE);
}

void loop() { }
hitachii
Posts: 22
Joined: Sat Jan 29, 2022 8:59 pm

Re: DMA Code not working on STM32F401 blackpill

Post by hitachii »

Following these guides I have tried to add a build_opt.h file with "#define HAL_TIM_MODULE_ONLY". This isn't producing any results either.

https://github.com/stm32duino/wiki/wiki ... uild_opt.h
https://github.com/stm32duino/wiki/wiki ... odule-only

Perhaps I've done the build_opt file incorrectly? In Platformio, build_opt.h is in the src folder of my project, and I've included it in the main file.
User avatar
fpiSTM
Posts: 1738
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 91
Location: Le Mans
Contact:

Re: DMA Code not working on STM32F401 blackpill

Post by fpiSTM »

So you didn't read carefully...
Syntax is :
-DHAL_TIM_MODULE_ONLY
User avatar
Bakisha
Posts: 139
Joined: Fri Dec 20, 2019 6:50 pm
Answers: 5
Contact:

Re: DMA Code not working on STM32F401 blackpill

Post by Bakisha »

Few errors here:
First, you used Stream6, channel0 , which is used for (timer1) ch1 or ch2 or ch3 but later you try to call it with "TIM_DMA_UPDATE" (which use Stream5, channel6).
Second, your "data" is 8 bit, and GPIO port is 16bit wide (register is 32bit, but 16 is used). If you used some some pins between 0 and 7, that might work, but you need to write 0x2000 (0x1<<13) to ODR for PC13.

Anyway, i played a little, so here is a sketch that should work:

Code: Select all

// TIM1 1Hz DMA Blinky.
TIM_HandleTypeDef htim1;
DMA_HandleTypeDef hdma_tim1;

uint16_t data[] = { 0x1 << 13, 0x0000 };

void setup() {

  pinMode(LED_BUILTIN, OUTPUT);

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 9999;  // 84MHz/10000 = 8400 Hz
  htim1.Init.Period = 8399;     // 8400Hz / 8400 = 1Hz = 1.0s
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  __TIM1_CLK_ENABLE();

  HAL_TIM_Base_Init(&htim1);
  HAL_TIM_Base_Start(&htim1);

  __DMA2_CLK_ENABLE();
  hdma_tim1.Instance = DMA2_Stream5;
  hdma_tim1.Init.Channel = DMA_CHANNEL_6;
  hdma_tim1.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma_tim1.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_tim1.Init.MemInc = DMA_MINC_ENABLE;
  hdma_tim1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
  hdma_tim1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
  hdma_tim1.Init.Mode = DMA_CIRCULAR;
  hdma_tim1.Init.Priority = DMA_PRIORITY_LOW;
  hdma_tim1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  HAL_DMA_Init(&hdma_tim1);

  HAL_DMA_Start(&hdma_tim1, (uint32_t)&data, (uint32_t)&GPIOC->ODR, 2);
  __HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_UPDATE);
}

void loop() {
}

Just for fun, here is version without HAL (a little messy, sry)

Code: Select all

TIM_TypeDef *Instance   = TIM1; //
HardwareTimer *MyTim = new HardwareTimer(Instance);

uint16_t data_size = 4;
uint16_t data[] = { 0xffff, 0x0000 ,  0x2000, 0x0000 , 0xffff, 0x0000 ,  0x2000, 0x0000 };


void setup() {

  pinMode(LED_BUILTIN, OUTPUT);


  MyTim->pause();

  MyTim->setOverflow( 1 , HERTZ_FORMAT); // 1Hz


  RCC->AHB1ENR = ( RCC->AHB1ENR ) | (RCC_AHB1ENR_DMA2EN); // enable DMA2 clock

  // DMA configuration (DMA2_Stream5, channel 6). (TIM1_UP -> DMA request )
  // CR register:
  // - channel select: 6 ( TIM1_UP )
  // - no memory burst, single transfer (0x00)
  // - no periferal burst, single transfer (0x00)
  // - Current target is M0AR (0x00)
  // - double buffer disabled (0x00)
  // - priority level: 0x0 ( low )
  // - PINCOS linked to PSIZE (0x00)
  // - 16-bit data size for both source and destination. ( memory to GPIOC->ODR )
  // - enable memory incremental mode (increment memory ptr) (0x01)
  // - disable periferal incremental mode (0x00)
  // - enable circular mode (0x01)
  // - direction: memory to peripheral (0x01)
  // - no interrupts

  DMA2_Stream5->CR &= ~(DMA_SxCR_EN );  // Disable DMA2 Stream 5 . // Note: Changing CMAR, CPAR and CNDTR is possible only when DMA_CCR_EN bit is 0

  DMA2_Stream5->CR |= (
                        /* 31 - 28   */                                       /* reserved */
                        /* 27 - 25   */     ( 0x6 <<  DMA_SxCR_CHSEL_Pos ) | /* CHSEL  [2:0] Channel selection. These bits are set and cleared by software. 000: channel 0 selected. 001: channel 1 selected. 010: channel 2 selected. 011: channel 3 selected. 100: channel 4 selected. 101: channel 5 selected. 110: channel 6 selected. 111: channel 7 selected */
                        /* 24 - 23   */     ( 0x0 << DMA_SxCR_MBURST_Pos ) | /* MBURST [1:0] Memory burst transfer configuration. These bits are set and cleared by software. 00: single transfer. 01: INCR4 (incremental burst of 4 beats). 10: INCR8 (incremental burst of 8 beats). 11: INCR16 (incremental burst of 16 beats). These bits are protected and can be written only if EN is ‘0’. In direct mode, these bits are forced to 0x0 by hardware as soon as bit EN= '1'. */
                        /* 22 - 21   */     ( 0x0 << DMA_SxCR_PBURST_Pos ) | /* PBURST [1:0] Peripheral burst transfer configuration. These bits are set and cleared by software. 00: single transfer. 01: INCR4 (incremental burst of 4 beats). 10: INCR8 (incremental burst of 8 beats). 11: INCR16 (incremental burst of 16 beats). These bits are protected and can be written only if EN is ‘0’. In direct mode, these bits are forced to 0x0 by hardware.*/
                        /* 20        */     ( 0x0                        ) | /* Bit 20       Reserved, must be kept at reset value. */
                        /* 19        */     ( 0x0 <<     DMA_SxCR_CT_Pos ) | /* CT           Current target (only in double buffer mode). This bits is set and cleared by hardware. It can also be written by software. 0: The current target memory is Memory 0 (addressed by the DMA_SxM0AR pointer). 1: The current target memory is Memory 1 (addressed by the DMA_SxM1AR pointer). This bit can be written only if EN is ‘0’ to indicate the target memory area of the first transfer. Once the stream is enabled, this bit operates as a status flag indicating which memory area is the current target. */
                        /* 18        */     ( 0x0 <<    DMA_SxCR_DBM_Pos ) | /* DBM          Double buffer mode. This bits is set and cleared by software. 0: No buffer switching at the end of transfer. 1: Memory target switched at the end of the DMA transfer. This bit is protected and can be written only if EN is ‘0’. */
                        /* 17 - 16   */     ( 0x0 <<     DMA_SxCR_PL_Pos ) | /* PL[1:0]      Priority level. These bits are set and cleared by software. 00: Low. 01: Medium. 10: High. 11: Very high. These bits are protected and can be written only if EN is ‘0’. */
                        /* 15        */     ( 0x0 << DMA_SxCR_PINCOS_Pos ) | /* PINCOS       Peripheral increment offset size. This bit is set and cleared by software. 0: The offset size for the peripheral address calculation is linked to the PSIZE. 1: The offset size for the peripheral address calculation is fixed to 4 (32-bit alignment). This bit has no meaning if bit PINC = '0'. This bit is protected and can be written only if EN = '0'. This bit is forced low by hardware when the stream is enabled (bit EN = '1') if the direct mode is selected or if PBURST are different from “00”.*/
                        /* 14 - 13   */     ( 0x1 <<  DMA_SxCR_MSIZE_Pos ) | /* MSIZE[1:0]   Memory data size. These bits are set and cleared by software. 00: byte (8-bit). 01: half-word (16-bit). 10: word (32-bit). 11: reserved. These bits are protected and can be written only if EN is ‘0’. In direct mode, MSIZE is forced by hardware to the same value as PSIZE as soon as bit EN = '1'. */
                        /* 12 - 11   */     ( 0x1 <<  DMA_SxCR_PSIZE_Pos ) | /* PSIZE[1:0]   Peripheral data size. These bits are set and cleared by software. 00: Byte (8-bit). 01: Half-word (16-bit). 10: Word (32-bit). 11: reserved. These bits are protected and can be written only if EN is ‘0’ */
                        /* 10        */     ( 0x1 <<   DMA_SxCR_MINC_Pos ) | /* MINC         Memory increment mode. This bit is set and cleared by software. 0: Memory address pointer is fixed. 1: Memory address pointer is incremented after each data transfer (increment is done according to MSIZE). This bit is protected and can be written only if EN is ‘0’. */
                        /* 09        */     ( 0x0 <<   DMA_SxCR_PINC_Pos ) | /* PINC         Peripheral increment mode. This bit is set and cleared by software. 0: Peripheral address pointer is fixed. 1: Peripheral address pointer is incremented after each data transfer (increment is done according to PSIZE). This bit is protected and can be written only if EN is ‘0’. */
                        /* 08        */     ( 0x1 <<   DMA_SxCR_CIRC_Pos ) | /* CIRC         Circular mode. This bit is set and cleared by software and can be cleared by hardware. 0: Circular mode disabled. 1: Circular mode enabled. When the peripheral is the flow controller (bit PFCTRL=1) and the stream is enabled (bit EN=1), then this bit is automatically forced by hardware to 0. It is automatically forced by hardware to 1 if the DBM bit is set, as soon as the stream is enabled (bit EN ='1'). */
                        /* 07 - 06   */     ( 0x1 <<    DMA_SxCR_DIR_Pos ) | /* DIR[1:0]     Data transfer direction. These bits are set and cleared by software. 00: Peripheral-to-memory. 01: Memory-to-peripheral. 10: Memory-to-memory. 11: reserved. These bits are protected and can be written only if EN is ‘0’. */
                        /* 05        */     ( 0x0 << DMA_SxCR_PFCTRL_Pos ) | /* PFCTRL       Peripheral flow controller. This bit is set and cleared by software. 0: The DMA is the flow controller. 1: The peripheral is the flow controller. This bit is protected and can be written only if EN is ‘0’. When the memory-to-memory mode is selected (bits DIR[1:0]=10), then this bit is automatically forced to 0 by hardware.*/
                        /* 04        */     ( 0x0 <<   DMA_SxCR_TCIE_Pos ) | /* TCIE         Transfer complete interrupt enable. This bit is set and cleared by software. 0: TC interrupt disabled. 1: TC interrupt enabled. Bit 3 HTIE: Half transfer interrupt */
                        /* 03        */     ( 0x0 <<   DMA_SxCR_HTIE_Pos ) | /* HTIE         Half transfer interrupt enable. This bit is set and cleared by software. 0: HT interrupt disabled. 1: HT interrupt enabled */
                        /* 02        */     ( 0x0 <<   DMA_SxCR_TEIE_Pos ) | /* TEIE         Transfer error interrupt enable. This bit is set and cleared by software. 0: TE interrupt disabled. 1: TE interrupt enabled. */
                        /* 01        */     ( 0x0 <<  DMA_SxCR_DMEIE_Pos ) | /* DMEIE        Direct mode error interrupt enable. This bit is set and cleared by software. 0: DME interrupt disabled 1: DME interrupt enabled */
                        /* 00        */     ( 0x0 <<     DMA_SxCR_EN_Pos )   /* EN           Stream enable / flag stream ready when read low. This bit is set and cleared by software. 0: Stream disabled. 1: Stream enabled. This bit may be cleared by hardware: – on a DMA end of transfer (stream ready to be configured) – if a transfer error occurs on the AHB master buses – when the FIFO threshold on memory AHB port is not compatible with the size of the burst. When this bit is read as 0, the software is allowed to program the Configuration and FIFO bits registers. It is forbidden to write these registers when the EN bit is read as 1. Note: Before setting EN bit to '1' to start a new transfer, the event flags corresponding to the stream in DMA_LISR or DMA_HISR register must be cleared. */

                      );
  // Enable direct mode (set bit to 0 )
  DMA2_Stream5->FCR &= ~( uint32_t( 0x1 <<   DMA_SxFCR_DMDIS_Pos )  );     /* DMDIS: Direct mode disable. This bit is set and cleared by software. It can be set by hardware. 0: Direct mode enabled. 1: Direct mode disabled. This bit is protected and can be written only if EN is ‘0’. This bit is set by hardware if the memory-to-memory mode is selected (DIR bit in DMA_SxCR are “10”) and the EN bit in the DMA_SxCR register */

  DMA2_Stream5->NDTR = uint16_t ( data_size );        // Set DMA data transfer length (16bit unsigned: 0-65535). // Note: Must be set again once all bytes are sended via DMA (not in circular mode)

  DMA2_Stream5->PAR  = (uint32_t)  & (GPIOC->ODR);    //  pointer to address of destination registe

  DMA2_Stream5->M0AR  = (uint32_t)   & (data);        //  pointer to address of source memory buffer

 // TIM1->CR2  &= ~(  0x1 << TIM_CR2_CCDS_Pos  ) ;    /*!<Capture/Compare DMA Selection */ // 0: CCx DMA request sent when CCx event occurs      1: CCx DMA requests sent when update event occurs 

  TIM1->DIER |=  ( TIM_DIER_UDE  ) ;          /* Update DMA request enable 0: Update DMA request disabled 1: Update DMA request enabled */

  DMA2_Stream5->CR |= ( DMA_SxCR_EN ); // Enable  DMA2 Channel 5.

  MyTim->resume(); // resume timer, as soon it reach overflow, it will trigger DMA transfer

}

void loop() {
  // put your main code here, to run repeatedly:


}
hitachii
Posts: 22
Joined: Sat Jan 29, 2022 8:59 pm

Re: DMA Code not working on STM32F401 blackpill

Post by hitachii »

fpiSTM wrote: Sun Feb 05, 2023 9:41 pm So you didn't read carefully...
Syntax is :
-DHAL_TIM_MODULE_ONLY
Thank you, I got mixed up reading the documentation when it was right there!
Bakisha wrote: Mon Feb 06, 2023 1:10 am Few errors here:
First, you used Stream6, channel0 , which is used for (timer1) ch1 or ch2 or ch3 but later you try to call it with "TIM_DMA_UPDATE" (which use Stream5, channel6).
Second, your "data" is 8 bit, and GPIO port is 16bit wide (register is 32bit, but 16 is used). If you used some some pins between 0 and 7, that might work, but you need to write 0x2000 (0x1<<13) to ODR for PC13.

Anyway, i played a little, so here is a sketch that should work:

Code: Select all

// TIM1 1Hz DMA Blinky.
TIM_HandleTypeDef htim1;
DMA_HandleTypeDef hdma_tim1;

uint16_t data[] = { 0x1 << 13, 0x0000 };

void setup() {

  pinMode(LED_BUILTIN, OUTPUT);

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 9999;  // 84MHz/10000 = 8400 Hz
  htim1.Init.Period = 8399;     // 8400Hz / 8400 = 1Hz = 1.0s
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  __TIM1_CLK_ENABLE();

  HAL_TIM_Base_Init(&htim1);
  HAL_TIM_Base_Start(&htim1);

  __DMA2_CLK_ENABLE();
  hdma_tim1.Instance = DMA2_Stream5;
  hdma_tim1.Init.Channel = DMA_CHANNEL_6;
  hdma_tim1.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma_tim1.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_tim1.Init.MemInc = DMA_MINC_ENABLE;
  hdma_tim1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
  hdma_tim1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
  hdma_tim1.Init.Mode = DMA_CIRCULAR;
  hdma_tim1.Init.Priority = DMA_PRIORITY_LOW;
  hdma_tim1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  HAL_DMA_Init(&hdma_tim1);

  HAL_DMA_Start(&hdma_tim1, (uint32_t)&data, (uint32_t)&GPIOC->ODR, 2);
  __HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_UPDATE);
}

void loop() {
}

Just for fun, here is version without HAL (a little messy, sry)

Code: Select all

TIM_TypeDef *Instance   = TIM1; //
HardwareTimer *MyTim = new HardwareTimer(Instance);

uint16_t data_size = 4;
uint16_t data[] = { 0xffff, 0x0000 ,  0x2000, 0x0000 , 0xffff, 0x0000 ,  0x2000, 0x0000 };


void setup() {

  pinMode(LED_BUILTIN, OUTPUT);


  MyTim->pause();

  MyTim->setOverflow( 1 , HERTZ_FORMAT); // 1Hz


  RCC->AHB1ENR = ( RCC->AHB1ENR ) | (RCC_AHB1ENR_DMA2EN); // enable DMA2 clock

  // DMA configuration (DMA2_Stream5, channel 6). (TIM1_UP -> DMA request )
  // CR register:
  // - channel select: 6 ( TIM1_UP )
  // - no memory burst, single transfer (0x00)
  // - no periferal burst, single transfer (0x00)
  // - Current target is M0AR (0x00)
  // - double buffer disabled (0x00)
  // - priority level: 0x0 ( low )
  // - PINCOS linked to PSIZE (0x00)
  // - 16-bit data size for both source and destination. ( memory to GPIOC->ODR )
  // - enable memory incremental mode (increment memory ptr) (0x01)
  // - disable periferal incremental mode (0x00)
  // - enable circular mode (0x01)
  // - direction: memory to peripheral (0x01)
  // - no interrupts

  DMA2_Stream5->CR &= ~(DMA_SxCR_EN );  // Disable DMA2 Stream 5 . // Note: Changing CMAR, CPAR and CNDTR is possible only when DMA_CCR_EN bit is 0

  DMA2_Stream5->CR |= (
                        /* 31 - 28   */                                       /* reserved */
                        /* 27 - 25   */     ( 0x6 <<  DMA_SxCR_CHSEL_Pos ) | /* CHSEL  [2:0] Channel selection. These bits are set and cleared by software. 000: channel 0 selected. 001: channel 1 selected. 010: channel 2 selected. 011: channel 3 selected. 100: channel 4 selected. 101: channel 5 selected. 110: channel 6 selected. 111: channel 7 selected */
                        /* 24 - 23   */     ( 0x0 << DMA_SxCR_MBURST_Pos ) | /* MBURST [1:0] Memory burst transfer configuration. These bits are set and cleared by software. 00: single transfer. 01: INCR4 (incremental burst of 4 beats). 10: INCR8 (incremental burst of 8 beats). 11: INCR16 (incremental burst of 16 beats). These bits are protected and can be written only if EN is ‘0’. In direct mode, these bits are forced to 0x0 by hardware as soon as bit EN= '1'. */
                        /* 22 - 21   */     ( 0x0 << DMA_SxCR_PBURST_Pos ) | /* PBURST [1:0] Peripheral burst transfer configuration. These bits are set and cleared by software. 00: single transfer. 01: INCR4 (incremental burst of 4 beats). 10: INCR8 (incremental burst of 8 beats). 11: INCR16 (incremental burst of 16 beats). These bits are protected and can be written only if EN is ‘0’. In direct mode, these bits are forced to 0x0 by hardware.*/
                        /* 20        */     ( 0x0                        ) | /* Bit 20       Reserved, must be kept at reset value. */
                        /* 19        */     ( 0x0 <<     DMA_SxCR_CT_Pos ) | /* CT           Current target (only in double buffer mode). This bits is set and cleared by hardware. It can also be written by software. 0: The current target memory is Memory 0 (addressed by the DMA_SxM0AR pointer). 1: The current target memory is Memory 1 (addressed by the DMA_SxM1AR pointer). This bit can be written only if EN is ‘0’ to indicate the target memory area of the first transfer. Once the stream is enabled, this bit operates as a status flag indicating which memory area is the current target. */
                        /* 18        */     ( 0x0 <<    DMA_SxCR_DBM_Pos ) | /* DBM          Double buffer mode. This bits is set and cleared by software. 0: No buffer switching at the end of transfer. 1: Memory target switched at the end of the DMA transfer. This bit is protected and can be written only if EN is ‘0’. */
                        /* 17 - 16   */     ( 0x0 <<     DMA_SxCR_PL_Pos ) | /* PL[1:0]      Priority level. These bits are set and cleared by software. 00: Low. 01: Medium. 10: High. 11: Very high. These bits are protected and can be written only if EN is ‘0’. */
                        /* 15        */     ( 0x0 << DMA_SxCR_PINCOS_Pos ) | /* PINCOS       Peripheral increment offset size. This bit is set and cleared by software. 0: The offset size for the peripheral address calculation is linked to the PSIZE. 1: The offset size for the peripheral address calculation is fixed to 4 (32-bit alignment). This bit has no meaning if bit PINC = '0'. This bit is protected and can be written only if EN = '0'. This bit is forced low by hardware when the stream is enabled (bit EN = '1') if the direct mode is selected or if PBURST are different from “00”.*/
                        /* 14 - 13   */     ( 0x1 <<  DMA_SxCR_MSIZE_Pos ) | /* MSIZE[1:0]   Memory data size. These bits are set and cleared by software. 00: byte (8-bit). 01: half-word (16-bit). 10: word (32-bit). 11: reserved. These bits are protected and can be written only if EN is ‘0’. In direct mode, MSIZE is forced by hardware to the same value as PSIZE as soon as bit EN = '1'. */
                        /* 12 - 11   */     ( 0x1 <<  DMA_SxCR_PSIZE_Pos ) | /* PSIZE[1:0]   Peripheral data size. These bits are set and cleared by software. 00: Byte (8-bit). 01: Half-word (16-bit). 10: Word (32-bit). 11: reserved. These bits are protected and can be written only if EN is ‘0’ */
                        /* 10        */     ( 0x1 <<   DMA_SxCR_MINC_Pos ) | /* MINC         Memory increment mode. This bit is set and cleared by software. 0: Memory address pointer is fixed. 1: Memory address pointer is incremented after each data transfer (increment is done according to MSIZE). This bit is protected and can be written only if EN is ‘0’. */
                        /* 09        */     ( 0x0 <<   DMA_SxCR_PINC_Pos ) | /* PINC         Peripheral increment mode. This bit is set and cleared by software. 0: Peripheral address pointer is fixed. 1: Peripheral address pointer is incremented after each data transfer (increment is done according to PSIZE). This bit is protected and can be written only if EN is ‘0’. */
                        /* 08        */     ( 0x1 <<   DMA_SxCR_CIRC_Pos ) | /* CIRC         Circular mode. This bit is set and cleared by software and can be cleared by hardware. 0: Circular mode disabled. 1: Circular mode enabled. When the peripheral is the flow controller (bit PFCTRL=1) and the stream is enabled (bit EN=1), then this bit is automatically forced by hardware to 0. It is automatically forced by hardware to 1 if the DBM bit is set, as soon as the stream is enabled (bit EN ='1'). */
                        /* 07 - 06   */     ( 0x1 <<    DMA_SxCR_DIR_Pos ) | /* DIR[1:0]     Data transfer direction. These bits are set and cleared by software. 00: Peripheral-to-memory. 01: Memory-to-peripheral. 10: Memory-to-memory. 11: reserved. These bits are protected and can be written only if EN is ‘0’. */
                        /* 05        */     ( 0x0 << DMA_SxCR_PFCTRL_Pos ) | /* PFCTRL       Peripheral flow controller. This bit is set and cleared by software. 0: The DMA is the flow controller. 1: The peripheral is the flow controller. This bit is protected and can be written only if EN is ‘0’. When the memory-to-memory mode is selected (bits DIR[1:0]=10), then this bit is automatically forced to 0 by hardware.*/
                        /* 04        */     ( 0x0 <<   DMA_SxCR_TCIE_Pos ) | /* TCIE         Transfer complete interrupt enable. This bit is set and cleared by software. 0: TC interrupt disabled. 1: TC interrupt enabled. Bit 3 HTIE: Half transfer interrupt */
                        /* 03        */     ( 0x0 <<   DMA_SxCR_HTIE_Pos ) | /* HTIE         Half transfer interrupt enable. This bit is set and cleared by software. 0: HT interrupt disabled. 1: HT interrupt enabled */
                        /* 02        */     ( 0x0 <<   DMA_SxCR_TEIE_Pos ) | /* TEIE         Transfer error interrupt enable. This bit is set and cleared by software. 0: TE interrupt disabled. 1: TE interrupt enabled. */
                        /* 01        */     ( 0x0 <<  DMA_SxCR_DMEIE_Pos ) | /* DMEIE        Direct mode error interrupt enable. This bit is set and cleared by software. 0: DME interrupt disabled 1: DME interrupt enabled */
                        /* 00        */     ( 0x0 <<     DMA_SxCR_EN_Pos )   /* EN           Stream enable / flag stream ready when read low. This bit is set and cleared by software. 0: Stream disabled. 1: Stream enabled. This bit may be cleared by hardware: – on a DMA end of transfer (stream ready to be configured) – if a transfer error occurs on the AHB master buses – when the FIFO threshold on memory AHB port is not compatible with the size of the burst. When this bit is read as 0, the software is allowed to program the Configuration and FIFO bits registers. It is forbidden to write these registers when the EN bit is read as 1. Note: Before setting EN bit to '1' to start a new transfer, the event flags corresponding to the stream in DMA_LISR or DMA_HISR register must be cleared. */

                      );
  // Enable direct mode (set bit to 0 )
  DMA2_Stream5->FCR &= ~( uint32_t( 0x1 <<   DMA_SxFCR_DMDIS_Pos )  );     /* DMDIS: Direct mode disable. This bit is set and cleared by software. It can be set by hardware. 0: Direct mode enabled. 1: Direct mode disabled. This bit is protected and can be written only if EN is ‘0’. This bit is set by hardware if the memory-to-memory mode is selected (DIR bit in DMA_SxCR are “10”) and the EN bit in the DMA_SxCR register */

  DMA2_Stream5->NDTR = uint16_t ( data_size );        // Set DMA data transfer length (16bit unsigned: 0-65535). // Note: Must be set again once all bytes are sended via DMA (not in circular mode)

  DMA2_Stream5->PAR  = (uint32_t)  & (GPIOC->ODR);    //  pointer to address of destination registe

  DMA2_Stream5->M0AR  = (uint32_t)   & (data);        //  pointer to address of source memory buffer

 // TIM1->CR2  &= ~(  0x1 << TIM_CR2_CCDS_Pos  ) ;    /*!<Capture/Compare DMA Selection */ // 0: CCx DMA request sent when CCx event occurs      1: CCx DMA requests sent when update event occurs 

  TIM1->DIER |=  ( TIM_DIER_UDE  ) ;          /* Update DMA request enable 0: Update DMA request disabled 1: Update DMA request enabled */

  DMA2_Stream5->CR |= ( DMA_SxCR_EN ); // Enable  DMA2 Channel 5.

  MyTim->resume(); // resume timer, as soon it reach overflow, it will trigger DMA transfer

}

void loop() {
  // put your main code here, to run repeatedly:


}
THANK YOU FOR THIS EXTENSIVE WRITEUP! It works!!! The reason that I was looking into using HAL in the first place was because I was having a difficult time getting DMA running without it! :D Cheers!
Post Reply

Return to “General discussion”