1
0
forked from me/IronOS

Merge pull request #917 from Ralim/messing_with_pd

Improvements and restructure to USB-PD
This commit is contained in:
Ben V. Brown
2021-04-28 21:36:03 +10:00
committed by GitHub
39 changed files with 577 additions and 944 deletions

View File

@@ -8,9 +8,6 @@
#ifndef USER_BSP_PD_H_
#define USER_BSP_PD_H_
#include "BSP.h"
/*
* An array of all of the desired voltages & minimum currents in preferred order
*/
extern const uint16_t USB_PD_Desired_Levels[];
extern const uint8_t USB_PD_Desired_Levels_Len;
bool getFUS302IRQLow(); // Return true if the IRQ line is still held low
#endif /* USER_BSP_PD_H_ */

View File

@@ -15,4 +15,5 @@ enum Orientation { ORIENTATION_LEFT_HAND = 0, ORIENTATION_RIGHT_HAND = 1, ORIENT
#define TICKS_SECOND configTICK_RATE_HZ
#define TICKS_MIN (60 * TICKS_SECOND)
#define TICKS_100MS (TICKS_SECOND / 10)
#define TICKS_10MS (TICKS_100MS / 10)
#endif /* BSP_DEFINES_H_ */

View File

@@ -1,22 +0,0 @@
/*
* BSP_PD.c
*
* Created on: 21 Jul 2020
* Author: Ralim
*/
#include "BSP_PD.h"
#include "Model_Config.h"
#ifdef POW_PD
/*
* An array of all of the desired voltages & minimum currents in preferred order
*/
const uint16_t USB_PD_Desired_Levels[] = {
// mV desired input, mA minimum required current
12000, 2400, // 12V @ 2.4A
9000, 2000, // 9V @ 2A
5000, 100, // 5V @ whatever
};
const uint8_t USB_PD_Desired_Levels_Len = 3;
#endif

View File

@@ -6,7 +6,9 @@
*/
#include "IRQ.h"
#include "Pins.h"
#include "int_n.h"
/*
* Catch the IRQ that says that the conversion is done on the temperature
* readings coming in Once these have come in we can unblock the PID so that it
@@ -32,3 +34,12 @@ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
(void)GPIO_Pin;
InterruptHandler::irqCallback();
}
bool getFUS302IRQLow() {
#ifdef POW_PD
// Return true if the IRQ line is still held low
return HAL_GPIO_ReadPin(INT_PD_GPIO_Port, INT_PD_Pin) == GPIO_PIN_RESET;
#else
return false;
#endif
}

View File

@@ -8,10 +8,12 @@
#include "int_n.h"
#include "policy_engine.h"
bool FUSB302_present = false;
bool FUSB302_probed = false;
void power_check() {
#ifdef POW_PD
if (FUSB302_present) {
PolicyEngine::PPSTimerCallback();
// Cant start QC until either PD works or fails
if (PolicyEngine::setupCompleteOrTimedOut() == false) {
return;
@@ -27,7 +29,12 @@ void power_check() {
}
uint8_t usb_pd_detect() {
#ifdef POW_PD
if (FUSB302_probed) {
return FUSB302_present;
} else {
FUSB302_present = fusb302_detect();
FUSB302_probed = true;
}
return FUSB302_present;
#endif
return false;

View File

@@ -1,246 +0,0 @@
/*
* PD Buddy Firmware Library - USB Power Delivery for everyone
* Copyright 2017-2018 Clayton G. Hobbs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Model_Config.h"
#ifdef POW_PD
#include "BSP.h"
#include "I2CBB.hpp"
#include "fusb302b.h"
#include "int_n.h"
#include <pd.h>
/*
* Read a single byte from the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address from which to read
*
* Returns the value read from addr.
*/
static uint8_t fusb_read_byte(uint8_t addr) {
uint8_t data[1];
if (!I2CBB::Mem_Read(FUSB302B_ADDR, addr, (uint8_t *)data, 1)) {
return 0;
}
return data[0];
}
/*
* Read multiple bytes from the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address from which to read
* size: The number of bytes to read
* buf: The buffer into which data will be read
*/
static bool fusb_read_buf(uint8_t addr, uint8_t size, uint8_t *buf) { return I2CBB::Mem_Read(FUSB302B_ADDR, addr, buf, size); }
/*
* Write a single byte to the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address to which we will write
* byte: The value to write
*/
static bool fusb_write_byte(uint8_t addr, uint8_t byte) { return I2CBB::Mem_Write(FUSB302B_ADDR, addr, (uint8_t *)&byte, 1); }
/*
* Write multiple bytes to the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address to which we will write
* size: The number of bytes to write
* buf: The buffer to write
*/
static bool fusb_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf) { return I2CBB::Mem_Write(FUSB302B_ADDR, addr, buf, size); }
void fusb_send_message(const union pd_msg *msg) {
if (!I2CBB::lock2()) {
return;
}
/* Token sequences for the FUSB302B */
static uint8_t sop_seq[5] = {FUSB_FIFO_TX_SOP1, FUSB_FIFO_TX_SOP1, FUSB_FIFO_TX_SOP1, FUSB_FIFO_TX_SOP2, FUSB_FIFO_TX_PACKSYM};
static const uint8_t eop_seq[4] = {FUSB_FIFO_TX_JAM_CRC, FUSB_FIFO_TX_EOP, FUSB_FIFO_TX_TXOFF, FUSB_FIFO_TX_TXON};
/* Take the I2C2 mutex now so there can't be a race condition on sop_seq */
/* Get the length of the message: a two-octet header plus NUMOBJ four-octet
* data objects */
uint8_t msg_len = 2 + 4 * PD_NUMOBJ_GET(msg);
/* Set the number of bytes to be transmitted in the packet */
sop_seq[4] = FUSB_FIFO_TX_PACKSYM | msg_len;
/* Write all three parts of the message to the TX FIFO */
fusb_write_buf(FUSB_FIFOS, 5, sop_seq);
fusb_write_buf(FUSB_FIFOS, msg_len, msg->bytes);
fusb_write_buf(FUSB_FIFOS, 4, eop_seq);
I2CBB::unlock2();
}
uint8_t fusb_read_message(union pd_msg *msg) {
if (!I2CBB::lock2()) {
asm("bkpt");
}
static uint8_t garbage[4];
uint8_t numobj;
// Read the header. If its not a SOP we dont actually want it at all
// But on some revisions of the fusb if you dont both pick them up and read them out of the fifo, it gets stuck
fusb_read_byte(FUSB_FIFOS);
/* Read the message header into msg */
fusb_read_buf(FUSB_FIFOS, 2, msg->bytes);
/* Get the number of data objects */
numobj = PD_NUMOBJ_GET(msg);
/* If there is at least one data object, read the data objects */
if (numobj > 0) {
fusb_read_buf(FUSB_FIFOS, numobj * 4, msg->bytes + 2);
}
/* Throw the CRC32 in the garbage, since the PHY already checked it. */
fusb_read_buf(FUSB_FIFOS, 4, garbage);
I2CBB::unlock2();
return 0;
}
void fusb_send_hardrst() {
if (!I2CBB::lock2()) {
return;
}
/* Send a hard reset */
fusb_write_byte(FUSB_CONTROL3, 0x07 | FUSB_CONTROL3_SEND_HARD_RESET);
I2CBB::unlock2();
}
bool fusb_setup() {
if (!I2CBB::lock2()) {
return false;
}
/* Fully reset the FUSB302B */
// fusb_write_byte( FUSB_RESET, FUSB_RESET_SW_RES);
// osDelay(2);
if (!fusb_read_id()) {
return false;
}
/* Turn on all power */
fusb_write_byte(FUSB_POWER, 0x0F);
/* Set interrupt masks */
// Setting to 0 so interrupts are allowed
fusb_write_byte(FUSB_MASK1, 0x00);
fusb_write_byte(FUSB_MASKA, 0x00);
fusb_write_byte(FUSB_MASKB, 0x00);
fusb_write_byte(FUSB_CONTROL0, 0b11 << 2);
/* Enable automatic retransmission */
fusb_write_byte(FUSB_CONTROL3, 0x07);
// set defaults
fusb_write_byte(FUSB_CONTROL2, 0x00);
/* Flush the RX buffer */
fusb_write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH);
/* Measure CC1 */
fusb_write_byte(FUSB_SWITCHES0, 0x07);
osDelay(10);
uint8_t cc1 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
/* Measure CC2 */
fusb_write_byte(FUSB_SWITCHES0, 0x0B);
osDelay(10);
uint8_t cc2 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
/* Select the correct CC line for BMC signaling; also enable AUTO_CRC */
if (cc1 > cc2) {
fusb_write_byte(FUSB_SWITCHES1, 0x25);
fusb_write_byte(FUSB_SWITCHES0, 0x07);
} else {
fusb_write_byte(FUSB_SWITCHES1, 0x26);
fusb_write_byte(FUSB_SWITCHES0, 0x0B);
}
I2CBB::unlock2();
fusb_reset();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_SetPriority(EXTI9_5_IRQn, 10, 0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
return true;
}
void fusb_get_status(union fusb_status *status) {
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
if (!I2CBB::lock2()) {
return;
}
}
/* Read the interrupt and status flags into status */
fusb_read_buf(FUSB_STATUS0A, 7, status->bytes);
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
I2CBB::unlock2();
}
}
enum fusb_typec_current fusb_get_typec_current() {
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
if (!I2CBB::lock2()) {
return fusb_tcc_none;
}
}
/* Read the BC_LVL into a variable */
enum fusb_typec_current bc_lvl = (enum fusb_typec_current)(fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL);
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
I2CBB::unlock2();
}
return bc_lvl;
}
void fusb_reset() {
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
if (!I2CBB::lock2()) {
return;
}
}
/* Flush the TX buffer */
fusb_write_byte(FUSB_CONTROL0, 0x44);
/* Flush the RX buffer */
fusb_write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH);
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
I2CBB::unlock2();
}
}
bool fusb_read_id() {
// Return true if read of the revision ID is sane
uint8_t version = 0;
fusb_read_buf(FUSB_DEVICE_ID, 1, &version);
if (version == 0 || version == 0xFF)
return false;
return true;
}
uint8_t fusb302_detect() {
// Probe the I2C bus for its address
return I2CBB::probe(FUSB302B_ADDR);
}
#endif

View File

@@ -0,0 +1,68 @@
#include "Model_Config.h"
#ifdef POW_PD
#include "BSP.h"
#include "I2CBB.hpp"
#include "Setup.h"
#include "fusb302b.h"
#include "fusb_user.h"
/*
* Read a single byte from the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address from which to read
*
* Returns the value read from addr.
*/
uint8_t fusb_read_byte(uint8_t addr) {
uint8_t data[1];
if (!I2CBB::Mem_Read(FUSB302B_ADDR, addr, (uint8_t *)data, 1)) {
return 0;
}
return data[0];
}
/*
* Read multiple bytes from the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address from which to read
* size: The number of bytes to read
* buf: The buffer into which data will be read
*/
bool fusb_read_buf(uint8_t addr, uint8_t size, uint8_t *buf) { return I2CBB::Mem_Read(FUSB302B_ADDR, addr, buf, size); }
/*
* Write a single byte to the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address to which we will write
* byte: The value to write
*/
bool fusb_write_byte(uint8_t addr, uint8_t byte) { return I2CBB::Mem_Write(FUSB302B_ADDR, addr, (uint8_t *)&byte, 1); }
/*
* Write multiple bytes to the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address to which we will write
* size: The number of bytes to write
* buf: The buffer to write
*/
bool fusb_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf) { return I2CBB::Mem_Write(FUSB302B_ADDR, addr, buf, size); }
uint8_t fusb302_detect() {
// Probe the I2C bus for its address
return I2CBB::probe(FUSB302B_ADDR);
}
void setupFUSBIRQ() {
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_SetPriority(EXTI9_5_IRQn, 10, 0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}
#endif

View File

@@ -11,11 +11,4 @@
#include "task.h"
// Initialisation to be performed with scheduler active
void postRToSInit() {
#ifdef POW_PD
if (usb_pd_detect() == true) {
// Spawn all of the USB-C processors
fusb302_start_processing();
}
#endif
}
void postRToSInit() {}

View File

@@ -1,26 +0,0 @@
/*
* BSP_PD.c
*
* Created on: 21 Jul 2020
* Author: Ralim
*/
#include "BSP_PD.h"
#include "Model_Config.h"
#ifdef POW_PD
/*
* An array of all of the desired voltages & minimum currents in preferred order
*/
const uint16_t USB_PD_Desired_Levels[] = {
// mV desired input, mA minimum required current
// Tip is ~ 7.5 ohms
20000, 2666, // 20V, 2.6A
18000, 2400, // 18V, 2.4A
15000, 2000, // 15V 2A
12000, 1600, // 12V @ 1.6A
9000, 1200, // 9V @ 1.2A
5000, 100, // 5V @ whatever
};
const uint8_t USB_PD_Desired_Levels_Len = 6;
#endif

View File

@@ -118,6 +118,10 @@ void EXTI5_9_IRQHandler(void) {
#endif
}
bool getFUS302IRQLow() {
// Return true if the IRQ line is still held low
return (RESET == gpio_input_bit_get(FUSB302_IRQ_GPIO_Port, FUSB302_IRQ_Pin));
}
// These are unused for now
void I2C0_EV_IRQHandler(void) {}

View File

@@ -8,10 +8,12 @@
#include "int_n.h"
#include "policy_engine.h"
bool FUSB302_present = false;
bool FUSB302_probed = false;
void power_check() {
#ifdef POW_PD
if (FUSB302_present) {
PolicyEngine::PPSTimerCallback();
// Cant start QC until either PD works or fails
if (PolicyEngine::setupCompleteOrTimedOut() == false) {
return;
@@ -27,8 +29,12 @@ void power_check() {
}
uint8_t usb_pd_detect() {
#ifdef POW_PD
if (FUSB302_probed) {
return FUSB302_present;
} else {
FUSB302_present = fusb302_detect();
FUSB302_probed = true;
}
return FUSB302_present;
#endif
return false;

View File

@@ -0,0 +1,58 @@
#include "Model_Config.h"
#ifdef POW_PD
#include "BSP.h"
#include "I2C_Wrapper.hpp"
#include "Setup.h"
#include "fusb302b.h"
#include "fusb_user.h"
/*
* Read a single byte from the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address from which to read
*
* Returns the value read from addr.
*/
uint8_t fusb_read_byte(uint8_t addr) {
uint8_t data[1];
if (!FRToSI2C::Mem_Read(FUSB302B_ADDR, addr, (uint8_t *)data, 1)) {
return 0;
}
return data[0];
}
/*
* Read multiple bytes from the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address from which to read
* size: The number of bytes to read
* buf: The buffer into which data will be read
*/
bool fusb_read_buf(uint8_t addr, uint8_t size, uint8_t *buf) { return FRToSI2C::Mem_Read(FUSB302B_ADDR, addr, buf, size); }
/*
* Write a single byte to the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address to which we will write
* byte: The value to write
*/
bool fusb_write_byte(uint8_t addr, uint8_t byte) { return FRToSI2C::Mem_Write(FUSB302B_ADDR, addr, (uint8_t *)&byte, 1); }
/*
* Write multiple bytes to the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address to which we will write
* size: The number of bytes to write
* buf: The buffer to write
*/
bool fusb_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf) { return FRToSI2C::Mem_Write(FUSB302B_ADDR, addr, (uint8_t *)buf, size); }
uint8_t fusb302_detect() {
// Probe the I2C bus for its address
return FRToSI2C::probe(FUSB302B_ADDR);
}
#endif

View File

@@ -19,10 +19,6 @@ void postRToSInit() {
hall_effect_present = Si7210::init();
}
#endif
#ifdef POW_PD
// Spawn all of the USB-C processors
fusb302_start_processing();
#endif
}
int16_t getRawHallEffect() {
if (hall_effect_present) {

View File

@@ -14,65 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Model_Config.h"
#ifdef POW_PD
#include "fusb302b.h"
#include "BSP.h"
#include "I2C_Wrapper.hpp"
#include "Setup.h"
#include "fusb302b.h"
#include "fusb_user.h"
#include "int_n.h"
#include <pd.h>
/*
* Read a single byte from the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address from which to read
*
* Returns the value read from addr.
*/
static uint8_t fusb_read_byte(uint8_t addr) {
uint8_t data[1];
if (!FRToSI2C::Mem_Read(FUSB302B_ADDR, addr, (uint8_t *)data, 1)) {
return 0;
}
return data[0];
}
/*
* Read multiple bytes from the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address from which to read
* size: The number of bytes to read
* buf: The buffer into which data will be read
*/
static bool fusb_read_buf(uint8_t addr, uint8_t size, uint8_t *buf) { return FRToSI2C::Mem_Read(FUSB302B_ADDR, addr, buf, size); }
/*
* Write a single byte to the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address to which we will write
* byte: The value to write
*/
static bool fusb_write_byte(uint8_t addr, uint8_t byte) {
FRToSI2C::Mem_Write(FUSB302B_ADDR, addr, (uint8_t *)&byte, 1);
return true;
}
/*
* Write multiple bytes to the FUSB302B
*
* cfg: The FUSB302B to communicate with
* addr: The memory address to which we will write
* size: The number of bytes to write
* buf: The buffer to write
*/
static bool fusb_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf) {
FRToSI2C::Mem_Write(FUSB302B_ADDR, addr, (uint8_t *)buf, size);
return true; // TODO
}
void fusb_send_message(const union pd_msg *msg) {
/* Token sequences for the FUSB302B */
@@ -122,15 +70,12 @@ void fusb_send_hardrst() {
}
bool fusb_setup() {
if (!FRToSI2C::probe(FUSB302B_ADDR)) {
return false;
}
/* Fully reset the FUSB302B */
fusb_write_byte(FUSB_RESET, FUSB_RESET_SW_RES);
osDelay(2);
vTaskDelay(TICKS_10MS);
uint8_t tries = 0;
while (!fusb_read_id()) {
osDelay(10);
vTaskDelay(TICKS_10MS);
tries++;
if (tries > 5) {
return false; // Welp :(
@@ -156,12 +101,12 @@ bool fusb_setup() {
/* Measure CC1 */
fusb_write_byte(FUSB_SWITCHES0, 0x07);
osDelay(10);
vTaskDelay(TICKS_10MS);
uint8_t cc1 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
/* Measure CC2 */
fusb_write_byte(FUSB_SWITCHES0, 0x0B);
osDelay(10);
vTaskDelay(TICKS_10MS);
uint8_t cc2 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
/* Select the correct CC line for BMC signaling; also enable AUTO_CRC */
@@ -210,9 +155,3 @@ bool fusb_read_id() {
return false;
return true;
}
uint8_t fusb302_detect() {
// Probe the I2C bus for its address
return FRToSI2C::probe(FUSB302B_ADDR);
}
#endif

View File

@@ -0,0 +1,29 @@
/*
* PD Buddy Firmware Library - USB Power Delivery for everyone
* Copyright 2017-2018 Clayton G. Hobbs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef PDB_FUSB_USER_H
#define PDB_FUSB_USER_H
#include <stdint.h>
uint8_t fusb_read_byte(uint8_t addr);
bool fusb_read_buf(uint8_t addr, uint8_t size, uint8_t *buf);
bool fusb_write_byte(uint8_t addr, uint8_t byte);
bool fusb_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf);
uint8_t fusb302_detect();
void setupFUSBIRQ();
#endif /* PDB_FUSB302B_H */

View File

@@ -11,7 +11,7 @@
#include "fusb302b.h"
#include "int_n.h"
#include "policy_engine.h"
#include "protocol_rx.h"
#include "protocol_tx.h"
#include <fusbpd.h>
#include <pd.h>
@@ -21,7 +21,6 @@ void fusb302_start_processing() {
if (fusb_setup()) {
PolicyEngine::init();
ProtocolTransmit::init();
ProtocolReceive::init();
InterruptHandler::init();
}
}

View File

@@ -17,34 +17,63 @@
#include "int_n.h"
#include "BSP.h"
#include "BSP_PD.h"
#include "fusb302b.h"
#include "fusbpd.h"
#include "policy_engine.h"
#include "protocol_rx.h"
#include "protocol_tx.h"
#include "task.h"
#include <pd.h>
#include <string.h>
osThreadId InterruptHandler::TaskHandle = NULL;
volatile osThreadId InterruptHandler::TaskHandle = NULL;
uint32_t InterruptHandler::TaskBuffer[InterruptHandler::TaskStackSize];
osStaticThreadDef_t InterruptHandler::TaskControlBlock;
union pd_msg InterruptHandler::tempMessage;
void InterruptHandler::init() {
TaskHandle = NULL;
osThreadStaticDef(intTask, Thread, PDB_PRIO_PRL_INT_N, 0, TaskStackSize, TaskBuffer, &TaskControlBlock);
TaskHandle = osThreadCreate(osThread(intTask), NULL);
}
void InterruptHandler::readPendingMessage() {
/* Get a buffer to read the message into. Guaranteed to not fail
* because we have a big enough pool and are careful. */
memset(&tempMessage, 0, sizeof(tempMessage));
/* Read the message */
fusb_read_message(&tempMessage);
/* If it's a Soft_Reset, go to the soft reset state */
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOFT_RESET && PD_NUMOBJ_GET(&tempMessage) == 0) {
/* TX transitions to its reset state */
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_RESET);
} else {
/* Tell ProtocolTX to discard the message being transmitted */
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_DISCARD);
/* Pass the message to the policy engine. */
PolicyEngine::handleMessage(&tempMessage);
}
}
void InterruptHandler::Thread(const void *arg) {
(void)arg;
union fusb_status status;
while (true) {
/* If the INT_N line is low */
if (xTaskNotifyWait(0x00, 0x0F, NULL, PolicyEngine::setupCompleteOrTimedOut() ? 1000 : 10) == pdPASS) {
// delay slightly so we catch the crc with better timing
osDelay(1);
for (;;) {
// If the irq is low continue, otherwise wait for irq or timeout
if (!getFUS302IRQLow()) {
xTaskNotifyWait(0x00, 0x0F, NULL, TICKS_SECOND * 30);
}
/* Read the FUSB302B status and interrupt registers */
fusb_get_status(&status);
/* If the I_GCRCSENT flag is set, tell the Protocol RX thread */
// This means a message was recieved with a good CRC
if (status.interruptb & FUSB_INTERRUPTB_I_GCRCSENT) {
readPendingMessage();
}
/* If the I_TXSENT or I_RETRYFAIL flag is set, tell the Protocol TX
* thread */
if (status.interrupta & FUSB_INTERRUPTA_I_TXSENT) {
@@ -54,25 +83,17 @@ void InterruptHandler::Thread(const void *arg) {
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_I_RETRYFAIL);
}
/* If the I_GCRCSENT flag is set, tell the Protocol RX thread */
// This means a message was recieved with a good CRC
if (status.interruptb & FUSB_INTERRUPTB_I_GCRCSENT) {
ProtocolReceive::notify(PDB_EVT_PRLRX_I_GCRCSENT);
}
/* If the I_OCP_TEMP and OVRTEMP flags are set, tell the Policy
* Engine thread */
if ((status.interrupta & FUSB_INTERRUPTA_I_OCP_TEMP) && (status.status1 & FUSB_STATUS1_OVRTEMP)) {
PolicyEngine::notify(PDB_EVT_PE_I_OVRTEMP);
PolicyEngine::notify(PolicyEngine::Notifications::PDB_EVT_PE_I_OVRTEMP);
}
}
}
void InterruptHandler::irqCallback() {
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
if (TaskHandle != NULL) {
BaseType_t taskWoke = pdFALSE;
xTaskNotifyFromISR(TaskHandle, 0x01, eNotifyAction::eSetBits, &taskWoke);
portYIELD_FROM_ISR(taskWoke);
}
}
}

View File

@@ -29,7 +29,7 @@ public:
private:
static void Thread(const void *arg);
static osThreadId TaskHandle;
static volatile osThreadId TaskHandle;
static const size_t TaskStackSize = 1536 / 3;
static uint32_t TaskBuffer[TaskStackSize];
static osStaticThreadDef_t TaskControlBlock;
@@ -44,6 +44,9 @@ private:
static enum hardrst_state hardrst_hard_reset_requested();
static enum hardrst_state hardrst_wait_pe();
static enum hardrst_state hardrst_complete();
// Mesage rx
static void readPendingMessage();
static union pd_msg tempMessage;
};
#endif /* PDB_INT_N_OLD_H */

View File

@@ -271,13 +271,13 @@
* Where a range is specified, the middle of the range (rounded down to the
* nearest millisecond) is used.
*/
#define PD_T_CHUNKING_NOT_SUPPORTED (450)
#define PD_T_HARD_RESET_COMPLETE (1000)
#define PD_T_PS_TRANSITION (5000)
#define PD_T_SENDER_RESPONSE (2700)
#define PD_T_SINK_REQUEST (1000)
#define PD_T_TYPEC_SINK_WAIT_CAP (1000)
#define PD_T_PD_DEBOUNCE (2000)
#define PD_T_CHUNKING_NOT_SUPPORTED (TICKS_SECOND / 2)
#define PD_T_HARD_RESET_COMPLETE (1 * TICKS_SECOND)
#define PD_T_PS_TRANSITION (5 * TICKS_SECOND)
#define PD_T_SENDER_RESPONSE (27 * TICKS_100MS)
#define PD_T_SINK_REQUEST (1 * TICKS_SECOND)
#define PD_T_TYPEC_SINK_WAIT_CAP (1 * TICKS_SECOND)
#define PD_T_PD_DEBOUNCE (2 * TICKS_SECOND)
/*
* Counter maximums

View File

@@ -19,13 +19,13 @@
#define PDB_CONF_H
/* Number of messages in the message pool */
#define PDB_MSG_POOL_SIZE 4
#define PDB_MSG_POOL_SIZE 8
#define EVENT_MASK(x) (1 << x)
#define eventmask_t uint32_t
/* PD Buddy thread priorities */
#define PDB_PRIO_PE (osPriorityNormal)
#define PDB_PRIO_PRL (osPriorityBelowNormal)
#define PDB_PRIO_PRL_INT_N (osPriorityLow)
#define PDB_PRIO_PE (osPriorityAboveNormal)
#define PDB_PRIO_PRL (osPriorityAboveNormal)
#define PDB_PRIO_PRL_INT_N (osPriorityAboveNormal)
#endif /* PDB_CONF_H */

View File

@@ -16,6 +16,7 @@
*/
#include "policy_engine.h"
#include "Defines.h"
#include "fusb302b.h"
#include "int_n.h"
#include "protocol_tx.h"
@@ -31,7 +32,6 @@ bool PolicyEngine::_explicit_contract;
int8_t PolicyEngine::_hard_reset_counter;
int8_t PolicyEngine::_old_tcc_match;
uint8_t PolicyEngine::_pps_index;
uint8_t PolicyEngine::_last_pps;
osThreadId PolicyEngine::TaskHandle = NULL;
uint32_t PolicyEngine::TaskBuffer[PolicyEngine::TaskStackSize];
osStaticThreadDef_t PolicyEngine::TaskControlBlock;
@@ -43,6 +43,8 @@ uint8_t PolicyEngine::ucQueueStorageArea[PDB_MSG_POOL_
QueueHandle_t PolicyEngine::messagesWaiting = NULL;
EventGroupHandle_t PolicyEngine::xEventGroupHandle = NULL;
StaticEventGroup_t PolicyEngine::xCreatedEventGroup;
bool PolicyEngine::PPSTimerEnabled = false;
TickType_t PolicyEngine::PPSTimeLastEvent = 0;
void PolicyEngine::init() {
messagesWaiting = xQueueCreateStatic(PDB_MSG_POOL_SIZE, sizeof(union pd_msg), ucQueueStorageArea, &xStaticQueue);
// Create static thread at PDB_PRIO_PE priority
@@ -51,9 +53,10 @@ void PolicyEngine::init() {
xEventGroupHandle = xEventGroupCreateStatic(&xCreatedEventGroup);
}
void PolicyEngine::notify(uint32_t notification) {
void PolicyEngine::notify(PolicyEngine::Notifications notification) {
EventBits_t val = (EventBits_t)notification;
if (xEventGroupHandle != NULL) {
xEventGroupSetBits(xEventGroupHandle, notification);
xEventGroupSetBits(xEventGroupHandle, val);
}
}
@@ -64,9 +67,7 @@ void PolicyEngine::pe_task(const void *arg) {
/* Initialize the old_tcc_match */
_old_tcc_match = -1;
/* Initialize the pps_index */
_pps_index = 8;
/* Initialize the last_pps */
_last_pps = 8;
_pps_index = 0xFF;
for (;;) {
// Loop based on state
@@ -133,7 +134,7 @@ void PolicyEngine::pe_task(const void *arg) {
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_startup() {
/* We don't have an explicit contract currently */
_explicit_contract = false;
PPSTimerEnabled = false;
// If desired could send an alert that PD is starting
/* No need to reset the protocol layer here. There are two ways into this
@@ -154,31 +155,27 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_discovery() {
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_wait_cap() {
/* Fetch a message from the protocol layer */
eventmask_t evt = 0;
if (readMessage()) {
evt = PDB_EVT_PE_MSG_RX_PEND;
} else {
evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_I_OVRTEMP | PDB_EVT_PE_RESET,
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_MSG_RX | (uint32_t)Notifications::PDB_EVT_PE_I_OVRTEMP | (uint32_t)Notifications::PDB_EVT_PE_RESET,
// Wait for cap timeout
PD_T_TYPEC_SINK_WAIT_CAP);
}
/* If we timed out waiting for Source_Capabilities, send a hard reset */
if (evt == 0) {
return PESinkHardReset;
}
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
return PESinkWaitCap;
}
/* If we're too hot, we shouldn't negotiate power yet */
if (evt & PDB_EVT_PE_I_OVRTEMP) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_I_OVRTEMP) {
return PESinkWaitCap;
}
/* If we got a message */
if (evt & (PDB_EVT_PE_MSG_RX | PDB_EVT_PE_MSG_RX_PEND)) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_MSG_RX) {
/* Get the message */
while ((evt & PDB_EVT_PE_MSG_RX_PEND) || readMessage() == true) {
while (readMessage()) {
/* If we got a Source_Capabilities message, read it. */
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOURCE_CAPABILITIES && PD_NUMOBJ_GET(&tempMessage) > 0) {
/* First, determine what PD revision we're using */
@@ -194,15 +191,13 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_wait_cap() {
}
}
return PESinkEvalCap;
/* If the message was a Soft_Reset, do the soft reset procedure */
}
evt = 0;
}
return PESinkWaitCap; // wait for more messages?
}
/* If we failed to get a message, send a hard reset */
return PESinkHardReset;
/* If we failed to get a message, wait longer */
return PESinkWaitCap;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_eval_cap() {
@@ -211,7 +206,9 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_eval_cap() {
* PE_SNK_Select_Cap. */
/* Start by assuming we won't find a PPS APDO (set the index greater
* than the maximum possible) */
_pps_index = 8;
_pps_index = 0xFF;
/* New capabilities also means we can't be making a request from the
* same PPS APDO */
/* Search for the first PPS APDO */
for (int i = 0; i < PD_NUMOBJ_GET(&tempMessage); i++) {
if ((tempMessage.obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED && (tempMessage.obj[i] & PD_APDO_TYPE) == PD_APDO_TYPE_PPS) {
@@ -219,13 +216,18 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_eval_cap() {
break;
}
}
/* New capabilities also means we can't be making a request from the
* same PPS APDO */
_last_pps = 8;
/* Ask the DPM what to request */
if (pdbs_dpm_evaluate_capability(&tempMessage, &_last_dpm_request)) {
/* If we're using PD 3.0 */
if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0) {
/* If the request was for a PPS APDO, start time callbacks if not started */
if (PD_RDO_OBJPOS_GET(&_last_dpm_request) >= _pps_index) {
PPSTimerEnabled = true;
} else {
PPSTimerEnabled = false;
}
}
return PESinkSelectCap;
}
@@ -235,29 +237,28 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_eval_cap() {
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_select_cap() {
/* Transmit the request */
waitForEvent(0xFFFF, 0); // clear pending
waitForEvent((uint32_t)Notifications::PDB_EVT_PE_ALL, 0); // clear pending
ProtocolTransmit::pushMessage(&_last_dpm_request);
// Send indication that there is a message pending
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
eventmask_t evt = waitForEvent(PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_TX_DONE | (uint32_t)Notifications::PDB_EVT_PE_TX_ERR | (uint32_t)Notifications::PDB_EVT_PE_RESET);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET || evt == 0) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET || evt == 0) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & PDB_EVT_PE_TX_ERR) == PDB_EVT_PE_TX_ERR) {
if ((evt & (uint32_t)Notifications::PDB_EVT_PE_TX_ERR) == (uint32_t)Notifications::PDB_EVT_PE_TX_ERR) {
return PESinkHardReset;
}
/* Wait for a response */
evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET, PD_T_SENDER_RESPONSE);
evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_MSG_RX | (uint32_t)Notifications::PDB_EVT_PE_RESET, PD_T_SENDER_RESPONSE);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If we didn't get a response before the timeout, send a hard reset */
if (evt == 0) {
return PESinkHardReset;
return PESinkSoftReset;
}
/* Get the response message */
@@ -265,7 +266,6 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_select_cap() {
readMessage();
/* If the source accepted our request, wait for the new power */
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_ACCEPT && PD_NUMOBJ_GET(&tempMessage) == 0) {
return PESinkTransitionSink;
/* If the message was a Soft_Reset, do the soft reset procedure */
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOFT_RESET && PD_NUMOBJ_GET(&tempMessage) == 0) {
@@ -280,7 +280,7 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_select_cap() {
return PESinkReady;
}
} else {
return PESinkSendSoftReset;
return PESinkSoftReset;
}
}
return PESinkHardReset;
@@ -288,111 +288,97 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_select_cap() {
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_transition_sink() {
/* Wait for the PS_RDY message */
eventmask_t evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET, PD_T_PS_TRANSITION);
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_MSG_RX | (uint32_t)Notifications::PDB_EVT_PE_RESET, PD_T_PS_TRANSITION);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If no message was received, send a hard reset */
if (evt == 0) {
return PESinkHardReset;
}
/* If we received a message, read it */
if (messageWaiting()) {
while (messageWaiting()) {
readMessage();
/* If we got a PS_RDY, handle it */
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_PS_RDY && PD_NUMOBJ_GET(&tempMessage) == 0) {
/* We just finished negotiating an explicit contract */
_explicit_contract = true;
/* Set the output appropriately */
/* Negotiation finished */
pdbs_dpm_transition_requested();
return PESinkReady;
/* If there was a protocol error, send a hard reset */
} else {
/* Turn off the power output before this hard reset to make sure we
* don't supply an incorrect voltage to the device we're powering.
*/
pdbs_dpm_transition_default();
return PESinkHardReset;
}
}
return PESinkHardReset;
return PESinkSoftReset;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_ready() {
eventmask_t evt;
/* Wait for an event */
evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET | PDB_EVT_PE_I_OVRTEMP);
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_ALL);
/* If SinkPPSPeriodicTimer ran out, send a new request */
if (evt & (uint32_t)Notifications::PDB_EVT_PE_PPS_REQUEST) {
return PESinkSelectCap;
}
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If we overheated, send a hard reset */
if (evt & PDB_EVT_PE_I_OVRTEMP) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_I_OVRTEMP) {
return PESinkHardReset;
}
/* If the DPM wants us to, send a Get_Source_Cap message */
if (evt & (uint32_t)Notifications::PDB_EVT_PE_GET_SOURCE_CAP) {
return PESinkGetSourceCap;
}
/* If the DPM wants new power, let it figure out what power it wants
* exactly. This isn't exactly the transition from the spec (that would be
* SelectCap, not EvalCap), but this works better with the particular
* design of this firmware. */
if (evt & (uint32_t)Notifications::PDB_EVT_PE_NEW_POWER) {
/* Tell the protocol layer we're starting an AMS */
return PESinkEvalCap;
}
/* If we received a message */
if (evt & PDB_EVT_PE_MSG_RX) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_MSG_RX) {
if (messageWaiting()) {
readMessage();
/* Ignore vendor-defined messages */
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_VENDOR_DEFINED && PD_NUMOBJ_GET(&tempMessage) > 0) {
return PESinkReady;
/* Ignore Ping messages */
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_PING && PD_NUMOBJ_GET(&tempMessage) == 0) {
return PESinkReady;
/* DR_Swap messages are not supported */
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_DR_SWAP && PD_NUMOBJ_GET(&tempMessage) == 0) {
return PESinkSendNotSupported;
/* Get_Source_Cap messages are not supported */
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_GET_SOURCE_CAP && PD_NUMOBJ_GET(&tempMessage) == 0) {
return PESinkSendNotSupported;
/* PR_Swap messages are not supported */
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_PR_SWAP && PD_NUMOBJ_GET(&tempMessage) == 0) {
return PESinkSendNotSupported;
/* VCONN_Swap messages are not supported */
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_VCONN_SWAP && PD_NUMOBJ_GET(&tempMessage) == 0) {
return PESinkSendNotSupported;
/* Request messages are not supported */
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_REQUEST && PD_NUMOBJ_GET(&tempMessage) > 0) {
return PESinkSendNotSupported;
/* Sink_Capabilities messages are not supported */
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SINK_CAPABILITIES && PD_NUMOBJ_GET(&tempMessage) > 0) {
return PESinkSendNotSupported;
/* Handle GotoMin messages */
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_GOTOMIN && PD_NUMOBJ_GET(&tempMessage) == 0) {
/* GiveBack is not supported */
return PESinkSendNotSupported;
/* Evaluate new Source_Capabilities */
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOURCE_CAPABILITIES && PD_NUMOBJ_GET(&tempMessage) > 0) {
/* Don't free the message: we need to keep the
* Source_Capabilities message so we can evaluate it. */
return PESinkEvalCap;
/* Give sink capabilities when asked */
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_GET_SINK_CAP && PD_NUMOBJ_GET(&tempMessage) == 0) {
return PESinkGiveSinkCap;
/* If the message was a Soft_Reset, do the soft reset procedure */
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOFT_RESET && PD_NUMOBJ_GET(&tempMessage) == 0) {
return PESinkSoftReset;
/* PD 3.0 messges */
} else if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0) {
@@ -411,9 +397,6 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_ready() {
return PESinkSendSoftReset;
}
} else {
/* if we get an unknown message code, silently ignore it*/
return PESinkReady;
}
}
}
@@ -428,15 +411,14 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_get_source_cap() {
get_source_cap->hdr = hdr_template | PD_MSGTYPE_GET_SOURCE_CAP | PD_NUMOBJ(0);
/* Transmit the Get_Source_Cap */
ProtocolTransmit::pushMessage(get_source_cap);
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
eventmask_t evt = waitForEvent(PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_TX_DONE | (uint32_t)Notifications::PDB_EVT_PE_TX_ERR | (uint32_t)Notifications::PDB_EVT_PE_RESET);
/* Free the sent message */
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
if ((evt & (uint32_t)Notifications::PDB_EVT_PE_TX_DONE) == 0) {
return PESinkHardReset;
}
@@ -451,17 +433,16 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_give_sink_cap() {
/* Transmit our capabilities */
ProtocolTransmit::pushMessage(snk_cap);
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
eventmask_t evt = waitForEvent(PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_TX_DONE | (uint32_t)Notifications::PDB_EVT_PE_TX_ERR | (uint32_t)Notifications::PDB_EVT_PE_RESET);
/* Free the Sink_Capabilities message */
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
if ((evt & (uint32_t)Notifications::PDB_EVT_PE_TX_DONE) == 0) {
return PESinkHardReset;
}
@@ -505,16 +486,15 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_soft_reset() {
accept.hdr = hdr_template | PD_MSGTYPE_ACCEPT | PD_NUMOBJ(0);
/* Transmit the Accept */
ProtocolTransmit::pushMessage(&accept);
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
eventmask_t evt = waitForEvent(PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_TX_DONE | (uint32_t)Notifications::PDB_EVT_PE_TX_ERR | (uint32_t)Notifications::PDB_EVT_PE_RESET);
/* Free the sent message */
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
if ((evt & (uint32_t)Notifications::PDB_EVT_PE_TX_DONE) == 0) {
return PESinkHardReset;
}
@@ -531,21 +511,20 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_send_soft_reset() {
softrst->hdr = hdr_template | PD_MSGTYPE_SOFT_RESET | PD_NUMOBJ(0);
/* Transmit the soft reset */
ProtocolTransmit::pushMessage(softrst);
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
eventmask_t evt = waitForEvent(PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_TX_DONE | (uint32_t)Notifications::PDB_EVT_PE_TX_ERR | (uint32_t)Notifications::PDB_EVT_PE_RESET);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
if ((evt & (uint32_t)Notifications::PDB_EVT_PE_TX_DONE) == 0) {
return PESinkHardReset;
}
/* Wait for a response */
evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET, PD_T_SENDER_RESPONSE);
evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_MSG_RX | (uint32_t)Notifications::PDB_EVT_PE_RESET, PD_T_SENDER_RESPONSE);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If we didn't get a response before the timeout, send a hard reset */
@@ -575,27 +554,25 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_send_soft_reset() {
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_send_not_supported() {
/* Get a message object */
union pd_msg *not_supported = &tempMessage;
if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_2_0) {
/* Make a Reject message */
not_supported->hdr = hdr_template | PD_MSGTYPE_REJECT | PD_NUMOBJ(0);
tempMessage.hdr = hdr_template | PD_MSGTYPE_REJECT | PD_NUMOBJ(0);
} else if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0) {
/* Make a Not_Supported message */
not_supported->hdr = hdr_template | PD_MSGTYPE_NOT_SUPPORTED | PD_NUMOBJ(0);
tempMessage.hdr = hdr_template | PD_MSGTYPE_NOT_SUPPORTED | PD_NUMOBJ(0);
}
/* Transmit the message */
ProtocolTransmit::pushMessage(not_supported);
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
eventmask_t evt = waitForEvent(PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
ProtocolTransmit::pushMessage(&tempMessage);
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_TX_DONE | (uint32_t)Notifications::PDB_EVT_PE_TX_ERR | (uint32_t)Notifications::PDB_EVT_PE_RESET);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a soft reset */
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
if ((evt & (uint32_t)Notifications::PDB_EVT_PE_TX_DONE) == 0) {
return PESinkSendSoftReset;
}
@@ -605,9 +582,9 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_send_not_supported() {
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_chunk_received() {
/* Wait for tChunkingNotSupported */
eventmask_t evt = waitForEvent(PDB_EVT_PE_RESET, PD_T_CHUNKING_NOT_SUPPORTED);
EventBits_t evt = waitForEvent((uint32_t)Notifications::PDB_EVT_PE_RESET, PD_T_CHUNKING_NOT_SUPPORTED);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
if (evt & (uint32_t)Notifications::PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
@@ -628,6 +605,23 @@ PolicyEngine::policy_engine_state PolicyEngine::pe_sink_source_unresponsive() {
return PESinkSourceUnresponsive;
}
uint32_t PolicyEngine::waitForEvent(uint32_t mask, TickType_t ticksToWait) { return xEventGroupWaitBits(xEventGroupHandle, mask, mask, pdFALSE, ticksToWait); }
EventBits_t PolicyEngine::waitForEvent(uint32_t mask, TickType_t ticksToWait) { return xEventGroupWaitBits(xEventGroupHandle, mask, mask, pdFALSE, ticksToWait); }
bool PolicyEngine::isPD3_0() { return (hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0; }
void PolicyEngine::handleMessage(union pd_msg *msg) {
xQueueSend(messagesWaiting, msg, 100);
notify(PolicyEngine::Notifications::PDB_EVT_PE_MSG_RX);
}
void PolicyEngine::PPSTimerCallback() {
if (PPSTimerEnabled && state == policy_engine_state::PESinkReady) {
// I believe even once per second is totally fine, but leaning on faster since everything seems cool with faster
// Have seen everything from 10ms to 1 second :D
if ((xTaskGetTickCount() - PPSTimeLastEvent) > (TICKS_SECOND)) {
// Send a new PPS message
PolicyEngine::notify(Notifications::PDB_EVT_PE_PPS_REQUEST);
PPSTimeLastEvent = xTaskGetTickCount();
}
}
}

View File

@@ -25,22 +25,12 @@
*
*/
#define PDB_EVT_PE_RESET EVENT_MASK(0)
#define PDB_EVT_PE_MSG_RX EVENT_MASK(1)
#define PDB_EVT_PE_TX_DONE EVENT_MASK(2)
#define PDB_EVT_PE_TX_ERR EVENT_MASK(3)
#define PDB_EVT_PE_HARD_SENT EVENT_MASK(4)
#define PDB_EVT_PE_I_OVRTEMP EVENT_MASK(5)
#define PDB_EVT_PE_MSG_RX_PEND EVENT_MASK(7) /* Never SEND THIS DIRECTLY*/
class PolicyEngine {
public:
// Sets up internal state and registers the thread
static void init();
// Push an incoming message to the Policy Engine
static void handleMessage(union pd_msg *msg);
// Send a notification
static void notify(uint32_t notification);
// Returns true if headers indicate PD3.0 compliant
static bool isPD3_0();
static bool setupCompleteOrTimedOut() {
@@ -53,7 +43,30 @@ public:
return false;
}
// Has pd negotiation completed
static bool pdHasNegotiated() { return pdNegotiationComplete; }
static bool pdHasNegotiated() {
if (state == policy_engine_state::PESinkSourceUnresponsive)
return false;
return true;
}
// Call this periodically, at least once every second
static void PPSTimerCallback();
enum class Notifications {
PDB_EVT_PE_RESET = EVENT_MASK(0),
PDB_EVT_PE_MSG_RX = EVENT_MASK(1),
PDB_EVT_PE_TX_DONE = EVENT_MASK(2),
PDB_EVT_PE_TX_ERR = EVENT_MASK(3),
PDB_EVT_PE_HARD_SENT = EVENT_MASK(4),
PDB_EVT_PE_I_OVRTEMP = EVENT_MASK(5),
PDB_EVT_PE_PPS_REQUEST = EVENT_MASK(6),
PDB_EVT_PE_GET_SOURCE_CAP = EVENT_MASK(7),
PDB_EVT_PE_NEW_POWER = EVENT_MASK(8),
PDB_EVT_PE_ALL = (EVENT_MASK(9) - 1),
};
// Send a notification
static void notify(Notifications notification);
// Debugging allows looking at state
static uint32_t getState() { return (uint32_t)state; }
private:
static bool pdNegotiationComplete;
@@ -72,17 +85,16 @@ private:
static int8_t _old_tcc_match;
/* The index of the first PPS APDO */
static uint8_t _pps_index;
/* The index of the just-requested PPS APDO */
static uint8_t _last_pps;
static void pe_task(const void *arg);
enum policy_engine_state {
PESinkStartup,
PESinkDiscovery,
PESinkWaitCap,
PESinkEvalCap,
PESinkSelectCap,
PESinkTransitionSink,
PESinkReady,
PESinkSelectCap, // 4
PESinkTransitionSink, // 5
PESinkReady, // 6
PESinkGetSourceCap,
PESinkGiveSinkCap,
PESinkHardReset,
@@ -113,7 +125,7 @@ private:
static enum policy_engine_state pe_sink_source_unresponsive();
static EventGroupHandle_t xEventGroupHandle;
static StaticEventGroup_t xCreatedEventGroup;
static uint32_t waitForEvent(uint32_t mask, TickType_t ticksToWait = portMAX_DELAY);
static EventBits_t waitForEvent(uint32_t mask, TickType_t ticksToWait = portMAX_DELAY);
// Task resources
static osThreadId TaskHandle;
static const size_t TaskStackSize = 2048 / 4;
@@ -131,6 +143,8 @@ private:
static bool messageWaiting();
// Read a pending message into the temp message
static bool readMessage();
static bool PPSTimerEnabled;
static TickType_t PPSTimeLastEvent;
// These callbacks are called to implement the logic for the iron to select the desired voltage

View File

@@ -5,10 +5,12 @@
* Author: Ralim
*/
#include "BSP_PD.h"
#include "configuration.h"
#include "pd.h"
#include "policy_engine.h"
/* The current draw when the output is disabled */
#define DPM_MIN_CURRENT PD_MA2PDI(50)
#define DPM_MIN_CURRENT PD_MA2PDI(100)
/*
* Find the index of the first PDO from capabilities in the voltage range,
* using the desired order.
@@ -54,52 +56,88 @@ bool PolicyEngine::pdbs_dpm_evaluate_capability(const union pd_msg *capabilities
/* Make sure we have configuration */
/* Look at the PDOs to see if one matches our desires */
// Look against USB_PD_Desired_Levels to select in order of preference
for (uint8_t desiredLevel = 0; desiredLevel < USB_PD_Desired_Levels_Len; desiredLevel++) {
uint8_t bestIndex = 0xFF;
int bestIndexVoltage = 0;
int bestIndexCurrent = 0;
bool bestIsPPS = false;
for (uint8_t i = 0; i < numobj; i++) {
/* If we have a fixed PDO, its V equals our desired V, and its I is
* at least our desired I */
if ((capabilities->obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_FIXED) {
// This is a fixed PDO entry
int voltage = PD_PDV2MV(PD_PDO_SRC_FIXED_VOLTAGE_GET(capabilities->obj[i]));
int current = PD_PDO_SRC_FIXED_CURRENT_GET(capabilities->obj[i]);
uint16_t desiredVoltage = USB_PD_Desired_Levels[(desiredLevel * 2) + 0];
uint16_t desiredminCurrent = USB_PD_Desired_Levels[(desiredLevel * 2) + 1];
// As pd stores current in 10mA increments, divide by 10
desiredminCurrent /= 10;
if (voltage == desiredVoltage) {
if (current >= desiredminCurrent) {
// Evaluate if it can produve sufficient current based on the tipResistance (ohms*10)
// V=I*R -> V/I => minimum resistance, if our tip resistance is >= this then we can use this supply
int voltage_mv = PD_PDV2MV(PD_PDO_SRC_FIXED_VOLTAGE_GET(capabilities->obj[i])); // voltage in mV units
int current_a_x100 = PD_PDO_SRC_FIXED_CURRENT_GET(capabilities->obj[i]); // current in 10mA units
int min_resistance_ohmsx10 = voltage_mv / current_a_x100;
if (voltage_mv <= (USB_PD_VMAX * 1000)) {
if (min_resistance_ohmsx10 <= tipResistance) {
// This is a valid power source we can select as
if (voltage_mv > bestIndexVoltage || bestIndex == 0xFF) {
// Higher voltage and valid, select this instead
bestIndex = i;
bestIndexVoltage = voltage_mv;
bestIndexCurrent = current_a_x100;
bestIsPPS = false;
}
}
}
} else
if ((capabilities->obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED && (capabilities->obj[i] & PD_APDO_TYPE) == PD_APDO_TYPE_PPS) {
// If this is a PPS slot, calculate the max voltage in the PPS range that can we be used and maintain
uint16_t max_voltage = PD_PAV2MV(PD_APDO_PPS_MAX_VOLTAGE_GET(capabilities->obj[i]));
// uint16_t min_voltage = PD_PAV2MV(PD_APDO_PPS_MIN_VOLTAGE_GET(capabilities->obj[i]));
uint16_t max_current = PD_PAI2CA(PD_APDO_PPS_CURRENT_GET(capabilities->obj[i])); // max current in 10mA units
// Using the current and tip resistance, calculate the ideal max voltage
// if this is range, then we will work with this voltage
// if this is not in range; then max_voltage can be safely selected
int ideal_voltage_mv = (tipResistance * max_current);
if (ideal_voltage_mv > max_voltage) {
ideal_voltage_mv = max_voltage; // constrain
}
if (ideal_voltage_mv > (USB_PD_VMAX * 1000)) {
ideal_voltage_mv = (USB_PD_VMAX * 1000); // constrain to model max
}
if (ideal_voltage_mv > bestIndexVoltage || bestIndex == 0xFF) {
bestIndex = i;
bestIndexVoltage = ideal_voltage_mv;
bestIndexCurrent = max_current;
bestIsPPS = true;
}
}
}
if (bestIndex != 0xFF) {
/* We got what we wanted, so build a request for that */
request->hdr = hdr_template | PD_MSGTYPE_REQUEST | PD_NUMOBJ(1);
/* GiveBack disabled */
request->obj[0] = PD_RDO_FV_MAX_CURRENT_SET(current) | PD_RDO_FV_CURRENT_SET(current) | PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(i + 1);
// We support usb comms (ish)
request->obj[0] |= PD_RDO_USB_COMMS;
if (bestIsPPS) {
request->obj[0] = PD_RDO_PROG_CURRENT_SET(PD_CA2PAI(bestIndexCurrent)) | PD_RDO_PROG_VOLTAGE_SET(PD_MV2PRV(bestIndexVoltage)) | PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(bestIndex + 1);
} else {
request->obj[0] = PD_RDO_FV_MAX_CURRENT_SET(bestIndexCurrent) | PD_RDO_FV_CURRENT_SET(bestIndexCurrent) | PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(bestIndex + 1);
}
// We dont do usb
// request->obj[0] |= PD_RDO_USB_COMMS;
/* Update requested voltage */
_requested_voltage = voltage;
return true;
}
}
}
}
}
_requested_voltage = bestIndexVoltage;
} else {
/* Nothing matched (or no configuration), so get 5 V at low current */
request->hdr = hdr_template | PD_MSGTYPE_REQUEST | PD_NUMOBJ(1);
request->obj[0] = PD_RDO_FV_MAX_CURRENT_SET(DPM_MIN_CURRENT) | PD_RDO_FV_CURRENT_SET(DPM_MIN_CURRENT) | PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(1);
/* If the output is enabled and we got here, it must be a capability
* mismatch. */
/* If the output is enabled and we got here, it must be a capability mismatch. */
if (pdNegotiationComplete) {
request->obj[0] |= PD_RDO_CAP_MISMATCH;
}
request->obj[0] |= PD_RDO_USB_COMMS;
// We dont do usb
// request->obj[0] |= PD_RDO_USB_COMMS;
/* Update requested voltage */
_requested_voltage = 5000;
return false;
}
// Even if we didnt match, we return true as we would still like to handshake on 5V at the minimum
return true;
}
void PolicyEngine::pdbs_dpm_get_sink_capability(union pd_msg *cap) {
@@ -112,8 +150,12 @@ void PolicyEngine::pdbs_dpm_get_sink_capability(union pd_msg *cap) {
cap->obj[numobj++] = PD_PDO_TYPE_FIXED | PD_PDO_SNK_FIXED_VOLTAGE_SET(PD_MV2PDV(5000)) | PD_PDO_SNK_FIXED_CURRENT_SET(DPM_MIN_CURRENT);
/* Get the current we want */
uint16_t current = USB_PD_Desired_Levels[1] / 10; // In centi-amps
uint16_t voltage = USB_PD_Desired_Levels[0]; // in mv
uint16_t voltage = USB_PD_VMAX * 1000; // in mv
if (_requested_voltage != 5000) {
voltage = _requested_voltage;
}
uint16_t current = (voltage) / tipResistance; // In centi-amps
/* Add a PDO for the desired power. */
cap->obj[numobj++] = PD_PDO_TYPE_FIXED | PD_PDO_SNK_FIXED_VOLTAGE_SET(PD_MV2PDV(voltage)) | PD_PDO_SNK_FIXED_CURRENT_SET(current);
@@ -143,6 +185,11 @@ void PolicyEngine::pdbs_dpm_get_sink_capability(union pd_msg *cap) {
cap->obj[2] ^= cap->obj[1];
cap->obj[1] ^= cap->obj[2];
}
/* If we're using PD 3.0, add a PPS APDO for our desired voltage */
if ((hdr_template & PD_HDR_SPECREV) >= PD_SPECREV_3_0) {
cap->obj[numobj++]
= PD_PDO_TYPE_AUGMENTED | PD_APDO_TYPE_PPS | PD_APDO_PPS_MAX_VOLTAGE_SET(PD_MV2PAV(voltage)) | PD_APDO_PPS_MIN_VOLTAGE_SET(PD_MV2PAV(voltage)) | PD_APDO_PPS_CURRENT_SET(PD_CA2PAI(current));
}
}
/* Set the unconstrained power flag. */
@@ -167,7 +214,6 @@ bool PolicyEngine::pdbs_dpm_evaluate_typec_current(enum fusb_typec_current tcc)
}
void PolicyEngine::pdbs_dpm_transition_default() {
/* Cast the dpm_data to the right type */
/* Pretend we requested 5 V */
current_voltage_mv = 5000;
@@ -175,12 +221,7 @@ void PolicyEngine::pdbs_dpm_transition_default() {
pdNegotiationComplete = false;
}
void PolicyEngine::pdbs_dpm_transition_requested() {
/* Cast the dpm_data to the right type */
pdNegotiationComplete = true;
}
void PolicyEngine::handleMessage(union pd_msg *msg) { xQueueSend(messagesWaiting, msg, 100); }
void PolicyEngine::pdbs_dpm_transition_requested() { pdNegotiationComplete = true; }
bool PolicyEngine::messageWaiting() { return uxQueueMessagesWaiting(messagesWaiting) > 0; }

View File

@@ -1,183 +0,0 @@
/*
* PD Buddy Firmware Library - USB Power Delivery for everyone
* Copyright 2017-2018 Clayton G. Hobbs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "protocol_rx.h"
#include "fusb302b.h"
#include "policy_engine.h"
#include "protocol_tx.h"
#include "string.h"
#include <pd.h>
#include <stdlib.h>
osThreadId ProtocolReceive::TaskHandle = NULL;
EventGroupHandle_t ProtocolReceive::xEventGroupHandle = NULL;
StaticEventGroup_t ProtocolReceive::xCreatedEventGroup;
uint32_t ProtocolReceive::TaskBuffer[ProtocolReceive::TaskStackSize];
osStaticThreadDef_t ProtocolReceive::TaskControlBlock;
union pd_msg ProtocolReceive::tempMessage;
uint8_t ProtocolReceive::_rx_messageid;
uint8_t ProtocolReceive::_tx_messageidcounter;
/*
* PRL_Rx_Wait_for_PHY_Message state
*/
ProtocolReceive::protocol_rx_state ProtocolReceive::protocol_rx_wait_phy() {
/* Wait for an event */
_rx_messageid = 0;
eventmask_t evt = waitForEvent(PDB_EVT_PRLRX_RESET | PDB_EVT_PRLRX_I_GCRCSENT | PDB_EVT_PRLRX_I_RXPEND);
/* If we got a reset event, reset */
if (evt & PDB_EVT_PRLRX_RESET) {
waitForEvent(PDB_EVT_PRLRX_RESET, 0);
return PRLRxWaitPHY;
}
/* If we got an I_GCRCSENT event, read the message and decide what to do */
if (evt & PDB_EVT_PRLRX_I_GCRCSENT) {
/* Get a buffer to read the message into. Guaranteed to not fail
* because we have a big enough pool and are careful. */
union pd_msg *_rx_message = &tempMessage;
memset(&tempMessage, 0, sizeof(tempMessage));
/* Read the message */
fusb_read_message(_rx_message);
/* If it's a Soft_Reset, go to the soft reset state */
if (PD_MSGTYPE_GET(_rx_message) == PD_MSGTYPE_SOFT_RESET && PD_NUMOBJ_GET(_rx_message) == 0) {
return PRLRxReset;
} else {
/* Otherwise, check the message ID */
return PRLRxCheckMessageID;
}
} else if (evt & PDB_EVT_PRLRX_I_RXPEND) {
// There is an RX message pending that is not a Good CRC
union pd_msg *_rx_message = &tempMessage;
/* Read the message */
fusb_read_message(_rx_message);
return PRLRxWaitPHY;
}
return PRLRxWaitPHY;
}
/*
* PRL_Rx_Layer_Reset_for_Receive state
*/
ProtocolReceive::protocol_rx_state ProtocolReceive::protocol_rx_reset() {
/* Reset MessageIDCounter */
_tx_messageidcounter = 0;
/* Clear stored MessageID */
_rx_messageid = -1;
/* TX transitions to its reset state */
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_RESET);
taskYIELD();
/* If we got a RESET signal, reset the machine */
if (waitForEvent(PDB_EVT_PRLRX_RESET, 0) != 0) {
return PRLRxWaitPHY;
}
/* Go to the Check_MessageID state */
return PRLRxCheckMessageID;
}
volatile uint32_t rxCounter = 0;
/*
* PRL_Rx_Check_MessageID state
*/
ProtocolReceive::protocol_rx_state ProtocolReceive::protocol_rx_check_messageid() {
/* If we got a RESET signal, reset the machine */
// if (waitForEvent(PDB_EVT_PRLRX_RESET, 0) == PDB_EVT_PRLRX_RESET) {
// return PRLRxWaitPHY;
// }
/* If the message has the stored ID, we've seen this message before. Free
* it and don't pass it to the policy engine. */
/* Otherwise, there's either no stored ID or this message has an ID we
* haven't just seen. Transition to the Store_MessageID state. */
// if (PD_MESSAGEID_GET(&tempMessage) == _rx_messageid) {
// return PRLRxWaitPHY;
// } else
{
rxCounter++;
return PRLRxStoreMessageID;
}
}
/*
* PRL_Rx_Store_MessageID state
*/
ProtocolReceive::protocol_rx_state ProtocolReceive::protocol_rx_store_messageid() {
/* Tell ProtocolTX to discard the message being transmitted */
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_DISCARD);
/* Update the stored MessageID */
_rx_messageid = PD_MESSAGEID_GET(&tempMessage);
/* Pass the message to the policy engine. */
PolicyEngine::handleMessage(&tempMessage);
PolicyEngine::notify(PDB_EVT_PE_MSG_RX);
taskYIELD();
/* Don't check if we got a RESET because we'd do nothing different. */
return PRLRxWaitPHY;
}
void ProtocolReceive::init() {
osThreadStaticDef(protRX, thread, PDB_PRIO_PRL, 0, TaskStackSize, TaskBuffer, &TaskControlBlock);
xEventGroupHandle = xEventGroupCreateStatic(&xCreatedEventGroup);
TaskHandle = osThreadCreate(osThread(protRX), NULL);
}
void ProtocolReceive::thread(const void *args) {
(void)args;
ProtocolReceive::protocol_rx_state state = PRLRxWaitPHY;
while (true) {
switch (state) {
case PRLRxWaitPHY:
state = protocol_rx_wait_phy();
break;
case PRLRxReset:
state = protocol_rx_reset();
break;
case PRLRxCheckMessageID:
state = protocol_rx_check_messageid();
break;
case PRLRxStoreMessageID:
state = protocol_rx_store_messageid();
break;
default:
/* This is an error. It really shouldn't happen. We might
* want to handle it anyway, though. */
state = PRLRxWaitPHY;
break;
}
}
}
void ProtocolReceive::notify(uint32_t notification) {
if (xEventGroupHandle != NULL) {
xEventGroupSetBits(xEventGroupHandle, notification);
}
}
uint32_t ProtocolReceive::waitForEvent(uint32_t mask, TickType_t ticksToWait) {
if (xEventGroupHandle != NULL) {
return xEventGroupWaitBits(xEventGroupHandle, mask, mask, pdFALSE, ticksToWait);
}
return 0;
}

View File

@@ -1,61 +0,0 @@
/*
* PD Buddy Firmware Library - USB Power Delivery for everyone
* Copyright 2017-2018 Clayton G. Hobbs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef PDB_PROTOCOL_RX_H
#define PDB_PROTOCOL_RX_H
#include <stdint.h>
#include <pd.h>
/* Events for the Protocol RX thread */
#define PDB_EVT_PRLRX_RESET EVENT_MASK(0)
#define PDB_EVT_PRLRX_I_GCRCSENT EVENT_MASK(1)
#define PDB_EVT_PRLRX_I_RXPEND EVENT_MASK(2)
class ProtocolReceive {
public:
static void init();
static void notify(uint32_t notification);
private:
static void thread(const void *args);
static EventGroupHandle_t xEventGroupHandle;
static StaticEventGroup_t xCreatedEventGroup;
static osThreadId TaskHandle;
static const size_t TaskStackSize = 1024 / 4;
static uint32_t TaskBuffer[TaskStackSize];
static osStaticThreadDef_t TaskControlBlock;
/*
* Protocol RX machine states
*
* There is no Send_GoodCRC state because the PHY sends the GoodCRC for us.
* All transitions that would go to that state instead go to Check_MessageID.
*/
enum protocol_rx_state { PRLRxWaitPHY, PRLRxReset, PRLRxCheckMessageID, PRLRxStoreMessageID };
static protocol_rx_state protocol_rx_store_messageid();
static protocol_rx_state protocol_rx_check_messageid();
static protocol_rx_state protocol_rx_reset();
static protocol_rx_state protocol_rx_wait_phy();
static union pd_msg tempMessage;
static uint8_t _rx_messageid;
static uint8_t _tx_messageidcounter;
static uint32_t waitForEvent(uint32_t mask, TickType_t ticksToWait = portMAX_DELAY);
};
#endif /* PDB_PROTOCOL_RX_H */

View File

@@ -16,10 +16,10 @@
*/
#include "protocol_tx.h"
#include "Defines.h"
#include "fusb302b.h"
#include "fusbpd.h"
#include "policy_engine.h"
#include "protocol_rx.h"
#include <pd.h>
osThreadId ProtocolTransmit::TaskHandle = NULL;
@@ -44,7 +44,7 @@ ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_phy_reset() {
* we failed to send it */
if (messagePending()) {
/* Tell the policy engine that we failed */
PolicyEngine::notify(PDB_EVT_PE_TX_ERR);
PolicyEngine::notify(PolicyEngine::Notifications::PDB_EVT_PE_TX_ERR);
/* Finish failing to send the message */
while (messagePending()) {
getMessage(); // Discard
@@ -88,10 +88,6 @@ ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_reset() {
/* Clear MessageIDCounter */
_tx_messageidcounter = 0;
/* Tell the Protocol RX thread to reset */
ProtocolReceive::notify(PDB_EVT_PRLRX_RESET);
taskYIELD();
return PRLTxConstructMessage;
}
@@ -104,17 +100,12 @@ ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_construct_mess
temp_msg.hdr |= (_tx_messageidcounter % 8) << PD_HDR_MESSAGEID_SHIFT;
/* PD 3.0 collision avoidance */
// if (PolicyEngine::isPD3_0()) {
// /* If we're starting an AMS, wait for permission to transmit */
// evt = waitForEvent((uint32_t) Notifications::PDB_EVT_PRLTX_START_AMS,
// 0);
// if ((uint32_t) evt
// & (uint32_t) Notifications::PDB_EVT_PRLTX_START_AMS) {
// while (fusb_get_typec_current() != fusb_sink_tx_ok) {
// osDelay(1);
// }
// }
// }
if (PolicyEngine::isPD3_0()) {
/* If we're starting an AMS, wait for permission to transmit */
while (fusb_get_typec_current() != fusb_sink_tx_ok) {
vTaskDelay(TICKS_10MS);
}
}
messageSending = true;
/* Send the message to the PHY */
fusb_send_message(&temp_msg);
@@ -173,7 +164,7 @@ ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_transmission_e
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
/* Tell the policy engine that we failed */
PolicyEngine::notify(PDB_EVT_PE_TX_ERR);
PolicyEngine::notify(PolicyEngine::Notifications::PDB_EVT_PE_TX_ERR);
return PRLTxWaitMessage;
}
@@ -184,7 +175,7 @@ ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_message_sent()
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
/* Tell the policy engine that we succeeded */
PolicyEngine::notify(PDB_EVT_PE_TX_DONE);
PolicyEngine::notify(PolicyEngine::Notifications::PDB_EVT_PE_TX_DONE);
return PRLTxWaitMessage;
}
@@ -257,7 +248,9 @@ void ProtocolTransmit::init() {
void ProtocolTransmit::pushMessage(union pd_msg *msg) {
if (messagesWaiting) {
xQueueSend(messagesWaiting, msg, 100);
if (xQueueSend(messagesWaiting, msg, 100) == pdTRUE) {
notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
}
}
}

View File

@@ -19,7 +19,7 @@
#define PDB_PROTOCOL_TX_H
#include "policy_engine.h"
#include "protocol_rx.h"
#include <pd.h>
#include <stdint.h>
@@ -38,7 +38,6 @@ public:
PDB_EVT_PRLTX_I_RETRYFAIL = EVENT_MASK(2), //
PDB_EVT_PRLTX_DISCARD = EVENT_MASK(3), //
PDB_EVT_PRLTX_MSG_TX = EVENT_MASK(4), //
PDB_EVT_PRLTX_START_AMS = EVENT_MASK(5), //
};
static void notify(Notifications notification);

View File

@@ -10,8 +10,6 @@
#include <I2CBB.hpp>
SemaphoreHandle_t I2CBB::I2CSemaphore = NULL;
StaticSemaphore_t I2CBB::xSemaphoreBuffer;
SemaphoreHandle_t I2CBB::I2CSemaphore2 = NULL;
StaticSemaphore_t I2CBB::xSemaphoreBuffer2;
void I2CBB::init() {
// Set GPIO's to output open drain
GPIO_InitTypeDef GPIO_InitStruct;
@@ -29,9 +27,7 @@ void I2CBB::init() {
SOFT_SDA_HIGH();
SOFT_SCL_HIGH();
I2CSemaphore = xSemaphoreCreateMutexStatic(&xSemaphoreBuffer);
I2CSemaphore2 = xSemaphoreCreateMutexStatic(&xSemaphoreBuffer2);
unlock();
unlock2();
}
bool I2CBB::probe(uint8_t address) {
@@ -291,14 +287,4 @@ void I2CBB::write_bit(uint8_t val) {
SOFT_SCL_LOW();
}
void I2CBB::unlock2() { xSemaphoreGive(I2CSemaphore2); }
bool I2CBB::lock2() {
if (I2CSemaphore2 == NULL) {
asm("bkpt");
}
bool a = xSemaphoreTake(I2CSemaphore2, (TickType_t)500) == pdTRUE;
return a;
}
#endif

View File

@@ -28,14 +28,10 @@ public:
static void Transmit(uint16_t DevAddress, uint8_t *pData, uint16_t Size);
static void Receive(uint16_t DevAddress, uint8_t *pData, uint16_t Size);
static void TransmitReceive(uint16_t DevAddress, uint8_t *pData_tx, uint16_t Size_tx, uint8_t *pData_rx, uint16_t Size_rx);
static void unlock2();
static bool lock2();
private:
static SemaphoreHandle_t I2CSemaphore;
static StaticSemaphore_t xSemaphoreBuffer;
static SemaphoreHandle_t I2CSemaphore2;
static StaticSemaphore_t xSemaphoreBuffer2;
static void unlock();
static bool lock();
static void start();

View File

@@ -5,9 +5,9 @@
* Author: Ben V. Brown
*/
#include "../../configuration.h"
#include "Translation.h"
#include "cmsis_os.h"
#include "configuration.h"
#include <OLED.hpp>
#include <stdlib.h>
#include <string.h>

View File

@@ -6,9 +6,9 @@
*/
#include "TipThermoModel.h"
#include "../../configuration.h"
#include "BSP.h"
#include "Settings.h"
#include "configuration.h"
#include "main.hpp"
#include "power.hpp"
/*

View File

@@ -122,8 +122,9 @@
#define POWER_LIMIT 0 // 0 watts default limit
#define MAX_POWER_LIMIT 65 //
#define POWER_LIMIT_STEPS 5 //
#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_TS100
#define TEMP_uV_LOOKUP_HAKKO
#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_TS100 //
#define TEMP_uV_LOOKUP_HAKKO //
#define USB_PD_VMAX 20 // Maximum voltage for PD to negotiate
#endif
#ifdef MODEL_Pinecil
@@ -133,8 +134,9 @@
#define POWER_LIMIT 0 // 0 watts default limit
#define MAX_POWER_LIMIT 65 //
#define POWER_LIMIT_STEPS 5 //
#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_TS100
#define TEMP_uV_LOOKUP_HAKKO
#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_TS100 // Uses TS100 resistors
#define TEMP_uV_LOOKUP_HAKKO // Use Hakko lookup table
#define USB_PD_VMAX 20 // Maximum voltage for PD to negotiate
#endif
#ifdef MODEL_TS80
@@ -143,9 +145,10 @@
#define CALIBRATION_OFFSET 900 // the adc offset in uV
#define POWER_LIMIT 24 // 24 watts default power limit
#define MAX_POWER_LIMIT 30 //
#define POWER_LIMIT_STEPS 2
#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_TS80
#define TEMP_uV_LOOKUP_TS80
#define POWER_LIMIT_STEPS 2 //
#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_TS80 //
#define TEMP_uV_LOOKUP_TS80 //
#define USB_PD_VMAX 12 // Maximum voltage for PD to negotiate
#endif
#ifdef MODEL_TS80P
@@ -154,9 +157,10 @@
#define CALIBRATION_OFFSET 1500 // the adc offset in uV
#define POWER_LIMIT 30 // 30 watts default power limit
#define MAX_POWER_LIMIT 35 //
#define POWER_LIMIT_STEPS 2
#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_TS80
#define TEMP_uV_LOOKUP_TS80
#define POWER_LIMIT_STEPS 2 //
#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_TS80 //
#define TEMP_uV_LOOKUP_TS80 //
#define USB_PD_VMAX 12 // Maximum voltage for PD to negotiate
#endif
#ifdef MODEL_TS100

View File

@@ -5,8 +5,8 @@
* Authors: Ben V. Brown, David Hilton (David's Idea)
*/
#include "../../configuration.h"
#include "BSP.h"
#include "configuration.h"
#include "expMovingAverage.h"
#include "stdint.h"
#include <history.hpp>

View File

@@ -81,12 +81,12 @@ void seekQC(int16_t Vx10, uint16_t divisor) {
return; // dont bother with small steps
while (steps < 0) {
QC_SeekContNeg();
osDelay(30);
vTaskDelay(3 * TICKS_10MS);
steps++;
}
while (steps > 0) {
QC_SeekContPlus();
osDelay(30);
vTaskDelay(3 * TICKS_10MS);
steps--;
}
osDelay(100);
@@ -140,7 +140,7 @@ void startQC(uint16_t divisor) {
// Delay 1.25 seconds
uint8_t enteredQC = 0;
for (uint16_t i = 0; i < 200 && enteredQC == 0; i++) {
osDelay(10); // 10mS pause
vTaskDelay(TICKS_10MS); // 10mS pause
if (i > 130) {
if (QC_DM_PulledDown()) {
enteredQC = 1;
@@ -165,7 +165,7 @@ void startQC(uint16_t divisor) {
QCMode = QCState::QC_3; // We have at least QC2, pray for 3
return;
}
osDelay(100); // 100mS
vTaskDelay(TICKS_100MS); // 100mS
}
QCMode = QCState::NOT_STARTED;
QCTries++;

View File

@@ -9,9 +9,9 @@
*/
#include "Settings.h"
#include "../../configuration.h"
#include "BSP.h"
#include "Setup.h"
#include "configuration.h"
#include "string.h"
volatile systemSettingsType systemSettings;

View File

@@ -6,11 +6,11 @@
*/
#include "gui.hpp"
#include "../../configuration.h"
#include "Buttons.hpp"
#include "TipThermoModel.h"
#include "Translation.h"
#include "cmsis_os.h"
#include "configuration.h"
#include "main.hpp"
void gui_Menu(const menuitem *menu);

View File

@@ -4,13 +4,11 @@
* Main.cpp bootstraps the device and then hands over to FreeRTOS and the threads
*/
#include "main.hpp"
#include "BSP.h"
#include "LIS2DH12.hpp"
#include "Settings.h"
#include "cmsis_os.h"
#include <MMA8652FC.hpp>
#include <main.hpp>
#include <power.hpp>
#include "power.hpp"
uint8_t DetectedAccelerometerVersion = 0;
bool settingsWereReset = false;
// FreeRTOS variables
@@ -45,21 +43,23 @@ int main(void) {
settingsWereReset = restoreSettings(); // load the settings from flash
resetWatchdog();
/* Create the thread(s) */
/* definition and creation of POWTask - Power management for QC */
osThreadStaticDef(POWTask, startPOWTask, osPriorityAboveNormal, 0, POWTaskStackSize, POWTaskBuffer, &POWTaskControlBlock);
POWTaskHandle = osThreadCreate(osThread(POWTask), NULL);
/* definition and creation of GUITask - The OLED control & update*/
osThreadStaticDef(GUITask, startGUITask, osPriorityBelowNormal, 0, GUITaskStackSize, GUITaskBuffer, &GUITaskControlBlock);
GUITaskHandle = osThreadCreate(osThread(GUITask), NULL);
/* definition and creation of PIDTask - Heating control*/
osThreadStaticDef(PIDTask, startPIDTask, osPriorityRealtime, 0, PIDTaskStackSize, PIDTaskBuffer, &PIDTaskControlBlock);
PIDTaskHandle = osThreadCreate(osThread(PIDTask), NULL);
/* definition and creation of POWTask - Power management for QC */
osThreadStaticDef(POWTask, startPOWTask, osPriorityAboveNormal, 0, POWTaskStackSize, POWTaskBuffer, &POWTaskControlBlock);
POWTaskHandle = osThreadCreate(osThread(POWTask), NULL);
/* definition and creation of MOVTask - Accelerometer management */
osThreadStaticDef(MOVTask, startMOVTask, osPriorityNormal, 0, MOVTaskStackSize, MOVTaskBuffer, &MOVTaskControlBlock);
MOVTaskHandle = osThreadCreate(osThread(MOVTask), NULL);
/* definition and creation of GUITask - The OLED control & update*/
osThreadStaticDef(GUITask, startGUITask, osPriorityBelowNormal, 0, GUITaskStackSize, GUITaskBuffer, &GUITaskControlBlock);
GUITaskHandle = osThreadCreate(osThread(GUITask), NULL);
resetWatchdog();
/* Start scheduler */

View File

@@ -7,7 +7,6 @@
extern "C" {
#include "FreeRTOSConfig.h"
}
#include "../../configuration.h"
#include "Buttons.hpp"
#include "I2CBB.hpp"
#include "LIS2DH12.hpp"
@@ -15,6 +14,7 @@ extern "C" {
#include "TipThermoModel.h"
#include "Translation.h"
#include "cmsis_os.h"
#include "configuration.h"
#include "main.hpp"
#include "stdlib.h"
#include "string.h"

View File

@@ -10,6 +10,7 @@
#include "QC3.h"
#include "Settings.h"
#include "cmsis_os.h"
#include "fusbpd.h"
#include "main.hpp"
#include "stdlib.h"
#include "task.h"
@@ -17,9 +18,20 @@
// Small worker thread to handle power (mostly QC) related steps
void startPOWTask(void const *argument __unused) {
// You have to run this once we are willing to answer PD messages
// Setting up too early can mean that we miss the ~20ms window to respond on some chargers
#ifdef POW_PD
if (usb_pd_detect() == true) {
// Spawn all of the USB-C processors
fusb302_start_processing();
}
#endif
vTaskDelay(TICKS_100MS);
// Init any other misc sensors
postRToSInit();
for (;;) {
osDelay(TICKS_100MS); // Slow down update rate
power_check();
osDelay(TICKS_100MS); // Slow down update rate
}
}