Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Comparator functionality #98

Merged
merged 12 commits into from
Aug 6, 2024
Merged

Added Comparator functionality #98

merged 12 commits into from
Aug 6, 2024

Conversation

RoaCode
Copy link
Contributor

@RoaCode RoaCode commented Jul 23, 2024

Enabled use of Comparator output by adding controls for the COMP_QUE fields in the Config Register. Also added an example of use.

Added controls for the COMP_QUE fields in the Config Register that handle the Comparator output. Also added an example of use.
Added comments for new functions
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR! I've added a few comments around the new APIs and their documentation.

adafruit_ads1x15/ads1x15.py Outdated Show resolved Hide resolved
adafruit_ads1x15/ads1x15.py Outdated Show resolved Hide resolved
adafruit_ads1x15/ads1x15.py Outdated Show resolved Hide resolved
adafruit_ads1x15/ads1x15.py Outdated Show resolved Hide resolved
adafruit_ads1x15/analog_in.py Outdated Show resolved Hide resolved
Added threshold properties, changed to "comparator_queue_length" property name, added "convert to voltage" and "convert to value" functions
@RoaCode
Copy link
Contributor Author

RoaCode commented Jul 24, 2024

Thanks for the feedback, @tannewt . I think I've addressed everything in the latest commit.

Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes! The docs look good. Just a couple more questions.

adafruit_ads1x15/ads1x15.py Outdated Show resolved Hide resolved
adafruit_ads1x15/ads1x15.py Outdated Show resolved Hide resolved
adafruit_ads1x15/ads1x15.py Outdated Show resolved Hide resolved
Writing of the threshold registers moved to the setter functions, "threshold" spelled out in variables, improved readability of some comments.
@RoaCode
Copy link
Contributor Author

RoaCode commented Jul 25, 2024

Comments addressed. I2C writes to threshold registers moved to setter functions, "threshold" spelled out in variables, and improved description in comments.

Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two minor things. Looks good otherwise. Getting close! Thanks!

adafruit_ads1x15/ads1x15.py Outdated Show resolved Hide resolved
Comment on lines 98 to 99
comparator_low_threshold: int = 0x8000,
comparator_high_threshold: int = 0x7FF0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the low threshold default higher than the default high threshold?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is tricky because the values are in 2's complement, but also they are 12-bit values stored in 16-bit registers where the 4 LSBs are 0. Because of this, 0x8000 is the lowest possible value (decimal -32768 in 16-bit or really -2048 in 12-bit) and 0x7FF0 is the highest possible value (decimal 32752 in 16-bit or really 2047 in 12-bit).
I expanded the comments to hopefully explain this better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! I think it'll be clearer to set the defaults to the actual values and have the properties do the conversion and masking.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I set the defaults to the actual (12-bit signed) values so that it is easier to read, and moved the 2's complement and bit shift to 16-bit to the setter functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use 16-bit values because this library is also for the ADS1115 which is 16-bit. In the math you can do self.bits to read if it is 12 or 16 bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to copy the handling of the comparator low/high threshold properties just like the other properties, such as data rate. The subclasses handle the lower level chip differences (12 vs 16 bits, default settings, etc) and pass those back to the higher level ads1x15 class.
Also, the ADC data in the analog_in class is handled as a signed 16-bit integer, so I felt it was appropriate to similarly handle the comparator threshold values as signed 16-bit integers as well.

Fixed issues in ads1x15 init comment. Removed stray copy/paste and added more info for comparator thresholds.
Disabled instance attributes req for ADS1x15 init function and shortened "possible_comparator_queue_lengths" to "possible_comp_queue_lengths"
@FoamyGuy
Copy link
Contributor

If you use the process in the guide here: https://learn.adafruit.com/improve-your-code-with-pylint/check-your-code-with-pre-commit to install pre-commit locally you can run it and it'll help you find and fix anything that the actions check will have issue with.

Trying to fix "too many instance attributes" issue
@RoaCode
Copy link
Contributor Author

RoaCode commented Jul 29, 2024

@FoamyGuy Thanks for the comment. I did have to change the pylint version (to v3.2.6 from v2.17.4) to get it to work, I believe because I am using python 3.12. I hope that is OK.

@FoamyGuy
Copy link
Contributor

@RoaCode thank you!

Yes thats fine to change the pylint version for now. We're in the process of switching to use Ruff so this repo will get swapped over to that anyway. I've been contending with that same issue with pyt3.12 and pylint.

Threshold properties changed to 12-bit signed integers and conversion to unsigned 16-bit numbers moved to threshold setter functions.
Adapted functions to use bits properties and moved part specific settings to subclasses
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for continuing to iterate on this with me. It is the first comparator API in CircuitPython-land so I want to ensure we get it right.

