Writing ISR in basic C for MCF51CN128 [Newbie Question]

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Writing ISR in basic C for MCF51CN128 [Newbie Question]

Jump to solution
1,390 Views
ugujjwal
Contributor I

Hi,

 

I am a student and new to MCUs.

 

I am trying to learn how to use interrupts (say IRQ) using basic C coding. Previously I have worked on ATmel 8 bit MCUs where I could simply write the ISR using the pointer name given in vector table and a predefined function name.

 

Not able to find a similar way in CN128. I have been able to write a simple code for polling the IRQ pin (PC4) and toggling LEDs.  kindly help me, how can I make use of interrupt to toggle LEDs.

 

#include<mqx.h>#include<bsp.h>#include<fio.h>void main(void){    int i=0;    VMCF51CN_STRUCT_PTR reg_ptr = (VMCF51CN_STRUCT_PTR)BSP_IPSBAR;reg_ptr-> GPIO.PTEDD|=(5<<3);          //reg_ptr-> GPIO.PTHDD|=(1<<3);    // Output for LEDsreg_ptr-> GPIO.PTGDD|=(1<<5);    //reg_ptr-> GPIO.PTGDD &= ~(1<<6); //  input (Switch)reg_ptr-> GPIO.PTCDD &= ~(1<<6); //  input (Switch)while(1) { reg_ptr->GPIO.PTED|=(5<<3); reg_ptr->GPIO.PTHD|=(1<<3); reg_ptr->GPIO.PTGD|=(1<<5) | (1 << 6); reg_ptr->GPIO.PTCD &= ~(1<<6); // Delay   i=0;  while(i<2000000)  {    i++;               }  //Polling, If (Any one switch is pressed) if(((reg_ptr->GPIO.PTGD & 0x40) == 0) | (((reg_ptr->GPIO.PTCD & 0x40) >> 6) == 0)) {    reg_ptr->GPIO.PTED &=~( 5<<3);  reg_ptr->GPIO.PTHD &=~( 1<<3);  reg_ptr->GPIO.PTGD &=~( 1<<5);  //Delay     i=0;   while(i<2000000)   {     i++;   }  }}}     

 

Thanks in advance.

Labels (1)
0 Kudos
1 Solution
826 Views
JimDon
Senior Contributor III

Well, since you asked, and since you are leaning,

I will give you my version of advice.I don't know how you generated this project and why you are using the headers you are.I am going to advise that you recreate the project as a regular project.
You should not be using the BSP_IPSBAR2 ?registers as you are.You can do that, but since it is unusual, others will not get what you are doing and it is highly non portable.

In real life, no one ever moves the IO base.I would also advise that you start using the Eclipse version of codewarrior for 3 good reasons:

1. The V1 debugger is much much better.

2. Freescale is really not going to support the so called "Classic" versions. Also, you will probably soon get a new computer that will be running a 64 bit os, and then you will have to change versions.

3. If you learn Eclipse, much of what you learn will transfer to Java, Android and other domains.
Now, for the code.You never ever do a bulk delay in an interrupt handler. For one thing, you could be overruning the interrupt, which means that you get another interrupt while you are still in the interrupt handler. Second you are locking out other interrupts and could cause other problems like missed data on a serial port.

 

I personally never use an edge interrupt to handle a button, because it needs to be debounced and in the end you have to do some sort timing anyway, also since it is a human pressing the button, you have plenty of time to service it. I use the RTC interrupt running a 1ms and require that the button be in the same state for 10-20ms.

 

Also, I don't see that you are turning off the watchdog timer nor are you servicing it. This will cause the MCU to reset.

 

So, based on my advice this is how I would write the program. I did not actaully test it on your board, but most of it is from working code. I am taking it on faith that you had the correct IO bits for your led and button.

You really don't need deboucing for this particular example, but at least it is there....

 

