Vol.889 23.Feb.2024

デイジーコラージュPersonal 赤外線リモコン(12)SONYリモコンコード送信

D デイジーコラージュPersonal

by fjk

 ソースネクストの「毎日ジャンボ宝くじ」にチャレンジしたところ、「デイジーコラージュpaersonal」が980円(84%引き)で購入する権利が当り、画像編集ソフトがないわけではないが、ついポチり。
 「デイジーコラージュ Personal」は、撮ったデジカメ写真をもっと楽しむための様々なツールを搭載した、デジカメ写真の総合ソフトで、補正や加工はもちろん、カレンダーや豆本も簡単に作成できる。
【主な仕様】

◇補正(14種):
色補正、自動補正/明るさ/ コントラスト/ ピント/ 色あい/濃さ/蛍光灯補正、明るさ・コントラスト、色調・彩度・明度、カラーバランス、レベル補正、トーンカーブ、ぼかし・シャープ、ノイズ除去 、美肌補正、赤目補正、コピー修正、偽色抑制、ペン先色補正、ペン先色変換
◇フィルター(30種):
一発美人、手ぶれ、クロスフォーカス、ソフトフォーカス、七色強調、レリーフ、陰影、輪郭線、デッサン、メタル、セピア、ソラリゼーション、拡散、波紋、放射、ネガポジ反転、モノクロ、モザイク、ノイズ付加、レンズ、ポスター、ぼかし/シャープ スリム、水面、絵画調、ステンドグラス、レンズフレア、雨、照明、正方分解
◇フレーム(100種)、スタンプ(200種)、ペン(30種)
◇文字表現:
単色、グラデーション、テクスチャー、メタリック(金属)、水滴、クリスタルグラス
◇合成:
輪郭線を自動で検出する「ニューロペン」で合成、同色の部分だけを合成する「カラーロック」、移動・拡縮・複製で自由に配置可能
◇プリント(14種):
豆本、塗り絵、カレンダー、はがき、名刺、レーベル印刷、専用紙、レイアウト、ポスター、サイコロ、通常印刷、インデックス、アルバム、専用フォト
◇アルバム:
写真・動画・音声をカテゴリー毎に分類して管理。カメラからワンクリックでパソコンに自動取込、28種類のスライドショーに出力

デイジーコラージュPersonal起動画面

アルバムに画像を登録(編集後は別画像に保存)

補正したいメニュー(明るさ)を選択

「自動補正」を選ぶと補正終了(手動も可)


R 赤外線リモコン (12)   〜SONYコード送信

by fjk

 前報告(abc888)でSONYコードの特徴がわかったので、PIC16F18426を使ってSONYおよびIOdata(NEC)リモコンの赤外線コードの送受信ができるかテストした。
 なお、LCD表示ユニットとの配線が煩雑なので、8ピンのヘッダー&ソケットを用いて基板間の配線を行った。多くのL型ピンヘッダー・ソケットはブレッドボード(BB)側のピン長が短いので、ピン長6〜10mmのストレート型のピンをペンチで曲げてL型とした。BBの配線は、コネクタを取り付けるため、バイパスコンデンサの位置と、黄LED配線の一部を変更したが、回路図はabc887と同じ。
 RA0,1はLCDユニットのプッシュスイッチに接続したが、スイッチは使用していないので、MCCの設定はabc887と同じ。ICSPの5ピンは垂直型に変更し、スイッチが押されていなければ回路はオープンなので、PIC書き込み時に問題は無い。もし、RA0,1を入力ピンとして使う場合(MCCでWPUをチェックし、「LCD画面シフト操作」のコメントを外すと、LCD画面の表示を左右にシフトできる)、PIC書込み終了後ICSPを外さないと、ノイズが入り、LCD画面が乱れるので、ICSPは必ず外すこと!。


LCD表示ユニットの配線

