SPI using USI not working

Go To Last Post
23 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My system is using an ATtiny1634 as an SPI slave.  Using a scope I can see the master send one byte with 8 clock pulses and MOSI is moving as expected, but MISO stays low.  It looks like it's trying to drive high with some pulses that are just above 0 volts.  If I understand how this should work, I should see a 0xAA pulse out from MISO and when I send a 2nd byte from the master, I should see an 0x55 output. 

This makes me think there's a drive contention, so I used a test program that clones MOSI onto MISO (without the USI) and that works fine.  Clock source is a 12mhz oscillator.  So what silly thing am I missing here?

 

#include <io.h>
//#include <interrupt.h>
#include <stdlib.h>
#include <stdio.h>
#include <delay.h>
#include <iobits.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>

#define LED PORTC.0

unsigned char result;

void main()
{

    // Main Clock source: External Clock
    // Output system clock on CLKO pin: Off
    #pragma optsize-
    while ((CLKSR & (1<<OSCRDY))==0);
    CCP=0xd8;
    CLKSR=(1<<OSCRDY) | (1<<CSTR) | (1<<CKOUT_IO);
    CCP=0xd8;       // Clock Prescaler division factor: 1
    CLKPR=(0<<CLKPS3) | (0<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0);
    #ifdef _OPTIMIZE_SIZE_
    #pragma optsize+
    #endif

    // Configure port directions.

	DDRB=(0<<DDB5) | (0<<DDB4) | (0<<DDB3) | (1<<DDB2) | (0<<DDB1) | (1<<DDB0);
	DDRC=(0<<DDC5) | (0<<DDC4) | (0<<DDC3) | (0<<DDC2) | (0<<DDC1) | (1<<DDC0);

    USICR = (1<<USIWM0) | (1<<USICS1);      // Configure USI to 3-wire slave mode

    USIDR = 0xAA;                   //load data register
    USISR |= (1<<USIOIF);           //clear flag
    LED = 0;                        //turn on the LED

    do {
        while (!(USISR & (1<<USIOIF)));
        result = USIBR;           //save the data
        USIDR = 0x55;
        USISR |= (1<<USIOIF);
        while (!(USISR & (1<<USIOIF)));
        LED = 1;                  //turn off the LED
        USIDR = 0xAA;
        USISR |= (1<<USIOIF);
    } while(1);
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did you look at AVR319: Using the USI module for SPI communication? It comes with C code for SPI Master and Slave (IAR but should be usable with Studio)
 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, I started with that. Since I use CodeCision I've also used its code wizard, but it only creates init code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is the code in the datasheet (ASM only) for comparison.

 

init:
ldi  r16,(1<<USIWM0)|(1<<USICS1)
out  USICR,r16
...
SlaveSPITransfer:
out  USIDR,r16
ldi  r16,(1<<USIOIF)
out  USISR,r16
SlaveSPITransfer_loop:
in   r16, USISR
sbrs r16, USIOIF
rjmp SlaveSPITransfer_loop
in   r16,USIDR
ret

and the comment s

 

The code is size optimized using only eight instructions (plus return). The code example assumes that the DO and
USCK pins have been enabled as outputs in the port data direction register. The value stored in register r16 prior to
the function is called is transferred to the master device, and when the transfer is completed the data received from
the master is stored back into the register r16.

maybe you can compare with the ASM from CV or just use it with CV.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's pretty much what I did, but checking against the generated asm is a good idea. I'll try that tomorrow. Thanks!

Last Edited: Tue. Aug 15, 2017 - 06:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well the generated code looks like the asm code.  Now I have noticed a problem with the datasheet for the ATtiny1634.  On page 132 there's a chart that shows the pins used for SPI.

 

 

So I'm using PB1, PB2, and PC1 for 3-wire mode.  Now later on on sheet 141, table 15-2 describes the USIWMx pits in USICR, but all the references are to PORTA, not PORTB????

 

 

Datasheet error?  Or do I need to be using pins on PORTA????

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Looks like a copy/paste error!

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There's some kind of error.  I change it up so the 1634 is an SPI master, endless loop.  I should see the clock line pulse and a data stream on DO, but nothing.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

According to the pinout you have done everything correct ( I'm using PB1, PB2, and PC1 for 3-wire mode).

 

May be try a pull up resistor on the wonky pin just in case something went wrong inside the chip? Or another chip? Unfortunately I have never used that chip myself even though I have recommended it to others and they have used it in a couple of products.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The USI pins seem to be all over the place on different chips, which seems strange.

 

USI pins on tiny261/461/861
 

PA0 (ADC0/DI/SDA/PCINT0
PA1 (ADC1/DO/PCINT1)
PA2 (ADC2/INT1/USCK/SCL/PCINT2)
 

 

USI pins on tiny2313A/4313

 

PB5 (MOSI/DI/SDA/PCINT5)
PB6 (MISO/DO/PCINT6)

PB7 (USCK/SCL/SCK/PCINT7)
 

USI pins on tiny24/44/84

 

(PCINT6/OC1A/SDA/MOSI/ADC6) PA6

PA5 (ADC5/DO/MISO/OC1B/PCINT5)

PA4 (ADC4/USCK/SCL/T1/PCINT4)

 

But NOTE!! USCK and DI  (Input/Open Drain)???
 

 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes I saw the open drain comment, but that's only for the input pins, DO should be able to drive without help.  I just made a bit bang version using the same I/O pins with software only (USI disabled), and it works fine.

 

Even if the DO pin requires a pullup, the internal logic should still work.  The counter should rollover from 15 to 0 and set the overflow flag.  My code sets an LED once this happens, but it never has never on that LED.  It's as though the clock is not getting to the counter, either do to an error in the setup, or the wrong pin is being used for the external clock.  Grrrrr!!!!

 

I don't think I can spend more time on this, so I'll either make a software version of the SPI or use UART1 and make a software UART on the master.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

DO should be able to drive without help.

Just reading a bit more on this and the  USITC: Toggle Clock Port Pin needs to be written to in order to toggle the clock pin.

 

• Bit 0 – USITC: Toggle Clock Port Pin
Writing a one to this bit location toggles the USCK/SCL value either from 0 to 1, or from
1 to 0. The toggling is independent of the setting in the Data Direction Register, but if the
PORT value is to be shown on the pin the DDRE4 must be set as output (to one). This
feature allows easy clock generation when implementing master devices. The bit will be
read as zero.
When an external clock source is selected (USICS1 = 1) and the USICLK bit is set to
one, writing to the USITC strobe bit will directly clock the 4-bit counter. This allows an
early detection of when the transfer is done when operating as a master device.
 

Also AVR319 has this line

/*! \brief  Timer/Counter 0 Compare Match Interrupt handler.
 *
 *  This interrupt handler is only enabled when transferring data
 *  in master mode. It toggles the USI clock pin, i.e. two interrupts
 *  results in one clock period on the clock pin and for the USI counter.
 */
#pragma vector=TIMER0_COMP_vect
__interrupt void timer0comp_handler()
{
	USICR |= (1<<USITC);	// Toggle clock output pin.
}

so it seems that once mastered the USI can be useful otherwise it's a pain!

 

Anyway it's time for breakfast now. wink

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Last Edited: Tue. Aug 15, 2017 - 10:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Both of those sections apply to master mode.  Since I'm suppose to be a slave on the tiny1634 it never drives the clock.

 

Breakfast?  No it's time to go home from work! laugh

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok I might have found something, but won't be able to verify till I get to work where the hardware is.  Apparently this statement won't clear the flag.

 

USISR |= (1<<USIOIF);           //clear flag

Instead a single out command must be used

USISR = (1<<USIOIF);           //clear flag

What I don't know is what happens if the overflow flag isn't cleared, will that prevent the counter from counting?  If so then that may be my problem.

 

Here's a reference I used.

 

http://www.atmel.com/webdoc/AVRL...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well just to close out this thread the change to reset the overflow flag didn't help the problem.  I can't waste anymore time on this so I'll just change the communication method used between the 2 processors.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The USI works fine as a 3-wire Slave. In fact it works pretty well for all modes.
.
Your example does not seem to use a /CS. And seems to just return an alternate 0x55 or 0xAA.
.
If you post a realistic spec, I will write the Slave for you. And on a tiny1634.
.
David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The intent here is to verify the concept will work, so I can commit to a board layout.  At a later date the code for the 1634 will be developed which involves a 750K RS485 communication to other products, using UART0.  The SPI path back to the master is all I'm trying to verify for now.  Since there's only one slave and 1 master, and the 2 processors are 2" apart, CS may not be needed, so I wasn't worried about it at this time.

 

So all I want to see for now is the master issue a few bytes, and readback a response from the slave.  Currently there is no data returning from the slave, and no indication that the USI counter is counting (light an LED if something changes).  I have an ICE debugger on the master, but no debugger on the 1634 currently.

 

Master is a Mega1281 using it's SPI port, and everything on that side is working as expected.

 

Interconnect as follows:

 

SCK  is pin 11 on the 1281 and pin 16 on the 1634

MOSI is pin 12 on the 1281 and pin 20 on the 1634

MISO is pin 13 on the 1281 and pin 19 on the 1634

 

All I'm looking for at this time would be a simple example that shows the hardware will work.  Please don't spend a lot of time on this, since I'm ready to switch to using UARTS if needed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your code "looked" ok to me.   My only comment is to use = rather than |= when clearing USIOIF

 

So I ran it on a tiny1634.  Sure enough,  nothing happened.   Not even the LED lit.

 

I was using the 8MHz RC.   You are using an External Clock.

 

The moral of the story is:   Read the CodeWizard generated comment lines.    They are there for humans to read.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

WOW it's a complicated thing, my guess is that there is no clock in the original slave program? Pity I'm so busy to play with this.

 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When god created mr Arduino the USI is left behind.
.
MG

I don't know why I'm still doing this hobby

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:

Your code "looked" ok to me.   My only comment is to use = rather than |= when clearing USIOIF

 

So I ran it on a tiny1634.  Sure enough,  nothing happened.   Not even the LED lit.

 

I was using the 8MHz RC.   You are using an External Clock.

 

The moral of the story is:   Read the CodeWizard generated comment lines.    They are there for humans to read.

 

David.

I would assume removing the clock startup code would allow it run from the internal RC. Thanks for trying.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes,  the tiny1634 starts up happily with its 8MHz RC.   If you omit the clock sequence,  the Tiny will run by itself.    (I always remove CLKDIV8 fuse)

 

There is nothing much different for the USI as the regular SPI

 

        while (!(USISR & (1<<USIOIF)));
        result = USIBR;         //save the data
        USIDR = 0x55;
        USISR = (1<<USIOIF);

For SPI it would be:

        while (!(SPSR & (1<<SPIF)));
        result = SPDR;         //save the data, clears the SPIF
        SPDR = 0x55;           //ready for the next transaction

So yes,  you have to clear the USI interrupt manually.   This is only a single cycle OUT instruction.

And you need to handle the /CS manually with USI.   But this is nothing more than resetting the USI status and counter.   e.g. USISR = (1 << USIOIF)

 

With any Slave,  you must service it promptly.   i.e. place the next "reply" into USIDR or SPDR ready for the next transaction

 

David.

Last Edited: Thu. Aug 17, 2017 - 08:57 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried one more thing which was a pullup on the output pin DO (PB2).  Now that pin sits high all the time instead of low, acts like it's an input.  I'm beginning to think that the USI is mapped somewhere other then what the datasheet shows.

Last Edited: Thu. Aug 17, 2017 - 05:06 PM