OLED128x64Dbl

Double OLED 128x64 SSD1306 or SH1106 on I2C. PB8=SCL PB9=SDA
Author: Smashed Transistors
License: LGPL
Github: tiar/HW/OLED128x64Dbl.axo

Inlets

charptr32 line1A

charptr32 line2A

charptr32 line3A

charptr32 line4A

charptr32 line1B

charptr32 line2B

charptr32 line3B

charptr32 line4B

int32.positive modeA

int32.positive modeB

Outlets

None

Attributes

objref scopeA

objref scopeB

combo type

Declaration
// add include tiar_font5x8
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 nibbleToByte[16] = {0b00000000, 0b00000011, 0b00001100, 0b00001111,
                            0b00110000, 0b00110011, 0b00111100, 0b00111111,
                            0b11000000, 0b11000011, 0b11001100, 0b11001111,
                            0b11110000, 0b11110011, 0b11111100, 0b11111111};

uint8_t cpt = 0;
uint8_t *txbuf;
uint8_t *rxbuf;
int32_t modeA;
int32_t modeB;

uint8_t textA[11 * 4] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                         ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                         ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                         ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
uint8_t textB[11 * 4] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                         ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                         ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                         ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
int8_t tYA[128];
int8_t tYB[128];
bool drawn = true;

// SETUP
// ------------------
void cmd(uint8_t c, uint8_t add) {
  txbuf[0] = 0;
  txbuf[1] = c;
  i2cMasterTransmitTimeout(&I2CD1, add, txbuf, 2, rxbuf, 0, 30);
  // chThdSleepMilliseconds(1);
}

// _____________________________________________________________________
void fill(uint8_t v, uint8_t add) {
  cmd(COLUMNADDR, add);
  cmd(0, add);
  cmd(127, add); // Column start end
  cmd(PAGEADDR, add);
  cmd(0, add);
  cmd(7, add); // 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, add, txbuf, 129, rxbuf, 0, 30);
    // chThdSleepMilliseconds(1);
  }
}
// _____________________________________________________________________
// scaled x2 text
// returns a page to be sent to the SSD1306 based on contents of text
void calcTextPage(int page, uint8_t tPage[128], uint8_t add) {
  uint8_t *text;
  if (add == 0x3C)
    text = textA;
  else
    text = textB;
  int i = 0;
  int tLine = page / 2;
  for (int nc = 0; nc < 11; nc++) {
    int ascii_32 = text[nc + 11 * tLine] - ' ';
    if (ascii_32 < 0 || ascii_32 >= 128 - 32)
      ascii_32 = 0;
    const uint8_t *adChar = tiar_font5x8 + ascii_32 * 5;
    for (int slice = 0; slice < 5; slice++) {
      uint8_t s;
      if ((page & 1) == 0) {
        s = nibbleToByte[adChar[slice] & 15]; // low nibble
      } else {
        s = nibbleToByte[(adChar[slice] >> 4) & 15]; // high nibble
      }
      tPage[i] = s;
      i++;
      tPage[i] = s;
      i++;
    }
    tPage[i] = 0;
    i++; // separator space
  }
  while (i < 128) {
    tPage[i] = 0;
    i++;
  } // space padding
}
// _____________________________________________________________________
// opt function draw

// LSB up
uint8_t tBar[9] = {0b00000000, 0b10000000, 0b11000000, 0b11100000, 0b11110000,
                   0b11111000, 0b11111100, 0b11111110, 0b11111111};

/*
page0
    1
    .
    7
*/

uint8_t vBar(uint8_t val, int page) {
  uint8_t _page = 7 - (val / 8);
  if (page > _page)
    return 0b11111111; // below => light
  else if (page < _page)
    return 0; // above => dark
  else
    return tBar[val & 7];
}
void _calcScopePage(int page, uint8_t tPage[128]) {
  for (int i = 0; i < 128; i++) {
    uint16_t y = ((attr_scopeA.t[i] + 64) * 3) / 8;
    tPage[i] = vBar(y, page);
  }
}
void calcScopePage(int page, uint8_t tPage[128], uint8_t add) {
  if (page < 2) {
    calcTextPage(page, tPage, add);
  } else {
    int8_t *tY = add == 0x3C ? tYA : tYB;
    int16_t y0 = tY[0];
    int16_t y1 = tY[1];
    int16_t y2;
    for (int i = 0; i < 128; i++) {
      if (i < 127)
        y2 = tY[i + 1];
      int16_t yM, ym;
      yM = ym = y1;
      int16_t y = (y0 + y1) >> 1;
      yM = y > yM ? y : yM;
      ym = y < ym ? y : ym;
      y = (y2 + y1) >> 1;
      yM = y > yM ? y : yM;
      ym = y < ym ? y : ym;
      if (ym == yM)
        if (yM > 0)
          ym--;
        else
          yM++;
      tPage[i] = vBar(yM, page) & ~vBar(ym, page);
      y0 = y1;
      y1 = y2;
    }
  }
}
// _____________________________________________________________________
int8_t scale3_4(int8_t x) {
  // if(x==-128) return -24;
  // if(x==127) return 70;
  return (int8_t)(((((int16_t)x) + 64) * 3) >> 3);
}
void sendTextPage(int page, uint8_t add) {
  cmd(COLUMNADDR, add);
  cmd(0, add);
  cmd(127, add); // Column start end
  cmd(PAGEADDR, add);
  cmd(page, add);
  cmd(page, add); // Page start end

  if (attr_type == 1106) {
    cmd(0xB0 + page, add);     // set page address
    cmd(2 & 0xf, add);         // set lower column address
    cmd(0x10 | (2 >> 4), add); // set higher column address
  }

  txbuf[0] = 0x40;
  int32_t mode = add == 0x3C ? modeA : modeB;
  if (mode == 0) {
    calcTextPage(page, txbuf + 1, add);
    i2cMasterTransmitTimeout(&I2CD1, add, txbuf, 129, rxbuf, 0, 30);
  } else if (mode == 1) {
    if (page == 1 && drawn) {
      for (int i = 0; i < 128; i++) {
        tYA[i] = scale3_4(attr_scopeA.t[i]);
        tYB[i] = scale3_4(attr_scopeB.t[i]);
      }
      drawn = false;
    }
    calcScopePage(page, txbuf + 1, add);
    i2cMasterTransmitTimeout(&I2CD1, add, txbuf, 129, rxbuf, 0, 30);
  }
}
// _____________________________________________________________________
void init() {
  for (int a = 0x3C; a <= 0x3D; a++) {
    // Init sequence
    cmd(DISPLAYOFF, a);

    // 2 byte cmd, low nibble A[3:0]+1 = div ratio, high nibble A[7:4] freq,
    // reset 1000b
    cmd(SETDISPLAYCLOCKDIV, a);
    cmd(0x80, a);
    cmd(SETMULTIPLEX, a);
    cmd(LCDHEIGHT - 1, a);
    cmd(SETDISPLAYOFFSET, a);
    cmd(attr_type == 1306 ? 0x00 : 0x01, a);
    cmd(SETSTARTLINE_0, a);
    cmd(CHARGEPUMP, a);
    cmd(0x14, a);
    cmd(MEMORYMODE, a);
    cmd(0x00, a);
    cmd(SEGREMAP | 0x1, a);
    cmd(COMSCANDEC, a);
    // 128 x 64
    cmd(SETCOMPINS, a);
    cmd(0x12, a);
    cmd(SETCONTRAST, a);
    cmd(0xCF, a);
    cmd(SETPRECHARGE, a);
    cmd(0xF1, a);
    cmd(SETVCOMDETECT, a);
    cmd(0x40, a);
    cmd(DISPLAYALLON_RESUME, a);
    cmd(NORMALDISPLAY, a);
    cmd(DEACTIVATE_SCROLL, a);
    cmd(DISPLAYON, a);
  }
}
// _____________________________________________________________________
void setup() {
  static uint8_t _txbuf[129] __attribute__((section(".sram2")));
  static uint8_t _rxbuf[8] __attribute__((section(".sram2")));
  txbuf = _txbuf;
  rxbuf = _rxbuf;
  init();
}
// _____________________________________________________________________
void loop() {
  uint8_t p[] = {0, 1, 4, 5, 3, 6, 2, 7};
  sendTextPage(p[cpt % 8], 0x3C);
  sendTextPage(p[cpt % 8], 0x3D);
  if ((cpt & 7) == 7)
    drawn = true;
  cpt++;
}
// _____________________________________________________________________
// THREADS
msg_t ThreadX2() {
  setup();
  while (!chThdShouldTerminate()) {
    loop();
    chThdSleepMilliseconds(1);
  }
  chThdExit((msg_t)0);
}

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

WORKING_AREA(waThreadX, 1024);
Thread *Thd;
Init
// setup the pins
palSetPadMode(GPIOB, 8,
              PAL_MODE_ALTERNATE(4) | PAL_STM32_PUDR_PULLUP |
                  PAL_STM32_OTYPE_OPENDRAIN); // SCL
palSetPadMode(GPIOB, 9,
              PAL_MODE_ALTERNATE(4) | PAL_STM32_PUDR_PULLUP |
                  PAL_STM32_OTYPE_OPENDRAIN); // SDA
static const I2CConfig i2cfg = {
    OPMODE_I2C,
    400000,
    FAST_DUTY_CYCLE_2,
};
/*static const I2CConfig i2cfg = {
    OPMODE_I2C,
    100000,
    STD_DUTY_CYCLE,
};*/
i2cStart(&I2CD1, &i2cfg);
Thd = chThdCreateStatic(waThreadX, sizeof(waThreadX), NORMALPRIO, ThreadX,
                        (void *)this);
Control Rate
modeA = inlet_modeA;

if (inlet_line1A != NULL) {
  int i = 0;
  while (i < 11 & inlet_line1A[i] != '\0') {
    textA[i] = inlet_line1A[i];
    i++;
  }
  while (i < 11) {
    textA[i] = ' ';
    i++;
  }
}
if (modeA == 0) {
  if (inlet_line2A != NULL) {
    int i = 0;
    while (i < 11 & inlet_line2A[i] != '\0') {
      textA[i + 11] = inlet_line2A[i];
      i++;
    }
    while (i < 11) {
      textA[i + 11] = ' ';
      i++;
    }
  }
  if (inlet_line3A != NULL) {
    int i = 0;
    while (i < 11 & inlet_line3A[i] != '\0') {
      textA[i + 2 * 11] = inlet_line3A[i];
      i++;
    }
    while (i < 11) {
      textA[i + 2 * 11] = ' ';
      i++;
    }
  }
  if (inlet_line4A != NULL) {
    int i = 0;
    while (i < 11 & inlet_line4A[i] != '\0') {
      textA[i + 3 * 11] = inlet_line4A[i];
      i++;
    }
    while (i < 11) {
      textA[i + 3 * 11] = ' ';
      i++;
    }
  }
}

modeB = inlet_modeB;

if (inlet_line1B != NULL) {
  int i = 0;
  while (i < 11 & inlet_line1B[i] != '\0') {
    textB[i] = inlet_line1B[i];
    i++;
  }
  while (i < 11) {
    textB[i] = ' ';
    i++;
  }
}
if (modeA == 0) {
  if (inlet_line2B != NULL) {
    int i = 0;
    while (i < 11 & inlet_line2B[i] != '\0') {
      textB[i + 11] = inlet_line2B[i];
      i++;
    }
    while (i < 11) {
      textB[i + 11] = ' ';
      i++;
    }
  }
  if (inlet_line3B != NULL) {
    int i = 0;
    while (i < 11 & inlet_line3B[i] != '\0') {
      textB[i + 2 * 11] = inlet_line3B[i];
      i++;
    }
    while (i < 11) {
      textB[i + 2 * 11] = ' ';
      i++;
    }
  }
  if (inlet_line4B != NULL) {
    int i = 0;
    while (i < 11 & inlet_line4B[i] != '\0') {
      textB[i + 3 * 11] = inlet_line4B[i];
      i++;
    }
    while (i < 11) {
      textB[i + 3 * 11] = ' ';
      i++;
    }
  }
}
Dispose
chThdTerminate(Thd);
chThdWait(Thd);
i2cStop(&I2CD1);
palSetPadMode(GPIOB, 8, PAL_MODE_INPUT_ANALOG);
palSetPadMode(GPIOB, 9, PAL_MODE_INPUT_ANALOG);

Privacy

© 2024 Zrna Research