Sida 1 av 1

Attiny4313 4ch PWM led AVR problem

Postat: 9 december 2013, 14:16:52
av LordofSwe
Hej,

Jag håller på med lite julbelysning så här och hade några attiny4313 över från tidigare projekt.
Nu har jag kopplat en RGB diod samt en varmvit diod till den.
Tanken är att kunna bygga en enkel "flickery flame" att montera in i en värmeljusstake.

Jag har skrivit lite kod för att testa och får ett litet lustigt resultat.
PWM dimmning fungerar perfekt på OCR0A och OCR1B utan problem.
PWM dimmning fungerar INTE alls på OCR0B och OCR1A utan där blinkar dioden bara (utan märkbar förändring i frekvensen).

Min första tanke var att felet skulle ligga i själva adresseringen men jag kan inte finna något problem och hoppas ni kan hjälpa mig.

Kod: Markera allt

#define F_CPU   16000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include <util/delay.h>
#include <inttypes.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include "pwm_logtable.h"

#define PINBLU PD5        //pin  9 - OC0B - 8bit
#define PINRED PB2        //pin 14 - OC0A - 8bit
#define PINGRN PB3        //pin 15 - OC1A -16bit
#define PINWHI PB4        //pin 16 - OC1b - 16bit

#define PWM_PORT_B PORTD
#define PWM_PORT_R PORTB
#define PWM_PORT_G PORTB
#define PWM_PORT_W PORTB

#define PWM_DDR_B DDRD
#define PWM_DDR_R DDRB
#define PWM_DDR_G DDRB
#define PWM_DDR_W DDRB

#define BLU_LED_OFF PWM_PORT_B &= ~(1<<PINBLU)
#define RED_LED_OFF PWM_PORT_R &= ~(1<<PINRED)
#define GRN_LED_OFF PWM_PORT_G &= ~(1<<PINGRN)
#define WHI_LED_OFF PWM_PORT_W &= ~(1<<PINWHI)

#define BLU_LED_ON PWM_PORT_B |= 1<<PINBLU
#define RED_LED_ON PWM_PORT_R |= 1<<PINRED
#define GRN_LED_ON PWM_PORT_G |= 1<<PINGRN
#define WHI_LED_ON PWM_PORT_W |= 1<<PINWHI

//#define STARTUP_EFFECT 0

typedef struct _color {
    uint8_t r;
	uint8_t g;
	uint8_t b;
  	uint8_t w;
} Color;

Color led_curr_color;

int main(void){

#define Test_Mode
#define TestColor
    
#if defined (TestColor)
    led_curr_color.r = 0;
    led_curr_color.b = 0;
    
    led_curr_color.g = 0;
    led_curr_color.w = 0;
#else
    led_curr_color.r = 100;
    led_curr_color.g = 0;
    led_curr_color.b = 60;
    led_curr_color.w = 40;
#endif

#if defined ( Test_Mode )
    int8_t rMod=-1, gMod=1, bMod=1, wMod=-1;
    led_curr_color.r = 0;
    led_curr_color.b = 0;
    
    led_curr_color.g = 0; //255;
    led_curr_color.w = 0; //255;
#endif
    

    PWM_DDR_R |= 1<<PINRED;
	PWM_DDR_G |= 1<<PINGRN;
	PWM_DDR_B |= 1<<PINBLU;
    PWM_DDR_W |= 1<<PINWHI;
    
	ICR1   = 0xFFFF;                            //Timer1 "TOP value"
	
	TIFR  =  (1<< TOV1) | (1<< TOV0);			//Timer0 clear interrupts on overflow
	TIMSK =  (1<< TOIE1) | (1<< TOIE0);         //Timer0 enable overflow ISR
    
//Setup Timer1 -16bit Timers
    TCCR1B = (1<< WGM12) | (1<< WGM13)          //Sets Timer1 to PWM(Fast), ICR(TOP), TOV(TOP) 1,1,1,0
            |(0<< CS12)|(0<< CS11)|(1<< CS10);  //Timer1 start, no Prescaling 0,0,1
    TCCR1A = (1<< WGM11) | (0<< WGM10)          //see TCCR1B
            |(1<< COM1A1) | (1<< COM1A0) 		//Set to Timer1A to Compare match, Clear on TOP
            |(1<< COM1B1) | (1<< COM1B0);       //Set to Timer1B to Compare match, Clear on TOP
    
//Setup Timer0 - 8bit Timers
    TCCR0A = (1<< WGM01) | (1<< WGM00)          //Sets Timer0 to Fast PWM, TOP(0xFF), OCRX updates(TOP), TOV(TOP) 0,1,1
            |(1<< COM0A1) | (1<< COM0A0)		//Set OC0A on Compare, clear at TOP
            |(1<< COM0B1) | (1<< COM0B0);       //Set OC0B on Compare, clear at TOP (if WGM02 TOP(0xFF))
    TCCR0B = //(0<< WGM02) //(0<< WGM02)          //see TCCR0A
            //|(0<< CS02)|(0<< CS01)|
    (1<< CS00);  //Timer start, no Prescaling 0,0,1
    
    sei();										//Enable Interrupts
    
	for(;;) {
        
        #if defined( Test_Mode )
            if ((led_curr_color.r == 255) || (led_curr_color.r == 0)) rMod =- rMod;
            if ((led_curr_color.g == 255) || (led_curr_color.g == 0)) gMod =- gMod;
            if ((led_curr_color.b == 255) || (led_curr_color.b == 0)) bMod =- bMod;
            if ((led_curr_color.w == 255) || (led_curr_color.w == 0)) wMod =- wMod;
         
            //led_curr_color.r += rMod;   //8-bit - OCR0A OK
            //led_curr_color.g += gMod;    //16-bit - OCR1A
            led_curr_color.b += bMod;   //8-bit - OCR0B
            //led_curr_color.w += wMod;   //16-bit - OCR1B OK
         
            _delay_ms(100);
        #endif
    }
    return 1;
}

