【STM32H7的DSP教程】第6章 ARM DSP源碼和庫移植方法(MDK5的AC5和AC6)


完整版教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547

第6章   ARM DSP源碼和庫移植方法(MDK5的AC5和AC6)

本期教程主要講解ARM官方DSP源碼和庫的移植以及一些相關知識的介紹。

6.1 初學者重要提示

6.2 DSP庫的下載和說明

6.3 DSP庫版本的區別

6.4 DSP庫的幾個重要的預定義宏含義

6.5 使用MDK的AC6編譯器優勢

6.6 DSP庫在MDK上的移植(AC5源碼移植方式)

6.7 DSP庫在MDK上的移植(AC5庫移植方式)

6.8 DSP庫在MDK上的移植(AC6源碼移植方式)

6.9 DSP庫在MDK上的移植(AC6庫移植方式)

6.10 升級到最新版DSP庫的方法

6.11 簡易DSP庫函數驗證

6.12 總結

 

 

6.1   初學者重要提示

  1.   MDK請使用5.26及其以上版本,CMSIS軟件包請使用5.6.0及其以上版本。
  2.   MDK的工程創建,下載和調試方法,在V7用戶手冊有詳細說明:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255
  3.   鑒於MDK的AC6(ARM Compiler 6.X)編譯器在浮點處理上的強勁性能,每個例子將必做一個AC6版,而且ARM編譯好的DSP庫也開始直接采用AC6。
  4.   MDK AC6有兩個地方在使用的時候要注意:
    •   工程目錄切記不要有中文路徑,而且不要太長,否則會導致無法使用go to def以及調試的時候不正常。
    •   MDK AC6工程模板的漢字編碼問題,在本章6.8小節有詳細說明。

6.2   DSP庫的下載和說明

下面詳細的給大家講解一下官方DSP庫的移植。

6.2.1  DSP庫的下載

DSP庫是包含在CMSIS軟件包(Cortex Microcontroller Software Interface Standard)里面,所以下載DSP庫也就是下載CMSIS軟件包。這里提供三個可以下載的地方:

  •   方式一:STM32CubeH7軟件包里面。

每個版本的Cube軟件包都會攜帶CMSIS文件夾,只是版本比較老,不推薦。即使是最新的CubeH7

軟件包,包含的CMSIS軟件包版本也有點低。

  •   方式二:MDK安裝目錄(下面是5.6.0版本的路徑)。

大家安裝了新版MDK后,CMSIS軟件包會存在於路徑:ARM\PACK\ARM\CMSIS\5.6.0\CMSIS。

如果有更新的版本,推薦大家使用最新版本,MDK的軟件包下載地址:http://www.keil.com/dd2/Pack/

 

  •   方式三:GitHub。

通過GitHub獲取也比較方便,地址:https://github.com/ARM-software/CMSIS_5 。點擊右上角就可以下載CMSIS軟件包了。

 

當然,也可以在ARM官網下載,只是這兩年ARM官網升級得非常頻繁,通過檢索功能找資料非常麻煩。所以不推薦大家到ARM官網下載資料了。

6.2.2  DSP庫的說明

這里我們以CMSIS V5.6.0為標准進行移植。打開固件庫里面的CMSIS文件,可以看到如下幾個文件:

 

其中DSP文件夾是我們需要的:

 

Examples文件夾中的文件如下,主要是提供了一些例子:

 

Include文件夾里面是DSP庫的頭文件:

 

Lib文件夾里面是MDK(ARM),IAR和CGG版庫文件:

 

Projects文件夾里面的文件如下,提供了三個版本的工程模板,每個模板里面都是把所有源碼文件添加了進來:

 

Source文件夾中的文件如下,這個是DSP的源碼文件:

 

6.3   DSP庫版本的區別

MDK版本的DSP庫如下:

 

  •   arm_cortexM7lfdp_math.lib

Cortex-M7內核,l表示小端格式,f表示帶FPU單元,dp表示Double Precision雙精度浮點。

  •   arm_cortexM7bfdp_math.lib

