Atemga32U4 - Schaltungen - Beispiele

Anleitungen für "hausgemachte" LED Projekte

Moderator: T.Hoffmann

Antworten
stromflo
Mega-User
Mega-User
Beiträge: 303
Registriert: Mi, 31.12.08, 12:16

Sa, 12.11.11, 20:47

Hallo zusammen,

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.
Bild

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.
Bild

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.
Bild

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;  
}
5. 8-Bit PWM (Phasen und Frequenz korrekter PWM Modus) und externer Fehlererkennung

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);
	 
}

Dateianhänge
ir_sensor_dimmung.zip
Enthält auch die include Files die zur Funktion notwendig sind!
(44.88 KiB) 198-mal heruntergeladen
stromflo
Mega-User
Mega-User
Beiträge: 303
Registriert: Mi, 31.12.08, 12:16

Mo, 14.11.11, 23:02

Guten Abend,

auch wenn die Rückmeldung nicht gerade hoch ist, möchte ich hier einen kleinen Temperaturschreiber veröffentlichen.
Das Herzstück vom Temperaturschreiber bietet dabei der Atmega32U4. Als Temperatursensoren eignen sich für den angehängten Quellcode LM35 Sensoren. Durch die gewählte Auflösung von 8-Bit und der Internen Referenz von 2,56V ergibt sich der kleinste messbare Spannungsanstieg von 10mV. Da jede 10mV gleichzeitig 1°C bedeuten, kann der ADC Wert direkt als Temperaturwert genommen werden. Es werden 12 Kanäle verarbeitet.

Der Quellcode lehnt sich an dieses Beispielvon Virtual Serial Port an. Hier können auch die Treiber runtergeladen werden, die für den Betrieb der Schnittstelle notwendig sind.

Die Werte können unter anderem mit einem Terminal mitgeschrieben werden. Zur Auswertung kann ich Realview empfehlen. Der Programmcode ist so aufgebaut, dass die Werte über dieses Tool ausgewertet werden können. Hierzu muss ein User Interface definiert werden. Die Baudrate kann auf den maximal möglichen Wert gestellt werden.

Bitte Bugs melden!

Gruß Flo
Dateianhänge
Temperaturschreiber_Atmega32U4.zip
(135.55 KiB) 190-mal heruntergeladen
Benutzeravatar
Volker K.
Site Admin
Site Admin
Beiträge: 746
Registriert: Mo, 14.08.06, 16:01

Mi, 16.11.11, 04:15

Hallo,


ich hoffe das ist jetzt kein unsachlicher Beitrag, ich kenne mich nämlich in Microcontrollerprogrammeriung nicht so sehr aus ^^:

Ist es nicht ziemlich ineffizient den Prozessor in der while(ADCSRA & (1<<ADSC)); Schleife bei voller Leistung aktiv warten zu lassen? Gerade bei einer AD-Wandlung wäre eine Abfrage über ein Timerinterrupt doch besser, oder?
Borax
Star-Admin
Star-Admin
Beiträge: 10783
Registriert: Mo, 10.09.07, 16:28

Mi, 16.11.11, 12:09

Ich glaube auch, das hier viele mitlesen!
Könntest Du zu diesem Punkt noch ein paar Erklärungen geben:
Der Controller kann bequem über das kostenlose Tool Flip programmiert werden. Es wird kein ISP Programmer benötigt.
Dann muss sich nicht jeder erst da durchsuchen. Und vielleicht auch eine 'Bezugsadresse'. Weil bei Reichelt, Conrad & Co gibt es diesen Chip nicht.
stromflo
Mega-User
Mega-User
Beiträge: 303
Registriert: Mi, 31.12.08, 12:16

Mi, 16.11.11, 18:41

Code: Alles auswählen

while(ADCSRA & (1<<ADSC));
Der Controller wartet bis die Wandlung abgeschlossen ist. Sicher könnte man das warten einsparen. Das ist natürlich auch eleganter, aber hier denke ich nicht unbedingt entscheidend. Da ich hier ohne hin keine Performance Engpässe habe, hab ich darauf auch keinen großen Wert gelegt. Trotzdem danke für die Info!

Um die Frage von Borax noch zu beantworten:

Flip ist ein Tool, dass kostenlos von Atmel bereitgestellt wird.
Link zu Flip Downloadseite
Es gibt auch eine Linux Variante, habe ich aber noch nie verwendet, daher kann ich nicht mehr dazu sagen.
Die Windows Variante wird einfach installiert. Wenn der Controller über USB mit dem Rechner verbunden wird fragt Windows nach dem Treiber, dieser ist nach der Installation von Flip im Programmverzeichnis vorhanden und muss nur noch ausgewählt werden. Anschließend kann der Controller mit Flip über USB programmiert werden. Über das drücken der Taste HWB und Reset bringt man den Controller in den Programmiermodus.

Beschaffungsquelle ist in der Tat nicht ganz einfach. Es gibt den Controller nur als SMD Ausführung, was erst mal einige Bastler aufschreckt. Es gibt daher auch einige Adapterboards. Stellvertretend verlinke ich hiereinfach mal eins. Den Controller selber kann man z.B. hier beziehen. Aufgrund der USB Funktionalität ist der Controller natürlich schon etwas teurer als ähnliche Controller ohne USB.

Gruß Florian
stromflo
Mega-User
Mega-User
Beiträge: 303
Registriert: Mi, 31.12.08, 12:16

Sa, 19.11.11, 23:37

Ja Borax, da hast du nicht ganz unrecht ich bevorzuge auch deutsche Beschaffungsquellen.
Stellvertretend hier mal ein Link.

Gruß Florian
Antworten