Archiv verlassen und diese Seite im Standarddesign anzeigen : Zwei getrennt regelbare PWM-Signale an zwei Pins von PortC
Hallo, ich möchte am PortC zwei Spulen mit Eisenkern, also zwei Elektromagneten, über eine externe Schaltung anschließen und getrennt voneinander steuern. Hierzu möchte ich an zwei Pins von PortC jeweils zwei unterschiedliche analoge Steuerspannungen über zwei PWM-Signale erzeugen.
Diese beiden Spannungen (also die PWM-Signale) möchte ich später über das UART vom PC aus steuern können.
Ich benutze einen Atmel Atmega32.
Nun meine Frage.: Muss ich zur Erzeugung der beiden PWM-Signale jeweils die beiden 8-bit Timer/Counter (8-bit Timer/Counter0, 8-bit Timer/Counter2) benutzen oder ist das nicht nötig? Reicht es aus den 16-bit Timer/Counter1 zu verwenden und über die beiden Output Compare Register 1A und 1B (OCR1A, OCR1b) zwei getrennte Signale zu erzeugen.
Hierzu habe ich folgenden Ansatz und weiss nun aber nicht, ob das so funktionieren kann.:
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
static void enable_pwm(void)
{
TCCR1A = 0x00; //Timer auf 0
TCCR1B = 0x00;
TCCR1A |= (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0) | (1<<WGM10); //Timer einstellen
TCCR1B |= (1<<CS10) | (1<<CS12);
}
int main(void)
{
uint16_t var_a, var_b;
DDRC = (1 << DDC4) | (1 << DDC5);
PORTC = 0x00;
enable_pwm();
while (1)
{
OCR1BL = var_b; // Kanal 1 verschiedene Werte
OCR1AL = var_a; // Kanal 2 verschiedene Werte
}
}
Es wäre nett, wenn mir jemand weiterhelfen könnte und gegebenenfalls meinen Quelltext analysiert und korrigiert. Danke.
SprinterSB
08.02.2006, 08:37
Die PWM-Ports sind fest verdrahtet, so etwas wie nen Output-Multiplexer kennt AVR nicht. Also entweder die Pins verweden, die ATmega32 vorsieht (OC2=B3, OC1A=D5, OC1B=D4, OC2=D7) oder selber Hand anlegen und ne Soft-PWM machen und an den Pins wackeln.
Der 16bit TC reicht, da Du hier zwei PWM Pins zur Verfügung hast.
Drei Sachen fallen mir auf die Schnelle auf:
Du steuerst OCR1B anders an als OCR1A (siehe Settings von COM1Ax und COM1Bx in TCCR1A).
Du setzt nur die low-register für die PWM. Damit bekommst Du nur ein sehr kleines Tastverhältnis hin (da Du die 16bit PWM verwendest).
var_a und var_b haben keinen definierten Wert.
Danke erst einmal für Eure schnelle Hilfe. Wenn ich das richtig verstehe, dann kann ich mit dem 16bit TC zwei unterschiedliche PWM Signale erzeugen, die ich dann an den Ports PD4 (OC1B) und PD5 (OC1A) von PortD abgreifen kann.
Ich möchte, wie bereits erwähnt, die beiden PWM-Signale über das UART vom PC aus steuern können. Da die Pins PD0 (RXD) und PD1 (TXD) von PortD ja schon für das UART reserviert sind und die beiden PWM-Signale ja auch über PortD ausgegeben werden sollen, hatte ich immer Porbleme mit der richtigen Initialisierung von PortD. Entweder ging die PWM oder das UART nicht. Vielleicht habe ich mich da irgendwie zu blöd angestellt. Vielleicht könnt Ihr mir da weiter helfen. Ich brauche auf jeden Fall die beiden PWM-Signale und das Uart gleichzeitig. Wie muss ich denn die PWM auf PortD initialisieren damit das Uart nicht blockiert wird?
DDRD = (1 << PD4) | (1 << PD5);
PORTD = 0x00; // ist das richtig, oder muss ich auch Pin-weise unterscheiden?
Du steuerst OCR1B anders an als OCR1A (siehe Settings von COM1Ax und COM1Bx in TCCR1A)
Was genau meinst Du damit? Was müßte ich denn machen um die Register gleich anzusteuern? Die Belegung von TCCR1A und TCCR1B unterscheidet sich ja grundsätzlich oder reden wir gerade aneinander vorbei? :-)
Du setzt nur die low-register für die PWM. Damit bekommst Du nur ein sehr kleines Tastverhältnis hin (da Du die 16bit PWM verwendest).
Wie kann ich das denn ändern? Wie kann ich die High-Register setzen um das Tastverhältnis zu vergrößern?
Ich habe nun die beiden Variablen var_a und var_b mit 0 initialisiert.
var_a und var_b haben keinen definierten Wert.
Die Werte von var_a und var_b sollen später vom PC aus über das UART gesendet werden und in der while-Schleife jeweils var_a und var_b zugewiesen werden. Im Moment sind das Dummy-Variablen.
Für den PWM-Test möchte ich erstmal OCR1AL und OCR1BL feste Werte zuweisen und sehen das die beiden LEDS an den Ports PD4 und PD5 unterschiedlich hell leuchten.
Danke nochmals für Eure Hilfe.
SprinterSB
08.02.2006, 11:32
Die UART-Pins werden automatisch bei Verwendung des UART so eingestellt wie gebraucht, und sind nicht mehr für "normale" I/O verwendbar.
OCR1A und OCR1B sind 16-bit-Register, die auch so gesetzt werden können:
OCR1A = var_a;
Wie die PWM-Pins wackeln, ist abhängig vom PWM-Modus und den Einstellungen COM1Ax und COM1Bx im TCCR1A.
Dein PWM-Mode ist #1, also ist TOP=255 und sinnvolle Werte für OCR1B/B sind 0...255. Welchem Duty das entspricht, ist von COM1xy Konfiguration abhängig. In deinem Fall ist das Tastverhältnis OCR1A/255, also in 256 Schritten von 0% bis 100% einstellbar. Mit OCR1A = 0x80 (0der 0x7f?) bekommst du 50%.
Zu bedenken ist noch, daß zwar die Intensität einer LED linear mit dem Tastverhältnis geht, der Helligkeitseindruck jedoch nicht.
Port D4 und D4 werden als OUT geschaltet:
DDRD |= (1 << PD4);
DDRD |= (1 << PD5);
@hender, sorry hatte mich verlesen. Die beiden Pins werden gleich angesteuert. Alles andere hat Georg-Johann ja schon erwähnt.
Nochmals Danke für Eure Hilfe. Ich habe es nun geschafft zwei unterschiedliche PWM-Signale zu erzeugen. Im nächsten Schritt wollte ich nun versuchen die beiden PWM Signale (erst einmal zusammen) über das Uart vom PC aus zu steuern. Sobald ich aber die Funktion "uart_getc()" aufrufe und versuche den Wert, also das empfangene Zeichen, einer Variablen zuzuweisen "var=uart_getc()", dann tut sich gar nichts mehr. Mein im Folgenden abgebildetes Programm bietet ebenfalls die Möglichkeit die PWMs über zwei Taster, die am PortA angeschlossen sind, zu steuern. Über die Taster lässt sich der Wert der Variable "status" erhöhen und senken. Die Variable "status" wird danach OCR1AL und OCR1BL zugewiesen.
Sobald ich die Funktion "uart_getc()" aufrufe, dann geht auch die Tastensteuerung der PWM nicht mehr. Kommentiere ich die Zeile "var = uart_getc();" aus, dann funktioniert die Tastensteuerung wieder.
Vielleicht habt Ihr da ja ne Idee woran das liegt und wie ich das Problem lösen kann? Die Uart-Funktionen sind sehr einfach und funktionieren im Moment noch ohne Interupts.
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
// Delay Funktion
void delay_ms (uint16_t delay_time)
{
uint16_t k=0;
while (k++<delay_time) {}
}
// Initialisierung der PWM
static void enable_pwm(void)
{
TCCR1A = 0x00; //Timer auf 0
TCCR1B = 0x00;
// PWM: 8-bit, phasenkorrekt
TCCR1A |= (1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (1<<WGM10); //Timer einstellen
//TCCR1B |= (1<<CS10) | (1<<CS12);
//TCCR1B |= (1<<CS10) | (1<<CS11) | (1<<CS12);
TCCR1B |= (1<<CS10); //kein Prescaling
}
// Initialisierung UART
void uart_init (void)
{
/* USART-Init 38400 Baud bei 8MHz für Mega32 */
UCSRB = ( (1<<RXCIE) | (1<<RXEN) | (1<<TXEN) );
UCSRC = (_BV (URSEL) /*| _BV (USBS) */| _BV (UCSZ1) | _BV (UCSZ0)); //syncron usart modus UMSEL
UBRRH = 0; // Highbyte ist 0
UBRRL = 12; // Lowbyte ist 51 ( dezimal )
}
// Zeichen senden
int uart_putc(unsigned char c)
{
while (!(UCSRA & (1<<UDRE))); /* warten bis Senden moeglich */
UDR = c; /* sende Zeichen */
return 0;
}
// String senden
void uart_puts (char *s)
{
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
uart_putc(*s);
s++;
}
}
// Zeichen empfangen
uint8_t uart_getc (void)
{
while (!(UCSRA & (1<<RXC))); // warten bis Zeichen verfuegbar
return UDR; // Zeichen aus UDR an Aufrufer zurueckgeben
}
int main(void)
{
uint16_t status=0;
uint8_t var;
DDRA=0; //PORTA as Input
PORTA=255;
DDRD |= (1 << PD4);
DDRD |= (1 << PD5);
//uart_init();
enable_pwm();
/* Loop forever */
while (1)
{
// Steuerung der PWM-Kanäle über 2 Taster (Up/Down)
if (status>127)
{
status=128;
}
if (status<2)
{
status=1;
}
if (( PINA & (1<<PINA3))==0 )
{
delay_ms(50000);
status=status+1;
}
if (( PINA & (1<<PINA4))==0 )
{
delay_ms(50000);
status=status-1;
}
OCR1BL = status; // Kanal 1 verschiedene Werte
OCR1AL = status; // Kanal 2 verschiedene Werte
// Versuch: Wertzuweisung über das UART vom PC aus
var = uart_getc(); /// AN DIESER STELLE KOLLIDIERT ES MIT DER PWM
///status=var;
}
}
SprinterSB
08.02.2006, 15:49
Du hast den Receive-Interrupt aktiviert. Mich dünkt du willst via Poll empfangen (warten) , und machst es auch. Dich haut's aus der Kurve, weil für die Receive-IRQ keine ISR implementiert ist.
Nimm mal das RXCIE aus UCSRB raus.
Danke erst einmal. Ich habe das RXCIE jetzt mal rausgenommen, aber ich kann die PWM über die Taster immer noch nicht steuern. Ich habe nun zusätzlich im Quelltext die Zeile "uart_putc(var);" gleich nach "var = uart_getc();" eingefügt. Das empfangene Zeichen wird sofort zurück gesendet. Das klappt mit Hyperterminal auch. Die PWM lässt sich aber über die beiden Taster komischerweise immer noch nicht steuern.
Die ISR für den Receive-IRQ "SIGNAL(SIG_UART_RECV){...}" will ich später mal noch mit einbauen. Jetzt soll erstmal die einfache Variante funktionieren.
Mein Quelltext sieht jetzt so aus.:
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
// Delay Funktion
void delay_ms (uint16_t delay_time)
{
uint16_t k=0;
while (k++<delay_time) {}
}
// Initialisierung der PWM
static void enable_pwm(void)
{
TCCR1A = 0x00; //Timer auf 0
TCCR1B = 0x00;
// PWM: 8-bit, phasenkorrekt
TCCR1A |= (1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (1<<WGM10); //Timer einstellen
//TCCR1B |= (1<<CS10) | (1<<CS12);
//TCCR1B |= (1<<CS10) | (1<<CS11) | (1<<CS12);
TCCR1B |= (1<<CS10); //kein Prescaling
}
// Initialisierung UART
void uart_init (void)
{
/* USART-Init 38400 Baud bei 8MHz für Mega32 */
UCSRB = (1<<RXEN) | (1<<TXEN);
/* Einstellen des Datenformats: 8 Datenbits, 1 Stoppbit */
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
UBRRH = 0; // Highbyte ist 0
UBRRL = 12; // Lowbyte ist 51 ( dezimal )
}
// Zeichen senden
int uart_putc(unsigned char c)
{
while (!(UCSRA & (1<<UDRE))); /* warten bis Senden moeglich */
UDR = c; /* sende Zeichen */
return 0;
}
// String senden
void uart_puts (char *s)
{
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
uart_putc(*s);
s++;
}
}
// Zeichen empfangen
uint8_t uart_getc (void)
{
while (!(UCSRA & (1<<RXC))); // warten bis Zeichen verfuegbar
return UDR; // Zeichen aus UDR an Aufrufer zurueckgeben
}
int main(void)
{
uint16_t status=0;
uint8_t var;
DDRA=0; //PORTA as Input
PORTA=255;
DDRD |= (1 << PD4);
DDRD |= (1 << PD5);
uart_init();
enable_pwm();
/* Loop forever */
while (1)
{
// Steuerung der PWM-Kanäle über 2 Taster (Up/Down)
if (status>127)
{
status=128;
}
if (status<2)
{
status=1;
}
if (( PINA & (1<<PINA3))==0 )
{
delay_ms(50000);
status=status+1;
}
if (( PINA & (1<<PINA4))==0 )
{
delay_ms(50000);
status=status-1;
}
OCR1BL = status; // Kanal 1 verschiedene Werte
OCR1AL = status; // Kanal 2 verschiedene Werte
// Versuch: Wertzuweisung über das UART vom PC aus
var = uart_getc(); /// AN DIESER STELLE KOLLIDIERT ES MIT DER PWM
uart_putc(var);
//status=var;
}
}
In Deiner routine kannst Du die Tasten genau in dem Moment erkennen, wenn gerade das Zeichen gesendet wurde, und noch nicht wieder auf ein Zeichen gewartet wird.
Wenn Du also mal testweise die serielle Kommunikation in main() auskommentierst, wird es schon besser funktionieren. Zum Testen würde ich auch um mehr als 1 in-/dekrementieren, ansonsten musst Du zu oft den Taster drücken.
Da Du Dich wahrscheinlich fragst, wie man beides - serielle Kommunikation und Taster - vernünftig zum Laufen bekommt: Mindestens eine dieser beiden Funktionen sollten über eine Interrupt-Routine gelöst werden.
SprinterSB
08.02.2006, 16:59
Oder du wartest nicht beim Empfang:
// Zeichen empfangen
int uart_getc_nowait (void)
{
if (!(UCSRA & (1<<RXC))) // warten bis Zeichen verfuegbar
return -1
return (int) UDR; // Zeichen aus UDR an Aufrufer zurueckgeben
}
Du reagierst dann nur auf die Funktion, wenn sie nicht -1 liefert (ist was anderes als 0xff, daher gibt sie int zurück).
Mit der Interrupt-Lösung musst du dir auch merken, ob das Zeichen schon ausgewertet wurde, und da reicht 1 Byte auch nicht mehr.
Hallo, danke für Eure Hinweise. Ich habe mein Programm jetzt nochmal etwas umdisponiert. Ich habe nun die Taster-Abfrage vollständig herausgenommen und versuche nun die PWM-Signale direkt durch Werte vom PC aus zu steuern. Der PC sendet Werte im Wertebereich von 0-255. Diese Werte sollen auf dem MC über "uart_getc()" der Variable "var" zugewiesen werden. Wenn ich jetzt versuche "OCR1BL = var;" zu setzen, dann funktioniert die Wertzuweisung irgendwie nicht, d.h. die LED ändert wird anfangs angeschaltet und ändert dann aber Ihre Helligkeit nicht mehr. Ich sende die Werte von "var" über "uart_putc(var);" gleich direkt an den PC zurück, um diese zu überprüfen. Sie scheinen soweit in Ordnung zu sein. Warum klappt das dann nicht?
Wenn ich vom PC aus ein "a" oder ein "b" sende und auf dem MC entsprecht die Variable Status incrementieren bzw. decrementiere und im Folgenden "OCR1AL = status;", dann funktioniert es. Die LED verändert Ihre Helligkeit.
Hier nochmal mein Programm:
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
// Delay Funktion
void delay_ms (uint16_t delay_time)
{
uint16_t k=0;
while (k++<delay_time) {}
}
// Initialisierung der PWM
static void enable_pwm(void)
{
TCCR1A = 0x00; //Timer auf 0
TCCR1B = 0x00;
// PWM: 8-bit, phasenkorrekt
TCCR1A |= (1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (1<<WGM10); //Timer einstellen
//TCCR1B |= (1<<CS10) | (1<<CS12);
//TCCR1B |= (1<<CS10) | (1<<CS11) | (1<<CS12);
TCCR1B |= (1<<CS10); //kein Prescaling
}
// Initialisierung UART
void uart_init (void)
{
/* USART-Init 38400 Baud bei 8MHz für Mega32 */
UCSRB = (1<<RXEN) | (1<<TXEN);
/* Einstellen des Datenformats: 8 Datenbits, 1 Stoppbit */
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
UBRRH = 0; // Highbyte ist 0
UBRRL = 12; // Lowbyte ist 51 ( dezimal )
}
// Zeichen senden
int uart_putc(unsigned char c)
{
while (!(UCSRA & (1<<UDRE))); /* warten bis Senden moeglich */
UDR = c; /* sende Zeichen */
return 0;
}
// String senden
void uart_puts (char *s)
{
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
uart_putc(*s);
s++;
}
}
// Zeichen empfangen
uint8_t uart_getc (void)
{
while (!(UCSRA & (1<<RXC))); // warten bis Zeichen verfuegbar
return UDR; // Zeichen aus UDR an Aufrufer zurueckgeben
}
// Zeichen empfangen
int uart_getc_nowait (void)
{
if (!(UCSRA & (1<<RXC))) // warten bis Zeichen verfuegbar
return -1;
return (int) UDR; // Zeichen aus UDR an Aufrufer zurueckgeben
}
int main(void)
{
uint16_t status=0;
uint8_t var;
DDRA=0; //PORTA as Input
PORTA=255;
DDRD |= (1 << PD4);
DDRD |= (1 << PD5);
uart_init();
enable_pwm();
/* Loop forever */
while (1)
{
var = uart_getc(); /// AN DIESER STELLE KOLLIDIERT ES MIT DER PWM
uart_putc(var);
//... wenn ich ein "a" sende
if (var=='a')
{
status=status+1;
}
//... wenn ich ein "b" sende
if (var=='b')
{
status=status-1;
}
// Ansonsten sende ich Werte von 0-255 (z.B. var=94)
if ((var!='a') & (var!='b'))
{
OCR1BL = var; // Kanal 1: direkte Wertzuweisung vom PC aus
}
OCR1AL = status; // Kanal 2: Wert von "status"
}
}
Muss ich die Werte erst in einen anderen Datentyp konvertieren? Wenn ja, wie genau muß ich "var" verändern das ich es direkt zuweisen kann? Vielleicht habt Ihr ja eine Idee?
SprinterSB
10.02.2006, 10:44
1. Die Warteschleife geht so nicht, siehe auch Warteschleife (https://www.roboternetz.de/wissen/index.php/Warteschleife).
2. Es muss heissen if (...&& ...), siehe auch Bitweise vs._Logische Operatoren (https://www.roboternetz.de/wissen/index.php/Avr-gcc#Bitweise_vs._Logische_Operatoren).
3. Konvertieren musst du je nachdem, was du schickst ;-). "255" ist etwas anderes als 255. Ersteres sind 3 oder 4 Byte (4, falls noch ein Endezeichen): 0x32, 0x35, 0x35, 0x00, letzteres ist 0xff. Welche Konvertierungs-Funktionen es gibt, findest du in deiner Doku zur avr-libc (WIN_AVR/doc/avr.libs oder so) oder im Net.
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.