Přepínání mezi 0 a vys. imp. v zavislosti na rozdílu napětí

Dobrý den,
Bojuju s rozběháním programu a pořád se mi nedaří nalézt chybu.
Mám vytvořený program, který mi načítá dvě analogové hodnoty a přes ADC mi je převádí na hodnotu adcKola a adcNapravainverz. tyto vstupy mám nastavené na portA piny 6 a 7 označení v programu jako čísla kanálu. nevím jestli mám tyto piny vybrány správně, protože mi nefungují výstupy při nahráni programu do AVR.

Jako vystupy volím portC kde na PIN 3 a 5 se mi střídá stav vysoké impedance a 0. a na PIN 4 a 6 se mi mění 1 a 0. Na PIN 7 přivádím výstup PWM (PIN7 je TOSC2) nevím jestli mi to bude fungovat jako PWM výstup nebo mám raději použít výstup např OC1 nebo jiné OC.

Když nahraju tento program tak mám na výstupech PIN 7-4 okolo 0,5V a na PIN 3 mám 5V.

Nevím si s tím rady vsázím na to že mám špatně ty čísla kanálů zvolené, ale vůbec nevím jak je nastavit jinak v AtmelStudiju mi ten program jede bez ERRORU a WARNINGU takže jsem bezradný.

#define F_CPU 16000000UL // Definice hodinoveho signalu
#include <avr/io.h>
#include <util/delay.h> // Hlavickovy soubor pro zpozdeni
#include <avr/interrupt.h> // Hlavickovy soubor pro preruseni

#define buzeni ((255/5)*(125/100)); //Definovana hodnota buzeni na 1,25V

unsigned int Read_ADC(unsigned char channel)
{
ADMUX &= 0b01100111; // Cisla kanalu
ADMUX |= channel & 0b01100110;

ADCSRA |= (1 << ADSC);						// Start prevodu A/D


while(!(ADCSRA & (1<<ADIF)));				// Cekej na priznak skonceni prevodu


return ADC;									//  Navratova hodnota - vysledek A/D prevodu

}

double rozdilCidel=0; // Pro nacetni hodnoty do PWM

