¿Está restringida la dirección de destino STM32 DMA?

Estoy usando un stm32f446y me gustaría usar la TIM4_CH2solicitud con el modo DMA1in Memory-to-peripheralpara transferir datos de una matriz al ODR(registro de datos de salida) de un puerto GPIO.

Por alguna razón, solo funciona con algunas direcciones de destino (y no con la &GPIOx->ODRdirección), incluso a través de los estados del manual de referencia en 9.3.6 Modos de origen, destino y transferencia :

Tanto las transferencias de origen como las de destino pueden dirigirse a periféricos y memorias en toda el área de 4 GB, en direcciones comprendidas entre 0x0000 0000 y 0xFFFF FFFF

¿Hay alguna otra restricción que deba tener en cuenta?

HAL_TIM_OC_Start(&htim4, TIM_CHANNEL_2);


uint16_t buffer[] = {1,2,3,4,5};
uint32_t dataLength = sizeof(buffer)/sizeof(buffer[0]);
uint32_t srcAddress = (uint32_t) buffer;
//uint32_t dstAddress = (uint32_t) &(TIM4->CCR2); // Works
//uint32_t dstAddress = (uint32_t) &(TIM4->CCR4); // Works
//uint32_t dstAddress = (uint32_t) &(TIM5->CCR4); // Doesn't work
uint32_t dstAddress = (uint32_t) &(GPIOC->ODR); // Doesn't work


HAL_DMA_Start_IT(&hdma_tim4_ch2, srcAddress, dstAddress, dataLength);
__HAL_TIM_ENABLE_DMA(&htim4, TIM_DMA_CC2);

El código de inicialización de DMA:

hdma_tim4_ch2.Instance = DMA1_Stream3;
hdma_tim4_ch2.Init.Channel = DMA_CHANNEL_2;
hdma_tim4_ch2.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tim4_ch2.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim4_ch2.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim4_ch2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_tim4_ch2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_tim4_ch2.Init.Mode = DMA_NORMAL;
hdma_tim4_ch2.Init.Priority = DMA_PRIORITY_LOW;
hdma_tim4_ch2.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma_tim4_ch2);

__HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_CC2],hdma_tim4_ch2);

Configuración del temporizador:

TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;

htim4.Instance = TIM4;
htim4.Init.Prescaler = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 72;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim4);

sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig);

HAL_TIM_OC_Init(&htim4);

sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC2REF;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig);

sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_OC_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_2);
Retrocediendo mis comentarios para permitirle concentrarse en el problema...

Respuestas (1)

Las direcciones DMA están restringidas por la arquitectura de la memoria (Consulte el Capítulo 2, Arquitectura de memoria y bus en el manual de referencia ). La siguiente figura del manual de referencia ilustra el problema de usar DMA1:

arquitectura de memoria stm32

El problema se puede resolver simplemente usando DMA2 (y otra solicitud de acuerdo con la tabla de asignación de solicitudes de DMA2). Por ejemplo, DMA2 con la solicitud del canal 1 del temporizador 1 funcionará bien:

HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_1);

uint16_t buffer[] = {0,1,2,4,8,16,32,64,128,256,512,1024,2048};
uint32_t dataLength = sizeof(buffer)/sizeof(buffer[0]);
uint32_t srcAddress = (uint32_t) buffer;
uint32_t dstAddress = (uint32_t) &(GPIOC->ODR);

HAL_DMA_Start_IT(&hdma_tim1_ch1, srcAddress, dstAddress, dataLength);
__HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_CC1);