Böpdisplayen

From Chalmers Robotförening
Revision as of 17:06, 25 June 2018 by Antbern (talk | contribs)

En sida om den omtalade böpdisplayen!

Böpdisplayen


Uppbyggnad/Displaygränssnitt

Displayen är uppbyggd av ett flertal seriekopplade skiftregister där varje databit bestämmer om ett segment i en siffra skall vara tänt (1) eller släckt (0). Eftersom varje siffra endast består av 7 segment och varje skiftregister har 8 utgångar krävs det att man efter att ha skiftat ut varje siffras på/av-segment (totalt 7 bitar) även skiftar ut en "dummybit" för att alla bitar ska stämma överens med respektive segment.

Skiftregisterna är av typen MC74HC4094A ([1])


Inbyggd Styrenhet

Displayen har en egen inbyggd styrenhet som sköter uppdatering av innehållet. Den är byggd med en Arduino Nano.

Kommunikation

Kommunikation med den inbyggda styrenheten sker via en enkelriktad (simplex) seriell länk med följande specifikationer:

  • Hastighet: 115200 baud
  • Databitar: 8 st
  • Paritetetsbitar: Inga (None)
  • Stopbitar: 1 st

Detta motsvarar upplägget 8N1 och är standard i Arduinobiblioteket. Den seriella länken finns tillgänglig via en BNC-kontakt på styrenheten (yttre = jord, inre = signal).

OBS! Eftersom Arduino Nano:n kör med spänningen 5 Volt är det viktigt att inte skicka in mer än det via serielänken!

Kommandon

Styrenheten accepterar flera kommandon via den seriella länken. Gemensamt är att de alla består av ett kommandotecken följt av en eller flera datatecken (x). Accepterade datatecken är siffrorna 0-9, alla andra tecken behandlas som blanksteg och ger en tom siffra.

  • Sxxxxxxxx – uppdaterar hela displayen med nya datatecken xxxxxxxx.
  • [A-H]x – uppdaterar motsvarande displaysiffra [A-H, se bild (W.I.P.)] med innehållet som direkt följer bokstaven.
  • L – ställer in längden på hur många siffror som uppdateras när ett I-kommando tas emot. Uppstartsvärdet (default) är 8 och innebär att I-kommandot alltid skriver över alla siffror och genererar inledande nollor. Antalet siffror som önskas följer L i kommandosekvensen. För att styrenheten skall veta när alla siffror tagits emot krävs det att det sista tecknet ej är en siffra.
  • I[A-H] – skriver ett heltal (integer) till displayen med start på det angivna segmentet. Antalet segment som uppdateras anges via L-kommandot. Heltalets siffror följer I i kommandosekvensen och även här krävs det att det sista tecknet ej är en siffra.
  • W – inaktiverar automatisk uppdatering. I vanliga fall uppdateras displayen automatiskt när något av kommandona som ändrar innehållet mottagits, men efter detta kommando behöver det göras manuellt med R-kommandot. Detta läge är bra då man tänker göra många små uppdateringar innan allt innehåll är färdigskickat och minskar "flicker" på displayen.
  • Q – aktiverar automatisk uppdatering.
  • R – uppdaterar den fysiska displayen med det lagrade segmentinnehållet (alltså en manuell uppdatering).

Exempel: Du vill skriva ut tid i formatet HH:MM:SS med start vid den tredje siffran (alltså högerjusterat). Först görs följande initiala inställningar:

  1. W - inaktivera automatisk uppdatering
  2. Sxxxxxxxx - tömmer displayen (x tolkas som ett mellanslag, kan vara vilket annat tecken som ej är en siffra här så länge det är åtta stycken)
  3. L2x - ställer in I-kommandot för två siffor. x är återigen ett tecken som inte är en siffra.

Varje gång du vill uppdatera med ny tid används följande kommandon:

  1. ICHHx - skriver antalet timmar med start på siffra C (den tredje), HH kan även vara endast H då antalet timmar är < 10. Inledande nollor sätts in automatiskt av styrenheten.
  2. IEMMx - samma som ovan fast för minuter
  3. IGSSx - samma som ovan fast för sekunder
  4. R - skriver ut det mottagna innehållet på displayen

Hårdvara

Pinout för styrenheten

På bilden till höger syns hur kontakterna på styrkortet är kopplade. Uppe till vänster kommer +12V och omvandlas till +5V för displayen och styrenheten. Displayen ansluts och styrs med kontakten uppe till höger. Kontakten längst ner till höger är till för kommunikation med styrenheten och bryter ut GND (jord), +5V, RX och TX från Arduino Nano:n. De seriella kommandona skickas till RX-pinnen (recieve), ingen data returneras för tillfället på TX-pinnen. Observera att styrenheten och apparaten som skickar kommandon måste sammankopplas via jord för att kommunikationen ska fungera. På bilden syns hur den monterade BNC-kontakten är inkopplad.


Mjukvara

Mjukvaran, i form av en Arduinosketch, ges nedan. Finns tillgänglig här

