LSI Jiu-Jitsu

半導体とブラジリアン柔術

OLED 0.96インチで遊ぶ ~その6~

前回の ~その5~ に続いて今回もPICから表示を行ってみます。

表示する画像データは予め外部のEEPROMに格納しておき、I2Cバスを通してリードを行いOLEDへ送信するようにしました。

EEPROMは24LC256を使用しました。
秋月電子で100円でした。
1枚の画像データのサイズは128x64=8,192bit(1,024Byte)なので、32KByteの24LC256には最大32枚セットしておくことができます。

今回、画像データはフリー素材の馬が走っているアニメーションgifから作りました。
アニメーションgifをこちらのサイトで1枚づつに分解しておきます。

ysklog.net

分解した画像を ~その3~ で使用したgimpで128x64にリサイズしてpbmファイルに変換します。

そして、EEPROMへ書き込むにはHEXフォーマットにしておく必要があるので、スクリプトを通してpbmファイルをHEXファイルに変換します。


引数にpbmファイルとEEPROMへ格納するアドレスを16進数で指定します。
1枚当たり1,023Byteなので、0x400番地単位でピッタリ収まります。
使用したアニメーションgifは8枚で構成されていました。

$ ./pbm2hex.pl horse0.pbm 0000 >  eeprom.hex
$ ./pbm2hex.pl horse1.pbm 0400 >> eeprom.hex
$ ./pbm2hex.pl horse2.pbm 0800 >> eeprom.hex
$ ./pbm2hex.pl horse3.pbm 0c00 >> eeprom.hex
$ ./pbm2hex.pl horse4.pbm 1000 >> eeprom.hex
$ ./pbm2hex.pl horse5.pbm 1400 >> eeprom.hex
$ ./pbm2hex.pl horse6.pbm 1800 >> eeprom.hex
$ ./pbm2hex.pl horse7.pbm 1c00 >> eeprom.hex

HEXファイルをFTPからWindowsに移してAKI-PICプログラマー Ver.4を使って24LC256へ書き込みました。

f:id:mohran:20180611212709j:plain
f:id:mohran:20180611213250j:plain


本当はPICKit3を使ってMPLAB IPEからオンボードで書き込みたかったのですが、どうやら24LC256は対応していないようでした。
24LC256を選択できるのに転送できない。
赤丸は非対応と言う意味を理解するのに2時間ほどかかりました(笑)

f:id:mohran:20180611214549j:plain

そこで、旧版のPICKit3プログラマーを使おうとしたらPICKit3のファームウェアを書き換えないと使えないようでした。
しかしそのファームウェアはPIC16F18326には非対応なので、IPEを使うにはまたファームウェアを書き換えてと・・。
あまりにも面倒なので諦めてAKI-PICプログラマーを使うことにしたのです(笑)

回路とプログラムはこのようにしました。

f:id:mohran:20180611215031p:plain


1画面分の画像データをEEPROMからリードして内部変数に保持しておきOLEDへ送信します。
送信後は次の画面へと繰り返します。
I2Cバスのスピードは400KHzで毎フレーム全画素を書き換えていますが、モタつくことも無くスムーズに表示されました。

動画はこちらです。

OLED 0.96インチで遊ぶ ~その5~

今回はPICから表示を行ってみます。

PICは秋月電子で購入したPIC16F18326を使用しました。
プログラム領域が多くて高機能、それでいて低価格(130円)という大変素晴らしいデバイスです。

部品はブレッドボードに実装しました。
RC0とRC1をI2Cバスとして使用してOLEDに接続しています。
I2Cのプルアップ抵抗は1KΩにしました。

回路図はこちらです。

f:id:mohran:20180606232928p:plain


続いてプログラムです。
クロックは内部発振の8MHzにして、I2Cクロックは20分周してSSD1306が動作できる最速の400KHzにしています。

#include <pic.h>
#include <htc.h>
#include <pic16f18326.h>

// クロックサイクル
#define _XTAL_FREQ  8000000

// I2Cデバイスアドレス
#define DEV_OLED_W  0x78  // 0111_1000

// ========================================
//  Config 1 ~ 4
// ========================================
#pragma config  FCMEN    = OFF     // クロックモニタ
#pragma config  CSWEN    = ON      // クロック切り替え(NOSC & NDIV有効)
#pragma config  CLKOUTEN = OFF     // クロック出力
#pragma config  RSTOSC   = HFINT1  // クロック選択(HFINTOSC(1MHz))
#pragma config  FEXTOSC  = OFF     // 外部発振
#pragma config  DEBUG    = OFF     // デバッグモード
#pragma config  STVREN   = OFF     // スタック異常リセット
#pragma config  PPS1WAY  = OFF     // PPSロック
#pragma config  BORV     = HIGH    // Brown-out Reset電圧設定
#pragma config  BOREN    = OFF     // Brown-out Resetイネーブル
#pragma config  LPBOREN  = OFF     // Low-Power Brown-out Resetイネーブル
#pragma config  WDTE     = OFF     // ウオッチドッグタイマ イネーブル
#pragma config  PWRTE    = ON      // Power-up Timer イネーブル(電源ONから64ms後にプログラムを開始)
#pragma config  MCLRE    = OFF     // Master Clear イネーブル
#pragma config  LVP      = OFF     // 低電圧プログラム
#pragma config  WRT      = OFF     // プログラムメモリ プロテクト
#pragma config  CPD      = OFF     // EEPROM プロテクト
#pragma config  CP       = OFF     // コード プロテクト


// ========================================
//  関数プロトタイプ
// ========================================
void i2c_idle(void);
void i2c_start(void);
void i2c_stop(void);
void i2c_send(unsigned char);
unsigned char i2c_recv(void);
void i2c_sack(void);
void i2c_snack(void);

void oled_init(void);
void oled_display(unsigned char);


// ========================================
//  プログラム メイン
// ========================================
void main(void){
  OSCCON1 = 0b01100000;  // NOSC=HFINTOSC(1MHz),NDIV=1
  OSCFRQ  = 0b00000100;  // HFFRQ=8MHz

  TRISA   = 0b00000000;
  TRISC   = 0b00000011;  // RC 0(SCL1),1(SDA1)
  PORTA   = 0b00000000;
  PORTC   = 0b00000000;
  LATA    = 0b00000000;
  LATC    = 0b00000011;
  ANSELA  = 0b00000000;
  ANSELC  = 0b00000000;

  SSP1CLKPPS = 0x10;  // RC0 - 入力先 = SCL
  SSP1DATPPS = 0x11;  // RC1 - 入力先 = SDA
  RC0PPS     = 0x18;  // RC0 - 出力源 = SCL
  RC1PPS     = 0x19;  // RC1 - 出力源 = SDA

  // I2C設定
  SSP1ADD  = 0b00000100;
  SSP1CON1 = 0b00101000;  // [7] WCOL    = 0    : ライト衝突無し
                          // [6] SSP1OV  = 0    : 受信オーバーフロー無し
                          // [5] SSP1EN  = 1    : SDA, SCL有効
                          // [4] CKP     = 0    : クロック極性(未使用)
                          // [3:0] SSP1M = 1000 : I2Cマスターモード クロック=FOSC/(4*(SSP1ADD+1))
                          //                                                 8MHz / (4*(4+1)) = 400KHz

  SSP1STAT = 0x00;
  INTCON   = 0x00;  // 割り込み無効

  __delay_ms(100);

  oled_init();

  while(1){
    oled_display(0x01);
    oled_display(0x02);
    oled_display(0x04);
    oled_display(0x08);
    oled_display(0x10);
    oled_display(0x20);
    oled_display(0x40);
    oled_display(0x80);
  }
}


// ========================================
//  I2C IDLE
// ========================================
void i2c_idle(void){
  while(SSP1CON2bits.SEN   |
        SSP1CON2bits.PEN   |
        SSP1CON2bits.RCEN  |
        SSP1CON2bits.ACKEN |
        SSP1STATbits.R_nW){}
}

