Hur gör man en snabb funktion i C?
Re: Hur gör man en snabb funktion i C?
Det kommer inte att gå snabbare med en extern komparator/opamp om den
ändå ska in på en interrupt ingång på processorn. Då kan du lika gärna
köra med den interna. Själv komparatorn jobbar helt för sig självt så snart
du har konfigurerat det hela. Använd den interna spänningsreferensen
och lägg den till ena komp ingången, Den andra kopplar du till den
spännng som ska övervakas, eventuelt via en enkel spänningsdelare.
> mäta spänningen över ett shunt-motstånd...
Hur sitter motståndet? Hur det strömmätning det handlar om?
High-side (i matningen) eller low-side (mellan lasten och GND)?
Vilket spänningsfall handlar det om? Har du någon förstärkning?
ändå ska in på en interrupt ingång på processorn. Då kan du lika gärna
köra med den interna. Själv komparatorn jobbar helt för sig självt så snart
du har konfigurerat det hela. Använd den interna spänningsreferensen
och lägg den till ena komp ingången, Den andra kopplar du till den
spännng som ska övervakas, eventuelt via en enkel spänningsdelare.
> mäta spänningen över ett shunt-motstånd...
Hur sitter motståndet? Hur det strömmätning det handlar om?
High-side (i matningen) eller low-side (mellan lasten och GND)?
Vilket spänningsfall handlar det om? Har du någon förstärkning?
- Magnus_K
- EF Sponsor
- Inlägg: 5854
- Blev medlem: 4 januari 2010, 17:53:25
- Ort: Skogen mellan Uppsala-Gävle
Re: Hur gör man en snabb funktion i C?
Njo jag vill nog använda interrupten så då får det bli den interna.
Sen är detta i ett väldigt tidigt skede så jag har inte funderat så mycket på det än. Det går säkert att lösa allt externt men nu vill jag labba vidare lite med mina microcontrollers..
Resistorerna tänkte jag lägga vid matningen (alltså high-side) och jag beställde några 0,01 Ohm's keramiska för att prova med.
Det borde ju innebära 10mV/A vilket kanske är lite väl litet men jag beställde också ett par AN623 för att försöka förstärka det en aning.
Ja, som sagt, inte funderat speciellt mycket på det än. Låter det tokigt?
Sen är detta i ett väldigt tidigt skede så jag har inte funderat så mycket på det än. Det går säkert att lösa allt externt men nu vill jag labba vidare lite med mina microcontrollers..
Resistorerna tänkte jag lägga vid matningen (alltså high-side) och jag beställde några 0,01 Ohm's keramiska för att prova med.
Det borde ju innebära 10mV/A vilket kanske är lite väl litet men jag beställde också ett par AN623 för att försöka förstärka det en aning.
Ja, som sagt, inte funderat speciellt mycket på det än. Låter det tokigt?
Re: Hur gör man en snabb funktion i C?
Ja, då behöver du något slags förstärkning av mätsignalen innan du
lägger in den på komparatoringången.
Ligger spånningen på matningen som ska övervakas på samma
nivå som matar processor och det övriga?
> Det borde ju innebära 10mV/A vilket kanske är lite väl litet...
Tja, om du kör 100A så är det väl lagom.
Annars finns det kretsar som kallas "current sense" eller liknande.
Fördelen (och skillnaden mot en vanlig opamp) är att inspänningen
på ingångarna kan vara långt utanför matningsspänningen. T.ex
73-074-46 med upp till +28V på ingångarna oavsett matningsspänning.
Eller kolla dessa två som jag råka ha liggandes:
http://jescab2.dyndns.org/pub_docs/zxct1020.pdf
http://www.ti.com/product/ina200
lägger in den på komparatoringången.
Ligger spånningen på matningen som ska övervakas på samma
nivå som matar processor och det övriga?
> Det borde ju innebära 10mV/A vilket kanske är lite väl litet...
Tja, om du kör 100A så är det väl lagom.

