Generátor: Generovaní sinusového a trojúhelníkového průběhu

No tak som sa vybuzeroval a tu je vysledok. Ak sa bude zdat byt niekomu zdrojak prilis dlhy, je to v najlepsom slova zmysle jeho problem :slight_smile:
Prekladane pre ATmega8 20MHz, -O3, GCC

Testoval som nasledovne varianty. Teda bolo ich omnoho viac, ale za zdielanie stoja tieto ako hlavne predstavitelky socialneho experimentu.

  1. dalsia vzorka najrychlejsie ako sa da - oba priebehy (SINUS a TROJUHOLNIK) maju rovnaky pocet vzoriek a teda aj rovnaku pevnu rychlost. Ide o demonstrovanie maximalnej rychlosti a zaroven demonstrovanie roznej rychlosti pri roznom algoritmyckom konstrukte.

Frekvencia sa da nastavit vhodnym poctom vzoriek, pripadne dosolichat roznym poctom ASM inistrukcii “nop”. V priklade su vlozene nop-y vyREMovane

  1. dalsia vzorka najrychlejsie ako sa da - rozna frekvencia pre SINUS a TROJUHOLNIK sa da dosiahnut roznym poctom vzoriek, podla zadania bude mat SINUS 1.2x viac vzoriek ako TROJUHOLNIK. Dalej sa da frekvencia znizit vlozenim potrebneho poctu “nop”-ov

  2. dalsia vzorka sa zapise na zaklade zmeny hodnoty predvolby casovaca, oba signaly maju rovnaky pocet vzoriek

  3. dalsia vzorka sa zapise na zaklade zmeny hodnoty predvolby casovaca, oba signaly maju rozny pocet vzoriek

Spolocne rysy:
Aby sme usetrili cas, vyuzijeme nasledovne javy.

  1. Tlacitko sa vyhodnoti cez interupt (dobezna hrana), netreba teda neustale testovat jeho stav.

  2. Nebude sa pri kazdej vzorke testovat aky tvar signalu sa ma generovat, ale vyuzije sa ukazovatel na zaciatok pola premennych pre ten ktory signal. Pri stlaceni tlacitka sa zmeni pociatocna hodnota ukazatela na zaciatok prislusneho pola a zaroven sa (podla demonstrovaneho pripadu) pripadne zmeni prednastavena hodnota poctu prvkov toho pola.

Spolocny kod na zaciatku - v roznych variantoch ho uz nebudem opakovat, ved naco :slight_smile:

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

#define SET(BAJT,BIT) ((BAJT) |= (1<<(BIT)))
#define TST(BAJT,BIT) ((BAJT) & (1<<(BIT)))

#define TRUE 0x00
#define FALSE 0xff
#define PREDVOLBA_CZ 10 // je naschval nizka, aby sw nestihal a tak sa ukazali jeho maximalne moznosti
#define MAX_VZORKY_TROJUHOLNIK 20
#define MAX_VZORKY_SINUS 32
#define PORT_PRE_DA PORTB
#define SMER_PORT_PRE_DA DDRB

uint8_t generuj_signal = SINUS;
uint8_t stav_tlacitka_new,  stav_tlacitka_old, i;

uint8_t sinu  [MAX_VZORKY_SINUS] = {127,138,149,160,171,182,193,205,      //127-205
                     205,193,182,171,160,149,138,127,      //205-127
                     127,116,105,94,83,72,61,49,         //127-49
                     49,61,72,83,94,105,116,127};      //49-127
uint8_t troj  [MAX_VZORKY_TROJUHOLNIK] = {0,12,24,36,48,      //0-48
                     60,71,82,93,103,      //60-103
                     114,125,136,147,154,      //114-154
                     124,93,62,31,0,};   //124-0

// trojuholnik s rovnakym poctom prvkov ako SINUS. 
uint8_t troj2  [MAX_VZORKY_SINUS] = {0,10,20,30,40,50,60,70,
                     80,90,100,110,120,130,140,150,  
                     160,150,140,130,120,110,100,90,    
                     80,70,60,50,40,30,20,10};   

Pripad 1a) maximalna rychlost, oba priebehy maju rovnaky pocet vzoriek.
Cas sa da upravit odREMovanim “nop”-ov pripadne zvysenim/znizenim poctu vzoriek. Cas behu slucky je 0.3us. Tento cas povazujem za velmi slusny. :slight_smile:

volatile uint8_t *p_bajt_pom;

