From 1602aa4297523fd0f9249fb64766657f0e5f4109 Mon Sep 17 00:00:00 2001 From: Leon Krieg Date: Wed, 4 Sep 2024 21:38:12 +0200 Subject: [PATCH] Implement ADS1115 register reading and writing --- src/bus/i2c.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/bus/i2c.h | 2 + src/main.c | 17 +++-- 3 files changed, 183 insertions(+), 8 deletions(-) diff --git a/src/bus/i2c.c b/src/bus/i2c.c index 931981d..df2e696 100644 --- a/src/bus/i2c.c +++ b/src/bus/i2c.c @@ -20,6 +20,9 @@ static bool I2C_AHT20_IsCalibrated(void); static void I2C_AHT20_Calibrate(void); static void I2C_AHT20_Trigger(void); +static int I2C_ADS1115_WriteRegister(unsigned char reg, unsigned short data); +static unsigned short I2C_ADS1115_ReadRegister(unsigned char reg); + int I2C_Init(void) { // Set SCL bit rate to 400kHz @@ -168,18 +171,18 @@ int I2C_SetChannel(int channel) crb = (1 << channel); // crb = 0x01; // Channel 0 | 00000001 - // crb = 0x02 // Channel 1 | 00000010 - // crb = 0x04 // Channel 2 | 00000100 - // crb = 0x08 // Channel 3 | 00001000 + // 0x02 // Channel 1 | 00000010 + // 0x04 // Channel 2 | 00000100 + // 0x08 // Channel 3 | 00001000 I2C_Write(crb); + I2C_Wait_ACK(); // 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. - I2C_Wait_ACK(); I2C_Stop(); return 0; @@ -352,3 +355,164 @@ static void I2C_AHT20_Trigger(void) } while ((status & BIT(7)) == 1); I2C_Stop(); } + +static int I2C_ADS1115_WriteRegister(unsigned char reg, unsigned short data) +{ + // The ADS111x have one address pin, ADDR, that + // configures the I2C address of the device. This + // pin can be connected to GND, VDD, SDA, or SCL, + // allowing for four different addresses to be + // selected with one pin: + + // GND 1001000 + // VDD 1001001 + // SDA 1001010 + // SCL 1001011 + + // In slave receive mode, the first byte transmitted + // from the master to the slave consists of the 7-bit + // device address followed by a low R/W bit. + + I2C_Start(0x48, 0); // ADDR pin to GND + I2C_Wait_ACK(); + + // The next byte transmitted by the master is the + // Address Pointer register. The ADS111x then + // acknowledge receipt of the Address Pointer + // register byte. + + I2C_Write(reg); + I2C_Wait_ACK(); + + // The next two bytes are written to the address + // given by the register address pointer bits P[1:0]. + // The ADS111x acknowledge each byte sent. Register + // bytes are sent with the most significant byte + // first, followed by the least significant byte. + + I2C_Write(data >> 8); + I2C_Wait_ACK(); + I2C_Write(data & 0xFF); + I2C_Wait_ACK(); + + I2C_Stop(); + + return 0; +} + +static unsigned short I2C_ADS1115_ReadRegister(unsigned char reg) +{ + unsigned char data[2]; + + // To access a specific register from the ADS1115, + // the master must first write an appropriate value + // to register address pointer bits P[1:0] in the + // Address Pointer register. + + // The Address Pointer register is written to + // directly after the slave address byte, low R/W bit, + // and a successful slave acknowledgment. + + I2C_Start(0x48, 0); + I2C_Wait_ACK(); + I2C_Write(reg); + + // After the Address Pointer register + // is written, the slave acknowledges, and the master + // issues a STOP or a repeated START condition. + + I2C_Wait_ACK(); + I2C_Stop(); + I2C_Start(0x48, 1); + I2C_Wait_ACK(); + + // The ADS111x provide 16 bits of data in binary + // two's complement format. A positive full-scale (+FS) + // input produces an output code of 7FFFh and a negative + // full-scale (–FS) input produces an output code of + // 8000h. The output clips at these codes for signals + // that exceed full-scale. XXX: ??? + + data[0] = I2C_Read_ACK(); + data[1] = I2C_Read_NACK(); + + I2C_Stop(); + + return data[0] << 8 | data[1]; +} + +unsigned short I2C_ADS1115_Read(int channel) +{ + unsigned short config; + unsigned short rate, gain, mux, os; + + // Operational status or single-shot conversion + // start: This bit determines the operational + // status of the device. OS can only be written + // when in power-down state and has no effect + // when a conversion is ongoing. + + // When writing: + // 0 : No effect + // 1 : Start single conversion (power-down state) + // When reading: + // 0 : Device is performing a conversion + // 1 : Device is not performing a conversion + + os = 0x8000; // Start single | 10000000 00000000 + + // Data rate: These bits control the data rate setting. + + // 000 | 8 SPS + // 001 | 16 SPS + // 010 | 32 SPS + // 011 | 64 SPS + // 100 | 128 SPS + // 101 | 250 SPS + // 110 | 475 SPS + // 111 | 860 SPS + + rate = 0x0080; // 128 SPS | 00000000 10000000 + + // Programmable gain amplifier configuration: These + // bits set the FSR of the programmable gain amplifier. + + // 000 | ±6.144 V + // 001 | ±4.096 V + // 010 | ±2.048 V + // 011 | ±1.024 V + // 100 | ±0.512 V + // 101 | ±0.256 V + // 110 | ±0.256 V + // 111 | ±0.256 V + + gain = 0x0400; // 2.048 V | 00000100 00000000 + + // Input multiplexer configuration: These bits + // configure the input multiplexer. + + // 000 | AINP = AIN0, AINN = AIN1 + // 001 | AINP = AIN0, AINN = AIN3 + // 010 | AINP = AIN1, AINN = AIN3 + // 011 | AINP = AIN2, AINN = AIN3 + // 100 | AINP = AIN0, AINN = GND + // 101 | AINP = AIN1, AINN = GND + // 110 | AINP = AIN2, AINN = GND + // 111 | AINP = AIN3, AINN = GND + + mux = 0; + if (channel == 0) + mux = 0x4000; // | 01000000 00000000 + else if (channel == 1) + mux = 0x5000; // | 01010000 00000000 + else if (channel == 2) + mux = 0x6000; // | 01100000 00000000 + else if (channel == 3) + mux = 0x7000; // | 01110000 00000000 + + // Single-shot conversion, active low, continous mode + config = os | rate | gain | mux; + + I2C_ADS1115_WriteRegister(0x01, config); + return I2C_ADS1115_ReadRegister(0x00); +} diff --git a/src/bus/i2c.h b/src/bus/i2c.h index 3c658b9..138ece8 100644 --- a/src/bus/i2c.h +++ b/src/bus/i2c.h @@ -18,4 +18,6 @@ int I2C_Stop(void); int I2C_AHT20_Init(void); int I2C_AHT20_Read(float *temp, float *rhum); +unsigned short I2C_ADS1115_Read(int channel); + #endif // MAD_CORE_BUS_I2C_H diff --git a/src/main.c b/src/main.c index bfbe83d..c094ffb 100644 --- a/src/main.c +++ b/src/main.c @@ -6,7 +6,7 @@ #include -// TODO: Get readings from ADC ADS1115 over I2C. +// TODO: Convert raw data from ADS1115 to usable values. // 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). @@ -78,18 +78,27 @@ int Init(void) void Update(void) { float temp, rhum; + short raw[4]; I2C_SetChannel(AHT01); - if (I2C_AHT20_Read(&temp, &rhum)) + if (I2C_AHT20_Read(&temp, &rhum) == 0) Info("TEMP=%.2fC, RHUM=%.2f%", temp, rhum); I2C_SetChannel(AHT02); - if (I2C_AHT20_Read(&temp, &rhum)) + if (I2C_AHT20_Read(&temp, &rhum) == 0) Info("TEMP=%.2fC, RHUM=%.2f%", temp, rhum); I2C_SetChannel(AHT03); - if (I2C_AHT20_Read(&temp, &rhum)) + if (I2C_AHT20_Read(&temp, &rhum) == 0) Info("TEMP=%.2fC, RHUM=%.2f%", temp, rhum); + + raw[0] = I2C_ADS1115_Read(0); + raw[1] = I2C_ADS1115_Read(1); + raw[2] = I2C_ADS1115_Read(2); + raw[3] = I2C_ADS1115_Read(3); + + Info("ADC0=%04X, ADC1=%04X, ADC2=%04X, ADC3=%04X", + raw[0], raw[1], raw[2], raw[3]); } int main(void)