zl程序教程

您现在的位置是:首页 >  后端

当前栏目

第八周-GD32F4XX的测试笔记(2)(调用DSP的FFT算法)

算法测试笔记 调用 DSP FFT 第八
2023-09-11 14:20:47 时间

GD32F4XX的测试笔记(2)(调用DSP的FFT算法)

上周实现串口通信和打印字符串功能,这周一开始的工作是先了解整个硬件已有管脚的定义,进行记录和实验;再了解串口实现数据传输的原理;最后导入算法库进行实验,主要以FFT为主,后面会对更多的算法库中函数进行实验。

软件工具:Keil4,STM32 ST-LINK Utility,串口工具,SerialPlot绘图串口工具
硬件工具:GD32F450ZGT6开发板,ST-Link编译器

1.按键和开关

LED1LED2S4S5
电源开关指示灯PC00(可以用来测试)PC01(可以用来测试)reset按键

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.实时绘图串口数据

2.1有关串口配置的函数

随着学习也慢慢发现:之前阅读当很多涉及到寄存器层面不用太多关注了,最重要的是了解头文件下的函数含义。这里总结了在串口配置函数和串口读写数据的函数:

第一个函数是给串口设置最高的优先级,以免被占用
第二个函数很简单,已经配置完成,可以直接使用。
第三四个函数是给串口设置模式:

在这里插入图片描述

    /* USART interrupt configuration */
    nvic_irq_enable(USART0_IRQn, 0, 0);
    
    /* configure EVAL_COM0 */
    gd_eval_com_init(EVAL_COM0);
    
    /* enable USART0 receive interrupt */
    usart_interrupt_enable(USART0, USART_INT_RBNE);
    
    /* enable USART0 transmit interrupt */
    usart_interrupt_enable(USART0, USART_INT_TBE);
    //第二个参数通过上图得到如下:
      \arg        USART_INT_PERR: parity error interrupt
      \arg        USART_INT_TBE: transmitter buffer empty interrupt
      \arg        USART_INT_TC: transmission complete interrupt
      \arg        USART_INT_RBNE: read data buffer not empty interrupt and overrun error interrupt
      \arg        USART_INT_IDLE: IDLE line detected interrupt
      \arg        USART_INT_LBD: LIN break detected interrupt
      \arg        USART_INT_ERR: error interrupt
      \arg        USART_INT_CTS: CTS interrupt
      \arg        USART_INT_RT: interrupt enable bit of receive timeout event
      \arg        USART_INT_EB: interrupt enable bit of end of block event

配置过后,tx_buffer就表示MCU通过串口传输给PC的数据,rx_buffer存储PC返回串口的数据/变量。他们二者记录数据逐一从缓存区传输出去,从而当缓存区数据空时表示当前操作完成。
拿传输数据为例,nbr_data_to_send为传输数据的数目,当tx_counter增加至nbr_data_to_send时表示完成传输,停止等待。

/* wait until USART0 send the tx_buffer */
    while(tx_counter < nbr_data_to_send){
    }

那tx_counter是为什么会主动实现逐一加一呢?通过查找头文件,发现这两个变量的运作主要通过以下函数。前一个if语句表示,当USART_INT_FLAG_RBNE(接收到的数据可以读取)标志作为中断,也就意味着打开了数据传输的中断,则进入判断,再逐一将rx_buffer值送入USART0,所以这里也能解释为什么这两个变量会主动加一。第二个if语句同理操作tx_counter。

void USART0_IRQHandler(void)
{
    if((RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) && 
       (RESET != usart_flag_get(USART0, USART_FLAG_RBNE))){
        /* Read one byte from the receive data register */
        rx_buffer[rx_counter++] = (uint8_t)usart_data_receive(USART0);

        if(rx_counter >= nbr_data_to_read){
            /* disable the USART0 receive interrupt */
            usart_interrupt_disable(USART0, USART_INT_RBNE);
        }
    }
       
    if((RESET != usart_flag_get(USART0, USART_FLAG_TBE)) && 
       (RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_TBE))){
        /* Write one byte to the transmit data register */
        usart_data_transmit(USART0, tx_buffer[tx_counter++]);

        if(tx_counter >= nbr_data_to_send){
            /* disable the USART0 transmit interrupt */
            usart_interrupt_disable(USART0, USART_INT_TBE);
        }
    }
}

2.2 串口工具

