Mikrocontroller Labor Timer

From EWIKI
Jump to navigation Jump to search

Idee

LabTimer.png

Ein komfortabler Timer für meinen DeVere Großformat-Vergrößerer muss her!

Zutaten:

  • uC: ATMega 8
    hat genug Ports für LCD, Taster, Status LEDs und Relais
  • Onboard 5V Netzteil
  • Relais:
    • HJR-4102-L 5V
  • Dot Matrix Display mit HD44780 (rot)
    • Nachteil 1: es benötigt wohl 7V für die Hintergrundbeleuchtung
    • Nachteil 2: es ist sehr träge, ca 0.5 sec Latenz
DeVere TrafoTimer plug.jpg
  • Taster
    • Timer start (mit Interrupt zum timer stop): schaltet Relais an und nach vorgegebener Zeit wieder aus
    • Focus Licht: schaltet Relais an/aus
    • Step: wählt zwischen
      • ganzen F-Stop Zeiten
      • 1/3 F-Stop Zeiten
      • 1/6 F-Stop Zeiten
    • Plus: höhere Zeit
    • Minus: niedrigere Zeit

Tipp: Taster elektrisch entprellen:

  • 2k zum I/O Port
  • zwischen R und Port 20nF nach GND

F-Stop Timer

Der Timer soll in Blenden-Stufen die Belichtung steuern. Dazu sollten die Schritte zwischen ganzen, drittel und sechstel Stufen gewählt werden können (Set Taste). Mit einer Up/Down Taste soll dann zwischen den Zeiten geschaltet werden. Der Vorteil dieser Methode ist, dass bei jedem Schritt die Belichtung proportional geändert wird. Im Gegensatz dazu hat man z.B. bei einer Belichtungsreihe mit 5 sec Schritten erst riesige Änderungen und in den letzten Belichtungen sieht man kaum noch Unterschied.

Die Focus Taste stellt (das Relais) auf Dauerlicht und die Timer Taste schaltet das Relais für die angegebene Zeit ein.

F-Stop Zeiten:

8 9 10.1 11.3 12.7 14.3 16 18 20.2 22.6 25.4 28.5 32 35.9 40.3 45.3 50.8 57 64

Fett=ganze Schritte / Normal=Drittelschritte / Kursiv=Sechstelschritte

Bedienung

Standardanzeige

 8.0  16.0  32.0  
 -1   ^^^^   +1

ausgehend davon:

Über die diversen Taster lassen sich folgende Einstellungen vornehmen

Taster SEL

Schrittweite auf 1/3 umschalten:

12.7  16.0  20.2  
-1/3  ^^^^  +1/3

Taster SEL

Schrittweite auf 1/6 umschalten:

14.3  16.0  18.0  
-1/6  ^^^^  +1/6

Danach schaltet man mit SEL wieder auf ganze Schritte.

Taster PLUS

Nächste längere Zeit auswählen

16.0  18.0  20.2  
-1/6  ^^^^  +1/6


längste Zeit (hier im 1/3 Modus)

50.8  64.0 
-1/3  ^^^^  +1/3

Taster MINUS

Nächste kürzere Zeit auswählen

12.7  14.3  16.0  
-1/6  ^^^^  +1/6

kürzeste Zeit

       8.0   9.0 
-1/6  ^^^^  +1/6

Taster FOCUS

Focus Licht an

 12.7  14.3  16.0  
*-1/6  ^^^^  +1/6

Taster TIMER

Zeit ablaufen: ##.# als Count Down

12.7  14.3  16.0  
      ##.#

Pin Belegung Red-LCD (LCM039A)

ATMega8
LCD Pin Stecker Belegung ATmega8
1 RS PC4
2 R/W (GND) > 4
3 E PC5
4 GND 0 V
5 VCC +5V
6 V0 (GND) > 4
oder 10k Poti
GND-V0-VCC
7 DB0 nc
8 DB1 nc
9 DB2 nc
10 DB3 nc
11 DB4 PC0
12 DB5 PC1
13 DB6 PC2
14 DB7 PC3
15 + LED +7 V
16 - LED1 0 V
17 - LED2 -> 16
18 - LED3 -> 16
19 - LED4 -> 16

Schema

TimerSchema.png

Change: Kein Poti an LCD notwendig, V0 -> GND

Prototyp

.. OK: Pinout zu den Tastern ist etwas anders als geplant ...

AVR-Timer-LCD.jpg Avr-timer-platine.png

AVR C Code

/*
 * avr-timer.c
 *
 *  Created on: Dec 15, 2014
 *      Author: mh
 */
#include <inttypes.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#include <avr/io.h>
#include "src/lcd-routines.h"

#include "src/led-helper.h"
#include "src/io-helper.h"

#define F_CPU 1000000UL						// ATmega8 default intenal clock at 1 MHz
#include <util/delay.h>

const int16_t fStopTime[] = { 0,				// f-Stops timings (x100ms)
		 40,  45,  50,  57,  63,  71,
		 80,  90, 101, 113, 127, 143,
		160, 180, 202, 226, 254, 285,
		320, 359, 403, 453, 508, 570,
		640, 0 };

int keyPressed = 0;
int tim = 0;

int timeSelStep = 6; 						//  6= 1 stop   2= 1/3 stop   1= 1/6 stop
int timeSel = 13;
const int timeSelMax = 25;


// ======================================================

void lcdPrintSelTimes() {
	int i1  = 0;						// calculate fixed comma times with XX.X format
	int i1n = 0;
	if ( timeSel > timeSelStep ) {
		i1  = fStopTime[ timeSel-timeSelStep ] / 10;
		i1n = fStopTime[ timeSel-timeSelStep ] % 10;
	}

	int i2  = fStopTime[ timeSel ] / 10;
	int i2n = fStopTime[ timeSel ] % 10;

	int i3  = 0;
	int i3n = 0;
	if ( timeSel + timeSelStep <= timeSelMax) {
		i3  = fStopTime[ timeSel+timeSelStep ] / 10;
		i3n = fStopTime[ timeSel+timeSelStep ] % 10;
	}

	char strTim[20];					// display times in 1st row
	if ( timeSel == 1 ) {
		sprintf( strTim, "      %2d.%d %2d.%d",
				i2, i2n, i3, i3n );
	} else if ( timeSel == timeSelMax ) {
		sprintf( strTim, " %2d.%d %2d.%d     ",
				i1, i1n, i2, i2n );
	} else {
		sprintf( strTim, " %2d.%d %2d.%d %2d.%d",
				i1, i1n, i2, i2n, i3, i3n );
	}
	lcd_setcursor( 0, 1 );
	lcd_string( strTim );

	lcd_setcursor( 0, 2 );					// 2nd row with info
	if ( timeSelStep == 6 ) {
		lcd_string( "  -1  ^^^^  +1 " );
	} else 	if ( timeSelStep == 2 ) {
		lcd_string( " -1/3 ^^^^ +1/3 " );
	} else 	if ( timeSelStep == 1 ) {
		lcd_string( " -1/6 ^^^^ +1/6 " );
	}
}

// ======================================================
void checkTimerChange() {
	// Pin D7 = +
    if ( ! ( PIND & (1<<PIND4) ) )  { 				// taste gedrueckt, kein entprellen notwendig, da delay bei LCD out
    	if ( timeSel + timeSelStep <= timeSelMax ) {
    		timeSel = timeSel + timeSelStep;
    	}
    	lcdPrintSelTimes();
    	while ( ! ( PIND & (1<<PIND4) ) )  {  			// immer noch gedrueckt, warte auf loslassen
        	_delay_ms( 100 );
    	}
    }
    // Pin D6 = -
    if ( ! ( PIND & (1<<PIND7) ) )  { 				// taste gedrueckt
    	if ( timeSel > timeSelStep ) {
    		timeSel = timeSel - timeSelStep;
    	}
    	lcdPrintSelTimes();
    	while ( ! ( PIND & (1<<PIND7) ) )  {  			// immer noch gedrueckt, warte auf loslassen
        	_delay_ms( 100 );
    	}
    }

    // Pin B7 = select step size
    if ( ! ( PIND & (1<<PIND3) ) )  { 				// taste gedrueckt

    	if ( timeSelStep == 6 ) {
    		timeSelStep = 2;
      	} else 	if ( timeSelStep == 2 ) {
    		timeSelStep = 1;
    	} else 	if ( timeSelStep == 1 ) {
    		timeSelStep = 6;
    		while ( ( timeSel % 6 )  != 1 ) { 		// adjust to full f-stop
    			timeSel++;
    		}
    	}
		lcdPrintSelTimes();
		while ( ! ( PIND & (1<<PIND3) ) )  {  		// immer noch gedrueckt, warte auf loslassen
			_delay_ms( 100 );
		}
	}
}

