製作モジュール一覧

ラベル ATMega の投稿を表示しています。 すべての投稿を表示
ラベル ATMega の投稿を表示しています。 すべての投稿を表示

2025年1月2日木曜日

Arduino Writerの製作

こちらの基板をBOOTHで販売しております。

製作の目的

素のATMega328PをArduinoとして使うために、Arduino Unoを使ってBootloaderを書き込めます。簡単な回路なのでブレッドボードでも出来ますが、毎回ブレッドボードで回路を組むのが面倒なので専用の基板を製作しました。また、Bootloaderを書き込んだ後、USB-Serial(TTL)モジュールを使って、Arduinoのスケッチも書き込めるようにしました。


大きく2つに分けて使用方法を示します。

  1. ArduinoのBootloaderを書き込む
  2. Arduinoのスケッチを書き込む

Arduino IDEはVer.2.3.2を使って説明しますが他のバージョンでも大きく違うことはないと思います。Arduino UnoはATMega16U2の代わりにCH340を使った互換ボードでも動作確認しています。

1.ArduinoのBootloaderを書き込む


基本的な概念はArduinoの公式ページを参照してください。

Arduino UnoにProgramer用スケッチを書き込む


まずArduino UnoをBootloaderのProgrammerとして使うためのスケッチを書き込みます。

手順
  1. Arduino UnoとPCをUSB接続する
    Arudino Writer(この基板)とは接続しないでおきます
  2. PCでArduino IDEを起動し、File→Examples→11.ArduinoISP→ArduinoISPを開く
  3. Tools→Board→Arduino AVR Boards→Arduino Uno を選択
  4. Tools→Port→<Arduinoを接続しているPort> を選択
  5. Sketch→Upload、またはメニューバーの(→)アイコンでスケッチを書き込む
  6. IDEの右下にDone Uploading.とポップアップされるのを確認

これでArduino UnoをBootloader書き込み器として使う準備完了です。


Arudino Unoを使ってATMega328PにBootloaderを書き込む


配線図

ハーネスの作例

接続のようす

手順
  1. Arduino Writer(この基板)のZIFソケットにATMega328Pを挿入してレバーを倒す
    レバー側が1ピンです
  2. Arduino UnoとPCのUSB接続を外す
  3. Arduino UnoとArduino Writer(この基板)を接続する
  4. Arduino UnoとPCをUSB接続する
    Arduino Writer上のLEDチェックが走り、HTBT LEDがじんわりと点滅します
  5. PCでArduino IDEを起動
  6. Tools→Port→<Arduinoを接続しているPort> を選択
  7. Tools→Programmer→Arduino as ISP を選択
  8. Tools→BurnBootloader を選択
  9. IDEの右下にDone burning bootloaderとポップアップされるのを確認

これでATMega328PにArudinoのスケッチを書き込むためのBootloaderが書き込まれました。

2.Arduinoのスケッチを書き込む


スケッチの書き込みにはUSB Serial(TTL)モジュールを使用します。FT232RLとCH340のモジュールで動作確認しています。

テスト用にATMega328PにLチカのスケッチを書き込んでみます。

配線図

配線表

※TXDとRXDは互い違いになるように配線します
※CTSは配線しないでおきます
※VCCはUSB Serialモジュールで5Vに設定

配線用ハーネス

接続のようす

手順
  1. Arduino UnoとPCのUSB接続を外す
  2. Arduino UnoとArduino Writerの配線を外す
  3. USB SerialモジュールとArduino Writer(この基板)を接続する
  4. USB SerialモジュールをPCにUSB接続する
  5. PCでArduino IDEを起動
  6. File→Examples→01.Basics→Blinkを選択
  7. Tools→Board→AVR Arduino Board→Arduino Unoを選択
  8. Tools→Port→<USB SerialモジュールのPort> を選択
    Arduinoを接続していたPortと異なるで注意
  9. Sketch→Upload、またはメニューバーの(→)アイコンでスケッチを書き込む
  10. Done Uploading.とIDEの右下にポップアップされるのを確認

Arduino Writer(この基板)の緑のLEDが点滅したら成功です。

回路について


回路図

Bootloaderの書き込みはSPIを使います(D13~D10)。L_PROG、L_ERR、L_HTBTはProgramerとしてつかうArduino Unoからの出力を受けてLEDを点滅させます(D9~D7)。LEDの電流制限抵抗(R2、R3、R4)は実際に使うLEDの明るさで調整してください。私の使っているLEDは緑色だけ暗いのでR4は330Ωに変更しています。

