Vol.905 25.Oct.2024

フォントデータのROM化(2) Highlight.js PICミニBB(15)_フォントROMで漢字文字列表示

F Excel_VBAによるフォントデータのROM化(2) 〜データの連続作成・書込

by fjk

 abc904で手動によるフォントデータの書き込みが出来るようになったが、3,000以上もある全角(漢字)フォントを手動で書き込むことは大変である。そこで、ある程度自動でフォントパターンデータの書き込みができるように、範囲指定(開始−終了)セルとAutoボタンを追加した(手動でも使えます)。
 なお、Excel_VBAで文字コードから漢字を表示するchr()関数の引数はsift-JISにする必要があり、JISコードからのコード変換関数Jis2SJis()を作成した(WorksheetFunctionでchar()関数が使えないので)。
 自動化に伴い、saveHexコマンドから、確認のためのmsgboxを削除し、saveHex_m関数でsaveHex関数とmsgboxを実行するようにした(手動実行時はmasgbox表示)。PICはabc902〜abc905のBBセットが対応。

【Auto機能の使い方】 (注:全/半角文字コードとファイルの文字コードを合わせること!)
@ CMDセルにコマンド(例えば'W')、Addressセルに書き込みたいROMアドレスを16進数で入力
A Startセルに書き込みを開始したいJIS文字コード、Endセルに終了したいJIS文字コードを16進数で入力
B 「Auto」ボタンを押すと、文字パターンの表示、HEXデータ保存、ROMへの書込を実行
C Autoが始まると、パラパラとフォントパターンが表示され、書込終了後アドレスは加算される
・ 実行速度は遅い(4秒弱/文字)が、その間、書込データの確認もできる(ESCキーで途中中断可)
【フォント格納マップ】番地は開始番地
(番地)
(文字種類)
00200
半角ASCII
01000
全角記号
01E00
全角数字
02000
全角ASCII(大文字)
02400
全角ASCII(小文字)
02800
全角ひらかな
03260
全角カタカナ
04000
全角第1水準漢字
 
00C00
ユーザ文字マップ(コード)
1C000
ユーザー文字フォント
  ※ユーザー文字は将来拡張用

フォントパターンの自動書込例
★フォントデーター格納例(FontRom.xlsx(zip)
 
【自動フォントデータ書込EXCELファイル】 BDFViewerWriterA.xlsm(zip)
 
【Excel VBAプログラム】 BDFViewerWriterA.bas(zip)

Option Explicit
Dim filePath As Variant
Dim strPath     As String
Dim shtNam As String
Dim RowSize As Long
Dim CorSize As Long

Sub BDF_Load()
  filePath = Application.GetOpenFilename(filefilter:="BDFファイル(*.bdf),*.bdf")
  If filePath = False Then Exit Sub
  Dim fi As Variant
  fi = Array(Array(1, xlTextFormat))
  Dim maxRow As Long
  Workbooks.OpenText Filename:=filePath, FieldInfo:=fi, Space:=True
  Dim ws As Worksheet
  Set ws = Workbooks.Item(Workbooks.Count).Sheets(1)
  ws.Move After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)
  Dim shtNam As String
  shtNam = ActiveSheet.Name                    ' 新シート名を取得
  Worksheets("16dot").Range("C1").Value = shtNam
  maxRow = ThisWorkbook.Sheets(shtNam).Cells(1, 1).End(xlDown).row
  MsgBox shtNam & " をロードしました " & maxRow
  Worksheets("16dot").Select
End Sub

Sub findKey()
  Dim ws As Worksheet
  Set ws = Worksheets(Worksheets.Count)
  Dim kw As String
  kw = "ENCODING"
  RowSize = 16
  CorSize = 16
  Dim code As Long
  code = Worksheets("16dot").Range("B24").Value
  Dim i As Integer
  Dim r As Range
  With ws.Range("A:A")
    Worksheets("16dot").Range("C1").Value = ws.Name
    Set r = .Find(what:="FONTBOUNDINGBOX", LookAt:=xlWhole)
    If r Is Nothing Then
      Exit Sub
    Else
      CorSize = r.Offset(0, 1).Value
      Worksheets("16dot").Range("C2") = CorSize
      RowSize = r.Offset(0, 2).Value
      Worksheets("16dot").Range("E2") = RowSize
    End If
    Set r = .Find(what:=kw, LookAt:=xlWhole)
    Do
      If r Is Nothing Then
        MsgBox ("該当無し")
        Exit Do
      Else
        If r.Next.Value = code Then
          For i = 1 To 16
            If i <= RowSize Then
              Worksheets("16dot").Range("B4").Offset(i) = r.Offset(4 + i).Value
            Else
              Worksheets("16dot").Range("B4").Offset(i) = 0
            End If
          Next i
          Worksheets("16dot").Select
          Exit Do
        Else
          Set r = .FindNext(r)
        End If
      End If
    Loop While Not r Is Nothing
  End With
End Sub

Sub saveHex()
  Dim fso         As Object
  Dim tso         As Object
  Dim hData()     As String
  Dim i As Integer, j As Integer, rofs As Integer, dsz As Integer, cnt As Integer
  Set fso = CreateObject("Scripting.FileSystemObject")
  strPath = Worksheets(Worksheets.Count).Name & ".csv"
  Set tso = fso.OpenTextFile(strPath, 8, True)
  RowSize = Worksheets("16dot").Range("E2")
  CorSize = Worksheets("16dot").Range("C2")
  rofs = 10 + 16 - CorSize
  dsz = CorSize * 2 + 2
  ReDim hData(dsz) As String
  Debug.Print CorSize & " " & dsz
  cnt = 0
  For j = 0 To 1
    For i = 0 To CorSize - 1
      hData(cnt) = "0x" & Cells(21 + j, rofs + i).Value
      cnt = cnt + 1
    Next i
  Next j
  hData(cnt) = "//"
  hData(cnt + 1) = Cells(23, 2)
  hData(cnt + 2) = Cells(23, 4)
    
  Debug.Print "data-saved"
  With tso
    .WriteLine Text:=Join(hData, ",")
    .Close  				'ファイルのクローズ
  End With
  Set fso = Nothing
  Set tso = Nothing
