// Синтезатор до 10 МГц на модуле AD9850
// 4 канала
// Микроконтроллер - ATMega-8 16МГц
// переключение полной/половинной частоты
// версия 2.02 от 22.11.2014
// Автор - RU0AOG
#include <EEPROM.h> // подключаем модуль работы с EEPROM
// Определим выводы для управления:
#define Rx_Tx     10 // нога 16, HIGH - режим передачи
#define CHLST     9  // нога 15, LOW  - листание каналов
// Определим выводы светодиодов каналов:
#define CHAN1     6  // нога 12
#define CHAN2     7  // нога 13
#define CHAN3     8  // нога 14
#define CHAN4     11 // нога 17
// Определим выводы для управления AD9850:
#define REST      5  // нога 11
#define BitData   2  // нога 4
#define FQUP      3  // нога 5
#define CLK       4  // нога 6
// Определим переменные и массивы
  long MemFreq[6]={0,1600000,1850000,3660000,7000000,1}; // массив частот, номер канала
  long ChanNo = MemFreq[5];          // определить номер канала
  long Freq = MemFreq[ChanNo];       // определить частоту
  boolean Rx_Mode = true;            // установить режим приёма
  byte DataIn[8];                    // принятая по RS-232 команда
  int ByteNo = 0;                    // номер принимаемого байта
union MyLong {
  // определим переменную для сборки/разборки длинного целого числа на 4 байта
  long l;
  byte b[4];
} 
val;
void setup() {
  // Выполнение стартовых функций
  // Прочитать EEPROM. Если там что-то записано, то перенести данные в массив частот
  for (int j = 1; j <= 5; j++) {          // j=1...5
    Freq = 0;
    EEPROM_Read (j);                      // считывать значение частоты канала из EEPROM
    if (Freq <= 0) 
    {
    EEPROM_Write (j, MemFreq[j]);
    }
    else
    {MemFreq[j] = Freq;
    }
  }  // Если считанное из EEPROM значение частоты не равно 0, то вписать его в рабочий массив частот
  
  ChanNo = MemFreq[5];  
  Freq = MemFreq[ChanNo];                 // установить частоту
  pinMode(CHLST, INPUT_PULLUP);           // установить кнопку листания каналов на ввод и подтянуть его к плюсу
  pinMode(CHAN1, OUTPUT);
  pinMode(CHAN2, OUTPUT);
  pinMode(CHAN3, OUTPUT);
  pinMode(CHAN4, OUTPUT);
  RxTx_Init();                            // установить параметры ноги Rx/Tx
  AD9850_Init();                          // назначить выводы управления AD9850
  AD9850_Reset_Serial();                  // инициализация AD9850
  AD9850_WR_Serial(0x00, Freq);           // установить частоту AD9850
  Disp_Freq();                            // отобразить канал
  Serial.begin(9600);                     // подключить порт RS-232
  delay(50);
  RS232Ready();                           // вывести сообщение о готовности
}
void loop() {
  RxTx_Detect();                          // проверить режим приёма/передачи
  CHLST_Detect();                         // проверить листание каналов
  if (Serial.available()) {               // если в порт пришёл байт, то
    byte Byte01 = Serial.read();          // считать входящий байт
    RS232RCVDATA(Byte01);                 // обработать принятый байт
  }
}
// служебные подпрограммы
void RS232RCVDATA(byte inByte)
  // обработка полученной команды по RS-232
{
  ByteNo = ByteNo + 1;
  
  if (ByteNo > 8) {                       // если длина команды больше предельной, то
    RS232Clear();                         // очистить массив принятых байт
    Serial.println("Error in command length");   // отправить сообщение об ошибке
    goto RS232Exit;}
    DataIn[ByteNo] = inByte;              // вписываем принимаемый байт в массив команд 
    
  if (DataIn[ByteNo] == 'D' || DataIn[ByteNo] == 'd') {  // если принимаем команду 'D', то
    for (int x=1; x<5; x++){              // вывести таблицу частот каналов
    Serial.print("Channel ");
    Serial.print(x);
    Serial.print(", Frequency = ");
    Serial.println(MemFreq[x]);  
    }
    Serial.print("Current channel ");     // вывести номер текущего канала
    Serial.println(MemFreq[5]);
    RS232Clear();
    goto RS232Exit;}    
  
  if (DataIn[ByteNo] == 'C' || DataIn[ByteNo] == 'c') {  // если принимаем команду 'C', то
    RS232Clear();
    ByteNo = 1;
    DataIn[ByteNo] = 'C';
    Serial.println("Channel command accepted");              // отправить сообщение
    goto RS232Exit;}
    
  if (DataIn[ByteNo] == 'F' || DataIn[ByteNo] == 'f') {  // если принимаем команду 'F', то
    RS232Clear();
    ByteNo = 1;
    DataIn[ByteNo] = 'F';
    Serial.println("Frequency command accepted");        // отправить сообщение
    goto RS232Exit;}
    
  if (ByteNo == 2 && DataIn[1] == 'C') {  // если принимаем команду 'C', то
    ChanNo = DataIn[2]-48;                // перевести ASCII-код в число
    if (ChanNo>0 && ChanNo<5){
      Serial.print("Channel selected: ");
      Serial.println(ChanNo);             // вывести номер выбранного канала
      Freq = MemFreq[ChanNo];             // установить значение частоты
      EEPROM_Write (5, ChanNo);           // сохранить в EEPROM номер канала
      MemFreq[5] = ChanNo;
      Disp_Freq();
      AD9850_WR_Serial(0x00, Freq);       // установить частоту AD9850
      Serial.print("Frequency on channel: ");
      Serial.println(Freq);}              // вывести частоту канала
      else
      {Serial.println("Channel number can be 1..4");}   // отправить сообщение об ошибке
    RS232Clear();
    goto RS232Exit;} 
    
  if (ByteNo > 1 && DataIn[1] == 'F') {            // если принимаем команду 'F', то
    if (DataIn[ByteNo]<48 || DataIn[ByteNo]>57){   // если принятый байт - не цифра, то
    RS232Clear();                                  // очистить массив принятых байт
    Serial.println("Error in frequency data");     // отправить сообщение об ошибке
    goto RS232Exit;}
    else
    {DataIn[ByteNo] = DataIn[ByteNo]-48;}
    }
    
  if (ByteNo == 8 && DataIn[1] == 'F') {    // если приняли команду 'F', то
    //Disp_OFF();                           // отключить дисплей
    Freq = int(DataIn[2])*1000000 + int(DataIn[3])*100000 + int(DataIn[4])*10000 + int(DataIn[5])*1000 + int(DataIn[6])*100 + int(DataIn[7])*10 + int(DataIn[8]);
    AD9850_WR_Serial(0x00, Freq);           // установить частоту AD9850
    Serial.print("Frequency selected: ");   // отправить сообщение
    Serial.println(Freq);                   // отправить сообщение
    if (ChanNo>0 && ChanNo<5){
    EEPROM_Write (ChanNo, Freq);            // сохранить в EEPROM частоту канала
    MemFreq[ChanNo] = Freq;                 // установить значение частоты
    Serial.print("Frequency saved on channel: ");
    Serial.println(ChanNo);                 // вывести номер сохранённого канала
    RS232Ready();}
    RS232Clear();                           // очистить массив принятых байт
  }  
RS232Exit:
Disp_Freq();
}
void RS232Clear(){
  // очистить массив принятых байт
  for (int x=0; x<8; x++){              // очистить массив 
  DataIn[ByteNo] = 0;}
  ByteNo = 0;}                          // очистить счётчик байт
  
void RS232Ready(){
  Serial.println("Radio ready");        // отправить в порт приглашение к обмену данными
  delay(50);}
  
void CHLST_Detect() {
  // определить листание каналов
  int pin_val;
  pin_val = digitalRead(CHLST);          // считать состояние ноги CHLST
  if (pin_val == LOW) {                  // если кнопка листания каналов нажата, то
    ChanNo = ChanNo+1;                   // установить следующий номер канала
    MemFreq[5] = ChanNo;
    if (ChanNo>4) {ChanNo=1;}            // если вышли за последний канал - вернуться к первому
    EEPROM_Write (5, ChanNo);            // сохранить в EEPROM номер канала
    Freq = MemFreq[ChanNo];              // установить значение частоты
    Disp_Freq();                         // отобразить частоту канала
    AD9850_WR_Serial(0x00, Freq);        // установить частоту AD9850
    Serial.print("Channel selected: ");
    Serial.println(ChanNo);              // вывести номер выбранного канала
    delay(200);}                         // ждём 200 мс для устранения дребезга контактов
}
void RxTx_Detect() {
  // определить, нажата ли кнопка передачи
  int pin_val;
  pin_val = digitalRead(Rx_Tx);          // считать состояние ноги Rx_Tx
  if (pin_val == LOW & Rx_Mode == false) {
    Rx_Mode = true;                      // установить режим приёма
    AD9850_WR_Serial(0x00, Freq);}       // установить частоту AD9850
  if (pin_val == HIGH & Rx_Mode == true)  {
    Rx_Mode = false;                    // установить режим передачи
    AD9850_WR_Serial(0x00, Freq);}}     // установить частоту AD9850
void Disp_Freq() {
  // отобразить номер канала
  Disp_OFF();
  switch (ChanNo) {                        // включить светодиод, соответствующий каналу
  case 1: 
    digitalWrite(CHAN1,HIGH);
    break;
  case 2: 
    digitalWrite(CHAN2,HIGH); 
    break;
  case 3: 
    digitalWrite(CHAN3,HIGH); 
    break;
  case 4: 
    digitalWrite(CHAN4,HIGH); 
    break;
  }
}
void Disp_OFF() {
  // отключить дисплей
  digitalWrite(CHAN1,LOW);                // выключить все светодиоды
  digitalWrite(CHAN2,LOW);
  digitalWrite(CHAN3,LOW);
  digitalWrite(CHAN4,LOW);
}
  
void RxTx_Init() {
  // установить параметры ноги Rx/Tx
  pinMode(Rx_Tx, INPUT_PULLUP);      // определить ногу Rx_Tx как вход, подтянутый к +5В через резистор 20...50 кОм.
}
void AD9850_Init(){
  // Назначить выводы управления AD9850
  pinMode(REST, OUTPUT);
  pinMode(FQUP, OUTPUT);
  pinMode(CLK , OUTPUT);
  pinMode(BitData, OUTPUT);
  // установить выводы управления AD9850 в ноль
  digitalWrite(REST, 0);
  digitalWrite(FQUP, 0);
  digitalWrite(CLK, 0);
  digitalWrite(BitData, 0);
}
void AD9850_Reset_Serial(){
  // Инициализация AD9850
  digitalWrite(CLK, 0);
  digitalWrite(FQUP, 0);
  //Reset signal
  digitalWrite(REST, 0);
  digitalWrite(REST, 1);
  digitalWrite(REST, 0);
  //Clk  signal
  digitalWrite(CLK, 0);
  digitalWrite(CLK, 1);
  digitalWrite(CLK, 0);
  //Fq-up signal
  digitalWrite(FQUP, 0);
  digitalWrite(FQUP, 1);
  digitalWrite(FQUP, 0);
}
void AD9850_WR_Serial(unsigned char w0,double frequence) {
  // Установить частоту AD9850
  unsigned char i,w;
  long int y;
  double x;
  //вычислить значение кода частоты для опорного генератора 125 МГц
  x = 4294967295 / 125; 
  frequence = frequence / 1000000;
  frequence = frequence * x;
  if (Rx_Mode == false)   //  установить полную или половинную частоту
  {
    y = frequence;
  } 
  else 
  {
    y = frequence/2;
  }
  //отправить слово W4
  w=(y>>=0);
  for(i=0; i<8; i++) {
    digitalWrite(BitData, (w>>i)&0x01);
    digitalWrite(CLK, 1);
    digitalWrite(CLK, 0); 
  }
  //отправить слово W3
  w=(y>>8);
  for(i=0; i<8; i++) {
    digitalWrite(BitData, (w>>i)&0x01);
    digitalWrite(CLK, 1);
    digitalWrite(CLK, 0); 
  }
  //отправить слово W2
  w=(y>>16);
  for(i=0; i<8; i++) {
    digitalWrite(BitData, (w>>i)&0x01);
    digitalWrite(CLK, 1);
    digitalWrite(CLK, 0); 
  }
  //отправить слово W1
  w=(y>>24);
  for(i=0; i<8; i++) {
    digitalWrite(BitData, (w>>i)&0x01);
    digitalWrite(CLK, 1);
    digitalWrite(CLK, 0); 
  }
  //отправить слово W0
  w=w0;
  for(i=0; i<8; i++) {
    digitalWrite(BitData, (w>>i)&0x01);
    digitalWrite(CLK, 1);
    digitalWrite(CLK, 0); 
  }
  digitalWrite(FQUP, 1);
  digitalWrite(FQUP, 0);
}
void EEPROM_Write (int NomW, long Freq_W) {
  // записать частоту в ячейку EEPROM
  MyLong var;
  var.l = Freq_W;
  for (int i01 = 0; i01 <= 3; i01++){
    EEPROM.write((NomW*4) + i01, var.b[i01]);
  }
}
void EEPROM_Read (int NomR){
  // считать частоту из ячейки EEPROM
  MyLong var;
  for (int i02 = 0; i02 <= 3; i02++){
    var.b[i02] = EEPROM.read((NomR*4) + i02);
  }
  Freq = var.l;
}