Offline
Sr. Member
Karma: 6
Posts: 414
|
 |
« on: March 05, 2013, 03:02:46 pm » |
I'm working on a little project with an attiny85 where it will need to talk to a couple of shift registers. With a 328 I'll usually just use SPI mode 0 and pretend that the shift register is an SPI device and it always works great.
I know the USI on the attiny can do SPI mode 0 but before I start coding it up I thought I would check and see if there isn't already a library for that. Anyone got one? If not I'll definitely share what I come up with.
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14110
Lua rocks!
|
 |
« Reply #1 on: March 05, 2013, 04:29:43 pm » |
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Sr. Member
Karma: 6
Posts: 414
|
 |
« Reply #2 on: March 05, 2013, 04:56:03 pm » |
Yeah I saw those two. The first one is i2c which seems to be a little more straightforward on the tiny.
The second one I had a hard time following. It seemed to deal more with making it work on that propeller board. But no real library I could use.
I'm going to try to write something that looks like the arduino SPI library. If I don't just say screw it and bang it out in my code. I reckon writing it now will save me in the future. If I get something that works I'll post it back here.
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14110
Lua rocks!
|
 |
« Reply #3 on: March 05, 2013, 05:18:46 pm » |
Yeah I saw those two. The first one is i2c which seems to be a little more straightforward on the tiny.
Oops, sorry. I followed a Google search of SPI though.  The SPI library is pretty small anyway, so adapting it for the Tiny shouldn't be too bad.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Full Member
Karma: 2
Posts: 115
Arduino rocks
|
 |
« Reply #4 on: March 05, 2013, 05:19:59 pm » |
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14110
Lua rocks!
|
 |
« Reply #5 on: March 05, 2013, 05:25:20 pm » |
|
|
|
|
|
Logged
|
|
|
|
|
Leeds, UK
Offline
Edison Member
Karma: 38
Posts: 1029
Once the magic blue smoke is released, it won't go back in!
|
 |
« Reply #6 on: March 05, 2013, 06:27:27 pm » |
I have written a drop in replacement library for SPI. I have both a USI Master version and a bitbanged software Master version. If you would like I can upload them.
|
|
|
|
|
Logged
|
~Tom~
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14110
Lua rocks!
|
 |
« Reply #7 on: March 05, 2013, 06:47:31 pm » |
I'd like one, because I've just been working on one myself. 
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14110
Lua rocks!
|
 |
« Reply #8 on: March 05, 2013, 08:04:02 pm » |
This is what I just developed: // Written by Nick Gammon // March 2013
// ATMEL ATTINY45 / ARDUINO pin mappings // // +-\/-+ // RESET Ain0 (D 5) PB5 1| |8 Vcc // CLK1 Ain3 (D 3) PB3 2| |7 PB2 (D 2) Ain1 SCK / USCK / SCL // CLK0 Ain2 (D 4) PB4 3| |6 PB1 (D 1) pwm1 MISO / DO // GND 4| |5 PB0 (D 0) pwm0 MOSI / DI / SDA // +----+
namespace tinySPI {
const byte DI = 0; // D0, pin 5 Data In const byte DO = 1; // D1, pin 6 Data Out (this is *not* MOSI) const byte USCK = 2; // D2, pin 7 Universal Serial Interface clock const byte SS = 3; // D3, pin 2 Slave Select void begin () { digitalWrite (SS, HIGH); // ensure SS stays high until needed pinMode (USCK, OUTPUT); pinMode (DO, OUTPUT); pinMode (SS, OUTPUT); USICR = _BV (USIWM0); // 3-wire mode } // end of tinySPI_begin // What is happening here is that the loop executes 16 times. // This is because the 4-bit counter in USISR is initially zero, and then // toggles 16 times until it overflows, thus counting out 8 bits (16 toggles). // The data is valid on the clock leading edge (equivalent to CPHA == 0). byte transfer (const byte b) { USIDR = b; // byte to output USISR = _BV (USIOIF); // clear Counter Overflow Interrupt Flag, set count to zero do { USICR = _BV (USIWM0) // 3-wire mode | _BV (USICS1) | _BV (USICLK) // Software clock strobe | _BV (USITC); // Toggle Clock Port Pin } while ((USISR & _BV (USIOIF)) == 0); // until Counter Overflow Interrupt Flag set return USIDR; // return read data } // end of tinySPI_transfer
}; // end of namespace tinySPI
What this does is provide the functionality of SPI.begin and SPI.transfer for the Attiny45/85. Example code using the above: void setup (void) { tinySPI::begin (); } // end of setup
void loop (void) { char c; // enable Slave Select digitalWrite (tinySPI::SS, LOW); // send test string for (const char * p = "Hello, world!" ; c = *p; p++) tinySPI::transfer (c);
// disable Slave Select digitalWrite (tinySPI::SS, HIGH);
delay (100); } // end of loop
Logic analyzer output:  As you can see, it outputs a byte in about 8.9 uS, with a SPI clock speed of around 1 MHz. This doesn't handle incoming interrupts or slave mode, however it should be a good start for talking to shift registers. 
|
|
|
|
« Last Edit: March 05, 2013, 08:37:32 pm by Nick Gammon »
|
Logged
|
|
|
|
|
Offline
Sr. Member
Karma: 6
Posts: 414
|
 |
« Reply #9 on: March 05, 2013, 08:29:40 pm » |
Thanks Nick. That is exactly what I was about to do.
On a side note, is there a particular reason you did it as a namespace and not as a class? Namespace is something I've never really understood how to use and your reasons for doing that may clue me in.
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14110
Lua rocks!
|
 |
« Reply #10 on: March 05, 2013, 08:35:53 pm » |
If you make a class, then you have to have an instance of the class. Whilst this is reasonable for (say) Serial where you might have multiple serial ports, for SPI on an 8-pin chip that is overkill. The namespace is just a way of having the internal names (eg. begin, transfer, DI, DO) "protected" by being in their own namespace, and avoids clumsy naming like: tinySPI_begin, tinySPI_transfer
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14110
Lua rocks!
|
 |
« Reply #11 on: March 05, 2013, 08:39:23 pm » |
You can "use" the namespace and thus avoid qualifying everything in it, like this: using namespace tinySPI;
void setup (void) { begin (); } // end of setup
void loop (void) { char c; // enable Slave Select digitalWrite(SS, LOW); // send test string for (const char * p = "Hello, world!" ; c = *p; p++) transfer (c);
// disable Slave Select digitalWrite(SS, HIGH);
delay (100); } // end of loop
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Sr. Member
Karma: 6
Posts: 414
|
 |
« Reply #12 on: March 05, 2013, 08:44:26 pm » |
do { USICR = _BV (USIWM0) // 3-wire mode | _BV (USICS1) | _BV (USICLK) // Software clock strobe | _BV (USITC); // Toggle Clock Port Pin } while ((USISR & _BV (USIOIF)) == 0); // until Counter Overflow Interrupt Flag set
This is where I would have messed up. I would have fixed it when I saw it on the scope, but I would have done this wrong the first time. From the second example on the datasheet, the one that just toggles r16 and r17, I had the impression that for every write to USICLK I would need a second write to USITC to toggle the pin back and complete one cycle. From the Datasheet: SPITransfer_Fast: out USIDR,r16 ldi r16,(1<<USIWM0)|(0<<USICS0)|(1<<USITC) ldi r17,(1<<USIWM0)|(0<<USICS0)|(1<<USITC)|(1<<USICLK) out USICR,r16 ; MSB out USICR,r17 out USICR,r16 out USICR,r17 out USICR,r16 out USICR,r17 out USICR,r16 out USICR,r17 out USICR,r16 out USICR,r17 out USICR,r16 out USICR,r17 out USICR,r16 out USICR,r17 out USICR,r16 ; LSB out USICR,r17 in r16,USIDR ret
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14110
Lua rocks!
|
 |
« Reply #13 on: March 05, 2013, 08:59:41 pm » |
I followed their first example, and am not sure why they did that except for timing reasons.
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14110
Lua rocks!
|
 |
« Reply #14 on: March 05, 2013, 09:00:22 pm » |
Put it like this, the second writes aren't doing much except buying time, as far as I can see. Whereas the looping example buys time by the time taken to test for the end condition.
|
|
|
|
|
Logged
|
|
|
|
|
|