PU2CLR RDA5807 Arduino Library 1.1.1
Arduino Library for RDA5807 Devices - By Ricardo Lima Caratti
RDA5807.cpp
Go to the documentation of this file.
1/**
2 * @mainpage RDA5807 Arduino Library implementation
3 * @details RDA5807 Arduino Library implementation. This is an Arduino library for the RDA5807, BROADCAST RECEIVER.
4 * @details It works with I2C protocol and can provide an easier interface to control the RDA5807 device.<br>
5 * @details This library was built based on "RDA5807M - SINGLE-CHIP BROADCAST FM RADIO TUNER - Rev.1.1–Aug.2015"
6 * @details and RDA microelectronics RDA5807FP - SINGLE-CHIP BROADCAST FM RADIO TUNER
7 * @details This library can be freely distributed using the MIT Free Software model.
8 * @copyright Copyright (c) 2020 Ricardo Lima Caratti.
9 * @author Ricardo LIma Caratti (pu2clr@gmail.com)
10 */
11
12#include <RDA5807.h>
13
14/**
15 * @defgroup GA03 Basic Functions
16 * @section GA03 Basic
17 */
18
19/**
20 * @ingroup GA03
21 * @brief Set the Device GPIO pins
22 * @details This method is useful to add control to the system via GPIO RDA devive pins.
23 * @details For example: You can use these pins to control RDS and SEEK via interrupt.
24 * @details GPIOs are General Purpose I/O pin.
25 * @details GPIO setup
26 * @details When GPIO1 (#1), gpioSetup can be: 00 = High impedance; 01 = Reserved; 10 = Low; 11 = High
27 * @details When GPIO2 (#2), gpioSetup can be: 00 = High impedance; 01 = Interrupt (INT) 10 = Low; 11 = High
28 * @details When GPIO3 (#3), gpioSetup can be: 00 = High impedance; 01 = Mono/Stereo indicator (ST) = Low; 11 = High
29 *
30 * @param gpioPin gpio number (1, 2 or 3)
31 * @param gpioSetup See description above
32 * @param mcuPip MCU (Arduino) pin connected to the gpio
33 */
34void RDA5807::setGpio(uint8_t gpioPin, uint8_t gpioSetup, int mcuPin)
35{
36
37 switch (gpioPin) {
38 case 1:
39 this->gpio1Control = mcuPin;
40 reg04->refined.GPIO1 = gpioSetup;
41 break;
42 case 2:
43 this->gpio2Control = mcuPin;
44 reg04->refined.GPIO2 = gpioSetup;
45 break;
46 case 3:
47 this->gpio3Control = mcuPin;
48 reg04->refined.GPIO3 = gpioSetup;
49 break;
50 default:
51 gpio1Control = gpio2Control = gpio3Control = -1;
52
53 }
54 setRegister(REG04,reg04->raw);
55}
56
57/**
58 * @ingroup GA03
59 * @brief Gets all current device status and RDS information registers (From 0x0A to 0x0F)
60 * @see RDA5807M - SINGLE-CHIP BROADCAST FMRADIO TUNER; pages 5, 9, 12 and 13.
61 * @see rda_reg0a, rda_reg0b, rda_reg0c, rda_reg0d, rda_reg0e, rda_reg0f
62 * @see shadowStatusRegisters;
63 */
65{
66 word16_to_bytes aux;
67 int i;
68
69 Wire.requestFrom(this->deviceAddressFullAccess, 12); // This call starts reading from 0x0A register
70 for (i = 0; i < 6; i++) {
71 aux.refined.highByte = Wire.read();
72 aux.refined.lowByte = Wire.read();
73 shadowStatusRegisters[i] = aux.raw;
74 }
75 Wire.endTransmission();
76}
77
78/**
79 * @ingroup GA03
80 * @brief Gets the register content of a given status register (from 0x0A to 0x0F)
81 * @details Useful when you need just a specific status register content.
82 * @details This methos update the first element of the shadowStatusRegisters linked to the register
83 * @return rdax_reg0a the reference to current value of the 0x0A register.
84 */
85void *RDA5807::getStatus(uint8_t reg)
86{
87 word16_to_bytes aux;
88
89 if ( reg < 0x0A || reg > 0x0F ) return NULL; // Maybe not necessary.
90
91 Wire.beginTransmission(this->deviceAddressDirectAccess);
92 Wire.write(reg);
93 Wire.endTransmission(false);
94 Wire.requestFrom(this->deviceAddressDirectAccess, 2); // reading 0x0A register
95 delayMicroseconds(250);
96 aux.refined.highByte = Wire.read();
97 aux.refined.lowByte = Wire.read();
98 Wire.endTransmission(true);
99 shadowStatusRegisters[reg - 0x0A] = aux.raw;
100
101 return &shadowStatusRegisters[reg - 0x0A];
102}
103
104
105/**
106 * @ingroup GA03
107 * @brief Sets a given value to a specific device register
108 *
109 * @see RDA5807M - SINGLE-CHIP BROADCAST FMRADIO TUNER; pages 5, 9, 10 and 11.
110 * @see rda_reg02, rda_reg03, rda_reg04, rda_reg05, rda_reg06, rda_reg07
111 *
112 * @param reg register number (valid values is between 0x02 and 0x07)
113 * @param value the unsigned 16 bits word value (see rda_rec0x data types)
114 */
115void RDA5807::setRegister(uint8_t reg, uint16_t value)
116{
117 word16_to_bytes aux;
118 if (reg > 8) return; // Maybe not necessary.
119 Wire.beginTransmission(this->deviceAddressDirectAccess);
120 Wire.write(reg);
121 aux.raw = value;
122 Wire.write(aux.refined.highByte);
123 Wire.write(aux.refined.lowByte);
124 Wire.endTransmission();
125 shadowRegisters[reg] = aux.raw; // Updates the shadowRegisters element
126 delayMicroseconds(3000); // Check
127}
128
129/**
130 * @ingroup GA03
131 * @brief Waits for Seek or Tune finish
132 */
134{
135 do {
136 getStatus(REG0A);
137 } while (reg0a->refined.STC == 0);
138}
139
140/**
141 * @ingroup GA03
142 * @brief Resets the device
143 * @details The RDA5807M is RESET itself When VIO is Power up.
144 * @details Also, it support soft reset by triggering the 0x02 register (rda_reg02) bit 1 from 0 to 1.
145 */
147{
148 reg02->refined.SOFT_RESET = 1;
149 setRegister(REG02,reg02->raw);
150}
151
152/**
153 * @ingroup GA03
154 * @brief Powers the receiver on
155 */
157{
158 reg02->raw = 0;
159 reg02->refined.NEW_METHOD = 0;
160 reg02->refined.RDS_EN = 0; // RDS disable
161 reg02->refined.CLK_MODE = this->clockType;
162 reg02->refined.RCLK_DIRECT_IN = this->oscillatorType;
163 reg02->refined.MONO = 1; // Force mono
164 reg02->refined.DMUTE = 1; // Normal operation
165 reg02->refined.DHIZ = 1; // Normal operation
166 reg02->refined.ENABLE = 1;
167 reg02->refined.BASS = 1;
168 reg02->refined.SEEK = 0;
169
170 setRegister(REG02,reg02->raw);
171
172 reg05->raw = 0x00;
173 reg05->refined.INT_MODE = 0;
174 reg05->refined.LNA_PORT_SEL = 2;
175 reg05->refined.LNA_ICSEL_BIT = 0;
176 reg05->refined.SEEKTH = 8; // 0b1000
177 reg05->refined.VOLUME = 0;
178
179 setRegister(REG05, reg05->raw);
180}
181
182/**
183 * @ingroup GA03
184 * @brief Sets new demodulate method. It can improve the receiver sensitivity about 1dB
185 *
186 * @param value true or false
187 */
189 reg02->refined.NEW_METHOD = value;
190 setRegister(REG02,reg02->raw);
191}
192
193/**
194 * @ingroup GA03
195 * @brief Power the receiver off
196 */
198{
199 reg02->refined.SEEK = 0;
200 reg02->refined.ENABLE = 0;
201 setRegister(REG02, reg02->raw);
202}
203
204/**
205 * @ingroup GA03
206 * @brief Starts the device
207 * @param clock_type Clock used.
208 * @param oscillator_type optional. Sets the Oscillator type used (Default Crystal or Ref. Clock).
209 */
210void RDA5807::setup(uint8_t clock_type, uint8_t oscillator_type)
211{
212 this->oscillatorType = oscillator_type;
213 this->clockType = clock_type;
214
215 Wire.begin();
216 delay(1);
217 powerUp();
218
219}
220
221/**
222 * @ingroup GA03
223 * @brief Sets the channel
224 * @details This method tunes the rteceiver in a given channel.
225 * @details The channel can be calculated by using the follow formula
226 * @details channel = (desired frequency - start band frequency) / space channel in use / 10.0);
227 *
228 * @see setFrequency, setBand, setSpace
229 * @see RDA5807M - SINGLE-CHIP BROADCAST FM RADIO TUNER - Rev.1.1–Aug.2015; pages 9 and 12.
230 *
231 * @param channel
232 */
233void RDA5807::setChannel(uint16_t channel)
234{
235 reg03->refined.CHAN = channel;
236 reg03->refined.TUNE = 1;
237 reg03->refined.BAND = this->currentFMBand;
238 reg03->refined.SPACE = this->currentFMSpace;
239 reg03->refined.DIRECT_MODE = 0;
240 setRegister(REG03, reg03->raw);
242}
243
244/**
245 * @ingroup GA03
246 * @brief Sets the frequency
247 * @param frequency
248 */
249void RDA5807::setFrequency(uint16_t frequency)
250{
251 uint16_t channel = (frequency - this->startBand[currentFMBand] ) / (this->fmSpace[this->currentFMSpace] );
252 setChannel(channel);
253 this->currentFrequency = frequency;
254}
255
256/**
257 * @ingroup GA03
258 * @brief Increments the current frequency
259 * @details The increment uses the band space as step. See array: uint16_t fmSpace[4] = {100/10, 200/10, 50/10, 25/10};
260 */
262{
263 if (this->currentFrequency < this->endBand[this->currentFMBand])
264 this->currentFrequency += (this->fmSpace[currentFMSpace] );
265 else
266 this->currentFrequency = this->startBand[this->currentFMBand];
267
268 setFrequency(this->currentFrequency);
269}
270
271/**
272 * @ingroup GA03
273 * @brief Decrements the current frequency
274 * @details The drecrement uses the band space as step. See array: uint16_t fmSpace[4] = {20, 10, 5, 1};
275 */
277{
278 if (this->currentFrequency > this->startBand[this->currentFMBand])
279 this->currentFrequency -= (this->fmSpace[currentFMSpace] );
280 else
281 this->currentFrequency = this->endBand[this->currentFMBand];
282
283 setFrequency(this->currentFrequency);
284}
285
286/**
287 * @ingroup GA03
288 * @brief Gets the current frequency.
289 * @return uint16_t
290 */
292{
293 return this->currentFrequency;
294}
295
296/**
297 * @ingroup GA03
298 * @brief Gets the current channel stored in 0x0A status register.
299 *
300 * @see setChannel, setFrequency, setBand, setSpace
301 * @see RDA5807M - SINGLE-CHIP BROADCAST FM RADIO TUNER - Rev.1.1–Aug.2015; pages 9 and 12.
302 *
303 * @return uint16_t current channel value
304 */
306{
307 getStatus(REG0A);
308 return reg0a->refined.READCHAN;
309}
310
311/**
312 * @ingroup GA03
313 * @brief Gets the current frequency bases on the current channel.
314 * @details The current channel is stored in the 0x0A register. This value is updated after a tune or seek operation.
315 * @details The current frequency can be calculated by the formula below
316 *
317 * | Band | Formula |
318 * | ------ | ------- |
319 * | 0 | Frequency = Channel Spacing (kHz) x READCHAN[9:0]+ 87.0 MHz |
320 * | 1 or 2 | Frequency = Channel Spacing (kHz) x READCHAN[9:0]+ 76.0 MHz |
321 * | 3 | Frequency = Channel Spacing (kHz) x READCHAN[9:0]+ 65.0 MHz |
322 *
323 * @see setChannel, setFrequency, setBand, setSpace
324 * @see RDA5807M - SINGLE-CHIP BROADCAST FM RADIO TUNER - Rev.1.1–Aug.2015; pages 9 and 12.
325 * @return uint16_t
326 */
328 return getRealChannel() * (this->fmSpace[this->currentFMSpace]) + this->startBand[currentFMBand];
329 }
330
331/**
332 * @ingroup GA03
333 * @brief Seek function
334 *
335 * @param seek_mode if 0, wrap at the upper or lower band limit and continue seeking; 1 = stop seeking at the upper or lower band limit
336 * @param direction if 0, seek down; if 1, seek up.
337 */
338void RDA5807::seek(uint8_t seek_mode, uint8_t direction)
339 {
340 reg02->refined.SEEK = 1;
341 reg02->refined.SKMODE = seek_mode;
342 reg02->refined.SEEKUP = direction;
343 setRegister(REG02,reg02->raw);
344}
345
346/**
347 * @ingroup GA03
348 * @brief Seek function
349 * @details Seeks a station up or down.
350 * @details Seek up or down a station and call a function defined by the user to show the frequency during the seek process.
351 * @details Seek begins at the current channel, and goes in the direction specified with the SEEKUP bit. Seek operation stops when a channel is qualified as valid according to the seek parameters, the entire band has been searched (SKMODE = 0), or the upper or lower band limit has been reached (SKMODE = 1).
352 * @details The STC bit is set high when the seek operation completes and/or the SF/BL bit is set high if the seek operation was unable to find a channel qualified as valid according to the seek parameters. The STC and SF/BL bits must be set low by setting the SEEK bit low before the next seek or tune may begin.
353 * @details The SEEK bit is set low and the STC bit is set high when the seek operation completes.
354 * @details It is important to say you have to implement a show frequency function. This function have to get the frequency via getFrequency function.
355 * @details Example:
356 * @code
357 *
358 * SI470X rx;
359 *
360 * void showFrequency() {
361 * uint16_t freq = rx.getFrequency();
362 * Serial.print(freq);
363 * Serial.println("MHz ");
364 * }
365 *
366 * void loop() {
367 * .
368 * .
369 * rx.seek(SI470X_SEEK_WRAP, SI470X_SEEK_UP, showFrequency); // Seek Up
370 * .
371 * .
372 * }
373 * @endcode
374 * @param seek_mode Seek Mode; 0 = Wrap at the upper or lower band limit and continue seeking (default); 1 = Stop seeking at the upper or lower band limit.
375 * @param direction Seek Direction; 0 = Seek down (default); 1 = Seek up.
376 * @param showFunc function that you have to implement to show the frequency during the seeking process. Set NULL if you do not want to show the progress.
377 */
378void RDA5807::seek(uint8_t seek_mode, uint8_t direction, void (*showFunc)())
379{
380 getStatus(REG0A);
381 do
382 {
383 reg02->refined.SEEK = 1;
384 reg02->refined.SKMODE = seek_mode;
385 reg02->refined.SEEKUP = direction;
386 setRegister(REG02, reg02->raw);
387 this->currentFrequency = getRealFrequency(); // gets the current seek frequency
388 if (showFunc != NULL)
389 {
390 showFunc();
391 }
392 delay(10);
393 getStatus(REG0A);
394 } while (reg0a->refined.STC == 0);
396 setFrequency(getRealFrequency()); // Fixes station found.
397}
398
399
400
401/**
402 * @ingroup GA03
403 * @brief Sets RSSI Seek Threshold
404 * @param value
405 */
406void RDA5807::setSeekThreshold(uint8_t value)
407{
408 reg05->refined.SEEKTH = value;
409 setRegister(REG05,reg05->raw);
410}
411
412/**
413 * @ingroup GA03
414 * @brief Sets the FM band. See table below.
415 *
416 * FM band table
417 *
418 * | Value | Description |
419 * | ----- | --------------------------- |
420 * | 00 | 87–108 MHz (US/Europe) |
421 * | 01 | 76–91 MHz (Japan) |
422 * | 10 | 76–108 MHz (world wide) |
423 * | 11 | 65 –76 MHz (East Europe) or 50-65MHz (see bit 9 of gegister 0x06) |
424 *
425 * @param band FM band index. See table above.
426 */
427void RDA5807::setBand(uint8_t band)
428{
429 // reg03->refined.BAND = band;
430 reg03->refined.BAND = this->currentFMBand = band; // Adjusted by anonimous developer
431 setRegister(REG03,reg03->raw);
432}
433
434/**
435 * @ingroup GA03
436 * @brief Sets the FM channel space.
437 *
438 * Channel space table
439 *
440 * | Value | Description |
441 * | ----- | ----------- |
442 * | 00 | 100KHz |
443 * | 01 | 200KHz |
444 * | 10 | 50KHz |
445 * | 11 | 25KHz |
446 *
447 * @param space FM channel space. See table above.
448 * @todo make the space 01 (200kHz) work.
449 */
450void RDA5807::setSpace(uint8_t space)
451{
452 reg03->refined.SPACE = space;
453 this->currentFMSpace = space;
454 setRegister(REG03, reg03->raw);
455}
456
457/**
458 * @ingroup GA03 - Frequency step
459 * @brief Sets the FM Step;
460 * @details Converts the step frequency (25, 50, 100 or 200 kHz) to Space. Invalid values will be converted to 0 (100 kHz)
461 * @param step 25, 50, 100 or 200 kHz
462 * @todo Make the step 200kHz work well
463 */
464void RDA5807::setStep(uint8_t step)
465{
466 uint8_t space;
467 switch (step) {
468 case 100:
469 space = 0; // b00
470 break;
471 case 200:
472 space = 1; // b01
473 break;
474 case 50:
475 space = 2; // b10
476 break;
477 case 25:
478 space = 3; // b11
479 break;
480 default:
481 space = 0;
482 }
483 this->setSpace(space);
484}
485
486
487/**
488 * @ingroup GA03
489 * @brief Gets the current Rssi
490 * @details RSSI; 000000 = min; 111111 = max; RSSI scale is logarithmic.
491 *
492 * @return int
493 */
495{
496 getStatus(REG0B);
497 return reg0b->refined.RSSI;
498}
499
500/**
501 * @ingroup GA03
502 * @brief Sets Soft Mute Enable or disable
503 * @param value true = enable; false=disable
504 */
505void RDA5807::setSoftmute(bool value)
506{
507 reg04->refined.SOFTMUTE_EN = value;
508 setRegister(REG04, reg04->raw);
509}
510
511
512
513
514/**
515 * @ingroup GA03
516 * @brief Sets Audio mute or unmute
517 * @param value TRUE = mute; FALSE = unmute
518 */
519void RDA5807::setMute(bool value)
520{
521 reg02->refined.SEEK = 0;
522 reg02->refined.DHIZ = !value;
523 setRegister(REG02,reg02->raw);
524}
525
526/**
527 * @ingroup GA03
528 * @brief Sets audio Mono or stereo
529 *
530 * @param value TRUE = Mono; FALSE force stereo
531 */
532void RDA5807::setMono(bool value)
533{
534 reg02->refined.SEEK = 0;
535 reg02->refined.MONO = value;
536 setRegister(REG02, reg02->raw);
537}
538
539/**
540 * @ingroup GA03
541 * @brief Sets Bass Boost
542 *
543 * @param value FALSE = Disable; TRUE = Enable
544 */
545void RDA5807::setBass(bool value)
546{
547 reg02->refined.SEEK = 0;
548 reg02->refined.BASS = value;
549 setRegister(REG02, reg02->raw);
550}
551
552/**
553 * @ingroup GA03
554 * @brief Gets the current Stereo status
555 *
556 * @return TRUE if stereo;
557 */
559{
560 getStatus(REG0A);
561 return reg0a->refined.ST;
562}
563
564
565/**
566 * @ingroup GA03
567 * @brief Sets the audio output hight or low impedance
568 * @details Useful to use the RDA5807 device with hight impedance speaker (headphone)
569 * @details if 1 = normal operation and 0 = hight impedance
570 * @param value true = hight impedance; false = normal operation
571 */
573 reg02->refined.DHIZ = !value; // invert the value
574 setRegister(REG02, reg02->raw);
575}
576
577
578/**
579 * @ingroup GA03
580 * @brief Sets the audio volume level
581 *
582 * @param value
583 */
584void RDA5807::setVolume(uint8_t value)
585{
586 if ( value > 15 ) value = 15;
587
588 reg05->refined.VOLUME = this->currentVolume = value;
589 setRegister(REG05, reg05->raw);
590}
591
592/**
593 * @ingroup GA03
594 * @brief Gets the current audio volume level
595 *
596 * @return uint8_t 0 to 15
597 */
599{
600 return this->currentVolume;
601}
602
603/**
604 * @ingroup GA03
605 * @brief Increments the audio volume
606 *
607 */
609{
610 if (this->currentVolume < 15)
611 {
612 this->currentVolume++;
613 setVolume(this->currentVolume);
614 }
615}
616
617/**
618 * @ingroup GA03
619 * @brief Decrements the audio volume
620 *
621 */
623{
624 if (this->currentVolume > 0)
625 {
626 this->currentVolume--;
627 setVolume(this->currentVolume);
628 }
629}
630
631
632/**
633 * @ingroup GA03
634 * @todo
635 * @brief Gets the Device identification
636 * @return number
637 */
639{
640 return 0;
641}
642
643/**
644 * @ingroup GA03
645 * @brief Sets De-emphasis.
646 * @details 75 μs. Used in USA (default); 50 μs. Used in Europe, Australia, Japan.
647 *
648 * @param de 0 = 75 μs; 1 = 50 μs
649 */
650void RDA5807::setFmDeemphasis(uint8_t de) {
651 reg04->refined.DE = de;
652 setRegister(REG04,reg04->raw);
653}
654
655/**
656 * @defgroup GA04 RDS Functions
657 * @section GA04 RDS/RBDS
658 * @todo Need optimizing the method to get the RDS informastion - getStatusRegisters should be called just once at a cicle.
659 */
660
661/**
662 * @ingroup GA04
663 * @brief Sets the RDS operation
664 * @details Enable or Disable the RDS
665 *
666 * @param true = turns the RDS ON; false = turns the RDS OFF
667 */
668void RDA5807::setRDS(bool value)
669{
670 reg02->refined.SEEK = 0;
671 reg02->refined.RDS_EN = value;
672 setRegister(REG02, reg02->raw);
673}
674
675/**
676 * @ingroup GA04
677 * @brief Sets the RBDS operation
678 * @details Enable or Disable the RDS
679 *
680 * @param true = turns the RBDS ON; false = turns the RBDS OFF
681 */
682void RDA5807::setRBDS(bool value)
683{
684 reg02->refined.SEEK = 0;
685 reg02->refined.RDS_EN = 1;
686 setRegister(REG02, reg02->raw);
687 reg04->refined.RBDS = value;
688 setRegister(REG04, reg04->raw);
689}
690
691
692/**
693 * @ingroup GA04
694 * @brief Returns true if RDS Ready
695 * @details Read address 0Ah and check the bit RDSR.
696 * @details When using the polling method, it is best not to poll continuously. The data will appear in intervals.
697 * @return true
698 * @return false
699 */
701{
702 getStatus(REG0A);
703
704 return reg0a->refined.RDSR;
705}
706
707/**
708 * @ingroup GA04
709 *
710 * @brief Returns the current Text Flag A/B
711 * @return uint8_t current Text Flag A/B
712 */
714{
715 rds_blockb blkb;
716 getStatusRegisters(); // TODO: Should be called just once and be processed by all RDS functions at a time.
717 blkb.blockB = reg0d->RDSB;
718 return blkb.refined.textABFlag;
719}
720
721/**
722 * @ingroup GA04
723 * @brief Return the group type
724 *
725 * @return uint16_t
726 */
728{
729 rds_blockb blkb;
730
731 getStatusRegisters(); // TODO: Should be called just once and be processed by all RDS functions at a time.
732 blkb.blockB = reg0d->RDSB;
733 return blkb.group0.groupType;
734}
735
736/**
737 * @ingroup GA04
738 *
739 * @brief Gets the version code (extracted from the Block B)
740 * @returns 0=A or 1=B
741 */
743{
744 rds_blockb blkb;
745 getStatusRegisters(); // TODO: Should be called just once and be processed by all RDS functions at a time.
746 blkb.blockB = reg0d->RDSB;
747 return blkb.refined.versionCode;
748}
749
750/**
751 * @ingroup GA04
752 * @brief Returns the Program Type (extracted from the Block B)
753 * @see https://en.wikipedia.org/wiki/Radio_Data_System
754 * @return program type (an integer betwenn 0 and 31)
755 */
757{
758 rds_blockb blkb;
759 getStatusRegisters(); // TODO: Should be called just once and be processed by all RDS functions at a time.
760 blkb.blockB = reg0d->RDSB;
761 return blkb.refined.programType;
762}
763
764/**
765 * @ingroup GA04
766 *
767 * @brief Process data received from group 2B
768 * @param c char array reference to the "group 2B" text
769 */
770void RDA5807::getNext2Block(char *c)
771{
772 char raw[2];
773 int i, j;
774 word16_to_bytes blk;
775
776 blk.raw = reg0f->RDSD;
777
778 raw[1] = blk.refined.lowByte;
779 raw[0] = blk.refined.highByte;
780
781 for (i = j = 0; i < 2; i++)
782 {
783 if (raw[i] == 0xD || raw[i] == 0xA)
784 {
785 c[j] = '\0';
786 return;
787 }
788 if (raw[i] >= 32)
789 {
790 c[j] = raw[i];
791 j++;
792 }
793 else
794 {
795 c[i] = ' ';
796 }
797 }
798}
799
800/**
801 * @ingroup GA04
802 *
803 * @brief Process data received from group 2A
804 *
805 * @param c char array reference to the "group 2A" text
806 */
807void RDA5807::getNext4Block(char *c)
808{
809 char raw[4];
810 int i, j;
811 word16_to_bytes blk_c, blk_d;
812
813 blk_c.raw = reg0e->RDSC;
814 blk_d.raw = reg0f->RDSD;
815
816 raw[0] = blk_c.refined.highByte;
817 raw[1] = blk_c.refined.lowByte;
818 raw[2] = blk_d.refined.highByte;
819 raw[3] = blk_d.refined.lowByte;
820
821 for (i = j = 0; i < 4; i++)
822 {
823 if (raw[i] == 0xD || raw[i] == 0xA)
824 {
825 c[j] = '\0';
826 return;
827 }
828 if (raw[i] >= 32)
829 {
830 c[j] = raw[i];
831 j++;
832 }
833 else
834 {
835 c[i] = ' ';
836 }
837 }
838}
839
840/**
841 * @ingroup GA04
842 *
843 * @brief Gets the RDS Text when the message is of the Group Type 2 version A
844 * @return char* The string (char array) with the content (Text) received from group 2A
845 */
846char *RDA5807::getRdsText(void)
847{
848 static int rdsTextAdress2A;
849 rds_blockb blkb;
850
852
853 blkb.blockB = reg0d->RDSB;
854 rdsTextAdress2A = blkb.group2.address;
855
856 if (rdsTextAdress2A >= 16)
857 rdsTextAdress2A = 0;
858
859 getNext4Block(&rds_buffer2A[rdsTextAdress2A * 4]);
860 rdsTextAdress2A += 4;
861 return rds_buffer2A;
862}
863
864/**
865 * @ingroup GA04
866 * @todo RDS Dynamic PS or Scrolling PS support
867 * @brief Gets the station name and other messages.
868 *
869 * @return char* should return a string with the station name.
870 * However, some stations send other kind of messages
871 */
873{
874 static int rdsTextAdress0A;
875 rds_blockb blkb;
876
878 blkb.blockB = reg0d->RDSB;
879
880 if (blkb.group0.groupType == 0)
881 {
882 // Process group type 0
883 rdsTextAdress0A = blkb.group0.address;
884 if (rdsTextAdress0A >= 0 && rdsTextAdress0A < 4)
885 {
886 getNext2Block(&rds_buffer0A[rdsTextAdress0A * 2]);
887 rds_buffer0A[8] = '\0';
888 return rds_buffer0A;
889 }
890 }
891 return NULL;
892}
893
894/**
895 * @ingroup @ingroup GA04
896 *
897 * @brief Gets the Text processed for the 2A group
898 *
899 * @return char* string with the Text of the group A2
900 */
902{
903 static int rdsTextAdress2A;
904 rds_blockb blkb;
905
907
908 blkb.blockB = reg0d->RDSB;
909 rdsTextAdress2A = blkb.group2.address;
910
911 if (blkb.group2.groupType == 2)
912 {
913 // Process group 2A
914 // Decode B block information
915 if (rdsTextAdress2A >= 0 && rdsTextAdress2A < 16)
916 {
917 getNext4Block(&rds_buffer2A[rdsTextAdress2A * 4]);
918 rds_buffer2A[63] = '\0';
919 return rds_buffer2A;
920 }
921 }
922 return NULL;
923}
924
925/**
926 * @ingroup GA04
927 * @brief Gets the Text processed for the 2B group
928 * @return char* string with the Text of the group AB
929 */
931{
932 static int rdsTextAdress2B;
933 rds_blockb blkb;
934
936 blkb.blockB = reg0d->RDSB;
937 if (blkb.group2.groupType == 2)
938 {
939 // Process group 2B
940 rdsTextAdress2B = blkb.group2.address;
941 if (rdsTextAdress2B >= 0 && rdsTextAdress2B < 16)
942 {
943 getNext2Block(&rds_buffer2B[rdsTextAdress2B * 2]);
944 return rds_buffer2B;
945 }
946 }
947 return NULL;
948}
949
950/**
951 * @ingroup GA04
952 * @todo Need to check. It is working on SI4735 and Si4703. Why not here?
953 * @brief Gets the RDS time and date when the Group type is 4
954 * @return char* a string with hh:mm +/- offset
955 */
957{
958 // Under Test and construction
959 // Need to check the Group Type before.
960 rds_date_time dt;
961 word16_to_bytes blk_b, blk_c, blk_d;
962 rds_blockb blkb;
963
965
966 blk_b.raw = blkb.blockB = reg0d->RDSB;
967 blk_c.raw = reg0e->RDSC;
968 blk_d.raw = reg0f->RDSD;
969
970 uint16_t minute;
971 uint16_t hour;
972
973 if (blkb.group0.groupType == 4)
974 {
975 char offset_sign;
976 int offset_h;
977 int offset_m;
978
979 // uint16_t y, m, d;
980
981 dt.raw[4] = blk_b.refined.lowByte;
982 dt.raw[5] = blk_b.refined.highByte;
983
984 dt.raw[2] = blk_c.refined.lowByte;
985 dt.raw[3] = blk_c.refined.highByte;
986
987 dt.raw[0] = blk_d.refined.lowByte;
988 dt.raw[1] = blk_d.refined.highByte;
989
990 // Unfortunately it was necessary to wotk well on the GCC compiler on 32-bit
991 // platforms. See si47x_rds_date_time (typedef union) and CGG “Crosses boundary” issue/features.
992 // Now it is working on Atmega328, STM32, Arduino DUE, ESP32 and more.
993 minute = (dt.refined.minute2 << 2) | dt.refined.minute1;
994 hour = (dt.refined.hour2 << 4) | dt.refined.hour1;
995
996 offset_sign = (dt.refined.offset_sense == 1) ? '+' : '-';
997 offset_h = (dt.refined.offset * 30) / 60;
998 offset_m = (dt.refined.offset * 30) - (offset_h * 60);
999
1000 sprintf(rds_time, "%02u:%02u %c%02u:%02u", hour, minute, offset_sign, offset_h, offset_m);
1001
1002 return rds_time;
1003 }
1004
1005 return NULL;
1006}
1007
1008/**
1009 * @ingroup GA04
1010 * @brief Get the Rds Sync
1011 * @details Returns true if RDS currently synchronized.
1012 * @return true or false
1013 */
1015{
1016 getStatus(REG0A);
1017 return reg0a->refined.RDSS;
1018}
1019
1020/**
1021 * @ingroup GA04
1022 * @brief Get the current Block ID
1023 * @details 1= the block id of register 0cH,0dH,0eH,0fH is E
1024 * @details 0= the block id of register 0cH, 0dH, 0eH,0fH is A, B, C, D
1025 * @return 0= the block id of register 0cH, 0dH, 0eH,0fH is A, B, C, D; 1 = the block id of register 0cH,0dH,0eH,0fH is E
1026 */
1028{
1029 getStatus(REG0B);
1030 return reg0b->refined.ABCD_E;
1031}
1032
1033/**
1034 * @ingroup GA04
1035 * @brief Get the current Status of block B
1036 *
1037 * Block Errors Level of RDS_DATA_1, and is always read as Errors Level of RDS BLOCK B (in RDS mode ) or E (in RBDS mode when ABCD_E flag is 1).
1038 * | value | description |
1039 * | ----- | ----------- |
1040 * | 00 | 0 errors requiring correction |
1041 * | 01 | 1~2 errors requiring correction |
1042 * | 10 | 3~5 errors requiring correction |
1043 * | 11 | 6+ errors or error in checkword, correction not possible |
1044 *
1045 * **Available only in RDS Verbose mode**
1046 *
1047 * @return value See table above.
1048 */
1050{
1051 getStatus(REG0B);
1052 return reg0b->refined.BLERB;
1053}
1054
1055/**
1056 * @ingroup GA04
1057 * @brief Returns true when the RDS system has valid information
1058 * @details Returns true if RDS currently synchronized; the information are A, B, C and D blocks; and no errors
1059 * @return true or false
1060 */
1062 getStatus(REG0B);
1063 return (reg0a->refined.RDSS && reg0b->refined.ABCD_E == 0 && reg0b->refined.BLERB == 0 );
1064}
1065
1066/**
1067 * @ingroup GA04
1068 * @brief Sets RDS fifo mode enable
1069 *
1070 * @param value If true, it makes the the fifo mode enable.
1071 * @return true or false
1072 */
1073void RDA5807::setRdsFifo(bool value) {
1074 reg04->refined.RDS_FIFO_EN = value;
1075 setRegister(REG04,reg04->raw);
1076}
1077
1078/**
1079 * @ingroup GA04
1080 * @brief Clear RDS fifo
1081 *
1082 * @param value If true, it makes the the fifo mode enable.
1083 * @return true or false
1084 */
1086{
1087 reg04->refined.RDS_FIFO_CLR = 1;
1088 setRegister(REG04, reg04->raw);
1089}
1090
1091
1092
1093
1094
1095/** @defgroup G05 Tools method
1096 * @details A set of functions used to support other functions
1097*/
1098
1099/**
1100 * @ingroup G05 Covert numbers to char array
1101 * @brief Converts a number to a char array
1102 * @details It is useful to mitigate memory space used by functions like sprintf or othetr generic similar functions
1103 * @details You can use it to format frequency using decimal or tousand separator and also to convert smalm numbers.
1104 *
1105 * @param value value to be converted
1106 * @param strValue char array that will be receive the converted value
1107 * @param len final string size (in bytes)
1108 * @param dot the decimal or tousand separator position
1109 * @param separator symbol "." or ","
1110 * @param remove_leading_zeros if true removes up to two leading zeros (default is true)
1111 */
1112void RDA5807::convertToChar(uint16_t value, char *strValue, uint8_t len, uint8_t dot, uint8_t separator, bool remove_leading_zeros)
1113{
1114 char d;
1115 for (int i = (len - 1); i >= 0; i--)
1116 {
1117 d = value % 10;
1118 value = value / 10;
1119 strValue[i] = d + 48;
1120 }
1121 strValue[len] = '\0';
1122 if (dot > 0)
1123 {
1124 for (int i = len; i >= dot; i--)
1125 {
1126 strValue[i + 1] = strValue[i];
1127 }
1128 strValue[dot] = separator;
1129 }
1130
1131 if (remove_leading_zeros) {
1132 if (strValue[0] == '0')
1133 {
1134 strValue[0] = ' ';
1135 if (strValue[1] == '0')
1136 strValue[1] = ' ';
1137 }
1138 }
1139}
1140
1141
1142/**
1143 * @defgroup GA06 I2S Functions
1144 * @section GA06 I2S
1145 * @details When setting I2S_ENABLE (register 04) bit is high, the RDA5807FP can get the output signals SCK, WS, SD signals from GPIO3, GPIO1 and GPIO2 (I2S master)
1146 */
1147
1148
1149/**
1150 * @ingroup GA06 set I2S
1151 * @brief Configure all parameters for I2S
1152 * @details I2S setup must be enabled
1153 * @details I2S_SW_CNT can be: I2S_WS_STEP_48, I2S_WS_STEP_44_1, I2S_WS_STEP_32, I2S_WS_STEP_24, I2S_WS_STEP_22_05, I2S_WS_STEP_16, I2S_WS_STEP_12, I2S_WS_STEP_11_025 or I2S_WS_STEP_8
1154 *
1155 * @param R_DELY If 1, R channel data delay 1T
1156 * @param L_DELY If 1, L channel data delay 1T
1157 * @param SCLK_O_EDGE If 1, invert sclk output when as master
1158 * @param SW_O_EDGE If 1, invert ws output when as master
1159 * @param I2S_SW_CNT Only valid in master mode. See table above
1160 * @param WS_I_EDGE If 0, use normal ws internally; If 1, inverte ws internally
1161 * @param DATA_SIGNED If 0, I2S output unsigned 16-bit audio data. If 1, I2S output signed 16-bit audio data.
1162 * @param SCLK_I_EDGE If 0, use normal sclk internally;If 1, inverte sclk internally
1163 * @param WS_LR Ws relation to l/r channel; If 0, ws=0 ->r, ws=1 ->l; If 1, ws=0 ->l, ws=1 ->r
1164 * @param SLAVE_MASTER I2S slave or master; 1 = slave; 0 = master
1165 * @param OPEN_MODE Open reserved register mode; 11=open behind registers writing function others: only open behind registers reading function
1166 *
1167 * @see RDA microelectronics RDA5807FP - SINGLE-CHIP BROADCAST FM RADIO TUNER pages 11 and 12
1168 *
1169 * @see setI2SOn
1170 */
1171void RDA5807::setI2SAllParameters(uint8_t R_DELY, uint8_t L_DELY, uint8_t SCLK_O_EDGE, uint8_t SW_O_EDGE, uint8_t I2S_SW_CNT, uint8_t WS_I_EDGE, uint8_t DATA_SIGNED, uint8_t SCLK_I_EDGE, uint8_t WS_LR, uint8_t SLAVE_MASTER, uint8_t OPEN_MODE ) {
1172 reg06->refined.R_DELY = R_DELY;
1173 reg06->refined.L_DELY = L_DELY;
1174 reg06->refined.SCLK_O_EDGE = SCLK_O_EDGE;
1175 reg06->refined.SW_O_EDGE = SW_O_EDGE;
1176 reg06->refined.I2S_SW_CNT = I2S_SW_CNT;
1177 reg06->refined.WS_I_EDGE = WS_I_EDGE;
1178 reg06->refined.DATA_SIGNED = DATA_SIGNED;
1179 reg06->refined.SCLK_I_EDGE = SCLK_I_EDGE;
1180 reg06->refined.WS_LR = WS_LR;
1181 reg06->refined.SLAVE_MASTER = SLAVE_MASTER;
1182 reg06->refined.OPEN_MODE = OPEN_MODE;
1183
1184 setRegister(REG06,reg06->raw);
1185}
1186
1187
1188
1189/**
1190 * @ingroup GA06 set I2S on or off
1191 * @brief Enable I2S setup
1192 * @details When setting I2S_ENABLE (register 04) bit is high, the RDA5807FP you can get the output signals SCK, WS, SD signals from GPIO3, GPIO1 and GPIO2 (I2S master)
1193 *
1194 * @param value true or false
1195 */
1196void RDA5807::setI2SOn(bool value) {
1197 reg04->refined.I2S_ENABLE = value;
1198 setRegister(REG04,reg04->raw);
1199}
1200
1201
1202/**
1203 * @ingroup GA06 set I2S Slave or Master
1204 * @brief
1205 *
1206 * @param value true or false
1207 */
1208void RDA5807::setI2SMaster(bool value) {
1209 reg06->refined.SLAVE_MASTER = !value;
1210 setRegister(REG06,reg06->raw);
1211}
1212
1213
1214
1215/**
1216 * @ingroup GA06 set I2S STEP/SPEED
1217 * @brief Sets the speed in kbps. You can use the predefined constantes: I2S_WS_STEP_48, I2S_WS_STEP_44_1, I2S_WS_STEP_32,
1218 * @brief I2S_WS_STEP_24, I2S_WS_STEP_22_05, I2S_WS_STEP_16, I2S_WS_STEP_12, I2S_WS_STEP_11_025 or I2S_WS_STEP_8
1219 *
1220 * @param value value
1221 */
1222void RDA5807::setI2SSpeed(uint8_t value) {
1223 reg06->refined.I2S_SW_CNT = value;
1224 setRegister(REG06,reg06->raw);
1225}
1226
1227
1228/**
1229 * @ingroup GA06 set I2S Data Signed
1230 * @brief If 0, I2S output unsigned 16-bit audio data. If 1, I2S output signed 16-bit audio data.
1231 *
1232 * @param value true (1) or false (0)
1233 */
1234void RDA5807::setI2SDataSigned(bool value) {
1235 reg06->refined.DATA_SIGNED = value;
1236 setRegister(REG06,reg06->raw);
1237}
#define REG02
Definition: RDA5807.h:61
#define REG0A
Definition: RDA5807.h:67
#define REG0B
Definition: RDA5807.h:68
#define REG05
Definition: RDA5807.h:64
#define REG04
Definition: RDA5807.h:63
#define REG03
Definition: RDA5807.h:62
#define REG06
Definition: RDA5807.h:65
void convertToChar(uint16_t value, char *strValue, uint8_t len, uint8_t dot, uint8_t separator, bool remove_leading_zeros=true)
Converts a number to a char array.
Definition: RDA5807.cpp:1112
KT0915 Class.
Definition: RDA5807.h:556
uint16_t getRealFrequency()
Gets the current frequency bases on the current channel.
Definition: RDA5807.cpp:327
void * getStatus(uint8_t reg)
Gets the register content of a given status register (from 0x0A to 0x0F)
Definition: RDA5807.cpp:85
void powerDown()
Power the receiver off.
Definition: RDA5807.cpp:197
bool isStereo()
Gets the current Stereo status.
Definition: RDA5807.cpp:558
void powerUp()
Powers the receiver on.
Definition: RDA5807.cpp:156
uint16_t getRealChannel()
Gets the current channel stored in 0x0A status register.
Definition: RDA5807.cpp:305
void setRegister(uint8_t reg, uint16_t value)
Sets a given value to a specific device register.
Definition: RDA5807.cpp:115
void softReset()
Resets the device.
Definition: RDA5807.cpp:146
void waitAndFinishTune()
Waits for Seek or Tune finish.
Definition: RDA5807.cpp:133
void setSeekThreshold(uint8_t value)
Sets RSSI Seek Threshold.
Definition: RDA5807.cpp:406
int getRssi()
Gets the current Rssi.
Definition: RDA5807.cpp:494
void setFrequencyUp()
Increments the current frequency.
Definition: RDA5807.cpp:261
void setFmDeemphasis(uint8_t de)
Sets De-emphasis.
Definition: RDA5807.cpp:650
void setVolume(uint8_t value)
Sets the audio volume level.
Definition: RDA5807.cpp:584
uint8_t getDeviceId()
Gets the Device identification.
Definition: RDA5807.cpp:638
uint16_t getFrequency()
Gets the current frequency.
Definition: RDA5807.cpp:291
void setGpio(uint8_t gpioPin, uint8_t gpioSetup=0, int mcuPin=-1)
Set the Device GPIO pins.
Definition: RDA5807.cpp:34
uint8_t getVolume()
Gets the current audio volume level.
Definition: RDA5807.cpp:598
void setSpace(uint8_t space=0)
Sets the FM channel space.
Definition: RDA5807.cpp:450
void setBass(bool value)
Sets Bass Boost.
Definition: RDA5807.cpp:545
void setVolumeUp()
Increments the audio volume.
Definition: RDA5807.cpp:608
void seek(uint8_t seek_mode, uint8_t direction)
Seek function.
Definition: RDA5807.cpp:338
void setBand(uint8_t band=0)
Sets the FM band. See table below.
Definition: RDA5807.cpp:427
void setStep(uint8_t step=100)
Sets the FM Step;.
Definition: RDA5807.cpp:464
void setVolumeDown()
Decrements the audio volume.
Definition: RDA5807.cpp:622
void setChannel(uint16_t channel)
Sets the channel.
Definition: RDA5807.cpp:233
void setAudioOutputHighImpedance(bool value)
Sets the audio output hight or low impedance.
Definition: RDA5807.cpp:572
void setNewDemodulateMethod(bool value)
Sets new demodulate method. It can improve the receiver sensitivity about 1dB.
Definition: RDA5807.cpp:188
void getStatusRegisters()
Gets all current device status and RDS information registers (From 0x0A to 0x0F)
Definition: RDA5807.cpp:64
void setup(uint8_t clock_type=CLOCK_32K, uint8_t oscillator_type=OSCILLATOR_TYPE_CRYSTAL)
Starts the device.
Definition: RDA5807.cpp:210
void setFrequency(uint16_t frequency)
Sets the frequency.
Definition: RDA5807.cpp:249
void setMono(bool value)
Sets audio Mono or stereo.
Definition: RDA5807.cpp:532
void setFrequencyDown()
Decrements the current frequency.
Definition: RDA5807.cpp:276
void setSoftmute(bool value)
Sets Soft Mute Enable or disable.
Definition: RDA5807.cpp:505
void seek(uint8_t seek_mode, uint8_t direction, void(*showFunc)())
Seek function.
Definition: RDA5807.cpp:378
void setMute(bool value)
Sets Audio mute or unmute.
Definition: RDA5807.cpp:519
uint8_t getRdsFlagAB(void)
Returns the current Text Flag A/B
Definition: RDA5807.cpp:713
bool hasRdsInfo()
Returns true when the RDS system has valid information.
Definition: RDA5807.cpp:1061
bool getRdsSync()
Get the Rds Sync.
Definition: RDA5807.cpp:1014
uint8_t getBlockId()
Get the current Block ID.
Definition: RDA5807.cpp:1027
uint16_t getRdsGroupType()
Return the group type.
Definition: RDA5807.cpp:727
void getNext4Block(char *c)
Process data received from group 2A.
Definition: RDA5807.cpp:807
char * getRdsText2A(void)
Gets the Text processed for the 2A group.
Definition: RDA5807.cpp:901
void setRDS(bool value)
Sets the RDS operation.
Definition: RDA5807.cpp:668
char * getRdsText0A(void)
Gets the station name and other messages.
Definition: RDA5807.cpp:872
void getNext2Block(char *c)
Process data received from group 2B.
Definition: RDA5807.cpp:770
char * getRdsText(void)
Gets the RDS Text when the message is of the Group Type 2 version A.
Definition: RDA5807.cpp:846
void clearRdsFifo()
Clear RDS fifo.
Definition: RDA5807.cpp:1085
char * getRdsText2B(void)
Gets the Text processed for the 2B group.
Definition: RDA5807.cpp:930
bool getRdsReady()
Returns true if RDS Ready.
Definition: RDA5807.cpp:700
uint8_t getRdsVersionCode(void)
Gets the version code (extracted from the Block B)
Definition: RDA5807.cpp:742
uint8_t getErrorBlockB()
Get the current Status of block B.
Definition: RDA5807.cpp:1049
void setRdsFifo(bool value)
Sets RDS fifo mode enable.
Definition: RDA5807.cpp:1073
uint8_t getRdsProgramType(void)
Returns the Program Type (extracted from the Block B)
Definition: RDA5807.cpp:756
void setRBDS(bool value)
Sets the RBDS operation.
Definition: RDA5807.cpp:682
char * getRdsTime()
Gets the RDS time and date when the Group type is 4.
Definition: RDA5807.cpp:956
void setI2SAllParameters(uint8_t R_DELY, uint8_t L_DELY, uint8_t SCLK_O_EDGE, uint8_t SW_O_EDGE, uint8_t I2S_SW_CNT, uint8_t WS_I_EDGE, uint8_t DATA_SIGNED, uint8_t SCLK_I_EDGE, uint8_t WS_LR, uint8_t SLAVE_MASTER, uint8_t OPEN_MODE)
Configure all parameters for I2S.
Definition: RDA5807.cpp:1171
void setI2SSpeed(uint8_t value)
Sets the speed in kbps. You can use the predefined constantes: I2S_WS_STEP_48, I2S_WS_STEP_44_1,...
Definition: RDA5807.cpp:1222
void setI2SMaster(bool value)
Definition: RDA5807.cpp:1208
void setI2SDataSigned(bool value)
If 0, I2S output unsigned 16-bit audio data. If 1, I2S output signed 16-bit audio data.
Definition: RDA5807.cpp:1234
void setI2SOn(bool value)
Enable I2S setup.
Definition: RDA5807.cpp:1196