From 89da4328a07c8736c42843607a3f3bf91c17601d Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Sun, 7 Dec 2008 00:07:47 +0000 Subject: Meg F/X can beep and click using a hardware timer so let us try it out. To match things up better, fix PCM beeping to give correct frequency (and get a pointer wrap bug too). Do some minor adjustments to compensate for corrections. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19355 a1c6a512-1295-4272-9138-f99709370657 --- apps/action.c | 2 +- apps/pcmbuf.c | 27 +++--- apps/playback.c | 4 +- firmware/drivers/audio/wm8751.c | 11 +++ firmware/export/config-gigabeat.h | 5 + .../target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c | 12 +-- .../target/arm/s3c2440/gigabeat-fx/timer-target.h | 1 + .../arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c | 103 +++++++++++++++++++++ 8 files changed, 141 insertions(+), 24 deletions(-) diff --git a/apps/action.c b/apps/action.c index 5ceeeb896f..5f845ab272 100644 --- a/apps/action.c +++ b/apps/action.c @@ -130,7 +130,7 @@ static int get_action_worker(int context, int timeout, /* Produce keyclick */ if (global_settings.keyclick && !(button & BUTTON_REL)) if (!(button & BUTTON_REPEAT) || global_settings.keyclick_repeats) - pcmbuf_beep(5000, 2, 2500*global_settings.keyclick); + pcmbuf_beep(4000, 2, 2500*global_settings.keyclick); #endif if ((context != last_context) && ((last_button & BUTTON_REL) == 0)) diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c index 61c6c45de2..c7db4d3101 100644 --- a/apps/pcmbuf.c +++ b/apps/pcmbuf.c @@ -954,14 +954,15 @@ bool pcmbuf_insert_buffer(char *buf, int count) } #endif +#ifndef HAVE_HARDWARE_BEEP /* Generates a constant square wave sound with a given frequency in Hertz for a duration in milliseconds. */ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) { - unsigned int count = 0; - unsigned int i; - unsigned int interval = NATIVE_FREQUENCY / frequency; - unsigned int samples = NATIVE_FREQUENCY / 1000 * duration; + int i; + unsigned int step = 0xffffffffu / NATIVE_FREQUENCY * frequency; + int32_t phase = 0; + int samples = NATIVE_FREQUENCY / 1000 * duration; int32_t sample; int16_t *bufstart; int16_t *bufptr; @@ -986,21 +987,17 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) bufptr = bufstart; for (i = 0; i < samples; ++i) { + int32_t amp = (phase >> 31) ^ (int32_t)amplitude; sample = mix ? *bufptr : 0; - *bufptr++ = clip_sample_16(sample + amplitude); - if (bufptr > pcmbuf_end) + *bufptr++ = clip_sample_16(sample + amp); + if (bufptr >= pcmbuf_end) bufptr = (int16_t *)audiobuffer; sample = mix ? *bufptr : 0; - *bufptr++ = clip_sample_16(sample + amplitude); - if (bufptr > pcmbuf_end) + *bufptr++ = clip_sample_16(sample + amp); + if (bufptr >= pcmbuf_end) bufptr = (int16_t *)audiobuffer; - /* Toggle square wave edge */ - if (++count >= interval) - { - count = 0; - amplitude = -amplitude; - } + phase += step; } /* Kick off playback if required */ @@ -1009,7 +1006,7 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) pcm_play_data(NULL, (unsigned char *)bufstart, samples * 4); } } - +#endif /* HAVE_HARDWARE_BEEP */ /* Returns pcm buffer usage in percents (0 to 100). */ int pcmbuf_usage(void) diff --git a/apps/playback.c b/apps/playback.c index b21a3c1d46..50c4017200 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -694,7 +694,7 @@ static void audio_skip(int direction) if (playlist_check(ci.new_track + wps_offset + direction)) { if (global_settings.beep) - pcmbuf_beep(5000, 100, 2500*global_settings.beep); + pcmbuf_beep(2000, 100, 2500*global_settings.beep); LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", direction); queue_post(&audio_queue, Q_AUDIO_SKIP, direction); @@ -706,7 +706,7 @@ static void audio_skip(int direction) { /* No more tracks. */ if (global_settings.beep) - pcmbuf_beep(1000, 100, 1000*global_settings.beep); + pcmbuf_beep(1000, 100, 1500*global_settings.beep); } } diff --git a/firmware/drivers/audio/wm8751.c b/firmware/drivers/audio/wm8751.c index 9d552b505f..2e0eb06dbf 100644 --- a/firmware/drivers/audio/wm8751.c +++ b/firmware/drivers/audio/wm8751.c @@ -152,6 +152,17 @@ void audiohw_postinit(void) wmcodec_write(LEFTMIX1, LEFTMIX1_LD2LO | LEFTMIX1_LI2LO_DEFAULT); wmcodec_write(RIGHTMIX2, RIGHTMIX2_RD2RO | RIGHTMIX2_RI2RO_DEFAULT); +#ifdef TOSHIBA_GIGABEAT_F +#ifdef HAVE_HARDWARE_BEEP + /* Single-ended mono input */ + wmcodec_write(MONOMIX1, 0); + + /* Route mono input to both outputs at 0dB */ + wmcodec_write(LEFTMIX2, LEFTMIX2_MI2LO | LEFTMIX2_MI2LOVOL(2)); + wmcodec_write(RIGHTMIX1, RIGHTMIX1_MI2RO | RIGHTMIX1_MI2ROVOL(2)); +#endif +#endif + audiohw_mute(false); #ifdef MROBE_100 diff --git a/firmware/export/config-gigabeat.h b/firmware/export/config-gigabeat.h index 313bdadcdd..664befd54e 100644 --- a/firmware/export/config-gigabeat.h +++ b/firmware/export/config-gigabeat.h @@ -52,6 +52,11 @@ #define LCD_SLEEP_TIMEOUT (5*HZ) #define HAVE_TOUCHPAD_SENSITIVITY_SETTING + +#ifndef SIMULATOR +#define HAVE_HARDWARE_BEEP +#endif + #endif /* BOOTLOADER */ #define CONFIG_KEYPAD GIGABEAT_PAD diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c index 76917c8c82..5ef8c8023a 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c @@ -32,18 +32,18 @@ void tick_start(unsigned int interval_in_ms) * Timer input clock frequency = * fPCLK / {prescaler value+1} / {divider value} * TIMER_FREQ = 49156800 / 2 - * 13300 = TIMER_FREQ / 231 / 8 - * 49156800 = 19*(11)*(7)*7*5*5*(3)*2*2*2*2*2*2 - * 231 = 11*7*3 + * 146300 = TIMER_FREQ / 21 / 8 + * 49156800 = 19*11*(7)*7*5*5*(3)*2*2*2*2*2*2 + * 21 = 7*3 */ /* stop timer 4 */ TCON &= ~(1 << 20); /* Set the count for timer 4 */ - TCNTB4 = (TIMER_FREQ / 231 / 8) * interval_in_ms / 1000; + TCNTB4 = (TIMER_FREQ / TIMER234_PRESCALE / 8) * interval_in_ms / 1000; /* Set the the prescaler value for timers 2,3, and 4 */ - TCFG0 = (TCFG0 & ~0xff00) | ((231-1) << 8); - /* MUX4 = 1/16 */ + TCFG0 = (TCFG0 & ~0xff00) | ((TIMER234_PRESCALE-1) << 8); + /* DMA mode off, MUX4 = 1/16 */ TCFG1 = (TCFG1 & ~0xff0000) | 0x030000; /* set manual bit */ TCON |= 1 << 21; diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h b/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h index ac195bf9a5..b5652a3365 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h +++ b/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h @@ -23,6 +23,7 @@ /* timer is based on PCLK and minimum division is 2 */ #define TIMER_FREQ (49156800/2) +#define TIMER234_PRESCALE 21 bool __timer_set(long cycles, bool set); bool __timer_register(void); diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c index 957d58b344..de965f0750 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c @@ -29,8 +29,14 @@ #include "kernel.h" #include "sound.h" #include "i2c-meg-fx.h" +#include "system-target.h" +#include "timer-target.h" #include "wmcodec.h" +#ifdef HAVE_HARDWARE_BEEP +static void beep_stop(void); +#endif + void audiohw_init(void) { /* GPC5 controls headphone output */ @@ -39,6 +45,14 @@ void audiohw_init(void) GPCDAT |= (1 << 5); audiohw_preinit(); + +#ifdef HAVE_HARDWARE_BEEP + /* pin pullup ON */ + GPBUP &= ~(1 << 3); + beep_stop(); + /* set pin to TIMER3 output (functional TOUT3) */ + GPBCON = (GPBCON & ~(0x3 << 6)) | (2 << 6); +#endif } void wmcodec_write(int reg, int data) @@ -48,3 +62,92 @@ void wmcodec_write(int reg, int data) d[1] = data; i2c_write(0x34, d, 2); } + +#ifdef HAVE_HARDWARE_BEEP +/** Beeping via TIMER3 output to codec MONO input **/ +static int beep_cycles = 0; +static int beep_cycle_count = 0; + +static void beep_stop(void) +{ + int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); + + /* stop interrupt */ + INTMSK |= TIMER3_MASK; + /* stop timer */ + TCON &= ~(1 << 16); + /* be sure timer PWM pin is LOW to avoid noise */ + TCON ^= (GPBDAT & (1 << 3)) << 15; + /* clear pending */ + SRCPND = TIMER3_MASK; + INTPND = TIMER3_MASK; + + restore_interrupt(oldstatus); +} + +/* Timer interrupt called on every cycle */ +void TIMER3(void) +{ + if (++beep_cycles >= beep_cycle_count) + { + /* beep is complete */ + beep_stop(); + } + + /* clear pending */ + SRCPND = TIMER3_MASK; + INTPND = TIMER3_MASK; +} + +void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) +{ + #define TIMER3_TICK_SEC (TIMER_FREQ / TIMER234_PRESCALE) + + unsigned long tcnt, tcmp; + int oldstatus; + + if (amplitude <= 0) + { + beep_stop(); /* won't hear it anyway */ + return; + } + + /* pretend this is pcm */ + if (amplitude > 32767) + amplitude = 32767; + + /* limit frequency range to keep math in range */ + if (frequency > 19506) + frequency = 19506; + else if (frequency < 18) + frequency = 18; + + /* set timer */ + tcnt = TIMER3_TICK_SEC / frequency; + + oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); + + beep_cycles = 0; + beep_cycle_count = TIMER3_TICK_SEC * duration / (tcnt*1000); + + /* divider = 1/2 */ + TCFG1 = (TCFG1 & ~(0xf << 12)) | (0 << 12); + /* stop TIMER3, inverter OFF */ + TCON &= ~((1 << 18) | (1 << 16)); + /* set countdown */ + TCNTB3 = tcnt; + /* set PWM counter - control volume with duty cycle. */ + tcmp = tcnt*amplitude / (65536*2 - 2*amplitude); + TCMPB3 = tcmp < 1 ? 1 : tcmp; + /* manual update: on (to reset count), interval mode (auto reload) */ + TCON |= (1 << 19) | (1 << 17); + /* clear manual bit */ + TCON &= ~(1 << 17); + /* start timer */ + TCON |= (1 << 16); + /* enable timer interrupt */ + INTMSK &= ~TIMER3_MASK; + + restore_interrupt(oldstatus); +} +#endif /* HAVE_HARDWARE_BEEP */ -- cgit v1.2.3