charptr32 line1
charptr32 line2
charptr32 line3
charptr32 line4
charptr32 line5
charptr32 line6
charptr32 line7
charptr32 line8
int32.positive mode
bool32 disable
None
combo type
combo I2CADDR
objref scope
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;
int32_t mode;
uint8_t text[21 * 8]; // text inputs
uint8_t textBuf[21 * 8]; // text inputs copy (to avoid flicker)
uint8_t tY[128]; // scope input copied and rescaled
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
// LSB up
uint8_t const 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];
}
// on the Oled display, a "page" is a 128x8 stripe of pixels
// described by 128 bytes. The 128x64 OLED consists of 8 "pages"
// I calculate and transmit one page at a time.
// It saves memory as small buffers are enough to transmit them.
void calcScopePage(int page, uint8_t tPage[128]) {
uint16_t y0 = tY[0];
uint16_t y1 = tY[1];
uint16_t y2;
for (int i = 0; i < 128; i++) {
if (i < 127)
y2 = tY[i + 1];
uint16_t yM, ym;
yM = ym = y1;
uint16_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;
}
}
// _____________________________________________________________________
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);
if (mode == 0) { // 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);
} else if (mode == 1) { // Title and scope
// on the beginning of drawing (page 0) we update the buffers
if (page == 0) {
// chSysLock();
// update the textBuffer
memcpy(textBuf, text, 21 * 8);
// update scope buffer
memcpy(tY, attr_scope.t, 128);
// chSysUnlock();
// scale for the display
int8_t *stY = (int8_t *)tY;
for (int i = 0; i < 128; i++) {
tY[i] = ((stY[i] + 64) * 3) >> 3;
}
}
if (page < 2) {
calcTextPage(page, txbuf + 1);
} else {
calcScopePage(page, txbuf + 1);
}
} else if (mode == 2) { // full screen scope
// on the beginning of drawing (page 0) we update the buffers
if (page == 0) {
// chSysLock();
// update scope buffer
memcpy(tY, attr_scope.t, 128);
// chSysUnlock();
// scale for the display
int8_t *stY = (int8_t *)tY;
for (int i = 0; i < 128; i++) {
tY[i] = (stY[i] + 64) >> 1;
}
}
calcScopePage(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;
for (int i = 0; i < 21 * 8; i++) {
text[i] = textBuf[i] = ' ';
}
Thd = chThdCreateStatic(waThreadX, sizeof(waThreadX), NORMALPRIO, ThreadX,
(void *)this);
disable = inlet_disable; // can be useful when playing with future modules
// targetting the same display
mode = inlet_mode;
// 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 (mode == 0) {
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);
*/
chThdTerminate(Thd);
chThdWait(Thd);