Kysymys:
Miksi TMR0IF: n tyhjentämispaikka muuttaa ohjelman käyttäytymistä?
Havenard
2018-02-07 02:40:57 UTC
view on stackexchange narkive permalink

Ohjelmoin PIC16F877A-mikrokontrolleria C: ssä, kääntäen Microchip XC8 1.44: llä.

Olen asettanut TMR0-keskeytyksen jokaista 800 mikrosekuntia kohden, joka skannaa LED-matriisin läpi ja valaisee jokaisen 8 LED-sarakkeen kerrallaan seuraavilla asetuksilla:

  OPTION_REG = 0x04; // TMR0-eskalkaleri (1:32)
    GIE = ​​1;
    TMR0IE = 1;
 

Ja tämä on minun ISR:

  void keskeytys isr ()
{
    staattinen vakio uint16_t divs [4] = {1000, 100, 10, 1};
    jos (TMR0IF)
    {
        PORTB = 0x00; // PORTB on 8 LED-saraketta
        jos minä)
        {
            PORTAbittejä.RA0 = 1; // tappi, joka kellottaa CD4017 seuraavaa saraketta varten
            __nop (); // anna lyhyt viive vain hyväksi
            PORTAbittiä.RA0 = 0;
        }
        muu
        {
            PORTAbittiä.RA1 = 1; // tappi, joka palauttaa CD4017: n ensimmäiseen sarakkeeseen
            __nop ();
            PORTAbittiä.RA1 = 0;
        }

        // näytettävä kuvio
        PORTB = fontti [i% 8U + 8U * ((c / divs [i / 8U])% 10U)];
        i = (i + 1U)% 31U; // inkrementti i seuraavaa iteraatiota varten

        TMR0 = TMR0_OFFSET; // 240 4 MHz: n kristalleille 800 us: n viiveellä laskelmieni mukaan (olen huono laskelmissa)
        TMR0IF = 0;
    }
}
 

Pohjimmiltaan c (laskurille) pitää lukua välillä 0-9999, ja 31 sarakkeen LED-matriisi näyttää sen käyttämällä font -tietoja piirräksesi numerot.

Tämä koodi toimii nyt oikein. Mutta jos siirrän TMR0IF = 0; lohkon alkuun, tapahtuu jotain outoa:

Ohjelma näyttää hidastuvan, ikään kuin kello olisi pudonnut kertoimella 100 tai enemmän, ja pääohjelmasilmukka kaatuu ja lopetan siksi niiden ulkoisten signaalien vastaanottamisen, joita käytän lisäämään tai palauttamaan arvon c , vain ISR: ää kutsutaan edelleen ja pidetään LED-matriisi palavana, mutta välkkyy kellon pudotuksen takia.

Jos siirrän TMR0IF = 0; loppuun, kuten yllä olevassa koodissa, kaikki näyttää toimivan normaalisti ja maailma on täydellinen.Mutta miksi?

Kaikkialla näen ajastimen 0 keskeytyskäytön koodiviittaukset, jotka ihmiset asettavat TMR0IF = 0; heti, miksi se aiheuttaa kaiken ongelman koodissani?

Missä tarkalleen tarkoitat siirtämällä sen lohkon alkuun?Minkä kahden koodirivin väliin?
@AlmostDone Heti "if (TMR0IF) {" (jossa {{merkitsee lohkon alkua C-syntaksissa) jälkeen.
Teet jakoja ISR: ssä!Mene oppimaan kone ennen kuin kirjoitat enää korkean tason kielikoodia.
Kolme vastused:
Olin Lathrop
2018-02-07 05:12:57 UTC
view on stackexchange narkive permalink

Perusongelma on, että keskeytyskoodisi suorittaminen kestää kauemmin kuin keskeytysten välinen aika. Tämä johtuu todennäköisesti jostakin kahdesta ongelmasta:

  1. Keskeytysjakso ei ole sitä mitä luulet. Keskeytysolosuhteet laukaistaan ​​itse asiassa useammin kuin on tarkoitettu.

  2. Käytät liian monta jaksoa keskeytysrutiinissa.

Syy, miksi näyttää toimivan, kun tyhjennät keskeytysolosuhteen ISR: n lopussa, on, että etukoodin on oltava jonkin aikaa ennen seuraavaa keskeytystä. Se ei kuitenkaan ole ratkaisu ongelmaan. Kun siirrät keskeytysolosuhteiden tyhjentämisen ISR: n alkuun siellä missä sen pitäisi olla, ISR kestää niin kauan, että seuraava keskeytys suoritetaan välittömästi ISR: n päättyessä. Ei, tämä ei täytä pinoa, kuten toinen vastaus väittää. Se kuitenkin estää etualan koodin saamasta jaksoja.

Poista keskeytysolosuhteet aina heti keskeytysrutiinin alkaessa. Jos käytät ajastinta 0 keskeytysjaksolle, ÄLÄ aseta TMR0: ta kiinteään arvoon jokaisesta keskeytyksestä. Sen sijaan lisää sopiva määrä jokaiseen keskeytykseen. Näin et menetä aikaa keskeytymishäiriön viiveen perusteella.

Älä myöskään käytä esivalmistetta, kun lisäät offsetia ajastimeen. Jos käytät ajastinta 0, määritä joko lisää jokaisen keskeytyksen poikkeama saadaksesi tietyn ajanjakson esivalitsimella 1, tai käytä vain tuloksena olevaa vapaata juoksevaa jaksoa ei-yhtenäisyyden esiasteen kanssa.

Jos haluat enemmän mielivaltaisia ​​keskeytysjaksoja, käytä ajastinta 2. Sitä varten se on.

Huomasin, että kirjoitit keskeytysrutiini C: hen. Olen nähnyt melko kamala koodin paisuvan, koska C-kääntäjä ei tiennyt mitä todella tarvitsee tallentaa, ja siksi säästin paljon tavaraa. Henkilökohtaisesti kirjoitan PIC 16 keskeytysrutiinit kokoonpanijaan. Se myös helpottaa sen selvittämistä, mitä tällaisissa tapauksissa tapahtuu, koska sinun ja laitteiston välillä ei ole kääntäjää.

Skannasin juuri ISR-koodisi. Teet jakoja ISR: ssä! Ei ihme, että se vie kauemmin kuin on suunniteltu.

Tämä on hieno esimerkki siitä, miksi pienillä tällaisilla mikro-ohjaimilla kääntäjiä ei pitäisi koskaan käyttää korvaamaan konetta käsitystasolla komentotasolla. Kääntäjä saa olla vain pikakuvake luoda joukko koodia sinulle, mutta sinun on silti oltava peruskäsitys siitä, mikä koodi on ja mitä koneen on tehtävä pyyntösi toteuttamiseksi.

Toisin sanoen sinun on suunniteltava koodi samalla kun ajattelet koneen raaka-ominaisuuksia. Kun olet tehnyt sen, kääntäjän käyttäminen tietyn todellisen koodin luomiseen voi olla oikeutettu pikakuvake. Silloinkin ISR on se rutiini, jonka haluat eniten harkita kirjoittamista assemblerissa.

Mene kirjoittamaan tämä keskeytysrutiini kokoonpanijaan. Tämä ei johdu siitä, että sen on välttämättä oltava kokoonpanossa, vaan siksi, että sinun on opittava kone. Sinulla ei ole liiketoimintaa täällä olematta ymmärtämättä käskyjoukkoa ja laitteiston muita matalan tason ominaisuuksia. Et olisi koskaan yrittänyt jakaa jakamista ISR: ssä, jos todella ymmärrät käskykokonaisuuden.

Joo, tiedän tarkalleen, mitä tarkoitat, myös aika, joka kuluu vain sen tavun selvittämiseen, minkä tavun sen pitäisi tulostaa, on tarpeeksi pitkä, mikä saa LEDit vilkkumaan, mitä minulla ei ollut Arduino-prototyypissäni.Yritin optimoida asian joillakin bittikohtaisilla operaatioilla, mutta ilmeisesti kääntäjä tekee sen jo soveltuvin osin, en ole kuitenkaan varma kuinka optimoida `% 10` ja jaot 10: n voimilla.Yritän tutkia joitain binäärisiä BCD-muunnoskoodeja, mutta toistaiseksi kaikki löydetyt eivät näytä ollenkaan optimoiduilta.
Toistaiseksi paras ratkaisuni oli soveltaa ennalta laskettua `` PORTB`` ja laskea sitten `` PORTB '' esilaskua varten seuraavalle jaksolle, jotta LEDien sytyttämisessä ei ole viivettä.Ja käytä myös 20 MHz: n kristallia, mutta haluaisin mieluummin löytää ratkaisun 4 MHz: n kristallille.
Aivan rahalla Olin.Se tekee minut iloiseksi, että opin ensin tekemään heksadesimaalin ja sitten kokoonpanijan, ennen kuin opin C: n ja sitten LabView: n.OP: n on opittava perusteet siitä, kuinka koodi toimii ytimessä.
Vince Patron
2018-02-07 03:37:41 UTC
view on stackexchange narkive permalink

En usko, että sinun pitäisi koskaan lisätä TMR0IF = 0 (tyhjennä keskeytyslippu) ISR: n alkuun.

Luulen, että ISR-rutiinisi vie yli 800 meitä tai TMR0 laukaisee nopeammin kuin luulet. Asettamalla TMR0IF = 0 eteenpäin tyhjennät keskeytyksen ja annat toisen TMR0-keskeytyksen keskeyttää keskeytys. MCU työntää nykyiset tiedot pinoon ja kutsuu saman keskeytyksen uudelleen. Lopulta pino täyttyy ja järjestelmä kaatuu.

  1. Pidä TMR0IF = 0 viimeisenä käskynä
  2. Aikaa ISR: nne nähdäksesi, onko se alle 800 meitä.
  3. Tarkista, toimiiko TMR0 mielestäsi haluamallasi nopeudella.

Ajoitan rutiinejani ajamalla GPIO korkealla alussa ja ajamalla samaa GPIO matalalla ennen rutiinista poistumista. Käytä o: n silmukkaa kyseisellä GPIO: lla ja voit varmistaa, että a) jos ISR on todella alle 800 meitä, b) jos TMR0 soittaa todellakin haluamallasi nopeudella.

