Vol.897 28.Jun.2024

フォント(2)_文字コード PICミニBB(7)_内蔵EEPROMメモリ

F FONTあれこれ(2)  〜文字コード

by fjk

 フォントを使う場合、気をつけなければならないことがある。それは文字コードが正しくないと「文字化け」が発生する。
 文字コードとは,文字をコンピュータで処理したり通信したりするために,文字の種類に番号を割り振ったもので、「符号化文字集合」と「符号化形成」がある。符号化文字集合はUnicodeやJIS X 0208があり、それを基に符号化形成が行われて、UTF-8やShift-JISなどの文字コードがつくられている。

 主な文字コードは、

ASCII
最も基礎となっている文字コードで、英語圏で広く利用される。数字・アルファベット・記号をそれぞれ1バイトで表現しており、これらの文字の他に改行コードやタブ文字、制御コードなどが存在。
Shift-JIS
日本語の文字を表現するため、ASCIIコード文字に日本語文字を加えたのがShift-JIS。半角カタカナは1バイトで表現して、それ以外の全角文字は2バイトで表現。文字コードの領域に外字領域といわれる使ってない文字コードの領域があり、その領域に対してメーカーが独自の文字を設定することができる。
EUC (Extended UNIX Code)
EUCはUNIXというOSのために作られた文字コードで、英語以外の文字も扱うためのもので、多くの国の言語が収録されている。日本語を表わす場合EUC-JP、中国語はEUC-CN、韓国語はEUC-KRと言う。しかし、UNIX系OSのLinuxシステムでは、後述するUTF-8が主流となっている。
UNICODE
Unicodeは、世界中のほぼすべての文字をカバーする文字コードで、文字に対して一意な符号値(コードポイント)を割り当てて表現されている。多言語環境や国際化されたシステムで広く使用されており、異なる言語や文字体系を統合的に扱うことができる。
Unicodeは拡張性があり、新しい文字や絵文字が追加されるたびにバージョンが更新される。
さらに、ASCIIはUnicodeの一部になっており、ASCIIデータをUnicodeに変換することが容易。
UTF-8
UTF-8はUnicodeのエンコーディング方式の1つで、可変長エンコーディングを採用しており、ASCIIコードと互換性が有り、ASCIIコード以外の文字は、2〜6バイトで表現される(日本語の場合は3バイトで表現される)。ASCIIコードと互換性が良いのでパソコンで扱いやすく、インターネット上でのデータなど、世界中の多くのソフトウェアが対応している。
UTF-16
UTF-16はASCIIコードを使わず、基本的な世界中の文字の全てを2〜4バイトで表現する。半角アルファベットや半角数字、全角文字(日本語)のほとんどが2バイトで表現される。
 
【エディッタで文字コードの確認】
・秀丸エディッタで文字コードを確認する。
  1. 「その他」「動作環境」
  2. 「ウィンドウ」「ウィンドウ下部」「ステータスバー」、更に「詳細」をクリック
  3. 「カーソル位置の文字コード」にチェック
  4. ステータスバーにカーソル位置にある文字コードが表示される
  5. ステータスバーの文字コードを左クリックすると右画面が表示される

秀丸エディッタで文字コードの確認
【Wordで文字コードを確認】
  1. 調べたい文字をドラッグで選ぶ(文字が網かけとなる)
  2. 右ボタンをクリックし「記号と特殊文字」を選ぶと、フォント情報が表示される

【エンコード】
(文字)エンコード(符号化)は、(文字)コードデータを規則に従って変換すること。
コード変換ツールやサービスはあるが、組み込み用エンコーダーとなると・・。
そこで、エンコードを簡単に行うため、「UTF−シフトJIS変換テーブル」などが使われる。
  ・Unicode−sJIS
  ・JIS−sJIS−UTF8
 
【UTF-8のBOMありとBOMなし】
BOMとは「byte order mark」の略語で、どのような文字コードを使っているか文字コードの種類を表すもので、UTF-8の場合、BOMなしのデータの頭に「0xEF 0xBB 0xBF」の3バイトが加わる。
他のコードと混同しないためにBOM付が良さそうで、EXCELなどでは推奨されているが、サーバーによってはBOM付のHTMLやPHPファイルを正常に行えない場合があるので、BOM付ファイル無しが良いとされているものもある。
 
【参考】
  ・JIS X 0208文字コード一覧
  ・とほほの文字コード入門


P PICミニBBシリーズ(7)  〜内蔵EEPROMメモリ

by fjk

 多くのPICでは電源を切っても記憶内容が消えない256バイトのEEPROMを内蔵している。データを記憶するRAMは電源が切れるとデータが失われるので、データが消えないROMを使わない手はない。
 さらに、PICにはプログラムの記憶用にフラッシュメモリがあるが、1ワードが14ビット(PIC16Fxxxxの場合)で、16または32ワード単位のブロック単位でしか扱うことができず、1のビットに0の書込みしかできない。しかし、EEPROMは容量は少ないがバイト単位で読み書きができる。
 MCCを使ってEEPROMを使うには、MEMORYモジュールを用い、Add DataEE Routinesにチェックを入れてGenerateすると、以下の関数が使えるようになる(関数の実行には数msecかかる)。アドレスは0xF000から0xF0FFまでの256バイト。なお、Flashメモリ用の関数も同時に作成されるが、今回は使用しない。

/** EEPROMの指定アドレスに1バイトのデータを書き込む
    bAdd - 書き込みたいEEPROM アドレス
    bData - 書き込みたいデータ */

void DATAEE_WriteByte(uint16_t bAdd, uint8_t bData);  
 
/** EEPROMの指定アドレスから1バイトを読み出す
    bAdd - 読み出したいEEPROM アドレス */

uint8_t DATAEE_ReadByte(uint16_t bAdd);

MCCのMEMORY設定

 ハードはabc890のPIC-BAS14(PIC16F18325で再製作)を用い、USB接続パソコンと通信用にEUSARTも設定。

【eusart.c/hプログラム】 eusart.c/h(zip)

/***    eusart.h    ***/
#define BFSIZE 20               // シリアル受信バーファーサイズ
 ・・・


/***	eusart.c	***/
#define EUSART1_TX_BUFFER_SIZE 8
#define EUSART1_RX_BUFFER_SIZE 8
extern  char    RBuf[];         // シリアル受信バッファー
extern  uint8_t SFlg;           // 受信完了フラグ

static  uint8_t sIdx;           // 受信文字列インデックス
 ・・・

void EUSART1_Receive_ISR(void)
{
    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();
    }

    ch = getch();
    putch(ch);
    if((ch == 0x0a)||(ch == 0x0d)){	  // 改行処理
        SFlg = 1;
        RBuf[sIdx] = 0;
        sIdx = 0;
    }else if((ch == 0x08)&&(sIdx > 0)){	   // BS処理
        sIdx--;
    }else{
        if(sIdx < BFSIZE) RBuf[sIdx++] = ch;
    }
}
 ・・・
※ 全てのグローバル変数名の先頭文字を大文字に変更したので、eusart.c/hを再掲(赤字部を追加)。

★ パソコンターミナルからのコマンド(大文字小文字をとわない)は、以下のとおりで、文字入力後CRを入力。
U:  メモリダンプ
W:  (アドレス)データを書込
E:  全メモリに00hを書込み
F:  全メモリにFFhを書込み

【EEPROM読み書きテストプログラム】 abc897-18325t.c(zip)

/*******************************(abc897-18325t.c)*******
 *  EEPROM 読み書きテスト
 *******************************************************/
#include "mcc_generated_files/mcc.h"
char     RBuf[BFSIZE];        	// シリアル受信バッファ
uint8_t   SFlg;              	// シリアル受信フラグ

void EE_dump(void){
    uint16_t a;
    uint8_t  b;
    for(a = 0; a < 256; a++){
        if((a % 16)==0) printf("\n\r");
        b = DATAEE_ReadByte(0xF000 + a);
        printf("%02X ",b);
    }
}
void EE_write(void){
    uint16_t a;
    uint8_t  b;
    for(a = 0; a < 256; a++){
        if((a % 16)==0) printf("\n\r");
        DATAEE_WriteByte(0xF000 + a, (uint8_t)a);
        printf("*");
    } 
}
void EE_set(uint8_t dt){
    uint16_t a;
    for(a = 0; a < 256; a++){
        if((a % 16)==0) printf("\n\r");
        DATAEE_WriteByte(0xF000 + a, dt);
        printf(".");
    } 
}