End Sub

Sub saveHex_m()
  Call saveHex
  MsgBox CurDir() & "\" & strPath & " にデータを追加しました"
End Sub

Sub ShowFolderPicker()
  Dim fd As FileDialog
  Set fd = Application.FileDialog(msoFileDialogFolderPicker)
  With fd
    If .Show = -1 Then
      MsgBox "選択されたフォルダ: " & .SelectedItems(1)
      ChDir Path:=.SelectedItems(1)
    Else
      MsgBox "フォルダが選択されませんでした。"
    End If
  End With
End Sub

Sub SerialEC()
  Dim cmd As String
  Dim row As Integer
  Dim rcv As String
  Dim S_Time As Date

  ec.COMn = Range("AB2")          	'@初期設定
  ec.Setting = "115200,n,8,2"
  ec.AsciiLineTimeOut = 1000
  cmd = Range("AB4")              	'Aデータの送信
  ec.AsciiLine = cmd
  row = 8                         	'Bデータの受信
  Range("AB6:AB40").Value = ""
  S_Time = Now
  Do
    If Now > S_Time + TimeSerial(0, 0, 1) Then
      Exit Do                 		'TimeOut 2sec
    End If
    DoEvents
    Debug.Print "EC: " & ec.InBuffer
    If ec.InBuffer > 0 Then
      DoEvents
      rcv = ec.AsciiLine
      Cells(row, 28) = rcv
      row = row + 1
      S_Time = Now
      Exit Do
    End If
  Loop
  ec.InBufferClear                '受信バッファをクリア
  ec.COMn = -1                    'Cポートを閉じる 終了処理
  Range("AB4").Value = ""         'D送信データの消去
End Sub

Sub setSerialData()
  Dim sData()     As String
  Dim i As Integer, j As Integer, rofs As Integer, dsz As Integer, cnt As Integer
  RowSize = Worksheets("16dot").Range("E2")
  CorSize = Worksheets("16dot").Range("C2")
  rofs = 10 + 16 - CorSize
  dsz = CorSize * 2 + 1
  ReDim sData(dsz) As String
  If Range("AC2") = "W" Or Range("AC2") = "S" Then
    sData(0) = Range("AC2") & Range("AD2")
    cnt = 1
  Else
    cnt = 0
  End If
  For j = 0 To 1
    For i = 0 To CorSize - 1
      sData(cnt) = Cells(21 + j, rofs + i).Value
      Debug.Print cnt; " "; sData(cnt)
      cnt = cnt + 1
    Next i
  Next j
  Cells(4, 28) = Join(sData, ",")
End Sub

Sub addAddress()
  Dim adr As Variant
  Dim hdt As Variant
  hdt = WorksheetFunction.Hex2Dec(Range("ad2"))
  adr = Val(hdt) + Range("C2") * 2
  Range("AD2") = WorksheetFunction.Dec2Hex(adr, 5)
End Sub

Sub sendPlus()
  Call SerialEC
  Call addAddress
End Sub

Function Jis2SJis(jCd As Variant) As Variant
    Dim hi As Variant, lo As Variant
    hi = Int(jCd / 256)
    lo = jCd Mod 256
    If hi And 1 Then
        If lo < &H60 Then
            lo = lo + &H1F
        Else
            lo = lo + &H20
        End If
    Else
        lo = lo + &H7E
    End If
    If hi < &H5F Then
        hi = (hi + &HE1) \ 2
    Else
        hi = (hi + &H161) \ 2
    End If
    Jis2SJis = hi * &H100 + lo
End Function

Sub AutoSet()
  Dim cd As Variant, kj As Variant
  Dim cds As Integer, cde As Integer
  Dim sMs As Integer, sLs As Integer
  Dim eMs As Integer, eLs As Integer
  Dim cMs As Integer, cLs As Integer
  Dim n As Integer, s As Integer, e As Integer
  cds = WorksheetFunction.Hex2Dec(Range("B26"))
  sMs = Int(cds / 256)
  sLs = cds Mod 256
  cde = WorksheetFunction.Hex2Dec(Range("B27"))
  eMs = Int(cde / 256)
  eLs = cde Mod 256
  n = 0
  For cMs = sMs To eMs
    If cMs = sMs Then
      If cMs < eMs Then
        s = sLs
        e = &H7E
      Else
        s = sLs
        e = eLs
      End If
    Else
      s = &H21
      If cMs = eMs Then
        e = eLs
      Else
        e = &H7E
      End If
    End If
    For cLs = s To e
      cd = cMs * 256 + cLs
      Debug.Print Hex(cd)
      If cd < 256 Then
        If cd = &H27 Then
          Cells(23, 4) = "''"
        Else
          Cells(23, 4) = Chr(cd)
        End If
      ElseIf cd = &H2147 Then
        Cells(23, 4) = "'’"
      Else
        kj = Jis2SJis(cd)
        If kj = 0 Then
          Debug.Print "Code Error!!"
          Exit Sub
        Else
          Cells(23, 4) = Chr(kj)
        End If
      End If
      DoEvents
      Call findKey
      DoEvents
      Call saveHex
      DoEvents
      Call setSerialData
      DoEvents
      Call SerialEC
      DoEvents
      Call addAddress
      DoEvents
      n = n + 1
    Next
  Next
End Sub


H HTMLでソースコードを見やすく  〜Highlight.js

