まず。Arduino言語はC++をベースとしています。さすがに、「なにもわかりません」でプログラムが成立するほど甘くはありません。C++、せめてCの入門書を手元において、出来れば一通り目を通してからプログラムに挑んで下さい。
#根本的に大事なこと(これは知らなきゃ話にならない)
C++そしてArduino言語では(今後はArduino言語に代表させますが)、基本的に「上から順に」プログラムを実行していきます。なので、
byte arr[2] = { i, j};
としたときには、その時点での i,jの'値'がarrの要素に設定されて初期化が行われますが、その後iとかjが変化してもarrに既に設定されている値には何の影響も及ぼしません。
Arduino
1int i,j; //グローバル変数なのでi,jは0に初期化される
2byte arr[2] = { i, j};//このときのi,jは0なので、arrは{0,0}に初期化される
3void setup(){
4 Serial.begin(9600);
5}
6void loop(){
7 i=100;//iが100になろうと
8 Serial.println(arr[0]);//arr[0]は0のまま
9 j=10000; //jが10000になっても
10 Serial.println(arr[1]);//arr[1]も0のまま
11}
とでもしてシリアルモニタで値を確認してみて下さい。
というレベルがあやふやだと、あとの話はどうしようかなぁ...ずぶずぶと深みにハマっていくのですが。
まぁ、お付き合いいただきましょうか。質問があればどうぞ。
実は、そのプログラムでも双方向通信(のようなこと)は出来ています。ただし、タイミング的に無理があって、自在にデータをやり取り出来る状態ではない、のです。
SPI通信は、ざっくり
- 送り側のSPDRに値をセットする
- クロックに同期してデータを送信する
- 送り側のSPDRと受け側のSPDRのデータが交換される(送り側のSPDRの内容が受け側のSPDRに入る/受け側のSPDRに入っていた内容が送り側のSPDRに入る)
ということが行われると思って下さい。void transfer(void *buf, size_t count)関数では、送信後のSPDRの内容を受け取ったbuf領域に書き込んでいます。なので、双方向通信は実は出来ている...のですがしかし。もう少し細かく考えると
- 送り側のSPDRに値をセットする
- クロックに同期してデータを送信する
- 1バイト分のデータが揃ったら...
受信側ではSPDRからデータを取得し、返信のデータをSPDRに設定する
送信側は、次のデータをSPDRに設定する
- 送信側でSPDRにデータが設定されると、次のデータ送信が始まる
送信側の方がちょっと手数が少ないですよね。そうすると、受信側で「返信のデータをSPDRに設定する」作業が間に合わない可能性が出てきます。というか、transfer()で送り側がどんどんデータを送りつけると全く間に合いません。返信のデータが間に合わないということは、受信した時点では受け取ったデータがSPDRに入っていますから、そのデータがそのまま次の返信に使用される(つまり送られたデータをそのまま送り返す)ことになります。実際にはこうなっちゃうわけですね。
- 1バイト分のデータが揃ったら受信側ではSPDRからデータを取得する
- 送信側は、次のデータをSPDRに設定する
- 送信側でSPDRにデータが設定されると、次のデータ送信が始まる。受信側は、前回受け取ったSPDRを持って通信開始する
- 受信側では返信のデータをSPDRに設定する
- 1バイト分のデータが揃ったら受信側ではSPDRからデータを取得する (受信側で設定したSPDRデータは上書きされちゃって無くなってる)
つまり、双方向で任意の複数データをやりとりしたいと思ったら、送信側は1byte分転送する度にいくらかの時間(別にdelayでミリ秒を確保する必要はありませんが)を設けて、受信側が返信データをSPDRに設定する余裕を作らなければいけない、ということです。
"void value not ignored as it ought to be"
について...
C++では、関数名が同じでも引数が違うと別の関数とみなされます。
さて、spi.hを覗いてみると、
inline static uint8_t transfer(uint8_t data) {
とあるので、SPIClass::transfer()に1つの整数値を与えたときは返り値がありますが、
inline static void transfer(void *buf, size_t count) {
は返り値がありませんからuint16_t MasterReceived = SPI.transfer(buffer, size);
はエラーになります。
(void *buf, size_t count)
の形式の引数であれば、bufには配列(ポインタ)を与え、countには配列のサイズを与えてポインタを介して配列の中身を読み書きする、というのが定番です。
とりあえず、以下で私の手元では(Arduino UNO - nano間)で双方向らしい動きは確認できました。
Arduino
1/*マスター*/
2
3#include <SPI.h>
4int i, j;
5byte arr[2] = {};
6
7void setup() {
8 Serial.begin(9600);
9 SPI.setBitOrder( MSBFIRST );
10 SPI.setDataMode( SPI_MODE3 );
11 SPI.begin();
12}
13
14void loop() {
15 digitalWrite( SS, LOW ); //対象の電子部品を接続したSS
16 arr[0] = i;
17 arr[1] = j;
18 Serial.print("MasterSend = ");
19 Serial.print(arr[0]);
20 Serial.print(',');
21 Serial.println(arr[1]);
22 for ( int idx = 0; idx < 2; idx++) {
23 arr[idx]=SPI.transfer(arr[idx]);
24 delayMicroseconds(50);
25 }
26 digitalWrite( SS, HIGH ); //通信終了
27 Serial.print("MasterReceive = ");
28 Serial.print(arr[0]);
29 Serial.print(',');
30 Serial.println(arr[1]);
31 i++;
32 if ( i > 255) i = 0;
33 j--;
34 if ( j < 0 ) j = 255;
35}
Arduino
1/*
2 * SPIスレーブ
3 * SS - Pin10
4 * MOSI - Pin11
5 * MISO - Pin12
6 * SCK - Pin13
7 */
8
9#include <SPI.h>
10
11uint16_t i,j;
12volatile byte Slavereceived[2],Slavesend;
13
14
15void setup() {
16 Serial.begin(9600);
17 pinMode(MISO,OUTPUT); //MISOを出力
18 SPI.setBitOrder(MSBFIRST); //最上位ビット(MSB)から送信
19 SPI.setDataMode(SPI_MODE3); //アイドル5Vで0V→5Vの変化で送信する
20 SPCR |= _BV(SPE);
21 SPI.attachInterrupt();
22}
23
24// SPI割り込み処理
25ISR(SPI_STC_vect)
26{
27 Slavereceived[j] = SPDR;
28 j=(j+1)%2;
29 SPDR = Slavesend;
30 Slavesend = i++;
31}
32
33void loop() {
34//表示は遅いので飛び飛びになることも
35 Serial.print("Slavereceived = ");
36 Serial.print(Slavereceived[0]);
37 Serial.print('/');
38 Serial.print(Slavereceived[1]);
39 Serial.print(", ");
40 Serial.print("Slavesend = ");
41 Serial.println(Slavesend);
42}
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。