adc.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. /**********************************
  2. * 文件名称: adc.c
  3. * 功能描述: ADC采样和电机控制模块
  4. * 主要功能:
  5. * 1. 温度传感器采样与转换
  6. * 2. 电流偏移校准
  7. * 3. FOC算法计算
  8. * 4. ADC中断处理
  9. *
  10. * 注意事项:
  11. * - 使用ADC1进行电流和电压采样
  12. * - 使用ADC3进行UVW端电压和温度采样
  13. * - 采样频率为10kHz(与PWM频率同步)
  14. **********************************/
  15. #include "main.h"
  16. #include "arm_math.h"
  17. #include "adc.h"
  18. #include "board_config.h"
  19. #define _constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
  20. // 测试变量
  21. int32_t ia_test,ib_test,ic_test;
  22. float Ua,Ualpha,Ub,Ubeta, Uc, dc_a,dc_b,dc_c;
  23. //float err;
  24. // 电流和电压变量
  25. double Ia,Ib,Ic;
  26. float Ia_test,Ib_test,Ic_test;
  27. float Vbus;
  28. float Temp;
  29. // ADC采样值数组
  30. uint16_t ADC3ConvertedValue[ADC3_DMA_BUFFER_SIZE];
  31. uint16_t i = 0;
  32. // 电流偏移校准值
  33. uint32_t A_offset,B_offset,C_offset;
  34. uint8_t get_offset_flag = 0;
  35. uint8_t get_offset_sample_cnt = 0;
  36. // 速度闭环控制标志和参考电流
  37. u8 speed_close_loop_flag;
  38. float Iq_ref = 0.0f;
  39. float Id_ref = 0.0f;
  40. // EKF相关变量
  41. float EKF_Hz;
  42. // 角度变量
  43. float theta_add;
  44. float theta;
  45. // 电机参数(从其他模块导入)
  46. extern float Rs;
  47. extern float Ls;
  48. extern float flux;
  49. /**
  50. * @brief 根据热敏电阻阻值计算温度
  51. * @param rt_kohm: 热敏电阻阻值,单位为千欧姆
  52. * @retval 温度值,单位为摄氏度
  53. */
  54. float calculate_temperature(float rt_kohm)
  55. {
  56. float temp;
  57. const float Rp_kohm = 10.0f; /* 10K热敏电阻参考电阻 */
  58. const float T2 = (273.15f + 25.0f); /* T2温度,单位为K */
  59. const float Bx = 3380.0f; /* B值 */
  60. const float Ka = 273.15f; /* 绝对零度,单位为℃ */
  61. temp = rt_kohm / Rp_kohm; /* 电阻比值 */
  62. temp = log(temp); /* 取自然对数 */
  63. temp /= Bx; /* 除以B值 */
  64. temp += (1.0f / T2); /* 加上1/T2 */
  65. temp = 1.0f / temp; /* 取倒数 */
  66. temp -= Ka; /* 减去绝对零度 */
  67. return temp;
  68. }
  69. /**
  70. * @brief 获取电流偏移值
  71. * @param a_offset A相电流偏移值指针
  72. * @param b_offset B相电流偏移值指针
  73. * @note 采集128次求平均值,在PWM关闭时采集
  74. * - ADC1->JDR2: A相电流采样值
  75. * - ADC1->JDR3: B相电流采样值
  76. * - 右移7位 = 除以128求平均值
  77. */
  78. static void get_offset(uint32_t *a_offset, uint32_t *b_offset, uint32_t* c_offset)
  79. {
  80. if(get_offset_sample_cnt<128)
  81. {
  82. *a_offset += ADC1->JDR1;
  83. *b_offset += ADC2->JDR1;
  84. *c_offset += ADC3->JDR1;
  85. get_offset_sample_cnt++;
  86. }
  87. else
  88. {
  89. *a_offset >>= 7;
  90. *b_offset >>= 7;
  91. *c_offset >>= 7;
  92. get_offset_sample_cnt=0;
  93. TIM_CtrlPWMOutputs(PWM_TIM,DISABLE);
  94. get_offset_flag = 2;
  95. }
  96. }
  97. /**
  98. * @brief 开环角度更新
  99. */
  100. static void Angle_OpenLoop_Update(float step)
  101. {
  102. if(get_motor()->direction == MOTOR_CW)
  103. get_motor()->eleangle += step;
  104. else
  105. get_motor()->eleangle -= step;
  106. }
  107. static void FOC_parm_input(void)
  108. {
  109. get_foc_input()->Id_ref = 0.0f;
  110. get_foc_input()->Tpwm = PWM_TIM_PULSE_TPWM; // FOC算法需要的PWM周期
  111. get_foc_input()->Udc = Vbus;
  112. get_foc_input()->Rs = Rs;
  113. get_foc_input()->Ls = Ls;
  114. get_foc_input()->flux = flux;
  115. get_foc_input()->ia = Ia;
  116. get_foc_input()->ib = Ib;
  117. get_foc_input()->ic = Ic;
  118. }
  119. //static void FOC_parm_output(void)
  120. //{
  121. // EKF_Hz = get_foc_ouput()->EKF[2]/(2.0f*PI) ;
  122. //}
  123. static void uvw_current_and_vbus_get(float* vbus, double* iu, double* iv, double* iw)
  124. {
  125. *vbus = ADC_TO_VBUS( ADC3ConvertedValue[ADC3_VBUS_INDEX]);
  126. *iu = ADC_TO_CURRENT((int16_t)((int16_t)ADC1->JDR1 - (int16_t)A_offset ));
  127. *iv = ADC_TO_CURRENT((int16_t)((int16_t)ADC2->JDR1 - (int16_t)B_offset ));
  128. *iw = ADC_TO_CURRENT((int16_t)((int16_t)ADC3->JDR1 - (int16_t)C_offset ));
  129. Ia_test = *iu;
  130. Ib_test = *iv;
  131. Ic_test = *iw;
  132. }
  133. /**
  134. * @brief 修改后的motor_run()函数
  135. * @note 集成了完整的开环→闭环切换逻辑
  136. */
  137. void motor_run(void)
  138. {
  139. // 采集三相电流 母线电压
  140. uvw_current_and_vbus_get(&Vbus, &Ia, &Ib, &Ic);
  141. // IF 软起动
  142. if(speed_close_loop_flag==0) //速度环闭环切换控制,电机刚启动时速度环不闭环
  143. { //并且电流参考值缓慢增加(防冲击),速度达到一定值
  144. if((Iq_ref<MOTOR_STARTUP_CURRENT)) //速度切入闭环
  145. { //电流环在电机运行过程中全程闭环
  146. Iq_ref += 0.001f; //角度在电机刚启动时就闭环运行,无需强拖,得益于卡尔曼滤波做
  147. } //状态观测器低速性能比教好
  148. else
  149. {
  150. speed_close_loop_flag=1;
  151. }
  152. }
  153. else
  154. {
  155. if(speed_close_loop_flag==1)
  156. {
  157. if(Iq_ref>(MOTOR_STARTUP_CURRENT)/2.0f)
  158. {
  159. Iq_ref -= 0.001f;
  160. }
  161. else
  162. {
  163. speed_close_loop_flag=2;
  164. }
  165. }
  166. }
  167. // ========== FOC参数设置 ==========
  168. FOC_parm_input();
  169. #ifdef HALL_FOC_SELECT
  170. Hall_Update_Interpolated_Angle(); // 霍尔角度插值
  171. if(Hall_Get()->Speed > SPEED_LOOP_CLOSE_RAD_S ) // 速度达到闭环阈值
  172. {
  173. FOC_Input.Id_ref = 0.0f;
  174. Speed_Fdk = Hall_Get()->Speed;
  175. FOC_Input.Iq_ref = Speed_Pid_Out;
  176. // 切闭环霍尔角度
  177. get_foc_input()->theta = Hall_Get()->angle;
  178. }
  179. else
  180. {
  181. FOC_Input.Id_ref = 0.0f;
  182. FOC_Input.Iq_ref = Iq_ref;
  183. Speed_Pid.I_Sum = Iq_ref;
  184. // 模拟自增电角度
  185. theta += 0.005f; // 自增角度大了影响切换?? 给到0.01的时候经常滑丝
  186. if(theta >= 2* PI)
  187. {
  188. theta -= 2 * PI;
  189. }
  190. if(theta < 0.0f)
  191. {
  192. theta += 2 * PI;
  193. }
  194. get_foc_input()->theta = theta;
  195. }
  196. FOC_Input.speed_fdk = Hall_Get()->Speed; // 可用于参数识别
  197. #endif
  198. // ========== 执行FOC算法 ==========
  199. motor_foc_alg_run();
  200. // ========== PWM输出更新 ==========
  201. if(motor_start_stop == 1 )
  202. {
  203. PWM_TIM->CCR1 = (u16)(FOC_Output.Tcmp1);
  204. PWM_TIM->CCR2 = (u16)(FOC_Output.Tcmp2);
  205. PWM_TIM->CCR3 = (u16)(FOC_Output.Tcmp3);
  206. }
  207. else
  208. {
  209. PWM_TIM->CCR1 = PWM_TIM_PULSE >> 1;
  210. PWM_TIM->CCR2 = PWM_TIM_PULSE >> 1;
  211. PWM_TIM->CCR3 = PWM_TIM_PULSE >> 1;
  212. }
  213. // ========== 停止命令处理 ==========
  214. }
  215. #if 0
  216. /**
  217. * @brief 电机运行函数
  218. * @note 在PWM中断中调用,频率10kHz
  219. * - 读取ADC采样值,获取A/B相电流
  220. * - 根据基尔霍夫定律计算C相电流(Ia + Ib + Ic = 0)
  221. * - 速度环控制,计算速度PID输出
  222. * - 根据控制模式选择开环/闭环控制
  223. * - 执行FOC算法,计算SVPWM占空比
  224. * - 更新PWM占空比
  225. */
  226. void motor_run(void)
  227. {
  228. uvw_current_and_vbus_get(&Vbus, &Ia, &Ib, &Ic); // 获取母线电压,相电流
  229. // 选择电机运行方式 霍尔 开环加闭环 无感EKF
  230. if(speed_close_loop_flag==0) // 启动阶段,逐渐增加电流
  231. { // 电流从零开始,避免启动冲击
  232. if((Iq_ref<MOTOR_STARTUP_CURRENT)) // 未达到启动电流
  233. {
  234. Iq_ref += 0.00004f; // 缓慢增加电流
  235. }
  236. else
  237. {
  238. speed_close_loop_flag=1;
  239. }
  240. }
  241. else
  242. {
  243. if(speed_close_loop_flag==1)
  244. {
  245. if(Iq_ref>(MOTOR_STARTUP_CURRENT/2.0f))
  246. {
  247. Iq_ref -= 0.001f;
  248. }
  249. else
  250. {
  251. speed_close_loop_flag=2;
  252. }
  253. }
  254. }
  255. // 根据传感器类型选择FOC控制模式
  256. #ifdef HALL_FOC_SELECT // 使用霍尔传感器的FOC控制
  257. if((hall_speed*2.0f*PI)>SPEED_LOOP_CLOSE_RAD_S) // 速度达到闭环阈值
  258. {
  259. FOC_Input.Id_ref = 0.0f;
  260. Speed_Fdk = hall_speed*2.0f*PI;
  261. FOC_Input.Iq_ref = Speed_Pid_Out;
  262. }
  263. else
  264. {
  265. FOC_Input.Id_ref = 0.0f;
  266. FOC_Input.Iq_ref = Iq_ref;
  267. Speed_Pid.I_Sum = Iq_ref;
  268. }
  269. FOC_Input.theta = hall_angle;
  270. FOC_Input.speed_fdk = hall_speed*2.0f*PI;
  271. #endif
  272. #ifdef SENSORLESS_FOC_SELECT // 无传感器FOC控制
  273. if(FOC_Output.EKF[2]>SPEED_LOOP_CLOSE_RAD_S) // 速度达到闭环阈值
  274. {
  275. FOC_Input.Id_ref = 0.0f;
  276. Speed_Fdk = FOC_Output.EKF[2];
  277. FOC_Input.Iq_ref = Speed_Pid_Out;
  278. }
  279. else
  280. {
  281. FOC_Input.Id_ref = 0.0f;
  282. FOC_Input.Iq_ref = Iq_ref;
  283. Speed_Pid.I_Sum = Iq_ref;
  284. }
  285. FOC_Input.theta = FOC_Output.EKF[3];
  286. FOC_Input.speed_fdk = FOC_Output.EKF[2];
  287. #endif
  288. //============================== FOC算法计算 ==============================
  289. FOC_parm_input();
  290. FOC_parm_output();
  291. if(get_motor()->direction == MOTOR_CW)
  292. get_motor()->eleangle += 0.01f; // 使用 float 后缀
  293. else if(get_motor()->direction == MOTOR_CCW)
  294. get_motor()->eleangle -= 0.01f;
  295. // if(speed_close_loop_flag == 2)
  296. // {
  297. // if(get_motor()->direction == MOTOR_CW)
  298. // get_motor()->eleangle += Hall_Get()->angle_add; // 使用 float 后缀
  299. // else if(get_motor()->direction == MOTOR_CCW)
  300. // get_motor()->eleangle -= Hall_Get()->angle_add;
  301. // }
  302. if(get_motor()->eleangle >= 2.0f * PI)
  303. {
  304. get_motor()->eleangle -= 2.0f * PI;
  305. }else if(get_motor()->eleangle <= 0.0f * PI)
  306. {
  307. get_motor()->eleangle += 2.0f * PI;
  308. }
  309. get_foc_input()->theta = get_motor()->eleangle;
  310. get_foc_input()->Id_ref = Id_ref;
  311. get_foc_input()->Iq_ref = Iq_ref;
  312. motor_foc_openloop_run();
  313. // foc_algorithm_step(); // 执行FOC算法,计算SVPWM占空比
  314. if(motor_start_stop==1)
  315. {
  316. PWM_TIM->CCR1 = (u16)(FOC_Output.Tcmp1); // 更新SVPWM占空比
  317. PWM_TIM->CCR2 = (u16)(FOC_Output.Tcmp2);
  318. PWM_TIM->CCR3 = (u16)(FOC_Output.Tcmp3);
  319. }
  320. else
  321. {
  322. PWM_TIM->CCR1 = PWM_TIM_PULSE>>1;
  323. PWM_TIM->CCR2 = PWM_TIM_PULSE>>1;
  324. PWM_TIM->CCR3 = PWM_TIM_PULSE>>1;
  325. }
  326. //communication_task();
  327. }
  328. #endif
  329. /**
  330. * @brief ADC中断处理函数
  331. * @note 处理ADC注入通道转换完成中断
  332. * - 当偏移校准完成后,执行电机运行函数
  333. * - 当需要校准偏移时,执行偏移校准函数
  334. * 为什么VBUS不需要零偏校准? 开始采样即VBUS已经接在电源上了
  335. */
  336. void ADC_IRQHandler(void)
  337. {
  338. // 检查ADC1注入组完成(三个ADC已同步完成)
  339. if((SAMPLE_ADC1->SR & ADC_FLAG_JEOC) == ADC_FLAG_JEOC)
  340. {
  341. // 处理数据
  342. if(get_offset_flag == 2)
  343. {
  344. // hall_angle += hall_angle_add;
  345. // if(hall_angle < 0.0f)
  346. // hall_angle += 2.0f * PI;
  347. // else if(hall_angle > (2.0f * PI))
  348. // hall_angle -= 2.0f * PI;
  349. motor_run();
  350. }
  351. else if(get_offset_flag == 1) // get_offset_flag = 2 表示偏移已经获取完成
  352. {
  353. get_offset(&A_offset, &B_offset, &C_offset);
  354. }
  355. // 清除所有三个ADC的JEOC标志
  356. SAMPLE_ADC1->SR = ~ADC_SR_JEOC;
  357. SAMPLE_ADC2->SR = ~ADC_SR_JEOC;
  358. SAMPLE_ADC3->SR = ~ADC_SR_JEOC;
  359. }
  360. }