PIC16F1827 ブレッドボード配線7  DCモータの回転数を計測。

# CCP2モジュールで、DCモータの回転数を計測します。
<iframe width="560" height="315" src="https://www.youtube.com/embed/S2A3AbY8fW0?si=Ubv8FxB0PGdy9PZh" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

![IMG_4989.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/19ca61c1-0dd8-9ad5-fdc1-7d73645b931f.jpeg)

:::note info
前回は、ccp１モジュールのRB3(9番ピン)からPWM信号を出力し、オープンドレインでDCモータを回転させました。

**今回は、そのDCモータの回転数を、CCP2モジュール(RA7)を使って計測します。**

これで、回転数を用いた、簡単なフィードバック処理ができそうですが、フリーのコンパイラから出力される実行ファイルは、フラッシュメモリを９５％使用しているので、16F1827では、PID制御などを実装するには、スペックが足りそうにありません。

PIC18F26K22、Qシリーズなどを使えば、回転数を一定に保つような制御を行えると、思います。

その基礎固めに、とりあえず、**回転数の計測まで行いました。** 

コードのアルゴリズムは、18Fになっても基本的には同じ動作で、微妙にレジスタ名とビット名が違うだけで、動作仕様に大差はないはずです。

:::

:::note info
# 回転数を計測する回路。
![キャプチャー回路図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/a61b143c-7c43-c7d7-f8ef-73c67650188c.jpeg)
赤外線LEDと、フォトトランジスタを組み合わせた回路からの出力を、CCP2キャプチャー端子(RA7)で捉え、DCモータに取り付けた回転歯の**1枚の通過時間**を計測します。
![IMG_4991.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/6065df8b-8a11-268e-4e7b-be8243800127.jpeg)
:::

:::note info
# 回転数を計測するアルゴリズム
![CCPキャプチャーデータシート.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/64be0a58-71a4-095e-aa60-6fc8419cf465.jpeg)

まず、CCP2モジュール端子をオルタナティブピンセレクトで、RA7に設定します。
```C:CCP2の専用端子をRA7にする
    TRISA7=1;
    APFCON0bits.CCP2SEL=1;//RA7
```
この端子に、赤外線受光素子フォトトランジスタの出力を接続して、その信号の立ち上がりをキャプチャーのトリガーに設定します。

信号が立ち上がるたびに、割り込みが発生します。

その割込み処理関数の中で、CCP2に連動しているTimer1の16ビットカウンタの値を記録します。

2つの立ち上がり信号をキャプチャーしたら、その時間差が1枚の回転歯の通過時間になっているので、それをもとに、1分間の回転数、RPMを求めます。

```C:CCP2割り込み処理
//CCP2 Capture Mode
    if(PIE2bits.CCP2IE==1 && PIR2bits.CCP2IF)
    {
        PIR2bits.CCP2IF = 0;
        ccp2.data[ccp2.index]=CCPR2H;
        ccp2.data[ccp2.index]<<=8;
        ccp2.data[ccp2.index++]|=CCPR2L;
        if(ccp2.index==2)
        {
            ccp2.index=0;
            ccp2.finished=true;
            PIE2bits.CCP2IE=0;
            T1CONbits.TMR1ON = 0;
            TMR1H=0x00;
            TMR1L=0x00;
        }
    }
```

2回の立ち上がりエッジの時間差から、1枚の回転翼の通過時間がわかります。その時間を6倍すれば、1回転の時間がわかります。

```C:RPM計算
if(ccp2.finished)//CCP2モジュールで２つの立ち上がりエッジの時刻を取得ずみなら、
{
    ccp2.finished=false;
    if(ccp2.data[1]>ccp2.data[0])//2つ目の時刻のほうが1つ目より後なら、RPMを計算。
    {
        rpm = (ccp2.data[1]-ccp2.data[0]);
        rpm*=6;
        rpm = 500000/rpm * 60;
        //printf("%u %u\n",ccp2.data[1],ccp2.data[0]);//デバッグで値を確かめる。
        //printf("%u\n",rpm);
    }
    else
    {//回転が遅い場合。
        rpm=0;
    } 

    //LCDバーメーター表示
    Lcd_BarMeter(LcdDeviceAdd,rpm);
    //LCD数値表示
    length=sprintf(txt,"RPM:%4d",rpm);
    LCD_Printf(LcdDeviceAdd,txt,length,0xC0);
    //割込み復帰処理
    ccp2.data[1]=0x0000;
    ccp2.data[0]=0x0000;
    PIR2bits.CCP2IF=0;
    PIE2bits.CCP2IE=1;                
    T1CONbits.TMR1ON = 1;
}
```
Timer1の1msはシステムクロック１６Mhzの時、500カウント。1秒間では、500,000カウント。
**1秒間の回転数を求めた後、60を掛けて、RPMをもとめています。**
![オシロ波形１.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/c58cf88e-694b-f9a9-ebd2-ee051ee540e9.jpeg)
オシロで計測して183ヘルツの場合、6枚歯のエンコーダなので、183÷6×60=1830RPMとなります。
1827で計算した値は、LCDに表示されます。
動画で確認していただけると、オシロの周波数と、LCDの回転数表示が一致しているので、正しく計測できていることが確認できるはずです。
:::

:::note info
回転数をLCDにバーメータとして表示させています。
LCD液晶には、ユーザー登録メモリエリアが存在しています。そこにバーメータの表示図形を登録します。
```C:I2C_LCD_BarMeter.h
#ifndef I2C_LCD_BARMETER_H
#define	I2C_LCD_BARMETER_H

#ifdef	__cplusplus
extern "C" {
#endif
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "Peripheral.h"
#include "LCDdriver.h"
    
extern uint8_t LcdMemoryData[5];
extern void Lcd_Mem_Char_Set(uint8_t deviceAdd);
extern void Lcd_BarMeter(uint8_t deviceAdd, int32_t val32);



#ifdef	__cplusplus
}
#endif

#endif	/* I2C_LCD_BARMETER_H */
```
```C:I2C_BarMeter.c
#include "I2C_LCD_BarMeter.h"

//バーメータのLCD登録図形
uint8_t LcdMemoryData[5]={0x00,0x10,0x18,0x1C,0x1E};


//*******************************************************//
// LCD ユーザーRAMデータセット　バーメーター
// Ldc_Mem_Char_Set
// 引数:deviceAdd　I2Cデバイスアドレス
// 
//*******************************************************//
void Lcd_Mem_Char_Set(uint8_t deviceAdd)
{
    int i,j,cgram,num;
    
    num=0;
    for(i=0x00; i<=0x20; i+=0x08)
    {
        cgram=0x40|i;//CGRAM address set Command = 0x40
        LCD_Command(deviceAdd,cgram);
        for(j=0x00; j<=0x7; j++)
        {
            LCD_WriteData(deviceAdd,LcdMemoryData[num]);
            __delay_ms(10);
        }
        num++;
    }
}

//*******************************************************//
// LCDバーメーター表示
// Ldc_BarMeter
// 100(RPM/縦列)
//*******************************************************//
void Lcd_BarMeter(uint8_t deviceAdd, int32_t val32)
{
    uint8_t A,B,C,adram;
    
    A=val32/100;
    B=A/5;
    C=A%5;

    LCD_Command(deviceAdd,0x80);//DDRAM開始アドレスセット
    for(adram=0; adram<B; adram++)
    {
        LCD_WriteData(deviceAdd,0xFF);//塗りつぶし
    }

    LCD_WriteData(deviceAdd,C);//Bar先頭　0?4列表示
    for(adram=B+1; adram<16; adram++)
    {
        LCD_WriteData(deviceAdd,' ');
    }  
}
```

:::

# コード
```C:Peripheral.h

#ifndef PERIPHERAL_H
#define	PERIPHERAL_H

#include <xc.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define _XTAL_FREQ 16000000

#ifdef	__cplusplus
extern "C" {
#endif

//**************************************************//    
//   USART 
//**************************************************//    
//#define BAUDRATE 19200
#define BAUDRATE 115200  
#define SET_SPBRG_DATA  (((unsigned int)((_XTAL_FREQ/16)/BAUDRATE))-1)
#define SET_SPBRG_DATA1 (((unsigned int)((_XTAL_FREQ/4)/BAUDRATE))-1)    
extern void USART_INIT(void);
extern void putch(uint8_t byte);
extern uint8_t getch(void);
#define rxLength 30
typedef struct {
    uint8_t length;
    uint8_t rxBuf[30];
    bool rxCompleted;
}_usart;    
extern _usart usart;

#define rxBuffer_length 30
typedef struct{
    uint8_t ch;
    uint8_t Buffer[rxBuffer_length];
    uint8_t index;
    uint8_t Completed;
}_rx;
extern _rx rx;

//************************************************//
//Timer0 8bits timer
//************************************************//
#define T0_UP 1
#define T0_STOP 0
typedef struct{
    bool up;
    uint16_t cnt;
}_tm0;    
extern _tm0 tm0;

//************************************************//
//Timer1
//************************************************//
#define T1_UP 1
#define T1_STOP 0
typedef struct{
    bool up;
    uint16_t cnt;
}_tm;    
extern _tm tm1,tm4;

//************************************************//
//ADC
//************************************************//
#define ADC_Sampling_Num 10    
typedef struct{
    uint8_t Ch;
    uint16_t val;
    bool    Completed;
    uint16_t data[ADC_Sampling_Num+1];
    uint8_t index;
    uint16_t RMS;
    int32_t cal;
}_ADC;
extern _ADC ADC;

//***************************************************//
//CCP Capture
//***************************************************//
typedef struct{
    uint16_t data[3];
    uint8_t index;
    bool finished;
}_CCP_CAP;
extern _CCP_CAP ccp2;

extern void Timer0_INIT(void);
extern void Timer1_INIT(void);
extern void Timer2_INIT(void);
extern void Timer4_INIT(void);
extern void Interrupt_START(void);
extern void ADC_INIT(void);
extern void CCP1_PWM_Init(void);
extern void CCP2_Capture_Init(void);

#ifdef	__cplusplus
}
#endif

#endif	/* MYLIB_H */

```

```C:Peripheral.c
#include <pic16f1827.h>

#include "Peripheral.h"

/*-----------------------------------------
 *USART
 *-----------------------------------------*/
_usart usart;
_rx rx;
void USART_INIT(void)
{
    uint8_t buf;
    unsigned int brg;
    //SPBRG value set
    brg = SET_SPBRG_DATA1;
    SP1BRGL =(unsigned char) brg;
    SP1BRGH =(unsigned char) (brg>>8); 
    //TX-------------------------------------
    TXREG=0x00;
    //TXSTA SYNC:1 BRGH:1 TXEN:1 TX9:0
    buf =TXSTA & 0x83;
    TXSTA = buf | 0x24;
    //RX-------------------------------------
    //RCSTA  SPEN:1 RX9:0 CREN:1
    buf =RCSTA & 0x2F;
    RCSTA =buf | 0x90;
    //BAUR RATE CONTROL REGISTER
    BAUDCON = 0x08;
    //if interrupt is desired, TXIE set(PIE1)
    PIE1bits.TXIE=0;//disable
    //if interrupts are desired, RCIE set(PIE1)
    PIE1bits.RCIE=1;//enable
    
    usart.length=0;
    usart.rxCompleted=false;
}
void putch(uint8_t byte)
{
    while(!PIR1bits.TXIF)continue;
    TXREG=byte;
}

uint8_t getch(void)
{
    while(!PIR1bits.RCIF){};
    return RCREG;
}



/*---------------------------------------------------
 Timer0     8bits　Timer　
 ---------------------------------------------------*/
_tm0 tm0;
void Timer0_INIT(void)
{
    OPTION_REGbits.TMR0CS = 0;  //ClockSource:Fosc/4
    OPTION_REGbits.PSA = 0;     //assingned to the Timer0
    OPTION_REGbits.PS = 0b111;  //1:256
    TMR0=0x63;  //Fosc:32Mhz interval time 10ms
    INTCONbits.T0IE = 1;
    tm0.cnt=0;
}


/*---------------------------------------------------
 Timer1
 ---------------------------------------------------*/
 _tm tm1;
void Timer1_INIT(void)
{
    //TMR1L=0x0B;//16MHz 1ms PSC:1:8
    //TMR1H=0xFE;
    T1CON=0x31;    
    T1GCON=0x00;
    PIE1bits.TMR1IE=1;
    tm1.up=T0_STOP;
    tm1.cnt=0x0000;
}

/*-------------------------------------------
Timer2
--------------------------------------------*/
void Timer2_INIT(void)
{
    T2CONbits.T2OUTPS=0b0000;//post sc 1:16
    T2CONbits.T2CKPS=0b11;//PSC 1:16
    T2CONbits.TMR2ON=1;
    PR2=0xFA;//1ms matching.
    TMR2=0;
    PIR1bits.TMR2IF=0;
    PIE1bits.TMR2IE=1;
}

/*---------------------------------------------------
 Timer4
 ---------------------------------------------------*/
_tm tm4;
void Timer4_INIT(void)
{
    T4CONbits.T4OUTPS=0b1111;   //1:16 Postscaler
    TMR4=0;
    T4CONbits.T4CKPS=0b11;  //prescle1:64
    PR4=0x27;   //Fosc=16Mhz 10ms
    PIE3bits.TMR4IE=1;
    PIR3bits.TMR4IF=0;
    T4CONbits.TMR4ON=1;
    tm4.up=false;
    tm4.cnt=0;
}

/*---------------------------------------------------
 Timer6
 ---------------------------------------------------*/
void Timer6_INIT(void)
{
    T6CONbits.T6OUTPS=0b0000;//1:16 Postscaler
    T6CONbits.T6CKPS=0b11;//prescle1:16
    T6CONbits.TMR6ON=1;
    PR6=0xFA;
    TMR6=0;
    PIR3bits.TMR6IF=0;
    PIE3bits.TMR6IE=1;
}
//interrupt start
void Interrupt_START(void)
{
    INTCONbits.PEIE=1;
    INTCONbits.GIE=1;
}
/*---------------------------------------------------
 AD converter
 -------------------------------------------------------*/
_ADC ADC;
void ADC_INIT(void)
{
    uint8_t buf;
    
    //1.Configure PortA
    buf=TRISA;
    TRISA=buf|0x01;
    ANSELA|=0x01;
  
    //2.Configure the ADC module
    ADCON1bits.ADFM=1;      //Right justified.
    ADCON1bits.ADCS=0b101;  //FOSC/16  Select ADC conversion clock
    ADCON1bits.ADNREF=0;    //Vref-:AVss Configure voltage referrence 
    ADCON1bits.ADPREF=0b00; //Vref+ is connected to Vdd.
    ADCON0bits.CHS=0x00;    //RA0:Select ADC input channel
    
    //Turn on ADC module
    ADCON0bits.ADON=1;
    PIR1bits.ADIF=0;
    PIE1bits.ADIE=1;
    
    //ADC struct data initialize
    ADC.index=0;
    ADC.cal=0;
    ADC.Completed=false;
    
  
}

/*----------------------------------------------*/
//PWM
/*-----------------------------------------------*/

//RB3 PWM mode
void CCP1_PWM_Init(void)
{
    CCP1CONbits.CCP1M=0b1100;//PWM mode
    CCPTMRSbits.C1TSEL0=0b00;//Timer2 in PWM mode
    CCPR1L=0x00;
    TRISB3=0;
    //APFCON0bits.CCP1SEL=1;//RB0
    APFCON0bits.CCP1SEL=0;//RB3
}

 _CCP_CAP ccp2;
//RA7 Capture mode
void CCP2_Capture_Init(void)
{
    CCP2CONbits.CCP2M=0b0101;//Capture mode rising edge
    CCPR2H=0x00;
    CCPR2L=0x00;
    TRISA7=1;
    APFCON0bits.CCP2SEL=1;//RA7
    //interrupt enable
    PIE2bits.CCP2IE=1;
    PIR2bits.CCP2IF=0;
    //user variant
    ccp2.data[0]=0x0000;
    ccp2.data[1]=0x0000;
    ccp2.finished=false;
}

//RA3
void CCP3_PWM_Init(void)
{
    CCP3CONbits.CCP3M=0b1100;//PWMmode
    CCPTMRSbits.C3TSEL0=0b00;//Timer2in PWM mode
    CCPR3L=0x00;
    TRISA3=0;
}

//RA4
void CCP4_PWM_Init(void)
{
    CCP4CONbits.CCP4M=0b1100;//PWMmode
    CCPTMRSbits.C4TSEL0=0b00;//Timer2in PWM mode
    CCPR4L=0x00;
    TRISA4=0;
}

```

