daisy-chaining multiple MAX11040K's

Thread Starter

emailjakefrench@googlemai

Joined May 16, 2025
7
Hi,
I'm using the MAX11040K to sample 9 channels (all microphones) which give a signal between 0 to 3.3V DC-biased by 1.65V; that's input to all channels for all 9 mics. I need to sample these at 20kHz simultaneously but I'm having trouble with my hardware:
[datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max11040k-max11060.pdf]

I've daisy-chaining multiple devices as per the datasheet (figure 13 of the above link) and I can set the configuration register, though I can't get any sampled data out of the chain. Specifically, DRDYOUT never goes low, it's always high:

MAX11040K.png
I'm unsure if I've interpreted the daisy-chain circuit correctly - I would've thought sharing a CS would case SPI bus contention with chips fighting to respond. However, when I set the config register I'm able to read back the correct setting. I'm sending this:

/* Configure (configuration register)
/ command: write to configuration register => 0110 0000 = 0x60
// payload:
Bit Name Description Value Why
7 SHDN Shutdown 0 Normal operation
6 RST Reset 0 Not resetting now
5 EN24BIT 24-bit mode enable 1 Yes, use 24-bit
4 XTALEN Enable external crystal 0 External clock
3 FAULTDIS Disable FAULT pin 1 FAULT disabled
2 PDBUF Buffer active 1 Use buffer
1 Reserved Reserved (must be 0) 0 Always 0
0 Reserved Reserved (must be 0) 0 Always 0
*/
// Write to Configuration Register (0x60) → 0x2C = example config
// Correct order: first bytes go to Chip 1
uint8_t cfg_write[4] = {
0x60, 0x2C, 0x2C, 0x2C,
};
// Step 1: Write config
HAL_GPIO_WritePin(CS_MAX_GPIO_Port, CS_MAX_Pin, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi2, cfg_write, 4, HAL_MAX_DELAY);
HAL_GPIO_WritePin(CS_MAX_GPIO_Port, CS_MAX_Pin, GPIO_PIN_SET);

// Step 2. I'm reading back 0x2C from all three chips

// step 3. Reading data
Yet I'm not getting any sampled data, DRDYOUT remains high always.

Is this a hardware fault or a software issue?
 

nsaspook

Joined Aug 27, 2009
16,270
Hi,
I'm using the MAX11040K to sample 9 channels (all microphones) which give a signal between 0 to 3.3V DC-biased by 1.65V; that's input to all channels for all 9 mics. I need to sample these at 20kHz simultaneously but I'm having trouble with my hardware:
[datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max11040k-max11060.pdf]

I've daisy-chaining multiple devices as per the datasheet (figure 13 of the above link) and I can set the configuration register, though I can't get any sampled data out of the chain. Specifically, DRDYOUT never goes low, it's always high:

View attachment 349434
I'm unsure if I've interpreted the daisy-chain circuit correctly - I would've thought sharing a CS would case SPI bus contention with chips fighting to respond. However, when I set the config register I'm able to read back the correct setting. I'm sending this:

/* Configure (configuration register)
/ command: write to configuration register => 0110 0000 = 0x60
// payload:
Bit Name Description Value Why
7 SHDN Shutdown 0 Normal operation
6 RST Reset 0 Not resetting now
5 EN24BIT 24-bit mode enable 1 Yes, use 24-bit
4 XTALEN Enable external crystal 0 External clock
3 FAULTDIS Disable FAULT pin 1 FAULT disabled
2 PDBUF Buffer active 1 Use buffer
1 Reserved Reserved (must be 0) 0 Always 0
0 Reserved Reserved (must be 0) 0 Always 0
*/
// Write to Configuration Register (0x60) → 0x2C = example config
// Correct order: first bytes go to Chip 1
uint8_t cfg_write[4] = {
0x60, 0x2C, 0x2C, 0x2C,
};
// Step 1: Write config
HAL_GPIO_WritePin(CS_MAX_GPIO_Port, CS_MAX_Pin, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi2, cfg_write, 4, HAL_MAX_DELAY);
HAL_GPIO_WritePin(CS_MAX_GPIO_Port, CS_MAX_Pin, GPIO_PIN_SET);

// Step 2. I'm reading back 0x2C from all three chips

// step 3. Reading data
Yet I'm not getting any sampled data, DRDYOUT remains high always.

Is this a hardware fault or a software issue?
Does your software and hardware work with just one device in the chain?
Do you have a scope to look at the data on the wire?

The command byte sequence on the wire MUST be MSB with no padding in the C structure you write to SPI.
C does NOT guarantee the byte endian-ness, alignment, padding or size of structures by default because it might be inefficient (it disables some optimizations that rely on inserting padding to get ideal alignment) in access for general programming that doesn't require exact alignment for hardware.

I usually typedef a command/buffer structure, then create variables from that. NOTICE this on the typdef __attribute__((packed)) It is often used when a structure is used to map onto a register structure.
https://developer.arm.com/documenta...es/--attribute----packed---variable-attribute

C:
    typedef struct __attribute__((packed))
    {
        uint8_t cmd;
        uint16_t out;
    }
    mc33996buf_type;

#define mc33996_onoff        0b00000000
#define mc33996_olce        0b00000100
#define mc33996_gsrc        0b00001011
#define mc33996_sfpd        0b00001100
#define mc33996_pwm        0b00010000
#define mc33996_andor        0b00010100
#define mc33996_reset           0b00011000

mc33996buf_type mc_onoff = { // R/W data buffer to the device
    .cmd = mc33996_onoff,
    .out = 0x0000,
};

const mc33996buf_type mc_pwm = { // R only command to the device
    .cmd = mc33996_pwm,
    .out = 0x0000,
};


send_spi1_mc33996_dma((void*) &mc_pwm, 3); // NO PWM
send_spi1_mc33996_dma((void*) &mc_onoff, 3); // ALL I/O off

1747509774203.png

Here is the on wire command of just the mc33996_onoff command changing device output lines.
LOWER decode MOSI mc_onoff (command zero) with changing data to the device
UPPER decode MISO Chip response from the device.
 
Last edited:

Thread Starter

emailjakefrench@googlemai

Joined May 16, 2025
7
Hi, thanks for replying quickly.

Does your software and hardware work with just one device in the chain?
Can't check I'm afraid, it's interconnected - the PCB is as the above sch

Do you have a scope to look at the data on the wire?
Yes but messy due to the above reason - in hindsight I should've had the option to isolate the chips (perhaps by a header or something)

The command byte sequence on the wire MUST be MSB with no padding in the C structure you write to SPI.
C does NOT guarantee the byte endian-ness, alignment, padding or size of structures by default because it might be inefficient (it disables some optimizations that rely on inserting padding to get ideal alignment) in access for general programming that doesn't require exact alignment for hardware.

OK understood, I'm developing this on the STM32 blue pill using the HAL commands so this is taken of under the hood.

Is my schmatic correct? It would go a long way if I can establish if this is a hardware fault or something I'm doing / not doing in software
 

nsaspook

Joined Aug 27, 2009
16,270
...

The command byte sequence on the wire MUST be MSB with no padding in the C structure you write to SPI.
C does NOT guarantee the byte endian-ness, alignment, padding or size of structures by default because it might be inefficient (it disables some optimizations that rely on inserting padding to get ideal alignment) in access for general programming that doesn't require exact alignment for hardware.

OK understood, I'm developing this on the STM32 blue pill using the HAL commands so this is taken of under the hood.

...
NO, it's not taken care of under the hood because there is no hood.. The HAL_SPI_Transmit(&hspi2, cfg_write, 4, HAL_MAX_DELAY); just sends 4 bytes at the given address, period, stop. It has NO idea what's correct for your devices.
You MUST have your data aligned correctly before sending it to the device. HAL is not your savior from proper programming here. Why would you say such a thing?

Correct hardware won't matter until your software is fixed. The software is a show-stopper.


If you have a 32-bit machine and a device that needs 32-bit protocol structures, then you can bit map it into (packed) structures like this.

C:
/*
* TIC12400 command structure
*/
typedef struct __attribute__((packed))
{
    uint32_t par : 1;
    uint32_t data : 24;
    uint32_t addr : 6;
    uint32_t wr : 1;
}
ticbuf_type;

/*
* TIC12400 response structure
*/
typedef struct __attribute__((packed))
{
    uint32_t par : 1;
    uint32_t data : 24;
    uint32_t oi : 1;
    uint32_t temp : 1;
    uint32_t vs_th : 1;
    uint32_t ssc : 1;
    uint32_t parity_fail : 1;
    uint32_t spi_fail : 1;
    uint32_t por : 1;
}
ticread_type;


const ticbuf_type ticread05 = {
    .wr = 0,
    .addr = 0x05,
    .data = 0,
    .par = 1,
};

volatile uint32_t tic12400_status = 0, tic12400_counts = 0, tic12400_value_counts = 0;
volatile uint32_t tic12400_value = 0;
volatile bool tic12400_init_fail = false, tic12400_event = false; // chip error detection flag
volatile bool tic12400_parity_status = false;
volatile int32_t tic12400_fail_value = 0;

static ticread_type *ticstatus = (ticread_type*) & tic12400_status;
static ticread_type *ticvalue = (ticread_type*) & tic12400_value;

bool tic12400_init(void)
{
    tic12400_status = tic12400_wr(&ticstat02, 0); // get status to check for proper operation

    if ((ticstatus->data > por_bit) || !ticstatus->por) { // check for any high bits beyond POR bits set
        tic12400_init_fail = true;
        tic12400_fail_value = -1;
        goto fail;
    }

    tic12400_wr(&setup32, 0); //all set to compare mode, 0x32
    tic12400_wr(&setup21, 0); //Compare threshold all bits 2V, 0x21
    tic12400_wr(&setup1c, 0); //all set to GND switch type, 0x1c
    tic12400_wr(&setup1b, 0); //all channels are enabled, 0x1b
    tic12400_wr(&setup22, 0); //set switch interrupts, 0x22
    tic12400_wr(&setup23, 0); //set switch interrupts, 0x23
    tic12400_wr(&setup24, 0); // enable interrupts, 0x24
    tic12400_wr(&setup1d, 0); // set wetting currents, 0x1d
    tic12400_wr(&setup1a, 0); // set switch debounce to max 4 counts, 0x1a
    tic12400_status = tic12400_wr(&setup1a_trigger, 2); // trigger switch detections & CRC, 0x1a

    if (ticstatus->spi_fail) {
        tic12400_init_fail = true;
        tic12400_fail_value = -2;
        goto fail;
    }

    tic12400_status = tic12400_wr(&ticdevid01, 0); // get device id, 0x01

fail:
    return !tic12400_init_fail; // flip to return true if NO configuration failures
}
1747513417100.png
 
Last edited:

Thread Starter

emailjakefrench@googlemai

Joined May 16, 2025
7
ok, but the byte array is fixed:
*I'm sending {0x60, 0x2C, 0x2C, 0x2C,}
*I'm reading back: 0x2C, 0x2C, 0x2C

I agree clarity and correctness at the bit level is crucial, but the chip has clear fixed length fields and an aligned byte-protocol.
Taking a step back for a moment, I'm not familiar with this chip - do you agree with the hardware as shown?
 

nsaspook

Joined Aug 27, 2009
16,270
ok, but the byte array is fixed:
*I'm sending {0x60, 0x2C, 0x2C, 0x2C,}
*I'm reading back: 0x2C, 0x2C, 0x2C

I agree clarity and correctness at the bit level is crucial, but the chip has clear fixed length fields and an aligned byte-protocol.
Taking a step back for a moment, I'm not familiar with this chip - do you agree with the hardware as shown?
Like I said, you need to check at the wire level to be sure. HAL (or whatever framework you're using) is not where it happens, it's your specific compiler for X target that decides. I've been burned by alignments issues before. Most of the compilers for embedded chips do the right thing but it's a implementations detail that you can't depend on for correctness.

As for your board, you seem (too much work when I have other things to do) to have the connections correctly for at least the first device to work with good software but I've not used this specific 'Daisy Chain' configurations before as it's not a classic, SPI buffer circulates, configuration.
What is the source of the XIN clock for the first device in the chain? What does it look like on a scope when connected to the board?

The lack of test-points and debug points is very bad engineering form. An blatantly obvious thing to include with a new board. No excuse.
 
Last edited:
Top