watchX:生成された Hello World の内容確認

mBlock が生成した「Hello World」のソースは、こういう感じでした。

#include <Arduino.h>
#include <Wire.h>
#include <SoftwareSerial.h>

#include "watchX.h"
#include <SPI.h>

double angle_rad = PI/180.0;
double angle_deg = 180.0/PI;
unsigned char animation_offsetY=0;
bool needClear = false;
void writeText(int oledPosX, int oledPosY, String oledText){
drawString(oledPosX,oledPosY,oledText.c_str() ,smallFont);}

void setup(){
 SPI.begin();
 ssd1306_configure();
 clearAll();
 writeText(0, 0, ((String)"Hello World!").c_str());
 needClear = true;
}

void loop(){
 _loop();
}

void _delay(float seconds){
 long endTime = millis() + seconds * 1000;
 while(millis() < endTime)_loop();
}

void _loop(){
 if(needClear){
 ssd1306_drawBuffer(0, 0, 128,64, mbuf);
 clearAll();
 needClear = false;
 }
}

内容を確認してみます。

#include <Arduino.h>
#include <Wire.h>
#include <SoftwareSerial.h>

#include "watchX.h"
#include <SPI.h>

通常の Arduino 系 include に加えて、”watchX.h”ヘッダーがあります。

watchX.h

#include "Adafruit_Sensor.h"
#include "Adafruit_BMP280.h"
#include "SparkFun_MAG3110.h"
#include "RTClib.h"
#include "MPU6050.h"
#include "oled.h"
#include "resources.h"
#include "Adafruit_BLE.h"
#include "Adafruit_BluefruitLE_SPI.h"
#include "Adafruit_BluefruitLE_UART.h"
#include "BluefruitConfig.h"

ライブラリ関連のincludeをまとめて行ってますね。

setup() の処理を見ると、

ssd1306_configure();

で watchX 表示関連の初期化を行っているみたいです。ssd1306_configure() は、oled.cpp にあります。

void ssd1306_configure(){
  const uint8_t s_oled128x64_initData[] =
  {
   SSD1306_DISPLAYOFF, // display off
   SSD1306_MEMORYMODE, HORIZONTAL_ADDRESSING_MODE, // Page Addressing mode
   SSD1306_COMSCANDEC, // Scan from 127 to 0 (Reverse scan)
   SSD1306_SETSTARTLINE | 0x00, // First line to start scanning from
   SSD1306_SETCONTRAST, 0xFF, // contast value to 0x7F according to datasheet
   SSD1306_SEGREMAP | 0x01, // Use reverse mapping. 0x00 - is normal mapping
   SSD1306_NORMALDISPLAY,
   SSD1306_SETMULTIPLEX, 63, // Reset to default MUX. See datasheet
// SSD1306_SETDISPLAYOFFSET, 0x00, // no offset
// SSD1306_SETDISPLAYCLOCKDIV, 0x80,// set to default ratio/osc frequency
// SSD1306_SETPRECHARGE, 0xF1, // switch precharge to 0x22 // 0xF1
// SSD1306_SETCOMPINS, 0x12, // set divide ratio
   SSD1306_SETVCOMDETECT, 0x40, // vcom deselect to 0x20 // 0x40
   SSD1306_CHARGEPUMP, 0x14, // Enable charge pump
// 0X20,0X80,
// SSD1306_DISPLAYALLON_RESUME,
   SSD1306_DISPLAYON
  };

  pinMode(rstPin, OUTPUT);
  pinMode(cesPin, OUTPUT);
  pinMode(dcPin, OUTPUT);

  digitalWrite(rstPin, HIGH);
  digitalWrite(dcPin, HIGH);
  digitalWrite(cesPin, HIGH);

  for( uint8_t i=0; i<sizeof(s_oled128x64_initData); i++)
  {
     ssd1306_sendCommand(s_oled128x64_initData[i]);
  }

}

ピンの状態を設定して、初期設定のコマンドを順に ssd1306_sendCommand で送ってます。

次が clearAll();

void clearAll(){
  //if(animation_offsetY==0)
   memset(mbuf, 0x00, 1024);
  //for(uint16_t i=0;i<1024;i++) mbuf[i]=0;
}

mbuf[1024]が、画面表示のバッファです。0クリアしてます。

次の

writeText(0, 0, ((String)"Hello World!").c_str());

が、”Hello World!” を バッファに書き込んでいるところです。

drawString(oledPosX,oledPosY,oledText.c_str() ,smallFont);

を(フォントの指定を追加して)呼び出してます。実際の処理は oled.cpp 内のdrawString。

void drawString(uint8_t x,uint8_t y,const char*s,const char* font,char startindex=-32);
void drawString(uint8_t x,uint8_t y,const char*s,const char* font,char startindex){
  char a=0;
  if(!s)return;
  while(s[a]!=0){
 // strtmpbuf[a]= pgm_read_byte_near(months[month]+a);
 // draw_bitmap( 0, 32, font_mid, 19, 24, false, 0);
    draw_bitmap( x+(a*6), y, font+((s[a]+startindex)*5), 5, 8, false, 0);
    a++;
  }
}

位置(x,y)から、文字列char* s それぞれ順番に、フォント char*font を用いて描きます。
startIndexはフォント文字コードとフォントデータの開始位置調整です。なにもなければデフォルトで指定されている「-32」が使われてます。文字コードの制御文字コードの部分を飛ばすためですね。

1文字描くごとに、x 位置を 6 ずつずらしています。(完全にフォントサイズ固定な処理ですね。)
font+((s[a]+startindex)*5)で、フォントデータの開始位置。これも1フォントのデータ5バイト固定想定での実装です。

フォントデータは,resources.h の CHARACTER_SET 経由で、english.h にありました。

#define CHARACTER_SET \
0x00,0x00,0x00,0x00,0x00,/* space */ \
0x00,0x5F,0x00,0x00,0x00,/* ! */ \
0x00,0x07,0x00,0x07,0x00,/* " */ \
0x14,0x7F,0x14,0x7F,0x14,/* # */ \
0x24,0x2A,0x7F,0x2A,0x12,/* $ */ \
0x23,0x13,0x08,0x64,0x62,/* % */ \
0x36,0x49,0x55,0x22,0x50,/* & */ \
0x00,0x05,0x03,0x00,0x00,/* ' */ \
0x1C,0x22,0x41,0x00,0x00,/* ( */ \
0x41,0x22,0x1C,0x00,0x00,/* ) */ \
:
0x3E,0x51,0x49,0x45,0x3E,/* 0 */ \
0x00,0x42,0x7F,0x40,0x00,/* 1 */ \
0x62,0x51,0x49,0x49,0x46,/* 2 */ \
0x22,0x41,0x49,0x49,0x36,/* 3 */ \
0x18,0x14,0x12,0x7F,0x10,/* 4 */ \
0x27,0x45,0x45,0x45,0x39,/* 5 */ \
0x3C,0x4A,0x49,0x49,0x30,/* 6 */ \
0x01,0x71,0x09,0x05,0x03,/* 7 */ \
0x36,0x49,0x49,0x49,0x36,/* 8 */ \
0x06,0x49,0x49,0x29,0x1E,/* 9 */ \
:
0x7E,0x09,0x09,0x09,0x7E,/* A */ \
0x7F,0x49,0x49,0x49,0x36,/* B */ \
0x3E,0x41,0x41,0x41,0x22,/* C */ \
0x7F,0x41,0x41,0x22,0x1C,/* D */ \
0x7F,0x49,0x49,0x49,0x41,/* E */ \
0x7F,0x09,0x09,0x09,0x01,/* F */ \
0x3E,0x41,0x41,0x51,0x72,/* G */ \
:

という感じ。例えば

0x7E,0x09,0x09,0x09,0x7E,/* A */ \

を16進→2進 変換してみると…

7E : ‭01111110‬
09 : 00001001
09 : 00001001
09 : 00001001
7E : ‭01111110‬

んんんー?「A」の形にならない…。と思ったら、縦向きに描かれるらしい。

漢字表示グラフィック液晶表示ライブラリ
http://www.picfun.com/PIC32MX/PIC32MXAp05.html

こういうことか!

このデータを

void draw_bitmap(byte x, byte yy, const char* bitmap, byte w, byte h, bool invert=false, byte offsetY=0);

で画面表示バッファmbuf に設定してます。中身の処理は追っかけないですけども、x , yy + offsetY の位置に、bitmap で指定したフォントデータを、幅 w 高さ h でセットするようですね。invert = true にすると、白黒反転すると思われます。

その後、loop() の中で、

ssd1306_drawBuffer(0, 0, 128,64, mbuf);

実際の画面に mbuf 内容を描画して、

clearAll();

で描画が終了した mbuf をクリアして、終了な感じですね。(実際は loop()を回ってますけども。)


フォントは縦横回転してしまうフォーマットなので、M5Stack の時に作ったデータはそのままでは使えないなあー。