2020-09-20 02:44 PM
I'm new to using the STM32, coming from AVR. I'm trying to switch in order to take advantage of DMA but I can't get it working. I have a Nucleo STM32F411re.
I've generated a new project, configured a few pins on GPIOC as output, configured TIM1 to use an internal clock, and generate a PWM out on CH1, and configured a DMA to use TIM_UP (DMA2 Stream 5) Memory to Peripheral.
in the code I start the PWM output -- this works and I see it on the scope.
if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
then I try to start the DMA transfer - this doesn't work.
if (HAL_DMA_Start(&hdma_tim1_up, (uint32_t)data, ((uint32_t)&(GPIOC->ODR)), 16) != HAL_OK) {
Error_Handler();
};
I'm not sure what I'm doing wrong. I've read a lot of forum posts about this with mixed results but even when I follow the examples that claim to work they don't work for me.
Eventually I'd like to get it working with 11 data lines and 1 clock line and would like to use interrupts and circular mode but I'm happy to just get the basics working first.
2020-09-20 04:16 PM
> sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
Fairly sure this should be TIM_TRGO_UPDATE since that's the event you want to trigger from.
Here is some code that works, although it doesn't use HAL directly. You could cross-reference the register values with your example to see what's different.
// set up the timer
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_TIM1_FORCE_RESET();
__HAL_RCC_TIM1_RELEASE_RESET();
TIM1->PSC = 0;
TIM1->ARR = 16;
TIM1->CNT = 0;
TIM1->DIER |= TIM_DIER_UDE;
uint32_t gpio_data[] = {
(1 << 10) | (1 << 11),
1 << 26,
(1 << 10) | (1 << 27),
1 << 26};
// set up the DMA channel
__HAL_RCC_DMA2_CLK_ENABLE();
__HAL_RCC_DMA2_FORCE_RESET();
__HAL_RCC_DMA2_RELEASE_RESET();
DMA_Stream_TypeDef * stream = DMA2_Stream5;
stream->M0AR = (uint32_t) gpio_data;
stream->NDTR = sizeof(gpio_data) / (sizeof(*gpio_data));
// disable direct mode
stream->FCR |= DMA_SxFCR_DMDIS;
//stream->FCR |= 0b11 << DMA_SxFCR_FTH_Pos;
// TIM1_UP is channel 6 on DMA2 stream 5
stream->CR |= 6 << DMA_SxCR_CHSEL_Pos;
stream->CR |= 0b10 << DMA_SxCR_MSIZE_Pos;
stream->CR |= 0b10 << DMA_SxCR_PSIZE_Pos;
stream->CR |= DMA_SxCR_MINC;
stream->CR |= DMA_SxCR_CIRC;
stream->CR |= 0b01 << DMA_SxCR_DIR_Pos;
stream->PAR = (uint32_t) &GPIOC->BSRR;
stream->CR |= 0b1 << DMA_SxCR_PL_Pos;
stream->CR |= DMA_SxCR_EN;
// start timer
TIM1->CR1 |= TIM_CR1_CEN;
2020-09-21 07:23 PM
Thank you so much -- this worked for me.
I also got it working with ODR with some slight changes, below.
Now I just need to get it working with interrupts for double buffered transfers but at least this gives me some confidence. Maybe it's time to jump on the HAL ***** bandwagon that everyone else seems to be on. Working with registers is closer to my AVR experience anyways.
// set up the timer
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_TIM1_FORCE_RESET();
__HAL_RCC_TIM1_RELEASE_RESET();
TIM1->PSC = 0; // prescaler
TIM1->ARR = 167; // period (auto reload)
TIM1->CNT = 0; // counter
TIM1->DIER |= TIM_DIER_UDE; // Update DMA request enable
uint16_t gpio_data[] = {
0xAA55,
0x55AA
};
// set up the DMA channel
__HAL_RCC_DMA2_CLK_ENABLE();
__HAL_RCC_DMA2_FORCE_RESET();
__HAL_RCC_DMA2_RELEASE_RESET();
DMA_Stream_TypeDef * stream = DMA2_Stream5;
stream->M0AR = (uint32_t) gpio_data; // memory 0 address
stream->NDTR = sizeof(gpio_data) / (sizeof(*gpio_data)); // number of data
stream->FCR |= DMA_SxFCR_DMDIS; // direct mode disabled
//stream->FCR |= 0b11 << DMA_SxFCR_FTH_Pos; // fifo threshold full
// TIM1_UP is channel 6 on DMA2 stream 5
// stream->CR = 0;
stream->CR |= 6 << DMA_SxCR_CHSEL_Pos; // channel 6
stream->CR |= 0b01 << DMA_SxCR_MSIZE_Pos; // memory size half-word
stream->CR |= 0b01 << DMA_SxCR_PSIZE_Pos; // peripheral size half-word
stream->CR |= DMA_SxCR_MINC; // memory increment
stream->CR |= DMA_SxCR_CIRC; // circular
stream->CR |= 0b01 << DMA_SxCR_DIR_Pos; // memory to peripheral
stream->PAR = (uint32_t) &GPIOC->ODR; // peripheral address
stream->CR |= 0b1 << DMA_SxCR_PL_Pos; // priority medium
stream->CR |= DMA_SxCR_EN; // enable
TIM1->CR1 |= TIM_CR1_CEN; // enable counter
2020-09-24 07:45 AM
Just moving the dma buffer out of the main block breaks this. I guess it's something to do with where the array is located in ram but I'm not clear on how to fix that.
2020-09-24 08:44 AM
> moving the dma buffer out of the main block
What does that mean?
JW
2020-09-24 08:54 AM
If I put the gpio_data array inside `int main(void) {}`, after `USER CODE BEGIN 2` then it works. If I put the gpio_data array in the private variables at the top in `USER CODE BEGIN PV` then it stops working.