米粒AVRでI2C通信(マスター)

公開日: 電子工作 | ,

米粒AVRでI2C通信

米粒AVRことATtiny10は3つまたは4つのI/Oピンしか使えないのですが、I2Cなら2つのピンを使うだけで複数のモジュールと通信ができると言うことで、I2Cが使えれば便利だろうと試行錯誤してみました。

結論を言うと、一応出来たのは出来たのですが、凝ったことをやろうとするとすぐにメモリオーバーになっちゃいます(´・ω・`)
そもそもプログラムメモリが1kバイトしかないので、当たり前といえば当たり前なんですが・・・I2C使いたいなら、素直にtiny45とかtiny2313とか使うべきなんだろうと思いました。
そもそも、I2Cモジュール自体が大きいので、小さいことが売りである米粒AVRでやる意味は無いと言えば無いので・・・

ま、とりあえず出来たので、ソースを公開しておきます。

 

ソース

tiny10softI2C.h

/*<br />
    tiny10softI2C.h<br />
    Software I2C Library for ATTiny10<br />
    http://s2jp.com/<br />
    <br />
    Copyright (c) 2012 Futaba. All Rights Reserved.<br />
*/<br />
<br />
#ifndef _S2_TINY10_SOFTI2C_H_<br />
#define _S2_TINY10_SOFTI2C_H_<br />
<br />
#include <avr/io.h><br />
#include <util/delay.h><br />
<br />
#define I2C_SDA_PIN            PB0<br />
#define I2C_SCL_PIN            PB1<br />
<br />
#define I2C_SDA_PIN_BV        _BV(I2C_SDA_PIN)<br />
#define I2C_SCL_PIN_BV        _BV(I2C_SCL_PIN)<br />
<br />
#define _I2C_SDA_HIGH        (PORTB |= I2C_SDA_PIN_BV)<br />
#define _I2C_SDA_LOW        (PORTB &= ~I2C_SDA_PIN_BV)<br />
<br />
#define _I2C_SCL_HIGH        (PORTB |= I2C_SCL_PIN_BV)<br />
#define _I2C_SCL_LOW        (PORTB &= ~I2C_SCL_PIN_BV)<br />
<br />
void I2C_clkWait()<br />
{<br />
    #if F_CPU == 1000000<br />
    __asm__ __volatile__("nop");<br />
    #elif F_CPU == 1200000<br />
    __asm__ __volatile__("nop");<br />
    #elif F_CPU == 2000000<br />
    __asm__ __volatile__("nop");<br />
    __asm__ __volatile__("nop");<br />
    __asm__ __volatile__("nop");<br />
    __asm__ __volatile__("nop");<br />
    __asm__ __volatile__("nop");<br />
    #elif F_CPU == 2400000<br />
    __asm__ __volatile__("nop");<br />
    __asm__ __volatile__("nop");<br />
    __asm__ __volatile__("nop");<br />
    __asm__ __volatile__("nop");<br />
    #elif F_CPU == 4000000<br />
    _delay_us(2);<br />
    #elif F_CPU == 4800000<br />
    _delay_us(2);<br />
    #else<br />
    _delay_us(4);<br />
    #endif<br />
}<br />
<br />
void I2C_clkWait2()<br />
{<br />
    #if F_CPU == 1000000<br />
    #elif F_CPU == 1200000<br />
    #elif F_CPU == 2000000<br />
    #elif F_CPU == 2400000<br />
    #elif F_CPU == 4000000<br />
    _delay_us(2);<br />
    #elif F_CPU == 4800000<br />
    _delay_us(2);<br />
    #else<br />
    _delay_us(3);<br />
    #endif<br />
}<br />
<br />
inline static void I2C_init()<br />
{<br />
    DDRB |= I2C_SDA_PIN_BV;<br />
    DDRB |= I2C_SCL_PIN_BV;<br />
<br />
    _I2C_SCL_HIGH;<br />
    _I2C_SDA_HIGH;<br />
}<br />
<br />
void I2C_start()<br />
{<br />
    _I2C_SDA_HIGH;<br />
    I2C_clkWait();<br />
    _I2C_SCL_HIGH;<br />
    <br />
    I2C_clkWait();<br />
    _I2C_SDA_LOW;<br />
    I2C_clkWait();<br />
    _I2C_SCL_LOW;<br />
}<br />
<br />
void I2C_stop()<br />
{<br />
    _I2C_SDA_LOW;<br />
    I2C_clkWait();<br />
    _I2C_SCL_HIGH;<br />
    I2C_clkWait();<br />
    _I2C_SDA_HIGH;<br />
    I2C_clkWait();<br />
}<br />
<br />
uint8_t I2C_write(uint8_t data)<br />
{<br />
    uint8_t temp;<br />
    for(temp=0x80; temp; temp>>=1)<br />
    {<br />
        if(data & temp)        _I2C_SDA_HIGH;<br />
        else                _I2C_SDA_LOW;<br />
        I2C_clkWait2();<br />
        _I2C_SCL_HIGH;<br />
        I2C_clkWait();<br />
        _I2C_SCL_LOW;<br />
    }<br />
    <br />
    DDRB &= ~I2C_SDA_PIN_BV;<br />
    <br />
    I2C_clkWait();<br />
    temp=PINB;<br />
    _I2C_SCL_HIGH;<br />
    I2C_clkWait();<br />
    <br />
    _I2C_SCL_LOW;<br />
    <br />
    DDRB |= I2C_SDA_PIN_BV;<br />
    <br />
    if(temp & I2C_SDA_PIN_BV)<br />
    {<br />
        return 1;<br />
    }<br />
    return 0;<br />
}<br />
<br />
uint8_t I2C_read(uint8_t ACK)<br />
{<br />
    uint8_t data=0;<br />
    uint8_t temp;<br />
    <br />
    DDRB &= ~I2C_SDA_PIN_BV;<br />
    <br />
    for(temp=0x80; temp; temp>>=1)<br />
    {<br />
        I2C_clkWait();<br />
        if(PINB & I2C_SDA_PIN_BV)        data |= temp;<br />
        _I2C_SCL_HIGH;<br />
        <br />
        I2C_clkWait();<br />
        _I2C_SCL_LOW;<br />
    }<br />
    <br />
    DDRB |= I2C_SDA_PIN_BV;<br />
    <br />
    if(ACK)    _I2C_SDA_LOW;<br />
    else    _I2C_SDA_HIGH;<br />
    <br />
    I2C_clkWait();<br />
    _I2C_SCL_HIGH;<br />
    I2C_clkWait();<br />
    _I2C_SCL_LOW;<br />
<br />
    return data;<br />
}<br />
<br />
#endif<br />

ソースはヘッダーファイル用に作っています。
Atmel Studio 6で、tiny10softI2c.h として保存し、#inlude して利用することを想定してます。
他のコンパイラとかでは動かないかもしれません。
ソースを分割してたりして複数のソース内でincludeする場合は工夫が必要です。(米粒AVRでそこまでやってる人っているのだろうか・・・)

 

ピン

デフォルトでは、

PB0 が SDA(データ)
PB1 が SCL(クロック)

としています。

米粒AVR soft-I2C 回路

 

命令説明

void I2C_init();

ライブラリの初期化。

void I2C_start();

start condition発行。

void I2C_stop();

stop condition発行。

uint8_t I2C_write(uint8_t data);

引数 data をI2Cへ送信
戻り値 0=ACK 1=NACK

uint8_t I2C_read(uint8_t ACK);

I2Cからデータを読み込む。
引数 ack == 1 でACK、ack == 0 でNACK
戻り値 読み込んだデータ

 

クロック

このライブラリでは、I2Cのクロックは米粒AVRが内臓8MHzで動作している時に、約100kHzとなっています。
ただし、ソフトI2Cなのでクロックやデューティ比には、かなりのばらつきがあり、必ずしもこの通りとは限りません。
だいたい100k・・・と言うことで

 

注意

僕自身I2Cの知識に乏しく、今回初めてI2C通信をやりました。
そんな勉強がてらに作ったものなので、かなりいい加減な作りになっています。
ですので、通信のタイミングに厳格なI2Cデバイスとかだと動作しない可能性があります。
一応、このソースを使って通信可能であることを確認したモジュールは以下の物があります。

 

関連記事

Raspberry Pi ロゴマーク

Raspberry Piのリビジョン

リビジョンの種類 現在、Rapsberry Piにはリビジョン1とリビジョン2と呼ばれる2つの

記事を読む

raspi-colortest

Raspbianの起動

Raspbianの起動画面をキャプチャしてみた。 SDカードにイメージを書き込んだままのデフォ

記事を読む

no image

Android+Arduinoでリモコン3 その1(AndroidAccessory接続)

何度か、Android+リモコンのネタを書いたことがあるのですが、今回はAndroid Access

記事を読む

Raspberry Pi - Ethernet

Raspberry Pi インターネット接続

Raspberry Piをインターネットに接続してみた。 有線で接続(Ethern

記事を読む

no image

Android+Arduinoでリモコン4(Microbridge接続)

前回AndroidAccessory接続で作った赤外線学習リモコンを、今回はMicrobridge接

記事を読む

no image

Softmodem Terminal 公開しました。

需要がどれほど有るのか疑問に思いつつ、以前のエントリ(AndroidでSoftModemを試してみた

記事を読む

no image

Android+Arduinoでリモコン3のおまけ

Androidとは関係ないのですが、前回の記事  Android+Arduinoでリモコ

記事を読む

no image

Android + Bluetoothドングル + PIC24F

@hrdakinori さんと言う方が、PIC24FとBluetoothドングルを使って、Andro

記事を読む

no image

Atmel Studio 6

米粒AVR(ATtiny10)の開発を行う為に、Atmel Studio 6.0をインストールし

記事を読む

no image

AVRISPmk2

米粒AVR(ATtiny10)のプログラム書き込み用にAVRISPmk2を購入しました。 先人

記事を読む

Comment (2件)

  1. ハヤシ

    はじめてコメントさせていただきます。
    よろしくお願い致します。

    私も同じINA226を購入し、LCDに電圧と電流を表示しようとし
    プログラムを勉強していますがなかなかできなくて悩んでいます。

    INA226関連のソースも公開できないでようか?

    よろしくお願いします。

    1. Futaba

      コメントありがとうございます。
      どの辺りがうまく行かないのでしょうか?

Arduinoで温湿度データロガー
Arduinoで温湿度計 #2

今回は、前回作った温湿度計にRTCモジュールとSDカードアダプタを

Arduinoで温湿度計
Arduinoで温湿度計

秋月電子の温湿度モジュールを使って、温湿度計を作ってみました。 以前

SDカード・マイクロSDカード
ArduinoでSDカード

電子工作で大量のデータを保存する方法を調べていると、 Arduin

秋月電子 リアルタイムクロック(RTC)モジュール
秋月のリアルタイムクロック(RTC)モジュール

今回は秋月電子のI2C接続のリアルタイムクロック(RTC)モジュールを

秋月電子 キャラクタLCD
秋月の小型キャラクタLCD

今回秋月電子で売られている小型のキャラクター液晶2種を買ったので、

PAGE TOP ↑