Projekt: programm. Countdowntimer für UV-Belichtungsgerät

HalloMoin,

Hab vor ein paar Tagen auf dem Flohmarkt sein sehr gut erhaltendes UV- Belichtungsgerät von Proma für 25 Taler erstanden. ( Für die , die nicht wissen was ein UV-Belichtungsgerät ist: Eine scanner-große Vorrichtung, mit der über eine prog. Zeit eine ROH-Platine mit UV belichtet wird, um daraus später eine Platine ätzen zu können....)

Leider ist der Timer mit der 3-stell. 7-Segmentanzeige defekt, ja es fehlt sogar der Controller... :% Naja bei dem Preis kann man dat verschmerzen. Jedoch ist die Restelektronik in Ordnung. Bei manueller Relaisauslösung starten die Röhren, soweit sogut. Nun soll der "fehlende Timer" nun erneuert werden. Hardware: 328 via UNO ISP geflasht, Adafruit OLED blau 16x2, ALPS Drehgeber original vom Gerät.

Ausstattung des Timer: 1. Tastendruck wechselt die Menüauswahl && Timer Start 2. Drehen inkrementiert die zu programierbaren Zahlenwerte ( Einzel-&Zehner-Sekunden, Minuten) 3. Nach Timeraktivierung kann dieser per Tastendruck resettet werden 4. Bei Timerende kann erneut eingestellt werden.

ALPS : Drehgeber: immer nach 2 Stellungen wird eine Zahl ehöht

Hinweise zum unten aufgeführten CODE: - µC nimmt kein Shield in Anspruch, Sekundentakt kommt durch 16-Bit Timer 1 im CTC -Bei Timer Beginn wird ein PIN ( später für das Steuerrelais) für den Countdown auf HIGH geschaltet -Timer ist relativ genau, Hab den mal den Test für 24 h ( geänderters Programm) gemacht und hatte zum Schluss 3 sek. zu viel; da die Belichtung nur wenige MIN dauert, ist der Timer, denke ich, brauchbar :) -Mit geänderten LCD-Befehlen müsste ein HD 47780 auch funzen - Zwischenzeitlich gibts noch einige Animationen, --> nur Spielerei

Über Vorschläge zur CODE-Verbesserung wäre ich dankbar, soweits funktionierts aber schon echt gut.

Video: https://www.youtube.com/watch?v=PtVH05byRhA&feature=youtu.be

[code/* Countdown Timer für UV-Belichtungsgerät
 Hardware: ATmega 328 , Adafruit 16x2 oled, ALPS Drehgeber
 Drehgeber: Menüauswahl über Druck, Zahlenauswahl durch Drehen
 Timer kann jederzeit resettet und neu  programmiert werden
 */
// Tasterentprellung: Komfortroutine (C für AVR µC) nach Timer-Verfahren 
// Quelle:   -->http://www.mikrocontroller.net/articles/Entprellung nach Peter Dannegger


#include <Adafruit_CharacterOLED.h>
// Komfortroutine zum Entprellen
#define KEY_DDR         DDRD
#define KEY_PORT        PORTD
#define KEY_PIN         PIND
#define KEY0            PORTD0
#define KEY1            PORTD1
#define KEY2            PORTD2
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1| 1<<KEY2)
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
#define REPEAT_START    50                        // after 500ms
#define REPEAT_NEXT     20                        // every 200ms

volatile uint8_t key_state;                                // debounced and inverted key state:
volatile uint8_t key_press;                                // key press detect
volatile uint8_t key_rpt;                                  // key long press and repeat
volatile uint8_t tick=0;
volatile uint8_t tack=0;
volatile uint8_t tock=0;
volatile uint16_t seconds =0;
volatile boolean run =false;
uint8_t cnt = 0;
uint8_t set_sec_einer =0;
uint8_t set_sec_zehner =0;
uint16_t setsek=0;
uint8_t set_minute=0;
uint16_t sek_v_set_minute =0;
uint8_t minute =0;
uint8_t ez=0; // 1-er & 10-er
uint16_t sec=0;
uint16_t sek =0;
uint8_t m=0;//min

ISR( TIMER2_OVF_vect )                            // every 10ms
{
  static uint8_t ct0, ct1, rpt;
  uint8_t i;
  TCNT2 = (uint8_t)(int16_t)-(16000000 / 1024 * 10e-3 + 0.5);  // preload for 10ms
  i = key_state ^ KEY_PIN;                        // Taster HIGH AKTIV!
  ct0 = ~( ct0 & i );                             // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
  i &= ct0 & ct1;                                 // count until roll over ?
  key_state ^= i;                                 // then toggle debounced state
  key_press |= key_state & i;                     // 0->1: key press detect

  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
    rpt = REPEAT_START;                          // start delay
  if( --rpt == 0 ){
    rpt = REPEAT_NEXT;                            // repeat delay
    key_rpt |= key_state & REPEAT_MASK;
  }
}
uint8_t get_key_press( uint8_t key_mask )
{
  cli();                                          // read and clear atomic !
  key_mask &= key_press;                          // read key(s)
  key_press ^= key_mask;                          // clear key(s)
  sei();
  return key_mask;
}
uint8_t get_key_rpt( uint8_t key_mask )
{
  cli();                                          // read and clear atomic !
  key_mask &= key_rpt;                            // read key(s)
  key_rpt ^= key_mask;                            // clear key(s)
  sei();
  return key_mask;
}
uint8_t get_key_state( uint8_t key_mask )
{
  key_mask &= key_state;
  return key_mask;
}
uint8_t get_key_short( uint8_t key_mask )
{
  cli();                                          // read key state and key press atomic !
  return get_key_press( ~key_state & key_mask );
}
uint8_t get_key_long( uint8_t key_mask )
{
  return get_key_press( get_key_rpt( key_mask ));
}
Adafruit_CharacterOLED lcd(OLED_V1, A0, A1, A2, 9, 10, 11, 12);

// 8 eigene Zeichen können definert werden
byte Char1[8] = {
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111
};
byte Char2[8] = {
  B00000,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111
};
byte Char3[8] = {
  B00000,
  B00000,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111
};
byte Char4[8] = {
  B00000,
  B00000,
  B00000,
  B11111,
  B11111,
  B11111,
  B11111,
  B11111
};
byte Char5[8] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B11111,
  B11111,
  B11111,
  B11111
};
byte Char6[8] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B11111,
  B11111,
  B11111
};
byte Char7[8] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B11111,
  B11111
};

//µ-Zeichen
byte Char8[8] = {
  B00000,
  B00000,
  B00000,
  B10010,
  B10010,
  B10010,
  B11100,
  B10000
};
void animation_beim_Start()
{
  for(uint8_t i =0;i<=6;i++)
  {
    lcd.setCursor(7-i,1);
    lcd.write(i);
    lcd.setCursor(8+i,1);
    lcd.write(i);  
    delay(20);
  }
  for(uint8_t i =6;i>0;i--)
  {
    lcd.setCursor(7-i,1);
    lcd.print(" ");
    lcd.setCursor(8+i,1);
    lcd.print(" ");  
    delay(20);
  }
}
void setup() 
{
  DDRB|=(1<<PORTB0);
  PORTB &= ~(1<<PORTB0);
  TCCR1A = 0;
  TCCR1B = 0;
  OCR1A = 15624;                   // 1 sek.
  TCCR1B |= (1 << WGM12) | (1 << CS12)| (1<<CS10);
  TIMSK1 |=(1 << OCIE1A);
  // Configure debouncing routines
  KEY_DDR &= ~ALL_KEYS;                // configure key port for input
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
  TCCR2B = (1<<CS22)|(1<<CS20);         // divide by 1024
  TCNT2 = (uint8_t)(int16_t)-(16000000 / 1024 * 10e-3 + 0.5);  // preload for 10ms
  TIMSK2 |= 1<<TOIE2;                   // enable timer interrupt
  sei();
  lcd.createChar(0, Char1);
  lcd.createChar(1, Char2);
  lcd.createChar(2, Char3);
  lcd.createChar(3, Char4);
  lcd.createChar(4, Char5);
  lcd.createChar(5, Char6);
  lcd.createChar(6, Char7);
  lcd.createChar(7, Char8);
  delay(1000);
  lcd.begin(16, 2);
  lcd.setCursor(0,0);
  lcd.print("TIMER powered by");
  lcd.setCursor(1,1);
  lcd.print("ATmega 328");
  lcd.setCursor(12,1);
  lcd.write(7);
  lcd.print("C");
  lcd.setCursor(13,1);
  delay(2000);
  lcd.setCursor(0,0);
  lcd.print("                ");
  lcd.setCursor(0,1);
  lcd.print("                ");
  lcd.setCursor(4,0);
  lcd.print("STARTING");
  animation_beim_Start();
  animation_beim_Start();
  animation_beim_Start();
  animation_beim_Start();
  animation_beim_Start();
  animation_beim_Start();
  animation_beim_Start();
  animation_beim_Start();
  animation_beim_Start();
  animation_beim_Start();
  animation_beim_Start();
  lcd.setCursor(0,0);
  lcd.print("                ");
  lcd.setCursor(0,1);
  lcd.print("                ");
  lcd.setCursor(0,0);
  lcd.print(" TIMER - SETUP: ");
  lcd.setCursor(0,1);
  lcd.print(" PRESS BUTTON ! ");
  delay(1000);
}

