PIC18F4550でタイマーを使う
2017/03/04


LEDの点滅が出来たので、その回路をそのまま使って、LEDの点滅をタイマーで動作させてみる。



回路は前回と同じで、プログラムのみを変更する。
test_pgm001


プログラムは、後から別のプログラムでも使用しやすいように機能を分割してパッケージにしておく。

まずはTimer1.hと名付けたヘッダーファイルは以下のようなものになる。

コメントにゴチャゴチャと書いてあるのは、タイマーを動作させる時間の計算方法で、このあたりをちゃんと説明しておかないと、色々なものが関係しているので、アッと言う間に忘れてしまうのである。
プログラムを設計して作っている最中は理解したつもりになっているのだが、ちょっとでも時間が経つと「何故この値はこう設定したのだろうか?」と、悩んでしまうのである。
挙句の果ては、アルツハイマーになっているのは母親ではなく自分ではないかという疑念も出てくる始末なのである。

タイマーの設定で重要なのは、CPUの動作速度が48MHzで、タイマーに使用されているのがその1/4の12MHzであり、さらにT1CONのプリスケーラーで1/8の1500KHzに設定していることなのである。
タイマーを16ビットで使用すると、65535のカウンターになるので、65535*(1/1500000)は0.04369秒が最長のタイマーということになる。

その時間以下ならば、オーバーフローする65536になるまでの数値を逆算して入力してやればよいのである。
たとえば20ミリ秒のタイマーが欲しければ、0.02/(1/1500000)で30000カウントとなる。
30000カウントして65536になるには35536を16ビットのカウンターにセットしてやれば良い。
上位バイトには138(0x8A)と下位バイトには208(0xD0)となる。


#include < xc.h > // include processor files - each processor file is guarded.  

void Open_Timer1(void);
void Start_Timer1(void);

// 水晶振動子 20MHz
// configでPLLDIV = 5   20/5-> 4MHz入力を行いPLLで96MHz発振
// CPUDIV = OSC1_PLL2    96MHzを半分にし、48MHzでCPUを駆動する
//
// タイマーはFosc/4で動き、T1CONのプリスケーラーで1/8にしているので
// 48000000/(4*8)->1500000
// 16ビットのカウンター1につき1/1500000->0.66666μ秒
unsigned char Timer1_time_L;    // 必要な時間 20ms とすると
unsigned char Timer1_time_H;    // 0.02/(1/1500000)->30000
                                // 65536-30000->35536->8AD0


タイマーを動作させる関数は、以下のようなものになる。

Open_Timer1で初期設定を行い、Start_Timer1でタイマーの実行開始を行う。



#include < stdio.h >
#include < stdlib.h >
#include < xc.h >
#include "Timer1.h"

void Open_Timer1(void) {
        
    T1CONbits.RD16 = 1;         // 16bit
    T1CONbits.T1RUN = 1;        //
    T1CONbits.T1CKPS1 = 1;      // プリスケーラー1/8
    T1CONbits.T1CKPS0 = 1;      //
    T1CONbits.T1OSCEN = 0;      //
    T1CONbits.T1SYNC = 1;       // 
    T1CONbits.TMR1CS = 0;       // internal clock

    TMR1L = Timer1_time_L;                  
    TMR1H = Timer1_time_H;                  
    
    INTCONbits.GIE = 1;   // Enables all high priority interruptsts
    INTCONbits.PEIE = 1;  // Enables all low priority peripheral interrupts
    
    PIR1bits.TMR1IF = 0;        // TMR1 register did not overflow
    PIE1bits.TMR1IE = 1;        // Enables the TMR1 overflow interrupt
    IPR1bits.TMR1IP = 0;        // Low priority
    
    RCONbits.IPEN = 1;          // Enable priority levels on interrupts

}

void Start_Timer1(void) {
 
    T1CONbits.TMR1ON = 1;       // timer start

}


次がタイマーで起動される割り込みルーチン。
このプログラムが20ミリ秒毎に起動される。

今回は、タイマーで起動される都度、RA0のLEDを反転させる。



#include < stdio.h >
#include < stdlib.h >
#include < xc.h >

#include "Timer1.h"

//
//  割り込み関数
//
void interrupt isr(void)            // 割り込み関数
{
    if( PIR1bits.TMR1IF )
	{            // タイマ1割り込み?
        PIR1bits.TMR1IF = 0;        // TMR1 register did not overflow
        TMR1L = Timer1_time_L;                 
        TMR1H = Timer1_time_H;                 
        if ( LATAbits.LA0 == 1 )
        {
            LATAbits.LA0 = 0;
        }
        else
        {
            LATAbits.LA0 = 1;
        }
    }
}                  


最後にメインのプログラム。

メインのプログラムは、タイマーのオープンとスタートを行った後は、RA1のLEDを5ms毎に高速で点滅させているだけである。
タイマー割り込みで、メインの動作と関係なくRA0が20ms毎に反転を繰り返す。



#include < stdio.h >
#include < stdlib.h >
#include "Timer1.h"

#define _XTAL_FREQ 48000000    // 使用するPIC等により動作周波数値を設定する
                               // 水晶は20MHz でPLLで48MHz動作

// コンフィギュレーション pic18f4550  c18
#pragma config PLLDIV = 5, CPUDIV = OSC1_PLL2, USBDIV = 2
#pragma config FOSC = HSPLL_HS, FCMEN = OFF, IESO = OFF
#pragma config PWRT = ON, BOR = OFF, BORV = 0
#pragma config VREGEN = ON, WDT = OFF, WDTPS = 1
#pragma config CCP2MX = OFF, PBADEN = OFF, LPT1OSC = OFF
#pragma config MCLRE = OFF, STVREN = OFF, LVP = OFF
#pragma config ICPRT = OFF, XINST = OFF, DEBUG = OFF
#pragma config CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF
#pragma config CPB = OFF, CPD = OFF
#pragma config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF
#pragma config WRTC = OFF, WRTB = OFF, WRTD = OFF
#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF, EBTRB = OFF

/*
 * 
 */
int main(int argc, char** argv) {
    
	TRISA = 0b00000000;			//PORTA=0-7 output
    
    Timer1_time_H = 0x8A;       // タイマー用 20ms
    Timer1_time_L = 0xD0;       // タイマー用
//    Timer1_time_H = 0x00;       // タイマー用 0.4369ms
//    Timer1_time_L = 0x00;       // タイマー用
    Open_Timer1();
    
    LATAbits.LA0 = 0;
    int i = 0;

    Start_Timer1();
    
    while(1){

        LATAbits.LA1 = 1;
//        for(i=0;i<5000;i++);   // 5msのディレイ
        __delay_ms(5) ;        // 5msのディレイ
        LATAbits.LA1 = 0;
//        for(i=0;i<5000;i++);   // 5msのディレイ
        __delay_ms(5) ;        // 5msのディレイ

    }

    return (EXIT_SUCCESS);
}

これが動作中のRA0とRA1で、時間軸は10msである。

test_pgm004


HOME