Post Go back to editing

AN9541-BATImpedance integration with other controllers (no ADICUP3029 CPU)

Thread Summary

The user encountered issues integrating AD5940/AD5941 BAT Impedance application code with a Linux-based system, including missing jumpers (JP7, JP8, JP9) and incomplete SDK documentation. The final solution involved installing JP7 and JP9, rewriting SPI drivers to handle 16-bit and 32-bit register access correctly, and ensuring proper sequencer and interrupt configurations. The AD5940/AD5941 datasheet and SDK were criticized for lacking clarity and completeness, particularly regarding register definitions and silicon revisions.
AI Generated Content
Category: Software
Product Number: EVAL-AD5941BATZ, AD5941, AD5941, AD5941
Software Version: 1

Hi Akila, and others in you team.
I am trying to integrate the BAT Impedance application code examples with my Linux based system (no ADICUP3029 CPU)
I

Edit Notes

After further investigation progress has been made see latter posts
[edited by: CAMTEK at 11:03 AM (GMT -5) on 28 Nov 2025]
Parents
  • Thanks Ivan,

    I have now been through the ad5940 datasheet in detail to extract as much information as possible. I repeat this is a poor example of a technical datasheet and has many short comings in particular the register definitions, addresses and their data widths. 

    1. I confirm that the ADI SDK's general rule below is correct because all analog front-end registers (ADC, DAC, TIA, etc.) reside in that 0x1000–0x3014 range and are 32-bit wide, the others are 16 bit. There are no real registers above 0x3014.
    YOUR CODE USES:
    if ((addr >= 0x1000) && (addr <= 0x3014))
    access_type = 32bit;
    else
    access_type = 16bit;

    This range is too broad — the Rev.D datasheet shows that the 32-bit registers exist in discontinuous regions:
    0x1000–0x1020 (High-level sequencer control)
    0x2000–0x3014 (AFE control blocks, sequencer SRAM, FIFO)

    Therefore I suggest to use:
    bool is_32bit_reg(uint16_t addr)
    {
    return ((addr >= 0x1000 && addr <= 0x1020) ||
    (addr >= 0x2000 && addr <= 0x3014));
    }


    2. 

    Range Meaning Width Valid for register access?
    0x0000–0x0FFF Control & Digital 16-bit
    0x1000–0x3014 Analog Front End (AFE) 32-bit
    0x4000–0x43FF Sequencer/FIFO memory 16-bit word memory Access only via memory commands

    • 0x3014 is indeed the last documented register (REG_AFE_ADCDAT).

    • Everything above that — e.g., 0x4000–0x43FF — is not normal register space, but maybe sequencer/FIFO memory accessed indirectly or through special SPI commands? -- If so can you explain how these work?

    3. The ADI SDK's are mixed and confusing in many parts, they don't properly segregate hardware specific control functions such as SPI and GPIO transactions. This leads to confusing ad5940.c and ad5940.h files containing a mix of hardware functions that could have been easily separated for the SDK's, making specific APP's difficult to work with, e.g. BATImpedence etc.

    4. I have now made separate files for ad5940.c, ad5940.h to work with  specific (hardware dependent) ad5940_port.c  and ad5940_port.h separating the much needed clarifications. 

    5.  I have re-written all the SPI functions and modified the necessary ad5940.c files as needed. When running the BATImpedance SDK the data being spurted out is incorrect .. eg when MRCAL is run I an getting inconsistant data like below.

    === Performing MRCAL  ===
    i: 1 Freq: 1.00 RcalVolt:(8.000000,-5.000000)
    i: 2 Freq: 1.25 RcalVolt:(5.000000,0.000000)
    i: 3 Freq: 1.56 RcalVolt:(4.000000,-3.000000)
    i: 4 Freq: 1.94 RcalVolt:(3.000000,-1.000000)
    i: 5 Freq: 2.42 RcalVolt:(1.000000,1.000000)
    i: 6 Freq: 3.02 RcalVolt:(1.000000,3.000000)
    i: 7 Freq: 3.76 RcalVolt:(-1.000000,2.000000)
    i: 8 Freq: 4.69 RcalVolt:(2.000000,-2.000000)
    i: 9 Freq: 5.85 RcalVolt:(-1.000000,-2.000000)
    i: 10 Freq: 7.30 RcalVolt:(1.000000,4.000000)
    i: 11 Freq: 9.10 RcalVolt:(0.000000,0.000000)
    i: 12 Freq: 11.35 RcalVolt:(-2.000000,-3.000000)
    i: 13 Freq: 14.15 RcalVolt:(-1.000000,1.000000)
    i: 14 Freq: 17.65 RcalVolt:(0.000000,0.000000)
    i: 15 Freq: 22.01 RcalVolt:(-1.000000,1.000000)
    i: 16 Freq: 27.44 RcalVolt:(1.000000,1.000000)

    As can be seen the data is all wrong, the measured data are effectively noise?
    This gets me back to my previous questions when I see the ADI code doing things like this?

        case BATCTRL_MRCAL:
        printf("=== Performing MRCAL 1 ===\n");
        if(AD5940_WakeUp(10) > 10)
            return AD5940ERR_WAKEUP;
        //Settle input RC filter.
        AD5940_WriteReg(REG_AFE_SWMUX, 0); //control ADG636 to measure rcal
        AD5940_WriteReg(REG_AFE_SYNCEXTDEVICE, 0x4);
        PreCharge(PRECHARGE_RCAL);
        PreCharge(PRECHARGE_AMP);
        AD5940_FIFOCtrlS(FIFOSRC_DFT, bFALSE);

    The BATImpedence code calls PRECHARGE functions even though the required hardware links for JP8 and JP9 are not installed, so how exactly is this supposed to work, it appears to be necessary, the code is implemented but links not installed on pcb nor any information on these matters provided by ADI.

    It seems that that the test excitation signal not being generated correctly in software, and or the DFT engine is not working due to my present coding issues with the sequencer setups, all these maybe SPI communication issues, I believe my coding meets the required SPI protocols outlined in the ad5940 datasheet.

     

