Zrychlování frekvence

Jo - necháš doběhnout periodu. To taky jde.
To, co jsem poslal reaguje ihned.
A může to být v leckterých případech i praktičtější.

Teď nevím co jsem na tom, tak dloho řešil. No co se dá dělat.

To se stává. Ty nejjednodušší věci občas člověka napadnou až poslední.
Důležité je, žes to nevzdal, něco ses přiučil a přístí nápad budeš řešit snáze.

To mám jako zábavu na dlouhé zimní večery, ale potrápí to někdy.

Skus toto, ale potrebuješ stiahnuť knižnicu PWM.h

#include <PWM.h> // Na generovanie F priamo na Arduine - potrebuješ stiahnuť
#define pwmPin 9 // Použijeme pin 9 pre generovanie PWM - môžeš zmeniť na ine čislo
#define potPin A3 // Pre potenciometer pin -nemusi byť
int f; // len do frekvencie 32 kHz
void setup() {
pwmWrite(pwmPin, 128); // Hĺbka PWM (hodnota od 0 do 255) Offset
}

void loop() {
for (f = 500; f < 1001; f++) { // f musí byť LONG - inač to neide
pwmWrite(pwmPin, 128); // Hĺbka PWM (hodnota od 0 do 255) Offset - pomer
SetPinFrequency(pwmPin, f); //frekvencia
delay(100);
}
}

Tak mám zase jeden problém, když zmáčknu tlačítko spustím frekvenci, kterou můžu měnit pomocí a/d převodníku. A problém je ten, že když se dostanu na 13.5KHz zhruba (já potřebuju až na 20Khz), tak se to sekne a nic nereaguje. Musím pustit tlačítko a snížit frekvenci. Potom to zase jde. Tak jestli je chyba v kódu nebo mcu už nestíhá? Mám ho taktovanej na 1Mhz.

include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>

#define minf 311 //1600hz

volatile uint16_t frekvence;
volatile uint8_t ZakmitTlacitka1,Tlacitko1_stisknute;

ISR(ADC_vect)
{ 
PORTD = ADCH;			// Output ADCH to PortD
ADCSRA |= 1<<ADSC;		// Start Conversion	
}


ISR (TIMER1_COMPA_vect)
{	

OCR1A=frekvence;

}

ISR (TIMER2_COMP_vect)
{	
	
ZakmitTlacitka1 <<= 1;	
				
	if((PINC & (1 << PC5)) == 0 )
		{        
        ZakmitTlacitka1++;
        }  		
		
	if (ZakmitTlacitka1 == 0xFF)
		{
		Tlacitko1_stisknute = true;	
		}
	else	
		{
		Tlacitko1_stisknute = false;					
		}
	
}

int main(void)
{

DDRB |= (1 << PB1)| (1 << PB4); 
DDRD = 0xFF;
DDRC = 0x00;
PORTC |= (1 << PC5);

ADCSRA = 0b10001010;
ADMUX = 0b01100000;	
ADCSRA |= 1<<ADSC;
 
  TCCR1A |= (1 << COM1A0);
  TCCR1B |= (1 << WGM12) | (1 << CS10);
  TIMSK |= (1 << OCIE1A);
  OCR1A=(minf-ADCH)-30;
  
TIMSK |= (1 << OCIE2);                   
TCCR2 |= (1 << WGM21) | (1 << CS21); 
 OCR2   = 249; 
	
	
 sei(); // povol globalne prerušenia
	
	  while(1)
{	   

	if(Tlacitko1_stisknute==true)
	{ 
		 if (ADCH>0)
	     {
	      TCCR1B |=(1 << CS10);
		  frekvence=(minf-ADCH)-30;			  
	     } 
	     else
	    {
			if((PINB & (1 << PB1)) == 0 )
			{
			TCCR1B &=~(1 << CS10);
			 frekvence=(minf-ADCH)-30;	
			TCNT1=0;
			}
		}
			
	}
	else 
	{ 											
			if((PINB & (1 << PB1)) == 0 )
				{
				TCCR1B &=~(1 << CS10);
				OCR1A=(minf-ADCH)-30;
				TCNT1=0;
				}				 
	}	 
		
}	
			
	return 0;

}

Co znamená, že “se to sekne” ? Přestane generovat kmitočet nebo jenom ten kmitočet nemění ?

Máš tam několik problémových věcí :

  1. AD převodník čteš na několika místech v programu - možná to funguje, ale správně to není. AD převodník přečteš v přerušení a uložíš do proměnné. Vše ostatní pak řešíš přes proměnnou.
  2. Hodnotu AD převoníku sypeš na port D - jaká je hodnota, když to přestane fungovat ?
  3. Při taktu 1 MHz a prescaleru 1 máš hodnotu OCR1A od 281 (těch Tvých nadefinovaných 311 mínus pokaždé odečítaných 30 - další nesmysl v programu - napiš to do definice rovnou…) do 26 (v závislosti na AD převodníku) → přerušení od 3571 do 40000 Hz → frekvence pinu od 1785 do 20000. To je teorie, ale protože procesor potřebuje na zpracování přerušení taky nějaký ten čas, tak mu ho musíš dopřát. Při 25 cyklech hodin má k dispozici jen 25 instrukcí (a to ještě musí být instrukce jednocyklové). Pokud píšeš, že přestává reagovat někde kolem 13,5kHz, pak to znamená, že začíná zpracovávat vlastně už jenom přerušení někde kolem hodnoty OCR1A = 27. To je na zpracování přerušení a programu opravdu hodně málo. Bude potřeba změnit kmitočet procesoru a přepočítat hodnoty pro OCR1A.

ADCH stále vrací hodnoty 0-255 a berme, že pro jednoduchost nebudeš číst celých 10 bitů, ale vždy si vystačíš s 8 bity z ADCH a hodnotu následně vynásobíš podle rozdílu pro minimání a maximální frekvenci → Koeficient = OCR1A(max)-OCR1A(min)/255

Budu brát vzoreček Fmcu/OCR1A/2 a kmitočet od 1600Hz (co máš v poznámce u definice minf) do 20000Hz.

Budeš potřebovat OCR1A v následujících rozsazích :
2 MHz → OCR1A → 626 až 51
4 MHz → OCR1A → 1251 až 101
8 MHz → OCR1A → 2501 až 201

Hodnotu AD převodníku budeš muset vynásobit koeficientem :
2 MHz → 2,26 → 51+255x2,26 = 627 → 627 - 51 → 1597,4Hz - 20kHz
4 MHz → 4,51 → 101+255x4,51 = 1251 → 1251 - 101 → 1600,0Hz - 20kHz
8 MHz → 9,02 → 201+255x9,02 = 2501 → 2501 - 201 → 1600,0Hz - 20kHz
když budu pro jednoduchost výpočtu brát koeficient jenom 9, pak
8 MHz → 9,00 → 201+255x9,00 = 2496 → 2496 - 201 → 1602,0Hz - 20kHz

Jak tak na to koukám, přepnul bych procesor na 8 MHz - je to nejjednodušší na naprogramování a výpočty, protože je mi jasný, že pro násobení těmi desetinnými čísly bys použil float…

P.S.: A když sem vkládáš program, měl bys ho jako kód označit celý a ne po částech…

include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>

#define minf 311 //1600hz

volatile uint16_t frekvence;
volatile uint8_t ZakmitTlacitka1,Tlacitko1_stisknute;

ISR(ADC_vect)
{ 
PORTD = ADCH;			// Output ADCH to PortD
ADCSRA |= 1<<ADSC;		// Start Conversion	
}


ISR (TIMER1_COMPA_vect)
{	

OCR1A=frekvence;

}

ISR (TIMER2_COMP_vect)
{	
	
ZakmitTlacitka1 <<= 1;	
				
	if((PINC & (1 << PC5)) == 0 )
		{        
        ZakmitTlacitka1++;
        }  		
		
	if (ZakmitTlacitka1 == 0xFF)
		{
		Tlacitko1_stisknute = true;	
		}
	else	
		{
		Tlacitko1_stisknute = false;					
		}
	
}

int main(void)
{

DDRB |= (1 << PB1)| (1 << PB4); 
DDRD = 0xFF;
DDRC = 0x00;
PORTC |= (1 << PC5);

ADCSRA = 0b10001010;
ADMUX = 0b01100000;	
ADCSRA |= 1<<ADSC;
 
  TCCR1A |= (1 << COM1A0);
  TCCR1B |= (1 << WGM12) | (1 << CS10);
  TIMSK |= (1 << OCIE1A);
  OCR1A=(minf-ADCH)-30;
  
TIMSK |= (1 << OCIE2);                   
TCCR2 |= (1 << WGM21) | (1 << CS21); 
 OCR2   = 249; 
	
	
 sei(); // povol globalne prerušenia
	
	  while(1)
{	   

	if(Tlacitko1_stisknute==true)
	{ 
		 if (ADCH>0)
	     {
	      TCCR1B |=(1 << CS10);
		  frekvence=(minf-ADCH)-30;			  
	     } 
	     else
	    {
			if((PINB & (1 << PB1)) == 0 )
			{
			TCCR1B &=~(1 << CS10);
			 frekvence=(minf-ADCH)-30;	
			TCNT1=0;
			}
		}
			
	}
	else 
	{ 											
			if((PINB & (1 << PB1)) == 0 )
				{
				TCCR1B &=~(1 << CS10);
				OCR1A=(minf-ADCH)-30;
				TCNT1=0;
				}				 
	}	 
		
}	
			
	return 0;

}

Takhle je to mnohem přehlednější - i když to odsazování je katastrofa (pokud to tedy nevzniklo během kopírování).

Ten program mám pro testování krokáče a s mikrokrokováním jsem měl 1600hz jako 60ot/min. Každopádně je to prasečina. to potom předělám.To počítání jsem tam měl zase kvůli něčemu co už tam dávno není to příjde potom pryč.

Právě, že generuje už jen ten určitý kmitočed a A/D převodník nereaguje, vždycky přestane na stejné hodnotě, kdy je OCR1A= 36 to je zhruba nějakých 13500Khz. Každopádně ještě stíhá reagovat na to tlačítko zda zmáčknuto nebo ne.Každopádně, když jsem zkoušel tam dát navrdo ten kmitočet 20Khz a na PORTD zobrazovat hodnotu A/D, tak to jen generovalo frekvenci a A/D nereagoval.
Takže to už teda bude tím, že nestíhá a bude potřeba změnit kmitočet? Jsem právě furt řešeil program, že mám špatně.

Když jsem si to přečetl, tak je to zcela logické a datasheet mi to i potvrdil. Při této hodnotě dochází k tomu, že procesor začne zpracovávat přerušení od Compare Match. To se děje takhle :

  1. Procesor shodí bit od Compare Match a začne zpracovávat program pro jeho obsluhu.
  2. Mezitím se nahodí třeba bit od AD převodníku.
  3. Jenže během zpracování Compare Match se tento bit znovu nahodí.
  4. Procesor ukončí ubsluhu přerušení Compare Match a vrátí se do hlavního programu.
  5. Vykoná jednu instrukci hlavního programu a jde zpracovat další přerušení.
  6. Jsou nahozené bity od Compare Match a AD převodníku - Compare Match má vyšší prioritu, tak jde na něj - tady se vracíme do bodu 1)

Proto se Ti zdá, že to nefunguje. Jakmile pustíš tlačítko, procesor se po pár cyklech v programu dopracuje až k nestisknutému tlačítku a zastaví Timer. V tuhle chvíli zpracuje přerušení od AD převodníku a pracuje dál.

