actual project
This commit is contained in:
137
src/fan_driver.h
Normal file
137
src/fan_driver.h
Normal file
@@ -0,0 +1,137 @@
|
||||
#ifndef FAN_DRIVER_H
|
||||
#define FAN_DRIVER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "ch32fun.h"
|
||||
|
||||
#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]);
|
||||
|
||||
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;
|
||||
|
||||
enum {
|
||||
FAN_TYPE_PWM = 0,
|
||||
FAN_TYPE_DC = 1
|
||||
} fan_driver_fan_type;
|
||||
|
||||
|
||||
// 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) {
|
||||
const uint16_t capture = TIM2->CH2CVR;
|
||||
_fan_driver_delta[_delta_idx % rpm_buf_size] = capture - _fan_driver_last_capture;
|
||||
_delta_idx++;
|
||||
_fan_driver_last_capture = capture;
|
||||
TIM2->INTFR &= ~TIM_IT_CC2; // clear bit
|
||||
}
|
||||
}
|
||||
|
||||
void fan_driver_init(void) {
|
||||
const uint8_t pwm_pin = 2; // also need to change TIM1->CCER
|
||||
const uint8_t tacho_pin = 3;
|
||||
|
||||
// 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
|
||||
|
||||
// pwm stuff init
|
||||
// port conf register is 32 bits. each pin is 4 bits
|
||||
// so for pd2 we skip pd0, pd1. 4 bits * 2 pins
|
||||
// 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->CHCTLR1 |= TIM_OC1M_2 | TIM_OC1M_1; // set compare mode, set output?
|
||||
TIM1->CH1CVR = 0; // initial duty at 0
|
||||
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
|
||||
|
||||
// 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->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
|
||||
|
||||
// enable interrupt, enable timer
|
||||
NVIC->IENR[((uint32_t)(TIM2_IRQn) >> 5)] = (1 << ((uint32_t)(TIM2_IRQn) & 0x1F)); // NVIC_EnableIRQ(TIM2_IRQn);
|
||||
TIM2->CTLR1 |= TIM_CEN; // enable timer
|
||||
}
|
||||
|
||||
static uint16_t fan_driver_pwm_get_max_duty(void) {
|
||||
return (pwm_timer_period - 1);
|
||||
}
|
||||
|
||||
static uint16_t fan_driver_pwm_get_duty(void) {
|
||||
return MIN(TIM1->CH1CVR, fan_driver_pwm_get_max_duty());
|
||||
}
|
||||
|
||||
static void fan_driver_pwm_set_duty(const uint16_t duty) {
|
||||
TIM1->CH1CVR = MIN(duty, fan_driver_pwm_get_max_duty());
|
||||
}
|
||||
|
||||
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_current_rpm = 0;
|
||||
fan_driver_is_stalled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
fan_driver_is_stalled = false;
|
||||
last_delta_idx = _delta_idx;
|
||||
|
||||
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;
|
||||
|
||||
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));
|
||||
fan_driver_is_steady = (duty_trim == 0);
|
||||
} else {
|
||||
fan_driver_is_steady = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //FAN_DRIVER_H
|
||||
7
src/funconfig.h
Normal file
7
src/funconfig.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef FUNCONFIG_H
|
||||
#define FUNCONFIG_H
|
||||
|
||||
#define FUNCONF_USE_DEBUGPRINTF 0
|
||||
#define FUNCONF_USE_UARTPRINTF 1
|
||||
|
||||
#endif //FUNCONFIG_H
|
||||
37
src/i2c.h
Normal file
37
src/i2c.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/* Bunch of i2c helper functions and defs mostly */
|
||||
|
||||
#ifndef I2C_H
|
||||
#define I2C_H
|
||||
|
||||
#define I2C_DEVICE_ADDR 0x2b
|
||||
|
||||
#define I2C_REG_WHOAMI 0x00
|
||||
#define I2C_REG_CURRENT_RPM 0x02
|
||||
#define I2C_REG_TARGET_RPM 0x04
|
||||
#define I2C_REG_CURRENT_DUTY 0x06
|
||||
#define I2C_REG_MAX_DUTY 0x08
|
||||
#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) {
|
||||
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) {
|
||||
if (addr % 2) return 0;
|
||||
return (reg[addr] << 8) + reg[addr + 1];
|
||||
}
|
||||
|
||||
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) {
|
||||
i2c_set_register_word(reg, I2C_REG_TARGET_RPM, 750);
|
||||
reg[I2C_REG_MODE] = 0x01; // target rpm mode, power enabled
|
||||
}
|
||||
|
||||
#endif //I2C_H
|
||||
221
src/i2c_driver.h
Normal file
221
src/i2c_driver.h
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Single-File-Header for using the I2C peripheral in slave mode
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Renze Nicolai
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __I2C_SLAVE_H
|
||||
#define __I2C_SLAVE_H
|
||||
|
||||
#include "ch32fun.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef void (*i2c_write_callback_t)(uint8_t reg, uint8_t length);
|
||||
typedef void (*i2c_read_callback_t)(uint8_t reg);
|
||||
|
||||
struct _i2c_slave_state {
|
||||
uint8_t first_write;
|
||||
uint8_t offset;
|
||||
uint8_t position;
|
||||
volatile uint8_t* volatile registers1;
|
||||
volatile uint8_t size1;
|
||||
volatile uint8_t* volatile registers2;
|
||||
volatile uint8_t size2;
|
||||
i2c_write_callback_t write_callback1;
|
||||
i2c_read_callback_t read_callback1;
|
||||
bool read_only1;
|
||||
i2c_write_callback_t write_callback2;
|
||||
i2c_read_callback_t read_callback2;
|
||||
bool read_only2;
|
||||
bool writing;
|
||||
bool address2matched;
|
||||
} i2c_slave_state;
|
||||
|
||||
void SetupI2CSlave(uint8_t address, volatile uint8_t* registers, uint8_t size, i2c_write_callback_t write_callback, i2c_read_callback_t read_callback, bool read_only) {
|
||||
i2c_slave_state.first_write = 1;
|
||||
i2c_slave_state.offset = 0;
|
||||
i2c_slave_state.position = 0;
|
||||
i2c_slave_state.registers1 = registers;
|
||||
i2c_slave_state.size1 = size;
|
||||
i2c_slave_state.registers2 = NULL;
|
||||
i2c_slave_state.size2 = 0;
|
||||
i2c_slave_state.write_callback1 = write_callback;
|
||||
i2c_slave_state.read_callback1 = read_callback;
|
||||
i2c_slave_state.read_only1 = read_only;
|
||||
i2c_slave_state.write_callback2 = NULL;
|
||||
i2c_slave_state.read_callback2 = NULL;
|
||||
i2c_slave_state.read_only2 = false;
|
||||
|
||||
// Enable I2C1
|
||||
RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
|
||||
|
||||
// Reset I2C1 to init all regs
|
||||
RCC->APB1PRSTR |= RCC_APB1Periph_I2C1;
|
||||
RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1;
|
||||
|
||||
I2C1->CTLR1 |= I2C_CTLR1_SWRST;
|
||||
I2C1->CTLR1 &= ~I2C_CTLR1_SWRST;
|
||||
|
||||
// Set module clock frequency
|
||||
uint32_t prerate = 2000000; // I2C Logic clock rate, must be higher than the bus clock rate
|
||||
I2C1->CTLR2 |= (FUNCONF_SYSTEM_CORE_CLOCK/prerate) & I2C_CTLR2_FREQ;
|
||||
|
||||
// Enable interrupts
|
||||
I2C1->CTLR2 |= I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN | I2C_CTLR2_ITERREN;
|
||||
|
||||
NVIC_EnableIRQ(I2C1_EV_IRQn); // Event interrupt
|
||||
NVIC_SetPriority(I2C1_EV_IRQn, 2 << 4);
|
||||
NVIC_EnableIRQ(I2C1_ER_IRQn); // Error interrupt
|
||||
NVIC_SetPriority(I2C1_ER_IRQn, 2 << 4);
|
||||
|
||||
// Set clock configuration
|
||||
uint32_t clockrate = 1000000; // I2C Bus clock rate, must be lower than the logic clock rate
|
||||
I2C1->CKCFGR = ((FUNCONF_SYSTEM_CORE_CLOCK/(3*clockrate))&I2C_CKCFGR_CCR) | I2C_CKCFGR_FS; // Fast mode 33% duty cycle
|
||||
//I2C1->CKCFGR = ((FUNCONF_SYSTEM_CORE_CLOCK/(25*clockrate))&I2C_CKCFGR_CCR) | I2C_CKCFGR_DUTY | I2C_CKCFGR_FS; // Fast mode 36% duty cycle
|
||||
//I2C1->CKCFGR = (FUNCONF_SYSTEM_CORE_CLOCK/(2*clockrate))&I2C_CKCFGR_CCR; // Standard mode good to 100kHz
|
||||
|
||||
// Set I2C address
|
||||
I2C1->OADDR1 = address << 1;
|
||||
I2C1->OADDR2 = 0;
|
||||
|
||||
// Enable I2C
|
||||
I2C1->CTLR1 |= I2C_CTLR1_PE;
|
||||
|
||||
// Acknowledge bytes when they are received
|
||||
I2C1->CTLR1 |= I2C_CTLR1_ACK;
|
||||
}
|
||||
|
||||
void SetupSecondaryI2CSlave(uint8_t address, volatile uint8_t* registers, uint8_t size, i2c_write_callback_t write_callback, i2c_read_callback_t read_callback, bool read_only) {
|
||||
if (address > 0) {
|
||||
I2C1->OADDR2 = (address << 1) | 1;
|
||||
i2c_slave_state.registers2 = registers;
|
||||
i2c_slave_state.size2 = size;
|
||||
i2c_slave_state.write_callback2 = write_callback;
|
||||
i2c_slave_state.read_callback2 = read_callback;
|
||||
i2c_slave_state.read_only2 = read_only;
|
||||
} else {
|
||||
I2C1->OADDR2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SetI2CSlaveReadOnly(bool read_only) {
|
||||
i2c_slave_state.read_only1 = read_only;
|
||||
}
|
||||
|
||||
void SetSecondaryI2CSlaveReadOnly(bool read_only) {
|
||||
i2c_slave_state.read_only2 = read_only;
|
||||
}
|
||||
|
||||
void I2C1_EV_IRQHandler(void) __attribute__((interrupt));
|
||||
void I2C1_EV_IRQHandler(void) {
|
||||
uint16_t STAR1, STAR2 __attribute__((unused));
|
||||
STAR1 = I2C1->STAR1;
|
||||
STAR2 = I2C1->STAR2;
|
||||
|
||||
if (STAR1 & I2C_STAR1_ADDR) { // Start event
|
||||
i2c_slave_state.first_write = 1; // Next write will be the offset
|
||||
i2c_slave_state.position = i2c_slave_state.offset; // Reset position
|
||||
i2c_slave_state.address2matched = !!(STAR2 & I2C_STAR2_DUALF);
|
||||
}
|
||||
|
||||
if (STAR1 & I2C_STAR1_RXNE) { // Write event
|
||||
if (i2c_slave_state.first_write) { // First byte written, set the offset
|
||||
i2c_slave_state.offset = I2C1->DATAR;
|
||||
i2c_slave_state.position = i2c_slave_state.offset;
|
||||
i2c_slave_state.first_write = 0;
|
||||
i2c_slave_state.writing = false;
|
||||
} else { // Normal register write
|
||||
i2c_slave_state.writing = true;
|
||||
if (i2c_slave_state.address2matched) {
|
||||
if (i2c_slave_state.position < i2c_slave_state.size2 && !i2c_slave_state.read_only2) {
|
||||
i2c_slave_state.registers2[i2c_slave_state.position] = I2C1->DATAR;
|
||||
i2c_slave_state.position++;
|
||||
}
|
||||
} else {
|
||||
if (i2c_slave_state.position < i2c_slave_state.size1 && !i2c_slave_state.read_only1) {
|
||||
i2c_slave_state.registers1[i2c_slave_state.position] = I2C1->DATAR;
|
||||
i2c_slave_state.position++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (STAR1 & I2C_STAR1_TXE) { // Read event
|
||||
i2c_slave_state.writing = false;
|
||||
if (i2c_slave_state.address2matched) {
|
||||
if (i2c_slave_state.position < i2c_slave_state.size2) {
|
||||
I2C1->DATAR = i2c_slave_state.registers2[i2c_slave_state.position];
|
||||
if (i2c_slave_state.read_callback2 != NULL) {
|
||||
i2c_slave_state.read_callback2(i2c_slave_state.position);
|
||||
}
|
||||
i2c_slave_state.position++;
|
||||
} else {
|
||||
I2C1->DATAR = 0x00;
|
||||
}
|
||||
} else {
|
||||
if (i2c_slave_state.position < i2c_slave_state.size1) {
|
||||
I2C1->DATAR = i2c_slave_state.registers1[i2c_slave_state.position];
|
||||
if (i2c_slave_state.read_callback1 != NULL) {
|
||||
i2c_slave_state.read_callback1(i2c_slave_state.position);
|
||||
}
|
||||
i2c_slave_state.position++;
|
||||
} else {
|
||||
I2C1->DATAR = 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (STAR1 & I2C_STAR1_STOPF) { // Stop event
|
||||
I2C1->CTLR1 &= ~(I2C_CTLR1_STOP); // Clear stop
|
||||
if (i2c_slave_state.address2matched) {
|
||||
if (i2c_slave_state.write_callback2 != NULL) {
|
||||
i2c_slave_state.write_callback2(i2c_slave_state.offset, i2c_slave_state.position - i2c_slave_state.offset);
|
||||
}
|
||||
} else {
|
||||
if (i2c_slave_state.write_callback1 != NULL) {
|
||||
i2c_slave_state.write_callback1(i2c_slave_state.offset, i2c_slave_state.position - i2c_slave_state.offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I2C1_ER_IRQHandler(void) __attribute__((interrupt));
|
||||
void I2C1_ER_IRQHandler(void) {
|
||||
uint16_t STAR1 = I2C1->STAR1;
|
||||
|
||||
if (STAR1 & I2C_STAR1_BERR) { // Bus error
|
||||
I2C1->STAR1 &= ~(I2C_STAR1_BERR); // Clear error
|
||||
}
|
||||
|
||||
if (STAR1 & I2C_STAR1_ARLO) { // Arbitration lost error
|
||||
I2C1->STAR1 &= ~(I2C_STAR1_ARLO); // Clear error
|
||||
}
|
||||
|
||||
if (STAR1 & I2C_STAR1_AF) { // Acknowledge failure
|
||||
I2C1->STAR1 &= ~(I2C_STAR1_AF); // Clear error
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
64
src/main.c
Normal file
64
src/main.c
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "ch32fun.h"
|
||||
#include "i2c_driver.h"
|
||||
#include "fan_driver.h"
|
||||
#include "i2c.h"
|
||||
|
||||
volatile uint8_t i2c_registers[16] = {0x00};
|
||||
|
||||
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);
|
||||
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));
|
||||
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);
|
||||
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();
|
||||
fan_driver_pwm_set_duty(1000);
|
||||
|
||||
// Initialize I2C slave
|
||||
funGpioInitC();
|
||||
funPinMode(PC1, GPIO_CFGLR_OUT_10Mhz_AF_OD); // SDA
|
||||
funPinMode(PC2, GPIO_CFGLR_OUT_10Mhz_AF_OD); // SCL
|
||||
i2c_registers_reinit(i2c_registers);
|
||||
i2c_registers_set_defaults(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);
|
||||
|
||||
while (1) {
|
||||
fan_driver_control_loop();
|
||||
|
||||
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);
|
||||
|
||||
Delay_Ms(200);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user