// Синтезатор до 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;
}