pdSink module

Status Last Update API Version
Active 2019-11-20 V1.00.00

Description

This module provides access to Power Delivery protocol as a role of sink.

pdSink and fastChgTrig

As you might think, the pdSink module should be fit into fastChgTrig module, PD is also a fast-charge protocol after all. But there’re a few reasons that one prefers not to do so:

  • PD protocol is much more powerful than the others, its operation principle is so different, making it hard to fit into fastChgTrig software model.

  • Unlike protocols in fastChgTrig, PD doesn’t use D+ D- as communication lines. In fact, the hardware for PD and that for other protocols are totally independent.

Precautions

  • Using this module requires some basic knowledge of PD protocol, whose specification could be found on USB-IF website.

  • Though the hardware for PD and other protocols are independent (In theory, they could work simultaneously), the charger would not be happy about two protocols running at the same time, this could make conflicting requests and generate undefined behaviors. Before using pdSink, one should call fastChgTrig.open() just like using fastChgTrig module, this prevents the situation stated above.

  • The module is designed to make a balance between complexity and flexibility. One who fully understands the APIs, but with lack of the knowledge of PD and the very hardware, bottom layer software implementation on this meter, could easily find his script not working, and difficult to find out the reason. It’s highly recommended to have a look at the example code at the bottom of this page.

Typical call sequence

The following lists the sequence of calls to establish PD with the charger and get voltage & current you want from the charger: 1.Call pdSink.init() to initialize hardware and bottom software layer, check its result, if pdSink.OK, continue operation.

2.Check pdSink.isSrcCapReceived() with a timeout of 2 seconds, if timeouts, operation fail, or you call pdSink.sendHardReset() and restart from step 1. Notice that after CC is attached, the charger typically sends src_cap message for only a few seconds (Detailed information could be found on the specification), this means except your code runs on start-up, your attempt to receive src_cap is doomed to end up with a timeout. To handle this case, you issues a hard reset with pdSink.sendHardReset() to reset the charger and you can receive src_cap again.

3.Call pdSink.getSrcCapNum() and pdSink.getSrcCap() to pass source capabilities to lua region, this step could be omitted if you already knows your charger’s capabilities or you don’t need such information.

4.Call pdSink.request() to get voltage and current limit you wanted.

pdSink.init()

Description

This initializes the pdSink module, it does the following things:

  • Get source attachment status, if no source attached, it returns pdSink.FAIL.

  • If attachment detected, initialize PD communications on the corresponding CC pin and return pdSink.OK. Reset bottom layer software status.

One should check the result to determine if further operation could be performed.

Parameters

nil

Return value

number pdSink.FAIL if failed. pdSink.OK if done.

Example call

rtval = pdSink.init()

pdSink.getCCStatus()

Description

This function doen’t do any hardware work, it just returns the CC attachment status after pdSink.init() call.

Parameters

nil

Return value

number pdSink.CC1_SRC_ATTACHED if source is attached on CC1. pdSink.CC2_SRC_ATTACHED if source is attached on CC2. pdSink.NO_SRC_ATTACHED if source is not attached.

Example call

status = pdSink.getCCStatus()

pdSink.isSrcCapReceived()

Description

Check if src_cap message is received. This status is cleared when pdSink.init() is called, and raised when a src_cap message is received.

Parameters

nil

Return value

boolean true if received. false if haven’t received yet..

Example call

received = pdSink.isSrcCapReceived()

pdSink.getNumofSrcCap()

Description

Get number of objects in Source_Capability message.

Parameters

nil

Return value

number Number of objects in Source_Capability message.

Example call

pdo_num = pdSink.getNumofSrcCap()

pdSink.getSrcCap()

Get specified PDO with an index.

Parameters

Name Type Range Usage
<index> number 0~6 Specify the index of the object to get

Return value

table Returns a table with the following key-value pairs:

Key Type Range Usage
type number pdSink.FIXED pdSink.BATTERY pdSink.VARIABLE pdSink.AUGMENTED Type of the PDO
voltage number If the PDO is type FIXED, this is the voltage of the PDO. If the PDO is type AUGMENTED, this is the minimum voltage of the PDO.
voltageMax number If the PDO is type FIXED, this field has no meaning. If the PDO is type AUGMENTED, this is the maximum voltage of the PDO.
currentMax number Maximum current of the PDO.

Example call

pdo0 = pdSink.getSrcCap(0)

pdSink.sendHardReset()

Description

Send a hard reset message.

Parameters

nil

Return value

nil

Example call

pdSink.sendHardReset()

pdSink.request()

Description

Send a request message to request specified voltage and current limit.

Parameters

Name Type Range Usage
<pdo> number 0~6 Specify the index of the PDO to request
[voltage] number If the PDO is FIXED, the FIXED voltage should be this parameter. If the PDO is AUGMENTED, this specifies the voltage to request, should be in the range of the corresponding PDO.If not specified, and the PDO is FIXED, the voltage will be the FIXED voltage.
[current] number Specify the operation current to request, this should be in the range of the corresponding PDO. If not specified, the current is the maxium current of the PDO.

Return value

number pdSink.OK if the request success with PS_Ready received. pdSink.FAIL if the request timed out or without PS_Ready received.

Example call

--Request PDO with index 1, voltage 9V, current 3A
result = pdSink.request(1,9.00,3.00)

pdSink.deinit()

Description

Disable PD communication, and reset bottom layer software to default status. Notice that this won’t reset the charger, you need to call pdSink.sendHardReset() additionally if you want to do so.

Parameters

nil

Return value

nil

Example call

pdSink.deinit()

pdSink.onSrcCapReceived()

Description

Register or unregister a callback when a source capability message is received.

Parameters

Name Type Range Usage
<callback> nil or function Specify the callback function, when such event happened, the callback is called, the callback won't be unregistered automatically. To unregister the callback, pass nil to this function.

Return value

nil

Example call

function onSrcCapReceived() 
  print("Source Capability received.")
end

pdSink.onSrcCapReceived(onSrcCapReceived)

Example code (Procedural)

--[[This is a demo for PD APIs.
    You connect the meter to a PD charger with a C-C cable, and DO NOT plug-in any other device.
    The output of the demo is on the debug terminal.
   
    The demo detect if any source is attached first, if no source is detected, it exits.
    Then it waits for src_cap, if not received, it sends a HARD_RST message to reset the charger, if still no src_cap is received, it exits.
    After the src_cap received, it prints out the src_cap on the terminal.
    Then it started to request FIXED pdos of the charger one by one.
    If there is any AUGMENTED(PPS) pdo, it requests them one by one.
    The voltage reading and request status is on the terminal.

    Detailed Documentation:
    Version: 191120
    Author:  yanke928
]]

function WaitForSourceCap()
  i = 2000
  while(i > 0) do 
    i = i - 1;
    if(pdSink.isSrcCapReceived()) then 
      return true
    end
    delay.ms(1)
  end
  return false
end

function DemoEnd()
  print("---End of PD demo---")
  os.exit(0)
end

print("---Start of PD demo---")

--Open fastChgTrig
if(fastChgTrig.open() ~= fastChgTrig.OK) then
  print("Failed to open fastChgTrig") 
  DemoEnd()
end

--Init PD
pdSink.init()

print("PD inited") 

--Check if any source is attached
if(pdSink.getCCStatus() == pdSink.NO_SRC_ATTACHED) then
  print("No CC attached") 
  DemoEnd()
end

print("Waiting for src_cap") 

--Wait for src_cap.
received = WaitForSourceCap()

--In case of time out, try a hard reset
if(received  == false) then
  print("src_cap timeout, sending hard reset") 
  pdSink.sendHardReset()
  pdSink.init()

  print("Waiting for src_cap again.") 
  received = WaitForSourceCap()

  --If we still cannot receive src_cap
  if(received == false) then
    print("No src_cap received, exiting...")
    DemoEnd()
  end
end

print("src_cap received")

delay.ms(1000)

--Get src_cap into table
src_cap_num = pdSink.getNumofSrcCap()
print(string.format("We have %d src_caps:",src_cap_num))
src_caps = {}
for i = 0,src_cap_num -1 do
  src_caps[i] = pdSink.getSrcCap(i)
  --Fixed type, print voltage and max current
  if(src_caps[i].type == pdSink.FIXED) then
     print(string.format("No.[%d] Type[FIXED] Voltage[%.2fV] Current[%.2fA]",i,src_caps[i].voltage,src_caps[i].currentMax))
  elseif (src_caps[i].type == pdSink.AUGMENTED) then
     print(string.format("No.[%d] Type[AUGMENTED] Voltage[%.2f~%.2fV] Current[%.2fA] ",i,src_caps[i].voltage,src_caps[i].voltageMax,src_caps[i].currentMax))
  else
     print(string.format("No.[%d] Unknown Type",i))
  end
end

meter.setDataSource(meter.INSTANT)

print("Started to request FIXED PDOs")
for i = 0,src_cap_num -1 do
  if(src_caps[i].type == pdSink.FIXED) then
     print(string.format("Requesting FIXED No.%d",i))
     if(pdSink.request(i,src_caps[i].voltage,src_caps[i].currentMax) ~= pdSink.OK) then 
        print("Request failed")
        DemoEnd()
     end
     print(string.format("Request Success, voltage now: %.3fV",meter.readVoltage()))
     delay.ms(1000)
   end
end