Pulssin leveys kertoo kuinka kauan ISR kestää. Ajanjakso kertoo sinulle, kuinka usein ISR: ääsi kutsutaan. Jos o'scope-signaalin jakso on epätasainen, ISR kestää kauemmin kuin TMR0-ajastinjaksot (ts. Ohittaa TMR0-jaksot)

Hyvä katsaus, mielestäni.+1.Olen kuitenkin tyhjentänyt keskeytyslipun hyvin ennen keskeytyksistä poistumista tapauksissa, joissa olen kohdassa, jossa voin sallia (ja haluan sallia) uudelleenkäynnistyksen ennen aikaisemman keskeytyksen päättymistä.Minusta ei kuitenkaan usein ole tarvetta tehdä sitä, ja se johtuu yleensä siitä, että en halunnut perustaa lippua, joka tunnustetaan muualla "enemmän työtä" varten ja halusin sen sijaan vain huijata ja kytkeä ylimääräinen työ, jokatapahtuu joskus aivan keskeytyskoodissa.Ei usein, kuten sanon.
Hyvä vastaus.Voisin lisätä, että ISR: lle pääsen sisään ja ulos.Jos keskeytyskoodi vie paljon aikaa, saattaa olla parempi asettaa vain lippu ISR: ään ja poistua.Lippu puolestaan laukaisee varsinaisen suoritettavan tehtävän pääohjelmasilmukasta.
Epäilen, että PIC16F tukee ISR: n puhelupinoa, on syy siihen, että sillä on vain 1 ISR.
Hyviä puolia.Re: 'päästä sisään / ulos', jos OP tarvitsee nopeutta, kenties fonttihakemisto tulisi laskea etukäteen ja tallentaa tavutaulukkoon.Sitten se olisi erittäin nopea haku ISR: lle.
@VincePatron Fontti käännetään ilmeisesti etuajassa.Se on joukossa.
Jos ISR ajoittain kestää pidempään kuin punkkien välinen aika, mutta ei koskaan kulje yli kaksi kertaa punkkien välistä aikaa, "TMR0IF = 0;" asettaminen alkuun voi olla oikea tapa.Jos ISR: n suorittamiseen tarvitaan useampi kuin yksi rasti, se suoritetaan uudelleen melkein heti `retfien` jälkeen (luulen, että keskusyksikkö suorittaa ensin yhden käskyn), mutta jos se päättyy ennen seuraavaa rastiä, kaikki on aikataulussaja CPU ei menetä keskeytyksiä.
@Havenard Ne sisältävät laitteistopinon ja keskeytys kuluttaa yhden.Joissakin laitteissa on vain 4 paikkaa laitteistossa - PIC16C54-C57, esimerkiksi 1980-luvun lopun henkilökohtaisesta kokemuksesta.Toisilla on 8 paikkaa.
Joo yllätyksekseni osoittautuu, että ISR-menettely kestää jostain kauemmin kuin käyttöjakso, joten PIC on aina varattu keskeytysten käsittelyyn eikä koskaan jatka pääohjelmaa.Tämä on hullua, ISR ei tee melkein mitään.Ellei ... PIC ei tue moduloa tai jakoa ja kääntäjä lisää koodia näiden tehtävien suorittamiseen?Joka tapauksessa, jos käytän 20 MHz: n kristallia, se näyttää kiertävän ongelmaa.
-1 PIC16Fxxx: ssä ISR: ää ei voida keskeyttää toisena ISR: nä.
Ah, ok, ei ISR-pinoa.Mutta kuulostaa siltä, että jos tyhjennät ISR: n ja ISR: n käynnistimet ennenaikaisesti ollessasi edelleen ISR: ssä, eikö PIC vain ajaisi ISR: ää uudelleen ja siten 1) ei täydennä alkuperäistä ISR: ää, 2) nälkää pääpiiriä.Pahempaa, jos sillä olisi vain yksi palautusosoite, korvaisiko se sitten alkuperäisen ISR-palautusosoitteen ja tallentaisiko uuden palautusosoitteen kuten jonnekin ISR: ssä?(Olisi hyvä kokeilla simulaattorissa.) Jos kyllä, sillä ei ole enää kelvollista paluuosoitetta eikä se voi palata pääohjelmaan.Olipa asia sitten mikä tahansa, keskeytyksen poistaminen, kun et ole vielä valmis, ei ole hyvä idea.
Katso myös c-koodisi assembler-lähtö.Voit saada käsityksen siitä, kuinka suuri matemaattinen kokonaislukufunktio (fonttihakemiston laskeminen) laajenee.Kokonaisluku matematiikka on nopeaa, mutta jos käytät 32-bittisiä kokonaislukuja tai jopa 16-bittisiä kokonaislukuja, siitä tulee koko joukko 8-bittisiä ohjeita!
"Kuulostaa siltä, että jos tyhjennät ISR: n ja ISR: n käynnistimet ennenaikaisesti ollessasi edelleen ISR: ssä, eikö PIC vain suorittaisi ISR: ää uudelleen ja siten 1) ei täydennä alkuperäistä ISR: ää." Ei. Jos tyhjennät keskeytyksen,keskeytyslippu asetetaan uudelleen ja uusi ISR käynnistyy vasta ensimmäisen päätyttyä.
Rurikred
2018-02-09 03:49:51 UTC
view on stackexchange narkive permalink

Vastaus on jo osoite toisessa viestissä ja kommentissa (div: n "LOT" ja ISR vie paljon aikaa tavaroiden tekemiseen), mutta huomautan, että se tulee töykeäksi.

Olemmeko edelleen kivikaudella (tosissamme)? Suurin osa Internetin esimerkistä antaa sinulle esimerkin, miksi? koska se on yksinkertaisempi ymmärtää ja sitä voidaan muuttaa vapaammin kuin assembler-koodi. Toki assemblerissa kirjoitettu koodi on nopeampi (ehkä joku, joka on viettänyt paljon aikaa koodaamiseen sen kanssa), mutta yksinkertaisen tehtävän suorittamiseksi kääntäjä tekee myös hienoa työtä.

Ongelma IMHO, se on opin ohjelmointitaidon lähde. Jos häntä käytetään ohjelmistojen kehittämiseen, et ymmärrä kuinka monta resurssia ohjelma käyttää, koska työskentelet korkeamman suorituskyvyn laitteiston kanssa, ja ei rajoitetulla kuvalla16.

Div ja mod, vievät varmasti paljon aikaa suoritettavaksi (kuvassa), mutta jos optimoit prosessin, se saattaa joskus olla elinkelpoinen. Esimerkiksi:

  i% 8U -> i&7
