Last active
February 27, 2025 15:23
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I suspect I may have gotten something wrong here.