I have a board that is designed and produced. It was designed to use an Atmega328 with the internal clock. There is no space on the board to place an external clock. After completing these and testing them I have found that 8Mhz is too slow to complete my main loop actions seamlessly. The main loop involved shifting data out to 20 registers. Is there any way to speed this up without going back to the drawing board on the PCB design? Can another AVR be substituted as a drop in replacement with a faster internal clock? Can the internal clock be made to run faster or doubled to achieve 16Mhz?

The application is a matrix display that has 50 columns and 7 rows. The registers are the display buffers and represent the current state of the display. Because of the multiplexing in the matrix, only 1/5 of the pixels are illuminated at any given time. In order to trick the eyes into seeing all lit at the same time the speed needs to be faster. The display flickers with the interal clock. If I connect and UNO board instead of using the on board ATMEGA328 (QFP) it works good. So the difference between 8Mhz and 16Mhz is visible. If I can double the speed of the display loop than that is a solution. Below is the loop, for reference, latchPin = 8, clockPin = 12, dataPin = 11.

  • You have to optimize your code, you may can do the shifting out using hardware SPI. Show your code and describe your application. – Bence Kaulics May 11 '17 at 8:14
  • I have updated the question to include details and the code. There are 20 called to shiftout() in the display loop. – Tanner Ewing May 11 '17 at 12:03
  • What is this function shiftOut it uses GPIO pins? – Bence Kaulics May 11 '17 at 13:28
  • Reference here: arduino.cc/en/Reference/ShiftOut – Tanner Ewing May 11 '17 at 13:30
  • 1
    Ideally improve the efficiency, but you could consider glueing an oscillator on top of the ATmega or somewhere on the PCB to improve your prototype, and then doing a re-spin of the board design when you need a quantity where such re-work is impractical. Note that there are a lot of ARM MCUs that cost less than an ATmega and can run faster with internal clocks, though they are not through-hole. – Chris Stratton May 11 '17 at 16:52

shiftOut() relies on digitalWrite(), which is dead slow by AVR standards. You could probably get a factor 5, or even 10, by reimplementing shiftOut() using direct port access instead. Using SPI (as suggested in the comments) would be even faster, but you would need to clock out of pin 13.

Edit: here is an attempt at such implementation. Note that it is completely untested. Note also the small delay meant to avoid the thing being too fast. You may try to play with the delay value.

#include <util/delay.h>

 * Especialized and faster shiftOut().
 * This assumes no one is touching PORTB at the same time.
 * dataPin = 11 (PB3), clockPin = 12 (PB4), bitOrder = MSBFIRST.
void myShiftOut(uint8_t val)
    for (uint8_t i = 0; i < 8; i++)  {
        // Send data bit.
        if (val & 0x80)
            PORTB |= _BV(PB3);
            PORTB &= ~_BV(PB3);
        val <<= 1;

        // Toggle clock twice.
        PORTB |= _BV(PB4);
        PORTB &= ~_BV(PB4);
  • When the SPI bus is not possible, then this is the way to do it. Arduino has bitSet() and bitClear macros which result into the same I/O bit instructions. The 'val' should shift the 0x80 to the right. It is not possible to delay "0.5" only unsigned long numbers, and delayMicroseconds() could be used, or the delay by clock cycles (I forgot the name). – Jot May 11 '17 at 22:08
  • @Jot: You wrote: “The 'val' should shift the 0x80 to the right”. Shifting 0x80 to the right works also, although it produces slightly less efficient code, as it requires an extra register to hold the bit mask. Shifting val to the left, with the fixed mask 0x80, allows the compiler to use a bit-test instruction (sbrs) and never store the bit mask. “It is not possible to delay ‘0.5’ only unsigned long numbers”. This is incorrect. Delaying by 0.5 µs (or even 0.125 µs, given an 8 MHz clock) works perfectly well with _delay_us() (not with delayMicroseconds()). Try it! – Edgar Bonet May 12 '17 at 7:57
  • I'm very sorry, I made two mistakes. You are right. I have tested it in software and _delay_us(0.5) results in a delay of 0.506 µs (according to a software approximation. Thank you. I learned something new. – Jot May 12 '17 at 21:23

I wrote the following earlier but the moderator deleted:

" A few things to try.

An external oscillator.

An external rc oscillator.

Optimized code: the code left a lot to be desired.

Optimized hardware: there is no reason you shift out the same value so many times. That's a waste of time. "

edit: just give you some sense on a 16MIPS ATmega:

1) your code executed in 2.54ms -> a refresh rate of 400Hz;

2) your code software-optimized can be executed in 0.103ms -> a refresh rate of 1Khz;

3) your code both software and hardware optimized can be executed in 0.054ms -> a refresh rate of 2Khz.

what it tells you is that:

1) raising the clock isn't the only way to speed up execution;

2) there is a fundamental limit for this topology on how many shift registers you can drive before it starts to flicker;

3) to drive the most out of your design, you have to pay attention to both software and hardware. that's why embedded engineers get paid a lot of money to do;

BTW, the higher clock rate, the more likely you will run into timing issues in a long chain of shift registers. 20 - 25 shift registers are likely the limit in real life applications.


It is 8MHz and not faster.
The only way to make it faster is to add an external crystal with two capacitors and change a fuse for external clock.

Many ATtiny chips can run at higher frequencies with the internal clock. Do you use a DIP package of the ATmega328(p) ?

Did you optimize the code ? The ATmega328(p) has highly optimized code to change an output pin. At what rate do you want to shift out data ?

  • The code could be optimized still (maybe). I would need to nearly double execution time of the loop. I have updated the question to include code and additional details about the application. – Tanner Ewing May 11 '17 at 12:04
  • Double the execution time is easy. A digitalWrite() takes about 5µs and a optimized I/O instruction is 0.125µs, that is 40 times faster. – Jot May 11 '17 at 22:11
  • This is the slow Arduino shiftOut code: github.com/arduino/Arduino/blob/master/hardware/arduino/avr/… – Jot May 11 '17 at 22:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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