電光掲示板とIOエキスパンダー

 

前回までで
 https://miha.jugem.cc/?eid=398
 日本語を自由に表示できるようになりました。

 

電光掲示板 ビデオ
 https://youtu.be/eZVUJIX5PmM

 

 

今回は電光掲示板に表示したいと考え、
 LT-5016M1
  https://akizukidenshi.com/catalog/g/g117699/
  写真

  
 と

  MCP23017
  https://akizukidenshi.com/catalog/g/g109486/
  写真

  
  
を仕入れて使ってみます。
 
電光掲示板について

 LT-5016M1 これ、結構厄介です。
 裏に接続用のピンが出ていますが、2ミリピッチで、
  しかも、一つおきに出ていますので、4ミリピッチとなります。
   そのままではブレッドボードに刺さりません。
 
 ブレッドボード用に接続するにはピンに直接はんだ付けして
  ケーブルを引き出す必要がありました。
  
 あと、説明書は他の製品のがくっついているだけで、参考レベルです。
  適当に当てをつけて3.3Vとか、を抵抗を入れてここをつなぐとここが光るぞ。
   みたいな解析が必要です。
 
 どのみち説明書付きでもそういう検証はするので、それほど手間ではないですが、
  とにかく、ケーブルが多い。
   16ドットx16ドットで、
    しかも、緑と赤が光るので、
   本体にくっついているケーブルは16x3で48本になります。
 
 はんだ付けが苦手な私には結構ハードル高かったです。
 
 写真。

 
 
 緑と赤とプラスと目印を付けました。
  プラスがアノード、マイナスがカソード
   プラスの1番に3.3Vを入れて、
    緑の1番にGNDをつなぐと 左上が光ります。
   プラスの1番に3.3Vを入れて、
    緑の2番にGNDをつなぐと 左上からひとつ下が光ります。
   プラスの2番に3.3Vを入れて、
    緑の2番にGNDをつなぐと 左上からひとつななめ下が光ります。
 後は判るね。赤の方も同じです。
 ちなみに、緑の電圧が5Vだと、オレンジに光ります。

 

MCP23017について
 I2Cの信号線で16個のGPIOを拡張可能
 電源は3.3V、5Vの両方対応可能
 GPIOひとつあたり、
  25mA最大しか取り出せない
 
MCP23017ピン配置
 
 GPA0~7 拡張するGPIO
 GPB0~7 拡張するGPIO
 VDD 3.3V
 VSS GND
 SCK ArduinoのSCKへ
 SDA ArduinoのSDAへ
 RESET 通常は3.3Vへ
 A0~2 I2Cアドレス指定
  A2 A1 A0 アドレス
  LO LO LO 0x20
  LO LO HI 0x21
  LO HI LO 0x22
  LO HI HI 0x23
  
  HI LO LO 0x24
  HI LO HI 0x25
  HI HI LO 0x26
  HI HI HI 0x27
 ※HI=3.3V LO=0V

 

ライブラリ
 ライブラリマネージャーで MCP23017 を検索
  Adafruit MCP23017 Arduino Library
   をインストール
 https://learn.adafruit.com/adafruit-mcp23017-i2c-gpio-expander/arduino

 

そんなには難しくない。
あとは漢字ROMから読み出して、
 ひたすらエキスパンダーにI2Cで書き出すんですけど
  普通にmcp.digitalWrite(7, HIGH);とかだと使い物にならない。
   いや、高速での出力に対応するにはスピードが足りなかったので、
    バイト単位で操作する必要があります。
     mcpY.writeGPIO(y_lo_bit ^ 0xff, 0);
    上はMCP23017のGPAのポートにy_lo_bitのバイトを反転して出力する例
    下記の回路上 mcpYはカソード(-)側になりますので、反転出力となります。

 

回路

 ケーブルが多いので大作になりましたね
 漢字ROM部分は
  https://miha.jugem.jp/?eid=395
   を参考にして下さい。

 

 

 

 

 
プログラム 抜粋

 全体は
   https://drive.google.com/file/d/1l256FM_AwMbmGRKxZXstq06MOOh83wOR/view?usp=sharing

 

 

void showOneLine(uint16_t xbit, uint16_t ybit)
{
  //
  xbit = swapBit(xbit);
  //
  uint16_t y_hi_bit = ybit/256;
  uint16_t y_lo_bit = ybit&0xff;
  //
  uint16_t x_hi_bit = xbit/256;
  uint16_t x_lo_bit = xbit&0xff;
  //
  mcpX.writeGPIO(0, 0);
  mcpX.writeGPIO(0, 1);
  mcpY.writeGPIO(y_lo_bit ^ 0xff, 0);
  mcpY.writeGPIO(y_hi_bit ^ 0xff, 1);
  mcpX.writeGPIO(x_lo_bit, 0);
  mcpX.writeGPIO(x_hi_bit, 1);
}

void showFONT(unsigned char *font_buf, int font_size, int width)
{
  uint32_t s = millis();
  for(int n=0;n<100;n++){
    uint16_t y=1;
    for(int i=0;i<font_size/2;i++){
      uint16_t bit = font_buf[i*2] *256 + font_buf[i*2+1];
      showOneLine(bit, y);
      y=y << 1;
    }
    uint32_t e = millis();
    if((e-s)>1000 || s>e){
      break;
    }
  }
}

arduinoで憧れのGUI表示(表示のみ)を試してみた。

 

arduinoを今風のPCみたいに
 GUI表示させたい。と思い、
  今までいろいろな記事を書いてきました。

 

FONTX2形式をESP32のmicropythonで扱う
 https://miha.jugem.cc/?eid=367
arduinoで漢字を扱う準備
 https://miha.jugem.jp/?eid=396
SJIS変換テーブルの記事
 https://miha.jugem.cc/?eid=397
SPI接続のカラーLCDの記事(ILI9328)
 https://miha.jugem.cc/?eid=111
arduinoに512Kバイトの漢字ROM等を増設する方法
 https://miha.jugem.jp/?eid=395

 

これらをつなぎ合わせてGUI風に表示するプログラムを
 作ってみました

 

結果的には昔のテキスト版のアドベンチャーゲームみたいな
 雰囲気になってしまいました。
 
 https://youtu.be/XJSQ4KoNolw


 
これが以下のような簡単なプログラムで表示できるようになります。

 

  DispColor(MakeColorGray(3));
  //テキスト表示
  showTftUTF8(0, 0, "吾輩は猫である", COLOR_BLUE);
  showTftUTF8(0, 1, "吾輩は猫でなし", COLOR_RED,SHOW_DOUBLE);
  showTftUTF8(0, 6, "1234567890ABCabcアイウ!#$♡", COLOR_WHITE);
  
  //ベースの箱
  //     (x  y    幅    高   色) 左下が起点
  BoxFill(0, 120, 240, 200, MakeColorGray(16));

 

  //警告ダイアログ
  BoxFill(20, 148, 212, 160, MakeColorGray(24));
  BoxFill(20, 288, 212, 20, COLOR_BLUE);
  showTftUTF8(3, 18, "警告", COLOR_RED,SHOW_NORMAL);
  showTftUTF8(3, 15, "吾輩は猫である。", COLOR_RED,SHOW_NORMAL);
  showTftUTF8(3, 14, " あなたにも、この後", COLOR_RED,SHOW_NORMAL);
  showTftUTF8(3, 13, "  猫になってもらいます", COLOR_RED,SHOW_NORMAL);

 

  //ボタン
  Button(9, 11, "了承", true);
  Button(19, 11, "断る", false);

 

以下は手順

 

arduinoIDEはUTF8でプログラムを記述する関係で
 SJISに変換する必要があります。
  https://miha.jugem.cc/?eid=397
   を参考にして変換テーブルを準備します。

 

用意するもの全角用と半角用のFONTを準備
 https://miha.jugem.jp/?eid=396
  を参考にして上記の変換テーブルと一緒に
   SDカードに記録しておきます。
   
   この時点でのSDカードの中身
   
   Convert.dat
   ILGH16XB.FNT
   ILGZ16XF.FNT

 

 ※こちらのフォントはIPAが作成したものを
  こばこのひみつ
   さんでFONTX形式にしたものです。
    http://ayati.cocolog-nifty.com/blog/2012/08/ipalx322416-64a.html
 IPAのページ
  https://moji.or.jp/ipafont/ipafontdownload/
 FONTX形式フォントのダウンロード
  http://www.ayati.com/LX/ILFONT03.zip

 

