Hi, I try to make very similar experiment as Bill with PWM controlled fan but I use Pico instead ESP32.
My fan is Delta AUB0912VH (tacho: 2 pulses per revolution, max 3800 RPM).
On fan datasheet I can read that optimal PWM control frequency is 25kHz so I set my Pico to generate such PWM frequecy and my (not verry expensive but working 😎) osciloscope confirm exactly this frequency.
Based of fan datasheet setting PWM duty to 0% should give minimal fan speed.
Pico show this is 870 RPM, my osciloscope shows tacho pulses frequency is 29Hz (with 2 pulses per revolition gives exactly 870 RPM). Datasheet says is should be about 500RPM so 870RMP is quie higher but acceptable in my opinion.
When PWM duty is 100% fan should spin on maximal speed.
Based on fan datasheet is should be about 3800 (with 10% tolerance) but my pico shows absurd value: about 22560 RPM. Osciloscope shows frequency 114Hz (that give about 3420 RPM what is within tolerance).
What I'm dong wrong??
Here is the code that runs on the Pico:
#include <stdio.h>
#include "hardware/adc.h"
#include "hardware/pwm.h"
#include "pico/stdlib.h"
#define PWM_PIN 2
#define TACHO_PIN 16
volatile uint32_t pulses = 0;
uint32_t lastTachTime = 0;
long map(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
void tacho_irq(uint gpio, uint32_t events) {
pulses++;
}
int main() {
uint32_t wrap;
const uint32_t TACH_SAMPLE_TIME = 1000; // Sample period in milliseconds
uint32_t rpm = 0;
uint16_t result;
stdio_init_all();
gpio_init(PWM_PIN); // initialization and setting of pin mode
gpio_set_function(PWM_PIN, GPIO_FUNC_PWM);
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
gpio_put(PICO_DEFAULT_LED_PIN, 1);
adc_init(); // ADC initialization
adc_gpio_init(26); // assignment to ADC pin 26 (ADC0)
adc_select_input(0); // Select ADC input 0 (GPIO26)
const float conversion_factor = 3.3f / (1 << 12);
lastTachTime = to_ms_since_boot(get_absolute_time());
// interrupt initialization
gpio_set_irq_enabled_with_callback(TACHO_PIN, GPIO_IRQ_EDGE_FALL, true, tacho_irq);
uint8_t slice_num = pwm_gpio_to_slice_num(PWM_PIN); // assignment of values to variables slice_num and channel
uint8_t channel = pwm_gpio_to_channel(PWM_PIN);
wrap = 4999; //PWM 25kHz
pwm_set_wrap(slice_num, wrap);
pwm_set_chan_level(slice_num, chan, 0); // 0% duty
pwm_set_enabled(slice_num, true); // PWM signal activation
while (true) {
result = adc_read();
uint16_t duty = map(result, 0, 4095, 0, 100);
uint16_t level = wrap * duty / 100;
pwm_set_chan_level(slice_num, channel, wrap * duty / 100);
uint32_t currentTime = to_ms_since_boot(get_absolute_time());
if (currentTime - lastTachTime >= TACH_SAMPLE_TIME) {
gpio_set_irq_enabled(TACHO_PIN, GPIO_IRQ_EDGE_FALL, false);
rpm = (pulses * 60000) / (TACH_SAMPLE_TIME * 2);
printf("Raw value: %d, voltage: %4.2f V duty: %d%%, level: %d RPM: %d\n", result, result * conversion_factor, duty, level, rpm);
// Reset counters
pulses = 0;
lastTachTime = currentTime;
gpio_set_irq_enabled(TACHO_PIN, GPIO_IRQ_EDGE_FALL, true);
}
sleep_ms(50);
}
}