#include "common.h" #include "bus/usart.h" #include "bus/mosfet.h" #include "bus/pwm.h" #include "bus/i2c.h" #include // TODO: Implement optional CRC8 sensor measurement check. // TODO: Either implement software PWM for the FAN03 timer // (which will be quite complicated) or pick a chip with // more than two 16-bit PWM outputs like the ATmega328PB. // https://www.mikrocontroller.net/articles/Soft-PWM // TODO: Check if FAN02 is receiving the right frequency. // TODO: Write an improved command parser (low priority). // TODO: Proper error handling and recovery (after testing). static int Init(void) { // MOSFETS control things like the heating element // so they are the highest priority to initialize // to a default state via MOS_Init(). MOS_Init(); // The serial interface is required for output // functions like Info() and Error() and it uses // IRQs, so we need to initialize it as soon as // possible and make sure to enable interrupts. USART_Init(); sei(); Info("Initializing..."); // The watchdog timer is clocked from a separate // on-chip oscillator which runs at 1 MHz. Eight // different clock cycle periods can be selected // to determine the reset period. If the reset // period expires, the chip resets and executes // from the reset vector. // The update loop must call WDT_Reset to reset // the timer, kind of like a dead man's switch // allowing us to detect infinite loops and any // other error that halts execution. if (WDT_HasTriggered()) Info("Unexpected system reset."); WDT_Enable(); WDT_SetTimeoutFlag(WDT2000); // 2 seconds // There is a possiblity to use interrupt signals // for I2C communication but only as one large // branching routine for the whole I2C system. // The blocking approach used right now is fine. I2C_Init(); PWM_Init(); MOS_Enable(MOS03); // MOS_Disable(MOS01); // Only FAN01 and FAN02 are receiving the correct // frequency (25 KHz) right now. The 16-bit timer on // the ATMega32A has two outputs so it would require // software PWM to have a variable frequency on PD7. // A simple implementation will take up around 30-50 // percent of CPU time. Faster approaches are quite // complicated so it might be worth it to switch to // something like an ATmega328PB. PWM_SetValue(FAN01, 50); // Fan Peltier Hot side PWM_SetValue(FAN02, 50); // Fan Peltier Cold Side // PWM_SetValue(FAN03, 20); // Fan Heating // The I2C_SetChannel command changes the channel // setting of the PCA9546 I2C multiplexer. Any // command after it will be sent to the device // listening on that channel. I2C_SetChannel(AHT01); I2C_AHT20_Init(); I2C_SetChannel(AHT02); I2C_AHT20_Init(); I2C_SetChannel(AHT03); I2C_AHT20_Init(); return 0; } static void Update(void) { float temp[3], rh[3]; float adct[3]; Info("Reading sensor values..."); I2C_SetChannel(AHT01); I2C_AHT20_Read(&temp[0], &rh[0]); I2C_SetChannel(AHT02); I2C_AHT20_Read(&temp[1], &rh[1]); I2C_SetChannel(AHT03); I2C_AHT20_Read(&temp[2], &rh[2]); Sleep(200); // FIXME: Poll config register until BIT(15) == 1 adct[0] = I2C_ADS1115_ReadThermistor(ADS01); Sleep(200); // FIXME: Remove me - see above. adct[1] = I2C_ADS1115_ReadThermistor(ADS02); Sleep(200); // FIXME: Remove me - see above. adct[2] = I2C_ADS1115_ReadThermistor(ADS03); Info("TEMP0=%.2fC, TEMP1=%.2fC, TEMP2=%.2fC", temp[0], temp[1], temp[2]); Info("ADCT0=%.2fC, ADCT1=%.2fC, ADCT2=%.2fC", adct[0], adct[1], adct[2]); Info("RH0=%.2f%%, RH1=%.2f%%, RH2=%.2f%%", rh[0], rh[1], rh[2]); } int main(void) { Init(); for (;;) { WDT_Reset(); Update(); WDT_Reset(); Sleep(1000); } return 0; }