by fjk

 Web画面で、ソースコードを見やすくする方法としてシンタックスハイライト機能が有りますが、そのうち、わずか数行コードを追記するだけで、ハイライト機能を実現できるhilight.jsを使ってみた。
 hilight.jsはJavaScripyライブラリーで、<pre><code>タグと</code></pre>タグ内に記述されたソースコードをハイライト処理してくれます(ハイライトの種類はstylesフォルダー内を参照)。
 言語を自動で判別してくれる機能もありますが、codeタグ内でclassを使って言語を指定することができます。例えば、<code class="basic">(使用できる言語は、languagesフォルダー内を参照)。

 hightlight.jsを利用するには以下の2つの方法がある。

1.ネットワーク経由でCDNのJavaScriptを読込、実行する
・以下のコードをコピーするだけで使え、動作が軽い。
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.css">
	<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/go.min.js"></script>
	<script>hljs.highlightAll();
2.ファイルをダウンロード後、解凍し、任意のフォルダーに保存後、Javascriptを実行する
・対応言語が多いが、多くの言語やスタイルをサーバーにアップすると、サーバーに負荷がかかる。
・呼出すファイルのフォルダーにhighlightフォルダー以下を保存した場合、以下を記述
	<link rel="stylesheet" href="highlight/styles/base16/windows-95.css">
	<script src="highlight/highlight.min.js"></script>
	<script>hljs.highlightAll();</script>

※ 上記の例で、最後のscript行以外は<head></head>内で記述、最後の行は<body></body>内の最後に記述が勧められているが、まとめてどちらに記述しても動作した。
※ 最後の行のscriptの実行ファイルがhljs.highlightAllと新しくなっていることに注意。

highlight.jsの入手は:hilight.js からdownload
highlight.jsの使い方などは:Web_Design_Leaves など


hilight.js未使用(preとcodeの記述順を逆に)

hilight.jsを使用


P PICミニBBシリーズ(15)  〜フォントROMで漢字文字列表示

by fjk

 前項で、フォントROMへ全角漢字フォントを書込めたので、全角文字/文字列表示関数を追加した。なお、PIC16F18325ではメモリが少なく(プログラムメモリが98.9%使用)、とりあえずバイト単位位置に全角(漢字)文字(列)を表示する関数のみを作成した(ドット位置単位の表示やユーザ文字は使用不可)。
 表示できるフォントはROM内の全てのフォントではなく、記号の一部を除くなど、代表的なフォントのみ表示可(前項のフォントデータ格納例の背景が薄黄色の文字のみ)。

   開発には、以下の項目に注意した(フォントデータが既に書き込まれていること)。

  • MPLAB-Xの文字コードはsift-JISだが、フォントROMはJIS => PICでsift-JISからJISに変換
    (EUC-JPを使うと、EUC-JP = JIS + 8080H と変換が簡単だが、windowsではsift-JISが主流なので・・、
     また、UTF-8はJISとの対応が複雑で可変長の文字になるので今回はパス)
  • XC8では、漢字文字の配列と文字列はエンディアンが異なる => 引数で選択できるようにした
  • XC8コンパイル時に、漢字は「文字エンコーディングが正しくない」として警告が出る => 無視

漢字(全角)配列表示例(Zコマンド)

漢字(全角)文字列表示例(Yコマンド)
【ハードウェア】
abc903のBBセットをそのまま利用。
 
【ソフトウェア】
テストのために以下のコマンドを追加した
  Lコマンド  漢字(全角)1文字表示
  Zコマンド  漢字(全角)配列表示
  Yコマンド  漢字(全角)文字列表示

【abc905-18325.cファイル】 abc905-18325.c(zip)

/*******************************(abc905-18325)*********
 * SPI接続メモリ(24LC1024/23LCV1024) with AQM1248A  
 ******************************************************/

#include "myProject.h"

char     sBf[BFSIZE];                           // 文字列作業エリア
char     RBuf[BFSIZE];                          // シリアル受信バッファ
uint8_t  SFlg;                                  // シリアル受信フラグ

uint16_t KANJI[] = {'漢','字',0};


//---- ターミナルにプロンプト文字表示
void prtPrompt(uint8_t m){
    if(m)  printf("# ");                        // 連続書込モード中
    else   printf("$ ");                        // 通常書込モード
}

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

    SYSTEM_Initialize();
    SSP1CON1bits.SSPEN = 1;                     //SPI1を有効に
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    gLCD_int();
    gLCD_clr(0);                                // 黒ベタは0xFF
    puts("Ready\r");                            // 動作確認用
    gLCD_strX(0,0,"Ready");
    prtPrompt(spiMem_SqMode());
    while (1){
        if(SFlg){
            gLCD_clr(0);                        // グラフィック画面消去
            gLCD_strX(0,0,RBuf);                // LCDにコマンド表示
            cmd = RBuf[0];                      // コマンドは第1文字
            #ifndef VBA
                printf("\n\r");
            #endif
            switch(cmd) {                       // 各コマンドの実行
                case 'W': spiMem_cmd_W(RBuf);  break;
                case 'R': spiMem_cmd_R(RBuf);  break;
                case 'Q': spiMem_cmd_Q(RBuf);  break;
                case 'U': spiMem_cmd_U(RBuf);  break;
                case 'S': spiMem_cmd_W(RBuf);  break;
                case 'P': spiMem_cmd_P(RBuf);  break;
                case 'K': spiMem_cmd_K(RBuf);  break;
                case 'L': gLCD_ZchrX(0,32,'愛',0); break;
                case 'Y': gLCD_ZstrX(0,32,"文字列です",1); break;
                case 'Z': gLCD_ZstrX(0,32,KANJI,0); break;
                                                // 有効なコマンドがなく
                default:  if(spiMem_SqMode()){
                            spiMem_cmd_WSQ(RBuf);
                          }                     // 連続書込モード中なら
                          break;                // 書込シーケンスを実行
            }
            SFlg = 0;
            #ifndef VBA
                prtPrompt(spiMem_SqMode());
            #endif
        }
        IO_RA4_Toggle();
        __delay_ms(500);
    }
}

 
【myProject.hファイル】 myProject.h(zip)
abc904/myProject.h のLCD表示ヘッダーファイルからの変更点
 ・グラフィックLCD表示用ヘッダーファイルを "AQM1248A_v4.h" に変更
 ・メモリ操作用バッファーサイズの指定 "#define MBFSIZE 128" を追加
 