I think we'll need the thresholds to be ints in the 17-bit signed range so that the voltage to value conversion can produce the whole 16 bit unsigned range that value has been defined as.

Comment on lines 82 to 83
function. Value is 12-bit, 2's complement stored in
16-bit register where 4 LSBs are 0. Defaults to 0x8000 (decimal -32768).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function. Value is 12-bit, 2's complement stored in
16-bit register where 4 LSBs are 0. Defaults to 0x8000 (decimal -32768).
function. Value is a signed 17-bit integer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized I forgot to update this comment in the higher level (ads1x15) class to be more generic, since the subclasses/chips handle this differently.
However, even for the ads1115, the conversion register (ADC data) and the threshold registers on the chip are only 16 bits total, whether or not you think of them as signed or unsigned, so there are only 2^16 (0 to 65535) possible values. So I don't understand why call it a 17-bit int? It would seem like python can handle it either way, as in the widths of ints don't have to be specified. But I agree that the comment should be correct so everyone understands.

Comment on lines 86 to 87
function. Value is 12-bit, 2's complement stored in
16-bit register where 4 LSBs are 0. Defaults to 0x7FF0 (decimal 32752).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function. Value is 12-bit, 2's complement stored in
16-bit register where 4 LSBs are 0. Defaults to 0x7FF0 (decimal 32752).
function. Value is a signed 17-bit integer.

