Skip to content

Instantly share code, notes, and snippets.

@nicolas17
Last active November 8, 2025 06:54
Show Gist options
  • Save nicolas17/16e7b51ae861cce6000aa5904826168d to your computer and use it in GitHub Desktop.
Save nicolas17/16e7b51ae861cce6000aa5904826168d to your computer and use it in GitHub Desktop.
HDQ decoder for sigrok
# Copyright (c) 2024 JJTech <[email protected]>
#
# SPDX-License-Identifier: GPL-2.0-or-later
"""
HDQ (High-speed Data Queue) is a simple protocol used for Texas Instruments
'Gas Gauge' battery fuel gauges. It is used to read and write data to the fuel
gauge's memory.
Similarly to the '1-wire' protocol, it is a single-wire protocol that uses a
single data line to communicate with the fuel gauge.
Protocol documentation: https://www.ti.com/lit/pdf/slua408
"""
import sigrokdecode as srd # type: ignore
from .pd import Decoder
# Copyright (c) 2024 JJTech <[email protected]>
# Copyright (c) 2024 Nicolás Alvarez <[email protected]>
#
# SPDX-License-Identifier: GPL-2.0-or-later
import sigrokdecode as srd # type: ignore
class Decoder(srd.Decoder):
api_version = 3
id = 'hdq'
name = 'HDQ'
longname = 'HDQ (High-speed Data Queue)'
desc = 'Texas Instruments Gas Gauge battery fuel gauge protocol'
license = 'mit'
inputs = ['logic']
outputs = ['hdq']
tags = ['Embedded/industrial']
channels = (
{'id': 'data', 'name': 'Data', 'desc': 'Data line'},
)
options = ()
annotations = (
('bit', 'Bit'),
('addr', 'Address'),
('data', 'Data'),
('rw', 'R/W Bit'),
)
annotation_rows = (
('bits', 'Bits', (0,)),
('values', 'Values', (1,2,3)),
)
def __init__(self):
print("INIT")
self.reset()
def reset(self):
print("RESET")
self.sample_rate = None
self.previous_sample_num = 0
def metadata(self, key, value):
print("METADATA")
if key == srd.SRD_CONF_SAMPLERATE:
self.sample_rate = value
def start(self):
print("START")
self.out_ann = self.register(srd.OUTPUT_ANN)
self.set_state('INITIAL')
def set_state(self, state):
self.state = state
self.num = 0
self.bitcount = 0
self.startsample = 0
def decode_txs(self, bits):
for bit, start, end in bits:
if bit == "reset":
self.set_state("INITIAL")
continue
bit = int(bit)
if self.state == 'INITIAL':
if self.startsample == 0:
self.startsample = start
self.num |= (bit << self.bitcount)
self.bitcount += 1
if self.bitcount == 7:
self.put(self.startsample, end, self.out_ann, [1, [
f'Address: #0x{self.num:04x}',
f'Addr: #0x{self.num:04x}',
f'#0x{self.num:04x}',
f'0x{self.num:x}'
]])
self.set_state('RW')
elif self.state == 'RW':
if bit:
label = ['Write', 'W']
else:
label = ['Read', 'R']
self.dir=bit
self.put(start, end, self.out_ann, [3, label])
self.set_state('DATA')
elif self.state == 'DATA':
if self.startsample == 0:
self.startsample = start
self.num |= (bit << self.bitcount)
self.bitcount += 1
if self.bitcount == 8:
self.put(self.startsample, end, self.out_ann, [2, [
f'Data {"to write" if self.dir else "read"}: 0x{self.num:x}',
f'Data {"W" if self.dir else "R"}: 0x{self.num:x}',
f'Data: 0x{self.num:x}',
f'D: 0x{self.num:x}',
f'0x{self.num:x}',
]])
self.set_state('INITIAL')
def decode_edges(self):
while True:
self.wait({0: 'f'})
fall_sample = self.samplenum
self.wait({0: 'r'})
raise_sample = self.samplenum
yield (fall_sample, raise_sample)
def decode_bits(self, edges):
"""
Measures the timings between edges and decodes them into bits.
Yields tuples with ("reset"|"0"|"1", start, end).
"""
MICRO = 1_000_000
MIN_BREAK = self.sample_rate * 190 // MICRO
MIN_ZERO = self.sample_rate * 86 // MICRO
MAX_ZERO = self.sample_rate * 145 // MICRO
MIN_ONE = self.sample_rate * 17 // MICRO
MAX_ONE = self.sample_rate * 50 // MICRO
for fall_sample, raise_sample in edges:
t = raise_sample - fall_sample
ann_start = fall_sample
ann_end = raise_sample
if t > MIN_BREAK:
self.put(ann_start, ann_end, self.out_ann, [0, ['Reset']])
yield ("reset", ann_start, ann_end)
elif MIN_ZERO <= t <= MAX_ZERO:
self.put(ann_start, ann_end, self.out_ann, [0, ['0']])
yield ("0", ann_start, ann_end)
elif MIN_ONE <= t <= MAX_ONE:
self.put(ann_start, ann_end, self.out_ann, [0, ['1']])
yield ("1", ann_start, ann_end)
def decode(self):
print("DECODE")
assert self.sample_rate is not None
for tx in self.decode_txs(self.decode_bits(self.decode_edges())):
pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment