平成27年3月28日
MPLAB Harmonyを変則的に使用する(その2)
先日はMPLAB Harmonyの使い難い部分(主にファイルと関数の位置関係の固定化)を避けてMPLAB Harmonyのメリットであるハードウェアの初期化関数を出力してくれる機能を利用する方法を検討し、変則的ながらも何とか使う方法を見つけました。
しかし、Harmonyが持つ周辺ライブラリの多くがインライン関数として実装されているためかブレークポイントを設定できない問題はそのまま残っています。また、このライブラリ自体も個人的には使い易いのか、使い難いのか判断しかねる代物です。
具体的に気になる点を列記すると、
- 後の拡張をも考えて実装してあるのか、現時点では無駄な引数(現時点では固定値を設定する)が多くある
- 非常に多くの関数が登録されており、欲しい関数名を覚えるのが困難
- ライブラリ関数の多くにブレークポイントを設定できない(インライン関数として実装されているため?)
特に多くの関数名を記憶するのは非常に苦痛です。多くは周辺機能レジスタの設定変更に関するものです。
周辺機能の動作を理解するために個々のレジスタの意味を理解するのは実務として避けられないのですが、それを操作する関数まで覚えるのは更に苦痛でしかない気がします。どの道、周辺機能を動作検証するために最初は個々のレジスタをアクセスした簡単なプログラムを記述します。そのときにレジスタの名前やビットの省略名を参照しますので、直接これらのレジスタ・ビットをアクセスするプログラムの方が馴染み易いのです。時間が経った後は、さすがに見直す前に時間が掛かりますが。
これは私が回路設計者出身であるためかもしれません。純粋なソフト屋なら違う意見になるかもしれません。
いずれにしても、レジスタ・ビットを直接アクセスするにしろ関数経由にしても、その動作内容が分からなければ無意味です。結果として最終的にはデータシートを読み直してプログラムを変更することに変わりは無く、個々のレジスタ・ビットの操作を関数に置き換えるのは関数名が多少の動作説明になる程度のメリットでしかない気がします。それならコメントでも十分です。
という考えからMPLAB Harmonyの周辺ライブラリを使用しない(=MPLAB Harmonyを使用しない)で、プログラムを記述してみます。
特に難しいことではなく、旧来はそのようにしていただけです。もし初期化関数のみHarmonyを使いたければ、コンパイルしてオブジェクトをリンクするようにします。
しかし、CPUもそれなりに複雑になっていますので通常使用する状態にまで設定する雛形を作成します。
PIC32の場合、一般的な初期状態にするためには以下のような操作が必要です。
- ROM/RAMのウエイト数を動作周波数に合わせて設定する
- 必要であれば周辺バスクロックを設定する
- キャッシュ機能があればキャッシュを有効にする
- 割り込みモードをマルチベクタモードに設定する
- AD入力と兼用のデジタル端子をデジタル端子に設定する
- グローバル割り込みを許可する(周辺機能の初期化が全て終了したあとで)
これを幾つかのファイルに収めます。最初は、PIC32で共通の定義を保管します。
デバイスの仕様で決められている定数と割り込み関連の記述を単純化するためのマクロです。
pic32def.h
#ifndef PIC32DEF_H
#define PIC32DEF_H
#include <xc.h>
/* pic32のデバイス仕様(将来的には変更になる可能性も)
*/
#define SYS_DEVCON_PIC32MX_MAX_PB_FREQ 80000000UL
#define ROM_0WAIT_FREQ 30000000Ul
#define ROM_1WAIT_FREQ 60000000Ul
/* 割り込み管理
*/
#define DI() __builtin_disable_interrupts()
(H27/ 4/ 1 追記変更)
/* __builtin_get_isr_state()はbit0〜2がIPL, bit3がIEの値を返す
*/
#define GI() (__builtin_get_isr_state() & 0x08)
#define GIDI(flag) flag = GI(); DI()
// GI()マクロはもう少し端的な記述が見つかったので変更します。
// GIDI()マクロはマクロの引数に結果を返すのは他のマクロとの整合性が悪いので変更します。
#define GI() (_CP0_GET_STATUS() & 0x01) //0x01はIE bitの位置
#define GIDI() GI();DI()
(追記変更ここまで)
#define EI() __builtin_enable_interrupts()
#define RI(flag) (flag) ? __builtin_enable_interrupts() : \
__builtin_disable_interrupts()
#endif //PIC32DEF_H
次はプロジェクトごとの変数を格納します。現時点では動作周波数と周辺バスクロックのみです。
system_config.h
#ifndef SYS_CONFIG_H
#define SYS_CONFIG_H
#define SYS_CLK 80000000uL
#define PB_CLK SYS_CLK
#endif //SYS_CONFIG_H
最後がメイン関数を含むプロジェクトファイルです。MPLAB Harmonyに含まれるSYS_PerrformanceConfig()関数の仕様に合わせて初期設定するようにしてあります。
この雛形をコピーしてプロジェクトを書いていけば、MPLAB Harmonyを使用しなくてもそれ程不便はありません。
#include <stdint.h>
#include <stdbool.h>
#include "system_config.h"
#include "pic32def.h"
//#define SIM_DBG
#ifndef SIM_DBG
/*** DEVCFG0 ***/
#pragma config DEBUG = ON
#pragma config ICESEL = ICS_PGx2
#pragma config PWP = 0xff
#pragma config BWP = OFF
#pragma config CP = OFF
/*** DEVCFG1 ***/
#pragma config FNOSC = PRIPLL
#pragma config FSOSCEN = OFF
#pragma config IESO = OFF
#pragma config POSCMOD = HS
#pragma config OSCIOFNC = ON
#pragma config FPBDIV = DIV_1
#pragma config FCKSM = CSDCMD
#pragma config WDTPS = PS1048576
#pragma config FWDTEN = OFF
/*** DEVCFG2 ***/
#pragma config FPLLIDIV = DIV_4
#pragma config FPLLMUL = MUL_20
#pragma config FPLLODIV = DIV_1
#pragma config UPLLIDIV = DIV_12
#pragma config UPLLEN = OFF
/*** DEVCFG3 ***/
#pragma config USERID = 0xffff
#pragma config FSRSSEL = PRIORITY_7
#pragma config FMIIEN = ON
#pragma config FETHIO = ON
#pragma config FCANIO = ON
#pragma config FUSBIDIO = ON
#pragma config FVBUSONIO = ON
#endif//SIM_DBG
/* MPLAB HarmonyのSYS_DEVCON_PerformanceConfig()関数の置き換え
*/
void sysPerrformanceConfig( unsigned int sysclk )
{
//キャッシュを有効にする(キャッシュ機能の無いCPUでは削除する)
/* キャッシュの管理はCPUではなくCP0にある。CP0のレジスタ16セレクト0
* ConfigRegのbit0〜2にあるK0レジスタがそれ。
* 詳細はPIC32日本語マニュアルセクション2「M4Kコア搭載デバイス用CPU」
*/
register unsigned long tmp;
asm("mfc0 %0,$16,0" : "=r"(tmp));
tmp = (tmp & ~7) | 3; //2:キャッシュ無効, 3:有効
asm("mtc0 %0,$16,0" :: "r" (tmp));
//ROMへアクセスするときのウェイト数
int wait;
if(sysclk <= ROM_0WAIT_FREQ) wait = 0;
else if (sysclk <= ROM_1WAIT_FREQ) wait = 1;
else wait = 2;
CHECONbits.PFMWS = wait;
//RAMへアクセスするときのウェイト数
BMXCONbits.BMXWSDRM = 0;
//PBDIVの設定
int pbdiv = (sysclk > SYS_DEVCON_PIC32MX_MAX_PB_FREQ) ? 2 : 1;
OSCCONbits.PBDIV = pbdiv;
}
void init(void)
{
sysPerrformanceConfig(SYS_CLK);
INTCONbits.MVEC = 1; //割り込みマルチベクタ
//ここの他の初期化関数を順次追加
}
int main(void)
{
init();
EI();
for(;;){
//ここに処理内容を記述
}
return 0;
}