发新帖本帖赏金 5.00元(功能说明)我要提问
123下一页
返回列表
[国产单片机]

PD快充也没那么神秘CH543D(PD诱骗受电Sink解析)

[复制链接]
6595|66
手机看帖
扫描二维码
随时随地手机跟帖
Liuhl1998|  楼主 | 2022-3-16 15:01 | 显示全部楼层 |阅读模式
本帖最后由 Liuhl1998 于 2022-8-8 18:00 编辑

随着USB PD3.1的发布,PD快充将支持最高48V电压输出,充电功率同步提升至240W,在生活中PD充电器随处可见,为人们日常提供不可或缺的便捷。如果有一款合适的“诱”电端,PD充电器就可以连续输出可调节的电压,这对于工程师来说不就相当于一个可调的电压源吗?基于CH543的评估板做了这样一个受电端,效果差不多这样:
视频演示.gif


PD快充是基于CC线通讯完成,需要遵循USB Power Delivery规范,从PD协议规范中可以了解到具体的协商过程:

PD 快充流程图

PD 快充流程图

由上图可以看出USB PD通讯就是Host(Source)和Power Brick(Sink)之间的数据交互。当Sink接入后(主机检测到5.1K下拉电阻的接入),主机(Source)将在100ms~200ms内会发送Source Cap(包含可提供电压档位的PDO)。
设备接收到主机发来的消息时会回复Good CRC进行响应,并在24ms内回复Request(请求需要的电压电流)。
主机收到设备发来的Request会回复Good CRC进行响应,并检测自身PDO是否支持设备Request,支持:会在24ms内回复Accept,不支持:回复Reject。
设备接收到主机发来的Accept/Reject,都会回复Good CRC进行响应。
主机若是支持设备请求,将会在450ms进行调压,调压完成后发送PS_RDY,然后等待设备回复Good CRC进行响应。


理论明白了,现在就动手来实践吧,硬件采用的是:“沁恒微电子”的CH543评估板,CH543 内置了 USB Power Delivery 控制器和 PD BMC PHY 收发器,支持 USB type C、BC、PD2.0、PD3.0、PPS,还支持 12V 高压电源,PD通讯时需要的BMC编解码、4B5B编解码、CRC校验也都有集成,这样的话用CH543实现一个PD通讯就比较简单了。

CH543PD简介

CH543PD简介

单片机原理图CC可以直接连接Type-C端口,不需要额外添加器件(CH543内置5.1K下拉),电路还是比较简单的:

原理图.png

我这次编写的是PD Sink受电端,那么我只需要在合适的场景下及时回复Good CRC和Request,就能完成USB PD通讯。在消息处理上,我是根据CH543 USB PD中断的特点来进行相应消息处理,软件如下:

void PD_PHY_ISR(void) interrupt INT_NO_USBPD using 1
{
        if ( PIF_RX_RST ) {/*接收到复位信息中断*/
                printf("RST ");
                PD_PHY_HRST_ISR();                //收到HRST
                PD_PHY_RX_INIT();
        }
        if ( PIF_RX_ACT ){/*数据包接受完成中断*/
            TR0 = 0;  ET0 = 0;
                        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){
                                                        PD_PHY_STAT.WaitingGoodCRC = 0;                  
                                                        if(PD_PHY_STAT.SendingRequest == 1){ PD_PHY_STAT.SendingRequest = 0;}        
                                                        Send_Count = 0;
                                                        PD_PROT_ISR();                                             
                                }
                        }else {               
                                        switch(Union_Header->HeaderStruct.MsgType)
                                        {
                                                case SourceCap:
                                                                 NDORcv=Union_Header->HeaderStruct.NDO;
                                                           memcpy(PD_Source,PD_RX_BUF,30);
                                                                 PD_PHY_STAT.SendingRequest = 1;
                                                                 break;
                                                case Request:
                                                                 break;
                                                case Accept:
                                                           MsgID ++;
                                                                 break;
                                                case Reject:
                                                                 break;
                                                case PS_RDY:
                                                                 break;
                                                case GetSrcCap:
                                                            MsgID++;
                                                           PD_PHY_STAT.SendingSourceCap = 1;
                                                     break;
                                                case GetSinkCap:
                                                                 MsgID++;
                                                                 PD_PHY_STAT.SendingSinkCap = 1;         
                                                                 break;
                                          case SourceCap_VDM:
                                                                 break;        
                                                default :
                                                break;        
                                        }
                                                mDelayuS(25);
                                          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;        
                                           mTimer_x_SetData(10000);    //5ms
                                        PD_PHY_RX_INIT();
                }
        }
}
Sink端的程序编写从一个Request开始,Request请求结构如下:

Request解析

Request解析


下面是用CH543编写Request的代码:
void PD_PHY_TX_Request(void)
{
        UINT16 Volt_Value;
  UINT8 temp=0xff;  //pdo 档位
        UINT8 i;
        UINT16 Temp;
        UINT16 Data_H8;
        UINT16 Cur_Temp = 0;
        
        for (i=0;i!=NDORcv;i++)
        {
                        Union_SrcCap = (_Union_SrcCap *)&PD_Source[2+(4*i)];  
                        Data_H8 = (Union_SrcCap->SrcCapStruct.DataH8);
                        if((Data_H8 >> 6 ) == 3)
                        {
                                        PPS_Flag = 1;
                                        printf("\r %d is PPS\n",(UINT16)(i+1));
                        }else{
                                                        Temp = (PD_Source[3+(i<<2)] >> 2)+((PD_Source[4+(i<<2)] & 0x0F)<<6);
                                                        Temp*=50;
                                                        if (Temp <= Volt_Value )
                                                        {
                                                                if(Temp > Cur_Temp)
                                                                {
                                                                        Cur_Temp = Temp;
                                                                        temp = i+1;
                                                                }
                                                        }
                         }
        }
        if(Temp_PDO != temp )
        {
                printf("No requested voltage! \r\n");
                printf("Request adjacent voltage! \r\n");
        }
        if(temp != 0xff)
        {
                        UPD_T_SOP = UPD_SOP0;
                        UPD_T_LEN = 6;
                        PD_TX_BUF[5] =( (temp<<4) | 0x02);
                        PD_TX_BUF[4] = ((PD_Source[3+((temp - 1)<<2)]&0x03)<<2) |(PD_Source[2+((temp - 1)<<2)]>>6) | (PD_Source[4+((temp - 1)<<2)] &0xF0);
                        PD_TX_BUF[3] =(PD_Source[3+((temp - 1)<<2)]&0x03) | (PD_Source[2+((temp - 1)<<2)]<<2);
                        PD_TX_BUF[2] = PD_Source[2+((temp - 1)<<2)];
                        PD_TX_BUF[1] = (0x10 | (MsgID << 1));
                        PD_TX_BUF[0] =0x02 | (0xC0 & PD_Source[0]);
                        PD_PHY_TX_INIT();
        }else{        
          printf("No Matched Volt.\r\n");
        }
}

按照PD的协商流程,可以完成PD通讯。另外在工程中增加了I/O中断来调节电压档位升降:
主程序内循环检测:
 if(Gears_Plus == 1)
        {
     if(P1_5 == 1)
                 {
                         PD_Request_Gears += 1;
                         Gears_Plus = 0;
                 }
                if(PD_Request_Gears>5)        
                 {
      PD_Request_Gears = 1;
                 }                        
        }
        if(Gears_Sub == 1)
        {
                 if(P1_4 == 1)
                 {
                         PD_Request_Gears -= 1;
                         Gears_Sub = 0;
                 }
     if(PD_Request_Gears<1)        
                 {
      PD_Request_Gears = 5;
                 }                        
        }
        if(Temp_PDO !=PD_Request_Gears)
        {
                Temp_PDO = PD_Request_Gears;

                printf("Temp_PDO == %d\r\n",(UINT16)Temp_PDO);
                PD_PHY_TX_Request();
        }



通过逻辑分析仪观察,PD的SINK通讯成功!

逻辑分析仪采样

逻辑分析仪采样


当主机发来Source Cap后,设备端点按照PD协议流程进行响应并回复Request,直到主机发送PS_RDY 结束一次PD请求调压过程,然后设备端点可以多次发送Request来请求新的电压,从上图中可以看出,多次请求成功!

至此PD Sink端的应用升降电压就实现了,另外要实现PPS调压还需要完善程序代码(PPS和固定档位的PDO处理方法有一定区别),后面会找时间继续。附件是硬件、软件资料,小伙伴们可以按需下载。



CH543SCH.pdf

189.54 KB

CH543DS1.PDF

735.67 KB

CH543 PD.zip

1022.15 KB

使用特权

评论回复

打赏榜单

lixiaojun1 打赏了 5.00 元 2023-12-25
理由:谢谢博主指导

评论
pan123pan 2022-10-17 08:59 回复TA
楼主您好,请问最新的资料,可以分享下吗 
Liuhl1998 2022-10-9 13:08 回复TA
@wang159357 :CS957914743 
wang159357 2022-10-9 11:12 回复TA
@Liuhl1998 :您好,请问有最新的代码吗,怎么联系您呢 
Liuhl1998 2022-10-9 10:54 回复TA
@wang159357 :默认5V哈,需要触发 
wang159357 2022-8-22 15:42 回复TA
我用数据线把你的CH543 PD文件的hex烧录进板子,再用万用表测电压,只有5v左右不动的电压,并没有诱骗成功啊 
tangxiao1979 2022-8-2 11:33 回复TA
楼主你好,能否给个联系方式,我有事想咨询一下,谢谢 
吃一颗草莓莓 2022-3-20 19:54 回复TA
看完我就懂了,原来我差的是个脑子 

