Go Down

Topic: Setting up the second SPI port on the Arduino Uno (Read 2 times) previous topic - next topic

Andy Baker

As I understand it, there's a second hardware SPI port that's available for use on the ATMEGA328P, by configuring the regular USART to run in SPI master mode, which should in theory mean it's available on the Arduino Uno. I'll admit, I'm not much of a genius in configuring the registers, etc, but I figure there's gotta be somebody out there that's done this on an Arduino. Thoughts? I've got an LED string that I have running GREAT over the SPI port, but I need to access other devices and there's no provisions for chip select on the LEDs.

I'm getting my onfo on page 206 of http://www.atmel.com/Images/doc8271.pdf

Or do I just give up and set up AND gates and MAKE my LED strip have chip select?

Andy

Nick Gammon

There's a slightly helpful application note here:

www.atmel.com/Images/doc2577.pdf

I'll see if I can make it work, since I can't easily find someone who has done it.

Nick Gammon

#2
Apr 12, 2012, 06:33 am Last Edit: Apr 12, 2012, 07:48 am by Nick Gammon Reason: 1
That wasn't too hard. Example:

Code: [Select]
/*
Example of USART in SPI mode on the Atmega328.

Author:   Nick Gammon
Date:     12th April 2012
Version:   1.0

Licence: Released for public use.

Pins: D0 MISO (Rx)
     D1 MOSI (Tx)
     D4 SCK  (clock)
     D5 SS   (slave select)  <-- this can be changed

Registers of interest:

UDR0 - data register

UCSR0A - USART Control and Status Register A
    Receive Complete, Transmit Complete, USART Data Register Empty

UCSR0B - USART Control and Status Register B
    RX Complete Interrupt Enable, TX Complete Interrupt Enable, Data Register Empty Interrupt Enable ,
    Receiver Enable, Transmitter Enable
 
UCSR0C - USART Control and Status Register C
    Mode Select (async, sync, SPI), Data Order, Clock Phase, Clock Polarity

UBRR0L and UBRR0H - Baud Rate Registers - together are UBRR0 (16 bit)

*/

const byte MSPIM_SCK = 4;
const byte MSPIM_SS = 5;

// sends/receives one byte
byte MSPIMTransfer (byte c)
{
 // wait for transmitter ready
 while ((UCSR0A & _BV (UDRE0)) == 0)
   {}
   
 // send byte
 UDR0 = c;
 
 // wait for receiver ready
 while ((UCSR0A & _BV (RXC0)) == 0)
   {}
   
 // receive byte, return it
 return UDR0;
}  // end of MSPIMTransfer

// select slave, write a string, wait for transfer to complete, deselect slave
void spiWriteString (const char * str)
 {
 if (!str) return;  // Sanity Clause
 
 char c;

 // enable slave select
 digitalWrite (MSPIM_SS, LOW);
 
 // send the string
 while (c = *str++)
   MSPIMTransfer (c);
 
 // wait for all transmissions to finish
 while ((UCSR0A & _BV (TXC0)) == 0)
   {}

 // disable slave select
 digitalWrite (MSPIM_SS, HIGH);
 }  // end of spiWriteString

void setup()
 {
 pinMode (MSPIM_SS, OUTPUT);   // SS
 // must be zero before enabling the transmitter
 UBRR0 = 0;
 UCSR0A = _BV (TXC0);  // any old transmit now complete
 pinMode (MSPIM_SCK, OUTPUT);   // set XCK pin as output to enable master mode
 UCSR0C = _BV (UMSEL00) | _BV (UMSEL01);  // Master SPI mode
 UCSR0B = _BV (TXEN0) | _BV (RXEN0);  // transmit enable and receive enable
 // must be done last, see page 206
 UBRR0 = 3;  // 2 Mhz clock rate
 }  // end of setup

void loop()
 {
 spiWriteString ("hello, world!");
 }  // end of loop


Screenshot in operation:



The stuff in setup configures the USART.

spiWriteString shows the general idea of using it ... you select the slave (if wanted), then repeatedly call MSPIMTransfer to transfer each byte. It waits for the last one to finish (so we can deselect SS in an orderly way).

MSPIMTransfer sends/receives one byte - blocking.

