STM32F103RCT6 — timer TIM2 output PWM waveform

1. Implementation method:

Generate a 40kHz PWM signal on STM32F103RCT6, you can choose to use TIM2 timer. First, you need to configure the clock source and prescaler coefficient of TIM2 to obtain the correct counting clock. Since a 40kHz PWM signal is to be output, the period of the counter can be set to 1800 (ie 72MHz/40kHz), so that the duration of each PWM cycle is 1/40kHz=25us.

Then, the duty cycle needs to be calculated. In this example, the duty cycle is 50%, so the CCR value can be set to half the period, which is 900. When 900 is reached before the counter overflows, the output is pulled high. When the counter is less than 900 again, the output will be pulled low.

Finally, you need to configure the PWM mode and output channel of TIM2 in order to generate the correct PWM signal. Here, we achieve this by setting the PWM mode as TIM_OCMode_PWM1 and the output channel as TIM_OCMode_PWM2.

2. Code implementation:

//Definition period
#define PWM_PERIOD 1800

// define duty cycle
#define DUTY_CYCLE (PWM_PERIOD / 2)

// Configure TIM2 as PWM output mode
void TIM2_PWM_Init(void)
{
    // define structure variables
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_OCInitTypeDef TIM_OCInitStruct;

    // Enable TIM2 clock
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    // Initialize TIM2
    TIM_TimeBaseInitStruct.TIM_Prescaler = 0; // no prescaler
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // Up counting mode
    TIM_TimeBaseInitStruct.TIM_Period = PWM_PERIOD - 1; // auto reload value
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // clock frequency division
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);

    // Configure TIM2 as PWM mode, output channel 2
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // PWM mode 1
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // enable output
    TIM_OCInitStruct.TIM_Pulse = DUTY_CYCLE; // duty cycle
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // output polarity is high
    TIM_OC2Init(TIM2, & TIM_OCInitStruct);

    // enable TIM2
    TIM_Cmd(TIM2, ENABLE);
}

It should be noted that when using TIM2 to generate PWM signals, it may be affected by other factors (such as power supply noise, interleaved IO interference, etc.), resulting in deviations in the duty cycle and frequency of the actual output signal. At this time, it is necessary to calibrate and optimize through actual testing and debugging to obtain a more accurate and stable PWM output signal.

3. Counting cycle calculation

Under the STM32F103RCT6 clock bit 72MHz, if you want to output a PWM signal with 40,000 pulses per second, then the time of each pulse is (1 / 40,000) s = 25us.

Therefore, if a timer module is used to generate this PWM signal, the counting period of the timer needs to be set to a count value of 25us per pulse. Calculated as follows:

Counting period = (timer clock frequency ÷ PWM frequency) – 1

Among them, the PWM frequency is 40kHz, and the timer clock frequency is 72MHz. Substitute into the formula to get:

Count period = (72,000,000 ÷ 40,000) – 1 = 1799

Because the timer starts counting from 0, the actual counting range of the counter should be 0~1799, a total of 1800 counting values. Therefore, setting the count period to 1800 generates a PWM signal with 40,000 pulses per second.

4. PWM wave usage

4.1 Control motor speed:

The PWM signal can be input into the motor driver, and the driver converts it into voltage or current to control the speed and power output of the motor.
The PWM signal can control the electrode by changing the duty cycle of the electrode output voltage. When controlling the electrode output, we usually define the duty cycle of the PWM signal as:

Duty Cycle = (High Time ÷ PWM Period) × 100%

Wherein, the PWM cycle is a complete cycle time length of the PWM signal, and the high level time indicates the duration of the PWM signal outputting the high level.

When the electrode is outputting voltage, its output voltage is proportional to the duration of the high level of the PWM signal (the longer the time, the greater the output voltage), so we can control the output voltage of the electrode by changing the duty cycle of the PWM signal.

4.1.1 Example:

If the PWM period is 10ms and the electrode needs to output 5V voltage, you can set the high-level time of the PWM signal to 5ms, and the low-level time to 5ms, and complete a PWM waveform output within a period of 10ms. Therefore, the duty cycle of this PWM signal is 50%.

In the specific implementation, we can use the timer function of the MCU to generate a PWM signal with a specified period and duty cycle to drive the electrode output. Taking the PWM signal generated by the timer function of STM32 as an example, the code is implemented as follows:

// Define the duty cycle as 50%
#define DUTY_CYCLE 500

// Configure TIM2 as PWM output mode
void TIM2_PWM_Init(void)
{
    // define structure variables
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_OCInitTypeDef TIM_OCInitStruct;

    // Enable TIM2 clock
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    // Initialize TIM2
    TIM_TimeBaseInitStruct.TIM_Prescaler = 0; // no prescaler
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // Up counting mode
    TIM_TimeBaseInitStruct.TIM_Period = 1800 - 1; // auto reload value
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // clock frequency division
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);

    // Configure TIM2 as PWM mode, output channel 2
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // PWM mode 1
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // enable output
    TIM_OCInitStruct.TIM_Pulse = DUTY_CYCLE; // duty cycle
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // output polarity is high
    TIM_OC2Init(TIM2, & TIM_OCInitStruct);

    // enable TIM2
    TIM_Cmd(TIM2, ENABLE);
}

In the above code, the counting period of the timer TIM2 is set to 1800 to generate a 40kHz PWM signal. Set the output channel of the PWM signal to channel 2 of TIM2, and set the duty cycle to 50% (DUTY_CYCLE = 500), then the electrode can be driven to output a voltage of 5V.

It should be noted that there may be certain errors in the actual output voltage and duty cycle, which can be calibrated and optimized through experiments and debugging. In addition, when controlling the electrode output, it is necessary to pay attention to the protection circuit and the stability of the control voltage to avoid damage to electronic equipment and users.

4.2 LED brightness adjustment:

By changing the brightness of the LED light, the luminous intensity of the LED can be changed to achieve the brightness adjustment effect. Use the PWM signal to control the on time and off time of the LED to change the brightness of the LED.
When the LED outputs current, its brightness is proportional to the current intensity, and the magnitude of the current is proportional to the duration of the high level of the PWM signal (the longer the time, the greater the output current), so we can change the duty cycle of the PWM signal To control the brightness and turn-on time of the LED.

4.2.1 Example:

If the PWM period is 10ms and the LED needs to output at 50% brightness, you can set the high-level time of the PWM signal to 5ms and the low-level time to 5ms, and complete a PWM waveform output within a period of 10ms. Therefore, the duty cycle of this PWM signal is 50%.

In the specific implementation, we can use the timer function of STM32 to generate a PWM signal with a specified period and duty cycle to drive the brightness output of the LED lamp. Take the use of TIM2 timer to generate 40kHz PWM signal as an example, the code implementation is as follows:

// Define the duty cycle as 50%
#define DUTY_CYCLE 500

// Configure TIM2 as PWM output mode
void TIM2_PWM_Init(void)
{
    // define structure variables
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_OCInitTypeDef TIM_OCInitStruct;

    // Enable TIM2 clock
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    // Initialize TIM2
    TIM_TimeBaseInitStruct.TIM_Prescaler = 0; // no prescaler
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // Up counting mode
    TIM_TimeBaseInitStruct.TIM_Period = 1800 - 1; // auto reload value
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // clock frequency division
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, & TIM_TimeBaseInitStruct);

    // Configure TIM2 as PWM mode, output channel 2
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // PWM mode 1
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // enable output
    TIM_OCInitStruct.TIM_Pulse = DUTY_CYCLE; // duty cycle
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // output polarity is high
    TIM_OC2Init(TIM2, & TIM_OCInitStruct);

    // enable TIM2
    TIM_Cmd(TIM2, ENABLE);
}

In the above code, the counting period of the timer TIM2 is set to 1800 to generate a 40kHz PWM signal. Set the output channel of the PWM signal to channel 2 of TIM2, and set the duty cycle to 50% (DUTY_CYCLE = 500), then the LED can be driven to output 50% brightness.

It should be noted that there may be certain errors in the actual output brightness and duty cycle, which can be calibrated and optimized through experiments and debugging.

4.3 Make a sound at a specific frequency:

The PWM signal can send a sound or vibration signal of a specific frequency through a device such as a speaker or a vibrator. This has applications in scenarios such as buzzers and mobile phone vibrations.

A PWM signal can produce a sound of a specific frequency by changing the frequency and duty cycle of the output waveform. When making sound, we usually define the frequency of the PWM signal as:

Frequency = PWM clock frequency ÷ (PWM period + 1)

Among them, the PWM clock frequency is the working clock frequency of the MCU timer, and the PWM cycle is a complete cycle time length of the PWM signal.

The PWM cycles corresponding to different frequencies are different, which also determines the high level and low level time in each cycle of the PWM signal. For example, if a sound of 1000Hz is required, the PWM period can be calculated according to the above formula as:

PWM period = PWM clock frequency ÷ frequency – 1 = 72MHz ÷ 1000Hz – 1 = 71999

4.3.1 Example:

Therefore, the timer function of STM32 can be used to generate a PWM signal of a specified frequency and duty cycle, and a sound of a specific frequency can be emitted. Taking the use of TIM2 timer to generate 1000Hz PWM signal as an example, the code implementation is as follows:

// Drive the buzzer to emit a 1000Hz sound
void Beep_Init(void)
{
    // define structure variables
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_OCInitTypeDef TIM_OCInitStruct;

    // Enable TIM2 clock
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    // Initialize TIM2 to generate 1000Hz PWM signal
    TIM_TimeBaseInitStruct.TIM_Period = 71999; // PWM period, corresponding to 1000Hz
    TIM_TimeBaseInitStruct.TIM_Prescaler = 0; // no prescaler
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // clock frequency division
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // Up counting mode
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, & TIM_TimeBaseInitStruct);

    // Configure TIM2 as PWM mode, output channel 2
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // PWM mode 1
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // enable output
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // output polarity is high
    TIM_OCInitStruct.TIM_Pulse = 36000; // duty cycle, corresponding to 50%
    TIM_OC2Init(TIM2, & TIM_OCInitStruct);

    // start TIM2
    TIM_Cmd(TIM2, ENABLE);
}

In the above code, the counting period of the timer TIM2 is set to 71999 to generate a 1000Hz PWM signal. Set the output channel of the PWM signal to channel 2 of TIM2, and set the duty cycle to 50% (corresponding to the equal high-level and low-level time of the PWM signal), then the buzzer can be driven to emit a sound at a specific frequency.

It should be noted that the corresponding PWM cycle and duty cycle need to be calculated according to the frequency of the sound to be emitted during specific implementation, and reasonably configured and adjusted. In addition, when making sound, it is necessary to protect the hearing health of the device and the user, and avoid hearing damage caused by excessive sound.

4.4 Voltage regulation:

The PWM signal can be input into the circuit, and the voltage can be adjusted by controlling the duty cycle of the output. For example, in a regulated power supply, a PWM signal can be used to control the output voltage value.
PWM can achieve voltage regulation by controlling the duty cycle of the output waveform. During PWM output, the output voltage is proportional to the length of the high level of the PWM signal (the longer the time, the greater the output voltage), so we can adjust the output voltage by changing the duty cycle of the PWM signal.

4.4.1 Example:

In the specific implementation, we can use the timer function of the MCU to generate a PWM signal with a specified period and duty cycle, and drive the output circuit to regulate the voltage. Taking the PWM signal generated by the timer function of STM32 as an example, the code is implemented as follows:

// Define the duty cycle as 50%
#define DUTY_CYCLE 500

// Configure TIM2 as PWM output mode
void TIM2_PWM_Init(void)
{
    // define structure variables
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_OCInitTypeDef TIM_OCInitStruct;

    // Enable TIM2 clock
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    // Initialize TIM2
    TIM_TimeBaseInitStruct.TIM_Prescaler = 0; // no prescaler
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // Up counting mode
    TIM_TimeBaseInitStruct.TIM_Period = 1800 - 1; // auto reload value
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // clock frequency division
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM2, & TIM_TimeBaseInitStruct);

    // Configure TIM2 as PWM mode, output channel 2
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // PWM mode 1
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // enable output
    TIM_OCInitStruct.TIM_Pulse = DUTY_CYCLE; // duty cycle
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // output polarity is high
    TIM_OC2Init(TIM2, & TIM_OCInitStruct);

    // enable TIM2
    TIM_Cmd(TIM2, ENABLE);
}

In the above code, the counting cycle of the timer TIM2 is set to 1800 to generate a 40kHz PWM signal. Set the output channel of the PWM signal to channel 2 of TIM2, and set the duty cycle to 50% (DUTY_CYCLE = 500), then the voltage can be adjusted by driving the output circuit.

It should be noted that there may be certain errors in the actual output voltage and duty cycle, which can be calibrated and optimized through experiments and debugging. In addition, when performing voltage regulation, factors such as load current and power supply stability need to be considered to avoid adverse effects on circuits and devices.