PWM - period change problems

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

PWM - period change problems

3,293 Views
mizer101
Contributor I
 
Would someone please look at my code and tell me why I am having trouble with the interrupts.
I am trying to vary the duty cycle of a pwm signal from an analog input.  Duty cycle is 20% at 0 V and 80% at full scale input.
 
Thanks,
 
Steve
 
 
 
 
 
 
       XDEF Entry, main, irq_TIMCH0, irq_TIMOvr, irq_isr
;*******************************************************************************
; Registers definition
;*******************************************************************************

   Include 'derivative.inc'  ; Include derivative-specific definitions
CURRPOS EQU $00FE                  ; Current Valve Position stored in RAM
         
;*******************************************************************************
DEFAULT_ROM             SECTION
;*******************************************************************************
; Peripheral Initialization
;*******************************************************************************
init:
       bset 0, CONFIG1                 ; Disables COP
       mov #$49, CONFIG2               ; Enables external oscillator and IRQ/RST pin  
       mov #$02, OSCSTAT               ; Enables external clock generator
       mov #$FF, DDRB                  ; Configures port B as output          
       mov #$21, ADSCR                 ; Enables ADC channel 1
       mov #$60, ADICLK                ; Bus clock / 8
       rts
       
;*****************************************************************************
;   PWM INITIALIZATION
;*****************************************************************************
;**********************************************************************************************
;      PWM INITIALIZATION
;**********************************************************************************************
pwm_init:
       BSET    TSC_TSTOP,TSC           ; TSTOP - Stop the timer
       BSET    TSC_TRST,TSC            ; TRST - Reset the count
               
       MOV     #$68,   TMODH           ; Set the PWM period (150Hz) - high byte
       MOV     #$2A,   TMODL           ;                            - low byte
       
       MOV     #$14,   TCH0H           ; Set initial output to 20% duty cycle (pulse width)
       MOV     #$D5,   TCH0L
       
                                       ; 80% = 5355h
                                       ; 20% = 14D5h
                                       ; 16MHz / 4 = 4MHz e-clock
                                       ; Oscillator is 16MHz. 16M/4=4Mz e-clock
                                       ; 4M/150 = 26667(682Ah) = period
                                       ; TMODH & TMODL = 682A
                                       ; 26667 * .8 = 21333 (5355h)
                                       ; 26667 * .2 = 5333  (14D5h)
                                       ; 21333-5333 = 16K   (3E80h)
                                       ; 16K/128 = 125      (7Dh)   conversion constant
                                     
       
       BCLR    TSC0_MS0B,TSC0                                                                       
       BSET    TSC0_MS0A,TSC0          ; Unbuffered output compare       
       
       BSET    TSC0_TOV0,TSC0
       
       BSET    TSC0_ELS0B,TSC0         ; ELS0B - Set the output polatity to 1
       BSET    TSC0_ELS0A,TSC0         ; ELS0A - Clear the output on compare
       
       BCLR    TSC_TSTOP,TSC           ; Restart the TIM
       
       BSET    PTB_PTB4,PTB            ; debug - set #4 led
       
       rts
;*******************************************************************************
;
;       MAIN CODE
;
;*******************************************************************************
Entry:
main:
       rsp                             ; SP <- 0xFF
       cli                             ; Enables global interrupts
       bsr init                        ; Peripheral initialization
       bsr pwm_init                    ; Initialize the PWM @ TCH0 unbuffered
       MOV #$00,CURRPOS                ; Initialize the current valve position to 0
       
main_loop:
       
       LDA ADR                         ; load ACC with the AD value
       CMP CURRPOS                     ; Compare the input with the previous positon
       BLO SHORTER                     ; Decrease the PWM period
       BHI LONGER                      ; Increase the PWM period
       BCLR    TSC0_CH0IE,TSC0         ; Disable the interrupt
       BCLR    TSC_TOIE,TSC            ; Disable the interrupt
       BRA main_loop                   ; Loop forever
       
SHORTER BSET    TSC0_CH0IE,TSC0         ; Enable CH0 output compare interrupt
       WAIT
       BRA main_loop

LONGER  BSET    TSC_TOIE,TSC            ; Enable TIM overflow interrupts
       WAIT
       BRA main_loop
 
;********************************************************************************
;
;       PWM PERIOD CHANGE INTERRUPTS
;
;********************************************************************************
irq_TIMCH0:                             ; TIM channel 0 output compare interrupt - shorter period
       
       JSR NEW_PERIOD
       BCLR    PTB_PTB7,PTB            ; debug - clear #7 led
       BSET    PTB_PTB0,PTB            ; debug - set   #0 led
       RTI
       
       
irq_TIMOvr:                             ; TIM overflow interrupt - longer period
       JSR NEW_PERIOD
       BCLR    PTB_PTB0,PTB            ; debug - clear #0 led
       BSET    PTB_PTB7,PTB            ; debug - set   #7 led
       RTI               
 

NEW_PERIOD:
       LDA ADR
       CLRH
       STA CURRPOS                     ; Store current valve postion in memory
       LDX #$7D                        ; V/DIV constant (125d)
       MUL                             ; Multiplys A and X result -> A-low byte, X-high byte
       ADD #$D5                        ; Add to get low byte (213d)
       STA TCH0L                       ; Store low byte - PWM period
       TXA                             ; Get the high byte from the MUL instruction
       ADC #$14                        ; Add from memory and carry bit to get the high byte(20d)
       STA TCH0H                       ; Store high byte - PWM period
       RTS

 
Labels (1)
0 Kudos
8 Replies

709 Views
bigmac
Specialist III
Hello Steve,
 
There are a couple of problems with your code, that would prevent correct PWM operation:
  1. Your ISR code does not clear the relevant interrupt flag - so you would continuously re-enter the ISR.
  2. Since you seem to require that the value of CURPOS range between 0 and 255, it would seem that your multiplier value should be 62.5 rather than 125.  Otherwise, the value written to TCH0 register may exceed the TMOD value, and the output compare would never occur, and you would see a continuously high output (100 percent duty cycle).  After doing the multiplication, you would need to divide the result by 2, before adding the offset value.  Simply insert the instructions LSRX followed by RORA into your code.
I also have some other suggestions, to improve and simplify your code, that would not have prevented PWM operation:
  1. I would tend to update the PWM value within the output compare ISR only.  For instances where the new value is greater than the previous value, a second output compare may occur in the same timer cycle, but the PWM output will already be zero at this point, so will not affect the PWM waveform.  The new pulse width would occur on the next timer cycle.  This will simplify the code.
  2. When you set the TSC0 register value during initialisation, it is a good idea to write all bits simultaneously, rather than a sequence of individual bits.  You can use the MOV instruction.  Also, read the register before writing the required value, to clear the flag, just in case it is set.  Probably also a good idea for the TSC register initialisation.
  3. In the interest of keeping the ISRs as short as possible, it should be possible to calculate the new PWM value within the main loop, and simply load the new value within the ISR.
  4. Another minor point to be aware of is that "toggle on overflow" will occur when the counter reaches the TMOD value, one timer clock period earlier than might be assumed.  This would mean that the PWM pulse width will be one timer clock period greater than the PWM value would indicate.  This error is probably not significant for your present project.

Regards,
Mac

 

Message Edited by bigmac on 2006-11-1512:42 PM

0 Kudos

709 Views
mizer101
Contributor I
Thanks Mac.
I am going to look through your recommendations.
0 Kudos

709 Views
mizer101
Contributor I
Got it working this morning!!!
 
Fixed the isr interrupt flag bit problem.
Biggest thing that I found that my interrupt was taking too much time.
PWM calculation is now done in main routine and it works fine.
 
Mac,
62.5 is the correct multiplier, that is where I started.  I do not rember why I had changed it 125.  But dividing my calculation by 2 keeps the Duty Cycle between 80 and 50%.  Without dividing by 2 it works fine.
 
Watching at a scope of the PWM output signal not every pulse width change is seamless.  I am going to change my code over to a buffered signal instead.  I played with it first and it seemed to work better with pulse width changes.
 
Thanks for your help.
 
Steve
0 Kudos

709 Views
bigmac
Specialist III
Hello Steve,
 
What do you acutally mean when you say the pulse width change is not seamless?  With duty cycle limits of 20% and 80%, and a PWM frequency of 150 Hz, there should be no timing difficulties with unbuffered PWM, and there shouldn't be interrupt latency issues, provided no other interrupts can cause significant delay to the output compare interrupt.  When the pulse width remains constant, do you observe any irregularities - you shouldn't, because timer interrupts would be disabled?
 
Since you are using an ADC channel reading to set the pulse width value,  I wonder if you are seeing the effect of noise fluctuations.  I am not sure how the ADC has been set up.  You might consider taking the average of a number of readings to average out short term fluctuations.
 
Since there is little point in updating the PWM value more than once per cycle, perhaps you might pace this process with the timer overflow flag becoming set (whilst not necessarily causing an interrupt).  I am not clear why you have included the WAIT instructions after enabling the timer interrupt - what event actually causes wake-up?
 
Regards,
Mac
 
0 Kudos

709 Views
Geezer
Contributor I
Hiya Mac & Steve--

Any old interrupt brings the CPU out of the WAIT instrcution, and the ISR will return to the instruction which follows it. The primary use is in systems where every ounce of amperage is precious (batteries, solar), since power consumption goes down a tad. Also you KNOW EXACTLY where the CPU will be when an INT occurs and can breakpoint with precision etc, though with careful design this shouldn't matter so much. Is is better than a mindless loop waiting on/for a flag? Depends. Your choice.

Al
0 Kudos

709 Views
mizer101
Contributor I
One other thing.....
I put my varibles in the same location that the stack is in.  That didn't help things either.
0 Kudos

709 Views
bigmac
Specialist III
Hello,


mizer101 wrote:
One other thing.....
I put my varibles in the same location that the stack is in.  That didn't help things either.


It may well explain the irregularities you are observing.
 
The RSP instruction in your code places the stack at a "reasonable" location - it could go to a higher address if the available RAM in your particular device permits this.
 
For assembly language programming, I normally ORG to the start of RAM, and allocate variables using DS pseudo-ops.  This allows maximum gap between the top of variables and the top of the stack.
 
Regards,
Mac
 
0 Kudos

709 Views
Geezer
Contributor I
Congrats.

But for greater fun initialize your stack pointer even lower, say in the middle of the function registers. :smileyhappy: This too causes many happy returns.

I wish you ever more speedy soultions.
Al
0 Kudos