Cortex-M7內核,b表示大端格式,f表示帶FPU單元,dp表示Double Precision雙精度浮點。

  •   arm_cortexM7lfsp_math.lib

Cortex-M7內核,l表示小端格式,f表示帶FPU單元,sp表示Single Precision單精度浮點。

  •   arm_cortexM7bfsp_math.lib

Cortex-M7內核,b表示大端格式,f表示帶FPU單元,sp表示Single Precision單精度浮點。

  •   arm_cortexM7l_math.lib

Cortex-M7內核,l表示小端格式。

  •   arm_cortexM7b_math.lib

Cortex-M7內核,b表示大端格式。

 

STM32H7是M7內核,雙精度浮點,一般使用小端格式,所以我們選擇庫arm_cortexM7lfdp_math.lib

6.4   DSP庫的幾個重要的預定義宏含義

根據用戶的使用要求,這幾個預定義宏可以添加到MDK的預定義選項中:

 

這里將這幾個預定義宏做個介紹:

  •   ARM_MATH_BIG_ENDIAN:

大端格式。

  •   ARM_MATH_MATRIX_CHECK:

檢測矩陣的輸入輸出大小

  • ARM_MATH_NEON:
  • ARM_MATH_NEON_EXPERIMENTAL:

這兩個暫時用不到,因為M0,M3,M4和M7內核不支持NEON指令,需要等待升級到ARMv8.1-M架構。

  •   ARM_MATH_ROUNDING:

主要用在浮點數轉Q32,Q15和Q7時,類似四舍五入的處理上,其它函數沒用到。

 

  •   ARM_MATH_LOOPUNROLL:

用於4個為一組的的小批量處理上,加快執行速度。

通過下面的求絕對值函數,可以方便的看出區別:

void arm_abs_f32(
  const float32_t * pSrc,
        float32_t * pDst,
        uint32_t blockSize)
{
        uint32_t blkCnt;                               /* Loop counter */

#if defined(ARM_MATH_NEON)
    float32x4_t vec1;
    float32x4_t res;

    /* Compute 4 outputs at a time */
    blkCnt = blockSize >> 2U;

    while (blkCnt > 0U)
    {
        /* C = |A| */

            /* Calculate absolute values and then store the results in the destination buffer. */
        vec1 = vld1q_f32(pSrc);
        res = vabsq_f32(vec1);
        vst1q_f32(pDst, res);

        /* Increment pointers */
        pSrc += 4;
        pDst += 4;
        
        /* Decrement the loop counter */
        blkCnt--;
    }

    /* Tail */
    blkCnt = blockSize & 0x3;

#else
#if defined (ARM_MATH_LOOPUNROLL)

  /* Loop unrolling: Compute 4 outputs at a time */
  blkCnt = blockSize >> 2U;

  while (blkCnt > 0U)
  {
    /* C = |A| */

    /* Calculate absolute and store result in destination buffer. */
    *pDst++ = fabsf(*pSrc++);

    *pDst++ = fabsf(*pSrc++);

    *pDst++ = fabsf(*pSrc++);

    *pDst++ = fabsf(*pSrc++);

    /* Decrement loop counter */
    blkCnt--;
  }

  /* Loop unrolling: Compute remaining outputs */
  blkCnt = blockSize % 0x4U;

#else

  /* Initialize blkCnt with number of samples */
  blkCnt = blockSize;

#endif /* #if defined (ARM_MATH_LOOPUNROLL) */
#endif /* #if defined(ARM_MATH_NEON) */

  while (blkCnt > 0U)
  {
    /* C = |A| */

    /* Calculate absolute and store result in destination buffer. */
    *pDst++ = fabsf(*pSrc++);

    /* Decrement loop counter */
    blkCnt--;
  }

}

6.5   使用MDK的AC6編譯器優勢

涉及到的知識點比較多,專門將其整理到附件章節E。

6.6   DSP庫在MDK上的移植(AC5源碼移植方式)

