wm_adc_cal.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /*****************************************************************************
  2. *
  3. * File Name : wm_adc_cal.c
  4. *
  5. * Description: adc calibration
  6. *
  7. * Copyright (c) 2014 Winner Microelectronics Co., Ltd.
  8. * All rights reserved.
  9. *
  10. * Author :
  11. *
  12. * Date : 2022-10-10
  13. *****************************************************************************/
  14. #include <stdio.h>
  15. #include <string.h>
  16. #include <stdlib.h>
  17. #include "wm_include.h"
  18. #include "wm_regs.h"
  19. #include "wm_adc.h"
  20. #include "wm_io.h"
  21. #include "wm_gpio_afsel.h"
  22. #include "wm_efuse.h"
  23. #define ZERO_CAL_MAX_NUM (10)
  24. /*f(x) = kx + b*/
  25. //#define ADC_CAL_B_POS (7)
  26. //#define ADC_CAL_K_POS (6)
  27. #define ADC_CAL_REF_VOLTAGE_POS (5)
  28. extern void polyfit(int n,double x[],double y[],int poly_n,double a[]);
  29. /**
  30. * @brief This function can used to calibrate adc coefficient if not calibrated,
  31. * or adc offset after FT or multipoint calibration.
  32. *
  33. * @param[in] chanused: bitmap, specified calibration channel,only bit0-3 can be used
  34. * @param[in] refvoltage[]: array, calibration reference voltage to be used, unit:mV
  35. * refvoltage keep the same position with chan bitmap
  36. * real range[100,2300)mV, suggest reference voltage[500,2000]mV
  37. *
  38. * @return 0: success, < 0: failure
  39. *
  40. * @note 1)Adc curve is y=ax+b,y is real voltage(unit:mV), x is adc sample data.
  41. * After calibration, we can get a and b, or only update b.
  42. * 2) Only used single-end adc
  43. * 3) For example, use chan 0,1,3, and refvoltage 500,1000,2000,
  44. * then chanused is 0xB, refvoltage[] value is {500,1000,0, 2000};
  45. */
  46. int adc_multipoint_calibration(int chanused, int refvoltage[])
  47. {
  48. #define MAX_ADC_CAL_CHAN (4)
  49. #define MAX_ADC_REF_VOLTAGE (2300)
  50. #define MIN_ADC_REF_VOLTAGE (100)
  51. if (((chanused &((1<<MAX_ADC_CAL_CHAN)-1)) == 0) || (refvoltage == NULL))
  52. {
  53. return -1;
  54. }
  55. FT_ADC_CAL_ST adc_cal;
  56. int average = 0;
  57. int chancnt = 0;
  58. u32 sum_value = 0;
  59. float b = 0.0;
  60. int *prefvoltage = refvoltage;
  61. /*update calibration parameter*/
  62. memset(&adc_cal, 0xFF, sizeof(FT_ADC_CAL_ST));
  63. for (int j = 0; j < MAX_ADC_CAL_CHAN; j++)
  64. {
  65. if ((chanused&(1<<j)) == 0)
  66. {
  67. continue;
  68. }
  69. if ((prefvoltage[j] < MIN_ADC_REF_VOLTAGE) || (prefvoltage[j] >= MAX_ADC_REF_VOLTAGE))
  70. {
  71. return -1;
  72. }
  73. /*config adc chan for calibration*/
  74. wm_adc_config(j);
  75. /*config adc and start capture data*/
  76. tls_adc_init(0, 0);
  77. tls_adc_reference_sel(ADC_REFERENCE_INTERNAL);
  78. tls_adc_set_pga(1,1);
  79. tls_adc_set_clk(40);
  80. tls_adc_start_with_cpu(j);
  81. tls_os_time_delay(1);
  82. sum_value = 0;
  83. for (int i = 1; i <= ZERO_CAL_MAX_NUM; i++)
  84. {
  85. tls_os_time_delay(1);
  86. average = *(TLS_REG *)(HR_SD_ADC_RESULT_REG);
  87. average = ADC_RESULT_VAL(average);
  88. signedToUnsignedData(&average);
  89. sum_value += average;
  90. }
  91. adc_cal.units[chancnt].ref_val = prefvoltage[j]*10;
  92. adc_cal.units[chancnt].real_val = sum_value/ZERO_CAL_MAX_NUM/4;
  93. chancnt++;
  94. tls_adc_stop(0);
  95. }
  96. if (chancnt >= 2)
  97. {
  98. float k = 0.0;
  99. double a[4] = {0.0};
  100. double x[8] = {0.0};
  101. double y[8] = {0.0};
  102. for(int i = 0; i < chancnt; i++)
  103. {
  104. x[i] = (double)adc_cal.units[i].real_val;
  105. y[i] = (double)adc_cal.units[i].ref_val;
  106. }
  107. polyfit(chancnt, x, y, 1, a);
  108. k = (float)a[1];
  109. b = (float)a[0];
  110. //memcpy((char *)&adc_cal.units[ADC_CAL_K_POS], (char *)&k, 4);
  111. //memcpy((char *)&adc_cal.units[ADC_CAL_B_POS], (char *)&b, 4);
  112. adc_cal.a = k;
  113. adc_cal.b = b;
  114. adc_cal.valid_cnt = chancnt;
  115. return tls_set_adc_cal_param(&adc_cal);
  116. }
  117. else /*only one reference voltage*/
  118. {
  119. /*update calibration parameter*/
  120. int currentvalue = cal_voltage((double)adc_cal.units[0].real_val*4);
  121. int ret = tls_get_adc_cal_param(&adc_cal);
  122. if ((ret == 0) && (adc_cal.valid_cnt >= 2) && (adc_cal.valid_cnt <= 4))
  123. {
  124. /*reference voltage by user settings*/
  125. for (int i = 0; i < MAX_ADC_CAL_CHAN; i++)
  126. {
  127. if (chanused&(1<<i))
  128. {
  129. adc_cal.units[ADC_CAL_REF_VOLTAGE_POS].ref_val = prefvoltage[i];
  130. average = prefvoltage[i];
  131. break;
  132. }
  133. }
  134. /*update parameter b that used by the function f(x)=ax+b */
  135. //memcpy((char *)&b, (char *)&adc_cal.units[ADC_CAL_B_POS], 4);
  136. b = adc_cal.b;
  137. b = b + (average - currentvalue)*10; /*0.1mV*/
  138. //memcpy((char *)&adc_cal.units[ADC_CAL_B_POS], (char *)&b, 4);
  139. adc_cal.b = b;
  140. return tls_set_adc_cal_param(&adc_cal);
  141. }
  142. else
  143. {
  144. return -1;
  145. }
  146. }
  147. }
  148. /**
  149. * @brief This function is used to calibrate adc offset after FT or multipoint calibration.
  150. *
  151. * @param[in] chan: adc calibration channel to be used
  152. * @param[in] refvoltage: calibration reference voltage to be used, unit:mV
  153. *
  154. * @return 0: success, < 0: failure
  155. *
  156. * @note After FT calibration or mulitpoint calibration, adc curve is y=ax+b,
  157. * y is real voltage(unit:mV), x is adc sample data
  158. * a and b is the coefficient. This fuction only used to revise b value.
  159. */
  160. int adc_offset_calibration(int chan, int refvoltage)
  161. {
  162. if (chan >= 0 && chan < 4)
  163. {
  164. int refvol[4] = {0, 0, 0, 0};
  165. refvol[chan] = refvoltage;
  166. return adc_multipoint_calibration((1<<chan), refvol);
  167. }
  168. return -1;
  169. }