Počítání pulzů za 1s s přerušením a zobrazení na LCD

Ahoj všem, mám drobný problém. Realizui program pro průtokoměr se zobrazením průtoku na LCD, čerpadlo posílá pulzy na INT0 kde je snímám procesorem, a po době 1s bych je chtěl vypsat na LCD, na to bych chtěl použít časovač mikroprocesoru. Aktuálně mi funguje čítání impulzů a jejich zobrazení na LCD, jakmile sem ale přidal časovač, tak to přestalo jet a chová se to divně, display bliká a údaje se nezobrazují. Posílám můj program, je tam i spousta věcí pro testy a další balast, jak sem nevěděl co s tím.

Děkuji za pomoc

Program:

[code]#include <avr/io.h>
#include<stdio.h>
#define F_CPU 8000000UL
#include<util/delay.h>
#include <avr/interrupt.h>
#include “lcd_h.h”

int pocet_pulzu; // pomocne promene
uint16_t i=0, j=0, pocet_pulzu_pomocna; // promena typu 16 bit integer
char pom[6], pom1[6]; // pole znaku

// preruseni casovac 1

ISR (TIMER0_OVF_vect)
{
TCNT1=34286; // reset citace cca po 1s
pocet_pulzu_pomocna=pocet_pulzu; // zaloha pulzu pro zobr. na LCD
pocet_pulzu=0; // vynulovani poctu pulzu
PORTC ^= (1 << PC0); //neguj PD2 - blika LED
++i; // inkrementace promene i
}

// preruseni INT0

ISR(INT0_vect)
{
++pocet_pulzu;
}

int main(void)
{
DDRD &= ~_BV(PORTD2); // nastavi PD2 jako vstup
DDRC |= (1 << PC0); // PC0 ako výstupní - test LED casovac

MCUCR |= _BV(ISC00) | _BV(ISC01);		       // Nastaveni INT0 na nastupnou hranu
GICR |= _BV(INT0);						// Nastaveni povoleni pro INT0

TCCR1B |= (1 << CS12);					// preddelička 256 (32us)
TIMSK |= (1 << TOIE1);					// prerušeni pri pretečení TCNT1

sei();									 // povol globalni prerušeni

lcd_init();


//OSCCAL = 0xA5;							// nastaveni kalibracniho bajtu interniho RC oscilatoru

lcd_gotoxy(0,0);
lcd_puts("Pocet pulzu:");
lcd_gotoxy(0,1);
lcd_puts("Pocitani:");
lcd_gotoxy(11,1);
lcd_puts("i=");
	
while(1)
   {
 lcd_gotoxy(13,1);
 sprintf(pom,"%d",(uint16_t)i);				// pretypovani cisla na znak
     lcd_puts(pom);								//tisk pole na LCD
 lcd_gotoxy(12,0);
 sprintf(pom1,"%04d",(uint16_t)pocet_pulzu_pomocna);	// pretypovani cisla na znak
 lcd_puts(pom1);
}

return 0;
}[/code]

Minimálně bude chyba v tom, že čekáš na přerušení od TMR0, místo TMR1!

Ajo, a to sem do toho blbec čuměl snad 2 hodiny a nevšiml jsem si :smiley:

A taky bych pridal nejakou podminku at se ten display prepise, jenom kdyz se zmeni hodnota. Nebo kdyz prijde to 1s preruseni. A nezapomen na to, ze kdyz pouzivas promennou v preruseni je nutno pred ni pridat volatile, jinak to prekladac vyhodi.
Takze

volatile int pocet_pulzu; // pomocne promene volatiole uint16_t i=0, j=0, pocet_pulzu_pomocna; // promena typu 16 bit integer char pom[6], pom1[6];

…a taky pokud není ta operace nad proměnou v přerušení na úrovni strojového kódu atomická, je dobre ji zamykat nebo ještě lépe zamykat její kopii.

Díky za reakci, až to budu v tejdnu předělávat, doplním to. Tenhle program byl zatím jenom test jestli to vůbec rozjedu, potom to teprve budu upravovat ať to za něco stojí :slight_smile: To s tou proměnou v přerušení jsem ale nevěděl, děkuju za radu.

V přerušení to nevadí. U AVRka se přerušení povolí až po instrukci RETI. V hlavním programu je ale třeba zajistit její zamčení.

Samozřejmě že jsem myslel zamykání mimo přerušení…

Tak posledni 4 prispevky jsou pro mne spanelska vesnice, prakticky nerozumim ani slovo
Atomicka uroven strojoveho kodu?
Kopie atomicke urovne strojoveho kodu?

Když nápíšeš operaci v C například inkrementaci proměné int32 tak na 8bit mcu jako je AVR se provede jako několik asm instrukcí, to znamená že ta operace není atomická. Takže pokud používáš takovou proměnou někde v kódu a její inkrementaci provádíš například v přerušení, musíš ji zamykat. Pokud ti apriory jde o to aby zamčení nezpůsobilo zablokování inkrementace (nechceš ztratit informaci) tak výsledek operace kopíruješ do proměné a ta se teprve zamyká a používá v kódu mimo přerušení.

NO taky z toho nejsem moudrej, ale komplet jsem to rozchodil, jestli budte chtít někdo, až to nějak doládím, tak sem kód dám :slight_smile:
Ještě bych poprosil o příklad zamykání, jak to vypadá . V asm bych to věděl, ale v GCC nevim, jestli se to dělá stejně nebo jak.

Zavedeš si pomocnou proměnou, v ní je 0 nebo 1 (odemceno/zamceno) V přerušení kde s uzamykatelnou proměnou pracuješ, tuhle pomocnou proměnou testuješ a zachováš se podle toho. V ASM je to stejný jako v C.

Ještě jeden dotaz, jak se nejlépe zobrazí nějaké číslo s desetinami na LCD, je mi jasné že rozdělit na jednotky, desítky a stovky, už jsem to i vytvořil a funguje to, hledám ale nějaký elegantnější způsob, moje řešení je nějaký kostrbatý, moc se mi nelíbí.

Řeším to nějak takhle:

[code]uint16_t prutok_d(void)
{
uint16_t d;
d= pocet_pulzu_pomocna/5.5;
l=d*10;
return (d);
}

uint16_t prutok_j(void)
{
uint16_t j,k;
k= (pocet_pulzu_pomocna/5.5)*10;
j=k-l;
return (j);
}

.
.
.
sprintf(pom,“Prutok:%02d.%01dL/min”,(uint16_t)prutok_d(),(uint16_t)prutok_j()); [/code]

Formatuj to jako float %f Ale pocitej s tim ze ti naroste velikost kodu…

NOjo, to jsem zkoušel, ale z nějakýho záhadnýho způsobu mi displej zobrazí jenom otazník, jakmile dám ale celočíselný formát, hned to jede.

d= pocet_pulzu_pomocna/5.5;
[/code]Nemůžeš takto míchat celé a desetinné čísla. (Výslede bude pocet_pulzu_pomocna/5). 

Stačí rozšířit zlomek:
[code]
d= pocet_pulzu_pomocna * 10 / 55;
[/code]
a hned je to lepší.

Výpočet s pevnou desetinnou čárkou (bez typu float):
Pokud chceš počítat na jedno desetinné místo, použij 10x větší hodnotu.
Výsledek bude 10x větší a ve funkci sprintf() ho upravíme.
Aby proměnná po vynásobení nepřetekla, použijeme větší typ (uint32_t).

[code]char display_string[8];  
uint32_t pocet_pulsu;

uint32_t prutok_d(void) 
{ 
   return (pocet_pulsu * 100 / 55);    // místo "pocet_pulsu * 10 / 55"   
}


int main(void)
{

   while(1)
   {
       sprintf(display_string, "%lu.%lu", prutok_d() / 10, prutok_d() % 10); 

Podobně pro počítání na 2 des. místa vynásobíme stem atd.

Nepoužívej pro proměnnou malé L, protože se plete s jedničkou.
Vůbec proměnné jako a, b, k dělají kód nepřehledný.
Vyplatí se trocha duševní námahy pro zvolení popisných jmen.

děkuji