PIC16F18346の基本動作から応用プログラムまでを学びます。

ホーム
12F1822
16F1455
16F1459
18F14K50
18F26J50
dsPIC
その他
    
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
MSSP - I2C
2020-05-17

16F18346 には、2本の Master Synchronous Serial Port (MSSP) があり、MSSP を使うと周辺機器と簡単にシリアル通信を行うことができます。MSSPは、SPIとI2Cという2種類の方式をサポートしていまが、ここでは、I2Cを利用したLCD表示器とのインターフェースだけを取り上げます。このため、I2C マスターモードが対象です。I2Cに関連するレジスター一覧を下表に示します。

*注意*
SCL SDAの2ピンで通信を行いますが、これらは、PPSでどのピンを使用するかを指定できます。これらは双方向で使うため、ピンはオープンドレイン回路にし、TRISレジスタは入力モードに設定する必要があります。

I2C クロック PIC クロック
1M 4M 16M 32M
400 kHz - - 0x09 0x13
100 kHz - 0x09 0x27 0x4F

SSPxADDに設定する baudレート ジェネレータ(BRG)の
再読み込み値

Fclock =  Fosc
( SSPxADD + 1 ) x 4
レジスタ bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
INTCON GIE PEIE - - - - - INTEDG
PIE1 TMR1GIE ADIE RCIE TXIE SSP1IE BCL1IE TMR2IE TMR1IE
PIR1 TMR1GIF ADIF RCIF TXIF SSP1IF BCL1IF TMR2IF TMR1IF
PIE2 TMR6IE C2IE C1IE NVMIE SSP2IE BLC2IE TMR4IE NCO1IE
PIR2 TMR6IF C2IF C1IF NVMIF SSP2IF BLC2IF TMR4IF NCO1IF
SSPxCON1 WCOL SSPOV SSPEN CKP SSPM<3:0>
SSPxCON2 GCEN ACKSTAT ACKDT ACKEN RCEN PEN RSEN SEN
SSPxSTAT SMP CKE D/nA P S R/nW UA BF
SSPxADD baud レートクロック分周値
SSPxBUF 同期シリアルポート受信バッファ/ 送信レジス
SSPxCLKPPS - - - SSPxCLKPPS<4:0>
SSPxDATPPS - - - SSPxDATPPS<4:0>
RxyPPS - - - RxyTPPS<4:0>
レジスタ BIT 説明 1 0
INTCON 7 GIE 全インタラプトの使用許可 許可
6 PEIE 周辺機能インタラプトの使用許可 許可
PIE1 3 SSP1IE SSP1 インタラプトを使用許可 許可
PIR1 3 SSP1IF SSP1 フラグ 発生 未発生
SSPxCON1 7 WCOL 書き込みコリジョン検出 発生 未発生
6 SSPOV 受信オーバーフロー 発生 未発生
5 SSPEN SSP イネーブルビット 有効 無効
4 CKP クロック ストレッチ制御 Open LOW
3 SSPM3 1000 = I2C マスタモード、
クロック = FOSC / (4 * (SSPxADD+1))(4)
2 SSPM2
1 SSPM1
0 SSPM0
SSPxCON2 7 GCEN ジェネラルコール アドレス 有効 無効
6 ACKSTAT 肯定応答(ACK)信号を受信 YESS NO
5 ACKDT マスタ受信モード肯定応答(ACK) シーケンス 開始 アイドル
4 ACKEN 受信イネーブルビット (I2C マスタモードのみ) 有効 無効
3 PEN ストップ条件 (I2C マスタモードのみ) 開始 アイドル
2 RSEN 反復スタート条件 (I2C マスタモードのみ) 開始 アイドル
0 SEN スタート条件 (I2C マスタモードのみ) 開始 アイドル
SSPxSTAT 7 SMP スルーレート制御 (400 kHzは有効「0」にする) 無効 有効
6 CKE SMBus 仕様 有効 無効
0 BF SSPxBUFバッファフル ステータス フル

MSSPのプログラム例

<回路図>

I2Cを利用したLCD表示器(LCD-AQM0802A)「LCD Test / I2C com」と表示するプログラムを作成しました。通常 I2Cラインに取り付けるプルアップ抵抗は、PIC内の弱プルアップを利用しています。雑音の大きい環境で使用するときは、別途プルアップ抵抗を回路に取り付けてください。

<プログラム>

/*************************************
 * File: ADC sample
 *  データをLCDに表示する。
 *    秋月(ACM1602NI)I2C LCD表示器を使用
 *     1MHz (内部クロック)
 *     RB4 SDA
 *     RB6 SCL
 * PIC16F18346 MPLAB XC8 v2.10
 * Created on May 22, 2020, 2:37 PM
 **************************************/
//*********************************************************************
//  調整用定数
//*********************************************************************
#define CONTRAST  0x28          // for 3.3V
// #define CONTRAST  0x18       // for 5.0V

#include <xc.h>
#define _XTAL_FREQ 1000000      // delay_ms(x) のための定義

#pragma config FEXTOSC = OFF,RSTOSC = HFINT1  // HFINTOSC (1MHz)
#pragma config CLKOUTEN = OFF,CSWEN = OFF,FCMEN = OFF
#pragma config MCLRE = ON,PWRTE = OFF,WDTE = OFF,LPBOREN = OFF
#pragma config BOREN = OFF,BORV = LOW,PPS1WAY = OFF,STVREN = ON
#pragma config DEBUG = OFF
#pragma config WRT = OFF,LVP = ON,CP = OFF,CPD = OFF

//******************* プロトタイプ *******************************
void i2cByteWrite(char, char, char);
void i2cTxData(char);
void LCD_dat(char);
void LCD_cmd(char);
void LCD_clr(void);
void LCD_posyx(char,char);
void LCD_int(void);

void LCD_str(char *);
void LCD_ROMstr(const char *);

// ******************* main ******************************************
void main() {
    char msgStart[] ="LCD Test";

    TRISB4 = 1;           // RB4を入力
    TRISB6 = 1;           // RB6を入力
    ANSB4  = 0;           // RB4をデジタル
    ANSB6  = 0;           // RB6をデジタル
    ODCB4  = 1;           // RB4をオープンドレイン
    ODCB6  = 1;           // RB6をオープンドレイン

    SSP1CLKPPS = 0x0E;    // RB6をCLK入力に指定
    RB6PPS     = 24;      // RB6をCLK出力に指定
    SSP1DATPPS = 0x0C;    // RB4をDATに入力指定
    RB4PPS     = 25;      // RB4をDAT出力に指定

    // SSP1設定 -----------------------------------------------
    SSP1STAT = 0b10000000;     // スルーレート制御はOff
    SSP1ADD  = 1;              // クロック設定 125k@1MHz
    SSP1CON1 = 0b00101000;     // I2C Master modeにする
    // --------------------------------------------------------
    LCD_int();                 // LCDを初期化
    LCD_str(msgStart);         // LCD上段に"LCD Disp Test"
    LCD_posyx(1,1);            // 下段にカーソル移動
    LCD_ROMstr("I2C com");     // 下段に追記
    while(1);
}

//********************************************************************
// I2C 関連
//********************************************************************
//-------- ByteI2C送信
void i2cByteWrite(char addr, char cont, char data){
    SSP1CON2bits.SEN = 1;      // Start condition 開始
    while(SSP1CON2bits.SEN);   // Start condition 確認
    i2cTxData(addr);           // アドレス送信
    i2cTxData(cont);           // 制御コード送信
    i2cTxData(data);           // データ送信
    SSP1CON2bits.PEN = 1;      // Stop condition 開始
    while(SSP1CON2bits.PEN);   // Stop condition 確認
}
//-------- Data送信
void i2cTxData(char data){
    PIR1bits.SSP1IF = 0;       // 終了フラグクリア
    SSP1BUF = data;            // データセット
    while(!PIR1bits.SSP1IF);   // 送信終了待ち
}
//********************************************************************
// LCD 関連
//********************************************************************
//-------- 1文字表示
void LCD_dat(char chr){
    i2cByteWrite(0x7C, 0x40, chr);
    __delay_us(50);            // 50μsec
}
//-------- コマンド出力
void LCD_cmd(char cmd){
    i2cByteWrite(0x7C, 0x00, cmd);
    if(cmd & 0xFC)             // 上位6ビットに1がある命令
        __delay_us(50);        // 50usec
    else
        __delay_ms(2);         // 2msec 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;
        case 1:    pcode=0xC0;break;
    }
    LCD_cmd(pcode += xpos);
}
//-------- 初期化
void LCD_int(void){
    __delay_ms(100);
    LCD_cmd(0x38);             // 8bit 2行 表示命令モード
    LCD_cmd(0x39);             // 8bit 2行 拡張命令モード
    LCD_cmd(0x14);             // OSC  BIAS 設定1/5
                               // コントラスト設定
    LCD_cmd(0x70 + (CONTRAST & 0x0F));
    LCD_cmd(0x5C + (CONTRAST >> 4));
    LCD_cmd(0x6B);             // Ffollwer
    __delay_ms(100);
    __delay_ms(100);
    LCD_cmd(0x38);             // 表示命令モード
    LCD_cmd(0x0C);             // Display On
    LCD_cmd(0x01);             // Clear Display
}
//-------- 文字列出力
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
}