Mimochodem - Přijdeš na to (nebo víš), jak násobit celé číslo desetinným bez použití float (při násobení konstantou je to o něco jednodušší) ?

Třeba nějak takhle by to šlo? Si 9.02 převedu na celé číslo
Vysledek = ((long)A/Dhodnota*902/100)+201;

Přesně. :+1:

Jen bych to (pro jistotu) zapsal takhle : Vysledek = (((long)ADhodnota*902)/100)+201;

To abys dal překladači jasně najevo, že jako první má udělat násobení 902 a pak dělení 100.

Myslíš, že je dobré řešení tehle cyklus v hlavním programu? Udávám tím kolik kroků má udělát motor.

for(;PocetKroku<200;)
{
TCCR1B |= (1 << CS11);
}
Počet kroku snímám takhle, vždycky přičtu jedničku na náběžnou hranu v přerušení

ISR (TIMER1_COMPA_vect)
{
OCR1A=frekvence;

if(PINB & (1 << PB1))
{
PocetKroku++;
}
}

Trochu nechápu, co má ta smyčka za úkol. Vždyť jen kolem dokola zapínáš zapnutý časovač…

V OCR přerušení nemusíš OCR1A opakovaně zapisovat, pokud hodnotu neměníš. Jinak počítání kroků takhle lze bez problémů udělat.

Tam je ještě tláčítko a když ho zmáčknu projede cyklus, zastaví se a zase čeká na zmáčknutí tlačítka. Měl jsem to nejdřív přes podmínku if else, ale tohle mi příšlo jednodušší. Tak právě co je lepší.

Z těhle dvou kousků kódu mi to smysl nedává, zkus sem dát celý kód a pak budu moct říct…

Teď mám v tom bordel, musím ten program trochu upravit, tak až to nějak dám dohromady hodím to sem.

Jsem si tak dělal nějakou pitomost, motor se točí sem tam a překvapilo mě, že když jsem nedal do přerušení znovu tu hodnotu OCR1A=499, tak to natočení neodpovídalo jedné otáčce. Tak teď řeším kdepak bude zakopanej pes abych příště neřešil, že mi zase něco nefunguje. Jestli teda hodnotu OCRxx musím znovu načíst po zastavení čítáče časovače nebo chyba je někde jinde,

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

#define povol TCCR1B |= (1 << CS11)
#define zakaz TCCR1B &=~(1 << CS11)

volatile uint16_t PocetKroku;

ISR (TIMER1_COMPA_vect)
{	
OCR1A=499;
if(PIND & (1 << PD5))
	{
	PocetKroku++;
	}	
}


int main(void)
{

DDRD |= (1 << PD5)|(1 << PD6);

TCCR1A |= (1 << COM1A0);
TCCR1B |= (1 << WGM12);
TIMSK |= (1 << OCIE1A);
OCR1A=499;	
			                      	
OSCCAL = 0xAE;	

sei(); 
		
while(1)
{		
	
	PORTD|=(1 << PD6);
	  	
	for(;PocetKroku<1600;)
	{
	povol;    
	}
	
	zakaz;		
	_delay_ms(1000);	
	PocetKroku=0;			 				
	PORTD &=~(1 << PD6);
	 
	for(;PocetKroku<1600;)
	{
	povol;    
	}
	
	zakaz;		
	_delay_ms(1000);	
	PocetKroku=0;
	  
}	
	
	
	return 0;
}
  1. OCR1x stačí zapsat jen jednou - je to registr, který se sám nemění.
  2. Čtení dvoubytové proměnné (PocetKroku) chvilku trvá, takže když nesynchronizuješ kontrolu této proměnné s její změnou v přerušení, může se stát, že během načítání se proměnná změní. Nejjednodušší je to v tomto případě pomocí sleep. Procesor se uspí a probudí se přerušením. Provede kontrolu proměnné, což stihne celé dříve, než prijde další přerušení a opět se uspí. Druhou možností je použít ATOMIC_BLOCK, který zakáže pro daný kus kódu přerušení. To je ale zároveň tak trošku riskantní a samozřejmě tento kód musí být co nejkratší.

Zkus to takhle :

#define F_CPU 8000000.0

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

#define povol TCCR1B |= (1 << CS11)
#define zakaz TCCR1B &=~(1 << CS11)

volatile uint16_t PocetKroku;

ISR (TIMER1_COMPA_vect)
{
    if(PIND & (1 << PD5))
    {
        PocetKroku++;
    }
}


int main(void)
{

    DDRD |= (1 << PD5)|(1 << PD6);

    TCCR1A |= (1 << COM1A0);
    TCCR1B |= (1 << WGM12);
    TIMSK |= (1 << OCIE1A);
    OCR1A=499;
    
    OSCCAL = 0xAE;

    sei();
    set_sleep_mode(SLEEP_MODE_IDLE);
    sleep_enable();
    
    while(1)
    {
        
        PORTD|=(1 << PD6);
        PocetKroku=0;
        povol;
        while(PocetKroku<1600) sleep_cpu();
        while(PIND & (1 << PD5)) sleep_cpu();
        zakaz;
        TCNT1 = 0;

        _delay_ms(1000);
  
        PocetKroku=0;
        PORTD &=~(1 << PD6);
        povol;
        while(PocetKroku<1600) sleep_cpu();
        while(PIND & (1 << PD5)) sleep_cpu();
        zakaz;
        TCNT1 = 0;

        _delay_ms(1000);        
    }
    
    
    return 0;
}

Na druhou stranu, jedndušší je to asi napsat takto :

#define F_CPU 8000000.0

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

#define povol TCCR1B |= (1 << CS11)
#define zakaz TCCR1B &=~(1 << CS11)

volatile uint16_t PocetKroku;
#define UDELEJ_KROKU 1600

ISR (TIMER1_COMPA_vect)
{
    if(PIND & (1 << PD5))
    {
        PocetKroku--;
    }
    else
    {
        if (!PocetKroku)
        {
            zakaz;
            TCNT1 = 0;
        }
    }
}


int main(void)
{

    DDRD |= (1 << PD5)|(1 << PD6);

    TCCR1A |= (1 << COM1A0);
    TCCR1B |= (1 << WGM12);
    TIMSK |= (1 << OCIE1A);
    OCR1A=499;
    
    OSCCAL = 0xAE;

    sei();
    set_sleep_mode(SLEEP_MODE_IDLE);
    sleep_enable();
    
    while(1)
    {
        
        PORTD|=(1 << PD6);
        PocetKroku=UDELEJ_KROKU-1;
        povol;
        while(PocetKroku) sleep_cpu();

        _delay_ms(1000);
  
        PocetKroku=UDELEJ_KROKU-1;
        PORTD &=~(1 << PD6);
        povol;
        while(PocetKroku) sleep_cpu();

        _delay_ms(1000);        
    }
    return 0;
}

V tomhle případě není ani ta synchronizace s přerušením úplně nutná.

Obě verze programu Ti vygenerují 1600 impulzů, vteřinu počkají, přepnou směr a opakují.

Tak jsem zase o něco chytřejší na co si dát pozor (jinak to už funguje, jsem udělal ten druhý způsob. Je mi trošku víc logičtější).
S tímhle programem kde mám tlačítko by problém být snad neměl. Tady by mělo být dost času ne. Nebo bych to měl udělat taky.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>

#define povol TCCR1B |= (1 << CS11)
#define zakaz TCCR1B &=~(1 << CS11)

volatile uint16_t frekvence=499, PocetKroku;
volatile uint8_t ZakmitTlacitka1,Tlacitko1_stisknute;

ISR (TIMER1_COMPA_vect)
{	

if(PIND & (1 << PD5))
	{
	PocetKroku++;
	}	
}

ISR (TIMER2_COMP_vect)
{	
	
ZakmitTlacitka1 <<= 1;	
				
	if((PINC & (1 << PC0)) == 0 )
		{        
        ZakmitTlacitka1++;
        }  		
		
	if (ZakmitTlacitka1 == 0xFF)
		{
		Tlacitko1_stisknute = true;	
		}
	else	
		{
		Tlacitko1_stisknute = false;					
		}
		
}


int main(void)
{

char stav_tlacitka=1;

DDRD |= (1 << PD5);
DDRB = 0xff; 
DDRC = 0b00000000; 
PORTC |= (1 << PC0);

TCCR1A |= (1 << COM1A0);
TCCR1B |= (1 << WGM12);
TIMSK |= (1 << OCIE1A);
OCR1A=frekvence;	
	
	                      
TIMSK |= (1 << OCIE2);                   
TCCR2 |= (1 << WGM21) | (1 << CS22); 
OCR2   = 249;     

OSCCAL = 0xA2;

sei(); 
		
	  while(1)
	  {		
	

	  	
		if(Tlacitko1_stisknute==true)
		{  					
			
			if((PocetKroku==1600)&&((PIND & (1 << PD5)) == 0 ))
			{ 	
			zakaz;		
			}
			else
			{
			povol;
			}
		}
						            
		else 
		{		
				if((PIND & (1 << PD5)) == 0 )
				{												
				zakaz;
				PocetKroku=0;
				TCNT1=0;
				}									
		 } 
				 				
	  }	
	
	
	return 0;
}

V tomhle případě s tím problém nebude, ale měl by ses snažit o to, aby program byl synchronizován s přerušením vždy a aby byl co možná nejefektivnější. Je třeba počítat s tím, že ne vždycky bude procesor řešit takhle jednoduchou úlohu, ale na téhle jednoduché úloze se můžeš naučit, jak využít procesor na maximum. V tomhle případě program bude fungovat, ale jak vidíš, procesor s ním má plno práce. Tady máš ještě obrovský potenciál, o kolik lze program zefektivnit, zjednodušit a i zpřehlednit.

Pro lepší efektivitu se nauč :

  1. U počítání pulzů, čekacích časů apod. nastavit požadovanou hodnotu a v přerušení při nenulové hodnotě odečítat. Test na 0 je pro procesor/program jednodušší, než test na určité číslo a není nezbytně nutná synchronizace s přerušením. Bez synchronizace s přerušením je test na nulu prostě nula nebo cokoliv, ale při kontrole na určité číslo se to číslo během kontroly může změnit.
  2. V hlavním programu používat sleep - samozřejmě, pokud program využívá přerušení ať už od časovače nebo periferií atd. Částečně tím program s přerušeními synchronizuješ a procesor nemusí intenzivně pracovat na zbytečných cyklech, když nedochází k žádné změně (obzvlášť pokud bys stavěl něco, co běží z baterky)
  3. Požadované/přednastavené hodnoty zapisovat na začátku jako definice. Obrovsky to zvýší přehlednost a hlavně, pokud máš takovýchto čísel v programu víc, změnou jednoho čísla to změníš všude, kde potřebuješ (nemusíš nic hledat) a při změně se nespleteš
#define KROKU_NA_OTACKU 180
#define POCET_OTACEK 180
  • Když měníš počet otáček, změníš jedno číslo a to se změní všude v programu, ale když máš v programu jen čísla, jak víš, která 180-ka je počet otáček a která počet kroků na otáčku…

A nauč se konečně vkládat do <code> program celý. Nechápu, jak děláš to, že máš program rozdělený na 2x. Když ho kopíruju z/do Atmel Studia, tak ho sem vložím celý jako kód. Když bych chtěl dosáhnout toho, aby se mi kód rozdělil, musel bych ho vkládat na dvakrát. Nebo prostě kód označuješ po vložení špatně…

