STM HAL I2C SSD1306

OLEDの表示器。SDD1306のドライバICを搭載しているもの。アマゾン等でも安く調達する事が可能。I2C接続で、Aleksanderさんのドライバーを使ってOLEDを過去に同じ内容を掲載している。

https://funasover.blogspot.com/2021/05/stm32ssd1306-oled128.html

 少しスクロールができるライブラリーを用いてみた。

https://controllerstech.com/oled-display-using-i2c-stm32/

基本、ここで解説しなくても、上記のリンクの内容を実施してみたというだけですが、これまで文字だけしか表示した事がなかったが、よりグラフィカルなものを実現しようというのが目的。


【回路の形成】
一度、IDEを立ち上げ、Connectivity のI2C1を選択、右上のI2CのモードをI2Cを選択。




下のI2C Speed ModeをFast Modeを選択する。I2C Clock speed(Hz)を400000にする。(400kHz).
PB7が、SDAとして、PB6がSCLとして緑になっているのが確認できる。
SDAはデータを流し込む側のピンで、SCLはクロックを配信するピン。


これで一度コードを吐き出す所までやってしまう。

配線を先に行ってしまう。ジャンパーワイヤーで飛ばしてもよいが、画面の上にワイヤーが出るのを避けるために、写真のように這わせている。GND、VCCも。
左からGND,VCC,SCL,SDA。
このOLEDのSCLとB6、SDAとB7を接続する。

配線が終わったらブレッドボードにOLEDをそれぞれのピンにあうように装着する。
これで回路自体は終わり。


【ビットマップの準備】
練習として表示させる画像を用意する。
このブログのロゴをやってみる。適当な画像を用意する。ここでは、上を画像を使った。(右クリックしてダウンロードで・・・)

誰もがもっているWindowsのPaintで開く。



2値化して保存をする。別名で保存するを選んで、ファイルの種類でモノクロビットマップを選択して適当な名前で保存する。

2値化して保存をしたものをまたWindowsのペイントで開く。

開いたら、”選択”の横に”サイズ変更”があるのでクリックする。

ピクセルにチェックを入れて、ここで、元画像のうち水平128垂直64のうち画像が大きくとれる方の軸の数字を128か64とする。この例では、元画像のアスペクトが横が画面より短いので縦を64に。

ペイントのキャンパスの大きさを調整して、128x64のピクセルサイズになるように調整する。大きくつぶれている所などは、ここで修正をかける。
(別に横を128にする必要はない。メモリサイズを小さくする観点でも)

この画像を適当な名前でビットマップ形式で保存する。
fun_logo_mono128x64.bmp

これをマイコンが読める形にするが、手でやってられないので、
便利なソフトを使わせてもらう。

https://lcd-assistant.software.informer.com/1.0/

解凍ずると実行形式が入っているので、立ち上げる。
Fileからファイルで2値化しサイズを落としたファイルを読み込む。
設定としてHolizontalにチェックを入れる(SSD1306は横に掃引して画像を描画するから)。またBIG側にチェックをいれる。
Big Endian(ビッグ エンディアン), Little Endian(リトル エンディアン) は、数値の格納方法の指定。Big Endianでは先頭のバイトに上位桁を格納し、little endian では先頭のバイトに下位桁を格納する形式。Big Endianをチェックする。
File-Save outputでファイル名を拡張子も付けて、”fun_logo.h”などの名前で保存する。

【SSD1306用のライブラリーの準備】
を覗いて、下の方にダウンロードボタンがあるので押してZIPをダウンロード、展開する。

・・・・・・¥oled\Incの下にあるヘッダーファイルのうち
fonts.h
ssd1306.h
をIDEのProjectフォルダのCore-Incの下にコピーする。コピーするか?リンクするか聞かれるのでコピーをする。
128x64のOLEDを今回は利用しているが、128x32のものを使う等の時は、ssd1306.hを開いて必要箇所を編集する。I2Cのアドレスも必要があれば変更する。(大抵購入してきたものならそのままでOK)





・・・・・・¥oled\Srcの下にあるヘッダーファイルのうち
fonts.c
ssd1306.c
をIDEのProjectフォルダのCore-Srcの下にコピーする。

今回は、STM32F103ではI2Cが2つ選べてI2C1を選んでいるので、ハンドラーがhi2c1になっている。そのため未編集で動作するが、I2C2を選んでいる場合は、ssd1306.cの中でハンドラーの名前を編集する。





ここで、自分で作製したビットマップデータの保存したファイルをIDEのプロジェクトのIncフォルダの下にドラックする。コピーするか?リンクするか聞かれるのでコピーをする。
これを開いてみると、中身が確認できて、配列fun_logo_mono128x64で画像のデータが格納されている事が確認できる。



【コードの記述:ビットマップの表示】


インクルードファイルを定義する。
#include "fonts.h"
#include "ssd1306.h"
#include "fun_logo.h"

続いて、whileの外の部分で、
  /* USER CODE BEGIN 2 */
  SSD1306_Init();  // initializing

初期設定のコマンドを配置する。これはSSD1306のOLEDを使う場合はおまじないで必ず最初に配置する。

whileの下には、以下のようなコードをかく。
 
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

  SSD1306_DrawBitmap(0,0,fun_logo_mono128x64, 128, 64, 1);
  SSD1306_UpdateScreen();
  HAL_Delay(2000);
 //right
  SSD1306_ScrollRight(0x00, 0x0f);   
  HAL_Delay (2000);
//left
  SSD1306_ScrollLeft(0x00, 0x0f);  
  HAL_Delay (2000);
//diagonal right
  SSD1306_Scrolldiagright(0x00, 0x0f);  
  HAL_Delay (2000);
//diagonal left
  SSD1306_Scrolldiagleft(0x00, 0x0f); 
  HAL_Delay (2000);
 // stop scrolling.
  SSD1306_Stopscroll();  
 // invert
  SSD1306_InvertDisplay(1);  

  HAL_Delay(2000);
 // nonmale
  SSD1306_InvertDisplay(0); 
  HAL_Delay(2000);

ここで使っている関数は、基本的には直感で引数は理解できるので説明は割愛する。
ここまでできたらビルトしてエラーがない事が確認できたら書き込みしてみる。


この写真のように、表示されれば成功。画面に作った絵が出るというのはちょっと感動する。

【コードの記述:テキストの表示】

ビットマップのコードの下に、テキストを表示させてみる。
(ここで数値をいれる際はsprintfを使ってバッファーへ流し込んであげて・・・とするのはUSBの所でやったのと同じ。floatを表示するならチェックをいれるのも忘れずに)

  /*text*/
  SSD1306_GotoXY (0,0);
  SSD1306_Puts ("Hello", &Font_11x18, 1);
  SSD1306_GotoXY (10, 30);
  SSD1306_Puts ("World:)", &Font_11x18, 1);
  SSD1306_UpdateScreen(); 
  HAL_Delay (2000);

           // scroll
           SSD1306_ScrollRight(0,7);  
  HAL_Delay(2000);  // 2 sec

  SSD1306_ScrollLeft(0,7);  
  HAL_Delay(2000);  // 2 sec

  SSD1306_Stopscroll();
  SSD1306_Clear();
  HAL_Delay(2000);

【コードの記述:線、四角、円】

線や、四角や円を書いてみる。AdafruitのArduino用のライブラリにあるテスト関数の一部を作ってみた。

/* USER CODE BEGIN 4 */
void testdrawline() {
  int16_t i;

  SSD1306_Clear(); // Clear display buffer
//SSD1306_DrawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, SSD1306_COLOR_t c)

  //-------------------------------------
  SSD1306_Clear(); // Clear display buffer

   for(i=0; i<SSD1306_WIDTH; i+=4) {
     SSD1306_DrawLine(0, 0, i, SSD1306_HEIGHT-1, 1);
     SSD1306_UpdateScreen(); // Update screen with each newly-drawn line
     HAL_Delay (1);
   }
   for(i=0; i<SSD1306_HEIGHT; i+=4) {
     SSD1306_DrawLine(0, 0, SSD1306_WIDTH-1, i, 1);
     SSD1306_UpdateScreen();
     HAL_Delay (1);
   }
   HAL_Delay (250);

   SSD1306_Clear();

   for(i=0; i<SSD1306_WIDTH; i+=4) {
     SSD1306_DrawLine(0, SSD1306_HEIGHT-1, i, 0, 1);
     SSD1306_UpdateScreen();
     HAL_Delay (1);
   }
   for(i=SSD1306_HEIGHT-1; i>=0; i-=4) {
     SSD1306_DrawLine(0, SSD1306_HEIGHT-1, SSD1306_WIDTH-1, i, 1);
     SSD1306_UpdateScreen();
     HAL_Delay (1);
   }
   HAL_Delay (250);

   SSD1306_Clear();

   for(i=SSD1306_WIDTH-1; i>=0; i-=4) {
     SSD1306_DrawLine(SSD1306_WIDTH-1, SSD1306_HEIGHT-1, i, 0, 1);
     SSD1306_UpdateScreen();
     HAL_Delay (1);
   }
   for(i=SSD1306_HEIGHT-1; i>=0; i-=4) {
     SSD1306_DrawLine(SSD1306_WIDTH-1, SSD1306_HEIGHT-1, 0, i, 1);
     SSD1306_UpdateScreen();
     HAL_Delay (1);
   }
   HAL_Delay (250);

   SSD1306_Clear();

   for(i=0; i<SSD1306_HEIGHT; i+=4) {
     SSD1306_DrawLine(SSD1306_WIDTH-1, 0, 0, i, 1);
     SSD1306_UpdateScreen();
     HAL_Delay (1);
   }
   for(i=0; i<SSD1306_WIDTH; i+=4) {
     SSD1306_DrawLine(SSD1306_WIDTH-1, 0, i, SSD1306_HEIGHT-1, 1);
     SSD1306_UpdateScreen();
     HAL_Delay (1);
   }

   HAL_Delay(2000); // Pause for 2 seconds
 }


//void SSD1306_DrawRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, SSD1306_COLOR_t c)
 void testdrawrect(void) {
   SSD1306_Clear();

   for(int16_t i=0; i<SSD1306_HEIGHT/2; i+=2) {
SSD1306_DrawRectangle(i, i, SSD1306_WIDTH-2*i, SSD1306_HEIGHT-2*i, 1);
     SSD1306_UpdateScreen(); // Update screen with each newly-drawn rectangle
     HAL_Delay (1);
   }

   HAL_Delay(2000);
 SSD1306_Clear();
 }

 void testfillrect(void) {


 void testdrawcircle(void) {
   SSD1306_Clear();

   for(int16_t i=0; i<SSD1306_WIDTH/2; i+=2) {
SSD1306_DrawCircle(SSD1306_WIDTH/2, SSD1306_HEIGHT/2, i, 1);
SSD1306_UpdateScreen();
     HAL_Delay(1);
   }

   HAL_Delay(2000);
 SSD1306_Clear();

 }

/* USER CODE END 4 */

プライベート関数のプロトタイプ宣言の所に、作った関数を追記する。

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);

void testdrawline(void);
void testdrawrect(void);
void testdrawcircle(void);

whileの中で、さっき作ったテキストの所の後ろに、
  testdrawline();
  testdrawrect();
  testdrawcircle();

を追加してやる。
ビルトしてエラーがなければ書き込みを行う。線、四角や円がかけていればOK。
他にも関数がライブラリに用意されているが、そこはヘッダーファイルを読むなりしてもらって。










コメント

このブログの人気の投稿

Attiny85とAQM0802A(LCD)のI2C接続

CH9329で日本語キーボード109で正しく表示する方法