Unrolling Adafruit AS7341 readAllChannels()
After successfully reading an AS7341's product ID via a non-blocking I2C API, I have gained confidence I can replicate any existing AS7341 library's actions with the non-blocking I2C API. This way it will play nice with Mozzi library. I decided to focus on Adafruit_AS7341::readAllChanels()
because it seemed like the most useful one to convert to non-blocking operation. It was used in multiple examples as well as Emily's color organ project.
bool Adafruit_AS7341::readAllChannels(uint16_t *readings_buffer) {
setSMUXLowChannels(true); // Configure SMUX to read low channels
enableSpectralMeasurement(true); // Start integration
delayForData(0); // I'll wait for you for all time
Adafruit_BusIO_Register channel_data_reg =
Adafruit_BusIO_Register(i2c_dev, AS7341_CH0_DATA_L, 2);
bool low_success = channel_data_reg.read((uint8_t *)readings_buffer, 12);
setSMUXLowChannels(false); // Configure SMUX to read high channels
enableSpectralMeasurement(true); // Start integration
delayForData(0); // I'll wait for you for all time
return low_success &&
channel_data_reg.read((uint8_t *)&readings_buffer[6], 12);
}
Examining this code, we can see it does the same thing twice, differing only in a single boolean parameter to setSMUXLowChannels
. This reflects AS7341 architecture where we have more sensors than ADCs so we have to configure SMUX to read a subset then reconfigure SMUX to read remaining sensors.
void Adafruit_AS7341::setSMUXLowChannels(bool f1_f4) {
enableSpectralMeasurement(false);
setSMUXCommand(AS7341_SMUX_CMD_WRITE);
if (f1_f4) {
setup_F1F4_Clear_NIR();
} else {
setup_F5F8_Clear_NIR();
}
enableSMUX();
}
Digging into setSMUXLowChannels
we see the following actions:
-
enableSpectralMeasurement
disables a register bit to turn off spectral measurement, necessary preparation for SMUX reconfiguration. -
setSMUXCommand
flips a different register bit to notify AS7341 that a new SMUX configuration is about to be uploaded. - Upload one of two SMUX configurations, depending on boolean parameter.
-
enableSMUX
repeatedly reads a register bit until it flips to 0, which is how AS7341 signifies that SMUX reconfiguration is complete.
Steps 1-3 above are I2C writes and can be done quickly. Step 4 will add complication: not only is it an I2C read, but we might also need to read it several times before the bit flips to zero.
Backing out to readAllChannels
, we see spectral measurement bit is flipped back on after SMUX reconfiguration. According to the datasheet, flipping this bit back on is a signal to start a new reading. How do we know when sensor integration is complete? That's indicated by yet another register bit. delayForData
repeatedly reads that until it flips to 1, clearing the way for us to read 12 bytes of sensor data. Representing data from all six ADCs channels, each of which gives us 16 bits (2 bytes) of data.
Unrolling all of the above code in terms of I2C operations, readAllChannels
breaks down to:
- I2C register write to turn OFF spectral measurement.
- I2C register write to notify incoming SMUX configuration data.
- I2C write to upload SMUX configuration for ADC to read sensors F1 through F4, plus Clear and NIR channels.
- Repeated I2C read for "SMUX reconfiguration" flag until that bit flips to 0.
- I2C register write to turn ON spectral measurement. (Starts new sensor integration.)
- Repeated I2C read for "Sensor integration complete" flag until that bit flips to 1.
- I2C read to download sensor data
- Repeat 1-7, except step #3 uploads SMUX configuration for sensors F5 through F8 instead of F1 through F4.
I like this plan but before I roll up my sleeves, I wanted to take a closer look at SMUX configuration.
Code for this project is publicly available on GitHub.