【spi_Mem3.c/hファイル】 spi_Mem3.c/h(zip)
abc904のspi_Mem3.cから以下の記述をコメントに(myProject.hで宣言したため)
 ・"//#define MBFSIZE 128 // メモリ操作用バッファーサイズ"
 
【AQM1248A_v4.hファイル】 AQM1248A_v4.c/h + Font_5x7.h(zip)

/******************************(AQM1248A_v4.h)************
 *   Grafic LCD  AQM1248A_m.h 用ライブラリ(FONT別)
 **********************************************************/
/*---  G_LCDを初期化する */
void gLCD_int(void);

/*---  G_LCDのRAMの書込みアドレスを指定する
 *	  表示文字位置(xpos,ypos) */
void gLCD_posyx(uint8_t xpos, uint8_t ypos);

/*---  G_LCDのRAMを全消去(指定データで埋める)
 *	  埋めたい1バイトデータ(dat) */
void gLCD_clr(char dat);

/*---  G_LCDにchrコードの文字パターンを表示
 *    表示位置(ypos,xpos)、表示文字(ch)
 *  ・戻り: 次表示X文字位置  */
uint8_t gLCD_chrX(uint8_t xpos, uint8_t ypos, char ch);

/*---  文字列strをG_LCDに表示する
 *	  表示位置(xpos,ypos)、表示文字列(str)
 * ・戻り: 次表示X文字位置  */
uint8_t gLCD_strX(uint8_t xpos, uint8_t ypos, char *str);

/*---  ビットパターンをG_LCDに表示する
 *	表示位置(xpos, ypos)、パターン(*ptn),バイト数(n)
 *  ・戻り: 次表示X文字位置 */
uint8_t gLCD_ptnX(uint8_t xpos, uint8_t ypos, char *ptn, uint8_t n);

/*---  指定したdatで指定数nだけ表示
 *	表示位置(xpos, ypos)、データ(dat)・・1バイト, 転送数(n) 
 *  ・戻り: 次表示X文字位置 */
uint8_t gLCD_clrX(uint8_t xpos, uint8_t ypos, char dat, uint8_t n);

/*--- バイト単位で16ドットパターン(漢字など)の表示
 *	  表示位置(xpos, ypos)、データ配列(k)
 *    フォントアドレス(*pKj)、 横ドット数(cdt) 
 *  ・戻り: 次表示X文字位置 */
uint8_t gLCD_nk16X(uint8_t xpos, uint8_t ypos, char *pKj, uint8_t cdt);

//---- バイtト単位で半角(8x16)文字表示(次xpos位置を返す)
/*	  表示位置(xpos, ypos)、半角文字コード(chCode)
 *  ・戻り: 次表示X文字位置 */
uint8_t gLCD_HchrX(uint8_t xpos, uint8_t ypos, uint8_t chCode);

//---- バイト単位で半角文字列表示(次xpos位置を返す)-----
/*	  表示位置(xpos, ypos)、半角文字列(*str)
 *  ・戻り: 次表示X文字位置 */
uint8_t gLCD_HstrX(uint8_t xpos, uint8_t ypos, char *str);

//---- バイト単位で行をクリアして半角文字列表示(次xpos位置を返す)
/*	  表示位置(xpos, ypos)、半角文字列(*str)
 *  ・戻り: 次表示X文字位置 */
uint8_t prtHstr_LnClr(uint8_t xpos, uint8_t ypos, char* str);

//---- 漢字フォントアドレス取得
/*      文字コード(JIS) :kCode
 *  ・戻り: フォントアドレス  */
uint32_t KfntAdr(uint16_t kCode);

//---- バイト単位位置で全角(漢字)文字を表示
/*      表示位置(xpos,ypos)、文字コード:kCd(S-JIS)
 *      エンディアン:ed (0:ビッグ、1:リトル)
 *  ・戻り、次表文字位置 */
uint8_t gLCD_ZchrX(uint8_t xpos, uint8_t ypos, uint16_t kCd, uint8_t ed);

//---- バイト単位位置で全角(漢字)文字列を表示
/*      表示位置(xpos,ypos)、文字列:*str (S-JIS)、
 *      エンディアン:ed (0:配列文字、1:文字列)
 *  ・戻り、次表文字位置 */
uint8_t gLCD_ZstrX(uint8_t xpos, uint8_t ypos, char *str, uint8_t ed);


/*========================================*
 *     ドット単位位置で描画               *
 *========================================*/
/*---  ドット単位で点を描く
 *    表示位置(xpos, ypos)、表示色(col、0:白/1:黒/8:反転)*/
 void gLCD_PSet(uint8_t xpos, uint8_t ypos, uint8_t col);

/*---  ドット単位で文字を描く(次xpos位置を返す)
 *    表示位置(xpos, ypos)、表示文字コード(ch)
 *    表示色(col、0:白/1:黒/8:反転)
 * ・戻り: 次表示X文字位置 */
 uint8_t gLCD_PutChr(uint8_t xpos, uint8_t ypos, char ch, uint8_t col);

/*---  ドット単位で文字列を描く(次xpos位置を返す)
 *    表示位置(xpos, ypos)、表示文字列(*str)
 *    表示色(col、0:白/1:黒/8:反転)
 *  ・戻り: 次表示X文字位置 */
 uint8_t gLCD_PutStr(uint8_t xpos, uint8_t ypos, char *ptn, uint8_t col);

/*-------ドット単位でパターンを描く(次xpos位置を返す)
 *    表示位置(xpos, ypos)、表示パターン(*ptn)・・バイト単位
 *    転送数(n)、表示色(col、0:白/1:黒/8:反転)
 *  ・戻り: 次表示X文字位置 */
 uint8_t gLCD_PutPtn(uint8_t xpos, uint8_t ypos, char *ptn, uint8_t n, uint8_t col);

