请选择 进入手机版 | 继续访问电脑版
搜索
房产
装修
汽车
婚嫁
健康
理财
旅游
美食
跳蚤
二手房
租房
招聘
二手车
教育
茶座
我要买房
买东西
装修家居
交友
职场
生活
网购
亲子
情感
龙城车友
找美食
谈婚论嫁
美女
兴趣
八卦
宠物
手机

【STM32H7的DSP教程】第12章 DSP基础函数-相反数,偏移,移位,减法和比

[复制链接]
查看: 5|回复: 0

2万

主题

2万

帖子

7万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
76562
发表于 2020-9-16 12:40 | 显示全部楼层 |阅读模式
完整版教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547
第12章       DSP基础函数-相反数,偏移,移位,减法和比例因子

本期教程主要讲基本函数中的相反数,偏移,移位,减法和比例因子。
12.1 初学者重要提示
12.2 DSP基础运算指令
12.3 相反数(Vector Negate)
12.4 偏移(Vector Offset)
12.5 移位(Vector Shift)
12.6 减法(Vector Sub)
12.7 比例因子(Vector Scale)
12.8 实验例程说明(MDK)
12.9 实验例程说明(IAR)
12.10 总结


12.1 初学者重要提示

在这里简单的跟大家介绍一下DSP库中函数的通用格式,后面就不再赘述了。

  •   这些函数基本都是支持重入的。
  •   基本每个函数都有四种数据类型,F32,Q31,Q15,Q7。
  •   函数中数值的处理基本都是4个为一组,这么做的原因是F32,Q31,Q15,Q7就可以统一采用一个程序设计架构,便于管理。更重要的是可以在Q15和Q7数据处理中很好的发挥SIMD指令的作用(因为4个为一组的话,可以用SIMD指令正好处理2个Q15数据或者4个Q7数据)。
  •   部分函数是支持目标指针和源指针指向相同的缓冲区。
  •   为什么定点DSP运算输出的时候容易出现结果为0的情况:http://www.armbbs.cn/forum.php?mod=viewthread&tid=95194
12.2 DSP基础运算指令

本章用到基础运算指令:

  •   相反数函数用到QSUB,QSUB16和QSUB8。
  •   偏移函数用到QADD,QADD16和QADD8。
  •   移位函数用到PKHBT和SSAT。
  •   减法函数用到QSUB,QSUB16和QSUB8。
  •   比例因子函数用到PKHBT和SSAT。

这里特别注意饱和运算问题,在第11章的第2小节有详细说明。
12.3 相反数(Vector Negate)

这部分函数主要用于求相反数,公式描述如下:

pDst[n] = -pSrc[n],   0 > 2U;14.    15.        while (blkCnt > 0U)16.        {17.            /* C = -A */18.    19.            /* Negate and then store the results in the destination buffer. */20.            vec1 = vld1q_f32(pSrc);21.            res = vnegq_f32(vec1);22.            vst1q_f32(pDst, res);23.    24.            /* Increment pointers */25.            pSrc += 4;26.            pDst += 4;27.            28.            /* Decrement the loop counter */29.            blkCnt--;30.        }31.    32.        /* Tail */33.        blkCnt = blockSize & 0x3;34.    35.    #else36.    #if defined (ARM_MATH_LOOPUNROLL)37.    38.      /* Loop unrolling: Compute 4 outputs at a time */39.      blkCnt = blockSize >> 2U;40.    41.      while (blkCnt > 0U)42.      {43.        /* C = -A */44.    45.        /* Negate and store result in destination buffer. */46.        *pDst++ = -*pSrc++;47.    48.        *pDst++ = -*pSrc++;49.    50.        *pDst++ = -*pSrc++;51.    52.        *pDst++ = -*pSrc++;53.    54.        /* Decrement loop counter */55.        blkCnt--;56.      }57.    58.      /* Loop unrolling: Compute remaining outputs */59.      blkCnt = blockSize % 0x4U;60.    61.    #else62.    63.      /* Initialize blkCnt with number of samples */64.      blkCnt = blockSize;65.    66.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */67.    #endif /* #if defined(ARM_MATH_NEON_EXPERIMENTAL) */68.    69.      while (blkCnt > 0U)70.      {71.        /* C = -A */72.    73.        /* Negate and store result in destination buffer. */74.        *pDst++ = -*pSrc++;75.    76.        /* Decrement loop counter */77.        blkCnt--;78.      }79.    80.    }[/code]
函数描述:
这个函数用于求32位浮点数的相反数。
函数解析:

  •   第8到35行,用于NEON指令集,当前的CM内核不支持。
  •   第36到61行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   浮点数的相反数求解比较简单,直接在相应的变量前加上负号即可。
  •   第69到78行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:

  •   第1个参数是原数据地址。
  •   第2个参数是求相反数后目的数据地址。
  •   第3个参数转换的数据个数,这里是指的浮点数个数。
12.3.2        函数arm_negate _q31

函数原型:
  1. 1.    void arm_negate_q31(2.      const q31_t * pSrc,3.            q31_t * pDst,4.            uint32_t blockSize)5.    {6.            uint32_t blkCnt;                               /* Loop counter */7.            q31_t in;                                      /* Temporary input variable */8.    9.    #if defined (ARM_MATH_LOOPUNROLL)10.    11.      /* Loop unrolling: Compute 4 outputs at a time */12.      blkCnt = blockSize >> 2U;13.    14.      while (blkCnt > 0U)15.      {16.        /* C = -A */17.    18.        /* Negate and store result in destination buffer. */19.        in = *pSrc++;20.    #if defined (ARM_MATH_DSP)21.        *pDst++ = __QSUB(0, in);22.    #else23.        *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;24.    #endif25.    26.        in = *pSrc++;27.    #if defined (ARM_MATH_DSP)28.        *pDst++ = __QSUB(0, in);29.    #else30.        *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;31.    #endif32.    33.        in = *pSrc++;34.    #if defined (ARM_MATH_DSP)35.        *pDst++ = __QSUB(0, in);36.    #else37.        *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;38.    #endif39.    40.        in = *pSrc++;41.    #if defined (ARM_MATH_DSP)42.        *pDst++ = __QSUB(0, in);43.    #else44.        *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;45.    #endif46.    47.        /* Decrement loop counter */48.        blkCnt--;49.      }50.    51.      /* Loop unrolling: Compute remaining outputs */52.      blkCnt = blockSize % 0x4U;53.    54.    #else55.    56.      /* Initialize blkCnt with number of samples */57.      blkCnt = blockSize;58.    59.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */60.    61.      while (blkCnt > 0U)62.      {63.        /* C = -A */64.    65.        /* Negate and store result in destination buffer. */66.        in = *pSrc++;67.    #if defined (ARM_MATH_DSP)68.        *pDst++ = __QSUB(0, in);69.    #else70.        *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;71.    #endif72.    73.        /* Decrement loop counter */74.        blkCnt--;75.      }76.    77.    }
复制代码

函数描述:
用于求32位定点数的相反数。
函数解析:

  •   第9到54行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第61到75行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   对于Q31格式的数据,饱和运算会使得数据0x80000000变成0x7fffffff,因为最小负数0x80000000(对应浮点数-1),求相反数后,是个正的0x80000000(对应浮点数正1),已经超过Q31所能表示的最大值0x7fffffff,因此会被饱和处理为正数最大值0x7fffffff。
  •   这里重点说一下函数__QSUB,其实这个函数算是Cortex-M7,M4/M3的一个指令,用于实现饱和减法。比如函数:__QSUB(0, in1) 的作用就是实现0 – in1并返回结果。这里__QSUB实现的是32位数的饱和减法。还有__QSUB16和__QSUB8实现的是16位和8位数的减法。
函数参数:

  •   第1个参数是原数据地址。
  •   第2个参数是求相反数后目的数据地址。
  •   第3个参数转换的数据个数,这里是指的定点数个数。
12.3.3        函数arm_negate_q15

函数原型:
  1. 1.    void arm_negate_q15(2.      const q15_t * pSrc,3.            q15_t * pDst,4.            uint32_t blockSize)5.    {6.            uint32_t blkCnt;                               /* Loop counter */7.            q15_t in;                                      /* Temporary input variable */8.    9.    #if defined (ARM_MATH_LOOPUNROLL)10.    11.    #if defined (ARM_MATH_DSP)12.      q31_t in1;                                    /* Temporary input variables */13.    #endif14.    15.      /* Loop unrolling: Compute 4 outputs at a time */16.      blkCnt = blockSize >> 2U;17.    18.      while (blkCnt > 0U)19.      {20.        /* C = -A */21.    22.    #if defined (ARM_MATH_DSP)23.        /* Negate and store result in destination buffer (2 samples at a time). */24.        in1 = read_q15x2_ia ((q15_t **) &pSrc);25.        write_q15x2_ia (&pDst, __QSUB16(0, in1));26.    27.        in1 = read_q15x2_ia ((q15_t **) &pSrc);28.        write_q15x2_ia (&pDst, __QSUB16(0, in1));29.    #else30.        in = *pSrc++;31.        *pDst++ = (in == (q15_t) 0x8000) ? (q15_t) 0x7fff : -in;32.    33.        in = *pSrc++;34.        *pDst++ = (in == (q15_t) 0x8000) ? (q15_t) 0x7fff : -in;35.    36.        in = *pSrc++;37.        *pDst++ = (in == (q15_t) 0x8000) ? (q15_t) 0x7fff : -in;38.    39.        in = *pSrc++;40.        *pDst++ = (in == (q15_t) 0x8000) ? (q15_t) 0x7fff : -in;41.    #endif42.    43.        /* Decrement loop counter */44.        blkCnt--;45.      }46.    47.      /* Loop unrolling: Compute remaining outputs */48.      blkCnt = blockSize % 0x4U;49.    50.    #else51.    52.      /* Initialize blkCnt with number of samples */53.      blkCnt = blockSize;54.    55.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */56.    57.      while (blkCnt > 0U)58.      {59.        /* C = -A */60.    61.        /* Negate and store result in destination buffer. */62.        in = *pSrc++;63.        *pDst++ = (in == (q15_t) 0x8000) ? (q15_t) 0x7fff : -in;64.    65.        /* Decrement loop counter */66.        blkCnt--;67.      }68.    69.    }
复制代码

函数描述:
用于求16位定点数的绝对值。
函数解析:

  •   第9到50行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第57到67行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   对于Q15格式的数据,饱和运算会使得数据0x8000求相反数后饱和为0x7fff。因为最小负数0x8000(对应浮点数-1),求相反数后,是个正的0x8000(对应浮点数正1),已经超过Q15所能表示的最大值0x7fff,因此会被饱和处理为正数最大值0x7fff。
  •   __QSUB16用于实现16位数据的饱和减法。
函数参数:

  •   第1个参数是原数据地址。
  •   第2个参数是求相反数后目的数据地址。
  •   第3个参数转换的数据个数,这里是指的定点数个数。
12.3.4        函数arm_negate_q7

函数原型:
  1. 1.    void arm_negate_q7(2.      const q7_t * pSrc,3.            q7_t * pDst,4.            uint32_t blockSize)5.    {6.            uint32_t blkCnt;                               /* Loop counter */7.            q7_t in;                                       /* Temporary input variable */8.    9.    #if defined (ARM_MATH_LOOPUNROLL)10.    11.    #if defined (ARM_MATH_DSP)12.      q31_t in1;                                    /* Temporary input variable */13.    #endif14.    15.      /* Loop unrolling: Compute 4 outputs at a time */16.      blkCnt = blockSize >> 2U;17.    18.      while (blkCnt > 0U)19.      {20.        /* C = -A */21.    22.    #if defined (ARM_MATH_DSP)23.        /* Negate and store result in destination buffer (4 samples at a time). */24.        in1 = read_q7x4_ia ((q7_t **) &pSrc);25.        write_q7x4_ia (&pDst, __QSUB8(0, in1));26.    #else27.        in = *pSrc++;28.        *pDst++ = (in == (q7_t) 0x80) ? (q7_t) 0x7f : -in;29.    30.        in = *pSrc++;31.        *pDst++ = (in == (q7_t) 0x80) ? (q7_t) 0x7f : -in;32.    33.        in = *pSrc++;34.        *pDst++ = (in == (q7_t) 0x80) ? (q7_t) 0x7f : -in;35.    36.        in = *pSrc++;37.        *pDst++ = (in == (q7_t) 0x80) ? (q7_t) 0x7f : -in;38.    #endif39.    40.        /* Decrement loop counter */41.        blkCnt--;42.      }43.    44.      /* Loop unrolling: Compute remaining outputs */45.      blkCnt = blockSize % 0x4U;46.    47.    #else48.    49.      /* Initialize blkCnt with number of samples */50.      blkCnt = blockSize;51.    52.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */53.    54.      while (blkCnt > 0U)55.      {56.        /* C = -A */57.    58.        /* Negate and store result in destination buffer. */59.        in = *pSrc++;60.    61.    #if defined (ARM_MATH_DSP)62.        *pDst++ = (q7_t) __QSUB(0, in);63.    #else64.        *pDst++ = (in == (q7_t) 0x80) ? (q7_t) 0x7f : -in;65.    #endif66.    67.        /* Decrement loop counter */68.        blkCnt--;69.      }70.    71.    }
复制代码

函数描述:
用于求8位定点数的相反数。
函数解析:

  •   第9到47行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  • 第54到69行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   对于Q7格式的数据,饱和运算会使得数据0x80变成0x7f。因为最小负数0x80(对应浮点数-1),求相反数后,是个正的0x80(对应浮点数正1),已经超过Q7所能表示的最大值0x7f,因此会被饱和处理为正数最大值0x7f。
  • __QSUB8用于实现8位数据的饱和减法。
函数参数:

  •   第1个参数是原数据地址。
  •   第2个参数是求相反数后目的数据地址。
  •   第3个参数转换的数据个数,这里是指的定点数个数。
12.3.5        使用举例

程序设计:
  1. /***********************************************************************************************************    函 数 名: DSP_Negate*    功能说明: 求相反数*    形    参: 无*    返 回 值: 无**********************************************************************************************************/static void DSP_Negate(void){     float32_t pSrc = 0.0f;     float32_t pDst;        q31_t pSrc1 = 0;    q31_t pDst1;        q15_t pSrc2 = 0;    q15_t pDst2;        q7_t pSrc3 = 0;     q7_t pDst3;        /*求相反数*********************************/        pSrc -= 1.23f;    arm_negate_f32(&pSrc, &pDst, 1);    printf("arm_negate_f32 = %f\r\n", pDst);    pSrc1 -= 1;    arm_negate_q31(&pSrc1, &pDst1, 1);    printf("arm_negate_q31 = %d\r\n", pDst1);    pSrc2 -= 1;    arm_negate_q15(&pSrc2, &pDst2, 1);    printf("arm_negate_q15 = %d\r\n", pDst2);    pSrc3 += 1;     arm_negate_q7(&pSrc3, &pDst3, 1);    printf("arm_negate_q7 = %d\r\n", pDst3);    printf("***********************************\r\n");}
复制代码

实验现象:
我的关键词 【STM32H7的DSP教程】第12章    DSP基础函数-相反数,偏移,移位,减法和比  新闻咨询 1379107-20200420153205986-49991102

12.4 偏移(Vector Offset)

这部分函数主要用于求偏移,公式描述如下:

pDst[n] = pSrc[n] + offset,   0 > 2U;15.    16.        while (blkCnt > 0U)17.        {18.            /* C = A + offset */19.     20.            /* Add offset and then store the results in the destination buffer. */21.            vec1 = vld1q_f32(pSrc);22.            res = vaddq_f32(vec1,vdupq_n_f32(offset));23.            vst1q_f32(pDst, res);24.    25.            /* Increment pointers */26.            pSrc += 4;27.            pDst += 4;28.            29.            /* Decrement the loop counter */30.            blkCnt--;31.        }32.    33.        /* Tail */34.        blkCnt = blockSize & 0x3;35.    36.    #else37.    #if defined (ARM_MATH_LOOPUNROLL)38.    39.      /* Loop unrolling: Compute 4 outputs at a time */40.      blkCnt = blockSize >> 2U;41.    42.      while (blkCnt > 0U)43.      {44.        /* C = A + offset */45.    46.        /* Add offset and store result in destination buffer. */47.        *pDst++ = (*pSrc++) + offset;48.    49.        *pDst++ = (*pSrc++) + offset;50.    51.        *pDst++ = (*pSrc++) + offset;52.    53.        *pDst++ = (*pSrc++) + offset;54.    55.        /* Decrement loop counter */56.        blkCnt--;57.      }58.    59.      /* Loop unrolling: Compute remaining outputs */60.      blkCnt = blockSize % 0x4U;61.    62.    #else63.    64.      /* Initialize blkCnt with number of samples */65.      blkCnt = blockSize;66.    67.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */68.    #endif /* #if defined(ARM_MATH_NEON_EXPERIMENTAL) */69.    70.      while (blkCnt > 0U)71.      {72.        /* C = A + offset */73.    74.        /* Add offset and store result in destination buffer. */75.        *pDst++ = (*pSrc++) + offset;76.    77.        /* Decrement loop counter */78.        blkCnt--;79.      }80.    81.    }[/code]
函数描述:
这个函数用于求32位浮点数的偏移。
函数解析:

  •   第9到36行,用于NEON指令集,当前的CM内核不支持。
  •   第37到62行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第70到79行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:

  •   第1个参数是源数据地址。
  •   第2个参数是偏移量。
  •   第3个参数是转换后的目的地址。
  •   第4个参数是浮点数个数,其实就是执行偏移的次数。
12.4.2        函数arm_offset_q31

函数原型:
  1. 1.    void arm_offset_q31(2.      const q31_t * pSrc,3.            q31_t offset,4.            q31_t * pDst,5.            uint32_t blockSize)6.    {7.            uint32_t blkCnt;                               /* Loop counter */8.    9.    #if defined (ARM_MATH_LOOPUNROLL)10.    11.      /* Loop unrolling: Compute 4 outputs at a time */12.      blkCnt = blockSize >> 2U;13.    14.      while (blkCnt > 0U)15.      {16.        /* C = A + offset */17.    18.        /* Add offset and store result in destination buffer. */19.    #if defined (ARM_MATH_DSP)20.        *pDst++ = __QADD(*pSrc++, offset);21.    #else22.        *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);23.    #endif24.    25.    #if defined (ARM_MATH_DSP)26.        *pDst++ = __QADD(*pSrc++, offset);27.    #else28.        *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);29.    #endif30.    31.    #if defined (ARM_MATH_DSP)32.        *pDst++ = __QADD(*pSrc++, offset);33.    #else34.        *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);35.    #endif36.    37.    #if defined (ARM_MATH_DSP)38.        *pDst++ = __QADD(*pSrc++, offset);39.    #else40.        *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);41.    #endif42.    43.        /* Decrement loop counter */44.        blkCnt--;45.      }46.    47.      /* Loop unrolling: Compute remaining outputs */48.      blkCnt = blockSize % 0x4U;49.    50.    #else51.    52.      /* Initialize blkCnt with number of samples */53.      blkCnt = blockSize;54.    55.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */56.    57.      while (blkCnt > 0U)58.      {59.        /* C = A + offset */60.    61.        /* Add offset and store result in destination buffer. */62.    #if defined (ARM_MATH_DSP)63.        *pDst++ = __QADD(*pSrc++, offset);64.    #else65.        *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);66.    #endif67.    68.        /* Decrement loop counter */69.        blkCnt--;70.      }71.    72.    }
复制代码

函数描述:
这个函数用于求两个32位定点数的偏移。
函数解析:

  •   第9到50行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第57到70行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   __QADD实现32位数的加法饱和运算。输出结果的范围[0x80000000 0x7FFFFFFF],超出这个结果将产生饱和结果,负数饱和到0x80000000,正数饱和到0x7FFFFFFF。
函数参数:

  •   第1个参数是源数据地址。
  •   第2个参数是偏移量。
  •   第3个参数是转换后的目的地址。
  •   第4个参数是定点数个数,其实就是执行偏移的次数。
12.4.3        函数arm_offset_q15

函数原型:
  1. 1.    void arm_offset_q15(2.      const q15_t * pSrc,3.            q15_t offset,4.            q15_t * pDst,5.            uint32_t blockSize)6.    {7.            uint32_t blkCnt;                               /* Loop counter */8.    9.    #if defined (ARM_MATH_LOOPUNROLL)10.    11.    #if defined (ARM_MATH_DSP)12.      q31_t offset_packed;                           /* Offset packed to 32 bit */13.    14.      /* Offset is packed to 32 bit in order to use SIMD32 for addition */15.      offset_packed = __PKHBT(offset, offset, 16);16.    #endif17.    18.      /* Loop unrolling: Compute 4 outputs at a time */19.      blkCnt = blockSize >> 2U;20.    21.      while (blkCnt > 0U)22.      {23.        /* C = A + offset */24.    25.    #if defined (ARM_MATH_DSP)26.        /* Add offset and store result in destination buffer (2 samples at a time). */27.        write_q15x2_ia (&pDst, __QADD16(read_q15x2_ia ((q15_t **) &pSrc), offset_packed));28.        write_q15x2_ia (&pDst, __QADD16(read_q15x2_ia ((q15_t **) &pSrc), offset_packed));29.    #else30.        *pDst++ = (q15_t) __SSAT(((q31_t) *pSrc++ + offset), 16);31.        *pDst++ = (q15_t) __SSAT(((q31_t) *pSrc++ + offset), 16);32.        *pDst++ = (q15_t) __SSAT(((q31_t) *pSrc++ + offset), 16);33.        *pDst++ = (q15_t) __SSAT(((q31_t) *pSrc++ + offset), 16);34.    #endif35.    36.        /* Decrement loop counter */37.        blkCnt--;38.      }39.    40.      /* Loop unrolling: Compute remaining outputs */41.      blkCnt = blockSize % 0x4U;42.    43.    #else44.    45.      /* Initialize blkCnt with number of samples */46.      blkCnt = blockSize;47.    48.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */49.    50.      while (blkCnt > 0U)51.      {52.        /* C = A + offset */53.    54.        /* Add offset and store result in destination buffer. */55.    #if defined (ARM_MATH_DSP)56.        *pDst++ = (q15_t) __QADD16(*pSrc++, offset);57.    #else58.        *pDst++ = (q15_t) __SSAT(((q31_t) *pSrc++ + offset), 16);59.    #endif60.    61.        /* Decrement loop counter */62.        blkCnt--;63.      }64.    65.    }
复制代码

函数描述:
这个函数用于求16位定点数的偏移。
函数解析:

  •   第9到43行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第50到63行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   函数__PKHBT也是SIMD指令,作用是将将两个16位的数据合并成32位数据。用C实现的话,如下:
  1.   #define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1)  2U;20.    21.      while (blkCnt > 0U)22.      {23.        /* C = A + offset */24.    25.    #if defined (ARM_MATH_DSP)26.        /* Add offset and store result in destination buffer (4 samples at a time). */27.        write_q7x4_ia (&pDst, __QADD8(read_q7x4_ia ((q7_t **) &pSrc), offset_packed));28.    #else29.        *pDst++ = (q7_t) __SSAT(*pSrc++ + offset, 8);30.        *pDst++ = (q7_t) __SSAT(*pSrc++ + offset, 8);31.        *pDst++ = (q7_t) __SSAT(*pSrc++ + offset, 8);32.        *pDst++ = (q7_t) __SSAT(*pSrc++ + offset, 8);33.    #endif34.    35.        /* Decrement loop counter */36.        blkCnt--;37.      }38.    39.      /* Loop unrolling: Compute remaining outputs */40.      blkCnt = blockSize % 0x4U;41.    42.    #else43.    44.      /* Initialize blkCnt with number of samples */45.      blkCnt = blockSize;46.    47.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */48.    49.      while (blkCnt > 0U)50.      {51.        /* C = A + offset */52.    53.        /* Add offset and store result in destination buffer. */54.        *pDst++ = (q7_t) __SSAT((q15_t) *pSrc++ + offset, 8);55.    56.        /* Decrement loop counter */57.        blkCnt--;58.      }59.    60.    }
复制代码

函数描述:
这个函数用于求两个8位定点数的偏移。
函数解析:

  •   第9到42行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第49到58行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   函数write_q7x4_ia的原型如下:
  1. __STATIC_FORCEINLINE void write_q7x4_ia (  q7_t ** pQ7,  q31_t   value){  q31_t val = value;  memcpy (*pQ7, &val, 4);  *pQ7 += 4;}
复制代码

作用是写4次8位数据,并将数据地址递增,方便下次继续写。

  •   __QADD8实现四次8位数的加法饱和运算。输出结果的范围[0x80 0x7F],超出这个结果将产生饱和结果,负数饱和到0x80,正数饱和到0x7F。
函数参数:

  •   第1个参数是源数据地址。
  •   第2个参数是偏移量。
  •   第3个参数是转换后的目的地址。
  •   第4个参数是定点数个数,其实就是执行偏移的次数。
12.4.5        使用举例

程序设计:
  1. /***********************************************************************************************************    函 数 名: DSP_Offset*    功能说明: 偏移*    形    参: 无*    返 回 值: 无**********************************************************************************************************/static void DSP_Offset(void){    float32_t   pSrcA = 0.0f;    float32_t   Offset = 0.0f;      float32_t   pDst;          q31_t  pSrcA1 = 0;      q31_t  Offset1 = 0;      q31_t  pDst1;      q15_t  pSrcA2 = 0;      q15_t  Offset2 = 0;      q15_t  pDst2;     q7_t  pSrcA3 = 0;     q7_t  Offset3 = 0;      q7_t  pDst3;      /*求偏移*********************************/            Offset--;    arm_offset_f32(&pSrcA, Offset, &pDst, 1);    printf("arm_offset_f32 = %f\r\n", pDst);    Offset1--;    arm_offset_q31(&pSrcA1, Offset1, &pDst1, 1);    printf("arm_offset_q31 = %d\r\n", pDst1);    Offset2--;    arm_offset_q15(&pSrcA2, Offset2, &pDst2, 1);    printf("arm_offset_q15 = %d\r\n", pDst2);    Offset3--;    arm_offset_q7(&pSrcA3, Offset3, &pDst3, 1);    printf("arm_offset_q7 = %d\r\n", pDst3);    printf("***********************************\r\n");}
复制代码

实验现象:
我的关键词 【STM32H7的DSP教程】第12章    DSP基础函数-相反数,偏移,移位,减法和比  新闻咨询 1379107-20200420153217580-1315890075

12.5 移位(Vector Shift)

这部分函数主要用于实现移位,公式描述如下:

pDst[n] = pSrc[n]  2U;16.    17.      /* If the shift value is positive then do right shift else left shift */18.      if (sign == 0U)19.      {20.        while (blkCnt > 0U)21.        {22.          /* C = A  shiftBits))28.            out = 0x7FFFFFFF ^ (in >> 31);29.          *pDst++ = out;30.    31.          in = *pSrc++;32.          out = in > shiftBits))34.            out = 0x7FFFFFFF ^ (in >> 31);35.          *pDst++ = out;36.    37.          in = *pSrc++;38.          out = in > shiftBits))40.            out = 0x7FFFFFFF ^ (in >> 31);41.          *pDst++ = out;42.    43.          in = *pSrc++;44.          out = in > shiftBits))46.            out = 0x7FFFFFFF ^ (in >> 31);47.          *pDst++ = out;48.    49.          /* Decrement loop counter */50.          blkCnt--;51.        }52.      }53.      else54.      {55.        while (blkCnt > 0U)56.        {57.          /* C = A >> shiftBits */58.    59.          /* Shift input and store results in destination buffer. */60.          *pDst++ = (*pSrc++ >> -shiftBits);61.          *pDst++ = (*pSrc++ >> -shiftBits);62.          *pDst++ = (*pSrc++ >> -shiftBits);63.          *pDst++ = (*pSrc++ >> -shiftBits);64.    65.          /* Decrement loop counter */66.          blkCnt--;67.        }68.      }69.    70.      /* Loop unrolling: Compute remaining outputs */71.      blkCnt = blockSize % 0x4U;72.    73.    #else74.    75.      /* Initialize blkCnt with number of samples */76.      blkCnt = blockSize;77.    78.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */79.    80.      /* If the shift value is positive then do right shift else left shift */81.      if (sign == 0U)82.      {83.        while (blkCnt > 0U)84.        {85.          /* C = A > shiftBits */99.    100.          /* Shift input and store result in destination buffer. */101.          *pDst++ = (*pSrc++ >> -shiftBits);102.    103.          /* Decrement loop counter */104.          blkCnt--;105.        }106.      }107.    108.    }[/code]
函数描述:
这个函数用于求32位定点数的左移或者右移。
函数解析:

  •   第10到73行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。

    •   第18到52行,如果参数shiftBits是正数,执行左移。
    •   第53到68行,如果蚕食shiftBits是负数,执行右移。
    •   第28行,数值的左移仅支持将其左移后再右移相应的位数后数值不变的情况,如果不满足这个条件,那么要对输出结果做饱和运算,这里分两种情况:

out = 0x7FFFFFFF ^ (in >> 31)   (in是正数)
= 0x7FFFFFFF ^ 0x00000000
= 0x7FFFFFFF
out = 0x7FFFFFFF ^ (in >> 31)   (in是负数)
= 0x7FFFFFFF ^ 0xFFFFFFFF
= 0x80000000

  •   第81到106行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。

    •   第88行,函数clip_q63_to_q31的原型如下:

  1. __STATIC_FORCEINLINE q31_t clip_q63_to_q31(  q63_t x)  {    return ((q31_t) (x >> 32) != ((q31_t) x >> 31)) ?      ((0x7FFFFFFF ^ ((q31_t) (x >> 63)))) : (q31_t) x;  }
复制代码

函数参数:

  •   第1个参数是源数据地址。
  •   第2个参数是左移或者右移位数,正数是左移,负数是右移。
  •   第3个参数是移位后数据地址。
  •   第4个参数是定点数个数,其实就是执行左移或者右移的次数。
12.5.2        函数arm_shift_q15

函数原型:
  1. 1.    void arm_shift_q15(2.      const q15_t * pSrc,3.            int8_t shiftBits,4.            q15_t * pDst,5.            uint32_t blockSize)6.    {7.            uint32_t blkCnt;                               /* Loop counter */8.            uint8_t sign = (shiftBits & 0x80);             /* Sign of shiftBits */9.    10.    #if defined (ARM_MATH_LOOPUNROLL)11.    12.    #if defined (ARM_MATH_DSP)13.      q15_t in1, in2;                                /* Temporary input variables */14.    #endif15.    16.      /* Loop unrolling: Compute 4 outputs at a time */17.      blkCnt = blockSize >> 2U;18.    19.      /* If the shift value is positive then do right shift else left shift */20.      if (sign == 0U)21.      {22.        while (blkCnt > 0U)23.        {24.          /* C = A  -shiftBits),89.                                         (in2 >> -shiftBits), 16));90.    #else91.          write_q15x2_ia (&pDst, __PKHBT((in2 >> -shiftBits),92.                                         (in1 >> -shiftBits), 16));93.    #endif /* #ifndef ARM_MATH_BIG_ENDIAN */94.    95.    #else96.          *pDst++ = (*pSrc++ >> -shiftBits);97.          *pDst++ = (*pSrc++ >> -shiftBits);98.          *pDst++ = (*pSrc++ >> -shiftBits);99.          *pDst++ = (*pSrc++ >> -shiftBits);100.    #endif101.    102.          /* Decrement loop counter */103.          blkCnt--;104.        }105.      }106.    107.      /* Loop unrolling: Compute remaining outputs */108.      blkCnt = blockSize % 0x4U;109.    110.    #else111.    112.      /* Initialize blkCnt with number of samples */113.      blkCnt = blockSize;114.    115.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */116.    117.      /* If the shift value is positive then do right shift else left shift */118.      if (sign == 0U)119.      {120.        while (blkCnt > 0U)121.        {122.          /* C = A > shiftBits */136.    137.          /* Shift input and store result in destination buffer. */138.          *pDst++ = (*pSrc++ >> -shiftBits);139.    140.          /* Decrement loop counter */141.          blkCnt--;142.        }143.      }144.    145.    }
复制代码

函数描述:
这个函数用于求16位定点数的左移或者右移。
函数解析:

  •   第10到115行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。

    •   第20到62行,如果参数shiftBits是正数,执行左移。
    •   第63到105行,如果蚕食shiftBits是负数,执行右移。
    •   第79行,函数write_q15x2_ia的原型如下,用于实现将两个Q15组成合并成一个Q31。

  1. __STATIC_FORCEINLINE void write_q15x2_ia (  q15_t ** pQ15,  q31_t    value){  q31_t val = value;  memcpy (*pQ15, &val, 4);  *pQ15 += 2;}
复制代码

函数__PKHBT也是SIMD指令,作用是将将两个16位的数据合并成32位数据。用C实现的话,如下:
  1. #define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1)  2U;18.    19.      /* If the shift value is positive then do right shift else left shift */20.      if (sign == 0U)21.      {22.        while (blkCnt > 0U)23.        {24.          /* C = A  -shiftBits),66.                                         (in4 >> -shiftBits) ));67.    #else68.          *pDst++ = (*pSrc++ >> -shiftBits);69.          *pDst++ = (*pSrc++ >> -shiftBits);70.          *pDst++ = (*pSrc++ >> -shiftBits);71.          *pDst++ = (*pSrc++ >> -shiftBits);72.    #endif73.    74.          /* Decrement loop counter */75.          blkCnt--;76.        }77.      }78.    79.      /* Loop unrolling: Compute remaining outputs */80.      blkCnt = blockSize % 0x4U;81.    82.    #else83.    84.      /* Initialize blkCnt with number of samples */85.      blkCnt = blockSize;86.    87.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */88.    89.      /* If the shift value is positive then do right shift else left shift */90.      if (sign == 0U)91.      {92.        while (blkCnt > 0U)93.        {94.          /* C = A > shiftBits */108.    109.          /* Shift input and store result in destination buffer. */110.          *pDst++ = (*pSrc++ >> -shiftBits);111.    112.          /* Decrement loop counter */113.          blkCnt--;114.        }115.      }116.    117.    }
复制代码

函数描述:
这个函数用于求8位定点数的左移或者右移。
函数解析:

  •   第10到87行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。

    •   第20到48行,如果参数shiftBits是正数,执行左移。
    •   第49到77行,如果蚕食shiftBits是负数,执行右移。
    •   第79行,函数write_q7x4_ia的原型如下,作用是写入4次8位数据,并将数据地址递增,方便下次写入。

  1. __STATIC_FORCEINLINE void write_q7x4_ia (  q7_t ** pQ7,  q31_t   value){  q31_t val = value;  memcpy (*pQ7, &val, 4);  *pQ7 += 4;}
复制代码

函数__PACKq7作用是将将4个8位的数据合并成32位数据,实现代码如下:
  1. #define __PACKq7(v0,v1,v2,v3) ( (((int32_t)(v0)  2U;44.    45.      while (blkCnt > 0U)46.      {47.        /* C = A - B */48.    49.        /* Subtract and store result in destination buffer. */50.        *pDst++ = (*pSrcA++) - (*pSrcB++);51.    52.        *pDst++ = (*pSrcA++) - (*pSrcB++);53.    54.        *pDst++ = (*pSrcA++) - (*pSrcB++);55.    56.        *pDst++ = (*pSrcA++) - (*pSrcB++);57.    58.        /* Decrement loop counter */59.        blkCnt--;60.      }61.    62.      /* Loop unrolling: Compute remaining outputs */63.      blkCnt = blockSize % 0x4U;64.    65.    #else66.    67.      /* Initialize blkCnt with number of samples */68.      blkCnt = blockSize;69.    70.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */71.    #endif /* #if defined(ARM_MATH_NEON) */72.    73.      while (blkCnt > 0U)74.      {75.        /* C = A - B */76.    77.        /* Subtract and store result in destination buffer. */78.        *pDst++ = (*pSrcA++) - (*pSrcB++);79.    80.        /* Decrement loop counter */81.        blkCnt--;82.      }83.    84.    }
复制代码

函数描述:
这个函数用于求32位浮点数的减法。
函数解析:

  •   第9到39行,用于NEON指令集,当前的CM内核不支持。
  •   第40到65行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第73到82行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:

  •   第1个参数是减数地址。
  •   第2个参数是被减数地址。
  •   第3个参数是结果地址。
  •   第4个参数是数据块大小,其实就是执行减法的次数。
12.6.2        函数arm_sub_q31

函数原型:
  1. 1.    void arm_sub_q31(2.      const q31_t * pSrcA,3.      const q31_t * pSrcB,4.            q31_t * pDst,5.            uint32_t blockSize)6.    {7.            uint32_t blkCnt;                               /* Loop counter */8.    9.    #if defined (ARM_MATH_LOOPUNROLL)10.    11.      /* Loop unrolling: Compute 4 outputs at a time */12.      blkCnt = blockSize >> 2U;13.    14.      while (blkCnt > 0U)15.      {16.        /* C = A - B */17.    18.        /* Subtract and store result in destination buffer. */19.        *pDst++ = __QSUB(*pSrcA++, *pSrcB++);20.    21.        *pDst++ = __QSUB(*pSrcA++, *pSrcB++);22.    23.        *pDst++ = __QSUB(*pSrcA++, *pSrcB++);24.    25.        *pDst++ = __QSUB(*pSrcA++, *pSrcB++);26.    27.        /* Decrement loop counter */28.        blkCnt--;29.      }30.    31.      /* Loop unrolling: Compute remaining outputs */32.      blkCnt = blockSize % 0x4U;33.    34.    #else35.    36.      /* Initialize blkCnt with number of samples */37.      blkCnt = blockSize;38.    39.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */40.    41.      while (blkCnt > 0U)42.      {43.        /* C = A - B */44.    45.        /* Subtract and store result in destination buffer. */46.        *pDst++ = __QSUB(*pSrcA++, *pSrcB++);47.    48.        /* Decrement loop counter */49.        blkCnt--;50.      }51.    52.    }
复制代码

函数描述:
这个函数用于求32位定点数的减法。
函数解析:

  •   这个函数使用了饱和减法__QSUB,所得结果是Q31格式,范围[0x80000000 0x7FFFFFFF]。
  •   第9到34行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第41到50行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:

  •   第1个参数是减数地址。
  •   第2个参数是被减数地址。
  •   第3个参数是结果地址。
  •   第4个参数是数据块大小,其实就是执行减法的次数。
12.6.3        函数arm_sub_q15

函数原型:
  1. 1.    void arm_sub_q15(2.      const q15_t * pSrcA,3.      const q15_t * pSrcB,4.            q15_t * pDst,5.            uint32_t blockSize)6.    {7.            uint32_t blkCnt;                               /* Loop counter */8.    9.    #if defined (ARM_MATH_LOOPUNROLL)10.    11.    #if defined (ARM_MATH_DSP)12.      q31_t inA1, inA2;13.      q31_t inB1, inB2;14.    #endif15.    16.      /* Loop unrolling: Compute 4 outputs at a time */17.      blkCnt = blockSize >> 2U;18.    19.      while (blkCnt > 0U)20.      {21.        /* C = A - B */22.    23.    #if defined (ARM_MATH_DSP)24.        /* read 2 times 2 samples at a time from sourceA */25.        inA1 = read_q15x2_ia ((q15_t **) &pSrcA);26.        inA2 = read_q15x2_ia ((q15_t **) &pSrcA);27.        /* read 2 times 2 samples at a time from sourceB */28.        inB1 = read_q15x2_ia ((q15_t **) &pSrcB);29.        inB2 = read_q15x2_ia ((q15_t **) &pSrcB);30.    31.        /* Subtract and store 2 times 2 samples at a time */32.        write_q15x2_ia (&pDst, __QSUB16(inA1, inB1));33.        write_q15x2_ia (&pDst, __QSUB16(inA2, inB2));34.    #else35.        *pDst++ = (q15_t) __SSAT(((q31_t) *pSrcA++ - *pSrcB++), 16);36.        *pDst++ = (q15_t) __SSAT(((q31_t) *pSrcA++ - *pSrcB++), 16);37.        *pDst++ = (q15_t) __SSAT(((q31_t) *pSrcA++ - *pSrcB++), 16);38.        *pDst++ = (q15_t) __SSAT(((q31_t) *pSrcA++ - *pSrcB++), 16);39.    #endif40.    41.        /* Decrement loop counter */42.        blkCnt--;43.      }44.    45.      /* Loop unrolling: Compute remaining outputs */46.      blkCnt = blockSize % 0x4U;47.    48.    #else49.    50.      /* Initialize blkCnt with number of samples */51.      blkCnt = blockSize;52.    53.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */54.    55.      while (blkCnt > 0U)56.      {57.        /* C = A - B */58.    59.        /* Subtract and store result in destination buffer. */60.    #if defined (ARM_MATH_DSP)61.        *pDst++ = (q15_t) __QSUB16(*pSrcA++, *pSrcB++);62.    #else63.        *pDst++ = (q15_t) __SSAT(((q31_t) *pSrcA++ - *pSrcB++), 16);64.    #endif65.    66.        /* Decrement loop counter */67.        blkCnt--;68.      }69.    70.    }
复制代码

函数描述:
这个函数用于求16位定点数的减法。
函数解析:

  •   第9到48行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。

    •   第25行,函数read_q15x2_ia一次读取两个Q15格式的数据,组成一个Q31格式。
    •   第32行,函数write_q15x2_ia一次写入两个Q15格式的数据,获得一个Q31格式数据。
    •   第32行,函数__QSUB16实现两次16bit的饱和减法。

  •   第55到68行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:

  • 第1个参数是减数地址。
  •   第2个参数是被减数地址。
  •   第3个参数是结果地址。
  • 第4个参数是数据块大小,其实就是执行减法的次数。
12.6.4        函数arm_sub_q7

函数原型:
  1. 1.    void arm_sub_q7(2.      const q7_t * pSrcA,3.      const q7_t * pSrcB,4.            q7_t * pDst,5.            uint32_t blockSize)6.    {7.            uint32_t blkCnt;                               /* Loop counter */8.    9.    #if defined (ARM_MATH_LOOPUNROLL)10.    11.      /* Loop unrolling: Compute 4 outputs at a time */12.      blkCnt = blockSize >> 2U;13.    14.      while (blkCnt > 0U)15.      {16.        /* C = A - B */17.    18.    #if defined (ARM_MATH_DSP)19.        /* Subtract and store result in destination buffer (4 samples at a time). */20.        write_q7x4_ia (&pDst, __QSUB8(read_q7x4_ia ((q7_t **) &pSrcA), read_q7x4_ia ((q7_t **) &pSrcB)));21.    #else22.        *pDst++ = (q7_t) __SSAT((q15_t) *pSrcA++ - *pSrcB++, 8);23.        *pDst++ = (q7_t) __SSAT((q15_t) *pSrcA++ - *pSrcB++, 8);24.        *pDst++ = (q7_t) __SSAT((q15_t) *pSrcA++ - *pSrcB++, 8);25.        *pDst++ = (q7_t) __SSAT((q15_t) *pSrcA++ - *pSrcB++, 8);26.    #endif27.    28.        /* Decrement loop counter */29.        blkCnt--;30.      }31.    32.      /* Loop unrolling: Compute remaining outputs */33.      blkCnt = blockSize % 0x4U;34.    35.    #else36.    37.      /* Initialize blkCnt with number of samples */38.      blkCnt = blockSize;39.    40.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */41.    42.      while (blkCnt > 0U)43.      {44.        /* C = A - B */45.    46.        /* Subtract and store result in destination buffer. */47.        *pDst++ = (q7_t) __SSAT((q15_t) *pSrcA++ - *pSrcB++, 8);48.    49.        /* Decrement loop counter */50.        blkCnt--;51.      }52.    53.    }
复制代码

函数描述:
这个函数用于求8位定点数的乘法。
函数解析:

  •   第9到35行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。

    •   第20行,函数write_q7x4_ia实现一次写入4个Q7格式数据到Q31各种中。

函数__QSUB8实现一次计算4个Q7格式减法。

  •   第42到51行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:

  • 第1个参数是减数地址。
  •   第2个参数是被减数地址。
  •   第3个参数是结果地址。
  •   第4个参数是数据块大小,其实就是执行减法的次数。
12.6.5        使用举例

程序设计:
  1. /***********************************************************************************************************    函 数 名: DSP_Sub*    功能说明: 减法*    形    参: 无*    返 回 值: 无**********************************************************************************************************/static void DSP_Sub(void){    float32_t   pSrcA[5] = {1.0f,1.0f,1.0f,1.0f,1.0f};    float32_t   pSrcB[5] = {1.0f,1.0f,1.0f,1.0f,1.0f};      float32_t   pDst[5];          q31_t  pSrcA1[5] = {1,1,1,1,1};      q31_t  pSrcB1[5] = {1,1,1,1,1};      q31_t  pDst1[5];       q15_t  pSrcA2[5] = {1,1,1,1,1};      q15_t  pSrcB2[5] = {1,1,1,1,1};      q15_t  pDst2[5];       q7_t  pSrcA3[5] = {0x70,1,1,1,1};     q7_t  pSrcB3[5] = {0x7f,1,1,1,1};      q7_t  pDst3[5];      /*求减法*********************************/        pSrcA[0] += 1.1f;    arm_sub_f32(pSrcA, pSrcB, pDst, 5);    printf("arm_sub_f32 = %f\r\n", pDst[0]);        pSrcA1[0] += 1;    arm_sub_q31(pSrcA1, pSrcB1, pDst1, 5);    printf("arm_sub_q31 = %d\r\n", pDst1[0]);    pSrcA2[0] += 1;    arm_sub_q15(pSrcA2, pSrcB2, pDst2, 5);    printf("arm_sub_q15 = %d\r\n", pDst2[0]);    pSrcA3[0] += 1;    arm_sub_q7(pSrcA3, pSrcB3, pDst3, 5);    printf("arm_sub_q7 = %d\r\n", pDst3[0]);    printf("***********************************\r\n");}
复制代码

实验现象:
我的关键词 【STM32H7的DSP教程】第12章    DSP基础函数-相反数,偏移,移位,减法和比  新闻咨询 1379107-20200420153239045-1766683289

12.7 比例因子(Vector Scale)

这部分函数主要用于实现数据的比例放大和缩小,浮点数据公式描述如下:

pDst[n] = pSrc[n] * scale,   0  0U)16.        {17.            /* C = A * scale */18.    19.            /* Scale the input and then store the results in the destination buffer. */20.            vec1 = vld1q_f32(pSrc);21.            res = vmulq_f32(vec1, vdupq_n_f32(scale));22.            vst1q_f32(pDst, res);23.    24.            /* Increment pointers */25.            pSrc += 4; 26.            pDst += 4;27.            28.            /* Decrement the loop counter */29.            blkCnt--;30.        }31.    32.        /* Tail */33.        blkCnt = blockSize & 0x3;34.    35.    #else36.    #if defined (ARM_MATH_LOOPUNROLL)37.    38.      /* Loop unrolling: Compute 4 outputs at a time */39.      blkCnt = blockSize >> 2U;40.    41.      while (blkCnt > 0U)42.      {43.        /* C = A * scale */44.    45.        /* Scale input and store result in destination buffer. */46.        *pDst++ = (*pSrc++) * scale;47.    48.        *pDst++ = (*pSrc++) * scale;49.    50.        *pDst++ = (*pSrc++) * scale;51.    52.        *pDst++ = (*pSrc++) * scale;53.    54.        /* Decrement loop counter */55.        blkCnt--;56.      }57.    58.      /* Loop unrolling: Compute remaining outputs */59.      blkCnt = blockSize % 0x4U;60.    61.    #else62.    63.      /* Initialize blkCnt with number of samples */64.      blkCnt = blockSize;65.    66.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */67.    #endif /* #if defined(ARM_MATH_NEON_EXPERIMENTAL) */68.    69.      while (blkCnt > 0U)70.      {71.        /* C = A * scale */72.    73.        /* Scale input and store result in destination buffer. */74.        *pDst++ = (*pSrc++) * scale;75.    76.        /* Decrement loop counter */77.        blkCnt--;78.      }79.    80.    }[/code]
函数描述:
这个函数用于求32位浮点数的比例因子计算。
函数解析:

  •   第8到35行,用于NEON指令集,当前的CM内核不支持。
  •   第36到61行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第69到78行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:

  •   第1个参数是数据源地址。
  •   第2个参数是比例因子
  •   第3个参数是结果地址。
  •   第4个参数是数据块大小,其实就是执行比例因子计算的次数。
12.7.2        函数arm_scale_q31

函数原型:
  1. 1.    void arm_scale_q31(2.      const q31_t *pSrc,3.            q31_t scaleFract,4.            int8_t shift,5.            q31_t *pDst,6.            uint32_t blockSize)7.    {8.            uint32_t blkCnt;                               /* Loop counter */9.            q31_t in, out;                                 /* Temporary variables */10.            int8_t kShift = shift + 1;                     /* Shift to apply after scaling */11.            int8_t sign = (kShift & 0x80);12.    13.    #if defined (ARM_MATH_LOOPUNROLL)14.    15.      /* Loop unrolling: Compute 4 outputs at a time */16.      blkCnt = blockSize >> 2U;17.    18.      if (sign == 0U)19.      {20.        while (blkCnt > 0U)21.        {22.          /* C = A * scale */23.    24.          /* Scale input and store result in destination buffer. */25.          in = *pSrc++;                                /* read input from source */26.          in = ((q63_t) in * scaleFract) >> 32;        /* multiply input with scaler value */27.          out = in > kShift))                   /* saturate the result */29.            out = 0x7FFFFFFF ^ (in >> 31);30.          *pDst++ = out;                               /* Store result destination */31.    32.          in = *pSrc++;33.          in = ((q63_t) in * scaleFract) >> 32;34.          out = in > kShift))36.            out = 0x7FFFFFFF ^ (in >> 31);37.          *pDst++ = out;38.    39.          in = *pSrc++;40.          in = ((q63_t) in * scaleFract) >> 32;41.          out = in > kShift))43.            out = 0x7FFFFFFF ^ (in >> 31);44.          *pDst++ = out;45.    46.          in = *pSrc++;47.          in = ((q63_t) in * scaleFract) >> 32;48.          out = in > kShift))50.            out = 0x7FFFFFFF ^ (in >> 31);51.          *pDst++ = out;52.    53.          /* Decrement loop counter */54.          blkCnt--;55.        }56.      }57.      else58.      {59.        while (blkCnt > 0U)60.        {61.          /* C = A * scale */62.    63.          /* Scale input and store result in destination buffer. */64.          in = *pSrc++;                                /* read four inputs from source */65.          in = ((q63_t) in * scaleFract) >> 32;        /* multiply input with scaler value */66.          out = in >> -kShift;                         /* apply shifting */67.          *pDst++ = out;                               /* Store result destination */68.    69.          in = *pSrc++;70.          in = ((q63_t) in * scaleFract) >> 32;71.          out = in >> -kShift;72.          *pDst++ = out;73.    74.          in = *pSrc++;75.          in = ((q63_t) in * scaleFract) >> 32;76.          out = in >> -kShift;77.          *pDst++ = out;78.    79.          in = *pSrc++;80.          in = ((q63_t) in * scaleFract) >> 32;81.          out = in >> -kShift;82.          *pDst++ = out;83.    84.          /* Decrement loop counter */85.          blkCnt--;86.        }87.      }88.    89.      /* Loop unrolling: Compute remaining outputs */90.      blkCnt = blockSize % 0x4U;91.    92.    #else93.    94.      /* Initialize blkCnt with number of samples */95.      blkCnt = blockSize;96.    97.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */98.    99.      if (sign == 0U)100.      {101.        while (blkCnt > 0U)102.        {103.          /* C = A * scale */104.    105.          /* Scale input and store result in destination buffer. */106.          in = *pSrc++;107.          in = ((q63_t) in * scaleFract) >> 32;108.          out = in > kShift))110.              out = 0x7FFFFFFF ^ (in >> 31);111.          *pDst++ = out;112.    113.          /* Decrement loop counter */114.          blkCnt--;115.        }116.      }117.      else118.      {119.        while (blkCnt > 0U)120.        {121.          /* C = A * scale */122.    123.          /* Scale input and store result in destination buffer. */124.          in = *pSrc++;125.          in = ((q63_t) in * scaleFract) >> 32;126.          out = in >> -kShift;127.          *pDst++ = out;128.    129.          /* Decrement loop counter */130.          blkCnt--;131.        }132.      }133.    134.    }
复制代码

函数描述:
这个函数用于求32位定点数的比例因子计算。
函数解析:

  •   第13到92行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。

    •   第18行到56行,如果函数的移位形参shift是正数,那么执行左移。
    •   第57行到87行,如果函数的移位形参shift是负数,那么执行右移。
    •   这里特别注意一点,两个Q31函数相乘是2.62格式,而函数的结果要是Q31格式的,所以程序里面做了专门处理。

第26行,左移32位,那么结果就是2.30格式。
第27行,kShift = shift + 1,也就是out = in  31)   (in是正数)
= 0x7FFFFFFF ^ 0x00000000
= 0x7FFFFFFF
out = 0x7FFFFFFF ^ (in >> 31)   (in是负数)
= 0x7FFFFFFF ^ 0xFFFFFFFF
= 0x80000000

  •   第99到132行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:

  •   第1个参数是数据源地址。
  •   第2个参数是比例因子。
  •   第3个参数是移位参数,正数表示右移,负数表示左移。
  •   第4参数是结果地址。
  •   第5参数是数据块大小,其实就是执行比例因子计算的次数。
12.7.3        函数arm_scale_q15

函数原型:
  1. 1.    void arm_shift_q15(2.      const q15_t * pSrc,3.            int8_t shiftBits,4.            q15_t * pDst,5.            uint32_t blockSize)6.    {7.            uint32_t blkCnt;                               /* Loop counter */8.            uint8_t sign = (shiftBits & 0x80);             /* Sign of shiftBits */9.    10.    #if defined (ARM_MATH_LOOPUNROLL)11.    12.    #if defined (ARM_MATH_DSP)13.      q15_t in1, in2;                                /* Temporary input variables */14.    #endif15.    16.      /* Loop unrolling: Compute 4 outputs at a time */17.      blkCnt = blockSize >> 2U;18.    19.      /* If the shift value is positive then do right shift else left shift */20.      if (sign == 0U)21.      {22.        while (blkCnt > 0U)23.        {24.          /* C = A  -shiftBits),89.                                         (in2 >> -shiftBits), 16));90.    #else91.          write_q15x2_ia (&pDst, __PKHBT((in2 >> -shiftBits),92.                                         (in1 >> -shiftBits), 16));93.    #endif /* #ifndef ARM_MATH_BIG_ENDIAN */94.    95.    #else96.          *pDst++ = (*pSrc++ >> -shiftBits);97.          *pDst++ = (*pSrc++ >> -shiftBits);98.          *pDst++ = (*pSrc++ >> -shiftBits);99.          *pDst++ = (*pSrc++ >> -shiftBits);100.    #endif101.    102.          /* Decrement loop counter */103.          blkCnt--;104.        }105.      }106.    107.      /* Loop unrolling: Compute remaining outputs */108.      blkCnt = blockSize % 0x4U;109.    110.    #else111.    112.      /* Initialize blkCnt with number of samples */113.      blkCnt = blockSize;114.    115.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */116.    117.      /* If the shift value is positive then do right shift else left shift */118.      if (sign == 0U)119.      {120.        while (blkCnt > 0U)121.        {122.          /* C = A > shiftBits */136.    137.          /* Shift input and store result in destination buffer. */138.          *pDst++ = (*pSrc++ >> -shiftBits);139.    140.          /* Decrement loop counter */141.          blkCnt--;142.        }143.      }144.    145.    }
复制代码

函数描述:
这个函数用于求16位定点数的比例因子计算。
函数解析:

  •   第10到110行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。

    •   第20到62行,如果函数的移位形参shiftBits是正数,执行左移。
    •   第63到105行,如果函数的移位形参shiftBits是负数,执行右移。
    •   第33行,函数__PKHBT也是SIMD指令,作用是将将两个16位的数据合并成32位数据。用C实现的话,如下:

  1.   #define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1)  2U;20.    21.      while (blkCnt > 0U)22.      {23.        /* C = A * scale */24.    25.    #if defined (ARM_MATH_DSP)26.        /* Reading 4 inputs from memory */27.        in1 = *pSrc++;28.        in2 = *pSrc++;29.        in3 = *pSrc++;30.        in4 = *pSrc++;31.    32.        /* Scale inputs and store result in the temporary variable. */33.        out1 = (q7_t) (__SSAT(((in1) * scaleFract) >> kShift, 8));34.        out2 = (q7_t) (__SSAT(((in2) * scaleFract) >> kShift, 8));35.        out3 = (q7_t) (__SSAT(((in3) * scaleFract) >> kShift, 8));36.        out4 = (q7_t) (__SSAT(((in4) * scaleFract) >> kShift, 8));37.    38.        /* Pack and store result in destination buffer (in single write) */39.        write_q7x4_ia (&pDst, __PACKq7(out1, out2, out3, out4));40.    #else41.        *pDst++ = (q7_t) (__SSAT((((q15_t) *pSrc++ * scaleFract) >> kShift), 8));42.        *pDst++ = (q7_t) (__SSAT((((q15_t) *pSrc++ * scaleFract) >> kShift), 8));43.        *pDst++ = (q7_t) (__SSAT((((q15_t) *pSrc++ * scaleFract) >> kShift), 8));44.        *pDst++ = (q7_t) (__SSAT((((q15_t) *pSrc++ * scaleFract) >> kShift), 8));45.    #endif46.    47.        /* Decrement loop counter */48.        blkCnt--;49.      }50.    51.      /* Loop unrolling: Compute remaining outputs */52.      blkCnt = blockSize % 0x4U;53.    54.    #else55.    56.      /* Initialize blkCnt with number of samples */57.      blkCnt = blockSize;58.    59.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */60.    61.      while (blkCnt > 0U)62.      {63.        /* C = A * scale */64.    65.        /* Scale input and store result in destination buffer. */66.        *pDst++ = (q7_t) (__SSAT((((q15_t) *pSrc++ * scaleFract) >> kShift), 8));67.    68.        /* Decrement loop counter */69.        blkCnt--;70.      }71.    72.    }
复制代码

函数描述:
这个函数用于求8位定点数的比例因子计算。
函数解析:

  •   第9行,这个变量设计很巧妙,这样下面处理正数左移和负数右移就很方面了,可以直接使用一个右移就可以实现。
  •   第11到54行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。

    •   33到36行,对输入的数据做8位的饱和处理。比如:

(in1 * scaleFract) >> kShift
= (in1 * scaleFract) * 2^(shift - 7)
= ((in1 * scaleFract) >>7)*(2^shift)
源数据in1格式Q7乘以比例因子scaleFract格式Q7,也就是2.14格式,再右移7bit就是2.7格式,
此时如果shift正数,那么就是当前结果左移shitf位,如果shift是负数,那么就是当前结果右移shift位。最终结果通过__SSAT做个饱和运算。

  •   第61到70行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:

  •   第1个参数是数据源地址。
  •   第2个参数是比例因子。
  •   第3个参数是移位参数,正数表示右移,负数表示左移。
  •   第4参数是结果地址。
  •   第5参数是数据块大小,其实就是执行比例因子计算的次数。
12.7.5        使用举例

程序设计:
  1. /***********************************************************************************************************    函 数 名: DSP_Scale*    功能说明: 比例因子*    形    参: 无*    返 回 值: 无**********************************************************************************************************/static void DSP_Scale(void){    float32_t   pSrcA[5] = {1.0f,1.0f,1.0f,1.0f,1.0f};    float32_t   scale = 0.0f;      float32_t   pDst[5];          q31_t  pSrcA1[5] = {0x6fffffff,1,1,1,1};      q31_t  scale1 = 0x6fffffff;      q31_t  pDst1[5];       q15_t  pSrcA2[5] = {0x6fff,1,1,1,1};      q15_t  scale2 = 0x6fff;      q15_t  pDst2[5];       q7_t  pSrcA3[5] = {0x70,1,1,1,1};     q7_t  scale3 = 0x6f;      q7_t pDst3[5];      /*求比例因子计算*********************************/        scale += 0.1f;    arm_scale_f32(pSrcA, scale, pDst, 5);    printf("arm_scale_f32 = %f\r\n", pDst[0]);        scale1 += 1;    arm_scale_q31(pSrcA1, scale1, 0, pDst1, 5);    printf("arm_scale_q31 = %x\r\n", pDst1[0]);    scale2 += 1;    arm_scale_q15(pSrcA2, scale2, 0, pDst2, 5);    printf("arm_scale_q15 = %x\r\n", pDst2[0]);    scale3 += 1;    arm_scale_q7(pSrcA3, scale3, 0, pDst3, 5);    printf("arm_scale_q7 = %x\r\n", pDst3[0]);    printf("***********************************\r\n");}
复制代码

实验现象:
我的关键词 【STM32H7的DSP教程】第12章    DSP基础函数-相反数,偏移,移位,减法和比  新闻咨询 1379107-20200420153251305-74250923

12.8 实验例程说明(MDK)

配套例子:
V7-207_DSP基础运算(相反数,偏移,移位,减法和比例因子)
实验目的:

  • 学习基础运算(相反数,偏移,移位,减法和比例因子)
实验内容:

  • 启动一个自动重装软件定时器,每100ms翻转一次LED2。
  • 按下按键K1, DSP求相反数运算。
  • 按下按键K2, DSP求偏移运算。
  • 按下按键K3, DSP求移位运算。
  • 按下摇杆OK键, DSP求减法运算。
  • 按下摇杆上键, DSP比例因子运算。
使用AC6注意事项
特别注意附件章节C的问题
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,停止位 1。
详见本章的3.5,4.5,5.4和6.5小节。
程序设计:
  系统栈大小分配:
我的关键词 【STM32H7的DSP教程】第12章    DSP基础函数-相反数,偏移,移位,减法和比  新闻咨询 1379107-20200420153257275-520012933

  RAM空间用的DTCM:
我的关键词 【STM32H7的DSP教程】第12章    DSP基础函数-相反数,偏移,移位,减法和比  新闻咨询 1379107-20200420153302379-473620434

  硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
  1. /***********************************************************************************************************    函 数 名: bsp_Init*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次*    形    参:无*    返 回 值: 无**********************************************************************************************************/void bsp_Init(void){    /* 配置MPU */    MPU_Config();        /* 使能L1 Cache */    CPU_CACHE_Enable();    /*        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:       - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。       - 设置NVIV优先级分组为4。     */    HAL_Init();    /*        配置系统时钟到400MHz       - 切换使用HSE。       - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。    */    SystemClock_Config();    /*        Event Recorder:       - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。       - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章    */    #if Enable_EventRecorder == 1      /* 初始化EventRecorder并开启 */    EventRecorderInitialize(EventRecordAll, 1U);    EventRecorderStart();#endif        bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */    bsp_InitTimer();      /* 初始化滴答定时器 */    bsp_InitUart();    /* 初始化串口 */    bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */        bsp_InitLed();        /* 初始化LED */    }
复制代码

  MPU配置和Cache配置:
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。
  1. /***********************************************************************************************************    函 数 名: MPU_Config*    功能说明: 配置MPU*    形    参: 无*    返 回 值: 无**********************************************************************************************************/static void MPU_Config( void ){    MPU_Region_InitTypeDef MPU_InitStruct;    /* 禁止 MPU */    HAL_MPU_Disable();    /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;    MPU_InitStruct.BaseAddress      = 0x24000000;    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;    MPU_InitStruct.SubRegionDisable = 0x00;    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;    HAL_MPU_ConfigRegion(&MPU_InitStruct);            /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;    MPU_InitStruct.BaseAddress      = 0x60000000;    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;    MPU_InitStruct.SubRegionDisable = 0x00;    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;        HAL_MPU_ConfigRegion(&MPU_InitStruct);    /*使能 MPU */    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);}/***********************************************************************************************************    函 数 名: CPU_CACHE_Enable*    功能说明: 使能L1 Cache*    形    参: 无*    返 回 值: 无**********************************************************************************************************/static void CPU_CACHE_Enable(void){    /* 使能 I-Cache */    SCB_EnableICache();    /* 使能 D-Cache */    SCB_EnableDCache();}
复制代码

  主功能:
主程序实现如下操作:

  •   启动一个自动重装软件定时器,每100ms翻转一次LED2。
  •   按下按键K1, DSP求相反数运算。
  •   按下按键K2, DSP求偏移运算。
  •   按下按键K3, DSP求移位运算。
  •   按下摇杆OK键, DSP求减法运算。
  •   按下摇杆上键, DSP比例因子运算。
  1. /***********************************************************************************************************    函 数 名: main*    功能说明: c程序入口*    形    参:无*    返 回 值: 错误代码(无需处理)**********************************************************************************************************/int main(void){    uint8_t ucKeyCode;        /* 按键代码 */        bsp_Init();        /* 硬件初始化 */    PrintfLogo();    /* 打印例程信息到串口1 */    PrintfHelp();    /* 打印操作提示信息 */        bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */    /* 进入主程序循环体 */    while (1)    {        bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */        /* 判断定时器超时时间 */        if (bsp_CheckTimer(0))            {            /* 每隔100ms 进来一次 */              bsp_LedToggle(2);        }        ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */        if (ucKeyCode != KEY_NONE)        {            switch (ucKeyCode)            {                case KEY_DOWN_K1:            /* K1键按下,求相反数 */                    DSP_Negate();                    break;                case KEY_DOWN_K2:            /* K2键按下, 求偏移 */                    DSP_Offset();                    break;                case KEY_DOWN_K3:            /* K3键按下,求移位 */                    DSP_Shift();                    break;                    case JOY_DOWN_OK:            /* 摇杆OK键按下,求减法 */                    DSP_Sub();                    break;                                case JOY_DOWN_U:            /* 摇杆上键按下,求比例因子计算 */                    DSP_Scale();                    break;                default:                    /* 其他的键值不处理 */                    break;            }        }    }}
复制代码

12.9 实验例程说明(IAR)

配套例子:
V7-207_DSP基础运算(相反数,偏移,移位,减法和比例因子)
实验目的:

  • 学习基础运算(相反数,偏移,移位,减法和比例因子)
实验内容:

  • 启动一个自动重装软件定时器,每100ms翻转一次LED2。
  • 按下按键K1, DSP求相反数运算。
  • 按下按键K2, DSP求偏移运算。
  • 按下按键K3, DSP求移位运算。
  • 按下摇杆OK键, DSP求减法运算。
  • 按下摇杆上键, DSP比例因子运算。
使用AC6注意事项
特别注意附件章节C的问题
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,停止位 1。
详见本章的3.5,4.5,5.4和6.5小节。
程序设计:
  系统栈大小分配:
我的关键词 【STM32H7的DSP教程】第12章    DSP基础函数-相反数,偏移,移位,减法和比  新闻咨询 1379107-20200420153310358-1779250683

  RAM空间用的DTCM:
我的关键词 【STM32H7的DSP教程】第12章    DSP基础函数-相反数,偏移,移位,减法和比  新闻咨询 1379107-20200420153316765-1654303436

  硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
  1. /***********************************************************************************************************    函 数 名: bsp_Init*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次*    形    参:无*    返 回 值: 无**********************************************************************************************************/void bsp_Init(void){    /* 配置MPU */    MPU_Config();        /* 使能L1 Cache */    CPU_CACHE_Enable();    /*        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:       - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。       - 设置NVIV优先级分组为4。     */    HAL_Init();    /*        配置系统时钟到400MHz       - 切换使用HSE。       - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。    */    SystemClock_Config();    /*        Event Recorder:       - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。       - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章    */    #if Enable_EventRecorder == 1      /* 初始化EventRecorder并开启 */    EventRecorderInitialize(EventRecordAll, 1U);    EventRecorderStart();#endif        bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */    bsp_InitTimer();      /* 初始化滴答定时器 */    bsp_InitUart();    /* 初始化串口 */    bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */        bsp_InitLed();        /* 初始化LED */    }
复制代码

  MPU配置和Cache配置:
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区。
  1. /***********************************************************************************************************    函 数 名: MPU_Config*    功能说明: 配置MPU*    形    参: 无*    返 回 值: 无**********************************************************************************************************/static void MPU_Config( void ){    MPU_Region_InitTypeDef MPU_InitStruct;    /* 禁止 MPU */    HAL_MPU_Disable();    /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;    MPU_InitStruct.BaseAddress      = 0x24000000;    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;    MPU_InitStruct.SubRegionDisable = 0x00;    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;    HAL_MPU_ConfigRegion(&MPU_InitStruct);            /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;    MPU_InitStruct.BaseAddress      = 0x60000000;    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;    MPU_InitStruct.SubRegionDisable = 0x00;    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;        HAL_MPU_ConfigRegion(&MPU_InitStruct);    /*使能 MPU */    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);}/***********************************************************************************************************    函 数 名: CPU_CACHE_Enable*    功能说明: 使能L1 Cache*    形    参: 无*    返 回 值: 无**********************************************************************************************************/static void CPU_CACHE_Enable(void){    /* 使能 I-Cache */    SCB_EnableICache();    /* 使能 D-Cache */    SCB_EnableDCache();}
复制代码

  主功能:
主程序实现如下操作:

  •   启动一个自动重装软件定时器,每100ms翻转一次LED2。
  •   按下按键K1, DSP求相反数运算。
  •   按下按键K2, DSP求偏移运算。
  •   按下按键K3, DSP求移位运算。
  •   按下摇杆OK键, DSP求减法运算。
  •   按下摇杆上键, DSP比例因子运算。
  1. /***********************************************************************************************************    函 数 名: main*    功能说明: c程序入口*    形    参:无*    返 回 值: 错误代码(无需处理)**********************************************************************************************************/int main(void){    uint8_t ucKeyCode;        /* 按键代码 */        bsp_Init();        /* 硬件初始化 */    PrintfLogo();    /* 打印例程信息到串口1 */    PrintfHelp();    /* 打印操作提示信息 */        bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */    /* 进入主程序循环体 */    while (1)    {        bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */        /* 判断定时器超时时间 */        if (bsp_CheckTimer(0))            {            /* 每隔100ms 进来一次 */              bsp_LedToggle(2);        }        ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */        if (ucKeyCode != KEY_NONE)        {            switch (ucKeyCode)            {                case KEY_DOWN_K1:            /* K1键按下,求相反数 */                    DSP_Negate();                    break;                case KEY_DOWN_K2:            /* K2键按下, 求偏移 */                    DSP_Offset();                    break;                case KEY_DOWN_K3:            /* K3键按下,求移位 */                    DSP_Shift();                    break;                    case JOY_DOWN_OK:            /* 摇杆OK键按下,求减法 */                    DSP_Sub();                    break;                                case JOY_DOWN_U:            /* 摇杆上键按下,求比例因子计算 */                    DSP_Scale();                    break;                default:                    /* 其他的键值不处理 */                    break;            }        }    }}
复制代码

12.10   总结

DSP基础函数就跟大家讲这么多,希望初学的同学多多的联系,并在自己以后的项目中多多使用,效果必将事半功倍。



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

技术支持:迪恩网络科技公司  Powered by Discuz! X3.2
快速回复 返回顶部 返回列表