08M Code Optimization and Networked 08Ms

rWAVE

Member
As I continue to mess with RGB LEDs, I have written a working, proof-of-concept program on an 08M to generate three, independent synchronous PWM outputs. Although the frequency won't be high enough (LED flicker) on an 08M for my application, it does allow me to test this algorithm. Currently, my program can handle RGB counters, ranging from 2-bits each to 8-bits each. This allows for discrete colors, ranging from 64 (4x4x4 for 2-bit) colors to 16.7 million (256x256x256 for 8-bit) colors. I ultimately plan to program a standard pic (actually, reprogram an extra 08M) when the program is finished for higher PWM frequencies.

I have a couple of questions:

1. I would like to "tighten" up my existing code, hopefully making it run faster on the 08M. After seeing Hippy's magic, I was hoping someone could suggest programming improvements to the following code:

Code:
Do
  counter = counter +1 & mask
  if counter = 0 then : PWMgen = red ^ green ^ blue : endif

  Rcounter = Rcounter + 1 & mask
  if Rcounter = 0 then
    if Rduty = mask then
      Rcounter = Rcounter + 1
    else var = red : endif
  endif
  gosub update

  Gcounter = Gcounter + 1 & mask
  if Gcounter = 0 then
    if Gduty = mask then
      Gcounter = Gcounter + 1
    else var = green : endif
  endif
  gosub update

  Bcounter = Bcounter + 1 & mask
  if Bcounter = 0 then
    if Bduty = mask then
      Bcounter = Bcounter + 1
    else var = blue : endif
  endif
  gosub update
  pins = PWMgen
loop

update:
  PWMgen = PWMgen xor var
  var = 0
return
2. Second, does anyone have any successful experience taking a Picaxe program into PicBASIC Pro (editing as necessary) to generate the hex file, and ultimately reprogramming an 08M? Recently, I think there was a thread talking about a DIY programmer (not the Microchip Pickit2), but I can't seem to find it. As I have never programmed a pic before, I am looking for suggestions to do it SIMPLY!

3. Finally, I plan to dedicate this chip to generating the PWM signals and another 08M for control and running my application. As I have not connected two 08Ms together before, I would appreciate suggestions on how to package the data to send to the "slave" PWM generator, as I need to do it quickly (every 100ms-500ms). Initially, I need to send a control signal (number of bits per color counter and uC clock speed) and then ongoing RGB color update information. I think each can be done in a single byte, but do I need to worry about a CRC check to verify successful transmission? I think not since another update comes quickly. Is any form of handshake required or just send the data?

All comments welcome.

Richard
 
Last edited:

Mycroft2152

Senior Member
That is an interesting code snippet. Please post the entire program so it can be fully understood and run under the simulator.

One way to speed it up is to increase the clock speed using the setfreq command.
 

Brietech

Senior Member
I would second the "post the entire code" sentiment. Not sure how close you are to hitting the code limit on the 08m. If you can spare the extra bytes, get rid of the "gosub" for update, and just post the 2 commands inline on all 3 occasions. That will avoid a penalty for having to process both the gosub and the return command.

08M's are fun challenges for cramming useful code into =)
 

hippy

Technical Support
Staff member
Also, unrolling the nested If-Then-Else-EndIf conditions may save a couple of bytes, go back to using the If-Then-Label style. It's less clear but likely shorter code.

It might also be possible to put the If conditionals into Gosubs and have Returns to prematurely drop out of that to save any Goto's around unused conditional If's. You'd have to experiment and keep a backup of known working code as it's hard to tell if an optimisation will improve or worsen things.
 

Mycroft2152

Senior Member
I noticed a slight inconsistancy in your format. You are using both "^" and "XOR" in statements. In PICAXE BASIC both mean the same. Is this just a shorthand?

Note: Some versions of BASIC use the "^" as a power indicator, ie, 2^N. 2^3 is the same as 2*2*2
 

Michael 2727

Senior Member
When scrounging for the very last byte, I also check
the pause values, changing -
e.g.
Pause 20
Pause 20
for
Pause 15 ' 0 to 15
Pause 15, ' will get you an extra byte.

e.g.
Pause 300
Pause 300
for
Pause 250 ' 16 to 255
Pause 250, ' will get you an extra couple of bytes.

Not only the pause values, any muneric value will work.
Just trimming back the numbers to <= 15, 255, 1023 has
gotten me out of many a jam.

$0.02c
 

rWAVE

Member
Program Listing

The following I believe was my initial version of the working program. It was written for the PICAXE as simply a demonstration project due to the ease of programming, testing, debug and simulation. Rev-Ed has some wonderful, easy to use tools and I am thankful they exist since my goal was to quickly code the algorithm described in an earlier post.

Please note that although this simple program works, it is not very useful due to its limited execution speed. Since my needs require MINIMUM frequencies of 800-1000Hz from these PWM streams, I must find a faster solution. Since I haven't re-programmed an 08M yet (overwriting the boot loader), I am hoping for some guidance from forum members who have this experience.

Richard

Code:
' ##############################################################
' ##	Program: 3-Channel PWM Generator.bas                  ##
' ##	  Title: 3-Synchronous/Independent PWM Outputs        ##
' ##	Version: 0.1a                                         ##
' ##	 Status: Working                                      ##
' ##	   Date: September 2007                               ##
' ##	 PICAXE: 08M                                          ##
' ##	 Editor: 5.1.5 (Enhanced Compiler)                    ##
' ##############################################################

' ########################
' ##  Program Overview  ##
' ########################

' This program demonstrates the use of a PICAXE 08M to generate 3 programmable,
' synchronous and independent PWM outputs for controlling an RGB LED.  Since
' program execution speed is severely limited, LED flicker is unavoidable.
' Higher PWM frequencies are possible using fewer PWM streams or compiling the
' code and programming a standard pic uC.


'                        PICAXE 08M
'                       (PIC 12F683)                     
'			.---    ---.
'                       |   \__/   |
'                +V ---o|1        8|o--- 0V
'                       |          |
'         Serial In ---o|2        7|o--- Out0/Serial Out
'                       |          |
'  Out4/In4/Analog4 ---o|3        6|o--- Out1/In1/Analog1
'                       |          |
'       In3/Infrain ---o|4        5|o--- Out2/In2/Analog2/PWM Out/Tune Out
'                       |__________|

' Each PWM output is generated using a technique called "Phase Shifted Counters",
' where the difference between two Modulo "n" counters equals the duty cycle.
' More information can be found at www.dattalo.com/technical/theory/pwm.html.

' In the following example, both counters roll over after 10 counts.  In the 
' beginning, the Rising Edge (RE) counter is cleared and the output is driven
' high.  Also, if the duty cycle is 4, the Falling Edge (FE) counter is then
' initialized to (10-4)=6.  When the RE counter counts from 9 to 10, it is
' "rolled over" back to zero and the output is driven high.  Similarly, the FE
' counter drives the output low when it rolls over.

'   Rising Edge   |---------|---------|---------|---------|---------
'   Counter       01234567890123456789012345678901234567890123456789

'   Falling Edge  ----|---------|---------|---------|---------|-----
'   Counter       67890123456789012345678901234567890123456789012345

'   PWM Output    ----______----______----______----______----______

' n-bit binary counters "automatically" roll over.  For example, an 8-bit binary
' counter ranges from 0 to 255 before resetting to zero.  If we instead want a
' 4-bit wide counter with a duty cycle of 7, we can initialize the FE counter to
' (16-7)=9, then logically "AND" the counter with a mask (2^4-1)=15 each time
' after incrementing as shown below:

'   Rising Edge		|---------------|---------------|---------------
'   Counter		012345678911111101234567891111110123456789111111
'                                 012345          012345          012345

'   Falling Edge	-------|---------------|---------------|--------
'   Counter		911111101234567891111110123456789111111012345678
'                        012345          012345          012345         

'   PWM Output		-------_________-------_________-------_________

' Hence, a 4-bit wide counter would allow for a duty cycle range of:

'   0000	00%  (PWM Always Off)
'   0001	06%
'   0010	13%
'   0011	19%
'   0100	25%
'   0101	31%
'   0110	38%
'   0111	44%
'   1000	50%
'   1001	56%
'   1010	63%
'   1011	69%
'   1100	75%
'   1101	81%
'   1110	94%
'   1111       100%  (PWM Always On)

' ##################
' ##  Directives  ##
' ##################

#picaxe 08m                           ' Set compiler mode for PICAXE 08M
#freq m4                              ' Set programmer download speed to 4MHz

' #######################
' ##  Define Contants  ##
' #######################

' The LED color depth is determined by the number of bits in each of the RGB
' color counters and can range from 2-bits to 8-bits.  Using 2-bits for each
' counter yields 4^3=64 colors.

Symbol num = 2                        ' Number of bits in each RGB color counter
Symbol red =	%00000001             ' Define as 08M Output 0 (Pin7)
Symbol green =	%00000010             ' Define as 08M Output 1 (Pin6)
Symbol blue =   %00000100             ' Define as 08M Output 2 (Pin5)


' ########################
' ##  Define Variables  ##
' ########################

