#include <cstdio>
#include <cstdint>
#include <cmath>
/*
Examples for dealing with ADCs & DACs, including how to work with
voltages and how to adjust readings to account for offset and gain
errors.
*/
/*
Examples for converting between code points and voltage.
Code points are the integer readings from an ADC or the integer
output register for a DAC. For example, if you have a 10-bit DAC,
then the code points are from 0 to 1023.
*/
#define RESOLUTION_8_BIT 256
#define RESOLUTION_10_BIT 1024
#define RESOLUTION_12_BIT 4096
#define RESOLUTION_14_BIT 16384
#define RESOLUTION_16_BIT 65536
/*
Convert a code point to a voltage.
This is useful for ADCs - if you know the range of the ADC's
measurements you can use this to determine which voltage
an analogRead() result corresponds to.
*/
float code_points_to_volts(uint32_t code_point, uint32_t resolution = RESOLUTION_12_BIT, float min_voltage = 0.0f, float max_voltage = 3.3f) {
float voltage_range = max_voltage - min_voltage;
float normalized_value = (float)(code_point) / (float)(resolution - 1);
return min_voltage + (voltage_range * normalized_value);
}
/*
Convert a voltage to a code point.
This is useful for DACs - if you want to output a specific
voltage, then you can use this to determine which code point
to pass to analogWrite()
*/
uint32_t volts_to_code_points(float volts, uint32_t resolution = RESOLUTION_12_BIT, float min_voltage = 0.0f, float max_voltage = 3.3f) {
float voltage_range = max_voltage - min_voltage;
float normalized_value = (volts - min_voltage) / voltage_range;
uint32_t code_points = ceil(normalized_value * (resolution - 1));
}
/*
Demonstration of the conversion functions
*/
void demonstrate_coversions() {
printf("\nConversion demonstration:\n\n");
printf("Code points -> Volts:\n");
float volts = code_points_to_volts(2048);
printf("2048 : 4096 :: %0.2f V : 3.3 V\n", volts);
volts = code_points_to_volts(256, RESOLUTION_10_BIT, 1.0f, 2.0f);
printf("256 : 1024 :: %0.2f V : 1.0 V to 2.0 V\n", volts);
printf("Volts -> Code points:\n");
uint32_t code_points = volts_to_code_points(1.65f);
printf("%u : 4096 :: 1.65 V : 3.3 V\n", code_points);
code_points = volts_to_code_points(1.25f, RESOLUTION_10_BIT, 1.0f, 2.0f);
printf("%u : 1024 :: 1.25 V : 1.0 V to 2.0 V\n", code_points);
}
/*
Helpers for determining and correcting gain and offset
errors.
*/
/*
Determines the gain and offset error using two measurements.
For example, if you were using this for a DAC, you would pick
two output voltages to measure - one near the bottom of the range,
one near the top. Use the volts_to_code_points() function to set
the DAC output's value. Measure the *actual* voltage for both
of the expected values. You would use those values here to get
the gain and offset errors.
*/
void calculate_gain_and_offset_error(float low_measured, float high_measured, float low_expected, float high_expected, float& gain_error, float& offset_error) {
gain_error = (high_measured - low_measured) / (high_expected - low_expected);
offset_error = (low_measured * (1.0f / gain_error)) - low_expected;
}
/*
Use the gain and offset error to correct a value.
*/
float apply_error_correction(float value, float gain_error, float offset_error) {
return (value * (1.0f / gain_error)) - offset_error;
}
/*
Demonstration of the conversion error functions.
*/
void demonstrate_error_correction() {
printf("\nGain & offset error demonstration:\n\n");
float low_expected = 1.0;
float high_expected = 3.0;
printf("Expected values: %0.2f and %0.2f\n\n", low_expected, high_expected);
/* Measurements are the same as expected, there should be no
errors measured. */
float low_measured = 1.0;
float high_measured = 3.0;
float gain_error;
float offset_error;
calculate_gain_and_offset_error(
low_measured, high_measured,
low_expected, high_expected,
gain_error, offset_error);
printf("Actual values: %0.2f and %0.2f: gain error: %0.4f, offset error: %0.2f\n", low_measured, high_measured, gain_error, offset_error);
/* The high-side measurment is more than it should be, which
indicates that the gain error is above one. */
high_measured = 3.5;
calculate_gain_and_offset_error(
low_measured, high_measured,
low_expected, high_expected,
gain_error, offset_error);
printf("Actual values: %0.2f and %0.2f: gain error: %0.4f, offset error: %0.2f\n", low_measured, high_measured, gain_error, offset_error);
/* The high-side measurment is less than it should be, which
indicates that the gain error is below one. */
high_measured = 2.5;
calculate_gain_and_offset_error(
low_measured, high_measured,
low_expected, high_expected,
gain_error, offset_error);
printf("Actual values: %0.2f and %0.2f: gain error: %0.4f, offset error: %0.2f\n", low_measured, high_measured, gain_error, offset_error);
/* Both readings are slightly more than they should be (by
the same amount) which indicates there is a positive
offset error, but no gain error. */
low_measured = 2;
high_measured = 4;
calculate_gain_and_offset_error(
low_measured, high_measured,
low_expected, high_expected,
gain_error, offset_error);
printf("Actual values: %0.2f and %0.2f: gain error: %0.4f, offset error: %0.2f\n", low_measured, high_measured, gain_error, offset_error);
/* Both readings are slightly less than they should be (by
the same amount) which indicates there is a negative
offset error, but no gain error. */
low_measured = 0;
high_measured = 2;
calculate_gain_and_offset_error(
low_measured, high_measured,
low_expected, high_expected,
gain_error, offset_error);
printf("Actual values: %0.2f and %0.2f: gain error: %0.4f, offset error: %0.2f\n", low_measured, high_measured, gain_error, offset_error);
printf("\nError correction demonstration:\n\n");
printf("Expected values: 1.0 and 3.0\n");
low_measured = 1.1;
high_measured = 2.95;
printf("Actual values: %0.2f and %0.2f\n", low_measured, high_measured);
calculate_gain_and_offset_error(
low_measured, high_measured,
low_expected, high_expected,
gain_error, offset_error);
printf("Gain error: %0.4f, offset error: %0.2f\n\n", gain_error, offset_error);
for(float v = low_expected; v <= high_expected; v += 0.1) {
printf("Unadjusted: %0.2f, adjusted: %0.2f\n", v, apply_error_correction(v, gain_error, offset_error));
}
}
/*
This main function is just here to demonstrate these functions,
you wouldn't include this in your Arduino sketch.
*/
int main() {
demonstrate_coversions();
demonstrate_error_correction();
return 0;
} Write, Run & Share C++ code online using OneCompiler's C++ online compiler for free. It's one of the robust, feature-rich online compilers for C++ language, running on the latest version 17. Getting started with the OneCompiler's C++ compiler is simple and pretty fast. The editor shows sample boilerplate code when you choose language as C++ and start coding!
OneCompiler's C++ online compiler supports stdin and users can give inputs to programs using the STDIN textbox under the I/O tab. Following is a sample program which takes name as input and print your name with hello.
#include <iostream>
#include <string>
using namespace std;
int main()
{
string name;
cout << "Enter name:";
getline (cin, name);
cout << "Hello " << name;
return 0;
}
C++ is a widely used middle-level programming language.
When ever you want to perform a set of operations based on a condition If-Else is used.
if(conditional-expression) {
//code
}
else {
//code
}
You can also use if-else for nested Ifs and If-Else-If ladder when multiple conditions are to be performed on a single variable.
Switch is an alternative to If-Else-If ladder.
switch(conditional-expression){
case value1:
// code
break; // optional
case value2:
// code
break; // optional
......
default:
code to be executed when all the above cases are not matched;
}
For loop is used to iterate a set of statements based on a condition.
for(Initialization; Condition; Increment/decrement){
//code
}
While is also used to iterate a set of statements based on a condition. Usually while is preferred when number of iterations are not known in advance.
while (condition) {
// code
}
Do-while is also used to iterate a set of statements based on a condition. It is mostly used when you need to execute the statements atleast once.
do {
// code
} while (condition);
Function is a sub-routine which contains set of statements. Usually functions are written when multiple calls are required to same set of statements which increases re-usuability and modularity. Function gets run only when it is called.
return_type function_name(parameters);
function_name (parameters)
return_type function_name(parameters) {
// code
}