int main(void)
{

unsigned int adcVolant, adcNapravainverz, adcNaprava;

ADMUX |=  (1 << REFS0);						// Reference 5V + pridano ADLAR (pro rozliseni 8 bitu)

ADCSRA |= (1 << ADEN) | (1 << ADIF) | (1 <<ADPS2) | (1 <<ADPS1)  | (1 <<ADPS0);

											// Povoleni AD prevodniku, Interrupt flag, preddelicka 128 pri frekvenci hodin 8MHz

DDRA = 0x00;								// PORTA – nastaveni jako vstup
PORTA = 0x00;								// PORTA – vypnuti pull-up
DDRC = 0xFF;								// PORTC – nastaveni jako vystup
PORTC = 0x00;								// PORTC – vynulováni vystupu

DDRC |= (1<< PC7);							// Nastaveni vystup PWM

TCCR0 |= (1<< COM01)|(1<<WGM01)|(1<<WGM00);
TIMSK |= (1<<TOIE0);
OCR0 = (rozdilCidel/100)*255;				// Rozdil 0-255

sei();										// Povoleni globalniho preruseni

TCCR0 |= (1<<CS00);		
								
while(1)

{		
	adcVolant = Read_ADC(0);				// Mereni napeti volantu
	adcNapravainverz = Read_ADC(1);			// Mereni napeti napravy
	
	adcNaprava=5-adcNapravainverz;			// Otoceni smeru cidla
	
	_delay_ms(50);							// cekani 50ms


// Prepinani smeru toceni volantu			
if (adcVolant<adcNaprava)					// Zataceni doprava
	{						
		PORTC |= (0<<PC6);					// Vynulovani vystupu P1
		
		DDRC |= (1<<PC5);					// Vynulovani vystupu N1
		PORTC |= (0<<PC5);
				
		PORTC |= (0<<PC4);					// Vynulovani vystupu P2
		
		DDRC |= (1<<PC3);					// Vynulovani vystupu N2
		PORTC |= (0<<PC3);
		
		PORTC |= (1<<PC6);					// Zapnuti P1
		DDRC |= (0<<PC3);					// Stav vzsoke impedance N2
		
		rozdilCidel=(adcNaprava-adcVolant);	// Hodnota PWM
	}
else
	if (adcVolant>adcNaprava)				// Zataceni doleva	
	{
		PORTC |= (0<<PC6);					// Vynulovani vystupu P1
		
		DDRC |= (1<<PC5);					// Vynulovani vystupu N1
		PORTC |= (0<<PC5);
		
		PORTC |= (0<<PC4);					// Vynulovani vystupu P2
		
		DDRC |= (1<<PC3);					// Vynulovani vystupu N2
		PORTC |= (0<<PC3);
		
		PORTC |= (1<<PC5);					// Zapnuti P2
		DDRC |= (0<<PC4);					// Stav vzsoke impedance N1
		
		rozdilCidel=(adcVolant-adcNaprava);	// Hodnota PWM
	}
else
	if (adcVolant==adcNaprava)				// Bez pohybu, otevrenz smer pro buzeni motoru
	{
		if (adcVolant<0)
		{
			PORTC |= (0<<PC6);					// Vynulovani vystupu P1
			
			DDRC |= (1<<PC5);					// Vynulovani vystupu N1
			PORTC |= (0<<PC5);
			
			PORTC |= (0<<PC4);					// Vynulovani vystupu P2
			
			DDRC |= (1<<PC3);					// Vynulovani vystupu N2
			PORTC |= (0<<PC3);
			
			PORTC |= (1<<PC6);					// Zapnuti P1
			DDRC |= (0<<PC3);					// Stav vzsoke impedance N2
			
			rozdilCidel=buzeni;					// Nastaveni buzeni
		}
		else
			if (adcVolant>0)
			{
				PORTC |= (0<<PC6);					// Vynulovani vystupu P1
				
				DDRC |= (1<<PC5);					// Vynulovani vystupu N1
				PORTC |= (0<<PC5);
				
				PORTC |= (0<<PC4);					// Vynulovani vystupu P2
				
				DDRC |= (1<<PC3);					// Vynulovani vystupu N2
				PORTC |= (0<<PC3);
				
				PORTC |= (1<<PC5);					// Zapnuti P2
				DDRC |= (0<<PC4);					// Stav vzsoke impedance N1
				
				rozdilCidel=buzeni;					// Nastaveni buzeni
			}
		else
			if (adcVolant==0)
			{
				PORTC |= (0<<PC6);					// Vynulovani vystupu P1
				
				DDRC |= (1<<PC5);					// Vynulovani vystupu N1
				PORTC |= (0<<PC5);
				
				PORTC |= (0<<PC4);					// Vynulovani vystupu P2
				
				DDRC |= (1<<PC3);					// Vynulovani vystupu N2
				PORTC |= (0<<PC3);
									
				rozdilCidel=0;						// Rovna jizda
			}
	}
	
}

}
ISR (TIMER0_OVF_vect)
{
OCR0 = (rozdilCidel/100)*255;
}

:arrow_right: administrator: přejmenováno z "Odlavění programu"

Máš to špatně, pokud to máš na PA.6 a PA.7 tak v programu musíš mít

adcVolant = Read_ADC(6);	// Mereni napeti volantu 
adcNapravainverz = Read_ADC(7);	// Mereni napeti napravy 

:slight_smile:

Příkaz** PORTx |= (0<<PCn); **neudělá vůbec nic. Bit musíš shodit na nulu takto : PORTx &= ~(1<<PCn);.
Zkus přijít na to proč …

Tohle taky asi nebude úplně OK :** ADMUX |= channel & 0b01100110; **

