Skip to content

Commit d9ff5d9

Browse files
committed
Initial commit
0 parents  commit d9ff5d9

35 files changed

+5018
-0
lines changed

.gitignore

Whitespace-only changes.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 Manuel Bleichenbacher
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# USB Power Delivery for Arduino
2+
3+
Implement a USB PD protocol analyzer, a USB PD trigger board or a more
4+
sophisticated power sink using with a few additional components and simple
5+
Arduino code. Supports several STM32 microcontrollers.
6+
7+
Dependning on the microcontroller, a comparator and a few resistors are needed, or only resistors or no additional component at all. See below for more details. For 5 USD in parts, you can build a USB PD protocol analyzer.
8+
9+
10+
11+
## Supported Boards
12+
13+
| Board | Required additional components |
14+
| - | - |
15+
| Blue Pill (STM32F103C8) | Dual comparator, several resistors |
16+
| Black Pill (STM32F401CC) | Dual comparator, several resistors |
17+
| Nucleo-L432KC | Several resistors (for power sink) or none (for protocol analyzer) |
18+
| Nucleo-G431KB | None |
19+
20+
All boards require an additional USB C connector as the standard connector is not ready for USB Power Delivery (no USB C connector, CC1/CC2 signals not available, voltage regular cannot handle more than 5V).
21+
22+
See the Wiki for how to wire the board and the additional components.
23+
24+
25+
## Library Installation (Arduino IDE)
26+
27+
1. In the Arduino IDE, navigate to *Sketch > Include Library > Manage Libraries...*
28+
29+
2. The Library Manager will open and you will find a list of libraries that are already installed or ready for installation.
30+
31+
3. Search for *Power Delivery* using the search bar.
32+
33+
4. Click on the *INSTALL* button to install it.
34+
35+
36+
37+
## Examples
38+
39+
### Protocol Analyzer
40+
41+
The protocol analyzer can be connected between two USB PD devices to monitor the USB PD communication.
42+
43+
```c++
44+
#include "PowerDelivery.h"
45+
46+
void setup() {
47+
Serial.begin(115200);
48+
PowerController.startMonitor();
49+
}
50+
51+
void loop() {
52+
ProtocolAnalyzer::poll();
53+
}
54+
```
55+
56+
See the Wiki for details regarding the required components and wiring.
57+
58+
59+
60+
### Trigger Board
61+
62+
The trigger boards communicates with a USB power supply and requests a different voltage than
63+
the initial 5V.
64+
65+
```c++
66+
#include "PowerDelivery.h"
67+
68+
void setup() {
69+
PowerSink.start();
70+
// request 12V @ 1A once power supply is connected
71+
PowerSink.requestPower(12000, 1000);
72+
}
73+
74+
void loop() {
75+
// nothing to do
76+
}
77+
```
78+
79+
See the Wiki for details regarding the required components and wiring.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include "PowerDelivery.h"
2+
3+
void setup() {
4+
Serial.begin(115200);
5+
PowerController.startMonitor();
6+
}
7+
8+
void loop() {
9+
ProtocolAnalyzer::poll();
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include "PowerDelivery.h"
2+
3+
void setup() {
4+
PowerSink.start();
5+
// request 12V @ 1A once power supply is connected
6+
PowerSink.requestPower(12000, 1000);
7+
}
8+
9+
void loop() {
10+
}

library.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json",
3+
"name": "USB-Power-Delivery",
4+
"version": "1.0.0",
5+
"description": "USB Power Delivery for Arduino projects. Create a USB PD protocol analyzer, trigger board or power sink for no or only few additional components. Supports several STM32 boards.",
6+
"keywords": "usb,usb-pd,usb-power-delivery,protocol-analyzer,trigger-board",
7+
"homepage": "https://github.com/manuelbl/usb-pd-arduino",
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/manuelbl/usb-pd-arduino.git"
11+
},
12+
"authors": {
13+
"name": "Manuel Bl",
14+
"email": "manuel.bleichenbacher@gmail.com",
15+
"url": "https://github.com/manuelbl/usb-pd-arduino/discussions",
16+
"maintainer": true
17+
},
18+
"license": "MIT",
19+
"frameworks": ["arduino"],
20+
"platforms": ["ststm32"],
21+
"headers": ["PowerDelivery.h", "PDController.h", "PDSink.h", "ProtocolAnalyzer.h"],
22+
"examples": [
23+
{
24+
"name": "Protocol Analyzer",
25+
"base": "pio-examples/ProtocolAnalyzer",
26+
"files": [ "platformio.ini", "main.cpp" ]
27+
},
28+
{
29+
"name": "Trigger Board",
30+
"base": "pio-examples/TriggerBoard",
31+
"files": [ "platformio.ini", "main.cpp" ]
32+
}
33+
]
34+
}
35+

