1

I want to learn more about the shiftout function used with Arduinos and used the implementation with my ATMEGA328p.

The Setup

This is the original shiftout function used in Arduino Programming, except that I replaced 1 with HIGH.

#define F_CPU 1000000UL
#define BAUD 9600UL
#define LSBFIRST 0
#define MSBFIRST 1
#define HIGH 0x1
#define LOW  0x0
#define INPUT 0x0
#define OUTPUT 0x1

#include <avr/io.h>
#include <util/delay.h>
#include <USART.h>
#include <pinDefines.h>

#define DATA PD7
#define LATCH PB2
#define CLOCK PB0

void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
    uint8_t i;
    for (i = 0; i < 8; ++i) {
        if (bitOrder == LSBFIRST) {
            PORTD |= (!!(val & (HIGH << i)) << DATA);
            } else {
            PORTD |= (!!(val & (HIGH << (7 - i))) << DATA);
        }
        PORTB |= (HIGH << clockPin);
        PORTB &= ~(HIGH << clockPin);
        }
}

Main

int main(void)
{
    initUSART();
    DDRD |= (HIGH << DATA);
    DDRB |= (HIGH << LATCH) && (HIGH << CLOCK);

    PORTB |= (HIGH << LATCH);
    shiftOut(DATA, CLOCK, LSBFIRST, 1);
    printBinaryByte(PIND);
    PORTB ^= (HIGH << LATCH);


    /* Replace with your application code */
    while (1) 
    {


    }

    return (0);
}

I created an example to understand more about the function, but the outcome does not add up to what I expected to see.

Szenario

PORTD |= (!!(val & (HIGH << 0)) << DATA);

This is what I expect for val = 1

  • (HIGH << 0) = 00000001
  • val & (HIGH << 0) = 00000001 & 00000001
  • !!(val & (HIGH << 0)) = 1
  • PORTD |= (1 << DATA)

As PORTD is the data pin on the shift register, it should get 00000001. When I print the binary number within PORTD I get: 10000111 and my LED Array connected to the 74HC595 displays: 10101111

I am really lost.... I checked the connections to the LEDs and they are all good. Can you explain to me why I receive this awkward result?

migrated from electronics.stackexchange.com May 8 '17 at 16:22

This question came from our site for electronics and electrical engineering professionals, students, and enthusiasts.

1

This is what I expect for val = 1

PORTD |= (1 << DATA) As PORTD is the data pin on the shift register, it should get 00000001.

your deduction is correct and the code is correct -> remember this is the case for LSBFIRST. so "val = 0x01" should be sent out as "0b1000 0000", which is precisely what the code is doing.

!(val & (HIGH << 0))

the "!!" operator is often used to turn a logic value into either '1' or '0'.

the code for shiftout() can be greatly improved.

1

First of all, PORTD represents the current output state of 8 physical GPIO pins (I think some devices have fewer pins on a port but usually there's 8). I think what you're trying to do is shift a sequence out of a single pin. So just reading it will only tell you what each of the 8 pin states are at that moment, not what you shifted out. I think Arduino's way of doing things is actually harder than just writing the plain C code so I'll ignore it (what is the '!!' ???).

So taking a step back, here's code for pushing an 8bit number serially out of a pin:

uint8_t serialData = 0b10011101;  // random data to shift out
uint8_t pin = 3;                  // the pin on PORTD we want to toggle


uint8_t i;
uint8_t nextBit;
for (i = 0; i < 8; i++) {

  // Find the nextBit value to set by shifting our input data by our index
  //   and clearing the other values.  E.g. if serialData == 0b10011101 and
  //   i == 2 then (0b10011101 >> 2) == 0b00100111.  0b00100111 & 1 == 1
  nextBit = (serialData >> i) & 0b00000001;

  // If the next bit is a one, we "or" it to set it
  if (nextBit) {
    // The 'or' operator can only set bits to 1
    PORTD |= 1 << pin;        // in this case, (1 << 3) == 0b00001000
  } else {
    // The 'and' operator can only clear bits to 0
    PORTD &= ~(1 << pin);    // in this case, ~(1 << 3) == 0b11110111
  }
}

If you want to add a "clock" pin to go along with the data (like the arduino code) then you can set the clock pin to whatever state before the "for" loop then use an "xor" operator to toggle it each time through the loop:

clockPin = 4;

// just before the 'for' loop set the clock to whatever starting state you
//   want.  In this case I'll start it at 0.
PORTB &= ~(1 << clockPin);
.
.
.
// Now in the 'for' loop (at the bottom for example)
PORTB ^= (1 << clockPin);

If you want the clock to toggle up then down in a sing loop cycle then you can just repeat the 'xor' twice in a row or force the state using 'or' and 'and' operators. Also, pay attention to timing, you might be toggling things too quickly if the microcontroller clock is fast. You can add delays to the loop to shape the timing of the pattern.

0

That is some ugly and needlessly slow code!

Nonetheless, I think the main point you need to keep in mind is that when you OR the value 00000001 into PORTD, you are only changing the bottom bit to a 1 - all other bits will be left untouched to whatever value they already had.

So, it seems that PORTD had the value of either 10000111 or 10000110 before you executed PORTD |= (1 << DATA) (which is the equivalent of PORTD |= 0b00000001)....

   10000110
OR 00000001
===========
   10000111

The output of your shift register is also just an artifact of whatever the most recent series of bits that you sent to it was. Each time you send a new bit, whatever was there will get shifted over and only the single new bit will be shifted in.

Make sense now?

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.