今回からEEPROMを使ったプログラムを作成してみます。
現在のタイマー機能の問題点
前回までのプログラムで、タイマー時間を設定する機能を追加することができました。
ところで、まだちょっと機能が足りませんよね。すでにお気づきかもしれませんが、タイマー時間を変更した場合、電源をOFFにすると変更した値は消されてしまい、次に電源を入れた時には元のデフォルトのタイマー時間に戻ってしまいます。
タイマー時間を変更したのであれば、その設定時間は電源を切ってもまた次回電源を入れた時に有効になっていて欲しいですよね。
PICマイコンに限らず、他のマイコンでも、この課題を解決する方法が用意されています。それは「EEPROM」と呼ばれる、電源をOFFにしても記憶内容を保持するメモリが搭載されています。
そこで今回から、タイマー時間を変更したらその設定時間を「EEPROM」に保存しておいて電源をOFFにしてもその設定時間を保持しておく、というプログラムを作成していきます。
メモリの種類
データを保持しておく「メモリ」には様々な種類がありますが、記憶保持の観点から大きく2種類に分かれます。
一つは電源がないと記憶した内容が消えてしまう「揮発性メモリ」、もう一つは電源をOFFにしても記憶内容が消えない「不揮発性メモリ」です。
これらは単に「消えるメモリ」「消えないメモリ」と呼べばいいところを、「揮発性メモリ」と呼んでいます。なんとなく意識高い系な気がしますが、学問の分野は基本的に意識高い系の用語を使う、という暗黙のルールがありますので我慢しましょう。
「揮発」という言葉ですが、一般に使われている意味とほぼ同じです。
例えば消毒用アルコールってすぐに蒸発してしまいますよね。これをちょっと難しくいうと「アルコールは揮発性が高い」と言ったりします。すぐに蒸発して消えてしまう液体を「揮発性がある」「揮発性が高い」というわけです。
「揮発性メモリ」とは、電源をOFFにすると記憶した内容が蒸発して消えてしまうことからこのように呼ばれています。ということは、日常生活で物忘れが多くなった場合には「最近、記憶の揮発性が高まった気がするんだよね」というと、、、ちょっと変ですね。
EEPROMは電源をOFFにしても記憶内容が消えないので、「不揮発性メモリ」と呼ばれています。
「EEPROM」とは
EEPROMは不揮発性メモリであることがわかりましたが、何の略なんでしょうか。
EEPROM = Electrically Erasable Programmable Read-Only Memory
です。日本に訳すと「電気的に消去、書き込み可能な、読み出し専用メモリ」です。
ん? ちょっとこれ、意味おかしくないですか?
「電気的に消去したり書き込み可能な」と言ってる割に「読み出し専用メモリ」ってなんか矛盾してるような気がします。「読み出し専用メモリ」でしたら消去とか書き込みができないような気もしますが…
気になってしまう方のために補足しておきますが、特にこの部分は読まなくても問題ありませんので、この説明は飛ばしていただいて構いません。
昔、コンピュータのメモリは「RAM (Random Access Memory)」と呼ばれる「読み書き可能・電源がないと記憶内容が消えるメモリ」と、「ROM (Read-Only Memory)」と呼ばれる「読み込みのみ可能(書き込み不可)・電源がなくても記憶内容が消えないメモリ」の2種類がありました。
このROMですが、いつどのようにデータを書き込むかというと、ROMライターと呼ばれるROMにデータを書き込む機器があり、新品のROMの内部接続回路を焼き切ってデータを書き込むようになっています。イメージとしては、ROMの中にたくさんヒューズが並んでいて、ヒューズがあるところが1、ヒューズが焼き切ってあるところが0、というように、記憶するデータに合わせてヒューズを焼き切っていきます。
このタイプのROM、ROMライタは今でも販売されています。
このようにデータを書き込んだROMは、ヒューズを焼き切ってしまっていますので、当然ながらデータ内容を変更できません。その代わり、電源がなくてもデータが保持できるわけです。このようなROMの仕組みでは、内容を変更したい場合はそのROMは捨てて新しいROMにデータを書き込むしかありません。
でも今回プログラムで実現しようとしているような、後からデータ内容を変更できる、電源をOFFにしても記憶内容が消えないメモリが欲しいですよね。
ということで、「あとから記憶内容を消去したり書き込んだりできるROM」が開発されたわけです(このメモリは、先ほどのヒューズを焼き切るタイプのROMの構造・原理とは違います)。
ここで我に返ってみます。「ROM」って読み出し専用メモリ」でしたよね。ということは、先ほど自分で言ってしまった「あとから記憶内容を消去したり書き込んだりできるROM」とは「あとから記憶内容を消去したり書き込んだりできる、読み出し専用メモリ」ってわけで、自分も矛盾した結論を出してしまったわけです。
PIC12F1822のEEPROM
それでは、PIC12F1822のEEPROMについて確認してみましょう。
最初にどれぐらいの記憶容量があるか確認します。以下はPIC12F1822のデータシートです。
(Microchip社 PIC12F1822データシートより引用・加工)
枠で囲んだ部分がEEPROMの容量の仕様です。データシートから256バイトであることがわかります。「メガバイト」とか「ギガバイト」ではなく「バイト」です。
メモリやSSDがギガバイト単位の世の中で、256バイトとはずいぶん少なくて心もとない気もしますが、マイコンでしたらこのぐらいの容量で十分なケースが多いです。今回のタイマー時間の保持も1バイトのみ使用しますので、256バイトはうまく使えば有効に活用できます。
このEEPROMですが、マイコン内部では独立したモジュールになっています。
(Microchip社 PIC12F1822データシートより引用・加工)
これからこのEEPROMに対して読み書きしていくわけですが、256バイトあるEEPROMのどこに書いたり、どこから読むか、場所を指定する必要があるわけです。この場所のことを「アドレス」または「番地」と呼ばれています。
例えばPIC12F1822ではEEPROMの256バイトに対して以下のようにアドレスが割り当てられています。
プログラムでEEPROMを扱う場合、「どのアドレスにデータを書くか」あるいは「どのアドレスからデータを読むか」という指定を行います。
それでは次に、EEPROMの具体的な使い方をみてみましょう。
PICマイコンのEEPROMの使い方
最初はEEPROMからのデータの読み出しです。例えばアドレス0から読み出した値を変数 read_data (unsigned char)に格納したい場合は、
read_data = eeprom_read(0);
とかけばOKです。
次にEEPROMへのデータの書き込みです。例えば write_data (unsigned char)の値をアドレス0に書き込みたい場合は、
eeprom_write(0, write_data);
とかけばOKです。
まとめると、EEPROMからデータを読み出す場合は、
eeprom_read( アドレス );
とかげは、返り値が読み出したデータになります。
またEEPROMへのデータの書き込みは、
eeprom_write( アドレス, データ );
とかけば、指定したアドレスにデータが書き込まれます。
これで読み書きができますが、初期値はどのように設定すればいいでしょうか。EEPROMの初期値は main関数で書き込むことはできません。
例えば、main関数の最初に
eeprom_write(0, 5);
などと、アドレス0にデータ5を書き込むように書いてしまうと、電源を投入するたびにmain関数が呼ばれますので、毎回アドレス0が5になってしまいます。
初期値を設定したい場合は、main関数の外に以下のフォーマットで書きます。
__EEPROM_DATA (アドレス0のデータ, アドレス1のデータ, アドレス2のデータ,アドレス3のデータ,アドレス4のデータ,アドレス5のデータ, アドレス6のデータ, アドレス7のデータ);
“EEPROM”の前はアンダースコア2個、”EEPROM”と”DATA”の間はアンダースコア1個です。
このように書いておくと、PICKIT3でプログラムを書き込む際、一緒にEEPROMにこのデータも書き込んでくれます。
なお、この書き方には注意点があります。必ず8バイト単位で書きます。以下の書き方はNGです。
__EEPROM (0x00, 0x01, 0x02, 0x03);
これは4バイトしかありません。
8バイトに満たない場合は、0xFFで埋めて8バイトにします(0xFFでなくても構いませんが…)。例えば、アドレス0に0x1Fを書き込んでおきたい場合は、
__EEPROM_DATA (0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
と書きます。また、8バイトを超える場合は、この行を複数行書きます。
以上がEEPROMの概要とプログラム方法です。
次回は、設定したタイマー値をEEPROMに保存、読み出しを行うプログラムを追加したいと思います。
更新履歴
日付 | 内容 |
---|---|
2017.7.30 | 新規投稿 |
2018.12.2 | 誤記訂正 |
2021.8.22 | 誤記訂正 |
また誤記と思われる箇所がありましたので、お知らせします。
第13回(この回)ーpicマイコンのEEPROMの使い方ー〜またEEPROMへのデータの書き込みは eepprom_write(アドレス,データ); (pが1つ多い?)
・この回で「学問の分野は基本的に意識高い系の用語を使うという暗黙のルールがある」というのはおもしろいなと思いました。(いつも学ばせていただき、ありがとうございます)
ご指摘どうもありがとうございました!
このような誤記はなかなか自分では気づかないので、本当に助かります。
とはいっても、このような誤記を見つけることを人に頼ってはいけませんね…
なるべくダブルチェックしていこうと思います。
学問的な分野は、簡単な用語で済むのに難しい言葉を使うので
ちょっと疲れてしまうことがありますが、慣れますので
ぜひいろいろな用語に触れてみてください!
こんにちは。読ませていただきました。
以前PWMの記事でもお世話になりました。
dsPIC30F4013を使用しているのですが,
eeprom_read();とeeprom_write();の関数を使用すると
undefined reference to `_eeprom_write’
というエラーが表示されてしまい,使用することができません。
dsPIC用のPeripheral Libraryもインストールしましたが,使えないようです。
データシート上ではdsPICのEEPROMは1024bitあるようなので,私のやり方が悪いように思うのですが,何卒アドバイスをよろしくお願いいたします。
PIC18F67J94のPICになりますが、もしお分かりでしたら教えてください。
EEPROMに設定値を書き込んで電源を起動してその設定を読み込むというプログラムを作りたいのですが、うまくいきません。。
現在書込みについては下記のような形で、
void eep_write(unsigned int edate)
{
INTCONbits.GIE = 0; // 全割込み禁止
TBLPTR = 0;//書き込みアドレスのセット
TBLPTRL = edate;
EECON1 = 0;//制御レジスタ1初期化
EECON1bits.WREN = 1;//書き込み許可
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1;//書き込み開始
while(EECON1bits.WR == 1){}//書き込み終了待ち
INTCONbits.GIE = 1; // 全割込み許可
}
読み込みについてはどう読み込んでよいのかわからず
void eep_reed(void)
{
short o = 0;
o = TABLAT;
}
上記のような状態です。。
MPLABにてEEPROMに書き込めたか確認しようとしても、このピックにはEEPROMの読み出し項目がなくできていません。
PIC18F67J94のEEPROMについてご質問です。
簡単な設定値を保存し、電源再起動時に読み込めるようにしたいのですが、うまくいきません。。
現在書込みはこんな感じで
void eep_write(unsigned int edate)
{
INTCONbits.GIE = 0; // 全割込み禁止
TBLPTR = 0;//書き込みアドレスのセット
TBLPTRL = edate;
EECON1 = 0;//制御レジスタ1初期化
EECON1bits.WREN = 1;//書き込み許可
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1;//書き込み開始
while(EECON1bits.WR == 1){}//書き込み終了待ち
INTCONbits.GIE = 1; // 全割込み許可
}
読み込みはあまりよくわからず
void eep_reed(void)
{
short o = 0;
o = TABLAT;
}
このような感じです。いろいろと探してみてもc言語でのサンプルコードが見つかりませんでした。もしお分かりでしたら教えていただけないでしょうか。
ずいぶん難しいPICマイコンを使われているようですが、何を製作されているのでしょうか。ちょっと興味があります。
実機を持っていないのでデータシートからの推定になります点、ご了承ください。
なお、PIC18F67J94はEEPROMを持っていません。EEPROMの代わりにプログラムメモリを使用します。(フログラムをPICKitで書き込む際、プログラムはPICマイコン内のプログラムメモリに書き込まれます。このエリアは電源をOFFにしても消えないので、ここを使用してEEPROMの代わりにしています)
[読み出し方法]
TBLPTRに読み出しアドレスを指定し、アセンブラの TBLRD (Table Read)命令を発行すると読み出したデータがTABLATレジスタに格納されます。
[読み出しプログラム]
動作確認はできませんが、おそらく以下のようなプログラムで読み出すことができると思います。
// 指定アドレスから1バイト読む関数
// flashAddrに読み出しアドレスを指定すると、読み出した値が返ってくる
uint8_t FLASH_ReadByte(uint32_t flashAddr)
{
TBLPTRU = (uint8_t)((flashAddr & 0x00FF0000) >> 16);
TBLPTRH = (uint8_t)((flashAddr & 0x0000FF00)>> 8);
TBLPTRL = (uint8_t)(flashAddr & 0x000000FF);
asm(“TBLRD”);
return (TABLAT);
}
[書き込み方法]
書き込みはちょっと厄介です。書き込みは512バイト単位で行いますので、書き込みたいアドレスを含む512バイトのエリアをまとめて書き込みします。
そのため書き込み手順は以下のようになります。(データシートサンプルプログラムを参考にしました)
1) 書き込みたいアドレスを含む512バイトのデータを読み出す。なお、512バイト単位でアドレスが区切られていますので、読み出し開始アドレスは0, 512, 1024…となります。
例えば、アドレス0x3100に書き込みしたい場合、0x3000〜0x3200のブロックですので、0x3000から512バイトを読み出します。
2) 書き込みたいアドレスのデータを書き換えます。0x3000から512バイト読み出しましたので、書き換えるデータは配列要素0x100のデータとなります。
(buffer[512]に読み出した場合、buffer[0x100]のデータを書き換える
3) 書き換えたデータを書き込むエリアの512バイトを消去します
4) 512バイトのデータを書き込みます
[書き込みプログラム]
以下の番号は上の書き込み方法の番号に対応しています。
(1)まずデータを退避するための512バイト読み出し関数です。1バイト読み出しは TBLRD 命令ですが、連続して読み出す場合はアドレスを1ずつ進める必要があるため、TBLRDPOSTINC 命令を使用します。
// 指定アドレスから512バイト読み出す関数
// bufferは呼び出す側で uint8_t buffer[512]; で確保する
// 書き込みは512バイトブロック単位なので、アドレスは512単位で指定 (0, 512, 1024…)
void FLASH_Read512Bytes(uint32_t flashAddr, uint8_t *buffer)
{
TBLPTRU = (uint8_t)((flashAddr & 0x00FF0000) >> 16);
TBLPTRH = (uint8_t)((flashAddr & 0x0000FF00)>> 8);
TBLPTRL = (uint8_t)(flashAddr & 0x000000FF);
for( uint16_t i = 0; i < 512; i++ ) { // 読み出し命令 // アドレスはポストインクリメント // ただし最後の読み出しはインクリメントなしの TBLRD にする必要があるかも asm("TBLRDPOSTINC"); // 読み出したデータはTABLATに入っている buffer[i] = TABLAT; } } (2) データを読み出し後、書き換えたいアドレスのデータを書き換えます。 buffer[該当の場所] = 書き込みデータ; (3) 書き込む前に512バイト領域を消去する必要がありますので、512バイト消去関数を作っておきます。(4)の書き込み関数から呼び出します。 // 指定アドレスから512バイト消去 void FLASH_EraseBlock(uint32_t baseAddr) { TBLPTRU = (uint8_t)((baseAddr & 0x00FF0000) >> 16);
TBLPTRH = (uint8_t)((baseAddr & 0x0000FF00)>> 8);
TBLPTRL = (uint8_t)(baseAddr & 0x000000FF);
EECON1bits.WREN = 1; // メモリ書き込み許可
EECON1bits.FREE = 1; // 処理をメモリ消去に指定
INTCONbits.GIE = 0; // 割り込み禁止
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1; // 消去
INTCONbits.GIE = 1; // 割り込み許可
}
(4) 512バイト書き込み
512バイトの書き込みですが、1回の書き込みは64バイトになりますので、8回繰り返します。
// 512バイト書き込み
// 書き込むデータをbuffer[512]にセットする
// 書き込みアドレスは512バイト単位 (0, 512, 1024…)
void FLASH_WriteBlock(uint32_t writeAddr, uint8_t *buffer)
{
// 書き込む前にメモリ消去
FLASH_EraseBlock(writeAddr);
// アドレス指定
TBLPTRU = (uint8_t)((writeAddr & 0x00FF0000) >> 16);
TBLPTRH = (uint8_t)((writeAddr & 0x0000FF00)>> 8);
TBLPTRL = (uint8_t)(writeAddr & 0x000000FF);
// 512バイトのデータ書き込み
// 64バイト書き込みを8回繰り返す
for ( uint8_t i=0; i<8; i++) {
for ( uint8_t j=0; J<64; J++) {
// データをセット
TABLAT = buffer[i*64 + j];
}
// 64バイト書き込み
EECON1bits.WREN = 1; // メモリ書き込み許可
EECON1bits.WWPROG = 0; // 64バイト書き込み指定
EECON1bits.FREE = 0; // 処理を書き込みに指定
INTCONbits.GIE = 0; // 割り込み禁止
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1; // 書き込み
INTCONbits.GIE = 1; // 割り込み許可
}
}
すみません、データシートで判断するしかありませんのでこの程度の回答になります。
なお、フラッシュメモリに書き込みますので、アドレスには注意いただければと思います。
アドレスの最初の方と最後の方はシステムが使用しますので、空いているところを使用します。
(データシートp.117〜118にメモリマップがあります)
今回もとても丁寧な回答いただき本当にありがとうございます!!
ただ、ためしてみましたが、まだ動いていません。
おそらく原因と思われるのは下記の2点です。
1_アドレスの設定が正しくない?データシートをみてもどこが空いているのかわかりませんでした。MPLABで吸い出してみながら空いてそうなアドレスに設定しています。
2_下記こみのところに、asm(“TBLWTPOSTINC”);//こういったコマンドは必要ないでしょうか?
また検証してわかったことがあったらご報告させていただきますね。
*何を作っているかですが、ごめんなさい。言えないんです。。
下記に現在のコードを載せますので、もしおかしなところがあれば教えていただけないでしょうか。
型については自分の環境用に変更しています。
//————————————————————————————–
//
char buffer[512];
//—————————————————–
//EEPROM_1Byte_読み込み
//—————————————————–
unsigned char FLASH_ReadByte(unsigned long flashAddr)
{
unsigned char FlashRead = 0;
TBLPTRU = (char)((flashAddr & 0x00FF0000) >> 16);
TBLPTRH = (char)((flashAddr & 0x0000FF00)>> 8);
TBLPTRL = (char)(flashAddr & 0x000000FF);
asm(“TBLRD”);
FlashRead = TABLAT;
return FlashRead;
}
//—————————————————–
//EEPROM_512Byte_読み込み //60000=1D4BE ~ 60511=1D8BC
//—————————————————–
// 指定アドレスから512バイト読み出す関数
// bufferは呼び出す側で uint8_t buffer[512]; で確保する
// 書き込みは512バイトブロック単位なので、アドレスは512単位で指定 (0, 512, 1024…)
void FLASH_Read512Bytes(unsigned long flashAddr, unsigned char *buffer)
{
TBLPTRU = (char)((flashAddr & 0x00FF0000) >> 16);
TBLPTRH = (char)((flashAddr & 0x0000FF00) >> 8);
TBLPTRL = (char)(flashAddr & 0x000000FF);
for( short i = 0; i < 512; i++ ) { // 読み出し命令 // アドレスはポストインクリメント // ただし最後の読み出しはインクリメントなしの TBLRD にする必要があるかも if(i == 511){ asm("TBLRD"); }else{ asm("TBLRDPOSTINC"); } // 読み出したデータはTABLATに入っている buffer[i] = TABLAT; } } //----------------------------------------------------- //EEPROM_消去 //----------------------------------------------------- // 指定アドレスから512バイト消去 void FLASH_EraseBlock(unsigned long baseAddr) { TBLPTRU = (char)((baseAddr & 0x00FF0000) >> 16);
TBLPTRH = (char)((baseAddr & 0x0000FF00)>> 8);
TBLPTRL = (char)(baseAddr & 0x000000FF);
EECON1bits.WREN = 1; // メモリ書き込み許可
EECON1bits.FREE = 1; // 処理をメモリ消去に指定
INTCONbits.GIE = 0; // 割り込み禁止
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1; // 消去
while(EECON1bits.WR == 1){}//処理終了待ち
EECON1bits.WREN = 0; // メモリ書き込み許可無効
INTCONbits.GIE = 1; // 割り込み許可
}
//—————————————————–
//EEPROM_512Byte_書き込み
//—————————————————–
// 512バイト書き込み
// 書き込むデータをbuffer[512]にセットする
// 書き込みアドレスは512バイトf単位 (0, 512, 1024…)
void FLASH_WriteBlock(unsigned long writeAddr, unsigned char *buffer)
{
// 書き込む前にメモリ消去
FLASH_EraseBlock(writeAddr);
// アドレス指定
TBLPTRU = (char)((writeAddr & 0x00FF0000) >> 16);
TBLPTRH = (char)((writeAddr & 0x0000FF00)>> 8);
TBLPTRL = (char)(writeAddr & 0x000000FF);
// 512バイトのデータ書き込み
// 64バイト書き込みを8回繰り返す
for (char i=0; i<8; i++) {
for (char j=0; j<64; j++) {
// データをセット
TABLAT = buffer[i*64 + j];
//asm("TBLWTPOSTINC");//こういったコマンドは必要ないでしょうか?
}
// 64バイト書き込み
EECON1bits.WREN = 1; // メモリ書き込み許可
EECON1bits.WWPROG = 0; // 64バイト書き込み指定
EECON1bits.FREE = 0; // 処理を書き込みに指定
INTCONbits.GIE = 0; // 割り込み禁止
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1; // 書き込み
while(EECON1bits.WR == 1){}//処理終了待ち
EECON1bits.WREN = 0; // メモリ書き込み許可無効
INTCONbits.GIE = 1; // 割り込み許可
}
}
//-----------------------------------------
*メインでの呼び出し
FLASH_Read512Bytes(0x3000,&buffer[0]);
buffer[100] = 5;
FLASH_WriteBlock(0x3000,&buffer[0]);
short aaa = (short)FLASH_ReadByte(0x3100);//<==ここが何を書いても255になります。
回答が遅くなりすみませんでした。
すみません。TBLWTPOSTINCは必要そうです。ただ、PIC18F67J94のデータシート以外に、アプリケーションノートを探してみたところ、AN1095がありました。以下のPDFファイルです。
http://ww1.microchip.com/downloads/en/AppNotes/01095D.pdf
これによると、どうもユーティリティコードがリリースされているようで、これを使用すると簡単にEEPROMライクな使い方ができるようです。
以下がライブラリとサンプルコードになります。8ビット版のコードで問題ないと思いますが、すみません、実機確認できませんので情報のみとなります。
http://ww1.microchip.com/downloads/en/AppNotes/DEE%20Emulation%208-bit%20v1.0.0.zip
情報ご提供いただき本当にありがとうございます。
最初に教えていただいたコードで1バイトのみの記録になりますが成功しました!本当に本当にありがとうございます。書き込みの際のコードを載せておきますね。
void FLASH_WriteBlock(unsigned long writeAddr, unsigned char *buffer)
{
// 書き込む前にメモリ消去
FLASH_EraseBlock(writeAddr);
// アドレス指定
TBLPTRU = (char)((writeAddr & 0x00FF0000) >> 16);
TBLPTRH = (char)((writeAddr & 0x0000FF00)>> 8);
TBLPTRL = (char)(writeAddr & 0x000000FF);
// 1バイト書き込み
TABLAT = buffer[0];// データをセット
asm(“TBLWT”);// データを書き込み
// 1バイト書き込み
EECON1bits.WREN = 1; // メモリ書き込み許可
EECON1bits.WWPROG = 1; // 2バイト書き込み指定
EECON1bits.FREE = 0; // 処理を書き込みに指定
INTCONbits.GIE = 0; // 割り込み禁止
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1; // 書き込み
while(EECON1bits.WR == 1){}//処理終了待ち
EECON1bits.WREN = 0; // メモリ書き込み許可無効
INTCONbits.GIE = 1; // 割り込み許可
}
情報共有いただきどうもありがとうございました!
ネットを調べてもフラッシュメモリをEEPROMとして使用する例はあまり見つからないので、貴重な情報です。動作確認済みということで私の方でもかなり参考になります。
こちらこそどうもありがとうございました。勉強になりました。