A mimochodem - nikde jsi nenapsal na jakém MCU se to snažíš rozběhnout…

Děkuji toho jsem si nevšiml když jsem měnil čísla kanálů.
Zkusil jsem nahrát opravený program a otestoval jsem jej a na výstupech mám trvale pin 7, 6 a 4 okolo 0,4V a na pinech 5 a 3 mám okolo 4V. Tento stav vůbec nereaguje na změnu hodnoty vstupních napětí.

ADMUX |= channel & 0b01100110; toto mi taky nepříjde ok ale nevím jak jinak zvolit ten kanál.

Dělám na ATmega16 zapomněl jsem to tam uvést.

ATmega16 NEMÁ na pinu 7 TOSC2…

Oprav si shazování bitů a na ADMUX se podívej do datasheetu. To, co jsi napsal do svého programu je naprostý nesmysl:

ADMUX &= 0b01100111; // Cisla kanalu - proč ?
ADMUX |= channel & 0b01100110; - Co to má dělat ?

Hardwarové generování PWM : Podívej se do datasheetu na kterém pinu tento výstup bude (malá nápověda : umístění každého PWM signálu je pevně dané).

ADMUX &= 0b01100111;
ADMUX |= channel & 0b01100110;
Toto jsem dával pro nastavení že má být napětí přiváděno na pin 7 a pin 6.

No jak jsem psal nahoře tak právě si myslím že to PWM mi pojede jen na těch OC např OC1A, ale nebyl jsem si jistý jsetli náhodou nepojede i na tom TOSC.

Shazování na 0 u těch výstupů jsem všude nastavil jako
PORTC &= ~(1<<PC6); a co jsem dával na jedničku tak jsem nechal stejně jako mám.

ATmega16 má na PC7 TOSC2 ale stejně by to nejspíš nešlo protože TOSC je jen pro osciloskop jestli jsem to dobře pochopil. Nevím jistě ale asi se mezi ty vstupy dává nějaký krystal nebo co takže musím použít to OC.

Na TOSC1 a TOSC2 se připojuje krystalu pro asynchronní režim Timeru2.
OCn → n je číslo, od kterého Timeru ten OC signál pochází.

Nějak jsem z toho nepochopil, jak chceš tímhle způsobem přivádět napětí na piny 6 a 7. Kromě toho, že piny 6 a 7 jsou PB5 a PB6 (chtělo by to vyjadřovat se přesně). V MCU je jenom 1 AD převodník. Do ADMUX nacpeš konfiguraci převodníku a výběr vstupu a spustíš převod. Když chceš udělat převod z jiného pinu procesoru, tak do ADMUX nacpeš konfiguraci převodníku a výběr druhého vstupu a spustíš převod.

Jako první instrukci v main máš :
ADMUX |= (1 << REFS0); // Reference 5V + pridano ADLAR (pro rozliseni 8 bitu)

neboli přidáváš 1 do ADMUX na pozici bitu REFS0. Ty víš, jaká hodnota je v ADMUX ? Kromě toho napsat, že přidáváš ADLAR do poznámky je sice hezký, ale MCU o tom opravdu vědět nebude…

Ono by to chtělo si důkladně pročíst datasheet (přinejmenším části k periferiím, které používáš) a přečíst si také něco o Booleovské algebře.

Toto je převzatý ode mne, včetně poznámek co jsem tam měl když jsem dělal pokusy s rozlišením převodníku. Koukám že to převzal komplet i s poznámkama sakum prdum :smiley:

ADMUX &= 0b01100111;
ADMUX |= channel & 0b01100110;
když to nastavím takhle tak mám nastaveno 0 1 AVCC with external capacitor at AREF pin, potom ADLAR na1 a 00111 je nastavení MUX pro ADC7 a 00110 pro ADC6