SDから読み込んでROM化する。

 パソコンからEEPROMへ直接書き込めないので、
  Arduinoで専用プログラムを書いて実行します。
 
 EEPROMは512Kバイト 0x00000~0x70000の連続領域
 
 メモリマップ
  0x00000-4ffff ILGZ16XF.FNT
  0x50000-5ffff ILGH16XB.FNT
  0x60000-6ffff CONVERT.DAT

 

 SDカードの接続
  MOSI - D11
  MISO - D12
  CLK  - D13
  CS   - D4
  
 EEPROM(25FC1025)接続
  参照
   https://miha.jugem.cc/?eid=395
   https://img-cdn.jg.jugem.jp/85e/51887/20241122_3600138.png
 
 ROM化プログラム(_99.writeKanjiROM-R1.ino)
  
  要点のみ抜粋(WriteFC1025についてはhttps://miha.jugem.cc/?eid=395参照)
  FileToMemoryを3回呼び出してEEPROMへ書き込みます。
  
   
   FileToMemory("CONVERT.DAT", 0x60000);
   FileToMemory("ILGZ16XF.FNT", 0x00000);
   FileToMemory("ILGH16XB.FNT", 0x50000);
   
   int FileToMemory(char *szFileName, uint32_t address)
   {
    File f = SD.open(szFileName, FILE_READ);
    if (!f) {
     Serial.print(szFileName);Serial.println(" error SD.open");
     return -1;
    }

    while(1){
     if(!f.available()){
      break;
     }
     char buf[32];
     uint32_t n = f.read(buf, 32);
     //Serial.print("read-byte=");Serial.println(n);
     
     int nRc = WriteFC1025(address, buf, n); //メモリ書き込み
     if(nRc!=0){
      Serial.println("error WriteFC1025");
     }
     Serial.print("address=");Serial.println(address, HEX);
     address = address + n;
    }
    f.close();
    return 0;
   }

 

ROMから文字関連データを読み込む様に改造する。(_99.TFT-SPI-KANJI-R2.ino)
 過去記事https://miha.jugem.cc/?eid=396
 つまり、上記過去記事はSDからFONTや変換テーブルの読み取りを
 していますので、
  fseekとreadの所を中心に書き換えればOKです。
  読み込み関数はReadFC1025で過去記事https://miha.jugem.jp/?eid=395
  で作成したものです。
  メモリなのでopenとcloseは不要になりますからメインメモリの節約にもなります。

 

LCDに表示する。(_99.TFT-SPI-KANJI-R2.ino)
 過去記事https://miha.jugem.cc/?eid=111
 を参考にしてROMから取得してきたビット情報を
  drawPixel関数でLCDに表示してやるのみです。
  
 抜粋 drawPixel呼ぶ所

  void showTftFONT(int col, int row, char *font_buf, int font_size, int width, int color, int bairitu=SHOW_NORMAL)
  {
   int x=0;
   int y=0;
   for(int i=0;i<font_size;i++){
    uint16_t bit = font_buf[i];
    for(int b=0;b<8;b++){
     if(bit & 128){
      drawPixel(col*8+x, row*16+16-y, color);
      x++;
     }
     else{
      x++;
     }
     bit = bit << 1;
    }
    if ((i+1) % int((width + 7) / 8) == 0){
     x=0;
     y++;
    }
   }
  }

まあまあ、遅いですね。それがまたいい雰囲気出してます。
 LCDの表示がおそらくはもっといい方法が有ると思うのですが、
  2016年当時アイテンドーさんから入手してきたものを
   結構な無理矢理感あるプログラムで動かしていますからね。
 今やるならILI9341で、adafruitの専用ライブラリ使えれば
  だいぶ早くなるんでしょうけど。今回は手持ちパーツという事で
   次の機会に取っておきます。

 

今回の関連プログラム置き場
 https://drive.google.com/file/d/1m759pW3nJRycYfg2ADz_a_mMqz396NeQ/view?usp=sharing

arduino専用UTF8→SJISの変換テーブルの作成
 
迷走している可能性もありますがUPします。

前の記事
 https://miha.jugem.cc/?eid=396

前回は漢字FONTをSDカードに入れてうまいこと読み出してみました。
FONTXというDOS/V時代のフォント形式を利用すれば
 arduinoの様なマシンでも扱うことが出来そうです。
  FONTXの文字パターンはSJISコード順で並んでおります。
 
しかし、arduinoIDEはソースコードをUTF8で記述する関係で
 そのままプログラムから文字列を指定しても
  文字化けするので、SJISに変換してから使う必要があります。
 
さてさて、文字コードうんちく含めて書きますね。
 
日本で生み出した文字コードはJIS規格に準拠していて、
 みんな並び方が同じだったんです。
  使われるコードの範囲が異なる程度しか違わなかったんです。
   つまり、古(いにしえ)の文字コード(JIS、SJIS、EUCたち)は
    簡単な計算によって相互に変換する事が出来たんですよ。
 
しかしですね。時代が進んで
 国ごとにパソコンの中の文字コード体系が異なると不便なわけで。
  文字化けしちゃうますからね。読めないわけです。
 
世界中の文字を共通の文字コード体系で表現するために
 新しく作り直したのがUNICODE(UNICODE/UTF7/UTF8/UTF16)なんです。
  こうすれば、アラビア語もロシア語も送った人の意図通りに
   表示されるはずです。読めるかどうかはその人次第です。
 
その際に日本の文字はほぼ全て取り込まれたんですが、
 文字コードが異なり、順番も無視されてしまいました。
  その為、プログラムロジックでの変換は難しくなり、
   結果、変換テーブルが必要になったわけです。
 
せめてもの共通点としては数字とアルファベットは同じなんです。
 コンピューターが生まれた英語圏で開発されたASCIIコードがUNICODEの
  先頭にそのまま取り込まれています。
 
現在はUNICODEのおかげで、
 世界中の文字が同じ様にパソコンや携帯で使えるようになりました。
 
しかし、arduinoでもプログラムに文字を記述したら
 そのままディスプレーに表示させたい自分としては、
  UTF8から変換できる文字コード表がないかとあっちこっち探したんですが、
   みつからず。
    ※SJIS変換後ファイルを別に持って表示させるのは方式的には有りですけどね。
   
 参考
  http://charset.7jp.net/sjis.html
  https://www.seiai.ed.jp/sys/text/java/utf8table.html
  https://www.ibm.com/docs/ja/aix/7.1?topic=8-utf-ucs-transformation-format
 
 どれも、完全な文字コード表になっていないので、変換テーブルを作れません。
 
という理由で、
 おそらく現代において一番正確なのはWindowsだよね。と考え、
  上記の情報をもとに、
   Windowsで総当たりでUTF8からSJISに変換をかけてみました。
    そしてうまく変換が成功したものだけを
     抜き出して変換コード表を作成しました。
 
プログラム
 
  CFile f;
  int rc = f.Open(L"Convert.dat", CFile::modeCreate | CFile::modeWrite);

  char bufUTF8[10];
  char bufSJIS[10];

  //UTF8の2バイト系
  for (int i = 0xC0; i <= 0xDF; i++) {
    for (int j = 0x00; j <= 0xFF; j++) {
      memset(bufUTF8, 0, sizeof(bufUTF8));
      memset(bufSJIS, 0, sizeof(bufSJIS));
      bufUTF8[0] = i;
      bufUTF8[1] = j;
      bufUTF8[2] = 0x00;
      bufUTF8[3] = 0x00;
      int rc = UTF8toSJIS(bufUTF8, bufSJIS);
      if (bufSJIS[0] != 0x3f) {
        TRACE("UTF8=%x-%x-%x-%x,SJIS=%x-%x-%x¥n",
          (unsigned char)bufUTF8[0], (unsigned char)bufUTF8[1], (unsigned char)bufUTF8[2], (unsigned char)bufUTF8[3],
          (unsigned char)bufSJIS[0], (unsigned char)bufSJIS[1], (unsigned char)bufSJIS[2]);
        f.Write(bufUTF8, 4);
        f.Write(bufSJIS, 2);
      }
    }
  }

  //UTF8の3バイト系
  for (int i = 0xE0; i <= 0xFF; i++) {
    for (int j = 0x00; j <= 0xFF; j++) {
      for (int k = 0x00; k <= 0xFF; k++) {
        memset(bufUTF8, 0, sizeof(bufUTF8));
        memset(bufSJIS, 0, sizeof(bufSJIS));
        bufUTF8[0] = i;
        bufUTF8[1] = j;
        bufUTF8[2] = k;
        bufUTF8[3] = 0x00;
        int rc = UTF8toSJIS(bufUTF8, bufSJIS);
        if (bufSJIS[0] != 0x3f) {
          TRACE("UTF8=%x-%x-%x-%x,SJIS=%x-%x-%x¥n",
            (unsigned char)bufUTF8[0], (unsigned char)bufUTF8[1], (unsigned char)bufUTF8[2], (unsigned char)bufUTF8[3],
            (unsigned char)bufSJIS[0], (unsigned char)bufSJIS[1], (unsigned char)bufSJIS[2]);
          f.Write(bufUTF8, 4);
          f.Write(bufSJIS, 2);
        }
      }
    }
  }
  f.Close();
}
int UTF8toSJIS(char* bufUTF8, char* bufSJIS)
{
    wchar_t bufUnicode[10];
    memset(bufUnicode, 0, sizeof(bufUnicode));

    int rc = MultiByteToWideChar(CP_UTF8, 0, bufUTF8, 9, bufUnicode, 9);
    if (rc == 0) {
        //TRACE("error MultiByteToWideChar¥n");
    }
    //TRACE("UNICODE=%x¥n", bufUnicode[0]);

    rc = WideCharToMultiByte(932, 0, bufUnicode, 9, bufSJIS, 9, NULL, NULL);
    if (rc == 0) {
        //TRACE("error WideCharToMultiByte¥n");
    }
    //TRACE("SJIS=%x-%x¥n", bufSJIS[0], bufSJIS[1]);

    return rc;
}

出来上がった変換テーブル
 
 https://drive.google.com/file/d/1rwbbrQ8xku0CfF8fxIO-Y8Aculgfkx3-/view?usp=sharing
 
変換テーブルを簡単に説明すると・・・
 
 一文字あたり6バイトで構成されています。
 
 最初の4バイトはUTF8の文字コード
  後ろの2バイトはSJISの文字コードです。
 バイナリエディタで開いた画面
 
  
  例えば全角カタカナの「ン」はUTF8でE383B3(16進)というコードです。
  画面の反転している所です。
  
  その後ろに00が続きますが。これは予備です。
  さらにその後にSJISの文字コード8393(16進)が続きます。
 
 つまり、UTF8からSJISに変換したい場合は先頭から3バイトが一致する行をを探して
  後ろの2バイトを取得してきます。

 

最後の文字コードがefbfa5でした。
 3バイトのUTF8文字コードで収まったのかと一瞬思ったんですが、
  4バイトを作ってテストしていないのでわからないですね。
   しかし、9359文字あることになっていますので、
    SJISはそんな数無いので
     Windowsの都合で拡張されているんですね。
      多分充分でしょう。
    arduinoで不要な文字は外せばいいので
     変換テーブルを小さくするのは
      この次の話ですね。

出来上がったんですけど。
 SJISとUTF8ではイメージが若干異なるので注意も必要です。
 
 

 


 

 


 

 


 

 


 
 
 なんかね、似て非なるものって感じですが、
  ひとつひとつ全部見たわけではないですけど、
   その他の部分は一致している模様。
    普通に使わない部分ですし良いですよね。

調べていたら見つけたもの

 久々にWindowsでプログラムしていたんですが、
  ソースプログラム中に文字列を埋め込む場合のやりかたで
   有用な書き方がありました。
 UNICODE文字列をプログラムに埋め込む場合は
  L"文字列"
   という感じで先頭にLと書いてダブルコーテーションで
    くくれば良いのは知っていたんですが、
 他にも
  UTF8 u8"文字列"
  UTF16 u"文字列"
  UTF32 U"文字列"
   が使えます。
   
 もしかしたらVC++固有の書き方かもしれません。
  試してないのですみません。
 
 未加工リテラルというのもあるようですが、
  こちらも試してないのですみません。
   こっちを参照願います。
    https://learn.microsoft.com/ja-jp/cpp/cpp/string-and-character-literals-cpp?view=msvc-170

というわけで、
 変換テーブルの作成まで。

最後に完成版の文字コード表のせておきます。

 

・・・と思ったんですが、やっぱり切られますね。

  2700行ぐらいしか書けないっぽいですね。

 

  全体は

   https://drive.google.com/file/d/1dMNG8bcM58w7uHRdBbqsP2R1HGAU9K2z/view?usp=sharing

 

抜粋です。

UTF8,SJIS,イメージ
e4b880,88ea,一
e4b881,929a,丁
e4b883,8eb5,七
e4b887,969c,万
e4b888,8fe4,丈
e4b889,8e4f,三
e4b88a,8fe3,上
e4b88b,89ba,下
e4b88d,9573,不
e4b88e,975e,与
e4b890,98a0,丐
e4b891,894e,丑
e4b894,8a8e,且
e4b895,98a1,丕
e4b896,90a2,世
e4b897,99c0,丗
e4b898,8b75,丘
e4b899,95b8,丙
e4b89e,8fe5,丞

arduinoで漢字を扱う準備
 

 

  
I2CのROMを前回やってみましたけど、
 前の記事
  https://miha.jugem.cc/?eid=395
 
SDカードでも実は性能変わらないのでは?と思い、
 SDカードに漢字のFONTを書きこんで読み込むテストをしました。
 
SDカードもシリコンディスクで24FC1025も中身は半導体
同じぐらいの性能出るかもみたいな。
 SDの方が壊れやすいイメージありつつ、
  大容量化が容易です。
 
Class10の32GBの製品を使っています。
 https://www.amazon.co.jp/dp/B08PTNWQ6P
 
SDカード版での処理時間は1文字あたり11.8ミリ秒でした。
1秒で84文字ぐらいSDカードからメモリに展開可能です。
ちなみに、試験機は外部クロックの8Mhzです。
 
FONTXの特性上、全角文字はデータの場所を先頭から検索していますけど
 バイナリサーチにすると少しスピードが上がるかもです。
ただ、下に書いた理由もあり、
 今の所はそこまで性能求めてないのでこれで良いかなーって思います。
 
半角文字は先頭から検索でなくインデックス指定で直接的にデータ持ってきてますが
 スピードは同じ11.8ミリ秒でしたので、そんなもんなのかもしれないです。
  まぁまぁ早いですしね。
 
と言いつつも、現在は一文字一文字SDのファイルをOpen/Closeを繰り返しておりまして
 そのせいで遅いかもですね。
 今openだけの時間を測ってみると7.79msでした。
 結構食いますね。
 元々はopenしっぱなしで作ってたんですが、
  下に書いた複数開きっぱなしにすると不安定な事件が発生して
   止めています。
 
なんか、2つまでなら同時にファイル開けるんですけどね、みっつめはエラーになるんですよね。
 それならと、open/closeしながら使えば大丈夫と思いきや、それもふたつめ以降はなんか
  不安定。なんか変と思い、常にオープンする数をひとつになるように改造して安定しました。
   へぼプログラムでどっかメモリ壊しているような症状ですね。
 
ちなみに、FONTXのデータがSJISなんですね。
 プログラムに文字列を直接書いた場合は文字化けします。
  arduinoIDEの文字コードがutf8だからです。
 
arduinoIDE経由でない場合はSJISで書けば問題ないと思いますけど
 こういう超小型マイコンではUTF-8からSJIS変換はかなり難しいので、
  なんかいい方法無いんでしょうかね?
  
面倒くさいですが
 文字列を保存したファイルをSJIS形式で用意してSDから読み取る方式にしています。
 
実行結果
 
start FontFileRead R02
・・・・・・・・
・・・・・・・・
・・・■■・・・
・・■■■・・・
・・・・■・・・
・・・・■・・・
・・・・■・・・
・・・・■・・・
・・・・■・・・
・・・・■・・・
・・・・■・・・
・・・・■・・・
・・・・・・・・

・・・・・・・・
・・・・・・・・
・・・・・・・・
・・・・・・・・
・・・■■・・・
・・・■■・・・
・・■■■■・・
・・■・・■・・
・・■・・■・・
・・■・・■・・
・■■■■■■・
・■・・・・■・
・■・・・・■・
■■・・・・■■
・・・・・・・・
・・・・・・・・

・・・・・・・・
・・・・・・・・
・・・■・・・・
・・・■・・・・
・・・■■■■・
・・■・・・■・
・・■・・・■・
・■■・・■■・
・■・・・■・・
・・・・・■・・
・・・・■・・・
・・・■■・・・
・・■■・・・・
・・■・・・・・
・・・・・・・・
・・・・・・・・
・・・・・・・・
・・■・・・・・
・・■・・・・・
・・■・・・・・
・・■■■■■・
・・■・■・・・
・■・・■・・・
・■・・■・・・
・・・・■・・・
・・・・■・・・
・・・■■・・・
・・・■・・・・
・・■■・・・・
・・・・・・・・
・・・・・・・・
・・・・・・・・
・・・・・・・・

・・・・・・・・
・・・・・・・・
・・・・・・・・
・・・・・・・・
・・・・・・・・
・・・・・■・・
・・・・■■・・
・・・・■・・・
・・・■■・・・
・・・■・・・・
・・・■・・・・
・・・■・・・・
・・・■・・・・
・・・■・・・・
・・・■■・・・
・・・・■・・・
・・・・■■・・
・・・・・■・・
・・・・・・・・
・・・・・・・・
・・・・・・・・

・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・・・・・■■■・・・・・・
・・・・・■■■■■・・・・・・
・・・・・・・・■■・・・・・・
・・・・・・・・■■・・・・・・
・・・・・・・・■■・・・・・・
・・・・・・・・■■・・・・・・
・・・・・・・・■■・・・・・・
・・・・・・・・■■・・・・・・
・・・・・・・・■■・・・・・・
・・・・・・・・■■・・・・・・
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・

・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・・・・・■■■・・・・・・
・・・・・・■■■■■・・・・・
・・・・・・■■・■■・・・・・
・・・・・・■■・■■・・・・・
・・・・・■■■・■■■・・・・
・・・・・■■・・・■■・・・・
・・・・■■■■■■■■■・・・
・・・・■■・・・・・■■・・・
・・・■■■・・・・・■■■・・
・・・■■■・・・・・・■■・・
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・

・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・■■■■■■■■■■■■■・
・・・・・・・・・・・・■■■・
・・・・・・・■■・・■■■・・
・・・・・・・■■・■■■・・・
・・・・・・・■■■■■・・・・
・・・・・・・■■・・・・・・・
・・・・・・■■■・・・・・・・
・・・・・・■■・・・・・・・・
・・・・・■■■・・・・・・・・
・・・・■■■・・・・・・・・・
・・・・■■・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・

・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・■■・・
・・■■■■■・・・・■■・・・
・■■■・■■■・・■■・・・・
・■■・・・■■・■■■・・・・
・■■・・・■■・■■・・・・・
・■■■・■■■■■・・・・・・
・・■■■■・■■・■■■■■・
・・・・・・・■■■■■・■■■
・・・・・・■■・■■・・・■■
・・・・・■■■・■■・・・■■
・・・・・■■・・■■■・■■■
・・・・■■・・・・■■■■■・
・・・■■・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・

・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・■■・・・・・・・・・・・
・・・■■・・・・■■・■■・・
・・・■■・・・■■■・■■・・
・■■■■■■・■■・・■■■・
・・・■■・・■■■・・・■■・
・・■■■・・■■・・・・・■■
・・■■■■■■■・■■・・・・
・・■■■■■■・■■■・・・・
・■・■■・■■・■■・■■・・
・■・■■・・・・■■・・■■・
・・・■■・・・■■・・・■■■
・・・■■・■■■■■■■・■■
・・・■■・・・・・・・・・■■
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・■■・・・・・■■・・・・
・・・■■・・・・・■■・・・・
・・■■■・・・・■■■・・・・
・・■■■■■■・■■■■■■■
・■■■■■・・■■■・■■・・
・■■・■■・■■■・・■■・・
・・・・■■・■■・・・■■・・
・・・・■■・・・・・・■■・・
・・・・■■・・・・・・■■・・
・・・・■■・・・・・・■■・・
・・・・■■・・・・・・■■・・
・・・・■■・・・・・・■■・・
・・・・■■・・・■■■■■・・
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
・・・■■・・・■■・・・・・・
・・・■■・・■■■■■■■■■
・・・■■・・■■・・・・・・・
・■■■■■■■■■■■■■■・
・・・■■・■■■・■■・■■・
・・■■■・・■■・■■・■■・
・・■■■■・■■・■■・■■・
・・■■■■■■■■■■■■■■
・■・■■■・■■・■■・■■・
・■・■■・・■■・■■・■■・
・・・■■・・■■■■■■■■■
・・・■■・■■・・・・・■■・
・・・■■・・・・・■■■■■・
・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・
 略


プログラム

/*
 * 接続
 * MOSI - D11
 * MISO - D12
 * CLK  - D13
 * CS   - D4
 */

#include <SPI.h>
#include <SD.h>

#define FONT_FILE_Z "ILGZ16XF.FNT"
#define FONT_FILE_H "ILGH16XB.FNT"

struct S_FONT{
  char file_name[20+1];
  char fontx[6+1];
  char f_name[8+1];
  char f_width;
  char f_height;
  char f_code;
  char f_block_num;
  uint16_t *p_block_start;
  uint16_t *p_block_end;
};
struct S_FONT font_Z;
struct S_FONT font_H;

int CreateFont(char *szFileName, struct S_FONT *font)
{
  strcpy(font->file_name, szFileName);
  File f = SD.open(szFileName, FILE_READ);
  if (!f) {
    Serial.print(szFileName);Serial.println(" error SD.open");
    return -1;
  }

  memset(font->fontx, 0, 6+1);
  memset(font->f_name, 0, 8+1);
  //
  f.read(font->fontx, 6);
  if(memcmp(font->fontx, "FONTX2", 6)!=0){
    Serial.println("error not font file");
    return -1;
  }
  //
  f.read(font->f_name, 8);
  f.read(&font->f_width, 1);
  f.read(&font->f_height, 1);
  f.read(&font->f_code, 1);
  //
  //Serial.print("fontx=");Serial.println(font->fontx);
  //Serial.print("f_name=");Serial.println(font->f_name);
  //Serial.print("f_width=");Serial.println((int)font->f_width);
  //Serial.print("f_height=");Serial.println((int)font->f_height);
  //Serial.print("f_code=");Serial.println((int)font->f_code);
  //
  if(font->f_code==0x00){
    //Serial.println("ANSI");
  }
  if(font->f_code==0x01){
    //Serial.println("SJIS");
    f.read(&font->f_block_num, 1);
    //Serial.print("f_block_num=");Serial.println((int)font->f_block_num);
    font->p_block_start = (uint16_t *)malloc(((int)font->f_block_num)*2);
    font->p_block_end   = (uint16_t *)malloc(((int)font->f_block_num)*2);
    unsigned char f_start[2];
    unsigned char f_end[2];
    for(int i=0;i<(int)font->f_block_num;i++){
      f.read(f_start, 2);
      f.read(f_end, 2);
      uint16_t bs = (uint16_t)f_start[1]*256+(uint16_t)f_start[0];
      uint16_t be = (uint16_t)f_end[1]*256  +(uint16_t)f_end[0];
      font->p_block_start[i]=bs;
      font->p_block_end[i]=be;
    }
    //for(int i=0;i<(int)f_block_num;i++){
    //  Serial.print(font.p_block_start[i], HEX);Serial.print("-");Serial.println(font.p_block_end[i], HEX);
    //}
  }
  f.close();
  return 0;
}

