PIC24FBGとAndroidを使ったYPVSコントローラですが、長期化しつつあります。
というのも、
・USBを接続に使うので、デバッグが捗らない
・Android側でOpenGLを使い始めたというアホな理由
・PIC24FBGがそこら辺に売っておらず、入手性が悪く、また金額も高い
という様々な理由があります。
あんまりにもアレなので、せめてアナログ回路の正しさ、ロジックの検証の為に、シンプルPIC12Fでひとつ作りました。
ざっくり動作しました。
PIC12Fは単純なのでこれでアナログとロジックの検証しようかと思います。
安いから焼けても痛くないし・・・。
丁度よい箱が無い為、場所が余りまくりです。半分で良いですね。
手組ですが、ジャンパーも無く出来ました。
あぁええわぁPIC12F。シンプルイズ最強ですね。
ロジックはこれからです。
ウィンウィン動かしながら調整していきます。
モータ制御にはTA8428Kを12Vで使っていますので容量的には十分だろうと思っています。
PICとポテンションメータはJRCの7805レギュレータで5V作っています。
USBと違ってほとんど電流を流さない為、熱的にも全く問題は無いようです。
PIC12FのInputCaptureについて、使い方が詳しく乗っているサイトもが無くてアレだったのですが、マニュアル読んで組んでみたところ無事動作している感じです。
久々にペンタさんのHPを除いてみたら、YPVSコントローラのA/D変換は5ビット程度の分解能だろうとの記事がありました。
32階層しか無いって事ですね〜。
対して今回作ったYPVS12F(仮称)は10ビットアナログコンバータを使っているので、1024階層です。
これは決して良い話って訳でもなくて、実際敏感すぎます。
サーボモータからのフィードバックはポテンションメータを通して行われますが、ノイズだって乗るでしょうし、敏感過ぎて収束せず、ずっとモータが行ったり来たりする可能性大です。
まぁそういう制御もありますから、一概にどうかって事は言えないのですが、、、。
この辺りは反応の鋭さと、モータの発振抑制との間でのせめぎあいになるのかも知れません。
抑制もアナログ回路でやるのか、ソフトウェアでやるのか、など等。
まずはモータにつけて動作比較をしてみようかと思います。
PIC12Fで機能を満足するだけの全く「素」の状態でのコードは以下です。
不具合もたくさん残っており、当然このままでは使い物になりませんが、割り込み処理などPICの使い方の参考にはなるかも知れません。
実験的にノーマルより低回転側でYPVSがやや閉まる方向でやっています。
2〜3馬力アップするらしいので・・・。
これを元に帳尻併せ、、、じゃなかったソフトウェアチューニングを行っていきます。
コンパイルするとメモリサマリーが出ますが、今こんな感じです。
Memory Summary:
というのも、
・USBを接続に使うので、デバッグが捗らない
・Android側でOpenGLを使い始めたというアホな理由
・PIC24FBGがそこら辺に売っておらず、入手性が悪く、また金額も高い
という様々な理由があります。
あんまりにもアレなので、せめてアナログ回路の正しさ、ロジックの検証の為に、シンプルPIC12Fでひとつ作りました。
ざっくり動作しました。
PIC12Fは単純なのでこれでアナログとロジックの検証しようかと思います。
安いから焼けても痛くないし・・・。
丁度よい箱が無い為、場所が余りまくりです。半分で良いですね。
手組ですが、ジャンパーも無く出来ました。
あぁええわぁPIC12F。シンプルイズ最強ですね。
ロジックはこれからです。
ウィンウィン動かしながら調整していきます。
モータ制御にはTA8428Kを12Vで使っていますので容量的には十分だろうと思っています。
PICとポテンションメータはJRCの7805レギュレータで5V作っています。
USBと違ってほとんど電流を流さない為、熱的にも全く問題は無いようです。
PIC12FのInputCaptureについて、使い方が詳しく乗っているサイトもが無くてアレだったのですが、マニュアル読んで組んでみたところ無事動作している感じです。
久々にペンタさんのHPを除いてみたら、YPVSコントローラのA/D変換は5ビット程度の分解能だろうとの記事がありました。
32階層しか無いって事ですね〜。
対して今回作ったYPVS12F(仮称)は10ビットアナログコンバータを使っているので、1024階層です。
これは決して良い話って訳でもなくて、実際敏感すぎます。
サーボモータからのフィードバックはポテンションメータを通して行われますが、ノイズだって乗るでしょうし、敏感過ぎて収束せず、ずっとモータが行ったり来たりする可能性大です。
まぁそういう制御もありますから、一概にどうかって事は言えないのですが、、、。
この辺りは反応の鋭さと、モータの発振抑制との間でのせめぎあいになるのかも知れません。
抑制もアナログ回路でやるのか、ソフトウェアでやるのか、など等。
まずはモータにつけて動作比較をしてみようかと思います。
PIC12Fで機能を満足するだけの全く「素」の状態でのコードは以下です。
不具合もたくさん残っており、当然このままでは使い物になりませんが、割り込み処理などPICの使い方の参考にはなるかも知れません。
実験的にノーマルより低回転側でYPVSがやや閉まる方向でやっています。
2〜3馬力アップするらしいので・・・。
これを元に帳尻併せ、、、じゃなかったソフトウェアチューニングを行っていきます。
コンパイルするとメモリサマリーが出ますが、今こんな感じです。
Memory Summary:
Program space used 48Ch ( 1164) of 800h words ( 56.8%)
Data space used 51h ( 81) of 80h bytes ( 63.3%)
EEPROM space used 0h ( 0) of 100h bytes ( 0.0%)
Configuration bits used 1h ( 1) of 1h word (100.0%)
ID Location space used 0h ( 0) of 4h bytes ( 0.0%)
約6割程度を使っていますが、今後のチューニングで埋まる程よいサイズだと思います。
PIC12Fジャストサイズ。
約6割程度を使っていますが、今後のチューニングで埋まる程よいサイズだと思います。
PIC12Fジャストサイズ。
#include #include <htc.h> #define _XTAL_FREQ 8000000 // 8MHz #define LED GP1 #define MC1 GP4 #define MC2 GP5 #define MODE GP3 #define TRUE 1 #define FALSE 0 #define INSENSITIVE_RANGE 10 __CONFIG( FOSC_INTOSCIO & // 発振機 内臓利用 WDTE_OFF & // ウォッチドッグ 無効 PWRTE_ON & // パワーアップタイマー 有効 BOREN_ON & // ブラウンアウト検出 有効 MCLRE_OFF & // ~MCLR 無効 CP_OFF & // コードプロテクション 無効 IESO_OFF & // IEスイッチオーバー 無効 FCMEN_OFF // フェイルセーフクロック 無効 ); // ポテンションメータテーブル const int gPotTable[] = { 180, // 0: 0rpm 180, // 1: 1000rpm 180, // 2: 2000rpm 180, // 3: 3000rpm 180, // 4: 4000rpm 180, // 5: 5000rpm 266, // 6: 6000rpm 348, // 7: 7000rpm 430, // 8: 8000rpm 485, // 9: 9000rpm 485, //10:10000rpm }; #define POT_FULL_OPEN_TAGET (gPotTable[10]) #define POT_FULL_CLOSE_TARGET (gPotTable[0]) // Engine state typedef enum{ EG_STARTED, // EG_STARTED EG_INIT_OPEN1, // 電源オン時の最初の全開動作 EG_INIT_CLOSE, // その次の全閉 EG_INIT_OPEN2, // その次の全開 EG_INITIALIZED, // 全開のままエンジン起動待ち }EG_STATE; // Moter state /* IN1 IN2 OUTA OUTB H H L L BRAKE L H L H 逆転 H L H L 正転 L L OFF (ハイインピーダンス) ストップ */ typedef enum{ MT_BRAKE, MT_OPEN, MT_CLOSE, MT_STOP }MT_STATE; #define MTBrake() {MC1=1;MC2=1;} #define MTOpen() {MC1=0;MC2=1;} #define MTClose() {MC1=1;MC2=0;} #define MTStop() {MC1=0;MC2=0;} EG_STATE gEGState = EG_STARTED; unsigned int gRPM = 0; unsigned int gPot = 0; void MoterHead(unsigned int pot, unsigned int rpm); /** ----------------------------------------------------------------- 1|VDD PIC12F687 VSS|8 MTR_C<- 2|GP5(MC2) GP0/AN0|7 <-Potension meter Analog IN MTR_O<- 3|GP4(MC1) GP1/AN1|6 ->LED MODE -> 4|GP3/~MCLR GP2/AN2/CCP1|5 <-PULSE ------------------------------------------------------------------ */ /** ----------------------------------------------------------------- void main(void) メイン関数 ------------------------------------------------------------------ */ void main(void) { OPTION_REG = 0b10000010; // PUしない WPU = 0b00000000; // PUしない OSCCON = 0b01110000; // 8MHz TRISIO = 0b00001101; // IN: GP0/GP2/GP3/ OUT:GP1/GP4/GP5 ANSEL = 0b01010001; // AN0選択、Tad(Fosc/16)=2.0us CMCON0 = 0x7; // コンパレータ無効 ADCON0 = 0b10000001; // 右詰、VDD、AN0利用 gEGState = EG_INIT_OPEN1; MTStop(); // モータ初期値 // Input capture初期化(*1) CCP1CON = 0; // 一度リセットする必要がある //CCP1CON = 0b00000101; // every rising edge CCP1CON = 0b00000110; // every 4th rising edge //CCP1CON = 0b00000111; // every 16th rising edg CCP1IF = 0; // 設定後は必ずIFをリセット // タイマー初期化 T1CON = 0b00110100; // T1CKPS 1:8 TMR1IF = 0; TMR1L=0; TMR1H=0; // 割り込み初期化 GIE = 1; PEIE = 1; TMR1IE = 1; // Timer1 intterupt on TMR1ON = 1; // Timer1 start CCP1IE = 1; // Capture interrupt on // メインループ while(1) { GO_DONE = 1; // 変換開始 while(GO_DONE); // 変換待ち Tad*11=220us gPot = (ADRESH * 256 + ADRESL)+1; // Analog input MoterHead(gPot, gRPM); } } /** ----------------------------------------------------------------- void MoterHead(unsigned int pot, unsigned int rpm) モータ制御 @param pot ポテンションメータ(1〜1,024) @param rpm 回転数(0〜9,999) ------------------------------------------------------------------ */ void MoterHead(unsigned int pot, unsigned int rpm) { // モータドライバTA8228Kは切り替えにSTOP時間100us必要 // A/D変換で220us使うので状態が変わるときは一度MT_STOPすればOK static MT_STATE pre_mt_state = MT_STOP; MT_STATE next_mt_state; unsigned int index, target; double ax; switch(gEGState) { case EG_STARTED: if( rpm > 10000 ) rpm = 9999; index = rpm/1000; // ターゲット算出 (*2) ax = ((gPotTable[index+1] - gPotTable[index]) * ((rpm%1000)/1000)); target = (unsigned int)ax + gPotTable[index]; break; // 電源オン時の最初の全開動作 case EG_INIT_OPEN1: if( pot >= POT_FULL_OPEN_TAGET ) gEGState = EG_INIT_CLOSE; else target = POT_FULL_OPEN_TAGET; break; // その次の全閉 case EG_INIT_CLOSE: if( pot <= POT_FULL_CLOSE_TARGET ) gEGState = EG_INIT_OPEN2; else target = POT_FULL_CLOSE_TARGET; break; // その次の全開 case EG_INIT_OPEN2: if( pot >= POT_FULL_OPEN_TAGET ) gEGState = EG_INITIALIZED; else target = POT_FULL_OPEN_TAGET; break; // 全開のままエンジン起動待ち case EG_INITIALIZED: target = POT_FULL_OPEN_TAGET; // パルスが入ればEG_STATEDへ遷移 if( rpm > 1000 ) { gEGState = EG_STARTED; } break; default: break; } // モーター制御 if(target > pot+INSENSITIVE_RANGE) next_mt_state = MT_OPEN; else if(target+INSENSITIVE_RANGE < pot) next_mt_state = MT_CLOSE; else next_mt_state = MT_BRAKE; // 前回と違う場合は一旦STOPを入れる if( pre_mt_state != MT_STOP && pre_mt_state != next_mt_state ) { next_mt_state = MT_STOP; } switch( next_mt_state ) { case MT_BRAKE: MTBrake(); break; case MT_OPEN: MTOpen(); break; case MT_CLOSE: MTClose(); break; case MT_STOP: MTStop(); break; default: MTStop(); break; } pre_mt_state = next_mt_state; } /** ----------------------------------------------------------------- void interrupt ISR(void) 割り込み制御関数 Timer1、Captureを処理する ------------------------------------------------------------------ */ void interrupt ISR(void) { // Input capture if(CCP1IF) { // タイマーリセット TMR1L=0; TMR1H=0; // 1000rpm -> gRPM=1000, 10000rpm -> gRPM=10000 gRPM = 60000L/(((CCPR1H*256)+CCPR1L)/100/4); if(gRPM >= 10000) { gRPM = 9999; // 1万以上は使わないので }else if(gRPM >=1000) { gEGState = EG_STARTED; } CCP1IF = 0; } // Timer1 タイムアウト→エンジン停止と判断 if(TMR1IF) { gRPM = 0; if(EG_STARTED == gEGState) { gEGState = EG_INIT_OPEN1; } TMR1IF = 0; } }
コメント
コメント一覧 (4)
急激に疲れが出ました。
それは大変でしたね〜
まぁ日記のネタっすね!
#一部コメント削除しました。