lab 3
Dependencies: mbed BSP_DISCO_F746NG mbed-dsp
main.cpp
- Committer:
- bmazzeo
- Date:
- 2020-01-31
- Revision:
- 0:c0f52e8223fe
- Child:
- 3:02dd3dd9120a
File content as of revision 0:c0f52e8223fe:
/** ****************************************************************************** * @file main.c * @author Brian Mazzeo * @date 2020 * @brief This file provides a set of code for signal processing in 487. * Parts are taken from example code from STMIcroelectronics ****************************************************************************** * @attention * This code was specifically developed for BYU ECEn 487 course * Introduction to Digital Signal Processing. * * ****************************************************************************** */ #include "mbed.h" #include "stm32746g_discovery_audio.h" #include "stm32746g_discovery_sdram.h" #include "stm32746g_discovery_lcd.h" #include "arm_math.h" #include "signal_processing.h" /* The following type definitions are used to control the * buffering of the audio data using a double buffering technique. * Most of the transactions between the WM8994 and the microcontroller * are handled by other code - but this signals what the buffering state * is, so the data can be appropriately processed. */ typedef enum { BUFFER_OFFSET_NONE = 0, BUFFER_OFFSET_HALF = 1, BUFFER_OFFSET_FULL = 2, } BUFFER_StateTypeDef; /* These audio block samples define the size of the buffering */ #define AUDIO_BLOCK_SAMPLES ((uint32_t)128) // Number of samples (L and R) in audio block (each samples is 16 bits) #define AUDIO_BLOCK_SIZE ((uint32_t)512) // Number of bytes in audio block (4 * AUDIO_BLOCK_SAMPLES) /* These RAM addresses are important to determine where the audio data is stored. */ #define SDRAM_DEVICE_ADDR_AUDIO_MEM ((uint32_t)0xC0400000) #define AUDIO_BUFFER_IN SDRAM_DEVICE_ADDR_AUDIO_MEM #define AUDIO_BUFFER_OUT (AUDIO_BUFFER_IN + (AUDIO_BLOCK_SIZE * 2)) /* These definitions define the size of the oscilloscope that is used to display data. */ #define OSC_START_X_POS 20 #define OSC_LINE_SIZE 256 #define OSC_Y_POS 130 #define AUDIO_DRAW_LIMIT 50 /* This define a timer that is then used to record the timing of the different processing stages. */ Timer timer; /* This variable is important because it define the audio buffer recording state. */ volatile uint32_t audio_rec_buffer_state = BUFFER_OFFSET_NONE; /* Function declarations */ static void Erase_Trace(uint16_t Xpos, uint16_t Ypos, uint16_t Length); static void Draw_Trace(uint16_t Xpos, uint16_t Ypos, uint16_t* Mem_start, uint16_t Length); static void Audio_to_Float(uint16_t* buffer_in, float32_t* L_out, float32_t* R_out, uint16_t Length); static void Float_to_Audio(float32_t* L_in, float32_t* R_in, uint16_t* buffer_out, uint16_t Length); /* These memory blocks are important for converting to floating point representation. */ float32_t L_channel_float_in[AUDIO_BLOCK_SAMPLES]; float32_t R_channel_float_in[AUDIO_BLOCK_SAMPLES]; float32_t L_channel_float_out[AUDIO_BLOCK_SAMPLES]; float32_t R_channel_float_out[AUDIO_BLOCK_SAMPLES]; float32_t *L_channel_float_in_p = &L_channel_float_in[0]; float32_t *R_channel_float_in_p = &R_channel_float_in[0]; float32_t *L_channel_float_out_p = &L_channel_float_out[0]; float32_t *R_channel_float_out_p = &R_channel_float_out[0]; /* These memory blocks are where the information is stored to send back out to the WM8994 chip. */ uint16_t Processed_audio[AUDIO_BLOCK_SAMPLES]; uint16_t *Processed_audio_p = &Processed_audio[0]; /* Useful variables during looping */ uint32_t counter = 0; // Loop counter char buf[40]; // Character buffer for sprintf statements to the LCD int first_half_time = 0; // Time of first processing block int second_half_time = 0; // Time of second processing block int total_time = 0; // Time of total loop (first and second blocks) /* Main Function */ int main() { /* Initialize the LCD Screen and display information */ BSP_LCD_Init(); BSP_LCD_LayerDefaultInit(LTDC_ACTIVE_LAYER, LCD_FB_START_ADDRESS); BSP_LCD_SelectLayer(LTDC_ACTIVE_LAYER); /* Clear the LCD and set the font to be default */ BSP_LCD_Clear(LCD_COLOR_BLACK); BSP_LCD_SetFont(&LCD_DEFAULT_FONT); /* Set the backcolor to be black and the textcolor to be orange. */ BSP_LCD_SetBackColor(LCD_COLOR_BLACK); BSP_LCD_SetTextColor(LCD_COLOR_ORANGE); /* The following are static display elements that will remain on the screen. */ BSP_LCD_DisplayStringAt(0, 0, (uint8_t *)"487 Demo Code (Mazzeo)", LEFT_MODE); /* Display the L and R colors for the channels */ BSP_LCD_SetTextColor(LCD_COLOR_BLUE); BSP_LCD_DisplayStringAt(0, OSC_Y_POS - 20, (uint8_t *)"L", LEFT_MODE); BSP_LCD_SetTextColor(LCD_COLOR_GREEN); BSP_LCD_DisplayStringAt(0, OSC_Y_POS, (uint8_t *)"R", LEFT_MODE); /* The following code should not be code that you need to worry about - it sets up the audio interfaces. */ /* Initialize the Audio Interface */ BSP_AUDIO_IN_OUT_Init(INPUT_DEVICE_DIGITAL_MICROPHONE_2, OUTPUT_DEVICE_HEADPHONE, DEFAULT_AUDIO_IN_FREQ, DEFAULT_AUDIO_IN_BIT_RESOLUTION, DEFAULT_AUDIO_IN_CHANNEL_NBR); /* Initialize SDRAM buffers */ BSP_SDRAM_Init(); memset((uint16_t *)AUDIO_BUFFER_IN, 0, AUDIO_BLOCK_SIZE * 2); memset((uint16_t *)AUDIO_BUFFER_OUT, 0, AUDIO_BLOCK_SIZE * 2); /* Start Recording */ if (BSP_AUDIO_IN_Record((uint16_t *)AUDIO_BUFFER_IN, AUDIO_BLOCK_SIZE) != AUDIO_OK) { printf("BSP_AUDIO_IN_Record error\n"); } /* Start Playback */ BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02); if (BSP_AUDIO_OUT_Play((uint16_t *)AUDIO_BUFFER_OUT, AUDIO_BLOCK_SIZE * 2) != AUDIO_OK) { printf("BSP_AUDIO_OUT_Play error\n"); } /* The audio interfaces are all now working. * Importantly - AUDIO_BUFFER_IN is the pointer to the incoming data from the WM8994 * AUDIO_BUFFER_OUT is the pointer to the outgoing data to the WM8994 */ /* Initialize signal processing filters - usually there are variables that * need to be set up or that there are arrays you need to precompute - this * function call allows you to do that. */ initalize_signal_processing(); /* Hardware timer starts. Set to zero */ timer.start(); /* Main signal processing while loop */ while (1) { /* First Half */ /* Wait until end of half block recording before going on in the first half cycle*/ while (audio_rec_buffer_state != BUFFER_OFFSET_HALF) {} /* This captures the time of an entire cycle */ total_time = timer.read_us(); /* Reset the timer counter to zero */ timer.reset(); /* Plot traces of first half block recording */ Erase_Trace(OSC_START_X_POS, OSC_Y_POS, AUDIO_BLOCK_SAMPLES); Draw_Trace(OSC_START_X_POS, OSC_Y_POS, (uint16_t *) AUDIO_BUFFER_IN, AUDIO_BLOCK_SAMPLES); /* Convert data to floating point representation for processing */ Audio_to_Float((uint16_t *) AUDIO_BUFFER_IN, L_channel_float_in_p, R_channel_float_in_p, AUDIO_BLOCK_SAMPLES); /* ------------------------------------------------------------------------ */ /* Here is where signal processing can be done on the floating point arrays */ process_audio_channel_signals(L_channel_float_in_p, R_channel_float_in_p, L_channel_float_out_p, R_channel_float_out_p, AUDIO_BLOCK_SAMPLES); /* Here is where signal processing can end on the floating point arrays */ /* -------------------------------------------------------------------- */ /* Covert floating point data back to fixed point audio format */ Float_to_Audio(L_channel_float_out_p, R_channel_float_out_p, (uint16_t *) Processed_audio, AUDIO_BLOCK_SAMPLES); /* Copy recorded 1st half block into the audio buffer that goes out */ /* Replace the second memcpy with this first one once you have worked out the processed audio functions. */ memcpy((uint16_t *)(AUDIO_BUFFER_OUT), (uint16_t *)(Processed_audio), AUDIO_BLOCK_SIZE); //memcpy((uint16_t *)(AUDIO_BUFFER_OUT), (uint16_t *)(AUDIO_BUFFER_IN), AUDIO_BLOCK_SIZE); /* Display useful cycle information (split up information display so the processing is more balanced) */ sprintf(buf, "Cycles: %9d", counter); BSP_LCD_SetTextColor(LCD_COLOR_RED); BSP_LCD_DisplayStringAt(0, 46, (uint8_t *) buf, LEFT_MODE); /* Capture the timing of the first half processing */ first_half_time = timer.read_us(); /* End First Half */ /* Second Half */ /* Wait end of one block recording */ while (audio_rec_buffer_state != BUFFER_OFFSET_FULL) {} /* Plot traces of second half block recording */ Erase_Trace(OSC_START_X_POS+AUDIO_BLOCK_SAMPLES, OSC_Y_POS, AUDIO_BLOCK_SAMPLES); Draw_Trace( OSC_START_X_POS+AUDIO_BLOCK_SAMPLES, OSC_Y_POS, (uint16_t *) (AUDIO_BUFFER_IN + (AUDIO_BLOCK_SIZE)), AUDIO_BLOCK_SAMPLES); /* Convert data to floating point representation for processing */ Audio_to_Float((uint16_t *) (AUDIO_BUFFER_IN + (AUDIO_BLOCK_SIZE)), L_channel_float_in_p, R_channel_float_in_p, AUDIO_BLOCK_SAMPLES); /* ------------------------------------------------------------------------ */ /* Here is where signal processing can be done on the floating point arrays */ process_audio_channel_signals(L_channel_float_in_p, R_channel_float_in_p, L_channel_float_out_p, R_channel_float_out_p, AUDIO_BLOCK_SAMPLES); /* Here is where signal processing can end on the floating point arrays */ /* -------------------------------------------------------------------- */ /* Covert floating point data back to fixed point audio format */ Float_to_Audio(L_channel_float_out_p, R_channel_float_out_p, (uint16_t *) Processed_audio, AUDIO_BLOCK_SAMPLES); /* Copy recorded 2nd half block into the audio buffer that goes out */ memcpy((uint16_t *)(AUDIO_BUFFER_OUT + (AUDIO_BLOCK_SIZE)), (uint16_t *) (Processed_audio), AUDIO_BLOCK_SIZE); //memcpy((uint16_t *)(AUDIO_BUFFER_OUT + (AUDIO_BLOCK_SIZE)), (uint16_t *)(AUDIO_BUFFER_IN + (AUDIO_BLOCK_SIZE)), AUDIO_BLOCK_SIZE); /* Compute important cycle information and display it*/ sprintf(buf, "1:%6d 2:%6d T:%6d", first_half_time, second_half_time, total_time); BSP_LCD_SetTextColor(LCD_COLOR_RED); BSP_LCD_DisplayStringAt(0, 20, (uint8_t *) buf, LEFT_MODE); /* Copy recorded 2nd half block into audio output buffer */ /* Change the recording buffer state to reflect the status of the buffer */ audio_rec_buffer_state = BUFFER_OFFSET_NONE; /* Increase the counter */ counter++; /* Measures the amount of time to process the second half */ second_half_time = timer.read_us(); /* End Second Half */ } } /** * @brief Draws a trace of the data line. * @param Xpos: X position * @param Ypos: Y position * @param Mem_start: Start of memory location * @param Length: length of trace * @retval None */ void Erase_Trace(uint16_t Xpos, uint16_t Ypos, uint16_t Length) { /* Creates a brown rectangle above and below the axis */ BSP_LCD_SetTextColor(LCD_COLOR_BROWN); BSP_LCD_FillRect(Xpos, Ypos - AUDIO_DRAW_LIMIT, Length, AUDIO_DRAW_LIMIT); BSP_LCD_FillRect(Xpos, Ypos+1, Length, AUDIO_DRAW_LIMIT); /* Draw axis for plotting */ BSP_LCD_SetTextColor(LCD_COLOR_WHITE); BSP_LCD_DrawHLine(Xpos, Ypos, Length); } /** * @brief Draws a trace of the data line. * @param Xpos: X position * @param Ypos: Y position * @param Mem_start: Start of memory location * @param Length: length of trace * @retval None */ void Draw_Trace(uint16_t Xpos, uint16_t Ypos, uint16_t* Mem_start, uint16_t Length) { uint16_t i; uint16_t* mem_address; char buf[10]; int16_t L_audio_value; int16_t R_audio_value; mem_address = Mem_start; for (i=0; i<Length; i++) { R_audio_value = (int16_t) *mem_address; mem_address++; L_audio_value = (int16_t) *mem_address; mem_address++; L_audio_value = L_audio_value / 100; R_audio_value = R_audio_value / 100; if (L_audio_value > AUDIO_DRAW_LIMIT) {L_audio_value = AUDIO_DRAW_LIMIT;} else if (L_audio_value < -AUDIO_DRAW_LIMIT) {L_audio_value = -AUDIO_DRAW_LIMIT;} if (R_audio_value > AUDIO_DRAW_LIMIT) {R_audio_value = AUDIO_DRAW_LIMIT;} else if (R_audio_value < -AUDIO_DRAW_LIMIT) {R_audio_value = -AUDIO_DRAW_LIMIT;} BSP_LCD_DrawPixel(Xpos + i, (uint16_t) ((int16_t) Ypos + L_audio_value), LCD_COLOR_BLUE); BSP_LCD_DrawPixel(Xpos + i, (uint16_t) ((int16_t) Ypos + R_audio_value), LCD_COLOR_GREEN); } } /** * @brief Converts audio data in buffer to floating point representation. * @param buffer_in: Pointer to Audio buffer start location * @param L_out: Pointer to Left channel out data (float32_t) * @param R_out: Pointer to Right channel out data (float32_t) * @param Length: length of data to convert * @retval None */ void Audio_to_Float(uint16_t* buffer_in, float32_t* L_out, float32_t* R_out, uint16_t Length) { uint16_t i; uint16_t* audio_mem_address; float32_t* L_chan_mem_address; float32_t* R_chan_mem_address; float32_t L_audio_value; float32_t R_audio_value; audio_mem_address = buffer_in; L_chan_mem_address = L_out; R_chan_mem_address = R_out; for (i=0; i<Length; i++) { R_audio_value = (float32_t) ((int16_t) *audio_mem_address); audio_mem_address++; L_audio_value = (float32_t) ((int16_t) *audio_mem_address); audio_mem_address++; *L_chan_mem_address = L_audio_value; L_chan_mem_address++; *R_chan_mem_address = R_audio_value; R_chan_mem_address++; } } /** * @brief Converts audio data in buffer to floating point representation. * @param L_out: Pointer to Left channel in data (float32_t) * @param R_out: Pointer to Right channel in data (float32_t) * @param buffer_out: Pointer to combined 32 bit (two 16-bit int samples) * @param Length: length of data to convert * @retval None */ void Float_to_Audio(float32_t* L_in, float32_t* R_in, uint16_t* buffer_out, uint16_t Length) { uint16_t i; uint16_t* audio_mem_address; float32_t* L_chan_mem_address; float32_t* R_chan_mem_address; int16_t L_audio_value; int16_t R_audio_value; audio_mem_address = buffer_out; L_chan_mem_address = L_in; R_chan_mem_address = R_in; for (i=0; i<Length; i++) { L_audio_value = (int16_t) ((float32_t) *L_chan_mem_address); L_chan_mem_address++; R_audio_value = (int16_t) ((float32_t) *R_chan_mem_address); R_chan_mem_address++; *audio_mem_address = (uint16_t) R_audio_value; audio_mem_address++; *audio_mem_address = (uint16_t) L_audio_value; audio_mem_address++; } } /*------------------------------------------------------------------------------------- Callbacks implementation: the callbacks API are defined __weak in the stm32746g_discovery_audio.c file and their implementation should be done in the user code if they are needed. Below some examples of callback implementations. -------------------------------------------------------------------------------------*/ /** * @brief Manages the DMA Transfer complete interrupt. * @param None * @retval None */ void BSP_AUDIO_IN_TransferComplete_CallBack(void) { audio_rec_buffer_state = BUFFER_OFFSET_FULL; } /** * @brief Manages the DMA Half Transfer complete interrupt. * @param None * @retval None */ void BSP_AUDIO_IN_HalfTransfer_CallBack(void) { audio_rec_buffer_state = BUFFER_OFFSET_HALF; } /** * @brief Audio IN Error callback function. * @param None * @retval None */ void BSP_AUDIO_IN_Error_CallBack(void) { printf("BSP_AUDIO_IN_Error_CallBack\n"); }