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.- € | --- |
Für diesen Sketch wird folgende Hardware verwendet
|
|
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. |
|
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:
- Wire-Lib der IDE 0022
- LiquidCrystal-Lib der IDE 0022
- PCF8583-Lib von Eric DeBill: https://github.com/edebill/PCF8583
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.
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; }