Kent Display

Mijn eerste echte Arduinoproject was de intervaluino. Aangestoken door Freek, maakte ik een Time-laps-machine voor mijn Digitale SLR. Het bleek al snel dat batterijen het niet lang uithouden, als je er allemaal lampjes en LEDjes opzet. Daarom zocht ik een een energiezuinig scherm.

Deze Kent Display houdt zijn beeld vast zonder stroom. Je kunt hem dus als een soort ad hock display gebruiken.

Ik heb dit scherm bij Sparkfun.com gekocht. Ik ben gelukkig het bijbehorend breakout-board niet vergeten. Aansluiten op de Arduino is dan een makkie.

De Arduino sketch die op de website van Sparkfun staat, deed het niet bij mij. Gelukkig vond ik een andere versie, waarop een beeld op de Display getoverd wordt. Vanuit deze sketch ben ik verder gaan programmeren. Ik heb nu alle functies uit de datasheet toegevoegd en daarbovenop de volgende functies geschreven:

  1. WRITEtxt(string, x, y, attribuut) ==>  schrijft een string tekst vanaf positie x en y. Attribuut 0 = pixels uit, 1 = pixels aan, 2 = XOR pixels
  2. LINE(x,y,x,y,attribuut) ==> lijn van x1, y1 naar x2, y2
  3. RECT(x,y,breedte, hoogte, attribuut)
  4. simpleCIRCLE(x, y, radius, attribuut)
  5. CIRCLE( complexe kromme met vele parameters   )
  6. PICTURE(y) ==> laad data in geheugen vanaf y positie (0-1091).



In de originele sketch werd de BUSY-pin van de Kent Display niet benut. Hierdoor was het maar wachten of de display klaar was voor de volgende opdracht. Ik heb alle onnodige delays die hier het gevolg van waren kunnen weghalen door netjes naar de BUSY-pin te kijken.

Het geheugen laat zich aanspreken met een hoogAdres en een LaagAdres. Twee keer een byte van acht bits. Het geheugen is 128 rijen van 256 bytes groot. Dus 256 x 128 = 32.768 bytes = 32 KB (kilo bytes).

Het beeldscherm is 240 x 160 pixels. Acht horizontale pixels gaan in één byte. Dus het scherm is 160 rijen van 30 bytes groot. Dus 30 x 160 = 4.800 bytes.

Het is makkelijk het geheugen te beschouwen als 1092 rijen van 30 bytes. Dit is ook 32 KB, maar heeft de vorm van 1092 / 160 = 6,825 schermen onder elkaar.

Omdat elke opdracht naar het geheugen een hoogAdres en een LaagAdres nodig heeft, moet er een vertaalslag gemaakt worden. Deze gaat als volgt:

  • x scherm = tussen 0 – 29
  • y scherm = tussen 0 – 1091 ( = 6,825 schermen van 160 rijen)
  • plek in het geheugen: PLEK = y * 30 + x
  • hoogAdres = PLEK / 256
  • laagAdres = PLEK modulus 256

De truck zit hem in de modulus, deze geeft mooi aan waar op de laatste rij de geheugenplaats x, y zich bevindt. Vermeld moet worden dat alles achter de punt vervalt in computerland. 255 / 256 is dus 0.

een byte op 140 (x) en 100 (y) positie geeft de volgende rekensom:

  • PLEK = 100 * 30 + 140 = 440
  • hoogAdres = 440 / 256 = 1  (dus niet 1,71875)
  • laagAdres = 440 % 256 = 184

De communicatie tussen de Arduino en de Kent Display, verloopt via SPI. Bij dit protocol dat meestal via Arduino-pin 11, 12 & 13 verloopt, wordt er altijd tegelijk gezonden en ontvangen. Omdat we het scherm voornamelijk voor uitput gebruiken, kunnen we alle ontvangen bytes zo de prullenbak ingooien. Alleen wanneer we het geheugen van het display willen uitlezen, wordt de retourgezonden byte interessant.

Hier de sketch : www.huinink.info/code01.ino

Hier de belangrijkste functies:

byte mask[] = {B10000000, B01000000, B00100000, B00010000, B00001000, B00000100, B00000010, B00000001};

void POINT(uint16_t x, uint16_t  y, int attr){
   uint16_t  memPoint = (y * 240) + x;
   uint16_t  memByte = memPoint / 8;
   uint16_t  memBit = memPoint - memByte * 8;

   uint16_t  pHigh  = memByte/0x100;
   uint16_t  pLow = memByte%0x100;

   switch (attr){
     case 0:
       CLEAR_BITS(pHigh, pLow, mask[memBit]);
       break;
     case 1:
       SET_BITS(pHigh, pLow, mask[memBit]);
       break;
     case 2:
       XOR_BITS(pHigh, pLow, mask[memBit]);
       break;
   }
}

void LINE(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, int attribuut){

  int difX = x2-x1;
  int difY = y2-y1;
  int attrib = attribuut;
if (abs(difX) > abs(difY)){ // if x-scope of line is bigger than y-scope, then use x-axis to count pixels ( i.e. f(x) = y )
  if (x2 < x1){  // turn into a left to right orientation
      uint16_t x0 = x1;
               x1 = x2;
               x2 = x0;
      uint16_t y0 = y1;
               y1 = y2;
               y2 = y0;
  }
  for (int xCount = 0.0; xCount < (difX+1); xCount++){
      int yy = (((xCount + 0.0) * difY) / difX);
      POINT(x1 + xCount, y1 + yy, attrib);
    }
}

else {
    if (y2 < y1){  // turn into a left to right orientation
      uint16_t x0 = x1;
               x1 = x2;
               x2 = x0;
      uint16_t y0 = y1;
               y1 = y2;
               y2 = y0;
  }
  for (int yCount = 0.0; yCount < (difY+1); yCount++){
      int xx = (((yCount + 0.0) * difX) / difY);
      POINT( x1 + xx, y1 + yCount, attribuut);
  }
}
}

void CIRCLE(int x, int y, int rad, float start, float einde, int attribuut, float freqX, float freqY, float aspectX, float aspectY, float snelheid){
 float tr;
 for (  tr = start*(2*314); tr < einde*(2*314); tr+=snelheid){
// Serial.println(tr);
 //int tijd = int(tr*1000);
 float dx = aspectX * rad*cos(tr/200*freqX);
 float dy = aspectY * rad*sin(tr/200*freqY);
  float xx=x+dx;
  float yy=y+dy;
  POINT( xx, yy, attribuut);
}
}

void SimpleCIRCLE(int x, int y, int rad, int attribuut){
 float tr;
 for (  tr = 0; tr < 2*314; tr++){
// Serial.println(tr);
 //int tijd = int(tr*1000);
 float dx = rad*cos(tr/100);
 float dy = rad*sin(tr/100);
  float xx=x+dx;
  float yy=y+dy;
  POINT( xx, yy, attribuut);
}
}

void WRITEtxt(String tekst, int x, int y, int karS){
  int lengte = tekst.length();
  for(int tekens = 0; tekens < lengte; tekens++){
    char ascii = tekst.charAt(tekens);
    Serial.println(ascii);
    int tekstX = x+1+tekens % 30;
    int tekstY = y+1+((tekens / 30));
    writeAsciiKent(1+ascii, karS, tekstX, tekstY);
  }
}

void RECT(int x, int y, int dx, int dy, int attribuut){
LINE(x,y,x+dx,y,attribuut);
LINE(x,y,x,y+dy,attribuut);
LINE(x+dx,y,x+dx,y+dy,attribuut);
LINE(x,y+dy,x+dx,y+dy,attribuut);

}
void PICTURE(int y){
  uint8_t HighAddress= (y*30)/ 0x100;
  uint8_t LowAddress= (y*30) % 0x100;
 //Serial.print(pgm_read_byte(&rawData[achtPix]));
 select();
  spi_transfer(0x00);         // Transmit command
  spi_transfer(HighAddress);  // High byte of the target memory
  spi_transfer(LowAddress);   // Low byte of the target memory
 for (int achtPix = 64; achtPix < 5184; achtPix++){
   if (((achtPix-64) % 0x20) < 0x1E){
  spi_transfer(pgm_read_byte(&rawData[achtPix]));//
 }
 }
  deselect();
}

 

2 Responses to Kent Display

  1. Peter says:

    Hoi Paul,
    Ik vind jouw uitdaging, en wat ik er achter voel, steeds weer een geniale inval, bedankt voor deze trigger. Dat je dit doet betekent best veel voor mij.
    Interessant, boeiend en uitdagend. Ik weet wat je/het doet maar voorlopig voor mij nu nog even “Oh Ja, zo zat/ging dat”. Maar het zaadje is geplant, nu nog water geven.
    Peter

  2. Pingback: Reverse GeoCaching | Huinink-Jongen

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>