CH32V003 Programming: How to use Timer Interrupt

CH32V003 has two timers, both could be used to generate periodic interrupt.

What is periodic Timer Interrupt?

A periodic timer interrupt is a signal generated by a timer at regular intervals. The timer is configured to count down from a specific value, and when it reaches zero, it generates an interrupt. The interrupt causes the processor to stop executing the current program and jump to a special function called an interrupt service routine (ISR).

The ISR typically performs some time-sensitive task, such as blinking an LED or updating a sensor reading. Once the ISR is finished executing, the processor returns to the main program.

CH32V003 Programming: How to use Timer Interrupt 1

Periodic timer interrupts are used in a wide variety of applications, including:

  • Generating precise delays
  • Controlling real-time systems
  • Measuring time intervals
  • Sampling data from sensors
  • Generating periodic signals
  • Creating Software RTC

Periodic timer interrupts are a powerful tool for developing embedded systems. By using them, you can create applications that can respond to events in a timely and predictable manner.

Configuring Timer for Periodic Interrupt

Now, let me explain, how to configure a 1-second timer interrupt.

Taking the system clock frequency of 48,000,000 Hz as an example, we can configure the PSC register to change the clock frequency provided to the TIM.

For instance, when I configure the PSC to 48-1, the clock provided to the timer becomes 1,000,000 Hz.

This means that the CNT register of the timer will increment by 1 every 1/1,000,000 = 0.000001 seconds.

The interrupt mode we have configured is the update interrupt, which means that when the CNT register reaches the same value as the ARR register, it will trigger a timer update interrupt, and at the same time, the CNT register will reset to zero. So, if we assign the ARR register a value of 200-1, we get a stable interrupt every 1/1,000,000 * 200 = 0.0002 seconds.

Following this logic, we can have many combinations of ARR and PSC register values to achieve a 1-second timer interrupt.

For example, when the system clock is 48,000,000 Hz, PSC=48000-1, and ARR=1000-1, we get (48000-1)/48,000,000 * (1000-1) = 1 second.

For PSC=24000 and ARR=2000, we get (24000-1)/48,000,000 * (2000-1) = 1 second.

However, it should be noted that these two registers are 16 bits, so care should be taken not to write a value greater than 65535 to the register.

/**
 * The function initializes the TIM2 timer with the specified period and prescaler, enables the TIM2
 * update interrupt, configures the NVIC interrupt settings, and enables the TIM2 timer.
 * 
 * @param arr The "arr" parameter is the value that determines the auto-reload register (ARR) value of
 * the timer. It specifies the maximum value that the timer counter will reach before generating an
 * update event.
 * @param psc The "psc" parameter stands for Prescaler value. It is used to divide the timer clock
 * frequency before it is fed into the timer counter. This division helps in achieving the desired
 * timer resolution.
 */
void TIM_INT_Init(u16 arr, u16 psc)
{
    TIM_TimeBaseInitTypeDef TIMBase_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIMBase_InitStruct.TIM_Period = arr;
    TIMBase_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIMBase_InitStruct.TIM_Prescaler = psc;
    TIMBase_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM2, &TIMBase_InitStruct);

    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 5;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 5;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    TIM_Cmd(TIM2, ENABLE);
}

Main function and Timer Interrupt handler function will be as shown below:

/*********************************************************************
 * @fn      main
 *
 * @brief   Main program.
 *
 * @return  none
 */
int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SystemCoreClockUpdate();
    Delay_Init();
    USARTx_CFG();
    TIM_INT_Init(1000-1,  48000-1);
    while (1);

}

/**
 * The function TIM2_IRQHandler handles the interrupt for Timer 2 and prints "In" when the update
 * interrupt flag is set.
 */
void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    {
        // this can be replaced with your code of flag so that in main's that flag can be handled 
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 
    }
}

I hope it was easy to understand how to use Timer for periodic interrupts for your application code requirement.

Similarly, you can configure Timer 1 for periodic interrupt.


See also:

  1. Setting up the Development Environment for CH32V003: Compile and Run first example code
  2. CH32V003: GPIO as Output
  3. CH32V003: GPIO as Input (Polling, Interrupt)
  4. CH32V003: UART Transmit / Receive data
  5. CH32V003: PWM output
  6. CH32V003: How to use an ADC
  7. CH32V003: How to use I2C
  8. CH32V003: How to use SPI
  9. CH32V003: Timer based Periodic Interrupt
  10. CH32V003: How to read 64-bit Unique ID

I am currently working as an embedded systems design consultant and helping companies build custom embedded products, develop test automation solution for their PCB.

If you have any feedback about the blog, you can share in the comments below or you can also contact me directly.


One comment

  1. Dear PALLAV

    The code for the CH32V003: Timer based Periodic Interrupt dose not work:
    1. It goes into the interrupt once and that to HardFault_Handler
    2. Attempt to change TIM2 to TIM1 shows errors.

    Thank you
    Danny

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.