7
\$\begingroup\$

I'm coding a system that need to shift out 16 bits fast from a uint16_t variable to two paired 74HC595 shift registers. I'm running the code on an Arduino (atmega328@16Mhz) and the shiftOut in the Arduino is way too slow. The librarys digitalWrite function is slow as well.

The best idea I came up with is a for loop that loops through all the 16 bits of the variable and writes 1/0 to data pin, set the clock bit high as short as possible and then goes to the next bit of the variable.

Is this the fastest solution and if so, how do I loop through a uint16_t variables bits in c++?

\$\endgroup\$
1

4 Answers 4

8
\$\begingroup\$

This is simple enough with the SPI library.

Connect your shift register input to the MOSI pin (Master Out, Slave In) - Digital pin 11 - and you will have nice fast transfers.

There will be a very slight delay between the upper and lower 8 bits of each word transmitted.

For example:

#include <SPI.h>

void begin()
{
    SPI.begin();
}

void loop()
{
    unsigned int val;

    val = rand()%0xFFFF; // Generate a random 16 bit number

    SPI.transfer(val >> 8);  // Output the MSB first
    SPI.transder(val & 0xFF); // Followed by the LSB

    delay(1000);  // Wait a sec...
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ What about the latch pins? This appears to just be toggling the serial data line, but I do't see how anything is getting latched into the register. \$\endgroup\$
    – captncraig
    Oct 20, 2011 at 21:27
  • \$\begingroup\$ I have never used one of the shift registers in question, so I don't know what is required. Usually you wrap the SPI in a digitalWrite(x,LOW) and digitalWrite(x,HIGH) to toggle a digital IO line of your choice as a "chip select" or "latch toggle" or whatever (obviously reverse the toggle for active high). Use whatever pin you like. \$\endgroup\$
    – Majenko
    Oct 20, 2011 at 23:30
8
\$\begingroup\$

If you're looking to use a software data transfer, you don't want to use the functions digitalWrite. They are very slow, this is because they need to translate the pin number through a table to an actual register (PORTx), mask the right bit and change it. All pins in arduino are mapped to numbers, while underneath they can belong to port A, B, C, and even more on the MEGA version of the Arduino.

It's much faster to directly modify the AVR registers. Such as PORTB and a-like. You indeed need to step through each bit. I would create a for loop from 0 to 15 and do some bit shifting and masking.

Because I don't you know pinning configuration I can't give an exact example. However it will probably look very close to this. With 'very close' I mean that this is untested.

void ShiftOut(UI16_t data)
{
 // Initialize (you may want to set CLK to low) - as we're toggling later on.

// step from bit 0 to 15 for(UI08_t i = 0; i < 15; i++) { // Check the content of this data bit // Shift data so this bit is LSB, and mask it with 1 so we only look at this bit. if ((data >> i) & 0x1 == 1) { // set data pin high, like PORTB |= 1<<4; // when pin 4 of portB is your data pin // Doing an OR will make pin B4 always high } else { // set data pin low, like PORTB &= ~(1<<4); // Doing an AND with the inverse means all pins except B4 will be unchanged }

// Generate clk to 'transfer' the bit: // This can likely be done by using PORTB ^= 1 << 5; (pin B5 in this example) // ^= toggle // Do this TWICE, so CLK goes high/low }

// as you're using a shift register, you may want to toggle LATCH pin as well.. }

To find out what hardware pin number (don't assume pin 4 is pin B4 or A4!) you need to look at the schematic of Arduino.

I've ran a similar code on a PIC32 (runs at 80MHz). The PIC32 was able to do this at about 1,5 MHz, but a few extra lines of code was running in main() to compute a new output. Nevertheless, it can be done very quickly.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for the detailed answer, I will try the SPI approach first as that one seem faster. I also found more examples for the software approach at bildr.org/2011/02/74hc595 \$\endgroup\$
    – tirithen
    Oct 19, 2011 at 20:47
5
\$\begingroup\$

You need to use the SPI peripheral instead of doing the shifting in software. I've never used an Arduino, but the online reference suggests there is an SPI library you can use. If that doesn't suit your needs, you can look up the SPI peripheral in the ATMega datasheet. SPI peripherals are usually fairly easy to setup and use.

\$\endgroup\$
1
\$\begingroup\$

I am old school, been coding since 100 kHz processors. Use inline code for bit manipulation directly to the port, rather than a loop. Loop requires overhead, inline is faster although takes more program space. See AVR035: Efficient C Coding for AVR for bit manipulation of the ports.

\$\endgroup\$

Your Answer

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

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