C8051F300 (CYGNAL)
概要
ハードウェア
ソフトウェア
ソフトウェアダウンロード
参考文献
加速度センサー処理
サーボ用PWM処理
変更履歴
2002年5月18日修正 ---- 説明追加(加速度センサー&サーボ制御)
2002年5月14日 ---- 新規プロジェクト設定
C8051F300 / 概要
関連情報
C8051F300の情報は下記WEBサイトで入手できます。
三洋電機(株) (Cygnal社と販売契約締結)
http://www.semic.sanyo.co.jp/c8051/jp/index.htmという事だったのですが、三洋は日本国内販売店契約を終了した模様です。
Cygnal社
http://www.cygnal.com/default.htmも、Silicon Laboratoriesに変わりました(吸収された?)。新たに下記ページで情報入手出来ます。
http://www.silabs.com/products/microcontroller/mlp_matrix.asp
http://www.silabs.com/tgwWebApp/public/index.htm
ここはまだCygnal社の紹介をしています。
http://www.microtek.co.jp/product/cygnal/
私の開発環境
私は、下記のWEBに載っているツールを入手して、使用しています。
http://www.semic.sanyo.co.jp/c8051/jp/toolkit.html
でしたが、アクセス出来ません。現在は、下記ページで日本語での情報が入手できる状態です。
http://micomfreaks.hp.infoseek.co.jp/
http://www.silabs.com/tgwWebApp/public/web_content/products/Microcontrollers/en/C8051F300DK.htmプロジェクト例
現在、バイク(Motorcycle)らしき模型制御のプロジェクトを手がけています(2004年1月1日現在、先に進んでいません )。
基本機能
制御対象は、
・小型DCモーターによる前進(後退不可)駆動制御
・模型用サーボモーターによる前輪舵角制御
であり、この機能を自立的に作動させる為に、
・PSD(光測距センサー)による前方障害物検知
・2軸加速度センサによる姿勢検知
の機能を持たせ、更に
・無線による制御データの監視機能
を実現しようとしています。
これら全ての機能を、C8051F300で制御しています。
電源は、電気二重層コンデンサを使用して、短時間の自走をさせる予定です。
C8051F300/ ハードウェア
ハードウェア構成
回路図等の情報は、下記を見てください。
回路図と付帯情報(2ページ)
C8051F300は、3mm角の非常に小さなパッケージの為、アマチュアが手ハンダで作業するのはかなりのスキルと良い道具が必要でしょう。新しく始めたい人は、汎用DIPが評価用として販売されていますので、そちらをお勧めします。但し、小型化の為には非常に有効で、今回のCPUモジュールは、秋月1.27ハーフピッチ基板を48mm x 13mmの大きさに分割した中に、RFモジュール以外の機能はほとんど入ってしまいます。
CPUクロックは内部発振モードを使用している為、外部結線は入出力とDebug用インターフェイスを実装しているだけで、非常にシンプルです。
加速度センサーは、ANALOG DEVICES社の ADXL202Eを実装しています。このチップも4.5mm x 5mmと非常に小さいもので、今回のプロジェクトで使用した、F300,ADXL202E、更にRF−TXをコンパクトに実装すれば、傾斜角度センサーや回転体の加速度測定などワイヤレスでのデータ計測と、非常に小さな筐体構成が実現できて、応用が広がると思います。EEPROMも実装して、標準器と校正した上で出力する方法を用いればアマチュアレベルがら実用領域へ利用が拡大出来ると思います。
ADXL202Eからの出力は、X&Y軸2出力をF300のPCAモジュールの入力に入れています。出力は、パルス幅に情報が入っているため、パルス幅計測する事で、加速度情報をF300に取り込みます。PCAは、Programable Counter/Timer Arrayの略で、3つの独立した機能を選択出来ます。今回は、2つをInput Caputure機能として、ADXL202Eからのパルス計測に用い、残り1つを16Bit PWM Output機能として、サーボ制御パルス出力に割り付けています。
アナログ入力は、PSD(シャープGP2D12)からの0〜2.6Vの出力信号をそのまま処理しています。GP2D12は、出力更新が38.3ms(中心値)毎の為、500kspsの能力を持つADCからするととんでもなく遅い20ms周期で計測するようにソフトウェア設定しています。
電源電流に関しては、GP2D12はとても大食いで困っています。便利なモジュールですが、カタログデータで50mA(最大値)と電気二重層コンデンサでのシステム構成の一番のネックです。モータ駆動電流も後輪空転時の電流が10mAですし、サーボモータも無負荷起動では同程度ですので、定常電流の50mAはいかに大食いかが分かります。送信モジュールも、4mA(標準)と優秀ですし、F300とADXL202Eもトータルで数ミリアンペア(今回の用途では)と優秀です。
シリアル通信は、結果としてうまく動いていますが推奨出来る内容では無いので、こんな方法もあるのか程度に考えて下さい。同じ方法を用いてもうまくいく保証は、ありません。
IPI(アイ・ピー・アイ)から入手したモジュールは、 AM-RT5-418(AM Hybrid Transmitter - RF Solutions)と AM-HRR3-418(AM Hybrid Receiver)です。このモジュールは、使い方として直流信号が扱えないとのコメントを Webページ内で確認出来ます(RF Moduleの所)。従って、データ送信時に絶え間なくデータ送出する事を前提としました。更に、 通信速度は推奨2400bpsとなっていますが、今回は有線でのDebugが先行し9600bpsで試験していたために、そのまま実験をスタートしたところうまく動作したので、そのままとしました。真似をして、動作しないからと言って私や購入先に文句を言わないで下さい。安定性向上の為に、2つの事を実施しました。
・F300のTX出力は、アクティブローですがトランジスタにより反転した上で、送信モジュールに入力しています
・微弱電波の範囲で運用するためには、送信側での改善が出来ない為、受信アンテナを製作しました
参考文献 「手軽に作れて実用的430MHz帯フォークヘンテナ」 片倉由一氏JH1OHZ(月刊誌モービルハム 1991年6月号)
データ送出は一方的な垂れ流しで使用しているため今回の様な使い方でも、十分に役立ちます。
C8051F300 / ソフトウェア
今回は、Keil社のCコンパイラを用いて開発しました。三洋から入手したツールキットには評価版のコンパイラが入っていますいましたが、4kBのコードサイズ制限がある為、残念ですがprintf()の様な大規模なLibをリンクすると、すぐにサイズが膨らみ思ったような応用が出来なくなります。しかし、正規製品版のコンパイラは、アマチュアにとっては高嶺の華でしょう。Linuxの環境下では、いろいろと方法がありそうですから、調査したらいかがでしょうか?例えば、このページ辺りから入ってみれば良いと思います。
現在のソフトは、Motorcycleとしてのプロジェクト完結まで至っていません。下記機能が実現されている段階です。
(a) ADCを用いた、PSD信号処理
(b) PCAを用いた、加速度センサー信号処理
(c) PCAを用いた、サーボモータ用PWM信号発生
(d) TIMER2を用いた、DCモーター速度制御
(e) UART0のTXとRFモジュールを用いた、データ送出機能
C8051F300使用時のノウハウ (Programing tips)
(a)PCA 16bit Software Timer
F300/1/2/3のマニュアルP137を見ると、ちゃんと
Important Note About Capture/Compare Registor: When writing a 16-bit value to the PCA0 Capture/Compre registers, the low byte should always be written first.
と書かれていて、ローバイトを先に書かないといけないとなっています。しかし、Cソースコード内でついつい
if ( CCF2 == 1){ // -------- Module 3 ------------------------
if ( pwm_period == 0 ){
PCA0CP2 = pwm_data;
}
CCF2 = 0; // For just in case
}
と記述すると、KeilのCコンパイラは下記の様にコード展開されてしまい先の約束事が守られなくなってしまいます。
MOV PCA0CP2+01H,pwm_data
MOV PCA0CP2,pwm_data+01H
そこで、ソースファイル上で、下記の様に記述すると、
if ( CCF2 == 1){ // -------- Module 3 ------------------------
if ( pwm_period == 0 ){
PCA0CPL2 = (unsigned char)pwm_data; // Set center position of servo data
PCA0CPH2 = (unsigned char)(pwm_data/256); // (need separate loading method !)
}
CCF2 = 0; // For just in case
}
コード展開が、
MOV PCA0CPL2,pwm_data+01H
MOV A,pwm_data
MOV PCA0CPH2,A
となり、約束が守られます。
後から考えれば当たり前ですがここで、はまってしまい半日無駄にしました。
(b) Priority Closbar Decoder
この機能の理解も、F300を使いこなす上では大変重要な部分と思われます。ポイントとなる点は、
・クロスバー機能を経由してポートと接続する機能と、必ずスキップしなければならない機能を層別する事
・各リソース(UART,SMBus,SYSCLK,CP0 Output,PCA,T0&T1 and Port Latch data)の禁止/使用を明確にする事
です。書いてしまえばそれまでですが、Debug中に機能がうまく実現出来る以前は、機能自身の設定ミスかクロスバーの設定ミスかを断定出来ずに悩む事になります。
C8051F300 / ソフトウェアダウンロード
現在進行中休止中の為、暫定版です。
Control program for C8051
------- Motocycle
C8051F300 / 参考
関係するサイト
各項目のリンク先をご覧ください。
C8051F300 / 加速度センサー処理
加速度センサーの データシート(ANALOG DEVICES社ADXL202E)を読むと、加速度情報は下記の式で計算出来る事が分かります。
// ADXL202 Acceleration data
// |<---data-->|
// |<---------period----->| (data/period) - 50%
// ________________ ________ g = ------------------------------
// | | | 12.5%
// ___| |_________________|
// t1 t2 t3 (PCA0 counter data)
//
// Overflow process is ignored ( only once is expected)
//
PCA0モジュールのインプットキャプチャー機能で、立ち上がりと立ち下がり両エッジでキャプチャーするモードに設定し、t1,t2、t3の各タイミングでフリーランニングカウンターの値を計測します(下記ソースコード 青色参照 )。フリーランニングカウンターは16Bitですから当然オーバーフローする訳ですが、それが起きても一度しか起きないようにADXL202Eの出力周期定数をRsetの値とPCA0のクロックソース選択で調整します。
今回は、PCA0クロックを
void PCA0_Init (void)
{
PCA0CN = 0x40; // Enable PCA counter
PCA0MD = 0x09; // Sysclk/1, Eable Overflow int.
// overflow = 2^16 * 1/(24.5 * 1e6)
// = 2.675mS
PCA0CPM0 = 0x31; // Module0,
// Both edge mode, enable interrupt
PCA0CPM1 = 0x31; // Module1,
// Both edge mode, enable interrupt
PCA0CPM2 = 0xcb;// 16bit PWM mode, comparator function
// Enable interrupt
// PCA0CP2 = SRV_CNTR; // Center position of servo data
PCA0CPL2 = SRV_CNTR;
PCA0CPH2 = SRV_CNTR/256;
pwm_period = SRV_CNT_N;
}
と最速としています。一方Rsetは、200KΩを付けていますので、周期設定は、
period = Rset/125Mohm = 200kohm/125Mohm = 200 *1e3 / 125 * 1e6 = 0.0016 [sec] = 1.6 [msec]
となります。
加速度センサーの生出力分析の為に、実測データをUART経由でパソコンに送るソフトウェアを作成しました。下記にソースコードを示します。void show_acc_data( void )
{
unsigned int dt0, dt1;
if ( x_ready == 1){
EA = 0; // disable interrupts
dt0 = x_period;
dt1 = x_data;
EA = 1; // re-enable interrupts
PutCRLF(); // Display T = xxxxmS
PutStr("Txp= ");
// show_time_data( dt0 );
show_time_data( x_period );
PutStr("d= ");
// show_time_data( dt1 );
show_time_data( x_data );
}
x_ready = 0;
if ( y_ready == 1){
EA = 0; // disable interrupts
dt0 = y_period;
dt1 = y_data;
EA = 1; // re-enable interrupts
PutCRLF(); // Display T = xxxxmS
PutStr(" Typ= ");
// show_time_data( dt0 );
show_time_data( y_period ); // こんな風にして、UARTへデータ送出していました。
PutStr("d= ");
// show_time_data( dt1 );
show_time_data( y_data );
}
y_ready = 0;
}
void show_time_data( unsigned int dt )
{
putchar(dt/10000 +'0'); // 10^4
dt = dt - (( dt / 10000 ) * 10000);
putchar(dt/1000 + '0'); // 10^3
dt = dt - (( dt / 1000 ) * 1000);
putchar(dt/100 + '0'); // 10^2
dt = dt - (( dt / 100 ) * 100);
putchar(dt/10 + '0'); // 10^1
putchar(dt%10 + '0'); // 10^0
PutStr(" mS ");
}
このソフトを使い、PCに無線でデータ送信して、エクセル処理したデータ を見ると、平均値で36893ですので、
period = 36893 * (2.675msec/ 2^16) = 36893 * (2.675*1e-3/65536) = 36893 * 40.817 * 1e-9 = 1505.87 [usec] = 1.506 [msec]
となっています。この値であれば、どんなタイミングでもオーバーフローは0回もしくは1回です。その為、符号なし16Bit幅の演算をすれば、何も気にせず計測データの差分をとれば、オーバーフローを無視しても正しい値が計算が出来ます。結果として、計算は 赤字の様になります。 unsigned int が、今回使用したCでは、16Bitですのでシンプル演算出来ます。
static unsigned int
t1_x, // Rising edge data for sarting
t2_x, // Falling edge data
t3_x, // Rising edge data for ending
t1_y, // Same rule as X-axis
t2_y,
t3_y;
void PCA0_ISR (void) interrupt 9 using 3
{
if ( CF == 1 ){ // -------- PCA0 Overflow -------------------
省略;
}
if ( CCF1 == 1 ){ // -------- Module1 -------------------------
if (X_IN == 1){ // Mesure rising egde data
t3_x = PCA0CP1; // Read caputure data from Module0
x_period = t3_x - t1_x; // Calculate cyclic time
x_data = t2_x - t1_x; // Calculate acceleration data
t1_x = t3_x; // Save data for next mesurement
x_ready = 1; // Set ready flag
} else { // Mesure falling egde data
t2_x = PCA0CP1; // Read caputure data from Module0
}
CCF1 = 0; // Clear interrupt source
}
if ( CCF0 == 1 ){ // -------- Module2 --Same as Module1 -------
省略;
次に、正規化処理ですがエクセルファイルの中の"calcu"シートを参照下さい。
//------------------------------------------------------------------------------------
// Normalization of Acceleration Data
//------------------------------------------------------------------------------------
// G = (data/period -50%) / 12.5% [G]
// = (data/period * 100 - 50) / 12.5 [G]
// = (data/period * 100 - 50) * 1000 / 12.5 [mG]
// = (data/period * 100 * 1000 - 50 * 1000) / 12.5 [mG]
// = data * 1000 * 10 / period * 100 / 125 - 50*1000 / 12.5 [mG]
// = data * 10000 / period * 100 / 125 - 4000 [mG]
//
void nor_acc_data( void )
{
unsigned int dt0, dt1;
if ( x_ready == 1){
EA = 0; // disable interrupts
dt0 = x_period;
dt1 = x_data;
EA = 1; // re-enable interrupts
x_g =(int)((unsigned long)dt1 * 10000L / (unsigned long)dt0 * 100 / 125) - 4000;
}
x_ready = 0;
if ( y_ready == 1){
EA = 0; // disable interrupts
dt0 = y_period;
dt1 = y_data;
EA = 1; // re-enable interrupts
y_g =(int)((unsigned long)dt1 * 10000L / (unsigned long)dt0 * 100 / 125) - 4000;
}
y_ready = 0;
}
となります。計測データを正規化し、UART経由で集めたデータは同じく エクセルファイル(シート名 0429_data_1と0429_data_2)を参照下さい。今回のチップは重力1gを期待して計測したところ、
X1 Y1 X2 Y2
Ave -75.37 -1082.40 870.33 -185.28
Shiguma 6.83 7.70 8.21 10.62
Max -60.00 -1058.00 897.00 -161.00
Min -113.00 -1105.00 848.00 -219.00
Diff 53.00 47.00 49.00 58.00
EXPECTED
DATA 0.00 -1000.00 1000.00 0.00
となりました。X1,Y1計測とX2,Y2計測は、基板を90度回転したデータです。この数値を使ってオフセット補正をする事や、最終的には角度を正確に計測しながら重力による補正を実施すれば、そこそこ実用的になると思います。
C8051F300 / サーボ用PWM出力処理
サーボは今回使用したサーボは、下記の様なものです。
・会社: Grand Wing Servo-tech Co.,Ltd.
・Web: http://www.grandwing.com(残念ですが情報が取れません)
・型式: PICO F/BB
商品の包装にニュートラル位置が1500マイクロセコンドである旨が、書かれていましたので、PCA0の1チャンネルを16BitPWMとして設定して制御する事にしました。PCA0のフリーランニングカウンターは1つを3つのチャンネルで共通に使用しますので、加速度センサー用設定の条件である2.657msecをそのままにすると、周期(15msecから20msec程度)が設定出来ません。そこで、カウンターのオーバーフロー割り込みを許可して全体周期をソフトウェアで制御する事にしました。幸いこの手のサーボは、周期については精度は問題ではなくアクティブパルス幅の精度だけが重要の用です。
PCA0の初期設定で、赤字部分でPWM設定、 青字部分でオーバーフロー割り込みを許可しています。
void PCA0_Init (void)
{
PCA0CN = 0x40; // Enable PCA counter
PCA0MD = 0x09; // Sysclk/1, Eable Overflow int.
// overflow = 2^16 * 1/(24.5 * 1e6)
// = 2.675mS
PCA0CPM0 = 0x31; // Module0,
// Both edge mode, enable interrupt
PCA0CPM1 = 0x31; // Module1,
// Both edge mode, enable interrupt
PCA0CPM2 = 0xcb;// 16bit PWM mode, comparator function
// Enable interrupt
// PCA0CP2 = SRV_CNTR; // Center position of servo data
PCA0CPL2 = SRV_CNTR;
PCA0CPH2 = SRV_CNTR/256;
pwm_period = SRV_CNT_N;
}
従って、PWM設定はアクティブパルス幅のみをコントロールします。パルス幅の最大値最小値は、非常に重要です。サーボの制御範囲を逸脱するとサーボ自身のギヤを壊す可能性があります(実は、今回壊してしまいました)。そこで、下記の様に制限値を規定し、制御値をその中に入れ込む必要があります。制御角度範囲は、ちょっと欲張り過ぎかも知れません。MINをより大きく、MAXをより小さい値にする事を推奨します。
#define SRV_CNTR 36750 // 1.5mS * 24.5MHz = 39200 counts
#define SRV_MIN 19600 // 0.8mS * 24.5MHz = 19600 counts
#define SRV_MAX 58800 // 2.4mS * 24.5MHz = 58800 counts
この値で、下記の様に制限します。この例では、PSDのアナログ値をそのままサーボデータとしています。これでPSDの前に手をかざして、距離を変化させると前輪の操舵角が変化して制御のデバッグに役立ちます。
//------------------------------------------------------------------------------------
// Show ADC data
//------------------------------------------------------------------------------------
// Vref = VDD = 3.28V
// Scale = 3.28 * 255/256 /256 [V/bit]
// = 3.28 * 255/256 /256 * 1000 * 1000 [mV/bit]
// = 12813 *255/256 [mV/bit]
// = 12763 [mV/bit]
//
void show_anlg_data( void )
{
long volt_dt; // ADC data (mV)
unsigned int dt;
EA = 0; // disable interrupts
volt_dt = acc_ana;
EA = 1; // re-enable interrupts
volt_dt = (volt_dt * 12763L) / 64000L;
show_volt_data( (unsigned int)volt_dt );
// ********** DEBUG *********** //
dt = SRV_MIN + (unsigned int)volt_dt * 14;
if ( dt > SRV_MAX ){
dt = SRV_MAX;
} else if ( dt < SRV_MIN ){
dt = SRV_MIN;
}
pwm_data = dt;
}
実は、以前作ったロボット(未だ詳細説明を公開していないのですが)では、SANWAやFUTABAのもう少し大型のサーボを使用していたためギヤがロックする状態にしても(デバッグ途中での話)壊れなかったのですが、今回の物は小型でギヤもプラスチックの小さな物でモーターのトルクが勝ってしまい破壊に至りました。みなさんも気をつけて下さい(私は、3290円で買ったものです−残念無念)。
さて、オーバーフロー時の処理ですが、
#define SRV_CNT_N 6 // Servo PWM cycle counter
void PCA0_ISR (void) interrupt 9 using 3
{
if ( CF == 1 ){ // -------- PCA0 Overflow -------------------
if ( pwm_period == 0 ){
pwm_period = SRV_CNT_N;
} else {
pwm_period--;
}
CF = 0; // Clear Overflow flag
}
if ( CCF1 == 1 ){ // -------- Module1 -------------------------
省略;
CCF1 = 0; // Clear interrupt source
}
if ( CCF0 == 1 ){ // -------- Module2 --Same as Module1 -------
省略;
CCF0 = 0;
}
if ( CCF2 == 1){ // -------- Module 3 ------------------------
if ( pwm_period == 0 ){
PCA0CPL2 = (unsigned char)pwm_data; // Set center position of servo data
PCA0CPH2 = (unsigned char)(pwm_data/256); // (need separate loading method !)
// PCA0CP2 = SRV_CNTR; // Don't use this command !!
} else {
PCA0CPL2 = 0; // Set center position of servo data
PCA0CPH2 = 0; // (need separate load !)
}
CCF2 = 0; // For just in case
}
}
の中で、pwm_periodの値を初期値(今回は6)からオーバーフロー毎に減算し(上記 赤字部分の処理)、ゼロであれば青字部分 の様にPWMの割り込みでパルス幅を設定し、それ以外ではパルスが出ないようにしています(緑の部分 )。これで、2.675msec * 7 = 18.725msecの周期設定(もちろん割り込みタイミングでこの値は変動します)が可能になります。