Bei der Messwerterfassung müssen häufig Zählimpulse bearbeitet werden. Da Zählimpulse von Gas- und Stromzählern zu unerwarteten Zeitpunkten auftreten, ist das häufige Abfragen des Ports (Polling) schwierig. Für Polling geht viel CPU-Zeit verloren und eventuell wird sogar ein Impuls verpasst. Hier bietet sich die Impulserfassung per Interrupt an. Sobald ein Zählimpuls auftritt, wird ein Interrupt ausgelöst und bearbeitet (z.B. eine Zählvariable erhöhen).

Hardware

Hersteller/Produkt Preis Link
Arduino UNO 26,20 € Link
FlamingoEDA Sensor Shield V5 8.- € Link
DFRobots LCD Keypad Shield
(Chinesische Anbieter)
14,50 € Ebay
CCTools PCF-RTC-EEPROM
Nr. 1084
ab 13.- €
Bausatz
Link
Breadboard mit zwei Tastern ca. 5.- € ---

 

UNO mit LCD-Shield

 

 

 

Für diesen Sketch wird folgende Hardware verwendet

  • LCD-Keypad Shield (oben)
  • Sensor-Shield (mittig)
  • Arduino UNO (unten)
RTC-Modul an I2C-Bus

 

 

 

An der I2C-Buchse wird das PCF8583-Uhrenmodul von CCTools angeschlossen. Es liefert Datum und Uhrzeit. Nähere Informationen sind in diesem Artikel zu finden.

Breadboard

 

 

 

Auf einem Breadboard werden zwei Taster gesteckt und über dreipolige Kabel mit dem Sensor-Shield verbunden. Die Taster simulieren die Impulsquelle (Stromzähler etc.). Die Taster benötigen keine Pullup-Widerstände, die internen Pullups des ATmega werden verwendet.

Libraries

Folgende Bibliotheken werden benötigt:

Sketch

Die RTC wird jede Sekunde abgefragt und auf dem Display Datum und Uhrzeit dargestellt. Die beiden Taster werden mit den Eingängen Digital Pin 2 und 3 angeschlossen und lösen bei einer fallenden Flanke einen Interupt aus. Die Zählvariablen der beiden Taster werden ebenfalls permanent auf der LCD ausgegeben. Zusätzlich werden die Taster des LCD-Shields abgefragt und bei Tastendruck auf dem LCD dargestellt. Der Taster "SELECT" dient zum Ein-/Ausschalten der LCD-Hintergrundbeleuchtung.

Die beiden Interrupt Serviceroutinen der Taster haben als Besonderheit eine eingebaute Entprellung (Variable bounceTime, hier 150ms). Die Entprellzeit kann über die Variable jederzeit angepasst werden. Hat man bereits entprellte Eingangsimpulse, kann die entsprechende Abfrage im Sketch auch entfernt werden.

LCD-Anzeige In der ersten Zeile wird das aktuelle Datum angezeigt. In der zweiten Zeile ganz links wird die Uhrzeit ausgegeben. Daneben sind die beiden dreistelligen Zähler ("023" und "018") der Taster zu sehen.

 

#include <Wire.h>
#include <PCF8583.h>
#include <LiquidCrystal.h>

/***************************************************************************** 
 * Sketch: PCF8583_LCD_IRQ.pde
 * Author: A. Kriwanek
 * Version: 1.0  01.04.2011/14:30
 *
 * Arduino analog input 5 - I2C SCL (PCF8583 pin 6)
 * Arduino analog input 4 - I2C SDA (PCF8583 pin 5)
 * 2x Pull Up Resistors 4k7: SCL->5V and SDA->5V
 * 1x CCTools Module PCF-RTC-EEPROM Nr. 1084 (www.cctools.eu)
 * Switch 0 between Digital Pin 2 and Gnd
 * Switch 1 between Digital Pin 3 and Gnd
 *
 * This sketch controls a I2C Real Time Clock based on PCF8583. The RTC can be set
 * by a command via the RS223 interface. Every second the RTC prints the actual
 * date and time on the serial port and on the LCD interface. The serial monitor
 * from the IDE can be used to monitor and set the clock. The LCD backlight can be
 * switched on/off by pressing button "SELECT". Pressing the Switch increments
 * the counter by an interrupt. The counters will be displayed on LCD.
 *
 * You can set the RTC by sending YYMMddhhmmss;
 * the semicolon on the end tells it you're done...
 *
 *
 * This sketch is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 *****************************************************************************/
// RTC values:
int correct_address = 0;
PCF8583 rtc (0xA0);              // Make instance with I2C address of RTC

// Select the pins used on the LCD panel:
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// Define some values used by the panel and buttons:
int lcd_key     = 0;
int lcd_key_prv = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

unsigned long prev_time;          // Last display cycle
int lcd_bl               = 1;     // State of LCD backlight

volatile int counter0    = 0;     // Incremented by switch0
volatile int counter1    = 0;     // Incremented by switch1
volatile int bounceTime  = 150;   // Switch bouncing time in milliseconds
volatile unsigned long last_irq0=millis();
volatile unsigned long last_irq1=millis();

//----------------------------------------------------------------------------------------------------------
// Setup and Main:

