From 3061ecca3d0fdfb87dabbf5f63c9e06c2a30f53a Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Thu, 23 Aug 2018 17:08:59 +0200 Subject: o Initial import. --- .../examples/peripheral/fpu_fft/main.c | 306 +++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/examples/peripheral/fpu_fft/main.c (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/examples/peripheral/fpu_fft/main.c') diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/examples/peripheral/fpu_fft/main.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/examples/peripheral/fpu_fft/main.c new file mode 100644 index 0000000..338101a --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/examples/peripheral/fpu_fft/main.c @@ -0,0 +1,306 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * @defgroup fpu_fft_example_main main.c + * @{ + * @ingroup fpu_fft_example + * @brief Floating Point Number FFT Example Application main file. + * + */ + +#include +#include +#include +#include +#include "app_util_platform.h" +#include "bsp.h" +#include "nrf_delay.h" + +#include "nrf_log.h" +#include "nrf_log_ctrl.h" +#include "nrf_log_default_backends.h" +/*lint -save -e689 */ /* Apparent end of comment ignored */ +#include "arm_const_structs.h" +/*lint -restore */ + +#define GRAPH_WINDOW_HEIGHT 20 //!< Graph window height used in draw function. + +#define FPU_EXCEPTION_MASK 0x0000009F //!< FPU exception mask used to clear exceptions in FPSCR register. +#define FPU_FPSCR_REG_STACK_OFF 0x40 //!< Offset of FPSCR register stacked during interrupt handling in FPU part stack. + +// We want to use 44100 Hz sampling rate to reach 22050Hz band. 128 (64 pairs) samples are used +// in FFT calculation with result contains 64 bins (22050Hz/64bins -> ~344,5Hz per bin). +#define FFT_TEST_SAMPLE_FREQ_HZ 44100.0f //!< Frequency of complex input samples. +#define FFT_TEST_COMP_SAMPLES_LEN 128 //!< Complex numbers input data array size. Correspond to FFT calculation this number must be power of two starting from 2^5 (2^4 pairs) with maximum value 2^13 (2^12 pairs). +#define FFT_TEST_OUT_SAMPLES_LEN (FFT_TEST_COMP_SAMPLES_LEN / 2) //!< Output array size. + +#define SIGNALS_RESOLUTION 100.0f //!< Sine wave frequency and noise amplitude resolution. To count resolution as decimal places in number use this formula: resolution = 1/SIGNALS_RESOLUTION . +#define SINE_WAVE_FREQ_MAX 20000 //!< Maximum frequency of generated sine wave. +#define NOISE_AMPLITUDE 1 //!< Amplitude of generated noise added to signal. + +static uint32_t m_ifft_flag = 0; //!< Flag that selects forward (0) or inverse (1) transform. +static uint32_t m_do_bit_reverse = 1; //!< Flag that enables (1) or disables (0) bit reversal of output. +static float32_t m_fft_input_f32[FFT_TEST_COMP_SAMPLES_LEN]; //!< FFT input array. Time domain. +static float32_t m_fft_output_f32[FFT_TEST_OUT_SAMPLES_LEN]; //!< FFT output data. Frequency domain. + +/** + * @brief Function for generating sine wave samples for FFT calculations. + * + * This function fill up output array with generated sine wave data with proper sampling frequency. + * Must be executed before fft_process function. + * + * @param[in] p_input Input array to fill with sine wave generated data. + * @param[in] size Input array size. + * @param[in] sample_freq Sine wave sampling frequency. + * @param[in] sine_freq Sine wave frequency. + * @param[in] add_noise Flag for enable or disble adding noise for generated data. + */ +static void fft_generate_samples(float32_t * p_input, + uint16_t size, + float32_t sampling_freq, + float32_t sine_freq, + bool add_noise) +{ + uint32_t i; + + /* Remember that sample is represented as two next values in array. */ + uint32_t sample_idx = 0; + + if (2 > size) + { + return; + } + + for (i = 0; i < (size - 1UL); i += 2) { + sample_idx = i / 2; + // Real part. + p_input[(uint16_t)i] = sin(sine_freq * (2.f * PI) * sample_idx / sampling_freq); + if (add_noise) + { + // Noise can be positive or negative number. Numbers can be cast to signed values. + p_input[(uint16_t)i] *= ((rand()) % ((int)(NOISE_AMPLITUDE * SIGNALS_RESOLUTION))) + / SIGNALS_RESOLUTION; + } + // Img part. + p_input[(uint16_t)i + 1] = 0; + } +} + +/** + * @brief Function for processing generated sine wave samples. + * @param[in] p_input Pointer to input data array with complex number samples in time domain. + * @param[in] p_input_struct Pointer to cfft instance structure describing input data. + * @param[out] p_output Pointer to processed data (bins) array in frequency domain. + * @param[in] output_size Processed data array size. + */ +static void fft_process(float32_t * p_input, + const arm_cfft_instance_f32 * p_input_struct, + float32_t * p_output, + uint16_t output_size) +{ + // Use CFFT module to process the data. + arm_cfft_f32(p_input_struct, p_input, m_ifft_flag, m_do_bit_reverse); + // Calculate the magnitude at each bin using Complex Magnitude Module function. + arm_cmplx_mag_f32(p_input, p_output, output_size); +} + +#ifdef FPU_INTERRUPT_MODE +/** + * @brief FPU Interrupt handler. Clearing exception flag at the stack. + * + * Function clears exception flag in FPSCR register and at the stack. During interrupt handler + * execution FPU registers might be copied to the stack (see lazy stacking option) and + * it is necessary to clear data at the stack which will be recovered in the return from + * interrupt handling. + */ +void FPU_IRQHandler(void) +{ + // Prepare pointer to stack address with pushed FPSCR register. + uint32_t * fpscr = (uint32_t * )(FPU->FPCAR + FPU_FPSCR_REG_STACK_OFF); + // Execute FPU instruction to activate lazy stacking. + (void)__get_FPSCR(); + // Clear flags in stacked FPSCR register. + *fpscr = *fpscr & ~(FPU_EXCEPTION_MASK); +} +#endif + +/** + * @brief Function for drawing line with given width. + * @param[in] line_width Line width. + */ +static void draw_line(uint16_t line_width) +{ + uint32_t i; + char line[line_width + 1]; + + for (i = 0; i < line_width; i++) + { + line[i] = '-'; + } + line[line_width] = 0; + NRF_LOG_RAW_INFO("%s\r\n", nrf_log_push(line)); +} + +/** + * @brief Function for drawing line and processed data informations. + * @param[in] input_sine_freq Input sine wave frequency. + * @param[in] is_noisy Flag if data is noisy. + * @param[in] chart_width Drawing chart height. + */ +static void draw_fft_header(float32_t input_sine_freq, bool is_noisy) +{ + NRF_LOG_RAW_INFO("Input: sine %uHz, noise: %s.\r\n", (uint16_t)input_sine_freq, + (uint32_t)((is_noisy == true) ? "yes" : "no")); +} + +/** + * @brief Function for drawing ASCII data processed by FFT function. + * @param[in] p_input_data Pointer to FFT data array. + * @param[in] data_size FFT array size. + * @param[in] chart_height Drawing chart height. + */ +static void draw_fft_data(float32_t * p_input_data, uint16_t data_size, uint16_t chart_height) +{ + uint32_t graph_y, graph_x; + float32_t curr_drawing_val; + float32_t curr_percent; + float32_t max_value; + uint32_t max_val_index; + char tmp_str[data_size + 1]; + + // Search FFT max value in input array. + arm_max_f32(p_input_data, data_size, &max_value, &max_val_index); + + // Draw graph. Put space if number is less than currently processed, put '|' character otherwise. + for (graph_y = chart_height; graph_y > 0; graph_y--) + { + curr_percent = ((graph_y - 1) / (chart_height * 1.f)); + curr_drawing_val = max_value * curr_percent; + for (graph_x = 0; graph_x < data_size; graph_x++) + { + if (m_fft_output_f32[graph_x] > curr_drawing_val) + { + tmp_str[graph_x] = '|'; + } else + { + tmp_str[graph_x] = ' '; + } + } + tmp_str[data_size] = 0; + NRF_LOG_RAW_INFO("%s\r\n", NRF_LOG_PUSH(tmp_str)); + NRF_LOG_FLUSH(); + } + + draw_line(data_size); +} + +/** + * @brief Function for application main entry. + */ +int main(void) +{ + uint32_t err_code; + bool noise = false; + float32_t sine_freq; + + err_code = NRF_LOG_INIT(NULL); + APP_ERROR_CHECK(err_code); + + NRF_LOG_DEFAULT_BACKENDS_INIT(); + + // Welcome message. + NRF_LOG_INFO("FPU FFT example started."); + NRF_LOG_RAW_INFO("This is FPU usage example with FFT calculation and drawing.\r\n"); + +#ifdef FPU_INTERRUPT_MODE + // Enable FPU interrupt + NVIC_SetPriority(FPU_IRQn, APP_IRQ_PRIORITY_LOWEST); + NVIC_ClearPendingIRQ(FPU_IRQn); + NVIC_EnableIRQ(FPU_IRQn); +#endif + + // Enter main loop. + for (;;) + { + // Generate new samples. + noise = !noise; + // Sine wave frequency must be positive number so cast rand to unsigned number. + // stdlib using with Segger Embedded Studio uses rand() configured to return values up to + // 32767 instead of 0x7fffffff configured in other compilers. It causes that generated sine + // frequency is smaller, but example works in the same way. + sine_freq = (((uint32_t)rand()) % ((uint32_t)(SINE_WAVE_FREQ_MAX * SIGNALS_RESOLUTION))) + / SIGNALS_RESOLUTION; + fft_generate_samples(m_fft_input_f32, + FFT_TEST_COMP_SAMPLES_LEN, + FFT_TEST_SAMPLE_FREQ_HZ, + sine_freq, noise); + + // Process generated data. 64 pairs of complex data (real, img). It is important to use + // proper arm_cfft_sR_f32 structure associated with input/output data length. + // For example: + // - 128 numbers in input array (64 complex pairs of samples) -> 64 output bins power data -> &arm_cfft_sR_f32_len64. + // - 256 numbers in input array (128 complex pairs of samples) -> 128 output bins power data -> &arm_cfft_sR_f32_len128. + fft_process(m_fft_input_f32, + &arm_cfft_sR_f32_len64, + m_fft_output_f32, + FFT_TEST_OUT_SAMPLES_LEN); + + // Draw FFT bin power chart. + draw_fft_header(sine_freq, noise); + draw_fft_data(m_fft_output_f32, FFT_TEST_OUT_SAMPLES_LEN, GRAPH_WINDOW_HEIGHT); + + NRF_LOG_FLUSH(); + +#ifndef FPU_INTERRUPT_MODE + /* Clear FPSCR register and clear pending FPU interrupts. This code is base on + * nRF5x_release_notes.txt in documentation folder. It is necessary part of code when + * application using power saving mode and after handling FPU errors in polling mode. + */ + __set_FPSCR(__get_FPSCR() & ~(FPU_EXCEPTION_MASK)); + (void) __get_FPSCR(); + NVIC_ClearPendingIRQ(FPU_IRQn); +#endif + nrf_delay_ms(1000); + } +} + +/** + * @} + */ -- cgit v1.2.3