EEPROM lezen/schrijven

hadv

Golden Member

Ik kan zo gauw de datasheet van dit device niet vinden, maar in de regel is het zo dat, in ieder geval bij 16F, dat je, om een timer1 interrupt te krijgen, je ook het PEIE-bit van INTCON (bit6) op 1 moet zetten.

@Daan: het aanzetten van het GIE bit kan inderdaad beter vlak voor de eerste code.
Het feit dat de compiler alleen 'GIE' niet herkent is omdat dit geen SFR is, maar een bit in een SFR. Voor dit device is geen bitnamefile gemaakt, dus het klikken op het device in de code explorer levert niets op. Je kunt zelf een bitnamefile maken, ik heb daar een tool voor gebouwd, te vinden op de Crownhill site. Daar kun je ook de fuse configurator van JohnGB vinden. Dit is zo ongeveer de handigste plugin die ik ken.

Overigens is de opmerking van Arco over autosaving op zijn plek, want het gebeurt nu twee keer: één keer door de PIC en één keer in de code. Of het op zich veel sneller is dan de software save/restore denk ik dat dat wel meevalt. Uiteindelijk gebruikt autosaving ook hulpvariabelen.
Of je echt interrupts kwijtraakt met aan/uitzetten durf ik niet te zeggen.

Just find out what you like and let it kill you

De autosave/restore feature is geheel hardwarematig en kost geen extra clockcycles.
Bijv. RETFIE doet meteen een restore en gebruikt toch maar 2 cycles. (zelfde als bij 'oude' PIC16's zonder auto-restore)
Geeft dus een enorme winst aan cycletijd... (de stack wordt hier niet voor gebruikt, maar een aparte geheugenpage waar de boel wordt gekopieerd)

Arco - "Simplicity is a prerequisite for reliability" - hard en software ontwikkeling: www.arcovox.com

Ik weet niet hoe groot je stack is maarbij de oudere pics weet ik dat die relatief klein is. Een "hserout" kan zomaar tig functies weer aanroepen waardoor je je stack "opblaast".

Verder over timing die je "correct" wilt houden: Dat is geen excuus om alles in de interrupt handler te prakken.

Wat je in zo'n geval beter kunt doen is de waarde + timer clock SAMEN opslaan in een buffer. In de main loop reken je dan alles uit.

Hoe je precies aan de tijd in je ISR komt is even een ander verhaal, je kunt een hardware timer maken die je laat tellen op een bepaalde clock rate en die zou je kunnen lezen in de ISR. Hoe je dat in picbasic moet doen weet ik effe niet. Zule je zelf moeten maken.

Dus de ISR hoeft alleen dit te doen:

code:


Int-hander 
   lees waarde
   lees timertick
   store beide in fifo of ringbuffer oid
   incrementeer een "werk todo counter"
ret from isr

In de main loop ga je wachten op de "werk todo counter" of die > 0 wordt en dan reken je alles uit wat je moet doen etc. dan decrementeer je die werk counter en wacht je weer.
Kijk/zoek even naar de uitleg/werking van "counting semaphores". Zoiets zul je moeten maken in picbasic (of misschien heeft die daar wat voor).

Henri's Law 1: De wet van behoud van ellende. Law 2: Ellende komt nooit alleen.

@ Arco: Ah ja het zou kunnen dat ik dat hi/low gezien heb in een 18F. Ik gebruik onderandere ook de 18F4520. Ik gooi de interupt uit om te voorkomen dat tijdens het afwerken van de interupt er nog een interupt komt. Omdat dat zooi geeft. Ik heb er inderdaad niet bij stil gestaan dat eigenlijk dus betekend dat ik potentieel interupts mis. Ik had bedacht dat het dus gewoon een kwestie is van de Interupthandler kort genoeg houden... maar dat heeft eigenlijk het zelfde effect als de interupt gewoon aan laten staan. Betekend dat dan ook dat ik de flag en preset helemaal in het begin van de interupthandler moet doen?

Haha ja met 50000 keer per seconde tikt dat aan. Ik zit op 10 keer per seconde. Dus dat valt nogal mee. Maar ik snap wat je bedoeld. Het is intussen ook uit de code ;)

@hadv: Hm, dat is vreemd die set ik namelijk niet (ook niet elders) en de interupt komt wel gewoon... in de datasheet staat het volgende:
bit 6 PEIE: Peripheral Interrupt Enable bit
1 = Enables all active peripheral interrup

Peripheral vertaal ik grofweg naar omgeving. Ik nam aan dat dat betekende interupts van IO, of iets dergelijks, zegmaar de "externe" interupts... Wat bedoel je met een SFR? Ik begrijp uit de rest van je verhaal dat er voor (sommige) chips een appart bestand is waar dingen als GIE gedefinieerd wordt en verwezen wordt naar het eigenlijke adres?
Wat betreft autosaving en interupts kwijtraken, zie het verhaal hierboven aan arco ;).

@henri62: De stack die ik zelf maak valt opzich wel mee. 1, hooguit 2, niveau's 2,3 als ik de interupt mee tel. Dus dat is niet zo spannend, ik heb wel heel veel meer gedaan (denk 6,7) met daar nog een hserout in. Al denk ik wel dat dat in een 18F was. Maar als het toch een stackoverflow is reset de hele chip... dan zou ik verwachten dat alle code (dus ook de hserout met de initialisatie) opnieuw uitgevoerd wordt, niet alleen de eread.
De berekeningen heb ik uit de interupt gehaald. het enige is nu dat servo verhaal. Dat kan ik inderdaad ook met een bitje in de interupt aangeven en dan in de hoofdlus uitvoeren. Maar ALS die situatie optreed wil ik dat de stroom vrij vlot uitgeschakeld wordt. En met de serin in de hoofdlus kan dat rustig een seconde zijn voor de hoofdlus. En wat ik zei, als die code aangeroepen wordt intereseert timing me geen fluit meer. Als die accu maar los gaat.
Ik moet eerlijk zeggen dat ik de rest van je verhaal niet helemaal volg. Begrijp ik goed dat ik eigenlijk zeg "op die en die tijd was de stroom zoveel" zodat ik dus ook met de tijd kan rekenen?

De ISR (neem even aan dat dat de interupthandler is ;)?) doet nu eigenlijk het volgende:
Seconde teller verhogen (equivalent van bitje hoog zetten wat een functie in de hoofdlus aftrapt)
analoge signalen binnen halen
de ruwe waarde daarvan vergelijken met een getal (Dus alleen maar twee dword's met elkaar vergelijken, geen lopend gemiddelde meer, geen omrekeninen naar floats).
Preset goed zetten
flag resetten
terug naar hoofdlus

Ik zal eens gaan zoeken naar counting semaphores. Nooit van gehoord maar ik ga even googlen ;).

Voor iedereen die de datashit niet kon vinden, bij deze ;)
http://ww1.microchip.com/downloads/en/devicedoc/40001574c.pdf

EDIT: Ik ben intussen nog stukje voor stukje aan het kopieren naar een project met wat eeprom lezen/schrijven. Kijken wanneer het er mee ophoudt. Ik heb de context save/restore er uit gehaald. En onderaan vervangen door een "return" maar dan voert hij de interupt maar 1 keer uit. Hij komt wel terug in de hoofdlus. Maar de interupt komt maar 1 keer. Weet je zeker dat die er niet in hoeven. Ik heb in de datasheet even gezocht en daar staat wel dat het automatisch gebeurt (saven en restoren). Moet ik soms iets anders dan return er onder zetten..?

PS: stack is 16 levels. Dus ik kom nieteens in de buurt (levels die de hserout bijv gebruikt even niet meegerekend)

Op 13 november 2018 21:50:28 schreef DaanSteeman:

Peripheral vertaal ik grofweg naar omgeving.

Niet alles gelezen, maar... Peripheral in deze context is eerder iets als "randapparaat". In een PIC is het dan een "ingebouwd randapparaat". Dus denk aan een "uart module".

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
hadv

Golden Member

Gebruik Retfie ipv Return. Retfie is speciaal voor interrupts. In tegenstelling tot een return, die is voor subroutines.

SFR: Special Function Register. Dit zijn geheugenplaatsen die zijn toegewezen aan specifieke taken zoals bijvoorbeeld het instellen van een Timer, UART of ADC en natuurlijk INTCON, PIE en PIR.

Peripheral is 'normaal' een randapparaat, maar binnen een PIC is het in grote lijnen alles dat niet mbv het INTCON register kan worden afgehandeld. Dat kan van alles zijn, ook Timer1.

Dat de interrupt wordt doorlopen is op zich vreemd, maar je kunt er wat aan doen:
na het declareren van je variabelen neem je een regel op met 'Goto Init'.
na deze regel voeg je de interrupthandler toe
en daarna dus een regel 'Init:'

Just find out what you like and let it kill you

Oke voor de tweede keer dat ik dit schrijf... op "nee ik wil niet de rechter balk verwijderen bij een resolutie kleiner dan whatever" klikken zorgt er voor dat je text ook meteen verdwijnt...

Peripheral betekend dus in dit geval een andere module in de chip die niet de CPU is eigenlijk. Duidelijk.

Retfie heb ik geprobeerd. Dan blijft hij rond fietsen in de interupthandler. Hij springt niet meer naar de hoofdlus. Momenteel staat er dus nog context save/restore.

Dat de interrupt wordt doorlopen is op zich vreemd, maar je kunt er wat aan doen:
na het declareren van je variabelen neem je een regel op met 'Goto Init'.
na deze regel voeg je de interrupthandler toe
en daarna dus een regel 'Init:'

Het over subroutines springen met goto ken ik wel. Het probleem was ook niet dat de routine doorlopen werd bij het opstarten. Hij werd maar 1 keer afgetrapt. Alsof ik na die ene keer de interupt uit zette (wat uiteraard niet zo was).

Dat gezegd hebbende. HET WERKT!?!? Ik snap alleen niet waarom. Ik heb de twee versies naast elkaar gelegd. Het enige verschil dat ik kan vinden is (afgezien van wat onbelangrijke dingen) dat ik bij de huidige versie met edata de eepromadressen die ik gebruik na het programeren op 0,000 zet. Ipv gewoon FF te laten tot ik er de eerste keer in schrijf. Maar volgensmij zou dat geen bal uit moeten maken. De eerste keer leest hij dan volkomen onzin uit het eeprom. Maar dat wordt na 1 keer meten gewoon overschreven in het normale programma en na 5 min in het eeprom geschreven voor de volgende keer.

Hoe dan ook, ik heb de interupthanlder helemaal uitgekleed. Het is nu eigenlijk alleen nog een counting semaphore (ja ik heb zitten googlen ;)). Die in de hoofdlus het rekenwerk aftrapt. Nu heb ik alleen weer het probleem dat mijn teller niet op 9 komt, het rekenwerk aftrapt en dan weer op 0 gezet wordt. Nu loopt de teller tot 23 op waardoor mijn seconde bijna 2,5 seconde duurt en er dus van mijn berekening in Wh niks klopt.... Heeft iemand enig idee hoe ik dat dan een beetje netjes op kan lossen (dus zonder het rekenwerk gewoon maar weer in de interupthandler te kwakken)?

Op 11 november 2018 23:07:19 schreef DaanSteeman:

pic basic code:


Device = 16F1938

Config1 FOSC_HS, WDTE_OFF, PWRTE_OFF, MCLRE_OFF, CP_OFF, CPD_OFF, BOREN_OFF, CLKOUTEN_OFF, IESO_OFF, FCMEN_OFF
Config2 WRT_OFF, VCAPEN_OFF, PLLEN_OFF, STVREN_OFF, BORV_19, LVP_OFF

All_Digital false             ;Alle ingangen digitaal
Xtal = 20
Declare Hserial_Baud = 38400                                        ; Set baud rate to 38400  
Declare Hserial_RCSTA = %10010000                                     ; Enable serial port and continuous receive
Declare Hserial_TXSTA = %00100000                                     ; Enable transmit and asynchronous mode 
Declare Hserial_Clear = On                                            ; Optionally clear the buffer before receiving
Declare Adin_Res 10                                                   ; an/dig converter resolutie van 10 bits
Declare Adin_Stime 500                                                 ; sampletime van 1000us

            
OPTION_REG.7 = 0 
       ;76543210
ADCON1 = %10000000
ANSELA = %00001111                                                    ; van A0, A1, A2 ANologe ingang maken
ANSELB = %00100000                                                    ; Poortb uitgangen digitale uitgangen maken
INTCON =%01100010 
T1CON = %00110101                                         
PIE1 =  %00000001
T1GCON =%00000000
T2CON.2 = 1
APFCON = %01000000                                                    ; CCP3 op pin b.5 zetten
CCPTMRS0 = %00000000    ; timer 2 word gebruikt voor alle ccpmodules
CCPTMRS1 = %00000000    ; timer 2 word gebruikt voor alle ccpmodules
PR2 = %11111011         ; timer2 instellen op ongeveer 2000Hz
CCP1CON = %00001100     ;alleen PxA word gemoduleerd. de overige worden als portpins geset
CCP2CON = %00001100     ;alleen PxA word gemoduleerd. de overige worden als portpins geset
CCP5CON = %00001100     ;alleen PxA word gemoduleerd. de overige worden als portpins geset

On_Hardware_Interrupt GoTo Interrupthandler 

Symbol EElaagstesom = 0;1,2,3
Symbol EEhoogstesom = 4;5,6,7
Symbol EEsom        = 8;9,10,11
Symbol EESOC        = 12;13,14,15

Dim Som As Float           ; elke seconde word hierbij de capaciteit in Wh bij opgeteld/afgetrokken om het netto van in en uitgaande stroom te krijgen 
Dim Hoogstesom As Float    ; de hoogste waarde die "SOM" ooit gehad heeft
Dim Laagstesom As Float    ; de laagste waarde die "SOM" ooit gehad heeft
Dim SOC As Float           ; actuele state of charge in procenten

Clear

HSerOut ["INITIALISATIE start", $0D] 


Som = ERead EEsom
Hoogstesom = ERead EEhoogstesom
Laagstesom = ERead EElaagstesom
SOC = ERead EESOC 

; Overige initialisatie van overige componenten en berekeningen

INTCON.7 = 1 ;alle intterupts inschakelen

Hoofdprogramma:

;een hele berg aan berekeningen voor dingen als cellen balancen, actuele stroom en spanningen bepalen

SerIn BUSRX, 84, 1000, Timeout1, [Wait("SIG"),Str RXbuffer];wachten op de vraag van de master voor gegevens

Verder:     

Select RXbuffer[0]
  Case 1 
    Output BUSTX 
    SerOut BUSTX, I9600, ["SIG",Dec3 Vcell1, ",", Dec3 Vcell2, ",", Dec3 Vcell3, ",", Dec3 ActueleStroom, $0D, "xxxxxxxxx"]  ; die gegevens terug sturen
    Input BUSTX             
EndSelect

GoTo Hoofdprogramma
End

Timeout1:

GoTo Hoofdprogramma

Interrupthandler:

Context Save
INC Secondeteller
INTCON.7 = 0
;metingen verrichten
;bepalen (zonder al te veel berekeningen) of alle waardes (voornamelijk de celvoltages) binnen de veilige marges liggen

If Secondeteller > 10 Then
  ; actuele State Of Charge berekenen
  Som = Som + (ActueleStroom * Vtot/3600)
  If Som > Hoogstesom Then Hoogstesom = Som
  If Som < Laagstesom Then Laagstesom = Som
  SOC = ((Som - Laagstesom) / (Hoogstesom - Laagstesom))*100 
  If Vtot > 12.3 And ActueleStroom < 0.5 Then Hoogstesom = Som
  If Vtot < 9 Then Laagstesom = Som
  HSerOut [Dec5 Som, $09, Dec5 Laagstesom, $09, Dec5 Hoogstesom, $09, Dec5 SOC, $0D]
; Je zou zoiets kunnen maken. Als secondeteller te hoog is, bijv 23 dan wordt
; bovenstaande berekening gewoon 2x achter elkaar uitgevoerd en de 3 wordt
; bewaard totdat er weer (meer dan) 0,7 sec verstreken zijn
;  Secondeteller = 0                    ; was
  Secondeteller = Secondeteller - 10   ; moet worden
  Inc Vijfminteller
EndIf

If Vijfminteller > 300 Then
  If Hoogstesom != ERead EEhoogstesom Then EWrite EEhoogstesom, [Hoogstesom]
  If Laagstesom != ERead EElaagstesom Then EWrite EElaagstesom, [Laagstesom]
  If Som != ERead EEsom Then EWrite EEsom, [Som]
  If SOC != ERead EESOC Then EWrite EESOC, [SOC]
;  Vijfminteller = 0                   ; zie commentaar boven, 
  Vijfminteller = Vijfminteller - 300
; Ik denk toch dat bovenstaande niet hoeft. Het heeft weinig zin om een
; aantal keren strak achter elkaar dezelfde Hoogstesom weg te schrijven in
; EEhoogstesom. De 5-minutencoordinatie klopt niet helemaal. Het is niet
; meer precies 5 minuten
EndIf

  INTCON.7 = 1
Context Restore

End


Bezoek mijn neefjes' site: www.tinuselectronics.nl

Ik weet niet hoe die ERead en EWrite functies werken, maar vaak keert dat soort functies na uitvoer direct terug, zonder te wachten tot read/write compleet is.
Je moet dan tussen writes en reads een korte delay doen van 5mS ofzo, anders gaat het geheid mis. (de ene is nog bezig als je de andere aanroept)

Arco - "Simplicity is a prerequisite for reliability" - hard en software ontwikkeling: www.arcovox.com

@Ohm pi: JUIST.... die tip had ik even nodig. Dat lijkt me inderdaad een prima oplossing. Ik tel gewoon hoe vaak de interupt gebeurt is. Als dat 5 keer is gebeurt is er dus 0,5seconde voorbij sinds de vorige keer. Als dat 200 keer is is er dus 20 seconde voorbij sinds de vorige keer. Dan klopt de Wh weer, en ik tel simpelweg dat aantal secondes op bij de vijfminteller zodat die ook weer klopt.

Ik snap alleen even niet wat je bedoeld met

; Ik denk toch dat bovenstaande niet hoeft. Het heeft weinig zin om een
; aantal keren strak achter elkaar dezelfde Hoogstesom weg te schrijven in
; EEhoogstesom. De 5-minutencoordinatie klopt niet helemaal. Het is niet
; meer precies 5 minuten

Het was inderdaad geen 5 minuten. Met de bovenstaande aanpasseningen zou dat wel weer moeten kloppen (hoewel die 5 minuten niet kritisch is. Het betekend alleen maar dat wanneer de spanning uit gegooit wordt ik iets meer of minder tijd aan gegevens kwijt ben. Maar 5 minuten op periodes van weken dat dit ding aan staat is totaal insignificant. Dus dat is geen punt.
Maar ik schrijf uberhaupt nooit de zelfde hoogstesom weg. Als die veranderd is (wat tijdens het leren van de capaciteit continue is, maar daarna alleen maar gebeurt als de accu helemaal vol is, of helemaal leeg is) schrijf ik hem naar het eeprom. Zo niet schijf ik ook niks om het eeprom een beetje te sparen.

@Arco: dat zou ik eerlijk gezegd niet weten. In de manual staat dat ewrite tot wel 5mS kan duren per byte. Dus dat is inderdaad behoorlijk lang. Maar ik zou eens moeten uitproberen wat er gebeurt als ik de delay er tussen gooi... Eread zou in elk geval een stuk sneller zijn.

Als je na een write meteen een read doet is de eeprom nog niet klaar daarmee. Je krijgt random 'rommel' te zien.

Arco - "Simplicity is a prerequisite for reliability" - hard en software ontwikkeling: www.arcovox.com

Op 17 november 2018 10:37:10 schreef DaanSteeman:
..
Ik snap alleen even niet wat je bedoeld met [...]
..

Originele 5-minutencode is goed. Daar hoef je de truc om 300 er van af te trekken niet toepassen. Foutje van mij.

Bezoek mijn neefjes' site: www.tinuselectronics.nl

Op 17 november 2018 12:14:39 schreef Arco:
Als je na een write meteen een read doet is de eeprom nog niet klaar daarmee. Je krijgt random 'rommel' te zien.

Dank voor de reminder dat ik NOOIT meer met PICs te maken wil hebben. Iedere zinnige CPU stopt dan totdat de write klaar is en geeft de verwachte resultaten. de handleiding zegt dan: Je moet niet van de eeprom/flash lezen als je niet wilt dat de boel ineens stopt.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

De hele CPU stoppen tot een write compleet is, is onzinnig en onwerkbaar... (in meeste applicaties kun je de boel niet zo lang bevriezen)
Het wordt aan het gezonde verstand van de programmeur overgelaten om geen read/writes te doen zolang de vorige niet klaar is.
(je kunt trouwens simpel testen of de write compleet is)

Arco - "Simplicity is a prerequisite for reliability" - hard en software ontwikkeling: www.arcovox.com
hadv

Golden Member

Misschien is het handig om de datasheet eens door te lezen (waar Arco wellicht ook al op hint). Daarin staat beschreven hoe je op het schrijven van de eeprom moet wachten.
Dan kom je er ook achter dat het echt niet zo erg is als rew zegt en dat je dus niet tot zulk soort over the top conclusies komt.

Uit het PicBasic manual: writing can take op to 10ms per byte.

Als je een .lst van de basic compiler bekijkt zie je ook dat deze keurig gebruikt maakt van de daartoe aangewezen registers en loopt tot de verschillende bits de juiste waarde hebben.

Conclusie: in PicBasic kun je gewoon een EWrite doen zonder zelf allerlei delay acties uit te voeren.

Just find out what you like and let it kill you

In Mikrobasic wat ik gebruik keert de routine meteen terug na een read/write; wachten moet je zelf doen.
Ook wel prettig: kun je ondertussen wat anders doen... (10mS is een eeuwigheid voor een microcontroller... :) )

Erg vaak gebruik ik het niet meer, want de meeste nieuwe pics hebben geen eeprom meer...

Arco - "Simplicity is a prerequisite for reliability" - hard en software ontwikkeling: www.arcovox.com

m.i. is het dom om een CPU te ontwerpen die "onjuiste resultaten" kan geven als je niet lang genoeg gewacht hebt na het schrijven van een eeprom.

Als ik een taal ontwerp met +, -, * en / met de gebruikelijke betekenis, dan -= trek van huidige waarde af, *=, vermenigvuldig huidige waarde met .. , maar dan ineens met += een logisch or beschrijf, dan heb je iets wat een verrassing veroorzaakt bij mensen die enigszins weten waar ze mee bezig zijn en extrapoleren van bestaande patronen, maar niet voor 100% de handleiding lezen.

Zo dus ook met het stopzetten van de CPU danwel gewoon foute resultaten geven. Wie trapt er in en heeft er last van de ontwerp beslissing om het op de ene of de andere manier te doen?

Volgens mij is het "m'n timing klopt niet meer, die stomme CPU is 9 ms gaan wachten om de eeprom read af te maken." een geavanceerde gebruiker die je makkelijk kan opzadelen met "had je maar het registertje moeten checken of de write al klaar is". De beginner die niet tijd-kritisch gewoon de 10ms delay accepteert krijgt tenminste betrouwbare resultaten.

Geef je onbetrouwbare resultaten, dan zadel je de beginner die niet de tijd of kapstok heeft om de handleiding in 1x goed te kunnen lezen op met een onverwacht resultaat.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Zo lust ik er nog wel een paar... :)
Zelfde geldt voor A/D converter, I2C,... (als je bij A/D niet de vereiste aquisition wachttijd in acht neemt krijg je ook onjuiste resultaten.)

Programmeur wordt geacht enige moeite te hebben gestoken in het leren van de werking van de betreffende processor.
Wat betreft de eeprom: je kunt simpel een bit afvragen om te zien of een write klaar is.

De processor geeft geen 'onbetrouwbare' resultaten terug, de 'programmeur' heeft geen idee waar 'ie mee bezig is dan...

Arco - "Simplicity is a prerequisite for reliability" - hard en software ontwikkeling: www.arcovox.com