| 【RT-Thread】基于infineonPSOC62开发板的-信号处理前端 虚拟示波器-工具集 个
 
 
 
 一、前言 
 
 本项目基于英飞凌PSoC 6 RTT开发板实现了信号处理前端-一个信号处理的工具集。 包括虚拟示波器,音频采集分析,谐波分析,周期幅值相位分析,数字滤波,极值检测,可上位机可视化和命令行人机交互,可以方便继续扩展相关功能,继续丰富工具集。 
 二、移植DSP算法库 
 
 2.1添加代码 CMSIS_5\CMSIS\DSP下是相关文件,Source下是源码 将DSP文件夹复制到自己的工程目录中,只保留 Include,PrivateInclude,Source三个文件夹 Source下的每个子文件夹都是一类算法,里面的每个c都对应一个计算函数,并且有一个总文件包括其中所有的单个.c,比如BasicMathFunctions.c中 删除这些总的.c,避免编译重复 删除以下文件和所有的非.c和.h文件  1BasicMathFunctions:BasicMathFunctions.c,BasicMathFunctionsF16.c 
 2BayesFunctions:BayesFunctions.c,BayesFunctionsF16.c 
 3CommonTables:CommonTables.c,CommonTablesF16.c 
 4ComplexMathFunctions:ComplexMathFunctions.c,ComplexMathFunctionsF16.c 
 5ControllerFunctions:ControllerFunctions.c 
 6DistanceFunctions  istanceFunctions.c,DistanceFunctionsF16.c 
 7FastMathFunctions:FastMathFunctions.c,FastMathFunctionsF16.c 
 8FilteringFunctions:FilteringFunctions.c,FilteringFunctionsF16.c 
 9InterpolationFunctions:InterpolationFunctions.c,InterpolationFunctionsF16.c 