在这里插入图片描述
https://github.com/hyOzd/serialplot
这里实验了一下输入一段2048点的数据,再将串口配置调成和代码一致(波特率,数据格式等):
在这里插入图片描述
在这里插入图片描述
再将keil程序的tx_buffer设置成官方算法库的一个数据库(2048个点),烧入MCU,实时绘图串口得到数据如下图:
在这里插入图片描述
这里拿MATLAB验证了一下:
在这里插入图片描述

3.FFT实验

3.1 导入DSP算法库

在官网中下载官方DSP算法库,只需要导入.lib文件。此文件包含了所有算法库头文件,只需要在前面包含头文件即可。
在这里插入图片描述

#include "arm_math.h"
#include "arm_const_structs.h"

3.2 添加宏定义

添加:USE_STDPERIPH_DRIVER,GD32F450,ARM_MATH_CM4,__CC_ARM,ARM_MATH_MATRIX_CHECK,ARM_MATH_ROUNDING
(英文逗号隔开)
在这里插入图片描述

3.3 调用算法库函数

需要调用三个主要的函数:
第一个函数是做fft,第二个函数是将结果求幅值,第三个函数是寻找频谱最高峰及其位置。
算法库的函数说明都可以在HTML格式网页找到函数说明:

… \en.stsw-stm32065_v1-9-0_v1.9.0\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\CMSIS\index.HTML

  /* Process the data through the CFFT/CIFFT module */
  arm_cfft_f32(&arm_cfft_sR_f32_len1024, testInput, ifftFlag, doBitReverse);

  /* Process the data through the Complex Magnitude Module for
  calculating the magnitude at each bin */
  arm_cmplx_mag_f32(testInput, tx_buffer, fftSize);

  /* Calculates maxValue and returns corresponding BIN value */
  arm_max_f32(tx_buffer, fftSize, &maxValue, &testIndex);

在这里插入图片描述
在这里插入图片描述

3.4 生成数据进行计算,将输出数据存入tx_buffer(缓存区)

第一步:生成数据testInput:三个谐波分量和一个直流分量;并且需要定义输出数据数组tx_buffer。输入数据是以奇数为实部,偶数为虚部保存。在这里,所以偶数项设为零,即虚部全为零。

	for(int i=0; i<FFT_LENGTH; i++)
{
testInput[2*i]=10+10*arm_sin_f32(2*PI*i*10/FFT_LENGTH)+10*arm_sin_f32(2*PI*i*50/FFT_LENGTH)+10*arm_cos_f32(2*PI*i*300/FFT_LENGTH);
testInput[2*i+1]=0;
}

float32_t tx_buffer[TEST_LENGTH_SAMPLES/2];

第二步:带入上述的三个函数

第三步:打开传输中断,等待数据缓冲区通过串口到PC

    /* enable USART0 transmit interrupt */
    usart_interrupt_enable(USART0, USART_INT_TBE);

    /* wait until USART0 send the tx_buffer */
    while(tx_counter < nbr_data_to_send){
    }

3.5 绘图串口显示

在这里插入图片描述

3.6 数据以字符串的形式显示或存储

只需要将打开中断程序注释掉,添加循环打印的程序:

	for(int i=0;i<1024;i++)
	{
		printf(" %f",tx_buffer[i]);
	}

在这里插入图片描述
如红色箭头所示,验证了寻找数组中最大值的函数。再用MATLAB画出输出数据:
在这里插入图片描述
频率点位置也分别在1 11 51 301,并且直流分量是谐波分量的两倍。

4.记录错误

4.1 error: #268

error: #268: declaration may not appear after executable statement in block

for(int i=0;i<1024;i++)

此错误中文意思:声明不能在可执行语句后出现。
C89不支持在函数内定义,需要使用C99
而keil 默认编译标准是C89,解决方法:
在这里插入图片描述

4.2 printf打印出错

原因一:
printf程序写在所以配置后
原因二:
需要加入以下函数用于打印:

/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{
    usart_data_transmit(EVAL_COM0, (uint8_t)ch);
    while(RESET == usart_flag_get(EVAL_COM0, USART_FLAG_TBE));
    return ch;
}

4.3 error: #169

expected a declaration

原因一:
注释符//,()需要在英文下输入
原因二:
有些语句一定要在主函数下进行

4.4 warning:#47-D

重复定义,可能在其他文件下已经定义过了,需要删一个,最好删除主程序的,尽量不动头文件。

5 下周任务

调用其他算法库的其他函数,尽量把常用的函数都过一遍
在这里插入图片描述