Friday, August 11, 2017

Generating a sinewave table for my DAC

Generating a sinewave table for my DAC


Unlike other AFSK implementations, I don't actually need a sinewave table with a lot (like 512) of values in it.  In fact, I'll be using just 24 values in my sinewave table for my DAC.

I decided to write a program to generate my table.  While I was at it, I decided that I would have it build in a scaling factor as a "#define".  By changing that one #define, I can change the amplitude of the sinewave with a recompile, rather than having to re-generate a whole new table.

Sinewave.pl

#!/usr/bin/perl
# Usage: sinewave.pl samples
# Generates a sinewave table containing the specified number of samples.
# For example, "sinewave.pl 36" yields:
#       #DEFINE DAC_PCT 100
#       #DEFINE SINE_RES        36
#       const uint16_t sinewave[SIN_RES] = { .... }
# The DAC_PCTxx define is a percentage multipler against all of the values in the table.  Useful if your DAC output is too high.
# By changing this one DEFINE in your code, you can scale the sinewave table at compile time.  Note, that the DAC values will remain
# centered on the sine(0) value, and will scale "inward" toward that number.  For example, on a 12 bit DAC, sin(0) is going to be at 2048.
# Even if you scale up and down, the sine wave will be centered around 2048.
#
# The output of this program is actually a C program which an be compiled to confirm the table is built properly.  When satisfied,
# you can just copy/paste the sinewave table and the few necessary defines into your own program.
use strict;
my $DACBITS = 12;                       # Number of bits the DAC (or pwm) is using
my $COLUMNS_PER_LINE = 3;               # Number of values per row
if ($#ARGV != 0) {
        print STDERR "Usage: $0 samples\n";
        exit 4;
}
my $center_value = 2 ** $DACBITS / 2;   # The peak 2 peak of the sine wave is twice $center_value, so we divide by two.
my $PI = 3.1415926;
my $samples = $ARGV[0];
my $degrees = 0;
my $column;
my $value;
my $row;
print("\#include \"stdint.h\"\n");
print("\#include <stdio.h>\n");
print("main()\n");
print("{\n");

print("\#define DAC_PCT 100             // Percentage of DAC output\n");
print("\#define SINE_RES        $samples\n");
print("const uint16_t sinewave\[SINE_RES\] = {\n");
for ($row = 0; $row < ($samples / $COLUMNS_PER_LINE); $row++) {
        print("\t");
        for ($column = 0; (($degrees < 360) &&  ($column < $COLUMNS_PER_LINE)); $degrees += 360/$samples) {
                $value = int($center_value * sin($degrees/180.0 * $PI));                # Perl sin() is in Radians.  Convert.
                print("(${value} * DAC_PCT / 100) + $center_value, ");
                $column++;
        }
        print("\n");
}
print("};\n");
print("int      i;\n\n");
print("for (i = 0; i < SINE_RES; i++) {\n");
print("\t");
print("printf(\"\%d\\n\", sinewave[i]);");
print("\n"); print("\t}\n"); print("}\n");

What it does

Rather than just creating the sinewave table, it actually generates a short C program which can be compiled in Unix to confirm that the table is properly formed, and that the values look correct.


Sample run for 24 data points

mqh1@debian:~/wisp2$ ./sinewave.pl 24
#include "stdint.h"
#include <stdio.h>
main()
{
#define DAC_PCT 100             // Percentage of DAC output
#define SINE_RES        24
const uint16_t sinewave[SINE_RES] = {
        (0 * DAC_PCT / 100) + 2048, (530 * DAC_PCT / 100) + 2048, (1023 * DAC_PCT / 100) + 2048,
        (1448 * DAC_PCT / 100) + 2048, (1773 * DAC_PCT / 100) + 2048, (1978 * DAC_PCT / 100) + 2048,
        (2047 * DAC_PCT / 100) + 2048, (1978 * DAC_PCT / 100) + 2048, (1773 * DAC_PCT / 100) + 2048,
        (1448 * DAC_PCT / 100) + 2048, (1024 * DAC_PCT / 100) + 2048, (530 * DAC_PCT / 100) + 2048,
        (0 * DAC_PCT / 100) + 2048, (-530 * DAC_PCT / 100) + 2048, (-1023 * DAC_PCT / 100) + 2048,
        (-1448 * DAC_PCT / 100) + 2048, (-1773 * DAC_PCT / 100) + 2048, (-1978 * DAC_PCT / 100) + 2048,
        (-2047 * DAC_PCT / 100) + 2048, (-1978 * DAC_PCT / 100) + 2048, (-1773 * DAC_PCT / 100) + 2048,
        (-1448 * DAC_PCT / 100) + 2048, (-1024 * DAC_PCT / 100) + 2048, (-530 * DAC_PCT / 100) + 2048,
};
int     i;
for (i = 0; i < SINE_RES; i++) {
        printf("%d\n", sinewave[i]);
        }
}
mqh1@debian:~/wisp2$

Compile and run the output


mqh1@debian:~/wisp2$ ./sinewave.pl 24 > t.c
mqh1@debian:~/wisp2$ cc t.c
mqh1@debian:~/wisp2$ ./a.out
2048
2578
3071
3496
3821
4026
4095
4026
3821
3496
3072
2578
2048
1518
1025
600
275
70
1
70
275
600
1024
1518
mqh1@debian:~/wisp2$

In the output above, you can see that it's got a sinewave centered at 2048, with values ranging from (almost) 0 to 4095.

Re-scaling the sine wave

To change the scale out the sinewave, I simply modify the #define

#define DAC_PCT 50              // Percentage of DAC output

This will set the DAC amplitude at 50%

mqh1@debian:~/wisp2$ vi t.c
mqh1@debian:~/wisp2$ cc t.c
mqh1@debian:~/wisp2$ ./a.out
2048
2313
2559
2772
2934
3037
3071
3037
2934
2772
2560
2313
2048
1783
1537
1324
1162
1059
1025
1059
1162
1324
1536
1783
Note that the center is still around 2048, but the amplitude only goes up and down by about 1024.



No comments:

Post a Comment