library.properties

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name=USB Power Delivery
2+
version=1.0.0
3+
author=Manuel Bl. <manuel.bleichenbacher@gmail.com>
4+
maintainer=Manuel Bl. <manuel.bleichenbacher@gmail.com>
5+
sentence=USB Power Delivery for Arduino projects.
6+
paragraph=Build a USB PD protocol analyzer, a trigger board or a more sophisticated power sink with no or only a few additional components. Supports several STM32 boards.
7+
category=Device Control
8+
url=https://github.com/manuelbl/usb-pd-arduino
9+
dot_a_linkage=true
10+
includes=PowerDelivery.h
11+
architectures=stm32

src/CMSISHelper.h

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//
2+
// USB Power Delivery for Arduino
3+
// Copyright (c) 2023 Manuel Bleichenbacher
4+
//
5+
// Licensed under MIT License
6+
// https://opensource.org/licenses/MIT
7+
//
8+
9+
#pragma once
10+
11+
#if defined(STM32F103xB)
12+
#include "stm32f103xb.h"
13+
#elif defined(STM32F401xC)
14+
#include "stm32f401xc.h"
15+
#elif defined(STM32L4xx)
16+
#include "stm32l4xx.h"
17+
#elif defined(STM32G431xx)
18+
#include "stm32g431xx.h"
19+
#else
20+
#pragma GCC error "This board is not supported by usb-pd-arduino. Supported boards: STM32F103xB (aka Bluepill), STM32F401xC (aka Blackpill), STM32L4 family, STM32G431"
21+
#endif
22+
23+
// --- Register manipulation ---
24+
25+
/**
26+
* @brief Sets bits of a register
27+
*
28+
* @param reg register
29+
* @param value values to set (must be masked to the relevant bits)
30+
* @param mask mask of bits to change
31+
*/
32+
static inline void setRegBits(volatile uint32_t& reg, uint32_t value, uint32_t mask) {
33+
reg = (reg & (~mask)) | (value & mask);
34+
}
35+
36+
/**
37+
* @brief Clears bits of a register
38+
*
39+
* @param reg register
40+
* @param mask mask of bits to clear
41+
*/
42+
static inline void clearRegBits(volatile uint32_t& reg, uint32_t mask) {
43+
reg = reg & ~mask;
44+
}
45+
46+
47+
#if defined(STM32L4) || defined(STM32F4xx)
48+
49+
// --- GPIO ---
50+
51+
// Helpers to build bits and masks for GPIO registers
52+
static constexpr uint32_t GPIO_MODER_INPUT(uint8_t gpio) { return 0b00 << (gpio * 2); }
53+
static constexpr uint32_t GPIO_MODER_OUTPUT(uint8_t gpio) { return 0b01 << (gpio * 2); }
54+
static constexpr uint32_t GPIO_MODER_ALTERNATE(uint8_t gpio) { return 0b10 << (gpio * 2); }
55+
static constexpr uint32_t GPIO_MODER_ANALOG(uint8_t gpio) { return 0b11 << (gpio * 2); }
56+
static constexpr uint32_t GPIO_MODER_Msk(uint8_t gpio) { return 0b11 << (gpio * 2); }
57+
static constexpr uint32_t GPIO_OTYPER_PUSH_PULL(uint8_t gpio) { return 0 << gpio; }
58+
static constexpr uint32_t GPIO_OTYPER_OPEN_DRAIN(uint8_t gpio) { return 1 << gpio; }
59+
static constexpr uint32_t GPIO_OTYPER_Msk(uint8_t gpio) { return 1 << gpio; }
60+
static constexpr uint32_t GPIO_OSPEEDR_LOW(uint8_t gpio) { return 0b00 << (gpio * 2); }
61+
static constexpr uint32_t GPIO_OSPEEDR_MEDIUM(uint8_t gpio) { return 0b01 << (gpio * 2); }
62+
static constexpr uint32_t GPIO_OSPEEDR_HIGH(uint8_t gpio) { return 0b10 << (gpio * 2); }
63+
static constexpr uint32_t GPIO_OSPEEDR_VERY_HIGH(uint8_t gpio) { return 0b11 << (gpio * 2); }
64+
static constexpr uint32_t GPIO_OSPEEDR_Msk(uint8_t gpio) { return 0b11 << (gpio * 2); }
65+
static constexpr uint32_t GPIO_PUPDR_NONE(uint8_t gpio) { return 0b00 << (gpio * 2); }
66+
static constexpr uint32_t GPIO_PUPDR_PULL_UP(uint8_t gpio) { return 0b01 << (gpio * 2); }
67+
static constexpr uint32_t GPIO_PUPDR_PULL_DOWN(uint8_t gpio) { return 0b10 << (gpio * 2); }
68+
static constexpr uint32_t GPIO_PUPDR_Msk(uint8_t gpio) { return 0b11 << (gpio * 2); }
69+
static constexpr uint32_t GPIO_AFRL(uint8_t gpio, uint8_t af) { return af << (gpio * 4); }
70+
static constexpr uint32_t GPIO_AFRL_Msk(uint8_t gpio) { return 0b1111 << (gpio * 4); }
71+
static constexpr uint32_t GPIO_AFRH(uint8_t gpio, uint8_t af) { return af << ((gpio - 8) * 4); }
72+
static constexpr uint32_t GPIO_AFRH_Msk(uint8_t gpio) { return 0b1111 << ((gpio - 8) * 4); }
73+
static inline void gpioSetOutputHigh(GPIO_TypeDef* port, uint8_t gpio) { port->BSRR = 1 << gpio; }
74+
static inline void gpioSetOutputLow(GPIO_TypeDef* port, uint8_t gpio) { port->BSRR = 1 << (16 + gpio); }
75+
76+
#endif
77+
78+
79+
// --- EXTI ---
80+
81+
#if defined(STM32L4)
82+
83+
// Additional EXTI constants
84+
static constexpr uint8_t EXTI_COMP1_Pos = 21;
85+
static constexpr uint32_t EXTI_COMP1 = 1 << EXTI_COMP1_Pos;
86+
static constexpr uint32_t EXTI_COMP1_Msk = 1 << EXTI_COMP1_Pos;
87+
static constexpr uint8_t EXTI_COMP2_Pos = 22;
88+
static constexpr uint32_t EXTI_COMP2 = 1 << EXTI_COMP2_Pos;
89+
static constexpr uint32_t EXTI_COMP2_Msk = 1 << EXTI_COMP2_Pos;
90+
91+
#endif
92+
93+
#if defined(STM32F103xB)
94+
95+
// --- GPIO ---
96+
97+
// Helpers to build bits and masks for GPIO registers
98+
static constexpr uint32_t GPIO_CRL_MODE_INPUT(uint8_t gpio) { return 0b00 << (gpio * 4); }
99+
static constexpr uint32_t GPIO_CRL_MODE_OUTPUT_10MHZ(uint8_t gpio) { return 0b01 << (gpio * 4); }
100+
static constexpr uint32_t GPIO_CRL_MODE_OUTPUT_2MHZ(uint8_t gpio) { return 0b10 << (gpio * 4); }
101+
static constexpr uint32_t GPIO_CRL_MODE_OUTPUT_50MHZ(uint8_t gpio) { return 0b11 << (gpio * 4); }
102+
static constexpr uint32_t GPIO_CRL_MODE_MASK(uint8_t gpio) { return 0b11 << (gpio * 4); }
103+
static constexpr uint32_t GPIO_CRL_CNF_ANALOG(uint8_t gpio) { return 0b00 << ((gpio * 4) + 2); }
104+
static constexpr uint32_t GPIO_CRL_CNF_FLOATING(uint8_t gpio) { return 0b01 << ((gpio * 4) + 2); }
105+
static constexpr uint32_t GPIO_CRL_CNF_INPUT_PUPD(uint8_t gpio) { return 0b10 << ((gpio * 4) + 2); }
106+
static constexpr uint32_t GPIO_CRL_CNF_OUTPUT(uint8_t gpio) { return 0b00 << ((gpio * 4) + 2); }
107+
static constexpr uint32_t GPIO_CRL_CNF_OUTPUT_OPENDRAIN(uint8_t gpio) { return 0b01 << ((gpio * 4) + 2); }
108+
static constexpr uint32_t GPIO_CRL_CNF_ALTERNATE(uint8_t gpio) { return 0b10 << ((gpio * 4) + 2); }
109+
static constexpr uint32_t GPIO_CRL_CNF_ALTERNATE_OPENDRAIN(uint8_t gpio) { return 0b11 << ((gpio * 4) + 2); }
110+
static constexpr uint32_t GPIO_CRL_CNF_MASK(uint8_t gpio) { return 0b11 << ((gpio * 4) + 2); }
111+
static constexpr uint32_t GPIO_CRH_MODE_INPUT(uint8_t gpio) { return 0b00 << ((gpio - 8) * 4); }
112+
static constexpr uint32_t GPIO_CRH_MODE_OUTPUT_10MHZ(uint8_t gpio) { return 0b01 << ((gpio - 8) * 4); }
113+
static constexpr uint32_t GPIO_CRH_MODE_OUTPUT_2MHZ(uint8_t gpio) { return 0b10 << ((gpio - 8) * 4); }
114+
static constexpr uint32_t GPIO_CRH_MODE_OUTPUT_50MHZ(uint8_t gpio) { return 0b11 << ((gpio - 8) * 4); }
115+
static constexpr uint32_t GPIO_CRH_MODE_MASK(uint8_t gpio) { return 0b11 << ((gpio - 8) * 4); }
116+
static constexpr uint32_t GPIO_CRH_CNF_ANALOG(uint8_t gpio) { return 0b00 << (((gpio - 8) * 4) + 2); }
117+
static constexpr uint32_t GPIO_CRH_CNF_FLOATING(uint8_t gpio) { return 0b01 << (((gpio - 8) * 4) + 2); }
118+
static constexpr uint32_t GPIO_CRH_CNF_INPUT_PUPD(uint8_t gpio) { return 0b10 << (((gpio - 8) * 4) + 2); }
119+
static constexpr uint32_t GPIO_CRH_CNF_OUTPUT(uint8_t gpio) { return 0b00 << (((gpio - 8) * 4) + 2); }
120+
static constexpr uint32_t GPIO_CRH_CNF_OUTPUT_OPENDRAIN(uint8_t gpio) { return 0b01 << (((gpio - 8) * 4) + 2); }
121+
static constexpr uint32_t GPIO_CRH_CNF_ALTERNATE(uint8_t gpio) { return 0b10 << (((gpio - 8) * 4) + 2); }
122+
static constexpr uint32_t GPIO_CRH_CNF_ALTERNATE_OPENDRAIN(uint8_t gpio) { return 0b11 << (((gpio - 8) * 4) + 2); }
123+
static constexpr uint32_t GPIO_CRH_CNF_MASK(uint8_t gpio) { return 0b11 << (((gpio - 8) * 4) + 2); }
124+
static inline void gpioSetOutputHigh(GPIO_TypeDef* port, uint8_t gpio) { port->BSRR = 1 << gpio; }
125+
static inline void gpioSetOutputLow(GPIO_TypeDef* port, uint8_t gpio) { port->BSRR = 1 << (16 + gpio); }
126+
127+
#endif

