2
\$\begingroup\$

Related with my old threads, I'm trying to set up ATMEGA328 at 8 MHz, in this way I could supply to ATMEGA328 with 3.3 V. I'm trying to make it by these lines:

CLKPR = (1 << CLKPCE); // Enable change of CLKPS bits
CLKPR = (1 << CLKPS0); // Set prescaler to 2, and system clock to 8 MHz

I'm using Arduino Uno board. Following ATMEGA328 datasheet, it should works fine, and if I try a simple code (blink) it works fine. But when I try a more complex WS2812B code, with these lines it doesn't work. My ATMEGA328 is assembled with 16 MHz crystal. I have tried Neopixel and FastLed libraries, the result is the same. Is to change the prescaler a good idea to get Arduino works at 8 MHz?

I have tried with 5 and 3.3 V (from Arduino Uno board).

I have tried too with clock_prescale_set(clock_div_2) from <avr/power.h> library. The result is the same.

When I say "it doesn't work" I mean I'm setting up with a red color and the LED color is white.

The test code I'm using:

    // NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// Released under the GPLv3 license to match the rest of the
// Adafruit NeoPixel library

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
 #include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif

// Which pin on the Arduino is connected to the NeoPixels?
#define PIN        6 // On Trinket or Gemma, suggest changing this to 1

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 1 // Popular NeoPixel ring size

// When setting up the NeoPixel library, we tell it how many pixels,
// and which pin to use to send signals. Note that for older NeoPixel
// strips you might need to change the third parameter -- see the
// strandtest example for more information on possible values.
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

#define DELAYVAL 500 // Time (in milliseconds) to pause between pixels

void setup() {
  // These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
  // Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif

 clock_prescale_set(clock_div_2); //If I set clock_div_1 it works
 pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
}

void loop() {
  pixels.clear(); // Set all pixel colors to 'off'

  pixels.setPixelColor(0, pixels.Color(200, 100, 0));

    pixels.show();   // Send the updated pixel colors to the hardware.

    delay(DELAYVAL); // Pause before next pass through loop
}
\$\endgroup\$
13
  • \$\begingroup\$ have you tried running your code after supplying 5Volts to atmega? \$\endgroup\$ Commented Jan 28, 2022 at 14:44
  • \$\begingroup\$ Yes, I'm trying the code with Arduino Uno board, and supplying 5 and 3.3v to led. \$\endgroup\$
    – Juanma
    Commented Jan 28, 2022 at 14:45
  • \$\begingroup\$ and I am assuming that it worked after supplying 5V, am I right? \$\endgroup\$ Commented Jan 28, 2022 at 14:47
  • 1
    \$\begingroup\$ Using PIC MCU in purpose to get right timing I set 16MHz. You need to do proper time calculation. And WS2812 need 5V, 3.3 is not proper. \$\endgroup\$
    – user263983
    Commented Jan 28, 2022 at 14:50
  • \$\begingroup\$ @Sanmvegsaini It doesn't work with these lines anyway. \$\endgroup\$
    – Juanma
    Commented Jan 28, 2022 at 14:53

1 Answer 1

2
\$\begingroup\$

This is a slight pain because the Neopixel library uses the F_CPU #define to figure out what speed to send the bits out. Unfortunately, I do not know a simple way to change the F_CPU #define that the IDE uses when libraries are compiled other than making a custom board type which is a pain.

So here is a hack that should work...

  1. Copy the files Adafruit_NeoPixel.h and Adafruit_NeoPixel.cpp from the Neopixel library into your sketch folder.

  2. Edit the Adafruit_NeoPixel.h file and add the line..

    #define F_CPU 8000000
    

    ...at the top.

  3. Recompile and download your sketch to the board.

How this works

The F_CPU #define is used by lots of Arduino code to figure out what speed the processor is running at. The board file for the Uno defines this as 16000000 since that is the default speed this board usually runs at. For better or for worse, there is no provision in this approach for changing the speed at run time (the #defines all are evaluated at compile time) so when you change the prescaler at run time, you still get the code that assumes the clock is running at 16MHhz. This means that your WS2812B signals are going to be twice as lloonngg as they should be, and so the pixels will see all bits as 1's and always show white. (More info on why this is the failure mode here).

So we need to tell the Neopixel code that it is really running at 8Mhz. The proper way to do this would be to make a custom board variant of the Uno called "Uno running at 8Mhz" where the only change is that above line with the F_CPU on it. Making a custom board file is possible, but too deep to put into a SE answer. Another proper way to do this would be to make a version of Adafruit_NeoPixel() that lets you optionally specify the clock speed (you could do this at compile time using C++ templates). Alas, adding this functionality to the Adafruit library is also too deep for this SE answer.

So instead we hack. The IDE will include any files in the sketch folder, so will use the files we copy and edit here. Since the Adafruit_NeoPixel.h is included in the Adafruit_NeoPixel.cpp file when it compiles, our re-definition of F_CPU here will be seen inside ``Adafruit_NeoPixel.cpp`. It will then generate the correct code for making the signals at 8Mhz.

Notes

  1. It is true that most WS2812B stripes will happily accept 3.3V signals on the DI line, but not all. Always test with 5V signals first to not waste time.
  2. Because you are changing the clock speed at runtime, expect other timing issues. For example, delay(1000) will take 2 seconds rather than 1. This could be fixed using a custom board file.
  3. If you are planning on powering the chip with only 3.3V, then you will need to change the speed in the fuses so that it is only running at 8Mhz form the moment it powers up. No good to have it start at 16Mhz because it may never get to the lines that slow it down to 8Mhz. This is probably easiest to do by setting the CLKDIV2 fuse, which will divide the 16Mhz clock down to 8Mhz. Again, this is properly done with a board variant in the boards.txt if you are willing to jump in! Maybe Easier to just use a board like a Pro Mini that already has the files set up to create 8MHz code.
\$\endgroup\$
1
  • \$\begingroup\$ I love this answer, thank you so much, I will test all the options, very interesting! As Justme I checked the code and I see something related with you are talking about. \$\endgroup\$
    – Juanma
    Commented Feb 2, 2022 at 15:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.