Snippets
Created by
Yuchong Li
last modified
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | //
// main.c
// multi_tone
//
// Created by NT on 3/28/16.
// Copyright © 2016 nt. All rights reserved.
//
#include <stdio.h>
// 这些是 CORDIC 算法用到的常数, 不需要改
#define cordic_1K 0x0000062F
#define half_pi 0x00001000
#define MUL 2608.000000
#define CORDIC_NTAB 16
int cordic_ctab [] = {
0x00000800, 0x000004B9, 0x0000027E, 0x00000144,
0x000000A2, 0x00000051, 0x00000028, 0x00000014,
0x0000000A, 0x00000005, 0x00000002, 0x00000001,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
};
/**
* CORDIC(Coordinate Rotation Digital Computer, 坐标旋转数字计算) 算法
* 一般用在低性能的定点处理器中实现非线性函数的逼近, 例如三角函数, 开方等
*
* @param theta 输入角度, 0~4096, 对应 0-pi
* @param n 位数
*
* @return Sin 输出, 范围 0~4096, 对应 0.0~1.0
*/
int cordic(int theta, int n)
{
int k, d, tx, ty, tz;
int x = cordic_1K, y = 0, z = theta;
n = (n > CORDIC_NTAB) ? CORDIC_NTAB : n;
for (k = 0; k < n; k++)
{
d = (z >= 0) ? 0 : -1;
tx = x - (((y >> k) ^ d) - d);
ty = y + (((x >> k) ^ d) - d);
tz = z - ((cordic_ctab[k] ^ d) - d);
x = tx;
y = ty;
z = tz;
}
return ((y * 3216) + 8388608) >> 12;
}
/**
* 做一些三角函数的周期变换, 然后调用 CORDIC 算法
*
* @param theta 输入角度, -8192~8192, 对应 0-2pi
*
* @return Sin 输出, 范围 0~4096, 对应 0.0~1.0
*/
int fast_trigonometry(int theta)
{
if(theta > 4096)
{
theta = 8192 - theta;
}
else if(theta < -4096)
{
theta = -(8192 + theta);
}
return cordic(theta, 15);
}
// 这个是你的 tone 的个数
#define NUMBER_TONES 3
// 这个是你的 timer 的频率, 也就是 DAC 的采样率
#define LOOP_FREQUENCY 1000
// 这些常数不需要改
#define PHASE_MAX_SCALED 33554432
#define MAKE_FREQUENCY(freq) (67108864 / LOOP_FREQUENCY * (freq))
int main(int argc, const char * argv[])
{
/**
* 这里用的是相位累加法而不是 sin(2pi*f*t) 的这种形式
* 算法如下:
* 首先计算 2pi*f 的值, 左移位 12 位, 也就是扩大 4096 倍
* 然后每个频率有一个相位累加器 phase, 从 0 开始, 不断地与 2pi*f 相加
* 计算输出 = sin(phase)
* 累加器的值大于 2pi 以后, 将它清零, 从头开始
*
* 有多个频率时, 将所有输出相加然后除以频率的个数, 最后归一化到 0~255 的范围内.
*
* 整个算法只有整数加减法, 整数乘法, 整数移位以及一个整数除法.
* CORDIC 算法你可以理解成一个炒鸡炒鸡炒鸡快的三角函数, 仅此而已.
*/
int i;
// 这些是你需要的初始频率, 也可以动态更改, 例如这样可以把第 0 个频率改成 150Hz:
// frequencies[0] = MAKE_FREQUENCY(150);
int frequencies[] = {
MAKE_FREQUENCY(50),
MAKE_FREQUENCY(100),
MAKE_FREQUENCY(300)
};
// 注意动态更改频率是比较慢的操作, 因为有一个除法
// 这些是每个对应频率的幅度, 最大值 255, 最小值 0.
int amplitudes[] = { 255, 255, 255 };
// 分配相位累加器内存, 并且清零相位
int phases[NUMBER_TONES];
for(i = 0; i < NUMBER_TONES; i++)
{
phases[i] = 0;
}
// 这个 for 循环在你的 microcontroller 里可以改成死循环, 这里只是为了演示
for(i = 0; i < 200; i++)
{
// 这个就是最后要写入 DAC 的值, 范围是 0~255.
int dac_value = 0;
int tone;
// 对于每一个 tone, 做如下操作
for(tone = 0; tone < NUMBER_TONES; tone++)
{
// 累计 tone 的相位, 并且达到 2pi 以后清零
phases[tone] += frequencies[tone];
if(phases[tone] > PHASE_MAX_SCALED)
{
// 这里清零相位
phases[tone] = -(PHASE_MAX_SCALED << 1) + phases[tone];
}
// 累加每个 tone 的幅度, 注意相位被左移了 12 位, 所以现在移回来
dac_value += fast_trigonometry(phases[tone] >> 12) * amplitudes[tone];
}
// 右移 12 位, 移入 0~255 的 DAC 范围, 最后除以 tone 的个数
dac_value = (dac_value >> 12) / NUMBER_TONES;
// 这句改成写入 DAC
printf("%d\n", dac_value);
}
}
|
Comments (0)
You can clone a snippet to your computer for local editing. Learn more.