STM32F407VET6 PWM using PD12 pin (bare metal)

Please help me because I am trying to practice PWM bare metal and using PD12 pin, but it is not work.

#include "stm32f4xx.h"                  // Device header
void GPIO_Init(void);
void Timer4_init(void);
void delay(int ms );
#define rate 255

int main(void)
{

GPIO_Init();
Timer4_init();

while(1)
{
	for(int i=0;i<rate;i++){
		TIM4->CCR1=i;
		TIM4->CCR2=rate-i;
		delay(5);
	}
	for (int i=255;i>0;i--){
		TIM4->CCR1=i;
		TIM4->CCR2=rate-i;
		delay(5);
	}

}
}

void GPIO_Init(void)
{
	RCC->AHB1ENR|=(1U<<3);
	GPIOA->AFR[1]|=(1U<<17);

/*Set PD12 as ALTERNATE FUNCTION */
	GPIOA->MODER &=~(1U<<24);
	GPIOA->MODER |= (1U<<25);

}
void  Timer4_init(void){
	RCC->APB1ENR|=(1U<<2); //enable clock access to tim4
	TIM4->PSC=0; //set prescaller to 0 (no divider)
	TIM4->ARR=255; //set the maximum count value
	TIM4->CNT=0; //seset the current count
	TIM4->CCMR1=(1<<5)|(1<<6); //configure the pins as PWM
	TIM4->CCER|=(1U<<0); //enbale channel1
	TIM4->CR1=1; //enable timer
}


void delay(int ms)

{

	int i;

for(; ms>0 ;ms--)

{

	for(i =0; i<3195;i++);

}

}

1 Like

Post updated: (Thanks @Nikolas for the update)

Hi,

You have to enable the clock for port D if you want use PD12, instead you have enable clock for port C.

you have to configure the Port D, instead you have configured PORT A.

Enable clock for Port D:

RCC->AHB1ENR|=(1U<<3);

Set PD12 as Alternate function mode.

image

GPIOD->MODER  |= ~(1UL << (12 * 2));
GPIOD->MODER  &= (1UL << (12 * 2+1));

Configure the Alternate function to AF2 for TIM4_CH1

image

GPIOD->AFR[1] |= (1U<<17);

Make the following changes and see if its working. If not I will suggest some additional changes.

3 Likes

Ok i did not check the data sheet yet, I only used the data sheet snippets from the reply message before.

But after a short look I think your code is ok. The only thing you did wrong is you used the GPIOA pointer instead of GPIOD. I think you do not have to change the RCC->AHB1ENR |= (1U << 3); to RCC->AHB1ENR |= (1U << 4); because that would be wrong. You would enable the clock on GPIO E instead of GPIO D.

I think you should also keep the code to enable the alternate function but use the GPIOD pointer. Because GPIOD->MODE |= ~(1UL << (12 * 2)) does the wrong thing. It does not set a zero to the 24th bit as expected. It does an OR operation with the whole register and 0xFEFFFFFF. So please do not change your code there. Just adjust the GPIOA pointer to the GPIOD.

And just after a short look I am not sure why you have those two for loops in you main function inside the while loop. Because the thing is, if you have enabled auto reload on your timer (check if it is enabled) you should not load constantly values into the capture compare register.

Your timer counts from zero to the value stored in the ARR register and if it reaches the value in the CCR the PWM signal changes its polarity. Means the ARR register defines the frequency of your signal and the CCR defines the duty cycle.

I also found a nice video on youtube that really should help you on this problem. So just check it out and tell me if you have further questions: Stm32 Timers in PWM mode - YouTube

1 Like

Hi Nikolas,

I completely agree with you. I did a mistake. To enable clock for port D use RCC->AHB1ENR |= (1U << 3) . I will update my previous post.

In the second case, the 24th bit of 0xFEFFFFFF is 0. The reset value for all ports except port A and port B is zer0. Please see the below image.

image

I think both code will work for bit 24 and 25, except rest of the bits.

Hey Prototype,

Thank you for the response and thank you also for those data sheet screen shots. They are really helpfull to analyze this problem here :+1: :muscle:

Yes you are right that, because the default value of the register is zero, your code will work also. But the thing is, that it only works if bit 24 is zero. Otherwise you would do an OR operation on bit 24 with zero. The result would be one. This would not be the outcome that was expected.
And with your code, you will also set all the other bits in the register. What would be bad if those bits matter.

I totally agree with you that for this program here, your approach would work because we have a register that holds its defult value and all the other bits in the register do not matter.

Do not understand me wrong, but this is not a good way to do it, because if you extend the program with new features, this can easaly lead to bugs if you do not pay attention.
So I would always use registerName &= ~(1U << bitNumber) to clear bits and registerName |= (1U << bitNumber) to set bits. This is how it should be done in my mind :slight_smile:

But I am here for further discusion if you see it in another way :wink:

1 Like

thank you a lot, it works !!!

1 Like

Sorry for late response. I was little busy.

The rest of the pins will be in any of the following states except alternate function mode if we don’t care. That’s normal thing for unused bits.

fd075c8b59948e66805d62af45ba978298c1ad05

How? Given the fact that a programmer should have the knowledge of the previous state of the register, how is it possible?