TEA5767.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /**
  2. * @file TEA5767.c
  3. * @author Coolshrimp - CoolshrimpModz.com
  4. * @brief Library for controlling the TEA5767 FM radio chip.
  5. * @version 0.1
  6. * @date 2023-09-29
  7. *
  8. * @copyright GPLv3
  9. */
  10. #include <furi.h>
  11. #include <furi_hal.h>
  12. #include <furi_hal_gpio.h>
  13. #include <furi_hal_resources.h>
  14. #include <stdio.h> // Include necessary libraries
  15. #include "TEA5767.h"
  16. #define TIMEOUT_MS 100
  17. // Define a structure to store station information
  18. struct StationInfo {
  19. float frequency; // Frequency in MHz
  20. int signalLevel; // Signal level
  21. };
  22. // Helper function to acquire I2C
  23. static bool acquire_i2c() {
  24. // Acquire I2C and check for device readiness
  25. furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
  26. return furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, TEA5767_ADR, 5);
  27. }
  28. static void release_i2c() {
  29. // Release I2C
  30. furi_hal_i2c_release(&furi_hal_i2c_handle_external);
  31. }
  32. bool tea5767_is_device_ready() {
  33. bool result = acquire_i2c();
  34. release_i2c();
  35. return result;
  36. }
  37. bool tea5767_read_registers(uint8_t* buffer) {
  38. if(buffer == NULL) return false;
  39. bool result = acquire_i2c();
  40. if(result) {
  41. result =
  42. furi_hal_i2c_rx(&furi_hal_i2c_handle_external, TEA5767_ADR, buffer, 5, TIMEOUT_MS);
  43. }
  44. release_i2c();
  45. return result;
  46. }
  47. bool tea5767_write_registers(uint8_t* buffer) {
  48. bool result = false;
  49. if(buffer == NULL) return false; // Added NULL check
  50. furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
  51. result = furi_hal_i2c_tx(&furi_hal_i2c_handle_external, TEA5767_ADR, buffer, 5, TIMEOUT_MS);
  52. furi_hal_i2c_release(&furi_hal_i2c_handle_external);
  53. return result;
  54. }
  55. bool tea5767_init(uint8_t* buffer) {
  56. bool result = false;
  57. buffer[0] = 0x00;
  58. buffer[1] = 0x00;
  59. buffer[2] = 0xB0;
  60. buffer[3] = REG_4_XTAL | REG_4_SMUTE;
  61. buffer[4] = 0x00;
  62. result = tea5767_write_registers(buffer);
  63. return result;
  64. }
  65. bool tea5767_set_mute(uint8_t* buffer, bool mute) {
  66. bool result = false;
  67. if(mute) {
  68. buffer[REG_1] |= REG_1_MUTE;
  69. } else {
  70. buffer[REG_1] &= ~REG_1_MUTE;
  71. }
  72. result = tea5767_write_registers(buffer);
  73. return result;
  74. }
  75. bool tea5767_set_stereo(uint8_t* buffer, bool stereo) {
  76. bool result = false;
  77. if(stereo) {
  78. buffer[REG_3] &= ~REG_3_MS;
  79. } else {
  80. buffer[REG_3] |= REG_3_MS;
  81. }
  82. result = tea5767_write_registers(buffer);
  83. return result;
  84. }
  85. bool tea5767_seek(uint8_t* buffer, bool seek_up) {
  86. bool result = false;
  87. if(buffer == NULL) {
  88. return false;
  89. }
  90. buffer[REG_1] |= REG_1_SM; // Set the Search Mode (SM) bit to initiate seek
  91. if(seek_up) {
  92. buffer[REG_3] |= REG_3_SUD;
  93. } // Set Search Up (SUD) bit
  94. else {
  95. buffer[REG_3] &= ~REG_3_SUD;
  96. } // Set Search Down (SUD) bit
  97. buffer[REG_3] |=
  98. (REG_3_SSL |
  99. 0x60); // Set the Search Stop Level (SSL) to high for better tuning accuracy, set bit 7 for RSSI 7
  100. buffer[REG_3] &= ~REG_3_MS; // Set stereo mode (clearing the Mono bit)
  101. //buffer[REG_3] |= REG_4_SNC; // Set the Stereo Noise Cancelling (SNC) bit to 1 to reduce noise in stereo reception
  102. buffer[REG_4] &= ~REG_4_STBY; // Clear the Standby bit in register 4 to exit standby mode
  103. buffer[REG_4] &= ~REG_4_BL; // Limit FM band 87.5 - 108 MHz.
  104. buffer[REG_5] |=
  105. REG_5_PLLREF; // Set the PLLREF bit to 1 to enable the 6.5 MHz reference frequency for the PLL
  106. buffer[REG_5] |= REG_5_DTC; // Set the De-emphasis Time Constant (DTC) bit to 1 for 75 µs
  107. // Write the updated register values to the TEA5767
  108. result = tea5767_write_registers(buffer);
  109. return result;
  110. }
  111. bool tea5767_get_frequency(uint8_t* buffer, int* value) {
  112. bool result = false;
  113. uint16_t frequency;
  114. if(buffer == NULL || value == NULL) return false; //NULL check
  115. if(tea5767_read_registers(buffer)) {
  116. frequency = ((buffer[REG_1] & REG_1_PLL) << 8) | buffer[1];
  117. *value = (frequency * QUARTZ / 4 - FILTER) / 10000;
  118. result = true;
  119. }
  120. return result;
  121. }
  122. bool tea5767_set_frequency(uint8_t* buffer, int value) {
  123. bool result = false;
  124. if(buffer == NULL) {
  125. return false;
  126. }
  127. uint16_t frequency = 4 * (value * 10000 + FILTER) / QUARTZ;
  128. buffer[REG_1] =
  129. ((buffer[0] & ~REG_1_PLL) |
  130. ((frequency >> 8) & REG_1_PLL)); // Set the upper 8 bits of the PLL word
  131. buffer[REG_2] = frequency & REG_2_PLL; // Set the lower 8 bits of the PLL word
  132. buffer[REG_1] &= ~REG_1_MUTE; // Clear the Mute bit in register 1
  133. buffer[REG_3] &= ~REG_3_MS; // Set stereo mode (clearing the Mono bit)
  134. //buffer[REG_3] |= REG_4_SNC; // Set the Stereo Noise Cancelling (SNC) bit to 1 to reduce noise in stereo reception
  135. buffer[REG_4] &= ~REG_4_STBY; // Clear the Standby bit in register 4 to exit standby mode
  136. buffer[REG_4] &= ~REG_4_BL; // Limit FM band 87.5 - 108 MHz.
  137. buffer[REG_5] |=
  138. REG_5_PLLREF; // Set the PLLREF bit to 1 to enable the 6.5 MHz reference frequency for the PLL
  139. buffer[REG_5] |= REG_5_DTC; // Set the De-emphasis Time Constant (DTC) bit to 1 for 75 µs
  140. result = tea5767_write_registers(buffer);
  141. return result;
  142. }
  143. bool tea5767_get_radio_info(uint8_t* buffer, struct RADIO_INFO* info) {
  144. bool result = false;
  145. int frequency_khz;
  146. // Error handling: Check if buffer and info are not NULL
  147. if(buffer && info && tea5767_read_registers(buffer)) {
  148. if(buffer[REG_3] & REG_3_MS) {
  149. info->stereo = true;
  150. } else {
  151. info->stereo = false;
  152. }
  153. info->signalLevel = buffer[REG_4] >> 4;
  154. // Determine signal quality based on signal level
  155. if(info->signalLevel >= 0 && info->signalLevel <= 3) {
  156. strncpy(info->signalQuality, "Poor", sizeof(info->signalQuality));
  157. } else if(info->signalLevel >= 4 && info->signalLevel <= 7) {
  158. strncpy(info->signalQuality, "Fair", sizeof(info->signalQuality));
  159. } else if(info->signalLevel >= 8 && info->signalLevel <= 11) {
  160. strncpy(info->signalQuality, "Good", sizeof(info->signalQuality));
  161. } else if(info->signalLevel >= 12 && info->signalLevel <= 15) {
  162. strncpy(info->signalQuality, "Excellent", sizeof(info->signalQuality));
  163. } else {
  164. strncpy(info->signalQuality, "Unknown", sizeof(info->signalQuality));
  165. }
  166. // Now get the frequency
  167. if(tea5767_get_frequency(buffer, &frequency_khz)) {
  168. info->frequency = frequency_khz / 100.0f; // Convert kHz to MHz
  169. result = true; // Only return true if both read_registers and get_frequency succeeded
  170. }
  171. // Check if the radio is muted
  172. if(buffer[REG_1] & REG_1_MUTE) {
  173. info->muted = true;
  174. } else {
  175. info->muted = false;
  176. }
  177. }
  178. return result;
  179. }
  180. void tea5767_seekUp() {
  181. //Get CUrrent Station
  182. double fm_frequency = tea5767_GetFreq();
  183. int targetFrequencyKHz = fm_frequency * 100;
  184. uint8_t buffer[5];
  185. if(tea5767_init(buffer)) {
  186. tea5767_set_frequency(buffer, targetFrequencyKHz);
  187. // Start seeking upwards
  188. tea5767_seek(buffer, true);
  189. }
  190. }
  191. void tea5767_seekDown() {
  192. //Get CUrrent Station
  193. double fm_frequency = tea5767_GetFreq();
  194. int targetFrequencyKHz = fm_frequency * 100;
  195. uint8_t buffer[5];
  196. if(tea5767_init(buffer)) {
  197. tea5767_set_frequency(buffer, targetFrequencyKHz);
  198. // Start seeking upwards
  199. tea5767_seek(buffer, false);
  200. }
  201. }
  202. void tea5767_ToggleMute() {
  203. uint8_t buffer[5];
  204. if(tea5767_read_registers(buffer)) {
  205. if((buffer[REG_1] & REG_1_MUTE) == 0) {
  206. tea5767_set_mute(buffer, true);
  207. } else {
  208. tea5767_set_mute(buffer, false);
  209. }
  210. }
  211. }
  212. void tea5767_MuteOn() {
  213. uint8_t buffer[5];
  214. if(tea5767_read_registers(buffer)) { // Read the current state into the buffer
  215. tea5767_set_mute(buffer, true); // Set the mute bit
  216. }
  217. }
  218. void tea5767_MuteOff() {
  219. uint16_t frequency;
  220. float value; // Changed to a float variable, not a pointer
  221. uint8_t buffer[5];
  222. if(tea5767_read_registers(buffer)) { // Read the current state into the buffer
  223. tea5767_set_mute(buffer, false); // Clear the mute bit
  224. frequency = ((buffer[0] & REG_1_PLL) << 8) | buffer[1];
  225. value = (float)(frequency * QUARTZ / 4 - FILTER) / 10000; // Explicitly cast to float
  226. tea5767_SetFreqMHz(value / 100.0); // Pass the float value, not the pointer
  227. }
  228. }
  229. void tea5767_SetFreqKHz(int freq_khz) {
  230. uint8_t buffer[5];
  231. if(tea5767_init(buffer)) {
  232. tea5767_set_frequency(buffer, freq_khz);
  233. }
  234. }
  235. void tea5767_SetFreqMHz(float freq_mhz) {
  236. uint8_t buffer[5];
  237. if(tea5767_init(buffer)) {
  238. int freq_khz = (int)(freq_mhz * 100.0); // Convert MHz to kHz
  239. tea5767_set_frequency(buffer, freq_khz);
  240. }
  241. }
  242. float tea5767_GetFreq() {
  243. uint8_t buffer[5];
  244. int value;
  245. if(tea5767_get_frequency(buffer, &value)) {
  246. return value / 100.0; // Convert to MHz
  247. }
  248. return -1; // Error
  249. }
  250. void tea5767_sleep(uint8_t* buffer) {
  251. if(tea5767_read_registers(buffer)) {
  252. buffer[REG_4] |= REG_4_STBY; // Set the Standby bit in register 4 to enter standby mode
  253. tea5767_write_registers(buffer);
  254. }
  255. }