フォト
2017年9月
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
無料ブログはココログ

« Windows 8.1 で署名なしドライバーソフトをインストールする | トップページ | ATtiny13AでI2C接続キャラクタLCDを利用する(2) »

2014年11月 5日 (水)

ATtiny13AでI2C接続キャラクタLCDを利用する

以前から試行錯誤していたのですが、やっとArduino IDE環境下のATtiny13Aで
I2Cが使えるようになりました。

取りあえず、キャラクタLCDに文字が表示できました。

Dscn3222

まあ、自作というよりはRaulさんのHPで公開しているソースをちょっと修正しただけですが..
内容を理解してやっと使えるようになりました。

参考文献

   Coding Laboratory - I2C on an AVR using bit banging

当然、ATtiny13AにはI2Cをサポートしていないのでビットバンディング(エミュレーション)です。
機器によっては動作しない場合もあるでしょう。

開発環境は、以前構築したArduino IDE+ATtiny対応(こちら)です。I
I2C接続LCDモジュールの制御はこちらの成果を利用しています。
ATtiny13Aの書き込みはこちらの成果(USBasp)を利用しています。

01

プログラムのサイズは872バイトです。
プログラマ接続で4ピンを使っているので余っている3番ピン、4番ピンを
I2C接続用に利用してLCDを制御しています。

Dscn3219

通信速度はちょっと遅い感じです。
ロジックアナライザで調べると1.6Kbps程度しかでていません。

これでもdelay時間をいじってオリジナルよりは速くなってます。
その為、他のCPUで利用する場合はタイミングの調整が必要かもしれません。
ちなみに、arduinoのI2C用ライブラリWireを使う場合、転送速度は100kbpsだそうです。

全ソース(スケッチ)はこちらに置いておきます。

以下、主要部を掲載します。

I2C通信部のソース
#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は次のようになってます。

02

このプロトコルに従ってコマンド送信は、
  // コマンド送信
  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」カテゴリの記事

ATtiny13A」カテゴリの記事

AVR」カテゴリの記事

表示器制御関連」カテゴリの記事

コメント

たま吉さん、はじめまして。

ATtinyでのI2Cの情報を探していてこちらのブログに辿り着き、早速tinyI2C.inoを使わせていただきました。
そのことをblogにアップし、また今回書いたコードはgithubで公開しています。
http://hine.hatenablog.com/entry/2014/11/09/235938
https://github.com/hine/I2CMatrix
本当によいライブラリの公開ありがとうございます。

blogエントリー、githubのコードともに、もし問題があればすぐに削除いたします。

hineさん、はじめまして

ご報告ありがとうございます。
blogエントリー、github公開問題なしです。

ブログ拝見しました。
私もこの小っちゃいI2Cマトリックス表示器は「いい!」と思い
中の人(HT16K33)を入手しました。遊んでみようと模索中です。

たま吉さん

ありがとうございます。I2C機器増えてきたので、
ATtinyでI2Cは流行るんじゃないかと思うんですよね。

HT16K33いいですね!データシート見ると、マトリックスLEDとかも
いいですけど、Keyscanに使えるのも面白そうだと思っています。

今後とも色々参考にさせてください!

Tiny13でI2Cマスタになる例を探してこちらを拝見しました。
秋月のAQM0802Aがうまく動かず波形を見たところ,どうもStop conditionがうまく出ていないようです。
Stop conditionを出す前に,SCKもSDAもLowにしておく必要がありますが,それが抜けています。
I2C_Stop()の先頭に

I2C_CLOCK_LO();
I2C_DATA_LO();

の2つを追加すると,動くようになります。

ついでに言うと,SCKとSDAが同時に変化する場所もあり,タイミング的にまずいところがあるようですが,相手のデバイスの実力でなんとか動いているような感じです。


>I2C_Stop()の先頭に
>
>I2C_CLOCK_LO();
>I2C_DATA_LO();
>
>の2つを追加すると,動くようになります。

ご指摘ありがとうございます。
修正し、動作確認の上記載内容を訂正します。

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/571408/60603040

この記事へのトラックバック一覧です: ATtiny13AでI2C接続キャラクタLCDを利用する:

« Windows 8.1 で署名なしドライバーソフトをインストールする | トップページ | ATtiny13AでI2C接続キャラクタLCDを利用する(2) »