• 0
JayWeeks

DP32 Timer 3 Interrupt with 1:64 Prescaler

Question

I'm using Arduino 1.6.9 with ChipKIT core and a DP32 rev B.

My code sets up an interrupt for timer 3. When I set the prescaler to 1:32 or less, the interrupt runs just fine (although with some quirky behavior that I'll explain below). However, when I set the prescaler to 1:64 or 1:256, the interrupt appears to fail.

My code is included below but I'll provide a brief explanation of its function here. The ISR increments a count variable, and sets a flag variable to high. The loop checks to see if the flag is high. If it is, it toggles LEDs 1, 2 and 3 to represent bits 5, 6, and 7 (respectively, with bit 0 as the LSB) of the count variable. LED 1 is toggled to represent the flag variable, so if the flag is high the LED is turned on, and it is turned off if the flag is low.

The behavior I am getting with the prescaler set to 1:32 is as expected. LED 4 glows dimly, while LEDs 1 through 3 count rapidly upward in binary. However, when the prescaler is set to 1:64, LED 1 (bit 5 LED) is toggled on, and remains, while the rest of the LEDs are off. This includes LED 4 (the flag LED) which leads me to believe that the flag is not being raised. As for how or why LED 1 gets toggled on, I have no idea.

I also mentioned some unusual behavior earlier. The the function that sets my timer accepts a desired interrupt frequency, and accounts for the prescaler when calculating the period needed to achieve this frequency. This means that the interrupt frequency should not vary much between different prescaler values. However, when I set my prescaler to 1:16, and my interrupt frequency to 40 Hz, the actual interrupt frequency is slower than when I set my prescaler to 1:32 (with an interrupt frequency of 40 Hz). In fact, prescaler 1:32 is so much faster that I cannot make out the individual LED flashes for any of my indicator LEDs.

What's more, the LEDs are running much faster than they should. With prescaler set to 1:32, and interrupt frequency set to 20 Hz, the frequency on the LED 1 (tied, as you recall, to bit 5 of my counter) should be 0.625 Hz (that is, 20 Hz / 2^4). In practice, however, it switches much faster than this.

Can anybody tell me what's going wrong? I feel like I'm probably missing something simple and obvious, or just overlooking something regarding the operation of timers.

TL;DR

68921994.jpg

Code below:

/***************************************************************
 * Define some constants
 ***************************************************************/
#define CLOCK_FREQ 40000000

#define T3CON_ENABLE_BIT 0x8000
#define T3_SOURCE_INT 0

// Prescaler values
// Don't change these. Set the prescaler below using these.
#define T3_PRESCALE_1_1 0
#define T3_PRESCALE_1_2 1
#define T3_PRESCALE_1_4 2
#define T3_PRESCALE_1_8 3
#define T3_PRESCALE_1_16 4
#define T3_PRESCALE_1_32 5
#define T3_PRESCALE_1_64 6
#define T3_PRESCALE_1_256 7

// This was originally going to test a

/***************************************************************
 * Set our prescaler
 ***************************************************************/
#define PRESCALE T3_PRESCALE_1_32

/***************************************************************
 * Various Variables
 ***************************************************************/
volatile uint32_t count = 0;
volatile unsigned int flag = 0;
int mask = 0;

/***************************************************************
 * ISR
 ***************************************************************/
void __attribute__((interrupt)) myISR()
{
  count++;
  flag = 1;
  clearIntFlag(_TIMER_3_IRQ);
}
 

/***************************************************************
 * Timer and interrupt setup function
 ***************************************************************/
void start_timer_3(uint32_t frequency)
{
  uint32_t period;
  period = CLOCK_FREQ / (2^PRESCALE * frequency); // This formula isn't correct for prescaler 1:256
  T3CONCLR = T3CON_ENABLE_BIT;            // Turn the timer off
  T3CON = PRESCALE;                       // Set the prescaler
  TMR3 = 0;                               // Clear the counter
  PR3 = period;                           // Set the period
  T3CONSET = T3CON_ENABLE_BIT;            // Turn the timer on
}

/***************************************************************
 * Setup
 ***************************************************************/
void setup()
{
  start_timer_3(20);
  setIntVector(_TIMER_3_VECTOR, myISR);
  setIntPriority(_TIMER_3_VECTOR, 4, 0);
  clearIntFlag(_TIMER_3_IRQ);
  setIntEnable(_TIMER_3_IRQ);

  pinMode(PIN_LED1, OUTPUT);
  pinMode(PIN_LED2, OUTPUT);
  pinMode(PIN_LED3, OUTPUT);
  pinMode(PIN_LED4, OUTPUT);
}

/***************************************************************
 * Loop
 ***************************************************************/
void loop()
{
  if (flag)
  {
    digitalWrite(PIN_LED4, HIGH);
    if (count & 0b000100000) digitalWrite(PIN_LED1, HIGH);
    else digitalWrite(PIN_LED1, LOW);
    if (count & 0b001000000) digitalWrite(PIN_LED2, HIGH);
    else digitalWrite(PIN_LED2, LOW);
    if (count & 0b010000000) digitalWrite(PIN_LED3, HIGH);
    else digitalWrite(PIN_LED3, LOW);
    flag = 0;
  } else digitalWrite(PIN_LED4, LOW);
}

 

Share this post


Link to post
Share on other sites

5 answers to this question

Recommended Posts

  • 0

I was doing some things wrong, so here's some code fixes.

Line 52: Carrots "^" are used as bitwise XOR, not power functions. Dunno why I thought they were power functions, but I've changed it to a bitshifted 1 instead.

Line 55: I swear the example code I pulled this from just set T3CON to whatever the desired prescaler was. I thought this was screwy but I never questioned it. I've since questioned it. Now I've shifted my prescaler and masked my prescaler onto T3CON.

My main problem (i.e. the timer not functioning when prescaler is set to 1:64 or 1:256) has gone away, so that's fantastic. I suspect that my previous code was setting the TCS bit (bit 1) to 1, which would select an external clock as the source. With no external clock implemented, the timer would freeze up. Still no idea why this would cause the LSB LED to light up though.

However, the screwy behavior persists. My LEDs blink too fast and when I change the prescaler, the speed changes, even though my code should prevent that.

Updated code:

/***************************************************************
 * Define some constants
 ***************************************************************/
#define CLOCK_FREQ 40000000

#define T3CON_ENABLE_BIT 0x8000
#define T3_SOURCE_INT 0

// Prescaler values
// Don't change these. Set the prescaler below using these.
#define T3_PRESCALE_1_1 0
#define T3_PRESCALE_1_2 1
#define T3_PRESCALE_1_4 2
#define T3_PRESCALE_1_8 3
#define T3_PRESCALE_1_16 4
#define T3_PRESCALE_1_32 5
#define T3_PRESCALE_1_64 6
#define T3_PRESCALE_1_256 7

// Prescaler bit mask
#define T3_PRESCALE_MASK 0b01110000

/***************************************************************
 * Set our prescaler
 ***************************************************************/
#define PRESCALE T3_PRESCALE_1_32

/***************************************************************
 * Various Variables
 ***************************************************************/
volatile uint32_t count = 0;
volatile unsigned int flag = 0;
int mask = 0; // This isn't used as a mask, but I can't bother to come up with a better name.

/***************************************************************
 * ISR
 ***************************************************************/
void __attribute__((interrupt)) myISR()
{
  count++;
  flag = 1;
  clearIntFlag(_TIMER_3_IRQ);
}
 

/***************************************************************
 * Timer and interrupt setup function
 ***************************************************************/
void start_timer_3(uint32_t frequency)
{
  uint32_t period;
  period = CLOCK_FREQ / (1<<PRESCALE * frequency); // This formula isn't correct for prescaler 1:256
  T3CONCLR = T3CON_ENABLE_BIT;            // Turn the timer off
  mask = PRESCALE << 4;                   // Shift our prescale
  mask = mask | T3CON;                    // Mask our prescaler
  T3CON = mask;                           // Set the prescaler
  TMR3 = 0;                               // Clear the counter
  PR3 = period;                           // Set the period
  T3CONSET = T3CON_ENABLE_BIT;            // Turn the timer on
}

/***************************************************************
 * Setup
 ***************************************************************/
void setup()
{
  start_timer_3(20);
  setIntVector(_TIMER_3_VECTOR, myISR);
  setIntPriority(_TIMER_3_VECTOR, 4, 0);
  clearIntFlag(_TIMER_3_IRQ);
  setIntEnable(_TIMER_3_IRQ);

  pinMode(PIN_LED1, OUTPUT);
  pinMode(PIN_LED2, OUTPUT);
  pinMode(PIN_LED3, OUTPUT);
  pinMode(PIN_LED4, OUTPUT);
}

/***************************************************************
 * Loop
 ***************************************************************/
void loop()
{
  if (flag)
  {
    digitalWrite(PIN_LED4, HIGH);
    if (count & 0b000100000) digitalWrite(PIN_LED1, HIGH);
    else digitalWrite(PIN_LED1, LOW);
    if (count & 0b001000000) digitalWrite(PIN_LED2, HIGH);
    else digitalWrite(PIN_LED2, LOW);
    if (count & 0b010000000) digitalWrite(PIN_LED3, HIGH);
    else digitalWrite(PIN_LED3, LOW);
    flag = 0;
  } else digitalWrite(PIN_LED4, LOW);
}

 

Share this post


Link to post
Share on other sites
  • 0

Hello JayWeeks,

I have asked some of our applications engineers about this; they will get back to you here on the Forum.

Thanks,
JColvin

Share this post


Link to post
Share on other sites
  • 0

So I finally figured out my problem, and like all truely frustrating problems it was small and stupid.

The formula I used to calculate period:

period = CLOCK_FREQ / (1<<PRESCALE * frequency);

while mathematically correct, for some reason my usage of defined constants throws the compiler for a loop. The result is that my period was (apparently?) set to 40 MHz every time. I fixed this by breaking it up into components:

// Calculate the period we need for our given frequency
  if (PRESCALE == 7) period = 256; // 1:256 is a special case
  else period = 1 << PRESCALE;
  period = period * frequency;
  period = CLOCK_FREQ / period;

and this works. Now my function reliably sets the timer interrupt to the desired frequency.

Also, for those interested types, you can check out the full tutorial I made from this work.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now