A/D převod ATMEGA 168-pokles napětí o 3-4 b při sepnutí relé

Beriem spät az teraz som si vsimol za nazov temy je A/D prevod atmega168…Po zhliadnuti datasheetu atmegy168 mi je jasny bit WGM22 aj mod 7 :slight_smile:

Zdroj hodin nemá vliv na “stabilitu”, ale na fázový jitter. To je vpodstatě rozdíl period mezi vzorky, který se ve výsledku projevuje jako šum ve střídavém signálu (DC to bude převádět správně). Významu nabývá v okamžiku, kdy je ta časová nestabilita srovnatelná se vzorkovací periodou (v rámci řekněme 3 řádů a v závislosti na počtu bitů převodníku). Problematika je to však mimo rozsah této diskuze.
Vzniklý signál by ovšem nebyl špatně převeden, nýbrž špatně navzorkován. To by znamenalo “pouze” zvýšený šum, který se ale průměrováním odstraní.
Použití krystalu místo RC ti tedy může odstranit “záhadný” šum při vzorkování proměnného rychlého signálu, který tam jiný přístroj nevidí (mcu si ho tam zanáší sám).
Až budeš někdy potřebovat vzorkovat v MHz a 12 nebo více bitech, zjistíš, že zdroj hodin je poměrně nepříjemný problém (tam už se řeší stabilita v ps a zlomcích, vedení hodin po dps a tak podobně).
V případě zájmu o problematiku lze začít třeba u aplikační poznámky TI slyt379.

V Tvojom pripade to bude myslim prakticky jedno (vid. Piityy-ho prispevok)

	#define TB_NASTAV_CASOVAC	TCCR2A = (1<<WGM21) | (1<<WGM20);\
						TCCR2B = ((1<<WGM22) | (1<<CS21)) // mod 7 a preddelic 8

Priklad je pre ATmega328 (88/168/324/644/1284). Pre Atmega8 to bude mod 2 - skratka citac pocita do hodnoty ulezenej v niektorom z OCR registrov, po dosiahnuti nastavi flag (spustenie prerusenia zavisi od dalsich povoleni), vynuluje sa a pocita znovu. Islo o to nemenit neustale .hodnotu TCNT

presne tak. Na zaciatku sa zakaze prerusenie a pri vyskoceni z makra sa prerusenie povoli, ak bolo na povodne povolene. Blizsie pozri dokumentaciu k avrlib. Ako parameter sa nemusi pouzit len ATOMIC_RESTORESTATE, ale to si dohladaj. Niet nad samostudium s pripadnymi otazkami na forum. Odchod z makra moze to byt aj na viacerych miestach, prekladac to spravne osetri sam. To je vyhoda pred pouzitim ZAKAZ_PRERUSENIE/POVOL_PRERUSENIE

priklad:

/*
kus programu popisuje nasledovny stav. Ak este nebol prijaty prvy bajt spravy (hlavicka - nejaky dohodnuty znak), tak prijaty bajt zahod. Ak uz bol prijaty prvy bajt spravy, potom prijate bajty ukladaj do urceneho pola, ale daj pozor aby nepretiekol vyhradeny bufer. Po prijati koncoveho znaku (napr 0x0D) nastav priznak prijatia celej spravy. UVodny a koncovy znak sa do pola neukladaju (ved naco aj :-) ).
premenna "prijaty_bajt" je hodnota nacitana z registra UARTU pod prerusenim.
Priklad sa da napisat samozrejme aj inak, formu som zvolil z didaktickych dovodov.

Z makra sa odchadza na troch miestach, prekladac spravne kod prelozi tak aby na zaciatku bolo prerusenie zakazane a pred kazdym vyskocenim z makra zase povolene (ak povodne povolene bolo)
*/

ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
   if (bol_prijaty_zaciatok_spravy == FALSE) {
      if (prijaty_bajt != HLAVICKA_SPRAVY) return;
      else {
         bol_prijaty_zaciatok_spravy =  TRUE;
         i = 0;
      }
   else {
      if (prijaty_bajt == KONIEC_SPRAVY) {
        prijata_sprava_je_ukoncena = TRUE;
        return;
      }
      
      if (i < MAX_POLE_PRE_SPRAVU) {
         pole_bajtov* = prijaty_bajt;
         i++;
      }
   }
}

Je to kvoli prehladnosti a aj kvoli tomu, aby zmena procesora alebo hw platformy nemala vplyv na samotny kod *.c ale aby sa iba vybrali prislusne spravne makra. Priklad (uryvok z rozsiahlejsieho cyklu:


// subor "platforma_atmega8.h"
// *************************

//...
		#define TB_NASTAV_CASOVAC	TCCR2A = ((1<<WGM21) | (1<<CS21))   // mod 2 a preddelic 8
//...

// subor "platforma_atmega328.h"
// *************************

//...
		#define TB_NASTAV_CASOVAC	TCCR2A = (1<<WGM21) | (1<<WGM20);\
											TCCR2B = ((1<<WGM22) | (1<<CS21)) // mod 7, a preddelic 8

//...

// subor "vyber_platformy.h"
// *************************


// ...
#define AT8 0
#define AT328 1

#define procesor AT8


#if (procesor == AT8)
   #include "platforma_atmega8.h"
#elif  (procesor == AT328)
   #include "platforma_atmega328.h"  
#else
   #error NIE_JE_DEFINOVANY_PROCESOR
#endif

// ...


// subor "moj_program.c"
// *************************

#include "vyber_platformy.h"

//...

int main(void) 
{

// ....
  TB_NASTAV_CASOVAC;
// ...

}
  

Pri novom projekte si vhodne zeditujes hlavickovy subor “vyber_platformy.h” a co je najdolezitejsie nemusis siahat do samotneho kodu - teda iba tam, kde je to nevyhnutne. Preto je tiez dobre robit s viacerymi *.c subormi v projekte. Jeden je hlavny spustac uloh, jeden sa stara o tlacitka, iny o zobrazenie, iny o AD, atd, atd. Potom v projekte editujes iba tie casti (alebo nove *.c-ka) ktore potrebujes a tam kde nie je nevyhnutne siahat tak nesiahas. zaroven sa vyhnes tomu, aby si musel *.c editovat na viacerych miestach ak sa rozhodnes pouzit iny procesor, alebo iny hw. Jednotlive funkcie si vymienaju udaje bud cez volanie funkcii ktore spristupnuju zapuzdrene premenne, alebo, co sa mi v tejto koncepcii osvedcilo daleko viac (v prvom pripade musis poznam mena tych funkcii a je pruser ak taky kus kodu v projekte nemas) je mat pole spolocnych premennych dostupnych cez funckiu v zakladnom bloku (ten je v projekte vzdy) a cez adresy definovane v jednom hlavickovom subore k udajom pristupovat. Priklad:

[code]
// subor “global_adr_v01.h”

//…

//ADRR su indexy tykajuce as vseobecneho pola v RAM
//ADRE su indexy tykajuce as vseobecneho pola v EEPROM
// je dobre vsetky indexy menezovat z jedneho suboru, je lepsie kontrolovatelne, ci nahodou nedochadza k prekryvaniu indexov

#define ADRR_TL 48 // tu sa ulozi okamzity (surovy) stav tlacitok
#define ADRR_KLAV 49 // tu sa ulozi spracovany stav klaves urceny zo stavu tlacitok
#define ADRR_DISP 50 // rezerva 16b nasleduju styri/pat bajty/bajtov, ktorych obsah sa kopiruje na display

#define ADRR_TST_PARAM_COM0 66 // // prac reg z ADRE_COM0_SETUP 8 // protokol, speed, bity, parita, stop_bity, adresa_rx, delay_tx, CHS
#define ADRR_TST_COM0_DEFAULT_ADR 74 // defaultna adresa z programu
#define ADRR_CISLO_PROTO_6B 76 // 6B
#define ADRR_KONTROLA_COM0 82 // 2B

#define ADRR_TEPLOTA1 84 // 8B
#define ADRR_TEPLOTA2 92 // 8B
#define ADRR_AD1 100 // 2B

#define ADRE_COM0_SETUP 24 // 8B: protokol, speed, bity, parita, stop_bity, adresa_rx, delay_tx, CHS

//…

// subor “ad.c”
// ************

static uint16_t ad_prevod_kumul[POCET_AD_KANALOV];

// …

fn_ram((uint8_t )DATA_WR, (uint16_t )1, (uint16_t )ADRR_AD1, (uint8_t *)&ad_prevod_kumul[0]);

// subor “zobraz.c”
// ************

static uint16_t aktualne_zobrazena_hodnota

// …

fn_ram((uint8_t )DATA_RD, (uint16_t )1, (uint16_t )ADRR_AD1, (uint8_t *)&aktualne_zobrazena_hodnota);

// …

/*
funkcia
uint8_t fn_ram(uint8_t prikaz, uint16_t pocet, uint16_t index, uint8_t *p_data)

je definovana v subore “zaklad_aplikacii.c”, ktory je pritomny v kazdom projekte. Vyhoda ist cez premenne je ta, ze ak chcem hodnotu z AD simulovat napriklad cez komunikaciu pre otestovanie nejakeho algoritmu, z projektu vyhodim suboe “ad.c” a cast komunikacnych indexom presmerujem na index ADRR_AD1. Nemusim nic menit v subore “zobraz.c”

/
[/code]

Takze tu mam trosku upraveny kod. Len pripominam jedna sa o atmegu8 proc. bezi na 8 MHz

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

#include "lcd.h"  

#define POCET_PREVODOV_PRE_PRIEMEROVANIE 20
#define FALSE 0
#define TRUE 0xff 
#define SET(ADDRESS,BIT) (ADDRESS |= (1<<BIT))

#define ZAKAZ_GLOB_PRERUSENIE cli()
#define POVOL_GLOB_PRERUSENIE sei() 

//mod 2, predelicka 64
#define T2_NASTAV_CASOVAC   TCCR2 = (1<<WGM21)| (1<<CS22) 
#define T2_POVOLENIE_PRERUSENIA   SET(TIMSK,OCIE2)
#define T2_OCR2 OCR2
#define T2_HODNOTA 125 
#define NASTAV_CELY_TIMER2 T2_NASTAV_CASOVAC;\
								T2_OCR2 = T2_HODNOTA;\
								T2_POVOLENIE_PRERUSENIA;

#define AD_REF SET(ADMUX,REFS0)
#define AD_NASTAVENIE ADCSRA = ((1 << ADEN)|(1 <<ADPS2)|(1<<ADPS1))

volatile unsigned int adc, adc1;
volatile char count=0, priznak;
 

ISR(TIMER2_COMP_vect){

	count++; //pocitanie prevodov

    adc+=ADC;   //hodnotu z AD pripocitavam k premenej	
	
   	ADCSRA |= (1 << ADSC); // start conversion 
	
	if(count==POCET_PREVODOV_PRE_PRIEMEROVANIE){

	adc1=adc;  //vlozenie kum. hodnoty do premennej

	priznak = TRUE; //kumulacia je hotova
	
	count = FALSE;

	adc = FALSE;

			}
	}


  	int main(){  

	NASTAV_CELY_TIMER2; 

	AD_REF; //referencia 5V  
  
    // povolenie AD prevodnika,preddelicka 128 pri frekvencii hodin 8MHz  
    AD_NASTAVENIE ;  
  
    POVOL_GLOB_PRERUSENIE; // povol globalne prerusenia   

	char text[32];

	unsigned int adc2;
  
    lcd_init(LCD_DISP_ON);  

  
    while(1){ 

		if(priznak){
	   
	    		ZAKAZ_GLOB_PRERUSENIE;

				//s priemerovanie kum. hodnoty
   				adc2 = adc1/POCET_PREVODOV_PRE_PRIEMEROVANIE; 			
		
				priznak = FALSE;
		
				POVOL_GLOB_PRERUSENIE;
						
				lcd_gotoxy(0,0);

				//vypisanie ADC hodnoty na lcd
        		sprintf(text,"ADC:%d ",adc2);
		  
       		 	lcd_puts(text);					
		
							}
		  
  	 				 }  
  
			}

Ked som si vypocitaval udaje pre T2 aby mal prerusenie kazdu 1ms vysla mi predelicka 64 pri 8MHz a hodnota v OCR2 125 je to spravne?

No nevím, možná je to věc názoru, ale takto počešťovat (nebo poslovenšťovat) základní funkce jazyka se mi nějak nezdá.
Když, tak bych to dal do komentáře.
Tohle #definování by mělo mít určitou hranici.

#define NASTAV_CELY_TIMER2 T2_NASTAV_CASOVAC;\
                        T2_OCR2 = T2_HODNOTA;\
                        T2_POVOLENIE_PRERUSENIA;

toto nebude fungovat

fungovat bude toto:

#define NASTAV_CELY_TIMER2 T2_NASTAV_CASOVAC;\
                        T2_OCR2 = T2_HODNOTA;\
                        T2_POVOLENIE_PRERUSENIA

počešťovat (nebo poslovenšťovat) zakladne funkcie ktoreho jazyka? V C nie je ziadna zakladna funkcia, ktorej evivalent by bol:

“zakáž globálne prerušenie”

a

“povol globálne prerušenie”

takze neviem co sa pocestuje alebo poslovenstuje, skor mam dojem ze sa v #define konstatna nazyva tak, aby bola aj v zdrojaku dostatocne zrozumitelna a nevyzadovala si nejake zbytocne komentare okolo, ze co pripadna mnemotechnicka nezrozumitelna skratka (nedajboze iny [bohuzial bezny] zhluk znakov) znamena. Treba pisat co najmenej (lebo lenivost) a zaroven co najzrozumitelnejsie (aby aj iny [alebo aj sam tvorca s odstupom mesiacov] v pripade potreby tomu rozumeli) :slight_smile:

jedina hranica je zrozumitelnost (aspon pre autora) na co najmenej pismen. Neviem si predstavit, ze by sa mne chcelo kazdy riadok s “cli()” okomentovat. No vykaslal by som sa pri pisani na to (ved v tej chvili by som tomu predsa rozumel). No a o mesiac by som nevedel co "cli() " znamena, lebo som za ten mesiac vystriedal dalsie styri rozne ine jadra mcu.

Chvalabohu do programovania nezasahuje ziaden jazykovedny ustav. Ci?

Ale proti gustu ziaden disputat. :slight_smile:

Ale najhlavnejsi dovod pouzit #define so zastupnym textom. Ak chcem vyuzit *.c pre rozne mcu platformy, tak by som bol sebevrah pisat do zdojaku sei() a cli(). Je daleko univezalnejsie tieto prikazy definovat cez headre (tak ako som popisoval v predchadzajucom prispevku). Ved pre ARM sa funkcie pre zakaz a povolenie prerusenia nevolaju tak ako pre AVR.
A ze zastupny text za #define nie je v anglictine? No zvysoka ma to netrapi. Pre mna je dolezita zrozumitelnost predovsetkym pre mna :slight_smile:

Bohužel vícežádková makra zrovna srozumitelnosti nepřispívají nehledě na jejich problém s laděním. Jesli má pisatel problém s přesahem kódu při volání funkce, ať si ji napíše inline.

Aky je problem so zrozumitelnostou a ladenim? Co sa moze pri ladeni stat?

V zdrojaku *.c je napisane

 NASTAV_CASOVAC;

ako moze prist k nezrozumitelnosti?

Co je to presah kodu respektive problem s presahom kodu a ako by vyzerali uvedene funkcie ako inline?

Nerypem, len som zvedavy ake zaludnosti sa mi doteraz vyhybali. :slight_smile:

Udělej v makru chybu a zkus to přeložit :wink:. Taky si ho můžeš zkusit odkrokovat… (do funkce vleze “step in”, do makra nikoli) To ve spojení s hláškama při překladu typu “na řádku xx je očekávána závorka”, přičemž chyba je způsobena chybějícím středníkem o 10 řádků výše, znamená pro začátečníka nemalé problémy. Koukne na řádek, kde má být chyba, ale žádná tam není.
Pak se tu bude rozčilovat jak je překladač dementní a přitom má chybu v makru o několik řádků výše.

Dále jsou tu obecná typografická pravidla céčka (která samozřejmě dodržovat nikdo nemusí, ale pak ať se nediví, že to po něm nechce nikdo luštit). TEXT_VELKYMI_PISMENY bez závorek pro mne a nemalou část céčkařů automaticky znamená konstantu. Přijatelné je parametrické makro bez parametrů - NEJAKE_MAKRO().

Standardně funkce překladač volá (call, ret) a s tím souvisí práce se STACKem (a několik instrukcí zpoždění). Napíšeš-li funkci jako inline, provede překladač místo volání přímé vložení jejího kódu(tedy to samé, co víceřádkové makro jen je na první pohled vidět, že se pod tím skrývá výkonný kód).
“inline void funkce(void)”

Aj napriek chybam v makrach somproblem pri ladeni nikdy nemal.

Prekladac ma akurat upozorni, ze nejaku konstatnu nemam definovanu. Ale moze to byt aj v tom, ze tento sposob pouzivam vyhradne na nastavenie HW a nerobim takto nejake rozsiahlejsie psie kusy. Na to sa presne ako pises hodia inline funkcie.

To predsa nie je neobvykle ani v inych pripadoch (napr. chybajuca zatvorka } ) :slight_smile:

S tymo sa da suhlasit.
Povodne som takto stavane makra pouzival a stacilo v nich priradit iba jednu konstantu. Az neskor zacal Atmel nastavovanie periferie rozhadzovat do viacerych bajtov, no pri sposobe som uz ostal :slight_smile:

Ano, suhlasim. Pre zaciatocnika bude tento sposob asi najprehladnejsi.

Ak chce niekto pisat prenositelny kod i na urovni nastavenia hw, v ziadnom pripade neodporucam pisat nastavenia periferii priamo do kodu. Tieto by mali byt v samostatnych suboroch a spravny vyber akcie by sa mal nastavovat prostrednictvom #define a #ifdef v subore nezavislom od funkcionality kodu. Iba tak sa da funkcny kod pisat nezavislo od hw platformy. Samozrejme za predpokladu, ze hw platforma prislusny hw vobec ma, alebo ho vie simulovat. Ako priklad uvadzam kod, ktory na AVR pracuje s druhym/tretim/stvrtym UARTOM bez ohladu na to ci je tento hw alebo sw implementovany. Z hladiska kodu vysli bajt/prijmi bajt je jedno ako je UART “fyzicky” zrealizovany.

Myslel jsem tím funkce z knihoven AVR-GCC.

[code]#define T2_NASTAV_CASOVAC TCCR2 = (1<<WGM21)| (1<<CS22)
#define T2_POVOLENIE_PRERUSENIA SET(TIMSK,OCIE2)
#define T2_HODNOTA 125
#define NASTAV_CELY_TIMER2 T2_NASTAV_CASOVAC;
T2_OCR2 = T2_HODNOTA;
T2_POVOLENIE_PRERUSENIA



main()
{
NASTAV_CELY_TIMER2;

[/code]

Když narazím v kódu na toto makro, tak se musím vrátit na začátek,
podívat se na definici, pak se podívat na definice výrazů použitých v této definici.
Tak dojdu k makru
#define T2_NASTAV_CASOVAC TCCR2 = (1<<WGM21) | (1<<CS22).
Pak ještě musím najít co je tím nastaveno (jaký mód a předdělič).

Mně osobně se zdá srozumitelnější a přehlednější toto (má to i méně písmen)

main() { TCCR2 = (1<<WGM21)| (1<<CS22) // mód CTC, TOP=OCR2A, předdělič=8 SET(TIMSK,OCIE2); // povol přerušení při shodě OCR2 = 125; sei();
Také souhlasím s piityy, že makro by se mělo psát se závorkami i když nemá parametry.

Chalani este mam otazku az by som potreboval udaje z viacerych AD kanalov akym sposobom sa to realizuje. Spravi sa 20 merani z jedneho kanala ulozi hodnota. Potom dalsich 20 merani z dalsieho kanala…?

No v tom je ta finta, ze ked si toto napises priamo do kodu a potom potrebujes ten subor *.c pouzit pre iny procesr s inymi nastaveniami tak to vsetko musis prepisovat, casto krat i na viacerych miestach a tak okrem zabiteho casu este hrozi problem s hladanim chyby. Ked ja pouzivam svoje makra, napriklad “NASTAV_CASOVAC”, nepotrebujem kde kade lozit po suboroch aby som vedel ako je co nastavene. K tomu nie je dovod, lebo rozne Xtaly a zdroje hodin su v tom makre osetrene.
Napriklad:


// napr. subor "zakladne_nastavenie_aplikacie.h"

#define XTAL 14745600
#define CASOVA_ZAKLADNA MIN_CAS_PRERUSENIA_500us


#define up_mega8 0
#define up_mega88 1
#define up_mega168 2

#define up_mega16 3
#define up_mega32 4 
#define up_mega324 5 
#define up_mega644 6
#define up_mega1284 7
#define up_mega128 8
#define up_mega328 9


#define PROCESOR up_mega328

#if (PROCESOR == up_mega8)
   #include "m8.h"
#elif (PROCESOR == up_mega328)
   #include "m328.h"
#elif
//...
#endif


// ...


// subor pre konkretny procesor "m328.h"

	#if (XTAL == 7372800)
		#define TB_NASTAV_CASOVAC	(TCCR2 = ((1<<WGM21) + 3) // mod CTC a preddelic 32, CS22 = L, CS21 = H, CS20 = H
	#elif (XTAL == 14745600)
		#define TB_NASTAV_CASOVAC	(TCCR2 = (1<<WGM21) + 4) // mod CTC a preddelic 64, CS22 = H, CS21 = L, CS20 = L
	#endif

// ...
// ide o princip, dalsie nastavenia ako prepinanie casovej zakladne nie su pre tento priklad zaujimave :-)

	#if (CASOVA_ZAKLADNA == MIN_CAS_PRERUSENIA_500us) // casova zakladna 500us
		#define TB_DEFAULT_1 114 //115-1   pouzije sa POCET_PRVEJ_PREDVOLBY
		#define TB_DEFAULT_2 115 //116-1	 pouzije sa 1x, 	(ak POCET_PRVEJ_PREDVOLBY = 5, potom bude priemer 115.2)
	#elif  (CASOVA_ZAKLADNA == MIN_CAS_PRERUSENIA_1ms) // casova zakladna 1ms
		#define TB_DEFAULT_1 229 //115-1   pouzije sa POCET_PRVEJ_PREDVOLBY
		#define TB_DEFAULT_2 231 //116-1	 pouzije sa 1x, 	(ak POCET_PRVEJ_PREDVOLBY = 5, potom bude priemer 230.4)
	#endif
 

Aplikaciu pre prekladom nastavim v rozsahu


// ...
#define XTAL 14745600
#define CASOVA_ZAKLADNA MIN_CAS_PRERUSENIA_500us
#define PROCESOR up_mega328
// ...

a nemam co skumat obsah konkretnych nastaveni pri tvorbe aplikacie. Je jasne, ze priprvom pouziti si musim makra odskusat a otestovat, ale potom uz na ne nesiaham, takze tam nema byt preco problem a ani sa mi nevyskytuju “tazko hladatelne” chyby v dosledku chybne napisaneho makra. Jednoducho sa raz spavia poriadne a nie je dovod na ne viac sahat. Ak pribudne dalsi procesor, tak sa pre neho vytvori novy subor “mXYZ.h”, ten sa otestuje a je vymalovane. Z praxe potvrdzujem, ze s takymto systemom nie je ziaden problem, je jednoduchy na pouzitie a zdrojovy kod *.c je dostatocne prehladny.

S tym sa da suhlasit, Piityyho argument a upozornenie “ze je za tym nieco schovane” je dobry.

:arrow_right: administrator: příspěvek byl upraven