-
Notifications
You must be signed in to change notification settings - Fork 0
/
tube-radio-FM-4 initial scan.ino
305 lines (259 loc) · 7.79 KB
/
tube-radio-FM-4 initial scan.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
/*
tube-radio-FM
Adds an FM receiver to an old tube radio.
The receiver is an RDA5807M module driven by an Arduino Pro Mini.
The original variable capacitor in the radio is used for tunning.
An external NE555 oscillator is used for varcap measuring.
Source pour l'oscillateur NE555 et la mesure de fréquences: http://www.f-legrand.fr/scidoc/docmml/sciphys/arduino/oscillateur/oscillateur.html
Channels are scanned during initial setup and values read on varcap are converted to a value corresponding to the nearest channel.
Version without radio library. Compatible ATM168 (16ko)
Philippe Leclercq 2019
*/
#define DEBUG 0
#include <Wire.h>
int prevChan=0;
// Min and max varcap values (pF)
#define MINCAP 50
#define MAXCAP 416
//#define MINCAP 32
//#define MAXCAP 512
// Convert pF boundaries to oscillator frequencies
// NE555 oscillator freq-capacitance conversion: F = 1.44 / ( (R1+2R2) * C ) with R1=R2=1Mohm
#define CONVERTFACTOR 480000 // (1.44/3)*10e6
unsigned long oscLowBound = CONVERTFACTOR/MAXCAP;
unsigned long oscFreqWidth = CONVERTFACTOR/MINCAP - CONVERTFACTOR/MAXCAP;
#define MAXCHAN 100
int sChan[MAXCHAN];
int nbChan=0;
// ----- Register Definitions ----
int reg;
// this chip only supports FM mode
#define FM_WIDTH 210
#define FM_MIN 870
#define FM_MAX 1080
#define RADIO_REG_CHIPID 0x00
#define RADIO_REG_CTRL 0x02
#define RADIO_REG_CTRL_OUTPUT 0x8000
#define RADIO_REG_CTRL_UNMUTE 0x4000
#define RADIO_REG_CTRL_MONO 0x2000
#define RADIO_REG_CTRL_BASS 0x1000
#define RADIO_REG_CTRL_SEEKUP 0x0200
#define RADIO_REG_CTRL_SEEK 0x0100
#define RADIO_REG_CTRL_RDS 0x0008
#define RADIO_REG_CTRL_NEW 0x0004
#define RADIO_REG_CTRL_RESET 0x0002
#define RADIO_REG_CTRL_ENABLE 0x0001
#define RADIO_REG_CHAN 0x03
#define RADIO_REG_CHAN_SPACE 0x0003
#define RADIO_REG_CHAN_SPACE_100 0x0000
#define RADIO_REG_CHAN_BAND 0x000C
#define RADIO_REG_CHAN_BAND_FM 0x0000
#define RADIO_REG_CHAN_BAND_FMWORLD 0x0008
#define RADIO_REG_CHAN_TUNE 0x0010
#define RADIO_REG_CHAN_NR 0x7FC0
#define RADIO_REG_VOL 0x05
#define RADIO_REG_VOL_VOL 0x000F
#define RADIO_REG_RA 0x0A
#define RADIO_REG_RA_RDS 0x8000
#define RADIO_REG_RA_STC 0x4000
#define RADIO_REG_RA_SF 0x2000
#define RADIO_REG_RA_RDSBLOCK 0x0800
#define RADIO_REG_RA_STEREO 0x0400
#define RADIO_REG_RA_NR 0x03FF
#define RADIO_REG_RB 0x0B
#define RADIO_REG_RB_RSSI 0xFE00
#define RADIO_REG_RB_FMTRUE 0x0100
#define RADIO_REG_RB_FMREADY 0x0080
#define RADIO_RSSI_SHIFT 10
#define I2C_RDA_SEQ 0x10 // I2C-Address RDA Chip for sequential Access
#define I2C_RDA_INDX 0x11 // I2C-Address RDA Chip for Index Access
#define INPUTPIN 5 // oscillator signal to pulse counter (Timer1)
volatile uint16_t count_high,count_low;
volatile uint32_t count;
volatile uint16_t ic; // interrupts counter
uint16_t nbPerSample=5; // number of periodic interrupts for a sample
float deltaT; // Elapsed time per sample
void start_count() {
// Timer2 : periodic interrupts
TCCR2A |= (1 << WGM21); // CTC mode 'clear timer on compare), top = OCR2A
OCR2A = 0xFF; // periodic interrupt frequency 16or8 MHz /1024/256
TIMSK2 = 1 << TOIE2; // overflow interrupt enable
TCNT2 = 0;
ic = 0;
// Timer1 : pulse counter (on rising edge)
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
TIMSK1 = 1<<TOIE1; // overflow interrupt enable
count = 0;
sei(); // enable interrupts
TCCR2B |= (1 << CS12) | (1 << CS11) | (1 << CS10); // prescaler = 1024
TCCR1B |= (1 << CS12) | (1 << CS11) | (1 << CS10); // external clock on rising edge
}
ISR(TIMER2_OVF_vect) { // Overflow interrupt
ic++;
if (ic==nbPerSample) {
ic = 0;
count_low = TCNT1;
TCNT1 = 0;
count = ((uint32_t)count_high)<<16 | count_low;
count_high = 0;
}
}
ISR(TIMER1_OVF_vect) {
count_high++;
}
// Store channels in sChan[]
int scan() {
int i = 0, j=0;
int f, prev;
int tuned; // A stable frequency is tuned.
TuneTo(FM_MIN); // Min of band
do {
seekUp(true);
do {
delay(20);
tuned=radioLevel();
} while (j++<40 && !tuned);
if (tuned) {
prev=f;
f = getFrequency();
#if DEBUG
Serial.print("chan ");
Serial.print(nbChan);
Serial.print(" freq ");
Serial.println(f);
#endif
sChan[nbChan++]=f;
}
j=0;
} while (i++ < MAXCHAN && f>prev);
nbChan--;
seekUp(false);
}
void setup()
{
#if DEBUG
Serial.begin(115200);
Serial.println("\nRadio...");
#endif
pinMode(INPUTPIN,INPUT);
deltaT = 1.0*nbPerSample*1024*256/F_CPU; // 1.0 necessary to compute as float
start_count();
// Initialize the Radio
Wire.begin();
WriteReg(RADIO_REG_CTRL, RADIO_REG_CTRL_RESET);
reg = RADIO_REG_CTRL_OUTPUT|RADIO_REG_CTRL_MONO|RADIO_REG_CTRL_NEW|RADIO_REG_CTRL_ENABLE;
WriteReg(RADIO_REG_CTRL, reg);
scan(); // Initial scan
WriteReg(RADIO_REG_VOL, 0x9080 | 13 ); // 15 = full volume
reg |= RADIO_REG_CTRL_UNMUTE;
WriteReg(RADIO_REG_CTRL, reg);
}
void loop()
{
unsigned long chan;
unsigned long freq = count/deltaT;
if (freq < oscLowBound)
chan = FM_MAX;
else
chan = FM_MAX - (freq - oscLowBound) * FM_WIDTH / oscFreqWidth;
if (chan < FM_MIN) chan = FM_MIN;
#if DEBUG
unsigned capa = CONVERTFACTOR/freq;
Serial.print("Interrupts=");
Serial.print(count);
Serial.print(" Freq=");
Serial.print(freq);
Serial.print("Hz Capa=");
Serial.print(capa);
Serial.print("pF chan=");
Serial.print(chan);
Serial.println("");
#endif
// Search nearest channel
int d, diff=0xFFF, index;
for (int i=0; i<nbChan; i++) {
d = chan - sChan[i];
if (d < 0) d = -d;
if (d < diff) {
diff = d;
index=i;
}
}
chan=sChan[index];
// Use nearest channel in chan array
// Set radio if channel changed
if (chan != prevChan) {
prevChan = chan;
#if DEBUG
Serial.print (" Tuned to ");
Serial.println (chan);
#endif
TuneTo((int)chan);
}
delay (100); // pause!
#if DEBUG
delay (900); // slow debug :)
#endif
}
void TuneTo( int ch)
{
ch -= FM_MIN;
uint8_t hiByte = ch>>2;
uint8_t loByte = ((ch&3)<<6 | RADIO_REG_CHAN_TUNE); // write frequency into bits 15:6, set tune bit
Wire.beginTransmission(I2C_RDA_INDX);
Wire.write(RADIO_REG_CHAN); // reg 0x03
Wire.write(hiByte);
Wire.write(loByte);
Wire.endTransmission();
}
// Writes 1 word register
void WriteReg(byte reg,unsigned int val)
{
Wire.beginTransmission(I2C_RDA_INDX);
Wire.write(reg); Wire.write(val >> 8); Wire.write(val & 0xFF);
Wire.endTransmission();
}
// Returns 1 word register
uint16_t ReadReg(byte reg)
{
Wire.beginTransmission(I2C_RDA_INDX);
Wire.write(reg);
Wire.endTransmission(0); // restart condition
Wire.requestFrom(I2C_RDA_INDX,2, 1); // Retransmit device address with READ, followed by 2 bytes
uint8_t hiByte = Wire.read();
uint8_t loByte = Wire.read();
Wire.endTransmission();
return(256*hiByte + loByte);
}
// start seek mode upwards
void seekUp(bool enable) {
if (enable) {
// start seek mode
reg |= RADIO_REG_CTRL_SEEK;
reg |= RADIO_REG_CTRL_SEEKUP;
WriteReg(RADIO_REG_CTRL, reg);
}
else {
// stop scanning right now
reg &= (~RADIO_REG_CTRL_SEEK);
WriteReg(RADIO_REG_CTRL, reg);
} // if
} // seekUp()
// Signal level of radio station
int radioLevel() {
int sig=0;
// get register B
uint16_t regRB = ReadReg(RADIO_REG_RB);
if (regRB & RADIO_REG_RB_FMTRUE)
sig = (regRB & RADIO_REG_RB_RSSI)>>RADIO_RSSI_SHIFT;
return (sig);
} // radioLevel()
// retrieve the real frequency from the chip after automatic tuning.
int getFrequency() {
// get register A
uint16_t regRA = ReadReg(RADIO_REG_RA);
uint16_t ch = regRA & RADIO_REG_RA_NR;
return (FM_MIN + ch);
} // getFrequency