Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien
Toon posts:

Softwarematige PWM met een ATmega32

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik heb hieronder een code geschreven voor software matige PWM. Het is de bedoeling dat er minstens 9 8-bits kanalen zijn, zodat je 3 rgb leds kunt aansturen en faden. De klokfrequentie is 8 MHz, met een 8-bits timer en een prescaler van 256 levert dit een frequentie van 122 Hz.

Klopt mijn aanpak op deze manier? Kunnen er dingen verbeterd worden? Ik wil graag met zo min mogelijk rekenkracht het maximale eruit halen.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#define DDR_LED DDRA
#define PORT_LED PORTA
#define PIN_LED PINA

#define LED0 PA0
#define LED1 PA1
#define LED2 PA2

#define led0On()  PORT_LED|=(1<<LED0)
#define led0Off() PORT_LED&=~(1<<LED0)
#define led1On()  PORT_LED|=(1<<LED1)
#define led1Off() PORT_LED&=~(1<<LED1)
#define led2On()  PORT_LED|=(1<<LED2)
#define led2Off() PORT_LED&=~(1<<LED2)

unsigned char pwmG, pwmB, pwmR=128; // 50% dutycycle

ISR (TIMER0_COMP_vect) {  // compare match interrupt

  led0Off();
}

ISR (TIMER0_OVF_vect) {  // overflow interrupt
  
  led0On();
}

int main() {

  DDR_LED=(1<<LED0);
  PORT_LED=(1<<LED0);

  TCCR0|=(1<<CS02);  // prescaling 256

  TIMSK|=(1<<TOIE0)  // enable overflow interrupt
       |(1<<OCIE0);  // enable compare match interrupt

  OCR0=pwmR;

  sei();

  while(1) {

  asm volatile("nop");
  }
}

[ Voor 3% gewijzigd door Verwijderd op 21-04-2009 22:20 ]


  • Sprite_tm
  • Registratie: September 2002
  • Laatst online: 29-10 06:07

Sprite_tm

Semi-Chinees

De code die je er neerzet zal prima werken, maar is natuurlijk niet op te scalen naar 9 kanalen: je hebt geen 9 OCRx-registers ;) De makkelijkste methode is iets als dit:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unsigned char r=128,g=128,b=128;
unsigned char timerval;

main {
  init_timer();
  init_int();
  while(1);
}

//laat deze afgaan op gewenste_freq*256.
int_van_timer() { 
  timerval++;
  if (timerval==0) {
    led1on();
    led2on();
    led3on();
  }
  if (timerval==r) led1off();
  if (timerval==g) led2off();
  if (timerval==b) led3off();
}


Edit: Ow, IIRC doet
unsigned char pwmG, pwmB, pwmR=128; // 50% dutycycle
niet helemaal wat je wilt: hij zal wel alle 3 de vars definieren maar alleen in pwmR 128 zetten; de rest is undefined of 0, welke van de 2 weet ik niet meer.

[ Voor 19% gewijzigd door Sprite_tm op 21-04-2009 22:48 ]

Relaxen und watchen das blinkenlichten. | Laatste project: Ikea Frekvens oog


Verwijderd

Topicstarter
Hoe kom jij aan die mooie kleurtjes in je code?

En met jouw methode zou ik toch constant moeten pollen?

En wat is IIRC? :P
Sprite_tm schreef op dinsdag 21 april 2009 @ 22:42:

unsigned char pwmG, pwmB, pwmR=128; // 50% dutycycle
niet helemaal wat je wilt: hij zal wel alle 3 de vars definieren maar alleen in pwmR 128 zetten; de rest is undefined of 0, welke van de 2 weet ik niet meer.
Dit had ik bewust gedaan. Ik stond me er echter niet bij stil dat ik meerdere OCR0 registers nodig hebt. Nu ben ik gebonden aan het aantal timers, oftewel pwm kanalen in dit geval. Terwijl ik juist softwarematig meer pwm kanalen wou.

