Post by jar (February 11, 2022 at 10:09 AM ).
The post was deleted, no further information is available.
ESp32 Signal Generator? Schau mal ob du hier fündig wirst!
irgendwas fehlt aber....
bin gerade dabei den Code endlich zu korrigieren!
// Der Sketch verwendet 252441 Bytes (19%) des Programmspeicherplatzes. Das Maximum sind 1310720 Bytes.
// Globale Variablen verwenden 16044 Bytes (4%) des dynamischen Speichers, 311636 Bytes für lokale Variablen // verbleiben. Das Maximum sind 327680 Bytes.
// reservierte Namen fuer Variablen nerven mich, deswegen
// step zu _step ersetzt
// monitor zu _monitor ersetzt
// size zu _size ersetzt
// div zu _div ersetzt
// nur mit mode funktioniert das nicht, extern definiert?
/* Funktionsgenerator fuer Sinus, Dreieck und Rechteck Signale
* Einstellbare Frequenz 20 Hz bis 20 KHz
* Fuer Dreieck und Rechteck einstellbares Tastverhaeltnis 0 bis 100%
*/
// Bibliotheken zum direkten Zugriff auf Steuerregister des ESP32
// Bild 3: Rueckseite ESP32 D1 R32 mit 10 kOhm-Widerstand zwischen GPIO12 und GND
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "soc/rtc.h"
// Bibliotheken zur Verwendung des Digital zu Analog Konverters und fuer den I2S-Bus
#include "driver/dac.h"
#include "driver/i2s.h"
// Bibliothek fuer das LCD Display
#include <LiquidCrystal.h>
#define SINFAKT 127.0 //gemessen fuer Schrittweite = 1 und kein Vorteiler (8.3MHz)
#define SIGNALOUT 26 //Pin fuer die Signalausgabe
// LCD Pins
#define PIN_RS 12 //Registerselect 0=Befehle 1=Daten
#define PIN_EN 13 //Enable Takt zum Schreiben
#define PIN_D4 17 //Datenbit
#define PIN_D5 16 //Datenbit
#define PIN_D6 27 //Datenbit
#define PIN_D7 14 //Datenbit
#define PIN_BL 5 //Hintergrundbeleuchtung 0=aus
// Analog PIN fuer Tasten
#define KEYS A12
// Spannungsteiler fuer Tasten
#define Rv 2000 //Vorwiderstand
#define R3 3900 //Schutzwiderstand fuer maximal 3.3 V
#define Rp 1000 //Widerstand GPIO2 gegen Masse
#define Rr 0 //Spannungsteiler bei gedrueckter RIGHT Taste
#define Ru 330 //Spannungsteiler bei gedrueckter UP Taste
#define Rd 950 //Spannungsteiler bei gedrueckter DOWN Taste
#define Rl 1950 //Spannungsteiler bei gedrueckter LEFT Taste
#define Rs 5250 //Spannungsteiler bei gedrueckter SELECT Taste
// Tasten Codes
#define NONE 0
#define LEFT 1
#define RIGHT 2
#define UP 3
#define DOWN 4
#define SELECT 5
// Betriebsarten
#define MSINUS 0
#define MRECTANGLE 1
#define MTRIANGLE 2
// Aenderungstypen
#define EMODE 0
#define EFREQUENCY 1
#define ERATIO 2
// Init I2C LCD
LiquidCrystal lcd(PIN_RS, PIN_EN, PIN_D4, PIN_D5, PIN_D6, PIN_D7);
// Variablen zum Speichern der Schwellwerte fuer Taster
uint16_t Ur, Uu, Ud, Ul, Us;
// Buffer zum Erstellen der Dreieckfunktion
uint32_t buf[128];
// Einstellwerte fuer Kurvenform, Frequenz und Tastverhaeltnis
int8_t mode = MSINUS; //0=Sinus, 1=Rechteck, 2=Dreieck
float frequency = 1000; //20 bis 200000 Hz
int8_t ratio = 50; //Tastverhaeltnis 0 bis 100%
int8_t edit = EMODE; //was wird veraendert 0=Mode 1=Frequenz2=Tastverhaeltnis
uint32_t tic; //Fuer Wartezeit
int8_t lastKey = 0; //zuletzt ermittelte Taste oder 0 wenn keine
uint16_t _step = 0; //Schrittweite fuer Frequenzerhoehung
float ftmp; //Variable zum Speichern der Frequenz waehrend der Einstellung
int16_t rtmp; //Variable zum Speichern des Tastverhaeltnis waehrend der Einstellung
int8_t mtmp; //Variable zum Speichern der Betriebsart waehrend der Einstellung
// Flag Ist wahr, wenn die Initialisierung bereits erfolgte
bool initDone = false;
// Konfiguration fuer den I2S Bus
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN), //Betriebsart
.sample_rate = 100000, //Abtastrate
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // der DAC verwendet nur 8 Bit des MSB
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // Kanalformat ESP32 unterstuetzt nur Stereo
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S_MSB,
// Standard Format fuer I2S
.intr_alloc_flags = 0, // Standard Interrupt
.dma_buf_count = 2, //Anzahl der FIFO Buffer
.dma_buf_len = 32, //Groeße der FIFO Buffer
.use_apll = 0 //Taktquelle
};
void fillBuffer(uint8_t up, uint8_t sz) { // Buffer fuer Dreieck Wellenform fuellen
// Parameter up ist die Dauer fuer den Anstieg in Prozent
// Parameter sz gibt die Buffergroeße fuer eine Periode an
// es werden die Werte fuer eine Periode in den Buffer geschrieben
uint8_t down; //Zeit fuer die fallende Flanke in %
uint32_t sample; //32Bit Datenwort (I2S benoetigt zwei Kanaele mit je 16 Bit
float du,dd,val; //Hilfsvariablen
down=100-up;
// Anzahl der Schritte fuer Anstieg und Abfall berechnen
uint16_t stup = round(1.0*sz/100 * up);
uint16_t stdwn = round(1.0*sz/100*down);
uint16_t i;
if ((stup + stdwn) < sz) stup++; //Ausgleich eventueller Rundungsfehler
// Amplitudenaenderung pro Schritt fuer Anstieg und Abfall
du = 256.0/stup;
dd = 256.0/stdwn;
// fuellen des Buffers
val = 0; //Anstieg beginnt mit 0
for (i=0; i<stup; i++) {
sample = val;
sample = sample << 8; //Byte in das hoeherwertige Byte verschieben
buf[i]=sample;
val = val+du; //Wert erhoehen
} // for (i=0; i<stup; i++)
val=255; // Abfallende Flanke beginnt mit Maximalwert
// Rest wie bei der ansteigenden Flanke
for (i=0; i<stdwn; i++) {
sample = val;
sample = sample << 8;
buf[i+stup]=sample;
val = val-dd;
} // for (i=0; i<stdwn; i++)
} // void fillBuffer(uint8_t up, uint8_t sz)
void stopAll() { // Alle Ausgaenge stoppen
ledcDetachPin(SIGNALOUT);
i2s_driver_uninstall((i2s_port_t)0);
dac_output_disable(DAC_CHANNEL_2);
dac_i2s_disable();
initDone=false;
} // void stopAll()
void startRectangle() { //Kurvenform Rechteck starten
//Pin fuer Signalausgang zuweisen
ledcAttachPin(SIGNALOUT,1 );
initDone=true;
} // void startRectangle()
void rectangleSetFrequency(double frequency,uint8_t ratio) { //Frequenz fuer Rechteck setzen mit entsprechendem Tastverhaeltnis
ledcSetup(1,frequency,7); //Wir nutzen die LEDC Funktion mit 7 bit Aufloesung
ledcWrite(1,127.0*ratio/100); //Berechnung der Schrittanzahl fuer Zustand = 1
} // void rectangleSetFrequency(double frequency,uint8_t ratio)
void startTriangle() { // Dreiecksignal starten
i2s_set_pin((i2s_port_t)0, NULL); //I2S wird mit dem DAC genutzt
initDone=true;
} // void startTriangle()
double triangleSetFrequency(double frequency,uint8_t ratio) { // Frequenz fuer Dreieck setzen mit entsprechendem Tastverhaeltnis
int _size=64;
// zuerst wird die geeignete Buffergroeße ermittelt
//damit die Ausgabe funktionier muss die I2S Abtastrate zwischen
//5200 und 650000 liegen
if (frequency<5000) {
_size = 64;
} // if (frequency<5000)
else if (frequency<10000) { // _size = 32;
_size = 32;
} //else if (frequency<10000)
else if (frequency<20000) { // _size = 16;
_size = 16;
} // else if (frequency<20000)
else { // _size = 8;
_size = 8;
}
//Abtastrate muss in einer Periode beide Buffer ausgeben
uint32_t rate = frequency * 2 * _size;
//Die Abtastrate darf nur innerhalb der Grenzwerte liegen
if (rate < 5200) rate = 5200;
if (rate > 650000) rate = 650000;
//wirklichen Frequenzwert setzen
frequency = rate / 2 / _size;
//I2S Treiber entfernen
i2s_driver_uninstall((i2s_port_t)0);
//Konfiguration anpassen
i2s_config.sample_rate = rate;
i2s_config.dma_buf_len = _size;
//und mit der neuen Konfiguration installieren
i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
//Abtastrate einstellen
i2s_set_sample_rates((i2s_port_t)0, rate);
//Buffer fuellen
fillBuffer(ratio,_size*2);
//und einmal ausgeben
i2s_write_bytes((i2s_port_t)0, (const char *)&buf, _size*8, 100);
return frequency;
} // double triangleSetFrequency(double frequency,uint8_t ratio)
void startSinus() { // Sinusausgabe vorbereiten
// Ausgang fuer Signalausgang freigeben
dac_output_enable(DAC_CHANNEL_2);
// Sinusgenerator aktivieren
SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_SW_TONE_EN);
// Ausgabe auf Kanal 1 starten
SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN2_M);
// Vorzeichenbit umkehren
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV2, 2,
SENS_DAC_INV2_S);
initDone=true;
} // void startSinus()
double sinusSetFrequency(double frequency) { //Frequenz fuer Sinus setzen
// Formel f = s * SINFAKT / v
// s sind die Schritte pro Taktimpuls
// v ist der Vorteiler fuer den 8MHz Takt
// Es gibt 8 Vorteiler von 1 bis 1/8 um die Kombination Vorteiler und
// Schrittanzahl zu finden, testen wir alle acht Vorteiler Varianten
// Die Kombination mit der geringsten Frequenzabweichung wird gewaehlt
double f,delta,delta_min = 999999999.0;
uint16_t divi=0, _step=1, s;
uint8_t clk_8m_div = 0;//0 bis 7
for(uint8_t _div = 1; _div<9; _div++) {
s=round(frequency * _div/SINFAKT);
if ((s>0) && ((_div == 1) || (s<1024))) {
f= SINFAKT*s/_div;
// Serial.print(f); Serial.print(" ");
// Serial.print(_div); Serial.print(" ");
// Serial.println(s);
//
delta = abs(f-frequency);
if(delta < delta_min) { //Abweichung geringer -> aktuelle Werte merken
_step = s; divi = _div-1; delta_min = delta;
} // if(delta < delta_min)
} // if ((s>0) && ((_div == 1) || (s<1024)))
} // for(uint8_t _div = 1; _div<9; _div++)
//wirklichen Frequenzwert setzen
frequency = SINFAKT * _step / (divi+1);
// Vorteiler einstellen
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, divi);
// Schritte pro Taktimpuls einstellen
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL1_REG, SENS_SW_FSTEP, _step, SENS_SW_FSTEP_S);
return frequency;
} // double sinusSetFrequency(double frequency)
void controlGenerator() { // //Einstellungsaenderungen durchfuehren
switch(mode) {
case MSINUS: if (!initDone) startSinus();
frequency = sinusSetFrequency(frequency);
break;
case MRECTANGLE : if (!initDone) startRectangle();
rectangleSetFrequency(frequency,ratio);
break;
case MTRIANGLE : if (!initDone) startTriangle();
frequency = triangleSetFrequency(frequency,ratio);
break;
} // switch(mode)
} // void controlGenerator()
void displayValues(boolean _monitor) { //Anzeige aktualisieren
// Wenn _monitor wahr ist, erfolgt die Ausgabe
// auch auf die serielle Schnittstelle
char buf[15];
//aktuelle Werte ausgeben
String ba;
switch(mode) {
case MSINUS: ba="Sinus "; break;
case MRECTANGLE: ba="Rechteck "; break;
case MTRIANGLE: ba="Dreieck "; break;
} // switch(mode)
//Betriebsart ausgeben
lcd.setCursor(0,0);
lcd.print(" ");
lcd.print(ba);
if(_monitor) {
Serial.println("**************** Eingestellte Werte *************************");
Serial.print("Betriebsart = "); Serial.println(ba);
} // if(_monitor)
if(frequency < 1000) { //Frequenz je nach Wert als Hz oder kHz
sprintf(buf,"%6.2f Hz",frequency);
}
else { // if!(frequency < 1000)
sprintf(buf,"%6.2fkHz",frequency/1000);
} // else { // if!(frequency < 1000)
//Frequenz ausgeben
lcd.setCursor(0,1);
lcd.print(" F"); lcd.print(buf);
if(_monitor) {
Serial.print("Frequenz = "); Serial.println(buf);
} // if(_monitor)
sprintf(buf,"%2i%%",ratio);
// Tastverhaeltnis ausgeben
lcd.setCursor(11,1);
lcd.print(" T"); lcd.print(buf);
if(_monitor) {
Serial.print("Tastverhaeltnis = "); Serial.println(buf);
Serial.println();
} // if(_monitor)
switch(edit) { // je nach Edit-Mode Pfeilzeichen ausgeben
case EMODE: lcd.setCursor(0,0); break;
case EFREQUENCY: lcd.setCursor(0,1); break;
case ERATIO: lcd.setCursor(11,1); break;
} // switch(edit)
lcd.print(char(126));
} // void displayValues(boolean _monitor)
void changeEdit(boolean up) { // Edit Mode aendern mit UP und DOWN Taste
// je nach Richtung Schritte positiv oder negativ
int s = up?1:-1;
edit += s;
// am Ende wieder auf Anfang springen
if (edit < 0) edit = 2;
if (edit > 2) edit = 0;
// die aktuellen Werte in die temporaeren Werte
// fuer die aenderung kopieren
ftmp = frequency;
rtmp = ratio;
mtmp = mode;
// Geaenderte Editmode ausgeben
Serial.print("Mode = ");Serial.println(mode);
// Anzeige aktualisieren ohne Ausgabe auf serielle
// Schnittstelle
displayValues(false);
} // void changeEdit(boolean up)
void changeMode(boolean up) { // Betriebsart aendern mit RIGHT und LEFT Taste
// je nach Richtung Schritte positiv oder negativ
int s = up?1:-1;
// temporaere Betriebsart aendern
mtmp += s;
// Wenn das Ende erreicht wird wieder auf Anfang springen
if (mtmp < 0) mtmp = 2;
if (mtmp > 2) mtmp = 0;
// Geaenderte Betriebsart am Display anzeigen
lcd.setCursor(1,0);
switch(mtmp) {
case 0: lcd.print("Sinus "); break;
case 1: lcd.print("Rechteck "); break;
case 2: lcd.print("Dreieck "); break;
} // switch(mtmp)
} // void changeMode(boolean up)
void changeFrequency(boolean up) { //Frequenz aendern mit RIGHT und LEFT Taste
// war die Taste vorher nicht gedrueckt, wird die Schrittweite auf 1 gesetzt
// waehrend die Taste gedrueckt bleibt, wird die Schrittweite laufend
// verdoppelt bis eine maximale Schrittweite erreicht wurde
_step = (lastKey == NONE)?1:_step*2;
if(_step > 1024) _step = 1024;
// Richtungsfaktor bestimmen
int16_t s = up?1:-1;
// temporaere Frequenz aendern
ftmp = ftmp+s*_step;
// auf Minimal- und Maximalwerte pruefen
if(ftmp < 20) ftmp=20;
if (ftmp > 20000) ftmp = 20000;
char buf[15];
// Fuer die Anzeige Hz oder kHz
if(ftmp > 999) {
sprintf(buf,"%6.2fkHz",ftmp/1000.0);
}
else { // if !(ftmp > 999)
sprintf(buf,"%6.2f Hz",ftmp*1.0);
}
// Geaenderte Frequenz am Display anzeigen
lcd.setCursor(2,1);
lcd.print(buf);
} // void changeFrequency(boolean up)
void changeRatio(boolean up) { // //Tastverhaeltnis aendern mit RIGHT und LEFT Taste
// Richtung festlegen
int8_t jx = up?1:-1;
// Temporaeres Tastverhaeltnis aendern
rtmp = rtmp+jx;
// auf Minimal- und Maximalwerte pruefen
if (rtmp < 0) rtmp=0;
if (rtmp > 100) rtmp = 100;
char buf[15];
// Geaendertes Tastverhaeltnis am Display anzeigen
sprintf(buf,"%2i%%",rtmp);
lcd.setCursor(13,1);
lcd.print(buf);
} // void changeRatio(boolean up)
void setValues() { // der Funktionsgenerator wird auf die geaenderte Einstellung gesetzt
// die temporaeren Werte werden in die Aktuellen Werte uebernommen
Serial.print("Set values edit = "); Serial.println(edit);
switch(edit) {
case EMODE: stopAll(); mode = mtmp; break;
case EFREQUENCY: frequency = ftmp; break;
case ERATIO: ratio = rtmp; break;
} // switch(edit)
// Funktionsgenerator selber aendern
controlGenerator();
displayValues(true);
} // void setValues()
void handleKeys() { // Tastaturspannung einlesen und auswerten
//Tastaturspannung einlesen
int x=analogRead(KEYS);
uint8_t key = NONE;
if (x < Ur) { key = RIGHT; }
else if (x < Uu) { key = UP; }
else if (x < Ud){ key = DOWN; }
else if (x < Ul){ key = LEFT; }
else if (x < Us){ key = SELECT; }
else {key = NONE;}
if(((key == UP) || (key == DOWN)) && (lastKey == NONE)) changeEdit(key== DOWN);
if((key == LEFT) || (key == RIGHT)) {
switch(edit) {
case EMODE: if (lastKey == NONE) changeMode(key == RIGHT);
break;
case EFREQUENCY: changeFrequency(key == RIGHT);
break;
case ERATIO: changeRatio(key == RIGHT);
break;
} // switch(edit)
} // if ((key == LEFT) || (key == RIGHT))
if ((key == SELECT) && (lastKey == NONE)) setValues();
lastKey = key;
tic = millis();
} // void handleKeys()
void setup() { // Serielle Schnittstelle aktivieren und
// Defaulteinstellungen 1kHz Sinus setzen
// Schwellwerte fuer Tastatur festlegen
Serial.begin(115200);
controlGenerator();
lcd.begin(16,2); // initialisiere LCD I2C Anzeige
lcd.clear();
displayValues(true);
tic = millis();
Serial.print("Kommando M,F,A,T,O : ");
pinMode(2,INPUT);
// Schwellwerte fuer Taster berechnen
// Diese Berechnung ist notwendig, da die Toleranzen sehr
// gering sind, und die Schwellwerte von der Betriebsspannung
// abhaengen.
// Versorgungsspannung ohne Taste ermitteln
int x=analogRead(A12);
float Rin = R3 * Rp / (R3 + Rp);
float Ub = x / Rin * (Rin + Rv);
// Schwellspannungen ermitteln
float Uup,Udn,Ulf,Usl,Rtmp;
// Mittelwert fuer UP Taste
Rtmp = Rin * Ru / (Rin + Ru);
Uup = Ub * Rtmp / (Rtmp + Rv);
// Mittelwert fuer DOWN Taste
Rtmp = Rin * Rd / (Rin + Rd);
Udn = Ub * Rtmp / (Rtmp + Rv);
// Mittelwert fuer LEFT Taste
Rtmp = Rin * Rl / (Rin + Rl);
Ulf = Ub * Rtmp / (Rtmp + Rv);
// Mittelwert fuer Select Taste
Rtmp = Rin * Rs / (Rin + Rs);
Usl = Ub * Rtmp / (Rtmp + Rv);
//eigentliche Schwellwerte berechnen
//immer in die Mitte zwischen zwei Mittelwerten
Ur = Uup/2;
Uu = Uup + (Udn - Uup) / 2;
Ud = Udn + (Ulf - Udn) / 2;
Ul = Ulf + (Usl - Ulf) / 2;
Us = Usl + (x-Usl) /2;
// Schwellwerte auf die serielle Schnittstelle ausgeben
Serial.printf("Schwellwerte: right %i, up %i, down %i, left %i, select%i\n",Ur,Uu,Ud,Ul,Us);
} // void setup()
void loop() {
if((millis()-tic) > 200) handleKeys();
//Serielle Schnittstelle abfragen
if (Serial.available() > 0) {
//Befehl von der Schnittstelle einlesen
String inp = Serial.readStringUntil('\n');
//und zur Kontrolle ausgeben
Serial.println(inp);
char cmd = inp[0]; //erstes Zeichen ist das Kommando
if((cmd == 'M') || (cmd == 'm')) { //war das Zeichen 'M' wird die Betriebsart eingestellt
char newMode = inp[1]; //zweites Zeichen ist die Betriebsart
uint8_t nm=0;
switch(newMode) {
case 's':
case 'S': nm=0;
break;
case 'r':
case 'R': nm=1;
break;
case 't':
case 'T': nm=2;
break;
} // switch (newMode)
if(nm != mode) { //Nur wenn eine Aenderung vorliegt, muss was getan werden
stopAll();
mode=nm;
controlGenerator();
} // if(nm != mode)
} // if((cmd == 'M') || (cmd == 'm'))
else { // if !((cmd == 'M') || (cmd == 'm')) // bei den anderen Befehlen folgt ein Zahlenwert
String dat = inp.substring(1);
switch(cmd) { //je nach Befehl, werden die Daten geaendert
case 'F' : //Frequenz
case 'f' :frequency = dat.toDouble();
break;
case 'T' : //Tastverhaeltnis
case 't' :ratio = dat.toInt();
break;
} // switch(cmd)
if(ratio > 100) ratio = 100; // Grenzwerte werden ueberprueft
if(frequency < 20) frequency = 20;
if(frequency > 20000) frequency = 20000;
controlGenerator();
} // else { // if !((cmd == 'M') || (cmd == 'm'))
displayValues(true); //aktuelle Werte ausgeben
Serial.print("Kommando M,F,T : ");
} // if (Serial.available() > 0)
} // void loop()
// void loop()
Display More
irgendwas fehlt aber....
bin gerade dabei den Code endlich zu korrigieren!C Display More// Der Sketch verwendet 252441 Bytes (19%) des Programmspeicherplatzes. Das Maximum sind 1310720 Bytes. // Globale Variablen verwenden 16044 Bytes (4%) des dynamischen Speichers, 311636 Bytes für lokale Variablen // verbleiben. Das Maximum sind 327680 Bytes. // reservierte Namen fuer Variablen nerven mich, deswegen // step zu _step ersetzt // monitor zu _monitor ersetzt // size zu _size ersetzt // div zu _div ersetzt // nur mit mode funktioniert das nicht, extern definiert? /* Funktionsgenerator fuer Sinus, Dreieck und Rechteck Signale * Einstellbare Frequenz 20 Hz bis 20 KHz * Fuer Dreieck und Rechteck einstellbares Tastverhaeltnis 0 bis 100% */ // Bibliotheken zum direkten Zugriff auf Steuerregister des ESP32 // Bild 3: Rueckseite ESP32 D1 R32 mit 10 kOhm-Widerstand zwischen GPIO12 und GND #include "soc/rtc_cntl_reg.h" #include "soc/sens_reg.h" #include "soc/rtc.h" // Bibliotheken zur Verwendung des Digital zu Analog Konverters und fuer den I2S-Bus #include "driver/dac.h" #include "driver/i2s.h" // Bibliothek fuer das LCD Display #include <LiquidCrystal.h> #define SINFAKT 127.0 //gemessen fuer Schrittweite = 1 und kein Vorteiler (8.3MHz) #define SIGNALOUT 26 //Pin fuer die Signalausgabe // LCD Pins #define PIN_RS 12 //Registerselect 0=Befehle 1=Daten #define PIN_EN 13 //Enable Takt zum Schreiben #define PIN_D4 17 //Datenbit #define PIN_D5 16 //Datenbit #define PIN_D6 27 //Datenbit #define PIN_D7 14 //Datenbit #define PIN_BL 5 //Hintergrundbeleuchtung 0=aus // Analog PIN fuer Tasten #define KEYS A12 // Spannungsteiler fuer Tasten #define Rv 2000 //Vorwiderstand #define R3 3900 //Schutzwiderstand fuer maximal 3.3 V #define Rp 1000 //Widerstand GPIO2 gegen Masse #define Rr 0 //Spannungsteiler bei gedrueckter RIGHT Taste #define Ru 330 //Spannungsteiler bei gedrueckter UP Taste #define Rd 950 //Spannungsteiler bei gedrueckter DOWN Taste #define Rl 1950 //Spannungsteiler bei gedrueckter LEFT Taste #define Rs 5250 //Spannungsteiler bei gedrueckter SELECT Taste // Tasten Codes #define NONE 0 #define LEFT 1 #define RIGHT 2 #define UP 3 #define DOWN 4 #define SELECT 5 // Betriebsarten #define MSINUS 0 #define MRECTANGLE 1 #define MTRIANGLE 2 // Aenderungstypen #define EMODE 0 #define EFREQUENCY 1 #define ERATIO 2 // Init I2C LCD LiquidCrystal lcd(PIN_RS, PIN_EN, PIN_D4, PIN_D5, PIN_D6, PIN_D7); // Variablen zum Speichern der Schwellwerte fuer Taster uint16_t Ur, Uu, Ud, Ul, Us; // Buffer zum Erstellen der Dreieckfunktion uint32_t buf[128]; // Einstellwerte fuer Kurvenform, Frequenz und Tastverhaeltnis int8_t mode = MSINUS; //0=Sinus, 1=Rechteck, 2=Dreieck float frequency = 1000; //20 bis 200000 Hz int8_t ratio = 50; //Tastverhaeltnis 0 bis 100% int8_t edit = EMODE; //was wird veraendert 0=Mode 1=Frequenz2=Tastverhaeltnis uint32_t tic; //Fuer Wartezeit int8_t lastKey = 0; //zuletzt ermittelte Taste oder 0 wenn keine uint16_t _step = 0; //Schrittweite fuer Frequenzerhoehung float ftmp; //Variable zum Speichern der Frequenz waehrend der Einstellung int16_t rtmp; //Variable zum Speichern des Tastverhaeltnis waehrend der Einstellung int8_t mtmp; //Variable zum Speichern der Betriebsart waehrend der Einstellung // Flag Ist wahr, wenn die Initialisierung bereits erfolgte bool initDone = false; // Konfiguration fuer den I2S Bus i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN), //Betriebsart .sample_rate = 100000, //Abtastrate .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // der DAC verwendet nur 8 Bit des MSB .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // Kanalformat ESP32 unterstuetzt nur Stereo .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S_MSB, // Standard Format fuer I2S .intr_alloc_flags = 0, // Standard Interrupt .dma_buf_count = 2, //Anzahl der FIFO Buffer .dma_buf_len = 32, //Groeße der FIFO Buffer .use_apll = 0 //Taktquelle }; void fillBuffer(uint8_t up, uint8_t sz) { // Buffer fuer Dreieck Wellenform fuellen // Parameter up ist die Dauer fuer den Anstieg in Prozent // Parameter sz gibt die Buffergroeße fuer eine Periode an // es werden die Werte fuer eine Periode in den Buffer geschrieben uint8_t down; //Zeit fuer die fallende Flanke in % uint32_t sample; //32Bit Datenwort (I2S benoetigt zwei Kanaele mit je 16 Bit float du,dd,val; //Hilfsvariablen down=100-up; // Anzahl der Schritte fuer Anstieg und Abfall berechnen uint16_t stup = round(1.0*sz/100 * up); uint16_t stdwn = round(1.0*sz/100*down); uint16_t i; if ((stup + stdwn) < sz) stup++; //Ausgleich eventueller Rundungsfehler // Amplitudenaenderung pro Schritt fuer Anstieg und Abfall du = 256.0/stup; dd = 256.0/stdwn; // fuellen des Buffers val = 0; //Anstieg beginnt mit 0 for (i=0; i<stup; i++) { sample = val; sample = sample << 8; //Byte in das hoeherwertige Byte verschieben buf[i]=sample; val = val+du; //Wert erhoehen } // for (i=0; i<stup; i++) val=255; // Abfallende Flanke beginnt mit Maximalwert // Rest wie bei der ansteigenden Flanke for (i=0; i<stdwn; i++) { sample = val; sample = sample << 8; buf[i+stup]=sample; val = val-dd; } // for (i=0; i<stdwn; i++) } // void fillBuffer(uint8_t up, uint8_t sz) void stopAll() { // Alle Ausgaenge stoppen ledcDetachPin(SIGNALOUT); i2s_driver_uninstall((i2s_port_t)0); dac_output_disable(DAC_CHANNEL_2); dac_i2s_disable(); initDone=false; } // void stopAll() void startRectangle() { //Kurvenform Rechteck starten //Pin fuer Signalausgang zuweisen ledcAttachPin(SIGNALOUT,1 ); initDone=true; } // void startRectangle() void rectangleSetFrequency(double frequency,uint8_t ratio) { //Frequenz fuer Rechteck setzen mit entsprechendem Tastverhaeltnis ledcSetup(1,frequency,7); //Wir nutzen die LEDC Funktion mit 7 bit Aufloesung ledcWrite(1,127.0*ratio/100); //Berechnung der Schrittanzahl fuer Zustand = 1 } // void rectangleSetFrequency(double frequency,uint8_t ratio) void startTriangle() { // Dreiecksignal starten i2s_set_pin((i2s_port_t)0, NULL); //I2S wird mit dem DAC genutzt initDone=true; } // void startTriangle() double triangleSetFrequency(double frequency,uint8_t ratio) { // Frequenz fuer Dreieck setzen mit entsprechendem Tastverhaeltnis int _size=64; // zuerst wird die geeignete Buffergroeße ermittelt //damit die Ausgabe funktionier muss die I2S Abtastrate zwischen //5200 und 650000 liegen if (frequency<5000) { _size = 64; } // if (frequency<5000) else if (frequency<10000) { // _size = 32; _size = 32; } //else if (frequency<10000) else if (frequency<20000) { // _size = 16; _size = 16; } // else if (frequency<20000) else { // _size = 8; _size = 8; } //Abtastrate muss in einer Periode beide Buffer ausgeben uint32_t rate = frequency * 2 * _size; //Die Abtastrate darf nur innerhalb der Grenzwerte liegen if (rate < 5200) rate = 5200; if (rate > 650000) rate = 650000; //wirklichen Frequenzwert setzen frequency = rate / 2 / _size; //I2S Treiber entfernen i2s_driver_uninstall((i2s_port_t)0); //Konfiguration anpassen i2s_config.sample_rate = rate; i2s_config.dma_buf_len = _size; //und mit der neuen Konfiguration installieren i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); //Abtastrate einstellen i2s_set_sample_rates((i2s_port_t)0, rate); //Buffer fuellen fillBuffer(ratio,_size*2); //und einmal ausgeben i2s_write_bytes((i2s_port_t)0, (const char *)&buf, _size*8, 100); return frequency; } // double triangleSetFrequency(double frequency,uint8_t ratio) void startSinus() { // Sinusausgabe vorbereiten // Ausgang fuer Signalausgang freigeben dac_output_enable(DAC_CHANNEL_2); // Sinusgenerator aktivieren SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_SW_TONE_EN); // Ausgabe auf Kanal 1 starten SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN2_M); // Vorzeichenbit umkehren SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV2, 2, SENS_DAC_INV2_S); initDone=true; } // void startSinus() double sinusSetFrequency(double frequency) { //Frequenz fuer Sinus setzen // Formel f = s * SINFAKT / v // s sind die Schritte pro Taktimpuls // v ist der Vorteiler fuer den 8MHz Takt // Es gibt 8 Vorteiler von 1 bis 1/8 um die Kombination Vorteiler und // Schrittanzahl zu finden, testen wir alle acht Vorteiler Varianten // Die Kombination mit der geringsten Frequenzabweichung wird gewaehlt double f,delta,delta_min = 999999999.0; uint16_t divi=0, _step=1, s; uint8_t clk_8m_div = 0;//0 bis 7 for(uint8_t _div = 1; _div<9; _div++) { s=round(frequency * _div/SINFAKT); if ((s>0) && ((_div == 1) || (s<1024))) { f= SINFAKT*s/_div; // Serial.print(f); Serial.print(" "); // Serial.print(_div); Serial.print(" "); // Serial.println(s); // delta = abs(f-frequency); if(delta < delta_min) { //Abweichung geringer -> aktuelle Werte merken _step = s; divi = _div-1; delta_min = delta; } // if(delta < delta_min) } // if ((s>0) && ((_div == 1) || (s<1024))) } // for(uint8_t _div = 1; _div<9; _div++) //wirklichen Frequenzwert setzen frequency = SINFAKT * _step / (divi+1); // Vorteiler einstellen REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, divi); // Schritte pro Taktimpuls einstellen SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL1_REG, SENS_SW_FSTEP, _step, SENS_SW_FSTEP_S); return frequency; } // double sinusSetFrequency(double frequency) void controlGenerator() { // //Einstellungsaenderungen durchfuehren switch(mode) { case MSINUS: if (!initDone) startSinus(); frequency = sinusSetFrequency(frequency); break; case MRECTANGLE : if (!initDone) startRectangle(); rectangleSetFrequency(frequency,ratio); break; case MTRIANGLE : if (!initDone) startTriangle(); frequency = triangleSetFrequency(frequency,ratio); break; } // switch(mode) } // void controlGenerator() void displayValues(boolean _monitor) { //Anzeige aktualisieren // Wenn _monitor wahr ist, erfolgt die Ausgabe // auch auf die serielle Schnittstelle char buf[15]; //aktuelle Werte ausgeben String ba; switch(mode) { case MSINUS: ba="Sinus "; break; case MRECTANGLE: ba="Rechteck "; break; case MTRIANGLE: ba="Dreieck "; break; } // switch(mode) //Betriebsart ausgeben lcd.setCursor(0,0); lcd.print(" "); lcd.print(ba); if(_monitor) { Serial.println("**************** Eingestellte Werte *************************"); Serial.print("Betriebsart = "); Serial.println(ba); } // if(_monitor) if(frequency < 1000) { //Frequenz je nach Wert als Hz oder kHz sprintf(buf,"%6.2f Hz",frequency); } else { // if!(frequency < 1000) sprintf(buf,"%6.2fkHz",frequency/1000); } // else { // if!(frequency < 1000) //Frequenz ausgeben lcd.setCursor(0,1); lcd.print(" F"); lcd.print(buf); if(_monitor) { Serial.print("Frequenz = "); Serial.println(buf); } // if(_monitor) sprintf(buf,"%2i%%",ratio); // Tastverhaeltnis ausgeben lcd.setCursor(11,1); lcd.print(" T"); lcd.print(buf); if(_monitor) { Serial.print("Tastverhaeltnis = "); Serial.println(buf); Serial.println(); } // if(_monitor) switch(edit) { // je nach Edit-Mode Pfeilzeichen ausgeben case EMODE: lcd.setCursor(0,0); break; case EFREQUENCY: lcd.setCursor(0,1); break; case ERATIO: lcd.setCursor(11,1); break; } // switch(edit) lcd.print(char(126)); } // void displayValues(boolean _monitor) void changeEdit(boolean up) { // Edit Mode aendern mit UP und DOWN Taste // je nach Richtung Schritte positiv oder negativ int s = up?1:-1; edit += s; // am Ende wieder auf Anfang springen if (edit < 0) edit = 2; if (edit > 2) edit = 0; // die aktuellen Werte in die temporaeren Werte // fuer die aenderung kopieren ftmp = frequency; rtmp = ratio; mtmp = mode; // Geaenderte Editmode ausgeben Serial.print("Mode = ");Serial.println(mode); // Anzeige aktualisieren ohne Ausgabe auf serielle // Schnittstelle displayValues(false); } // void changeEdit(boolean up) void changeMode(boolean up) { // Betriebsart aendern mit RIGHT und LEFT Taste // je nach Richtung Schritte positiv oder negativ int s = up?1:-1; // temporaere Betriebsart aendern mtmp += s; // Wenn das Ende erreicht wird wieder auf Anfang springen if (mtmp < 0) mtmp = 2; if (mtmp > 2) mtmp = 0; // Geaenderte Betriebsart am Display anzeigen lcd.setCursor(1,0); switch(mtmp) { case 0: lcd.print("Sinus "); break; case 1: lcd.print("Rechteck "); break; case 2: lcd.print("Dreieck "); break; } // switch(mtmp) } // void changeMode(boolean up) void changeFrequency(boolean up) { //Frequenz aendern mit RIGHT und LEFT Taste // war die Taste vorher nicht gedrueckt, wird die Schrittweite auf 1 gesetzt // waehrend die Taste gedrueckt bleibt, wird die Schrittweite laufend // verdoppelt bis eine maximale Schrittweite erreicht wurde _step = (lastKey == NONE)?1:_step*2; if(_step > 1024) _step = 1024; // Richtungsfaktor bestimmen int16_t s = up?1:-1; // temporaere Frequenz aendern ftmp = ftmp+s*_step; // auf Minimal- und Maximalwerte pruefen if(ftmp < 20) ftmp=20; if (ftmp > 20000) ftmp = 20000; char buf[15]; // Fuer die Anzeige Hz oder kHz if(ftmp > 999) { sprintf(buf,"%6.2fkHz",ftmp/1000.0); } else { // if !(ftmp > 999) sprintf(buf,"%6.2f Hz",ftmp*1.0); } // Geaenderte Frequenz am Display anzeigen lcd.setCursor(2,1); lcd.print(buf); } // void changeFrequency(boolean up) void changeRatio(boolean up) { // //Tastverhaeltnis aendern mit RIGHT und LEFT Taste // Richtung festlegen int8_t jx = up?1:-1; // Temporaeres Tastverhaeltnis aendern rtmp = rtmp+jx; // auf Minimal- und Maximalwerte pruefen if (rtmp < 0) rtmp=0; if (rtmp > 100) rtmp = 100; char buf[15]; // Geaendertes Tastverhaeltnis am Display anzeigen sprintf(buf,"%2i%%",rtmp); lcd.setCursor(13,1); lcd.print(buf); } // void changeRatio(boolean up) void setValues() { // der Funktionsgenerator wird auf die geaenderte Einstellung gesetzt // die temporaeren Werte werden in die Aktuellen Werte uebernommen Serial.print("Set values edit = "); Serial.println(edit); switch(edit) { case EMODE: stopAll(); mode = mtmp; break; case EFREQUENCY: frequency = ftmp; break; case ERATIO: ratio = rtmp; break; } // switch(edit) // Funktionsgenerator selber aendern controlGenerator(); displayValues(true); } // void setValues() void handleKeys() { // Tastaturspannung einlesen und auswerten //Tastaturspannung einlesen int x=analogRead(KEYS); uint8_t key = NONE; if (x < Ur) { key = RIGHT; } else if (x < Uu) { key = UP; } else if (x < Ud){ key = DOWN; } else if (x < Ul){ key = LEFT; } else if (x < Us){ key = SELECT; } else {key = NONE;} if(((key == UP) || (key == DOWN)) && (lastKey == NONE)) changeEdit(key== DOWN); if((key == LEFT) || (key == RIGHT)) { switch(edit) { case EMODE: if (lastKey == NONE) changeMode(key == RIGHT); break; case EFREQUENCY: changeFrequency(key == RIGHT); break; case ERATIO: changeRatio(key == RIGHT); break; } // switch(edit) } // if ((key == LEFT) || (key == RIGHT)) if ((key == SELECT) && (lastKey == NONE)) setValues(); lastKey = key; tic = millis(); } // void handleKeys() void setup() { // Serielle Schnittstelle aktivieren und // Defaulteinstellungen 1kHz Sinus setzen // Schwellwerte fuer Tastatur festlegen Serial.begin(115200); controlGenerator(); lcd.begin(16,2); // initialisiere LCD I2C Anzeige lcd.clear(); displayValues(true); tic = millis(); Serial.print("Kommando M,F,A,T,O : "); pinMode(2,INPUT); // Schwellwerte fuer Taster berechnen // Diese Berechnung ist notwendig, da die Toleranzen sehr // gering sind, und die Schwellwerte von der Betriebsspannung // abhaengen. // Versorgungsspannung ohne Taste ermitteln int x=analogRead(A12); float Rin = R3 * Rp / (R3 + Rp); float Ub = x / Rin * (Rin + Rv); // Schwellspannungen ermitteln float Uup,Udn,Ulf,Usl,Rtmp; // Mittelwert fuer UP Taste Rtmp = Rin * Ru / (Rin + Ru); Uup = Ub * Rtmp / (Rtmp + Rv); // Mittelwert fuer DOWN Taste Rtmp = Rin * Rd / (Rin + Rd); Udn = Ub * Rtmp / (Rtmp + Rv); // Mittelwert fuer LEFT Taste Rtmp = Rin * Rl / (Rin + Rl); Ulf = Ub * Rtmp / (Rtmp + Rv); // Mittelwert fuer Select Taste Rtmp = Rin * Rs / (Rin + Rs); Usl = Ub * Rtmp / (Rtmp + Rv); //eigentliche Schwellwerte berechnen //immer in die Mitte zwischen zwei Mittelwerten Ur = Uup/2; Uu = Uup + (Udn - Uup) / 2; Ud = Udn + (Ulf - Udn) / 2; Ul = Ulf + (Usl - Ulf) / 2; Us = Usl + (x-Usl) /2; // Schwellwerte auf die serielle Schnittstelle ausgeben Serial.printf("Schwellwerte: right %i, up %i, down %i, left %i, select%i\n",Ur,Uu,Ud,Ul,Us); } // void setup() void loop() { if((millis()-tic) > 200) handleKeys(); //Serielle Schnittstelle abfragen if (Serial.available() > 0) { //Befehl von der Schnittstelle einlesen String inp = Serial.readStringUntil('\n'); //und zur Kontrolle ausgeben Serial.println(inp); char cmd = inp[0]; //erstes Zeichen ist das Kommando if((cmd == 'M') || (cmd == 'm')) { //war das Zeichen 'M' wird die Betriebsart eingestellt char newMode = inp[1]; //zweites Zeichen ist die Betriebsart uint8_t nm=0; switch(newMode) { case 's': case 'S': nm=0; break; case 'r': case 'R': nm=1; break; case 't': case 'T': nm=2; break; } // switch (newMode) if(nm != mode) { //Nur wenn eine Aenderung vorliegt, muss was getan werden stopAll(); mode=nm; controlGenerator(); } // if(nm != mode) } // if((cmd == 'M') || (cmd == 'm')) else { // if !((cmd == 'M') || (cmd == 'm')) // bei den anderen Befehlen folgt ein Zahlenwert String dat = inp.substring(1); switch(cmd) { //je nach Befehl, werden die Daten geaendert case 'F' : //Frequenz case 'f' :frequency = dat.toDouble(); break; case 'T' : //Tastverhaeltnis case 't' :ratio = dat.toInt(); break; } // switch(cmd) if(ratio > 100) ratio = 100; // Grenzwerte werden ueberprueft if(frequency < 20) frequency = 20; if(frequency > 20000) frequency = 20000; controlGenerator(); } // else { // if !((cmd == 'M') || (cmd == 'm')) displayValues(true); //aktuelle Werte ausgeben Serial.print("Kommando M,F,T : "); } // if (Serial.available() > 0) } // void loop() // void loop()
Hi,
Ich hole das Thema aus aktuellem Anlass mal aus dem Keller.
Auf der Website "https://derrcmodellbauer.de/produkt/hoodie-with-logo-4/" wird ein Servotester Vorgestellt und teilweise Bauteile/Platinen dafür angeboten.
Die Software wurde von "https://github.com/TheDIYGuy999/Servotester_Deluxe" überarbeitet.
Diese Software bietet eine Ozilloskop-Funktion auf Kanal 5, welche auch gut funktioniert.
Die Hardware basiert auf ein "ESP32 Wroom Board", mit dem ich auch dieses Projekt verwirklicht habe.
Jetzt habe ich den kpl. Signalgenerator mit deiner Software kpl. auf Lochraster aufgebaut, aber leider funktioniert das Display nicht wie gewünscht.
Da ich das Display steckbar angebracht habe, kann ich evtl. einige Hilfestellung bei dem Softwarebug geben.
Mit angestektem Display läuft die Installierte oben aufgeführte SW leider nicht und es werden Fehlermeldungen über den Seriellen Monitor ausgegeben.
Sobald ich das Display trenne, bleibt die serielle Ausgabe stehen, und der Generator verrichtet dann sofort seine Arbeit.
Es lassen sich dann auch alle Einstellungen über den PC sowie auch über die Hardware Tasten steuern.
Wird dann das Display wieder angeschlossen, passiert ausser der Hintergrundbeleuchtung nichts, erst nach diversen Tastendrücken der Hardwaretasten erscheinen zuerst
viele Sonderzeichen am Display bis irgendwann dann der richtige Text sichtbar wird, passend auch zu den Eingaben der Tastatur.
Wäre super, sich diesem Thema noch einmal zu widmen, da das meiner Meinung nach eine sehr gute Kombination beider Entwicklungen darstellt.
Danke
Um das etwas zu Veranschaulichen, hier einige Bilder des Verhaltens:
Nach dem ersten Anlegen der Versorgungsspannung über USB-C am ESP mit montiertem Display
kommen diese Ausgaben am Seriellen Monitor.
Ansicht der Platine mit entferntem Display
Die Funktion ist danach sofort gegeben, man sieht halt nur nichts am Display
Sobald das Display im laufendem Betrieb wieder montiert wird, kommen nach einigen Tastendrücken auf den Bedientasten der Lochrasterplatine diese Zeichen
nach weiteren Tastendrücken werden dann auch beide Zeilen angesteuert
bis irgendwann nach weiteren Tastenbetätigungen "fast" alles funktioniert.
Es ist mir schon klar, das ein Anschluß vom Display im laufenden Betrieb absolut zu vermeiden ist, aber ich bin nur durch Zufall auf dieses Verhalten gestoßen.
Mit angeschlossenem Display beim Start ist auch nach vielen vielen Versuchen (und es waren wirklich viele) keine Displayausgabe zu erreichen!
Platine von unten
Lochmaster 4.0 Zeichnung
So, nach weiteren Versuchen den Fehler gefunden!
Anscheinend sind die GPIO-Pin´s der verschiedenen ESP-Versionen nicht direkt kompatibel!
Auf jeden Fall hat das Umverdrahten vom Display "RS"-Pin 4 vom ESP Pin-13 GPIO-12 auf den ESP Pin-8 GPIO-33 geholfen.
Das Programm wurde dementsprechend angepasst.
Jetzt läuft das vorerst Tadellos und werde ein passendes Gehäuse zum 3-D-Druck entwerfen.
Wenn Interesse besteht, werde ich die Vorlagen nach der Beta-Phase hier zur Verfügung stellen.
P.S.:
Die von mir verwendete "esp32" Boardversion war die 1.0.5!
Diese Version war bei meinem verwendetem ESP-Board zwingend erforderlich und lässt sich einfach über den Arduino-Boarverwalter installieren bzw. bei Bedarf auch Downgraden!
Die Datenübertragung/Kompilieren zum ESP wird ansonsten mit einer Fehlermeldung abgebrochen.
Es gibt zwar ein "Lösungsansatz" für diese bekannte Fehlermeldung, aber es lässt sich damit nur ein Sinussignal am Ausgang erzeugen, alle anderen Wellenformen werden aufgrund fehlender Treiberinstallation ignoriert.
Moin Pieper,
dann kann ich mir ja das Gemeckere sparen...
Nee, im Ernst, wenn du ein Code einstellst, der offensichtlich für die Arduino Ide ist, dann ist es zwingend erforderlich einige Daten bekannt zu geben.
Welche IDE
Welche Core-Version
Welche Bibliothek ( es gibt für ein Gerät, meistens mehrere)
Und zuletzt, es gibt so viele verschiedene ESP32-Versionen.
73 de Bernd
Werde kurzfristig die fehlenden Informationen nachreichen 😉
Ich muss aber erst die Finale Version fertigstellen 🤷🏻♂️ (damit nicht wieder Gemeckert wird 😜)
Wie versprochen, hier die nötigen Informationen zum Nachbau:
Die von mir verwendeten Versionen:
Wie oben im voran gegangenen Beitrag ist die von mir verwendete "esp32 Dev Module" Boardversion die 1.0.5 und für das unten aufgeführte Listing ZWINGEND erforderlich!
Der von mir modifizierte Code:
(Im Anschluß daran sind die Änderungen nochmals bildlich dargestellt)
// Der Sketch verwendet 252441 Bytes (19%) des Programmspeicherplatzes. Das Maximum sind 1310720 Bytes.
// Globale Variablen verwenden 16044 Bytes (4%) des dynamischen Speichers, 311636 Bytes für lokale Variablen // verbleiben. Das Maximum sind 327680 Bytes.
// reservierte Namen fuer Variablen nerven mich, deswegen
// step zu _step ersetzt
// monitor zu _monitor ersetzt
// size zu _size ersetzt
// div zu _div ersetzt
// nur mit mode funktioniert das nicht, extern definiert?
/* Funktionsgenerator fuer Sinus, Dreieck und Rechteck Signale
* Einstellbare Frequenz 20 Hz bis 20 KHz
* Fuer Dreieck und Rechteck einstellbares Tastverhaeltnis 0 bis 100%
*/
// Bibliotheken zum direkten Zugriff auf Steuerregister des ESP32
// Bild 3: Rueckseite ESP32 D1 R32 mit 10 kOhm-Widerstand zwischen GPIO12 und GND
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "soc/rtc.h"
// Bibliotheken zur Verwendung des Digital zu Analog Konverters und fuer den I2S-Bus
#include "driver/dac.h"
#include "driver/i2s.h"
// Bibliothek fuer das LCD Display
#include <LiquidCrystal.h>
#define SINFAKT 127.0 //gemessen fuer Schrittweite = 1 und kein Vorteiler (8.3MHz)
#define SIGNALOUT 26 //Pin fuer die Signalausgabe
// LCD Pins
#define PIN_RS 33 //Registerselect 0=Befehle 1=Daten
#define PIN_EN 13 //Enable Takt zum Schreiben
#define PIN_D4 17 //Datenbit
#define PIN_D5 16 //Datenbit
#define PIN_D6 27 //Datenbit
#define PIN_D7 14 //Datenbit
#define PIN_BL 5 //Hintergrundbeleuchtung 0=aus
// Analog PIN fuer Tasten
#define KEYS A12
// Spannungsteiler fuer Tasten
#define Rv 2000 //Vorwiderstand
#define R3 3900 //Schutzwiderstand fuer maximal 3.3 V
#define Rp 1000 //Widerstand GPIO2 gegen Masse
#define Rr 0 //Spannungsteiler bei gedrueckter RIGHT Taste
#define Ru 330 //Spannungsteiler bei gedrueckter UP Taste
#define Rd 950 //Spannungsteiler bei gedrueckter DOWN Taste
#define Rl 1950 //Spannungsteiler bei gedrueckter LEFT Taste
#define Rs 5250 //Spannungsteiler bei gedrueckter SELECT Taste
// Tasten Codes
#define NONE 0
#define LEFT 1
#define RIGHT 2
#define UP 3
#define DOWN 4
#define SELECT 5
// Betriebsarten
#define MSINUS 0
#define MRECTANGLE 1
#define MTRIANGLE 2
// Aenderungstypen
#define EMODE 0
#define EFREQUENCY 1
#define ERATIO 2
// Init I2C LCD
LiquidCrystal lcd(PIN_RS, PIN_EN, PIN_D4, PIN_D5, PIN_D6, PIN_D7);
// Variablen zum Speichern der Schwellwerte fuer Taster
uint16_t Ur, Uu, Ud, Ul, Us;
// Buffer zum Erstellen der Dreieckfunktion
uint32_t buf[128];
// Einstellwerte fuer Kurvenform, Frequenz und Tastverhaeltnis
int8_t mode = MSINUS; //0=Sinus, 1=Rechteck, 2=Dreieck
float frequency = 1000; //20 bis 200000 Hz
int8_t ratio = 50; //Tastverhaeltnis 0 bis 100%
int8_t edit = EMODE; //was wird veraendert 0=Mode 1=Frequenz2=Tastverhaeltnis
uint32_t tic; //Fuer Wartezeit
int8_t lastKey = 0; //zuletzt ermittelte Taste oder 0 wenn keine
uint16_t _step = 0; //Schrittweite fuer Frequenzerhoehung
float ftmp; //Variable zum Speichern der Frequenz waehrend der Einstellung
int16_t rtmp; //Variable zum Speichern des Tastverhaeltnis waehrend der Einstellung
int8_t mtmp; //Variable zum Speichern der Betriebsart waehrend der Einstellung
// Flag Ist wahr, wenn die Initialisierung bereits erfolgte
bool initDone = false;
// Konfiguration fuer den I2S Bus
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN), //Betriebsart
.sample_rate = 100000, //Abtastrate
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // der DAC verwendet nur 8 Bit des MSB
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // Kanalformat ESP32 unterstuetzt nur Stereo
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S_MSB,
// Standard Format fuer I2S
.intr_alloc_flags = 0, // Standard Interrupt
.dma_buf_count = 2, //Anzahl der FIFO Buffer
.dma_buf_len = 32, //Groeße der FIFO Buffer
.use_apll = 0 //Taktquelle
};
void fillBuffer(uint8_t up, uint8_t sz) { // Buffer fuer Dreieck Wellenform fuellen
// Parameter up ist die Dauer fuer den Anstieg in Prozent
// Parameter sz gibt die Buffergroeße fuer eine Periode an
// es werden die Werte fuer eine Periode in den Buffer geschrieben
uint8_t down; //Zeit fuer die fallende Flanke in %
uint32_t sample; //32Bit Datenwort (I2S benoetigt zwei Kanaele mit je 16 Bit
float du,dd,val; //Hilfsvariablen
down=100-up;
// Anzahl der Schritte fuer Anstieg und Abfall berechnen
uint16_t stup = round(1.0*sz/100 * up);
uint16_t stdwn = round(1.0*sz/100*down);
uint16_t i;
if ((stup + stdwn) < sz) stup++; //Ausgleich eventueller Rundungsfehler
// Amplitudenaenderung pro Schritt fuer Anstieg und Abfall
du = 256.0/stup;
dd = 256.0/stdwn;
// fuellen des Buffers
val = 0; //Anstieg beginnt mit 0
for (i=0; i<stup; i++) {
sample = val;
sample = sample << 8; //Byte in das hoeherwertige Byte verschieben
buf[i]=sample;
val = val+du; //Wert erhoehen
} // for (i=0; i<stup; i++)
val=255; // Abfallende Flanke beginnt mit Maximalwert
// Rest wie bei der ansteigenden Flanke
for (i=0; i<stdwn; i++) {
sample = val;
sample = sample << 8;
buf[i+stup]=sample;
val = val-dd;
} // for (i=0; i<stdwn; i++)
} // void fillBuffer(uint8_t up, uint8_t sz)
void stopAll() { // Alle Ausgaenge stoppen
ledcDetachPin(SIGNALOUT);
i2s_driver_uninstall((i2s_port_t)0);
dac_output_disable(DAC_CHANNEL_2);
dac_i2s_disable();
initDone=false;
} // void stopAll()
void startRectangle() { //Kurvenform Rechteck starten
//Pin fuer Signalausgang zuweisen
ledcAttachPin(SIGNALOUT,1 );
initDone=true;
} // void startRectangle()
void rectangleSetFrequency(double frequency,uint8_t ratio) { //Frequenz fuer Rechteck setzen mit entsprechendem Tastverhaeltnis
ledcSetup(1,frequency,7); //Wir nutzen die LEDC Funktion mit 7 bit Aufloesung
ledcWrite(1,127.0*ratio/100); //Berechnung der Schrittanzahl fuer Zustand = 1
} // void rectangleSetFrequency(double frequency,uint8_t ratio)
void startTriangle() { // Dreiecksignal starten
i2s_set_pin((i2s_port_t)0, NULL); //I2S wird mit dem DAC genutzt
initDone=true;
} // void startTriangle()
double triangleSetFrequency(double frequency,uint8_t ratio) { // Frequenz fuer Dreieck setzen mit entsprechendem Tastverhaeltnis
int _size=64;
// zuerst wird die geeignete Buffergroeße ermittelt
//damit die Ausgabe funktionier muss die I2S Abtastrate zwischen
//5200 und 650000 liegen
if (frequency<5000) {
_size = 64;
} // if (frequency<5000)
else if (frequency<10000) { // _size = 32;
_size = 32;
} //else if (frequency<10000)
else if (frequency<20000) { // _size = 16;
_size = 16;
} // else if (frequency<20000)
else { // _size = 8;
_size = 8;
}
//Abtastrate muss in einer Periode beide Buffer ausgeben
uint32_t rate = frequency * 2 * _size;
//Die Abtastrate darf nur innerhalb der Grenzwerte liegen
if (rate < 5200) rate = 5200;
if (rate > 650000) rate = 650000;
//wirklichen Frequenzwert setzen
frequency = rate / 2 / _size;
//I2S Treiber entfernen
i2s_driver_uninstall((i2s_port_t)0);
//Konfiguration anpassen
i2s_config.sample_rate = rate;
i2s_config.dma_buf_len = _size;
//und mit der neuen Konfiguration installieren
i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
//Abtastrate einstellen
i2s_set_sample_rates((i2s_port_t)0, rate);
//Buffer fuellen
fillBuffer(ratio,_size*2);
//und einmal ausgeben
i2s_write_bytes((i2s_port_t)0, (const char *)&buf, _size*8, 100);
return frequency;
} // double triangleSetFrequency(double frequency,uint8_t ratio)
void startSinus() { // Sinusausgabe vorbereiten
// Ausgang fuer Signalausgang freigeben
dac_output_enable(DAC_CHANNEL_2);
// Sinusgenerator aktivieren
SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_SW_TONE_EN);
// Ausgabe auf Kanal 1 starten
SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN2_M);
// Vorzeichenbit umkehren
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV2, 2,
SENS_DAC_INV2_S);
initDone=true;
} // void startSinus()
double sinusSetFrequency(double frequency) { //Frequenz fuer Sinus setzen
// Formel f = s * SINFAKT / v
// s sind die Schritte pro Taktimpuls
// v ist der Vorteiler fuer den 8MHz Takt
// Es gibt 8 Vorteiler von 1 bis 1/8 um die Kombination Vorteiler und
// Schrittanzahl zu finden, testen wir alle acht Vorteiler Varianten
// Die Kombination mit der geringsten Frequenzabweichung wird gewaehlt
double f,delta,delta_min = 999999999.0;
uint16_t divi=0, _step=1, s;
uint8_t clk_8m_div = 0;//0 bis 7
for(uint8_t _div = 1; _div<9; _div++) {
s=round(frequency * _div/SINFAKT);
if ((s>0) && ((_div == 1) || (s<1024))) {
f= SINFAKT*s/_div;
// Serial.print(f); Serial.print(" ");
// Serial.print(_div); Serial.print(" ");
// Serial.println(s);
//
delta = abs(f-frequency);
if(delta < delta_min) { //Abweichung geringer -> aktuelle Werte merken
_step = s; divi = _div-1; delta_min = delta;
} // if(delta < delta_min)
} // if ((s>0) && ((_div == 1) || (s<1024)))
} // for(uint8_t _div = 1; _div<9; _div++)
//wirklichen Frequenzwert setzen
frequency = SINFAKT * _step / (divi+1);
// Vorteiler einstellen
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, divi);
// Schritte pro Taktimpuls einstellen
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL1_REG, SENS_SW_FSTEP, _step, SENS_SW_FSTEP_S);
return frequency;
} // double sinusSetFrequency(double frequency)
void controlGenerator() { // //Einstellungsaenderungen durchfuehren
switch(mode) {
case MSINUS: if (!initDone) startSinus();
frequency = sinusSetFrequency(frequency);
break;
case MRECTANGLE : if (!initDone) startRectangle();
rectangleSetFrequency(frequency,ratio);
break;
case MTRIANGLE : if (!initDone) startTriangle();
frequency = triangleSetFrequency(frequency,ratio);
break;
} // switch(mode)
} // void controlGenerator()
void displayValues(boolean _monitor) { //Anzeige aktualisieren
// Wenn _monitor wahr ist, erfolgt die Ausgabe
// auch auf die serielle Schnittstelle
char buf[15];
//aktuelle Werte ausgeben
String ba;
switch(mode) {
case MSINUS: ba="Sinus "; break;
case MRECTANGLE: ba="Rechteck "; break;
case MTRIANGLE: ba="Dreieck "; break;
} // switch(mode)
//Betriebsart ausgeben
lcd.setCursor(0,0);
lcd.print(" ");
lcd.print(ba);
if(_monitor) {
Serial.println("**************** Eingestellte Werte *************************");
Serial.print("Betriebsart = "); Serial.println(ba);
} // if(_monitor)
if(frequency < 1000) { //Frequenz je nach Wert als Hz oder kHz
sprintf(buf,"%6.2f Hz",frequency);
}
else { // if!(frequency < 1000)
sprintf(buf,"%6.2fkHz",frequency/1000);
} // else { // if!(frequency < 1000)
//Frequenz ausgeben
lcd.setCursor(0,1);
lcd.print(" F"); lcd.print(buf);
if(_monitor) {
Serial.print("Frequenz = "); Serial.println(buf);
} // if(_monitor)
sprintf(buf,"%2i%%",ratio);
// Tastverhaeltnis ausgeben
lcd.setCursor(11,1);
lcd.print(" T"); lcd.print(buf);
if(_monitor) {
Serial.print("Tastverhaeltnis = "); Serial.println(buf);
Serial.println();
} // if(_monitor)
switch(edit) { // je nach Edit-Mode Pfeilzeichen ausgeben
case EMODE: lcd.setCursor(0,0); break;
case EFREQUENCY: lcd.setCursor(0,1); break;
case ERATIO: lcd.setCursor(11,1); break;
} // switch(edit)
lcd.print(char(126));
} // void displayValues(boolean _monitor)
void changeEdit(boolean up) { // Edit Mode aendern mit UP und DOWN Taste
// je nach Richtung Schritte positiv oder negativ
int s = up?1:-1;
edit += s;
// am Ende wieder auf Anfang springen
if (edit < 0) edit = 2;
if (edit > 2) edit = 0;
// die aktuellen Werte in die temporaeren Werte
// fuer die aenderung kopieren
ftmp = frequency;
rtmp = ratio;
mtmp = mode;
// Geaenderte Editmode ausgeben
Serial.print("Mode = ");Serial.println(mode);
// Anzeige aktualisieren ohne Ausgabe auf serielle
// Schnittstelle
displayValues(false);
} // void changeEdit(boolean up)
void changeMode(boolean up) { // Betriebsart aendern mit RIGHT und LEFT Taste
// je nach Richtung Schritte positiv oder negativ
int s = up?1:-1;
// temporaere Betriebsart aendern
mtmp += s;
// Wenn das Ende erreicht wird wieder auf Anfang springen
if (mtmp < 0) mtmp = 2;
if (mtmp > 2) mtmp = 0;
// Geaenderte Betriebsart am Display anzeigen
lcd.setCursor(1,0);
switch(mtmp) {
case 0: lcd.print("Sinus "); break;
case 1: lcd.print("Rechteck "); break;
case 2: lcd.print("Dreieck "); break;
} // switch(mtmp)
} // void changeMode(boolean up)
void changeFrequency(boolean up) { //Frequenz aendern mit RIGHT und LEFT Taste
// war die Taste vorher nicht gedrueckt, wird die Schrittweite auf 1 gesetzt
// waehrend die Taste gedrueckt bleibt, wird die Schrittweite laufend
// verdoppelt bis eine maximale Schrittweite erreicht wurde
_step = (lastKey == NONE)?1:_step*2;
if(_step > 1024) _step = 1024;
// Richtungsfaktor bestimmen
int16_t s = up?1:-1;
// temporaere Frequenz aendern
ftmp = ftmp+s*_step;
// auf Minimal- und Maximalwerte pruefen
if(ftmp < 20) ftmp=20;
if (ftmp > 20000) ftmp = 20000;
char buf[15];
// Fuer die Anzeige Hz oder kHz
if(ftmp > 999) {
sprintf(buf,"%6.2fkHz",ftmp/1000.0);
}
else { // if !(ftmp > 999)
sprintf(buf,"%6.2f Hz",ftmp*1.0);
}
// Geaenderte Frequenz am Display anzeigen
lcd.setCursor(2,1);
lcd.print(buf);
} // void changeFrequency(boolean up)
void changeRatio(boolean up) { // //Tastverhaeltnis aendern mit RIGHT und LEFT Taste
// Richtung festlegen
int8_t jx = up?1:-1;
// Temporaeres Tastverhaeltnis aendern
rtmp = rtmp+jx;
// auf Minimal- und Maximalwerte pruefen
if (rtmp < 0) rtmp=0;
if (rtmp > 100) rtmp = 100;
char buf[15];
// Geaendertes Tastverhaeltnis am Display anzeigen
sprintf(buf,"%2i%%",rtmp);
lcd.setCursor(13,1);
lcd.print(buf);
} // void changeRatio(boolean up)
void setValues() { // der Funktionsgenerator wird auf die geaenderte Einstellung gesetzt
// die temporaeren Werte werden in die Aktuellen Werte uebernommen
Serial.print("Set values edit = "); Serial.println(edit);
switch(edit) {
case EMODE: stopAll(); mode = mtmp; break;
case EFREQUENCY: frequency = ftmp; break;
case ERATIO: ratio = rtmp; break;
} // switch(edit)
// Funktionsgenerator selber aendern
controlGenerator();
displayValues(true);
} // void setValues()
void handleKeys() { // Tastaturspannung einlesen und auswerten
//Tastaturspannung einlesen
int x=analogRead(KEYS);
uint8_t key = NONE;
if (x < Ur) { key = RIGHT; }
else if (x < Uu) { key = UP; }
else if (x < Ud){ key = DOWN; }
else if (x < Ul){ key = LEFT; }
else if (x < Us){ key = SELECT; }
else {key = NONE;}
if(((key == UP) || (key == DOWN)) && (lastKey == NONE)) changeEdit(key== DOWN);
if((key == LEFT) || (key == RIGHT)) {
switch(edit) {
case EMODE: if (lastKey == NONE) changeMode(key == RIGHT);
break;
case EFREQUENCY: changeFrequency(key == RIGHT);
break;
case ERATIO: changeRatio(key == RIGHT);
break;
} // switch(edit)
} // if ((key == LEFT) || (key == RIGHT))
if ((key == SELECT) && (lastKey == NONE)) setValues();
lastKey = key;
tic = millis();
} // void handleKeys()
void setup() { // Serielle Schnittstelle aktivieren und
// Defaulteinstellungen 1kHz Sinus setzen
// Schwellwerte fuer Tastatur festlegen
Serial.begin(115200);
controlGenerator();
lcd.begin(16,2); // initialisiere LCD I2C Anzeige
lcd.clear();
displayValues(true);
tic = millis();
Serial.print("Kommando M,F,A,T,O : ");
pinMode(2,INPUT);
// Schwellwerte fuer Taster berechnen
// Diese Berechnung ist notwendig, da die Toleranzen sehr
// gering sind, und die Schwellwerte von der Betriebsspannung
// abhaengen.
// Versorgungsspannung ohne Taste ermitteln
int x=analogRead(A12);
float Rin = R3 * Rp / (R3 + Rp);
float Ub = x / Rin * (Rin + Rv);
// Schwellspannungen ermitteln
float Uup,Udn,Ulf,Usl,Rtmp;
// Mittelwert fuer UP Taste
Rtmp = Rin * Ru / (Rin + Ru);
Uup = Ub * Rtmp / (Rtmp + Rv);
// Mittelwert fuer DOWN Taste
Rtmp = Rin * Rd / (Rin + Rd);
Udn = Ub * Rtmp / (Rtmp + Rv);
// Mittelwert fuer LEFT Taste
Rtmp = Rin * Rl / (Rin + Rl);
Ulf = Ub * Rtmp / (Rtmp + Rv);
// Mittelwert fuer Select Taste
Rtmp = Rin * Rs / (Rin + Rs);
Usl = Ub * Rtmp / (Rtmp + Rv);
//eigentliche Schwellwerte berechnen
//immer in die Mitte zwischen zwei Mittelwerten
Ur = Uup/2;
Uu = Uup + (Udn - Uup) / 2;
Ud = Udn + (Ulf - Udn) / 2;
Ul = Ulf + (Usl - Ulf) / 2;
Us = Usl + (x-Usl) /2;
// Schwellwerte auf die serielle Schnittstelle ausgeben
Serial.printf("Schwellwerte: right %i, up %i, down %i, left %i, select%i\n",Ur,Uu,Ud,Ul,Us);
} // void setup()
void loop() {
if((millis()-tic) > 200) handleKeys();
//Serielle Schnittstelle abfragen
if (Serial.available() > 0) {
//Befehl von der Schnittstelle einlesen
String inp = Serial.readStringUntil('\n');
//und zur Kontrolle ausgeben
Serial.println(inp);
char cmd = inp[0]; //erstes Zeichen ist das Kommando
if((cmd == 'M') || (cmd == 'm')) { //war das Zeichen 'M' wird die Betriebsart eingestellt
char newMode = inp[1]; //zweites Zeichen ist die Betriebsart
uint8_t nm=0;
switch(newMode) {
case 's':
case 'S': nm=0;
break;
case 'r':
case 'R': nm=1;
break;
case 't':
case 'T': nm=2;
break;
} // switch (newMode)
if(nm != mode) { //Nur wenn eine Aenderung vorliegt, muss was getan werden
stopAll();
mode=nm;
controlGenerator();
} // if(nm != mode)
} // if((cmd == 'M') || (cmd == 'm'))
else { // if !((cmd == 'M') || (cmd == 'm')) // bei den anderen Befehlen folgt ein Zahlenwert
String dat = inp.substring(1);
switch(cmd) { //je nach Befehl, werden die Daten geaendert
case 'F' : //Frequenz
case 'f' :frequency = dat.toDouble();
break;
case 'T' : //Tastverhaeltnis
case 't' :ratio = dat.toInt();
break;
} // switch(cmd)
if(ratio > 100) ratio = 100; // Grenzwerte werden ueberprueft
if(frequency < 20) frequency = 20;
if(frequency > 20000) frequency = 20000;
controlGenerator();
} // else { // if !((cmd == 'M') || (cmd == 'm'))
displayValues(true); //aktuelle Werte ausgeben
Serial.print("Kommando M,F,T : ");
} // if (Serial.available() > 0)
} // void loop()
// void loop()
Display More
Codeänderungen:
Diese Änderungen waren bei meiner verwendeten Hardware nötig, um fehlerfreie Displayausgaben und Tastatureingaben zu erzielen.
Hardwareseitige Anpassung ist gegenüber der Original-Version ein "Umzug" vom Display RS-Pin auf den Pin 8 (GPIO33) des von mir verwendetem
ESP-WROOM-32 USB-C 38PIN DEV-Board.
Zuzdem war es eine gute Idee, auf den 1k-Widerstand zu verzichten, der parallel zum 3,9k-Widerstand sitzt. Dadurch erhöt sich der Spannungsunterschied zwischen den unterschiedlichen Tastendrücken und das Gerät reagiert damit sehr viel besser auf die linke Minus-Taste.
Hier einige Bilder der Finalen Version.
Damit es zu keinen Rauchzeichen kommt, da die Verkabelung zwischen den Geräten schlecht zu sehen ist:
Ich wünsche allen Interessenten viel Freude beim Nachbau mit kostengünstigen Bauteilen, die auch in Deutschland erhältlich sind.
Die nötigen 3D-Duck-Dateien sind hier kostenlos erhältlich:
Den auf dem letzten Bild zu sehende "Servotester mit Oszilloskop" bitte hier weiter verfolgen:
Hardware: https://derrcmodellbauer.de/produkt-kategorie/platine/
Software: https://github.com/TheDIYGuy999/Servotester_Deluxe/tree/main
Vielen Dank an dieses Forum für die mir zur Verfügung gestellten Informationen !
Grossartige Arbeit, gefällt mir.
Aber könntest Du den Code im "Codeblock" ablegen statt in Bildern?
Dann muss man nicht alles vom Bild abtippen.
Und was ist das für ein Mini-Oszi?
Danke und MfG
Jürgen
Moin Pieper,
danke für deinen ausführlichen Bericht!
73 de Bernd
Grossartige Arbeit, gefällt mir.
Aber könntest Du den Code im "Codeblock" ablegen statt in Bildern?Dann muss man nicht alles vom Bild abtippen.
Und was ist das für ein Mini-Oszi?
Danke und MfG
Jürgen
Oben gewünschte Änderungen mit Erfolg durchgeführt
Don’t have an account yet? Register yourself now and be a part of our community!