外部回路まとめ

これまでに作ったプログラムをひとつにまとめる。リセット信号はATmega328Pと共用することにした。
f:id:ti-nspire:20201025080429p:plain:w500

#include "TD4_external.h"

/* ニモニック(のようなもの)一覧:
MOV_A(Im) : AレジスタにImを転送
MOV_B(Im) : BレジスタにImを転送
MOV_AB    : AレジスタにBレジスタを転送
MOV_BA    : BレジスタにAレジスタを転送
ADD_A(Im) : AレジスタにImを加算
ADD_B(Im) : BレジスタにImを加算
IN_A      : 入力ポートからAレジスタへ転送
IN_B      : 入力ポートからBレジスタへ転送
OUT(Im)   : 出力ポートへImを転送
OUT_B     : 出力ポートへBレジスタを転送
JMP(Im)   : Im番地へジャンプ
JNC(Im)   : Cフラグが1ではないときにIm番地へジャンプ
*/

int main(){
    // アセンブル(のようなこと)をしてROM (と見なした排列)へ格納する。
    _[0] = OUT_B;
    _[1] = ADD_B(1);
    _[2] = JMP(0);

    // アドレスの変化に応じて排列からデータが出力できるようにする。
    enable_rom();

    // PD2のスイッチ入力に応じてPD5から手動クロックが出力できるようにする。
    enable_manual_clock();

    //PD7から10Hzを、PD6から1Hzを出力する。
    enable_1Hz_10Hz_clock();

    // PD2の入力をディバウンスしてPD5から出力する(押してH出力、放してL出力)。
    while(1) debounce_PD2_to_PD5();

    return 0;
}
#ifndef TD4_EXTERNAL_H_
#define TD4_EXTERNAL_H_

#define F_CPU 8000000UL

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

#define MOV_A(Im) ( 3 << 4 | ((Im) & 0xF)) // AレジスタにImを転送
#define MOV_B(Im) ( 7 << 4 | ((Im) & 0xF)) // BレジスタにImを転送
#define MOV_AB    ( 1 << 4)                // AレジスタにBレジスタを転送
#define MOV_BA    ( 4 << 4)                // BレジスタにAレジスタを転送
#define ADD_A(Im) ( 0 << 4 | ((Im) & 0xF)) // AレジスタにImを加算
#define ADD_B(Im) ( 5 << 4 | ((Im) & 0xF)) // BレジスタにImを加算
#define IN_A      ( 2 << 4)                // 入力ポートからAレジスタへ転送
#define IN_B      ( 6 << 4)                // 入力ポートからBレジスタへ転送
#define OUT(Im)   (11 << 4 | ((Im) & 0xF)) // 出力ポートへImを転送
#define OUT_B     ( 9 << 4)                // 出力ポートへBレジスタを転送
#define JMP(Im)   (15 << 4 | ((Im) & 0xF)) // Im番地へジャンプ
#define JNC(Im)   (14 << 4 | ((Im) & 0xF)) // Cフラグが1ではないときにIm番地へジャンプ

volatile uint8_t _[16]; // 命令を格納しておく排列。
volatile uint8_t switch_changed = 0;
volatile int8_t counter = 10;

ISR(INT0_vect){         // 手動クロック用のタクトスイッチが押されるか放されるかしたら、
    switch_changed = 1; // そのフラグを立てる
}
ISR(PCINT1_vect){           // アドレスが変化したら、
    PORTB = _[PINC & 0x0F]; // そのアドレスに応じたデータを出力する。
}
ISR(TIMER1_COMPA_vect){
    PORTD ^= (1 << PD7); // 20Hzの頻度でPD7出力をトグルする。結局10Hzの方形波が出力される。
    if(--counter <= 0){  // その方形波を10分周してPD6から出力する。結局1Hzの方形波が出力される。
        PORTD ^= (1 << PD6);
        counter = 10;
    }
}

/*
void enable_reset(){
   DDRD  |=  (1 << PD3); // PD3から!リセット信号を出力することにする。
   PORTD &= ~(1 << PD3); // 最初に!リセット信号をLにしておいて、
   _delay_ms(100 - 65);  // 適当にリセット時間を設けて、(65msはATmega328P自体のリセットからの遅延時間)
   PORTD |= (1 << PD3);  // リセット期間が明けたら!リセット信号をHに戻す。
}
*/
void enable_manual_clock(){
    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();  
}
void debounce_PD2_to_PD5(){
    if(switch_changed){                              // PD2に接続したスイッチが変化したら、
        switch_changed = 0;
        _delay_ms(5);                                // バウンスの収まるまで待ってから、
        if(PIND & (1 << PD2)){PORTD &= ~(1 << PD5);} // スイッチが放されていたらPD5からLを出力し、
        else                 {PORTD |=  (1 << PD5);} // スイッチが押されていたらPD5からHを出力する。
    }
}
void enable_rom(){
    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_1Hz_10Hz_clock(){
    DDRD   |= (1 << PD6)|(1 << PD7); // クロックを出力する端子を指定する。
    TCCR1B |= (1 << WGM12);          // タイマー1をCTCモードで動かす。
    TCCR1B |= (1 << CS11);           // 8分周してタイマー1でカウントする。
    OCR1A   = 49999;                 // コンペア値。(F_CPU/8分周)/(10Hz*2)-1 = 49999
    TIMSK1 |= (1 << OCIE1A);         // タイマー1のコンペアマッチ割り込みを有効にする。
    sei();
}

/*テキストの3分15秒ラーメンタイマー(クロックは1Hz)
_[ 0] = OUT(0b0111); // LEDを3つ点灯
_[ 1] = ADD_A(1);
_[ 2] = JNC(1);      // 16回ループ
_[ 3] = ADD_A(1);
_[ 4] = JNC(3);      // 16回ループ
_[ 5] = OUT(0b0110); // LEDを2つ点灯
_[ 6] = ADD_A(1);
_[ 7] = JNC(6);      // 16回ループ
_[ 8] = ADD_A(1);
_[ 9] = JNC(8);      // 16回ループ
_[10] = OUT(0b0000);
_[11] = OUT(0b0100);
_[12] = ADD_A(1);
_[13] = JNC(10);     // LED点滅を16回ループ点滅
_[14] = OUT(0b1000); // LEDを1つ点灯
_[15] = JMP(15);     // ここにとどまる。
*/

/*テキストのLEDちかちか
_[0] = OUT(0b0011);
_[1] = OUT(0b0110);
_[2] = OUT(0b1100);
_[3] = OUT(0b1000);
_[4] = OUT(0b1000);
_[5] = OUT(0b1100);
_[6] = OUT(0b0110);
_[7] = OUT(0b0011);
_[8] = OUT(0b0001);
_[9] = JMP(0);
*/

/* 外部入力をそのまま外部出力する。
_[0] = IN_B;
_[1] = OUT_B;
_[2] = JMP(0);
*/

/* 0~15まで1ずつインクリメントする、を繰り返す。
_[0] = OUT_B;
_[1] = ADD_B(1);
_[2] = JMP(0);
*/

#endif