4.1.1.2. SoC Peripheral: Timer

Purpose

  • To get familar with the peripheral of GD32VF103 MCU: Timer

  • To know how to program peripheral registers to control the MCU Timer

Requirements

The following hardware and source codes are required:

  • PC host

  • Nuclei board (RV-STAR Development Board)

  • USB Type-C cable

  • nuclei-sdk/board-labs/rvstar/timer_pwm

Content

  • Through learning the Chapter 15 Timer of GD32VF103 MCU User Mannual to get familar with the usage of Timer.

  • Programming the Timer peripheral registers to generate PWM signals, and output these through pins which connected to RGB LED. Assigning the duty cycle of generated PWM signals to set the color of on-board RGB LED.

Principles

GD32VF103 MCU offers up to one 16-bit advanced timer (TIMER0), four 16-bit general timers(TIMERx=1,2,3,4), and two 16-bit basic timer (TIMER5 & TIMER6).

The advanced timer (TIMER0) can be seen as a three-phase PWM multiplexed on 6 channels. It has complementary PWM outputs with programmable dead-time generation. It can also be used as a complete general timer. The 4 independent channels can be used for input capture, output compare, PWM generation (edge-aligned or center-aligned counting modes), single pulse mode output. If configured as a general 16-bit timer, it can be synchronized with external signals or to interconnect with other general timers together which have the same architecture and features.

The general timer, known as TIMERx=1,2,3,4 can be used for a variety of purposes including general time, input signal pulse width measurement or output waveform generation such as a single pulse generation or PWM output, up to 4 independent channels for input capture/output compare. The general timer also supports an encoder interface with two inputs using quadrature decoder.

The basic timer, known as TIMER5 and TIMER6 are mainly used for DAC trigger generation. They can also be used as a simple 16-bit time base.

From the schematic of RV-STAR Development Board, we can see that PA1, PA2, PA3 are used to control RGB LED, and these pins can be used as TIMER1_CH1, TIMER1_CH2, TIMER1_CH3, so here we use TIMER1.

lab1_2_fig1

Fig. 4.5 Part of RV-STAR Development Board schematic

In Nuclei SDK, gd32vf103_gpio.h provides API to config the SoC GPIO, gd32vf103_timer.h provides API to operate the SoC Timer.

The code for this lab is located in nuclei-sdk/board-labs/rvstar/timer_pwm. You can see it in the appendix. It can be divided into 3 parts:

  • Part1 : GPIO config, set PA1, PA2, PA3 to used as peripheral alternate function(TIMER1 CH1 CH2 CH3)

  • Part2 : Timer config, program TIMER1 to generate PWM signals

  • Part3 : RGB config, assign the duty cycle of generated PWM signals to set the color of on-board RGB LED

Steps

  1. Connect RV-STAR Development Board and your computer with the USB Type-C cable.

lab1_2_fig2

Fig. 4.6 Connect with PC

Note

When connect your board with the PC, just keep the on-board jumpers as default. About on-board jumpers’ function, please refer to Jumper Section.

  1. Compile and run the nuclei-sdk/board-labs/rvstar/timer_pwm example. About the develop environment, you can choose Nuclei SDK or Segger Embedded Studio.

  • Nuclei SDK

    Using the following commands:

    cd /nuclei-sdk/board-labs/rvstar/timer_pwm
    make SOC=gd32vf103 BOARD=gd32vf103v_rvstar upload
    

    Note

    1. About the acquisition of source codes, please refer to Software Source Codes.

    2. About the detailed usage of Nuclei SDK, please refer to How to develop with Nuclei SDK.

  • Segger Embedded Studio

    Using the following actions:

    lab1_2_fig3

    Fig. 4.7 Operations in Embedded Studio

    Note

    1. About the acquisition of Segger Embedded Studio solutions, please refer to Software Source Codes.

    2. About the detailed usage of Segger Embedded Studio, please refer to How to develop with Segger Embedded Studio.

  1. Check the color change of on-board RGB LED.

lab1_2_fig4

Fig. 4.8 Experimental results

Exercises

Try to create you own application which using TIMER4 to generate PWM signals to control on-board RGB LED.