Annars finns det kretsar som kallas "current sense" eller liknande.
Fördelen (och skillnaden mot en vanlig opamp) är att inspänningen
på ingångarna kan vara långt utanför matningsspänningen. T.ex
73-074-46 med upp till +28V på ingångarna oavsett matningsspänning.
Eller kolla dessa två som jag råka ha liggandes:
http://jescab2.dyndns.org/pub_docs/zxct1020.pdf
http://www.ti.com/product/ina200
- Magnus_K
- EF Sponsor
- Inlägg: 5854
- Blev medlem: 4 januari 2010, 17:53:25
- Ort: Skogen mellan Uppsala-Gävle
Re: Hur gör man en snabb funktion i C?
Angående spänningen; nej den kommer inte ligga på samma nivå. Om det blir som tänkt så kommer jag använda +3,3V, +5V, +12V, -12V och en reglerbar som komponenterna får sätta stopp för hur stort "spannet" blir. Mest troligt kring 3-25V.
Hehe, 100A.. Jo jag tackar ja.
När jag grottade i det här första gången så ställde jag mig just den frågan; hur gör jag med de spänningsnivåer som ligger utanför vad jag ska köra kontroll-enheten (controllern) på? Hittade då just dessa INAXXXX enheter hos TI som verkade fantastiska.
Då jag aldrig provat innan så testade jag att sampla 2st IC som jag tyckte verkade lovande (hur någon på min nivå kan tycka det...). Resultatet blev denna tråd.
Dessa INA3221 valde jag pga I2C interfacet och att jag kan gå upp till +26V, samt 3 kanaler.
Så här i efterhand så har det slått mig att I2C-kommunikationen antagligen är för långsam för att hinna slå av matningen till kretsen vid överström men det ska bli skitkul att knäppa i en sån på labbplattan och testa lite.
När jag kopplat/räknat mer så vet jag mer. Återkommer gärna angående kretsarna du säljer. Måste läsa lite datablad först. Eller ja, är det billiga krestar? I så fall är det ju alltid "bra-att-ha"!
Hehe, 100A.. Jo jag tackar ja.
När jag grottade i det här första gången så ställde jag mig just den frågan; hur gör jag med de spänningsnivåer som ligger utanför vad jag ska köra kontroll-enheten (controllern) på? Hittade då just dessa INAXXXX enheter hos TI som verkade fantastiska.
Då jag aldrig provat innan så testade jag att sampla 2st IC som jag tyckte verkade lovande (hur någon på min nivå kan tycka det...). Resultatet blev denna tråd.
Dessa INA3221 valde jag pga I2C interfacet och att jag kan gå upp till +26V, samt 3 kanaler.
Så här i efterhand så har det slått mig att I2C-kommunikationen antagligen är för långsam för att hinna slå av matningen till kretsen vid överström men det ska bli skitkul att knäppa i en sån på labbplattan och testa lite.
När jag kopplat/räknat mer så vet jag mer. Återkommer gärna angående kretsarna du säljer. Måste läsa lite datablad först. Eller ja, är det billiga krestar? I så fall är det ju alltid "bra-att-ha"!
Re: Hur gör man en snabb funktion i C?
De finns här:
http://www.ebay.com/itm/171202965444
http://www.ebay.com/itm/181294218593
Men du kan ju alltid höra av dig direkt också...
http://www.ebay.com/itm/171202965444
http://www.ebay.com/itm/181294218593
Men du kan ju alltid höra av dig direkt också...
Re: Hur gör man en snabb funktion i C?
Brasklapp: Jag har inte pysslat med PIC och än mindre med just den nämnda kompilatorn.
En rätt avgörande faktor är givetvis hur bra kompilatorn är på att optimera.
En kompilator som är usel på att optimera kan kanske få hjälp på traven om man ändrar
if(analog > 500) PORTA.RA0 = 0;
till
PORTA.RA0 = PORTA.RA0 && (analog <= 500)
Det kan också ge långsammare kod. Det beror lite på hur processorn är konstruerad. Vissa processorer tar lång tid på sig att göra hopp, andra inte.
Om du kan nöja dig med en funktion i stil med
if(analog > 500) PORTA.RA0 = 0; else PORTA.RA0 = 1;
så kan du istället nyttja denna kod:
PORTA.RA0 = (analog <= 500)
P.S. C har "sant"/TRUE = 1 till skillnad från en del andra språk. Exempelvis har microsoft basic TRUE = -1, d.v.s. alla bitar satta i ett vanligt tvåkomplementärt heltal.
En rätt avgörande faktor är givetvis hur bra kompilatorn är på att optimera.
En kompilator som är usel på att optimera kan kanske få hjälp på traven om man ändrar
if(analog > 500) PORTA.RA0 = 0;
till
PORTA.RA0 = PORTA.RA0 && (analog <= 500)
Det kan också ge långsammare kod. Det beror lite på hur processorn är konstruerad. Vissa processorer tar lång tid på sig att göra hopp, andra inte.
Om du kan nöja dig med en funktion i stil med
if(analog > 500) PORTA.RA0 = 0; else PORTA.RA0 = 1;
så kan du istället nyttja denna kod:
PORTA.RA0 = (analog <= 500)
P.S. C har "sant"/TRUE = 1 till skillnad från en del andra språk. Exempelvis har microsoft basic TRUE = -1, d.v.s. alla bitar satta i ett vanligt tvåkomplementärt heltal.
- Magnus_K
- EF Sponsor
- Inlägg: 5854
- Blev medlem: 4 januari 2010, 17:53:25
- Ort: Skogen mellan Uppsala-Gävle
Re: Hur gör man en snabb funktion i C?
Precis MiaM! Det var det här jag försökte fråga från början och har som vanligt fått många mycket bra svar på (om)vägen!
Jag misstänker att någon som är duktig på det här hade kompilerat de olika kodsnuttarna och räknat antal instruktionscykler, rätt?
Sen förstår jag att i just detta fallet så ligger den största tidsbesparingen på A/D-omvandlingen/komparatorn snabbhet men som jag skrev i första inlägget så vill jag också försöka få till "rätt" tänk från början.
Det är så många gånger jag får olika idéer om hur jag kan lösa ett problem men det är sällan jag kan bekräfta vilket som är lämpligast/bäst.
PORTA.RA0 = (analog <= 500) var ju också ett sätt att skriva men om jag förstår det rätt så kommer utgången toggla beroende på om analog är över eller under 500.
I min lilla if-sats var just syftet att när en gång tröskeln är nådd så ställ utgången till 0 och sen spelar det ingen roll vad analogvärdet är längre.
Nu var jag ju inte speciellt tydlig med det här när jag tittar tillbaka.
Om jag öppnar .asm filen i ett litet simpelt program med en if-sats så ser det ut så här:
Kan man helt enkelt säga att en rad av detta är en assembly-instruktion och tar en instruktionscykel, dvs 4 klockcykler per rad? Eller har jag missuppfattat det?
EDIT: Ser nu att det blir lite extra instruktioner pga "Button" är en funktion från ett bibliotek men vänligen bortse från detta, frågan kvarstår!
Jag misstänker att någon som är duktig på det här hade kompilerat de olika kodsnuttarna och räknat antal instruktionscykler, rätt?
Sen förstår jag att i just detta fallet så ligger den största tidsbesparingen på A/D-omvandlingen/komparatorn snabbhet men som jag skrev i första inlägget så vill jag också försöka få till "rätt" tänk från början.
Det är så många gånger jag får olika idéer om hur jag kan lösa ett problem men det är sällan jag kan bekräfta vilket som är lämpligast/bäst.
PORTA.RA0 = (analog <= 500) var ju också ett sätt att skriva men om jag förstår det rätt så kommer utgången toggla beroende på om analog är över eller under 500.
I min lilla if-sats var just syftet att när en gång tröskeln är nådd så ställ utgången till 0 och sen spelar det ingen roll vad analogvärdet är längre.
Nu var jag ju inte speciellt tydlig med det här när jag tittar tillbaka.
Om jag öppnar .asm filen i ett litet simpelt program med en if-sats så ser det ut så här:
Kod: Markera allt
;I2C1840.c,19 :: if (Button(&PORTA, 4, 50, 0)) { // If button at RA4 detected low > 50ms then set old_state to 1
MOVLW PORTA+0
MOVWF FARG_Button_port+0
MOVLW hi_addr(PORTA+0)
MOVWF FARG_Button_port+1
MOVLW 4
MOVWF FARG_Button_pin+0
MOVLW 50
MOVWF FARG_Button_time_ms+0
CLRF FARG_Button_active_state+0
CALL _Button+0
MOVF R0, 0
BTFSC STATUS+0, 2
GOTO L_main3
;I2C1840.c,20 :: old_state = 1;
BSF _old_state+0, BitPos(_old_state+0)
;I2C1840.c,21 :: }
EDIT: Ser nu att det blir lite extra instruktioner pga "Button" är en funktion från ett bibliotek men vänligen bortse från detta, frågan kvarstår!
Re: Hur gör man en snabb funktion i C?
Jag har som sagt inte pysslat med PIC, så specifika grejer har jag inte koll på.
Varje rad som "gör" något är en assembler/maskinkodsinstruktion. D.v.s. alla rader utom kommentarraderna. Om det i PIC-dokumentationen står att varje instruktion tar 4 cykler så tar alltså varje rad 4 cykler. (Ja, förutom som du redan skrivit så tar givetvis hoppinstruktionen i sig säkert bara 4 cykler men sedan ska koden man hoppar till köras).
Många processorfamiljer tar olika lång tid att köra olika instruktioner. Om man t.ex. hoppar till en subrutin (CALL) så brukar det vanligtvis vara så att programräknaren först sparas på en stack och sedan utförs själva hoppet. PIC kanske gör på något annat sätt.
Apropå interrupter så har åtminstone antika Motorla 6800 en WAI-instruktion, "WAit for Interrupt". Det speciella med den är att den gör början av interrupthanteringen i förväg. 6800 skickar in "allt" på stacken när en interrupt infaller, och WAI ser alltså till att skicka ut "allt" på stacken innan den sover i väntan på en interrupt. Det gör att ifall man kört en WAI så kommer interruptresponstiden vara några cykler snabbare.
Varje rad som "gör" något är en assembler/maskinkodsinstruktion. D.v.s. alla rader utom kommentarraderna. Om det i PIC-dokumentationen står att varje instruktion tar 4 cykler så tar alltså varje rad 4 cykler. (Ja, förutom som du redan skrivit så tar givetvis hoppinstruktionen i sig säkert bara 4 cykler men sedan ska koden man hoppar till köras).
Många processorfamiljer tar olika lång tid att köra olika instruktioner. Om man t.ex. hoppar till en subrutin (CALL) så brukar det vanligtvis vara så att programräknaren först sparas på en stack och sedan utförs själva hoppet. PIC kanske gör på något annat sätt.
Apropå interrupter så har åtminstone antika Motorla 6800 en WAI-instruktion, "WAit for Interrupt". Det speciella med den är att den gör början av interrupthanteringen i förväg. 6800 skickar in "allt" på stacken när en interrupt infaller, och WAI ser alltså till att skicka ut "allt" på stacken innan den sover i väntan på en interrupt. Det gör att ifall man kört en WAI så kommer interruptresponstiden vara några cykler snabbare.
Re: Hur gör man en snabb funktion i C?
Magnus_K: låt se nu om jag fattar rätt...
Du försöker skapa ett system som har en mycket snabb respons på en mätning - men mitt i det hela låser du upp µC'n i 50ms för att kolla om knappen är påverkad?
Jag tror att du nog får tänka lite extra och fundera på vad du egentligen ämnar att göra, du verkar inte ha speciellt med koll på hela programflödet och vilka krav du har.
Du försöker skapa ett system som har en mycket snabb respons på en mätning - men mitt i det hela låser du upp µC'n i 50ms för att kolla om knappen är påverkad?

Jag tror att du nog får tänka lite extra och fundera på vad du egentligen ämnar att göra, du verkar inte ha speciellt med koll på hela programflödet och vilka krav du har.
- Magnus_K
- EF Sponsor
- Inlägg: 5854
- Blev medlem: 4 januari 2010, 17:53:25
- Ort: Skogen mellan Uppsala-Gävle
Re: Hur gör man en snabb funktion i C?
Jag ber om ursäkt Icecap! Kodsnutten jag postade är inte från det aktuella programmet (det är inte skrivet än).
Öppnade bara första bästa program med en if-sats och postade den. Koden postades alltså enbart som förtydling inför frågan som står nedanför kodsnutten och för att ha en assembly-kod att diskutera kring.
Ja, det var som sagt klantigt att röra till det så här men jag vågar nog påstå att jag har så pass god koll så jag förstår att jag inte kan låta processorn spendera tid på debounce-funktioner av knappar när jag samtidigt försöker jaga ms, µs och kanske tom ns i en mätning.
Öppnade bara första bästa program med en if-sats och postade den. Koden postades alltså enbart som förtydling inför frågan som står nedanför kodsnutten och för att ha en assembly-kod att diskutera kring.
Ja, det var som sagt klantigt att röra till det så här men jag vågar nog påstå att jag har så pass god koll så jag förstår att jag inte kan låta processorn spendera tid på debounce-funktioner av knappar när jag samtidigt försöker jaga ms, µs och kanske tom ns i en mätning.
Re: Hur gör man en snabb funktion i C?
> Kan man helt enkelt säga att en rad av detta är en assembly-instruktion och tar
> en instruktionscykel, dvs 4 klockcykler per rad? Eller har jag missuppfattat det?
Jag vet inte vad det finns att missuppfatta. Det finns en enkel tabell i databladet
med tider för varje instruktion. Generellt kan man säga att alla tar en maskincykel
förrutom de instruktioner som ändrar programräknaren (t.ex GOTO, CALL, RETURN
och om man själv laddar PC med ett nytt värde) som tar två. Vissa specialare som
t.ex läsning från flash via tabell-instruktionerna tar en extra cykel. Men som sagt,
det finns en tydlig tabell i alla datablad.
> en instruktionscykel, dvs 4 klockcykler per rad? Eller har jag missuppfattat det?
Jag vet inte vad det finns att missuppfatta. Det finns en enkel tabell i databladet
med tider för varje instruktion. Generellt kan man säga att alla tar en maskincykel
förrutom de instruktioner som ändrar programräknaren (t.ex GOTO, CALL, RETURN
och om man själv laddar PC med ett nytt värde) som tar två. Vissa specialare som
t.ex läsning från flash via tabell-instruktionerna tar en extra cykel. Men som sagt,
det finns en tydlig tabell i alla datablad.
- Magnus_K
- EF Sponsor
- Inlägg: 5854
- Blev medlem: 4 januari 2010, 17:53:25
- Ort: Skogen mellan Uppsala-Gävle
Re: Hur gör man en snabb funktion i C?
@MiaM: Den där WAI-funktionen verkade ju rätt smart men då jag inte vet vad varken en stack är eller om den funktionen används i min PIC så kan jag inte komma med något bra svar. Ska läsa på lite mer om "stacken" idag för det här börjar bli mycket intressant!
@sodjan: Det var som tusan! Jag trodde faktiskt att det här var något mer "generellt" men jag förstår nu att sättet processorn jobbar är induviduellt för varje uppbyggnad (låter kanske självklart så här i efterhand), vilket leder till databladet.
Precis som du säger så var det ganska lättläst/lättförståeligt under "Instruction Set Summary".
Det här ska bli så roligt att greja runt med så jag nästan får gåshud. Nu kan jag ju skriva lite olika små snuttar och läsa ut vilken funktion som kompilerades mest effektivast. Eller ja, det låter ju bra men nu har jag mycket mer kött på benen!
"Bonusen" blir ju också att man ser assembly-koden och kanske får lite mer på köpet om vad som verkligen händer i processorn.
@sodjan: Det var som tusan! Jag trodde faktiskt att det här var något mer "generellt" men jag förstår nu att sättet processorn jobbar är induviduellt för varje uppbyggnad (låter kanske självklart så här i efterhand), vilket leder till databladet.
Precis som du säger så var det ganska lättläst/lättförståeligt under "Instruction Set Summary".
Det här ska bli så roligt att greja runt med så jag nästan får gåshud. Nu kan jag ju skriva lite olika små snuttar och läsa ut vilken funktion som kompilerades mest effektivast. Eller ja, det låter ju bra men nu har jag mycket mer kött på benen!
"Bonusen" blir ju också att man ser assembly-koden och kanske får lite mer på köpet om vad som verkligen händer i processorn.
Re: Hur gör man en snabb funktion i C?
Olika processorer jobbar på väldigt olika sätt, blandar man dessutom in pipelining så blir det ännu större variation.
Jag har t.ex. kodat för en processor som kunde köra upp till fyra operationer per klockcykel, men access av externt RAM (processorn hade internt RAM också) tog 11 klockcykler.
Jag har en gammal nerskriven förklaring av en bit kod där, koden räknar ut kvadratiskt medelvärde för ett antal bytes med en klockcykel per byte (under förutsättning att det är minst 4 bytes och en multipel av 4). Kan lägga in den här som lite kuriosa. Processorn är en TMS 320-C80 "multimedia processor" som alltså har totalt 5 kärnor och internt RAM (både "privat" och "delat" för kärnorna). En RISC-kärna och fyra "parallellprocessorer". Koden nedan är för EN parallellprocessor. Jag skulle alltså kunna köra detta parallellt på 4 kärnor i samma kapsel.
d0-d7 är register (32-bitars)
a0-a8 är adressregister (minns inte hur många som fanns, men a0 och a8 verkar användas i koden)
x0-x1 är indexregister (minns inte heller hur många det fanns)
|| först på en rad betyder "görs parallellt med föregående"
Nu är den här koden extremt "handoptimerad", det är inte jag som skrivit den, den var med som exempel i databladet. Det där med ealu är t.ex. en riktig "fuling". Och själva loopen är inte med heller.
Först har vi en "splittad" subtraktion. Registren är 32-bitars men är delade så att det bara är bytevis subtraktion. m:et står fär multiple och c står för att vi vill att multiple flags (mf) lägsta fyra bitar skall sättas som carry-flaggorna. I princip görs det alltså fyra 8-bitars subtraktioner parallellt.
Sen har vi en unsigned halfword (uh) extract av det lägre halfwordet (0).
Och slutligen en "fuskis". d5 används som ett dummyregister. Vi vill bara räkna upp adressregsitret med indexregistrer. Detta sköts av en av adressenheterna, och behöver alltså inte ALUn. d5 får visserligen också värdet av a0, men det används alltså inte. Notera habrovinken med &*, vi vill inte läsa från adress a0 och det finns ingen instruktion som är bara a0+=x0.
Första arden där ser lite rörig ut, men det beror alltså på att det finns en färdig opkod för detta (d.v.s att få fram absolutbeloppet).
@mf betyder Expanded Multiple Flags. De fyra lägsta bitarna i mf sattes till carry. Expansionen innebär att var och en av de bitarna fyller upp en byte.
Alltså, binärt: 0000000000000000000000000000abcd blir aaaaaaaabbbbbbbbccccccccdddddddd
m:et står även här för multiple, d.v.s. att varje byte hanteras för sig. Resultatet blir att absolutbeloppet för varje byte i d7 hamnar som bytes i d4.
Kör man denna utan m och @mf så skulle d4 alltså få absolutbeloppet av d7 bara, men ALU kan alltså köra bytes eller halfwords "parallellt" istället för hela 32-bitars ord.
Sen har vi en till halfword extract, den här gången det övre halfwordet.
Slutligen laddas d6 från adressen som a1 pekar på, och a1 räknas upp (det funkar precis som i C).
En unsigned (u) split (m=multiple) multiply. Bytesen i d4 kvadreras alltså och blir i samma veva halfwords. Det är alltså bara de två lägsta bytesen som kvadreras.
ealu är en lite speciall sak. Operationen anges i d0-registret, och opkoden innehåller alltså bara information om att det är en ealu-operation och vilka register som skall användas. d0 är satt tidigare. >>u16 betyder 16 steg unsigned shift right. d3 är laddad med noll! Orsaken förklaras nedan, men resultatet blir i alla fall en halfword extract (övre halvordet från d4 till d7).
Och så en halfword extract och en adressaritmetisk beräkning igen.
En kvadrering till.
Och en ealu igen. Nu är grejen att den här ealun har SAMMA operationskod som den förra. Vi använder alltså samma d0, men har andra register (de stod ju i opkoden). Det var alltså anledningen till att vi hade d3 som nollregister tidigare. Här används den inte som halfword extract, utan den summerar kvadrerade värden (de blev ju halfwords när de kvadrerades).
Sen har vi en adressaritmetisk pryl igen, och en load från minnet. Nu råkar bägge två ha samma destination, vilket ser lite konstigt ut. Som tur är (jo, det visste de som skrev det här:-) så finns det nåt som heter Write Priority. Det fungerar som så att Global Transfer (och det är det här, eftersom a8 är ett globalt adressregister) har högre prioritet än ALU-operationer. Bägge prylarna utförs alltså, men bara den ena skriver till destinationen.
Så, summa summarum, på 4 instruktioner (om än väldigt komplexa! men de ryms i 64 bitars opkoden) så har vi summerat kvadraten av fyra bytes.
Litet sidospår, men poängen här är då att de bytes jag vill jobba på måste ligga i internt RAM, annars dröjer det 11 klockcykler innan man får datat (skulle ju motsvara nästan 3 varv i loopen).
I det program jag skrev (implementering av ett digitalt filter) så utnyttjade jag väntetiden till beräkningar.
Jag har t.ex. kodat för en processor som kunde köra upp till fyra operationer per klockcykel, men access av externt RAM (processorn hade internt RAM också) tog 11 klockcykler.
Jag har en gammal nerskriven förklaring av en bit kod där, koden räknar ut kvadratiskt medelvärde för ett antal bytes med en klockcykel per byte (under förutsättning att det är minst 4 bytes och en multipel av 4). Kan lägga in den här som lite kuriosa. Processorn är en TMS 320-C80 "multimedia processor" som alltså har totalt 5 kärnor och internt RAM (både "privat" och "delat" för kärnorna). En RISC-kärna och fyra "parallellprocessorer". Koden nedan är för EN parallellprocessor. Jag skulle alltså kunna köra detta parallellt på 4 kärnor i samma kapsel.
d0-d7 är register (32-bitars)
a0-a8 är adressregister (minns inte hur många som fanns, men a0 och a8 verkar användas i koden)
x0-x1 är indexregister (minns inte heller hur många det fanns)
|| först på en rad betyder "görs parallellt med föregående"
Nu är den här koden extremt "handoptimerad", det är inte jag som skrivit den, den var med som exempel i databladet. Det där med ealu är t.ex. en riktig "fuling". Och själva loopen är inte med heller.
Kod: Markera allt
d7= mc d6-d5
|| x0=uh0 d4
|| d5=&*(a0+=x0)
Sen har vi en unsigned halfword (uh) extract av det lägre halfwordet (0).
Och slutligen en "fuskis". d5 används som ett dummyregister. Vi vill bara räkna upp adressregsitret med indexregistrer. Detta sköts av en av adressenheterna, och behöver alltså inte ALUn. d5 får visserligen också värdet av a0, men det används alltså inte. Notera habrovinken med &*, vi vill inte läsa från adress a0 och det finns ingen instruktion som är bara a0+=x0.
Kod: Markera allt
d4= m 0 + ((d7&@mf)|(-d7&~@mf))
|| x1=uh1 d4
|| d6=*a1++
@mf betyder Expanded Multiple Flags. De fyra lägsta bitarna i mf sattes till carry. Expansionen innebär att var och en av de bitarna fyller upp en byte.
Alltså, binärt: 0000000000000000000000000000abcd blir aaaaaaaabbbbbbbbccccccccdddddddd
m:et står även här för multiple, d.v.s. att varje byte hanteras för sig. Resultatet blir att absolutbeloppet för varje byte i d7 hamnar som bytes i d4.
Kör man denna utan m och @mf så skulle d4 alltså få absolutbeloppet av d7 bara, men ALU kan alltså köra bytes eller halfwords "parallellt" istället för hela 32-bitars ord.
Sen har vi en till halfword extract, den här gången det övre halfwordet.
Slutligen laddas d6 från adressen som a1 pekar på, och a1 räknas upp (det funkar precis som i C).
Kod: Markera allt
d4= um d4*d4
|| d7=ealu(d3+d4>>u16)
|| x0=uh0 d2
|| d5=&*(a0+=x0)
ealu är en lite speciall sak. Operationen anges i d0-registret, och opkoden innehåller alltså bara information om att det är en ealu-operation och vilka register som skall användas. d0 är satt tidigare. >>u16 betyder 16 steg unsigned shift right. d3 är laddad med noll! Orsaken förklaras nedan, men resultatet blir i alla fall en halfword extract (övre halvordet från d4 till d7).
Och så en halfword extract och en adressaritmetisk beräkning igen.
Kod: Markera allt
d2= um d7*d7
|| d1=ealu(d1+d2>>u16)
|| d5=&*(a0+=x1)
|| d5=*a8++
Och en ealu igen. Nu är grejen att den här ealun har SAMMA operationskod som den förra. Vi använder alltså samma d0, men har andra register (de stod ju i opkoden). Det var alltså anledningen till att vi hade d3 som nollregister tidigare. Här används den inte som halfword extract, utan den summerar kvadrerade värden (de blev ju halfwords när de kvadrerades).
Sen har vi en adressaritmetisk pryl igen, och en load från minnet. Nu råkar bägge två ha samma destination, vilket ser lite konstigt ut. Som tur är (jo, det visste de som skrev det här:-) så finns det nåt som heter Write Priority. Det fungerar som så att Global Transfer (och det är det här, eftersom a8 är ett globalt adressregister) har högre prioritet än ALU-operationer. Bägge prylarna utförs alltså, men bara den ena skriver till destinationen.
Så, summa summarum, på 4 instruktioner (om än väldigt komplexa! men de ryms i 64 bitars opkoden) så har vi summerat kvadraten av fyra bytes.
Litet sidospår, men poängen här är då att de bytes jag vill jobba på måste ligga i internt RAM, annars dröjer det 11 klockcykler innan man får datat (skulle ju motsvara nästan 3 varv i loopen).
I det program jag skrev (implementering av ett digitalt filter) så utnyttjade jag väntetiden till beräkningar.
Re: Hur gör man en snabb funktion i C?
> men då jag inte vet vad varken en stack är eller om den funktionen används i min PIC...
Jo det gör den. Tänk dig att du läser en vanlig bok. Vid ett tillfälle får du behov
av att slå upp något i ordförklaringen längst bak. Du kanske stoppar in ett finger
i boken för att enkelt och snabbt hitta tillbaka till där du var innan "avbrottet".
Fingret motsvarar att lägga den aktuella adressen på stacken för att sedan
kunna hitta tillbaka. Instruktionen RETFIE (Return From Interrupt) hämtar
tillbaka adressen från stacken och lägger tillbaka den i programräknaren
så att (huvud-) koden kan fortsätta där den var.
Samtidigt så behöver man ofta spara undan WREG, STATUS och några register
till så att även dessa kan återställas vid RETFIE. På de nya PIC16F1xxx så sker
detta med automatik i ett antal "shadow registers". Se kapitel "8.5 Automatic
Context Saving" i databladet. Så det som MiaM beskriver (att spara undan
register i förväg) är i datta fall onödigt, det sker utan tidsfördröjning med
automatik när interruptet sker. Detta medför också att svarstiden på interrupt
på den nya PIC16F1xxx (och PIC12F1xxx) är betydligt kortare än på de tidigare
PIC16Fxxx modellerna, där fick man själv spara undan rellevanta register och
återställa dom precis innan RETFIE.
Jo det gör den. Tänk dig att du läser en vanlig bok. Vid ett tillfälle får du behov
av att slå upp något i ordförklaringen längst bak. Du kanske stoppar in ett finger
i boken för att enkelt och snabbt hitta tillbaka till där du var innan "avbrottet".
Fingret motsvarar att lägga den aktuella adressen på stacken för att sedan
kunna hitta tillbaka. Instruktionen RETFIE (Return From Interrupt) hämtar
tillbaka adressen från stacken och lägger tillbaka den i programräknaren
så att (huvud-) koden kan fortsätta där den var.
Samtidigt så behöver man ofta spara undan WREG, STATUS och några register
till så att även dessa kan återställas vid RETFIE. På de nya PIC16F1xxx så sker
detta med automatik i ett antal "shadow registers". Se kapitel "8.5 Automatic
Context Saving" i databladet. Så det som MiaM beskriver (att spara undan
register i förväg) är i datta fall onödigt, det sker utan tidsfördröjning med
automatik när interruptet sker. Detta medför också att svarstiden på interrupt
på den nya PIC16F1xxx (och PIC12F1xxx) är betydligt kortare än på de tidigare
PIC16Fxxx modellerna, där fick man själv spara undan rellevanta register och
återställa dom precis innan RETFIE.
- Magnus_K
- EF Sponsor
- Inlägg: 5854
- Blev medlem: 4 januari 2010, 17:53:25
- Ort: Skogen mellan Uppsala-Gävle
Re: Hur gör man en snabb funktion i C?
@Nerre: Du får ursäkta mig men det här var lite för svårt. Normalt sett brukar jag göra mitt bästa att visa respekt och ge lite följdfrågor till ett så välskrivet och välmenat inlägg men det här förstod jag tyvärr inte mycket av. Förhoppningsvis ger det mycket till någon annan som ligger på högre nivå än mig!
Dock tror jag att jag förstår idén med att försöka ha processorn arbetandes hela tiden. Om man kan utnyttja väntetiden till beräkningstid i förberedande syfte så ska man göra det. Hoppas jag kan implementera tänket bara... Ja som sagt, ledsen att jag inte lägger ner mer energi!
@sodjan: Det var mig en bra/enkel förklaring på vad stacken är och hur man nyttjar den!
Jag har läst om "shadow register" i databladen men inte riktigt förstått vad det är jag läser, men nu vet jag mer.
En följdfråga som blir lite svår att ställa men jag gör ett försök:
I de senare modellerna säger du att undansparningen av vissa register sker per automatik och därmed "förloras" ingen tid när man kallar ett interrupt. Kan man i det här fallet göra, som jag tolkade Nerre's inlägg, att på de äldre PIC:arna låta denna process ske i bakgrunden så dessa register sparas undan och därmed kan få samma tidsbesparing som när "shadow registers" används?
Det var mig klurigt att få till så ni förstår jag jag frågar efter. Ett lite enklare försök: Kan man rent programmeringsmässigt göra samma tidsbesparing på en äldre PIC genom att låta denna process ske i bakgrunden?
Dock tror jag att jag förstår idén med att försöka ha processorn arbetandes hela tiden. Om man kan utnyttja väntetiden till beräkningstid i förberedande syfte så ska man göra det. Hoppas jag kan implementera tänket bara... Ja som sagt, ledsen att jag inte lägger ner mer energi!
@sodjan: Det var mig en bra/enkel förklaring på vad stacken är och hur man nyttjar den!
Jag har läst om "shadow register" i databladen men inte riktigt förstått vad det är jag läser, men nu vet jag mer.
En följdfråga som blir lite svår att ställa men jag gör ett försök:
I de senare modellerna säger du att undansparningen av vissa register sker per automatik och därmed "förloras" ingen tid när man kallar ett interrupt. Kan man i det här fallet göra, som jag tolkade Nerre's inlägg, att på de äldre PIC:arna låta denna process ske i bakgrunden så dessa register sparas undan och därmed kan få samma tidsbesparing som när "shadow registers" används?
Det var mig klurigt att få till så ni förstår jag jag frågar efter. Ett lite enklare försök: Kan man rent programmeringsmässigt göra samma tidsbesparing på en äldre PIC genom att låta denna process ske i bakgrunden?