相关帖子

Liuhl1998|  楼主 | 2022-3-16 16:34 | 显示全部楼层
本帖最后由 Liuhl1998 于 2022-3-18 18:33 编辑

叙述的是大概流程,想要深入了解的可以参考www.usb.org 下载PD协议相关手册。

使用特权

评论回复
评论
tangxiao1979 2022-8-2 11:34 回复TA
楼主你好,能否给个联系方式,有事想咨询一下,谢谢 
cjseng| | 2022-3-18 15:36 | 显示全部楼层
楼主很厉害,不错

使用特权

评论回复
ahongW| | 2022-3-18 17:32 | 显示全部楼层
看完了感觉很神秘!

使用特权

评论回复
吃一颗草莓莓| | 2022-3-20 20:02 | 显示全部楼层
我不允许这篇贴还没有人看到!写得太好了吧!

使用特权

评论回复
ughbss| | 2022-3-22 13:42 | 显示全部楼层
有空我也研究下PD

使用特权

评论回复
Liuhl1998|  楼主 | 2022-3-23 10:04 | 显示全部楼层
cjseng 发表于 2022-3-18 15:36
楼主很厉害,不错

使用特权

评论回复
Liuhl1998|  楼主 | 2022-3-23 10:05 | 显示全部楼层
ughbss 发表于 2022-3-22 13:42
有空我也研究下PD

我这有好多资料,需要时候扣我

使用特权

评论回复
评论
Liuhl1998 2023-4-12 18:38 回复TA
@zengzh :附件里有源码哈 
zengzh 2023-2-2 10:53 回复TA
邮箱:1686374074@qq.com 
zengzh 2023-2-2 10:48 回复TA
@zengzh :邮箱:1686374074@qq.com 
zengzh 2023-2-2 10:48 回复TA
楼主,您这边的PD快充的资料可以发我一份吗,我最近在学习PD快充协议,非常感谢! 
Liuhl1998 2022-10-9 13:09 回复TA
@wang159357 :需要资料? 
wang159357 2022-10-9 11:13 回复TA
Liuhl1998|  楼主 | 2022-3-23 10:06 | 显示全部楼层
ahongW 发表于 2022-3-18 17:32
看完了感觉很神秘!

为了篇幅 精简,牺牲了内容。

使用特权

评论回复
pq113_6| | 2022-3-25 16:15 | 显示全部楼层
CH549应该也支持吧?官方资料有点少,讲的不清楚,是不是要用到库?

使用特权

评论回复
评论
Liuhl1998 2022-4-6 16:33 回复TA
回头研究下 
Liuhl1998|  楼主 | 2022-4-11 10:01 | 显示全部楼层

使用特权

评论回复
playxzy| | 2022-4-13 15:17 | 显示全部楼层
学习了

使用特权

评论回复
mainch| | 2022-5-23 14:25 | 显示全部楼层
谢谢楼主,有用的东西

使用特权

评论回复
Liuhl1998|  楼主 | 2022-7-21 14:51 | 显示全部楼层
有最新代码   需要的说一声

使用特权

评论回复
评论
Liuhl1998 2022-10-9 13:09 回复TA
@wang159357 :联系方式 ?? 
wang159357 2022-10-9 10:17 回复TA
我想要,楼主怎么联系 
dryy| | 2022-7-29 20:31 | 显示全部楼层
Liuhl1998 发表于 2022-7-21 14:51
有最新代码   需要的说一声

楼主请问有协议层代码可以分享一下吗

使用特权

评论回复
zwsam| | 2022-8-8 09:13 | 显示全部楼层

使用特权

评论回复
Liuhl1998|  楼主 | 2022-8-8 17:59 | 显示全部楼层
dryy 发表于 2022-7-29 20:31
楼主请问有协议层代码可以分享一下吗

都在例程里了哈,需要最新的可以私我

使用特权

评论回复
Liuhl1998|  楼主 | 2022-8-8 17:59 | 显示全部楼层

使用特权

评论回复
stb988| | 2022-8-23 08:43 | 显示全部楼层

使用特权

评论回复
gentlman2006| | 2022-8-27 16:50 | 显示全部楼层
请问楼主PPS的实现了吗

使用特权

评论回复
评论
Liuhl1998 2022-10-17 09:44 回复TA
@zou870848836 :联系方式? 
zou870848836 2022-10-11 15:41 回复TA
@Liuhl1998 :请问能学习下吗? 
Liuhl1998 2022-10-9 10:52 回复TA
实现了 
Liuhl1998 2022-10-9 10:51 回复TA
早就实现了 
发新帖 本帖赏金 5.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

16

主题

109

帖子

23

粉丝