PIC12F629タイマー割り込みを使ったサイコロの作成

2026.06.17

1. 概要
pic12f629を使って、ランダム関数を使わずにタイマー割り込み関数と目の頻度調整により擬似的に1〜6の目をランダムに出現させる。
操作はタクトスイッチを押して離したときスリープ状態から醒め、ランダムにLEDを光らせ、再びタクトスイッチを押して離したとき1から6までの デモンストレーション表示をしたのち、決定したサイコロの目をブリンクさせてスリープ状態に入る。
タクトスイッチを押してから離すまで、次のステップに移行しないようにして操作する人のタイミングをはぐらかせ、スリープ状態から覚醒して二度目 の押して離すまでのタイマー割り込み回数をもとにサイコロの目を決める。ただし、サイコロの目の頻度が偏らないように、各目の頻度を記録して、調 整を行う。

2.回路

下図のとおり


回路図


PIC12F629  
GP3はタクトSWの入力
GP0,GP1,GP2,GP4,はサイコロの目のLEDをダイナミック点灯により光らせる
(瞬時にはLEDは一つしか点灯させない)



3 プログラム
クロック周波数4MHz
Timer0割込み OPTION_REGに5をセットするとプリスケーラは 64になる。
割込み周期=4/(システムクロック)✕プリスケーラ値✕(256-TMR0初期値)
       
= 4/(4000000Hz)✕64✕(256-0)=0.016384sec=16.3msec

回路図のLEDの配置とプログラムで定義したLEDの位置番号は一致していない。
プログラムの19行目にあるLEDの配置でLEDを点灯させる
/***************************************************
 * File:   saikoro_C05.c
 * Author: karappi
 * Created on 2026/06/18, 21:33

 12f629   さいころ  2026.05.06(xc)
 TMR0割り込みでランダムぽく表示 度数をカウントして頻度を操作

 GP3  MCLR
       Anode-->Kathode
 LED1    GP0-->GP1
 LED2    GP1-->GP2
 LED3    GP2-->GP4
 LED4    GP0-->GP2
 LED5    GP1-->GP4
 LED6    GP1-->GP0
 LED7    GP2-->GP1
   
 LEDの配置
 R7    R1
 R4 R2 R6
 R3    R5 

点灯パターン O:on X:off
   SEVEN_以降はデバッグ用
 ONE_         TWO_         THREE_       FOUR_      
 X     X      X     O      X     O      O     O
 X  O  X      X  X  X      X  O  X      X  X  X 
 X     X      O     X      O     X      O     O 

 FIVE_        SIX_        
 O     O      O     O
 X  O  X      O  X  O
 O     O      O     O

サイコロ目のパターン
 一    LED_R2
 二    LED_R1,LED_R3
 三    LED_R1,LED_R2,LED_R3
 四    LED_R!,LED_R5,LED_R3,LED_R7
 五 LED_R!,LED_R5,LED_R3,LED_R7,LED_R2
 六    ED_R1,LED_R3,LED_R4,LED_R5,LED_R6,LED_R7
***************************************************/
#include<stdio.h>
#include <xc.h>
//#include <stdlib.h>
#include <pic12f629.h>

#define _XTAL_FREQ 40000000  //内部clock 4MHz for delay macro
//config 1
#pragma config WDTE=OFF     //Watchdog Timer off
#pragma config PWRTE=ON     //Power up timer on(スイッチを入れた直後電源が安定するまで待つ)
#pragma config MCLRE=OFF    //MCLR PIN off (ハードウェアリセットのピンの用途を無しにしてdigital pinとして使えるようにする)
#pragma config CP=OFF       //CODE PROTECT OFF プログラムの読み出しのプロテクトoff
#pragma config CPD=OFF      //Data Protect OFF データ領域の読み出しのプロテクトoff
#pragma config BOREN=ON     // Brownout on (もし電源が不安定のとき一時停止する)
#pragma config FOSC=INTRCIO //Oscillator selection bits (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN))

unsigned char dice; //サイコロの目 タイマー割り込みで変わる
//unsigned char dosu[]={0,0,0,0,0,0};
static unsigned char dosu[6]; //サイコロの目の度数の記録
/*******************************
       タイマー割り込み
 *******************************/
void __interrupt() ISR(void){
    if(TMR0IE){
        dice++;
        if(dice > 6)
            dice=1;
        INTCONbits.T0IF=0;//TMR0オーバーフラグクリア
     }
}
/*************************************
   LED n ON モジュール群  
*************************************/
/***  led on timer ***/
void ledontimer(unsigned char lonc){
    unsigned char i,j;
    for(i=1;i<=lonc;i++){
        for(j=1;j<=lonc;j++){}
    }
    GPIO=0; //LED OFF
}
/*** led R1 on   GP0 -->GP1 ***/
void led_r1_on(unsigned char lonc){
    TRISIO=0xfc; //GP0,GP1 OUT
    GPIO=0x01;   //LED R1 ON
    ledontimer(lonc); //led on timer
}
/***  led R2 on   GP1 -->GP2 ***/
void led_r2_on(unsigned char lonc){
    TRISIO=0xf9; //GP1,GP2 OUT
    GPIO=0x02;   //LED R2 ON
    ledontimer(lonc); //led on timer
}
/***  led R3 on   GP2 -->GP4 ***/
void led_r3_on(unsigned char lonc){
    TRISIO=0xeb; //GP2,GP4 OUT
    GPIO=0x04;   //LED R3 ON
    ledontimer(lonc); //led on timer
}
/***  led R4 on   GP0 -->GP2 ***/
void led_r4_on(unsigned char lonc){
    TRISIO=0xfa; //GP0,GP2 OUT
    GPIO=0x01;   //LED R4 ON
    ledontimer(lonc); //led on timer
}
/***  led R5 on   GP1 -->GP4 ***/
void led_r5_on(unsigned char lonc){
    TRISIO=0xed; //G1,GP4 OUT
    GPIO=0x02;   //LED R5 ON
    ledontimer(lonc); //led on timer
}
/***  led R6 on   GP1 -->GP0 ***/
void led_r6_on(unsigned char lonc){
    TRISIO=0xfc; //G1,GP0 OUT
    GPIO=0x02;   //LED R6 ON
    ledontimer(lonc); //led on timer
}
/***  led R7 on   GP2 -->GP1 ***/
void led_r7_on(unsigned char lonc){
    TRISIO=0xf9; //G2,GP1 OUT
    GPIO=0x04;   //LED R6 ON
    ledontimer(lonc); //led on timer
}
/*********************************************
  サイコロの目表示
  入力 MEON  サイコロの目の点灯時間
       SNUM   表示したいサイコロの目の番号1〜6
**********************************************/
void saikoro_d(unsigned char snum,unsigned char meon){
    unsigned char  i,j;
    for(i=0;i<=meon/2;i++){
        for(j=0;j<=meon;j++){
            switch(snum){
                case 1:
                    led_r2_on(12); //引数は点灯タイマカウンタ
                    break;
                case 2:
                    led_r1_on(8); //引数は点灯タイマカウンタ
                    led_r3_on(8); //引数は点灯タイマカウンタ
                    break;
                case 3:
                    led_r1_on(7); //引数は点灯タイマカウンタ
                    led_r2_on(7); //引数は点灯タイマカウン タ           
                    led_r3_on(7); //引数は点灯タイマカウンタ
                    break;
                case 4:
                    led_r1_on(5); //引数は点灯タイマカウンタ
                    led_r3_on(5); //引数は点灯タイマカウンタ
                    led_r7_on(5); //引数は点灯タイマカウンタ
                    led_r5_on(5); //引数は点灯タイマカウン タ          
                    break;
                case 5:
                    led_r1_on(4); //引数は点灯タイマカウンタ
                    led_r2_on(4); //引数は点灯タイマカウン タ                
                    led_r3_on(4); //引数は点灯タイマカウンタ
                    led_r7_on(4); //引数は点灯タイマカウンタ
                    led_r5_on(4); //引数は点灯タイマカウン タ          
                    break;
                case 6:
                    led_r1_on(3); //引数は点灯タイマカウンタ
                    led_r3_on(3); //引数は点灯タイマカウンタ
                    led_r7_on(3); //引数は点灯タイマカウンタ
                    led_r5_on(3); //引数は点灯タイマカウンタ
                    led_r4_on(3); //引数は点灯タイマカウン タ           
                    led_r6_on(3); //引数は点灯タイマカウン タ           
                    break;           
                default:
                    break;
            }
        }
    }
    GPIO=0;
}
/***********************************
 サイコロを振る  入力 無し  出力 unsingend char snum
 実際はタイマー割り込みが毎度更新するdiceをsnumとし出力する
 但、目の出現頻度の差が4を超えたら最小頻度の目を出力
 diceが最大頻度の目と同じでかつ出現頻度差が2を超えていたら
 __delay_(17)でウェイトしてから再度diceをsnumとして出力 
************************************/
unsigned char snumgen(){
    unsigned char i,max,min;
    unsigned char min_i,max_i; //最小頻度、最大頻度の目のindex
    unsigned char snum; //サイコロの目
      min=255;
      max=0;
      for(i=0;i<=5;i++){
        if(dosu[i]<min){
            min=dosu[i];
            min_i=i; //最小頻度の目記憶
        }
        if(dosu[i]>max){
            max=dosu[i];
            max_i=i; //最大頻度の目記憶
        }
      }
      snum=dice; //サイコロを振る
      if(max-min>4){//もし頻度差が4を超えていたら
          snum=min_i+1; //目は最小頻度の目にする
      }else if((snum==max_i+1) && (max-min>2)){//最大頻度の目と同じで頻度差が2を
          __delay_ms(17); //越えていたらちょっと待って
          snum=dice; //振りなおす
      }
      dosu[snum-1]++;//頻度更新
      if(dosu[snum-1]==0){//オーバーフロー
               dosu[0]=dosu[1]=dosu[2]=dosu[3]=dosu[4]=dosu[5]=0;//オールクリア& amp; amp; amp; amp; amp; amp; amp; amp; nbsp;              
      }
      return snum;
}
/******************************
   サイコロ メイン関数      
 *****************************/
int main(void){
    unsigned char  lonc; //LED on counter
    unsigned char lpc; //loop counter
    unsigned char snum  ; //さいころの表示num
    unsigned char meon  ; //さいころの目ONのループカウンタ初期値
     
    while(1){
        INTCONbits.GIE=0;   //割り込み禁止
        INTCONbits.PEIE=0;  //割り込み禁止
   
        TRISIO=0x08; //PORT 0,1,2,4,5=OUT, PORT3=INPUT/MCRLE
        OPTION_REG=0; //Option regi 0
        IOC=0x08; //GPIO3でsleepからWakeupするための割り込み設定
        INTCON=0x08; //GPIE on (GPIO 割り込み許可)

        GPIO=0;
        SLEEP(); //GPIO3(MCRL)がLOWになるまで冬眠
   
        TRISIO=0x08; //PORT 0,1,2,4,5=OUT, PORT3=INPUT/MCRLE
        OPTION_REG=0x05; //Option regi 内部クロック プリスケーラ64
        TMR0=0;
        INTCON=0; //余計な割り込みフラグは消しておく
        INTCONbits.T0IE=1; //TMR0割り込み許可
        INTCONbits.GIE=1;   //全体割り込み許可

        __delay_ms(40); //ここでワンクッションおいてタクトスイッチ
                      //チャタリングを避ける
        while(GPIO3==0){}//GPIO3が押されている間はここでループ
                           //GPIO3はプルアップなのでonのとき==0     & nbsp; 
        lonc=0x30;
        while(GPIO3 !=0){ //GPIO3がonになるまでバラバラledを点滅
                          //GPIO3はプルアップなのでonのとき==0
            led_r1_on(lonc);
            __delay_ms(1);   
            led_r2_on(lonc);
            __delay_ms(1);
            led_r3_on(lonc);
            __delay_ms(1);
            led_r4_on(lonc);
            __delay_ms(1);
            led_r5_on(lonc);
            __delay_ms(1);
            led_r6_on(lonc);
            __delay_ms(1);   
            led_r7_on(lonc); 
            __delay_ms(1);  
        }
        //タクトスイッチON  TMR0  取り出し サイコロの最終の目決定
        while(GPIO3 ==0){} //タクトスイッチ離すまで何もしない
   
        meon=0x10; //サイコロの目on時間初期値
        lpc=0x1e;   // ループカウンタセット
       
        while(lpc-- != 0){
            saikoro_d(lpc % 6 +1,meon); //サイコロの目表示
        }
        //目をブリンクして終了する
        meon=0x20;
        snum=snumgen();//サイコロをふる
        saikoro_d(snum,meon);
        __delay_ms(30);
        saikoro_d(snum,meon);
        __delay_ms(30);
         saikoro_d(snum,meon);
        __delay_ms(30);

        meon=0x40;
        saikoro_d(snum,meon);
    }
    return 0;
}