can we have more than 2 PWM ports on BX24P?

Started by Yuli January 28, 2010
Hi BX24 gurus,

The BX24 example code shows that we can configure Pin26 and Pin27 as PWM signal output ports, is it possible to have one more? If so, how can we configure it? Thanks


> ... is it possible to have one more?

Yes, an 8-bit PWM on pin 25 is available, too. See the message immediately before yours:


Thanks Tom.
There are two more questions related to the timer1 and timer2:
1. If we use timer1 and timer2 together, we can have maximum 3 PWMs
working together. Are the timers considered fully occupied now? If I
need to generate one additional square-wave using Output-Compare, I need
to use another BX24 right? (The frequency of the square wave is 600Hz,
so I can't use delay() because the resolution of it is about 2ms).
2. I have another requirement which is to generate 3 100Hz square-waves
on a BX24, each with 1/6 period's phase difference from another, how
shall I do it? I plan to make a square-wave output of 600Hz (using one
of your earlier code as shown below) and connect it internally to an
input Pin, say Pin1. Then each edge in Pin1 will change the status of
Pin2, Pin3, Pin4 consecutively. The status of the Pin1-Pin4 will
sequentially be: 1100, 0110, 1111, 0011, 1001, 0000. I'm just concerned
that if the program execution time for each step (about 2 lines per
step) is too large compared to the period of square-wave, the precision
will be lost.
Sub Put20HzOnPin25() Const T2mode As Byte = bx0001_1111 'flip 25,
clear, 7200Hz Register.TCCR2 = 0 'stop Timer2 Register.TCNT2 = 0
Register.OCR2 = 180 '20Hz=(7372800/1024/20/2)0 Register.TCCR2 T2modeEnd Sub

> ... I need to generate one additional square-wave [] 600Hz...

Yes, if Timer1 is in use generating PWM you cannot normally use
OuputCapture nor any other function that uses Timer1 without
interaction. However, if the processor need do no other work beyond
making these four signals, since the PWMs are being generated entirely
in hardware and need no code once started, you can dedicate the code to
generating the 600Hz output. Using the RTC tick won't, as you say,
provide very good resolution; you can use it to produce 600Hz average,
but it would be very irregular.

I suggest that you simply use a delaying counter loop, like

sub DelayAWhile(HowLongCount as long)
do until not cbool(HowLongCount)
HowLongCount = HowLongCount - 1
end sub

or something similar. Then just toggle a pin in an endless loop with
the delay between them, like

sub Make600Hz()
putpin(pin600Hz, bxOutputHigh)
call DelayAWhile(HalfCycle600HzPeriodCount)
putpin(pin600Hz, bxOutputLow)
call DelayAWhile(HalfCycle600HzPeriodCount)
end sub

This will still produce some irregularity due to the processor stealing
time for maintenance each RTC tick, so you will probably see the 600Hz
edges jitter. You can eliminate that jitter by preventing multitasking
by disabling Timer0, which stops the system tick. See . Be careful to
heed the warning to place a pause at startup before you disable Timer0,
though, or you might not be able to regain control of the processor; you
must also do a physical Reset (drop pin 23 or power-cycle), since the
ATN pin won't be monitored.

On generating three-phase 100Hz, outputting a state list as you suggest
should be fine, each step triggered by the code above. To ensure that
all pins change state simultaneously, after you've set the pin direction
register you can output a byte to the port, like

register.DDRC = bx1111_0000 '4 pins as outputs
register.PortC = StateList(n)

See for driving
a 3-phase H-bridge for an implementation. Of course, you can also use
an external shift register to clock a single output bit through six
steps, each delayed by one-sixth cycle.


Don Kinser points out that my ATN pin monitoring comment is not correct for current BX-24 machines, on which ATN just causes a hardware reset.

Still, I believe you should place a Delay() or Sleep() of a second or so at the start of your code, before you disable Timer0 to make sure the IDE can get control.

Hi Tom,

1. My Pin25-27 works fine for outputing PWMs, but I can't get them work for Output Compare. I tried the code you've suggested in an earlier post. There's no fluctuation on the oscilloscope and the voltage reading is kept at 3.2V, below is the code I modified based on yours:

Option Explicit
Public Const GreenLEDO As Byte = 25

Public Sub Main()

Call Put20HzOnPin25()

End Sub

Sub Put20HzOnPin25()
Const T2mode As Byte = bx0001_1111 'flip 25, clear, 7200Hz
Register.TCCR2 = 0 'stop Timer2
Register.TCNT2 = 0
Register.OCR2 = 180 '20Hz>>>180, the relationship is linear
Register.TCCR2 = T2mode
End Sub

2. For the delaying counter loop idea you've suggested, I have concern over the delaying time consistency & the system overhead. I've tried similar thing yesterday, but instead of using counter loop as a more precise half-period delay, I used pulseout() to pulse a fixed duration to a dummy pin. Because the pulsing will halt the task for exactly the duration of pulsewidth, I assumed it to be precise. I did manage to generate a nice squarewave. However, my task needs me to vary the frequency discretely. I used 0.5ms (the half-period of 1000Hz) as the base, and I looped the pulseout(5ms) for 10 times trying to get 100Hz, and 100 times trying to get 10Hz. The result is not accurate, and I guess the reason lies with the system overhead (to my understanding, it's the time that spend on executing the commands like For Next etc etc). Is it really so?

3. My BX24 will need to carry out 3 other tasks (including one PID control, 3 ADC reading and some other Digital I/O work) while pausing out the 3 PWMs. Will it be too demanding for the controller?



> ... I can't get them work for Output Compare. I tried the code you've
suggested in an earlier post.

I assume you mean OutputCapture. The code you cite doesn't use
OutputCapture, and OutputCapture only generates a signal on pin 27; it
uses Timer1. The code you cited uses Timer2 to produce a signal on pin
25; there is no apparent output because the pin needs to be initialized
as an output, either high or low. Add
call PutPin(25, 1)
before you call Put20HzOnPin25 and it should work.

> ... I have concern over the delaying time consistency & the system

Any code execution will take some time, some of it unproductive. As I
recall, the minimum execution time for a line of BX-24 code is ~32uS.
The following, for example, executes in ~32uS:
call PutPin(25, 1) ' LED off (~32uS)

Any conditional loop will need some additional time to test and loop or
exit, so even a simple loop like
call PutPin(25, 1) ' LED off (~32uS)
loop until True
uses ~53uS. The loop test alone takes ~21uS. A scope will also show
~10uS jitter, due to the system tick.

This means that - unless the signal is generated entirely in hardware
like the processor's PWM facility - some irregularity in a
processor-generated signal is unavoidable without care; adding dummy
instructions to an execution path can minimize jitter, for example.
Still, the system tick will cause jitter unless it is sacrificed.
Disabling it will produce a clean jitter-free signal, but at the cost of
multitasking and RTC functions. This code will loop at ~96uS, jitter-free:
sub Main()
call sleep(1.0)
register.TCCR0 = 0 ' system tick off
call PutPin(25,1) ' led off '~32uS
call PutPin(25,0) ' led off '~32uS
loop ' loop ~32uS
end sub

All this means that - if you need a clean, jitter-free signal, you
should generate it in hardware, either internal or external. If you use
code, especially multitasking code, to produce the signal it will be
irregular. You might be able to use a simpler processor dedicated to
the function, though, like a PIC.

> ... My BX24 will need to carry out 3 other tasks (including one PID
control, 3 ADC reading and some other Digital I/O work) while pausing
out the 3 PWMs. Will it be too demanding for the controller?

Since you already have difficulty with what you see from the machine, I
suspect yes.
> ... 3. My BX24 will need to carry out 3 other tasks (including one
PID control, 3 ADC reading and some other Digital I/O work) while
pausing out the 3 PWMs. Will it be too demanding for the controller?

On reconsideration - if you do not also need to produce the 100Hz three
phase you mentioned earlier, just the three PWMs - you should be able
to do what you list above on a BX-24. If you mix functions like PWM,
PulseOut and OutputCapture, though, you'll run into trouble since they
all use the same hardware timer and usually cannot coexist.

Hi Tom,

Thanks so much, after initialized the pin25 as Output pin for OutputCapture and it works very well now. :-) However I can't get the pin26 and pin27 work as OutputCapture, though I configured and initialized them in the similar way. Could you help me look at the new codes?


Option Explicit

Public Const OpCmp1 As Byte = 25
Public Const OpCmp2 As Byte = 26
Public Const OpCmp3 As Byte = 27

Public Const T2mode As Byte = bx0001_1111 'flip 25, clear, 7200Hz
Public Const T1Amode As Byte = bx0101_0000
Public Const T1Bmode As Byte = bx0000_1111

Public Sub Main()

Call PutPin(Enable1,1)
Call PutPin(Enable2,0)

Call InitializeSqrWv()
Call SqrWvOnPin25(45)
Call SqrWvOnPin26(180)
Call SqrWvOnPin27(180)

End sub
Sub InitializeSqrWv()
' Register.TCCR2 = 0 'stop Timer2 (however I noticed that these initializations are not affecting the results, so I deactivated them)
' Register.TCNT2 = 0
' Register.OCR2 = 0
Register.TCCR2 = T2mode

' Register.TCCR1A = 0 'stop Timer1 and initialize the Timer1 value
' Register.TCCR1B = 0
' Register.TCNT1H = 0
' Register.TCNT1L = 0
' Register.OCR1AH = 0
' Register.OCR1BH = 0
' Register.OCR1AL = 0
' Register.OCR1BL = 0
Register.TCCR1A = T1Amode
Register.TCCR1B = T1Bmode

Call PutPin(OpCmp1,0) ' either output 0 or 1 will set the pin as output
Call PutPin(OpCmp2,0)
Call PutPin(OpCmp3,0)

End Sub

Sub SqrWvOnPin25(ByVal Freq As Byte)
Register.OCR2 = Freq '20Hz = Freq is 180, the relationship is inversely linear
End Sub

Sub SqrWvOnPin26(ByVal Freq As Byte)
Register.OCR1AL = Freq
End Sub

Sub SqrWvOnPin27(ByVal Freq As Byte)
Register.OCR1BL = Freq
End Sub


Here is a three-PWM demo that drives a three-color LED. Perhaps you can
find your errors by comparing your code to this code.

' Drive three-color LED
' GTBecker 2007-06-17

const pinPWMRed as byte = 25 ' Timer2 PWM
const pinPWMGreen as byte = 26 ' Timer1A PWM
const pinPWMBlue as byte = 27 ' Timer1B PWM

const pinControlRed as byte = 13 ' analog control inputs
const pinControlGreen as byte = 14
const pinControlBlue as byte = 15

Public Sub InitializePWMs()
Call PutPin(pinPWMRed, bxOutputHigh) ' init PWM pins
Call PutPin(pinPWMGreen, bxOutputHigh)
Call PutPin(pinPWMBlue, bxOutputHigh)
register.TCCR2 = bx0111_0001 ' Timer2 8-bit PWM, Clk/1024
register.OCR2 = 0 ' Clear duty cycle
Register.TCCR1A = bx1111_0001 ' Timer1 8-bit PWM
Register.TCCR1B = bx0000_0001 ' Clk/1024
Register.OCR1AH = 0 ' Clear duty cycles
Register.OCR1AL = 0
Register.OCR1BH = 0
Register.OCR1BL = 0
End Sub

Public Sub SetPWMs(ByVal PWMA As Byte, ByVal PWMB As byte, ByVal PWMC As
'sets 8-bit PWMs
Register.OCR2 = PWMA
Register.OCR1AH = 0
Register.OCR1AL = PWMB
Register.OCR1BH = 0
Register.OCR1BL = PWMC
End Sub

Sub Main()
dim r as single, g as single, b as single, x as single

call sleep(0.5)
call InitializePWMs
do 'endless

r = csng(GetADC(pinControlRed)) / 4.0 ' ADC is 10-bit, PWM is
g = csng(GetADC(pinControlGreen)) / 4.0
b = csng(GetADC(pinControlBlue)) / 4.0

'normalize to brightest color
x = r
if g > x then
x = g
end if
if b > x then
x = b
end if
x = x / 255.0 'normalize to 255

call SetPWMs(cbyte(g / x), cbyte(b / x), cbyte(r / x))
debug.print fmt(r / x, 0) &" "& fmt(g / x, 0) &" "& fmt(b / x, 0)

loop 'endless
end sub