-
Notifications
You must be signed in to change notification settings - Fork 9
/
AKC695X.cpp
946 lines (847 loc) · 29 KB
/
AKC695X.cpp
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
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
#include <AKC695X.h>
/**
* SI47XX Arduino Library implementation
*
* This is an Arduino library for the AKC695X, BROADCAST RECEIVER, IC family.
* It works with I2C protocol and can provide an easier interface for controlling the AKC695X devices.<br>
*
* This library was built based on [AKC6955 stereo FM / TV / MW / SW / LW digital tuning radio](http://maximradio.altervista.org/akc6955/AKC6955-datasheet-english.pdf) document from "AKC technology".
* It also intend to be used on **all members of the AKC695X family** respecting, of course, the features available for each IC version.
*
* This library can be freely distributed using the MIT Free Software model. [Copyright (c) 2019 Ricardo Lima Caratti](https://pu2clr.github.io/AKC695X/#mit-license).
* Contact: [email protected]
*/
/**
* @defgroup GA03 Basic Methods
* @section GA03 Basic
*/
/**
* @ingroup GA03
* @brief Resets the system.
* @details This function can be used to reset the AKC695X device. ypu can also use the RTS pin of your MCU.
* @details In this case, the RESET pin have to be set to -1. This setup can be configured calling AKC695X::setup method.
*
* @see setup
*/
void AKC695X::reset()
{
if (this->resetPin < 0 ) return; // Do nothing.
pinMode(this->resetPin, OUTPUT);
delay(10);
digitalWrite(this->resetPin, LOW);
delay(10);
digitalWrite(this->resetPin, HIGH);
delay(10);
}
/**
* @ingroup GA03
* @brief Sets the I2C bus device address
* @details You do not need use this function if your i2c device address is 0x10 (default value)
*
* @param deviceAddress
*/
void AKC695X::setI2CBusAddress(int deviceAddress)
{
this->deviceAddress = deviceAddress;
}
/**
* @ingroup GA03
* @brief Receiver startup
* @details Use this method to define the MCU (Arduino) RESET pin and the crystal type you are using.
* @details The options for the crystal type is: CRYSTAL_32KHz (32.768kHz) or CRYSTAL_12MHZ (12MHz).
* @details If you omit the crystal type parameter, will be considered 32.768kHz. Example:
* @code
* #include <AKC695X.h>
* #define RESET_PIN 12 // set it to -1 if you want to use the RST pin of your MCU.
* AKC695X radio;
* void setup() {
* // Set RESET_PIN to -1 if you are using the Arduino RST pin; Select CRYSTAL_32KHz or CRYSTAL_12MHZ
* // radio.setup(RESET_PIN); Instead the line below, if you use this line, the crystal type considered will be 32.768kHz.
* radio.setup(RESET_PIN, CRYSTAL_12MHZ);
* radio.setFM(0, 870, 1080, 1039, 1); // Tunes on 103.9MHz, FM, band 0.
* }
* @endcode
*
* @see setCrystalType, akc595x_reg2
*
* @param resetPin if >= 0, then you control the RESET. if -1, you are using ths Arduino RST pin.
* @param crystal_type if 1 = 32.768kHz (default); 0 = 12MHz
*/
void AKC695X::setup(int resetPin, uint8_t crystal_type)
{
this->resetPin = resetPin;
if (resetPin >= 0)
reset();
Wire.begin();
setCrystalType(crystal_type);
}
/**
* @ingroup GA03
* @brief Receiver startup
* @details Use this method to define the MCU (Arduino) RESET. If you call this method the crystal type will be set to 32.768kHz
* @code
* #include <AKC695X.h>
* AKC695X radio;
* void setup() {
* // Set RESET_PIN to -1 if you are using the Arduino RST pin; Select CRYSTAL_32KHz or CRYSTAL_12MHZ
* // radio.setup(-1); Use this line if you are using RST pin of the Arduino. The crystal type considered will be 32.768kHz.
* radio.setup(12); // You ara usint Arduino Pin 12 to reset control. The crystal type considered will be 32.768kHz.
* radio.setFM(0, 870, 1080, 1039, 1); // Tunes on 103.9MHz, FM, band 0.
* }
* @endcode
* @param resetPin if >= 0, then you control the RESET. if -1, you are using ths MCU RST pin.
*/
void AKC695X::setup(int resetPin) {
this->setup(resetPin, CRYSTAL_32KHz);
}
/**
* @ingroup GA03
* @brief Power the device on
*
* @details Starts the AKC695X with some parameters.
*
* @param fm_en 1 = FM mode; 0 = AM mode
* @param tune If 1 Trigger tune process. The STC bit is set high when the tune operation completes
* @param mute If 1 mute L/R audio
* @param seek If 1 Trigger tune process. The STC bit is set high when the tune operation completes
* @param seekup Seek direction control bit. 0 = Seek down; 1 = Seek up
*/
void AKC695X::powerOn(uint8_t fm_en, uint8_t tune, uint8_t mute, uint8_t seek, uint8_t seekup)
{
akc595x_reg0 reg0;
reg0.refined.power_on = 1;
reg0.refined.rsv = 0;
reg0.refined.fm_en = fm_en;
reg0.refined.mute = mute;
reg0.refined.seek = seek;
reg0.refined.seekup = seekup;
reg0.refined.tune = tune;
setRegister(REG00, reg0.raw);
this->currentMode = fm_en; // Save the current mode (FM or AM)
}
/**
* @ingroup GA03
* @brief Sets a given register with a given value
* @details It is a basic function to deal with the AKC695X devices
* @param reg register number to be written (only for RW type registers)
* @param parameter value to be written in the register
*/
void AKC695X::setRegister(uint8_t reg, uint8_t parameter)
{
Wire.beginTransmission(this->deviceAddress);
Wire.write(reg);
Wire.write(parameter);
Wire.endTransmission();
delayMicroseconds(3000);
}
/**
* @ingroup GA03
* @brief Gets a given register content
* @details It is a basic function to get a value from a given AKC695X device register
* @param reg register number to be read (0 ~ 26)
* @return the register content
*/
uint8_t AKC695X::getRegister(uint8_t reg)
{
uint8_t result;
Wire.beginTransmission(this->deviceAddress);
Wire.write(reg);
Wire.endTransmission();
delayMicroseconds(3000);
Wire.requestFrom(this->deviceAddress, 1);
result = Wire.read();
delayMicroseconds(2000);
return result;
}
/**
* @ingroup GA03
* @brief Sets the kind of Crystal
* @details This method sets the Crystal type you are using in your circuit.
* @details The valid crystal type are 12MHz or 32.768kHz
*
* @param crystal 0 = 12MHz; 1 = 32.768kHz
*/
void AKC695X::setCrystalType(uint8_t crystal) {
akc595x_reg2 reg2;
reg2.raw = getRegister(REG02); // Gets the current value of the register 2
reg2.refined.ref_32k_mode = crystal; // sets the crystal used
setRegister(REG02,reg2.raw);
this->currentCrystalType = crystal;
}
/**
* @defgroup GA03A Status and Configuration Methods
* @section GA03A Status and Configuration Methods
*/
/**
* @ingroup GA03A
* @brief Gets the result of tune processing
* @details This method gets the result of a tune process (seek or tune).
*
* @return true Catched a channel. You are tuned.
* @return false No channel
*/
bool AKC695X::isTuned() {
akc595x_reg20 reg20;
reg20.raw = getRegister(REG20);
return reg20.refined.tuned;
}
/**
* @ingroup GA03A
* @brief Gets the Status of seeking or tuning process
* @details Return the status of tunning process
*
* @return true Completed
* @return false Not completed
*/
bool AKC695X::isTuningComplete()
{
akc595x_reg20 reg20;
reg20.raw = getRegister(REG20);
return reg20.refined.stc;
}
/**
* @ingroup GA03A
* @brief Gets the current operation mode;
* @details This method returns the current mode stored on register 20.
*
* @return 1 FM mode
* @return 0 AM mode
*/
uint8_t AKC695X::isCurrentModeFM()
{
akc595x_reg20 reg20;
reg20.raw = getRegister(REG20);
return reg20.refined.st;
}
/**
* @ingroup GA03A
* @brief Gets the current channel
* @details Returns the current channel stored in the registers 20 and 21.
*
* @return unit16_t current channel
*/
uint16_t AKC695X::getCurrentChannel() {
akc595x_reg20 reg20;
akc595x_reg21 reg21;
uint16_t channel = 0;
reg20.raw = getRegister(REG20);
reg21 = getRegister(REG21);
channel = reg20.refined.readchan;
channel = channel << 8;
channel = channel | reg21;
return channel;
}
/**
* @ingroup GA03A
* @brief Converts the channel stored in the registers 20 and 21 to frequency
* @details Returns the calculated current frequency based on current channel
*
* @see getCurrentChannel, akc595x_reg20, akc595x_reg21
* @return unit16_t current frequency
*/
uint16_t AKC695X::channelToFrequency()
{
uint16_t frequency;
if (this->currentMode == CURRENT_MODE_FM)
{
// the FM tuned frequency = channel / 4 + 300.
frequency = (getCurrentChannel() >> 2) + 300;
}
else
{
// the AM tuned frequency is channel * current step.
frequency = (this->currentMode3k)? getCurrentChannel() * 3:getCurrentChannel() * 5;
}
return frequency;
}
/**
* @ingroup GA03A
* @brief Gets the current AM carrier to noise ratio
* @details Return the current AM carrier to noise ratio in dB.
*
* @see akc595x_reg22
*
* @return uint8_t value in dB of carrier to noise ratio.
*/
uint8_t AKC695X::getAmCarrierNoiseRatio(){
akc595x_reg22 reg22;
reg22.raw = getRegister(REG22);
return reg22.refined.cnram;
};
/**
* @ingroup GA03A
* @brief Gets the current AM space
* @details Returns the AM current space stored in the register 22.
*
* @see akc595x_reg22
*
* @return uint8_t current space 3kHz or 5kHz
*/
uint8_t AKC695X::getAmCurrentSpace(){
akc595x_reg22 reg22;
reg22.raw = getRegister(REG22);
return reg22.refined.mode3k_f;
};
/**
* @ingroup GA03A
* @brief Gets the current FM stereo status
* @details Returns true when the FM stereo signal is more than 30% percent
*
* @see akc595x_reg23
*
* @return true if stereo is detected.
*/
bool AKC695X::isFmStereo() {
akc595x_reg23 reg23;
reg23.raw = getRegister(REG23);
return reg23.refined.st_dem;
}
/**
* @ingroup GA03A
* @brief Gets the current FM carrier to noise ratio
* @details Return the current FM carrier to noise ratio in dB.
*
* @see akc595x_reg23
*
* @return uint8_t value in dB of carrier to noise ratio.
*/
uint8_t AKC695X::getFmCarrierNoiseRatio()
{
akc595x_reg23 reg23;
reg23.raw = getRegister(REG23);
return reg23.refined.cnrfm;
};
/**
* @ingroup GA03A
* @brief Sets de-emphasis
* @details Sets de-emphasis to 50us (Asia) or 75us (USA).
*
* @param de 0 = 75us; 1 = 50us
*/
void AKC695X::setFmEmphasis( uint8_t de) {
akc595x_reg7 reg7;
reg7.raw = getRegister(REG07); // Gets the current value
reg7.refined.de = de; // Sets just DE attribute
setRegister(REG07, reg7.raw); // Store the new REG07 content
}
/**
* @ingroup GA03A
* @brief Sets the FM stereo or mono
* @details This method configures the stereo behavior
*
* | Parameter vaue | Description |
* | -------------- | ------------- |
* | 0 | Auto stereo |
* | 2 | Force stereo |
* | 1 | Mono |
*
* @param value see table above
*/
void AKC695X::setFmStereoMono(uint8_t value)
{
akc595x_reg7 reg7;
reg7.raw = getRegister(REG07); // Gets the current value
reg7.refined.stereo_mono = value; // Sets just the attribute
setRegister(REG07, reg7.raw); // Store the new REG07 content
}
/**
* @ingroup GA03A
* @brief Sets the FM Bandwidth
* @details This method configures FM Bandwidth
*
* | Parameter vaue | Bandwidth |
* | -------------- | -----------|
* | 0 | 150kHz |
* | 1 | 200kHz |
* | 2 | 50kHz |
* | 3 | 100kHz |
*
* @param value see table above
*/
void AKC695X::setFmBandwidth(uint8_t value)
{
akc595x_reg7 reg7;
reg7.raw = getRegister(REG07); // Gets the current value
reg7.refined.bw = value; // Sets just the attribute
setRegister(REG07, reg7.raw); // Store the new REG07 content
}
/**
* @defgroup GA04 Receiver Operation Methods
* @section Receiver Operation
*/
/**
* @ingroup GA04
* @brief Sets the STC bit to high when the tune operation completes
* @details Tells the device that the tune process is over.
*/
void AKC695X::commitTune()
{
akc595x_reg0 reg0;
// I know! it can be simpler and faster than this.
reg0.raw = 0;
reg0.refined.fm_en = this->currentMode; // Sets to the current mode
reg0.refined.power_on = 1;
reg0.refined.tune = 1;
setRegister(REG00, reg0.raw);
reg0.refined.tune = 0;
reg0.refined.seek = 0;
reg0.refined.seekup = 0;
setRegister(REG00, reg0.raw);
};
/**
* @ingroup GA04
* @brief Sets the start and end frequencies for a custom band
* @details This method is used by setAM and setFM methods.
* @details This will called when you set a band greater than 17 on AM mode or greater than 7 on FM mode.
* @details You can use more than one band at a time.
*
* @see akc595x_reg4, akc595x_reg5
* @param band if FM, a value greater than 7. If AM, a value greater than 17. You can use more than one.
* @param minimum_frequency Start frequency
* @param maximum_frequency Final frequency
*/
void AKC695X::setCustomBand(uint16_t minimum_frequency, uint16_t maximum_frequency) {
uint16_t start_channel, end_channel;
akc595x_reg4 reg4; // start channel for custom band
akc595x_reg5 reg5; // end channel for custom band
if (this->currentMode == CURRENT_MODE_FM)
{
start_channel = (minimum_frequency - 300) * 4;
end_channel = (maximum_frequency - 300) * 4;
}
else
{
start_channel = minimum_frequency / ((this->currentMode3k)? 3: 5);
end_channel = maximum_frequency / ((this->currentMode3k)? 3: 5);
}
reg4 = start_channel / 32;
reg5 = end_channel / 32;
setRegister(REG04, reg4);
setRegister(REG05, reg5);
}
/**
* @ingroup GA04
* @brief Sets the AKC695X to FM mode
* @details Sets the device to FM mode. You can configure a custom FM band by setting band number greater than 7.
*
* | FM band | N# |Description |
* | --------| --- |------------ |
* | 000 | 0 | FM1,87 ~ 108, station search space specified intervals |
* | 001 | 1 | FM2,76 ~ 108, station search space specified intervals |
* | 010 | 2 | FM3,70 ~ 93, with a space station search interval set |
* | 011 | 3 | FM4,76 ~ 90, Tuning predetermined space intervals |
* | 100 | 4 | FM5,64 ~ 88, with a space station search interval set |
* | 101 | 5 | TV1,56.25 ~ 91.75, station search space specified intervals |
* | 110 | 6 | TV2, 174.75 ~ 222.25, found |
* | 111 | 7 | sets predetermined space intervals, custom FM, station search space specified intervals |
*
* @param akc695x_fm_band FM band (see manual FM band table above). Set to a number greater than 7 if you want a custom FM band.
* @param minimum_freq Minimal frequency of the band
* @param maximum_freq Band maximum frequency
* @param default_frequency default frequency
* @param default_step increment and decrement step
*/
void AKC695X::setFM(uint8_t akc695x_fm_band, uint16_t minimum_freq, uint16_t maximum_freq, uint16_t default_frequency, uint8_t default_step)
{
akc595x_reg1 reg1;
this->currentMode = 1;
this->currentBand = akc695x_fm_band;
this->currentBandMinimumFrequency = minimum_freq;
this->currentBandMaximumFrequency = maximum_freq;
this->currentFrequency = default_frequency;
this->currentStep = default_step;
reg1.raw = 0;
reg1.refined.fmband = akc695x_fm_band; // Selects the band will be used for FM (see fm band table)
setRegister(REG00, 0b11000000); // Sets to FM (Power On)
if (akc695x_fm_band > 6 )
setCustomBand(minimum_freq, maximum_freq); // Sets a custom FM band
setRegister(REG01, reg1.raw); // Sets the FM band
setFrequency(default_frequency);
}
/**
* @ingroup GA04
* @brief Sets the AKC695X to AM mode and selects the band
* @details This method configures the AM band you want to use.
* @details You must respect the frequency limits defined by the AKC595X device documentation.
* @details You can configure a custom band by setting a band greater than 17
*
* | AM band | N# |Description |
* | ------------ | --- |------------ |
* | 00000 | 0 |LW, 0.15 ~ 0.285, 3K station search |
* | 00001 | 1 |MW1, 0.52 ~ 1.71, 5K station search |
* | 00010 | 2 |MW2, 0.522 ~ 1.62, 9K station search |
* | 00011 | 3 |MW3, 0.52 ~ 1.71, 10K station search |
* | 00100 | 4 |SW1, 4.7 ~ 10, 5K station search |
* | 00101 | 5 |SW2, 3.2 ~ 4.1, 5K station search |
* | 00110 | 6 |SW3, 4.7 ~ 5.6, 5K station search |
* | 00111 | 7 |SW4, 5.7 ~ 6.4, 5K station search |
* | 01000 | 8 |SW5, 6.8 ~ 7.6, 5K station search |
* | 01001 | 9 |SW6, 9.2 ~ 10, 5K station search |
* | 01010 | 10 |SW7, 11.4 ~ 12.2, 5K station search |
* | 01011 | 11 |SW8, 13.5 ~ 14.3 |
* | 01100 | 12 |SW9, 15 ~ 15.9 |
* | 01101 | 13 |SW10, 17.4 ~ 17.9 |
* | 01110 | 14 |SW11, 18.9 ~ 19.7, 5K station search |
* | 01111 | 15 |SW12, 21.4 ~ 21.9, 5K station search |
* | 10000 | 16 |SW13, 11.4 ~ 17.9, 5K station search |
* | 10010 | 17 |MW4, 0.52 to 1.73, 5K station search |
* | Other | 18+ |custom band, station search interval = 3K |
*
* @param akc695x_am_band AM band. Set to a value greater than 17 if you want a custom AM band (see manual AM band table above)
* @param minimum_freq Minimal frequency of the band
* @param maximum_freq Band maximum frequency
* @param default_frequency default frequency
* @param default_step increment and decrement step
*/
void AKC695X::setAM(uint8_t akc695x_am_band, uint16_t minimum_freq, uint16_t maximum_freq, uint16_t default_frequency, uint8_t default_step)
{
akc595x_reg1 reg1;
this->currentMode = 0;
this->currentBand = akc695x_am_band;
this->currentBandMinimumFrequency = minimum_freq;
this->currentBandMaximumFrequency = maximum_freq;
this->currentFrequency = default_frequency;
this->currentStep = default_step;
reg1.raw = 0;
reg1.refined.amband = akc695x_am_band; // Selects the AM band will be used (see AM band table)
setRegister(REG00, 0b10000000); // Sets to AM (Power On)
if (akc695x_am_band > 17)
setCustomBand(minimum_freq, maximum_freq); // Sets a custom AM band
setRegister(REG01, reg1.raw);
setFrequency(default_frequency);
}
/**
* @ingroup GA04
* @brief Sets the step that will be used to increment and decrement the current frequency
* @details The AKC695X has two possible steps (3kHz and 5kHz). The step value is important to calculate the frequenvy you want to set.
*
* @see setFrequency
* @see AKC6955 stereo FM / TV / MW / SW / LW digital tuning radio documentation; page 12
*
* @param step The valid values are 3 and 5. Other values will be ignored.
*/
void AKC695X::setStep(uint8_t step)
{
this->currentStep = step;
}
/**
* @ingroup GA04
* @brief Sets FM step for seeking.
*
* @details Sets FM seek step.
*
* | spece | N# | step |
* | ----- | --- | ------- |
* | 00 | 0 | 25 kHz |
* | 01 | 1 | 50 kHz |
* | 10 | 2 | 100 kHz | (default)
* | 11 | 3 | 200 kHz |
*
* @see AKC6955 stereo FM / TV / MW / SW / LW digital tuning radio documentation; page 14
*
* @param space value betwenn 0 and 3 (see table above ).
*/
void AKC695X::setFmSeekStep(uint8_t space)
{
akc595x_reg11 reg11;
reg11.refined.space = (space > 3) ? 3 : space;
setRegister(REG11, reg11.raw);
}
/**
* @ingroup GA04
* @brief Seeks a FM station
* @details Seek a FM Station
*
* @code
* // Do this if you want to show the frequency during the seek process.
* // Where showFrequency is the function name of your sketch that shows the current frequency.
* radio.seekStation(up_down,showFrequency);
* @endcode
*
* @code
* // Do this if you don't want to show the frequency during seek process.
* radio.seekStation(up_down);
* @endcode
*
* @see akc595x_reg20, akc595x_reg21, channelToFrequency
*
* @param up_down if 0, seek down; if 1, seek up.
* @param showFunc Optional. Point to the function in you sketch that shows the current frequency. If NULL, do nothing (default).
*/
void AKC695X::seekStation(uint8_t up_down, void (*showFunc)())
{
akc595x_reg0 reg0;
long max_time = millis();
do {
reg0.raw = 0;
reg0.refined.fm_en = this->currentMode; // Sets the current mode
reg0.refined.mute = 0; // Normal operation
reg0.refined.power_on = 1; // Power on
reg0.refined.tune = 0;
reg0.refined.seek = 1; // Trigger seeking process
reg0.refined.seekup = up_down;
setRegister(REG00, reg0.raw);
if (showFunc != NULL) {
this->currentFrequency = channelToFrequency(); // gets the Current frequency in the registers 20 and 21.
showFunc(); // Call your function that shows the frequency
}
} while (!isTuningComplete() && (millis() - max_time) < MAX_SEEK_TIME);
reg0.raw = 0;
reg0.refined.fm_en = this->currentMode;
reg0.refined.mute = 0;
reg0.refined.power_on = 1;
reg0.refined.tune = 0;
reg0.refined.seek = 0;
reg0.refined.seekup = up_down;
setRegister(REG00, reg0.raw);
// Updates the currentFrequency member variable to a calculated frequency based on the channel
// value stored in the registers 20 and 21
this->currentFrequency = channelToFrequency();
}
/**
* @ingroup GA04
* @brief Sets the the device to a given frequency
*
* @details This methods check the current mode (AM or FM), calculates the right channel to be setted.
* @details Sets to reg2 structure the 5 most significan bist of the channel.
* @details Sets to reg3 the 8 less significant bits of the channel.
*
* @see akc595x_reg2, akc595x_reg3
*
* @param frequency frequency you want to set to
*/
void AKC695X::setFrequency(uint16_t frequency)
{
uint16_t channel, tmpFreq;
akc595x_reg2 reg2;
akc595x_reg3 reg3;
// Check the band limits
if (frequency > this->currentBandMaximumFrequency)
tmpFreq = this->currentBandMinimumFrequency;
else if (frequency < this->currentBandMinimumFrequency )
tmpFreq = this->currentBandMaximumFrequency;
else
tmpFreq = frequency;
reg2.raw = getRegister(REG02); // Gets the current value of the REG02
if (this->currentMode == 0)
{
// AM mode
channel = tmpFreq / ((this->currentMode3k)? 3: 5);
reg2.refined.channel = (channel >> 8); // Changes just the 5 higher bits of the channel.
reg2.refined.ref_32k_mode = this->currentCrystalType;
reg2.refined.mode3k = this->currentMode3k;
reg3 = channel & 0b0000011111111; // Sets the 8 lower bits of the channel
setRegister(REG03, reg3);
setRegister(REG02, reg2.raw);
}
else
{
// FM mode
channel = (tmpFreq - 300) * 4;
reg2.refined.channel = (channel >> 8);
reg2.refined.ref_32k_mode = this->currentCrystalType;
reg2.refined.mode3k = this->currentMode3k;
reg3 = channel & 0b0000011111111;
setRegister(REG03, reg3);
setRegister(REG02, reg2.raw);
}
commitTune();
this->currentFrequency = tmpFreq;
}
/**
* @ingroup GA04
* @brief Returns the current frequency value
* @details Gets the current frequency.
* @return uint16_t Current frequency value.
*/
uint16_t AKC695X::getFrequency()
{
return this->currentFrequency;
}
/**
* @ingroup GA04
* @brief Adds the current step to the current frequency and sets the new frequency
* @details Goes to the next frequency channel
*/
void AKC695X::frequencyUp()
{
this->currentFrequency += this->currentStep;
setFrequency(this->currentFrequency);
}
/**
* @ingroup GA04
* @brief Subtracts the current step from the current frequency and assign the new frequency
* @details Goes to the previous frequency channel
*/
void AKC695X::frequencyDown()
{
this->currentFrequency -= this->currentStep;
setFrequency(this->currentFrequency);
}
/**
* @ingroup GA04
* @brief Configures the audio output
*
* @details This method sets the AKC695X device audio behaviour
*
* @param phase_inv if 0, audio output inphase; if 1, audio output inverted
* @param line if 0, audio input mode; if 1, radio mode.
* @param volume if 25 a 63, audio volume; if <= 24 mute
*/
void AKC695X::setAudio(uint8_t phase_inv, uint8_t line, uint8_t volume)
{
akc595x_reg6 reg6;
this->volume = reg6.refined.volume = (volume > 63) ? 63 : volume;
reg6.refined.line = line;
reg6.refined.phase_inv = phase_inv;
setRegister(REG06, reg6.raw);
}
/**
* @ingroup GA04
* @brief Configures the audio output with default values
* @details this method sets the audio phase_inv = 0; line = 1 and volume = 40;
* @details Also, this set the audio controlled by MCU (Arduino)
*
* @see setVolumeControl
*
*/
void AKC695X::setAudio()
{
setVolumeControl(1); // Audio controlled by MCU
setAudio(0, 0, 40);
}
/**
* @ingroup GA04
* @brief Sets the output audio volume
* @details Values less than 24 mute the audio output.
* @details Values between 25 and 63 set the output audio volume.
* @param volume
*/
void AKC695X::setVolume(uint8_t volume)
{
akc595x_reg6 reg6;
reg6.raw = getRegister(REG06); // gets the current register value;
if (volume > 63)
volume = 63;
this->volume = reg6.refined.volume = volume; // changes just the volume attribute
setRegister(REG06, reg6.raw); // writes the new reg9 value
}
/**
* @ingroup GA04
* @brief Increments the audio volume
* @details The maximum volume is 63
*/
void AKC695X::setVolumeUp()
{
this->volume = (this->volume > 63) ? 63 : (this->volume + 1);
setVolume(this->volume);
}
/**
* @ingroup GA04
* @brief Decrements the audio volume
* @details The minimum volume is 25
*/
void AKC695X::setVolumeDown()
{
this->volume = (this->volume < 25) ? 25 : (this->volume - 1);
setVolume(this->volume);
}
/**
* @ingroup GA04
* @brief Sets the kind of audio volume control will be used.
* @details This method configures the kind of audio volume control will be used.
* @details You can control the audio volume by potentiometer or by MCU (Arduino).
* @details If you choose volume conttolled by Arduino (type 1), you can set the volume from 25 to 63 levels.
*
* @see setVolume, akc595x_reg9
*
* @param type 0 = controlled by poteciometer; 1 controlled by the MCU
*/
void AKC695X::setVolumeControl(uint8_t type)
{
akc595x_reg9 reg9;
reg9.raw = getRegister(REG09); // gets the current register value;
reg9.refined.pd_adc_vol = type; // changes just the attribute pd_adc_vol
setRegister(REG09, reg9.raw); // writes the new reg9 value
}
/**
* @ingroup GA04
* @brief Gets the current RSSI
* @details This method calculates the RSSI based on two register (24 and 27).
* @details Actually, it is the Antenna aperture and can be calculated using signal levels rssi, pgalevel_rf, pgalevel_if.
* @details The rssi is stored in the register 27 and the pgalevel_rf, pgalevel_if are stored in register 24.
* @details On FM mode or SW band, the formula is: Pin (dBuV) = 103 - rssi - 6 * pgalevel_rf - 6 * pgalevel_if
* @details On AM mode (LW or MW), the formula is: Pin (dBuV) = 123 - rssi - 6 * pgalevel_rf - 6 * pgalevel_if
*
* @see AKC6955 stereo FM / TV / MW / SW / LW digital tuning radio; page 16
* @see akc595x_reg24, akc595x_reg27
*
* @return int RSSI value
*/
int AKC695X::getRSSI()
{
akc595x_reg24 reg24;
akc595x_reg27 reg27;
int factor = (this->currentMode == CURRENT_MODE_FM || this->currentFrequency > 3000) ? 103 : 123;
reg24.raw = getRegister(REG24);
reg27.raw = getRegister(REG27);
// Calculates the RSSI
return ( factor - reg27.refined.rssi - 6 * (reg24.refined.pgalevel_rf + reg24.refined.pgalevel_if) );
}
/**
* @ingroup GA04
* @brief Gets the supply voltage
* @details Gets the current supply voltage
* @return float the supply voltage
*/
float AKC695X::getSupplyVoltage()
{
akc595x_reg25 reg25;
reg25.raw = getRegister(REG25);
return (1.8 + 0.05 * reg25.refined.vbat);
}
/**
* @defgroup GA05 Tools Methods
* @section GA05 Format
*/
/**
* @ingroup GA05
* @brief Converts a number to a char array
* @details It is useful to mitigate memory space used by functions like sprintf or othetr generic similar functions
* @details You can use it to format frequency using decimal or tousand separator and also to convert small numbers.
*
* @param value value to be converted
* @param strValue char array that will be receive the converted value
* @param len final string size (in bytes)
* @param dot the decimal or tousand separator position
* @param separator symbol "." or ","
* @param remove_leading_zeros if true removes up to two leading zeros
*/
void AKC695X::convertToChar(uint16_t value, char *strValue, uint8_t len, uint8_t dot, uint8_t separator, bool remove_leading_zeros)
{
char d;
for (int i = (len - 1); i >= 0; i--)
{
d = value % 10;
value = value / 10;
strValue[i] = d + 48;
}
strValue[len] = '\0';
if (dot > 0)
{
for (int i = len; i >= dot; i--)
{
strValue[i + 1] = strValue[i];
}
strValue[dot] = separator;
}
if (remove_leading_zeros)
{
if (strValue[0] == '0')
{
strValue[0] = ' ';
if (strValue[1] == '0')
strValue[1] = ' ';
}
}
}