PDA

View Full Version : Unstable sine PWM output



pxidr84
- 8th May 2011, 12:29
Hi everyone,

I'm currently making a 3-phase VFD. I use the PIC18F4431 with a 10MHz crystal (PLL enabled, so 10*4=40MHz).

My program works quite good, because I've tried it on a real 3-phase motor, the PIC was feeding the IGBT power module, and the motor ran very fine (thanks to Henrik for the timer reload routine).

Unfortunately, I've discover a phenomenon on the oscilloscope : I've connected a RC filter and I obtain this :


http://www.youtube.com/watch?v=z561eyX6Kmc

As you can see, sometimes the sine "jumps" randomly (it's very quick). It happens more often at high frequencies (here about 120Hz). I think it can be quite bad for the motor and the IGBT power module.

My complete code :


' PIC initialization
DEFINE OSC 40
DEFINE LCD_DREG PORTC
DEFINE LCD_EREG PORTD
DEFINE LCD_RSREG PORTD
DEFINE LCD_EBIT 0
DEFINE LCD_RSBIT 1
DEFINE ADC_BITS 10
DEFINE USE_LOWPRIORITY 1


' BAS includes
INCLUDE "DT_INTS-18.bas"
INCLUDE "ReEnterPBP-18.bas"
INCLUDE "ReEnterPBP-18LP.bas"
INCLUDE "Sine_table.bas"


' ADC registers configuration
ADCON0=%11101
ADCON1=%10000
ADCON2=%10000110
ANSEL1=%0


' PCPWM registers configuration
PTCON0=%10
PTCON1=%10000000
PWMCON1=%1


' Inverter variables
ustep var byte
vstep var byte
wstep var byte
uduty var word
vduty var word
wduty var word
freq VAR word
amp var word
copy VAR WORD
reload VAR WORD
dum VAR WORD
dum1 VAR WORD
dum2 VAR wORD
flag var bit
flag1 VAR BIT
flag2 VAR BIT
flag3 var bit
flag4 var bit
flag5 var bit
flag6 var bit
flag7 var bit
b var byte
b1 var byte
run VAR BIT
rot var BIT
menu var bit
menupos VAR BYTE
isense VAR WORD
usense VAR WORD
tsense VAR WORD
potsense var word
maxfreq var word
minfreq var word
imax var byte
pwmf var byte
res var byte
fault var byte
lastf var byte


' Default variables definition
dum1=34696
dum2=32
menupos=1
b=0
b1=0
flag=%1
flag3=%1
run=%0
fault=%0


' Reading EEPROM
read 0,rot
read 1,maxfreq.highbyte
read 2,maxfreq.lowbyte
read 3,minfreq.highbyte
read 4,minfreq.lowbyte
read 5,imax
read 6,pwmf
read 7,lastf


' PWM carrier frequency configuration
gosub pwmsub


' Create interrupt processors
ASM
INT_LIST_L macro
INT_Handler TMR0_INT,_calcint,PBP,yes
endm
INT_CREATE_L
INT_LIST macro
INT_Handler TMR1_INT,_pwmint,PBP,yes
INT_Handler INT1_INT,_faultint,PBP,yes
INT_Handler INT2_INT,_stopint,PBP,yes
endm
INT_CREATE
ENDASM


' Interrupts and timers enable
@ INT_ENABLE TMR0_INT
@ INT_ENABLE TMR1_INT
@ INT_ENABLE INT1_INT
@ INT_ENABLE INT2_INT
T0CON=%10000010
T1CON=%1


' Inverter startup
high PORTD.5
HIGH PORTE.0
pause 1000


' Turn LCD on and write custom characters to CGRAM
LCDOUT $fe,$40,$0,$4,$e,$15,$4,$4,$4,$0
LCDOUT $fe,$48,$0,$4,$4,$4,$15,$e,$4,$0
LCDOUT $fe,1


' Main program loop
mainlp:

' Start ADC conversion
ADCON0.1=%1
WHILE ADCON0.1=%1:WEND

' Store ADC results
isense.HighByte=ADRESH
isense.LowByte=ADRESL
usense.HighByte=ADRESH
usense.LowByte=ADRESL
tsense.HighByte=ADRESH
tsense.LowByte=ADRESL
potsense.HighByte=ADRESH
potsense.LowByte=ADRESL

' Security fault verifications
if isense**19218>imax then fault=1
if usense>820 then fault=2
if tsense<330 then fault=3

' Security fault tripping
IF fault>0 then goto faultsub

' Brake signal generation
if usense>770 then PORTD.7=%1

' Run
if run=%1 then
IF flag1=%0 then PWMCON0.6=%1:HIGH PORTD.6:flag1=%1
endif

' Stop
if run=%0 then
if flag1=%0 then PWMCON0=%0:LOW PORTD.6:flag1=%1
ENDIF

' FWD rotation
if rot=%1 then
if flag2=%0 THEN ustep=29:vstep=59:wstep=89:write 0,rot:flag2=%1
endif

' RWD rotation
if rot=%0 then
if flag2=%0 THEN ustep=89:vstep=59:wstep=29:write 0,rot:flag2=%1
endif

' - button
Button PORTB.6,1,128,32,b,0,ret
if menu=%0 THEN menupos=menupos-1
ret:

' + button
Button PORTB.7,1,128,32,b1,0,ret1
if menu=%0 THEN menupos=menupos+1
ret1:

' Run/stop button
if PORTD.2=%1 then
if run=%0 and menupos<6 and flag3=%0 then run=%1:flag1=%0:flag3=%1
if run=%1 and flag3=%0 then run=%0:flag1=%0:flag3=%1
endif

' FWD/RWD button
if PORTD.3=%1 then
if rot=%0 and run=%0 and flag4=%0 then rot=%1:flag2=%0:flag4=%1
if rot=%1 and run=%0 and flag4=%0 then rot=%0:flag2=%0:flag4=%1
endif

' Menu/OK button
if PORTD.4=%1 then
if menu=%0 and run=%0 and menupos>5 and flag5=%0 then menu=%1:flag5=%1
if menu=%1 and flag5=%0 then menu=%0:flag5=%1
endif

' Buttons released state
if PORTD.2=0 then flag3=0
if PORTD.3=0 then flag4=0
IF PORTD.4=0 then flag5=0

' LCD first line writing
if rot=%1 then LCDOUT $fe,$2,"+"
if rot=%0 then LCDOUT $fe,$2,"-"
LCDOUT DEC freq DIG 3,DEC freq DIG 2,DEC freq DIG 1,".",DEC freq DIG 0,"Hz"
IF run=%1 then LCDOUT rep 32\5,"RUN"
IF run=%0 then LCDOUT rep 32\4,"STOP"

' Inverter menu (LCD second line writing)
select case menupos

' Menu limits
if menupos<1 then menupos=1
if menupos>10 then menupos=10
if run=%1 then
if menupos>5 then menupos=5
endif

' Current sensing submenu
CASE 1
isense=isense**19218
lcdout $fe,$c0,$1,$20,"1 ISENS: ",DEC isense DIG 2,DEC isense DIG 1,".",DEC isense DIG 0,"A"

' Voltage sensing submenu
case 2
usense=usense**32030
lcdout $fe,$c0,$1,$0,"2 USENS: ",DEC3 usense,"V",32

' Temperature sensing submenu
case 3
tsense=(tsense-1023)/7
lcdout $fe,$c0,$1,$0,"3 TEMP: ",DEC tsense DIG 1,DEC tsense DIG 0,$df,"C",rep $20\2

' Last fault reading submenu
case 4
lcdout $fe,$c0,$1,$0,"4 LASTFAULT: ",DEC1 lastf

' Firmware reading submenu
case 5
IF run=%1 then
lcdout $fe,$c0,$1,$20,"5 FW: V2011.00"
else
lcdout $fe,$c0,$1,$0,"5 FW: V2011.00"
endif

' Maximum frequency adjust submenu
case 6
if menu=%1 then
Lcdout $fe,$c0,$3e,$3e,"A MAX: ",DEC maxfreq DIG 3,DEC maxfreq DIG 2,DEC maxfreq DIG 1,".",DEC maxfreq DIG 0,"Hz"
Button PORTB.6,1,128,4,b,0,ret2
if maxfreq>minfreq THEN maxfreq=maxfreq-1
ret2:
Button PORTB.7,1,128,4,b1,0,ret3
if maxfreq<1200 THEN maxfreq=maxfreq+1
ret3:
flag6=%0
else
lcdout $fe,$c0,$1,$0,"A MAX: ",DEC maxfreq DIG 3,DEC maxfreq DIG 2,DEC maxfreq DIG 1,".",DEC maxfreq DIG 0,"Hz"
IF flag6=%0 then write 1,maxfreq.highbyte:write 2,maxfreq.lowbyte:flag6=%1
endif

' Minimum frequency adjust submenu
case 7
if menu=%1 then
Lcdout $fe,$c0,$3e,$3e,"B MIN: ",DEC minfreq DIG 3,DEC minfreq DIG 2,DEC minfreq DIG 1,".",DEC minfreq DIG 0,"Hz"
Button PORTB.6,1,128,4,b,0,ret4
if minfreq>10 THEN minfreq=minfreq-1
ret4:
Button PORTB.7,1,128,4,b1,0,ret5
if minfreq<maxfreq THEN minfreq=minfreq+1
ret5:
flag6=%0
else
lcdout $fe,$c0,$1,$0,"B MIN: ",DEC minfreq DIG 3,DEC minfreq DIG 2,DEC minfreq DIG 1,".",DEC minfreq DIG 0,"Hz"
IF flag6=%0 then write 3,minfreq.highbyte:write 4,minfreq.lowbyte:flag6=%1
endif

' Current trip adjust submenu
case 8
if menu=%1 then
Lcdout $fe,$c0,$3e,$3e,"C ITRIP: ",DEC imax DIG 2,DEC imax DIG 1,".",DEC imax DIG 0,"A"
Button PORTB.6,1,128,8,b,0,ret6
if imax>0 THEN imax=imax-1
ret6:
Button PORTB.7,1,128,8,b1,0,ret7
if imax<150 THEN imax=imax+1
ret7:
flag6=%0
else
lcdout $fe,$c0,$1,$0,"C ITRIP: ",DEC imax DIG 2,DEC imax DIG 1,".",DEC imax DIG 0,"A"
IF flag6=%0 then write 5,imax:flag6=%1
endif

' PWM carrier frequency adjust submenu
case 9
if menu=%1 then
Lcdout $fe,$c0,$3e,$3e,"D PWMFREQ: ",DEC1 pwmf,rep $20\2
Button PORTB.6,1,128,32,b,0,ret8
if pwmf>1 THEN pwmf=pwmf-1
ret8:
Button PORTB.7,1,128,32,b1,0,ret9
if pwmf<5 THEN pwmf=pwmf+1
ret9:
flag6=%0
else
lcdout $fe,$c0,$1,$0,"D PWMFREQ: ",DEC1 pwmf,rep $20\2
IF flag6=%0 then write 6,pwmf:gosub pwmsub:flag6=%1
endif

' Reset all settings submenu
case 10
if menu=%1 then
if flag7=%0 then
rot=%1:write 0,rot
maxfreq=500:write 1,maxfreq.highbyte:write 2,maxfreq.lowbyte
minfreq=10:write 3,minfreq.highbyte:write 4,minfreq.lowbyte
imax=150:write 5,imax
pwmf=3:write 6,pwmf
Lcdout $fe,$c0,$3e,$3e,"E RESET? OK",REP $20\3:flag7=%1
endif
endif
if menu=%0 then lcdout $fe,$c0,$20,$0,"E RESET?",rep $20\6:flag7=%0

end select
goto mainlp


' PWM update interrupt (Timer 1)
pwmint:

' Timer management
T1CON.0=%0
copy.HighByte=TMR1H
copy.LowByte=TMR1L
copy=copy+reload
TMR1H=copy.HighByte
TMR1L=copy.LowByte
T1CON.0=%1

' PWM U phase calculation
uduty=sine[ustep]
uduty=(uduty<<res)**amp

' PWM V phase calculation
vduty=sine[vstep]
vduty=(vduty<<res)**amp

' PWM W phase calculation
wduty=sine[wstep]
wduty=(wduty<<res)**amp

' PWM U, V and W update
PDC0H=uduty.highbyte
PDC0L=uduty.lowbyte
PDC1H=vduty.highbyte
PDC1L=vduty.lowbyte
PDC2H=wduty.highbyte
PDC2L=wduty.lowbyte

' Phase angle calculation
@ decf _ustep,1
@ decf _vstep,1
@ decf _wstep,1

' Phase angle reinitialization
if ustep=0 then ustep=89
if vstep=0 then vstep=89
if wstep=0 then wstep=89

@ INT_RETURN


' PWM calculation interrupt (Timer 0)
calcint:

' Recalculations
if flag=%1 THEN

' Reload timer calculation
dum=dum1*dum2
reload=Div32 freq
reload=(65535-reload)+8

' U/F calculation
if freq<=500 then
amp=(freq*131)+35
ELSE
AMP=65535
ENDIF

flag=%0
ENDIF

' Frequency reference control by potentiometer
potsense=((potsense<<6)**maxfreq)+5
if potsense<>freq then freq=potsense:flag=%1

' Frequency limits
if freq<minfreq then freq=minfreq
if freq>maxfreq then freq=maxfreq

@ INT_RETURN


' PWM carrier frequency configuration subroutine
pwmsub:

if pwmf=1 then PTPERH=$f:PTPERL=$ff:res=6
if pwmf=2 then PTPERH=$7:PTPERL=$ff:res=5
if pwmf=3 then PTPERH=$3:PTPERL=$ff:res=4
if pwmf=4 then PTPERH=$1:PTPERL=$ff:res=3
if pwmf=5 then PTPERH=$0:PTPERL=$ff:res=2

return


' IPM fault interrupt
faultint:

fault=4
goto faultsub

@ INT_RETURN


' Emergency stop interrupt
stopint:

fault=5
goto faultsub

@ INT_RETURN


' Inverter fault subroutine
faultsub:

' Emergency stop of the inverter
PWMCON0=%0
T1CON.0=%0
LOW PORTD.5
low PORTD.6
low PORTD.7
low PORTE.0
write 7,fault

' Disable interrupts
@ INT_DISABLE TMR0_INT
@ INT_DISABLE TMR1_INT
@ INT_DISABLE INT1_INT
@ INT_DISABLE INT2_INT

' LCD fault display
lcdout $fe,$2,"FAULT!",rep $20\10
lcdout $fe,$c0,"CODE: ",dec fault,rep $20\9

' Fault LED blink loop
faultlp:
HIGH PORTE.1
PAUSE 500
LOW PORTE.1
pause 500
goto faultlp

The sine table :

' Sine table (4 degrees/step, 90*4=360 degrees)
sine VAR byte[90]
sine[0]=128
sine[1]=137
sine[2]=146
sine[3]=155
sine[4]=163
sine[5]=172
sine[6]=180
sine[7]=188
sine[8]=196
sine[9]=203
sine[10]=210
sine[11]=217
sine[12]=223
sine[13]=229
sine[14]=234
sine[15]=239
sine[16]=243
sine[17]=247
sine[18]=250
sine[19]=252
sine[20]=254
sine[21]=255
sine[22]=255
sine[23]=255
sine[24]=255
sine[25]=254
sine[26]=252
sine[27]=250
sine[28]=247
sine[29]=243
sine[30]=239
sine[31]=234
sine[32]=229
sine[33]=223
sine[34]=217
sine[35]=210
sine[36]=203
sine[37]=196
sine[38]=188
sine[39]=180
sine[40]=172
sine[41]=163
sine[42]=155
sine[43]=146
sine[44]=137
sine[45]=128
sine[46]=119
sine[47]=110
sine[48]=101
sine[49]=93
sine[50]=84
sine[51]=76
sine[52]=68
sine[53]=60
sine[54]=53
sine[55]=46
sine[56]=39
sine[57]=33
sine[58]=27
sine[59]=22
sine[60]=17
sine[61]=13
sine[62]=9
sine[63]=6
sine[64]=4
sine[65]=2
sine[66]=1
sine[67]=0
sine[68]=0
sine[69]=1
sine[70]=2
sine[71]=4
sine[72]=6
sine[73]=9
sine[74]=13
sine[75]=17
sine[76]=22
sine[77]=27
sine[78]=33
sine[79]=39
sine[80]=46
sine[81]=53
sine[82]=60
sine[83]=68
sine[84]=76
sine[85]=84
sine[86]=93
sine[87]=101
sine[88]=110
sine[89]=119

And if you have some ideas and suggestions to improve the program... it will be good.

Thanks. :)

Kamikaze47
- 8th May 2011, 16:18
I'm assuming that the changing frequency shown on the scope was because you were changing the frequency yourself. If thats the case, does it ever glitch when the frequency is left the same or is it only when the frequency is changed?

pxidr84
- 8th May 2011, 18:52
Hi,

Yes, I'm changing the frequency during the video. But when I'm in an idle state, the glitches also occurs.

mister_e
- 8th May 2011, 19:07
If you don't have any Logic Analyser handy, I would use a PC soundcard and record the PWM waveform to see if it's a Scope Trigger problem first.

Where your scope get it's trigger from?!? Internal or External?

pxidr84
- 8th May 2011, 19:54
The scope is trigerred to external (CH1), rising edge with noise rejection.

pxidr84
- 11th May 2011, 19:40
News :

I've made some experiments, it's not an oscilloscope trigger problem.

I think it comes from my code. But where?

Acetronics2
- 11th May 2011, 20:08
Hi, Pxidr84

Something caught my attention ... in the reloading, you use DIV32; are you sure there's no possible interrupt while DIV32 works ???

as timer0 is a low priority interrupt ... I'd check that two or three times.

... just an idea after a very quick browse ... ;)

Alain

pxidr84
- 11th May 2011, 20:25
Hi, Pxidr84

Something caught my attention ... in the reloading, you use DIV32; are you sure there's no possible interrupt while DIV32 works ???

as timer0 is a low priority interrupt ... I'd check that two or three times.

... just an idea after a very quick browse ... ;)

Alain

Hi,

Timer0 interrupt is used for the calculation of the inverter paramaters (like the V/F ratio, the timer reload variable, etc.) It's a low priority interrupt because the Timer1 interrupt (used for the PWM generation) is very critical and must be
executed all of the time.

The DIV32 is used for calculate the timer reload value (the timer routine is from Henrik). However this routine is needed for output the desired sine frequency. This routine directly controls the Timer1 interrupt frequency. It is possible to avoid this DIV32 command (because it seems to take a LOT of ressources)?

Maybe it's better to use Timer0 as a high priority interrupt?

Maybe a PWM register is misconfigured?

I don't know :p

pxidr84
- 12th May 2011, 12:48
No ideas for this problem?

cncmachineguy
- 12th May 2011, 13:10
Not sure of a solution yet, but from watching the posted video, I am guessing the problem is some kind of timing issue. I don't think you have things configured wrong as the output looks great most of the time. Rather I think there are some times when a counter gets reset of an interrupt gets interrupted. It glitches then goes right back to perfect wave.

If you watch it and don't alter the frequency, does the glitch happen at regular intervals, or is it random in nature?

