提到快充协议都会想到USB PD快充,实际市面上还有很多主流的快充协议,比如华为的FCP、SCP,三星的AFC,OPPO的VOOC,高通的QC,VIVO的Flash Charge等……,如此之多的快充协议该如何来辨别?相信很多电粉和相关的开发人员应该都知道PowerZ这款性能强悍的诱骗产品,支持QC2.0、QC3.0、VOOC、SCP、SSCP、PD等协议的触发。本期将使用CH543制作一个简易版的诱骗器Power Low。
以上是利用CH543设计的简易版电路原理图,可通过DM、DP、CC来实现主要的诱骗功能,通过DM、DP来触发QC、AFC、FCP等协议,通过CC触发PD协议。屏幕显示采用SPI驱动的TFT:ST7735S(占用片外和片内资源,后续将更换成I2C驱动的屏)。设计中还使用了Power Z一样的三档拨轮开关,用来选择档位和功能操作等。整体电路设计下来参考价格在15元左右,主要是TFT比较贵,不然可以将成本压缩在10元以内。
因为某些快充协议属于私有协议,处于未公开状态,第三方需要取得授权才可以使用,所以本次将围绕USB PD快充的诱骗触发来讲解。首先诱骗器的定位是Sink受电端,所以CC引脚上必须要有5.1K的下拉电阻,才能让Source供电端进行识别送电。系统默认请求5V电压,可通过三挡拨轮开关进行选择Fixed或者PPS,同时可以调节选择请求不同的电压。具体的实物效果如下:
很Power Low Low Low Low Low ……
以下为PD通讯协商过程,利用PD中断来解析Source发送的协议包,收到SourceCap包时会将具体内容进行缓存在PDO_BUF中:
/*******************************************************************************
* Function Name : PD_PHY_ISR(void)
* Description : PD中断处理函数
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void PD_PHY_ISR(void) interrupt INT_NO_USBPD using 1
{
/*接收到复位信息中断*/
if ( PIF_RX_RST ) {
PD_PHY_HRST_ISR(); //收到HRST
PD_PHY_RX_INIT();
}
/*数据包接受完成中断*/
if ( PIF_RX_ACT ){
PIF_RX_ACT = 0;
if ( (UPD_INT_FG & MASK_PD_STAT) == PD_RX_SOP0 ) { //收到HRST或SOP数据
Union_Header = (_Union_Header *)PD_RX_BUF; //强制转化
if ( PD_PHY_STAT.WaitingGoodCRC ) { //是否在等待GoodCRC
if(Union_Header->HeaderStruct.MsgType == GoodCRC){ //判断收到的消息是否为GOODCRC
PD_PHY_STAT.WaitingGoodCRC = 0;
PD_PHY_Clear_Flag(); //清除标志函数
PD_PROT_ISR();
}
}else {
switch(Union_Header->HeaderStruct.MsgType)
{
case SourceCap:
memcpy(PDO_BUF,PD_RX_BUF,sizeof(PD_RX_BUF));
Versions = Union_Header->HeaderStruct.SpecRev;
SourceCapNDO = Union_Header->HeaderStruct.NDO;
RecvPDOFlag = 1; //修改RecvPDOFlag = 1 开启解析PDO,为0则关闭,若开启后本次回复GOODCRC时间会适当延长,此处默认开启
PD_PHY_STAT.SendingRequest = 1;
break;
case Accept:
break;
case Reject:
break;
case PS_RDY:
RecvPS_RDYFlag = 1;
break;
case GetSinkCap:
MsgID++;
PD_PHY_STAT.SendingSinkCap = 1;
break;
case SourceCap_VDM:
MsgID++;
PD_PHY_STAT.SendingNotSup = 1;
break;
default :
break;
}
mDelayuS(10);
PD_PHY_STAT.SendingGoodCRC = 1; //置发送GoodCRC标志位
PD_PHY_TX_GoodCRC(); //回复GoodCRC
}
}else PD_PHY_RX_INIT();
}
/*数据包发送完成中断 */
if ( PIF_TX_END ) {
PIF_TX_END = 0;
if(CCSel == 1){ //发送完成关闭低压
CC1_CTRL &= ~bCC_LVO;
}else if(CCSel == 2){
CC2_CTRL &= ~bCC_LVO;
}
if ( PD_PHY_STAT.SendingGoodCRC ==1 ) {
PD_PHY_STAT.SendingGoodCRC = 0;
PD_PROT_ISR(); //GoodCRC发送完成,向Prot转交数据
}else { /*开始接收GoodCRC*/
PD_PHY_STAT.WaitingGoodCRC = 1;
Timer_Counter = Save_Counter;
PD_PHY_RX_INIT();
}
}
}
本段用来解析Source支持PD协议的具体档位,发生在一次PD协商结束时,此时RecvPS_RDYFlag = 1,解析上述缓存在PDO_BUF中的SourceCap包,并显示在屏幕(后续会精简优化代码):
/*******************************************************************************
* Function Name : Analysis_PDO(void)
* Description : 解析PDO、PPS函数
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void Analysis_PDO(void)
{
UINT8 Temp;
UINT16 PDO_Value = 0;
if(RecvPDOFlag == 1){
LCD_Fill(0,0,159,12,CYAN);
LCD_DrawLine(0,0,159,0,CYAN);
LCD_DrawLine(0,0,0,127,CYAN);
LCD_DrawLine(159,0,159,127,CYAN);
LCD_DrawLine(0,127,159,127,CYAN);
if(Versions == 2) //判断PD版本
{
LCD_ShowString(3,0,"Power Delivery 3.0",BLACK,CYAN,12,0);
}else{
LCD_ShowString(3,0,"Power Delivery 2.0",BLACK,CYAN,12,0);
}
Union_Header = (_Union_Header *)PDO_BUF; //解析SourceCap
for(Temp=0;Temp<SourceCapNDO;Temp++)//pdo的档位
{
Union_SrcCap = (_Union_SrcCap*)(&PDO_BUF[2+(Temp*4)]);
if((Union_SrcCap->SrcCapStruct.DataH8>>6)== 0x00) //表示固定档
{
PDO_Value =((((Union_SrcCap->SrcCapStruct.VoltH4<<6)+( Union_SrcCap->SrcCapStruct.VoltL6))*50)/100);
if( PDO_Value/10 >= 10) //电压
{
LCD_ShowFloatNum1(108-7*6,(Temp+1)*12,(float)PDO_Value/10,4,CYAN,BLACK,12);
}else{
LCD_ShowFloatNum1(108-6*6,(Temp+1)*12,(float)PDO_Value/10,3,CYAN,BLACK,12);
}
PDO_Value=((((Union_SrcCap->SrcCapStruct.CurrentH2<<8)+( Union_SrcCap->SrcCapStruct.CurrentL8))*10)/10); //电流
LCD_ShowFloatNum1(120,(Temp+1)*12,(float)PDO_Value/100,3,CYAN,BLACK,12);
LCD_ShowString(108,(Temp+1)*12,"@",CYAN,BLACK,12,0);
LCD_ShowString(3,(Temp+1)*12,"< > Fix",CYAN,BLACK,12,0);
LCD_ShowIntNum(10,(Temp+1)*12,Temp+1,1,CYAN,BLACK,12);
LCD_ShowString(108-2*6,(Temp+1)*12,"V",CYAN,BLACK,12,0);
LCD_ShowString(145,(Temp+1)*12,"A",CYAN,BLACK,12,0);
}else if((Union_SrcCap->SrcCapStruct.DataH8>>6)== 0x03){//表示PPS
Union_PPS_SrcCap = (_Union_PPS_SrcCap*)(&PDO_BUF[2+(Temp*4)]); //转换为PPS
PDO_Value = (Union_PPS_SrcCap->SRC_Cap_PPS_Struct.VoltMin);
if(PDO_Value/10 >= 10)
{
LCD_ShowFloatNum1(111-10*6,(Temp+1)*12,(float)PDO_Value/10,4,CYAN,BLACK,12);
}else{
LCD_ShowFloatNum1(111-9*6,(Temp+1)*12,(float)PDO_Value/10,3,CYAN,BLACK,12);
}
PDO_Value = ((Union_PPS_SrcCap->SRC_Cap_PPS_Struct.VoltMaxH1<<7) + (Union_PPS_SrcCap->SRC_Cap_PPS_Struct.VoltMaxL7));
LCD_ShowString(75,(Temp+1)*12,"-",CYAN,BLACK,12,0);
if(PDO_Value/10 >= 10)
{
LCD_ShowFloatNum1(108-4*6,(Temp+1)*12,(float)PDO_Value/10,4,CYAN,BLACK,12);
}else{
LCD_ShowFloatNum1(108-3*6,(Temp+1)*12,(float)PDO_Value/10,3,CYAN,BLACK,12);
}
PDO_Value = ((Union_PPS_SrcCap->SRC_Cap_PPS_Struct.Current)*50)/10;
LCD_ShowFloatNum1(120,(Temp+1)*12,(float)PDO_Value/100,3,CYAN,BLACK,12);
LCD_ShowString(3,(Temp+1)*12,"< > PPS",CYAN,BLACK,12,0);
LCD_ShowIntNum(10,(Temp+1)*12,Temp+1,1,CYAN,BLACK,12);
LCD_ShowString(108,(Temp+1)*12,"V",CYAN,BLACK,12,0);
LCD_ShowString(145,(Temp+1)*12,"A",CYAN,BLACK,12,0);
}
}
LCD_ShowString(44,(Temp+2)*12-6,"Fixed",BLACK,CYAN,12,0);
LCD_ShowString(92,(Temp+2)*12-6,"PPS",CYAN,BLACK,12,0);
RecvPDOFlag = 0;
}
}
因A口协议属于未公开状态,本次将不进行讲解。有兴趣的小伙伴可以多了解一下,附送一张QC2.0的协议规范。
后续会上传附件,在此处公开资料代码和PCB等,开源。多协议电源诱骗触发器Power Low(可多快充协议PD、QC等) - 电源论坛_电源技术论坛 - 21ic电子技术开发论坛https://bbs.21ic.com/icview-3232588-1-1.html