i / 8U -> i>>3
 

Jotkut div- ja mod-moduulit voidaan yksinkertaistaa siirtymä- tai logiikkatoiminnolla (joissain tapauksissa numerot ovat mod 2), mutta opit temppuja kokeilemalla asioita ja joskus tekemällä virheitä.

Kun isr-työ on kuormitettu, on parempi ehdotuksen mukaan asettaa lippu ja päivittää pääsisältöisesti tietosi, mutta yrittää parantaa koodiasi, auttaa sinua tulevassa projektissa.

En halua olla ankara, mutta kaikki on arvioitava lopullisen laajuuden mukaan. Tällöin kuvan työmäärän käsittelemisen pitäisi olla oikea tapa osoittaa ongelma, kirjoittamalla uudestaan ​​kokoonpanijaan, mitä saavutat? Monimutkaisuuden lisääminen ei ratkaise sitä, sanomalla esimerkiksi: uunisi (mikro) voi kypsentää 1 pizzaa (n ohje) 10 minuutin välein (ennen kuin ISR kutsutaan uudelleen), mutta pyydät kypsentämään 10 pizzaa samanaikaisesti , antaa todennäköisesti konkreettisen kuvan ongelmasta.

Ole vain tietoinen siitä, mitä käytät.

Olen osittain samaa mieltä, myös kaikki ehdotetut optimoinnit, minkä tahansa C-kääntäjän, joka ei ole roskaa, pitäisi tehdä oletuksena.Kääntäjän tuottama konekoodi on yhtä nopea ja usein nopeampi kuin koodi, jonka yrität kirjoittaa itse.Assembly-kirjoittaminen on kuitenkin järkevää joissakin projekteissa, koska se mahdollistaa erittäin tiukan muistinhallinnan, ja kun ohjelmoit mikro-ohjaimia, muisti voi olla ylellisyyttä, jota sinulla ei ole.
Tässä projektissa käytän noin 10% käytettävissä olevasta muistista, mikä on paljon ottaen huomioon, kuinka vähän muuttujia minulla on.Jos tekisin saman projektin PIC12C508: lla, muistini olisi loppunut.Kokouksessa tämä ei olisi ongelma.


Tämä Q & A käännettiin automaattisesti englanniksi.Alkuperäinen sisältö on saatavilla stackexchange-palvelussa, jota kiitämme cc by-sa 3.0-lisenssistä, jolla sitä jaetaan.
Loading...