[ Voor 68% gewijzigd door Verwijderd op 22-04-2009 07:59 ]


  • Sprite_tm
  • Registratie: September 2002
  • Laatst online: 29-10 06:07

Sprite_tm

Semi-Chinees

Verwijderd schreef op woensdag 22 april 2009 @ 07:57:
Hoe kom jij aan die mooie kleurtjes in je code?
[code=c] gebruiken ipv [code]. Ipv 'c' werken andere talen (php, java, ...) ook.
En met jouw methode zou ik toch constant moeten pollen?
Nope. Het idee is dat je die timer interrupt opzet op 256 keer sneller af te gaan dan de pwm-frequentie die je wilt, en de pwm-logica in de interrupt handler te zetten. Je code kan verder gewoon z'n ding doen.
En wat is IIRC? :P
If I Recall Correctly.

Relaxen und watchen das blinkenlichten. | Laatste project: Ikea Frekvens oog


Verwijderd

Topicstarter
Zou je je code iets meer kunnen toelichten. Ik snap hem namelijk niet helemaal.

Je laat de int_van_timer af gaan op de gewenste OCR0 waarde. Maar hoe kan je hiermee de frequentie besturen. Want ik neem aan dat een dutycycle = 1/f * 256 * prescaler. Dus de frequentie kan je alleen beinvloeden door de prescaler en de klokfrequentie.

  • LED-Maniak
  • Registratie: Oktober 2003
  • Laatst online: 00:58
Je veranderd de frequentie dmv OCR0 idd. Frequentie is vast, maar de duty cycle(= "felheid") veranderd.

50% van de tijd pin hoog = 50% licht(in geval van lineairiteit natuurlijk ;))
100% van de tijd pin hoog = 100% licht.

Mitsubishi externe temperatuur sensor (Home Assistant compatible): V&A - ClimaControl - Ook voor Panasonic & LG.


Verwijderd

Topicstarter
@Sprite_tm
Wat bedoel je precies met timerval?
Wanneer wordt timerval 0. Bedoel je hiermee de timer van de ATmega. In dit geval register TCNT0?

Verwijderd

als de timer afloopt wordt een routine gestart die iedere keer 1 bij een teller optelt max is 256
in een ander register staat de pwm waarde die aangeeft hoe lang een led moet branden, zoals 128 (50%).
in de interupt routine wordt vergeleken of led waarde lager is als de teller waarde, zo ja zet de led aan.
teller klapt om naar 0 en alles begint opnieuw.

[ Voor 7% gewijzigd door Verwijderd op 22-04-2009 23:13 ]


  • LED-Maniak
  • Registratie: Oktober 2003
  • Laatst online: 00:58
Verwijderd schreef op woensdag 22 april 2009 @ 22:36:
@Sprite_tm
Wat bedoel je precies met timerval?
Wanneer wordt timerval 0. Bedoel je hiermee de timer van de ATmega. In dit geval register TCNT0?
Timerval is gewoon "een" byte die bijhoudt hoe ver de timer is. Dit is nodig omdat je softwarematig tot 255 aan het tellen bent.

Als Timerval 128 is, dan weet je dat hij op 50% van zijn totale lengte zit(en dus 50% duty-cycle).

Mitsubishi externe temperatuur sensor (Home Assistant compatible): V&A - ClimaControl - Ook voor Panasonic & LG.


  • Sprite_tm
  • Registratie: September 2002
  • Laatst online: 29-10 06:07

Sprite_tm

Semi-Chinees

Waarbij er misschien nog even opgemerkt moet worden dat timerval een unsigned char is, oftewel een enkele byte. Als die 255 bereikt heeft en je telt er dan 1 bijop, zal de waarde overflowen naar 0. Timerval kan dus wel degelijk 0 worden.

Relaxen und watchen das blinkenlichten. | Laatste project: Ikea Frekvens oog


  • Adion
  • Registratie: Januari 2001
  • Laatst online: 17:51
