打印
[开发工具]

向数字信号处理器更进一步-STM32G431RB Nucleo开发板评测

[复制链接]
5310|23
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
前言
STM32G4系列是ST新推出的系列, 主打信号处理方向的应用. 从配置上来讲, 类似于之前的STM32F3系列的升级版本, 各种参数上又有很多进步. 尤其是增加了定点计算加速器CORDICFMA, 使得这个系列向DSP又进了一步.有的同学会有疑问, 既然这个系列已经有了单精度的FPU, 增加CORDIC, FMA这些外设是否有点多余. 后面的章节会对这个CORDIC单元做一个Benchmark, 结果显示在牺牲一定的精度的情况下CORDIC比起FPU计算有很大的性能优势. 在某些应用领域, 能够实时得到运算结果比绝对精度要更重要. 比如电机控制, 数字电源等领域, 算法本身有反馈, 如果能迅速迭代,修正, 那么输出稳定的速度快很多. 工业控制领域中的很多传感器数据处理时, 快速得到一定精度的结果也比追求绝对精度要更适合应用场景. 这也是以前的DSP在微控制器市场拥有独特地位的原因.

争论DSP, ARM哪个好没有意义, 但是现在嵌入式应用的发展方向就是: 数据来源越来越多, 对数据的处理的要求越来越多. 所以控制器的计算能力的需求也越来越大, 计算能力并非仅仅取决于处理器内核的位宽和CPU运行速度. 还有几个重要因素:

  • 1.      内核能配备适合数**算的指令架构, 这一点传统的DSP所擅长, 指令上有很多专业做向量运算, Cortex M3, Cortex M4也有一些这样的指令
  • 2.      硬件加速单元, FPU也算是加速单元. 但是FPU对芯片门数增加很多,而且目前大多数的嵌入式应用中并不需要FPU那样的精度. 还有FPU虽然一般被认为是协处理器, 但是设计上常常跟CPU放在一起, 所以作浮点运算时常常也需要CPU同步等待结果. G4系列增加的CORDIC,FMA属于典型的硬件加速单元. 大多STM32上存在的CRC计算单元也是硬件加速单元, 某些面向安全领域的AES加速器也是.
  • 3.      与内核紧密配合的软件运算库. 用过DSP的程序员都知道, 芯片厂商一般都会提供针对特定内核优化过的运算库. 包括常见的电机控制算法, PID算法, 矩阵算法, 滤波器算法等等. ARM公司也有一个类似的算法库:CMSIS DSP, 但是这个库跟传统DSP厂商比还是一个起步阶段.


STM32G4这个系列大概就是一个直接与DSP对标的系列, 从性价比,STM32的开发者生态来讲, 是很有竞争力的.



board_by_mi9.jpg (518.87 KB )

board_by_mi9.jpg

使用特权

评论回复
评论
lihui567 2019-12-2 20:01 回复TA
带V3啊 
沙发
zhanzr21|  楼主 | 2019-9-7 15:44 | 只看该作者
芯片与板子
这个板子也属于Nucleo 64系列, 但是同样叫做Nucleo 64, 硬件上也有很多改进的.

图 板子前视跟之前的Nucleo64板子相比:
  • 1.     板载调试器升级了, 现在的调试器主控是STM32F723, 和服务了很多年的STM32F103相比,强大了不少, 至于具体功能还没有发现很大的升级, 但是起码有很大的升级空间.
  • 2.     增加了MIPI10接口的调试接口, 虽然只有非常专业的工程师才有这种接口的调试器吧.
  • 3.     主控芯片的两个晶体都焊接了. 以前的Nucleo系列板子有一个外接晶体没有焊接的.要么使用STLINK的MCO输出, 要么使用内部时钟,要么自己找个晶体焊接上去. 其实一个晶体能有多少成本, ST公司大钱都出了, 之前真是没有想通, 给工程师带来方便是最重要的.
除此之外就没有很多新意了,最大程度保持跟原有的Nucleo开发生态兼容.
图开发板框图
官方的详解图(G474板子那里抄的,但是G431板子跟这个同一批)

STM32G431芯片框图(感兴趣的可以跟STM32F3系列对比下)
板子就介绍到这里, 老司机们应该对这个板子都很熟, 实在没有很多需要介绍的.


使用特权

评论回复
板凳
zhanzr21|  楼主 | 2019-9-7 15:50 | 只看该作者
软件-HelloWorld工程生成
STM32系列之所以在工程师心中有这么高的人气, 很大一个原因就是开发生态非常完善. 从初学者到高手, 都能很快上手, 很快开始自己的学习和工程设计.
早期的SPL(StandardPeripheral Library)开启了快速开发的风气, 现在STM32CubeMX+HAL库更是引领了图形化,模块化设计的趋势. 这里以使用CubeMX创建一个Hello World工程为例, 展示这种设计方式的优越性, 高手们可以跳过这一章节.
要输出Hello World, 需要一个输出口, 这个板子的板载STlink确实有模拟串口, 但是没有直接把模拟串口与芯片的串口连接起来, 芯片的串口连接到板子的Arduino接口上去了. 手边没有烙铁, 所以这个工程使用SWV作为输出.

