Hallo,
wollte in diesem Beitrag mal ein paar Möglichkeiten vom Atemga32U4 näher bringen, da er bei vielen Schaltungen bei denen er zum Einsatz kommen könnte vernachlässigt wird. Der Controller kann bequem über das kostenlose Tool Flip programmiert werden. Es wird kein ISP Programmer benötigt.
1. 16-Bit PWM
Über Timer/Counter1 können 3 PWM Ausgänge angesteuert werden. Im folgenden Beispiel wird die PWM mit 16-Bit konfiguriert. Durch die 16Mhz wird bei 16-Bit eine Frequenz von 122Hz erreicht. Als Taktgeber wird ein 16Mhz Quarz verwendet. Der Interne Clockdiv wird auf 0 gesetzt.
Berechnung der PWM Frequenz:
fpwm = Clock/(2xVorteilerxTimertopwert) --> 16 Mhz/(2x1x65535) = ca. 122 Hz
Über das Register ICR1 wird der Timertopwert vorgegeben.
#include <avr/io.h>
//Funktionen definieren
void timer1_init(void);
void switch_clock_rc_to_extern(void);
void jtag_deaktivieren(void);
void set_clockdiv(uint8_t div);
#define LED0 OCR1A
#define LED1 OCR1B
#define LED2 OCR1C
int main(void)
{
//Externer Quarz als Taktgeber verwenden
switch_clock_rc_to_extern();
//Clockdiv auf 0 stellen
set_clockdiv(0);
//JTAG deaktivieren
jtag_deaktivieren();
//PWM Ausgänge definieren
DDRB |= (1<<DDB7)|(1<<DDB6)|(1<<DDB5);
//Timer1 Initialisieren
timer1_init();
//50% PWM für LED0,LED1,LED2 einstellen
LED0 = 32768;
LED1 = 32768;
LED2 = 32768;
while(1)
{
}
}
void timer1_init(void){
//Modus und Vorteiler wählen (Phase Correct PWM und Vorteiler 1)
TCCR1A |= (1<<COM1A1)|(1<<COM1B1)|(1<<COM1C1)|(1<<WGM11);
TCCR1B |= (1<<WGM13)|(1<<CS10);
//Auflösung 16-Bit
ICR1 |= 0xFFFF;
}
//Wechsel zur externen Clock/Crystal
void switch_clock_rc_to_extern(void){
if(UDINT & (1<<WAKEUPI))
{
UDINT &= ~(1<<WAKEUPI);
CLKSEL0 |= (1<<EXTE);
while(!(CLKSTA & (1<<EXTON)));
CLKSEL0 |= (1<<CLKS);
PLLCSR |= (1<<PLLE);
CLKSEL0 &= ~(1<<RCE);
while(!(PLLCSR & (1<<PLOCK)));
USBCON &= ~(1<<FRZCLK);
}
}
//JTAG deaktivieren
void jtag_deaktivieren(void){
MCUCR |= (1<<JTD);
MCUCR |= (1<<JTD);
}
//div = 0x00 (div1),div = 0x01(div2),div = 0x02(div4),div = 0x03(div8);
//div = 0x04 (div16),div = 0x05(div32),div = 0x06(div64),div = 0x07(div128)
//div = 0x08 (div256)
void set_clockdiv(uint8_t div){
// Zunächst muss Clock Prescaler Change Enable gesetzt werden
CLKPR = 0x80;
//Anschließend wird 4x das Register CLKPR gelöscht und damit auf Clock Division Faktor 1 gestellt
CLKPR = div;
CLKPR = div;
CLKPR = div;
CLKPR = div;
}
Alles anzeigen
Das angehängte Oszibild zeigt die PWM gemessen am Ausgang PB7.
2. 12-Bit PWM
Über Timer/Counter1 können 3 PWM Ausgänge angesteuert werden. Über Timer/Counter3 wird 1 PWM Ausgang angesteuert. Im folgenden Beispiel wird die PWM mit 12-Bit konfiguriert. Durch die 16Mhz wird bei 12-Bit eine Frequenz von ca. 244Hz erreicht. Der Vorteiler vom Timer wird mit 8 konfiguriert. Als Taktgeber wird ein 16Mhz Quarz verwendet. Der Interne Clockdiv wird auf 0 gesetzt.
#include <avr/io.h>
//Funktionen definieren
void timer1_init(void);
void switch_clock_rc_to_extern(void);
void jtag_deaktivieren(void);
void set_clockdiv(uint8_t div);
void timer3_init(void);
#define LED0 OCR1A
#define LED1 OCR1B
#define LED2 OCR1C
#define LED3 OCR3A
int main(void)
{
//Externer Quarz als Taktgeber verwenden
switch_clock_rc_to_extern();
//Clockdiv auf 0 stellen
set_clockdiv(0);
//JTAG deaktivieren
jtag_deaktivieren();
//PWM Ausgänge definieren
DDRB |= (1<<DDB7)|(1<<DDB6)|(1<<DDB5);
DDRC |= (1<<DDC6);
//Timer1 Initialisieren
timer1_init();
timer3_init();
//50% PWM für LED0,LED1,LED2,LED3 einstellen
LED0 = 2048;
LED1 = 2048;
LED2 = 2048;
LED3 = 2048;
while(1)
{
}
}
void timer1_init(void){
//Modus und Vorteiler wählen (Phase Correct PWM und Vorteiler 8)
TCCR1A |= (1<<COM1A1)|(1<<COM1B1)|(1<<COM1C1)|(1<<WGM11);
TCCR1B |= (1<<WGM13)|(1<<CS11);
//Auflösung 12-Bit
ICR1 |= 0x0FFF;
}
void timer3_init(void){
//Modus und Vorteiler wählen (Phase Correct PWM und Vorteiler 8)
TCCR3A |= (1<<COM3A1)|(1<<WGM31);
TCCR3B |= (1<<WGM33)|(1<<CS31);
//Auflösung 12-Bit
ICR3 |= 0x0FFF;
}
//Wechsel zur externen Clock/Crystal
void switch_clock_rc_to_extern(void){
if(UDINT & (1<<WAKEUPI))
{
UDINT &= ~(1<<WAKEUPI);
CLKSEL0 |= (1<<EXTE);
while(!(CLKSTA & (1<<EXTON)));
CLKSEL0 |= (1<<CLKS);
PLLCSR |= (1<<PLLE);
CLKSEL0 &= ~(1<<RCE);
while(!(PLLCSR & (1<<PLOCK)));
USBCON &= ~(1<<FRZCLK);
}
}
//JTAG deaktivieren
void jtag_deaktivieren(void){
MCUCR |= (1<<JTD);
MCUCR |= (1<<JTD);
}
//div = 0x00 (div1),div = 0x01(div2),div = 0x02(div4),div = 0x03(div8);
//div = 0x04 (div16),div = 0x05(div32),div = 0x06(div64),div = 0x07(div128)
//div = 0x08 (div256)
void set_clockdiv(uint8_t div){
// Zunächst muss Clock Prescaler Change Enable gesetzt werden
CLKPR = 0x80;
//Anschließend wird 4x das Register CLKPR gelöscht und damit auf Clock Division Faktor 1 gestellt
CLKPR = div;
CLKPR = div;
CLKPR = div;
CLKPR = div;
}
Alles anzeigen
Stellvertretend für die 4 konfigurierten PWM Ausgänge, habe ich an PC6 gemessen.
3. 8-Bit PWM (Phasen und Frequenz korrekter PWM Modus)
In diesem Beispiel wird der Timer4 verwendet. Es handelt sich um einen 10-Bit High-Speed Timer.
Die Auflösung wird hier über das Register OCR4C festgelegt. Der Timer hat insgesamt 6 PWM Ausgänge, wobei diese immer paarweise anzusehen (Komplementär) sind.
#include <avr/io.h>
//Funktionen definieren
void timer4_init(void);
void switch_clock_rc_to_extern(void);
void jtag_deaktivieren(void);
void set_clockdiv(uint8_t div);
#define LED0 OCR4A
#define LED1 OCR4B
#define LED3 OCR4D
int main(void)
{
//Externer Quarz als Taktgeber verwenden
switch_clock_rc_to_extern();
//Clockdiv auf 0 stellen
set_clockdiv(0);
//JTAG deaktivieren
jtag_deaktivieren();
//PWM Ausgänge definieren
DDRB |= (1<<DDB6)|(1<<DDB5);
DDRC |= (1<<DDC6)|(1<<DDC7);
DDRD |= (1<<DDD6)|(1<<DDD7);
//Timer1 Initialisieren
timer4_init();
//50% PWM für LED0,LED1,LED2 einstellen
LED0 = 250;
LED1 = 250;
LED3 = 250;
while(1)
{
}
}
//Wechsel zur externen Clock/Crystal
void switch_clock_rc_to_extern(void){
if(UDINT & (1<<WAKEUPI))
{
UDINT &= ~(1<<WAKEUPI);
CLKSEL0 |= (1<<EXTE);
while(!(CLKSTA & (1<<EXTON)));
CLKSEL0 |= (1<<CLKS);
PLLCSR |= (1<<PLLE);
CLKSEL0 &= ~(1<<RCE);
while(!(PLLCSR & (1<<PLOCK)));
USBCON &= ~(1<<FRZCLK);
}
}
//JTAG deaktivieren
void jtag_deaktivieren(void){
MCUCR |= (1<<JTD);
MCUCR |= (1<<JTD);
}
//div = 0x00 (div1),div = 0x01(div2),div = 0x02(div4),div = 0x03(div8);
//div = 0x04 (div16),div = 0x05(div32),div = 0x06(div64),div = 0x07(div128)
//div = 0x08 (div256)
void set_clockdiv(uint8_t div){
// Zunächst muss Clock Prescaler Change Enable gesetzt werden
CLKPR = 0x80;
//Anschließend wird 4x das Register CLKPR gelöscht und damit auf Clock Division Faktor 1 gestellt
CLKPR = div;
CLKPR = div;
CLKPR = div;
CLKPR = div;
}
void timer4_init(void){
//Phasen und Frequenz korrekte PWM OCR4A,OCR4B und OCRA4,OCR4B negiert verbinden
//PWM4A und PWM4B freigeben
TCCR4A |= (1<<COM4A0)|(1<<COM4B0)|(1<<PWM4A)|(1<<PWM4B);
//Vorteiler 128
TCCR4B |= (1<<CS43);
//Phasen und Frequenz korrekte PWM OCR4D und OCR4D negiert verbinden, PWM4D freigeben
TCCR4C |= (1<<COM4D0)|(1<<PWM4D);
//Phasen und Frequenz korrekte PWM aktivieren
TCCR4D |= (1<<WGM40);
//PWM Auflösung auf 8-Bit einstellen
OCR4C |= 0xFF;
}
Alles anzeigen
Auf diesem Oszibild ist stellvertretend der Ausgang PB5 und PB6 enthalten.
Soweit so gut erstmal.
Gruß Flo