#include <p32xxxx.h>
#include <sys/attribs.h>

// 外部の8MHz水晶発振器から40MHzのクロックを生成するための設定
#pragma config FNOSC = PRIPLL // 発信源 = 主外部発振器
#pragma config POSCMOD = XT // 主発振の方法　= 外部高精度発振器を使う
#pragma config FPLLIDIV = DIV_2 // PLL入力分数 = x1/2
#pragma config FPLLMUL = MUL_20 // PLL逓倍比 = x20
#pragma config FPLLODIV = DIV_2 // PLL出力分数 = x1/2
#pragma config FPBDIV = DIV_2 //周辺モジュールクロック倍数 : x1/2

#define BASE_CLK 20000000  // 周辺クロック(20MHz)

void updateDuty(int hz)
{
    // 1サイクルあたりの時間を計算
    int ticks = BASE_CLK / hz;
    PR2 = ticks;
    
    // PR2のタイマーカウントの半分で出力を切り替えるようにするとデューティ比が50%になる
    // 1/4ならデューティ比は25%になる。
    ticks /= 2;
    
    // OCxRSに値をセットするとバッファとして機能する。
    // これによりOCxRの値が急に切り替わることによって発生するノイズ(グリッチ)を抑止できる
    OC1RS = ticks;
    
    // 出力コンペアに割り当てているタイマーをリセットすることでPWMを再始動
    TMR2 = 0;
}

void InitTimer1()
{
    T1CONbits.ON = 0;    // 設定のため、タイマー1を無効にする
    TMR1 = 0;            // タイマー1をリセット
     
    // 100ms周期で割り込み <- 500ns(1 / 20MHz) * 256 * 7812(0x1E84) = 100ms
    T1CONbits.TCKPS = 3; // プリスケーラ : x1/256
    PR1 = 0x1E84;        // カウンター
        
    IPC1bits.T1IS = 3;   // 副優先度(0-3)
    IPC1bits.T1IP = 4;   // 割り込み優先度(0-7)
    IFS0bits.T1IF = 0;   // 割り込みフラグをリセット
    IEC0bits.T1IE = 1;   // タイマー1の割り込みを許可
    
    T1CONbits.ON = 1;    // タイマー1有効
}

int beep_mode = 1;

void __ISR(_TIMER_1_VECTOR, IPL4AUTO) OnTimer1()
{
    IFS0bits.T1IF = 0;

    if(beep_mode == 1){
        OC1CONbits.ON = 1;  // 出力コンペアを有効
        updateDuty(2000);
    }else if(beep_mode == 2){
        updateDuty(1000);
    }else if(beep_mode == 3){
        OC1CONbits.ON = 0;  // 出力コンペアを無効
    }else if(beep_mode == 16){
        beep_mode = 0;
    }
    beep_mode++;
}

int main(void)
{
    ANSELB = 0x00;          // ポートBをすべてデジタルモードにする  
    TRISB = 0;              // ポートBをすべて出力にする
    
    // タイマー2の設定(プリスケーラ1/1、内部クロックを使用)
    T2CONbits.ON = 0;
    T2CONbits.T32 = 1;       // タイマー3と併合して32bitモードで使用
    T2CONbits.TCKPS = 0b000; // 分周比 1:1
    T2CONbits.ON = 1;  
    TMR2 = 0;
        
    OC1CONbits.ON = 0;        // 設定のため出力コンパレータ(OC1)を無効に
    OC1CONbits.OCTSEL = 0;    // タイマー2を使用
    OC1CONbits.OCM = 0b0110;  // PWMで、失敗(fault)の検知を無効
    OC1CONbits.OC32 = 1;      // 32bitで計測
    RPB3Rbits.RPB3R = 0b0101; // B3にOC1を割り当てる
    OC1CONbits.ON = 1;
    
    InitTimer1();             // タイマー1の初期化
    
    INTCONbits.MVEC = 1;            // マルチベクタ割り込みを有効にする      
    __builtin_enable_interrupts();  // マイコンにおける割り込みの有効
    
    while(1) ;
}