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 hidden or 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; | |
| } |
Author
Author
ADC CONVERSION MACRO
Fixed Linear Conversion
ADC Value --> Millivolt --> ADC Value
| ADC Value | Millivolts (Fixed Point) | ADC From mV (Fixed Point) | error |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 64 | 206 | 63 | -1 |
| 128 | 412 | 127 | -1 |
| 192 | 618 | 191 | -1 |
| 256 | 825 | 256 | 0 |
| 320 | 1031 | 319 | -1 |
| 384 | 1237 | 383 | -1 |
| 448 | 1443 | 447 | -1 |
| 512 | 1650 | 512 | 0 |
| 576 | 1856 | 575 | -1 |
| 640 | 2062 | 639 | -1 |
| 704 | 2268 | 703 | -1 |
| 768 | 2475 | 768 | 0 |
| 832 | 2681 | 831 | -1 |
| 896 | 2887 | 895 | -1 |
| 960 | 3093 | 959 | -1 |
| 1024 | 3300 | 1024 | 0 |
Millivolt --> ADC Value --> Millivolt
| MV Value | ADC From mV (Fixed Point) | Millivolts (Fixed Point) | error |
|---|---|---|---|
| 0 mV | 0 | 0 | 0 |
| 100 mV | 31 | 99 | -1 |
| 200 mV | 62 | 199 | -1 |
| 300 mV | 93 | 299 | -1 |
| 400 mV | 124 | 399 | -1 |
| 500 mV | 155 | 499 | -1 |
| 600 mV | 186 | 599 | -1 |
| 700 mV | 217 | 699 | -1 |
| 800 mV | 248 | 799 | -1 |
| 900 mV | 279 | 899 | -1 |
| 1000 mV | 310 | 999 | -1 |
| 1100 mV | 341 | 1098 | -2 |
| 1200 mV | 372 | 1198 | -2 |
| 1300 mV | 403 | 1298 | -2 |
| 1400 mV | 434 | 1398 | -2 |
| 1500 mV | 465 | 1498 | -2 |
| 1600 mV | 496 | 1598 | -2 |
| 1700 mV | 527 | 1698 | -2 |
| 1800 mV | 558 | 1798 | -2 |
| 1900 mV | 589 | 1898 | -2 |
| 2000 mV | 620 | 1998 | -2 |
| 2100 mV | 651 | 2097 | -3 |
| 2200 mV | 682 | 2197 | -3 |
| 2300 mV | 713 | 2297 | -3 |
| 2400 mV | 744 | 2397 | -3 |
| 2500 mV | 775 | 2497 | -3 |
| 2600 mV | 806 | 2597 | -3 |
| 2700 mV | 837 | 2697 | -3 |
| 2800 mV | 868 | 2797 | -3 |
| 2900 mV | 899 | 2897 | -3 |
| 3000 mV | 930 | 2997 | -3 |
| 3100 mV | 961 | 3096 | -4 |
| 3200 mV | 992 | 3196 | -4 |
| 3300 mV | 1024 | 3300 | 0 |
Integer Linear Conversion
Note: Interesting to see less accuracy here... but this is why we use floats or fixed point
ADC Value --> Millivolt --> ADC Value
| ADC Value | Millivolts (Integer) | ADC From mV (Integer) | error |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 64 | 192 | 59 | -5 |
| 128 | 384 | 119 | -9 |
| 192 | 576 | 178 | -14 |
| 256 | 768 | 238 | -18 |
| 320 | 960 | 297 | -23 |
| 384 | 1152 | 357 | -27 |
| 448 | 1344 | 417 | -31 |
| 512 | 1536 | 476 | -36 |
| 576 | 1728 | 536 | -40 |
| 640 | 1920 | 595 | -45 |
| 704 | 2112 | 655 | -49 |
| 768 | 2304 | 714 | -54 |
| 832 | 2496 | 774 | -58 |
| 896 | 2688 | 834 | -62 |
| 960 | 2880 | 893 | -67 |
| 1024 | 3072 | 953 | -71 |
Millivolt --> ADC Value --> Millivolt
| MV Value | ADC From mV (Integer) | Millivolts (Integer) | error |
|---|---|---|---|
| 0 mV | 0 | 0 | 0 |
| 100 mV | 31 | 93 | -7 |
| 200 mV | 62 | 186 | -14 |
| 300 mV | 93 | 279 | -21 |
| 400 mV | 124 | 372 | -28 |
| 500 mV | 155 | 465 | -35 |
| 600 mV | 186 | 558 | -42 |
| 700 mV | 217 | 651 | -49 |
| 800 mV | 248 | 744 | -56 |
| 900 mV | 279 | 837 | -63 |
| 1000 mV | 310 | 930 | -70 |
| 1100 mV | 341 | 1023 | -77 |
| 1200 mV | 372 | 1116 | -84 |
| 1300 mV | 403 | 1209 | -91 |
| 1400 mV | 434 | 1302 | -98 |
| 1500 mV | 465 | 1395 | -105 |
| 1600 mV | 496 | 1488 | -112 |
| 1700 mV | 527 | 1581 | -119 |
| 1800 mV | 558 | 1674 | -126 |
| 1900 mV | 589 | 1767 | -133 |
| 2000 mV | 620 | 1860 | -140 |
| 2100 mV | 651 | 1953 | -147 |
| 2200 mV | 682 | 2046 | -154 |
| 2300 mV | 713 | 2139 | -161 |
| 2400 mV | 744 | 2232 | -168 |
| 2500 mV | 775 | 2325 | -175 |
| 2600 mV | 806 | 2418 | -182 |
| 2700 mV | 837 | 2511 | -189 |
| 2800 mV | 868 | 2604 | -196 |
| 2900 mV | 899 | 2697 | -203 |
| 3000 mV | 930 | 2790 | -210 |
| 3100 mV | 961 | 2883 | -217 |
| 3200 mV | 992 | 2976 | -224 |
| 3300 mV | 1024 | 3072 | -228 |
Floating Linear Conversion
ADC Value --> Millivolt --> ADC Value
| ADC Value | Millivolts (dfloat) | ADC From mV (Integer) | error |
|---|---|---|---|
| 0 | 0.000000 | 0 | 0 |
| 64 | 206.250000 | 64 | 0 |
| 128 | 412.500000 | 128 | 0 |
| 192 | 618.750000 | 192 | 0 |
| 256 | 825.000000 | 256 | 0 |
| 320 | 1031.250000 | 320 | 0 |
| 384 | 1237.500000 | 384 | 0 |
| 448 | 1443.750000 | 448 | 0 |
| 512 | 1650.000000 | 512 | 0 |
| 576 | 1856.250000 | 576 | 0 |
| 640 | 2062.500000 | 640 | 0 |
| 704 | 2268.750000 | 704 | 0 |
| 768 | 2475.000000 | 768 | 0 |
| 832 | 2681.250000 | 832 | 0 |
| 896 | 2887.500000 | 896 | 0 |
| 960 | 3093.750000 | 960 | 0 |
| 1024 | 3300.000000 | 1024 | 0 |
Millivolt --> ADC Value --> Millivolt
| MV Value | ADC From mV (Integer) | Millivolts (dfloat) | error |
|---|---|---|---|
| 0 mV | 0 | 0.000000 | 0.00 |
| 100 mV | 31 | 99.902344 | -0.10 |
| 200 mV | 62 | 199.804688 | -0.20 |
| 300 mV | 93 | 299.707031 | -0.29 |
| 400 mV | 124 | 399.609375 | -0.39 |
| 500 mV | 155 | 499.511719 | -0.49 |
| 600 mV | 186 | 599.414062 | -0.59 |
| 700 mV | 217 | 699.316406 | -0.68 |
| 800 mV | 248 | 799.218750 | -0.78 |
| 900 mV | 279 | 899.121094 | -0.88 |
| 1000 mV | 310 | 999.023438 | -0.98 |
| 1100 mV | 341 | 1098.925781 | -1.07 |
| 1200 mV | 372 | 1198.828125 | -1.17 |
| 1300 mV | 403 | 1298.730469 | -1.27 |
| 1400 mV | 434 | 1398.632812 | -1.37 |
| 1500 mV | 465 | 1498.535156 | -1.46 |
| 1600 mV | 496 | 1598.437500 | -1.56 |
| 1700 mV | 527 | 1698.339844 | -1.66 |
| 1800 mV | 558 | 1798.242188 | -1.76 |
| 1900 mV | 589 | 1898.144531 | -1.86 |
| 2000 mV | 620 | 1998.046875 | -1.95 |
| 2100 mV | 651 | 2097.949219 | -2.05 |
| 2200 mV | 682 | 2197.851562 | -2.15 |
| 2300 mV | 713 | 2297.753906 | -2.25 |
| 2400 mV | 744 | 2397.656250 | -2.34 |
| 2500 mV | 775 | 2497.558594 | -2.44 |
| 2600 mV | 806 | 2597.460938 | -2.54 |
| 2700 mV | 837 | 2697.363281 | -2.64 |
| 2800 mV | 868 | 2797.265625 | -2.73 |
| 2900 mV | 899 | 2897.167969 | -2.83 |
| 3000 mV | 930 | 2997.070312 | -2.93 |
| 3100 mV | 961 | 3096.972656 | -3.03 |
| 3200 mV | 992 | 3196.875000 | -3.12 |
| 3300 mV | 1024 | 3300.000000 | 0.00 |
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
Considering including the above into https://github.com/clibs/clib/wiki/Packages once I'm happy with the consistency of the macro in practical usage. Ergo, should I have a _float macro version?
Recommended extra reading https://dobrian.github.io/cmp/topics/linear-mapping-and-interpolation/1.IntroductionToLinearInterpolation&LinearMapping.html