Low freq PWM problem


Closed Thread
Results 1 to 6 of 6
  1. #1
    Join Date
    Apr 2006
    Location
    GearSweaterMountain, The Netherlands
    Posts
    52

    Default 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
    Last edited by ultiblade; - 29th January 2010 at 10:40.

  2. #2
    Join Date
    Apr 2006
    Location
    GearSweaterMountain, The Netherlands
    Posts
    52


    Did you find this post helpful? Yes | No

    Default

    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

  3. #3
    Join Date
    Nov 2005
    Location
    Perth, Australia
    Posts
    429


    Did you find this post helpful? Yes | No

    Default

    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).
    "I think fish is nice, but then I think that rain is wet, so who am I to judge?" - Douglas Adams

  4. #4
    Join Date
    Apr 2006
    Location
    GearSweaterMountain, The Netherlands
    Posts
    52


    Did you find this post helpful? Yes | No

    Default

    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 !

  5. #5
    Join Date
    Aug 2005
    Location
    Michigan, USA
    Posts
    224


    Did you find this post helpful? Yes | No

    Default

    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)                   //
    ;    {                          //
    ;    }                          //
    ;  }
    ;
    Last edited by Mike, K8LH; - 7th February 2010 at 13:53.

  6. #6
    Join Date
    Apr 2006
    Location
    GearSweaterMountain, The Netherlands
    Posts
    52


    Did you find this post helpful? Yes | No

    Default 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
    Last edited by ultiblade; - 17th February 2010 at 07:14. Reason: Code formatting was off

Similar Threads

  1. Old and beyond help ?
    By DavidFMarks in forum mel PIC BASIC Pro
    Replies: 46
    Last Post: - 11th December 2008, 15:23
  2. confused problem with interrupt in a working program
    By illuminator4 in forum mel PIC BASIC Pro
    Replies: 5
    Last Post: - 14th November 2008, 17:01
  3. problem with PWM
    By mimmmis in forum mel PIC BASIC Pro
    Replies: 3
    Last Post: - 12th September 2008, 13:36
  4. Microcontroller with 2 way paging application problem
    By oneohthree in forum mel PIC BASIC Pro
    Replies: 30
    Last Post: - 20th April 2007, 17:27
  5. 4-line LCD Help - using PortA instead of B
    By Tom Gonser in forum mel PIC BASIC Pro
    Replies: 28
    Last Post: - 31st March 2005, 03:14

Members who have read this thread : 2

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts