OLED128x64bigNoScope

OLED 128x64 SSD1306 or SH1106 os SSD1309 on I2C. PB8=SCL PB9=SDA needs a single foactory/gpio/i2c/config object
Author:
License: LGPL
Github: tiar/HW/OLED128x64bigNoScope.axo

Inlets

charptr32 line1

charptr32 line2

charptr32 line3

charptr32 line4

charptr32 line5

charptr32 line6

charptr32 line7

charptr32 line8

bool32 disable

Outlets

None

Attributes

combo type

combo I2CADDR

Declaration
enum SSD1306 {
  LCDWIDTH = 128,
  LCDHEIGHT = 64,
  SETCONTRAST = 0x81, // 2byte cmd,256 contrast steps, reset = 7F
  DISPLAYON = 0xAF,   // entire display on
  DISPLAYOFF = 0xAE,  // entire display off
  DISPLAYALLON_RESUME = 0xA4,
  NORMALDISPLAY = 0xA6,     // invert = 0xA7
  DEACTIVATE_SCROLL = 0x2E, // stop control scroll conf by 26 27 29 2A
  MEMORYMODE =
      0x20, // 2byte cmd, 0 horizontal, 1 vertical, 2 page addressing, 3 invalid
  COLUMNADDR = 0x21,     // 3bytes, start, end (included) valid in horizontal or
                         // vertical mode
  PAGEADDR = 0x22,       // 3bytes, start, end (included) valid in horizontal or
                         // vertical mode
  SETSTARTLINE_0 = 0x40, // set display RAM start line at 0
  SEGREMAP = 0xA0,       // segment remap 0 mapped to SEG0
  SETMULTIPLEX = 0xA8,   // 2 byte cmd, set mux ratio
  COMSCANDEC = 0xC8, // scan from COM[N-1] to COM[0] (0xC0 is COM0 to COM[N-1])
  SETDISPLAYOFFSET = 0xD3,   // 2 byte cmd, vertical shift
  SETCOMPINS = 0xDA,         // 2 byte cmd, seq com pin conf, left right remap
  SETDISPLAYCLOCKDIV = 0xD5, // 2 byte cmd, low nibble A[3:0]+1 = div ratio,
                             // high nibble A[7:4] freq, reset 1000b
  SETPRECHARGE =
      0xD9, // 2 byte cmd, precharge period A[3:0] phase1 A[7:4] phase2, reset:2
  SETVCOMDETECT = 0xDB, // 2 byte Vcomh deselect level A[6:4] 000b 0.65xVcc 010b
                        // 0.77(reset) 011b 0.83
  CHARGEPUMP = 0x8D,    // Enable charge pump seq: 0x8D, 0x14, 0xAF (Charge pump
                        // setting, enable charge pump, display on)
  EXTERNALVCC = 0x1,
  SWITCHCAPVCC = 0x2
};
uint8_t const nibbleToByte[16] = {
    0b00000000, 0b00000011, 0b00001100, 0b00001111, 0b00110000, 0b00110011,
    0b00111100, 0b00111111, 0b11000000, 0b11000011, 0b11001100, 0b11001111,
    0b11110000, 0b11110011, 0b11111100, 0b11111111};

uint8_t *txbuf;
uint8_t *rxbuf;

uint8_t text[21 * 8];    // text inputs
uint8_t textBuf[21 * 8]; // text inputs copy (to avoid flicker)
bool disable;
// SETUP
// ------------------
void cmd(uint8_t c) {
  txbuf[0] = 0;
  txbuf[1] = c;
  i2cMasterTransmitTimeout(&I2CD1, attr_I2CADDR, txbuf, 2, rxbuf, 0, 30);
}
void cmd(uint8_t c1, uint8_t c2) {
  cmd(c1);
  cmd(c2);
}
void cmd(uint8_t c1, uint8_t c2, uint8_t c3) {
  cmd(c1, c2);
  cmd(c3);
}

// _____________________________________________________________________
void fill(uint8_t v) {
  i2cAcquireBus(&I2CD1);
  cmd(COLUMNADDR, 0, 127); // Column start end
  cmd(PAGEADDR, 0, 7);     // Page start end
  txbuf[0] = 0x40;
  for (int i = 1; i < 129; i++)
    txbuf[i] = v;
  for (int p = 0; p < 8; p++) {
    i2cMasterTransmitTimeout(&I2CD1, attr_I2CADDR, txbuf, 129, rxbuf, 0, 30);
  }
  i2cReleaseBus(&I2CD1);
}

/* returns i
 */
int drawTxt(int i, int NBC, uint8_t *tb, int page, uint8_t *tPage) {
  for (int nc = 0; nc < NBC; nc++) {
    int ascii_32 = tb[nc] - ' ';
    const uint8_t *adChar = tiar_font5x8 + ascii_32 * 5;
    for (int slice = 0; slice < 5; slice++) { // slices are one pixel wide
      tPage[i] = adChar[slice];
      i++;
    }
    tPage[i] = 0;
    i++; // separator space 1 pixel wide => 6 pixels per char
  }
  return i;
}
// _____________________________________________________________________
// text
// returns a page to be sent to the display based on contents of text
void calcTextPage(int page, uint8_t tPage[128]) {
  int i = 0;
  uint8_t *tb = textBuf + 21 * page;
  if (tb[0] >= ' ') { // full text line
    i = drawTxt(i, 21, tb, page, tPage);
    for (; i < 128; i++) {
      tPage[i] = 0;
    }                      // space padding
  } else if (tb[0] == 1) { // strbar

    i = drawTxt(i, 10, tb + 2, page, tPage);

    while (i < 63) {
      tPage[i] = 0;
      i++;
    }                  // space padding
    if (tb[1] < 128) { // positive value => filled bar
      tPage[i] = 0;
      i++;
      for (i = 64; i < 63 + tb[1] && i < 128; i++) {
        tPage[i] = 0b11111000;
      }
    } else { // negative value => hollow bar
      tPage[i] = 0b11111000;
      i++;
      for (i = 64; i < 63 + 256 - tb[1] && i < 127; i++) {
        tPage[i] = 0b10001000;
      }
      tPage[i] = 0b11111000;
      i++;
    }
    for (; i < 128; i++) {
      tPage[i] = 0;
    }                      // space padding
  } else if (tb[0] == 2) { // bar
    if (tb[1] <= 128) {    // positive value => filled bar
      tPage[i] = 0;
      i++;
      for (i = 0; i < tb[1]; i++) {
        tPage[i] = 0b01111100;
      }
    } else { // negative value => hollow bar
      tPage[i] = 0b01111100;
      i++;
      for (; i < 256 - tb[1]; i++) {
        tPage[i] = 0b01000100;
      }
      tPage[i] = 0b01111100;
      i++;
    }
    for (; i < 128; i++) {
      tPage[i] = 0;
    } // space padding

  } else if (tb[0] == 3) { // strbar2
    i = drawTxt(i, 10, tb + 3, page, tPage);

    for (; i < 63; i++) {
      tPage[i] = 0;
    } // space padding

    uint8_t filledBar = 0b00001110;
    uint8_t hollowBar = 0b00001010;
    int v = tb[1];
    if (v < 128) { // positive value => filled bar
      for (i = 63; i < 63 + v && i < 128; i++) {
        tPage[i] = filledBar;
      }
    } else { // negative value => hollow bar
      tPage[i] = filledBar;
      i++;
      for (i = 64; i < 63 + 256 - v && i < 127; i++) {
        tPage[i] = hollowBar;
      }
      tPage[i] = filledBar;
      i++;
    }
    for (; i < 128; i++) {
      tPage[i] = 0;
    } // space padding

    filledBar = 0b11100000;
    hollowBar = 0b10100000;
    v = tb[2];
    if (v < 128) { // positive value => filled bar
      for (i = 63; i < 63 + v && i < 128; i++) {
        tPage[i] |= filledBar;
      }
    } else { // negative value => hollow bar
      i = 63;
      tPage[i] |= filledBar;
      i++;
      for (; i < 63 + 256 - v && i < 127; i++) {
        tPage[i] |= hollowBar;
      }
      tPage[i] |= filledBar;
      i++;
    }

  }
  //_______________________________________________________
  else if (tb[0] == 4) { // bar dble
    uint8_t filledBar = 0b00001110;
    uint8_t hollowBar = 0b00001010;
    int v = tb[1];
    if (v <= 128) { // positive value => filled bar
      for (i = 0; i < v; i++) {
        tPage[i] = filledBar;
      }
    } else { // negative value => hollow bar
      i = 0;
      tPage[i] = filledBar;
      i++;
      for (; i < 256 - v; i++) {
        tPage[i] = hollowBar;
      }
      tPage[i] = filledBar;
      i++;
    }
    for (; i < 128; i++) {
      tPage[i] = 0;
    } // space padding

    filledBar = 0b11100000;
    hollowBar = 0b10100000;
    v = tb[2];
    if (v <= 128) { // positive value => filled bar
      for (i = 0; i < v; i++) {
        tPage[i] |= filledBar;
      }
    } else { // negative value => hollow bar
      i = 0;
      tPage[i] |= filledBar;
      i++;
      for (; i < 256 - v; i++) {
        tPage[i] |= hollowBar;
      }
      tPage[i] |= filledBar;
      i++;
    }
  }
}
// _____________________________________________________________________
// opt function draw

// _____________________________________________________________________
void sendPage(int page) {
  /*
    Note: I consider that having a little flickering is not a big deal
    (compared to potential audio glitches).
    So, i do not use the chSysLock() chSysUnlock to protect the memcpy.
  */
  i2cAcquireBus(&I2CD1);
  // prepare transmission to the "page"
  cmd(COLUMNADDR, 0, 127);   // Column start end
  cmd(PAGEADDR, page, page); // Page start end
  if (attr_type == 1106) {
    cmd(0xB0 + page);     // set page address
    cmd(2 & 0xf);         // set lower column address
    cmd(0x10 | (2 >> 4)); // set higher column address
  }
  i2cReleaseBus(&I2CD1);

  // 8 text lines
  // on the beginning of drawing (page 0) we update the buffers
  if (page == 0) {
    memcpy(textBuf, text, 21 * 8);
  }
  calcTextPage(page, txbuf + 1);

  // transmission of the page

  // transmit the page
  txbuf[0] = 0x40;
  i2cAcquireBus(&I2CD1);
  i2cMasterTransmitTimeout(&I2CD1, attr_I2CADDR, txbuf, 129, rxbuf, 0, 30);
  i2cReleaseBus(&I2CD1);
}
// _____________________________________________________________________
void init() {
  i2cAcquireBus(&I2CD1);
  // Init sequence
  if (attr_type == 1106 || attr_type == 1306) {
    cmd(DISPLAYOFF);
    // 2 byte cmd,
    //  low nibble A[3:0]+1 = div ratio,
    //  high nibble A[7:4] freq  reset 1000b
    cmd(SETDISPLAYCLOCKDIV, 0x80);
    cmd(SETMULTIPLEX, LCDHEIGHT - 1);
    cmd(SETDISPLAYOFFSET, attr_type == 1306 ? 0x00 : 0x01);
    cmd(SETSTARTLINE_0);
    cmd(MEMORYMODE, 0x00); // horizontal
    cmd(SEGREMAP | 0x1);
    cmd(COMSCANDEC);
    // 128 x 64
    cmd(SETCOMPINS, 0x12);
    cmd(SETCONTRAST, 0xCF);
    cmd(SETPRECHARGE, 0xF1);
    cmd(SETVCOMDETECT, 0x40);
    cmd(DISPLAYALLON_RESUME);
    cmd(NORMALDISPLAY);
    cmd(DEACTIVATE_SCROLL);
    cmd(CHARGEPUMP, 0x14);
    cmd(DISPLAYON);
  } else {
    cmd(DISPLAYOFF);
    cmd(SETDISPLAYCLOCKDIV, 0xA0); // vs 80
    cmd(SETMULTIPLEX, 0x3F);
    cmd(SETDISPLAYOFFSET, 0x00);
    cmd(SETSTARTLINE_0);
    cmd(MEMORYMODE, 0x00);
    cmd(SEGREMAP | 0x1);
    cmd(COMSCANDEC);
    cmd(SETCOMPINS, 0x12);
    cmd(SETCONTRAST, 0x6F); // vs 0xCF
    cmd(SETPRECHARGE,
        0xD3); // vs 0xF1 		/* [2] pre-charge period 0x022/f1*/
    cmd(SETVCOMDETECT,
        0x20); // vs 0x40 		/* vcomh deselect level */
               // if vcomh is 0, then this will give the biggest range for
               // contrast control issue #98 restored the old values for the
               // noname constructor, because vcomh=0 will not work for all
               // OLEDs, #116
    cmd(DEACTIVATE_SCROLL);
    cmd(DISPLAYALLON_RESUME);
    cmd(NORMALDISPLAY);
    cmd(CHARGEPUMP, 0x14);
    cmd(DISPLAYON);
  }

  i2cReleaseBus(&I2CD1);
}
// _____________________________________________________________________
void setup() {
  static uint8_t _txbuf[132] __attribute__((section(".sram2")));
  static uint8_t _rxbuf[8] __attribute__((section(".sram2")));
  txbuf = _txbuf;
  rxbuf = _rxbuf;
  init();
}

// _____________________________________________________________________
// THREADS
msg_t ThreadX2() {
  setup();
  while (!chThdShouldTerminate()) {
    if (!disable) {
      for (int i = 0; i < 8; i++) {
        sendPage(i);
      }
    }
    chThdSleepMilliseconds(32);
  }
  chThdExit((msg_t)0);
}

static msg_t ThreadX(void *arg) { ((attr_parent *)arg)->ThreadX2(); }

WORKING_AREA(waThreadX, 192);
Thread *Thd;
Init
for (int i = 0; i < 21 * 8; i++) {
  text[i] = textBuf[i] = ' ';
}
Thd = chThdCreateStatic(waThreadX, sizeof(waThreadX), NORMALPRIO, ThreadX,
                        (void *)this);
Control Rate
disable = inlet_disable; // can be useful when playing with future modules
                         // targetting the same display

// two first lines are present whatever the mode
if (inlet_line1 != NULL) {
  int i = 0;
  while (i < 21 & inlet_line1[i] != '\0') {
    text[i] = inlet_line1[i];
    i++;
  }
  while (i < 21) {
    text[i] = ' ';
    i++;
  }
}
if (inlet_line2 != NULL) {
  int i = 0;
  while (i < 21 & inlet_line2[i] != '\0') {
    text[i + 21] = inlet_line2[i];
    i++;
  }
  while (i < 21) {
    text[i + 21] = ' ';
    i++;
  }
}

if (inlet_line3 != NULL) {
  int i = 0;
  while (i < 21 & inlet_line3[i] != '\0') {
    text[i + 2 * 21] = inlet_line3[i];
    i++;
  }
  while (i < 21) {
    text[i + 2 * 21] = ' ';
    i++;
  }
}
if (inlet_line4 != NULL) {
  int i = 0;
  while (i < 21 & inlet_line4[i] != '\0') {
    text[i + 3 * 21] = inlet_line4[i];
    i++;
  }
  while (i < 21) {
    text[i + 3 * 21] = ' ';
    i++;
  }
}
if (inlet_line5 != NULL) {
  int i = 0;
  while (i < 21 & inlet_line5[i] != '\0') {
    text[i + 4 * 21] = inlet_line5[i];
    i++;
  }
  while (i < 21) {
    text[i + 4 * 21] = ' ';
    i++;
  }
}
if (inlet_line6 != NULL) {
  int i = 0;
  while (i < 21 & inlet_line6[i] != '\0') {
    text[i + 5 * 21] = inlet_line6[i];
    i++;
  }
  while (i < 21) {
    text[i + 5 * 21] = ' ';
    i++;
  }
}
if (inlet_line7 != NULL) {
  int i = 0;
  while (i < 21 & inlet_line7[i] != '\0') {
    text[i + 6 * 21] = inlet_line7[i];
    i++;
  }
  while (i < 21) {
    text[i + 6 * 21] = ' ';
    i++;
  }
}
if (inlet_line8 != NULL) {
  int i = 0;
  while (i < 21 & inlet_line8[i] != '\0') {
    text[i + 7 * 21] = inlet_line8[i];
    i++;
  }
  while (i < 21) {
    text[i + 7 * 21] = ' ';
    i++;
  }
}

/*
void copyText(const uint8_t* line, int buff, int nbc){
 if(line != NULL){
    int i = 0;
    while(i < nbc & line[i] != '\0'){
          buff[i] = line[i];
          i++;
    }
    while(i < nbc ){buff[i] = ' ';i++;}
  }
}
ex:
copyText(inlet_line1, text,      21);
copyText(inlet_line2, text+1*21, 21);
copyText(inlet_line3, text+2*21, 21);
copyText(inlet_line4, text+3*21, 21);
copyText(inlet_line5, text+4*21, 21);
copyText(inlet_line6, text+5*21, 21);
copyText(inlet_line7, text+6*21, 21);
copyText(inlet_line8, text+7*21, 21);

*/
Dispose
chThdTerminate(Thd);
chThdWait(Thd);

Privacy

© 2024 Zrna Research