/*----------ドット単位で直線を描く
 *    開始位置(x0,y0)、終了位置(x1,y1)、
 *    表示色(col、0:白/1:黒/8:反転)*/
 void gLCD_Line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t col);

/*--- バイト単位で16ドットパターン(漢字など)の表示
 *    表示位置(xpos, ypos)、データ配列(k)
 *    データ要素番号(kj)、 横ドット数(cln)
 *    表示色(col、0:白/1:黒/8:反転)
 *  ・戻り: 次表示X文字位置 */
uint8_t Put_nK16(uint8_t xpos, uint8_t ypos, char *k, uint8_t cln, uint8_t col);

//---- ドット単位で半角文字表示(次xpos位置を返す)
/*    表示位置(xpos, ypos)、半角文字コード(chCode)
 *    表示色(col、0:白/1:黒/8:反転)
 *  ・戻り: 次表示X文字位置 */
uint8_t Put_HchrX(uint8_t xpos, uint8_t ypos, uint8_t chCode,uint8_t col);

//---- ドット単位で半角文字列を表示(次xpos位置を返す)
/*    表示位置(xpos, ypos)、半角文字列(*str)
 *    表示色(col、0:白/1:黒/8:反転)
 *  ・戻り: 次表示X文字位置 */
uint8_t Put_HstrX(uint8_t xpos, uint8_t ypos, char *str, uint8_t col);

 
【AQM1248A_v4.cファイル】

/***********************************(AQM1248A_v4.c)*******
 *     AQM1248A グラフィックライブラリ(spi-vRam付き)
 **********************************************************/

#include "myProject.h"
#include "Font_5x7.h"

#define VRAM_ADR    0x80000
#define LCG_C_SIZE  128
#define HASC_OFS    0x00200
#define H_BLANK     0x007F0

static uint8_t vBf[32];                         // フォントパターン用バッファー

//--- SPI-vRam アクセス関数
uint8_t vRam_read(uint8_t xpos, uint8_t ypos){
	uint32_t adr;
	adr = VRAM_ADR + (uint32_t)ypos * LCG_C_SIZE + xpos;
	return SPI_Mem_Read(adr);
}

void vRam_write(uint8_t xpos, uint8_t ypos, uint8_t dat){
	uint32_t adr;
	adr = VRAM_ADR + (uint32_t)ypos * LCG_C_SIZE + xpos;
	SPI_Mem_Write(adr, dat);
}

void vRam_Nread(uint8_t xpos,uint8_t ypos,char *ptn,uint8_t n){
	uint32_t adr;
	adr = VRAM_ADR + (uint32_t)ypos * LCG_C_SIZE + xpos;
	SPI_Mem_NRead(adr,(uint8_t *)ptn,n);
}

void vRam_Nwrite(uint8_t xpos,uint8_t ypos,char *ptn,uint8_t n){
	uint32_t adr;
	adr = VRAM_ADR + (uint32_t)ypos * LCG_C_SIZE + xpos;
	SPI_Mem_NWrite(adr,(uint8_t *)ptn,n);
}

//-------- G-LCDを初期化 ------------------------------------
void gLCD_int (void){
    gCS_SetLow();                               // gLCDセレクト有効
    gRS_SetLow();                               // コマンドデータ指定
    SPI1_ExchangeByte(0xAE);                    // Display = OFF
    SPI1_ExchangeByte(0xA0);                    // ADC = normal
    SPI1_ExchangeByte(0xC8);                    // C.output = revers
    SPI1_ExchangeByte(0xA3);                    // bias = 1/7
    SPI1_ExchangeByte(0x2C);                    // power control 1
    __delay_ms(2);                              // 2mS遅延
    SPI1_ExchangeByte(0x2E);                    // power control 2
    __delay_ms(2);                              // 2mS遅延v
    SPI1_ExchangeByte(0x2F);                    // power control 3
    SPI1_ExchangeByte(0x23);                    // Vo ratio set
    SPI1_ExchangeByte(0x81);                    // E-volume mode
    SPI1_ExchangeByte(0x1C);                    // E-volume value
    SPI1_ExchangeByte(0xA4);                    // 全点灯しない
    SPI1_ExchangeByte(0x40);                    // start line = 0
    SPI1_ExchangeByte(0xA6);                    // 白黒反転しない
    SPI1_ExchangeByte(0xAF);                    // Display = ON
    gCS_SetHigh();                              // gLCDセレクト無効
}

//-------- G-LCDのデータを全消去(指定データで埋める)---
void gLCD_clr (char dat){
    uint8_t x, y;
    gCS_SetLow();                               // gLCDセレクト有効
    for(y = 0; y < 6; y++){
        gRS_SetLow();                           // コマンド指定
        SPI1_ExchangeByte(0xB0 + y);
        SPI1_ExchangeByte(0x10);
        SPI1_ExchangeByte(0x00);
        gRS_SetHigh();                          // 表示データ指定
        for(x = 0; x < 128; x++){
            SPI1_ExchangeByte(dat);
        }
    }
    gCS_SetHigh();                              // gLCDセレクト無効
    for(y = 0; y < 6; y++){
        for(x = 0; x < 128; x++){
            vRam_write(x,y,dat);                // vRamクリア
        }
    }
}

//-------- カーソル位置指定 -----------------------------------
void gLCD_posXY(uint8_t xpos, uint8_t ypos){
    gCS_SetLow();                               // チップセレクト有効
    gRS_SetLow();                               // コマンド指定
    SPI1_ExchangeByte(0xB0 | (ypos >> 3));
    SPI1_ExchangeByte(0x10 | (xpos >> 4));
    SPI1_ExchangeByte(xpos & 0x0F);
    gCS_SetHigh();                              // チップセレクト無効
}

