Variable PWM duty cycle signal on STM32F4
Hello everyone!
For last week I am struggling to use Variable PWM duty cycle to generate signal for an array of addressable LEDs (WS2812) which I had successfully running on STM32F103.
I was forced to use STM32F412 and ever since I cannot port my old code to work with the new MCU.
Few days ago I decided to shift my attempt to a register level of things and I cannot update CCR3 using DMA, to allow me changing the duty cycle simultaneously (every ARR reload).
I found this topic and it lead me to a very helpful code, which in theory would allow me to dump values into CRR3 using DMA, but the DMA doesn't seem to put any values in CCR3.
Can you please tell me if you see anything I am doing wrong in my code?
I hope it is not too much code in one post
#include "stm32f412rx.h"
void GPIO_Init(void);
void TIM_Init(void);
void DMA_Init(void);
const uint16_t valuesTable[] = { // Values to be dumped CCR3 registers simultaneously
20, 20,
30, 10,
20, 20,
30, 10
};
void main(void){
__HAL_RCC_TIM4_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
GPIO_Init();
DMA_Init();
TIM_Init();
while(1){
}
}
void GPIO_Init(void){
GPIOB -> MODER |= GPIO_MODER_MODE8_1; // ALTERNATE FUNCTION MODE
GPIOB -> OTYPER &= ~GPIO_OTYPER_OT8; // PUSH-PULL TYPE
GPIOB -> OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8_1; // FAST SPEED
GPIOB -> PUPDR |= GPIO_PUPDR_PUPDR8_1; // PULLDOWN
GPIOB -> AFR[1] |= (2UL << (0U)); // ALTERNATE FUNCTION #2 (TIM4 CH3)
}
void TIM_Init(void){
/* CR1 - CONTROL REGISTER 1
* CR2 - CONTROL REGISTER 2
* PSC - PRESCALER REGISTER
* ARR - AUTO-RELOAD REGISTER
* SR - STATUS REGISTER
* SMCR- SLAVE MODE CONTROL REGISTER
* DIER- DMA/INTERRUPT ENABLE REGISTER
* EGR - EVENT GENERATION REGISTER
* CCMR1-CAPTURE/COMPARE MODE REGISTER 1
* CCMR2-CAPTURE/COMPARE MODE REGISTER 2
* CCER- CAPTURE/COMPARE ENABLE REGISTER
* CNT - COUNT REGISTER
* CCR3- CAPTURE/COMPARE 3 REGISTER IN ITS OWN
* DMAR- DMA ADDRESS FOR FULL TRANSFER
* */
TIM4 -> CR1 &= ~(0x3UL <<(8U)); // CLOCK DIVISION 1 (NO DIVISION)
TIM4 -> CR1 &= ~(TIM_CR1_CMS_1); // EDGE ALIGNED MODE
TIM4 -> CR1 &= ~(TIM_CR1_DIR); // COUNTER AS UPCOUNTER
TIM4 -> CR1 &= ~(TIM_CR1_OPM); // ONE-PULSE MODE DISABLED
TIM4 -> CR1 &= ~(TIM_CR1_URS); // UPDATE REQUEST SOURCE
TIM4 -> CCMR2 = 0
|( 1 * (6UL << (4U))) // OUTPUT COMPARE PWM 1 MODE
|( 1 * (1UL << (3U))) // OUTPUT COMPARE 3 PRELOAD ENABLE
|( 0 * (1UL << (2U))) // OUTPUT COMPARE FAST MODE ENABLE
;
TIM4 -> CCER = 0
|( 1 * (1UL << (8U))) // CAPTURE/COMPARE OC3 ACTIVE
|( 0 * (1UL << (9U))) // CC3 POLARITY ACTIVE LOW
;
TIM4 -> DCR = 0
|(17UL) // INDEX OF CCR3 REGISTER
|((0UL) << (8U)) // 1 TRANSFER
;
TIM4 -> ARR = 40; // AUTO-RELOAD REGISTER
TIM4 -> PSC = 10; // PRESCALER
TIM4 -> DIER |= (1UL <<(14U)); // UPDATE DMA REQUEST ENABLE
TIM4 -> CR1 |= TIM_CR1_ARPE; // AUTO-RELOAD PRELOAD ENABLE
DMA1_Stream7 -> CR |= DMA_SxCR_EN; // DMA GO!!
TIM4 -> CR1 |= TIM_CR1_CEN; // TIM GO!!
}
void DMA_Init(void){
/* CR - DMA STREAM CONFIGURATION REGISTER
* NDTR - DMA STREAM NUMBER OF DATA TO TRANSFER
* PAR - DMA STREAM PERIPHERAL ADDRESS
* M0AR - DMA STREAM MEMORY ADDRESS 1
* M1AR - DMA STREAM MEMORY ADDRESS 2
* FCR - DMA STREAM FIFO CONTROL REGISTER
* */
DMA1_Stream7 -> M0AR = (uint32_t)valuesTable; // MEMORY(BUFFER) ADDRESS
DMA1_Stream7 -> PAR = (uint32_t)&TIM4->DMAR; // PERIPHERAL ADDRESS (SHADOW REGISTER)
DMA1_Stream7 -> NDTR = sizeof(valuesTable) / sizeof(valuesTable[0]); // TRANSFER SIZE
DMA1_Stream7 -> CR = 0
|( 1 * (2UL << (25U))) // CHANNEL 2 SELECTED
|( 0 * (0UL << (23U))) // MBURST SINGLE TRANSFER
|( 0 * (0UL << (21U))) // PBURST SINGLE TRANSFER
|( 0 * (1UL << (19U))) // CURRENT TRGET (ONLY IN DOUBLE-BUFFER MODE)
|( 0 * (1UL << (18U))) // DOUBLE-BUFFER MODE
|( 1 * (1UL << (16U))) // PRIORITY LEVEL MEDIUM
|( 0 * (0UL << (15U))) // PERIPHERAL INCREMENT OFFSET SIZE 0
|( 1 * (1UL << (13U))) // MEMORY DATA SIZE HALFWORD
|( 1 * (1UL << (11U))) // PERIPH DATA SIZE HALFWORD
|( 1 * (1UL << (10U))) // MEMORY INCREMENT MODE ENABLE
|( 0 * (1UL << (9U))) // PERIPH INCREMENT MODE ENABLE
|( 1 * (1UL << (8U))) // CIRCULAR MODE ENABLE
|( 1 * (1UL << (6U))) // DATA TRANSFER DIRECTION (MEM 2 PER)
|( 0 * (1UL << (5U))) // PERIPHERAL AS FLOW CONTROLLER
|( 0 * (1UL << (4U))) // TRANSFER COMPLETE INTERRUPT ENABLE
|( 0 * (1UL << (3U))) // HALF TRANSFER COMPLETE INTERRUPT ENABLE
|( 0 * (1UL << (2U))) // TRANSFER ERROR INTERRUPT ENABLE
|( 0 * (1UL << (1U))) // DIRECT MODE INTERRUPT ENABLE
;
}