Hello,
There's an expansion board attached to the LPC2478 which contains a motor and a lightgate which can count the revolutions of the motor, connected to a counter (timer1).
My program is written to enable the user to select various motor speeds and receive feedback as to what the actual motor speed is, which is printed on the display.
The display contains various lines of text explaining the program and various speeds the user can select.
Essentially the problem I have is that for every line of 'lcd_putString' that is used, it multiplies the reading of the motor revolutions. There's 12 lines of lcd_putString which are used in the initialisation code of the program to print this text to the display and then never called upon again. If I comment these lines of text out, so that nothing other than the motor speed reading is display, the motor speed is displayed correctly on the display (tested using an oscilloscope).
To put it simply, if there is text printed to the display using lcd_putString, the counter reading is multiplied.
A lot of hours have gone into trying to sort this issue so any help is hugely appreciated.
Not too easy to guess what your code might look like.
You haven't told us hardly anything at all. We don't even know if you are talking about a DC-controlled motor, where the processor controls the voltage and a higher voltage gives more revolutions. Or a servo motor where you give some servo signal and the motor then have electronics to regulate the speed based on the servo signal. Or if maybe you have a stepper motor where the processor generates the stepper pulses. Or maybe you even have a synchronous AC motor and have the processor create an AC output of varying frequency?
Next thing - we don't know anything about how your program initializes any frequencies or other critical parameters for producing the control signal(s) to the motor. And because we still don't know the type of motor or how you are controlling the speed, we don't know if the additional speed comes from a higher voltage or a changed pulse width or a changed frequency of pulses or something else.
We further don't know how your LCD is connected, so we do not know if the LCD interface in some way may interfere with the control signals for the motor drive.
And even if we knew all of the above, we would still not know exactly how the code is written so we could figure out if you have uninitialized variables getting different values depending on your printouts. Or if the code does some buffer overflow of some PWM data array you intended to control he motor with. Or if you incorrectly compute some parameter values you send to some processor registers.
The most logical thing to do would be to identify all register values that should affect the motor speed. Print these register values when the program works ok (i.e. without the LCD output) and when it doesn't. Somewhere you should be able to find some difference to some values - then it's just a question of backtracking to figure out what is caused the difference. But "we" as other forum visitors can't do it - only you have access to all the relevant information.
In the end, debugging is the process of breaking problems down into smaller pieces until each piece is simple enough that you can identify if it works or not. And if that doesn't work, try to figure out if the problem can be sliced in a different direction. And if that doesn't work, try to figure out if you can throw in some perturbations that will force the guilty sub-block(s) to reveal their flaws. But all these steps must be based on knowledge about the actual problem case - so knowledge of debugging is probably your most valuable skill to ace in.
Per - Thank you for your quick reply. I've included the code below which should answer some of the questions. The motor is a 3V DC motor controller by the PWM of the board.
Here's the code:
#include <lpc24xx.h> #include <textDisplay.h> #include <lcd_grph.h> #define UP (1<<10) #define DOWN (1<<11) #define LEFT (1<<12) #define RIGHT (1<<13) #define CENTRE (1<<22) volatile unsigned int countval1; volatile unsigned int countval2; //unsigned int rps; int motoron = 0; void Initialisation(void); void rev_print(void); void pwm(void); void counter1(void); void set_screen(void); void main_menu(void); void interrupt(void); void timer3(void); void delay(void); void timer2(void); int main(void) { Initialisation(); /*Intialisation parameters*/ T2TCR = 0x01; /*Timer Start*/ T3TCR = 0x01; /*Timer Start*/ while(1) { rev_print(); } } void Initialisation(void) { char *s = "Exercise 2 - Motor Control"; PCONP |= (1<<22); /*Timer 2 Power*/ PCONP |= (1<<23); /*Timer 2 Power*/ textInit(); /*Initialises Text Display*/ // lcd_init(); pwm(); /*Initialises PWM*/ counter1(); /*Initialise Counter 1*/ timer2(); /*Initialise Timer 2*/ timer3(); /*Initialise Timer 3*/ set_screen(); /*Sets display properties*/ // main_menu(); /*Prints menu options*/ lcd_putString(42, 7, s); lcd_putString(6, 37, s); lcd_putString(5, 46, s); lcd_putString(5, 55, s); interrupt(); /*Enable Interrupt*/ } void pwm(void) { PINSEL2 |= 0x000000C0; /*PWM Pin Config*/ PWM0PCR |= (1<<10); /*PWM Set*/ PWM0MCR |= (1<<1); /*Reset Control*/ PWM0MR0 = 120000; /*PWM Frequency*/ PWM0MR2 = 30000; /*PWM Pulse Width*/ PWM0LER = (1<<2); PWM0TCR = 0x00000009; /*PWM Start*/ } void counter1(void) /*Counter for motor revs*/ { PINSEL3 |= (3 << 6); /*(P1.19) to Counter Mode*/ T1CTCR = 0x05; /*Count Pulses*/ T1TCR = 0x02; /*Counter Reset*/ T1MCR = 0; T1PR = 0; T1TCR = 0x01; /*Counter Enable*/ } void timer2(void) { T2TCR = 0x02; /*Timer Reset*/ T2IR = 1; /*Clear Interrupt Register*/ T2MR0 = 12000000; /*Match Value 1 sec*/ T2MCR = 3; /*Reset Counter and Set Interrupt on Match*/ } void timer3(void) { T3TCR = 0x02; /*Timer Reset*/ T3IR = 1; /*Clear Interrupt Register*/ T3MR0 = 12000000; /*Match Value 1 sec*/ T3MCR = 3; /*Reset Counter and Set Interrupt on Match*/ } void set_screen(void) { lcd_fillScreen(WHITE); /*Screen background = White*/ lcd_fontColor(BLACK, WHITE); /*Sets*/ } void main_menu(void) { // delay(); lcd_putString(42, 7, "Exercise 2 - Motor Control"); lcd_putString(6, 37, " Use the joystick to navigate up and"); lcd_putString(5, 46, " down the menu, the centre button"); lcd_putString(5, 55, " toggles the selected motor speed on"); // lcd_putString(5, 64, " and off."); // lcd_putString(85, 90, "1. 10 RPS"); // lcd_putString(85, 120, "2. 20 RPS"); // lcd_putString(85, 150, "3. 30 RPS"); // lcd_putString(85, 180, "4. 40 RPS"); // lcd_putString(70, 210, "5. Custom Speed"); // lcd_putString(80, 260, "Select a Speed RPS"); // lcd_putString(170, 278, "RPS"); // lcd_putString(5, 300, "***All speeds are approximate due to"); // lcd_putString(20, 309, "varying performance of components***"); } void interruptfunction(void) __attribute__ ((interrupt)); void interruptfunction(void) { T3IR = 1; /*Clear Interrupt Register*/ countval1 = T1TC; VICVectAddr = 0;// clear VIC } void interrupt(void) { VICIntSelect &= ~(1<<27); VICVectAddr27 = (unsigned int)interruptfunction; VICIntEnable |= (1<<27); } void rev_print(void) { unsigned int rps; // if ((T2IR & 1) == 0) // { // T2TCR = 0x01; /*Timer Start*/ // T3TCR = 0x01; /*Timer Start*/ // } // if ((T2IR & 1) == 1) // { while(1) { if(countval1 != countval2) { unsigned int tmp = countval1; rps = tmp - countval2; countval2 = tmp; // if (motoron == 0) // { // rps = 0; // } */ textSetCursor(11, 30); /*Set RPM position*/ lcd_fillRect(143, 278, 162, 285, WHITE); /*Refresh RPM*/ simplePrintf("Current Speed : %d", rps); /*Print RPM to screen*/ T2IR = 1; /*Clear print timer interrupt flag*/ } } }
How do you manage to understand your own code, when you don't use symbol names that represents what they do?
counter1(); /*Initialise Counter 1*/ timer2(); /*Initialise Timer 2*/ timer3(); /*Initialise Timer 3*/ set_screen(); /*Sets display properties*/ // main_menu(); /*Prints menu options*/ lcd_putString(42, 7, s); lcd_putString(6, 37, s); lcd_putString(5, 46, s); lcd_putString(5, 55, s); interrupt(); /*Enable Interrupt*/
Notice all the comments you need, to tell what is happening.
Wouldn't it be so much better with
initialize_rpm_counter(); initialize_rpm_period_timer(); ... enable_rpm_timer_interrupt();
Suddenly no need for comments. And even despite having your comments, a reader can't know what timer2() or timer3() is actually used for.
You have multiple timers that you claim you configure to generate interrupts. But you only register a single interrupt handler. And you do that with hard-coded integers, to make it hard to see exactly which interrupt handler you actually register.
Your busy-loop do claims it clear an interrupt flag - but interrupt flags should normally be cleared in an interrupt service routine. Except in init code, just before enabling the interrupt source.
Maybe you should start by revising your code, and refresh it to be more readable - that is normally the best way to catch bugs. Unreadable logic seldom actually work...
Thank you for your replies, I've taken your comments and provided simplified and hopefully more understandable code below.
#include <lpc24xx.h> #include <textDisplay.h> #include <lcd_grph.h> volatile unsigned int countval1; volatile unsigned int countval2; void initialization(void); void revolution_printer(void); void initialize_pwm(void); void initialize_revolution_counter(void); void initialize_display_settings(void); void print_menu_text(void); void initialize_revolution_count_interrupt(void); void initialize_interrupt_timer(void); void setup_screen(void); int main(void) { initialization(); T3TCR = 0x01; //Start Interrupt Timer while(1) { revolution_printer(); } } void initialization(void) //Configures All Counters/Timers/Interrupts And Sets Display Settings { textInit(); lcd_init(); initialize_pwm(); initialize_revolution_counter(); initialize_interrupt_timer(); setup_screen(); print_menu_text(); initialize_revolution_count_interrupt(); } void initialize_pwm(void) { PINSEL2 |= (3<<6); //Set (P1.3PWM Pin Configuration PWM0PCR |= (1<<10); //PWM Set To PWM0[2] PWM0MR0 = 120000; //PWM Frequency PWM0MCR |= (1<<1); //Reset Count On Match PWM0MR2 = 120000; //PWM Pulse Width PWM0TCR = 0x00000009; //PWM Start } void initialize_revolution_counter(void) { PINSEL3 |= (3<<6); //Set (P1.19) to Counter Mode T1CTCR = 0x05; //Set Timer1 To Count Pulses T1TCR = 0x02; //Reset Count To 0 T1PR = 0; //Set Prescaler To 0 T1TCR = 0x01; //Enable Counter } void initialize_interrupt_timer(void) { PCONP |= (1<<23); //Set Timer3 On In PCONP T3TCR = 0x02; //Reset Timer T3IR = 1; //Clear Timer3 Interrupt Register T3MR0 = 12000000; //Set Match Register Value T3MCR = 3; //On Match Reset Timer And Set Interrupt } void setup_screen(void) { lcd_fillScreen(WHITE); //Screen background = White lcd_fontColor(BLACK, WHITE); //Sets test colour/background colour } void print_menu_text(void) { lcd_putString(42, 7, "Exercise 2 - Motor Control"); } void interruptfunction(void) __attribute__ ((interrupt)); void interruptfunction(void) { T3IR = 1; //Clear Timer3 Interrupt Register countval1 = T1TC; //Set Counter1 Value To Countval1 VICVectAddr = 0; //Clear Vectored Interrupt Controller } void initialize_revolution_count_interrupt(void) { VICIntSelect &= ~(1<<27); //Set Interrupt To Timer3 Interrupt VICVectAddr27 = (unsigned int)interruptfunction; //Assign Interrupt Service Routine VICIntEnable |= (1<<27); //Enable VIC } void revolution_printer(void) { unsigned int rps; while(1) { if(countval1 != countval2) //On Change In Countval1 { unsigned int tmp = countval1; rps = tmp - countval2; countval2 = tmp; textSetCursor(11, 30); lcd_fillRect(143, 278, 162, 285, WHITE); simplePrintf("Current Speed : %d", rps); //Print RPS To Display } } }
The code is easier to read now. But still a number of things to consider.
volatile unsigned int countval1; volatile unsigned int countval2;
Two global, volatile, variables. But only one used in an interrupt - why is the other one volatile? And why is the other one global, when it is only used within a single function - a function that doesn't return? And why isn't the name mentioning that you count motor pulses? "count value" is rather generic. And one of the variables is the last value processed - but no "last" or "current" in the names. "1" and "2" are rather generic.
void interruptfunction(void) __attribute__ ((interrupt)); void interruptfunction(void) { ... }
You don't need two separate lines just to inform the compiler that "interruptfunction" is an interrupt handler.
And how will a reader know that "interruptfunction" reads out motor pulse values? A program can have 10 different "interruptfunction" - so each should get a name that tells what peripherial it services, or what service it performs. This basically is your "one_second_isr()", since I think your goal is to have the timer interrupt once/second. Or it's your "capture_motor_count_isr()". All depending on if the focus is on what is activating the ISR or what goal you have with it.
But you don't get an interrupt every 1000ms.
T3MR0 = 12000000; //Set Match Register Value
You program with a "magic" value, without any information to the reader how you came up with this value. We might guess that the timer is fed by a 12MHz signal, and that you intend to get an interrupt once/second. But in that case, the user manual for the processor tells you that your formula should contain a "-1".
#define TIMER3_PCLK 12000000 // clock frequency to timer 3 #define TIMER3_FREQ 1 // want 1 Hz interrupt ... T3MR0 = (TIMER3_PCLK/TIMER3_FREQ)-1;
Next thing - your loop is:
while (1) { if (countval1 != countval2) { ... } }
countval1 gets updated once per timer interrupt. Have you made sure that the code within this function is always fast enough to perform your printout and return back to start check the above countval1 != countval2 comparison before the interrupt happens again? Because if the ISR gets called twice before you sample countval1, then you will print twice as large rps as expected. And if the ISR manages to get called three times before you sample countval1 and updates countval2, you will print a three times too large value.
The code - as written - will only print correct values as long as you fulfill your hard real-time requirements.
You could have your ISR instead to this:
volatile unsigned rpm = 0; ... void rpm_capture_isr() { static unsigned prev_count; unsigned curr_count; prev_count = curr_count; curr_count = T1TC; rpm = curr_count-prev_count; ... }
And you could have your printout loop do this:
void display_rpm() { unsigned prev_rpm = (unsigned)-1; unsigned curr_rpm; for (;;) { curr_rpm = rpm; // snapshot volatile value if (prev_rpm != curr_rpm) { prev_rpm = curr_rpm; print("current rpm:%u +",curr_rpm); ... } } }
Now it doesn't matter if the printout code is slow. All that happens is that it will miss one printout - but the next time it has time to capture the "rpm" value it will still print the pulse count for one timer tick interval.
I've made changes you suggested to the variables as well as adjusting the T3MR0 to include the clock feed speed. I've included the updated code below.
With regards to only using one line to inform the compiler that the function 'pulse_count' is an interrupt handler, I'm not sure how I'd do that.
The code is fast enough to perform the printout and check the comparison before the interrupt occurs again.
The problem is that with the function "print_menu_text" enabled, it results in the rps printout being roughly 1.1x what it should be, but the motor speed/revolution count is not changing. The rps printout multiplication increases with the more lines of lcd_putString there is within "print_menu_text".
Thanks, Joe
#include <lpc24xx.h> #include <textDisplay.h> #include <lcd_grph.h> #define TIMER3_PCLK 12000000 // clock frequency to timer 3 #define TIMER3_FREQ 1 // want 1 Hz interrupt volatile unsigned int pulse_count_current; void initialization(void); void revolution_printer(void); void initialize_pwm(void); void initialize_revolution_counter(void); void initialize_display_settings(void); void print_menu_text(void); void initialize_revolution_count_interrupt(void); void initialize_interrupt_timer(void); void setup_screen(void); int main(void) { initialization(); T3TCR = 0x01; //Start Interrupt Timer while(1) { revolution_printer(); } } void initialization(void) //Configures All Counters/Timers/Interrupts And Sets Display Settings { textInit(); lcd_init(); initialize_pwm(); initialize_revolution_counter(); initialize_interrupt_timer(); setup_screen(); print_menu_text(); initialize_revolution_count_interrupt(); } void initialize_pwm(void) { PINSEL2 |= (3<<6); //Set (P1.3PWM Pin Configuration PWM0PCR |= (1<<10); //PWM Set To PWM0[2] PWM0MR0 = 120000; //PWM Frequency PWM0MCR |= (1<<1); //Reset Count On Match PWM0MR2 = 120000; //PWM Pulse Width PWM0TCR = 0x00000009; //PWM Start } void initialize_revolution_counter(void) { PINSEL3 |= (3<<6); //Set (P1.19) to Counter Mode T1CTCR = 0x05; //Set Timer1 To Count Pulses T1TCR = 0x02; //Reset Count To 0 T1PR = 0; //Set Prescaler To 0 T1TCR = 0x01; //Enable Counter } void initialize_interrupt_timer(void) { PCONP |= (1<<23); //Set Timer3 On In PCONP T3TCR = 0x02; //Reset Timer T3IR = 1; //Clear Timer3 Interrupt Register T3MR0 = (TIMER3_PCLK/TIMER3_FREQ)-1; //Set Match Register Value T3MCR = 3; //On Match Reset Timer And Set Interrupt } void setup_screen(void) { lcd_fillScreen(WHITE); //Screen background = White lcd_fontColor(BLACK, WHITE); //Sets test colour/background colour } void print_menu_text(void) { lcd_putString(42, 7, "Exercise 2 - Motor Control"); } void pulse_count(void) __attribute__((interrupt)); void pulse_count(void) { T3IR = 1; //Clear Timer3 Interrupt Register pulse_count_current = T1TC; //Set Counter1 Value To pluse_count_current VICVectAddr = 0; //Clear Vectored Interrupt Controller } void initialize_revolution_count_interrupt(void) { VICIntSelect &= ~(1<<27); //Set Interrupt To Timer3 Interrupt VICVectAddr27 = (unsigned int)pulse_count; //Assign Interrupt Service Routine VICIntEnable |= (1<<27); //Enable VIC } void revolution_printer(void) { unsigned int rps; unsigned int pulse_count_last = 0; while(1) { if(pulse_count_current != pulse_count_last) //On Change In pulse_count_current { unsigned int tmp = pulse_count_current; rps = tmp - pulse_count_last; pulse_count_last = tmp; textSetCursor(11, 30); lcd_fillRect(143, 278, 162, 285, WHITE); simplePrintf("Current Speed : %d", rps); //Print RPS To Display } } }
Might your display code turn off interrupts, so the ISR can't be directly invoked to capture the counter value?
You haven't told what compiler you use, but doesn't it accept the following?
void __attribute__ ((interrupt)) pulse_count(void) { ... }
Before implementing the interrupt I used a timer to capture the counter value instead, so I don't believe it to be that.
The compiler accepts the code you provided, thank you.
You implemented a timer? How?
If the display code makes use of slow interrupt disable code, it can make big effects on the performance.
Extra printouts should not get the processor to change the PWM ratio. Extra printouts should not get more voltage to the motor.
So what then remains, is that your 1-second timing window becomes longer than 1 second, making you count more pulses.
Or that the display output creates noise that makes the processor see spurious extra pulses.
But neither of the two are likely to "multiplies the reading of the motor revolutions" - unless you manage to get the pulse sensor to produce ringing on the pulses, so a quick processor can capture 2, 3 or more count values for each pulse. And the processor can capture pulses in the MHz range, so high-amplitude ringing on rising or falling flank of the pulse signal can be captured as additional pulses - this should be visible with an oscilloscope.
Prior to implementing the interrupt, there was a timer that after one second, would check the pulse count value and return it to the print function.
The maximum speed of the motor, using the maximum pulse width set in the code, is 120rps with fluctuation of +/-10rps (the motors are fairly poor quality). This is confirmed with both an oscilloscope as well as the display printout with the print_menu_text function disabled.
If I enable the print_menu_text function and have around 14 lines of lcd_putString in there, the oscilloscope will still show 120rps but the display will printout something like 210-290rps.
But if your 14 lines of lcd_putString makes the function take more than 1 second, then your code - as posted above - will take the pulse count difference for 2 seconds and print. So the printed value will indicate twice the motor speed. 210-290 rps printed will correspond to a true speed of 105-145 rps.
That was why I posted a suggested ISR change where the ISR itself computed the difference. With the subtraction in the ISR you could perform one printout every 10 seconds an would still print the pulse count for a one-second interval instead of printing a 10 times too large count.
I've implemented the ISR you suggested as well as the print function, code included below. It's currently printing the running total pulse count rather than that of just the past second, but that shouldn't be too difficult to fix. Unfortunately it is still printing a 'multiplied' pulse count with the 'print_menu_text' function enabled.
#include <lpc24xx.h> #include <textDisplay.h> #include <lcd_grph.h> #define TIMER3_PCLK 12000000 // clock frequency to timer 3 #define TIMER3_FREQ 1 // want 1 Hz interrupt volatile unsigned rps = 0; void initialization(void); void revolution_printer(void); void initialize_pwm(void); void initialize_revolution_counter(void); void initialize_display_settings(void); void print_menu_text(void); void initialize_revolution_count_interrupt(void); void initialize_interrupt_timer(void); void setup_screen(void); void pulse_count(void); int main(void) { initialization(); T3TCR = 0x01; //Start Interrupt Timer while(1) { revolution_printer(); } } void initialization(void) //Configures All Counters/Timers/Interrupts And Sets Display Settings { textInit(); lcd_init(); initialize_pwm(); initialize_revolution_counter(); initialize_interrupt_timer(); setup_screen(); print_menu_text(); initialize_revolution_count_interrupt(); } void initialize_pwm(void) { PINSEL2 |= (3<<6); //Set (P1.3PWM Pin Configuration PWM0PCR |= (1<<10); //PWM Set To PWM0[2] PWM0MR0 = 120000; //PWM Frequency PWM0MCR |= (1<<1); //Reset Count On Match PWM0MR2 = 120000; //PWM Pulse Width PWM0TCR = 0x00000009; //PWM Start } void initialize_revolution_counter(void) { PINSEL3 |= (3<<6); //Set (P1.19) to Counter Mode T1CTCR = 0x05; //Set Timer1 To Count Pulses T1TCR = 0x02; //Reset Count To 0 T1PR = 0; //Set Prescaler To 0 T1TCR = 0x01; //Enable Counter } void initialize_interrupt_timer(void) { PCONP |= (1<<23); //Set Timer3 On In PCONP T3TCR = 0x02; //Reset Timer T3IR = 1; //Clear Timer3 Interrupt Register T3MR0 = (TIMER3_PCLK/TIMER3_FREQ)-1; //Set Match Register Value T3MCR = 3; //On Match Reset Timer And Set Interrupt } void setup_screen(void) { lcd_fillScreen(WHITE); //Screen background = White lcd_fontColor(BLACK, WHITE); //Sets test colour/background colour } void print_menu_text(void) { lcd_putString(42, 7, "Exercise 2 - Motor Control"); lcd_putString(6, 37, " Use the joystick to navigate up and"); lcd_putString(5, 46, " down the menu, the centre button"); lcd_putString(5, 55, " toggles the selected motor speed on"); lcd_putString(5, 64, " and off."); lcd_putString(85, 90, "1. 1000 RPS"); lcd_putString(85, 120, "2. 2000 RPS"); lcd_putString(85, 150, "3. 3000 RPS"); lcd_putString(85, 180, "4. 4000 RPS"); lcd_putString(70, 210, "5. Custom Speed"); lcd_putString(80, 260, "Select a Speed RPS"); lcd_putString(170, 278, "RPS"); lcd_putString(5, 300, "***All speeds are approximate due to"); lcd_putString(20, 309, "varying performance of components***"); } void __attribute__((interrupt)) pulse_count(void) { T3IR = 1; //Clear Timer3 Interrupt Register static unsigned prev_count; unsigned curr_count; prev_count = curr_count; curr_count = T1TC; rps = curr_count-prev_count; VICVectAddr = 0; //Clear Vectored Interrupt Controller } void initialize_revolution_count_interrupt(void) { VICIntSelect &= ~(1<<27); //Set Interrupt To Timer3 Interrupt VICVectAddr27 = (unsigned int)pulse_count; //Assign Interrupt Service Routine VICIntEnable |= (1<<27); //Enable VIC } void revolution_printer(void) { unsigned prev_rpm = (unsigned)-1; unsigned curr_rpm; for (;;) { curr_rpm = rps; // snapshot volatile value if (prev_rpm != curr_rpm) { prev_rpm = curr_rpm; textSetCursor(11, 30); simplePrintf("current rpm:%u",curr_rpm); } } }
static unsigned prev_count; unsigned curr_count; curr_count = T1TC; rps = curr_count-prev_count; prev_count = curr_count;
So - have you actually measured how long time it takes for your loop, when you have the extra text emitted? Easy to do by having a GPIO pin:
for (;;) { clear_trace_print_pin(); curr_rpm = rps; // snapshot volatile value if (prev_rpm != curr_rpm) { set_trace_print_pin(); ... } }
That will get you an I/O pin that shows how long your code takes.
And if you have a:
toggle_trace_capture_pin();
in the ISR, then you would get a 0.5 Hz square wave from the ISR.
Compare these two signals on an oscilloscope, and you can see how fast your code will react and how much time it will consume for showing the speed values.
I've added what I believe you're suggesting to the code below. On pin 10 for monitoring the print there is a pulse with a width of 1.4ms at a frequency of 1Hz.
I'm currently unable to get an output from pin 11 monitoring the ISR.
This is my current code:
#include <lpc24xx.h> #include <textDisplay.h> #include <lcd_grph.h> #define TIMER3_PCLK 12000000 // clock frequency to timer 3 #define TIMER3_FREQ 1 // want 1 Hz interrupt volatile unsigned rps = 0; void initialization(void); void revolution_printer(void); void initialize_pwm(void); void initialize_revolution_counter(void); void initialize_display_settings(void); void print_menu_text(void); void initialize_revolution_count_interrupt(void); void initialize_interrupt_timer(void); void setup_screen(void); void pulse_count(void); void set_trace_print_pin(void); void clear_trace_print_pin(void); void toggle_trace_capture_pin(void); int main(void) { initialization(); T3TCR = 0x01; //Start Interrupt Timer while(1) { revolution_printer(); } } void initialization(void) //Configures All Counters/Timers/Interrupts And Sets Display Settings { FIO0DIR |= (3<<10); textInit(); lcd_init(); initialize_pwm(); initialize_revolution_counter(); initialize_interrupt_timer(); setup_screen(); print_menu_text(); initialize_revolution_count_interrupt(); } void initialize_pwm(void) { PINSEL2 |= (3<<6); //Set (P1.3PWM Pin Configuration PWM0PCR |= (1<<10); //PWM Set To PWM0[2] PWM0MR0 = 120000; //PWM Frequency PWM0MCR |= (1<<1); //Reset Count On Match PWM0MR2 = 120000; //PWM Pulse Width PWM0TCR = 0x00000009; //PWM Start } void initialize_revolution_counter(void) { PINSEL3 |= (3<<6); //Set (P1.19) to Counter Mode T1CTCR = 0x05; //Set Timer1 To Count Pulses T1TCR = 0x02; //Reset Count To 0 T1PR = 0; //Set Prescaler To 0 T1TCR = 0x01; //Enable Counter } void initialize_interrupt_timer(void) { PCONP |= (1<<23); //Set Timer3 On In PCONP T3TCR = 0x02; //Reset Timer T3IR = 1; //Clear Timer3 Interrupt Register T3MR0 = (TIMER3_PCLK/TIMER3_FREQ)-1; //Set Match Register Value T3MCR = 3; //On Match Reset Timer And Set Interrupt } void setup_screen(void) { lcd_fillScreen(WHITE); //Screen background = White lcd_fontColor(BLACK, WHITE); //Sets test colour/background colour } void print_menu_text(void) { lcd_putString(42, 7, "Exercise 2 - Motor Control"); lcd_putString(6, 37, " Use the joystick to navigate up and"); lcd_putString(5, 46, " down the menu, the centre button"); lcd_putString(5, 55, " toggles the selected motor speed on"); lcd_putString(5, 64, " and off."); lcd_putString(85, 90, "1. 1000 RPS"); lcd_putString(85, 120, "2. 2000 RPS"); lcd_putString(85, 150, "3. 3000 RPS"); lcd_putString(85, 180, "4. 4000 RPS"); lcd_putString(70, 210, "5. Custom Speed"); lcd_putString(80, 260, "Select a Speed RPS"); lcd_putString(170, 278, "RPS"); lcd_putString(5, 300, "***All speeds are approximate due to"); lcd_putString(20, 309, "varying performance of components***"); } void __attribute__((interrupt)) pulse_count(void) { T3IR = 1; //Clear Timer3 Interrupt Register static unsigned prev_count; unsigned curr_count; toggle_trace_capture_pin(); prev_count = curr_count; curr_count = T1TC; rps = curr_count-prev_count; VICVectAddr = 0; //Clear Vectored Interrupt Controller } void initialize_revolution_count_interrupt(void) { VICIntSelect &= ~(1<<27); //Set Interrupt To Timer3 Interrupt VICVectAddr27 = (unsigned int)pulse_count; //Assign Interrupt Service Routine VICIntEnable |= (1<<27); //Enable VIC } void revolution_printer(void) { unsigned prev_rpm = (unsigned)-1; unsigned curr_rpm; for (;;) { clear_trace_print_pin(); curr_rpm = rps; // snapshot volatile value if (prev_rpm != curr_rpm) { set_trace_print_pin(); prev_rpm = curr_rpm; textSetCursor(11, 30); simplePrintf("current rpm:%u",curr_rpm); } } } void set_trace_print_pin(void) //Pin 10 for Print Monitoring { FIO0SET |= (1<<10); } void clear_trace_print_pin(void) { FIO0CLR |= (1<<10); } void toggle_trace_capture_pin(void) //Pin 11 for ISR Monitoring unsigned int toggle; if (toggle == 0) { FIO0SET |= (1<<11); toggle = 1; } else { FIO0CLR |= (1<<11); toggle = 0; } }
Sorry noticed I had deleted the { from the toggle_trace_capture_pin function whilst copying.
Here's the correct code:
#include <lpc24xx.h> #include <textDisplay.h> #include <lcd_grph.h> #define TIMER3_PCLK 12000000 // clock frequency to timer 3 #define TIMER3_FREQ 1 // want 1 Hz interrupt volatile unsigned rps = 0; void initialization(void); void revolution_printer(void); void initialize_pwm(void); void initialize_revolution_counter(void); void initialize_display_settings(void); void print_menu_text(void); void initialize_revolution_count_interrupt(void); void initialize_interrupt_timer(void); void setup_screen(void); void pulse_count(void); void set_trace_print_pin(void); void clear_trace_print_pin(void); void toggle_trace_capture_pin(void); int main(void) { initialization(); T3TCR = 0x01; //Start Interrupt Timer while(1) { revolution_printer(); } } void initialization(void) //Configures All Counters/Timers/Interrupts And Sets Display Settings { FIO0DIR |= (3<<10); textInit(); lcd_init(); initialize_pwm(); initialize_revolution_counter(); initialize_interrupt_timer(); setup_screen(); print_menu_text(); initialize_revolution_count_interrupt(); } void initialize_pwm(void) { PINSEL2 |= (3<<6); //Set (P1.3PWM Pin Configuration PWM0PCR |= (1<<10); //PWM Set To PWM0[2] PWM0MR0 = 120000; //PWM Frequency PWM0MCR |= (1<<1); //Reset Count On Match PWM0MR2 = 120000; //PWM Pulse Width PWM0TCR = 0x00000009; //PWM Start } void initialize_revolution_counter(void) { PINSEL3 |= (3<<6); //Set (P1.19) to Counter Mode T1CTCR = 0x05; //Set Timer1 To Count Pulses T1TCR = 0x02; //Reset Count To 0 T1PR = 0; //Set Prescaler To 0 T1TCR = 0x01; //Enable Counter } void initialize_interrupt_timer(void) { PCONP |= (1<<23); //Set Timer3 On In PCONP T3TCR = 0x02; //Reset Timer T3IR = 1; //Clear Timer3 Interrupt Register T3MR0 = (TIMER3_PCLK/TIMER3_FREQ)-1; //Set Match Register Value T3MCR = 3; //On Match Reset Timer And Set Interrupt } void setup_screen(void) { lcd_fillScreen(WHITE); //Screen background = White lcd_fontColor(BLACK, WHITE); //Sets test colour/background colour } void print_menu_text(void) { lcd_putString(42, 7, "Exercise 2 - Motor Control"); lcd_putString(6, 37, " Use the joystick to navigate up and"); lcd_putString(5, 46, " down the menu, the centre button"); lcd_putString(5, 55, " toggles the selected motor speed on"); lcd_putString(5, 64, " and off."); lcd_putString(85, 90, "1. 1000 RPS"); lcd_putString(85, 120, "2. 2000 RPS"); lcd_putString(85, 150, "3. 3000 RPS"); lcd_putString(85, 180, "4. 4000 RPS"); lcd_putString(70, 210, "5. Custom Speed"); lcd_putString(80, 260, "Select a Speed RPS"); lcd_putString(170, 278, "RPS"); lcd_putString(5, 300, "***All speeds are approximate due to"); lcd_putString(20, 309, "varying performance of components***"); } void __attribute__((interrupt)) pulse_count(void) { T3IR = 1; //Clear Timer3 Interrupt Register static unsigned prev_count; unsigned curr_count; toggle_trace_capture_pin(); prev_count = curr_count; curr_count = T1TC; rps = curr_count-prev_count; VICVectAddr = 0; //Clear Vectored Interrupt Controller } void initialize_revolution_count_interrupt(void) { VICIntSelect &= ~(1<<27); //Set Interrupt To Timer3 Interrupt VICVectAddr27 = (unsigned int)pulse_count; //Assign Interrupt Service Routine VICIntEnable |= (1<<27); //Enable VIC } void revolution_printer(void) { unsigned prev_rpm = (unsigned)-1; unsigned curr_rpm; for (;;) { clear_trace_print_pin(); curr_rpm = rps; // snapshot volatile value if (prev_rpm != curr_rpm) { set_trace_print_pin(); prev_rpm = curr_rpm; textSetCursor(11, 30); simplePrintf("current rpm:%u",curr_rpm); } } } void set_trace_print_pin(void) //Pin 10 for Print Monitoring { FIO0SET |= (1<<10); } void clear_trace_print_pin(void) { FIO0CLR |= (1<<10); } void toggle_trace_capture_pin(void) //Pin 11 for Print Monitoring { unsigned int toggle; if (toggle == 0) { FIO0SET |= (1<<11); toggle = 1; } else { FIO0CLR |= (1<<11); toggle = 0; } }