Problem med C-kod för en RGB-lampa - Lång kod

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Problem med C-kod för en RGB-lampa - Lång kod

Inlägg av Korken »

Hej!

Jag har skrivit en C-kod för min RGB-lampa men det tycks inte fungera som den ska.
Den hoppar totalt över alla funktioner och lägger sig på ca 50% på PWMen.
Så, init_seq(), hoppar den över vilket som annars ska fadea mellan Röd, Grön, Blå sen bli vit.
Interupten för "change program" tycks inte heller fungera.
Jag har setat länge med att försöka hitta ett fel men hittar inget. :(

Röd sitter på PORTB.0, grön på PORTB.1 och blå på PORTB.2 sen interupten på PINB.5.

Kan någan ta en liten stund att kolla igenom denna kod på 261 rader?

Edit1: Har ändrat koden till hur den ser ut nu.
Edit2: Ändrat efter medelandet 24-sep-2006 21:50.

Kod: Markera allt

/*
This code is specially made for an Atmel AVR ATtiny45.
*/

#define F_CPU 8000000
#define RGB_PIN (1<<0)|(1<<1)|(1<<2)

#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/eeprom.h>
#include <stdlib.h>

void init_seq(void);
void rgb_program(void);
void rgb_comp(uint8_t, uint8_t, uint8_t);
void rgb_counter(void);
void rgb_pwm(void);
void delay_x(void);

volatile uint8_t rgb[3] = {0,0,0};
volatile uint8_t rgb_compare[3] = {0,0,0};
volatile uint8_t program_nr = 0;
volatile uint16_t counter = 0;
volatile uint8_t pwm_counter = 0;
volatile uint16_t delay_time = 0;
volatile uint8_t buttom_time = 0; 

int main(void)
{
	unsigned int addr = 1; // EEPROM adress.
	uint8_t out_value = eeprom_read_byte((unsigned char *) addr);
	
	// Increase value on that location in EEPROM and write it to down.
	out_value++;
	eeprom_write_byte((unsigned char *) addr, out_value);
	
	// Use out_value to get a different rand() after each start.
	srand(out_value);
	
	// Set DDRB0,1,2 for output, rest for input.
	DDRB = RGB_PIN;
	PORTB = ~RGB_PIN; // Enable pullup on the inputs and set RGB Pins to low.
	
	// The interupt sequence for the "Change Program" button.
	PCMSK = (1<<5); // Triggen on PORTB.5.
	GIFR = (1<<5)|(1<<6);
	GIMSK = (1<<5);
	
	// Initialize Timer.
	// Enable timer compare interrupt.
	TIMSK = (1<<OCIE0A);
	
	// Sets the compare value.
	OCR0A = 200;
	
	// Set Clear on Timer Compare (CTC) mode, No prescaler.
	TCCR0A = (1<<WGM01)|(0<<WGM00);	// CTC mode.
	TCCR0B = (1<<CS00);				// No prescaler.
	
	init_seq();
	
    while(1)
		rgb_program();
	
	return 0;
}


// For the RGB PWM and R,G,B values inc/dec.
SIGNAL(SIG_OUTPUT_COMPARE0A)
{
	rgb_counter();   // The counter for a soft color change.
	rgb_pwm();		 // Run the Software RGB PWM.
}


// For the "Change Program" button.
SIGNAL(SIG_PIN_CHANGE)
{   
	buttom_time = 0;
	if (PINB & (1<<5))
		buttom_time = 0xD0;   
}


// Then init sequence.
void init_seq(void)
{
	rgb_comp(255,0,0);
	delay_x();

	rgb_comp(0,255,0);
	delay_x();
		
	rgb_comp(0,0,255);
	delay_x();
}


// The program that changes color after the program number.
void rgb_program(void)
{
	const unsigned char RGB_Data[][3] =
	{
		{255,255,255}, {128,128,128},
		{255,0,0}, {128,0,0},
		{255,255,0}, {128,128,0},
		{0,255,0}, {0,128,0},
		{0,255,255}, {0,128,128},
		{0,0,255}, {0,0,128},
		{255,0,255}, {128,0,128},
		{255,156,230}, {128,78,115}
	};
	uint8_t r1, r2, r3;
	
	if (program_nr < 16)
		rgb_comp(RGB_Data[program_nr][0],RGB_Data[program_nr][1],RGB_Data[program_nr][2]);
	
	switch (program_nr)
	{
		case 16:
		{
			rgb_comp(255,0,0);
			delay_x();
				
			rgb_comp(255,255,0);
			delay_x();
				
			rgb_comp(0,255,0);
			delay_x();
				
			rgb_comp(0,255,255);
			delay_x();
				
			rgb_comp(0,0,255);
			delay_x();
				
			rgb_comp(255,0,255);
			delay_x();
				
			break;
		}
		
		case 17 :
		{
			rgb_comp(128,0,0);
			delay_x();
				
			rgb_comp(128,128,0);
			delay_x();
				
			rgb_comp(0,128,0);
			delay_x();
				
			rgb_comp(0,128,128);
			delay_x();
				
			rgb_comp(0,0,128);
			delay_x();
				
			rgb_comp(128,0,128);
			delay_x();
				
			break;
		}
		
		case 18 :
		{
			r1 = ((unsigned int) rand() % 128) + 128;
			r2 = ((unsigned int) rand() % 128) + 128;
			r3 = ((unsigned int) rand() % 128) + 128;
			
			rgb_comp(r1,r2,r3);
			delay_x();
			
			break;
		}
		
		default: program_nr = 0;
	} 
}


// The color changer. It tells rgb_counter() what it should count to for a dynamic change. 
void rgb_comp(uint8_t r, uint8_t g, uint8_t b)
{
	rgb_compare[0] = r;
	rgb_compare[1] = g;
	rgb_compare[2] = b;
}


// Counts the RGB up and down with aprox 89steps/sec. So it takes aprox 3sec to go from 0-255.
// So it won't be a stepy convertion between colors.
void rgb_counter(void)
{
	if (counter > 450)
	{
		if (rgb_compare[0] < rgb[0])
			rgb[0]--;
		else if (rgb_compare[0] > rgb[0])
			rgb[0]++;
		
		if (rgb_compare[1] < rgb[1])
			rgb[1]--;
		else if (rgb_compare[1] > rgb[1])
			rgb[1]++;

		if (rgb_compare[2] < rgb[2])
			rgb[2]--;
		else if (rgb_compare[2] > rgb[2])
			rgb[2]++;

		counter = 0;
	}
	
	counter++;
}


// The Software PWM that controlls the Red, Green and Blue brightness and mix.
// Works at aprox. 156Hz.
void rgb_pwm(void)
{
	if (pwm_counter == 0)
	{
		if (rgb[0] != 0)
			PORTB |= 0x01;
	
		if (rgb[1] != 0)
			PORTB |= 0x02;
	
		if (rgb[2] != 0)
			PORTB |= 0x04;
			
		delay_time++;

		if (buttom_time & 0x80)
			buttom_time++;
		if (buttom_time == 0xFF)
			program_nr++; 
	}

    if (pwm_counter > rgb[0])
        PORTB &= ~0x01;
	
	if (pwm_counter > rgb[1])
        PORTB &= ~0x02;
	
	if (pwm_counter > rgb[2])
        PORTB &= ~0x04;

    pwm_counter++;
}

void delay_x(void)
{
	delay_time = 0;
	while (delay_time >= 450);
}
//Emil
Senast redigerad av Korken 25 september 2006, 01:38:27, redigerad totalt 4 gånger.
Användarvisningsbild
$tiff
Inlägg: 4941
Blev medlem: 31 maj 2003, 19:47:52
Ort: Göteborg
Kontakt:

Inlägg av $tiff »

Du har ett fel i void rgb_pwm(void). Du måste ju deklarera din räknare (pwm_counter) som global variabel (dessutom volatile) för att det ska göra någon skillnad att dess värde ska sparas till nästa gång rutinen exekveras.

Sedan kör du en väldigt tajt timing. 300 Hz behöver du inte riktigt. Du kan få problem med att interruptrutinen är för lång och inte hinner slutföras innan nästa avbrott kommer, om du inte redan har det :)
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Inlägg av Korken »

