PU2CLR BK108X Arduino Library 1.0.2
This is an Arduino Library to control the BK108X device
Loading...
Searching...
No Matches
BK108X.cpp
Go to the documentation of this file.
1/**
2 * @mainpage BK108X Arduino Library implementation
3 * @details BK108X Arduino Library implementation. This is an Arduino library for the BK108X, BROADCAST RECEIVER.
4 * @details This is an Arduino library for the BK1086 and BK1088 DSP BROADCAST RECEIVER.<br>
5 * @details It works with I2C protocol and can provide an easier interface for controlling the BK1086/88 devices.<br>
6 * @details This library is based on the Document: BEKEN - BK1086/88 - BROADCAST AM/FM/SW/LW RADIO RECEIVER Rev 1.3.
7 *
8 * __THIS LIBRARY IS UNDER CONSTRUCTION.....___
9 *
10 * This library can be freely distributed using the MIT Free Software model.
11 * Copyright (c) 2020-2003 Ricardo Lima Caratti.
12 * Contact: pu2clr@gmail.com
13 */
14
15#include <BK108X.h>
16#include <Wire.h> // This library is not used to communicate wwith BK108X family device. It is used to check I2C bus.
17
18/**
19 * @defgroup GA02 BEKEN I2C BUS
20 * @section GA02 I2C
21 *
22 * @brief About I2C Address on BK1086/88 and Arduino platform
23 *
24 * The BK1086/88 Datasheet says that the I2C buss address is 0x80. However, the Wire (I2C) Arduino library does not find
25 * the device on 0x80. Actually the Arduino finds the device on 0x40.
26 * This must be due to the internal conversion of the address from 8 bits to 7 bits. (0x80 = 0b10000000; 0x40 = 0b01000000)
27 * After a few unsuccessful attempts at using the Arduino I2C library, I decided to write the necessary I2C routines to deal
28 * with BK1086/88 device.
29 *
30 * @see setI2C, i2cInit, i2cStart, i2cEndTransaction(), i2cAck, i2cNack, i2cReceiveAck, i2cWriteByte, i2cReadByte, writeRegister, readRegister
31 *
32 * IMPORTANT:
33 * For stable communication, the rising edge time of SCLK should be less than 200ns.
34 */
35
36/**
37 * @ingroup GA02
38 * @brief Sets I2C bus address
39 * @details Useful if some release of BEKEN device is different of 0x80.
40 *
41 * @param i2c_addr
42 */
43void BK108X::setI2C(uint8_t i2c_addr)
44{
45 this->deviceAddress = i2c_addr;
46}
47
48/**
49 * @ingroup GA02
50 * @brief Sets the MCU pins connected to the I2C bus
51 * @details Configures the I2C bus for BK108X
52 *
53 * @param pin_sdio SDA/SDIO MCU/Arduino pin
54 * @param pin_sclk CLK/SCLK MCU/Arduino pin
55 */
56void BK108X::i2cInit(int pin_sdio, int pin_sclk)
57{
58 this->pin_sdio = pin_sdio;
59 this->pin_sclk = pin_sclk;
60}
61
62/**
63 * @ingroup GA02
64 * @brief Starts the I2C bus transaction
65 */
67{
68 pinMode(this->pin_sdio, OUTPUT);
69 pinMode(this->pin_sclk, OUTPUT);
70 digitalWrite(this->pin_sdio, HIGH);
71 digitalWrite(this->pin_sclk, HIGH);
72 delayMicroseconds(1);
73
74 digitalWrite(this->pin_sdio, LOW);
75 delayMicroseconds(1);
76 digitalWrite(this->pin_sclk, LOW);
77 delayMicroseconds(1);
78 digitalWrite(this->pin_sdio, HIGH);
79}
80
81/**
82 * @ingroup GA02
83 * @brief Finish the I2C bus transaction
84 */
86{
87 pinMode(pin_sdio, OUTPUT);
88 digitalWrite(this->pin_sdio, LOW);
89 delayMicroseconds(1);
90
91 digitalWrite(this->pin_sclk, HIGH);
92 delayMicroseconds(1);
93
94 digitalWrite(this->pin_sdio, HIGH);
95 delayMicroseconds(1);
96}
97
98/**
99 * @ingroup GA02
100 * @brief Sends Acknowledge (ACK)
101 * @details Each byte of data (including the address byte) have to be followed by one ACK bit from the receiver.
102 * @details The ACK bit allows the receiver to communicate to the transmitter.
103 * @see https://www.ti.com/lit/an/slva704/slva704.pdf
104 */
106{
107 pinMode(pin_sdio, OUTPUT);
108 digitalWrite(this->pin_sclk, LOW);
109 digitalWrite(this->pin_sdio, LOW);
110 delayMicroseconds(1);
111 digitalWrite(this->pin_sclk, HIGH);
112 delayMicroseconds(1);
113 digitalWrite(this->pin_sclk, LOW);
114}
115
116/**
117 * @ingroup GA02
118 * @brief Sends Not Acknowledge (ACK)
119 * @see https://www.ti.com/lit/an/slva704/slva704.pdf
120 */
122{
123 pinMode(pin_sdio, OUTPUT);
124
125 digitalWrite(this->pin_sclk, LOW);
126 digitalWrite(this->pin_sdio, HIGH);
127 delayMicroseconds(1);
128 digitalWrite(this->pin_sclk, HIGH);
129 delayMicroseconds(1);
130 digitalWrite(this->pin_sclk, LOW);
131}
132
133/**
134 * @ingroup GA02
135 * @brief Gets Acknowledge (ACK)
136 * @see https://www.ti.com/lit/an/slva704/slva704.pdf
137 * @return ack value
138 */
140{
141 uint8_t ack;
142 pinMode(pin_sdio, INPUT);
143 delayMicroseconds(1);
144
145 digitalWrite(this->pin_sclk, HIGH);
146 delayMicroseconds(1);
147
148 ack = digitalRead(this->pin_sdio);
149
150 digitalWrite(this->pin_sclk, LOW);
151 delayMicroseconds(1);
152
153 return ack;
154}
155
156/**
157 * @ingroup GA02
158 * @brief Sends a Byte to the slave device
159 * @param data to be sent to the slave device
160 */
161void BK108X::i2cWriteByte(uint8_t data)
162{
163 pinMode(pin_sdio, OUTPUT);
164 delayMicroseconds(1);
165
166 for (int i = 0; i < 8; i++)
167 {
168
169 digitalWrite(this->pin_sdio, (bool)(data & this->deviceAddress));
170
171 delayMicroseconds(1);
172 digitalWrite(this->pin_sclk, HIGH);
173 delayMicroseconds(1);
174 digitalWrite(this->pin_sclk, LOW);
175 data = data << 1;
176 }
177}
178
179/**
180 * @ingroup GA02
181 * @brief Gets a Byte from the slave device
182 * @return value read from the device
183 */
185{
186 uint8_t value = 0;
187
188 pinMode(pin_sdio, INPUT);
189 delayMicroseconds(1);
190
191 for (int i = 0; i < 8; i++)
192 {
193 digitalWrite(this->pin_sclk, HIGH);
194 value = value << 1;
195 delayMicroseconds(1);
196 if (digitalRead(this->pin_sdio))
197 value = value | 1;
198 digitalWrite(this->pin_sclk, LOW);
199 delayMicroseconds(1);
200 }
201
202 return value;
203}
204
205/**
206 * @ingroup GA02
207 * @brief Sends an array of values to a BK108X given register
208 * @param reg register to be written
209 * @param value content to be stored into the register
210 */
211void BK108X::writeRegister(uint8_t reg, uint16_t value)
212{
213
214 word16_to_bytes data;
215 data.raw = value;
216
218 this->i2cWriteByte(this->deviceAddress);
219 this->i2cReceiveAck();
220
221 reg = reg << 1; // Converts address and sets to write operation
222
223 this->i2cWriteByte(reg);
224 this->i2cReceiveAck();
225
226 this->i2cWriteByte(data.refined.highByte);
227 this->i2cReceiveAck();
228 this->i2cWriteByte(data.refined.lowByte);
229 this->i2cReceiveAck();
230
232}
233
234/**
235 * @ingroup GA02
236 * @brief Gets an array of values from a BK108X given register
237 * @param reg register to be read
238 * @return register content
239 */
241{
242
243 word16_to_bytes data;
244
246 this->i2cWriteByte(this->deviceAddress);
247 this->i2cReceiveAck();
248
249 reg = (reg << 1) | 1; // Converts address and sets to read operation
250
251 this->i2cWriteByte(reg);
252 this->i2cReceiveAck();
253
254 data.refined.highByte = this->i2cReadByte();
255 this->i2cAck();
256 data.refined.lowByte = this->i2cReadByte();
257 this->i2cNack();
258
260
261 return data.raw;
262}
263
264/**
265 * @defgroup GA03 Basic Functions
266 * @section GA03 Basic
267 */
268
269/**
270 * @ingroup GA03
271 * @brief Gets a givens current register content of the device
272 * @see shadowRegisters;
273 * @param device register address
274 * @return the register content (the shadowRegisters array has this content. So, you do not need to use it most of the cases)
275 */
277{
278 uint16_t reg_content;
279 reg_content = this->readRegister(reg);
280 shadowRegisters[reg] = reg_content; // Syncs with the shadowRegisters
281 return reg_content; // Optional
282}
283
284/**
285 * @ingroup GA03
286 * @brief Sets a given value to the device registers
287 * @details For write operations, the device acknowledge is followed by an eight bit data word latched internally on rising edges of SCLK. The device acknowledges each byte of data written by driving SDIO low after the next falling SCLK edge, for 1 cycle.
288 * @details An internal address counter automatically increments to allow continuous data byte writes, starting with the upper byte of register 02h, followed by the lower byte of register 02h, and onward until the lower byte of the last register is reached. The internal address counter then automatically wraps around to the upper byte of register 00h and proceeds from there until continuous writes end.
289 * @details The registers from 0x2 to 0x07 are used to setup the device. This method writes the array shadowRegisters, elements 8 to 14 (corresponding the registers 0x2 to 0x7 respectively) into the device. See Device registers map in BK108X.h file.
290 * @details To implement this, a register maping was created to deal with each register structure. For each type of register, there is a reference to the array element.
291 *
292 * @see shadowRegisters;
293 *
294 * @param device register address
295 */
296void BK108X::setRegister(uint8_t reg, uint16_t value)
297{
298 this->writeRegister(reg, value);
299 shadowRegisters[reg] = value; // Syncs with the shadowRegisters
300 delayMicroseconds(250);
301}
302
303/**
304 * @brief Returns the Device Indentifiction
305 * @return device id
306 */
308{
309 return getRegister(REG00);
310}
311
312/**
313 * @brief Returns the Chip Indentifiction
314 *
315 * @return IC id
316 */
318{
319 return getRegister(REG01);
320}
321
322/**
323 * @ingroup GA03
324 * @brief Gets the current status (register 0x0A) content
325 * @details You can use this function when you need to get more than one status attribute at once.
326 * @details See example code below.
327 * @code
328 * bk_reg0a status = getStatus();
329 *
330 * Serial.println(status.refined.ST); // Stereo Signal Received Indicator
331 * Serial.println(status.refined.RSSI); // Current RSSI value
332 * Serial.println(status.refined.RDSR); // RDS Ready Indicator
333 *
334 * @endcode
335 *
336 * @see getRSSI
337 * @see BK1086/88E - BROADCAST AM/FM/SW/LW RADIO RECEIVER Rev 1.3; page 17.
338 *
339 * @return bk_reg0a data type status register (Register 0Ah. Status2)
340 */
341bk_reg0a BK108X::getStatus()
342{
343 bk_reg0a tmp;
344 tmp.raw = getRegister(REG0A); // update the reg0a shadow register
345 return tmp;
346}
347
348/**
349 * @ingroup GA03
350 * @brief Wait STC (Seek/Tune Complete) status becomes 0
351 * @details Should be used before processing Tune or Seek.
352 * @details The STC bit being cleared indicates that the TUNE or SEEK bits may be set again to start another tune or seek operation. Do not set the TUNE or SEEK bits until the BK108X clears the STC bit.
353 */
355{
356 while (reg0a->refined.STC == 0)
357 {
358 delay(10);
359 getRegister(REG0A);
360 }
361
362 reg03->refined.TUNE = 0;
363 setRegister(REG03, reg03->raw);
364 // delay(40);
365}
366
367/**
368 * @ingroup GA03
369 * @brief Resets the device
370 */
372{
373 reg02->refined.DISABLE = 1;
374 reg02->refined.ENABLE = 0;
375 setRegister(REG02, reg02->raw);
376 reg02->refined.DISABLE = 0;
377 reg02->refined.ENABLE = 1;
378 setRegister(REG02, reg02->raw);
379}
380
381/**
382 * @ingroup GA03
383 * @brief Powers the receiver on
384 * @details Starts the receiver and set default configurations suggested by the BELEN
385 * @see BEKEN - BK1086/88 - BROADCAST AM/FM/SW/LW RADIO RECEIVER Rev 1.3; pages 12-21
386 * @see setup
387 */
389{
390 // Starts the mains register with default values suggested by BEKEN.
391
392 // Turn the receiver on
393 reg02->raw = 0x0280; // Sets to 0 all attributes of the register 0x02 (Power Configuration)
394 reg02->refined.DISABLE = 0; // Power up Disable: 0 = Normal operation
395 reg02->refined.ENABLE = 1; // Power the receiver UP (DISABLE has to be 0)
396 setRegister(REG02, reg02->raw); // Stores the register 0x02
397
398 setRegister(REG03, 0x0000); // Sets to 0 all attributes of the register 0x03 (Channel)
399
400 setRegister(REG04, 0x60D4); // 0b0110000011010100
401 setRegister(REG05, 0x37CF); // 0b0011011111001111
402
403 reg06->raw = 0x086F; // Sets to the default value - 0b0000100001101111 -> CLKSEL = 1
404 reg06->refined.CLKSEL = this->oscillatorType; // Sets to the clock type selected by the user
405 setRegister(REG06, reg06->raw);
406
407 setRegister(REG07, 0x0101); // 0b0000000100000001
408 setRegister(REG08, 0xAC90); // 0b1010110010010000
409
410 setRegister(REG10, 0x7B11); // 0b0111101100010001
411 setRegister(REG11, 0x004A); // 0b0000000001001010
412 setRegister(REG12, 0x4000); // 0b0100000000000000
413 setRegister(REG13, 0x3E00); // 0b0011111000000000
414 setRegister(REG14, 0xC29A); // 0b1100001010011010
415 setRegister(REG15, 0x79F8); // 0b0111100111111000
416 setRegister(REG16, 0x4012); // 0b0100000000010010
417
418 setRegister(REG17, 0x0040); // 0b0000000001000000
419 // setRegister(REG17, 0x0800); // 0b0000100000000000
420 setRegister(REG18, 0x341C); // 0b0011010000011100
421 setRegister(REG19, 0x0080); // 0b0000000010000000
422 setRegister(REG1A, 0x0000); // 0
423 setRegister(REG1B, 0x4CA2); // 0b0100110010100010
424 // 32768 crystal clock (default setup)
425 setRegister(REG1C, 0x8820); // 0b1000100000100000
426 setRegister(REG1D, 0x0200); // 0b0000001000000000 -> 512
427 // If the setup is not factory default -- The commands below are not working propely
428 if (this->oscillatorType != OSCILLATOR_TYPE_CRYSTAL || oscillatorFrequency != 32768)
429 {
430 uint32_t bk_number = (oscillatorFrequency / 512) + 0.5; // The result is a 18 bit number
431 uint16_t final_result = 0;
432 uint16_t aux;
433 // Sets the two most significant bits of result (bk_number) to the REG1C [1:0]
434 // reg1c->refined.FREQ_SEL = 0 // 12MHz test
435 reg1c->refined.FREQ_SEL = (bk_number >> 16);
436 setRegister(REG1C, reg1c->raw);
437 //The REG1D receives the inverted order of the 16 least significant bits of the bk_number.. The bit order of REG1D is opposite to the calculation result
438 aux = bk_number & 0b001111111111111111;
439 // Inverts the bit order of aux;
440 for (int i = 0; i < 16; i++)
441 {
442 if ((aux & (1 << i)) != 0)
443 final_result |= 1 << (15 - i);
444 }
445 // setRegister(REG1D, final_result); // It has no effect on the REG1d register.
446 setRegister(REG1D,0x71DA); // 12MHz test
447 }
448
449
450 delay(this->maxDelayAfterCrystalOn);
451}
452
453/**
454 * @ingroup GA03
455 * @brief Powers the receiver off
456 */
458{
459 reg02->refined.DISABLE = 1;
460 reg02->refined.ENABLE = 0;
461 setRegister(REG02, reg02->raw);
462 delay(100);
463}
464
465
466/**
467 * @ingroup GA03
468 * @brief Starts the device
469 * @details Initiates the divice (similar to the previous setup function)
470 * @details This method allows different clock frequencies.
471 * @details You have to inform at least two parameters: RESET pin and I2C SDA pin of your MCU
472 * @param sda_pin MCU SDA/SDIO pin (ATMEGA328 must be 4)
473 * @param sclk_pin MCU SCLK/CLK pin (ATMEGA328 must be 5)
474 * @param oscillator_type 0 = External clock input; 1= Internal oscillator input (default).
475 * @param oscillator_frequency from 32768Hz (32.768 KHz) to 34400000Hz (34.4Mhz). Default (32.768 kHz). For frequency less than 4 MHz, it must be multiplier of 32.768KHz.
476 */
477void BK108X::setup(int sda_pin, int sclk_pin, uint8_t oscillator_type, uint32_t oscillator_frequency)
478{
479 // Configures BEKEN I2C bus
480 this->i2cInit(sda_pin, sclk_pin);
481 this->oscillatorType = oscillator_type;
482 this->oscillatorFrequency = oscillator_frequency;
483 powerUp();
484}
485
486/**
487 * @ingroup GA03
488 * @brief Sets the receiver to FM mode
489 * @details Configures the receiver on FM mode; Also sets the band limits, defaul frequency and step.
490 *
491 * @param minimum_frequency minimum frequency for the band
492 * @param maximum_frequency maximum frequency for the band
493 * @param default_frequency default freuency
494 * @param step increment and decrement frequency step in KHz (default 10 * 10KHz)
495 */
496void BK108X::setFM(uint16_t minimum_frequency, uint16_t maximum_frequency, uint16_t default_frequency, uint16_t step)
497{
498 this->currentStep = step;
499 this->currentFrequency = default_frequency;
500 this->minimumFrequency = minimum_frequency;
501 this->maximumFrequency = maximum_frequency;
502 this->currentMode = BK_MODE_FM;
503
504 reg07->refined.MODE = BK_MODE_FM;
505 setRegister(REG07, reg07->raw);
506 delay(50);
507 // Sets BAND, SPACE and other parameters
508 this->currentFMBand = reg05->refined.BAND = 0;
509 this->currentFMSpace = reg05->refined.SPACE = 2; // Space will be always 2 and the tune is controlled by currentStep (multiple of 100)
510 setRegister(REG05, reg05->raw);
511 setFrequency(default_frequency);
512};
513
514/**
515 * @ingroup GA03
516 * @brief Sets the receiver to AM mode
517 * @details Configures the receiver on AM mode; Also sets the band limits, defaul frequency and step.
518 *
519 * @param minimum_frequency minimum frequency for the band
520 * @param maximum_frequency maximum frequency for the band
521 * @param default_frequency default freuency
522 * @param step increment and decrement frequency step
523 * @param am_space (default 0 = 1kHz). You can control the freqyuency step by using am_space = 0 and just set the step to 1, 5, 9 or 10 kHz.
524 * This way, you can keep the space always 0
525 */
526void BK108X::setAM(uint16_t minimum_frequency, uint16_t maximum_frequency, uint16_t default_frequency, uint16_t step, uint16_t am_space)
527{
528 this->currentStep = step;
529 this->currentFrequency = default_frequency;
530 this->minimumFrequency = minimum_frequency;
531 this->maximumFrequency = maximum_frequency;
532
533 this->currentMode = reg07->refined.MODE = BK_MODE_AM;
534 setRegister(REG07, reg07->raw);
535 delay(50);
536 // Sets BAND, SPACE and other parameters
537
538 if (minimum_frequency < 520)
539 this->currentAMBand = reg05->refined.BAND = 0; // LW
540 else if (minimum_frequency < 1800)
541 this->currentAMBand = reg05->refined.BAND = 1; // MW
542 else
543 this->currentAMBand = reg05->refined.BAND = 2; // SW
544
545 this->currentAMSpace = reg05->refined.SPACE = am_space; // Space default value 0 (0=1KHz; 1 = 5KHz; 2=9KHz; 3 = 10KHz)
546
547 setRegister(REG05, reg05->raw);
548 this->setFrequency(default_frequency);
549}
550
551/**
552 * @ingroup GA03
553 * @brief Sets the channel
554 * @param channel
555 */
556void BK108X::setChannel(uint16_t channel)
557{
558 reg02->refined.SEEK = 0;
559 setRegister(REG02, reg02->raw);
560
561 reg03->refined.TUNE = 1;
562 reg03->refined.CHAN = channel;
563
564 setRegister(REG03, reg03->raw);
565 // delay(50);
567
568 this->currentChannel = channel;
569}
570
571/**
572 * @ingroup GA03
573 * @brief Sets the FM frequency
574 * @details ....
575 * @param frequency
576 */
577void BK108X::setFrequency(uint16_t frequency)
578{
579 uint16_t channel;
580
581 if (this->currentMode == BK_MODE_FM)
582 {
583 channel = (frequency - this->fmStartBand[this->currentFMBand]) / this->fmSpace[this->currentFMSpace];
584 }
585 else
586 {
587 channel = (frequency - this->amStartBand[this->currentAMBand]) / this->amSpace[this->currentAMSpace];
588 }
589
590 this->setChannel(channel);
591}
592
593/**
594 * @ingroup GA03
595 * @brief Increments the current frequency
596 * @details The increment uses the band space as step.
597 */
599{
600 this->currentFrequency += this->currentStep;
601
602 if (this->currentFrequency > this->maximumFrequency)
603 this->currentFrequency = this->minimumFrequency;
604
605 setFrequency(this->currentFrequency);
606}
607
608/**
609 * @ingroup GA03
610 * @brief Decrements the current frequency
611 * @details The drecrement uses the band space as step.
612 */
614{
615 this->currentFrequency -= this->currentStep;
616
617 if (this->currentFrequency < this->minimumFrequency)
618 this->currentFrequency = this->maximumFrequency;
619
620 setFrequency(this->currentFrequency);
621}
622
623/**
624 * @ingroup GA03
625 * @brief Gets the current frequency.
626 * @return uint16_t
627 */
629{
630 return this->currentFrequency;
631}
632
633/**
634 * @ingroup GA03
635 * @brief Gets the current channel.
636 * @return uint16_t
637 */
639{
640 return this->currentChannel;
641}
642
643/**
644 * @ingroup GA03
645 * @brief Gets the current channel stored in register 0x0B
646 * @details This method is useful to query the current channel during the seek operations.
647 * @return uint16_t
648 */
650{
651 getRegister(REG0B);
652 return reg0b->refined.READCHAN;
653}
654
655/**
656 * @ingroup GA03
657 * @brief Gets the frequency based on READCHAN register (0x0B)
658 * @details Unlike getFrequency method, this method queries the device.
659 *
660 * @return uint16_t
661 */
663{
664 if (currentMode == BK_MODE_AM)
665 {
666 return getRealChannel() * this->amSpace[this->currentAMSpace] + this->amStartBand[this->currentAMBand];
667 }
668 else
669 {
670 return getRealChannel() * this->fmSpace[this->currentFMSpace] + this->fmStartBand[this->currentFMBand];
671 }
672}
673
674/**
675 * @todo it is not working properly
676 * @ingroup GA03
677 * @brief Seeks a station via Software
678 * @details Seeks a station up or down.
679 * @details Seek up or down a station and call a function defined by the user to show the frequency during the seek process.
680 * @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).
681 * @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.
682 * @details Seek performance for 50 kHz channel spacing varies according to RCLK tolerance. Silicon Laboratories recommends ±50 ppm RCLK crystal tolerance for 50 kHz seek performance.
683 * @details A seek operation may be aborted by setting SEEK = 0.
684 * @details It is important to say you have to implement a show frequency function. This function have to get the frequency via getFrequency function.
685 * @details Example:
686 * @code
687 *
688 * BK108X rx;
689 *
690 * void showFrequency() {
691 * uint16_t freq = rx.getFrequency();
692 * Serial.print(freq);
693 * Serial.println("MHz ");
694 * }
695 *
696 * void loop() {
697 * .
698 * .
699 * rx.seek(BK_SEEK_WRAP, BK_SEEK_UP, showFrequency); // Seek Up
700 * .
701 * .
702 * }
703 * @endcode
704 * @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.
705 * @param direction Seek Direction; 0 = Seek down (default); 1 = Seek up.
706 * @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.
707 */
708void BK108X::seekSoftware(uint8_t seek_mode, uint8_t direction, void (*showFunc)())
709{
710 long max_time = millis(); // Maximum time seeking a station.
711
712 do
713 {
714 reg03->refined.TUNE = 0;
715 setRegister(REG03, reg03->raw);
716 delay(50);
717 reg02->refined.SKMODE = seek_mode;
718 reg02->refined.SEEKUP = direction;
719 reg02->refined.SEEK = 1;
720 setRegister(REG02, reg02->raw);
721 delay(50);
722 if (showFunc != NULL)
723 {
724 this->currentFrequency = getRealFrequency();
725 showFunc();
726 }
727 getRegister(REG0A);
728 } while ((!reg0a->refined.STC && reg0a->refined.SF_BL) && (millis() - max_time) < MAX_SEEK_TIME);
729
730 reg02->refined.SEEK = 0;
731 reg03->refined.TUNE = 0;
732 setRegister(REG02, reg02->raw);
733 setRegister(REG03, reg03->raw);
734
735 this->currentFrequency = getRealFrequency();
736}
737
738/**
739 * @todo make it work.
740 * @ingroup GA03
741 * @brief Seeks a station via hardware functionality
742 *
743 * @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.
744 * @param direction Seek Direction; 0 = Seek down (default); 1 = Seek up.
745 */
746void BK108X::seekHardware(uint8_t seek_mode, uint8_t direction, void (*showFunc)())
747{
748
749 long max_time = millis(); // Max time seeking a station.
750
751 do
752 {
753 reg03->refined.TUNE = 0;
754 setRegister(REG03, reg03->raw);
755
756 reg02->refined.SKMODE = seek_mode;
757 reg02->refined.SEEKUP = direction;
758 reg02->refined.SKAFCRL = 1;
759 reg02->refined.SEEK = 1;
760 setRegister(REG02, reg02->raw);
761 delay(40);
762 while (reg0a->refined.STC == 0 && (millis() - max_time) < MAX_SEEK_TIME)
763 {
764 if (showFunc != NULL)
765 {
766 this->currentFrequency = getRealFrequency();
767 showFunc();
768 }
769 getRegister(REG0A);
770 delay(40);
771 }
772 reg02->refined.SEEK = 0;
773 setRegister(REG02, reg02->raw);
774 delay(50);
775
776 this->setChannel(this->getRealChannel());
777 this->currentFrequency = getRealFrequency();
778 this->setFrequency(this->currentFrequency);
779
780 } while (reg0a->refined.SF_BL != 0 && (millis() - max_time) < MAX_SEEK_TIME);
781 reg03->refined.TUNE = 0;
782 setRegister(REG03, reg03->raw);
783}
784
785/**
786 * @todo make it work.
787 * @ingroup GA03
788 * @brief Sets RSSI and SNR Seek Threshold
789 * @param rssiValue between 0 and 127
790 * @param snrValue between 0 and 127
791 */
792void BK108X::setSeekThreshold(uint8_t rssiValue, uint8_t snrValue)
793{
794 reg05->refined.SEEKTH = rssiValue;
795 setRegister(REG05, reg05->raw);
796
797 reg06->refined.SKSNR = snrValue;
798 setRegister(REG06, reg06->raw);
799}
800
801/**
802 * @ingroup GA03
803 * @brief Sets the current band for AM or FM
804 * @details Configures the band by setting the Register 05h. System Configuration2
805 *
806 * | Band value | AM / KHz | FM / MHz |
807 * | ---------- | -------------- | ------------------- |
808 * | 0 | LW - 153~279 | FULL - 64~108 |
809 * | 1 | MW - 520~1710 | East Europe 64~76 |
810 * | 2 | SW - 2.3~21.85 | Japan 76~91 |
811 * | 3 | MW - 522~1710 | Europe 87~108 |
812 *
813 * @see BK1086/88E - BROADCAST AM/FM/SW/LW RADIO RECEIVER Rev 1.3; page 15
814 * @param band the valid values are 0, 1, 2 and 3. See table above.
815 */
816
817void BK108X::setBand(uint8_t band)
818{
819 if (this->currentMode == BK_MODE_AM)
820 this->currentAMBand = band;
821 else
822 this->currentFMBand = band;
823
824 reg05->refined.BAND = band;
825 setRegister(REG05, reg05->raw);
826}
827
828/**
829 * @ingroup GA03
830 * @brief Sets the Space channel for AM or FM
831 *
832 * | Band value | AM | FM |
833 * | ---------- | ---------| --------- |
834 * | 0 | 1 KHz | 10 KHz |
835 * | 1 | 5 KKz | 50 KHz |
836 * | 2 | 9 KHz | 100 KHz |
837 * | 3 | 10 KHz | 200 KHz |
838 *
839 * @see BK1086/88E - BROADCAST AM/FM/SW/LW RADIO RECEIVER Rev 1.3; page 15
840 * @param space valid values 0,1,2 and 3. See table above.
841 */
842void BK108X::setSpace(uint8_t space)
843{
844 if (this->currentMode == BK_MODE_AM)
845 this->currentAMSpace = space;
846 else
847 this->currentFMSpace = space;
848
849 reg05->refined.SPACE = space;
850 setRegister(REG05, reg05->raw);
851}
852
853/**
854 * @ingroup GA03
855 * @brief Gets the current Rssi
856 *
857 * @return int
858 */
860{
861 getRegister(REG0A);
862 return reg0a->refined.RSSI;
863}
864
865/**
866 * @ingroup GA03
867 * @brief Gets the current SNR
868 *
869 * @return int The SNR Value.( in dB)
870 */
872{
873 getRegister(REG09);
874 return reg09->refined.SNR;
875}
876
877/**
878 * @ingroup GA03
879 * @brief Sets the Softmute true or false
880 * @details Enable or Disable Soft Mute resource.
881 * @param value TRUE or FALSE
882 */
883void BK108X::setSoftMute(bool value)
884{
885 reg02->refined.DSMUTE = !value; // Soft mute TRUE/ENABLE means DSMUTE = 0.
886 setRegister(REG02, reg02->raw);
887}
888
889/**
890 * @ingroup GA03
891 * @brief Sets Softmute Attack/Recover Rate.
892 *
893 * Soft mute Attack/Recover
894 *
895 * | Value | Description |
896 * | ----- | ----------- |
897 * | 0 | fastest |
898 * | 1 | fast |
899 * | 2 | slow |
900 * | 3 | slowest |
901 *
902 * @param value See table above.
903 */
904void BK108X::setSoftMuteAttack(uint8_t value)
905{
906 reg06->refined.SMUTER = value;
907 setRegister(REG06, reg06->raw);
908}
909
910/**
911 * @ingroup GA03
912 * @brief Sets Softmute Attenuation.
913 *
914 * Soft mute Attenuation.
915 * | Value | Description |
916 * | ----- | ----------- |
917 * | 0 | fastest |
918 * | 1 | fast |
919 * | 2 | slow |
920 * | 3 | slowest |
921 * @param value See table above
922 */
923void BK108X::setSoftMuteAttenuation(uint8_t value)
924{
925 reg06->refined.SMUTEA = value;
926 setRegister(REG06, reg06->raw);
927}
928
929/**
930 * @ingroup GA03
931 * @brief Set the Mute Threshold based on RSSI and SNR
932 *
933 * @see BK1086/88 - BROADCAST AM/FM/SW/LW RADIO RECEIVER Rev 1.3; page 19; Register 0x14 (Boot Configuration5)
934 * @param rssi The Mute Threshold Based on RSSI (default 26)
935 * @param snr The Mute Threshold Based on SNR (default 5)
936 */
937void BK108X::setMuteThreshold(uint8_t rssi, uint8_t snr)
938{
939 reg14->refined.RSSIMTH = rssi;
940 reg14->refined.SNRMTH = snr;
941 setRegister(REG14, reg14->raw);
942}
943
944/**
945 * @ingroup GA03
946 * @brief Disable or Enable soft mute when seeking
947 *
948 * @param value If true, enable mute during the seek;
949 */
950void BK108X::setSeekMute(bool value)
951{
952 reg14->refined.SKMUTE = value;
953 setRegister(REG14, reg14->raw);
954}
955
956/**
957 * @ingroup GA03
958 * @brief Disable or Enable soft mute when AFCRL is high
959 *
960 * @param value If true, enable soft mute when AFCRL is high
961 */
962void BK108X::setAfcMute(bool value)
963{
964 reg14->refined.AFCMUTE = value;
965 setRegister(REG14, reg14->raw);
966}
967
968/**
969 * @ingroup GA03
970 * @brief Sets the Mute true or false
971 *
972 * @param left left channel (TRUE = MUTE/ FALSE = UNMUTE)
973 * @param left right channel (TRUE = MUTE / FALSE = UMUTE)
974 */
975void BK108X::setAudioMute(bool left, bool right)
976{
977 reg02->refined.MUTEL = left;
978 reg02->refined.MUTER = right;
979 setRegister(REG02, reg02->raw);
980}
981
982/**
983 * @ingroup GA03
984 * @brief Sets the Mute true or false
985 *
986 * @param value left and right channels (TRUE = MUTE/ FALSE = UNMUTE)
987 */
988void BK108X::setAudioMute(bool value)
989{
990 this->setAudioMute(value, value);
991}
992
993/**
994 * @ingroup GA03
995 * @brief Sets the Mono true or false (stereo)
996 * @details if TRUE, force mono; else force stereo
997 * @param value TRUE or FALSE
998 */
999void BK108X::setMono(bool value)
1000{
1001 reg02->refined.MONO = value;
1002 reg02->refined.STEREO = !value;
1003 setRegister(REG02, reg02->raw);
1004}
1005
1006/**
1007 * @ingroup GA03
1008 * @brief Checks stereo / mono status
1009 *
1010 * @see getStatus
1011 * @param value TRUE if stereo
1012 */
1014{
1015 getRegister(REG0A);
1016 return reg0a->refined.STEN;
1017}
1018
1019/**
1020 * @ingroup GA03
1021 * @brief Sets the audio volume level
1022 *
1023 * @param value 0 to 31 (if 0, mutes the audio)
1024 */
1025void BK108X::setVolume(uint8_t value)
1026{
1027 if (value > 31)
1028 return;
1029 this->currentVolume = value;
1030 // reg05 is a shadow register and has the last value read or written from/to the internal device register
1031 reg05->refined.VOLUME = value;
1032
1033 setRegister(REG05, reg05->raw);
1034}
1035
1036/**
1037 * @ingroup GA03
1038 * @brief Gets the current audio volume level
1039 *
1040 * @return uint8_t 0 to 15
1041 */
1043{
1044 return this->currentVolume;
1045}
1046
1047/**
1048 * @ingroup GA03
1049 * @brief Increments the audio volume
1050 *
1051 */
1053{
1054 if (this->currentVolume < 31)
1055 {
1056 this->currentVolume++;
1057 setVolume(this->currentVolume);
1058 }
1059}
1060
1061/**
1062 * @ingroup GA03
1063 * @brief Decrements the audio volume
1064 *
1065 */
1067{
1068 if (this->currentVolume > 0)
1069 {
1070 this->currentVolume--;
1071 setVolume(this->currentVolume);
1072 }
1073}
1074
1075/**
1076 * @defgroup GA04 RDS Functions
1077 * @section GA04 RDS/RBDS
1078 */
1079
1080/**
1081 * @ingroup GA04
1082 * @brief Gets the RDS registers information
1083 * @details Gets the value of the registers from 0x0A to 0x0F
1084 * @details This function also updates the value of shadowRegisters[0];
1085 * @return bk_reg0a
1086 */
1088{
1089}
1090
1091/**
1092 * @ingroup GA04
1093 * @brief Sets the Rds Mode Standard or Verbose
1094 *
1095 * @param rds_mode 0 = Standard (default); 1 = Verbose
1096 */
1097void BK108X::setRdsMode(uint8_t rds_mode)
1098{
1099 this->rds_mode = rds_mode;
1100}
1101
1102/**
1103 * @ingroup GA04
1104 * @brief Sets the RDS operation
1105 * @details Enable or Disable the RDS
1106 * @details You can setup interrupt via GPIO2. It is useful to avoid pulling during RDS queries.
1107 * @details When a new RDS data is available (new RDS information came) a 5ms low pulse will appear at GPIO2
1108 * @param true = turns the RDS ON; false = turns the RDS OFF
1109 * @param true = enable interruot at GPIO2 when new RDS is available (default false).
1110 */
1111void BK108X::setRds(bool value, bool interrupt_enable)
1112{
1113 reg04->refined.RDSEN = value;
1114 if ( interrupt_enable ) {
1115 reg04->refined.RDSIEN = interrupt_enable; // enable interrupt when new RDS information is available
1116 reg04->refined.GPIO2 = 1; // set to 5ms low pulse at GPIO2
1117 }
1118 setRegister(REG04, reg04->raw);
1119}
1120
1121/**
1122 * @ingroup GA04
1123 * @brief Returns true if RDS Ready
1124 * @details Read addresses 0Ah, 0Ch, and check the bit RDSR.
1125 * @details If in verbose mode, the BLERA bits indicate how many errors were corrected in block A. If BLERA indicates 6 or more errors, the data in RDSA should be discarded.
1126 * @details When using the polling method, it is best not to poll continuously. The data will appear in intervals of ~88 ms and the RDSR indicator will be available for at least 40 ms, so a polling rate of 40 ms or less should be sufficient.
1127 * @details ATTENTION: You must call this function before calling any RDS function to process data
1128 * @return true or false
1129 */
1131{
1132 getRegister(REG0A);
1133 if (!reg0a->refined.RDSR)
1134 return false;
1135 getRegister(REG0C); // The First Register of RDS - Block A
1136 getRegister(REG0D); // The second register of RDS - Block B
1137 getRegister(REG0E); // The third register of RDS - Block C
1138 getRegister(REG0F); // The third register of RDS - Bloco D
1139 return true;
1140};
1141
1142/**
1143 * @ingroup GA04
1144 *
1145 * @brief Returns the current Text Flag A/B
1146 * @details You must call getRdsReady before calling this function
1147 * @return uint8_t current Text Flag A/B
1148 */
1150{
1151 bk_rds_blockb blkb;
1152 blkb.blockB = reg0d->raw;
1153 return blkb.refined.textABFlag;
1154}
1155
1156/**
1157 * @ingroup GA04
1158 * @brief Return the group type - Gets the Group Type (extracted from the Block B)
1159 * @details You must call getRdsReady before calling this function
1160 * @return uint16_t
1161 */
1163{
1164 bk_rds_blockb b;
1165 b.blockB = reg0d->raw;
1166 return b.refined.groupType;
1167}
1168
1169/**
1170 * @ingroup GA04
1171 *
1172 * @brief Gets the version code (extracted from the Block B)
1173 * @details You must call getRdsReady before calling this function
1174 * @returns 0=A or 1=B
1175 */
1177{
1178 bk_rds_blockb b;
1179 b.blockB = reg0d->raw;
1180 return b.refined.versionCode;
1181}
1182
1183/**
1184 * @ingroup GA04
1185 * @brief Returns the Program Type (extracted from the Block B)
1186 * @details You must call getRdsReady before calling this function
1187 * @see https://en.wikipedia.org/wiki/Radio_Data_System
1188 * @return program type (an integer betwenn 0 and 31)
1189 */
1191{
1192 bk_rds_blockb b;
1193 b.blockB = reg0d->raw;
1194 return b.refined.programType;
1195}
1196
1197/**
1198 * @ingroup GA04
1199 *
1200 * @brief Process data received from group 2B
1201 * @param c char array reference to the "group 2B" text
1202 */
1203void inline BK108X::getNext2Block(char *c)
1204{
1205 c[1] = reg0f->refined.lowByte;
1206 c[0] = reg0f->refined.highByte;
1207}
1208
1209/**
1210 * @ingroup GA04
1211 *
1212 * @brief Processes data received from group 2A
1213 * @details decodes data received from block C and block D
1214 * @param c char array reference to the "group 2A" text
1215 */
1216void inline BK108X::getNext4Block(char *c)
1217{
1218 c[0] = reg0e->refined.highByte; // RDS Block C high byte
1219 c[1] = reg0e->refined.lowByte; // RDS Block C low byte
1220 c[2] = reg0f->refined.highByte; // RDS Block D high byte
1221 c[3] = reg0f->refined.lowByte; // RDS Block D low byte
1222}
1223
1224/**
1225 * @ingroup GA04
1226 *
1227 * @brief Gets the RDS Text when the message is of the Group Type 2 version A
1228 * @return char* The string (char array) with the content (Text) received from group 2A
1229 */
1230char *BK108X::getRdsText(void)
1231{
1232 return NULL;
1233}
1234
1235/**
1236 * @ingroup GA04
1237 * @todo Need to check Block B and Block E and RDS mode
1238 * @todo RDS Dynamic PS or Scrolling PS support
1239 * @brief Gets the Station Name and other messages.
1240 * @details Same getRdsStationName.
1241 * @return char* should return a string with the station name.
1242 * However, some stations send other kind of messages
1243 */
1245{
1246 static int rdsTextAdress0A;
1247 bk_rds_blockb blkb;
1248
1249 blkb.blockB = reg0d->raw;
1250
1251 if (blkb.group0.groupType == 0)
1252 {
1253 // Process group type 0
1254 rdsTextAdress0A = blkb.group0.address;
1255 if (rdsTextAdress0A >= 0 && rdsTextAdress0A < 4)
1256 {
1257 // strcpy(rds_buffer0A, "TO DO..."); // Need to check the device and RDS protocol.
1258 getNext2Block(&rds_buffer0A[rdsTextAdress0A * 2]);
1259 rds_buffer0A[8] = '\0';
1260 return rds_buffer0A;
1261 }
1262 }
1263 return NULL;
1264}
1265
1266/**
1267 * @ingroup @ingroup GA04
1268 *
1269 * @brief Gets the Text processed for the 2A group
1270 *
1271 * @return char* string with the Text of the group A2
1272 */
1274{
1275 static int rdsTextAdress2A;
1276 bk_rds_blockb blkb;
1277
1278 blkb.blockB = reg0d->raw;
1279 rdsTextAdress2A = blkb.group2.address;
1280
1281 if (blkb.group2.groupType == 2)
1282 {
1283 // Process group 2A
1284 // Decode B block information
1285 if (rdsTextAdress2A >= 0 && rdsTextAdress2A < 16)
1286 {
1287 getNext4Block(&rds_buffer2A[rdsTextAdress2A * 4]);
1288 rds_buffer2A[63] = '\0';
1289 return rds_buffer2A;
1290 }
1291 }
1292 return NULL;
1293}
1294
1295/**
1296 * @ingroup GA04
1297 * @brief Gets the Text processed for the 2B group
1298 * @return char* string with the Text of the group AB
1299 */
1301{
1302 static int rdsTextAdress2B;
1303 bk_rds_blockb blkb;
1304
1305 blkb.blockB = reg0d->raw;
1306 if (blkb.group2.groupType == 1)
1307 {
1308 // Process group 2B
1309 rdsTextAdress2B = blkb.group2.address;
1310 if (rdsTextAdress2B >= 0 && rdsTextAdress2B < 16)
1311 {
1312 getNext2Block(&rds_buffer2B[rdsTextAdress2B * 2]);
1313 return rds_buffer2B;
1314 }
1315 }
1316 return NULL;
1317}
1318
1319/**
1320 * @ingroup GA04
1321 * @brief Gets Station Name, Station Information, Program Information and utcTime
1322 * @details This function populates four char pointer variable parameters with Station Name, Station Information, Programa Information and UTC time.
1323 * @details You must call setRDS(true), setRdsFifo(true) before calling getRdsAllData(...)
1324 * @details ATTENTION: the parameters below are point to point to array of char.
1325 * @details the right way to call this function is shown below.
1326 * @code {.cpp}
1327 *
1328 * char *stationName, *stationInfo, *programInfo, *rdsTime;
1329 * // The char pointers above will be populate by the call below. So, the char pointers need to be passed by reference (pointer to pointer).
1330 * if (rx.getRdsAllData(&stationName, &stationInfo , &programInfo, &rdsTime) ) {
1331 * showProgramaInfo(programInfo);
1332 * showStationName(stationName);
1333 * showStationInfo(stationInfo);
1334 * showUtcTime(rdsTime);
1335 * }
1336 * @endcode
1337 * @param stationName (reference) - if NOT NULL, point to Name of the Station (char array - 9 bytes)
1338 * @param stationInformation (reference) - if NOT NULL, point to Station information (char array - 33 bytes)
1339 * @param programInformation (reference) - if NOT NULL, point to program information (char array - 65 nytes)
1340 * @param utcTime (reference) - if NOT NULL, point to char array containing the current UTC time (format HH:MM:SS +HH:MM)
1341 * @return True if found at least one valid data
1342 * @see setRDS, setRdsFifo, getRdsAllData
1343 */
1344bool BK108X::getRdsAllData(char **stationName, char **stationInformation, char **programInformation, char **utcTime)
1345{
1346
1347 if (!this->getRdsReady())
1348 return false;
1349 *stationName = this->getRdsText0A(); // returns NULL if no information
1350 *stationInformation = this->getRdsText2B(); // returns NULL if no information
1351 *programInformation = this->getRdsText2A(); // returns NULL if no information
1352 *utcTime = this->getRdsTime(); // returns NULL if no information
1353
1354 return (bool)stationName | (bool)stationInformation | (bool)programInformation | (bool)utcTime;
1355}
1356
1357/**
1358 * @ingroup GA04
1359 * @brief Gets the RDS time and date when the Group type is 4
1360 * @return char* a string with hh:mm +/- offset
1361 */
1363{
1364 bk_rds_date_time dt;
1365 word16_to_bytes blk_b, blk_c, blk_d;
1366 bk_rds_blockb blkb;
1367
1368 blk_b.raw = blkb.blockB = reg0d->raw; // Block B
1369 blk_c.raw = reg0e->raw; // Block C
1370 blk_d.raw = reg0f->raw; // Block D
1371
1372 uint16_t minute;
1373 uint16_t hour;
1374
1375 if (blkb.group0.groupType == 4)
1376 {
1377 char offset_sign;
1378 int offset_h;
1379 int offset_m;
1380
1381 // uint16_t y, m, d;
1382
1383 dt.raw[4] = blk_b.refined.lowByte;
1384 dt.raw[5] = blk_b.refined.highByte;
1385
1386 dt.raw[2] = blk_c.refined.lowByte;
1387 dt.raw[3] = blk_c.refined.highByte;
1388
1389 dt.raw[0] = blk_d.refined.lowByte;
1390 dt.raw[1] = blk_d.refined.highByte;
1391
1392 minute = dt.refined.minute;
1393 hour = dt.refined.hour;
1394
1395 offset_sign = (dt.refined.offset_sense == 1) ? '+' : '-';
1396 offset_h = (dt.refined.offset * 30) / 60;
1397 offset_m = (dt.refined.offset * 30) - (offset_h * 60);
1398
1399 // If wrong time, return NULL
1400 if (offset_h > 12 || offset_m > 60 || hour > 24 || minute > 60)
1401 return NULL;
1402
1403 this->convertToChar(hour, rds_time, 2, 0, ' ', false);
1404 rds_time[2] = ':';
1405 this->convertToChar(minute, &rds_time[3], 2, 0, ' ', false);
1406 rds_time[5] = ' ';
1407 rds_time[6] = offset_sign;
1408 this->convertToChar(offset_h, &rds_time[7], 2, 0, ' ', false);
1409 rds_time[9] = ':';
1410 this->convertToChar(offset_m, &rds_time[10], 2, 0, ' ', false);
1411 rds_time[12] = '\0';
1412
1413 return rds_time;
1414 }
1415
1416 return NULL;
1417}
1418
1419/**
1420 * @ingroup GA04
1421 * @todo Need to check.
1422 * @brief Gets the RDS time converted to local time.
1423 * @details ATTENTION: You must call getRdsReady before calling this function.
1424 * @details ATTENTION: Some stations broadcast wrong time.
1425 * @return char* a string with hh:mm
1426 * @see getRdsReady
1427 */
1429{
1430 bk_rds_date_time dt;
1431 word16_to_bytes blk_b, blk_c, blk_d;
1432 bk_rds_blockb blkb;
1433
1434 blk_b.raw = blkb.blockB = reg0d->raw; // Block B
1435 blk_c.raw = reg0e->raw; // Block C
1436 blk_d.raw = reg0f->raw; // Block D
1437
1438 uint16_t minute;
1439 uint16_t hour;
1440 uint16_t localTime;
1441
1442 if (blkb.group0.groupType == 4)
1443 {
1444 int offset_h;
1445 int offset_m;
1446
1447 dt.raw[4] = blk_b.refined.lowByte;
1448 dt.raw[5] = blk_b.refined.highByte;
1449
1450 dt.raw[2] = blk_c.refined.lowByte;
1451 dt.raw[3] = blk_c.refined.highByte;
1452
1453 dt.raw[0] = blk_d.refined.lowByte;
1454 dt.raw[1] = blk_d.refined.highByte;
1455
1456 minute = dt.refined.minute;
1457 hour = dt.refined.hour;
1458
1459 offset_h = (dt.refined.offset * 30) / 60;
1460 offset_m = (dt.refined.offset * 30) - (offset_h * 60);
1461
1462 localTime = (hour * 60 + minute);
1463 if (dt.refined.offset_sense == 1)
1464 localTime -= (offset_h * 60 + offset_m);
1465 else
1466 localTime += (offset_h * 60 + offset_m);
1467
1468 hour = localTime / 60;
1469 minute = localTime - (hour * 60);
1470
1471 if (hour > 24 || minute > 60)
1472 return NULL;
1473
1474 this->convertToChar(hour, rds_time, 2, 0, ' ', false);
1475 rds_time[2] = ':';
1476 this->convertToChar(minute, &rds_time[3], 2, 0, ' ', false);
1477 rds_time[5] = '\0';
1478
1479 return rds_time;
1480 }
1481
1482 return NULL;
1483}
1484
1485/**
1486 * @ingroup GA04
1487 * @brief Get the Rds Sync
1488 * @details Returns true if RDS currently synchronized.
1489 * @return true or false
1490 */
1492{
1493 return NULL;
1494}
1495
1496/**
1497 * @ingroup GA04
1498 * @brief Clear RDS Information (Station Name, Station Information, Program Information and Time)
1499 * @details Clear the buffer with latest RDS information
1500 */
1502{
1503 memset(rds_buffer0A, 0, sizeof(rds_buffer0A));
1504 memset(rds_buffer2A, 0, sizeof(rds_buffer2A));
1505 memset(rds_buffer2B, 0, sizeof(rds_buffer2B));
1506 memset(rds_time, 0, sizeof(rds_time));
1507}
1508
1509/**
1510 * @defgroup GA05 Tools
1511 * @section GA05 Tools / Helper
1512 */
1513
1514/**
1515 * @ingroup GA05 Check the I2C buss address
1516 * @brief Check the I2C bus address
1517 * @details For some reason, the BK1088 device does not work with the standard Wire.h library of Arduino.
1518 * @details The checkI2C function is only used to test the circuit.
1519 * @details In practice, no function from the Wire.h library is utilized in a real application with the BK1088 in this project.
1520 * @param uint8_t address Array - this array will be populated with the I2C bus addresses found (minimum three elements)
1521 * @return 0 if no i2c device is found; -1 if error is found or n > 0, where n is the number of I2C bus address found
1522 */
1523int BK108X::checkI2C(uint8_t *addressArray)
1524{
1525 Wire.begin();
1526 int error, address;
1527 int idx = 0;
1528 for (address = 1; address < 127; address++)
1529 {
1530 Wire.beginTransmission(address);
1531 error = Wire.endTransmission();
1532 if (error == 0)
1533 {
1534 addressArray[idx] = address;
1535 idx++;
1536 }
1537 else if (error == 4)
1538 return -1;
1539 }
1540 Wire.end();
1541 delay(200);
1542 return idx;
1543}
1544
1545/**
1546 * @ingroup GA05 Check the I2C buss address
1547 * @brief Returns the point of uint16_t array (size 32)
1548 * @details Useful to monitor the device internal registers
1549 * @return point of shadowRegisters
1550 */
1552 for (uint8_t i = 0; i < 32; i++ ) {
1553 this->getRegister(i); // Gets the current value of the internal register
1554 }
1555 return shadowRegisters;
1556}
1557
1558/**
1559 * @ingroup GA05 Covert numbers to char array
1560 * @brief Converts a number to a char array
1561 * @details It is useful to mitigate memory space used by functions like sprintf or othetr generic similar functions
1562 * @details You can use it to format frequency using decimal or tousand separator and also to convert smalm numbers.
1563 *
1564 * @param value value to be converted
1565 * @param strValue char array that will be receive the converted value
1566 * @param len final string size (in bytes)
1567 * @param dot the decimal or tousand separator position
1568 * @param separator symbol "." or ","
1569 * @param remove_leading_zeros if true removes up to two leading zeros (default is true)
1570 */
1571void BK108X::convertToChar(uint16_t value, char *strValue, uint8_t len, uint8_t dot, uint8_t separator, bool remove_leading_zeros)
1572{
1573 char d;
1574 for (int i = (len - 1); i >= 0; i--)
1575 {
1576 d = value % 10;
1577 value = value / 10;
1578 strValue[i] = d + 48;
1579 }
1580 strValue[len] = '\0';
1581 if (dot > 0)
1582 {
1583 for (int i = len; i >= dot; i--)
1584 {
1585 strValue[i + 1] = strValue[i];
1586 }
1587 strValue[dot] = separator;
1588 }
1589
1590 if (remove_leading_zeros)
1591 {
1592 if (strValue[0] == '0')
1593 {
1594 strValue[0] = ' ';
1595 if (strValue[1] == '0')
1596 strValue[1] = ' ';
1597 }
1598 }
1599}
#define REG12
Definition: BK108X.h:72
#define REG02
Definition: BK108X.h:56
#define REG0C
Definition: BK108X.h:66
#define REG0E
Definition: BK108X.h:68
#define REG07
Definition: BK108X.h:61
#define REG13
Definition: BK108X.h:73
#define REG17
Definition: BK108X.h:77
#define BK_MODE_AM
Definition: BK108X.h:48
#define REG15
Definition: BK108X.h:75
#define REG0D
Definition: BK108X.h:67
#define REG09
Definition: BK108X.h:63
#define REG10
Definition: BK108X.h:70
#define REG19
Definition: BK108X.h:79
#define REG0A
Definition: BK108X.h:64
#define REG1A
Definition: BK108X.h:80
#define REG1C
Definition: BK108X.h:82
#define REG00
Definition: BK108X.h:54
#define REG0B
Definition: BK108X.h:65
#define REG16
Definition: BK108X.h:76
#define REG05
Definition: BK108X.h:59
#define REG04
Definition: BK108X.h:58
#define REG1B
Definition: BK108X.h:81
#define REG1D
Definition: BK108X.h:83
#define REG0F
Definition: BK108X.h:69
#define REG11
Definition: BK108X.h:71
#define REG08
Definition: BK108X.h:62
#define REG03
Definition: BK108X.h:57
#define REG06
Definition: BK108X.h:60
#define REG18
Definition: BK108X.h:78
#define MAX_SEEK_TIME
Definition: BK108X.h:25
#define OSCILLATOR_TYPE_CRYSTAL
Definition: BK108X.h:27
#define REG14
Definition: BK108X.h:74
#define BK_MODE_FM
Definition: BK108X.h:47
#define REG01
Definition: BK108X.h:55
uint16_t getChipId()
Returns the Chip Indentifiction.
Definition: BK108X.cpp:317
char rds_buffer2A[65]
RDS Radio Text buffer - Program Information.
Definition: BK108X.h:788
char rds_buffer2B[33]
RDS Radio Text buffer - Station Informaation.
Definition: BK108X.h:789
uint16_t getDeviceId()
Returns the Device Indentifiction.
Definition: BK108X.cpp:307
char rds_buffer0A[9]
RDS Basic tuning and switching information (Type 0 groups)
Definition: BK108X.h:790
int deviceAddress
Definition: BK108X.h:795
char rds_time[20]
RDS date time received information.
Definition: BK108X.h:791
KT0915 Class.
Definition: BK108X.h:733
uint8_t i2cReceiveAck()
Gets Acknowledge (ACK)
Definition: BK108X.cpp:139
uint8_t i2cReadByte()
Gets a Byte from the slave device.
Definition: BK108X.cpp:184
uint16_t readRegister(uint8_t reg)
Gets an array of values from a BK108X given register.
Definition: BK108X.cpp:240
void i2cBeginTransaction()
Starts the I2C bus transaction.
Definition: BK108X.cpp:66
void i2cEndTransaction()
Finish the I2C bus transaction.
Definition: BK108X.cpp:85
void i2cWriteByte(uint8_t data)
Sends a Byte to the slave device.
Definition: BK108X.cpp:161
void writeRegister(uint8_t reg, uint16_t vakue)
Sends an array of values to a BK108X given register.
Definition: BK108X.cpp:211
void i2cNack()
Sends Not Acknowledge (ACK)
Definition: BK108X.cpp:121
void setI2C(uint8_t i2c_addr=I2C_DEVICE_ADDR)
Sets I2C bus address.
Definition: BK108X.cpp:43
void i2cAck()
Sends Acknowledge (ACK)
Definition: BK108X.cpp:105
void i2cInit(int pin_sdio, int pin_sclk)
Sets the MCU pins connected to the I2C bus.
Definition: BK108X.cpp:56
void reset()
Resets the device.
Definition: BK108X.cpp:371
uint16_t getChannel()
Gets the current channel.
Definition: BK108X.cpp:638
void seekHardware(uint8_t seek_mode, uint8_t direction, void(*showFunc)()=NULL)
Seeks a station via hardware functionality.
Definition: BK108X.cpp:746
void setSpace(uint8_t space=0)
Sets the Space channel for AM or FM.
Definition: BK108X.cpp:842
void seekSoftware(uint8_t seek_mode, uint8_t direction, void(*showFunc)()=NULL)
Seeks a station via Software.
Definition: BK108X.cpp:708
uint16_t getRealChannel()
Gets the current channel stored in register 0x0B.
Definition: BK108X.cpp:649
void waitAndFinishTune()
Wait STC (Seek/Tune Complete) status becomes 0.
Definition: BK108X.cpp:354
void setSoftMuteAttack(uint8_t value)
Sets Softmute Attack/Recover Rate.
Definition: BK108X.cpp:904
void setSeekThreshold(uint8_t rssiValue, uint8_t snrValue)
Sets RSSI and SNR Seek Threshold.
Definition: BK108X.cpp:792
void setAM(uint16_t minimum_frequency, uint16_t maximum_frequency, uint16_t default_frequency, uint16_t step, uint16_t am_space=0)
Sets the receiver to AM mode.
Definition: BK108X.cpp:526
int getSnr()
Gets the current SNR.
Definition: BK108X.cpp:871
void setAudioMute(bool left, bool right)
Sets the Mute true or false.
Definition: BK108X.cpp:975
void setFrequency(uint16_t frequency)
Sets the FM frequency.
Definition: BK108X.cpp:577
void setBand(uint8_t band=1)
Sets the current band for AM or FM.
Definition: BK108X.cpp:817
void setFrequencyDown()
Decrements the current frequency.
Definition: BK108X.cpp:613
void setAudioMute(bool value)
Sets the Mute true or false.
Definition: BK108X.cpp:988
void setSoftMute(bool value)
Sets the Softmute true or false.
Definition: BK108X.cpp:883
void setAfcMute(bool value)
Disable or Enable soft mute when AFCRL is high.
Definition: BK108X.cpp:962
bk_reg0a getStatus()
Gets the current status (register 0x0A) content.
Definition: BK108X.cpp:341
bool isStereo()
Checks stereo / mono status.
Definition: BK108X.cpp:1013
void setVolumeUp()
Increments the audio volume.
Definition: BK108X.cpp:1052
uint16_t getFrequency()
Gets the current frequency.
Definition: BK108X.cpp:628
void setSoftMuteAttenuation(uint8_t value)
Sets Softmute Attenuation.
Definition: BK108X.cpp:923
void powerDown()
Powers the receiver off.
Definition: BK108X.cpp:457
void setChannel(uint16_t channel)
Sets the channel.
Definition: BK108X.cpp:556
void setFM(uint16_t minimum_frequency, uint16_t maximum_frequency, uint16_t default_frequency, uint16_t step)
Sets the receiver to FM mode.
Definition: BK108X.cpp:496
void setVolume(uint8_t value)
Sets the audio volume level.
Definition: BK108X.cpp:1025
uint8_t getVolume()
Gets the current audio volume level.
Definition: BK108X.cpp:1042
void setMono(bool value)
Sets the Mono true or false (stereo)
Definition: BK108X.cpp:999
void setFrequencyUp()
Increments the current frequency.
Definition: BK108X.cpp:598
uint16_t getRealFrequency()
Gets the frequency based on READCHAN register (0x0B)
Definition: BK108X.cpp:662
void setVolumeDown()
Decrements the audio volume.
Definition: BK108X.cpp:1066
void powerUp()
Powers the receiver on.
Definition: BK108X.cpp:388
void setup(int sda_pin, int sclk_pin, uint8_t oscillator_type=OSCILLATOR_TYPE_CRYSTAL, uint32_t oscillator_frequency=32768)
Starts the device.
Definition: BK108X.cpp:477
int getRssi()
Gets the current Rssi.
Definition: BK108X.cpp:859
void setRegister(uint8_t reg, uint16_t value)
Sets a given value to the device registers.
Definition: BK108X.cpp:296
void setMuteThreshold(uint8_t rssi, uint8_t snr)
Set the Mute Threshold based on RSSI and SNR.
Definition: BK108X.cpp:937
void setSeekMute(bool value)
Disable or Enable soft mute when seeking.
Definition: BK108X.cpp:950
uint16_t getRegister(uint8_t reg)
Gets a givens current register content of the device.
Definition: BK108X.cpp:276
char * getRdsText0A(void)
Gets the Station Name and other messages.
Definition: BK108X.cpp:1244
bool getRdsReady()
Returns true if RDS Ready.
Definition: BK108X.cpp:1130
char * getRdsText(void)
Gets the RDS Text when the message is of the Group Type 2 version A.
Definition: BK108X.cpp:1230
char * getRdsText2B(void)
Gets the Text processed for the 2B group.
Definition: BK108X.cpp:1300
void getNext4Block(char *c)
Processes data received from group 2A.
Definition: BK108X.cpp:1216
uint8_t getRdsVersionCode(void)
Gets the version code (extracted from the Block B)
Definition: BK108X.cpp:1176
bool getRdsAllData(char **stationName, char **stationInformation, char **programInformation, char **utcTime)
Gets Station Name, Station Information, Program Information and utcTime.
Definition: BK108X.cpp:1344
char * getRdsTime()
Gets the RDS time and date when the Group type is 4.
Definition: BK108X.cpp:1362
uint16_t getRdsGroupType()
Return the group type - Gets the Group Type (extracted from the Block B)
Definition: BK108X.cpp:1162
uint8_t getRdsFlagAB(void)
Returns the current Text Flag A/B.
Definition: BK108X.cpp:1149
uint8_t getRdsProgramType(void)
Returns the Program Type (extracted from the Block B)
Definition: BK108X.cpp:1190
void getNext2Block(char *c)
Process data received from group 2B.
Definition: BK108X.cpp:1203
char * getRdsText2A(void)
Gets the Text processed for the 2A group.
Definition: BK108X.cpp:1273
void clearRdsBuffer()
Clear RDS Information (Station Name, Station Information, Program Information and Time)
Definition: BK108X.cpp:1501
bool getRdsSync()
Get the Rds Sync.
Definition: BK108X.cpp:1491
void setRdsMode(uint8_t rds_mode=0)
Sets the Rds Mode Standard or Verbose.
Definition: BK108X.cpp:1097
void getRdsStatus()
Gets the RDS registers information.
Definition: BK108X.cpp:1087
void setRds(bool value, bool interrupt_enable=false)
Sets the RDS operation.
Definition: BK108X.cpp:1111
char * getRdsLocalTime()
Gets the RDS time converted to local time.
Definition: BK108X.cpp:1428
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: BK108X.cpp:1571
uint16_t * getRegisterValues()
Returns the point of uint16_t array (size 32)
Definition: BK108X.cpp:1551
int checkI2C(uint8_t *addressArray)
Check the I2C bus address.
Definition: BK108X.cpp:1523