Zuletzt aktualisiert: 09.04.2023
Aquaristik
Modellbau
Software
3D-Druck
Garten
Radreisen
Kontakt
Impressum

RC-Signal mit Atmega8 entschlüsseln (selbstlernend)

An dieser Stelle möchte ich im Detail auf ein Programm eingehen, dass auf einem Atmega8 von Atmel in meinem U-Boot zum Einsatz kommt. Der Sinn des Programms ist eigentlich weniger von Belang, eher die eigentliche Umsetzung und die Lösung des Problems.

Gerade für Anfänger ist es sehr schwer, von einem Problem über ein fertiges Programm zur Lösung zu kommen, da entweder Elektronik oder ein Programm niemals auf Anhieb funktioniert.

Die Aufgabenstellung war relativ einfach: Im UBoot sind 3 Farben von LEDs verbaut, blaue, grüne und rote. Die Roten sollen anfangen zu blinken, sobald ein Sensor einen Wassereinbruch meldet. Davon unabhängig sollen Grüne und Blaue leuchten, und zwar je nachdem wie die Schalterstellung an der Fernsteuerung ist (3fach - Schalter).

Eigentlich kann man das Problem nun in folgende Phasen einteilen:
1. Empfängersignal, das sonst an die Servos weitergeleitet wurde, dekodieren.
2. Signal je nach Schalterstellung verarbeiten.
3. Mikrocontroller auf eigene Platine bauen.


Phase 1:
Zuerst müssen wir wissen, wie ein Servosignal aussieht, das am Ausgang des Empfängers anliegt. Es besteht aus einem sich periodisch wiederholenden Signal, mit der Frequenz von 50Hz , also 50 mal pro Sekunde, die Dauer eines Signals ist also 1 / 50s = 20 ms. Ein einziges Signalteil besteht nun daraus, dass zu Beginn ein fixer Teil "High" ist, nämlich genau 1ms. Hiernach bleibt das Signal auf "High", und zwar zwischen 0ms und 1ms, je nachdem wie die Stellung des Servo sein soll. Anschlag 1 ist also 0ms, Anschlag 2 ist 1ms, Mittelstellung ist 0,5ms. Danach fällt das Signal auf "Low" ab, und zwar so lange, bis in der Gesamtdauer 20 ms erreicht sind.

Dieses Signal wird am Port PD2 angelegt. Auf diesem Port konfigurieren wir den Atmega so, dass er jedes mal, wenn ein Flankenwechsel auftritt, einen Interrupt auslöst. Wir bekommen also den Anfang des Signals und das Ende genau mit. Anhand des Flankenwechsels können wir nun quasi eine Stoppuhr mitlaufen lassen, diese misst wie lange das Signal auf "High" ist. Eine Art Stoppuhr wir hier implementiert, indem ein Timer im Moment der steigenden Flanke gestartet wird, bei der fallenden Flanke gestoppt wird und zwischenzeitlich bei jedem Zeintintervall des Timers einen Zähler hochzählt.

2. Phase:
Grundbedingung, das Signal immer wiederzuerkennen, ist, dass der Atmega mit einer konstanten Taktung läuft, also mit einem externen Quarz, in meinem Fall mit 16 MHz. Nun müssen wir wir für die Schalterstellungen 0, -1 und 1 jeweils wissen, wie lange der Timer gezählt hat, dann können wir diese Werte nehmen, um eine IF - Bedingung zu bauen. Also sei das Signal fertig, so nehmen wir den Zählerstand, vergleichen ihn mit den Zählerständen, die bei den Schalterstellungen auftreten und schalten somit die LEDs an bzw. aus. Eigentlich könnte man diese festen Zählerstände fest ins Programm schreiben, ich habe mich jedoch für einen Selbstlerneffekt entschieden. Startet man also den Atmega, so merkt er sich die aktuelle Stellung an der Fernsteuerung als Nullstellung, schaltet man anders, so merkt er sich das Maximum und Minimum.

3. Phase:
Ist das Programm ersteinmal fertig, wird der Sockel auf die Platine gesetzt, GND an Minus, VCC an Plus, OCSEL1 und OCSEL2 mit dem Quarz verbinden bzw. nach GND mit Kondensatoren verbinden. Die Ausgangsports werden mit Widerständen von 470 Ohm bestückt, zum Schutz der LEDs. Der Signaleingang vom Servosignal wird direkt beschaltet, der Wassersensor mithilfe einer Darlingtonstufe so kräftig verstärkt, dass sogar ein Fingerauflegen auf den Wassersensor (zwei parallele blanke Kabel) zum Signal führt.

Nun das Beipsielprogramm:

#define F_CPU  16000000

#include <stdint.h>
#include <avr/eeprom.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define setBit(target,bitnumber)    (target|=(1<<bitnumber))
#define delBit(target,bitnumber)    (target&=(~(1<<bitnumber)))

#define isSet(port, pin)            ((port&(1 << pin)) == (1 << pin))
#define isNotSet(port, pin)         ((port&(1 << pin)) == 0)

#define configureAsOutput(target,bitnumber)   (setBit(target,bitnumber))
#define configureAsInput(target,bitnumber)    (delBit(target,bitnumber))

#define switchHigh(target,bitnumber)   (setBit(target,bitnumber))
#define switchLow(target,bitnumber)    (delBit(target,bitnumber))