ISR(TIMER1_OVF_vect){
    static uint16_t pwm_period_whi;
    static uint16_t pwm_period_grn;
    
    pwm_period_whi = PWM_LOG(led_curr_color.w);
    pwm_period_grn = PWM_LOG(led_curr_color.g);
	
    OCR1A = pwm_period_grn;
    OCR1B = pwm_period_whi;
}

ISR(TIMER0_OVF_vect) {
    static uint16_t pwm_period_blu;
    static uint16_t pwm_period_red;
    
    pwm_period_red = 255 - led_curr_color.r; //PWM_LOG(led_curr_color.r);
    pwm_period_blu = 255 - led_curr_color.b; //PWM_LOG(led_curr_color.b);

    OCR0A = pwm_period_red;
    OCR0B = pwm_period_blu;
}


Mina tidigare programmeringar har varit skrivna med arduino och tänkte härmed göra ett försök för att gå över till AVR.
Olyckligtvis tar arduino en av de tillgängliga timers som jag har förstått det vilket gör det svårt som jag vill ha alla 4 till detta projekt.
Samtidigt så ser jag det som ett intressant projekt för att lära sig AVR programmering lite bättre.

Tack på förhand...

Re: Attiny4313 4ch PWM led AVR problem

Postat: 9 december 2013, 21:01:54
av polmanswe
Har inte granskat koden men har inte den processor bara 2 eller 3 timrar???
Är trött och sliten så jag kan tänka helt i nattmössan men kolla datablad.

Re: Attiny4313 4ch PWM led AVR problem

Postat: 9 december 2013, 23:28:18
av jesse
Det var nog nattmössan du hade på dig, ja. TS använder två timers, nämligen timer0 och timer1. De kan generera två separata PWM-signaler vardera.
Jag ser inget fel = orkar inte lusläsa koden så noga (än), fast jag ser något annat:

Kod: Markera allt

ISR(TIMER0_OVF_vect) {
    static uint16_t pwm_period_blu;
    static uint16_t pwm_period_red;
    
    pwm_period_red = 255 - led_curr_color.r; //PWM_LOG(led_curr_color.r);
    pwm_period_blu = 255 - led_curr_color.b; //PWM_LOG(led_curr_color.b);

    OCR0A = pwm_period_red;
    OCR0B = pwm_period_blu;
}
Jag ser inte någon nytta av att man anger två statiska variabler här. De tilldelas ju ändå ett nytt värde varje gång interruptet utförs. Troligtvis optimeras de bort, men ju fler "mellanled" man har desto fler ställen det kan gå fel på. Jag hade skrivit så här istället:

Kod: Markera allt

ISR(TIMER0_OVF_vect) {
    OCR0A = 255 - led_curr_color.r; //PWM_LOG(led_curr_color.r);
    OCR0B = 255 - led_curr_color.b; //PWM_LOG(led_curr_color.b);
}
Dessutom är OCR0A, OCR0B,OCR1A och OCR1B vanligtvis buffrade på AVR-processorer (vet inte om det gäller för Attiny4313, men det står i så fall i beskrivningen av registren under respektive timer), vilket innebär att värdet läggs i en buffer tills timern slår om till noll. Då laddas det in i det verkliga registret "OCRnX". Detta gör att du inte kan få glitchar på grund av dålig timing. DÄrför kan du egentligen uppdatera dessa direkt i main() utan att alls behöva använda interrupt.

EDIT: Jo, det gäller även ATtiny4313:
The OCR0x Registers are double buffered when using any of the Pulse Width Modulation
(PWM) modes. ... The double buffering synchronizes the update of the OCR0x Compare
Registers to either top or bottom of the counting sequence. The synchronization prevents the
occurrence of odd-length, non-symmetrical PWM pulses, thereby making the output glitch-free.

