ステッピングモータを駆動するための パルス発生器が必要となったので、お盆休みを使って簡単な基板を作成してみました。
配線が楽なので I2C接続のLCDモジュールを使用したのですが、思いのほか 処理時間を取られてしまい、パルス幅を設定するエンコーダの応答が イマイチとなってしまいました。
当初、4ch出力を想定していたのですが、同じような処理が増えてしまったので ソースは2ch設定になっています。
●主な部品
No. | 品名 | 型番 | 備考 |
---|---|---|---|
1 | コントローラ | ATmega328P | |
2 | I2C接続LCD | AE-AQM1602A(KIT) | 秋月電子 K-08896 |
3 | ロータリーエンコーダ | EC16B | 秋月電子 |
4 | ダクトSW | ||
5 | デジタルトランジスタ | DTC144WD |
●ソースコード
パルス発生器
#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;
}
投稿者の人気記事
-
marua
さんが
2023/08/16
に
編集
をしました。
(メッセージ: 初版)
ログインしてコメントを投稿する