vpaeder / pymcp2221

A python driver for the Microchip MCP2221/MCP2221A USB 2.0 to I2C/UART protocol converters
https://pypi.org/project/pymcp2221
MIT License
5 stars 2 forks source link

ADC Voltage Reference aren't used correctly #9

Closed jremmet closed 1 year ago

jremmet commented 1 year ago

I saw a strange behavior while using ADC. If I first set ReferenceVoltageSource.Internal and than GPIO3Function.ADC3 the settings seams to be ignored. This test was done with a fix voltage on GPIO3 (ABOUT 3.9V so 938 seams right at 4096mV ref)

(Pdb) self._mcp2221.chip.read_adc(2)
815
(Pdb) self._mcp2221.chip.write_adc_reference_source(ReferenceVoltageSource.Internal)
96 0 0 0 0 135 0 0 16 8 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
(Pdb) self._mcp2221.chip.read_adc(2)
938
(Pdb) self._mcp2221.chip._write_sram(SramDataSubcode.GPSettings, 3, GPIO3Function.ADC3)
96 0 0 0 0 0 0 128 16 8 8 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
(Pdb) self._mcp2221.chip.read_adc(2)
815

All readbacks of SRAM via 0x61 where fine. Also 'read_adc_reference_source`.Maybe a bug in the CPU? I saw a workaround for gpio, not sure if something like that it is needed for "ADC Voltage Reference" too?

vpaeder commented 1 year ago

Ok yes the setting change goes through _sram_write as well. Your fix should have fixed that reference change too.

jremmet commented 1 year ago

Actually this is in the SramDataSubcode.ChipSettings section and not covered by my change. From datasheet I would say the reference should not be touched because of

Bit 7: Enable loading of a
new ADC reference
1 Bits 2-0 will be used for ADC reference voltage selection
0 ADC reference will remain unaltered

in "Set SRAM settings"

It feels a bit wrong to set this bit and force a rewrite of the configuration.

vpaeder commented 1 year ago

Yes I see, and it wouldn't help since you say the voltage source setting remains in SRAM the way it should. What puzzles me is that both of your readings are not that close to 3.9V (should be 975 with a 4.096V ref). Feels more like an impedance issue to me. I'd play with the source impedance maybe.

jremmet commented 1 year ago

Thanks for your suggestion. The values are quite repeatable. My VDD is about 4675mV and the meassured voltage about 3815mV. It comes over a resistor divider 10k + 1k875 from a 24V source. So I should get 3815mV / 4096mV 1024 = 954 3815mV / 4675mV 1024 = 835

What I see is

(Pdb) from mcp2221.enums import ReferenceVoltageSource, GPIO3Function
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.write_adc_reference_source(ReferenceVoltageSource.Internal)
(Pdb) self._mcp2221.chip.read_adc(2)
945
(Pdb) self._mcp2221.chip.read_adc(2)
940
(Pdb) self._mcp2221.chip.read_adc(2)
940
(Pdb) self._mcp2221.chip.read_adc(2)
939
(Pdb) self._mcp2221.chip.read_adc(2)
942
(Pdb) self._mcp2221.chip.read_adc(2)
941
(Pdb) self._mcp2221.chip.read_adc(2)
944
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.gpio3_write_function(GPIO3Function.ADC3)
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
819
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.gpio3_write_function(GPIO3Function.ADC3)
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
819
(Pdb) self._mcp2221.chip.read_adc(2)
820
(Pdb) self._mcp2221.chip.read_adc(2)
820

820÷1024 × 4675mV = 3743 mV 942÷1024 × 4096mV = 3768 mV

So they both are equally lower. This may be an impedance issue. We are quite over the max 10k and have no capacitor.

But Setting the gpio Function to ADC3 as it was set before will change the result in the same way as switching the Reference to VDD. Setting the Reference actively to Internal heals this.

I guess for my usecase I can set the reference_source again as poor workaround.

vpaeder commented 1 year ago

I tried with my device, which has pin 1 connected to 3.3V through a 10k resistor and pin 3 through a 100k resistor (originally this is pull-up resistors for flash and enable signals for a uC, so not designed for that but anyway). The power-up settings are: all pins GPIO. This is also the state of the SRAM after boot. But then I get the following results:

After power-up
Voltage ref.: Internal (1.024V)
Pin 1 (GPIO): 0.00V
Pin 3 (GPIO): 2.76V - should be 0 since it is configured as GPIO

