0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PIC16F18326 SPI通信 with MCP23S08

Last updated at Posted at 2024-05-15

MCP23S08 SPI 8ビットIOエキスパンダーを、PIC16F18326で動かしてみました。

IMG_4644.JPG

MCP23S08 ピン配列

MCP23S08ピン配.jpg

MCP23S08 Hardware Address Pin

A1,A0端子でSPIスレーブアドレスを指定できます。アドレスピンプロトコル.png

MCP23S08 レジスタ一覧

レジスタ一覧.png
ハードウェアアドレスピンを有効にするため、IOCONレジスタのHAENビットをセットしています。

PIC16F18326 SPIモジュール master mode コード

SPIの書き込みと、読込は同時に行われています。入力と出力が同時にビット単位で行われています。デイジーチェーンという単語が、ぴったりします。I2Cよりも、単純でなので、速度が出せます。
SPIxBufに書込むと、すぐにクロックと同期してビットデータが出力されます。
読込は、空の8ビットクロックを出力すると、SPIxBufに8ビットデータが入力されるので、それを読みます。
SPI master diagram.png

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

16F18326 SPI配線接続
16F18326ピン配.jpg
PPS(Peripheral Pin Select)で、USARTとSPIに、IOピンを割り付けています。

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 */
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;
}
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
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;
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?