News:

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

Main Menu

Fraction Calculations

Started by Fanie, Aug 12, 2025, 11:12 AM

Previous topic - Next topic

Fanie

What is the difference between the calculation speed of the 16F pics vs the 18F pics when dealing with fractions at the same clock speed ?

What is the difference between the calculation speed of the 18F pics vs the 24F pics when dealing with fractions at the same clock speed ?

trastikata

#1
Hello Fanie, can clarify what do you mean by fractional calculations?

Did you mean for example multiplication of two floating point variables?

Or you meant the fixed point arithmetic acceleration available in PIC24F devices compared to emulated fixed point arithmetic in 18F and 16F devices?

Fanie

#2
Yes, multiplication / division of values by 0,1 to 0,01

I think I may have to use a 24F pic in any case.

There is a glitch in the software (or somewhere else.  Will post the code in the next post and what it does.

Frizie

I hardly ever use fractions (float), even though I use the MCP3422 ADC to display the distance between two rollers with a resolution of 0.01 mm.
For example:

DIM Distance AS WORD
Distance = 12345
PRINT DEC Distance / 100, ".", DEC2 Distance // 100, "mm"


Wil display 123.45mm
Ohm sweet Ohm | www.picbasic.nl

trastikata

Les' Positron 24 has really well optimized floating point routines and they are quite faster than the 8-bit routines - I have an extremely computational intensive program for IR imaging sensors which is ruining quite fast on a dsPIC33.

If you are looking for faster calculations, take a dsPIC33 instead of the PIC24, you can run the former at 160 MHz.

Fanie

I have a simple application that has to change an input frequency to less or more.
There is an input and an output for this.
There are two LED's as simple indicators.
There are two switches, one decrease the output frequency, the next increase the frequency.

The code is working, I do however find that a sporadic glitch happens and not sure if it is hardware or software.

I'm using a 18F14K50 currently, it just seems a huge micro to perform such an app.  I was hoping to get a 16 bit 12 pin TQFP but no.  And then I have to consider what is available here, seems the 24FJ16MC102 but in a SOIC and cost roughly the same as the 18F but with potentially better performance.

There may be a better way to do the software. If there is I am keen to learn.

Device = 18F14K50    ' 24FJ16MC102     '32CM5164JH00032T

Xtal = 32

Config_Start
   FOSC = IRC        ; Internal RC oscillator
   PLLEN = On       ; PLL is under software control
   PCLKEN = OFF      ; Primary clock is under software control     
   FCMEN = OFF       ; Fail-Safe Clock Monitor disabled         
   IESO = On        ; Oscillator Switchover mode enabled
   PWRTEN = On      ; PWRT enabled
   BOREN = Off      ; Brown-out Reset enabled and controlled by software (SBOREN is enabled)
   BORV = 30        ; VBOR set to 2.2 V nominal
   WDTEN = OFF      ; WDT is controlled by SWDTEN bit of the WDTCON register
   WDTPS = 64
   MCLRE = OFF      ; RA3 input pin enabled; MCLR disabled
   HFOFST = On      ; The system clock is held off until the HF-INTOSC is stable.
   STVREN = On      ; Stack full/underflow will cause Reset
   LVP = Off        ; (Single-Supply icsp ?) Low Voltage programming disabled
   BBSIZ = OFF        ; 1kW boot block size
   XINST = On        ; Instruction set extension and Indexed Addressing mode enabled
   Cp0 = On            ; Block 0 code-protected
   CP1 = On         ; Block 1 (002000-003FFFh) code-protected
   CPB = On         ; Boot block (000000-0007FFh) code-protected
   CPD = On         ; Data EEPROM code-protected
   WRT0 = OFF       ; Block 0 (000800-001FFFh) not write-protected
   WRT1 = OFF       ; Block 1 (002000-003FFFh) not write-protected
   WRTB = OFF       ; Boot block (000000-0007FFh) not write-protected
   WRTC = OFF       ; Configuration registers (300000-3000FFh) 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
   EBTRB = OFF      ; Boot block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End
      
TRISA = %00001000               ' Configure I/O
TRISB = %11000000
TRISC = %00001000
Bcf ANSEL.7                     ' disable A/D cha 7 to make portC3 a digital input

ADCON0 = %00101100              ' Set analogue inputs on AN11       01 enable ADC 00 disable ADC
ADCON1 = %00000000              ' AD control reg
ADCON2 = %1001011
OSCCON = %01101100             '
                                ' %01011100 select internal 4 MHz clock defined by FOSC<2:0> of the CONFIG1
                                ' -111---- = 16MHz   1MHz
                                ' -110---- = 8MHz    500kHz
                                ' -101---- = 4MHz    250kHz
                                ' -100---- = 2MHz
OSCCON2 = %00000111
OSCTUNE = %11000000             'b7 = INTSRC, b6 = PLLEN, b0-5 = max freq
'Config1H.3 = 1
'WDTCON =  %00000000
'ewrite 300001, [%10111000]     'Config1H = %00001000     Int RC Osc    ***** clock *****
                               'PLL enabled CLK x 4

Dim Cnti As Dword               ' Count cycles in
Dim Cntitot As Float
Dim Cnto As Dword               ' Count cycles out
Dim FacTor As Float
Dim Vin As Dword                ' Voltage input
Dim Tin As Float                '
Dim FNew As Dword
Dim A As Bit
Dim B As Bit
Dim Dis3 As Byte       
Dim Tel As Byte
Dim Braam As Byte

Symbol NCA0 = PORTA.0            ' NC output                         o
Symbol NCA1 = PORTA.1            ' NC Output                         o
Symbol NCA2 = PORTA.2            ' NC Output                         o

Symbol NCA4 = PORTA.4            ' NC Output                         o
Symbol LedR = PORTA.5            ' LedR                              o

Symbol NCB4 = PORTB.4            ' NC Output                        0
Symbol Fn1 = PORTB.5            ' Analog 11 Pot                     i   An
Symbol Sw2 = PORTB.6            ' Sw2 Input                         i
Symbol Sw1 = PORTB.7            ' Sw1 Input                         i
                           
Symbol FOut = PORTC.0            ' Freq Output                       o
Symbol NCC1 = PORTC.1              ' NC Output                         0
Symbol NCC2 = PORTC.2            ' NC Output                         0
Symbol Fin = PORTC.3            ' Freq Input                        i

Symbol LedY = PORTC.4           ' LedY Output                       0
Symbol LedG = PORTC.5           ' LedG Output                       o
Symbol FinA = PORTC.6            ' Freq Input Analog                 i
Symbol NCC7 = PORTC.7            ' NC Output                         0

PORTA = 0 : PORTB = 0 : PORTC = 0

'********************************************************
'*              Main Program Starts Here
'********************************************************

Start: 
           Tin = 0
           Cnti = 0
           FOut = 1
           FacTor = ERead 10
           DelayMS 20
                If FacTor < 0.20 Or FacTor > 100 Then
                FacTor = 1.07                    ' was 1
                EWrite 10, [FacTor]
                DelayMS 20
                EndIf
               
           Cnti = 10000                  'was 1000

           Cnto = 20000                  ' Was 10000   8000  2000    100
           Cntitot = 10000               ' Was 10000   8000  2000    100
          
           LedR = 1 : LedG = 1 : DelayMS 50 : LedR = 0 : LedG = 0 : DelayMS 50
           LedR = 1 : LedG = 1 : DelayMS 50 : LedR = 0 : LedG = 0 : DelayMS 50
           LedR = 1 : LedG = 1 : DelayMS 50 : LedR = 0 : LedG = 0 : DelayMS 50
          
           GoTo InloOutHi
  
' cnto & cnto1 value
' 1700 ~ 6000rpm  2000 ~ 5000rpm  4000 ~ 2500rpm  10000 = 1000rpm  20000 = 500rpm  30000 = 250rpm  40000 = 100rpm   50000 ~ 50 rpm
           
       
InHiOutHi:     ' Input High and Output High

            FOut = 1
            LedR = 1
            PORTC.0 = 1
            PORTA.5 = 1
            If Fin = 1 Then Cnti = Cnti + 1
           
            If Fin = 0 Then
                Cntitot = Cntitot * 5
                Cntitot = Cntitot + Cnti
                Cntitot = Cntitot / 6
                GoTo InloOutHi
                EndIf
               
            Cnto = Cnto - 1
          
            If Cnto = 0 Then
                GoSub Calcul
                LedG = 0
                GoTo InhiOutLo
                EndIf
           
            GoTo InHiOutHi
           
InhiOutLo:     ' Input High and Output Low

            FOut = 0
            LedR = 1
            PORTC.0 = 0
            PORTA.5 = 1
            If Fin = 1 Then Cnti = Cnti + 1
           
           
            If Fin = 0 Then
                Cntitot = Cntitot * 5
                Cntitot = Cntitot + Cnti
                Cntitot = Cntitot / 6
                GoTo InLoOutLo
                EndIf
           
            Cnto = Cnto - 1
           
            If Cnto = 0 Then
                GoSub Calcul
                LedG = 1
                GoTo InHiOutHi
                EndIf
           
            GoTo InhiOutLo
 
InloOutHi:     ' Input Low and Output High
           
            FOut = 1
            LedR = 0
            PORTC.0 = 1
            PORTA.5 = 0
            If Fin = 1 Then
                Tin = 0
                Cntitot = Cntitot * 5
                Cntitot = Cntitot + Cnti 
                Cntitot = Cntitot / 6
                Cnti = 0
                GoTo InHiOutHi
                EndIf
               
            Cnto = Cnto - 1
           
            If Cnto = 0 Then
                PORTC.5 = 0
                Tin = Tin + 1
                GoSub Calcul
                LedG = 0
                GoTo InLoOutLo
                EndIf
           
            GoTo InloOutHi
     
InLoOutLo:     ' Input Low and Output Low
           
            FOut = 0
            LedR = 0
            PORTC.0 = 0
            PORTA.5 = 0
            If Fin = 1 Then
                Tin = 0
                Cntitot = Cntitot * 5
                Cntitot = Cntitot + Cnti
                Cntitot = Cntitot / 6
                Cnti = 0
                GoTo InhiOutLo
                EndIf
                           
            Cnto = Cnto - 1
           
            If Cnto = 0 Then
                PORTC.5 = 1
                Tin = Tin + 1
                GoSub Calcul
                LedG = 1
                GoTo InloOutHi
                EndIf
 
            GoTo InLoOutLo
           
 '**************************************************************************
'                       SUBROUTINES START HERE
'***************************************************************************           
 
' cnto value vs output
' 1700 ~ 6000rpm  2000 ~ 5000rpm  4000 ~ 2500rpm  10000 = 1000rpm  20000 = 500rpm  30000 = 250rpm  40000 = 100rpm   50000 ~ 50 rpm           

Calcul:
            If Tin > 7 Then
               Cntitot = 50000
               Tin = 7     ' tin was 5 , was 7
               EndIf      
           
            Cnto = Cntitot * FacTor ' 0.25
            
            If Cnto < 100 Then  
               Cntitot = 100
               EndIf   ' Max output frequency, Less is faster (300)
           
            If Cnto > 50000 Then
               Cnto = 50000
               EndIf
           
            Dis3 = Dis3 + 1
           
            If Dis3 > 50 Then
                Dis3 = 0
                If Sw2 = 1 Then FacTor = FacTor - 0.01 : A = 1 : EndIf
                If FacTor < 0.10 Then FacTor = 0.10 : EndIf             ' was 0.20
                If Sw1 = 1 Then FacTor = FacTor + 0.1 : A = 1 : EndIf
                If FacTor > 40 Then FacTor = 40
                If Sw1 = 1 And Sw2 = 1 Then
                   FacTor = FacTor - 1
                   A = 1
                   If FacTor > 60 Then FacTor = 60              ' was 40
                   If FacTor < 0.10 Then FacTor = 0.10          ' Was 0.20
                   EndIf
                  
            If Sw1 = 0 And Sw2 = 0 And A = 1 Then
                'LedR = 0 : LedG = 0 : DelayMS 2
                EWrite 10, [FacTor]
                DelayMS 20
                A = 0
                EndIf
                       
            EndIf
                                            ' mul by 4 max range freq in
                                            ' devide by 2 = 200Hz FS
                                            ' devide by 3 = 150Hz FS
                                            ' devide by 4 = 100Hz FS
          
            Return

            End

top204

#6
In your code, disable the Extended Instructions in the config fuses:

XINST = Off        ; Instruction set extension and Indexed Addressing mode disabled

The compiler does not use these, 'rather redundant', mnemonics and device map, and it will alter the code's operations because it changes the device's memory map, and some other mnemonic operations.

Fanie

Input frequency is not very high, 0 to 600Hz

Thank you Les.

trastikata

#8
Hello Fanie,

is it possible in your calculations routine"Calcul" or the other logic a situation where those Dword variables overflow to 65535 when subtracting from 0?

But what troubles me is this logic here:

If Sw2 = 1 Then FacTor = FacTor - 0.01 : A = 1 : EndIf
                If FacTor < 0.10 Then FacTor = 0.10 : EndIf             ' was 0.20
                If Sw1 = 1 Then FacTor = FacTor + 0.1 : A = 1 : EndIf
                If FacTor > 40 Then FacTor = 40
                If Sw1 = 1 And Sw2 = 1 Then
                   FacTor = FacTor - 1
                   A = 1
                   If FacTor > 60 Then FacTor = 60              ' was 40
                   If FacTor < 0.10 Then FacTor = 0.10          ' Was 0.20
                   EndIf

What happens if both SW1 and SW2 are equal to 1 and the FacTor is less than 1.01, say for example 0.5?

QuoteIf Sw2 = 1 Then FacTor = FacTor - 0.01 : A = 1 : EndIf
If FacTor < 0.10 Then FacTor = 0.10 : EndIf            ' was 0.20

This gets executed and the FacTor is 0.49

QuoteIf Sw1 = 1 Then FacTor = FacTor + 0.1 : A = 1 : EndIf
If FacTor > 40 Then FacTor = 40


This gets executed too and the FacTor becomes 0.59 (the 40 probably should be modified to 60 looking at the next lines)

Thus far your FacTor variable is within tolerances.

QuoteIf Sw1 = 1 And Sw2 = 1 Then
FacTor = FacTor - 1
A = 1
If FacTor > 60 Then FacTor = 60              ' was 40
If FacTor < 0.10 Then FacTor = 0.10          ' Was 0.20
EndIf


This also gets executed, so FacTor = 0.59 - 1 = -0.41 which is negative.
Next lines says if > 60 then it is 60 --> this is not executed.
Next line says "If FacTor < 0.10 Then FacTor = 0.10" --> this is true because FacTor = -0.41 and will set FacTor to 0.1

Thus anytime Calcul routine is called and SW1=1 and SW2=1 and your FacTor is less than 1.01 it will get reset to 0.1 although from 0.1 to 1.01 is a valid range.

Next step is the EEPROM update with the new FacTor but there's only 20 Ms delay, is it long enough to debounce any intermittent on-off states of the switches or this routine can be executed several times while the buttons are being pressed thus by corrupting the EEPROM write?

Fanie

Of all the micro's and their fancy peripherals, timers, there is not a single one I know of with a simple peripheral that can output a user defined frequency.

trastikata

#10
Quote from: Fanie on Aug 13, 2025, 04:25 PMOf all the micro's and their fancy peripherals, timers, there is not a single one I know of with a simple peripheral that can output a user defined frequency.

PIC16F1769
PIC18F27K42

NCO+CLC+DAC+PWM so use the NCO and the DAC.

Fanie

Hi Trastikata,
The code works fine, then suddenly the output falls away momentary and then it works again for any length of time before it does it again.
I want do redo the board and use a 16 bit micro instead of the 8 bit.
I wish Les would do the 32 bit ESP's  :'(  it will open a new world in what one can do.  They have small 32 bit micro's too at 240MHz with lots of peripherals and are less expensive than the pics.

I will have a look into the DAC peripheral.  Thank you !  Didn't think of it.
PWM frequency is far out of range.

trastikata

Quote from: Fanie on Aug 13, 2025, 04:57 PMI will have a look into the DAC peripheral.

Try a flash table with the register values equivalent of the DAC resolution for example 32 voltage levels for the DAC voltage which are describing the desired wave form.

Use one of the timers as a 32 positions phase accumulator and update the DAC register output value from that table each timer interrupt.

Changing the frequency should be matter of changing the timer interrupt period. Some RC-filtering probably will be required.

At least this is how I imagine the design.

Fanie

Getting the timing differences overlapping was challenging, that is why I divided the main loop into 4 parts -

InLoOutHi, InLoOutLo, InHiOutHi and InHiOutLo.

As the input changes and as the output is supposed to change, the code will jump to the relevant of the four routines while keeping track of the input and the output timing.

How else can this be done ?


Fanie

DAC as I see it is basically a PWM value charging a capacitor with a resistor.  I haven't used a DAC peripheral of a pic so I have no idea what to expect.

trastikata

#15
Quote from: Fanie on Aug 13, 2025, 05:54 PMDAC as I see it is basically a PWM value charging a capacitor with a resistor.  I haven't used a DAC peripheral of a pic so I have no idea what to expect.

No, DAC is exactly what it says Digital-to-Analog Converter. You have a voltage reference which is being divided into n-steps by a resistor ladder and the value written in the DAC register will set this voltage output level at the DAC output pin.

For example the PIC16F1769 has 5-bit and 10-bit DAC. Let's take the 5-bit DAC with a positive voltage reference of 3 volts and negative as ground (the negative reference voltage can be higher than ground).

So the DAC output voltage as measured at the output pin can be changed between 0 and 3 volts in 32 steps i.e 3/32= 0.09375v. This voltage change is very fast, I think the propagation delay is less than a microsecond.

If we describe our complete sinusoidal wave as 32 equally spaced in the time domain voltage points in the range of 0 to 3 volts and convert those to 5-bit DAC values, we can store them as a FLASH table describing a complete sinusoidal wave in terms of equally spaced voltage levels.

Now if we set our DAC output sequentially at those voltages it will output a complete sinusoidal wave. How often we will cycle between those values will determine our frequency and here comes the Timer interrupt in play.

If we set our Timer interrupt to 32 times our desired frequency and increment a phase accumulator variable by one in this ISR then we can look in the FLASH table for the corresponding phase voltage and update the DAC output register with this value, once the value reached 31 (or other resolution) it should roll-over back to 0 to repeat the wave.

At lower frequencies this should offload the MCU from complex calculations and the only thing it will have to calculate is the new timer interrupt period according the new frequency. Which makes the code very simple.

With higher DAC resolutions (10-bit) and NCO available we can increase wave smoothness and offload the MCU further by using the NCO as phase accumulator for any desired frequency and the timer will be used to set only the sampling frequency of the output thusby further offloading the MCU from any computations. You can create other waves forms simply by changing the values in the FLASH table.

P.s. This is how I remember the theoretical reading I did some time ago when I wanted to make a GPS jammer and needed different wave forms generator because it seems the noise wave form affects the jamming efficiency - never got to actually build it as it became too risky of getting hefty fine for a hobby project.  ;D