lil refactor for clarity

This commit is contained in:
2026-04-13 00:24:56 +02:00
parent 4abb1df03c
commit 7f9dbfd683
5 changed files with 79 additions and 81 deletions

View File

@@ -7,6 +7,8 @@ This one is based on WCH CH32V003 and it's (should be) much better than the shit
The `i2c_driver.h` is copied wholesale from ch32fun i2c_slave examples folder. It literally just works.
RPM readings are getting unstable at ~310-320 RPM but that's probably okay.
## How compile?
There's a Makefile shithead
@@ -62,7 +64,7 @@ State equal to 0 means all is good.
| 1 (000000x0) | Fan stalled. **0** - fan operating normally. **1** - fan stalled. |
### Mode register
| Bit | Description |
|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 0 (0000000x) | Mode of operation: **0** - Constant duty cycle mode, the controller will keep the fan at a duty cycle set in register 0x06 and not change it automatically. **1** - Target RPM mode, the controller will target RPM set in register 0x04. **This is the initial mode.** |
| 1 (000000x0) | Fan power disable: **0** - Fan operates normally. **1** - 12V supply to the fan is cut. |
| Bit | Description |
|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 0 (0000000x) | Mode of operation: **0** - Constant duty cycle mode, the controller will keep the fan at a duty cycle set in register 0x06 and not change it automatically. **1** - Constant RPM mode, the controller continuously adjust PWM duty until target RPM is reached. **This is the initial mode.** |
| 1 (000000x0) | Fan power disable: **0** - Fan operates normally. **1** - 12V supply to the fan is cut. |

View File

