気がついたら前回工作してから半年もたってた。
SN3E0090.jpg

ArduinoからATtinyに指示を送ってモーターの制御をする、てなことをやるつもり。で、まずはSPIで指示を送り、これに従って何かする、ことをやってみたい。

 ※以下の内容は単にやってみたことの記録です。すべて無保証で、障害・損害・不利益・不都合に対し、筆者は一切の責任を負いません。

SPI自体についてはこのへんを参照。
なんでSPIを選んだかというと、
I2Cよりも、仕組みが単純、転送速度も速くできるみたいだから。

  • Arduinoをマスター、AVR ATtiny2313Vをスレーブとする。
  • ATtiny2313にはSSがないみたいなので、PB4をLOWにしたらスレーブ選択、という体(てい)で。
  • マスターから送ったデータ8ビットを、スレーブ側でLED点灯で表現(面倒だったのでLEDは4つ。下位4ビットだけ・・・)&前回データを反転したものをマスターに返すことで、通信がうまくいったことを確認する。Arduino側はスレーブから受け取ったデータをSerialに書きだす。
ATtiny2313は、外部クリスタル8Mhzを使用かつCKDIV8を無効にしているので、システムクロックは8Mhz。
電源はUSBからArduinoに給電。ATtinyはArduinoから出ている5Vを使う。
spi_test.PNG


マスター:
最近公開されたarduino 0022ではSPIが標準ライブラリになっているので、マスター側はこれを利用した。

#include <SPI.h>
#include <util/delay.h>

void setup()
{
Serial.begin(9600);

SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV8);
SPI.setDataMode(SPI_MODE0);

pinMode(2, OUTPUT);
digitalWrite(2, HIGH);

uint8_t d = 0;
for (;;)
{
digitalWrite(2, LOW);
uint8_t a = SPI.transfer(d);
digitalWrite(2, HIGH);

Serial.print(d, HEX);
Serial.write(", ");
Serial.println(a, HEX);
d++;
_delay_ms(300);
}
}


void loop()
{
}


スレーブ:

試しながら学ぶAVR入門―マイコンの基礎と電子工作とWindowsアプリケーションの作り方 (SkiLL up mycomputerシリーズ)
スレーブ側は、ATtinyのデータシートに、アセンブリ言語で書かれた実装例があるので、ニーモニックの説明を見つつC言語に書き下してみた。
ニーモニックについてはデータシートに説明がある。

外部割り込みがどういう挙動か知りたかったこともあり、SS相当のPB4で割り込みがかかるようにした。
PB4がLOWになったら受信開始。8ビット受信する。受信にあたりマスターへの送信データは、前回受け取ったデータをビット反転したものとした。特に意味はない。
受け取った8ビットのうち下位4ビットのON/OFFをPB0~PB3につないだLEDに反映する。


#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>


#define USCK PB7
#define MOSI PB5
#define MISO PB6


volatile uint8_t triggered = 0;

ISR(PCINT_vect)
{
if (!bit_is_set(PINB, 4))
{
triggered = 1;
}
}

uint8_t spi_slave_receive(uint8_t send)
{
USIDR = send;
USISR = (1<<USIOIF);
while (!(USISR & (1<<USIOIF)));
uint8_t receive = USIDR;

return receive;
}

void spi_slave_init()
{
USICR = (1<<USIWM0)|(1<<USICS1);

// MISO(PB6)は出力に
DDRB |= _BV(MISO);
// MOSI(PB5)/USCKは入力に
DDRB &= ~(_BV(MOSI) | _BV(USCK));

// MOSI(PB5)/USCK(PB7)プルアップ
PORTB |= (_BV(MOSI) | _BV(USCK));
}

int main(void)
{
spi_slave_init();

// LED(PB0..PB3)は出力に
DDRB |= _BV(PB0) | _BV(PB1) | _BV(PB2) | _BV(PB3);

// LEDを消灯
PORTB &= ~(_BV(PB0) | _BV(PB1) | _BV(PB2) | _BV(PB3));

// PB4プルアップ
PORTB |= _BV(PB4);

// PB4の変化割込み
GIMSK |= _BV(PCIE);
PCMSK |= _BV(PB4);

sei();

uint8_t d = 0x00;
for (;;)
{
while (!triggered)
{
}

// PB4がLOWになった

// clear
triggered = 0;


// 1バイト受信+送信
d = spi_slave_receive(~d);


// display bits as LED
for (int i = 0, check = 1; i < 4; i++, check*=2)
{
if (d & check)
{
PORTB |= check;
}
else
{
PORTB &= ~(check);
}
}
}
}


おわり:
ようやくマイコン間で8ビット転送できるようになった。
普段やってることは、重層な抽象化レイヤの上に成り立っているので、8ビットどころかネットワークを超えて簡単に送り放題だし、RPCでもなんでもいくらでもやりようがある。たまにこうやってプリミティブなところから積み上げるのもいい訓練になるなと思った。

環境:
 Windows 7 x64 Ultimate
  arduino 0022
  AVR Studio 4.18 + SP3
  WinAVR 20100110
Arduino Duemilanove
AVR ATtiny 2313V