概要にゃ
リンリンにゃ.しもべがなんかアレなので,今日はリンリンがマイコンやるときにLEDをチカチカさせるあれを作るにゃ.センモンヨーゴでエルチカとか言うらしいにゃ.リンリン,がんばるにゃ!
設計にゃ
まず,作るもののイメージにゃ.「イメージしろ」って,櫂クンも言ってたにゃ.最初のイメージですべてが決まるにゃ.イメージが固まったら,あとは説明書見ながら作るだけなので簡単にゃ!
作るもののイメージにゃ
イメージするにゃ.
階層 | 内容 |
---|---|
アプリケーション | LEDを光らせたいとこで光らせるにゃ |
API | ドライバはめんどいから使いやすくするにゃ |
ドライバ | マイコンのレジスタを操作するにゃ |
マイコン | どこのご家庭にもある普通のマイコンにゃ |
LED | マイコンにつながってるにゃ |
にゃん♪イメージできたから,ほぼ完成と言っても,過言ではないにゃ!
あとは,うにゃーと書いて,にゃっとコンパイルして,にゃふんと動かすだけにゃ.
LEDとマイコン
最近のマイコンは,ものによっては,LEDをトランジスタかまさずに直接つないでも大丈夫だったりするみたいにゃ.マイコンとLEDがどうつながってるかは回路図を見るといいらしいけど,リンリン,難しいことはわかんないにゃ.多分,Hi-Zで消灯,Loで点灯じゃないかにゃ?回路図見なくても,適当に試してみれば,多分,どれかが正解にゃ!
ドライバ
イメージを表現するとこんな感じかにゃあ?ドライバなので,マイコンごとに,設計が必要かもしれないにゃ.ヘッダの部分だけならそうでもないかもしれないけどにゃあ?レジスタと1対1で対応する,ラッパみたいなもんということにしておくにゃ.マイコンの説明書見ながら作るにゃ.
#ifndef PWMDRIVER_H
#define PWMDRIVER_H
/*
* ドライバ
* LED1チャンネル当たり,この構造体を1個用意するにゃ.
*/
typedef struct {
void (*init)(uint16_t pcsr, uint16_t pdut); // 初期化
void (*setPCSR)(uint16_t pcsr); // PCSRレジスタ設定
void (*setPDUT)(uint16_t pdut); // PDUTレジスタ設定
} PwmDrv_st;
#endif /* PWMDRIVER_H */
API
APIは次の表みたいな感じかにゃあ?APIはめんどいドライバを扱いやすくするにゃ.ついでに付加機能も付けるにゃ.
項目 | 内容 |
---|---|
LEDの点灯/消灯 | 個別にやりたいにゃ |
初期化 | めんどいから一発でやりたいにゃ |
ドライバとの違い | 点灯/消灯を明確にするにゃ |
付加機能 | マスターイネーブル付けるにゃ |
マスタイネーブルは各LEDの点灯/消灯状態とは別管理で,すべてのLEDを一括管理にするにゃ.
#ifndef LEDAPI_H
#define LEDAPI_H
/*
* LEDのチャンネル
* 使うLEDのチャンネル数だけ列挙するにゃ.
* めんどいから1個しか書かないけど,あとは大体分かるにゃあ?
*/
typedef enum {
LedApiCH_0 = 0,
xLedApiCH_Num,
} LedApiCH;
/*
* LEDの状態
* LEDは点灯と消灯の2状態しかないにゃ
*/
typedef enum {
LedState_OFF = 0,//LED消灯にゃ
LedState_ON,//LED点灯にゃ
} LedState;
/*
* API
* 違う仕様のマイコンを使うときでも,このヘッダを流用すれば,上層はそのまま使えるはずにゃ!
*/
typedef struct {
void (*Init)(void); //初期化
void (*set)(const LedApiCH ch, LedState state); // チャンネルchのLEDの点灯/消灯の制御
struct {
void (*setEn)(void); //LEDを使うにゃ!
void (*setDe)(void); //LEDを使わないにゃ!
} ctrl;
void (*setPeriod)(uint16_t period); //周波数設定
uint16_t (*setBright)(uint16_t bright); //明るさ設定
} LedApi_st;
#endif /* LEDAPI_H */
アプリケーション
APIで楽々LEDを制御できるはずにゃ.
#include "LedApi.h"
extern LedApi_st LedApi;
int main() {
InitPort();
LedApi.init(); // LED APIの初期化にゃ
LedApi.set(LedApiCH_0, LedState_ON); // LEDがチカチカするにゃ
}
APIの他の関数にゃ?そんなの,使いたい子が勝手に使えば良いにゃ.
実装
あとは,おまけみたいなもんにゃ.
API
APIの中身は大体,こんなもんかにゃ.
#include "PwmDriver.h"
#include "LedApi.h"
#define LED_BRIGHT (0x0FFD)
#define LED_PERIOD (0x3FFD)
extern PwmDrv_st PwmDrv;
/*******************************************************************************
* API関数定義にゃ.
* staticで書いてても,ポインタ経由なら大丈夫にゃ.
******************************************************************************/
static void Init(void);
static void set(const LedApiCH ch, LedState state);
static void ctrl_setEn(void);
static void ctrl_setDe(void);
static void setPeriod(uint16_t period);
static uint16_t setBright(uint16_t bright);
LedApi_st LedApi = {
.Init = Init,
.set = set,
.ctrl = {
.setEn = ctrl_setEn,
.setDe = ctrl_setDe,
},
.setPeriod = setPeriod,
.setBright = setBright,
};
/*******************************************************************************
* 外部非公開のいろいろにゃ.
******************************************************************************/
typedef enum {
LedApiCtrl_Disable = 0,//LED使わないにゃ
LedApiCtrl_Enable,//LED使うにゃ
} LedApiCtrl;
/*
* APIで扱うドライバにゃ
*/
typedef struct {
PwmDrv_st *pDrv; // ドライバのポインタ
} LedApiCfg_st;
/*
* APIで扱うドライバのポインタ配列にゃ
* 初期化関数で実際にポインタを設定するにゃ.
* 配列にしとけば,多い日も安心にゃ.
*/
static LedApiCfg_st LedApiCfg[xLedApiCH_Num];
/*
* APIで管理すべき情報にゃ
*/
typedef struct {
union {
uint16_t LedOff; // 消灯のときにレジスタに書く値にゃ
uint16_t Period;
};
union {
uint16_t LedOn; // 点灯のときにレジスタに書く値にゃ
uint16_t Bright;
};
struct {
LedState state; // 点灯/消灯の管理はAPIがやるにゃ
} ch[xLedApiCH_Num];
LedApiCtrl ctrl; // マスタイネーブルにゃ
} LedApiInfo_st;
/*
* APIで管理している情報は,基本的に外部非公開にゃ.
* 必要があれば,API関数経由で何とかするにゃ.
*/
static LedApiInfo_st LedApiInfo = {
.Period = LED_PERIOD,
.Bright = LED_BRIGHT,
.ch = {
[LedApiCH_CH0] = {
.state = LedState_OFF, // 初期状態でLEDは消灯にゃ
},
},
.ctrl = LedApiCtrl_Enable, // 初期状態でLEDを使うにゃ
};
static void all_update(void);
static void ch_update(const LedApiCH ch);
/*******************************************************************************
* API関数
******************************************************************************/
/*
* 初期化
* すべてのLEDを初期化するにゃ
*/
static void Init(void) {
// 確か,この書き方ではポインタを初期値で入れれなかったと思ったにゃ.
// 今回はすべてのLEDドライバがPwmDrv_stで表現できたとするにゃ.
// いろんなのが混ざってるときは,void*にしたり,ドライバの種別ごとに処理を
// 分けたり,いろいろめんどいにゃあ.
LedApiCfg[LedApiCH_0].pDrv = &PwmDrv;
// nullチェックとか,めんどいにゃあ.
for(uint8_t ch = 0; ch < xLedApiCH_Num; ch++) {
LedApiInfo.ch[ch].pDrv->init(LedApiInfo.Period, LedApiInfo.Bright);
}
}
/*
* LEDの点灯/消灯
* const LedApiCH ch 操作するチャンネル
* LedState state 点灯/消灯の指定
*/
static void set(const LedApiCH ch, LedState state) {
LedApiInfo.ch[ch].state = state;
ch_update(ch);
}
/*
* LED使うにゃ!
*/
static void ctrl_setEn(void) {
LedApiInfo.ctrl = LedApiCtrl_Enable;
all_update();
}
/*
* LED使わないにゃ!
*/
static void ctrl_setDe(void) {
LedApiInfo.ctrl = LedApiCtrl_Disable;
all_update();
}
/*
* 周波数設定にゃ
* めんどいから,全部のLEDで同じ設定やるにゃ.
*/
static void setPeriod(uint16_t period) {
LedApiInfo.Period = period;
for(uint8_t ch = 0; ch < xLedApiCH_Num; ch++) {
LedApiCfg[ch].pDrv->setPCSR(LedApiInfo.Period);
}
}
/*
* 明るさ設定にゃ
* この値は制限があるにゃ.詳細はマイコンの説明書見るにゃ.
*/
static uint16_t setBright(uint16_t bright) {
LedApiInfo.Bright = (LedApiInfo.Period < bright) ? LedApiInfo.Period : bright;
all_update();
return LedApiInfo.Bright;
}
/*******************************************************************************
* 内部関数
******************************************************************************/
/*
* 全部のLED状態のアップデートにゃ!
*/
static void all_update(void) {
for(uint8_t ch = 0; ch < xLedApiCH_Num; ch++) {
ch_update((LedApiCH)ch);
}
}
/*
* LED状態のアップデートにゃ!
* const LedApiCH ch アップデートするチャンネル
* LedApiInfoの値はいくら操作しても,LEDは制御されないにゃ.
* 制御するには,ドライバを使わなければならないにゃ.
*/
static void ch_update(const LedApiCH ch) {
// LEDが点灯するのは,LEDを使うときで,点灯状態にしたときだけにゃ
if((LedApiInfo.ch[ch].state == LedState_ON) && (LedApiInfo.ctrl == LedApiCtrl_Enable)) {
LedApiCfg[ch].pDrv->setPDUT(LedApiInfo.LedOn);
}
else {
LedApiCfg[ch].pDrv->setPDUT(LedApiInfo.LedOff);
}
}
ドライバ
ドライバの中身はこんな感じかにゃ?これは,環境によって,中身が大胆に変わるかもしれないにゃ.ドライバでは,基本的にレジスタを設定することしか書かないにゃ.いろいろ書くのはめんどいからにゃあ?
#include "mcu.h"
#include "PwmDriver.h"
#define PWM_PCSR (FM3_BT7_PWM->PCSR)
#define PWM_PDUT (FM3_BT7_PWM->PDUT)
/*******************************************************************************
* ドライバ定義
******************************************************************************/
static void init(uint16_t pcsr, uint16_t pdut);
static void setPCSR(uint16_t pcsr);
static void setPDUT (uint16_t pdut);
PwmDrv_st PwmDrv = {
.init = init,
.setPCSR = setPCSR,
.setPDUT = setPDUT ,
};
/*******************************************************************************
* ドライバ関数
******************************************************************************/
static void init(uint16_t pcsr, uint16_t pdut) {
setPCSR(pcsr);
setPDUT(pdut);
}
static void setPCSR(uint16_t pcsr) {
PWM_PCSR = pcsr;
}
static void setPDUT (uint16_t pdut) {
PWM_PDUT = pdut;
}
マイコン構成
マイコンの各ピンの機能の指定にゃ.機能の指定と,ドライバの初期化はちょっと意味が違うからにゃ,分けて書いた方が良いと思うにゃ.
リンリン,難しいことはわかんないから,マイコンの説明書に書いてあったとおりにゃ.
#include "mcu.h"
void InitPort(void) {
// よくわかんないけど,説明書にこうしろって書いてあったにゃ
FM3_GPIO->PFR2 |= 0x0008U;
bFM3_GPIO_EPFR05_TIOA7E0 = 0;
bFM3_GPIO_EPFR05_TIOA7E1 = 1;
bFM3_GPIO_EPFR12_TIOA10E0 = 1;
bFM3_GPIO_EPFR12_TIOA10E1 = 1;
FM3_BT7_PWM->TMCR = 0x0010;
FM3_BT7_PWM->TMCR2 = 0x00;
FM3_BT7_PWM->PCSR = 0x2000;
FM3_BT7_PWM->PDUT = 0x1000;
FM3_BT7_PWM->TMCR |= 0x0002;
}
謎のヘッダ
mcu.hは,割り込みベクタとかレジスタの名前の定義とか,システム設定とかが書いてあるにゃ.この辺は機種依存なので,各マイコンに合わせて,どっかから調達するにゃ.今回のは見て分かるとおり,FM3になってるにゃ.サンプルソースがどっかにあるから,それを使うにゃ.
にんともかんともにゃんにゃん
にゃん♪リンリン,できちゃったにゃ!みんな,こんなんわざわざ書いてるのにゃ?大変だにゃあ.
LEDがチカチカしないように見える子は,オシロを使うと良いと聞くにゃ.備中松山城とかに持っていけばいいのかにゃ?リンリン,よくわかんないにゃ.
基板を振れば,もしかしたらチカチカしてるの分かるかもしれないにゃ.