Reply
  • Thanks Ivan,

    I have now been through the ad5940 datasheet in detail to extract as much information as possible. I repeat this is a poor example of a technical datasheet and has many short comings in particular the register definitions, addresses and their data widths. 

    1. I confirm that the ADI SDK's general rule below is correct because all analog front-end registers (ADC, DAC, TIA, etc.) reside in that 0x1000–0x3014 range and are 32-bit wide, the others are 16 bit. There are no real registers above 0x3014.
    YOUR CODE USES:
    if ((addr >= 0x1000) && (addr <= 0x3014))
    access_type = 32bit;
    else
    access_type = 16bit;

    This range is too broad — the Rev.D datasheet shows that the 32-bit registers exist in discontinuous regions:
    0x1000–0x1020 (High-level sequencer control)
    0x2000–0x3014 (AFE control blocks, sequencer SRAM, FIFO)

    Therefore I suggest to use:
    bool is_32bit_reg(uint16_t addr)
    {
    return ((addr >= 0x1000 && addr <= 0x1020) ||
    (addr >= 0x2000 && addr <= 0x3014));
    }


    2. 

    Range Meaning Width Valid for register access?
    0x0000–0x0FFF Control & Digital 16-bit
    0x1000–0x3014 Analog Front End (AFE) 32-bit
    0x4000–0x43FF Sequencer/FIFO memory 16-bit word memory Access only via memory commands

    • 0x3014 is indeed the last documented register (REG_AFE_ADCDAT).

    • Everything above that — e.g., 0x4000–0x43FF — is not normal register space, but maybe sequencer/FIFO memory accessed indirectly or through special SPI commands? -- If so can you explain how these work?

    3. The ADI SDK's are mixed and confusing in many parts, they don't properly segregate hardware specific control functions such as SPI and GPIO transactions. This leads to confusing ad5940.c and ad5940.h files containing a mix of hardware functions that could have been easily separated for the SDK's, making specific APP's difficult to work with, e.g. BATImpedence etc.

    4. I have now made separate files for ad5940.c, ad5940.h to work with  specific (hardware dependent) ad5940_port.c  and ad5940_port.h separating the much needed clarifications. 

    5.  I have re-written all the SPI functions and modified the necessary ad5940.c files as needed. When running the BATImpedance SDK the data being spurted out is incorrect .. eg when MRCAL is run I an getting inconsistant data like below.

    === Performing MRCAL  ===
    i: 1 Freq: 1.00 RcalVolt:(8.000000,-5.000000)
    i: 2 Freq: 1.25 RcalVolt:(5.000000,0.000000)
    i: 3 Freq: 1.56 RcalVolt:(4.000000,-3.000000)
    i: 4 Freq: 1.94 RcalVolt:(3.000000,-1.000000)
    i: 5 Freq: 2.42 RcalVolt:(1.000000,1.000000)
    i: 6 Freq: 3.02 RcalVolt:(1.000000,3.000000)
    i: 7 Freq: 3.76 RcalVolt:(-1.000000,2.000000)
    i: 8 Freq: 4.69 RcalVolt:(2.000000,-2.000000)
    i: 9 Freq: 5.85 RcalVolt:(-1.000000,-2.000000)
    i: 10 Freq: 7.30 RcalVolt:(1.000000,4.000000)
    i: 11 Freq: 9.10 RcalVolt:(0.000000,0.000000)
    i: 12 Freq: 11.35 RcalVolt:(-2.000000,-3.000000)
    i: 13 Freq: 14.15 RcalVolt:(-1.000000,1.000000)
    i: 14 Freq: 17.65 RcalVolt:(0.000000,0.000000)
    i: 15 Freq: 22.01 RcalVolt:(-1.000000,1.000000)
    i: 16 Freq: 27.44 RcalVolt:(1.000000,1.000000)

    As can be seen the data is all wrong, the measured data are effectively noise?
    This gets me back to my previous questions when I see the ADI code doing things like this?

        case BATCTRL_MRCAL:
        printf("=== Performing MRCAL 1 ===\n");
        if(AD5940_WakeUp(10) > 10)
            return AD5940ERR_WAKEUP;
        //Settle input RC filter.
        AD5940_WriteReg(REG_AFE_SWMUX, 0); //control ADG636 to measure rcal
        AD5940_WriteReg(REG_AFE_SYNCEXTDEVICE, 0x4);
        PreCharge(PRECHARGE_RCAL);
        PreCharge(PRECHARGE_AMP);
        AD5940_FIFOCtrlS(FIFOSRC_DFT, bFALSE);

    The BATImpedence code calls PRECHARGE functions even though the required hardware links for JP8 and JP9 are not installed, so how exactly is this supposed to work, it appears to be necessary, the code is implemented but links not installed on pcb nor any information on these matters provided by ADI.

    It seems that that the test excitation signal not being generated correctly in software, and or the DFT engine is not working due to my present coding issues with the sequencer setups, all these maybe SPI communication issues, I believe my coding meets the required SPI protocols outlined in the ad5940 datasheet.

     

Children
  • No response --- looks like ADI are suggesting to give up. Or its too difficult to answer 

    After waiting so long for any detailed response from ADI, I will go one step further an say this at the risk of being killed off.

    ADI’s AD5940 SDK is so confusing, and full of:

    • redundant functions

    • half-implemented helpers

    • undocumented “magic” code 

    • broken SDK examples

    • hidden assumptions 

    • lack of decent explanations on functioning of the chip 

    • inconsistent register access patterns and very bad documentation

    • consists of old code copied from original AD5941 firmware that doesn’t match Rev-D silicon definitions and no documentation to say why

    • Hardware discrepancies in Battery Impedance Board. 

      The precharge() function uses GPO1 and GPO2, that map directly to the Arduino header through JP8 and JP9 which were NEVER INSTALLED. The RCAL measurements are therefore useless and cause garbage data. ADI fails to explain why these functions do not work.

    • And the coding errors go on and on .... 
       
    • I really wonder if any of the development boards have ever worked without using the ADI ADICUP3029


      Their SDK examples do things that do not make sense, and many of their functions are not required or even used and not even allowed in the REV D datasheet, like those mysterious _readwrite_16b and _readwrite_32b. functions which can be dangerous if used incorrectly.

      This AD5940/1 chip and its SDK examples/documentation has got to be the worst example of a decent and useful chip ruined by lack engineering professionalism, sadly from a such a reputable organization.

      So come-on people please respond and defend your SDK and code or just say you cannot help and tell me to shut-up and go away!

  • WHAT I HAVE LEARNED SO FAR - ATTEMPTING TO GET THIS DEVICE TO WORK

    1. The AD5940 SPI protocol is NOT “normal SPI” 
        The AD5940 does not use standard SPI protocol like this:
        CS low
        send command+address
        send/receive data
        CS high
        Instead, it has 3 different SPI modes, depending on what subsystem you access: which is not readily          documented anywhere.

    A normal SPI driver holds CS low for the entire transfer, which is perfectly fine for normal SPI devices…but the AD5940 is NOT a normal SPI device.

    2. The ad5940 (Rev D) datasheet is a pathetic example of a technical document. 
    It dismally fails to provide a full table of registers, register widths, register address ranges and proper explanations. 
    ADI SDK example code correctly identifies address ranges using this c code.

    if(((RegAddr>=0x1000)&&(RegAddr<=0x3014)))

    AD5940_ReadWrite32B(RegData);
    else
    AD5940_ReadWrite16B(RegData);

    3. Standard official SPI Register Read/Write Modes Work fine and I have successfully implemented all 16bit and 32 bit read/write functions and confirmed their operation, however the ADI ad5940 chip itself for some unknown reason implements non standard SPI modes for Sequencer and fifo access. I really cannot understand the reason behind this as the official standard SPI transaction modes can easily accommodate all operations without resorting to this. Maybe it was just an experimental silicon version left over without bothering to revise and produce a newer updated silicon version, and since then ADI have introduced a new silicon version that incorporates both the ADI CPU  ( ADuCM3029 ) ... in effect interconnecting and dumping two old silicon versions to produce a new device?   

    4. Sequencer Load Functions require completely different SPI patterns/modes (NOT NORMAL)

    5. ADI uses one continuous SPI transaction.(This is why ADI original code uses Bit-Bang SPI modes)

    6. All of the ADI SDK code functions rely on sequencer/fifo instructions and difficult to implement using an  O/S such as Linux because it doesn't support silly bit-banging modes.

    7. As a result we have:

    • FIFO never outputs valid DFT data

    • AppBATISR sees garbage (noise) 

    • RcalVolt is nonsense

    • EXC_P never enables correctly and does not output correct test signals

    8.  The ADI EVAL-AD5941BATZ BOARD as shipped does not contain jumpers or          headers for JP8 and JP9 but the SDK Example codes require these jumpers          otherwise calibration functions don't work. No notes or documentation about            these have been provided. Also placing these jumpers may effect other                    operations or EIS functions?

    9.  Currently stuck with getting the Sequencer and FIFO Read/Write functions to           work but there is difficulty in implementing the correct protocols without using           bit-banging SPI code to meet the stupid ad5940 protocol requirements  and             this is really where I would like ADI assistance. 

    10. I need to get rid of the ADI SDK code requirements that rely on their silly and antiquated use of the bit-bang coding used in AD5940_ReadWriteNBytes() this is a major problem with all ADI SDK example code. At the hardware/chip level, the AD5940 silicon itself, unfortunately has implemented/ruled the adaptation of the cumbersome/silly need of 3 different SPI protocol requirements (when only one is needed) which depend on bit-bang techniques.



  • SOME PROGRESS MADE

    1. Have now implemented updated Linux SPI Comms to work with external patch for CS gpio control with some hardware and software modifications so now I can emulate and test the ADI AD5940_ReadWriteNBytes()...WITH NO BIT-BANGING. 

    2. The emulated AD5940_ReadWriteNBytes() now works fine with all SPI functions working via calls to AD5940_ReadWriteNBytes(). However as yet,  no improvements with EIS outputs, just noise and failure to generate excitation signals at EXC_P and AIN1.

    3. Have now installed headers and jumpers for JP8 and JP9 ... different but no change!

    • With JP8/JP9 fitted (in the “A” position),  MCU D3/D4 lines are actually connected into the pre-charge control network (via resistors into the ADG1609/ADG636 and the PRE1/2/3 nets).

    • With JP8/JP9 open, D3/D4 from the MCU header are not connected; the precharge logic sees only its local pull-ups/pull-downs and basically ignores what BATImpedance code does.

      However, with JP8/JP9 installed → PreCharge() finally controls the pre-charge FETs/switches, and measured results should change for the better and be more stable, but alas no help from ADI who never bothered (except for Ivan) to answer basic questions reading the SDK's to save me a lot of time and effort?

    • On the CN0510 / EVAL-AD5941BATZ schematic: we see that GPIO2 of the AD5941 is the ONLY one that actually goes into the U2 analog switch / mux control If the link (JP9) is removed, that node is held high by the pull-up.
      In that state, the U2 mux is stuck in a fixed selection; your SDK can’t change it.

    But for some unknown reason the SDK Precharge code operates both JP8 (GPIO1) and JP9 and GPIO1 is only connected to a LED. Very confusing ADI CODE especially with no dam explanation?

    4. With JP9 installed the Impedance results are very different indeed. I get a reading of ~ 50milliohm for a 18625 Li-ion battery (seems reasonable) but with the JP9 link removed I get a reading of ~225milliohm ... very unreasonable and very different ... So ... JP9 is absolutely critical because RCAL measurements are used as the main bridge reference for impedance calculations .... SO What's going ON ADI EXPERTS ....NO EXPLANATION NO COMMENTS?

    5. After further modifications to Interrupt routines, My new coding and my modified BATImpedance example code is now basically working now. All SPI code works reliably including Sequencer and FIFO functions, not sure about RCAL results but the Impedance measurements seem to approximately align with what I expect, I now claim definitely that JP9 (omitted in the original EVAL-AD5941BATZ shipped board) needs to be installed otherwise impedance results are way way off and Reference calibration Ratio will be INACCURATE.

    6. I need to test further to confirm impedance result data. That said, I feel I have accomplished a some good results and proved that my Linux Code can now drive this complex chip using my modified SPI code drivers without the need to use bit-banging techniques like the ADI  ADuCM3029 CPU.