mister_e
- 12th May 2011, 13:51
I think Alain may have a point, I would try to skip the DIV32... let's say assign a fixed value there and let it run idle to see if the problem persist.

HenrikOlsson
- 12th May 2011, 17:53
Hi,
I don't know if DT-Ints have any impact on DIV32, I'd guess no but I don't know. The manual says that it's not recomended to have interrupts enbles if using ON INTERRUPT because the system variables that DIV32 relies on might get changed by the ISR. But DT-Ints saves the system variables so I don't think it should be an issue.

With that said I don't see the reason for calculating the interrupt frequency "all the time". If I read your code correctly your TMR0 interrupt executes at around 38Hz so it calculates the reload values 38 times per second. As long as the desired frequency isn't changed the reload value for TMR1 doesn't change so why keep calculating it?

Instead, do it in your Main-loop and even better calculatie it only when the desired frequency have changed.

Now, I think you think that you AREN'T calculating it "all the time" but look closely at your TMR0 interrupt service routine:

' PWM calculation interrupt (Timer 0)
calcint:
' Recalculations
if flag=%1 THEN '<----Calculate only when Flag=1
' Reload timer calculation
dum=dum1*dum2
reload=Div32 freq
reload=(65535-reload)+8
' U/F calculation
if freq<=500 then
amp=(freq*131)+35
ELSE
AMP=65535
ENDIF
flag=%0
ENDIF
' Frequency reference control by potentiometer
potsense=((potsense<<6)**maxfreq)+5
if potsense<>freq then freq=potsense:flag=%1
' Frequency limits
if freq<minfreq then freq=minfreq
if freq>maxfreq then freq=maxfreq
@ INT_RETURN
That flag=%1 is NOT part of the IF-THEN statement so flag will get set every interrupt no matter if the frequency have changed or not and therfor the calculations of the reload values will get executed every interrupt.

pxidr84
- 13th May 2011, 17:24
Thanks for your help.

So I've tried first to set a fixed reload value (64594) without calculations (to obtain 120Hz sine, the max. frequency). The sine wave was perfectly stable on the oscilloscope.

This :

dum=dum1*dum2
reload=DIV32 freq
reload=(65535-reload)+8

was removed and replaced with :


reload=64594

I've also applied Henrik tips, like this :


' Frequency reference control by potentiometer
potsense=(potsense<<6)**maxfreq
IF potsense<>freq THEN
freq=potsense
flag=%1
endif

And set the reload calculation routine into the main loop.

Results : it's more stable, but the glitches still appears when I change the frequency of the sine (so the reload calculation routine is executed during this time).

I'm pretty sure that the problem is caused by DIV32. However this routine is needed :


dum=dum1*dum2
reload=DIV32 freq
reload=(65535-reload)+8

How to replace the "DIV32" by an another thing? It is possible to use that : http://melabs.com/resources/fp.htm ? Or anything else?

HenrikOlsson
- 13th May 2011, 18:08
Hi,
It might be the DIV32, we'll have to get Darrel to answer that or have someone dig into the ASM code and figure out if the PBP registers used by DIV32 are being saved by DT-Ints or not (I still think they are).

My guess is that it isn't actually DIV32 that is causing it but something else with the way the frequency is changed. Why not try constantly ramping the frequency up and down in your main loop? Don't use DIV32, just "sweep" the reload value up and down between your MIN and MAX values. Does it still glitch? If so we know that it's not DIV32 that's causing it but something else.

If it turns out that it IS DIV32 then we can look at other solutions. The DIV32 thing was in response to your request of directly calculating the reload value based on the desired output frequency scaled in Hz. Simply rescaling to some arbitrary "unit" might allow the calculations to be done without DIV32 but it comes with a couple of commitments which I won't go into until we know that that DIV32 is the problem.

Can you explain to me where the values 34696 and 32 comes from?

/Henrik.

ScaleRobotics
- 13th May 2011, 18:20
If for some reason you can't make it a one time calculation, you could use N-Bit math.

http://www.picbasic.co.uk/forum/showthread.php?t=12433 This basically uses assembly routines, so it shouldn't bother the interrupts.

pxidr84
- 13th May 2011, 20:09
Hi,
It might be the DIV32, we'll have to get Darrel to answer that or have someone dig into the ASM code and figure out if the PBP registers used by DIV32 are being saved by DT-Ints or not (I still think they are).

My guess is that it isn't actually DIV32 that is causing it but something else with the way the frequency is changed. Why not try constantly ramping the frequency up and down in your main loop? Don't use DIV32, just "sweep" the reload value up and down between your MIN and MAX values. Does it still glitch? If so we know that it's not DIV32 that's causing it but something else.

If it turns out that it IS DIV32 then we can look at other solutions. The DIV32 thing was in response to your request of directly calculating the reload value based on the desired output frequency scaled in Hz. Simply rescaling to some arbitrary "unit" might allow the calculations to be done without DIV32 but it comes with a couple of commitments which I won't go into until we know that that DIV32 is the problem.

Can you explain to me where the values 34696 and 32 comes from?

/Henrik.

I've done like you said a simple ramp in the main loop :


reload=reload-1
if reload<40000 then reload=64600

The sine frequency ramped down, and after many cycles, no glitch appeared on the oscilloscope. During this time I've played with the potentiometer to vary the amplitude and also I navigate into the inverter menu, the sine wave was not disturbed at all.

So the problem is clearly DIV32. Apparently this command disturbs DT Instant Interrupts.

I've already changed the "34696" and "32" dummy values to "1120" and "1000", it's far better to understand.

HenrikOlsson
- 13th May 2011, 22:28
So the problem is clearly DIV32. Apparently this command disturbs DT Instant Interrupts.Or the other way around, or there's a problem with the actual values being calculated like overflow/ underflow/whatever. I'm still not convinced but it seems you are so lets go with that for now.

OK, you're using an analog input to set the desired output frequency, the ADC have a resolution of 10 bits giving you a value of 0-1023. If we're going to try to come up with an alternative way to calculate the reload value (not using DIV32) can you tell us:

A) What is your minimum output frequency, ie what frequency should an ADC value of 0 give and what TMR1 reload value does that frequency require?

B) What is your maximum output frequency, ie what frequency should an ADC value of 1023 give and what TMR1 reload value does that frequency require?

/Henrik.

pxidr84
- 14th May 2011, 10:34
Maybe it isn't DIV32... I don't know, but when I remove it, the glitches disapppears. I've also already tried to change the dummy values.

My min. frequency is 1Hz (freq=10), reload value=about 0
and my max. frequency is 120Hz (freq=1200), reload value=about 64600

And I don't directly use the 0-1023 potentiometer value, first I convert it to the "freq" variable :


potsense=(potsense<<6)**maxfreq

"maxfreq" correspond to the maximum frequency defined by the user. Here, "maxfreq"=1200. So here "freq" variable varies between 0 and 1200. Note I'm using an additional number to have the 0.1Hz accuracy (for exemple, 50.2Hz frequency equals freq=502).

I have to said, your timer reload routine with DIV32 was pretty accurate and provides very good results. In terms of accuracy, I really had a 0.1Hz precision, both in low and high frequencies.

HenrikOlsson
- 14th May 2011, 12:35
Hi,
OK, so when Freq is 10 you want Reload to be 0 and when Freq is 1200 you want Reload to be 64600. That's 64600 over 1190 or a reload value of 54.286 per unit Freq.

Reload = (Freq */13897) - 542
When Freq is 10 you'll get (10*13897/256)-542 = 0
When Freq is 1200 you'll get (1200*13897/256)-542 = 64600
Do not allow Freq to be less than 10, you'll end up with a negative value.

This is not perfect but it might work, at least you'll get rid of DIV32. If you use the exact reload values for Freq=10 and Freq=1200 instead of the aproximate ones it might get a bit better in between the two extreme ends.

By the way, the lowest possible interrupt frequency, when running at 40Mhz and without changing the prescaler is 152.588Hz which equals an output frequency of 1.69Hz so Freq should really only be allowed to range from 17 to 1200 for 1.7 to 120Hz.

/Henrik.

pxidr84
- 14th May 2011, 16:28
Unfortunately, it doesn't work as expected but there is no glitch when I played with the potentiometer.

For example, at 100.0Hz I obtain an 9.5Hz output,
at 110.0Hz -> 17.6 Hz
at 90.0Hz -> 6.5Hz...

And there is no way to use this? http://www.picbasic.co.uk/forum/showthread.php?t=12433

or this?
http://melabs.com/resources/fp.htm

(just a suggestion)

HenrikOlsson
- 14th May 2011, 18:05
Hi,
Aaargh, sorry about that, temporary brain-fart on my behalf.

At the moment I'm afraid I can't come up with an alternative solution. I'm sure there is one though and hopefully someone else will jump in.

I've never used the floating point routines and I don't really see any reason to do it here. N-Bit math might be an option but I currently don't have the time to do the experiments for you so you're going to have to try it yourself. Darrels examples in the thread is pretty self explainatory.

pxidr84
- 14th May 2011, 18:13
To avoid an useless calculation, I would store the 1120*1000 result (1120000) into a 24-bit variable with the N-bit math function (but how?). And then divide this by the frequency, and so on.

Thanks anyway. Now I know where is my problem. I think I will open a new thread, because the DIV32 problem is different than PWM and sines.

HenrikOlsson
- 15th May 2011, 09:56
Hi,
I gave it a shot anyway:


PRECISION CON 3 SYSTEM ' 24bits is sufficent for this
INCLUDE "N-bit_Math.pbp" ' Include the math routines.

Dummy1 VAR BYTE[PRECISION] ' Used by N-Bit math
Dummy2 VAR BYTE[PRECISION] ' Used by N-Bit math
Result VAR BYTE[PRECISION] ' Used by N-Bit math

Frequency VAR WORD ' Desired output frequency times 10 (123=12.3Hz)
Reload VAR Word ' Caluclated timer reload value to get desired interrupt rate.
i VAR BYTE ' General purpose counter

HSEROUT ["Start",13]

Main:
For i = 0 to 20
Lookup2 i, [18, 20, 22, 25, 30, 40, 50, 60, 80, 100, 150, 200, 300, 400, 500, 700, 800, 900, 1000, 1100, 1200], Frequency
Gosub CalculateIt
Gosub PrintIt
Pause 100
Next
END

CalculateIt:
' There's 90 "steps" for a complete sine-cycle. If we want 1Hz output frequency
' we need an interrupt frequency of 90Hz, 1/90=0.01111111111111s.
' At 40MHz each instruction cycle (and timertick when prescaler is 1:1) is 100nS
' so 0.0111111111111/100E-9 = 111111 but because we want one decimal place on the
' frequency variable (123 = 12.3Hz) we multiply that by 10 so 1111111.

' Load 1111111 into first 24bit math variable.
Dummy1[2] = $10
Dummy1[1] = $F4
Dummy1[0] = $47

'Load frequency variable into second math variable
Dummy2[2] = 0
Dummy2[1] = Frequency.HighByte
Dummy2[0] = Frequency.LowByte

'Perform the division.
@ MATH_DIV _Dummy1, _Dummy2, _Result

'Move the result to the Reload variable
@ MOVE?PW _Result, _Reload ; Truncate Result into a WORD var

'Negate the value to get the actual reload value.
Reload = -Reload

RETURN
End

PrintIt:
HSEROUT ["Frequency: ", #Frequency/10, ".", #Frequency // 10, "Hz", " Reload: ", #Reload, 13]
Return

1111111 is closer to the real deal than 1120000, upto around Freq=1100 the error is less than 0.01% while at Freq=1200 it "jumps" to 0.1% - pretty good.

Remember that you can not allow the Frequency variable to be less than 17 or you'll end up with a negative reload value which means the frequency will be WAY off. Ie you can't allow Frequency to be 10 for 1Hz output.

pxidr84
- 15th May 2011, 13:34
Henrik, you had right about the DIV32 command. It doesn't disturbs DT Instant Interrupts at all.

Look at this routine :


dum=dum1*dum2
reload=DIV32 freq
reload=(65535-reload)+8

The result of the division is stored into the "reload" variable.
But if the TMR1 interrupt occurs BEFORE the end of the calculation (before reload=(65535-test)+8)), the timer management routine located in TMR1 interrupt will take the "unfinished" reload variable. So is how the sine "jumps".

But now, with this :


dum=dum1*dum2
store=DIV32 freq
reload=(65535-store)+8

The result of the division is stored into the "store" variable, so when "reload" is calculated, the routine calculation is really finished.

I've tried this, and now, absolutely no glitch appears on the sine. So this fixed my problem.

3 days for find that. :eek:

pxidr84
- 15th May 2011, 13:36
Hi,
I gave it a shot anyway:


PRECISION CON 3 SYSTEM ' 24bits is sufficent for this
INCLUDE "N-bit_Math.pbp" ' Include the math routines.

Dummy1 VAR BYTE[PRECISION] ' Used by N-Bit math
Dummy2 VAR BYTE[PRECISION] ' Used by N-Bit math
Result VAR BYTE[PRECISION] ' Used by N-Bit math

Frequency VAR WORD ' Desired output frequency times 10 (123=12.3Hz)
Reload VAR Word ' Caluclated timer reload value to get desired interrupt rate.
i VAR BYTE ' General purpose counter

HSEROUT ["Start",13]

Main:
For i = 0 to 20
Lookup2 i, [18, 20, 22, 25, 30, 40, 50, 60, 80, 100, 150, 200, 300, 400, 500, 700, 800, 900, 1000, 1100, 1200], Frequency
Gosub CalculateIt
Gosub PrintIt
Pause 100
Next
END

CalculateIt:
' There's 90 "steps" for a complete sine-cycle. If we want 1Hz output frequency
' we need an interrupt frequency of 90Hz, 1/90=0.01111111111111s.
' At 40MHz each instruction cycle (and timertick when prescaler is 1:1) is 100nS
' so 0.0111111111111/100E-9 = 111111 but because we want one decimal place on the
' frequency variable (123 = 12.3Hz) we multiply that by 10 so 1111111.

' Load 1111111 into first 24bit math variable.
Dummy1[2] = $10
Dummy1[1] = $F4
Dummy1[0] = $47

'Load frequency variable into second math variable
Dummy2[2] = 0
Dummy2[1] = Frequency.HighByte
Dummy2[0] = Frequency.LowByte

'Perform the division.
@ MATH_DIV _Dummy1, _Dummy2, _Result

'Move the result to the Reload variable
@ MOVE?PW _Result, _Reload ; Truncate Result into a WORD var

'Negate the value to get the actual reload value.
Reload = -Reload

RETURN
End

PrintIt:
HSEROUT ["Frequency: ", #Frequency/10, ".", #Frequency // 10, "Hz", " Reload: ", #Reload, 13]
Return

1111111 is closer to the real deal than 1120000, upto around Freq=1100 the error is less than 0.01% while at Freq=1200 it "jumps" to 0.1% - pretty good.

Remember that you can not allow the Frequency variable to be less than 17 or you'll end up with a negative reload value which means the frequency will be WAY off. Ie you can't allow Frequency to be 10 for 1Hz output.

Thanks, maybe I will later modify my program with this.

HenrikOlsson
- 15th May 2011, 13:53
Sometimes it's just there, staring you in the face all along and you still don't see it.... I guess this is one of those of times. It's pretty obvious now that you found it, which I'm glad you did!

Now that it works properly with DIV32 I see no reason to change to N-Bit math.

pxidr84
- 15th May 2011, 14:02
Sometimes it's just there, staring you in the face all along and you still don't see it.... I guess this is one of those of times. It's pretty obvious now that you found it, which I'm glad you did!

Now that it works properly with DIV32 I see no reason to change to N-Bit math.

Maybe your new routine with n-bit maths is more accurate?

Anyway, I think that I will open an another thread soon for this 3-phase VFD, with a YouTube video, the source code, schematics, etc. Because it works fine on my 250W 3-phase motor (in theory my IGBT module is capable of 2,2kW of output power). It will be cool. :)

HenrikOlsson
- 15th May 2011, 15:47
Hi,
I tried both methods for both accuarcy and speed, here are the results:

N-BIT: Frequency: 1.8Hz Reload: 3808 Cycles: 4497
DIV32: Frequency: 1.8Hz Reload: 3809 Cycles: 333
N-BIT: Frequency: 2.0Hz Reload: 9981 Cycles: 4593
DIV32: Frequency: 2.0Hz Reload: 9981 Cycles: 345
N-BIT: Frequency: 2.2Hz Reload: 15031 Cycles: 4579
DIV32: Frequency: 2.2Hz Reload: 15032 Cycles: 349
N-BIT: Frequency: 2.5Hz Reload: 21092 Cycles: 4743
DIV32: Frequency: 2.5Hz Reload: 21092 Cycles: 337
N-BIT: Frequency: 3.0Hz Reload: 28499 Cycles: 4583
DIV32: Frequency: 3.0Hz Reload: 28500 Cycles: 349
N-BIT: Frequency: 4.0Hz Reload: 37759 Cycles: 4282
DIV32: Frequency: 4.0Hz Reload: 37759 Cycles: 349
N-BIT: Frequency: 5.0Hz Reload: 43314 Cycles: 4528
DIV32: Frequency: 5.0Hz Reload: 43314 Cycles: 337
N-BIT: Frequency: 6.0Hz Reload: 47018 Cycles: 4272
DIV32: Frequency: 6.0Hz Reload: 47018 Cycles: 349
N-BIT: Frequency: 8.0Hz Reload: 51648 Cycles: 3971
DIV32: Frequency: 8.0Hz Reload: 51648 Cycles: 353
N-BIT: Frequency: 10.0Hz Reload: 54425 Cycles: 4313
DIV32: Frequency: 10.0Hz Reload: 54425 Cycles: 337
N-BIT: Frequency: 15.0Hz Reload: 58129 Cycles: 4270
DIV32: Frequency: 15.0Hz Reload: 58129 Cycles: 333
N-BIT: Frequency: 20.0Hz Reload: 59981 Cycles: 4002
DIV32: Frequency: 20.0Hz Reload: 59981 Cycles: 341
N-BIT: Frequency: 30.0Hz Reload: 61833 Cycles: 3959
DIV32: Frequency: 30.0Hz Reload: 61833 Cycles: 337
N-BIT: Frequency: 40.0Hz Reload: 62759 Cycles: 3705
DIV32: Frequency: 40.0Hz Reload: 62759 Cycles: 345
N-BIT: Frequency: 50.0Hz Reload: 63314 Cycles: 3617
DIV32: Frequency: 50.0Hz Reload: 63314 Cycles: 349
N-BIT: Frequency: 70.0Hz Reload: 63949 Cycles: 3420
DIV32: Frequency: 70.0Hz Reload: 63949 Cycles: 349
N-BIT: Frequency: 80.0Hz Reload: 64148 Cycles: 3412
DIV32: Frequency: 80.0Hz Reload: 64148 Cycles: 349
N-BIT: Frequency: 90.0Hz Reload: 64302 Cycles: 3298
DIV32: Frequency: 90.0Hz Reload: 64302 Cycles: 353
N-BIT: Frequency: 100.0Hz Reload: 64425 Cycles: 3420
DIV32: Frequency: 100.0Hz Reload: 64425 Cycles: 349
N-BIT: Frequency: 110.0Hz Reload: 64526 Cycles: 3333
DIV32: Frequency: 110.0Hz Reload: 64526 Cycles: 345
N-BIT: Frequency: 120.0Hz Reload: 64611 Cycles: 3351
DIV32: Frequency: 120.0Hz Reload: 64611 Cycles: 345
For N-Bit I used 1111111/frequency and for DIV32 I used 11111*100 DIV32 Frequency. As you can see the results are pretty much spot on, the small difference there is likely due to 11111*100=1111100. What is surprising is that DIV32 is about 10 times faster than N-Bit in this particular case so I wouldn't change if I were you - it's just way more power than you need for this, and it comes a cost.

Yes, please do post the project.

/Henrik.

ScaleRobotics
- 15th May 2011, 16:50
Thanks Henrik! That's good to know! I knew the N-Bit for multiplication would be slow (doesn't utilize hardware multiplier), but did not think that division would be very different.

Thanks for the info!

Walter

pxidr84
- 16th May 2011, 06:20
Of course I'm keeping DIV32, and I've modified the TMR1 prescaler to 1/2, now I really have 1.0Hz at the min. frequency (and I still keep a good accuracy at high frequencies).

The project will be posted in one or two weeks.