こんにちは、ENGかぴです。
PIC16F1827はI2C(MSSP)通信機能を持っておりLCD等の外部機器と通信することができます。MCCによるI2C通信の実装例とLCDを使用したストップウォッチのソフトを作り経過時間を表示して動作確認しました。
LCDはAQM1602XA-RN-GBW(秋月電子)を使用しています。PIC16F1827で動作確認したことについてリンクをまとめています。
PICマイコン(PIC16F1827)で実現できる機能と解説リンクまとめ
I2Cを実装する
PIC16F1827の設定はMCCを使っています。MCCの使い方については下記記事にまとめています。
MPLAB Code Configurator(MCC)の追加と使い方
今回はMCCを使用してMSSP1(I2C)及びTMR0の設定を行っています。
System Moduleの設定
最初に共通事項であるSystem Moduleの設定を行います。MPLAB X IDEを起動しMCCのアイコンをクリックしてMCCを有効にします。

内部クロックを使用するためINTOSCを選択しています。クロック周波数は任意でもよいですが4MHz_HFを選択しています。クロックを高速にするほど消費電流が増えてしまいます。他にも設定項目はありますがクロックの設定のみとしています。
MSSP1(MSSP1)を設定する
MSSPはMaster Synchronous Serial Port の略称です。PICマイコンのI2C通信はMSSP機能に含まれています。Device Resources内のPeripherals欄のMSSP1を選択して追加します。

MSSP1を選択しSerial ProtocolをI2Cを選択するとModeやClockの設定画面が表示されます。PICマイコンがLCDに対してコマンドを送信するためMasterとなります。
I2C通信が完了したときに割り込みを発生させることができますが特に処理する必要はないため割り込みは使用していません。
PR:わからないを放置せず、あなたにあった最低限のスキルを身に着けるコツを教える テックジム 「書けるが先で、理解が後」を体験しよう!
TMR0を設定する
TMR0はDevice Resources内のPeripherals欄のTimerを選択しTMR0をクリックして追加します。

Timer Clock内でクロックに関する設定を行います。Clock Sourceは内部クロックを使用しているためFOSC(FOSC/4)を指定します。PrescalerはClock Sourceに対してTMR0のカウントアップする分周比を指定します。
Timer PeriodはTMR0がオーバーフローするタイミングを設定します。1ms毎にオーバーフロー割り込みが発生しますがCallback Function Rateを0x0A(10)を指定しているため10ms毎にコールバック関数が呼び出されます。
void mainTimer(void); //タイマー0割り込みのコールバック関数
//コールバック関数の使用例
TMR0_SetInterruptHandler(mainTimer); //コールバック関数を指定
上記例ではmainTimer()が10ms毎にコールされます。
MCCが生成した関数の使用例
#include "mcc_generated_files/examples/i2c1_master_example.h" //追加
uint8_t initmoji[2][16] ={"PIC16F1827-I2C"," Ver1.00"};
I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, FUNC1_SET ); //コマンドを送信
for( i = 0; i < sizeof(initmoji[1]); i++ ){
I2C1_Write1ByteRegister(SLAVE_ADRS,LINE1_ADRS,initmoji[1][i]); //1バイトずつ送信
}
MCCでI2Cの設定を行うとi2c1_master.cなどほかにexamplesフォルダが生成されます。フォルダー内のi2c1_master_example.cファイルに実装されている関数を使用するため#include “mcc_generated_files/examples/i2c1_master_example.h”を追加しています。
LCDは書き込み専用であるためWriteを使用します。LCDの初期化ではコマンド1バイト送信して設定するためI2C1_Write1ByteRegister()をします。
PR:RUNTEQ(ランテック )- マイベスト3年連続1位を獲得した実績を持つWebエンジニア養成プログラミングスクール
LCDの初期化と文字表示

LCDで文字を表示するために初期化を行います。秋月電子のHPに公開されているAQM1602XA-RN-GBWのLCD資料(参考資料)の例から変更している部分について説明します。
void LcdInit(void){
//Function set
I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, 0x38); //8ビットバス・2LINE表示
__delay_us(40);
//Function set
I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, 0x39); //拡張コマンド
__delay_us(40);
//Internal OSC frequency
I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, 0x14); //内部周波数調整
__delay_us(40);
//Contrast set
I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, 0x77); //コントラスト1
__delay_us(40);
//Power/ICON/Contrast control
I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, 0x54);//コントラスト2
__delay_us(40);
//Follower control
I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, 0x6C);//フォロワー制御
__delay_ms(250);
//Function set
I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, 0x38); //拡張コマンドをオフ
__delay_us(40);
//Clear Display
I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, CLR_DISP ); //ディスプレイをクリア
__delay_us(40);
//Display ON/OFF control
I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, DISP_ONOFF_SET ); //ディスプレイON/OFF
__delay_us(40);
}
電源がDC5Vを使用しているためコントラストは例の通りだと暗くなり(ビット表示が黒くなる)文字が見えなくなるため薄くなるように設定します。C0~C2を1としC4とC5が0になるようにビットをセットします。Contrast Setに0x77、Power/ICON/Contrast controlを0x54をセットしています。
Follower controlレジスタV0の増幅率の調整で使用します。
ST7032のデータシートを確認するとI2C使用時のレイアウトではOPF1、OPF2は0とするためFonに1をセットすると内部フォロアが有効になります。
続けてdisptbl配列に表示したい文字を格納しているものとして使用例を示しています。
#define LINE1_ADRS 0x40
for( i = 0; i < sizeof(disptbl[0]); i++ ){
I2C1_Write1ByteRegister(SLAVE_ADRS_LCD,LINE1_ADRS,disptbl[0][i]);
}

I2C1_Write1ByteRegister()関数でスレーブアドレスを送信した後にcontrol byteを指定しますが2-line interface protocolに従って書き込みアドレスと文字データを指定します。control byte以降に制御コードを書き込まないのでCoに0をセットしRSがHになるので0x40になります。R/WはW固定なのでLになります。
LCDのアドレスカウンタは書き込みごとに自動でインクリメントされるため表示する文字のサイズ分繰り返して文字を表示しています。
動作確認
PIC16F1827とLCDを接続して動作確認を行います。LCDにI2C通信してデータを書き込み表示します。
動作確認用の回路

I2C通信にはプルアップ抵抗が必要ですが変換基板に抵抗が実装されています。電源を投入するとLCDの初期化を行い1列目に「PIC16F1827-I2C」、2列目に「Ver1.00」を2秒間表示します。
2秒が経過するとストップウォッチアプリがスタートします。SW1を押すとカウント開始しもう一度SW1を押すと停止します。SW1を2秒間長押しするとカウントをクリアします。ピン設定は以下の通りです。

RB0はウィークプルアップしているため信号レベルが浮くことはありません。SW1を押すとLED3が点灯し押さない場合はLED3が消灯するようにします。PB0はINPUTでウィークプルアップ(WPU)設定しています。WPUを有効にするためにはRegistersのnWPUENをenabledにする必要があります。
Easy SetupでWPUにチェックするのみでは有効にならないので注意が必要です。Notificationsにワーニングで通知されます。
PR:次の一手があなたの未来を決める! アビリティクラウドーフリーランスを目指すあなたに向けたエンジニアのマッチングサービス。大手のエンドクライアントメインの企業が多く、業務内容も規模が大きいのが特徴
ストップウォッチを作る
TMR0を使ってスタートからストップまでの時刻を管理します。SW1を押すとtimcntの更新をスタートします。ShowDataSet()はtimcntをLCD表示用のテキストデータ(アスキーコード)に変換する関数です。
void ShowDataSet(void){
uint8_t msdata;
uint8_t sdata;
uint8_t ms10;
uint8_t ms1;
uint8_t s10;
uint8_t s1;
sdata = (timcnt / 100 ) % 60;
msdata = timcnt % 100;
ms10 = msdata / 10 + 0x30;
ms1 = msdata % 10 + 0x30;
s10 = sdata / 10 + 0x30;
s1 = sdata % 10 + 0x30;
disptbl1[1][11] = s10;
disptbl1[1][12] = s1;
disptbl1[1][13] = '.';
disptbl1[1][14] = ms10;
disptbl1[1][15] = ms1;
}
msdataはms単位(ミリ秒)の表示のためtimcntを100で割った余りの数を計算しています。小数点以下の10の位をテキスト表示するためmsdataを10で割った商に0x30(アスキーコードで0を示す)を加えてテキストデータに変換しています。
小数点以下の1の位はmsdataを10で割った余りであり0x30を加えてテキストデータに変換しています。
sdataはs単位(秒)の表示のためtimcntを100で割った商となりますが秒は60までの59秒までなので60で割った余りを計算しています。
sdataを10で割ると秒の10の位となり10で割った余りが1の位となるため、それぞれに0x30を加えてテキストデータに変換しています。
動作結果

電源を投入すると初期画面が2秒間表示されていることが確認できSW1を押すとカウントがスタートすることが確認できました。カウント中にSW1を押すとカウントがストップしSW1を長押しするとカウントがクリアされることも確認できました。
ソースコード全体
ソースコードは記事作成時点において動作確認できていますが、使用しているライブラリの更新により動作が保証できなくなる可能性があります。また、ソースコードを使用したことによって生じた不利益などの一切の責任を負いかねます。参考資料としてお使いください。
リンクからZIPファイル形式のファイルをダウンロードし、任意の場所に展開していただくとテキストファイルが生成されます。
main.cをコピーすると使用できます。本ソースコードはMPLAB X IDEにMCCのプラグインをインストールしていることが前提となります。MCCをインストールする方法は下記記事を参考にしてください。
MPLAB Code Configurator(MCC)の追加と使い方
関連リンク
PICマイコンを使ってマイコンのレジスタの設定やMPLAB X IDEのプラグインであるMCCを使用して動作確認したことについてまとめています。
PICマイコン(PIC12F675)で実現できる機能と解説リンクまとめ
PICマイコン(PIC16F1827)で実現できる機能と解説リンクまとめ
PR:
わからないを放置せず、あなたにあったスキルを身に着けるコツを教える テックジムPython入門講座の申込
最後まで、読んでいただきありがとうございました。
コントラストと関係で目安となる値は決まりますが実際に表示してみてC0~C5を調整するかFollower controlレジスタを調整するか検討するのが良いと思います。