【STM32H7的DSP教程】第11章 DSP基础函数-绝对值,求和,乘法和点乘

article/2025/10/7 14:27:04

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

第11章       DSP基础函数-绝对值,求和,乘法和点乘

本期教程开始学习ARM官方的DSP库,这里我们先从基本数学函数开始。本期教程主要讲绝对值,加法,点乘和乘法四种运算。

目录

第11章       DSP基础函数-绝对值,求和,乘法和点乘

11.1 初学者重要提示

11.2 DSP基础运算指令

11.3 绝对值(Vector Absolute Value)

11.3.1        函数arm_abs_f32

11.3.2        函数arm_abs_q31

11.3.3        函数arm_abs_q15

11.3.4        函数arm_abs_q7

11.3.5        使用举例

11.4 求和(Vector Addition)

11.4.1        函数arm_add_f32

11.4.2        函数arm_add_q31

11.4.3        函数arm_add_q15

11.4.4        函数arm_add_q7

11.4.5        使用举例

11.5 点乘(Vector Dot Product)

11.5.1        函数arm_dot_prod_f32

11.5.2        函数arm_dot_prod_q31

11.5.3        函数arm_dot_prod_q15

11.5.4        函数arm_dot_prod_q7

11.5.5        使用举例

11.6 乘法(Vector Multiplication)

11.6.1        函数arm_mult_f32

11.6.2        函数arm_mult_q31

11.6.3        函数arm_mult_q15

11.6.4        函数arm_mult_q7

11.6.5        使用举例

11.7 实验例程说明(MDK)

11.8 实验例程说明(IAR)

11.9 总结


 

11.1 初学者重要提示

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

  1.   基本所有的函数都是可重入的。
  2.   大部分函数都支持批量计算,比如求绝对值函数arm_abs_f32。所以如果只是就几个数的绝对值,用这个库函数就没有什么优势了。
  3.   库函数基本是CM0,CM0+,CM3,CM4和CM7内核都是支持的,不限制厂家。
  4.   每组数据基本上都是以4个数为一个单位进行计算,不够四个再单独计算。大部分函数都是配有f32,Q31,Q15和Q7四种格式。
  5.   为什么定点DSP运算输出的时候容易出现结果为0的情况:http://www.armbbs.cn/forum.php?mod=viewthread&tid=95194 。

11.2 DSP基础运算指令

本章用到基础运算指令:

  •   绝对值函数用到QSUB,QSUB16和QSUB8。
  •   求和函数用到QADD,QADD16和QADD8。
  •   点乘函数用到SMLALD和SMLAD。
  •   乘法用到__PKHBT和__SSAT。

用到的这几个指令,在本章讲解具体函数时都有专门的讲解说明。这里重点说一下饱和运算的问题,字母Q打头的指令是饱和运算指令,饱和的意思超过所能表示的数值范围时,将直接取最大值,比如QSUB16减法指令,如果是正数,那么最大值是0x7FFF(32767),大于这个值将直接取0x7FFF,如果是负数,那么最小值是0x8000(-32768),比这个值还小将直接取值0x8000。

反应到实际应用中就是下面这种效果:

 

11.3 绝对值(Vector Absolute Value)

这部分函数主要用于求绝对值,公式描述如下:

pDst[n] = abs(pSrc[n]),   0 <= n < blockSize.

特别注意,这部分函数支持目标指针和源指针指向相同的缓冲区。

11.3.1        函数arm_abs_f32

函数原型:

1.    void arm_abs_f32(
2.      const float32_t * pSrc,
3.            float32_t * pDst,
4.            uint32_t blockSize)
5.    {
6.            uint32_t blkCnt;                               /* Loop counter */
7.    
8.    #if defined(ARM_MATH_NEON)
9.        float32x4_t vec1;
10.        float32x4_t res;
11.    
12.        /* Compute 4 outputs at a time */
13.        blkCnt = blockSize >> 2U;
14.    
15.        while (blkCnt > 0U)
16.        {
17.            /* C = |A| */
18.    
19.            /* Calculate absolute values and then store the results in the destination buffer. */
20.            vec1 = vld1q_f32(pSrc);
21.            res = vabsq_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.    #else
36.    #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.        /* Calculate absolute and store result in destination buffer. */
46.        *pDst++ = fabsf(*pSrc++);
47.    
48.        *pDst++ = fabsf(*pSrc++);
49.    
50.        *pDst++ = fabsf(*pSrc++);
51.    
52.        *pDst++ = fabsf(*pSrc++);
53.    
54.        /* Decrement loop counter */
55.        blkCnt--;
56.      }
57.    
58.      /* Loop unrolling: Compute remaining outputs */
59.      blkCnt = blockSize % 0x4U;
60.    
61.    #else
62.    
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) */
68.    
69.      while (blkCnt > 0U)
70.      {
71.        /* C = |A| */
72.    
73.        /* Calculate absolute and store result in destination buffer. */
74.        *pDst++ = fabsf(*pSrc++);
75.    
76.        /* Decrement loop counter */
77.        blkCnt--;
78.      }
79.    
80.    }

 

函数描述:

这个函数用于求32位浮点数的绝对值。

函数解析:

  •   第8到35行,用于NEON指令集,当前的CM内核不支持。
  •   第36到66行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   函数fabsf不是用Cortex-M内核支持的DSP指令实现的,而是用C库函数实现的,这个函数是被MDK封装了起来。
  •  第69到78行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。

函数参数:

  •   第1个参数是原数据地址。
  •   第2个参数是求绝对值后目的数据地址。
  •   第3个参数转换的数据个数,这里是指的浮点数个数。

函数描述:

函数形参的源地址和目的地址可以使用同一个缓冲。

11.3.2        函数arm_abs_q31

函数原型:

1.    void arm_abs_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 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.        /* Calculate absolute of input (if -1 then saturated to 0x7fffffff) and store result in destination
19.          buffer. */
20.        in = *pSrc++;
21.    #if defined (ARM_MATH_DSP)
22.        *pDst++ = (in > 0) ? in : (q31_t)__QSUB(0, in);
23.    #else
24.        *pDst++ = (in > 0) ? in : ((in == INT32_MIN) ? INT32_MAX : -in);
25.    #endif
26.    
27.        in = *pSrc++;
28.    #if defined (ARM_MATH_DSP)
29.        *pDst++ = (in > 0) ? in : (q31_t)__QSUB(0, in);
30.    #else
31.        *pDst++ = (in > 0) ? in : ((in == INT32_MIN) ? INT32_MAX : -in);
32.    #endif
33.    
34.        in = *pSrc++;
35.    #if defined (ARM_MATH_DSP)
36.        *pDst++ = (in > 0) ? in : (q31_t)__QSUB(0, in);
37.    #else
38.        *pDst++ = (in > 0) ? in : ((in == INT32_MIN) ? INT32_MAX : -in);
39.    #endif
40.    
41.        in = *pSrc++;
42.    #if defined (ARM_MATH_DSP)
43.        *pDst++ = (in > 0) ? in : (q31_t)__QSUB(0, in);
44.    #else
45.        *pDst++ = (in > 0) ? in : ((in == INT32_MIN) ? INT32_MAX : -in);
46.    #endif
47.    
48.        /* Decrement loop counter */
49.        blkCnt--;
50.      }
51.    
52.      /* Loop unrolling: Compute remaining outputs */
53.      blkCnt = blockSize % 0x4U;
54.    
55.    #else
56.    
57.      /* Initialize blkCnt with number of samples */
58.      blkCnt = blockSize;
59.    
60.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */
61.    
62.      while (blkCnt > 0U)
63.      {
64.        /* C = |A| */
65.    
66.        /* Calculate absolute of input (if -1 then saturated to 0x7fffffff) and store result in destination
67.           buffer. */
68.        in = *pSrc++;
69.    #if defined (ARM_MATH_DSP)
70.        *pDst++ = (in > 0) ? in : (q31_t)__QSUB(0, in);
71.    #else
72.        *pDst++ = (in > 0) ? in : ((in == INT32_MIN) ? INT32_MAX : -in);
73.    #endif
74.    
75.        /* Decrement loop counter */
76.        blkCnt--;
77.      }
78.    
79.    }

 

函数描述:

用于求32位定点数的绝对值。

