Skip to content

Instantly share code, notes, and snippets.

@csrohit
Last active May 13, 2025 10:35
Show Gist options
  • Save csrohit/0868f2e09caa350b3b12dc31ae9441b1 to your computer and use it in GitHub Desktop.
Save csrohit/0868f2e09caa350b3b12dc31ae9441b1 to your computer and use it in GitHub Desktop.
This gist if for article on Timer3 Output Compare
// Set AF02(Tim3 Channel1 & 2) as alternate function for PA6 & PA7
GPIOA->AFR[0] &= ~(GPIO_AFRL_AFRL6 | GPIO_AFRL_AFRL7);
GPIOA->AFR[0] |= GPIO_AFRL_AFRL6_1 | GPIO_AFRL_AFRL7_1;
// Set AF02(Tim3 Channel3 & 4) as alternate function for PB0 & PB1
GPIOB->AFR[0] &= ~(GPIO_AFRL_AFRL0 | GPIO_AFRL_AFRL1);
GPIOB->AFR[0] |= GPIO_AFRL_AFRL0_1 | GPIO_AFRL_AFRL1_1;
/* Configure output compare toggle mode for CH1 and CH2 */
TIM3->CCMR1 &= ~(TIM_CCMR1_OC1M | TIM_CCMR1_OC2M);
TIM3->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_0;
/* Enable preload for CH1 and CH2, set as output */
TIM3->CCMR1 |= TIM_CCMR1_OC2PE | TIM_CCMR1_OC1PE;
TIM3->CCMR1 &= ~(TIM_CCMR1_CC1S | TIM_CCMR1_CC2S);
/* Configure output compare toggle mode for CH3 and CH4 */
TIM3->CCMR2 &= ~(TIM_CCMR2_OC3M | TIM_CCMR2_OC4M);
TIM3->CCMR2 |= TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_0 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_0;
/* Enable preload for CH3 and CH4, set as output */
TIM3->CCMR2 |= TIM_CCMR2_OC3PE | TIM_CCMR2_OC4PE;
TIM3->CCMR2 &= ~(TIM_CCMR2_CC3S | TIM_CCMR2_CC4S);
// CH1 event triggers after 200ms
TIM3->CCR1 = 200 - 1;
// CH2 event triggers after 400ms
TIM3->CCR2 = 400 - 1;
// CH3 event triggers after 600ms
TIM3->CCR3 = 600 - 1;
// CH4 event triggers after 800ms
TIM3->CCR4 = 800 - 1;
void delay_ms(uint32_t ms)
{
/* Wait for the update interrupt */
while (!flag)
__WFI();
flag = 0;
}
// Enable Clock for PortA and PortB
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN;
// Enable clock for Tim3
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
// Enable Capture-Compare Channel 1, 2, 3 & 4
TIM3->CCER |= TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E;
// Enable timer
TIM3->CR1 |= TIM_CR1_CEN;
TIM3->DIER |= TIM_DIER_UIE;
NVIC_EnableIRQ(TIM3_IRQn);
volatile int flag = 0;
void TIM3_IRQHandler(void)
{
if (TIM3->SR & TIM_SR_UIF)
{
TIM3->SR &= ~TIM_SR_UIF; // Clear interrupt flag
flag = 1;
}
}
int main(void)
{
// Enable clock for GPIOC
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
// Set PC13 as output
GPIOC->MODER &= ~GPIO_MODER_MODER13;
GPIOC->MODER |= GPIO_MODER_MODER13_0;
Timer3_Init();
while (1)
{
GPIOToggle(GPIOC, GPIO_ODR_OD13); // Toggle LED
delay_ms(1000); // Wait 1 second
}
}
// Configure PA6 & PA7 as Alternate function
GPIOA->MODER &= ~(GPIO_MODER_MODE6 | GPIO_MODER_MODE7);
GPIOA->MODER |= GPIO_MODER_MODE6_1 | GPIO_MODER_MODE7_1;
// Configure PB0 & PB1 as Alternate function
GPIOB->MODER &= ~(GPIO_MODER_MODE0 | GPIO_MODER_MODE1);
GPIOB->MODER |= GPIO_MODER_MODE0_1 | GPIO_MODER_MODE1_1;
// Timer clock frequency = SystemCoreClock / (PSC + 1)
// Assuming SystemCoreClock is 16 MHz,
// To get a timer clock of 1 KHz (1 ms per tick): (16 MHz / 1KHz) - 1 = 15999
TIM1->PSC = 15999; // Prescaler value for 1 KHz timer clock
// For a 1 s period with 1 ms ticks: 1 s / 1 ms = 1000 counts
// Since counting starts from 0, ARR should be set to 999.
TIM3->ARR = 1000 - 1;
Register Purpose
RCC->AHB1ENR Enable clock for GPIO ports (GPIOA, GPIOB)
RCC->APB1ENR Enable clock for TIM3 timer peripheral
GPIOx->MODER Configure GPIO pins mode (Alternate Function for timer channels)
GPIOx->AFR Select alternate function (AF2) for timer channels
TIM3->PSC Prescaler to divide timer clock frequency
TIM3->ARR Auto-reload register to set timer period
TIM3->DIER Enable timer interrupts (update interrupt)
NVIC_EnableIRQ Enable timer interrupt in NVIC
TIM3->CCR1, CCR2, CCR3, CCR4 Capture/Compare registers to set compare values for each channel
TIM3->CCMR1, CCMR2 Capture/Compare mode registers to configure output compare mode (toggle, PWM, etc.)
TIM3->CCER Capture/Compare enable register to enable channel outputs
TIM3->CR1 Control register to start/stop timer and configure basic timer features
void Timer3_Init(void)
{
// Enable clocks for GPIOA, GPIOB, and TIM3
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN;
// Set PA6, PA7 (TIM3_CH1, TIM3_CH2) to alternate function mode
GPIOA->MODER &= ~(GPIO_MODER_MODE6 | GPIO_MODER_MODE7);
GPIOA->MODER |= GPIO_MODER_MODE6_1 | GPIO_MODER_MODE7_1;
// Set PB0, PB1 (TIM3_CH3, TIM3_CH4) to alternate function mode
GPIOB->MODER &= ~(GPIO_MODER_MODE0 | GPIO_MODER_MODE1);
GPIOB->MODER |= GPIO_MODER_MODE0_1 | GPIO_MODER_MODE1_1;
// Set alternate function AF2 for these pins
GPIOA->AFR[0] &= ~(GPIO_AFRL_AFRL6 | GPIO_AFRL_AFRL7);
GPIOA->AFR[0] |= GPIO_AFRL_AFRL6_1 | GPIO_AFRL_AFRL7_1;
GPIOB->AFR[0] &= ~(GPIO_AFRL_AFRL0 | GPIO_AFRL_AFRL1);
GPIOB->AFR[0] |= GPIO_AFRL_AFRL0_1 | GPIO_AFRL_AFRL1_1;
// Enable TIM3 clock
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
// Set prescaler for 1 microsecond tick (assuming 16 MHz clock)
TIM3->PSC = 15999;
// Auto-reload for 1 ms period
TIM3->ARR = 1000 - 1;
// Enable update interrupt and NVIC IRQ
TIM3->DIER |= TIM_DIER_UIE;
NVIC_EnableIRQ(TIM3_IRQn);
// Set output compare values for each channel
TIM3->CCR1 = 200 - 1;
TIM3->CCR2 = 400 - 1;
TIM3->CCR3 = 600 - 1;
TIM3->CCR4 = 800 - 1;
// Configure output compare toggle mode for channels 1 and 2
TIM3->CCMR1 &= ~(TIM_CCMR1_OC1M | TIM_CCMR1_OC2M);
TIM3->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_0;
TIM3->CCMR1 |= TIM_CCMR1_OC2PE | TIM_CCMR1_OC1PE;
TIM3->CCMR1 &= ~(TIM_CCMR1_CC1S | TIM_CCMR1_CC2S);
// Configure output compare toggle mode for channels 3 and 4
TIM3->CCMR2 &= ~(TIM_CCMR2_OC3M | TIM_CCMR2_OC4M);
TIM3->CCMR2 |= TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_0 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_0;
TIM3->CCMR2 |= TIM_CCMR2_OC3PE | TIM_CCMR2_OC4PE;
TIM3->CCMR2 &= ~(TIM_CCMR2_CC3S | TIM_CCMR2_CC4S);
// Enable all 4 channels
TIM3->CCER |= TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E;
// Start the timer
TIM3->CR1 |= TIM_CR1_CEN;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment