本期教程主要讲基本函数中的相反数,偏移,移位,减法和比例因子。
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 <= n < blockSize.
特别注意,这部分函数支持目标指针和源指针指向相同的缓冲区。
12.3.1 函数arm_negate_f32
函数原型:
1. void arm_negate_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_EXPERIMENTAL)
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. /* 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. #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. /* 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. #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_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. }
函数描述:
这个函数用于求32位浮点数的相反数。
函数解析:
- 第8到35行,用于NEON指令集,当前的CM内核不支持。
- 第36到61行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
- 浮点数的相反数求解比较简单,直接在相应的变量前加上负号即可。
- 第69到78行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:
- 第1个参数是原数据地址。
- 第2个参数是求相反数后目的数据地址。
- 第3个参数转换的数据个数,这里是指的浮点数个数。
12.3.2 函数arm_negate _q31
函数原型:
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. #else
23. *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;
24. #endif
25.
26. in = *pSrc++;
27. #if defined (ARM_MATH_DSP)
28. *pDst++ = __QSUB(0, in);
29. #else
30. *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;
31. #endif
32.
33. in = *pSrc++;
34. #if defined (ARM_MATH_DSP)
35. *pDst++ = __QSUB(0, in);
36. #else
37. *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;
38. #endif
39.
40. in = *pSrc++;
41. #if defined (ARM_MATH_DSP)
42. *pDst++ = __QSUB(0, in);
43. #else
44. *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;
45. #endif
46.
47. /* Decrement loop counter */
48. blkCnt--;
49. }
50.
51. /* Loop unrolling: Compute remaining outputs */
52. blkCnt = blockSize % 0x4U;
53.
54. #else
55.
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. #else
70. *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;
71. #endif
72.
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. 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. #endif
14.
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. #else
30. 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. #endif
42.
43. /* Decrement loop counter */
44. blkCnt--;
45. }
46.
47. /* Loop unrolling: Compute remaining outputs */
48. blkCnt = blockSize % 0x4U;
49.
50. #else
51.
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. 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. #endif
14.
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. #else
27. 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. #endif
39.
40. /* Decrement loop counter */
41. blkCnt--;
42. }
43.
44. /* Loop unrolling: Compute remaining outputs */
45. blkCnt = blockSize % 0x4U;
46.
47. #else
48.
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. #else
64. *pDst++ = (in == (q7_t) 0x80) ? (q7_t) 0x7f : -in;
65. #endif
66.
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 使用举例
程序设计:
/*
*********************************************************************************************************
* 函 数 名: 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 = %frn", pDst);
pSrc1 -= 1;
arm_negate_q31(&pSrc1, &pDst1, 1);
printf("arm_negate_q31 = %drn", pDst1);
pSrc2 -= 1;
arm_negate_q15(&pSrc2, &pDst2, 1);
printf("arm_negate_q15 = %drn", pDst2);
pSrc3 += 1;
arm_negate_q7(&pSrc3, &pDst3, 1);
printf("arm_negate_q7 = %drn", pDst3);
printf("***********************************rn");
}
实验现象:
12.4 偏移(Vector Offset)
这部分函数主要用于求偏移,公式描述如下:
pDst[n] = pSrc[n] + offset, 0 <= n < blockSize.
注意,这部分函数支持目标指针和源指针指向相同的缓冲区。
12.4.1 函数arm_offset_f32
函数原型:
1. void arm_offset_f32(
2. const float32_t * pSrc,
3. float32_t offset,
4. float32_t * pDst,
5. uint32_t blockSize)
6. {
7. uint32_t blkCnt; /* Loop counter */
8.
9. #if defined(ARM_MATH_NEON_EXPERIMENTAL)
10. float32x4_t vec1;
11. float32x4_t res;
12.
13. /* Compute 4 outputs at a time */
14. blkCnt = blockSize >> 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. #else
37. #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. #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_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. }
函数描述:
这个函数用于求32位浮点数的偏移。
函数解析:
- 第9到36行,用于NEON指令集,当前的CM内核不支持。
- 第37到62行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
- 第70到79行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是偏移量。
- 第3个参数是转换后的目的地址。
- 第4个参数是浮点数个数,其实就是执行偏移的次数。
12.4.2 函数arm_offset_q31
函数原型:
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. #else
22. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
23. #endif
24.
25. #if defined (ARM_MATH_DSP)
26. *pDst++ = __QADD(*pSrc++, offset);
27. #else
28. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
29. #endif
30.
31. #if defined (ARM_MATH_DSP)
32. *pDst++ = __QADD(*pSrc++, offset);
33. #else
34. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
35. #endif
36.
37. #if defined (ARM_MATH_DSP)
38. *pDst++ = __QADD(*pSrc++, offset);
39. #else
40. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
41. #endif
42.
43. /* Decrement loop counter */
44. blkCnt--;
45. }
46.
47. /* Loop unrolling: Compute remaining outputs */
48. blkCnt = blockSize % 0x4U;
49.
50. #else
51.
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. #else
65. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
66. #endif
67.
68. /* Decrement loop counter */
69. blkCnt--;
70. }
71.
72. }
————————————————
版权声明:本文为CSDN博主「Simon223」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Simon223/article/details/105635186
函数描述:
这个函数用于求两个32位定点数的偏移。
函数解析:
- 第9到50行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
- 第57到70行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
- __QADD实现32位数的加法饱和运算。输出结果的范围[0x80000000 0x7FFFFFFF],超出这个结果将产生饱和结果,负数饱和到0x80000000,正数饱和到0x7FFFFFFF。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是偏移量。
- 第3个参数是转换后的目的地址。
- 第4个参数是定点数个数,其实就是执行偏移的次数。
12.4.3 函数arm_offset_q15
函数原型:
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实现的话,如下:
#define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0x0000FFFF) | (((int32_t)(ARG2) << ARG3) & (int32_t)0xFFFF0000) )
__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个参数是定点数个数,其实就是执行偏移的次数。
12.4.4 函数arm_offset_q7
函数原型:
1. void arm_offset_q7(2. const q7_t * pSrc,3. q7_t offset,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. 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 = __PACKq7(offset, offset, offset, offset);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 (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的原型如下:
__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 使用举例
程序设计:
/*********************************************************************************************************** 函 数 名: 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 = %frn", pDst); Offset1--; arm_offset_q31(&pSrcA1, Offset1, &pDst1, 1); printf("arm_offset_q31 = %drn", pDst1); Offset2--; arm_offset_q15(&pSrcA2, Offset2, &pDst2, 1); printf("arm_offset_q15 = %drn", pDst2); Offset3--; arm_offset_q7(&pSrcA3, Offset3, &pDst3, 1); printf("arm_offset_q7 = %drn", pDst3); printf("***********************************rn");}
实验现象:
12.5 移位(Vector Shift)
这部分函数主要用于实现移位,公式描述如下:
pDst[n] = pSrc[n] << shift, 0 <= n < blockSize.
注意,这部分函数支持目标指针和源指针指向相同的缓冲区
12.5.1 函数arm_shift_q31
函数原型:
1. void arm_shift_q31(2. const q31_t * pSrc,3. int8_t shiftBits,4. q31_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. q31_t in, out; /* Temporary variables */13. 14. /* Loop unrolling: Compute 4 outputs at a time */15. blkCnt = blockSize >> 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 */23. 24. /* Shift input and store result in destination buffer. */25. in = *pSrc++;26. out = in << shiftBits;27. if (in != (out >> shiftBits))28. out = 0x7FFFFFFF ^ (in >> 31);29. *pDst++ = out;30. 31. in = *pSrc++;32. out = in << shiftBits;33. if (in != (out >> shiftBits))34. out = 0x7FFFFFFF ^ (in >> 31);35. *pDst++ = out;36. 37. in = *pSrc++;38. out = in << shiftBits;39. if (in != (out >> shiftBits))40. out = 0x7FFFFFFF ^ (in >> 31);41. *pDst++ = out;42. 43. in = *pSrc++;44. out = in << shiftBits;45. if (in != (out >> 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 */86. 87. /* Shift input and store result in destination buffer. */88. *pDst++ = clip_q63_to_q31((q63_t) *pSrc++ << shiftBits);89. 90. /* Decrement loop counter */91. blkCnt--;92. }93. }94. else95. {96. while (blkCnt > 0U)97. {98. /* 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. }
函数描述:
这个函数用于求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的原型如下:
__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. 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 */25. 26. #if defined (ARM_MATH_DSP)27. /* read 2 samples from source */28. in1 = *pSrc++;29. in2 = *pSrc++;30. 31. /* Shift the inputs and then store the results in the destination buffer. */32. #ifndef ARM_MATH_BIG_ENDIAN33. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in1 << shiftBits), 16),34. __SSAT((in2 << shiftBits), 16), 16));35. #else36. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in2 << shiftBits), 16),37. __SSAT((in1 << shiftBits), 16), 16));38. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */39. 40. /* read 2 samples from source */41. in1 = *pSrc++;42. in2 = *pSrc++;43. 44. #ifndef ARM_MATH_BIG_ENDIAN45. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in1 << shiftBits), 16),46. __SSAT((in2 << shiftBits), 16), 16));47. #else48. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in2 << shiftBits), 16),49. __SSAT((in1 << shiftBits), 16), 16));50. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */51. 52. #else53. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);54. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);55. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);56. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);57. #endif58. 59. /* Decrement loop counter */60. blkCnt--;61. }62. }63. else64. {65. while (blkCnt > 0U)66. {67. /* C = A >> shiftBits */68. 69. #if defined (ARM_MATH_DSP)70. /* read 2 samples from source */71. in1 = *pSrc++;72. in2 = *pSrc++;73. 74. /* Shift the inputs and then store the results in the destination buffer. */75. #ifndef ARM_MATH_BIG_ENDIAN76. write_q15x2_ia (&pDst, __PKHBT((in1 >> -shiftBits),77. (in2 >> -shiftBits), 16));78. #else79. write_q15x2_ia (&pDst, __PKHBT((in2 >> -shiftBits),80. (in1 >> -shiftBits), 16));81. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */82. 83. /* read 2 samples from source */84. in1 = *pSrc++;85. in2 = *pSrc++;86. 87. #ifndef ARM_MATH_BIG_ENDIAN88. write_q15x2_ia (&pDst, __PKHBT((in1 >> -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 */123. 124. /* Shift input and store result in destination buffer. */125. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);126. 127. /* Decrement loop counter */128. blkCnt--;129. }130. }131. else132. {133. while (blkCnt > 0U)134. {135. /* 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。
__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实现的话,如下:
#define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0x0000FFFF) | (((int32_t)(ARG2) << ARG3) & (int32_t)0xFFFF0000) )
- 第118到143行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是左移或者右移位数,正数是左移,负数是右移。
- 第3个参数是移位后数据地址。
- 第4个参数是定点数个数,其实就是执行左移或者右移的次数。
12.5.3 函数arm_shift_q7
函数原型:
函数原型:
1. void arm_shift_q7(
2. const q7_t * pSrc,
3. int8_t shiftBits,
4. q7_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. q7_t in1, in2, in3, in4; /* Temporary input variables */
14. #endif
15.
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 */
25.
26. #if defined (ARM_MATH_DSP)
27. /* Read 4 inputs */
28. in1 = *pSrc++;
29. in2 = *pSrc++;
30. in3 = *pSrc++;
31. in4 = *pSrc++;
32.
33. /* Pack and store result in destination buffer (in single write) */
34. write_q7x4_ia (&pDst, __PACKq7(__SSAT((in1 << shiftBits), 8),
35. __SSAT((in2 << shiftBits), 8),
36. __SSAT((in3 << shiftBits), 8),
37. __SSAT((in4 << shiftBits), 8) ));
38. #else
39. *pDst++ = (q7_t) __SSAT(((q15_t) *pSrc++ << shiftBits), 8);
40. *pDst++ = (q7_t) __SSAT(((q15_t) *pSrc++ << shiftBits), 8);
41. *pDst++ = (q7_t) __SSAT(((q15_t) *pSrc++ << shiftBits), 8);
42. *pDst++ = (q7_t) __SSAT(((q15_t) *pSrc++ << shiftBits), 8);
43. #endif
44.
45. /* Decrement loop counter */
46. blkCnt--;
47. }
48. }
49. else
50. {
51. while (blkCnt > 0U)
52. {
53. /* C = A >> shiftBits */
54.
55. #if defined (ARM_MATH_DSP)
56. /* Read 4 inputs */
57. in1 = *pSrc++;
58. in2 = *pSrc++;
59. in3 = *pSrc++;
60. in4 = *pSrc++;
61.
62. /* Pack and store result in destination buffer (in single write) */
63. write_q7x4_ia (&pDst, __PACKq7((in1 >> -shiftBits),
64. (in2 >> -shiftBits),
65. (in3 >> -shiftBits),
66. (in4 >> -shiftBits) ));
67. #else
68. *pDst++ = (*pSrc++ >> -shiftBits);
69. *pDst++ = (*pSrc++ >> -shiftBits);
70. *pDst++ = (*pSrc++ >> -shiftBits);
71. *pDst++ = (*pSrc++ >> -shiftBits);
72. #endif
73.
74. /* Decrement loop counter */
75. blkCnt--;
76. }
77. }
78.
79. /* Loop unrolling: Compute remaining outputs */
80. blkCnt = blockSize % 0x4U;
81.
82. #else
83.
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 */
95.
96. /* Shift input and store result in destination buffer. */
97. *pDst++ = (q7_t) __SSAT(((q15_t) *pSrc++ << shiftBits), 8);
98.
99. /* Decrement loop counter */
100. blkCnt--;
101. }
102. }
103. else
104. {
105. while (blkCnt > 0U)
106. {
107. /* 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. }
————————————————
版权声明:本文为CSDN博主「Simon223」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Simon223/article/details/105635186
函数描述:
这个函数用于求8位定点数的左移或者右移。
函数解析:
- 第10到87行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
- 第20到48行,如果参数shiftBits是正数,执行左移。
- 第49到77行,如果蚕食shiftBits是负数,执行右移。
- 第79行,函数write_q7x4_ia的原型如下,作用是写入4次8位数据,并将数据地址递增,方便下次写入。
__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位数据,实现代码如下:
#define __PACKq7(v0,v1,v2,v3) ( (((int32_t)(v0) << 0) & (int32_t)0x000000FF) | (((int32_t)(v1) << 8) & (int32_t)0x0000FF00) | (((int32_t)(v2) << 16) & (int32_t)0x00FF0000) | (((int32_t)(v3) << 24) & (int32_t)0xFF000000) )
- 第90到115行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是左移或者右移位数,正数是左移,负数是右移。
- 第3个参数是移位后数据地址。
- 第4个参数是定点数个数,其实就是执行左移或者右移的次数
12.5.4 使用举例
程序设计:
/*********************************************************************************************************** 函 数 名: DSP_Shift* 功能说明: 移位* 形 参: 无* 返 回 值: 无**********************************************************************************************************/static void DSP_Shift(void){ q31_t pSrcA1 = 0x88886666; q31_t pDst1; q15_t pSrcA2 = 0x8866; q15_t pDst2; q7_t pSrcA3 = 0x86; q7_t pDst3; /*求移位*********************************/ arm_shift_q31(&pSrcA1, 3, &pDst1, 1); printf("arm_shift_q31 = %8xrn", pDst1); arm_shift_q15(&pSrcA2, -3, &pDst2, 1); printf("arm_shift_q15 = %4xrn", pDst2); arm_shift_q7(&pSrcA3, 3, &pDst3, 1); printf("arm_shift_q7 = %2xrn", pDst3); printf("***********************************rn");}
实验现象:
这里特别注意Q31和Q7的计算结果,表示负数已经饱和到了最小值。另外注意,对于负数来说,右移时,右侧补1,左移时,左侧补0。
12.6 减法(Vector Sub)
这部分函数主要用于实现减法,公式描述如下:
pDst[n] = pSrcA[n] - pSrcB[n], 0 <= n < blockSize。
12.6.1 函数arm_sub_f32
函数原型:
1. void arm_sub_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. /* Subtract and then store the results in the destination buffer. */
22. vec1 = vld1q_f32(pSrcA);
23. vec2 = vld1q_f32(pSrcB);
24. res = vsubq_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. /* 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. #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. /* 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. 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. #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. /* 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. 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. #else
22. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
23. #endif
24.
25. #if defined (ARM_MATH_DSP)
26. *pDst++ = __QADD(*pSrc++, offset);
27. #else
28. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
29. #endif
30.
31. #if defined (ARM_MATH_DSP)
32. *pDst++ = __QADD(*pSrc++, offset);
33. #else
34. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
35. #endif
36.
37. #if defined (ARM_MATH_DSP)
38. *pDst++ = __QADD(*pSrc++, offset);
39. #else
40. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
41. #endif
42.
43. /* Decrement loop counter */
44. blkCnt--;
45. }
46.
47. /* Loop unrolling: Compute remaining outputs */
48. blkCnt = blockSize % 0x4U;
49.
50. #else
51.
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. #else
65. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
66. #endif
67.
68. /* Decrement loop counter */
69. blkCnt--;
70. }
71.
72. }
函数描述:
这个函数用于求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. 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. #endif
17.
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. #else
30. *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. #endif
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 + 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. #else
58. *pDst++ = (q15_t) __SSAT(((q31_t) *pSrc++ + offset), 16);
59. #endif
60.
61. /* Decrement loop counter */
62. blkCnt--;
63. }
64.
65. }
函数描述:
这个函数用于求8位定点数的乘法。
函数解析:
- 第9到35行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
- 第20行,函数write_q7x4_ia实现一次写入4个Q7格式数据到Q31各种中。
函数__QSUB8实现一次计算4个Q7格式减法。
- 第42到51行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:
- 第1个参数是减数地址。
- 第2个参数是被减数地址。
- 第3个参数是结果地址。
- 第4个参数是数据块大小,其实就是执行减法的次数。
12.6.5 使用举例
程序设计:
/*********************************************************************************************************** 函 数 名: 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 = %frn", pDst[0]); pSrcA1[0] += 1; arm_sub_q31(pSrcA1, pSrcB1, pDst1, 5); printf("arm_sub_q31 = %drn", pDst1[0]); pSrcA2[0] += 1; arm_sub_q15(pSrcA2, pSrcB2, pDst2, 5); printf("arm_sub_q15 = %drn", pDst2[0]); pSrcA3[0] += 1; arm_sub_q7(pSrcA3, pSrcB3, pDst3, 5); printf("arm_sub_q7 = %drn", pDst3[0]); printf("***********************************rn");}
实验现象:
12.7 比例因子(Vector Scale)
这部分函数主要用于实现数据的比例放大和缩小,浮点数据公式描述如下:
pDst[n] = pSrc[n] * scale, 0 <= n < blockSize.
如果是Q31,Q15,Q7格式的数据,公式描述如下:
pDst[n] = (pSrc[n] * scaleFract) << shift, 0 <= n < blockSize.
这种情况下,比例因子就是:
scale = scaleFract * 2^shift.
注意,这部分函数支持目标指针和源指针指向相同的缓冲区
12.7.1 函数arm_scale_f32
函数原型:
1. void arm_scale_f32(2. const float32_t *pSrc,3. float32_t scale,4. float32_t *pDst,5. uint32_t blockSize)6. {7. uint32_t blkCnt; /* Loop counter */8. #if defined(ARM_MATH_NEON_EXPERIMENTAL)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 * 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. }
函数描述:
这个函数用于求32位浮点数的比例因子计算。
函数解析:
- 第8到35行,用于NEON指令集,当前的CM内核不支持。
- 第36到61行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
- 第69到78行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:
- 第1个参数是数据源地址。
- 第2个参数是比例因子
- 第3个参数是结果地址。
- 第4个参数是数据块大小,其实就是执行比例因子计算的次数。
12.7.2 函数arm_scale_q31
函数原型:
函数原型:
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; /* apply shifting */
28. if (in != (out >> 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;
35. if (in != (out >> kShift))
36. out = 0x7FFFFFFF ^ (in >> 31);
37. *pDst++ = out;
38.
39. in = *pSrc++;
40. in = ((q63_t) in * scaleFract) >> 32;
41. out = in << kShift;
42. if (in != (out >> kShift))
43. out = 0x7FFFFFFF ^ (in >> 31);
44. *pDst++ = out;
45.
46. in = *pSrc++;
47. in = ((q63_t) in * scaleFract) >> 32;
48. out = in << kShift;
49. if (in != (out >> kShift))
50. out = 0x7FFFFFFF ^ (in >> 31);
51. *pDst++ = out;
52.
53. /* Decrement loop counter */
54. blkCnt--;
55. }
56. }
57. else
58. {
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. #else
93.
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;
109. if (in != (out >> kShift))
110. out = 0x7FFFFFFF ^ (in >> 31);
111. *pDst++ = out;
112.
113. /* Decrement loop counter */
114. blkCnt--;
115. }
116. }
117. else
118. {
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 <<(shift + 1)多执行了一次左移操作。
相当于2.30格式,转换为2.31格式。
- 第28到29行,做了一个Q31的饱和处理,也就是将2.31格式转换为1.31。
数值的左移仅支持将其左移后再右移相应的位数后数值不变的情况,如果不满足这个条件,那么要对输出结果做饱和运算,这里分两种情况:
out = 0x7FFFFFFF ^ (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. 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. #endif
15.
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 */
25.
26. #if defined (ARM_MATH_DSP)
27. /* read 2 samples from source */
28. in1 = *pSrc++;
29. in2 = *pSrc++;
30.
31. /* Shift the inputs and then store the results in the destination buffer. */
32. #ifndef ARM_MATH_BIG_ENDIAN
33. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in1 << shiftBits), 16),
34. __SSAT((in2 << shiftBits), 16), 16));
35. #else
36. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in2 << shiftBits), 16),
37. __SSAT((in1 << shiftBits), 16), 16));
38. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */
39.
40. /* read 2 samples from source */
41. in1 = *pSrc++;
42. in2 = *pSrc++;
43.
44. #ifndef ARM_MATH_BIG_ENDIAN
45. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in1 << shiftBits), 16),
46. __SSAT((in2 << shiftBits), 16), 16));
47. #else
48. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in2 << shiftBits), 16),
49. __SSAT((in1 << shiftBits), 16), 16));
50. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */
51.
52. #else
53. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);
54. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);
55. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);
56. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);
57. #endif
58.
59. /* Decrement loop counter */
60. blkCnt--;
61. }
62. }
63. else
64. {
65. while (blkCnt > 0U)
66. {
67. /* C = A >> shiftBits */
68.
69. #if defined (ARM_MATH_DSP)
70. /* read 2 samples from source */
71. in1 = *pSrc++;
72. in2 = *pSrc++;
73.
74. /* Shift the inputs and then store the results in the destination buffer. */
75. #ifndef ARM_MATH_BIG_ENDIAN
76. write_q15x2_ia (&pDst, __PKHBT((in1 >> -shiftBits),
77. (in2 >> -shiftBits), 16));
78. #else
79. write_q15x2_ia (&pDst, __PKHBT((in2 >> -shiftBits),
80. (in1 >> -shiftBits), 16));
81. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */
82.
83. /* read 2 samples from source */
84. in1 = *pSrc++;
85. in2 = *pSrc++;
86.
87. #ifndef ARM_MATH_BIG_ENDIAN
88. write_q15x2_ia (&pDst, __PKHBT((in1 >> -shiftBits),
89. (in2 >> -shiftBits), 16));
90. #else
91. write_q15x2_ia (&pDst, __PKHBT((in2 >> -shiftBits),
92. (in1 >> -shiftBits), 16));
93. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */
94.
95. #else
96. *pDst++ = (*pSrc++ >> -shiftBits);
97. *pDst++ = (*pSrc++ >> -shiftBits);
98. *pDst++ = (*pSrc++ >> -shiftBits);
99. *pDst++ = (*pSrc++ >> -shiftBits);
100. #endif
101.
102. /* Decrement loop counter */
103. blkCnt--;
104. }
105. }
106.
107. /* Loop unrolling: Compute remaining outputs */
108. blkCnt = blockSize % 0x4U;
109.
110. #else
111.
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 */
123.
124. /* Shift input and store result in destination buffer. */
125. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);
126.
127. /* Decrement loop counter */
128. blkCnt--;
129. }
130. }
131. else
132. {
133. while (blkCnt > 0U)
134. {
135. /* 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实现的话,如下:
#define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0x0000FFFF) |
(((int32_t)(ARG2) << ARG3) & (int32_t)0xFFFF0000) )
函数write_q15x2_ia的原型如下:
__STATIC_FORCEINLINE void write_q15x2_ia (
q15_t ** pQ15,
q31_t value)
{
q31_t val = value;
memcpy (*pQ15, &val, 4);
*pQ15 += 2;
}
作用是写入两次Q15格式数据,组成一个Q31格式数据,并将数据地址递增,方便下次写入。
第118到143行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理
函数参数:
第1个参数是数据源地址。
第2个参数是比例因子。
第3个参数是移位参数,正数表示右移,负数表示左移。
第4参数是结果地址。
第5参数是数据块大小,其实就是执行比例因子计算的次数。
12.7.4 函数arm_scale_q7
函数原型:
1. void arm_scale_q7(
2. const q7_t * pSrc,
3. q7_t scaleFract,
4. int8_t shift,
5. q7_t * pDst,
6. uint32_t blockSize)
7. {
8. uint32_t blkCnt; /* Loop counter */
9. int8_t kShift = 7 - shift; /* Shift to apply after scaling */
10.
11. #if defined (ARM_MATH_LOOPUNROLL)
12.
13. #if defined (ARM_MATH_DSP)
14. q7_t in1, in2, in3, in4; /* Temporary input variables */
15. q7_t out1, out2, out3, out4; /* Temporary output variables */
16. #endif
17.
18. /* Loop unrolling: Compute 4 outputs at a time */
19. blkCnt = blockSize >> 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. #else
41. *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. #endif
46.
47. /* Decrement loop counter */
48. blkCnt--;
49. }
50.
51. /* Loop unrolling: Compute remaining outputs */
52. blkCnt = blockSize % 0x4U;
53.
54. #else
55.
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 使用举例
程序设计:
/*********************************************************************************************************** 函 数 名: 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 = %frn", pDst[0]); scale1 += 1; arm_scale_q31(pSrcA1, scale1, 0, pDst1, 5); printf("arm_scale_q31 = %xrn", pDst1[0]); scale2 += 1; arm_scale_q15(pSrcA2, scale2, 0, pDst2, 5); printf("arm_scale_q15 = %xrn", pDst2[0]); scale3 += 1; arm_scale_q7(pSrcA3, scale3, 0, pDst3, 5); printf("arm_scale_q7 = %xrn", pDst3[0]); printf("***********************************rn");}
实验现象:
12.8 实验例程说明(MDK)
配套例子:
V6-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小节。
程序设计:
系统栈大小分配:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/*
*********************************************************************************************************
* 函 数 名: bsp_Init
* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
/*
STM32F407 HAL 库初始化,此时系统用的还是F407自带的16MHz,HSI时钟:
- 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
- 设置NVIV优先级分组为4。
*/
HAL_Init();
/*
配置系统时钟到168MHz
- 切换使用HSE。
- 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
*/
SystemClock_Config();
/*
Event Recorder:
- 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
- 默认不开启,如果要使能此选项,务必看V5开发板用户手册第8章
*/
#if Enable_EventRecorder == 1
/* 初始化EventRecorder并开启 */
EventRecorderInitialize(EventRecordAll, 1U);
EventRecorderStart();
#endif
bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
bsp_InitTimer(); /* 初始化滴答定时器 */
bsp_InitUart(); /* 初始化串口 */
bsp_InitExtIO(); /* 初始化扩展IO */
bsp_InitLed(); /* 初始化LED */
}
主功能:
主程序实现如下操作:
启动一个自动重装软件定时器,每100ms翻转一次LED2。
按下按键K1, DSP求相反数运算。
按下按键K2, DSP求偏移运算。
按下按键K3, DSP求移位运算。
按下摇杆OK键, DSP求减法运算。
按下摇杆上键, DSP比例因子运算。
/*
*********************************************************************************************************
* 函 数 名: 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)
配套例子:
V6-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小节。
程序设计:
系统栈大小分配:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/*
*********************************************************************************************************
* 函 数 名: bsp_Init
* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
/*
STM32F407 HAL 库初始化,此时系统用的还是F407自带的16MHz,HSI时钟:
- 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
- 设置NVIV优先级分组为4。
*/
HAL_Init();
/*
配置系统时钟到168MHz
- 切换使用HSE。
- 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
*/
SystemClock_Config();
/*
Event Recorder:
- 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
- 默认不开启,如果要使能此选项,务必看V5开发板用户手册第8章
*/
#if Enable_EventRecorder == 1
/* 初始化EventRecorder并开启 */
EventRecorderInitialize(EventRecordAll, 1U);
EventRecorderStart();
#endif
bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
bsp_InitTimer(); /* 初始化滴答定时器 */
bsp_InitUart(); /* 初始化串口 */
bsp_InitExtIO(); /* 初始化扩展IO */
bsp_InitLed(); /* 初始化LED */
}
主功能:
主程序实现如下操作:
按下按键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;
}
}
}
}
12.10 总结
DSP基础函数就跟大家讲这么多,希望初学的同学多多的联系,并在自己以后的项目中多多使用,效果必将事半功倍。
本期教程主要讲基本函数中的相反数,偏移,移位,减法和比例因子。
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 <= n < blockSize.
特别注意,这部分函数支持目标指针和源指针指向相同的缓冲区。
12.3.1 函数arm_negate_f32
函数原型:
1. void arm_negate_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_EXPERIMENTAL)
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. /* 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. #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. /* 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. #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_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. }
函数描述:
这个函数用于求32位浮点数的相反数。
函数解析:
- 第8到35行,用于NEON指令集,当前的CM内核不支持。
- 第36到61行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
- 浮点数的相反数求解比较简单,直接在相应的变量前加上负号即可。
- 第69到78行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:
- 第1个参数是原数据地址。
- 第2个参数是求相反数后目的数据地址。
- 第3个参数转换的数据个数,这里是指的浮点数个数。
12.3.2 函数arm_negate _q31
函数原型:
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. #else
23. *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;
24. #endif
25.
26. in = *pSrc++;
27. #if defined (ARM_MATH_DSP)
28. *pDst++ = __QSUB(0, in);
29. #else
30. *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;
31. #endif
32.
33. in = *pSrc++;
34. #if defined (ARM_MATH_DSP)
35. *pDst++ = __QSUB(0, in);
36. #else
37. *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;
38. #endif
39.
40. in = *pSrc++;
41. #if defined (ARM_MATH_DSP)
42. *pDst++ = __QSUB(0, in);
43. #else
44. *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;
45. #endif
46.
47. /* Decrement loop counter */
48. blkCnt--;
49. }
50.
51. /* Loop unrolling: Compute remaining outputs */
52. blkCnt = blockSize % 0x4U;
53.
54. #else
55.
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. #else
70. *pDst++ = (in == INT32_MIN) ? INT32_MAX : -in;
71. #endif
72.
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. 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. #endif
14.
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. #else
30. 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. #endif
42.
43. /* Decrement loop counter */
44. blkCnt--;
45. }
46.
47. /* Loop unrolling: Compute remaining outputs */
48. blkCnt = blockSize % 0x4U;
49.
50. #else
51.
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. 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. #endif
14.
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. #else
27. 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. #endif
39.
40. /* Decrement loop counter */
41. blkCnt--;
42. }
43.
44. /* Loop unrolling: Compute remaining outputs */
45. blkCnt = blockSize % 0x4U;
46.
47. #else
48.
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. #else
64. *pDst++ = (in == (q7_t) 0x80) ? (q7_t) 0x7f : -in;
65. #endif
66.
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 使用举例
程序设计:
/*
*********************************************************************************************************
* 函 数 名: 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 = %frn", pDst);
pSrc1 -= 1;
arm_negate_q31(&pSrc1, &pDst1, 1);
printf("arm_negate_q31 = %drn", pDst1);
pSrc2 -= 1;
arm_negate_q15(&pSrc2, &pDst2, 1);
printf("arm_negate_q15 = %drn", pDst2);
pSrc3 += 1;
arm_negate_q7(&pSrc3, &pDst3, 1);
printf("arm_negate_q7 = %drn", pDst3);
printf("***********************************rn");
}
实验现象:
12.4 偏移(Vector Offset)
这部分函数主要用于求偏移,公式描述如下:
pDst[n] = pSrc[n] + offset, 0 <= n < blockSize.
注意,这部分函数支持目标指针和源指针指向相同的缓冲区。
12.4.1 函数arm_offset_f32
函数原型:
1. void arm_offset_f32(
2. const float32_t * pSrc,
3. float32_t offset,
4. float32_t * pDst,
5. uint32_t blockSize)
6. {
7. uint32_t blkCnt; /* Loop counter */
8.
9. #if defined(ARM_MATH_NEON_EXPERIMENTAL)
10. float32x4_t vec1;
11. float32x4_t res;
12.
13. /* Compute 4 outputs at a time */
14. blkCnt = blockSize >> 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. #else
37. #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. #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_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. }
函数描述:
这个函数用于求32位浮点数的偏移。
函数解析:
- 第9到36行,用于NEON指令集,当前的CM内核不支持。
- 第37到62行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
- 第70到79行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是偏移量。
- 第3个参数是转换后的目的地址。
- 第4个参数是浮点数个数,其实就是执行偏移的次数。
12.4.2 函数arm_offset_q31
函数原型:
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. #else
22. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
23. #endif
24.
25. #if defined (ARM_MATH_DSP)
26. *pDst++ = __QADD(*pSrc++, offset);
27. #else
28. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
29. #endif
30.
31. #if defined (ARM_MATH_DSP)
32. *pDst++ = __QADD(*pSrc++, offset);
33. #else
34. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
35. #endif
36.
37. #if defined (ARM_MATH_DSP)
38. *pDst++ = __QADD(*pSrc++, offset);
39. #else
40. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
41. #endif
42.
43. /* Decrement loop counter */
44. blkCnt--;
45. }
46.
47. /* Loop unrolling: Compute remaining outputs */
48. blkCnt = blockSize % 0x4U;
49.
50. #else
51.
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. #else
65. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
66. #endif
67.
68. /* Decrement loop counter */
69. blkCnt--;
70. }
71.
72. }
————————————————
版权声明:本文为CSDN博主「Simon223」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Simon223/article/details/105635186
函数描述:
这个函数用于求两个32位定点数的偏移。
函数解析:
- 第9到50行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
- 第57到70行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
- __QADD实现32位数的加法饱和运算。输出结果的范围[0x80000000 0x7FFFFFFF],超出这个结果将产生饱和结果,负数饱和到0x80000000,正数饱和到0x7FFFFFFF。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是偏移量。
- 第3个参数是转换后的目的地址。
- 第4个参数是定点数个数,其实就是执行偏移的次数。
12.4.3 函数arm_offset_q15
函数原型:
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实现的话,如下:
#define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0x0000FFFF) | (((int32_t)(ARG2) << ARG3) & (int32_t)0xFFFF0000) )
__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个参数是定点数个数,其实就是执行偏移的次数。
12.4.4 函数arm_offset_q7
函数原型:
1. void arm_offset_q7(2. const q7_t * pSrc,3. q7_t offset,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. 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 = __PACKq7(offset, offset, offset, offset);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 (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的原型如下:
__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 使用举例
程序设计:
/*********************************************************************************************************** 函 数 名: 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 = %frn", pDst); Offset1--; arm_offset_q31(&pSrcA1, Offset1, &pDst1, 1); printf("arm_offset_q31 = %drn", pDst1); Offset2--; arm_offset_q15(&pSrcA2, Offset2, &pDst2, 1); printf("arm_offset_q15 = %drn", pDst2); Offset3--; arm_offset_q7(&pSrcA3, Offset3, &pDst3, 1); printf("arm_offset_q7 = %drn", pDst3); printf("***********************************rn");}
实验现象:
12.5 移位(Vector Shift)
这部分函数主要用于实现移位,公式描述如下:
pDst[n] = pSrc[n] << shift, 0 <= n < blockSize.
注意,这部分函数支持目标指针和源指针指向相同的缓冲区
12.5.1 函数arm_shift_q31
函数原型:
1. void arm_shift_q31(2. const q31_t * pSrc,3. int8_t shiftBits,4. q31_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. q31_t in, out; /* Temporary variables */13. 14. /* Loop unrolling: Compute 4 outputs at a time */15. blkCnt = blockSize >> 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 */23. 24. /* Shift input and store result in destination buffer. */25. in = *pSrc++;26. out = in << shiftBits;27. if (in != (out >> shiftBits))28. out = 0x7FFFFFFF ^ (in >> 31);29. *pDst++ = out;30. 31. in = *pSrc++;32. out = in << shiftBits;33. if (in != (out >> shiftBits))34. out = 0x7FFFFFFF ^ (in >> 31);35. *pDst++ = out;36. 37. in = *pSrc++;38. out = in << shiftBits;39. if (in != (out >> shiftBits))40. out = 0x7FFFFFFF ^ (in >> 31);41. *pDst++ = out;42. 43. in = *pSrc++;44. out = in << shiftBits;45. if (in != (out >> 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 */86. 87. /* Shift input and store result in destination buffer. */88. *pDst++ = clip_q63_to_q31((q63_t) *pSrc++ << shiftBits);89. 90. /* Decrement loop counter */91. blkCnt--;92. }93. }94. else95. {96. while (blkCnt > 0U)97. {98. /* 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. }
函数描述:
这个函数用于求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的原型如下:
__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. 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 */25. 26. #if defined (ARM_MATH_DSP)27. /* read 2 samples from source */28. in1 = *pSrc++;29. in2 = *pSrc++;30. 31. /* Shift the inputs and then store the results in the destination buffer. */32. #ifndef ARM_MATH_BIG_ENDIAN33. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in1 << shiftBits), 16),34. __SSAT((in2 << shiftBits), 16), 16));35. #else36. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in2 << shiftBits), 16),37. __SSAT((in1 << shiftBits), 16), 16));38. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */39. 40. /* read 2 samples from source */41. in1 = *pSrc++;42. in2 = *pSrc++;43. 44. #ifndef ARM_MATH_BIG_ENDIAN45. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in1 << shiftBits), 16),46. __SSAT((in2 << shiftBits), 16), 16));47. #else48. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in2 << shiftBits), 16),49. __SSAT((in1 << shiftBits), 16), 16));50. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */51. 52. #else53. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);54. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);55. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);56. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);57. #endif58. 59. /* Decrement loop counter */60. blkCnt--;61. }62. }63. else64. {65. while (blkCnt > 0U)66. {67. /* C = A >> shiftBits */68. 69. #if defined (ARM_MATH_DSP)70. /* read 2 samples from source */71. in1 = *pSrc++;72. in2 = *pSrc++;73. 74. /* Shift the inputs and then store the results in the destination buffer. */75. #ifndef ARM_MATH_BIG_ENDIAN76. write_q15x2_ia (&pDst, __PKHBT((in1 >> -shiftBits),77. (in2 >> -shiftBits), 16));78. #else79. write_q15x2_ia (&pDst, __PKHBT((in2 >> -shiftBits),80. (in1 >> -shiftBits), 16));81. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */82. 83. /* read 2 samples from source */84. in1 = *pSrc++;85. in2 = *pSrc++;86. 87. #ifndef ARM_MATH_BIG_ENDIAN88. write_q15x2_ia (&pDst, __PKHBT((in1 >> -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 */123. 124. /* Shift input and store result in destination buffer. */125. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);126. 127. /* Decrement loop counter */128. blkCnt--;129. }130. }131. else132. {133. while (blkCnt > 0U)134. {135. /* 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。
__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实现的话,如下:
#define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0x0000FFFF) | (((int32_t)(ARG2) << ARG3) & (int32_t)0xFFFF0000) )
- 第118到143行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是左移或者右移位数,正数是左移,负数是右移。
- 第3个参数是移位后数据地址。
- 第4个参数是定点数个数,其实就是执行左移或者右移的次数。
12.5.3 函数arm_shift_q7
函数原型:
函数原型:
1. void arm_shift_q7(
2. const q7_t * pSrc,
3. int8_t shiftBits,
4. q7_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. q7_t in1, in2, in3, in4; /* Temporary input variables */
14. #endif
15.
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 */
25.
26. #if defined (ARM_MATH_DSP)
27. /* Read 4 inputs */
28. in1 = *pSrc++;
29. in2 = *pSrc++;
30. in3 = *pSrc++;
31. in4 = *pSrc++;
32.
33. /* Pack and store result in destination buffer (in single write) */
34. write_q7x4_ia (&pDst, __PACKq7(__SSAT((in1 << shiftBits), 8),
35. __SSAT((in2 << shiftBits), 8),
36. __SSAT((in3 << shiftBits), 8),
37. __SSAT((in4 << shiftBits), 8) ));
38. #else
39. *pDst++ = (q7_t) __SSAT(((q15_t) *pSrc++ << shiftBits), 8);
40. *pDst++ = (q7_t) __SSAT(((q15_t) *pSrc++ << shiftBits), 8);
41. *pDst++ = (q7_t) __SSAT(((q15_t) *pSrc++ << shiftBits), 8);
42. *pDst++ = (q7_t) __SSAT(((q15_t) *pSrc++ << shiftBits), 8);
43. #endif
44.
45. /* Decrement loop counter */
46. blkCnt--;
47. }
48. }
49. else
50. {
51. while (blkCnt > 0U)
52. {
53. /* C = A >> shiftBits */
54.
55. #if defined (ARM_MATH_DSP)
56. /* Read 4 inputs */
57. in1 = *pSrc++;
58. in2 = *pSrc++;
59. in3 = *pSrc++;
60. in4 = *pSrc++;
61.
62. /* Pack and store result in destination buffer (in single write) */
63. write_q7x4_ia (&pDst, __PACKq7((in1 >> -shiftBits),
64. (in2 >> -shiftBits),
65. (in3 >> -shiftBits),
66. (in4 >> -shiftBits) ));
67. #else
68. *pDst++ = (*pSrc++ >> -shiftBits);
69. *pDst++ = (*pSrc++ >> -shiftBits);
70. *pDst++ = (*pSrc++ >> -shiftBits);
71. *pDst++ = (*pSrc++ >> -shiftBits);
72. #endif
73.
74. /* Decrement loop counter */
75. blkCnt--;
76. }
77. }
78.
79. /* Loop unrolling: Compute remaining outputs */
80. blkCnt = blockSize % 0x4U;
81.
82. #else
83.
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 */
95.
96. /* Shift input and store result in destination buffer. */
97. *pDst++ = (q7_t) __SSAT(((q15_t) *pSrc++ << shiftBits), 8);
98.
99. /* Decrement loop counter */
100. blkCnt--;
101. }
102. }
103. else
104. {
105. while (blkCnt > 0U)
106. {
107. /* 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. }
————————————————
版权声明:本文为CSDN博主「Simon223」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Simon223/article/details/105635186
函数描述:
这个函数用于求8位定点数的左移或者右移。
函数解析:
- 第10到87行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
- 第20到48行,如果参数shiftBits是正数,执行左移。
- 第49到77行,如果蚕食shiftBits是负数,执行右移。
- 第79行,函数write_q7x4_ia的原型如下,作用是写入4次8位数据,并将数据地址递增,方便下次写入。
__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位数据,实现代码如下:
#define __PACKq7(v0,v1,v2,v3) ( (((int32_t)(v0) << 0) & (int32_t)0x000000FF) | (((int32_t)(v1) << 8) & (int32_t)0x0000FF00) | (((int32_t)(v2) << 16) & (int32_t)0x00FF0000) | (((int32_t)(v3) << 24) & (int32_t)0xFF000000) )
- 第90到115行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是左移或者右移位数,正数是左移,负数是右移。
- 第3个参数是移位后数据地址。
- 第4个参数是定点数个数,其实就是执行左移或者右移的次数
12.5.4 使用举例
程序设计:
/*********************************************************************************************************** 函 数 名: DSP_Shift* 功能说明: 移位* 形 参: 无* 返 回 值: 无**********************************************************************************************************/static void DSP_Shift(void){ q31_t pSrcA1 = 0x88886666; q31_t pDst1; q15_t pSrcA2 = 0x8866; q15_t pDst2; q7_t pSrcA3 = 0x86; q7_t pDst3; /*求移位*********************************/ arm_shift_q31(&pSrcA1, 3, &pDst1, 1); printf("arm_shift_q31 = %8xrn", pDst1); arm_shift_q15(&pSrcA2, -3, &pDst2, 1); printf("arm_shift_q15 = %4xrn", pDst2); arm_shift_q7(&pSrcA3, 3, &pDst3, 1); printf("arm_shift_q7 = %2xrn", pDst3); printf("***********************************rn");}
实验现象:
这里特别注意Q31和Q7的计算结果,表示负数已经饱和到了最小值。另外注意,对于负数来说,右移时,右侧补1,左移时,左侧补0。
12.6 减法(Vector Sub)
这部分函数主要用于实现减法,公式描述如下:
pDst[n] = pSrcA[n] - pSrcB[n], 0 <= n < blockSize。
12.6.1 函数arm_sub_f32
函数原型:
1. void arm_sub_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. /* Subtract and then store the results in the destination buffer. */
22. vec1 = vld1q_f32(pSrcA);
23. vec2 = vld1q_f32(pSrcB);
24. res = vsubq_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. /* 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. #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. /* 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. 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. #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. /* 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. 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. #else
22. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
23. #endif
24.
25. #if defined (ARM_MATH_DSP)
26. *pDst++ = __QADD(*pSrc++, offset);
27. #else
28. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
29. #endif
30.
31. #if defined (ARM_MATH_DSP)
32. *pDst++ = __QADD(*pSrc++, offset);
33. #else
34. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
35. #endif
36.
37. #if defined (ARM_MATH_DSP)
38. *pDst++ = __QADD(*pSrc++, offset);
39. #else
40. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
41. #endif
42.
43. /* Decrement loop counter */
44. blkCnt--;
45. }
46.
47. /* Loop unrolling: Compute remaining outputs */
48. blkCnt = blockSize % 0x4U;
49.
50. #else
51.
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. #else
65. *pDst++ = (q31_t) clip_q63_to_q31((q63_t) * pSrc++ + offset);
66. #endif
67.
68. /* Decrement loop counter */
69. blkCnt--;
70. }
71.
72. }
函数描述:
这个函数用于求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. 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. #endif
17.
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. #else
30. *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. #endif
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 + 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. #else
58. *pDst++ = (q15_t) __SSAT(((q31_t) *pSrc++ + offset), 16);
59. #endif
60.
61. /* Decrement loop counter */
62. blkCnt--;
63. }
64.
65. }
函数描述:
这个函数用于求8位定点数的乘法。
函数解析:
- 第9到35行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
- 第20行,函数write_q7x4_ia实现一次写入4个Q7格式数据到Q31各种中。
函数__QSUB8实现一次计算4个Q7格式减法。
- 第42到51行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:
- 第1个参数是减数地址。
- 第2个参数是被减数地址。
- 第3个参数是结果地址。
- 第4个参数是数据块大小,其实就是执行减法的次数。
12.6.5 使用举例
程序设计:
/*********************************************************************************************************** 函 数 名: 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 = %frn", pDst[0]); pSrcA1[0] += 1; arm_sub_q31(pSrcA1, pSrcB1, pDst1, 5); printf("arm_sub_q31 = %drn", pDst1[0]); pSrcA2[0] += 1; arm_sub_q15(pSrcA2, pSrcB2, pDst2, 5); printf("arm_sub_q15 = %drn", pDst2[0]); pSrcA3[0] += 1; arm_sub_q7(pSrcA3, pSrcB3, pDst3, 5); printf("arm_sub_q7 = %drn", pDst3[0]); printf("***********************************rn");}
实验现象:
12.7 比例因子(Vector Scale)
这部分函数主要用于实现数据的比例放大和缩小,浮点数据公式描述如下:
pDst[n] = pSrc[n] * scale, 0 <= n < blockSize.
如果是Q31,Q15,Q7格式的数据,公式描述如下:
pDst[n] = (pSrc[n] * scaleFract) << shift, 0 <= n < blockSize.
这种情况下,比例因子就是:
scale = scaleFract * 2^shift.
注意,这部分函数支持目标指针和源指针指向相同的缓冲区
12.7.1 函数arm_scale_f32
函数原型:
1. void arm_scale_f32(2. const float32_t *pSrc,3. float32_t scale,4. float32_t *pDst,5. uint32_t blockSize)6. {7. uint32_t blkCnt; /* Loop counter */8. #if defined(ARM_MATH_NEON_EXPERIMENTAL)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 * 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. }
函数描述:
这个函数用于求32位浮点数的比例因子计算。
函数解析:
- 第8到35行,用于NEON指令集,当前的CM内核不支持。
- 第36到61行,实现四个为一组进行计数,好处是加快执行速度,降低while循环占用时间。
- 第69到78行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理。
函数参数:
- 第1个参数是数据源地址。
- 第2个参数是比例因子
- 第3个参数是结果地址。
- 第4个参数是数据块大小,其实就是执行比例因子计算的次数。
12.7.2 函数arm_scale_q31
函数原型:
函数原型:
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; /* apply shifting */
28. if (in != (out >> 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;
35. if (in != (out >> kShift))
36. out = 0x7FFFFFFF ^ (in >> 31);
37. *pDst++ = out;
38.
39. in = *pSrc++;
40. in = ((q63_t) in * scaleFract) >> 32;
41. out = in << kShift;
42. if (in != (out >> kShift))
43. out = 0x7FFFFFFF ^ (in >> 31);
44. *pDst++ = out;
45.
46. in = *pSrc++;
47. in = ((q63_t) in * scaleFract) >> 32;
48. out = in << kShift;
49. if (in != (out >> kShift))
50. out = 0x7FFFFFFF ^ (in >> 31);
51. *pDst++ = out;
52.
53. /* Decrement loop counter */
54. blkCnt--;
55. }
56. }
57. else
58. {
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. #else
93.
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;
109. if (in != (out >> kShift))
110. out = 0x7FFFFFFF ^ (in >> 31);
111. *pDst++ = out;
112.
113. /* Decrement loop counter */
114. blkCnt--;
115. }
116. }
117. else
118. {
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 <<(shift + 1)多执行了一次左移操作。
相当于2.30格式,转换为2.31格式。
- 第28到29行,做了一个Q31的饱和处理,也就是将2.31格式转换为1.31。
数值的左移仅支持将其左移后再右移相应的位数后数值不变的情况,如果不满足这个条件,那么要对输出结果做饱和运算,这里分两种情况:
out = 0x7FFFFFFF ^ (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. 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. #endif
15.
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 */
25.
26. #if defined (ARM_MATH_DSP)
27. /* read 2 samples from source */
28. in1 = *pSrc++;
29. in2 = *pSrc++;
30.
31. /* Shift the inputs and then store the results in the destination buffer. */
32. #ifndef ARM_MATH_BIG_ENDIAN
33. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in1 << shiftBits), 16),
34. __SSAT((in2 << shiftBits), 16), 16));
35. #else
36. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in2 << shiftBits), 16),
37. __SSAT((in1 << shiftBits), 16), 16));
38. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */
39.
40. /* read 2 samples from source */
41. in1 = *pSrc++;
42. in2 = *pSrc++;
43.
44. #ifndef ARM_MATH_BIG_ENDIAN
45. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in1 << shiftBits), 16),
46. __SSAT((in2 << shiftBits), 16), 16));
47. #else
48. write_q15x2_ia (&pDst, __PKHBT(__SSAT((in2 << shiftBits), 16),
49. __SSAT((in1 << shiftBits), 16), 16));
50. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */
51.
52. #else
53. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);
54. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);
55. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);
56. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);
57. #endif
58.
59. /* Decrement loop counter */
60. blkCnt--;
61. }
62. }
63. else
64. {
65. while (blkCnt > 0U)
66. {
67. /* C = A >> shiftBits */
68.
69. #if defined (ARM_MATH_DSP)
70. /* read 2 samples from source */
71. in1 = *pSrc++;
72. in2 = *pSrc++;
73.
74. /* Shift the inputs and then store the results in the destination buffer. */
75. #ifndef ARM_MATH_BIG_ENDIAN
76. write_q15x2_ia (&pDst, __PKHBT((in1 >> -shiftBits),
77. (in2 >> -shiftBits), 16));
78. #else
79. write_q15x2_ia (&pDst, __PKHBT((in2 >> -shiftBits),
80. (in1 >> -shiftBits), 16));
81. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */
82.
83. /* read 2 samples from source */
84. in1 = *pSrc++;
85. in2 = *pSrc++;
86.
87. #ifndef ARM_MATH_BIG_ENDIAN
88. write_q15x2_ia (&pDst, __PKHBT((in1 >> -shiftBits),
89. (in2 >> -shiftBits), 16));
90. #else
91. write_q15x2_ia (&pDst, __PKHBT((in2 >> -shiftBits),
92. (in1 >> -shiftBits), 16));
93. #endif /* #ifndef ARM_MATH_BIG_ENDIAN */
94.
95. #else
96. *pDst++ = (*pSrc++ >> -shiftBits);
97. *pDst++ = (*pSrc++ >> -shiftBits);
98. *pDst++ = (*pSrc++ >> -shiftBits);
99. *pDst++ = (*pSrc++ >> -shiftBits);
100. #endif
101.
102. /* Decrement loop counter */
103. blkCnt--;
104. }
105. }
106.
107. /* Loop unrolling: Compute remaining outputs */
108. blkCnt = blockSize % 0x4U;
109.
110. #else
111.
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 */
123.
124. /* Shift input and store result in destination buffer. */
125. *pDst++ = __SSAT(((q31_t) *pSrc++ << shiftBits), 16);
126.
127. /* Decrement loop counter */
128. blkCnt--;
129. }
130. }
131. else
132. {
133. while (blkCnt > 0U)
134. {
135. /* 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实现的话,如下:
#define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0x0000FFFF) |
(((int32_t)(ARG2) << ARG3) & (int32_t)0xFFFF0000) )
函数write_q15x2_ia的原型如下:
__STATIC_FORCEINLINE void write_q15x2_ia (
q15_t ** pQ15,
q31_t value)
{
q31_t val = value;
memcpy (*pQ15, &val, 4);
*pQ15 += 2;
}
作用是写入两次Q15格式数据,组成一个Q31格式数据,并将数据地址递增,方便下次写入。
第118到143行,四个为一组剩余数据的处理或者不采用四个为一组时数据处理
函数参数:
第1个参数是数据源地址。
第2个参数是比例因子。
第3个参数是移位参数,正数表示右移,负数表示左移。
第4参数是结果地址。
第5参数是数据块大小,其实就是执行比例因子计算的次数。
12.7.4 函数arm_scale_q7
函数原型:
1. void arm_scale_q7(
2. const q7_t * pSrc,
3. q7_t scaleFract,
4. int8_t shift,
5. q7_t * pDst,
6. uint32_t blockSize)
7. {
8. uint32_t blkCnt; /* Loop counter */
9. int8_t kShift = 7 - shift; /* Shift to apply after scaling */
10.
11. #if defined (ARM_MATH_LOOPUNROLL)
12.
13. #if defined (ARM_MATH_DSP)
14. q7_t in1, in2, in3, in4; /* Temporary input variables */
15. q7_t out1, out2, out3, out4; /* Temporary output variables */
16. #endif
17.
18. /* Loop unrolling: Compute 4 outputs at a time */
19. blkCnt = blockSize >> 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. #else
41. *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. #endif
46.
47. /* Decrement loop counter */
48. blkCnt--;
49. }
50.
51. /* Loop unrolling: Compute remaining outputs */
52. blkCnt = blockSize % 0x4U;
53.
54. #else
55.
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 使用举例
程序设计:
/*********************************************************************************************************** 函 数 名: 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 = %frn", pDst[0]); scale1 += 1; arm_scale_q31(pSrcA1, scale1, 0, pDst1, 5); printf("arm_scale_q31 = %xrn", pDst1[0]); scale2 += 1; arm_scale_q15(pSrcA2, scale2, 0, pDst2, 5); printf("arm_scale_q15 = %xrn", pDst2[0]); scale3 += 1; arm_scale_q7(pSrcA3, scale3, 0, pDst3, 5); printf("arm_scale_q7 = %xrn", pDst3[0]); printf("***********************************rn");}
实验现象:
12.8 实验例程说明(MDK)
配套例子:
V6-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小节。
程序设计:
系统栈大小分配:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/*
*********************************************************************************************************
* 函 数 名: bsp_Init
* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
/*
STM32F407 HAL 库初始化,此时系统用的还是F407自带的16MHz,HSI时钟:
- 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
- 设置NVIV优先级分组为4。
*/
HAL_Init();
/*
配置系统时钟到168MHz
- 切换使用HSE。
- 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
*/
SystemClock_Config();
/*
Event Recorder:
- 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
- 默认不开启,如果要使能此选项,务必看V5开发板用户手册第8章
*/
#if Enable_EventRecorder == 1
/* 初始化EventRecorder并开启 */
EventRecorderInitialize(EventRecordAll, 1U);
EventRecorderStart();
#endif
bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
bsp_InitTimer(); /* 初始化滴答定时器 */
bsp_InitUart(); /* 初始化串口 */
bsp_InitExtIO(); /* 初始化扩展IO */
bsp_InitLed(); /* 初始化LED */
}
主功能:
主程序实现如下操作:
启动一个自动重装软件定时器,每100ms翻转一次LED2。
按下按键K1, DSP求相反数运算。
按下按键K2, DSP求偏移运算。
按下按键K3, DSP求移位运算。
按下摇杆OK键, DSP求减法运算。
按下摇杆上键, DSP比例因子运算。
/*
*********************************************************************************************************
* 函 数 名: 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)
配套例子:
V6-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小节。
程序设计:
系统栈大小分配:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/*
*********************************************************************************************************
* 函 数 名: bsp_Init
* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
/*
STM32F407 HAL 库初始化,此时系统用的还是F407自带的16MHz,HSI时钟:
- 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
- 设置NVIV优先级分组为4。
*/
HAL_Init();
/*
配置系统时钟到168MHz
- 切换使用HSE。
- 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
*/
SystemClock_Config();
/*
Event Recorder:
- 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
- 默认不开启,如果要使能此选项,务必看V5开发板用户手册第8章
*/
#if Enable_EventRecorder == 1
/* 初始化EventRecorder并开启 */
EventRecorderInitialize(EventRecordAll, 1U);
EventRecorderStart();
#endif
bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
bsp_InitTimer(); /* 初始化滴答定时器 */
bsp_InitUart(); /* 初始化串口 */
bsp_InitExtIO(); /* 初始化扩展IO */
bsp_InitLed(); /* 初始化LED */
}
主功能:
主程序实现如下操作:
按下按键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;
}
}
}
}
12.10 总结
DSP基础函数就跟大家讲这么多,希望初学的同学多多的联系,并在自己以后的项目中多多使用,效果必将事半功倍。
举报