int GetFont(uint16_t code, struct S_FONT *font, char *font_buf, int *font_size, int *width)
{
  unsigned long time = millis();
  
  File f = SD.open(font->file_name, FILE_READ);
  if (!f) {
    Serial.print(font->file_name);Serial.println(" error SD.open");
    return -1;
  }
  {
    unsigned long now_time = millis();
    Serial.print("SD.open = ");Serial.print(now_time-time);Serial.println("ms");
  }

  uint16_t char_count=0;
  if(font->f_code==0x01){
    int exist_flag = 0;
    int i;
    for(i=0;i<(int)font->f_block_num;i++){
      if(font->p_block_start[i] <= code && code <= font->p_block_end[i]){
        char_count = char_count + (code - font->p_block_start[i]);
        exist_flag = 1;
        break;
      }
      else{
        uint16_t add = (font->p_block_end[i] - font->p_block_start[i]) + 1;
        char_count = char_count + add;
        //char_count = char_count + (p_block_end[i] - p_block_start[i]) + 1;
      }
    }
    if(exist_flag == 0){
        Serial.println("no exist");
        return -1;
    }
    if(exist_flag == 1){
        //Serial.print("exist:");Serial.println(i);
    }
  }
  *font_size = int(((int)font->f_width + 7) / 8) * (int)font->f_height;
  *width = font->f_width;
  
  uint32_t seek_point;
  if(font->f_code==0x01){ //SJIS
    seek_point = 18 + 4 * (uint32_t)font->f_block_num + (uint32_t)char_count * (uint32_t)(*font_size);
  }
  else{ //ANSI
    seek_point = 17 + code * (*font_size);
  }
  //Serial.print("font_size=");Serial.println(*font_size);
  //Serial.print("seek_point=");Serial.println(seek_point);
  
  f.seek(seek_point);

  f.read(font_buf, *font_size);

  f.close();
  {
    unsigned long now_time = millis();
    Serial.print("GetFont = ");Serial.print(now_time-time);Serial.println("ms");
  }
  
  return 0;
}

void show(char *font_buf, int font_size, int width)
{
  char str[100];
  memset(str, 0, sizeof(str));
  for(int i=0;i<font_size;i++){
    uint16_t bit = font_buf[i];
    for(int b=0;b<8;b++){
      if(bit & 128){
        strcat(str, "■");
      }
      else{
        strcat(str, "・");
      }
      bit = bit << 1;
    }
    if ((i+1) % int((width + 7) / 8) == 0){
        Serial.println(str);
        memset(str, 0, sizeof(str));
    }
  }

}

void CreateFont()
{
  CreateFont(FONT_FILE_Z, &font_Z);
  CreateFont(FONT_FILE_H, &font_H);
}

void show(unsigned char *szBuf)
{
  uint16_t code;
  char font_buf[2*16];
  int font_size;
  int width;

  int i=0;
  while(1){
    if(szBuf[i]==0){
      break;
    }
    if(0x81 <= szBuf[i] && szBuf[i] <= 0x9F 
    || 0xE0 <= szBuf[i] && szBuf[i] <= 0xFC ){
      code = szBuf[i] * 256 + szBuf[i+1];
      GetFont(code, &font_Z, font_buf, &font_size, &width);
      show(font_buf, font_size, width);
      i=i+2;
    }
    else{
      code = szBuf[i];
      //Serial.print("code=");Serial.println((int)code, HEX);
      GetFont(code, &font_H, font_buf, &font_size, &width);
      //Serial.print("font_size=");Serial.println((int)font_size);
      //Serial.print("width=");Serial.println((int)width);
      show(font_buf, font_size, width);
      i++;
    }
  }
}


void setup()
{
  Serial.begin(115200);
  Serial.println("start FontFileRead R02");
  
  if (!SD.begin(4)) {
    Serial.println("error SD.begin");
    return;
  }

  CreateFont();

  File fM = SD.open("MESSAGE.TXT", FILE_READ);
  if (!fM) {
    Serial.println("error SD.open MESSAGE.TXT");
    return -1;
  }
  
  char szMessage[256];
  memset(szMessage, 0, sizeof(szMessage));
  fM.read(szMessage, sizeof(szMessage)-1);
  fM.close();
  show(szMessage);
  
  //unsigned char szBuf[100]={0x82, 0xA0, 0x61, 0x82, 0xA2, 0x69, 0x82, 0x4F, 0x30, 0x00};
  //show(szBuf);
  //
  //show((unsigned char *)"12345");
  //show((unsigned char *)"ABCDEFG");
  //show((unsigned char *)"ABCDEFG"); //NG文字化け
}

void loop()
{
  // nothing happens after setup
}

arduinoでメモリをいっぱい使いたいので512Kバイト増設してみた。
 
メモリと言えば、PC8801だと、RAM64Kバイト、N88BASICのROMが40Kバイト
 漢字ROMはmk2で標準搭載とかでした。CPUはZ80互換のNEC製ですね。
 
16bitのPC9801でRAMが128Kバイト、F以降は漢字ROMが標準装備でした。
 
MZ80はRAMが48Kバイト、クリーンゆえROMがあんまり無かったですね。
 
異色のモトローラ系のCPUを搭載したFM7、CPUは6809、ROM44Kバイト,RAM64Kバイトでした。
 
各メーカー独自設計でいろいろで楽しかったですね。
 
その後MSXの規格統一の波があったり。
 
90年代の
 DOS/Vの上陸とWindowsの流れで黒船に国内のいろいろがやられたイメージです。
  一太郎とか花子とかワープロやお絵かきソフト懐かしいですね。
 
メモリに戻ると、
その当時はメモリ増設するだけで何万円もかかったりとメモリ高かったですよねー。
 今ではすっごい安くなりました。今回のは128Kバイトで300円とかですよ。
 
さて、我々の、arduino=ATMEGA328ですが、
 ATMEGAで最大クラスのメモリを搭載していますが、
  32Kバイトのプログラムエリア(=ROM)と1Kバイトのデータ用ROM、2KバイトのRAMがあります。
  さすがにこれだけだと、いろいろ不便ですよね。
   今回は実験ですけど、漢字ROMとして使いたいと考えましたので、
    300Kバイトは欲しいと考えました。
     漢字フォント、文字コード変換用テーブルとかいろいろ要りますよね。

写真
 
 
 
 ケーブルのトンネルですね。
 


こちらI2CのEEPROMになります。
 https://akizukidenshi.com/catalog/g/g103570/

書き換え回数に制限があり、
 100万回ライト/イレース可能 となっています。
  ・・・ 実質無限か?とも思えますけど。
   今回テストするだけでも100回ぐらい書きましたけど。
  実運用上は1回書いたら読み込むだけなんで問題なしです。
 
24FC1025ピン配置
 
 
  
 A0 : I2Cアドレス指定
 A1 : I2Cアドレス指定
 A2 : ON推奨 LOWだと、半分無駄になる
 WP : WriteProtect=ライトプロテクト LOWだと書き込み可能 HIGHは書き込み禁止
 SCL: arduinoのSCLへ
 SDA: arduinoのSDAへ
 
 A0,A1,A2 I2Cアドレス
 LO,LO,HI 0x50, 0x54,
 HI,LO,HI 0x51, 0x55,
 LO,HI,HI 0x52, 0x56,
 HI,HI,HI 0x53, 0x57 
 LO=GND,HI=VCC
 
 非推奨ですが、何のために有るのかわかりません。
  A2をLOにすると、0x54,0x55,0x56,0x57にアクセスできなくなります。
 
回路図
 
 
 
ポイントは上記ピン配置でも触れましたけど
 A0、A1、A2の接続でそれぞれのICのI2Cアドレスが変化する点です。

 

ICのメモリブロック一つで64Kバイトになります。
 今回のICひとつあたり、2ブロック持っていて合計128Kバイトです。
 IC4個で8ブロック、512Kバイトになります。
 I2Cアドレスを考慮しながらプログラムするのは
 やり難くてしょうがないので
  I2Cアドレスを気にしないでメモリにアクセスできるようにすることを目標に
   メモリ読み書き用の関数を作成しました。
  
    int WriteFC1025(uint32_t address, uint8_t *data, uint32_t len);
    uint32_t ReadFC1025(uint32_t address, uint8_t *data, uint32_t len);
  
  全体で512キロバイトのエリアになるので16bitに3ビット足して
   19ビット=0x00000~0x7FFFFでメモリを指定可能です。
  
  上位の3ビットはI2Cアドレス指定用に(関数内で)使っています。
 
 このICを使うにあたりの制限や、arduinoのI2Cの制限とか色々ありましたので、
  以下箇条書きしておきます。

 

■今回色々調べた事箇条書き(重複記載あり)

 

8ピンのICで一つ当たり1024Kbitを記録できます。
 1024bit=128Kbye
  今回4個接続しますので
   512Kbyteになります。

1ブロックあたりは64Kbyteで
 24FC1025 は 2ブロック持っている
  アドレスが16bit=0x0000~0xFFFF(65535)なので、
   バンク切り替え的な考え方で指定する。
    実際にはI2Cアドレスで指定する。

A2をVCCにすると、IC内の2ブロック(両方のブロック)が動く
 A2をGNDをにすると、1ブロックのみが動く

 

