Linduino  1.3.0
Linear Technology Arduino-Compatible Demonstration Board
Synth.ino
Go to the documentation of this file.
1 /*
2 LTC Synthesizer Demo
3 
4 Copyright 2018(c) Analog Devices, Inc.
5 
6 All rights reserved.
7 
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10  - Redistributions of source code must retain the above copyright
11  notice, this list of conditions and the following disclaimer.
12  - Redistributions in binary form must reproduce the above copyright
13  notice, this list of conditions and the following disclaimer in
14  the documentation and/or other materials provided with the
15  distribution.
16  - Neither the name of Analog Devices, Inc. nor the names of its
17  contributors may be used to endorse or promote products derived
18  from this software without specific prior written permission.
19  - The use of this software may or may not infringe the patent rights
20  of one or more patent holders. This license does not release you
21  from the requirement that you obtain separate licenses from these
22  patent holders to use this software.
23  - Use of the software either in source or binary form, must be run
24  on or directly connected to an Analog Devices Inc. component.
25 
26 THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
27 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
28 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
30 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
32 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37 
38 #include <Arduino.h>
39 #include <stdint.h>
40 #include "Linduino.h"
41 #include "LT_SPI.h"
42 #include "UserInterface.h"
43 #include "LT_I2C.h"
44 #include "QuikEval_EEPROM.h"
45 #include "LTC6946.h"
46 #include "math.h"
47 #include <LiquidCrystal.h>
48 #include <EEPROM.h>
49 #include <SPI.h>
50 #include <Wire.h>
51 
52 const int16_t RS = 8;
53 const int16_t E = 9;
54 const int16_t D4 = 4;
55 const int16_t D5 = 5;
56 const int16_t D6 = 6;
57 const int16_t D7 = 7;
58 const int16_t BUTTON_PIN = 0;
59 const int8_t UP = 1;
60 const int8_t DOWN = 2;
61 const int8_t LEFT = 3;
62 const int8_t RIGHT = 4;
63 const int8_t SELECT = 5;
64 
65 uint8_t REG[12]; //!< Register values to be written to read from
66 int8_t demo_board_connected; //!< Demo Board Name stored in QuikEval EEPROM
67 char demo_name[] = "DC1705"; //!< Demo Board Name stored in QuikEval EEPROM
68 
69 int16_t pos = 5;
70 int8_t MHz[6] = {0, 0, 0, 0, 0, 1};
71 
72 // initialize the library with the numbers of the interface pins
73 LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
74 
75 void setup()
76 {
77  quikeval_SPI_init(); //! Configure the spi port for 4MHz SCK
78  quikeval_SPI_connect(); //! Connect SPI to main data port
79  quikeval_I2C_init(); //! Configure the EEPROM I2C port for 100kHz
80 
81  demo_board_connected = discover_demo_board(demo_name); //! Checks if correct demo board is connected.
82 
83  lcd.begin(16, 2);
84 
85  lcd.print("LTC Synthesizer");
86  lcd.setCursor(0, 1);
87  lcd.print("V 1.0");
88  delay(1000);
89  lcd.clear();
90 
92  {
93  lcd.print("No Demo Board");
94  lcd.setCursor(0, 1);
95  lcd.print(" Detected");
96  while (1); //! Does nothing if the demo board is not connected
97  }
98 
99  lcd.print("Found The");
100  lcd.setCursor(0, 1);
101  lcd.print(demo_board.product_name);
102  delay(1000);
103  lcd.clear();
104 
106  lcd.clear();
107 }
108 
109 void loop()
110 {
111  int8_t in;
112 
113  in = read_switches();
114 
115  //Display Frequency
116  lcd.setCursor(0, 0);
117  lcd.print("Freq:");
118  lcd.print(MHz[5]);
119  lcd.print(MHz[4]);
120  lcd.print(MHz[3]);
121  lcd.print(MHz[2]);
122  lcd.print(".");
123  lcd.print(MHz[1]);
124  lcd.print(MHz[0]);
125  lcd.print("MHz");
126 
127  //Controls position
128  if (in == RIGHT)
129  pos -= 1;
130  if (in == LEFT)
131  pos += 1;
132  if (pos > 5)
133  pos = 0;
134  if (pos < 0)
135  pos = 5;
136 
137  //Sets cursor to correct location
138  lcd.cursor();
139  if (pos == 0)
140  lcd.setCursor(11, 0);
141  if (pos == 1)
142  lcd.setCursor(10, 0);
143  if (pos == 2)
144  lcd.setCursor(8, 0);
145  if (pos == 3)
146  lcd.setCursor(7, 0);
147  if (pos == 4)
148  lcd.setCursor(6, 0);
149  if (pos == 5)
150  lcd.setCursor(5, 0);
151 
152  // Frequency Out Array Control
153  if (in == UP)
154  {
155  MHz[pos] += 1;
156  lcd.noCursor();
157  lcd.clear();
158  }
159  if (in == DOWN)
160  {
161  MHz[pos] -= 1;
162  lcd.noCursor();
163  lcd.clear();
164  }
165  if (MHz[pos] < 0)
166  MHz[pos] = 9;
167  if (MHz[pos] > 9)
168  MHz[pos] = 0;
169 
170  //Designes the loop when select is pressed
171  if (in == SELECT)
172  {
173  loop_design();
174  lcd.clear();
175  }
176  delay(250);
177 }
178 
179 //Reads the Switches
180 //Returns the selected button
182 {
183  int16_t input = 0;
184 
185  input = analogRead(BUTTON_PIN);
186  if ((input <= 170) && (input >= 120))
187  return UP;
188  if ((input <= 350) && (input >= 300))
189  return DOWN;
190  if ((input <= 550) && (input >= 450))
191  return LEFT;
192  if ((input <= 800) && (input >= 700))
193  return SELECT;
194  if (input <= 100)
195  return RIGHT;
196  return 0;
197 }
198 
200 {
201  uint8_t O_DIV;
202  uint8_t filt;
203  uint8_t LKWIN;
204  uint8_t B_DIV;
205  uint8_t BST;
206  float R_DIV;
207  float f_PFD;
208  float N_DIV;
209  float f_VCO;
210  uint8_t O_DIV_possible = 0;
211  uint8_t ref_out = 0; //!< Used to keep track of reference out status
212 
213  //Change these Variables to design the loop
214  //to different configurations
215  uint8_t rfo = 3;
216  uint8_t i_cp = 11;
217  uint8_t lkcnt = 0;
218  float f_ref = 100; //MHz
219  float f_STEP = .5; //MHz
220  float f_rf= 0; //Desired frequency
221  float dBm = 21;
222 
223  //Convert the Array to a float
224  f_rf += MHz[0]*.01;
225  f_rf += MHz[1]*.1;
226  f_rf += MHz[2];
227  f_rf += MHz[3]*10;
228  f_rf += MHz[4]*100;
229  f_rf += MHz[5]*1000;
230 
231 
232  //Selcts the O divder for different parts
233  if (strcmp(demo_board.product_name, "LTC6946-1") == 0)
234  O_DIV_possible = O_divide_1(f_rf, &O_DIV);
235  else if (strcmp(demo_board.product_name, "LTC6946-2") == 0)
236  O_DIV_possible = O_divide_2(f_rf, &O_DIV);
237  else if (strcmp(demo_board.product_name, "LTC6946-3") == 0)
238  O_DIV_possible = O_divide_3(f_rf, &O_DIV);
239 
240  //If a solution is not found, then it does not design the loop
241  if (!O_DIV_possible)
242  {
243  lcd.setCursor(0, 1);
244  lcd.print("No Loop Solution");
245  delay(1000);
246  return;
247  }
248 
249  //Calculates the R Divider
250  R_DIV = R_divide(f_ref, &f_STEP, O_DIV);
251 
252  //Calculates the PFD
253  f_PFD = f_pfd(f_ref, R_DIV);
254 
255  //Calculates the N divider
256  N_DIV = N_divide(f_rf, O_DIV, f_PFD);
257 
258  //Calculates the VCO
259  f_VCO = f_vco(f_ref, N_DIV, R_DIV);
260 
261  //Calculates the B divider
262  B_DIV = B_div(f_PFD);
263 
264  //Selects the filter
265  filt = filter(f_ref);
266 
267  //Calculates the LKWIN
268  LKWIN = lkwin(f_PFD);
269 
270  //Calculates the BST
271  BST = bst(dBm);
272 
273  //Sets the RFO
274  if (rfo == 0)
275  rfo = LTC6946_RFO_0;
276  else if (rfo == 1)
277  rfo = LTC6946_RFO_1;
278  else if (rfo == 2)
279  rfo = LTC6946_RFO_2;
280  else
281  rfo = LTC6946_RFO_3;
282 
283  //Sets the I_CP
284  switch (i_cp)
285  {
286  case 0:
287  i_cp = LTC6946_CP_0;
288  break;
289  case 1:
290  i_cp = LTC6946_CP_1;
291  break;
292  case 2:
293  i_cp = LTC6946_CP_2;
294  break;
295  case 3:
296  i_cp = LTC6946_CP_3;
297  break;
298  case 4:
299  i_cp = LTC6946_CP_4;
300  break;
301  case 5:
302  i_cp = LTC6946_CP_5;
303  break;
304  case 6:
305  i_cp = LTC6946_CP_6;
306  break;
307  case 7:
308  i_cp = LTC6946_CP_7;
309  break;
310  case 8:
311  i_cp = LTC6946_CP_8;
312  break;
313  case 9:
314  i_cp = LTC6946_CP_9;
315  break;
316  case 10:
317  i_cp = LTC6946_CP_10;
318  break;
319  default:
320  i_cp = LTC6946_CP_11;
321  break;
322  }
323 
324  //Sets the lock count
325  if (lkcnt == 3)
326  lkcnt = LTC6946_LKCNT_3;
327  else if (lkcnt == 2)
328  lkcnt = LTC6946_LKCNT_2;
329  else if (lkcnt == 1)
330  lkcnt = LTC6946_LKCNT_1;
331  else
332  lkcnt = LTC6946_LKCNT_0;
333 
334  //Places loop design into correct registers
335  union
336  {
337  uint8_t R[2];
338  uint16_t code;
339  };
340  code = R_DIV;
341  if (ref_out == 0)
343  else
344  REG[2] = LTC6946_MTCAL;
345  REG[3] = B_DIV |R[1];
346  REG[4] = R[0];
347 
348  union
349  {
350  uint8_t N[2];
351  uint16_t Code;
352  };
353  Code = N_DIV;
354  REG[5] = N[1];
355  REG[6] = N[0];
356  REG[7] = LTC6946_ALCCAL | LTC6946_ALCULOK | LTC6946_LKEN | LTC6946_CAL; // Required settings to allow proper loop locking
357  REG[8] = BST | filt | O_DIV | rfo;
358  REG[9] = i_cp | LKWIN | lkcnt;
359  REG[10] = LTC6946_CPCHI | LTC6946_CPCLO; // Sets charge pump
360 
361  //Set registers
362  write_all();
363 
364  lcd.clear();
365  lcd.print("Loop Set");
366  delay(1000);
367 
368  store_settings();
369 }
371 // Store the PLL Settings to the EEPROM
372 {
374  for (uint8_t i = 2; i <= 10 ; i++)
376  lcd.clear();
377  lcd.print("Settings Stored");
378  delay(1000);
379 }
380 //! Read stored PLL settings from nonvolatile EEPROM on demo board
382 // Read the PLL settings from EEPROM
383 {
384  int16_t cal_key;
385  // read the cal key from the EEPROM
387  if (cal_key == EEPROM_CAL_KEY)
388  {
389  // PLL Settings has been stored, read PLL Settings
390  for (uint8_t i = 2; i <= 10 ; i++)
392 
393  //Update registers
394  write_all();
395 
396  lcd.clear();
397  lcd.print("Settings");
398  lcd.setCursor(0,1);
399  lcd.print("Restored");
400  delay(1000);
401  }
402  else
403  {
404  lcd.clear();
405  lcd.print("No Settings");
406  lcd.setCursor(0,1);
407  lcd.print(" Found");
408  delay(1000);
409  }
410 }
411 
412 //! Write all registers for the LTC6946 and displays them.
413 void write_all()
414 {
415  // Write All Registers
416  LTC6946_write(LTC6946_CS, LTC6946_REG_H01, REG[1]);
417  LTC6946_write(LTC6946_CS, LTC6946_REG_H02, REG[2]);
418  LTC6946_write(LTC6946_CS, LTC6946_REG_H03, REG[3]);
419  LTC6946_write(LTC6946_CS, LTC6946_REG_H04, REG[4]);
420  LTC6946_write(LTC6946_CS, LTC6946_REG_H05, REG[5]);
421  LTC6946_write(LTC6946_CS, LTC6946_REG_H06, REG[6]);
422  LTC6946_write(LTC6946_CS, LTC6946_REG_H07, REG[7]);
423  LTC6946_write(LTC6946_CS, LTC6946_REG_H08, REG[8]);
424  LTC6946_write(LTC6946_CS, LTC6946_REG_H09, REG[9]);
425  LTC6946_write(LTC6946_CS, LTC6946_REG_H0A, REG[10]);
426 }
struct demo_board_type demo_board
Instantiate demo board structure.
static int16_t pos
Definition: Synth.ino:69
LiquidCrystal lcd(RS, E, D4, D5, D6, D7)
const int16_t E
Definition: Synth.ino:53
uint8_t eeprom_read_int16(uint8_t i2c_address, int16_t *read_data, uint16_t address)
Read the two byte integer data from the EEPROM starting at address.
static void loop_design()
Definition: Synth.ino:199
const int16_t D4
Definition: Synth.ino:54
#define EEPROM_I2C_ADDRESS
uint8_t eeprom_write_byte(uint8_t i2c_address, char data, uint16_t address)
Write the data byte to the EEPROM with i2c_address starting at EEPROM address.
Header File for Linduino Libraries and Demo Code.
#define LTC6946_ALCULOK
for spi_map array, defines location for field specific information used to create the spi map ...
Definition: LTC6946.h:86
#define LTC6946_CS
Define the SPI CS pin.
Definition: LTC6946.h:76
char demo_name[]
Demo Board Name stored in QuikEval EEPROM.
Definition: Synth.ino:67
static int8_t read_switches()
Definition: Synth.ino:181
const int8_t RIGHT
Definition: Synth.ino:62
static void restore_settings()
Read stored PLL settings from nonvolatile EEPROM on demo board.
Definition: Synth.ino:381
#define LTC6946_MTCAL
for spi_map array, defines location for field specific information used to create the spi map ...
Definition: LTC6946.h:104
#define LTC6946_LKEN
for spi_map array, defines location for field specific information used to create the spi map ...
Definition: LTC6946.h:101
static uint8_t ref_out
Used to keep track of reference out status.
Definition: DC1649A.ino:122
const int16_t D7
Definition: Synth.ino:57
uint8_t eeprom_write_int16(uint8_t i2c_address, int16_t write_data, uint16_t address)
Write the 2 byte integer data to the EEPROM starting at address.
uint8_t eeprom_read_byte(uint8_t i2c_address, char *data, uint16_t address)
Read a data byte at address from the EEPROM with i2c_address.
QuikEval EEPROM Library.
const int8_t SELECT
Definition: Synth.ino:63
#define input(pin)
Return the state of pin "pin".
Definition: Linduino.h:79
void quikeval_SPI_init(void)
Configure the SPI port for 4Mhz SCK.
Definition: LT_SPI.cpp:151
#define LTC6946_PDREFO
for spi_map array, defines location for field specific information used to create the spi map ...
Definition: LTC6946.h:112
#define EEPROM_CAL_STATUS_ADDRESS
int8_t discover_demo_board(char *demo_name)
Read the ID string from the EEPROM and determine if the correct board is connected.
static void setup()
Definition: Synth.ino:75
static int8_t demo_board_connected
Demo Board Name stored in QuikEval EEPROM.
Definition: Synth.ino:66
void LTC6946_write(uint8_t cs, uint8_t address, uint8_t Data)
LTC6946 Write Single Address writes 8 bit Data field to LTC6946.
Definition: LTC6946.cpp:165
#define LTC6946_CAL
for spi_map array, defines location for field specific information used to create the spi map ...
Definition: LTC6946.h:89
const int8_t LEFT
Definition: Synth.ino:61
const int16_t D5
Definition: Synth.ino:55
LT_SPI: Routines to communicate with ATmega328P&#39;s hardware SPI port.
LTC6946: Ultralow Noise and Spurious 0.37GHz to 6.39GHz Integer-N Synthesizer with Integrated VCO...
static uint8_t REG[12]
Register values to be written to read from.
Definition: Synth.ino:65
LT_I2C: Routines to communicate with ATmega328P&#39;s hardware I2C port.
#define LTC6946_ALCCAL
for spi_map array, defines location for field specific information used to create the spi map ...
Definition: LTC6946.h:81
const int8_t DOWN
Definition: Synth.ino:60
void quikeval_SPI_connect()
Connect SPI pins to QuikEval connector through the Linduino MUX. This will disconnect I2C...
Definition: LT_SPI.cpp:138
char product_name[15]
LTC Product (LTC2654-L16)
static void store_settings()
Definition: Synth.ino:370
static void write_all()
Write all registers for the LTC6946 and displays them.
Definition: Synth.ino:413
void quikeval_I2C_init(void)
Initializes Linduino I2C port.
Definition: LT_I2C.cpp:394
static int i
Definition: DC2430A.ino:184
#define LTC6946_CPCHI
for spi_map array, defines location for field specific information used to create the spi map ...
Definition: LTC6946.h:91
const int16_t BUTTON_PIN
Definition: Synth.ino:58
const int8_t UP
Definition: Synth.ino:59
const int16_t RS
Definition: Synth.ino:52
const int16_t D6
Definition: Synth.ino:56
#define EEPROM_CAL_KEY
#define LTC6946_CPCLO
for spi_map array, defines location for field specific information used to create the spi map ...
Definition: LTC6946.h:92
static void loop()
Definition: Synth.ino:109
static int8_t MHz[6]
Definition: Synth.ino:70