PWM As DAC - Secret of Arduino

by Lithium-ion in Circuits > Arduino

474 Views, 0 Favorites, 0 Comments

PWM As DAC - Secret of Arduino

mini_IMG_3069.jpg

Let me show how the signal processing works, The basic concept of PWM and Low pass filter can generate a signal comparable to DAC.

Supplies

Components Required:

  • Arduino Nano
  • 100K Resistor
  • 100nF capacitor
  • Breadboard and wiring
  • Power supply

Story:

Whenever we need analog/sound out from a microcontroller or digital processing unit then DAC(Digital to Analog Converter) is used. Which works as a basic digital to analog conversion unit. Basically DAC works on exactly the opposite phenomenon of ADC (Analog to Digital Converter). As it is cleared from name, DAC - a digital signal will be given as input and the Analog signal will be the output. This digital signal is fed into the DAC input register. After filling up the whole input register, it enables the conversion based on the input value and produces an analog signal corresponding to that, it may be done serially or in parallel.

And in this conversion the timing plays a very crucial role, because every DAC has conversion and settling time. So there will be a little delay from input to output, maybe in microseconds. This article is sponsored by PCBWay, to get custom Arduino please visit this link. PCBWay provide most affordable PCB prototyping and assembly services. Get the full Quote of your custom PCB now.

PWM As DAC:

image_op.jpg

Usually in small microcontrollers the DAC feature is not available because of low cost prototyping and the signal processing is not required every time. If you don’t want to use external DAC modules then PWM function as DAC is the best low cost alternative to use. PWM can be modulated as per the signal we want to produce. PWM is nothing but a varying duty cycle constant time period wave, the duty can be controlled by sending an array of values in the Arduino PWM generator or by an external Potentiometer.

Usually in DAC the conversion is done when the input register is filled from the data but here in PWM the mapping is done by programming the Arduino to produce a particular signal. I will show two examples, to generate Sinewave by sending array of values in PWM and in second to generate a stable DC signal out of PWM using potentiometer.

Judging the Quality of DAC:

mini_IMG_3069.jpg

There are few figures of merit I want to discuss. When talking about signal conversion from one to another form. In this way it is easy to compare the PWM as DAC and the actual DAC. There are different algorithms and architectures but we want to discuss only Resolution and conversion time.

Resolution depends on step size, shows how closely two values of signal it is able to differentiate from each other. Bit size shows the total number of steps it will take to get max output. Let's take a 10 Bit DAC for example and the total steps are calculated as 2^10-1, which are 1024 steps in total (including zero). And Resolution is given by Vref/1023, where Vref is the taken as the maximum voltage value of the signal we are measuring. If we are using a 8 bit DAC there will be 256 total steps. DAC can take 256 steps to go from zero to the max value, hence defining the resolution of DAC.

Working of PWM:

My Video3.gif

With Pulse-Width Modulation (PWM), An analogue voltage is generated by varying the proportion of the duty cycle of square wave. By decreasing the On/off time of a time period of PWM we can generate different signals and tones.

In DAC 10bit means a total of 1024 steps, but here in PWM 10bit means that the whole time period of square wave is broken into 1024 parts, which overall increases the resolution.

But you can see the signal is not continues, it's not a analog signal. But we need a continuous time signal out of PWM. To achieve this we can either connect a capacitor across the output, or I would recommend to add a 2nd order low pass filter to PWM output. Low pass filter or capacitor acts as an averaging circuit, takes the average of the PWM hence give a smooth analog signal in output.

Data, PWM and Sine Function Used Here:

I want to generate a Sine wave which is known as a very fundamental analog signal. So I am using 50 samples of 10 bit data (array) and 8 bit PWM value mapping function. Also I am sharing this formula to compute the number of samples and data values of sine waves. The formula only works with sine function, if any special signal is required then the general formula is required for that specific signal.

=512+511*sin(2*3.14*SampleNumber/TotalSample)

Using the above formula we can make a look up table to produce a specific sine wave please download the Excel sheet for the proper calculation from here. When calculating the final look up (array values) decrease the decimal digits through Excel functions.

Python Program to Generate Array Values:

import math

# Fill the number of samples
num_points = 1000

# Calculate the step size
step_size = 2 * math.pi / num_points

# Initialize the lookup table
sine_lookup_table = []

# Generate the sine wave values and fill the lookup table
for i in range(num_points):
# Calculate the sine value
sine_value = math.sin(i * step_size)

# Scale the sine value to fit within the 12-bit range (0 to 4095)
# for 10bit replace with 511.5
# for 12 bit replace with 2147.5
# for 15 bit 16383.5
scaled_value = int((sine_value + 1) * 16383.5)

# Append the scaled value to the lookup table
sine_lookup_table.append(scaled_value)

# Print the sine wave lookup table
print(sine_lookup_table)

Making of a Signal Using Array Value:

mini_IMG_3058.jpg

