lil refactor for clarity
This commit is contained in:
10
README.md
10
README.md
@@ -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. |
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
41
src/main.c
41
src/main.c
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user