STM32F103C6T6A Bare Metal Programming – Capture mode

The CC module can capture the value of the counter at the moment a specified event occurs on an input pin. This is useful for measuring input signal frequencies, pulse widths, and timing intervals. If you haven’t gone throught my previous post “STM32F103C6T6A Bare Metal Programming – Compare mode”, I strongly recommend you to go through that before continuing this.

This is almost opposite of the comapre mode, here whenever an event happen in the physical pin TIMx_CHn, the data from the counter register will be copied into the CCR register. The capture module is used to measure the time between two events in the input.

Exercise

Write a program to print the counter value through the UART when a button is pressed. Since we haven’t covered the UART now, so you can take the souce code to access UART from my GitHub repository. I have given the link at the end of the post.

Registers used

  • APB2 peripheral clock enable register (RCC_APB2ENR) – Enable the clock source for timer and GPIOA
  • Port A configuration register high (GPIOA_CRH) – Configure the input pin
  • Port A output data register (GPIOA_ODR) – Configure the input pin
  • TIM1 Prescaler register (TIM1_PSC) – Configure the timer
  • TIM1 auto-reload register (TIM_ARR) – Configure the timer
  • TIM1 control register 1 (TIM1_CR1) – Enable the timer
  • TIM1 status register (TIM1_SR) – Capture flag
  • TIM1 capture/compare mode register 1 (TIM1_CCMR1) – Select the trigger
  • TIM1 capture/compare enable register (TIMx_CCER) – Enable the capture mode
  • TIM1 Capture/Compare register (TIM1_CCR1) – Take the counter value after capture.

Solution

As I mentioned in the compare module post, I am using the TIM1_CH1(PA8) as the input. From the table 22 of reference manual RM0008, the pin should be configured as input floating. But interestingly input pull-up and pull-down also working fine in capture mode. So I am using the pull down mode so that I can connect the pin and 3.3v supply via a push button switch to give logical high input.

// Configure GPIOA8 pin as input
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;               // Enable clock for Port A
GPIOA->CRH &= ~(GPIO_CRH_CNF8 | GPIO_CRH_MODE8);  // Set PA8 as input
GPIOA->CRH |= GPIO_CRH_CNF8_1;                    // Set PA8 as pull-up/pull-down
GPIOA->ODR &= ~(GPIO_ODR_ODR8);                   // Set PA8 as pull down;

Enable the timer with random prescalar and auto reload register value

// Enable TIMER1
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;               // Enable clock for TIM1
TIM1->PSC = 0x3FFF;                               // set prescalar
TIM1->ARR = 0xFFFF;                               // set maximum count
TIM1->CR1 |=  TIM_CR1_CEN;                        // Start the timer.

Now we have to select the trigger which is used to capture the value. We have so many trigger options that can be chosen. You can check the description of bits 7:4 IC1F: Input capture 1 filter, I am selecting the internal clock for sampling and wait atleast 8 cycles to ensure the state is high or low. So the value of IC1F would be 0011

// Select filter trigger
TIM1->CCMR1 &= ~(TIM_CCMR1_IC1F);                 // Sampling at internal clock and wait unitl 8 cycles
TIM1->CCMR1 |= (TIM_CCMR1_IC1F_1 | TIM_CCMR1_IC1F_0);   // to ensure the low/high state

We have configured the input as pull down. So when ever the button is pressed the raising edge will come. So the trigger should be raising edge. And then enable the capture module.

// Select edge and channel
TIM1->CCMR1 |= TIM_CCMR1_CC1S_0;                  // CC1 configured as input, IC1 mapped with TI1 (TIM1_CH1)
TIM1->CCMR1 &= ~(TIM_CCMR1_IC1PSC);               // No prescalar. capture on every event on the TI
TIM1->CCER &= ~(TIM_CCER_CC1P);                   // Raising edge of IC1.
TIM1->CCER |= TIM_CCER_CC1E;                      // Capture enabled for channel 1

So we have completed the capture mode setup. Now from the main function we will wait until the CC1IF flag in the TIM1_SR register is set. When ever it’s set we can take the counter value from the TIM1_CCR1 register.

uint16_t data;
while( (TIM1->SR & TIM_SR_CC1IF) == 0) {}
TIM1->SR &= ~(TIM_SR_CC1IF);
data = TIM1->CCR1;
uart1_send_string("Captured data ... 0x%x\r\n", data);

I have pushed the complete source code in my GitHub page, you can access it from this link

Leave a comment