bell.c 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /*
  2. * Bicycle Bell/Horn firmware
  3. * Copyright (C) 2015 Stuart Longland
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  18. */
  19. #define F_CPU 16000000UL
  20. #include <avr/io.h>
  21. #include <string.h>
  22. #include <util/delay.h>
  23. #include <stdint.h>
  24. #include <avr/pgmspace.h>
  25. #include <avr/interrupt.h>
  26. #include "bellsnd.h"
  27. #include "hornsnd.h"
  28. /*
  29. * Connections:
  30. * - Port B Pin 4: External Source (active low)
  31. * - Port B Pin 5: Green LED
  32. * - Port B Pin 6: Blue LED
  33. * - Port B Pin 7: Sound output (PWM)
  34. * - Port C Pin 7: Red LED
  35. * - Port D Pin 6: Power On (active low)
  36. * - Port D Pin 7: Mode
  37. * - Port E Pin 6: Bell (active low)
  38. *
  39. * Test points:
  40. * - Port C Pin 6: PWM Next Sample
  41. * - Port D Pin 4: Main loop
  42. * - Port D Pin 3: PWM Buffer Wait
  43. * - Port D Pin 2: PWM Buffer Switch
  44. * - Port D Pin 1: Bell down
  45. * - Port D Pin 0: Bell release
  46. * - Port F Pin 7: Bell state (main loop)
  47. */
  48. #define MODE_SW (PIND & (1 << DDB7))
  49. #define BELL_SW (!(PINE & (1 << DDB6)))
  50. #define EXT_SW (!(PINB & (1 << DDB4)))
  51. #define PWR_ON_PORT PORTD
  52. #define PWR_ON_PIN (1 << DDB6)
  53. #define RED_LED_PORT PORTC
  54. #define GREEN_LED_PORT PORTB
  55. #define BLUE_LED_PORT PORTB
  56. #define RED_LED_PIN (1 << DDB7)
  57. #define GREEN_LED_PIN (1 << DDB5)
  58. #define BLUE_LED_PIN (1 << DDB6)
  59. /*
  60. * Timer configuration. Freetronics picked GPIO B7 as their output for the
  61. * sound, which means we're stuck with either using timers 0 (8-bit) or 1
  62. * (16-bit). I could pick a different pin, but sod it, I've already wired up
  63. * the board so there's no going back now.
  64. *
  65. * Timers 0 and 1 are fed off the system clock, so we're stuck with a maximum
  66. * of 16MHz as the input frequency. (If we had Timer4 available to us, then
  67. * we've got 64MHz.) Timer 1 *can* do 16-bits, but at that resolution, it'll
  68. * give us a 244Hz carrier, which is utterly useless for audio. So we'll
  69. * suffer with 8-bits, which gives us a 62.5kHz carrier.
  70. *
  71. * Timer 0 is probably our easiest choice for this. Timer 1 can also do 8-bit
  72. * but why complicate things with 16-bit registers? Our output will be OC0A.
  73. * We will feed the samples into OCR0A.
  74. */
  75. #define T0_COMA (2) /* Output A: Clear on match */
  76. #define T0_COMB (0) /* Output B: Not used */
  77. #define T0_WGM (3) /* Fast PWM mode */
  78. #define T0_CS (1) /* 16MHz clock (no prescale) */
  79. #define T0_FCA (0) /* Do not force A */
  80. #define T0_FCB (0) /* Do not force B */
  81. #define TCCR0A_VAL \
  82. ( (T0_COMA << 6) \
  83. | (T0_COMB << 4) \
  84. | (T0_WGM & 0x3) )
  85. #define TCCR0B_VAL \
  86. ( (T0_FCA << 7) \
  87. | (T0_FCB << 6) \
  88. | ((T0_WGM & 0x04) << 1) \
  89. | T0_CS)
  90. /*
  91. * Timer 3 is well suited to give us a stable sample rate clock at sample
  92. * rates up to 32kHz. Multiples of 8kHz should be smack on (according to the
  93. * crystal), and multiples of 11025Hz should be less than 1% off. 48kHz will
  94. * be off, but you'd be insane to expect DVD-quality audio out of the PWM
  95. * output of an 8-bit micro.
  96. *
  97. * We set OCR3A according to the equation:
  98. * OCR3A = (F_CPU / f_sample) - 1
  99. *
  100. * The interrupt vector name is TIMER3_COMPA.
  101. */
  102. #define T3_COMA (0) /* Output A: Not used */
  103. #define T3_COMB (0) /* Output B: Not used */
  104. #define T3_COMC (0) /* Output C: Not used */
  105. #define T3_WGM (4) /* Mode: CTC */
  106. #define T3_CS (1) /* 16MHz clock (no prescale) */
  107. #define T3_FCA (0) /* Do not force A */
  108. #define T3_FCB (0) /* Do not force B */
  109. #define T3_FCC (0) /* Do not force C */
  110. #define T3_ICNC (0) /* Don't care */
  111. #define T3_ICES (0) /* Don't care */
  112. #define TCCR3A_VAL \
  113. ( (T3_COMA << 6) \
  114. | (T3_COMB << 4) \
  115. | (T3_COMC << 2) \
  116. | (T3_WGM & 0x3) )
  117. #define TCCR3B_VAL \
  118. ( (T3_ICNC << 7) \
  119. | (T3_ICES << 6) \
  120. | ((T3_WGM & 0xc) << 1) \
  121. | T3_CS)
  122. #define OCR3A_VAL(freq) \
  123. ((F_CPU / freq) - 1)
  124. #define TIMSK3_VAL (1 << 1)
  125. /*
  126. * The following is our output sample buffers, two buffers that get rotated
  127. * around (double-buffering) to ensure we're not writing to the one we're
  128. * reading from, buffer selector and the buffer pointer.
  129. */
  130. #define BUFFER_SZ 256
  131. #define BUFFER_NUM 2
  132. static volatile uint8_t pwm_buffer[BUFFER_NUM][BUFFER_SZ];
  133. static volatile uint8_t buffer_ready = 0;
  134. static volatile uint8_t buffer_sel = 0;
  135. static volatile uint8_t buffer_ptr = 0;
  136. static volatile uint8_t buffer_wait = 0;
  137. static volatile uint8_t pwm_on = 0;
  138. /*
  139. * That leaves us timer 1 for a tick counter, which we can tune as needed.
  140. * Best rate would be about 10Hz to give us 100msec time slices that we can
  141. * give us a nice tick counter. The primary aim of this will be to delay the
  142. * system power-down after the bell or external source signals are
  143. * de-asserted.
  144. *
  145. * We set OCR1A according to the equation:
  146. * OCR1A = (F_CPU / (1024*f_sample)) - 1
  147. *
  148. * This can be hard-coded since it will not be changing at runtime. The
  149. * interrupt vector name is TIMER1_COMPA.
  150. */
  151. #define T1_COMA (0) /* Output A: Not used */
  152. #define T1_COMB (0) /* Output B: Not used */
  153. #define T1_COMC (0) /* Output C: Not used */
  154. #define T1_WGM (4) /* Mode: CTC */
  155. #define T1_CS (5) /* 15.625kHz clock (16MHz / 1024) */
  156. #define T1_FCA (0) /* Do not force A */
  157. #define T1_FCB (0) /* Do not force B */
  158. #define T1_FCC (0) /* Do not force C */
  159. #define T1_ICNC (0) /* Don't care */
  160. #define T1_ICES (0) /* Don't care */
  161. #define T1_FREQ (10) /* 10Hz */
  162. #define TCCR1A_VAL \
  163. ( (T1_COMA << 6) \
  164. | (T1_COMB << 4) \
  165. | (T1_COMC << 2) \
  166. | (T1_WGM & 0x3) )
  167. #define TCCR1B_VAL \
  168. ( (T1_ICNC << 7) \
  169. | (T1_ICES << 6) \
  170. | ((T1_WGM & 0xc) << 1) \
  171. | T1_CS)
  172. #define OCR1A_VAL \
  173. ((F_CPU / (1024*T1_FREQ)) - 1)
  174. #define TIMSK1_VAL (1 << 1)
  175. /* This is our global tick counter variable */
  176. static volatile uint8_t system_tick = 0;
  177. /* LED state information */
  178. static volatile uint8_t led_colour = 0;
  179. /* Audio set-up routine, buffer 0 better be ready! */
  180. void start_audio(uint16_t sample_rate) {
  181. /* Stop interrupts momentarily */
  182. if (pwm_on) {
  183. /* We already have PWM running??? */
  184. RED_LED_PORT |= RED_LED_PIN;
  185. GREEN_LED_PORT &= ~GREEN_LED_PIN;
  186. BLUE_LED_PORT &= ~BLUE_LED_PIN;
  187. while(1);
  188. }
  189. cli();
  190. /* Set up buffer pointers */
  191. buffer_sel = 1;
  192. buffer_ptr = BUFFER_SZ-1;
  193. /* Set up timer 0 */
  194. OCR0A = UINT8_MAX/2;
  195. TCCR0A = TCCR0A_VAL;
  196. TCCR0B = TCCR0B_VAL;
  197. /* Set up timer 3 */
  198. OCR3A = OCR3A_VAL(sample_rate);
  199. TCCR3A = TCCR3A_VAL;
  200. TCCR3B = TCCR3B_VAL;
  201. TIMSK3 = TIMSK3_VAL;
  202. /* Resume interrupts */
  203. sei();
  204. /* Wait for audio interrupt to tick */
  205. //led_colour = 0xc;
  206. while(!pwm_on);
  207. }
  208. /* Audio tear-down routine */
  209. void stop_audio() {
  210. /* Stop interrupts momentarily */
  211. cli();
  212. //led_colour = 0xc;
  213. /* Re-set buffer pointers and PWM state */
  214. buffer_sel = 0;
  215. buffer_ptr = 0;
  216. buffer_ready = 0;
  217. buffer_wait = 0;
  218. pwm_on = 0;
  219. /* Silence output */
  220. OCR0A = UINT8_MAX/2;
  221. /* Stop timer 3 */
  222. TIMSK3 = 0;
  223. TCCR3A = 0;
  224. TCCR3B = 0;
  225. OCR3A = 0;
  226. /* Stop timer 0 */
  227. TCCR0A = 0;
  228. TCCR0B = 0;
  229. /* Clear buffers */
  230. memset(pwm_buffer, 0, sizeof(pwm_buffer));
  231. /* Resume interrupts */
  232. sei();
  233. }
  234. /* Write audio to the output buffer */
  235. uint16_t write_audio(const uint8_t* audio, uint16_t offset,
  236. uint16_t len, uint8_t is_ram, uint8_t loop) {
  237. /* Wait until the interrupt handler switches buffers */
  238. while(buffer_ready);
  239. /* Pick the buffer not being read */
  240. uint8_t buf_num = buffer_sel ? 0 : 1;
  241. uint16_t buf_rem = BUFFER_SZ;
  242. uint16_t buf_ptr = 0;
  243. uint16_t in_rem = len - offset;
  244. volatile uint8_t* out = pwm_buffer[buf_num];
  245. const uint8_t* in = &audio[offset];
  246. while(buf_rem && in_rem) {
  247. if (is_ram)
  248. *out = *in;
  249. else
  250. *out = pgm_read_byte(in);
  251. out++;
  252. buf_rem--;
  253. in++;
  254. in_rem--;
  255. if (loop && (!in_rem)) {
  256. in_rem = len;
  257. in = audio;
  258. }
  259. }
  260. /* Mark the buffer as ready */
  261. buffer_ready = 1;
  262. /* Return where we got to */
  263. return len - in_rem;
  264. }
  265. /* The loop point in the bell effect */
  266. #define BELL_LOOP_SZ (2054)
  267. /* Bell states */
  268. #define BELL_IDLE 0
  269. #define BELL_DOWN 1
  270. #define BELL_RELEASE 2
  271. #define BELL_STOP 3
  272. static volatile uint8_t bell_state = BELL_IDLE;
  273. static volatile uint8_t bell_released = 0;
  274. static volatile uint16_t bell_ptr = 0;
  275. static uint8_t bell_mode = 0;
  276. static const uint8_t* bell_snd = NULL;
  277. static uint16_t bell_loop_sz = 0;
  278. static uint16_t bell_sz = 0;
  279. static uint8_t bell_loop = 0;
  280. /* What do we do when the bell is idle? */
  281. void bell_idle(void) {
  282. if (BELL_SW) {
  283. /*
  284. * Someone has pressed the bell button, enter the "down"
  285. * state, load the initial buffer then start the audio.
  286. */
  287. bell_state = BELL_DOWN;
  288. bell_mode = MODE_SW;
  289. if (bell_mode) {
  290. bell_snd = horn;
  291. bell_loop_sz = HORN_LOOP_SZ;
  292. bell_sz = HORN_SZ - HORN_LOOP_OFFSET;
  293. } else {
  294. bell_snd = bell;
  295. bell_loop_sz = BELL_LOOP_SZ;
  296. bell_sz = BELL_SZ;
  297. }
  298. bell_ptr = write_audio(
  299. bell_snd, 0, bell_loop_sz, 0, 1);
  300. start_audio(BELL_RATE);
  301. }
  302. }
  303. /* What do we do while the button is held? */
  304. void bell_down(void) {
  305. if (BELL_SW) {
  306. /* The button is still down, is there room? */
  307. if (!buffer_ready) {
  308. /* There is, put some more dinging noises in */
  309. if (bell_mode && !bell_loop) {
  310. /*
  311. * We've played the initial part, now for
  312. * the rest.
  313. */
  314. bell_snd = &horn[HORN_LOOP_OFFSET];
  315. bell_loop_sz = HORN_LOOP_SZ - HORN_LOOP_OFFSET;
  316. bell_ptr -= HORN_LOOP_OFFSET;
  317. bell_sz -= HORN_LOOP_OFFSET;
  318. bell_loop = 1;
  319. }
  320. bell_ptr = write_audio(
  321. bell_snd, bell_ptr, bell_loop_sz,
  322. 0, 1);
  323. }
  324. } else {
  325. /* Button just released? Or switch bounce */
  326. bell_state = BELL_RELEASE;
  327. bell_released = system_tick;
  328. }
  329. }
  330. /* What do we do when the button is released? */
  331. void bell_release(void) {
  332. if (BELL_SW) {
  333. /* The button is bouncing */
  334. bell_state = BELL_DOWN;
  335. bell_down();
  336. } else if ((system_tick - bell_released) > 2) {
  337. /* I'll call this released. */
  338. bell_state = BELL_STOP;
  339. } else if (!buffer_ready) {
  340. /* Keep making the dinging noises in the meantime */
  341. bell_ptr = write_audio(
  342. bell_snd, bell_ptr, bell_loop_sz,
  343. 0, 1);
  344. }
  345. }
  346. /* Waiting for the final ding */
  347. void bell_stop(void) {
  348. if (bell_ptr < bell_sz) {
  349. if (!buffer_ready) {
  350. /* One more ding since there's room */
  351. bell_ptr = write_audio(
  352. bell_snd, bell_ptr, bell_sz, 0, 0);
  353. }
  354. } else {
  355. /* We're done, wait for the buffer to finish */
  356. while(!buffer_wait);
  357. stop_audio();
  358. bell_state = BELL_IDLE;
  359. }
  360. }
  361. /* Our main loop */
  362. int main(void) {
  363. /* Our last-activity time */
  364. uint8_t last_act = 0;
  365. /* Ensure interrupts are off */
  366. cli();
  367. /* Ensure audio is not running */
  368. pwm_on = 0;
  369. stop_audio();
  370. PORTB |= (1 << DDB0);
  371. /* Set up inputs */
  372. PORTE |= (1 << 6);
  373. PORTD |= (1 << 6);
  374. /* Set up outputs */
  375. DDRB |= (1 << DDB5)|(1 << DDB6)|(1 << DDB7)|(1 << DDB0);
  376. DDRC |= (1 << DDB7)|(1 << DDB6);
  377. DDRD |= (1 << DDB6)|(1 << DDB5)|
  378. (1 << DDB3)|(1 << DDB2)|(1 << DDB1)|(1 << DDB0);
  379. /* Hold the power on */
  380. PWR_ON_PORT |= PWR_ON_PIN;
  381. /* Set up Timer 1 */
  382. OCR1A = OCR1A_VAL;
  383. TIMSK1 = TIMSK1_VAL;
  384. TCCR1A = TCCR1A_VAL;
  385. TCCR1B = TCCR1B_VAL;
  386. /* Enable interrupts */
  387. sei();
  388. while(1) {
  389. if (bell_state == BELL_IDLE) {
  390. led_colour = 1;
  391. bell_idle();
  392. } else if (bell_state == BELL_DOWN) {
  393. led_colour = 3;
  394. bell_down();
  395. PORTD ^= (1 << DDB1);
  396. } else if (bell_state == BELL_RELEASE) {
  397. led_colour = 2;
  398. bell_release();
  399. PORTD ^= (1 << DDB0);
  400. } else {
  401. led_colour = 5;
  402. bell_stop();
  403. }
  404. /* Turn power off if self-powered */
  405. if (pwm_on)
  406. PWR_ON_PORT |= PWR_ON_PIN;
  407. else
  408. PWR_ON_PORT &= ~PWR_ON_PIN;
  409. }
  410. }
  411. ISR(TIMER1_COMPA_vect) {
  412. /*
  413. * Executed every tick to time things like shutdown delay and
  414. * to blink the LEDs.
  415. */
  416. uint8_t c = led_colour;
  417. system_tick++;
  418. if ((c & 8) && (!(system_tick & 0x02)))
  419. c = 0;
  420. if (c & 1)
  421. RED_LED_PORT |= RED_LED_PIN;
  422. else
  423. RED_LED_PORT &= ~RED_LED_PIN;
  424. if (c & 2)
  425. GREEN_LED_PORT |= GREEN_LED_PIN;
  426. else
  427. GREEN_LED_PORT &= ~GREEN_LED_PIN;
  428. if (c & 4)
  429. BLUE_LED_PORT |= BLUE_LED_PIN;
  430. else
  431. BLUE_LED_PORT &= ~BLUE_LED_PIN;
  432. }
  433. ISR(TIMER3_COMPA_vect) {
  434. /*
  435. * Executed to pull data from the buffer and stuff it into
  436. * the PWM output. We begin by reading the sample at the current
  437. * buffer location and writing that to PWM.
  438. */
  439. OCR0A = pwm_buffer[buffer_sel][buffer_ptr];
  440. /* Is this the end of the buffer? */
  441. if (buffer_ptr < (BUFFER_SZ-1)) {
  442. /* No, move on */
  443. buffer_ptr++;
  444. //led_colour = 0x4;
  445. PORTC ^= (1 << DDB6);
  446. /* It is, is the other buffer ready? */
  447. } else if (buffer_ready) {
  448. /* Swap */
  449. buffer_sel = buffer_sel ? 0 : 1;
  450. buffer_ptr = 0;
  451. buffer_ready = 0;
  452. buffer_wait = 0;
  453. PORTD ^= (1 << DDB2);
  454. /* We're waiting on a buffer */
  455. } else if (!buffer_wait) {
  456. buffer_wait = 1;
  457. //led_colour = 0x9;
  458. PORTD ^= (1 << DDB3);
  459. }
  460. pwm_on = 1;
  461. }