ATtiny13AでI2C接続キャラクタLCDを利用する
以前から試行錯誤していたのですが、やっとArduino IDE環境下のATtiny13Aで
I2Cが使えるようになりました。
取りあえず、キャラクタLCDに文字が表示できました。

内容を理解してやっと使えるようになりました。
参考文献
Coding Laboratory - I2C on an AVR using bit banging
当然、ATtiny13AにはI2Cをサポートしていないのでビットバンディング(エミュレーション)です。
機器によっては動作しない場合もあるでしょう。
開発環境は、以前構築したArduino IDE+ATtiny対応(こちら)です。I
I2C接続LCDモジュールの制御はこちらの成果を利用しています。
ATtiny13Aの書き込みはこちらの成果(USBasp)を利用しています。
プログラムのサイズは872バイトです。
プログラマ接続で4ピンを使っているので余っている3番ピン、4番ピンを
I2C接続用に利用してLCDを制御しています。

通信速度はちょっと遅い感じです。
ロジックアナライザで調べると1.6Kbps程度しかでていません。
これでもdelay時間をいじってオリジナルよりは速くなってます。
その為、他のCPUで利用する場合はタイミングの調整が必要かもしれません。
ちなみに、arduinoのI2C用ライブラリWireを使う場合、転送速度は100kbpsだそうです。
全ソース(スケッチ)はこちらに置いておきます。
以下、主要部を掲載します。
#include <avr/io.h>
#include <util/delay.h>
// Port for the I2C
#define I2C_DDR DDRB
#define I2C_PIN PINB
#define I2C_PORT PORTB
// Pins to be used in the bit banging
#define I2C_CLK 3
#define I2C_DAT 4
#define I2C_DATA_HI() I2C_DDR &= ~(1 << I2C_DAT);I2C_PORT |= (1 << I2C_DAT);
#define I2C_DATA_LO() I2C_DDR |= (1 << I2C_DAT);I2C_PORT &= ~(1 << I2C_DAT);
#define I2C_CLOCK_HI() I2C_DDR &= ~(1 << I2C_CLK);I2C_PORT |= (1 << I2C_CLK);
#define I2C_CLOCK_LO() I2C_DDR |= (1 << I2C_CLK);I2C_PORT &= ~(1 << I2C_CLK);
void I2C_WriteBit(unsigned char c) {
if (c > 0) {
I2C_DATA_HI();
} else {
I2C_DATA_LO();
}
I2C_CLOCK_HI();
while ((I2C_PIN & (1 << I2C_CLK)) == 0);
_delay_us(200);
I2C_CLOCK_LO();
_delay_us(200);
if (c > 0) {
I2C_DATA_LO();
}
_delay_us(200);
}
unsigned char I2C_ReadBit() {
I2C_DATA_HI();
I2C_CLOCK_HI();
while ((I2C_PIN & (1 << I2C_CLK)) == 0);
_delay_us(200);
unsigned char c = I2C_PIN;
I2C_CLOCK_LO();
_delay_us(200);
return (c >> I2C_DAT) & 1;
}
// Inits bitbanging port, must be called before using the functions below
//
void I2C_Init() {
I2C_PORT &= ~((1 << I2C_DAT) | (1 << I2C_CLK));
I2C_CLOCK_HI();
I2C_DATA_HI();
}
// Send a START Condition
//
void I2C_Start() {
// set both to high at the same time
I2C_DDR &= ~((1 << I2C_DAT) | (1 << I2C_CLK));
_delay_us(100);
I2C_DATA_LO();
_delay_us(100);
I2C_CLOCK_LO();
_delay_us(100);
}
// Send a STOP Condition
//
void I2C_Stop(){
I2C_CLOCK_HI();
_delay_us(100);
I2C_DATA_HI();
_delay_us(100);
}
// write a byte to the I2C slave device
//
void I2C_Write(unsigned char c) {
for (char i = 0; i < 8; i++){
I2C_WriteBit(c & 0x80);
c <<= 1;
}
}
// read a byte from the I2C slave device
//
unsigned char I2C_Read(unsigned char ack) {
unsigned char res = 0;
for (char i = 0; i < 8; i++) {
res <<= 1;
res |= I2C_ReadBit();
}
if (ack > 0) {
I2C_WriteBit(0);
} else {
I2C_WriteBit(1);
}
return res;
}
LCD制御部のソース
#include "IC2LCD.h"
uint8_t DisplayMode;
uint8_t DisplayControl;
// コマンド送信
void Lcd_writeCmd(uint8_t cmd) {
I2C_Start();
I2C_Write(0xa0);
I2C_ReadBit();
I2C_Write(0x00);
I2C_ReadBit();
I2C_Write(cmd);
I2C_ReadBit();
I2C_Stop();
}
// データ送信
void WriteData(uint8_t data) {
I2C_Start();
I2C_Write(0xa0);
I2C_ReadBit();
I2C_Write(0x80);
I2C_ReadBit();
I2C_Write(data);
I2C_ReadBit();
I2C_Stop();
}
// LCD初期化
void LCD_init() {
_delay_ms(15);
Lcd_writeCmd(0x01);
_delay_ms(5);
Lcd_writeCmd(0x38);
_delay_ms(5);
Lcd_writeCmd(0x0f);
_delay_ms(5);
Lcd_writeCmd(0x06);
_delay_ms(5);
DisplayControl = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
DisplayMode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
}
// カーソル移動
void LCD_setCursor(uint8_t col, uint8_t row){
uint8_t row_offsets[] = { 0x00, 0x40 };
if ( row >= NUM_LINES ) {
row = NUM_LINES - 1;
}
Lcd_writeCmd(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}
// 非表示設定
void LCD_noDisplay() {
DisplayControl &= ~LCD_DISPLAYON;
Lcd_writeCmd(LCD_DISPLAYCONTROL | DisplayControl);
}
// 表示設定
void LCD_display() {
DisplayControl |= LCD_DISPLAYON;
Lcd_writeCmd(LCD_DISPLAYCONTROL | DisplayControl);
}
// カーソル非表示
void LCD_noCursor() {
DisplayControl &= ~LCD_CURSORON;
Lcd_writeCmd(LCD_DISPLAYCONTROL | DisplayControl);
}
// カーソル表示
void LCD_cursor() {
DisplayControl |= LCD_CURSORON;
Lcd_writeCmd(LCD_DISPLAYCONTROL | DisplayControl);
}
// カーソルブリンクなし
void LCD_noBlink() {
DisplayControl &= ~LCD_BLINKON;
Lcd_writeCmd(LCD_DISPLAYCONTROL | DisplayControl);
}
// カーソルブリンク
void LCD_blink() {
DisplayControl |= LCD_BLINKON;
Lcd_writeCmd(LCD_DISPLAYCONTROL | DisplayControl);
}
// 文字列表示
void LCD_string(char *str) {
for(uint8_t i = 0; i < 16; i++) {
if(str[i] == 0x00) {
break;
} else {
WriteData(str[i]);
}
}
}
LCDモジュールの制御のほとんどは、コマンド送信とデータ送信を行う関数
void Lcd_writeCmd(uint8_t cmd);
void WriteData(uint8_t data);
で行っています。
LCDモジュールの資料を見ると、I2C Intaface protocolは次のようになってます。

このプロトコルに従ってコマンド送信は、
// コマンド送信
void Lcd_writeCmd(uint8_t cmd) {
I2C_Start();
I2C_Write(0xa0);
I2C_ReadBit();
I2C_Write(0x00);
I2C_ReadBit();
I2C_Write(cmd);
I2C_ReadBit();
I2C_Stop();
}
こんな感じになっています。図の各部の対応において、先頭かた順番に
Sが I2C_Start()、
SLAVE ADDRESS が I2C_Write(0xa0)、
Acknowledgement from Icmが I2C_ReadBit()、
RS+CONTROL BYTEが I2C_Write(0x00)、
Acknowledgement from Icmが I2C_ReadBit()、
COMMAND OR DATA BYTEが I2C_Write(cmd)、
Acknowledgement from Icmが I2C_ReadBit()、
Pが I2C_Stop()
を処理しています。
ATtiny13AでI2Cが使えると、いろいろを応用できそうです。
ただ、LCD接続ではプログラム領域 1024バイトのうち既に872バイトを消費しており、
後は何もできそうにないです。あと1024バイト欲しいところです。
« Windows 8.1 で署名なしドライバーソフトをインストールする | トップページ | ATtiny13AでI2C接続キャラクタLCDを利用する(2) »
「arduino」カテゴリの記事
- Freenove Mecanum ホイール カー キットを購入しました(2025.05.28)
- Arduino IDE+Arduino STM32環境で指定と異なるgccが使われてしまう(2025.01.23)
- Zorin OSでArduino Uno互換機(CH340)が認識しない(2025.01.19)
- Arduino IDE 2.3.4でArduino STM32を利用する(2025.01.12)
- Arduino用 SKK日本語変換ライブラリの開発 その1(2024.12.28)
「AVR」カテゴリの記事
- NeoPixel(WS2812B)の制御 その5(2024.09.15)
- Arduino用SJIS漢字フォントライブラリ SDカード版を作成しました(2018.10.30)
- ATtiny13AでI2C接続キャラクタLCDを利用する(4)(2018.04.16)
- ATtiny13Aで赤外線リモコン受信センサーを使う(2)(2018.04.15)
- ATtiny13AでHC-SR04を使った距離計測(2018.04.14)
「ATtiny13A」カテゴリの記事
- ATtiny13AでI2C接続キャラクタLCDを利用する(4)(2018.04.16)
- ATtiny13Aで赤外線リモコン受信センサーを使う(2)(2018.04.15)
- ATtiny13AでHC-SR04を使った距離計測(2018.04.14)
- Arduino 1.8.5環境でATtiny13Aを利用する(2018.04.14)
- Arduino IDEを使ったATtiny13用スケッチの開発について(2016.08.30)
「表示器制御関連」カテゴリの記事
- NeoPixel(WS2812B)の制御 その5(2024.09.15)
- Arduino用 美咲フォントライブラリを更新しました(2024.03.21)
- Raspberry Pi Pico(MicroPython)でLEDドットマトリックスを使ってみる(2024.03.14)
- Raspberry Pi Pico MicroPython用のマルチフォントライブラリ(2023.02.09)
- MicroPython(Raspberry Pi pico)で8x8ドットNeoPixcel文字表示(2023.02.08)
コメント
« Windows 8.1 で署名なしドライバーソフトをインストールする | トップページ | ATtiny13AでI2C接続キャラクタLCDを利用する(2) »

たま吉さん、はじめまして。
ATtinyでのI2Cの情報を探していてこちらのブログに辿り着き、早速tinyI2C.inoを使わせていただきました。
そのことをblogにアップし、また今回書いたコードはgithubで公開しています。
http://hine.hatenablog.com/entry/2014/11/09/235938
https://github.com/hine/I2CMatrix
本当によいライブラリの公開ありがとうございます。
blogエントリー、githubのコードともに、もし問題があればすぐに削除いたします。
投稿: hine | 2014年11月10日 (月) 00時11分
hineさん、はじめまして
ご報告ありがとうございます。
blogエントリー、github公開問題なしです。
ブログ拝見しました。
私もこの小っちゃいI2Cマトリックス表示器は「いい!」と思い
中の人(HT16K33)を入手しました。遊んでみようと模索中です。
投稿: たま吉さん(管理者) | 2014年11月10日 (月) 01時06分
たま吉さん
ありがとうございます。I2C機器増えてきたので、
ATtinyでI2Cは流行るんじゃないかと思うんですよね。
HT16K33いいですね!データシート見ると、マトリックスLEDとかも
いいですけど、Keyscanに使えるのも面白そうだと思っています。
今後とも色々参考にさせてください!
投稿: hine | 2014年11月10日 (月) 01時40分
Tiny13でI2Cマスタになる例を探してこちらを拝見しました。
秋月のAQM0802Aがうまく動かず波形を見たところ,どうもStop conditionがうまく出ていないようです。
Stop conditionを出す前に,SCKもSDAもLowにしておく必要がありますが,それが抜けています。
I2C_Stop()の先頭に
I2C_CLOCK_LO();
I2C_DATA_LO();
の2つを追加すると,動くようになります。
ついでに言うと,SCKとSDAが同時に変化する場所もあり,タイミング的にまずいところがあるようですが,相手のデバイスの実力でなんとか動いているような感じです。
投稿: | 2016年3月16日 (水) 08時01分
>I2C_Stop()の先頭に
>
>I2C_CLOCK_LO();
>I2C_DATA_LO();
>
>の2つを追加すると,動くようになります。
ご指摘ありがとうございます。
修正し、動作確認の上記載内容を訂正します。
投稿: たま吉さん(管理者) | 2016年3月16日 (水) 09時42分