-1

I'm trying to output 16 bits of data to two 8 bit shift registers (74HC595), which drive a 8x8 led matrix. There is a problem with the way the code outputs data to the registers, its like no data is even shifted onto them. The first 8 bits handle the rows and the last 8 bits handle the columns.

I have already written this code in Arduino, but I am trying to re-write in it C (we have to learn AVR programming in C for college). The Arduino version works fine, so I know the circuit is correct. I'm using Atmel Studio.

For example, I replaced all the digitalWrite(clockPin, HIGH)'s with PORTD = PORTD | (1<<clockPin). But I can't get anything to output with my code. For refreshing the led display the variable refreshFlag is used. For updating the game state (which hasn't been implemented yet) the variable updateFlag is used.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>

//portd 2(latch), 4(clock), 7(data). Same as pins on arduino board
#define latchPin 2
#define clockPin 4
#define dataPin 7

//functions
void init_ports(void);
void init_USART(void);
void init_timer0(void);
void sendmsg (char *s);
void drawData(uint8_t myData[]);
void shiftOut(uint8_t myDataOut);

unsigned char qcntr = 0,sndcntr = 0;   /*indexes into the queue*/
unsigned char queue[100];       /*character queue*/

int updateTimeCount;
int updateDelay; 
int updateFlag; // Flag for updating game state
int refreshFlag;// Flag for refreshing display


uint8_t data[] = {0b00000001,
                  0b01111110,
                  0b11000110,
                  0b01001010,
                  0b01010010,
                  0b11100010,
                  0b01111110,
                  0b01000010};

int main(void) {  
    //init_USART();// Serial for debugging purposes  
    init_ports();
    init_timer0();
    clearData();
    newBlock();

    sei();

    PORTB = 0; //PortB for debugging
    updateTimeCount = 0;
    updateDelay = 13; // Game updates every 13* 16ms = 208ms.
    updateFlag = 0; 
    refreshFlag = 0; // Display updates every 16ms

    while (1) {

        if(updateFlag) { //updates game state
            PORTB = (1<<5); //debugging led
            updateFlag = 0;     
        }

        if(refreshFlag) { //refreshes display
            drawData(data); //output data array

            refreshFlag = 0;
        }
    }
    return 0;
}

void drawData(uint8_t myData[]) { //takes data and outputs it
  for (uint8_t j = 0; j <= 7; j++) {

    //ground latchPin and hold low for as long as you are transmitting
    PORTD = PORTD & ~(1<<latchPin); //latch pin low

    //shift data into regs   

    shiftOut(~(1<<j)); //grounds row. For example 10111111 means 6th row is on

    shiftOut(myData[j]);  //outputs column data.

    //return the latch pin high to signal chip that it 
    //no longer needs to listen for information
    PORTD = PORTD | (1<<latchPin); //latch pin high 
  }
}

void shiftOut(uint8_t myDataOut) { //shifts 8 bits out to register

  // This shifts 8 bits out LSB first, 
  //on the rising edge of the clock,
  //clock idles low

//internal function setup
  uint8_t i=0;
  int pinState;

//prepare shift register for bit shifting
  PORTD = PORTD & ~((1<<dataPin) | (1<<clockPin)); //clock & data pin low

  //for each bit in the myDataOut
  for (i=0; i<=7; i++)  {

    PORTD = PORTD & ~(1<<clockPin); //clock low

    //if the value passed to myDataOut and a bitmask result 
    // true then... so if we are at i=6 and our value is
    // %11010100 it would the code compares it to %01000000 
    // and proceeds to set pinState to 1.
    if ( myDataOut & (1<<i) ) {
      pinState= 1;
    }
    else {  
      pinState= 0;
    }

    //Sets the pin to HIGH or LOW depending on pinState
    PORTD = PORTD | (pinState<<dataPin);

    //register shifts bits on upstroke of clock pin  
    PORTD = PORTD | (1<<clockPin);

    //zero the data pin after shift to prevent bleed through
    PORTD = PORTD & ~(1<<dataPin);
  }

  //stop shifting
  PORTD = PORTD & ~(1<<clockPin);
}

void sendmsg (char *s) { //create serial message
    qcntr = 0;    /*preset indices*/
    sndcntr = 1;  /*set to one because first character already sent*/

    queue[qcntr++] = 0x0d;   /*put CRLF into the queue first*/
    queue[qcntr++] = 0x0a;
    while (*s)
    queue[qcntr++] = *s++;   /*put characters into queue*/

    UDR0 = queue[0];  /*send first character to start process*/
}


/*this interrupt occurs whenever the */
/*USART has completed sending a character*/

ISR(USART_TX_vect) {
    /*send next character and increment index*/
    if (qcntr != sndcntr)
        UDR0 = queue[sndcntr++];
}

ISR(TIMER0_OVF_vect) { //overflow every 16ms
    TCNT0 = 6; // 256-250 = 6
    ++updateTimeCount;
    if(updateTimeCount >= updateDelay) { //after 208ms, update game state
        updateFlag = 1; // Call for updating game
        updateTimeCount = 0;

    }

    refreshFlag = 1; // Call for updating screen
}

void init_ports() {
    DDRD = (1<<latchPin) | (1<<clockPin) | (1<<dataPin); //enable pins
    PORTD = 0; //set all pins low
    DDRB = (1<<5); //enable the built-in led for debugging
    PORTB = 0; //set all pins low
}
void init_USART() {
    UCSR0B = (1<<TXEN0) | (1<<TXCIE0); //enable transmitter and tx interrupt
    UBRR0 = 103; //baud 9600
}
void init_timer0() {
    TCCR0A = 0;
    TIMSK0 = (1<<TOIE0);    // enable interrupt
    TCCR0B = (5<<CS00); // prescalar 1024. 16Mhz/1024 = 15625Hz = 0.064ms
    // For refresh rate of 62.5Hz, 16ms /0.064ms = 250
    TCNT0 = 6; // 256-250 = 6
}

I really can't tell what the problem is and I don't what to look up either. I think the problem is in the functions drawData and shiftOut, because that's where all the data is shifted out. I tested the timer0 interrupt by flipping the built-in led and that seems to be working fine.
Are the ports flipping too fast for the shift regs to receive the data? Is there a semantic error somewhere? Can you see any problems. Thanks for any help!

11
  • 2
    Re “I think the problem is”: better be sure. Break down the problem into smaller chunks and test them one by one. Writing the full program before you start testing is a bad idea. Apr 21 '20 at 22:29
  • @EdgarBonet I did some testing with LED's and the refreshFlag isn't getting set to one when the timer0 overflows. Which means drawData() is never called. But I have tested the updateFlag, and it does work. So I tried calling the drawData() when the updateFlag is one but still nothing. So I'm not really sure where the problem is.
    – sparpo
    Apr 21 '20 at 23:04
  • 1
    As Edgar Bonet said: start with code that's as simple as possible for testing / debugging basic functionality. First get that to work before moving on to more complex code. See if your problem can be reproduced with (much) simpler code.
    – StarCat
    Apr 22 '20 at 7:10
  • 1
    @juraj I'm using C not Arduino code
    – sparpo
    Apr 22 '20 at 10:15
  • 1
    @KIIV @Gerben @Edgar_Bonet @StarCat @jsotola @Juraj There was no data being sent because a function clearData() was called before the while loop. I feel so stupid! However there was a problem with the refreshFlag which @KIIV noticed! Thanks for your help!
    – sparpo
    Apr 22 '20 at 12:59
1

Thanks to everyone who commented on my question, I was able to solve the problem with your help!

The first issue was that the compiler interpreted the if(refreshFlag) {...} as if(0) {...} so the data would have never been outputted to the display.

This happens is because the compiler doesn't see that the refreshFlag can be set to 1 in the timer0 interrupt service routine. From its perspective I have written an if statement that never runs.

As @KIIV pointed out, if the volatile keyword is used when declaring refreshFlag, this tells the compiler that this variable can change at any time.

The second and more embarrassing issue was that I forgot to remove a call to clearData() function which clears the data array. So I was just outputting nothing to the registers!

This is what the fixed variables and main should look like:

int updateTimeCount;
int updateDelay; 
volatile int updateFlag; // Flag for updating game state
volatile int refreshFlag;// Flag for refreshing display


uint8_t data[] = {0b00000001,
                  0b01111110,
                  0b11000110,
                  0b01001010,
                  0b01010010,
                  0b11100010,
                  0b01111110,
                  0b01000010};

int main(void) {  
    //init_USART();// Serial for debugging purposes  
    init_ports();
    init_timer0();

    sei();

    PORTB = 0; //PortB for debugging
    updateTimeCount = 0;
    updateDelay = 13; // Game updates every 13* 16ms = 208ms.
    updateFlag = 0; 
    refreshFlag = 0; // Display updates every 16ms

    while (1) {

        if(updateFlag) { //updates game state
            PORTB = (1<<5); //debugging led
            updateFlag = 0;     
        }

        if(refreshFlag) { //refreshes display
            drawData(data); //output data array

            refreshFlag = 0;
        }
    }
    return 0;
}
1
  • It's OK to accept your own answer as the solution to the problem. You'll have to wait 48 hours before you can accept it.
    – VE7JRO
    Apr 22 '20 at 15:14

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.