Rename module TWI to I2C and add comments

This commit is contained in:
2024-09-04 02:47:45 +02:00
parent b9443f8cec
commit c4b6f8cdd9
4 changed files with 150 additions and 116 deletions

View File

@@ -1,5 +1,5 @@
#include "common.h"
#include "bus/twi.h"
#include "bus/i2c.h"
#include <avr/io.h>
#include <assert.h>
@@ -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();
}

21
src/bus/i2c.h Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -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 <avr/interrupt.h>
// 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)