Attiny 13 časovač

Zdravím
Už jsem tady dlouho nebyl odradilo mě ta změna vizáže tohoto fóra no nic změna je asi život…
Potřeboval bych na švába Attiny 13 program kde jedou hodiny a určitou dobu kterou si sám určím mě nahodí pin( který je volný ten pin aby nekolidoval programovacími piny) a vydržel třeba 2 hodiny a to v jeden den několikrát.Mám kompiler GCC nějak díky

Ahoj.
Takový program není problém, ale :

  1. Co to přesně znamená “kde jedou hodiny”
  2. Jak si určíš dobu/doby, kdy se nahodí pin.
  3. Chceš, aby délka sepnutí byla pevná nebo nastavitelná ?

Když chceš pin, který nebude kolidovat s programovacími piny, tak na celém IO zbývají 2.
Máš nějaký zvláštní důvod k tomuto požadavku (tedy samozřejmě kromě toho, aby se Ti výstup nespínal během programování) ?

  1. v attiny 13
  2. změním v programu podle situace
  3. ano chci aby délka byla nastavitelná
    A co se týká programovací prostředí nejlepší v arduinu mám i atmel studio 6 ale v něm jsem dlouho nedělal musel bych se do toho vpravit díky

Dobře - zeptám se přesněji : jaké hodiny más na mysli ?
Něco jako RTC nebo jenom odpočítávání doby ?
V případě RTC musíš nějak řešit přesnost, protože interní RC oscilátor zrovna přesností a teplotní stabilitou nevyniká.
Jako, jestli to chceš, že přes pin spustíš časování, tak tam interní RC stačí. Při délce výstupního pulzu 2 hodiny se odchýlíš maximálně o pár vteřin.

Arduino nepoužívám, pokud to budu psát, tak Atmel Studio 7.

Ty vnitřní hodiny ,tam krystal by se tam ani nevlezl do čeho to zamýšlím , sekunda sem sekunda tam na tom nezáleží

Čí to byl nápad přejít na tento styl fóra ? :sleepy:

OK. Jak si představuješ start pulzu ? Při zapnutí ? Nebo nějakým pinem (tlačítkem) ? Nastavení času jsem myslel třeba trimrem na druhém volném vstupu MCU, když už nechceš používat programovací piny.

Já bych nekomplikoval tím trimrem já to potřebuji co nejmenší prostě jen attiny13 a to je vše díky

/*
 * Meloun1_AtTiny13A_PulsNaPinu.cpp
 *
 * Created: 21.5.2022 16:54:51
 * Author : Balů
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/atomic.h>

// Přednastavení délky impulzu
#define HourPreset (0)
#define MinPreset  (0)
#define SecPreset  (3)

//Pokud chceš pulz aktivní v 0, odkomentuj následující řádek.
//#define PulseActiveLow

#define KeyIn (PINB&(1<<PINB3))

#define PulsePin (PORTB4)
#if defined(PulseActiveLow)
#pragma message "Output active low"
    #define PulseOn (PORTB &= (~(1<<PulsePin)))
    #define PulseOff (PORTB |= (1<<PulsePin))
#else
#pragma message "Output active high"
    #define PulseOn (PORTB |= (1<<PulsePin))
    #define PulseOff (PORTB &= (~(1<<PulsePin)))
#endif

#define OneSecondPreset (72) // Frekvence přerušení 73,2421875 - počítá se 72 až 0 => 73
unsigned char OneSecondTimer;
#define OneSecondCorrectionPreset (3) // Kompenzace toho cca 0,25 impulzu
unsigned char OneSecondCorrection;

typedef struct PulseTimerStruct
{
    unsigned char Hour;
    unsigned char Min;
    unsigned char Sec;
} PulseTimerStruct;

volatile PulseTimerStruct PulseTimer;

unsigned char KeyDebounce;

ISR(TIM0_OVF_vect)
{
    KeyDebounce <<= 1;
    if (!(KeyIn)) KeyDebounce++;
    
    if (!OneSecondTimer)
    {
        if ((PulseTimer.Hour)||(PulseTimer.Min)||(PulseTimer.Sec))
        {
            OneSecondTimer = OneSecondPreset;
            if (OneSecondCorrection)
            {
                OneSecondCorrection--;
            }
            else
            {
                OneSecondTimer++;
                OneSecondCorrection = OneSecondCorrectionPreset;
            }
            PulseOn;
        }
        else
        {
            PulseOff;
        }

        if (PulseTimer.Sec)
        {
            PulseTimer.Sec--;
        }
        else
        {
            if ((PulseTimer.Hour) || (PulseTimer.Min))
            {
                PulseTimer.Sec = 59;
                if (PulseTimer.Min)
                {
                    PulseTimer.Min--;
                }
                else
                {
                    PulseTimer.Min = 59;
                    PulseTimer.Hour--;
                }
            }
        }
    }
    else
    {
        OneSecondTimer--;
    }
}

void HW_Setup( void )
{
#if defined(PulseActiveLow)
  PORTB = 0xFF;
#else
  PORTB = ~(1<<PulsePin);
#endif
  DDRB = (1<<PulsePin);
  

  TCCR0A = 0;
  TCCR0B = (0<<CS02)|(1<<CS01)|(1<<CS00);
  TIFR0 = 255;
  TIMSK0 = (1<<TOIE0);

  sei();
  set_sleep_mode(SLEEP_MODE_IDLE);
}

int main(void)
{
    HW_Setup();

    while (1)
    {
        sleep_enable();
        sleep_cpu();
        sleep_disable();

        if (KeyDebounce == 0xFF)
        {
            ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
            {
                PulseTimer.Hour = HourPreset;
                PulseTimer.Min = MinPreset;
                PulseTimer.Sec = SecPreset;
            }
        }
    }
}

Připojením vstupu B3 (tlačítko, externí signál) na GND (log. 0) se zapne impulz na pinu B4 (aktivní podle nastavení v programu) a délka pulzu se nastaví na požadovanou dobu. Pokud je impulz aktivní, tak se jen čas na požadovanou dobu nastaví (prodlouží).

Program je tak jednonuchý, že nebylo třeba ho dělit do více souborů. Vytvoř si projekt v Atmel Studiu a program tam zkopíruj. Pojistky procesoru nech v default - tím pádem by procesor měl běžet na 1,2MHz.

Pulz se dá nastavit na 1s až 255h, 255min, 255sekund - nehlídám, jestli si nastavíš začátek na nesmysly…

Na druhou stranu, když chceš třeba 150 minut nebo vteřin, tak to nemusíš přepočítávat. Jediné, čím jsi limitovaný je, že se jedná o 8-bitovou hodnotu, tedy maximálně 255.

P.S.: Do programu se vloudila malá chybička, byla opravena 21.5.2022 v 11:07 - pokud si někdo zkopíroval prográmek předtím, zkopírujte si tento.

1 Like

Ahoj. Měl bych dotaz. Líbí se mi ATtiny85 a chtěl jsem ho využít jako snímač otáček s výstupem na LCD16x2 , ale všechny programy nelze zkompilovat.Ty jsi v těchto MC jednička.Mohl by jsi mi pomoct?
Na komunikaci s LCD je PB0 a PB2 , a tak mne napadlo využít PB1 jako snímač impulzu, a na LCD zobrazovat otáčky.
Jestli by jsi mi poradil bych bych velice rád.
Díky předem.
Zdenek-

Tak samozřejmě, že není problém pomoct nebo poradit. Trochu nerozumím tomu “všechny programy nelze zkomplikovat”. Hoď sem zapojení, jak si představuješ, že by to mělo (mohlo) vypadat, případně jestli máš nějakou část programu, se kterým potřebuješ poradit, ať se máme od čeho odrazit.

Ahoj,

začínám s ATTINY a mám prosbu. Potřebuji jednoduchý program pro aplikaci, kdy po sepnutí tlačítka se počka na uvolnění tohoto tlačítka (sestupná hrana) dojde k odpočtu 100ms a poté se sepne výstup na kterém bude tranzistor, relé nebo jiný spínací prvek. Pak program skončí a až do odpojení a opětovného připojení napájení na nic nereaguje. Ideálně by mohlo být kontrolováno po celou dobu před inicializací, že je výstup v nule a po dobu této kontroly (jistoty, že je výstup 0) by mohla blikat LED. Mám attiny13a programátor USBASP - ISP link: USBASP - ISP programátor pro ATMEL | LaskaKit a PC s UBUNTU 22 s avrdude.
Jedné co jsem zatím vyzkoušel je toto: ATtiny13 - blinky with delay function - Łukasz Podkalicki

Děkuji, za pomoc.

Žádný problém - hoď sem, co už jsi naprogramoval a poradíme/pomůžeme.

#include <avr/io.h>
#include <util/delay.h>

#define F_CPU 1000000UL
#define LED1_PIN PB0 // PB0 as a LED 1 pin
#define LED3_PIN PB2 // PB3 as a LED 3 pin
#define BTN5_PIN PB4 // PB4 as Button 5 pin

int
main(void)
{

    /* setup */
    DDRB = 0b00000101;              // set LED pin as OUTPUT
    PORTB = 0b00010000;             // set all pins to LOW

    /* loop */
    while (1) {
            if ((PINB & 0b00010000) == 0) {
                    _delay_ms(1000);
                    PORTB &= 0b11111110; }
                 else {
                    PORTB |= 0b00000001; }

    PORTB ^= _BV(LED3_PIN);
    _delay_ms(100);

    }

}

Začínáš programovat, tak je dobré vědět (a řídít se tím) pár důležitých věcí.


VŽDY používej v programu nadefinované konstanty :

Takže správný (a především mnohem přehlednější) zápis je :

Pokud přesuneš jednu z LED na jiné místo, jen přepíšeš definici a nemusíš hledat, kde jsi s LEDkou pracoval.

Kromě toho :

Pracoval jsi tady s LEDkou nebo jsi dělal něco jineho ?
Co třeba takhle :

                    PORTB &= ~(1<<LED1_PIN); }

Je to přehlednější ?


Zápis DDR už jsme si vysvětlili, teď ještě - nepoužité piny nenechávej jako plovoucí a NIKDY je nepřipojuj externě natvrdo na VCC nebo GND (pokud se nepletu, je to i v datasheetu). Přepnutím pinu na výstup a jeho vyzkratovánám (pošleš 0 na pin připojený na VCC nebo 1 na pin připojený na GND) můžeš zničit procesor (v lepším případě “jen” odpálíš daný pin). Takže správně by to mělo být tahle :

    /* setup */
    DDRB = ((1<<LED1_PIN)|(1<<LED3_PIN));              // set LED pin as OUTPUT
    PORTB = ~((1<<LED1_PIN)|(1<<LED3_PIN));              // set all pins to LOW

Nastaví výstupní piny (v tomhle případě jenom piny pro LEDky) do log. 0, ostatní do log. 1. Piny přepnuté jako vstupní tím připojí interním pull-up rezistorem na VCC - opět : informace najdeš v datasheetu.

A ještě jedna věc : NIKDY nepřipojuj LED diody bez ochranného odporu. Dělá to spousta “bastlířů” a dá se to i najít ve spoustě “návodů” na Internetu. To, že to procesor a pin vydrží, ještě neznamená, že je to správně. Ten pin přetěžuješ nad výrobcem stanovené limity a je jenom otázkou času, kdy to vzdá…


Mimochodem - číst datasheety je jedna z nejdůležitějších věcí, když vytváříš program pro MCU. Dozvíš se tam, to procesor umí, jak pracuje, dozvíš se třeba jak komunikovat s čidlem teploty (například DS18B20), dozvíš se, jak komunikovat s LCD displejem atd. Námitky typu “stáhnu si knihovnu” neberu. Stahováním knihoven se nic nenaučíš. To lze provozovat až v okamžiku, kdy už něco umíš. Pak to ušetří práci a čas a v případě, že něco nefunguje, jsi schopen přijít na to, proč to tak je. Už jsem tady na fóru opravoval knihovnu pro čidlo teploty Dallas. Při frekvenci procesoru 1MHz šlapala, ale jak se procesor přepnul na 8MHz (interní hodiny), tak knihovna nefungovala. A to nemluvím o tom, kdyby dotyčný přepnul na krystal a dal tam 20MHz.
A když už píšu o tom čtení v datasheetu, tak se do něj zkus podívat a zjistíš, že tento procesor neběhá na kmitočtu 1MHz.

Uf, to je vydatná odpověď, musel jsem si ji přečíst několikrát. Každopádně jsem za ni velmi vděčný. Mám pár dotazů.

DDRB = ((1<<LED1_PIN)|(1<<LED3_PIN)); // set LED pin as OUTPUT
Jak z tohoto zápisu Assembler pozná jaký pin je co? Pokud tedy není LED1_PIN zrovna nějak předem na první daný, to by byla náhoda, protože jsem si to označení vymyslel.

PORTB &= ~(1<<LED1_PIN); }
Co v tomto zápisu děla vlnovka?

A k těm datasheetům, mám osobní problém, protože nezvládáte angličtinu. Těch pár slov jako vstup, výstup nebo když, jinak atd ještě zvládnu. Ale porozumět textu v datasheetu je hodně mimo moji úroveň. Takže jsem odkázán na příklady použití co najdu nebo, když se mi podaří najít někoho jako jsi Ty, kdo je ochotný se mnou ztrácet čas a má trpělivost mi to vysvětlovat. V práci se starám o PLC od SIEMENSu, ale Step7 se od tohot značně liší. Na Step7 mám navíc několik školení, kde mi to polopaticky vysvětlili a mám kolegu, který se v nich hrabe skoro dvacet let, takže mám koho se ptát.

  1. Není Assembler, ale Cčko.
  2. Od toho jsou definice :

Tohle není Arduino - tady neříkáš překladači, na kterém pinu daný vstup nebo výstup je. Třeba u ATmega8 je bit 0 brány B (PORTB0) u DIP nebo SOIC pouzdra na pinu 14, ale u pouzfra TSOP na pinu 12. Tady definuješ, na jakém bitu je připojena LED (nebo cokoliv jiného). Například :

#define LED_CERVENA 0 // Červená LED je na bitu 0 (lhostejno jaká brána)
#define LED_ZLUTA 0 // Žlutá LED je na bitu 0 (lhostejno jaká brána)
#define LED_ZELENA 5 // Zelená LED je na bitu 5 (lhostejno jaká brána)

A teď použití :

DDRA = ((1<<LED_ZELENA)|(1<<LED_CERVENA));
DDRB = (1<<LED_ZLUTA);
PORTA = ~((1<<LED_ZELENA)|(1<<LED_CERVENA));
PORTB = ~(1<<LED_ZLUTA);

Vlnovka znamená bitová negace.
Z výše uvedeného pak plyne, že červená a zelená je na bráně A, bity 0 a 5 → A0, A5 a tomu odpovídají piny podle použitého pouzdra (z datasheetu). Totéž platí pro žlutou LED, která je na B0.

Po dosazení definic pak zápis z pohledu překladače vypadá takhle :

DDRA = ((1<<5)|(1<<0));
DDRB = (1<<0);
PORTA = ~((1<<5)|(1<<0));
PORTB = ~(1<<0);

tedy

DDRA = 0b00100001;
DDRB = 0b00000001;
PORTA = ~(0b00100001);
PORTB = ~(0b00000001);

a konečně

DDRA = 0b00100001;
DDRB = 0b00000001;
PORTA = 0b11011110;
PORTB = 0b11111110;

Angličtina, datasheety …

Nikdo po Tobě nechce, abys zvládal angličtinu na nějaké vysoké úrovni, ale čtení datasheetů, dokumentace apod bys měl na pozici PLC programátora zvládat. Dneska máš spoustu různých možností, jak si potřebnou část dokumentace přeložit, Když jsem začínal já, museli jsme to řešit slovníkem. Dneska máš online překladače atd a časem se Ti to v hlavě usadí samo. A existují i jazykové kurzy. Když Tě zaměstnavatel poslal na školení STEP7, mohl by nabídnout i jazykové kurzy (nebo příspěvek na něj) jako benefit. Chce to snahu. Ani STEP7, pokud vím, tak v češtině není (maximálně němčina).


Rádi tady poradíme/pomůžeme, ale je nutné vidět snahu. Začínáš (nebo se snažíš začít) v Cčku, tak se klidně ptej, vysvětlíme, poradíme, ale hledat a učit se musíš sám. Nikdo Ti tu nebude vysvětloval základy Cčka (nebo jakéhokoliv jiného jazyka). Od toho jsou kurzy, kde na to je čas. Tady pak budeš řešit věci specifické pro MCU a Tvůj domácí projekt.

Děkuji za vysvětlení, takto polopaticky to snad nejde nepochopit. K tomu Step7, tam je možnost přepnout do formátu zobrazení LAD a pak to programování vypadá spíše jako kreslení schémat. Je to celé graficky nebo spíše blokově znázorněno a pro mě jako pro elektrikáře je to automaticky ihned srozumitelné. Angličtinu k tomu vlastně vůbec nepotřebuji.
Co se tyče datasheetu, ty se samozřejmě přeložit snažím, ale 90% věci o kterých se tam píše je pro mne Španělská vesnice. Také jsem si našel pár školení programování v Céčku na YouTube, ale asi jsou různé způsoby zápisu, protože nic z toho mi v kompiler nesežral.
Ještě bych se rad vrátil tomu svému projektu. Myslím si, že tak jak jsem to napsal a poté i s těmi úpravami je to nedostačující. Momentálně se to chová tak, ze je třeba tlačítko držet dokud nevyprší delay a pak se teprve výstup, nyní reprezentován LED1. A jakmile se tlačítko uvolní, tak výstup spadne. Já potřebuji aby v i případě krátkého plusu na vstup (tlačítko) se počkali na uvolnění tlačítka a pak se po 100ms aktivoval výstup pro LED1. Nevím jak udělat detekci sestupné hrany na tlačítku a jak zajistit aby se výstup aktivoval i když už tlačítko vlastně stisknuté není. Ve Step7 bych si se sestupnou hranou tlačítka zapsal memory byte a v dalším kroku bych aktivoval timer tímto memory bitem. Pak bych po vypršení času aktivoval výstup. Nakonec bych program ukončil Stopem. Tady jsem nebyl schopen něco takového objevit.

LAD i FBD je spíš o kreslení schémat.


Ne všechno, co se v datasheetu píše je pro každý projekt důležité. V každém případě pro to, co jsi napsal je v důležité si z datasheetu zjistit, na jaké frekvenci při jakém nastavení pojistek procesor pracuje. To je důležité pro ten delay, co tam máš použitý. Mimochodem - delay delší, než jednotky ms nemá v programu co dělat. Během toho 1000ms dlouhého delay totiž program naplno maká na delayi, ale procesor navenek na vteřinu zamrzne. To je potřeba řešit jinak, ale s tím Tě zatím zatěžovat nebudu.


Cčko má syntaxi jenom jednu. Všechno je to o tom, že když Ti kompilátor vypíše nějakou chybu, musíš vědět, co po Tobě chce, jinak se dál neposuneš. A další věc - chceš něco vysvětlit a nenapsal jsi v jakém kompilátoru to překládáš.


Tohle není o tom v jakém jazyku to napíšeš, ale o tom, že máš v hlavě nějaký algoritmus, ale do programu jsi napsal něco úplně jiného. Zkusíme to projít.


Zkontroluješ vstup :

Když je aktivní, vteřinu počkáš a na výstup B0 pošleš 0 :

když ne, tak na výstup B0 pošleš 1 :

překlopíš výstup na pinu B2:

počkáš 100ms :

a jdeš na začátek …