#include <hidef.h> /* for EnableInterrupts macro */#include "derivative.h" /* include peripheral declarations */// Function prototypes.void RTCTickCallback(void);void InitRTCInternalClock(void);void BUTTONHandler(void);#define BUTTON_DEBOUNCE   20void InitRTCInternalClock(void){ // Set RTIE to enable interrupts, select the 1KHz internal oscillator // set the divider to 1 for a 1ms interrupt. RTCSC = 0x18; RTCMOD = 0;}void BUTTONHandler(void){ static byte state = 1; static byte debounce = 0;    static byte ledstate = 0; if( ledstate )    PTED |= (1 << 5); else    PTED &= ~(1 << 5); // Is the state the same as last time? if((PTCD & (1 << 4)) == state  ) {  if(debounce == BUTTON_DEBOUNCE + 1)   return;  ++debounce;  if(debounce == BUTTON_DEBOUNCE)  {   // The state just changed here.   ledstate = state;   return;  } } // State changed last time, so start over debouncing. else {  state = PTCD & (1 << 4);   debounce = 0;   } }interrupt  VectorNumber_Vrtc void  RTC_InterruptHandler(void);interrupt  VectorNumber_Vrtc void  RTC_InterruptHandler(void){   BUTTONHandler();RTCSC |= 0x80;   // Ack the interrupt}void main(void) { SOPT1 = 0x10; // Disable watch dog. InitRTCInternalClock(); PTEDD |= (1 << 5);    // PTE5 made output pin    PTCDD &= ~(1 << 4);   // PTC4 made as input pin    PTCPE &= ~(1 << 4);   // PTC4 Turn the pull up on as well    EnableInterrupts;   for(;;)   {     } /* loop forever */  /* please make sure that you never leave main */}

 

View solution in original post

0 Kudos
8 Replies
826 Views
angelo_d
Senior Contributor I

hi,

 

what IDE are you using ? CodeWarrior ?

 

Try to see some examples, or using the wizard to create a new project, there should be bnothing difficult on this.

A file called exceptions.c should be generated. From there you should eb able to set up your interrupt handlers.

 

 

 

0 Kudos
826 Views
ugujjwal
Contributor I

Hi Angelo,

 

Yes, I am using CodeWarrior 6.3. I am going through the examples, but finding them a bit hard to understand.

Will surely put more effort.

 

Thanks,

Ujjwal