Appendix

rvstar/timer_pwm/main.c

/**
    \brief      main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{

    int cnt;

    /* GPIO Config */
    gpio_config();

    /* Timer Config */
    timer_config();

    while(1)
    {
      /* set rgb_led status */

        for(cnt = 255; cnt >=0; cnt--)
        {
          rgb_config(cnt, 0, 0);
          delay_1ms(5);
        }
        for(cnt = 255; cnt >=0; cnt--)
        {
          rgb_config(0, cnt, 0);
          delay_1ms(5);
        }
        for(cnt = 255; cnt >=0; cnt--)
        {
          rgb_config(0, 0, cnt);
          delay_1ms(5);
        }
    }

    return 0;
}

/**
    \brief      configure the GPIO ports
    \param[in]  none
    \param[out] none
    \retval     none
*/
void gpio_config()
{
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_AF);

    /*Configure PA1 PA2 PA3(TIMER1 CH1 CH2 CH3) as alternate function*/
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, LEDG_PIN);
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, LEDR_PIN);
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, LEDB_PIN);
}

/**
    \brief      configure the TIMER peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
void timer_config()
{

   timer_oc_parameter_struct timer_ocinitpara;
   timer_parameter_struct timer_initpara;

   rcu_periph_clock_enable(RCU_TIMER1);

   timer_deinit(TIMER1);
   /* initialize TIMER init parameter struct */
   timer_struct_para_init(&timer_initpara);
   /* TIMER1 configuration */
   timer_initpara.prescaler         = 107;
   timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
   timer_initpara.counterdirection  = TIMER_COUNTER_UP;
   timer_initpara.period            = 254;
   timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
   timer_initpara.repetitioncounter = 0;
   timer_init(TIMER1, &timer_initpara);

   /* initialize TIMER channel output parameter struct */
   timer_channel_output_struct_para_init(&timer_ocinitpara);
   /* CH0, CH1 and CH2 configuration in PWM mode */
   timer_ocinitpara.outputstate  = TIMER_CCX_ENABLE;
   timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;
   timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
   timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
   timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
   timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;

   timer_channel_output_config(TIMER1,TIMER_CH_1,&timer_ocinitpara);
   timer_channel_output_config(TIMER1,TIMER_CH_2,&timer_ocinitpara);
   timer_channel_output_config(TIMER1,TIMER_CH_3,&timer_ocinitpara);


   /* CH1 configuration in PWM mode0 */
   timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_1,255);
   timer_channel_output_mode_config(TIMER1,TIMER_CH_1,TIMER_OC_MODE_PWM0);
   timer_channel_output_shadow_config(TIMER1,TIMER_CH_1,TIMER_OC_SHADOW_DISABLE);

   /* CH2 configuration in PWM mode0 */
   timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_2,255);
   timer_channel_output_mode_config(TIMER1,TIMER_CH_2,TIMER_OC_MODE_PWM0);
   timer_channel_output_shadow_config(TIMER1,TIMER_CH_2,TIMER_OC_SHADOW_DISABLE);

   /* CH3 configuration in PWM mode0 */
   timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_3,255);
   timer_channel_output_mode_config(TIMER1,TIMER_CH_3,TIMER_OC_MODE_PWM0);
   timer_channel_output_shadow_config(TIMER1,TIMER_CH_3,TIMER_OC_SHADOW_DISABLE);

   /* auto-reload preload enable */
   timer_auto_reload_shadow_enable(TIMER1);
   /* auto-reload preload enable */
   timer_enable(TIMER1);
}

/**
    \brief      configure the rgb_led
    \param[in]  temp1:set value of red channel between 0 to 255
    \param[in]  temp2:set value of green channel between 0 to 255
    \param[in]  temp3:set value of blue channel between 0 to 255
    \param[out] none
    \retval     none
*/
void rgb_config(int temp1,int temp2,int temp3)
{
   /* CH1 duty cycle set */
   timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_1,(255-temp2));

   /* CH2 duty cycle set */
   timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_2,(255-temp1));

   /* CH3 duty cycle set */
   timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_3,(255-temp3));
}