10MatrixFunctions:MatrixFunctions.c,MatrixFunctionsF16.c 
11QuaternionMathFunctions  uaternionMathFunctions.c 
12StatisticsFunctions:StatisticsFunctions.c,StatisticsFunctionsF16.c 
13SupportFunctions:SupportFunctions.c,SupportFunctionsF16.c 
14SVMFunctions:SVMFunctions.c,SVMFunctionsF16.c 
15TransformFunctions:TransformFunctions.c,TransformFunctionsF16.c,arm_bitreversal2.S
 工程设置添加相关头文件包含路径 
 2.2测试 
 复制CMSIS_5\CMSIS\DSP\Examples\ARM\arm_fft_bin_example下的arm_fft_bin_data.c和arm_fft_bin_example_f32.c到自己的工程目录 arm_fft_bin_example_f32.c下的 int32_t main(void)改为int32_t ffttest_main(void) 并添加#define SEMIHOSTING以使能printf打印,我们已经重定向实现了printf打印到串口。 由于 arm_cfft_f32(&varInstCfftF32, testInput_f32_10khz, ifftFlag, doBitReverse);会修改testInput_f32_10khz的内容,所以添加一个缓存,以便能重复测试 1float32_t testtmp_f32_10khz[2048];2  /* Process the data through the CFFT/CIFFT module */
 3  memcpy(testtmp_f32_10khz,testInput_f32_10khz,sizeof(testInput_f32_10khz));
 4  arm_cfft_f32(&varInstCfftF32, testtmp_f32_10khz, ifftFlag, doBitReverse);
 5  /* Process the data through the Complex Magnitude Module for
 6  calculating the magnitude at each bin */
 7  arm_cmplx_mag_f32(testtmp_f32_10khz, testOutput, fftSize);
 在自己的main函数中申明并调用 int32_t ffttest_main(void) ffttest_main() 编译运行可以看到串口打印SUCCESS说明测试OK。 将输入输出数据打印  1 printf("SUCCESS\n");2    for(int i=0; i<TEST_LENGTH_SAMPLES; i++)
 3    {
 4        if(i<TEST_LENGTH_SAMPLES/2)
 5        {
 6            printf("/*%f,%f*/\r\n", testInput_f32_10khz,testOutput);
 7        }
 8        else
 9        {
 10            printf("/*%f,%f*/\r\n", testInput_f32_10khz,0.0);
 11        }
 12    }
 使用serialstudio可视化显示,可以看到计算结果FFT频率明显的峰值 
 三、音频采集 
 
 3.1原理图 
 从原理图看到有6路模拟输入,分别对应 P10.0~P10.5, VREF为模拟参考电压。 使用的是MAX4466的MIC,接到ADC0,如下图所示 3.2配置模拟采集引脚 3.3代码 Adc.c   1#include "cy_pdl.h"2#include "cyhal.h"
 3#include "cybsp.h"
 4#include "cy_retarget_io.h"
 5#define VPLUS_CHANNEL_0  (P10_0)
 6/* Conversion factor */
 7#define MICRO_TO_MILLI_CONV_RATIO        (1000u)
 8/* Acquistion time in nanosecond */
 9#define ACQUISITION_TIME_NS              (116680u)
 10/* ADC Scan delay in millisecond */
 11#define ADC_SCAN_DELAY_MS                (200u)
 12/*******************************************************************************
 13*       Enumerated Types
 14*******************************************************************************/
 15/* ADC Channel constants*/
 16enum ADC_CHANNELS
 17{
 18  CHANNEL_0 = 0,
 19  NUM_CHANNELS
 20} adc_channel;
 21/*******************************************************************************
 22* Global Variables
 23*******************************************************************************/
 24/* ADC Object */
 25cyhal_adc_t adc_obj;
 26/* ADC Channel 0 Object */
 27cyhal_adc_channel_t adc_chan_0_obj;
 28/* Default ADC configuration */
 29const cyhal_adc_config_t adc_config = {
 30        .continuous_scanning=false, // Continuous Scanning is disabled
 31        .average_count=1,           // Average count disabled
 32        .vref=CYHAL_ADC_REF_VDDA,   // VREF for Single ended channel set to VDDA
 33        .vneg=CYHAL_ADC_VNEG_VSSA,  // VNEG for Single ended channel set to VSSA
 34        .resolution = 12u,          // 12-bit resolution
 35        .ext_vref = NC,             // No connection
 36        .bypass_pin = NC };       // No connection
 37/* Asynchronous read complete flag, used in Event Handler */
 38static bool async_read_complete = true;
 39#define NUM_SCAN                    (1000)
 40#define NUM_CHANNELS                (1)
 41/* Variable to store results from multiple channels during asynchronous read*/
 42int32_t result_arr[NUM_CHANNELS * NUM_SCAN] = {0};
 43static void adc_event_handler(void* arg, cyhal_adc_event_t event)
 44{
 45    if(0u != (event & CYHAL_ADC_ASYNC_READ_COMPLETE))
 46    {
 47        /* Set async read complete flag to true */
 48        async_read_complete = true;
 49    }
 50}
 51int adc_init(void)
 52{
 53    /* Variable to capture return value of functions */
 54    cy_rslt_t result;
 55    /* Initialize ADC. The ADC block which can connect to the channel 0 input pin is selected */
 56    result = cyhal_adc_init(&adc_obj, VPLUS_CHANNEL_0, NULL);
 57    if(result != CY_RSLT_SUCCESS)
 58    {
 59        printf("ADC initialization failed. Error: %ld\n", (long unsigned int)result);
 60        CY_ASSERT(0);
 61    }
 62    /* ADC channel configuration */
 63    const cyhal_adc_channel_config_t channel_config = {
 64            .enable_averaging = false,  // Disable averaging for channel
 65            .min_acquisition_ns = ACQUISITION_TIME_NS, // Minimum acquisition time set to 1us
 66            .enabled = true };          // Sample this channel when ADC performs a scan
 67    /* Initialize a channel 0 and configure it to scan the channel 0 input pin in single ended mode. */
 68    result  = cyhal_adc_channel_init_diff(&adc_chan_0_obj, &adc_obj, VPLUS_CHANNEL_0,
 69                                          CYHAL_ADC_VNEG, &channel_config);
 70    if(result != CY_RSLT_SUCCESS)
 71    {
 72        printf("ADC first channel initialization failed. Error: %ld\n", (long unsigned int)result);
 73        CY_ASSERT(0);
 74    }
 75    /* Register a callback to handle asynchronous read completion */
 76     cyhal_adc_register_callback(&adc_obj, &adc_event_handler, result_arr);
 77     /* Subscribe to the async read complete event to process the results */
 78     cyhal_adc_enable_event(&adc_obj, CYHAL_ADC_ASYNC_READ_COMPLETE, CYHAL_ISR_PRIORITY_DEFAULT, true);
 79     printf("ADC is configured in multichannel configuration.\r\n\n");
 80     printf("Channel 0 is configured in single ended mode, connected to the \r\n");
 81     printf("channel 0 input pin. Provide input voltage at the channel 0 input pin \r\n");
 82     return 0;
 83}
 84int adc_samp(void)
 85{
 86    /* Variable to capture return value of functions */
 87    cy_rslt_t result;
 88    /* Variable to store ADC conversion result from channel 0 */
 89    int32_t adc_result_0 = 0;
 90        /* Clear async read complete flag */
 91        async_read_complete = false;
 92        /* Initiate an asynchronous read operation. The event handler will be called
 93         * when it is complete. */
 94        memset(result_arr,0,sizeof(result_arr));
 95        cyhal_gpio_write_internal(CYBSP_USER_LED,true);
 96        result = cyhal_adc_read_async_uv(&adc_obj, NUM_SCAN, result_arr);
 97        if(result != CY_RSLT_SUCCESS)
 98        {
 99            printf("ADC async read failed. Error: %ld\n", (long unsigned int)result);
 100            CY_ASSERT(0);
 101        }
 102        while(async_read_complete == false);
 103        cyhal_gpio_write_internal(CYBSP_USER_LED,false);
 104        /*
 105         * Read data from result list, input voltage in the result list is in
 106         * microvolts. Convert it millivolts and print input voltage
 107         *
 108         */
 109        for(int i=0; i<NUM_SCAN; i++)
 110        {
 111            adc_result_0 = result_arr / MICRO_TO_MILLI_CONV_RATIO;
 112            printf("/*%4ld*/\r\n", (long int)adc_result_0);
 113        }
 114    return 0;
 115}
 Adc.h 1#ifndef ADC_H2#define ADC_H
 3int adc_init(void);
 4int adc_samp(void);
 5#endif
 Main.c调用 adc_init(); adc_samp(); 
 3.4时钟源 
 时钟源是100Mhz,12分频=8.33M,满足1.8MHz~18MHz之间的要求 默认是按照8M配置 3.5采样时间 采样前后翻转LED用示波器测量时间  1int adc_samp(void)2{
 3    /* Variable to capture return value of functions */
 4    cy_rslt_t result;
 5    /* Variable to store ADC conversion result from channel 0 */
 6    int32_t adc_result_0 = 0;
 7        /* Clear async read complete flag */
 8        async_read_complete = false;
 9        /* Initiate an asynchronous read operation. The event handler will be called
 10         * when it is complete. */
 11        memset(result_arr,0,sizeof(result_arr));
 12        cyhal_gpio_write_internal(CYBSP_USER_LED,true);
 13        result = cyhal_adc_read_async_uv(&adc_obj, NUM_SCAN, result_arr);
 14        if(result != CY_RSLT_SUCCESS)
 15        {
 16            printf("ADC async read failed. Error: %ld\n", (long unsigned int)result);
 17            CY_ASSERT(0);
 18        }
 19        while(async_read_complete == false);
 20        cyhal_gpio_write_internal(CYBSP_USER_LED,false);
 21        /*
 22         * Read data from result list, input voltage in the result list is in
 23         * microvolts. Convert it millivolts and print input voltage
 24         *
 25         */
 26        for(int i=0; i<NUM_SCAN; i++)
 27        {
 28            adc_result_0 = result_arr / MICRO_TO_MILLI_CONV_RATIO;
 29            printf("/*%4ld*/\r\n", (long int)adc_result_0);
 30        }
 31    return 0;
 32}
 采样1000次,分别设置采样时间为2uS和1uS对比。 #define ACQUISITION_TIME_NS (2000u) 10.28mS #define ACQUISITION_TIME_NS (1000u) 9.32mS 10.28-9.32=0.96mS 1000次约1mS,1次刚好是1uS。 而1000次除去采样时间其他时间为8.32mS,即一次8.32uS。 因为前面设置了时钟为8.33MHz, 从前面时序一节可以看到,除去采样时间,其他转换时间等需要14个CLK,所以需要14/8.33uS=1.7uS. 剩余的8.32-1.7为数据搬运,软件处理等时间。 
 3.6 采样值正确性 
 1.545V和示波器采集为1.54V差不多是正确的,这里没有高精度的万用表就不对测试精度了,只测试了正确性。 
 3.7音频采集 
 一次采集1000次然后串口打印,使用SerialStudio可视化显示  1int adc_samp(void)2{
 3    /* Variable to capture return value of functions */
 4    cy_rslt_t result;
 5    /* Variable to store ADC conversion result from channel 0 */
 6    int32_t adc_result_0 = 0;
 7        /* Clear async read complete flag */
 8        async_read_complete = false;
 9        /* Initiate an asynchronous read operation. The event handler will be called
 10         * when it is complete. */
 11        memset(result_arr,0,sizeof(result_arr));
 12        cyhal_gpio_write_internal(CYBSP_USER_LED,true);
 13        result = cyhal_adc_read_async_uv(&adc_obj, NUM_SCAN, result_arr);
 14        if(result != CY_RSLT_SUCCESS)
 15        {
 16            printf("ADC async read failed. Error: %ld\n", (long unsigned int)result);
 17            CY_ASSERT(0);
 18        }
 19        while(async_read_complete == false);
 20        cyhal_gpio_write_internal(CYBSP_USER_LED,false);
 21        /*
 22         * Read data from result list, input voltage in the result list is in
 23         * microvolts. Convert it millivolts and print input voltage
 24         *
 25         */
 26        for(int i=0; i<NUM_SCAN; i++)
 27        {
 28            adc_result_0 = result_arr / MICRO_TO_MILLI_CONV_RATIO;
 29            printf("/*%4ld*/\r\n", (long int)adc_result_0);
 30        }
 31    return 0;
 32}
 
 四、信号处理前端 
 
 4.1 电能质量,谐波分析 
 4.1.1添加命令行 
 在电能检测应用中,电能质量一项分析即谐波分析,谐波分量大,说明电能质量不好, 基于本板信号处理前端也实现了该功能。 shell_fun.h中 1void FftFun(void* param); shell_fun.c中 1include "fft.h" shell_cmd_list中添加一行 1 { (const uint8_t*)"fft",         FftFun,           "fft"},                 /*打印帮助信息*/ 添加命令执行函数 1void FftFun(void* param)2{
 3    fft_main();
 4}
 
 4.1.2添加实现 Fft.c  1#include "arm_math.h"2#include "arm_const_structs.h"
 3#include <stdio.h>
 4#define TEST_LENGTH_SAMPLES 2048
 5extern float32_t testInput_f32_10khz[TEST_LENGTH_SAMPLES];
 6static float32_t testOutput[TEST_LENGTH_SAMPLES/2];
 7static uint32_t fftSize = 1024;
 8static uint32_t ifftFlag = 0;
 9static uint32_t doBitReverse = 1;
 10static arm_cfft_instance_f32 varInstCfftF32;
 11static int testIndex = 0;
 12static float testtmp_f32_10khz[2048];
 13static int32_t adcbuffer[2048];
 14int32_t fft_main(void)
 15{
 16  arm_status status;
 17  float32_t maxValue;
 18  status = ARM_MATH_SUCCESS;
 19  status=arm_cfft_init_f32(&varInstCfftF32,fftSize);
 20  //memcpy(testtmp_f32_10khz,testInput_f32_10khz,sizeof(testInput_f32_10khz));
 21  adc_samp(adcbuffer,2048);
 22  for(int i=0; i<2048;i ++)
 23  {
 24      testtmp_f32_10khz = (float)adcbuffer;
 25  }
 26  arm_cfft_f32(&varInstCfftF32, testtmp_f32_10khz, ifftFlag, doBitReverse);
 27  arm_cmplx_mag_f32(testtmp_f32_10khz, testOutput, fftSize);
 28  /* Calculates maxValue and returns corresponding BIN value */
 29  arm_max_f32(testOutput, fftSize, &maxValue, &testIndex);
 30  int32_t out = 0;
 31  for(int i=0; i<TEST_LENGTH_SAMPLES; i++)
 32  {
 33      if(i>TEST_LENGTH_SAMPLES/2)
 34      {
 35          out = testOutput[i-TEST_LENGTH_SAMPLES/2]/1024;
 36      }
 37      else
 38      {
 39          out = testOutput/1024;
 40      }
 41      printf("/*%ld,%ld*/\r\n", adcbuffer,out);
 42  }
 43}
 44 /** \endlink */
 Fft.h 1#ifndef FFT_H2#define FFT_H
 3int fft_main(void);
 4#endif
 测试 
 4.2 周期(频率),幅值,相位分析 
 4.2.1 原理 
 FFT变换结果,幅值最大的横坐标对应信号频率,纵坐标对应幅度。幅值最大的为out[m]=val;则信号频率f0=(Fs/N)m ,信号幅值Vpp=val/(N/2)。N为FFT的点数,Fs为采样频率。相位Pha=atan2(a, b)弧度制,其中ab是输出虚数结果的实部和虚部。 
 4.2.2添加命令行 shell_fun.h中 1void FrqFun(void* param); shell_fun.c中 include "frq.h" shell_cmd_list中添加一行 1{ (const uint8_t*)"frt",         FrqFun,           "frq"}, 添加命令执行函数 1void FrqFun(void* param)2{
 3    Frq_main();
 4}
 
 4.2.3实现代码 Frq.c  1#include "arm_math.h"2#include "arm_const_structs.h"
 3#include <stdio.h>
 4#define TEST_LENGTH_SAMPLES 2048
 5#define FS 10000
 6extern float32_t testInput_f32_10khz[TEST_LENGTH_SAMPLES];
 7static float32_t testOutput[TEST_LENGTH_SAMPLES/2];
 8static uint32_t fftSize = 1024;
 9static uint32_t ifftFlag = 0;
 10static uint32_t doBitReverse = 1;
 11static arm_cfft_instance_f32 varInstCfftF32;
 12static int testIndex = 0;
 13static float testtmp_f32_10khz[2048];
 14static int32_t adcbuffer[2048];
 15int32_t frq_main(void)
 16{
 17  arm_status status;
 18  float32_t maxValue;
 19  status = ARM_MATH_SUCCESS;
 20  status=arm_cfft_init_f32(&varInstCfftF32,fftSize);
 21  //memcpy(testtmp_f32_10khz,testInput_f32_10khz,sizeof(testInput_f32_10khz));
 22  adc_samp(adcbuffer,2048);
 23  for(int i=0; i<2048;i ++)
 24 {
 25      testtmp_f32_10khz = (float)adcbuffer;
 26  }
 27  arm_cfft_f32(&varInstCfftF32, testtmp_f32_10khz, ifftFlag, doBitReverse);
 28  arm_cmplx_mag_f32(testtmp_f32_10khz, testOutput, fftSize);
 29  /* Calculates maxValue and returns corresponding BIN value */
 30  arm_max_f32(testOutput, fftSize, &maxValue, &testIndex);
 31  float freq = (FS/TEST_LENGTH_SAMPLES)*testIndex;
 32  float vpp = maxValue/(TEST_LENGTH_SAMPLES/2);
 33  float pha = atan2(testOutput[2*testIndex],testOutput[2*testIndex+1]);
 34  printf("freq=%f,vpp=%f,pha=%f\r\n",freq,vpp,pha);
 35}
 36 /** \endlink */
 Frq.h 1#ifndef FRQ_H2#define FRQ_H
 3int frq_main(void);
 4#endif
 
 4.2.4测试 
 输入frq开始测试印如下 实时采集测试 此时采集的是音频背景声,噪声很小,所以频率为0 
 4.3数字滤波信号前端 
 4.3.1原理 
 CMSIS-DSP提供直接I型IIR库支持Q7,Q15,Q31和浮点四种数据类型。其中Q15和Q31提供了快速版本。 直接I型IIR滤波器是基于二阶Biquad级联的方式来实现的。每个Biquad由一个二阶的滤波器组成: y[n] = b0 x[n] + b1 x[n-1] + b2 x[n-2] + a1 y[n-1] + a2 * y[n-2] 直接I型算法每个阶段需要5个系数和4个状态变量。 matlab使用上面的公式实现,在使用fdatool工具箱生成的a系数需要取反才能用于直接I型IIR滤波器的函数中。 高阶IIR滤波器的实现是采用二阶Biquad级联的方式来实现的。其中参数numStages就是用来做指定二阶Biquad的个数。比如8阶IIR滤波器就可以采用numStages=4个二阶Biquad来实现。 如果要实现9阶IIR滤波器就需要将numStages=5,这时就需要其中一个Biquad配置成一阶滤波器(也就是b2=0,a2=0)。 
 4.3.2添加命令行 
 shell_fun.h中 void IirFun(void* param); shell_fun.c中 1include "iir.h"
 shell_cmd_list中添加一行 1  { (const uint8_t*)"iir",         IirFun,           “iir"},
 添加命令执行函数 1void IirFun(void* param)2{
 3    Iir_main();
 4}
 
 4.3.3实现代码 Iir.c  1#include "arm_math.h"2#include "arm_const_structs.h"
 3#include <stdio.h>
 4#define TEST_LENGTH_SAMPLES 2048
 5#define FS 10000
 6extern float32_t testInput_f32_10khz[TEST_LENGTH_SAMPLES];
 7static float32_t testOutput[TEST_LENGTH_SAMPLES];
 8static uint32_t fftSize = 1024;
 9static uint32_t ifftFlag = 0;
 10static uint32_t doBitReverse = 1;
 11static arm_cfft_instance_f32 varInstCfftF32;
 12static int testIndex = 0;
 13static float testtmp_f32_10khz[2048];
 14static int32_t adcbuffer[2048];
 15#define numStages  2                /* 2阶IIR滤波的个数 */
 16#define BLOCK_SIZE           128    /* 调用一次arm_biquad_cascade_df1_f32处理的采样点个数 */
 17uint32_t blockSize = BLOCK_SIZE;
 18uint32_t numBlocks = TEST_LENGTH_SAMPLES/BLOCK_SIZE;      /* 需要调用arm_biquad_cascade_df1_f32的次数 */
 19static float32_t IIRStateF32[4*numStages];                      /* 状态缓存 */
 20/* 巴特沃斯低通滤波器系数 80Hz*/
 21const float32_t IIRCoeffs32LP[5*numStages] = {
 22    1.0f,  2.0f,  1.0f,  1.479798894397216679763573665695730596781f,
 23-0.688676953053861784503908438637154176831f,
 24    1.0f,  2.0f,  1.0f,  1.212812092620218384908525877108331769705f,
 25-0.384004162286553540894828984164632856846f
 26};
 27int32_t iir_main(void)
 28{
 29    uint32_t i;
 30    arm_biquad_casd_df1_inst_f32 S;
 31    float32_t ScaleValue;
 32    float32_t  *inputF32, *outputF32;
 33    /* 初始化输入输出缓存指针 */
 34    //memcpy(testtmp_f32_10khz,testInput_f32_10khz,sizeof(testInput_f32_10khz));
 35#if 1
 36    adc_samp(adcbuffer,2048);
 37    for(int i=0; i<2048;i ++)
 38   {
 39        testtmp_f32_10khz = (float)adcbuffer;
 40    }
 41#endif
 42    inputF32 = testtmp_f32_10khz;
 43    outputF32 = testOutput;
 44    /* 初始化 */
 45    arm_biquad_cascade_df1_init_f32(&S, numStages, (float32_t *)&IIRCoeffs32LP[0],
 46(float32_t *)&IIRStateF32[0]);
 47    /* 实现IIR滤波,这里每次处理1个点 */
 48    for(i=0; i < numBlocks; i++)
 49    {
 50        arm_biquad_cascade_df1_f32(&S, inputF32 + (i * blockSize),  outputF32 + (i * blockSize),
 51  blockSize);
 52    }
 53    /*放缩系数 */
 54    ScaleValue = 0.012f* 0.42f;
 55    /* 打印滤波后结果 */
 56    for(i=0; i<TEST_LENGTH_SAMPLES; i++)
 57    {
 58        printf("/*%f, %f*/\r\n", testtmp_f32_10khz, testOutput*ScaleValue);
 59    }
 60}
 61 /** \endlink */
 Iir.h 1#ifndef IIR_H2#define IIR_H
 3int iir_main(void);
 4#endif
 
 4.3.4测试 
 输入iir回车,查看波形 见视频 以下可以看到滤波导致了滞后,黄色线有滞后 以下是实时采集滤波 
 4.4 极大值检测 
 在电力等行业,分析电压极值,是一项重要的参数分析,可以分析电压的波动;示波器中也有自动测量极值的功能更。本板作为信号处理前端也实现了该功能。 
 4.4.1 算法 
 核心代码如下  1void ampd(int32_t* data, int32_t len)2{
 3    int row_sum;
 4    for(int k=1; k<len/2+1; k++)
 5    {
 6        row_sum = 0;
 7        for(int i=k; i<len-k; i++)
 8        {
 9            if((data > data[i - k]) && (data > data[i + k]))
 10            {
 11                row_sum -= 1;
 12            }
 13        }
 14        arr_rowsum[k-1] = row_sum;
 15    }
 16    int min_index = argmin(arr_rowsum,len/2+1);
 17     max_window_length = min_index;
 18    for(int k=1; k<max_window_length + 1; k++)
 19    {
 20        for(int i=k; i<len - k; i++)
 21        {
 22            if((data > data[i - k]) && (data > data[i + k]))
 23            {
 24                p_data += 1;
 25            }
 26        }
 27    }
 28    for(int k=0; k<len; k++)
 29    {
 30        if(p_data[k] == max_window_length)
 31        {
 32            /* 极大值 */
 33        }
 34    }
 35}
 4.4.2 添加命令行 1  { (const uint8_t*)"max",         MaxFun,           "max"},                 /*打印帮助信息*/2void MaxFun(void* param)
 3{
 4    max_test();
 5}
 6void MaxFun(void* param);
 测试代码如下,串口命令行输入命令max,开始采集ADC值,并计算极值,打印到PC串口通过seraistudio可视化显示  1int max_test(void)2{
 3    for(int i=0; i<10; i++)
 4    {
 5        memset(p_data,0,sizeof(p_data));
 6        //adc_samp(sim_data_buffer,1000);
 7        sim_data();
 8        ampd(sim_data_buffer, sizeof(sim_data_buffer)/sizeof(sim_data_buffer[0]));
 9        for(int k=0; k<sizeof(sim_data_buffer)/sizeof(sim_data_buffer[0]); k++)
 10        {
 11            if(p_data[k] == max_window_length)
 12            {
 13                /* 极大值 */
 14                printf("/*%ld,%ld*/\r\n",sim_data_buffer[k],sim_data_buffer[k]);
 15            }
 16            else
 17            {
 18                printf("/*%ld,%d*/\r\n",sim_data_buffer[k],0);
 19            }
 20            cyhal_system_delay_ms(10);
 21        }
 22    }
 23    return 0;
 24}
 4.4.3 测试 效果如下IN是原始数据,MAX是检测到的极大值,如果检测极小值将原始数据取反即可。 检测语音,效果如下 
 五、总结 
 
 得益于开发板出色的处理性能,和外设性能,以及官方可视化的代码配置工具,可以方便的搭建开发环境,实现外设采集信号比如ADC,移植DSP库,实现各种算法。本Demo实现了谐波分析,周期幅值相位分析,数字滤波,极大值检测等功能,是一个小的工具集,还可以继续扩展,设计了人机交互命令行,方便实用和测试,具备一定实用价值。 
 ———————End——————— 
 
 |