Slow background PWM on 28X2

greencardigan

Senior Member
Hi,

I have some code which reads some i2c chips, does a few calcs and sends some serial data to a PC. The i2c reads and calcs could take up to 500ms. This runs in a 1 second loop controlled by the timer variable. I'm not using interupts, just waiting until timer increments before looping.

Now I want to be able to output some slow PWM at 1 Hz with the duty able to range from 0 to 100%. The 500ms of i2c reads, calcs and serial out need to keep happening at regular 1 second intervals.

What are my options?

Use timer3 to control the main 1 second loop and timer with interrupts to do the pwm? Can timer3 be used to give me 1 second loops?

Using a second picaxe to do the 1 Hz PWM with the main picaxe sending the required duty value by serial?

Any better options?
 

hippy

Ex-Staff (retired)
If you want 1Hz with 100% duty, to 1% resolution, you're looking at timing to 10ms resolution for that PWM.

Probably the easiest way is to dedicate another PICAXE to PWM generation. The 18M2 could be ideal with it's one-byte high-speed serial receive so it can bit-bang the PWM and check if any new duty has been received.
 

greencardigan

Senior Member
I thought that might be the case.

Is the timer with interrupts a possibility though? Set the preload based on the desired duty.
 

hippy

Ex-Staff (retired)
Using SETTIMER I think you should be able to generate interrupts every 10ms, and you can sync activation of the main loop to every 100 of those.
 

womai

Senior Member
You could use the timer interrupt code I posted here:

http://www.picaxeforum.co.uk/showthread.php?t=8541

In this case set it up for 100 events per second. In the interrupt routine increment a counter. Start out with the output high. When the counter equals the duty cycle (e.g. 35 for 35%) then set the output low. When the counter reaches 100 then reset it to zero and set the output to high. The main program can easily modify the duty cycle (it's just a variable).
 

greencardigan

Senior Member
You could use the timer interrupt code I posted here:

http://www.picaxeforum.co.uk/showthread.php?t=8541

In this case set it up for 100 events per second. In the interrupt routine increment a counter. Start out with the output high. When the counter equals the duty cycle (e.g. 35 for 35%) then set the output low. When the counter reaches 100 then reset it to zero and set the output to high. The main program can easily modify the duty cycle (it's just a variable).
I've been thinking about this and don't know if it will be useful in my case.

I will have serout (to LCD) commands which will stop some of the interrupts from occurring?

Also I have a 70ms pause command used to wait for an i2c chip to do ADC. With interrupts every 10ms the pause will be cut short with the chip being read before the ADC is finished. Actually, maybe I can just do small pauses in a loop to overcome this.

Hippy said:
The 18M2 could be ideal with it's one-byte high-speed serial receive
Can you point me to some more info re this. I couldn't see anything in the manuals.
 

womai

Senior Member
For the srout, yes, this will introduce some jitter to the interrupt timing. You'll have to see if it is acceptable. One workaround could be to measure/calculate how much time the serout takes. If it is e.g. 20ms then add 2 to the counter right after you did the serout.

Second, yes, breaking up the 70ms pause into smaller chunks (e.g. 7-8 times 10ms) will minimize the effect of the interrupt on your pause timing.
 

womai

Senior Member
For the serout, this will indeed introduce some jitter to the interrupt timing. You'll have to see if it is acceptable. One workaround could be to measure/calculate how much time the serout takes. If it is e.g. 20ms then add 2 to the counter right after you did the serout.

Another option would be to break up the serout into several commands which each send e.g. a single byte - each of them will then take a much shorter time and the interrupt will then experience much less jitter.

Finally, yes, breaking up the 70ms pause into smaller chunks (e.g. 7-8 times 10ms) will minimize the effect of the interrupt on your pause timing.
 

hippy

Ex-Staff (retired)
The SEROUT for LCD should only delay the interrupt, which could introduce some jitter into the PWM but you can mitigate that my splitting into multiple SEROUT commands.

Yes, I'd use multiple shorter pauses to avoid being cut short. You can also add a PAUSE in the interrupt to make sure the delay is long enough in total even when cut short, you can control that by a flag variable so you only invoke that PAUSE if in the 70ms pause section.

Have a look at the HSERIN example in Manual 2. For the 18M2 it's not full background receive into the scratchpad ( as there isn't scratchpad ), but you can still do background receive ...

Code:
#Picaxe 18M2

Symbol PWM_OUT  = C.0

Symbol rxd      = w0
Symbol ramp     = b2
Symbol duty     = b3

HSerSetup B2400_4, %000

Do
  rxd = $FFFF
  HSerIn rxd
  If rxd <> $FFFF Then
    duty = rxd
  End If 
  ramp = ramp // 101 + 1
  If ramp <= duty Then
    High PWM_OUT
  Else
    Low PWM_OUT
  End If 
  Pause 10
Loop
Then all you need to do to control PWM, for a very slow change in this case ...

Code:
For b0 = 0 To 100
  SerOut TX_PIN, T2400, ( b0 )
  Pause 500
Next
 
Last edited:

greencardigan

Senior Member
Hippy said:
The SEROUT for LCD should only delay the interrupt, which could introduce some jitter into the PWM but you can mitigate that my splitting into multiple SEROUT commands.
So will the interrupt be delayed or missed?

If they are missed, I don't understand how splitting the serout into bits will help. Wont it still take the same total time to send in parts.
 

Janne

Senior Member
If you have enough pins to spare, you could utilize a parallel data transfer to the LCD, the way it's supposed to be connected without the serial backpack. That would reduce the chances of the SEROUTs causing jitter.

How much jitter is acceptable, I think that is the deciding factor here.
 

greencardigan

Senior Member
If you have enough pins to spare, you could utilize a parallel data transfer to the LCD, the way it's supposed to be connected without the serial backpack. That would reduce the chances of the SEROUTs causing jitter.

How much jitter is acceptable, I think that is the deciding factor here.
I'm not to worried about jitter in the PWM if it's minimal. As long as I can keep a reasonably regular and accurate 1 second loop happening for the temp reads.

Parallel LCD connection is a possibility. Also I2C. I have an AXE033 serial/I2C LCD in the mail.
 

BeanieBots

Moderator
If you want regular/accurate loop timing whilst using serial, then you should consider the use of timer3.
Timer is effected by serial comms. Timer3 is only effected when the total duration of a single serout line exceeds the timer3 tick value.

For critical loops that need to update displays (eg PID control) I always use timer3 which is polled within the loop. Using timer will accumulate errors when serial is used.
 

hippy

Ex-Staff (retired)
I'm not to worried about jitter in the PWM if it's minimal. As long as I can keep a reasonably regular and accurate 1 second loop happening for the temp reads.
The problem here is that we don't have your definition of "minimal" or "accurate" nor have we seen the code you currently have.

If you have an every-10ms interrupt, with SEROUT at 2400 baud, the interrupt could be delayed by at least 4ms per byte sent in any SEROUT. Is that acceptable ? Only you can say, and there are tricks to reduce jitter on PWM; if PWM duty < 50% then only execute SEROUT after 5ms, if > 50% execute in the first 5ms, or similar.
 

greencardigan

Senior Member
That sort of delay may be acceptable. I won't really know until I get a chance to try it out. I dont know yet how many bytes of serial I'll be sending to the LCD. The pwm will controlling a heating element via a SSR.

PWM is likely to be above 50% most of the time.

How long does it take per byte send to the AXE003 using I2C?
 
Last edited:

greencardigan

Senior Member
Lock Up?!?

I have added Womai's interrupt code and have run a few tests (no serouts in my code yet). I am having some strange lock up type problems again!! :mad:

It seems to work fine with 20, 50 and 80 interrupts per second. When I try 100 interrupts per second the code seems to lock up after a short time (different length of time after each reset but usually within a few minutes). :confused: The led on c.5 stops flashing.

Here's the code I added. I have attached the full code also if that is helpful.

Code:
symbol EVENTS_PER_SEC = 100
symbol duty = EVENTS_PER_SEC / 2

symbol DUMMY_VAL = 65536 - t1s_16
symbol TIMER_PRELOAD_VAL_DIFF = DUMMY_VAL / EVENTS_PER_SEC
symbol TIMER_PRELOAD_VAL = 65536 - TIMER_PRELOAD_VAL_DIFF

symbol t = w8 'seconds counter
symbol t_int = w7 'interrupt counter

setfreq m16

settimer TIMER_PRELOAD_VAL ' set time preload value
gosub timer_setup


mainloop:
'hi2c, hserout, calcs here
goto mainloop

interrupt:
    inc t_int  
    if t_int >= duty then
    	low c.5
    endif  
    if t_int >= EVENTS_PER_SEC then
    	t_int = 0
    	high c.5
    	inc t
    endif   
    gosub timer_setup    
return

timer_setup:
    timer = 0xffff ' generate interrupt at next overflow
    toflag = 0 ' clear timer overflow flag
    setintflags %10000000,%10000000 ' interrupt on timer overflow
return
 

Attachments

greencardigan

Senior Member
Does hserout conflict with the timers or interrupts?

I found I can skip the main hserout subroutine in my code to fix the lockup issue in my previous post.
 

greencardigan

Senior Member
An update for anyone still watching this thread.

Does hserout conflict with the timers or interrupts?

I found I can skip the main hserout subroutine in my code to fix the lockup issue in my previous post.
I have split the longer hserouts up into smaller chunks and it has works ok now.

I still don't understand why the hardware serial outs were a problem??
 

womai

Senior Member
The hardware serout places the data into a 1-byte buffer from where they get sent out automatically (driven by dedicated logic which does not interfere with your program). If the previous buffer hasn't been completely sent out then the send routine has to wait until the buffer becomes available again.

So when you are sending out only one byte at a time (and have sufficient time between subsequent bytes) then the program experiences virtually no delay when trying to send. But if you send out a longer string of bytes with no break inbetween then the first byte gets put in the buffer right away but following ones will have to wait. Since it's only a single hserout command it means the program will spend a while executing it before it goes back and checks for the interrupt flag (and executes the interrupt if ncessary). But if you send the data in small chunks (i.e. using several hserout's instead of just one) then the program gets the occasion to check for interrupts more frequently.
 
Top