int main(void)
{
register uint8_t i, predvolba;
register uint8_t *p_bajt;

 	sei(); //povolenie globálneho prerušenia  
	SET(GICR,INT0); // povolenie prerušenia od INT0 
	SET(MCUCR, ISC01); // prerusenie na dobeznu hranu
	p_bajt_pom = sinu;
	p_bajt = (uint8_t *)p_bajt_pom;
	
	predvolba = MAX_VZORKY_SINUS;
	i = predvolba;
	SMER_PORT_PRE_DA = 0xff;
	while(1) {

// kod s ktorym slucka s tou istou funkcionalitou trva 0.3us
		if (i == 0) {
			i = predvolba;
			p_bajt = (uint8_t *)p_bajt_pom;
/*
asm volatile("nop\n\t"
             "nop\n\t"
             ::);
*/
	   }
		else {

/*
asm volatile("nop\n\t"
             "nop\n\t"
             "nop\n\t"
             "nop\n\t"
             "nop\n\t"
             "nop\n\t"
             ::);
*/
		}
// ----------------------------------------------------------
		
		PORT_PRE_DA = *p_bajt;
		i--;
		p_bajt++;

	}

}


// nech je tlacitko pripojene na pin s INT0 a dobre osetrene proti zakmitom
ISR (INT0_vect) { 
	if (p_bajt_pom == sinu) {
		 p_bajt_pom = troj2;
	}	 
	else {
		p_bajt_pom = sinu;
	}
	return;   
}  

Pripad 1b) to co predtym, len s horsie napisanym algoritmom. Testovanie na konstantu moze trvat dlhsie ako testovanie na nulu. Vsimnite si tiez poprehadzovanie riadkov s dekrementom i a inkrementom ukazatela. Ich umiestnenim na povodne miesto v zdrojaku bude mat vplyv na rychlejsi beh programu.

Cas trvania slucky 0.45us.


volatile uint8_t *p_bajt_pom;

int main(void)
{
register uint8_t i, predvolba;
register uint8_t *p_bajt;

 	sei(); //povolenie globálneho prerušenia  
	SET(GICR,INT0); // povolenie prerušenia od INT0 
	SET(MCUCR, ISC01); // prerusenie na dobeznu hranu
	p_bajt_pom = sinu;
	p_bajt = (uint8_t *)p_bajt_pom;
	
	predvolba = MAX_VZORKY_SINUS;
	i = predvolba;
	SMER_PORT_PRE_DA = 0xff;
	while(1) {

		PORT_PRE_DA = *p_bajt;

		if (i == predvolba) {
			i = 0;
			p_bajt = p_bajt_pom;
	   }
		else {
		}
		i++; 
		p_bajt++;
	}
}

// nech je tlacitko pripojene na pin s INT0 a dobre osetrene proti zakmitom
ISR (INT0_vect) { 
	if (p_bajt_pom == sinu) {
		 p_bajt_pom = troj2;
	}	 
	else {
		p_bajt_pom = sinu;
	}
	return;   
}  
  1. premenna predvolba uz musi byt typu volatile kvoli spracovaniu cez interupt (obmedzenie je len z hladiska pouzitia prekladaca C, pre ASM by to neplatilo). Vysledny kod trva 0.45us
volatile uint8_t *p_bajt_pom, predvolba;

int main(void)
{
register uint8_t i;
register uint8_t *p_bajt;

 	sei(); //povolenie globálneho prerušenia  
	SET(GICR,INT0); // povolenie prerušenia od INT0 
	SET(MCUCR, ISC01); // prerusenie na dobeznu hranu
	p_bajt_pom = sinu;
	p_bajt = (uint8_t *)p_bajt_pom;
	
	predvolba = MAX_VZORKY_SINUS;
	i = predvolba;
	SMER_PORT_PRE_DA = 0xff;
	while(1) {

// kod s ktorym slucka trva 11.35us
		if (i == 0) {
			i = predvolba;
			p_bajt = p_bajt_pom;
	   }
		else {
		}
// ----------------------------------------------------------

		PORT_PRE_DA = *p_bajt;
		i--; 
		p_bajt++;
	}
}


// nech je tlacitko pripojene na pin s INT0 a dobre osetrene proti zakmitom
ISR (INT0_vect) { 
	if (p_bajt_pom == sinu) {
		 p_bajt_pom = troj;
		 predvolba = MAX_VZORKY_TROJUHOLNIK;
	}	 
	else {
		p_bajt_pom = sinu;
		predvolba = MAX_VZORKY_SINUS;
	}
	return;   
}  
  1. Vzorkovanie je riadenie presne casovacom. Kratky cas slucky je 0.7us

PREDVOLBA_CZ_1 20  // pre casovanie pod 1us,  pre trojuholnik 20 vzoriek
PREDVOLBA_CZ_2 24  // pre casovanie pod 1.2us, pre sinus tiez 20 vzoriek


volatile uint8_t *p_bajt_pom;

int main(void)
{
	register uint8_t i, predvolba, j;
	register uint8_t *p_bajt, p_bajt_pom;

	p_bajt_pom = sinu;
	p_bajt = p_bajt_pom;
		
	predvolba = MAX_VZORKY_SINUS;
	i = predvolba;
	DDRB = 0xff;

   OCR2 = PREDVOLBA_CZ;
   SET(TCCR2,WGM21);
   SET(TCCR2,CS20); // delicka 1


	while(1) {

		j = TIFR;
		if (TST(j, OCF2)) {
		   TIFR = j;

   		if (i == 0) {
	   		i = predvolba;
				p_bajt = p_bajt_pom;
	      }
		   else {
			   i++;
				p_bajt++;
		   }

		   PORTB = *p_bajt;
		}
	}
}

// nech je tlacitko pripojene na pin s INT0 a dobre osetrene proti zakmitom
ISR (INT0_vect) { 
	if (p_bajt_pom == sinu) {
		 p_bajt_pom = troj2;
//   OCR2 = PREDVOLBA_CZ_1;

	}	 
	else {
		p_bajt_pom = sinu;
//   OCR2 = PREDVOLBA_CZ_2;
	}
	return;   
}  
  1. cas slusky je 0.85us. V tomto stave si mozeme dovolit konfort rozneho poctu vzoriek a roznej periody presne definovanej casovacom a nie dlzkou programu pri roznych rozhodovacich vetveniach. Netreba ich teda vyhodnocovat a zaprasovat kod "nop"mi
volatile uint8_t *p_bajt_pom, predvolba;

int main(void)
{
	register uint8_t i, j;
	register uint8_t *p_bajt, p_bajt_pom;

	p_bajt_pom = sinu;
	p_bajt = p_bajt_pom;
		
	predvolba = MAX_VZORKY_SINUS;
	i = predvolba;
	DDRB = 0xff;

   OCR2 = PREDVOLBA_CZ;
   SET(TCCR2,WGM21);
   SET(TCCR2,CS20); // delicka 1


	while(1) {

		j = TIFR;
		if (TST(j, OCF2)) {
		   TIFR = j;

   		if (i == 0) {
	   		i = predvolba;
				p_bajt = p_bajt_pom;
	      }
		   else {
			   i--;
				p_bajt++;
		   }

		   PORTB = *p_bajt;
		}
	}
}

// nech je tlacitko pripojene na pin s INT0 a dobre osetrene proti zakmitom
ISR (INT0_vect) { 
	if (p_bajt_pom == sinu) {
		 p_bajt_pom = troj;
		 predvolba = MAX_VZORKY_TROJUHOLNIK;
	}	 
	else {
		p_bajt_pom = sinu;
		predvolba = MAX_VZORKY_SINUS;
	}
	return;   
}  

Vysledky su medzi casmi 0.3us na vzorku az 0.85us na vzorku. Pravdu povediac, sam som milo poteseny vybornymi vysledkami prekladaca GCC. ASm som ciastocne analyzoval a kujon jeden, zdrojak prelozil v celku efektivne. Vzhladom k tomu, ze zapis v C vedie k daleko lepsie udrziavatelnejsiemu kodu, ktory je zaroven lepsie zdielatelny s komunitou, pracovat v cistom ASM neprinasa nejaku znatelnu efektivitu.

Zaver:
Pre rovnaku frekvenciu a maximalne casovanie oboch signalov je mozne pouzit 66.67 vzorky signalu na frekvenciu 50kHz. Vzorkovanie treba na presnu frekvenciu dosolichat nopmi z in-line asembleru.

Pre rozne frekvencie a maximalne casovanie je mozne pouzit 44 vzorky na periodu pre 50kHz a 53 vzorky pre 41.67kHz. Vzorkovanie treba na presnu frekvenciu dosolichat nopmi z in-line asembleru.

Pre roznu frekvenciu nastavenu casovacom je mozne pouzit na 50kHz 23 vzorky a dosolichat casovac a na 41.67kHz 49 vzoriek.

Osobne by som siel na casovu zakladnu 1us. Na generovanie trojuholnika by som pouzil 20 vzoriek a na generovanie sinusu 24 vzoriek. Tym sa dosiahnu frekvencie presne podla zadania.
Medzi nami , nie je to z hladiska skreslenia boh vie co, ale signal sa bude ako tak podobat a vyucujuci by mal byt spokojny.

Usetreny cas by som venoval napriklad moznej komunikacii s PC a cez neho nastavovaniu vystupnej frekvenie alebo typu priebehu.