動作確認
出来上がったので動作確認をする。ATmega328Pによる制御回路、制御コードの中身は、だいぶ整理したが『CPUの創りかた』のときとまったく同じである。
#include "CPU1738_external.h" /* ニモニック(のようなもの)一覧: ※ ジャンプ命令は1クロック、それ以外は2クロック。HALTは状態を保持して停止。 LD_A(Data) : A ← [Data] LD_B(Data) : B ← [Data] LD_AB : A ← B LD_BA : B ← A ADD_AB : A ← A + B SUB_AB : A ← A - B ADD_A(Data) : A ← A + [Data] SUB_A(Data) : A ← A - [Data] OUT_A : OUT ← A OUT_B : OUT ← B OUT(Data) : OUT ← [Data] IN_A : A ← IN JUMP(Address) : PC ← [Address] JNC(Address) : PC ← [Address] if NOT Carry JNZ(Address) : PC ← [Address] if NOT Zero HALT : HALT */ int main(void){ // アセンブル(のようなこと)をしてROM (に見立てた配列)へ格納する。 // 1 + 2 + 3 + 4 + 5 = 15を計算する。 _[0] = LD_A(1); _[1] = OUT_A; _[2] = ADD_A(2); _[3] = OUT_A; _[4] = ADD_A(3); _[5] = OUT_A; _[6] = ADD_A(4); _[7] = OUT_A; _[8] = ADD_A(5); _[9] = OUT_A; _[10] = LD_A(0); _[11] = LD_A(0); _[12] = LD_A(0); _[13] = LD_A(0); _[14] = LD_A(0); _[15] = OUT(0); // CPU1738用の制御回路を有効化する。 // 第1引数: PD7から出力するクロック周波数Hz。8~32767Hz。 // 第2引数: それを何分周して低速クロックとしてPD6から出力するか。1~65535分周。 // ※第2引数を指定しない場合はPD6から1Hzが出力される。 enable_CPU1738_external(10); return 0; }
#ifndef CPU1738_EXTERNAL_H_ #define CPU1738_EXTERNAL_H_ #define F_CPU 8000000UL #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> // ジャンプ命令は1クロック、それ以外は2クロック。HALTは状態を保持して停止。 #define LD_A(Data) (0 << 4 | ((Data) & 0xF)) // A ← [Data] #define LD_B(Data) (1 << 4 | ((Data) & 0xF)) // B ← [Data] #define LD_AB (2 << 4) // A ← B #define LD_BA (3 << 4) // B ← A #define ADD_AB (4 << 4) // A ← A + B #define SUB_AB (5 << 4) // A ← A - B #define ADD_A(Data) (6 << 4 | ((Data) & 0xF)) // A ← A + [Data] #define SUB_A(Data) (7 << 4 | ((Data) & 0xF)) // A ← A - [Data] #define OUT_A ( 8 << 4) // OUT ← A #define OUT_B ( 9 << 4) // OUT ← B #define OUT(Data) (10 << 4 | ((Data) & 0xF)) // OUT ← [Data] #define IN_A (11 << 4) // A ← IN #define JUMP(Address) (12 << 4 | ((Address) & 0xF)) // PC ← [Address] #define JNC(Address) (13 << 4 | ((Address) & 0xF)) // PC ← [Address] if NOT Carry #define JNZ(Address) (14 << 4 | ((Address) & 0xF)) // PC ← [Address] if NOT Zero #define HALT (15 << 4) // HALT volatile bool Switch_Changed = false; ISR(INT0_vect){ // 手動クロック用のタクトスイッチが押されるか放されるかしたら、 Switch_Changed = true; // そのフラグを立てる } volatile uint8_t _[16]; // 命令を格納しておく配列。 ISR(PCINT1_vect){ // アドレスが変化したら、 PORTB = _[PINC & 0x0F]; // そのアドレスに応じたデータを出力する。 } volatile uint16_t Prescale; // 高速クロックを何分周して低速クロックにするか。 volatile uint16_t Counter = 0; ISR(TIMER1_COMPA_vect){ PORTD ^= (1 << PD7); // 高速クロックの倍の頻度でPD7出力をトグルする。 if(++Counter >= Prescale){ // その方形波を何分周かして低速クロックとしてPD6から出力する。 PORTD ^= (1 << PD6); Counter = 0; } } void enable_reset(void){ DDRD |= (1 << PD3); // PD3から!リセット信号を出力することにする。 PORTD &= ~(1 << PD3); // 最初に!リセット信号をLにしておいて、 _delay_ms(100 - 65); // 適当にリセット時間を設けて、(65msはATmega328P自体のリセットからの遅延時間) PORTD |= (1 << PD3); // リセット期間が明けたら!リセット信号をHに戻す。 } void enable_manual_clock(void){ DDRD |= (1 << PD5); // PD5から手動クロックを出力することにする。 PORTD &= ~(1 << PD5); // 最初はLを出力しておく。 DDRD &= ~(1 << PD2); // PD2をスイッチ入力にする。 PORTD |= (1 << PD2); // PD2を内部プルアップする。 EICRA |= (1 << ISC00); // INT0 (PD2)が変化したときに割り込み要求を出すことにする。 EIMSK |= (1 << INT0); // 外部割り込みINT0を有効にする。 sei(); } inline void debounce_PD2_to_PD5(void){ if(Switch_Changed){ // PD2に接続したスイッチが変化したら、 Switch_Changed = false; _delay_ms(5); // バウンスの収まるまで待ってから、 if(PIND & (1 << PD2)){PORTD &= ~(1 << PD5);} // スイッチが放されていたらPD5からLを出力し、 else {PORTD |= (1 << PD5);} // スイッチが押されていたらPD5からHを出力する。 } } void enable_rom(void){ DDRC &= ~0x0F; // PC3:0にアドレスを入力することにする。 //PORTC |= 0x0F; // PC3:0を内部プルアップする。※実際にTD4に接続するときは内部プルアップは不要。 DDRB = 0xFF; // PB7:0から命令を出力することにする。 PORTB = _[0]; // 最初のクロックが立ち上がる前に0番地の命令を出力しておく。 // PCINT11:8 (PC3:0)からピン変化割り込みをかけることにする。 PCICR |= (1 << PCIE1); PCMSK1 |= ((1 << PCINT11)|(1 << PCINT10)|(1 << PCINT9)|(1 << PCINT8)); sei(); } void enable_Hi_Lo_clock(uint16_t freq_hi){ DDRD |= (1 << PD6) | (1 << PD7); // 自動クロックを出力する端子を指定する。 TCCR1B |= (1 << WGM12); // タイマー1をCTCモードで動かす。 TCCR1B |= (1 << CS11); // 8分周してタイマー1でカウントする。 OCR1A = (F_CPU/8)/(freq_hi*2)-1; // コンペア値。(F_CPU/8分周)/(高速クロック周波数*2)-1 TIMSK1 |= (1 << OCIE1A); // タイマー1のコンペアマッチ割り込みを有効にする。 sei(); } // 全部まとめる // 第1引数: PD7から出力するクロック周波数Hz。8~32767Hz。 // 第2引数: それを何分周して低速クロックとしてPD6から出力するか。1~65535分周。 // ※第2引数を指定しない場合はPD6から1Hzが出力される。 void enable_CPU1738_external(uint16_t freq_hi, uint16_t prescale){ Prescale = prescale; enable_rom(); enable_reset(); enable_manual_clock(); enable_Hi_Lo_clock(freq_hi); while(true) debounce_PD2_to_PD5(); } void enable_CPU1738_external(uint16_t freq_hi){ // 分周比を指定しない場合 enable_CPU1738_external(freq_hi, freq_hi); } /*動作確認*/ /*テキストリスト1 加算プログラム(ループなし) 1 + 2 + 3 + 4 + 5 = 15を計算する。 _[0] = LD_A(1); _[1] = OUT_A; _[2] = ADD_A(2); _[3] = OUT_A; _[4] = ADD_A(3); _[5] = OUT_A; _[6] = ADD_A(4); _[7] = OUT_A; _[8] = ADD_A(5); _[9] = OUT_A; _[10] = LD_A(0); _[11] = LD_A(0); _[12] = LD_A(0); _[13] = LD_A(0); _[14] = LD_A(0); _[15] = OUT(0); */ /* _[0] = OUT_A; _[1] = ADD_A(1); // 1ずつインクリメントして、 _[2] = JNC(0); _[3] = SUB_A(1); // 1ずつデクリメントして、 _[4] = OUT_A; _[5] = JNZ(3); _[6] = JUMP(0); // を繰り返す。 */ #endif