- 
	
	
	
		
Low freq PWM problem
	
	
		Hi All,
I'm kind of stuck at the moment.
I need a pwm signal with a low frequency. Darrel has made the awesome slow pwm module but i can't use because i need timer1 to do a continues pulse count. So i'm trying to code my own, but i can't seem to figure it out.
I'm using timer0 to to make the frequency, with Darrels DT_INTS-14. This works perfect. Preloading timer0 with 190 give about 118Hz where i need 120Hz so thats ok. On every interrupt the PORTC.0 pin is made high. 
I'm using timer2 to make the dutycycle. I thought if i have a frequency of 120Hz the period is about 8333uS. If i need 256 dutycycle steps i need a interrupt at 8333uS/256 steps = 32,6uS. Timer2 is setup (presc. 1:1, postsc. 1:1, pr2=64) to generate an interrupt every 32,5uS. 
If i need a 50% dutycycle, simply count 128x timer2 interrupts and that make the output low. Could be quite simple I thought, but i was wrong. On my scope i see a nice 117Hz stable signal with a dutycycle of 1 or so. If i alter the dutycycle var i sometimes see a dutycycle that ramps down at every cycle ? 
Here's my code :
	Code:
	
'==== Set fuses =========================================
'Fuse are set in .inc file
'==== Set includes ======================================
INCLUDE "DT_INTS-14.bas"                                'DTints Interrupt System
include "ReEnterPBP.bas"                                'Enable PBP int reenter
'==== Dec's for OSC =====================================
DEFINE OSC 8                                            'Int OSC @ 8 MHz, for PBP
OSCCON = %01110001                                      'Int OSC @ 8 MHz
'==== Set defines =======================================
OPTION_REG  = %00000111                                 'Set TMR0 precsaler to 1:256, source Fosc/4
WPU         = %00000000                                 'Weak pull-ups disabled
INTCON      = %11100000                                 'Global + Perhip. INT enabled, TMR0 int enabled
PIE1        = %00000010                                 'TMR2 to PR2 match INT enabled
PIE2        = %00000000                                 'Set TMR1 overflow interrupt
PIR1        = %00000000                                 
PIR2        = %00000000                                 
PCON        = %00000000
IOC         = %00000000                                 'Interrupt On Change disabled
PCON        = %00000000                                 'Ultra lowpower + BOR disabled
ANSEL       = %00000000                                 'Select analog inputs: none
ANSELH      = %00000000                                 'Select analog inputs: none
ADCON0      = %00000000                                 'A/D Module is OFF
ADCON1      = %00000000                                 'A/D control register
CM1CON0     = %00000000                                 'Comparator1 Module is OFF
CM2CON0     = %00000000                                 'Comparator2 Module is OFF
VRCON       = %00000000                                 'Voltage ref. disabled
TRISA       = %00000000                                 'Set Input/Output (0 to 5)
PORTA       = %00000000                                 'Ports High/Low (0 to 5)
TRISB       = %00000000                                 'Set Input/Output (4 to 7)
PORTB       = %00000000                                 'Ports High/Low (4 to 7)
TRISC       = %00000000                                 'Set Input/Output (0 to 7)
PORTC       = %00000000                                 'Ports High/Low (0 to 7)
T2CON       = %00000100                                 'Timer2 is On,                                 
T1CON       = %00000000                                 'Timer1 is off
CCP1CON     = %00000000                                 'CCP engine is off
'==== Set Constants =====================================
'none
'==== Setup DTints interrupt handler ====================
ASM
INT_LIST  macro                                         ;Setup IntSource
        INT_Handler    TMR0_INT,  _intTMR0,  PBP,  yes  ;Enable TMR0 interrupts
        INT_Handler    TMR2_INT,  _intTMR2,  PBP,  yes  ;Enable TMR0 interrupts
    endm
    INT_CREATE                                          ;Create interrupt processor
ENDASM
'==== Dec's for pin aliasing INPUT'S ====================
'none  
'==== Dec's for pin aliasing OUTPUT'S ===================
gi1 var PORTC.0                                         'Injector 1 output pin
                                                
'==== Dec's for variables ===============================
dutycycle var byte                                      'Holds the needed dutycycle
dutycount var byte                                      'Counter for dutycycle
'==== Initialise variables ==============================
gistate   = 0
dutycycle = 0
dutycount = 0 
'==== Wait for hardware settle ==========================
pause 500
'==== Enable interrupt processing =======================
TMR0 = 190                                              'Preload Timer0 for interrupt @ 120Hz
PR2 = 64                                                'Preload PR2 for dutycycle interrupt
dutycycle = 128                                         'Set duty cycle to 50%
@   INT_ENABLE   TMR0_INT                               ;Activate TMR0 INT
@   INT_ENABLE   TMR2_INT                               ;Activate TMR2 INT
'==== Main program loop =================================
main:
@   nop                                                 ;Do nothing
goto main
          
'==== The End ===========================================
theend:                                                 ' Endless loop
goto theend                                             ' Endless loop
'Routines and Handlers
'==== TMR0 Interrupt handler ============================
intTMR0:                                                'TMR0 INTERRUPT HANDLER
TMR0 = 190                                          'Reload TMR0
gi1 = 1                                                 'Activate injector 2
@ INT_RETURN                                            ;Jump back to previous process
'==== TMR2 Interrupt handler ============================
intTMR2:                                                'TMR0 INTERRUPT HANDLER
PR2 = 64                                                'Reload PR2
if dutycount = dutycycle then                           'If dutycycle is met,
    gi1 = 0                                             'Deactivate injector 1
    dutycount = 0                                       'Reset the dutycounter
else
    dutycount = dutycount + 1                           'Add 1 to dutycounter
endif
@ INT_RETURN                                            ;Jump back to previous process
 Anyone have an idea ?
Best regards, UB
	 
 - 
	
	
	
	
		I just had another idea. I need a base frequency of 120Hz. On a 8Mhz intosc, if i make the frequency 256 x 120Hz = 30720Hz and count 256 interrupts and then fire a pin i should have 120Hz, right ?
If i setup timer0 with a 1:2 prescaler and preload it with 224 it should have 30,303 KHz which is acceptable. However i can't seem to go any higher than 10Khz.
The code below toggle's the pin on every interrupt, so i should see a 15Khz signal, instead i'm getting a 5Khz signal.
	Code:
	
'==== Dec's for OSC =====================================
DEFINE OSC 8                                            'Int OSC @ 8 MHz, for PBP
OSCCON = %01110001                                      'Int OSC @ 8 MHz
'==== Set includes ======================================
INCLUDE "DT_INTS-14.bas"                                'DTints Interrupt System
include "ReEnterPBP.bas"                                'Enable PBP int reenter
'==== Set defines =======================================
OPTION_REG  = %00000000                                 'Set TMR0 precsaler to 1:256, source Fosc/4
WPU         = %00000000                                 'Weak pull-ups disabled
INTCON      = %00100000                                 'Global + Perhip. INT enabled, TMR0 int enabled
'PIE1        = %00000000                                 'TMR2 to PR2 match INT enabled
'PIE2        = %00000000                                 'Set TMR1 overflow interrupt
'PIR1        = %00000000                                 
'PIR2        = %00000000                                 
'PCON        = %00000000
'IOC         = %00000000                                 'Interrupt On Change disabled
'PCON        = %00000000                                 'Ultra lowpower + BOR disabled
ANSEL       = %00000000                                 'Select analog inputs: none
ANSELH      = %00000000                                 'Select analog inputs: none
ADCON0      = %00000000                                 'A/D Module is OFF
ADCON1      = %00000000                                 'A/D control register
CM1CON0     = %00000000                                 'Comparator1 Module is OFF
CM2CON0     = %00000000                                 'Comparator2 Module is OFF
VRCON       = %00000000                                 'Voltage ref. disabled
TRISA       = %00000000                                 'Set Input/Output (0 to 5)
PORTA       = %00000000                                 'Ports High/Low (0 to 5)
TRISB       = %00000000                                 'Set Input/Output (4 to 7)
PORTB       = %00000000                                 'Ports High/Low (4 to 7)
TRISC       = %00000000                                 'Set Input/Output (0 to 7)
PORTC       = %00000000                                 'Ports High/Low (0 to 7)
T2CON       = %00000100                                 'Timer2 is On,                                 
T1CON       = %00000000                                 'Timer1 is off
CCP1CON     = %00000000                                 'CCP engine is off
'==== Setup DTints interrupt handler ====================
ASM
INT_LIST  macro                                         ;Setup IntSource
        INT_Handler    TMR0_INT,  _intTMR0,  PBP,  YES  ;Enable TMR0 interrupts
    endm
    INT_CREATE                                          ;Create interrupt processor
ENDASM
'==== Wait for hardware settle ==========================
pause 500
'==== Enable interrupt processing =======================
@   INT_ENABLE   TMR0_INT                               ;Activate TMR0 INT
'==== Main program loop =================================
main:
@   nop                                                 ;Do nothing
goto main
          
'==== The End ===========================================
theend:                                                 ' Endless loop
goto theend                                             ' Endless loop
'Routines and Handlers
'==== TMR0 Interrupt handler ============================
intTMR0:                                                'TMR0 INTERRUPT HANDLER
TMR0 = 223                                              'Reload TMR0
toggle PORTC.0
@ INT_RETURN                                            ;Jump back to previous process
 
	 
 - 
	
	
	
	
		With a 8Mhz internal clock (which translates to a 2Mhz timer clock), if you set timer 0 with a no scalers, the variable TMR0 will increment every 0.5uS, and overflow every 128uS.
For a frequency of 30720Hz you want TMR0 to overflow every 32.552083uS which translates to 65.104167 increments of 0.5uS.
So if you preload TMR0 with the value 191, it will increment 65 times every time before overflowing, giving you a frequency of 30.769kHz (or equiv. of 120.19Hz PWM).
	 
 - 
	
	
	
	
		You're so right ! I don't know how i missed this. Thanks !
A couple of day's ago i found a timer calculator, i just entered the frequency and the calculations were made. However, it seems that the scrollbars that are used to set the prescalers and preload values in this calculator are refreshed after clicking calculate. I was using bad combinations every calculation. 
I tried your values, and it worked like a charm, thanks for replying !
Thanks !
	 
 - 
	
	
	
	
		You might also consider using the PWM module along with a small interrupt driven "helper" to link together several smaller PWM period "frames" into the much longer 120-Hz PWM period.  This method produces a relatively low overhead high resolution jitter free PWM output.
I don't have PBP but perhaps you and other Forum members can muddle through this pseudo C code example.  
This example uses a 12-MHz clock (Tcy = 0.3334-usecs) with PWM module settings that produce a PWM period of 333.3334-usecs (250 x 1.3334-usec 'steps').  The interrupt "helper" links together 25 of these short 333.3334-usec PWM period "frames" to produce a precise 120-Hz (8333.3334-usec) period with an output pulse width of 0 to 6250 x 1.3334-usec "steps".
Regards, Mike
	Code:
	
;******************************************************************
;
;  // setup PWM for a 333.3 usec period (12-MHz xtal, Timer 2
;  // prescale 4:1, PR2 = 249) and use 25 of these 333.3 usec
;  // PWM "frames" for a precise 120-Hz (8.3334 msec) period.
;
;  char frame = 1;              // initialize "frame" var'
;  int width = 100;             // 0..6250 (1.333 usec steps)
;  int pulse;                   // isr work variable, 0..6250
;
;  void interrupt()             // 120-Hz example
;  { pir1.TMR2IF = 0;           // clear timer 2 interrupt flag
;    frame--;                   // decrement 'frame' number
;    if(frame == 0)             // if end-of-period
;    { frame = 25;              // reset for new period
;      pulse = width;           // refresh work variable
;    }
;    if(pulse > 250)            //
;    { ccpr1l = 250;            // do a 100% duty cycle frame
;      pulse -= 250;            // update 'pulse'
;    }
;    else                       //
;    { ccpr1l = pulse;          // do variable duty cycle
;      pulse = 0;               // force remaining frames to 0%
;    }
;
;
;
;  void Main()                  //
;  { cmcon0 = 7;                // comparators off
;    ansel = 0;                 // a2d convertor off
;    pir1 = 0;                  // clear all interrupt flags
;    pie1.TMR2IE = 1;           // enable TMR2 interrupts
;    ccpr1l = 0;                // set 0% duty cycle (output off)
;    t2con = 0b00000101;        // '0-------' unimplemented bit
;                               // '-0000---' TOUTPS<3:0>, postscale 1
;                               // '-----1--' TMR2ON, turn Timer 2 on
;                               // '------01' T2CKPS<1:0>, prescale 4
;    pr2 = 250-1;               // 250 x 1333-nsec 'ticks' = 333 usecs
;    intcon = 0b11000000;       // '1-------' GIE, enable global ints
;                               // '-1------' PEIE, enable peripheral ints
;                               // '--0-----' T0IE, TMR0 ints disabled
;                               // '----0---' GPIE, IOC disabled
;                               // '-----000' T0IF/INTF/GPIF flags
;    while(1)                   //
;    {                          //
;    }                          //
;  }
;
 
	 
 - 
	
	
	
		
Low frequency PWM
	
	
		Hi all,
Thanks for the helping hand and code. I used your code samples but it just didn't do the trick. My goal was to be able to have 2 low freqency PWM signals with a 90 degree phase shift. 
I finally accomplished this by using a 18F1230 @ 8Mhz , DT-INTS and a few lines of code.
The code below produces a 30Hz PWM that increments it's dutycycle every interrupt until it reaches 100%, it will then decrement to 1% and start over. It uses a 'base' frequency of 60Hz (PTPER 519), and distributes the dutycycle over the 30Hz periods.
	Code:
	
'==== Set fuses
@ __CONFIG    _CONFIG1H, _OSC_INTIO2_1H & _FCMEN_OFF_1H & _IESO_OFF_1H
@ __CONFIG    _CONFIG2L, _PWRT_OFF_2L & _BOR_OFF_2L & _BORV_3_2L
@ __CONFIG    _CONFIG2H, _WDT_OFF_2H & _WDTPS_1_2H
@ __CONFIG    _CONFIG3L, _HPOL_HIGH_3L & _LPOL_HIGH_3L & _PWMPIN_ON_3L
@ __CONFIG    _CONFIG3H, _FLTAMX_RA5_3H & _T1OSCMX_HIGH_3H & _MCLRE_OFF_3H
@ __CONFIG    _CONFIG4L, _STVREN_OFF_4L & _BBSIZ_BB256_4L & _XINST_OFF_4L & _DEBUG_OFF_4L
@ __CONFIG    _CONFIG5L, _CP0_OFF_5L & _CP1_OFF_5L
@ __CONFIG    _CONFIG5H, _CPB_OFF_5H & _CPD_OFF_5H
@ __CONFIG    _CONFIG6L, _WRT0_OFF_6L & _WRT1_OFF_6L
@ __CONFIG    _CONFIG6H, _WRTB_OFF_6H & _WRTC_OFF_6H & _WRTD_OFF_6H
@ __CONFIG    _CONFIG7L, _EBTR0_OFF_7L & _EBTR1_OFF_7L
'==== Include DT interrupts
INCLUDE "DT_INTS-18.bas" ;Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" ;Include if using PBP interrupts
'==== Dec's for OSC
OSCCON  = %01110000 'Int OSC @ 8 MHz
'OSCTUNE = %00000000  'PLL disabled, center freqency
DEFINE OSC 8 'Int OSC @ 8 MHz, for PBP
'==== Initialize DT_INTS
ASM
INT_LIST  macro    ; IntSource,          Label,   Type,        ResetFlag?
        INT_Handler                PT_INT,        _pwmINT,        PBP,        YES
    endm
    INT_CREATE ;Creates the interrupt processor
ENDASM                     
'==== Set port associated registers
ADCON0  = %00000000 'A/D convertor disabled
ADCON1  = %00001111 'All ports configured digital
ADCON2  = %10000000 'Right justify results
CMCON   = %00000000 'Comparator modules disabled
CVRCON  = %00000000
PTCON0  = %00001100 'Fosc/4 Prescale 1:64, Free running mode
PWMCON0 = %00110111 'PWM0,1,2,3 enabled, independent mode
PWMCON1 = %00000001 'Special event on count upwards, Sync PWM timebase 
PORTA   = %00000000
PORTB   = %00000000
TRISA   = %01100000
TRISB   = %00000100
'==== Dec's for pin aliasing INPUT'S
'none  
'==== Dec's for pin aliasing OUTPUT'S
'none
'==== Reset PWM duty registers
PDC0L = 0: PDC0H = 0 'Reset duty register 0
PDC1L = 0: PDC1H = 0 'Reset duty register 1
PDC2L = 0: PDC2H = 0 'Reset duty register 2
                                       
'==== Dec's for variables
pwmFREQ var word
pwmDUTY_TGT var word
pwmDUTY var word
pwmDIR  var bit
pwmTOG  var bit
pwmFL   var bit
'==== Initialise variables
pwmFREQ = 0
pwmDUTY_TGT = 0
pwmDUTY = 0
pwmDIR  = 0
pwmTOG  = 0
pwmFL   = 0
'==== Wait for hardware settle
Pause 500 'Settle for 0,5 seconds
'==== Enable PWM interrupts
@ INT_ENABLE   PT_INT ;Enable PWM to PTPER match interrupts            
'==== SET PWM frequency and dutycycle
pwmFREQ = 519 'Makes 60,010 Hz, 100% dutycycle = 2076
pwmDUTY = 1 'Initial dutycycle
PTPERL = pwmFREQ.lowbyte 'Set PWM frequency lowbyte
PTPERH = pwmFREQ.highbyte 'Set PWM frequency lowbyte
'pwmFREQ_CO = 2076 '100% dutycycle
'Find out if desired PWM is below 50% or over.
'Double the ammount of dutycycle because 
'the frequency will be half the base frequency .. 
if pwmDUTY <= 1038 then
        pwmDUTY_TGT = pwmDUTY * 2
        pwmDIR = 0
else
        pwmDUTY_TGT = (pwmDUTY - 1038) * 2
        pwmDIR = 1
endif
PTCON1         = %10000000 'Start PWM output 
'==== Main program loop
main_loop:
@ nop 'Do nothing
goto main_loop
          
'==== The End
theend: 'Endless loop
goto theend 'Endless loop
'==== Routines
pwmINT:
PWMCON1.1 = 1
    if pwmDIR = 0 then        
        if pwmTOG = 0 then
            PDC0L = 0 
            PDC0H = 0
            PDC1L = pwmDUTY_TGT.lowbyte 
            PDC1H = pwmDUTY_TGT.highbyte
            pwmTOG = 1
                           else     
            PDC0L = pwmDUTY_TGT.lowbyte 
            PDC0H = pwmDUTY_TGT.highbyte
            PDC1L = 0 
            PDC1H = 0
            pwmTOG = 0
                           endif
    else
        if pwmTOG = 0 then
            PDC0L = 2076 
            PDC0H = 2076
            PDC1L = pwmDUTY_TGT.lowbyte 
            PDC1H = pwmDUTY_TGT.highbyte
            pwmTOG = 1
                           else
            PDC0L = pwmDUTY_TGT.lowbyte 
            PDC0H = pwmDUTY_TGT.highbyte
            PDC1L = 2076 
            PDC1H = 2076
            pwmTOG = 0
                           endif
    endif
                if pwmfl = 0 then
                    pwmduty = pwmduty + 1
                else
                    pwmduty = pwmduty - 1
                endif
                if pwmduty >= 2076 then pwmfl = 1
                if pwmduty <= 0 then pwmfl = 0    
                if pwmDUTY < 1038 then
                                pwmDUTY_TGT = pwmDUTY * 2
                                pwmDIR = 0
                else
                                pwmDUTY_TGT = (pwmDUTY - 1038) * 2
                                pwmDIR = 1
                endif
PWMCON1.1 = 0
@ INT_RETURN
 Problem solved, Thanks guys !
Best regards, UltiBlade