RC Empfänger PWM Signal Motorregler Atmega8

Digit-22

Neues Mitglied
13. Juni 2013
3
0
0
41
Sprachen
Hej Leute

Ich bin neu hier und habe ein kleines Problem woran ich schon seit ne ewigkeit arbeite.


Mein Vorhaben: ich bin leidenschaftlicher Modellbauer (Modellflug) und baue ultra kleine leichte micro Modelle
wie z.b. Microflieger oder microcopter und benötige für die Micro Bürstenmotoren micro
Regler die sehr klein und leicht sind.
Da ich im Netzt kaum was gefunden habe und wenn dann fast unbezahlbar, hab ich mich dafür entschieden
meinen eigenen Regler zu bauen.

Meine Hardware/Software:

Atmega8 mit 4MHz Extern.
Oszilloskop
Multimeter...
Diverse Transistoren und Mosfets.
Micro Bürstenmotoren (max 1A), LEDs...
RC Empfänger / Servotester.

AtmelStudio6.
Extreme Burner AVR
Programmiersprache C++

Was ich erreichen möchte ist ein PWM Signal was aus einem RC Epfänger / Servotester (50Hz) kommt,
an einem Eingang vom Atmega8 anzuschließen und an einem Ausgang ein PWM Signal mit höherer Frequenz (KHz) zu erzeugen.
Mit diesen Signal möchte ich ein Mosfet ansteuern der wiederrum einen kleinen Bürstenmotor betreibt.
Die Drehzahl sollte natürlich über das PWM Signal gesteuert werden können.
Z.B. Gasknüppel 0= Motor Aus, Gasknüppel voll = Motor maximale Drehzahl und natürlich alles was da ziwchen liegt.
Im Prinziep einfach die Drehzal des Micro Bürstmotors Steuern.

Hier ist mein Code Ansatz womit ich zwar die Drehzahl steuern kann, aber absolut ungenau und primitiv.
Ich bekomme es nicht hin ein genaues PWM Signal zu erzeugen der über das PWM Signal am Eingang gesteuert wird.
Es gibt sicherlich eine elegantere Lösung. Bin schon am verzewifeln.

Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define F_CPU 4000000UL


volatile int flanke=0;
volatile int aktuell_pwm_l=0;
volatile int aktuell_pwm_h=0;

ISR(TIMER1_CAPT_vect)
{
	if (flanke==0)
	{
		TCNT1H = 0;             //Timer auf null zurück
		TCNT1L = 0;
		TCCR1B&= ~(1<<ICES1); //ICP-Pin auf fallende Flanke einstellen
		flanke=1;
	}
	else
	{
		flanke=0;
		TCCR1B|=(1<<ICES1);    //ICP auf steigende Flanke wieder aktivieren
		aktuell_pwm_l=ICR1L;    //Zählerstand speichern
		aktuell_pwm_h=ICR1H;    //zählerstand speichern
	}
}

void init_prozedur(void)
{
	TCCR1B |=  (1<<ICNC1)|(1<<ICES1)|(1<<CS11) | (1<<CS10); //vorteiler 64, Mittelung ON, steigende flanke
	TIMSK |= (1<<TICIE1);//INTERRUPT ON

	sei();
}

int main (void)
{
	init_prozedur();
	DDRC=0xff;
	DDRB=0b11110000;
	while(1)
	{
		if (aktuell_pwm_l >= 50 && aktuell_pwm_l < 60)
		{	
			PORTC^=(1<<PC1);
			_delay_us(200);
		}
		if (aktuell_pwm_l >= 60 && aktuell_pwm_l < 62)
		{	
			PORTC^=(1<<PC1);
			_delay_us(150);
		}
		if (aktuell_pwm_l >= 62 && aktuell_pwm_l < 64)
		{	
			PORTC^=(1<<PC1);
			_delay_us(100);
		}
		if (aktuell_pwm_l >= 64 && aktuell_pwm_l < 66)
		{
			PORTC^=(1<<PC1);
			_delay_us(100);
		}
		if (aktuell_pwm_l >= 66 && aktuell_pwm_l < 68)
		{
			PORTC^=(1<<PC1);
			_delay_us(100);
		}
		if (aktuell_pwm_l >= 68 && aktuell_pwm_l < 70)
		{
			PORTC^=(1<<PC1);
			_delay_us(150);
		}
		if (aktuell_pwm_l >= 70 && aktuell_pwm_l < 72)
		{
			PORTC^=(1<<PC1);
			_delay_us(200);
		}
		if (aktuell_pwm_l >= 72 && aktuell_pwm_l < 74)
		{
			PORTC^=(1<<PC1);
			_delay_us(250);
		}
		if (aktuell_pwm_l >= 74 && aktuell_pwm_l < 76)
		{
			PORTC^=(1<<PC1);
			_delay_us(300);
		}
		if (aktuell_pwm_l >= 76 && aktuell_pwm_l < 78)
		{
			PORTC^=(1<<PC1);
			_delay_us(350);
		}
		if (aktuell_pwm_l >= 78 && aktuell_pwm_l < 80)
		{
			PORTC^=(1<<PC1);
			_delay_us(400);
		}
		if (aktuell_pwm_l >= 80 && aktuell_pwm_l < 82)
		{
			PORTC^=(1<<PC1);
			_delay_us(450);
		}
		if (aktuell_pwm_l >= 82 && aktuell_pwm_l < 84)
		{
			PORTC^=(1<<PC1);
			_delay_us(500);
		}
		if (aktuell_pwm_l >= 84 && aktuell_pwm_l < 86)
		{
			PORTC^=(1<<PC1);
			_delay_us(550);
		}
		if (aktuell_pwm_l >= 86 && aktuell_pwm_l < 88)
		{
			PORTC^=(1<<PC1);
			_delay_us(600);
		}
		if (aktuell_pwm_l >= 88 && aktuell_pwm_l < 90)
		{
			PORTC^=(1<<PC1);
			_delay_us(650);
		}
		if (aktuell_pwm_l >= 90 && aktuell_pwm_l < 92)
		{
			PORTC^=(1<<PC1);
			_delay_us(700);
		}
		if (aktuell_pwm_l >= 92 && aktuell_pwm_l < 94)
		{
			PORTC^=(1<<PC1);
			_delay_us(750);
		}
		if (aktuell_pwm_l >= 94 && aktuell_pwm_l < 96)
		{
			PORTC^=(1<<PC1);
			_delay_us(800);
		}
		if (aktuell_pwm_l >= 96 && aktuell_pwm_l < 98)
		{
			PORTC^=(1<<PC1);
			_delay_us(850);
		}
		if (aktuell_pwm_l >= 98 && aktuell_pwm_l < 100)
		{
			PORTC^=(1<<PC1);
			_delay_us(900);
		}
		if (aktuell_pwm_l >= 100 && aktuell_pwm_l < 102)
		{
			PORTC^=(1<<PC1);
			_delay_us(950);
		}
		if (aktuell_pwm_l >= 102 && aktuell_pwm_l < 104)
		{
			PORTC^=(1<<PC1);
			_delay_us(1000);
		}
		if (aktuell_pwm_l >= 104 && aktuell_pwm_l < 106)
		{
			PORTC^=(1<<PC1);
			_delay_us(1200);
		}
		if (aktuell_pwm_l >= 106 && aktuell_pwm_l < 108)
		{
			PORTC^=(1<<PC1);
			_delay_us(1400);
		}
		if (aktuell_pwm_l >= 108 && aktuell_pwm_l < 110)
		{
			PORTC^=(1<<PC1);
			_delay_us(2000);
		}
		if (aktuell_pwm_l >= 110 && aktuell_pwm_l < 112)
		{
			PORTC^=(1<<PC1);
			_delay_us(6000);
		}
		if (aktuell_pwm_l >= 112 && aktuell_pwm_l < 114)
		{
			PORTC^=(1<<PC1);
			_delay_us(10000);
		}
		if (aktuell_pwm_l >= 114 && aktuell_pwm_l < 116)
		{
			PORTC^=(1<<PC1);
			_delay_us(14000);
		}
		if (aktuell_pwm_l >= 116 && aktuell_pwm_l < 118)
		{
			PORTC^=(1<<PC1);
			_delay_us(18000);
		}
		if (aktuell_pwm_l >= 118 && aktuell_pwm_l < 120)
		{
			PORTC^=(1<<PC1);
			_delay_us(200000);
		}
		if (aktuell_pwm_l >= 120 && aktuell_pwm_l < 122)
		{
			PORTC^=(1<<PC1);
			_delay_us(200000);
		}
		if (aktuell_pwm_l >= 122 && aktuell_pwm_l < 124)
		{
			PORTC &= ~(1<<PC1);
		}
		
		
	}
}

Über eure Hilfe würde ich mich sehr freuen.

LG
Digit-22
 
Hallo Digit-22,

Willkommen im AVR-Praxis-Forum!

Versuchen doch zunächst erst einmal den DC-Motor anzusteuern, indem du fese PWM-Werte vorgibst. Dazu würde ich die Output Compare Funktion eines Timers verwenden und die entprechende Portpin-Funktion dazu aktivieren.

Wenn dies dann funktioniert, würde ich mich mit der PWM Messung beschäftigen. Dafür würde ich einen anderen Timer und dessen InputCapture Funktion verwenden. Es gibt da sicherlich aber auch andere Wege.

