Radiofunkuhr - Projektvorstellung (In Progress)

Willkommen zum Projekt 'RADIOFUNKUHR' !

Speziell für dieses Projekt habe ich folgende Radiouhr zur Gehäuse Gewinnung gekauft. Die alte Elektronik ist schon komplett ausgebaut !

So sieht die Schaltung zur Zeit auf dem Breadboard aus: Uploaded with ImageShack.us

Das Radiomodul

Als Radiomodul benutze ich ein modifiziertes Sparkfun SI4703 Modul: http://arduino.cc/forum/index.php/topic,112489.0.html

Die Lautstärke des Radiomoduls wird über ein logaritmisches ALPS RK16812MG 2-fach Motorpotentiometer gesteuert. Dadurch kann die Lautstärke auch mit der Infrarotfernbedienung gesteuert werden. ![](http://i.ebayimg.com/00/$(KGrHqIOKjoE24ioWTSWBNvnm+u37w~~_35.JPG)

Die Lautstärkeausgabe des Radiomoduls wird auf Maximum, als Stufe 15 eingestellt. Mit dem logaritmischen Motorpotentiometer wird dann die entgültige Lautstärke gesteuert, wodurch eine feinere Lautstärkeregelung möglich wird.

Der Motor des Motorpotentiometers wird durch eine L293NE H-Bridge gesteuert.

Die Tastatur

Das 4X4 Keypad wird durch ein 74C922 und einem PCF8574A abgefragt, über I2C.
http://arduino.cc/playground/Main/I2CPortExpanderAndDecoders

Die Infrarotfernbedienung

Bei der Infrarotfernbedienung handelt es sich um das DFRobot IR Kit: http://www.dfrobot.com/index.php?route=product/product&filter_name=ir remote&product_id=366

Infrarotfernbedienung Keycodes: An/Aus = FD00FF Lauter = FD807F Funtion Stop = FD40BF Rückwärts = FD20DF Wiedergabe = FDA05F Vorwärts = FD609F Runter = FD10EF Leiser = FD906F Hoch = FD50AF 0 = FD30CF EQ = FDB04F ST = FD708F 1 = FD08F7 2 = FD8877 3 = FD48B7 4 = FD28D7 5 = FDA857 6 = FD6897 7 = FD18E7 8 = FD9867 9 = FD58A7

Die 7-Segmentanzeige

Diese Anzeige wird über einen zweiten Atmega328 multiplext, er liest die aktuelle Uhrzeit aus der RTC und stellt diese auf der 7-Segmentanzeige dar.

Diese Teilschaltung besteht aus einem Atmega328, einem ULN2803 und einer 4 stelligen 7-Segmentanzeige.

Als 7-Segmentanzeige wird ein Lite-On LTC-5653HR mit roten Segmenten benutzt.
!(http://i.ebayimg.com/00/s/NzY4WDEwMjQ=/$(KGrHqV,!pUE7BcvinrzBP2hotW!9!~~60_35.JPG)

#include <WProgram.h>
#include <Wire.h>
#include <DS1307.h> 

byte Digit_Nr;
byte Digit_PinNr[5];
byte Zeichen_Segmente[10][8];
byte ZeichenNr[4];
byte Stunden;
byte Minuten;
byte Wiederholung;

void setup() {
  Digit_PinNr[1] = 5;
  Digit_PinNr[2] = 6;  
  Digit_PinNr[3] = 9;  
  Digit_PinNr[4] = 10;
  // NULL Segmente
  Zeichen_Segmente[0][0] = HIGH;
  Zeichen_Segmente[0][1] = HIGH;  
  Zeichen_Segmente[0][2] = HIGH;  
  Zeichen_Segmente[0][3] = HIGH;  
  Zeichen_Segmente[0][4] = HIGH;  
  Zeichen_Segmente[0][5] = HIGH;  
  Zeichen_Segmente[0][6] = LOW;
  Zeichen_Segmente[0][7] = LOW;
  // EINS Segmente
  Zeichen_Segmente[1][0] = LOW;
  Zeichen_Segmente[1][1] = HIGH;  
  Zeichen_Segmente[1][2] = HIGH;  
  Zeichen_Segmente[1][3] = LOW;  
  Zeichen_Segmente[1][4] = LOW;  
  Zeichen_Segmente[1][5] = LOW;  
  Zeichen_Segmente[1][6] = LOW;
  Zeichen_Segmente[1][7] = LOW;
  // ZWEI Segmente
  Zeichen_Segmente[2][0] = HIGH;
  Zeichen_Segmente[2][1] = HIGH;  
  Zeichen_Segmente[2][2] = LOW;  
  Zeichen_Segmente[2][3] = HIGH;  
  Zeichen_Segmente[2][4] = HIGH;  
  Zeichen_Segmente[2][5] = LOW;  
  Zeichen_Segmente[2][6] = HIGH;
  Zeichen_Segmente[2][7] = LOW;
  // DREI Segmente
  Zeichen_Segmente[3][0] = HIGH;
  Zeichen_Segmente[3][1] = HIGH;  
  Zeichen_Segmente[3][2] = HIGH;  
  Zeichen_Segmente[3][3] = HIGH;  
  Zeichen_Segmente[3][4] = LOW;  
  Zeichen_Segmente[3][5] = LOW;  
  Zeichen_Segmente[3][6] = HIGH;
  Zeichen_Segmente[3][7] = LOW;
  // VIER Segmente
  Zeichen_Segmente[4][0] = LOW;
  Zeichen_Segmente[4][1] = HIGH;  
  Zeichen_Segmente[4][2] = HIGH;  
  Zeichen_Segmente[4][3] = LOW;  
  Zeichen_Segmente[4][4] = LOW;  
  Zeichen_Segmente[4][5] = HIGH;  
  Zeichen_Segmente[4][6] = HIGH;
  Zeichen_Segmente[4][7] = LOW;
  // FÜNF Segmente
  Zeichen_Segmente[5][0] = HIGH;
  Zeichen_Segmente[5][1] = LOW;  
  Zeichen_Segmente[5][2] = HIGH;  
  Zeichen_Segmente[5][3] = HIGH;  
  Zeichen_Segmente[5][4] = LOW;  
  Zeichen_Segmente[5][5] = HIGH;  
  Zeichen_Segmente[5][6] = HIGH;
  Zeichen_Segmente[5][7] = LOW;
  // SECHS Segmente
  Zeichen_Segmente[6][0] = HIGH;
  Zeichen_Segmente[6][1] = LOW;  
  Zeichen_Segmente[6][2] = HIGH;  
  Zeichen_Segmente[6][3] = HIGH;  
  Zeichen_Segmente[6][4] = HIGH;  
  Zeichen_Segmente[6][5] = HIGH;  
  Zeichen_Segmente[6][6] = HIGH;
  Zeichen_Segmente[6][7] = LOW;
  // SIEBEN Segmente
  Zeichen_Segmente[7][0] = HIGH;
  Zeichen_Segmente[7][1] = HIGH;  
  Zeichen_Segmente[7][2] = HIGH;  
  Zeichen_Segmente[7][3] = LOW;  
  Zeichen_Segmente[7][4] = LOW;  
  Zeichen_Segmente[7][5] = LOW;  
  Zeichen_Segmente[7][6] = LOW;
  Zeichen_Segmente[7][7] = LOW;
  // ACHT Segmente
  Zeichen_Segmente[8][0] = HIGH;
  Zeichen_Segmente[8][1] = HIGH;  
  Zeichen_Segmente[8][2] = HIGH;  
  Zeichen_Segmente[8][3] = HIGH;  
  Zeichen_Segmente[8][4] = HIGH;  
  Zeichen_Segmente[8][5] = HIGH;  
  Zeichen_Segmente[8][6] = HIGH;
  Zeichen_Segmente[8][7] = LOW;
  // NEUN Segmente
  Zeichen_Segmente[9][0] = HIGH;
  Zeichen_Segmente[9][1] = HIGH;  
  Zeichen_Segmente[9][2] = HIGH;  
  Zeichen_Segmente[9][3] = HIGH;  
  Zeichen_Segmente[9][4] = LOW;  
  Zeichen_Segmente[9][5] = HIGH;  
  Zeichen_Segmente[9][6] = HIGH;
  Zeichen_Segmente[9][7] = LOW;
  pinMode(2,OUTPUT); 
  pinMode(3,OUTPUT); 
  pinMode(4,OUTPUT);
  pinMode(7,OUTPUT); 
  pinMode(8,OUTPUT); 
  pinMode(11,OUTPUT);  
  pinMode(12,OUTPUT); 
  pinMode(13,OUTPUT);   
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);  
  pinMode(9,OUTPUT);  
  pinMode(10,OUTPUT);
  digitalWrite(5,LOW);
  digitalWrite(6,LOW);  
  digitalWrite(9,LOW);
  digitalWrite(10,LOW);
  Digit_Nr = 0;
  ZeichenNr[0] = 0;
  ZeichenNr[1] = 0;  
  ZeichenNr[2] = 0;  
  ZeichenNr[3] = 0;
  Stunden = 0;
  Minuten = 0;
}

void loop()
{
  Stunden = RTC.get(DS1307_HR,true);
  Minuten = RTC.get(DS1307_MIN,false);
  ZeichenNr[1] = Stunden / 10;
  ZeichenNr[2] = Stunden % 10;
  ZeichenNr[3] = Minuten / 10;
  ZeichenNr[4] = Minuten % 10;
  for (Wiederholung = 1; Wiederholung <= 70; Wiederholung ++)
  {
    for (Digit_Nr = 1; Digit_Nr <= 4; Digit_Nr ++)
    {
      if (Digit_Nr >= 5) 
        Digit_Nr = 1;
      if (Digit_Nr == 2 || Digit_Nr == 3)
      {
        Zeichen_Segmente[ZeichenNr[Digit_Nr]][7] = HIGH;
      }
        else 
        {
          Zeichen_Segmente[ZeichenNr[Digit_Nr]][7] = LOW;
        }
      digitalWrite(2,Zeichen_Segmente[ZeichenNr[Digit_Nr]][0]);
      digitalWrite(3,Zeichen_Segmente[ZeichenNr[Digit_Nr]][1]);    
      digitalWrite(4,Zeichen_Segmente[ZeichenNr[Digit_Nr]][2]);    
      digitalWrite(7,Zeichen_Segmente[ZeichenNr[Digit_Nr]][3]);
      digitalWrite(8,Zeichen_Segmente[ZeichenNr[Digit_Nr]][4]);    
      digitalWrite(11,Zeichen_Segmente[ZeichenNr[Digit_Nr]][5]);    
      digitalWrite(12,Zeichen_Segmente[ZeichenNr[Digit_Nr]][6]);  
      digitalWrite(13,Zeichen_Segmente[ZeichenNr[Digit_Nr]][7]);
      digitalWrite(Digit_PinNr[Digit_Nr], HIGH);
      delay(5);
      digitalWrite(Digit_PinNr[Digit_Nr], LOW);
      delay(1);
    }
  }
}

Das LCD-Display

Das WM-C1602K HD44780 LCD-Display wird über das 3-Wire Interface gesteuert, dazu wird ein HEF4094 benutzt. Es besitzt 2 Zeilen mit jeweils 20 Zeichen, blaue Hintergrundbeleuchtung und eine weisse Schrift. http://arduino.cc/playground/Code/LCD3wires

Die Uhr - RTC DS1337 & DCF77

Dies ist die Uhr des Radios die Real Time Clock DS1337, sie ist in dieser Schaltung batteriegepuffert. Sie besitzt den Vorteil das in Ihr zwei Alarmzeiten gespeichert werden können. Die RTC benötigt ein 32.768KHz Quarz mit einer Lastkapazität von 6pF. Ich benutze den MS1V-6 Uhrenquarz, dessen Gehäuse habe ich mit der Masse verbunden um äußere Störungen abzuschirmen.

Der erste Atmega328, in dem das Hauptprogramm läuft, stellt die Uhrzeit und die Alarmzeiten in der RTC über I2C. Er überwacht auch den Alarmpin der RTC. Der zweite Atmega328 der Schaltung liest die Uhrzeit aus der RTC und stellt diese auf der 7-Segmentanzeige an.

Anschluss des DCF77 Moduls von Pollin an den Arduino:

DCF77:

Logic Level Converter (Pegelwandler) Sparkfun:

Stellen der Uhrzeit

Das Stellen der Uhrzeit kann manuel oder durch das DCF77 Empfangmodul gestellt werden.

Im Falle des automatischen stellens wird der Vorgang durch die Taste ‘*’ (Tastatur) oder ‘EQ’ auf der Fernbedienung gestartet. Sobald die korrekte Uhrzeit durch analyse des Funkuhrsignals ermittelt wurde wird die Uhrzeit aktualisiert.

Im Falle des manuellen stellens steht auf dem LCD-Display:
Bitte Uhrzeit eingeben:
HH:MM:SS

Stunden, Minuten und Sekunden werden immer als zweier Zahlenblock eingegeben, also 06 im Falle einer 6.

Beim Eintippen der Uhrzeit verändert sich die Darstellung folgendermassen:
HH:MM:SS
12:MM:SS
12:1M:SS
12:15:00

Nach dem Eingeben der letzen Ziffer wird um Bestätigung gebetten, im Falle dieser wird die Uhrzeit aktualisiert. Ansonsten wird die Eingabe verworfen !

Die Uhrzeit kann sowohl über die Tastatur als auch über die Fernbedienung eingegeben werden.

Stellen der Alarmzeit, die Alarmfunktion

Kommt noch !

Das EEPROM

Ein 24LC256 32KByte EEPROM.

In Ihm werden die Frequenzen der Senderstationen gespeichert die man mit der Schnellauswahl auswählen können möchte.

Die Steuerung der Motorenpotentiometer Motoren

Der Motor des Potentiometers wird durch eine L293NE H-Bridge gesteuert.

Das Hauptprogramm

Noch nicht komplett !

// Infrarotsensor -> Arduino       -> Atmega328
// --------------------------------------------
// Signal         -> digital Pin 7 -> Pin 13

// PCF8574 -> Arduino        -> Atmega328
// --------------------------------------
// Pin 16  -> 5V             -> 5V Vcc
// Pin 8   -> GND            -> GND
// Pin 15  -> Analog 4 (SDA) -> Pin 27
// Pin 14  -> Analog 5 (SCL) -> Pin 28

// HEF4094 -> Arduino    -> Atmega328
// ----------------------------------
// Pin 1   -> digital 12 -> Pin 18
// Pin 2   -> digital 11 -> Pin 17
// Pin 3   -> digital 1o -> Pin 16

// DCF77   -> Arduino       -> Atmega328
// -------------------------------------
// Data    -> digital Pin 9 -> Pin 15
// Pon     -> digital Pin 8 -> Pin 14 (Bei 5V über einen Voltage Divider auf 3,3V)

#include <IRremote.h>
#include <Wire.h>
#include <i2cdeckpd.h>
#include <LCD3Wire.h> 
#include <DCF77.h>
#include <DS1337.h>
#include <avr/power.h>
#include <avr/sleep.h>

#define KBDADDR (0x70 >> 1)    // PCF8574A I2C Adresse 0x70 1 Bit nach rechts geshiftet
#define InfraredReceiverPin  7 // Signal des Infratorempfängers
#define LCDLinesNumbers      2 // number of lines in your display
#define HEF4094_DOUT_PIN    11 // Dout pin, 3-Wire LCD Steuerung
#define HEF4094_STR_PIN     12 // Strobe pin, 3-Wire LCD Steuerung
#define HEF4094_CLK_PIN     10 // Clock pin, 3-Wire LCD Steeurung
#define DCF77DataPin         9 // DCF1 DATA
#define DCF77PonPin          8 // DCF77 Modul An/Aus
#define LedBlinkPin         13 // Led

char KeyChar;
int Key;
unsigned int Infrared_Value;
int DCF77_Seconds=0;
int DCF77_PreviousSecond=0;
int DCF77_Minutes=0;
int DCF77_Hours=0;
int DCF77_Month=0;
int DCF77_Day=0;
int DCF77_Year=0;
byte DCF77_DayofWeek=0;
String AktuellerLCDText1; // Aktueller LCD Text Zeile 1
String AktuellerLCDText2; // Aktueller LCD Text Zeile 2
String DepoLCDText1;      // Zwischengespeicherter LCD Text Zeile 1
String DepoLCDText2;      // Zwischengespeicherter LCD Text Zeile 2

IRrecv irrecv(InfraredReceiverPin);
decode_results results;
I2CDecodedKeypad kpd(KBDADDR, LedBlinkPin);
DCF77 myDCF=DCF77(DCF77DataPin);

LCD3Wire lcd = LCD3Wire(LCDLinesNumbers, HEF4094_DOUT_PIN, HEF4094_STR_PIN, HEF4094_CLK_PIN); 
DS1337 RTC = DS1337();

void setup()
{
  Wire.begin();
  kpd.init();
  RTC.start();
  Serial.begin(9600);
  irrecv.enableIRIn(); // Start the receiver
  lcd.init();
  pinMode(DCF77PonPin, OUTPUT); // DCF1 PON - Empfänger an/aus
  pinMode(LedBlinkPin, OUTPUT);
  digitalWrite(DCF77PonPin, LOW); // DCF1 Empfänger anschalten
}

void loop()
{
  KeyChar = ' ';
  Infrared_Value = 0;
  Key = kpd.getKeyStroke();
  KeyChar = Key;
  if (Key > 0)
  {
    /*    Serial.print("KeyNr: ");
     Serial.print(Key);
     Serial.print("   Keychar: ");
     Serial.println(KeyChar);*/
  }
  if (irrecv.decode(&results)) 
  {
    if (results.value != 0xffffffff)
    {
      Infrared_Value = results.value;
      /*      Serial.print("infrarot: ");
       Serial.println(Infrared_Value);*/
    }
    irrecv.resume(); // Receive the next value
  }
  if (KeyChar == '*' || Infrared_Value == 45135)
  {
    RTC_durch_DCF77_stellen();
  }
}

void RTC_durch_DCF77_stellen()
{
  DCF77_Hours = 0;
  DCF77_Minutes = 0;
  DCF77_Seconds = 0;
  myDCF.hh = 0;
  myDCF.mm = 0;
  myDCF.ss = 0;
  lcd.cursorTo(2, 0);
  AktuellerLCDText2 = "DCF77 Zeitempfang !";
  lcd.print(AktuellerLCDText2);
  do
  {
    int DCFsignal = myDCF.scanSignal();
    if (DCFsignal) 
    {
      digitalWrite(LedBlinkPin, HIGH);
    } 
      else 
      {
        digitalWrite(LedBlinkPin, LOW);
      }
    DCF77_Hours = myDCF.hh;
    DCF77_Minutes = myDCF.mm;
    DCF77_Seconds = myDCF.ss;
    DCF77_Day = myDCF.day;
    DCF77_Month = myDCF.mon;
    DCF77_Year = myDCF.year;
    DCF77_DayofWeek = myDCF.wkd;
    DCF77_PreviousSecond = DCF77_Seconds;
    lcd.cursorTo(1, 0);
    AktuellerLCDText1 = String(DCF77_Hours) + ":" + String(DCF77_Minutes) + ":" + String(DCF77_Seconds) + "   ";
    lcd.print(AktuellerLCDText1);
    delay(55);
  } 
  while((DCF77_Hours == 0));
  lcd.cursorTo(1, 0);
  lcd.print("RTC aktualisiert !");
  lcd.cursorTo(2, 0);
  AktuellerLCDText2 = "DCF77 Fertig !     ";
  lcd.print(AktuellerLCDText2);
  RTC.setSeconds(DCF77_Seconds);     // Stellen der RTC Zeit durch die ermittelte DCF77 Zeit
  RTC.setMinutes(DCF77_Minutes);     // Stellen der RTC Zeit durch die ermittelte DCF77 Zeit
  RTC.setHours(DCF77_Hours);         // Stellen der RTC Zeit durch die ermittelte DCF77 Zeit
  RTC.setDays(DCF77_Day);            // Stellen der RTC Zeit durch die ermittelte DCF77 Zeit
  RTC.setMonths(DCF77_Month);        // Stellen der RTC Zeit durch die ermittelte DCF77 Zeit
  RTC.setYears(DCF77_Year);          // Stellen der RTC Zeit durch die ermittelte DCF77 Zeit
  RTC.setDayOfWeek(DCF77_DayofWeek); // Stellen der RTC Zeit durch die ermittelte DCF77 Zeit
  RTC.writeTime();                   // Schreiben der neuen Daten in die RTC
  digitalWrite(LedBlinkPin, LOW);
  delay(5000);
  lcd.clear();
}

Bisher wurde die Tastaturabfrage, die Infrarotfernbedienungabfrage und das Stellen der Uhr über DCF77 implementiert.

Bravo!!!!

Danke !

Hallo,

Megaionstorm: Die Steuerung der Motorenpotentiometer Motoren

Kommt noch !

Die Lautstärke des Radiomoduls wird über ein lineares ALPS RK16812MG 10kOhm Motorpotentiometer gesteuert. Dadurch kann die Lautstärke auch mit der Infrarotfernbedienung gesteuert werden.

Die Lautstärke wird mit dem Potentiometer jedoch indirekt gesteuert. Die Werte 0 ... 1023 werden auf die 16 Lautstärken welche das Radiomodul hat umgerechnet.

schreibe doch bitte mal, wie Du Dir die Verschaltung des Potis gedacht hast und warum Du überhaupt eines brauchst.

Wenn Du den Widerstandswert umrechnen möchtest, kannst Du doch auch auf das Poti verzichten. Statt der Umrechnung könntest Du das Ergebniss direkt mit der Fernbedienung beeinflussen.

Wenn Du dann noch für der 'Reset-Fall' einen Wert initialisierst, bei dem einem nicht die Ohren abfallen ist doch alles getan.

Gruss Kurti

schreibe doch bitte mal, wie Du Dir die Verschaltung des Potis gedacht hast und warum Du überhaupt eines brauchst.

Nur zu gerne !

5V an der einen Seite, GND an der anderen und der Schleifer auf einen analog Pin.

Demzufolge erhalte ich einen Wert zwischen 0 und 1023. Diesen rechne ich dann mit der MAP Funktion auf einen Wert zwischen 0 und 15 um.

Dieser Wert wird dann an das Radiomodul übertragen. Lautstärke eingestellt.

Wozu brauche ich ein Poti ? Damit ich die Lautstärke auch ohne Fernsteuerung regeln kann.
Wozu hat das Poti einen Motor ? Damit es in die richtige Stellung gefahren wird wenn ich die Lautstärke über die Fernbedienung einstellen will.

Also ich kenne das so, das für Lautstärkeregelungen normalerweise logarithmische potis genommen werden, weil das höhrempfinden ja mit dem logarithmus der leistung und nicht linear mit der leistung sinkt... dementsprechend solltest du vielleicht darüber nachdenken das nicht per map() zu machen, da diese linear skaliert...

@derder: Da kann ich dir eigentlich auch nur voll und ganz recht geben, aber ...

Der Si4703 besitzt eine interne Lautstärkeregelung, diese besitzt eine logarithmische Lautstärkeskalierung.

Deshalb der von mir gewählte Weg !

Hallo, an den Handbetrieb hatte ich nicht gedacht. Ich denke, ich hätte die Lautstärke per Tasten verstellbar gemacht. So ein Motor-Poti ist oft nicht ganz billig.

Alternative: beim Boseradio wird die Fernbedienung in einer Mulde im Gehäuse aufbewahrt. Die Fernbedienung dient damit auch zur Bedienung am Gerät. Notfalls könnte man zwei identische Fernbedienungen verwenden. Eine wird am Gehäuse befestigt und dient zur 'Handbedienung', die zweite dient als echte Fernbedienung.

Gruss Kurti

So ein Motor-Poti ist oft nicht ganz billig.

Ist das billigste Alps Motorpoti von allen, die anderen die ich sah sind schon mindestens doppelt so teuer !