---
# System prepended metadata

title: '[STM32 TIMER PWM & Input Capture] - Fan Speed Control and Monitoring'

---

## [STM32 TIMER PWM & Input Capture] - Fan Speed Control and Monitoring
**GitHub:** https://github.com/CarterWu-M/STM32_FAN_CTRL
**Board:** NUCLEO-F072RB
**IDE:** STM32CubeIDE
**FAN:** ADDA AB0505LB-GBB

![imgpsh_fullsize_anim](https://hackmd.io/_uploads/r1q7pPm1Jx.png)

---
### Introduction
---
This article introduces how to use Timer PWM to adjust the fan's speed and how to use Timer input capture to measure the fan's speed.

During the operation, a scope is used to observe the PWM duty cycle and the fan's speed feedback signal.

---
### Fan Wiring
---
![fan](https://hackmd.io/_uploads/SJtvCw7k1g.png)
**Black:** Ground
**Red:** 5V
**White:** Outputs a pulse signal based on fan rotation speed
**Blue:** Takes a PWM signal to adjust fan speed

---
### STM32CubeMX settings
---
![image](https://hackmd.io/_uploads/Skb_Uum1ye.png)
**PC13:** Configure GPIO EXTI for a button
**PA8:** TIM PWM to control the fan speed
**PA0:** TIM Input Capture to monitor the fan speed

![image](https://hackmd.io/_uploads/SyS0KdXJ1e.png)
Configure the APB1 Timer clocks to 48MHz

---
### Timer - PWM
---
![image](https://hackmd.io/_uploads/rybZt_Xyye.png)
Configure TIM1 CH1 for PWM generation
Configure TIM1 Presaler to 48-1
Configure TIM1 Auto-Reload Register(ARR) to 100-1

---
#### Formula
---
TIM CLOCK = $\dfrac{APB\ \ TIM\ \ CLOCK}{PRESCALAR}$
\
FREQUENCY = $\dfrac{TIM\ \ CLOCK}{ARR}$
\
DUTY % = $\dfrac{CCRx}{ARR}$ X 100%

**Example:**
TIM CLOCK = $\dfrac{APB\ \ TIM\ \ CLOCK = 48MHz}{PRESCALAR = 48}$ = 1MHz
\
FREQUENCY = $\dfrac{TIM\ \ CLOCK\ \ = 1MHz}{ARR = 100}$ = 100KHz
\
DUTY % = $\dfrac{CCRx=10}{ARR = 100}$ X 100% = 10%

```c=
/* USER CODE BEGIN 0 */
static uint8_t duty = 0;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_PIN_13 == GPIO_Pin) {
        if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOC, GPIO_Pin))
        {
            //When the user button is pressed, increase the duty cycle by 10%.
            //If the duty cycle exceeds 100, reset the duty cycle to 0.
            if (100 < (duty += 10)) 
            {
                duty = 0;
            }
            TIM1->CCR1 = duty;
        }
    }
}
/* USER CODE END 0 */

int main(void)
{
    /* USER CODE BEGIN 2 */
    TIM1->CCR1 = 100;//Set the duty cycle to 100%
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);//Start the PWM generation
    /* USER CODE END 2 */
    
    while (1)
    {
    }
}

```
---
#### Scope
---
![NewFile4](https://hackmd.io/_uploads/B10TX3Qykl.png)
![image](https://hackmd.io/_uploads/HysmDaNyJe.png)
* PWM Duty = 0%
* Freq = 0 Hz

![NewFile1](https://hackmd.io/_uploads/By32m271yx.png)
![image](https://hackmd.io/_uploads/Sk38vT41kg.png)
* PWM Duty = 20%
* Freq = 10k Hz

![NewFile2](https://hackmd.io/_uploads/ryUa7h7kyg.png)
![image](https://hackmd.io/_uploads/Sk2Fv64yyl.png)
* PWM Duty = 70%
* Freq = 10k Hz

![NewFile3](https://hackmd.io/_uploads/B15T7nQ1Jg.png)
![image](https://hackmd.io/_uploads/r1_sva4Jkx.png)
* PWM Duty = 100%
* Freq = 0 Hz

---
#### Youtube Vedio
---
{%youtube rUN6wgfiBok %}

---
### Timer - Input Capture
---
![image](https://hackmd.io/_uploads/SJIsd3Qkyg.png)
Configure TIM2 CH1 as Input Capture direct mode
Configure TIM2 Prescaler to 48-1
Configure TIM2 Auto-Reload Register(ARR) to 0xffffffff-1

![image](https://hackmd.io/_uploads/r1TvlaXkkg.png)
Remember to configure the GPIO as pull-up because the fan speed signal is open-drain. If you don't configure this, the signal will appear as shown in the picture below.
![NewFile1](https://hackmd.io/_uploads/rJFfGa7Jyx.png)
You can see that the maximum voltage is 80mV, which is very samll


```c=
/* USER CODE BEGIN 0 */
#define REF_FREQ (48000000 / 48)
static bool isFirstCap = true;
static uint32_t ic1 = 0;
static uint32_t ic2 = 0;
static float icFreq = 0;

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (HAL_TIM_ACTIVE_CHANNEL_1 == htim->Channel)
    {
        if (true == isFirstCap)
        {
            ic1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
            isFirstCap = false;
        }
        else
        {
            uint32_t diff;
            ic2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
            if (ic2 >= ic1)
            {
                diff = ic2 - ic1;
            }
            else
            {
                diff = (0xffffffff - ic1) + ic2;
            }
            
            ic1 = ic2;
            icFreq = REF_FREQ / diff;
        }
    }
}
/* USER CODE END 0 */

int main(void)
{
    /* USER CODE BEGIN 2 */
     HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
    /* USER CODE END 2 */
    
    while (1)
    {
    }
}
```
---
#### Scope
---
![NewFile1](https://hackmd.io/_uploads/HyrxH1E11e.png)
When the PWM output duty cycle is 0%, the measured fan speed frequency is:
- Scope: 0 Hz (no rising or falling signal)
- Input Capture: 0 Hz (no rising or falling signal)

![NewFile1](https://hackmd.io/_uploads/H1_dTnEy1x.png)
![image](https://hackmd.io/_uploads/Sk66ph4ykg.png)
When the PWM output duty cycle is 10%, the measured fan speed frequency is:
- scope: 12 Hz
- Input Capture: 11 Hz

![NewFile2](https://hackmd.io/_uploads/BkMC0h4J1e.png)
![image](https://hackmd.io/_uploads/ByoZ0n4yJx.png)
When the PWM output duty cycle is 50%, the measured fan speed frequency is:
- scope: 85.5 Hz
- Input Capture: 87 Hz

![NewFile4](https://hackmd.io/_uploads/SJ3WHkV1kx.png)
![image](https://hackmd.io/_uploads/BkNFCnNkyg.png)
When the PWM output duty cycle is 100%, the measured fan speed frequency is:
- scope: 147 Hz
- Input Capture: 145 Hz

---
#### Youtube Vedio
---
{%youtube mxPrWlIAvi4 %}

---
### References
---
- https://controllerstech.com/pwm-in-stm32/
- https://controllerstech.com/input-capture-in-stm32/