Why does my debug routine fix the program?

nsaspook

Joined Aug 27, 2009
13,272
SSD1306_puts uses I2C to communicate with the display. The I2C is bit bang and so uses __delay_us for the timing.
Just curious.
Do you update a offscreen frame buffer used for rendering then push that buffer via I2C later or is a all done sequentially in one function with SSD1306_puts?
 

gunther1

Joined Sep 1, 2020
3
I think the I2C data register isn't declared volatile and thus treated like a memory location. So for the compiler the commands are:
Write 0x20 to this memory location, write 0x20 to the same memory location, [repeat], write 0x00 to the same memory location.
The end result is "skip that, write zero to this memory location".
 

Thread Starter

AlbertHall

Joined Jun 4, 2014
12,346
I think the I2C data register isn't declared volatile and thus treated like a memory location. So for the compiler the commands are:
Write 0x20 to this memory location, write 0x20 to the same memory location, [repeat], write 0x00 to the same memory location.
The end result is "skip that, write zero to this memory location".
Remember that the I2C is executed by the bit bang tecnique.
 

nsaspook

Joined Aug 27, 2009
13,272
Remember that the I2C is executed by the bit bang tecnique.
I'm not saying it's the culprit in this case but the Bit bang routine can be in an interrupt context like a timer ISR for sequence timing that normal compiler variable flow analysis can't follow so it can't see memory modifications or data transfers from the main code path. It's not what happen's, it's where.
 

gunther1

Joined Sep 1, 2020
3
Remember that the I2C is executed by the bit bang tecnique.
As far as the bit IO port register is not declared as volatile, the same principle will take effect. I think the debug code soultion works because of the NOP which defies optimization of the loop as it is inserting assembler code which is not touched.
 

xox

Joined Sep 8, 2017
838
MPLABX V6.0, XC8 v2.40

At the start of the program:
Code:
enum DisplayText_t{OFF_T, MAINS_T, REMAINING_T, RAW_T, LIMIT_T, LIMIT_PLUS_T, LIMIT_MINUS_T};
const char *Text[] = {"      ", "Mains ", "Spare ", "Raw   ", "Limit ", "Limit+", "Limit-"};
Where the value of 'Text' is used:
Code:
        SSD1306_GotoXY(1,3);
        SSD1306_puts(Text[NewText], 2);
        LastText = NewText;
The problem is that 'Text[NewText]' is nul and so the display is not written. This is regardless of the optimisation setting.
I added the code below to check the values in the array:
Code:
    uint8_t Test;
    for(uint8_t i = 0; i < 7; i++)
    {
        Test = Text[0][i];
        if(Test == 0x20)
            NOP();
    }
The array contents checked out OK, but with this code in place the display writing works correctly.

If necessary I can leave the extra code in place, but I cannot see why this happens.
It's difficult to say, without seeing more code (such as how 'NewText' is being assigned and such).

Why are you hard coding 6-character strings by the way? That is a very error prone way to do things. If the purpose is to ensure that previous print statements are erased, then a more practical method would be to write a function that "automagically" handles all of that.

As to best practices, a more standard approach for fetching the desired string might be something like this:

Code:
enum DisplayText_t {
  OFF_T,
  MAINS_T,
  REMAINING_T,
  RAW_T,
  LIMIT_T,
  LIMIT_PLUS_T,
  LIMIT_MINUS_T
};

const char* GetText(int which) {
  switch (which) {
    case OFF_T:
      return "";
    case MAINS_T:
      return "Mains";
    case REMAINING_T:
      return "Spare";
    case RAW_T:
      return "Raw";
    case LIMIT_T:
      return "Limit";
    case LIMIT_PLUS_T:
      return "Limit+";
    case LIMIT_MINUS_T:
      return "Limit-";
    default:
      return "ERROR!";
  }
}
 
Top