動作環境
ホストOS: Ubuntu 18.04 LTS
ボード: STM32F769 Discovery Kit (以下、STM32F769)
RTOS: Zephyr 2.1.0-rc1
キャラクタLCD: AQM0802
概要
- Zephyr + STM32F769でのI2C送信の練習
- AQM0802に文字を書く
- AQM0802 @ 秋月電子
- 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 > ユニバーサル基板実装
- ピン
- 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) : 黄
ブレッドボードで以下のように実装した。
実行手順
-
- STM32F769をUSB接続しておく
- ST-LinkのUSB接続
-
- プログラムをビルド
cd ~/Zephyr_191116/zephyrproject/
west build -p auto -b stm32f769i_disco samples/drivers/wrk_AQM0802_191125
-
- プログラム書き込み
west flash
に以下のように「Hello」が表示される。
ソース
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を移植した。
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で本格利用する時には整備するだろう