// ========================================
//  I2C スタート送信
// ========================================
void i2c_start(void){
  i2c_idle();
  SSP1CON2bits.SEN = 1;      // スタート出力
  while(SSP1CON2bits.SEN){}  // スタート終了待ち
}

// ========================================
//  I2C ストップ送信
// ========================================
void i2c_stop(void){
  SSP1CON2bits.PEN = 1;      // ストップ出力
  while(SSP1CON2bits.PEN){}  // ストップ終了待ち
  i2c_idle();
}

// ========================================
//  I2C データ送信
// ========================================
void i2c_send(unsigned char data){
  SSP1BUF = data;                // データセット送信開始
  while(SSP1STATbits.BF){}       // 送信終了待ち
  while(SSP1CON2bits.ACKSTAT){}  // ACK返信待ち
  i2c_idle();                    // アイドル待ち
}

// ========================================
//  I2C データ受信
// ========================================
unsigned char i2c_recv(void){
  SSP1CON2bits.RCEN = 1;
  while(SSP1CON2bits.RCEN){}
  return(SSP1BUF);
}

// ========================================
//  I2C Ack送信
// ========================================
void i2c_sack(void){
  SSP1CON2bits.ACKDT = 0;
  SSP1CON2bits.ACKEN = 1;
  while(SSP1CON2bits.ACKEN){}
}

// ========================================
//  I2C NAck送信
// ========================================
void i2c_snack(void){
  SSP1CON2bits.ACKDT = 1;
  SSP1CON2bits.ACKEN = 1;
  while(SSP1CON2bits.ACKEN){}
}

// ========================================
//  OLED初期化
// ========================================
void oled_init(void){
  // 表示モード,アドレスモード指定
  i2c_start();
  i2c_send(DEV_OLED_W);
  i2c_send(0x00);
  i2c_send(0xa0);  // 表示モード設定 左右 - 通常:0xa0 / 逆転:0xa1
  i2c_send(0xc0);  // 表示モード設定 上下 - 通常:0xc0 / 逆転:0xc8
  i2c_send(0x20);  // アドレッシングモード指定
  i2c_send(0x00);  //   → Horizontalモード
  i2c_send(0x21);  // カラム指定
  i2c_send(0x00);  //   → 開始位置(0)
  i2c_send(0x7f);  //   → 終了位置(127)
  i2c_send(0x22);  // ページ指定
  i2c_send(0x00);  //   → 開始ページ(0)
  i2c_send(0x07);  //   → 終了ページ(7)
  i2c_send(0x8d);  // チャージポンプ設定
  i2c_send(0x14);  //   → On
  i2c_send(0xaf);  // 画面表示 On
  i2c_stop();
}

// ========================================
//  OLED表示
// ========================================
void oled_display(unsigned char data){
  i2c_start();
  i2c_send(DEV_OLED_W);
  i2c_send(0x40);
  for(int j=0; j<8; j++){
    for(int i=0; i<128; i++){
      i2c_send(data);
    }
  }
  i2c_stop();
}

XC8でコンパイル後、PICKit3を通して書き込みます。

8本のラインが上から下へスクロールするというシンプルなプログラムです。

PICのMSSPモジュールを使用したことによりI2C通信のプログラムは簡単に実装できました。

C言語によるPICプログラミング大全

f:id:mohran:20180531234626j:image

 

最近、PIC16F1系に触れる機会が多々あったので4月に発売された書籍

C言語によるPICプログラミング大全

を買いました。

ペリフェラルについての情報が一番の目的だったのですが、PICの機能全般やツールについての多くの情報が記載されており、とても役に立っています。

お値段はちょっと高めですが、PICを使う方は手元に置いておいておけば困った時の手助けになるのでは無いでしょうか。

OLED 0.96インチで遊ぶ ~その4~

今回はImagemagickのconvertを使用して文字列をPBMフォーマットに変換して表示を行ってみます。

文字列「柔術」を画像化してみます。

$ convert -font /usr/share/fonts/truetype/takao-gothic/TakaoGothic.ttf \
          -pointsize 32 \
          -extent 64x32 \
          -compress none \
          -monochrome \
          +antialias \
          label:'柔術' \
          bjj.pbm

font : フォント名
pointsize : フォントサイズ
extent : 画像サイズ
label : 文字列
を指定します。
他のオプションはPBMを生成するのに必要なオプションとなります。
フォントは色々と試してみましたがTakaoGothicが一番見やすかったです。

生成されたbjj.pbmを開くとこんな感じで見えます。(見やすいようにデータ間のスペースは削除しています)

f:id:mohran:20180529234158j:plain

指定した位置にPBMファイルを表示するスクリプトを作りましたので、これを通して表示してみます。

#!/usr/bin/perl -w

open(RFP, "<" . $ARGV[0]) || die("Fatal : Can't open file ... $ARGV[0]\n");

$pos_xs = $ARGV[1];
$pos_ys = $ARGV[2];

@pbm = ();

$w = 0;
$h = 0;
$i = 0;
while(<RFP>){
  $l = $_;
  chomp($l);

  $l =~ s/#.*$//;

  if($l eq ''){ next; }

  $i++;
  if($i==1){
    if($l ne 'P1'){ die("Error : It isn't P1 format ...\n"); }
  }elsif($i==2){
    ($width, $height) = split(/\s+/, $l);
  }else{
    $l =~ s/\s+//g;
    foreach $d (split(//, $l)){
      $pbm[$h][$w] = $d;
      $w++;
      if($w==$width){
        $h++;
        $w = 0;
      }
    }
  }
}
close(RFP);

# display set
$cmd = 'i2cset -y 1 0x3c 0x00 0xa0 0xc0 i';
system($cmd);

$pos_xe = $pos_xs + $width - 1;
$pos_ye = $pos_ys + $height / 8 - 1;

$cmd  = 'i2cset -y 1 ';
$cmd .= '0x3c 0x00 ';
$cmd .= '0x20 0x00 ';  # horizontal mode
$cmd .= sprintf("0x21 0x%02x 0x%02x ", $pos_xs, $pos_xe);  # column
$cmd .= sprintf("0x22 0x%02x 0x%02x ", $pos_ys, $pos_ye);  # page
$cmd .= 'i';
system($cmd);

$i = 0;
for($h=0; $h<$height; $h+=8){
  for($w=0; $w<$width; $w++){
    $d = unpack("C", pack("B8", $pbm[$h+7][$w] .
                                $pbm[$h+6][$w] .
                                $pbm[$h+5][$w] .
                                $pbm[$h+4][$w] .
                                $pbm[$h+3][$w] .
                                $pbm[$h+2][$w] .
                                $pbm[$h+1][$w] .
                                $pbm[$h+0][$w]));
    if($i==0){
      $cmd = "i2cset -y 1 0x3c 0x40 ";
    }
    $cmd .= sprintf("0x%02x ", $d);
    $i++;
    if($i==16){
      $i = 0;
      $cmd .= "i";
      system($cmd);
    }
  }
}

if($i != 0){
  $cmd .= "i";
  system($cmd);
}

# display enable
$cmd = 'i2cset -y 1 0x3c 0x00 0x8d 0x14 0xaf i';
system($cmd);


引数に「PBMファイル」「表示開始カラム」「表示開始ページ 」を指定します。

$ ./disp_pbm.pl bjj.pbm 32 2


こんな感じで表示されました。


色々と組み合わせて文字列を表示してみました。

なお、上記のスクリプトはPBMファイルのHeightが8の倍数であることが前提となっていますのでご注意ください。

D-NET & SJJJF TOKYO SPRING

4月は2つの大会に出場しました。

 

1つ目は4月1日に開催されたD-NET

D-NET マスターグラップリングトーナメント 02

です。

4人トーナメントで行われましたので優勝までは2回の山です。

グラップリングの試合は2年ぶり、そして2回目の出場です。

2・2・2づくしのなか5分間ヘロヘロになりながらもなんとか勝つことができました。

嬉しい優勝でした!

f:id:mohran:20180527224133j:image

 

ちなみに今大会に出場するためにパンツを新調しました。

VENUMのカッコイイパンツです(笑)

f:id:mohran:20180527231634j:image

 

 

そして2週間後の4月15日に行われた

SJJJF TOKYO SPRING JIU JITSU CHAMPIONSHIP 2018

は、ワンマッチでした。

スイープで先行するも残り時間30秒でひっくり返されてしまいポイント2-2でタイムアップ。

ASJJFルールなので延長戦です。

前回の延長戦は開始早々に巴投げを食らって負けました。

今回は自分からタックル行くつもりでいました。

が・・

コンバッチと同時に先にタックルされてしまい・・

一度は切ったものの、更なるアタックで倒されてしまいました。

残念銀メダル。

延長戦の勝率は非常に悪くこれで1勝3敗となりました。

なんとも・・。

また次頑張りたいと思います。

f:id:mohran:20180527224138j:image

 

 

OLED 0.96インチで遊ぶ ~その3~

またまた久しぶりの投稿です(笑)
OLEDについて纏めたネタが色々あるので少しづつ上げていこうと思います。

今回は画像ファイルを表示する方法について紹介します。

任意の画像をGIMPで128x64にリサイズして、PBMフォーマットにエクスポートします。

f:id:mohran:20180521230445j:plain

フォーマット指定はASCIIにします。
f:id:mohran:20180521230839j:plain

こんな感じのスクリプトを通して変換→表示を行いました。
(画素値を128x64の配列に取り込んで、縦8列を1Byteにしてラスター順に出力しています)

#!/usr/bin/perl -w

open(RFP, "<" . $ARGV[0]) || die("Fatal : Can't open file ... $ARGV[0]\n");

@pbm = ();

$w = 0;
$h = 0;
$i = 0;
while(<RFP>){
  $l = $_;
  chomp($l);

  $l =~ s/#.*$//;

  if($l eq ''){ next; }

  $i++;
  if($i==1){
    if($l ne 'P1'){ die("Error : It isn't P1 format ...\n"); }
  }elsif($i==2){
    ($width, $height) = split(/\s+/, $l);
  }else{
    foreach $d (split(//, $l)){
      $pbm[$h][$w] = $d; 
      $w++;
      if($w==$width){
        $h++;
        $w = 0;
      }
    }
  }
}
close(RFP);

# display set
$cmd = 'i2cset -y 1 0x3c 0x00 0xa0 0xc0 i';
system($cmd);

# horizontal mode
#    column = 0 ... 127
#    page = 0 ... 7
$cmd = 'i2cset -y 1 0x3c 0x00 0x20 0x00 0x21 0x00 0x7f 0x22 0x00 0x07 i';
system($cmd);

$i = 0;
for($h=0; $h<$height; $h+=8){
  for($w=0; $w<$width; $w++){
    $d = unpack("C", pack("B8", $pbm[$h+7][$w] .
                                $pbm[$h+6][$w] .
                                $pbm[$h+5][$w] .
                                $pbm[$h+4][$w] .
                                $pbm[$h+3][$w] .
                                $pbm[$h+2][$w] .
                                $pbm[$h+1][$w] .
                                $pbm[$h+0][$w]));
    if($i==0){
      $cmd = "i2cset -y 1 0x3c 0x40 ";
    }
    $cmd .= sprintf("0x%02x ", $d);
    $i++;
    if($i==16){
      $i = 0;
      $cmd .= "i";
      system($cmd);
    }
  }
}

# display enable
$cmd = 'i2cset -y 1 0x3c 0x00 0x8d 0x14 0xaf i';
system($cmd);


引数にpbmファイルを指定して実行させます。

$ ./disp128x64.pl miku.pbm

写真ではちょっと見難いですが綺麗に表示できました。

ヒクソン・グレイシー杯2018 & 第12回全日本マスターズ選手権

2018年の柔術コンペティションは2月11日に行われた

JJFJ - ヒクソン・グレイシー杯2018

から始まりました。

会場は、駒沢オリンピック体育館。

広くて綺麗な会場で、海外の試合会場の様な雰囲気でした。

 

試合は、4人トーナメントでした。

お正月に食べ過ぎたのか、体重がビックリするほど増えてしまったので(笑)、階級を1つ上げての挑戦です。

1回戦は開始早々にタックルで倒されて2点リードされてしまいましたが、なんとかガードに戻してスイープ成功。

その後はバックを奪えて試合終了。

チョークを狙ったのですが、極めきれず力を使ってしまいました・・。

15分後、決勝戦開始。

引き込みと同時にタックルを合わせられてしまい、またもや2点先制されてしまいました。

ガードに戻せたのですが、相手選手のベースが強い。

パスアタックを防ぎながら、スイープチャンスを伺いますが上手くいきません。

そのままタイムアップ。

準優勝で銀メダルでした。 

f:id:mohran:20180312234456j:image

その他、ヒクソンさんのミニセミナーなどがあったり、一緒に写真を撮って頂いたりと思い出に残る大会となりました。

ちなみにJJFJ のメダルは毎回ディテールまでしっかりした作りで高級感がありますね。

お金かかっていそうです。

 

そして、2週間後の2月24日に墨田区体育館で行われた

JBJJF - 第12回全日本マスターズ選手権

にも出場して参りました。

4回目のマスターズ挑戦です。

過去3回は、銀・銀・1回戦敗退と優勝は1度もありません。

一度で良いので取ってみたいタイトルなので、本来の階級でエントリーです。

今年のエントリーはなんと、去年と同じ面子の5名となりました。

試合前に今回は減量が上手くいかず苦戦しました。

試合前日に一気に水抜きをしたところ、落としすぎてしまい試合当日はマイナス800グラムという状態・・。

ちなみに相手選手の4名のうち、3名には過去のマスターズで負けています。

残りの1名は昨年の覇者。

このメンバーの中では実質、ワタクシが一番の弱者となります(笑)

1回戦は一昨年の決勝で負けた選手です。

試合開始。

お互い引き込んでWガードになりました。

一昨年は、このWガード20秒のペナルティがお互い3回付きました。

今年は・・。

上からアタック仕掛けちゃおう!

ワタクシが上になってアドバンテージ先制。

相手選手はディープハーフガードの使い手です。

左足に絡み着かれましたが、潜られないように必死に体制を低くしながら左足を抜きにかかります。

潜り狙いと、低い体勢での足抜き。

お互い全力で地味~な攻防です。

結局、そのままジワジワした攻防を5分間続けてタイムアップ。

アドバンテージ差で勝利となりましたが、なんともスッキリしない勝ち方です・・。

とは言ってもいちおうリベンジ成功。

そのまま優勝を狙います。

相手選手は、去年の1回戦の相手選手となりました。

三角締めでタップを取られた選手です。

今回も必殺の三角締めて勝ち上がって来ました。

 

試合開始。

引き込んで下から伺います。

スイープが成功するも下から強烈な脚力で足が絡みついて来ます。

防ぐので精一杯。

スイープされた方がまだマシなので、自分から下になります。

相手選手もあえてスイープされるのを狙っているようにも見えます。

カウンターの三角狙い。

残り時間1分。

体力もまだあるし、なんとか上から防げるかも。

上を取ったところ案の定、怒涛の足技が絡みついて来ました。

甘かった。

防ぎきれずにあっという間に捕まってしまいました。

なんとか状態を起こして三角締めは絶えましたが、残り時間20秒。

腕十字に切り替えられたところでタップ。

またもや完敗・・。

銀メダル。

どうしようも無いほどの完敗でしたが、またコツコツ頑張って、来年のマスターズにも挑戦しようと思います!

f:id:mohran:20180312234500j:image

そして、今回は無理な減量が影響したのか次の日から高熱が・・。

インフルエンザA型との診断でした(涙)

しばらく会社も練習も休むことになってしまい、ヒクソン杯と同様に忘れられない大会となりました(笑)

普段の節制が大切だと身をもって痛感しました。