Grundfunktionen der MCU und mehrere MCUs

Aus Technische Beeinflussbarkeit der Geschmacksache Kaffee
Zur Navigation springen Zur Suche springen

Armin Rohnen, 28.12.2022

Da bei mehreren angeschlossenen MCUs nicht immer eindeutig ist, welche MCU sich an welchem COM-Port befindet ist eine MCU Identifizierung eingeführt worden.

Das Python-Programm ident.py

def ident():
     print('rpi')

muss sich auf jeder verwendeten MCU befinden. Dieses wird durch

import ident

importiert. Über den Aufruf

ident.ident()

wiurd der MCU-Erkennungscode ausgegeben. Der Erkennungscode besteht aus drei Buchstaben, welche je verwendeter MCU eindeutig vergeben werden. Die Erkennungscodes sind beim Aktuellen Programmcode abgelegt.

Jan Budnick, 02.04.2022

Während des Arbeitens an der MCU ist aufgefallen, dass diese ,,beschäftigt ist während sie auf der Basisplatine steckt, die Basisplatine jedoch nicht eingeschaltet ist.

Jan Budnick, 01.04.2022

Die Grundfunktionen der MCU sind für den jetzigen Aufbau der Laborespressomaschine implementiert. Alle Sensoren und Aktoren lassen sich ansprechen (die Heizung wurde bisher nicht getestet aber das XSSR lässt sich ansprechen). Dafür wurden zwei Funktionen in ,,main.py für die Messwertaufnahme und zyklische Weitergabe an die GUI abgelegt. Eine Beschreibung dessen kann unter dem To-Do ,,Messwerte erfassen gefunden werden.

Auf Grund der aktuellen Verkabelung haben sich die ADC-Pins für die NTCs verändert. Außerdem wurden die mcp2317 Adressen für die Ventile erfasst und in der To-Do unter ,,Schalten Magnetventile (Labor) dokumentiert.

Jan Budnick, 28.03.2022

Im Laufe der Softwareentwickelung kam es zu Performanceproblemen. Im Zuge dessen wurden sich Gedanken zur Übertragungsrate gemacht. Zur Verbindung mit der REPL wird eine Baudrate von 115200 Baud angegeben. Dabei wird impliziert, dass die Baudrate der Bitrate entspricht, dies wird von mehreren Quellen im Netz bestätigt. Dies wird jedoch nie explizit in der Dokumentation von MicroPython angegeben. Wenn mit mehr als zwei Spannungspegeln gearbeitet wird, könnten pro Baud mehrere Bits übertragen werden. Diese implizite Annahme lässt sich somit nicht zu 100% bestätigen dennoch ist es aus folgenden Gründen zu 99% sicher:

1. MATLAB® gibt für das Serialportobjekt in der Doku an, dass die Baudrate der Bitrate entspricht.

2. Die Übertragung läuft über den COM-Port. Dieser verwendet den RS232-Standard, welcher nur 2 Spannungslevel hat, dementsprechend entspricht die Baudrate der Bitrate.

3. Schaut man in die ,,Reference Manual des STM32F411RE, findet man ein Diagramm zur USART Übertragung (Die REPL verwendet das UART0) auch darin ist zu sehen, dass pro Takt ein Bit übertragen wird.

Nimmt man diese Information und verbindet sie damit, dass laut Microbit ein Startbit und ein Stopbit verwendet wird, sowie 8 Datenbits (=ein Char), ergibt sich eine Übertragungsrate von 11520 Chars/s, die Übertragung von 50 Chars wäre somit etwa 230 mal die Sekunde möglich.

Fraglich ist ob eine solch hohe Zyklusrate möglich ist, da MATLAB® für ,,readline und ,,writeline schreibt:

,,The function suspends MATLAB® execution until the terminator is reached or a timeout occurs.. Dies würde bedeuten, dass MATLAB® während der Übertragung keine Rechnungen durchführen kann und diese somit zusätzlich die Zyklusrate reduzieren.

Jan Budnick, 22.03.2022