diesel

#3
Feb 06, 2013, 04:50 am Last Edit: Feb 06, 2013, 05:14 am by Nick Gammon Reason: 1
Nick,

Thanks for posting the code to create SPI port on uart pins. I am, however, having a problem with it. I tried setting up a simple test. I am using an Arduino Uno w/no devices attached. Since I was having a problem, I tried this simple test using std SPI library, works as you would expect. I am using FTDI breakout from Sparkfun to read. hello there, hello there....

Code: [Select]
#include <SPI.h>
#include <SoftwareSerial.h>
SoftwareSerial DebugPort = SoftwareSerial(2,3);

char mystring[100]="hello there";
char mystring2[100]="";
void setup() {
 // put your setup code here, to run once:
 DebugPort.begin(9600);
 SPI.begin();
 SPI.setBitOrder(MSBFIRST);
 SPI.setClockDivider(SPI_CLOCK_DIV2);
 SPI.setDataMode(SPI_MODE0);
}

void loop() {
 uint8_t x=0;
 digitalWrite(SS,LOW);
for(x;x<(strlen(mystring));x++){
 mystring2[ x ] = SPI.transfer( mystring [ x ] ) ;
}
digitalWrite(SS,HIGH);
mystring2[x+1]='\0';
DebugPort.println(mystring2);
}


*************
Then I tried a similar setup using the code you posted. On this I get an output of ......,......,...... (yes dots). Do you know what may be the problem?
************



Code: [Select]
#include <SoftwareSerial.h>
SoftwareSerial DebugPort = SoftwareSerial(2,3);
const byte MSPIM_SCK = 4;
const byte MSPIM_SS = 5;
char mystring[100]="hello there";
char mystring2[100]="";
// sends/receives one byte
char MSPIMTransfer (char c)
{
 // wait for transmitter ready
 while ((UCSR0A & _BV (UDRE0)) == 0)
   {}
   
 // send byte
 UDR0 = c;
 
 // wait for receiver ready
 while ((UCSR0A & _BV (RXC0)) == 0)
   {}
   
 // receive byte, return it
 return UDR0;
}  // end of MSPIMTransfer

// select slave, write a string, wait for transfer to complete, deselect slave
void spiWriteString (const char * str)
 {
 if (!str) return;  // Sanity Clause
 
 char c;

 // enable slave select
 digitalWrite (MSPIM_SS, LOW);
// const char * mystr = str;
 uint8_t count =0;
 // send the string
 while (c = *str++){
  mystring[count] =MSPIMTransfer (c);
 }

 // wait for all transmissions to finish
while ((UCSR0A & _BV (TXC0)) == 0)
   {}

 // disable slave select
 digitalWrite (MSPIM_SS, HIGH);
 }  // end of spiWriteString

void setup()
 {
 DebugPort.begin(9600);
 pinMode (MSPIM_SS, OUTPUT);   // SS
 // must be zero before enabling the transmitter
 UBRR0 = 0;
 UCSR0A = _BV (TXC0);  // any old transmit now complete
 pinMode (MSPIM_SCK, OUTPUT);   // set XCK pin as output to enable master mode
 UCSR0C = _BV (UMSEL00) | _BV (UMSEL01);  // Master SPI mode
 UCSR0B = _BV (TXEN0) | _BV (RXEN0);  // transmit enable and receive enable
 // must be done last, see page 206
 UBRR0 =3;// 3;  // 2 Mhz clock rate
 }  // end of setup

void loop()
 {
   
     uint8_t x=0;
 digitalWrite(MSPIM_SS,LOW);

for(x;x<(strlen(mystring));x++){
 mystring2 [ x ] =MSPIMTransfer( mystring  [ x ] );
}
digitalWrite(MSPIM_SS,HIGH);
 mystring2[x+1]='\0';
 DebugPort.println(&mystring2[0]);  
}


Thanks,

-diesel

Moderator edit: [code] ... [/code] tags added. (Nick Gammon)

Nick Gammon

Code: [Select]

  DebugPort.println(&mystring2[0]);   


Why not:

Code: [Select]

  DebugPort.println(mystring2);   


As you had in the working example.

Go Up