Symbol Rduty = b0                     ' Red counter duty cycle (0<=Rduty<=mask)
Symbol Gduty = b1                     ' Green counter duty cycle (0<=Gduty<=mask)
Symbol Bduty = b2                     ' Blue counter duty cycle (0<=Bduty<=mask)
Symbol maxcount = b3                  ' Maximum count of num-bit wide counter
Symbol mask = b4                      ' Used to reset to zero RE and FE counters
Symbol PWMgen = b5                    ' Outputs 3 PWM streams to drive RGB LED
Symbol counter = b6                   ' Rising Edge counter
Symbol Rcounter = b7                  ' Falling Edge Red counter
Symbol Gcounter = b8                  ' Falling Edge Green counter
Symbol Bcounter = b9                  ' Falling Edge Blue counter
Symbol var = b10                      ' General purpose variable
Symbol temp = b11                     ' General purpose variable

' ######################
' ##  Initialization  ##
' ######################

' The PICAXE is initialized by first tri-stating all output pins and then
' setting each output low prior to enabling.

dirs = %00000000                      ' Tri-state all outputs
pins = %00000000                      ' Set Out0=Out1=Out2=0
dirs = %00000111                      ' Enable Outputs

Rduty = 3                             ' Initialize 0 <= red duty cycle <= mask 
Gduty = 2                             ' Initialize 0 <= green duty cycle <= mask
Bduty = 1                             ' Initialize 0 <= blue duty cycle <= mask

maxcount = 1                          ' Initialize maxcount for calculated 2^num
for var = num to 1 step -1            ' Loop calculate
  maxcount = maxcount * 2             ' Maximum count of num-bit wide counter
next var                              ' Repeat until calculation finished

mask = maxcount - 1                   ' Used to reset RE and FE counters
counter = mask                        ' Initialize RE counter
Rcounter = mask - Rduty               ' Preset FE Red counter to duty cycle 
Gcounter = mask - Gduty               ' Preset FE Green counter to duty cycle
Bcounter = mask - Bduty               ' Preset FE Blue counter to duty cycle
temp = red xor green xor blue         ' Combine all PWM outputs
PWMgen = 0                            ' Initialize all PWM outputs low
pins = PWMgen                         ' Output all PWM streams					'  

setfreq m8                            ' Set program execution speed to 8MHz

' ####################
' ##  Main Program  ##
' ####################

Do																		' Main program execution	
  counter = counter + 1 & mask        ' Increment RE counter modulo maxcount
  if counter = 0 then                 ' Check for RE counter rollover	
    PWMgen = temp : endif             ' If rollover, all outputs high
	
  Rcounter = Rcounter + 1 & mask      ' Increment FE Red counter modulo maxcount
  if Rcounter = 0 then                ' Check for FE Red counter rollover
    if Rduty = mask then              ' Check for 100% Red duty cycle
      Rcounter = Rcounter + 1         ' If 100%, skip around reset to low
    else var = red : endif            ' Prepare to reset red counter to low
  endif
  gosub update                        ' Update status of red PWM stream

  Gcounter = Gcounter + 1 & mask      ' Increment FE Green counter modulo maxcount
  if Gcounter = 0 then                ' Check for FE Green counter rollover
    if Gduty = mask then              ' Check for 100% Green duty cycle
      Gcounter = Gcounter + 1         ' If 100%, skip around reset to low
    else var = green : endif          ' Prepare to reset green counter to low
  endif
  gosub update                        ' Update status of green PWM stream

  Bcounter = Bcounter + 1 & mask      ' Increment FE Blue counter modulo maxcount
  if Bcounter = 0 then                ' Check for FE Blue counter rollover
    if Bduty = mask then              ' Check for 100% Blue duty cycle
      Bcounter = Bcounter + 1         ' If 100%, skip around reset to low
    else var = blue : endif           ' Prepare to reset blue counter to low
  endif
  gosub update                        ' Update status of blue PWM stream
  pins = PWMgen                       ' Output all updated PWM streams 
loop																	' Loop forever


' ###################
' ##  Subroutines  ##
' ###################

update:
  PWMgen = PWMgen xor var             ' Update status of each PWM stream 
  var = 0                             ' Reset re-useable variable
return
 

rWAVE

Member
Thanks for all the comments regarding speeding up my do . . . loop. Unfortunately, it will never go fast enough in PICAXE BASIC for my needs, so I am VERY interested in trying to compile the program and re-program the 08M. Suggestions? :confused:

One way to speed it up is to increase the clock speed using the setfreq command.
Thanks, but way ahead on that! ;)

Not sure how close you are to hitting the code limit on the 08m.
Not close at all . . . the posted program only uses about 148 of the available 256 bytes. :)

I noticed a slight inconsistancy in your format. You are using both "^" and "XOR" in statements. In PICAXE BASIC both mean the same. Is this just a shorthand?
Correct . . . early AM programming gets boring. So instead of the adage "one size fits all", I decided to make it "two sizes fits all". Unfortunately, you will find in my verbose comments references to the "caret" (^) symbol, generally used for exponentiation (referencing a software routine to do just that). :D

Richard
 

Mycroft2152

Senior Member
Hmmm, starting to sound like a commercial project to me. We can always negotiate consulting fees.

It seems that you are sharing only bits and pieces of the project. Your posted program "was initial attempt..."
The program did not have any "^" subroutines that you mentioned in your response.

It's difficult to suggest improvemnets with only part of the big picture.

If this were a one-of hobbyist project, I would initially try the program on one of the X1 chips running at 20mHz. This would be a very cost effecftive way to check out the flicker rate.

Programming a regular PIC will be more expensive, just to buy the tools and hardware needed. Let alone the learning curve will be much longer to create the software, but more cost effective for high volume products.
 
Last edited:

hippy

Technical Support
Staff member
Maybe I'm missing something, but what's the advantage in this implementation of phase shifted counters over a more normal 'set PWM bit if higher than counter' approach ?

Code:
Do
  For duty = MAX_DUTY_MINUS_1 To 0 Step -1
    R_PIN = rDuty > duty
    G_PIN = gDuty > duty
    B_PIN = bDuty > duty
  Next
Loop
True, the above code has more overhead than shown when implemented on a PICAXE because it has to use If-Then-Else to set or clear each bit and For-Next is slow but it's just a few uS per bit test-and-set on a 4MHz PICmicro. I've done six channels of LED dimming that way and including calling a lookup table to linearise brightness to control level. It had 50Hz (20ms) refresh rate and 128 levels of brightness.

I can appreciate that phase shift counters do have an advantage, but I'm not sure that using rising and falling edge counters are the most efficient way to do that. What's wrong with -

- rCurrent = ( rCurrent + rRate ) Mod rMaxDuty
- R_PIN = rCurrent > rDuty

That still has the advantage of being potentially less flickery; instead of 10% brightness being on for 10 periods in every 1000, it can be dynamically adjusted to being on for 1 period in every 100.

In fact, take that principle and one can probably determine a value for N so the LED is on once in every N periods and optimise even further.
 

Mycroft2152

Senior Member
Hippy, you brought up another good point, "a lookup table to linearise brightness to control level". Our eyes have a logarithmic, rather than linear, response to liight. so what looks nice and clean on paper, does reflect the real world.
 

rWAVE

Member
Say . . . What?

¨Hmmm, starting to sound like a commercial project to me. We can always negotiate consulting fees.¨

Sorry Mycroft, you missed that one badly . . . purely hobby.

¨It seems that you are sharing only bits and pieces of the project. Your posted program "was initial attempt..."
The program did not have any "^" subroutines that you mentioned in your response.¨


Sorry again Mycroft, but I posted what you requested in an earlier post (¨Please post the entire program so it can be fully understood and run under the simulator..¨). The posted program is the only current ¨working¨ program I have which demonstrates (to my satisfaction) how the referenced algorithm works. I have other (more recent) versions with additional features that currently do not run error-free in the simulator. Since your post intimated a desire to run the program in the simulator, I felt providing a working program would allow for that. In addition, to assist you in fully understanding the program, I took additional time and care to fully document the posted program so you could follow how it works.

Mycroft, please, PLEASE carefully re-read the program listing as the ¨^¨ referenced in the comments is there as is the software routine (I did not mention subroutine) to do the exponentiation calculation I needed.

¨It's difficult to suggest improvemnets with only part of the big picture.¨

Entirely possible for you Mycroft! :) Others, however, kindly offered constructive suggestions.

<Ctrl><Alt><Del>
Richard
 

rWAVE

Member
Thank You Hippy

The time you took to comment and suggest alternate ways to approach my project is very much appreciated. As I am still exploring and learning PICAXE programming, your recent post has given me plenty to study, as my initial read left me lost.

As a summary, here is what I think I need for my project:

1. PWM generators, capable of at least 800Hz-1000Hz (preferably 2x-3x that frequency)
2. Synchronous streams (all start at the same time, all with the same frequency)
3. User-selectable duty cycle resolution (up to 8-bits each)
4. Scalable software architecture, up to 10 streams

Although phase shifted counters may not be the best, fastest, most elegant or cost-effective approach, they have a few things in their favor:

1. I understand the concept
2. I have implemented in software a working proof-of-concept
3. Points 1-4 above can be easily achieved with them (with a pic, not PICAXE)
4. I get to learn new things as I pursue this hobby project (compiling, programming, etc)
5. I can easily re-purpose the PWM generators for numerous other hobby projects waiting
6. I am ready to move forward and finish the project

THANKS AGAIN for your input!

Richard
 
Top