Es gibt Situationen, in denen man seinen Arduino-Sketch debuggen möchte, aber den USB-Serial-Anschluß aus unterschiedlichen Gründen nicht benutzen kann. Für diesen Fall habe ich diesen Debugmonitor gebaut:
Dieser Monitor wird mit nur 3 Anschlüssen mit dem Master-Arduino verbunden : Ground, VCC +5V und Serial-In. Er besteht aus einem Arduino ProMicro und einem LCD Display Nokia 5110.
Als TX-Ausgabe-Pin kann im Master-Arduino jeder beliebige Pin verwendet werden. Für die Sende-Funktion ist keine Interupt-Funktionalität erforderlich. Als RX-Pin kann man ebenfalls jeden freien Pin deklarieren. Es werden vom Debug-Monitor keine Daten gesendet.
Das Display umfaßt 6 Zeilen mit 14 Zeichen. Zahlen können in einem größeren Font dargestellt werden. In diesem Font können nur die Ziffern 0 bis 9 und das Minus-Zeichen ausgegeben werden.
Das Display rollt automatisch. Man kann also fortwährend Zeile um Zeile zum Monitor schicken. Carriage return und linefeed werden erkannt und verarbeitet.
Durch das automatische Rollen sind immer die letzten 6 Zeilen sichtbar. Das Rollen beschränkt sich jedoch auf die normale Schrift. Die Zahlen in der Großschrift werden nicht mit gerollt.
Man kann mit einer Reihe von Kommandos die Anzeige steuern: Bildschirm löschen, Cursor positionieren, Font umschalten. Auf dem Arduino ProMirco gibt es zwei Leds (gelb und grün). Diese können zusätzlich gesteuert werden : ein, aus und blinken.
Die Kommandos werden in ein Start- und ein Endezeichen eingeschlossen (im Beispiel „$“ und „#’“). Dazwischen können mehrere Kommandos stehen, sie werden durch ein Trennzeichen (im Beispiel „,“) getrennt.
Beispiel :
mySerial.print(“$CLS,Y3,X15,LGEE#Hallo Welt“);
Der Bildschirm wird gelöscht, auf Zeile 4, X-Position Pixel 16 positioniert und die gelbe Led eingeschaltet. Dann wird „Hallo Welt“ ausgegeben.
Im Programm kann man die Vereinbarung der Steuerzeichen Start- Ende- und Trennzeichen leicht ändern. Um keine druckenden Zeichen zu verlieren, bieten sich kleine Zahlen an, die im Print-Statement des Mutter-Arduino oktal geschrieben werden. Das sieht dann z.B. so aus “ … \001CLS\002 …“ usw.
Die Kommandos und die Anschlußbelegung des Arduino ProMicro und Nokia LCD 5110 sind im Coding dokumentiert.
Hier das Coding (ohne Fonts) :
//---------------------------------------------------------------------- // Debug Monitor mit LCD Nokia 5110 //---------------------------------------------------------------------- // // Vorlage zum Ansteuern des Display war ein Programm von Nathan Seidle, // Spark Fun Electronics; vom 17.07.2011; Beerware license // // Ich schulde ihm Dank und ein großes Bier ;-) // // // PinBelegung Arduino Pro Micro : // Achtung es gibt verschiedene Versionen des Nokia Displays! // Unbedingt Pinbelegung vergleichen !! // // Tx0 // Rx1 Serial Eingang zum TX des Master Arduino // Gnd // Gnd // D2 // D3 SCLK Clk LCD Pin 7 Clock // D4 SDIN Din LCD Pin 6 MOSI // D5 DC LCD Pin 5 Command-Control // D6 RST LCD Pin 4 Reset // D7 SCE CE LCD Pin 3 Chip enabled // D8 // D9 // RAW // Gnd Gnd LCD Pin 2 // RST // VCC VCC LCD Pin 1 // A3 // A2 // A1 // A0 // D15 // D14 // D16 // D10 // Zusätzlich : // LCD LED Pin 8 über 470 Ohm an Vcc // VCC und Gnd zum Master Arduino //---------------------------------------------------------------------- // Nokia LCD-Display 5110 #define PIN_SCE 7 #define PIN_RESET 6 #define PIN_DC 5 #define PIN_SDIN 4 #define PIN_SCLK 3 #define LCD_COMMAND 0 #define LCD_DATA 1 //---------------------------------------------------------------------- // Zeichengroesse im Textmode : #define ZEICHEN_WW 6 #define ZEICHEN_HH 8 // Displaygröße in Pixel #define LCD_WW 84 #define LCD_HH 48 //Displaygröße in Anzahl Zeichen (Textmode) #define ANZ_ZEICHEN LCD_WW/ZEICHEN_WW #define ANZ_ZEILEN LCD_HH/ZEICHEN_HH // Index Grenzen #define MAX_ZEICHEN_IDX ANZ_ZEICHEN-1 #define MAX_ZEILEN_IDX ANZ_ZEILEN-1 //---------------------------------------------------------------------- // Scrollbuffer char ScrollBuffer[ANZ_ZEILEN][ANZ_ZEICHEN]; byte ScrollFlag = 0; //---------------------------------------------------------------------- /// Die Fonts sind im Include definiert daher : extern uint8_t ASCII[][5]; extern uint8_t NUMS[]; //---------------------------------------------------------------------- // Input Mode 0 = Zeichen einlesen und anzeigen, // 1 = Kommando einlesen #define IM_TEXT 0 #define IM_CMD 1 byte InputMode = IM_TEXT; #define FONT_TEXT 0 #define FONT_BIGNUM 1 byte DisplayFont = FONT_TEXT; char RecvChar; byte Cursor_x; // Pixel byte Cursor_y; // Zeilen a 8 Pixel //---------------------------------------------------------------------- // gruene und gelbe LED steuern #define LED_AUS 0 #define LED_EIN 1 #define LED_BLINK 2 byte led_mode_gelb = LED_AUS; byte led_mode_gruen = LED_AUS; unsigned int Next_Led_blink = 0; //---------------------------------------------------------------------- // Kommandos interpretieren #define CMD_MAXLEN 8 char cmdBuffer[CMD_MAXLEN]; byte cmdPointer; #define CMD_START '$' #define CMD_SEPARATOR ',' #define CMD_ENDE '#' // Kommandos z.B. $CLS,Y2# = Bildschirm löschen // und auf Zeile 3 positionieren // ohne CrLf senden ! // CLS Clear screen // Xnn X-Position auf Pixel nn (0-83) // Yn Y-Position auf Zeile n (0-5) // FT Font Text // FN Font Nummern gross // LGNE gruene LED ein // LGNA gruene LED aus // LGNB gruene LED blinken // LGEE gelbe LED ein // LGEA gelbe LED aus // LGEB gelbe LED blinken //---------------------------------------------------------------------- // Routinen declarieren void LCDInit(void); void LCDClear(void); void gotoXY(int x, int y); void LCDCharacterText(char character); void LCDCharacterBigNumber(char character); void LCDWrite(byte data_or_command, byte data); byte PickZahl(char * p); void LED_Handling(); void clr_scroll_buffer(); void scroll(); void init_cmdBuffer(); //---------------------------------------------------------------------- void setup() { Serial1.begin(9600); LCDInit(); delay(100); LCDClear(); delay(100); Next_Led_blink = millis()+300; } //---------------------------------------------------------------------- void loop() { byte tmp_byte; if (Serial1.available()){ RecvChar = Serial1.read(); if (InputMode == IM_TEXT){ switch (RecvChar){ case CMD_START: init_cmdBuffer(); InputMode = IM_CMD; break; case 13: // Wagenruecklauf Cursor_x=0; gotoXY(Cursor_x,Cursor_y); break; case 10: // Zeilenvorschub if(ScrollFlag == 1){ scroll(); } if( Cursor_y<MAX_ZEILEN_IDX){ Cursor_y++; gotoXY(Cursor_x,Cursor_y); } else { Cursor_y = MAX_ZEILEN_IDX; ScrollFlag = 1; } break; default : if (DisplayFont == FONT_TEXT ){ if(ScrollFlag == 1){ scroll(); } LCDCharacterText(RecvChar); } else { LCDCharacterBigNumber(RecvChar); } } } else { // im Command-Modus if (RecvChar == CMD_ENDE || RecvChar == CMD_SEPARATOR){ if (RecvChar == CMD_ENDE){ InputMode = IM_TEXT; } if (strncmp(cmdBuffer,"CLS",3)==0){ LCDClear(); } if (strncmp(cmdBuffer,"FT",2)==0){ DisplayFont = FONT_TEXT; } if (strncmp(cmdBuffer,"FN",2)==0){ DisplayFont = FONT_BIGNUM; } if (cmdBuffer[0]=='X'){ tmp_byte = PickZahl(&cmdBuffer[1]); if (tmp_byte<(LCD_WW - ZEICHEN_WW)){ Cursor_x=tmp_byte; gotoXY(Cursor_x,Cursor_y); } } if (cmdBuffer[0]=='Y'){ tmp_byte = PickZahl(&cmdBuffer[1]); if (tmp_byte<=MAX_ZEILEN_IDX){ Cursor_y=tmp_byte; gotoXY(Cursor_x,Cursor_y); } } if (strncmp(cmdBuffer,"LGNA",4)==0){ led_mode_gruen = LED_AUS; } if (strncmp(cmdBuffer,"LGNE",4)==0){ led_mode_gruen = LED_EIN; } if (strncmp(cmdBuffer,"LGNB",4)==0){ led_mode_gruen = LED_BLINK; } if (strncmp(cmdBuffer,"LGEA",4)==0){ led_mode_gelb = LED_AUS; } if (strncmp(cmdBuffer,"LGEE",4)==0){ led_mode_gelb = LED_EIN; } if (strncmp(cmdBuffer,"LGEB",4)==0){ led_mode_gelb = LED_BLINK; } init_cmdBuffer(); } else{ if (cmdPointer < (CMD_MAXLEN-1)){ cmdBuffer[cmdPointer++]=RecvChar; } } } } LED_Handling(); } //---------------------------------------------------------------------- // 1 oder 2 Stellige Zahl einlesen byte PickZahl(char * p){ byte tmp = 0; if (*p>='0' && *p<='9') tmp = *p-'0'; p++; if (*p>='0' && *p<='9') tmp = tmp*10 + *p-'0'; return tmp; } //---------------------------------------------------------------------- // LED schalten, ein / aus / blinken (300ms) void LED_Handling(){ static byte blinky = 0; if (millis()<Next_Led_blink) return; Next_Led_blink = millis()+300; blinky = 1- blinky; if (led_mode_gelb == LED_BLINK){ if (blinky) RXLED0; else RXLED1; } if (led_mode_gelb == LED_AUS) RXLED0; if (led_mode_gelb == LED_EIN) RXLED1; if (led_mode_gruen == LED_BLINK){ if (blinky) TXLED0; else TXLED1; } if (led_mode_gruen == LED_AUS) TXLED0; if (led_mode_gruen == LED_EIN) TXLED1; } //---------------------------------------------------------------------- void clr_scroll_buffer(){ byte z,s; for (z=0;z<=MAX_ZEILEN_IDX;z++) for (s=0;s<=MAX_ZEICHEN_IDX;s++) ScrollBuffer[z][s]=' '; ScrollFlag=0; } //---------------------------------------------------------------------- void scroll(){ byte z,s; int index; char character; // alle Zeilen um eine Zeile nach oben for (z=1;z<=MAX_ZEILEN_IDX;z++){ gotoXY(0,z-1); for (s=0;s<=MAX_ZEICHEN_IDX;s++){ character=ScrollBuffer[z][s]; ScrollBuffer[z-1][s]=character; for (index=0; index<5; index++){ LCDWrite(LCD_DATA, ASCII[character - 0x20][index]); } LCDWrite(LCD_DATA, 0x00); } } // unterste Zeile löschen for (s=0;s<=MAX_ZEICHEN_IDX;s++) ScrollBuffer[MAX_ZEILEN_IDX][s]=' '; gotoXY(0,MAX_ZEILEN_IDX); for (index=0; index<LCD_WW; index++){ LCDWrite(LCD_DATA, 0x00); } Cursor_y=MAX_ZEILEN_IDX; gotoXY(Cursor_x,Cursor_y); ScrollFlag = 0; } //---------------------------------------------------------------------- void init_cmdBuffer(){ int i; cmdPointer = 0; for (i=0;i<CMD_MAXLEN;i++) cmdBuffer[i]=0; } //---------------------------------------------------------------------- void LCDInit(void) { // This sends the magical commands to the PCD8544 // Configure control pins pinMode(PIN_SCE, OUTPUT); pinMode(PIN_RESET, OUTPUT); pinMode(PIN_DC, OUTPUT); pinMode(PIN_SDIN, OUTPUT); pinMode(PIN_SCLK, OUTPUT); // Send command to commands to the PCD8544 // Reset the LCD to a known state digitalWrite(PIN_RESET, LOW); digitalWrite(PIN_RESET, HIGH); // configurate the LCD LCDWrite(LCD_COMMAND, 0x21); //Tell LCD that extended commands follow LCDWrite(LCD_COMMAND, 0xB0); //Set LCD Vop (Contrast): Try 0xB1(good @ 3.3V) or 0xBF if your display is too dark LCDWrite(LCD_COMMAND, 0x04); //Set Temp coefficent LCDWrite(LCD_COMMAND, 0x14); //LCD bias mode 1:48: Try 0x13 or 0x14 LCDWrite(LCD_COMMAND, 0x20); //We must send 0x20 before modifying the display control mode LCDWrite(LCD_COMMAND, 0x0C); //Set display control, normal mode. 0x0D for inverse } //---------------------------------------------------------------------- void LCDClear(void) { for (int index = 0 ; index < (LCD_WW * LCD_HH / 8) ; index++) LCDWrite(LCD_DATA, 0x00); // LCD löschen clr_scroll_buffer(); Cursor_x=0; Cursor_y=0; gotoXY(Cursor_x,Cursor_y); } //---------------------------------------------------------------------- void gotoXY(int x, int y) { LCDWrite(0, 0x80 | x); // Column LCDWrite(0, 0x40 | y); // Row } //---------------------------------------------------------------------- void LCDCharacterText(char character) { if (character>=' ' && character <= 0x7f ){ for (int index = 0 ; index < 5 ; index++) LCDWrite(LCD_DATA, ASCII[character - 0x20][index]); LCDWrite(LCD_DATA, 0x00); //Senkrechter Abstandsstrich // Zeichen in Scroll-Tabelle eintragen if (Cursor_y <= MAX_ZEILEN_IDX && (Cursor_x/ZEICHEN_WW) <= MAX_ZEICHEN_IDX) ScrollBuffer[Cursor_y][Cursor_x/ZEICHEN_WW] = character; // Position mitrechnen Cursor_x+=ZEICHEN_WW; } } //---------------------------------------------------------------------- void LCDCharacterBigNumber(char character) { // Nur Ziffern '0' bis '9' und '-' sind definiert. // 12 Pixel breit und 16 Pixel hoch // Keine Berücksichtigung für Scrolling char myChar; if (Cursor_y>0){ if (character=='-')myChar = '9'+1; else myChar = character; if (myChar>='0' && myChar <= '9'+1){ // Obere Hälfte ausgeben gotoXY(Cursor_x,Cursor_y-1); for (int index = 0 ; index < 12 ; index++) LCDWrite(LCD_DATA, NUMS[(myChar - 48)*24+index]); // Untere Hälfte ausgeben gotoXY(Cursor_x,Cursor_y); for (int index = 0 ; index < 12 ; index++) LCDWrite(LCD_DATA, NUMS[(myChar - 48)*24+index+12]); Cursor_x+=12; } } } //---------------------------------------------------------------------- void LCDWrite(byte data_or_command, byte data) { //There are two memory banks in the LCD, data/RAM and commands. This //function sets the DC pin high or low depending, and then sends //the data byte digitalWrite(PIN_DC, data_or_command); //Send the data digitalWrite(PIN_SCE, LOW); shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data); digitalWrite(PIN_SCE, HIGH); }
Ausblick : Man kann natürlich noch mehr Ausgabe-Elemente einbauen und über Kommandos steuern z.B. weitere Leds oder einen Lautsprecher oder Summer usw.
Tolle Idee.
Hallo, habe damit sofort diverse Anfängerprobleme.
„Kommandos und die Anschlußbelegung des Arduino ProMicro und Nokia LCD 5110 sind im Coding dokumentiert“
Toll, der Code ist aber nicht für einen Micro oder Nano
sondern Leonardo oder ähnliches.
Beim Übersetzen für den Micro oder Nano kommen folgende Fehler:
Serial1.begin(9600); ok, ist wohl ab Mega, also Serial.begin
nächster Fehler ist, if (blinky) TXLED0
Das kriege ich nur weg wenn ich als Board „Leonardo“ wähle.
Was mache ich falsch, habe ich falsch verstanden?
Das Bild zeigt doch deutlich die verwendete Hardware,
die ich auch zum kompilieren angebe
Gerd
Hallo Gerd,
.
der Sketch läuft auf einem Arduino Pro Micro,. In der Arduino SDK muß als Board Micro gewählt werden.
.
Wenn Du ein anderes Board nutzen willst und Probleme mit der Anpassung hast, melde dich bitte. Ich muß schon wissen welches Board Du hast und wo es klemmt.
.
Hubert
Danke Hubert,
habe die mal IDE erneuert und schon klappte es.
Gerd
Hallo,
gute Idee. werde ich mal nachbauen.
Muss nicht ein 3.3V Arduino Pro Micro verwendet werden?
Das würde doch sicher auch mit einem Pro Mini funktionieren. Die serielle Schnittstelle wird ja gar nicht benötigt.
Hallo Reinhard,
.
ich habe einen Arduino Pro Micro mit 5V verwendet. Die serielle Schnittstelle wird benötigt..
.
Hubert
Ich dachte das Nokia 5110 Display ist nicht für 5V sondern für 3.3V ausgelegt?
Hallo Reinhard,
.
mein Nokia 5110 Display kann mit 5 V oder 3,3 V betrieben werden.
.
Hubert
Hallo könnten sie eine abgeänderte Version für einen Nano machen? Folgende Fehler kriege ich.
LCD5110_Debug.ino: In function ‚void setup()‘:
LCD5110_Debug:157: error: ‚Serial1‘ was not declared in this scope
LCD5110_Debug.ino: In function ‚void loop()‘:
LCD5110_Debug:169: error: ‚Serial1‘ was not declared in this scope
LCD5110_Debug.ino: In function ‚void LED_Handling()‘:
LCD5110_Debug:289: error: ‚RXLED0‘ was not declared in this scope
LCD5110_Debug:290: error: ‚RXLED1‘ was not declared in this scope
LCD5110_Debug:292: error: ‚RXLED0‘ was not declared in this scope
LCD5110_Debug:293: error: ‚RXLED1‘ was not declared in this scope
LCD5110_Debug:296: error: ‚TXLED0‘ was not declared in this scope
LCD5110_Debug:297: error: ‚TXLED1‘ was not declared in this scope
LCD5110_Debug:299: error: ‚TXLED0‘ was not declared in this scope
LCD5110_Debug:300: error: ‚TXLED1‘ was not declared in this scope
‚Serial1‘ was not declared in this scope
Hallo Cihan,
.
ersetze Serial1 durch Serial. Dann entferne alles was mit RXLEDn TXLEDn zu tun hat.
Dann läuft das auch auf dem Nano.
.
Viel Erfolg Hubert
Klasse Idee!
Pingback http://thdarduino.blogspot.de/2016/07/arduino-debugger-mit-nokia-5110-lcd.html