/***** ダイソーの観覧車を回す *****/ // ステッピングモータ 28BYJ-48 5V // 巻線を変え、バイポーラ接続に // ドライバ A4988 1/2,1/4,1/8,1/16マイクロステップ可能 // 1/1で2048パルス/回転 // 1/4ステップで8192P/R 1/16で32768P/R // PB1 stepパルス OC1Aで出力 立ち上がり // PB5 LED表示 // AD0 最高速度設定 VR1接続 // AD1 加速時間設定 VR2接続 // AIN1 24V電圧低下検出 // 電源オンで加速しながら回転開始。 // VR1が目標速度。 VR2で加速時間を設定 // 電源断検出で減速停止。 // タイトル const char pgm_ttl[] PROGMEM = "Wheel drive. 2020-05-09"; // マクロ #define DIMSIZ(a) sizeof(a)/sizeof(*a)) // 配列のデータ数を返す #define nop() asm volatile("nop;") // NOPコード // I/Oマクロ #define LED_ON (PORTB |= (1 << PB5)) // PB5 LED #define LED_OFF (PORTB &= ~(1 << PB5)) #define CK_VLO (ACSR & (1 << PORTB5)) // AIN1コンパレータ Low Volt #define OC1A_L (TCCR1A = 0b10000000) // OC1A comp1AでL #define OC1A_H (TCCR1A = 0b11000000) // OC1A comp1AでH #define OC1A_X (TCCR1A = 0b01000000) // OC1A comp1Aでトグル #define OC1A_ON (TCCR1C = 0b10000000) // FOC1A on, OC1A状態をトリガ // テストパルス #define PB0_H (PORTB |= (1 << PB0)) // (!!!)PB0 H/L 14pin #define PB0_L (PORTB &= ~(1 << PB0)) #define PB2_H (PORTB |= (1 << PB2)) // (!!!)PB2 H/L 16pin #define PB2_L (PORTB &= ~(1 << PB2)) #define PB3_H (PORTB |= (1 << PB3)) // (!!!)PB3 H/L 17pin #define PB3_L (PORTB &= ~(1 << PB3)) #define PD5_H (PORTD |= (1 << PD5)) // (!!!)PD5 H/L 13pin #define PD5_L (PORTD &= ~(1 << PD5)) #define PD6_H (PORTD |= (1 << PD6)) // (!!!)PD6 H/L 12pin #define PD6_L (PORTD &= ~(1 << PD6)) // 文字出力バッファ char tx_str[64]; // sprintf用 // パルス出力データ #define FRQ_PLS 1000000L // 基準パルス周波数 (タイマー1) // 1MHz 1us // OC1Aをトグルしてパルス出力するので // 2MHzクロックを1/2して1MHzで計算する #define FRQ_MIN 16 // 最低周波数 20Hz (1MHz/65536=15.3Hz) byte f_plson; // パルス出力有効フラグ // 1でタイマー1割り込み内でパルスを出力 volatile short speed_set; // 速度設定値 単位はHz // この値に向かって加減速 volatile short speed_now; // 現在速度 // speed_setが目標 volatile word tcnt_set; // OCR1A 割り込みサイクル設定値 // 基準は1MHz // 電源電圧低下検出 byte f_VLO; // CK_VLOでチェック // 1で電圧低下 /********************************/ /* 加減速処理用ルーチン */ /********************************/ // 加減速データ #define ACC_VLO 96 // 電圧低下時の減速 1msで12Hz volatile word accel_set; // 加減速設定値 1~64 // VR2値からaccsetで計算 volatile word accel_add; // 1msサイクルでaccel_set(1~64)を加算 // 下位6bitが小数桁で上位が1msあたりの // 周波数(Hz単位)増減値となる /***** 加減速設定値計算 *****/ // 10bit VR値からaccel_set設定値を計算 // 1~64の値をとる word accset(word vr) { word d; d = vr >> 4; // 0~1023を0~63にして return (d + 1); // 1~64でリターン } /***** アクセル処理 *****/ // 1msサイクルでaccel_setデータを加算 // 下位4bitを捨てて上位桁で速度(Hz)を加減速 // 8で1msで1Hz 64で1msで8Hz void accel(void) { word d; accel_add += accel_set; // 加減速設定値 1~64を加算 d = accel_add & 0xFFF8; // 下位3bit値捨てる accel_add -= d; // 加算値から引く d >>= 3; // 上位桁で周波数を増減 if(speed_now != speed_set){ // 目標速度比較 if(speed_now > speed_set){ // 減速 speed_now -= d; if(speed_now < speed_set) speed_now = speed_set; } else{ // 加速 speed_now += d; if(speed_now > speed_set) speed_now = speed_set; } tcnt_set = (uint16_t)(FRQ_PLS / (uint32_t)speed_now); // パルス出力割り込み } } /********************************/ /* タイマー1割込み */ /********************************/ // ステッピングモータ駆動パルスを発生 /***** タイマー1コンペアマッチA割込み *****/ // プリスケーラ後のクロックは2MHz 0.5uS // OCR1Aで設定 OC1Aのトグルでパルス出力 (1MHzで周波数計算) ISR(TIMER1_COMPA_vect) { PB0_H; // (!!!)PB0 H // 次割り込みサイクルをセット OCR1A = tcnt_set - 1; // 割込周期(4us単位) PB0_L; // (!!!)PB0 L } /********************************/ /* タイマー2割込み */ /********************************/ /***** タイマーデータ *****/ volatile byte tm_10ms; // 10msダウンカウントタイマー max 2.55s /***** タイマー2 1kHz割り込み *****/ // 1ms計時処理 // ADCをトリガー ISR(TIMER2_COMPA_vect) { volatile static byte c1 = 0; // 1msカウント PB2_H; // (!!!) 16pin // 10msカウント c1++; // 10msチェック if(c1 >= 10){ c1 = 0; if(tm_10ms) tm_10ms--; // 10msダウンカウントタイマー } // アクセル処理 if(f_plson){ // ステップパルス出力中 accel(); // 出力onならアクセル処理 } // 内蔵A/D開始 ADCSRA |= (1 << ADSC); // A/D変換開始 PB2_L; // (!!!) 16pin } /******************************/ /* 内蔵A/D処理 */ /******************************/ /**** A/D データ *****/ // A/D変換チャンネル (ADMUX) const byte ad_mpx[] PROGMEM ={ 0b01000000, // ADC0 VR1最高速度 0b01000001, // ADC1 VR2加速時間 }; // ||| ++++---- MPX // ||+--------- ADLAR // ++---------- REFS AVCC接続 // A/Dデータ(2ch) volatile word ad_avr[2]; // A/D変換データ 0~1023 // 平均処理された結果 volatile uint32_t ad_add[2]; // A/D平均処理用加算データ volatile byte f_adok; // A/D変換完了フラグ // 割り込みでセット // VRデータ 10bit (タイミングを気にせず自由に読める) word ad_vr[2]; // 10bitのA/Dデータ // VR1とVR2 byte f_advr; // データ確定 // 256ms周期 /***** A/D割り込み処理 *****/ // タイマー割り込みでA/D変換開始 // 128回(2chで256ms)で平均値算出 ISR(ADC_vect) { word d; byte i; static byte ch = 0; // A/D変換チャンネル static byte cnt = 0; // A/D変換平均回数 PB3_H; // (!!!) 17pin d = (word)ADCL; // d |= (ADCH & 0x03) << 8; // 符号なしで 0~3FF // 測定値 ad_add[ch] += (uint32_t)d; // 平均用に加算 // 次変換チャンネル ch++; // ch0,1 if(ch >= 2){ ch = 0; // 0に戻す cnt++; // 平均加算回数 if(cnt >= 128){ // 128回? cnt = 0; for(i = 0; i < 2; i++){ // 2ch loop ad_avr[i] = word(ad_add[i] / 128L); // 平均 ad_add[i] = 0L; // 次加算データクリア } f_adok = 1; // 変換完了 } } ADMUX = pgm_read_byte(&ad_mpx[ch]); // 次ch PB3_L; // (!!!) 17pin } /******************************/ /* セットアップ */ /******************************/ /***** セットアップ *****/ // ATmega328Pのレジスタを直接制御 void setup() { cli(); // 割り込み禁止で speed_now = FRQ_MIN; // 現在周波数 speed_set = FRQ_MIN; // 目標周波数 16Hz tcnt_set = (uint16_t)(FRQ_PLS / (uint32_t)speed_now); // I/Oイニシャル PORTB = 0b00000000; // data/pull up DDRB = 0b00111111; // port指定 // |||||+---- PB0 IO8 out // ||||+----- PB1 IO9 out OC1A stepパルス // |||+------ PB2 IO10 out // ||+------- PB3 IO11 out // |+-------- PB4 IO12 out // +--------- PB5 IO13 out LED PORTC = 0b00000000; // data/pull up DDRC = 0b00111100; // |||||+---- PC0 AD0 in VR1 最大速度 // ||||+----- PC1 AD1 in VR2 加速時間 // |||+------ PC2 AD2 out // ||+------- PC3 AD3 out // |+-------- PC4 AD4 out // +--------- PC5 AD5 out PORTD = 0b00000011; // data/pull up DDRD = 0b01111110; // |||||||+---- PD0 RXD in // ||||||+----- PD1 TXD out // |||||+------ PD2 IO2 out // ||||+------- PD3 IO3 out // |||+-------- PD4 IO4 out // ||+--------- PD5 IO5 out // |+---------- PD6 IO6 out // +----------- PD7 IO7 in AIN1 power down検出 // A/D割り込み DIDR0 = 0b00000011; // AD0,1 デジタル入力禁止 ADMUX = 0b01000000; // 内蔵A/D // ||| ++++---- ch0から // ||+--------- ADLAR // ++---------- REFS AVCC接続 ADCSRA = 0b10001111; // |||||+++--- ADPS 1/128=125kHz // ||||+------ ADIE 割り込み有効 // |||+------- ADIF // ||+-------- ADATE // |+--------- ADSC // +---------- ADEN A/D有効 // アナログコンパレータ DIDR1 = 0b00000010; // AIN1 デジタル入力禁止 ACSR = 0b01000000; // ||||||++---- ACIS // |||||+------ ACIC // ||||+------- ACIE // |||+-------- ACI // ||+--------- ACO 比較出力 Low Voltで1 // |+---------- ACBG 内蔵1.1Vに // +----------- ACD コンパレータ使用 // タイマー1,OCR1Aコンペアマッチ TCCR1A = 0b00000000; // |||| ++--- WGM // ||++------- COM1B // ++--------- COM1A stepパルス TCCR1B = 0b00001010; // || ||+++--- CS 1/8 2MHz 0.5uS // || ++------ WGM CTC OCR1A // |+--------- ICES1 // |---------- ICNC1 OCR1A = tcnt_set - 1; // 割込周期 TIMSK1 = 0b00000010; // | ||+--- TOIE1 // | |+---- OCIE1A 割込on // | +----- OCIE1B // +-------- ICIE1 // タイマー2 1mSタイマー割り込み TCCR2A = 0b00000010; // |||| ++--- CTC動作 // ||++------- OCR2B // ++--------- OCR2A TCCR2B = 0b00000100; // || |++++-- CS 1/64 250kHz // || +------ WGM22 // ++--------- FOC2 OCR2A = 250 - 1; // 1kHz TIMSK2 = 0b00000010; // ||+--- TOIE2 // |+---- OCIE2A 割込on // +----- OCIE2B sei(); // 割り込み有効に // シリアル Serial.begin(9600); // シリアル通信 } /******************************/ /* ループ */ /******************************/ /***** LOOP *****/ void loop() { word d1, d2, d3; byte f_xLED = 0; // LED点滅用 byte exc = 0; // 実行区分 while(1){ f_VLO = CK_VLO ? 1 : 0; // 電圧低下チェック if(f_VLO){ // 電圧低下 cli(); // いったん割込禁止に speed_set = FRQ_MIN; // 目標最低周波数に accel_set = ACC_VLO; // 電圧低下時の減速 sei(); } // A/Dデータ if(f_adok){ // A/D確定 f_adok = 0; // VR1,VR2データをコピー cli(); // 割込禁止にしてwordデータ転送 ad_vr[0] = ad_avr[0]; // VR1最高速度 ad_vr[1] = ad_avr[1]; // VR2加速時間 sei(); // 割込有効に戻す f_advr = 1; // VRデータ確定(256ms周期) } // VR値設定 if(f_advr){ // VRデータ確定(256ms周期) f_advr = 0; PD6_H; // (!!!) 12pin if(!f_VLO){ // 電源電圧okなら加速処理 d1 = (4 * ad_vr[0]) + FRQ_MIN; // 目標周波数 d2 = accset(ad_vr[1]); // 加速データ VR2 A/D値 cli(); // いったん割込禁止に speed_set = d1; // 目標周波数 accel_set = d2; // 加速データ sei(); } cli(); // 割込禁止で d1 = speed_now; // 現在周波数 d2 = speed_set; // 目標周波数 d3 = accel_set; // 加速データ sei(); sprintf_P(tx_str, PSTR("now:%5d set:%5d acc:%2d"), d1, d2, d3); Serial.println(tx_str); // 状態をシリアル出力 f_xLED ^= 1; // LED点滅 if(f_xLED) LED_ON; else LED_OFF; PD6_L; // (!!!) 12pin } // 実行区分 switch(exc){ case 0: // タイトル出力 strcpy_P(tx_str, pgm_ttl); Serial.println(tx_str); // タイトルをシリアル出力 tm_10ms = 100; // 1.0秒待ち exc++; break; case 1: // VR読み込み安定待ち if(tm_10ms == 0){ // タイムアップで f_plson = 1; // パルス出力開始 OC1A_X; // ステップパルス出力開始 exc++; } break; case 2: // 加減速処理 cli(); // 割込禁止で d1 = speed_now; // 現在周波数 d2 = speed_set; // 目標周波数 sei(); if((f_VLO) && // 電圧低下検出 (d2 == FRQ_MIN) && // 最低周波数指定になっている (d1 == d2)){ // 最低周波数に到達 f_plson = 0; // パルス出力禁止に OC1A_L; // ステップパルス出力 Lに exc++; // 電圧回復待ちに } break; case 3: // 電圧回復待ち if(f_VLO == 0){ // 電圧ok tm_10ms = 100; // 1.0秒待ち exc = 1; // 時間待ちへ } break; } } } /*==== end of "wheel1.c" ====*/