// ======================================================

void enableInt1Interrupt() {
	MCUCR |= (1<<ISC01) | (1<<ISC00);   			//Steigende Flanke von INT0 als Auslöser
	GICR  |= (1<<INT0);					//Global Interrupt Flag für INT0
	sei();							//Interrupt aktiviere
}
void disableInt1Interrupt() {
	cli();							// Interrupts abschalten
}

// ======================================================

ISR(INT0_vect) {
	keyPressed = 1;
	tim = 0;
	LED2on();
}

// ======================================================

int timerButtonPressed() {
    if ( ! ( PIND & (1<<PIND2) ) )  { 			// taste gedrueckt
    	_delay_ms( 200 );
    	while ( ! ( PIND & (1<<PIND2) ) )  {  		// immer noch gesruckt, warte auf loslassen
        	_delay_ms( 100 );
    	}
    	return 1; 					// gedrueckt
    }
    return 0; 						// nicht gedrueckt
}

// ======================================================

int focusLightButtonPressed() {
    if ( ! ( PINB & (1<<PINB0) ) )  { 			// taste gedrueckt
    	_delay_ms( 200 );
    	while ( ! ( PINB & (1<<PINB0) ) )  {  		// immer noch gesruckt, warte auf loslassen
        	_delay_ms( 100 );
    	}
    	return 1; 					// gedrueckt
    }
    return 0; 						// nicht gedrueckt
}

// ======================================================
void timerCountDown( int time ) {
	char strTim[20];
	enableInt1Interrupt();
	_delay_ms( 100 ); 					// ignore wild interrupts
	keyPressed = 0;
	tim = time;						// set start time
	int tgPre = -1;
	while ( tim > 0 ) {					// count down loop
		tim = tim - 1;					// time/tim is 1/10th sec unit
		_delay_ms( 100-3 ); 				// 100 ms loop - sum of LCD routine delays
		// count down on LCD:
		int tg = tim / 10;
		int tn = tim % 10;
		if ( (tim % 3) == 0 ) {				// LCD output all 300 ms
			if ( tgPre != tg ) {
				sprintf( strTim, "      %2d.%d sec  ", tg, tn );
				lcd_setcursor( 0, 2 );		// internal delay: 2x 20 µs + 42 µs
				lcd_string( strTim );		// internal delay: 46 µs + 2x 20 µs
				tgPre = tg;
			} else {				// output only comma digits (LCD too slow)
				sprintf( strTim, "%d", tn );
				lcd_setcursor( 9, 2 );		// internal delay: 2x 20 µs + 42 µs
				lcd_string( strTim );		// internal delay: 46 µs + 2x 20 µs
			}
		}
		if ( keyPressed ) {	break; }
	}
	disableInt1Interrupt();
}

// ======================================================
// main program
int main(void) {
	prepareInputs();
	switchRelaisOff();
	lcd_init();

	lcd_setcursor( 0, 1 );
	lcd_string("F-Stop Timer");
	lcd_setcursor( 0, 2 );
	lcd_string("v1.12 (MH/2015)");

	while ( ! timerButtonPressed() ) {
		_delay_ms( 10 );
	}
	lcd_clear();
	lcdPrintSelTimes();

	while (1) {						// endless loop

		checkTimerChange();				// evaluate key press actions

		if ( timerButtonPressed() ) {			// start timer
			LEDon();
			switchRelaisOn();
			timerCountDown( fStopTime[ timeSel ] );	// wait for given time or INT0
			switchRelaisOff();
			lcdPrintSelTimes();
			LEDoff();
		}
		if ( focusLightButtonPressed() ) {
			LEDon();
			lcd_setcursor( 0, 2 );
			lcd_string("*");
			switchRelaisOn();
			while ( ! focusLightButtonPressed() ) {
				_delay_ms( 10 );
			}
			switchRelaisOff();
			lcdPrintSelTimes();
			LEDoff();
			lcd_setcursor( 0, 2 );
			lcd_string(" ");
		}
	}
	return 0;
}

Fertiges Projekt

AVR-Timer fertig.JPG

Links