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?

PICマイコンで、スーファミコントローラを入力デバイスにする。

Last updated at Posted at 2024-05-06

ネット上にあったスーパーファミコンのコントローラの動作仕様が、おもしろそうだったので、PICマイコンで動かしてみました。

IMG_4541.JPG

マイコンのスイッチ入力に、スーパーファミコンのコントローラが使えれば、操作性がよくなり、アプリケーションの幅が広がりそうだったので、トライしてみました。

使用したマイコン:PIC16F18326
USBシリアル変換器:MCP2221A

1.スーパーファミコンコントローラの基板

IMG_4538.JPG

基板には、V520Bと印字された20ピンのICが乗っています。

IMG_4540.JPG

このV520Bの仕様が良くわからないのですが、ネット情報を集めて、端子機能をまとめてみると以下のようになりました。
1.gif
IMG_4550.JPG

スーファミオリジナルのコネクタは、使用せず、ケーブルを切断。個別の信号線をXHコネクタ端子に変更しました。

基板のコネクタに合わせて、4ピンコネクタと、3ピンコネクタに分けて、PICマイコンと接続しています。

コネクタ写真.jpg

電源ピンは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.スーファミコントローラのタイミングチャート

SFCコントローラタイミングチャート.jpg

DATA1(赤)は、アクティブロー出力です。Lowで出力されたビットに対応したボタンが押下(おうか)されたことになります。

3.PIC16F18326 ソースコード

押したボタンがシリアル出力されます。同時に押したボタンも、表示されます。
シリアル出力.png

P/S:RC3(Output)
CLOCK:RC4(Outpu)
DATA1:RA4(Input)

sFamiCon.h
//*******************************************//
//        スーファミコントローラ制御
//  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 */
sFamiCon.c
#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");
}
main.c

// 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;
}
peripheral.h

#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_ */

peripheral.c

#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;
}
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?