diff --git a/README.md b/README.md index 5f2e42a..05318a0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # I2C Fan Controller v2 -~because I found a cool new microcontroller~ +~ because I found a cool new microcontroller ~ This one is based on WCH CH32V003 and it's (should be) much better than the shitty ATTiny one. @@ -12,25 +12,40 @@ There's a Makefile shithead We compile on Debian/ubuntu. Other than the usual `build-essential` stuff you need also ```bash -sudo apt install gcc-riscv64-unknown-elf libnewlib-dev libudev-dev +sudo apt install gcc-riscv64-unknown-elf libnewlib-dev libudev-dev libusb-1.0-0-dev ``` +There is a distrobox.ini provided. You will also need to clone the [`ch32fun repo`](https://github.com/cnlohr/ch32fun) somewhere and modify the `Makefile`. - -You will also need to compile `minichlink`. Included in the `ch32fun` repo. +To flash you will also need to compile `minichlink`. Included in the `ch32fun` repo. ### ???.. Or just use PlatformIO idc. It has debugging. +## Pins + +For the TSSOP20 package, CH32V003FxPx + +| Pin # | Port | Function | +|-------|------|----------| +| 3 | PD5 | TX | +| 4 | PD7 | NRST | +| 11 | PC1 | SDA | +| 12 | PC2 | SCL | +| 16 | PC6 | POWER_EN | +| 18 | PD1 | SWIO | +| 19 | PD2 | PWM_OUT | +| 20 | PD3 | TACH_IN | + ## Register index -Registers are 8 bit each, but they hold 16-bit values. +Registers are 8 bit each, but most of them hold 16-bit values. Bytes in even registers are high, bytes in odd registers are low. | Register address | Function | R/W | |------------------|-----------------------------------------------------------|-----| -| 0x00, 0x01 | whoami | RO | +| 0x00, 0x01 | whoami (always returns 0x69 0x2b) | RO | | 0x02, 0x03 | Current fan speed (RPM) | RO | | 0x04, 0x05 | Target fan speed (RPM). Initial default speed is 750 RPM. | R/W | | 0x06, 0x07 | Current duty cycle | R/W | @@ -39,19 +54,15 @@ Bytes in even registers are high, bytes in odd registers are low. | 0x0b | Mode register | R/W | ### State register +State equal to 0 means all is good. + | Bit | Description | |--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| 0 (0000000x) | Steady state flag. **0** - fan didn't (yet) reach the target speed. **1** - fan reached the requested target speed or fan running in constant duty mode. | +| 0 (0000000x) | Steady state flag. **0** - fan reached the requested target speed or fan running in constant duty mode. **1** - fan didn't (yet) reach the target speed. | | 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 type: **0** - Fan controlled by PWM pin (4 pin plug), **this is the initial mode**. **1** - Fan controlled by DC voltage (3 pin plug) | -| 2 (00000x00) | Fan power disable: **0** - Fan operates normally. **1** - 12V supply to the fan is cut. | - -# TODO: - - - fan modes, states - - pwm/dc mode +| 1 (000000x0) | Fan power disable: **0** - Fan operates normally. **1** - 12V supply to the fan is cut. | \ No newline at end of file diff --git a/src/fan_driver.h b/src/fan_driver.h index b9d3906..ec5cbd7 100644 --- a/src/fan_driver.h +++ b/src/fan_driver.h @@ -6,12 +6,10 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) -#define CLAMP(min, max, val) MIN(MAX(min, val), max) volatile uint16_t _fan_driver_delta[12]; volatile uint32_t _delta_idx = 0; - static const uint16_t pwm_timer_period = 2048; static const uint8_t rpm_buf_size = sizeof(_fan_driver_delta) / sizeof(_fan_driver_delta[0]); @@ -26,11 +24,6 @@ enum { MODE_RPM_TARGET = 1 } fan_driver_control_mode; -enum { - FAN_TYPE_PWM = 0, - FAN_TYPE_DC = 1 -} fan_driver_fan_type; - // callback for the pin interrupt void TIM2_IRQHandler(void) __attribute__((interrupt)); @@ -48,10 +41,15 @@ void TIM2_IRQHandler(void) { 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_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)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * power_pin); // pwm stuff init // port conf register is 32 bits. each pin is 4 bits @@ -64,7 +62,7 @@ void fan_driver_init(void) { 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 = 0; // initial duty at 0 + TIM1->CH1CVR = TIM1->ATRLR; // initial duty at full speed TIM1->BDTR |= TIM_MOE; // enable outputs TIM1->CTLR1 |= TIM_CEN; // enable timer @@ -103,30 +101,35 @@ static void fan_driver_pwm_set_duty(const uint16_t duty) { TIM1->CH1CVR = MIN(duty, fan_driver_pwm_get_max_duty()); } +static void fan_driver_set_power(const bool power) { + GPIOC->BSHR = (1 << (6 + 16 * !power)); +} + void fan_driver_control_loop(void) { - // static uint16_t counter = 0; static uint32_t last_delta_idx = 0xffffffff; - if(_delta_idx == last_delta_idx) { + fan_driver_is_stalled = (_delta_idx == last_delta_idx); + + if(fan_driver_is_stalled) { fan_driver_current_rpm = 0; - fan_driver_is_stalled = true; return; } - - fan_driver_is_stalled = false; last_delta_idx = _delta_idx; + fan_driver_set_power(!fan_driver_powered_down); + if (fan_driver_powered_down) + return; + uint32_t delta = 0; for (int i = 0; i < rpm_buf_size; i++) delta += _fan_driver_delta[i]; delta /= rpm_buf_size; - - // each tick is 2us. one minute is 30000000 ticks, each rotation is 2 pulses 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 == MODE_RPM_TARGET) { - const int32_t duty_trim = (fan_driver_current_rpm - fan_driver_target_rpm) / 3; - fan_driver_pwm_set_duty(fan_driver_pwm_get_duty() + CLAMP(-100, 100, duty_trim)); + 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; diff --git a/src/i2c.h b/src/i2c.h index 6e05651..9948503 100644 --- a/src/i2c.h +++ b/src/i2c.h @@ -31,7 +31,7 @@ static void i2c_registers_reinit(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, power enabled + reg[I2C_REG_MODE] = 0x01; // target rpm mode, pwm control, power enabled } #endif //I2C_H diff --git a/src/main.c b/src/main.c index 37066f1..26296e3 100644 --- a/src/main.c +++ b/src/main.c @@ -5,25 +5,31 @@ volatile uint8_t i2c_registers[16] = {0x00}; +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_powered_down = (reg[I2C_REG_MODE] & 0x02) >> 1; +} + void onWrite(const uint8_t reg, const uint8_t length) { if (length == 0) return; switch (reg) { case I2C_REG_TARGET_RPM: - fan_driver_target_rpm = i2c_get_register_word(i2c_registers, 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: - printf("set duty cycle to %d\n", i2c_get_register_word(i2c_registers, I2C_REG_CURRENT_DUTY)); + update_fan_from_regs(i2c_registers); + printf("set duty cycle to %d\n", fan_driver_pwm_get_duty()); break; case I2C_REG_MODE: - fan_driver_control_mode = (i2c_registers[I2C_REG_MODE] & 0x01); - fan_driver_fan_type = (i2c_registers[I2C_REG_MODE] & 0x02) >> 1; - fan_driver_powered_down = (i2c_registers[I2C_REG_MODE] & 0x04) >> 2; - printf("set mode register to %x. control mode is %d, type is %d, powered down is %d\n", i2c_registers[I2C_REG_MODE], fan_driver_control_mode, fan_driver_fan_type, fan_driver_powered_down); + 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; default: @@ -39,7 +45,6 @@ int main() { printf("System bootup\n"); fan_driver_init(); - fan_driver_pwm_set_duty(1000); // Initialize I2C slave funGpioInitC(); @@ -47,6 +52,7 @@ int main() { funPinMode(PC2, GPIO_CFGLR_OUT_10Mhz_AF_OD); // SCL i2c_registers_reinit(i2c_registers); i2c_registers_set_defaults(i2c_registers); + update_fan_from_regs(i2c_registers); 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); @@ -56,9 +62,9 @@ int main() { i2c_set_register_word(i2c_registers, I2C_REG_CURRENT_RPM, fan_driver_current_rpm); i2c_set_register_word(i2c_registers, I2C_REG_CURRENT_DUTY, fan_driver_pwm_get_duty()); - i2c_registers[I2C_REG_STATE] = (fan_driver_is_steady) | (fan_driver_is_stalled << 1); + i2c_registers[I2C_REG_STATE] = (!fan_driver_is_steady) | (fan_driver_is_stalled << 1); - Delay_Ms(200); + Delay_Ms(250); } }