参照(重複記載あり)


 http://try3dcg.world.coocan.jp/note/i2c/24fc1025.html

 A2をGNDをに接続したとき(1ブロックのみ動作)のI2Cアドレス
  A0=0,A1=0の場合0x50
  A0=1,A1=0の場合0x51
  A0=0,A1=1の場合0x52
  A0=1,A1=1の場合0x53
  
 A2をVCCをに接続したとき(2ブロックの両方動作)のI2Cアドレス
  A0=0,A1=0の場合0x50と0x54
  A0=1,A1=0の場合0x51と0x55
  A0=0,A1=1の場合0x52と0x56
  A0=1,A1=1の場合0x53と0x57


上記の通りA0、A1によって4つのI2Cアドレスに切り替え可能。
 24FC1025を同時に4つまで制御可能なので、
  128Kbyte×4個=512Kbyteまでアクセス可能

 

個別ルール
 ArduinoのI2Cライブラリの制限で
  連続書き込みは30バイト、
  連続読み込みは32バイトの制限あり
 
 24FC1025は 128Byte x 512ページで構成されている。
  連続書き込み時にはページ境界を超えることができない。
  ページ境界を跨ぐ場合には、一旦トランザクションを終了し、
  開始アドレスを設定しなおす必要がある。

 

JUGEMテーマ:電子工作

 

■プログラム

//
// 24FC1025を利用したメモリ読み書き
//   メモリアドレスとして0x00000~0x7FFFFを指定可能
//    int WriteFC1025(uint32_t address, uint8_t *data, uint32_t len);
//    uint32_t ReadFC1025(uint32_t address, uint8_t *data, uint32_t len);
//

#include <Wire.h>

//24FC1025を4個接続して
// 512Kbyteの連続したメモリとして扱う。
int i2c_address[8]={
              0x50, 0x54, //ひとつめ
              0x51, 0x55, //ふたつめ
              0x52, 0x56, //みっつめ
              0x53, 0x57  //よっつめ
             };

//
//i2c_addressテーブルを利用してI2Cアドレスを返す
// addressは0x00000~0x7FFFFを指定可能
//
int getI2cAdress(uint32_t address)
{
  int i2cIdx=address >> 16;
  int i2cAddr = i2c_address[i2cIdx];

//Serial.print("getI2cAdress address=");Serial.print(address, HEX);Serial.print(" i2cAddr=");Serial.println(i2cAddr, HEX);
  
  return i2cAddr;
}
//
//30バイト以下の書き込み 128バイト境界を考慮しない
//arduino制限の30バイト単位での読み書きを基本にする
//
int writeFC1025_short(uint32_t address, uint8_t *data, uint32_t len)
{
  //Serial.print("writeFC1025_short address=");Serial.print(address, HEX);
  //    Serial.print(" len=");Serial.println(len);
  
  int i2cAddr = getI2cAdress(address);
  address = address & 0x0000FFFF;
  //Serial.print("write i2cAddr=");Serial.println(i2cAddr, HEX);
  //Serial.print("write address=");Serial.println(address, HEX);
  if(len > 30){
    Serial.println("error length 30 over [writeFC1025_short]");
    return -1;
  }

  Wire.beginTransmission(i2cAddr);
  Wire.write(highByte(address));
  Wire.write(lowByte(address));
  uint32_t nBytes = Wire.write(data, len);
  Wire.endTransmission();
  delay(3);
  if(nBytes==len){
    return 0;
  }
  else{
    return -1;
  }
}
//
//128バイト境界の処理
// 128バイトのページ毎に分割
//  チップ成約で128バイト境界を超えて連続書き込みはできない
//
int writeFC1025_devide128(uint32_t address, uint8_t *data, uint32_t len)
{
  uint32_t kabemade = address % 128;
  kabemade = 128 - kabemade;


  int nRc;
  if(len > kabemade){
    nRc = writeFC1025_short(address, data, kabemade);
    if(nRc!=0){
      return -1;
    }
    nRc = writeFC1025_short(address + kabemade, data + kabemade, len - kabemade);
    if(nRc!=0){
      return -1;
    }
  }
  else{
    nRc = writeFC1025_short(address, data, len);
    if(nRc!=0){
      return -1;
    }
  }
  return 0;
}

//
//メモリに書き込みする
//  addressは0x00000~0x7FFFFで指定。
//
int WriteFC1025(uint32_t address, uint8_t *data, uint32_t len)
{
  uint32_t nokori_len = len;
  uint8_t *p = data;
  uint32_t a = address;
  if(0x7FFFF < (address+len)){
    Serial.println("error address over 0x7FFFF [WriteFC1025]");
    return -1;
  }
  //
  while(1){
    int nRc;
    if(nokori_len<=30){
      //writeFC1025_short(a, p, nokori_len);
      nRc = writeFC1025_devide128(a, p, nokori_len);
      if(nRc!=0){
        return -1;
      }
      break;
    }
    else{
      //writeFC1025_short(a, p, 30);
      nRc = writeFC1025_devide128(a, p, 30);
      if(nRc !=0){
        return -1;
      }
      a=a+30;
      p=p+30;
      nokori_len = nokori_len - 30;
    }
  }
  return 0;
}

//
//30バイト以下の読み込み 128バイト境界を考慮しない
// arduino制限の30バイト単位での読み書きを基本にする
//
uint32_t readFC1025_short(uint32_t address, uint8_t *data, uint32_t len)
{
  //Serial.print("readFC1025_short address=");Serial.print(address, HEX);
  //    Serial.print(" len=");Serial.println(len);

  int i2cAddr = getI2cAdress(address);
  address = address & 0x0000FFFF;
  //Serial.print("read i2cAddr=");Serial.println(i2cAddr, HEX);
  //Serial.print("read address=");Serial.println(address, HEX);
  if(len > 30){
    Serial.println("error length 30 over [readFC1025_short_short]");
    return -1;
  }
  //
  Wire.beginTransmission(i2cAddr);
  Wire.write(highByte(address));
  Wire.write(lowByte(address));
  Wire.endTransmission();
  Wire.requestFrom(i2cAddr, len);
  int i=0;
  unsigned long time = millis();
  while(1){
    if(Wire.available()){
      data[i] = Wire.read();
      i++;
      if(i>=len){
        break;
      }
    }
    else{
      if(time>millis()){
        time=millis();
      }
      if((millis()-time)>1000){
        //Serial.println("timeout break");
        break;
      }
    }
  }
  return i;
}

//
//128バイト境界の処理
// 128バイトのページ毎に分割
//  ブロックの境界を超える場合にI2Cアドレスが異なるため、
//  連続読み込みができない。2回に分けて書き込み
//
uint32_t readFC1025_devide128(uint32_t address, uint8_t *data, uint32_t len)
{
  uint32_t kabemade = address % 128;
  kabemade = 128 - kabemade;

  uint32_t total_length=0;
  if(len > kabemade){
    total_length = readFC1025_short(address, data, kabemade);
    total_length += readFC1025_short(address + kabemade, data + kabemade, len - kabemade);
  }
  else{
    total_length = readFC1025_short(address, data, len);
  }
}

//
//メモリから読み込む
//  addressは0x00000~0x7FFFFで指定。
//
uint32_t ReadFC1025(uint32_t address, uint8_t *data, uint32_t len)
{
  uint32_t nokori_len = len;
  uint8_t *p = data;
  uint32_t a = address;
  uint32_t total_read_len = 0;
  uint32_t read_len = 0;
  //
  while(1){
    if(nokori_len<=30){
      read_len = readFC1025_devide128(a, p, nokori_len);
      //read_len = readFC1025_short(a, p, nokori_len);
      nokori_len = nokori_len - read_len;
      total_read_len = total_read_len + read_len;
      break;
    }
    else{
      read_len = readFC1025_devide128(a, p, 30);
      //read_len = readFC1025_short(a, p, 30);
      a=a+read_len;
      p=p+read_len;
      nokori_len = nokori_len - read_len;
      total_read_len = total_read_len + read_len;
    }
  }
  return total_read_len;
}

//
//デバッグ用 25FC1025メモリから直接読み取って値を返す。遅い
//
uint8_t debugReadFC1025byte(int i2cAddr, uint32_t address)
{
  address = address & 0x0000FFFF;
  //Serial.print("read i2cAddr=");Serial.println(i2cAddr, HEX);
  //Serial.print("read address=");Serial.println(address, HEX);

  uint8_t data;
  Wire.beginTransmission(i2cAddr); // 送信開始
  Wire.write(highByte(address)); // アドレスの上位1バイトをキューに送信
  Wire.write(lowByte(address)); // アドレスの下位1バイトをキューに送信
  Wire.endTransmission(); // 送信完了
  Wire.requestFrom(i2cAddr, 1); // 受信開始
  data = Wire.read(); // データの読み出し
  return data;
}

//
//デバッグ用 25FC1025メモリに書き込む 遅い
//
void WriteFC1025byte(int i2cAddr, uint32_t address, uint8_t data)
{
  address = address & 0x0000FFFF;
  //Serial.print("write i2cAddr=");Serial.println(i2cAddr, HEX);
  //Serial.print("write address=");Serial.println(address, HEX);

  Wire.beginTransmission(i2cAddr); // 送信開始
  Wire.write(highByte(address)); // アドレスの上位1バイトをキューに送信
  Wire.write(lowByte(address)); // アドレスの下位1バイトをキューに送信
  Wire.write(data); // 書き込むデータをキューに送信
  Wire.endTransmission(); // 送信完了
  delay(4);
}
//
//デバッグ用 25FC1025メモリから直接読み取る
//
void debug_dump(int i2cAddr, uint32_t address, uint32_t len)
{
  Serial.print("debug dump i2cAddr=");Serial.println(i2cAddr, HEX);
   Serial.print(" address=");Serial.println(address, HEX);
    Serial.print(" len=");Serial.println(len);
  
  uint8_t byte;

  int i=0;
  int kosu = 0;
  while(1){
    byte = debugReadFC1025byte(i2cAddr, address);
    Serial.print(byte, HEX);Serial.print(" ");
    kosu++;
    if(kosu>=16){
      kosu=0;
      Serial.println("");
    }
    i++;
    if(i>=len){
      break;
    }
    address++;
  }
  Serial.println("");
}

//
//デバッグ用 メモリ内容をダンプ出力
//
void dump(uint8_t *data, uint32_t len)
{
  Serial.print("dump len=");Serial.println(len);
  int i=0;
  int kosu = 0;
  while(1){
    Serial.print(data[i], HEX);Serial.print(" ");
    kosu++;
    if(kosu>=16){
      kosu=0;
      Serial.println("");
    }
    i++;
    if(i>=len){
      break;
    }
  }
  Serial.println("");
}

