2021年7月8日木曜日

安価なステッピングモータで遊ぶ(その4、動画とスケッチ)

安価なステッピングモータで遊ぶ(その3)の動画とスケッチです。
    

適当に書いたスケッチです。正しくないかも知れません。無保証です。

Aruduino nanoとステッピングモータドライバ、タクトスイッチ、小型LCDの接続はスケッチの先頭に#defineしています。スイッチの反対側のピンはGNDです。

時計の秒針のように精確な1rpm(回転/60秒)を実現するために、Timer1割り込みでパルスを作って回転/5秒(12rpm)を基準にしています。1回転4秒でも回りましたが、5秒の倍数にしたほうがrpmを細かく分けられたのでこうしました。
//------------------------------------------------------------------
//                                                     21.07.07 naka
// ステッピングモータのテスト
//   P1-P4: ステッピングモータのドライバの各ピン
//   SW1  : タクトスイッチ(アクティブLow) 回転方向切り替え
//   SW2  : タクトスイッチ(アクティブLow) 回転角度切り替え(+90度)0度は連続回転
//   SW3  : タクトスイッチ(アクティブLow) 回転RPM切り替え(1~12)
//   SW4  : タクトスイッチ(アクティブLow) Start/Stop
//   SDAPIN,SLCPIN  : 小型液晶ディスプレイのI2C
//
//   Timer1割り込みで5秒/回転(2,048Step)するパルス409.6Hzを生成
//------------------------------------------------------------------
#define P1  2
#define P2  3
#define P3  4
#define P4  5
#define SW1 6
#define SW2 7
#define SW3 8
#define SW4 9
#define SDAPIN  A4
#define SCLPIN  A5
#include <FaBoLCDmini_AQM0802A.h>
FaBoLCDmini_AQM0802A lcd;

#define steps_per_rotation 2048
volatile int timer1_flag;
volatile int ph_cnt,rpm_cnt,deg_cnt;
int          rpm_i,rpm_mag,dir;
long         deg,deg_step;
int          run_flag;
int rpm_tbl[6][2] = {1,12,  // 1rpm, 5秒を12倍
                     2,6,   // 2rpm
                     3,4,   // 3rpm
                     4,3,   // 4rpm
                     6,2,   // 6rpm
                     12,1}; //12rpm, 1: 5秒/回転(これが基準)
void setup() {
  pinMode(P1, OUTPUT);
  pinMode(P2, OUTPUT);
  pinMode(P3, OUTPUT);
  pinMode(P4, OUTPUT);
  pinMode(SW1, INPUT_PULLUP);
  pinMode(SW2, INPUT_PULLUP);
  pinMode(SW3, INPUT_PULLUP);
  pinMode(SW4, INPUT_PULLUP);

  motor_off();
  ph_cnt = 0;

  // Timer1設定
  TCCR1A = 0;
  TCCR1B = 0;
  // 5秒/回転  2048/5 = 409.6Hz
  // 1/409.6 = 0.00244140625 sec
  // 0.00244140625*16MHz = 39,062.5 (0.5は割り込み交互に吸収)
  TCNT1 = 26474;         // offset 65536-39062 = 26474  と
                         //        65536-39063 = 26473  を交互
  TCCR1B |= (1 << CS10); // プリスケーラ 1

  // モニタLCD設定
  SetupLCD();
  dispLCD_start();
  
  rpm_cnt = 0;
  rpm_i   = 0;
  rpm_mag = rpm_tbl[rpm_i][1]; // 基準5秒/回転の倍数
  dir     = 0;  // 右回転
  deg     = 0;  // 90度
  deg_step = steps_per_rotation * deg / 360;  // ステップ数
  deg_cnt = 0;

  dispLCD();
  run_flag = 0;
  TIMSK1 |= (1 << TOIE1); // Timer1イネーブル
}

byte ph1[4][4] = { // 1相励磁
  1,0,0,0,
  0,1,0,0,
  0,0,1,0,
  0,0,0,1
};

ISR(TIMER1_OVF_vect) {
  if (timer1_flag==1)
    TCNT1 = 26474;
  else
    TCNT1 = 26473;
  timer1_flag ^= 1;

  rpm_cnt+=1;
  if(run_flag==1 && rpm_cnt==rpm_mag) {
    if (deg_cnt<deg_step || deg_step==0) {
      digitalWrite(P1,ph1[ph_cnt][0]);
      digitalWrite(P2,ph1[ph_cnt][1]);
      digitalWrite(P3,ph1[ph_cnt][2]);
      digitalWrite(P4,ph1[ph_cnt][3]);
      if (dir==0)
        ph_cnt = (ph_cnt+1)%4;
      else {
        ph_cnt -= 1;
        if(ph_cnt<0) ph_cnt = 3;
      }
      rpm_cnt = 0;
      deg_cnt++;
    }
    else if (deg_cnt == deg_step) { // 規定数回転したのでstop
      run_flag = 0;
      motor_off();
    }
  }
}

void loop() {
  checkSW(); // タクトスイッチ状態確認
}

void checkSW() {
  if (digitalRead(SW1)==LOW) { // direction
    dir ^= 1;  // 回転方向逆転
    dispLCD();
    while(digitalRead(SW1)==LOW);
  }
  else if (digitalRead(SW2)==LOW) { // rpm
    rpm_i   = (rpm_i+1)%6;
    rpm_mag = rpm_tbl[rpm_i][1];
    rpm_cnt = 0;
    dispLCD();
    while(digitalRead(SW2)==LOW);
  }
  else if (digitalRead(SW3)==LOW) { // degree
    deg += 45;
    if (deg>360) deg = 0;
    deg_step = steps_per_rotation * deg / 360;
    deg_cnt = 0;
    dispLCD();
    while(digitalRead(SW3)==LOW);
  }
  else if (digitalRead(SW4)==LOW) { // Start/Stop
    run_flag ^= 1;
    if (run_flag==0) motor_off();
    deg_cnt = 0;
    rpm_cnt = 0;
    while(digitalRead(SW4)==LOW);
  }
  delay(10); // チャタリング回避
}

void motor_off() {
  digitalWrite(P1,LOW);
  digitalWrite(P2,LOW);
  digitalWrite(P3,LOW);
  digitalWrite(P4,LOW);
}

void dispLCD_start() {
  lcd.clear();
  lcd.print("STEPPING");
  lcd.setCursor(0, 1); // Col,Raw
  lcd.print("   MOTOR");
  delay(1000);
  dispLCD();
}

void dispLCD() {
  char buff[10];
  char dir_txt[2];

  if (dir) strcpy(dir_txt,"L");
  else     strcpy(dir_txt,"R");

  lcd.clear();
  lcd.setCursor(0, 0); // Col,Raw
  sprintf(buff,"%s %3dDEG",dir_txt,deg);
  lcd.print(buff);
  lcd.setCursor(0, 1); // Col,Raw
  sprintf(buff,"   %2dRPM",rpm_tbl[rpm_i][0]);
  lcd.print(buff);
}

void SetupLCD() {
  lcd.begin();
  lcd.command(0x38);
  lcd.command(0x39);
  lcd.command(0x14);
  lcd.command(0x73);
  lcd.command(0x51);  // 5V
//  lcd.command(0x56);  // 3.3V
  delay(2);
  lcd.command(0x6c);
  delay(300);
  lcd.command(0x38);
  delay(1);
  lcd.command(0x01);
  delay(2);
  lcd.command(0x0c);
  delay(2);
}
//------------------------------------------------------------------
// EOF
//------------------------------------------------------------------

0 件のコメント:

コメントを投稿