0 Kudos
826 Views
JimDon
Senior Contributor III
interrupt  VectorNumber_Vrtc void  RTC_InterruptHandler(void){ RTCSC |= 0x80;   // Ack the interrupt}

This is a sample Real Time Clock interrupt handler. 

The vectors in exceptions.c are declared as weak, so you can override them in this way. The vector numbers are from the device specific header, so you can get them from there. No need to change the vector table...

0 Kudos
826 Views
ugujjwal
Contributor I

Hi Jim,

 

Thanks a lot, you have given me what asked for :smileyhappy:. I have written a very basic code, but its still not working.

Please tell me what mistake I am doing.

/*Description: A LED is connected to PTE5 which is ON, everytime an external interrupt IRQ * comes the LED switches off, as soon as the switch at IRQ is released the LED becomes on again* as the program returns to infinite loop.** MCU used: MCF51CN 128** IDE: Codewarrior v6.3** Aim: To learn how to use external interrupt IRQ (At PTC4 pin).*/ #include<mqx.h>#include<bsp.h>#include<fio.h>#define BSP_IPSBAR2 ((uint_32)0xFFFF80E0) VMCF51CN_STRUCT_PTR irq_ptr = (VMCF51CN_STRUCT_PTR)BSP_IPSBAR2;VMCF51CN_STRUCT_PTR reg_ptr = (VMCF51CN_STRUCT_PTR)BSP_IPSBAR;int i;// ISR functioninterrupt  64 void  IRQ_InterruptHandler(void) // Vector number for IRQ is 64 {   irq_ptr-> IRQ.IRQSC |= (1 << 2);                // Ack the interrupt/* Switch off LED with a delay */reg_ptr-> GPIO.PTED &= ~(1 << 5);   // LED Offfor( i = 0; i <= 2000000; i++){} // Delay loop// return from ISR}// Main functionint main(void){// Initialize the interrupt vector/* I/O Initialization */reg_ptr-> GPIO.PTEDD |= (1 << 5);    // PTE5 made output pinreg_ptr-> GPIO.PTCDD &= ~(1 << 4);   // PTC4 made as input pinirq_ptr-> IRQ.IRQSC |= (1 << 3);                   // Clear interrupt flagsirq_ptr-> IRQ.IRQSC |= (1 << 1) | (1 << 0);        // Enable the specific interrupt source/* Loop forever */for(;;){ reg_ptr-> GPIO.PTED |= (1 << 5); // LED ON}return 0;}

 

Thanks,

Ujjwal

0 Kudos
827 Views
JimDon
Senior Contributor III

Well, since you asked, and since you are leaning,

I will give you my version of advice.I don't know how you generated this project and why you are using the headers you are.I am going to advise that you recreate the project as a regular project.
You should not be using the BSP_IPSBAR2 ?registers as you are.You can do that, but since it is unusual, others will not get what you are doing and it is highly non portable.

In real life, no one ever moves the IO base.I would also advise that you start using the Eclipse version of codewarrior for 3 good reasons:

1. The V1 debugger is much much better.

2. Freescale is really not going to support the so called "Classic" versions. Also, you will probably soon get a new computer that will be running a 64 bit os, and then you will have to change versions.

3. If you learn Eclipse, much of what you learn will transfer to Java, Android and other domains.
Now, for the code.You never ever do a bulk delay in an interrupt handler. For one thing, you could be overruning the interrupt, which means that you get another interrupt while you are still in the interrupt handler. Second you are locking out other interrupts and could cause other problems like missed data on a serial port.

 

I personally never use an edge interrupt to handle a button, because it needs to be debounced and in the end you have to do some sort timing anyway, also since it is a human pressing the button, you have plenty of time to service it. I use the RTC interrupt running a 1ms and require that the button be in the same state for 10-20ms.

 

Also, I don't see that you are turning off the watchdog timer nor are you servicing it. This will cause the MCU to reset.

 

So, based on my advice this is how I would write the program. I did not actaully test it on your board, but most of it is from working code. I am taking it on faith that you had the correct IO bits for your led and button.

You really don't need deboucing for this particular example, but at least it is there....

 

#include <hidef.h> /* for EnableInterrupts macro */#include "derivative.h" /* include peripheral declarations */// Function prototypes.void RTCTickCallback(void);void InitRTCInternalClock(void);void BUTTONHandler(void);#define BUTTON_DEBOUNCE   20void InitRTCInternalClock(void){ // Set RTIE to enable interrupts, select the 1KHz internal oscillator // set the divider to 1 for a 1ms interrupt. RTCSC = 0x18; RTCMOD = 0;}void BUTTONHandler(void){ static byte state = 1; static byte debounce = 0;    static byte ledstate = 0; if( ledstate )    PTED |= (1 << 5); else    PTED &= ~(1 << 5); // Is the state the same as last time? if((PTCD & (1 << 4)) == state  ) {  if(debounce == BUTTON_DEBOUNCE + 1)   return;  ++debounce;  if(debounce == BUTTON_DEBOUNCE)  {   // The state just changed here.   ledstate = state;   return;  } } // State changed last time, so start over debouncing. else {  state = PTCD & (1 << 4);   debounce = 0;   } }interrupt  VectorNumber_Vrtc void  RTC_InterruptHandler(void);interrupt  VectorNumber_Vrtc void  RTC_InterruptHandler(void){   BUTTONHandler();RTCSC |= 0x80;   // Ack the interrupt}void main(void) { SOPT1 = 0x10; // Disable watch dog. InitRTCInternalClock(); PTEDD |= (1 << 5);    // PTE5 made output pin    PTCDD &= ~(1 << 4);   // PTC4 made as input pin    PTCPE &= ~(1 << 4);   // PTC4 Turn the pull up on as well    EnableInterrupts;   for(;;)   {     } /* loop forever */  /* please make sure that you never leave main */}

 

0 Kudos
826 Views
ugujjwal
Contributor I

Thanks a lot Jim for the insight.

 

I was using the headers mqx.h, bsp.h, fio.h because it was there in an example, I thought of modifying it.

I am using a freescale Tower board. It has a option to use mqx RTOS, I don't understand mqx's use as of now, seems quite complex to me.

 

Regarding the mistakes, I will surely keep them in mind. Also will try to use the Eclipse version.

 

Your code is working fine, I do believe that your code can be used as a mode to bridge the learning gap between the usage of pure external interrupt which disturbs the processor only when an event occurs and pure polling inside the infinite loop, which is keeping the processor busy. While using your code we can disturb the processor in fixed intervals of time and check for the event.

 

If we use mqx then watchdog timer need not be turned off by us, it is by default taken care of, I am assuming this since the first code I posted here was working and MCU was not resetting.

 

Why I was learning about IRQ was because in the system I am working on, the Tower board is connected to a FPGA board.

And the FPGA will send the interrupt to the MCU. I was using a DIP switch insead of FPGA board just for testing and learning purpose. The data exchange between FPGA and MCU is done through flex bus and the code is written using mqx. Now there is only one issue, on how to intergrate the mqx code with the code having the hidef.h and derivstive.h headers.

 

Meanwhile I have written a code for using IRQ with the help of your code, and its working. :smileyhappy:

#include <hidef.h> /* for EnableInterrupts macro */#include "derivative.h" /* include peripheral declarations */// Function prototypes.void InitIRQ(void);void Job_Handler(void);void InitIRQ(void){ PTCPF1 |= 0x01; // Choosing the alternate function for PTC4 IRQSC = 0x32;   // Rising edge, Pin enable, Enabling interrupt, Only edge detection}void Job_Handler(void){ PTED &= ~(1 << 5); // PTE5 LED Off PTED |= (1 << 3);  // PTE3 LED On}interrupt  VectorNumber_Virq void  IRQ_InterruptHandler(void); // interrupt routine is defined by using a keyword "interrupt" interrupt  VectorNumber_Virq void  IRQ_InterruptHandler(void){  Job_Handler();  IRQSC |= (1 << 2);   // Ack the interrupt, clearing the IRQ flag}void main(void) { int i; SOPT1 = 0x10; // Disable watch dog. InitIRQ();     PTEDD |= (1 << 5) | (1 << 3);    // PTE5 made output pin     PTCDD &= ~(1 << 4);   // PTC4 made as input pin PTED |= (1 << 5);     // LED On PTCD |= (1 << 4);     EnableInterrupts;

 

 

Thanks,

Ujjwal

 

0 Kudos
826 Views
ugujjwal
Contributor I

Sorry I made a mistake while copying, here is the code;

#include <hidef.h> /* for EnableInterrupts macro */#include "derivative.h" /* include peripheral declarations */// Function prototypes.void InitIRQ(void);void Job_Handler(void);void InitIRQ(void){ PTCPF1 |= 0x01; // Choosing the alternate function for PTC4 IRQSC = 0x32;   // Rising edge, Pin enable, Enabling interrupt, Only edge detection}void Job_Handler(void){ PTED &= ~(1 << 5); // PTE5 LED Off PTED |= (1 << 3);  // PTE3 LED On}interrupt  VectorNumber_Virq void  IRQ_InterruptHandler(void); // interrupt routine is defined by using a keyword "interrupt" interrupt  VectorNumber_Virq void  IRQ_InterruptHandler(void){  Job_Handler();  IRQSC |= (1 << 2);   // Ack the interrupt, clearing the IRQ flag}void main(void) { int i; SOPT1 = 0x10; // Disable watch dog. InitIRQ();     PTEDD |= (1 << 5) | (1 << 3);    // PTE5 made output pin     PTCDD &= ~(1 << 4);   // PTC4 made as input pin PTED |= (1 << 5);     // LED On PTCD |= (1 << 4);     EnableInterrupts;          for(;;)   { for (i = 0; i <= 2000000; i++) {} // delay PTED |= (1 << 5);  // LED on        PTED &= ~ (1 << 3); // LED off  } /* loop forever */  /* please make sure that you never leave main */}

 

0 Kudos
826 Views
JimDon
Senior Contributor III

Ujjwal,

You did a fantastic job.

I just have one more suggestion that would take this from an A to and A+.

Put the RTC tick back (and the init code) and increment a counter instead of your for loop delay.

There are two reasons for this:

1. If you change the MCU clock all your delays change.

2. At some future time you may need to do other things in main, and this will just stall your code.

3. It\'s impossable for me to tell how much delay you intended.

 

dword mscounter;interrupt  VectorNumber_Vrtc void  RTC_InterruptHandler(void);interrupt  VectorNumber_Vrtc void  RTC_InterruptHandler(void){  ++mscounter;   RTCSC |= 0x80;   // Ack the interrupt}..........  In main for loop  // Wait for 2 seconds   if( 2000 == mscounter)  {       mscounter = 0;      // do your delayed stuff here  }

 

0 Kudos