void setup() {
  Serial.begin(115200);
  Serial.println("¥nstart:");

  Serial.println("begin:");
  Wire.begin(); 
  Serial.println("setClock:");
  Wire.setClock(1000000); //I2C 1Mhz

  unsigned long start;
  unsigned long sa;
  //メモリをきれいにする
  Serial.println("clear0-1:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x50, 0x0000+i, 0);
  }
  Serial.println("clear0-2:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x50, 0xff00+i, 1);
  }
  
  Serial.println("clear1-1:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x51, 0x0000+i, 2);
  }
  Serial.println("clear1-2:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x51, 0xff00+i, 3);
  }
  
  Serial.println("clear2-1:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x52, 0x0000+i, 4);
  }
  Serial.println("clear2-2:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x52, 0xff00+i, 5);
  }
  
  Serial.println("clear3-1:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x53, 0x0000+i, 6);
  }
  Serial.println("clear3-2:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x53, 0xff00+i, 7);
  }
  
  Serial.println("clear4-1:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x54, 0x0000+i, 8);
  }
  Serial.println("clear4-2:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x54, 0xff00+i, 9);
  }
  
  Serial.println("clear5-1:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x55, 0x0000+i, 0xa);
  }
  Serial.println("clear5-2:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x55, 0xff00+i, 0xb);
  }
  
  Serial.println("clear6-1:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x56, 0x0000+i, 0xc);
  }
  Serial.println("clear6-2:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x56, 0xff00+i, 0xd);
  }
  
  Serial.println("clear7-1:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x57, 0x0000+i, 0xe);
  }
  Serial.println("clear7-2:");
  for(int i=0;i<256;i++){
    WriteFC1025byte(0x57, 0xff00+i, 0xf);
  }
  Serial.println("clear End:");
 


  //試しに書き込み
  uint8_t buffer100[100];
                   //                                                           6         7         8         9        90
                   //1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
  memcpy(buffer100, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzAB", 100);
  start = millis();
  int nRc = WriteFC1025(0x70000, buffer100, 100); //先頭から書き込み
  if(nRc!=0){
    Serial.println("error WriteFC1025");
  }
  
  sa = millis()- start;
  Serial.print("write 100byte = ");Serial.print(sa);Serial.println("ms");
  
  nRc = WriteFC1025(0x7FFFF, buffer100, 100); //1ブロック目の一番最後から書き込んで1ブロックの先頭まで書き込み
  if(nRc!=0){
    Serial.println("error WriteFC1025");
  }

  //上記の書き込みがちゃんと書かれているかを見る
  char read_data[512];
  int read_len;
  //メモリのアドレス直接指定でちゃんと書かれているかを確かめる
  Serial.println("****");
  Serial.println("DUMP");
  Serial.println("****");
  read_len = ReadFC1025(0x00000, read_data, 256);
  Serial.print("read data 0x00000 ");dump(read_data, read_len);
  read_len = ReadFC1025(0x0FF00, read_data, 256);
  Serial.print("read data 0x0FF00 ");dump(read_data, read_len);
  Serial.println("****");
  read_len = ReadFC1025(0x10000, read_data, 256);
  Serial.print("read data 0x10000 ");dump(read_data, read_len);
  read_len = ReadFC1025(0x1FF00, read_data, 256);
  Serial.print("read data 0x1FF00 ");dump(read_data, read_len);
  Serial.println("****");
  read_len = ReadFC1025(0x20000, read_data, 256);
  Serial.print("read data 0x20000 ");dump(read_data, read_len);
  read_len = ReadFC1025(0x2FF00, read_data, 256);
  Serial.print("read data 0x2FF00 ");dump(read_data, read_len);
  Serial.println("****");
  read_len = ReadFC1025(0x30000, read_data, 256);
  Serial.print("read data 0x30000 ");dump(read_data, read_len);
  read_len = ReadFC1025(0x3FF00, read_data, 256);
  Serial.print("read data 0x3FF00 ");dump(read_data, read_len);
  Serial.println("****");
  read_len = ReadFC1025(0x40000, read_data, 256);
  Serial.print("read data 0x40000 ");dump(read_data, read_len);
  read_len = ReadFC1025(0x4FF00, read_data, 256);
  Serial.print("read data 0x4FF00 ");dump(read_data, read_len);
  Serial.println("****");
  read_len = ReadFC1025(0x50000, read_data, 256);
  Serial.print("read data 0x50000 ");dump(read_data, read_len);
  read_len = ReadFC1025(0x5FF00, read_data, 256);
  Serial.print("read data 0x5FF00 ");dump(read_data, read_len);
  Serial.println("****");
  read_len = ReadFC1025(0x60000, read_data, 256);
  Serial.print("read data 0x60000 ");dump(read_data, read_len);
  read_len = ReadFC1025(0x6FF00, read_data, 256);
  Serial.print("read data 0x6FF00 ");dump(read_data, read_len);
  Serial.println("****");
  read_len = ReadFC1025(0x70000, read_data, 256);
  Serial.print("read data 0x70000 ");dump(read_data, read_len);
  read_len = ReadFC1025(0x7FF00, read_data, 256);
  Serial.print("read data 0x7FF00 ");dump(read_data, read_len);

}

void loop()
{

}

手動でグローランプをやってみた。

 

前に蛍光灯の安定器を自分で巻いて記事にしておりました。
 https://miha.jugem.cc/?eid=379

 

今回はその続きで、
 グローランプも自分でやってみたの記録です。

 

Youtube画像 https://youtu.be/JuUeBzEys98

 


見てご覧の通り蛍光灯は無事点灯しました。

 

グローランプの解説

 

「電源が入るとグロースターターに放電が生じ、可動電極と固定電極が接触する。その結果、蛍光灯の電極が予熱され、熱電子が出やすくなる。可動電極と固定電極が離れる瞬間、安定器から発生するパルス電圧が蛍光灯電圧に加圧され、始動する」

 

 

 

回路


 
 
 回路の右側のGがグローランプで、ここを手動でやってます。

  はじめはスイッチでと思ったんですけど。結局
   ワニ口クリップを手動で接触してみました。

 

 パチって手元で火花が散るんですけどね。
  意外と慣れると怖くないです。
 

AVRライター修理・復活!!

 

同様に壊れてしまった人は参考になるかもと思いまして、記録します。

 

 

機種は「しなぷすのハード制作記」製
   の
 「Arduino用ブートローダ/スケッチライタキット」
 
 販売ページ
  https://www.switch-science.com/products/1999/
   売り切れ


 サポートページ
  https://synapse.kyoto/hard/bootloader_sketch/page001.html
  懐かしいですね。
  サポートしっかりしているのでこれを買った思い出があります。

 

今は完成版の販売ですね。
 https://www.switch-science.com/products/3230
  ATMEGA328がDIPではないので外せないですね。
   この記事の直し方は通じないかも。

 

本題

 

前の記事でAVRライターが壊れたので独自設計で作りました。
 https://miha.jugem.cc/?eid=390
しかし、もったいないのもあり。捨てずに持ってました。

 

後日・・・もしかして、
 ATMEGAに書き込まれていたスケッチやブートローダーが静電気か何かで
  吹き飛んだせいではないか?と思い、
   ARDUINO-UNOのブートローダーと、
    ARDUINO-ISPを書き込んだけど、
     ハートビートのLEDの明暗は復活しましたが、
      書き込みまでは至らず。


さらにその後日。
 もしかしたら専用のスケッチが有るのでは?
 と、「しなぷすのハード制作記」をじっくり見ていたら、
  ArduinoPro or ProMiniの3.3V/8MHzのブートローダーを書き込み
   さらに、
  スケッチは若干の修正が必要との情報。(※)
  
   https://synapse.kyoto/hard/bootloader_sketch/page003.html
   https://synapse.kyoto/hard/bootloader_sketch/page004.html
    ここのページって「キットの場合は不要」なので、
     見てなかったんですね。
    ※スケッチは標準のARDUINO-ISPのまま変えなくてもOKでした。

 

その結果

 

 無事復活をとげました。

 

静電気怖いねー。今も炬燵で作業中。

 

 

ArduinoIDEで定義済みdefineの活用

 

前の記事
 https://miha.jugem.cc/?eid=391

 

ATtiny85用とATmega328用で実行するプログラムを切り替えたい
 その場合はifdefで切り替えるといった趣旨の事を前の記事で
  書いてみましたが。

 

実際に活用する書き方などまとめてみました。
 自分で使う場合の私的なメモ的な意味が強いですが、
  参考になればと思います。

 

基本的な書き方。

 

例・・・ ATtiny85とATmega328で生成されるコードを書き換える。
 
ATtiny85であれば、__AVR_ATtiny85__が定義されていてます。
ATmega328であれば、__AVR_ATmega328P__が定義されています。

 

以下の例は、
 ATtiny85はシリアルがそのまま使えず、ソフトシリアルで9600bps
 ATmega328は普通のシリアルで115200bpsにする場合の例です。

 

プログラム例
  
#if defined (__AVR_ATtiny85__)
#include <SoftwareSerial.h>
SoftwareSerial s(3, 4); // RX, TX
#endif

void setup()
{
  
#if defined (__AVR_ATtiny85__)
  s.begin(9600);
#endif

#if defined (__AVR_ATmega328P__)
  Serial.begin(115200);
#endif

}

「#if defined ()」で定義の有無を調べて存在すれば「#endif」までのコードを生成します。
 「コードを生成」と書きましたが、これらはコンパイラに指示するためのもので、
  実行時ではないからです。

上記の場合は実行されるチップを見分けるものですが、
 他にも応用は沢山あると思います。
 
例・・・8ピン系と14ピン系でピンの使い方を変える
 8ピン系シリーズ
  __AVR_ATtiny13__
  __AVR_ATtiny25__
  __AVR_ATtiny45__
  __AVR_ATtiny85__
 14ピン系シリーズ
  __AVR_ATtiny24__
  __AVR_ATtiny44__
  __AVR_ATtiny84__
  __AVR_ATtiny441__
  __AVR_ATtiny841__
 28ピン系シリーズ
  __AVR_ATmega328P__
  __AVR_ATmega168P__

 

「#if」も || (または)を使う事ができて

 

#if defined( __AVR_ATtiny13__ ) || defined( __AVR_ATtiny25__ ) || defined( __AVR_ATtiny45__ ) || defined( __AVR_ATtiny85__ )

 8ピンシリーズの記述 

#elif defined( __AVR_ATtiny24__ ) || defined( __AVR_ATtiny44__ ) || defined( __AVR_ATtiny84__ ) || defined( __AVR_ATtiny441__ ) || defined( __AVR_ATtiny841__ )

 14ピンシリーズの記述

#endif

 

その他には?

他にも、ARDUINOの種類がわかる為のもの

 ARDUINO_AVR_ADK
 ARDUINO_AVR_BT
 ARDUINO_AVR_DUEMILANOVE
 ARDUINO_AVR_ESPLORA
 ARDUINO_AVR_ETHERNET
 ARDUINO_AVR_FIO
 ARDUINO_AVR_GEMMA
 ARDUINO_AVR_LEONARDO
 ARDUINO_AVR_LILYPAD
 ARDUINO_AVR_LILYPAD_USB
 ARDUINO_AVR_MEGA
 ARDUINO_AVR_MEGA2560
 ARDUINO_AVR_MICRO
 ARDUINO_AVR_MINI
 ARDUINO_AVR_NANO
 ARDUINO_AVR_NG
 ARDUINO_AVR_PRO
 ARDUINO_AVR_ROBOT_CONTROL
 ARDUINO_AVR_ROBOT_MOTOR
 ARDUINO_AVR_UNO
 ARDUINO_AVR_YUN

 ARDUINO_AVR_ATTINYX4
 ARDUINO_AVR_ATTINYX5
  ・
  ・
  ・
  略

 

ARDUINOはUNOのしか持っていなくて、
 互換機でもARDUINO_AVR_UNOが定義されているというか、
  互換機だからねー当たり前ですねー

 

ARDUINOの種類でもそれぞれに色々特徴があるので、

 活用次第と思いますが、自分は使ってないかなーと思います。 

 

自分が使うのは今のところ上で書いた
 CPUの種類が判れば良いってぐらいですね。


 

ATtiny85とBME280で温度湿度気圧をUARTで表示する

 

ATtinyシリーズのマイコンはI2CとUARTの接続ピンを持ちません。
しかし、ですが、
 それを補う為に
  ソフトウェアシリアルや、
  tiny用I2Cライブラリ(TinyWireM.h等)が用意されています。
 
 これらを使う例として参考プログラムと限界(特にソフトウェアシリアル)
  について記したいと思います。

 

I2Cについてですが、
 参考ページ https://kurobekoblog.com/attiny85_tinywirem
 にあるように、通常のwireと同じ様に使える反面
  writeメソッドだけがsendというメソッドになっていて、
   ATtiny用にプログラムを書き換えるか、ifdef等する必要が出てきます。

 

 プログラムでは以下が該当箇所です。
  
  #include <TinyWireM.h>
  
  TinyWireM.begin();
  writeReg(0xF2, ctrl_hum_reg);
  writeReg(0xF4, ctrl_meas_reg);
  writeReg(0xF5, config_reg);
  
  TinyWireM.beginTransmission(BME280_ADDRESS);
  TinyWireM.send(0x88);
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(BME280_ADDRESS, 24);
  while (TinyWireM.available()) {
    data[i] = TinyWireM.read();
    i++;
  }
  data[i] = TinyWireM.read();
  
  
ソフトウェアシリアルについては、
 最近ではteratermとの接続は115200bpsで行うことが多く、
  もっぱらこれでやっていますが、
   スピードが付いてこないみたいです。


 ですので、
  (伝統的な9600bpsを使います。)
  (それ以前は300ボーとかの時代もありましたけど。)
 本プログラムでは9600bpsでUART通信する事にしました。


 でも、それでも、文字化けが発生することもあり、
 teraterm側からの送信設定(設定→シリアルポート)の送信遅延に2ミリ秒/字
  と 2ミリ秒/行を入れることは文字化けは防ぐことが出来ました。


 デバッグで文字をPCへ表示したいといった場合はこれで活用できることと思います。
  デバッグが終わったらシリアル部分を外すとメモリが節約できてよろしい。
   →本ちゃんでも残したい場合は後術するpicoUART.hの方法がよろしい。
  今回のプログラムではマイコン→パソコンの方向だけですが、
   PCからマイコンに送る際に文字化けすることが多いようです。
    ですので、teratermの設定をいじるのが早いと判断します。
 
 プログラムでは以下が該当箇所です。


  ※これらは最終的にはpicoUART.h化したのでコメントになっています。
  
  #include <SoftwareSerial.h>
  
  SoftwareSerial s(3, 4); // RX, TX
  
  s.begin(9600);
  s.println("start ");
  
  s.print("temp_act= ");  s.println(temp_act);
  s.print("press_act= "); s.println(press_act);
  s.print("hum_act= ");   s.println(hum_act);
  

回路
  
  
  
  

I2Cの接続が何処になるのかというと、
 D2がSCL
 D0がSDA
  のようです。
   ライブラリを調べてないですが、設定有るかもしれません。

 

8ピンのうち6ピン消費ですね。
 残りまだ2ピンも残っています。
 何かに使えそうですね。もう一つソフトウェアシリアルを使ってもいいですし。
 I2C接続のものであれば、まだまだ繋げますしね。
 可能性は無限大なんちゃって。

 

機能は少ないですがコンパイルの結果を見ると以下のようにメモリを消費していて
 
 スケッチが6310バイト(77%)
 
ATtiny85の77%のプログラムエリアを消費してしまいました。
 ATtiny13やATtiny25、ATtiny45とかだとプログラム用メモリが1Kbyte~4Kbyteなので、入りません。
  ソフトウェアシリアルのライブラリが結構大きそうな気がします。

 

ソフトウェアシリアルの部分を全部外すと
 
 スケッチが1376バイト(16%)
 
 77%→16%って結構な削減効果ですね。ATtiny25以上なら走らせることが出来そうです。

 

それならと、
上記の参考ページからBasicSerial3.hのライブラリを探すのですが、見つからず。
 よく製造元のページを見ると新型が作られており、名前をpicoUART.hというみたいです。

それに合わせて組み直して最終版にしました。
  
  スケッチが4682バイト(57%)
  
 これではATtiny45以下では走りませんね。残念。
  でも、当初よりもだいぶ減ったんですがね。77%→57%
 
 ソフトシリアルでサポートされていた小数点の数字を表示する為に
  数字を文字列に変換する関数を自前で組んだんですが、まあまあ容量食いましたね。
 
ただ、小数点数字を文字列に変換する関数dtoaの中身の
 
 str[i] = wari+0x30;
 
  この行だけコメントにすると
 
 スケッチが2422バイト(29%)
 
・・となる。どうなってるんでしょうね?色々やってみましたがわかりませんでした。


picoUARTライブラリについて。
 以下から入手可能になっています。
  https://github.com/nerdralph/picoUART/releases

 

 picoUART.hの調整箇所

  Arduino¥libraries¥picoUART-1.2.0¥src
   pu_config.hの5行の通信速度
    #define PU_BAUD_RATE 9600L            // default baud rate
   同pu_config.hの8行9行のTXとRXのピン番号
    #define PU_TX B,4
    #define PU_RX B,3
  であります。
   BasicSerial3.hと考え方は同じ

 

プログラムです

/*
 * ATtiny85とBME280を使って
 * 温度湿度気圧をUartで表示するプログラム
 * 
 *接続
 * ATtiny85   BME280      UART
 *        1      
 *    RX  2           --> TX
 *    TX  3           --> RX
 *    GND 4---GND     --> GND
 *    SDA 5---SDA
 *        6
 *    SCL 7---SCL
 *    VCC 8---VCC
 *            SDO - GND アドレス0x76
*/

//https://github.com/nerdralph/picoUART/releases
#include <picoUART.h>
#include <pu_print.h>

//https://github.com/dustin/buggles
//#include <BasicSerial3.h>

//https://github.com/adafruit/TinyWireM
#include <TinyWireM.h>
//#include <SoftwareSerial.h>

#define BME280_ADDRESS 0x76

//SoftwareSerial s(3, 4); // RX, TX

unsigned long int hum_raw, temp_raw, pres_raw;
signed long int t_fine;
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
int8_t dig_H1;
int16_t dig_H2;
int8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
int8_t dig_H6;

//
// double convert to char *
//  9999.999まで 正の数のみ
//
void dtoa(double in, int shousuu_keta, char *out, int len)
{
  long base = 10000000;

  int shousuu_kake=1;
  for(int i=0;i<shousuu_keta;i++){
    shousuu_kake = shousuu_kake * 10;
  }
  
  //Serial.print("shousuu_kake=");Serial.println(shousuu_kake);

  char str[20];
  memset(str, 0, sizeof(str));
  long nMoto = (long)((double)in * (double)shousuu_kake);
  //Serial.print("nMoto=");Serial.println(nMoto);
  
  for(int i=0;i<8;i++){
    unsigned long wari = nMoto / base;
    unsigned long amari = nMoto % base;
    //Serial.print("wari=");Serial.println(wari);
    //Serial.print("amari=");Serial.println(amari);
    str[i] = wari+0x30;  //ここのstr[i]に入れるところここをコメントにするとスケッチのサイズが大幅に小さくなるなぜ?スケッチが2422バイト(29%)
    nMoto = amari;
    base = base / 10;
    //Serial.print("base=");Serial.println(base);
  }
  //
  //Serial.print("str=");Serial.println(str);
  //
  int j=0;
  //char out[20];
  memset(out, 0, len);
  int first = 1;
  for(int i=0;i<8;i++){
    if(str[i]!='0'){
      first=0;
    }
    if(first==0){
      out[j]=str[i];
      j++;
      first=0;
    }
  }
  //Serial.print("out=");Serial.println(out);
  
  char tmp[20];
  char tmp2[20];
  if(j<=shousuu_keta){
    strcpy(tmp, out);
    strcpy(out, "0.");
    strcat(out, tmp);
  }
  if(j>=(shousuu_keta+1)){
    strcpy(tmp, &out[j-shousuu_keta]);
    memset(tmp2, 0, sizeof(tmp2));
    memcpy(tmp2, out, j-shousuu_keta);
    strcpy(out, tmp2);
    strcat(out, ".");
    strcat(out, tmp);
  }
  
  //Serial.print("str=");Serial.println(str);
  //Serial.print("out=");Serial.println(out);
}

void setup()
{
  uint8_t osrs_t = 1; //Temperature oversampling x 1
  uint8_t osrs_p = 1; //Pressure oversampling x 1
  uint8_t osrs_h = 1; //Humidity oversampling x 1
  uint8_t mode = 3; //Normal mode
  uint8_t t_sb = 5; //Tstandby 1000ms
  uint8_t filter = 0; //Filter off
  uint8_t spi3w_en = 0; //3-wire SPI Disable
  uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode;
  uint8_t config_reg = (t_sb << 5) | (filter << 2) | spi3w_en;
  uint8_t ctrl_hum_reg = osrs_h;

  //s.begin(9600);
  //s.println("start ");
  prints("Start¥r¥n");

  TinyWireM.begin();
  writeReg(0xF2, ctrl_hum_reg);
  writeReg(0xF4, ctrl_meas_reg);
  writeReg(0xF5, config_reg);
  readTrim();
  // starttime = millis(); //タイマーリセット
}

void loop()
{
  double temp_act = 0.0, press_act = 0.0, hum_act = 0.0;
  signed long int temp_cal;
  unsigned long int press_cal, hum_cal;
  readData();
  temp_cal = calibration_T(temp_raw);
  press_cal = calibration_P(pres_raw);
  hum_cal = calibration_H(hum_raw);
  
  temp_act = (double)temp_cal / 100.0;
  press_act = (double)press_cal / 100.0;
  hum_act = (double)hum_cal / 1024.0;
  
  //s.print("temp_act= ");  s.println(temp_act);
  //s.print("press_act= "); s.println(press_act);
  //s.print("hum_act= ");   s.println(hum_act);
  
  char str_temp_act[16];
  char str_press_act[16];
  char str_hum_act[16];
  dtoa((double)temp_act,  1, str_temp_act,  sizeof(str_temp_act));
  dtoa((double)press_act, 1, str_press_act, sizeof(str_press_act));
  dtoa((double)hum_act,   1, str_hum_act,   sizeof(str_hum_act));

  prints("temp_act=  ");  prints(str_temp_act);   prints("¥r¥n");  
  prints("press_act= ");  prints(str_press_act);  prints("¥r¥n");  
  prints("hum_act=   ");  prints(str_hum_act);    prints("¥r¥n");  


  delay(1000); // 一定時間待ち
}

void readTrim()
{
  uint8_t data[32], i = 0;
  TinyWireM.beginTransmission(BME280_ADDRESS);
  TinyWireM.send(0x88);
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(BME280_ADDRESS, 24);
  while (TinyWireM.available()) {
    data[i] = TinyWireM.read();
    i++;
  }
  TinyWireM.beginTransmission(BME280_ADDRESS);
  TinyWireM.send(0xA1);
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(BME280_ADDRESS, 1);
  data[i] = TinyWireM.read();
  i++;
  TinyWireM.beginTransmission(BME280_ADDRESS);
  TinyWireM.send(0xE1);
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(BME280_ADDRESS, 7);
  while (TinyWireM.available()) {
    data[i] = TinyWireM.read();
    i++;
  }
  dig_T1 = (data[1] << 8) | data[0];
  dig_T2 = (data[3] << 8) | data[2];
  dig_T3 = (data[5] << 8) | data[4];
  dig_P1 = (data[7] << 8) | data[6];
  dig_P2 = (data[9] << 8) | data[8];
  dig_P3 = (data[11] << 8) | data[10];
  dig_P4 = (data[13] << 8) | data[12];
  dig_P5 = (data[15] << 8) | data[14];
  dig_P6 = (data[17] << 8) | data[16];
  dig_P7 = (data[19] << 8) | data[18];
  dig_P8 = (data[21] << 8) | data[20];
  dig_P9 = (data[23] << 8) | data[22];
  dig_H1 = data[24];
  dig_H2 = (data[26] << 8) | data[25];
  dig_H3 = data[27];
  dig_H4 = (data[28] << 4) | (0x0F & data[29]);
  dig_H5 = (data[30] << 4) | ((data[29] >> 4) & 0x0F);
  dig_H6 = data[31];
}

void writeReg(uint8_t reg_address, uint8_t data)
{
  TinyWireM.beginTransmission(BME280_ADDRESS);
  TinyWireM.send(reg_address);
  TinyWireM.send(data);
  TinyWireM.endTransmission();
}

void readData()
{
  int i = 0;
  uint32_t data[8];
  TinyWireM.beginTransmission(BME280_ADDRESS);
  TinyWireM.send(0xF7);
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(BME280_ADDRESS, 8);
  while (TinyWireM.available()) {
    data[i] = TinyWireM.receive();
    i++;
  }
  pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
  temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
  hum_raw = (data[6] << 8) | data[7];
}

signed long int calibration_T(signed long int adc_T)
{
  signed long int var1, var2, T;
  var1 = ((((adc_T >> 3) - ((signed long int)dig_T1 << 1))) * ((signed long int)dig_T2)) >> 11;
  var2 = (((((adc_T >> 4) - ((signed long int)dig_T1)) * ((adc_T >> 4) - ((signed long int)dig_T1))) >> 12) * ((signed long int)dig_T3)) >> 14;
  t_fine = var1 + var2;
  T = (t_fine * 5 + 128) >> 8;
  return T;
}

unsigned long int calibration_P(signed long int adc_P)
{
  signed long int var1, var2;
  unsigned long int P;
  var1 = (((signed long int)t_fine) >> 1) - (signed long int)64000;
  var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((signed long int)dig_P6);
  var2 = var2 + ((var1 * ((signed long int)dig_P5)) << 1);
  var2 = (var2 >> 2) + (((signed long int)dig_P4) << 16);
  var1 = (((dig_P3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + ((((signed long int)dig_P2) * var1) >> 1)) >> 18;
  var1 = ((((32768 + var1)) * ((signed long int)dig_P1)) >> 15);
  if (var1 == 0) {
    return 0;
  }
  P = (((unsigned long int)(((signed long int)1048576) - adc_P) - (var2 >> 12))) * 3125;
  if (P < 0x80000000) {
    P = (P << 1) / ((unsigned long int) var1);
  } else {
    P = (P / (unsigned long int)var1) * 2;
  }
  var1 = (((signed long int)dig_P9) * ((signed long int)(((P >> 3) * (P >> 3)) >> 13))) >> 12;
  var2 = (((signed long int)(P >> 2)) * ((signed long int)dig_P8)) >> 13;
  P = (unsigned long int)((signed long int)P + ((var1 + var2 + dig_P7) >> 4));
  return P;
}

unsigned long int calibration_H(signed long int adc_H)
{
  signed long int v_x1;
  v_x1 = (t_fine - ((signed long int)76800));
  v_x1 = (((((adc_H << 14) - (((signed long int)dig_H4) << 20) - (((signed long int)dig_H5) * v_x1)) +
            ((signed long int)16384)) >> 15) * (((((((v_x1 * ((signed long int)dig_H6)) >> 10) *
                (((v_x1 * ((signed long int)dig_H3)) >> 11) + ((signed long int) 32768))) >> 10) + (( signed long int)2097152)) *
                ((signed long int) dig_H2) + 8192) >> 14));
  v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((signed long int)dig_H1)) >> 4));
  v_x1 = (v_x1 < 0 ? 0 : v_x1);
  v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1);
  return (unsigned long int)(v_x1 >> 12);
}