//-------- バイト単位で文字表示(次xpos位置を返す) ---------
uint8_t gLCD_chrX(uint8_t xpos, uint8_t ypos, char chr){
    uint8_t i, sx, yAdr, ptn;
    chr -= 0x20;                                //配列アドレスを計算
    gLCD_posXY(xpos, ypos);                     //表示位置を指定
    sx = xpos;                                  // posxを保存
    yAdr = ypos >> 3;
    gCS_SetLow();                               // チップセレクト有効
    gRS_SetHigh();                              // 表示データ指定
    for(i = 0; i < 5; i++) {                    //データを順に取得
        ptn = Font[chr][i];
        if(ptn == 0xFF)  break;                 //幅狭文字なら抜ける
        vBf[i] = ptn;
        SPI1_ExchangeByte(ptn);
        xpos++;
    }
    vBf[i] = 0;
    SPI1_ExchangeByte(0);                       //文字間隔を空ける
    xpos++;
    gCS_SetHigh();                              // チップセレクト無効
    vRam_Nwrite(sx,ypos,(char *)vBf, i);        // vRamに書き込む 
    return xpos;                                //次表示位置を返す
}

//-------- バイト単位で文字列表示(次xpos位置を返す)-----
uint8_t gLCD_strX(uint8_t xpos, uint8_t ypos, char *str){
    while(*str)  xpos = gLCD_chrX(xpos, ypos, *str++);
    return xpos;                                //次表示位置を返す
}

//-------- バイト単位でパターンを表示(次xpos位置を返す)
uint8_t gLCD_ptnX(uint8_t xpos, uint8_t ypos, char *ptn, uint8_t n){
    uint8_t i, yAdr;
    gLCD_posXY(xpos, ypos);                     // 表示位置を指定
    gCS_SetLow();                               // チップセレクト有効
    gRS_SetHigh();                              // 表示データ指定
    yAdr = ypos >> 3;
    for(i = 0; i < n; i++){
         SPI1_ExchangeByte(*(ptn + i));
    }
    gCS_SetHigh();                              // チップセレクト無効
    vRam_Nwrite(xpos,ypos,ptn,n);
    return xpos + n;                            // 次表示位置を返す
}

