---
---
16F145xのMaster Synchronous Serial Port (MSSP) を使うと周辺機器と簡単にシリアル通信を行うことができます。MSSPは、SPIとI2Cという2種類の方式をサポートしていまが、ここでは、I2Cを利用したLCD表示器のインターフェースだけを取り上げます。このため、I2C マスターモードが対象です。 SCL SDAの2ピンが対応しています。I2Cモードで使うときには、この2つのピンはオープンドレインの回路にする必要があるため、TRISレジスタで入力モードに設定しておく必要があります。 I2Cに関連するレジスター一覧を下表に示します。
| レジスタ | bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
|---|---|---|---|---|---|---|---|---|
| INTCON | GIE | PEIE | TMR0IE | INTE | IOCIE | TMR0IF | INTF | IOCIF |
| PIE1 | TMR1GIE | ADIE | RCIE | TXIE | SSP1IE | - | TMR2IE | TMR1IE |
| PIR1 | TMR1GIF | ADIF | RCIF | TXIF | SSP1IF | - | TMR2IF | TMR1IF |
| PIE2 | OSFIE | C2IE | C1IE | - | BCL1IE | USBIE | ACTIE | - |
| PIR2 | OSFIF | C2IF | C1IF | - | BCL1IF | USBIF | ACTIF | - |
| SSPCON1 | WCOL | SSPOV | SSPEN | CKP | SSPM<3:0> | |||
| SSPCON2 | GCEN | ACKSTAT | ACKDT | ACKEN | RCEN | PEN | RSEN | SEN |
| SSPCON3 | ACKTIM | PCIE | SCIE | BOEN | SDAHT | SBCDE | AHEN | DHEN |
| SSPSTAT | SMP | CKE | D/A | P | S | WnR | UA | BF |
| SSPADD | ADD<7:0> | |||||||
| SSPBUF | MSSP Receive Buffer/Transmit Register | |||||||
| TRISC | TRISC7 | TRISC6 | TRISC5 | TRISC4 | TRISC3 | TRISC2 | TRISC1 | TRISC |
| レジスタ | BIT | 名 | 説明 | 1 | 0 |
|---|---|---|---|---|---|
| INTCON | 7 | GIE | 全インタラプトの使用許可 | 許可 | 否 |
| 6 | PEIE | 周辺機能インタラプトの使用許可 | 許可 | 否 | |
| PIE1 | 3 | SSP1IE | SSP インタラプトを使用許可 | 許可 | 否 |
| PIR1 | 3 | SSP1IF | SSP インタラプト フラグ | 発生 | 未発生 |
| PIE2 | 3 | BCL1IE | バス衝突 インタラプトを使用許可 | 許可 | 否 |
| PIR2 | 3 | BCL1IF | バス衝突 インタラプト フラグ | 発生 | 未発生 |
| SSPCON1 | 7 | WCOL | 送信時Bus衝突 | 発生 | なし |
| 6 | SSPOV | 受信時SSPBUFオーバーラン | 発生 | なし | |
| 5 | SSPOV | MSSPの動作 | ON | OFF | |
| <3:0> | SSPM | I2C Master modeは、0b1000 に設定 | |||
| SSPCON2 | 6 | ACKSTAT | ACK受信状況 | 未 | 受信済 |
| 5 | ACKDT | マスタ受信時のNACK/ACK指示 | NACK | ACK | |
| 4 | ACKEN | マスタ受信時のACKシーケンス開始 (自動リセット) | 開始 | 待ち | |
| 3 | RCEN | 受信機能を許可 | 許可 | 禁止 | |
| 2 | PEN | Stop 状況開始 (完了で自動リセット) | 開始 | 待ち | |
| 1 | RSEN | Repeat Start 状況開始 (完了で自動リセット) | 開始 | 待ち | |
| 0 | SEN | Start 状況開始 (完了で自動リセット) | 開始 | 待ち | |
| SSPCON3 | 7 | ACKTIM | ACKタイミング状況 | ACK内 | ACK外 |
| 6 | PCIE | Stop 状況割込み | 許可 | 禁止 | |
| 5 | SCIE | Start 状況割込み | 許可 | 禁止 | |
| 3 | SDAHT | SCL立下り後のSDA Hold Time | 300 nS | 100 nS | |
| SSPSTAT | 7 | SMP | Slew rate 制御の無効(100k用)/有効(400k用) | 100Hzk | 400kHz |
| 6 | CKE | スレッシュホールド電圧の異なるbusを使用 | SMbus | 標準 | |
| 5 | D/A | (Read only) 直前Byteの内容 | data | address | |
| 4 | P | (Read only) 直前にStop bit | なし | 検出 | |
| 3 | S | (Read only) 直前にStart bit | なし | 検出 | |
| 2 | R/W | (Read only) 送信状況 | 送信中 | 待機 | |
| 1 | UA | (Read only) 10-bit I2C mode only | |||
| 0 | BF | (Read only) SSPBUF状況 | フル | 空 |
| I2C クロック周波数 | Fosc | ||
|---|---|---|---|
| 48MHz | 16MHz | 4MHz | |
| 400 KHz | 0x1D | 0x18 | --- |
| 100 KHz | 0x77 | 0x63 | 0x09 |
I2Cクロック周波数と、SSPADDへの設定値
秋月(ACM1602NI)I2C LCD表示器へ文字を表示するプログラムです。 i2cは双方向性バスで受信(スレーブ)側の応答を確認したり、送受信信号が衝突した場合などのエラーを検出する機能を持っています。しかし、このプログラムでは、これら機能をすべて省略し、表示データや制御コードを一方的にLCD表示器に送信しています
<回路図>
<プログラム>
// データをLCDに表示する。
// 秋月(ACM1602NI)I2C LCD表示器への書き込み
// 「LCD Disp Test」と液晶表示する。
// 1)Define I/O PORT
// --PIC16F1455-- --PIC16F1459--
// SDA : PORTC(1) pin 9 PORTB(4) pin13
// SCL : PORTC(0) pin10 PORTB(6) pin11
// 2)OSC
// 4MHz (内部クロック)
//
// Language: MPLABX XC8
// Target: PIC16F1455
// Copyright (c) 2012 iwamoto All Rights Reserved
//*********************************************************
#include <xc.h>
#define _XTAL_FREQ 4000000
//************* Config ***********************************
#pragma config FOSC = INTOSC, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
#pragma config BOREN = ON, CLKOUTEN = OFF, IESO = OFF, FCMEN = OFF
#pragma config WRT = OFF, CPUDIV = NOCLKDIV, USBLSCLK = 48MHz, PLLMULT = 3x
#pragma config PLLEN = DISABLED, STVREN = ON, BORV = LO, LPBOR = OFF, LVP = OFF
//********************************************************
void i2cByteWrite(char, char, char); // i2c byte送信
void i2cTxData(char); // i2c SSPBUF セット
void LCD_dat(char); // 1文字表示
void LCD_cmd(char); // コマンド出力
void LCD_clr(void); // 全消去
void LCD_int(void); // 初期化
void LCD_str(char *); // 文字列表示
void LCD_ROMstr(const char *); // ROM文字列表示
void LCD_posyx(char,char); // カーソル位置指定
void LCD_hex(char); // 16進文字変換表示
//////////// Main //////////////////////////////////
void main(void){
char msgStart[] ="LCD Test";
unsigned char num = 0;
OSCCON = 0b00110100; // 内部クロック4Mhz
ANSELC = 0; // PortCをデジタルI/Oにする
TRISC = 0b11111111; // PortCを入力I/Oにする
SSPCON1 = 0x28; // I2Cマスターモード指定
SSPSTAT = 0x00;
SSPADD = 0x09; // I2Cクロック周波数100KHz for 4MHz
LCD_int(); // LCD初期化
// ------------------------------------------------------
LCD_str(msgStart); // 1行目に表示
LCD_dat(' ');
LCD_dat('!');
// ------------------------------------------------------
LCD_posyx(1,0); // 2行目に表示
LCD_ROMstr("1234567890123456");
while(1){
LCD_posyx(0,14); // 1行14文字目に表示
LCD_hex(num++); // 数値を表示し、+1する
__delay_ms(1000); // 1000msec
}
}
//-------- i2cで1byteデータを送信する -----------------------
// 以下の引数が必要
// addr : Slaveのアドレス
// cont : Slaveへ制御コード
// data : 送信するデータ
// NACKやBus衝突などの対応は行っていない
// -----------------------------------------------------------
void i2cByteWrite(char addr, char cont, char data){
SEN = 1; // Start condition 開始
while(SEN); // Start condition 確認
i2cTxData(addr); // アドレス送信
i2cTxData(cont); // 制御コード送信
i2cTxData(data); // データ送信
SSP1IF = 0; // 終了フラグクリア
PEN = 1; // Stop condition 開始
while(PEN); // Stop condition 確認
}
//-------- SSPBUFに1文字保存し送信終了を待つ -----------------
void i2cTxData(char data){
SSP1IF = 0; // 終了フラグクリア
SSPBUF = data; // データセット
while(!SSP1IF); // 送信終了待ち
}
//-------- 1文字表示 --------------------------------------
void LCD_dat(char chr){
i2cByteWrite(0xA0, 0x80, chr);
__delay_us(60); // 60μsec
}
//-------- コマンド出力 --------------------------------------
void LCD_cmd(char cmd){
i2cByteWrite(0xA0, 0x00, cmd);
if(cmd & 0xFC) // 上位6ビットに1がある命令
__delay_us(60); // 60usec
else
__delay_ms(3); // 3msec ClearおよびHomeコマンド
}
//-------- 全消去 ------------------------------------------------
void LCD_clr(void){
LCD_cmd(0x01); //Clearコマンド出力
}
//-------- カーソル位置指定 --------------------------------------
void LCD_posyx(char ypos, char xpos){
unsigned char pcode;
switch(ypos & 0x03){ // 縦位置を取得
case 0: pcode=0x80;break; // 1行目
case 1: pcode=0xC0;break; // 2行目
case 2: pcode=0x94;break; // 3行目
case 3: pcode=0xD4;break; // 4行目
}
LCD_cmd(pcode += xpos); // 横位置を加える
}
//-------- 文字列出力 -----------------------------------------
void LCD_str(char *str){
while(*str) //文字列の終わり(00)まで継続
LCD_dat(*str++); //文字出力しポインタ+1
}
//-------- Rom 文字列出力 ------------------------------------
void LCD_ROMstr(const char *str){
while(*str) //文字列の終わり(00)まで継続
LCD_dat(*str++); //文字出力しポインタ+1
}
//-------- 16進文字変換表示 --------------------------------
void LCD_hex(char c){
const char hexch[] ="0123456789ABCDEF";
LCD_dat(hexch[c >> 4]); //上位4bit表示
LCD_dat(hexch[c & 0xF]); //下位4bit表示
}
//-------- 初期化 --------------------------------------
void LCD_int(void){
__delay_ms(100); // 電源安定するまでの時間
LCD_cmd(0x38); // 8bit 2行 表示命令モード
LCD_cmd(0x0C); // Display on Cursor=0 Blink=0
LCD_cmd(0x06); // Entry Inc/Dec=1 Shift=0
LCD_cmd(0x01); // Clear Display
}
*注意*
I2Cピンは、ICSP と共通ピンを使用しています。PICkitは内部でこの端子をプルダウンしているため、PICkit を接続したままでは、I2C機能が働きません。
プログラムの動作確認をするときには、必ずPICkitを回路から取り外します。