MCP23S08 SPI 8ビットIOエキスパンダーを、PIC16F18326で動かしてみました。
MCP23S08 ピン配列
MCP23S08 Hardware Address Pin
MCP23S08 レジスタ一覧
ハードウェアアドレスピンを有効にするため、IOCONレジスタのHAENビットをセットしています。
PIC16F18326 SPIモジュール master mode コード
SPIの書き込みと、読込は同時に行われています。入力と出力が同時にビット単位で行われています。デイジーチェーンという単語が、ぴったりします。I2Cよりも、単純でなので、速度が出せます。
SPIxBufに書込むと、すぐにクロックと同期してビットデータが出力されます。
読込は、空の8ビットクロックを出力すると、SPIxBufに8ビットデータが入力されるので、それを読みます。
マスター側が、1クロック出力すると、シフトレジスタは、MOSI端子から1ビット出力し、MISO端子から1ビット入力します。これを一つのシフトレジスタで行っています。
16F18326 SPI配線接続
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;
}