Ok!

Har fixat pwm_counter och counter till globala.
OCR0A är nu 200 istellet för 100 så den har dubbelt så mycket med tid nu.

Ska lägga in det (måste löda dit alla kablarna igen) och ge en lägesraport.

//Emil
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Inlägg av Korken »

Fortfarande samma problem...
Den ligger på minsta styrkan och fiser.

Några fler förslag?
Den tycks inte räkna i PWMen utan ligger på 0an helatiden.

//Emil
Användarvisningsbild
$tiff
Inlägg: 4941
Blev medlem: 31 maj 2003, 19:47:52
Ort: Göteborg
Kontakt:

Inlägg av $tiff »

Har du definierat din räknare utanför alla funktioner så här?
volatile uint8_t pwm_counter = 0;
:?:
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Inlägg av Korken »

volatile uint8_t pwm_counter = 0;
Japp.

//Emil
Användarvisningsbild
Icecap
Inlägg: 26737
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Kod: Markera allt

void rgb_program(void)
  {
const unsigned char RGB_Data[][3] = {
  {255,255,255},{128,128,128},{255,  0,  0},{128,  0,  0},
  {255,255,  0},{128,128,0},{  0,255,  0},{  0,128,  0},
  {  0,255,255},{  0,128,128},{  0,  0,255},{  0,  0,128},
  {255,  0,255},{128,  0,128},{255,156,230},{128,78,115}};
  uint8_t r1, r2, r3;
  if(program_nr < 16) rgb_comp(RGB_Data[program_nr][0],RGB_Data[program_nr][1],RGB_Data[program_nr][2]);
  switch(program_nr)
     {
      case 16:
        {
        rgb_comp(255,0,0);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        rgb_comp(255,255,0);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        rgb_comp(0,255,0);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        rgb_comp(0,255,255);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        rgb_comp(0,0,255);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        rgb_comp(255,0,255);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        break;
        }
      case 17 :
        {
        rgb_comp(128,0,0);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        rgb_comp(128,128,0);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        rgb_comp(0,128,0);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        rgb_comp(0,128,128);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        rgb_comp(0,0,128);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        rgb_comp(128,0,128);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        break;
        }
      case 18 :
        {
        r1 = ((unsigned int) rand() % 128) + 128;
        r2 = ((unsigned int) rand() % 128) + 128;
        r3 = ((unsigned int) rand() % 128) + 128;
        rgb_comp(r1,r2,r3);
        for(int i = 0; i < 100; i++) _delay_ms(30);
        break;
        }
      default: program_nr = 0;
     }
  }
