SPI Driver for Atmel USI - Missing LSB on ATtiny26

Thread Starter

Robert.Adams

Joined Feb 16, 2010
112
Hello everyone,

I'm trying to get a SPI interface working using the USI on the Attiny26 and I can not figure out why my LSB is always 0 when using SPI mode 0. In Mode 1 I get bad alignment of the clock & output data. I've asked this question to AVRfreaks but haven't gotten anywhere.

The SPI driver is an adoption from the Atmel AVR319 app note. I've modified the timers to work with the ATtiny26 (using output compares 1A & 1C, 1C for the automatic clear and 1A for the interrupt). I've also added a chip select to automatically go active low when a transmission is initialized and be brought high at the end of the transmission with the USI OVF interrupt.

I'm attaching the code and some Saleae logic captures for the two different SPI transmission modes. Please help!
 

Attachments

dannyf

Joined Sep 13, 2015
2,197
First of all, you will need to figure out which spi mode you want to be in.

After that, you will need to recognize that usi is very much like bit banging in that you will need to manually handle flipping the pins.

I can confirm for you that usi works on the 25 45 and 85 family and I presume that it works for your chip too.
 

Thread Starter

Robert.Adams

Joined Feb 16, 2010
112
First of all, you will need to figure out which spi mode you want to be in.

After that, you will need to recognize that usi is very much like bit banging in that you will need to manually handle flipping the pins.

I can confirm for you that usi works on the 25 45 and 85 family and I presume that it works for your chip too.
At this point I want to get both working - I am debugging the driver code and just looking at the outputs with a logic analyzer. All of the manual pin flipping is handled - CS goes low & I'm using USITC to toggle the CLK pin. The data that is shifted out also looks OK in mode 0, except the LSB is missing. My first guess would be the clock toggles once before transmission occurs, causing the shift register to throw out the MSB and broadcast everything shifted by 1 bit when it comes out, but this would lead to a sequence of 0,2,4,6 (for output 0,1,2,3) etc. I'm getting 0,0,2,2,4,4,6,6,8,8, etc, which doesn't make sense to me.
 

dannyf

Joined Sep 13, 2015
2,197
My first guess
Then post your code in plain text here - no one will go through a .zip file for that.

A waveform is also helpful.

When I do this soft of things I typically go through the interrupt: load up the pointer to the string to be communicated, enable the interrupt and start transmitting the first byte. At the end of it, the execution goes to the interrupt where you test for end of transmission. If not, load up the next byte and transmit it.

The beauty of it is that 1. It is a set-and-forget arrangement. Your code is free to do something else. And 2. It is easy to adapt the same code to spi, UART and i2c.
 

dannyf

Joined Sep 13, 2015
2,197
Here is what I mean by that.

Code:
//isr
/usi ovf interrupt
ISR(USI_OVF_vect) {
    USISR |= (1<<USIOIF);                            //clear the flag by writing 1 to it
    if (*++_USI_ptr==0) {                            //last non-zero char has been sent
        USICR &=~(1<<USIOIE);                        //disable the interrupt
        _USI_BUSY = 0;                                //usi no long busy
        IO_SET(USI_PORT, USI_CS);                    //terminate spi chip select
    }
    else spi_write(*_USI_ptr);                        //send the next byte
}
and here is how you would use it:

Code:
    while(1) {
        if (!spi_busy()) {                    //if spi is not busy
            spi_puts(sRAM);                    //send the string
        }
        IO_FLP(LED_PORT, LED);                //flip led - simulate other tasks
    }
spi_busy() returns 1 if (the usi-emulated) spi is busy, and 0 otherwise.
If the spi is not busy, spi_puts() loads the string to be sent (in this case, sRAM) and starts the transmission.

The isr detects if the null-terminated string has been sent. If not, it loads the next char and it starts transmission again.

Here is the waveform of sending "01234abcd\00" - the terminating char is not sent.

The attiny is running at 1MIPS.

The code is largely hardware independent - I actually adapted my uart code on a STM32L1 for this purpose. and with minor modifications, you can run uart, or i2c over it as well.

note: i didn't implement receiving but it is fairly simple to do.

hope it helps.

usispi_isr.PNG
 
Last edited:
Top