<syntaxhighlight lang="c" line='line'> /* Author: Anton Berneving Copyright 2018

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

  • /


// pin definitions and macros for the display interface

  1. define PIN_CLK PB1
  2. define PIN_DATA PB2
  3. define PIN_STB PB3
  1. define PIN_HIGH(x) PORTB |= (1 << (x))
  2. define PIN_LOW(x) PORTB &= ~(1 << (x))

// variables for keeping track of which state we are in

  1. define STATE_NONE 0
  2. define STATE_STRING 1
  3. define STATE_SINGLE 2
  4. define STATE_INTEGER 3

char current_state = STATE_NONE; char serial_index = 0; char integer_length = 8; char auto_refresh = 1;

// table of characters for the display 0-9, A-Z unsigned char digits[] = { 0b01111110, // zero 0b00011000, // one 0b00110111, // two 0b00111101, // three 0b01011001, // four 0b01101101, // five 0b01101111, // six 0b00111000, // seven 0b01111111, // eight 0b01111001, // nine 0b00000000, // empty };

// stores the character in each place in the display

  1. define DISPLAY_DIGITS 8

unsigned char display_content[DISPLAY_DIGITS] = {10, 10, 10, 10, 10, 10, 10, 10};

void setup() { // initialize serial communication Serial.begin(115200);

// set display pins as output DDRB |= (1 << PIN_CLK) | (1 << PIN_DATA) | (1 << PIN_STB);

// initial states PIN_LOW(PIN_CLK); PIN_LOW(PIN_DATA); PIN_LOW(PIN_STB);

# show all segments at startup setDisplayContent(88888888, 0, 8); refreshDisplayContent();

delay(2000); clearDispForce(); }

void loop() { // do serial communication while(Serial.available() > 0){ // read a character char data = Serial.read();

// take action based on current state switch(current_state){ case STATE_NONE: if(data >= 'A' && data <= 'H'){ // Single Edit Command current_state = STATE_SINGLE; serial_index = data - 'A';

} else if (data == 'S'){ // is this a "String" command? // yes, move to correct state and reset the index counter current_state = STATE_STRING; serial_index = 0;

} else if (data == 'I'){ // "Integer" command current_state = STATE_INTEGER;

} else if (data == 'R'){ // "Force Refresh" command refreshDisplayContent();

} else if (data == 'L'){ // set integer length command integer_length = Serial.parseInt();

} else if (data == 'W'){ // disable auto refresh command auto_refresh = 0;

} else if (data == 'Q'){ // enable auto refresh command auto_refresh = 1;

} break;

case STATE_STRING: // read input digit data = data - '0'; if(data < 0 || data > 9) data = 10; display_content[serial_index] = (unsigned char)data;

// increment index (move to next position) (this could all be done with pointers...) serial_index++;

// if we are done filling all positions, apply change if(serial_index >= DISPLAY_DIGITS){ current_state = STATE_NONE;

// update display if(auto_refresh == 1) refreshDisplayContent(); } break;

case STATE_SINGLE: // read input digit data = data - '0'; if(data < 0 || data > 9) data = 10; display_content[serial_index] = (unsigned char)data;

// no longer in "single" mode current_state = STATE_NONE;

// apply changes if(auto_refresh == 1) refreshDisplayContent(); break;

case STATE_INTEGER: if(data >= 'A' && data <= 'H'){ // this is where to start the integer (with leading zeros and configurable length) serial_index = data - 'A';


long val = Serial.parseInt();

setDisplayContent(val, serial_index, integer_length); if(auto_refresh == 1) refreshDisplayContent(); }else current_state = STATE_NONE;

break; } } }

// sets the display content as a number starting at index start_index with the specified length. Leading zeros are inserted automatically void setDisplayContent(long number, char start_index, char length){ for(int i = start_index + length - 1; i >= start_index; i--){

// skip wrong indexes if(i > 7 || i < 0) continue;

display_content[i] = number % 10; number /= 10; } }

// writes the characters of display_contents to the display void refreshDisplayContent(){ //check if any of the characters is a space... char hasSpace = 0; for(int i = 0; i < 8; i++){ if(display_content[i] == 10){ hasSpace = 1; break; } }

// if the contents contain a space, enable strobe line during the shift out (does not work properly otherwise) if(hasSpace){ PIN_HIGH(PIN_STB); }

// write the eight digits shiftDisplayContent();

// strobe data strobeDisp(); }

void shiftDisplayContent(){

 for(int i = DISPLAY_DIGITS - 1; i >= 0; i--){
   
   shift_out_char7(digits[display_content[i]], i == 0);
 }

}

void strobeDisp(){

 PIN_HIGH(PIN_STB);
 PIN_LOW(PIN_STB);

}

// fill display with zeroes (note: this happens continuously on the display since STB must be active for writing all zeroes unfortunately...) void clearDispForce(){ PIN_LOW(PIN_DATA); PIN_HIGH(PIN_STB); for(int i = 0; i < 8*DISPLAY_DIGITS; i++){ PIN_HIGH(PIN_CLK); PIN_LOW(PIN_CLK); } PIN_LOW(PIN_STB); }


/* Shift the lower 7 bits of the provided character to the display

  • /

void shift_out_char7(unsigned char data, bool last){ for(unsigned char i = 0; i < (last ? 7 : 8); i++){

// write data if(data & 0x01) // lsb bit == 1 ? PIN_HIGH(PIN_DATA); else PIN_LOW(PIN_DATA);

// pulse clock PIN_HIGH(PIN_CLK); PIN_LOW(PIN_CLK);

// shift data data = data >> 1; } }

</syntaxhighlight>