图串口默认连接到Arduino接口上去了
首先打开STM32CubeMX, 没有安装的自行去下载安装, 很简单的, 这里不浪费篇幅.第二步安装STM32CubeMX G4支持包, 如果你之前没有安装的话:
图安装CubeMXG4支持包
这个包200多M, 网络安装不顺畅的话, 用户可以去官网找到下载连接使用专门的下载工具(如迅雷)下载来安装. 下一步新建工程, 选板子, 注意这个工具的板子图片是错误的还是用的原来的老图片, 这个小问题了, 虽然还是希望ST公司能够加以改进.

图选板子
图板子的图片错了

又是一张错误图片

使用特权

评论回复
地板
zhanzr21|  楼主 | 2019-9-7 15:53 | 只看该作者
选好板子后, 就使用默认的配置就可以生成工程了, 本人使用Keil MDK.

图生成工程
之后打开工程, 注意又要下载一次Keil MDKSTM32G4的支持包, 如果你之前没有下载的话, 这里还是有下载不顺畅的可能, 解决办法还是到官网找到下载连接使用专业下载工具.
编译之前还要配置一下输出重定向:
图配置重定向如果是重定向到串口的话, 后面的下拉框选User, 之后实现输出函数. ITM就不用自己实现输出函数了, ITM是CMSIS标准库的内容.主函数修改一点点:
int main(void) {
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */
  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */       
        printf("Hello World, %08X %u\n", SCB->CPUID, SystemCoreClock);
        /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1) {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}



install_keil_rte_stm32g4_pack.png (54.72 KB )

install_keil_rte_stm32g4_pack.png

使用特权

评论回复
5
zhanzr21|  楼主 | 2019-9-7 15:54 | 只看该作者
编译,下载, 打开任何一个可以看ITM输出的工具, 这里使用STLink Utility,SWV显示:
SWV输出显示至此, HelloWorld工程就创建完毕. 整个工程代码, 包括CubeMX配置文件都在后文有下载连接.

使用特权

评论回复
6
zhanzr21|  楼主 | 2019-9-7 15:57 | 只看该作者
本帖最后由 zhanzr21 于 2019-9-7 16:06 编辑

软件-CORDIC数学和定点运算
据我所知, G4系列应该是第一个配备CORDIC单元的STM32系列.
CORDIC数学计算对于做过DSP的工程师应该不陌生, 但是对于大多数STM32开发者来讲应该是个模糊的概念. 这里简单介绍下.
CORDICSTM32G4上是个协处理器, 也可以叫做硬件加速单元. 它用坐标转换的方法来计算, 只能接受定点数, 目前支持的函数: Sine, Cosine,Sinh, Cosh, Atan, Atan2, Atanh, Modulus, Square root, Natural logarithm.内部精度为24bit,一般喂给它Q31.1的定点数,输出也是Q31.1,当然也可以喂给它Q15.1的定点数. 因为它的处理原理是加法+移位, 虽然动态范围比浮点运算小导致精度小, 但是运算起来比FPU做泰勒级数逼近要快很多.况且它是真正的协处理器, 可以跟DMA联合起来, 所以在精度牺牲一点的情况下比FPU计算要快,而且可以解放CPU做其他事情.所以在电机控制, 传感器数据前置处理的应用中这种运算用的很广泛.
Q格式的定点数,如果使用过CMSIS DSP的程序员可能比较了解, 但是估计大多数程序员还是很陌生, 也介绍下.
简而言之, Q定点数, 就是用整型数来表示小数的一种方式.
Q31.1使用32bit的整数来表示-1到1的区间,也有人用来表示0到2的区间, 内部原理是一致的. 大多数运算中, 采用-1到1的区间.Q格式与浮点数的转换方式:
将浮点数转换为q._n._t的格式:
   1.浮点数乘以2的n次方
   2.四舍五入

将q._n._t格式转换为浮点格式:
   1.按照native的方式将浮点数转为定数(四舍五入)
   2.将上一步结果除以2的n次方
关于转换,本人有个简单的Python脚本可以供参考.
import os,sys,math

INT32_MAX = (1<<31)-1
UINT32_MAX = (1<<32)-1

ARRAY_SIZE = 64
TEST_CSV_FILE = "test_q.csv"

test_q_angle_cordic = [0x00000000, 0x04000000, 0x08000000, 0x0C000000,
  0x10000000, 0x14000000, 0x18000000, 0x1C000000,
  0x20000000, 0x24000000, 0x28000000, 0x2C000000,
  0x30000000, 0x34000000, 0x38000000, 0x3C000000,
  0x40000000, 0x44000000, 0x48000000, 0x4C000000,
  0x50000000, 0x54000000, 0x58000000, 0x5C000000,
  0x60000000, 0x64000000, 0x68000000, 0x6C000000,
  0x70000000, 0x74000000, 0x78000000, 0x7C000000,
  0x80000000, 0x84000000, 0x88000000, 0x8C000000,
  0x90000000, 0x94000000, 0x98000000, 0x9C000000,
  0xA0000000, 0xA4000000, 0xA8000000, 0xAC000000,
  0xB0000000, 0xB4000000, 0xB8000000, 0xBC000000,
  0xC0000000, 0xC4000000, 0xC8000000, 0xCC000000,
  0xD0000000, 0xD4000000, 0xD8000000, 0xDC000000,
  0xE0000000, 0xE4000000, 0xE8000000, 0xEC000000,
  0xF0000000, 0xF4000000, 0xF8000000, 0xFC000000]