函数解析:

  •   第9到60行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第69到78行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •  这个函数使用了饱和运算,其实不光这个函数,后面很多函数都是使用了饱和运算的,关于什么是饱和运算,大家看Cortex-M3权威指南中文版的4.3.6 小节:汇编语言:饱和运算即可。
  •   对于Q31格式的数据,饱和运算会使得数据0x80000000变成0x7fffffff(这个数比较特殊,算是特殊处理,记住即可)。
  •   这里重点说一下函数__QSUB,其实这个函数算是Cortex-M7,M4/M3的一个指令,用于实现饱和减法。比如函数:__QSUB(0, in1) 的作用就是实现0 – in1并返回结果。这里__QSUB实现的是32位数的饱和减法。还有__QSUB16和__QSUB8实现的是16位和8位数的减法。

函数参数:

  •   第1个参数是原数据地址。
  •   第2个参数是求绝对值后目的数据地址。
  •   第3个参数转换的数据个数,这里是指的定点数个数。

函数描述:

函数形参的源地址和目的地址可以使用同一个缓冲。

11.3.3        函数arm_abs_q15

函数原型:

1.    void arm_abs_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.      /* Loop unrolling: Compute 4 outputs at a time */
12.      blkCnt = blockSize >> 2U;
13.    
14.      while (blkCnt > 0U)
15.      {
16.        /* C = |A| */
17.    
18.        /* Calculate absolute of input (if -1 then saturated to 0x7fff) and store result in destination buffer.
19.         */
20.        in = *pSrc++;
21.    #if defined (ARM_MATH_DSP)
22.        *pDst++ = (in > 0) ? in : (q15_t)__QSUB16(0, in);
23.    #else
24.        *pDst++ = (in > 0) ? in : ((in == (q15_t) 0x8000) ? 0x7fff : -in);
25.    #endif
26.    
27.        in = *pSrc++;
28.    #if defined (ARM_MATH_DSP)
29.        *pDst++ = (in > 0) ? in : (q15_t)__QSUB16(0, in);
30.    #else
31.        *pDst++ = (in > 0) ? in : ((in == (q15_t) 0x8000) ? 0x7fff : -in);
32.    #endif
33.    
34.        in = *pSrc++;
35.    #if defined (ARM_MATH_DSP)
36.        *pDst++ = (in > 0) ? in : (q15_t)__QSUB16(0, in);
37.    #else
38.        *pDst++ = (in > 0) ? in : ((in == (q15_t) 0x8000) ? 0x7fff : -in);
39.    #endif
40.    
41.        in = *pSrc++;
42.    #if defined (ARM_MATH_DSP)
43.        *pDst++ = (in > 0) ? in : (q15_t)__QSUB16(0, in);
44.    #else
45.        *pDst++ = (in > 0) ? in : ((in == (q15_t) 0x8000) ? 0x7fff : -in);
46.    #endif
47.    
48.        /* Decrement loop counter */
49.        blkCnt--;
50.      }
51.    
52.      /* Loop unrolling: Compute remaining outputs */
53.      blkCnt = blockSize % 0x4U;
54.    
55.    #else
56.    
57.      /* Initialize blkCnt with number of samples */
58.      blkCnt = blockSize;
59.    
60.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */
61.    
62.      while (blkCnt > 0U)
63.      {
64.        /* C = |A| */
65.    
66.        /* Calculate absolute of input (if -1 then saturated to 0x7fff) and store result in destination buffer.
67.         */
68.        in = *pSrc++;
69.    #if defined (ARM_MATH_DSP)
70.        *pDst++ = (in > 0) ? in : (q15_t)__QSUB16(0, in);
71.    #else
72.        *pDst++ = (in > 0) ? in : ((in == (q15_t) 0x8000) ? 0x7fff : -in);
73.    #endif
74.    
75.        /* Decrement loop counter */
76.        blkCnt--;
77.      }
78.    
79.    }

 

函数描述:

用于求16位定点数的绝对值。

函数解析:

  •   第9到55行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第62到77行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   对于Q15格式的数据,饱和运算会使得数据0x8000变成0x7fff。
  •   __QSUB16用于实现16位数据的饱和减法。

函数参数:

  •   第1个参数是原数据地址。
  •   第2个参数是求绝对值后目的数据地址。
  •   第3个参数转换的数据个数,这里是指的定点数个数。

函数描述:

函数形参的源地址和目的地址可以使用同一个缓冲。

11.3.4        函数arm_abs_q7

函数原型:

1.    void arm_abs_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.      /* Loop unrolling: Compute 4 outputs at a time */
12.      blkCnt = blockSize >> 2U;
13.    
14.      while (blkCnt > 0U)
15.      {
16.        /* C = |A| */
17.    
18.        /* Calculate absolute of input (if -1 then saturated to 0x7f) and store result in destination buffer.
19.         */
20.        in = *pSrc++;
21.    #if defined (ARM_MATH_DSP)
22.        *pDst++ = (in > 0) ? in : (q7_t)__QSUB(0, in);
23.    #else
24.        *pDst++ = (in > 0) ? in : ((in == (q7_t) 0x80) ? (q7_t) 0x7f : -in);
25.    #endif
26.    
27.        in = *pSrc++;
28.    #if defined (ARM_MATH_DSP)
29.        *pDst++ = (in > 0) ? in : (q7_t)__QSUB(0, in);
30.    #else
31.        *pDst++ = (in > 0) ? in : ((in == (q7_t) 0x80) ? (q7_t) 0x7f : -in);
32.    #endif
33.    
34.        in = *pSrc++;
35.    #if defined (ARM_MATH_DSP)
36.        *pDst++ = (in > 0) ? in : (q7_t)__QSUB(0, in);
37.    #else
38.        *pDst++ = (in > 0) ? in : ((in == (q7_t) 0x80) ? (q7_t) 0x7f : -in);
39.    #endif
40.    
41.        in = *pSrc++;ezi le mexia 
42.    #if defined (ARM_MATH_DSP)
43.        *pDst++ = (in > 0) ? in : (q7_t)__QSUB(0, in);
44.    #else
45.        *pDst++ = (in > 0) ? in : ((in == (q7_t) 0x80) ? (q7_t) 0x7f : -in);
46.    #endif
47.    
48.        /* Decrement loop counter */
49.        blkCnt--;
50.      }
51.    
52.      /* Loop unrolling: Compute remaining outputs */
53.      blkCnt = blockSize % 0x4U;
54.    
55.    #else
56.    
57.      /* Initialize blkCnt with number of samples */
58.      blkCnt = blockSize;
59.    
60.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */
61.    
62.      while (blkCnt > 0U)
63.      {
64.        /* C = |A| */
65.    
66.        /* Calculate absolute of input (if -1 then saturated to 0x7f) and store result in destination buffer.
67.         */
68.        in = *pSrc++;
69.    #if defined (ARM_MATH_DSP)
70.        *pDst++ = (in > 0) ? in : (q7_t) __QSUB(0, in);
71.    #else
72.        *pDst++ = (in > 0) ? in : ((in == (q7_t) 0x80) ? (q7_t) 0x7f : -in);
73.    #endif
74.    
75.        /* Decrement loop counter */
76.        blkCnt--;
77.      }
78.    
79.    }

 

函数描述:

用于求8位定点数的绝对值。

函数解析:

  •   第9到55行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第62到77行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   对于Q7格式的数据,饱和运算会使得数据0x80变成0x7f。
  •   __QSUB用于实现32位数据的饱和减法。而当前的DSP库版本却将其用到了Q7函数中,导致0x80的饱和出错。详情看此贴:http://www.armbbs.cn/forum.php?mod=viewthread&tid=95152 。

函数参数:

  •   第1个参数是原数据地址。
  •   第2个参数是求绝对值后目的数据地址。
  •   第3个参数转换的数据个数,这里是指的定点数个数。

函数描述:

函数形参的源地址和目的地址可以使用同一个缓冲。

11.3.5        使用举例

程序设计:

/*
*********************************************************************************************************
*    函 数 名: DSP_ABS
*    功能说明: 求绝对值
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void DSP_ABS(void)
{float32_t pSrc;float32_t pDst;q31_t pSrc1;q31_t pDst1;q15_t pSrc2;q15_t pDst2;q7_t pSrc3; q7_t pDst3;/*求绝对值*********************************/pSrc -= 1.23f;arm_abs_f32(&pSrc, &pDst, 1);printf("arm_abs_f32 = %f\r\n", pDst);pSrc1 -= 1;arm_abs_q31(&pSrc1, &pDst1, 1);printf("arm_abs_q31 = %d\r\n", pDst1);pSrc2 = -32768;arm_abs_q15(&pSrc2, &pDst2, 1);printf("arm_abs_q15 = %d\r\n", pDst2);pSrc3 = 127; arm_abs_q7(&pSrc3, &pDst3, 1);printf("arm_abs_q7 = %d\r\n", pDst3);printf("***********************************\r\n");
}

 

实验现象:

这里特别注意Q15的计算,数值-32768被饱和处理到32767,即0 - (-32768)= 32768,超出了正数所能表示的最大值,经过饱和后,输出为32767。

11.4 求和(Vector Addition)

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

pDst[n] = pSrcA[n] + pSrcB[n],   0 <= n < blockSize.

11.4.1        函数arm_add_f32

函数原型:

1.    void arm_add_f32(
2.      const float32_t * pSrcA,
3.      const float32_t * pSrcB,
4.            float32_t * pDst,
5.            uint32_t blockSize)
6.    {
7.            uint32_t blkCnt;                               /* Loop counter */
8.    
9.    #if defined(ARM_MATH_NEON)
10.        float32x4_t vec1;
11.        float32x4_t vec2;
12.        float32x4_t res;
13.    
14.        /* Compute 4 outputs at a time */
15.        blkCnt = blockSize >> 2U;
16.    
17.        while (blkCnt > 0U)
18.        {
19.            /* C = A + B */
20.    
21.            /* Add and then store the results in the destination buffer. */
22.            vec1 = vld1q_f32(pSrcA);
23.            vec2 = vld1q_f32(pSrcB);
24.            res = vaddq_f32(vec1, vec2);
25.            vst1q_f32(pDst, res);
26.    
27.            /* Increment pointers */
28.            pSrcA += 4;
29.            pSrcB += 4; 
30.            pDst += 4;
31.            
32.            /* Decrement the loop counter */
33.            blkCnt--;
34.        }
35.    
36.        /* Tail */
37.        blkCnt = blockSize & 0x3;
38.    
39.    #else
40.    #if defined (ARM_MATH_LOOPUNROLL)
41.    
42.      /* Loop unrolling: Compute 4 outputs at a time */
43.      blkCnt = blockSize >> 2U;
44.    
45.      while (blkCnt > 0U)
46.      {
47.        /* C = A + B */
48.    
49.        /* Add and store result in destination buffer. */
50.        *pDst++ = (*pSrcA++) + (*pSrcB++);
51.        *pDst++ = (*pSrcA++) + (*pSrcB++);
52.        *pDst++ = (*pSrcA++) + (*pSrcB++);
53.        *pDst++ = (*pSrcA++) + (*pSrcB++);
54.    
55.        /* Decrement loop counter */
56.        blkCnt--;
57.      }
58.    
59.      /* Loop unrolling: Compute remaining outputs */
60.      blkCnt = blockSize % 0x4U;
61.    
62.    #else
63.    
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) */
69.    
70.      while (blkCnt > 0U)
71.      {
72.        /* C = A + B */
73.    
74.        /* Add and store result in destination buffer. */
75.        *pDst++ = (*pSrcA++) + (*pSrcB++);
76.    
77.        /* Decrement loop counter */
78.        blkCnt--;
79.      }
80.    
81.    }

 

函数描述:

这个函数用于求两个32位浮点数的和。

函数解析:

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

函数参数:

  •   第1个参数是加数地址。
  •   第2个参数是被加数地址。
  •   第3个参数是和地址。
  •   第4个参数是浮点数个数,其实就是执行加法的次数。

11.4.2        函数arm_add_q31

函数原型:

1.    void arm_add_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.        /* Add and store result in destination buffer. */
19.        *pDst++ = __QADD(*pSrcA++, *pSrcB++);
20.    
21.        *pDst++ = __QADD(*pSrcA++, *pSrcB++);
22.    
23.        *pDst++ = __QADD(*pSrcA++, *pSrcB++);
24.    
25.        *pDst++ = __QADD(*pSrcA++, *pSrcB++);
26.    
27.        /* Decrement loop counter */
28.        blkCnt--;
29.      }
30.    
31.      /* Loop unrolling: Compute remaining outputs */
32.      blkCnt = blockSize % 0x4U;
33.    
34.    #else
35.    
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.        /* Add and store result in destination buffer. */
46.        *pDst++ = __QADD(*pSrcA++, *pSrcB++);
47.    
48.        /* Decrement loop counter */
49.        blkCnt--;
50.      }
51.    
52.    }

 

函数描述:

这个函数用于求两个32位定点数的和。

函数解析:

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

函数参数:

  •   第1个参数是加数地址。
  •   第2个参数是被加数地址。
  •   第3个参数是和地址。
  •   第4个参数是定点数个数,其实就是执行加法的次数。

11.4.3        函数arm_add_q15

函数原型:

1.    void arm_add_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.    #endif
15.    
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.        /* Add and store 2 times 2 samples at a time */
32.        write_q15x2_ia (&pDst, __QADD16(inA1, inB1));
33.        write_q15x2_ia (&pDst, __QADD16(inA2, inB2));
34.    #else
35.        *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.    #endif
40.    
41.        /* Decrement loop counter */
42.        blkCnt--;
43.      }
44.    
45.      /* Loop unrolling: Compute remaining outputs */
46.      blkCnt = blockSize % 0x4U;
47.    
48.    #else
49.    
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.        /* Add and store result in destination buffer. */
60.    #if defined (ARM_MATH_DSP)
61.        *pDst++ = (q15_t) __QADD16(*pSrcA++, *pSrcB++);
62.    #else
63.        *pDst++ = (q15_t) __SSAT(((q31_t) *pSrcA++ + *pSrcB++), 16);
64.    #endif
65.    
66.        /* Decrement loop counter */
67.        blkCnt--;
68.      }
69.    
70.    }

 

函数描述:

这个函数用于求两个16位定点数的和。

函数解析:

  •   第23到34行,对于M4和M7带DSP单元的芯片使用。
  •   第35到38行,对于不带DSP单元的M0,M0+和M3使用。
  •   第55到68行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   函数read_q15x2_ia的原型如下:
__STATIC_FORCEINLINE q31_t read_q15x2_ia (q15_t ** pQ15)
{q31_t val;memcpy (&val, *pQ15, 4);*pQ15 += 2;return (val);
}

 

作用是读取两次16位数据,返回一个32位数据,并将数据地址递增,方便下次读取。

  •   __QADD16实现两次16位数的加法饱和运算。输出结果的范围[0x8000 0x7FFF],超出这个结果将产生饱和结果,负数饱和到0x8000,正数饱和到0x7FFF。
  •   __SSAT也是SIMD指令,这里是将结果饱和到16位精度。

函数参数:

  •   第1个参数是加数地址。
  •   第2个参数是被加数地址。
  •   第3个参数是和地址。
  •   第4个参数是定点数个数,其实就是执行加法的次数。

11.4.4        函数arm_add_q7

函数原型:

1.    void arm_add_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.        /* Add and store result in destination buffer (4 samples at a time). */
20.        write_q7x4_ia (&pDst, __QADD8 (read_q7x4_ia ((q7_t **) &pSrcA), read_q7x4_ia ((q7_t **) &pSrcB)));
21.    #else
22.        *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.    #endif
27.    
28.        /* Decrement loop counter */
29.        blkCnt--;
30.      }
31.    
32.      /* Loop unrolling: Compute remaining outputs */
33.      blkCnt = blockSize % 0x4U;
34.    
35.    #else
36.    
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.        /* Add 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位定点数的和。

函数解析:

  •   第18到21行,对于M4和M7带DSP单元的芯片使用。
  •   第22到25行,对于不带DSP单元的M0,M0+和M3使用。
  •   第42到51行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   函数read_q15x2_ia的原型如下:
__STATIC_FORCEINLINE void write_q7x4_ia (q7_t ** pQ7,q31_t   value)
{q31_t val = value;memcpy (*pQ7, &val, 4);*pQ7 += 4;
}

 

作用是读取4次8位数据,返回一个32位数据,并将数据地址递增,方便下次读取。

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

函数参数:

  •   第1个参数是加数地址。
  •   第2个参数是被加数地址。
  •   第3个参数是和地址。
  •   第4个参数是定点数个数,其实就是执行加法的次数。

11.4.5        使用举例

程序设计:

/*
*********************************************************************************************************
*    函 数 名: DSP_Add
*    功能说明: 加法
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void DSP_Add(void)
{float32_t   pSrcA;float32_t   pSrcB;  float32_t   pDst;  q31_t  pSrcA1;  q31_t  pSrcB1;  q31_t  pDst1;  q15_t  pSrcA2;  q15_t  pSrcB2;  q15_t  pDst2; q7_t  pSrcA3; q7_t  pSrcB3;  q7_t  pDst3;  /*求和*********************************/pSrcA = 0.1f;pSrcB = 0.2f;arm_add_f32(&pSrcA, &pSrcB, &pDst, 1);printf("arm_add_f32 = %f\r\n", pDst);pSrcA1 = 1;pSrcB1 = 1;arm_add_q31(&pSrcA1, &pSrcB1, &pDst1, 1);printf("arm_add_q31 = %d\r\n", pDst1);pSrcA2 = 2;pSrcB2 = 2;arm_add_q15(&pSrcA2, &pSrcB2, &pDst2, 1);printf("arm_add_q15 = %d\r\n", pDst2);pSrcA3 = 30;pSrcB3 = 120;arm_add_q7(&pSrcA3, &pSrcB3, &pDst3, 1);printf("arm_add_q7 = %d\r\n", pDst3);printf("***********************************\r\n");
}

 

实验现象:

这里特别注意Q7的计算处理,30+120已经超出了Q7所能表示的最大值127,经过饱和处理后,经过饱和后,输出为127。

11.5 点乘(Vector Dot Product)

这部分函数主要用于点乘,公式描述如下:

sum = pSrcA[0]*pSrcB[0] + pSrcA[1]*pSrcB[1] + ... + pSrcA[blockSize-1]*pSrcB[blockSize-1]

11.5.1        函数arm_dot_prod_f32

函数原型:

1.    void arm_dot_prod_f32(
2.      const float32_t * pSrcA,
3.      const float32_t * pSrcB,
4.            uint32_t blockSize,
5.            float32_t * result)
6.    {
7.            uint32_t blkCnt;                               /* Loop counter */
8.            float32_t sum = 0.0f;                          /* Temporary return variable */
9.    
10.    #if defined(ARM_MATH_NEON)
11.        float32x4_t vec1;
12.        float32x4_t vec2;
13.        float32x4_t res;
14.        float32x4_t accum = vdupq_n_f32(0);    
15.    
16.        /* Compute 4 outputs at a time */
17.        blkCnt = blockSize >> 2U;
18.    
19.        vec1 = vld1q_f32(pSrcA);
20.        vec2 = vld1q_f32(pSrcB);
21.    
22.        while (blkCnt > 0U)
23.        {
24.            /* C = A[0]*B[0] + A[1]*B[1] + A[2]*B[2] + ... + A[blockSize-1]*B[blockSize-1] */
25.            /* Calculate dot product and then store the result in a temporary buffer. */
26.            
27.        accum = vmlaq_f32(accum, vec1, vec2);
28.        
29.            /* Increment pointers */
30.            pSrcA += 4;
31.            pSrcB += 4; 
32.    
33.            vec1 = vld1q_f32(pSrcA);
34.            vec2 = vld1q_f32(pSrcB);
35.            
36.            /* Decrement the loop counter */
37.            blkCnt--;
38.        }
39.        
40.    #if __aarch64__
41.        sum = vpadds_f32(vpadd_f32(vget_low_f32(accum), vget_high_f32(accum)));
42.    #else
43.        sum = (vpadd_f32(vget_low_f32(accum), vget_high_f32(accum)))[0] + (vpadd_f32(vget_low_f32(accum), 
44.              vget_high_f32(accum)))[1];
45.    #endif    
46.    
47.        /* Tail */
48.        blkCnt = blockSize & 0x3;
49.    
50.    #else
51.    #if defined (ARM_MATH_LOOPUNROLL)
52.    
53.      /* Loop unrolling: Compute 4 outputs at a time */
54.      blkCnt = blockSize >> 2U;
55.    
56.      /* First part of the processing with loop unrolling. Compute 4 outputs at a time.
57.       ** a second loop below computes the remaining 1 to 3 samples. */
58.      while (blkCnt > 0U)
59.      {
60.        /* C = A[0]* B[0] + A[1]* B[1] + A[2]* B[2] + .....+ A[blockSize-1]* B[blockSize-1] */
61.    
62.        /* Calculate dot product and store result in a temporary buffer. */
63.        sum += (*pSrcA++) * (*pSrcB++);
64.    
65.        sum += (*pSrcA++) * (*pSrcB++);
66.    
67.        sum += (*pSrcA++) * (*pSrcB++);
68.    
69.        sum += (*pSrcA++) * (*pSrcB++);
70.    
71.        /* Decrement loop counter */
72.        blkCnt--;
73.      }
74.    
75.      /* Loop unrolling: Compute remaining outputs */
76.      blkCnt = blockSize % 0x4U;
77.    
78.    #else
79.    
80.      /* Initialize blkCnt with number of samples */
81.      blkCnt = blockSize;
82.    
83.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */
84.    #endif /* #if defined(ARM_MATH_NEON) */
85.    
86.      while (blkCnt > 0U)
87.      {
88.        /* C = A[0]* B[0] + A[1]* B[1] + A[2]* B[2] + .....+ A[blockSize-1]* B[blockSize-1] */
89.    
90.        /* Calculate dot product and store result in a temporary buffer. */
91.        sum += (*pSrcA++) * (*pSrcB++);
92.    
93.        /* Decrement loop counter */
94.        blkCnt--;
95.      }
96.    
97.      /* Store result in destination buffer */
98.      *result = sum;
99.    }

 

函数描述:

这个函数用于求32位浮点数的点乘。

函数解析:

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

函数参数:

  •   第1个参数是乘数地址。
  •   第2个参数是被乘数地址。
  •   第3个参数是浮点数个数,其实就是执行点乘的次数。
  •   第4个参数是结果地址。

11.5.2        函数arm_dot_prod_q31

函数原型:

1.    void arm_dot_prod_q31(
2.      const q31_t * pSrcA,
3.      const q31_t * pSrcB,
4.            uint32_t blockSize,
5.            q63_t * result)
6.    {
7.            uint32_t blkCnt;                               /* Loop counter */
8.            q63_t sum = 0;                                 /* Temporary return variable */
9.    
10.    #if defined (ARM_MATH_LOOPUNROLL)
11.    
12.      /* Loop unrolling: Compute 4 outputs at a time */
13.      blkCnt = blockSize >> 2U;
14.    
15.      while (blkCnt > 0U)
16.      {
17.        /* C = A[0]* B[0] + A[1]* B[1] + A[2]* B[2] + .....+ A[blockSize-1]* B[blockSize-1] */
18.    
19.        /* Calculate dot product and store result in a temporary buffer. */
20.        sum += ((q63_t) *pSrcA++ * *pSrcB++) >> 14U;
21.    
22.        sum += ((q63_t) *pSrcA++ * *pSrcB++) >> 14U;
23.    
24.        sum += ((q63_t) *pSrcA++ * *pSrcB++) >> 14U;
25.    
26.        sum += ((q63_t) *pSrcA++ * *pSrcB++) >> 14U;
27.    
28.        /* Decrement loop counter */
29.        blkCnt--;
30.      }
31.    
32.      /* Loop unrolling: Compute remaining outputs */
33.      blkCnt = blockSize % 0x4U;
34.    
35.    #else
36.    
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[0]* B[0] + A[1]* B[1] + A[2]* B[2] + .....+ A[blockSize-1]* B[blockSize-1] */
45.    
46.        /* Calculate dot product and store result in a temporary buffer. */
47.        sum += ((q63_t) *pSrcA++ * *pSrcB++) >> 14U;
48.    
49.        /* Decrement loop counter */
50.        blkCnt--;
51.      }
52.    
53.      /* Store result in destination buffer in 16.48 format */
54.      *result = sum;
55.    }

 

函数描述:

这个函数用于求32位定点数的点乘。

函数解析:

  •   第10到35行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第42到51行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   两个Q31格式的32位数相乘,那么输出结果的格式是1.31*1.31 = 2.62。实际应用中基本不需要这么高的精度,这个函数将低14位的数据截取掉,反应在函数中就是两个数的乘积左移14位,也就是定点数的小数点也左移14位,那么最终的结果的格式是16.48。所以只要乘累加的个数小于2^16就没有输出结果溢出的危险。