Ich könnte mir vorstellen, dass es schon bei der Ansteuerung des DC-Motors über PWM nicht so einfach ist, da du keine Regelung einsetzt und die Drehzahl nicht unbedingt proportional zur PWM ist (Stichpunkt BackEMF Messung).

Viel Spaß noch im Forum!

Dirk :ciao:
 
Der ATmega8 verfügt über 3 Hardware-PWM-Kanäle. (Der Nachfolger, der ATmega88 hätte sogar 6).
Lies Dir mal bitte die entsprechenden Seiten im Datenblatt durch, und frage dann nach.

Der PWM soll dann mehr oder weniger direkt einen LL-FET ansteuern, welcher den Motor unterbricht? Der Motor muß nur vorwärts können, oder?
Trotzdem würde ich überlegen, statt der FETs so'n H-Brücken-IC zu nehmen, falls sich dadurch der Schaltungsaufwand minimieren läßt.
Da die Dinger üblicherweise 2 H-Brücken enthalten, könntest Du 2 Motoren dranhängen.

Mit'nem Mega88 und 2 solcher Bausteine könntest Du also problemlos 4 Motoren ansteuern, aber möglicherweise willst Du die Regler dezentral platzieren.
Den fließenden Motorstrom etc. willst Du nicht auswerten?
Dann würde auch ein kleinerer Controller reichen...
 
Hallo danke für die Antworten.

-Der Atmega8 ist erstmal zum testen und später möchte ich mit diesen Regler mehrere Motoren mit 4 separaten PWM Signale ansteuern. (Z.b. Multikopter)
Ich denke spätestens dann brauch ich einen anderen Controller...
-Ja das PWM Signal wird direkt an einem Fet angeschlossen. (Aktuell zum testen eine einfachen Transistor BC328 )
-Ja nur Vorwerts mehr werde ich nicht benötigen.
- Mega88 und 2 Brücken für 4 Motoren hört sich super an.
- Nein den Motorstrom will ich nicht auswerten. Vielleicht später wenn das ganze schon etwas ausgereift ist.


Ich habe noch was im Netz gefunden. Der Code erzeugt Fast PWM am PB1.

Code:
#include <avr/io.h>
#include <avr/interrupt.h>

int main(void)
{

  DDRB = 0x02;                         // Setup PB1 as output
  DDRC = 0x03;                         // Setup PC0 and PC1 as output

  ICR1 = 1000;                         // Set TOP to 10000
  OCR1A = 500;                         // Set Dutycycle to 10%

  
  
  TCCR1A = (1<<WGM11)|(1<<COM1A1);     // Set Timer1 in Phase Correct Mode
  // with ICR1 as TOP value. Set OC1A
  TCCR1B = (1<<WGM13)|(1<<CS10);       // on compare match and clear OC1A
  // on BOTTOM. Clock Prescaler is 1024
  
  
  TIMSK |= (1<<TOIE1) | (1<<OCIE1A);   // Enable Timer 1 Overflow
  // and Compare interrupt
  sei();                               // Set the I-bit in SREG

  for(;;);                             // Endless loop
  // main() will never be left

  return 0;                            // This line will never be executed
}

// Interrupt subroutine timer 1 overflow
ISR(TIMER1_OVF_vect)
{
  PORTC ^= 0x01;                       // Toggle Pc0
  PORTC ^= 0x01;                       // Toggle PC0
}

// Interrupt subroutine timer 1 compare A match
ISR(TIMER1_COMPA_vect)
{
  PORTC ^= 0x02;                       // Toggle PC1
  PORTC ^= 0x02;                       // Toggle PC1
}




Wenn ich diesen Wert "OCR1A = 500;" verändere, ändert sich die Drehzahl
an meinem Motor. Der Motor läuft sehr ruhig und sauber.
Bei 100 läuft der motor schneller und bei 500 langsamer...
Das ist genau das was ich gesucht habe. Am besten von 1 bis 1000. Denn bei 1 hab ich max drehzahl und bei 1000 steht der motor.

Jetzt ist die Frage wie bekomme ich diesen Code in meinem Code eingebaut
so das ich mit meinem PWM vom RC Empfänger diesen Wert "OCR1A = 500;"
veränderen kann.



Hat da einer ne idee?




LG
Shabi
 
Hallo nochmals.


Hat denn keiner eine Idee wie man die beiden Codes kombinieren kann?



LG
 
Hi,

auch von mir ein herzlich willkommen :flowers:

Hat denn keiner eine Idee wie man die beiden Codes kombinieren kann?
dein erster Beitrag von heute ist um 10:00. Da sind noch einige auf Arbeit :rolleyes: Die meißten helfen ja in ihrer Freizeit. Außerdem muß man sich noch in C auskennen was bei mir nicht der Fall ist. Ein wenig Gedult ... ;)

