Después de haber añadido un amplificador en la segunda parte con el que se puede ajustar la amplitud así como el offset, ahora queremos hacer la operación directamente en el dispositivo en lugar de usar el monitor en serie.
Por ejemplo, es adecuada la pantalla LCD1602 Display Keypad Shield, que tiene una pantalla LCD con dos líneas de 16 caracteres y botones para su funcionamiento. Este escudo fue diseñado para el Arduino. Para facilitar su uso con el ESP32, sustituimos el ESP32 DevKit CV4 de la primera parte por la placa ESP32 D1 R32, que tiene sus pines de conexión dispuestos de la misma manera que un Arduino Uno. Así que podemos conectar el escudo directamente en él. Sin embargo, los ejemplos no funcionarán porque el ESP32 tiene diferentes pines IO que el Arduino. Además, cabe señalar que las entradas del ESP32 sólo toleran 3,3 V.
Las figuras muestran el pinout de la placa ESP32 D1 R32 y el pinout del escudo del teclado LCD. Sólo están etiquetados los alfileres que se usan realmente.
Como todos los pines de la LCD se usan como entradas del controlador de la LCD, no tenemos que preocuparnos por el voltaje, porque las entradas funcionarán con seguridad incluso con 3,3V.
La situación es algo diferente con la conexión de los pulsadores. Dependiendo del botón pulsado, entrega un valor entre 0 y 5V. En el Escudo la salida está conectada al terminal de +5V a través de una resistencia de 2 kOhm. Si ahora soldamos una resistencia de 3,9 kOhm del conector del botón y del conector de 0V, el voltaje máximo en este conector es sólo 5V * 3900 / (2000 + 3900) = 3,3V. En la foto se puede ver esta resistencia.
Tenemos otro problema con el conector GPIO12. Debe ser ajustado a 0V durante el proceso de arranque, de lo contrario el voltaje de funcionamiento de la memoria flash se cambia a 1.8V. Una vez más, una resistencia de 10 kohms entre GPIO12 y GND ayuda aquí. Pero mejor soldamos esta resistencia a la placa ESP32 D1 R32, porque este problema también puede ocurrir con otros escudos de Arduino.
Figura 1: Asignación de pines del ESP32 D1 R32
Figura 2: Pinout de la pantalla del teclado LCD (sólo se utilizan los conectores)
Figura 3: Panel trasero ESP32 D1 R32 con resistencia de 10 kOhm entre GPIO12 y GND
Hardware requerido
Aquí están todas las partes de nuevo, también las de la segunda parte.
Número | Parte | Nota |
---|---|---|
1 | Junta ESP32 D1 R32 | |
2 | Escudo LCD Keypad | |
1 | Resistencia 3.9 kOhm | |
1 | Resistencia 10 kOhm | |
1 | LM358 Amplificador de operación dual | Desde la Parte 2 |
2 | Potentiómetro 10 kOhm con eje de 4 mm | Desde la Parte 2 |
1 | Resistencia 1 kOhm R3 | Desde la Parte 2 |
1 | Resistencia 1,5 kOhm R5 | Desde la Parte 2 |
1 | Resistencia 2.2 kOhm R2 | Desde la Parte 2 |
2 | Resistencia 100 kOhm R1 y R4 | Desde la Parte 2 |
1 | barra de plumas 7-polvo | |
1 | barra de plumas 6-polvo | |
3 | barra de pin 3-polo | Desde la Parte 2 |
1 | barra de pin 2-polar | Desde la Parte 2 |
3 | Jumper cable de cable femenino / femenino 3 polos | |
2 | Jumper cable cable femenino / femenino 2 polo | |
1 | Placa de la cuadrícula PCB o agujero 30 x 40 mm | Desde la Parte 2 |
1 | Conversor de impulso de CC DC con entrada de voltaje positiva y negativa 5V, salida +/- 5V | Desde la Parte 2 |
1 | BNC consumido | |
2 | botones para potenciómetros | |
1 | Botones de la impresora 3D con filamento TPU | |
4 | Casos y piezas a distancia de la impresora 3D con filamento PLA |
|
8 | tornillos de hoja 2.2 x 6.5 mm | |
4 | tornillos de hoja 2.2 x 9.5 mm |
El software
/*
* Funktionsgenerator für Sinus, Dreieck und Rechteck Signale
* Einstellbare Frequenz 20 Hz bis 20 KHz
* Für Dreieck und Rechteck einstellbares Tastverhältnis 0 bis 100%
*/
//Bibliotheken zum direkten Zugriff auf Steuerregister des ESP32
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "soc/rtc.h"
//Bibliotheken zur Verwendung des Digital zu Analog Konverters und für den I2S-Bus
#include "driver/dac.h"
#include "driver/i2s.h"
//Bibliothek für das LCD Display
#include <LiquidCrystal.h>
#define SINFAKT 127.0 //gemessen für Schrittweite = 1 und kein Vorteiler (8.3MHz)
#define SIGNALOUT 26 //Pin für 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 für Tasten
#define KEYS A12
//Spannungsteiler für Tasten
#define Rv 2000 //Vorwiderstand
#define R3 3900 //Schutzwiderstand für maximal 3.3 V
#define Rp 1000 //Widerstand GPIO2 gegen Masse
#define Rr 0 //Spannungsteiler bei gedrückter RIGHT Taste
#define Ru 330 //Spannungsteiler bei gedrückter UP Taste
#define Rd 950 //Spannungsteiler bei gedrückter DOWN Taste
#define Rl 1950 //Spannungsteiler bei gedrückter LEFT Taste
#define Rs 5250 //Spannungsteiler bei gedrückter 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
//Änderungstypen
#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 für Taster
uint16_t Ur, Uu, Ud, Ul, Us;
//Buffer zum Erstellen der Dreieckfunktion
uint32_t buf[128];
//Einstellwerte für Kurvenform, Frequenz und Tastverhältnis
int8_t mode = MSINUS; //0=Sinus, 1=Rechteck, 2=Dreieck
float frequency = 1000; //20 bis 200000 Hz
int8_t ratio = 50; //Tastverhältnis 0 bis 100%
int8_t edit = EMODE; //was wird verändert 0=Mode 1=Frequenz 2=Tastverhältnis
uint32_t tic; //Für Wartezeit
int8_t lastKey = 0; //zuletzt ermittelte Taste oder 0 wenn keine
uint16_t step = 0; //Schrittweite für Frequenzerhöhung
float ftmp; //Variable zum Speichern der Frequenz während der Einstellung
int16_t rtmp; //Variable zum Speichern des Tastverhältnis während der Einstellung
int8_t mtmp; //Variable zum Speichern der Betriebsart während der Einstellung
//Flag Ist wahr, wenn die Initialisierung bereits erfolgte
bool initDone = false; //Konfiguration für 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 unterstützt nur Stereo
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S_MSB, //Standard Format für I2S
.intr_alloc_flags = 0, // Standard Interrupt
.dma_buf_count = 2, //Anzahl der FIFO Buffer
.dma_buf_len = 32, //Größe der FIFO Buffer
.use_apll = 0 //Taktquelle
}; //Buffer für Dreieck Wellenform füllen
//Parameter up ist die Dauer für den Anstieg in Prozent
//Parameter sz gibt die Buffergröße für eine Periode an
//es werden die Werte für eine Periode in den Buffer geschrieben
void fillBuffer(uint8_t up, uint8_t sz) {
uint8_t down; //Zeit für die fallende Flanke in %
uint32_t sample; //32Bit Datenwort (I2S benötigt zwei Kanäle mit je 16 Bit
float du,dd,val; //Hilfsvariablen
down=100-up;
//Anzahl der Schritte für 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
//Amplitudenänderung pro Schritt für Anstieg und Abfall
du = 256.0/stup;
dd = 256.0/stdwn;
//füllen des Buffers
val = 0; //Anstieg beginnt mit 0
for (i=0; i<stup; i++) {
sample = val;
sample = sample << 8; //Byte in das höherwertige Byte verschieben
buf[i]=sample;
val = val+du; //Wert erhöhen
}
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;
}
} //Alle Ausgänge stoppen
void stopAll(){
ledcDetachPin(SIGNALOUT);
i2s_driver_uninstall((i2s_port_t)0);
dac_output_disable(DAC_CHANNEL_2);
dac_i2s_disable();
initDone=false;
} //Kurvenform Rechteck starten
//Pin für Signalausgang zuweisen
void startRectangle(){
ledcAttachPin(SIGNALOUT,1 );
initDone=true;
} //Frequenz für Rechteck setzen mit entsprechendem Tastverhältnis
void rectangleSetFrequency(double frequency,uint8_t ratio)
{
ledcSetup(1,frequency,7); //Wir nutzen die LEDC Funktion mit 7 bit Auflösung
ledcWrite(1,127.0*ratio/100); //Berechnung der Schrittanzahl für Zustand = 1
}
//Dreiecksignal starten
void startTriangle(){
i2s_set_pin((i2s_port_t)0, NULL); //I2S wird mit dem DAC genutzt
initDone=true;
}
//Frequenz für Dreieck setzen mit entsprechendem Tastverhältnis
double triangleSetFrequency(double frequency,uint8_t ratio)
{ int size=64;
//zuerst wird die geeignete Buffergröße ermittelt
//damit die Ausgabe funktionier muss die I2S Abtastrate zwischen
//5200 und 650000 liegen
if (frequency<5000) {
size = 64;
} else if (frequency<10000) {
size = 32;
} else if (frequency<20000) {
size = 16;
} else {
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 füllen
fillBuffer(ratio,size*2);
//und einmal ausgeben
i2s_write_bytes((i2s_port_t)0, (const char *)&buf, size*8, 100);
return frequency;
}
//Sinusausgabe vorbereiten
void startSinus(){
//Ausgang für 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;
}
//Frequenz für Sinus setzen
double sinusSetFrequency(double frequency)
{ //Formel f = s * SINFAKT / v
//s sind die Schritte pro Taktimpuls
//v ist der Vorteiler für 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 gewählt
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;
}
}
}
//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;
}
//Einstellungsänderungen durchführen
void controlGenerator() {
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;
}
}
//Anzeige aktualisieren
//Wenn monitor wahr ist, erfolgt die Ausgabe
//auch auf die serielle Schnittstelle
void displayValues(boolean monitor) {
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;
}
//Betriebsart ausgeben lcd.setCursor(0,0); lcd.print(" "); lcd.print(ba); if (monitor) { Serial.println("**************** Eingestellte Werte *************************"); Serial.print("Betriebsart = "); Serial.println(ba); } //Frequenz je nach Wert als Hz oder kHz if (frequency < 1000){ sprintf(buf,"%6.2f Hz",frequency); } else { sprintf(buf,"%6.2fkHz",frequency/1000); } //Frequenz ausgeben lcd.setCursor(0,1); lcd.print(" F"); lcd.print(buf); if (monitor) { Serial.print("Frequenz = "); Serial.println(buf); } sprintf(buf,"%2i%%",ratio); //Tastverhältnis ausgeben lcd.setCursor(11,1); lcd.print(" T"); lcd.print(buf); if (monitor) { Serial.print("Tastverhältnis = "); Serial.println(buf); Serial.println(); } //je nach Edit-Mode Pfeilzeichen ausgeben switch (edit) { case EMODE: lcd.setCursor(0,0); break; case EFREQUENCY: lcd.setCursor(0,1); break; case ERATIO: lcd.setCursor(11,1); break; } lcd.print(char(126)); }
//Edit Mode ändern mit UP und DOWN Taste
void changeEdit(boolean up) {
//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 temporären Werte
//für die Änderung kopieren
ftmp = frequency;
rtmp = ratio;
mtmp = mode;
//Geänderte Editmode ausgeben
Serial.print("Mode = ");Serial.println(mode);
//Anzeige aktualisieren ohne Ausgabe auf serielle
//Schnittstelle
displayValues(false);
}
//Betriebsart ändern mit RIGHT und LEFT Taste
void changeMode(boolean up) {
//je nach Richtung Schritte positiv oder negativ
int s = up?1:-1;
//temporäre Betriebsart ändern
mtmp += s;
//Wenn das Ende erreicht wird wieder auf Anfang springen
if (mtmp < 0) mtmp = 2;
if (mtmp > 2) mtmp = 0;
//Geänderte 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;
}
}
//Frequenz ändern mit RIGHT und LEFT Taste
void changeFrequency(boolean up) {
//war die Taste vorher nicht gedrückt, wird die Schrittweite auf 1 gesetzt
//während die Taste gedrückt 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;
//temporäre Frequenz ändern
ftmp = ftmp+s*step;
//auf Minimal- und Maximalwerte prüfen
if (ftmp < 20) ftmp=20;
if (ftmp > 20000) ftmp = 20000;
char buf[15];
//Für die Anzeige Hz oder kHz
if (ftmp > 999) {
sprintf(buf,"%6.2fkHz",ftmp/1000.0);
} else {
sprintf(buf,"%6.2f Hz",ftmp*1.0);
}
//Geänderte Frequenz am Display anzeigen
lcd.setCursor(2,1);
lcd.print(buf);
}
//Tastverhältnis ändern mit RIGHT und LEFT Taste
void changeRatio(boolean up) {
//Richtung festlegen
int8_t jx = up?1:-1;
//Temporäres Tastverhältnis ändern
rtmp = rtmp+jx;
//auf Minimal- und Maximalwerte prüfen
if (rtmp < 0) rtmp=0;
if (rtmp > 100) rtmp = 100;
char buf[15];
//Geändertes Tastverhältnis am Display anzeigen
sprintf(buf,"%2i%%",rtmp);
lcd.setCursor(13,1);
lcd.print(buf);
}
//der Funktionsgenerator wird auf die geänderte Einstellung gesetzt
//die temporären Werte werden in die Aktuellen Werte übernommen
void setValues() {
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;
}
//Funktionsgenerator selber ändern
controlGenerator();
displayValues(true);
}
//Tastaturspannung einlesen und auswerten
void handleKeys() {
//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;
}
}
if ((key == SELECT) && (lastKey == NONE)) setValues();
lastKey = key;
tic = millis();
}
//Serielle Schnittstelle aktivieren und
//Defaulteinstellungen 1kHz Sinus setzen
//Schwellwerte für Tastatur festlegen
void setup()
{
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 für Taster berechnen
//Diese Berechnung ist notwendig, da die Toleranzen sehr
//gering sind, und die Schwellwerte von der Betriebsspannung
//abhängen.
//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 für UP Taste
Rtmp = Rin * Ru / (Rin + Ru);
Uup = Ub * Rtmp / (Rtmp + Rv);
//Mittelwert für DOWN Taste
Rtmp = Rin * Rd / (Rin + Rd);
Udn = Ub * Rtmp / (Rtmp + Rv);
//Mittelwert für LEFT Taste
Rtmp = Rin * Rl / (Rin + Rl);
Ulf = Ub * Rtmp / (Rtmp + Rv);
//Mittelwert für 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 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;
}
if (nm != mode) { //Nur wenn eine Änderung vorliegt, muss was getan werden
stopAll();
mode=nm;
controlGenerator();
}
} else {
//bei den anderen Befehlen folgt ein Zahlenwert
String dat = inp.substring(1);
//je nach Befehl, werden die Daten geändert
switch (cmd) {
case 'F' :
case 'f' :frequency = dat.toDouble(); break; //Frequenz
case 'T' :
case 't' :ratio = dat.toInt(); break; //Tastverhältnis
}
//Grenzwerte werden überprüft
if (ratio > 100) ratio = 100;
if (frequency < 20) frequency = 20;
if (frequency > 20000) frequency = 20000;
controlGenerator();
}
//aktuelle Werte ausgeben
displayValues(true);
Serial.print("Kommando M,F,T : ");
}
}
La parte para el generador de funciones y para el funcionamiento a través de la interfaz en serie es idéntica al esquema de la parte 1. Lo que es nuevo es el funcionamiento a través de los botones del escudo del teclado de la pantalla. Especialmente la evaluación de los botones requiere una atención especial.
Figura 4: Divisor de tensión para las teclas del escudo del teclado LCD y a la izquierda la placa ESP32 D1 R32 con la resistencia paralela
El divisor de voltaje está diseñado para que a 5V la diferencia entre los voltajes individuales sea de aproximadamente 1V. Pero como tenemos la resistencia de protección de 3900 ohmios incorporada, de modo que la salida nunca alcanza más de 3,3V, esta diferencia se reduce. Pero el problema se hace aún más grande. La placa ESP32 D1 R32 tiene una resistencia de 1kOhm en paralelo con la entrada de GPIO2, de modo que sólo es necesario conectar GPIO0 a GND para el parpadeo. Esta resistencia reduce aún más la diferencia de voltaje. La tabla muestra los voltajes que se obtienen de los pulsadores (sin resistencia paralela, con 3900 Ohm en paralelo y con 3900 Ohm y 1000 Ohm en paralelo.
Taster | Ohne Parallelwiderstand | 3900 Ohm | 3900 Ohm und 1000 Ohm |
---|---|---|---|
Kein Taster | 5,00 V | 3,30 V | 2,20 V |
SELECTO | 3,60 V | 2,60 V | 2,04 V |
LEFT | 2,50 V | 1,97 V | 1,10 V |
DOWN | 1,60 V | 1,4 V | 0,89 V |
UP | 0,70 V | 0.66 V | 0.52 V |
DERECHO | 0.0V | 0.0V | 0.0V |
Las pruebas han demostrado que el voltaje de 5V no es muy exacto. Con una fuente de alimentación externa el voltaje era de 4,4V, con la alimentación a través de USB entre 5,0V en un puerto de ordenador y 5,2V en un cargador USB.
Si se utilizaran valores de umbral establecidos para cada botón, el dispositivo funcionaría o no dependiendo del voltaje de funcionamiento. Para garantizar una función segura, la tensión de funcionamiento se determina por lo tanto al encenderse sin necesidad de pulsar un botón. A partir de esto se pueden calcular los voltajes medios de cada botón. Dado que la consulta para la evaluación de la clave es menor, el mejor resultado se obtiene añadiendo la mitad de la distancia al siguiente valor medio superior al voltaje medio para obtener el valor umbral.
El alojamiento
Para aumentar la utilidad, hemos diseñado una carcasa que puede ser producida con una impresora 3D. La carcasa está construida de tal manera que todas las partes pueden caber en su interior.
Figura 5: Alojamiento de la impresora 3D
En la parte inferior se puede ver el hueco rectangular para la pantalla y las aberturas para los botones. A su lado, a la derecha, están los dos agujeros para los potenciómetros. En la pared derecha hay un agujero para el enchufe BNC y en la pared izquierda un hueco rectangular para el enchufe USB y para el enchufe de la fuente de alimentación.
Se proporcionan cuatro cilindros espaciadores con agujeros para montar los circuitos.
La tapa a juego no tiene ningún hueco y simplemente se puede poner.
Los botones están dispuestos en una placa base común para facilitar el montaje. Para que puedan ser presionados individualmente, deben ser impresos con un filamento elástico, por ejemplo TPU. Si esto no es posible, los botones individuales deben ser separados de la placa base después de la impresión para que puedan ser presionados individualmente.
Figura 6: Botones de la impresora 3D
Por último, hay dos espaciadores que se deben insertar entre el escudo y la pantalla para evitar que la pantalla se ponga tensa cuando se atornille.
Figura 7: Escudo del teclado del LCD, instalación de los espaciadores
Enlaces para descargar los archivos de impresión 3D:
Parte inferior de la carcasa, tapa, perillas, espaciador 1, espaciador 2
Montaje
El escudo del teclado LCD tiene almohadillas de soldadura libres, además de los cabezales utilizados para conectarse a la placa ESP32 D1 R32, que están conectados en paralelo con los cabezales. Los usamos para conectar los módulos externos a la placa. Para ello tenemos que equiparlos con cabezas de alfiler también.
Figura 8: Cabezas de alfiler adicionales en la pantalla del teclado LCD
Tampoco hay que descuidar la resistencia protectora de 3,9 kOhm.
Comenzamos el montaje con los pomos. Se introducen en los correspondientes huecos de la carcasa. Siguen los dos potenciómetros con cables de conexión. Para los cables de conexión cortamos un cable de puente de 3 polos por la mitad y lo soldamos a los potenciómetros. Un alivio de tensión con una atadura de cable es ventajoso.
Figura 9: Instalación de los botones en la carcasa
Figura 10: Instalación de los potenciómetros y la toma BNC (salida de señal)
Un cable de puente de 2 polos también se corta por la mitad y se suelda al enchufe BNC.
Cuando se instalan los dos potenciómetros y la toma BNC, el escudo del teclado LCD puede fijarse en la carcasa con cuatro tornillos. No te olvides de insertar los dos espaciadores entre la pantalla y el tablero. También el módulo transformador de voltaje y el módulo amplificador pueden ser fijados con cuatro tornillos cada uno.
Figura 11: Carcasa con pantalla de teclado LCD, convertidor DC-DC, amplificador operacional y cableado
Ahora se pueden conectar los cables según el diagrama de cableado.
Figura 12: Diagrama de cableado
El último paso es conectar la placa ESP32 D1 R32 en el escudo del teclado LCD.
Figura 13: Parte inferior de la carcasa con el conjunto completo
Ahora sólo hay que poner la tapa y montar las perillas para los potenciómetros, entonces nuestro generador de funciones está listo para su uso. Para la alimentación podemos usar un cargador USB o una fuente de alimentación de 6 a 12 V.
Figura 14: Generador de funciones terminadas en el osciloscopio
Buena suerte con la reconstrucción.
9 comentarios
Christian Magg
Ein anspruchsvolles Projekt, das ist mir klar, aber ich komme über diese Fehlermeldung: “CONFLICT! driver_ng is not allowed to be used with the legacy driver” einfach nicht hinaus. Nach meinen Recherchen liegt das an dem i2s.h driver. Der scheint die alte Version zu sein, aber wenn ich die 3 notwendigen neuen einbaue (i2s_std, i2s_pdm und i2s_tdm) bekomme ich das Programm nicht mehr kompiliert. Ich wäre jetzt wirklich für jede Hilfe dankbar. Im Voraus schon mal vielen Dank!
Andreas Wolter
@lorenz: ich vermute, dass es eine Änderung im ESP Core gab. Die Methode ledcDetachPin() gehört zur LED Control (LEDC) Library und ist für PWM Steuerung gedacht. Es scheint, als hätte man an der Stelle Änderungen vorgenommen. Ich würde zuerst versuchen, sie umzubenennen in ‘ledcDetach’
Infos dazu hier: https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/ledc.html
Es könnte auch helfen, auf eine ältere Version der Bibliotheken zurückzugehen. Das ist leider ein sehr häufig auftretendes Problem bei der Verwendung der ESP Bibliotheken.
Grüße,
Andreas Wolter
AZ-Delivery Blog
lorenz
Hallo,
ich habe den Fehler.
‘ledcDetachPin’ was not declared in this scope
Wie kann ich das ändern?
Gruß Lorenz
Andreas Wolter
@Bastlersiggi: wahrscheinlich wurde die Bibliothek verändert. Es gibt die Funktion i2s_write(). Damit könnte es wieder funktionieren. Oder zur älteren Version zurückkehren.
https://github.com/earlephilhower/ESP8266Audio/issues/273
Mit freundlichen Grüßen,
Andreas Wolter
AZ-Delivery Blog
Bastlersiggi
Der obige Sketch ergibt bei mir folgende Fehlermeldung:
FunktionsgeneratorESP32.ino:
In function ‘double triangleSetFrequency(double, uint8_t)’:
146:2: error: ‘i2s_write_bytes’ was not declared in this scope
i2s_write_bytes((i2s_port_t)0, (const char )&buf, size8, 100);
^~~~~~~~~~~~~~~
i2s_write_expand
exit status 1
Compilation error: ‘i2s_write_bytes’ was not declared in this scope
Verwendete IDE: 2.3.2 Board: ESP32 WROOM
Welchen Code kann man stattdessen verwenden bzw. den Sketch aktualisieren?
Gerald Lechner
Hallo Claus, GPIO12 ist schon richtig. Es ist nicht GPIO02 gemeint. Der Pin GPIO12 dient beim ESP32 dazu, die Versorgungsspannung für den Flash-Speicher zu steuern. Ist GPIO12 beim Bootvorgang auf High, beträgt die Versorgungsspannung 1.8 V, bei LOW 3.3V. Daher muss GPIO12 beim Booten auf LOW sein.
Das mit dem Widerstand ist richtig. Beim ESP32 Board könnte er weggelassen werden. Ich habe ihn auf das Display gelötet, da ich dieses auch mit der ESP8266 Version genutzt habe. Bei diesem Board ist dieser Anschluss mit A0 verbunden. A0 ist aber ein hochohmiger Eingang, der dann mit 5V beschädigt würde. Mit dem 3.9 kOhm Widerstand ist man auf der sicheren Seite und es funktionieren die Tasten trotzdem problemlos. Der Funktionsgenerator funktioniert natürlich nur mit dem ESP32!
Claus Teubner
Hallo,
wie immer, sehr schöner Artikel.
Heute habe ich den versteckten Tipfehler :-) gefunden.
‘…Ein weiteres Problem haben wir mit dem GPIO12 Anschluss. Der muss während des Bootvorgangs auf 0V liegen…’
Sollte doch GPIO2 statt GPIO12 Anschluss sein.
Außerdem sollte doch der 3,9kOhm Widerstand direkt am ESP32 reichen, dann bleibt das Display unverändert und wenn beides zusammengesteckt ist, ist sichergestellt, dass nur max. 3,3V am ESP anliegen. Falls die 3,9kOhm zum booten nicht reicht, tut es auch der 1kOhm alleine, dann sind die Taster-Spannungen etwas größer.
Bernd Albrecht
@Sven: Der D1 R32 ist “baulich” am Uno angelehnt. Die Uno Shields können also aufgesteckt werden. Aber es gibt deutliche Unterschiede bei der Pinbelegung, der gravierendste ist die zulässige Spannung von 3,3V beim D1 R32. Um das LCD Keypad Shield zu verwenden, müssen deshalb wie beschrieben die zusätzlichen Widerstände angelötet werden.
Sven Waibel
Hallo,
wieso steht beim LCD, den ihr verlinkt habt, dass dieser nicht mit dem D1 R32 kompatibel ist?
Grüße
Sven