16bit resolution PWM wave player with SD card, super lite version.
Dependencies: mbed DirectSPI FastPWM
Supported boards (confirmed):
Nucleo-F030R8
Nucleo-L152RE
Nucleo-F401RE
Nucleo-F411RE
Only compilation is OK (unchecked, but may work):
Nucleo-L073RZ
Nucleo-F334R8
Nucleo-F303RE
Nucleo-F429ZI
Nucleo-F446RE
Nucleo-F446ZE
Nucleo-L476RG
Supported SD card:
SDSC/SDHC card,
FAT16 and FAT32.
(1) At first, format SD card using SD Card Formatter
https://www.sdcard.org/downloads/formatter_4/index.html
(2) Copy PCM wav files to the SD card.
Supported file:
PCM wave file that have file extension ".wav" on root directory.
16bit/8bit, fs(sampling rate)=32kHz,44.1kHz,48kHz.
Stereo/Mono.
Hardware setting:
Refer to the file port_setting.txt
PWM output port:
Left upper(Hi) PWM 8bit out: PB_5 (TM3_CH2)
Right upper(Hi) PWM 8bit out: PB4 (TM3_CH1)
Left lower(Low) PWM 8bit out: PC_9 (TM3_CH4)
Right lower(Low) PWM 8bit out: PC_8 (TM3_CH3)
USER_BUTTON: PC_13(default button)
Next song: One click in Play mode.
Pause : Push long time .
Play : One click from Pause.
wave_player_main.cpp
- Committer:
- mimi3
- Date:
- 2020-10-09
- Revision:
- 20:c7066a530b1f
- Parent:
- 19:54aed6200a35
- Child:
- 21:dc161a192ba7
File content as of revision 20:c7066a530b1f:
#include "sys.h" #include "sd_card.h" #include "fat_lib.h" #include "pwm_lib.h" #include "timers.h" #define CH_STEREO 2 // 2:STEREO, 1:MONO #define wREAD_COUNT 512 #define bHEADER_COUNT 44 #define send_ff() spi_read() #define send_ff16() spi_read16() #define CUT_LAST_TAG_NOISE (30*1000) #if (HAVE_LED_IND_PWM==1) || (HAVE_LED_IND_BLINK==1) DigitalOut led(IND_LED); #endif #define ind_on() (led = 1) #define ind_off() (led = 0) #define pwm_period_timer_music_start() ( pwm_period_timer_start(),fPlaying = true) #define pwm_period_timer_music_stop() ( pwm_period_timer_stop(), fPlaying = false) /****************** varialble definitions *******************/ #define LED_PERIOD_PLAYNG 75 //[ * 10 msec] #define LED_PERIOD_PAUSING 10 //[ * 10 msec] dword ldwSongFileSectors; dword ldwSample_freq; word lwReadCount; byte lbSample_bits = 16; byte lbCh_mode = CH_STEREO; byte bGainNormalizeFactor; bool fPlaying = false; DigitalIn btn_bit_now(USER_BUTTON,PullUp); #if TEST_PORT_ENABLE DigitalOut test_port(TEST_PORT); #endif /********************** Period Timer Interrupt **********************/ /* HAL callback for PWM Timer Intrrupt * */ void OPT_SPEED pwmPeriodIntr( void ){ if(TIM_PWM->SR & TIM_SR_UIF){ #if TEST_PORT_ENABLE test_port =1; #endif #if PWM16BIT byte bL_low,bL_hi,bR_low,bR_hi; if( lbSample_bits == 16 ) { bL_low = bR_low = send_ff(); bL_hi = bR_hi = send_ff() + 0x80; lwReadCount -= 2; if( lbCh_mode == CH_STEREO ) { bR_low = send_ff(); bR_hi = send_ff() + 0x80; lwReadCount -= 2; } } #if DATA_8BIT_SUPPORT else { /* 8bit data */ bL_hi = bR_hi = send_ff(); bL_low = 0; bR_low = 0; lwReadCount -= 1; if( lbCh_mode == CH_STEREO ) { /* for stereo */ bR_hi = send_ff(); bR_low = 0; lwReadCount -= 1; } } #endif //# Gain up dword dwL = (((word)bL_hi)<<8) + bL_low; dword dwR = (((word)bR_hi)<<8) + bR_low; dwL <<= bGainNormalizeFactor; dwR <<= bGainNormalizeFactor; //# change pwm duties pwm_dutyL_hi( dwL >> 8); pwm_dutyL_low( dwL & 0xff); pwm_dutyR_hi( dwR >> 8); pwm_dutyR_low( dwR & 0xff); #else // not PWM16BIT word wL,wR; if( lbSample_bits == 16 ) { wL = wR = ((word)(send_ff() + (send_ff()<<8) + 0x8000))>>gPcmShifNum; lwReadCount -= 2; if( lbCh_mode == CH_STEREO ) { wR = (word)(send_ff() + (send_ff()<<8) + 0x8000)>>gPcmShifNum; lwReadCount -= 2; } } #if DATA_8BIT_SUPPORT else { /* 8bit data */ wL = wR = send_ff() << bGainNormalizeFactor; lwReadCount -= 1; if( lbCh_mode == CH_STEREO ) { /* for stereo */ wR = send_ff() << bGainNormalizeFactor; lwReadCount -= 1; } } #endif //# change pwm duties pwm_dutyL_hi( wL ) ; pwm_dutyR_hi( wR ) ; #endif //; check 512byte sector boundery if( lwReadCount == 0){ //; end 1 sector send_ff() ;// dummy read 1st. discard CRC send_ff() ;// 2nd. while( send_ff() != 0xFE) {} lwReadCount = wREAD_COUNT; ldwSongFileSectors -= 1; if(ldwSongFileSectors == 0){ pwm_period_timer_music_stop(); } } #if TEST_PORT_ENABLE test_port =0; #endif TIM_PWM->SR &= (~TIM_SR_UIF); } } /****************** wave_player_main *******************/ void wave_player_main(){ bGainNormalizeFactor = calcPcmValidBits() - 8; word wBtnLowCount = 0; #if HAVE_LED_IND_BLINK byte bTimeout_led = 0; #endif sbit btn_bit_prev = true, btn_short_on = false; sbit btn_long_on = false; #if HAVE_POWER_OFF_MODE btn_long_on2 = false; #endif sbit btn_pause_prev = false; #define fbtn_next_song_on btn_short_on #define btn_pause_on btn_long_on #define btn_power_off_on btn_long_on2 #if HAVE_LED_IND_PWM // pseudo PWM setting const int8_t IND_PERIOD = 125; const int8_t IND_DUTY_LOW_SPEED = 1; const int8_t IND_DUTY_HI_SPEED = 3; int8_t sbIndDuty = 0; int8_t sbIndCurrPos = 0; int8_t sbIndSpeed = IND_DUTY_LOW_SPEED; int8_t sbIndDelta = sbIndSpeed; #endif init_tickTimer(); while(1){ //; wait 10msec Ticker flag while ( !isTickTimer_IF() ) { if( ldwSongFileSectors == 0 ){ //; found end of file break; //promptly exit and prepare next song } #if HAVE_LED_IND_PWM /*# --------------------- # pseudo PWM for LED # ---------------------*/ if(sbIndCurrPos < sbIndDuty) { ind_on();} else{ ind_off();} sbIndCurrPos++; if(sbIndCurrPos == IND_PERIOD){ sbIndCurrPos = 0; } #endif } //; end. wait 10msec Ticker flag //------------------- tickTimer_IF_clear(); //------------------- /*------------------- Next song and start -------------------*/ if ( ( ldwSongFileSectors == 0 ) | fbtn_next_song_on ){ pwm_period_timer_music_stop(); fbtn_next_song_on = false; sd_stop_read(); /*# ------------------ # Search next song # ------------------*/ searchNextFile(); //## Seek to Target file sector sd_start_read( gdwTargetFileSector ); //; delete about last 30Kbyte to cut tag data in *.wav file ldwSongFileSectors = ( (gdwBPB_FileSize - CUT_LAST_TAG_NOISE) )>>9 ; //512 , change to sector /*# ------------------ # Get wav header info # ------------------*/ sd_read_pulse_byte(22); // pos(22) ## Skip part of WAV header lbCh_mode = sd_data_byte();// pos(23) sd_data_byte(); // pos(24) # dummy read ldwSample_freq = sd_data_byte() + (sd_data_byte() << 8) + (sd_data_byte() << 16) + (sd_data_byte() << 24); // pos(28) sd_read_pulse_byte(6); // pos(34) ## Skip some headers lbSample_bits = sd_data_byte(); // pos(35) sd_read_pulse_byte(9) ; // pos(44) ## Skip to last position of header /*# ------------------ # Set sampling frequency # ------------------*/ setPwmPeriod(ldwSample_freq); /*# ------------------ # Music start # ------------------*/ lwReadCount = wREAD_COUNT - bHEADER_COUNT; pwm_period_timer_music_start(); } /*------------------- LED indicator 1 --- pseudo PWM -------------------*/ #if HAVE_LED_IND_PWM sbIndDuty += sbIndDelta; if(sbIndDuty > IND_PERIOD){ sbIndDelta = -1 * sbIndSpeed;} if(sbIndDuty == 0){ sbIndDelta = sbIndSpeed;} if(fPlaying){ sbIndSpeed = IND_DUTY_LOW_SPEED;} else{ sbIndSpeed = IND_DUTY_HI_SPEED;} #endif /*------------------- LED indicator 2 --- simple ON/OFF -------------------*/ #if HAVE_LED_IND_BLINK if (bTimeout_led == 0){ if( fPlaying ){ bTimeout_led = LED_PERIOD_PLAYNG ;// during Playing, on/off led = !led; }else{ #if HAVE_LED_PAUSE_INDICATOR bTimeout_led = LED_PERIOD_PAUSING ;// during Pause, on/off led = !led; #else bTimeout_led = 1; led = 0; #endif } } bTimeout_led = bTimeout_led - 1; #endif /*------------------- button sw input -------------------*/ #if HAVE_BUTTON_SW if( btn_bit_prev ^ btn_bit_now) { if (btn_bit_now){ // ; 0 --> 1: btn released if( (wBtnLowCount > 10) && (wBtnLowCount < 130)){// ; 100msec < x < 1.3sec btn_short_on = true; } wBtnLowCount = 0; } } btn_bit_prev = btn_bit_now; if (btn_bit_now == false){ wBtnLowCount = wBtnLowCount + 1; } //; recognized pause on if (wBtnLowCount > 120) { // ; 1.2sec > btn_long_on = true; } #if HAVE_POWER_OFF_MODE if (wBtnLowCount > 400){ //; 4sec > //; "long on2" is meaning go to sleep mode btn_long_on2 = true; } #endif /*------------------- Pause release -------------------*/ if( fbtn_next_song_on){ if( !fPlaying){ // ; if during pause fPlaying = true ; //release pause fbtn_next_song_on = false; btn_pause_on = false; pwm_period_timer_music_start(); } } /*------------------- Pause enter -------------------*/ if( btn_pause_prev ^ btn_pause_on ){ if( btn_pause_on ){ fPlaying = false; pwm_period_timer_music_stop(); } } btn_pause_prev = btn_pause_on; #endif } //; [forever loop end] }