PICユニットとコネクタで接続(SONY受信)

 プログラムはabc887の赤外線コード自動解読機能に、USBからのシリアルデータを受信するか、BB上の黄プッシュボタンが押されたら、指示された赤外線コードを送信(以後「IR送信」)するプログラムを追加した。
 USBシリアルで4桁の16進数(0x----)を送ると、SONYコードをIR送信する(コードの詳細はabc888参照)。20ビットコードでアドレスも指定する場合は、スペースを挟んでさらに4桁の16進数を送ること。シリアル受信文字列が16進数でない場合はIOdata(NEC)データと見なし、あらかじめ配列に格納されたIOdataコードをIR送信する。
 変数btcはIR送信するビット数で、指定されたコードから判断し、btc = 0ならIR送信しない。送信ビット数に併せて送信ビットデータicdを作成し、btc = 0でないなら、リーダーをIR送信後、icdのbtcビット分をIR送信し、フレーム時間が45msになるようにbtcに毎に異なる無信号時間としている。自分がIR送信した信号を受信しないように、SMTの動作を一時停止し、IR送信終了後にSMTを再開している。
 なお、USBシリアル受信文字列を処理するため、受信処理用の変数・宣言などをeusart1.hとeusart1.cおよびmain.cに処理ルーチンと伴に追加した(eusartの設定は 下記 を参照)。受信文字列からはstrtol()関数で2つの16進数ロング数値として取得している("string.h"をinclude宣言しておくこと)。
 受信文字列が16進数かどうかを判断するため、大小文字区別しないstrichr()関数を使ったところ、何故かエラーとなったので、大文字と小文字を別々のstrchr()関数で判断した。
 また、BBボード上のプッシュスイッチはテスト用で、押されると最後に使用したSONY赤外線コードを送信する。コメント部分を変更すると、IOデータや他のコードのIR送信テストを行うことができる。
 IR送信テストを実行すると、解析器に正しい数値が表示されなかった。これは、SONYバイトの最後のデータが表示されない解析ソフトのバグで、修正した(abc856abc887も修正)。その結果、SONYコードの赤外線送受信することができた。また、念のため、SONYコードを2回リピート送信(SONY測定データでは3回リピートしていた)。


SONYコード送受信例(12ビット)

テラターム画面例(左:受信、右:送信)

SONYコード送受信例(20ビット)

IOdataコード送受信例

★プログラム abc889s_18426.c(zip)
   SONYコード送信に send_SONY() を新規に作成し、Iodataコード送信はabc857からコピー(青色)した。
   なお、赤字abc887から追加・変更したところ。
/***********************************************(abc889_18426.c)***
 *      NEC/SONY赤外線リモコンの送受信
 ******************************************************************/

#include "mcc_generated_files/mcc.h"
#include "string.h"
#include "i2cLCD_ST7032i.h"

#define MAXDATA 90                              // 赤外線データ配列数
#define MAXADDR 10                              // SONYアドレス配列数
/*
#define BFSIZE  20                              // eusart.hで定義
*/
// ----- NEC pulse time parameter ----
#define MARK()      __delay_us(562)
#define SPACE_1()   __delay_us(1686)
#define SPACE_0()   __delay_us(562)
#define LEADER_1()  __delay_us(8992)
#define LEADER_0()  __delay_us(4496)
#define GYAP_0()    __delay_ms(43)
// ---- SONY puls time parameter -----
#define S_LEADER()  __delay_us(2400)
#define S_SPACE()   __delay_us(600)
#define S_MARK_0()  __delay_us(600)
#define S_MARK_1()  __delay_us(1200)
#define S_GYAP_20() __delay_ms(19)
#define S_GYAP_15() __delay_ms(25)
#define S_GYAP_12() __delay_ms(29)
// ----- DSM & STM controle ----
#define DSM_1()     (MD1CON0bits.MD1BIT = 1)
#define DSM_0()     (MD1CON0bits.MD1BIT = 0)
#define SMT_START() (SMT1CON1bits.SMT1GO = 1)
#define SMT_STOP()  (SMT1CON1bits.SMT1GO = 0)
// ---- IOdata Customer Code ----
#define IO_CUSTOM   0xE880

/* -----  Variables  ----- */
uint16_t PW[MAXDATA];                           // Mark配列
int16_t  PR[MAXDATA];                           // Space配列
uint8_t  p_PW;                                  // Mark配列ポインタ
uint8_t  p_PR;                                  // Space配列ポインタ

