Snippets

Yuchong Li qrMp9: Untitled snippet

Created by Yuchong Li last modified
//
//  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)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.