Simultaneous control of momentary LEDs

Thread Starter

TokTok

Joined May 16, 2014
3
Hi all,

Apologies if this sound trivial but currently I’m stuck in terms of figuring out the logic to complete what I suspect is a fairly easy task.

I’m currently turning on my LEDs for a certain amount of time using the output_toggle() method as seen in my code below at the end of my post but I’d like to move on to turning on another relay when I’ve got the first LED currently engaged. So essentially simultaneous operation of the LEDs is what I’m after.

Rich (BB code):
#include<18F2580.h> 

#include <stdlib.h> 
#include <string.h> 

#fuses XT,PUT,NOBROWNOUT,NOWDT,NOPROTECT,NOLVP 
  
#use delay(clock=4000000) 
#use rs232(baud=19200, parity=N, xmit=PIN_C6, rcv=PIN_C7, STREAM=HWAREUART, ERRORS) 
#use rs232(baud=19200, parity=N, xmit=PIN_A4, Stream=PCSOFTWAREUART) 

#define REL_1 PIN_A1 
#define REL_2 PIN_A2 
#define REL_3 PIN_B0 
#define REL_4 PIN_B1 
#define REL_5 PIN_A5 

// 
void main() { 
   output_low(REL_1); 
   output_low(REL_2); 
   output_low(REL_3); 
   output_low(REL_4);    
   output_low(REL_5); 

while(1) { 
   int num, i; 

   for(i=0; i<5; i++) { 

       num = i; 
    
      if(num == 1) { 
         output_toggle(REL_1); 
         delay_ms(10000); 
         output_toggle(REL_1); 
      } else if(num == 2) { 
         output_toggle(REL_2); 
      } else if(num == 3) { 
         output_toggle(REL_3); 
         delay_ms(5000); 
         output_toggle(REL_3); 
      } else if(num == 4) { 
         output_toggle(REL_4); 
      }  else if(num == 5) { 
         output_toggle(REL_5); 
         delay_ms(2500); 
         output_toggle(REL_5); 
      } 
   } 
}
How does one go about this in an efficient way? As I come from a java background is there any threading within the CCS library?

My understanding is you could use timers which I tried below but no success:

Rich (BB code):
#include<18F2580.h>

#include <stdlib.h>
#include <string.h>

#fuses XT,PUT,NOBROWNOUT,NOWDT,NOPROTECT,NOLVP
 
#use delay(clock=4000000) 
#use rs232(baud=19200, parity=N, xmit=PIN_C6, rcv=PIN_C7, STREAM=HWAREUART, ERRORS) 

#define LED_ONE PIN_B2 
#define LED_TWO PIN_B3

int push_btn_flag;
int millisecs, outs;

#int_timer0
void toggle_LED() {
   if(push_btn_flag == 1) {

	    // At this point in time in the interrupt service routineI would like to count down or up for 5 seconds in here so as to turn off LED_ONE after 5 seconds
	    output_toggle(LED_ONE);
	
		if(millisecs++ == 100) {
	    	output_toggle(LED_ONE);
			millisecs = 0;
		}

        push_btn_flag = 0;
   } else if(push_btn_flag == 2) {

      // At this point in time in the interrupt service routine I would like to count down or up for 10 seconds in here so as to turn off LED_ONE after 10 seconds
      output_toggle(LED_TWO);

		if(millisecs++ == 100) {
	    	output_toggle(LED_TWO);
			millisecs = 0;
		}

        push_btn_flag = 0;
   }
}

void main() {

   setup_timer_0(RTCC_EXT_L_TO_H|RTCC_DIV_16);

   enable_interrupts(int_timer0); 
   enable_interrupts(global); 

   output_low(LED_ONE);
   output_low(LED_TWO);

   // Here I essentially want to enable the user to play around with operating the LEDs SIMULTANEOUSLY
   // So say user presses PUSH_BTN_ONE essentially I want it that the push_btn_flag changes and
   // this causes the timer interrupt to start counting down or up based on a specified time i.e. 5 secs

   // Now in the interrupt service routine whilst the counting related to LED one is ongoing if the user were
   // to press PUSH_BTN_TWO then I would like in the interrupt service rountine the counting for LED_TWO 
   // to be started as the push_btn_flag would have been set to two.
   
   while(1) {
      if (input(PIN_B4) == 0){
         delay_ms(10);
         push_btn_flag = 1;
      } else if (input(PIN_A3) == 0){ 
         delay_ms(10);
         push_btn_flag = 2;
      }
   }
}
Edit:
Essentially below is how I would like to use a timer interrupt to provide me with my desired outcome:

Step 1 - In the main() function enable the global interrupts and the specific timer interrupt.

Step 2 - In the interrupt service routine function start watching out for changes i.e. on the push_btn_flag.

The value/state of the push_btn_flag will be changed in the main function when the user presses one of two momentary push button to operate the LEDs for a desired period of time.

So in the interrupt service routine if the push_btn_flag has been set to 1 then LED_ONE is turned on for a specific period of time. Now this specific period of time is what I need the timer ISR to count down and is what I'm stuck on.

The same applies with the turning on of a different LED when the push_btn_flag has been set to 2.

Step 3 - In the main() function watch out for the pressing of the push buttons as explained in step 2 which when pressed changes the push_btn_flag which the timer interrupt service routine is monitoring for change.

Note: My development environment is MPLAB IDE version 8.91.00.00 with my compiler being the CCS C PCWHD compiler version 4.1114.

Any help is appreciated.

TokTok.
 

djsfantasi

Joined Apr 11, 2010
9,163
Without using timer interrupts, one could calculate the end time of the delay for each push button and after checking the push button status, check to see if any of the delays have expired and toggle the LEDs/pins appropriately.

For example, see the following pseudo-code (it has not been compiled nor tested).

Rich (BB code):
void main() { 
unsigned long end_LED_1;
unsigned long end_LED_2;
unsigned long end_LED_3;
unsigned long end_LED_4;
unsigned long end_LED_5;


   output_low(REL_1); 
   output_low(REL_2); 
   output_low(REL_3); 
   output_low(REL_4);    
   output_low(REL_5); 

while(1) { 
   int button; 

   for(button=0; button<5; button++) { 

      switch (button) {
      
	      case 1; {
		output_toggle(REL_1);
		end_LED_1 = millis()+10000;
		}
	      case 2; {
		output_toggle(REL_2);
		}      	
	      case 3: {
		output_toggle(REL_3);
		end_LED_3 = millis()+5000;
		}
	      case 4: {
		output_toggle(REL_4);
		]
	      case 5: {
		output_toggle(REL_5);
		end_LED_1 = millis()+2500;
		}      	

	if ( !(end_LED_1 == 0) AND (end_LED_1 <= millis()) {
	   output_toggle(REL_1);
	   end_LED_1 = 0;
	   }
	if ( !(end_LED_3 == 0) AND (end_LED_3 <= millis()) {
	   output_toggle(REL_3);
	   end_LED_3 = 0;
	   }
	if ( !(end_LED_5 == 0) AND (end_LED_5 <= millis()) {
	   output_toggle(REL_5);
	   end_LED_5 = 0;
	   }
	} 
    }
}
 

Thread Starter

TokTok

Joined May 16, 2014
3
Hi djsfantasi,

Thanks for the idea but mills() which I understand in Arduino returns the number of milliseconds elapsed since the time the program started running isn't available in a PIC.

Any idea on PIC how to go about returning the number of milliseconds elapsed since the the program started running?

I'll around to see how this can be done on a PIC.
 

ErnieM

Joined Apr 24, 2011
8,377
Hello there, and welcome to the forums!

Apologies if this sound trivial but currently I’m stuck in terms of figuring out the logic to complete what I suspect is a fairly easy task.
Completely untrivial. Getting a blinkie LED project to work is a good basis for larger projects.

How does one go about this in an efficient way? As I come from a java background is there any threading within the CCS library?
Yikes. did you say threading? Just as there is no crying in baseball there is NO threading in these PIC devices. Threading is an abstraction not physically realized in a single core device anyway.

In a PIC you are running bare metal, no operating systsm, just your code inside the core.

First off, while I'm familiar with PICs I havn't used the CCS C compiler in a few years, so I may be slightly off here, but with other C compilers it is generally undesirable to do any function calls from inside the ISR. That is because C must do a ton of work to make sure the ISR doesn't step on any variables the main code uses.

Just imagine if the main has called function A() when an interrupt triggers also calling A(). The internal state of the first call can be corrupted by the 2nd call there; some compilers notice this and actually give you two version of function A() to keep them seperate.

Next, the delays inside the ISR are bad indeed. An ISR should be short sweet to the point and done and out. Besides, if button A makes a 5 second delay during that time button B cannot be seen as the single thread is just waiting for the delay to finish.

I'm not quite sure what your buttons and states are, but generally a state machine (google that) can be worked out to handle these situations.

My default method of work is to set up a timer to bang into an ISR every 1 millisecond. In that routine I have a global variable say TickCount I just increment every time. I'll also put that routine to good use to debounce switch inputs by testing switches every 25 mS and ignoring anything that doesn't read the same 2x in a row.

Outside of the ISR TickCount itself can be used as a timer as long as you are careful using it: if longer then 8 bits it can change while you are using it.

There's lots of stuff for you to review here, but here's the short list to start you off.
 

djsfantasi

Joined Apr 11, 2010
9,163
Hi djsfantasi,

Thanks for the idea but mills() which I understand in Arduino returns the number of milliseconds elapsed since the time the program started running isn't available in a PIC.

Any idea on PIC how to go about returning the number of milliseconds elapsed since the the program started running?
Missed that you were running this on a PIC. D'oh. That's where the timer interrupt is necessary. In your ISR, you would only increment a variable every millisecond, hence implementing your own "millis()" function.
 

Thread Starter

TokTok

Joined May 16, 2014
3
@djsfantasi: Thanks. Thats exactly what I'm trying to figure out using the timers.

@ErnieM: Thansk for the welcome. I'll have a look at your suggestion and upload any issue.
 

BobTPH

Joined Jun 5, 2013
9,003
Yikes. did you say threading? Just as there is no crying in baseball there is NO threading in these PIC devices. Threading is an abstraction not physically realized in a single core device anyway.
You do not need multiple cores to run multiple threads. I have a multi-threading kernel that works on midrange PICs. Each thread context is 5 bytes (plus of 2-bytes per level of call needed). I have used this in a project where multiplexing a display is done as a background thread, PWM to a tri-color LED is another thread, and the foreground thread is a user interface. Yes, it could have been done simply with interrupts, but I did this just for fun.

Bob
 
Top