Set pins 1 & 3 to GPIO
Voltage ref.: Internal (1.024V)
Pin 1 (GPIO): 0.00V
Pin 3 (GPIO): 2.76V - should be 0 since it is configured as GPIO

Set voltage ref. to internal and 4.096V
Voltage ref.: Internal (4.096V)
Pin 1 (GPIO): 0.00V
Pin 3 (GPIO): 3.26V - measured 3.29V with voltmeter, OK, but should be GPIO

Set pin 3 to ADC
Voltage ref.: Internal (4.096V)
Pin 1 (GPIO): 0.00V
Pin 3 (ADC3): 2.75V - switched to ref = Vdd w/o being requested

Set pin 3 to GPIO
Voltage ref.: Internal (4.096V)
Pin 1 (GPIO): 0.00V
Pin 3 (GPIO): 0.00V

Set pin 1 to ADC
Voltage ref.: Internal (4.096V)
Pin 1 (ADC1): 2.74V - again, voltage ref. switched to Vdd
Pin 3 (GPIO): 0.00V

Set voltage ref. to 4.096V
Voltage ref.: Internal (4.096V)
Pin 1 (ADC1): 3.26V
Pin 3 (GPIO): 0.00V

Set pin 3 to ADC
Voltage ref.: Internal (4.096V)
Pin 1 (ADC1): 2.32V
Pin 3 (ADC3): 2.73V

Set pin 3 to GPIO
Voltage ref.: Internal (4.096V)
Pin 1 (ADC1): 2.75V
Pin 3 (GPIO): 0.00V

Set voltage ref. to internal and 4.096V
Voltage ref.: Internal (4.096V)
Pin 1 (ADC1): 3.26V
Pin 3 (GPIO): 0.00V

Set pin 3 to ADC and pin 1 to GPIO
Voltage ref.: Internal (4.096V)
Pin 1 (GPIO): 0.00V
Pin 3 (ADC3): 2.75V

Set voltage ref. to internal and 4.096V
Voltage ref.: Internal (4.096V)
Pin 1 (GPIO): 0.00V
Pin 3 (ADC3): 3.26V

Set pin 1 to ADC
Voltage ref.: Internal (4.096V)
Pin 1 (ADC1): 2.34V
Pin 3 (ADC3): 2.76V - again, voltage ref. switched to Vdd

Set pins 1 & 3 to GPIO
Voltage ref.: Internal (4.096V)
Pin 1 (GPIO): 0.00V
Pin 3 (GPIO): 0.00V

Set voltage ref. to internal and 4.096V
Voltage ref.: Internal (4.096V)
Pin 1 (GPIO): 0.00V
Pin 3 (GPIO): 0.00V

Set pin 1 to ADC
Voltage ref.: Internal (4.096V)
Pin 1 (ADC1): 2.75V - again, voltage ref. switched to Vdd
Pin 3 (GPIO): 0.00V

There's indeed something not right with the firmware.

jremmet commented 1 year ago

:( I didn't find any errata regarding this behavior, Not sure if reading SramDataSubcode.ChipSettings: and forcing to rewrite the ADC Parameters is a "good" workaround

https://github.com/vpaeder/pymcp2221/blob/master/mcp2221/__init__.py#L348-L375

.Me may use self._write(0x61)or alter https://github.com/vpaeder/pymcp2221/blob/d8a4309a2786747de34378ec92e49e788fc8302f/mcp2221/__init__.py#L330 to except a SramDataSubcode.All to avoid a double read

vpaeder commented 1 year ago

Tried to fiddle around with this a little more. Microchip's utilities are very basic and there's no official example that'd help. I've tried with 3 other packages just to see and I get the same result. Surprising that no one complained about it on Microchip's forums. I can't see for now how to avoid a read+rewrite but I'd stick it in https://github.com/vpaeder/pymcp2221/blob/d8a4309a2786747de34378ec92e49e788fc8302f/mcp2221/__init__.py#L1270 with data from self._write(0x61).

vpaeder commented 1 year ago

Ok I've pushed a fix, now I still have to see with the phony ADC values on startup. I don't think it affects the pin function when set to something else than ADC, but it's bothering me nonetheless.

vpaeder commented 1 year ago

That should be fine now. I let you check if it works on your side too.

jremmet commented 1 year ago

Works fine on my side :) Thanks. I only see that self._read_sram get called twice: https://github.com/vpaeder/pymcp2221/blob/b26b25fa7f447a2b39cf28c3826286d4652d6db4/mcp2221/__init__.py#L371-L375

vpaeder commented 1 year ago

ah yes I messed up with commit merging, oups. Fixed.