//---- バイト単位でn個の指定データで埋める(次xpos位置を返す)
uint8_t gLCD_clrX(uint8_t xpos, uint8_t ypos, char dat, uint8_t n){
    uint8_t i, yAdr;
    gLCD_posXY(xpos,ypos);                      // 表示位置を指定
    gCS_SetLow();                               // チップセレクト有効
    gRS_SetHigh();                              // 表示データ指定
    yAdr = ypos >> 3;
    for(i = 0; i < n; i++) {
        SPI1_ExchangeByte(dat);
    }
    gCS_SetHigh();                              // チップセレクト無効
    for(i=0;i<// 次開始位置を返す
}

//---- バイト単位で(縦)16ドットパターン(漢字など)の表示
/*	xpos: X位置、	ypos: Y位置
 *	*pKj: 漢字パターンデータポインタ、 cdt: 漢字横ドット数
 *---------------------------------------------------- */
uint8_t gLCD_nk16X(uint8_t xpos, uint8_t ypos, char *pKj, uint8_t cdt){
    gLCD_ptnX(xpos, ypos,(char *)pKj, cdt); 
    xpos = gLCD_ptnX(xpos, ypos+8, (char *)(pKj + cdt),cdt);
    return xpos;
}

//---- 半角(8x16)文字表示(次xpos位置を返す)-----
uint8_t gLCD_HchrX(uint8_t xpos, uint8_t ypos, uint8_t chCode){
    uint32_t adr;
    if((chCode >= 0x20) & (chCode < 0x7F)){
        adr = (chCode - 0x20) * 16 + HASC_OFS;
    }else{
        adr = H_BLANK;                          // 半角ブランク表示
    }
    SPI_Mem_NRead(adr,(uint8_t *)vBf,16);
    xpos = gLCD_nk16X(xpos, ypos, (char *)vBf, 8); 
    return xpos;
}

//-------- バイト単位で半角文字列表示(次xpos位置を返す)-----
uint8_t LCD_HstrX(uint8_t xpos, uint8_t ypos, char *str){
    while(*str)  xpos = gLCD_HchrX(xpos, ypos, *str++);
    return xpos;                                //次表示位置を返す
}

//--- 行をクリアして半角文字列表示(次xpos位置を返す)
uint8_t prtHstr_LnClr(uint8_t xpos, uint8_t ypos,char* str){
    gLCD_clrX(0,ypos  ,0,LCG_C_SIZE);
    #ifdef FONT_ROM
        gLCD_clrX(0,ypos+8,0,LCG_C_SIZE);
        return  gLCD_HstrX(xpos,ypos,str);
    #else
        return  gLCD_strX(xpos,ypos,str);
    #endif
}

/*--- JIS−>シフトJIS変換
uint16_t Jis2SJis(uint16_t jCd){
    uint8_t hi,lo;
    hi = jCd >> 8;
    lo = (uint8_t)jCd;
    if(hi && 1){
        if(lo < 0x60)   lo = lo + 0x1F;
        else            lo = lo + 0x20;
    }else{              lo = lo + 0x7E;
    }
    if(hi < 0x5F)       hi = (hi + 0xE1) >> 1;
    else                hi = (hi + 0x161) >> 1;
    return (hi * 256 + lo);
}
*/
//---- シフトJIS−>JIS変換
uint16_t SJis2Jis(uint16_t sCd){
    uint8_t hi,lo;
    hi = sCd >> 8;
    lo = (uint8_t)sCd;
    if(hi <= 0x9F){
        if(lo < 0x9F)   hi = hi * 2 - 0xE1;
        else            hi = hi * 2 - 0xE0;
    }else{
        if(lo < 0x9F)   hi = (hi - 0xB0) * 2 - 1;
        else            hi = (hi - 0xB0) * 2;
    }
    if(lo < 0x7F)       lo = lo - 0x1F;
    else{
        if(lo < 0x9F)   lo = lo - 0x20;
        else            lo = lo - 0x7E;
    }
    return (hi * 256 + lo);
}

//--- 全角(漢字)フォントアドレス取得
uint32_t KfntAdr(uint16_t kCode){
    uint8_t msb, lsb;
    uint32_t lmsb, llsb;
    msb = kCode >> 8;
    lsb = (uint8_t)kCode & 0x000FF;
    lmsb = (uint32_t)msb;
    llsb = (uint32_t)lsb;
    if(msb>=0x21 & msb<0x30){
        if(msb==0x21){                      // 記号1
            if(lsb>=0x21 & lsb<=0x7E) return 0x01000+(lsb-0x21)*32;
            else                      return 0;
        }else if(msb==0x22){                // 記号2
            if(lsb>=0x21 & lsb<=0x2E) return 0x01C00+(lsb-0x21)*32;
            else                      return 0;
        }else if(msb==0x23){                // ASCII
            if(lsb>=0x30 & lsb<=0x39) return 0x01E00+(lsb-0x30)*32;
            if(lsb>=0x41 & lsb<=0x5A) return 0x02000+(lsb-0x41)*32;
            if(lsb>=0x61 & lsb<=0x7A) return 0x02400+(lsb-0x61)*32;
            else                      return 0;
        }else if(msb==0x24){                // ひらかな
            if(lsb>=0x21 & lsb<=0x73) return 0x02800+(lsb-0x21)*32;
            else                      return 0;
        }else if(msb==0x25){                // カタカナ
            if(lsb>=0x21 & lsb<=0x76) return 0x03260+(lsb-0x21)*32;
            else                      return 0;
        }
    }else if(msb<0x50){                     // 漢字	
        if(msb!=0x4F){
            if(lsb>=0x21 & lsb<=0x7E) return 0x04000+(lmsb-0x30)*3008+(llsb-0x21)*32;
            else                      return 0;
        }else{
            if(lsb>=0x21 & lsb<=0x53) return 0x1AC40+(llsb-0x21)*32;
            else                      return 0;
        }
    }
    return 0;
}

//---- バイト位置単位で全角(漢字)文字表示
uint8_t gLCD_ZchrX(uint8_t xpos, uint8_t ypos, uint16_t kCd,uint8_t ed){
    uint32_t adr;
    uint16_t kk;
    uint8_t h,l;
    if(ed){
        h = (uint8_t)(kCd>>8);
        l = (uint8_t)kCd;
        kCd = l*256+h;
    }
    kk = SJis2Jis(kCd);
    adr = KfntAdr(kk);
//    printf(" %X %lX\r",kk,adr);
    SPI_Mem_NRead(adr,(uint8_t *)vBf,32);
    if(adr)	return gLCD_nk16X(xpos,ypos,(char *)vBf,16);
    return xpos;
}

//---- バイト単位位置で全角(漢字)文字列表示
uint8_t gLCD_ZstrX(uint8_t xpos, uint8_t ypos, char *str,uint8_t ed){
    uint16_t cd, *kjP;
    kjP = (uint16_t *)str;
    while(*kjP & 0xFF){
        xpos = gLCD_ZchrX(xpos,ypos,*kjP,ed);
        kjP++;
    }
    return xpos;
}		


/****** ドット単位位置で描画 ******/
//----------ドット単位で点を描く----------------------
void gLCD_PSet(uint8_t xpos, uint8_t ypos, uint8_t col){
    uint8_t yAdr, yBit;                         // yアドレス、ビット位置
    uint8_t ptn = 0x01;                         // ビットパターン
    uint8_t dat;
    if((xpos > 127) || (ypos > 47))  return;
    dat = vRam_read(xpos, ypos);
    yAdr = ypos >> 3; yBit = ypos & 0x07;
    ptn <<= yBit;
    switch(col){
        case 0:	dat &= (~ptn);	break;
        case 1: dat |= ptn;		break;
        case 8: dat ^= ptn;		break;
    }
    gLCD_posXY(xpos,ypos);  
    gCS_SetLow();                               // チップセレクト有効
    gRS_SetHigh();                              // 表示データ指定
    SPI1_ExchangeByte(dat);
    gCS_SetHigh();                              // チップセレクト無効
    vRam_write(xpos, ypos, dat);
}

//-------ドット単位で文字を描く(次xpos位置を返す)-------
uint8_t gLCD_PutChr(uint8_t xpos, uint8_t ypos, char ch, uint8_t col){
    uint8_t i, j, ptn, msk;                     //ビットマスクデータ
            //有効フォントデータでないなら何もしない
    if((ch < 0x20)||(ch > 0x7f)) return xpos;
    ch -= 0x20;                                 //配列アドレスを計算
    for(i = 0; i < 5; i++){
        msk = 0x01;
        ptn = (uint8_t)Font[ch][i];
        if(ptn == 0xFF) break;                  //幅狭文字なら抜ける
        for(j = 0; j < 8; j++){
            if(ptn & msk)    gLCD_PSet(xpos + i,ypos + j,col);
            msk <<= 1;
        }
    } 
    return xpos + i + 1;
}

//-------ドット単位で文字列を描く(次xpos位置を返す)---
uint8_t gLCD_PutStr(uint8_t xpos, uint8_t ypos, char * str, uint8_t col){
    while(*str)                                 //文字列終端まで継続
        xpos = gLCD_PutChr(xpos,ypos,*str++,col);
    return xpos;                                //次表示位置を返す
}

//-------ドット単位でパターンを描く(次xpos位置を返す)-------
uint8_t gLCD_PutPtn(uint8_t xpos, uint8_t ypos, char * ptn, uint8_t n, uint8_t col){
    uint8_t i, j, msk;                          //ビットマスクデータ
    for(i = 0; i < n; i++){
        msk = 0x01;
        for(j = 0; j < 8; j++){
            if(*ptn & msk)    gLCD_PSet(xpos+i,ypos+j,col);
            msk <<= 1;
        }
        ptn++;
    }
    return xpos + i;
}

// --- ABS、SWAP関数定義

void swap_uint8_t(uint8_t *a, uint8_t *b){
	uint8_t tmp; tmp = *a; *a = *b; *b = tmp;
}
#define ABS(a)    (((a)>0) ? (a) : -(a))

//----------ドット単位で直線を描く-------------------
void gLCD_Line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t col){
    uint8_t steep, x, y, tmp;
    int   ystep, deltax, deltay, error;
    steep = (ABS(y1 - y0) > ABS(x1 - x0));
    if(steep){    swap_uint8_t(&x0,&y0); swap_uint8_t(&x1,&y1); }
    if(x0 > x1){  swap_uint8_t(&x0,&x1); swap_uint8_t(&y0,&y1); }
    deltax = x1 - x0;                           //  傾き計算
    deltay = abs(y1 - y0);
    error = 0;
    y = y0;
    if(y0 < y1) ystep = 1; else ystep = -1;
    for(x = x0; x < x1 + 1; x++){
        if(steep) gLCD_PSet(y,x,col); 
        else      gLCD_PSet(x,y,col);
        error += deltay;
        if((error << 1) >= deltax){
            y += ystep;
            error -= deltax;
        }
    }
}

