有线学习笔记(A)
2026/7/5 8:56:58 网站建设 项目流程

## 5. 任务四:固定频率+最佳位姿,换负载测功率/效率

**目标**:保持任务2频率、任务3最佳位姿,换不同充电笔(换负载),算各负载下功率与效率并打印。

**原理**:负载变化改变 RX 反射阻抗与工作点,效率随负载变。此题看不同负载下系统表现。

```c
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` 区。

```c
/* ==================== 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_TYPE==HALL_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/step+0.5f); if(d>127)d=127; 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,(arr+1)/2);
#if BRIDGE_TYPE==BRIDGE_FULL
__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,(arr+1)/2);
#endif
}
void PWM_Start(void){
TIM_BreakDeadTimeConfigTypeDef bd={0};
bd.OffStateRunMode=TIM_OSSR_DISABLE; bd.OffStateIDLEMode=TIM_OSSI_DISABLE;
bd.LockLevel=TIM_LOCKLEVEL_OFF; bd.DeadTime=deadtime_dtg(DEADTIME_NS);
bd.BreakState=TIM_BREAK_DISABLE; bd.BreakPolarity=TIM_BREAKPOLARITY_HIGH;
bd.AutomaticOutput=TIM_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_TYPE==BRIDGE_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 s=0; for(uint8_t k=0;k<ADC_OVS;k++) s+=adc_raw[r+k*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_CASE==EFF_RECT_OVER_IN
return rank_volt(R_VRECT,VRECT_DIV)*shunt_amp(R_IRECT,IRECT_RS,IRECT_G);
#elif EFFICIENCY_CASE==EFF_VOUT_OVER_IN
return rank_volt(R_VOUT,VOUT_DIV)*shunt_amp(R_IOUT,IOUT_RS,IOUT_G);
#elif EFFICIENCY_CASE==EFF_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){
*pin=Power_In(); *pout=Power_Out(); *eff=(*pin>0.001f)?(*pout/ *pin):0.0f;
}

uint8_t Hall_PenOK(void){
#if HALL_TYPE==HALL_DIGITAL
static uint8_t st=0,c=0;
uint8_t now=(HAL_GPIO_ReadPin(HALL_GPIO_Port,HALL_Pin)==HALL_ACTIVE);
if(now!=st){ if(++c>=5){st=now;c=0;} } else c=0; return st;
#else
static uint8_t p=0; float v=rank_volt(R_HALL,1.0f);
if(!p&&v>HALL_ON_V)p=1; if(p&&v<HALL_OFF_V)p=0; return p;
#endif
}
static void LED_SetBar(uint8_t n){
HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,n>=1);
HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,n>=2);
HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,n>=3);
HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,n>=4);
}
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 n=pout>P_L4?4:pout>P_L3?3:pout>P_L2?2:pout>P_L1?1:0; LED_SetBar(n);
}
uint8_t Protection_Check(void){
float vin=rank_volt(R_VIN,VIN_DIV), vr=rank_volt(R_VRECT,VRECT_DIV), iin=shunt_amp(R_IIN,IIN_RS,IIN_G);
if(vin<UV_VIN)return 1; if(vin>OV_VIN)return 2; if(vr>OV_VRECT)return 3; if(iin>OC_IIN)return 4; return 0;
}

#if TASK_NUM==2
uint32_t Task2_SweepBestFreq(void){
float bestEff=0; uint32_t bestF=PWM_INIT_HZ;
for(uint32_t f=SWEEP_FMAX;f>=SWEEP_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(eff>bestEff&&pin>0.1f){bestEff=eff;bestF=f;}
#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_TYPE==BRIDGE_FULL)?"FULL":"HALF", DEADTIME_NS);
PWM_Start();
#if (TASK_NUM==3)||(TASK_NUM==4)
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 fault=0;
#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_NUM==1
{ /* 任务1:出口检测+器件判断 */
float vrect=rank_volt(R_VRECT,VRECT_DIV);
if(vrect>VRECT_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_NUM==2
{ /* 任务2:只扫频一次,之后保持最佳频率 */
static uint8_t done=0;
if(!done && !fault){ Task2_SweepBestFreq(); done=1; }
printf("[T2] hold-best Pin=%.2f Pout=%.2f eff=%.1f%% pen=%d\r\n",pin,pout,eff*100,pen);
}
#elif TASK_NUM==3
printf("[T3 coil-tune] Pin=%.2f Pout=%.2f eff=%.1f%% ←调线圈 pen=%d\r\n",pin,pout,eff*100,pen);
#elif TASK_NUM==4
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 点不要用这个。

```c
/* 对同步采样缓冲(低16=V,高16=I)算 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 sv=0,si=0;
for(uint16_t k=0;k<n;k++){ sv+=(buf[k]&0xFFFF); si+=((buf[k]>>16)&0xFFFF); }
float vm=(float)sv/n, im=(float)si/n;
double aV=0,aI=0,aP=0;
for(uint16_t k=0;k<n;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防溢出 */
}
*Vrms=sqrtf(aV/n); *Irms=sqrtf(aI/n); *P=(float)(aP/n); /* P自带功率因数 */
}
```

---

## 附录 B:现场踩坑速记

1. **全桥 B 臂不反相** → 两臂同相=不换流=没能量。CubeMX 里 CH2 必须设 **PWM mode 2**。
2. **死区忘设/太小** → 上下管直通烧 MOS。`DEADTIME_NS` 从 500ns 起,示波器确认上下管驱动不重叠。
3. **改频率出毛刺脉冲** → TIM1 Counter Settings 里 **auto-reload preload = Enable**。
4. **ADC 乱跳/不准** → ADC 时钟没 /6(超 14MHz)、没校准、采样时间太短。
5. **扫频扫过谐振点** → 一定 **高频→低频**,`SWEEP_FMIN` 别低于谐振,容性区会炸管。
6. **功率算成视在功率** → 交流点必须 `mean(v·i)`;直流点用 `Vdc×Idc`。别用 Vrms×Irms 当有功。
7. **printf 无输出** → CubeIDE 重写 `__io_putchar`(不是 fputc/MicroLIB),并 `setvbuf` 关缓冲。
8. **保护误触发** → 阈值留裕量+对采样值做均值滤波,避免开关噪声瞬时误判。
```

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询