Comprehensive Analysis of ADS131A04 Data
Transfer and Bit Manipulation Logic
Based on the provided flowchart, schematic, and code, here's a detailed breakdown of the data
reading and assembly process for the ADS131A04 ADC.
ADS131A04 Data Frame Structure
Data Frame Organization
data_frame_size = 5 # Total words per frame
device_word_size = 3 # Bytes per word (24-bit)
The ADS131A04 transmits data in frames consisting of:
Word 0: Status word (24-bit)
Word 1: Channel 1 ADC data (24-bit)
Word 2: Channel 2 ADC data (24-bit)
Word 3: Channel 3 ADC data (24-bit)
Word 4: Channel 4 ADC data (24-bit)
Detailed Code Section Analysis
Outer Loop: Frame Processing
for i in range(data_frame_size): # i = 0,1,2,3,4 (5 words total)
data = 0 # Reset accumulator for each 24-bit word
val = 0 # Reset byte holder
negative = False # Reset sign flag
Purpose: Processes each of the 5 words in the data frame sequentially.
Inner Loop: Byte Assembly
for j in range(device_word_size): # j = 0,1,2 (3 bytes per word)
val = spi.xfer2([0x00])[^0] # Read one byte from SPI
if j == 0 and (val & 0x80): # Check MSB of first byte
negative = True
data = (data << 8) | val # Shift and combine bytes
Purpose: Assembles three 8-bit bytes into one 24-bit word using MSB-first protocol.
Bit Manipulation Logic: (data << 8) | val
Step-by-Step Assembly Process
Let's trace through a complete 24-bit word assembly with example data:
Example: Reading ADC value 0x123456 (positive)
Iteration j val data (before) Operation data (after)
1 0 0x12 0x000000 (0x000000 << 8) \| 0x12 0x000012
2 1 0x34 0x000012 (0x000012 << 8) \| 0x34 0x001234
3 2 0x56 0x001234 (0x001234 << 8) \| 0x56 0x123456
Example: Reading ADC value 0x823456 (negative, MSB = 1)
Iteration j val data (before) Sign Check data (after)
1 0 0x82 0x000000 0x82 & 0x80 = 0x80 → negative = True 0x000082
2 1 0x34 0x000082 No check 0x008234
3 2 0x56 0x008234 No check 0x823456
Bit Shifting Explanation
The operation (data << 8) | val performs two actions:
1. Left Shift (<< 8): Moves existing bits 8 positions left
data = 0x001234
data << 8 = 0x123400 (shift left by 8 bits)
2. Bitwise OR (| val): Combines with new byte
0x123400 | 0x56 = 0x123456
This creates space for the new byte while preserving existing data.
Sign Detection and Two's Complement Conversion
Sign Bit Detection
if j == 0 and (val & 0x80): # Check MSB for sign
negative = True
Logic:
Only checks the first byte (j == 0)
Uses bitwise AND with 0x80 (binary: 10000000)
If MSB = 1, the 24-bit number is negative in two's complement format
Two's Complement Conversion
if negative:
data = data - (1 << 24) # Convert to signed 32-bit
negative = False
Mathematical Explanation:
(1 << 24) = 2^24 = 16,777,216
For negative numbers: signed_value = unsigned_value - 2^24
Example:
Raw data: 0x823456 = 8,533,078 (unsigned)
Signed conversion: 8,533,078 - 16,777,216 = -8,244,138
Context Analysis: recv = (recv << 8) | val
This pattern appears throughout the code in different contexts:
Context 1: READY Word Assembly (reset_ADC)
val = spi.xfer2([0x00])[^0]
recv = (recv << 8) | val # Build first byte
val = spi.xfer2([0x11])[^0]
recv = (recv << 8) | val # Build 16-bit READY word
Purpose: Assembles the 16-bit READY word (0xFF04) during initialization.
Context 2: Command Response Assembly
response = spi.xfer2([0x06, 0x55, 0x00])
recv = (response[^0] << 8) | response[^1] # Build 16-bit response
Purpose: Constructs command acknowledgment responses from the ADC.
Context 3: Register Read Response
val = spi.xfer2([0x00])[^0]
recv = (recv << 8) | val # Build register value
Purpose: Assembles register values during read operations.
Data Flow Visualization
Complete Data Frame Reading Process
SPI Bus → [Status][Ch1][Ch2][Ch3][Ch4]
↓ ↓ ↓ ↓ ↓
3 bytes each (24-bit words)
↓ ↓ ↓ ↓ ↓
Byte Assembly: MSB → LSB
↓ ↓ ↓ ↓ ↓
Sign Detection & Two's Complement
↓ ↓ ↓ ↓ ↓
Voltage Conversion & Processing
Timing Relationship with DRDY Signal
According to the flowchart:
1. Wait for DRDY LOW - indicates new data available
2. Read complete frame - all 5 words (15 bytes total)
3. Process each word - assemble, sign-extend, convert
4. DRDY goes HIGH - indicates end of data frame
Error Handling and Validation
Potential Issues with Current Implementation
1. No timeout handling in DRDY waiting loops
2. No CRC verification of received data
3. No range validation of assembled values
Improved Implementation Suggestions
def read_24bit_word():
"""Enhanced 24-bit word reading with validation"""
word = 0
negative = False
for j in range(3): # 3 bytes per word
try:
val = spi.xfer2([0x00])[^0]
# Sign detection on first byte
if j == 0 and (val & 0x80):
negative = True
# Assemble word
word = (word << 8) | val
except Exception as e:
print(f"SPI read error: {e}")
return None
# Convert to signed if negative
if negative:
word = word - (1 << 24)
return word
Performance Considerations
Efficiency Analysis
The bit manipulation approach is highly efficient because:
No floating-point operations during assembly
Minimal memory allocation
Direct hardware bit operations
Single-pass processing of incoming data
Timing Analysis
At 8 kSPS sampling rate:
Frame interval: 125 μs between frames
SPI transmission time: ~15 bytes × 8 bits ÷ 8 MHz ≈ 15 μs
Processing overhead: Minimal bit operations
Available margin: ~110 μs for voltage conversion and application processing
This analysis demonstrates that the current implementation is well-suited for real-time seismic
data acquisition, providing efficient assembly of 24-bit ADC values while maintaining the timing
requirements of the ADS131A04's continuous conversion mode.
⁂