| Naam |
Bericht |
amateurtje
|
Hallo allemaal,
Ik ben bezig met een tripmaster (meterteller) en een stopwatch in 1 pic (16F628). Er moet later nog meer bij maar dat is voor later... Ik heb de opzet van beide programmaatjes in elkaar geknutseld in Proton DS (natuurlijk met veel hulp van anderen!). Ze werken alleen beide op interrupts. Nu wil ik de beide samenvoegen maar weet niet goed hoe dit te doen. Ik loop al vast op het "On interrupt" commando. Naar welke interrupt er gegaan dient te worden.. Nu heb ik in de handleiding van PDS wel iets gelezen over een low-level interrupt maar als ik dat goed begrijp kan er dan wel eens een interrupt niet doorgaan(?). Dat de stopwatch voor gaat en de interupt van de meterteller 'iets' later ge-update wordt is niet erg maar zowel de stopwatch als de meterteller mogen natuurlijk absoluut geen interrupts missen!
Kan iemand helpen?
De teller code:
code:
Device 16F628A ;Gebruik een 16F628A type
Config WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF,HS_OSC
XTAL = 20 ;HS_OSC ;
' I/O Definition
' --------------
; 76543210
TRISA = %00111000 ; PORTA.4 input for signal
TRISB = %00000000 ; PORTB connected to LCD
' Interrupt and register definition
' ---------------------------------
'
OPTION_REG = %1111000 ; TMR0 clock source : RA4/T0CKI
; ; increment on low to high transition
; ; Prescaler assign to WDT
; ; WDT rate 1:1
;
INTCON = %10100000 ; Enable global interrupt
; ; Disable EE write interrupt
; ; Enable TMR0 overflow interrupt
Symbol APPO = 9 ; Aantal pulsen per omwenteling in diff voor snelheid, km teller tandrad
Symbol OVW = 1840 ; omtrek wiel in mm
Dim ToBeDisplay As Word ; Result of count to be send to 7 segment display
Dim OverFlowVar As Word
Dim DwordTemp As DWord
On Interrupt GoTo SetVarToBeDisplay
;DelayMS 2000 ;LCD stabilisering
ToBeDisplay = 0 ; set initial value of count
TMR0 = 0 ; reset prescaller
OverFlowVar=0
MainLoop:
While 1 = 1
ToBeDisplay=TMR0 + OverFlowVar
DwordTemp = ((ToBeDisplay/APPO)*OVW) ; change value to meters
DwordTemp=DwordTemp/1000
ToBeDisplay = DwordTemp
Print At 1,1, Dec ToBeDisplay ," " ;print the meters
Print At 2,1,Dec (TMR0 + OverFlowVar), " " ;print for test
Wend
Disable
SetVarToBeDisplay:
OverFlowVar = OverFlowVar + 256
INTCON.2 = 0
TMR0 = 0
Resume
Enable
End
De stopwatch code:
code:
Device 16F628A ;Gebruik een 16F628A type
Config WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF, HS_OSC ;INTRC_OSC_NOCLKOUT verwijderd
XTAL = 20
; Include "proton_4.inc"
Dim SubSecondCount As Byte
Dim Seconds As Byte
Dim Minutes As Byte
Dim Hours As Word
Dim TempSeconds As Byte
Dim TempSubSeconds As Byte
Dim TempMinutes As Byte
Dim TempHours As Word
'---- INTERRUPT CONTROL DIMS -------------
Dim GIE As INTCON.7
Dim TIMER1REG As TMR1L.Word
Symbol FUDGE_FACTOR = 7
Symbol TMR1_VAL =((65536)-(XTAL*2500))+FUDGE_FACTOR ' CALCULATE OSC OFFSET VALUES FOR 100HZ INTERRUPT
On_Interrupt INTERRUPT_ROUTINE ' WHERE TO GO ON AN INTERRUPT
GoTo over_subs
INTERRUPT_ROUTINE:
Clear T1CON.0 ' STOP TMR1
TIMER1REG = TIMER1REG + TMR1_VAL ' LOAD TMR1
Set T1CON.0 ' START TMR1
Inc SubSecondCount
If SubSecondCount = 100 Then
SubSecondCount = 0
Inc Seconds
If Seconds=60 Then
Seconds = 0
Inc Minutes
If Minutes = 60 Then
Minutes = 0
Inc Hours
End If
EndIf
End If
Clear PIR1.0 ' CLEAR TMR1 INTERRUPT FLAG
Context Restore
'-----------------------------------------------
' INITALISATION COMMANDS
'-----------------------------------------------
INITALISATION:
T1CON = %00000000 ' Set up Tmr1 to have 1:1 prescaler and act as a timer
PIR1.0 = 0 ' Clear Tmr1 interrupt flag
INTCON = %11000000 ' Global and Peripheral interrupts on
PIE1.0 = 1 ' Enable Tmr1 as peripheral interrupt source
T1CON.0 = 1
Clear
SubSecondCount = 100
Return
over_subs:
GoSub INITALISATION
GIE = 0
SubSecondCount = 0
Seconds = 0 ' Count down for 2 mins
Minutes=0
Hours = 0
GIE = 1
Cls
While 1 = 1
TempSeconds = Seconds ' read the seconds safely
TempSubSeconds= SubSecondCount
TempMinutes = Minutes
TempHours = Hours
Print At 1,1, DEC2 TempHours,":",DEC2 TempMinutes,":", DEC2 TempSeconds,".", DEC2 TempSubSeconds
Wend
Wat is eigenlijk anders het nut van 3 timers/counters in een pic? [Bericht gewijzigd door Henry S. op 25 augustus 2008 19:48:09]
|
JoWi
|
Als je meerdere interrupts aan hebt staan:
In de interrupt routine moet je testen wie de interrupt veroorzaakte en hem dan afhandelen. Dus met IF statements.
Als je hem wilt afhandelen in BASIC (en niet in assembly) den k dan ook aan de CONTEXT SAVE en RESTORE, anders krijg je op de meest vreemde momenten rare fouten (soms gaat het goed, soms niet).
En het nut van 3 timers:
Ik gebruik ze wel eens alle drie: eentje als DDS timer, eentje als systeemklok en de laatste voor delays (software loopjes doen het niet meer als je ook heel veel interrupts loopt af te handelen)
|
amateurtje
|
ik programmer inderdaad in Proton development suite.
Ik had inderdaad zelf vandaag ook bedacht met if.. then statements de verschillende interupts af te handelen.
Maar gaat het goed als er tijdens de ene interrupt een andere binnenkomt? handeled hij ze dan beide af?
Hoe kan ik de commando's CONTEXT SAVE en RESTORE het beste gebruiken?
Mijn interupt ziet er nu zo uit:
code:
INTERRUPT_HANDLER:
If PIR1.0= 1 Then
Clear T1CON.0 ' STOP TMR1
TIMER1REG = TIMER1REG + TMR1_VAL ' LOAD TMR1
Set T1CON.0 ' START TMR1
Inc SubSecondCount
If SubSecondCount = 100 Then
SubSecondCount = 0
Inc Seconds
If Seconds=60 Then
Seconds = 0
Inc Minutes
If Minutes = 60 Then
Minutes = 0
Inc Hours
End If
EndIf
End If
Clear PIR1.0 ' CLEAR TMR1 INTERRUPT FLAG
EndIf
If INTCON.2 = 1 Then
OverFlowVar = OverFlowVar + 256
INTCON.2 = 0 ; clear overflow flag
TMR0 = 0 ; reload TMR0
EndIf
Context Restore
|
Babylon
|
Ik heb geen tijd om je hele code te lezen, maar ik zal een korte beschrijving geven. Misschien dat je er wat aan hebt.
Als een microcontroller een interrupt ontvangt, slaat hij het adres waar hij was gebleven op in de stack. Zodra de interrupt gehandeled is, ga je met een return commando weer terug naar dat adres.
Wordt er nu een interrupt B veroorzaakt tijdens de handler van een interrupt A, dan wordt het adres waar de controller was gebleven als nieuw adres in de stack 'gepushed'. Het oude adres schuift een plekje op, maar wordt nog steeds onthouden. Bij het eerstvolgende return commando, wordt het bovenste adres uit de stack 'gepulled' en wordt de interrupt handler van A dus vervolgd. Tot het volgende return commando, dan wordt het eerste adres uit de stack gepulled.
Dit kan natuurlijk maar tot een bepaalde diepte, want op een gegeven moment is de stack vol. Echter, met 2 interrupts denk ik niet dat dat zo snel zal gebeuren. Ook kun je interrupts uitschakelen zodra je in een interrupt handler zit, zodat je geen last krijgt van interruptende interrupts. (mooie woordencombo  )
Heeft ook een Website. Nu doet ie het weer!
|
GreenMagic
|
Nog een kleine toevoeging: Bij de 628 zal bij het optreden van een interrupt het GIE bit ge-cleared worden. (En later weer ge-set) Dat betekent dat de eerstvolgende interrupt pas optreedt nadat de huidige is afgehandeld. Je kunt er voor kiezen om dit bit in je ISR weer in te schakelen, zo krijg je nested interrupts. Als het je allemaal nog wat vreemd klinkt, begin dan nog niet aan nested interrrupts.
Om dus op je vraag te antwoorden: Nee, er wordt niets gelijktijdig afgehandeld. Dat KAN ook niet, het is geen dual-core met multitasking OS. (En ook multitasking is niet gelijktijdig, maar sequentieel.) Eerst wordt de ISR afgewerkt, als er na de ISR nog een interrupt flag ge-set is, wordt gelijk weer naar de ISR gesprongen. [Bericht gewijzigd door GreenMagic op 25 augustus 2008 19:53:48]
Huidig project: Extended Fader
|
JoWi
|
Het GIE bit clearen is zeer onverstandig: Je hebt geen hardware stack pointer dus bij het saven van STATUS en WREG krijg je problemen want je hebt geen push en pop instructies. Meestal gebruik je meestal een vaste lokatie voor. GIE wordt gecleared door de retfie instructie.
Als er twee interrupts gelijktig optreden: met je rijtje IF instructies handel je ze indezelfde ISR af, en dat is wat efficienter want je hoeft maar een keer naar de ISR te springen.
Mocht er een interrupt van bron B komen tijdens de ISR terwijl je bron A afhandelt: geen probleem, na de retfie springt de processor gewoon terug naar de ISR om B af te handelen.
|
amateurtje
|
hoi, bedankt voor de reacties.
Tegelijk bedoelde ik natuurlijk niet maar gelijk achter elkaar is ook al iets. Wat ik bedoelde is dat er geen vergeten wordt ( verloren gaat).
Nu ben ik een beetje de weg kwijt. Greenmagic zegt dus dat als er een interupt afgehandeld wordt, de GIE low wordt en er dus geen andere interupt uitgevoerd wordt of zelfs maar opgeslagen wordt en dat dus bovenaan de interuptafhandeling GIE=1 geplaatst dient te worden om zodoende de tweede interupt TUSSENDOOR de eerste interupt af te handelen.
Babylon en Jowi zeggen dat dmv de stack na de eerste afhandeling de tweede afhandeling volgt.
DUS als ik het goed begrijp: met de GIE=1 worden de interrupts nested maar anders (tot een limit die ik waarschijnlijk niet bereik) worden de interupts direct achter elkaar uitgevoerd.
Klopt dit?
Klopt het volgende ook?
Nu is de ene afhandeling een stopwatch en de andere een meter teller. beide zijn niet echt super nauwkeurig. de interupts lopen wel door en daardoor komt dus alleen de scherm weergave hooguit een "kleine tel" later maar de doortelling blijft nauwkeurig indien de interrupts na elkaar worden afgehandeld.
PS, ik heb hier ook nog een 16F648 liggen. Qua hoeveelheid code heb ik deze waarschiijnlijk niet nodig. Is er nog een andere reden waarvoor ik deze of een andere pic zou moeten gebruiken? [Bericht gewijzigd door amateurtje op 25 augustus 2008 23:47:17]
|
JoWi
|
quote: Op 25 augustus 2008 23:45:10 schreef amateurtje:
DUS als ik het goed begrijp: met de GIE=1 worden de interrupts nested maar anders (tot een limit die ik waarschijnlijk niet bereik) worden de interupts direct achter elkaar uitgevoerd.
Klopt dit?
Nee, als je GIE zet aan het begin van je interrupt routine heb je je PIC opgehangen, dus hij doet niks meer (totdat je hem reset)
Het simpele antwoord: Je moet gewoon van GIE afblijven in de interrupt routine en interrupt routines zo kort mogelijk houden.
|
Frits Kieftenbelt
Honourable member
|
Bij gebruik van een interrupt om een PIC uit zijn low power toestand (slaap) te halen, zonder gebruik van een interrupt afhandeling moet je GIE op 0 zetten (laten).
Zie www.picbasic.nl/interrupt_on_change.htm
Ohm sweet Ohm l www.picbasic.nl
|
Babylon
|
Inderdaad, als je meerdere interrupts kan verwachten, moet ze interrupts kort houden. Het berekenen van het aantal secondes en minuten vanuit het aantal honderdsten van secondes moet je dus niet in de interrupt doen. Ik ben geen kenner van PICs maar volgens mij doe je dat nu wel in je interrupt, of niet?
code: Inc SubSecondCount
If SubSecondCount = 100 Then
SubSecondCount = 0
Inc Seconds
If Seconds=60 Then
Seconds = 0
Inc Minutes
If Minutes = 60 Then
Minutes = 0
Inc Hours
End If
EndIf
End If
Je kan in de interrupt af met het ophogen van het aantal "subseconden" en eventueel het zetten van een bitje dat zegt 'er is een extra subseconde'. In je main loop kan je deze dan checken en als deze er is, kan je daar ongestoord gaan rekenen.
Als je sleep mode gebruikt is bovenstaande wat lastiger te doen maar je snapt het idee. [Bericht gewijzigd door Babylon op 26 augustus 2008 10:34:22]
Heeft ook een Website. Nu doet ie het weer!
|
amateurtje
|
hoi allemaal.
Het lijkt te werken maar ik weet niet of er routines worden overgeslagen. Voor de zekerheid: als er een interrupt dus tijdens een andere interrupt komt wordt hij naderhand (wanneer interrupt 1 klaar is) afgehandeld. toch?
Gaat er dan nog iets fout met mijn secondenpulsen (behalve dan een aantal ms van de laatste afhandeling waarmee ik kan leven?)
Ik heb de interrupt tot een minimum beperkt en in de hoofdlus de seconden enz laten zetten.
@Frits.
Ik haal hem toch helemaal niet uit sleepmode?
Mijn code ziet er nu zo uit:
code:
DIM TIMER1REG AS TMR1L.WORD
SYMBOL FUDGE_FACTOR = 7
SYMBOL TMR1_VAL =((65536)-(XTAL*2500))+FUDGE_FACTOR ' CALCULATE OSC OFFSET VALUES FOR 100HZ INTERRUPT
INTERRUPT_ROUTINE:
;Stopwatch
IF PIR1.0= 1 THEN
CLEAR T1CON.0 ' STOP TMR1
TIMER1REG = TIMER1REG + TMR1_VAL ' LOAD TMR1
SET T1CON.0 ' START TMR1
INC Tijdophoging
CLEAR PIR1.0 ' CLEAR TMR1 INTERRUPT FLAG
ENDIF
;Tripmaster
IF INTCON.2 = 1 THEN
OverFlowVar = OverFlowVar + 256
INTCON.2 = 0 ; clear overflow flag
TMR0 = 0 ; reload TMR0
;
ENDIF
CONTEXT RESTORE
Het eerste gedeelte is de stopwatch en het tweede gedeelte is de overflow van de meterteller.
Voor de stopwatch wordt er iedere keer een puls hoog gezet die ik afhandel. Deze stopwatch loopt nu ook eigenlijk te snel omdat ik een resolutie van tienden van seconden of zelfs seconden genoeg vindt. Ik moet zeggen dat ik deze code nietzelf heb gemaakt en ik hem net niet doorheb hoe ik hem om kan zetten naar tienden van seconden(iets met de TMR1_VAL).
Voor de meterteller tel ik in de code de tmr0+overflowvar samen en die pulsen bereken ik naar meters. deze interrupt treed dus maar 1 keer op de ongeveer 40 meter op maar ik wil dan toch geen tijdsmeting missen....
DUS: grote vraag: VERLIES ik tijd of meetpulsen? [Bericht gewijzigd door amateurtje op 26 augustus 2008 20:07:18]
|
amateurtje
|
Hoi Allemaal
Nu gebruik ik de bovenstaande routine voor een interval van 0,01 seconde. Dit heb ik niet nodig en ik denk dat het redelijk wat processortijd/vertraging neemt. Weet iemand een goede routine met 0,1 seconde interval? Ik werk met een 20 Mhz kristal maar dit zou eventueel gewijzigd kunnen worden. Als ik het goed begrijp werkt een 32,xx kristal aleen maar handig voor een routine met een interval van een complete seconde.
|
Arco
|
32786 kristal deelt niet zo mooi voor 0.1 sec. Simpelste is een 2MHz kristal gebruiken, is een standaardwaarde.
(1 MHz kan ook, maar die zie je niet zo vaak)
T.o.v. 20MHz daalt dan ook je stroomverbruik van 2.5 naar 0.25mA, mooi meegenomen bij batterijgebruik.
Arco
|