平成27年3月23日
MPLAB Harmonyを変則的に使用する
MPLAB Harmonyでは、プロジェクトの雛形と基本的なソースプログラムを自動的に出力してくれます。
この機能は確かに便利です。特にハードウェアの初期化部分を出力してくれるのは非常に魅力的です。初期のデバッグではこの部分の記述誤りを正しく直す作業が主体になりますが、大幅な工数削減が期待できます。
反面使い難い面もあります。プログラムにおけるファイルと関数の配置管理がMPLAB Harmonyによって固定されてしまう点と、一度MPLAB
Harmony Configratorを使ってプログラムを作成し、その後に再びMPLAB Harmony Configratorを再起動してデバイスやサービスを追加変更すると、その度にMPLAB
Harmony Configratorが出力する各ファイルのマージ動作を要求されます(ファイルが変更されている場合のみ)。
MPLAB Harmonyではmain.cやapp.cなどユーザーがコードを追加することを前提にしているファイルがあり、更にデバイスのバグ対策で初期化関数に追加的なコードを書き加える必要があるなど、複数のファイルに追加コードが入ります。これらのファイル全てを人手でマージするのは非常に手間が掛かります。結果として、MPLAB
Harmony Configratorを使用する場合は最初に使用する全てのデバイス・サービスを登録し、その後は出来るだけMPLAB Harmony
Configratorを再起動しないで使用することになります。
私にはこのような使い方が非常に不便に感じます。このため、当初はMPLAB Harmony Configratorを使用しないことを考えていたのですが、手動でプロジェクトを記述するのも手間がかかるので思い直しました。MPLAB
Harmony Configratorを使っても、その後デバイス・サービスの追加によって発生する手作業がなるべく少なく機械的な操作で済むような使い方を考えます。
で、考えた方法はMPLAB Harmonが出力する主要なファイルにはユーザーコードを追加しない方法です。これによって二回目以降のMPLAB
Harmony Configratorの動作でもファイルのマージが不要になるか、機械的な処理で済むようになります。
具体的な例として、タイマを1CHだけ使用するプロジェクトを新規に作成して、その後UARTを追加するプロジェクトで説明します。
プロジェクトを作成後、下記のような新規ファイルをプロジェクトに追加します。
最初のファイルtry1.cはmain.c, system_tasks.c, app.cの置き換えとして動作します。置き換えられる元ファイルはコンパイル動作で邪魔にならないだけの最低限の変更のみに留めることで、マージ動作を単純で機械的な操作に出来ます。
try1.c
#include <stddef.h> // Defines NULL
#include <stdbool.h> // Defines true
#include <stdlib.h> // Defines EXIT_FAILURE
#include "system/common/sys_module.h" // SYS function prototypes
#include "system_config.h"
#include "system_definitions.h"
//アプリケーション関連のインクルード
/* SYS_Initialize ( NULL );相当
*/
void Init(void)
{
extern const SYS_DEVCON_INIT sysDevconInit;
//SYS_CLK_Initialize(&sysClkInit);
sysObj.sysDevcon = SYS_DEVCON_Initialize(SYS_DEVCON_INDEX_0, (SYS_MODULE_INIT*)&sysDevconInit);
SYS_DEVCON_PerformanceConfig(SYS_DEVCON_SYSTEM_CLOCK);
SYS_INT_Initialize();
//ここにハード関連の初期化関数が並ぶ
TMR4_Init();
//アプリ関連の初期化があればここに記述する。
}
int main ( void )
{
Init();
SYS_INT_Enable();
while ( true )
{
/* SYS_Tasks ( ); 相当
*/
SYS_DEVCON_Tasks(sysObj.sysDevcon);
//ここにアプリケーションプログラム呼出を記述する
}
/* Execution should not come here during normal operation */
return ( EXIT_FAILURE );
}
既存ファイルでは、maim.cを下記のように変更します。つまり、処理内容全てをマクロによって削除します。これでmain()関数が複数になる問題はなくなります。残りのsystem_tasks.c,
app.cは削除してもいいのですが、面倒なのでそのまま残して起きます。プログラムが完成して余計なコードを残したくない状態の時に削除します。
main.c
//先頭部分のコメントは省略してある。
#if 0
// *****************************************************************************
// *****************************************************************************
// Section: Included Files
// *****************************************************************************
// *****************************************************************************
#include <stddef.h> // Defines NULL
#include <stdbool.h> // Defines true
#include <stdlib.h> // Defines EXIT_FAILURE
#include "system/common/sys_module.h" // SYS function prototypes
// *****************************************************************************
// *****************************************************************************
// Section: Main Entry Point
// *****************************************************************************
// *****************************************************************************
int main ( void )
{
/* Initialize all MPLAB Harmony modules, including application(s). */
SYS_Initialize ( NULL );
while ( true )
{
/* Maintain state machines of all polled MPLAB Harmony modules. */
SYS_Tasks ( );
}
/* Execution should not come here during normal operation */
return ( EXIT_FAILURE );
}
/*******************************************************************************
End of File
*/
#endif //0
次にタイマ関連の機能を一つのファイルにまとめます。これは、その方がプログラムを管理しやすいと思うからです。
既存ファイルであるsystem_init.c, system_interrupt.cから初期化関数と割り込み処理ルーチンをコピーして新規ファイルtmr4.cとしてプロジェクトに追加します。このとき、
関数名は元ファイルとバッティングしないように変更しておきます。これで、関数名の重複が起きません。ここで示したソースは主要なパラメータをマクロに置き換えてありますが、基本的には上記の元ファイルの内容と同じです。唯一の例外が初期化関数内にタイマ開始指示が追加されています。
tmr4.c
#include <xc.h>
#include <sys/attribs.h>
#include "system_definitions.h"
#define TMR_ID TMR_ID_4
#define INT_SOURCE INT_SOURCE_TIMER_4
#define INT_VECTOR INT_VECTOR_T4
#define INT_PRIORITY INT_PRIORITY_LEVEL1
#define INT_SUBPRIORITY INT_SUBPRIORITY_LEVEL0
#define INTERVAL 10000
#define _TIMER_VECTOR _TIMER_4_VECTOR
#define IPL ipl1
//void DRV_TMR0_Initialize(void)
void TMR4_Init(void)
{
/* Setup TMR0 Instance */
PLIB_TMR_Stop(TMR_ID); /* Disable Timer */
PLIB_TMR_ClockSourceSelect(TMR_ID, TMR_CLOCK_SOURCE_PERIPHERAL_CLOCK); /* Select clock source */
PLIB_TMR_PrescaleSelect(TMR_ID, TMR_PRESCALE_VALUE_8); /* Select prescalar value */
PLIB_TMR_Mode16BitEnable(TMR_ID); /* Enable 16 bit mode */
PLIB_TMR_Counter16BitClear(TMR_ID); /* Clear counter */
PLIB_TMR_Period16BitSet(TMR_ID, INTERVAL); /*Set period */
/* Setup Interrupt */
PLIB_INT_SourceEnable(INT_ID_0, INT_SOURCE);
PLIB_INT_VectorPrioritySet(INT_ID_0, INT_VECTOR, INT_PRIORITY);
PLIB_INT_VectorSubPrioritySet(INT_ID_0, INT_VECTOR, INT_SUBPRIORITY);
//TMRx開始
PLIB_TMR_Start(TMR_ID); //この行はDRV_TMR0_Initialize()にはないが新規に追加した
}
//void __ISR(_TIMER_4_VECTOR, ipl1) _IntHandlerDrvTmrInstance0(void)
void __ISR(_TIMER_VECTOR, IPL) TMR4_ISR(void)
{
PLIB_INT_SourceFlagClear(INT_ID_0,INT_SOURCE);
Nop();
}
この変更と同時に既存ファイルsytem_interrupt.cを下記のように変更します。ここでも__ISR記述が重複するので、これをマクロによって削除します。
sytem_interrupt.c
//先頭部分のコメントは省略
#if 0
// *****************************************************************************
// *****************************************************************************
// Section: Included Files
// *****************************************************************************
// *****************************************************************************
#include <xc.h>
#include <sys/attribs.h>
#include "app.h"
#include "system_definitions.h"
// *****************************************************************************
// *****************************************************************************
// Section: System Interrupt Vector Functions
// *****************************************************************************
// *****************************************************************************
void __ISR(_TIMER_4_VECTOR, ipl1) _IntHandlerDrvTmrInstance0(void)
{
PLIB_INT_SourceFlagClear(INT_ID_0,INT_SOURCE_TIMER_4);
}
/*******************************************************************************
End of File
*/
#endif //0
以上で変更は終了です。ファイルツリーは下記のようにソースファイルとしてtry1.c, tmr4.cの二つが追加されています。
この状態でMPLAB Harmony Configratorを起動し、UARTを1CH追加します。
追加によって既存のファイルに必要な情報が追加され、マージが必要なファイルは順番にマージ画面が表示されます。
今回はsystem_interrupt.cに割り込み処理が追加されたので、このファイルのみがマージの対象になります。先に追加してある[#if
0], [#endif //0]を残したままUARTの割り込み処理を追加します。追加位置はマクロによって実質的に無効な領域ですので、後から別ファイルにコピーするか有効になるようにマクロを変更します。 UARTもTimer同様に関連関数を一つのファイルにまとめた方が良いと思いますが、ここは同じ手順になるので割愛します。
以上の方法ならMPLAB Harmony Configratorを複数回起動しても、ファイルのマージ作業はほぼsystemu_interrupt.cのみで済ませることが出来ます。最終的にプロジェクト全体の検証が終わったら、実質的に未使用であるmain.c,
system_interrupt.cなどの不要ソースを削除すればバイナリーファイル中に不要なオブジェクトを持ち込む精神衛生上の弊害を取り除くことが出来ます。これらのファイル削除はMPLAB
Harmony Configratorを起動するたびに生成されるので、最後でのみ有意です。