前回に引き続き今回はMCCを使ったTimer割り込みプログラムの説明です。前回コードで書いていたwait(LEDを点滅させる間隔)をTimer割り込みで行う事を目指します。は設定する項目が多いので前回のGPIO編と違いMCCの有り難さが分かりました。前回と同じ様にPIC24FJ64GA002用のプロジェクトを作成して下さい。プロダクトネームは、”timer00″とします。
Timerの設定はシステムクロックの設定から
Timerはシステムクロックを基準に動作します。先ずはシステムクロックの確認設定から始めます。IDEには空のプロダクト、”timer00″がロードされているとして、MCCを起動して下さい。MCCは既にインストールされているとします。まだインストールされていない方は前回を参照してインストールして下さい。
- IDEツールバーに有るMCCアイコンをクリック。これでMCCが起動します。
- プロダクトを作成して最初に起動するとこの画面が出ます。矢印のボタンを押して
- ここでも矢印のボタンを押すと下記が表示されます。
- MCCのSystem欄で System Module を選ぶと
- 中央にこの画面が出ます。これがシステムクロックの設定画面です。Postscaler PLL Enable等を選択すればクロックの値を変更出来ますが、今回はデフォルトをそのまま使用します。システムクロックは4MHz。
次はタイマーの設定です。PIC24FJ64GA002は5つのタイマーを持っていますが、今回はTimer1を使います。
- Device Resoueces欄で下にスクロースしTimerフォルダーを見つけ、横の三角をクリックするとタイマー一覧が現れます。
- ここで、Timer1の左に有る緑のプラスマークをクリックすると
- 上の Project Resource欄に新たに Peripheralsフォルダーが作成されTimer1が追加されます。これを選ぶと右側にTimer1の設定画面が表示されます。一秒間隔で割り込みが発生する様Timerを設定して行きます。
- 先ずは、ここをクリックしてタイマーを有効にします。
- ここで、クロックの分周を指定します。今回は間隔が1秒と長いので分周を最大の256と指定します。
- ここでタイマーの時間を設定します。設定枠の両側に有る数字は設定出来る時間の最小値と最大値です。今回のクロックで256分周を行うと256us から 8.38s の間でタイマーの時間を設定する事ができるようです。今回は間隔を1秒としたいので、”1s”と入力して下さい。
- ここをクリックしてタイマー割り込みを有効にします。
- ここは多分、希望するタイマーの時間が11でタイマーの時間より長い時に指定するものと思います。今回は11で十分なので、”1”を入力しました。
これでTimer1の設定は完了です。次にLED点滅用のGPIOの設定を行いコードを作成します。
- Project Resource欄 SystemフォルダーのPin Moduleをクリック
- 右側のPin Manager欄のPin Module GPIO output行, Port B 15列をクリック。(鍵がロックに変わる)
- 上の段いRB15が追加され、Outputにチェックが入っている事を確認。
- 最後にGenarate ボタンを押すとコードが作成されます。作成の状況が右側の画面に表示されます。最後に、”Generation complete.”と表示されて作成完了です。
今回のメインは、”tmr1.c”です。MCCが作成したコードは各関数を外部からコールするとタイマー1を操作出来る様なっています。下記は関数の例です。
機能 | 関数 |
---|---|
初期設定 | TMR1_Initialize (void) |
タイマー開始 | TMR1_Start( void ) |
タイマー停止 | TMR1_Stop( void ) |
カウンターの書き込み | TMR1_Period16BitSet( uint16_t value ) |
カウンターの読み込み | TMR1_Period16BitGet( void ) |
ただ特徴的なのが、割り込み作業の実行(定義?)方法です。
tmr1.c
/**
Section: Included Files
*/
#include <stdio.h>
#include "tmr1.h"
/**
Section: File specific functions
*/
void (*TMR1_InterruptHandler)(void) = NULL;
void TMR1_CallBack(void);
/**
Section: Data Type Definitions
*/
/** TMR Driver Hardware Instance Object
@Summary
Defines the object required for the maintenance of the hardware instance.
@Description
This defines the object required for the maintenance of the hardware
instance. This object exists once per hardware instance of the peripheral.
Remarks:
None.
*/
typedef struct _TMR_OBJ_STRUCT
{
/* Timer Elapsed */
volatile bool timerElapsed;
/*Software Counter value*/
volatile uint8_t count;
} TMR_OBJ;
static TMR_OBJ tmr1_obj;
/**
Section: Driver Interface
*/
void TMR1_Initialize (void)
{
//TMR1 0;
TMR1 = 0x00;
//Period = 1.000064 s; Frequency = 2000000 Hz; PR1 7812;
PR1 = 0x1E84;
//TCKPS 1:256; TON enabled; TSIDL disabled; TCS FOSC/2; TSYNC disabled; TGATE disabled;
T1CON = 0x8030;
if(TMR1_InterruptHandler == NULL)
{
TMR1_SetInterruptHandler(&TMR1_CallBack);
}
IFS0bits.T1IF = false;
IEC0bits.T1IE = true;
tmr1_obj.timerElapsed = false;
}
void __attribute__ ( ( interrupt, no_auto_psv ) ) _T1Interrupt ( )
{
/* Check if the Timer Interrupt/Status is set */
//***User Area Begin
// ticker function call;
// ticker is 1 -> Callback function gets called everytime this ISR executes
if(TMR1_InterruptHandler)
{
TMR1_InterruptHandler();
}
//***User Area End
tmr1_obj.count++;
tmr1_obj.timerElapsed = true;
IFS0bits.T1IF = false;
}
void TMR1_Period16BitSet( uint16_t value )
{
/* Update the counter values */
PR1 = value;
/* Reset the status information */
tmr1_obj.timerElapsed = false;
}
uint16_t TMR1_Period16BitGet( void )
{
return( PR1 );
}
void TMR1_Counter16BitSet ( uint16_t value )
{
/* Update the counter values */
TMR1 = value;
/* Reset the status information */
tmr1_obj.timerElapsed = false;
}
uint16_t TMR1_Counter16BitGet( void )
{
return( TMR1 );
}
void __attribute__ ((weak)) TMR1_CallBack(void)
{
// Add your custom callback code here
}
void TMR1_SetInterruptHandler(void (* InterruptHandler)(void))
{
IEC0bits.T1IE = false;
TMR1_InterruptHandler = InterruptHandler;
IEC0bits.T1IE = true;
}
void TMR1_Start( void )
{
/* Reset the status information */
tmr1_obj.timerElapsed = false;
/*Enable the interrupt*/
IEC0bits.T1IE = true;
/* Start the Timer */
T1CONbits.TON = 1;
}
void TMR1_Stop( void )
{
/* Stop the Timer */
T1CONbits.TON = false;
/*Disable the interrupt*/
IEC0bits.T1IE = false;
}
bool TMR1_GetElapsedThenClear(void)
{
bool status;
status = tmr1_obj.timerElapsed;
if(status == true)
{
tmr1_obj.timerElapsed = false;
}
return status;
}
int TMR1_SoftwareCounterGet(void)
{
return tmr1_obj.count;
}
void TMR1_SoftwareCounterClear(void)
{
tmr1_obj.count = 0;
}
/**
End of File
*/
- タイマー割り込みが発生すると、ISRとして定義された68行目の void attribute ( ( interrupt, no_auto_psv ) ) _T1Interrupt ( ) が実行されます。
- この中でユーザーの定義する割り込み処理はTMR1_InterruptHandler()関数によって実行されます。しかし関数は、 if(TMR1_InterruptHandler)によって真の時に実行する様定義されています。
- TMR1_InterruptHandler は実行したい関数のポインターで上記の”if”文は、”実行したい関数が定義されていたら、それを実行しなさい”となります。それでは、TMR1_InterruptHandler は何処で定義されているか。それは、120行の void TMR1_SetInterruptHandler(void (* InterruptHandler)(void)) です。この関数がMCCが作成したコードのキー関数と思います。
- この関数の引数 void (* InterruptHandler)(void) は実行したい割り込み処理用関数のポインターで、この関数内で、TMR1_InterruptHandler = InterruptHandler; としてTMR1_InterruptHandlerを定義しています。
- としてここで実行したい関数のポインターを、TMR1_InterruptHandlerにセットしています。
このコーディングの利点は、”tmr1.c” を変更せずに、ISRをコーディング出来る点です。
例えば、
- タイマー割り込みで実行したい関数を、”main.c”にコーディング
- そこから、引数をその関数のポインターとして、関数TMR1_SetInterruptHandler(void (* InterruptHandler)(void))をコールする。
- これにより、TMR1_InterruptHandler に実行したい関数が登録される。
- その状態でタイマー割り込みがかかれば、以下の様になる。
- 68行目の void attribute ( ( interrupt, no_auto_psv ) ) _T1Interrupt ( ) が実行され
- if(TMR1_InterruptHandler) に到達する。
- TMR1_InterruptHandlerに実行したい関数のポインターが登録されいるので
- 次の行、TMR1_InterruptHandler()が実行される。この関数は実は、”main.c”でコーディングしたISR。
これを元に、”main.c”をコーディングすると以下の様になります。
main.c
/**
Section: Included Files
*/
#include "mcc_generated_files/system.h"
int fl;
void myTimerISR(void)
{
fl = !fl;
}
/*
Main application
*/
int main(void)
{
// initialize the device
SYSTEM_Initialize();
TMR1_SetInterruptHandler (myTimerISR);
TMR1_Start();
fl = 1;
while (1)
{
LATB = 0xFFFF;
while(fl) ;
LATB = 0x0000;
while(!fl) ;
// Add your application code
}
return 1;
}
/**
End of File
*/
- 6行:外部変数 ”fl”を定義。割り込みが発生するとこの値を変更します。
- 8行:割り込み関数、void myTimerISR(void) の定義。割り込みの度にflの値を反転します。
- 19行:ここで割り込み関数を TMR1_SetInterruptHandler (myTimerISR); の様に定義しています。
- 20行:タイマーの開始
- 25行:LED点灯
- 26行:fl=0になるまで待ちます。タイマー割り込みがかかり、flが0になると
- 27行:LED消灯
- 28行:fl=1になるまで待ちます。タイマー割り込みがかかり、flが1になると 20行へ
これでLEDが1秒に1回点滅します。この様に、”trm1.c”を全く変更無くコーディング出来ます。ちなみに使用したPIC側の回路は前回と同じ。
次回は
今回はMCCの便利さを痛感しました。タイマー設定に関し殆どユーザーのコーディング無しです。PICには他にも、I2C SPI 外部割り込み等色々な機能が有ります。次回はこれらの機能に付いてMCCがどの様に働くか確かめて行きたいと思います。