I am currently doing an internship. Where I was given a side project in which I am required to create a sawtooth wave using a DAC Tester board that utilizes the AK4495. Initially, I need to establish communication between the DAC and microcontroller and generate a sawtooth. I have figured out how to read and write to the registers of the DAC. I need help generating the sawtooth through the i2s protocol.
The sawtooth must be generated by the microcontroller or it can be sent from another source (e. g. a PC)?
The sawtooth wave is supposed to have a fixed frequency or it has to be adjustable? What is the pretended sample rate?
The sawtooth wave is supposed to have a fixed frequency or it has to be adjustable? What is the pretended sample rate?
Does the microcontroller run Linux? If so, then it should already have a driver. You can call ALSA to output sound.
A DAC can only generate bandwidth-limited sawtooths. An inexpensive function generator would not be limited to 20KHz harmonics.
Ed
A DAC can only generate bandwidth-limited sawtooths. An inexpensive function generator would not be limited to 20KHz harmonics.
Ed
Should be generated through the microcontroller. Nothing else is specifiedThe sawtooth must be generated by the microcontroller or it can be sent from another source (e. g. a PC)?
The sawtooth wave is supposed to have a fixed frequency or it has to be adjustable? What is the pretended sample rate?
Not entirely my field but in some ways relates.
On the face of things via a quick look you need to read up on DCM and PCM audio encoding and produce an encoding of a sawtooth using one or the other. Probably best to store values in a table. I'm assuming this will be done with a microcontroller. The table could be generated by values in the source code or I assume calculated by the microcontroller.
The table approach helps speed, something I suspect you will want. Imagine 2 timebases. One slow to allow things to be started and stopped. The other fast and interrupt driven to meet the bit rate needs.All that one needs to do is read a table via an index, present it where needed and then advance the index counter. Ideally that would roll over to zero itself.
Then a set up routine that might even create the table. The slower routine may not be needed. Some code might just sit waiting for start or stop signals. Using interrupts for that gets more complicated as interrupts can only be turned off for a time depending on the rate of the fast one - that will still alter the bit rate period now and again.
😉 Hope this helps your thoughts. I've always thought of this as hand cranking a serial line. For all I know you may need to handle several when it's all running. I'm assuming the chip can just be set up and driven via one.
On the face of things via a quick look you need to read up on DCM and PCM audio encoding and produce an encoding of a sawtooth using one or the other. Probably best to store values in a table. I'm assuming this will be done with a microcontroller. The table could be generated by values in the source code or I assume calculated by the microcontroller.
The table approach helps speed, something I suspect you will want. Imagine 2 timebases. One slow to allow things to be started and stopped. The other fast and interrupt driven to meet the bit rate needs.All that one needs to do is read a table via an index, present it where needed and then advance the index counter. Ideally that would roll over to zero itself.
Then a set up routine that might even create the table. The slower routine may not be needed. Some code might just sit waiting for start or stop signals. Using interrupts for that gets more complicated as interrupts can only be turned off for a time depending on the rate of the fast one - that will still alter the bit rate period now and again.
😉 Hope this helps your thoughts. I've always thought of this as hand cranking a serial line. For all I know you may need to handle several when it's all running. I'm assuming the chip can just be set up and driven via one.
You should also look at what the microcontroller can do. Transferring table data is a useful technique and it may have something that does that built in. Useful when output compares are used.
Generating a digital sawtooth just requires a modulo something addition, if band limiting and dithered rounding are not required. If the peak-to-peak value is 2n - 1, you can just let a fixed-point binary number overflow. A two's complement binary number will automatically overflow from positive to negative when the sign bit becomes 1.
Regarding I2S, see https://www.nxp.com/docs/en/user-manual/UM11732.pdf , serializing the data may take most of the CPU cycles if there is no hardware shift register available. That might be a problem for slow microcontrollers.
Regarding I2S, see https://www.nxp.com/docs/en/user-manual/UM11732.pdf , serializing the data may take most of the CPU cycles if there is no hardware shift register available. That might be a problem for slow microcontrollers.
If the microcontroller IDE allows programing in C/C++ language a sawtooth waveform generator algorithm can be defined as follows:
#include <DueTimer.h>
/* Example constants/variables */
const uint8_t sampleRate = 48000; /* Hz */
const uint8_t bitsPerSample = 16;
uint8_t frequency = 1000; /* Hz */
double amplitude = 1.4142; /* V */
double offset = 0.0; /* V */
/* DAC Parameters (example) */
double maxAmplitudeDAC = 1.4142; /* V */
/* Function variables */
int16_t y1 = 0;
uint16_t m = 0;
uint32_t n = 0
uint32_t N = 0;
int16_t b1 = 0;
int16_t b2 = 0;
bool posSlope = true;
void sawtoothWaveGen() {
if (posSlope) {
y1 = m * n / (N - 1) + b1;
}
else {
y1 = - m * n / (N - 1)) + b2;
}
++n;
if (n >= N)
n = 0;
setDACValue(y1);
}
void setup() {
m = uint16_t(amplitude / maxAmplitudeDAC * (pow(2, bitsPerSample) - 1));
N = uint32_t(sampleFrequency / frequency);
b1 = int16_t((offset - amplitude) / maxAmplitudeDAC * (pow(2, bitsPerSample)/2 - 1));
b2 = int16_t((offset + amplitude) / maxAmplitudeDAC * (pow(2, bitsPerSample)/2 - 1));
Timer1.attachInterrupt(sawtoothWaveGen); //Supply samples to DAC
Timer1.setFrequency(sampleRate);
Timer1.start();
}
void loop(){}
I have never tested this code, so I may have made some mistake(s), please check every single line for errors.
This was written for Arduino Due at low sampling frequencies (1 kHz or less) and with DACs based on resistive ladders (e. g. R2R, segmented, Kelvin-Varley divider), with SPI or I2C interfaces, it may differ in other platforms, but at least you can have an idea of a possible algorithm. Instead of generating the sawtooth inside the interrupt function it may be preferable to generate it at startup and store it's values in an array. This code uses interrupts and a timer.
#include <DueTimer.h>
/* Example constants/variables */
const uint8_t sampleRate = 48000; /* Hz */
const uint8_t bitsPerSample = 16;
uint8_t frequency = 1000; /* Hz */
double amplitude = 1.4142; /* V */
double offset = 0.0; /* V */
/* DAC Parameters (example) */
double maxAmplitudeDAC = 1.4142; /* V */
/* Function variables */
int16_t y1 = 0;
uint16_t m = 0;
uint32_t n = 0
uint32_t N = 0;
int16_t b1 = 0;
int16_t b2 = 0;
bool posSlope = true;
void sawtoothWaveGen() {
if (posSlope) {
y1 = m * n / (N - 1) + b1;
}
else {
y1 = - m * n / (N - 1)) + b2;
}
++n;
if (n >= N)
n = 0;
setDACValue(y1);
}
void setup() {
m = uint16_t(amplitude / maxAmplitudeDAC * (pow(2, bitsPerSample) - 1));
N = uint32_t(sampleFrequency / frequency);
b1 = int16_t((offset - amplitude) / maxAmplitudeDAC * (pow(2, bitsPerSample)/2 - 1));
b2 = int16_t((offset + amplitude) / maxAmplitudeDAC * (pow(2, bitsPerSample)/2 - 1));
Timer1.attachInterrupt(sawtoothWaveGen); //Supply samples to DAC
Timer1.setFrequency(sampleRate);
Timer1.start();
}
void loop(){}
I have never tested this code, so I may have made some mistake(s), please check every single line for errors.
This was written for Arduino Due at low sampling frequencies (1 kHz or less) and with DACs based on resistive ladders (e. g. R2R, segmented, Kelvin-Varley divider), with SPI or I2C interfaces, it may differ in other platforms, but at least you can have an idea of a possible algorithm. Instead of generating the sawtooth inside the interrupt function it may be preferable to generate it at startup and store it's values in an array. This code uses interrupts and a timer.
Last edited:
The simple approach of generating an exact sawtooth will produce aliasing.
A bandwidth-limited sawtooth can be created by summing the harmonics, stopping at 20KHz or until the CPU cycles are exhausted. A 1KHz sawtooth will look crummy, but that is the best one can do in 20KHz bandwidth.
Ed
A bandwidth-limited sawtooth can be created by summing the harmonics, stopping at 20KHz or until the CPU cycles are exhausted. A 1KHz sawtooth will look crummy, but that is the best one can do in 20KHz bandwidth.
Ed
Attachments
In that case it is better to produce it in MATLAB or so and store it in memory as a constant array of integers. I think it will be a bit cumbersome to produce it in the microcontroller unless a library is already available.
Since the application and the requirements are unknown, I don't know whether a waveform that looks crummy due to bandwidth limitations is to be preferred over one that looks crummy due to aliasing and bandwidth limitations...
By the way, if the ratio of the sample rate to the signal frequency can be chosen to be an integer, aliasing at least won't cause any new frequencies to occur, it can then at most disturb the ratio of the amplitudes of the harmonics.
By the way, if the ratio of the sample rate to the signal frequency can be chosen to be an integer, aliasing at least won't cause any new frequencies to occur, it can then at most disturb the ratio of the amplitudes of the harmonics.
- Home
- Source & Line
- Digital Line Level
- Generating a sawtooth using AK4495