配線図
Nucleo(Slave)のCSピンはArduinoのようにD10にすると実行時にSerial経由でエラーが表示される。Nucleo F401REのピンアウトを見ると、A2がSPI1_NSSとなっているので、こちらに設定したら動作した。NSSのNはたぶんActive Lowの意味でSS(Slave Select)に前置しているんだと思う。
<追記:2016.10.01>
シリアル経由のエラーメッセージ
</追記>
SPIの信号線だけではSlaveからアクションを起こせないので、SPIとは別に外部割込みを配線(Nucleo:PC_7→Arduino:D2)し、Nucleo(Slave)からArduino(Master)に割込みをかけてMasterからSPI通信を開始するようにした。
また、秋月のNucleo F401REの商品説明によると「GPIO数:50(5Vトレラント、一部を除く)」となっているので5V→3.3Vのレベルシフトはしないで直結した。除かれる「一部」がどこのピンか調べてないがAruduinoヘッダは5Vトレラントだろうと憶測(^q^;
<追記:2016.09.29>
STM32 F401のDATASHEET(DM00102166.pdf 「STM32F401xD STM32F401xE」)の「4 Pinouts and pin description」の「Table 8. STM32F401xD/xE pin definitions」にどのピンが5Vトレラントなのか載っていた。「Pin type」がFTとなっているピンが5Vトレラントで、LQFP64パッケージのI/Oピンはすべて5Vトレラントのようです。
VREFとBOOT0(何をするピンかはよく調べてない)は5Vトレラントではないので要注意です。
</追記>
動作概要
1 タクトスイッチの読み取り(Master→Slave送信)
1.1 Arduino(SPI Master)でタクトスイッチ×6の押し下げ状態を取得。
1.2 タクトスイッチの押し下げ状態に変化があった場合に、SPIで読み取り値をNucleo(SPI Slave)に送信。Nucleoからの返値は無視。
1.3 NucleoはSPI受信した読み取り値をLED×6に表示
2. 一定間隔でカウンタをインクリメント(Slave→Master送信)
2.1 NucleoはmbedのRtosTimerで一定間隔でカウンタをインクリメントし、SPI通信の返値にセット。カウンタをインクリメントした時に一度だけArduinoに外部割込みをかける。
2.2 Arduinoは外部割込みがかかったときNucleoにSPI送信(Master→Slave)し、返値にセットされているカウンタ値をLEDx4に表示。
Arduino(Master)のスケッチ
SPI_Master_for_Nucleo_SPI_Slave.ino#include <SPI.h> const int SpiCsPin = 10; const int SpiSpeed = 10000000; const int InterruptPin = 2; volatile bool isStepChanged = false; uint8_t prevSendVal = 0x00; void setup() { // PD4 - PD7: Output DDRD = 0xF0; // LED Check for (int i = 0; i < 5; i++) { PORTD = 0xF0; delay(100); PORTD = 0x00; delay(100); } // PORTC: Input Pullup DDRC = 0x00; PORTC = 0xFF; // Setup Interrupt Pin pinMode(InterruptPin , INPUT); attachInterrupt(digitalPinToInterrupt(InterruptPin), setStepChange, RISING); // Setup SPI pinMode(SpiCsPin, OUTPUT); digitalWrite(SpiCsPin, HIGH); SPI.begin(); SPI.beginTransaction(SPISettings(SpiSpeed, MSBFIRST, SPI_MODE0)); } void loop() { uint8_t sendVal = ~PINC & 0x3f; if (prevSendVal != sendVal || isStepChanged) { digitalWrite(SpiCsPin, LOW); uint8_t recievedVal = SPI.transfer(sendVal); digitalWrite(SpiCsPin, HIGH); prevSendVal = sendVal; if (isStepChanged) { PORTD = recievedVal << 4; isStepChanged = false; } } } void setStepChange() { isStepChanged = true; }
Nucleo(Slave)のプログラム(mbed)
https://developer.mbed.org/users/ryood/code/Nucleo_rtos_SPISlave_Test/mbedのLibraryは個人的に動作実績のあるRevisionを使うことにした。
mbed: Revision 121
mbed-rtos Revision 117
main.cpp
#include "mbed.h" #include "rtos.h" #include "SPISlave.h" #define SPI_SPEED (10000000) BusOut Leds(PA_10, PB_3, PB_5, PB_4, PB_10, PA_8); DigitalOut StepChangePin(PC_7); SPISlave SpiS(PA_7, PA_6, PA_5, PA_4); // mosi, miso, sclk, ssel unsigned int step = 0; void stepUp(void const* arg) { step++; // Masterにinterruptをかける。 StepChangePin.write(1); StepChangePin.write(0); } int main() { printf("\r\n\nNucleo rtos SPISlave Test..\r\n"); // Setup LED for (int i = 0; i < 5; i++) { Leds.write(0x3f); Thread::wait(100); Leds.write(0x00); Thread::wait(100); } // Setup SPISlave SpiS.format(8, 0); SpiS.frequency(SPI_SPEED); // RtosTimer RtosTimer stepTimer(stepUp, osTimerPeriodic, (void *)0); stepTimer.start(250); // BPM:60 SpiS.reply(0); while(1) { if(SpiS.receive()) { int v = SpiS.read(); // Read byte from master Leds.write(v); SpiS.reply(step % 16); } } }
SPI通信のようす。
MOSI(タクトスイッチの押し下げ状態)
ch1:MOSI ch2:SCK
MISO(カウンタ)
ch1:MISO ch2:SCK
CS
ch1:CS ch2:SCK
Interrupt
ch1:Interrupt ch2:SCK
Arduinoへの外部割込み信号とSPIのSCK信号の間隔は最大5マス程度で変動する。
MISO/MOSI
ch1:MOSI ch2:MISO
SPIのMOSIとMISOは同時にデータを送り合っている。
0 件のコメント:
コメントを投稿