Není to takhle lepší ? Je to ten tvůj kód jen CTRL-C/CTRL-V z/do Atmel Studia :

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>

#define povol TCCR1B |= (1 << CS11)
#define	zakaz TCCR1B &=~(1 << CS11)

volatile uint16_t frekvence=499, PocetKroku;
volatile uint8_t ZakmitTlacitka1,Tlacitko1_stisknute;

ISR (TIMER1_COMPA_vect)
{

    if(PIND & (1 << PD5))
    {
        PocetKroku++;
    }
}

ISR (TIMER2_COMP_vect)
{
    
    ZakmitTlacitka1 <<= 1;
    
    if((PINC & (1 << PC0)) == 0 )
    {
        ZakmitTlacitka1++;
    }
    
    if (ZakmitTlacitka1 == 0xFF)
    {
        Tlacitko1_stisknute = true;
    }
    else
    {
        Tlacitko1_stisknute = false;
    }
    
}


int main(void)
{

    char stav_tlacitka=1;

    DDRD |= (1 << PD5);
    DDRB = 0xff;
    DDRC = 0b00000000;
    PORTC |= (1 << PC0);

    TCCR1A |= (1 << COM1A0);
    TCCR1B |= (1 << WGM12);
    TIMSK |= (1 << OCIE1A);
    OCR1A=frekvence;
    
    
    TIMSK |= (1 << OCIE2);
    TCCR2 |= (1 << WGM21) | (1 << CS22);
    OCR2   = 249;

    OSCCAL = 0xA2;

    sei();
    
    while(1)
    {
        

        
        if(Tlacitko1_stisknute==true)
        {
            
            if((PocetKroku==1600)&&((PIND & (1 << PD5)) == 0 ))
            {
                zakaz;
            }
            else
            {
                povol;
            }
        }
        
        else
        {
            if((PIND & (1 << PD5)) == 0 )
            {
                zakaz;
                PocetKroku=0;
                TCNT1=0;
            }
        }
        
    }
    
    
    return 0;
}

Malý tip, třeba bude užitečný - zákmit tlačítka plus možnost provedení akce na náběžnou/sestupnou hranu :

Nejdřív si nadefinujeme typ pro tlačítko (tohle se většinou píše do .H souboru)

typedef struct StatusTlacitkaStruct
{
    char TlacitkoStisknute:1;
    char NabeznaHrana:1;
    char SestupnaHrana:1;
    uint8_t OsetreniZakmitu;
} StatusTlacitkaStruct;

Upravime definici pro tlačítko

volatile StatusTlacitkaStruct Tlacitko1;

A upravíme kontrolu tlačítka v přerušení

ISR (TIMER2_COMP_vect)
{
    
    Tlacitko1.OsetreniZakmitu <<= 1;
    
    if((PINC & (1 << PC0)) == 0 )
    {
        Tlacitko1.OsetreniZakmitu++;
    }

    Tlacitko1.NabeznaHrana = false;
    Tlacitko1.SestupnaHrana = false;

    if (Tlacitko1.OsetreniZakmitu == 0xFF)
    {
        if (Tlacitko1.TlacitkoStisknute == false) Tlacitko1.NabeznaHrana = true;
        Tlacitko1.TlacitkoStisknute = true;
    }
    else
    {
        if (Tlacitko1.TlacitkoStisknute == true) Tlacitko1.SestupnaHrana = true;
        Tlacitko1.TlacitkoStisknute = false;
    }
    
}

V programu pak můžeš kontrolovat

    if (Tlacitko1.TlacitkoStisknute) // Stisknuté tlačítko
    
    if (Tlacitko1.NabeznaHrana) // Okamžik stisku tlačítka
    
    if (Tlacitko1.SestupnaHrana) // Okamžik uvolnění tlačítka

Totéž pak klidně i Tlacitko2, Tlacitko3 …