tile_player.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. /**
  2. * NOTICE
  3. *
  4. * Copyright 2019 Tile Inc. All Rights Reserved.
  5. * All code or other information included in the accompanying files ("Tile Source Material")
  6. * is PROPRIETARY information of Tile Inc. ("Tile") and access and use of the Tile Source Material
  7. * is subject to these terms. The Tile Source Material may only be used for demonstration purposes,
  8. * and may not be otherwise distributed or made available to others, including for commercial purposes.
  9. * Without limiting the foregoing , you understand and agree that no production use
  10. * of the Tile Source Material is allowed without a Tile ID properly obtained under a separate
  11. * agreement with Tile.
  12. * You also understand and agree that Tile may terminate the limited rights granted under these terms
  13. * at any time in its discretion.
  14. * All Tile Source Material is provided AS-IS without warranty of any kind.
  15. * Tile does not warrant that the Tile Source Material will be error-free or fit for your purposes.
  16. * Tile will not be liable for any damages resulting from your use of or inability to use
  17. * the Tile Source Material.
  18. *
  19. * Support: firmware_support@tile.com
  20. */
  21. /**
  22. * @file tile_player.c
  23. * @brief Tile player for Nordic Platform
  24. */
  25. #include "sdk_common.h"
  26. #if NRF_MODULE_ENABLED(TILE_SUPPORT)
  27. #include "tile_config.h"
  28. #include "tile_lib.h"
  29. uint8_t g_FindActivate_SongPlayed = 0;
  30. #if TILE_ENABLE_PLAYER
  31. #include <stdint.h>
  32. #include <stdbool.h>
  33. #include "tile_player.h"
  34. #include "tile_storage/tile_storage.h"
  35. #include "tile_service/tile_service.h"
  36. #include "tile_features/tile_features.h"
  37. #include "app_timer.h"
  38. #include "nrf_drv_gpiote.h"
  39. #include "nrf_drv_timer.h"
  40. #include "nrf_drv_ppi.h"
  41. #include "nrfx_ppi.h"
  42. #include "nrf_log.h"
  43. static nrf_ppi_channel_t ppi_channel1;
  44. uint32_t compare_evt_addr1;
  45. uint32_t gpiote_task_addr1;
  46. /* Set PIN_PIEZO for toggle on timer event */
  47. nrf_drv_gpiote_out_config_t config1 = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);
  48. /* Convert a frequency into number of microseconds for a half-pulse */
  49. #define CONV(n) (1000000 / (n) / 2)
  50. uint8_t g_inbetween = 0;
  51. /* Some defines for accessing the note array properly */
  52. #define NOTE_ARRAY_BASE_NOTE (C3)
  53. #define NOTE_ARRAY_MAX_NOTE (B8)
  54. #define NOTE_ARRAY_INDEX(n) ((int) (n) - NOTE_ARRAY_BASE_NOTE)
  55. /* Values for setting the PWM to the correct frequency for each note.
  56. * For our implementation, we only store the notes useful to us, which
  57. * is the range from C3 to B8 */
  58. static const uint16_t notes[] = {
  59. [NOTE_ARRAY_INDEX(C3)] = CONV(131),
  60. [NOTE_ARRAY_INDEX(CS3)] = CONV(138),
  61. [NOTE_ARRAY_INDEX(D3)] = CONV(147),
  62. [NOTE_ARRAY_INDEX(DS3)] = CONV(156),
  63. [NOTE_ARRAY_INDEX(E3)] = CONV(165),
  64. [NOTE_ARRAY_INDEX(F3)] = CONV(175),
  65. [NOTE_ARRAY_INDEX(FS3)] = CONV(185),
  66. [NOTE_ARRAY_INDEX(G3)] = CONV(196),
  67. [NOTE_ARRAY_INDEX(GS3)] = CONV(208),
  68. [NOTE_ARRAY_INDEX(A3)] = CONV(220),
  69. [NOTE_ARRAY_INDEX(AS3)] = CONV(233),
  70. [NOTE_ARRAY_INDEX(B3)] = CONV(247),
  71. [NOTE_ARRAY_INDEX(C4)] = CONV(262),
  72. [NOTE_ARRAY_INDEX(CS4)] = CONV(277),
  73. [NOTE_ARRAY_INDEX(D4)] = CONV(294),
  74. [NOTE_ARRAY_INDEX(DS4)] = CONV(311),
  75. [NOTE_ARRAY_INDEX(E4)] = CONV(330),
  76. [NOTE_ARRAY_INDEX(F4)] = CONV(349),
  77. [NOTE_ARRAY_INDEX(FS4)] = CONV(370),
  78. [NOTE_ARRAY_INDEX(G4)] = CONV(392),
  79. [NOTE_ARRAY_INDEX(GS4)] = CONV(415),
  80. [NOTE_ARRAY_INDEX(A4)] = CONV(440),
  81. [NOTE_ARRAY_INDEX(AS4)] = CONV(466),
  82. [NOTE_ARRAY_INDEX(B4)] = CONV(494),
  83. [NOTE_ARRAY_INDEX(C5)] = CONV(523),
  84. [NOTE_ARRAY_INDEX(CS5)] = CONV(554),
  85. [NOTE_ARRAY_INDEX(D5)] = CONV(587),
  86. [NOTE_ARRAY_INDEX(DS5)] = CONV(622),
  87. [NOTE_ARRAY_INDEX(E5)] = CONV(659),
  88. [NOTE_ARRAY_INDEX(F5)] = CONV(698),
  89. [NOTE_ARRAY_INDEX(FS5)] = CONV(740),
  90. [NOTE_ARRAY_INDEX(G5)] = CONV(784),
  91. [NOTE_ARRAY_INDEX(GS5)] = CONV(831),
  92. [NOTE_ARRAY_INDEX(A5)] = CONV(880),
  93. [NOTE_ARRAY_INDEX(AS5)] = CONV(932),
  94. [NOTE_ARRAY_INDEX(B5)] = CONV(988),
  95. [NOTE_ARRAY_INDEX(C6)] = CONV(1047),
  96. [NOTE_ARRAY_INDEX(CS6)] = CONV(1109),
  97. [NOTE_ARRAY_INDEX(D6)] = CONV(1175),
  98. [NOTE_ARRAY_INDEX(DS6)] = CONV(1245),
  99. [NOTE_ARRAY_INDEX(E6)] = CONV(1319),
  100. [NOTE_ARRAY_INDEX(F6)] = CONV(1397),
  101. [NOTE_ARRAY_INDEX(FS6)] = CONV(1480),
  102. [NOTE_ARRAY_INDEX(G6)] = CONV(1568),
  103. [NOTE_ARRAY_INDEX(GS6)] = CONV(1661),
  104. [NOTE_ARRAY_INDEX(A6)] = CONV(1760),
  105. [NOTE_ARRAY_INDEX(AS6)] = CONV(1865),
  106. [NOTE_ARRAY_INDEX(B6)] = CONV(1976),
  107. [NOTE_ARRAY_INDEX(C7)] = CONV(2093),
  108. [NOTE_ARRAY_INDEX(CS7)] = CONV(2217),
  109. [NOTE_ARRAY_INDEX(D7)] = CONV(2349),
  110. [NOTE_ARRAY_INDEX(DS7)] = CONV(2489),
  111. [NOTE_ARRAY_INDEX(E7)] = CONV(2637),
  112. [NOTE_ARRAY_INDEX(F7)] = CONV(2794),
  113. [NOTE_ARRAY_INDEX(FS7)] = CONV(2960),
  114. [NOTE_ARRAY_INDEX(G7)] = CONV(3136),
  115. [NOTE_ARRAY_INDEX(GS7)] = CONV(3322),
  116. [NOTE_ARRAY_INDEX(A7)] = CONV(3520),
  117. [NOTE_ARRAY_INDEX(AS7)] = CONV(3729),
  118. [NOTE_ARRAY_INDEX(B7)] = CONV(3951),
  119. [NOTE_ARRAY_INDEX(C8)] = CONV(4186),
  120. [NOTE_ARRAY_INDEX(CS8)] = CONV(4435),
  121. [NOTE_ARRAY_INDEX(D8)] = CONV(4699),
  122. [NOTE_ARRAY_INDEX(DS8)] = CONV(4978),
  123. [NOTE_ARRAY_INDEX(E8)] = CONV(5274),
  124. [NOTE_ARRAY_INDEX(F8)] = CONV(5588),
  125. [NOTE_ARRAY_INDEX(FS8)] = CONV(5920),
  126. [NOTE_ARRAY_INDEX(G8)] = CONV(6272),
  127. [NOTE_ARRAY_INDEX(GS8)] = CONV(6645),
  128. [NOTE_ARRAY_INDEX(A8)] = CONV(7040),
  129. [NOTE_ARRAY_INDEX(AS8)] = CONV(7459),
  130. [NOTE_ARRAY_INDEX(B8)] = CONV(7902),
  131. };
  132. const uint8_t FixedSong0[] = { C3, 1, REST, REST }; // Click Song
  133. const uint8_t FixedSong1[] = {
  134. D5, 3, FS5, 3, D5, 3, FS5, 3, D5, 3, FS5, 3, D5, 3, FS5, 3, D5, 3, FS5, 6,
  135. REST, 3, D6, 13, FS5, 13, G5, 13,
  136. A5, 13, D6, 9, REST, 4, A5, 6,
  137. REST, 6, A6, 6, REST, 6, A5, 6, REST, 19, FS6, 3,
  138. A6, 3, FS6, 3, A6, 3, FS6, 3, A6, 3, REST, 6, D6, 3, FS6, 3, D6, 3, FS6, 3,
  139. D6, 3, FS6, 3, REST, 6, G5, 3, B5, 3, G5, 3, B5, 3, G5, 3, B5, 3, G5, 3,
  140. B5, 3, G5, 3, B5, 6, REST, 3, G6, 13, B5, 13,
  141. C6, 13, D6, 13, G6, 9,
  142. REST, 4, D6, 6, REST, 6, D7, 6, REST, 6, D6, 6,
  143. REST, 19, B6, 3, D7, 3, B6, 3, D7, 3,
  144. B6, 3, D7, 3, B6, 3, D7, 6, REST, 22, A5, 3,
  145. CS6, 3, A5, 3, CS6, 3, A5, 3, CS6, 3, A5, 3, CS6, 3, A5, 3, CS6, 6, REST, 3, A6, 13,
  146. CS6, 13, D6, 13, E6, 13,
  147. A6, 9, REST, 4, E6, 6, REST, 6, E7, 6,
  148. REST, 6, E6, 6, REST, 19, CS7, 3,
  149. E7, 3, CS7, 3, E7, 3, CS7, 3, E7, 3, REST, 6, A6, 3, CS7, 3, A6, 3, CS7, 3,
  150. A6, 3, CS7, 3, REST, 6, D6, 3, FS6, 3, D6, 3, FS6, 3, D6, 3, FS6, 3, D6, 3,
  151. FS6, 3, D6, 3, FS6, 6, REST, 3, D7, 13, FS6, 13,
  152. G6, 13, A6, 13, D7, 9,
  153. REST, 4, A6, 6, REST, 6, A7, 6, REST, 6, A6, 6,
  154. REST, 19, FS7, 3, A7, 3, FS7, 3, A7, 3,
  155. FS7, 3, A7, 3, FS7, 3, A7, 6, REST, 11,
  156. REST, REST, REST, REST
  157. };
  158. const uint8_t FixedSong2[] = { // Active Song
  159. A5, 5, REST, 7, A6, 2, REST, 11, A5, 2, REST, 23, A5, 2, REST, 11, A6, 2, REST, 11, A5, 2, REST, 23, D6, 13, FS5, 13, G5, 13, A5, 13, D5, 26, D6, 14, REST, REST
  160. };
  161. const uint8_t FixedSong3[] = { // Sleep Song
  162. A6, 38, D6, 13, G6, 13, FS6, 13, D6, 13, A5, 10, REST, 3, D5, 5, REST, 7, D6, 2, REST, 11, D5, 2, REST, 23, D5, 2, REST, 11, D6, 2, REST, 11, D3, 2, REST, 1, REST, REST
  163. };
  164. const uint8_t FixedSong4[] = { // Wake Song
  165. D5,38,
  166. A5,13,
  167. FS5,13,
  168. G5,13,
  169. A5,13,
  170. D6,10,
  171. REST,3, A5,5,
  172. REST,7, A6,2,
  173. REST,11, A5,2,
  174. REST,23, A5,2,
  175. REST,11, A6,2,
  176. REST,11, A5,2,
  177. REST, REST };
  178. const uint8_t FixedSong5[] = { FS7,250, FS7,250, FS7,250, FS7,250, REST, REST }; /* Factory Song: For factory test song - 10 seconds of F#7 at 2960Hz */
  179. const uint8_t FixedSong6[] = { C8,1, CS8,1, D8,1, DS8,1, E8,1, F8,1, REST, REST };
  180. const uint8_t FixedSong7[] = { REST, REST }; /* Silent Song */
  181. const uint8_t FixedSong8[] = { REST, REST }; /* Button Song: currently silent */
  182. const uint8_t FixedSong9[] = { A5, 2, REST,11, A6, 2, REST, 11, A5, 2, REST,REST }; /* WakePart Song */
  183. const uint8_t FixedSong10[] = { FS4, 3, REST,10, D5, 11, REST, 2, A5, 3, REST, 10, D6, 12, REST,REST }; /* double tap success Song */
  184. const uint8_t FixedSong11[] = { GS4, 3, REST,10, GS5, 11, REST, 2, D4, 3, REST, 10, GS3, 12, REST,1, REST,REST }; /* double tap failure Song */
  185. const uint8_t FixedSong12[] = { /*C3, 1, REST, 10, C3, 1,*/ REST, REST }; /* 2 clicks song */
  186. const uint8_t FixedSong13[] = { D5, 30, REST, REST }; /* 1 bip Song */
  187. const uint8_t FixedSong14[] = { D5, 30, REST,11, D5, 30, REST, REST }; /* 2 bip Song */
  188. const uint8_t FixedSong15[] = { D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST, REST }; /* 3 bip Song */
  189. const uint8_t FixedSong16[] = { D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST, REST }; /* 4 bip Song */
  190. const uint8_t FixedSong17[] = { D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST, REST }; /* 5 bip Song */
  191. const uint8_t FixedSong18[] = { D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST, REST }; /* 6 bip Song */
  192. const uint8_t FixedSong19[] = { D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST,11, D5, 30, REST, REST }; /* 7 bip Song */
  193. const uint8_t *tile_song_array[] = {
  194. FixedSong0,
  195. FixedSong1,
  196. FixedSong2,
  197. FixedSong3,
  198. FixedSong4,
  199. FixedSong5,
  200. FixedSong6,
  201. FixedSong7,
  202. FixedSong8,
  203. FixedSong9,
  204. FixedSong10,
  205. FixedSong11,
  206. FixedSong12,
  207. FixedSong13,
  208. FixedSong14,
  209. FixedSong15,
  210. FixedSong16,
  211. FixedSong17,
  212. FixedSong18,
  213. FixedSong19,
  214. };
  215. static uint8_t startup_sequence = 0;
  216. static nrf_drv_timer_t player_timer = NRF_DRV_TIMER_INSTANCE(PLAYER_TIMER_ID);
  217. APP_TIMER_DEF(song_timer_id);
  218. APP_TIMER_DEF(song_timer_loop);
  219. static void NextNote(void);
  220. static void StartupPlayer(void);
  221. static song_t current_song = {0};
  222. static song_t next_song = {0};
  223. static uint8_t song_loop_flag = 1;
  224. static void song_timer_handler(void *p_context)
  225. {
  226. /* TODO: guard against calling NextNote when song is not playing */
  227. if(startup_sequence)
  228. {
  229. StartupPlayer();
  230. }
  231. else
  232. {
  233. NextNote();
  234. }
  235. }
  236. /**
  237. * @brief song_duration_timeout_handler
  238. * This is the interrupt handler for the song duration timer.
  239. * The idea is that the variable song_loop_flag is set to 1 to ensure
  240. * normal song_done() operation at all times. It is only set to 0 if
  241. * The song is requested to play forever. If it is requested to play for a
  242. * fixed duration, the timer allows the song_loop_flag to stay 0 until it times out.
  243. */
  244. static void song_duration_timeout_handler(void *p_context)
  245. {
  246. song_loop_flag = 1;
  247. }
  248. /**
  249. * @brief Player Boot Config
  250. * Allocate ppi Channels for Player once at Boot:
  251. * Call once at Boot, so we don't keep on assigning it again and again:
  252. * There are limited number of channels, and every channel alloc from same place does not alloc a new channel
  253. * It returns error: channel already allocated and moves on without apparent immediate problem, but assigned channels can go out of context,
  254. * allocate more than needed channels, and then stop playing song/advertise in some scenarios
  255. * So do not call alloc again and again
  256. */
  257. void tile_boot_config_player(void)
  258. {
  259. nrfx_err_t err_code;
  260. /* Initialize the gpiote driver if it isn't already */
  261. if(!nrf_drv_gpiote_is_init())
  262. {
  263. err_code = nrf_drv_gpiote_init();
  264. APP_ERROR_CHECK(err_code);
  265. }
  266. err_code = nrf_drv_ppi_channel_alloc(&ppi_channel1);
  267. NRF_LOG_INFO("ppi channel 1 alloc gave %u, &ppi_channel1: 0x%08x",err_code,&ppi_channel1);
  268. APP_ERROR_CHECK(err_code);
  269. err_code = nrf_drv_gpiote_out_init(PIN_PIEZO, &config1);
  270. APP_ERROR_CHECK(err_code);
  271. }
  272. /**
  273. * @brief Configure PPI/GPIOTE for the layer
  274. */
  275. static void ConfigureBuzzer()
  276. {
  277. uint32_t compare_evt_addr;
  278. uint32_t gpiote_task_addr;
  279. nrf_ppi_channel_t ppi_channel;
  280. (void) nrf_drv_ppi_channel_alloc(&ppi_channel);
  281. /* Set PIN_PIEZO for toggle on timer event */
  282. nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);
  283. (void) nrf_drv_gpiote_out_init(PIN_PIEZO, &config);
  284. /* Tie timer events to piezo toggle */
  285. compare_evt_addr = nrf_drv_timer_event_address_get(&player_timer, NRF_TIMER_EVENT_COMPARE0);
  286. gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get(PIN_PIEZO);
  287. (void) nrf_drv_ppi_channel_assign(ppi_channel, compare_evt_addr, gpiote_task_addr);
  288. (void) nrf_drv_ppi_channel_enable(ppi_channel);
  289. }
  290. /**
  291. * @brief Set the GPIO Player PIN as an Out PIN and Start Song after delay
  292. */
  293. static void StartupPlayer()
  294. {
  295. startup_sequence = 0;
  296. /* This is the start of the Find Default Song*/
  297. if (current_song.p_firstNote == tile_song_array[TILE_SONG_FIND])
  298. {
  299. g_FindActivate_SongPlayed = 1;
  300. }
  301. /* find length of the song */
  302. if (current_song.duration == TILE_SONG_DURATION_ONCE)
  303. {
  304. song_loop_flag = 1;
  305. }
  306. else if (current_song.duration == TILE_SONG_DURATION_FOREVER)
  307. {
  308. song_loop_flag = 0;
  309. }
  310. else
  311. {
  312. /* Create the timer for Song Loop */
  313. NRF_LOG_INFO("Song Request received for duration - %d", current_song.duration);
  314. song_loop_flag = 0;
  315. (void) app_timer_start(song_timer_loop, APP_TIMER_TICKS(current_song.duration * 1000), NULL);
  316. }
  317. nrf_gpio_pin_clear(PIN_PIEZO);
  318. nrf_drv_gpiote_out_task_enable(PIN_PIEZO);
  319. (void) app_timer_start(song_timer_id, APP_TIMER_TICKS(10), NULL);
  320. }
  321. /**
  322. * @brief Set the GPIO Player PIN to default state
  323. */
  324. static void ShutdownPlayer()
  325. {
  326. nrf_drv_gpiote_out_task_disable(PIN_PIEZO);
  327. nrf_gpio_pin_clear(PIN_PIEZO);
  328. }
  329. /**
  330. * @brief Nordic SDK requires an interrupt handler for Timer1, even though we do not need one
  331. */
  332. void timer_dummy_handler(nrf_timer_event_t event_type, void * p_context){}
  333. /**
  334. * @brief Function called at the end of a song.
  335. * Will start enqueued song if any, otherwise shut everything Off and notify application.
  336. */
  337. static void SongDone(void)
  338. {
  339. NRF_LOG_INFO("Song Done");
  340. ShutdownPlayer();
  341. (void) app_timer_stop(song_timer_loop);
  342. g_inbetween = 0;
  343. if(NULL != next_song.p_firstNote)
  344. {
  345. /* Start enqueued Song */
  346. current_song = next_song;
  347. memset(&next_song,0,sizeof(song_t));
  348. startup_sequence = 1;
  349. /* Give 200 ms between songs */
  350. (void) app_timer_start(song_timer_id, APP_TIMER_TICKS(200), NULL);
  351. }
  352. else
  353. {
  354. /* Shut everything Off */
  355. nrf_drv_timer_disable(&player_timer);
  356. (void) app_timer_stop(song_timer_id);
  357. UninitPlayer();
  358. /********************************************************/
  359. memset(&current_song,0,sizeof(song_t));
  360. if (g_FindActivate_SongPlayed == 1)
  361. {
  362. if( (nrf_fstorage_is_busy(&app_data_bank0) == false) && (nrf_fstorage_is_busy(&app_data_bank1) == false) ) // If flash activity on-going, wait
  363. {
  364. if (BLE_CONN_HANDLE_INVALID == tile_ble_env.conn_handle) // Disconnected
  365. {
  366. /* These will auto clear to 0 because of reboot, but still add it for logical purposes */
  367. g_FindActivate_SongPlayed = 0;
  368. }
  369. }
  370. }
  371. }
  372. }
  373. /**
  374. * @brief Play the next Note
  375. */
  376. static void NextNote(void)
  377. {
  378. uint8_t note = *current_song.p_currentNote++;
  379. uint8_t duration = *current_song.p_currentNote++;
  380. tile_unchecked->piezoMs += duration;
  381. nrf_drv_timer_disable(&player_timer);
  382. if(REST == note && REST == duration)
  383. {
  384. NRF_LOG_INFO("Song Duration: %d\r\n",tile_unchecked->piezoMs);
  385. if(song_loop_flag == 1)
  386. {
  387. /* End of song reached */
  388. SongDone();
  389. return;
  390. }
  391. else
  392. {
  393. if (g_inbetween == 0)
  394. {
  395. NRF_LOG_INFO("song loop len is Non0, g_inbetween is 0");
  396. /* Give 200 ms between songs*/
  397. (void) app_timer_start(song_timer_id, APP_TIMER_TICKS(200), NULL);
  398. current_song.p_currentNote -=2;
  399. g_inbetween = 1;
  400. }
  401. else
  402. {
  403. NRF_LOG_INFO("song loop len is Non0, g_inbetween is 1");
  404. /* Start one more loop */
  405. current_song.p_currentNote = current_song.p_firstNote;
  406. note = *current_song.p_currentNote++;
  407. duration = *current_song.p_currentNote++;
  408. tile_unchecked->piezoMs += duration;
  409. g_inbetween = 0;
  410. }
  411. }
  412. }
  413. /* We come here if we are in the middle of a song, or we are starting a new loop */
  414. if(REST == note)
  415. {
  416. /* reached a rest, disable the piezo pin and put it down */
  417. nrf_drv_gpiote_out_task_disable(PIN_PIEZO);
  418. nrf_gpio_pin_clear(PIN_PIEZO);
  419. }
  420. else
  421. {
  422. /* reached a note, set the Piezo Pin to toggle at the proper frequency */
  423. nrf_drv_timer_clear(&player_timer);
  424. nrf_drv_timer_extended_compare(&player_timer, GPIOTE_SOUND_CHANNEL, nrf_drv_timer_us_to_ticks(&player_timer, notes[NOTE_ARRAY_INDEX(note)]), NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
  425. nrf_drv_gpiote_out_task_enable(PIN_PIEZO);
  426. if (nrf_drv_timer_is_enabled(&player_timer) == 0)
  427. nrf_drv_timer_enable(&player_timer);
  428. }
  429. if (g_inbetween == 0)
  430. {
  431. (void) app_timer_start(song_timer_id, APP_TIMER_TICKS(duration*10), NULL);
  432. }
  433. }
  434. /**
  435. * @brief Initialize the Tile player
  436. */
  437. void InitPlayer(void)
  438. {
  439. /* The Tile player uses GPIOTE to toggle the piezo pin, and
  440. * Timer1 triggers the toggle using PPI */
  441. nrf_drv_timer_config_t timer_config = NRF_DRV_TIMER_DEFAULT_CONFIG;
  442. /* Configure timer */
  443. (void) nrf_drv_timer_init(&player_timer, &timer_config, timer_dummy_handler);
  444. ConfigureBuzzer();
  445. /* Create the timer for switching frequencies */
  446. (void) app_timer_create(&song_timer_id, APP_TIMER_MODE_SINGLE_SHOT, song_timer_handler);
  447. /* Create the timer for Song Loop */
  448. (void) app_timer_create(&song_timer_loop, APP_TIMER_MODE_SINGLE_SHOT, song_duration_timeout_handler);
  449. }
  450. /**
  451. * @brief Uninitialize the Tile player, for Power Save reasons
  452. * nrfx timer consumes ~0.5 mA current on Average
  453. */
  454. void UninitPlayer(void)
  455. {
  456. ret_code_t err_code;
  457. NRF_LOG_INFO("UninitPlayer");
  458. /* Do not call nrf_drv_ppi_uninit() or nrf_drv_gpiote_uninit() as they are used by other modules */
  459. nrf_drv_timer_uninit(&player_timer);
  460. err_code = nrf_drv_ppi_channel_disable(ppi_channel1);
  461. if (err_code != 0)
  462. NRF_LOG_INFO("ppi channel 1 disable gave error %u",err_code);
  463. // No Need to assert if Disable failed
  464. }
  465. /**
  466. * @brief Play a song. Queue song if necessary
  467. */
  468. int PlaySong(uint8_t number, uint8_t strength, uint8_t duration)
  469. {
  470. NRF_LOG_INFO("Play Song Request received");
  471. if(strength == 0 || duration == 0)
  472. {
  473. return TILE_ERROR_SUCCESS;
  474. }
  475. if(number < ARRAY_SIZE(tile_song_array))
  476. {
  477. if((NULL != current_song.p_firstNote) && (NULL == next_song.p_firstNote))
  478. {
  479. /* A song is currently playing but there is NO enqueued song */
  480. /* SO enqueue the song */
  481. NRF_LOG_INFO("Enqueue Default song");
  482. next_song = (song_t) { tile_song_array[number], tile_song_array[number], duration};
  483. }
  484. else if(NULL == current_song.p_firstNote)
  485. {
  486. /* no song is currently playing, start it right away */
  487. InitPlayer();
  488. song_loop_flag = 1;
  489. NRF_LOG_INFO("Play Default song");
  490. current_song = (song_t) { tile_song_array[number], tile_song_array[number], duration};
  491. startup_sequence = 1;
  492. StartupPlayer();
  493. }
  494. else
  495. {
  496. /* If queue is full, ignore */
  497. }
  498. }
  499. return TILE_ERROR_SUCCESS;
  500. }
  501. /**
  502. * @brief Stop currently playing song and remove enqueued songs
  503. */
  504. int StopSong(void)
  505. {
  506. /* Destroy the queue */
  507. if(next_song.p_firstNote != NULL)
  508. {
  509. memset(&next_song,0,sizeof(song_t));
  510. }
  511. /* Turn off the songs */
  512. if(current_song.p_firstNote != NULL)
  513. {
  514. song_loop_flag = 1;
  515. SongDone();
  516. }
  517. return TILE_ERROR_SUCCESS;
  518. }
  519. /**
  520. * @brief Return whether a song is playing or not
  521. */
  522. bool SongPlaying(void)
  523. {
  524. return current_song.p_firstNote != NULL;
  525. }
  526. /**
  527. * @brief Return whether a Find song is playing or not
  528. */
  529. bool CheckFindSong(void)
  530. {
  531. /*Return true if the current song note matches to the TILE_SONG_FIND note*/
  532. if ( (current_song.p_firstNote != NULL) &&
  533. (current_song.p_firstNote == tile_song_array[TILE_SONG_FIND]) )
  534. {
  535. return true;
  536. }
  537. return false;
  538. }
  539. #else
  540. void InitPlayer(void){}
  541. void UninitPlayer(void){}
  542. int PlaySong(uint8_t number, uint8_t strength, uint8_t duration){return TILE_ERROR_SUCCESS;}
  543. int StopSong(void){return TILE_ERROR_SUCCESS;}
  544. bool SongPlaying(void){return false;}
  545. void tile_boot_config_player(void){}
  546. #endif // TILE_ENABLE_PLAYER
  547. #endif // NRF_MODULE_ENABLED(TILE_SUPPORT)