自作赤外線リモコンコントロールアンプのシステム設定インターフェースを実装した

最低必要な機能

  • ボリューム初期値の登録
    システム電源OFFはAVRのスリープモードなのでSRAM内に記憶されているのですが,電源断後の初期ボリュームはEEPROMに保存しておいたものを使います.
    ここではその値を登録します.
  • 電源リモコンコードの登録
    受信したコードをEEPROMに保存します.
  • 音量リモコンコードの登録
    受信したコードをEEPROMに保存します.
  • ディスプレイOFFリモコンコードの登録
    受信したコードをEEPROMに保存します.

メニューとメニューアイテム

設定メニューの設計
設定メニューの設計
//
//セットアップメニューアイテム
//
typedef struct {
    //メニューの見出し
    const char * PROGMEM caption;
    //フック関数に渡す情報
    void    *detail;
    //onFocusイベントフック関数
    void    (*onFocus_hook)( display_buffer_t display_buf, uint8_t target_linenum, void* detail );
    //keyUpイベントフック関数
    bool    (*onKeyUp_hook)( display_buffer_t display_buf, uint8_t target_linenum, void* detail );
} setup_menu_item_t;

//
//セットアップメニュー
//
typedef struct _setup_menu_t {
    void (*constructor)(void *initializer);     //コンストラクタ関数
    setup_menu_item_t   menu_items[];           //メニューアイテムの配列
} setup_menu_t;

オブジェクト指向言語の出番なんだろうけども意地でC言語で頑張ってみた.
onFocusイベントとkeyUpイベントの発生時にそれぞれのフック関数が呼ばれます.

トップメニュー

カーソルに選択されている時に対応するonFocusフック関数が呼ばれます.
スイッチが押された時に対応するonKeyUpフック関数が呼ばれます.

const setup_menu_t top_setup_menu = {
    top_setup_menu_constructor,     //コンストラクタ,
    {
        { WhiteSpaceFilledLine, NULL, nop_onFocus_hook, nop_and_continue_onKeyUp_hook },                //ガード
        // 1234567890123456
        {            "ABOUT", &workspace_for_about_menu_hook,                   messages_menu_onFocus_hook,         messages_menu_onKeyUp_hook },
        {          "license", &workspace_for_license_menu_hook,                 messages_menu_onFocus_hook,         messages_menu_onKeyUp_hook },
        {         "PWR fail", NULL,                                             bor_count_menu_onFocus_hook,        nop_and_continue_onKeyUp_hook },
        {      "system down", NULL,                                             wdt_count_menu_onFocus_hook,        nop_and_continue_onKeyUp_hook },
        {         "Init vol", NULL,                                             init_vol_menu_onFocus_hook,         init_vol_menu_onKeyUp_hook },
        {         "check IR", workspace_for_IR_hook,                            learn_IR_sub_menu_onFocus_hook,     check_IR_menu_onKeyUp_hook },
        {           "ON/OFF", &Saved_EEPROM_data.ircodes[IRCODE_onoff],         learn_IR_menu_onFocus_hook,         learn_IR_menu_onKeyUp_hook },
        {            "SwSrc", &Saved_EEPROM_data.ircodes[IRCODE_switch_source], learn_IR_menu_onFocus_hook,         learn_IR_menu_onKeyUp_hook },
        {            "Vol +", &Saved_EEPROM_data.ircodes[IRCODE_volume_up],     learn_IR_menu_onFocus_hook,         learn_IR_menu_onKeyUp_hook },
        {            "Vol -", &Saved_EEPROM_data.ircodes[IRCODE_volume_down],   learn_IR_menu_onFocus_hook,         learn_IR_menu_onKeyUp_hook },
        {             "MUTE", &Saved_EEPROM_data.ircodes[IRCODE_mute],          learn_IR_menu_onFocus_hook,         learn_IR_menu_onKeyUp_hook },
        {         "OmitDisp", &Saved_EEPROM_data.ircodes[IRCODE_omit_display],  learn_IR_menu_onFocus_hook,         learn_IR_menu_onKeyUp_hook },
        {           "Revert", NULL,                                             revert_menu_onFocus_hook,           nop_and_break_reset_onKeyUp_hook },
        {        "Save&Exit", NULL,                                             save_and_exit_menu_onFocus_hook,    save_and_exit_menu_onKeyUp_hook },
        // 1234567890123456
        { WhiteSpaceFilledLine, NULL, nop_onFocus_hook, nop_and_continue_onKeyUp_hook },                //ガード
    },
};

ABOUTメニューでは

//
//ABOUTメニューのメッセージ
//プログラムメモリへ配置する
//
static const char about_menu_message[] PROGMEM = {
//   1234567890123456
    "firmware Ver1.00 for IR remote control headphone amplifier main board"
    " "
    "Rev:A2.0 or above."
//   1234567890123456
    "                "
};

//
//ABOUT,licenseメニューonFocusフック関数
//
static void messages_menu_onFocus_hook( display_buffer_t display_buf, uint8_t target_linenum, void* detail )
{
    struct workspace_for_messages_menu_hook_t *workspace = (struct workspace_for_messages_menu_hook_t *)detail;
    int16_t delta;

    if (workspace == NULL) {
        return;
    }
    //1文字目
    delta = workspace->counter - DISPLAY_WIDTH;
    if (delta < 0) { //最初は表示行幅一杯になるまで表示する delta = 0; } //スクロール表示 strncpy_P( display_buf[target_linenum].line, &workspace->message[delta], DISPLAY_WIDTH );
    if (pgm_read_byte (&workspace->message[workspace->counter]) == '�') {
        //始めに戻る
        workspace->counter = 0;
        workspace->remain  = MESSAGE_SCROLL_WAIT;
    }
    ///
    if (--workspace->remain < 0) { workspace->counter++;
        workspace->remain = MESSAGE_SCROLL_WAIT;
    }

}
//
//ABOUT,licenseメニューonKeyUpフック関数
//
static bool messages_menu_onKeyUp_hook( display_buffer_t display_buf, uint8_t target_linenum, void* detail )
{
    struct workspace_for_messages_menu_hook_t *workspace = (struct workspace_for_messages_menu_hook_t *)detail;

    if (workspace != NULL) {
        workspace->counter = 0;
        workspace->remain = MESSAGE_SCROLL_WAIT;
    }

    return true;    //現在の状態を継続する
}

カーソルに選択されている時に対応するonFocusフック関数が呼ばれます.
ここではスクロールしてメッセージを表示します.

スイッチが押された時に対応するonKeyUpフック関数が呼ばれます.
ここではカウンタをクリアしてメッセージの最初から表示します.

ボリューム選択メニュー

onKeyUpイベントが発生する=スイッチが押された時に選択中のボリュームが選択されます.

static const setup_menu_t volume_setup_menu = {
    volume_setup_menu_constructor,  //コンストラクタ
    {
        { WhiteSpaceFilledLine,  NULL, nop_onFocus_hook, nop_and_continue_onKeyUp_hook },               //ガード
        // 1234567890123456
        {                "0", "x00", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {                "1", "x01", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {                "2", "x02", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {                "3", "x03", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {                "4", "x04", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {                "5", "x05", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {                "6", "x06", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {                "7", "x07", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {                "8", "x08", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {                "9", "x09", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "10", "x0a", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "11", "x0b", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "12", "x0c", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "13", "x0d", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "14", "x0e", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "15", "x0f", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "16", "x10", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "17", "x11", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "18", "x12", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "19", "x13", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "20", "x14", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "21", "x15", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "22", "x16", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "23", "x17", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "24", "x18", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "25", "x19", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "26", "x1a", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "27", "x1b", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "28", "x1c", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "29", "x1d", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "30", "x1e", nop_onFocus_hook, volume_set_onKeyUp_hook },
        {               "31", "x1f", nop_onFocus_hook, volume_set_onKeyUp_hook },
        // 1234567890123456
        { WhiteSpaceFilledLine,  NULL, nop_onFocus_hook, nop_and_continue_onKeyUp_hook },               //ガード
    }
};

赤外線信号登録メニュー

static const setup_menu_t learn_IR_sub_menu = {
    learn_IR_sub_menu_constructor,  //コンストラクタ関数
    {
        { WhiteSpaceFilledLine, NULL, nop_onFocus_hook, nop_and_continue_onKeyUp_hook },                //ガード
        // 1234567890123456
        {           "Cancel", workspace_for_IR_hook, learn_IR_sub_menu_onFocus_hook, nop_and_break_reset_onKeyUp_hook },
        {               "OK", workspace_for_IR_hook, learn_IR_sub_menu_onFocus_hook, learn_IR_sub_menu_OK_onKeyUp_hook },
        // 1234567890123456
        { WhiteSpaceFilledLine, NULL, nop_onFocus_hook, nop_and_continue_onKeyUp_hook },                //ガード
    }
};

再帰構造だから再帰呼び出しで処理しますよ

設定メニューの再帰的構造
設定メニューの再帰的構造

メニューの中にメニューがある構造なので,トップメニューより再帰的に下のメニューに降りていきます.

//
//init volメニューonKeyUpフック関数
//
static bool init_vol_menu_onKeyUp_hook( display_buffer_t display_buf, uint8_t target_linenum, void* detail )
{
    //再帰的に呼びだす
    system_setup( display_buf, target_linenum, &volume_setup_menu, detail, get_volume_menu_cursor_pos(Saved_EEPROM_data.init_volume) );
    return true;    //現在の状態を継続する
}

スタックオーバーフローに注意していれば,再起構造は再帰呼び出しで処理するのがベターかな。

SRAMに入りきらないので出来るだけプログラムメモリーへ

SRAM 2kBが貴重なので出来るだけ32kBあるプログラムメモリーに押し込みます.
#include <avr/pgmspace.h>
これをincludeしてから
static const char about_menu_message[] PROGMEM = {
PROGMEMを配列につけることでプログラムメモリーに配置されます。

EEPROMアクセス

初期ボリュームとリモコンコードをEEPROMに保存/読み出す時の構造体


typedef enum {
    IRCODE_onoff            = 0,    //電源ONOFFリモコンコード
    IRCODE_switch_source    = 1,    //入力切替リモコンコード
    IRCODE_volume_up        = 2,    //音量+リモコンコード
    IRCODE_volume_down      = 3,    //音量ーリモコンコード
    IRCODE_mute             = 4,    //ミュートリモコンコード
    IRCODE_omit_display     = 5,    //表示省略リモコンコード
    //
    IRCODE_total_num        = 6,    //全リモコンコード数
    //
    IRCODE_not_found        = -1,   //見つからないリモコンコード
} IRCODE_t;

//
//EEPROMに保存するデーター
//
typedef struct __attribute__((__packed__)) {
            elevol_t                    init_volume;                //電子ボリュームの初期値
    union   irr_necformat_or_uint32_t   ircodes[IRCODE_total_num];  //リモコンコード
} saved_eeprom_data_t;

システム設定ソースコード

AudioAmpApplication.c
setup_menu.h
setup_menu.c
store_EEPROM.h
store_EEPROM.c

2 thoughts on “自作赤外線リモコンコントロールアンプのシステム設定インターフェースを実装した

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください