Zdravím, potřeboval bych naprogramovat takovýhle průběh v céčku. Jestli to je vůbec možné na atmega8 s taktovací frekvencí 8Mhz. Zatím jsem se zasekl na tom jak to spočítat a hodit do mcu. Jestli to dělat přes přerušení nebo na to vzít Pwm se střídou 1:1 a nějak lineárně měnit frekvenci od 500hz do 1Khz
Vezmeš ten 16-bitový čítač, nastavíš ho do CTC režimu a nastavíš OCRx tak, aby čítač přetekl 1000x za sekundu. Při každém přerušení překlopíš výstupní pin => získáváš tak 500Hz na výstupu. Cílem pak bude mít OCRx nastavené tak, aby čítač přetekl 2000x za sekundu => tím máš na výstupu 1kHz. Rozdíl hodnoty pro registr OCRx rozpočítáš tak, abys jej rovnoměrně rozprostřel do celé sekundy. Tenhle čítač ale nemůžeš použít pro časování pravidelných činností, protože měníš jeho frekvenci. Vezmeš si druhý čítač a nastavíš ho podle potřeby - v tomhle případě je ideální vzít 8-bitový čítač, spustit ho bez prescaleru => tím pádem získáváš časování 8MHz/256(8-bitový čítač => 0-255)=31 250 Hz. To znamená, že získanou hodnotu buď rozdělíš na 31250 kousků (což počítám, že nebude potřeba) nebo spočítáš po kolika přerušeních od tohoto druhého čítače budeš muset odečíst od OCRx jedničku. A POZOR - musíš zachovat synchronizaci - tedy nastavíš si nějaký flag a jakmile ten proměnný čítač přeteče a vyvolá přerušení, tak s překlopením pinu také odečte 1 od daného OCRx a flag zase vynuluje.
Upraveno : Lepší vzít 8-bitový čítač v CTC režimu a nastavit ho na 40000Hz (nebo i jen 4000Hz) - tím pádem získáš přesnější rozdělení těch kroků pro změnu frekvence.
Ještě jsem se chtěl optat, bych potom potřeboval počítat jednotlivé impulsi. Například až tam bude napočítaná hodnota 500 impulsů udělej něco, mě napadlo si tam dát zpětnou vazbu na vstup int0, ale to bych musel obsadit zase další pin.
V těch čítačích a časovačích nějak tápů. Se snažim zprovoznit krokáč přes driver. Ono to funguje, ale jak chci větší rychlost, tak už to bohužel z fleku nezvláda.
Proč zpětnou vazbu na čítač ? Vždyť impulzy generuješ pomocí SW (v přerušení překlápíš pin). Čítače používáš jen na časování hrany pro překlopení. Klidně si tam můžeš přidat počítání impulzů (například sestupná hrana = dokončený impulz) a pokud napočítáš do hodnoty, kterou požaduješ, nastavíš si nějaký flag a v hlavním programu, jakmile zjistíš, že je tento flag nahozený, tak provedeš akci a flag shodíš. Tou akcí samozřejmě může být cokoliv. Ale pozor ! Pokud to budeš vázat na ten proměnný čítač, pak impulzy půjdou různou rychlostí. Pokud tím chceš řešit nějaké periodické akce (například přepínání znaků multiplexovaného 7-segmentového displeje), tak na to se tento postup používá běžně, jen to musíš navázat na ten čítač, jehož časování se nemění.
Zcela běžně takto řeším treba delay.
Například Blikání LEDkou :
void Blikání LEDkou(void)
{
if (LEDka_Sviti)
{
Rozsvit_LED();
}
else
{
Zhasnit_LED();
}
delay_ms(500);
}
int main(void)
{
while(1)
{
Blikání LEDkou();
if (Stisknute_Tlacitko)
{
Prepni_Druhy_Vystup();
}
}
}
Když to uděláš takhle, tak procesor nebude dělat nic jinýho, než se motat v delayi a bude se ti zdát, že přepínání výstupu tlačítkem nefunguje nebo funguje nějak podivně.
V následujícím příkladu si nadefinuju proměnnou TimeoutProBlikani :
volatile unsigned int TimeoutProBlikani;
A v přerušení časovače (dejme tomu nastavenému na 1 kHz => 1 ms) pak tuto proměnnou zmenšuju :
ISR(Prerusovaci vektor od timeru)
{
if (TimeoutProBlikani) TimeoutProBlikani--;
}
V hlavním programu to pak vypadá takhle :
void Blikání LEDkou(void)
{
if (TimeoutProBlikani == 0)
{
if (LEDka_Sviti)
{
Zhasnit_LED();
}
else
{
Rozsvit_LED();
}
TimeouProBlikani = 500;
}
}
int main(void)
{
while(1)
{
Blikání LEDkou();
if (Stisknute_Tlacitko)
{
Prepni_Druhy_Vystup();
}
}
}
V tomhle případě pak LEDka také bliká stejně jako v předchozím případě, ale druhý výstup reaguje na tlačítko okamžitě a kdykoliv.
Krokový motor má nějakou maximální rychlost, při které ještě zvládá krokovat, než začne tzv. ztrácet impulzy. Nevím, na co chceš tu změnu frekvence, ale jestli to chceš kvůli tomu, abys mohl zrychlovat s krokovým motorem, tak by možná bylo nejlepší, aby sis připojil trimr na AD převodník a k nějaké výchozí hodnotě OCRx přičetl/odečetl hodnotu načtenou z AD převodníku. Můžeš tak externě (trimrem) řídit rychlost impulzů v tom rozsahu 500-1000Hz.
Ta frekvence byla jen příklad. S tím trimrem by to šlo potom realizovat i přes 555. Já to chci mít trochu automatický aby se motor roztočil a zastavil v určitý poloze. Právě řeším problém s tím zrychlováním a zpomalováním aby neztrácel kroky, když bych tam hned naprasil velkou frekvenci.
To nic nemění na tom, že pulzy nemusíš počítat přes zpětnou vazbu a další přerušení, ale můžeš si je počítat během generování. Například tlačítkem spustíš pulzy a ty skončí třeba po 200 krocích (motor udělá 200 kroků) a pak se pulzy vypnou a budou čekat na další stisk tlačítka.Tím, že si generuješ pulzy softwarově je můžeš počítat nebo i třeba je v době, kdy je nepotřebuješ, tak je nebudeš generovat. Všechno záleží jenom na tobe. Nebo třeba 200 pulzů poslat, pak třeba 500 pulzů stát a znova. Prostě v programu se tvé fantazii meze nekladou.
Jen vědět jak na to. Trochu už jsem něco napsal, ale postupuju pomalu.
Žádný učený z nebe nespadl. Zkoušej a když nebudeš vědět, klidně se ptej.
Už jsem si chtěl koupit arduino kde je na řízení celá knihovna, ale to by bylo zase moc jednoduchý a pro mě to je svazující to arduino
Stáhnout a nainstalovat knihovnu umí každý, tím se nic nenaučíš. Kromě toho Arduino nasype do procesoru mraky balastu, které tam zbytečně zabírá místo a ve velké většině případů to ani nepoužiješ.
Klidně si arduino kup. Je to hotová destička ke které stačí připojit napájení a už běží. Pak už jen stačí lehce připojovat periférie třeba na prkénku. Většina arduin se dá stále programovat přes ISP pomocí třeba USBasp, takže z toho lehce uděláš prázdný čip na základní desce do kterého si nasypeš co chceš. Třeba i program v asm. Jestli píšeš v AVRstudiu (MicrochipStudio) a nevadí ti po zapnutí, že chvilku běží bootloader arduina, tak lze nastavit programování přes avrdude za pomoci původního bootloaderu a připojuješ se už jen usb kabelem. Já jsem tyhle varianty využíval běžně.
Dnes už v rámci lenosti a zlepšení programovacího prostředí (hlavně našeptávání při psani), tak nějak přecházím na arduino úplně i za cenu plno smetí v programu. Moc neprogramuji a na to občasný sem tam něco využiju už hotové knihovny.
Zdravím, tak jsem o něco pokročil, pohrál jsem si s čítačema a udělal jsem provizorní výpočet a jestli to teda dobře chápů???
Orcx pro 500hz je 999
Orcx pro 1000hz je 499
To mám mezi nima rozdíl 500, tím pádem 1s je 1000ms a z toho plyne, že hodnotu OCRx budu měnit každe 2ms. Takže si vezmu ten druhý čítač ve kterém budu měnit každe 2ms hodnotu OCRx a tím pádem bych měl mít za 1s na výstupu 1000hz.
Je má teorie správná?
Teorie je správná, jen nezapoměň na synchronizaci.
A hodnoty OCRx závisí na hodinách procesoru prescaleru pro čítač. Takže pokud máš hodiny procesoru nastavené na 1MHz a prescaler na 1, tak i ty hodnoty souhlasí.
A ten druhý čítač si teda můžu nastavit na 500hz?? To by teda mělo být při 1MHZ, prescaler na 8 je OCRX 124. Zatím dělám jen pokusy, tak mám hodnoty nastavený tak, aby se to dobře počítalo.
To je úplně jedno, ale úpravu OCRx pro změnu frekvence musíš provést v tom čítači, co tu frekvenci generuje pomocí přerušení od Compare Match. V čítači pro měnění frekvence jen nastavíš nějaký příznak a v přerušení od Compare Match toho čítače, co generuje frekvenci překlopíš pin (jako pokaždé), upravíš hodnotu a příznak shodíš. Jinak by ses mohl trefit nešikovně a v daném cyklu by čítač mohl dojet až na 65535 a neproběhlo by přerušení od Compare Match - vznikla by chyba při generování požadované frekvence. Popsal jsem Ti postup hned v mém prvním příspěvku.
Takže by to mohlo být nějak takhle?
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint16_t hodnota=999,n;
ISR (TIMER1_COMPA_vect)
{
OCR1A=hodnota;
}
ISR (TIMER2_COMP_vect)
{
n++;
if (n==2)
{
if (hodnota>499)
{
hodnota-- ;
}
else
{
hodnota=999 ;
}
n=0;
}
}
int main(void){
DDRB |= (1 << PB1);
TCCR1A |= (1 << COM1A0);
TCCR1B |= (1 << WGM12) | (1 << CS10);
TIMSK |= (1 << OCIE1A);
TIMSK |= (1 << OCIE2);
TCCR2 |= (1 << WGM21) | (1 << CS21);
OCR2 = 124;
sei();
while(1)
{
}
return 0;
}
Myslel jsem to sice maličko jinak, ale i takhle možno.
A otázka - Timer 2 má nastavený na 1kHz a v přerušení zbytečně počítáš průchody, abys dostal 500Hz. Proč nenastavíš OCR2 na 249 a nevyhodíš to “n” ? Tedy - pokud na něco dalšího potřebuješ ten 1kHz, pak je to jasné a OK.
To máš pravdu, n tam mám zbytečně bez něj to bude pro mě jednodušší.
Tak mě zase něco napadlo a přemýšlím jestli to bude problém nebo ne.
Bych si tam přidal tlačítko. Při zmáčknutí by to zrychlovalo až by to dosáhlo tý požadovaný frekvence a tam by to bylo dokud by se zase tlačítko nepustilo. Po odmáčknutí by to plynule zpomalilo do zastavení. A zda při této aplikaci řešit zákmity tlačítka nebo ne. Protože v jednu chvíli nebude vědět zda má zrychlovat nebo zpomalovat. A když bych chtěl potom třeba počítat kolik pulsů dostane krokovej motor, tak by to vadilo ne.
To co jsi naprogramoval je triviální záležitost a procesor prakticky nic nedělá. Řešení zákmitů tkačítek je otázka pár instrukcí, to taky není problém. Nevím, proč by něco mělo vadit počítání impulzů pro krokový motor - pulzy generuješ v přerušení a tam je můžeš taky načítat. Stejně budeš muset řešit zrychlování, zpomalování a vůbec děje, které jsou pro procesor pomalé. Ono i 100ms je pro ten procesor v podstatě dlouhá doba. Takže to, co píšeš, že bys chtěl je velice jednoduchá úloha. Stačí si jen uvědomit, co, kdy procsor dělá a jak tam přidat další věci, abys dosáhl toho, co bys chtěl. Ostatně podívej se do toho příkladu, který jsem Ti napsal výše s blikáním LEDkou a zárověň přepínáním druhé LEDky pomocí tlačítka (není tam řešení zákmitů).
Ukázka řešení zákmitů pro 1 tlačítko přidané do toho tvého programu
ISR (TIMER2_COMP_vect)
{
n++;
if (n==2)
{
if (hodnota>499)
{
hodnota-- ;
}
else
{
hodnota=999 ;
}
n=0;
}
ZakmitTlacitka1 <<= 1;
if (Pin_Tlacitko1_stisknute)
{
ZakmitTlacitka1++;
}
if (ZakmitTlacitka1 == 0xFF)
{
Tlacitko1_stisknute = true;
}
else
{
Tlacitko1_stisknute = false;
}
}
Zákmity tlačítek (a nejenom zákmity, ale i náběžné/sestupné hrany) řeším právě v periodickém přerušení, kde nastavuju příznaky pro jednotlivá tlačítka (stisknuto, nestisknuto, náběžná, sestupná hrana, doba stisku a další protřebné věci) a v hlavním programu pak už jenom s těmito příznaky pracuju.
Pak už záleží jenom na tom, jak často to periodické přerušení přichází. Při 1ms (1kHz) stačí tlačítko kontrolovat každé destáté přerušení (10ms) a tlačítko je bráno jako stisknuté po 8 průchodech (80ms) v řadě. V tomhle případě lze kontrolovat 10 tlačítek (při každém přerušení jiné) bez toho, abys výrazně zasáhl do délky přerušení. To, že zpracování přerušení musí být co možná nejkratší snad ani psát nemusím.