U2のLM358はD13に接続するLEDをドライブしているだけです。D13はSPIのSCKとしても使うのでバッファリングして干渉を避けます。Arduino Unoでも同様の回路になっています。前述の理由でR5は330Ωに変更しています。

Sketchの書き込みにはSerial(D0、D1)とRESETを使います。DTRから0.1uFのコンデンサ(C5)介し、10kΩ(R1)でVCCにプルアップ必要があります。C5がハイパスフィルターとして働き、RESETピンに+5Vを超える電圧がかかります。このため、SBD(D4)でクランプして+5Vを超えないようにしています。


2017年8月26日土曜日

Arduino UnoをSPI Slaveとして使う。

なぜだか分からないが、ArduinoのライブラリはSPIのSlaveに対応していない。I2CはSlaveでも使えるのに何故なんだろう?

今まで素のAVRも含めてSPIのスレーブとして使ったことが無かったので、直接レジスタを叩いてやってみた。

Arduino UnoをSlaveにする



配線図

写真のオフィシャルのArduino Uno Rev3(緑色)をMaster、aitendoのびんぼうでいーの(黄土色)をSlaveとして使っている。

SPIの信号線の間にブレッドボードを入れているのはオシロで測定しやすくするためで、必要がなければArduinoのPin同士を直結した方がいい。以前mbedでSPIの接続を悪条件にしてテストした時エラーが多発した。(参考「Nucleo(mbed OS5)のSPI通信を検証してみる。」)

動作としては、Masterから1ビットずつシフトした8bit値をSlaveに送り(MOSI)、Slaveは受信したデータを8個のLEDに表示する。LEDは流れるように点灯する。

Slaveは1づつインクリメントした8bit値をMasterに送り(MISO)、Master側は受信したデータをSerialで出力する。←Arduino IDEのシリアルモニタでMasterが受信したデータを表示できる。

※Arduinoを2個使うとArduino IDEで操るのがそこそこややこしいです(^q^;

Masterのスケッチ
SPI_Master_LEDx8.ino

#include <SPI.h>
#define SPI_CS_PIN  (10)

uint8_t cnt = 1;

void setup()
{
  Serial.begin(9600);
  Serial.println("SPI_Master_LEDx8 Test");
  
  pinMode(SPI_CS_PIN, OUTPUT);
  digitalWrite(SPI_CS_PIN, HIGH);
  
  SPI.begin();
}

void loop()
{
  digitalWrite(SPI_CS_PIN, LOW);
  uint8_t rdata = SPI.transfer(1 << cnt);
  digitalWrite(SPI_CS_PIN, HIGH);

  cnt++;
  if (cnt > 8) {
    cnt = 0;
  }

  Serial.println(rdata);
  
  delay(100);  
}

Master側のSPIの設定はデフォルトで、Clock:4MHz、MSB First、Mode:0になる。

Slaveのスケッチ
SPI_Slave_LEDx8

volatile uint8_t sdata = 0;

void setup()
{
  // LEDs output
  DDRD = 0xFF;
  PORTD = 0xFF;
  _delay_ms(100);

  // LEDs check
  for (int i = 0; i < 8; i++) {
    PORTD = 1 << i;
    _delay_ms(100);
  }
  PORTD = 0x00;

  // SPI slave
  DDRB = 0x10;  // MISO(PB4)をoutputに設定

  // SPCRのビット設定
  // SPI有効:1 | SPI割り込み有効:1 | MSBから送信:0 | スレーブ:0
  // クロック極性:0 | クロック位相:0 | SPR1:0 | SPR0:0
  SPCR = (1 << SPIE) | (1 << SPE);
}

ISR (SPI_STC_vect)
{
  // 受信データをLEDに出力
  PORTD = SPDR;

  // 次に送信するデータをセット
  SPDR = sdata;
  sdata++;
}

void loop()
{
}

Slave側のスケッチは、まぎれが無いように基本的にArduinoの関数は使っていない。

「//LED check」の部分は、起動時に接続しているLEDを順番に全点灯して配線ミスがないか確認できるようにしている。

ATMega328PのハードウェアSPIは

SCK: PB5 (D13)
MISO: PB4 (D12)
MOSI: PB3 (D11)
CS(SS): PB2 (D10)

を使うようになっていて、Slaveの場合MISOだけはPinの機能をOUTPUTに指定する必要があるので、

  // SPI slave
  DDRB = 0x10;  // MISO(PB4)をoutputに設定

としている。

「ISR (SPI_STC_vect){}」は割り込みハンドラで、SPCRレジスタのSPIEビットを1に設定すると、SPI通信が完了したときに呼び出されるようになる。

SlaveからMasterに送信するデータはSPDRレジスタに設定すればよく、「ISR (SPI_STC_vect){}」内で、受信データをSPDRレジスタから読み出した後、SPDRに書き込んでいる。このデータは、次回SPI通信が行われたときにMISOから出力されてMasterに送信される。

SPI通信は割り込みで処理しているので、メインループの「loop()」では何もしていない。

SPI通信の状態


MOSI

ch1:MOSI ch2:SCK

SPIクロックは4MHz

MISO

ch1:MISO ch2:SCK

ATMega88VをSlaveにする



同じことをATMega328Pとピン互換で、安価なATMega88Vでもやってみた。

配線図

配線図ではatemega168となっているが、実際はATMega88Vを使って内蔵RC/8MHzで動作させている。

Fuse Bit
hFuse: DFh
lFuse: A2h (動作確認のためPB0からクロック出力)
eHuze: 01h

Slave(ATMega88V)のソースコード

Atmel Studio7 (Version: 7.0.1417)でビルド

/*
 * SPI_Slave_LEDx8.c
 *
 * Created: 2017/08/26 9:16:53
 * Author : gizmo
 */

#define F_CPU   (8000000UL)

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

volatile uint8_t sdata = 0;

ISR (SPI_STC_vect)
{
    // 受信データをLEDに出力
    PORTD = SPDR;
    
    // 次に送信するデータをセット
    SPDR = sdata;
    sdata++;
}

int main(void)
{
    // LEDs output
    DDRD = 0xFF;
    PORTD = 0xFF;
    _delay_ms(100);

    // LEDs check
    for (int i = 0; i < 8; i++) {
        PORTD = 1 << i;
        _delay_ms(100);
    }
    PORTD = 0x00;

    // SPI slave
    DDRB = 0x10;  // MISO(PB4)をoutputに設定

    // SPCRのビット設定
    // SPI有効:1 | SPI割り込み有効:1 | MSBから送信:0 | スレーブ:0
    // クロック極性:0 | クロック位相:0 | SPR1:0 | SPR0:0
    SPCR = (1 << SPIE) | (1 << SPE);

    sei();  // 割り込み許可
    
    while (1)
    {
    }
}

基本的にはArduinoのスケッチと同じだが、ハマってしまった点。

#include <interrupt.h>

これを指定しなくても、Atmel Studio 7ではWarningは出るがBuild出来てしまう。「int ISR (SPI_STC_vect){}」という普通の関数と解釈してコンパイルしてしまうからだろう。プログラムの中で「ISR()」は呼び出されていないのでリンクも出来てしまう。

sei();

Arduinoでは(全体の)割り込みはデフォルトで許可されているが、素のAVRのプログラムでは明示的にsei()を呼び出して、割り込みを有効化しないとダメ。

またSPIクロックも問題があって、Arduino同士で使ったMasterのスケッチではLEDの点灯動作がおかしくなった。

SlaveはATMega88Vを8MHz駆動させていて、SPIクロックの最大周波数はマスタークロックの1/2なので、Masterが送ってくるSPIクロックが4MHzだと処理が追いつかないんだと思う。

SPIクロックを1MHzにしたMasterのスケッチ

#include <SPI.h>
#define SPI_CS_PIN  (10)

uint8_t cnt = 1;

void setup()
{
  Serial.begin(9600);
  Serial.println("SPI_Master_LEDx8 Test");

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

  SPI.begin();
}

void loop()
{
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
  digitalWrite(SPI_CS_PIN, LOW);
  uint8_t rdata = SPI.transfer(1 << cnt);
  digitalWrite(SPI_CS_PIN, HIGH);
  SPI.endTransaction();

  cnt++;
  if (cnt > 8) {
    cnt = 0;
  }

  Serial.println(rdata);

  delay(100);
}

どこが違うかというと、

  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));

として、SPIクロックを1MHzにしている点。SPI.beginTransaction()しているので、SPI通信の終了時にSPI.endtransmission()している。

SPIの通信状態


MOSI

ch1:MOSI ch2:SCK

SPIクロックは1MHz

MISO

ch1:MISO ch2:SCK

Github:
https://github.com/ryood/Arduino_SPI_Slave