First and last bit lost on shiftOut

Hi guys, I am using an Arduino nano in conjunction with a UAA2022 16bit shift register to control a 16 segment display. The seriel data is sent via shiftOut.

The problem I'm having is the first bit is lost completely and the last bit is counted twice.

I have tried setting clock and latch pins to HI/LO in different combinations, but the only way I can get the shift register to work properly is to use a digitalWrite between shiftOut and latch to manually send an extra bit.

Has anyone experienced anything like this before? Any ideas?

Here is the complete sketch (problem is present)

//v1.5.1

//Input from photoresistor
const byte Brightness = A0;

//Input from X and Y poti's for gear position
const byte x_axis = A4;
const byte y_axis = A5;

//Outputs for UAA2022 display driver
const byte Data = 3;
const byte VDR = 4;
const byte CLK = 5;
const byte CO = 6;

//assign a number to R and N for neutral and reverse
const byte N = 0;
const byte R = 7;

const unsigned int Up = 3;    // voltage Y Axis between 3rd and neutral gearstick position
const unsigned int Down = 2;  // Vlotage Y Axis between 4th and neutral gearstick position
const unsigned int Left = 2;  // voltage X Axis between 1st and 3rd gearstick position
const unsigned int Right = 3; // Voltage x Axis between 3rd and 5th gearstick position

unsigned long startMillis; //some global variables available anywhere in the program
unsigned long currentMillis;
const unsigned long period = 1000; //the value is a number of milliseconds
int brightnessValue;
int brightnessOutput;

void setup()
{
  startMillis = millis(); //initial start time
  pinMode(Brightness, INPUT);
  pinMode(x_axis, INPUT);
  pinMode(y_axis, INPUT);
  pinMode(Data, OUTPUT);
  digitalWrite(VDR, HIGH);
  pinMode(VDR, OUTPUT);
  digitalWrite(CLK, HIGH);
  pinMode(CLK, OUTPUT);
  pinMode(CO, OUTPUT);
}

void updateGearIndicator(uint8_t first, uint8_t second)
{
  digitalWrite(VDR, LOW); //ground vdr_tlach and hold low for as long as you are transmitting
  //shiftout only supports one byte, so the 16 bits needed for display driver are split into 2 bytes
  //The second byte is sent first.
  shiftOut(Data, CLK, MSBFIRST, first);
  shiftOut(Data, CLK, MSBFIRST, second);
  //return the latch pin high to signal chi0 that it no longer needs Data to listen for information
  digitalWrite(VDR, HIGH); //pull the VDR clock to save the data
}

void loop()
{

  //Brightness control code
  // runs every 1 seconds
  currentMillis = millis();
  if (currentMillis - startMillis >= period) {
    //Create brightness value from photoresistor input
    brightnessValue = analogRead(Brightness);
    //Map Values between 0 to 255 for an output value
    brightnessOutput = map(brightnessValue, 1, 1023, 1, 255);
    startMillis = currentMillis;
  }
  //Send brightness value as constant PWM to display driver.
  analogWrite(CO, brightnessOutput);

  

  //Gear indicator

  // read the input from x axis poti.
  int x_input = analogRead(x_axis);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float x_position = x_input * (5.0 / 1023.0);

  // repeat for y axis
  int y_input = analogRead(y_axis);
  float y_position = y_input * (5.0 / 1023.0);

  if (y_position < Up && y_position > Down)
    //Neutral
  {
    updateGearIndicator(B01100001, B01100001);
  }

  if (x_position < Left && y_position > Up)
    //First
  {
    updateGearIndicator(B01000000, B00000101);
  }

  if (x_position < Left && y_position < Down)
    //Second
  {
    updateGearIndicator(B10010011, B10010011);
  }

  if (x_position > Left && x_position < Right && y_position > Up)
    //Third
  {
    updateGearIndicator(B01010010, B10010011);
  }

  if (x_position > Left && x_position < Right && y_position < Down)
    //Fourth
  {
    updateGearIndicator(B01000000, B11000001);
  }

  if (x_position > Right && y_position > Up)
    //Fifth
  {
    updateGearIndicator(B01010010, B11010010);
  }

  if (x_position > Right && y_position < Down)
    //Reverse
  {
    updateGearIndicator(B00100001, B11010011);
  }
}

Here is a snippet of how i can make it work by sending an extra bit manually

if (x_position < Left && y_position > Up){
    digitalWrite(vdr_latch,LOW);
    shiftOut(data_output,clk,MSBFIRST,B01000000);
    shiftOut(data_output,clk,MSBFIRST,B00000101);
    digitalWrite(data_output, LOW);
    digitalWrite(vdr_latch,HIGH);
    }
    if (x_position < Left && y_position < Down){
    digitalWrite(vdr_latch,LOW); 
    shiftOut(data_output,clk,MSBFIRST,B00010011);
    shiftOut(data_output,clk,MSBFIRST,B10010011);
    digitalWrite(data_output, HIGH);
    digitalWrite(vdr_latch,HIGH); 
    }

There are two ways a chip can transfer data in the rising or falling edge of the clock. If you are missing a bit then it sounds like your software is not matching what the chip wants.

To fix this either simply bit bang the output pins so you can control the edge of the clock. Or wire it up to the SPI pins and change the SPI mode to one of the values 0 through 3 and see which one works.

Thanks for the reply, SPI is out of the question due to constraints on the circuit board (its a very small package) plus I read the nano doesnt support SPI?

I will look into bitbang and give it a try.

Is there any chance you could help me out with an example of how one would do a bitbang alongside a clock signal? I'm a little overwhelmed.

plus I read the nano doesnt support SPI?

Where did you read that? It is wrong, it is the same chip as a Uno.

SPI is out of the question due to constraints on the circuit board (its a very small package)