Gruß
Dino
 
Hallo,
Hallo nochmals.


Hat denn keiner eine Idee wie man die beiden Codes kombinieren kann?

LG

dein Programm aus dem ersten Beitrag verstehe ich nicht so ganz. Du erzeugt hier auch keine PWM sondern einfach eine Modulation der Frequenz.

Danach hast du ja die OutputCompare-Funktion des Timer1 eingesetzt und die PWM für den DC Motor zu erzeugen. Hier benötigst du auch keine Timer ISRs, da ja der Mikrocontroller die PWM per Hardware erzeugt.

Im ersten Beispiel möchtest du die InputCapture-Funktion nutzen, um ein PWM-Verhältnis oder eine Frequenz zu messen?! (Ist mir noch nicht ganz klar, ich denke auch nicht, dass hier "aktuell_pwm_h/l" sttimmt). Du nutzt jedenfalls hierfür ebenfalls den Timer 1, der wird aber nun für PWM-Erzeugung genutzt. Also eventuell auf einen anderen Timer ausweichen, ggf. auf einen anderen Mikrocontroller. Du wirst es also so im Moment nicht hinbekommen.


PWM : 0...1000 (schnell ... langsam)

Eingangswert (aktuell_pwm_l): 0...127 ? (soll schnell drehen ... soll langsam drehen) ?

Das könntest du zum Testen verwenden:
Code:
uint16_t data = aktuell_pwm_l << 3; // *8
if (data > 1000) data = 1000;
OCR1A = data; // 0...1000


Eine Fertiglösung habe ich leider nicht. Der Timer1 erzeugt die PWM, hier müsste man die InputCapture Funktion noch einbauen (falls möglich) oder auf einen anderen Timer ausweichen, oder anders messen.

Dirk :ciao:




PS. Dein Thema läuft ja hier schon einige Zeit: http://www.mikrocontroller.net/topic/267407
Wenn ihr dort eine Lösung findet, kannst du ja dies hier vermerken.
 
So, von der Arbeit zurück...

Irgendwie sehe ich hier jetzt auch nicht so recht, was Du eigentlich willst.
-Was für ein Signal liefert Dir denn Deine Fernbedienung? (Ich kenne bisher nur die, wo ca alle 20ms oder so ein low-hi-Wechsel erfolgt, dieser ist zwischen 1ms (min) und 2ms (max) lang. Vor dem (De-)Multiplexing sind alle Kanäle zusammen hintereinander, deswegen hier die Lange Pause))
-Der Kommentar beim beschreiben von TCCR1B stimmt nicht. Im Phasenkorrekten PWM zählt der Timer immer zwischen bottom und top hin und her. Beim Compare match wird dann der Ausgang entsprechend geschaltet (je nach Compare output mode). Hier wird der Ausgang beim hochzählen beim compare match gelöscht, beim runterzählen gesetzt. Deswegen ist die Drehzahl invers zum Compare-Wert.
-eigentlich müßtest Du nur Deine Pulslängen-Messung so sinnig gestalten, daß das Ergebnis mehr oder weniger direkt in die Compare-Register übernommen werden kann. Vielleicht reicht auch der reichweitenbegrenzte FastPWM (singleSlope) aus.

(Was ist eigentlich der Unterschied zwischen WGM8/9 und WGM10/11? Nur der Aktualisierungszeitpunkt der Compareregister?)

P.S.: wenn's auf die Größe ankommt, wäre der Tiny4/5/9/10 (bzw hier)'ne Option. Allerdings mit nur einem Timer etwas Tricky. Ich würde das in etwa so angehen:
-Die Timerreichweite ist sinnig zu wählen - mindestens 2ms (um die Berechnung der Pulslänge einfach zu gestalten), andererseits sollte sich die Berechnung des OutputCompareRegisters vereinfachen lassen. (Vorschlag: Top=15999)
-Das Signal wird per Interrupt gemessen (dummerweise ist ICP0 und OC0B derselbe Pin - man muß also die Reichweite entweder über diesen Pin begrenzen (ICR0) oder den PWM ausgeben (OCR0B - wenn OCR0A begrenzen soll) - InputCapture geht hier also nicht. Also über Int0 die Flankenwechsel erfassen, und die Differenz bilden (etwaige Unterläufe beachten)
-vom erhaltenen Wert ist 1ms abzuziehen (8000) - der Rest einmal nach links zu schieben. Das sollte derWert für das OutputCompareRegister sein.

Ich bin von internen 8MHz ausgegangen - sind hier zu später Stunde noch gravierende Fehler zu finden?
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)