To překlápění toho ADC nechápu. Jak tam zadefinuju kdy se má se kterého pinu číst? Potřebuju to tak aby se mi střídaly ty vstupy ideálně při změně jednoho s napětí.

Jo mám to tady odsat s tematu ADC Convertor použil jsem to komplet jen jsem nastavil ty kanaly ale asi jsem to špatně pochopil :slight_smile:

Pokud dáš ale ADLAR do jedna, tak máš převodník 8 bitů, a musíš číst horní bajt, tuším že ADCH. Pokud je ADLAR v nule je to 10 bitů a čteš oba bajty - ADC.

Dobrá - tak jinak.

V ADMUX je nějaká neznámá hodnota (0b???)
Máš tam :
**ADMUX |= (1 << REFS0); // Reference 5V + pridano ADLAR (pro rozliseni 8 bitu) **

Návověda : Co se stane, když před provedením této instrukce bude v ADMUX hodnota 0xFF ?
Převzal jsi to od Jendy25, ale vůbec jsi se nad tím nezamyslel. Každý může udělat chybu, ale pokud se to učíš, měl by ses radami ostatních poučit a ne je bez přemýšlení kopírovat.

Tohle snad ani nemá smysl komentovat…

  1. v registru ADMUX je nějaká neznámá hodnota. 0b?1??? (ta 1 v bitu 6 je díky té první instrukci v main)
  2. ADMUX &= 0b01100111; z této hodnoty vymaskuju bity 6,5,2,1,0 a bity 7,4,3 vynuluju => dostávám 0b01?00???
  3. ADMUX |= channel & 0b01100110; (řekněme, že channel = 7) => provede se 0b00000111 (channel) & 0b001100110 => dostávám 0b00000110, provede se | ADMUX => výsledek je 01??011?
  4. V tuto chvíli se spouští AD převodník s ADMUX=0b01??011?.

Takže k tomuto :

přidávám ještě : Přečti si něco o operátorech, bitových operacích a prioritách operací v C-čku.

Jestli se C-čko teprve učíš, tak budiž, ale tohle jsou v tuto chvíli asi nejvíc viditelné mezery.

jj, celkem jsi to domotal, opravdu by se to chtělo kouknout jak se provádí maskování a bitové oprace v C, původně tam bylo toto: ADMUX &= 0xF0; // vloz cislo kanala, vymaskuj nepouzite bity ADMUX |= channel & 0x0F;

Prvně se maskují horní 4 bity a dolní jsou nula (násobíš ADMUX a 0xF0), potom se vynásobí číslo kanálu a maska 0x0F, tzn. horní bity budou nula dolní budou číslo kanálu, a to se potom sečte s ADMUX. Výsledkem je že horní 4 bity se nezmění, a dolní se změní pouze podle čísla kanálu.

Děkuji za rady a že máte se mnou trpělivost začínám v tom takže celkem s tím bojuju. tedka jsem to nějak louskal a snažil se pochopit.

ADMUX &= 0xF0;
ADMUX |= channel & 0x0F;
To zapisování jsem tedy změnil takhle aby se mi přepisovaly ty spodní bity podle čísla kanálu. Původně jsem to maskování pochopil jinak jako že si volím číslo kanálu se kterého se mi mají načítat ty hodnoty proto jsem tam dával něco úplně jiného.

Díval jsem se na ten ADLAR = 0 tak beru tu hodnotu ADCH ale když to chci implementovat k sobě tak moc nevím

Upravil jsem to maskování na 0 a snažil jsem se upravit vše co jsem tady pochytil. Změnil jsem i PWM výstup na PD5 což je OC1A.

#define F_CPU 16000000UL // Definice hodinoveho signalu
#include <avr/io.h>
#include <util/delay.h> // Hlavickovy soubor pro zpozdeni
#include <avr/interrupt.h> // Hlavickovy soubor pro preruseni

#define buzeni ((255/5)*(125/100)); //Definovana hodnota buzeni na 1,25V

unsigned int Read_ADC(unsigned char channel)
{
ADMUX &= 0xF0; // Cisla kanalu
ADMUX |= channel & 0x0F;

ADCSRA |= (1 << ADSC);						// Start prevodu A/D


while(!(ADCSRA & (1<<ADIF)));				// Cekej na priznak skonceni prevodu


return ADC;									//  Navratova hodnota - vysledek A/D prevodu

}

double rozdilCidel=0; // Pro nacteni hodnoty do PWM

int main(void)
{

unsigned int adcVolant, adcNapravainverz, adcNaprava;

ADMUX |=  (1 << REFS0);						// Reference 5V + pridano ADLAR (pro rozliseni 8 bitu)

ADCSRA |= (1 << ADEN) | (1 << ADIF) | (1 <<ADPS2) | (1 <<ADPS1)  | (1 <<ADPS0);

											// Povoleni AD prevodniku, Interrupt flag, preddelicka 128 pri frekvenci hodin 8MHz

DDRA = 0x00;								// PORTA – nastaveni jako vstup
PORTA = 0x00;								// PORTA – vypnuti pull-up
DDRC = 0xFF;								// PORTC – nastaveni jako vystup
PORTC = 0x00;								// PORTC – vynulováni vystupu

DDRC |= (1<< PD5);							// Nastaveni vystup PWM

TCCR0 |= (1<< COM01)|(1<<WGM01)|(1<<WGM00);
TIMSK |= (1<<TOIE0);
OCR0 = (rozdilCidel/100)*255;				// Rozdil 0-255

sei();										// Povoleni globalniho preruseni

TCCR0 |= (1<<CS00);		
								
while(1)

{		
	adcVolant = Read_ADC(6);				// Mereni napeti volantu
	adcNapravainverz = Read_ADC(7);			// Mereni napeti napravy
	
	adcNaprava=5-adcNapravainverz;			// Otoceni smeru cidla
	
	_delay_ms(50);							// cekani 50ms


// Prepinani smeru toceni volantu			
if (adcVolant<adcNaprava)					// Zataceni doprava
	{						
		PORTC &= ~(1<<PC6);					// Vynulovani vystupu P1
		
		DDRC |= (1<<PC5);					// Vynulovani vystupu N1
		PORTC &= ~(1<<PC5);
				
		PORTC &= ~(1<<PC4);					// Vynulovani vystupu P2
		
		DDRC |= (1<<PC3);					// Vynulovani vystupu N2
		PORTC &= ~(1<<PC3);
		
		PORTC |= (1<<PC6);					// Zapnuti P1
		DDRC |= (0<<PC3);					// Stav vzsoke impedance N2
		
		rozdilCidel=(adcNaprava-adcVolant);	// Hodnota PWM
	}
else
	if (adcVolant>adcNaprava)				// Zataceni doleva	
	{
		PORTC &= ~(1<<PC6);					// Vynulovani vystupu P1
		
		DDRC |= (1<<PC5);					// Vynulovani vystupu N1
		PORTC &= ~(1<<PC5);
		
		PORTC &= ~(1<<PC4);					// Vynulovani vystupu P2
		
		DDRC |= (1<<PC3);					// Vynulovani vystupu N2
		PORTC &= ~(1<<PC3);
		
		PORTC |= (1<<PC5);					// Zapnuti P2
		DDRC |= (0<<PC4);					// Stav vzsoke impedance N1
		
		rozdilCidel=(adcVolant-adcNaprava);	// Hodnota PWM
	}
else
	if (adcVolant==adcNaprava)				// Bez pohybu, otevrenz smer pro buzeni motoru
	{
		if (adcVolant<0)
		{
			PORTC &= ~(1<<PC6);					// Vynulovani vystupu P1
			
			DDRC |= (1<<PC5);					// Vynulovani vystupu N1
			PORTC &= ~(1<<PC5);
			
			PORTC &= ~(1<<PC4);					// Vynulovani vystupu P2
			
			DDRC |= (1<<PC3);					// Vynulovani vystupu N2
			PORTC &= ~(1<<PC3);
			
			PORTC |= (1<<PC6);					// Zapnuti P1
			DDRC |= (0<<PC3);					// Stav vzsoke impedance N2
			
			rozdilCidel=buzeni;					// Nastaveni buzeni
		}
		else
			if (adcVolant>0)
			{
				PORTC &= ~(1<<PC6);					// Vynulovani vystupu P1
				
				DDRC |= (1<<PC5);					// Vynulovani vystupu N1
				PORTC &= ~(1<<PC5);
				
				PORTC &= ~(1<<PC4);					// Vynulovani vystupu P2
				
				DDRC |= (1<<PC3);					// Vynulovani vystupu N2
				PORTC &= ~(1<<PC3);
				
				PORTC |= (1<<PC5);					// Zapnuti P2
				DDRC |= (0<<PC4);					// Stav vzsoke impedance N1
				
				rozdilCidel=buzeni;					// Nastaveni buzeni
			}
		else
			if (adcVolant==0)
			{
				PORTC &= ~(1<<PC6);					// Vynulovani vystupu P1
				
				DDRC |= (1<<PC5);					// Vynulovani vystupu N1
				PORTC &= ~(1<<PC5);
				
				PORTC &= ~(1<<PC4);					// Vynulovani vystupu P2
				
				DDRC |= (1<<PC3);					// Vynulovani vystupu N2
				PORTC &= ~(1<<PC3);
									
				rozdilCidel=0;						// Rovna jizda
			}
	}
	
}

}
ISR (TIMER0_OVF_vect)
{
OCR0 = (rozdilCidel/100)*255;
}