@@ -17,16 +17,12 @@ uint16_t fan_driver_target_rpm = 0;
uint16_t fan_driver_current_rpm = 0;
bool fan_driver_is_stalled = false;
bool fan_driver_is_steady = false;
bool fan_driver_powered_down = false;
enum {
MODE_CONSTANT_DUTY = 0,
MODE_RPM_TARGET = 1
} fan_driver_control_mode;
volatile bool fan_driver_powered_down = false;
volatile bool fan_driver_constant_duty = false; // true = constant rpm mode
// callback for the pin interrupt
void TIM2_IRQHandler(void) __attribute__((interrupt));
void TIM2_IRQHandler(void) {
static uint16_t _fan_driver_last_capture = 0;
if (TIM2->INTFR & TIM_IT_CC2) {
@@ -38,14 +34,15 @@ void TIM2_IRQHandler(void) {
}
}
void fan_driver_init(void) {
static void fan_driver_init(void) {
const uint8_t pwm_pin = 2; // also need to change TIM1->CCER
const uint8_t tacho_pin = 3;
const uint8_t power_pin = 6; // also change in set_power func
// enable clock gating to relevant periphs
RCC->APB1PCENR |= RCC_APB1Periph_TIM2;
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_TIM1 | RCC_APB2Periph_AFIO; // enable clock gating for gpio and pwm timer
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_TIM1 | RCC_APB2Periph_AFIO;
// enable clock gating for gpio and pwm timer
// power pin init
GPIOC->CFGLR &= ~(0xf << (4 * power_pin));
@@ -57,35 +54,35 @@ void fan_driver_init(void) {
// we init pin register to 1111, then mask the features we need
GPIOD->CFGLR &= ~(0xf << (4 * pwm_pin));
GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * pwm_pin);
TIM1->PSC = 0x0000; // prescaler = 0
TIM1->ATRLR = pwm_timer_period - 1; // this value gets auto-reloaded on timer reset
TIM1->SWEVGR |= TIM_UG; // reload immediately
TIM1->CCER |= TIM_CC1E | TIM_CC1NP; // enable timer output t1ch1, positive polarity
TIM1->PSC = 0x0000; // prescaler = 0
TIM1->ATRLR = pwm_timer_period - 1; // this value gets auto-reloaded on timer reset
TIM1->SWEVGR |= TIM_UG; // reload immediately
TIM1->CCER |= TIM_CC1E | TIM_CC1NP; // enable timer output t1ch1, positive polarity
TIM1->CHCTLR1 |= TIM_OC1M_2 | TIM_OC1M_1; // set compare mode, set output?
TIM1->CH1CVR = TIM1->ATRLR; // initial duty at full speed
TIM1->BDTR |= TIM_MOE; // enable outputs
TIM1->CTLR1 |= TIM_CEN; // enable timer
TIM1->CH1CVR = TIM1->ATRLR; // initial duty at full speed
TIM1->BDTR |= TIM_MOE; // enable outputs
TIM1->CTLR1 |= TIM_CEN; // enable timer
// tacho stuff init
// set up GPIO (pd3, t2ch2)
GPIOD->CFGLR &= ~(0xf << (4 * tacho_pin));
GPIOD->CFGLR |= (GPIO_CNF_IN_PUPD) << (4 * tacho_pin); // pullup/down mode
GPIOD->OUTDR |= 1 << tacho_pin; // 1 - pullup, 0 - pulldown
GPIOD->OUTDR |= 1 << tacho_pin; // 1 - pullup, 0 - pulldown
// reset timer to reinint
RCC->APB1PRSTR |= RCC_APB1Periph_TIM2;
RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2;
// timer setup
TIM2->PSC = (FUNCONF_SYSTEM_CORE_CLOCK / 500000) - 1; // prescaler, 2us tick
TIM2->SWEVGR |= TIM_UG; // reload timer immediately
TIM2->CHCTLR1 &= ~(0xff << 8); // reset config bits
TIM2->PSC = (FUNCONF_SYSTEM_CORE_CLOCK / 500000) - 1; // prescaler, 2us tick
TIM2->SWEVGR |= TIM_UG; // reload timer immediately
TIM2->CHCTLR1 &= ~(0xff << 8); // reset config bits
TIM2->CHCTLR1 |= TIM_CC2S_0 | TIM_IC2F_2 | TIM_IC2F_3; // capture compare as input, set filter to 1100
TIM2->CCER |= TIM_CC2E; // enable capture on capture compare ch2 (cc2)
TIM2->DMAINTENR |= TIM_IT_CC2; // trigger interrupt on cc2
TIM2->CCER |= TIM_CC2E; // enable capture on capture compare ch2 (cc2)
TIM2->DMAINTENR |= TIM_IT_CC2; // trigger interrupt on cc2
// enable interrupt, enable timer
NVIC->IENR[((uint32_t)(TIM2_IRQn) >> 5)] = (1 << ((uint32_t)(TIM2_IRQn) & 0x1F)); // NVIC_EnableIRQ(TIM2_IRQn);
NVIC->IENR[((uint32_t) (TIM2_IRQn) >> 5)] = (1 << ((uint32_t) (TIM2_IRQn) & 0x1F)); // NVIC_EnableIRQ(TIM2_IRQn);
TIM2->CTLR1 |= TIM_CEN; // enable timer
}
@@ -105,36 +102,40 @@ static void fan_driver_set_power(const bool power) {
GPIOC->BSHR = (1 << (6 + 16 * !power));
}
void fan_driver_control_loop(void) {
static uint32_t last_delta_idx = 0xffffffff;
fan_driver_is_stalled = (_delta_idx == last_delta_idx);
if(fan_driver_is_stalled) {
fan_driver_current_rpm = 0;
return;
}
last_delta_idx = _delta_idx;
fan_driver_set_power(!fan_driver_powered_down);
if (fan_driver_powered_down)
return;
static void fan_driver_compute_rpm(void) {
uint32_t delta = 0;
for (int i = 0; i < rpm_buf_size; i++)
delta += _fan_driver_delta[i];
delta /= rpm_buf_size;
fan_driver_current_rpm = delta ? 30000000 / 2 / delta : 0;
// each tick is 2us. one minute is 30000000 ticks, each rotation is 2 pulses
if(fan_driver_control_mode == 1) {
const int32_t duty_trim = (fan_driver_target_rpm - fan_driver_current_rpm) / 4;
fan_driver_pwm_set_duty(fan_driver_pwm_get_duty() + duty_trim);
fan_driver_is_steady = (duty_trim == 0);
} else {
fan_driver_is_steady = true;
}
}
#endif //FAN_DRIVER_H
static void fan_driver_control_loop(void) {
// if we need to power down, disable power pin and return
fan_driver_set_power(!fan_driver_powered_down);
if (fan_driver_powered_down) return;
// see if fan stalled, set rpm to 0 and return if it is
static uint32_t last_delta_idx = 0xffffffff;
fan_driver_is_stalled = (_delta_idx == last_delta_idx);
last_delta_idx = _delta_idx;
if (fan_driver_is_stalled) {
fan_driver_current_rpm = 0;
return;
}
// compute rpms then return if we're in constant duty mode
fan_driver_compute_rpm();
if (fan_driver_constant_duty) {
fan_driver_is_steady = true;
return;
}
// compute rpm error, adjust duty so it's 0
const int32_t rpm_error = (fan_driver_target_rpm - fan_driver_current_rpm) / 4;
fan_driver_pwm_set_duty(fan_driver_pwm_get_duty() + rpm_error);
fan_driver_is_steady = (rpm_error == 0);
}
#endif //FAN_DRIVER_H

View File

@@ -1,7 +1,7 @@
#ifndef FUNCONFIG_H
#define FUNCONFIG_H
#define FUNCONF_USE_DEBUGPRINTF 0
#define FUNCONF_USE_UARTPRINTF 1
#define FUNCONF_USE_DEBUGPRINTF 1
#define FUNCONF_USE_UARTPRINTF 0
#endif //FUNCONFIG_H

View File

@@ -13,23 +13,23 @@
#define I2C_REG_STATE 0x0a
#define I2C_REG_MODE 0x0b
static void i2c_set_register_word(volatile uint8_t* reg, const uint8_t addr, const uint16_t value) {
static void i2c_set_register_word(volatile uint8_t *reg, const uint8_t addr, const uint16_t value) {
if (addr % 2) return;
reg[addr] = value >> 8;
reg[addr + 1] = value & 0xff;
}
static uint16_t i2c_get_register_word(volatile uint8_t* reg, const uint8_t addr) {
static uint16_t i2c_get_register_word(volatile uint8_t *reg, const uint8_t addr) {
if (addr % 2) return 0;
return (reg[addr] << 8) + reg[addr + 1];
}
static void i2c_registers_reinit(volatile uint8_t* reg) {
static void i2c_registers_reinit(volatile uint8_t *reg) {
i2c_set_register_word(reg, I2C_REG_WHOAMI, 26923); // magic value ;)
i2c_set_register_word(reg, I2C_REG_MAX_DUTY, fan_driver_pwm_get_max_duty());
}
static void i2c_registers_set_defaults(volatile uint8_t* reg) {
static void i2c_registers_set_defaults(volatile uint8_t *reg) {
i2c_set_register_word(reg, I2C_REG_TARGET_RPM, 750);
reg[I2C_REG_MODE] = 0x01; // target rpm mode, pwm control, power enabled
}

View File

@@ -5,10 +5,10 @@
volatile uint8_t i2c_registers[16] = {0x00};
void update_fan_from_regs(volatile uint8_t* reg) {
void update_fan_from_regs(volatile uint8_t *reg) {
fan_driver_target_rpm = i2c_get_register_word(reg, I2C_REG_TARGET_RPM);
fan_driver_pwm_set_duty(i2c_get_register_word(reg, I2C_REG_CURRENT_DUTY));
fan_driver_control_mode = (reg[I2C_REG_MODE] & 0x01);
fan_driver_constant_duty = !(reg[I2C_REG_MODE] & 0x01);
fan_driver_powered_down = (reg[I2C_REG_MODE] & 0x02) >> 1;
}
@@ -16,36 +16,32 @@ void onWrite(const uint8_t reg, const uint8_t length) {
if (length == 0) return;
switch (reg) {
case I2C_REG_TARGET_RPM:
update_fan_from_regs(i2c_registers);
printf("set tgt fan speed to %d\n", fan_driver_target_rpm);
break;
case I2C_REG_CURRENT_DUTY:
case I2C_REG_TARGET_RPM:
update_fan_from_regs(i2c_registers);
printf("set duty cycle to %d\n", fan_driver_pwm_get_duty());
break;
printf("set tgt fan speed to %d\n", fan_driver_target_rpm);
break;
case I2C_REG_MODE:
case I2C_REG_CURRENT_DUTY:
update_fan_from_regs(i2c_registers);
printf("set mode register to %x. control mode is %d, powered down is %d\n", i2c_registers[I2C_REG_MODE], fan_driver_control_mode, fan_driver_powered_down);
break;
printf("set duty cycle to %d\n", fan_driver_pwm_get_duty());
break;
default:
i2c_registers_reinit(i2c_registers);
break;
case I2C_REG_MODE:
update_fan_from_regs(i2c_registers);
printf("set mode register to %x. constant duty mode is %d, powered down is %d\n",
i2c_registers[I2C_REG_MODE], fan_driver_constant_duty, fan_driver_powered_down);
break;
default:
i2c_registers_reinit(i2c_registers);
break;
}
printf("wrote %d bytes to reg 0x%0x\n", length, reg);
}
int main() {
SystemInit();
printf("System bootup\n");
fan_driver_init();
// Initialize I2C slave
funGpioInitC();
funPinMode(PC1, GPIO_CFGLR_OUT_10Mhz_AF_OD); // SDA
@@ -53,8 +49,8 @@ int main() {
i2c_registers_reinit(i2c_registers);
i2c_registers_set_defaults(i2c_registers);
update_fan_from_regs(i2c_registers);
fan_driver_init();
fan_driver_target_rpm = i2c_get_register_word(i2c_registers, I2C_REG_TARGET_RPM);
SetupI2CSlave(I2C_DEVICE_ADDR, i2c_registers, sizeof(i2c_registers), onWrite, NULL, false);
while (1) {
@@ -66,5 +62,4 @@ int main() {
Delay_Ms(250);
}
}