Spannungsmessung mit PCF8591 am Pico

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Achim Klein
User
Beiträge: 41
Registriert: Dienstag 21. Februar 2023, 13:57

Hallo
Mit einem PCF8591 messe ich 4 Spannungen die ich mit je einem Poti zwschen 0 und 5V regeln kann. Über den I2C Bus übertrage ich diese Werte an den Raspberry Pi Pico. Verbindung zwischen 3,3V und 5V mit einem Pegelwandler. Der Pico zeigt mir aber nur die Werte 64, 128 und 192 wenn ich die Potis von GND bis Vcc drehe. Keinerlei Zwischenwerte. es springt immer nur auf diese Werte. Verwende diese Programm dazu:

Code: Alles auswählen

#
# PCF8591 Analog GPIO Port Expand
#
# AUTHOR:  Renzo Mischianti
# Website: www.mischianti.org
# VERSION: 0.0.1
#
# Description:
# This script read one channel for time of pcf8591
#
#            +---------------+
#  AIN0 (1)  | * 1    U   16 |  VDD
#  AIN1 (2)  | * 2        15 |  AOUT
#  AIN2 (3)  | * 3        14 |  VREF
#  AIN3 (4)  | * 4        13 |  AGND
#    A0 (5)  | * 5        12 |  EXT
#    A1 (6)  | * 6        11 |  OSC
#    A2 (7)  | * 7        10 |  SCL
#   VSS (8)  | * 8         9 |  SDA
#            +---------------+
#
# AIN0: Analog input channel 0
# AIN1: Analog input channel 1
# AIN2: Analog input channel 2
# AIN3: Analog input channel 3
# A0: Address input 0
# A1: Address input 1
# A2: Address input 2
# VSS: Ground
# SDA: Serial data line (I2C)
# SCL: Serial clock line (I2C)
# OSC: Oscillator output
# EXT: External trigger input
# AGND: Analog ground
# VREF: Voltage reference input
# AOUT: Analog output
# VDD: Power supply
#
# Porting of PCF8591 library for Arduino
# https://www.mischianti.org/2019/01/03/pcf8591-i2c-analog-i-o-expander/
#

from machine import I2C, Pin
from PCF8591 import PCF8591

# Initialize the I2C bus
i2c = I2C(0, scl=Pin(21), sda=Pin(20))  # Adjust the pins and frequency as needed for your board

# Initialize the PCF8591
pcf8591 = PCF8591(0x48, i2c)  # Adjust the address if needed
if pcf8591.begin():
    print("PCF8591 found")

print("AIN0 ", pcf8591.analog_read(PCF8591.AIN0))  # You can use PCF8591.CHANNEL_0
print("AIN1 ", pcf8591.analog_read(PCF8591.AIN1))
print("AIN2 ", pcf8591.analog_read(PCF8591.AIN2))
print("AIN3 ", pcf8591.analog_read(PCF8591.AIN3))
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

Code & Treiber sehen ok aus. Also Datenblatt wälzen & Oszi/Logic Analyzer abklemmen. Der Fehler sieht so aus, als ob nur die obersten 2 Bit - 128, 64 - zurückgeben werden.
Achim Klein
User
Beiträge: 41
Registriert: Dienstag 21. Februar 2023, 13:57

Leider kann ich die Datei auf dem Pico hier nicht komplett reinstellen, keine Ahnung was ich da falsch mache. Egal machen wir es stückweise. Der Code dazu:

Code: Alles auswählen

import utime
from machine import I2C, Pin

AIN0 = CHANNEL0 = 0b00000000
AIN1 = CHANNEL1 = 0b00000001
AIN2 = CHANNEL2 = 0b00000010
AIN3 = CHANNEL3 = 0b00000011


class PCF8591:
    AIN0 = CHANNEL0 = 0b00000000
    AIN1 = CHANNEL1 = 0b00000001
    AIN2 = CHANNEL2 = 0b00000010
    AIN3 = CHANNEL3 = 0b00000011

    AUTOINCREMENT_READ = 0b00000100

    SINGLE_ENDED_INPUT = 0b00000000
    TREE_DIFFERENTIAL_INPUT = 0b00010000
    TWO_SINGLE_ONE_DIFFERENTIAL_INPUT = 0b00100000
    TWO_DIFFERENTIAL_INPUT = 0b00110000

    ENABLE_OUTPUT = 0b01000000
    DISABLE_OUTPUT = 0b00000000

    OUTPUT_MASK = 0b01000000

    def __init__(self, address, i2c=None, i2c_id=0, sda=None, scl=None):
        if i2c:
            self._i2c = i2c
        elif sda and scl:
            self._i2c = I2C(i2c_id, scl=Pin(scl), sda=Pin(sda))
        else:
            raise ValueError('Either i2c or sda and scl must be provided')

        self._address = address
        self._output_status = self.DISABLE_OUTPUT  # default

    def begin(self):
        if self._i2c.scan().count(self._address) == 0:
            # raise OSError('PCF8591 not found at I2C address {:#x}'.format(self._address))
            return False
        else:
            return True

    def analog_read_all(self, read_type=SINGLE_ENDED_INPUT):
        operation = self.AUTOINCREMENT_READ | read_type | (self._output_status & self.OUTPUT_MASK)
        self._i2c.writeto(self._address, bytearray([operation]))
        utime.sleep_ms(1)
        # data = self._i2c.readfrom(self._address, 5)
        data = []
        self._i2c.readfrom(self._address, 1)
        data.append(int.from_bytes(self._i2c.readfrom(self._address, 1), 'big'))
        data.append(int.from_bytes(self._i2c.readfrom(self._address, 1), 'big'))
        data.append(int.from_bytes(self._i2c.readfrom(self._address, 1), 'big'))
        data.append(int.from_bytes(self._i2c.readfrom(self._address, 1), 'big'))

        return data[0], data[1], data[2], data[3]

    def analog_read(self, channel, read_type=SINGLE_ENDED_INPUT):
        operation = channel | read_type | (self._output_status & self.OUTPUT_MASK)
        self._i2c.writeto(self._address, bytearray([operation]))
        utime.sleep_ms(1)
        data = self._i2c.readfrom(self._address, 2)
        return data[1]

    def voltage_read(self, channel, reference_voltage=3.3):
        voltage_ref = reference_voltage
        ana = self.analog_read(channel, self.SINGLE_ENDED_INPUT)
        return ana * voltage_ref / 255

    def voltage_write(self, value, reference_voltage=3.3):
        ana = value * 255 / reference_voltage
        self.analog_write(ana)

    def analog_write(self, value):
        if value > 255 or value < 0:
            Exception('Value must be between 0 and 255')

        self._output_status = self.ENABLE_OUTPUT

        self._i2c.writeto(self._address, bytearray([self.ENABLE_OUTPUT, value]))

    def disable_output(self):
        self._output_status = self.DISABLE_OUTPUT

        self._i2c.writeto(self._address, bytearray([self.DISABLE_OUTPUT]))
Habe das DB angesehen und versucht das mit den 2 Bit zu finden, leider ohne Erfolg. Könntest du mal schauen?
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich sehe in dem Datenblatt nichts, was das zumindest offensichtlich erklärt. Da musst du eben tiefer einsteigen. Die exakte Kommunikation aufzeichnen und analysieren. Und mit dem Datenblatt abgleichen.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum sind die CHANNEL-Konstanten doppelt im Code?
`count` benutzt man, wenn man wissen will, wie oft ein Element vorkommt, `in` wenn man wissen will, ob.
Wenn man etwas mehrfach machen will, benutzt man eine Schleife, und kopiert den Code nicht vier mal.
Wenn man nur ein Byte liest, braucht es kein from_bytes, denn ein Byte hat keine Byte-Ordnung.
Warum wird `reference_voltage` in `voltage_ref` umbenannt?
Eine Exception sollte man auch raise(n).

Code: Alles auswählen

import utime
from machine import I2C, Pin

class PCF8591:
    AIN0 = CHANNEL0 = 0b00000000
    AIN1 = CHANNEL1 = 0b00000001
    AIN2 = CHANNEL2 = 0b00000010
    AIN3 = CHANNEL3 = 0b00000011

    AUTOINCREMENT_READ = 0b00000100

    SINGLE_ENDED_INPUT = 0b00000000
    TREE_DIFFERENTIAL_INPUT = 0b00010000
    TWO_SINGLE_ONE_DIFFERENTIAL_INPUT = 0b00100000
    TWO_DIFFERENTIAL_INPUT = 0b00110000

    ENABLE_OUTPUT = 0b01000000
    DISABLE_OUTPUT = 0b00000000

    OUTPUT_MASK = 0b01000000

    def __init__(self, address, i2c=None, i2c_id=0, sda=None, scl=None):
        if i2c:
            self._i2c = i2c
        elif sda and scl:
            self._i2c = I2C(i2c_id, scl=Pin(scl), sda=Pin(sda))
        else:
            raise ValueError('Either i2c or sda and scl must be provided')

        self._address = address
        self._output_status = self.DISABLE_OUTPUT  # default

    def begin(self):
        return self._address in self._i2c.scan()

    def analog_read_count(self, channel, count, read_type=SINGLE_ENDED_INPUT):
        operation = channel | read_type | (self._output_status & self.OUTPUT_MASK)
        self._i2c.writeto(self._address, bytearray([operation]))
        utime.sleep_ms(1)
        data = self._i2c.readfrom(self._address, 1 + count)
        return tuple(data[1:])

    def analog_read_all(self, read_type=SINGLE_ENDED_INPUT):
        return self.analog_read_count(self.AUTOINCREMENT_READ, 4, read_type)

    def analog_read(self, channel, read_type=SINGLE_ENDED_INPUT):
        return self.analog_read_count(channel, 1, read_type)

    def analog_write(self, value):
        if value > 255 or value < 0:
            raise ValueError('Value must be between 0 and 255')

        self._output_status = self.ENABLE_OUTPUT
        self._i2c.writeto(self._address, bytearray([self.ENABLE_OUTPUT, value]))

    def disable_output(self):
        self._output_status = self.DISABLE_OUTPUT
        self._i2c.writeto(self._address, bytearray([self.DISABLE_OUTPUT]))

    def voltage_read(self, channel, reference_voltage=3.3):
        ana = self.analog_read(channel, self.SINGLE_ENDED_INPUT)
        return ana * reference_voltage / 255

    def voltage_write(self, value, reference_voltage=3.3):
        ana = value * 255 / reference_voltage
        self.analog_write(ana)
Achim Klein
User
Beiträge: 41
Registriert: Dienstag 21. Februar 2023, 13:57

Habe das doppelte raus genommen, ist alles so wie vorher. Es wird beim durchdrehen des Potis 64, 128 und 192 angezeigt. Alle anderen werte fehlen.
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist auch nicht verwunderlich. Sirius3 Anmerkungen haben keinen semantischen Unterschied gemacht. Und sollten a den Autor der Bibliothek gerichtet werden. Darum gilt weiter, was ich gesagt habe: mehr Einsichten erzeugen. Mit Oszilloskop oder Logik Analyzer.
Antworten