//---- ドット単位で(縦)16ドットパターンの表示(次xpos位置を返す)
/*	xpos: X位置、	ypos: Y位置,		col: 表示モード
 *	*pKj: 漢字パターンデータポインタ、	cdt: 漢字横ドット数
 *---------------------------------------------------- */
uint8_t Put_nK16(uint8_t xpos, uint8_t ypos, char *pKj, uint8_t cdt, uint8_t col){
 	gLCD_PutPtn(xpos, ypos, (char *)pKj, cdt, col); 
	xpos = gLCD_PutPtn(xpos, ypos+8, (char *)(pKj + cdt),cdt, col);
	return xpos;
}

//---- ドット単位で半角文字表示(次xpos位置を返す)
uint8_t Put_HchrX(uint8_t xpos, uint8_t ypos, uint8_t chCode,uint8_t col){
    uint32_t adr;
    if((chCode >= 0x20) & (chCode < 0x7F)){
        adr = (chCode - 0x20) * 16 + HASC_OFS;
    }else{
        adr = H_BLANK;                          // 半角ブランク
    }
    SPI_Mem_NRead(adr,(uint8_t *)vBf,16);
    xpos = Put_nK16(xpos, ypos, (char *)vBf, 8, col);       
    return xpos;
}

//---- ドット単位で半角文字列を表示(次xpos位置を返す)
uint8_t Put_HstrX(uint8_t xpos, uint8_t ypos, char *str, uint8_t col){
    while(*str)  xpos = Put_HchrX(xpos, ypos, *str++, col);
    return xpos;                                //次表示位置を返す
}

【EUSART】   EUSARTの設定 (abc900)
USBシリアル通信の設定
【PICライブラリ】 ライブラリの使い方 (abc840)
使用するライブラリの追加など
【MCCの設定】  MCC設定例 (abc895)
MCCでSPIを使う場合(一部のピン設定は変更すること)


★★★ おまけ(1) ★★★  (spi_Mem3およびAQM1248A_v4の変更詳細は、次のおまけ2のリストを参照)
 PIC16F18325でプログラムメモリ使用量が98.3%とほとんど無くなったが、(s)printf関数を使用しないで、自作の16進数変換関数 my_utoa(abc891を参照)を使うことで、メモリ使用量を79.7%に減らすことができた。


(s)printfを使った場合

my_utoaを使った場合

★★★ おまけ(2) ★★★  printfを使わない全リスト、abc905-18325npf(zip)(同名のファイルを置き換え)
 折角メモリに余裕ができたので、最大512個のユーザーフォントも使えるようにした。
 ユーザフォントの文字コードは00C00H番地から(リトルエンディアン)、ユーザーフォントデータは1C000H以降に格納しておく。
 最初に標準で登録した文字コードを探し、該当がなければ00C00H番地以降で一致する文字コード(2バイト)を検索し、一致するコードがあれば、その文字に対応するユーザーフォントデーターを1C000Hエリアから読み出し、表示。


ユーザーフォントの格納方法

ユーザーフォント表示例("L6775")

ユーザーフォント付きデモ例

 テストをしやすくするため、以下のようにコマンドを変更した。

Kコマンド K[H][aaaa] ・・・aaaaはフォントデータアドレス(16進数)
・K[H]のみであればページバッファー内の文字表示(変更無し)。
・アドレスを付けると、アドレスaaaaから一端フォントデータを読み出してから表示。
・ページバッファーサイズ(MBFSIZE)をmyProject.hで256バイトに変更し、表示文字数を増やした。
 (ROMによってはページ書込バイト数に制限がある場合があり、その場合はその最大バイト数にすること)
 
Lコマンド L[jCd] ・・・jCdはJISコード(16進数)
・Lのみであれば、前回表示した文字の次の文字を表示。
・JIS文字コード(jCd)を付けると、指定した文字を表示(半角/全角は自動判定)。

 なお、漢字を表示する gLCD_ZchrX()関数の エンディアン(ed)は以下のとおり

ed = 0: シフトJIS(リトルエンディアン)、JIS変換有り ・・・2バイト数値(uint16_t)
ed = 1: シフトJIS(ビッグエンディアン)、JIS変換有り ・・・全角文字列(char[])
ed = 2: JIS(リトルエンディアン)
ed = 3: JIS(ビッグエンディアン)


※プログラムのリストをハイライト無しのスタイルで見る場合はここをクリック


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


フォントデータのROM化(2) Highlight.js PICミニBB(15)_フォントROMで漢字文字列表示