CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

setup_timer_2() postscale non-functional

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
apakSeO



Joined: 07 Dec 2016
Posts: 60
Location: Northeast USA

View user's profile Send private message

setup_timer_2() postscale non-functional
PostPosted: Thu Jan 12, 2017 4:58 pm     Reply with quote

Using: PIC18F46J50
MPLAB X
CCS v 5.065

Code below:

Code:

// Below 4 lines must be in this order!!!
#include <18F46J50.h>
#fuses NOWDT,NOPROTECT,NODSBOR
#device PIC18F46J50 ICD=TRUE 
#use delay(internal=8MHz, clock=48MHz)   // Use internal 8MHz osc;  setup CPU clock to 48MHz
// Tried experimenting with line below;  not very functional
//#use pwm( CCP2,OUTPUT=PIN_A5,TIMER=2,FREQUENCY=5.86kHz,BITS=8,DUTY=65,PWM_ON)

//#use FIXED_IO( A_outputs=PIN_A7,PIN_A6,PIN_A5,PIN_A2 )
//#use FIXED_IO( C_outputs=PIN_C6,PIN_C2)
//#use FIXED_IO( D_outputs=PIN_D7,PIN_D6,PIN_D5,PIN_D4,PIN_D3,PIN_D2 )
//#use FIXED_IO( E_outputs=PIN_E2,PIN_E1,PIN_E0 )

//***** SET PERIPHERAL PIN SELECT FUNCTIONS
// Make pin RA5 a PWM capable pin output
 #pin_select P2A = PIN_A5

void visOn( unsigned int8 duty )
{
    delay_us(10);                       // Delay for settling
    setup_ccp2(CCP_PWM);                // Set ECCP2 module as PWM mode
   
    // Clock period is 1/CLOCK_MHZ = 1/48e6 = 20.833ns
    // Timer clock is 4*Fosc so each tick is 4*20.833ns = 83.33ns.
    // Set timer2  to roll over @ 0xFF, so every 256 ticks * 83.33ns = 21.33us
    // Prescaler set to 16, so 16 * 21.33us = 341us  or 2.93kHz
    // Now PWM from CPP2 is set at 2.93kHz with a post-scale of 1.  Confirmed.
    // Experimenting with other values of post scale does not work!!!  Freq
    // seems to be fixed at 2.93kHz and post scale value does nothing.
    setup_timer_2(T2_DIV_BY_16,0xFF,2);   
    set_pwm2_duty(duty);
}


void main(void)

    visOn(128);             // Sets a duty cycle of 128/256 = 50%
   
    while(1){
        // Sit in da loop       
    }
   
    return;
}



The line setup_timer_2(T2_DIV_BY_16,0xFF,2) is functional except for the post-scale value. Prescale works and timer2 interrupt/rollover works great. However, the post-scale value appears to be non-functional. No matter if I set the post-scale value to 1,2,4,8, or 16 the PWM output frequency never changes from 2.93kHz.

set_pwm_duty() works flawlessly.

With a 48MHz clock, the lines should result in:

setup_timer_2(T2_DIV_BY_16,0xFF,1) --> 2.93kHz
setup_timer_2(T2_DIV_BY_16,0xFF,2) --> 1.47kHz
setup_timer_2(T2_DIV_BY_16,0xFF,4) --> 733Hz
setup_timer_2(T2_DIV_BY_16,0xFF,8) --> 366Hz
setup_timer_2(T2_DIV_BY_16,0xFF,16) --> 183Hz

However I am having no success in achieving these PWM frequencies; from what I read in the manuals ( both CCS and the specific PIC ) these options should be possible.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Jan 12, 2017 6:29 pm     Reply with quote

It's in the CCS manual:
Quote:
setup_timer_2( )

Postscale is a number 1-16 that determines how many timer overflows
before an interrupt
: (1 means once, 2 means twice, an so on)

In other words, the postscaler doesn't control the Timer2 frequency.
It controls the frequency of interrupts generated by Timer2.
Timer2 will normally generate an interrupt every time it overflows from
0xFF to 0x00. The postscaler value lets you change this. You could
have it interrupt once every 8 PWM cycles, for example.

PWM postscaler explanation:
http://www.ccsinfo.com/forum/viewtopic.php?t=29786&start=1
Sample code for PWM postscaler:
http://www.ccsinfo.com/forum/viewtopic.php?t=41473&start=3
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Fri Jan 13, 2017 2:44 am     Reply with quote

also _read the chip data sheet_.....

If you want lower frequency PWM, you can't do it with the hardware PWM. Can be generated quite easily using the CCP (since this has a 16bit counter).

The hardware PWM, is designed basically for speed. Given that for most PWM applications it is better to be clocking fast (provided the switchers can cope), and that high speed clocks are the one thing difficult to do in software or by other forms of hardware.

If you think about a 'PWM', you can't actually have a postscaler. Doing so, would get rid of the 'width' information in the PWM....
Since the PWM does it's comparisons directly to the selected timer, this would apply if you added a postscaler to the timer.
apakSeO



Joined: 07 Dec 2016
Posts: 60
Location: Northeast USA

View user's profile Send private message

PostPosted: Fri Jan 13, 2017 10:06 am     Reply with quote

My error in thinking was that that the PWM frequency is governed by the timer2 interrupt frequency. It appears that the PWM frequency is governed only by the pre-scaler and the timerX roll-over from 0xFF to 0x00.
apakSeO



Joined: 07 Dec 2016
Posts: 60
Location: Northeast USA

View user's profile Send private message

PostPosted: Fri Jan 13, 2017 10:11 am     Reply with quote

I've been trying for hours to get CCP1 to do a low frequency PWM with no success. Its difficult to wrap my head around what is going on here.

I tried looking at something similar in the example file ex_ccp1s.c:


Code:

setup_ccp1(CCP_COMPARE_CLR_ON_MATCH);        // Configure CCP1 in COMPARE mode
   setup_timer_1(T1_INTERNAL);                  // Set up timer to instruction clk

   while(TRUE)
   {
       while(input(PIN_B0)) ;                   // Wait for keypress

       setup_ccp1(CCP_COMPARE_SET_ON_MATCH);    // Configure CCP1 to set
       set_compare_time(1,0);                   // C2 high now
       set_timer1(0);

       set_compare_time(1,500);                 // Set high time limit
                                                // to 100 us
                                                // limit is time/(clock/4)
                                                // 500 = .0001*(20000000/4)

       setup_ccp1(CCP_COMPARE_CLR_ON_MATCH);    // Configure CCP1 in COMPARE
                                                // mode and to pull pin C2
                                                // low on a match with timer1

       delay_ms(1000);                          // Debounce - Permit only one pulse/sec
   }


But this solution does not jive with my program structure because in the while(1) loop CCP is constantly being toggled between CCP_COMPARE_SET_ON_MATCH and CCP_COMPARE_CLR_ON_MATCH and I don't want to be constantly doing this in the main while(1) loop.

Is there another way? My goal is to have a single function:
Code:

void pwmOn( unsigned int8 duty, unsigned int16 freq)
{
}

which takes the duty cycle and freq as an input and does the PWM on pin A5.

Are there any examples somewhere of using the CCP method of low-frequency PWM with variable duty cycle?
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Fri Jan 13, 2017 10:36 am     Reply with quote

You do it in the CCP interrupt.

So just have a 'toggle' bit, and in the interrupt switch between set, and clear, and alternate the toggle.
apakSeO



Joined: 07 Dec 2016
Posts: 60
Location: Northeast USA

View user's profile Send private message

PostPosted: Fri Jan 13, 2017 12:30 pm     Reply with quote

OK I tried following your lead and first want to make sure I can get a 50% duty cycle by toggling the bit in the ISR using the CCP mode. Code below:

Code:

#include <18F46J50.h>
#fuses NOWDT,NOPROTECT,NODSBOR
#device PIC18F46J50 ICD=TRUE 
#use delay(internal=8MHz, clock=48MHz)   // Use internal 8MHz osc;  setup CPU clock to 48MHz

void main(void)
{
   
    setup_ccp1(CCP_COMPARE_INT);              // CCP1 setup to interrupt on compare
    setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);   // Timer1 ticks every 1/48e6  * 4  = 83.33ns 
    enable_interrupts(INT_CCP1);             
    enable_interrupts(GLOBAL);
    set_compare_time(1,0xFFFF);     // Using full timer1 count 0xFFFF should result in
                                    // interrupt every 83.333ns * 65536 = 5.45ms
                                    // or a period of 10.9ms ... OK WORKS
                                    // Any other value does NOT work here,
                                    // looks like compare is always 0xFFFF
                                    // e.g. a value of 0x8000 for compare time
                                    // should result in a period of 5.45ms
 
    while(1)
    {       
        // In main loop       
    }
 }
     
#int_ccp1
void ccp1_isr()

    output_toggle(PIN_A5);
}
       



Interrupt is generated, output on scope look good, and I end up with a 50% duty cycle. Great. Now I'm trying to change the value in set_compare_time(1, 0xFFFF) to something other than 0xFFFF but no matter what value I set for the compare time, the frequency of my output does not change. Tried values of 0x8000, 0x4000, 0x2000 ... feels like the set_compare_time() function is not modifying the compare time.

What am I missing here?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Jan 13, 2017 12:35 pm     Reply with quote

apakSeO wrote:

Are there any examples somewhere of using the CCP method of low-
frequency PWM ?

Sample code:
http://www.ccsinfo.com/forum/viewtopic.php?t=44328&start=3

How to search CCS example files for PWM examples:
http://www.ccsinfo.com/forum/viewtopic.php?t=39699
It shows a list of examples for the CCP Compare mode.

Using Compare mode to generate two signals with a phase shift.
http://www.ccsinfo.com/forum/viewtopic.php?t=30607&start=3
apakSeO



Joined: 07 Dec 2016
Posts: 60
Location: Northeast USA

View user's profile Send private message

PostPosted: Fri Jan 13, 2017 3:39 pm     Reply with quote

I checked out your sample code and got the main idea, but couldn't comprehend some aspects of it like the usage of !pulse_done_flag .... my application cannot sit and wait within main() for the pulse to be done.

So, here's what I came up with:

Code:
#include <18F46J50.h>
#fuses NOWDT,NOPROTECT,NODSBOR
#device PIC18F46J50 ICD=TRUE 
#use delay(internal=8MHz, clock=48MHz)   // Use internal 8MHz osc;  setup CPU clock to 48MHz

#define PWM_PIN PIN_A5

#define PWM_PERIOD   5460L               // 1/48e6 * 4 * 8 = 83.33ns per timer1 tick
                                         // Timer1 divisor of 8 means 666.66ns per timer1 tick
                                         // We want a pwm frequency of 275Hz or period of 3.64ms
                                         // 3.64ms / 2 / 666.66ns = 2730 timer1 ticks
                                         // Aiming for a PWM frequency of 275Hz


float duty = 50;            // Default to 50% duty cycle
volatile float dutyDiv100;
volatile long  high_time;
volatile float cMinusDuty;
volatile long  low_time;

#int_ccp1
void ccp1_isr( void )

    dutyDiv100 = duty/100;                 
    high_time  = dutyDiv100 * PWM_PERIOD;

    cMinusDuty = (100 - duty)/100;
    low_time   =  cMinusDuty * PWM_PERIOD;
   
    if(!input_state(PWM_PIN)){          // If the pin is low,
        output_high(PWM_PIN);           // Set the pin high ...
        set_compare_time(1, high_time); // .. and change timer to time the low state
    }
    else{
        output_low(PWM_PIN);             // Set the pin low...
        set_compare_time(1, low_time);   // .. change timer to now time the high state
    }
   
    set_timer1(0);                       // Reset the timer1 to start counting from zero
}
                       
void main(void)
{
    setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);   // Timer1 ticks every 1/48e-6  * 4  * 8 = 666.66ns
    set_timer1(0);                            // Ensure timer starts at 0
   
    setup_ccp1(CCP_COMPARE_INT);              // CCP1 setup to interrupt on compare
    output_low(PWM_PIN);                      // Ensure pin starts in OFF state
 
    clear_interrupt(INT_CCP1);
    enable_interrupts(INT_CCP1);             
    enable_interrupts(GLOBAL);
   
   
    while(1)
    {       
        for( int8 i = 1; i<100; i++) {
            duty = i;
            delay_ms(20);
        }
        for( int8 i = 100; i>0; i--) {
            duty=i;
            delay_ms(20);
        }
       
       
       
    }
 }
     

       


The goal is to change a single variable 'duty' from within main() in order to vary the duty cycle of the constant frequency PWM output on A5.
I'm testing this observing the scope output as well as seeing a test LED brighten and dim continuously on my dev board.

Right now the code is mostly functional, but I don't like the smell of it because:

-- I had to define all those floats to deal with the math. Its ugly and probably wasteful. Is there a better way around this? Seems like set_compare_timer will not take anything other than a long for its argument.
-- I had to do the math from within ccp1_isr to constantly re-calculate the proper compare time. Doing math especially floating point within an ISR is probably a bad idea but I can't see a way around this.
-- The PWM frequency and duty cycle is off by few percent; I'm thinking ISR latency due to the floating point arithmetic?

Thanks for all your help so far.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group