test_q_angle_cmsis_dsp = [  0x00000000, 0x02000000, 0x04000000, 0x06000000,
  0x08000000, 0x0A000000, 0x0C000000, 0x0E000000,
  0x10000000, 0x12000000, 0x14000000, 0x16000000,
  0x18000000, 0x1A000000, 0x1C000000, 0x1E000000,
  0x20000000, 0x22000000, 0x24000000, 0x26000000,
  0x28000000, 0x2A000000, 0x2C000000, 0x2E000000,
  0x30000000, 0x32000000, 0x34000000, 0x36000000,
  0x38000000, 0x3A000000, 0x3C000000, 0x3E000000,
  0x40000000, 0x42000000, 0x44000000, 0x46000000,
  0x48000000, 0x4A000000, 0x4C000000, 0x4E000000,
  0x50000000, 0x52000000, 0x54000000, 0x56000000,
  0x58000000, 0x5A000000, 0x5C000000, 0x5E000000,
  0x60000000, 0x62000000, 0x64000000, 0x66000000,
  0x68000000, 0x6A000000, 0x6C000000, 0x6E000000,
  0x70000000, 0x72000000, 0x74000000, 0x76000000,
  0x78000000, 0x7A000000, 0x7C000000, 0x7E000000]

test_q_ref_sin = [  0x00000000, 0x0C8BD35E, 0x18F8B83C, 0x25280C5D,
  0x30FBC54D, 0x3C56BA70, 0x471CECE6, 0x5133CC94,
  0x5A827999, 0x62F201AC, 0x6A6D98A4, 0x70E2CBC6,
  0x7641AF3C, 0x7A7D055B, 0x7D8A5F3F, 0x7F62368F,
  0x80000000, 0x7F62368F, 0x7D8A5F3F, 0x7A7D055B,
  0x7641AF3C, 0x70E2CBC6, 0x6A6D98A4, 0x62F201AC,
  0x5A827999, 0x5133CC94, 0x471CECE6, 0x3C56BA70,
  0x30FBC54D, 0x25280C5D, 0x18F8B83C, 0x0C8BD35E,
  0x00000000, 0xF3742CA2, 0xE70747C4, 0xDAD7F3A3,
  0xCF043AB3, 0xC3A94590, 0xB8E3131A, 0xAECC336C,
  0xA57D8667, 0x9D0DFE54, 0x9592675C, 0x8F1D343A,
  0x89BE50C4, 0x8582FAA5, 0x8275A0C1, 0x809DC971,
  0x80000000, 0x809DC971, 0x8275A0C1, 0x8582FAA5,
  0x89BE50C4, 0x8F1D343A, 0x9592675C, 0x9D0DFE54,
  0xA57D8667, 0xAECC336C, 0xB8E3131A, 0xC3A94590,
  0xCF043AB3, 0xDAD7F3A3, 0xE70747C4, 0xF3742CA2]

test_q_ref_cos = [  0x80000000, 0x7F62368F, 0x7D8A5F3F, 0x7A7D055B,
  0x7641AF3C, 0x70E2CBC6, 0x6A6D98A4, 0x62F201AC,
  0x5A827999, 0x5133CC94, 0x471CECE6, 0x3C56BA70,
  0x30FBC54D, 0x25280C5D, 0x18F8B83C, 0x0C8BD35E,
  0x00000000, 0xF3742CA2, 0xE70747C4, 0xDAD7F3A3,
  0xCF043AB3, 0xC3A94590, 0xB8E3131A, 0xAECC336C,
  0xA57D8667, 0x9D0DFE54, 0x9592675C, 0x8F1D343A,
  0x89BE50C4, 0x8582FAA5, 0x8275A0C1, 0x809DC971,
  0x80000000, 0x809DC971, 0x8275A0C1, 0x8582FAA5,
  0x89BE50C4, 0x8F1D343A, 0x9592675C, 0x9D0DFE54,
  0xA57D8667, 0xAECC336C, 0xB8E3131A, 0xC3A94590,
  0xCF043AB3, 0xDAD7F3A3, 0xE70747C4, 0xF3742CA2,
  0x00000000, 0x0C8BD35E, 0x18F8B83C, 0x25280C5D,
  0x30FBC54D, 0x3C56BA70, 0x471CECE6, 0x5133CC94,
  0x5A827999, 0x62F201AC, 0x6A6D98A4, 0x70E2CBC6,
  0x7641AF3C, 0x7A7D055B, 0x7D8A5F3F, 0x7F62368F]

test_q_cordic_result = [0x00000600, 0x7FFFFE00,
0x0C8BD600, 0x7F623100,
0x18F8B700, 0x7D8A5A00,
0x25280600, 0x7A7D0700,
0x30FBC900, 0x7641A800,
0x3C56B400, 0x70E2CA00,
0x471CEC00, 0x6A6D9900,
0x5133C500, 0x62F20400,
0x5A827600, 0x5A827900,
0x62F20600, 0x5133C300,
0x6A6D9900, 0x471CEC00,
0x70E2CA00, 0x3C56B400,
0x7641AC00, 0x30FBC300,
0x7A7D0700, 0x25280600,
0x7D8A5A00, 0x18F8B500,
0x7F623100, 0x0C8BD400,
0x7FFFFF00, 0xFFFFFF00,
0x7F623200, 0xF3742900,
0x7D8A5B00, 0xE7074A00,
0x7A7D0800, 0xDAD7F100,
0x7641A800, 0xCF043C00,
0x70E2CB00, 0xC3A94B00,
0x6A6D9900, 0xB8E31100,
0x62F20400, 0xAECC3200,
0x5A827A00, 0xA57D8500,
0x5133C400, 0x9D0E0300,
0x471CED00, 0x95926A00,
0x3C56B500, 0x8F1D3700,
0x30FBC400, 0x89BE5500,
0x25280500, 0x85830100,
0x18F8B400, 0x8275A400,
0x0C8BD300, 0x809DCF00,
0xFFFFFA00, 0x80000200,
0xF3742A00, 0x809DCF00,
0xE7074900, 0x8275A600,
0xDAD7FA00, 0x8582F900,
0xCF043700, 0x89BE5800,
0xC3A94C00, 0x8F1D3600,
0xB8E31400, 0x95926700,
0xAECC3B00, 0x9D0DFC00,
0xA57D8A00, 0xA57D8700,
0x9D0DFA00, 0xAECC3D00,
0x95926700, 0xB8E31400,
0x8F1D3600, 0xC3A94C00,
0x89BE5400, 0xCF043D00,
0x8582F900, 0xDAD7FA00,
0x8275A600, 0xE7074B00,
0x809DCF00, 0xF3742C00,
0x80000100, 0x00000100,
0x809DCE00, 0x0C8BD700,
0x8275A500, 0x18F8B600,
0x8582F800, 0x25280F00,
0x89BE5800, 0x30FBC400,
0x8F1D3500, 0x3C56B500,
0x95926700, 0x471CEF00,
0x9D0DFC00, 0x5133CE00,
0xA57D8600, 0x5A827B00,
0xAECC3C00, 0x62F1FD00,
0xB8E31300, 0x6A6D9600,
0xC3A94B00, 0x70E2C900,
0xCF043C00, 0x7641AB00,
0xDAD7FB00, 0x7A7CFF00,
0xE7074C00, 0x7D8A5C00,
0xF3742D00, 0x7F623100,]

test_q_cmsis_result = [0x00000000, 0x7FFFFFFE,
0x0C8BD35E, 0x7F62368E,
0x18F8B83C, 0x7D8A5F40,
0x25280C5E, 0x7A7D055A,
0x30FBC54C, 0x7641AF3C,
0x3C56BA70, 0x70E2CBC6,
0x471CECE6, 0x6A6D98A4,
0x5133CC94, 0x62F201AC,
0x5A82799A, 0x5A82799A,
0x62F201AC, 0x5133CC94,
0x6A6D98A4, 0x471CECE6,
0x70E2CBC6, 0x3C56BA70,
0x7641AF3C, 0x30FBC54C,
0x7A7D055A, 0x25280C5E,
0x7D8A5F40, 0x18F8B83C,
0x7F62368E, 0x0C8BD35E,
0x7FFFFFFE, 0x00000000,
0x7F62368E, 0xF3742CA2,
0x7D8A5F40, 0xE70747C4,
0x7A7D055A, 0xDAD7F3A2,
0x7641AF3C, 0xCF043AB2,
0x70E2CBC6, 0xC3A94590,
0x6A6D98A4, 0xB8E31318,
0x62F201AC, 0xAECC336C,
0x5A82799A, 0xA57D8666,
0x5133CC94, 0x9D0DFE54,
0x471CECE6, 0x9592675C,
0x3C56BA70, 0x8F1D343A,
0x30FBC54C, 0x89BE50C2,
0x25280C5E, 0x8582FAA4,
0x18F8B83C, 0x8275A0C0,
0x0C8BD35E, 0x809DC970,
0x00000000, 0x80000000,
0xF3742CA2, 0x809DC970,
0xE70747C4, 0x8275A0C0,
0xDAD7F3A2, 0x8582FAA4,
0xCF043AB2, 0x89BE50C2,
0xC3A94590, 0x8F1D343A,
0xB8E31318, 0x9592675C,
0xAECC336C, 0x9D0DFE54,
0xA57D8666, 0xA57D8666,
0x9D0DFE54, 0xAECC336C,
0x9592675C, 0xB8E31318,
0x8F1D343A, 0xC3A94590,
0x89BE50C2, 0xCF043AB2,
0x8582FAA4, 0xDAD7F3A2,
0x8275A0C0, 0xE70747C4,
0x809DC970, 0xF3742CA2,
0x80000000, 0x00000000,
0x809DC970, 0x0C8BD35E,
0x8275A0C0, 0x18F8B83C,
0x8582FAA4, 0x25280C5E,
0x89BE50C2, 0x30FBC54C,
0x8F1D343A, 0x3C56BA70,
0x9592675C, 0x471CECE6,
0x9D0DFE54, 0x5133CC94,
0xA57D8666, 0x5A82799A,
0xAECC336C, 0x62F201AC,
0xB8E31318, 0x6A6D98A4,
0xC3A94590, 0x70E2CBC6,
0xCF043AB2, 0x7641AF3C,
0xDAD7F3A2, 0x7A7D055A,
0xE70747C4, 0x7D8A5F40,
0xF3742CA2, 0x7F62368E,]

def main():
    f_output = open(TEST_CSV_FILE, mode='wb')

    # The CORDIC Q Format input
    tmp_str = ("The CORDIC Q Format input\n")
    f_output.write(bytes(tmp_str, encoding="ansi"))
   
    for q in test_q_angle_cordic:
        if(q > INT32_MAX):
            q = q - (1<<32)
        f = q/(1<<31)
        tmp_str = ("%08X , %f\n" % (q, f))
        f_output.write(bytes(tmp_str, encoding="ansi"))            
        pass
   
    tmp_str = ("\n")
    f_output.write(bytes(tmp_str, encoding="ansi"))

    # The CMSIS DSP Q Format input
    tmp_str = ("The CMSIS DSP Q Format input\n")
    f_output.write(bytes(tmp_str, encoding="ansi"))   
    for q in test_q_angle_cmsis_dsp:
        if(q > INT32_MAX):
            q = q - (1<<32)        
        f = q/(1<<31)
        tmp_str = ("%08X , %f\n" % (q, f))
        f_output.write(bytes(tmp_str, encoding="ansi"))
        pass   
    pass

    tmp_str = ("\n")
    f_output.write(bytes(tmp_str, encoding="ansi"))

    # The Ref Sine Q Format input
    tmp_str = ("The Ref Sine Q Format input\n")
    f_output.write(bytes(tmp_str, encoding="ansi"))     
    for q in test_q_ref_sin:
        if(q > INT32_MAX):
            q = q - (1<<32)        
        f = q/(1<<31)
        tmp_str = ("%08X , %f\n" % (q, f))
        f_output.write(bytes(tmp_str, encoding="ansi"))
        pass   
    pass
    tmp_str = ("\n")
    f_output.write(bytes(tmp_str, encoding="ansi"))

    # The Ref Cos Q Format input
    tmp_str = ("The Ref Cos Q Format input\n")
    f_output.write(bytes(tmp_str, encoding="ansi"))   
    for q in test_q_ref_cos:
        if(q > INT32_MAX):
            q = q - (1<<32)        
        f = q/(1<<31)
        tmp_str = ("%08X , %f\n" % (q, f))
        f_output.write(bytes(tmp_str, encoding="ansi"))
        pass   
    pass
    tmp_str = ("\n")
    f_output.write(bytes(tmp_str, encoding="ansi"))

    # The Result of Cordic Calculation   
    tmp_str = ("The Result of Cordic Calculation\n")
    f_output.write(bytes(tmp_str, encoding="ansi"))
    res_len = len(test_q_cordic_result)
    for i in range(res_len//2):
        q = test_q_cordic_result[2*i]
        r = test_q_cordic_result[1 + (2*i)]
        
        if(q > INT32_MAX):
            q = q - (1<<32)        
        f = q/(1<<31)
        if(r > INT32_MAX):
            r = r - (1<<32)        
        g = r/(1<<31)
        
        tmp_str = ("%08X , %f, %08X , %f\n" % (q, f, r, g))
        f_output.write(bytes(tmp_str, encoding="ansi"))
        pass   
    pass
    tmp_str = ("\n")
    f_output.write(bytes(tmp_str, encoding="ansi"))

    # The Result of CMSIS DSP Calculation   
    tmp_str = ("The Result of CMSIS DSP Calculation\n")
    f_output.write(bytes(tmp_str, encoding="ansi"))
    res_len = len(test_q_cmsis_result)
    for i in range(res_len//2):
        q = test_q_cordic_result[2*i]
        r = test_q_cordic_result[1 + (2*i)]
        
        if(q > INT32_MAX):
            q = q - (1<<32)        
        f = q/(1<<31)
        if(r > INT32_MAX):
            r = r - (1<<32)        
        g = r/(1<<31)
        
        tmp_str = ("%08X , %f, %08X , %f\n" % (q, f, r, g))
        f_output.write(bytes(tmp_str, encoding="ansi"))
        pass   
    pass
    tmp_str = ("\n")
    f_output.write(bytes(tmp_str, encoding="ansi"))

    #Generate Ref Sine output
    print("The generated sine result")
    for i in range(ARRAY_SIZE):
        ang = (i * math.pi * 2)/ARRAY_SIZE

        d_sin_res = math.sin(ang)
        q_1_31_t = int(d_sin_res * (UINT32_MAX) / 2)
        if(q_1_31_t < 0):
            q_1_31_t = q_1_31_t + (1<<32)
        #print("0x%08X, %f" % (q_1_31_t, d_sin_res))
        print("0x%08X, " % q_1_31_t, end='')
        if(0 == (i+1)%4):
            print()
        pass
    print()
   
    print("The generated cos result")
    for i in range(ARRAY_SIZE):
        ang = (i * math.pi * 2)/ARRAY_SIZE
        
        d_cos_res = math.cos(ang)
        q_1_31_t = int(d_cos_res * (UINT32_MAX) / 2)
        if(q_1_31_t < 0):
            q_1_31_t = q_1_31_t + (1<<32)
        #print("0x%08X, %f" % (q_1_31_t, d_cos_res))
        print("0x%08X, " % q_1_31_t, end='')
        if(0 == (i+1)%4):
            print()
        pass
    print()
     
if __name__ == '__main__':
    main()


这些都有点枯燥, 但是用起来的话, 理解其实很容易.下面来做个简单的CORDIC运算的例子:
[0,2PI]也就是一个圆的弧度空间分为64个点, 求每个点的正弦值.
首先把CORDIC外设打开:

CORDIC配置配置就这么简单, 测试代码摘要如下, 整个工程代码在后文有下载连接.

使用特权

评论回复
7
zhanzr21|  楼主 | 2019-9-7 15:59 | 只看该作者
/* Array of angles in Q1.31 format, regularly incremented from 0 to 2*pi */
static int32_t aAngles[ARRAY_SIZE] =
{
  0x00000000, 0x04000000, 0x08000000, 0x0C000000,
  0x10000000, 0x14000000, 0x18000000, 0x1C000000,
  0x20000000, 0x24000000, 0x28000000, 0x2C000000,
  0x30000000, 0x34000000, 0x38000000, 0x3C000000,
  0x40000000, 0x44000000, 0x48000000, 0x4C000000,
  0x50000000, 0x54000000, 0x58000000, 0x5C000000,
  0x60000000, 0x64000000, 0x68000000, 0x6C000000,
  0x70000000, 0x74000000, 0x78000000, 0x7C000000,
  0x80000000, 0x84000000, 0x88000000, 0x8C000000,
  0x90000000, 0x94000000, 0x98000000, 0x9C000000,
  0xA0000000, 0xA4000000, 0xA8000000, 0xAC000000,
  0xB0000000, 0xB4000000, 0xB8000000, 0xBC000000,
  0xC0000000, 0xC4000000, 0xC8000000, 0xCC000000,
  0xD0000000, 0xD4000000, 0xD8000000, 0xDC000000,
  0xE0000000, 0xE4000000, 0xE8000000, 0xEC000000,
  0xF0000000, 0xF4000000, 0xF8000000, 0xFC000000
};

/* Array of reference sines in Q1.31 format */
static int32_t aRefSin[ARRAY_SIZE] =
{
  0x00000000, 0x0C8BD35E, 0x18F8B83C, 0x25280C5D,
  0x30FBC54D, 0x3C56BA70, 0x471CECE6, 0x5133CC94,
  0x5A827999, 0x62F201AC, 0x6A6D98A4, 0x70E2CBC6,
  0x7641AF3C, 0x7A7D055B, 0x7D8A5F3F, 0x7F62368F,
  0x80000000, 0x7F62368F, 0x7D8A5F3F, 0x7A7D055B,
  0x7641AF3C, 0x70E2CBC6, 0x6A6D98A4, 0x62F201AC,
  0x5A827999, 0x5133CC94, 0x471CECE6, 0x3C56BA70,
  0x30FBC54D, 0x25280C5D, 0x18F8B83C, 0x0C8BD35E,
  0x00000000, 0xF3742CA2, 0xE70747C4, 0xDAD7F3A3,
  0xCF043AB3, 0xC3A94590, 0xB8E3131A, 0xAECC336C,
  0xA57D8667, 0x9D0DFE54, 0x9592675C, 0x8F1D343A,
  0x89BE50C4, 0x8582FAA5, 0x8275A0C1, 0x809DC971,
  0x80000000, 0x809DC971, 0x8275A0C1, 0x8582FAA5,
  0x89BE50C4, 0x8F1D343A, 0x9592675C, 0x9D0DFE54,
  0xA57D8667, 0xAECC336C, 0xB8E3131A, 0xC3A94590,
  0xCF043AB3, 0xDAD7F3A3, 0xE70747C4, 0xF3742CA2
};
  /*## Configure the CORDIC peripheral ####################################*/
  sCordicConfig.Function         = CORDIC_FUNCTION_SINE;     /* sine function */
  sCordicConfig.Precision        = CORDIC_PRECISION_6CYCLES; /* max precision for q1.31 sine */
  sCordicConfig.Scale            = CORDIC_SCALE_0;           /* no scale */
  sCordicConfig.NbWrite          = CORDIC_NBWRITE_1;         /* One input data: angle. Second input data (modulus) is 1 after cordic reset */
  sCordicConfig.NbRead           = CORDIC_NBREAD_1;          /* One output data: sine */
  sCordicConfig.InSize           = CORDIC_INSIZE_32BITS;     /* q1.31 format for input data */
  sCordicConfig.OutSize          = CORDIC_OUTSIZE_32BITS;    /* q1.31 format for output data */

  if (HAL_CORDIC_Configure(&hcordic, &sCordicConfig) != HAL_OK)
  {
    /* Configuration Error */
                assert(0);
    Error_Handler();
  }

  /*## Start calculation of sines in DMA mode #############################*/
  if (HAL_CORDIC_Calculate_DMA(&hcordic, aAngles, aCalculatedSin,
                               ARRAY_SIZE, CORDIC_DMA_DIR_IN_OUT) != HAL_OK)
  {
    /* Processing Error */
                assert(0);
    Error_Handler();
  }

  /*  Before starting a new process, you need to check the current state of the peripheral;
      if it’s busy you need to wait for the end of current transfer before starting a new one.
      For simplicity reasons, this example is just waiting till the end of the
      process, but application may perform other tasks while transfer operation
      is ongoing. */
  while (HAL_CORDIC_GetState(&hcordic) != HAL_CORDIC_STATE_READY)
  {
  }
CORDIC运算的步骤一般是先把输入归一化到Q31.1的范围内,将输入喂给CORDIC等待运算完成又将Q31.1格式的结果转化为其他格式,如浮点数或者直接使用.


使用特权

评论回复
8
zhanzr21|  楼主 | 2019-9-7 16:02 | 只看该作者
软件-Benchmark
为了体现CORDIC运算的优越性, 这里来个简单的Benchmark.
运算目标:
[0,2PI]也就是一个圆的弧度空间分为64个点, 求每个点的正弦值和余弦值.
这个目标对CORDIC有点偏向, 因为CORDIC可以同时计算出正弦值和余弦值, 不过这也正是它的优势. 还需要再提一句, CORDIC只能输入定点数,所以计算前后一般都要做转换, 比起FPU和浮点库都是占有一定便宜, 但是绝对公平难以实现, 这个Benchmark也就是一个参考吧.
参赛选手:
  • 1.      CORDIC单元
  • 2.      CMSIS DSP库(CMSISDSP库可能以后会修改到自适应到利用CORDIC,但目前还都是软件实现).
  • 3.      单精度FPU
  • 4.      双精度软件浮点库
友情选手3S. 单精度软件浮点库(要把FPU关闭掉)

部分测试代码, 完整测试工程可以在后文下载连接中找:
  /*## Configure the CORDIC peripheral ####################################*/
  sCordicConfig.Function         = CORDIC_FUNCTION_SINE;     /* sine function */
  sCordicConfig.Precision        = CORDIC_PRECISION_6CYCLES; /* max precision for q1.31 sine */
  sCordicConfig.Scale            = CORDIC_SCALE_0;           /* no scale */
  sCordicConfig.NbWrite          = CORDIC_NBWRITE_1;         /* One input data: angle. Second input data (modulus) is 1 after cordic reset */
  sCordicConfig.NbRead           = CORDIC_NBREAD_2;          /* Two output data: sine then cosine */
  sCordicConfig.InSize           = CORDIC_INSIZE_32BITS;     /* q1.31 format for input data */
  sCordicConfig.OutSize          = CORDIC_OUTSIZE_32BITS;    /* q1.31 format for output data */

  if (HAL_CORDIC_Configure(&hcordic, &sCordicConfig) != HAL_OK) {
    /* Configuration Error */
                assert(0);
    Error_Handler();
  }

        /*################### Calculation using CORDIC #######################*/       
        start_ticks = HAL_GetTick();
        for (uint32_t i = 0; i < LOOP_NB; i++) {       
                if (HAL_CORDIC_Calculate_DMA(&hcordic, aAnglesCordic, aResults,
                                                                                                                                 ARRAY_SIZE, CORDIC_DMA_DIR_IN_OUT) != HAL_OK) {
                        /* Processing Error */
                        assert(0);
                        Error_Handler();
                }

                /*  Before starting a new process, you need to check the current state of the peripheral;
                                if it’s busy you need to wait for the end of current transfer before starting a new one.
                                For simplicity reasons, this example is just waiting till the end of the
                                process, but application may perform other tasks while transfer operation
                                is ongoing. */
                while (HAL_CORDIC_GetState(&hcordic) != HAL_CORDIC_STATE_READY) {
                }
        }
        end_ticks = HAL_GetTick();

   /* Compare calculated results to the reference values */
   for (uint32_t i = 0; i < ARRAY_SIZE; i++) {                 
//                 printf("0x%08X, 0x%08X,\n", aResults[2*i], aResults[(2*i) + 1]);
     if ((Check_Residual_Error(aResults[2*i], aRefSin[i], DELTA) == FAIL) ||
         (Check_Residual_Error(aResults[(2*i) + 1], aRefCos[i], DELTA) == FAIL)) {
       Error_Handler();
     }
   }

  /* Correct CORDIC output values: Turn LED2 on */
  BSP_LED_On(LED2);       
        printf("Correct CORDIC output[%u-%u = %u]\n",
        start_ticks,
        end_ticks,
        end_ticks-start_ticks);
                
        /*################### Calculation using CMSIS DSP library #############*/
        start_ticks = HAL_GetTick();
        for (uint32_t i = 0; i < LOOP_NB; i++) {
                for (uint32_t j = 0; j < ARRAY_SIZE; j++) {
                        /* Calculate sine */
                        aResults[2*j] = arm_sin_q31(aAnglesLib[j]);

                        /* Calculate cosine */
                        aResults[(2*j) + 1] = arm_cos_q31(aAnglesLib[j]);
                }
        }       
        end_ticks = HAL_GetTick();

   /* Compare calculated results to the reference values */
   for (uint32_t i = 0; i < ARRAY_SIZE; i++) {
//                 printf("0x%08X, 0x%08X,\n", aResults[2*i], aResults[(2*i) + 1]);
     if ((Check_Residual_Error(aResults[2*i], aRefSin[i], DELTA) == FAIL) ||
         (Check_Residual_Error(aResults[(2*i) + 1], aRefCos[i], DELTA) == FAIL)) {
       Error_Handler();
     }
   }

  /* Correct output values: Turn LED2 on */
  BSP_LED_On(LED2);       
        printf("Correct CMSIS DSP output[%u-%u = %u]\n",
        start_ticks,
        end_ticks,
        end_ticks-start_ticks);       
        /*################### Calculation using FPU(Single Precision) #############*/
         //Generate the radian angle
        float angle_floats[ARRAY_SIZE];
        for(uint32_t i=0; i<ARRAY_SIZE; ++i) {
                angle_floats[i] = (i * PI * 2)/ARRAY_SIZE;
        }
       
        start_ticks = HAL_GetTick();
        for (uint32_t i = 0; i < LOOP_NB; i++) {
                for (uint32_t j = 0; j < ARRAY_SIZE; j++) {
                        /* Calculate sine */
                        aResults[2*j] = sinf(angle_floats[j]);

                        /* Calculate cosine */
                        aResults[(2*j) + 1] = cosf(angle_floats[j]);
                }
        }       
        end_ticks = HAL_GetTick();

  /* Correct output values: Turn LED2 on */
  BSP_LED_On(LED2);       
        printf("Correct FPU Single precision output[%u-%u = %u]\n",
        start_ticks,
        end_ticks,
        end_ticks-start_ticks);               
       
        /*################### Calculation using soft float library(Double Precision) #############*/
         //Generate the radian angle
        double angle_doubles[ARRAY_SIZE];
        for(uint32_t i=0; i<ARRAY_SIZE; ++i) {
                angle_doubles[i] = (i * PI * 2)/ARRAY_SIZE;
        }
       
        start_ticks = HAL_GetTick();
        for (uint32_t i = 0; i < LOOP_NB; i++) {
                for (uint32_t j = 0; j < ARRAY_SIZE; j++) {
                        /* Calculate sine */
                        aResults[2*j] = sin(angle_doubles[j]);

                        /* Calculate cosine */
                        aResults[(2*j) + 1] = cos(angle_doubles[j]);
                }
        }       
        end_ticks = HAL_GetTick();

  /* Correct output values: Turn LED2 on */
  BSP_LED_On(LED2);       
        printf("Correct Soft float point double precision output[%u-%u = %u]\n",
        start_ticks,
        end_ticks,
        end_ticks-start_ticks);       


使用特权

评论回复
9
zhanzr21|  楼主 | 2019-9-7 16:03 | 只看该作者
本帖最后由 zhanzr21 于 2019-9-7 16:04 编辑

结果(使能FPU)
Correct CORDIC output[0-39 = 39]

Correct CMSIS DSP output[39-159 = 120]

Correct FPU Single precision output[160-412 = 252]

Correct Soft float point double precision output[412-9882 = 9470]
结果(禁止FPU)
Cordic Test 1, 410FC241 170000000

Correct CORDIC output[0-39 = 39]

Correct CMSIS DSP output[39-159 = 120]
Correct FPU Single precision output[160-2451 = 2291]

Correct Soft float point double precision output[2451-10774 = 8323]

禁止FPU后双精度的软件模拟运算性能也有所提升, 这应该有些浮点运算的基本库已经做了初始化导致的.
用图表来直观感受下差距:


结束语,参考与下载连接

STM32G4系列还有一个重要的硬件加速单元FMA, 专业服务于滤波器的,  篇幅与时间原因,本次先不讨论. 留着下次吃.

参考页面:

所有测试工程代码下载: https://github.com/zhanzr/stm32g431rb-demo.git注意一个branch是一个实验.

使用特权

评论回复
10
icecut| | 2019-9-8 14:05 | 只看该作者
仿真器比板子贵了啊

使用特权

评论回复
11
zhanzr21|  楼主 | 2019-9-8 19:30 | 只看该作者
icecut 发表于 2019-9-8 14:05
仿真器比板子贵了啊

应该很快就能看到附带F723仿真器的STM32F0/L0/G0的板子


反正这买卖ST不打算挣钱,不如搞个扩展可能性强的仿真器芯片, 这样固件升级余地很大, 软件开发便利了也是节约成本.

使用特权

评论回复
12
icecut| | 2019-9-8 23:32 | 只看该作者
zhanzr21 发表于 2019-9-8 19:30
应该很快就能看到附带F723仿真器的STM32F0/L0/G0的板子

这个仿真器可是usbhs的, st对hs一直不舍得钱. 要是价格不贵把仿真器二次开发更有意义.

使用特权

评论回复
13
触觉的爱| | 2019-9-14 17:05 | 只看该作者
看起来功能够强大,不知道性价比如何

使用特权

评论回复
14
huangqi412| | 2019-9-14 19:30 | 只看该作者
仿真器一下子大方起来了。

使用特权

评论回复
15
yiyigirl2014| | 2019-10-9 16:51 | 只看该作者
论坛有送板子活动?

使用特权

评论回复
16
香水城| | 2019-10-24 11:43 | 只看该作者
STM32系列之所以在工程师心中有这么高的人气, 很大一个原因就是开发生态非常完善. 从初学者到高手, 都能很快上手, 很快开始自己的学习和工程设计.

使用特权

评论回复
17
whhzlwea| | 2019-10-24 14:40 | 只看该作者
zhanzr21 发表于 2019-9-7 16:03
结果(使能FPU)结果(禁止FPU)
在禁止FPU后双精度的软件模拟运算性能也有所提升, 这应该有些浮点运算的基本库 ...

版主,FMAC什么时候更新啊?

使用特权

评论回复
18
zhanzr21|  楼主 | 2019-10-28 22:21 | 只看该作者
whhzlwea 发表于 2019-10-24 14:40
版主,FMAC什么时候更新啊?

你用到这功能没有
我想写, 怕没有多少人用到这功能, 要是有人用到这功能我就找个周末写了.

Cordic这功能好像用的工程师就不多.

使用特权

评论回复
19
ouxueguo| | 2019-12-2 09:34 | 只看该作者
看了这么多好像也有点模糊;楼主这个做伺服电机驱动控制可靠吗?仿真器怎么搞价格上呢?

使用特权

评论回复
20
whhzlwea| | 2019-12-2 19:41 | 只看该作者
zhanzr21 发表于 2019-10-28 22:21
你用到这功能没有
我想写, 怕没有多少人用到这功能, 要是有人用到这功能我就找个周末写了.

用到了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:每天都進步

91

主题

1011

帖子

34

粉丝