Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STUSB4500 USB-PD chip driver #574

Merged
merged 2 commits into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,16 +254,17 @@ can easily configure them for you specific needs.
<td align="center">SK6812</td>
<td align="center">SK9822</td>
<td align="center">SSD1306</td>
<td align="center">STUSB4500</td>
<td align="center">SX1276</td>
<td align="center">TCS3414</td>
</tr><tr>
<td align="center">TCS3414</td>
<td align="center">TCS3472</td>
<td align="center">TLC594X</td>
<td align="center">TMP102</td>
<td align="center">TMP175</td>
<td align="center">VL53L0</td>
<td align="center">VL6180</td>
</tr><tr>
<td align="center">VL6180</td>
<td align="center">WS2812</td>
</tr>
</table>
Expand Down
65 changes: 65 additions & 0 deletions examples/stm32f072_discovery/stusb4500/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2021, Raphael Lehmann
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#include <modm/board.hpp>
#include <modm/debug/logger.hpp>
#include <modm/driver/usb/stusb4500.hpp>

modm::IODeviceWrapper< Usart1, modm::IOBuffer::BlockIfFull > loggerDevice;
#undef MODM_LOG_LEVEL
#define MODM_LOG_LEVEL modm::log::INFO
modm::log::Logger modm::log::debug(loggerDevice);
modm::log::Logger modm::log::info(loggerDevice);
modm::log::Logger modm::log::warning(loggerDevice);
modm::log::Logger modm::log::error(loggerDevice);

using Sda = GpioB7;
using Scl = GpioB6;
using MyI2cMaster = I2cMaster1;

modm::Stusb4500<MyI2cMaster> usb{};

int
main()
{
Board::initialize();

// Enable USART 1 (TX only)
Usart1::connect<GpioOutputA9::Tx>();
Usart1::initialize<Board::SystemClock, 115200_Bd>();

MODM_LOG_INFO << "STM32F072 Discovery Example: USB-PD with STUSB4500 chip" << modm::endl;

MyI2cMaster::connect<Scl::Scl, Sda::Sda>();
MyI2cMaster::initialize<Board::SystemClock, 400_kHz>();

Board::LedUp::set();

RF_CALL_BLOCKING(usb.configurePdo(1, 5000, 500)); // must be set to 5V according to USB standard
RF_CALL_BLOCKING(usb.configurePdo(2, 20000, 1500)); // 20V, 1.5A -> 30W
RF_CALL_BLOCKING(usb.configurePdo(3, 20000, 4000)); // 20V, 4A -> 80W

RF_CALL_BLOCKING(usb.setValidPdo(3)); // we prefer PDO 3 (more current)

modm::delay(200ms);

// check results
modm::stusb4500::RdoRegStatusData status = RF_CALL_BLOCKING(usb.getRdoRegStatus());

MODM_LOG_INFO << "Maximum current: " << status.MaxCurrent << " mA" << modm::endl;

Board::LedDown::set();

while (true) {
Board::LedRight::toggle();
modm::delay(1000ms);
}
}
13 changes: 13 additions & 0 deletions examples/stm32f072_discovery/stusb4500/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<library>
<extends>modm:disco-f072rb</extends>
<options>
<option name="modm:build:build.path">../../../build/stm32f072_discovery/stusb4500</option>
</options>
<modules>
<module>modm:build:scons</module>
<module>modm:debug</module>
<module>modm:driver:stusb4500</module>
<module>modm:platform:i2c:1</module>
<module>modm:platform:uart:1</module>
</modules>
</library>
157 changes: 157 additions & 0 deletions src/modm/driver/usb/stusb4500.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* Copyright (c) 2021, Henrik Hose
* Copyright (c) 2021, Raphael Lehmann
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#ifndef MODM_STUSB4500_HPP
#define MODM_STUSB4500_HPP

#include <modm/architecture/interface/i2c_device.hpp>

