米粒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デバイスとかだと動作しない可能性があります。
一応、このソースを使って通信可能であることを確認したモジュールは以下の物があります。

 

関連記事

Hello Tiny10

米粒AVRでI2C通信を使ってLCD表示

前の記事で紹介したI2Cライブラリを使った、LCDの表示のライブラリ的なもの  

記事を読む

no image

AndroidにPS2キーボードを繋いでみた

@yishiiさんの素のAndroid端末を外部のキーにより操作する実験と言う記事を参考に、Ardu

記事を読む

Raspberry Piのサウンドを試したら音が出なかった話

(これはX-WindowのVLCメディアプレイヤーのスクショ) Raspberry Piでは、

記事を読む

no image

Arduino + USBホストシールド で ADK を試す(その2)

前回「Arduino + USBホストシールド で ADK を試す(その1)」では、Androidで

記事を読む

soundIR 送信機

赤外線リモコン送信機(イヤホンジャック接続)の回路

前回は受信機で、今回は送信機を作ってみました。 受信機と違って、送信機の作成は少し面倒です。

記事を読む

no image

Android + Bluetooth + Arduino

 以前書いた『気になる物』で紹介したBluetoothモジュールですが、本当に気になって実

記事を読む

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

今回は秋月電子のI2C接続のリアルタイムクロック(RTC)モジュールを買ったので、ちょっと試してみま

記事を読む

Raspberry Piの起動SDカードを準備する

先日入手したRaspberry PiでLinuxを起動する為のSDカードを準備します。 Ras

記事を読む

Androidから米粒AVRへデータを送る

米粒AVRでSoftModem的なこと その2

前回は「米粒AVRからAndroidへデータを送る」ことを試しました。 今回はその逆の「And

記事を読む

米粒AVR リモコン

米粒AVRでリモコン送信機

米粒AVR(tiny10)を使った赤外線リモコン送信機を作ってみました。 これで送信できるのは

記事を読む

Comment (2件)

  1. ハヤシ

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

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

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

    よろしくお願いします。

    1. Futaba

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

Arduinoで温湿度計 #2

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

Arduinoで温湿度計

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

ArduinoでSDカード

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

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

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

秋月の小型キャラクタLCD

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

PAGE TOP ↑