답변:
직렬 주변 인터페이스 버스 (SPI) 인터페이스는 단거리 여러 장치 사이의 통신에 사용하고 고속으로된다.
일반적으로 통신을 시작하고 데이터 전송 속도를 제어하는 클럭을 공급하는 단일 "마스터"장치가 있습니다. 하나 이상의 슬레이브가있을 수 있습니다. 하나 이상의 슬레이브의 경우 각 슬레이브에는 나중에 설명 할 자체 "슬레이브 선택"신호가 있습니다.
본격적인 SPI 시스템에는 4 개의 신호선이 있습니다.
여러 슬레이브가 MISO 신호에 연결되면 슬레이브 선택에 의해 선택 될 때까지 MISO 라인을 3 상태 (높은 임피던스로 유지)해야합니다. 일반적으로 Slave Select (SS)는 낮게 설정합니다. 즉, 활성이 낮습니다. 특정 슬레이브가 선택되면 MISO 라인을 출력으로 구성하여 마스터로 데이터를 보낼 수 있습니다.
이 이미지는 1 바이트가 전송 될 때 데이터가 교환되는 방식을 보여줍니다.
마스터 (MOSI, SCK, SS)에서 3 개의 신호가 출력되고 하나는 입력 (MISO)입니다.
이벤트 순서는 다음과 같습니다.
SS
그것을 주장하고 노예를 활성화하기 위해 낮아집니다.SCK
선은 데이터 선을 샘플링 할시기를 나타 내기 위해 토글SCK
(기본 클록 위상 사용).SCK
MISO
MOSI
SS
을 해제하여 해제합니다.참고 :
동일한 클럭 펄스에서 데이터가 송수신되므로 슬레이브가 마스터에 즉시 응답 할 수 없습니다. SPI 프로토콜은 일반적으로 마스터가 한 번의 전송으로 데이터를 요청하고 후속 전송에서 응답을 얻습니다.
Arduino에서 SPI 라이브러리를 사용하여 단일 전송을 수행하는 것은 코드에서 다음과 같습니다.
byte outgoing = 0xAB;
byte incoming = SPI.transfer (outgoing);
송신 전용의 예 (수신 데이터 무시) :
#include <SPI.h>
void setup (void)
{
digitalWrite(SS, HIGH); // ensure SS stays high
SPI.begin ();
} // end of setup
void loop (void)
{
byte c;
// enable Slave Select
digitalWrite(SS, LOW); // SS is pin 10
// send test string
for (const char * p = "Fab" ; c = *p; p++)
SPI.transfer (c);
// disable Slave Select
digitalWrite(SS, HIGH);
delay (100);
} // end of loop
위 코드 (전송 만)는 출력 직렬 시프트 레지스터를 구동하는 데 사용될 수 있습니다. 이것들은 출력 전용 장치이므로 들어오는 데이터에 대해 걱정할 필요가 없습니다. 이 경우 SS 핀을 "스토어"또는 "래치"핀이라고합니다.
이것의 예로는 74HC595 직렬 시프트 레지스터와 다양한 LED 스트립이 있습니다. 예를 들어,이 64 픽셀 LED 디스플레이는 MAX7219 칩으로 구동됩니다.
이 경우 보드 제조업체가 약간 다른 신호 이름을 사용했음을 알 수 있습니다.
대부분의 보드는 비슷한 패턴을 따릅니다. 때때로 DIN은 DI (Data In) 일뿐입니다.
다음은 또 다른 예입니다. 이번에는 7- 세그먼트 LED 디스플레이 보드 (MAX7219 칩 기반) :
이것은 다른 보드와 정확히 동일한 신호 이름을 사용합니다. 이 두 경우 모두 보드에 5 개의 전선, SPI 용 3 개, 전력 및 접지 만 있으면됩니다.
SPI 클럭을 샘플링 할 수있는 네 가지 방법이 있습니다.
SPI 프로토콜은 클럭 펄스의 극성을 변화시킬 수 있습니다. CPOL은 클럭 극성이고 CPHA는 클럭 위상입니다.
이것들은이 그래픽에 설명되어 있습니다 :
위상과 극성을 올바르게하려면 장치의 데이터 시트를 참조하십시오. 일반적으로 클럭을 샘플링하는 방법을 보여주는 다이어그램이 있습니다. 예를 들어, 74HC595 칩의 데이터 시트에서 :
보시다시피 클럭은 일반적으로 낮고 (CPOL = 0) 클럭은 리딩 엣지 (CPHA = 0)에서 샘플링되므로 SPI 모드 0입니다.
다음과 같은 코드에서 클럭 극성과 위상을 변경할 수 있습니다 (물론 하나만 선택하십시오).
SPI.setDataMode (SPI_MODE0);
SPI.setDataMode (SPI_MODE1);
SPI.setDataMode (SPI_MODE2);
SPI.setDataMode (SPI_MODE3);
이 방법은 Arduino IDE 버전 1.6.0 이상에서 더 이상 사용되지 않습니다. 최근 버전의 경우 SPI.beginTransaction
통화 에서 시계 모드를 다음 과 같이 변경합니다.
SPI.beginTransaction (SPISettings (2000000, MSBFIRST, SPI_MODE0)); // 2 MHz clock, MSB first, mode 0
기본값은 가장 중요한 비트 우선이지만 하드웨어에 가장 중요하지 않은 비트를 먼저 처리하도록 지시 할 수 있습니다.
SPI.setBitOrder (LSBFIRST); // least significant bit first
SPI.setBitOrder (MSBFIRST); // most significant bit first
다시 말하지만 이것은 Arduino IDE 버전 1.6.0 이상에서 더 이상 사용되지 않습니다. 최근 버전의 경우 SPI.beginTransaction
호출 에서 비트 순서를 다음 과 같이 변경합니다.
SPI.beginTransaction (SPISettings (1000000, LSBFIRST, SPI_MODE2)); // 1 MHz clock, LSB first, mode 2
SPI의 기본 설정은 16MHz CPU 클록을 가정하여 시스템 클록 속도를 4로 나눈 값 즉, 250ns마다 1 개의 SPI 클록 펄스를 사용하는 것입니다. 다음과 같이 클럭 디바이더를 변경할 수 있습니다 setClockDivider
.
SPI.setClockDivider (divider);
"분배기"는 다음 중 하나입니다.
16MHz CPU 클록을 가정하면 가장 빠른 속도는 125ns마다 "2로 나누기"또는 1 개의 SPI 클록 펄스입니다. 따라서 1 바이트를 전송하려면 8 * 125ns 또는 1µs가 필요합니다.
이 방법은 Arduino IDE 버전 1.6.0 이상에서 더 이상 사용되지 않습니다. 최신 버전에서는 다음 SPI.beginTransaction
과 같이 통화 의 전송 속도를 변경합니다 .
SPI.beginTransaction (SPISettings (4000000, MSBFIRST, SPI_MODE0)); // 4 MHz clock, MSB first, mode 0
그러나 경험적 테스트에 따르면 바이트 사이에 2 개의 클럭 펄스가 필요하므로 바이트를 클럭 아웃 할 수있는 최대 속도는 각각 1.125 µs입니다 (클럭 디바이더 2).
요약하면, 각 바이트는 1.125 µs 당 최대 1 개의 속도 (16 MHz 클록 사용)로 전송되어 이론상 최대 전송 속도는 1 / 1.125 µs 또는 초당 888,888 바이트입니다 (SS를 낮게 설정하는 등의 오버 헤드 제외) 의 위에).
디지털 핀 10 ~ 13을 통해 연결
ICSP 헤더를 통한 연결 :
디지털 핀을 통해 연결 50 ~ 52 :
위의 Uno와 유사한 ICSP 헤더를 사용할 수도 있습니다.
Leonardo 및 Micro는 Uno 및 Mega와 달리 디지털 핀에 SPI 핀을 노출시키지 않습니다. 유일한 옵션은 Uno에 대해 위에서 설명한 것처럼 ICSP 헤더 핀을 사용하는 것입니다.
마스터는 여러 슬레이브와 통신 할 수 있습니다 (단, 한 번에 하나만). 하나의 슬레이브에 SS를 선언하고 다른 모든 슬레이브에 대해 SS를 비활성화하여이를 수행합니다. SS가 주장한 슬레이브 (일반적으로 LOW를 의미 함)는 슬레이브 및 해당 슬레이브 만 마스터에 응답 할 수 있도록 MISO 핀을 출력으로 구성합니다. SS가 주장되지 않으면 다른 슬레이브는 들어오는 클록 펄스를 무시합니다. 따라서 다음과 같이 각 슬레이브에 대해 하나의 추가 신호가 필요합니다.
이 그림에서 MISO, MOSI, SCK는 두 슬레이브간에 공유되지만 각 슬레이브에는 자체 SS (슬레이브 선택) 신호가 있습니다.
SPI 사양은 프로토콜을 지정하지 않으므로 데이터의 의미에 동의하는 것은 개별 마스터 / 슬레이브 쌍에 달려 있습니다. 바이트를 동시에 보내고받을 수 있지만, 수신 된 바이트는 전송 된 바이트에 대한 직접적인 응답이 될 수 없습니다 (동시에 조립되므로).
따라서 한쪽 끝이 요청을 보내는 것이 더 논리적 일 것입니다 (예 : 4는 "디스크 디렉터리 나열"을 의미 할 수 있음). 그러면 완전한 응답을받을 때까지 전송 (아마도 0을 바깥쪽으로 보내는 것)을 수행합니다. 응답은 줄 바꿈 또는 0x00 문자로 끝날 수 있습니다.
슬레이브 장치의 데이터 시트를 읽고 예상되는 프로토콜 순서를 확인하십시오.
앞의 예는 마스터로서 Arduino를 보여 주며 슬레이브 장치에 데이터를 전송합니다. 이 예는 Arduino가 어떻게 슬레이브가 될 수 있는지 보여줍니다.
서로 연결된 다음 핀과 함께 두 개의 Arduino Unos를 연결하십시오.
13 (SCK)
+ 5v (필요한 경우)
Arduino Mega에서 핀은 50 (MISO), 51 (MOSI), 52 (SCK) 및 53 (SS)입니다.
어떤 경우에는, 한쪽 끝에서 MOSI는 다른, MOSI에 연결되어, 당신이 하지 주위를 교환이다 (당신이 없는 MOSI을 <-> MISO). 소프트웨어는 MOSI의 한쪽 끝 (마스터 끝)을 출력으로 구성하고 다른 쪽 끝 (슬레이브 끝)을 입력으로 구성합니다.
#include <SPI.h>
void setup (void)
{
digitalWrite(SS, HIGH); // ensure SS stays high for now
// Put SCK, MOSI, SS pins into output mode
// also put SCK, MOSI into LOW state, and SS into HIGH state.
// Then put SPI hardware into Master mode and turn SPI on
SPI.begin ();
// Slow down the master a bit
SPI.setClockDivider(SPI_CLOCK_DIV8);
} // end of setup
void loop (void)
{
char c;
// enable Slave Select
digitalWrite(SS, LOW); // SS is pin 10
// send test string
for (const char * p = "Hello, world!\n" ; c = *p; p++)
SPI.transfer (c);
// disable Slave Select
digitalWrite(SS, HIGH);
delay (1000); // 1 seconds delay
} // end of loop
#include <SPI.h>
char buf [100];
volatile byte pos;
volatile bool process_it;
void setup (void)
{
Serial.begin (115200); // debugging
// turn on SPI in slave mode
SPCR |= bit (SPE);
// have to send on master in, *slave out*
pinMode (MISO, OUTPUT);
// get ready for an interrupt
pos = 0; // buffer empty
process_it = false;
// now turn on interrupts
SPI.attachInterrupt();
} // end of setup
// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR; // grab byte from SPI Data Register
// add to buffer if room
if (pos < sizeof buf)
{
buf [pos++] = c;
// example: newline means time to process buffer
if (c == '\n')
process_it = true;
} // end of room available
} // end of interrupt routine SPI_STC_vect
// main loop - wait for flag set in interrupt routine
void loop (void)
{
if (process_it)
{
buf [pos] = 0;
Serial.println (buf);
pos = 0;
process_it = false;
} // end of flag set
} // end of loop
슬레이브는 완전히 인터럽트로 구동되므로 다른 작업을 수행 할 수 있습니다. 들어오는 SPI 데이터는 버퍼에 수집되며 "유의 한 바이트"(이 경우 줄 바꿈)가 도착하면 플래그가 설정됩니다. 이것은 슬레이브에게 데이터 처리를 시작하고 시작하도록 지시합니다.
위의 코드에서 SPI 마스터에서 슬레이브로 데이터를 전송하는 다음 예제는 슬레이브로 데이터를 전송하여 데이터를 처리하고 응답을 반환하는 것을 보여줍니다.
마스터는 위의 예와 비슷합니다. 그러나 중요한 점은 약간의 지연 (20 마이크로 초)을 추가해야한다는 것입니다. 그렇지 않으면 슬레이브는 들어오는 데이터에 반응 할 수있는 기회가 없습니다.
이 예는 "명령"전송을 보여줍니다. 이 경우 "a"(무언가 추가) 또는 "s"(무언가 빼기). 이것은 슬레이브가 실제로 데이터로 무언가를하고 있음을 보여주기위한 것입니다.
트랜잭션을 시작하기 위해 슬레이브 선택 (SS)을 선언 한 후 마스터는 명령과 임의의 바이트 수를 전송 한 다음 SS를 올려 트랜잭션을 종료합니다.
매우 중요한 점은 슬레이브가 동시에 들어오는 바이트에 응답 할 수 없다는 것입니다. 응답은 다음 바이트에 있어야합니다. 전송중인 비트와 수신중인 비트가 동시에 전송되기 때문입니다. 따라서 4 개의 숫자에 무언가를 추가하려면 다음과 같이 5 개의 전송이 필요합니다.
transferAndWait ('a'); // add command
transferAndWait (10);
a = transferAndWait (17);
b = transferAndWait (33);
c = transferAndWait (42);
d = transferAndWait (0);
먼저 10 번에 조치를 요청합니다. 그러나 다음 번 이체 (17 번)까지 응답을받지 못합니다. 그러나 "a"는 10으로 응답하도록 설정됩니다. 마지막으로 "더미"번호 0을 보내 42의 응답을 얻습니다.
#include <SPI.h>
void setup (void)
{
Serial.begin (115200);
Serial.println ();
digitalWrite(SS, HIGH); // ensure SS stays high for now
SPI.begin ();
// Slow down the master a bit
SPI.setClockDivider(SPI_CLOCK_DIV8);
} // end of setup
byte transferAndWait (const byte what)
{
byte a = SPI.transfer (what);
delayMicroseconds (20);
return a;
} // end of transferAndWait
void loop (void)
{
byte a, b, c, d;
// enable Slave Select
digitalWrite(SS, LOW);
transferAndWait ('a'); // add command
transferAndWait (10);
a = transferAndWait (17);
b = transferAndWait (33);
c = transferAndWait (42);
d = transferAndWait (0);
// disable Slave Select
digitalWrite(SS, HIGH);
Serial.println ("Adding results:");
Serial.println (a, DEC);
Serial.println (b, DEC);
Serial.println (c, DEC);
Serial.println (d, DEC);
// enable Slave Select
digitalWrite(SS, LOW);
transferAndWait ('s'); // subtract command
transferAndWait (10);
a = transferAndWait (17);
b = transferAndWait (33);
c = transferAndWait (42);
d = transferAndWait (0);
// disable Slave Select
digitalWrite(SS, HIGH);
Serial.println ("Subtracting results:");
Serial.println (a, DEC);
Serial.println (b, DEC);
Serial.println (c, DEC);
Serial.println (d, DEC);
delay (1000); // 1 second delay
} // end of loop
슬레이브 코드는 기본적으로 인터럽트 루틴 (수신 SPI 데이터가 도착할 때 호출)의 거의 모든 작업을 수행합니다. 들어오는 바이트를 취하고 기억 된 "명령 바이트"에 따라 더하거나 뺍니다. 다음에 루프를 통해 응답이 "수집"됩니다. 그렇기 때문에 마스터는 최종 응답을 얻기 위해 하나의 최종 "더미"전송을 보내야합니다.
내 예제에서 메인 루프를 사용하여 SS가 언제 높아지는지를 감지하고 저장된 명령을 지우고 있습니다. 이렇게하면 다음 트랜잭션에서 SS를 다시 낮출 때 첫 번째 바이트가 명령 바이트로 간주됩니다.
더 확실하게, 이것은 인터럽트로 수행됩니다. 즉, SS를 인터럽트 입력 중 하나 (예 : Uno에서 핀 10 (SS)를 핀 2 (인터럽트 입력)에 연결하거나 핀 변경 인터럽트를 핀 10에 물리적으로 연결합니다.
그런 다음 SS를 낮게 또는 높게 당기면 인터럽트를 사용하여 알 수 있습니다.
// what to do with incoming data
volatile byte command = 0;
void setup (void)
{
// have to send on master in, *slave out*
pinMode(MISO, OUTPUT);
// turn on SPI in slave mode
SPCR |= _BV(SPE);
// turn on interrupts
SPCR |= _BV(SPIE);
} // end of setup
// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR;
switch (command)
{
// no command? then this is the command
case 0:
command = c;
SPDR = 0;
break;
// add to incoming byte, return result
case 'a':
SPDR = c + 15; // add 15
break;
// subtract from incoming byte, return result
case 's':
SPDR = c - 8; // subtract 8
break;
} // end of switch
} // end of interrupt service routine (ISR) SPI_STC_vect
void loop (void)
{
// if SPI not active, clear current command
if (digitalRead (SS) == HIGH)
command = 0;
} // end of loop
Adding results:
25
32
48
57
Subtracting results:
2
9
25
34
Adding results:
25
32
48
57
Subtracting results:
2
9
25
34
위 코드에서의 송수신 시간을 보여줍니다.
IDE 버전 1.6.0은 SPI 작동 방식을 어느 정도 변경했습니다. 당신은 여전히해야 할 SPI.begin()
SPI를 사용하기 전에. SPI 하드웨어를 설정합니다. 당신은 노예와 통신을 시작하려고 할 때 그러나 지금, 당신은 또한 할 SPI.beginTransaction()
올바른와 (이 슬레이브) SPI를 설정합니다 :
슬레이브와의 통신이 끝나면을 호출 SPI.endTransaction()
합니다. 예를 들면 다음과 같습니다.
SPI.beginTransaction (SPISettings (2000000, MSBFIRST, SPI_MODE0));
digitalWrite (SS, LOW); // assert Slave Select
byte foo = SPI.transfer (42); // do a transfer
digitalWrite (SS, HIGH); // de-assert Slave Select
SPI.endTransaction (); // transaction over
SPI를 언제 / 왜 사용 하시겠습니까? 멀티 마스터 구성이나 많은 수의 슬레이브가 필요하면 스케일이 I2C쪽으로 기울어집니다.
이것은 훌륭한 질문입니다. 내 대답은 다음과 같습니다
두 방법 모두 자리가 있습니다. I 2 C를 사용하면 많은 장치를 단일 버스 (2 개의 와이어와 접지)에 연결할 수 있으므로 많은 수의 장치를 심문해야하는 경우에 선호되는 선택입니다. 그러나 SPI 속도는 빠른 출력 (예 : LED 스트립) 또는 빠른 입력 (예 : ADC 변환기)이 필요한 상황에 더 적합 할 수 있습니다.
SPI에 대한 내 페이지 -비트 뱅킹 SPI에 대한 세부 정보와 USART를 사용하여 Atmega328 칩에서 두 번째 하드웨어 SPI를 얻습니다.
Are you going to cover the weirdness that is the Due's SPI?
-Due의 SPI에 대해서는 아무것도 모릅니다 (전체 프로토콜이 동일하다고 가정하는 것 제외). 당신은 그것의 측면에 대한 답변을 추가 할 수 있습니다.