函数参数:

  •   第1个参数是乘数地址。
  •   第2个参数是被乘数地址。
  •   第3个参数是定点数个数,其实就是执行点乘的次数。
  •   第4个参数是结果地址。

11.5.3        函数arm_dot_prod_q15

函数原型:

1.    void arm_dot_prod_q15(
2.      const q15_t * pSrcA,
3.      const q15_t * pSrcB,
4.            uint32_t blockSize,
5.            q63_t * result)
6.    {
7.            uint32_t blkCnt;                               /* Loop counter */
8.            q63_t sum = 0;                                 /* Temporary return variable */
9.    
10.    #if defined (ARM_MATH_LOOPUNROLL)
11.    
12.      /* Loop unrolling: Compute 4 outputs at a time */
13.      blkCnt = blockSize >> 2U;
14.    
15.      while (blkCnt > 0U)
16.      {
17.        /* C = A[0]* B[0] + A[1]* B[1] + A[2]* B[2] + .....+ A[blockSize-1]* B[blockSize-1] */
18.    
19.    #if defined (ARM_MATH_DSP)
20.        /* Calculate dot product and store result in a temporary buffer. */
21.        sum = __SMLALD(read_q15x2_ia ((q15_t **) &pSrcA), read_q15x2_ia ((q15_t **) &pSrcB), sum);
22.        sum = __SMLALD(read_q15x2_ia ((q15_t **) &pSrcA), read_q15x2_ia ((q15_t **) &pSrcB), sum);
23.    #else
24.        sum += (q63_t)((q31_t) *pSrcA++ * *pSrcB++);
25.        sum += (q63_t)((q31_t) *pSrcA++ * *pSrcB++);
26.        sum += (q63_t)((q31_t) *pSrcA++ * *pSrcB++);
27.        sum += (q63_t)((q31_t) *pSrcA++ * *pSrcB++);
28.    #endif
29.    
30.        /* Decrement loop counter */
31.        blkCnt--;
32.      }
33.    
34.      /* Loop unrolling: Compute remaining outputs */
35.      blkCnt = blockSize % 0x4U;
36.    
37.    #else
38.    
39.      /* Initialize blkCnt with number of samples */
40.      blkCnt = blockSize;
41.    
42.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */
43.    
44.      while (blkCnt > 0U)
45.      {
46.        /* C = A[0]* B[0] + A[1]* B[1] + A[2]* B[2] + .....+ A[blockSize-1]* B[blockSize-1] */
47.    
48.        /* Calculate dot product and store result in a temporary buffer. */
49.    //#if defined (ARM_MATH_DSP)
50.    //    sum  = __SMLALD(*pSrcA++, *pSrcB++, sum);
51.    //#else
52.        sum += (q63_t)((q31_t) *pSrcA++ * *pSrcB++);
53.    //#endif
54.    
55.        /* Decrement loop counter */
56.        blkCnt--;
57.      }
58.    
59.      /* Store result in destination buffer in 34.30 format */
60.      *result = sum;
61.    }

 

函数描述:

这个函数用于求32位定点数的点乘。

函数解析:

  •   第10到37行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第44到57行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   两个Q15格式的数据相乘,那么输出结果的格式是1.15*1.15 = 2.30,这个函数将输出结果赋值给了64位变量,那么输出结果就是34.30格式。所以基本没有溢出的危险。
  •   __SMLALD也是SIMD指令,实现两个16位数相乘,并把结果累加给64位变量。

函数参数:

  •   第1个参数是乘数地址。
  •   第2个参数是被乘数地址。
  •   第3个参数是定点数个数,其实就是执行点乘的次数。
  •   第4个参数是结果地址。

11.5.4        函数arm_dot_prod_q7

函数原型:

1.    void arm_dot_prod_q7(
2.      const q7_t * pSrcA,
3.      const q7_t * pSrcB,
4.            uint32_t blockSize,
5.            q31_t * result)
6.    {
7.            uint32_t blkCnt;                               /* Loop counter */
8.            q31_t sum = 0;                                 /* Temporary return variable */
9.    
10.    #if defined (ARM_MATH_LOOPUNROLL)
11.    
12.    #if defined (ARM_MATH_DSP)
13.      q31_t input1, input2;                          /* Temporary variables */
14.      q31_t inA1, inA2, inB1, inB2;                  /* Temporary variables */
15.    #endif
16.    
17.      /* Loop unrolling: Compute 4 outputs at a time */
18.      blkCnt = blockSize >> 2U;
19.    
20.      while (blkCnt > 0U)
21.      {
22.        /* C = A[0]* B[0] + A[1]* B[1] + A[2]* B[2] + .....+ A[blockSize-1]* B[blockSize-1] */
23.    
24.    #if defined (ARM_MATH_DSP)
25.        /* read 4 samples at a time from sourceA */
26.        input1 = read_q7x4_ia ((q7_t **) &pSrcA);
27.        /* read 4 samples at a time from sourceB */
28.        input2 = read_q7x4_ia ((q7_t **) &pSrcB);
29.    
30.        /* extract two q7_t samples to q15_t samples */
31.        inA1 = __SXTB16(__ROR(input1, 8));
32.        /* extract reminaing two samples */
33.        inA2 = __SXTB16(input1);
34.        /* extract two q7_t samples to q15_t samples */
35.        inB1 = __SXTB16(__ROR(input2, 8));
36.        /* extract reminaing two samples */
37.        inB2 = __SXTB16(input2);
38.    
39.        /* multiply and accumulate two samples at a time */
40.        sum = __SMLAD(inA1, inB1, sum);
41.        sum = __SMLAD(inA2, inB2, sum);
42.    #else
43.        sum += (q31_t) ((q15_t) *pSrcA++ * *pSrcB++);
44.        sum += (q31_t) ((q15_t) *pSrcA++ * *pSrcB++);
45.        sum += (q31_t) ((q15_t) *pSrcA++ * *pSrcB++);
46.        sum += (q31_t) ((q15_t) *pSrcA++ * *pSrcB++);
47.    #endif
48.    
49.        /* Decrement loop counter */
50.        blkCnt--;
51.      }
52.    
53.      /* Loop unrolling: Compute remaining outputs */
54.      blkCnt = blockSize % 0x4U;
55.    
56.    #else
57.    
58.      /* Initialize blkCnt with number of samples */
59.      blkCnt = blockSize;
60.    
61.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */
62.    
63.      while (blkCnt > 0U)
64.      {
65.        /* C = A[0]* B[0] + A[1]* B[1] + A[2]* B[2] + .....+ A[blockSize-1]* B[blockSize-1] */
66.    
67.        /* Calculate dot product and store result in a temporary buffer. */
68.    //#if defined (ARM_MATH_DSP)
69.    //    sum  = __SMLAD(*pSrcA++, *pSrcB++, sum);
70.    //#else
71.        sum += (q31_t) ((q15_t) *pSrcA++ * *pSrcB++);
72.    //#endif
73.    
74.        /* Decrement loop counter */
75.        blkCnt--;
76.      }
77.    
78.      /* Store result in destination buffer in 18.14 format */
79.      *result = sum;
80.    }

 

函数描述:

这个函数用于求8位定点数的点乘。

函数解析:

  •   第10到56行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第63到76行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   两个Q8格式的数据相乘,那么输出结果就是1.7*1.7 = 2.14格式。这里将最终结果赋值给了32位的变量,那么最终的格式就是18.14。如果乘累加的个数小于2^18那么就不会有溢出的危险。
  •   __SXTB16也是SIMD指令,用于将两个8位的有符号数扩展成16位。__ROR用于实现数据的循环右移。
  •   __SMLAD也是SIMD指令,用于实现如下功能:

sum = __SMLAD(x, y, z)

sum = z + ((short)(x>>16) * (short)(y>>16)) + ((short)x * (short)y)

函数参数:

  •   第1个参数是乘数地址。
  •   第2个参数是被乘数地址。
  •   第3个参数是定点数个数,其实就是执行点乘的次数。
  •   第4个参数是结果地址。

11.5.5        使用举例

程序设计:

/*
*********************************************************************************************************
*    函 数 名: DSP_DotProduct
*    功能说明: 点乘
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void DSP_DotProduct(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   result;  q31_t  pSrcA1[5] = {0x7ffffff0,1,1,1,1};  q31_t  pSrcB1[5] = {1,1,1,1,1};  q63_t  result1;   q15_t  pSrcA2[5] = {1,1,1,1,1};  q15_t  pSrcB2[5] = {1,1,1,1,1};  q63_t  result2;   q7_t  pSrcA3[5] = {1,1,1,1,1}; q7_t  pSrcB3[5] = {1,1,1,1,1};  q31_t result3;  /*求点乘*********************************/arm_dot_prod_f32(pSrcA, pSrcB, 5, &result);printf("arm_dot_prod_f32 = %f\r\n", result);arm_dot_prod_q31(pSrcA1, pSrcB1, 5, &result1);printf("arm_dot_prod_q31 = %lld\r\n", result1);arm_dot_prod_q15(pSrcA2, pSrcB2, 5, &result2);printf("arm_dot_prod_q15 = %lld\r\n", result2);arm_dot_prod_q7(pSrcA3, pSrcB3, 5, &result3);printf("arm_dot_prod_q7 = %d\r\n", result3);printf("***********************************\r\n");
}

 

实验现象:

11.6 乘法(Vector Multiplication)

这部分函数主要用于乘法,公式描述如下:

pDst[n] = pSrcA[n] * pSrcB[n],   0 <= n < blockSize.    

11.6.1        函数arm_mult_f32

函数原型:

1.    void arm_mult_f32(
2.      const float32_t * pSrcA,
3.      const float32_t * pSrcB,
4.            float32_t * pDst,
5.            uint32_t blockSize)
6.    {
7.        uint32_t blkCnt;                               /* Loop counter */
8.    
9.    #if defined(ARM_MATH_NEON)
10.        float32x4_t vec1;
11.        float32x4_t vec2;
12.        float32x4_t res;
13.    
14.        /* Compute 4 outputs at a time */
15.        blkCnt = blockSize >> 2U;
16.    
17.        while (blkCnt > 0U)
18.        {
19.            /* C = A * B */
20.    
21.            /* Multiply the inputs and then store the results in the destination buffer. */
22.            vec1 = vld1q_f32(pSrcA);
23.            vec2 = vld1q_f32(pSrcB);
24.            res = vmulq_f32(vec1, vec2);
25.            vst1q_f32(pDst, res);
26.    
27.            /* Increment pointers */
28.            pSrcA += 4;
29.            pSrcB += 4; 
30.            pDst += 4;
31.            
32.            /* Decrement the loop counter */
33.            blkCnt--;
34.        }
35.    
36.        /* Tail */
37.        blkCnt = blockSize & 0x3;
38.    
39.    #else
40.    #if defined (ARM_MATH_LOOPUNROLL)
41.    
42.      /* Loop unrolling: Compute 4 outputs at a time */
43.      blkCnt = blockSize >> 2U;
44.    
45.      while (blkCnt > 0U)
46.      {
47.        /* C = A * B */
48.    
49.        /* Multiply inputs 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.    #else
66.    
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.        /* Multiply input 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个参数是数据块大小,其实就是执行乘法的次数。

11.6.2        函数arm_mult_q31

函数原型:

1.    void arm_mult_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.            q31_t out;                                     /* Temporary output variable */
9.    
10.    #if defined (ARM_MATH_LOOPUNROLL)
11.    
12.      /* Loop unrolling: Compute 4 outputs at a time */
13.      blkCnt = blockSize >> 2U;
14.    
15.      while (blkCnt > 0U)
16.      {
17.        /* C = A * B */
18.    
19.        /* Multiply inputs and store result in destination buffer. */
20.        out = ((q63_t) *pSrcA++ * *pSrcB++) >> 32;
21.        out = __SSAT(out, 31);
22.        *pDst++ = out << 1U;
23.    
24.        out = ((q63_t) *pSrcA++ * *pSrcB++) >> 32;
25.        out = __SSAT(out, 31);
26.        *pDst++ = out << 1U;
27.    
28.        out = ((q63_t) *pSrcA++ * *pSrcB++) >> 32;
29.        out = __SSAT(out, 31);
30.        *pDst++ = out << 1U;
31.    
32.        out = ((q63_t) *pSrcA++ * *pSrcB++) >> 32;
33.        out = __SSAT(out, 31);
34.        *pDst++ = out << 1U;
35.    
36.        /* Decrement loop counter */
37.        blkCnt--;
38.      }
39.    
40.      /* Loop unrolling: Compute remaining outputs */
41.      blkCnt = blockSize % 0x4U;
42.    
43.    #else
44.    
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 * B */
53.    
54.        /* Multiply inputs and store result in destination buffer. */
55.        out = ((q63_t) *pSrcA++ * *pSrcB++) >> 32;
56.        out = __SSAT(out, 31);
57.        *pDst++ = out << 1U;
58.    
59.        /* Decrement loop counter */
60.        blkCnt--;
61.      }
62.    
63.    }

 

函数描述:

这个函数用于求32位定点数的乘法。

函数解析:

  •   这个函数使用了饱和运算__SSAT,所得结果是Q31格式,范围Q31 range[0x80000000 0x7FFFFFFF]
  •   第10到43行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第50到61行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   第20行,所得乘积左移32位。
  •   第21行,实现31位精度的饱和运算。
  •   第22行,右移一位,保证所得结果是Q31格式。

函数参数:

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

11.6.3        函数arm_mult_q15

函数原型:

1.    void arm_mult_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, inB1, inB2;                  /* Temporary input variables */
13.      q15_t out1, out2, out3, out4;                  /* Temporary output variables */
14.      q31_t mul1, mul2, mul3, mul4;                  /* Temporary variables */
15.    #endif
16.    
17.      /* Loop unrolling: Compute 4 outputs at a time */
18.      blkCnt = blockSize >> 2U;
19.    
20.      while (blkCnt > 0U)
21.      {
22.        /* C = A * B */
23.    
24.    #if defined (ARM_MATH_DSP)
25.        /* read 2 samples at a time from sourceA */
26.        inA1 = read_q15x2_ia ((q15_t **) &pSrcA);
27.        /* read 2 samples at a time from sourceB */
28.        inB1 = read_q15x2_ia ((q15_t **) &pSrcB);
29.        /* read 2 samples at a time from sourceA */
30.        inA2 = read_q15x2_ia ((q15_t **) &pSrcA);
31.        /* read 2 samples at a time from sourceB */
32.        inB2 = read_q15x2_ia ((q15_t **) &pSrcB);
33.    
34.        /* multiply mul = sourceA * sourceB */
35.        mul1 = (q31_t) ((q15_t) (inA1 >> 16) * (q15_t) (inB1 >> 16));
36.        mul2 = (q31_t) ((q15_t) (inA1      ) * (q15_t) (inB1      ));
37.        mul3 = (q31_t) ((q15_t) (inA2 >> 16) * (q15_t) (inB2 >> 16));
38.        mul4 = (q31_t) ((q15_t) (inA2      ) * (q15_t) (inB2      ));
39.    
40.        /* saturate result to 16 bit */
41.        out1 = (q15_t) __SSAT(mul1 >> 15, 16);
42.        out2 = (q15_t) __SSAT(mul2 >> 15, 16);
43.        out3 = (q15_t) __SSAT(mul3 >> 15, 16);
44.        out4 = (q15_t) __SSAT(mul4 >> 15, 16);
45.    
46.        /* store result to destination */
47.    #ifndef ARM_MATH_BIG_ENDIAN
48.        write_q15x2_ia (&pDst, __PKHBT(out2, out1, 16));
49.        write_q15x2_ia (&pDst, __PKHBT(out4, out3, 16));
50.    #else
51.        write_q15x2_ia (&pDst, __PKHBT(out1, out2, 16));
52.        write_q15x2_ia (&pDst, __PKHBT(out3, out4, 16));
53.    #endif /* #ifndef ARM_MATH_BIG_ENDIAN */
54.    
55.    #else
56.        *pDst++ = (q15_t) __SSAT((((q31_t) (*pSrcA++) * (*pSrcB++)) >> 15), 16);
57.        *pDst++ = (q15_t) __SSAT((((q31_t) (*pSrcA++) * (*pSrcB++)) >> 15), 16);
58.        *pDst++ = (q15_t) __SSAT((((q31_t) (*pSrcA++) * (*pSrcB++)) >> 15), 16);
59.        *pDst++ = (q15_t) __SSAT((((q31_t) (*pSrcA++) * (*pSrcB++)) >> 15), 16);
60.    #endif
61.    
62.        /* Decrement loop counter */
63.        blkCnt--;
64.      }
65.    
66.      /* Loop unrolling: Compute remaining outputs */
67.      blkCnt = blockSize % 0x4U;
68.    
69.    #else
70.    
71.      /* Initialize blkCnt with number of samples */
72.      blkCnt = blockSize;
73.    
74.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */
75.    
76.      while (blkCnt > 0U)
77.      {
78.        /* C = A * B */
79.    
80.        /* Multiply inputs and store result in destination buffer. */
81.        *pDst++ = (q15_t) __SSAT((((q31_t) (*pSrcA++) * (*pSrcB++)) >> 15), 16);
82.    
83.        /* Decrement loop counter */
84.        blkCnt--;
85.      }
86.    
87.    }

 

函数描述:

这个函数用于求16位定点数的乘法。

函数解析:

  •   这个函数使用了饱和运算__SSAT,所得结果是Q31格式,范围Q31 range[0x80000000 0x7FFFFFFF]。
  •   第9到69行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第79到85行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   第26行,一次读取两个Q15格式的数据。
  •   第35到38行,将四组数的乘积保存到Q31格式的变量mul1,mul2,mul3,mul4。
  •   第41到44行,丢弃32位数据的低15位,并把最终结果饱和到16位精度。
  •   第51到52行的SIMD指令__PKHBT,将两个Q15格式的数据保存的结果数组中,从而一个指令周期就能完成两个数据的存储。

函数参数:

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

11.6.4        函数arm_mult_q7

函数原型:

1.    void arm_mult_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.    #if defined (ARM_MATH_DSP)
12.      q7_t out1, out2, out3, out4;                   /* Temporary output variables */
13.    #endif
14.    
15.      /* Loop unrolling: Compute 4 outputs at a time */
16.      blkCnt = blockSize >> 2U;
17.    
18.      while (blkCnt > 0U)
19.      {
20.        /* C = A * B */
21.    
22.    #if defined (ARM_MATH_DSP)
23.        /* Multiply inputs and store results in temporary variables */
24.        out1 = (q7_t) __SSAT((((q15_t) (*pSrcA++) * (*pSrcB++)) >> 7), 8);
25.        out2 = (q7_t) __SSAT((((q15_t) (*pSrcA++) * (*pSrcB++)) >> 7), 8);
26.        out3 = (q7_t) __SSAT((((q15_t) (*pSrcA++) * (*pSrcB++)) >> 7), 8);
27.        out4 = (q7_t) __SSAT((((q15_t) (*pSrcA++) * (*pSrcB++)) >> 7), 8);
28.    
29.        /* Pack and store result in destination buffer (in single write) */
30.        write_q7x4_ia (&pDst, __PACKq7(out1, out2, out3, out4));
31.    #else
32.        *pDst++ = (q7_t) __SSAT((((q15_t) (*pSrcA++) * (*pSrcB++)) >> 7), 8);
33.        *pDst++ = (q7_t) __SSAT((((q15_t) (*pSrcA++) * (*pSrcB++)) >> 7), 8);
34.        *pDst++ = (q7_t) __SSAT((((q15_t) (*pSrcA++) * (*pSrcB++)) >> 7), 8);
35.        *pDst++ = (q7_t) __SSAT((((q15_t) (*pSrcA++) * (*pSrcB++)) >> 7), 8);
36.    #endif
37.    
38.        /* Decrement loop counter */
39.        blkCnt--;
40.      }
41.    
42.      /* Loop unrolling: Compute remaining outputs */
43.      blkCnt = blockSize % 0x4U;
44.    
45.    #else
46.    
47.      /* Initialize blkCnt with number of samples */
48.      blkCnt = blockSize;
49.    
50.    #endif /* #if defined (ARM_MATH_LOOPUNROLL) */
51.    
52.      while (blkCnt > 0U)
53.      {
54.        /* C = A * B */
55.    
56.        /* Multiply input and store result in destination buffer. */
57.        *pDst++ = (q7_t) __SSAT((((q15_t) (*pSrcA++) * (*pSrcB++)) >> 7), 8);
58.    
59.        /* Decrement loop counter */
60.        blkCnt--;
61.      }
62.    
63.    }

 

函数描述:

这个函数用于求8位定点数的乘法。

函数解析:

  •   这个函数使用了饱和算法__SSAT,所得结果是Q7格式,范围 [0x80  0x7F]
  •   第9到45行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
  •   第52到61行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
  •   第24到27行,将两个Q7格式的数据乘积左移7位,也就是丢掉低7位的数据,并将所得结果饱和到8位精度。
  •   第30行,__PACKq7函数可以在一个时钟周期就能完成相应操作。

函数参数:

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

11.6.5        使用举例

程序设计:

/*
*********************************************************************************************************
*    函 数 名: DSP_Multiplication
*    功能说明: 乘法
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void DSP_Multiplication(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_mult_f32(pSrcA, pSrcB, pDst, 5);printf("arm_mult_f32 = %f\r\n", pDst[0]);pSrcA1[0] += 1;arm_mult_q31(pSrcA1, pSrcB1, pDst1, 5);printf("arm_mult_q31 = %d\r\n", pDst1[0]);pSrcA2[0] += 1;arm_mult_q15(pSrcA2, pSrcB2, pDst2, 5);printf("arm_mult_q15 = %d\r\n", pDst2[0]);pSrcA3[0] += 1;arm_mult_q7(pSrcA3, pSrcB3, pDst3, 5);printf("arm_mult_q7 = %d\r\n", pDst3[0]);printf("***********************************\r\n");
}

 

实验现象:

这里特别注意为什么Q31和Q15结算的输出结果会有0,关于这个问题,在此贴进行了详细说明:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=95194。

11.7 实验例程说明(MDK)

配套例子:

V7-206_DSP基础运算(绝对值,求和,乘法和点乘)

实验目的:

  1. 学习基础运算(绝对值,求和,乘法和点乘)。

实验内容:

  1. 启动一个自动重装软件定时器,每100ms翻转一次LED2。
  2. 按下按键K1, DSP求绝对值运算。
  3. 按下按键K2, DSP求和运算。
  4. 按下按键K3, DSP求点乘运算。
  5. 按下摇杆OK键, DSP求乘积运算。 

使用AC6注意事项

特别注意附件章节C的问题

上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1。

详见本章的4.5,5.5和6.5小节。

程序设计:

  系统栈大小分配:

  RAM空间用的DTCM:

 

  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*
*********************************************************************************************************
*    函 数 名: 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();
#endifbsp_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区。

/*
*********************************************************************************************************
*    函 数 名: 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();
}

 

  主功能:

主程序实现如下操作:

  •  按下按键K1, DSP求绝对值运算。
  •  按下按键K2, DSP求和运算。
  •  按下按键K3, DSP求点乘运算。
  •  按下摇杆OK键, DSP求乘积运算。 
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: c程序入口
*    形    参: 无
*    返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{uint8_t ucKeyCode;        /* 按键代码 */uint8_t ucValue;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_ABS();break;case KEY_DOWN_K2:            /* K2键按下, 求和 */DSP_Add();break;case KEY_DOWN_K3:            /* K3键按下,求点乘 */DSP_DotProduct();break;case JOY_DOWN_OK:            /* 摇杆OK键按下,求乘积 */DSP_Multiplication();break;default:/* 其他的键值不处理 */break;}}}
}

 

11.8 实验例程说明(IAR)

配套例子:

V7-206_DSP基础运算(绝对值,求和,乘法和点乘)

实验目的:

  1. 学习基础运算(绝对值,求和,乘法和点乘)。

实验内容:

  1. 启动一个自动重装软件定时器,每100ms翻转一次LED2。
  2. 按下按键K1, DSP求绝对值运算。
  3. 按下按键K2, DSP求和运算。
  4. 按下按键K3, DSP求点乘运算。
  5. 按下摇杆OK键, DSP求乘积运算。 

上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1。

详见本章的4.5,5.5和6.5小节。

程序设计:

  系统栈大小分配:

  RAM空间用的DTCM:

  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*
*********************************************************************************************************
*    函 数 名: 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();
#endifbsp_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区。

/*
*********************************************************************************************************
*    函 数 名: 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();
}

 

  主功能:

主程序实现如下操作:

  •   按下按键K1, DSP求绝对值运算。
  •   按下按键K2, DSP求和运算。
  •   按下按键K3, DSP求点乘运算。
  •   按下摇杆OK键, DSP求乘积运算。 
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: c程序入口
*    形    参: 无
*    返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{uint8_t ucKeyCode;        /* 按键代码 */uint8_t ucValue;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_ABS();break;case KEY_DOWN_K2:            /* K2键按下, 求和 */DSP_Add();break;case KEY_DOWN_K3:            /* K3键按下,求点乘 */DSP_DotProduct();break;case JOY_DOWN_OK:            /* 摇杆OK键按下,求乘积 */DSP_Multiplication();break;default:/* 其他的键值不处理 */break;}}}
}

 

11.9 总结

本期教程就跟大家讲这么多,还是那句话,可以自己写些代码调用本期教程中讲的这几个函数,如果可以的话,可以自己尝试直接调用这些DSP指令。

 


http://chatgpt.dhexx.cn/article/PKReO8sO.shtml

相关文章

c++中ifstream和ofstream的详细用法

TITLE&#xff1a;因为不经常用这两个操作&#xff0c;每次用都得找&#xff0c;网上的文章良莠不齐&#xff0c;有时候不容易找到合适的&#xff0c;故将其记录下来&#xff0c;供以后使用方便。 1. fstream fstream提供了三个类&#xff0c;用来实现c对文件的操作。&#x…

C++文件读取ifstream

使用ifstream流来读取文件 说明&#xff1a; 1.ifstream类的对象创建成功的时候会返回非空值&#xff0c;借此判断是否创建文件对象成功 2.ifstream有个函数eof()用来判断文件是否读到尾部,没读到尾部返回false&#xff0c;否则返回true。 若尾部有回车&#xff0c;那么最后一…

C++中读取文件ifstream、ofstream

现在&#xff0c;我想对一个utf-8的json文件进行读写操作。在此过程中&#xff0c;遇到了一些问题。 头文件的添加&#xff1a; #include <iostream> #include <cstring> #include <fstream> #include <sstream> &#xff08;一&#xff09;ifstrea…

C++ 文件读写操作std::ofstream和std::ifstream

文章目录 前言一、文件操作的介绍二、使用步骤1.读文件操作&#xff08;std::ifstream&#xff09;2.写文件操作&#xff08;std::ofstream&#xff09; 前言 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了学习…

C++ 中 ifstream读取txt文件内容

文章目录 头文件把txt文件放在当前目录下1、逐行读入文件2、逐个字符读入&#xff08;忽略空格与回车&#xff09;3、逐个字符读入&#xff08;包括空格与回车&#xff09;示例代码 头文件 #include <iostream> #include <fstream> #include <cassert> #inc…

【C++11】文件操作ifstreamofstream

文章目录 文件输入流创建ifstream对象读取文件数据 文件输出流文件流的打开模式创建ofstream对象写入文件数据 文件输入输出示例 文件输入流 创建ifstream对象 使用ifstream类创建ifstream对象&#xff0c;所获取的对象能够像cin一样使用>>运算符从所绑定文件中读取数据…

C++ ifstream eof()的使用

C ifstream eof() 的使用 eof() 的使用方法1 ifstream.eof() 读到文件结束符时返回true。 大家可能有一个误区&#xff0c;认为读到文件结束符就是读到文件的最后一个字符。 其实不然&#xff0c;文件结束符是文件最后一个字符的下一个字符0xFF&#xff0c;eof() 读到文件结束…

C++文件读写详解(ofstream,ifstream,fstream)

在看C编程思想中&#xff0c;每个练习基本都是使用ofstream,ifstream,fstream&#xff0c;以前粗略知道其用法和含义&#xff0c;在看了几位大牛的博文后&#xff0c;进行整理和总结&#xff1a; 这里主要是讨论fstream的内容&#xff1a; #include <fstream>ofstream …

c++输入文件流ifstream用法详解

目录 文章目录 输入流的继承关系&#xff1a;成员函数Public member functions 1&#xff0c; (constructor) 2&#xff0c;ifstream::open 3&#xff0c;ifstream:: is_open 4&#xff0c;ifstream:: close 5&#xff0c;ifstream:: rdbuf 6&#xff0c;ifstream:: operator …

ifstream的使用

fstream提供了三个类&#xff0c;用来实现c对文件的操作。&#xff08;文件的创建、读、写&#xff09;。 ifstream – 从已有的文件读入 ofstream – 向文件写内容 fstream - 打开文件供读写 文件打开模式&#xff1a; ios::in 只读 ios::out 只写 ios::app 从文件末尾开始写&…

c++ 输入文件流ifstream用法详解

文章目录 c 输入文件流ifstream用法详解输入流的继承关系&#xff1a;C 使用标准库类来处理面向流的输入和输出&#xff1a;成员函数Public member functions1. **(constructor)**2. **ifstream::open**3. **ifstream:: is_open**4. **ifstream:: close**5. **ifstream:: rdbuf…

计算机工业控制高职教材,计算机控制技术(21世纪高职高专系列规划教材)

导语 本书以工业控制计算机(IPC)为主线&#xff0c;理论联系实际&#xff0c;突出工程应用&#xff0c;阐述了计算机控制技术及其工程实现方法。全书分为8章&#xff0c;内容包括&#xff1a;计算机控制系统概述&#xff0c;计算机控制过程通道&#xff0c;数字控制技术&#x…

高职高专信息工程学院专业设置

学院全面落实立德树人根本任务,注重实习实训,着力培养德才兼备的技能型信息技术人才。与华为、腾讯、百度、阿里巴巴、新浪、搜狐、网易等多家知名IT企业合作,建成多所紧贴行业前沿的实习实训基地,保证人才培养与企业需求无缝对接。学院现有计算机应用技术、云计算技术应用…

湖北省高职计算机本科学校有哪些,盘点最新湖北十大高职高专院校排名,湖北最好的高职院校有哪些?...

高职高专就是高等职业学院和高等专科学校的简称&#xff0c;是专科(大专)层次的普通高等学校。简单点来说&#xff0c;高职高专院校就是职业技术教育&#xff0c;是职业技术教育的高等阶段。今天小编就来给大家盘点下最新湖北十大高职高专院校排名&#xff0c;湖北最好的高职院…

江西省计算机学会高职高专,我校应邀出席江西省计算机学会高职高专工作委员会成立大会...

8月21日下午&#xff0c;江西省计算机学会高职高专工作委员会成立大会在南昌召开&#xff0c;中国计算机学会职业教育发展委员会、江西省计算机学会、广东省计算机学会高职高专分会、省内相关高职院校领导和企业代表等90余人参加成立大会。我校作为主任委员单位&#xff0c;副校…

武汉高职高专计算机专业分数线,武汉高职高专学校有哪些及分数线

武汉市高职高专众多&#xff0c;其中也包含不少野鸡大学&#xff0c;哪些高职高专是值得2020年高考生选择的正规高校&#xff0c;7月9日教育部官网已公布全国高校名单&#xff0c;其中武汉市有37所公办或民办的高职学校&#xff0c;已整理各高职院校2019年名单及最低录取分数线…

高职高专计算机毕业论文平面设计,高职高专平面设计论文

高职高专平面设计论文 1高职高专平面设计教学的问题 (1)学生学习态度不够端正 随着高校的不断扩招&#xff0c;大学本科的门槛变低&#xff0c;学习不好的学生也可以考上高职高专的&#xff0c;所以很多学生高中学习成绩就不好&#xff0c;基础不扎实&#xff0c;他们一直是态度…

计算机网络 高职,高职高专计算机网络

高职高专计算机网络 1、高职高专院校精品课程现状 从教学方法到教学手段、从教学思想到教学内容、从教材到管理、从教师到学生&#xff0c;计算机网络精品课程建设涉及广泛。然而&#xff0c;要想提升教学质量&#xff0c;就必须紧抓每一个环节。 1.1教学实践比重失衡 在现阶段…

湖北省高职高专计算机专业排名,湖北高职高专学校排名

今天就是高职高专院校填报志愿的日子了&#xff0c;湖北省的高职高专院校有哪些&#xff0c;排名比较靠前的是哪些&#xff0c;大家在填报志愿的时候总是非常迷茫不知道该填报什么专业&#xff0c;下面还给大家推荐了一些该院校的王牌专业&#xff0c;希望能对大家有所帮助。 周…

模式识别、机器学习、深度学习的区别

1、模式识别概念 模式识别是指对表征事物或现象的各种形式的(数值的、文字的和逻辑关系的)信息进行处理和分析&#xff0c;以对事物或现象进行描述、辨认、分类和解释的过程&#xff0c;是信息科学和人工智能的重要组成部分。 2、机器学习 计算机程序可以在给定某种类别的任…