質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.37%

外付けEEPROMに温度データを書き込みたい

受付中

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 317

score 9

PICを使い始めた初心者です。
現在、LM35を使用した温度センサーを作製しております。
最終目標は下記のデバイスを作製することです。

・LM35で1分ごとに温度データを測定
・読み取ったデータを温度に変換し、PIC(18F27Q84)に外付けしたEEPROM(24LC256)に保存
・温度データがEEPROMにある程度蓄積したところで、PCにUART接続し、モニターにデータを出力

現在、回路を作り、プログラムを作成しているところです。
まず初めに動作確認のため、温度を測定し、EEPROMへ保存、その保存したデータを即座にPCに出力できるよう下記のプログラムを作成しました。開発環境はMPLAB(v5.50)で、MCCを使用しております。

#include "mcc_generated_files/mcc.h"
#include <stdio.h>
unsigned int adrs;
unsigned int tempValue, temperature; 

void main(void)
{
    SYSTEM_Initialize();

    while (1)
    {
        for(adrs = 0; adrs < 32767; adrs++){
            tempValue = ADC_GetSingleConversion(RA0); //LM35 is connected to RA) pin
            temperature = (tempValue * 5 / 4096) * 100; //10bit ADC
            I2C1_WriteNBytes(adrs, temperature, 1);
            I2C1_ReadNBytes(adrs);
            __delay_ms(60000);
        }
    }
}

また、MCCで作成されたADCおよびI2Cに関するコードは下記のとおりです。

adc_result_t ADC_GetSingleConversion(ADC_channel_t channel)
{
    //Select the A/D channel
    ADPCH = channel;

    //Turn ON the ADC module
    ADCON0bits.ON = 1;

    //Disable the continuous mode
    ADCON0bits.CONT = 0;

    //Start the conversion
    ADCON0bits.GO = 1;

    //Wait for the conversion to finish
    while(ADCON0bits.GO)
    {

    }

    return ((adc_result_t) ((ADRESH << 8) + ADRESL));
}

void I2C1_WriteNBytes(i2c1_address_t address, uint8_t* data, size_t len)
{
    while(!I2C1_Open(address)); // sit here until we get the bus..
    I2C1_SetBuffer(data,len);
    I2C1_SetAddressNackCallback(NULL,NULL); //NACK polling?
    I2C1_MasterWrite();
    while(I2C1_BUSY == I2C1_Close()); // sit here until finished.
}

void I2C1_ReadNBytes(i2c1_address_t address, uint8_t *data, size_t len)
{
    while(!I2C1_Open(address)); // sit here until we get the bus..
    I2C1_SetBuffer(data,len);
    I2C1_MasterRead();
    while(I2C1_BUSY == I2C1_Close()); // sit here until finished.
}

このコードをPICに書き込もうとしたところ、下記のエラーがでました。

error: (1098) conflicting declarations for variable "_I2C1_WriteNBytes" (mcc_generated_files/examples/i2c1_master_example.c:111)

このエラーは一つの関数に対して複数の定義を与えると発生するようですが、私のケースになぜ当てはまるのかわかりませんでした。
外付けEEPROMの記事をネットで探したのですが、MCCを使用しているものが見当たりませんでしたので、解決方法がわからず、こちらに投稿させていただいた次第です。

また、EEPROMへの書き込みかた、読み込み方についても、もしかしたら全く見当違いのコードになっているという懸念もございます。私の認識では、EEPROMのアドレスの一つに、一つの温度データを格納し、次のアドレスへ移動、そこに同じように温度データを格納していくことを繰り返すイメージを持っておりますが、もし間違っておりましたら、こちらもアドバイスいただきたく存じます。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

0

google翻訳
error: (1098) 変数の宣言が競合しています "_I2C1_WriteNBytes"

こいつの第二引数、アドレスのはずだけど、intが与えられてます。
これどーにかしよう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

PICには手を出していないので追試などは出来ませんが。

mcc_generated_files/examples/i2c1_master_example.c:111
の場所にあるのが
void I2C1_WriteNBytes(i2c1_address_t address, uint8_t* data, size_t len)
ということですか?

でこの関数のプロトタイプ宣言は
mcc_generated_files/mcc.h
に含まれているのでしょうか。

Cでは、プロトタイプ宣言のない関数は、int型を返し、プログラムに書いてある型の引数を受け取る関数であると決めつけてコンパイルが行われます。つまり、
            I2C1_WriteNBytes(adrs, temperature, 1);
は、(int, unsigned int, int)を受け取ってintを返す関数と決めつけてコンパイルしています。
一方、
void I2C1_WriteNBytes(i2c1_address_t address, uint8_t* data, size_t len)
は、i2c1_address_tが実態としてint型だったとしても、(int ,uint8_t*, size_t)ですから、特に2番めの引数がなんとも一致しません。

・I2C1_WriteNBytes()のプロトタイプがmainのプログラムに含まれるようにする
・main中での同関数の呼び出し方法を再確認する

あたりが対応になりそうに思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

PIC18F26Q84 + 24FC256 ( MPLAB X v5.50 + xc8 v2.32 + MCC )
にて 24FC256 1バイト読み書きテストしました config省略

#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/examples/i2c1_master_example.h"

#define i2c_eeprom_adr  0x50        // 24fc256 eeprom i2c adr

uint8_t i2c_eeprom_read( uint16_t adr ){
    uint8_t buf[2];
    buf[0] = adr >> 8;
    buf[1] = adr & 0x00FF;
    I2C1_WriteNBytes( i2c_eeprom_adr , buf , 2 );
    I2C1_ReadNBytes( i2c_eeprom_adr , buf , 1 );
    return buf[0]; 
}
void i2c_eeprom_write( uint16_t adr , uint8_t dat ){
    uint8_t buf[3];
    buf[0] = adr >> 8;
    buf[1] = adr & 0x00FF;
    buf[2] = dat;
    I2C1_WriteNBytes( i2c_eeprom_adr , buf , 3 );
    __delay_ms(5);
}
void i2c_eeprom_test(void){
    uint8_t dat;
    uint16_t adr = 7;
    dat = 0x00;
    i2c_eeprom_write( adr  , dat );
    printf(" write = %02X read = %02X \n\r" , dat , i2c_eeprom_read( adr ) );
    dat = 0x78;
    i2c_eeprom_write( adr , dat );
    printf(" write = %02X read = %02X \n\r" , dat , i2c_eeprom_read( adr ) );
    dat = 0xFF;
    i2c_eeprom_write( adr , dat );
    printf(" write = %02X read = %02X \n\r" , dat , i2c_eeprom_read( adr ) );    
}

void main(void)
{
    SYSTEM_Initialize();

    printf("PIC18F26Q84 xc8v2 mcc i2c \n\r");

    i2c_eeprom_test();

    while (1)
    {
        IO_RC2_Toggle(); // RC2 blink
        __delay_ms(500);
    }
}


実行結果

PIC18F26Q84 xc8v2 mcc i2c
write = 00 read = 00
write = 78 read = 78
write = FF read = FF

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

正直にいって、「滅茶苦茶」です。
すべてあなたの思い込みで書かれているようですけど、(少なくても私にはそう感じます)
プログラム(I2C通信、レジスタ操作)にそのようなものは1ミリも入り込む余地はありません。
データシートを見なくても出来ると思っているのでしょうか?

このプログラムの核心部分

       tempValue = ADC_GetSingleConversion(RA0); //LM35 is connected to RA) pin
       temperature = (tempValue * 5 / 4096) * 100; //10bit ADC
       I2C1_WriteNBytes(adrs, temperature, 1);
       I2C1_ReadNBytes(adrs);

のたった4行だけで、あまりの多くの間違い(無理解)があります。

まず、最初に

 tempValue = ADC_GetSingleConversion(RA0); //LM35 is connected to RA) pin

引数を「RA0」としているのは、きっと貴方はRA0ピンをアナログ入力ピンとして指定していると思っているのでしょうけど、これはRA0ピンの電圧(1か0)を読み込んでそれを引数として代入する事を意味します。

ADC_GetSingleConversion関数を見れば

adc_result_t ADC_GetSingleConversion(ADC_channel_t channel)

と引数はADCのチャンネルをしていするものです。そして関数の中で

ADPCH = channel;

とありますよね?
つまり、ADPCHに代入するレジスタ値になる訳です。

で、データシートの「43.7.8  ADPCH」の項を見れば
RA0のchannel値は0です。つまり、0を入れれば良い事になります。
※偶然、RA0がLOWレベルなら、というか、この温度センサならほぼLOWレベルでしょうから0になりますけど、それで動いたとしても奇跡的に動いただけです。違うピンにすれば動きませんから。

次に温度の計算

temperature = (tempValue * 5 / 4096) * 100; //10bit ADC

ですけど、演算子や変数の型等を理解していません。
この式では0にしかなりません。まぁ100度以上なら100にはなりますけど。

算数じゃないのですから、常に計算式は順序や型(範囲)を意識して書きましょう。
()内は現状では整数計算になっていますから0,1,2,3,4のいずれかにしかなりません。
※現状では四則演算もまともに出来ていない、という事を認識した方が良いです。

でやっと、今回のI2CのEEPROMの部分ですけど、I2Cのやり取りの手法を理解していないのと、
このデバイス特有のものの両者を理解していません。
(勿論、前者を理解していなくても24LC256のデータシートにある仕様に沿って通信すれば読み書きは出来ます。
ただし、MCCで作成されるコードは逆に、典型的なI2Cのやり取りをする為のテンプレートです。
なので逆に使いにくいかもしれません。

まずI2C(の標準的な操作)を簡単に説明すれば、書き込みの場合
最初に「I2Cアドレス+W」の1バイトを送ります。(ここでデバイスアドレスと一致したデバイスが反応する)
次にレジスタアドレスを送りデバイス内のアドレス位置をセットします。
その次にそのアドレスに書き込むデータを送ります。

読み込みの場合はもう少し複雑になります。
最初に「I2Cアドレス+W」の1バイトを送ります。
次にレジスタアドレスを送りデバイス内のアドレス位置をセットします。
ここまでは書き込みと同じです。で、ここでいったん終了してから
「I2Cアドレス+R」の1バイトを送って、送信の要求をします。
そして、1バイト送ってもらいます。

※レジスタアドレスは「カーソル位置」或いはエクセルの「選択セルの位置」のようなものです。
書き込み或いは読み込みをすれば自動で移動してくれたりしますけど、それはデバイス次第です。
このEEPROMも自動で移動してくれますけど、「特殊な動きをする」とだけ指摘しておきます。

次にMCCが作ってくれる関数

void I2C1_WriteNBytes(i2c1_address_t address, uint8_t* data, size_t len)

ですけど、最初の引数はI2Cのデバイスアドレスです。2番目はデータの先頭アドレス、最期にデータ長(バイト数)です。
つまり、
デバイスアドレス+Wの1バイトを送り、
次にデータをバイト数送る関数です。

貴方は

I2C1_WriteNBytes(adrs, temperature, 1);

と、相手も指定しないまま、データの書き込み位置(実際はadrs+W)と温度データを送っています。
当然ながら、デバイス(EEPROM)は自分相手の通信では無いので何も反応しません。

ここからはEEPROMのデータシートが必要です。
このデバイスの場合は
1010[A2]00がI2Cアドレスになります。つまり、A2ピンをHIGHにしていれば0x54、LOWにしていれば0x50です。
で、レジスタアドレス(書き込み位置)もこのデバイスの場合は2バイトで上位バイトから送ります。
更に言えば、書き込んだら書き込み完了まで待つ必要があります。その時間(TWC)は5msとなっています。
※それらはこのデータシートを見ないと書いてない情報です。単にI2C通信を知っているだけでは出来ませんし、
同じシリーズのの24LC016でさえ、同じコードでは通用しません。

つまり、
第2引数の為のデータを

①レジスタアドレスの上位バイト
②レジスタアドレスの下位バイト
③温度データ

と作って送る事になります。
更に、書き込んだら5ms待つ必要があります。
そこまで考慮して作ってくれたのが、koujikuuさんのi2c_eeprom_write関数です。

I2C読み込みの関数

void I2C1_ReadNBytes(i2c1_address_t address, uint8_t *data, size_t len)

は、
I2Cアドレス+Rで1バイト送り
その後に、lenバイト読み込みデータアドレスの先頭から書き込みます。

で、前述したように、デバイスとの通信ではまず最初にEEPROMのレジスタアドレス位置を指定する必要があります。
なので、この関数だけでは読み込めません。(というか、現在デバイスにセットされている位置を読み込んでしまいます)
まずはWRITEでデバイスのレジスタアドレス(カーソル位置)を合わせてからこの関数を使い読み込みます。

貴方は

I2C1_ReadNBytes(adrs);

と読み込み位置(スレーブアドレス)を指定しているようですけど、これは全く理解していない事を示しています。
readでは読み込み位置は動かせません。また、読み込むのに読み込む為の変数を用意していません。
「その保存したデータを即座にPCに出力できるよう下記のプログラムを作成しました。」との事ですけど
一体どうやって確認するつもりだったのでしょうか?

この辺りもkoujikuuさんが作ってくれたi2c_eeprom_read関数を見て勉強した方が良いです。

MCCを利用して関数を自動で作ってもらったとしても、その関数の意味合い、使い方を理解する力が無ければ何にもなりません。却って自分でレジスタ操作する方が理解が早いと思います。

実際の所、「行いたい事」に比べて、貴方の実力は全く追いついていません。
上記で述べた事の1つでも間違えていたら望んでいる結果は得られません。
「回路を作り」と書いてありますけど、このEEPROMの場合は回路でデバイスアドレスを作ります。
そのあたりの事がコードに全く書かれていない所を鑑みても、信頼は出来ません。

ここまで間違えていると、何処が間違えているのか結果から推測するのは不可能です。
(一部を正しくしたとしてもその結果が得られないので)
もう少し簡単なもので実践を積んだ方が良いと思います。
回り道に思うかもしれませんけど、五里霧中になっている方がよっぽど無駄な時間を使います。

それに何と言っても、データシートを見る事やC言語を覚える事です。
結果を求めるよりも、理解を深める事に重点を置いた方が良いです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.37%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る