PDA

View Full Version : How to blink 8 LEDs at different rates- concurrently?



rmteo
- 25th April 2010, 16:05
To do the equivalent of the code below with each LED blinking at a different rate - except that all 8 LEDs must blink concurrently.

PORTB = 0
TRISB = 0

MAIN:

LOOP1:
TOGGLE PORTB.0
PAUSE 500
GOTO LOOP1

LOOP2:
TOGGLE PORTB.1
PAUSE 220
GOTO LOOP2

LOOP3:
TOGGLE PORTB.2
PAUSE 380
GOTO LOOP1

LOOP4:
TOGGLE PORTB.3
PAUSE 750
GOTO LOOP4

LOOP5:
TOGGLE PORTB.4
PAUSE 170
GOTO LOOP5

LOOP6:
TOGGLE PORTB.5
PAUSE 400
GOTO LOOP6

LOOP7:
TOGGLE PORTB.6
PAUSE 620
GOTO LOOP7

LOOP8:
TOGGLE PORTB.7
PAUSE 130
GOTO LOOP8

GOTO MAIN

mackrackit
- 25th April 2010, 16:24
You want them all to come on at the same time and drop out at different rates?


PORTB = %11111111
PAUSE XXX
PORTB = %11111110
PAUSE XXX
PORTB = %11111100
...
LOOP OVER...
Something like that?

Melanie
- 25th April 2010, 16:41
Have as many LED's as you have pins...


LoopALED var BYTE
LoopBLED var BYTE
LoopCLED var BYTE
LoopDLED var BYTE
LoopELED var BYTE
LoopFLED var BYTE
LoopGLED var BYTE
LoopHLED var BYTE
'
LoopACON con 50
LoopBCON con 22
LoopCCon con 38
LoopDCon con 75
LoopECon con 17
LoopFCon con 40
LoopGCon con 62
LoopHCon con 13
'
TRISB=0
Loop:
If LoopALED=0 then
LoopALED=LoopACON
Toggle PortB.0
else
LoopALED=LoopALED-1
endif
'
If LoopBLED=0 then
LoopBLED=LoopBCON
Toggle PortB.1
else
LoopBLED=LoopBLED-1
endif
'
If LoopCLED=0 then
LoopCLED=LoopCCON
Toggle PortB.2
else
LoopCLED=LoopCLED-1
endif
'
If LoopDLED=0 then
LoopDLED=LoopDCON
Toggle PortB.3
else
LoopDLED=LoopDLED-1
endif
'
If LoopELED=0 then
LoopELED=LoopECON
Toggle PortB.4
else
LoopELED=LoopELED-1
endif
'
If LoopFLED=0 then
LoopFLED=LoopFCON
Toggle PortB.5
else
LoopFLED=LoopFLED-1
endif
'
If LoopGLED=0 then
LoopGLED=LoopGCON
Toggle PortB.6
else
LoopGLED=LoopGLED-1
endif
'
If LoopHLED=0 then
LoopHLED=LoopHCON
Toggle PortB.7
else
LoopHLED=LoopHLED-1
endif
'
Pause 10
Goto Loop

Homework Done! Melanie's off down the pub...

Now repeat the above (excluding definitions) in less than 8 Lines of Code... (yes it can be done)!

rmteo
- 25th April 2010, 19:34
mack, the LEDs need to blink all at the same time, so that will not work.

Mel, use arrays and a for-next loop - along these lines?

Loop:
for x = 0 to 7
If LoopLED[x] = 0 then
LoopLED[x] = LoopCON[x]
Toggle PORTB.x
else
LoopLED[x] = LoopLED[x]-1
endif
next x
Pause 10
Goto Loop

Melanie
- 25th April 2010, 20:49
Yes, pretty much, but I don't think PortB.x will work, so...



ShadowPortB=0
Loop:
for x = 0 to 7
If LoopLED[x] = 0 then
LoopLED[x] = LoopCON[x]
ShadowPortB.0(x)=ShadowPortB.0(x)^1
else
LoopLED[x] = LoopLED[x]-1
endif
next x
PortB=ShadowPortB
Pause 10
Goto Loop


Where ShadowPort is a BYTE.

Darrel Taylor
- 25th April 2010, 21:06
Just to be different ...
And because she said "less than 8 Lines of Code" ... :)


TRISB = 0
Main:
x = (x + 1) // 8
PORTB.0(x) = !(LoopLED(x) < LoopCON(x))
LoopLED(x) = (LoopLED(x) + 1) // (LoopCON(x) << 1)
if x=7 THEN PAUSE 10
GOTO Main

Darrel Taylor
- 26th April 2010, 03:19
OUCH!

After running my loop in the SIM.
I realized the timing was Waaaayyyyy OFF.
Fortunately, the other examples were "OFF" too ... just not as far. :eek:

I've always said PAUSE is the worst statement in the BASIC language.
It got me again. :(

Here's a version that should compensate ...
It uses the CCP module in "Compare" mode to create a precise 10mS interval.
So when you give a delay value of 50, it actually takes 500mS, on the nose.

Sorry, it's more than 8 lines now. :o

DEFINE OSC 4
DEFINE BLINKYFREQ 100 ; 10mS periods

DATA 50,22,38,75,17,40,62,13 ; default periods for each Output
;----------------------------------------------------------------
CCPR1val CON EXT : @CCPR1val = (OSC*1000000/4)/ BLINKYFREQ
CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L
Timer1 VAR WORD EXT : @Timer1 = TMR1L
CCPIF VAR PIR1.2

LoopLED VAR BYTE[8]
LoopCON VAR BYTE[8]
x VAR BYTE

;----[Initialize]------------------------------------------------
FOR x = 0 to 7 ; load the periods from EEPROM
READ x, LoopCON(x)
NEXT X
;-- setup CCP1 and Start Timer1 --
CCPR1 = CCPR1val ; set compare value
CCP1CON = %00001011 ; compare mode, special event
Timer1 = 0 ; clear Timer1
T1CON.0 = 1 ; start Timer1

TRISB = 0 ; PORTB all OUTPUT

;----[Main Program Loop]----------------------------------------
Main:
x = (x + 1) // 8
PORTB.0(x) = !(LoopLED(x) < LoopCON(x))
LoopLED(x) = (LoopLED(x) + 1) // (LoopCON(x) << 1)
IF x != 7 THEN Main
Waiting: IF !CCPIF THEN Waiting
CCPIF = 0
GOTO Main

Cheers,

Melanie
- 26th April 2010, 07:43
Personally, I'd have reduced PAUSE by a couple of clicks *smiles*

Mike, K8LH
- 26th April 2010, 08:04
Darrel, that algorithm is slick!

Does PBP allow assignments in expressions like C? For example;


while(1)
{ x = (x+1)&7;
portb ^= ((!(LoopLED[x]=(LoopLED[x]+1)%LoopCON[x])) << x);
delay_us(2500);
}

Darrel Taylor
- 26th April 2010, 08:59
Thanks Mike.

Does PBP allow assignments in expressions like C? ...
Nope, not even a +=

However, your x = (x+1)&7 takes much less time than my x = (x + 1) // 8 (Modulus) approach.
What was I thinking? :o

Cool, thanks!

keithdoxey
- 26th April 2010, 09:24
Thanks Mike.

Nope, not even a +=

However, your x = (x+1)&7 takes much less time than my x = (x + 1) // 8 (Modulus) approach.
What was I thinking? :o

Cool, thanks!

DOH!!! The problem with working with so many different languages is that you can misread code. I took " //8 " to be a comment (Javascript style) that you were going to do it 8 times LOL

sayzer
- 26th April 2010, 10:36
If "8 lines" is about the loop part, then here is my code.


Think like a politician. Is is not less then 8?
:D





<font color="#000080"><b>EEPROM </b></font><font color="#FF0000">0</font>,[<font color="#FF0000">50</font>,<font color="#FF0000">22</font>,<font color="#FF0000">38</font>,<font color="#FF0000">75</font>,<font color="#FF0000">17</font>,<font color="#FF0000">40</font>,<font color="#FF0000">62</font>,<font color="#FF0000">13</font>]
<font color="#000080"><i>'RB0 - RB7 time intervals

</i></font>Time <font color="#000080"><b>VAR BYTE</b></font>[<font color="#FF0000">8</font>]
Timer <font color="#000080"><b>VAR BYTE</b></font>[<font color="#FF0000">8</font>]
Temp <font color="#000080"><b>VAR BYTE
</b></font>Preload <font color="#000080"><b>VAR WORD
</b></font>T1CON = <font color="#FF0000">%00000000 </font><font color="#000080"><i>' 1:1 @4Mhz
</i></font>TMR1IF <font color="#000080"><b>VAR </b></font>PIR1.<font color="#FF0000">0 </font><font color="#000080"><i>' An alias for overflow bit.
</i></font>TMR1ON <font color="#000080"><b>VAR </b></font>T1CON.<font color="#FF0000">0


</font>Begin:
Preload = <font color="#FF0000">55543 </font><font color="#000080"><i>'Timer1 preload value for excat 10ms. interval.
</i><b>FOR </b></font>Temp = <font color="#FF0000">0 </font><font color="#000080"><b>TO </b></font><font color="#FF0000">7
</font><font color="#000080"><b>READ </b></font>Temp ,Time[Temp] <font color="#000080"><i>'Fill time-intervals.
</i></font>Timer[Temp] = <font color="#FF0000">0 </font><font color="#000080"><i>'Clear timer array.
</i><b>NEXT </b></font>Temp

T:
TMR1IF = <font color="#FF0000">0
</font>TMR1L = Preload.LowByte
TMR1H = Preload.HighByte
TMR1ON = <font color="#FF0000">1

</font>Start:

<font color="#000080"><b>IF </b></font>TMR1IF <font color="#000080"><b>THEN </b></font>INT_TMR ' 1 line.

<font color="#000080"><b>GOTO </b></font>Start


INT_TMR:
TMR1ON = <font color="#FF0000">0
</font><font color="#000080"><b>FOR </b></font>Temp = <font color="#FF0000">0 </font><font color="#000080"><b>TO </b></font><font color="#FF0000">7
</font><font color="#000080"><b>IF </b></font>Timer[Temp] = Time[Temp] <font color="#000080"><b>THEN
</b></font>PORTB.<font color="#FF0000">0</font>[Temp] = PORTB.<font color="#FF0000">0</font>[Temp] ^<font color="#FF0000">1
</font>Timer[Temp] = <font color="#FF0000">0
</font><font color="#000080"><b>ENDIF
</b></font>Timer[Temp] = Timer[Temp] + <font color="#FF0000">1
</font><font color="#000080"><b>NEXT </b></font>Temp

<font color="#000080"><b>GOTO </b></font>T

<font color="#000080"><b>END



</b></font>

rmteo
- 26th April 2010, 19:25
I am working on the skeleton of a generic task handler that will manage say 16 tasks concurrently. The LEDs are just simulating tasks that occur at set intervals. One of the tasks will be a seconds counter (with an interval of 1000mS) that is the basis of a RTC (good to the accuracy/stability of the HS crystal clock) and accurate timing is therefore an important concern. So using any kind of Pause or Delay function would not work.

Using a hardware timer is really the only way to do it. However, the timer also has to account for whatever time is used up by instruction cycles - which can vary depending on conditional branches and time spent in each task. DT's method of using a CCP in compare mode solves this problem nicely. Thanks.

Bruce
- 26th April 2010, 22:39
Have you seen this? http://www.microchipc.com/Hi-Tech_C_multitask/

rmteo
- 26th April 2010, 23:47
No I have not seen that link, thanks for pointing it out. I looked at Salvo and several other co-operative and pre-emptive RTOSes. Salvo is actually quite nice and has a small footprint. However, at $1,500 for a full-source license, it is somewhat pricey for my current needs - although it must be said that compared to some other commercial (particularly the pre-emptive ones such Avix, CMX, etc.) products, you could still call it a bargain. There are several free (and/or open source) pre-emptive systems for higher end devices but I find them extremely cumbersome to use and very resource hungry. The few that I looked at required about 20K ROM and 5-6K RAM for a useable configuration. I am also wanting something that can be easily ported to other devices/platforms (such as PIC24/dsPIC33 and 32-bit ARM Cortex M0/M3) and can be implemented using only a high level language (whether BASIC or C).

I have implemented what is basically a time-sliced task handler that will do 16 tasks in about 250 bytes of ROM and 75 bytes of RAM - additional tasks require 2 bytes of RAM per task. Small enough to run on a PIC12/16 - reducing the number of tasks could reduce the ROM (and RAM) footprint even further. Works fine - as I mentioned in a previous post, I have an application where an RTC is implemented using one of the tasks as an accurate seconds counter.