loop:

void loop() 
{  
  if (cnt==0)
  {    
    lcd.setCursor(0,0);
    lcd.print("                ");
    animation_beim_Start();
    PORTB &=~ (1<<PORTB0);          // UV Lampen ( kommt später noch ) aus
  }
  if( get_key_press( 1<<KEY0 ))
  {
    cnt++;
  }
  //Menü 1 Display ohne "lcd.Clear" löschen (dauert), alle nullen
  if (cnt==1)
  { 
    for( uint16_t w =0;w<=700;w++)
    {
      if (w<55)
      {
        lcd.setCursor(0,0);
        lcd.print("                ");
        lcd.setCursor(0,1);
        lcd.print("                ");
      }
      else{
        lcd.setCursor(5,0);
        lcd.print("00::00");
      }
    }
  }
  // Menüs 2 : einer sekunden setzen
  else  if(cnt==2)
  { 
    lcd.setCursor(10,1);
    lcd.print("_");

    if( get_key_press( 1<<KEY1 ))
    {
      set_sec_einer++;
      if(set_sec_einer>=10)
      {
        set_sec_einer =0;
      }
    }
    char s [2];
    s[0] = '0' + set_sec_einer  % 10;
    s[1] = 0;
    lcd.setCursor(10,0);
    lcd.print(s);
  }
  //Menü 3 : zehnersekunden setzten
  else if (cnt==3)
  { 
    lcd.setCursor(9,1);
    lcd.print("_ ");
    if( get_key_press( 1<<KEY1 ))
    {  
      set_sec_zehner++;
      if(set_sec_zehner>5)
      {
        set_sec_zehner=0;
      }
    }
    ez = (set_sec_zehner*10)+set_sec_einer;
    lcd.setCursor(9,0);
    char z [2];
    z[0] = '0' + set_sec_zehner % 10;
    z[1] = 0;
    lcd.print(z);
  }
  //Menü 4: minuten setzen
  else if (cnt==4)
  {
    lcd.setCursor(9,1);
    lcd.print("  ");
    lcd.setCursor(5,1);
    lcd.print("__  ");
    if( get_key_press( 1<<KEY1 ))
    {
      set_minute++;
      if(set_minute>=11)
      {
        set_minute =0;
      }
      lcd.setCursor(5,0);
      char m [3];
      m[0] = '0' + (set_minute/ 10) % 10;
      m[1] = '0' + set_minute % 10;
      m[2] = 0;
      lcd.print(m);
    }
  }
  else if (cnt==5)
  {
    lcd.setCursor(2,1);
    lcd.print("PRESS START !");
    delay(150);
    lcd.setCursor(2,1);
    lcd.print("                ");
    delay(150);
  }
  //Menü 6: timer aktivieren, 1,5 sekunden der Startzeit der UV-Röhren berücksichtigen
  else if  (cnt==6)
  { 
    delay(1000);
    run =true;
    PORTB |= (1<<PORTB0);
    sek_v_set_minute = set_minute*60;
    setsek= ez+sek_v_set_minute;
    sec =  setsek - seconds;
    // 10:59-00:00
    if((sec>599)&&(sec<=659))
    {
      m=10;
      sek = sec-(60*10);
    }
    if((sec>539)&&(sec<=599))
    {
      m=9;
      sek = sec-(60*m);
    }
    if((sec>479)&&(sec<=539))
    {
      m=8;
      sek = sec-(60*m); 
    }
    if((sec>419)&&(sec<=479))
    {
      m=7;
      sek = sec-(60*m);
    }
    if((sec>359)&&(sec<=419))
    {
      m=6;
      sek = sec-(60*m);
    }
    if((sec>299)&&(sec<=359))
    {
      m=5;
      sek = sec-(60*m);
    }
    if((sec>239)&&(sec<=299))
    {
      m=4;
      sek = sec-(60*m);
    }
    if((sec>179)&&(sec<=239))
    {
      m=3;
      sek = sec-(60*m);
    }
    if((sec>119)&&(sec<=179))
    {
      m=2;
      sek = sec-(60*m);  
    }
    if((sec>59)&&(sec<=119))
    {
      m=1;
      sek = sec-(60*m);
    }
    if((sec>=0)&&(sec<=59))
    {
      m=0;
      sek = sec;
    }

    lcd.setCursor(5,0);
    char tm[3];
    tm[0] = '0' + (m  / 10) % 10;	
    tm[1] = '0' + m  % 10;
    tm[2] = 0;
    lcd.print(tm);
    lcd.setCursor(9,0);
    char ts[3];
    ts[0] = '0' + (sek  / 10) % 10;	
    ts[1] = '0' + sek  % 10;
    ts[2] = 0;
    lcd.print(ts);

    switch (tock){

    case 0:
      lcd.setCursor(tick+8,1);
      lcd.print("_");
      lcd.setCursor(7-tack,1);
      lcd.print("_");
      break;
    case 1:
      lcd.setCursor(tick+8,1);
      lcd.print("_");
      lcd.setCursor(7-tack,1);
      lcd.print("_");
      break;
    case 2:
      lcd.setCursor(tick+8,1);
      lcd.print("_");
      lcd.setCursor(7-tack,1);
      lcd.print("_");
      break;
    case 3:
      lcd.setCursor(tick+8,1);
      lcd.print("_");
      lcd.setCursor(7-tack,1);
      lcd.print("_");
      break;
    case 4:
      lcd.setCursor(tick+8,1);
      lcd.print("_");
      lcd.setCursor(7-tack,1);
      lcd.print("_");
      break;
    case 5:
      lcd.setCursor(tick+8,1);
      lcd.print("_");
      lcd.setCursor(7-tack,1);
      lcd.print("_");
      break; 
    case 6:
      lcd.setCursor(tick+8,1);
      lcd.print("_");
      lcd.setCursor(7-tack,1);
      lcd.print("_");
      break;
    case 7:
      lcd.setCursor(tick+8,1);
      lcd.print("_");
      lcd.setCursor(7-tack,1);
      lcd.print("_");
      break;
    case 8:
      lcd.setCursor(0,1);
      lcd.print("                ");
      break;  
    }
  }
  if((cnt==6)&&(sek==0)&&(m==0))
  {  
    run=false;
    PORTB &=~ (1<<PORTB0);
    cnt=7;
  }
  // Reset
  else if  (cnt==7)
  {   
    tick=0;
    tack=0;
    tock=0;
    seconds =0;
    run =false;
    set_sec_einer =0;
    set_sec_zehner =0;
    setsek=0;
    set_minute=0;
    sek_v_set_minute =0;
    minute =0;
    ez=0;
    sec=0;
    sek =0;
    m=0;
    delay(10);
    cnt = 0;  
  }
}
//Timer 1 ISR
ISR(TIMER1_COMPA_vect)
{
  if (run==true)
  {
    PORTB |= (1<<PORTB0);
    tick=(tick + 1  ) %9;
    tack=(tack + 1 ) %9;
    tock=(tock + 1 ) %9;
    seconds++;
  }
}