/***** 3相モータ駆動の実験 *****/ // Arduino UNO, ATmega328Pで // タイマー0は横取り delay()は使えない // OC0A PD6 U相 8bit位相基準PWMで // OC0B PD5 V相 16MHz/8/255/2で // OC2B PD3 W相 3.921kHz // PB0,1,2,5 タイミングチェック // ADC0 PC0 VR1 PWM振幅調整 // ADC1 PC1 VR2 回転速度 // タイマー1 100us割り込み // タイトル const char pgm_ttl[] PROGMEM = "3ph_mot2.ino 2023-05-05 JH3DBO"; // マクロ #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 PB0_H (PORTB |= (1 << PB0)) // (!!!)PB0 H/L 14pin #define PB0_L (PORTB &= ~(1 << PB0)) #define PB1_H (PORTB |= (1 << PB1)) // (!!!)PB1 H/L 15pin #define PB1_L (PORTB &= ~(1 << PB1)) #define PB2_H (PORTB |= (1 << PB2)) // (!!!)PB2 H/L 16pin #define PB2_L (PORTB &= ~(1 << PB2)) // 文字出力バッファ char tx_str[64]; // sprintf用 /********************************/ /* 8bit SINテーブル */ /********************************/ //=================================================== #if 0 # gawkを使ってSINデータを作成 # 8bit PWM(0~255)に合わせてして±127で作成。 0~359度 BEGIN{ PAI = atan2(0, -1) # π=3.14 print("/********************************/") print("/* 8bit SINテーブル */") print("/********************************/") print("#include ") print("/***** SIN 0~359 *****/") print("// 振幅±127, 中央値は0") print("const int8_t sin_tbl[] PROGMEM ={") for(i = 0; i < 360; i++){ if((i % 10) == 0){ n = i c = 0 printf(" ") } printf("%4d,", 127 * sin(i * PAI / 180)) c++ if((i % 10) == 9){ printf(" // %d\n", n) n = i c = 0 } } if(c != 0){ while(c < 10){ printf(" ") c++ } printf(" // %d\n", n) } print("};") } #endif //=================================================== /***** SIN 0~359 *****/ // 振幅±127, 中央値は0 const int8_t sin_tbl[] PROGMEM ={ 0, 2, 4, 6, 8, 11, 13, 15, 17, 19, // 0 22, 24, 26, 28, 30, 32, 35, 37, 39, 41, // 10 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, // 20 63, 65, 67, 69, 71, 72, 74, 76, 78, 79, // 30 81, 83, 84, 86, 88, 89, 91, 92, 94, 95, // 40 97, 98, 100, 101, 102, 104, 105, 106, 107, 108, // 50 109, 111, 112, 113, 114, 115, 116, 116, 117, 118, // 60 119, 120, 120, 121, 122, 122, 123, 123, 124, 124, // 70 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, // 80 127, 126, 126, 126, 126, 126, 126, 126, 125, 125, // 90 125, 124, 124, 123, 123, 122, 122, 121, 120, 120, // 100 119, 118, 117, 116, 116, 115, 114, 113, 112, 111, // 110 109, 108, 107, 106, 105, 104, 102, 101, 100, 98, // 120 97, 95, 94, 92, 91, 89, 88, 86, 84, 83, // 130 81, 79, 78, 76, 74, 72, 71, 69, 67, 65, // 140 63, 61, 59, 57, 55, 53, 51, 49, 47, 45, // 150 43, 41, 39, 37, 35, 32, 30, 28, 26, 24, // 160 22, 19, 17, 15, 13, 11, 8, 6, 4, 2, // 170 0, -2, -4, -6, -8, -11, -13, -15, -17, -19, // 180 -22, -24, -26, -28, -30, -32, -35, -37, -39, -41, // 190 -43, -45, -47, -49, -51, -53, -55, -57, -59, -61, // 200 -63, -65, -67, -69, -71, -72, -74, -76, -78, -79, // 210 -81, -83, -84, -86, -88, -89, -91, -92, -94, -95, // 220 -97, -98,-100,-101,-102,-104,-105,-106,-107,-108, // 230 -109,-111,-112,-113,-114,-115,-116,-116,-117,-118, // 240 -119,-120,-120,-121,-122,-122,-123,-123,-124,-124, // 250 -125,-125,-125,-126,-126,-126,-126,-126,-126,-126, // 260 -127,-126,-126,-126,-126,-126,-126,-126,-125,-125, // 270 -125,-124,-124,-123,-123,-122,-122,-121,-120,-120, // 280 -119,-118,-117,-116,-116,-115,-114,-113,-112,-111, // 290 -109,-108,-107,-106,-105,-104,-102,-101,-100, -98, // 300 -97, -95, -94, -92, -91, -89, -88, -86, -84, -83, // 310 -81, -79, -78, -76, -74, -72, -71, -69, -67, -65, // 320 -63, -61, -59, -57, -55, -53, -51, -49, -47, -45, // 330 -43, -41, -39, -37, -35, -32, -30, -28, -26, -24, // 340 -22, -19, -17, -15, -13, -11, -8, -6, -4, -2, // 350 }; /******************************/ /* PWM処理 */ /******************************/ byte f_pwm_on; // PWM出力オン指令 // 電源オン後 2秒経過で制御開始 volatile short rot_deg; // 回転角 0~359度 // 割り込みでカウントアップ volatile word rot_cnt; // 360度の回数を数える // 1秒ごとに処理 /**** 角度からPWMデータに変換 *****/ // 角度 0~359 → 8bit PWMデータ(0~255) // VR1値(8bit)でPWMの振幅を設定 // 中央値は128 byte degpwm(short deg, byte vr1) { short a, g; byte d; while(deg < 0){ // マイナスならdeg+360 deg = deg + 360; // プラスに } // deg = deg % 360; // 0~359の範囲に(割り算やめて) while(deg >= 360){ // 360を越えている場合はdeg-360 deg = deg - 360; } // SIN値に変換 a = (short)(int8_t)pgm_read_byte(&sin_tbl[deg]); // 0~359度→sin g = (a * (short)vr1) / 256; // VR1 : PWM振幅調整 ±127最大 d = (byte)(g + 128); // 8bitの中央値 return d; } /******************************/ /* 内蔵A/D処理 */ /******************************/ /**** A/D データ *****/ // A/D変換チャンネル (ADMUX) const byte ad_mpx[] PROGMEM ={ 0b01000000, // ADC0 VR1 PWM振幅調整 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データ 8bit (タイミングを気にせず自由に読める) volatile byte vr_pwm; // VR1 PWM振幅調整 volatile byte vr_spd; // VR2 回転速度 /***** A/D割り込み処理 *****/ // タイマー割り込みでA/D変換開始 // 128回(2chで256ms)で平均値算出 ISR(ADC_vect) { word d; byte i; static byte ch; // A/D変換チャンネル static byte cnt; // A/D変換平均回数 PB1_H; // (!!!) 15pin d = ADC; // 符号なしで 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; // 次加算データクリア } vr_pwm = ad_avr[0] / 4; // VR1 PWM振幅調整(8bitに) vr_spd = ad_avr[1] / 4; // VR2 回転速度 f_adok = 1; // 変換完了 } } ADMUX = pgm_read_byte(&ad_mpx[ch]); // 次ch PB1_L; // (!!!) 15pin } /********************************/ /* タイマー1割込み */ /********************************/ // タイマーデータ volatile byte tm_100us; // 100usタイマー // ダウンカウント volatile byte f_1sec; // 1秒経過フラグ volatile byte tu_10ms; // 10msアップカウントタイマ // 2.55秒max /***** タイマー1コンペアマッチA割込み *****/ // 100us(10kHz) ISR(TIMER1_COMPA_vect) { static byte t_1ms; // A/D変換タイミング 1msごと static byte t_10ms; // 10msカウント static byte t_1sec; // 1秒カウント PB0_H; // (!!!)PB0 H if(tm_100us) tm_100us--; // 0.1msダウンカウント // A/D変換タイミング t_1ms++; if(t_1ms >= 10){ // 1ms t_1ms = 0; ADCSRA |= (1 << ADSC); // A/D変換開始 // 10ms t_10ms++; if(t_10ms >= 10){ // 10ms t_10ms = 0; if(tu_10ms < 255){ // アップカウント tu_10ms++; } // 1秒カウント t_1sec++; if(t_1sec >= 100){ // 1sec t_1sec = 0; f_1sec = 1; // 1秒経過フラグ } } } // PWM制御 if((tm_100us == 0) && // タイムアップ ? (f_pwm_on != 0)){ // PWM制御オン ? PB2_H; // (!!!)PB2 H tm_100us = vr_spd; // VR2 回転速度 25.5ms OCR0A = degpwm(rot_deg, vr_pwm); // U相 OCR0B = degpwm(rot_deg + 120, vr_pwm); // V相 OCR2B = degpwm(rot_deg + 240, vr_pwm); // W相 rot_deg++; // 0~359度 if(rot_deg >= 360){ // 360度で0に戻す rot_deg = 0; rot_cnt++; // 回数を+1 } PB2_L; // (!!!)PB2 L } PB0_L; // (!!!)PB0 L } /******************************/ /* セットアップ */ /******************************/ /***** セットアップ *****/ // ATmega328Pのレジスタを直接制御 // タイマー0を横取りするのでdelayなどは使えない void setup() { cli(); // 割り込み禁止で // I/Oイニシャル PORTB = 0b00000000; // data/pull up DDRB = 0b00111111; // port指定 // |||||+---- PB0 IO8 out (!!!) // ||||+----- PB1 IO9 out (!!!) // |||+------ PB2 IO10 out (!!!) // ||+------- PB3 IO11 out // |+-------- PB4 IO12 out // +--------- PB5 IO13 out LED PORTC = 0b00000000; // data/pull up DDRC = 0b00111100; // |||||+---- PC0 AD0 in VR1 PWM振幅調整 // ||||+----- PC1 AD1 in VR2 モータ回転速度 // |||+------ PC2 AD2 out // ||+------- PC3 AD3 out // |+-------- PC4 AD4 out // +--------- PC5 AD5 out PORTD = 0b00000011; // data/pull up DDRD = 0b11111110; // |||||||+---- PD0 RXD in // ||||||+----- PD1 TXD out // |||||+------ PD2 IO2 out // ||||+------- PD3 IO3 out OC2B W相 // |||+-------- PD4 IO4 out // ||+--------- PD5 IO5 out OC0B V相 // |+---------- PD6 IO6 out OC0A U相 // +----------- PD7 IO7 out // 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有効 // タイマー0,2 PWM出力 OC0A,OC0B,OC2B TIMSK0 = 0b00000000; // 割込禁止 TIMSK2 = 0b00000000; // ||+--- TOIE // |+---- OCIEA // +----- OCIEB GTCCR = 0b10000011; // | |+--- PSRSYNC タイマ0,1前置分周器リセット // | +---- PSRACY タイマ2前置分周器リセット // +---------- TSM 同期処理 TCNT0 = 0; // タイマーカウント値クリア TCNT2 = 0; OCR0A = 128; // U相 中央値 OCR0B = 128; // V相 (-1せず) OCR2B = 128; // W相 (H=128:L=127) TCCR0A = 0b10100001; // |||| ++--- 8bit位相基準PWM // ||++------- OCR0B 非反転PWM // ++--------- OCR0A 非反転PWM TCCR0B = 0b00000010; // || |++++-- CS 1/8 2MHz →3.92kHz // || +------ WGM02 // ++--------- FOC0 TCCR2A = 0b00100001; // |||| ++--- 8bit位相基準PWM // ||++------- OCR2B 非反転PWM // ++--------- OCR2A Portで TCCR2B = 0b00000010; // || |++++-- CS 1/8 2MHz // || +------ WGM22 // ++--------- FOC2 GTCCR = 0b00000000; // +---------- TSM=0で計数開始 // タイマー1,OCR1Aコンペアマッチで割り込み(100us) TCCR1A = 0b00000000; // |||| ++--- WGM // ||++------- COM1B // ++--------- COM1A TCCR1B = 0b00001010; // || ||+++--- CS 1/8 2MHz 0.5uS // || ++------ WGM CTC OCR1A // |+--------- ICES1 // |---------- ICNC1 OCR1A = 200 - 1; // 割込周期 100us TIMSK1 = 0b00000010; // | ||+--- TOIE1 // | |+---- OCIE1A 割込on // | +----- OCIE1B // +-------- ICIE1 // 割込許可 sei(); // 割り込み有効に // シリアル Serial.begin(9600); // シリアル通信 } /******************************/ /* ループ */ /******************************/ /***** LOOP *****/ void loop() { static byte f_ledx; static word cnt; strcpy_P(tx_str, pgm_ttl); Serial.println(tx_str); // タイトルをシリアル出力 while(1){ // 1秒経過 if(f_1sec){ // 1秒 f_1sec = 0; cli(); // 割り込み禁止 cnt = rot_cnt; // 360度回数 rot_cnt = 0; // 読んだ後ゼロクリア sei(); // 割り込み有効に sprintf(tx_str,"%5d" ,cnt); Serial.println(tx_str); // 回数出力 // LED点滅 f_ledx ^= 1; // 1秒サイクルで if(f_ledx) LED_ON; // LED on/off else LED_OFF; } // 制御開始 if(tu_10ms >= 200){ // 2秒経過 f_pwm_on = 1; // 制御開始 } } } /*==== end of "3ph_mot2.ino" ====*/