Search code examples
avrspiatmegaatmelstudio

using AVR Atmega32 SPI without SPI pins


I am fairly new to programming microcontrollers. I have some experience with the Arduino, but after I had nearly finished my project, I deceided to moved my current project to something cheaper and smaller. So I am now using the AVR ATmega32 with Atmel studio.

I'm trying to use the ATmega32 to communicate to a MAX7219 chip for multiplexing with an led matrix. however, I do have multiple different devices that I want to communicate with.

How can I communicate with the device without actually using the SPI pins provided on the microcontroller? I have made a test project, but there seems to be something wrong and I can't figure it out what the issue is. I think I managed to get it in test mode, because all LEDs are lit, but I can't get it to do anything after that. I can't even clear the display/shut it down. I have checked the wiring over again. Is my coding perhaps incorrect as far as pin configuration and assigning the pins? Are there any suggestions for this or a better way to write my code?

Here is the link to the MA7219 datasheet

//test

#include <avr/io.h>


int main(void)
{

DDRB = 0b00000111; // pin 1(data), 2(clock) and 3(latch) are outputs

PORTB = 0 << PINB0; // data pin 1 is low
PORTB = 0 << PINB1; // clock pin 2 is low
PORTB = 0 << PINB2; // latch pin 3 is low

uint16_t data;
data = 0b0000110000000000; // data to shift out to the max7219

 //read bit
uint16_t mask;
 for (mask = 0b0000000000000001; mask>0; mask <<= 1) 
{ 
    //iterate through bit mask
    if (data & mask)
    { // if bitwise AND resolves to true
        // send one
        PORTB = 1 << PINB0;
        // tick
        PORTB = 1 << PINB1;
        // tock
        PORTB = 0 << PINB1;
    }
     else{ //if bitwise and resolves to false
        // send 0
        // send one
        PORTB = 0 << PINB0;
        // tick
        PORTB = 1 << PINB1;
        // tock
        PORTB = 0 << PINB1;
    }

}

PORTB = 1 << PINB2; // latch all the data
PORTB = 1 << PINB0; // data pin 1 is high
PORTB = 0 << PINB1; // clock pin 2 is low
PORTB = 0 << PINB2; // latch pin 3 is low
}

Solution

  • Yes, your bit-bang code has a problem in that you assign the entire value of the register each time without preserving the existing value. Hence you erase your data signal at the instant you drive your clock, violating the hold time of the receiver and resulting in unpredictable operation.

    Instead of assigning pins with =, you should set them with |= or clear them with &= ~(value)

    For example:

         PORTB = 1 << PINB0;      //drive data
        // tick
        PORTB |= 1 << PINB1;      //clock high PRESERVING data
        // tock
        PORTB &= ~(1 << PINB1);   //clock low
    

    You may also need to insert a slight delay between pin operations.

    Technically, given that you are already using an if for the data state, you could also re-drive the data signal with an OR in the assignment, for example

    if (data & mask)
    { // if bitwise AND resolves to true
        // send one
        PORTB = 1 << PINB0;
        // tick
        PORTB = (1 << PINB1) | (1 << PINB0);
        // tock
        PORTB = 0 << PINB1 | (1 << PINB0);
    }
     else{ //if bitwise and resolves to false
        // send 0
        // send one
        PORTB = 0 << PINB0;
        // tick
        PORTB = 1 << PINB1;
        // tock
        PORTB = 0 << PINB1;
    }