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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

マイコン

マイクロコンピュータの略で、CPUにマイクロプロセッサを用いたコンピュータのこと。家電製品、電磁機器などの制御に用いられています。単体でコンピュータとしての機能を一通り備えています。 現代のパーソナルコンピュータに近く、同時期のメインフレームやミニコンピュータと比べ、小さいことが特徴です。

Q&A

解決済

4回答

3957閲覧

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

klose0609

総合スコア11

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

マイコン

マイクロコンピュータの略で、CPUにマイクロプロセッサを用いたコンピュータのこと。家電製品、電磁機器などの制御に用いられています。単体でコンピュータとしての機能を一通り備えています。 現代のパーソナルコンピュータに近く、同時期のメインフレームやミニコンピュータと比べ、小さいことが特徴です。

0グッド

0クリップ

投稿2021/09/30 11:55

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

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

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

C

1#include "mcc_generated_files/mcc.h" 2#include <stdio.h> 3unsigned int adrs; 4unsigned int tempValue, temperature; 5 6void main(void) 7{ 8 SYSTEM_Initialize(); 9 10 while (1) 11 { 12 for(adrs = 0; adrs < 32767; adrs++){ 13 tempValue = ADC_GetSingleConversion(RA0); //LM35 is connected to RA) pin 14 temperature = (tempValue * 5 / 4096) * 100; //10bit ADC 15 I2C1_WriteNBytes(adrs, temperature, 1); 16 I2C1_ReadNBytes(adrs); 17 __delay_ms(60000); 18 } 19 } 20}

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

c

1adc_result_t ADC_GetSingleConversion(ADC_channel_t channel) 2{ 3 //Select the A/D channel 4 ADPCH = channel; 5 6 //Turn ON the ADC module 7 ADCON0bits.ON = 1; 8 9 //Disable the continuous mode 10 ADCON0bits.CONT = 0; 11 12 //Start the conversion 13 ADCON0bits.GO = 1; 14 15 //Wait for the conversion to finish 16 while(ADCON0bits.GO) 17 { 18 19 } 20 21 return ((adc_result_t) ((ADRESH << 8) + ADRESL)); 22} 23 24void I2C1_WriteNBytes(i2c1_address_t address, uint8_t* data, size_t len) 25{ 26 while(!I2C1_Open(address)); // sit here until we get the bus.. 27 I2C1_SetBuffer(data,len); 28 I2C1_SetAddressNackCallback(NULL,NULL); //NACK polling? 29 I2C1_MasterWrite(); 30 while(I2C1_BUSY == I2C1_Close()); // sit here until finished. 31} 32 33void I2C1_ReadNBytes(i2c1_address_t address, uint8_t *data, size_t len) 34{ 35 while(!I2C1_Open(address)); // sit here until we get the bus.. 36 I2C1_SetBuffer(data,len); 37 I2C1_MasterRead(); 38 while(I2C1_BUSY == I2C1_Close()); // sit here until finished. 39}

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

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

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

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

投稿2021/10/01 12:04

koujikuu

総合スコア401

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

klose0609

2022/01/27 17:55

時間がかかりましたがコードを読み解きまして、ようやく目的のものを作ることができました。おかげさまでPICの使用に関して理解が深まりました。ご丁寧にありがとうございました。
koujikuu

2022/01/28 22:38

24xx256 EEPROM は、一度に16バイト単位で書き込みでき 書き込み完了も delay ではなく ポーリング(I2C ACK) で チェックすることもできます 試してみて下さい。
guest

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言語を覚える事です。
結果を求めるよりも、理解を深める事に重点を置いた方が良いです。

投稿2021/10/03 06:14

nac_tnk

総合スコア463

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

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中での同関数の呼び出し方法を再確認する

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

投稿2021/09/30 13:16

thkana

総合スコア7629

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

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

投稿2021/09/30 12:47

y_waiwai

総合スコア87749

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問