da meine Ausführungen zu diesem Controller auch in einem anderen Forum schon Anklang gefunden haben, möchte ich auch in diesem Forum das ganze posten.
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.
Code: Alles auswählen
#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;
}
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.
Code: Alles auswählen
#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;
}
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.
Code: Alles auswählen
#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;
}
Auf diesem Oszibild ist stellvertretend der Ausgang PB5 und PB6 enthalten.

4. IR Dimmer - Timer0 als PWM und Timer1 im CTC Modus
IR-Sensor: Shapr GP2Y0A21YK0F an PF0
PWM-Signal: PB7
Bei einer Entfernung von weniger als 20cm zum IR Sensor wird die Helligkeit erhöht, bei einer Entfernung von ca. 21cm bis 40cm wird die Helligkeit gesenkt. Es wird eine PWM Auflösung von 8-Bit verwendet. Die PWM Frequenz beträgt 122Hz.
Über den Timer1 im CTC Modus wird ein Sekundentakt generiert. Die IR_Sequenz legt fest, wie schnell hintereinander der IR Sensor eingelesen werden soll und damit wie schnell gedimmt werden soll.
Code: Alles auswählen
/*
* ir_sensor_dimmung.c
*
* Copyright by stromflo
*
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include "adc.h"
#include "clock.h"
void timer0_init(void);
void timer1_init(void);
void jtag_deaktivieren(void);
//Flags
volatile struct {
unsigned sec_flag:1; // 1 Bit für sec_flag
} bitfeld;
uint8_t adc_value[1];
uint8_t seccounter;
//IR Sequenz gibt an wie oft der IR Sensor abgefragt wird (Angabe in Sekunden)
uint8_t IR_sequenz = 2;
uint8_t pwm_array[11] = {255,200,150,100,50,25,10,8,5,3,1};
uint8_t pwm_index = 10;
int main(void)
{
// 16Mhz Quarz wählen
switch_clock_rc_to_extern();
set_clockdiv(0);
adc_init();
jtag_deaktivieren();
//PB7 als Ausgang definieren (PWM Out)
DDRB = (1 << DDB7);
timer0_init();
//PWM Ausgang mit Standardwert laden
OCR0A = pwm_array[pwm_index];
timer1_init();
sei();
seccounter = IR_sequenz;
while (1) {
//Wird jede Sekunde abgefragt
if(bitfeld.sec_flag == 1){
if(seccounter > 0){
seccounter--;
}else{
seccounter = IR_sequenz;
//ADC0 in Variable speichern
adc_value[0] = fasterread_ADC(0,3);
//Wenn der Messwert größer als125 ist
//und der pwm_index größer als 0
//Wird die Helligkeit erhöht
//Distanz kleiner 20cm zu IR Sensor
if(adc_value[0]>125 && pwm_index > 0 ){
pwm_index--;
OCR0A = pwm_array[pwm_index];
}
//Wenn der Messwert größer kleiner 125 ist
//und der pwm_index größer als < 10
//und der Messwert > 75 ist
//Wird die Helligkeit gesenkt
//Distanz größer 20cm zu IR Sensor und kleiner 40cm
if(adc_value[0]<=125 && pwm_index < 10 && adc_value[0] > 75){
pwm_index++;
OCR0A = pwm_array[pwm_index];
}
}
bitfeld.sec_flag = 0;
}
}
}
//Timer0 als PWM konfigurieren
void timer0_init(void){
//Phase-Correct PWM
TCCR0A |= (1<<COM0A1)|(1<<WGM00);
//Vorteiler 256
TCCR0B |= (1<<CS02);
//Interrupt ausschalten
TIMSK0&= ~((1<<OCIE0A));
}
//Timer1 im CTC Modus
void timer1_init(void){
//Vorteiler auf 1024 , CTC Modus
TCCR1B |= (1<<WGM12)|(1<<CS12);
//Compare Interrupt aktivieren
TIMSK1 |= (1<<OCIE1A);
//Vergleichswert auf 31250 festlegen
OCR1A = 31250;
}
void jtag_deaktivieren(void){
MCUCR |= (1<<JTD);
MCUCR |= (1<<JTD);
}
// Interrupt wird jede Sekunde ausgelöst
ISR(TIMER1_COMPA_vect)
{
bitfeld.sec_flag = 1;
}
Dieses Beispiel nimmt das Beispiel unter 3. als Grundlage. Ergänzt wird das ganze durch eine Fehlererkennung.
Beim PIn PD0 wird der interne Pullup Widerstand aktiviert.
Die Fehlererkennung wird so konfiguriert, dass eine fallende Flanke am Pin PDO (INT0) einen Interrupt auslöst.
Wird die Pegeländerung an INT0 erkannt, so werden die Compare Register gelöscht, die PWM wird gestoppt.
Zusätzlich kann wie im Beispiel gezeigt eine Interruptroutine ausgelöst werden. Im Beispiel schaltet diese den Ausgang PD1 ein.
Code: Alles auswählen
/*
*
* Created: 30.10.2011 21:58:53
* Author: stromflo
*/
#include <avr/io.h>
#include <avr/interrupt.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)|(1<<DDD1);
PORTD |= (1<<PD0);
//Timer1 Initialisieren
timer4_init();
sei();
//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 OC4A,OC4B und OCA4,OC4B 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 OC4D und OC4D negiert verbinden, PWM4D freigeben
TCCR4C |= (1<<COM4D0)|(1<<PWM4D);
//Phasen und Frequenz korrekte PWM aktivieren
TCCR4D |= (1<<WGM40) | (1<<FPIE4)|(1<<FPEN4)|(1<<FPES4);
//PWM Auflösung auf 8-Bit einstellen
OCR4C |= 0xFF;
}
ISR(TIMER4_FPF_vect)
{
PORTD|= (1<<PD1);
}



