ネット上にあったスーパーファミコンのコントローラの動作仕様が、おもしろそうだったので、PICマイコンで動かしてみました。
マイコンのスイッチ入力に、スーパーファミコンのコントローラが使えれば、操作性がよくなり、アプリケーションの幅が広がりそうだったので、トライしてみました。
使用したマイコン:PIC16F18326
USBシリアル変換器:MCP2221A
1.スーパーファミコンコントローラの基板
基板には、V520Bと印字された20ピンのICが乗っています。
このV520Bの仕様が良くわからないのですが、ネット情報を集めて、端子機能をまとめてみると以下のようになりました。
スーファミオリジナルのコネクタは、使用せず、ケーブルを切断。個別の信号線をXHコネクタ端子に変更しました。
基板のコネクタに合わせて、4ピンコネクタと、3ピンコネクタに分けて、PICマイコンと接続しています。
電源ピンは5v(白)、GNDピン(茶)です。
データ線は、全部で3本。CLOCK, P/S, DATA1
配線の色
GND | NC | NC | DATA1 | P/S | CLOCK | VCC(5V) |
---|---|---|---|---|---|---|
茶 | - | - | 赤 | オレンジ | 黄 | 白 |
・CLOCK端子(黄)は、V520Bの入力端子。
・P/S端子(オレンジ)は、パラレルインプットをラッチするトリガー入力端子。
・DATA1端子(赤)は、ボタン押下の16ビットデータのv520B出力端子。
<PIC16F18326側の処理>
1. 1ビット目は、P/S端子とClock端子を同期させて、1パルスを出力し、1ビットデータを取得。
・P/S端子で、データ先頭の同期をとります。
・P/S端子は、webによっては、ラッチLatch端子と呼んでいることから、パラレルインプットをここで取り込んでいると思います。
・P/S端子をLowに落とすことで、データが確定しているはずです。**
2. 2ビット目以降は、clock端子にパルスを出力、残り15ビットをシリアルデータでビットごとに、取得。
1.スーファミコントローラのタイミングチャート
3.PIC16F18326 ソースコード
押したボタンがシリアル出力されます。同時に押したボタンも、表示されます。
P/S:RC3(Output)
CLOCK:RC4(Outpu)
DATA1:RA4(Input)
//*******************************************//
// スーファミコントローラ制御
// PS:データ取得トリガー
// CLK:クロック
// DAT:データ(active low)
//*******************************************//
#ifndef SFAMICON_H
#define SFAMICON_H
#ifdef __cplusplus
extern "C" {
#endif
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
//define pins---------------------------
#define sFami_PS PORTCbits.RC3
#define sFami_CLK PORTCbits.RC4
#define sFami_DAT PORTAbits.RA4
//スーファミ構造体
typedef struct{
uint16_t data; //16bitsData
}_sFamiCon;
extern _sFamiCon SFC;
//functions------------------------------
extern void initSFamiCon(void);
extern uint16_t getSFamiConButtons(void);
extern void getSFamiConButtonName(uint16_t vButtonData);
#ifdef __cplusplus
}
#endif
#endif /* SFAMICONCONTROLLER_H */
#include "sFamiCon.h"
#include "peripheral.h"
//スーファミ構造体
_sFamiCon SFC;
//スーファミ初期化
void initSFamiCon(void)
{
sFami_PS=0;
sFami_CLK=0;
SFC.data=0;
}
//***********************************************
//スーファミコントローラ 押下ボタン取得関数
//return 16bitボタンデータ
//***********************************************
uint16_t getSFamiConButtons(void)
{
uint16_t buttonData; //押下ボタンデータ
uint8_t clockCount; //クロックカウンタ
//0.変数初期化-----------------------------------------------
buttonData=0x0000;
clockCount=0;
//1.通信開始PS出力&クロック-----------------------------
sFami_PS=1;
sFami_CLK=1;
__delay_us(10);
sFami_PS=0;
sFami_CLK=0;
__delay_us(10);
//ビットデータ取込み(1ビット目)----------------------------
buttonData|=sFami_DAT;
buttonData<<=1;
clockCount++;
//2.ビットデータ取込み(2ビット目以降16ビットまで)----------
do{
sFami_CLK=1;
__delay_us(5);
sFami_CLK=0;
// __delay_us(5);
buttonData|=sFami_DAT;
if(clockCount!=15)
{
buttonData<<=1;
}
clockCount++;
}while(clockCount!=16);
//16ビットデータ取込み終了------------------------------------
//3.押下ボタンデータを返信
return (buttonData&0xFFF0);//MSB16bits返信
}
//***********************************************
//スーファミコントローラ 押下ボタン名 取得関数
//vButtonData:押下ボタンデータ
//***********************************************
void getSFamiConButtonName(uint16_t vButtonData)
{
if((vButtonData&0x8000)==0x0)
printf("B ");
if((vButtonData&0x4000)==0x0)
printf("Y ");
if((vButtonData&0x2000)==0x0)
printf("SELECT ");
if((vButtonData&0x1000)==0x0)
printf("START ");
if((vButtonData&0x0800)==0x0)
printf("UP ");
if((vButtonData&0x0400)==0x0)
printf("DOWN ");
if((vButtonData&0x0200)==0x0)
printf("LEFT ");
if((vButtonData&0x0100)==0x0)
printf("RIGHT ");
if((vButtonData&0x0080)==0x0)
printf("A ");
if((vButtonData&0x0040)==0x0)
printf("X ");
if((vButtonData&0x0020)==0x0)
printf("L ");
if((vButtonData&0x0010)==0x0)
printf("R ");
printf("\n");
}
// 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 = ON // 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <PIC16f18326.h>
#include "peripheral.h"
#include "sFamiCon.h"
//Port初期化PPS割り付け
void port_init(void);
void __interrupt()isr()
{
if(PIR0bits.TMR0IF)
{
PIR0bits.TMR0IF=0;
TMR0L=0x56; //10msec
tm0.count++;
if(tm0.count==50)
{
tm0.count=0;
PORTAbits.RA5=~PORTAbits.RA5;
}
}
}
void main(void)
{
//Oscillator 初期化
OSCCON1bits.NOSC=0b110; //HFINTOSC(1MHz)
OSCCON1bits.NDIV=0b000; //divide 1
OSCFRQbits.HFFRQ=0b101; //100:8MHz 101:12Mhz
//ペリフェラル初期化
port_init();
USART_INIT();
Timer0_init();
//スーファミ初期化
initSFamiCon();
Interrupt_START();
while(1)
{
SFC.data = getSFamiConButtons();//スーファミボタンデータ取得
//どこが押されたか、照合---------------------------------
if(SFC.data!=0xFFF0)
getSFamiConButtonName(SFC.data);
__delay_ms(10);
}
}
//****************************************//
//PIC16F18326ポート初期化
//****************************************//
void port_init(void)
{
//PORTA initialize---------------
TRISA = 0x00;
TRISAbits.TRISA4=1;
ODCONA = 0x00;
ANSELA = 0x00;
WPUA =0x00;
PORTA=0x00;
//PORTC initialize---------------
TRISC=0x00;
TRISCbits.TRISC5=1;//RX
ODCONC =0x00;
ANSELC=0x00;
WPUC =0x00;
PORTC=0x00;
//PPS ペリフェラルピン割り付け-----
//USART
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 0;
RXPPS =0x15; //RC5:RX
RC2PPS=0x14; //RC2 output pin for TX
TXPPS =0x12; //RC2:TX
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 1;
}
#ifndef PERIPHERAL_H_
#define PERIPHERAL_H_
#include <xc.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
//oscillator speed 12MHz
#define _XTAL_FREQ 12000000
//USART
#define BAUDRATE 115200
//USART---------------------------------------------------------------
#define SET_SPBRG_DATA (((unsigned int)((_XTAL_FREQ/16)/BAUDRATE))-1)
#define SET_SPBRG_DATA1 (((unsigned int)((_XTAL_FREQ/4)/BAUDRATE))-1)
#define USART_BUF_SIZE 30
//USART data structure
typedef struct {
uint8_t length;
uint8_t rxBuf[USART_BUF_SIZE];
bool rxCompleted;
}_usart;
extern _usart usart;
//function prototype
extern void USART_INIT(void);
extern void putch(uint8_t byte);
typedef struct{
uint8_t count;
}_tm;
extern _tm tm0;
extern void Timer0_init(void);
extern void Interrupt_START(void);
#endif /* PERIPHERAL_H_ */
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include "peripheral.h"
_usart usart;
/*-----------------------------------------
*USART
*-----------------------------------------*/
void USART_INIT(void)
{
unsigned char 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
}
void putch(unsigned char byte)
{
while(!PIR1bits.TXIF)continue;
TX1REG=byte;
}
/*---------------------------------------------------
Timer0
---------------------------------------------------*/
_tm tm0;
void Timer0_init(void)
{
TMR0L=0x56; //10msec
TMR0H=0xFC; //10msec
T0CON0=0x90;//16bits timer
T0CON1=0x45;//prescaler 1/32
PIE0bits.TMR0IE=1;
tm0.count=0;
}
void Interrupt_START(void)
{
INTCONbits.PEIE=1;
INTCONbits.GIE=1;
}