Rename module TWI to I2C and add comments
This commit is contained in:
@@ -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
21
src/bus/i2c.h
Normal 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
|
||||
@@ -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
|
||||
72
src/main.c
72
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 <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)
|
||||
|
||||
Reference in New Issue
Block a user