nRF I2C SSD1306

I2Cの使い方を勉強していく。

とは言え、表示器は手にいれると、いろいろデバッグにも便利なので、同じみのOLED:SSD1306をちゃんと動かす所から。

【I2C】

XAIOは、以下のようなピンのアサインがあって、P0.04がSDA、P0.05がSCLというように表現されているが、これはArduino環境での事であって、どのピンでもよい。I2CのSDAとSCLに指定する事はできる。

どのピンでもよいとは言ったが、見ると
https://infocenter.nordicsemi.com/pdf/nRF52840_PS_v1.1.pdf

データシートの7 Hardware and layoutに、”Standard drive, low frequency I/O only”とピンによっては記載されている。じゃあこれらのピンは動かないのか?というとそうではなくて、無線に近いピンので、無線機の動作中に標準駆動または低周波以外の使用に設定すると、無線機に干渉する可能性があるためこのタグが付ついている。動かないって訳ではなく、動作が不安定になる事があるらしい・・・。

とは言え、わかりやすいので、上の絵の通りに、SDAにP0.04を、SCLにP0.05を使ってみた。画面の上にワイヤーが来ないように先に写真のように、左からGND,VCC,SCL,SDAの接続し、SSD1306をブレッドボードに装着する。


装着後はこんな感じになる。


【コードの記載】

・SDKから・・\examples\peripheral\twi_scannerを個人のフォルダー(myprojects)にでもコピーする。

コピーしたフォルダのsesの下の

・・・\examples\myprojects\twi_scanner\pca10056\blank\ses

twi_scanner_pca10056.emProject

をダブルクリックしてSESで立ち上げる。

立ち上げたら、同じフォルダのflash_placement.xmlをSES上にドラックして開いて、"size”の問題を対処しておく。(size="0x4"の部分2か所を削除) 


次に、Project ItemsのnRF_Seggar_RTTの下にあるSeggar_RTT_Syscalls_SES.cを削除する。('__vfprintf.h' file not foundとエラーが出る)


とは言え、わかりやすいので、上の絵の通りに、SDAにP0.04を、SCLにP0.05を使ってみた。画面の上にワイヤーが来ないように先に写真のように、左からGND,VCC,SCL,SDAの接続し、SSD1306をブレッドボードに装着する

メインを開いて、思い切って余計な所を削除。



int mainの中は、からのwhileだけ。twi_intという関数、これも決してもよいがどうせ使うので、本当はエラー処理をすべきだがここは割愛。

static const nrf_drv_twi_t m_twi =NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);

の部分だけは残す。

nrf_drv_twi_t は構造体でm_twiとい構造体を NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID)という部分はTWIマスタードライバインスタンスを作成するためのマクロ。TWI_INSTANCE_IDは0か1か。nRF52840はI2C0とI2C1の二つをもっているので、それのどちらを使うかってのを指定する(ものだと思っている・・・)
static const nrf_drv_twi_t m_twi =NRF_DRV_TWI_INSTANCE(0);
としてもOK。

twi_init という関数の中身を見ていく。

void twi_init (void)
{
    const nrf_drv_twi_config_t twi_config = {
       .scl                = 5,
       .sda                = 4,
       .frequency          = NRF_DRV_TWI_FREQ_400K,
       .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
       .clear_bus_init     = false
    };
    nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);

    nrf_drv_twi_enable(&m_twi);
}

nrf_drv_twi_config_t でI2Cの設定する構造体で、twi_config という名前(任意)をつけている。中身は、SCL,SDAのピンの指定、ここでは5、4と数字直接書き換える。
.frequencyはI2Cのバスのスピード。
numerator
NRF_TWI_FREQ_100K 

100 kbps.

NRF_TWI_FREQ_250K 

250 kbps.

NRF_TWI_FREQ_400K 

400 kbps.

の中から選ぶ。
interrupt_priority は割り込みの優先順位。

APP_IRQ_PRIORITY_HIGHEST 

Running in Application Highest interrupt level.

APP_IRQ_PRIORITY_HIGH 

Running in Application High interrupt level.

APP_IRQ_PRIORITY_MID 

Running in Application Middle interrupt level.

APP_IRQ_PRIORITY_LOW_MID 

Running in Application Middle Low interrupt level.

APP_IRQ_PRIORITY_LOW 

Running in Application Low interrupt level.

APP_IRQ_PRIORITY_LOWEST 

Running in Application Lowest interrupt level.

APP_IRQ_PRIORITY_THREAD 

Running in Thread Mode.


clear_bus_init     :初期化中にバスをクリアするか?falseしか例題で見たことがないのでここは不勉強。
twi_config に設定値を格納してあげたら、

ドライバのインスタンスを使用する前に、nrf_drv_twi_init関数で初期化

    nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);

第一引数に、インスタンスをポインタで入れて
第二引数に、設定をいれる。
第三引数は、イベントのハンドラーを入れる所。ここではNULL
第四引数は、コンテキスト・・・となっているがここはよくわからない。
第三引数、第四引数については使い方というのはよくわかってはいないが、NULLで。

初期化後、インスタンスはnrf_drv_twi_enableで有効にする必要
    nrf_drv_twi_enable(&m_twi);

これでI2Cのバスを使う準備はできた。

// Address
#define SSD1306_ADDRESS                0x3C

void ssd1306_command(uint8_t c)
{
        uint8_t dta_send[] = {0x00, c};
        nrf_drv_twi_tx(&m_twi, SSD1306_ADDRESS, dta_send, 2, false);
}

という関数をメインの前にたす。
SSD1306だが、コマンドを送るときは、先に0x00を送って、続いてコマンドを送りつける。データを送る時は、0x40を先に送って、続けてデータを送る。
まず、コマンドを送りつけて初期化をする

nrf_drv_twi_tx
第一引数はインスタンス
第二引数に、アドレスをいれる。このアドレスは以前に説明したとおりで7ビットなので一つシフトさせた値にする。
送るデータの配列として、dta_sendを作ってあげて、一つ目には、0x00、二つ目はcで指定したものにしている。
第三引数は送るデータの配列を入れてあげる
第四引数は、データを何バイト送付するか?でここでは2
最後の引数は、立て続けにデータをこの後おくるか?

ssd1306_commandがこれで用意できた。

void ssd1306Init() {
    ssd1306_command(SSD1306_DISPLAY_OFF);                                   /*0xAE:Entire Display OFF */
    ssd1306_command(SSD1306_SET_DISPLAY_CLOCK_DIV);                         /*0xD5:Set Display Clock Divide Ratio and Oscillator Frequency */
    ssd1306_command(0xF0);                                                  /*0xF0 original 0x80:Default Setting for Display Clock Divide Ratio and Oscillator Frequency that is recommended */
    ssd1306_command(SSD1306_SET_MULTIPLEX_RATIO);                           /*0xA8: Set Multiplex Ratio */
    ssd1306_command(0x3F);
    ssd1306_command(SSD1306_SET_DISPLAY_OFFSET);                            /* 0xD3:Set display offset */
    ssd1306_command(0x00);                                                  /* 0 offset */
    ssd1306_command(SSD1306_SET_START_LINE | 0x00);                         /* Set first line as the start line of the display */
    ssd1306_command(SSD1306_SET_CHARGE_PUMP);                               /* Charge pump */
    ssd1306_command(0x14);                                                  /* Enable charge dump during display on */
    ssd1306_command(SSD1306_MEMORY_ADDRESS_MODE);                           /*Set memory addressing mode */
    ssd1306_command(SSD1306_SET_LCOL_START_ADDRESS);                        /*Horizontal addressing mode */
    ssd1306_command(SSD1306_SEGMENT_REMAP | 0x01);                          /* Set segment remap with column address 127 mapped to segment 0 */
    ssd1306_command(SSD1306_COM_SCAN_INVERSE);                              /* Set com output scan direction, scan from com63 to com 0 */
    ssd1306_command(SSD1306_SET_COM_PINS_CONFIG);                           /* Set com pins hardware configuration */
    ssd1306_command(0x12);
    ssd1306_command(SSD1306_SET_CONTRAST);                                  /* Set contrast control */
    ssd1306_command(0x80);                                                  /* Set Contrast to 128 */
    ssd1306_command(SSD1306_SET_PRECHARGE_PERIOD);                          /* Set pre-charge period */
    ssd1306_command(0xF1);                                                  /* Phase 1 period of 15 DCLK, Phase 2 period of 1 DCLK */
    ssd1306_command(SSD1306_SET_VCOM_DESELECT_LVL);                         /* Set Vcomh deselect level */
    ssd1306_command(0x40);                                                  /* Vcomh deselect level */
    ssd1306_command(SSD1306_ENTIRE_DISPLAY_RESUME);                         /* Entire display ON, resume to RAM content display */
    ssd1306_command(SSD1306_NORMAL_DISPLAY);                                /* Set Display in Normal Mode, 1 = ON, 0 = OFF */
    ssd1306_command(SSD1306_DISPLAY_ON);                                    /* Display on in normal mode */
    }


/**
 * @brief Function for main application entry.
 */

 void setCursor (unsigned char x, unsigned char p) {
    ssd1306_command(SSD1306_SET_LCOL_START_ADDRESS | (x & 0x0F));
    ssd1306_command(SSD1306_SET_HCOL_START_ADDRESS | (x >> 4));
    ssd1306_command(SSD1306_SET_PAGE_START_ADDRESS | p);
}

void fillDisplay(unsigned char param) {
  unsigned char page, x;
  unsigned char *dataBuffer;
  dataBuffer = malloc(129);
  dataBuffer[0] = SSD1306_DATA_MODE;
  for (page = 0; page < 8; page++) {
      setCursor(0, page);
    for (x = 0; x < 128; x++) {
        dataBuffer[x + 1] = param;
    }
    //sendData(&dataBuffer[0], 129);
    nrf_drv_twi_tx(&m_twi, SSD1306_ADDRESS, dataBuffer, 129, false);
    //sendData(dataBuffer, 129);
  }
  free(dataBuffer);
}

void draw6x8Str(unsigned char x, unsigned char p, const char str[],
                        unsigned char invert, unsigned char underline) {
  unsigned char i, j, b, buf[FONT6X8_WIDTH + 1];
  unsigned int c;

  i = 0;
  buf[0] = SSD1306_DATA_MODE; // fist item "send data mode"
  while (str[i] != '\0') {
    if (str[i] > 191)
      c = (str[i] - 64) * FONT6X8_WIDTH;
    else
      c = str[i] * FONT6X8_WIDTH;
    if (x > (SSD1306_WIDTH - FONT6X8_WIDTH - 1))
    {
      x = 0;
      p++;
    };
    if (p > 7) p = 0;
    setCursor(x, p);
    for (j = 0; j < FONT6X8_WIDTH; j++)
    {
      if (underline)
        b = font6x8[(unsigned int)(c + j)] | 0x80;
      else
        b = font6x8[(unsigned int)(c + j)];
      if (invert)
        buf[j + 1] = b;
      else
        buf[j + 1] = ~b;
    };
    //sendData(i2c, &buf[0], FONT6X8_WIDTH + 1);
    nrf_drv_twi_tx(&m_twi, SSD1306_ADDRESS, buf, FONT6X8_WIDTH + 1, false);

    x += FONT6X8_WIDTH;
    i++;
  };
}









コメント

このブログの人気の投稿

Attiny85とAQM0802A(LCD)のI2C接続

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