delay funktio

Kuinka saan tehtyä assemblerilla delay funktion (esim 200ms) c -ohjemaan?
152149
Äänestä

Muut lukivat myös

Vastaukset 15

Vanhimmat

Loopissa vaan tarpeeksi monta kertaa esim NOP (No OPeration) käskyä. Sisäkkäisiäkin looppeja voi käyttää.

Tietysti tällöin ohjelma jököttää 200ms tekemättä mitään hyödyllistä..

Äänestä
  • Paitsi että tuolla tavalla tehty 'hidastaja' vie eri ajan eritehoisilla prosessoreilla.

    Äänestä

  • nop kirjoitti:

    Paitsi että tuolla tavalla tehty 'hidastaja' vie eri ajan eritehoisilla prosessoreilla.

    oletetaan että kyseessä on joku sulautettu rauta jossa on vakio taajuinen kide

    tosin nuo looppaamalla tehdyt hidastajat on vähän kökköjä..mitä sitä suotta prosessoritehoa semmoiseen käyttämään? Löytyyhän niitä ajastimiakin.

    Äänestä

RTC-keskeytykselle. Keskeytysaliohjelma laskee
sopivan määrän tulleita keskeytyksiä ja nostaa
lipun.
Sitten asetat RTC:n keskeyttämään 1024
kertaa sekunnissa ja keskeytysmaskin säädät
niin, että RTC-keskeytykset sallitaan.
Tuskin säästät kovin paljon tekemällä tämän
assemblerilla.
Tasan millisekunnin monikertaa tällä tavalla
ei saada, mutta aika lähelle kuitenkin.

Äänestä

sleep(sekunteja) toimii unixilla (unistd.h?). windowsilla sitten..jotain muuta.

Äänestä
  • uhh, sori, assylla piti.. silmät ristissä..

    Äänestä

Delay-funktio voidaan luoda kääntämällä assemblerilla yksinkertainen silmukka, joka odottaa tietyn ajanjakson ennen kuin se siirtyy eteenpäin ohjelmassa. Tässä esimerkissä luodaan 200ms viive käyttämällä x86-arkkitehtuurin NASM-assembleria:

section .data

delay_time equ 200      ; Viiveaika 200ms

section .text
global delay

delay:
    mov ecx, delay_time   ; Aseta viiveaika rekisteriin
  delay_loop:
    dec ecx               ; Vähennä rekisterin arvoa yhdellä
    jnz delay_loop        ; Toista silmukka, kunnes rekisteri on 0
    ret                   ; Palauta ohjaus ohjelman päävaiheeseen


Tässä assembler-koodissa delay-funktio luo 200ms viiveen suorittamalla silmukan, joka vähentää rekisterin arvoa yhdellä ja odottaa, kunnes rekisterin arvo on nolla. Sitten funktion ohjaus palautetaan takaisin C-ohjelmaan. Voit kutsua tätä delay-funktiota C-ohjelmassasi seuraavasti:

extern void delay();    // Esittele delay-funktio

int main() {
    // Aja delay-funktio viiveen luomiseksi
    delay();

    // Jatka ohjelman suorittamista
    return 0;
}

Äänestä
  • Meinaatko että aloittaja odottelee vastaustasi 19 vuotta?

    Äänestä

  • Anonyymi00008 kirjoitti:

    Meinaatko että aloittaja odottelee vastaustasi 19 vuotta?

    No, hyvää kannattaa odottaa.

    Äänestä

Ohjelmassa on / tulee olla jokin tick timer -keskeytys, josta viiveet ym. ajoitukset on helppo johtaa. PC-koneissa tällainen keskeytys tikitti aikoinaan n. 55 ms, nyttemmin 1 ms välein. Jossakin sulautetussa järjestelmässä keskeytysintervalli voi olla lyhyempikin, mutta sen kerrannaisten ja nanosekuntiluokan viiveiden (nop-luuppi tms.) väliin jää yleensä hankalasti toteutettava viiveen pituus, joka tyypillisesti ratkeaa vain omalla h/w-timerilla.

Äänestä
  • Erittäin lyhyet odottelut voi kannattaa tehdä nop-loopilla. Muistelisin, että avr:ssä nop-käsky on tasan 1 kellojakso ja hyppy siihen takaisn vie 3 kellojaksoa, siispä yksi kierros looppia vie 4 kellokiteen jaksoa. Hiukan pidemmät odottelut voi sitten tehdä h/w-timerilla ilman keskeytysrutiinia. Sitten kun aletaan puhua alle 100-200 keskeytyksestä sekunnissa voi ottaa keskeytysrutiinin käyttöön. Esimerkiksi avr:llä joskus tuli laskettua, että kääntäjä tunkee rutiiniin 68 kellojaksoa koodia ilman, että oma koodi vielä pyörähtää lainkaan: Tämä vie melkoisesti jo kellojaksoja. Nopean rutiinin teko vaatii siis koodin läpikäymistä ja kannattaa mitata rutiinin tyhjäkäyntinopeus eli kuinka nopea kello tarvitaan, jotta rutiinia voidaan kutsua ylipäätään x kertaa sekunnissa.
    http://www.rjhcoding.com/avr-asm-delay-subroutine.php

    avr-gcc toteutuksessa on valmiiksi assembler-toteutus uint8_t tyyppiselle viiveelle. Muistelisin, että tätä on käytetty uint16_t tyyppisessä hyväksi - joskaan rutiini ei ole enää kellojakson tarkka toisin kuin uint8_t:lle oleva rutiini. Tässäkin tosin on yllätyksiä: inline-koodi edellyttää, että ei käytetä size-optimointia, joka lisää viiveisiin funktiokutsujen ajat. Siispä uint16_t loopin pituus menee heti rikki, jos koodin optimointia muutetaan!
    https://www.nongnu.org/avr-libc/user-manual/group__util__delay__basic.html

    Äänestä

  • Anonyymi00011 kirjoitti:

    Erittäin lyhyet odottelut voi kannattaa tehdä nop-loopilla. Muistelisin, että avr:ssä nop-käsky on tasan 1 kellojakso ja hyppy siihen takaisn vie 3 kellojaksoa, siispä yksi kierros looppia vie 4 kellokiteen jaksoa. Hiukan pidemmät odottelut voi sitten tehdä h/w-timerilla ilman keskeytysrutiinia. Sitten kun aletaan puhua alle 100-200 keskeytyksestä sekunnissa voi ottaa keskeytysrutiinin käyttöön. Esimerkiksi avr:llä joskus tuli laskettua, että kääntäjä tunkee rutiiniin 68 kellojaksoa koodia ilman, että oma koodi vielä pyörähtää lainkaan: Tämä vie melkoisesti jo kellojaksoja. Nopean rutiinin teko vaatii siis koodin läpikäymistä ja kannattaa mitata rutiinin tyhjäkäyntinopeus eli kuinka nopea kello tarvitaan, jotta rutiinia voidaan kutsua ylipäätään x kertaa sekunnissa.
    http://www.rjhcoding.com/avr-asm-delay-subroutine.php

    avr-gcc toteutuksessa on valmiiksi assembler-toteutus uint8_t tyyppiselle viiveelle. Muistelisin, että tätä on käytetty uint16_t tyyppisessä hyväksi - joskaan rutiini ei ole enää kellojakson tarkka toisin kuin uint8_t:lle oleva rutiini. Tässäkin tosin on yllätyksiä: inline-koodi edellyttää, että ei käytetä size-optimointia, joka lisää viiveisiin funktiokutsujen ajat. Siispä uint16_t loopin pituus menee heti rikki, jos koodin optimointia muutetaan!
    https://www.nongnu.org/avr-libc/user-manual/group__util__delay__basic.html

    "Esimerkiksi avr:llä joskus tuli laskettua, että kääntäjä tunkee rutiiniin 68 kellojaksoa koodia ilman, että oma koodi vielä pyörähtää lainkaan"

    Enin osa tuosta 68 kellojaksosta lienee rekisterien talletusta ja palautusta. Jos keskeytyskoodista kutsuu mitä tahansa funktiota (toisessa moduulissa), kääntäjällä ei ole muuta vaihtoehtoa kuin työntää kaikki mahdollisesti muuttuvat rekisterit pinoon. Etenkin AVR:ssä työrekistereitä riittää.

    Keskeytyskoodit kannattaakin kirjoittaa mahdollisimman yksinkertaisiksi, mielellään ilman ensimmäistäkään funktiokutsua ja jättää varsinainen tekeminen "pääohjelmaan". Aina tämä ei ole mahdollista ja silloin tuo 68 kellojakson overhead on vain hyväksyttävä.

    Puheena olevassa tick timer -keskeytyksessä asialla on oleellinen merkitys. Esimerkiksi millisekunnin resoluutiolla ja 8 MHz kellotaajuudella pelkkä 68 kellojakson overhead käyttää lähes prosentin CPU-kapasiteetista.

    Äänestä

Miksi haluat tehdä hidastajan assyllä?
Eihän siinä ole mitään optimoitavaa.
C-kääntäjä tekee saman delay tai delay_ms funktioilla.

Jos haluat opiskella assyä, niin voit tulkita c-kääntäjän tekemää koodia.
Pitää vain kääntää listauskoodi mukaan.

Äänestä
  • Vaan voiko kääntäjään luottaa? Useamman kerran pitänyt täsmätä c-kääntäjän epämääräistä tuotosta, joka ei ota huomioon esim. funktio-kutsujen overheadia vaan laskee viiveeseen mukaan pelkän nop-käskyn suoritusajan loopissa. 1000ms voi sitten olla esim. 1036ms, mikä ei käy useinkaan. Joskus löytyy jopa assembler-toteutus koodista, joka ei kuitenkaan toimi.

    Äänestä

Et mitenkään, koska Assembler ei ole ohjelmointikieli vaan kääntäjä. Kieli on nimeltään Assembly.

Äänestä

Kommentoi aloitusta


C -ohjelma kirjoitti:

Kuinka saan tehtyä assemblerilla delay funktion (esim 200ms) c -ohjemaan?

Anonyymi
5000

Ilmoita asiaton sisältö

  • Sisältää materiaalia, joka on loukkaavaa, herjaavaa, rasistista, uhkailevaa tai ahdistelevaa.

  • Materiaali sisältää nimiä, yhteystietoja tai muita henkilökohtaisia tietoja. Julkisuuden henkilöistä, julkisissa viroissa toimivista ihmisistä sekä yritysten vastuuhenkilöistä saa keskustella työhön liittyen. Myös yksityiselämään liittyvistä asioista voi keskustella siltä osin, kuin niistä on julkisesti kerrottu.

  • Sisältää lapsille haitallista tai heiltä kiellettyä materiaalia. Seksuaalinen sisältö on sallittua aiheeseen tarkoitetuilla palstoilla, joiden ikäraja on 18 vuotta.

  • Lainvastaista sisältöä voi olla mm. yksityiselämää loukkaavan tiedon levitys, kiihottaminen kansanryhmää vastaan, laiton uhkaaminen, alaikäisiin liittyvä seksuaalinen sisältö/grooming, petokset, identiteettivarkaudet, kunnianloukkaukset ja tekijänoikeusrikkomukset.

  • Sisältö ei liity palstan tai keskusteluketjun aiheeseen.

  • Viesti ei ole suomea tai ruotsia (pl. International Forums), se sisältää mainontaa, se sisältää tekijänoikeuksin suojattua materiaalia, se on massapostitus tai jokin muu syy.

Jaa keskustelu