Zdravím.
Zkouším obsluhu stopek pomocí tlačítek. Ty pak zobrazuji na 7segmentový display. Nedaří se mi bohužel zprovozni změnu hodnot při zdvihu tlačitka.
Dotaz: Jak mohu ovlvnit jestli program zareaguje na stisk nebo uvolnění tlačítka?
Něco jsem tu už našel, ale moc jsem z toho nezmoudřel.
Děkuji.
[code]
…
if ((PIND & (1<<PD6))== 0) start= 0; //start stopek
//========PROBLEM===========
//----nastaveni casu
if(start== 1)
{
if ((PIND & (1<<PD5))== 0) minj++;
/{
_delay_ms(100);
while (! (PIND & (1<<PD5))) minj++;
_delay_ms(100);
}/
if ((PIND & (1<<PD4))== 0) secj++;
/{
_delay_ms(100);
while (! (PIND & (1<<PD4))) secj++;
_delay_ms(100);
}/
}
//==========================
if (start== 0)
{
if(count>50)
{
if (mind == 0 && minj == 0 && secd == 0 && secj == 0) break; //stopky konci
if (minj == 0 && secd == 0 && secj == 0)
{
if (mind != 0) minj = 10;
mind–; //zmena desitek minut
}
if (secd == 0 && secj == 0)
{
minj–; //zmena jednotek minut
secd = 6;
}
if (secj == 0)
{
secj=9;
secd–;
} else secj–;
count=0;
}
else count++;
}
}
return 0;
}[/code]
ATMEGA644P, WinAVR-20100110
administrator: přejmenováno z "Nastavení stopek tlačítky"
Jak myslíš při zdvihu tlačítka, co by to mělo dělat? Běžně se reaguje jen na stisk. Jen je potřeba ošetřovat zákmity. V nejjednodušším případě tak, že se po reakci na stisk přidá prodleva, tím se zabrání opakované reakci při zákmitu. Dokonaleji tak, že se po uvolnění chvíli nereaguje na stisk (opakovaný stisk resetuje čítač prodlevy) a pak se dá tak zajistit rozlišení delší prodlevy na začátku a pak rychlé opakování.
Možná se špatně ptáš, protože v tom kódu Ti jedou stopky jen když držíš tlačítko Start. Vzhledem k tomu, že u funkce Start není vhodné opakování a je potřeba přesnou reakci, tak to je ten druhý případ, kdy je potřeba přesnější obsluha. Např. tady funkce na stisk tlačítka Start, kterou program volá z hlavní smyčky každých 10 ms:
#define BTNHOLD 10 // doba podrzeni tlacitka = 100 ms
char StartCitac = 0;
char StiskStart(void)
{
char old = StartCitac;
if (StartCitac > 0) StartCitac--;
if ((PIND & (1<<PD6))== 0) StartCitac = BTNHOLD;
return ((StartCitac > 0) && (old == 0));
}
A v hlavní smyčce pak:
[code]char StopkyBezi = 0;
char Citac10ms = 0;
char SecJ = 0;
char SecD = 0;
char MinJ = 0;
char MinD = 5;
void main ()
{
…
while (1)
{
if (StiskStart()) StopkyBezi = !StopkyBezi;
if (StopkyBezi)
{
Citac10ms++;
if (Citac10ms == 100)
{
Citac10ms = 0;
SecJ--;
if (SecJ < 0)
{
SecJ = 9;
SecD--;
if (SecD < 0)
{
SecD = 5;
MinJ--;
if (MinJ < 0)
{
MinJ = 9;
MinD--;
if (MinD < 0)
{
SecJ = SecD = MinJ = MinD = 0;
StopkyBezi = FALSE;
}
}
}
}
}
}
else
{
// zde podobne obsluha ostatnich tlacitek
// pri kazde zmene udaje nulovat Citac10ms !
}
// zobrazeni casu
DispTime();
// prodleva 10 ms
_delay_ms(10);
}
Cílem je, aby se hodnota při stisku změnila vždy jen o +1. Měl jsem na mysli stisk tlačítka a ne uvolnění. Omlouvám se. Start mám nastaven na start= 1, když tedy stisknu tlačítko na PD6, vše funguje jak má.
Zkusil jsem hodnoty měnit pomocí uvedeného kódu. Dochází k tomu, že se navýší o jedna a stopky se spustí bez stisku jakéhokoliv tlačtka.
Když nepoužívám změnu hodnot, pak stopky jdou spusti stiskem PD6 až k nule.
while (start == 1)
{
if ((PIND & (1<<PD5))== 0)
{
minj++;
_delay_ms(500);
}
if ((PIND & (1<<PD4))== 0)
{
secj++;
_delay_ms(500);
}
if ((PIND & (1<<PD6))== 0) start= 0; //start stopek
}
Celý program je v příloze. Možná nevidím chybu někde jinde.
Děkuji main.c (1.84 KB)
Jeden z triků na ošetření zákmitů, který používám je následující :
lds Temp, Stav_Tlacitka ; Temp je registr, Stav_Tlacitka je pozice v RAM
clc
sbic PINx, Py ; Brána x, pin y
sec
rol Temp
sts Stav_Tlacitka, Temp
Stav tlačítka se mění až při hodnotách 0x00 nebo 0xFF. Vše ostatní je stav se zákmity.
Na zjištění, zda je tlačítko právě stisknuto, drženo nebo právě uvolněno je možné použít něco v tomto smyslu :
lds Temp, Stav_Tlacitek
sts Stav_Tlacitek_Minule, Temp
rcall Zjisti_Aktualni_Stav ; Tady se na pozici Stav_Tlacitek vrátí aktuální hodnoty.
; Vlastní zpracování stavů - tlačítko je stisknuto při log. 0
lds Temp, Stav_Tlacitek_Minule
lds Temp2, Stav_Tlacitek
sbrc Temp2, 0 ; Pokud je bit0 = 1
rjmp Nestisk_tlacitka_0 ; Tlačítko není stisknuté
sbrs Temp, 0 ; Pokud je bit0 = 0
rjmp Drzeni_tlacitka_0 ; Tlačítko už bylo stisknuté minule
; Zpracování stisku tlačítka 0
rjmp Tlacitko_0_konec
Drzeni_tlacitka_0:
; Zpracování držení tlačítka 0
rjmp Tlacitko_0_konec
Nestisk_tlacitka_0:
sbrc Temp, 0 ; Pokud je bit0 = 1
rjmp Stisk_tlacitka_0_konec ; Tlačítko už bylo nestisknuté minule
; Zpracování puštění tlačítka
Tlacitko_0_konec:
Jen jsem zatím nezpohodlněl natolik, abych začal pro mikrokontrolery používat C-čko.
Dlouho jsem se tomu bránil, ale nakonec mě šéf přesvědčil. S těmi neustálými změnami procesorů není možné stíhat přeučovat se na stále jiné instrukční sady a hlavně - v assembleru je vývoj mnohem pomalejší, pracnější, dá se nasekat více chyb. Proto je optimum - pokud to ničemu nevadí, psát v C a jen v kritických bodech doplňovat assembler (většinou stačí inline). Novější procesory mají už docela dost paměti, tak místo není tak kritické. A většina operací probíhá pomalu, kdy by se optimalizace neprojevila. Dřívější překladače překládaly dost hloupě, ale dnešní (jako gcc nebo Keil) už překládají hodně optimalizovaně.
Tak, já zatím používám jen 8-bitový AVRka, takže si s assemblerem vystačím, krom toho mám vlastních knihoven tolik, že se mi do C-čka ani nechce. Ne, že bych ho už nezkoušel, ale prostě se mi to zdá jako jít s kanónem na vrabce. Jakmile bych sahnul po jiným mcu, kde nemám knihovny napsaný, pak by byl přechod na C-čko pochopitelně na pořadu dne…
Balů tohle je trochu zrádné. Člověku to pak brání v tom, zkoušet jiné nové věci, přejít na něco jiného, protože má vybudované “to své” a v nových oblastech by to neměl a tak se mu do toho nechce (znám to velmi dobře, např. se mi nechce do Linuxu, protože pod Windows mám moře svých knihoven). Možná bys mohl, jako duchovní rozcvičku, občas zkoušet zabrousit i do neznámých oblastí, např. pomalu přepisovat své knihovny do C a vybudovat si i tam to své zázemí, jinak Tě to bude svazovat a nikdy si nevyzkoušíš třeba ARMy. Když mě v práci nutí dělat v oblastech co neznám, tak sice nadávám, ale nakonec zpětně jsem rád, protože jinak bych ustrnul na svém písečku a nepohnul se dál.
Podle datasheetu jsou na portech interní pull-up rezistory. Měl jsi na mysli tohle? V simulátoru jsem to nezkoušel, ale po nahrání do mcu funguje start, nastevení času i odpočet. Jen nějaká možnost přesnějšího čekání než za použítí delay? Jak se eventuelně ošetruje ten pull up?
na portoch su sice pull-up rezistory, ale treba ich nastavit, defaultne su odpojene. napr pull-up na PORTD, pin6 nastavis ako PORTD = 0b01000000;
Cas sa da najpresnejsie kontrolovat Timerom. Timer kontroluje takt procesora, takze vies uplne presne, aky cas ubehol.
DDRD = 0b00001111; // PD4 PD5 PD6 vstup(tlačítka), PD3,2,1,0 výstup
PORTD = 0b01110000; // PD4 PD5 PD6 pull-up pro tlačítka
Dál je třeba upravit kód pro zobrazení.
...
...
PORTC = gen [mind] ^128;
PORTD =0b11110111;
_delay_ms(5);
PORTD = 0x00;[/code] Tady se posledním příkazem pull-upy zruší.
Kromě toho je zbytečné používat v celém kódu desítky a jednotky, když je potřebujeme jenom pro zobrazení.
Kód by mohl vypadat třeba takto
[code]/*--------------------
obsluha 7segmentoveho displeje - stopky
PORTC..segmenty zobrazovače
PD0,1,2,3...anody (katody) 7-seg
PD4.........tlačítko nastavení sekund
PD5.........tlačítko nastavení minut
PD6.........tlačítko počítání času
----------------------*/
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
// globální proměnné
unsigned char gen] = // hodnoty pro 7-seg displej
{
0b00000001, //0 abcdefg
0b01001111, //1 6543210
0b00010010, //2
0b00000110, //3
0b01001100, //4
0b00100100, //5
0b00100000, //6
0b00001111, //7
0b00000000, //8
0b00000100, //9
};
volatile signed char sec=1, min, count;
//-------------------------------
int main (void)
{
// inicializace portů
DDRC = 0xff; // cely port C je vystupni
PORTC = 0;
DDRD = 0b00001111; // PD4 PD5 PD6 vstup(tlačítka), PPD3,2,1,0 výstup
PORTD = 0b01110000; // PD4 PD5 PD6 pull-up pro tlačítka
//-------------------------------
while (1)
{
// nastavení času
if(bit_is_set(PIND,6))
{
if ((PIND & (1<<PD5))== 0)
{
min++;
_delay_ms(500);
}
if ((PIND & (1<<PD4))== 0)
{
sec++;
_delay_ms(500);
}
}
// počítání času
if(bit_is_clear(PIND,6))
{
count++;
if(count>50) // každou vteřinu
{
count = 0;
sec--;
if(sec<0) {sec=59; min--;}
if(min<0 && sec==59) {break;}
}
}
// zobrazení
PORTC = gen[sec%10]; // jednotky sec
PORTD = 0b1111101;
_delay_ms(5);
PORTD = 0b01110000;
PORTC = gen[sec/10] +128; // desítky sec
PORTD = 0b1111110;
_delay_ms(5);
PORTD = 0b01110000;
PORTC = gen[min%10];
PORTD = 0b11111011;
_delay_ms(5);
PORTD = 0b01110000;
PORTC = gen[min/10] +128;
PORTD =0b11110111;
_delay_ms(5);
PORTD = 0b01110000;
}//while(1)
}//main
Zdroják v C vyjde sice jednodušší, a v této nenáročné aplikaci to má zřejmě opodstatnění, ale - MCU nemá hw děličku a výpočet / 10 je dost náročný, tak tam bych se k této úpravě asi nepřikláněl.
V tomto programu nehraje doba dělení žádnou roli, takže zjednodušení kódu se vyplatí.
Pro přesnější měření je ovšem třeba upravit pomocí simulátoru zpoždění 5 ms v části “zobrazení”.
Pro oscilátor 8 MHz (mega88) mi vyšlo _delay_us(4985).
Schéma + program jednoduchých stopek 99,99s (R5-R8 jsou použity je pro správnou funkci simulátoru):
Reset funguje jen při zastavení stopek. Stopky.c (2.81 KB)