Jag har upplevd viss kompilatorer som får hicka av att man deklarerar variabler mitt i koden, avsätt heller de lokala variabler precis vid rutinens start, det kan ge bra resultat.
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Inlägg av Korken »

Ok!
Ska testa det. Men de konstiga är att den inte räknar i PWMen.

Ser någon ett fel med den?


//Emil
Kaggen
Inlägg: 432
Blev medlem: 29 januari 2005, 03:06:02

Inlägg av Kaggen »

Vet inte vad du menar med "inte räknar i PWM:en" men du nollställer ju variabeln pwm_counter det första du gör i funktionen rgb_pwm(). Då kan den väl inte bli annat än noll, eller?
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Inlägg av Korken »

Jag har ändra det i koden.
Ska uppdatera koden i första inlägget efter de som har sagts här.

//Emil
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Inlägg av exile »

Varför det inte fungerar beror på några fel läsningar i databladet....

Dom här raden enablar externt interupt på INT0 delvis PB2
GIMSK = (1<<6);
MCUCR = (0<<ISC01)|(1<<ISC00); // Trigger interupt on Rising Edge.

Vad som inträffar när du kör programet är att du ändra på PB2 via programet genom rutinen rgb_pwm(), detta generarar ett interupt nämligen SIG_INTERRUPT0, men efter som du inte tar hand om det interuptet så behandlas det som ett otillåtet interupt vilket generarar ett reset.. vilket får programet att "loop" runt i början...

Pin change interrupt, är vad det låter som, om pinen eller pinnarna bytter läge (ex 0 till 1, eller 1 till 0) så generas ett interrupt... således måste du behandla "Rising Edge" själv... men du har även ett problem till som du kanse inte vet om, nämligen kontakt studs vid en mekanisk brytare... (som en knapp) detta ställer till bekymmer efter som programet uppfatar flera knapp tryckningar... dock går det att lösa med "smart" program vara.

Där du använder cli(); är helt onödigt efter som vid en reset stängs all intrupt av...(tanken är good, men det behövs inte)

Sedan har vi

