組み込みシステムにおける C 言語の設計パターンには、主に次のようなものがあります。
-
ステート マシン モード: ステート マシン モードは、マルチステート制御ロジックの処理に使用できる一般的に使用される組み込みシステム設計モードです。ステートマシンモードは、システムの状態と状態遷移を状態と遷移のルールの集合として定義し、状態遷移を通じて制御ロジックを実現します。
-
オブザーバー モード: オブザーバー モードは、システム内でイベント通知を処理するために使用できる、一般的に使用されるイベント処理モードです。オブザーバー モードは、イベント処理をイベント通知から分離し、登録および通知メカニズムを通じてイベント処理を実装します。
-
アダプター モード: アダプター モードは、一般的に使用されるインターフェイス変換モードであり、システム内のインターフェイスの互換性の問題に対処するために使用できます。アダプター モードは、互換性のないインターフェイスを互換性のあるインターフェイスに変換することで、システムの互換性を実現します。
-
シングルトン モード: シングルトン モードは、システム内のオブジェクトの数を制御するために使用できる一般的なオブジェクト作成モードです。シングルトン パターンは、作成されるオブジェクトの数を制限することでシステムの制御と最適化を実現します。
-
ファクトリ パターン: ファクトリ パターンは、システム内でオブジェクトの作成と管理を処理するために使用できる共通のオブジェクト作成パターンです。ファクトリパターンはオブジェクトの作成と管理を分離し、ファクトリを通じてオブジェクトの作成と管理を実現します。
-
ストラテジー モード: ストラテジー モードは、システム内でアルゴリズムの選択と実行を処理するために使用できる一般的なアルゴリズム処理モードです。ストラテジ パターンは、さまざまなアルゴリズムの実装をストラテジのセットとして定義し、ストラテジの選択を通じてアルゴリズムの処理を実現します。
上記は、組み込みシステムで一般的に使用される C 言語のデザイン パターンです。さまざまなアプリケーション シナリオに適したさまざまなデザイン パターンがあり、特定の要件に応じて選択して適用する必要があります。
組み込み C 言語設計モードを使用して、P2 ポートに 8 つのボタンを備えた STC89C52、C51 アプリケーション例を作成します。
ステートマシンモード
以下は、STC89C52 チップの P2 ポートの 8 つのボタンを制御するために使用される単純なステート マシン モードの例です。これは単なる基本的な例であり、実際には、特定のニーズに応じて変更および最適化する必要がある場合があることに注意してください。
#include <reg52.h>
#define KEY_PORT P2 // 按键端口
sbit LED = P1^0; // LED端口
typedef enum{
IDLE_STATE = 0,
KEY1_PRESSED,
KEY2_PRESSED,
KEY3_PRESSED,
KEY4_PRESSED,
KEY5_PRESSED,
KEY6_PRESSED,
KEY7_PRESSED,
KEY8_PRESSED
} KeyState;
void key_scan(void)
{
static KeyState state = IDLE_STATE;
switch(state){
case IDLE_STATE:
if(KEY_PORT != 0xFF){
// 检测到按键按下
switch(KEY_PORT){
case 0xFE:
state = KEY1_PRESSED;
break;
case 0xFD:
state = KEY2_PRESSED;
break;
case 0xFB:
state = KEY3_PRESSED;
break;
case 0xF7:
state = KEY4_PRESSED;
break;
case 0xEF:
state = KEY5_PRESSED;
break;
case 0xDF:
state = KEY6_PRESSED;
break;
case 0xBF:
state = KEY7_PRESSED;
break;
case 0x7F:
state = KEY8_PRESSED;
break;
default:
break;
}
}
break;
case KEY1_PRESSED:
LED = ~LED; // LED取反
state = IDLE_STATE;
break;
case KEY2_PRESSED:
// do something
state = IDLE_STATE;
break;
case KEY3_PRESSED:
// do something
state = IDLE_STATE;
break;
case KEY4_PRESSED:
// do something
state = IDLE_STATE;
break;
case KEY5_PRESSED:
// do something
state = IDLE_STATE;
break;
case KEY6_PRESSED:
// do something
state = IDLE_STATE;
break;
case KEY7_PRESSED:
// do something
state = IDLE_STATE;
break;
case KEY8_PRESSED:
// do something
state = IDLE_STATE;
break;
default:
state = IDLE_STATE;
break;
}
}
void main()
{
while(1)
{
key_scan(); // 扫描按键
}
}
上記のコードでは、列挙型 (KeyState) を使用してステート マシンの状態を表します。key_scan()
関数では、静的変数を使用してstate
現在の状態を表し、キーの状態に応じて状態を切り替えます。キーの押下が検出されると、キーの値に基づいてさまざまな状態に切り替わり、対応する操作が実行されます。ステートの実行が完了すると、ステートを元に戻しIDLE_STATE
、次のキー イベントが発生するのを待ちます。
上記のコードは単なる例であり、特定のニーズに応じて変更および最適化する必要があることに注意してください。同時に、別の開発ボードを使用している場合は、開発ボードに応じてコード内のピン定義とポート番号を変更する必要があります。
オブザーバーパターン
以下は、オブザーバー モードに基づく STC89C52 P2 ポートの 8 つのボタンの C51 コードです。
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
// 定义观察者模式的接口
typedef struct {
void (*press)(uchar key); // 按键按下事件
void (*release)(uchar key); // 按键释放事件
} KeyObserver;
// 定义按键
typedef struct {
uchar state; // 按键状态,0表示未按下,1表示按下
uchar code; // 按键编码,从0开始
KeyObserver* observer; // 观察者,可以为空
} Key;
// 定义按键数组
Key keys[8];
// 定义状态机
typedef enum {
IDLE, PRESSED, RELEASED
} KeyState;
// 定义状态机转移表
KeyState stateTable[3][2] = {
{
PRESSED, IDLE}, // IDLE状态下按下按键,转移到PRESSED状态
{
PRESSED, RELEASED}, // PRESSED状态下释放按键,转移到RELEASED状态
{
IDLE, IDLE} // RELEASED状态下不做任何事情,转移到IDLE状态
};
// 定义按键扫描函数
void scanKeys() {
uchar i;
for (i = 0; i < 8; i++) {
// 设置P2口为输入
P2 = P2 & ~(1 << i);
_nop_();
_nop_();
_nop_();
keys[i].state = (P2 >> i) & 1; // 读取P2口状态
// 设置P2口为输出
P2 = P2 | (1 << i);
}
}
// 定义按键状态机处理函数
void handleKeys() {
uchar i;
for (i = 0; i < 8; i++) {
Key* key = &keys[i];
KeyState prevState = key->state;
KeyState nextState = stateTable[prevState][key->state];
key->state = nextState;
if (prevState == IDLE && nextState == PRESSED) {
// 触发按键按下事件
if (key->observer != NULL && key->observer->press != NULL) {
key->observer->press(key->code);
}
} else if (prevState == PRESSED && nextState == RELEASED) {
// 触发按键释放事件
if (key->observer != NULL && key->observer->release != NULL) {
key->observer->release(key->code);
}
}
}
}
// 定义状态机模式的例子,用于控制P2口8个按键
void main() {
uchar i;
// 初始化按键数组
for (i = 0; i < 8; i++) {
keys[i].state = 0;
keys[i].code = i;
keys[i].observer = NULL;
}
// 设置P2口为输出
P2 = 0xff;
// 循环扫描按键并处理状态机
while (1) {
scanKeys();
handleKeys();
}
}
KeyObserver
この例では、キーを押したり離したりするイベントのコールバック関数を定義するために使用されるオブザーバー モード インターフェイスを定義します。Key
次に、キーの状態、コード、オブザーバーを含むキー構造が定義されます。keys
次に、 8 つのキーの状態を保存するキー配列が定義されます。KeyState
次に、IDLE、PRESSED、RELEASED の 3 つの状態を含むステート マシンが定義されます。次に、ステート マシン遷移テーブルが定義されますstateTable
。これは、現在の状態と入力状態に応じて次の状態を計算するために使用されます。scanKeys()
次に、P2 ポートの状態を読み取り、キーの状態を更新するために使用されるキー スキャン関数を定義します。次に、ボタン ステート マシン処理関数を定義しますhandleKeys()
。この関数は、現在の状態と次の状態に応じてボタン イベントをトリガーするために使用されます。最後に、main()
関数内でボタン配列を初期化し、ループ内でscanKeys()
とhandleKeys()
関数を呼び出してボタン イベントを処理します。
これは、オブザーバー パターンを使用して組み込みシステムのキー ハンドラーを設計する方法を示すための単純な例であることに注意してください。実際のアプリケーションでは、特定の状況に応じて変更や最適化が必要になる場合があります。
アダプターパターン
以下は、組み込み C 言語設計モードのアダプター モードを使用して、STC89C52 チップの P2 ポートの 8 つのボタンを設計する完全な C51 コードです。
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
sbit K1 = P2^0;
sbit K2 = P2^1;
sbit K3 = P2^2;
sbit K4 = P2^3;
sbit K5 = P2^4;
sbit K6 = P2^5;
sbit K7 = P2^6;
sbit K8 = P2^7;
/* 适配器模式 */
typedef struct {
void (*init)(void);
uint (*read)(void);
} KeyAdapter;
/* 适配器实现 */
void P2AdapterInit(void) {
P2 = 0xff;
}
uint P2AdapterRead(void) {
return P2;
}
/* 状态机模式 */
typedef enum {
IDLE = 0,
PRESS,
RELEASE
} KeyState;
/* 状态机结构体 */
typedef struct {
KeyState state;
uint cnt;
} KeyFSM;
/* 状态机初始化 */
void KeyFSMInit(KeyFSM* fsm) {
fsm->state = IDLE;
fsm->cnt = 0;
}
/* 状态机更新 */
void KeyFSMUpdate(KeyFSM* fsm, uint input) {
switch(fsm->state) {
case IDLE:
if(input != 0xff) {
fsm->state = PRESS;
fsm->cnt = 0;
}
break;
case PRESS:
if(input == 0xff) {
fsm->state = RELEASE;
fsm->cnt = 0;
}
else {
fsm->cnt++;
if(fsm->cnt == 100) {
fsm->cnt = 0;
/* 按键按下 */
if(input & 0x01) {
/* K1被按下 */
// TODO: 在这里添加相应的处理代码
}
else if(input & 0x02) {
/* K2被按下 */
// TODO: 在这里添加相应的处理代码
}
else if(input & 0x04) {
/* K3被按下 */
// TODO: 在这里添加相应的处理代码
}
else if(input & 0x08) {
/* K4被按下 */
// TODO: 在这里添加相应的处理代码
}
else if(input & 0x10) {
/* K5被按下 */
// TODO: 在这里添加相应的处理代码
}
else if(input & 0x20) {
/* K6被按下 */
// TODO: 在这里添加相应的处理代码
}
else if(input & 0x40) {
/* K7被按下 */
// TODO: 在这里添加相应的处理代码
}
else if(input & 0x80) {
/* K8被按下 */
// TODO: 在这里添加相应的处理代码
}
}
}
break;
case RELEASE:
if(input != 0xff) {
fsm->state = PRESS;
fsm->cnt = 0;
}
break;
default:
break;
}
}
/* 主函数 */
void main() {
KeyAdapter adapter = {
.init = P2AdapterInit,
.read = P2AdapterRead
};
KeyFSM fsm;
KeyFSMInit(&fsm);
adapter.init();
while(1) {
uint input = adapter.read();
KeyFSMUpdate(&fsm, input);
}
}
このコードでは、アダプター モードを使用して STC89C52 チップの P2 ポート入力を調整し、通常のキー入力として扱えるようにします。次に、ステート マシン パターンを使用してキー押下イベントとポップ イベントを処理し、キーが押されたときに対応する処理コードを呼び出します。ここでは、いくつかの TODO マークを残すだけです。特定のニーズに応じて、対応する処理コードを記述する必要があります。
シングルトンパターン
参考までに、以下はステート マシン モードのシングルトン モードに基づいて設計された STC89C52 チップの P2 ポートに 8 つのボタンを備えた C51 の完全なコードです。
#include <reg52.h>
// 定义按键的状态
#define KEY_UP 0
#define KEY_DOWN 1
// 定义状态机的状态
typedef enum {
STATE_IDLE,
STATE_PRESS,
STATE_RELEASE
} State;
// 定义单例模式实例
static State state = STATE_IDLE;
// 定义按键的状态数组
static unsigned char key_states[8] = {
0};
// 定义按键的状态更新函数
void update_key_states() {
// 读取P2口的值
unsigned char p2_value = P2;
// 遍历8个按键
for (int i = 0; i < 8; i++) {
// 判断按键状态是否改变
if ((p2_value & (1 << i)) && key_states[i] == KEY_UP) {
key_states[i] = KEY_DOWN;
} else if (!(p2_value & (1 << i)) && key_states[i] == KEY_DOWN) {
key_states[i] = KEY_UP;
}
}
}
// 定义状态机的状态转换函数
void transition_state() {
switch (state) {
case STATE_IDLE:
for (int i = 0; i < 8; i++) {
if (key_states[i] == KEY_DOWN) {
state = STATE_PRESS;
break;
}
}
break;
case STATE_PRESS:
for (int i = 0; i < 8; i++) {
if (key_states[i] == KEY_UP) {
state = STATE_RELEASE;
break;
}
}
break;
case STATE_RELEASE:
for (int i = 0; i < 8; i++) {
if (key_states[i] == KEY_DOWN) {
state = STATE_PRESS;
break;
}
}
if (state == STATE_RELEASE) {
state = STATE_IDLE;
}
break;
}
}
// 定义主函数
void main() {
while (1) {
update_key_states();
transition_state();
// 按键按下时的处理
if (state == STATE_PRESS) {
// TODO: 在此处编写按键按下时的处理代码
}
// 按键释放时的处理
if (state == STATE_RELEASE) {
// TODO: 在此处编写按键释放时的处理代码
}
}
}
上記のコードは単なる例であり、特定のニーズに応じて変更および改善する必要があることに注意してください。また、ハードウェアの接続に応じて、ボタン状態更新関数のP2ポート読み取りコードも変更する必要があります。
工場パターン
参考までに、以下は工場モードに基づいた STC89C52 チップの P2 ポートに 8 つのボタンを備えた C51 の完全なコードです。
#include <reg52.h>
// 定义按键的状态
#define KEY_UP 0
#define KEY_DOWN 1
// 定义状态机的状态
typedef enum {
STATE_IDLE,
STATE_PRESS,
STATE_RELEASE
} State;
// 定义按键的状态数组
static unsigned char key_states[8] = {
0};
// 定义按键处理器接口
typedef void (*KeyHandler)(void);
// 定义按键处理器工厂
typedef struct {
KeyHandler press_handler;
KeyHandler release_handler;
} KeyHandlerFactory;
// 定义按键处理器
static void handle_key_1_press(void) {
// TODO: 在此处编写按键1按下时的处理代码
}
static void handle_key_1_release(void) {
// TODO: 在此处编写按键1释放时的处理代码
}
static void handle_key_2_press(void) {
// TODO: 在此处编写按键2按下时的处理代码
}
static void handle_key_2_release(void) {
// TODO: 在此处编写按键2释放时的处理代码
}
// 定义按键处理器工厂实例
static KeyHandlerFactory key_handler_factory[8] = {
{
handle_key_1_press, handle_key_1_release },
{
handle_key_2_press, handle_key_2_release },
// TODO: 在此处添加剩余按键的处理器
};
// 定义按键的状态更新函数
void update_key_states() {
// 读取P2口的值
unsigned char p2_value = P2;
// 遍历8个按键
for (int i = 0; i < 8; i++) {
// 判断按键状态是否改变
if ((p2_value & (1 << i)) && key_states[i] == KEY_UP) {
key_states[i] = KEY_DOWN;
} else if (!(p2_value & (1 << i)) && key_states[i] == KEY_DOWN) {
key_states[i] = KEY_UP;
}
}
}
// 定义状态机的状态转换函数
void transition_state() {
static State state = STATE_IDLE;
switch (state) {
case STATE_IDLE:
for (int i = 0; i < 8; i++) {
if (key_states[i] == KEY_DOWN) {
state = STATE_PRESS;
break;
}
}
break;
case STATE_PRESS:
for (int i = 0; i < 8; i++) {
if (key_states[i] == KEY_UP) {
state = STATE_RELEASE;
break;
}
}
break;
case STATE_RELEASE:
for (int i = 0; i < 8; i++) {
if (key_states[i] == KEY_DOWN) {
state = STATE_PRESS;
break;
}
}
if (state == STATE_RELEASE) {
state = STATE_IDLE;
}
break;
}
// 调用对应的按键处理器
if (state == STATE_PRESS) {
for (int i = 0; i < 8; i++) {
if (key_states[i] == KEY_DOWN) {
key_handler_factory[i].press_handler();
}
}
} else if (state == STATE_RELEASE) {
for (int i = 0; i < 8; i++) {
if (key_states[i] == KEY_UP) {
key_handler_factory[i].release_handler();
}
}
}
}
// 定义主函数
void main() {
while (1) {
update_key_states();
transition_state();
}
}
上記のコードは単なる例であり、特定のニーズに応じて変更および改善する必要があることに注意してください。さらに、ハードウェア接続に応じてボタン状態更新機能の P2 ポート読み取りコードを変更し、実際のニーズに応じてボタン プロセッサ ファクトリにプロセッサ機能を追加する必要もあります。
戦略パターン
参考までに、以下は戦略モードに基づいた STC89C52 チップの P2 ポート上の 8 つのキーを持つ C51 の完全なコードです。
#include <reg52.h>
// 定义按键的状态
#define KEY_UP 0
#define KEY_DOWN 1
// 定义按键处理器接口
typedef void (*KeyHandler)(void);
// 定义按键处理器策略
typedef struct {
KeyHandler press_handler;
KeyHandler release_handler;
} KeyHandlerStrategy;
// 定义按键处理器
static void handle_key_1_press(void) {
// TODO: 在此处编写按键1按下时的处理代码
}
static void handle_key_1_release(void) {
// TODO: 在此处编写按键1释放时的处理代码
}
static void handle_key_2_press(void) {
// TODO: 在此处编写按键2按下时的处理代码
}
static void handle_key_2_release(void) {
// TODO: 在此处编写按键2释放时的处理代码
}
// 定义按键处理器策略数组
static KeyHandlerStrategy key_handler_strategies[8] = {
{
handle_key_1_press, handle_key_1_release },
{
handle_key_2_press, handle_key_2_release },
// TODO: 在此处添加剩余按键的处理器策略
};
// 定义按键状态数组
static unsigned char key_states[8] = {
0};
// 定义按键状态更新函数
void update_key_states() {
// 读取P2口的值
unsigned char p2_value = P2;
// 遍历8个按键
for (int i = 0; i < 8; i++) {
// 判断按键状态是否改变
if ((p2_value & (1 << i)) && key_states[i] == KEY_UP) {
key_states[i] = KEY_DOWN;
} else if (!(p2_value & (1 << i)) && key_states[i] == KEY_DOWN) {
key_states[i] = KEY_UP;
}
}
}
// 定义按键状态处理函数
void handle_key_states() {
// 遍历8个按键
for (int i = 0; i < 8; i++) {
// 判断按键状态
if (key_states[i] == KEY_DOWN) {
// 调用按键按下处理器
key_handler_strategies[i].press_handler();
} else if (key_states[i] == KEY_UP) {
// 调用按键释放处理器
key_handler_strategies[i].release_handler();
}
}
}
// 定义主函数
void main() {
while (1) {
update_key_states();
handle_key_states();
}
}
上記のコードは単なる例であり、特定のニーズに応じて変更および改善する必要があることに注意してください。さらに、ハードウェア接続に応じてボタン ステータス更新関数の P2 ポート読み取りコードを変更し、実際のニーズに応じてボタン プロセッサ戦略を追加する必要もあります。