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.
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; } }
This can't work unless the "toggle" variable is static or global - it needs to retain the state between each invocation of the function.
But you can also check the physical pin state directly when toggling.
void toggle_trace_capture_pin(void) //Pin 11 for ISR Monitoring static unsigned int toggle; if (toggle == 0) { FIO0SET |= (1<<11); toggle = 1; } else { FIO0CLR |= (1<<11); toggle = 0; } }
So have you checked how long time your print function takes with full text emitted?
1.6ms with the 14 lines of text enabled. The saw wave is also now visible on the scope with a frequency of 0.5hz.
Can it be assumed from the above information that the lcd_putString isn't causing a significant enough delay to cause the issue?
The delay shouldn't matter.
But have you looked at the flanks of the pulse signal, to make sure you don't get spurious extra-pulses when your code is performing display communication?
Slow flanks and some noise is all it takes to get extra pulses. And the noise is likely higher while you emit data to the display.
When monitoring the signal from the lightgate which is counting the pulses, it doesn't appear to be carrying a lot of noise so I don't think it's reading extra pulses.
Does anyone have any further suggestions on this? Many other people have managed to achieve this so I can only assume there's an error in the code order or initialization.
I've now managed to narrow this down to the number of characters being rather than the lcd_putString function.
The simplified code below demonstrates the issue. The lcd_putString function prints 1 full line of characters, roughly, to the display. Removing some of the words from that, so it only covers about half a line, prints a more representative revolution count.
#include <lpc24xx.h> #include <textDisplay.h> #include <lcd_grph.h> int main(void) { int countval = 0; textInit(); lcd_init(); PINSEL2 |= (3<<6); PWM0PCR |= (1<<10); PWM0MR0 = 120000; PWM0MCR |= (1<<1); PWM0MR2 = 10000; PWM0TCR = 9; PINSEL3 |= (3<<6); T1TCR = 2; T1CTCR = 5; T1TCR = 1; lcd_putString(10,30, "This is a test to check if characters"); while(1) { countval = T1TC; textSetCursor(1,1); simplePrintf("%u", countval); } }