News:

Let's find out together what makes a PIC Tick!

Main Menu

Problem with hpwm 4 Mhz

Started by Giuseppe, Jun 22, 2024, 10:48 AM

Previous topic - Next topic

Giuseppe

Using a 18f25k22 and setting the frequency to 4Mhz I can't get an output frequency of 150 Hz but I can get 391 Hz. Below is the listing
Device = 18F25K22
Config_Start
  FOSC = INTIO7 ;Internal oscillator block
  PLLCFG = OFF ;Oscillator used directly
  PRICLKEN = On ;Primary clock enabled
  FCMEN = OFF ;Fail-Safe Clock Monitor disabled
  IESO = OFF ;Oscillator Switchover mode disabled
  PWRTEN = OFF ;Power up timer disabled
  BOREN = SBORDIS ;Brown-out Reset enabled in hardware only (SBOREN is disabled)
  BORV = 190 ;VBOR set to 1.90 V nominal
  WDTEN = OFF ;Watch dog timer is always disabled. SWDTEN has no effect.
  WDTPS = 32768 ;1:32768
  CCP2MX = PORTC1 ;CCP2 input/output is multiplexed with RC1
  PBADEN = OFF ;PORTB<5:0> pins are configured as digital I/O on Reset
  CCP3MX = PORTB5 ;P3A/CCP3 input/output is multiplexed with RB5
  HFOFST = On ;HFINTOSC output and ready status are not delayed by the oscillator stable status
  T3CMX = PORTC0 ;T3CKI is on RC0
  P2BMX = PORTB5 ;P2B is on RB5
  MCLRE = EXTMCLR ;MCLR pin enabled, RE3 input pin disabled
  STVREN = OFF ;Stack full/underflow will not cause Reset
  LVP = OFF ;Single-Supply ICSP disabled
  XINST = OFF ;Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
  Debug = OFF ;Disabled
  Cp0 = On ;Block 0 (000800-001FFFh) code-protected
  CP1 = On ;Block 1 (002000-003FFFh) code-protected
  CP2 = On ;Block 2 (004000-005FFFh) code-protected
  CP3 = On ;Block 3 (006000-007FFFh) code-protected
  CPB = On ;Boot block (000000-0007FFh) code-protected
  CPD = OFF ;Data EEPROM not code-protected
  WRT0 = OFF ;Block 0 (000800-001FFFh) not write-protected
  WRT1 = OFF ;Block 1 (002000-003FFFh) not write-protected
  WRT2 = OFF ;Block 2 (004000-005FFFh) not write-protected
  WRT3 = OFF ;Block 3 (006000-007FFFh) not write-protected
  WRTC = OFF ;Configuration registers (300000-3000FFh) not write-protected
  WRTB = OFF ;Boot Block (000000-0007FFh) not write-protected
  WRTD = OFF ;Data EEPROM not write-protected
  EBTR0 = OFF ;Block 0 (000800-001FFFh) not protected from table reads executed in other blocks
  EBTR1 = OFF ;Block 1 (002000-003FFFh) not protected from table reads executed in other blocks
  EBTR2 = OFF ;Block 2 (004000-005FFFh) not protected from table reads executed in other blocks
  EBTR3 = OFF ;Block 3 (006000-007FFFh) not protected from table reads executed in other blocks
  EBTRB = OFF ;Boot Block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End
;**** End of Fuse Configurator Settings ****
;-------------------------------------------------------------------------------
Xtal = 4                   '
OSCCON = %01010100         '
Declare Hserial_Baud = 9600
'-------------------------------------------------------------------------------
     
INTCON = %00100000 
TRISE.3 = 1
'Trise.7 = 1 'on pullup MClr Pag.156
TRISC = %00000000
TRISB = %00000000
TRISA = %00001000   '
'WPUB

PORTA = 0          '
CM1CON0.7 = 0      '
SRCON0.7 = 0       '
LATA = %00000000       '
LATB = %00000000
LATC = %00000000   
   
'-----------------------------------Set A/D-------------------------------------------

'VREFCON0 =%10110000  ' on VREF Off Flag 4096V sel. Pag.344
ANSELC =%00000000
ANSELB =%00000000
ANSELA = %00001000     '
ADCON0 = %00001111     '
ADCON1 = %10000000     '
ADCON2 = %10100011     '
Declare Adin_Res 10
Declare Adin_Stime 50'       
'----------------------------------------------------------------------------------------
'--------Set Reg. Interrupt----------

INTCON = %00000000  '
INTCON2 = %00000000 '
INTCON3 = %00000000 '
PIE1 = %00000000    '
PIR1 = %00000000    '
IPR1 = %00000000    '
IOCB = %00000000    '

'------Set TMR0-------------
T0CON = %01000101 '
TMR0H = 0
TMR0L = 0 '

Symbol GIE    = INTCON.7
Symbol PEIE   = INTCON.6
Symbol TMR0IE = INTCON.5
Symbol TMR0IF = INTCON.2
Symbol TMR0ON = T0CON.7    '
'--------INT0---------------
Symbol INT0IE = INTCON.4
Symbol INT0IF = INTCON.1
Symbol INTDG0 = INTCON2.6    'Pag.117


'Symbol RCIE = PIE1.5   ' USART Receive Interrupt Enable
'Symbol RCIF = PIR1.5   ' USART Receive Interrupt Flag
Symbol GIEH = INTCON.7
Symbol GIEL = INTCON.6

 '------------On interrupt---------------------------

'Clear RCIF
Clear TMR0IF
Set TMR0IE
Set TMR0ON
Set PEIE
'Set RCIE
Set GIE

'----------------PWM ---------------------------------------------

Declare CCP1_Pin PORTC.2 '

'------------------------------------------------------------------------------


Dim  ticks As Byte
Dim let_sensor As Word    '
Dim valore_tot As Word
Dim y As Byte
Dim valore[5] As Word
Dim flag As Byte
Dim secondi As Byte
'------------------------------------------V------------------------------------------------
Dim Ln_Floatin As Float
Dim Ln_Floatout As Float   
Dim Ln_Temp_Float1 As Float
Dim Ln_Temp_Float2 As Float
Dim Ln_Temp_Float3 As Float
Dim Ln_Temp_Byte As SByte
Dim PP_AARG As Float System
Dim FloatOut As Float
'------------------------------------------------------------------------------------------
Dim Steinhart_a As Float '
Dim Steinhart_b As Float '
Dim Steinhart_c As Float '

Dim x0 As Float '     
Dim x1 As Float '   
Dim x2 As Float '   
Dim x3 As Float '   
Dim x4 As Float '   
Dim x5 As Float '   
Dim mV As Float '


Dim temporaneo As Float          '
Dim RESISTENZA As Float          '
Dim lnResistance  As Float       '
Dim lnResistanceCubed  As Float  '
   
Dim Kelvin As Float      '
Dim Celsius As Float     ' 
Dim duty As Byte
Dim index As Byte
Dim celsius_1 As Byte

flag = 0
secondi = 0
Clear duty
Clear index

'-----------------------------------------------------------------------------------------------------------------------------------
   
Steinhart_a = 0.001208958717       
Steinhart_b = 0.0002773567556       
Steinhart_c = 0.00000006725522745   

Symbol Vref = 5000.00               
Symbol Risoluzione = Vref / 1024,00 '
Symbol Resistor_value = 10000       '


INTCON.7 = 1    '

'------------------------------------------------------------------------------------------

On_Interrupt GoTo Int_Label

GoTo Main
'-------------------------- -----------------
                                                                   
Int_Label:               
Context Save
 
ticks = ticks + 1 '

If ticks <61 Then exit ' 61 ticks  (16.384ms per tick)
ticks = 0
Inc secondi       
 
exit: INTCON.2 = 0    '
Resume                                                                   
Context Restore   ' Return from the interrupt subroutine


'-------------------------- Main program -------------------------
                                                                   
Main:  '

If secondi >= 2 Then       '
 secondi = 0
 
  valore_tot = 0
  For y = 0 To 4                           '
  valore [y]= ADIn 3 : DelayUS 50          '
  valore_tot = valore_tot + valore[y]      '
  Next y
  let_sensor = valore_tot/5
 
 ConvertAdvalToTemp()
 vent_pwm()
 If flag.4 = 1 Then
  HSerOut [Dec RESISTENZA,13,10]
  HSerOut [Dec celsius_1,13,10]
 End If
 Toggle PORTC.3
End If

GoTo Main

'------------------ Steinhart-Hart -------------

Proc ConvertAdvalToTemp()
   
        mV = Risoluzione * let_sensor             'Ex let_sensor = 500  3,222 * 500 = 1611 mV
        temporaneo = Vref - mV                    ' 3300 - 1611 = 1689
        x0 = Risoluzione * let_sensor             ' x0 = 3,222 * 500 = 1611
        x1 = x0 * Resistor_value                  ' x1 = 1611 * 16900 = 27225900 
        RESISTENZA = x1 / temporaneo              ' 27225900 : 1689 = 16119,538
                                                  '
Ln_Floatin = RESISTENZA

_FP_Ln:
    Ln_Temp_Float1 = Ln_Floatin
    If Ln_Floatin <> 1.0 Then               '
        Ln_Temp_Float1.Byte0 = $7E          '
        Ln_Temp_Float2 = Ln_Temp_Float1
        Ln_Temp_Float1 = Ln_Temp_Float2 - 1.0
        Ln_Temp_Float1 = Ln_Temp_Float1 / (Ln_Temp_Float2 + 1.0)
        Ln_Temp_Float2 = Ln_Temp_Float1 * Ln_Temp_Float1      ' Ln_Temp_Float2 = 1,9685039
        PP_AARG = (0.45145214 * Ln_Temp_Float2) + -9.0558803
        PP_AARG = (PP_AARG * Ln_Temp_Float2) + 26.940971
        PP_AARG = (PP_AARG * Ln_Temp_Float2) + -19.860189
        Ln_Floatout = PP_AARG
        PP_AARG = (1.0 * Ln_Temp_Float2) + -8.1354259
        PP_AARG = (PP_AARG * Ln_Temp_Float2) + 16.780517
        PP_AARG = (PP_AARG * Ln_Temp_Float2) + -9.9300943
        Ln_Temp_Float3 = PP_AARG
        Ln_Floatout = (Ln_Temp_Float1 * Ln_Floatout) / Ln_Temp_Float3
        Ln_Temp_Byte = Ln_Floatin.Byte0 - $7E
        If Ln_Temp_Byte.7 = 1 Then
             Ln_Temp_Float3 = -Ln_Temp_Byte
        Else
             Ln_Temp_Float3 = Ln_Temp_Byte
        EndIf
        Ln_Floatout = (Ln_Floatout + Ln_Temp_Float3) * 0.6931471806 
    Else
        Ln_Floatout = 0.0
    EndIf

lnResistance = Ln_Floatout 
                             
        x2 = lnResistance * lnResistance
        lnResistanceCubed = lnResistance * x2
        x3 = Steinhart_b * lnResistance
        x4 = Steinhart_c * lnResistanceCubed
        x5 = (Steinhart_a + x3 + x4)
       
        Kelvin = 1 / x5
        Celsius = Kelvin - 273,15
        celsius_1 = Celsius
       
flag.4 = 1 '

EndProc

'----------------------------------------------------------------------------------------------

Proc vent_pwm()

 Select celsius_1
   Case  < 96            'Sotto i 95°    ok
   duty = 0             'PWM 0%
   Case  96 To 97        'Tra 96° e 97°  ok
   duty = 25             'PWM 10%
   Case 98 To 100        'Tra 98° e 100° ok       
   duty = 38             'PWM 15%
   Case  101 To 102      'Tra 101° e 102°
   duty = 73             'PWM 29%
   Case 103 To 104       'Tra 103° e 104° 'ok
   duty = 99             'PWM 39%
   Case = 105            ' 105°      '
   duty = 122             'PWM 48%
   Case   106 To 107     'Tra 106°e 107°  'ok
   duty = 135             'PWM 53%
   Case 108 To 109       'Tra 108°e 109° 'ok
   duty = 160             'PWM 63%
   Case 110 To 112       'Tra 110°e 112°
   duty = 186             'PWM 73%
   Case  = 113           '113°
   duty = 219             'PWM 86%
   Case >= 114           'sopra i 114 ° ok
   duty = 229            'PWM 90%
  End Select
 
HPWM 1,duty,150  '

EndProc



tumbleweed

At 4MHz the min HPWM freq is about 245Hz.

david

4,000,000 (osc)/(1024 (10bit pwm)*16 (prescale))=244.1406....

It really would have been useful to have a wider range of prescaler.
You may be able to use the 16 bit timer and a compare function to give you a lower frequency 16 bit PWM but it will be running under an interrupt.

David

Giuseppe

From manual on page 148 with a 4Mhz oscillator the lowest frequency is 145Hz. In the past I have tried with a 12f1840 and I always manage to have a frequency of 100Hz at 4Mhz even lower than the 145Hz declared by the Proton manual. why is it impossible to go below 395Hz with 18f25k22

david

I think that first value in the table on page 146 should read 245 rather than 145.
The data for the 12F1840 would indicate that 245Hz is also the lowest frequency for that part so I can't explain how you would get 100Hz with a 4MHz clock.

David

tumbleweed

#5
The 12F1840 has a 1:64 setting for the TMR2 prescaler, which would let you go lower in freq (61Hz @ 4M clock).
The 18F25K22 max prescaler value is 1:16, so the min freq is higher (~245Hz)

Some of the newer devices (18FxxK40, Q10, Q43 for example) have a 1:128 prescaler setting for TMR2, so they go even lower.

JonW

#6
NCO and CLC can be used for high-resolution PWM.  The NCO can get to low frequencies.  See appnote NCO + CLC

At such low frequencies, you could run the PIC faster and bit bash the PWM

Giuseppe

Tested up to 245 Hz there are no problems. When you go down with the frequency it no longer responds to the parameters set in the HPWM syntax.

david

Quote from: tumbleweed on Jun 22, 2024, 02:40 PMThe 12F1840 has a 1:64 setting for the TMR2 prescaler, which would let you go lower in freq (61Hz @ 4M clock).
The 18F25K22 max prescaler value is 1:16, so the min freq is higher (~245Hz)

Some of the newer devices (18FxxK40, Q10, Q43 for example) have a 1:128 prescaler setting for TMR2, so they go even lower.

Now I'm confused!
The table of PWM frequencies for the 12F1840 shown in the attachment shows the maximum prescaler value of 1:16 and a maximum PR2 value of 0xFF.  OK I know it's for a 32MHz clock but if you divide everything by 8 to get results for a 4MHz clock you still end up with a minimum frequency of 1.95kHz/8 = 244Hz.   What am I missing here?

David

tumbleweed

It looks to me that the examples are a typical copy-paste error from another datasheet.
If you look at Figure 22-1 and Register 22-1 T2CON definitions, you'll see that there's a 1:64 prescaler setting in the 12F1840.

Also, many newer devices have a dedicated PWM peripheral with its own individual timer instead of having to use a CCP/TMR combination, and these allow many different clock sources and 1:256 prescalers giving you a much wider range of PWM freqs.

Those wouldn't work with the HPWM command though, so you'd have to setup the registers manually.



david

Indeed there is!  Thank you for pointing that out.   It's not the first error I've come across in the 12F1840 data sheet but after looking at the PWM tables I would have assumed it was 1:16 maximum and not gone looking any further.
So is this how Giuseppe got his 100Hz?  He seems to be using HPWM commands.
I find these days more and more I prefer to talk directly to the registers and it's great we can work either way.
Thanks again,
David

keytapper

I wrote this small calculation
$define wantedFreq  300             ' set here the wanted frequency
$define Prescaler  16               ' check the datasheet for a possible one
$define Fosc Xtal * 250000          ' internal frequency. Usually
$define VALUE Fosc / Prescaler / wantedFreq
$if (VALUE > 255)
$SendError "impossible frequency"
$else
PR2 = VALUE                         ' or whichever is needed
$endif
But I can't understand how to make it work.
Ignorance comes with a cost

Giuseppe

I can confirm that with the 12f1840 and the HPWM syntax I managed to obtain a frequency of 100Hz without problems. While with the 18f25k22 I reach a maximum of 245Hz

Maxi

Hi, has anyone tried this? I have solved my low frequency pwm need many times in this way. I write the xtal declare setting as 4mhz, but I make the osc frequency 250khz or 500khz from the osccon register, so my hpwm frequency can be very very low.

keytapper

#14
Quote from: Maxi on Jun 23, 2024, 10:43 AMI write the xtal declare setting as 4mhz, but I make the osc frequency 250khz or 500khz from the osccon register, so my hpwm frequency can be very very low.
You can do this as long as no other functions are involved. Like the Delay and baud rate settings.
Besides that the CPU will be sluggish.
Ignorance comes with a cost

tumbleweed

#15
Quote from: david on Jun 23, 2024, 12:20 AMSo is this how Giuseppe got his 100Hz?  He seems to be using HPWM commands.
I assume so. When I said "Those wouldn't work with the HPWM command" I was referring to using the dedicated PWM peripheral (which the 12F1840 doesn't have), not the 1:64 prescaler with CCP/TMR combo.

Having said that, I went to compile some code for the 12F1840 to test and I don't see how HPWM works with the 12F1840 at all.

When I compile this:
Device = 12F1840
Declare Xtal = 4

HPWM 1,50,100


The resulting asm code never seems to actually set up anything... the HPWM command does a 'call __hpwm_' which immediately branches to '__hpwm_exit_' and returns!

__hpwm_
    movwf PP4H
    bra __hpwm_exit_
<SNIP>
__hpwm_exit_
    movlb 0x00
    return
<SNIP>
F1_000004 equ $ ; in [TEST_18F.BAS] HPWM 1,50,100
    movlb 0x00
    movlw 50
    movwf GEN
    clrf PP1H
    movlw 100
    movwf PP1
    movlw 1
    movlp ((__hpwm_) >> 8)
    call __hpwm_


top204

#16
The HPWM frequencies in the manual are a guide and are for the standard devices. However, the newer devices have a mix of SFR settings for the CCP and PWM peripherals that now make the frequency list obsolete, and they also make the HPWM command obsolete to a certain extent because it is a generic command that uses default settings to give out a waveform of a particular frequency and duty cycle.

I will be removing the frequency list for the next manual update, because it is too out-dated, and a particular min and max frequency is now not possible to give because of all the SFR settings and timer options etc...

For more refined CCP or PWM peripheral operations, it will require procedures to be created by a user for the device being used.

tumbleweed

When I compile the code in post #15, the HPWM command calls the routine '__hpwm_', which does a movwf and then a bra to __hpwm_exit_, which returns without setting up TMR2/CCPCON.

Is there something else that has to be added to get it to execute the rest of the setup and start code?
 
 

tumbleweed

That's the compiler version and code I'm using.

The routine called by 'HPWM 1,50,100' starts off with this:
__hpwm_
    movwf PP4H
    bra __hpwm_exit_
__hpwm_CalcFrequency_

The second instruction branches directly to the exit/return (__hpwm_exit_) without setting up anything.


top204

#19
Your code has not set the pin to use for the CCP1 peripheral, so the compiler's library assembler code cannot create the code for the dedicated pin, so it does not add it, in case it is incorrect and screws up other mechanisms. Default pins are no longer supported with devices, because microchip constantly move things around on them, and it would be a logistic nightmare to go through every PPI file and add default pin settings for all supported peripherals on all supported devices.

Your code listing needs something like:

Declare CCP1_Pin = PORTA.2

The compiler then creates the assembler code:

__hpwm_
    movwf PP4H
    movlw 0x01
    subwf PP4H,W
    btfss STATUS,2
    bra __hpwm_checkCCP2_
    movlw 0x91
    movwf FSR0L
    movlw 0x02
    movwf FSR0H
    movlb ((TRISA)>>7)
    bcf TRISA,2
    bra __hpwm_CalcFrequency_
__hpwm_checkCCP2_
    bra __hpwm_exit_
__hpwm_CalcFrequency_
    movlb 0x00
    clrf PP0
    movlw 0x12
    movwf PP0H
    movlw 0x7A
    movwf PP2
    clrf PP2H
    call __divide_int_u1616_
    movlw 0xFC
    andwf T2CON,F
    movf PP0H,W
    btfsc STATUS,2
    bra __hpwm_start_timer_
    bsf T2CON,PP_T2CKPS0
    call __hpwm_dividebyfour_
    andlw 0xFC
    btfsc STATUS,2
    bra __hpwm_start_timer_
    bcf T2CON,PP_T2CKPS0
    bsf T2CON,PP_T2CKPS1
    call __hpwm_dividebyfour_
    andlw 0xF0
    btfsc STATUS,2
    bra __hpwm_start_timer_
    bsf T2CON,PP_T2CKPS0
    call __hpwm_dividebyfour_
__hpwm_start_timer_
    bsf T2CON,PP_TMR2ON
    decf PP0,W
    movwf PR2
    incf GEN,W
    btfss STATUS,2
    bra __hpwm_notfullon_
    movlw 0xFF
    movwf PP2H
    movwf PP2
    bra __hpwm_loadCCPCONX_
__hpwm_notfullon_
    clrf PP2H
    movf GEN,W
    movwf PP2
    clrf PP3H
    movwf PP3
    incf PR2,W
    btfsc STATUS,Z
    bra __hpwm_loadCCPCONX_
    clrf PP1H
    movwf PP1
    call __multiply_u1616_
__hpwm_loadCCPCONX_
    movf PP2H,W
    movwi INDF0++
    moviw ++INDF0
    andlw 0xCF
    btfsc PP2,7
    bsf WREG,5
    btfsc PP2,6
    bsf WREG,4
    iorlw 0x0C
    movwf INDF0
__hpwm_exit_
    movlb 0x00
    return
__hpwm_dividebyfour_
    rrf PP0H,F
    rrf PP0,F
    rrf PP0H,F
    rrf PP0,F
    return

It looks as though I am going to have to add a warning message if the declares are not used when an HPWM command is in the code listing.