marua が 2023年08月16日14時28分37秒 に編集
初版
タイトルの変更
パルス発生器
タグの変更
ArduinoIDE
ATmega328
メイン画像の変更
記事種類の変更
製作品
ライセンスの変更
(CC BY-SA 4+) Creative Commons Attribution-ShareAlike CC BY-SA version 4.0 or later
本文の変更
ステッピングモータを駆動するための パルス発生器が必要となったので、お盆休みを使って簡単な基板を作成してみました。 配線が楽なので I2C接続のLCDモジュールを使用したのですが、思いのほか 処理時間を取られてしまい、パルス幅を設定するエンコーダの応答が イマイチとなってしまいました。 ![メインルーチンの巡回時間](https://camo.elchika.com/7b98a5010f17eda7d39c41a1c0ffe5454d7e594c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33633934613036612d653465342d343838392d383133342d3434636466393330333066352f39373933303663332d343033642d346262372d616533332d356435363930633737396332/) 当初、4ch出力を想定していたのですが、同じような処理が増えてしまったので ソースは2ch設定になっています。 ●主な部品 | No. | 品名 | 型番 | 備考 | |:---:|:---|:---|:---| | 1 | コントローラ | ATmega328P | | | 2 | I2C接続LCD | AE-AQM1602A(KIT) | 秋月電子 K-08896 | | 3 | ロータリーエンコーダ | EC16B | 秋月電子 | | 4 | ダクトSW | | | | 5 | デジタルトランジスタ | DTC144WD | | ●I/O一覧 ![キャプションを入力できます](https://camo.elchika.com/c499ff397822006345f309f3bd2ba2fd210c1dcb/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f33633934613036612d653465342d343838392d383133342d3434636466393330333066352f66373335326566362d373030652d343062322d623066332d623034356461626435663938/) ●ソースコード ```arduino:パルス発生器 #include <Wire.h> #define LCD_ADRS 0x3E #define dLED 0x20 // PORTB #define dEnable 0x80 // (1xxx xxxx):イネーブル // ■ main() unsigned long main_tim0 = 0; // メインルーチン 計測基準時間 unsigned long main_tim; // メインルーチン 巡回時間(最終) unsigned long main_tim_max = 0; // メインルーチン 巡回時間(Max) unsigned long main_tim_min = 0xffffffff; // メインルーチン 巡回時間(min) char fMain = 0x60; // メインルーチン制御 フラグ #define dPls_EN 0x80 // (1xxx xxxx): パルス出力のON/OFF #define dLCD_EN 0x40 // (x1xx xxxx): LCD処理のON/OFF #define dLog_EN 0x20 // (xx1x xxxx): Log処理のON/OFF // ■ LCD_ctrl() const int dLCD_col = 16; // LCDパネル1行の文字数 // 111111 //0123456789012345 char vram1[dLCD_col+1] = "Pulse Generator "; // 1行目の文字データ char vram2[dLCD_col+1] = " v0.01 "; // 2行目の文字データ char LCD_task = 0; // LCD タスクNo. int LCD_col = 0; // LCD LCD 書込み桁 unsigned long LCD_tim0 = 0; // LCD 基準時間 // 文字列 const char str_Pwidth12[dLCD_col+1] = "1: 2: "; // パルス幅 P1,P2 const char str_Pwidth34[dLCD_col+1] = "3: 4: "; // パルス幅 P1,P2 // ■ cmd1() char cmd1_task = 0; // cmd1 タスクNo. unsigned long cmd1_tim0 = 0; // コマンド1 基準時間 unsigned long tim_cnt = 0; // タイム・カウンタ unsigned char sec = 0; // 秒カウンタ word P1_width,P2_width,P3_width,P4_width; // パルス幅 char item_num = 0; // アイテムNo. char fPulse = 0; // パルス出力 ON/OFFフラグ // ■ LED_ctrl() char LED_task = 0; // LED制御 タスクNo. unsigned long LED_tim0 = 0; // LED制御 基準時間 // ■ ログ制御 char log_task = 0; // ログ タスクNo. unsigned long log_tim0 = 0; // ログ 基準時間 // ■ センサ読取り char sens_task = 0; // タスクNo. unsigned long sens_tim0 = 0; // 基準時間 char sensor[4]; // センサ・データ[確定,今回,前回,前々回] // ■ エンコーダ読取り char enc_task = 0; // タスクNo. unsigned long enc_tim0 = 0; // 基準時間 char enc_bak; // 前回のsensor[0]データ char fEnc; // エンコーダ・フラグ int enc_cnt; // エンコーダ・カウンタ // ■ キー読取り char key_task = 0; // タスクNo. unsigned long key_tim0 = 0; // 基準時間 char keyd; // キー・データ byte key_bak; // 前回のキー・データ(Port C) // ■ パルス出力 #define pls_led 0x08; // (xxxx 1xxx):PORTD char pls1_task = 0; // タスクNo. unsigned long pls1_tim0 = 0; // 基準時間 #define pls1_out 0x10; // (xxx1 xxxx):PORTD char pls2_task = 0; // タスクNo. unsigned long pls2_tim0 = 0; // 基準時間 #define pls2_out 0x20; // (xx1x xxxx):PORTD char pls3_task = 0; // タスクNo. unsigned long pls3_tim0 = 0; // 基準時間 #define pls3_out 0x40; // (x1xx xxxx):PORTD char pls4_task = 0; // タスクNo. unsigned long pls4_tim0 = 0; // 基準時間 #define pls4_out 0x80; // (1xxx xxxx):PORTD void setup() { // put your setup code here, to run once: Serial.begin(9600); Wire.begin(); init_LCD(); // I2C LCD 初期化 pinMode(13,OUTPUT); // PB5(SCK) 出力 pinMode(3,OUTPUT); // PD3 出力(パルスLED) pinMode(4,OUTPUT); // PD4 出力(パルス出力1) pinMode(5,OUTPUT); // PD5 出力(パルス出力2) pinMode(6,OUTPUT); // PD6 出力(パルス出力3) pinMode(7,OUTPUT); // PD7 出力(パルス出力4) P1_width = 50; P2_width = 100; P3_width = 200; P4_width = 400; sensor[1] = PINC; // 今 回データ <= Port C sensor[2] = PINC; // 前 回データ <= Port C sensor[3] = PINC; // 前々回データ <= Port C enc_bak = PINC & 0x0C; // (0000 xx00):前回のsensor[0]データ Serial.println("Pulse v0.01"); } void loop() { // put your main code here, to run repeatedly: get_Sens(); // センサ読取り(ノイズ除去) Enc_cnt(); // エンコーダ読取り get_Key(); // キー読取り LCD_ctrl(); // LCD 表示制御 cmd1(); // コマンド1処理 pls_ctrl1(); // パルス出力制御1 pls_ctrl2(); // パルス出力制御2 LED_cmp(); // } // ■■ void pls_ctrl1(){ // パルス出力制御1 if ( (fMain & dPls_EN) == 0) { return; } // 動作禁止 switch (pls1_task) { case 0: // パルス"H"、 基準時間設定 pls1_tim0 = micros(); PORTD |= pls1_out; // pls1_task ++; break; case 1: // 時間待ち case 3: if ( (micros() - pls1_tim0) > (( (long)P1_width *1000) >> 1) ) { pls1_task ++; } break; case 2: // パルス"L"、 基準時間設定 pls1_tim0 = micros(); PORTD &= ~pls1_out; pls1_task ++; break; // case 3: // 時間待ち ↑ default: pls1_task = 0; break; } } void pls_ctrl2(){ // パルス出力制御2 if ( (fMain & dPls_EN) == 0) { return; } // 動作禁止 switch (pls2_task) { case 0: // パルス"H"、 基準時間設定 pls2_tim0 = micros(); PORTD |= pls2_out; // pls2_task ++; break; case 1: // 時間待ち case 3: if ( (micros() - pls2_tim0) > (( (long)P2_width *1000) >> 1) ) { pls2_task ++; } break; case 2: // パルス"L"、 基準時間設定 pls2_tim0 = micros(); PORTD &= ~pls2_out; pls2_task ++; break; // case 3: // 時間待ち ↑ default: pls2_task = 0; break; } } // ■■ void cmd1() { int val,pw; unsigned long tim; switch (cmd1_task) { // □□ 起動処理 case 0: case 10: case 20: case 23: cmd1_tim0 = millis(); // 基準時間セット cmd1_task ++; break; case 1: // 1s 待ち(起動画面表示) tim = abs(millis() - cmd1_tim0); if ( (millis() - cmd1_tim0) > 1000 ) { // 1s 待ち cmd1_task ++; } break; case 2: cmd1_task =10; break; // □□ モード1: パルス幅 // case 10: // 基準時間セット ↑ case 11: // 1s 待ち if ( (millis() - cmd1_tim0) > 300 ) { cmd1_task ++; } // 300ms 待ち switch(keyd) { case 1: // 選択キー keyd = 0; cmd1_task = 20; break; case 2: // スタート・キー keyd = 0; fMain ^= dPls_EN; // パルス出力 ON/OFF if ( (fMain & dPls_EN) == 0) { // パルス出力 OFF fMain |= (dLCD_EN | dLog_EN); // LCD制御 EN PORTD &= ~pls_led; // モニタLED OFF } else { // パルス出力 ON fMain &= (~dLCD_EN & ~dLog_EN); // LCD制御 DIS PORTD |= pls_led; // モニタLED ON } break; default: keyd = 0; break; } break; case 12: // vram[]に表示データ書込み disp_Pwidth(); cmd1_task ++; break; case 13: cmd1_task = 10; // 1s毎に 表示を更新 break; // □□ モード切り替え処理 // case 20: // 基準時間セット ↑ case 21: // 選択キー 長押しチェック if ( (sensor[0] & 0x01) != 0 ) { // (xxxx xxx1):選択キー OFF item_num = 0; // アイテムNo.0: パルス出力1 cmd1_task ++; } else if ( (millis() - cmd1_tim0) > 3000 ) { cmd1_task = 30; } // 3s 待ち break; // □ パルス長入力 case 22: pw = get_Pwidth(item_num); //(pw) = 出力パルス幅 enc_cnt = pw; // (enc_cnt) <= (P1_width) disp_PWin(pw); // 入力画面を表示 cmd1_task ++; break; // case 23: // 基準時間セット ↑ case 24: // キー・チェック switch(keyd) { case 1: // 選択キーで アイテム変更 keyd = 0; set_Pwidth(item_num, enc_cnt); // パルス幅 更新 if (item_num < 1) { item_num ++; } else { item_num = 0;} cmd1_task = 22; // 表示切替え break; case 2: // スタート・キーで モード1に戻る keyd = 0; set_Pwidth(item_num, enc_cnt); // パルス幅 更新 cmd1_task = 10; break; } if ( (millis() - cmd1_tim0) > 300 ) { // 300ms 待ち disp_PWin(enc_cnt); // 入力画面 cmd1_task = 23; // キーチェックへ } break; // □□ モード2: case 30: default: cmd1_task = 10; // モード1へ break; } } // □ サブルーチン // int get_Pwidth(char item) // 出力パルス幅 取得 // void set_Pwidth(char item, int width) // パルス幅 更新 void set_Pwidth(char item, int width) { // パルス幅 更新 switch (item) { case 0: // item=0 P1_width = width; break; case 1: // item=1 P2_width = width; break; case 2: // item=2 P3_width = width; break; case 3: // item=3 P4_width = width; break; } } int get_Pwidth(char item) { switch (item) { // case 0: // item=0 ↓ case 1: // item=1 return(P2_width); break; case 2: // item=2 return(P3_width); break; case 3: // item=3 return(P4_width); break; case 0: default: return(P1_width); break; } } // ■ キー読取り void get_Key() { char a,b; switch (key_task) { case 0: key_tim0 = micros(); // 基準時間セット key_task ++; break; case 1: if ( (micros() - key_tim0) > 1000 ) { // 1ms間隔 a = sensor[0] & 0x03; // (0000 00xx) b = key_bak; // 前回の値 key_bak = a; // key_bak更新 a = (b ^ key_bak) & b; // 押されたbitが"1" switch(a) { case 0: break; case 1: // スタート・キー押し keyd = 1; break; case 2: // 選択キー押し keyd = 2; break; default: break; } key_task ++; } break; default: key_task = 0; break; } } // ■ エンコーダ読取り void Enc_cnt() { unsigned long tim; char a,b; switch (sens_task) { case 0: enc_tim0 = micros(); // 基準時間セット enc_task ++; break; case 1: if ( (micros() - enc_tim0) > 200 ) { // 0.2ms間隔 a = sensor[0] & 0x0C; // (0000 xx00) b = a ^ enc_bak; // 変化したbitが"1" enc_bak = a; // enk_bak更新 if ( (b & a & 0x04) != 0) { // (xxxx x1xx):A相 立上りで"1" // A相 立上り if ( (a & 0x08) == 0) { // (xxxx 1xxx):B相 // □ 正転 fEnc |= 0x80; // (1xxx xxxx) enc_cnt ++; } else { // □ 逆転 fEnc |= 0x40; // (x1xx xxxx) enc_cnt --; } } enc_task ++; } break; default: enc_task = 0; break; } } // ■ センサ読取り(ノイズ除去) void get_Sens() { switch (sens_task) { case 0: sens_tim0 = micros(); // 基準時間セット sens_task ++; break; case 1: unsigned long tim; char a,b; if ( (micros() - sens_tim0) > 200 ) { // 0.2ms間隔 sensor[3] = sensor[2]; // センサ・データ[確定,今回,前回,前々回] sensor[2] = sensor[1]; // sensor[1] = PINC; // Port C a = sensor[3] | sensor[2] | sensor[1]; // 全て"0"のbitが"0" b = sensor[3] & sensor[2] & sensor[1]; // 全て"1"のbitが"1" sensor[0] &= a; sensor[0] |= b; sens_task ++; } break; default: sens_task = 0; break; } } // ■ LCD制御 void LCD_ctrl() { if ( (fMain & dLCD_EN) == 0) { return; } // LCD処理 禁止 switch (LCD_task) { case 0: writeCommand(0x80+0x00); // ADRESS SET (1行目の先頭) LCD_col = 0; LCD_task ++; break; case 1: LCD_tim0 = millis(); // 基準時間セット LCD_task ++; break; case 2: if ( (millis() - LCD_tim0) > 9 ) { writeData(vram1[LCD_col]); LCD_col ++; if (LCD_col >15) { LCD_task ++; } } break; case 3: writeCommand(0x80+0x40); // ADRESS SET (2行目の先頭) LCD_col = 0; LCD_task ++; break; case 4: LCD_tim0 = millis(); // 基準時間セット LCD_task ++; break; case 5: if ( (millis() - LCD_tim0) > 9 ) { writeData(vram2[LCD_col]); LCD_col ++; if (LCD_col >15) { LCD_task ++; } } break; default: LCD_task = 0; break; } } void set_VRAM2(int col, int dat) { for (int num=4; num>=0; num--) { vram2[col+num] = (dat % 10) + 0x30; dat /= 10; } } //データ書き込み void writeData(byte t_data) { Wire.beginTransmission(LCD_ADRS); Wire.write(0x40); Wire.write(t_data); Wire.endTransmission(); delay(1); } //コマンド書き込み void writeCommand(byte t_command) { Wire.beginTransmission(LCD_ADRS); Wire.write(0x00); Wire.write(t_command); Wire.endTransmission(); delay(10); } //液晶初期化 void init_LCD() { delay(100); writeCommand(0x38); // (001 1 1000):FUNCTION SET(8bit, 2行, Normal Height, 0, IS=0) delay(20); writeCommand(0x39); // (001 1 1001):IS=1 delay(20); writeCommand(0x14); // (0001 0100):INT OSC FREQUENCY(1/5 bias, freq="100") delay(20); writeCommand(0x7A); // (0111 1010):CONTRAST SET 0,1,2,3(contrast L="1010") fDC5V用 // writeCommand(0x7E); // (0111 1110):CONTRAST SET 0,1,2,3(contrast L="1110") fDC4V用 delay(20); writeCommand(0x54); // (0101 0100):CONTRAST SET 4,5(icon OFF, booster ON, contrast H="00") delay(20); writeCommand(0x6C); // (0110 1100):F0LLOWER CONTROL delay(20); writeCommand(0x38); // (001 1 1000):IS=0 delay(20); writeCommand(0x0C); // (00001 100):Display ON, cursor OFF, position OFF delay(20); writeCommand(0x01); // (0000 0001 ):Clear Display delay(20); writeCommand(0x06); // (000001 10 ):Entry Mode(cursor INC, shift OFF) delay(20); } void disp_Pwidth() { // 出力1~4のパルス幅/ SW状態/ (enc_cnt:8bit) 表示 int i,j; char a; if ( (fMain & dLCD_EN) == 0) { return; } // LCD処理 禁止 for (i=0; i<dLCD_col; i++) { vram1[i] = ' '; } // VRAM1 クリア for (i=0; i<dLCD_col; i++) { vram2[i] = ' '; } // VRAM2 クリア // vram1[]に出力1のパルス幅表示 vram1[0] = '1'; vram1[1] = ':'; i = P1_width; if (i > 999) { vram1[4] = '-'; vram1[3] = '-'; vram1[2] = '-'; } else { j = i%10; i = i/10; vram1[4] = int2ASC(j); j = i%10; i = i/10; vram1[3] = int2ASC(j); j = i%10; vram1[2] = int2ASC(j); } // vram1[]に出力2のパルス幅表示 vram1[7] = '2'; vram1[8] = ':'; i = P2_width; if (i > 999) { vram1[11] = '-'; vram1[10] = '-'; vram1[9] = '-'; } else { j = i%10; i = i/10; vram1[11] = int2ASC(j); j = i%10; i = i/10; vram1[10] = int2ASC(j); j = i%10; vram1[9] = int2ASC(j); } // センサ(Port C)状態表示 vram1[14] = int2ASC(sensor[0]/16); // 上位8bit vram1[15] = int2ASC(sensor[0]%16); // 下位8bit } void disp_PWin(int dat) { // パルス幅 入力画面 int i,j; char a; if ( (fMain & dLCD_EN) == 0) { return; } // LCD処理 禁止 for (i=0; i<dLCD_col; i++) { vram1[i] = ' '; } // VRAM1 クリア for (i=0; i<dLCD_col; i++) { vram2[i] = ' '; } // VRAM2 クリア vram1[0] = 'P'; vram1[1] = 'u'; vram1[2] = 'l'; vram1[3] = 's'; vram1[4] = 'e'; vram1[6] = 'W'; vram1[7] = 'i'; vram1[8] = 'd'; vram1[9] = 't'; vram1[10] = 'h'; vram2[2] = int2ASC(item_num+1); // アイテムNo. vram2[3] = ':'; i = dat; j = i%10; i = i/10; vram2[7] = int2ASC(j); j = i%10; i = i/10; vram2[6] = int2ASC(j); j = i%10; vram2[5] = int2ASC(j); } char int2ASC(int dat) { dat &= 0x0f; // (0000 xxxx) if (dat < 10) { return(dat+0x30); } // 0~9 return(dat+0x37); // A~F } // ■ LED制御 void LED_cmp(void) { PORTB ^= dLED; } void LED_on(void) { PORTB |= dLED; } void LED_off(void) { PORTB &= ~dLED; } ```