/*-----------------------------------
 *       Main application
 *-----------------------------------*/
void main(void){
    uint8_t  cmd;

    SYSTEM_Initialize();
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();
    printf("ready\n\r>");
    
    while (1){
        if(SFlg) cmd = RBuf[0];
        else    cmd = 0; 
        switch (cmd){
            case 'u': case 'U': EE_dump();  break;
            case 'w': case 'W': EE_write(); break;
            case 'e': case 'E': EE_set(0);   break;
            case 'f': case 'F': EE_set(255); break;
            default :  break;    
        }
        if(cmd)printf("\n\r>");
        cmd =0;
        SFlg = 0;
        __delay_ms(500);
        IO_RA4_Toggle();
    }
}
/*****  End of File  ******/

EEPROM読み書き例(W-Uを実行)

【EEPROMを使用した簡易温度データロガー】
 EEPROMへデータの書込み・読出しを確認することができたが、応用として、さらに温度センサADT7410(秋月#106675、600円)のデータを記憶し、パソコンから条件設定や読出しができるようにした。ハードは前記のPIC-Base14とabc893の「改良LCDユニット」を用いた。(要i2cLCD_ST7032i.c/.hライブラリ[i2c1])
 ADT7410はI2C接続(アドレス:0x48)で、デフォルトのままデータを2回読み出すと、13ビット長(左詰め)測定データを、上位8ビットデータ、下位8ビットデータの順に取得できる。ADT7410の温度測定範囲は -55 〜 150℃ で、最上位ビットが1ならマイナス温度で、温度に変換する場合、13ビットなら16,16ビットなら128で割る。
  ★温度の計算例 (13ビットの場合、ADC_Dataは右に3ビットシフト後)
    ・プラスの温度  = ADC_Data/16
    ・マイナスの温度 = (ADC_Data - 8,192)/16

【ターミナルコマンド】(N,R,Uコマンドは小文字でもOK)

G: 測定開始
温度測定・記録実行中例
X: 測定終了
N: 測定データ数表示
T[n]:インターバル時間(n=1〜65,534), 
(秒単位、n無しは現在値表示)
R: 測定データ読み出し・表示
U: EEPROMデータのダンプ表示
Sn: EEPROMへデータ書込(n=0〜255),
(メモリテスト用)


テラターム画面例(T1-G-X-U-N-Rを実行)
(0C-D:インターバル、0E-F:データ数、10〜:温度データ)

※温度測定・記録プログラム (温度測定データはシフト前の16ビットデータでEEPROMに記録)

【I2C接続温度センサとEEPROMメモリ読み書きプログラム】abc897-18325TE.c(zip)

/***************************(abc897_18325TE.c)***
 *  I2C接続温度センサとEEPROMメモリ読み書き
 ************************************************/

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

#define  ADT_ADR  0x48      // ADT I2Cアドレス
#define  DT_START 0x10      // データ格納開始アドレス

//---- グローバル変数 ----------
char     SBuf[BFSIZE];      // 文字列操作バッファー
char     RBuf[BFSIZE];      // シリアル受信バッファー
uint8_t  SFlg;              // シリアル受信フラグ
uint16_t TpNum = 0;         // 所得温度データ数
uint16_t TmInt = 1;         // 測定間隔秒数

//---- EEPRONMから1ワード読み出す ----
uint16_t EE_ReadWd(uint8_t adr){
    uint16_t tp;
    tp  = (uint16_t)DATAEE_ReadByte(0xF000 + adr);
    tp |= (uint16_t)DATAEE_ReadByte(0xF001 + adr)<<8;
    return tp;
}

//---- EEPROMに1ワード書き出す ----
void EE_WriteWd(uint8_t adr, uint16_t dt){
    DATAEE_WriteByte(0xF000 + adr, (uint8_t)dt);
    DATAEE_WriteByte(0xF001 + adr, (uint8_t)(dt >> 8));
}

//---- EEPROMの内容をダンプリスト表示 ----
void EE_dump(void){
    uint16_t a;
    uint8_t  b;
    LCD_clr(); LCD_str("*Dump");
    puts("\n\r*Dump");
    for(a = 0; a < 256; a++){
        if((a % 16)==0) printf("\n\r");
        b = DATAEE_ReadByte(0xF000 + a);
        printf("%02X ",b);
    }
}

//---- EEPROMの全てに指定されたデータを書き込む --
void EE_setNum(char *str){
    uint16_t a;
    uint8_t  b;
    b = (uint8_t)atoi(str);
    if((b > 0) || (str[0] == '0')){
        for(a = 0; a < 256; a++){
            if((a % 16)==0) printf("\n\r");
            DATAEE_WriteByte(0xF000 + a, b);
            printf(".");
        }
    } 
}

//---- 温度データの表示 ----
void prt_Temp(uint16_t n, uint16_t tDat){
    uint16_t  tp;
    sprintf(SBuf,"%3d: %04X ",n,tDat);
    printf(SBuf);
    LCD_cursor(0,1); LCD_str(SBuf);
    if(tDat & 0x8000){                    	// 負数なら
        tp = ((8192 - (tDat >> 3)) * 10) / 16;
        sprintf(SBuf,"-%2d.%1dC\r", tp / 10, tp % 10);
    }else{                               	// 正数
        tp = ((tDat >> 3) * 10) / 16;
        sprintf(SBuf,"%3d.%1dC\r", tp / 10, tp % 10);
    }
    puts(SBuf);
    LCD_cursor(10,1); LCD_str(SBuf);
}

//---- 温度データを読み出す -----
void readTemp(void){
    uint8_t  a,n;
    uint16_t   tp;
    for(n = 0; n < TpNum; n++){
        a = n * 2 + DT_START;
        tp = EE_ReadWd(a);
        prt_Temp(n,tp);
    }
}

//---- タイムインターバルの設定 ----
void setTmInt(char *str){
    uint16_t t;
    if(strlen(str) > 1){
        t = (uint16_t)atoi(&str[1]);
        if((t > 0) && (t < 0xFFFF)){
            TmInt = t;
            EE_WriteWd(0x0C,TmInt);
        }
    }
    printf("\n\rInterval: %d sec\n\r",TmInt);
}

/*-----------------------------------
 *       Main application
 *-----------------------------------*/
void main(void){
    uint8_t eAdr;               // EEPROMアドレス
    uint16_t tct;               // インターバルカウンタ
    uint8_t  md = 0;            // 0:測定停止、1:測定中
    uint8_t  cmd = 0;           // 指示コマンド
    uint16_t temp;              // 測定温度データ

    SYSTEM_Initialize();
    LCD_init();
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();
   
    tct = TmInt;
    eAdr = DT_START;
    sprintf(SBuf,"ready\n\r>");
    printf(SBuf);
    LCD_str(SBuf);
    
    while (1)    {
        if(SFlg){
            cmd = RBuf[0];
            LCD_clr(); LCD_str(RBuf);
        }
        //--- コマンド解析
        switch (cmd){
            case 'G': md = 1; puts("*start\r");
                        eAdr = DT_START; TpNum = 0; break;
            case 'X': md = 0; puts("*stop\r"); break;
            case 'N': case 'n':
                        printf(" Num: %d\n\r",TpNum);  break;
            case 'T': setTmInt(RBuf);   break;
            case 'R': case 'r':
                        readTemp();   break;
            case 'U': case 'u': 
                        EE_dump();   break;
            case 'S': EE_setNum(RBuf + 1); break;
            default :   break;    
        }
        //--- 測定動作
        if(md){
            I2C1_ReadNBytes(ADT_ADR,(uint8_t *)&temp,2); // 温度データ読込
            temp = (temp<<8) | ((uint8_t)(temp>>8));     // 上下バイトデータ入替
            if(tct == 1){
                prt_Temp(TpNum, temp);
                if(eAdr < 255){
                    EE_WriteWd(eAdr,temp); 
                    eAdr += 2;
                    TpNum++;
                    EE_WriteWd(0x0E,TpNum);
                }
                tct = TmInt;
            }else{
                tct--;
            }
        }
        if(cmd && (md == 0)) printf("\n\r>");
        cmd = 0; SFlg = 0;
        IO_RA4_SetHigh();
        __delay_ms(400);
        IO_RA4_Toggle();
        __delay_ms(550);            // ほぼ1秒確保
    }    
}
/******  End of File  ******/


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


フォント(2)_文字コード PICミニBB(7)_内蔵EEPROMメモリ