As I said before we need digital data to be converted, which in DAC is given in the input DAC register but here in PWM, the data is given as an array consisting of 10 bit values. Which then converted to 8 bit mode of Arduino PWM using the mapping function given below. And the concept is very clear from the sketch given below. Here the resolution of the output wave depends upon the number of samples given to PWM for making a full wave. As I am using 50 samples to define one full time period. More the number of samples then more accurate will be the wave. And the delay between the number of samples sent, defines the frequency of signal.

outputPWM = map(data, 0, 1024, 0, 255);   // for 8 bit PWM value mapping.

Why I Used 10 Bit Value:

image_op.jpg
Sine wave Schematics.png

You can also take the values from 0 to 255 for data instead of 1023, then there is no need of mapping. But I have given the general formula for calculation so if any microcontroller which support higher order PWM then the calculation is easy to perform. Using a higher bit data (10 bit data value) means that you can take more samples which are more close to each other. Increasing number of samples using 10-bit PWM may improve resolution but the time taken by microcontroller to compute for output also increases and limits the overall frequency to a very lower value.

Code:

const int analogOutPin = 9;
int outputPWM = 0;
int data;
unsigned int inp[50]={576,
639,
700,
758,
812,
862,
906,
943,
974,
998,
1014,
1022,
1022,
1014,
998,
975,
944,
906,
862,
813,
759,
701,
640,
577,
513,
449,
386,
325,
267,
212,
163,
119,
81,
50,
26,
10,
2,
2,
10,
26,
49,
80,
117,
161,
210,
265,
322,
383,
446,
510,
};

void setup() {
Serial.begin(9600);
}

void loop() {
int i;
for(i=0; i<50; i++){
data = inp[i];
outputPWM = map(data, 0, 1024, 0, 255);
delay(10);
analogWrite(analogOutPin, outputPWM);
}
}

Here is the Sine Wave I got from the above signal. And as I am using 50 samples, the frequency of the signal will be very low because of high computation samples and delay. The code can be optimized for high frequency by minimizing the delay function. The output can be seen on the Digital pin number 9 of the Arduino.

Downloads

Getting the PWM Output:

mini_IMG_3056.jpg
mini_IMG_3061.jpg

The charging and discharging time of capacitor is very crucial in LPF, I designed this circuit keeping all the considerations. Please use the same values for the best output.

Let me show how with the change in duty cycle the average value of signal is changing. I will give the whole idea of converting a Digital signal into analog with PWM function of Arduino.

Let me feed a 2.5 volt signal into Arduino ADC which is interpreted as 512 by it's 10 bit ADC (Formula: (2.5/5)*1023). And in Arduino I am using 8 bit mode, hence mapping function is used to map 0-1023 value to 0-255. Now the PWM will output a square wave signal but with 50% duty cycle and when given to a LPF we get 2.5v value back. Get all the required code and schematics material from my Github repository.

Generation of an Arbitrary DC Signal:

My Video2.gif
My Video1.gif
mini_IMG_3065.jpg
Screenshot_2024_06_15-1.png

Here is the second example of converting the PWM into stable DC voltage, the same circuit is used in addition this time the value is not given through array. The value is feed into the Arduino externally using potentiometer through ADC pin. Now ADC interpret the position of potentiometer on a scale of 0-1023 and convert that signal into the respective PWM. As the potentiometer is rotated the PWM values will change. When given to a Low pass filter it will convert the signal into Stable analog signal.

Code:

const int analogInPin = A0;  // Analog input pin that the potentiometer is attached to
const int analogOutPin = 9; // Digital pin 9
int potValue = 0; // value read from the pot
int outputPWM = 0; // value output to the PWM (analog out)
int Duty = 0;
int delaytime = 1000;

void setup() {
Serial.begin(9600);
}

void loop() {
// read the analog in value:
potValue = analogRead(analogInPin);
// map it to the range of the analog out:
outputPWM = map(potValue, 0, 1023, 0, 255);
Duty = map(outputPWM, 0, 255, 0, 100 );
// change the analog out value:
analogWrite(analogOutPin, outputPWM);

// print the results to the Serial Monitor:
Serial.print("Pot Value = ");
Serial.print(potValue);
Serial.print("\t Output PWM = ");
Serial.print(outputPWM);
Serial.print("\t Duty = ");
Serial.println(Duty);

delay(delaytime); // change the frequency
}

Downloads

Design You Own Arduino:

Screenshot_2024_06_14-3.png

In past, I have designed some Arduino Boards which can be seen in the project. These are compatible and better featured boards rather than buying a cheap non compatible now these custom boards can be designed very easily. I arranged the parts form external source and the PCB boards from PCBWAY. These boards are very well designed, I used USB type C and Compatible USB to TTL chips to lower down the cost. The functioning and full review is posted in the another article. To Get your custom Arduino design visit the PCBWay Quote now page through this link.