node-red-contrib-waveshare-shield-adda-11010
Loading...
Searching...
No Matches
ads1256.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# ads1256.py
3#
4# Copyright (c) 2020-2022 Spring City Solutions LLC and other contributors
5# Licensed under the Apache License, Version 2.0
6#
7# Need to have the rpi.gpio and spidev python libraries installed
8#
9# For development, need to install the pytest system.
10# sudo apt-get install python3-pytest
11# See also tests/ads1256_test.py
12# Running 'python -m pytest' should result in no errors
13#
14# For development, need to install the flake8 system.
15# sudo apt-get install python3-flake8
16# Running flake8 should report no serious problems,
17# with E501 being ignored and excluding tests/
18#
19# Hardware Links:
20#
21# Board Manufacturer Store:
22# https://www.waveshare.com/High-Precision-AD-DA-Board.htm
23#
24# Board Manufacturer Wiki:
25# https://www.waveshare.com/wiki/High-Precision_AD/DA_Board
26#
27# Links for ADS1256 chip:
28# https://www.ti.com/product/ADS1256
29# https://www.ti.com/lit/gpn/ads1256
30# Book "Fundamentals of Precision ADC Noise Analysis" https://www.ti.com/lit/pdf/slyy192
31# Journal Series "How delta-sigma ADCs work" https://www.ti.com/lit/pdf/slyt423 https://www.ti.com/lit/pdf/slyt438
32# App Note "Digital Filter Types in Delta-Sigma ADCs" https://www.ti.com/lit/pdf/sbaa230
33#
34# Link for LM285 chip, connected to the ADS1256:
35# https://www.ti.com/product/LM285-2.5-N
36# https://www.ti.com/lit/gpn/lm285-2.5-n
37#
38# Link for DAC8532 chip:
39# https://www.ti.com/product/DAC8532
40# https://www.ti.com/lit/gpn/dac8532
41#
42# Spring City Solutions Links:
43#
44# Spring City Solutions Node-RED project page:
45# https://www.springcitysolutions.com/nodered
46#
47# Spring City Solutions page for this node:
48# https://www.springcitysolutions.com/nodered-waveshare-adda-shield
49#
50# Spring City Solutions Node-RED project email:
51# nodered@springcitysolutions.com
52#
53# Spring City Solutions Gitlab for this node:
54# https://gitlab.com/SpringCitySolutionsLLC/waveshare-shield-adda-11010
55#
56# Patreon Page:
57# https://www.patreon.com/springcitysolutions_nodered
58#
59# Software Links:
60#
61# Spring City Solutions Gitlab for this node:
62# https://gitlab.com/SpringCitySolutionsLLC/waveshare-shield-adda-11010
63#
64# Doxygen docs for this node autogenerated by Gitlab CI/CD:
65# https://springcitysolutionsllc.gitlab.io/waveshare-shield-adda-11010/index.html
66#
67# npmjs for this node:
68# https://npmjs.org/package/node-red-contrib-waveshare-shield-adda-11010
69#
70# Node-RED flows for this node:
71# https://flows.nodered.org/node/node-red-contrib-waveshare-shield-adda-11010
72#
73# Documentation Links:
74#
75# Gitlab wiki for this node (the master copy of list of links is here):
76# https://gitlab.com/SpringCitySolutionsLLC/waveshare-shield-adda-11010/-/wikis/home
77#
78# Waveshare's libraries for this hardware:
79# https://github.com/waveshare/High-Precision-AD-DA-Board
80#
81# Youtube video "How to set up":
82# TODO
83#
84# Youtube video "How to use":
85# TODO
86#
87# Youtube video "Testing Results":
88# TODO
89#
90# Usage:
91#
92# ads1256.py analog "channel number 0 to 7" "gain 1 to 64" "rate 2.5 to 30000" "on or off buffer" "sdcs off of current"
93# returns 0 for 0 volts and decimal 0x7fffff for 5 volts
94#
95# ads1256.py calibrationFSC
96# returns fullscale calibration register value as a decimal integer
97#
98# ads1256.py calibrationOFC
99# returns offset calibration register value as a decimal integer
100#
101# ads1256.py digitalIn "D0 to D3"
102# returns 0 or 1 as per input to the specified pin
103#
104# ads1256.py digitalOut "D0 to D3" "0 or 1"
105
106import argparse
107import json
108import sys
109
110import RPi.GPIO as GPIO
111import spidev
112
113# TODO: Could make the SPI control pins configurable.
114cs = 22
115drdy = 17
116
117# The overall mode of operation
118mode = 'analog'
119# For GPIO
120output = -1
121
122register_address = dict([('status', 0x00),
123 ('mux', 0x01),
124 ('adcon', 0x02),
125 ('drate', 0x03),
126 ('gpio', 0x04),
127 ('ofc0', 0x05),
128 ('ofc1', 0x06),
129 ('ofc2', 0x07),
130 ('fsc0', 0x08),
131 ('fsc1', 0x09),
132 ('fsc2', 0x0a)])
133
134register_data = dict([('adcon', 0x00),
135 ('drate', 0x00),
136 ('fsc', 0x000000),
137 ('gpio_direction_bitmask', 0x00),
138 ('gpio_status_bitmask', 0x00),
139 ("mux", 0x00),
140 ('ofc', 0x000000),
141 ('status', 0x00)])
142
143
144# Functions that do things.
145
146
148 """Prints a JSON of the register_data for development and testing."""
149 print(json.dumps(register_data, sort_keys=True, indent=4))
150
151
152def parse_args(raw_args):
153 """Parses the args into ready to write register_data."""
154 # First parse the CLI.
155 parser = argparse.ArgumentParser()
156 parser.add_argument("--mode", type=str, default="analog",
157 help="Mode: analog, calibrationFSC, calibrationOFC, digitalIn, digitalOut default analog")
158 parser.add_argument("--port_p", type=str, default="0",
159 help="Positive Analog Input Port: 0 thru 8 default 0")
160 parser.add_argument("--port_n", type=str, default="8",
161 help="Negative Analog Input Port: 0 thru 8 default 8 (AINCOM)")
162 parser.add_argument("--gain", type=str, default="1",
163 help="Input Amp Gain: 1, 2, 4, 8, 16, 32, 64 default 1")
164 parser.add_argument("--rate", type=str, default="2.5",
165 help="Data Rate in samples per second: 2.5, 5, 10, 15, 25, 30, 50, 60, 100, 500, 1000, 2000, 3750, 7500, 15000, 30000 default 2.5")
166 parser.add_argument("--buffer", type=str, default="off",
167 help="Input Buffer: off, on default off")
168 parser.add_argument("--sdcs", type=str, default="off",
169 help="SDCS sensor direct current source microamps: off, 0.5, 2, 10 default off")
170 parser.add_argument("--pin", type=str, default="D0",
171 help="Specify pin for modes digitalIn and digitalOut: D0, D1, D2, D3 default D0")
172 parser.add_argument("--output", type=str, default="-1",
173 help="Absence means input mode, or specify output for modes digitalIn and digitalOut: 0, 1 default input mode")
174 parser.add_argument("-t", "--test", action="store_true",
175 help="Print JSON of register_data")
176 args = parser.parse_args(raw_args)
177
178 # Save global configs
179 global mode
180 mode = args.mode
181 global output
182 output = args.output
183
184 # Process the STATUS register, including the BUFEN analog input buffer property.
185 register_data['status'] = 0x00
186 if (args.buffer == 'on'):
187 register_data['status'] = 0x02
188 if (args.buffer == 'off'):
189 register_data['status'] = 0x00
190
191 # Process the ADCON register, including PGA gain and SDCS property.
192 register_data['adcon'] = 0x20 # Default clock out rate setting
193 if (args.gain == '1'):
194 register_data['adcon'] += 0x00
195 if (args.gain == '2'):
196 register_data['adcon'] += 0x01
197 if (args.gain == '4'):
198 register_data['adcon'] += 0x02
199 if (args.gain == '8'):
200 register_data['adcon'] += 0x03
201 if (args.gain == '16'):
202 register_data['adcon'] += 0x04
203 if (args.gain == '32'):
204 register_data['adcon'] += 0x05
205 if (args.gain == '64'):
206 register_data['adcon'] += 0x06
207 if (args.sdcs == 'off'):
208 register_data['adcon'] += 0x00
209 if (args.sdcs == '0.5'):
210 register_data['adcon'] += 0x08
211 if (args.sdcs == '2'):
212 register_data['adcon'] += 0x10
213 if (args.sdcs == '10'):
214 register_data['adcon'] += 0x18
215
216 # Process the DRATE register, including the rate property.
217 register_data['drate'] = 0x03
218 if (args.rate == '30000'):
219 register_data['drate'] = 0xF0
220 if (args.rate == '15000'):
221 register_data['drate'] = 0xE0
222 if (args.rate == '7500'):
223 register_data['drate'] = 0xD0
224 if (args.rate == '3750'):
225 register_data['drate'] = 0xC0
226 if (args.rate == '2000'):
227 register_data['drate'] = 0xB0
228 if (args.rate == '1000'):
229 register_data['drate'] = 0xA1
230 if (args.rate == '500'):
231 register_data['drate'] = 0x92
232 if (args.rate == '100'):
233 register_data['drate'] = 0x82
234 if (args.rate == '60'):
235 register_data['drate'] = 0x72
236 if (args.rate == '50'):
237 register_data['drate'] = 0x63
238 if (args.rate == '30'):
239 register_data['drate'] = 0x53
240 if (args.rate == '25'):
241 register_data['drate'] = 0x43
242 if (args.rate == '15'):
243 register_data['drate'] = 0x33
244 if (args.rate == '10'):
245 register_data['drate'] = 0x23
246 if (args.rate == '5'):
247 register_data['drate'] = 0x13
248 if (args.rate == '2.5'):
249 register_data['drate'] = 0x03
250
251 # Process the MUX register, including the port.
252 # register_data['mux'] = 0x08
253 register_data['mux'] = (int(args.port_p) << 4) | int(args.port_n)
254
255 # Process the gpio_direction_bitmask and gpio_status_bitmask registers, including the pin property.
256 register_data['gpio_direction_bitmask'] = 0x10
257 register_data['gpio_status_bitmask'] = 0x01
258 if (args.pin == 'D0'):
259 register_data['gpio_direction_bitmask'] = 0x10
260 register_data['gpio_status_bitmask'] = 0x01
261 if (args.pin == 'D1'):
262 register_data['gpio_direction_bitmask'] = 0x20
263 register_data['gpio_status_bitmask'] = 0x02
264 if (args.pin == 'D2'):
265 register_data['gpio_direction_bitmask'] = 0x40
266 register_data['gpio_status_bitmask'] = 0x04
267 if (args.pin == 'D3'):
268 register_data['gpio_direction_bitmask'] = 0x80
269 register_data['gpio_status_bitmask'] = 0x08
270
271 # Optionally output register state for development purposes.
272 if args.test:
274 exit()
275
276
277# Functions that read or write to the device.
278# Device commands are run by functions named after the datasheet provided device command names.
279# The datasheet names are all caps, Python names are lowercase, other than that issue, they match.
280# The complete list of commands is on datasheet page 34 table 24.
281
282
283def rreg(register):
284 """Run RREG command as explained on datasheet page 36."""
285 GPIO.output(cs, 0)
286 firstbyte = 0x10 + register
287 secondbyte = 0x01
288 spi.writebytes([firstbyte, secondbyte])
289 data = spi.readbytes(1)
290 GPIO.output(cs, 1)
291 return data[0]
292
293
294def wreg(register, data):
295 """Run WREG command as explained on datasheet page 36."""
296 GPIO.output(cs, 0)
297 firstbyte = 0x50 + register
298 secondbyte = 0x01
299 spi.writebytes([firstbyte, secondbyte, data])
300 GPIO.output(cs, 1)
301
302
304 """Run SELFCAL command as explained on datasheet page 36."""
305 GPIO.output(cs, 0)
306 spi.writebytes([0xf0])
307 GPIO.output(cs, 1)
308 while True:
309 if (GPIO.input(drdy) == 0):
310 break
311
312
313def rdata():
314 """Run RDATA command as explained on datasheet page 34."""
315 while True:
316 if (GPIO.input(drdy) == 0):
317 break
318 GPIO.output(cs, 0)
319 spi.writebytes([0x01])
320 buf = spi.readbytes(3)
321 GPIO.output(cs, 1)
322 read = (buf[0] << 16) & 0xff0000
323 read |= (buf[1] << 8) & 0xff00
324 read |= (buf[2]) & 0xff
326
327
328def sync():
329 """Run SYNC command as explained on datasheet page 37."""
330 GPIO.output(cs, 0)
331 spi.writebytes([0xfc])
332 GPIO.output(cs, 1)
333
334
335def wakeup():
336 """Run WAKEUP command as explained on datasheet page 37."""
337 GPIO.output(cs, 0)
338 spi.writebytes([0xff])
339 GPIO.output(cs, 1)
340
341
343 """Read the FSC calibration value."""
344 # See datasheet page 33 to read the raw byte values.
345 # FSC per datasheet pg 24 is an unsigned 24 bit integer.
346 register_data['fsc'] = 0x000000
347 register_data['fsc'] += (rreg(register_address['fsc2']) << 16)
348 register_data['fsc'] += (rreg(register_address['fsc1']) << 8)
349 register_data['fsc'] += rreg(register_address['fsc0'])
350
351
353 """Read the OFC calibration value."""
354 # See datasheet page 33 to read the raw byte values.
355 # OFC per datasheet pg 24 is a twos complement 24 bit signed integer.
356 ofc_twos_complement = 0x000000
357 ofc_twos_complement += (rreg(register_address['ofc2']) << 16)
358 ofc_twos_complement += (rreg(register_address['ofc1']) << 8)
359 ofc_twos_complement += rreg(register_address['ofc0'])
360 register_data['ofc'] = twos_complement_to_integer_24bit(ofc_twos_complement)
361
362
364 """Write the ADCON register. See datasheet page 31."""
365 wreg(register_address['adcon'], register_data['adcon'])
366
367
369 """Write the DRATE register. See datasheet page 32."""
370 wreg(register_address['drate'], register_data['drate'])
371
372
374 """Write the MUX register. See datasheet page 31."""
375 wreg(register_address['mux'], register_data['mux'])
376
377
379 """Write the STATUS register. See datasheet page 30."""
380 wreg(register_address['status'], register_data['status'])
381
382
383# Functions that convert
384
385
387 """Convert a 24 bit twos complement value to a plain Python integer."""
388 result = twos_complement
389 if (twos_complement & (1 << 23)):
390 result = twos_complement & ~(1 << 24)
391 result = -(1 << 24) + result
392 return result
393
394
395parse_args(sys.argv[1:])
396
397
398GPIO.setmode(GPIO.BCM)
399GPIO.setwarnings(False)
400GPIO.setup(cs, GPIO.OUT)
401GPIO.output(cs, 1)
402GPIO.setup(drdy, GPIO.IN)
403
404spi = spidev.SpiDev()
405spi.open(0, 0)
406spi.max_speed_hz = 20000
407spi.mode = 0b01
408
409if (mode == 'analog'):
410 # Write the registers in the correct order.
415 # Self Calibration required after MUX change.
416 selfcal()
417 # Sync and Wakeup, in that order, are required to start a conversion.
418 sync()
419 wakeup()
420 # Read data.
421 print(rdata())
422
423if (mode == 'calibrationFSC'):
425 print(register_data['fsc'])
426
427if (mode == 'calibrationOFC'):
429 print(register_data['ofc'])
430
431if (mode == 'digitalIn'):
432 # Change exactly one bit to input mode.
433 wreg(0x04, rreg(0x04) | register_data['gpio_direction_bitmask'])
434 if ((rreg(0x04) & register_data['gpio_status_bitmask']) > 0):
435 print('1')
436 else:
437 print('0')
438
439if (mode == 'digitalOut'):
440 if (output == '0'):
441 wreg(0x04,
442 rreg(0x04) & ~register_data['gpio_direction_bitmask'] & ~register_data['gpio_status_bitmask'])
443 if (output == '1'):
444 wreg(0x04,
445 rreg(0x04) & ~register_data['gpio_direction_bitmask'] | register_data['gpio_status_bitmask'])
twos_complement_to_integer_24bit(twos_complement)
Definition ads1256.py:386
write_mux_reg()
Definition ads1256.py:373
wakeup()
Definition ads1256.py:335
rdata()
Definition ads1256.py:313
read_fsc_reg()
Definition ads1256.py:342
write_status_reg()
Definition ads1256.py:378
write_adcon_reg()
Definition ads1256.py:363
parse_args(raw_args)
Definition ads1256.py:152
selfcal()
Definition ads1256.py:303
output_json()
Definition ads1256.py:147
write_drate_reg()
Definition ads1256.py:368
read_ofc_reg()
Definition ads1256.py:352
wreg(register, data)
Definition ads1256.py:294
rreg(register)
Definition ads1256.py:283