PWM2
This is a revision of David Barker's PWM Module posted previously. This module makes the following changes:
- Added functions for control of the second PWM output connected to the CCP2 module
- Changes to the original SetFreq() function
- Added the SetFreqByTable() function.
The SetFreqByTable() function uses a constants table of PR2 and Timer2 Prescaler load values so the user can switch easily between optimum frequencies and duty cycle resolution. A small utility program has been created to simplify the creation of this array of constants which can be downloaded from PWM WRITER. A screenshot of this utility is provided below.
PWM.bas Revised 29.09.2007
{ ***************************************************************************** * Name : PWM.BAS * * Author : David John Barker & Warren Schroeder * * Notice : Copyright (c) 2007 Mecanique * * : All Rights Reserved * * Date : 23/08/2007 * * Version : 1.0 * * Notes : 29/09/2007 - Added support for 2 PWM outputs * * - Added FreqSetByTable() function * * - PWM Table Writer Utility available at * * http://circuit-ed.com/uplds/pwm_writer.exe * ***************************************************************************** } Module PWM Dim FMaxDuty As Word, FTMR2ON As T2CON.2 #if _device in (18F1220, 18F1320) Dim FPWM1Pin As PORTB.3 // RB3 connected to CCP1 module #else Dim FPWM1Pin As PORTC.2 // RC2 connected to CCP1 module Dim FPWM2Pin As PORTC.1 // RC1 connected to CCP2 module #endif { **************************************************************************** * Name : Start1 * * Purpose : Start PWM Channel 1 * **************************************************************************** } Public Sub Start1() CCP1CON = $0C Output(FPWM1Pin) FTMR2ON = 1 End Sub { **************************************************************************** * Name : Start2 * * Purpose : Start PWM Channel 2 * **************************************************************************** } Public Sub Start2() CCP2CON = $0C Output(FPWM2Pin) FTMR2ON = 1 End Sub { **************************************************************************** * Name : Stop1 * * Purpose : Stop PWM Channel 1 * **************************************************************************** } Public Sub Stop1() Input(FPWM1Pin) CCP1CON = $00 End Sub { **************************************************************************** * Name : Stop2 * * Purpose : Stop PWM Channel 2 * **************************************************************************** } Public Sub Stop2() Input(FPWM2Pin) CCP2CON = $00 End Sub { **************************************************************************** * Name : SetDuty1 * * Purpose : The CCPR1L contains the eight MSbs And the CCP1CON<5:4> * * : contains the two LSbs. This 10-Bit value is represented by * * : CCPR1L:CCP1CON<5:4>. * **************************************************************************** } Public Sub SetDuty1(pDuty As Word) CCP1CON.5 = pDuty.1 CCP1CON.4 = pDuty.0 CCPR1L = pDuty >> 2 End Sub { **************************************************************************** * Name : SetDuty2 * * Purpose : The CCPR2L contains the eight MSbs And the CCP2CON<5:4> * * : contains the two LSbs. This 10-Bit value is represented by * * : CCPR2L:CCP2CON<5:4>. * **************************************************************************** } Public Sub SetDuty2(pDuty As Word) CCP2CON.5 = pDuty.1 CCP2CON.4 = pDuty.0 CCPR2L = pDuty >> 2 End Sub { **************************************************************************** * Name : SetDuty1Percent * * Purpose : Set the duty as a percentage for channel 1 * **************************************************************************** } Public Sub SetDuty1Percent(pPercent As Byte) SetDuty1(FMaxDuty * pPercent / 100) End Sub { **************************************************************************** * Name : SetDuty2Percent * * Purpose : Set the duty as a percentage for channel 2 * **************************************************************************** } Public Sub SetDuty2Percent(pPercent As Byte) SetDuty2(FMaxDuty * pPercent / 100) End Sub { **************************************************************************** * Name : MaxDuty * * Purpose : * **************************************************************************** } Public Inline Function MaxDuty() As FMaxDuty End Function { **************************************************************************** * Name : SetFreq * * Purpose : * * Notes : Initializes Freq settings * * : Resets Duty Cycle To 0 * * : Requires PWM.Start afterward if PWM is not running * **************************************************************************** } Public Function SetFreq(pFrequency As LongWord) As Boolean Const Fosc As LongWord = _clock * 1000000 Dim Prescale As Byte Dim PR2Value, PRConst As Word // loop through all the valid prescalers... PRConst = Fosc / pFrequency / 4 Prescale = 1 Result = false Repeat PR2Value = PRConst / Prescale - 1 // calculate a PR2 value If PR2Value < 256 Then // if it is a valid value, then... FMaxDuty = (PR2Value + 1) * 4 // determine maximum duty... PR2 = PR2Value // initialise PR2 Select Prescale // configure T2CON prescale Case 1 : Prescale = %00000000 // prescale 1 Case 4 : Prescale = %00000001 // prescale 4 Case 16 : Prescale = %00000011 // prescale 16 End Select SetDuty1(0) // output = 0V SetDuty2(0) // T2CON = (T2CON And 252) Or Prescale // load prescaler value Result = true // function return true (success) Exit // exit the sub EndIf Prescale = Prescale * 4 Until Prescale > 16 End Function { **************************************************************************** * Name : SetFreqByTable * * Purpose : Change PWM frequency settings by indexing table * * Notes : Initializes Freq settings * * : Resets Duty Cycle To 0 * * : Requires PWM.Start afterward if PWM is not running * **************************************************************************** } Public Sub SetFreqByTable(pIndex As Byte) Const pwmtbl(4) As Byte = (255,1,127,3) // (Fosc=48) 11719; 5859; Dim prv As Byte pindex = pindex * 2 // table offset value prv = pwmtbl(pindex) // get new PR2 value from array for frequency value FMaxDuty = (prv + 1) * 4 // maximum duty cycle resolution based on PR2 value PR2 = prv // load PR2 SetDuty1(0) // output = 0V SetDuty2(0) // T2CON = (T2CON And 252) Or pwmtbl(pindex+1) // load prescaler value from array End Sub // initialise the module FMaxDuty = 0
Here is example code that uses some of the newly added functions. This example uses an 18F2550 @ 48MHz. Potentiometers are connected to AN2 and AN3 which create the variable duty-cycle for PWM1 and PWM2 outputs. A pushbutton connected to GND and PORTB.0 is used to toggle between PWM frequencies of 11.75KHz and 5.8KHz which have resolutions of 1024 and 512, respectively. These frequencies were determined from a constants table created with the PWM_Writer Utility. The EasyPIC3 PIC development board was used for this test.
Device = 18F2550 Clock = 48 ' set up config for 8MHz crystal and USB Config USBDIV = 2 Config CPUDIV = OSC1_PLL2 Config PLLDIV = 2 Config FOSC = HSPLL_HS Include "pwm.bas" Include "adc.bas" Dim adc0,adc1 As Word Dim tt As Byte TRISA = 15 TRISB = 1 tt = 0 ADCON1 = 11 // AN0..AN3 active ADC's INTCON2.7 = 0 // RBPU active PWM.SetFreqByTable(tt) PWM.Start1 PWM.Start2 While true adc0 = ADC.Read(2) adc1 = ADC.Read(3) If tt = 0 Then PWM.SetDuty1(adc0) PWM.SetDuty2(adc1) Else PWM.SetDuty1(adc0 >> 1) PWM.SetDuty2(adc1 >> 1) End If If (PORTB And 1) = 0 Then Inc(tt) tt = tt And 1 PWM.SetFreqByTable(tt) Repeat DelayMS(5) Until (PORTB And 1) <> 0 EndIf Wend