Nezadouci blikani LED pri PWM regulaci

Ahoj.Mam k MCU pripojenou RGB LED a ovladam ji pomoci softwarovyho PWM ktery sem si napsal.Dioda mi ale ocas nepravidelne problikne.Deje se to kdyz v hlavni smycce napriklad plynule prolinam barvy.Kdyz v hlavni smycce dokola nastavuji statickou hodnotu PWM promenych tak se nic nedeja a dioda krasne sviti.
Dalsi pripad je ze kdyz diodu ovladam pomoci seriove linky a barvy nemenim tak problikava taky.Jakmile ale ser linku odpojim blikani prestane a dioda sviti podle posledni prijatych dat v klidu dal bez problikavani.

[code]#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

// Konstanty
#define TIME 50
#define REG_TCNT0 190 // frekvence PWM

#define CLOCK_SPEED 8000000 // systemovy hodiny 8Mhz
#define BAUD 9600 // rychlost ser. linky
#define MY_UBRR (CLOCK_SPEED/16/BAUD)-1 // vypocet registru UBRR

// Pozice
#define BITPOS_0 0
#define BITPOS_1 1
#define BITPOS_2 2
#define PORT_1 PORTB

// Makra
#define SET_BIT(BYTE,BIT)(BYTE|=(1<<BIT))
#define CLEAR_BIT(BYTE,BIT)(BYTE&=~(1<<BIT))
#define CHECK_BIT(BYTE,BIT)(BYTE&=(1<<BIT))

// Deklarace globalnich promennych
volatile unsigned char Red = 0,Green = 0,Blue = 0;

// Obsluha preruseni TIMER0 pri preteceni
ISR(TIMER0_OVF_vect)
{
static unsigned char Pwm;

TCNT0 = REG_TCNT0;
Pwm++;

if ((Pwm == 0) && (Red != 0)) SET_BIT(PORT_1,BITPOS_0);
if ((Pwm == Red) && (Pwm != 255)) CLEAR_BIT(PORT_1,BITPOS_0);

if ((Pwm == 0) && (Green != 0)) SET_BIT(PORT_1,BITPOS_1);
if ((Pwm == Green) && (Pwm != 255)) CLEAR_BIT(PORT_1,BITPOS_1);

if ((Pwm == 0) && (Blue != 0)) SET_BIT(PORT_1,BITPOS_2);
if ((Pwm == Blue) && (Pwm != 255)) CLEAR_BIT(PORT_1,BITPOS_2);
}

// Obsluha pri prijmu dat ze ser. linky
ISR(USART_RXC_vect)
{
static unsigned char Pole[3];
static unsigned char Index;

Pole[Index++] = UDR;
if(Index == 3)
{
Index = 0;

Red = Pole[0];
while ( !( UCSRA & (1<<UDRE)) );  
UDR = Red;
    
Green = Pole[1];
while ( !( UCSRA & (1<<UDRE)) );
UDR  = Green;

Blue = Pole[2];
while ( !( UCSRA & (1<<UDRE)) );
UDR = Blue;

}
if (CHECK_BIT(PORTC,BITPOS_1)) CLEAR_BIT(PORTC,BITPOS_1);
else SET_BIT(PORTC,BITPOS_1);
}

void main (void)
{
// Inicializace mcu
DDRA = 0xFF; // port A jako vystup
DDRB = 0xFF; // port B jako vystup
DDRC = 0xFF; // port C jako vystup
DDRD = 0xFE; // port D jako vystup

// Zhasne vsechny LED
PORTA = 0x00;
PORTB = 0x00;
PORTC = 0x00;
PORTD = 0x00;

// Globalni povoleni preruseni
sei();

// Nastaveni TIMER0 a Preruseni (Timer0 zajistuje softwarove PWM)
TCNT0 = REG_TCNT0;
TIMSK |= (1<<TOIE0) ; // Preruseni pri preteceni
TCCR0 |= (1<<CS01); // Normalni rezim,delicka 8.,

// Nastaveni Serive linky
UBRRH = (unsigned char)(MY_UBRR>>8); // Nastavi rychlost
UBRRL = (unsigned char)MY_UBRR;
//UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE); // Zapne vysilac a prijimac,povoli preruseni pri prijmu a dokonceni vysilani
UCSRC = (1<<URSEL)|(1<<USBS)|(1<<UCSZ0)|(1<<UCSZ1); // 8 datovych bitu,2 stop bit

// MAIN LOOP
for(;:wink:

{

for (; Green != 255 ; Green++) 		{ _delay_ms(TIME); }
for (; Red != 0 ; Red--) 			{ _delay_ms(TIME); }
for (; Blue != 255 ; Blue++) 		{ _delay_ms(TIME); }
    for (; Green != 0 ; Green--) 		{ _delay_ms(TIME); }
for (; Red != 255 ; Red++)			{ _delay_ms(TIME); }	
for (; Green != 255 ; Green++) 		{ _delay_ms(TIME); }
    for (; Blue != 0 ; Blue--) 			
{ 
  Green--;
  _delay_ms(TIME); 
    }

}

}
[/code]

pls help

Těžko říct, když to nevidíme. Ten popis je dost strohej. Můžeš pro začátek zrušit zpětný odesílání hodnot do PC, protože si tím na docela dlouho (nevhodnou konstrukcí kódu) blokuješ celej procesor včetně přerušení od timeru.

No prave sem psal ze se to deje i kdyz nepouzivam rizeni z pc.v tom kodu co sem poslal je prijimac i vysilac vypnuty.A zpusobuje to kod v hlavni smycce.Co myslis tou nevhodnou konstrukci kodu?Ono to stim bude soviset protoze kdyz pridam do ty obsluhy pri prijmu este neco slozitejsiho tak to dela este vic.Sou to takovy problesky,zdase ze sablesky bily barvy.

“while ( !( UCSRA & (1<<UDRE)) );” tenhle řádek trvá dost dlouho - musí se počkat, než se odešle celý byte a to je při malých přenosových rychlostech vzhledem k procesorovýmu času nesrovnatelná doba. Teď ti to způsobeje blokaci procesoru v milisekundách. Pokud bys to chtěl zachovat, musíš opět použít pro odesílání přerušení (udre, případně txc).

Pravděpodobně se ti stává, že při blokaci procesoru nestihneš přerušení od timeru (všechna přerušení jsou blokovaná) a dioda ti zůstane svítit. Navíc v tom přerušení zkracuješ timer přednastevením. Když je blokovaný, timer jede celej. Bylo by lepší použít mód, kde má timer TOP v jinym registru (některý z pwm režimů, samozřejmě nemusíš využívat přerušení pwm a nechat to jak to máš).

V mainu se zase může stát, že snížíš hodnotu pro pwm, jenže už máš napočítáno víc a stopovací podmínka díky “==” už neproběhne. Tam bys měl použít “>=”.

Opet piityy vse vyresil !!! :smiley:

To zabralo na vse!!!

Samozrejme bude taky lepsi kdyz vyuziju i ten pwm rezim a bude to efetivnejsi.Diky PANE.

Neprisel sem na to jak vyuzit pwm rezim k vytvoreni vice pwm signalu jak ja potrebuji.V pwm rezimu pretyka citac porad dokola a hardvarove si porovnava obsah citace s pwm registrem,ale pouze s jednim nebo dvouma.Proto sem ji vytvoril ten citac kterej vyvolava preruseni (1 preruseni=1 tik citace v rezimu pwm).A v tom preruseni si muzu porovnavat libovolnej pocet pwm registru s promenou ktera se inkrementuju v preruseni.
Jediny co me napadlo je ze nebudu prednastavovat citac v preruseni,ale necham ho bezet na plnej 256 tiku.K tomu vypnu delicku.Tim dosahnu, ale PWM frekvence 122Hz pri mim taktu 8MHz.Coz je zbytecne moc ale este se to da.

Dobrý den, téma už je uzavřené ale chtěl jsem se zeptat jenom na jednu jednoduchou věc v kódu. Jde o toto:

UCSRC = (1<<URSEL)|(1<<USBS)|(1<<UCSZ0)|(1<<UCSZ1)

Právě se učím v assembleru a jde mi přímo o funkci tohohle: registr=(k1<<neco1)|(k2<<neco2)|…

Díky! :unamused:

To nastavuje jednotlivy bity v registru,jestli ale delas assembler tak te tohle nemusi zajimet preci,ne?| or,<< rotace

Přesně tak, jde o zápis z Céčka, v asm tohle nelze (je pravda, že některé překladače asm tohle umějí, ale myslím, že AVRkový nikoli).

“<<” je bitový posun vlevo. Když by ses podíval do definičního souboru procesoru, najdeš tam něco
jako například “#define USBS 3”.
Preprocesor potom v (1<<USBS) nahradí název bitu číslem: (1<<3), pak to spočítá. Výsledkem při 8-bitovém výpočtu je “1” posunutá o 3 bity doleva, tedy “00001000”. Takhle se to spočítá ve všech závorkách a pak se provede se všemi mezivýsledky OR (log. součet). Toto počítá ještě překladač (pokud jsou tam jen konstanty jako zde) - proč tím zaměstnávat procesor, když výsledek je znám v době překladu.
Co z toho vyleze se dá do proměnné vlevo od rovnítka.

Právě že jsem to viděl v assembleru, v kódu který mi dal člvke co mě zasvětil do jednočipů. Po krokování jsem zjistil že to nuluje ostatní bity což neni občas dobrý. Přiznám se že to asi přesně nechápu, kor když k je větší jak jedna. Ale co snad to potřebovat nebudu. Nechám to plavat… :unamused:

Díky

…po předchozí odpovědi mi to je jasný. AVR studio4 s překladačem na assembler to právě podporuje! :slight_smile: Dík

Jde to taky.

ldi r16, (1<<URSEL)|(1<<USBS)|(1<<UCSZ0)|(1<<UCSZ1) out UCSRC, r16

Pokud nechceš ostatní bity nulovat (ve většině případů, protože si tím můžeš rozhodit předchozí nastavení), nesmíš do registru rovnou zapsat, ale nejdřív ho načíst, provést s ním a požadovanou hodnotou OR (v případě nulování bitů jde o AND s invertovanou hodnotou) a zapsat zpátky.
Není to “atomic” operace, ve chvíli zápisu hodnoty zpět do registru už nemusí být jeho obsah platný, je třeba tomu věnovat pozornost pokud by to mohlo způsobit problémy (dočasná deaktivace přerušení apod.).
Při modifikaci 1 bitu u registru v bitově adresovatelné oblasti je rychlejší sbi/cbi.

AB: tím líp :slight_smile: