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のバスのスピード。
の中から選ぶ。
interrupt_priority は割り込みの優先順位。
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++;
};
}
コメント
コメントを投稿