[开发工具]

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

[复制链接]
5260|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

使用特权

评论回复
评论
lihui567 2019-12-2 20:01 回复TA
带V3啊 
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标准库的内容.主函数修改一点点:
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

使用特权

评论回复
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脚本可以供参考.
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_config_1.png
CORDIC配置配置就这么简单, 测试代码摘要如下, 整个工程代码在后文有下载连接.

使用特权

评论回复
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格式的结果转化为其他格式,如浮点数或者直接使用.


使用特权

评论回复
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);       


使用特权

评论回复
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后双精度的软件模拟运算性能也有所提升, 这应该有些浮点运算的基本库已经做了初始化导致的.
用图表来直观感受下差距:
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

主题

1005

帖子

34

粉丝