下面我們講解下如何在MDK上面移植DSP庫源碼,DSP庫的移植相對比較容易。

6.6.1  第一步:建立MDK工程並添加DSP庫

為了方便起見,我們這里不再專門建立一個MDK工程了,直接以V7開發板中的例子:V7-001_跑馬燈例程為模板進行添加即可。打開這個實例並在左側添加分組CMSIS/DSP:

 

我們這里不需要添加每個C文件源碼,僅需添加包含這些C文件的匯總文件,比如BasicMathFunctions.c文件里面包含的C文件就是:

#include "arm_abs_f32.c"
#include "arm_abs_q15.c"
#include "arm_abs_q31.c"
#include "arm_abs_q7.c"
#include "arm_add_f32.c"
#include "arm_add_q15.c"
#include "arm_add_q31.c"
#include "arm_add_q7.c"
#include "arm_dot_prod_f32.c"
#include "arm_dot_prod_q15.c"
#include "arm_dot_prod_q31.c"
#include "arm_dot_prod_q7.c"
#include "arm_mult_f32.c"
#include "arm_mult_q15.c"
#include "arm_mult_q31.c"
#include "arm_mult_q7.c"
#include "arm_negate_f32.c"
#include "arm_negate_q15.c"
#include "arm_negate_q31.c"
#include "arm_negate_q7.c"
#include "arm_offset_f32.c"
#include "arm_offset_q15.c"
#include "arm_offset_q31.c"
#include "arm_offset_q7.c"
#include "arm_scale_f32.c"
#include "arm_scale_q15.c"
#include "arm_scale_q31.c"
#include "arm_scale_q7.c"
#include "arm_shift_q15.c"
#include "arm_shift_q31.c"
#include "arm_shift_q7.c"
#include "arm_sub_f32.c"
#include "arm_sub_q15.c"
#include "arm_sub_q31.c"
#include "arm_sub_q7.c"

這樣一來,MDK編譯后會自動關聯,查看源碼非常方便:

 

6.6.2  第二步:添加頭文件路徑

添加DSP所需的頭文件路徑,這個頭文件路徑是已經在模板工程中添加好的,這里只是跟大家強調一下:

 

這里要注意一點,為什么直接添加路徑Libraries\CMSIS\Include里面的頭文件即可,而沒有添加Libraries\CMSIS\DSP\Include,這是因為路徑Libraries\CMSIS\Include里面已經包含了DSP庫的頭文件。

6.6.3  第三步:添加宏定義

我們這里僅使能一個宏定義ARM_MATH_LOOPUNROLL:

 

6.6.4  第四步:開啟FPU

需要客戶通過MDK開啟FPU,由於STM32H7支持雙精度浮點,這里要開啟Double Precision。

 

6.6.5  第五步:添加頭文件arm_math.h

用到DSP庫函數的文件得添加#include "arm_math.h"就可以調用DSP庫的API了。至此就完成了DSP庫的移植。

6.7   DSP庫在MDK上的移植(AC5庫移植方式)

移植方法與本章6.5小節的相同,僅第1步不同,將源碼的添加修改為庫添加:

 

6.8   DSP庫在MDK上的移植(AC6源碼移植方式)

AC6的DSP源碼移植與本章6.5小節里面的AC5移植完全相同,沒有區別。但AC5和AC6工程上有三處區別,這里着重指出下:

  •   第1處,采用AC6編譯器:

 

  •   第2處,警告類型選擇AC5-like:

 

  •   第3處,MDK的AC6工程代碼如果有源文件是GBK編碼,而且使用漢字,MDK編譯時會報錯,需要用記事本打開使用漢字的源碼文件,另存為UTF-8。比如main.c文件的串口打印函數printf用到了漢字,那么就需要做如下修改:

 

然后再重新編譯就不會報錯了。同時,串口打印時,使用的串口助手要支持UTF-8,推薦用SecureCRT,下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=91718

設置如下:

 

6.9   DSP庫在MDK上的移植(AC6庫移植方式)

AC6的DSP庫移植與本章6.6小節里面的AC5移植完全相同,沒有區別。不過要注意6.8小節中所講解的問題即可。

6.10 升級到最新版DSP庫的方法

由於CMSIS軟件包是實時更新的,這里提供一種升級的簡單辦法,按照本章6.1小節的說明下載到最新版CMSIS軟件包,然后直接覆蓋DSP工程里面的CMSIS文件夾即可。

6.11 簡易DSP庫函數驗證

這里我們主要運行arm_abs_f32,arm_abs_q31,arm_abs_q15這三個函數,以此來驗證我們移植的DSP庫是否正確。

配套例子:

本章配套了如下兩個例子:

  •   V7-200_DSP程序模板(源碼方式)
  •   V7-201_DSP程序模板(庫方式)

每個例子都配套了MDK的AC5和AC6兩個版本的工程。

實驗目的:

1. 學習官方DSP庫的移植

實驗內容:

1. 按下按鍵K1, 串口打印函數arm_abs_f32的輸出結果

2. 按下按鍵K2, 串口打印函數arm_abs_q31的輸出結果

3. 按下按鍵K3, 串口打印函數arm_abs_q15的輸出結果

實驗現象:

       通過串口上位機軟件SecureCRT看打印信息現象如下(分別按幾次K1,K2,K3)。如果編譯的是MDK的AC6工程,特別要注意本章6.7小節所說的問題。

 

程序設計:

程序的設計也比較簡單,通過按下不同的按鍵從而打印不同的DSP庫函數執行結果,主程序如下:

#include "bsp.h"            /* 底層硬件驅動 */
#include "arm_math.h"



/* 定義例程名和例程發布日期 */
#define EXAMPLE_NAME    "V7-ARM的DSP移植模板(源碼方式)"
#define EXAMPLE_DATE    "2019-07-31"
#define DEMO_VER        "1.0"

static void PrintfLogo(void);
static void PrintfHelp(void);

/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程序入口
*    形    參: 無
*    返 回 值: 錯誤代碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;        /* 按鍵代碼 */
    float32_t pSrc;
    float32_t pDst;
    q31_t pSrc1;
    q31_t pDst1;
    q15_t pSrc2;
    q15_t pDst2;

    
    bsp_Init();        /* 硬件初始化 */
    
    PrintfLogo();    /* 打印例程名稱和版本等信息 */
    PrintfHelp();    /* 打印操作提示 */

    bsp_StartAutoTimer(0, 100);    /* 啟動1個100ms的自動重裝的定時器 */
    
    /* 主程序大循環 */
    while (1)
    {
        /* CPU空閑時執行的函數,在 bsp.c */
        bsp_Idle();        
        
        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */
            /* 翻轉LED2的狀態 */
            bsp_LedToggle(2);    
        }

        /* 處理按鍵事件 */
        ucKeyCode = bsp_GetKey();
        if (ucKeyCode > 0)
        {
            /* 有鍵按下 */
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:               /* K1鍵按下 */
                    pSrc -= 1.23f;
                    arm_abs_f32(&pSrc, &pDst, 1);
                    printf("pDst = %f\r\n", pDst);
                    break;
                    
                case KEY_DOWN_K2:              /* K2鍵按下 */
                    pSrc1 -= 1;
                    arm_abs_q31(&pSrc1, &pDst1, 1);
                    printf("pDst1 = %d\r\n", pDst1);
                    break;

                case KEY_DOWN_K3:              /* K3鍵按下 */
                    pSrc2 -= 1;
                    arm_abs_q15(&pSrc2, &pDst2, 1);
                    printf("pDst2 = %d\r\n", pDst2);
                    break;
                
                default:
                    break;
            }
        }
    }
}

6.12 總結

本期教程主要跟大家介紹了官方DSP庫的移植,相對來說移植也比較簡單,建議初學的同學按照這個步驟移植一遍。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM