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.
Diff: wave_player_main.cpp
- Revision:
- 0:9a5c2fb0d96f
- Child:
- 5:4aa4cc29d5ef
diff -r 000000000000 -r 9a5c2fb0d96f wave_player_main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wave_player_main.cpp Tue Jan 15 12:45:24 2019 +0000 @@ -0,0 +1,351 @@ +#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_INDICATOR==1) || (HAVE_LED_INDICATOR_2==1) + DigitalOut led(IND_LED); +#endif + +/****************** + 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 bVolumeUpFactor; +sbit fPlaying; +sbit lfEndOneSong = true ; //to start music automatically + +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 HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) +{ + if( htim->Instance == TIM_PWM ) { + #if TEST_PORT_ENABLE + test_port =1; + #endif + fPlaying = true; + #if PWM16BIT + word wL_low,wL_hi,wR_low,wR_hi; + dword dw; + if( lbSample_bits == 16 ) { + dw = ((word)(send_ff() + (send_ff()<<8) + 0x8000)) << bVolumeUpFactor; + wL_low = wR_low = (dw & 0xff); // get lower 8bit + wL_hi = wR_hi = (dw >>8); // get upper 10bit + lwReadCount -= 2; + if( lbCh_mode == CH_STEREO ) { + dw = ((word)(send_ff() + (send_ff()<<8) + 0x8000)) << bVolumeUpFactor; + wR_low = (dw & 0xff); + wR_hi = (dw >> 8); + lwReadCount -= 2; + } + } + #if DATA_8BIT_SUPPORT + else { /* 8bit data */ + wL_hi = wR_hi = send_ff() << bVolumeUpFactor; // volume up to 10bit (x4) from 8bit + wL_low = 0; + wR_low = 0; + lwReadCount -= 1; + if( lbCh_mode == CH_STEREO ) { /* for stereo */ + wR_hi = send_ff() << 2; + wR_low = 0; + lwReadCount -= 1; + } + } + #endif + //# change pwm duties + pwm_dutyL_hi( wL_hi ) ; + pwm_dutyL_low( wL_low ) ; + pwm_dutyR_hi( wR_hi ) ; + pwm_dutyR_low( wR_low ) ; + + #else // not PWM16BIT + word wL =0,wR = 0; + 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() << bVolumeUpFactor; // volume up to 10bit (x4) from 8bit + lwReadCount -= 1; + if( lbCh_mode == CH_STEREO ) { /* for stereo */ + wR = send_ff() << bVolumeUpFactor; + 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 TEST_PORT_ENABLE + test_port =0; + #endif + } +} + +/****************** + wave_player_main +*******************/ +void wave_player_main(){ + bVolumeUpFactor = calcPcmValidBits() - 8; + word wBtnLowCount = 0; + #if HAVE_LED_INDICATOR_2 + 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 btn_next_song_on btn_short_on + #define btn_pause_on btn_long_on + #define btn_power_off_on btn_long_on2 + + #if HAVE_LED_INDICATOR // pseudo PWM setting + const byte LED_OFF_DELAY_PAUSE = 5; + const byte LED_OFF_DELAY_PLAY = 50; + const sbyte PRD_COUNT = 0x6f ; //max 0x7f + const sbyte PAUSE_DUTY_MAX = PRD_COUNT/4; + sbyte sbDuty=0, sbCurrent_pos=0, sbDir=1; + sbyte sbMode_duty = PRD_COUNT ; //set duty playing + sbyte sbPseudo_period = PRD_COUNT ; //period for pseudo PWM + byte bOff_delay = LED_OFF_DELAY_PAUSE; + #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_INDICATOR + /*--------------------- + pseudo PWM for LED + ---------------------*/ + if( sbCurrent_pos < sbDuty ){ + if( sbDir == 0 ){ + led = 0; + }else{ + led = 1; + } + } else { + led = 0; + } + sbCurrent_pos = sbCurrent_pos + 1; + sbPseudo_period = sbPseudo_period - 1; + if( sbPseudo_period == 0 ){ + sbPseudo_period = PRD_COUNT; + sbCurrent_pos = 0; + } + #endif + } //; end. wait 10msec Ticker flag + + //------------------- + tickTimer_IF_clear(); + //------------------- + + #if HAVE_BUTTON_SW + /*------------------- + Pause release + -------------------*/ + if( btn_next_song_on){ + if( !fPlaying){ // ; if during pause + fPlaying = true ; //release pause + btn_next_song_on = false; + btn_pause_on = false; + #if HAVE_LED_INDICATOR + sbDuty = 0; + sbDir = 1; + #endif + pwm_period_timer_start(); + } + } + + /*------------------- + Pause enter + -------------------*/ + if( btn_pause_prev ^ btn_pause_on ){ + if( btn_pause_on ){ + fPlaying = false; + pwm_period_timer_stop(); + #if HAVE_LED_INDICATOR + sbDuty = PAUSE_DUTY_MAX/2; + sbDir = -1; + #endif + } + } + btn_pause_prev = btn_pause_on; + #endif + + /*------------------- + Next song and start + -------------------*/ + if( lfEndOneSong){ + lfEndOneSong = false; + searchNextFile(); + //; delete about last 30Kbyte to cut tag data in *.wav file + ldwSongFileSectors = ( (gdwBPB_FileSize - CUT_LAST_TAG_NOISE) )>>9 ; //512 , change to sector + //## Seek to Target file sector + sd_start_read( gdwTargetFileSector ); + + //### get wav header info ### + sd_read_pulse_byte(22);// ## Skip part of WAV header + lbCh_mode = sd_data_byte(); + sd_data_byte(); //# dummy read + ldwSample_freq = sd_data_byte() + (sd_data_byte() << 8) + + (sd_data_byte() << 16) + + (sd_data_byte() << 24); + setPwmPeriod(ldwSample_freq); + sd_read_pulse_byte(6);// ## Skip some headers + lbSample_bits = sd_data_byte(); + sd_read_pulse_byte(9) ;//## Skip to last position of header + //########################### + + lwReadCount = wREAD_COUNT - bHEADER_COUNT; + //; music start + pwm_period_timer_start(); + + #if HAVE_LED_INDICATOR + sbDuty = 0; + sbDir = 1; + #endif + } + + /*------------------- + wait end of one song + -------------------*/ + if ( ( ldwSongFileSectors == 0 ) | btn_next_song_on ){ + pwm_period_timer_stop(); + fPlaying = false; + btn_next_song_on = false; + lfEndOneSong = true; + sd_stop_read(); + } + + /*------------------- + LED indicator 1 --- pseudo PWM + -------------------*/ + #if HAVE_LED_INDICATOR + if( fPlaying ){ + sbMode_duty = PRD_COUNT ; //during Playing + }else{ + #if HAVE_LED_PAUSE_INDICATOR + sbMode_duty = PAUSE_DUTY_MAX ;// during Pause + #else + sbMode_duty = 1; + #endif + } + //--- duty control + sbDuty = sbDuty + sbDir; + if(sbDuty == sbMode_duty ){ + sbDir = -1; + } + if( sbDuty == 0){ + sbDir = 0; + sbDuty = 1; + bOff_delay = LED_OFF_DELAY_PAUSE ;// during pause + if( fPlaying ){ + bOff_delay = LED_OFF_DELAY_PLAY ;// during play + } + } + if( sbDir == 0 ){ //; wait for a while at LED OFF + if( bOff_delay > 0 ){ + bOff_delay = bOff_delay - 1; + }else{ + sbDir = 1; + } + } + #endif + + /*------------------- + LED indicator 2 --- simple ON/OFF + -------------------*/ + #if HAVE_LED_INDICATOR_2 + 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 + #endif + } //; [forever loop end] +} + +