Skip to content

Instantly share code, notes, and snippets.

@mofosyne
Last active February 27, 2025 15:23
Show Gist options
  • Save mofosyne/afd4cbd06a7d3b9ee00088ad366a21dc to your computer and use it in GitHub Desktop.
Save mofosyne/afd4cbd06a7d3b9ee00088ad366a21dc to your computer and use it in GitHub Desktop.
These are C macro for converting between ADC to millivolt and back. Might be useful for embedded projects.
/* ADC Linear Conversion Macros
Brian Khuu 2025
ADC_MILLIVOLT_FROM_ADC_VAL(ADC_BIT_COUNT, MILLI_VOLT_REFL, MILLI_VOLT_REFH, ADC_VAL) = ( (MILLI_VOLT_REFL) + ((ADC_VAL) * (((MILLI_VOLT_REFH) - (MILLI_VOLT_REFL)) / (1 << (ADC_BIT_COUNT)))) )
ADC_VAL_FROM_MILLIVOLT(ADC_BIT_COUNT, MILLI_VOLT_REFL, MILLI_VOLT_REFH, MILLIVOLT) = ( (((MILLIVOLT) - (MILLI_VOLT_REFL)) * (1 << (ADC_BIT_COUNT))) / ((MILLI_VOLT_REFH) - (MILLI_VOLT_REFL)) )
*/
// This is the linear conversion macros
#define ADC_MILLIVOLT_FROM_ADC_VAL(ADC_BIT_COUNT, MILLI_VOLT_REFL, MILLI_VOLT_REFH, ADC_VAL) ( (MILLI_VOLT_REFL) + ((ADC_VAL) * (((MILLI_VOLT_REFH) - (MILLI_VOLT_REFL)) / (1 << (ADC_BIT_COUNT)))) )
#define ADC_VAL_FROM_MILLIVOLT(ADC_BIT_COUNT, MILLI_VOLT_REFL, MILLI_VOLT_REFH, MILLIVOLT) ( (((MILLIVOLT) - (MILLI_VOLT_REFL)) * (1 << (ADC_BIT_COUNT))) / ((MILLI_VOLT_REFH) - (MILLI_VOLT_REFL)) )
// This uses left shift (Akin to scaling down via multiplication) to temporarily convert the integer division into fixed integer arithmetic operation
// before using right shift (Akin to scaling upward via division) to convert from fixed notation back to the original scale but with better precision
#define ADC_MILLIVOLT_FROM_ADC_VAL_INTEGER(ADC_BIT_COUNT, SCALING_FACTOR, MILLI_VOLT_REFL, MILLI_VOLT_REFH, ADC_VAL) (ADC_MILLIVOLT_FROM_ADC_VAL(ADC_BIT_COUNT, MILLI_VOLT_REFL << SCALING_FACTOR, MILLI_VOLT_REFH << SCALING_FACTOR, ADC_VAL) >> SCALING_FACTOR)
#define ADC_VAL_FROM_MILLIVOLT_INTEGER(ADC_BIT_COUNT, SCALING_FACTOR, MILLI_VOLT_REFL, MILLI_VOLT_REFH, MILLIVOLT) (ADC_VAL_FROM_MILLIVOLT(ADC_BIT_COUNT, MILLI_VOLT_REFL, MILLI_VOLT_REFH, MILLIVOLT))
// Same as above, but scaling factor rearranged for minimal operations.
#define ADC_MILLIVOLT_FROM_ADC_VAL_INTEGER_FAST(ADC_BIT_COUNT, SCALING_FACTOR, MILLI_VOLT_REFL, MILLI_VOLT_REFH, ADC_VAL) (( (MILLI_VOLT_REFL << SCALING_FACTOR) + ((ADC_VAL) * ((((MILLI_VOLT_REFH) - (MILLI_VOLT_REFL)) << SCALING_FACTOR) / (1 << (ADC_BIT_COUNT)))) ) >> SCALING_FACTOR)
#define ADC_VAL_FROM_MILLIVOLT_INTEGER_FAST(ADC_BIT_COUNT, SCALING_FACTOR, MILLI_VOLT_REFL, MILLI_VOLT_REFH, MILLIVOLT) ( (((((MILLIVOLT) - (MILLI_VOLT_REFL)) * (1 << (ADC_BIT_COUNT))) << SCALING_FACTOR) / ((MILLI_VOLT_REFH) - (MILLI_VOLT_REFL)) ) >> SCALING_FACTOR)
#include <stdio.h>
#include <stdint.h>
#define MILLI_VOLT_REFL (0L) ///< Lower Ref Voltage
#define MILLI_VOLT_REFH (3300L) ///< Upper Ref Voltage
#define ADC_BIT_COUNT 10
#define SCALING_FACTOR 10
int main()
{
printf("\n\n# ADC CONVERSION MACRO\n\n");
printf("\n\n## Fixed Linear Conversion\n\n");
printf("\n\n### ADC Value --> Millivolt --> ADC Value \n\n");
printf("| ADC Value | Millivolts (Fixed Point) | ADC From mV (Fixed Point) | error |\n");
printf("|-----------|--------------------------|---------------------------|-------|\n");
for (uint16_t adc_val = 0; adc_val <= (1 << ADC_BIT_COUNT); adc_val += 64)
{
uint64_t millivolt_fixed = ADC_MILLIVOLT_FROM_ADC_VAL_INTEGER(ADC_BIT_COUNT, SCALING_FACTOR, MILLI_VOLT_REFL, MILLI_VOLT_REFH, adc_val);
uint16_t adc_from_mv_fixed = ADC_VAL_FROM_MILLIVOLT_INTEGER(ADC_BIT_COUNT, SCALING_FACTOR, MILLI_VOLT_REFL, MILLI_VOLT_REFH, millivolt_fixed);
printf("| %8d | %22ld | %25d | %5d |\n", adc_val, millivolt_fixed, adc_from_mv_fixed, adc_from_mv_fixed - adc_val);
}
printf("\n\n");
printf("\n\n### Millivolt --> ADC Value --> Millivolt \n\n");
printf("| MV Value | ADC From mV (Fixed Point) | Millivolts (Fixed Point) | error |\n");
printf("|--------------|---------------------------|--------------------------|-------|\n");
for (uint64_t mv_val = MILLI_VOLT_REFL; mv_val <= MILLI_VOLT_REFH; mv_val += 100)
{
uint16_t adc_from_mv_fixed = ADC_VAL_FROM_MILLIVOLT_INTEGER(ADC_BIT_COUNT, SCALING_FACTOR, MILLI_VOLT_REFL, MILLI_VOLT_REFH, mv_val);
uint64_t millivolt_fixed = ADC_MILLIVOLT_FROM_ADC_VAL_INTEGER(ADC_BIT_COUNT, SCALING_FACTOR, MILLI_VOLT_REFL, MILLI_VOLT_REFH, adc_from_mv_fixed);
printf("| %8lu mV | %25d | %22ld | %5d |\n", mv_val, adc_from_mv_fixed, millivolt_fixed, (int) millivolt_fixed - (int) mv_val);
}
printf("\n\n");
printf("\n\n## Integer Linear Conversion\n\n");
printf("Note: Interesting to see less accuracy here... but this is why we use floats or fixed point\n\n");
printf("\n\n### ADC Value --> Millivolt --> ADC Value \n\n");
printf("| ADC Value | Millivolts (Integer) | ADC From mV (Integer) | error |\n");
printf("|-----------|------------------------|------------------------|-------|\n");
for (uint16_t adc_val = 0; adc_val <= (1 << ADC_BIT_COUNT); adc_val += 64)
{
uint64_t millivolt_int = ADC_MILLIVOLT_FROM_ADC_VAL(ADC_BIT_COUNT, MILLI_VOLT_REFL, MILLI_VOLT_REFH, adc_val);
uint16_t adc_from_mv_int = ADC_VAL_FROM_MILLIVOLT(ADC_BIT_COUNT, MILLI_VOLT_REFL, MILLI_VOLT_REFH, millivolt_int);
printf("| %8d | %22ld | %22d | %5d |\n", adc_val, millivolt_int, adc_from_mv_int, adc_from_mv_int - adc_val);
}
printf("\n\n");
printf("\n\n### Millivolt --> ADC Value --> Millivolt \n\n");
printf("| MV Value | ADC From mV (Integer) | Millivolts (Integer) | error |\n");
printf("|--------------|------------------------|------------------------|-------|\n");
for (uint64_t mv_val = MILLI_VOLT_REFL; mv_val <= MILLI_VOLT_REFH; mv_val += 100)
{
uint16_t adc_from_mv_int = ADC_VAL_FROM_MILLIVOLT(ADC_BIT_COUNT, MILLI_VOLT_REFL, MILLI_VOLT_REFH, mv_val);
uint64_t millivolt_int = ADC_MILLIVOLT_FROM_ADC_VAL(ADC_BIT_COUNT, MILLI_VOLT_REFL, MILLI_VOLT_REFH, adc_from_mv_int);
printf("| %8lu mV | %22d | %22ld | %5d |\n", mv_val, adc_from_mv_int, millivolt_int, (int) millivolt_int - (int) mv_val);
}
printf("\n\n");
printf("\n\n## Floating Linear Conversion\n\n");
printf("\n\n### ADC Value --> Millivolt --> ADC Value \n\n");
printf("| ADC Value | Millivolts (dfloat) | ADC From mV (Integer) | error |\n");
printf("|-----------|------------------------|------------------------|-------|\n");
for (uint16_t adc_val = 0; adc_val <= (1 << ADC_BIT_COUNT); adc_val += 64)
{
double millivolt_int = ADC_MILLIVOLT_FROM_ADC_VAL(ADC_BIT_COUNT, (double)MILLI_VOLT_REFL, (double)MILLI_VOLT_REFH, (double)adc_val);
uint16_t adc_from_mv_int = ADC_VAL_FROM_MILLIVOLT(ADC_BIT_COUNT, MILLI_VOLT_REFL, MILLI_VOLT_REFH, millivolt_int);
printf("| %8d | %22lf | %22d | %5d |\n", adc_val, millivolt_int, adc_from_mv_int, adc_from_mv_int - adc_val);
}
printf("\n\n");
printf("\n\n### Millivolt --> ADC Value --> Millivolt \n\n");
printf("| MV Value | ADC From mV (Integer) | Millivolts (dfloat) | error |\n");
printf("|--------------|------------------------|------------------------|-------|\n");
for (uint64_t mv_val = MILLI_VOLT_REFL; mv_val <= MILLI_VOLT_REFH; mv_val += 100)
{
uint16_t adc_from_mv_int = ADC_VAL_FROM_MILLIVOLT(ADC_BIT_COUNT, MILLI_VOLT_REFL, MILLI_VOLT_REFH, mv_val);
double millivolt = ADC_MILLIVOLT_FROM_ADC_VAL(ADC_BIT_COUNT, (double)MILLI_VOLT_REFL, (double)MILLI_VOLT_REFH, (double)adc_from_mv_int);
printf("| %8lu mV | %22d | %22lf | %5.2f |\n", mv_val, adc_from_mv_int, millivolt, (double) millivolt - (double) mv_val);
}
printf("\n\n");
return 0;
}
@mofosyne
Copy link
Author

I suspect I may have gotten something wrong here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment