Zephyr + STM32F769 Discovery Kit  > I2C > AQM0802に「Hello」と表示するまで > v0.1

```txt:動作環境
ホストOS: Ubuntu 18.04 LTS
ボード: STM32F769 Discovery Kit (以下、STM32F769)
RTOS: Zephyr 2.1.0-rc1
キャラクタLCD: AQM0802
```

## 概要

- Zephyr + STM32F769でのI2C送信の練習
- AQM0802に文字を書く
    - [AQM0802 @ 秋月電子](http://akizukidenshi.com/catalog/g/gP-06669/)
    - ESP8266で実装したコードを移植する
        - thanks to @exabugs さん (2016年にESP8266の実装時に参考にさせていただきました)

## Zephyrの準備

i2c_scannerを`samples/drivers/wrk_AQM0802_191125`にディレクトリコピーした。
その後、実装をAQM0802用に書き換えた。

- proj.confの変更
- libAQM0802.cの追加
- libAQM0802.hの追加
- main.cの書き換え

```proj.conf
CONFIG_PRINTK=y
CONFIG_I2C=y
CONFIG_SERIAL=y
```

main.c, libAQM0802.c,.hについては後述する (ソースが長いため)。

## 回路

以下に実装した基板を使った。

- [電子工作 > AQM0802 > ユニバーサル基板実装](https://qiita.com/7of9/items/bf5a653d1925cbb38569)
    - ピン
        - 1: VCC
        - 2: VCC
        - 3: GND
        - 4: SDA
        - 5: SCL
        - 6: VCC

以下のように接続した。

- AQM0802 <-> STM32F769
    - VCC <-> CN11 ピン4 (3V3) : 赤
    - GND <-> CN9 ピン7 (GND) : 緑
    - SDA <-> CN9 ピン9 (SDA) : 青
    - SCL <-> CN9 ピン10 (SCL) : 黄

ブレッドボードで以下のように実装した。

![IMG_1384.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/16982a97-b0a0-b8e8-3836-9b0f5c6c26bd.jpeg)



## 実行手順

- 1. STM32F769をUSB接続しておく 
    - ST-LinkのUSB接続
- 2. プログラムをビルド 
    - `cd ~/Zephyr_191116/zephyrproject/`
    - `west build -p auto -b stm32f769i_disco samples/drivers/wrk_AQM0802_191125`
- 3. プログラム書き込み 
    - `west flash`

に以下のように「Hello」が表示される。


![IMG_1382.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/19c7545a-d0d7-5755-865e-a16230e4600e.jpeg)


## ソース

### main.c

```main.c
/*
 * Copyright (c) 2018 Tavish Naruka <tavishnaruka@gmail.com>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <errno.h>
#include <zephyr.h>
#include <sys/printk.h>
#include <device.h>
#include <drivers/i2c.h>

// below added by user (7of9)
#include <stdint.h>
#include "libAQM0802.h"

#ifdef ARDUINO_I2C_LABEL
#define I2C_DEV ARDUINO_I2C_LABEL
#else
//#define I2C_DEV "I2C_0"
#define I2C_DEV "I2C_1" // Nov. 25, 2019 for STM32F769I_disco
#endif

/*
Nov. 26, 2019
	- add [AQM0802] library from ESP8266 implementation
	- branched from [i2c_scanner]
*/

void main(void)
{
	struct device *i2c_dev;
	u8_t cnt = 0;

	uint8_t msgs[10] = "hello";
	int msgLen = strlen(msgs);

	AQM0802_Initialize(/* cursorOn=*/true, /* cursorPosOn=*/true, /* contrast=*/2);
	AQM0802_PutMessage(msgs, msgLen, 1, 1);
}
```

### libAQM0802.h

```libAQM0802.h
#ifndef LIBAQM0802_H
#define LIBAQM0802_H

#include <stdint.h>

void AQM0802_Initialize(bool cursorOn, bool cursorPosOn, uint8_t contrast);
void AQM0802_PutMessage(uint8_t *msgPtr, int msgLen, uint8_t x_st1, uint8_t y_st1);

#endif
```

### libAQM0802.c

Hackaday用にESP8266実装した[esp8266_160217_AQM0802.ino](https://github.com/yasokada/esp8266_160220_BYOP/blob/master/esp8266_160217_AQM0802.ino)を移植した。

- [Bring your own PiZero: BYOP](https://hackaday.io/project/9526-bring-your-own-pizero-byop)


```libAQM0802.c
#include <stdint.h>
#include <stdlib.h>
#include <zephyr.h>
#include <device.h>
#include <drivers/i2c.h>
#include <sys/printk.h>

/*
v0.1 2019 Nov. 28
	- add libAQM0802.h
	- change i2c write function to those for Zephyr+I2C

===== migrated from [esp8266_160217_AQM0802.ino] to [libAQM0802.c] =====

 * v0.4 2016 Feb. 20
 *  - add [kOffset_addr2ndline]
 *  - update AQM0802_PutMessage() for auto wrapping (more than 8 characters)
 * v0.3 2016 Feb. 18
 *  - add message display function
 *    + add Test_AQM0802_showDateTime()
 *    + add AQM0802_PutMessage()
 * v0.2 2016 Feb. 17
 *  - add test functions()
 *    + add Test_AQM0802_cursorOn_posOn_contrastHigh()
 *    + add Test_AQM0802_cursorOn_posOn_contrastLow()
 *    + add Test_AQM0802_cursorOn_posOff_contrastLow()
 * v0.1 2016 Feb. 17
 *  - add AQM0802_Initialize()
 *  - add AQM0802_Clear()
 *  - add AQM0802_WriteData()
 *  - add AQM0802_WriteInstruction()
 *  - add writeToDevice()
 *
 * -------
 * Special thanks to exabugs for ( http://qiita.com/exabugs/items/9d1b66aa1d22805afbc8 )
 */

#ifdef ARDUINO_I2C_LABEL
#define I2C_DEV ARDUINO_I2C_LABEL
#else
//#define I2C_DEV "I2C_0"
#define I2C_DEV "I2C_1" // Nov. 25, 2019 for STM32F769I_disco
#endif

static const uint8_t kDeviceAddr = 0x3e;

static const int kMaxXsize = 8;
static const int kMaxYsize = 2;

static const int kOffset_addr2ndline = 0x40;

static uint8_t ControlByteList[] = {
  0x00, // Instruction write operation. ( Co=0, Rs=0 )
  0x40, // Data write operation. ( Co=0, Rs=1 )
};

enum {
  TYPE_INSTRUCITON = 0,
  TYPE_DATA,
};

// private functions ---------------------------
void writeToDevice(int type, uint8_t *dataByte, size_t len) 
{
	struct device *i2c_dev;
	struct i2c_msg msgs[1];
	u8_t dst[10];

	i2c_dev = device_get_binding(I2C_DEV);

	/* Send the address to read from */

	for (int idx = 0; idx < len; idx++) {
		// [ESP8266 + I2C]
		// Wire.beginTransmission(kDeviceAddr);
		// Wire.write(ControlByteList[type]);
		// Wire.write(dataByte[idx]);
		// Wire.endTransmission();
		// delayMicroseconds(27); // 26.3us
		//
		// [Zephyr + STM32F769I_Discovery]
		dst[0] = ControlByteList[type];
		dst[1] = dataByte[idx];
		msgs[0].buf = &dst[0];
		msgs[0].len = 2U;
		msgs[0].flags = I2C_MSG_WRITE | I2C_MSG_STOP;
		i2c_transfer(i2c_dev, &msgs[0], 1, kDeviceAddr);
		k_usleep(27); // 26.3us
	}
}

// public functions ---------------------------
void AQM0802_WriteSingleInstruction(uint8_t data) 
{ 
  size_t len = 1;
  uint8_t list[] = {data};
  writeToDevice(TYPE_INSTRUCITON, list, len); 
}

void AQM0802_WriteInstructions(uint8_t *data, int len) 
{ 
  writeToDevice(TYPE_INSTRUCITON, data, len); 
}

void AQM0802_WriteData(uint8_t *data, size_t len) 
{ 
  writeToDevice(TYPE_DATA, data, len); 
}

void AQM0802_Clear()
{
    AQM0802_WriteSingleInstruction(0x01);
}

void AQM0802_Initialize(bool cursorOn, bool cursorPosOn, uint8_t contrast)
{ 
  // 
  k_sleep(40); // delay(40); // Wait time > 40ms after VDD stable

  // Function set
  AQM0802_WriteSingleInstruction(0x38);

  // Function set
  AQM0802_WriteSingleInstruction(0x39);

  // Internal OSC frequency
  AQM0802_WriteSingleInstruction(0x14);

  // { Contrast set -----------------------------
  uint8_t ctrst = contrast;
  if (ctrst > 0b1111) {
    ctrst = 0b1111;
  }
  AQM0802_WriteSingleInstruction(0x70 | ctrst);
  // } Contrast set -----------------------------

  // Power/ICON/Contrast control
  AQM0802_WriteSingleInstruction(0x56);

  // Follower control
  AQM0802_WriteSingleInstruction(0x6C);

  // Wait time > 200mS (for power stable)
  k_sleep(200); // delay(200);

  // Function set
  AQM0802_WriteSingleInstruction(0x38);

  // { Display ON/OFF control -----------------------
  uint8_t extra = 0x0;
  if (cursorOn) {
    extra = extra | 0b10;
  }
  if (cursorPosOn) {
    extra = extra | 0b11;    
  }
  AQM0802_WriteSingleInstruction(0x0C | extra);
  // } Display ON/OFF control -----------------------

  // Clear Display
  AQM0802_WriteSingleInstruction(0x01);

  // Wait time > 1.08ms
  k_sleep(2); // delay(2);
}

#if 1 // 2019/11/26
void AQM0802_PutMessage(uint8_t *msgPtr, int msgLen, uint8_t x_st1, uint8_t y_st1)
{
  // _st1 : index starting from 1
  if ((x_st1 > kMaxXsize) || (y_st1 > kMaxYsize)) {
    return; // error
  }

  uint8_t pos;

  if (msgLen <= kMaxXsize) {
    pos = 0x80 | ((y_st1 - 1) * kOffset_addr2ndline);
    pos = pos | (x_st1 - 1);
    AQM0802_WriteSingleInstruction(pos);
    AQM0802_WriteData( (uint8_t *)msgPtr, msgLen);
    return;
  }

}
#else // work in progress 
void AQM0802_PutMessage(String msg, uint8_t x_st1, uint8_t y_st1)
{
  // _st1 : index starting from 1
  if ((x_st1 > kMaxXsize) || (y_st1 > kMaxYsize)) {
    return; // error
  }

  uint8_t pos;

  if (msg.length() <= kMaxXsize) {
    pos = 0x80 | ((y_st1 - 1) * kOffset_addr2ndline);
    pos = pos | (x_st1 - 1);
    AQM0802_WriteSingleInstruction(pos);
    AQM0802_WriteData( (uint8_t *)msg.c_str(), msg.length() );
    return;
  }

  char szbuf[10] = { 0 };
  // 1st line
  strncpy(szbuf, msg.c_str(), kMaxXsize);
  pos = 0x80 | ((y_st1 - 1) * kOffset_addr2ndline);
  pos = pos | (x_st1 - 1);
  AQM0802_WriteSingleInstruction(pos);
  AQM0802_WriteData( (uint8_t *)szbuf, strlen(szbuf) );

  // 2nd line
  msg = msg.substring(kMaxXsize); // remove string for 1st line
  memset(szbuf, 0, sizeof(szbuf));
  strncpy( szbuf, msg.c_str(), msg.length() );
  pos = 0x80 | ((y_st1 - 1) * kOffset_addr2ndline);
  pos += kOffset_addr2ndline; // for 2nd line, 1st column
//  pos = pos | (x_st1 - 1);  
  AQM0802_WriteSingleInstruction(pos);
  AQM0802_WriteData( (uint8_t *)szbuf, strlen(szbuf) );
}
#endif


//---------------------------------------------------------------------------------

// test functions ---------------------------

#if 1 // 2019/11/26

#else // work in progress
void Test_AQM0802_cursorOn_posOff_contrastLow()
{
  AQM0802_Initialize(/* cursorOn=*/true, /* cursorPosOn=*/false, /* contrast=*/1);
}

void Test_AQM0802_cursorOn_posOn_contrastLow()
{
  AQM0802_Initialize(/* cursorOn=*/true, /* cursorPosOn=*/true, /* contrast=*/1);
}

void Test_AQM0802_cursorOn_posOn_contrastHigh()
{
  AQM0802_Initialize(/* cursorOn=*/true, /* cursorPosOn=*/true, /* contrast=*/15);
}

void Test_AQM0802_showDateTime(String datstr, String timstr)
{
  AQM0802_PutMessage(datstr, /* x_st1=*/1, /* y_st1=*/1);
  AQM0802_PutMessage(timstr, /* x_st1=*/3, /* y_st1=*/2);
}
#endif
```

### 備考

- libAQM0802.c, .hはI2C実装の確認用に最低限の実装しかしていない
- 今後AQM0802をZephyrで本格利用する時には整備するだろう