Re: Attiny4313 4ch PWM led AVR problem

Postat: 9 december 2013, 23:33:08
av sodjan
LordofSwe, som du märker så har du postat för mycket kod. :-)
Vad har du kört för felsökning själv?
Har du något test case som *enbart* kör den PWM som inte fungerar?
D.v.s har du verifierat att det alls ska fungera så som du tror?
Kan du få OCR0B och OCR1A att fungera på något sätt alls?

Re: Attiny4313 4ch PWM led AVR problem

Postat: 9 december 2013, 23:57:53
av jesse
Du har valt mode "Set OC0A on Compare Match, clear OC0A at TOP", vilket ger en inverterad PWM-signal ut, sedan inverterar du värdet i interruptet:

Kod: Markera allt

OCR0A = 255 - led_curr_color.r;
Enklare vore kanske "Clear OC0A on Compare Match, set OC0A at TOP" och sedan OCR0A = led_curr_color.r; :D

Men det löser ju tyvärr inte problemet.

Re: Attiny4313 4ch PWM led AVR problem

Postat: 10 december 2013, 07:08:44
av void
"compound assignment " har blivit fel på ett par ställen. Du har skrivit "rMod =- rMod;" istället för "rMod -= rMod;"

Re: Attiny4313 4ch PWM led AVR problem

Postat: 10 december 2013, 07:51:50
av Icecap
jesse: en static deklaration i en rutin håller kvar variabeln oförändrat! Den assignas inte igen vid varje interrupt!

Re: Attiny4313 4ch PWM led AVR problem

Postat: 10 december 2013, 08:50:30
av Zeela
Icecap, så du menar att det i koden nedan (vilken var den kod jesse syftade på antar jag) inte tilldelas något till de statiska variablerna varje interrupt?

Kod: Markera allt

ISR(TIMER0_OVF_vect) {
    static uint16_t pwm_period_blu;
    static uint16_t pwm_period_red;
    
    pwm_period_red = 255 - led_curr_color.r; //PWM_LOG(led_curr_color.r);
    pwm_period_blu = 255 - led_curr_color.b; //PWM_LOG(led_curr_color.b);

    OCR0A = pwm_period_red;
    OCR0B = pwm_period_blu;
}

Re: Attiny4313 4ch PWM led AVR problem

Postat: 10 december 2013, 09:41:27
av snigelen
Några kommentarer:
  • led_curr_color skall vara deklarerad volatile om den används både i main och i avbrottsrutinerna.
  • static på variablerna i avbrottsrutinerna tillför inget eftersom du inte använder värdena från föregående avbrott (som Jesse sade). Det genererar möjligen bara ineffektivare kod.
  • Varför använder du överhuvudtaget avbrott bara för att ändra de värden som sätts i main? Kan du inte sätta de värden du vill ha direkt?
Sedan kunde det kanske vara enklare att konfigurera timer1 för 8 bitar så att alla kanaler får samma upplösning?

Re: Attiny4313 4ch PWM led AVR problem

Postat: 10 december 2013, 10:29:25
av LordofSwe
Jag älskar detta forum måste jag bara säga, ni är oerhört snabba på att svara och ni gör verkligen ert bästa för att hjälpa till.
sodjan skrev:LordofSwe, som du märker så har du postat för mycket kod. :-)
Vad har du kört för felsökning själv?
Har du något test case som *enbart* kör den PWM som inte fungerar?
D.v.s har du verifierat att det alls ska fungera så som du tror?
Kan du få OCR0B och OCR1A att fungera på något sätt alls?
Jag är kanske lite för vann de internationella arduino forumen där kommentarerna som oftast är "We need to se all the code, pleas past them for further assistance" kommentarer och det var därför jag tänkte att det var bättre att förekomma.

Jag har testat koden på där jag stegvis "dimma" upp och ner var diod för sig/tillsammans genom att kommentera bort olika segment ur main rutinen där jag kommenterar bort olika segment för att testa på olika sätt med variation i PWM. Och när jag testkör funkar ju OCR0A och OCR1B precis som det är tänkt, men inte de andra två, och jag kan inte hitta några skillnader mellan dem i själva koden..

Kod: Markera allt

        #if defined( Test_Mode )
            if ((led_curr_color.r == 255) || (led_curr_color.r == 0)) rMod = -rMod;
            if ((led_curr_color.g == 255) || (led_curr_color.g == 0)) gMod = -gMod;
            if ((led_curr_color.b == 255) || (led_curr_color.b == 0)) bMod = -bMod;
            if ((led_curr_color.w == 255) || (led_curr_color.w == 0)) wMod = -wMod;
         
            //led_curr_color.r += rMod;   //8-bit - OCR0A OK
            //led_curr_color.g += gMod;    //16-bit - OCR1A
            led_curr_color.b += bMod;   //8-bit - OCR0B
            //led_curr_color.w += wMod;   //16-bit - OCR1B OK
         
            _delay_ms(100);
        #endif
Den enkla beskrivningen av problemet är att när jag kör ovanstående test kod så blinkar(tänds och släcks) med ungefär samma periodicitet som jag ställer i _delay_ms vilket gör att jag tror att de verkar blinka var gång de får en uppdatering om nya värden. ska dubbelkolla efter jag fixat några ärenden nu på morgonen med en bättre och mer väl testad beskrivning. Ställer jag in statiska värden lyser den, men det är svårt att avgöra om jag har någon PWM (men ska testa det mer ingående)
jesse skrev:Du har valt mode "Set OC0A on Compare Match, clear OC0A at TOP", vilket ger en inverterad PWM-signal ut, sedan inverterar du värdet i interruptet:

Kod: Markera allt

OCR0A = 255 - led_curr_color.r;
Enklare vore kanske "Clear OC0A on Compare Match, set OC0A at TOP" och sedan OCR0A = led_curr_color.r; :D

Men det löser ju tyvärr inte problemet.
Tack, som du säger så löser det inte problemet men det är bra att känna till som jag just nu har många tillfällen där jag "inveterar PWM signalen" vilket nog mest beror på att man "stulit" kod lite här och där för att lära sig ;)
void skrev:"compound assignment " har blivit fel på ett par ställen. Du har skrivit "rMod =- rMod;" istället för "rMod -= rMod;"
Nej koden stämmer i alla fall på det stället (kanske fins bättre sätt att skriva det på) men tanken är att +rMod tills värdet är 255, då istället -rMod tills värdet är 0.
Ett tydligare sätt att skriva hade nog varit rMod = -rMod; för att undvika missförstånd (det är nu förbättrar som du ser i kommentaren ovan)

En fundering som slog mig nu på morgonen är om felet kan ligga i hur säkringarna (?) (=fuses) är satta? då denna variant kör med en 16Mhz extern kristall medan arduino bootloadern för attiny4313 enbart kör 8MHz.

Kod: Markera allt

FUSES: -U lfuse:w:0xde:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m
(hämtat från http://www.engbedded.com/fusecalc/)

Re: Attiny4313 4ch PWM led AVR problem

Postat: 10 december 2013, 10:41:07
av LordofSwe
snigelen skrev:Några kommentarer:
  • led_curr_color skall vara deklarerad volatile om den används både i main och i avbrottsrutinerna.
  • static på variablerna i avbrottsrutinerna tillför inget eftersom du inte använder värdena från föregående avbrott (som Jesse sade). Det genererar möjligen bara ineffektivare kod.
  • Varför använder du överhuvudtaget avbrott bara för att ändra de värden som sätts i main? Kan du inte sätta de värden du vill ha direkt?
Sedan kunde det kanske vara enklare att konfigurera timer1 för 8 bitar så att alla kanaler får samma upplösning?
Jag provar att ändra till volatile.
Jag provar att ta bort static.

Tanken är (men kanske är fel) att använda 16bitars timer för timer1 så jag även kan använda den för "house hold functions" (vet ej om man kallar det så inom programmering) för att uppdatera effekterna m.m. Koden jag tittat på är för Attiny85 och 84 vilken kasnke fungerar annorlunda så du har säkert rätt. Tanken är sedan att jag ska använder avbrotts timern för timer0 (8-bitar) för att simulera 16bitar genom att kompensera för skillnaden. För att ge finare dimkurva vid lägre värden. jag har dock provat att flytta uppdateringen för timerna till main vilket inte ger någon skillnad d.v.s samma fel.
Orginal koden:

Kod: Markera allt

pwm_period_blu = PWM_LOG(led_curr_color.b);
         count_blu   = pwm_period_blu / 256;
         modulus_blu = pwm_period_blu % 256;
        
        OCR0B = modulus_blu;

Re: Attiny4313 4ch PWM led AVR problem

Postat: 10 december 2013, 10:49:57
av sodjan
> Jag är kanske lite för vann de internationella arduino forumen där kommentarerna som oftast är "We need
> to se all the code, pleas past them for further assistance" kommentarer och det var därför jag tänkte
> att det var bättre att förekomma.

Helt OK. :-)
Jo, man vill ju normalt se helheten, men inte mer! :-)
Min fråga var mer ifall den postade koden var det minsta möjliga
exemplet som uppvisar det aktuella problemet? Jag vet inte mycket om
AVR men blir bara fundersam när det postas mycket kod. Det handlade
mer om felsäkningsmetodik än PWM och AVR... :-)