Tlačítko nemusíš držet celou vteřinu. To se jen tak navenek zdá, protože jakmile stiskneš tlačítko a hned pustíš, tak procesor díky vteřinovýmu delay “zamrzne”, pak aktivuje výstup, překlopí B2, počká 100ms a jde znova na začátek. Takže výstup je navenek aktivovaný jen těch 100ms díky tomu druhému delay.

Zkus schválně přehodit ty dva řádky a uvidíš, jak se to chová :

                    PORTB &= 0b11111110;
                    _delay_ms(1000); }

Pro tenhle prográmek si bohatě vystačíme s delayema, ale určitě budeš potřebovat seznámit se pro začátek přinejmenším s přerušeními a časovači. Uvědom si, že řádek

_delay_ms(100);

pro procesor znamená (ve výrobním nastavení pojistek ATtiny13) až 120000 instrukcí - to je 234x celá programová paměť procesoru.

Ahoj,

tak jsem zkusil ty dva řádky přehodit a pak uplně odstranit a stále se to chová tak, že to tlačítko musí být stisknuto déle než vteřinu aby došlo k aktivaci výstupu pro LED1.

Ohledně kompileru, nainstaloval jsem avrdude do linuxu a používám to dle návodu kde kód píši do souboru “main.c” a pak spouštím dávkový soubor “compile” . v dávkovém souboru je:

avr-gcc -mmcu=attiny13 -std=gnu99 -Wall -Os -o main.elf main.c
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avr-size --mmcu=attiny13 --format=avr main.elf
sudo avrdude -B 150kHz -c usbasp -p t13 -U flash:w:“main.hex”:a

Jestli z toho pozáš o jaký kompiler jde netuším, já to určitě nepoznám.

Když jsem zkoušel jiné návody z internetu, tak mi kompiler píše chyby např.:

main.c:8:10: error: unknown type name “bool”
volatile bool powerState = false;

nebo

main.c: In function “setup”:
main.c:16:22: error: “INPUT” undeclared (first use in this function)
pinMode(buttonPin, INPUT);

tento konktrétně jsem našel na github.com pod názvem ATTiny-PushButton-ON-OFF-Controller

Nevím co je špatně a většina takto stažených příkladů mi nejde zkompilovat s obdobnými chybami.

No - pokud jsi ty 2 řádky přehodil nebo úplně odstranil, tak se to určitě nemohlo chovat stejně. Jediná možnost je, že to pak vyhodilo nějaké chyby a nepřeložilo se to. Pak AVRDUDE do procesoru nahrál původní program. Při přehození nebo odstranění těch dvou řádek bych řekl, že jsi neopravil umístění } a tím pádem se program nepřeložil.

Nebylo by mnohem lepší kvůli přehlednosti místo tohohle zápisu :

použít tenhle ? :

            if ((PINB & 0b00010000) == 0)
            {
                        _delay_ms(1000);
                        PORTB &= 0b11111110;
            }
            else
            {
                        PORTB |= 0b00000001;
            }

Kompiler je to GCC, takže možnost použít C (tvůj případ) nebo C++. AVRDUDE je jenom ovládací program pro programátor.


Neznámý typ bool → základy Cčka. Je nutné používat H soubory (v podstatě něco jako knihovny) pro možnost použití některých typů a vestavěných funkcí.


Nejenom, že není nadefinovaný pojem INPUT, ale muselo to vyhodit i chybu na pinMode. To je Arduino - nadstavba nad Cčkem a musel bys mít nainstalované Arduino prostředí.


Já píšu programy v Atmel Studiu 7 (dneska už se to jmenuje Microchip Studio). Tam nejsou Arduino nadstavby, zato vím, co procesor dělá. Když jsi psal, že začínáš v C, tak jsem počítal s tím, že začínáš v C. Měl by sis o Cčku nejdřív něco nastudovat a tady se ptát. Jak jsem psal - tady Tě Cčko učit nebudeme. Takhle jednoduchý prográmek, co chceš, je v Cčku práce na 5 minut, když procesor nebude řešit nic jinýho a nevadí extrémně dlouhé delaye v programu, ale je třeba znát alespoň základy.