Handle I2C multiplexer channel selection

This commit is contained in:
2024-09-02 03:07:44 +02:00
parent 19983fb613
commit 3f336798c3
7 changed files with 155 additions and 14 deletions

BIN
docs/PCA9546-02.pdf Executable file

Binary file not shown.

View File

@@ -1,12 +1,104 @@
#include "common.h"
#include "bus/twi.h" #include "bus/twi.h"
#include <avr/io.h> #include <avr/io.h>
#include <assert.h>
#define TW_START 0x08
#define TW_REP_START 0x10
#define TW_MT_SLA_ACK 0x18
#define TW_MT_SLA_NACK 0x20
#define TW_MT_DATA_ACK 0x28
#define TW_MR_SLA_ACK 0x40
#define TW_MR_SLA_NACK 0x48
#define TW_MR_DATA_ACK 0x50
int TWI_Init(void) int TWI_Init(void)
{ {
// Set SCL bit rate to 400kHz // Set SCL bit rate to 400kHz
TWSR = 0x00; // TWI status register TWSR = 0x00; // TWI status register
TWBR = 0x0C; // TWI bit rate register TWBR = 0x0C; // TWI bit rate register
return 0;
}
int TWI_Start(char addr, char mode)
{
unsigned int status;
assert(mode == 0 || mode == 1);
// Send start condition
TWCR = (1 << TWEN) // Enable TWI
| (1 << TWINT) // Clear interrupt flag
| (1 << TWSTA); // Send start condition
// Wait until start condition sent
while ((TWCR & (1 << TWINT)) == 0);
if ((TWSR & 0xf8) != TW_START)
return -1;
// Send slave address
addr = addr << 1; // Lowest bit for mode
TWDR = addr + mode; // Mode 0=R, 1=W
TWCR = (1 << TWEN) // Enable TWI
| (1 << TWINT); // Clear interrupt flag
// Wait until slave address sent
while ((TWCR & (1 << TWINT)) == 0);
status = TWSR & 0xF8;
if ((mode == 0 && status != TW_MT_SLA_ACK) ||
(mode == 1 && status != TW_MR_SLA_ACK))
return -2;
return 0;
}
int TWI_Write(char data)
{
TWDR = data;
TWCR = (1 << TWEN) // Enable TWI
| (1 << TWINT); // Clear interrupt flag
while ((TWCR & (1 << TWINT)) == 0);
if ((TWSR & 0xF8) != TW_MT_DATA_ACK)
return -1;
return 0;
}
int TWI_Wait_ACK(void)
{
unsigned int status;
while ((TWCR & (1 << TWINT)) == 0);
status = TWSR & 0xF8;
if (status != TW_MT_DATA_ACK &&
status != TW_MR_DATA_ACK)
return -1;
return 0;
}
int TWI_Stop(void)
{
// Send stop condition
TWCR = (1 << TWEN) // Enable TWI
| (1 << TWINT) // Clear interrupt flag
| (1 << TWSTO); // Send stop condition
// Wait until stop condition sent
while (TWCR & (1 << TWSTO));
return 0; return 0;
} }

View File

@@ -2,5 +2,9 @@
#define MAD_CORE_BUS_TWI_H #define MAD_CORE_BUS_TWI_H
int TWI_Init(void); int TWI_Init(void);
int TWI_Start(char addr, char mode);
int TWI_Write(char data);
int TWI_Wait_ACK(void);
int TWI_Stop(void);
#endif // MAD_CORE_BUS_TWI_H #endif // MAD_CORE_BUS_TWI_H

View File

@@ -1,5 +1,6 @@
#include "common.h" #include "common.h"
#include "bus/usart.h" #include "bus/usart.h"
#include <avr/io.h> #include <avr/io.h>
#include <avr/interrupt.h> #include <avr/interrupt.h>
//#include <util/atomic.h> //#include <util/atomic.h>

View File

@@ -9,4 +9,7 @@
void Info(const char *fmt, ...); void Info(const char *fmt, ...);
void Error(const char *fmt, ...); void Error(const char *fmt, ...);
#include <util/delay.h>
#define Sleep(ms) _delay_ms(ms)
#endif // MAD_CORE_COMMON_H #endif // MAD_CORE_COMMON_H

View File

@@ -3,26 +3,67 @@
#include "bus/twi.h" #include "bus/twi.h"
#include <avr/interrupt.h> #include <avr/interrupt.h>
#include <util/delay.h>
#define Sleep(ms) _delay_ms(ms)
int main(int argc, char **argv) int main(void)
{ {
USART_Init(); char crb;
TWI_Init();
sei();
Info("Initializing...");
Sleep(500);
// ADS1115 (ADC) // ADS1115 (ADC)
// PCA9546 (Multiplexer TWI) // PCA9546 (Multiplexer TWI)
// ATH20 (3x) // ATH20 (3x)
// BMP280 (3x) // BMP280 (3x)
UNUSED(argc); USART_Init();
UNUSED(argv); TWI_Init();
sei();
Info("Initializing...");
// PCA9546 I2C Multiplexer
// =======================
TWI_Stop();
// Excerpts taken from the PCA9546A datasheet:
// https://www.ti.com/lit/ds/symlink/pca9546a.pdf
// Following a start condition, the bus master
// must output the address of the slave it is
// accessing.
// 7 6 5 4 | 3 2 1 0
// 1 1 1 0 | A2 A1 A0 RW
// FIXED_____ | SELECTABLE____
TWI_Start(0x70, 0);
TWI_Wait_ACK();
// Following the successful acknowledgment of
// the slave address, the bus master sends a byte
// which is stored in the control register.
// 7 6 5 4 | 3 2 1 0
// X X X X | B3 B2 B1 B0
// | CHANNEL_______
crb = 0x01; // Channel 0 | 00000001
// = 0x02 // Channel 1 | 00000010
// = 0x04 // Channel 2 | 00000100
// = 0x08 // Channel 3 | 00001000
TWI_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();
Info("Switched to TWI channel 0.");
Sleep(500);
return 0; return 0;
} }