def __init__(
self,
i2c: I2C,
gain: float = 1,
data_rate: Optional[int] = None,
mode: int = Mode.SINGLE,
comparator_queue_length: int = 0,
comparator_low_threshold: Optional[int] = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
comparator_low_threshold: Optional[int] = None,
comparator_low_threshold: int = -32768,

def __init__(
self,
i2c: I2C,
gain: float = 1,
data_rate: Optional[int] = None,
mode: int = Mode.SINGLE,
comparator_queue_length: int = 0,
comparator_low_threshold: Optional[int] = None,
comparator_high_threshold: Optional[int] = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
comparator_high_threshold: Optional[int] = None,
comparator_high_threshold: int = 32767,

Comment on lines 111 to 120
self.comparator_low_threshold = (
self._comp_low_thres_default()
if comparator_low_threshold is None
else comparator_low_threshold
)
self.comparator_high_threshold = (
self._comp_high_thres_default()
if comparator_high_threshold is None
else comparator_high_threshold
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default is the same for both because the lower four bits are 1s in the high threshold.

Suggested change
self.comparator_low_threshold = (
self._comp_low_thres_default()
if comparator_low_threshold is None
else comparator_low_threshold
)
self.comparator_high_threshold = (
self._comp_high_thres_default()
if comparator_high_threshold is None
else comparator_high_threshold
)
self.comparator_low_threshold = comparator_low_threshold
self.comparator_high_threshold = comparator_high_threshold

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct. I mistakenly went to some effort to separate them when I didn't need to.


:param int value: 16-bit signed integer to write to register
"""
if value < 0 or value > 65535:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the right range to check against since it is signed?

Suggested change
if value < 0 or value > 65535:
if value < -32768 or value > 32767:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are technically correct, but python will look at 0xFFFF as 65535 and say that it is out of range, instead of looking at it as -1 and say that it is in range. I feel like what I did was a workaround to make things work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just discovered "a_bytes_big = an_int.to_bytes(2, 'big', signed = True)" that should convert between signed integers and bytes easier. I'll try to work that in.

Comment on lines -72 to +78
return value >> 4
return value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't change this. You'll change how read() works otherwise. Only .value needs to be the 16bit standard.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the ads1015 flow, I believe there are 2 unnecessary bit shifts. This is the right bit shift, which is immediately cancelled out by the left bit shift in the value property of analog_in.py. I removed both of these. There is no need to take in a 16-bit number, convert to 12, convert back to 16, and then report as 16. Removing both keeps .value at 16-bits for both ads1015 and ads1115.

adafruit_ads1x15/analog_in.py Outdated Show resolved Hide resolved
Comment on lines 89 to 94
if value_int & 0x8000:
# Need to convert negative number through 2's complement
value_int -= 0x10000

# Need to bit shift if value is only 12-bits
value_int >>= 16 - self._ads.bits
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again. Don't worry about two's complement or the numbers of bits. This should be 16 bit unsigned or "17 bit" signed to voltage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are on different pages as far as how to represent value. From "Signed number representations" on Wikipedia, 8-bit signed goes from -127 to 127. 8-bit unsigned goes from 0 to 255. I think value should match the registers in the datasheet, which is 16 bit two's complement or signed.

Can you explain 17-bit to me?

Voltage is a float since we want it to have decimal places.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value for reading from an adc has been defined as 16-bit unsigned since we added the AnalogIn API. If you want this resolution signed, then you need to add another bit for the sign. That would make it 17 total bits. I realize that the comparator will only do 16-bit signed. This conversion from voltage to value should handle the full 16-bit unsigned range still though.

def comparator_high_threshold(self, value: int) -> None:
"""Set comparator high threshold value for ADS1015 ADC

:param int value: 16-bit signed integer to write to register
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
:param int value: 16-bit signed integer to write to register
:param int value: 17-bit signed integer to write to register. The lowest bit is dropped.

Moved specific comments about threshold registers to subclasses
Conversion calculations handled with signed integers, removing 2s complement. Removed subclass comparator threshold defaults since both chips have same default values.
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks much better! Thank you! Just one more thing about the warning prints.

adafruit_ads1x15/ads1x15.py Outdated Show resolved Hide resolved
adafruit_ads1x15/ads1x15.py Outdated Show resolved Hide resolved
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Thank you!

@tannewt tannewt merged commit bf390e7 into adafruit:main Aug 6, 2024
1 check passed
@RoaCode RoaCode deleted the comparator branch August 6, 2024 17:03
tannewt added a commit to adafruit/Adafruit_CircuitPython_Bundle that referenced this pull request Aug 16, 2024
Updating https://github.com/adafruit/Adafruit_CircuitPython_ADS1x15 to 2.3.0 from 2.2.26:
  > Merge pull request adafruit/Adafruit_CircuitPython_ADS1x15#98 from RoaCode/comparator

Updating https://github.com/adafruit/Adafruit_CircuitPython_DisplayIO_SSD1306 to 2.0.3 from 2.0.2:
  > Merge pull request adafruit/Adafruit_CircuitPython_DisplayIO_SSD1306#46 from EAGrahamJr/revert-sleep

Updating https://github.com/adafruit/Adafruit_CircuitPython_FT5336 to 1.1.0 from 1.0.2:
  > Merge pull request adafruit/Adafruit_CircuitPython_FT5336#6 from adafruit/axis_swap

Updating https://github.com/adafruit/Adafruit_CircuitPython_IS31FL3731 to 3.4.2 from 3.4.1:
  > Merge pull request adafruit/Adafruit_CircuitPython_IS31FL3731#56 from FoamyGuy/use_ruff

Updating https://github.com/adafruit/Adafruit_CircuitPython_PyPortal to 6.3.5 from 6.3.4:
  > Merge pull request adafruit/Adafruit_CircuitPython_PyPortal#136 from tannewt/fix_gitattributes

Updating https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display to 3.12.5 from 3.12.4:
  > Merge pull request adafruit/Adafruit_CircuitPython_RGB_Display#126 from simonldwg/ssd1331-remove-prints

Updating https://github.com/adafruit/Adafruit_CircuitPython_SCD4X to 1.4.3 from 1.4.2:
  > Merge pull request adafruit/Adafruit_CircuitPython_SCD4X#20 from kolcz/main

Updating https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad to 1.23.0 from 1.21.0:
  > Merge pull request adafruit/Adafruit_CircuitPython_ImageLoad#84 from deshipu/bug-74
  > Merge pull request adafruit/Adafruit_CircuitPython_ImageLoad#83 from deshipu/png-filters
  > Merge pull request adafruit/Adafruit_CircuitPython_ImageLoad#82 from ch4nsuk3/png-transparency

Updating https://github.com/adafruit/Adafruit_CircuitPython_Logging to 5.5.0 from 5.4.0:
  > Merge pull request adafruit/Adafruit_CircuitPython_Logging#61 from FoamyGuy/formatters
  > Merge pull request adafruit/Adafruit_CircuitPython_Logging#62 from FoamyGuy/remove_extra_newline

Updating https://github.com/adafruit/Adafruit_CircuitPython_MIDI to 1.4.19 from 1.4.18:
  > Merge pull request adafruit/Adafruit_CircuitPython_MIDI#52 from jordanhemingway-revvity/type_annotations

Updating https://github.com/adafruit/Adafruit_CircuitPython_Ticks to 1.1.0 from 1.0.13:
  > Merge pull request adafruit/Adafruit_CircuitPython_Ticks#11 from adafruit/ticks-exception-like-micropython

Updating https://github.com/adafruit/Adafruit_CircuitPython_Bundle/circuitpython_library_list.md to NA from NA:
  > Added the following libraries: Adafruit_CircuitPython_RFM
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants