mTouchはお仕着せのまま使うと簡単に使えることがわかりましたが、一方でMCCでのコード生成に「そうじゃなくて」という要望を加えるとなると、ちょっとハードルが高いですね。なにより "Generate" ボタンを押すと、生成されていたすべてのコードがチェックされ、変更があれば上書きされてしまうようですし。
自分の場合、PIC16F18346 にて mTouch を使いながら、同時にピリオドタイマが3つ欲しいという仕様を実現したいのです。そうなると、TIMER2/4/6 は3つとも同じ仕様なのでコードも基本部分は共通化できます。もちろんレジスタや割り込みフラグの位置などは違っていますが、流れとしては同じです。ところが MCC の mTouch では同じようにピリオドレジスタを実現できる TIMER0 は選択できません。
機械的コード生成を汎用的に考えるなら、PICの機種間で共通の TIMER2/4/6 のみに絞ったコードを生成するのは理にかなっていますから、これはしかたないとは思いますが、同時に自由度もなくなってしまっています。
それでも TIMER0 をピリオドタイマとして使いたければ、一旦生成されたコードを手作業でいじるしかありません。
ということで以下のようにやってみます。ただ、これはできるかどうかの実装テストなので簡単に。
- まずMCCでシステム設定、ペリフェラル、ピン割当などを行う。できればクロック設定や割り込みについても指定しておく。
- MCCでコード生成をする。
- バージョン管理システムにコードを登録する。
- コードをいじる。
で、方針は決まった感じですが、その前にまずは AFA(Automatic Frequency Adaptation)の機能をある程度把握しておかないと、無駄なことをやってしまう可能性があります。
The Automatic Frequency Adaptation (AFA) section allows you to enable the advanced noise immunity algorithm. The automatic frequency adaptation routine continuously tracks the amount of noise that is on each sensor and chooses a new scan frequency intelligently to avoid noise frequency and its harmonics. The routine requires an 8-bit timer to precisely control the scanning frequency for non-ADCC parts implementation. Due to this requirement, mTouch® will take ownership of the first available 8-bit timer module by default, if one is available when the automatic frequency adaptation routine is enabled.
「AFAによって高度なノイズ除去アルゴリズムを利用可能になります。自動周波数適応(AFA)ルーチンは、それぞれのセンサのノイズの総量を継続的に追いかけ、ノイズ周波数とその倍音成分を除去するための新たなスキャン周波数を賢く選択します。ルーチンはADCC以外の実装におけるスキャン周波数を精密に制御するための8bitタイマを必要とします。この必要条件によって、AFAルーチンが選択されたときには、mTouchはデフォルトで最初に利用可能な8bitタイマモジュールを専有するようになっています。」
という感じです。ADCCというのがよくわかりませんが、もしかしてADCのtypoかと思って調べてみたら Analog-to-Digital Converter with Computation だそうです。Computationは数値処理ですから、ADCとそれに伴う計算ということですね。
CVD方式のmTouchアルゴリズムでは、プリチャージとアクイジション期間、ADCサンプリングを繰り返すシーケンスと、そのシーケンスを起動するサイクルを制御するのと二種類があると思いますが、ノイズ除去に効くのはどちらでしょうね。ちょっとAPIリストとかも眺めてたんですが、端的な説明はありませんでした。とりあえず、ピリオドタイマでスキャン周波数を精密に決定する、という用途らしいので、TIMER0 でも使える、と踏んで進みます。
まず、TIMER0とTIMER2のレジスタを比較してみます。
TIMER0 | TIMER2/4/6 | |||||
---|---|---|---|---|---|---|
COUNT REGISTER | TMR0L | TMRx | ||||
PERIOD REGISTER | TMR0H | カウントはTMR0L | PRx | カウントはTMRx | ||
CONTROL REGISTER 0 | T0CON0 | TxCON | ||||
bit7 | T0EN | bit7 | '0’ | |||
bit6 | '0’ | bit6 | TxOUTPS3 | ポストスケーラ 1/1~1/16 | ||
bit5 | T0OUT (RO) | bit5 | TxOUTPS2 | |||
bit4 | T016BIT | bit4 | TxOUTPS1 | |||
bit3 | T0OUTPS3 | ポストスケーラ 1/1~1/16 | bit3 | TxOUTPS0 | ||
bit2 | T0OUTPS2 | bit2 | TMRxON | |||
bit1 | T0OUTPS1 | bit1 | TxCKPS1 | プリスケーラ 1,4,16,64 | ||
bit0 | T0OUTPS0 | bit0 | TxCKPS0 | |||
CONTROL REGISTER 1 | T0CON1 | |||||
bit7 | T0CS2 | クロックソース選択 | FOSC/4のみ | |||
bit6 | T0CS1 | |||||
bit5 | T0CS0 | |||||
bit4 | T0ASYNC | |||||
bit3 | T0CKPS3 | プリスケーラ 1~32768 | ||||
bit2 | T0CKPS2 | |||||
bit1 | T0CKPS1 | |||||
bit0 | T0CKPS0 | |||||
INTERRUPT FLAG REGISTER | PIR0 | TMR0IF bit | PIR1/2 | TMRxIF bit | ||
INTERRUPT ENABLE REGISTER | PIE0 | TMR0IE bit | PIE1/2 | TMRxEN bit |
あとは実装がどうなっているのか、それを変更することができるのか、という点がポイントになりそうです。
mTouch の AFA(TIMER2を使用)を有効にした状態で MCC でソースを生成して眺めてみます。
TIMER2 関係は mtouch_sensor.c にありました。
まず、
#include "../tmr2.h"でヘッダファイルを読み込んでいます。
次に、MTOUCH_Sensor_Scan_Initialize() で、TIMER2 のプリスケーラを指定しています。
/* * ======================================================================= * MTOUCH_SensorScan_Initialize * ======================================================================= * initialization for ADC and Timer module */ void MTOUCH_Sensor_Scan_Initialize(void) { T2CONbits.T2CKPS = 0x0; ADCON0 = (uint8_t)0; /* overwrite the ADC configuration for mTouch scan */ ADCON1 = (uint8_t)( 0x1<<7 | 0x2<<4 | 0x0 ); ADACT = (uint8_t)0; }そして、Sensor_Acq_ExecutePacket() で
/* * ======================================================================= * Sensor_Acq_ExecutePacket() * ======================================================================= */ static enum mtouch_sensor_error Sensor_Acq_ExecutePacket(mtouch_sensor_t* sensor) { /* software CVD with AFA requires interrupt enabled */ if(!(INTCONbits.GIE & INTCONbits.PEIE)) return MTOUCH_SENSOR_ERROR_interrupt_notEnabled; enum mtouch_sensor_error error = MTOUCH_SENSOR_ERROR_none; uint8_t ADCON0_temp; uint8_t ADCON1_temp; uint8_t ADACT_temp; ADCON0_temp = ADCON0; /* store the current ADC configuration */ ADCON1_temp = ADCON1; ADACT_temp = ADACT; MTOUCH_Sensor_Scan_Initialize(); Sensor_setScanFunction(sensor); /* Setup the scan function */ currentScannSensor = sensor->sensor_name; packet_counter = sensor->oversampling; packet_sample = 0; sensor_globalFlags.packet_done = 0; packet_noise = 0; TMR2_SetInterruptHandler(Sensor_Acq_ExecuteScan); /* Use timer2 to schedule the scan */ TMR2_LoadPeriodRegister(sample_period); TMR2_StartTimer(); sensor_globalFlags.interrupted = false; /* Perform packet samples */ do { while(PIR1bits.ADIF == 0) { if(sensor_globalFlags.packet_done == (uint8_t)1) break; } PIR1bits.ADIF = 0; } while(sensor_globalFlags.packet_done == 0); TMR2_StopTimer(); ADCON0 = ADCON0_temp; /* restore the previous ADC configuration */ ADCON1 = ADCON1_temp; ADACT = ADACT_temp; if(sensor_globalFlags.interrupted) { error = MTOUCH_SENSOR_ERROR_interruptedScan; } return error; }という処理を行っています。スタート、ストップ、ピリオドレジスタへのロードはいいとして、割り込みハンドラに Sensor_Acq_ExecuteScan() を割り当てているのでこれを追いかけてみます。
/* * ======================================================================= * Sensor_Acq_ExecuteScan() * ======================================================================= * Perform a single sample on the sensor. This is a local function and * requires that the ExecutePacket() function guarantees the correct PIC * and scanning configuration. * * This function is written to be independent of mainloop vs ISR context. */ static void Sensor_Acq_ExecuteScan(void) { while(ADCON0bits.ADGO); mtouch_sensor_adcsample_t result = ADRES; /* result from previous scan */ static mtouch_sensor_adcsample_t last_a,last_b; if(sensor_globalFlags.packet_done) return; if (packet_counter != (uint8_t)0) { #pragma switch time switch(packet_counter & 0x01) { case 0: Sensor_scanA();break; case 1: Sensor_scanB();break; default: break; } /* Accumulate previous sample result during the ADC conversion */ if(packet_counter!=mtouch_sensor[currentScannSensor].oversampling) { if(packet_counter & 0x01) { result = PIC_ADC_RESOLUTION - result; packet_noise += (mtouch_sensor_packetsample_t)abs(last_a-result); last_a = result; } else { packet_noise += (mtouch_sensor_packetsample_t)abs(last_b-result); last_b = result; } packet_sample += result; } packet_counter--; } else { packet_sample += result; packet_noise += (mtouch_sensor_packetsample_t)abs(last_b-result); sensor_globalFlags.packet_done = (uint8_t)1; } }長くなるのでコードは畳んでいますが、どうやらここではタイマをいじってはいないようです。 次に tmr2.c を眺めてみます。
/** Section: Global Variables Definitions */ void (*TMR2_InterruptHandler)(void); /** Section: TMR2 APIs */ void TMR2_Initialize(void) { // Set TMR2 to the options selected in the User Interface // PR2 255; PR2 = 0xFF; // TMR2 0; TMR2 = 0x00; // Clearing IF flag before enabling the interrupt. PIR1bits.TMR2IF = 0; // Enabling TMR2 interrupt. PIE1bits.TMR2IE = 1; // Set Default Interrupt Handler TMR2_SetInterruptHandler(TMR2_DefaultInterruptHandler); // T2CKPS 1:1; T2OUTPS 1:1; TMR2ON on; T2CON = 0x04; }
void TMR2_ISR(void) { // clear the TMR2 interrupt flag PIR1bits.TMR2IF = 0; if(TMR2_InterruptHandler) { TMR2_InterruptHandler(); } } void TMR2_SetInterruptHandler(void (* InterruptHandler)(void)){ TMR2_InterruptHandler = InterruptHandler; } void TMR2_DefaultInterruptHandler(void){ // add your TMR2 interrupt custom code // or set custom function using TMR2_SetInterruptHandler() }あたりで割り込み処理などを行っています。
ということは、この辺をいじってやればなんとかなるかもしれません。
というあたりで次回に続きます。
0 件のコメント:
コメントを投稿