Timer Interrupt examples for dsPIC30F4011

This post features two timer interrupt examples for the dsPIC30F4011 microcontroller.

The first example just illustrates the bare essentials of setting up a Timer 1 interrupt and using it to increment a millisecond counter variable. The interrupt service routine (ISR), _T1Interrupt(), runs every 1ms. It simply resets the interrupt flag bit and then increments a global variable, ms_time. When ms_time reaches 1000 it is reset to 0. The main function switches an LED on or off depending on the value of ms_time. The net result is that the LED flashes on for 500ms and then off for 500ms over and over again.

//
// dsPIC30F4011 simple timer interrupt example
// Written by Ted Burke, Last updated 30-11-2015
//
 
#include <xc.h>
#include <libpic30.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, i.e. 30 MIPS
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

int ms_time = 0; // used to count milliseconds

// Timer 1 interrupt service routine (ISR)
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void)
{
    // Clear Timer 1 interrupt flag
    IFS0bits.T1IF = 0;
 
    // Increment the millisecond count
    ms_time = ms_time + 1;
    if (ms_time == 1000) ms_time = 0;
}

int main(void)
{
    _TRISC14 = 0;   // RC14 (pin 16) controls an LED
    
    // Configure Timer 1
    // Set PR1 and TCKPS to cause interrupt every 1ms
    PR1 = 30000;          // Set the Timer 1 period to 1ms
    T1CONbits.TCKPS = 0;  // Prescaler (0=1:1, 1=1:8, 2=1:64, 3=1:256)
    IEC0bits.T1IE = 1;    // Enable Timer 1 interrupt
    T1CONbits.TON = 1;    // Turn on Timer 1

    // Now just use ms_time value to make an LED flash
    while(1)
    {
        if (ms_time < 500) _LATD14 = 0; // LED off
        else _LATD14 = 1;               // LED on
    }
}

This second example is more complicated. In this case, the timer ISR not only increments the millisecond counter, ms_time, but it also looks after stepping the stepper motor backwards or forwards until it reaches a target angle determined by the global variable stepper_target. The timer ISR runs every 1ms, which is more frequent than the maximum stepping frequency of the stepper, so I used a little counter trick in the ISR to only step the motor every 3rd time an interrupt occurs. Of course, the stepper only steps when its current position is different from the target angle.

The actual sequence of stepper positions is determined by a simple state machine with four states. There’s nothing particularly significant about the angles I chose here – I just wanted to illustrate the principle of controlling stepper angle just by assigning a value to a global step counter variable and then leaving the timer ISR to do the job of moving the stepper to that angle one step at a time.

Here’s the full C code:

//
// dsPIC30F4011 stepper motor timer interrupt example
// Written by Ted Burke, Last updated 30-11-2015
//
 
#include <xc.h>
#include <libpic30.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, i.e. 30 MIPS
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

int state = 1;            // State variable for state machine
int ms_time = 0;          // Counts milliseconds in current state

int stepper_target = 0;   // Assign a value to this to set stepper angle
int stepper_position = 0; // Used to count steps towards target value
int count = 0;            // Counts timer interrupts - step every 4th time

void change_state(int);   // Updates state variable and resets ms_time
void step_forward();      // Steps forward and increments stepper_position
void step_back();         // Steps backwards and decrements stepper_position
void led(int);            // Switch LED on or off

int main(void)
{
    // Digital outputs
    _TRISC14 = 0;   // RC14 (pin 16) controls an LED
    TRISD = 0b0000; // RD0-3 control the stepper motor
    
    // Configure Timer 1
    // Set PR1 and TCKPS to cause interrupt every 1ms
    PR1 = 30000;          // Set the Timer 1 period to 1ms
    T1CONbits.TCKPS = 0;  // Prescaler (0=1:1, 1=1:8, 2=1:64, 3=1:256)
    IEC0bits.T1IE = 1;    // Enable Timer 1 interrupt
    T1CONbits.TON = 1;    // Turn on Timer 1
    
    // This simple state machine moves the stepper
    // through a repeating sequence of 4 positions.
    // The state changes every 5 seconds.
    while(1)
    {
        if (state == 1)
        {
            led(1);                // LED on
            stepper_target = 0;    // stepper at 0 degrees
            if (ms_time >= 5000) change_state(2);
        }
        else if (state == 2)
        {
            led(0);                // LED off
            stepper_target = 1024; // stepper at 180 degrees
            if (ms_time >= 5000) change_state(3);
        }
        else if (state == 3)
        {
            led(1);                // LED on
            stepper_target = 1536; // stepper at 270 degrees
            if (ms_time >= 5000) change_state(4);
        }
        else if (state == 4)
        {
            led(0);                // LED off
            stepper_target = 512;  // stepper at 90 degrees
            if (ms_time >= 5000) change_state(1);
        }
    }
}

// Timer 1 interrupt service routine (ISR)
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void)
{
    // Clear Timer 1 interrupt flag
    IFS0bits.T1IF = 0;
 
    // Increment the millisecond count
    ms_time = ms_time + 1;
    
    // Stepping every 1ms would be too often for the stepper,
    // so only step once every 3 interrupts.
    count = count + 1;
    if (count == 3)
    {
        // This only happens every 3rd interrupt - i.e. once every 3ms
        count = 0;
        if (stepper_position < stepper_target) step_forward();
        if (stepper_position > stepper_target) step_back();
    }
}

// This function updates the global state variable and resets
// the millisecond counter.
void change_state(int n)
{
    state = n;
    ms_time = 0;
}

// Move stepper motor forward one full step
void step_forward()
{
    if      (LATD == 0b0001) LATD = 0b0010;
    else if (LATD == 0b0010) LATD = 0b0100;
    else if (LATD == 0b0100) LATD = 0b1000;
    else if (LATD == 0b1000) LATD = 0b0001;
    else LATD = 0b0001;
    stepper_position = stepper_position + 1;
}

// Move stepper motor back one full step
void step_back()
{
    if      (LATD == 0b0001) LATD = 0b1000;
    else if (LATD == 0b0010) LATD = 0b0001;
    else if (LATD == 0b0100) LATD = 0b0010;
    else if (LATD == 0b1000) LATD = 0b0100;
    else LATD = 0b0001;
    stepper_position = stepper_position - 1;
}

void led(int on)
{
    if (on) _LATC14 = 1;
    else _LATC14 = 0;
}

dsPIC

Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

1 Response to Timer Interrupt examples for dsPIC30F4011

  1. Pingback: Controlling a Stepper Motor | fitzgeraldcathal

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s