void setup(void)
{
  pinMode(2, INPUT);             // Pin2 is input
  digitalWrite(2, HIGH);         // Switch on pull up
  pinMode(3, INPUT);             // Pin3 is input
  digitalWrite(3, HIGH);         // Switch on pull up
  pinMode(10, OUTPUT);           // Backlight control on pin 10
  digitalWrite(10, HIGH);        // Switch on backlight
  lcd.begin(16, 2);              // Start the LCD library for 2x16
  Serial.begin(9600);            // Start serial interface with 9600 Baud
  Serial.print("booting...");
  Serial.println(" done");
  prev_time=millis();
  attachInterrupt(0, irq_Svc0, FALLING);
  attachInterrupt(1, irq_Svc1, FALLING);
}


void loop(void){
  if(Serial.available() > 0){    // Set RTC after receiving serial characters:
    set_RTC();
  }
  if((millis()-prev_time)>999){  // Display date and time every second:
    display_RTC();               // Display date, time and counters on LCD
    prev_time=millis();
  }
  display_Buttons();             // Read and display Buttons on LCD
  display_Counter();             // Displays counter 0 and 1 on LCD
}

//----------------------------------------------------------------------------------------------------------
// Subs and Functions:
void irq_Svc0(){
  // Interrupt 0 Service Routine (Switch 0)
  unsigned long act_irq0;
  act_irq0=millis();
  if(act_irq0 - last_irq0 > bounceTime){
    // Valid state change
    counter0++;
    if(counter0>255) counter0 = 0;
    last_irq0=act_irq0;
  }
}

void irq_Svc1(){
  // Interrupt 1 Service Routine (Switch 1)
  unsigned long act_irq1;
  act_irq1=millis();
  if(act_irq1 - last_irq1 > bounceTime){
    // Valid state change
    counter1++;
    if(counter1>255) counter1 = 0;
    last_irq1=act_irq1;
  }
}

void set_RTC(){
  // Read received characters, build values and set RTC:
  rtc.year= (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48)) + 2000;
  rtc.month = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  rtc.day = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  rtc.hour  = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  rtc.minute = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  rtc.second = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result. 
  if(Serial.read() == ';')
  {
    Serial.println("setting date");
    rtc.set_time();
  }  
}

void display_RTC(){
  // Read RTC and display date and time on LCD an send via serial interface:
  rtc.get_time();
  char datetime[21];
  sprintf(datetime, "%02d/%02d/%02d %02d:%02d:%02d",
  rtc.day, rtc.month, rtc.year, rtc.hour, rtc.minute, rtc.second);
  Serial.println(datetime);
  sprintf(datetime, "%02d.%02d.%02d", rtc.day, rtc.month, rtc.year);
  lcd.setCursor(0,0);            // move to the begining of the first line
  lcd.print(datetime);
  sprintf(datetime, "%02d:%02d:%02d", rtc.hour, rtc.minute, rtc.second);
  lcd.setCursor(0,1);            // move to the begining of the second line
  lcd.print(datetime);
  lcd.setCursor(9,1); 
  sprintf(datetime, "%03d", counter0);
  lcd.print(datetime);
  lcd.setCursor(13,1); 
  sprintf(datetime, "%03d", counter1);  
  lcd.print(datetime);
}

void display_Counter(){
  char Cnt[4];
  lcd.setCursor(9,1); 
  sprintf(Cnt, "%03d", counter0);
  lcd.print(Cnt);
  lcd.setCursor(13,1); 
  sprintf(Cnt, "%03d", counter1);  
  lcd.print(Cnt);  
}

int read_LCD_buttons(){
  // Read the button state:
  adc_key_in = analogRead(0);      // read the value from the sensor
  // my buttons when read are centered at these valies: 0, 144, 329, 504, 741
  // we add approx 50 to those values and check to see if we are close
  if (adc_key_in > 1000) return btnNONE;
  if (adc_key_in < 50)   return btnRIGHT; 
  if (adc_key_in < 195)  return btnUP;
  if (adc_key_in < 380)  return btnDOWN;
  if (adc_key_in < 555)  return btnLEFT;
  if (adc_key_in < 790)  return btnSELECT;  
  return btnNONE;  // when all others fail, return this...
}

void display_Buttons(){
  lcd.setCursor(11,0);           // move to the begining of the second line
  lcd_key = read_LCD_buttons();  // read the buttons
  if(lcd_key!=lcd_key_prv){
    switch (lcd_key)               // depending on which button was pushed, we perform an action
    {
    case btnRIGHT:
      {
        lcd.print("RIGHT");
        break;
      }
    case btnLEFT:
      {
        lcd.print(" LEFT");
        break;
      }
    case btnUP:
      {
        lcd.print(" UP");
        break;
      }
    case btnDOWN:
      {
        lcd.print(" DOWN");
        break;
      }
    case btnSELECT:
      {
        // Toggle LCD backlight:
        if(lcd_bl==1){
          lcd.print(" OK-0");
          digitalWrite(10, LOW);
          lcd_bl=0;
        } 
        else {
          lcd.print(" OK-1");
          digitalWrite(10, HIGH);
          lcd_bl=1;
        }
        delay(150);
        break;
      }
    case btnNONE:
      {
        lcd.print(" ");
        break;
      }
    } 
  }
  lcd_key_prv=lcd_key; 
}