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」カテゴリの記事
- 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)
- NeoPixel(WS2812B)の制御 その5(2024.09.15)
「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分