7/29/2011

Cortex-M0ボードでリアルタイム実数演算






Cortex-M0ボード"MARY"で実数演算を使ったリアルタイム制御がどの程度できるのかを確認していきます.
まずやってみたのはこれです.

【サンプル1: 浮動小数点演算で正弦波出力】
#ifdef __USE_CMSIS
#include "LPC11xx.h"
#endif

#include "pwm.h"
#include "math.h"
#define omega (2 * 3.141592 / 10.0)
int sample_out = 0; 
uint32_t msec;
float u;

int main(void)
{
  sample_out = 0;
  GPIOSetDir(1, 8, 1);          // PIO1_8 : OUTPUT

  Init_PWM();

  // Set TIMER32B0 as Free-Running milliseconds Timer
  LPC_SYSCON->SYSAHBCLKCTRL |= (1<<9);    // Timer32B0 Clock ON (48MHz)
  LPC_TMR32B0->PR  = 48000;    // Pre-scaler : 48MHz/48000 → 1msecで1カウント
  LPC_TMR32B0->TCR = 1;        // Timer Start

  while(1)
    {
      GPIOSetValue(1, 8, sample_out); 

      msec = LPC_TMR32B0->TC; 

      u = sin(omega * msec);   /* -1 〜 1 */
      PWM_Set_Duty(0, (uint16_t)((u+1)*500) ) ; /* 0 〜 1000 */
      sample_out = 1 - sample_out;
    }
    return 0; 
}
このプログラムは32ビットタイマとしてTMR32B0とTMR32B1の二つを使用します.TMR32B0は1kHzのフリーランニングタイマとして用います.カウント値(TMR32B0TC)は現在時刻[msec]を表し,これを変数msecに読み込みます.一方,TMR32B1はPWM信号の生成に用います.ここではCQ.zipのサンプルにあるpwm.cを利用していますので,デフォルトで搬送波周期が約20usec,0〜999の整数でデューティー比を設定できます.これらを用い,msec を浮動小数点演算ライブラリ math.h の sin 関数に与えることにより,100Hz(周期10msec)の正弦波をPWM出力します.

下図のように,PWMの出力(CN1の8番,青)とサンプル周期テスト出力(CN3の1番,黄色)をオシロで測ってみましょう.


結果は以下のとおり.
黄色がサンプル周期出力,青がPWM出力です.少し見づらいですが,サンプル周期は420usecくらい(計算負荷によって変動します).PWMの方はデューティー比を周期的に変化させているので疎密波のようにみえます.これを単純なRC回路(R=33kΩ,C=0.1μF)で平滑化してみてやると,
のように,確かに周期10msecの正弦波が出ていることがわかります.

以前に調べた限界のサンプル周期は2usecほどでしたので,sin一つをいれただけでずいぶん時間がかかるようになったのがわかります.また,コードサイズは 21KB ほどで,メモリが32KBしかないことを考えるとこれもかなりの大きさです.

【サンプル2: 固定小数点演算で正弦波出力】
CQ.zipで配布されているfixedpoint.hを使ってsinの部分を固定小数点演算に書き換えてみます.
#ifdef __USE_CMSIS
#include "LPC11xx.h"
#endif

#include "pwm.h"
#include "fixedpoint.h"
int sample_out = 0; 
uint32_t msec;

int main(void)
{
  fix32_t u, omega; 
  int32_t duty;

  omega = FIX_Mul(FIX_PI, FIX_DBL(0.2));  // omega = 2*PI/10 (100Hz)

  sample_out = 0;
  GPIOSetDir(1, 8, 1);          // PIO1_8 : OUTPUT

  Init_PWM();

  // Set TIMER32B0 as Free-Running milliseconds Timer
  LPC_SYSCON->SYSAHBCLKCTRL |= (1<<9);    // Timer32B0 Clock ON (48MHz)
  LPC_TMR32B0->PR  = 48000;    // Pre-scaler : 48MHz/48000 → 1msecで1カウント
  LPC_TMR32B0->TCR = 1;        // Timer Start

  while(1)
    {
      GPIOSetValue(1, 8, sample_out); 

      msec = LPC_TMR32B0->TC; 

      u = FIX_Sin( FIX_Mul(omega, FIX_INT(msec)) );     /* -1 〜 1 */
      duty = INT_FIX( FIX_Mul(u, FIX_INT(500)) ) + 500; /* 0 〜 1000 */
      PWM_Set_Duty(0, duty) ; 
      sample_out = 1 - sample_out;
    }
    return 0; 
}
すると結果は
のように,サンプル周期は220usecと約半分になりました.また,コードサイズは約10KBと,こちらも約半分です.劇的とまではいえませんが,うまく使えば効果的でしょう.