Introduction

Microcontrollers have revolutionized the world of embedded systems by providing a versatile platform for building intelligent devices. In this blog post, we will explore the powerful dsPIC33FJ64GS606 microcontroller and learn how to create an engaging LED blinking project using a 10-millisecond timer interrupt. With a deep dive into the intricacies of timer configuration, we’ll leverage the prescaler value of 8 and a system frequency (Fcy) of 40MHz to achieve precise timing. By the end of this tutorial, you’ll have a solid understanding of using timer interrupts and be ready to illuminate your own creative projects.

Required components

S.NoCOMPONENTSQUANTITY 
1DSPIC33FJ64GS6061Amazon
2PICkit4 Programmer1Amazon
3Led1Amazon
3Oscilloscope1Amazon
4Logic Analyser1Amazon
5connection wires1Amazon

Specification

ParameterValue
Core TypedsPIC33F
CPU Speed40 MIPS (10 MHz)
Flash Memory64 KB
SRAM8 KB
Operating Voltage2.5V – 3.6V
Number of Pins64
Timers5 Timers
Analog-to-Digital Converter (ADC)10-bit, up to 16 channels
Digital-to-Analog Converter (DAC)2x 10-bit DACs
Pulse Width Modulation (PWM)Up to 12 channels
Serial Communication InterfacesUART, SPI, I2C
Inter-Integrated Circuit (I2C)Up to 2 channels
Universal Asynchronous Receiver/Transmitter (UART)Up to 2 channels
Serial Peripheral Interface (SPI)Up to 2 channels
USBUSB 2.0 Full-Speed Host/Device
Operating Temperature Range-40°C to +125°C

Understanding Timer Interrupts

Timer interrupts are an essential microcontroller feature that enables precise timing and real-time event handling. By setting up a timer interrupt, we can trigger a specific action, such as toggling an LED, at regular intervals. The dsPIC33FJ64GS606 microcontroller offers a range of timers with diverse configurations, making it a perfect choice for this project.

explanation of the timer setting registers typically found in microcontrollers like the dsPIC33FJ64GS606. These registers control various aspects of the timer module’s behaviour, such as clock source selection, Prescaler configuration, and interrupt enable/disable settings. Here are the commonly used timer-setting registers and their functionalities:

TMRx (Timer Register)

  • The TMRx register holds the current value of the timer counter.
  • It increments or decrements on each clock cycle depending on the timer configuration.

PRx (Period Register)

  • The PRx register determines the timer period or the value at which the timer counter should reset.
  • When the timer counter matches the value in PRx, an interrupt can be generated, or a specific action can be taken.

TCONx (Control Register)

  • The TCONx register contains control bits for configuring the timer module.
  • It typically includes bits for enabling/disabling the timer, selecting the clock source, setting the prescaler, and configuring other specific options.

TCKPSx (Prescaler Select Bits)

  • The TCKPSx bits determine the prescaler value for dividing the input clock frequency.
  • The Prescaler value determines the timer’s resolution and affects the timer’s interrupt frequency.

TCSx (Clock Source Select Bit)

  • The TCSx bit selects the clock source for the timer.
  • It allows you to choose between using an internal clock or an external clock input.

TIE, TIF (Interrupt Enable and Interrupt Flag)

  • The TIE bit enables or disables the timer interrupt.
  • The TIF bit indicates whether a timer interrupt has occurred.

Explanation Of The Timer Setting

The TCKPS bits in the T1CON register control the prescaler settings for Timer1 in the dsPIC33FJ64GS606 microcontroller. The Prescaler value determines the division factor for the input clock frequency, allowing you to adjust the timer’s resolution and interrupt frequency.

Prescaler Division Factor Calculation:

With TCKPS = 0b01 (binary 01) and a prescaler value of 8, the prescaler division factor is determined by the prescaler bits as follows: Prescaler Division Factor = 8

Timer Frequency Calculation

The timer frequency (Ftimer) can be calculated using the following formula: Ftimer = Fcy / (Prescaler * (PR1 + 1))

Given:

  • Fcy: System frequency (40 MHz)
  • Prescaler: Prescaler division factor (8)

Assuming a desired interrupt period of 10 milliseconds (10 ms), we can calculate the value for PR1:

PR1 = (Fcy / (Prescaler * Ftimer)) – 1
= (40,000,000 / (8 * 0.01)) – 1
= 50000 – 1
= 49999

Therefore, to achieve a timer interrupt frequency of approximately 100 Hz (10 ms period) with a prescaler of 8 and Fcy of 40 MHz, you should set the TCKPS bits to 0b01 and the PR1 register to 49999.

Source Code

/*
 * File:   main.c
 * Author: Admin
 *
 * 
 */

#include "xc.h"
#include <p33FJ64GS606.h>
#include "stdio.h"
#include "stdlib.h"
#include <string.h>
#include <math.h>
#include <dsp.h>

#include "libpic30.h"
#define HEARTBEAT_LED           LATGbits.LATG3      // Heart Beat LED

_FOSCSEL(FNOSC_FRC & IESO_OFF);                     // Internal FRC start-up without PLL,// no Two Speed Start-up
_FOSC(FCKSM_CSECMD & OSCIOFNC_ON & POSCMD_NONE);    // POSCMD_XT);// Clock switch enabled,// Primarly Oscillator XT
_FWDT(FWDTEN_OFF);                                  // Watchdog Timer disabled
_FICD(JTAGEN_OFF);                                  // & ICS_PGD3);// Disable JTAG
_FPOR(FPWRT_PWR128);                                // Power up timer enabled by 128 mS



void initOscFreq(void);
void initInputCapture (void);                       // Initialization of Input capture for Hall sensor sensing
void initTimer ();

unsigned int uiTmrCnt = 0;
int main(void) {
    PTCONbits.PTEN = 0;                              /* Enable the PWM Module */
    //------------------- Start ------------------------//

    // -------------- Output's ------------- //
    TRISGbits.TRISG3 = 0;                           // Led_1
    TRISGbits.TRISG2 = 0;                           // Led_2
    TRISDbits.TRISD2 = 0;                           // Led

    initOscFreq();                                  // Oscillator Initialization
    initInputCapture();                             // Input Capture Init
    initTimer();                                    // Timer One Initialization


    while(1)
    {

    }
}

void initTimer ()
{
    // Prescaler=8, Fcy=40MHz, T = 1/Fcy = 25nS 
    // Ex: For 10ms, 10mS/(25nS*8)
    //TimerInterruptCount = 0; // Clear Interrupt counter flag
    T1CONbits.TCKPS = 1;    //Pre scale = 8
    PR1 = 0XC350;           //5mS = 0X61A8; 2.5mS = 0x30d4;  10mS = 0xC350
    IPC0bits.T1IP = 4;      // Set Interrupt Priority lower then ADC
    IEC0bits.T1IE = 1;      // Enable Timer1 interrupt
    T1CONbits.TON = 1;      // Enable Timer1 module
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

void __attribute__((__interrupt__, no_auto_psv)) _T1Interrupt()
        {   
    IFS0bits.T1IF = 0;              // Clear Interrupt Flag

    uiTmrCnt = uiTmrCnt + 1;
    if(uiTmrCnt >= 100)             //for 1Sec 
    {
        uiTmrCnt = 0;
        HEARTBEAT_LED^=1;

    }   



        }

//***************Function Definitions starts from here************************************
void initOscFreq(void)
{
    // Configure PLL prescaler, PLL postscaler, PLL divisor for FOsc=80Mhz and Fcy=40Mhz
    PLLFBD = 63;            // M = 65
    CLKDIVbits.PLLPOST=0;   // N2 = 2
    CLKDIVbits.PLLPRE=1;    // N1 = 3

    // Initiate Clock Switch to Internal FRC with PLL (NOSC = 0b001)
    __builtin_write_OSCCONH(0x01);
    __builtin_write_OSCCONL(OSCCON | 0x01); //Clock witch Enable

    //OSCTUNbits.TUN = 0b111111;        //FRC tuning to 7.345Mhz    

    while (OSCCONbits.COSC != 0b001);   // Wait for Clock switch to occur
    while (OSCCONbits.LOCK != 1);       //wait till PLL is locked

    /* Now setup the ADC and PWM clock for 120MHz
     ** ((FRC * 16) / APSTSCLR ) = (7.37 * 16) / 1 = ~ 120MHz
     */
    ACLKCONbits.FRCSEL = 1;         // FRC provides input for Auxiliary PLL (x16)
    ACLKCONbits.SELACLK = 1;        // Auxiliary Ocillator provides clock source for PWM & ADC
    ACLKCONbits.APSTSCLR = 7;       // Divide Auxiliary clock by 1 
    ACLKCONbits.ENAPLL = 1;         // Enable Auxiliary PLL 

    while(ACLKCONbits.APLLCK != 1); // Wait for Aux. PLL to Lock  {{for Testing}}

}

void initInputCapture()
{
    IC1CONbits.ICSIDL = 0;
    IC1CONbits.ICM = 1;

}

By Devilal

Leave a Reply

Your email address will not be published. Required fields are marked *