Ještě program vkládej jako program je tam na to tag: code a konec /code, klikni na zaslat odpověď a tam to je, takhle aby se v tom prase vyznalo :slight_smile:

K tomu ADC doporučuju si přečíst toto: svetelektro.com/clanky/programuj … t-472.html
zde jsou popsané bity a co dělá i ADLAR, to samé je i v datasheetu, ale anglicky :slight_smile:

A co se tak dívám, nastuduj si operace s porty:

Třeba toto:

PORTC |= (1<<PC5); // Zapnuti P2 DDRC |= (0<<PC4); // Stav vzsoke impedance N1
Co když DDRC.4 bude v log.1, potom ta tvá operace udělá toto na bitu 4 (1+0=1) tedy nic se nezmění, a proč je jednou PC5 a potom DDRC 4?

Správně by mělo být např:

PORTC |= (1<<PC4); // Pull UP je zapnutý DDRC &= ~(1<<PC4); // Pin jako vstup

Děkuji za odkaz tedka to musím projet a nějak pochopit :slight_smile:
Jak jsem se díval tak je to hodně podobné jako datasheet ale jsou tam i kody což je faj at mám větší představu.
Ale přímo pro ukládání dvou hodnot toho moc není ani nikde na netu nejsou nějaké podrobnější návody, nebo jsem aspon žádné nenašel.

Toto najdeš už jenom v programování #C, PORT,ADC,DDR atd je HW procesoru, ale to co a jak se ukládá, přesouvá, proměnné atd, to je už #C

:arrow_right: administrator: příspěvek byl upraven
Předchozí příspěvky se necitují.

ale i v C jdou překlápět ty hodnoty přece? C# jsem trochu taky dělal a příjde mi složitější než C takže bych raději zůstal u C. Potřebuju hlavně abych rozchodil to ADC a pak už ty vystupy to není takový problém až zase na ten s PWM.

Ještě bych se chtěl zeptat jak to je s log 0 u te ATmegy bere se to jako 0V nebo jako GND? V aplikaci bych potřeboval abych měl GND. Když to nebere jako zem tak tam teče malé napětí a to už je pro mě špatně.