Bääääär
31.07.2009, 03:48
Hallo Community!
Ich hab mal wieder ein Problem:
Ich habe hier einen ATMEGA32 und habe eine PWM-Software geschrieben. Da ich 6 (Später mal 7) Kanäle brauche, entfällt Hardware PWM also...
Folgender Code geht wunderbar:
#define F_CPU 16000000UL
volatile uint16_t TimerCounter;
volatile int LEDState[6][2];
uint16_t pwmtable[64] PROGMEM = {0, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5,
5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 15, 17,
19, 21, 23, 26, 29, 32, 36, 40, 44, 49, 55,
61, 68, 76, 85, 94, 105, 117, 131, 146, 162,
181, 202, 225, 250, 279, 311, 346, 386, 430,
479, 534, 595, 663, 739, 824, 918, 1023};
// PWM-timer ISR
ISR(TIMER2_OVF_vect) {
TimerCounter++;
if (TimerCounter > 1023 ) {
TimerCounter = 0;
if (LEDState[0][0] != 0) { PREVR_Port |= (1 << PREVR_Pin);}
if (LEDState[1][0] != 0) { PREVG_Port |= (1 << PREVG_Pin);}
if (LEDState[2][0] != 0) { PREVB_Port |= (1 << PREVB_Pin);}
if (LEDState[3][0] != 0) { REALR_Port |= (1 << REALR_Pin);}
if (LEDState[4][0] != 0) { REALG_Port |= (1 << REALG_Pin);}
if (LEDState[5][0] != 0) { REALB_Port |= (1 << REALB_Pin);}
}
if (TimerCounter > LEDState[0][0]) {
PREVR_Port &= ~(1 << PREVR_Pin);
}
if (TimerCounter > LEDState[1][0]) {
PREVG_Port &= ~(1 << PREVG_Pin);
}
if (TimerCounter > LEDState[2][0]) {
PREVB_Port &= ~(1 << PREVB_Pin);
}
if (TimerCounter > LEDState[3][0]) {
REALR_Port &= ~(1 << REALR_Pin);
}
if (TimerCounter > LEDState[4][0]) {
REALG_Port &= ~(1 << REALG_Pin);
}
if (TimerCounter > LEDState[5][0]) {
REALB_Port &= ~(1 << REALB_Pin);
}
}
void SetLED(int number, int i) {
LEDState[number][1] = i;
LEDState[number][0] = pgm_read_word(pwmtable + i);
}
int main(void) {
// Init global variables
TimerCounter = 0;
// Init LED-Pins for I/O use
PREVR_DDR |= (1 << PREVR_Pin);
PREVG_DDR |= (1 << PREVG_Pin);
PREVB_DDR |= (1 << PREVB_Pin);
REALR_DDR |= (1 << REALR_Pin);
REALG_DDR |= (1 << REALG_Pin);
REALB_DDR |= (1 << REALB_Pin);
// All Interrupts off
cli();
// Init the timer for Software-PWM
TCCR2 |= (1<<CS20);
TIMSK |= (1<<TOIE2);
sei();
// Init the LEDState flag-array
for (int i=0; i<8; i++) {
LEDState[i][0] = pgm_read_word(pwmtable + 63); // Here is the value from the pwmtable stored; this is the value used by pwm
LEDState[i][1] = 63; // This is the "seen" brightness of the LEDs, from 0 to 63. We start with full brightness
}
while(1) {
//Tolle Sachen machen... Faden... Farben durchschalten usw.
}
return 0;
}
(Der Umstand, dass ich einmal den Wert aus dem PWM-Table und einmal den "gesehenen" Wert speichere, hängt damit zusammen, dass ich damit noch Fade...)
Jetzt dachte ich, 10Bit ist cool, 16Bit ist besser. Also los:
uint16_t pwmtable_16[256] PROGMEM = {0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6,
6, 7, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11,
11, 12, 12, 13, 13, 14, 15, 15, 16, 17, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
29, 31, 32, 33, 35, 36, 38, 40, 41, 43, 45,
47, 49, 52, 54, 56, 59, 61, 64, 67, 70, 73,
76, 79, 83, 87, 91, 95, 99, 103, 108, 112,
117, 123, 128, 134, 140, 146, 152, 159, 166,
173, 181, 189, 197, 206, 215, 225, 235, 245,
256, 267, 279, 292, 304, 318, 332, 347, 362,
378, 395, 412, 431, 450, 470, 490, 512, 535,
558, 583, 609, 636, 664, 693, 724, 756, 790,
825, 861, 899, 939, 981, 1024, 1069, 1117,
1166, 1218, 1272, 1328, 1387, 1448, 1512,
1579, 1649, 1722, 1798, 1878, 1961, 2048,
2139, 2233, 2332, 2435, 2543, 2656, 2773,
2896, 3025, 3158, 3298, 3444, 3597, 3756,
3922, 4096, 4277, 4467, 4664, 4871, 5087,
5312, 5547, 5793, 6049, 6317, 6596, 6889,
7194, 7512, 7845, 8192, 8555, 8933, 9329,
9742, 10173, 10624, 11094, 11585, 12098,
12634, 13193, 13777, 14387, 15024, 15689,
16384, 17109, 17867, 18658, 19484, 20346,
21247, 22188, 23170, 24196, 25267, 26386,
27554, 28774, 30048, 31378, 32768, 34218,
35733, 37315, 38967, 40693, 42494, 44376,
46340, 48392, 50534, 52772, 55108, 57548,
60096, 62757, 65535};
// PWM-timer ISR
ISR(TIMER2_OVF_vect) {
TimerCounter++;
if (TimerCounter == 0) { // Beim Überlauf
if (LEDState[0][0] != 0) { PREVR_Port |= (1 << PREVR_Pin);}
if (LEDState[1][0] != 0) { PREVG_Port |= (1 << PREVG_Pin);}
if (LEDState[2][0] != 0) { PREVB_Port |= (1 << PREVB_Pin);}
if (LEDState[3][0] != 0) { REALR_Port |= (1 << REALR_Pin);}
if (LEDState[4][0] != 0) { REALG_Port |= (1 << REALG_Pin);}
if (LEDState[5][0] != 0) { REALB_Port |= (1 << REALB_Pin);}
}
if (TimerCounter > LEDState[0][0]) {
PREVR_Port &= ~(1 << PREVR_Pin);
}
if (TimerCounter > LEDState[1][0]) {
PREVG_Port &= ~(1 << PREVG_Pin);
}
if (TimerCounter > LEDState[2][0]) {
PREVB_Port &= ~(1 << PREVB_Pin);
}
if (TimerCounter > LEDState[3][0]) {
REALR_Port &= ~(1 << REALR_Pin);
}
if (TimerCounter > LEDState[4][0]) {
REALG_Port &= ~(1 << REALG_Pin);
}
if (TimerCounter > LEDState[5][0]) {
REALB_Port &= ~(1 << REALB_Pin);
}
}
void SetLED(int number, int i) {
LEDState[number][1] = i;
LEDState[number][0] = pgm_read_word(pwmtable_16 + i);
}
int main(void) {
TimerCounter = 0;
// Init LED-Pins for I/O use
PREVR_DDR |= (1 << PREVR_Pin);
PREVG_DDR |= (1 << PREVG_Pin);
PREVB_DDR |= (1 << PREVB_Pin);
REALR_DDR |= (1 << REALR_Pin);
REALG_DDR |= (1 << REALG_Pin);
REALB_DDR |= (1 << REALB_Pin);
// All Interrupts off
cli();
// Init the timer for Software-PWM
TCCR2 |= (1<<CS20);
TIMSK |= (1<<TOIE2);
sei();
// Init the LEDState flag-array
for (int i=0; i<6; i++) {
LEDState[i][0] = pgm_read_word(pwmtable_16 + 255); // Here is the value from the pwmtable be stored; this is the value used by pwm
LEDState[i][1] = 256; // This is the "seen" brightness of the LEDs, from 0 to 255. We start with full brightness
}
while(1) {
// Wieder ne Menge tolle Farben durchschalten
}
return 0;
}
Hmm, wenn der Timer bei einem Prescaler von Null zählt, dann habe ich
16MHz/256 = 62500 Interrupte pro Sekunde.
Wenn ich helles Gelb erzeugen will (255,255,100) geht mein Timer aber so quälend langsam... Gelb (Rot+Grün) ist wie erwartet durchweg an, aber Blau blinkt alle Sekunde kurz auf.
Eben, die 62500 Interrupte reichen eben nicht aus, um x-mal/s 16Bit hochzuzählen...
Was mach ich jetzt?
Also Timer in den Clear-Timer-On-Compare-Match-Modus versetzt. Ich schreibe in das OCR2 Register 20 rein und er zählt, bis er 20 erreicht hat und dann kommt der Interrupt. Also 800.000 Interrpupt pro Sekunde. Heißt aber auch, ich hab nur 20 Takte Zeit, um all die LEds zu Schalten und eigentlich soll neben dem PWM ja auch noch Rechenzeit dasein für andere Sachen...
Jedenfalls hab ich folgende Änderungen am Code gemacht:
ISR (TIMER2_COMP_vect) {
...
// Init the timer for Software-PWM
TCCR2 |= (1<<CS20) | (1<<WGM21);
TCCR2 &= ~(1<<CS21);
TCCR2 &= ~(1<<CS22);
TIMSK |= (1<<OCIE2);
OCR2 = 20;
...
Leider ist das Resultat alles andere als gut: Immernoch sehe ich meine Blaue LED blinken, wenn auch schon in etwas weniger als einer Sekunde.
Welche Möglichkeiten bleiben mir noch?
Danke Vielmals,
Bääääär
Ich hab mal wieder ein Problem:
Ich habe hier einen ATMEGA32 und habe eine PWM-Software geschrieben. Da ich 6 (Später mal 7) Kanäle brauche, entfällt Hardware PWM also...
Folgender Code geht wunderbar:
#define F_CPU 16000000UL
volatile uint16_t TimerCounter;
volatile int LEDState[6][2];
uint16_t pwmtable[64] PROGMEM = {0, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5,
5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 15, 17,
19, 21, 23, 26, 29, 32, 36, 40, 44, 49, 55,
61, 68, 76, 85, 94, 105, 117, 131, 146, 162,
181, 202, 225, 250, 279, 311, 346, 386, 430,
479, 534, 595, 663, 739, 824, 918, 1023};
// PWM-timer ISR
ISR(TIMER2_OVF_vect) {
TimerCounter++;
if (TimerCounter > 1023 ) {
TimerCounter = 0;
if (LEDState[0][0] != 0) { PREVR_Port |= (1 << PREVR_Pin);}
if (LEDState[1][0] != 0) { PREVG_Port |= (1 << PREVG_Pin);}
if (LEDState[2][0] != 0) { PREVB_Port |= (1 << PREVB_Pin);}
if (LEDState[3][0] != 0) { REALR_Port |= (1 << REALR_Pin);}
if (LEDState[4][0] != 0) { REALG_Port |= (1 << REALG_Pin);}
if (LEDState[5][0] != 0) { REALB_Port |= (1 << REALB_Pin);}
}
if (TimerCounter > LEDState[0][0]) {
PREVR_Port &= ~(1 << PREVR_Pin);
}
if (TimerCounter > LEDState[1][0]) {
PREVG_Port &= ~(1 << PREVG_Pin);
}
if (TimerCounter > LEDState[2][0]) {
PREVB_Port &= ~(1 << PREVB_Pin);
}
if (TimerCounter > LEDState[3][0]) {
REALR_Port &= ~(1 << REALR_Pin);
}
if (TimerCounter > LEDState[4][0]) {
REALG_Port &= ~(1 << REALG_Pin);
}
if (TimerCounter > LEDState[5][0]) {
REALB_Port &= ~(1 << REALB_Pin);
}
}
void SetLED(int number, int i) {
LEDState[number][1] = i;
LEDState[number][0] = pgm_read_word(pwmtable + i);
}
int main(void) {
// Init global variables
TimerCounter = 0;
// Init LED-Pins for I/O use
PREVR_DDR |= (1 << PREVR_Pin);
PREVG_DDR |= (1 << PREVG_Pin);
PREVB_DDR |= (1 << PREVB_Pin);
REALR_DDR |= (1 << REALR_Pin);
REALG_DDR |= (1 << REALG_Pin);
REALB_DDR |= (1 << REALB_Pin);
// All Interrupts off
cli();
// Init the timer for Software-PWM
TCCR2 |= (1<<CS20);
TIMSK |= (1<<TOIE2);
sei();
// Init the LEDState flag-array
for (int i=0; i<8; i++) {
LEDState[i][0] = pgm_read_word(pwmtable + 63); // Here is the value from the pwmtable stored; this is the value used by pwm
LEDState[i][1] = 63; // This is the "seen" brightness of the LEDs, from 0 to 63. We start with full brightness
}
while(1) {
//Tolle Sachen machen... Faden... Farben durchschalten usw.
}
return 0;
}
(Der Umstand, dass ich einmal den Wert aus dem PWM-Table und einmal den "gesehenen" Wert speichere, hängt damit zusammen, dass ich damit noch Fade...)
Jetzt dachte ich, 10Bit ist cool, 16Bit ist besser. Also los:
uint16_t pwmtable_16[256] PROGMEM = {0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6,
6, 7, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11,
11, 12, 12, 13, 13, 14, 15, 15, 16, 17, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
29, 31, 32, 33, 35, 36, 38, 40, 41, 43, 45,
47, 49, 52, 54, 56, 59, 61, 64, 67, 70, 73,
76, 79, 83, 87, 91, 95, 99, 103, 108, 112,
117, 123, 128, 134, 140, 146, 152, 159, 166,
173, 181, 189, 197, 206, 215, 225, 235, 245,
256, 267, 279, 292, 304, 318, 332, 347, 362,
378, 395, 412, 431, 450, 470, 490, 512, 535,
558, 583, 609, 636, 664, 693, 724, 756, 790,
825, 861, 899, 939, 981, 1024, 1069, 1117,
1166, 1218, 1272, 1328, 1387, 1448, 1512,
1579, 1649, 1722, 1798, 1878, 1961, 2048,
2139, 2233, 2332, 2435, 2543, 2656, 2773,
2896, 3025, 3158, 3298, 3444, 3597, 3756,
3922, 4096, 4277, 4467, 4664, 4871, 5087,
5312, 5547, 5793, 6049, 6317, 6596, 6889,
7194, 7512, 7845, 8192, 8555, 8933, 9329,
9742, 10173, 10624, 11094, 11585, 12098,
12634, 13193, 13777, 14387, 15024, 15689,
16384, 17109, 17867, 18658, 19484, 20346,
21247, 22188, 23170, 24196, 25267, 26386,
27554, 28774, 30048, 31378, 32768, 34218,
35733, 37315, 38967, 40693, 42494, 44376,
46340, 48392, 50534, 52772, 55108, 57548,
60096, 62757, 65535};
// PWM-timer ISR
ISR(TIMER2_OVF_vect) {
TimerCounter++;
if (TimerCounter == 0) { // Beim Überlauf
if (LEDState[0][0] != 0) { PREVR_Port |= (1 << PREVR_Pin);}
if (LEDState[1][0] != 0) { PREVG_Port |= (1 << PREVG_Pin);}
if (LEDState[2][0] != 0) { PREVB_Port |= (1 << PREVB_Pin);}
if (LEDState[3][0] != 0) { REALR_Port |= (1 << REALR_Pin);}
if (LEDState[4][0] != 0) { REALG_Port |= (1 << REALG_Pin);}
if (LEDState[5][0] != 0) { REALB_Port |= (1 << REALB_Pin);}
}
if (TimerCounter > LEDState[0][0]) {
PREVR_Port &= ~(1 << PREVR_Pin);
}
if (TimerCounter > LEDState[1][0]) {
PREVG_Port &= ~(1 << PREVG_Pin);
}
if (TimerCounter > LEDState[2][0]) {
PREVB_Port &= ~(1 << PREVB_Pin);
}
if (TimerCounter > LEDState[3][0]) {
REALR_Port &= ~(1 << REALR_Pin);
}
if (TimerCounter > LEDState[4][0]) {
REALG_Port &= ~(1 << REALG_Pin);
}
if (TimerCounter > LEDState[5][0]) {
REALB_Port &= ~(1 << REALB_Pin);
}
}
void SetLED(int number, int i) {
LEDState[number][1] = i;
LEDState[number][0] = pgm_read_word(pwmtable_16 + i);
}
int main(void) {
TimerCounter = 0;
// Init LED-Pins for I/O use
PREVR_DDR |= (1 << PREVR_Pin);
PREVG_DDR |= (1 << PREVG_Pin);
PREVB_DDR |= (1 << PREVB_Pin);
REALR_DDR |= (1 << REALR_Pin);
REALG_DDR |= (1 << REALG_Pin);
REALB_DDR |= (1 << REALB_Pin);
// All Interrupts off
cli();
// Init the timer for Software-PWM
TCCR2 |= (1<<CS20);
TIMSK |= (1<<TOIE2);
sei();
// Init the LEDState flag-array
for (int i=0; i<6; i++) {
LEDState[i][0] = pgm_read_word(pwmtable_16 + 255); // Here is the value from the pwmtable be stored; this is the value used by pwm
LEDState[i][1] = 256; // This is the "seen" brightness of the LEDs, from 0 to 255. We start with full brightness
}
while(1) {
// Wieder ne Menge tolle Farben durchschalten
}
return 0;
}
Hmm, wenn der Timer bei einem Prescaler von Null zählt, dann habe ich
16MHz/256 = 62500 Interrupte pro Sekunde.
Wenn ich helles Gelb erzeugen will (255,255,100) geht mein Timer aber so quälend langsam... Gelb (Rot+Grün) ist wie erwartet durchweg an, aber Blau blinkt alle Sekunde kurz auf.
Eben, die 62500 Interrupte reichen eben nicht aus, um x-mal/s 16Bit hochzuzählen...
Was mach ich jetzt?
Also Timer in den Clear-Timer-On-Compare-Match-Modus versetzt. Ich schreibe in das OCR2 Register 20 rein und er zählt, bis er 20 erreicht hat und dann kommt der Interrupt. Also 800.000 Interrpupt pro Sekunde. Heißt aber auch, ich hab nur 20 Takte Zeit, um all die LEds zu Schalten und eigentlich soll neben dem PWM ja auch noch Rechenzeit dasein für andere Sachen...
Jedenfalls hab ich folgende Änderungen am Code gemacht:
ISR (TIMER2_COMP_vect) {
...
// Init the timer for Software-PWM
TCCR2 |= (1<<CS20) | (1<<WGM21);
TCCR2 &= ~(1<<CS21);
TCCR2 &= ~(1<<CS22);
TIMSK |= (1<<OCIE2);
OCR2 = 20;
...
Leider ist das Resultat alles andere als gut: Immernoch sehe ich meine Blaue LED blinken, wenn auch schon in etwas weniger als einer Sekunde.
Welche Möglichkeiten bleiben mir noch?
Danke Vielmals,
Bääääär