Beide DACs wurden durch DAC.config ('Off',0, eeprom=True) auf den Startwert Null gesetzt. Die Pumpe geht nun nur noch kurz bei Stromanschluss an. Somit wurde der Workaround aus main.py entfernt.

Jan Budnick, 14.03.2022

Der DAC kann durch DAC.config('Off',0, eeprom=True) auf 0 bei Startup gesetzt werden. Sollte dies nicht funktionieren muss über eine While-Schleife in main.py das Angehen der Basisplatine abgewartet werden und der DAC dann auf 0 gesetzt werden.

Jan Budnick, 21.02.2022

DAC (Pumpe) beim Einschalten der Basisplatine auf 0 setzen. Prüfen, wie über die MCU erkannt werden kann, dass die Basisplatine bestromt wird und die beiden DACs auf 0 V Signal gesetzt werden können.

Armin Rohnen, 07.12.2021

Für die Bedienung der MCU ist eine MATLAB®-App erstellt worden. Diese dient den ersten Funktionstests von Labormaschine und Pumpenprüfstand.

Der Funktionsumfang der MATLAB®-APP ist noch nicht 100%ig zum Funktionsumfang der Basisplatine. Es funktioniert Digital-OUT (zum Ansteuern der Magnetventile), 0 bis 10 V Spannungssignal (zum Ansteuern von Dosierventil, Appoldt-Phasen-Schnitt-Regler, Getriebepumpe), Messwerte einlesen und Füllstand abfragen.

Aktuell sind die PWM-Signale noch nicht in Funktion. Gleiches gilt für das/die Flowmeter.

Auf eine aufwändige Visualisierung der MCU-Antworten wurde verzichtet. Es wird lediglich der Antworttext der MCU in einer 10Zeiligen Ausgabe dargestellt. Die APP soll den Einstieg in die Programmierung der jeweiligen Bedienungen unterstützen.

Die APP kann bei Armin Rohnen angefordert werden.

Armin Rohnen, 05.12.2021

Beim Einschalten der Basisplatine werden die beiden 0 bis 10 V Sollwertsignale auf 5 V eingestellt. Dadurch werden die daran angeschlossenen Devices (z.B. Getriebepumpe) aktiviert.

Der genaue Hintergrund für dieses Verhalten ist aktuell unbekannt. Eigentlich sollten die Sollwert-DACs den letzten DA-Wert auch über das Ausschalten hinweg beibehalten.

Work-Arround hierzu: Auf der MCU existiert ein Programm mit dem namen boot.py und enthällt die Zeilen:

import pyb
pyb.country('US')
pyb.main('main.py')

Dieses Skript muss aktiv sein. Es wird automatisch zum Start der MCU durchlaufen und ruft das Skript main.py auf.

main.py:

from machine import SoftI2C
from machine import Pin
import mcp4725

i2c_dac = SoftI2C(scl = Pin('PB10'), sda = Pin('PB9'), freq = 400000)
dac1 = mcp4725.MCP4725(i2c_dac, mcp4725.BUS_ADDRESS[1])
dac2 = mcp4725.MCP4725(i2c_dac, mcp4725.BUS_ADDRESS[0])
dac1.write(0)
dac2.write(0)

Damit wird nur kurzzeitig von den DACs ein Sollwertsignal größer 0 ausgegeben.

ACHTUNG: Die MCU startet erst dann, wenn die USB-Verbindung zum PC hergestellt worden ist.

Armin Rohnen, 27.11.2021

Den Grundfunktionen der MCU ist die Verarbeitung der Flowmetersignale hinzugefügt worden. Die PIN-Belegung des Flowmeterstecker ist 4 - GND 3 - FLOW2 2 - FLOW1 1 - 3,3 V

PIN 1 ist der PIN am Platinenrand.

Im MicroPython-Code sind einige Erweiterungen hinzugefügt worden. Dies wurde in der Schnittstellenbeschreibung aufgenommen. Die Fvnktionalität ist allerdings noch nicht sichergestellt. Hierzu stehen noch Tests mit Flowmeter aus.

Philipp Wieland, 09.11.2021 - COM-Port

Zur Erkennung des Microcontrollers STM32 auf Windows wird eine gesonderte Software benötigt. Im folgenden eine Anleitung:

Treiber runterladen über: https://www.st.com/en/development-tools/stsw-link009.html.

Zum Download muss eine E-Mail angegeben werden zu der ein Bestätigungslink gesendet wird. Nach Bestätigung startet der Download einer ZIP-Datei automatisch. Extrahieren der Datei und starten der Installation als Administrator von "stlink_winusb_install.bat". Sollte das nicht klappen, geht auch die "dpinst_amd64.exe".

Es hat funktioniert wenn im Gerätemanager folgendes vorhanden ist (gegebenenfalls erst nach Verbindung mit Microcontroller) :

- Anschlüsse (COM & Lpt) -> STMicroelectronics STLink Virtual COM Port (COM3) - USB-Geräte -> ST-Link Debug

Armin Rohnen, 01.11.2021

Für die Grundfunktionen der MCU wurde die Schnittstellendokumentation [41] erstellt.
Zum Verständniss dieser Beschreibung sind die Grundlagen wie in [40] beschreiben erforderlich.

Kurzfassung der Schnittstellenbeschreibung (Python-Code)

from machine import SoftI2C
from machine import Pin
import mcp23017
from pyb import ADC
from pyb import Timer
import mcp4725
import utime


         #
         # Callback Funktionen für die Flowmeteranschluesse
         #

def flow1_callback(Pin):
   print('flow1: '+str(utime.ticks_us()))

def flow2_callback(Pin):
   print('flow2: '+str(utime.ticks_us()))
         #
         # MCP23017 initialisieren
         #
i2c_exp = SoftI2C(scl = Pin('PB6'), sda = Pin('PB7'), freq=400000)
gpio_exp = mcp23017.MCP23017(i2c_exp, 0x20)

gpio_exp.porta.mode = 0b00000000                     # Port A und Port B als Output definieren
gpio_exp.portb.mode = 0b00000000                     # und alle PINs auf LOW
gpio_exp.porta.gpio = 0b00000000 # Zuordnung: value = 1 -> HIGH; value = 0 -> LOW gpio_exp.portb.gpio = 0b00000000 # # D01: gpio_exp.pin(1, value = 1) # D02: gpio_exp.pin(0, value = 1) # D03: gpio_exp.pin(8, value = 1) # D04: gpio_exp.pin(9, value = 1) # D05: gpio_exp.pin(10, value = 1) # D06: gpio_exp.pin(11, value = 1) # D07: gpio_exp.pin(12, value = 1) # D08: gpio_exp.pin(13, value = 1) # D09: gpio_exp.pin(14, value = 1) # D10: gpio_exp.pin(15, value = 1) # # Messkanaele definieren # T_Boiler = ADC('PA0') # NTC Boilertemperatur T_Befuellung = ADC('PA1') # NTC Boilerbefuellun T_Eingang = ADC('PA4') # Temperatur am Leitwertsensor Leitwert = ADC('PC2') # Leitwert P_Gruppe = ADC('PC1') # Druck in der Bruehgruppe Taste = ADC('PC3') # Spannung durch Tastendruck P_Boiler = ADC('PC0') # Boilerdruck T_Zwischenraum = ADC('PA5') # Glaszylinder Zwischenraumtemperatur Gewicht1 = ADC('PA6') # Waegezelle 1 T_Mischer = ADC('PA7') # Mischwassertemperatur P_Boiler_Alt = ADC('PB1') # Alternativer Boilerdruck Gewicht2 = ADC('PC2') # Waegezelle 2 # # Temperaturmesswerte ermitteln # print(str(T_Boiler.read()*3.3/4096)+','+str(T_Befuellung.read()*3.3/4096)+','+str(T_Eingang.read()*3.3/4096)+','+str(T_Zwischenraum.read()*3.3/4096)+','+str(T_Mischer.read()*3.3/4096)) # # Druecke ermitteln # print(str(P_Gruppe.read()*3.3/4096)+','+str(P_Boiler.read()*3.3/4096)+','+str(P_Boiler_Alt.read()*3.3/4096)) # # Sonstige Messwerte # print(str(Leitwert.read()*3.3/4096)+','+str(Taste.read()*3.3/4096)+','+str(Gewicht1.read()*3.3/4096)+','+str(Gewicht2.read()*3.3/4096)) # # PINs der Fuellstaende definieren und abfragen # Fuell_1 = Pin('PB15', Pin.IN, Pin.PULL_UP) Fuell_2 = Pin('PC8', Pin.IN, Pin.PULL_UP) # bei Kurzschlussdetektion ergibt Fuell_2.value() = 1 # # 0 bis 10 V Steuersignal fuer Dosierventiel und Appoldt Phasenschnittregler # die Sollsignale koennen auch anderweitig verwendet werden # i2c_dac = SoftI2C(scl = Pin('PB10'), sda = Pin('PB9'), freq=400000)
dosierventil = mcp4725.MCP4725(i2c_dac, mcp4725.BUS_ADDRESS[1])
appoldt = mcp4725.MCP4725(i2c_dac, mcp4725.BUS_ADDRESS[0])
# über appoldt.write(wert) # mit wert zwischen 0 und 4095 # wird ein Spannungswert zwischen 0 und 10 V eingestellt. # # PWM Signale # # PB8 - PWM Dampfventil, Servo 50 Hz, Timer 4 Kanal 3 # PC6 - PWM Entnahmerohr, Servo 50 Hz, Timer 3, Kanal 1 # PC7 - PWM Licht, LED 50 Hz, Timer 3, Kanal 2 # PC9 - PWM Dosierventil, Servo 50 Hz, Timer 3 Kanal 4 # # PA9 - PWM Tassenwaermer, SSR 8 Hz, Timer 1, Kanal 2 # PA11 - PWM Boiler, SSR 8 Hz, Timer 1, Kanal 4 # timer1 = Timer(1, freq = 8) # Timer und Frequenzeinstellung timer3 = Timer(3, freq = 50) timer4 = Timer(4, freq = 50) PWM_Dampf = timer4.channel(4, mode = Timer.PWM, pin = Pin('PB8')) # PWM anlegen PWM_Dampf.pulse_width_percent(0) # Duty cycle in %
PWM_Entnahme = timer3.channel(1, mode = Timer.PWM, pin = Pin('PC6')) PWM_Entnahme.pulse_width_percent(0) PWM_Licht = timer3.channel(2, mode = Timer.PWM, pin = Pin('PC7')) PWM_Licht.pulse_width_percent(0) PWM_Dosierventil = timer3.channel(4, mode = Timer.PWM, pin = Pin('PC9')) PWM_Dosierventil.pulse_width_percent(0) PWM_Tassen = timer1.channel(2, mode = Timer.PWM, pin = Pin('PA9')) PWM_Tassen.pulse_width_percent(0) PWM_Boiler = timer1.channel(4, mode = Timer.PWM, pin = Pin('PA11')) PWM_Boiler.pulse_width_percent(0) # # Flowmeter # FLOW1 = Pin('PA12', Pin.IN) # PINs definieren und ISR aktivieren FLOW1.irq(trigger=Pin.IRQ_FALLING, handler=flow1_callback) FLOW2 = Pin('PB5', Pin.IN) FLOW2.irq(trigger=Pin.IRQ_FALLING, handler=flow2_callback)

MATLAB®-Anweisungen für die MATLAB® - MicroPython Kommunikation

mcu = serialport("COM3", 115200);                                                            % die Port-Nr des COM-Ports variiert
configureTerminator(mcu, "CR=LF");
configureCallback(mcu, "terminator", @datenverarbeitung);

Wobei der Port "COM3" bzw. ein anderer über die Systemsteuerung zu ermitteln ist.

function datenverarbeitung(device, ~)                                                         % in der Funktion Datenverarbeitung muss jede
                                                                                              %  "Antwort" der MCU bearbeitet werden
     zeile = readline(device)
     (und weitere Datenverarbeitung)
end