Go Down

Topic: Display Driver memory addresses...I think... [solved] (Read 417 times) previous topic - next topic

GreyArea

Hi all

I've managed to wire up three seven segment, three digit displays to a HT16K33 running from my Arduino Mega.

My wiring is obviously right as the looped display (see code below) results in a pleasing display as can be seen here;

Displays Running

BUT...I cannot for the life of me figure out how to assign characters from the library in the sketch; when |I try I just get gibberish...even thogh the fact that the pins are set so that segments light up in a sensible "0-9 then the decimal" order would indicate my assignments are right.

I suspect it's somethign to do with the memory addresses I need to reference...can anyone aid me with a few lines that will direct numbers to individual digits on the displays...or better yet, how I can send a number from 0 to 255 to each of the red, green and blue displays?

Many thanks!

Code: [Select]

//Sketch to control 3x seven segment three digit displays
//each to display values 0-255 from three separate potentiometers.


#include <Wire.h>

const uint8_t addr = 0x70; // HT16K33 default address
uint16_t displayBuffer[8];

const uint8_t SevenSegmentASCII[96] = {
  0b00000000, /* (space) */
  0b10000110, /* ! */
  0b00100010, /* " */
  0b01111110, /* # */
  0b01101101, /* $ */
  0b11010010, /* % */
  0b01000110, /* & */
  0b00100000, /* ' */
  0b00101001, /* ( */
  0b00001011, /* ) */
  0b00100001, /* * */
  0b01110000, /* + */
  0b00010000, /* , */
  0b01000000, /* - */
  0b10000000, /* . */
  0b01010010, /* / */
  0b00111111, /* 0 */
  0b00000110, /* 1 */
  0b01011011, /* 2 */
  0b01001111, /* 3 */
  0b01100110, /* 4 */
  0b01101101, /* 5 */
  0b01111101, /* 6 */
  0b00000111, /* 7 */
  0b01111111, /* 8 */
  0b01101111, /* 9 */
  0b00001001, /* : */
  0b00001101, /* ; */
  0b01100001, /* < */
  0b01001000, /* = */
  0b01000011, /* > */
  0b11010011, /* ? */
  0b01011111, /* @ */
  0b01110111, /* A */
  0b01111100, /* B */
  0b00111001, /* C */
  0b01011110, /* D */
  0b01111001, /* E */
  0b01110001, /* F */
  0b00111101, /* G */
  0b01110110, /* H */
  0b00110000, /* I */
  0b00011110, /* J */
  0b01110101, /* K */
  0b00111000, /* L */
  0b00010101, /* M */
  0b00110111, /* N */
  0b00111111, /* O */
  0b01110011, /* P */
  0b01101011, /* Q */
  0b00110011, /* R */
  0b01101101, /* S */
  0b01111000, /* T */
  0b00111110, /* U */
  0b00111110, /* V */
  0b00101010, /* W */
  0b01110110, /* X */
  0b01101110, /* Y */
  0b01011011, /* Z */
  0b00111001, /* [ */
  0b01100100, /* \ */
  0b00001111, /* ] */
  0b00100011, /* ^ */
  0b00001000, /* _ */
  0b00000010, /* ` */
  0b01011111, /* a */
  0b01111100, /* b */
  0b01011000, /* c */
  0b01011110, /* d */
  0b01111011, /* e */
  0b01110001, /* f */
  0b01101111, /* g */
  0b01110100, /* h */
  0b00010000, /* i */
  0b00001100, /* j */
  0b01110101, /* k */
  0b00110000, /* l */
  0b00010100, /* m */
  0b01010100, /* n */
  0b01011100, /* o */
  0b01110011, /* p */
  0b01100111, /* q */
  0b01010000, /* r */
  0b01101101, /* s */
  0b01111000, /* t */
  0b00011100, /* u */
  0b00011100, /* v */
  0b00010100, /* w */
  0b01110110, /* x */
  0b01101110, /* y */
  0b01011011, /* z */
  0b01000110, /* { */
  0b00110000, /* | */
  0b01110000, /* } */
  0b00000001, /* ~ */
  0b00000000, /* (del) */
};

void setup() {
  Wire.begin();

  Wire.beginTransmission(addr);
  Wire.write(0x20 | 1); // turn on oscillator
  Wire.endTransmission();

  setBrightness(15);
  blink(0);
}

void loop() {
  const int dTime = 50;

  //Trying to write letters to the display, just on the red display - this bit not working so commented out

  // for (int k = 0; k < 96; k++) {
  // writeCharacter(1,SevenSegmentASCII[k]);
  //  show();
  //  delay(1000);
  //  clear();
  //}



  // Loop through all segments

  // I edited this to reverse "k" and "i" - so I've learned that "i" is the segment and "k" the digit position.
  for (int k = 0; k < 9; k++) {
    for (int i = 0; i < 8; i++) {
      displayBuffer[i] = 1 << k; //my inexperience with C fails me here..what is the shorthand "1<<k" doing?
      show();
      delay(dTime);
    }
    clear();
  }

  // Turn on all segments, one at a time
  for (int i = 0; i < 8; i++) {
    for (int k = 0; k < 9; k++) {
      displayBuffer[i] |= 1 << k;
      show();
      delay(dTime);
    }
  }

  // Test blinking
  for (int i = 3 ; i > 0; i--) {
    blink(i);
    delay(2000);
  }
  blink(0); // Turn blinking off

  // Test blanking
  for (int i = 0; i < 10; i++) {
    blank();
    delay(dTime * 2);
  }

  // Test dimming
  for (int i = 15; i >= 0; i--) {
    setBrightness(i);
    delay(dTime * 2);
  }
  clear();

  setBrightness(15);
}

void show() {
  Wire.beginTransmission(addr);
  Wire.write(0x00); // start at address 0x0

  for (int i = 0; i < 8; i++) {
    Wire.write(displayBuffer[i] & 0xFF);
    Wire.write(displayBuffer[i] >> 8);
  }
  Wire.endTransmission();
}

void clear() {
  for (int i = 0; i < 8; i++) {
    displayBuffer[i] = 0;
  }
}

void setBrightness(uint8_t b) {
  if (b > 15) return;

  Wire.beginTransmission(addr);
  Wire.write(0xE0 | b); // Dimming command
  Wire.endTransmission();
}

void blank() {
  static boolean blankOn;

  Wire.beginTransmission(addr);
  Wire.write(0x80 | blankOn); // Blanking / blinking command
  Wire.endTransmission();

  blankOn = !blankOn;
}

void blink(uint8_t b) {
  if (b > 3) return;

  Wire.beginTransmission(addr);
  Wire.write(0x80 | b << 1 | 1); // Blinking / blanking command
  Wire.endTransmission();
}

void writeCharacter(uint8_t dispNum, char c) {
  uint8_t bufferN = (dispNum + 3) / 2;

  switch (dispNum & 1) {
    case (1): // Odd display #, no shift
      displayBuffer[bufferN] &= 0xFF00;
      displayBuffer[bufferN] |= pgm_read_word_near(SevenSegmentASCII + c) & 0xFF;
      break;
    case (0): // Even display #, shift right by 8
      displayBuffer[bufferN] &= 0x00FF;
      displayBuffer[bufferN] |= (pgm_read_word_near(SevenSegmentASCII + c) & 0xFF) << 8;
      break;
  }
}

void writeNumber(uint32_t n, uint8_t nDisp, uint8_t sDisp) {
  if (sDisp + nDisp > 13) {
    return;
  }

  for (int i = 1; i <= nDisp; i++) {
    writeCharacter(sDisp + nDisp - i, (n % 10) + 48);
    n /= 10;
  }
}

PaulS

Quote
what is the shorthand "1<<k" doing?
<< is the left shift operator. It IS documented on the Reference page.

Code: [Select]
      displayBuffer[bufferN] |= pgm_read_word_near(SevenSegmentASCII + c) & 0xFF;

You did not put the array in PROGMEM. Why are you trying to read it from there?
The art of getting good answers lies in asking good questions.

GreyArea

Thanks for the hint on left shift...bitwise math is new to me (surprise) but I see what it does...moves registers of a binary number to the left so that 0000001 becomes 00000010, 00000100, 00001000...etc. Plus I can see how that would be useful when dealing with a seven-segment LCD, so thank you very much!

As to PROGMEM...I'll admit I lifted the majority of that sketch from here;

http://www.partsnotincluded.com/electronics/controlling-led-matrix-with-the-ht16k33/

And it was very helpful in proving I at least got my wiring right...plus it's a complete sketch.~

However in the programming section here;

http://www.partsnotincluded.com/bttf/programming-the-time-circuit-display/

The author resorts to just posting snippets (boy, I see now how annoying that is!)...plus the links to the SevenSegmentASCII library on a different page. He doesn't mention putting it into PROGMEM, and since that's something I've never done before I'm hoping someone can let me know how to edit the commands so that I don't need to use it...

I was in the process of finding a way to read the addresses of my 7 segment LCDs when the power went down...with literally 15 minutes to run on the ten hour print on my 3D printer. I am grumpy this morning.

REALLY grumpy.

PaulS

Quote
He doesn't mention putting it into PROGMEM
But he does:
Quote
After making slight modifications to a few characters based on my research, I loaded the character literals from my segmented ASCII library as arrays into the Arduino's flash memory. Then I wrote a function to set displays to characters.
Not that that's all that helpful without an explanation of HOW.

And, yeah, snippets suck. At least he should have posted the complete program.
The art of getting good answers lies in asking good questions.

GreyArea

I've been littering his diode with serial.print commands to see if I can make sense of how it works. So far just adding to the confusion. I got it to output the value of "displayBuffer" at various points, thinking I'd be able to see a pattern.

Well, I can...but it's not helping me that much!

The first demo's output steps up in a fairly obvious binary progression; 1, 2, 4, 8, 16, 32, 64, 128, 256, 512. That moves a single segment (segment 1, the topmost horizontal) across the characters of the display from left to right. It writes the value first, then seven zero values.

Then it seems to output the same thing, but a "step" later...ie, it outputs one zero first, then the binary value, then seven zeros. Is this the effect of that bit shift?

After that, I get a bit lost...values change, the binary pattern still seems evident, but in an "n-1" kind of way...ie 0,1,3,7,15,31...etc...

I'll keep playing with it...trying to learn as well as just relying on someone here to deliver a "this is what you need" type solution...

GreyArea

Sorry for the italics, not sure where they came from...

Right..I can now interpret the library codes too...I realised that they are reversed to what I expected...where I thought the segments would be recorded left to right they are in fact recorded right to left...thus where I expected "3" to be "11110010" It's actual "01001111"...

This bitshift things seems to be very clever, but I'm not quite grasping it yet...the statement that's in the "write command of "displaybuffer>>8"; that's effectively clearing the value isn't it? shifting it eight points to the right moves all the exisiting eight bits completely "off-register", so we are back to "00000000"?

Still trawling through my serial outputs...I think displaybuffer is holding 8 values of 0 or 1, which corresponds to the segments...just not QUITE sure how it "finds" the right digit/display, or what the difference is between all the other segments going out or staying on when a new one is added...

GreyArea

"There are 10 types of people in the world. Those who understand binary and those who don't." Currently, feet firmly in the latter camp... :-(

PaulS

Quote
Currently, feet firmly in the latter camp... :-(
That's because you have 10 fingers. But, you only have two hands. So, try counting with your hands, instead of your fingers.
The art of getting good answers lies in asking good questions.

GreyArea

Bit of a light bulb moment with the relationship between hex and binary though...hadn't realised how easily 16bit binary could be displayed using just four hex digits...a real "Aha! So that's why they use it" moment...

But  I'm still struggling to understand what that sketch is doing...every time I think I'm getting there, I try and apply what Iive learned to show a simple run of digits 1 to 0 on the dispaly and...no..that's gibberish, so obviously I still dont get it.

I swear I used to be smarter than this.

GreyArea

#9
Apr 27, 2018, 12:31 pm Last Edit: Apr 27, 2018, 12:32 pm by GreyArea
Still trying, but I can't quite seem to crack it...

This loop;

Code: [Select]

// Turn on all displays, one digit at a time
  for (int k = 0; k < 9; k++) {  //this controls digit position
    for (int i = 0; i < 8; i++) {    //this controls segment number
      displayBuffer[i] |= 1 << k;  //this puts an (OR) command...so will keep segments on
      Serial.print ("DB");
      Serial.print ("\t");
      Serial.println(displayBuffer[i]);
      delay(dTime);
    }
    show();
  }


followed by this write procedure;

Code: [Select]

void show() {
  Wire.beginTransmission(addr);
  //Serial.print ("addr: ");
  //Serial.print ("\t");
  //Serial.print (addr);
  Wire.write(0x00); // start at address 0x0

  for (int i = 0; i < 8; i++) {
    Wire.write(displayBuffer[i] & 0xFF);  //& is the "AND" operator and it's working on display buffer and "0xFF" which is 11111111
    Serial.print ("Write i=; ");
    Serial.print (i);
    Serial.print ("\t");
    Serial.print (displayBuffer[i] & 0xff);
    Serial.print ("\t");
    Wire.write(displayBuffer[i] >> 8);   // >>8 shifts the value of displayBuffer 8 places right...is this effectively clearing the value?
    Serial.print (">>8 write; ");
    Serial.print ("\t");
    Serial.print(displayBuffer[i] >> 8);
    Serial.print ("\t");
    Serial.println (displayBuffer[i] >> 8);

  }
  Wire.endTransmission();
}


Works...it turns on all segments in each digit of the display, and lights them up one at a time left to right. SO I thought..."Aha! I have the positions and segments down pat, all I need to do is replace the data in the buffer currently filled by

Code: [Select]

displayBuffer[i] |= 1 << k;


Which if I understand correctly is an "OR" command between the current contents of the buffer and the value of "1<<k", which is the value "00000001", shifted one place left for each increment in "k", "k" relating to the number of digits in the display.

So...I thought if I replace "1<<k" with something that reads from the appropriate position in the SevenSegment ASCII array - for example;

Code: [Select]

SevenSegmentASCII(k+17)


then that position in the array should equate to the number for "k" (in the array, digits start at element 16 with zero) - so the plan is to get the display to read "123456789" reading left to right.

However the following loop;

Code: [Select]

    for (int k = 0; k < 9; k++) {  //this controls digit position
        for (int i = 0; i < 8; i++) {    //this controls segment number
          displayBuffer[i] = SevenSegmentASCII[k + 17] & 1 << i; //Can't seem to get this "bit" right haha
          Serial.print("Alpha:");
          Serial.print(k);
          Serial.print("\t");
          Serial.print(SevenSegmentASCII[k + 17]);
          Serial.print("\t");
          Serial.println (displayBuffer[i]);
          delay(dTime);
        }
        show();
      }
      clear();


Followed by the same write procedure results in all the right segments being lit up - but spread across the display, so the first segment of "1". appears on the leftmost digit...but the second segment is illuminated on the digit one to the right.

What am I doing wrong???

PaulS

Quote
What am I doing wrong???
Here's a clue:
Quote
all I need to do is replace the data in the buffer currently filled by displayBuffer |= 1 << k;
What buffer is that? What are you starting with in that buffer? What do you WANT to be starting with?
The art of getting good answers lies in asking good questions.

sterretje

The author resorts to just posting snippets (boy, I see now how annoying that is!)...
And you still keep on doing that as well ;)

It's OK to highlight snippets, but please provide full code; if too big, attach.
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

GreyArea

I've figure out my problem...an assumption on my part that there was consistency in the example code I was using. I thought since "i" filled the segments in the characters at the first stage, "i" was also looping through the segments in the second stage (the "Wire.write" procedure.) I now realise the buffer elements hold whole characters, not segments of one character as I thought.

That said, haven't tried it yet, but with help from my best pal (real life coder and programmer) I'm confident it will work...will post back here when it does.

sterretje; yes I posted snippets...but the whole code is present in this thread. I think if I posted the whole sketch each time, there's bound to be someone on here who'd complain that I don't need to repeat myself.

sterretje

The tendency here is that we like to see complete code so we don't have to copy from different parts from several posts and run the risk of making mistakes when we want to analyse / test.

Glad you got it working.
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

GreyArea

The tendency here is that we like to see complete code so we don't have to copy from different parts from several posts and run the risk of making mistakes when we want to analyse / test.

Glad you got it working.
Well, as it turns out it wasn't quite so simple.

I think the problem from the start was that I was expecting some sort of logic from a "logic" system...so get this...

The HT16K33 sends data in 16 bit bursts, preceded by a command nybble. The activation nybble is 0x00. 16 bits of memory that follow it are sequential...meaning you can't just "ignore" a bit...you have to make an acftive decision whether to write a 0 or a 1...

...or possibly other values, but that's a tale for later...

...because if you decide to do a "human" operation, and just say which bits are on in for example a "1" (00000110)...you can't.

(by the way, in terms of the segment numbners you have to read that binary number backkwards (or at least it feels backwards for me) so segment "a" is the rightmost bit, segment "h" (the full stop) the leftmost bit...)

The reason you can't is because the first bit the system expects you to write is the first OFF bit...if you try to tell it bit two is ON first...well you can't, it will just think you're talking about bit one.

And then it goes on to write bit two. Ha. You'd think so wouldn't you?

No. For some totally insane reason, the sequence is that even bits get assigned to the first byte, odd bits to the second...so I'm expecting to tell it the status of character one, bit two...but it's expecting me to tell it the status of character 9, bit one. So the "filling" order is like this

Code: [Select]

                  Byte one                Byte two
First bit;      x
Second bit;  x                           x
Third bit;     xx                          x
Fourth bit;   xx                          xx
Fifth bit;      xxx

etc.


Note; I've made no attempt to right or left justify the bits...by judicious use of the << or >> comands I now know I can fill the bytes from either end...though I'[m sure someone will tell me there's a convention as to how you should do it. (not that there should be...these bytes are not numbers. It took me a long time to get that into my head. Sure, you can represent them as numbers...but they are actually patterns...effectively a set of eight two-position switches.)


I'm sorry, but the way it fills just seems extremely awkward. Maybe it's to do with the order of displays with more segments...but for me, it's very (as in unecessarily) complex. It means for an automated system of filling the register, you HAVE to have the "Write" commands in pairs, and there has to be some logic involved when you start writing data to the 9th (or higher) character...because the HT16K33 demands that before you do, you have to RE-Write data to it's partner bit - effectively running the risk of overwriting the character 8 spaces before it.

So...yeah. My programmer friend used to code for professional organisations and hundreds of pounds per hour...he's good. Damn good. And he's been scratching his head about how the displays were behaving just as much as me...so I feel a little less dim than I did before.

It's not over yet.

The sketch attached below (which does have a lot of irrelevant guff in it that I haven't time to edit out) counts from 0 to 9 in each character position...and when it gets to position 9 rewrites a "zero" in position zero.

You have no idea how pleased that made me. By all means look at the code. I think I've come at this in a very "round the houses" way and it's extremely possible that logical functions like AND, OR and SHIFTs are only necessary in the write statements to correct for the way I've chosen to start the loop. If anyone can advise on ways to simplify, I'd be happy to hear them.

Next step is to address the display in it's entirety, so that I can write nine characters of individual data. I can do that in a fashion now...if I take out all the delays, the display will read 012345678...due to persistence orf vision...but with massive flickering.

The thing that attracted me to the demo code from partsnotincluded in the first place was the fact that it doesn't flicker at all. I'm hoping to replicate that, but with the data from my three potentiometers rather than simple sequential assignment of bits.

Watch this space (please!)


Go Up