PIC16F18326 SPI通信 with MCP23S08 

# MCP23S08 SPI 8ビットIOエキスパンダーを、PIC16F18326で動かしてみました。
<iframe width="560" height="315" src="https://www.youtube.com/embed/zpKWMpOc2C4?si=iOANHCp82JRlotsF" 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_4644.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/693e3c4d-6ffa-beb3-ee30-3d2ab59779d9.jpeg)
# MCP23S08 ピン配列
![MCP23S08ピン配.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/949aa115-3286-47dd-ddbe-8f80c300e671.jpeg)
# MCP23S08 Hardware Address Pin
A1,A0端子でSPIスレーブアドレスを指定できます。![ｱﾄﾞﾚｽピンプロトコル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/4a7637bb-3cef-6b30-0756-e2e46ee10157.png)
# MCP23S08 レジスタ一覧
![レジスタ一覧.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/88c2f5fc-05b7-5498-9cb4-258f68cdb68b.png)
ハードウェアアドレスピンを有効にするため、IOCONレジスタのHAENビットをセットしています。

# PIC16F18326 SPIモジュール　master mode コード
SPIの書き込みと、読込は同時に行われています。入力と出力が同時にビット単位で行われています。デイジーチェーンという単語が、ぴったりします。I2Cよりも、単純でなので、速度が出せます。
SPIxBufに書込むと、すぐにクロックと同期してビットデータが出力されます。
読込は、空の8ビットクロックを出力すると、SPIxBufに8ビットデータが入力されるので、それを読みます。
![SPI master diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/22911071-5bc1-8de8-a823-698fb07bf41a.png)

**マスター側が、1クロック出力すると、シフトレジスタは、MOSI端子から1ビット出力し、MISO端子から1ビット入力します。これを一つのシフトレジスタで行っています。**

16F18326　SPI配線接続
![16F18326ピン配.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/5b3db79b-7ce3-5d84-4bc8-e49bfc331268.jpeg)
PPS(Peripheral Pin Select)で、USARTとSPIに、IOピンを割り付けています。

```C:SPI.h
#ifndef SPI_H
#define	SPI_H

#ifdef	__cplusplus
extern "C" {
#endif
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "Peripheral18326.h"


#define SPI_SDO_TRIC TRISC2
#define SPI_SS LATCbits.LATC3
#define Low 0
#define High 1

#define MCP23Sadd 0x40
#define MCP23Sadd1 0x42
#define MCP23S_R 1
#define MCP23S_W 0

    
typedef struct{
    uint8_t ret;
    bool error;
}_SPI_RD;    

_SPI_RD SPI_RD;
void SPI_Init(void);    
bool SPI_Write(uint8_t _data);
_SPI_RD SPI_Read(void);
void SPI_Stop(void);
/*-----------------------------------*/
//MCP23S08 I/O expander
/*-----------------------------------*/ 

typedef enum 
{
        IODIR=0x00,
        IPOL,
        GPINTEN,
        DEFVAL,
        INTCN,
        IOCON,
        GPPU,
        mINTF,
        INTCAP,
        GPIO,
        OLAT=0x0A
}_reg;

extern void MCP23S08_Init(uint8_t _deviceAdd);
extern bool MCP23S08_SendControl(uint8_t _device_add, uint8_t _reg_add, uint8_t RW);
extern bool MCP23S08_WriteReg(uint8_t _device_add,uint8_t _reg_add, uint8_t _data);
extern uint8_t MCP23S08_b1ReadReg(uint8_t _device_add, uint8_t _reg_add);


#ifdef	__cplusplus
}
#endif

#endif	/* SPI_H */
```
```C:SPI.c
#include "SPI.h"

//*******************************
//MSSP SPI初期化
//*******************************
void SPI_Init(void)
{
    SSP1CON=0x2A;//SPI masterMode:Fosc/(4*(SSPADD+1))
    SSP1STAT=0x40;
    SSP1ADD=0x1D;//12Mhz 100kh
    //SSP1ADD=0x05;//12Mhz 500kh
    SPI_SS=High;
}

//*******************************
//MSSP SPI スタート(CS=>Low)
//*******************************
void SPI_Start(void)
{
    SPI_SS=Low;
}
//*******************************
//MSSP SPI バイト送信
//_data:送信バイト
//return:送信成功
//*******************************
bool SPI_Write(uint8_t _data)
{
    uint8_t buf;
    SSP1BUF=_data;//書込みとほぼ同時に出力
    while(!SSP1STATbits.BF);//8ビット出力完了？
    return true;
}
//*******************************
//MSSP SPIバイト読込
//return:_SPI_RD構造体
//*******************************
_SPI_RD SPI_Read(void)
{
    SPI_RD.error=false;
    SSP1BUF=0x00;//8bits空クロック出力
    while(!SSP1STATbits.BF);//8ビット入力完了？
    SPI_RD.ret=SSP1BUF;//受信データ取出し
    return SPI_RD;
}
//*******************************
//MSSP SPI　ストップ(CS=>High)
//*******************************   
void SPI_Stop(void)
{
    SPI_SS=High;
    __delay_ms(5);
}

/*-----------------------------------*/
//MCP23S08 I/O expander
/*-----------------------------------*/
//*******************************************
//MCP23S08 レジスタ初期化
//*******************************************
void MCP23S08_Init(uint8_t _deviceAdd)
{
    //IODIR:IOピンディレクション：出力
    MCP23S08_WriteReg(_deviceAdd,IODIR,0x00);
    
    //IOCONレジスタ
    //シーケンシャルリード：禁止
    //ハードウェアｱﾄﾞﾚｽ：許可
    MCP23S08_WriteReg(_deviceAdd,IOCON,0x28);
}

//*******************************
//MCP23S08 Controlbyte送信
//_device_add:ｽﾚｰﾌﾞｱﾄﾞﾚｽ
//_reg_add:レジスタアドレス
//RW:0:write 1:read
//*******************************
bool MCP23S08_SendControl(uint8_t _device_add, uint8_t _reg_add, uint8_t RW)
{
    bool ret=false;
   
    ret=SPI_Write(_device_add|RW);
    ret=SPI_Write(_reg_add);
    return ret;
}

//*******************************
//MCP23S08 レジスタ書込み
//_device_add:ｽﾚｰﾌﾞｱﾄﾞﾚｽ
//_reg_add:レジスタアドレス
//_data:書込みデータ
//*******************************
bool MCP23S08_WriteReg(uint8_t _device_add,uint8_t _reg_add, uint8_t _data)
{
    bool ret=false;
    SPI_Start();
    MCP23S08_SendControl(_device_add, _reg_add, MCP23S_W);
    ret=SPI_Write(_data);
    SPI_Stop();
    return ret;
}        


//*******************************
//MCP23S08 レジスタ読込み
//_device_add:ｽﾚｰﾌﾞｱﾄﾞﾚｽ
//_reg_add:レジスタアドレス
//return:_SPI_RD構造体(エラー番号、受信バイト)
//*******************************
uint8_t MCP23S08_b1ReadReg(uint8_t _device_add, uint8_t _reg_add)
{
    _SPI_RD RD;
    SPI_Start();
    MCP23S08_SendControl(_device_add,_reg_add,MCP23S_R);
    RD=SPI_Read();  
    SPI_Stop();
    return RD.ret;
}
```
```main.c

/* 
 * File:   main.c
 * Author: h
 *
 * Created on 2019/12/26
 */
// PIC16F18325 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
#pragma config FEXTOSC = OFF    // FEXTOSC External Oscillator mode Selection bits (Oscillator not enabled)
#pragma config RSTOSC = HFINT1  // Power-up default value for COSC bits (HFINTOSC (1MHz))
#pragma config CLKOUTEN = OFF   // Clock Out Enable bit (CLKOUT function is disabled; I/O or oscillator function on OSC2)
#pragma config CSWEN = ON      // Clock Switch Enable bit (The NOSC and NDIV bits cannot be changed by user software)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config MCLRE = ON     // Master Clear Enable bit (MCLR/VPP pin function is digital input; MCLR internally disabled; Weak pull-up under control of port pin's WPU control bit.)
#pragma config PWRTE = OFF       // Power-up Timer Enable bit (PWRT enabled)
#pragma config WDTE = OFF       // Watchdog Timer Enable bits (WDT disabled; SWDTEN is ignored)
#pragma config LPBOREN = OFF    // Low-power BOR enable bit (ULPBOR disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bits (Brown-out Reset disabled)
#pragma config BORV = LOW       // Brown-out Reset Voltage selection bit (Brown-out voltage (Vbor) set to 2.45V)
#pragma config PPS1WAY = ON     // PPSLOCK bit One-Way Set Enable bit (The PPSLOCK bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will cause a Reset)
#pragma config DEBUG = OFF      // Debugger enable bit (Background debugger disabled)

// CONFIG3
#pragma config WRT = OFF        // User NVM self-write protection bits (Write protection off)
#pragma config LVP = ON        // Low Voltage Programming Enable bit (High Voltage on MCLR/VPP must be used for programming.)

// CONFIG4
#pragma config CP = OFF         // User NVM Program Memory Code Protection bit (User NVM code protection disabled)
#pragma config CPD = OFF        // Data NVM Memory Code Protection bit (Data NVM code protection disabled)

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



#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "Peripheral18326.h"
#include "SPI.h"

#define LED PORTAbits.RA5
void portInit(void);

void __interrupt()isr()
{
   
    unsigned char _ch;
    INTCONbits.GIE=0;
    //USART1 受信割込み処理--------------------------------
    if(PIR1bits.RCIF)
    { 
        if(RC1STAbits.OERR==1)//overRun Error?
        {
            RC1STAbits.CREN=0;  
            RC1STAbits.CREN=1;            
        }
        _ch=getch();      //USART受信バッファから読み出し
        usart.rxBuf[usart.length]=_ch;//User受信バッファに格納
        usart.length++;     //受信長をｲﾝｸﾘﾒﾝﾄ
        if(usart.length>15) usart.length=0;
        if(_ch==0x0A)//\nを受信したら処理開始。
        {
            PIE1bits.RCIE=false;
            usart.rxBuf[usart.length-2]=0x00;
            usart.rxCompleted=true;//main内で処理。    
        }
    }
    
    //Timer0割込み処理100ms
    if(PIR0bits.TMR0IF)
    {     
        PIR0bits.TMR0IF=0;
        tm0.cnt++;
        if(tm0.cnt==5)
        {
            tm0.up=true;
            PIE0bits.TMR0IE=false;
            tm0.cnt=0;
        }else
        {
            TMR0L=0x5E;//100ms
        } 
    }
   
    INTCONbits.GIE=1;
}


int main(int argc, char** argv) {
  
    uint8_t cVal,i,retVal;
    uint8_t spiVal;
    //Oscillator Setting
    OSCCON1bits.NOSC=0b110;
    OSCCON1bits.NDIV=0b000;
    OSCFRQbits.HFFRQ=0b101; //100:8MHz 101:12Mhz
    //Peripheral initializing
    portInit();
    USART_INIT();
    timer0Init();
    SPI_Init();
    //SPI device initializing
    MCP23S08_Init(MCP23Sadd);
    printf("MCP23S08 Add=0x40\n");
    for(i=0x00; i<=0x0A; i++)
    {
        retVal = MCP23S08_b1ReadReg(MCP23Sadd,i);
        printf("[%02X]:0x%02X\n",i,retVal);
    }
    MCP23S08_Init(MCP23Sadd1);
    printf("MCP23S08 Add=0x42\n");
    for(i=0x00; i<=0x0A; i++)
    {
        retVal = MCP23S08_b1ReadReg(MCP23Sadd1,i);
        printf("[%02X]:0x%02X\n",i,retVal);
    }
    //interrupt start
    interruptStart();
    
    printf("Start18325\n\r");
 
    cVal=0;
    spiVal=0x55;
    while(1)
    {
        //USART受信割込み処理--------------------
       if(usart.rxCompleted)
       {
            usart.rxCompleted=false;;
            printf("len=%d\n",usart.length);
            printf("str:%s\n",usart.rxBuf);
            usart.length = 0;
            PIE1bits.RCIE=1;
            INTCONbits.GIE=1;
       }
       //Timer0割込み処理-----------------------
       if(tm0.up)
       {
            tm0.up=false;
            LED=~LED;
            printf("%d\n",cVal++);
            
            MCP23S08_WriteReg(MCP23Sadd,OLAT,spiVal);
            MCP23S08_WriteReg(MCP23Sadd1,OLAT,spiVal);
            if(spiVal==0x55)
                spiVal=0xAA;
            else
                spiVal=0x55;
            
            PIR0bits.TMR0IF=0;
            TMR0L=0x5E;
            PIE0bits.TMR0IE=1;
        }
    }
    return (EXIT_SUCCESS);
}

void portInit(void)
{
    //PORTA initialize
    TRISA = 0x08;   //RA3 is  input-only.
    ODCONA = 0x00;  //When anODCONA bit is set, the corresponding port output becomes an open-drain driver capable of sinking current only.
    ANSELA = 0x00;
    WPUA =0x00;
    PORTA=0x00;

    //PORTC initialize
    TRISC = 0x22;//RC5:RX RC1:SDI
    ODCONC =0x00;
    ANSELC=0x00;
    WPUC=0x08;
    PORTC=0xFF;
   

    PPSLOCK = 0x55;
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 0;
    RXPPS =0x15;    //RC5:RX
    RC4PPS=0x14;    //output TX
    TXPPS =0x14;    //RC4:TX
    
    //MSSP1 SPI
    SSP1DATPPS = 0x11;//MSSP1module DAT  RC1
    RC0PPS=0x18; //output SCK1
    RC2PPS=0x19; //output SDO1
    //PPS
    PPSLOCK = 0x55;
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 1;
}
```

```C:Peripheral18326.h

#ifndef MYLIBRARY18325_H
#define MYLIBRARY18325_H
        
#ifdef	__cplusplus
extern "C" {
#endif

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

//#define _XTAL_FREQ 8000000
#define _XTAL_FREQ 12000000
//USART---------------------------------------------------------------------
#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)

void USART_INIT(void);
void putch(uint8_t byte);
char getch(void);

typedef struct {
    uint8_t length;
    uint8_t rxBuf[30];
    bool rxCompleted;
}usartdata;
extern usartdata usart;

//Timer0---------------------------------------------------
typedef struct{
    bool up;
    uint8_t cnt;
}tm;
extern tm tm0;
extern void timer0Init(void);


//Interrupt------------------------------------------------------------------
extern void interruptStart(void);


#ifdef	__cplusplus
}
#endif

#endif
```
```C:Peripheral18326.c
#include "Peripheral18326.h"

/*-----------------------------------------
 *USART
 *-----------------------------------------*/
usartdata usart;
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-------------------------------------
    TX1REG=0x00;
    //TXSTA SYNC:1 BRGH:1 TXEN:1 TX9:0
    buf =TX1STA & 0x83;
    TX1STA = buf | 0x24;
    //RX-------------------------------------
    //RCSTA  SPEN:1 RX9:0 CREN:1
    buf =RC1STA & 0x2F;
    RC1STA =buf | 0x90;
    //BAUR RATE CONTROL REGISTER
    BAUD1CON = 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;
    TX1REG=byte;
}

char getch()
{
    char ch;
    while(!PIR1bits.RCIF){}
    ch=RC1REG;
    return ch;
}
/*---------------------------------------------------
 Timer0
 ---------------------------------------------------*/
tm tm0;
void timer0Init(void)
{
    TMR0L=0x61;     //Fosc:12Mhz 100ms counter value Low byte
    TMR0H=0xDB;     //Fosc:12Mhz 100ms counter value High byte
    T0CON0=0x90;    //T0EN:1 16bits counter:1     
    T0CON1=0x45;    //ClockSource:Fosc/4 PSC:1/32
    PIE0bits.TMR0IE=1;
    tm0.up=false;
    tm0.cnt=0;
}

void interruptStart(void)
{
    INTCONbits.GIE=1;
    INTCONbits.PEIE=1;
}
```