```C:Interrupt.h

#ifndef INTERRUPT_H
#define	INTERRUPT_H

#ifdef	__cplusplus
extern "C" {
#endif
    
    extern void __interrupt()isr();



#ifdef	__cplusplus
}
#endif

#endif	/* INTERRUPT_H */
```

```C:main.c

// PIC16F1827 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
//#pragma config FOSC = HS      // Oscillator Selection (HS Oscillator, High-speed crystal/resonator connected between OSC1 and OSC2 pins)
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP =ON      // Low-Voltage Programming Enable (Low-voltage programming enabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include "Interrupt.h"
#include "Peripheral.h"
#include "I2C_MSSP1.h"
#include "LCDdriver.h"
#include "I2C_LCD_BarMeter.h"

#define _XTAL_FREQ  16000000

void Oscillator_Init(void);
void Port_Init(void);

char txt[20];

void main(void)
{    
    uint8_t val=0x55;
    uint8_t counter=0x00;
    uint8_t length=0;
    uint32_t rpm;
            
    unsigned int t=5000;
    //Basic Hard Initialize
    Oscillator_Init();
    Port_Init(); 
    
    //Peripheral初期化
    Timer0_INIT();
    USART_INIT();
    I2C1_Reg_Init();  //I2C初期化
    Timer2_INIT();    //1khzサンプリング
    ADC_INIT();       //ADC初期化
    CCP1_PWM_Init();        //CCP1 PWM1初期化
    Timer1_INIT();
    CCP2_Capture_Init();    //CCP2 capture mode 初期化
    Timer4_INIT();
    
    //IOex:MCP23017初期化
    I2C1_b2Write(0x4C,0x0A,0x00);//コンフィグ
    I2C1_b2Write(0x4C,0x00,0x00);//PortA IOdirection
    I2C1_b2Write(0x4C,0x01,0x00);//PortB IOdirection
    
    //LCD SC1602　初期化
    LCD_Init(LcdDeviceAdd);//Fclock=100kHzで初期化。
    SSP1ADD=0x09;//Fosc=16MHz Fclock=400kHz
    Lcd_Mem_Char_Set(LcdDeviceAdd);
    //LCD_Printf(LcdDeviceAdd,"PIC16F1827",10,0x80);
    
    //割り込み許可
    Interrupt_START();
    
    while(1)
    {
        //USART interrupt processing
        /*if(usart.rxCompleted)
        {
            usart.rxCompleted=false;
            printf("%s\n",usart.rxBuf);
            usart.length=0;
            PIE1bits.RCIE=1;
        }*/
        
        //Timer0 interrupt processing
        if(tm0.up)
        {
            tm0.up=false;
            //LATBbits.LATB0=~LATBbits.LATB0;
            I2C1_b2Write(0x4C,0x14,val);//IOex:PortA
            I2C1_b2Write(0x4C,0x15,val);//IOex:PortB
            
            if(val==0x55)
                val=0xAA;
            else
                val=0x55;
            
            
         
            INTCONbits.TMR0IE=1;
        }   
        
        if(ADC.Completed)
        {
            ADC.Completed=false;

            //length=sprintf(txt,"%4d",ADC.data[0]);
            //LCD_Printf(LcdDeviceAdd,txt,length,0xCC);

            CCPR1L=ADC.data[0]/4;

            PIE1bits.ADIE = 1;
            PIE1bits.TMR2IE=1;
        }
        
        if(tm4.up)
        {
            tm4.up=false;
            if(ccp2.finished)
            {
                ccp2.finished=false;
                if(ccp2.data[1]>ccp2.data[0])
                {
                    rpm = (ccp2.data[1]-ccp2.data[0]);
                    rpm*=6;
                    rpm = 500000/rpm * 60;
                    //printf("%u %u\n",ccp2.data[1],ccp2.data[0]);
                    //printf("%u\n",rpm);
                }
                else
                {
                    rpm=0;
                } 
                Lcd_BarMeter(LcdDeviceAdd,rpm);
                
                length=sprintf(txt,"RPM:%4d",rpm);
                LCD_Printf(LcdDeviceAdd,txt,length,0xC0);
                
                ccp2.data[1]=0x0000;
                ccp2.data[0]=0x0000;
                PIR2bits.CCP2IF=0;
                PIE2bits.CCP2IE=1;                
                T1CONbits.TMR1ON = 1;
            }
            PIR3bits.TMR4IF=0;
            PIE3bits.TMR4IE=1;
        }
        
    }
    return;
}

void Oscillator_Init(void)
{
    OSCCONbits.SPLLEN=0;
    OSCCONbits.IRCF=0b1111;//16Mhz
    OSCCONbits.SCS=0b10;//InternalOscillator
}

void Port_Init(void)
{
    TRISA=0x00;
    ANSELA=0x00;
    TRISB=0x04;     //RX:RB2
    ANSELB=0x00;
    PORTA=0x00;
    PORTB=0x00;  
    //Altanative Pin Selective
    APFCON0bits.RXDTSEL=1;  //RX:RB2
    APFCON1bits.TXCKSEL=1;  //TX:RB5
    //I2Cpin
    TRISBbits.TRISB1=1;
    TRISBbits.TRISB4=1;
    //PWM1pin
    APFCON0bits.CCP1SEL=0; //CCP1:RB3
}

```

```C:I2C_MSSP1.h
#ifndef I2C_MSSP1_H
#define	I2C_MSSP1_H
#include <xc.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#define _XTAL_FREQ  16000000

#ifdef	__cplusplus
extern "C" {
#endif
//-----------------------------------------------
#define I2C1_Ack   0//SSP1CON2.ACKDT master:receive mode
#define I2C1_NoAck 1//SSP1CON2.ACKDT master:receive mode
extern void I2C1_Reg_Init(void);
extern bool I2C1_Start(void);
extern bool I2C1_ReStart(void);
extern bool I2C1_Stop(void);
extern bool I2C1_Tx_Buffer_Write(uint8_t _byte_data);
extern uint8_t I2C1_Rx_Buffer_Read(void);
extern bool I2C1_Wait_Ack(void);
extern bool I2C1_Send_Ack(uint8_t _Ack_Nack);
extern bool I2C1_CheckIdle(void);
extern bool I2C1_ClearSSPxIF(void);
//------------------------------------------------
//-----------------------------------------------
extern bool I2C1_b1Write(uint8_t _device_add, uint8_t _data);
extern uint8_t I2C1_b2Write(uint8_t _device_add, uint8_t _data1, uint8_t _data2);
extern uint8_t I2C1_DataBuffer[10];
extern bool I2C1_bnWrite(uint8_t _device_add, uint8_t *_data, uint8_t _len);
extern uint8_t I2C1_b1Read(uint8_t _device, uint8_t _data1);
extern uint16_t I2C1_b2Read(uint8_t _device_add, uint8_t _data1);
extern bool I2C1_bnRead(uint8_t _device_add, uint8_t _data1,uint8_t _len);
//-----------------------------------------------
#ifdef	__cplusplus
}
#endif

#endif	/* I2C_MSSP1_H */
```
```C:I2C_MSSP1.c
#include "I2C_MSSP1.h"


uint8_t I2C1_DataBuffer[10];//読取りバッファ

//--------------------------------------------
//I2C1 MSSP1初期化
//--------------------------------------------
void I2C1_Reg_Init(void)
{
    SSP1STAT=0xC0;
    SSP1CON1=0x38;
    SSP1CON3=0x00;
    //SSP1ADD=0x50;//Fosc=16Mhz Fclock=50kHz
    SSP1ADD=0x27;//Fosc=16MHz Fclock=100kHz
    //SSP1ADD=0x09;//Fosc=16MHz Fclock=400kHz
    PIE1bits.SSP1IE=0;
}

//--------------------------------------------
//I2C1 MSSP1スタートコンディション発令
//--------------------------------------------
bool I2C1_Start(void)
{
    if(!I2C1_CheckIdle()) return false;
    SSP1CON2bits.SEN=1;
    if(!I2C1_ClearSSPxIF())return false;
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    return true;
}

//--------------------------------------------
//I2C1 MSSP1リスタートコンディション発令
//--------------------------------------------
bool I2C1_ReStart(void)
{
     //restart
    if(!I2C1_CheckIdle()) return false;
    SSP1CON2bits.RSEN=1;
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    return true;
    
}

//--------------------------------------------
//I2C1 MSSP1ストップコンディション発令
//--------------------------------------------
bool I2C1_Stop(void)
{
     if(!I2C1_CheckIdle()) return false;
    SSP1CON2bits.PEN=1;
    if(!I2C1_ClearSSPxIF())return false;
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    return true;
}

//--------------------------------------------
//I2C1 MSSP1　送信バッファ書込み
//--------------------------------------------
bool I2C1_Tx_Buffer_Write(uint8_t _byte_data)
{
    if(!I2C1_CheckIdle()) return false;
    SSP1BUF=_byte_data;
    if(!I2C1_ClearSSPxIF())return false;
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    return true;
}

//--------------------------------------------
//I2C1 MSSP1 受信バッファ読み出し
//--------------------------------------------
uint8_t I2C1_Rx_Buffer_Read(void)
{
    uint8_t ret, cnt;
    ret=0xFF;
    cnt=0;
    if(!I2C1_CheckIdle()) return false;
    SSP1CON2bits.RCEN=1;
    while(!SSP1STATbits.BF){
        __delay_us(3);
        cnt++;
        if(cnt==100)return false;
    }
    if(!I2C1_CheckIdle()) return false;
    ret = SSP1BUF;
    if(!I2C1_ClearSSPxIF())return false;
    return ret;
}

//--------------------------------------------
//I2C1 MSSP1　ACK確認
//--------------------------------------------
bool I2C1_Wait_Ack(void)
{
    //while(SSP1CON2bits.ACKSTAT);
    if(SSP1CON2bits.ACKSTAT)
    {
        return false;//error
        
    }else
    {
        return true;//success
    }
}

//--------------------------------------------
//I2C1 MSSP1　ACK送信
//--------------------------------------------
bool I2C1_Send_Ack(uint8_t _Ack_Nack)
{
    if(!I2C1_CheckIdle()) return false;
    SSP1CON2bits.ACKDT=_Ack_Nack;
    SSP1CON2bits.ACKEN=1;   
    if(!I2C1_ClearSSPxIF())return false;
    return true;
}

//--------------------------------------------
//I2C1 MSSP1　アイドリングチェック
//--------------------------------------------
bool I2C1_CheckIdle(void)
{
    uint8_t cnt=0;
    while(SSP1STATbits.R_nW || SSP1CON2 & 0x1F)
    {
    /*    __delay_us(2);
        cnt++;
        if(cnt==100) 
            return false;*/
    };//MSSP1 is idle mode
    return true;
}

//--------------------------------------------
//I2C1 MSSP1 SSP1IFフラグクリア
//--------------------------------------------
bool I2C1_ClearSSPxIF(void)
{
    uint8_t cnt=0;
    while(!PIR1bits.SSP1IF)
    {
    /*    __delay_us(2);
        cnt++;
        if(cnt==100)
            return false;*/
    };
    PIR1bits.SSP1IF=0;
    return true;
}


//-----------------------------------------------------------------


//----------------------------------------------------
//  I2C1 MSSP1 1バイト送信
//_device_add:I2Cスレーブアドレス
//_data:送信データ
//----------------------------------------------------
bool I2C1_b1Write(uint8_t _device_add, uint8_t _data)
{  
    //start condition
    I2C1_Start();
    //send device address
    I2C1_Tx_Buffer_Write(_device_add);
    if(!I2C1_Wait_Ack())
        return false;
    //send data
    I2C1_Tx_Buffer_Write(_data);
    if(!I2C1_Wait_Ack())
        return false;
    //Stop condition
    I2C1_Stop();
    return true;
}

//----------------------------------------------------
//  I2C1 MSSP1 2バイト送信
//_device_add:I2Cスレーブアドレス
//_data1:送信データ
//_data2:送信データ
//----------------------------------------------------
uint8_t I2C1_b2Write(uint8_t _device_add, uint8_t _data1, uint8_t _data2)
{
    //start condition
    I2C1_Start();
    //send device address
    I2C1_Tx_Buffer_Write(_device_add);
    if(!I2C1_Wait_Ack())
    {
        return 0xF1;
        //return false;
    }
    //send data
    I2C1_Tx_Buffer_Write(_data1);
    if(!I2C1_Wait_Ack())
    {    
        return 0xF2;
        //return false;
    }
        //send data
    I2C1_Tx_Buffer_Write(_data2);
    if(!I2C1_Wait_Ack())
    {
        return 0xF3;
        //return false;
    }
    //Stop condition
    I2C1_Stop();
    return true;
}

//----------------------------------------------------
//  I2C1 MSSP1 nバイト送信
//_device_add:I2Cスレーブアドレス
//_data:送信データ先頭アドレス
//_len:送信長
//----------------------------------------------------
bool I2C1_bnWrite(uint8_t _device_add, uint8_t *_data, uint8_t _len)
{
    //start condition
    I2C1_Start();
    //send device address
    I2C1_Tx_Buffer_Write(_device_add);
     if(!I2C1_Wait_Ack())
        return false;
    //send data
    do{
        I2C1_Tx_Buffer_Write(*_data);
        if(!I2C1_Wait_Ack())
        return false;
        _data++;
    }while(--_len);
    //Stop condition
    I2C1_Stop();
    return true;
}

//----------------------------------------------------
//  I2C1 MSSP1 １バイト受信
//_device_add:I2Cスレーブアドレス
//_data1:送信データ
//戻り値：受信バイトデータ
//----------------------------------------------------
uint8_t I2C1_b1Read(uint8_t _device_add, uint8_t _data1)
{
    uint8_t ret;
    //Start Condition
    I2C1_Start();
     //send device address
    I2C1_Tx_Buffer_Write(_device_add);
     if(!I2C1_Wait_Ack())
        return false;
    //send data
    I2C1_Tx_Buffer_Write(_data1);
     if(!I2C1_Wait_Ack())
        return false;
    //restart
    I2C1_ReStart();
      //send device address
    I2C1_Tx_Buffer_Write(_device_add|0x01);
     if(!I2C1_Wait_Ack())
        return false;
    //recieve data
    ret=I2C1_Rx_Buffer_Read();
    //send Nack
    I2C1_Send_Ack(I2C1_NoAck);
    //Stop Condition
    I2C1_Stop();
    return ret;
}

//----------------------------------------------------
//  I2C1 MSSP1 2バイト受信
//_device_add:I2Cスレーブアドレス
//_data1:送信データ
//戻り値：２バイトデータ
//----------------------------------------------------
uint16_t I2C1_b2Read(uint8_t _device_add, uint8_t _data1)
{
    uint16_t ret;
    //Start Condition
    I2C1_Start();
     //send device address
    I2C1_Tx_Buffer_Write(_device_add);
     if(!I2C1_Wait_Ack())
        return false;
    //send data
    I2C1_Tx_Buffer_Write(_data1);
     if(!I2C1_Wait_Ack())
        return false;
    //restart
    I2C1_ReStart();
    //send device address
    I2C1_Tx_Buffer_Write(_device_add|0x01);
     if(!I2C1_Wait_Ack())
        return false;
    //recieve data
    ret=I2C1_Rx_Buffer_Read();
    ret<<=8;
    //send Ack
    I2C1_Send_Ack(I2C1_Ack);
    //recieve data
    ret|=I2C1_Rx_Buffer_Read();
    //send Nack
    I2C1_Send_Ack(I2C1_NoAck);
    //Stop Condition
    I2C1_Stop();
    return ret;
}

//----------------------------------------------------
//  I2C1 MSSP1 nバイト受信
//_device_add:I2Cスレーブアドレス
//_data1:送信データ
//_len:受信長
//戻り値：受信の成否
//----------------------------------------------------
bool I2C1_bnRead(uint8_t _device_add, uint8_t _data1,uint8_t _len)
{
    uint8_t index=0;
    
    //Start Condition
    I2C1_Start();
     //send device address
    I2C1_Tx_Buffer_Write(_device_add);
     if(!I2C1_Wait_Ack())
        return false;
    //send data
    I2C1_Tx_Buffer_Write(_data1);
     if(!I2C1_Wait_Ack())
        return false;
    //restart
    I2C1_ReStart();
      //send device address
    I2C1_Tx_Buffer_Write(_device_add|0x01);
     if(!I2C1_Wait_Ack())
        return false;
    //recieve data
    do{
        I2C1_DataBuffer[index++]=I2C1_Rx_Buffer_Read();
        if(_len!=1)
        {
            I2C1_Send_Ack(I2C1_Ack);
        }
        _len--;
    }while(_len!=0);
    //send Nack
    I2C1_Send_Ack(I2C1_NoAck);
    //Stop Condition
    I2C1_Stop();
    return true;
}
```
```C:LCDDriver.h

#ifndef LCDDRIVER_H
#define	LCDDRIVER_H

#ifdef	__cplusplus
extern "C" {
#endif

#include <xc.h>  
#include "I2C_MSSP1.h"

#define _XTAL_FREQ  16000000
    
#define I2C_b1Write I2C1_b1Write    
#define CM	0	//Command Mode
#define DM	1	//Data Mode
#define WM  0	//出力WriteMode
#define RM  1   //入力ReadMode
#define High 1
#define Low  0
#define LcdDeviceAdd 0x4E//FC-113(RCF8574 address)
#define LcdDeviceAdd_AT 0x7E//RCF8574AT address    

#define LcdDDRAMAdd_firstline 0x80
#define LcdDDRAMAdd_secondline 0xC0
#define LcdDDRAMAdd_thirdline 0x94
#define LcdDDRAMAdd_forthline 0xD4
#define LcdCmd_Clear_all 0x01
    
//LCD SC1602構造体宣言
//Optical Device _OD
typedef union {
	uint8_t byt;
	struct{
		uint8_t RS:1;//LSB
		uint8_t RW:1;
		uint8_t EN:1;
		uint8_t LED:1;
		uint8_t D4:1;
		uint8_t D5:1;
		uint8_t D6:1;
		uint8_t D7:1;//MSB
	};
}_LCDdata;


extern _LCDdata LCDdata;
extern void LCD_send( uint8_t _device_add,uint8_t _data, uint8_t _mode);
extern void LCD_WriteData(uint8_t _device_add,uint8_t data);
extern void LCD_Command(uint8_t _device_add,uint8_t cmd);
extern void LCD_Init(uint8_t _device_add);
extern void LCD_Printf(uint8_t _device_add, uint8_t *string, uint8_t _length, uint8_t _DDRAM_Add);
extern void LCD_LED_Pow(uint8_t _device_add,uint8_t _led);
 


#ifdef	__cplusplus
}
#endif

#endif	/* LCDDRIVER_H */


```

```C:LCDdriver.c

#include "LCDdriver.h"

_LCDdata LCDdata;
//-----------------------------------------//
//関数名：LCD_send
//機能：LCDコマンド、データ送信
//data: Upper or Lower 4bits data
//_mode: CM 0 :Command Mode
//		   DM 1 :Data Mode
//_device_add
//-----------------------------------------//
void LCD_send(uint8_t _device_add,uint8_t _data, uint8_t _mode)
{   
    LCDdata.byt=0x00;
    LCDdata.LED=High;//バックライト点灯
    LCDdata.byt = LCDdata.byt|(_data& 0xF0);
    I2C_b1Write(_device_add,LCDdata.byt);
    NOP();
    NOP();
    LCDdata.RS=_mode;
    I2C_b1Write(_device_add,LCDdata.byt);
    NOP();
    NOP();
    LCDdata.EN=High;
    I2C_b1Write(_device_add,LCDdata.byt);
    NOP();
    NOP();
    NOP();
    LCDdata.EN=Low;
    I2C_b1Write(_device_add,LCDdata.byt);
    __delay_us(500);    
}

//----------------------------------------------//
//関数名：LCD_WriteData
//機能：データ送信
//引数１：_device_add スレーブアドレス(8bits)
//引数２：_data 送信データ
//----------------------------------------------//
void LCD_WriteData(uint8_t _device_add,uint8_t _data)
{
    LCD_send(_device_add, _data,DM);    //下位4ビット
    LCD_send(_device_add, _data<<4,DM); //上位４ビット
    __delay_us(40);
}

//----------------------------------------------//
//関数名：LCD_Command
//機能：コマンド送信
//引数１：_device_add スレーブアドレス(8bits)
//引数２：_cmd コマンド(8bits)
//----------------------------------------------//
void LCD_Command(uint8_t _device_add,uint8_t cmd)
{
    LCD_send(_device_add,cmd,CM);
    LCD_send(_device_add,cmd<<4,CM);
    __delay_us(40);
}

//----------------------------------------------//
//関数名：LCD_Init
//機能：LCD初期化
//引数１：_device_add スレーブアドレス
//----------------------------------------------//
void LCD_Init(uint8_t _device_add)
{
    __delay_ms(15);
    LCD_send(_device_add,0x30,CM);//8bits mode
    __delay_ms(5);
    LCD_send(_device_add,0x30,CM);//8bits mode
	__delay_us(100);
	LCD_send(_device_add,0x30,CM);//8bits mode three times setting
	LCD_send(_device_add,0x20,CM);//4bits mode 
	LCD_Command(_device_add,0x28);//Function setting.
	LCD_Command(_device_add,0x08);//display off
	LCD_Command(_device_add,0x01);//display clear
	LCD_Command(_device_add,0x06);//entry
	LCD_Command(_device_add,0x02);//cursor home
	LCD_Command(_device_add,0x0C);//display on
}

//----------------------------------------------//
//関数名：LCD_Printf
//機能：文字列表示
//引数１：_device_add スレーブアドレス(8bits)
//引数２：*string 文字列先頭アドレス
//引数３：文字列長
//引数４：表示位置アドレス
//----------------------------------------------//
void LCD_Printf(uint8_t _device_add, uint8_t *string, uint8_t _length, uint8_t _DDRAM_Add)
{
    LCD_Command(_device_add,_DDRAM_Add);
    do{
        LCD_WriteData(_device_add, *string);
        string++;
    }while(--_length);
}

//------------------------------------------------//
//関数名：LCD_LED_Pow
//機能：LCDバックライト点灯
//引数１：_device_add スレーブアドレス(8bits)
//引数２：_led 0:消灯　１：点灯
//------------------------------------------------//
void LCD_LED_Pow(uint8_t _device_add,uint8_t _led)
{
    uint8_t _data=0x01;
    
    LCDdata.byt=0x00;
    LCDdata.LED=_led;
    LCDdata.byt = LCDdata.byt|(_data& 0xF0);
    I2C_b1Write(_device_add,LCDdata.byt);
    NOP();
    NOP();
    LCDdata.RS=DM;
    I2C_b1Write(_device_add,LCDdata.byt);
    NOP();
    NOP();
    LCDdata.EN=High;
    I2C_b1Write(_device_add,LCDdata.byt);
    NOP();
    NOP();
    NOP();
    LCDdata.EN=Low;
    I2C_b1Write(_device_add,LCDdata.byt);
    __delay_us(500);
    
}

```

```C:I2C_LCD_BarMeter.h

#ifndef I2C_LCD_BARMETER_H
#define	I2C_LCD_BARMETER_H

#ifdef	__cplusplus
extern "C" {
#endif
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "Peripheral.h"
#include "LCDdriver.h"
    
extern uint8_t LcdMemoryData[5];
extern void Lcd_Mem_Char_Set(uint8_t deviceAdd);
extern void Lcd_BarMeter(uint8_t deviceAdd, int32_t val32);
```

```C:I2C_LCD_BarMeter.c
#include "I2C_LCD_BarMeter.h"

uint8_t LcdMemoryData[5]={0x00,0x10,0x18,0x1C,0x1E};


//*******************************************************//
// LCD ユーザーRAMデータセット　バーメーター
// Ldc_Mem_Char_Set
// 引数:deviceAdd　I2Cデバイスアドレス
// 
//*******************************************************//
void Lcd_Mem_Char_Set(uint8_t deviceAdd)
{
    int i,j,cgram,num;
    
    num=0;
    for(i=0x00; i<=0x20; i+=0x08)
    {
        cgram=0x40|i;//CGRAM address set Command = 0x40
        LCD_Command(deviceAdd,cgram);
        for(j=0x00; j<=0x7; j++)
        {
            LCD_WriteData(deviceAdd,LcdMemoryData[num]);
            __delay_ms(10);
        }
        num++;
    }
}

//*******************************************************//
// LCDバーメーター表示
// Ldc_BarMeter
// 100(RPM/縦列)
//*******************************************************//
void Lcd_BarMeter(uint8_t deviceAdd, int32_t val32)
{
    uint8_t A,B,C,adram;
    
    A=val32/100;
    B=A/5;
    C=A%5;

    LCD_Command(deviceAdd,0x80);//DDRAM開始アドレスセット
    for(adram=0; adram<B; adram++)
    {
        LCD_WriteData(deviceAdd,0xFF);//塗りつぶし
    }

    LCD_WriteData(deviceAdd,C);//Bar先頭　0?4列表示
    for(adram=B+1; adram<16; adram++)
    {
        LCD_WriteData(deviceAdd,' ');
    }  
}
```