From c4b6f8cdd9e67426a744641c5a7d18ace20f0eed Mon Sep 17 00:00:00 2001 From: Leon Krieg Date: Wed, 4 Sep 2024 02:47:45 +0200 Subject: [PATCH] Rename module TWI to I2C and add comments --- src/bus/{twi.c => i2c.c} | 152 +++++++++++++++++++-------------------- src/bus/i2c.h | 21 ++++++ src/bus/twi.h | 21 ------ src/main.c | 72 ++++++++++++++----- 4 files changed, 150 insertions(+), 116 deletions(-) rename src/bus/{twi.c => i2c.c} (75%) create mode 100644 src/bus/i2c.h delete mode 100644 src/bus/twi.h diff --git a/src/bus/twi.c b/src/bus/i2c.c similarity index 75% rename from src/bus/twi.c rename to src/bus/i2c.c index b4dafd0..988b2f3 100644 --- a/src/bus/twi.c +++ b/src/bus/i2c.c @@ -1,5 +1,5 @@ #include "common.h" -#include "bus/twi.h" +#include "bus/i2c.h" #include #include @@ -13,15 +13,15 @@ #define TW_MR_SLA_NACK 0x48 #define TW_MR_DATA_ACK 0x50 -// XXX: Handle interrupts in TWI_vect ISR? This may not +// XXX: Handle interrupts in I2C_vect ISR? This may not // actually be much better than the blocking approach -static void TWI_AHT20_Reset(void); -static bool TWI_AHT20_IsCalibrated(void); -static void TWI_AHT20_Calibrate(void); -static void TWI_AHT20_Trigger(void); +static void I2C_AHT20_Reset(void); +static bool I2C_AHT20_IsCalibrated(void); +static void I2C_AHT20_Calibrate(void); +static void I2C_AHT20_Trigger(void); -int TWI_Init(void) +int I2C_Init(void) { // Set SCL bit rate to 400kHz @@ -31,7 +31,7 @@ int TWI_Init(void) return 0; } -int TWI_Start(unsigned char addr, unsigned char mode) +int I2C_Start(unsigned char addr, unsigned char mode) { unsigned int status; @@ -68,7 +68,7 @@ int TWI_Start(unsigned char addr, unsigned char mode) return 0; } -int TWI_Write(unsigned char data) +int I2C_Write(unsigned char data) { TWDR = data; TWCR = (1 << TWEN) // Enable TWI @@ -82,7 +82,7 @@ int TWI_Write(unsigned char data) return 0; } -unsigned char TWI_Read_ACK(void) +unsigned char I2C_Read_ACK(void) { // Read data and acknowledge @@ -97,7 +97,7 @@ unsigned char TWI_Read_ACK(void) return TWDR; } -unsigned char TWI_Read_NACK(void) +unsigned char I2C_Read_NACK(void) { // Read data, expect last byte @@ -111,7 +111,7 @@ unsigned char TWI_Read_NACK(void) return TWDR; } -int TWI_Wait_ACK(void) +int I2C_Wait_ACK(void) { unsigned int status; @@ -125,7 +125,7 @@ int TWI_Wait_ACK(void) return 0; } -int TWI_Stop(void) +int I2C_Stop(void) { // Send stop condition @@ -140,7 +140,7 @@ int TWI_Stop(void) return 0; } -int TWI_SetChannel(int channel) +int I2C_SetChannel(int channel) { unsigned char crb; @@ -157,8 +157,8 @@ int TWI_SetChannel(int channel) // 1 1 1 0 | A2 A1 A0 RW // FIXED_____ | SELECTABLE____ - TWI_Start(0x70, 0); - TWI_Wait_ACK(); + I2C_Start(0x70, 0); + I2C_Wait_ACK(); // Following the successful acknowledgment of // the slave address, the bus master sends a byte @@ -175,22 +175,22 @@ int TWI_SetChannel(int channel) // crb = 0x04 // Channel 2 | 00000100 // crb = 0x08 // Channel 3 | 00001000 - TWI_Write(crb); + I2C_Write(crb); // When a channel is selected, the channel // becomes active after a stop condition has been // placed on the I2C bus. A stop condition always // must occur right after the acknowledge cycle. - TWI_Wait_ACK(); - TWI_Stop(); + I2C_Wait_ACK(); + I2C_Stop(); return 0; } -int TWI_AHT20_Init(void) +int I2C_AHT20_Init(void) { - TWI_AHT20_Reset(); + I2C_AHT20_Reset(); // Wait 40ms after power-on @@ -198,9 +198,9 @@ int TWI_AHT20_Init(void) Info("Initializing AHT20 sensor..."); - if (!TWI_AHT20_IsCalibrated()) { - TWI_AHT20_Calibrate(); - if (!TWI_AHT20_IsCalibrated()) { + if (!I2C_AHT20_IsCalibrated()) { + I2C_AHT20_Calibrate(); + if (!I2C_AHT20_IsCalibrated()) { Info("Error: Calibration failed."); return -1; } @@ -209,24 +209,24 @@ int TWI_AHT20_Init(void) return 0; } -int TWI_AHT20_Read(float *temp, float *rhum) +int I2C_AHT20_Read(float *temp, float *rhum) { unsigned char data[6], crc; unsigned long hraw, traw; - TWI_AHT20_Trigger(); + I2C_AHT20_Trigger(); - TWI_Start(0x38, 1); - TWI_Wait_ACK(); + I2C_Start(0x38, 1); + I2C_Wait_ACK(); - data[0] = TWI_Read_ACK(); - data[1] = TWI_Read_ACK(); - data[2] = TWI_Read_ACK(); - data[3] = TWI_Read_ACK(); - data[4] = TWI_Read_ACK(); - data[5] = TWI_Read_ACK(); - crc = TWI_Read_NACK(); - TWI_Stop(); + data[0] = I2C_Read_ACK(); + data[1] = I2C_Read_ACK(); + data[2] = I2C_Read_ACK(); + data[3] = I2C_Read_ACK(); + data[4] = I2C_Read_ACK(); + data[5] = I2C_Read_ACK(); + crc = I2C_Read_NACK(); + I2C_Stop(); // After receiving six bytes, the next byte is the // CRC check data, the user can read it as needed, @@ -253,7 +253,7 @@ int TWI_AHT20_Read(float *temp, float *rhum) return 0; } -static void TWI_AHT20_Reset(void) +static void I2C_AHT20_Reset(void) { // The command 0xBA is used to restart the sensor // system without turning the power off and on @@ -262,17 +262,17 @@ static void TWI_AHT20_Reset(void) // default setting state. The time required for // soft reset does not exceed 20 ms. - TWI_Start(0x38, 0); - TWI_Wait_ACK(); + I2C_Start(0x38, 0); + I2C_Wait_ACK(); - TWI_Write(0xBA); - TWI_Wait_ACK(); - TWI_Stop(); + I2C_Write(0xBA); + I2C_Wait_ACK(); + I2C_Stop(); Sleep(20); } -static bool TWI_AHT20_IsCalibrated(void) +static bool I2C_AHT20_IsCalibrated(void) { unsigned char status; @@ -281,18 +281,18 @@ static bool TWI_AHT20_IsCalibrated(void) // enable bit Bit [3] of the status word is 1 (you // can get a byte of status word by sending 0x71). - TWI_Start(0x38, 0); - TWI_Wait_ACK(); + I2C_Start(0x38, 0); + I2C_Wait_ACK(); - TWI_Write(0x71); - TWI_Wait_ACK(); - TWI_Stop(); + I2C_Write(0x71); + I2C_Wait_ACK(); + I2C_Stop(); - TWI_Start(0x38, 1); - TWI_Wait_ACK(); + I2C_Start(0x38, 1); + I2C_Wait_ACK(); - status = TWI_Read_NACK(); - TWI_Stop(); + status = I2C_Read_NACK(); + I2C_Stop(); // Note: The calibration status check only needs to // be checked at power-on. No operation is required @@ -301,26 +301,26 @@ static bool TWI_AHT20_IsCalibrated(void) return status & BIT(3); } -static void TWI_AHT20_Calibrate(void) +static void I2C_AHT20_Calibrate(void) { // To calibrate you need to send 0xBE command (for // initialization), this command parameter has two // bytes, the first byte is 0x08, the second byte // is 0x00, and then wait for 10ms. - TWI_Start(0x38, 0); - TWI_Wait_ACK(); + I2C_Start(0x38, 0); + I2C_Wait_ACK(); - TWI_Write(0xBE); - TWI_Wait_ACK(); - TWI_Write(0x08); - TWI_Wait_ACK(); - TWI_Write(0x00); - TWI_Wait_ACK(); - TWI_Stop(); + I2C_Write(0xBE); + I2C_Wait_ACK(); + I2C_Write(0x08); + I2C_Wait_ACK(); + I2C_Write(0x00); + I2C_Wait_ACK(); + I2C_Stop(); } -static void TWI_AHT20_Trigger(void) +static void I2C_AHT20_Trigger(void) { unsigned char status; @@ -329,16 +329,16 @@ static void TWI_AHT20_Trigger(void) // two bytes, the first byte is 0x33 and the second // byte is 0x00. - TWI_Start(0x38, 0); - TWI_Wait_ACK(); + I2C_Start(0x38, 0); + I2C_Wait_ACK(); - TWI_Write(0xAC); - TWI_Wait_ACK(); - TWI_Write(0x33); - TWI_Wait_ACK(); - TWI_Write(0x00); - TWI_Wait_ACK(); - TWI_Stop(); + I2C_Write(0xAC); + I2C_Wait_ACK(); + I2C_Write(0x33); + I2C_Wait_ACK(); + I2C_Write(0x00); + I2C_Wait_ACK(); + I2C_Stop(); // Wait for 80ms to wait for the measurement to be // completed. If the read status word Bit [7] is 0, @@ -346,12 +346,12 @@ static void TWI_AHT20_Trigger(void) // and six bytes can be read in a row. Otherwise // continue to wait. - TWI_Start(0x38, 1); - TWI_Wait_ACK(); + I2C_Start(0x38, 1); + I2C_Wait_ACK(); do { Sleep(80); - status = TWI_Read_ACK(); + status = I2C_Read_ACK(); } while ((status & BIT(7)) == 1); - TWI_Stop(); + I2C_Stop(); } diff --git a/src/bus/i2c.h b/src/bus/i2c.h new file mode 100644 index 0000000..3c658b9 --- /dev/null +++ b/src/bus/i2c.h @@ -0,0 +1,21 @@ +#ifndef MAD_CORE_BUS_I2C_H +#define MAD_CORE_BUS_I2C_H + +// Sensors +#define AHT01 0x0 // Upper Sensor TWI Channel +#define AHT02 0x1 // Middle Sensor TWI Channel +#define AHT03 0x2 // Lower Sensor TWI Channel + +int I2C_Init(void); +int I2C_Start(unsigned char addr, unsigned char mode); +int I2C_SetChannel(int channel); +int I2C_Write(unsigned char data); +unsigned char I2C_Read_ACK(void); +unsigned char I2C_Read_NACK(void); +int I2C_Wait_ACK(void); +int I2C_Stop(void); + +int I2C_AHT20_Init(void); +int I2C_AHT20_Read(float *temp, float *rhum); + +#endif // MAD_CORE_BUS_I2C_H diff --git a/src/bus/twi.h b/src/bus/twi.h deleted file mode 100644 index 59d53dc..0000000 --- a/src/bus/twi.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MAD_CORE_BUS_TWI_H -#define MAD_CORE_BUS_TWI_H - -// Sensors -#define AHT01 0x0 // Upper Sensor TWI Channel -#define AHT02 0x1 // Middle Sensor TWI Channel -#define AHT03 0x2 // Lower Sensor TWI Channel - -int TWI_Init(void); -int TWI_Start(unsigned char addr, unsigned char mode); -int TWI_SetChannel(int channel); -int TWI_Write(unsigned char data); -unsigned char TWI_Read_ACK(void); -unsigned char TWI_Read_NACK(void); -int TWI_Wait_ACK(void); -int TWI_Stop(void); - -int TWI_AHT20_Init(void); -int TWI_AHT20_Read(float *temp, float *rhum); - -#endif // MAD_CORE_BUS_TWI_H diff --git a/src/main.c b/src/main.c index 724fb34..874ac0d 100644 --- a/src/main.c +++ b/src/main.c @@ -2,12 +2,14 @@ #include "bus/usart.h" #include "bus/mosfet.h" #include "bus/pwm.h" -#include "bus/twi.h" +#include "bus/i2c.h" #include -// TODO: Get readings from ADC ADS1115 over TWI. +// TODO: Get readings from ADC ADS1115 over I2C. // TODO: Implement optional sensor value check with CRC8. +// TODO: Set timeouts for polling based things like I2C. +// TODO: Write an improved serial parser (low priority). // 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. @@ -15,30 +17,62 @@ int Init(void) { + // MOSFETS control things like the heating element + // so this is 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..."); - MOS_Init(); + // There is a possiblity to use interrupt signals + // for I2C communication but it requires to have + // one large routine for basically the whole I2C + // implementation. The blocking approach used right + // now is fine. + + I2C_Init(); PWM_Init(); - TWI_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 only has two outputs so it would + // require software PWM to have a variable frequency + // on pin 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); PWM_SetValue(FAN02, 50); PWM_SetValue(FAN03, 20); - TWI_SetChannel(AHT01); - TWI_AHT20_Init(); + // The I2C_SetChannel command changes the channel + // setting of the PCA9546 I2C multiplexer. Any + // AHT20 command after it will only be sent on + // the respective channel. - TWI_SetChannel(AHT02); - TWI_AHT20_Init(); + I2C_SetChannel(AHT01); + I2C_AHT20_Init(); - TWI_SetChannel(AHT03); - TWI_AHT20_Init(); + I2C_SetChannel(AHT02); + I2C_AHT20_Init(); + + I2C_SetChannel(AHT03); + I2C_AHT20_Init(); return 0; } @@ -47,17 +81,17 @@ void Update(void) { float temp, rhum; - TWI_SetChannel(AHT01); - TWI_AHT20_Read(&temp, &rhum); - Info("TEMP=%.2f, RHUM=%.2f", temp, rhum); + I2C_SetChannel(AHT01); + if (I2C_AHT20_Read(&temp, &rhum)) + Info("TEMP=%.2fC, RHUM=%.2f%", temp, rhum); - TWI_SetChannel(AHT02); - TWI_AHT20_Read(&temp, &rhum); - Info("TEMP=%.2f, RHUM=%.2f", temp, rhum); + I2C_SetChannel(AHT02); + if (I2C_AHT20_Read(&temp, &rhum)) + Info("TEMP=%.2fC, RHUM=%.2f%", temp, rhum); - TWI_SetChannel(AHT03); - TWI_AHT20_Read(&temp, &rhum); - Info("TEMP=%.2f, RHUM=%.2f", temp, rhum); + I2C_SetChannel(AHT03); + if (I2C_AHT20_Read(&temp, &rhum)) + Info("TEMP=%.2fC, RHUM=%.2f%", temp, rhum); } int main(void)