AVRライターを作りました

 

しなっぷすさんのライターを使っていたのですが、
 https://www.switch-science.com/products/1999/
残念ながら壊れてしまいました。
 10年以上おつかれさまでした。

 

同じのを買うのもつまんないので、自分で設計して作ってみました。

 

仕様としては、
 ATMEGA328PUとATTINY85-20PUに書き込めれば良い。
 その他はICSPの端子を用意して必要に応じで繋げば良い。
  という事にしました。

 

重要なピンがぶつかっていない事を祈りつつ設計してみると、
 意外と大丈夫で。

 

スイッチ無しでも
 以下の回路で済むことが判明。
  ピン互換であれば、同じシリーズなら大丈夫です。試験してないけど(笑)
   欲張って他のAVRもと、色々考えましたが、今回はここまで。
 
回路図
 
 
 
 
書き込み対象ボードとしては以下を試験しました。
 Arduino Uno 16MHz (ATmega328)
 Arduino Pro  or ProMini 3.3V 8Mhz (ATmega328)
 ATmega328 on a breadboard(8Mhz internal clock)
 ATtiny85 Internal 8MHz
 
 自分は下の2つをよく使います。Xtal不要なんで重宝しています。
 
書き込まれる側のボードとしてとしてArduinoUNOの16Mhzを選んだ場合でも、
 8Mhzのセラロックで動きます。

 

