一、前言
本项目基于英飞凌PSoC 6 RTT开发板实现了信号处理前端-一个信号处理的工具集。
包括虚拟示波器,音频采集分析,谐波分析,周期幅值相位分析,数字滤波,极值检测,可上位机可视化和命令行人机交互,可以方便继续扩展相关功能,继续丰富工具集。
视频: https://www.bilibili.com/video/BV1PM4y147v1/
代码仓库: https://gitee.com/qinyunti/infineon-psoc62.git
二、移植DSP算法库
2.1添加代码
git clone https://github.com/ARM-software/CMSIS_5.git
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:DistanceFunctions.c,DistanceFunctionsF16.c
7FastMathFunctions:FastMathFunctions.c,FastMathFunctionsF16.c
8FilteringFunctions:FilteringFunctions.c,FilteringFunctionsF16.c
9InterpolationFunctions:InterpolationFunctions.c,InterpolationFunctionsF16.c
10MatrixFunctions:MatrixFunctions.c,MatrixFunctionsF16.c
11QuaternionMathFunctions:QuaternionMathFunctions.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[i],testOutput[i]);
7 }
8 else
9 {
10 printf("/*%f,%f*/\r\n", testInput_f32_10khz[i],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[i] / 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_H
2#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[i] / 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[i] / 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[i] = (float)adcbuffer[i];
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[i]/1024;
40 }
41 printf("/*%ld,%ld*/\r\n", adcbuffer[i],out);
42 }
43}
44 /** \endlink */
Fft.h
1#ifndef FFT_H
2#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[i] = (float)adcbuffer[i];
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_H
2#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[i] = (float)adcbuffer[i];
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[i], testOutput[i]*ScaleValue);
59 }
60}
61 /** \endlink */
Iir.h
1#ifndef IIR_H
2#define IIR_H
3int iir_main(void);
4#endif
4.3.4测试
输入iir回车,查看波形
见视频
以下可以看到滤波导致了滞后,黄色线有滞后
以下是实时采集滤波
4.4 极大值检测
在电力等行业,分析电压极值,是一项重要的参数分析,可以分析电压的波动;示波器中也有自动测量极值的功能更。本板作为信号处理前端也实现了该功能。
4.4.1 算法
算法来源于论文 https://www.mdpi.com/1999-4893/5/4/588/htm
核心代码如下
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[i] > data[i - k]) && (data[i] > 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[i] > data[i - k]) && (data[i] > data[i + k]))
23 {
24 p_data[i] += 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———————
👇 点击阅读原文进入官网
本文分享自微信公众号 - RTThread物联网操作系统(RTThread)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。