Kod: Markera allt

   uint8_t out_value = eeprom_read_byte((unsigned char *) addr); // Read from EEPROM.   
   // If i already have a value on that location in EEPROM then increase it and write it to EEPROM.
   if (out_value)
   {
      out_value++;
      eeprom_write_byte((unsigned char *) addr, out_value);
   }
   
   // Else write start value.
   else
   {
      out_value = 0;
      eeprom_write_byte((unsigned char *) addr, out_value);
   }
Så om eepromet är 0 på address 1 så skriver vi till baka 0 till eepromet, vilket resulterar i att upprepar samma sak nästa gång...
så skippa else och if satserna...

Sedan har vi _delay_ms(), vilket är en ganska kass funktion, vist fungerar den om man INTE har interrupt, varför? jo för att den loop som kör runt ett antal gånger, så vid interrupt stanar den av, vilket gör att man inte vet hur lång tid den tar...

Så jag hoppas att du blev lite klokare ^^
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Inlägg av exile »

Icecap skrev:

Kod: Markera allt

void rgb_program(void)
  {
*en massa code*
        r1 = ((unsigned int) rand() % 128) + 128;
*en massa code*
  }
Jag har upplevd viss kompilatorer som får hicka av att man deklarerar variabler mitt i koden, avsätt heller de lokala variabler precis vid rutinens start, det kan ge bra resultat.
Jag för stog nog inte riktigt vad du menar... Om du menar (unsigned int) så är det inte en deklaration av variabel utan en type casting.
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Inlägg av Korken »

Ok!
Mycket jag inte visste. :)

Så,

Kod: Markera allt

	GIMSK = (1<<5)|(1<<6);
	MCUCR = (1<<ISC01)|(1<<ISC00); // Trigger interupt on Rising Edge.
Ska ändras till:

Kod: Markera allt

	GIMSK = (1<<6);
	MCUCR = (1<<ISC01)|(1<<ISC00); // Trigger interupt on Rising Edge.
Och så in med en rutin som kollar så den inte matar på program knappen så det kanske måste vara 200ms mellan varje tryck, kanske göra en liten funktion som räknar ticks från timern eller nått.

Sen för att slippa reseten hur ska man göra det?
Ska jag lägga till:

Kod: Markera allt

SIGNAL(SIG_INTERRUPT0) 
{
}
Och ha den tom så att den inte gör något då?


If och else på EEPROM ska bort.

Men jag är lite osäker hur jag ska göra för att byta ut delay-funktionerna.
Om någon har en bra idé så säg gärna till!

//Emil
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Inlägg av exile »

He... jag kanse var otydlig...

Du kan slipa reseten genom att använda GIMSK = (1<<5); istället för
GIMSK = (1<<5)|(1<<6);
MCUCR = (1<<ISC01)|(1<<ISC00); // Trigger interupt on Rising Edge.

^^

Delayen kan lösas på lite olika sätt...

Ett exemple

Kod: Markera allt

volatile uint8_t delay_time;

void delay_x(uint8_t tid){
	uint8_t delay_time_temp = delay_time + tid;
	while( delay_time != delay_time_temp);
}

void rgb_pwm(void)
{
	static uint8_t pwm_counter = 0;

	if (pwm_counter == 0){
		PORTB |= 0x07;
		delay_time++;//ökar
	}

	*massa code*
}

delay_x(); istället för _delay_ms()
liknade kan användas för avstutsnig rutin för knappen

exempel

Kod: Markera allt

volatile uint8_t buttom_time;

SIGNAL(SIG_PIN_CHANGE)
{	
	buttom_time = 0;
	if (PINB & (1<<5))
		buttom_time = 0x80;	
}

void rgb_pwm(void)
{
    static uint8_t pwm_counter = 0;

    if (pwm_counter == 0){
        PORTB |= 0x07;

		if (buttom_time & 0x80)
			buttom_time++;
		if (buttom_time == 0xff)
			program_nr++;
		delay_time++;
	}
osv...
Hoppas att jag skrev lite tydligar den här gången ^^
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Inlägg av Korken »

Ahh! Mycket bättre. :D
Man tackar ödmjukast för hjälpen!

Men jag har en liten fråga om din avstutsnigs rutin.
Om man trycker innan den har räknat klart så kommer den bli 0x80 och räkna där ifrån tills den kommit till 0xFF igen.

Eller har jag tänkt helt åt skogen nu?

//Emil

Edit: Har ändrat koden i första medelandet igen.
Skriv svar