AVRマイコン開発2|ATmega1284PをJTAGで使う

当サイトにはアフィリエイト広告が表示されます。

※Arduinoやmbedの記事ではありません。
AVRをCとアセンブラで開発、JTAGICEでデバッグ。
CPUはTiny2313・ATmega328・ATmega1284など。
ラジコンで使う比例制御方式のサーボを動かす記事があります。

AVRはArduinoに使われてますが、ここではJTAGICEと共にC言語やアセンブラで直接コーディングしています。

スポンサーリンク
興味があるところを読む

Mega1284PをJTAGで接続

mega1284Pを使い始めたのでメモ。
このCPUをJTAGで接続しましたが、ISP接続の数倍の速さが出るんでかなり快適です。
コンパイル終了後、最初のステップ実行まで2秒ほどで行ける。
この速さはかなり大事です。
「コンパイル → デバッグ」は開発中は数百数千回も繰り返す動作だから、この動作が速いのは開発効率に凄く響く・・・遅いと次にやることを忘れたりするし(^^;

と言うことで、もうISP接続は却下。
これからはやれる場合はコーディングは1284Pで行い、大体完成してから328P等に移植する方式で行きたい。
CPUの交換方法はこちら
これならSPIのデバッグも簡単にできる(ISP接続してるとSPI部分の実動作デバッグが出来なくて不便だった)。
それとこのCPUはPA~PD迄のポートが整然と8bit並んでて気持ち良いです。
なお5V動作の場合は外部Xtalの18.432MHz(ボーレートの絡み。ここの表を参照)を使用可能。ボーレートも115200以上も使える。

Fuse設定の注意
UARTにクロックを供給する為には外部Xtalをフルスイング動作させる必要が有る。
フルスイング動作電圧2.7~5.5V。
3Vでは10MHz迄しか動かないので、内蔵8MhzOSCを使い、UARTのボーレートの最高速度は38400としてる。

外部Xtal使用時のFuse設定。詳細はこちら

内蔵OSC使用時のFuse設定

//F_CPU定義
 	
#ifndef _COMMON_H_
#define _COMMON_H_

// F_CPU指定(Fuseを合わせる)
#define F_CPU 18432000UL    // 外部Xtalの18.432MHz
//#define F_CPU  8000000UL  // 内蔵OSCの8.000MHz
 	
//UARTの定義部分
 	
// バッファサイズ:328はRAM=2K,1284はRAM=16Kだ
#define FIFO_SIZE 64

// ボーレート:F_CPU=8MHz時は38400,18.432Mhzなら230400まで行ける
#if F_CPU == 8000000UL
#define BAUD 38400
#warning "ボーレートは38400だ in _uart.c"
#elif F_CPU == 18432000UL
#define BAUD 115200
#warning "ボーレートは115200だ in _uart.c"
#else
#warning "F_CPU指定が変だよ in _uart.c"
#endif

// 1284はUART0を使用
#if defined (__AVR_ATmega1284P__)
#define _ISR_RX_NAME    USART0_RX_vect    // 受信割込みベクタ
#define _ISR_UDRE_NAME  USART0_UDRE_vect  // 送信    "
#warning "UARTは1284p用に作成された in _uart.c"
// 328はUARTは一つだけだ
#elif defined (__AVR_ATmega328P__)
#define _ISR_RX_NAME    USART_RX_vect  // 受信割込みベクタ
#define _ISR_UDRE_NAME  USART_UDRE_vect  // 送信    "
#warning "UARTは328p用に作成された in _uart.c"
#endif

5V動作時:消費電流はUARTで通信する程度なら15mA程
 ・電源はTTL_232R_5V(Max75mA)からの出力。
 ・外部Xtal使用
 ・UARTのボーレートは230400以下

3V動作時:消費電流はUARTで通信する程度なら8mA程
 ・電源はTTL_232R_3V3(Max75mA)、またはFT232RL(Max24mA)からの出力。
 ・内蔵OSC使用
 ・UARTのボーレートは38400以下

その他
 ・抵抗入りLEDの消費電流は4mA

Timer0でインターバル割込み

Timer1は16bitで、今後なにかと使い途があるので、インターバル割込み程度は8bitのTimer0で。
使ったCPUはmega1284Pだけど、328P(分周比設定レジスタTCCR0BのCS0/1/2が5段階タイプ)も使えるはず。
能書きはここが簡潔で詳しいです。ここも。

タイマーは結構面倒くさい部分が多いんだけど、AVRのは割と簡単。
クロックソースは外部Xtalの18.432MHzと、内蔵OSCの8MHzを #if で切り換える方式。
PA0にオシロを繋いでインターバルを見ながら作った。使ってるオシロはこれ
この手のパソコンオシロは画面を取り込みやすくて良いですね。

ヘッダファイル “atmega164p_354p_644p_mori2.5.0.h” は、ルネサス方式のレジスタ指定を可能にするファイルで、森下功啓製作所さんで頂いたこれ・・・便利に使わせて頂いております。有り難うございます。
この頃シミジミ思うけど、みなさん親切でホント助かってます。

//Timer0による割込み
 	
#include "_common.h"
#include "_typedefs.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/sfr_defs.h>
#include <avr/power.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "_prototype.h"
#include "atmega164p_354p_644p_mori2.5.0.h"

static volatile CALLBACK_FUNC _cbfunc=0;
static volatile CALLBACK_FUNC _dpcfunc = 0;
static volatile ulong _dpctm = 0;

//==========================================================================================================
// timer0の初期化
//  TCCR0Bで分周比を設定する:000=停止,001=分周ナシ,010=8,011=64,100=256,101=1024
//  OCR0Aでカウント個数を設定する
//==========================================================================================================
void timer0_init(CALLBACK_FUNC func)
{
    _cbfunc=func;   // コールバック関数設定
    TCCR0B = 0b00000000;    // 停止させてから設定する
    TCCR0A = 0b00000010;    // CTC動作
    TIMSK0 = 0b0000010;     // 比較A一致割り込み有効
    //------------------------------
    // φ18.432MHzの場合
    //------------------------------
#if F_CPU == 18432000UL      
    // 1mSのインターバルの場合
    TCCR0B = 0b00000100;    // 100=256分周
    OCR0A = 71;             // 0.001秒 / (1 / 18432000 * 256分周) = 72(71設定時1.00mS)
    // 100uSのインターバルの場合
    //TCCR0B = 0b00000010;  // 010=8分周
    //OCR0A = 230;      // 0.0001秒 / (1 / 18432000 * 8分周) = 230.4(230設定時100.26uS)
    //------------------------------
    // φ8.000MHzの場合
    //------------------------------
#elif F_CPU == 8000000UL    
    // 1mSのインターバルの場合
    TCCR0B = 0b00000011;    // 011=64分周
    OCR0A = 124;            // 0.001秒 / (1 / 8000000 * 64分周) = 125(124設定時1.00mS)
    // 100uSのインターバルの場合
    //TCCR0B = 0b00000010;  // 010=8分周
    //OCR0A = 99;       // 0.0001秒 / (1 / 8000000 * 8分周) = 100.0(99設定時100.00uS)
#else
#warning "F_CPUエラー in _timer0.c"
#endif
    PA.DDR.BIT.B0 = 1;  // for debug PA0=OUT
}
//---------------------------------------------------------------------------------------------------
// Timer0 ISR
//---------------------------------------------------------------------------------------------------
ISR(TIMER0_COMPA_vect)
{
    PA.PIN.BIT.B0 = 1;  // for debug PA0=bit反転
    
    // インターバル毎に登録された関数を呼出す
    if (_cbfunc) _cbfunc();
    // DPC処理
    if (_dpcfunc && _dpctm) {
        if (0 == --_dpctm) _dpcfunc();
    }
}
//===================================================================================================
// Deferred Procedure Call:指定時間経過時に実行する関数を登録する
//===================================================================================================
void timer0_dpcr(CALLBACK_FUNC func, ulong tcnt)
{
    _dpcfunc = func;
    _dpctm = tcnt;
}

Timer0とTimer2によるハードウエアPWM

ラジコンのサーボを動かしたり、LEDの明るさ調節なんかに使うハードウエアPWM。
周波数が低くて構わないならインターバル割込みでソフト的にPWMする手も有る。
ヘッダファイル “atmega164p_354p_644p_mori2.5.0.h” はこちらに記述。

//----------------------------------------------------------------------------------------------------------/*
 * _pwm.c
 *
 * Created: 平成 24/8/3 5:10:21
 *  Author: oj3
 *  2013/11/09 機能追加
 *
 *  <注意>
 *  timer0/2を使ったハードウエアPWM(mylib.cにはtimer0割込みによるソフトPWMがあるよ)
 *  ここでtimer0を使うと_timer0.cのインターバルタイマは使えなくなる
 *
 *  <解説>
 *  timer0/2は8bitカウンタのタイマーだ(timer1は16bitでインプットキャプチャ可)。
 *  timer0は外部φを入力可、timer2は内部φのみだけど分周比を細かく設定できる・・・違いはここだけ。
 *  なおどちらも8bitカウンタなので、出力される周波数は "CPUφ÷256÷分周値"となる。
 *
 *  <他>
 *  ラジコンサーボを動かす場合は、timer一個で2CH分を動かすことが可能。
 *  つまりOCxAとOCxBには同じ周波数で異なるdutyを出すことができる。
 *  // duty値:15=1.03mS, 22=1.47mS, 31=1.99mS
 *  OCRxA = 22; // CH1用のduty:1.47mS幅
 *  OCRxB = 15; // CH2   "    :1.03mS幅
 *  なお16MHzを1024分周すれば手頃な周波数になるよ(16MHz÷256÷1024=周期16mS)
 *
 *  <参照先>
 *  http://startelc.com/AVR/Avr_100timrMemo.html
 *  https://sites.google.com/site/hananekosugan/sanpuru-kodo/avr-basic-usage-sample-code/fastpwm
 *  http://usicolog.nomaki.jp/engineering/avr/avrPWM.html
 *  http://www.natural-science.or.jp/article/20101223202536.php
 *///----------------------------------------------------------------------------------------------------------

#include "_common.h"
#include "_typedefs.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/sfr_defs.h>
#include <string.h>
#include <stdlib.h>
#include "atmega164p_354p_644p_mori2.5.0.h"

#define _TIMER0_USE   0    // timer0をPWMとして使うなら1(_timer0.cのインターバルタイマは使えなくなるよ)
#define _TIMER2_USE   1    // timer2       "

//==========================================================================================================
// 初期化
//==========================================================================================================
void pwm_init(int usetimer)
{
#if _TIMER0_USE == 1
    //-------------------------------------------------------------------
    // 8bitのtimer0によるPWM周波数の設定(timer2と異なり外部φも使える)
    //-------------------------------------------------------------------
#if defined (__AVR_ATmega1284P__)
    PB.DDR.BIT.B3 = 1;    // PB3(OC0A)=OUT
    PB.DDR.BIT.B4 = 1;    // PB4(OC0B)=OUT
#elif defined (__AVR_ATmega328P__)
    PD.DDR.BIT.B6 = 1;    // PD6(OC0A)=OUT
    PD.DDR.BIT.B5 = 1;    // PD5(OC0B)=OUT
#endif
    TCCR0A = 0b10100011;    // OC0A=PWM出力,OC0B=PWM出力,高速PWM&非反転動作
    TCCR0B = 0b00000011;    // 000=停止,001=分周ナシ,010=8,011=64,100=256,101=1024,110&111=外部φ
#endif

#if _TIMER2_USE == 1
    //-------------------------------------------------------------------
    // 8bitのtimer2によるPWM周波数の設定(timer0よりも分周比を細かく設定できる)
    //-------------------------------------------------------------------
#if defined (__AVR_ATmega1284P__)
    PD.DDR.BIT.B7 = 1;    // PD7(OC2A)=OUT
    PD.DDR.BIT.B6 = 1;    // PD6(OC2B)=OUT
#elif defined (__AVR_ATmega328P__)
    PB.DDR.BIT.B3 = 1;    // PB3(OC2A)=OUT
    PD.DDR.BIT.B3 = 1;    // PD3(OC2B)=OUT
#endif
    TCCR2A = 0b10100011;    // OC2A=PWM出力,OC2B=PWM出力,高速PWM&非反転動作
    TCCR2B = 0b00000100;    // 000=停止,001=分周ナシ,010=8,011=32,100=64,101=128,110=256,111=1024
#endif
}
//==========================================================================================================
// dutyのセット(1~255)
// OCxAとOCxBには同一周波数で異なるdutyのPWM波形を出力可
//==========================================================================================================
void pwm_duty0(uint8_t a, uint8_t b)    // Timer0
{
#if _TIMER0_USE == 1
    OCR0A = a;
    OCR0B = b;
#else
    while (1);  // timer0未使用時はstop
#endif
}
void pwm_duty2(uint8_t a, uint8_t b)    // Timer2
{
#if _TIMER2_USE == 1
    OCR2A = a;
    OCR2B = b;  
#else
    while (1);  // timer2未使用時はstop
#endif
}

インターバル割込みを使ったソフトウエアPWM

 周波数が低くて構わないのならインターバル割込みでソフト的にPWMする。
 ここではPBにPWMを出力してる。
 タイマー割込みのインターバルが1mSならPWM周波数は1KHzになるが、LEDの明るさ調節程度ならこれで十分。
 またdutyは0~10で指定している(0=消灯)。

//===================================================================================================
// ソフトPWMで点灯する
//===================================================================================================
static BYTE _cp, _ch;
void mylib_softPwmSet(BYTE usebit, BYTE lum)        
{
    _ch = usebit; // 点灯するBIT
    _cp = lum;    // 明るさ(0~10)
}
// PWM動作(1mS毎のインターバルタイマ割込みで実行される)
static void _softPwmIntr(void)
{
    static BYTE c;
    static bool onoff;
    if (++c == 10) c = 0;
    // 点灯
    if (c < _cp) {
        if (onoff == OFF) {
            onoff = ON;               
            PORTB = _ch;    // Hで点灯
        }
    } 
    // 消灯
    else {
        if (onoff == ON) {
            onoff = OFF;
            PORTB = 0;    // Lで消灯
        }
    }
}

Mega1284PのSPIでIOを増やす

この記事はアホ丸出しです。
こんなことをしなくてもLPC1768かLPC1114のQFPパッケージを使うとか、IOエキスパンダでも良いし。
世間知らずは恐ろしいことを始めます(爆)
多数のIOが必要な場合は、今では素直にmbedを使ってます。

mbedを使って新規製品のプロトタイプを作ってるんだけど、I/Oが不足してきた。
もう一個mbedを足しても良いんだけど、将来もっともっとI/Oを増やすことを考えると、ここはAVRでしょ!・・・そんなに増えることなんて無いって(笑)
で、mega1284Pの出番。
これはJTAGが使えるのでやっぱり便利。

mbedとはSPIで接続した。
通信速度は1MHz、MODE=3。
1284Pを最高速度で動作させるには仕様上は5Vが必要です・・・3.3Vでも動いてる感じはしますが・・・

mbedはSPIのmaster(マスター)、mega1284PはSPIのslave(スレーブ)。
mbedからSPIでコマンドを出力し、受け取った1284PはポートにOUTしたり、PORTからINした値をmbedに返したりする。
最初はUARTでやってたんだけど、拡張性を考えるとやっぱりSPIだろってことで。
I2Cでも良いんだけど、今回はSPI接続の周辺が他にあるんでSPIにした。

回路はこんな感じで・・・ごく当たり前に繋いだだけです。

 プロトコルと言うほどの物じゃないけど、念のため通信用のヘッダを付けることにした。
 1パケットは ヘッダ(0xff)+PORT+DATA。
 1284PのポートからINするときはPORTのBIT7を立てる、OUTするときは0のまま。
 またDATA=0xffの時はヘッダと区別がつかなくなるんで、PORTのBIT6を立てることにした。
 mbed側のソフトはこれ(省略有り)。
 ちょい無駄なコードも入ってます。

// SPI
SPI             SPI_a(p5, p6, p7);      // mosi, miso, sck
DigitalOut      DGO_VoiceSs(p21);       // Voice ss
DigitalOut      DGO_M1284Ss(p8);        // M1284 ss
DigitalOut      DGO_Fpga1Ss(p13);       // FPGA-1 ss
DigitalOut      DGO_Fpga2Ss(p14);       // FPGA-2 ss

// mainで初期設定しておく
SPI_a.format(8, 3);             // 8bit,mode=3
SPI_a.frequency(1000000);       // clk=1MHz

//----------------------------------------------------------------------------------------
// Mega1284Pとの通信
//----------------------------------------------------------------------------------------
// M1284へOUTする(port指定はPA=0/PB=1/PC=2/PD=3)
static void AvrOut(int port, BYTE data)
{
    if (data == 0xff) {
        port |= BIT6;
        data = 0;
    }
    DGO_M1284Ss = 0;
    SPI_a.write(0xff);  // header send
    SPI_a.write(port);  // port send
    SPI_a.write(data);  // data send
    DGO_M1284Ss = 1;
}
// M1284からINする(port指定はPA=0/PB=1/PC=2/PD=3)
static BYTE AvrIn(int port)
{
    DGO_M1284Ss = 0;
    wait_us(2);   // Slaveが準備出来るまでチョイ待つ
    SPI_a.write(0xff);        // header send
    SPI_a.write(port|BIT7);   // port send
    SPI_a.write(0);           // data send
    wait_us(5);   // Slaveが転送中にSSをDisableにしないようにチョイ待つ
    DGO_M1284Ss = 1;

    DGO_M1284Ss = 0;
    wait_us(2);   // Slaveが準備出来るまでチョイ待つ
    while (SPI_a.write(0) != 0xff);  // header recv
    port = SPI_a.write(0);            // port recv
    BYTE data = SPI_a.write(0);       // data recv
    wait_us(5);   // Slaveが転送中にSSをDisableにしないようにチョイ待つ
    DGO_M1284Ss = 1;
    if (port & BIT6) data = 0xff;
    return data;
}

 mega1284Pのソースはこれ。
 こっちは「部品としての動き」が必要なんでWDTを使用。
 ヘタなタイムアウトのコードを入れるよりも、WDTの方がずっと安全。
 なおWDTに関してはこちらこちらを参照させて頂いた。有り難うございます。

//----------------------------------------------------------------------------------------
//
// SPI_IO 2013/12/07 oj3 mbedとMega1284PとのSPI(MODE=3)通信によるI/O
//  ・m1284はSlaveとして動作する(内蔵φ8Mで使用)
//  ・timeoutはWDTを使用(ヘッダ受信後に残りのデータは250mS以内に受信終了すること)
//
// mbed → 1284
//  [0]:ヘッダ(FFh)
//  [1]:ポートアドレスなど
//      bit7  :I/O方向の指定で0=OUT, 1=IN
//      bit6  :1ならOUT値はFFh
//      bit5~2:予備
//      bit1~0:ポートアドレス(PA=0,PB=1,PC=2,PD=3)
//  [2]:ポートへのOUT値
//      0~FEh(FFhをOUTする時はDontCare)
//
// 1284 → mbed
//  [0]:ヘッダ(FFh)
//  [1]:ポートアドレスなど
//      bit7 :DontCare
//      bit6 :1ならIN値はFFh
//      bit5~2:予備
//      bit1~0:ポートアドレス(PA=0,PB=1,PC=2,PD=3)
//  [2]:IN値
//      0~FEh(FFhをINした時はDontCare)
//
//----------------------------------------------------------------------------------------

#define _MAIN

#include "_common.h"
#include "_typedefs.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/sfr_defs.h>
#include <avr/power.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "_prototype.h"
#include "atmega164p_354p_644p_mori2.5.0.h"

#define _WDT_15MS  ((0<<WDP3)|(0<<WDP2)|(0<<WDP1)|(0<<WDP0))
#define _WDT_30MS  ((0<<WDP3)|(0<<WDP2)|(0<<WDP1)|(1<<WDP0))
#define _WDT_60MS  ((0<<WDP3)|(0<<WDP2)|(1<<WDP1)|(0<<WDP0))
#define _WDT_120MS ((0<<WDP3)|(0<<WDP2)|(1<<WDP1)|(1<<WDP0))
#define _WDT_250MS ((0<<WDP3)|(1<<WDP2)|(0<<WDP1)|(0<<WDP0))
#define _WDT_500MS ((0<<WDP3)|(1<<WDP2)|(0<<WDP1)|(1<<WDP0))
#define _WDT_1S    ((0<<WDP3)|(1<<WDP2)|(1<<WDP1)|(0<<WDP0))
#define _WDT_2S    ((0<<WDP3)|(1<<WDP2)|(1<<WDP1)|(1<<WDP0))
#define _WDT_4S    ((1<<WDP3)|(0<<WDP2)|(0<<WDP1)|(0<<WDP0))
#define _WDT_8S    ((1<<WDP3)|(0<<WDP2)|(0<<WDP1)|(1<<WDP0))

//----------------------------------------------------------------------------------------------------------
// SPI初期化
//----------------------------------------------------------------------------------------------------------
static void spi_init(void)
{
    //-------------------------------------------------------
    // SPI設定(MODE=3の場合)
    //  ・MSB=first
    //  ・SCK=アイドル状態の時Hレベル(CPOL=1)
    //  ・データのサンプリングはSCKの立下りで行われる(CPHA=1)
    //  ・SPI周波数は1MHzにしてるがSlaveなので無関係だ
    //-------------------------------------------------------
    SPCR = 0b01001101;  // b7=0:SPIF(転送終了時)の割込みは使わない
                        // b6=1:SPIを使う
                        // b5=0:MSB first
                        // b4=0:slave mode
                        // b3=1:CPOL(クロックの向き)0=常時L, 1=常時H
                        // b2=1:CPHA(サンプルエッジ)0=前, 1=後ろ
                        // b1/0=01:分周比はfosc/16(SPI2X=1なので倍速モードでfosc/8)
                        //  SPI2X=0 & 00=4, 01=16, 10=64, 11=128
    sbi(SPSR, SPI2X);   //  SPI2X=1 & 00=2, 01=8 , 10=32, 11=64 → '101'=8分周=CPUφ8MHz÷8=1MHz
}
//==========================================================================================================
// main 
//==========================================================================================================
int main(void)
{
    cli();

    // PBポートのI/O方向の設定
    DDRB = 0b01000000;  // PB7=IN  SCK
                        // PB6=OUT MISO
                        // PB5=IN  MOSI
                        // PB4=IN  SS
                        // PB3=IN  エンコーダPUSHSW(Push時L)
                        // PB2=IN  DIPSW(On=L)
                        // PB1=IN  モード選択SW(dontCare)
                        // PB0=IN  検査開始SW(Push時L)

    // PB3/2/1/0をPullUp
    // SPIで使うPB7/5/4はPulUp禁止である(PullUpすると他のデバイスがSPI信号を拾えなくなる)
    // IN方向のポートに1をOUTするとPullUpしてしまうので注意せよ
    PORTB = 0b00001111;
    
    // PAポートのI/O方向の設定
    DDRA = 0b11111111;  // PA7~0=OUT LED付きSWのLED表示制御(Lで点灯)
    // PDポートのI/O方向の設定
    DDRD = 0b00000000;  // PD7~0=IN  LED付きSWのSW入力(Push時L)
    PORTD = 0b11111111;   // PD7~0 PullUp
    // PCポートのI/O方向の設定
    DDRC |= 0b11000011; // PC7=OUT 検査モード中LED(Lで点灯)
                        // PC6=OUT 登録モード中LED(Lで点灯)
                        // PC5~2   JTAG
                        // PC1=OUT 検査結果LED赤(Lで点灯)
                        // PC0=OUT 検査結果LED緑(Lで点灯)
    // SPI設定
    spi_init();

    // WDT設定(250MS)
    wdt_reset();
    MCUSR &= ~(1<<WDRF);          // 「WDTリセットされた」フラグクリア
    WDTCSR |= (1<<WDCE)|(1<<WDE); // wdt変更前処理
    WDTCSR  = (1<<WDIE)|(1<<WDE)|_WDT_250MS;

    sei();

    // Loop(SS/SCIF割込みを使わずにLoopで動作)
    while (1) {
        while(!(SPSR & (1<<SPIF))) wdt_reset(); // header待ち(WDTクリア)
        if (SPDR != 0xff) continue;
        while(!(SPSR & (1<<SPIF)));             // port
        BYTE port = SPDR;
        while(!(SPSR & (1<<SPIF)));             // data
        BYTE data = SPDR;
        //---------------------------------
        // INなら・・・
        //---------------------------------
        if (port & BIT7) {
            port &= 0x3;
            switch (port) {
                case 0:   data = PINA;   break; // PA
                case 1:   data = PINB;   break; // PB
                case 2:   data = PINC;   break; // PC
                case 3:   data = PIND;   break; // PD
            }
            // IN値がFFhならPORTのBIT6を立てる
            if (data == 0xff) {
                port |= BIT6;
                data = 0x00;
            }
            SPDR = 0xff;    // header send
            while(!(SPSR & (1<<SPIF)));
            SPDR = port;    // port send
            while(!(SPSR & (1<<SPIF)));
            SPDR = data;    // data send
            while(!(SPSR & (1<<SPIF)));
            SPDR = 0;  // clear SPIF
        }
        //---------------------------------
        // OUTなら・・・
        //---------------------------------
        else {
            // PORTのBIT6が立ってるならOUT値はFFhだ
            if (port & BIT6) data = 0xff;
            port &= 0x3;
            switch (port) {
                case 0:   PORTA = data;        break;  // PA
                case 1:   PORTB = data & 0xf;  break;  // PBは上位4bitがSPI用なので1をOUTしない
                case 2:   PORTC = data;        break;  // PC
                case 3:   PORTD = data;        break;  // PD
            }
        }
    }
}

動作の確認にLAP-C(プロトコルアナライザ)を使った。設定
mbed、AVR、LAP-C、パソコンオシロ・・・安くて良い物を売ってます。
有り難い。

スポンサーリンク

CPUの交換方法

ATmega1284Pは高速でバッグが可能なJTAGを使用可能。
そこで1284pでコードを作ってから、328pにCPUを入れ替える方式で開発してる。
この場合のCPUの変更方法。

プロジェクトメニューでデバイスを328pに変更

ToolタブのICEのインターフェースを”debugWIRE”に変更(ISPではICE動作不可)

これだけで 1284p → 328p へと変更できます。

ピン配置

スポンサーリンク

購入リンク

¥34,241 (2023/01/30 04:50時点 | Amazon調べ)
\楽天ポイント4倍セール!/
楽天市場で探す
スポンサーリンク
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


興味があるところを読む