namespace modm
{

/// @ingroup modm_driver_stusb4500
struct stusb4500
{
public:
enum class
Register : uint8_t
{
RdoRegStatus = 0x91,
// PortStatus1 = 0x0E,
// CcStatus = 0x11,
// TxHeaderLow = 0x51,
// PdCommandCtrl = 0x1A,
DpmPdoNumb = 0x70,
DpmSnkPdo1 = 0x85,
DpmSnkPdo2 = 0x89,
DpmSnkPdo3 = 0x8D,
};

struct RdoRegStatusData {
uint32_t MaxCurrent; /// [mA]
uint32_t OperatingCurrent; /// [mA]
uint8_t ObjectPos;
};
};

/**
* @ingroup modm_driver_stusb4500
* @author Henrik Hose, Raphael Lehmann
*/
template<class I2cMaster>
class Stusb4500 : public stusb4500, public modm::I2cDevice< I2cMaster, 4 >
{
public:
Stusb4500(uint8_t address=0x28):
modm::I2cDevice<I2cMaster,4>(address)
{};

/**
* Configures a PDO.
*
* Note: According to the USB standard the first PDO (1) always has to set
* voltage to 5000 [mV].
*
* @param pdoNumber Which PDO to configure, range 1 to 3.
* @param voltage in mV
* @param current in mA
*/
modm::ResumableResult<bool>
configurePdo(uint8_t pdoNumber, uint32_t voltage, uint32_t current)
{
RF_BEGIN();

if((pdoNumber < 1) || (pdoNumber > 3))
RF_RETURN(false);

RF_CALL(readRegister(DpmSnkPdos[pdoNumber - 1], buffer2.data(), 4));

current /= 10;
voltage /= 50;

buffer2[0] = current;
buffer2[1] = ((current >> 8) & 0b11) | ((voltage & 0b11'1111 ) << 2);
buffer2[2] = (buffer2[2] & 0b1111'0000) | ((voltage >> 6) & 0b1111);

RF_END_RETURN_CALL(updateRegister(DpmSnkPdos[pdoNumber - 1], buffer2.data(), 4));
}

/**
* Sets the valid PDO.
*
* @param pdoNumber Which PDO to set valid, range 1 to 3.
*/
modm::ResumableResult<bool>
setValidPdo(uint8_t pdoNumber)
{
RF_BEGIN();

if((pdoNumber < 1) || (pdoNumber > 3))
RF_RETURN(false);

RF_END_RETURN_CALL(updateRegister(Register::DpmPdoNumb, &pdoNumber, 1));
}

/**
* Retrieve RDO status from STUSB4500 chip.
*
* @return RdoRegStatusData object. Typically only
* RdoRegStatusData::MaxCurrent [mA] is of interest.
*/
modm::ResumableResult<RdoRegStatusData>
getRdoRegStatus()
{
RF_BEGIN();

RF_CALL(readRegister(Register::RdoRegStatus, buffer2.data(), 4));

// MaxCurrent: Bits 9..0; OperatingCurrent: Bits 19..10; ObjectPos: Bits 30..28
rdoRegStatusData.MaxCurrent = buffer2[0] | ((buffer2[1] & 0b11) << 8);
rdoRegStatusData.MaxCurrent *= 10;
rdoRegStatusData.OperatingCurrent = ((buffer2[1] & 0b1111'1100) >> 2) | ((buffer2[2] & 0b1111) << 6);
rdoRegStatusData.OperatingCurrent *= 10;
rdoRegStatusData.ObjectPos = (buffer2[3] & 0b0110'0000) >> 5;

RF_END_RETURN(rdoRegStatusData);
}

public:
modm::ResumableResult<bool>
readRegister(Register reg, uint8_t* output, size_t length)
{
RF_BEGIN();
buffer[0] = uint8_t(reg);
this->transaction.configureWriteRead(buffer.data(), 1, output, length);
RF_END_RETURN_CALL( this->runTransaction() );
}
rleh marked this conversation as resolved.
Show resolved Hide resolved

modm::ResumableResult<bool>
updateRegister(Register reg, const uint8_t* data, size_t length)
{
RF_BEGIN();
if(length > (std::tuple_size<decltype(buffer)>::value - 1))
salkinium marked this conversation as resolved.
Show resolved Hide resolved
RF_RETURN(false);
buffer[0] = uint8_t(reg);
std::memcpy(&buffer[1], data, length);
this->transaction.configureWriteRead(buffer.data(), length + 1, nullptr, 0);
RF_END_RETURN_CALL( this->runTransaction() );
}

private:
static constexpr std::array DpmSnkPdos {Register::DpmSnkPdo1, Register::DpmSnkPdo2, Register::DpmSnkPdo3};
std::array<uint8_t, 5> buffer;
std::array<uint8_t, 4> buffer2;
rleh marked this conversation as resolved.
Show resolved Hide resolved
RdoRegStatusData rdoRegStatusData;
};

} // namespace modm

#endif // MODM_STUSB4500_HPP
35 changes: 35 additions & 0 deletions src/modm/driver/usb/stusb4500.lb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2021, Henrik Hose
# Copyright (c) 2021, Raphael Lehmann
#
# This file is part of the modm project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# -----------------------------------------------------------------------------

def init(module):
module.name = ":driver:stusb4500"
module.description = """\
# STUSB4500 USB PD controller

## Standalone USB PD controller for power sinking devices

The STUSB4500 is a USB power delivery controller for USB power sink devices.
See https://www.st.com/en/interfaces-and-transceivers/stusb4500.html

The STUSB4500 is connected via I2C to the microcontroller.

This driver only implements basic functionality for now.
"""

def prepare(module, options):
module.depends(":architecture:i2c.device")
return True

def build(env):
env.outbasepath = "modm/src/modm/driver/usb"
env.copy("stusb4500.hpp")