SSブログ

VVVFのみスケッチ [ds-DCCデコーダ]

 VVVFスケッチからメロディーを抜いて、説明してくださいとの依頼がありました。こちらのスケッチになります。(今回、延々と文字だけが並ぶ話です)
 このスケッチの説明をしますと、
 CV47に2を設定して吊りかけ音は50~440Hzです。
 CV48は無視。
 CV2が起動電圧(0~255)、
 CV3が加速時のスムーズ値(0~255)(大きくするとスムーズになる)
 CV4が減速時のスムーズ値(0~255)(大きくなるとスムーズになる)
 CV5が最大電圧(0~255),
 です。
 ファンクションは
 F0:Offでモーター音On、Onでモーター音Off
 
 です。

・loop関数はステートマシンになっていますが、最初のループがl_pre_dccで、次のループからl_dccです。

・VVVF音が鳴る制御部分はMotor_Control関数で、中身を説明します。
・motor_control関数にはnotifyDccSpeed関数でときどき書き込まれるgSpeedCmdとgPwmDirが入力になります。
・まずLPF部分(スピードをスムーズにする)になります。DCCは128スピードしかありませんから、これをそのまま周波数にしてしまうと全部ドレミファインバーターのカッコ悪いのになってしまいますので、これをスムーズにしています。あとはついでに方向が変わったら急停止になるようにしています。
入力スピードはgSpeedCmd(0~255)で、出力スピード(スムーズになった後)はaPWMRef(0~1023)です。
-------------------
  //LPF routin
  float instruct_speed = (float)(gSpeedCmd * 4);//change from 256 to 1023
  float speed_step = 0;
  if( gPrevSpeed < instruct_speed )
  {
    //Acceleration
    accel_flag = true;
    speed_step = 1024 / (1 + (float)gCV3_AccRatio * 16);//  "* 16  " is because of smooth speed 
    gPrevSpeed += speed_step ;
    if (gPrevSpeed > instruct_speed)
    {
      gPrevSpeed = instruct_speed;
    }
  }
  else if( gPrevSpeed > instruct_speed )
  {
    //Deceleration
    speed_step = 1024 / (1 + (float)gCV4_DecRatio * 16);//  "* 8  " is because of smooth speed 
    gPrevSpeed -= speed_step ;
    if (gPrevSpeed < instruct_speed)
    {
      gPrevSpeed = instruct_speed;
    }
  }
  //when direction is different
  if(preDir != gPwmDir)
  {
    //stop
    gPrevSpeed = 0;
  }
  preDir = gPwmDir;
  aPWMRef = (uint16_t)gPrevSpeed;

-------------------
・次が、はじめの一回(vvvf_first_flag==true)はVVVF行列(kasoku[][])をロードしています。なんでこんなところに持ってきているかというと、Setupに入れたら、頭でっかちになってしまい、Directモード書き込みなどのタイミングに間に合わなかったためです。(l_pre_dccに入れてもよさそうですが)
・次に、スムーズ後のスピードであるaPWMRefとVVVF行列(kasoku[][])からVVVF音の周波数を計算してその時の周期current_period1,currnet_period2を求めます。currnet_period2はモーター音を重厚にしようと思った名残で残っているだけで、今回は使っていません。(またそのうち使うかも)
 そして、モーターの回転やトルクを出すためのduty比であるpwm_dutyを計算します。周波数によってかなりトルクが変わるため、これもVVVF行列[kasoku[][])にいっしょに入れてあります。
 次に、方向情報を付けて最終的にcurrent_duty_a,current_duty_bでDuty比を出してやります。
 なお、随所に「//interruptで渡す」と書いておりますが、やっていません。motor_control関数の最後にTimer1に書き込んでいます。
--------------------
  if(aPWMRef != 0)
  {
    //初期化 一回だけ行う
    if(vvvf_first_flag == true)
    {
      vvvf_first_flag = false;
      //安全策
      if(gCV47_SoundSwitch > 8)
      {
        gCV47_SoundSwitch = 8;
      }
      int kasoku_offset =  (int)pgm_read_word_near(&progmem_ref_kasoku[gCV47_SoundSwitch]);
      uplimit = (int)pgm_read_word_near(&progmem_ref_kasoku[gCV47_SoundSwitch+1]) - kasoku_offset;
      for(int i = 0;i < uplimit;i++)
      {
        for (int j = 0;j < 8;j++)
        {
          kasoku[i][j] =  (int)pgm_read_word_near(&progmem_kasoku [i + kasoku_offset][j]); 
          Serial.print(kasoku[i][j],DEC);
          Serial.print(",");
        }
        Serial.println("");
      }        
    }
    for(int i = 0;i < uplimit;i++)
    {
      if ( (kasoku[i][0] <= aPWMRef ) and ( aPWMRef <= kasoku [i][1]))
      {
        //音色側
        //周波数1
        pwm_freq = (double)kasoku[i][2]
                 + (double)(kasoku[i][3] - kasoku[i][2]) / (double)(kasoku [i][1] - kasoku [i][0])
                 * (double)(aPWMRef - kasoku [i][0]);
        current_period1 = (double)1000000 / pwm_freq;//interruptで渡す
        //Serial.print(pwm_freq,DEC);
        //Serial.print(F(" Hz,"));
        //周波数2
        pwm_freq = (double)kasoku[i][4]
                 + (double)(kasoku[i][5] - kasoku[i][4]) / (double)(kasoku [i][1] - kasoku [i][0])
                 * (double)(aPWMRef - kasoku [i][0]);
        current_period2 = (double)1000000 / pwm_freq;//interruptで渡す
        //Serial.print(pwm_freq,DEC);
        //Serial.print(F(" Hz,"));
        //電力側
        pwm_duty = (double)kasoku[i][6]
          + (double)(kasoku[i][7] - kasoku[i][6]) / (double)(kasoku [i][1] - kasoku [i][0])
          * (double)(aPWMRef - kasoku [i][0]);
        //CV2,CV5で修正
        pwm_duty = (double)gCV2_Vstart * 4 + (double)pwm_duty * (double)(gCV5_Vhigh - gCV2_Vstart) / (double)255;
        if (pwm_duty >1023)
        {
          pwm_duty = 1023;
        }
        //Serial.print(F("Duty:"));
        //Serial.println(pwm_duty,DEC);
        if(gPwmDir > 0)
        {
          //interruptで渡す
          current_duty_b = 0;
          current_duty_a = pwm_duty;
        }
        else
        {
          //interruptで渡す
          current_duty_a = 0;
          current_duty_b = pwm_duty;
        }
      }
    }
  }
  else
  {
    //interruptで渡す
    current_duty_b = 0;
    current_duty_a = 0;
  
  }

--------------------

・その次が(今は残骸のF0によるヘッドライトテールライトルーチンが・・・)
--------------------
  //F0
  if(gPwmDir > 0)
  {
    //F0
    exec_function( 0, FunctionPin_CCW_Light, HIGH );
    exec_function( 0, FunctionPin_CW_Light, !F0_flag );
  }
  else
  {
    //CCW
    exec_function( 0, FunctionPin_CW_Light, HIGH );
    exec_function( 0, FunctionPin_CCW_Light, !F0_flag );
  }

--------------------
・その次に実際にTimer1にPeriodとDutyを書き込んでやっています。この時、F0_Flagが1ならcurrent_periodを20,000Hz(中年には聞こえない高い音)にして、Current_Dutyを適当に1.9倍して、無音走行(ノッチオフ)にするというのもやっています。F0_Flagが0なら、上のほうで求めたPeriodとDutyでモーターを動かします。
 Back EMFにするなら、目標スピードがaPWMRefで実際の指示スピードがpwm_dutyになると思います。
 現在、pwm_dutyはkaosku[][]から求めていますが、BackEMFからうまくいくなら、個々の配列部分はすべて無視で「VVVF音あり」、「音無し」共に行けるかなあと思います。
---------------------
  //実際の命令
  //if(accel_flag == false)
  if(F0_flag)
  {
    current_period1 = (double)1000000 / 20000;
    current_duty_a *= (double)1.9;
    current_duty_b *= (double)1.9;
    if( current_duty_a > 1023)
    {
      current_duty_a = 1023;
    }
    if( current_duty_b > 1023)
    {
      current_duty_b = 1023;
    }
  }
  Timer1.setPeriod(current_period1);
  Timer1.setPwmDuty(MOTOR_PWM_A,current_duty_a);
  Timer1.setPwmDuty(MOTOR_PWM_B,current_duty_b);

---------------------
ところでaccel_flagですが、加速中はVVVF音、定速になったり減速時は無音というのをやるために作った(作り途中の)flagです。


コメント(0)  トラックバック(0) 

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

※ブログオーナーが承認したコメントのみ表示されます。

トラックバック 0