【N32G401开发板】国民技术开发板FPU、DSP、高速比较器功能测评(增加图片)

N32G401芯片概述


N32G401芯片是国民技术(NationzTechnologies)推出的一款高性价比的32位MCU(微控制器单元),它采用了Arm® Cortex®-M4F内核,具有高性能和丰富的外设资源。以下是关于N32G401芯片使用的一些关键点和步骤:

一、芯片特点

  • 内核:基于Arm Cortex-M4F,支持浮点运算和DSP指令,适用于需要复杂数学运算的应用。
  • 主频:最高工作主频可达72MHz,提供强劲的处理能力。
  • 存储:集成高达64KB的嵌入式加密Flash和8KB的SRAM,满足大多数应用的数据存储需求。
  • 外设:集成多个高性能模拟器件,如1个12bit 4.2Msps ADC、3个高速比较器等,以及多个数字通信接口,如4个U(S)ART、2个I2C、2个SPI等。
  • 应用领域:适用于电控、照明、扫地机、无线充、智能门锁、车身电子、无线麦克风、报警装置等多个领域。

二、开发环境搭建

  • 获取开发工具
    • 选择合适的IDE(如Keil MDK-ARM、IAR Embedded Workbench、STM32CubeIDE等),这些IDE都支持Arm Cortex-M系列微控制器的开发。
    • 下载并安装所选IDE的最新版本。
  • 获取N32G401的官方支持包
    • 通常,芯片厂商会提供包含启动文件、外设驱动等的官方支持包,可以从国民技术的官方网站或第三方资源下载。
  • 配置项目
    • 在IDE中创建一个新的项目,并选择N32G401作为目标芯片。
    • 导入官方支持包中的相关文件,包括启动文件、头文件、源文件等。
    • 配置项目的编译选项、链接选项等,确保它们与N32G401芯片兼容。

三、代码编写与调试

  • 编写代码
    • 根据应用需求,编写或移植相应的代码。可以使用C或C++等编程语言。
    • 调用N32G401的库函数或API来操作外设,如GPIO、ADC、UART等。
  • 调试
    • 使用IDE提供的调试工具进行代码调试,如设置断点、观察变量值、单步执行等。
    • 如果需要,可以连接外部调试器(如JTAG或SWD调试器)进行更深入的调试。
  • 优化
    • 根据调试结果对代码进行优化,以提高程序的执行效率和稳定性。
    • 优化可能包括算法改进、内存管理优化、代码结构优化等。

四、烧录与测试

  • 烧录程序
    • 使用编程器(如JTAG编程器、SWD编程器等)将编译好的程序烧录到N32G401芯片中。
    • 确保烧录过程中芯片处于正确的编程模式,并且电源电压稳定。
  • 测试
    • 将烧录好的芯片安装到目标设备上,并进行功能测试。
    • 检查外设是否正常工作,如LED是否按预期闪烁、传感器数据是否正确读取等。

五、注意事项

  • 在使用N32G401芯片时,务必参考其官方数据手册和用户手册,以确保正确配置和使用芯片。
  • 在进行硬件设计和电路布局时,要注意遵守相关的电气规范和布局规范,以确保电路的稳定性和可靠性。
在进行软件开发时,要注意代码的可读性和可维护性,以便于后续的维护和升级。

FPU功能测试

芯片原厂提供的Demo中有FPU相关的测试例程可以参考:M4F_FPU工程。

1.png

2.png

程序使用一个函数测试GenerateJulia_fpu()浮点运算速度。

定义了相关的宏:

  1. #define SUBMODE_FPU_USED_MODE     ((uint8_t)1)
  2. #define SUBMODE_FPU_NOT_USED_MODE ((uint8_t)0)

  3. #if (__FPU_USED == 1)
  4. #define SCORE_FPU_MODE    "FPU On"
  5. #define PROGRESS_FPU_MODE "FPU ON"
  6. #else
  7. #define SCORE_FPU_MODE    "FPU Off"
  8. #define PROGRESS_FPU_MODE "FPU OFF"

测试过程中,简单更改SUBMODE_FPU_USED_MODE和SUBMODE_FPU_NOT_USED_MODE并不能够实现开关FPU功能,网上查阅资料之后发现可以通过更改下图框中选项配置来开关该功能。

3.png 4.png