char     sBf[BFSIZE];                           // 文字列作業エリア
char     rBuf[BFSIZE];                          // シリアル受信バッファ
uint8_t  sFlg;                                  // シリアル受信フラグ
uint8_t  Sony;                                  // Sonyフォーマット?
uint16_t Sa[MAXADDR];                           // SONYアドレス配列

uint16_t IO2[] = {0x1084,0x2084,0x3084};        // IOdata赤外線コード例

/*----- Send NEC Code  -----*/
void Ir_Leader(){
    DSM_1();                                    // send Leader
    LEADER_1();
    DSM_0();
    LEADER_0();
}

void Ir_Stop(){
    DSM_1();
    MARK();                                     // send stop
    DSM_0();
}

void Ir_Send(uint8_t *dt, uint8_t x){           // send Byte
    uint8_t sft;
    for (int j = 0; j < x;j++){
        sft = 0x01;
        for(int i = 0; i < 8;i++){
            DSM_1();
            MARK();
            DSM_0();
            if(dt[j] & sft)  SPACE_1();
            else             SPACE_0();
            sft <<= 1;
        }
    }
}

void Ir_Send2(uint16_t wd){                     // send Word
    uint16_t wsft = 0x0001;
    for(int i = 0; i < 16;i++){
        DSM_1();
        MARK();
        DSM_0();
        if(wd & wsft)   SPACE_1();
        else            SPACE_0();
        wsft <<= 1;
    }
}

void send_NEC(uint16_t cst, uint16_t d){
    uint16_t dat;
    IO_RC2_SetLow();
    SMT_STOP();                                 // 赤外線測定中断
    dat = (d & 0x00FF) | ((~d << 8) & 0xFF00);  // Calc data code
    Ir_Leader();                                // Leader Start
    Ir_Send2(cst);                              // Send Custmer
    Ir_Send2(dat);                              // Send Data
    Ir_Stop();         	                        // stop
    LCD_clr();
    sprintf(sBf,">NEC %04X-%04X",cst,dat);
    LCD_str(sBf);
  printf("\n\r>NEC %04X-%04X",cst,dat);
    SMT_START();                                // 測定再開
}

/*----- Send IOdata code  ------*/

void send_IOdata(uint8_t c){                    // from IOarray(word)
    uint16_t w = IO2[c];
    uint16_t wLo;
    uint16_t wHi;
    IO_RC2_SetLow();
    SMT_STOP();                                 // 赤外線測定中断
    wLo = (w & 0x00FF) | ((~w << 8) & 0xFF00);  // Calc Low word
    wHi = ((w >> 8) & 0x00FF) | (~w & 0xFF00);  // Calc High word
    Ir_Leader();                                // Leader Start
    Ir_Send2(IO_CUSTOM);                        // Send Custom  
    Ir_Send2(wLo);                              // Send Data1
    Ir_Stop();         	                        // stop
    GYAP_0();                                   // Gyap time
    Ir_Leader();                                // Leader Start 
    Ir_Send2(IO_CUSTOM);                        // Send Custom  
    Ir_Send2(wHi);                              // Send Data2
    Ir_Stop();                                  // stop
    LCD_clr();
    sprintf(sBf,">IOdata %08X",w);
    LCD_str(sBf);
    printf("\n\r>IOdata %08X",w);
    SMT_START();                                // 測定再開
}

/*----- Send SONY Code ----*/
void send_SONY(uint16_t wd, uint16_t ad){
    uint8_t  btc;                               // bit counter
    uint32_t icd;                               // IRcode data
    uint8_t  adx;                               // address number
    uint32_t sft;                               // shift data

    LCD_clr();
    sprintf(sBf,">Sony:%04X-%04X",wd,ad);
    LCD_str(sBf);
    printf("\r\n%s\n\r",sBf);
    icd = (uint32_t)(wd & 0x007F);
    if(wd & 0x0080){                            // 15bit mode?
        btc = 15;
        icd += (uint32_t)((wd >> 1) & 0x7F80); 
    }else{
        if(wd & 0x8000){                        // 20bit mode?
            btc = 20;
            adx = (wd >> 8) & 0x003F;
            if(adx >= MAXADDR){
                btc = 0;
            }else{
                if(wd & 0x4000){                // write?
                    icd += (uint32_t)ad << 7;
                    Sa[adx] = ad;
                }else{                          // read
                    icd += (uint32_t)Sa[adx] << 7;
                }
            }
        }else{                                  // 12bit mode
            btc = 12;
            icd += (uint32_t)((wd >> 1) & 0x0F80);
        }
    }
    if(btc){                                    // 送信データが有効なら
        IO_RC2_SetHigh();
        SMT_STOP();                             // 赤外線測定中断
        sft = 0x00000001;
        DSM_1();                                // send Leader
        S_LEADER();
        for(int i = 0; i < btc; i++){
            DSM_0();                            // send Space
            S_SPACE();
            DSM_1();                            // send Mark
            if(icd & sft)   S_MARK_1();
            else            S_MARK_0();
            sft <<= 1;
        }
        DSM_0();                                // send Stop
        if(btc==20){
            S_GYAP_20();
        }else if(btc==15){
            S_GYAP_15();
        }else{
            S_GYAP_12();
        }
        sprintf(sBf,"- IR %05X",icd);
        LCD_cursor(0,1);
        LCD_str(sBf);
        printf("\r%s",sBf);
        SMT_START();                            // 測定再開
    }
}

void prt_Hex(uint8_t w, uint8_t n){             // 16進数表示
    if(n){
        sprintf(sBf,"%02X ",w);
        printf("  >> %02XH\n\r",w);
    }else{
        sprintf(sBf,"%02X",w);
        printf("  >> %02XH",w);  
    }
    LCD_str(sBf);                               // LCD表示
 }

/*-------------------------------------------
 *            Main application
 *-------------------------------------------*/
void main(void){
    uint8_t  ird;                               // 赤外線データ
    uint8_t  sbt;                               // ビットセット用バイト
    uint8_t  bct;                               // 有効ビットカウンタ
    uint8_t  sgf;                               // データ有効フラグ
    uint16_t sDat;                              // SONY送信データ
    uint16_t sAdr;                              // SONY送信アドレス
    char     *end;                              // 文字列切り出し処理用

    SYSTEM_Initialize();                        // デバイス初期化
    LCD_init();                                 // LCD初期化

    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    LCD_str("== ready ==");
  // --- テスト用データセット
    Sa[0] = 0x04B9;                             // SONYデフォルトアドレス
    sDat  = 0xC000;
    sAdr  = 0x1C5A;

    while (1)                                   // 赤外線信号受信繰返し
    {
        p_PW = 0;                               // データポインタ初期化
        p_PR = 0;
        SMT1PWAIF = 0;                          // フラグクリア
        SMT1PRAIF = 0;
        SMT1IF = 0;
        SMT_START();                            // 赤外線測定開始
        printf("\r\n-ready\r\n");               // 準備完了メッセージ送信

        // ====== 赤外線信号の受信・送出ループ =====
        while((p_PR < MAXDATA)){                // バッファ満杯まで繰返
           if(sFlg){                            // 受信データがあれば?
                if(strchr(rBuf,'X') || strchr(rBuf,'x')){
                    sDat = (uint16_t)strtol(rBuf, &end, 16);
                    sAdr = (uint16_t)strtol(end,  NULL, 16);
                    send_SONY(sDat,sAdr);       // SONYコード送信
                    send_SONY(sDat,sAdr);       // SONYコード送信(2回目)
                }else{                          // IODATA(NEC)コード
                    uint8_t n = atoi(rBuf);
                    if((n > 0) & (n < 4)){      // 配列データ数内か?
                         send_IOdata(n-1);      // IOdataコード送信
                    }
                }
                sFlg = 0;                       // シリアルフラグをクリア
                printf("\n\r");
            }
            if(PORTCbits.RC4 == 0){             // テストボタンが押された?
          // ---- 赤外線コード送信(テスト用、以下のどれかを有効に)
                send_SONY(sDat,sAdr);           // 前回データを送信
          //      send_IOdata(0);               // 赤外線送信(IOdata)
          //      send_NEC(0x847C,0x10EF);      // 赤外線送信(NEC)
          //      send_SONY(0x0200,0);          // 赤外線送信(SONY12)
          //      send_SONY(0xB4D0,0);          // 赤外線送信(SONY15)
          //      send_SONY(0xC000,0x1C5A);     // 赤外線送信(SONY20)
          //      send_SONY(0x8000,0);          // 赤外線送信(SONY20)
            }
/*          // ---- LCD画面シフト操作 (現在無効)
            if(PORTAbits.RA0 == 0){             // ボタン0が押されていたら
                LCD_sift(RIGHT);                // LCD表示右シフト
                __delay_ms(100);                // チャタリング対策
            }
            if(PORTAbits.RA1 == 0){             // ボタン1が押されていたら
                LCD_sift(LEFT);                 // LCD表示左シフト
                __delay_ms(100);                // チャタリング対策
            }
 */
            // ----- 赤外線信号受信 ----
            if(SMT1PWAIF){                      // mark終了なら
                SMT1PWAIF = 0;                  // フラグクリア
                PW[p_PW++]=SMT1CPW;             // High時間を保存
            }
            if(SMT1PRAIF){                      // space終了なら
                SMT1PRAIF = 0;                  // フラグクリア
                if(SMT1CPR < 10000){            // 10ms未満なら
                    PR[p_PR++] = SMT1CPR;       //    Low時間を保存
                }else{                          // 10ms以上なら
                    PR[p_PR++] = -(SMT1CPR / 1000); // msに変換し負数で保存
                }
            }
            if(SMT1IF){                         // 無信号が100ms続いたら
                SMT1IF = 0;                     // フラグクリア
                PR[p_PR++]=0;                   // 無信号記号「0」を保存
                break;
            }
        }
        SMT_STOP();                             // 測定終了

        // ===== 受信結果を解析・送信する =====
        ird = 0;
        sbt = 1;
        bct = 0;
        sgf = 0;
        LCD_clr();
        if(PW[0]<3000){
            Sony = 7;                           // -> SONY Mode
            IO_RC2_SetHigh();
            sprintf(sBf,"$SONY, %d",p_PW);
            LCD_str(sBf);
            puts(sBf);
        }else{
            Sony = 0;                           // -> NEC Mode
            IO_RC2_SetLow();
            sprintf(sBf,"$NEC, %d",p_PW);
            LCD_str(sBf);
            puts(sBf);
        }
        printf(" -  mark  space (us)\r\n");     // タイトル送信
        LCD_cursor(0,1);
        for(char i = 0; i < p_PW; i++){         // 保存した時間を送信
            printf("%2d %5d",i,(uint16_t)PW[i]); // mark時間
            if(PR[i]==0) {                      // 無信号なら
                printf("    ---");              //  「---」を送信
                if(Sony){
                    if(PW[i] < 1600){           // markが1600us未満なら   
                        bct++;
                        if(PW[i] > 900){        // markが900us超なら
                            ird |= sbt;         // シフトバイトを加える
                            printf("  1");
                        }else{
                            printf("  0");
                        }
                        prt_Hex(ird, 0);        // 16進数で表示
                    }
                } 
            }else if(PR[i] > 0){                // 10ms未満なら
                printf("  %5d",PR[i]);          //  us単位で送信(無改行)
                if (Sony){                      // *** SONY Code (Mark) 
                    if(PW[i] < 1600){           // markが1500us未満なら
                        sgf = 1;                // 有効データ
                        if(PW[i] > 900){        // markが900us超なら
                            ird |= sbt;         // シフトバイトを加える
                            printf("  1");
                        }else{
                            printf("  0");
                        }
                    }
                }else{                          // *** NEC Code (Space)
                    if(PW[i] < 1500){           // markが1500未満なら
                        if(PR[i]<2000){         // spaceが2000未満なら
                            sgf = 1;            // 有効データ
                            if(PR[i]>900){      // 更にspaceが900以上なら
                                ird |= sbt;     // シフトバイトを加える
                                printf("  1");
                            }else{
                                printf("  0");
                            }
                        }
                    }
                }
                if(sgf){                        // 有効データなら 
                    sbt <<= 1;                  // シフトバイトを左シフト
                    bct++;                      // 有効データ数を1増やす
                    if(Sony){                   // コードがSONYなら
                        if(bct == Sony){
                            prt_Hex(ird, 0);    // 16進数で表示
                            ird = 0;            // 赤外データをクリア 
                            sbt = 1;            // シフトバイトも初期化  
                            Sony = bct + 8;
                        }
                    }else{                      // コードがSONY以外
                        if((bct % 8) == 0){     // データ数が8の倍数?
                            prt_Hex(ird, 0);    // 16進数で表示
                            ird = 0;            // 赤外データをクリア 
                            sbt = 1;            // シフトバイトも初期化  
                        }
                    }
                    sgf = 0;
                }
            }else{                              // 10ms超なら
                printf("%7d (ms)",-PR[i]);      //   ms単位で送信
                 if(Sony){
                    if(PW[i] < 1600){           // markが1500us未満なら
                        bct++;
                        if(PW[i] > 900){        // markが900us超なら
                            ird |= sbt;         // シフトバイトを加える
                            printf("  1");
                        }else{
                            printf("  0");
                        }
                        prt_Hex(ird, 1);        // 16進数で表示
                        ird = 0;                // 赤外データをクリア 
                        sbt = 1;                // シフトバイトも初期化  
                        Sony = bct + 7;
                    }
                }
            }
            printf("\r\n");
        }
        if(p_PW >= MAXDATA){                    // バッファ満杯なら
            printf("-- Buffer Full --\r\n");    //  Buffer Full 送信
        }
    }
}
/*****   End of File   *****/

 
◆おまけ( eusartの設定 )
eusart設定は、「文字列の受信処理」を行っているabc869をそのまま利用しても良いが、ターミナルで入力を間違えたときにBSキーで1文字を消すことが出来る機能を追加した。具体的には、 PICのeusart1.hおよびeusart1.cに、以下のプログラムコードを追加(赤字
※ eusart1.hに以下の追加・修正が必要
/***	eusart1.h 	***/
#define BFSIZE 20                   // シリアル受信バッファーサイズ
 ・・・
※ eusart1.cに以下の追加・修正が必要
/***	eusart1.c	***/
#define EUSART1_TX_BUFFER_SIZE 8
#define EUSART1_RX_BUFFER_SIZE 8
extern char    rBuf[];              // シリアル受信バッファー
extern uint8_t sFlg;                // 受信完了フラグ
 ・・・

void EUSART1_Receive_ISR(void)
{
    static uint8_t sIdx;                // 受信文字列インデックス
    char ch;
    eusart1RxStatusBuffer[eusart1RxHead].status = 0;
    if(RC1STAbits.FERR){
        eusart1RxStatusBuffer[eusart1RxHead].ferr = 1;
        EUSART1_FramingErrorHandler();
    }
    if(RC1STAbits.OERR){
        eusart1RxStatusBuffer[eusart1RxHead].oerr = 1;
        EUSART1_OverrunErrorHandler();
    }    
    if(eusart1RxStatusBuffer[eusart1RxHead].status){
        EUSART1_ErrorHandler();
    } else {
        EUSART1_RxDataHandler();
    }
    // or set custom function using EUSART1_SetRxInterruptHandler()
    ch = getch();
    putch(ch);
    if((ch == 0x0a)||(ch == 0x0d)){         // 改行処理
//        putch('\n');              // 正しく改行されない場合に追加
        sFlg = 1;
        rBuf[sIdx] = 0;
        sIdx = 0;
    }else if((ch == 0x08)&&(sIdx > 0)){     // BS処理
        sIdx--;
    }else{
        if(sIdx < BFSIZE) rBuf[sIdx++] = ch;
    }
}
注1)変数sIdxのIは、Lの小文字ではなく、iの大文字! さらに、eusart内のstatic変数に変更した。
注2)ターミナルによって、改行が正しく行われない場合、putch('\n');を追加。(3/3追加)

< 参 考 >
  エスケープ文字
   


※ 本レポートの参考・利用は、あくまでも自己責任でお願いします。


デイジーコラージュPersonal 赤外線リモコン(12)SONYリモコンコード送信