print("Started to request AUGMENTED PDOs")
for i = 0,src_cap_num -1 do
  if(src_caps[i].type == pdSink.AUGMENTED) then
     --Here, we use the middle point of the PPS voltage to give an example.
     req_volt = (src_caps[i].voltage + src_caps[i].voltageMax) / 2
     print(string.format("Requesting AUGMENTED No.%d Voltage %.3fV",i,req_volt))
     if(pdSink.request(i,req_volt,src_caps[i].currentMax) ~= pdSink.OK) then 
        print("Request failed")
        DemoEnd()
     end
     print(string.format("Request Success, voltage now: %.3fV",meter.readVoltage()))
     delay.ms(1000)
   end
end

DemoEnd()

Example code (Event-Driven)

--[[This is a event-driven demo for PD APIs.
    You connect the meter to a PD charger with a C-C cable, and DO NOT plug-in any other device.
    The output of the demo is on the debug terminal.
   
    The demo detect if any source is attached first, if no source is detected, it exits.
    Then it waits for src_cap, if not received, it sends a HARD_RST message to reset the charger, if still no src_cap is received, it exits.
    After the src_cap received, it prints out the src_cap on the terminal.
    Then it started to request FIXED pdos of the charger one by one.
    If there is any AUGMENTED(PPS) pdo, it requests them one by one.
    The voltage reading and request status is on the terminal.

    Detailed Documentation:
    Version: 191120
    Author:  yanke928
]]


timeoutTimerCount = 0
srcCapOK = false
src_caps = {}
src_cap_num = 0
request_index = 0

function demoEnd()
  print("----End of demo----")
  os.exit()
end

function requestTimer_callback(tim)
   if(src_caps[request_index].type == pdSink.FIXED) then
     print(string.format("Requesting FIXED No.%d",request_index))
     if(pdSink.request(request_index,src_caps[request_index].voltage,src_caps[request_index].currentMax) ~= pdSink.OK) then 
        print("Request failed")
        DemoEnd()
     end
     print(string.format("Request Success, voltage now: %.3fV",meter.readVoltage()))
   else if(src_caps[request_index].type == pdSink.AUGMENTED) then
       --Here, we use the middle point of the PPS voltage to give an example.
       req_volt = (src_caps[request_index].voltage + src_caps[request_index].voltageMax) / 2
       print(string.format("Requesting AUGMENTED No.%d Voltage %.3fV",request_index,req_volt))
       if(pdSink.request(request_index,req_volt,src_caps[request_index].currentMax) ~= pdSink.OK) then 
          print("Request failed")
          DemoEnd()
       end
       print(string.format("Request Success, voltage now: %.3fV",meter.readVoltage()))
     end
   end

   request_index = request_index + 1
   if(request_index == src_cap_num) then 
     print("All PDO requested, exiting...")
     demoEnd()
   end
end

function timeoutTimer_callback(tim)
  timeoutTimerCount = timeoutTimerCount + 1

  if(srcCapOK ~= true) then
    if(timeoutTimerCount == 2) then
      print("No src_cap received, exiting...")
      demoEnd()
    end
    print("src_cap timeout, sending hard reset...") 
    pdSink.sendHardReset()
    pdSink.init()
  else 
    requestTimer= tmr.new()
    requestTimer:setup(0,1000,tmr.AUTO_RELOAD,requestTimer_callback)
    requestTimer:start()
    pdSink.onSrcCapReceived(nil)
    tim:remove()
  end
end

function onSrcCapReceived() 
  print("Source Capability Received")
  srcCapOK = true
  --Get src_cap into table
  src_cap_num = pdSink.getNumofSrcCap()
  print(string.format("We have %d src_caps:",src_cap_num))
  for i = 0,src_cap_num -1 do
    src_caps[i] = pdSink.getSrcCap(i)
    --Fixed type, print voltage and max current
    if(src_caps[i].type == pdSink.FIXED) then
       print(string.format("No.[%d] Type[FIXED] Voltage[%.2fV] Current[%.2fA]",i,src_caps[i].voltage,src_caps[i].currentMax))
    elseif (src_caps[i].type == pdSink.AUGMENTED) then
       print(string.format("No.[%d] Type[AUGMENTED] Voltage[%.2f~%.2fV] Current[%.2fA] ",i,src_caps[i].voltage,src_caps[i].voltageMax,src_caps[i].currentMax))
    else
      print(string.format("No.[%d] Unknown Type",i))
    end
  end
end 

print("----Start of demo----")

if(fastChgTrig.open() ~= fastChgTrig.OK) then 
  print("Failed to open fastChgTrig")
  demoEnd()
end

--Init PD
pdSink.init()
print("PD inited") 

--Check if any source is attached
if(pdSink.getCCStatus() == pdSink.NO_SRC_ATTACHED) then
  print("No CC attached") 
  DemoEnd()
end

timeoutTimer = tmr.new()
timeoutTimer :setup(2000,2000,tmr.AUTO_RELOAD,timeoutTimer_callback)
timeoutTimer :start()
pdSink.onSrcCapReceived(onSrcCapReceived)
print("Waiting for src_cap...")