开启FPU结果:计算时间为85ms

关闭FPU结果:计算时间为1161ms

两个对比可以看出开启FPU对于浮点数运算有很大的提升。


DSP使用

测试DSP的时候查找芯片使用手册的时候,感到了一头雾水。不知道应该从何下手。在群里一位朋友说的使用cubemx开发的启发下,来了一点灵感。就按照STM32的使用方法操作试试,反正都是基于ARM Cortex-M4F 内核。

参考:CubeMX生成的STM32F4xx MDK工程FPU和DSP库的使用_fpu present-CSDN博客

实现DSP相关配置:  
1、DSP库初识

STM32F4的[url=]Cortex-M4内核[/url]不仅内置硬件FPU单元,还支持DSP多种指令集,比如支持单周期乘加指令(MAC)、优化的单指令多数据指令(SIMD)等。因此Cortex-M4执行所有的DSP指令集都可以在单周期内完成,而Cortex-M3和M0需要多个指令和多个周期才能完成同样的功能。比如开方运算,M3和M0只能通过迭代法(标准数学函数库)计算,而M4F直接调用VSQRT指令完成。N32G401芯片也是Cortex-M4内核芯片,


2、获取DSP库源码

- 直接从STM32HAL库中获取DSP

- 直接在keil中配置DSP