ゼロプレッシャーソケットで28ピンの328を繋ぐ場合と
 Tiny85を繋ぐ場合で以下の回路になりますが、
  うまいことぶつからない様になっています。
   大丈夫みたいです。
  ソケットには2つ同時にセット出来ないのでこうなってます。

 

28ピンの328の場合の結線
 
 
 
 
8ピンのTiny85の場合の結線
 
 
 
 
写真
 
 
 
 
書き込み用の制御はATMEGA328を使っております。
 これにはArduinoISPのスケッチが書き込まれてます。
  このスケッチは、ArduinoIDEにスケッチ例として
   標準で入っているものです。

 

使い方
 PCとの接続をします。


 本器はSwitchSCIENCEさんで販売している「USB変換アダプタ」
  https://www.switch-science.com/products/2782?variant=42381940883654
 と接続を前提として作っております。ここはしなっぷすさんを踏襲。

 

 電圧は5Vです。

 3.3Vでも調子が良いと動きますが、LEDのハートビートが止まっている場合は停止しています。

 
 ゼロプレッシャーソケットのレバーをフリー(立てる)にして
 
 ICをセット。向きは1番ピンをレバーの有る方。
 
 セロプレッシャーソケットのレバーをロック(横にする)にする。
 
 次に、ArduinoIDEの操作をします。
 
 ツールからボードを指定して(上記のどれか)
  ボードによってはチップやプロセッサ、クロック電圧等の指定
 
 comポートの指定
 
 書き込み装置としてArduino as ISPを指定
 
 ブートローダー書き込みの場合は
  ツールからブートローダーを書き込む
 
 スケッチ書き込みの場合は
  スケッチから書き込み装置を使って書き込む
  
 書き込み完了まで待つ。
 
使用感としては
 スイッチサイエンスで売っているものと比較してですけど、
 使えるボードが少ない。ここが致命的な人もいるでしょう。
 
 自分が使っているICの種類が少ないが故に、そう不便は感じずに
  スイッチの切替が不要になったので、より便利になってます。
 ここらへんが自作の醍醐味なんでしょうね。
 自分としては元通り書き込みできるので大満足です。