#define switchLedOff(target,bitnumber)  (delBit(target,bitnumber))
#define switchLedOn(target,bitnumber)   (setBit(target,bitnumber))

#define isSwitchPressed(target,bitnumber)      (isNotSet(target,bitnumber))
#define isSwitchNotPressed(target,bitnumber)   (isSet(target,bitnumber))

#define isLow(port, pin)            (isSet(port,pin))
#define isHigh(port, pin)           (isNotSet(port,pin))

#define LED_DDR            DDRB
#define LED_OUTPUT_PORT    PORTB
#define LED_INPUT_PORT     PINB
#define LED_GREEN          PB0
#define LED_BLUE1          PB1
#define LED_BLUE2          PB2
#define LED_RED1           PB3
#define LED_RED2           PB4
#define WATERSENSOR        PINB5
#define BLINK_MS           200
volatile uint16_t cnt;
volatile uint16_t min_counter;
volatile uint16_t max_counter;
volatile uint16_t mid_counter;
volatile char is_up = 0;

void var_init() {
	cnt = 0;
	min_counter = 0;
	max_counter = 0;
}

void switchRedOn() {
	switchLedOn(LED_OUTPUT_PORT, LED_RED1);
	switchLedOn(LED_OUTPUT_PORT, LED_RED2);
}

void switchRedOff() {
	switchLedOff(LED_OUTPUT_PORT, LED_RED1);
	switchLedOff(LED_OUTPUT_PORT, LED_RED2);
}

void switchBlueOn() {
	switchLedOn(LED_OUTPUT_PORT, LED_BLUE1);
	switchLedOn(LED_OUTPUT_PORT, LED_BLUE2);
}

void switchBlueOff() {
	switchLedOff(LED_OUTPUT_PORT, LED_BLUE1);
	switchLedOff(LED_OUTPUT_PORT, LED_BLUE2);
}

void switchGreenOn() {
	switchLedOn(LED_OUTPUT_PORT, LED_GREEN);
}

void switchGreenOff() {
	switchLedOff(LED_OUTPUT_PORT, LED_GREEN);
}

void blinkRed() {
	for(char c = 0; c < 5; c++) {
		switchRedOn();
		_delay_ms(BLINK_MS);
		switchRedOff();
		_delay_ms(BLINK_MS);
	}
}

void blinkGreen() {
	for(char c = 0; c < 5; c++) {
		switchGreenOn();
		_delay_ms(BLINK_MS);
		switchGreenOff();
		_delay_ms(BLINK_MS);
	}
}

void blinkBlue() {
	for(char c = 0; c < 5; c++) {
		switchBlueOn();
		_delay_ms(BLINK_MS);
		switchBlueOff();
		_delay_ms(BLINK_MS);
	}
}

void interrupt_init() {
	// flankengesteuert, bei jeder flanke umschalten
	// PD2 ist INT0
	GICR |= (1 << INT0);
	MCUCR |= (1 << ISC00);

	// timer anschalten
	// vorteiler 64 -> 250 ticks pro 1 ms
	TCCR0 |= (1 << CS00);
	TIMSK |= (1 << TOIE0);
}

char isIn(uint16_t limit, uint16_t counter) {
	if (limit == counter || (limit - 1) == counter || (limit + 1) == counter) {
		return 1;
	}

	return 0;
}

ISR(INT0_vect) {
	if (is_up == 0) {
		// steigende flanke
		is_up = 1;
		// starte counter
	} else {
		cli();      
		// fallende flanke
		is_up = 0;

		if (min_counter == 0 && max_counter == 0) {
			min_counter = cnt;
			max_counter = cnt;
			mid_counter = cnt;
		} else {
			if (cnt < min_counter) {
				min_counter = cnt;
			}
			
			if (cnt > max_counter) {
				max_counter = cnt;
			}

			if (isIn(min_counter, cnt) == 1) {
				switchBlueOn();
				switchGreenOff();
			} else if (isIn(max_counter, cnt) == 1) {
				switchGreenOn();
				switchBlueOff();
			} else if (isIn(mid_counter, cnt) == 1) {
				switchGreenOff();
				switchBlueOff();
			}
		}

		sei();
	}
}

ISR(TIMER0_OVF_vect ) {
	if (is_up == 1) {
		cnt++;
	} else {
		cnt = 0;
	}
}

int main() {
	cli();

	var_init();

	configureAsOutput(LED_DDR,LED_RED1);
	configureAsOutput(LED_DDR,LED_RED2);
	configureAsOutput(LED_DDR,LED_BLUE1);
	configureAsOutput(LED_DDR,LED_BLUE2);
	configureAsOutput(LED_DDR,LED_GREEN);
	configureAsInput(LED_DDR,WATERSENSOR);

	switchRedOff();
	switchBlueOff();
	switchGreenOff();

	_delay_ms(1000);

	blinkRed();
	blinkGreen();
	blinkBlue();

	interrupt_init();
	sei();

	while(1) {
		if (isSwitchPressed(LED_INPUT_PORT, WATERSENSOR)) {
			// enter loop for blinking
			while(1) {
				switchRedOn();
				_delay_ms(500);
				switchRedOff();
				_delay_ms(500);
			}    
		}
	}

	return 0;
}
Copyright © 2023 Evolutec Alle Rechte vorbehalten.