lexus2k / ssd1306

Driver for SSD1306, SSD1331, SSD1351, IL9163, ILI9341, ST7735, PCD8544, Nokia 5110 displays running on Arduino/ESP32/Linux (Rasperry) platforms
MIT License
676 stars 127 forks source link

Avoid timer0 and Timer2 on nano #124

Closed Shumatic closed 3 years ago

Shumatic commented 3 years ago

Hi Thanks for the lib. it is awsome I am using an Nano with the lib and a 128x32 I2C. afaik this lib uses othe Timer0 and Timer2, could this be avoided or is it I2C

lexus2k commented 3 years ago

Hi,

Could you please provide code example that doesn't work for you?

Shumatic commented 3 years ago

Hi,

Thank you for taking the time to answer. I tried to strip my code, and I am not sure it is your code that is the problem, but I hope it is OK to send the code anyway, i understand if you dont answer.

I am using a Nano3 ATMEL 168 I found out if i reduced the buffer in my function subStr to 38 (#define MAX_STRING_LEN 38) i works. and the freeRam() report 135 bytes, when i increase MAX_STRING_LEN 39, it fails and the freeRam() report 151 bytes free RAM. I havent made this RAM function.

Any help would be appriciated, I like to know what i am missing.

/**************************************************************************************************
  Filename:       ClickEncoder_timer2.ino

  Description:    ClickEncoder using Timer2

  Author:         Jesper Schumacher
  Last modified:  08-02-2021
*******Version history****************
  V1.0 08-02-2021 - Initial

**************************************************************************************************
*/
#define __FILENAME__ strrchr("\\" __FILE__, '\\') + 1

const byte numChars = 32; // for serial
char receivedChars[numChars];   // an array to store the received data from serial
boolean newData = false; // for serial
int DutyCycle = 50; // default
int cursorLCD = 9;

#include <ClickEncoder.h> //encoder
ClickEncoder *encoder; //encoder
long last; //encoder
int value; //encoder
int modeEncoder = 0; //encoder

//encoder interrupt
ISR(TIMER2_COMPA_vect) {
  encoder->service();
  //  digitalWrite(A0, !digitalRead(A0));
}

#include "ssd1306.h" // I2C A5 SCL - A4 SDA
int freeRam() {
  extern int __heap_start, *__brkval;
  int v;
  return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int) __brkval);
}
void setup() {
  Serial.begin(115200);  Serial.print(F("\r\nFilename: ")); Serial.println(__FILENAME__);
  Serial.print(F(" Compiled:  "));  Serial.print(__DATE__);  Serial.print(" ");  Serial.println(__TIME__);

  pinMode(9, OUTPUT); //50% dutycycle
  pinMode(10, OUTPUT);

  //set timer2 interrupt at 1kHz
  TCCR2A = 0;// set entire TCCR2A register to 0
  TCCR2B = 0;// same for TCCR2B
  TCNT2  = 0;//initialize counter value to 0
  // set compare match register for 1khz increments
  OCR2A = 249;// = (16*10^6) / (8000*8) - 1 (must be <256)
  // turn on CTC mode
  TCCR2A |= (1 << WGM21);
  // Set CS21 bit for 8 prescaler
  TCCR2B = _BV(CS22);    //prescaler 32
  // enable timer compare interrupt
  TIMSK2 |= (1 << OCIE2A);
  sei();//allow interrupts

  encoder = new ClickEncoder(A1, A0, 6, 4); //encoder
  last = -1; //encoder
  value = 0; //encoder

  ssd1306_setFixedFont(ssd1306xled_font6x8);
  ssd1306_128x32_i2c_init();
  ssd1306_clearScreen();
  ssd1306_printFixed(0,  8, "Normal text", STYLE_NORMAL);
  Set_frequency(1000);
}

long selector = 1000;
long frequency = 1000; //default
char buffer2[14];

void loop() {
  char *ptr;
  recvWithEndMarker();
  if (newData == true) {
frequency = strtol(subStr((receivedChars), ",", 1), &ptr, 10);
    Serial.print(F(" FreeRAM:  "));  Serial.println(freeRam());
    Serial.print(Set_frequency(frequency)); Serial.print(F(" Hz"));
    newData = false;
  }
  //encoder
  long curretFreq;
  if (modeEncoder == 0) {
    frequency += encoder->getValue()  * selector;
    if (frequency != last) {
      last = frequency;
      Serial.print(F("  Encoder Value: ")); Serial.println(value);
      frequency = constrain(frequency, 1, 8000000);
      curretFreq = Set_frequency(frequency);
      Serial.print(ultoa(curretFreq, buffer2, 4));  Serial.print(F(" Hz"));
      Serial.print(F("  Selector: ")); Serial.println(ultoa(selector, buffer2, 4));
    }
  } else {
    DutyCycle += encoder->getValue();
    if (DutyCycle != last) {
      last = DutyCycle;
      DutyCycle = constrain(DutyCycle, 0, 101);
      curretFreq = Set_frequency(frequency);
    }
  }
  buttons();
}

long Set_frequency(long freq) {
  freq = constrain(frequency, 1, 8000000);
  // TCCR1A Bit              7     6      5      4    3 2   1     0
  TCCR1A = 0b01100011;  //COM1A1 COM1A0 COM1B1 COM1B0 – – WGM11 WGM10
  //                         0     1      0      0    0 0   0     0
  uint16_t ocr1a;
  if (freq < 2) {
    // TCCR1B Bit            7     6   5   4     3    2    1    0
    TCCR1B = 0b00011100; //ICNC1 ICES1 – WGM13 WGM12 CS12 CS11 CS10
    //                       0     0   0   0     1    1    0    0
    ocr1a = 62500UL / freq - 1; // count 0-1-2-3...
    freq = 62500UL / (ocr1a + 1);
    Serial.println(F(" prescaler 256"));
  } else if (freq < 16) {
    TCCR1B = 0b00011011;
    ocr1a = 250000UL / freq - 1;
    freq = 250000UL / (ocr1a + 1);
    Serial.println(F(" prescaler 64"));
  } else if (freq < 123) {
    TCCR1B = 0b00011010;
    ocr1a = 2000000UL / freq - 1;
    freq = 2000000UL / (ocr1a + 1);
    Serial.println(F(" prescaler 8"));
  } else {
    TCCR1B = 0b00011001;
    ocr1a = 16000000UL / freq - 1;
    freq = 16000000UL / (ocr1a + 1);
    Serial.println(F(" NO prescaler"));
  }
  if (DutyCycle > 100) DutyCycle = 100;
  Serial.print(F(" ocr1a: "));  Serial.print(ocr1a);
  unsigned long maxDutyCycle = ocr1a - 1;
  OCR1A = ocr1a;
  unsigned long  ocr1b  = (ocr1a + 1); //as zero is first count
  ocr1b = (ocr1b  * DutyCycle / 100);
  if (ocr1b != 0) ocr1b = ocr1b - 1; //-1 as ocr1b counts from zero
  if (ocr1b > maxDutyCycle) ocr1b = maxDutyCycle; // needs to 1 below OCR1A
  int DutyCycleCorr = (ocr1b + 1) * 100 / (ocr1a + 1);
  Serial.print(F(" DutyCycleCorr: ")); Serial.println(DutyCycleCorr);
  // lcd.setCursor(11, 1);
  //Serial.print(ultoa(Set_frequency(frequency), buffer2,4))
  //  lcd.print(ultoa(DutyCycleCorr, buffer2, 11));
  //  lcd.print("%");
  OCR1B = ocr1b;
  TIMSK1 = 0;
  return freq;
}

void recvWithEndMarker() {
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;

  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();

    if (rc != endMarker) {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
      newData = true;
    }
  }
}
void buttons() {
  ClickEncoder::Button b = encoder->getButton();
  if (b != ClickEncoder::Open) {
    Serial.print("Button: ");
#define VERBOSECASE(label) case label: Serial.println(#label); break;
    switch (b) {
        VERBOSECASE(ClickEncoder::Pressed);
        VERBOSECASE(ClickEncoder::Held)
        VERBOSECASE(ClickEncoder::Released)
        VERBOSECASE(ClickEncoder::Clicked)
      case ClickEncoder::DoubleClicked:
        Serial.println("ClickEncoder::DoubleClicked");
        encoder->setAccelerationEnabled(!encoder->getAccelerationEnabled());
        Serial.print("  Acceleration is ");
        Serial.println((encoder->getAccelerationEnabled()) ? "enabled" : "disabled");
    }
  }
}
/**************************************************************************************************
   @fn      ultoa

   @brief   Format an unsigned long (32 bits) into a string in the format "23.854.972".

   @param   val  input long value
            s    The provided buffer must be at least 14 bytes long. The number will
                 be right-adjusted in the buffer. Returns a pointer to the first digit.

   testcode char buffer2[14];
            Serial.println(ultoa(val, buffer2));

   @return  char with thousin seperator

   https://arduino.stackexchange.com/questions/28603/the-most-effective-way-to-format-numbers-on-arduino
   /**********************************************************************************************
*/
char *ultoa(unsigned long val, char *s, int padding) {
  char *p = s + 13;
  *p = '\0';
  do {
    if ((p - s) % 4 == 2) // plads 4 - 8 -12 ...
      *--p = '.';
    *--p = '0' + val % 10;
    val /= 10;
  } while (val);
  //this adds padding
  for (0; p - s > padding; 0) {
    *--p = ' ';
  }
  return p;
}
/**************************************************************************************************
   @fn      subStr

   @brief   split string on given delimiter

   @param   str     input string to splitted
            delim   char to use as delimiter
            index   block to return,1 is first block BEFORE delim.

   @return  Block between chosen delimiters
   /**********************************************************************************************
*/
char* subStr (char* str, const char *delim, int index) {
#define MAX_STRING_LEN 100
  char *act, *sub, *ptr;
  static char copy[MAX_STRING_LEN];
  int i;
  // Since strtok consumes the first arg, make a copy
  strcpy(copy, str); 
  copy[MAX_STRING_LEN] = '\0';
  for (i = 1, act = copy; i <= index; i++, act = NULL) {
    ////Serial.print(".");
    sub = strtok_r(act, delim, &ptr);
    if (sub == NULL) break;
  }
  return sub;
} //subStr
lexus2k commented 3 years ago

Hi,

Unfortunately, I cannot compile the example. The system doesn't know anything about #include <ClickEncoder.h>. As for ssd1306 library, the only place, where interrupt handlers are declared for VGA mode is vga_isr.h header, which is not included by library cpp/c files. The only place, where this header is included, is examples. So, I don't think that library affects TIMER0 in your case.

As for free ram. What is reported by Arduino, when it compiles your code?

Shumatic commented 3 years ago

Hi

No, it is an external lib https://github.com/0xPIT/encoder. i Like it becurse it handle both button and encoder (with acceleration) Arduino dosnt report freeRam, only flash, and the sketchuse app. 10500byte /j

lexus2k commented 3 years ago

Arduino dosnt report freeRam, only flash, and the sketchuse app. 10500byte

That's very strange. My Arduino IDE 1.8.3 for Arduino Nano based on Atmega328p shows:

Sketch uses 5826 bytes (18%) of program storage space. Maximum is 32256 bytes.
Global variables use 360 bytes (17%) of dynamic memory, leaving 1688 bytes for local variables. Maximum is 2048 bytes.

So, it actually displays how much memory is required for global variables, the rest 1688 can be used for local variable and function calls (all of these ones are allocated in Stack)

Shumatic commented 3 years ago

Hi Aleksei Yes i had comprehensive output on, so i didnt notice. then i wouldn't have posted this in the first place, as it clearly say Sketch uses 10572 bytes (73%) of program storage space. Maximum is 14336 bytes. Global variables use 862 bytes (84%) of dynamic memory, leaving 162 bytes for local variables. Maximum is 1024 bytes. Low memory available, stability problems may occur. Theres a little diff, this says 162 b, FreeRAM states 151 b

Thanks for you positive attitude and explanation. Sorry for wasting you timeon such triviallity. BR Jesper

lexus2k commented 3 years ago

Hi

You're welcome. Did you solve the original problem with timers?

Best regards

Shumatic commented 3 years ago

Hi Yes, it wasn't a timer og SSD issue, it was only a too big program for a too small Proc. :-) Normally ClickEncoder use a lib called TimerOne, that ...... use timer1 The code i posted here, use Timer2, i have also teste with Timer0, but that them millis() are gone.

lexus2k commented 3 years ago

Thank you for the great explanation and a lot of code examples. I think all those information will be very helpful for everyone.

For now, I'm closing the issue. Feel free to open the new one if you have any problems.

Best regards