I don't understand that, it makes little sense.

an example of how one would do a bitbang

void SPIbang(int val) { // bit bang SPI so it is interruptible
  for ( int i = 0; i < 8; i++) {
    if ( (val & 0x80) == 0) {
      digitalWrite(SDIPIN, LOW);
    }
    else {
      digitalWrite(SDIPIN, HIGH);
    }
    digitalWrite(SCKPIN, HIGH); // toggle clock
    digitalWrite(SCKPIN, LOW);
    val = val << 1;
  }
}

alongside a clock signal?

I don't understand what that means.

UAA2022 is from 1983. Where did you even find one?

Looks to me that it needs SPI mode 3, clock starts high, ends high.
https://www.totalphase.com/support/articles/200349236-SPI-Background

You manage that in setup(). The default is Clock Mode 0, 4 MHz clock speed, MSB First.

So:

digitalWrite (VDPpin, LOW);
SPI.transfer(byte1);
SPI.transfer (byte2);
digitalWrite (VDPin, HIGH); // outputs update on this rising edge.

Sorry, I didn’t explain myself properly. I have had a circuit board printed for my project. Due to the limited space I am working with, packaging the nano, the shift register, and other components on the board was quite difficult. I would not be able to pick up the SPI pins on the nano, it’s a physical problem, there is no clear path between these pins and the pins on the shift register with the space I have available.

Anyway, thanks for the example, I will play around and see if I can figure it out.
Appreciate the help :slight_smile:

I would not be able to pick up the SPI pins on the nano, it's a physical problem, there is no clear path between these pins and the pins on the shift register with the space I have available.

This sort of things happens all the time with professional circuit development. What you do is use a scalpel to cut tracks and Kynar wire ( 30 AWG insulated wire ) to make the connections you want.

Are the SPI pins currently unused? If so, the shiftOut pins can be left as inputs, and the SPI pins just jumpered on the Nano to connect them.

Jumping the pins definitly seems like the simplest solution, however bitbang seems like it would be a nice tool to have at my disposal so i think ill try to solve the problem with bitbang first.

So, just getting my bearings here.. I would need to send 16 bits, using bit bang i could just send 16 bits
without having to split them into 2 bytes?

So i could write something like this:

void SPIbang(int val) { // bit bang SPI so it is interruptible
  for ( int i = 0; i < 16; i++) {
    if ( (val & 0x8000) == 0) {
      digitalWrite(SDIPIN, LOW);
    }
    else {
      digitalWrite(SDIPIN, HIGH);
    }
    digitalWrite(SCKPIN, HIGH); // toggle clock
    digitalWrite(SCKPIN, LOW);
    val = val << 1;
  }
}

Then i just write something like this:

if (a<b){
SPIbang(0010000100100001);
    }

am i at least on the right track? :cold_sweat:

Also do i have the option with bitbang to send msbfirst/lsbfirst or will it automatically send bits from right to left?

Got it! :slight_smile:

void updateGearIndicator(uint8_t first, uint8_t second)
{
  digitalWrite(VDR, LOW);
  for ( int i = 0; i < 8; i++) {
    if ( (first & 0x80) == 0) {
      digitalWrite(Data, LOW);
    }
    else {
      digitalWrite(Data, HIGH);
    }
    digitalWrite(CLK, LOW); // toggle clock
    digitalWrite(CLK, HIGH);
    first = first << 1;
  }
  for ( int i = 0; i < 8; i++) {
    if ( (second & 0x80) == 0) {
      digitalWrite(Data, LOW);
    }
    else {
      digitalWrite(Data, HIGH);
    }
    digitalWrite(CLK, LOW); // toggle clock
    digitalWrite(CLK, HIGH);
    second = second << 1;
  }
  digitalWrite(VDR, HIGH);
}
 if (y_position < Up && y_position > Down)
    //Neutral
  {
    updateGearIndicator(B11000010, B11000010);
  }

  if (x_position < Left && y_position > Up)
    //First
  {
    updateGearIndicator(B10000000, B00001010);
  }

  if (x_position < Left && y_position < Down)
    //Second
  {
    updateGearIndicator(B00100111, B00100111);
  }

  if (x_position > Left && x_position < Right && y_position > Up)
    //Third
  {
    updateGearIndicator(B10100101, B00100111);
  }

  if (x_position > Left && x_position < Right && y_position < Down)
    //Fourth
  {
    updateGearIndicator(B10000001, B10000011);
  }

  if (x_position > Right && y_position > Up)
    //Fifth
  {
    updateGearIndicator(B10100101, B10100101);
  }

  if (x_position > Right && y_position < Down)
    //Reverse
  {
    updateGearIndicator(B01000011, B10100111);
  }
}

Thanks guys, appreciate the help

Does that work for you?
If not try reversing the order of these two commands:-

    digitalWrite(CLK, LOW); // toggle clock
    digitalWrite(CLK, HIGH);

SPIbang(0010000100100001);

Probably 0b0010000100100001 would work.
Or it's hex equivalent 0x2121

Yes the bit bang works on the UAA2022, in my example the clock is reversed (compared to the example mike originally posted), then all I had to do was swap the first and last bits as the first bits were previously lost, and now everything is working great.

I couldn’t get it to work without splitting the information into two bytes, is there some kind of trick to it?

couldn't get it to work without splitting the information into two bytes, is there some kind of trick to it?

You had it in the code you posted but the variable was still an int type. Try making this a long.

Or to keep it as an int change

   if ( (val & 0x8000) == 0) {
Into this

  if ( (val & mask) == 0) {

Having defined the variable mask as a unsigned int at the start With a value of 0x8000 at the start of the function. Then instead of shifting the val variable one place to the left you shift the mask variable one place to the right.

Got it figured out, thanks for the help.