- 从git上获取最新版本的DSP(https://gitcode.com/gh_mirrors/cm/CMSIS-DSP.git

3、DSP源码分析

采用第一种方式获取DSP,先了解下DSP文件。打开文件夹:STM32Cube_FW_F4_V1.26.0→ Drivers→CMSIS→DSP可以看到DSP的源码:DSP 源码包的 Source 文件夹是所有 DSP 库的源码,Examples 文件夹是相对应的一些测试实例。这些测试实例都是带 main 函数的,也就是拿到工程中可以直接使用。下面为Source 源码文件夹下面的子文件夹包含的 DSP 库的功能。

  ⚫ BasicMathFunctions 基本数学函数:提供浮点数的各种基本运算函数,如向量加减乘除等运算。

  ⚫ CommonTables 文件提供位翻转或相关参数表。arm_const_structs.c 文件提供一些常用的常量结构体,方便用户使用。

  ⚫ ComplexMathFunctions 复数学功能,如向量处理,求模运算。

  ⚫ ControllerFunctions 控制功能函数。包括正弦余弦,PID 电机控制,矢量 Clarke 变换,矢量 Clarke 逆变换。

  ⚫ FastMathFunctions 快速数学功能函数。提供了一种快速的近似正弦,余弦和平方根等相比 CMSIS 计算库要快的数学函数。

  ⚫ FilteringFunctions 滤波函数功能,主要为 FIR 和LMS(最小均方根)等滤波函数。

  ⚫ MatrixFunctions 矩阵处理函数。包括矩阵加法、矩阵初始化、矩阵反、矩阵乘法、矩阵规模、矩阵减法、矩阵转置等函数。

  ⚫ StatisticsFunctions 统计功能函数。如求平均值、最大值、最小值、计算均方根 RMS、计算方差/标准差等。

  ⚫ SupportFunctions 支持功能函数,如数据拷贝,Q 格式和浮点格式相互转换,Q 任意格式相互转换。

  ⚫ TransformFunctions 变换功能。包括复数 FFT(CFFT)/复数 FFT 逆运算(CIFFT)、实数 FFT(RFFT)/实数 FFT 逆运算(RIFFT)、和 DCT(离散余弦变换)和配套的初始化函数。

所有这些 DSP 库代码合在一起是比较多的,因此,ST 为我们提了.lib 格式的文件,方便使用。这些.lib 文件就是由Source 文件夹下的源码编译生成的,如果想看某个函数的源码,大家可以在 Source 文件夹下面查找。.lib格式文件 HAL 库包路径:Drivers→CMSIS→Lib→ARM,总共有 4 个.lib 文件,如下:

① arm_cortexM4b_math.lib (Cortex-M4 大端模式)

② arm_cortexM4l_math.lib (Cortex-M4 小端模式)

③ arm_cortexM4bf_math.lib (浮点 Cortex-M4 大端模式)

④ arm_cortexM4lf_math.lib (浮点 Cortex-M4 小端模式)

我们得根据所用 MCU 内核类型以及端模式来选择符合要求的.lib 文件,N32G401 属于Cortex-M4 内核,双精度浮点小端模式,应选择:arm_cortexM4lf_math.lib (单精度浮点 Cortex-M4 小端模式)。

4、搭建DSP库运行环境
从C:\Users\xmfu\STM32Cube\Repository\STM32Cube_FW_F4_V1.26.2\Drivers\CMSIS\Lib\ARM复制文件库文件添加文件到项目文件夹中:
5.png


添加好.lib 文件后,我们要添加头文件包含路径:
6.png
添加全局宏定义:

将全局变量使用这个替换:N32G401, USE_STDPERIPH_DRIVER, ARM_MATH_CM4,
ARM_MATH_MATRIX_CHECK,ARM_MATH_ROUNDING

7.png
开启FPU功能。
编写测试例程:
  1. /**
  2. * @brief       sin cos 测试
  3. * @param       angle : 起始角度
  4. * @param       times : 运算次数
  5. * @param       mode  : 是否使用DSP库
  6. *   @arg       0 , 不使用DSP库;
  7. *   @arg       1 , 使用DSP库;
  8. *
  9. * @retval      无
  10. */
  11. uint8_t sin_cos_test(float angle, uint32_t times, uint8_t mode)
  12. {
  13.     float sinx, cosx;
  14.     float result;
  15.     uint32_t i = 0;

  16.     if (mode == 0)
  17.     {
  18.         for (i = 0; i < times; i++)
  19.         {
  20.   cosx = cosf(angle);                 /* 不使用DSP优化的sin,cos函数 */
  21.   sinx = sinf(angle);
  22.   result = sinx * sinx + cosx * cosx; /* 计算结果应该等于1 */
  23.   result = fabsf(result - 1.0f);      /* 对比与1的差值 */

  24.             if (result > DELTA)return 0XFF;     /* 判断失败 */

  25.             angle += 0.001f;                    /* 角度自增 */
  26.         }
  27.     }
  28.     else
  29.     {
  30.         for (i = 0; i < times; i++)
  31.         {
  32.             cosx = arm_cos_f32(angle);          /* 使用DSP优化的sin,cos函数 */
  33.             sinx = arm_sin_f32(angle);
  34.             result = sinx * sinx + cosx * cosx; /* 计算结果应该等于1 */
  35.             result = fabsf(result - 1.0f);      /* 对比与1的差值 */

  36.             if (result > DELTA)return 0XFF;     /* 判断失败 */

  37.             angle += 0.001f;                    /* 角度自增 */
  38.         }
  39.     }

  40.     return 0;                                   /* 任务完成 */
  41. }
  42. int main(void)
  43. {
  44.     RCC_ClocksType RCC_ClocksStatus;

  45.     RCC_Clocks_Frequencies_Value_Get(&RCC_ClocksStatus);
  46.    
  47.     TIM_Basic_init();
  48.    
  49.     InitTIMER();

  50.     usart_init();

  51.     /* Output a message on Hyperterminal using printf function */
  52.     printf("\n\rUSART Printf Example: retarget the C library printf function to the USART\n\r");
  53.    
  54.    
  55.     printf("data = %f\n\r",data);
  56.     /* Start generating the fractal */
  57.     while (1)
  58.     {
  59.         /* Start the timer */
  60.         StartTIMER();
  61.         
  62.         data = sin_cos_test(PI / 6, 1000000, 0);
  63.         
  64.         score_fpu = StopTIMER();

  65.         printf("%s : %dms\r\n", PROGRESS_FPU_MODE, score_fpu/2);

  66.         /* Start the timer */
  67.         StartTIMER();
  68.         
  69.         data = sin_cos_test(PI / 6, 1000000, 1);
  70.         
  71.         score_fpu = StopTIMER();

  72.         printf("%s : %dms\r\n", SCORE_DSP_MODE, score_fpu/2);

  73.         break;
  74.     }
  75.     while (1)
  76.     {
  77.    
  78.     }
  79. }
测试结果:
9.png
开启使用DSP库计算1000000次运算时间明显比不使用DSP运算时间短。

比较器使用

1、比较器相关参数了解

参考数据手册:

10.png
比较器系统连接图:
11.png 12.png
COMP配置流程

参考使用手册,根据项目需要配置相关寄存器。

13.png
参考Demo程序可以直接测试比较器的响应速度。
14.png
将Demo程序烧录开发板中,将PB11连接到PB10,PB12连接到PA5,将PA5、PB10、PA11连接到逻辑分析仪,测试波形: 15.png
比较器功能正常
16.png
比较器响应时间:100ns
使用逻辑分析仪测试响应时间可能不准确,最好是使用示波器测试。

PID测试模型

PID(比例-积分-微分)控制器是一种广泛使用的反馈回路组件,用于控制各种过程,如温度、速度、位置等。在C语言中实现PID控制器通常涉及定义PID结构体、初始化PID参数、以及实现PID算法的函数。以下是一个简单的PID控制器实现示例:

1. 定义PID结构体
首先,我们需要定义一个PID结构体来存储PID控制器的参数和状态。
  1. typedef struct {
  2.     double Kp;       // 比例增益
  3.     double Ki;       // 积分增益
  4.     double Kd;       // 微分增益
  5.     double SetPoint; // 目标设定点
  6.     double Integral; // 积分项
  7.     double PrevError;// 上一次误差
  8. } PID;
2. 初始化PID控制器
接下来,我们实现一个函数来初始化PID控制器的参数。
  1. void PID_Init(PID *pid, double Kp, double Ki, double Kd, double SetPoint) {
  2.     pid->Kp = Kp;
  3.     pid->Ki = Ki;
  4.     pid->Kd = Kd;
  5.     pid->SetPoint = SetPoint;
  6.     pid->Integral = 0.0;
  7.     pid->PrevError = 0.0;
  8. }
3. 实现PID算法
然后,我们实现PID算法的核心函数,该函数根据当前测量值计算控制输出。
  1. double PID_Update(PID *pid, double MeasuredValue) {
  2.     double Error = pid->SetPoint - MeasuredValue; // 计算误差
  3.     pid->Integral += Error; // 积分项累加
  4.     double Derivative = Error - pid->PrevError; // 计算微分项

  5.     // 计算PID输出
  6.     double Output = pid->Kp * Error + pid->Ki * pid->Integral + pid->Kd * Derivative;

  7.     // 更新前一次误差
  8.     pid->PrevError = Error;

  9.     return Output;
  10. }
4. 使用PID控制器
最后,我们可以在主函数或任何需要控制的地方使用PID控制器。
  1. #include <stdio.h>

  2. int main() {
  3.     PID myPID;
  4.     PID_Init(&myPID, 0.1, 0.01, 0.05, 100.0); // 初始化PID参数

  5.     double measuredValue = 0.0; // 假设的初始测量值
  6.     for (int i = 0; i < 100; i++) {
  7.         // 模拟测量值变化
  8.         measuredValue = 90.0 + (i % 10); // 假设的测量值变化

  9.         // 更新PID并获取控制输出
  10.         double control = PID_Update(&myPID, measuredValue);

  11.         // 输出控制值(实际应用中,这个值会用于控制被控对象)
  12.         printf("Control: %f\n", control);

  13.         // 在实际应用中,这里可能还有将被控对象的实际响应反馈回系统的代码
  14.     }

  15.     return 0;
  16. }

注意:以上代码仅用于演示PID控制器的实现和基本用法。在实际应用中,PID参数的调整(即Kp、Ki、Kd的值)是非常关键的,通常需要根据被控对象的特性和期望的响应来仔细调整。此外,对于某些系统,可能还需要加入抗饱和、死区等额外的控制逻辑。

实际项目根据需要对PID算法进行裁剪,下面将测试下PID运算速度。
17.png
测试结果:
18.png

芯片使用感受

1、按照官网提供的资料可以很快的下载到芯片使用相关手册、芯片使用Demo。基本的功能都能够在Demo上找到。

2、芯片使用的过程中,阅读芯片手册的时候感觉有些内容没有写出来。这个对新手不是很友好。在DSP的使用过程中,该开始按照常规的开发思路阅读手册写程序还真不知从何下手(PS:之前没有使用过DSP功能)。后面将这个芯片当作STM32替代使用有了一点豁然开朗的感觉。

3、硬件上的一些缺陷在相关资料中有提到过,使用的是开发板,没有对硬件部分测试。


资料整理到gitee:
https://gitee.com/xmfu1012/n32-g401.git