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

[复制链接]
7157|23
 楼主| zhanzr21 发表于 2019-9-7 15:40 | 显示全部楼层 |阅读模式
board_by_huawei.jpg
前言
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

评论

带V3啊  发表于 2019-12-2 20:01
 楼主| zhanzr21 发表于 2019-9-7 15:44 | 显示全部楼层
芯片与板子
这个板子也属于Nucleo 64系列, 但是同样叫做Nucleo 64, 硬件上也有很多改进的.
board_by_mi9.jpg
图 板子前视跟之前的Nucleo64板子相比:
  • 1.     板载调试器升级了, 现在的调试器主控是STM32F723, 和服务了很多年的STM32F103相比,强大了不少, 至于具体功能还没有发现很大的升级, 但是起码有很大的升级空间.
  • 2.     增加了MIPI10接口的调试接口, 虽然只有非常专业的工程师才有这种接口的调试器吧.
  • 3.     主控芯片的两个晶体都焊接了. 以前的Nucleo系列板子有一个外接晶体没有焊接的.要么使用STLINK的MCO输出, 要么使用内部时钟,要么自己找个晶体焊接上去. 其实一个晶体能有多少成本, ST公司大钱都出了, 之前真是没有想通, 给工程师带来方便是最重要的.
除此之外就没有很多新意了,最大程度保持跟原有的Nucleo开发生态兼容. board_block.png
图开发板框图 stm32g431_nucleo64_board_detail.png
官方的详解图(G474板子那里抄的,但是G431板子跟这个同一批)
en.bd_stm32g431.jpg
STM32G431芯片框图(感兴趣的可以跟STM32F3系列对比下)
板子就介绍到这里, 老司机们应该对这个板子都很熟, 实在没有很多需要介绍的.


 楼主| zhanzr21 发表于 2019-9-7 15:50 | 显示全部楼层
软件-HelloWorld工程生成
STM32系列之所以在工程师心中有这么高的人气, 很大一个原因就是开发生态非常完善. 从初学者到高手, 都能很快上手, 很快开始自己的学习和工程设计.
早期的SPL(StandardPeripheral Library)开启了快速开发的风气, 现在STM32CubeMX+HAL库更是引领了图形化,模块化设计的趋势. 这里以使用CubeMX创建一个Hello World工程为例, 展示这种设计方式的优越性, 高手们可以跳过这一章节.
要输出Hello World, 需要一个输出口, 这个板子的板载STlink确实有模拟串口, 但是没有直接把模拟串口与芯片的串口连接起来, 芯片的串口连接到板子的Arduino接口上去了. 手边没有烙铁, 所以这个工程使用SWV作为输出.
default_uart_connnection_SB.png
图串口默认连接到Arduino接口上去了
首先打开STM32CubeMX, 没有安装的自行去下载安装, 很简单的, 这里不浪费篇幅.第二步安装STM32CubeMX G4支持包, 如果你之前没有安装的话:
cubemx_install_stm32g4_package.png
图安装CubeMXG4支持包
这个包200多M, 网络安装不顺畅的话, 用户可以去官网找到下载连接使用专门的下载工具(如迅雷)下载来安装. 下一步新建工程, 选板子, 注意这个工具的板子图片是错误的还是用的原来的老图片, 这个小问题了, 虽然还是希望ST公司能够加以改进. select_board.png

图选板子
incorrect_image.png
图板子的图片错了

incorrect_image_large.png
又是一张错误图片

 楼主| zhanzr21 发表于 2019-9-7 15:53 | 显示全部楼层
选好板子后, 就使用默认的配置就可以生成工程了, 本人使用Keil MDK.
generate_mdk_project.png
图生成工程
之后打开工程, 注意又要下载一次Keil MDKSTM32G4的支持包, 如果你之前没有下载的话, 这里还是有下载不顺畅的可能, 解决办法还是到官网找到下载连接使用专业下载工具.
编译之前还要配置一下输出重定向: itm_retarget.png
图配置重定向如果是重定向到串口的话, 后面的下拉框选User, 之后实现输出函数. ITM就不用自己实现输出函数了, ITM是CMSIS标准库的内容.主函数修改一点点:
  1. int main(void) {
  2.   /* USER CODE BEGIN 1 */
  3.   /* USER CODE END 1 */
  4.   /* MCU Configuration--------------------------------------------------------*/

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

  7.   /* USER CODE BEGIN Init */
  8.   /* USER CODE END Init */

  9.   /* Configure the system clock */
  10.   SystemClock_Config();

  11.   /* USER CODE BEGIN SysInit */

  12.   /* USER CODE END SysInit */

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

  19.   /* Infinite loop */
  20.   /* USER CODE BEGIN WHILE */
  21.   while (1) {
  22.     /* USER CODE END WHILE */
  23.     /* USER CODE BEGIN 3 */
  24.   }
  25.   /* USER CODE END 3 */
  26. }



install_keil_rte_stm32g4_pack.png
 楼主| zhanzr21 发表于 2019-9-7 15:54 | 显示全部楼层
编译,下载, 打开任何一个可以看ITM输出的工具, 这里使用STLink Utility,SWV显示:
swo_viewer.png
SWV输出显示至此, HelloWorld工程就创建完毕. 整个工程代码, 包括CubeMX配置文件都在后文有下载连接.

 楼主| 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脚本可以供参考.
  1. import os,sys,math

  2. INT32_MAX = (1<<31)-1
  3. UINT32_MAX = (1<<32)-1

  4. ARRAY_SIZE = 64
  5. TEST_CSV_FILE = "test_q.csv"

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

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

  38. test_q_ref_sin = [  0x00000000, 0x0C8BD35E, 0x18F8B83C, 0x25280C5D,
  39.   0x30FBC54D, 0x3C56BA70, 0x471CECE6, 0x5133CC94,
  40.   0x5A827999, 0x62F201AC, 0x6A6D98A4, 0x70E2CBC6,
  41.   0x7641AF3C, 0x7A7D055B, 0x7D8A5F3F, 0x7F62368F,
  42.   0x80000000, 0x7F62368F, 0x7D8A5F3F, 0x7A7D055B,
  43.   0x7641AF3C, 0x70E2CBC6, 0x6A6D98A4, 0x62F201AC,
  44.   0x5A827999, 0x5133CC94, 0x471CECE6, 0x3C56BA70,
  45.   0x30FBC54D, 0x25280C5D, 0x18F8B83C, 0x0C8BD35E,
  46.   0x00000000, 0xF3742CA2, 0xE70747C4, 0xDAD7F3A3,
  47.   0xCF043AB3, 0xC3A94590, 0xB8E3131A, 0xAECC336C,
  48.   0xA57D8667, 0x9D0DFE54, 0x9592675C, 0x8F1D343A,
  49.   0x89BE50C4, 0x8582FAA5, 0x8275A0C1, 0x809DC971,
  50.   0x80000000, 0x809DC971, 0x8275A0C1, 0x8582FAA5,
  51.   0x89BE50C4, 0x8F1D343A, 0x9592675C, 0x9D0DFE54,
  52.   0xA57D8667, 0xAECC336C, 0xB8E3131A, 0xC3A94590,
  53.   0xCF043AB3, 0xDAD7F3A3, 0xE70747C4, 0xF3742CA2]

  54. test_q_ref_cos = [  0x80000000, 0x7F62368F, 0x7D8A5F3F, 0x7A7D055B,
  55.   0x7641AF3C, 0x70E2CBC6, 0x6A6D98A4, 0x62F201AC,
  56.   0x5A827999, 0x5133CC94, 0x471CECE6, 0x3C56BA70,
  57.   0x30FBC54D, 0x25280C5D, 0x18F8B83C, 0x0C8BD35E,
  58.   0x00000000, 0xF3742CA2, 0xE70747C4, 0xDAD7F3A3,
  59.   0xCF043AB3, 0xC3A94590, 0xB8E3131A, 0xAECC336C,
  60.   0xA57D8667, 0x9D0DFE54, 0x9592675C, 0x8F1D343A,
  61.   0x89BE50C4, 0x8582FAA5, 0x8275A0C1, 0x809DC971,
  62.   0x80000000, 0x809DC971, 0x8275A0C1, 0x8582FAA5,
  63.   0x89BE50C4, 0x8F1D343A, 0x9592675C, 0x9D0DFE54,
  64.   0xA57D8667, 0xAECC336C, 0xB8E3131A, 0xC3A94590,
  65.   0xCF043AB3, 0xDAD7F3A3, 0xE70747C4, 0xF3742CA2,
  66.   0x00000000, 0x0C8BD35E, 0x18F8B83C, 0x25280C5D,
  67.   0x30FBC54D, 0x3C56BA70, 0x471CECE6, 0x5133CC94,
  68.   0x5A827999, 0x62F201AC, 0x6A6D98A4, 0x70E2CBC6,
  69.   0x7641AF3C, 0x7A7D055B, 0x7D8A5F3F, 0x7F62368F]

  70. test_q_cordic_result = [0x00000600, 0x7FFFFE00,
  71. 0x0C8BD600, 0x7F623100,
  72. 0x18F8B700, 0x7D8A5A00,
  73. 0x25280600, 0x7A7D0700,
  74. 0x30FBC900, 0x7641A800,
  75. 0x3C56B400, 0x70E2CA00,
  76. 0x471CEC00, 0x6A6D9900,
  77. 0x5133C500, 0x62F20400,
  78. 0x5A827600, 0x5A827900,
  79. 0x62F20600, 0x5133C300,
  80. 0x6A6D9900, 0x471CEC00,
  81. 0x70E2CA00, 0x3C56B400,
  82. 0x7641AC00, 0x30FBC300,
  83. 0x7A7D0700, 0x25280600,
  84. 0x7D8A5A00, 0x18F8B500,
  85. 0x7F623100, 0x0C8BD400,
  86. 0x7FFFFF00, 0xFFFFFF00,
  87. 0x7F623200, 0xF3742900,
  88. 0x7D8A5B00, 0xE7074A00,
  89. 0x7A7D0800, 0xDAD7F100,
  90. 0x7641A800, 0xCF043C00,
  91. 0x70E2CB00, 0xC3A94B00,
  92. 0x6A6D9900, 0xB8E31100,
  93. 0x62F20400, 0xAECC3200,
  94. 0x5A827A00, 0xA57D8500,
  95. 0x5133C400, 0x9D0E0300,
  96. 0x471CED00, 0x95926A00,
  97. 0x3C56B500, 0x8F1D3700,
  98. 0x30FBC400, 0x89BE5500,
  99. 0x25280500, 0x85830100,
  100. 0x18F8B400, 0x8275A400,
  101. 0x0C8BD300, 0x809DCF00,
  102. 0xFFFFFA00, 0x80000200,
  103. 0xF3742A00, 0x809DCF00,
  104. 0xE7074900, 0x8275A600,
  105. 0xDAD7FA00, 0x8582F900,
  106. 0xCF043700, 0x89BE5800,
  107. 0xC3A94C00, 0x8F1D3600,
  108. 0xB8E31400, 0x95926700,
  109. 0xAECC3B00, 0x9D0DFC00,
  110. 0xA57D8A00, 0xA57D8700,
  111. 0x9D0DFA00, 0xAECC3D00,
  112. 0x95926700, 0xB8E31400,
  113. 0x8F1D3600, 0xC3A94C00,
  114. 0x89BE5400, 0xCF043D00,
  115. 0x8582F900, 0xDAD7FA00,
  116. 0x8275A600, 0xE7074B00,
  117. 0x809DCF00, 0xF3742C00,
  118. 0x80000100, 0x00000100,
  119. 0x809DCE00, 0x0C8BD700,
  120. 0x8275A500, 0x18F8B600,
  121. 0x8582F800, 0x25280F00,
  122. 0x89BE5800, 0x30FBC400,
  123. 0x8F1D3500, 0x3C56B500,
  124. 0x95926700, 0x471CEF00,
  125. 0x9D0DFC00, 0x5133CE00,
  126. 0xA57D8600, 0x5A827B00,
  127. 0xAECC3C00, 0x62F1FD00,
  128. 0xB8E31300, 0x6A6D9600,
  129. 0xC3A94B00, 0x70E2C900,
  130. 0xCF043C00, 0x7641AB00,
  131. 0xDAD7FB00, 0x7A7CFF00,
  132. 0xE7074C00, 0x7D8A5C00,
  133. 0xF3742D00, 0x7F623100,]

  134. test_q_cmsis_result = [0x00000000, 0x7FFFFFFE,
  135. 0x0C8BD35E, 0x7F62368E,
  136. 0x18F8B83C, 0x7D8A5F40,
  137. 0x25280C5E, 0x7A7D055A,
  138. 0x30FBC54C, 0x7641AF3C,
  139. 0x3C56BA70, 0x70E2CBC6,
  140. 0x471CECE6, 0x6A6D98A4,
  141. 0x5133CC94, 0x62F201AC,
  142. 0x5A82799A, 0x5A82799A,
  143. 0x62F201AC, 0x5133CC94,
  144. 0x6A6D98A4, 0x471CECE6,
  145. 0x70E2CBC6, 0x3C56BA70,
  146. 0x7641AF3C, 0x30FBC54C,
  147. 0x7A7D055A, 0x25280C5E,
  148. 0x7D8A5F40, 0x18F8B83C,
  149. 0x7F62368E, 0x0C8BD35E,
  150. 0x7FFFFFFE, 0x00000000,
  151. 0x7F62368E, 0xF3742CA2,
  152. 0x7D8A5F40, 0xE70747C4,
  153. 0x7A7D055A, 0xDAD7F3A2,
  154. 0x7641AF3C, 0xCF043AB2,
  155. 0x70E2CBC6, 0xC3A94590,
  156. 0x6A6D98A4, 0xB8E31318,
  157. 0x62F201AC, 0xAECC336C,
  158. 0x5A82799A, 0xA57D8666,
  159. 0x5133CC94, 0x9D0DFE54,
  160. 0x471CECE6, 0x9592675C,
  161. 0x3C56BA70, 0x8F1D343A,
  162. 0x30FBC54C, 0x89BE50C2,
  163. 0x25280C5E, 0x8582FAA4,
  164. 0x18F8B83C, 0x8275A0C0,
  165. 0x0C8BD35E, 0x809DC970,
  166. 0x00000000, 0x80000000,
  167. 0xF3742CA2, 0x809DC970,
  168. 0xE70747C4, 0x8275A0C0,
  169. 0xDAD7F3A2, 0x8582FAA4,
  170. 0xCF043AB2, 0x89BE50C2,
  171. 0xC3A94590, 0x8F1D343A,
  172. 0xB8E31318, 0x9592675C,
  173. 0xAECC336C, 0x9D0DFE54,
  174. 0xA57D8666, 0xA57D8666,
  175. 0x9D0DFE54, 0xAECC336C,
  176. 0x9592675C, 0xB8E31318,
  177. 0x8F1D343A, 0xC3A94590,
  178. 0x89BE50C2, 0xCF043AB2,
  179. 0x8582FAA4, 0xDAD7F3A2,
  180. 0x8275A0C0, 0xE70747C4,
  181. 0x809DC970, 0xF3742CA2,
  182. 0x80000000, 0x00000000,
  183. 0x809DC970, 0x0C8BD35E,
  184. 0x8275A0C0, 0x18F8B83C,
  185. 0x8582FAA4, 0x25280C5E,
  186. 0x89BE50C2, 0x30FBC54C,
  187. 0x8F1D343A, 0x3C56BA70,
  188. 0x9592675C, 0x471CECE6,
  189. 0x9D0DFE54, 0x5133CC94,
  190. 0xA57D8666, 0x5A82799A,
  191. 0xAECC336C, 0x62F201AC,
  192. 0xB8E31318, 0x6A6D98A4,
  193. 0xC3A94590, 0x70E2CBC6,
  194. 0xCF043AB2, 0x7641AF3C,
  195. 0xDAD7F3A2, 0x7A7D055A,
  196. 0xE70747C4, 0x7D8A5F40,
  197. 0xF3742CA2, 0x7F62368E,]

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

  200.     # The CORDIC Q Format input
  201.     tmp_str = ("The CORDIC Q Format input\n")
  202.     f_output.write(bytes(tmp_str, encoding="ansi"))
  203.    
  204.     for q in test_q_angle_cordic:
  205.         if(q > INT32_MAX):
  206.             q = q - (1<<32)
  207.         f = q/(1<<31)
  208.         tmp_str = ("%08X , %f\n" % (q, f))
  209.         f_output.write(bytes(tmp_str, encoding="ansi"))            
  210.         pass
  211.    
  212.     tmp_str = ("\n")
  213.     f_output.write(bytes(tmp_str, encoding="ansi"))

  214.     # The CMSIS DSP Q Format input
  215.     tmp_str = ("The CMSIS DSP Q Format input\n")
  216.     f_output.write(bytes(tmp_str, encoding="ansi"))   
  217.     for q in test_q_angle_cmsis_dsp:
  218.         if(q > INT32_MAX):
  219.             q = q - (1<<32)        
  220.         f = q/(1<<31)
  221.         tmp_str = ("%08X , %f\n" % (q, f))
  222.         f_output.write(bytes(tmp_str, encoding="ansi"))
  223.         pass   
  224.     pass

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

  227.     # The Ref Sine Q Format input
  228.     tmp_str = ("The Ref Sine Q Format input\n")
  229.     f_output.write(bytes(tmp_str, encoding="ansi"))     
  230.     for q in test_q_ref_sin:
  231.         if(q > INT32_MAX):
  232.             q = q - (1<<32)        
  233.         f = q/(1<<31)
  234.         tmp_str = ("%08X , %f\n" % (q, f))
  235.         f_output.write(bytes(tmp_str, encoding="ansi"))
  236.         pass   
  237.     pass
  238.     tmp_str = ("\n")
  239.     f_output.write(bytes(tmp_str, encoding="ansi"))

  240.     # The Ref Cos Q Format input
  241.     tmp_str = ("The Ref Cos Q Format input\n")
  242.     f_output.write(bytes(tmp_str, encoding="ansi"))   
  243.     for q in test_q_ref_cos:
  244.         if(q > INT32_MAX):
  245.             q = q - (1<<32)        
  246.         f = q/(1<<31)
  247.         tmp_str = ("%08X , %f\n" % (q, f))
  248.         f_output.write(bytes(tmp_str, encoding="ansi"))
  249.         pass   
  250.     pass
  251.     tmp_str = ("\n")
  252.     f_output.write(bytes(tmp_str, encoding="ansi"))

  253.     # The Result of Cordic Calculation   
  254.     tmp_str = ("The Result of Cordic Calculation\n")
  255.     f_output.write(bytes(tmp_str, encoding="ansi"))
  256.     res_len = len(test_q_cordic_result)
  257.     for i in range(res_len//2):
  258.         q = test_q_cordic_result[2*i]
  259.         r = test_q_cordic_result[1 + (2*i)]
  260.         
  261.         if(q > INT32_MAX):
  262.             q = q - (1<<32)        
  263.         f = q/(1<<31)
  264.         if(r > INT32_MAX):
  265.             r = r - (1<<32)        
  266.         g = r/(1<<31)
  267.         
  268.         tmp_str = ("%08X , %f, %08X , %f\n" % (q, f, r, g))
  269.         f_output.write(bytes(tmp_str, encoding="ansi"))
  270.         pass   
  271.     pass
  272.     tmp_str = ("\n")
  273.     f_output.write(bytes(tmp_str, encoding="ansi"))

  274.     # The Result of CMSIS DSP Calculation   
  275.     tmp_str = ("The Result of CMSIS DSP Calculation\n")
  276.     f_output.write(bytes(tmp_str, encoding="ansi"))
  277.     res_len = len(test_q_cmsis_result)
  278.     for i in range(res_len//2):
  279.         q = test_q_cordic_result[2*i]
  280.         r = test_q_cordic_result[1 + (2*i)]
  281.         
  282.         if(q > INT32_MAX):
  283.             q = q - (1<<32)        
  284.         f = q/(1<<31)
  285.         if(r > INT32_MAX):
  286.             r = r - (1<<32)        
  287.         g = r/(1<<31)
  288.         
  289.         tmp_str = ("%08X , %f, %08X , %f\n" % (q, f, r, g))
  290.         f_output.write(bytes(tmp_str, encoding="ansi"))
  291.         pass   
  292.     pass
  293.     tmp_str = ("\n")
  294.     f_output.write(bytes(tmp_str, encoding="ansi"))

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

  299.         d_sin_res = math.sin(ang)
  300.         q_1_31_t = int(d_sin_res * (UINT32_MAX) / 2)
  301.         if(q_1_31_t < 0):
  302.             q_1_31_t = q_1_31_t + (1<<32)
  303.         #print("0x%08X, %f" % (q_1_31_t, d_sin_res))
  304.         print("0x%08X, " % q_1_31_t, end='')
  305.         if(0 == (i+1)%4):
  306.             print()
  307.         pass
  308.     print()
  309.    
  310.     print("The generated cos result")
  311.     for i in range(ARRAY_SIZE):
  312.         ang = (i * math.pi * 2)/ARRAY_SIZE
  313.         
  314.         d_cos_res = math.cos(ang)
  315.         q_1_31_t = int(d_cos_res * (UINT32_MAX) / 2)
  316.         if(q_1_31_t < 0):
  317.             q_1_31_t = q_1_31_t + (1<<32)
  318.         #print("0x%08X, %f" % (q_1_31_t, d_cos_res))
  319.         print("0x%08X, " % q_1_31_t, end='')
  320.         if(0 == (i+1)%4):
  321.             print()
  322.         pass
  323.     print()
  324.      
  325. if __name__ == '__main__':
  326.     main()


这些都有点枯燥, 但是用起来的话, 理解其实很容易.下面来做个简单的CORDIC运算的例子:
[0,2PI]也就是一个圆的弧度空间分为64个点, 求每个点的正弦值.
首先把CORDIC外设打开:
cordic_config_1.png
CORDIC配置配置就这么简单, 测试代码摘要如下, 整个工程代码在后文有下载连接.

 楼主| zhanzr21 发表于 2019-9-7 15:59 | 显示全部楼层
  1. /* Array of angles in Q1.31 format, regularly incremented from 0 to 2*pi */
  2. static int32_t aAngles[ARRAY_SIZE] =
  3. {
  4.   0x00000000, 0x04000000, 0x08000000, 0x0C000000,
  5.   0x10000000, 0x14000000, 0x18000000, 0x1C000000,
  6.   0x20000000, 0x24000000, 0x28000000, 0x2C000000,
  7.   0x30000000, 0x34000000, 0x38000000, 0x3C000000,
  8.   0x40000000, 0x44000000, 0x48000000, 0x4C000000,
  9.   0x50000000, 0x54000000, 0x58000000, 0x5C000000,
  10.   0x60000000, 0x64000000, 0x68000000, 0x6C000000,
  11.   0x70000000, 0x74000000, 0x78000000, 0x7C000000,
  12.   0x80000000, 0x84000000, 0x88000000, 0x8C000000,
  13.   0x90000000, 0x94000000, 0x98000000, 0x9C000000,
  14.   0xA0000000, 0xA4000000, 0xA8000000, 0xAC000000,
  15.   0xB0000000, 0xB4000000, 0xB8000000, 0xBC000000,
  16.   0xC0000000, 0xC4000000, 0xC8000000, 0xCC000000,
  17.   0xD0000000, 0xD4000000, 0xD8000000, 0xDC000000,
  18.   0xE0000000, 0xE4000000, 0xE8000000, 0xEC000000,
  19.   0xF0000000, 0xF4000000, 0xF8000000, 0xFC000000
  20. };

  21. /* Array of reference sines in Q1.31 format */
  22. static int32_t aRefSin[ARRAY_SIZE] =
  23. {
  24.   0x00000000, 0x0C8BD35E, 0x18F8B83C, 0x25280C5D,
  25.   0x30FBC54D, 0x3C56BA70, 0x471CECE6, 0x5133CC94,
  26.   0x5A827999, 0x62F201AC, 0x6A6D98A4, 0x70E2CBC6,
  27.   0x7641AF3C, 0x7A7D055B, 0x7D8A5F3F, 0x7F62368F,
  28.   0x80000000, 0x7F62368F, 0x7D8A5F3F, 0x7A7D055B,
  29.   0x7641AF3C, 0x70E2CBC6, 0x6A6D98A4, 0x62F201AC,
  30.   0x5A827999, 0x5133CC94, 0x471CECE6, 0x3C56BA70,
  31.   0x30FBC54D, 0x25280C5D, 0x18F8B83C, 0x0C8BD35E,
  32.   0x00000000, 0xF3742CA2, 0xE70747C4, 0xDAD7F3A3,
  33.   0xCF043AB3, 0xC3A94590, 0xB8E3131A, 0xAECC336C,
  34.   0xA57D8667, 0x9D0DFE54, 0x9592675C, 0x8F1D343A,
  35.   0x89BE50C4, 0x8582FAA5, 0x8275A0C1, 0x809DC971,
  36.   0x80000000, 0x809DC971, 0x8275A0C1, 0x8582FAA5,
  37.   0x89BE50C4, 0x8F1D343A, 0x9592675C, 0x9D0DFE54,
  38.   0xA57D8667, 0xAECC336C, 0xB8E3131A, 0xC3A94590,
  39.   0xCF043AB3, 0xDAD7F3A3, 0xE70747C4, 0xF3742CA2
  40. };
  1.   /*## Configure the CORDIC peripheral ####################################*/
  2.   sCordicConfig.Function         = CORDIC_FUNCTION_SINE;     /* sine function */
  3.   sCordicConfig.Precision        = CORDIC_PRECISION_6CYCLES; /* max precision for q1.31 sine */
  4.   sCordicConfig.Scale            = CORDIC_SCALE_0;           /* no scale */
  5.   sCordicConfig.NbWrite          = CORDIC_NBWRITE_1;         /* One input data: angle. Second input data (modulus) is 1 after cordic reset */
  6.   sCordicConfig.NbRead           = CORDIC_NBREAD_1;          /* One output data: sine */
  7.   sCordicConfig.InSize           = CORDIC_INSIZE_32BITS;     /* q1.31 format for input data */
  8.   sCordicConfig.OutSize          = CORDIC_OUTSIZE_32BITS;    /* q1.31 format for output data */

  9.   if (HAL_CORDIC_Configure(&hcordic, &sCordicConfig) != HAL_OK)
  10.   {
  11.     /* Configuration Error */
  12.                 assert(0);
  13.     Error_Handler();
  14.   }

  15.   /*## Start calculation of sines in DMA mode #############################*/
  16.   if (HAL_CORDIC_Calculate_DMA(&hcordic, aAngles, aCalculatedSin,
  17.                                ARRAY_SIZE, CORDIC_DMA_DIR_IN_OUT) != HAL_OK)
  18.   {
  19.     /* Processing Error */
  20.                 assert(0);
  21.     Error_Handler();
  22.   }

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


 楼主| 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关闭掉)

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

  9.   if (HAL_CORDIC_Configure(&hcordic, &sCordicConfig) != HAL_OK) {
  10.     /* Configuration Error */
  11.                 assert(0);
  12.     Error_Handler();
  13.   }

  14.         /*################### Calculation using CORDIC #######################*/       
  15.         start_ticks = HAL_GetTick();
  16.         for (uint32_t i = 0; i < LOOP_NB; i++) {       
  17.                 if (HAL_CORDIC_Calculate_DMA(&hcordic, aAnglesCordic, aResults,
  18.                                                                                                                                  ARRAY_SIZE, CORDIC_DMA_DIR_IN_OUT) != HAL_OK) {
  19.                         /* Processing Error */
  20.                         assert(0);
  21.                         Error_Handler();
  22.                 }

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

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

  40.   /* Correct CORDIC output values: Turn LED2 on */
  41.   BSP_LED_On(LED2);       
  42.         printf("Correct CORDIC output[%u-%u = %u]\n",
  43.         start_ticks,
  44.         end_ticks,
  45.         end_ticks-start_ticks);
  46.                 
  47.         /*################### Calculation using CMSIS DSP library #############*/
  48.         start_ticks = HAL_GetTick();
  49.         for (uint32_t i = 0; i < LOOP_NB; i++) {
  50.                 for (uint32_t j = 0; j < ARRAY_SIZE; j++) {
  51.                         /* Calculate sine */
  52.                         aResults[2*j] = arm_sin_q31(aAnglesLib[j]);

  53.                         /* Calculate cosine */
  54.                         aResults[(2*j) + 1] = arm_cos_q31(aAnglesLib[j]);
  55.                 }
  56.         }       
  57.         end_ticks = HAL_GetTick();

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

  66.   /* Correct output values: Turn LED2 on */
  67.   BSP_LED_On(LED2);       
  68.         printf("Correct CMSIS DSP output[%u-%u = %u]\n",
  69.         start_ticks,
  70.         end_ticks,
  71.         end_ticks-start_ticks);       
  72.         /*################### Calculation using FPU(Single Precision) #############*/
  73.          //Generate the radian angle
  74.         float angle_floats[ARRAY_SIZE];
  75.         for(uint32_t i=0; i<ARRAY_SIZE; ++i) {
  76.                 angle_floats[i] = (i * PI * 2)/ARRAY_SIZE;
  77.         }
  78.        
  79.         start_ticks = HAL_GetTick();
  80.         for (uint32_t i = 0; i < LOOP_NB; i++) {
  81.                 for (uint32_t j = 0; j < ARRAY_SIZE; j++) {
  82.                         /* Calculate sine */
  83.                         aResults[2*j] = sinf(angle_floats[j]);

  84.                         /* Calculate cosine */
  85.                         aResults[(2*j) + 1] = cosf(angle_floats[j]);
  86.                 }
  87.         }       
  88.         end_ticks = HAL_GetTick();

  89.   /* Correct output values: Turn LED2 on */
  90.   BSP_LED_On(LED2);       
  91.         printf("Correct FPU Single precision output[%u-%u = %u]\n",
  92.         start_ticks,
  93.         end_ticks,
  94.         end_ticks-start_ticks);               
  95.        
  96.         /*################### Calculation using soft float library(Double Precision) #############*/
  97.          //Generate the radian angle
  98.         double angle_doubles[ARRAY_SIZE];
  99.         for(uint32_t i=0; i<ARRAY_SIZE; ++i) {
  100.                 angle_doubles[i] = (i * PI * 2)/ARRAY_SIZE;
  101.         }
  102.        
  103.         start_ticks = HAL_GetTick();
  104.         for (uint32_t i = 0; i < LOOP_NB; i++) {
  105.                 for (uint32_t j = 0; j < ARRAY_SIZE; j++) {
  106.                         /* Calculate sine */
  107.                         aResults[2*j] = sin(angle_doubles[j]);

  108.                         /* Calculate cosine */
  109.                         aResults[(2*j) + 1] = cos(angle_doubles[j]);
  110.                 }
  111.         }       
  112.         end_ticks = HAL_GetTick();

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


 楼主| zhanzr21 发表于 2019-9-7 16:03 | 显示全部楼层
本帖最后由 zhanzr21 于 2019-9-7 16:04 编辑

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

  2. Correct CMSIS DSP output[39-159 = 120]

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

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

  2. Correct CORDIC output[0-39 = 39]

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

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

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

结束语,参考与下载连接

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

参考页面:

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

icecut 发表于 2019-9-8 14:05 | 显示全部楼层
仿真器比板子贵了啊
 楼主| zhanzr21 发表于 2019-9-8 19:30 | 显示全部楼层
icecut 发表于 2019-9-8 14:05
仿真器比板子贵了啊

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


反正这买卖ST不打算挣钱,不如搞个扩展可能性强的仿真器芯片, 这样固件升级余地很大, 软件开发便利了也是节约成本.
icecut 发表于 2019-9-8 23:32 | 显示全部楼层
zhanzr21 发表于 2019-9-8 19:30
应该很快就能看到附带F723仿真器的STM32F0/L0/G0的板子

这个仿真器可是usbhs的, st对hs一直不舍得钱. 要是价格不贵把仿真器二次开发更有意义.
触觉的爱 发表于 2019-9-14 17:05 | 显示全部楼层
看起来功能够强大,不知道性价比如何
huangqi412 发表于 2019-9-14 19:30 | 显示全部楼层
仿真器一下子大方起来了。
yiyigirl2014 发表于 2019-10-9 16:51 | 显示全部楼层
论坛有送板子活动?
香水城 发表于 2019-10-24 11:43 | 显示全部楼层
STM32系列之所以在工程师心中有这么高的人气, 很大一个原因就是开发生态非常完善. 从初学者到高手, 都能很快上手, 很快开始自己的学习和工程设计.
whhzlwea 发表于 2019-10-24 14:40 | 显示全部楼层
zhanzr21 发表于 2019-9-7 16:03
结果(使能FPU)结果(禁止FPU)
在禁止FPU后双精度的软件模拟运算性能也有所提升, 这应该有些浮点运算的基本库 ...

版主,FMAC什么时候更新啊?
 楼主| zhanzr21 发表于 2019-10-28 22:21 | 显示全部楼层
whhzlwea 发表于 2019-10-24 14:40
版主,FMAC什么时候更新啊?

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

Cordic这功能好像用的工程师就不多.
ouxueguo 发表于 2019-12-2 09:34 | 显示全部楼层
看了这么多好像也有点模糊;楼主这个做伺服电机驱动控制可靠吗?仿真器怎么搞价格上呢?
whhzlwea 发表于 2019-12-2 19:41 | 显示全部楼层
zhanzr21 发表于 2019-10-28 22:21
你用到这功能没有
我想写, 怕没有多少人用到这功能, 要是有人用到这功能我就找个周末写了.

用到了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:每天都進步

91

主题

1017

帖子

34

粉丝
快速回复 在线客服 返回列表 返回顶部