有线学习笔记2
2. 任务一初始 PWM 驱动 出口检测 器件判断 Hall LED目标MCU 出 150kHz/50% PWM全桥4路/半桥2路驱动无线充电ADC 测 VRECT 判断传能是否工作串口打印器件正常与否Hall 判笔在位4 LED 按笔用电显示。原理全/半桥把 VIN 斩成 150kHz 方波激励 TX LC Tank → 耦合到 RX → 整流出 VRECT。VRECT 高于门限即证明MCU→桥→线圈→整流整条链路通。void Task1_Loop(uint8_t pen, float pin, float pout){ float vrect rank_volt(R_VRECT, VRECT_DIV); if(vrect VRECT_OK_V) printf([T1] Charging RUN VRECT%.2fV Pin%.2fW Pout%.2fW pen%d\r\n, vrect, pin, pout, pen); else printf([T1] NO POWER? VRECT%.2fV 查:桥驱动/死区/线圈/整流 pen%d\r\n, vrect, pen); }3. 任务二扫频找最佳效率 → 锁定频率目标在扫频范围内输出不同 PWM测检测点 Urms/Irms 算功率与效率找到效率最高的频率并保持。中继线圈居中不动。原理LC 回路谐振时耦合最强、效率最高。扫频就是画效率-频率曲线找峰。从高频往低频逼近避免掉进谐振点下方的容性区烧 MOS。效率取EFFICIENCY_CASE指定检测口。uint32_t Task2_SweepBestFreq(void){ float bestEff0; uint32_t bestFPWM_INIT_HZ; for(uint32_t fSWEEP_FMAX; fSWEEP_FMIN; f-SWEEP_STEP){ /* 高→低更安全 */ PWM_SetFreq(f); HAL_Delay(SWEEP_SETTLE_MS); float pin,pout,eff; Measure(pin,pout,eff); printf([T2] f%lukHz Pin%.2f Pout%.2f eff%.1f%%\r\n, f/1000, pin, pout, eff*100); if(effbestEff pin0.1f){ bestEffeff; bestFf; } #if ENABLE_PROTECTION if(Protection_Check()){ PWM_Stop(); printf([T2] protect during sweep\r\n); break; } #endif } PWM_SetFreq(bestF); /* 锁定最佳频率 */ printf([T2] BEST f%lukHz eff%.1f%% 填进 BEST_FREQ_HZ\r\n, bestF/1000, bestEff*100); return bestF; }4. 任务三固定最佳频率调中继线圈实时打印效率目标保持任务2最佳频率手动调中继线圈位置/角度实时打印功率与效率人工找线圈最佳位姿。原理耦合系数 k 随线圈相对位姿变化k 越大效率越高。频率固定只看效率随位姿的变化找峰。void Task3_Loop(uint8_t pen){ float pin,pout,eff; Measure(pin,pout,eff); printf([T3 coil-tune] Pin%.2fW Pout%.2fW eff%.1f%% ← 调线圈看这个数 pen%d\r\n, pin, pout, eff*100, pen); }5. 任务四固定频率最佳位姿换负载测功率/效率目标保持任务2频率、任务3最佳位姿换不同充电笔换负载算各负载下功率与效率并打印。原理负载变化改变 RX 反射阻抗与工作点效率随负载变。此题看不同负载下系统表现。void Task4_Loop(uint8_t pen){ float pin,pout,eff; Measure(pin,pout,eff); printf([T4 load-test] Pin%.2fW Pout%.2fW eff%.1f%% ← 换笔记录此组数据 pen%d\r\n, pin, pout, eff*100, pen); }6. 附加任务欠压 / 过压 / 过流保护与任意任务共存Protection_Check()已在 1.8 给出主循环里每圈调用命中即PWM_Stop()切断输出并闪灯报警见总代码。7. 总代码main.c宏切换任务只贴USER CODE区应填的内容MX_xxx_Init()、句柄htim1/hadc1/huart1/hi2c1/hspi1由 CubeMX 生成。把上面各函数放进main.c的USER CODE BEGIN 4区。/* USER CODE BEGIN Includes */ #include stdio.h #include math.h /* USER CODE END Includes */ /* 全局宏见第0节此处照抄 */ #define TASK_NUM 1 #define ENABLE_PROTECTION 1 #define BRIDGE_HALF 0 #define BRIDGE_FULL 1 #define BRIDGE_TYPE BRIDGE_FULL #define DEADTIME_NS 500 #define PWM_INIT_HZ 150000UL #define TIM_CLK_HZ 72000000UL #define SWEEP_FMAX 205000UL #define SWEEP_FMIN 110000UL #define SWEEP_STEP 1000UL #define SWEEP_SETTLE_MS 3 #define BEST_FREQ_HZ 145000UL #define HALL_DIGITAL 0 #define HALL_LINEAR 1 #define HALL_TYPE HALL_DIGITAL #define HALL_ACTIVE GPIO_PIN_RESET #define HALL_ON_V 2.0f #define HALL_OFF_V 1.6f #define EFF_RECT_OVER_IN 1 #define EFF_VOUT_OVER_IN 2 #define EFF_BAT_OVER_IN 3 #define EFFICIENCY_CASE EFF_RECT_OVER_IN #define UV_VIN 4.5f #define OV_VIN 6.5f #define OV_VRECT 6.0f #define OC_IIN 2.0f #define VREF 3.30f #define ADC_FS 4096.0f #define VRECT_OK_V 3.0f #define P_L1 0.5f #define P_L2 1.0f #define P_L3 1.5f #define P_L4 2.0f #define VIN_DIV 11.0f #define IIN_RS 0.05f #define IIN_G 20.0f #define VRECT_DIV 3.0f #define IRECT_RS 0.05f #define IRECT_G 20.0f #define VOUT_DIV 2.0f #define IOUT_RS 0.05f #define IOUT_G 20.0f #define VBAT_DIV 2.0f #define ICHG_RS 0.05f #define ICHG_G 20.0f /* USER CODE BEGIN PV */ #if HALL_TYPEHALL_LINEAR #define ADC_NCH 9 #define R_HALL 8 #else #define ADC_NCH 8 #endif #define R_VIN 0 #define R_IIN 1 #define R_VRECT 2 #define R_IRECT 3 #define R_VOUT 4 #define R_IOUT 5 #define R_VBAT 6 #define R_ICHG 7 #define ADC_OVS 16 uint16_t adc_raw[ADC_NCH*ADC_OVS]; /* USER CODE END PV */ /* USER CODE BEGIN 4 */ int __io_putchar(int ch){ HAL_UART_Transmit(huart1,(uint8_t*)ch,1,10); return ch; } static uint8_t deadtime_dtg(uint32_t ns){ float step 1000.0f/(TIM_CLK_HZ/1000000.0f); uint32_t d(uint32_t)(ns/step0.5f); if(d127)d127; return (uint8_t)d; } void PWM_SetFreq(uint32_t hz){ uint32_t arr(uint32_t)(TIM_CLK_HZ/hz)-1; __HAL_TIM_SET_AUTORELOAD(htim1,arr); __HAL_TIM_SET_COMPARE(htim1,TIM_CHANNEL_1,(arr1)/2); #if BRIDGE_TYPEBRIDGE_FULL __HAL_TIM_SET_COMPARE(htim1,TIM_CHANNEL_2,(arr1)/2); #endif } void PWM_Start(void){ TIM_BreakDeadTimeConfigTypeDef bd{0}; bd.OffStateRunModeTIM_OSSR_DISABLE; bd.OffStateIDLEModeTIM_OSSI_DISABLE; bd.LockLevelTIM_LOCKLEVEL_OFF; bd.DeadTimedeadtime_dtg(DEADTIME_NS); bd.BreakStateTIM_BREAK_DISABLE; bd.BreakPolarityTIM_BREAKPOLARITY_HIGH; bd.AutomaticOutputTIM_AUTOMATICOUTPUT_ENABLE; HAL_TIMEx_ConfigBreakDeadTime(htim1,bd); PWM_SetFreq(PWM_INIT_HZ); HAL_TIM_PWM_Start(htim1,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(htim1,TIM_CHANNEL_1); #if BRIDGE_TYPEBRIDGE_FULL HAL_TIM_PWM_Start(htim1,TIM_CHANNEL_2); HAL_TIMEx_PWMN_Start(htim1,TIM_CHANNEL_2); #endif } void PWM_Stop(void){ __HAL_TIM_MOE_DISABLE(htim1); } void ADC_StartAll(void){ HAL_ADCEx_Calibration_Start(hadc1); HAL_ADC_Start_DMA(hadc1,(uint32_t*)adc_raw,ADC_NCH*ADC_OVS); } static float adc_avg(uint8_t r){ uint32_t s0; for(uint8_t k0;kADC_OVS;k) sadc_raw[rk*ADC_NCH]; return (float)s/ADC_OVS; } static float rank_volt(uint8_t r,float div){ return adc_avg(r)*(VREF/ADC_FS)*div; } static float shunt_amp(uint8_t r,float rs,float g){ return adc_avg(r)*(VREF/ADC_FS)/g/rs; } static float Power_In(void){ return rank_volt(R_VIN,VIN_DIV)*shunt_amp(R_IIN,IIN_RS,IIN_G); } static float Power_Out(void){ #if EFFICIENCY_CASEEFF_RECT_OVER_IN return rank_volt(R_VRECT,VRECT_DIV)*shunt_amp(R_IRECT,IRECT_RS,IRECT_G); #elif EFFICIENCY_CASEEFF_VOUT_OVER_IN return rank_volt(R_VOUT,VOUT_DIV)*shunt_amp(R_IOUT,IOUT_RS,IOUT_G); #elif EFFICIENCY_CASEEFF_BAT_OVER_IN return rank_volt(R_VBAT,VBAT_DIV)*shunt_amp(R_ICHG,ICHG_RS,ICHG_G); #endif } void Measure(float *pin,float *pout,float *eff){ *pinPower_In(); *poutPower_Out(); *eff(*pin0.001f)?(*pout/ *pin):0.0f; } uint8_t Hall_PenOK(void){ #if HALL_TYPEHALL_DIGITAL static uint8_t st0,c0; uint8_t now(HAL_GPIO_ReadPin(HALL_GPIO_Port,HALL_Pin)HALL_ACTIVE); if(now!st){ if(c5){stnow;c0;} } else c0; return st; #else static uint8_t p0; float vrank_volt(R_HALL,1.0f); if(!pvHALL_ON_V)p1; if(pvHALL_OFF_V)p0; return p; #endif } static void LED_SetBar(uint8_t n){ HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,n1); HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,n2); HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,n3); HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,n4); } void LED_Update(uint8_t pen,float pout,uint8_t fault){ if(fault){ uint8_t b(HAL_GetTick()/200)1; LED_SetBar(b?4:0); return; } if(!pen){ LED_SetBar(0); return; } uint8_t npoutP_L4?4:poutP_L3?3:poutP_L2?2:poutP_L1?1:0; LED_SetBar(n); } uint8_t Protection_Check(void){ float vinrank_volt(R_VIN,VIN_DIV), vrrank_volt(R_VRECT,VRECT_DIV), iinshunt_amp(R_IIN,IIN_RS,IIN_G); if(vinUV_VIN)return 1; if(vinOV_VIN)return 2; if(vrOV_VRECT)return 3; if(iinOC_IIN)return 4; return 0; } #if TASK_NUM2 uint32_t Task2_SweepBestFreq(void){ float bestEff0; uint32_t bestFPWM_INIT_HZ; for(uint32_t fSWEEP_FMAX;fSWEEP_FMIN;f-SWEEP_STEP){ PWM_SetFreq(f); HAL_Delay(SWEEP_SETTLE_MS); float pin,pout,eff; Measure(pin,pout,eff); printf([T2] f%lukHz Pin%.2f Pout%.2f eff%.1f%%\r\n,f/1000,pin,pout,eff*100); if(effbestEffpin0.1f){bestEffeff;bestFf;} #if ENABLE_PROTECTION if(Protection_Check()){PWM_Stop();break;} #endif } PWM_SetFreq(bestF); printf([T2] BEST f%lukHz eff%.1f%% \r\n,bestF/1000,bestEff*100); return bestF; } #endif /* USER CODE END 4 */ int main(void){ HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_ADC1_Init(); MX_TIM1_Init(); MX_USART1_UART_Init(); MX_I2C1_Init(); MX_SPI1_Init(); /* USER CODE BEGIN 2 */ setvbuf(stdout,NULL,_IONBF,0); ADC_StartAll(); printf( WPT Relay boot | TASK%d | EFF_CASE%d | %s bridge | DT%dns \r\n, TASK_NUM, EFFICIENCY_CASE, (BRIDGE_TYPEBRIDGE_FULL)?FULL:HALF, DEADTIME_NS); PWM_Start(); #if (TASK_NUM3)||(TASK_NUM4) PWM_SetFreq(BEST_FREQ_HZ); /* 任务3/4 直接锁任务2最佳频率 */ #endif /* USER CODE END 2 */ while(1){ /* ---- USER CODE BEGIN 3 ---- */ uint8_t pen Hall_PenOK(); float pin,pout,eff; Measure(pin,pout,eff); uint8_t fault0; #if ENABLE_PROTECTION fault Protection_Check(); if(fault){ PWM_Stop(); printf(!! PROTECT trip code%d (UV/OV/OVR/OC)\r\n, fault); } #endif #if TASK_NUM1 { /* 任务1出口检测器件判断 */ float vrectrank_volt(R_VRECT,VRECT_DIV); if(vrectVRECT_OK_V) printf([T1] RUN VRECT%.2fV Pin%.2f Pout%.2f pen%d\r\n,vrect,pin,pout,pen); else printf([T1] NO POWER VRECT%.2fV 查桥/死区/线圈/整流 pen%d\r\n,vrect,pen); } #elif TASK_NUM2 { /* 任务2只扫频一次之后保持最佳频率 */ static uint8_t done0; if(!done !fault){ Task2_SweepBestFreq(); done1; } printf([T2] hold-best Pin%.2f Pout%.2f eff%.1f%% pen%d\r\n,pin,pout,eff*100,pen); } #elif TASK_NUM3 printf([T3 coil-tune] Pin%.2f Pout%.2f eff%.1f%% ←调线圈 pen%d\r\n,pin,pout,eff*100,pen); #elif TASK_NUM4 printf([T4 load-test] Pin%.2f Pout%.2f eff%.1f%% ←换笔记录 pen%d\r\n,pin,pout,eff*100,pen); #endif LED_Update(pen, pout, fault); /* 4灯故障闪/离位灭/在位按功率1~4颗 */ HAL_Delay(200); /* ---- USER CODE END 3 ---- */ } }附录 A交流点 RMS仅当检测点确为交流线圈侧时用F1 采不动 150kHz需双 ADC 同步 定时器触发 DMA 批采见之前的采样方案。DC 点不要用这个。/* 对同步采样缓冲(低16V,高16I)算 Vrms/Irms/有功P先减均值去直流 */ void RMS_FromBuf(const uint32_t *buf, uint16_t n, float divV, float rs, float g, float *Vrms, float *Irms, float *P){ uint32_t sv0,si0; for(uint16_t k0;kn;k){ sv(buf[k]0xFFFF); si((buf[k]16)0xFFFF); } float vm(float)sv/n, im(float)si/n; double aV0,aI0,aP0; for(uint16_t k0;kn;k){ float v((float)(buf[k]0xFFFF)-vm)*(VREF/ADC_FS)*divV; float i((float)((buf[k]16)0xFFFF)-im)*(VREF/ADC_FS)/g/rs; aV(double)v*v; aI(double)i*i; aP(double)v*i; /* double防溢出 */ } *Vrmssqrtf(aV/n); *Irmssqrtf(aI/n); *P(float)(aP/n); /* P自带功率因数 */ }附录 B现场踩坑速记全桥 B 臂不反相→ 两臂同相不换流没能量。CubeMX 里 CH2 必须设PWM mode 2。死区忘设/太小→ 上下管直通烧 MOS。DEADTIME_NS从 500ns 起示波器确认上下管驱动不重叠。改频率出毛刺脉冲→ TIM1 Counter Settings 里auto-reload preload Enable。ADC 乱跳/不准→ ADC 时钟没 /6超 14MHz、没校准、采样时间太短。扫频扫过谐振点→ 一定高频→低频SWEEP_FMIN别低于谐振容性区会炸管。功率算成视在功率→ 交流点必须mean(v·i)直流点用Vdc×Idc。别用 Vrms×Irms 当有功。printf 无输出→ CubeIDE 重写__io_putchar不是 fputc/MicroLIB并setvbuf关缓冲。保护误触发→ 阈值留裕量对采样值做均值滤波避免开关噪声瞬时误判。