SPI Driver for Atmel USI - Missing LSB on ATtiny26

Discussion in 'Embedded Systems and Microcontrollers' started by Robert.Adams, Aug 8, 2016.

  1. Robert.Adams

    Thread Starter Active Member

    Feb 16, 2010
    112
    5
    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!
     
  2. dannyf

    Well-Known Member

    Sep 13, 2015
    1,811
    362
    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.
     
  3. Robert.Adams

    Thread Starter Active Member

    Feb 16, 2010
    112
    5
    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.
     
  4. dannyf

    Well-Known Member

    Sep 13, 2015
    1,811
    362
    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.
     
  5. dannyf

    Well-Known Member

    Sep 13, 2015
    1,811
    362
    Here is what I mean by that.

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

    Code (Text):
    1.  
    2.     while(1) {
    3.         if (!spi_busy()) {                    //if spi is not busy
    4.             spi_puts(sRAM);                    //send the string
    5.         }
    6.         IO_FLP(LED_PORT, LED);                //flip led - simulate other tasks
    7.     }
    8.  
    9.  
    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: Aug 8, 2016
  6. Robert.Adams

    Thread Starter Active Member

    Feb 16, 2010
    112
    5
    Sorry about the delay - I fat fingered my board and had to replace a few ICs. What simulation software is your picture from? If you are able to simulate code running on at ATtiny it may be a best first step for me to try my code on it. It would certainly keep me from blowing up parts for a bit.
     
Loading...