|
|
View previous topic :: View next topic |
Author |
Message |
TokTok
Joined: 22 Jan 2014 Posts: 35
|
Simultaneous control of delay operated LEDs |
Posted: Fri Apr 25, 2014 9:45 am |
|
|
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 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.
How does one go about this in an efficient way?
I’m running the code on my PIC18F2580 PIC MCU and as I mentioned it runs fine but I would like to engage the LED simultaneously not sequentially.
Note: As I come from a java background is there any threading within the CCS library?
Any help is appreciated.
TokTok
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);
}
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Apr 25, 2014 11:00 am |
|
|
since you are using A6,A7 for the oscillator -
bit mapping your led on/off commands into a shadow BYTE
for port A would do what you want in a very clean way |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Sat Apr 26, 2014 1:59 am |
|
|
This is not quite the same as true multi-tasking, but may do what you want.
Set up one of the timers to give (say) a 1ms tick.
Use the tick to trigger an ISR.
Set up a tick_counter for each of your tasks.
In the ISR, decrement all the tick_counters.
In main() you simply poll each tick_counter in turn, and do the necessary.
OR
You can increment counters. Whichever you are most comfortable with.
I prefer to set tick_counters to a value for the delay needed, and watch for reaching zero.
In the ISR I also prevent the tick_counters going below zero.
Mike |
|
|
TokTok
Joined: 22 Jan 2014 Posts: 35
|
Simultaneous control of delay operated LEDs |
Posted: Mon Apr 28, 2014 4:29 am |
|
|
Hi all,
Following the advice from Mike Walne plus the sample code in the CCS C compiler manual the code below is my first attempt to enable the multiple operation of relays using a timer and its interrupt.
Apologies if the below approach sounds silly but I’m learning about interrupts as I try to use them to provide a solution to my task.
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_B2
#define REL_2 PIN_B3
#define REL_3 PIN_A3
#define REL_4 PIN_B2
#define REL_5 PIN_B3
int LED_flag;
#INT_TIMER1
void LED_timer() {
if(LED_flag == 1) {
output_toggle(REL_1);
set_timer1(0xFC4F);
output_toggle(REL_1);
} else if(LED_flag == 2) {
output_toggle(REL_2);
} else if(LED_flag == 3) {
output_toggle(REL_3);
set_timer1(0xFC4F);
output_toggle(REL_3);
} else if(LED_flag == 4) {
output_toggle(REL_4);
} else if(LED_flag == 5) {
output_toggle(REL_5);
set_timer1(0xFC4F);
output_toggle(REL_5);
}
}
void main() {
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
output_low(REL_1);
output_low(REL_2);
output_low(REL_3);
output_low(REL_4);
output_low(REL_5);
LED_flag = 1;
LED_flag = 2;
LED_flag = 3;
LED_flag = 4;
LED_flag = 5;
}
|
The code builds successfully but doesn’t work as I think it should.
Any suggestions/corrections regarding where I’m going wrong with my approach.
As I mentioned I’m also trying to grasp the use of timer interrupts (I’ve utilised the serial interrupt successfully on past occasions hence I’ve got a good idea on how the interrupt concept works).
Thanks for any help. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19540
|
|
Posted: Mon Apr 28, 2014 5:04 am |
|
|
OK.
First problem is speed. Your timer is counting in single machine instructions. Loading with FC4F, means it'll trigger next just 945 instructions later (counter counts _up_ and interrupt fires when it wraps from FFFF to 0000. 945 instructions is just under 1mSec. You are not going to see much....
Then your four instructions selecting the different things to do, are all executed before the timer triggers even once. So LED+flag gets to 5, before the timer gets called...
You need to have each change wait till the interrupt is triggered.
Then the code 'drops off the end'. Classic problem here. Remember _your code is the only thing running_. When writing code on a PC, you are running 'inside' an operating system. When the code drops off the end, you go back to running the OS. On the PIC, there is nothing else in the chip, and when the code runs off the end, everything stops (the chip goes to sleep).
Then in a couple of your states, you toggle the output twice. You are probably never going to see this. Just a couple of uSec apart....
If you mean these to be separated by 1mSec, then they need to become new states.
Best Wishes |
|
|
TokTok
Joined: 22 Jan 2014 Posts: 35
|
|
Posted: Mon Apr 28, 2014 8:49 am |
|
|
Hi Ttelmah,
Thanks for the reply.
I don't fully get the following bit of your suggestion below:
Then your four instructions selecting the different things to do, are all executed before the timer triggers even once. So LED+flag gets to 5, before the timer gets called...
You need to have each change wait till the interrupt is triggered.
From my code below when LED_flag == 1, how should I look to set my delay in the ISR i.e. using set_timer() to toggle REL_1 and REL_2 assuming the delay between setting the value of LED_flag in the infinite while loop in the main varies.
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_B2
#define REL_2 PIN_B3
#define REL_3 PIN_A3
#define REL_4 PIN_B2
#define REL_5 PIN_B3
int LED_flag;
#INT_TIMER1
void LED_timer() {
if(LED_flag == 1) {
output_toggle(REL_1);
set_timer1(81);
output_toggle(REL_1);
} else if(LED_flag == 2) {
output_toggle(REL_2);
}
}
void main() {
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
output_low(REL_1);
output_low(REL_2);
output_low(REL_3);
output_low(REL_4);
output_low(REL_5);
while(1) {
LED_flag = 1;
delay_ms(2000);
LED_flag = 2;
}
}
|
As for your advice regarding the classic error I made this is noted and I've applied an infinite while loop so the code won't fall of at the end.
Thanks. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Tue Apr 29, 2014 2:33 am |
|
|
The following code flashes two LEDs at slightly different rates.
They start off in step, slowly go out of step then back in step after ~2 minutes.
I've used a '458 and 16MHz because that's what happens to be plugged into my PICDEM2 board.
The PIC is a close relative of the one you're using, so should not be a problem.
Code: | #include <18F458.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=16000000)
signed int16 rb0_led_timer; //
signed int16 rb1_led_timer;
#int_timer2
void update_timers()
{
rb0_led_timer--;
rb1_led_timer--;
if (rb0_led_timer<0) { rb0_led_timer = 0;}
if (rb1_led_timer<0) { rb1_led_timer = 0;}
}
void main()
{
setup_timer_2(T2_DIV_BY_16, 249, 1); // Generates 1ms tick
enable_interrupts(int_timer2);
enable_interrupts(global);
rb0_led_timer = rb1_led_timer = 0; // clears timers
output_low(pin_b0); // forces defined state
output_low(pin_b1); // "
while( TRUE )
{
if (rb0_led_timer==0) // Tests for time out
{
rb0_led_timer = 250; // Restarts timer
output_toggle (pin_b0); // Flashes LED
}
if (rb1_led_timer==0) // Tests for time out
{
rb1_led_timer = 251; // Restarts timer
output_toggle (pin_b1); // Flashes LED
}
}
} |
Mike |
|
|
Jerson
Joined: 31 Jul 2009 Posts: 125 Location: Bombay, India
|
|
Posted: Tue Apr 29, 2014 6:17 am |
|
|
I prefer to write it this way.
Code: | #int_timer2
void update_timers()
{
if (rb0_led_timer>0) rb0_led_timer--;
if (rb1_led_timer>0) rb1_led_timer--;
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19540
|
|
Posted: Tue Apr 29, 2014 7:24 am |
|
|
Yes, and it is also slightly more code efficient. Forcing sign to be used, is a little extra work.... |
|
|
TokTok
Joined: 22 Jan 2014 Posts: 35
|
|
Posted: Tue Apr 29, 2014 8:26 am |
|
|
Hi All,
@Mike Walne:
Thanks for the reply. I really appreciate the code.
There's just one bit I need to clearly understand is the 249 setup_timer_2(T2_DIV_BY_16, 249, 1);
Based on the CCS C compiler manual, 249 is an int 0-255 that determines when the clock value is reset hence the first time it counts does it count from 249 to 255?
Note: I played around with the 249 and the smaler the values the faster the flashes and vice versa.
Apologies if my reasoning sounds basic.
I get more clearly the other two arguments i.e. T2_DIV_BY_16 being the prescaler and 1 - the postscaler.
Thanks. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Tue Apr 29, 2014 2:31 pm |
|
|
This is microchip's version of how PR2 affects Timer2.
The Timer2 module has an 8-bit period register, PR2.
Timer2 increments from 00h until it matches PR2 and
then resets to 00h on the next increment cycle. PR2 is
a readable and writable register. The PR2 register is
initialized to FFh upon RESET.
So in my case Timer2 counts from 0 to 249 then resets.
That's a total count between resets of 249 + 1 i.e. 250.
The second parameter in the CCS set_up_timer2 goes into the PR2 register
To completely understand what's going on you need to read the microchip manual as well as the CCS one.
Mike |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Tue Apr 29, 2014 2:34 pm |
|
|
Jerson wrote: | I prefer to write it this way.
Code: | #int_timer2
void update_timers()
{
if (rb0_led_timer>0) rb0_led_timer--;
if (rb1_led_timer>0) rb1_led_timer--;
} |
|
Yes, I agree it's better.
Mike |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19540
|
|
Posted: Tue Apr 29, 2014 2:48 pm |
|
|
The 249 value loads 'PR2' (a register in the PIC). Timer2, counts from 0 to PR2 (so gives PR2+1 counts). It is the only timer in the normal PIC's that has the ability to automatically reset 'at' a count. |
|
|
TokTok
Joined: 22 Jan 2014 Posts: 35
|
|
Posted: Thu May 01, 2014 5:12 am |
|
|
Hi Mike and all,
Having read the PIC 18F2580 data sheet and the CCS C manual regarding the TIMER 2, I still cant seem how to piece together how the three arguments that go into the setup_timer_2(); method affect the counting.
Essentially below is what I would like to achieve specifically as per my comments:
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
#define PUSH_BTN_ONE PIN_B4
#define PUSH_BTN_TWO PIN_A3
int push_btn_flag;
#int_timer2
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);
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);
push_btn_flag = 0;
}
}
void main() {
// ???? - Based on needing to count down for varying times dependent on which push button is engaged what do I need to set my prescaler, mode and postcaler parameters to be or do I need to use a different timer may be timer 1
//setup_timer_2(?, ?, ?);
enable_interrupts(int_timer2);
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 user 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(TRUE) {
if(PUSH_BTN_ONE == 0) {
delay_ms(10);
output_toggle(LED_ONE);
push_btn_flag = 1;
} else if (PUSH_BTN_TWO == 0) {
delay_ms(10);
output_toggle(LED_TWO);
push_btn_flag = 2;
}
}
}
|
Thanks. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|