src/CRC32.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// USB Power Delivery for Arduino
3+
// Copyright (c) 2023 Manuel Bleichenbacher
4+
//
5+
// Licensed under MIT License
6+
// https://opensource.org/licenses/MIT
7+
//
8+
9+
#include "CRC32.h"
10+
#include "CMSISHelper.h"
11+
12+
#if defined(STM32L4xx)
13+
14+
bool CRC32::isValid(const uint8_t* data, int len) {
15+
CRC->CR = (0b01 << CRC_CR_REV_IN_Pos) | CRC_CR_RESET;
16+
17+
for (int i = 0; i < len; i += 1)
18+
*((volatile uint8_t*)&CRC->DR) = data[i]; // bytewise operation
19+
20+
return CRC->DR == ExpectedResidual;
21+
}
22+
23+
uint32_t CRC32::compute(const uint8_t* data, int len) {
24+
CRC->CR = (0b01 << CRC_CR_REV_IN_Pos) | CRC_CR_REV_OUT | CRC_CR_RESET;
25+
26+
for (int i = 0; i < len; i += 1)
27+
*((volatile uint8_t*)&CRC->DR) = data[i]; // bytewise operation
28+
29+
return ~CRC->DR;
30+
}
31+
32+
#else
33+
34+
/// look-up table
35+
static const uint32_t CRC32Lookup[16] = {
36+
0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC,
37+
0x76DC4190, 0x6B6B51F4, 0x4DB26158, 0x5005713C,
38+
0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C,
39+
0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C
40+
};
41+
42+
/// Calculates CRC32
43+
static uint32_t CRC32Calc(const uint8_t* data, int len) {
44+
uint32_t crc = CRC32::InitialValue;
45+
46+
// nibble-wise calculation
47+
for (int i = 0; i < len; i += 1) {
48+
crc = CRC32Lookup[(crc ^ data[i] ) & 0x0f] ^ (crc >> 4);
49+
crc = CRC32Lookup[(crc ^ (data[i] >> 4)) & 0x0f] ^ (crc >> 4);
50+
}
51+
52+
return ~crc;
53+
}
54+
55+
56+
bool CRC32::isValid(const uint8_t* data, int len) {
57+
uint32_t crc = CRC32Calc(data, len);
58+
return __RBIT(~crc) == ExpectedResidual;
59+
}
60+
61+
uint32_t CRC32::compute(const uint8_t* data, int len) {
62+
return CRC32Calc(data, len);
63+
}
64+
65+
#endif

0 commit comments

Comments
 (0)