RCStrobo

RCStrobo basiert auf der gleichen Platine wie das RCLight. Statt eines Fernsteuerempfängerausgangs wird hier ein HAL Sensor TLE4905g verwendet, um die an den Schaltausgängen angeschlossenen Leistungs LEDs mit dem Hauptrotor eines Helikopters zu synchronisieren. Dadurch entsteht ein farbiges stehendes Bild beim Nachtflug.

Die Idee dazu habe ich bei Aleksey Zaitsevsky gesehen, aber vollständig selber entwickelt. Wobei das Berechnen der richtigen Werte für den Prescaler des Controller Timers das Aufwendigste war, da der RCtiny wenig Reserven für diese zeitkritische Anwendung hat.

Der Schaltplan als PDF liegt hier.

Die Firmware ist Atmels AVR-Studio entwickelt und dann mit USBasp geflasht. Als Programmierstecker dient ein halbierter Floppydisk Platinenstecker, der nur einige Sekunden auf die Platinenpads gehalten werden muss.

Der C-Source Code ist hier als Download. Die nicht kommerzielle Nutzung der Daten ist gestattet.

/*
 * RcStrobo I/O
 * Author: Heinz Bruederlin
 * 01.04.10 First implemented
 *
 * CPU:       ATtiny 13
 *
 * Clock:                  9.6MHz
 * Prescaler:               64
 * Timer0 Freq:            150kHz
 * Timer0 Period:   0,00000667sec  
 * Timer0 Overflow: 0,00170667sec  
 *                                                                      
 * Timing                                                               
 * Speed                    1500RPM                      3000RPM
 * Rotation / second          25                           50
 * Time / Rotation      0,040000sec = 6000(1770)tic  0,020000sec = 3000(0BB8)tic
 * Time / Segment     2 0,020000sec = 3000(0BB8)tic  0,010000sec = 1500(05DC)tic
 *                    4 0,010000sec = 1500(05DC)tic  0,005000sec =  750(02EE)tic
 *                    8 0,005000sec =  750(02EE)tic  0,002500sec =  375(0177)tic
 *                   16 0,002500sec =  375(0177)tic  0,001250sec =  188(00BB)tic
 *                   32 0,001250sec =  188(00BB)tic  0,000625sec =   94(005D)tic
 *
 * OUT1 : red
 * OUT2 : green
 * OUT3 : yellow
 * OUT4 : blue
 */
#ifndef F_CPU
#define F_CPU 9600000  /* Clock in Hz */
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

FUSES = {
    .low  = 0xff & (FUSE_CKSEL0 &   /* internal RC 9.6MHz */
		    FUSE_SUT0   &   /* max startup time */
		    FUSE_SPIEN),    /* SPI enabled */
    .high = 0xff & (FUSE_BODLEVEL1) /* Brown-out 2.7V */
};

/* PORTB defines */
#define OUT4  _BV(PB0) /* connected to FET */
#define HAL   _BV(PB1) /* connected to HAL Sensor*/
#define OUT1  _BV(PB2) /* connected to FET */
#define OUT2  _BV(PB3) /* connected to FET */
#define OUT3  _BV(PB4) /* connected to FET */

/*
 * globals
 */
typedef struct { uint8_t l,h; } bytelh_t;
typedef union {
    bytelh_t _;
    uint16_t __;
} uint8_16_t; 

volatile uint8_t    TcntH;	 // high byte (timer overflow)
volatile uint8_16_t SegDuration; // 16 bit duration for one segment
volatile uint8_16_t SegTime;	 // 16 bit time of next segment switch
volatile uint8_t    SegCnt;	 // number of segments
volatile uint8_t    SegIdx;	 // current segment index modulo 4

/*
 * io_init -
 *	init io ports
 */
void io_init(void) {
   /* switch off outputs and set pullup at input */
   PORTB = HAL;
   /* Set PORT B outputs */
   DDRB  = OUT1 | OUT2 | OUT3 | OUT4;
}

/*
 * timer_init -
 *	init timer 0
 */
void timer_init(void) {
    TCCR0B |= _BV(CS01) | _BV(CS00); // set timer0 prescaler to 64
    TCNT0  = 0;		 // clear timer0
    TcntH  = 0;		 // clear high byte
    TIFR0 |= _BV(TOV0);	 // clear pending overflow 
}

/*
 * global_init -
 *	init globals
 */
void global_init(void) {
    SegCnt = 8;
}


/*
 * intr_init -
 *	enable timer0 overflow and int0 interrupts
 */
void intr_init(void) {
    TIMSK0 |= _BV(OCIE0A) |	     // enable timer0 compare match A interrupt
              _BV(TOIE0);	     // enable timer0 overflow interrupt
    MCUCR  |= _BV(ISC01)|_BV(ISC00); // rising edge generates an interrupt
    GIMSK  |= _BV(INT0); 	     // enable external interrupt for HAL
    sei();		 	     // enable interupts
}

/*
 * setLeds -
 *	sets LED depending on SegIdx
 */
void setLeds() {
    uint8_t o = PORTB & ~(OUT1 | OUT2 | OUT3 | OUT4);
    switch(SegIdx) {
	case 0: o |= OUT1; break;
	case 1: o |= OUT2; break;
	case 2: o |= OUT3; break;
	case 3: o |= OUT4; break;
    }
    PORTB = o;
}

/*
 * TIM0_OVF_vect -
 *	timer 0 overflow interrupt handler
 */
ISR(TIM0_OVF_vect) {
    TcntH++;
}

/*
 * INT0_vect -
 *	external interrupt 0
 *	called on each rising edge of HAL
 */
ISR(INT0_vect) {
    SegDuration._.l = TCNT0;
    SegDuration._.h = TcntH;
    TCNT0  = 0;		 // clear timer0
    TcntH  = 0;		 // clear high byte

    SegDuration.__ /= SegCnt;
    SegTime.__      = SegDuration.__;
    SegIdx          = 0;

    OCR0A = SegTime._.l;	
    setLeds();
}

/*
 * TIM0_COMPA_vect -
 *	Timer0 Compare Match A interrupt
 *	called on when timer 0 reached compare value
 */
ISR(TIM0_COMPA_vect) {
    if (TcntH == SegTime._.h) {
	SegTime.__ += SegDuration.__;
	OCR0A = SegTime._.l;	
	SegIdx++;
	if (SegIdx > 3) SegIdx = 0;
	setLeds();
    }
}

/*
 * main -
 *	init everything and go to endlees loop
 */
int main(void) {
    io_init();
    timer_init();
    global_init();
    intr_init();

    for(;;) {
    }
}