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メモリ