Ik weet nog niet zeker of het echt voordelen heeft, maar ik ben zelf vandaag bezig geweest met een alternatieve PWM-methode. Ik heb nog niet getest of het ook vlot werkt met 9 kanalen, maar in principe is het wel iets zuiniger in de interrupts (bij sprite_tm's methode zijn er 256*frequentie * 9 kanalen vergelijkingen nodig)

De volledige uitleg kan je op het Arduino forum vinden, voorlopig heb ik het nog maar met 3 kanalen getest.

VirtualDJ 2024 - Fast Image Resizer - Instagram


Verwijderd

Topicstarter
Dit is de code van Sprite met 3 kanalen
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <avr/io.h>
#define F_CPU 8000000UL
#include <util/delay.h>
#include <avr/interrupt.h>

#define LED_DDR DDRA
#define LED_PORT PORTA
#define LED_PIN PINA

#define LED0 PA0
#define LED1 PA1
#define LED2 PA2

#define led0On()  LED_PORT|=(1<<LED0)
#define led0Off() LED_PORT&=~(1<<LED0)
#define led1On()  LED_PORT|=(1<<LED1)
#define led1Off() LED_PORT&=~(1<<LED1)
#define led2On()  LED_PORT|=(1<<LED2)
#define led2Off() LED_PORT&=~(1<<LED2)

volatile unsigned char pwmR=127, pwmG=127, pwmB=0, frequency=127, counter=0;

ISR(TIMER0_COMP_vect) {
  
  counter++;
  if(counter==0) {
    led0On();
    led1On();
    led2On();
  }

  if(counter==pwmR)
  led0Off();

  if(counter==pwmG)
  led1Off();

  if(counter==pwmB)
  led2Off();
}

int main() {

  LED_DDR=(1<<LED0)|(1<<LED1)|(1<<LED2);

  TIMSK|=(1<<OCIE0);  // enable compare match interrupt

  OCR0=frequency; // frequency = OCR0 * counter * 1/F_CPU
                  // the lower value frequency, the higher led frequency

  sei();

  TCCR0|=(1<<WGM01) // CTC mode
       |(1<<CS00);  // no prescaling

  while(1) {

    //asm volatile("nop");
  }
}


Deze werkt gewoon goed. Alleen vreet hij wel resources bij veel kanalen.

Eventueel is deze dus ook interessant:
http://www.batsocks.co.uk/readme/art_bcm_1.htm

Ik probeer eigenlijk het beste resultaat te krijgen voor de volgende situaties:
1) Met adc bestuurbare PWM met xx kanalen met alle resources tot zijn beschikking
2) Met adc bestuurbare PWM met xx kanalen met weinig resources tot zijn beschikking

[ Voor 15% gewijzigd door Verwijderd op 23-04-2009 22:02 ]


  • LED-Maniak
  • Registratie: Oktober 2003
  • Laatst online: 00:58
Ach, ik heb dit met 16 kanalen 100Hz, SPI data uitlezing, Signaal decoding en USB gemaakt op een AVR dus als je goed programmeert valt het best mee met de resources :P


Atmel heeft overigens erg mooie application notes betreft softPWM dus lees dat eens door :)

[ Voor 36% gewijzigd door LED-Maniak op 23-04-2009 23:58 ]

Mitsubishi externe temperatuur sensor (Home Assistant compatible): V&A - ClimaControl - Ook voor Panasonic & LG.


  • dbeijer85
  • Registratie: Januari 2007
  • Laatst online: 18-06 16:22

dbeijer85

Super fast charger

Als je de PWM gebruikt voor ledjes is het nuttig om een extra test aan het begin van een cycle te zetten om te checken of het kanaal wel echt aan moet (je gewenste dutycycle >0%). Anders komen er met de bovenstaande code namelijk nog hele kleine pulsjes op je kanaal te staan, die bijvoorbeeld bij leds net te zien zijn. (dit heeft me ooit een uur debuggen gekost voordat ik doorhad wat er aan de hand was :))

Een andere optie die je hebt is de kanalen standaard uit te laten beginnen, en vervolgens op 1-dutycycle aan te laten gaan.

Assumption is the mother of all fuck-ups.

Pagina: 1