1
0
forked from me/IronOS

Merge pull request #672 from Ralim/supporting-FUSB302

Adding support for the TS80P using FUSB302 for USB-PD
This commit is contained in:
Ben V. Brown
2020-08-15 17:10:35 +10:00
committed by GitHub
64 changed files with 5487 additions and 1665 deletions

View File

@@ -2,7 +2,7 @@
#include "BSP_Power.h"
#include "BSP_QC.h"
#include "Defines.h"
#include "UnitSettings.h"
#include "Model_Config.h"
#include "stdint.h"
/*
* BSP.h -- Board Support
@@ -47,8 +47,11 @@ void reboot();
// If the user has programmed in a bootup logo, draw it to the screen from flash
// Returns 1 if the logo was printed so that the unit waits for the timeout or button
uint8_t showBootLogoIfavailable();
//delay wrapper for delay using the hardware timer (used before RTOS)
void delay_ms(uint16_t count) ;
//Used to allow knowledge of if usb_pd is being used
uint8_t usb_pd_detect();
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,16 @@
/*
* BSP_PD.h
*
* Created on: 21 Jul 2020
* Author: Ralim
*/
#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;
#endif /* USER_BSP_PD_H_ */

View File

@@ -12,9 +12,6 @@
extern "C" {
#endif
// Called once at startup, after RToS
// This can handle negotiations for QC/PD etc
void power_probe();
// Called periodically in the movement handling thread
// Can be used to check any details for the power system

View File

@@ -7,6 +7,7 @@
#include "Pins.h"
#include "main.hpp"
#include "history.hpp"
#include "Model_Config.h"
#include "I2C_Wrapper.hpp"
volatile uint16_t PWMSafetyTimer = 0;
volatile uint8_t pendingPWM = 0;
@@ -16,8 +17,89 @@ history<uint16_t, PID_TIM_HZ> rawTempFilter = { { 0 }, 0, 0 };
void resetWatchdog() {
HAL_IWDG_Refresh(&hiwdg);
}
#ifdef TEMP_NTC
//Lookup table for the NTC
//Stored as ADCReading,Temp in degC
static const uint16_t NTCHandleLookup[] = {
//ADC Reading , Temp in C
29189, 0, //
29014, 1, //
28832, 2, //
28644, 3, //
28450, 4, //
28249, 5, //
28042, 6, //
27828, 7, //
27607, 8, //
27380, 9, //
27146, 10, //
26906, 11, //
26660, 12, //
26407, 13, //
26147, 14, //
25882, 15, //
25610, 16, //
25332, 17, //
25049, 18, //
24759, 19, //
24465, 20, //
24164, 21, //
23859, 22, //
23549, 23, //
23234, 24, //
22915, 25, //
22591, 26, //
22264, 27, //
21933, 28, //
21599, 29, //
// 21261, 30, //
// 20921, 31, //
// 20579, 32, //
// 20234, 33, //
// 19888, 34, //
// 19541, 35, //
// 19192, 36, //
// 18843, 37, //
// 18493, 38, //
// 18143, 39, //
// 17793, 40, //
// 17444, 41, //
// 17096, 42, //
// 16750, 43, //
// 16404, 44, //
// 16061, 45, //
// 15719, 46, //
// 15380, 47, //
// 15044, 48, //
// 14710, 49, //
// 14380, 50, //
// 14053, 51, //
// 13729, 52, //
// 13410, 53, //
// 13094, 54, //
// 12782, 55, //
// 12475, 56, //
// 12172, 57, //
// 11874, 58, //
// 11580, 59, //
// 11292, 60, //
};
#endif
uint16_t getHandleTemperature() {
#ifdef TEMP_NTC
//TS80P uses 100k NTC resistors instead
//NTCG104EF104FT1X from TDK
//For now not doing interpolation
int32_t result = getADC(0);
for (uint32_t i = 0; i < (sizeof(NTCHandleLookup) / (2 * sizeof(uint16_t)));
i++) {
if (result > NTCHandleLookup[(i * 2) + 0]) {
return NTCHandleLookup[(i * 2) + 1] * 10;
}
}
return 0;
#endif
#ifdef TEMP_TMP36
// We return the current handle temperature in X10 C
// TMP36 in handle, 0.5V offset and then 10mV per deg C (0.75V @ 25C for
// example) STM32 = 4096 count @ 3.3V input -> But We oversample by 32/(2^2) =
@@ -32,6 +114,7 @@ uint16_t getHandleTemperature() {
result *= 100;
result /= 993;
return result;
#endif
}
uint16_t getTipInstantTemperature() {
@@ -94,6 +177,9 @@ uint16_t getInputVoltageX10(uint16_t divisor, uint8_t sample) {
sum += samples[i];
sum /= BATTFILTERDEPTH;
if (divisor == 0) {
divisor = 1;
}
return sum * 4 / divisor;
}
@@ -214,6 +300,7 @@ uint8_t getButtonB() {
}
void reboot() {
NVIC_SystemReset();
}

View File

@@ -0,0 +1,22 @@
/*
* 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,6 +6,7 @@
*/
#include "IRQ.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
@@ -41,3 +42,8 @@ void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c __unused) {
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c __unused) {
FRToSI2C::CpltCallback();
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
(void)GPIO_Pin;
InterruptHandler::irqCallback();
}

View File

@@ -24,6 +24,7 @@ void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c);
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c);
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c);
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c);
void HAL_GPIO_EXTI_Callback(uint16_t);
#ifdef __cplusplus
}

View File

@@ -0,0 +1,44 @@
/*
* Model_Config.h
*
* Created on: 25 Jul 2020
* Author: Ralim
*/
#ifndef BSP_MINIWARE_MODEL_CONFIG_H_
#define BSP_MINIWARE_MODEL_CONFIG_H_
/*
* Lookup for mapping features <-> Models
*/
#if defined(MODEL_TS100) + defined(MODEL_TS80)+defined(MODEL_TS80P) > 1
#error "Multiple models defined!"
#elif defined(MODEL_TS100) + defined(MODEL_TS80)+ defined(MODEL_TS80P) == 0
#error "No model defined!"
#endif
#ifdef MODEL_TS100
#define ACCEL_MMA
#define ACCEL_LIS
#define TEMP_TMP36
#endif
#ifdef MODEL_TS80
#define ACCEL_LIS
#define POW_QC
#define TEMP_TMP36
#define LIS_ORI_FLIP
#define OLED_FLIP
#endif
#ifdef MODEL_TS80P
#define ACCEL_LIS
#define POW_PD
#define POW_QC
#define TEMP_NTC
#define I2C_SOFT
#define LIS_ORI_FLIP
#define OLED_FLIP
#endif
#endif /* BSP_MINIWARE_MODEL_CONFIG_H_ */

View File

@@ -7,12 +7,7 @@
#ifndef BSP_MINIWARE_PINS_H_
#define BSP_MINIWARE_PINS_H_
#if defined(MODEL_TS100) + defined(MODEL_TS80) > 1
#error "Multiple models defined!"
#elif defined(MODEL_TS100) + defined(MODEL_TS80) == 0
#error "No model defined!"
#endif
#include "Model_Config.h"
#ifdef MODEL_TS100
@@ -45,8 +40,8 @@
#define SCL_GPIO_Port GPIOB
#define SDA_Pin GPIO_PIN_7
#define SDA_GPIO_Port GPIOB
#else
#endif
#ifdef MODEL_TS80
// TS80 pin map
#define KEY_B_Pin GPIO_PIN_0
#define KEY_B_GPIO_Port GPIOB
@@ -77,6 +72,51 @@
#define SCL_GPIO_Port GPIOB
#define SDA_Pin GPIO_PIN_7
#define SDA_GPIO_Port GPIOB
#define SCL2_Pin GPIO_PIN_5
#define SCL2_GPIO_Port GPIOA
#define SDA2_Pin GPIO_PIN_1
#define SDA2_GPIO_Port GPIOA
#define INT_PD_Pin GPIO_PIN_9
#define INT_PD_GPIO_Port GPIOA
#endif
#ifdef MODEL_TS80P
// TS80P pin map
#define KEY_B_Pin GPIO_PIN_0
#define KEY_B_GPIO_Port GPIOB
#define TMP36_INPUT_Pin GPIO_PIN_4
#define TMP36_INPUT_GPIO_Port GPIOA
#define TMP36_ADC1_CHANNEL ADC_CHANNEL_4
#define TIP_TEMP_Pin GPIO_PIN_3
#define TIP_TEMP_GPIO_Port GPIOA
#define TIP_TEMP_ADC1_CHANNEL ADC_CHANNEL_3
#define TIP_TEMP_ADC2_CHANNEL ADC_CHANNEL_3
#define VIN_Pin GPIO_PIN_2
#define VIN_GPIO_Port GPIOA
#define VIN_ADC1_CHANNEL ADC_CHANNEL_2
#define VIN_ADC2_CHANNEL ADC_CHANNEL_2
#define OLED_RESET_Pin GPIO_PIN_15
#define OLED_RESET_GPIO_Port GPIOA
#define KEY_A_Pin GPIO_PIN_1
#define KEY_A_GPIO_Port GPIOB
#define INT_Orientation_Pin GPIO_PIN_4
#define INT_Orientation_GPIO_Port GPIOB
#define PWM_Out_Pin GPIO_PIN_6
#define PWM_Out_GPIO_Port GPIOA
#define PWM_Out_CHANNEL TIM_CHANNEL_1
#define INT_Movement_Pin GPIO_PIN_5
#define INT_Movement_GPIO_Port GPIOB
#define SCL_Pin GPIO_PIN_6
#define SCL_GPIO_Port GPIOB
#define SDA_Pin GPIO_PIN_7
#define SDA_GPIO_Port GPIOB
#define SCL2_Pin GPIO_PIN_5
#define SCL2_GPIO_Port GPIOA
#define SDA2_Pin GPIO_PIN_1
#define SDA2_GPIO_Port GPIOA
#define INT_PD_Pin GPIO_PIN_9
#define INT_PD_GPIO_Port GPIOA
#endif

View File

@@ -2,20 +2,35 @@
#include "BSP_Power.h"
#include "QC3.h"
#include "Settings.h"
void power_probe() {
// If TS80 probe for QC
// If TS100 - noop
#ifdef MODEL_TS80
startQC(systemSettings.voltageDiv);
seekQC((systemSettings.cutoutSetting) ? 120 : 90,
systemSettings.voltageDiv); // this will move the QC output to the preferred voltage to start with
#endif
}
#include "Pins.h"
#include "fusbpd.h"
#include "Model_Config.h"
#include "policy_engine.h"
#include "int_n.h"
bool FUSB302_present = false;
void power_check() {
#ifdef MODEL_TS80
#ifdef POW_PD
if (FUSB302_present) {
//Cant start QC until either PD works or fails
if (PolicyEngine::setupCompleteOrTimedOut() == false) {
return;
}
if (PolicyEngine::pdHasNegotiated()) {
return;
}
}
#endif
#ifdef POW_QC
QC_resync();
#endif
}
uint8_t usb_pd_detect() {
#ifdef POW_PD
FUSB302_present = fusb302_detect();
return FUSB302_present;
#endif
return false;
}

View File

@@ -9,6 +9,8 @@
#include "QC3.h"
#include "Settings.h"
#include "stm32f1xx_hal.h"
#include "Model_Config.h"
#ifdef POW_QC
void QC_DPlusZero_Six() {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET); // pull down D+
}
@@ -65,9 +67,9 @@ void QC_Post_Probe_En() {
}
uint8_t QC_DM_PulledDown() { return HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == GPIO_PIN_RESET ? 1 : 0; }
#endif
void QC_resync() {
#ifdef MODEL_TS80
#ifdef POW_QC
seekQC((systemSettings.cutoutSetting) ? 120 : 90,
systemSettings.voltageDiv); // Run the QC seek again if we have drifted too much
#endif

View File

@@ -35,7 +35,7 @@ void Setup_HAL() {
SystemClock_Config();
__HAL_AFIO_REMAP_SWJ_DISABLE()
;
// __HAL_AFIO_REMAP_SWJ_NOJTAG();
MX_GPIO_Init();
MX_DMA_Init();
MX_I2C1_Init();
@@ -458,3 +458,8 @@ static void MX_GPIO_Init(void) {
HAL_Delay(30);
HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_SET);
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line){
asm("bkpt");
}
#endif

View File

@@ -0,0 +1,27 @@
/*
* Software_I2C.h
*
* Created on: 25 Jul 2020
* Author: Ralim
*/
#ifndef BSP_MINIWARE_SOFTWARE_I2C_H_
#define BSP_MINIWARE_SOFTWARE_I2C_H_
#include "Model_Config.h"
#include "BSP.h"
#include "stm32f1xx_hal.h"
#ifdef I2C_SOFT
#define SOFT_SCL_HIGH() HAL_GPIO_WritePin(SCL2_GPIO_Port, SCL2_Pin, GPIO_PIN_SET)
#define SOFT_SCL_LOW() HAL_GPIO_WritePin(SCL2_GPIO_Port, SCL2_Pin, GPIO_PIN_RESET)
#define SOFT_SDA_HIGH() HAL_GPIO_WritePin(SDA2_GPIO_Port, SDA2_Pin, GPIO_PIN_SET)
#define SOFT_SDA_LOW() HAL_GPIO_WritePin(SDA2_GPIO_Port, SDA2_Pin, GPIO_PIN_RESET)
#define SOFT_SDA_READ() (HAL_GPIO_ReadPin(SDA2_GPIO_Port,SDA2_Pin)==GPIO_PIN_SET?1:0)
#define SOFT_SCL_READ() (HAL_GPIO_ReadPin(SCL2_GPIO_Port,SCL2_Pin)==GPIO_PIN_SET?1:0)
#define SOFT_I2C_DELAY() {for(int xx=0;xx<40;xx++){asm("nop");}}
#endif
#endif /* BSP_MINIWARE_SOFTWARE_I2C_H_ */

View File

@@ -1,18 +0,0 @@
/*
* UnitSettings.h
*
* Created on: 29 May 2020
* Author: Ralim
*/
#ifndef BSP_MINIWARE_UNITSETTINGS_H_
#define BSP_MINIWARE_UNITSETTINGS_H_
//On the TS80, the LIS accel is mounted backwards
#ifdef MODEL_TS80
#define LIS_ORI_FLIP
#endif
#endif /* BSP_MINIWARE_UNITSETTINGS_H_ */

View File

@@ -1041,32 +1041,32 @@ HAL_StatusTypeDef HAL_ADCEx_InjectedConfigChannel(ADC_HandleTypeDef* hadc, ADC_I
/* conversions is forced to 0x00 for alignment over all STM32 devices. */
/* - if scan mode is enabled, injected channels sequence length is set to */
/* parameter "InjectedNbrOfConversion". */
if (hadc->Init.ScanConvMode == ADC_SCAN_DISABLE)
{
if (sConfigInjected->InjectedRank == ADC_INJECTED_RANK_1)
{
/* Clear the old SQx bits for all injected ranks */
MODIFY_REG(hadc->Instance->JSQR ,
ADC_JSQR_JL |
ADC_JSQR_JSQ4 |
ADC_JSQR_JSQ3 |
ADC_JSQR_JSQ2 |
ADC_JSQR_JSQ1 ,
ADC_JSQR_RK_JL(sConfigInjected->InjectedChannel,
ADC_INJECTED_RANK_1,
0x01U));
}
/* If another injected rank than rank1 was intended to be set, and could */
/* not due to ScanConvMode disabled, error is reported. */
else
{
/* Update ADC state machine to error */
SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_CONFIG);
tmp_hal_status = HAL_ERROR;
}
}
else
// if (hadc->Init.ScanConvMode == ADC_SCAN_DISABLE)
// {
// if (sConfigInjected->InjectedRank == ADC_INJECTED_RANK_1)
// {
// /* Clear the old SQx bits for all injected ranks */
// MODIFY_REG(hadc->Instance->JSQR ,
// ADC_JSQR_JL |
// ADC_JSQR_JSQ4 |
// ADC_JSQR_JSQ3 |
// ADC_JSQR_JSQ2 |
// ADC_JSQR_JSQ1 ,
// ADC_JSQR_RK_JL(sConfigInjected->InjectedChannel,
// ADC_INJECTED_RANK_1,
// 0x01U));
// }
// /* If another injected rank than rank1 was intended to be set, and could */
// /* not due to ScanConvMode disabled, error is reported. */
// else
// {
// /* Update ADC state machine to error */
// SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_CONFIG);
//
// tmp_hal_status = HAL_ERROR;
// }
// }
// else
{
/* Since injected channels rank conv. order depends on total number of */
/* injected conversions, selected rank must be below or equal to total */

View File

@@ -3479,6 +3479,7 @@ void HAL_I2C_EV_IRQHandler(I2C_HandleTypeDef *hi2c)
}
}
/* Slave mode selected */
#if 0
else
{
/* ADDR set --------------------------------------------------------------*/
@@ -3520,6 +3521,7 @@ void HAL_I2C_EV_IRQHandler(I2C_HandleTypeDef *hi2c)
}
}
}
#endif
}
/**

View File

@@ -369,57 +369,6 @@ HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct)
assert_param(IS_RCC_OSCILLATORTYPE(RCC_OscInitStruct->OscillatorType));
/*------------------------------- HSE Configuration ------------------------*/
if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSE) == RCC_OSCILLATORTYPE_HSE)
{
/* Check the parameters */
assert_param(IS_RCC_HSE(RCC_OscInitStruct->HSEState));
/* When the HSE is used as system clock or clock source for PLL in these cases it is not allowed to be disabled */
if((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_SYSCLKSOURCE_STATUS_HSE)
|| ((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_SYSCLKSOURCE_STATUS_PLLCLK) && (__HAL_RCC_GET_PLL_OSCSOURCE() == RCC_PLLSOURCE_HSE)))
{
if((__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET) && (RCC_OscInitStruct->HSEState == RCC_HSE_OFF))
{
return HAL_ERROR;
}
}
else
{
/* Set the new HSE configuration ---------------------------------------*/
__HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState);
/* Check the HSE State */
if(RCC_OscInitStruct->HSEState != RCC_HSE_OFF)
{
/* Get Start Tick */
tickstart = HAL_GetTick();
/* Wait till HSE is ready */
while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)
{
if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
}
else
{
/* Get Start Tick */
tickstart = HAL_GetTick();
/* Wait till HSE is disabled */
while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET)
{
if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
}
}
}
/*----------------------------- HSI Configuration --------------------------*/
if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSI) == RCC_OSCILLATORTYPE_HSI)
{
@@ -486,121 +435,9 @@ HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct)
}
}
/*------------------------------ LSI Configuration -------------------------*/
if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_LSI) == RCC_OSCILLATORTYPE_LSI)
{
/* Check the parameters */
assert_param(IS_RCC_LSI(RCC_OscInitStruct->LSIState));
/* Check the LSI State */
if(RCC_OscInitStruct->LSIState != RCC_LSI_OFF)
{
/* Enable the Internal Low Speed oscillator (LSI). */
__HAL_RCC_LSI_ENABLE();
/* Get Start Tick */
tickstart = HAL_GetTick();
/* Wait till LSI is ready */
while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) == RESET)
{
if((HAL_GetTick() - tickstart ) > LSI_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
/* To have a fully stabilized clock in the specified range, a software delay of 1ms
should be added.*/
RCC_Delay(1);
}
else
{
/* Disable the Internal Low Speed oscillator (LSI). */
__HAL_RCC_LSI_DISABLE();
/* Get Start Tick */
tickstart = HAL_GetTick();
/* Wait till LSI is disabled */
while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) != RESET)
{
if((HAL_GetTick() - tickstart ) > LSI_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
}
}
/*------------------------------ LSE Configuration -------------------------*/
if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_LSE) == RCC_OSCILLATORTYPE_LSE)
{
FlagStatus pwrclkchanged = RESET;
/* Check the parameters */
assert_param(IS_RCC_LSE(RCC_OscInitStruct->LSEState));
/* Update LSE configuration in Backup Domain control register */
/* Requires to enable write access to Backup Domain of necessary */
if(__HAL_RCC_PWR_IS_CLK_DISABLED())
{
__HAL_RCC_PWR_CLK_ENABLE();
pwrclkchanged = SET;
}
if(HAL_IS_BIT_CLR(PWR->CR, PWR_CR_DBP))
{
/* Enable write access to Backup domain */
SET_BIT(PWR->CR, PWR_CR_DBP);
/* Wait for Backup domain Write protection disable */
tickstart = HAL_GetTick();
while(HAL_IS_BIT_CLR(PWR->CR, PWR_CR_DBP))
{
if((HAL_GetTick() - tickstart) > RCC_DBP_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
}
/* Set the new LSE configuration -----------------------------------------*/
__HAL_RCC_LSE_CONFIG(RCC_OscInitStruct->LSEState);
/* Check the LSE State */
if(RCC_OscInitStruct->LSEState != RCC_LSE_OFF)
{
/* Get Start Tick */
tickstart = HAL_GetTick();
/* Wait till LSE is ready */
while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET)
{
if((HAL_GetTick() - tickstart ) > RCC_LSE_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
}
else
{
/* Get Start Tick */
tickstart = HAL_GetTick();
/* Wait till LSE is disabled */
while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) != RESET)
{
if((HAL_GetTick() - tickstart ) > RCC_LSE_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
}
/* Require to disable power clock if necessary */
if(pwrclkchanged == SET)
{
__HAL_RCC_PWR_CLK_DISABLE();
}
}
#if defined(RCC_CR_PLL2ON)
/*-------------------------------- PLL2 Configuration -----------------------*/

View File

@@ -3968,129 +3968,129 @@ HAL_StatusTypeDef HAL_TIM_ConfigClockSource(TIM_HandleTypeDef *htim, TIM_ClockCo
}
break;
case TIM_CLOCKSOURCE_ETRMODE1:
{
/* Check whether or not the timer instance supports external trigger input mode 1 (ETRF)*/
assert_param(IS_TIM_CLOCKSOURCE_ETRMODE1_INSTANCE(htim->Instance));
/* Check ETR input conditioning related parameters */
assert_param(IS_TIM_CLOCKPRESCALER(sClockSourceConfig->ClockPrescaler));
assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
/* Configure the ETR Clock source */
TIM_ETR_SetConfig(htim->Instance,
sClockSourceConfig->ClockPrescaler,
sClockSourceConfig->ClockPolarity,
sClockSourceConfig->ClockFilter);
/* Get the TIMx SMCR register value */
tmpsmcr = htim->Instance->SMCR;
/* Reset the SMS and TS Bits */
tmpsmcr &= ~(TIM_SMCR_SMS | TIM_SMCR_TS);
/* Select the External clock mode1 and the ETRF trigger */
tmpsmcr |= (TIM_SLAVEMODE_EXTERNAL1 | TIM_CLOCKSOURCE_ETRMODE1);
/* Write to TIMx SMCR */
htim->Instance->SMCR = tmpsmcr;
}
break;
case TIM_CLOCKSOURCE_ETRMODE2:
{
/* Check whether or not the timer instance supports external trigger input mode 2 (ETRF)*/
assert_param(IS_TIM_CLOCKSOURCE_ETRMODE2_INSTANCE(htim->Instance));
/* Check ETR input conditioning related parameters */
assert_param(IS_TIM_CLOCKPRESCALER(sClockSourceConfig->ClockPrescaler));
assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
/* Configure the ETR Clock source */
TIM_ETR_SetConfig(htim->Instance,
sClockSourceConfig->ClockPrescaler,
sClockSourceConfig->ClockPolarity,
sClockSourceConfig->ClockFilter);
/* Enable the External clock mode2 */
htim->Instance->SMCR |= TIM_SMCR_ECE;
}
break;
case TIM_CLOCKSOURCE_TI1:
{
/* Check whether or not the timer instance supports external clock mode 1 */
assert_param(IS_TIM_CLOCKSOURCE_TIX_INSTANCE(htim->Instance));
/* Check TI1 input conditioning related parameters */
assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
TIM_TI1_ConfigInputStage(htim->Instance,
sClockSourceConfig->ClockPolarity,
sClockSourceConfig->ClockFilter);
TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_TI1);
}
break;
case TIM_CLOCKSOURCE_TI2:
{
/* Check whether or not the timer instance supports external clock mode 1 (ETRF)*/
assert_param(IS_TIM_CLOCKSOURCE_TIX_INSTANCE(htim->Instance));
/* Check TI2 input conditioning related parameters */
assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
TIM_TI2_ConfigInputStage(htim->Instance,
sClockSourceConfig->ClockPolarity,
sClockSourceConfig->ClockFilter);
TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_TI2);
}
break;
case TIM_CLOCKSOURCE_TI1ED:
{
/* Check whether or not the timer instance supports external clock mode 1 */
assert_param(IS_TIM_CLOCKSOURCE_TIX_INSTANCE(htim->Instance));
/* Check TI1 input conditioning related parameters */
assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
TIM_TI1_ConfigInputStage(htim->Instance,
sClockSourceConfig->ClockPolarity,
sClockSourceConfig->ClockFilter);
TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_TI1ED);
}
break;
case TIM_CLOCKSOURCE_ITR0:
{
/* Check whether or not the timer instance supports external clock mode 1 */
assert_param(IS_TIM_CLOCKSOURCE_ITRX_INSTANCE(htim->Instance));
TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_ITR0);
}
break;
case TIM_CLOCKSOURCE_ITR1:
{
/* Check whether or not the timer instance supports external clock mode 1 */
assert_param(IS_TIM_CLOCKSOURCE_ITRX_INSTANCE(htim->Instance));
TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_ITR1);
}
break;
case TIM_CLOCKSOURCE_ITR2:
{
/* Check whether or not the timer instance supports external clock mode 1 */
assert_param(IS_TIM_CLOCKSOURCE_ITRX_INSTANCE(htim->Instance));
TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_ITR2);
}
break;
case TIM_CLOCKSOURCE_ITR3:
{
/* Check whether or not the timer instance supports external clock mode 1 */
assert_param(IS_TIM_CLOCKSOURCE_ITRX_INSTANCE(htim->Instance));
TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_ITR3);
}
break;
// case TIM_CLOCKSOURCE_ETRMODE1:
// {
// /* Check whether or not the timer instance supports external trigger input mode 1 (ETRF)*/
// assert_param(IS_TIM_CLOCKSOURCE_ETRMODE1_INSTANCE(htim->Instance));
//
// /* Check ETR input conditioning related parameters */
// assert_param(IS_TIM_CLOCKPRESCALER(sClockSourceConfig->ClockPrescaler));
// assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
// assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
//
// /* Configure the ETR Clock source */
// TIM_ETR_SetConfig(htim->Instance,
// sClockSourceConfig->ClockPrescaler,
// sClockSourceConfig->ClockPolarity,
// sClockSourceConfig->ClockFilter);
// /* Get the TIMx SMCR register value */
// tmpsmcr = htim->Instance->SMCR;
// /* Reset the SMS and TS Bits */
// tmpsmcr &= ~(TIM_SMCR_SMS | TIM_SMCR_TS);
// /* Select the External clock mode1 and the ETRF trigger */
// tmpsmcr |= (TIM_SLAVEMODE_EXTERNAL1 | TIM_CLOCKSOURCE_ETRMODE1);
// /* Write to TIMx SMCR */
// htim->Instance->SMCR = tmpsmcr;
// }
// break;
//
// case TIM_CLOCKSOURCE_ETRMODE2:
// {
// /* Check whether or not the timer instance supports external trigger input mode 2 (ETRF)*/
// assert_param(IS_TIM_CLOCKSOURCE_ETRMODE2_INSTANCE(htim->Instance));
//
// /* Check ETR input conditioning related parameters */
// assert_param(IS_TIM_CLOCKPRESCALER(sClockSourceConfig->ClockPrescaler));
// assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
// assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
//
// /* Configure the ETR Clock source */
// TIM_ETR_SetConfig(htim->Instance,
// sClockSourceConfig->ClockPrescaler,
// sClockSourceConfig->ClockPolarity,
// sClockSourceConfig->ClockFilter);
// /* Enable the External clock mode2 */
// htim->Instance->SMCR |= TIM_SMCR_ECE;
// }
// break;
//
// case TIM_CLOCKSOURCE_TI1:
// {
// /* Check whether or not the timer instance supports external clock mode 1 */
// assert_param(IS_TIM_CLOCKSOURCE_TIX_INSTANCE(htim->Instance));
//
// /* Check TI1 input conditioning related parameters */
// assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
// assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
//
// TIM_TI1_ConfigInputStage(htim->Instance,
// sClockSourceConfig->ClockPolarity,
// sClockSourceConfig->ClockFilter);
// TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_TI1);
// }
// break;
// case TIM_CLOCKSOURCE_TI2:
// {
// /* Check whether or not the timer instance supports external clock mode 1 (ETRF)*/
// assert_param(IS_TIM_CLOCKSOURCE_TIX_INSTANCE(htim->Instance));
//
// /* Check TI2 input conditioning related parameters */
// assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
// assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
//
// TIM_TI2_ConfigInputStage(htim->Instance,
// sClockSourceConfig->ClockPolarity,
// sClockSourceConfig->ClockFilter);
// TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_TI2);
// }
// break;
// case TIM_CLOCKSOURCE_TI1ED:
// {
// /* Check whether or not the timer instance supports external clock mode 1 */
// assert_param(IS_TIM_CLOCKSOURCE_TIX_INSTANCE(htim->Instance));
//
// /* Check TI1 input conditioning related parameters */
// assert_param(IS_TIM_CLOCKPOLARITY(sClockSourceConfig->ClockPolarity));
// assert_param(IS_TIM_CLOCKFILTER(sClockSourceConfig->ClockFilter));
//
// TIM_TI1_ConfigInputStage(htim->Instance,
// sClockSourceConfig->ClockPolarity,
// sClockSourceConfig->ClockFilter);
// TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_TI1ED);
// }
// break;
// case TIM_CLOCKSOURCE_ITR0:
// {
// /* Check whether or not the timer instance supports external clock mode 1 */
// assert_param(IS_TIM_CLOCKSOURCE_ITRX_INSTANCE(htim->Instance));
//
// TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_ITR0);
// }
// break;
// case TIM_CLOCKSOURCE_ITR1:
// {
// /* Check whether or not the timer instance supports external clock mode 1 */
// assert_param(IS_TIM_CLOCKSOURCE_ITRX_INSTANCE(htim->Instance));
//
// TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_ITR1);
// }
// break;
// case TIM_CLOCKSOURCE_ITR2:
// {
// /* Check whether or not the timer instance supports external clock mode 1 */
// assert_param(IS_TIM_CLOCKSOURCE_ITRX_INSTANCE(htim->Instance));
//
// TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_ITR2);
// }
// break;
// case TIM_CLOCKSOURCE_ITR3:
// {
// /* Check whether or not the timer instance supports external clock mode 1 */
// assert_param(IS_TIM_CLOCKSOURCE_ITRX_INSTANCE(htim->Instance));
//
// TIM_ITRx_SetConfig(htim->Instance, TIM_CLOCKSOURCE_ITR3);
// }
// break;
default:
break;

View File

@@ -0,0 +1,270 @@
/*
* 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 "fusb302b.h"
#include "I2CBB.hpp"
#include <pd.h>
#include "int_n.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();
}
void fusb_setup() {
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
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, 12, 0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
if (!I2CBB::lock2()) {
return;
}
}
/* Fully reset the FUSB302B */
fusb_write_byte( FUSB_RESET, FUSB_RESET_SW_RES);
osDelay(2);
if (!fusb_read_id()) {
return;
}
/* 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);
}
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
I2CBB::unlock2();
}
fusb_reset();
}
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);
/* Reset the PD logic */
// fusb_write_byte( FUSB_RESET, FUSB_RESET_PD_RESET);
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

@@ -8,7 +8,12 @@
#include "stdlib.h"
#include "task.h"
#include "I2C_Wrapper.hpp"
#include "fusbpd.h"
void postRToSInit() {
// Any after RTos setup
FRToSI2C::FRToSInit();
#ifdef POW_PD
//Spawn all of the USB-C processors
fusb302_start_processing();
#endif
}

View File

@@ -9,6 +9,9 @@
#include "BSP.h"
#include "Setup.h"
#include "Pins.h"
#include "I2CBB.hpp"
#include "fusbpd.h"
#include "Model_Config.h"
void preRToSInit() {
/* Reset of all peripherals, Initializes the Flash interface and the Systick.
*/
@@ -18,5 +21,9 @@ void preRToSInit() {
HAL_Delay(50);
HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_SET);
HAL_Delay(50);
#ifdef I2C_SOFT
I2CBB::init();
#endif
}

View File

@@ -82,3 +82,6 @@ void DMA1_Channel6_IRQHandler(void) {
void DMA1_Channel7_IRQHandler(void) {
HAL_DMA_IRQHandler(&hdma_i2c1_rx);
}
void EXTI9_5_IRQHandler(void){
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);
}

View File

@@ -11,6 +11,7 @@
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
/* #define DATA_IN_ExtSRAM */
#endif /* STM32F100xE || STM32F101xE || STM32F101xG || STM32F103xE || STM32F103xG */
//#define LOCAL_BUILD
#ifndef LOCAL_BUILD
#define VECT_TAB_OFFSET 0x00004000U /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */

View File

@@ -24,7 +24,7 @@ ButtonState getButtonState() {
*/
static uint8_t previousState = 0;
static uint32_t previousStateChange = 0;
const uint16_t timeout = 40;
const uint16_t timeout = 400;
uint8_t currentState;
currentState = (getButtonA()) << 0;
currentState |= (getButtonB()) << 1;

View File

@@ -0,0 +1,305 @@
/*
* 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_FUSB302B_H
#define PDB_FUSB302B_H
#include <stdint.h>
#include "pd.h"
#include <pdb_msg.h>
/* I2C addresses of the FUSB302B chips */
#define FUSB302B_ADDR (0x22<<1)
#define FUSB302B01_ADDR (0x23<<1)
#define FUSB302B10_ADDR (0x24<<1)
#define FUSB302B11_ADDR (0x25<<1)
/* Device ID register */
#define FUSB_DEVICE_ID 0x01
#define FUSB_DEVICE_ID_VERSION_ID_SHIFT 4
#define FUSB_DEVICE_ID_VERSION_ID (0xF << FUSB_DEVICE_ID_VERSION_ID_SHIFT)
#define FUSB_DEVICE_ID_PRODUCT_ID_SHIFT 2
#define FUSB_DEVICE_ID_PRODUCT_ID (0x3 << FUSB_DEVICE_ID_PRODUCT_ID_SHIFT)
#define FUSB_DEVICE_ID_REVISION_ID_SHIFT 0
#define FUSB_DEVICE_ID_REVISION_ID (0x3 << FUSB_DEVICE_ID_REVISION_ID_SHIFT)
/* Switches0 register */
#define FUSB_SWITCHES0 0x02
#define FUSB_SWITCHES0_PU_EN2 (1 << 7)
#define FUSB_SWITCHES0_PU_EN1 (1 << 6)
#define FUSB_SWITCHES0_VCONN_CC2 (1 << 5)
#define FUSB_SWITCHES0_VCONN_CC1 (1 << 4)
#define FUSB_SWITCHES0_MEAS_CC2 (1 << 3)
#define FUSB_SWITCHES0_MEAS_CC1 (1 << 2)
#define FUSB_SWITCHES0_PDWN_2 (1 << 1)
#define FUSB_SWITCHES0_PDWN_1 1
/* Switches1 register */
#define FUSB_SWITCHES1 0x03
#define FUSB_SWITCHES1_POWERROLE (1 << 7)
#define FUSB_SWITCHES1_SPECREV_SHIFT 5
#define FUSB_SWITCHES1_SPECREV (0x3 << FUSB_SWITCHES1_SPECREV_SHIFT)
#define FUSB_SWITCHES1_DATAROLE (1 << 4)
#define FUSB_SWITCHES1_AUTO_CRC (1 << 2)
#define FUSB_SWITCHES1_TXCC2 (1 << 1)
#define FUSB_SWITCHES1_TXCC1 1
/* Measure register */
#define FUSB_MEASURE 0x04
#define FUSB_MEASURE_MEAS_VBUS (1 << 6)
#define FUSB_MEASURE_MDAC_SHIFT 0
#define FUSB_MEASURE_MDAC (0x3F << FUSB_MEASURE_MDAC_SHIFT)
/* Slice register */
#define FUSB_SLICE 0x05
#define FUSB_SLICE_SDAC_HYS_SHIFT 6
#define FUSB_SLICE_SDAC_HYS (0x3 << FUSB_SLICE_SDAC_HYS_SHIFT)
#define FUSB_SLICE_SDAC_SHIFT 0
#define FUSB_SLICE_SDAC (0x3F << FUSB_SLICE_SDAC_SHIFT)
/* Control0 register */
#define FUSB_CONTROL0 0x06
#define FUSB_CONTROL0_TX_FLUSH (1 << 6)
#define FUSB_CONTROL0_INT_MASK (1 << 5)
#define FUSB_CONTROL0_HOST_CUR_SHIFT 2
#define FUSB_CONTROL0_HOST_CUR (0x3 << FUSB_CONTROL0_HOST_CUR_SHIFT)
#define FUSB_CONTROL0_AUTO_PRE (1 << 1)
#define FUSB_CONTROL0_TX_START 1
/* Control1 register */
#define FUSB_CONTROL1 0x07
#define FUSB_CONTROL1_ENSOP2DB (1 << 6)
#define FUSB_CONTROL1_ENSOP1DB (1 << 5)
#define FUSB_CONTROL1_BIST_MODE2 (1 << 4)
#define FUSB_CONTROL1_RX_FLUSH (1 << 2)
#define FUSB_CONTROL1_ENSOP2 (1 << 1)
#define FUSB_CONTROL1_ENSOP1 1
/* Control2 register */
#define FUSB_CONTROL2 0x08
#define FUSB_CONTROL2_TOG_SAVE_PWR_SHIFT 6
#define FUSB_CONTROL2_TOG_SAVE_PWR (0x3 << FUSB_CONTROL2_TOG_SAVE_PWR)
#define FUSB_CONTROL2_TOG_RD_ONLY (1 << 5)
#define FUSB_CONTROL2_WAKE_EN (1 << 3)
#define FUSB_CONTROL2_MODE_SHIFT 1
#define FUSB_CONTROL2_MODE (0x3 << FUSB_CONTROL2_MODE_SHIFT)
#define FUSB_CONTROL2_TOGGLE 1
/* Control3 register */
#define FUSB_CONTROL3 0x09
#define FUSB_CONTROL3_SEND_HARD_RESET (1 << 6)
#define FUSB_CONTROL3_BIST_TMODE (1 << 5)
#define FUSB_CONTROL3_AUTO_HARDRESET (1 << 4)
#define FUSB_CONTROL3_AUTO_SOFTRESET (1 << 3)
#define FUSB_CONTROL3_N_RETRIES_SHIFT 1
#define FUSB_CONTROL3_N_RETRIES (0x3 << FUSB_CONTROL3_N_RETRIES_SHIFT)
#define FUSB_CONTROL3_AUTO_RETRY 1
/* Mask1 register */
#define FUSB_MASK1 0x0A
#define FUSB_MASK1_M_VBUSOK (1 << 7)
#define FUSB_MASK1_M_ACTIVITY (1 << 6)
#define FUSB_MASK1_M_COMP_CHNG (1 << 5)
#define FUSB_MASK1_M_CRC_CHK (1 << 4)
#define FUSB_MASK1_M_ALERT (1 << 3)
#define FUSB_MASK1_M_WAKE (1 << 2)
#define FUSB_MASK1_M_COLLISION (1 << 1)
#define FUSB_MASK1_M_BC_LVL (1 << 0)
/* Power register */
#define FUSB_POWER 0x0B
#define FUSB_POWER_PWR3 (1 << 3)
#define FUSB_POWER_PWR2 (1 << 2)
#define FUSB_POWER_PWR1 (1 << 1)
#define FUSB_POWER_PWR0 1
/* Reset register */
#define FUSB_RESET 0x0C
#define FUSB_RESET_PD_RESET (1 << 1)
#define FUSB_RESET_SW_RES 1
/* OCPreg register */
#define FUSB_OCPREG 0x0D
#define FUSB_OCPREG_OCP_RANGE (1 << 3)
#define FUSB_OCPREG_OCP_CUR_SHIFT 0
#define FUSB_OCPREG_OCP_CUR (0x7 << FUSB_OCPREG_OCP_CUR_SHIFT)
/* Maska register */
#define FUSB_MASKA 0x0E
#define FUSB_MASKA_M_OCP_TEMP (1 << 7)
#define FUSB_MASKA_M_TOGDONE (1 << 6)
#define FUSB_MASKA_M_SOFTFAIL (1 << 5)
#define FUSB_MASKA_M_RETRYFAIL (1 << 4)
#define FUSB_MASKA_M_HARDSENT (1 << 3)
#define FUSB_MASKA_M_TXSENT (1 << 2)
#define FUSB_MASKA_M_SOFTRST (1 << 1)
#define FUSB_MASKA_M_HARDRST 1
/* Maskb register */
#define FUSB_MASKB 0x0F
#define FUSB_MASKB_M_GCRCSENT 1
/* Control4 register */
#define FUSB_CONTROL4 0x10
#define FUSB_CONTROL4_TOG_EXIT_AUD 1
/* Status0a register */
#define FUSB_STATUS0A 0x3C
#define FUSB_STATUS0A_SOFTFAIL (1 << 5)
#define FUSB_STATUS0A_RETRYFAIL (1 << 4)
#define FUSB_STATUS0A_POWER3 (1 << 3)
#define FUSB_STATUS0A_POWER2 (1 << 2)
#define FUSB_STATUS0A_SOFTRST (1 << 1)
#define FUSB_STATUS0A_HARDRST 1
/* Status1a register */
#define FUSB_STATUS1A 0x3D
#define FUSB_STATUS1A_TOGSS_SHIFT 3
#define FUSB_STATUS1A_TOGSS (0x7 << FUSB_STATUS1A_TOGSS_SHIFT)
#define FUSB_STATUS1A_RXSOP2DB (1 << 2)
#define FUSB_STATUS1A_RXSOP1DB (1 << 1)
#define FUSB_STATUS1A_RXSOP 1
/* Interrupta register */
#define FUSB_INTERRUPTA 0x3E
#define FUSB_INTERRUPTA_I_OCP_TEMP (1 << 7)
#define FUSB_INTERRUPTA_I_TOGDONE (1 << 6)
#define FUSB_INTERRUPTA_I_SOFTFAIL (1 << 5)
#define FUSB_INTERRUPTA_I_RETRYFAIL (1 << 4)
#define FUSB_INTERRUPTA_I_HARDSENT (1 << 3)
#define FUSB_INTERRUPTA_I_TXSENT (1 << 2)
#define FUSB_INTERRUPTA_I_SOFTRST (1 << 1)
#define FUSB_INTERRUPTA_I_HARDRST 1
/* Interruptb register */
#define FUSB_INTERRUPTB 0x3F
#define FUSB_INTERRUPTB_I_GCRCSENT 1
/* Status0 register */
#define FUSB_STATUS0 0x40
#define FUSB_STATUS0_VBUSOK (1 << 7)
#define FUSB_STATUS0_ACTIVITY (1 << 6)
#define FUSB_STATUS0_COMP (1 << 5)
#define FUSB_STATUS0_CRC_CHK (1 << 4)
#define FUSB_STATUS0_ALERT (1 << 3)
#define FUSB_STATUS0_WAKE (1 << 2)
#define FUSB_STATUS0_BC_LVL_SHIFT 0
#define FUSB_STATUS0_BC_LVL (0x3 << FUSB_STATUS0_BC_LVL_SHIFT)
/* Status1 register */
#define FUSB_STATUS1 0x41
#define FUSB_STATUS1_RXSOP2 (1 << 7)
#define FUSB_STATUS1_RXSOP1 (1 << 6)
#define FUSB_STATUS1_RX_EMPTY (1 << 5)
#define FUSB_STATUS1_RX_FULL (1 << 4)
#define FUSB_STATUS1_TX_EMPTY (1 << 3)
#define FUSB_STATUS1_TX_FULL (1 << 2)
#define FUSB_STATUS1_OVRTEMP (1 << 1)
#define FUSB_STATUS1_OCP 1
/* Interrupt register */
#define FUSB_INTERRUPT 0x42
#define FUSB_INTERRUPT_I_VBUSOK (1 << 7)
#define FUSB_INTERRUPT_I_ACTIVITY (1 << 6)
#define FUSB_INTERRUPT_I_COMP_CHNG (1 << 5)
#define FUSB_INTERRUPT_I_CRC_CHK (1 << 4)
#define FUSB_INTERRUPT_I_ALERT (1 << 3)
#define FUSB_INTERRUPT_I_WAKE (1 << 2)
#define FUSB_INTERRUPT_I_COLLISION (1 << 1)
#define FUSB_INTERRUPT_I_BC_LVL 1
/* FIFOs register */
#define FUSB_FIFOS 0x43
#define FUSB_FIFO_TX_TXON 0xA1
#define FUSB_FIFO_TX_SOP1 0x12
#define FUSB_FIFO_TX_SOP2 0x13
#define FUSB_FIFO_TX_SOP3 0x1B
#define FUSB_FIFO_TX_RESET1 0x15
#define FUSB_FIFO_TX_RESET2 0x16
#define FUSB_FIFO_TX_PACKSYM 0x80
#define FUSB_FIFO_TX_JAM_CRC 0xFF
#define FUSB_FIFO_TX_EOP 0x14
#define FUSB_FIFO_TX_TXOFF 0xFE
#define FUSB_FIFO_RX_TOKEN_BITS 0xE0
#define FUSB_FIFO_RX_SOP 0xE0
#define FUSB_FIFO_RX_SOP1 0xC0
#define FUSB_FIFO_RX_SOP2 0xA0
#define FUSB_FIFO_RX_SOP1DB 0x80
#define FUSB_FIFO_RX_SOP2DB 0x60
/*
* FUSB status union
*
* Provides a nicer structure than just an array of uint8_t for working with
* the FUSB302B status and interrupt flags.
*/
union fusb_status {
uint8_t bytes[7];
struct {
uint8_t status0a;
uint8_t status1a;
uint8_t interrupta;
uint8_t interruptb;
uint8_t status0;
uint8_t status1;
uint8_t interrupt;
};
};
/* FUSB functions */
/*
* Send a USB Power Delivery message to the FUSB302B
*/
void fusb_send_message(const union pd_msg *msg);
/*
* Read a USB Power Delivery message from the FUSB302B
*/
uint8_t fusb_read_message(union pd_msg *msg);
/*
* Tell the FUSB302B to send a hard reset signal
*/
void fusb_send_hardrst();
/*
* Read the FUSB302B status and interrupt flags into *status
*/
void fusb_get_status(union fusb_status *status);
/*
* Read the FUSB302B BC_LVL as an enum fusb_typec_current
*/
enum fusb_typec_current fusb_get_typec_current();
/*
* Initialization routine for the FUSB302B
*/
void fusb_setup();
/*
* Reset the FUSB302B
*/
void fusb_reset();
bool fusb_read_id();
#endif /* PDB_FUSB302B_H */

View File

@@ -0,0 +1,38 @@
/*
* fusbpd.cpp
*
* Created on: 13 Jun 2020
* Author: Ralim
*/
#include "Model_Config.h"
#ifdef POW_PD
#include <fusbpd.h>
#include <pd.h>
#include "BSP.h"
#include "I2CBB.hpp"
#include "fusb302b.h"
#include "policy_engine.h"
#include "protocol_rx.h"
#include "protocol_tx.h"
#include "int_n.h"
#include "hard_reset.h"
void fusb302_start_processing() {
/* Initialize the FUSB302B */
resetWatchdog();
fusb_setup();
resetWatchdog();
/* Create the policy engine thread. */
PolicyEngine::init();
/* Create the protocol layer threads. */
ProtocolReceive::init();
ProtocolTransmit::init();
ResetHandler::init();
resetWatchdog();
/* Create the INT_N thread. */
InterruptHandler::init();
}
#endif

View File

@@ -0,0 +1,18 @@
/*
* fusbpd.h
*
* Created on: 13 Jun 2020
* Author: Ralim
*/
#ifndef DRIVERS_FUSB302_FUSBPD_H_
#define DRIVERS_FUSB302_FUSBPD_H_
//Wrapper for all of the FUSB302 PD work
extern struct pdb_config pdb_config_data;
#include <stdint.h>
//returns 1 if the FUSB302 is on the I2C bus
uint8_t fusb302_detect();
void fusb302_start_processing();
#endif /* DRIVERS_FUSB302_FUSBPD_H_ */

View File

@@ -0,0 +1,152 @@
/*
* 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 "hard_reset.h"
#include "fusbpd.h"
#include <pd.h>
#include "policy_engine.h"
#include "protocol_rx.h"
#include "protocol_tx.h"
#include "fusb302b.h"
osThreadId ResetHandler::TaskHandle = NULL;
uint32_t ResetHandler::TaskBuffer[ResetHandler::TaskStackSize];
osStaticThreadDef_t ResetHandler::TaskControlBlock;
/*
* PRL_HR_Reset_Layer state
*/
ResetHandler::hardrst_state ResetHandler::hardrst_reset_layer() {
/* First, wait for the signal to run a hard reset. */
eventmask_t evt = waitForEvent(
PDB_EVT_HARDRST_RESET | PDB_EVT_HARDRST_I_HARDRST);
if (evt & (PDB_EVT_HARDRST_RESET | PDB_EVT_HARDRST_I_HARDRST)) {
/* Reset the Protocol RX machine */
ProtocolReceive::notify( PDB_EVT_PRLRX_RESET);
taskYIELD();
/* Reset the Protocol TX machine */
ProtocolTransmit::notify(
ProtocolTransmit::Notifications::PDB_EVT_PRLTX_RESET);
taskYIELD();
/* Continue the process based on what event started the reset. */
if (evt & PDB_EVT_HARDRST_RESET) {
/* Policy Engine started the reset. */
return PRLHRRequestHardReset;
} else {
/* PHY started the reset */
return PRLHRIndicateHardReset;
}
} else {
return PRLHRResetLayer;
}
}
ResetHandler::hardrst_state ResetHandler::hardrst_indicate_hard_reset() {
/* Tell the PE that we're doing a hard reset */
PolicyEngine::notify( PDB_EVT_PE_RESET);
return PRLHRWaitPE;
}
ResetHandler::hardrst_state ResetHandler::hardrst_request_hard_reset() {
/* Tell the PHY to send a hard reset */
fusb_send_hardrst();
return PRLHRWaitPHY;
}
ResetHandler::hardrst_state ResetHandler::hardrst_wait_phy() {
/* Wait for the PHY to tell us that it's done sending the hard reset */
waitForEvent(PDB_EVT_HARDRST_I_HARDSENT, PD_T_HARD_RESET_COMPLETE);
/* Move on no matter what made us stop waiting. */
return PRLHRHardResetRequested;
}
ResetHandler::hardrst_state ResetHandler::hardrst_hard_reset_requested() {
/* Tell the PE that the hard reset was sent */
PolicyEngine::notify( PDB_EVT_PE_HARD_SENT);
return PRLHRWaitPE;
}
ResetHandler::hardrst_state ResetHandler::hardrst_wait_pe() {
/* Wait for the PE to tell us that it's done */
waitForEvent(PDB_EVT_HARDRST_DONE);
return PRLHRComplete;
}
ResetHandler::hardrst_state ResetHandler::hardrst_complete() {
/* I'm not aware of anything we have to tell the FUSB302B, so just finish
* the reset routine. */
return PRLHRResetLayer;
}
void ResetHandler::init() {
osThreadStaticDef(rstHand, Thread, PDB_PRIO_PRL, 0, TaskStackSize,
TaskBuffer, &TaskControlBlock);
TaskHandle = osThreadCreate(osThread(rstHand), NULL);
}
void ResetHandler::notify(uint32_t notification) {
if (TaskHandle != NULL) {
xTaskNotify(TaskHandle, notification, eNotifyAction::eSetBits);
}
}
void ResetHandler::Thread(const void *arg) {
(void) arg;
ResetHandler::hardrst_state state = PRLHRResetLayer;
while (true) {
switch (state) {
case PRLHRResetLayer:
state = hardrst_reset_layer();
break;
case PRLHRIndicateHardReset:
state = hardrst_indicate_hard_reset();
break;
case PRLHRRequestHardReset:
state = hardrst_request_hard_reset();
break;
case PRLHRWaitPHY:
state = hardrst_wait_phy();
break;
case PRLHRHardResetRequested:
state = hardrst_hard_reset_requested();
break;
case PRLHRWaitPE:
state = hardrst_wait_pe();
break;
case PRLHRComplete:
state = hardrst_complete();
break;
default:
/* This is an error. It really shouldn't happen. We might
* want to handle it anyway, though. */
state = PRLHRResetLayer;
break;
}
}
}
uint32_t ResetHandler::waitForEvent(uint32_t mask, uint32_t ticksToWait) {
uint32_t pulNotificationValue;
xTaskNotifyWait(0x00, mask, &pulNotificationValue, ticksToWait);
return pulNotificationValue;
}

View File

@@ -0,0 +1,63 @@
/*
* 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_HARD_RESET_H
#define PDB_HARD_RESET_H
#include <pd.h>
/* Events for the Hard Reset thread */
#define PDB_EVT_HARDRST_RESET EVENT_MASK(0)
#define PDB_EVT_HARDRST_I_HARDRST EVENT_MASK(1)
#define PDB_EVT_HARDRST_I_HARDSENT EVENT_MASK(2)
#define PDB_EVT_HARDRST_DONE EVENT_MASK(3)
class ResetHandler {
public:
static void init();
static void notify(uint32_t notification);
private:
static void Thread(const void *arg);
static osThreadId TaskHandle;
static const size_t TaskStackSize = 1536 / 2;
static uint32_t TaskBuffer[TaskStackSize];
static osStaticThreadDef_t TaskControlBlock;
static uint32_t waitForEvent(uint32_t mask, uint32_t ticksToWait =
portMAX_DELAY);
/*
* Hard Reset machine states
*/
enum hardrst_state {
PRLHRResetLayer,
PRLHRIndicateHardReset,
PRLHRRequestHardReset,
PRLHRWaitPHY,
PRLHRHardResetRequested,
PRLHRWaitPE,
PRLHRComplete
};
static hardrst_state hardrst_reset_layer();
static hardrst_state hardrst_indicate_hard_reset();
static hardrst_state hardrst_request_hard_reset();
static hardrst_state hardrst_wait_phy();
static hardrst_state hardrst_hard_reset_requested();
static hardrst_state hardrst_wait_pe();
static hardrst_state hardrst_complete();
};
#endif /* PDB_HARD_RESET_H */

View File

@@ -0,0 +1,108 @@
/*
* 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 "int_n.h"
#include "fusbpd.h"
#include <pd.h>
#include "fusb302b.h"
#include "protocol_rx.h"
#include "protocol_tx.h"
#include "hard_reset.h"
#include "policy_engine.h"
#include "protocol_rx.h"
#include "protocol_tx.h"
#include "task.h"
#include "BSP.h"
osThreadId InterruptHandler::TaskHandle=NULL;
uint32_t InterruptHandler::TaskBuffer[InterruptHandler::TaskStackSize];
osStaticThreadDef_t InterruptHandler::TaskControlBlock;
void InterruptHandler::init() {
osThreadStaticDef(intTask, Thread, PDB_PRIO_PRL_INT_N, 0, TaskStackSize,
TaskBuffer, &TaskControlBlock);
TaskHandle = osThreadCreate(osThread(intTask), NULL);
}
void InterruptHandler::Thread(const void *arg) {
(void) arg;
union fusb_status status;
volatile uint32_t events;
bool notifSent = false;
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);
}
notifSent = false;
/* Read the FUSB302B status and interrupt registers */
fusb_get_status(&status);
/* If the I_TXSENT or I_RETRYFAIL flag is set, tell the Protocol TX
* thread */
if (status.interrupta & FUSB_INTERRUPTA_I_TXSENT) {
ProtocolTransmit::notify(
ProtocolTransmit::Notifications::PDB_EVT_PRLTX_I_TXSENT);
notifSent = true;
}
if (status.interrupta & FUSB_INTERRUPTA_I_RETRYFAIL) {
ProtocolTransmit::notify(
ProtocolTransmit::Notifications::PDB_EVT_PRLTX_I_RETRYFAIL);
notifSent = true;
}
/* 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);
notifSent = true;
}
/* If the I_HARDRST or I_HARDSENT flag is set, tell the Hard Reset
* thread */
if (notifSent == false) {
events = 0;
if (status.interrupta & FUSB_INTERRUPTA_I_HARDRST) {
events |= PDB_EVT_HARDRST_I_HARDRST;
notifSent = true;
} else if (status.interrupta & FUSB_INTERRUPTA_I_HARDSENT) {
events |= PDB_EVT_HARDRST_I_HARDSENT;
notifSent = true;
}
if (events) {
ResetHandler::notify(events);
}
}
/* 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);
notifSent = true;
}
}
}
void InterruptHandler::irqCallback() {
if (TaskHandle != NULL) {
BaseType_t taskWoke = pdFALSE;
xTaskNotifyFromISR(TaskHandle, 0x01, eNotifyAction::eSetBits,
&taskWoke);
portYIELD_FROM_ISR(taskWoke);
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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_INT_N_OLD_H
#define PDB_INT_N_OLD_H
#include <pd.h>
class InterruptHandler {
public:
//Creates the thread to handle the Interrupt pin
static void init();
static void irqCallback();
private:
static void Thread(const void *arg);
static osThreadId TaskHandle;
static const size_t TaskStackSize = 1536 / 3;
static uint32_t TaskBuffer[TaskStackSize];
static osStaticThreadDef_t TaskControlBlock;
/*
* Hard Reset machine states
*/
enum hardrst_state {
PRLHRResetLayer,
PRLHRIndicateHardReset,
PRLHRRequestHardReset,
PRLHRWaitPHY,
PRLHRHardResetRequested,
PRLHRWaitPE,
PRLHRComplete
};
static enum hardrst_state hardrst_reset_layer();
static enum hardrst_state hardrst_indicate_hard_reset();
static enum hardrst_state hardrst_request_hard_reset();
static enum hardrst_state hardrst_wait_phy();
static enum hardrst_state hardrst_hard_reset_requested();
static enum hardrst_state hardrst_wait_pe();
static enum hardrst_state hardrst_complete();
};
#endif /* PDB_INT_N_OLD_H */

View File

@@ -0,0 +1,400 @@
/*
* 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_PD_H
#define PDB_PD_H
#include <stdint.h>
#include "FreeRTOS.h"
#include "pdb_msg.h"
#include "cmsis_os.h"
#include "pdb_conf.h"
/*
* Macros for working with USB Power Delivery messages.
*
* This file is mostly written from the PD Rev. 2.0 spec, but the header is
* written from the Rev. 3.0 spec.
*/
/*
* PD Header
*/
#define PD_HDR_MSGTYPE_SHIFT 0
#define PD_HDR_MSGTYPE (0x1F << PD_HDR_MSGTYPE_SHIFT)
#define PD_HDR_DATAROLE_SHIFT 5
#define PD_HDR_DATAROLE (0x1 << PD_HDR_DATAROLE_SHIFT)
#define PD_HDR_SPECREV_SHIFT 6
#define PD_HDR_SPECREV (0x3 << PD_HDR_SPECREV_SHIFT)
#define PD_HDR_POWERROLE_SHIFT 8
#define PD_HDR_POWERROLE (1 << PD_HDR_POWERROLE_SHIFT)
#define PD_HDR_MESSAGEID_SHIFT 9
#define PD_HDR_MESSAGEID (0x7 << PD_HDR_MESSAGEID_SHIFT)
#define PD_HDR_NUMOBJ_SHIFT 12
#define PD_HDR_NUMOBJ (0x7 << PD_HDR_NUMOBJ_SHIFT)
#define PD_HDR_EXT (1 << 15)
/* Message types */
#define PD_MSGTYPE_GET(msg) (((msg)->hdr & PD_HDR_MSGTYPE) >> PD_HDR_MSGTYPE_SHIFT)
/* Control Message */
#define PD_MSGTYPE_GOODCRC 0x01
#define PD_MSGTYPE_GOTOMIN 0x02
#define PD_MSGTYPE_ACCEPT 0x03
#define PD_MSGTYPE_REJECT 0x04
#define PD_MSGTYPE_PING 0x05
#define PD_MSGTYPE_PS_RDY 0x06
#define PD_MSGTYPE_GET_SOURCE_CAP 0x07
#define PD_MSGTYPE_GET_SINK_CAP 0x08
#define PD_MSGTYPE_DR_SWAP 0x09
#define PD_MSGTYPE_PR_SWAP 0x0A
#define PD_MSGTYPE_VCONN_SWAP 0x0B
#define PD_MSGTYPE_WAIT 0x0C
#define PD_MSGTYPE_SOFT_RESET 0x0D
#define PD_MSGTYPE_NOT_SUPPORTED 0x10
#define PD_MSGTYPE_GET_SOURCE_CAP_EXTENDED 0x11
#define PD_MSGTYPE_GET_STATUS 0x12
#define PD_MSGTYPE_FR_SWAP 0x13
#define PD_MSGTYPE_GET_PPS_STATUS 0x14
#define PD_MSGTYPE_GET_COUNTRY_CODES 0x15
/* Data Message */
#define PD_MSGTYPE_SOURCE_CAPABILITIES 0x01
#define PD_MSGTYPE_REQUEST 0x02
#define PD_MSGTYPE_BIST 0x03
#define PD_MSGTYPE_SINK_CAPABILITIES 0x04
#define PD_MSGTYPE_BATTERY_STATUS 0x05
#define PD_MSGTYPE_ALERT 0x06
#define PD_MSGTYPE_GET_COUNTRY_INFO 0x07
#define PD_MSGTYPE_VENDOR_DEFINED 0x0F
/* Extended Message */
#define PD_MSGTYPE_SOURCE_CAPABILITIES_EXTENDED 0x01
#define PD_MSGTYPE_STATUS 0x02
#define PD_MSGTYPE_GET_BATTERY_CAP 0x03
#define PD_MSGTYPE_GET_BATTERY_STATUS 0x04
#define PD_MSGTYPE_BATTERY_CAPABILITIES 0x05
#define PD_MSGTYPE_GET_MANUFACTURER_INFO 0x06
#define PD_MSGTYPE_MANUFACTURER_INFO 0x07
#define PD_MSGTYPE_SECURITY_REQUEST 0x08
#define PD_MSGTYPE_SECURITY_RESPONSE 0x09
#define PD_MSGTYPE_FIRMWARE_UPDATE_REQUEST 0x0A
#define PD_MSGTYPE_FIRMWARE_UPDATE_RESPONSE 0x0B
#define PD_MSGTYPE_PPS_STATUS 0x0C
#define PD_MSGTYPE_COUNTRY_INFO 0x0D
#define PD_MSGTYPE_COUNTRY_CODES 0x0E
/* Data roles */
#define PD_DATAROLE_UFP (0x0 << PD_HDR_DATAROLE_SHIFT)
#define PD_DATAROLE_DFP (0x1 << PD_HDR_DATAROLE_SHIFT)
/* Specification revisions */
#define PD_SPECREV_1_0 (0x0 << PD_HDR_SPECREV_SHIFT)
#define PD_SPECREV_2_0 (0x1 << PD_HDR_SPECREV_SHIFT)
#define PD_SPECREV_3_0 (0x2 << PD_HDR_SPECREV_SHIFT)
/* Port power roles */
#define PD_POWERROLE_SINK (0x0 << PD_HDR_POWERROLE_SHIFT)
#define PD_POWERROLE_SOURCE (0x1 << PD_HDR_POWERROLE_SHIFT)
/* Message ID */
#define PD_MESSAGEID_GET(msg) (((msg)->hdr & PD_HDR_MESSAGEID) >> PD_HDR_MESSAGEID_SHIFT)
/* Number of data objects */
#define PD_NUMOBJ(n) (((n) << PD_HDR_NUMOBJ_SHIFT) & PD_HDR_NUMOBJ)
#define PD_NUMOBJ_GET(msg) (((msg)->hdr & PD_HDR_NUMOBJ) >> PD_HDR_NUMOBJ_SHIFT)
/*
* PD Extended Message Header
*/
#define PD_EXTHDR_DATA_SIZE_SHIFT 0
#define PD_EXTHDR_DATA_SIZE (0x1FF << PD_EXTHDR_DATA_SIZE_SHIFT)
#define PD_EXTHDR_REQUEST_CHUNK_SHIFT 10
#define PD_EXTHDR_REQUEST_CHUNK (1 << PD_EXTHDR_REQUEST_CHUNK_SHIFT)
#define PD_EXTHDR_CHUNK_NUMBER_SHIFT 11
#define PD_EXTHDR_CHUNK_NUMBER (0xF << PD_EXTHDR_CHUNK_NUMBER_SHIFT)
#define PD_EXTHDR_CHUNKED_SHIFT 15
#define PD_EXTHDR_CHUNKED (1 << PD_EXTHDR_CHUNKED_SHIFT)
/* Data size */
#define PD_DATA_SIZE(n) (((n) << PD_EXTHDR_DATA_SIZE_SHIFT) & PD_EXTHDR_DATA_SIZE)
#define PD_DATA_SIZE_GET(msg) (((msg)->exthdr & PD_EXTHDR_DATA_SIZE) >> PD_EXTHDR_DATA_SIZE_SHIFT)
/* Chunk number */
#define PD_CHUNK_NUMBER(n) (((n) << PD_EXTHDR_CHUNK_NUMBER_SHIFT) & PD_EXTHDR_CHUNK_NUMBER)
#define PD_CHUNK_NUMBER_GET(msg) (((msg)->exthdr & PD_EXTHDR_CHUNK_NUMBER) >> PD_EXTHDR_CHUNK_NUMBER_SHIFT)
/*
* PD Power Data Object
*/
#define PD_PDO_TYPE_SHIFT 30
#define PD_PDO_TYPE (0x3 << PD_PDO_TYPE_SHIFT)
/* PDO types */
#define PD_PDO_TYPE_FIXED ((unsigned) (0x0 << PD_PDO_TYPE_SHIFT))
#define PD_PDO_TYPE_BATTERY ((unsigned) (0x1 << PD_PDO_TYPE_SHIFT))
#define PD_PDO_TYPE_VARIABLE ((unsigned) (0x2 << PD_PDO_TYPE_SHIFT))
#define PD_PDO_TYPE_AUGMENTED ((unsigned) (0x3 << PD_PDO_TYPE_SHIFT))
#define PD_APDO_TYPE_SHIFT 28
#define PD_APDO_TYPE (0x3 << PD_APDO_TYPE_SHIFT)
/* APDO types */
#define PD_APDO_TYPE_PPS (0x0 << PD_APDO_TYPE_SHIFT)
/* PD Source Fixed PDO */
#define PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT 29
#define PD_PDO_SRC_FIXED_DUAL_ROLE_PWR (1 << PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT)
#define PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT 28
#define PD_PDO_SRC_FIXED_USB_SUSPEND (1 << PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT)
#define PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT 27
#define PD_PDO_SRC_FIXED_UNCONSTRAINED (1 << PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT)
#define PD_PDO_SRC_FIXED_USB_COMMS_SHIFT 26
#define PD_PDO_SRC_FIXED_USB_COMMS (1 << PD_PDO_SRC_FIXED_USB_COMMS_SHIFT)
#define PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT 25
#define PD_PDO_SRC_FIXED_DUAL_ROLE_DATA (1 << PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT)
#define PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG_SHIFT 24
#define PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG (1 << PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG_SHIFT)
#define PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT 20
#define PD_PDO_SRC_FIXED_PEAK_CURRENT (0x3 << PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT)
#define PD_PDO_SRC_FIXED_VOLTAGE_SHIFT 10
#define PD_PDO_SRC_FIXED_VOLTAGE (0x3FF << PD_PDO_SRC_FIXED_VOLTAGE_SHIFT)
#define PD_PDO_SRC_FIXED_CURRENT_SHIFT 0
#define PD_PDO_SRC_FIXED_CURRENT (0x3FF << PD_PDO_SRC_FIXED_CURRENT_SHIFT)
/* PD Source Fixed PDO current */
#define PD_PDO_SRC_FIXED_CURRENT_GET(pdo) (((pdo) & PD_PDO_SRC_FIXED_CURRENT) >> PD_PDO_SRC_FIXED_CURRENT_SHIFT)
/* PD Source Fixed PDO voltage */
#define PD_PDO_SRC_FIXED_VOLTAGE_GET(pdo) (((pdo) & PD_PDO_SRC_FIXED_VOLTAGE) >> PD_PDO_SRC_FIXED_VOLTAGE_SHIFT)
/* PD Programmable Power Supply APDO */
#define PD_APDO_PPS_MAX_VOLTAGE_SHIFT 17
#define PD_APDO_PPS_MAX_VOLTAGE (0xFF << PD_APDO_PPS_MAX_VOLTAGE_SHIFT)
#define PD_APDO_PPS_MIN_VOLTAGE_SHIFT 8
#define PD_APDO_PPS_MIN_VOLTAGE (0xFF << PD_APDO_PPS_MIN_VOLTAGE_SHIFT)
#define PD_APDO_PPS_CURRENT_SHIFT 0
#define PD_APDO_PPS_CURRENT (0x7F << PD_APDO_PPS_CURRENT_SHIFT)
/* PD Programmable Power Supply APDO voltages */
#define PD_APDO_PPS_MAX_VOLTAGE_GET(pdo) (((pdo) & PD_APDO_PPS_MAX_VOLTAGE) >> PD_APDO_PPS_MAX_VOLTAGE_SHIFT)
#define PD_APDO_PPS_MIN_VOLTAGE_GET(pdo) (((pdo) & PD_APDO_PPS_MIN_VOLTAGE) >> PD_APDO_PPS_MIN_VOLTAGE_SHIFT)
#define PD_APDO_PPS_MAX_VOLTAGE_SET(v) (((v) << PD_APDO_PPS_MAX_VOLTAGE_SHIFT) & PD_APDO_PPS_MAX_VOLTAGE)
#define PD_APDO_PPS_MIN_VOLTAGE_SET(v) (((v) << PD_APDO_PPS_MIN_VOLTAGE_SHIFT) & PD_APDO_PPS_MIN_VOLTAGE)
/* PD Programmable Power Supply APDO current */
#define PD_APDO_PPS_CURRENT_GET(pdo) ((uint8_t) (((pdo) & PD_APDO_PPS_CURRENT) >> PD_APDO_PPS_CURRENT_SHIFT))
#define PD_APDO_PPS_CURRENT_SET(i) (((i) << PD_APDO_PPS_CURRENT_SHIFT) & PD_APDO_PPS_CURRENT)
/* PD Sink Fixed PDO */
#define PD_PDO_SNK_FIXED_DUAL_ROLE_PWR_SHIFT 29
#define PD_PDO_SNK_FIXED_DUAL_ROLE_PWR (1 << PD_PDO_SNK_FIXED_DUAL_ROLE_PWR_SHIFT)
#define PD_PDO_SNK_FIXED_HIGHER_CAP_SHIFT 28
#define PD_PDO_SNK_FIXED_HIGHER_CAP (1 << PD_PDO_SNK_FIXED_HIGHER_CAP_SHIFT)
#define PD_PDO_SNK_FIXED_UNCONSTRAINED_SHIFT 27
#define PD_PDO_SNK_FIXED_UNCONSTRAINED (1 << PD_PDO_SNK_FIXED_UNCONSTRAINED_SHIFT)
#define PD_PDO_SNK_FIXED_USB_COMMS_SHIFT 26
#define PD_PDO_SNK_FIXED_USB_COMMS (1 << PD_PDO_SNK_FIXED_USB_COMMS_SHIFT)
#define PD_PDO_SNK_FIXED_DUAL_ROLE_DATA_SHIFT 25
#define PD_PDO_SNK_FIXED_DUAL_ROLE_DATA (1 << PD_PDO_SNK_FIXED_DUAL_ROLE_DATA_SHIFT)
#define PD_PDO_SNK_FIXED_VOLTAGE_SHIFT 10
#define PD_PDO_SNK_FIXED_VOLTAGE (0x3FF << PD_PDO_SNK_FIXED_VOLTAGE_SHIFT)
#define PD_PDO_SNK_FIXED_CURRENT_SHIFT 0
#define PD_PDO_SNK_FIXED_CURRENT (0x3FF << PD_PDO_SNK_FIXED_CURRENT_SHIFT)
/* PD Sink Fixed PDO current */
#define PD_PDO_SNK_FIXED_CURRENT_SET(i) (((i) << PD_PDO_SNK_FIXED_CURRENT_SHIFT) & PD_PDO_SNK_FIXED_CURRENT)
/* PD Sink Fixed PDO voltage */
#define PD_PDO_SNK_FIXED_VOLTAGE_SET(v) (((v) << PD_PDO_SNK_FIXED_VOLTAGE_SHIFT) & PD_PDO_SNK_FIXED_VOLTAGE)
/*
* PD Request Data Object
*/
#define PD_RDO_OBJPOS_SHIFT 28
#define PD_RDO_OBJPOS (0x7 << PD_RDO_OBJPOS_SHIFT)
#define PD_RDO_GIVEBACK_SHIFT 27
#define PD_RDO_GIVEBACK (1 << PD_RDO_GIVEBACK_SHIFT)
#define PD_RDO_CAP_MISMATCH_SHIFT 26
#define PD_RDO_CAP_MISMATCH (1 << PD_RDO_CAP_MISMATCH_SHIFT)
#define PD_RDO_USB_COMMS_SHIFT 25
#define PD_RDO_USB_COMMS (1 << PD_RDO_USB_COMMS_SHIFT)
#define PD_RDO_NO_USB_SUSPEND_SHIFT 24
#define PD_RDO_NO_USB_SUSPEND (1 << PD_RDO_NO_USB_SUSPEND_SHIFT)
#define PD_RDO_UNCHUNKED_EXT_MSG_SHIFT 23
#define PD_RDO_UNCHUNKED_EXT_MSG (1 << PD_RDO_UNCHUNKED_EXT_MSG_SHIFT)
#define PD_RDO_OBJPOS_SET(i) (((i) << PD_RDO_OBJPOS_SHIFT) & PD_RDO_OBJPOS)
#define PD_RDO_OBJPOS_GET(msg) (((msg)->obj[0] & PD_RDO_OBJPOS) >> PD_RDO_OBJPOS_SHIFT)
/* Fixed and Variable RDO, no GiveBack support */
#define PD_RDO_FV_CURRENT_SHIFT 10
#define PD_RDO_FV_CURRENT (0x3FF << PD_RDO_FV_CURRENT_SHIFT)
#define PD_RDO_FV_MAX_CURRENT_SHIFT 0
#define PD_RDO_FV_MAX_CURRENT (0x3FF << PD_RDO_FV_MAX_CURRENT_SHIFT)
#define PD_RDO_FV_CURRENT_SET(i) (((i) << PD_RDO_FV_CURRENT_SHIFT) & PD_RDO_FV_CURRENT)
#define PD_RDO_FV_MAX_CURRENT_SET(i) (((i) << PD_RDO_FV_MAX_CURRENT_SHIFT) & PD_RDO_FV_MAX_CURRENT)
/* Fixed and Variable RDO with GiveBack support */
#define PD_RDO_FV_MIN_CURRENT_SHIFT 0
#define PD_RDO_FV_MIN_CURRENT (0x3FF << PD_RDO_FV_MIN_CURRENT_SHIFT)
#define PD_RDO_FV_MIN_CURRENT_SET(i) (((i) << PD_RDO_FV_MIN_CURRENT_SHIFT) & PD_RDO_FV_MIN_CURRENT)
/* TODO: Battery RDOs */
/* Programmable RDO */
#define PD_RDO_PROG_VOLTAGE_SHIFT 9
#define PD_RDO_PROG_VOLTAGE (0x7FF << PD_RDO_PROG_VOLTAGE_SHIFT)
#define PD_RDO_PROG_CURRENT_SHIFT 0
#define PD_RDO_PROG_CURRENT (0x7F << PD_RDO_PROG_CURRENT_SHIFT)
#define PD_RDO_PROG_VOLTAGE_SET(i) (((i) << PD_RDO_PROG_VOLTAGE_SHIFT) & PD_RDO_PROG_VOLTAGE)
#define PD_RDO_PROG_CURRENT_SET(i) (((i) << PD_RDO_PROG_CURRENT_SHIFT) & PD_RDO_PROG_CURRENT)
/*
* Time values
*
* 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)
/*
* Counter maximums
*/
#define PD_N_HARD_RESET_COUNT 2
/*
* Value parameters
*/
#define PD_MAX_EXT_MSG_LEN 260
#define PD_MAX_EXT_MSG_CHUNK_LEN 26
#define PD_MAX_EXT_MSG_LEGACY_LEN 26
/*
* Unit conversions
*
* V: volt
* CV: centivolt
* MV: millivolt
* PRV: Programmable RDO voltage unit (20 mV)
* PDV: Power Delivery voltage unit (50 mV)
* PAV: PPS APDO voltage unit (100 mV)
*
* A: ampere
* CA: centiampere
* MA: milliampere
* PDI: Power Delivery current unit (10 mA)
* PAI: PPS APDO current unit (50 mA)
*
* W: watt
* CW: centiwatt
* MW: milliwatt
*
* O: ohm
* CO: centiohm
* MO: milliohm
*/
#define PD_MV2PRV(mv) ((mv) / 20)
#define PD_MV2PDV(mv) ((mv) / 50)
#define PD_MV2PAV(mv) ((mv) / 100)
#define PD_PRV2MV(prv) ((prv) * 20)
#define PD_PDV2MV(pdv) ((pdv) * 50)
#define PD_PAV2MV(pav) ((pav) * 100)
#define PD_MA2CA(ma) (((ma) + 10 - 1) / 10)
#define PD_MA2PDI(ma) (((ma) + 10 - 1) / 10)
#define PD_MA2PAI(ma) (((ma) + 50 - 1) / 50)
#define PD_CA2PAI(ca) (((ca) + 5 - 1) / 5)
#define PD_PDI2MA(pdi) ((pdi) * 10)
#define PD_PAI2MA(pai) ((pai) * 50)
#define PD_PAI2CA(pai) ((pai) * 5)
#define PD_MW2CW(mw) ((mw) / 10)
#define PD_MO2CO(mo) ((mo) / 10)
/* Get portions of a voltage in more normal units */
#define PD_MV_V(mv) ((mv) / 1000)
#define PD_MV_MV(mv) ((mv) % 1000)
#define PD_PDV_V(pdv) ((pdv) / 20)
#define PD_PDV_CV(pdv) (5 * ((pdv) % 20))
#define PD_PAV_V(pav) ((pav) / 10)
#define PD_PAV_CV(pav) (10 * ((pav) % 10))
/* Get portions of a PD current in more normal units */
#define PD_PDI_A(pdi) ((pdi) / 100)
#define PD_PDI_CA(pdi) ((pdi) % 100)
#define PD_PAI_A(pai) ((pai) / 20)
#define PD_PAI_CA(pai) (5 * ((pai) % 20))
/* Get portions of a power in more normal units */
#define PD_CW_W(cw) ((cw) / 100)
#define PD_CW_CW(cw) ((cw) % 100)
/* Get portions of a resistance in more normal units */
#define PD_CO_O(co) ((co) / 100)
#define PD_CO_CO(co) ((co) % 100)
/*
* Unit constants
*/
#define PD_MV_MIN 0
#define PD_MV_MAX 21000
#define PD_PDV_MIN PD_MV2PDV(PD_MV_MIN)
#define PD_PDV_MAX PD_MV2PDV(PD_MV_MAX)
#define PD_MA_MIN 0
#define PD_MA_MAX 5000
#define PD_CA_MIN PD_MA2CA(PD_MA_MIN)
#define PD_CA_MAX PD_MA2CA(PD_MA_MAX)
#define PD_PDI_MIN PD_MA2PDI(PD_MA_MIN)
#define PD_PDI_MAX PD_MA2PDI(PD_MA_MAX)
#define PD_MW_MIN 0
#define PD_MW_MAX 100000
#define PD_MO_MIN 500
#define PD_MO_MAX 655350
/*
* FUSB Type-C Current level enum
*/
enum fusb_typec_current {
fusb_tcc_none = 0,
fusb_tcc_default = 1,
fusb_tcc_1_5 = 2,
fusb_sink_tx_ng = 2,
fusb_tcc_3_0 = 3,
fusb_sink_tx_ok = 3
};
#endif /* PDB_PD_H */

View File

@@ -0,0 +1,32 @@
/*
* 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_CONF_H
#define PDB_CONF_H
/* Number of messages in the message pool */
#define PDB_MSG_POOL_SIZE 4
#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)
#endif /* PDB_CONF_H */

View File

@@ -0,0 +1,55 @@
/*
* 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_MSG_H
#define PDB_MSG_H
#include <stdint.h>
/*
* PD message union
*
* This can be safely read from or written to in any form without any
* transformations because everything in the system is little-endian.
*
* Two bytes of padding are required at the start to prevent problems due to
* alignment. Specifically, without the padding, &obj[0] != &bytes[2], making
* the statement in the previous paragraph invalid.
*/
union pd_msg {
struct {
uint8_t _pad1[2];
uint8_t bytes[30];
} __attribute__((packed));
struct {
uint8_t _pad2[2];
uint16_t hdr;
union {
uint32_t obj[7];
struct {
uint16_t exthdr;
uint8_t data[26];
};
};
} __attribute__((packed));
};
#endif /* PDB_MSG_H */

View File

@@ -0,0 +1,699 @@
/*
* 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 "policy_engine.h"
#include <stdbool.h>
#include "int_n.h"
#include <pd.h>
#include "protocol_tx.h"
#include "hard_reset.h"
#include "fusb302b.h"
bool PolicyEngine::pdNegotiationComplete;
int PolicyEngine::current_voltage_mv;
int PolicyEngine::_requested_voltage;
bool PolicyEngine::_unconstrained_power;
union pd_msg PolicyEngine::currentMessage;
uint16_t PolicyEngine::hdr_template;
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;
union pd_msg PolicyEngine::tempMessage;
union pd_msg PolicyEngine::_last_dpm_request;
PolicyEngine::policy_engine_state PolicyEngine::state = PESinkStartup;
StaticQueue_t PolicyEngine::xStaticQueue;
uint8_t PolicyEngine::ucQueueStorageArea[PDB_MSG_POOL_SIZE
* sizeof(union pd_msg)];
QueueHandle_t PolicyEngine::messagesWaiting = NULL;
EventGroupHandle_t PolicyEngine::xEventGroupHandle = NULL;
StaticEventGroup_t PolicyEngine::xCreatedEventGroup;
void PolicyEngine::init() {
messagesWaiting = xQueueCreateStatic(PDB_MSG_POOL_SIZE,
sizeof(union pd_msg), ucQueueStorageArea, &xStaticQueue);
//Create static thread at PDB_PRIO_PE priority
osThreadStaticDef(PolEng, pe_task, PDB_PRIO_PE, 0, TaskStackSize,
TaskBuffer, &TaskControlBlock);
TaskHandle = osThreadCreate(osThread(PolEng), NULL);
xEventGroupHandle = xEventGroupCreateStatic(&xCreatedEventGroup);
}
void PolicyEngine::notify(uint32_t notification) {
if (xEventGroupHandle != NULL) {
xEventGroupSetBits(xEventGroupHandle, notification);
}
}
void PolicyEngine::pe_task(const void *arg) {
(void) arg;
//Internal thread loop
hdr_template = PD_DATAROLE_UFP | PD_POWERROLE_SINK;
/* Initialize the old_tcc_match */
_old_tcc_match = -1;
/* Initialize the pps_index */
_pps_index = 8;
/* Initialize the last_pps */
_last_pps = 8;
for (;;) {
//Loop based on state
switch (state) {
case PESinkStartup:
state = pe_sink_startup();
break;
case PESinkDiscovery:
state = pe_sink_discovery();
break;
case PESinkWaitCap:
state = pe_sink_wait_cap();
break;
case PESinkEvalCap:
state = pe_sink_eval_cap();
break;
case PESinkSelectCap:
state = pe_sink_select_cap();
break;
case PESinkTransitionSink:
state = pe_sink_transition_sink();
break;
case PESinkReady:
state = pe_sink_ready();
break;
case PESinkGetSourceCap:
state = pe_sink_get_source_cap();
break;
case PESinkGiveSinkCap:
state = pe_sink_give_sink_cap();
break;
case PESinkHardReset:
state = pe_sink_hard_reset();
break;
case PESinkTransitionDefault:
state = pe_sink_transition_default();
break;
case PESinkSoftReset:
state = pe_sink_soft_reset();
break;
case PESinkSendSoftReset:
state = pe_sink_send_soft_reset();
break;
case PESinkSendNotSupported:
state = pe_sink_send_not_supported();
break;
case PESinkChunkReceived:
state = pe_sink_chunk_received();
break;
case PESinkSourceUnresponsive:
state = pe_sink_source_unresponsive();
break;
case PESinkNotSupportedReceived:
state = pe_sink_not_supported_received();
break;
default:
state = PESinkStartup;
break;
}
}
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_startup() {
/* We don't have an explicit contract currently */
_explicit_contract = 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
* state: startup and exiting hard reset. On startup, the protocol layer
* is reset by the startup procedure. When exiting hard reset, the
* protocol layer is reset by the hard reset state machine. Since it's
* already done somewhere else, there's no need to do it again here. */
return PESinkDiscovery;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_discovery() {
/* Wait for VBUS. Since it's our only power source, we already know that
* we have it, so just move on. */
return PESinkWaitCap;
}
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,
//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) {
return PESinkWaitCap;
}
/* If we're too hot, we shouldn't negotiate power yet */
if (evt & 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)) {
/* Get the message */
while ((evt & PDB_EVT_PE_MSG_RX_PEND) || readMessage() == true) {
/* 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 */
if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_1_0) {
// /* If the other end is using at least version 3.0, we'll
// * use version 3.0. */
// if ((tempMessage.hdr & PD_HDR_SPECREV) >= PD_SPECREV_3_0) {
// hdr_template |= PD_SPECREV_3_0;
// /* Otherwise, use 2.0. Don't worry about the 1.0 case
// * because we don't have hardware for PD 1.0 signaling. */
// } else {
hdr_template |= PD_SPECREV_2_0;
// }
}
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;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_eval_cap() {
/* If we have a Source_Capabilities message, remember the index of the
* first PPS APDO so we can check if the request is for a PPS APDO in
* 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;
/* Search for the first PPS APDO */
for (int8_t 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) {
_pps_index = i + 1;
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)) {
return PESinkSelectCap;
}
return PESinkWaitCap;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_select_cap() {
/* Transmit the request */
waitForEvent(0xFFFF, 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);
/* If we got reset signaling, transition to default */
if (evt & 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) {
return PESinkHardReset;
}
/* Wait for a response */
evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET,
PD_T_SENDER_RESPONSE);
/* If we got reset signaling, transition to default */
if (evt & 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;
}
/* Get the response message */
if (messageWaiting()) {
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) {
return PESinkSoftReset;
/* If the message was Wait or Reject */
} else if ((PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_REJECT
|| PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_WAIT)
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
/* If we don't have an explicit contract, wait for capabilities */
if (!_explicit_contract) {
return PESinkWaitCap;
/* If we do have an explicit contract, go to the ready state */
} else {
return PESinkReady;
}
} else {
return PESinkSendSoftReset;
}
}
return PESinkHardReset;
}
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);
/* If we got reset signaling, transition to default */
if (evt & 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()) {
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 */
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;
}
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);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If we overheated, send a hard reset */
if (evt & PDB_EVT_PE_I_OVRTEMP) {
return PESinkHardReset;
}
/* If we received a message */
if (evt & 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) {
/* If the message is a multi-chunk extended message, let it
* time out. */
if ((tempMessage.hdr & PD_HDR_EXT)
&& (PD_DATA_SIZE_GET(&tempMessage)
> PD_MAX_EXT_MSG_LEGACY_LEN)) {
return PESinkChunkReceived;
/* Tell the DPM a message we sent got a response of
* Not_Supported. */
} else if (PD_MSGTYPE_GET(&tempMessage)
== PD_MSGTYPE_NOT_SUPPORTED
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
return PESinkNotSupportedReceived;
/* If we got an unknown message, send a soft reset */
} else {
return PESinkSendSoftReset;
}
/* If we got an unknown message, send a soft reset ??? */
} else {
return PESinkSendSoftReset;
}
}
}
return PESinkReady;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_get_source_cap() {
/* Get a message object */
union pd_msg *get_source_cap = &tempMessage;
/* Make a Get_Source_Cap message */
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);
/* Free the sent message */
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
return PESinkHardReset;
}
return PESinkReady;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_give_sink_cap() {
/* Get a message object */
union pd_msg *snk_cap = &tempMessage;
/* Get our capabilities from the DPM */
pdbs_dpm_get_sink_capability(snk_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);
/* Free the Sink_Capabilities message */
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
return PESinkHardReset;
}
return PESinkReady;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_hard_reset() {
/* If we've already sent the maximum number of hard resets, assume the
* source is unresponsive. */
if (_hard_reset_counter > PD_N_HARD_RESET_COUNT) {
return PESinkSourceUnresponsive;
}
/* Generate a hard reset signal */
ResetHandler::notify(PDB_EVT_HARDRST_RESET);
waitForEvent(PDB_EVT_PE_HARD_SENT);
/* Increment HardResetCounter */
_hard_reset_counter++;
return PESinkTransitionDefault;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_transition_default() {
_explicit_contract = false;
/* Tell the DPM to transition to default power */
pdbs_dpm_transition_default();
/* There is no local hardware to reset. */
/* Since we never change our data role from UFP, there is no reason to set
* it here. */
/* Tell the protocol layer we're done with the reset */
ResetHandler::notify( PDB_EVT_HARDRST_DONE);
return PESinkStartup;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_soft_reset() {
/* No need to explicitly reset the protocol layer here. It resets itself
* when a Soft_Reset message is received. */
/* Get a message object */
union pd_msg accept;
/* Make an Accept message */
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);
/* Free the sent message */
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
return PESinkHardReset;
}
return PESinkWaitCap;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_send_soft_reset() {
/* No need to explicitly reset the protocol layer here. It resets itself
* just before a Soft_Reset message is transmitted. */
/* Get a message object */
union pd_msg *softrst = &tempMessage;
/* Make a Soft_Reset message */
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);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a hard reset */
if ((evt & 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);
/* If we got reset signaling, transition to default */
if (evt & 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;
}
/* Get the response message */
if (messageWaiting()) {
readMessage();
/* If the source accepted our soft reset, wait for capabilities. */
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_ACCEPT
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
return PESinkWaitCap;
/* 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;
/* Otherwise, send a hard reset */
} else {
return PESinkHardReset;
}
}
return PESinkHardReset;
}
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);
} 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);
}
/* 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);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
/* If the message transmission failed, send a soft reset */
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
return PESinkSendSoftReset;
}
return PESinkReady;
}
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);
/* If we got reset signaling, transition to default */
if (evt & PDB_EVT_PE_RESET) {
return PESinkTransitionDefault;
}
return PESinkSendNotSupported;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_not_supported_received() {
/* Inform the Device Policy Manager that we received a Not_Supported
* message. */
return PESinkReady;
}
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_source_unresponsive() {
//Sit and chill, as PD is not working
osDelay(PD_T_PD_DEBOUNCE);
return PESinkSourceUnresponsive;
}
uint32_t PolicyEngine::waitForEvent(uint32_t mask, uint32_t ticksToWait) {
return xEventGroupWaitBits(xEventGroupHandle, mask, mask, pdFALSE,
ticksToWait);
}
bool PolicyEngine::isPD3_0() {
return (hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0;
}

View File

@@ -0,0 +1,198 @@
/*
* 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_POLICY_ENGINE_H
#define PDB_POLICY_ENGINE_H
#include <pd.h>
/*
* Events for the Policy Engine thread, used internally + sent by user code
*
*/
#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() {
if (pdNegotiationComplete)
return true;
if (state == policy_engine_state::PESinkSourceUnresponsive)
return true;
if (state == policy_engine_state::PESinkReady)
return true;
return false;
}
//Has pd negotiation completed
static bool pdHasNegotiated() {
return pdNegotiationComplete;
}
private:
static bool pdNegotiationComplete;
static int current_voltage_mv; //The current voltage PD is expecting
static int _requested_voltage; //The voltage the unit wanted to requests
static bool _unconstrained_power; // If the source is unconstrained
//Current message being handled
static union pd_msg currentMessage;
/* PD message header template */
static uint16_t hdr_template;
/* Whether or not we have an explicit contract */
static bool _explicit_contract;
/* The number of hard resets we've sent */
static int8_t _hard_reset_counter;
/* The result of the last Type-C Current match comparison */
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,
PESinkGetSourceCap,
PESinkGiveSinkCap,
PESinkHardReset,
PESinkTransitionDefault,
PESinkSoftReset,
PESinkSendSoftReset,
PESinkSendNotSupported,
PESinkChunkReceived,
PESinkNotSupportedReceived,
PESinkSourceUnresponsive
};
static enum policy_engine_state pe_sink_startup();
static enum policy_engine_state pe_sink_discovery();
static enum policy_engine_state pe_sink_wait_cap();
static enum policy_engine_state pe_sink_eval_cap();
static enum policy_engine_state pe_sink_select_cap();
static enum policy_engine_state pe_sink_transition_sink();
static enum policy_engine_state pe_sink_ready();
static enum policy_engine_state pe_sink_get_source_cap();
static enum policy_engine_state pe_sink_give_sink_cap();
static enum policy_engine_state pe_sink_hard_reset();
static enum policy_engine_state pe_sink_transition_default();
static enum policy_engine_state pe_sink_soft_reset();
static enum policy_engine_state pe_sink_send_soft_reset();
static enum policy_engine_state pe_sink_send_not_supported();
static enum policy_engine_state pe_sink_chunk_received();
static enum policy_engine_state pe_sink_not_supported_received();
static enum policy_engine_state pe_sink_source_unresponsive();
static EventGroupHandle_t xEventGroupHandle;
static StaticEventGroup_t xCreatedEventGroup;
static uint32_t waitForEvent(uint32_t mask, uint32_t ticksToWait =
portMAX_DELAY);
//Task resources
static osThreadId TaskHandle;
static const size_t TaskStackSize = 2048 / 4;
static uint32_t TaskBuffer[TaskStackSize];
static osStaticThreadDef_t TaskControlBlock;
static union pd_msg tempMessage;
static union pd_msg _last_dpm_request;
static policy_engine_state state;
//queue of up to PDB_MSG_POOL_SIZE messages to send
static StaticQueue_t xStaticQueue;
/* The array to use as the queue's storage area. This must be at least
uxQueueLength * uxItemSize bytes. */
static uint8_t ucQueueStorageArea[PDB_MSG_POOL_SIZE * sizeof(union pd_msg)];
static QueueHandle_t messagesWaiting;
static bool messageWaiting();
//Read a pending message into the temp message
static bool readMessage();
// These callbacks are called to implement the logic for the iron to select the desired voltage
/*
* Create a Request message based on the given Source_Capabilities message. If
* capabilities is NULL, the last non-null Source_Capabilities message passes
* is used. If none has been provided, the behavior is undefined.
*
* Returns true if sufficient power is available, false otherwise.
*/
static bool pdbs_dpm_evaluate_capability(const union pd_msg *capabilities,
union pd_msg *request);
/*
* Create a Sink_Capabilities message for our current capabilities.
*/
static void pdbs_dpm_get_sink_capability(union pd_msg *cap);
/*
* Return whether or not GiveBack support is enabled.
*/
static bool pdbs_dpm_giveback_enabled();
/*
* Evaluate whether or not the currently offered Type-C Current can fulfill our
* power needs.
*
* Returns true if sufficient power is available, false otherwise.
*/
static bool pdbs_dpm_evaluate_typec_current(enum fusb_typec_current tcc);
/*
* Indicate that power negotiations are starting.
*/
static void pdbs_dpm_pd_start();
/*
* Transition the sink to default power.
*/
static void pdbs_dpm_transition_default();
/*
* Transition to the requested minimum current.
*/
static void pdbs_dpm_transition_min();
/*
* Transition to Sink Standby if necessary.
*/
static void pdbs_dpm_transition_standby();
/*
* Transition to the requested power level
*/
static void pdbs_dpm_transition_requested();
/*
* Transition to the Type-C Current power level
*/
static void pdbs_dpm_transition_typec();
};
#endif /* PDB_POLICY_ENGINE_H */

View File

@@ -0,0 +1,227 @@
/*
* policy_engine_user.cpp
*
* Created on: 14 Jun 2020
* Author: Ralim
*/
#include "pd.h"
#include "policy_engine.h"
#include "BSP_PD.h"
/* The current draw when the output is disabled */
#define DPM_MIN_CURRENT PD_MA2PDI(50)
/*
* Find the index of the first PDO from capabilities in the voltage range,
* using the desired order.
*
* If there is no such PDO, returns -1 instead.
*/
static int8_t dpm_get_range_fixed_pdo_index(const union pd_msg *caps) {
/* Get the number of PDOs */
uint8_t numobj = PD_NUMOBJ_GET(caps);
/* Get ready to iterate over the PDOs */
int8_t i;
int8_t step;
i = numobj - 1;
step = -1;
uint16_t current = 100; // in centiamps
uint16_t voltagemin = 8000;
uint16_t voltagemax = 10000;
/* Look at the PDOs to see if one falls in our voltage range. */
while (0 <= i && i < numobj) {
/* If we have a fixed PDO, its V is within our range, and its I is at
* least our desired I */
uint16_t v = PD_PDO_SRC_FIXED_VOLTAGE_GET(caps->obj[i]);
if ((caps->obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_FIXED) {
if ( PD_PDO_SRC_FIXED_CURRENT_GET(caps->obj[i]) >= current) {
if (v >= PD_MV2PDV(voltagemin) && v <= PD_MV2PDV(voltagemax)) {
return i;
}
}
}
i += step;
}
return -1;
}
bool PolicyEngine::pdbs_dpm_evaluate_capability(
const union pd_msg *capabilities, union pd_msg *request) {
/* Get the number of PDOs */
uint8_t numobj = PD_NUMOBJ_GET(capabilities);
/* Get whether or not the power supply is constrained */
_unconstrained_power =
capabilities->obj[0] & PD_PDO_SRC_FIXED_UNCONSTRAINED;
/* 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++) {
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) {
/* 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;
/* Update requested voltage */
_requested_voltage = voltage;
return true;
}
}
}
}
}
/* 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 (pdNegotiationComplete) {
request->obj[0] |= PD_RDO_CAP_MISMATCH;
}
request->obj[0] |= PD_RDO_USB_COMMS;
/* Update requested voltage */
_requested_voltage = 5000;
return false;
}
void PolicyEngine::pdbs_dpm_get_sink_capability(union pd_msg *cap) {
/* Keep track of how many PDOs we've added */
int numobj = 0;
/* If we have no configuration or want something other than 5 V, add a PDO
* for vSafe5V */
/* Minimum current, 5 V, and higher capability. */
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
/* 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);
/* Get the PDO from the voltage range */
int8_t i = dpm_get_range_fixed_pdo_index(cap);
/* If it's vSafe5V, set our vSafe5V's current to what we want */
if (i == 0) {
cap->obj[0] &= ~PD_PDO_SNK_FIXED_CURRENT;
cap->obj[0] |= PD_PDO_SNK_FIXED_CURRENT_SET(current);
} else {
/* If we want more than 5 V, set the Higher Capability flag */
if (PD_MV2PDV(voltage) != PD_MV2PDV(5000)) {
cap->obj[0] |= PD_PDO_SNK_FIXED_HIGHER_CAP;
}
/* If the range PDO is a different voltage than the preferred
* voltage, add it to the array. */
if (i
> 0&& PD_PDO_SRC_FIXED_VOLTAGE_GET(cap->obj[i]) != PD_MV2PDV(voltage)) {
cap->obj[numobj++] =
PD_PDO_TYPE_FIXED
| PD_PDO_SNK_FIXED_VOLTAGE_SET(
PD_PDO_SRC_FIXED_VOLTAGE_GET(cap->obj[i])) | PD_PDO_SNK_FIXED_CURRENT_SET(
PD_PDO_SRC_FIXED_CURRENT_GET(cap->obj[i]));
}
/* If we have three PDOs at this point, make sure the last two are
* sorted by voltage. */
if (numobj == 3
&& (cap->obj[1] & PD_PDO_SNK_FIXED_VOLTAGE)
> (cap->obj[2] & PD_PDO_SNK_FIXED_VOLTAGE)) {
cap->obj[1] ^= cap->obj[2];
cap->obj[2] ^= cap->obj[1];
cap->obj[1] ^= cap->obj[2];
}
}
/* Set the unconstrained power flag. */
if (_unconstrained_power) {
cap->obj[0] |= PD_PDO_SNK_FIXED_UNCONSTRAINED;
}
/* Set the USB communications capable flag. */
cap->obj[0] |= PD_PDO_SNK_FIXED_USB_COMMS;
/* Set the Sink_Capabilities message header */
cap->hdr = hdr_template | PD_MSGTYPE_SINK_CAPABILITIES | PD_NUMOBJ(numobj);
}
bool PolicyEngine::pdbs_dpm_evaluate_typec_current(
enum fusb_typec_current tcc) {
(void) tcc;
//This is for evaluating 5V static current advertised by resistors
/* We don't control the voltage anymore; it will always be 5 V. */
current_voltage_mv = _requested_voltage = 5000;
//For the soldering iron we accept this as a fallback, but it sucks
pdNegotiationComplete = false;
return true;
}
void PolicyEngine::pdbs_dpm_transition_default() {
/* Cast the dpm_data to the right type */
/* Pretend we requested 5 V */
current_voltage_mv = 5000;
/* Turn the output off */
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);
}
bool PolicyEngine::messageWaiting() {
return uxQueueMessagesWaiting(messagesWaiting) > 0;
}
bool PolicyEngine::readMessage() {
return xQueueReceive(messagesWaiting, &tempMessage, 0) == pdTRUE;
}
void PolicyEngine::pdbs_dpm_transition_typec() {
//This means PD failed, so we either have a dump 5V only type C or a QC charger
//For now; treat this as failed neg
pdNegotiationComplete = false;
}

View File

@@ -0,0 +1,189 @@
/*
* 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 <stdlib.h>
#include "string.h"
#include <pd.h>
#include "policy_engine.h"
#include "protocol_tx.h"
#include "fusb302b.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, uint32_t ticksToWait) {
if (xEventGroupHandle != NULL) {
return xEventGroupWaitBits(xEventGroupHandle, mask, mask,
pdFALSE, ticksToWait);
}
return 0;
}

View File

@@ -0,0 +1,64 @@
/*
* 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, uint32_t ticksToWait =
portMAX_DELAY);
};
#endif /* PDB_PROTOCOL_RX_H */

View File

@@ -0,0 +1,298 @@
/*
* 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_tx.h"
#include <pd.h>
#include "policy_engine.h"
#include "protocol_rx.h"
#include "fusb302b.h"
#include "fusbpd.h"
osThreadId ProtocolTransmit::TaskHandle = NULL;
uint32_t ProtocolTransmit::TaskBuffer[ProtocolTransmit::TaskStackSize];
osStaticThreadDef_t ProtocolTransmit::TaskControlBlock;
StaticQueue_t ProtocolTransmit::xStaticQueue;
bool ProtocolTransmit::messageSending = false;
uint8_t ProtocolTransmit::ucQueueStorageArea[PDB_MSG_POOL_SIZE
* sizeof(union pd_msg)];
QueueHandle_t ProtocolTransmit::messagesWaiting = NULL;
uint8_t ProtocolTransmit::_tx_messageidcounter;
union pd_msg ProtocolTransmit::temp_msg;
EventGroupHandle_t ProtocolTransmit::xEventGroupHandle = NULL;
StaticEventGroup_t ProtocolTransmit::xCreatedEventGroup;
/*
* PRL_Tx_PHY_Layer_Reset state
*/
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_phy_reset() {
/* Reset the PHY */
fusb_reset();
/* If a message was pending when we got here, tell the policy engine that
* we failed to send it */
if (messagePending()) {
/* Tell the policy engine that we failed */
PolicyEngine::notify( PDB_EVT_PE_TX_ERR);
/* Finish failing to send the message */
while (messagePending()) {
getMessage(); //Discard
}
}
/* Wait for a message request */
return PRLTxWaitMessage;
}
/*
* PRL_Tx_Wait_for_Message_Request state
*/
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_wait_message() {
/* Wait for an event */
ProtocolTransmit::Notifications evt = waitForEvent(
(uint32_t) Notifications::PDB_EVT_PRLTX_RESET
| (uint32_t) Notifications::PDB_EVT_PRLTX_DISCARD
| (uint32_t) Notifications::PDB_EVT_PRLTX_MSG_TX);
if ((uint32_t) evt & (uint32_t) Notifications::PDB_EVT_PRLTX_RESET) {
return PRLTxPHYReset;
}
/* If the policy engine is trying to send a message */
if ((uint32_t) evt & (uint32_t) Notifications::PDB_EVT_PRLTX_MSG_TX) {
/* Get the message */
getMessage();
/* If it's a Soft_Reset, reset the TX layer first */
if (PD_MSGTYPE_GET(&temp_msg) == PD_MSGTYPE_SOFT_RESET
&& PD_NUMOBJ_GET(&(temp_msg)) == 0) {
return PRLTxReset;
/* Otherwise, just send the message */
} else {
return PRLTxConstructMessage;
}
}
/* Silence the compiler warning */
return PRLTxWaitMessage;
}
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;
}
/*
* PRL_Tx_Construct_Message state
*/
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_construct_message() {
/* Set the correct MessageID in the message */
temp_msg.hdr &= ~PD_HDR_MESSAGEID;
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);
// }
// }
// }
messageSending = true;
/* Send the message to the PHY */
fusb_send_message(&temp_msg);
return PRLTxWaitResponse;
}
/*
* PRL_Tx_Wait_for_PHY_Response state
*/
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_wait_response() {
/* Wait for an event. There is no need to run CRCReceiveTimer, since the
* FUSB302B handles that as part of its retry mechanism. */
ProtocolTransmit::Notifications evt = waitForEvent(
(uint32_t) Notifications::PDB_EVT_PRLTX_RESET
| (uint32_t) Notifications::PDB_EVT_PRLTX_DISCARD
| (uint32_t) Notifications::PDB_EVT_PRLTX_I_TXSENT
| (uint32_t) Notifications::PDB_EVT_PRLTX_I_RETRYFAIL);
if ((uint32_t) evt & (uint32_t) Notifications::PDB_EVT_PRLTX_RESET) {
return PRLTxPHYReset;
}
if ((uint32_t) evt & (uint32_t) Notifications::PDB_EVT_PRLTX_DISCARD) {
return PRLTxDiscardMessage;
}
/* If the message was sent successfully */
if ((uint32_t) evt & (uint32_t) Notifications::PDB_EVT_PRLTX_I_TXSENT) {
return PRLTxMatchMessageID;
}
/* If the message failed to be sent */
if ((uint32_t) evt & (uint32_t) Notifications::PDB_EVT_PRLTX_I_RETRYFAIL) {
return PRLTxTransmissionError;
}
/* Silence the compiler warning */
return PRLTxDiscardMessage;
}
/*
* PRL_Tx_Match_MessageID state
*/
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_match_messageid() {
union pd_msg goodcrc;
/* Read the GoodCRC */
fusb_read_message(&goodcrc);
/* Check that the message is correct */
if (PD_MSGTYPE_GET(&goodcrc) == PD_MSGTYPE_GOODCRC
&& PD_NUMOBJ_GET(&goodcrc) == 0
&& PD_MESSAGEID_GET(&goodcrc) == _tx_messageidcounter) {
return PRLTxMessageSent;
} else {
return PRLTxTransmissionError;
}
}
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_transmission_error() {
/* Increment MessageIDCounter */
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
/* Tell the policy engine that we failed */
PolicyEngine::notify( PDB_EVT_PE_TX_ERR);
return PRLTxWaitMessage;
}
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_message_sent() {
messageSending = false;
/* Increment MessageIDCounter */
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
/* Tell the policy engine that we succeeded */
PolicyEngine::notify( PDB_EVT_PE_TX_DONE);
return PRLTxWaitMessage;
}
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_discard_message() {
/* If we were working on sending a message, increment MessageIDCounter */
if (messageSending) {
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
return PRLTxPHYReset;
} else {
return PRLTxWaitMessage;
}
}
void ProtocolTransmit::thread(const void *args) {
(void) args;
ProtocolTransmit::protocol_tx_state state = PRLTxPHYReset;
//Init the incoming message queue
while (true) {
switch (state) {
case PRLTxPHYReset:
state = protocol_tx_phy_reset();
break;
case PRLTxWaitMessage:
state = protocol_tx_wait_message();
break;
case PRLTxReset:
state = protocol_tx_reset();
break;
case PRLTxConstructMessage:
state = protocol_tx_construct_message();
break;
case PRLTxWaitResponse:
state = protocol_tx_wait_response();
break;
case PRLTxMatchMessageID:
state = protocol_tx_match_messageid();
break;
case PRLTxTransmissionError:
state = protocol_tx_transmission_error();
break;
case PRLTxMessageSent:
state = protocol_tx_message_sent();
break;
case PRLTxDiscardMessage:
state = protocol_tx_discard_message();
break;
default:
state = PRLTxPHYReset;
break;
}
}
}
void ProtocolTransmit::notify(ProtocolTransmit::Notifications notification) {
if (xEventGroupHandle != NULL) {
xEventGroupSetBits(xEventGroupHandle, (uint32_t) notification);
}
}
void ProtocolTransmit::init() {
messagesWaiting = xQueueCreateStatic(PDB_MSG_POOL_SIZE,
sizeof(union pd_msg), ucQueueStorageArea, &xStaticQueue);
osThreadStaticDef(pd_txTask, thread, PDB_PRIO_PRL, 0, TaskStackSize,
TaskBuffer, &TaskControlBlock);
TaskHandle = osThreadCreate(osThread(pd_txTask), NULL);
xEventGroupHandle = xEventGroupCreateStatic(&xCreatedEventGroup);
}
void ProtocolTransmit::pushMessage(union pd_msg *msg) {
if (messagesWaiting) {
xQueueSend(messagesWaiting, msg, 100);
}
}
bool ProtocolTransmit::messagePending() {
if (messagesWaiting) {
return uxQueueMessagesWaiting(messagesWaiting) > 0;
}
return false;
}
void ProtocolTransmit::getMessage() {
//Loads the pending message into the buffer
if (messagesWaiting) {
xQueueReceive(messagesWaiting, &temp_msg, 1);
}
}
ProtocolTransmit::Notifications ProtocolTransmit::waitForEvent(uint32_t mask,
uint32_t ticksToWait) {
if (xEventGroupHandle) {
return (Notifications) xEventGroupWaitBits(xEventGroupHandle, mask,
mask,
pdFALSE, ticksToWait);
}
return (Notifications)0;
}

View File

@@ -0,0 +1,97 @@
/*
* 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_TX_H
#define PDB_PROTOCOL_TX_H
#include <stdint.h>
#include "policy_engine.h"
#include "protocol_rx.h"
#include <pd.h>
/* Events for the Protocol TX thread */
class ProtocolTransmit {
public:
static void init();
//Push a message to the queue to be sent out the pd comms bus
static void pushMessage(union pd_msg *msg);
enum class Notifications {
PDB_EVT_PRLTX_RESET = EVENT_MASK(0), //
PDB_EVT_PRLTX_I_TXSENT = EVENT_MASK(1), //
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);
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;
static bool messageSending;
/*
* Protocol TX machine states
*
* Because the PHY can automatically send retries, the Check_RetryCounter state
* has been removed, transitions relating to it are modified appropriately, and
* we don't even keep a RetryCounter.
*/
enum protocol_tx_state {
PRLTxPHYReset,
PRLTxWaitMessage,
PRLTxReset,
PRLTxConstructMessage,
PRLTxWaitResponse,
PRLTxMatchMessageID,
PRLTxTransmissionError,
PRLTxMessageSent,
PRLTxDiscardMessage
};
//Internal states
static protocol_tx_state protocol_tx_discard_message();
static protocol_tx_state protocol_tx_message_sent();
static protocol_tx_state protocol_tx_transmission_error();
static protocol_tx_state protocol_tx_match_messageid();
static protocol_tx_state protocol_tx_wait_response();
static protocol_tx_state protocol_tx_construct_message();
static protocol_tx_state protocol_tx_reset();
static protocol_tx_state protocol_tx_wait_message();
static protocol_tx_state protocol_tx_phy_reset();
//queue of up to PDB_MSG_POOL_SIZE messages to send
static StaticQueue_t xStaticQueue;
/* The array to use as the queue's storage area. This must be at least
uxQueueLength * uxItemSize bytes. */
static uint8_t ucQueueStorageArea[PDB_MSG_POOL_SIZE * sizeof(union pd_msg)];
static QueueHandle_t messagesWaiting;
static uint8_t _tx_messageidcounter;
static bool messagePending();
//Reads a message off the queue into the temp message
static void getMessage();
static union pd_msg temp_msg;
static Notifications waitForEvent(uint32_t mask, uint32_t ticksToWait =
portMAX_DELAY);
};
#endif /* PDB_PROTOCOL_TX_H */

View File

@@ -126,18 +126,6 @@ const uint8_t idleScreenBG[] = {
0x8A,0x84,0x82,0x81,0x80,0x80,0x80,0x40,0x40,0x20,0x18,0x07
};
const uint8_t idleScreenBGF[] = {
//width = 84
//height = 16
0xE0,0x18,0x04,0x02,0x02,0x01,0x41,0x81,0x01,0x01,0x65,0x99,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x02,0x04,0x18,0xE0,0x00,0x00,0xE0,0x18,0x04,0x02,0x02,
0x01,0x01,0x41,0x61,0x61,0x61,0xE1,0xC1,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0xC1,
0xE1,0x61,0x61,0x61,0x41,0x01,0x02,0x02,0x04,0x18,0xE0,0x00,
0x07,0x18,0x20,0x40,0x40,0x80,0x80,0x80,0x81,0x82,0x84,0x8A,0x92,0x82,0x82,0x82,0x80,0x82,0x80,0x82,0x82,0x82,0x87,0x87,
0x85,0x87,0x85,0x87,0x87,0x85,0x85,0x85,0x85,0x87,0x82,0x80,0x40,0x40,0x20,0x18,0x07,0x00,0x00,0x07,0x18,0x20,0x40,0x40,
0x80,0x80,0x82,0x86,0x86,0x86,0x87,0x83,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x83,
0x87,0x86,0x86,0x86,0x82,0x80,0x40,0x40,0x20,0x18,0x07,0x00
};
/*
* 16x16 icons

View File

@@ -0,0 +1,310 @@
/*
* I2CBB.cpp
*
* Created on: 12 Jun 2020
* Author: Ralim
*/
#include "Model_Config.h"
#ifdef I2C_SOFT
#include <I2CBB.hpp>
#include "FreeRTOS.h"
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;
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Pin = SDA2_Pin ;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(SDA2_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Pin = SCL2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(SCL2_GPIO_Port, &GPIO_InitStruct);
SOFT_SDA_HIGH();
SOFT_SCL_HIGH();
I2CSemaphore = xSemaphoreCreateMutexStatic (&xSemaphoreBuffer);
I2CSemaphore2 = xSemaphoreCreateMutexStatic (&xSemaphoreBuffer2);
unlock();
unlock2();
}
bool I2CBB::probe(uint8_t address) {
start();
bool ack = send(address);
stop();
return ack;
}
bool I2CBB::Mem_Read(uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData,
uint16_t Size) {
if (!lock())
return false;
start();
bool ack = send(DevAddress);
if (!ack) {
stop();
unlock();
return false;
}
ack = send(MemAddress);
if (!ack) {
stop();
unlock();
return false;
}
SOFT_SCL_LOW();
SOFT_I2C_DELAY();
// stop();
start();
ack = send(DevAddress | 1);
if (!ack) {
stop();
unlock();
return false;
}
while (Size) {
pData[0] = read(Size > 1);
pData++;
Size--;
}
stop();
unlock();
return true;
}
bool I2CBB::Mem_Write(uint16_t DevAddress, uint16_t MemAddress,
const uint8_t *pData, uint16_t Size) {
if (!lock())
return false;
start();
bool ack = send(DevAddress);
if (!ack) {
stop();
asm("bkpt");
unlock();
return false;
}
ack = send(MemAddress);
if (!ack) {
stop();
asm("bkpt");
unlock();
return false;
}
while (Size) {
resetWatchdog();
ack = send(pData[0]);
if (!ack) {
stop();
asm("bkpt");
unlock();
return false;
}
pData++;
Size--;
}
stop();
unlock();
return true;
}
void I2CBB::Transmit(uint16_t DevAddress, uint8_t *pData, uint16_t Size) {
if (!lock())
return;
start();
bool ack = send(DevAddress);
if (!ack) {
stop();
unlock();
return;
}
while (Size) {
ack = send(pData[0]);
if (!ack) {
stop();
unlock();
return;
}
pData++;
Size--;
}
stop();
unlock();
}
void I2CBB::Receive(uint16_t DevAddress, uint8_t *pData, uint16_t Size) {
if (!lock())
return;
start();
bool ack = send(DevAddress | 1);
if (!ack) {
stop();
unlock();
return;
}
while (Size) {
pData[0] = read(Size > 1);
pData++;
Size--;
}
stop();
unlock();
}
void I2CBB::TransmitReceive(uint16_t DevAddress, uint8_t *pData_tx,
uint16_t Size_tx, uint8_t *pData_rx, uint16_t Size_rx) {
if (Size_tx == 0 && Size_rx == 0)
return;
if (lock() == false)
return;
if (Size_tx) {
start();
bool ack = send(DevAddress);
if (!ack) {
stop();
unlock();
return;
}
while (Size_tx) {
ack = send(pData_tx[0]);
if (!ack) {
stop();
unlock();
return;
}
pData_tx++;
Size_tx--;
}
}
if (Size_rx) {
start();
bool ack = send(DevAddress | 1);
if (!ack) {
stop();
unlock();
return;
}
while (Size_rx) {
pData_rx[0] = read(Size_rx > 1);
pData_rx++;
Size_rx--;
}
}
stop();
unlock();
}
void I2CBB::start() {
/* I2C Start condition, data line goes low when clock is high */
SOFT_SCL_HIGH();
SOFT_SDA_HIGH();
SOFT_I2C_DELAY();
SOFT_SDA_LOW();
SOFT_I2C_DELAY();
SOFT_SCL_LOW();
SOFT_I2C_DELAY();
SOFT_SDA_HIGH();
}
void I2CBB::stop() {
/* I2C Stop condition, clock goes high when data is low */
SOFT_SDA_LOW();
SOFT_I2C_DELAY();
SOFT_SCL_HIGH();
SOFT_I2C_DELAY();
SOFT_SDA_HIGH();
SOFT_I2C_DELAY();
}
bool I2CBB::send(uint8_t value) {
for (uint8_t i = 0; i < 8; i++) {
write_bit(value & 0x80); // write the most-significant bit
value <<= 1;
}
SOFT_SDA_HIGH();
bool ack = (read_bit() == 0);
return ack;
}
uint8_t I2CBB::read(bool ack) {
uint8_t B = 0;
uint8_t i;
for (i = 0; i < 8; i++) {
B <<= 1;
B |= read_bit();
}
SOFT_SDA_HIGH();
if (ack)
write_bit(0);
else
write_bit(1);
return B;
}
uint8_t I2CBB::read_bit() {
uint8_t b;
SOFT_SDA_HIGH();
SOFT_I2C_DELAY();
SOFT_SCL_HIGH();
SOFT_I2C_DELAY();
if (SOFT_SDA_READ())
b = 1;
else
b = 0;
SOFT_SCL_LOW();
return b;
}
void I2CBB::unlock() {
xSemaphoreGive(I2CSemaphore);
}
bool I2CBB::lock() {
if (I2CSemaphore == NULL) {
asm("bkpt");
}
bool a = xSemaphoreTake(I2CSemaphore, (TickType_t) 100) == pdTRUE;
return a;
}
void I2CBB::write_bit(uint8_t val) {
if (val) {
SOFT_SDA_HIGH();
} else {
SOFT_SDA_LOW();
}
SOFT_I2C_DELAY();
SOFT_SCL_HIGH();
SOFT_I2C_DELAY();
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

@@ -0,0 +1,51 @@
/*
* I2CBB.hpp
*
* Created on: 12 Jun 2020
* Author: Ralim
*/
#ifndef BSP_MINIWARE_I2CBB_HPP_
#define BSP_MINIWARE_I2CBB_HPP_
#include "Model_Config.h"
#ifdef I2C_SOFT
#include "BSP.h"
#include "Setup.h"
#include "Pins.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "Software_I2C.h"
class I2CBB {
public:
static void init();
//Probe if device ACK's address or not
static bool probe(uint8_t address);
//Issues a complete 8bit register read
static bool Mem_Read(uint16_t DevAddress, uint16_t MemAddress,
uint8_t *pData, uint16_t Size);
//Implements a register write
static bool Mem_Write(uint16_t DevAddress, uint16_t MemAddress,
const uint8_t *pData, uint16_t Size);
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();
static void stop();
static bool send(uint8_t value);
static uint8_t read(bool ack);
static uint8_t read_bit();
static void write_bit(uint8_t val);
};
#endif
#endif /* BSP_MINIWARE_I2CBB_HPP_ */

View File

@@ -40,6 +40,9 @@ public:
static bool probe(uint16_t DevAddress);
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 I2C_RegisterWrite(uint8_t address, uint8_t reg, uint8_t data);
static uint8_t I2C_RegisterRead(uint8_t address, uint8_t reg);

View File

@@ -171,7 +171,7 @@ void OLED::transitionSecondaryFramebuffer(bool forwardNavigation) {
while (duration <= totalDuration) {
duration = xTaskGetTickCount() - start;
uint8_t progress = duration * 100 / totalDuration;
uint8_t progress = duration * 1000 / totalDuration;
progress = easeInOutTiming(progress);
progress = lerp(0, OLED_WIDTH, progress);
if (progress > OLED_WIDTH) {
@@ -213,7 +213,7 @@ void OLED::useSecondaryFramebuffer(bool useSecondary) {
}
void OLED::setRotation(bool leftHanded) {
#ifdef MODEL_TS80
#ifdef OLED_FLIP
leftHanded = !leftHanded;
#endif
if (inLeftHandedMode == leftHanded) {

View File

@@ -27,7 +27,6 @@
* This was bought to my attention by <Kuba Sztandera>
*/
uint32_t TipThermoModel::convertTipRawADCTouV(uint16_t rawADC) {
// This takes the raw ADC samples, converts these to uV
// Then divides this down by the gain to convert to the uV on the input to the op-amp (A+B terminals)
@@ -76,7 +75,7 @@ uint32_t TipThermoModel::convertuVToDegC(uint32_t tipuVDelta) {
tipuVDelta *= 10;
tipuVDelta /= systemSettings.TipGain;
#ifdef MODEL_TS80
#if defined( MODEL_TS80)+defined( MODEL_TS80P)>0
tipuVDelta /= OP_AMP_GAIN_STAGE_TS100 / OP_AMP_GAIN_STAGE_TS80;
#endif

View File

@@ -98,18 +98,18 @@
#define configUSE_IDLE_HOOK 1
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( SystemCoreClock )
#define configTICK_RATE_HZ ((TickType_t)100)
#define configMAX_PRIORITIES ( 4 )
#define configTICK_RATE_HZ ((TickType_t)1000)
#define configMAX_PRIORITIES ( 6 )
#define configMINIMAL_STACK_SIZE ((uint16_t)256)
#define configTOTAL_HEAP_SIZE ((size_t)1024*14) /*Currently use about 9000*/
#define configMAX_TASK_NAME_LEN ( 24 )
#define configMAX_TASK_NAME_LEN ( 32 )
#define configUSE_16_BIT_TICKS 0
#define configUSE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 8
#define configUSE_TIMERS 0
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
#define configCHECK_FOR_STACK_OVERFLOW 2 /*Bump this to 2 during development and bug hunting*/
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
@@ -162,12 +162,10 @@ standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
/* IMPORTANT: This define MUST be commented when used with STM32Cube firmware,
to prevent overwriting SysTick_Handler defined within STM32Cube HAL */
/* #define xPortSysTickHandler SysTick_Handler */
/* USER CODE BEGIN Defines */
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
/* USER CODE END Defines */
#if configUSE_TIMERS
#define configTIMER_TASK_PRIORITY 2
#define configTIMER_QUEUE_LENGTH 8
#define configTIMER_TASK_STACK_DEPTH (512/4)
#endif
#endif /* FREERTOS_CONFIG_H */

View File

@@ -12,8 +12,8 @@
#include "BSP.h"
#define PRESS_ACCEL_STEP 3
#define PRESS_ACCEL_INTERVAL_MIN 10
#define PRESS_ACCEL_INTERVAL_MAX 30
#define PRESS_ACCEL_INTERVAL_MIN 100
#define PRESS_ACCEL_INTERVAL_MAX 300
//GUI holds the menu structure and all its methods for the menu itself

View File

@@ -6,7 +6,7 @@
extern uint8_t PCBVersion;
extern uint32_t currentTempTargetDegC;
extern bool settingsWereReset;
extern bool usb_pd_available;
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -9,6 +9,7 @@
#include <history.hpp>
#include "BSP.h"
#include "expMovingAverage.h"
#include "../../configuration.h"
#ifndef POWER_HPP_
#define POWER_HPP_
@@ -18,16 +19,6 @@
// This is necessary because of the temp noise and thermal lag in the system.
// Once we have feed-forward temp estimation we should be able to better tune this.
#ifdef MODEL_TS100
const int32_t tipMass = 45; // X10 watts to raise 1 deg C in 1 second
const uint8_t tipResistance = 85; //x10 ohms, 8.5 typical for ts100, 4.5 typical for ts80
#endif
#ifdef MODEL_TS80
const uint32_t tipMass = 40;
const uint8_t tipResistance = 45; //x10 ohms, 8.5 typical for ts100, 4.5 typical for ts80
#endif
const uint8_t wattHistoryFilter = 24; // I term look back weighting
extern expMovingAverage<uint32_t, wattHistoryFilter> x10WattHistory;

View File

@@ -338,7 +338,6 @@
#include "stm32f1xx_hal_hcd.h"
#endif /* HAL_HCD_MODULE_ENABLED */
/* Exported macro ------------------------------------------------------------*/
#ifdef USE_FULL_ASSERT
/**

View File

@@ -23,10 +23,12 @@ void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
/* place for user code */
}
void vApplicationStackOverflowHook(xTaskHandle *pxTask,
signed portCHAR *pcTaskName) {
(void) pxTask;
(void) pcTaskName;
asm("bkpt");
// We dont have a good way to handle a stack overflow at this point in time
reboot();
}

View File

@@ -30,27 +30,30 @@ void QC_SeekContMode() {
}
void QC_SeekContPlus() {
QC_SeekContMode();
vTaskDelay(3);
osDelay(30);
QC_Seek20V();
vTaskDelay(1);
osDelay(10);
QC_SeekContMode();
}
void QC_SeekContNeg() {
QC_SeekContMode();
vTaskDelay(3);
osDelay(30);
QC_Seek12V();
vTaskDelay(1);
osDelay(10);
QC_SeekContMode();
}
uint8_t QCMode = 0;
uint8_t QCTries = 0;
void seekQC(int16_t Vx10, uint16_t divisor) {
if (QCMode == 5) startQC(divisor);
if (QCMode == 0) return; // NOT connected to a QC Charger
if (QCMode == 0)
startQC(divisor);
if (Vx10 < 45) return;
if (xTaskGetTickCount() < 100) return;
if (Vx10 > 130) Vx10 = 130; // Cap max value at 13V
if (Vx10 < 45)
return;
if (xTaskGetTickCount() < 1000)
return;
if (Vx10 > 130)
Vx10 = 130; // Cap max value at 13V
// Seek the QC to the Voltage given if this adapter supports continuous mode
// try and step towards the wanted value
@@ -62,18 +65,19 @@ void seekQC(int16_t Vx10, uint16_t divisor) {
int steps = difference / 2;
if (QCMode == 3) {
if (steps > -2 && steps < 2) return; // dont bother with small steps
if (steps > -2 && steps < 2)
return; // dont bother with small steps
while (steps < 0) {
QC_SeekContNeg();
vTaskDelay(3);
osDelay(30);
steps++;
}
while (steps > 0) {
QC_SeekContPlus();
vTaskDelay(3);
osDelay(30);
steps--;
}
vTaskDelay(10);
osDelay(100);
}
#ifdef ENABLE_QC2
// Re-measure
@@ -106,6 +110,9 @@ void startQC(uint16_t divisor) {
QCMode = 1; // Already at 12V, user has probably over-ridden this
return;
}
if (QCTries > 10) {
return;
}
QC_Init_GPIO();
// Tries to negotiate QC for 9V
@@ -118,7 +125,7 @@ void startQC(uint16_t divisor) {
// Delay 1.25 seconds
uint8_t enteredQC = 0;
for (uint16_t i = 0; i < 200 && enteredQC == 0; i++) {
vTaskDelay(1); // 10mS pause
osDelay(10); // 10mS pause
if (i > 130) {
if (QC_DM_PulledDown()) {
enteredQC = 1;
@@ -143,15 +150,14 @@ void startQC(uint16_t divisor) {
QCMode = 3; // We have at least QC2, pray for 3
return;
}
vTaskDelay(10); // 100mS
osDelay(100); // 100mS
}
QCMode = 5;
QCTries++;
if (QCTries > 10) // 10 goes to get it going
QCMode = 0;
QCTries++;
} else {
// no QC
QCMode = 0;
}
if (QCTries > 10) QCMode = 0;
}

View File

@@ -291,7 +291,7 @@ static int userConfirmation(const char *message) {
for (;;) {
int16_t messageOffset = ((xTaskGetTickCount() - messageStart)
/ (systemSettings.descriptionScrollSpeed == 1 ? 1 : 2));
/ (systemSettings.descriptionScrollSpeed == 1 ? 10 : 20));
messageOffset %= messageWidth; // Roll around at the end
if (lastOffset != messageOffset) {
@@ -353,8 +353,7 @@ static void settings_setInputPRange(void) {
static void settings_displayInputPRange(void) {
printShortDescription(0, 5);
//0 = 9V, 1=12V (Fixed Voltages, these imply 1.5A limits)
//2 = 18W, 2=24W (Auto Adjusting V, estimated from the tip resistance???) # TODO
// Need to come back and look at these ^ as there were issues with voltage hunting
/// TODO TS80P
switch (systemSettings.cutoutSetting) {
case 0:
OLED::printNumber(9, 2);
@@ -647,7 +646,7 @@ static void settings_setResetSettings(void) {
OLED::print(ResetOKMessage);
OLED::refresh();
waitForButtonPressOrTimeout(200); // 2 second timeout
waitForButtonPressOrTimeout(2000); // 2 second timeout
}
}
@@ -729,6 +728,10 @@ static void settings_setCalibrateVIN(void) {
case BUTTON_F_LONG:
case BUTTON_B_LONG:
saveSettings();
OLED::setCursor(0, 0);
OLED::printNumber(systemSettings.voltageDiv, 3);
OLED::refresh();
waitForButtonPressOrTimeout(1000);
return;
break;
case BUTTON_NONE:
@@ -740,7 +743,7 @@ static void settings_setCalibrateVIN(void) {
osDelay(40);
// Cap to sensible values
#ifdef MODEL_TS80
#if defined(MODEL_TS80)+defined(MODEL_TS80P)>0
if (systemSettings.voltageDiv < 500) {
systemSettings.voltageDiv = 500;
} else if (systemSettings.voltageDiv > 900) {
@@ -930,7 +933,7 @@ void gui_Menu(const menuitem *menu) {
OLED::setCursor(0, 0);
// If the user has hesitated for >=3 seconds, show the long text
// Otherwise "draw" the option
if ((xTaskGetTickCount() - lastButtonTime < 300)
if ((xTaskGetTickCount() - lastButtonTime < 3000)
|| menu[currentScreen].description == NULL) {
OLED::clearScreen();
menu[currentScreen].draw.func();
@@ -949,7 +952,7 @@ void gui_Menu(const menuitem *menu) {
int16_t descriptionOffset =
((xTaskGetTickCount() - descriptionStart)
/ (systemSettings.descriptionScrollSpeed == 1 ?
1 : 2));
10 : 20));
descriptionOffset %= descriptionWidth; // Roll around at the end
if (lastOffset != descriptionOffset) {
OLED::clearScreen();
@@ -1037,7 +1040,7 @@ void gui_Menu(const menuitem *menu) {
osDelay(40);
lcdRefresh = false;
}
if ((xTaskGetTickCount() - lastButtonTime) > (100 * 30)) {
if ((xTaskGetTickCount() - lastButtonTime) > (1000 * 30)) {
// If user has not pressed any buttons in 30 seconds, exit back a menu layer
// This will trickle the user back to the main screen eventually
earlyExit = true;

View File

@@ -13,7 +13,7 @@
#include "cmsis_os.h"
uint8_t PCBVersion = 0;
// File local variables
bool usb_pd_available = false;
bool settingsWereReset = false;
// FreeRTOS variables
@@ -43,21 +43,28 @@ int main(void) {
OLED::setFont(0); // default to bigger font
// Testing for which accelerometer is mounted
resetWatchdog();
usb_pd_available = usb_pd_detect();
resetWatchdog();
settingsWereReset = restoreSettings(); // load the settings from flash
#ifdef ACCEL_MMA
if (MMA8652FC::detect()) {
PCBVersion = 1;
MMA8652FC::initalize(); // this sets up the I2C registers
} else if (LIS2DH12::detect()) {
} else
#endif
#ifdef ACCEL_LIS
if (LIS2DH12::detect()) {
PCBVersion = 2;
// Setup the ST Accelerometer
LIS2DH12::initalize(); // startup the accelerometer
} else {
} else
#endif
{
PCBVersion = 3;
systemSettings.SleepTime = 0;
systemSettings.ShutdownTime = 0; // No accel -> disable sleep
systemSettings.sensitivity = 0;
}
resetWatchdog();
/* Create the thread(s) */

View File

@@ -22,7 +22,7 @@ extern "C" {
#include "stdlib.h"
#include "string.h"
#include "unit.h"
#include "I2CBB.hpp"
// File local variables
extern uint32_t currentTempTargetDegC;
extern uint8_t accelInit;
@@ -59,11 +59,13 @@ void gui_drawTipTemp(bool symbol) {
// Draw tip temp handling unit conversion & tolerance near setpoint
uint16_t Temp = 0;
#ifdef ENABLED_FAHRENHEIT_SUPPORT
if (systemSettings.temperatureInF)
if (systemSettings.temperatureInF){
Temp = TipThermoModel::getTipInF();
else
}else
#endif
{
Temp = TipThermoModel::getTipInC();
}
OLED::printNumber(Temp, 3); // Draw the tip temp out finally
if (symbol) {
@@ -126,12 +128,15 @@ static void gui_drawBatteryIcon() {
// User is on a lithium battery
// we need to calculate which of the 10 levels they are on
uint8_t cellCount = systemSettings.cutoutSetting + 2;
uint32_t cellV = getInputVoltageX10(systemSettings.voltageDiv, 0) / cellCount;
uint32_t cellV = getInputVoltageX10(systemSettings.voltageDiv, 0)
/ cellCount;
// Should give us approx cell voltage X10
// Range is 42 -> 33 = 9 steps therefore we will use battery 1-10
if (cellV < 33) cellV = 33;
if (cellV < 33)
cellV = 33;
cellV -= 33; // Should leave us a number of 0-9
if (cellV > 9) cellV = 9;
if (cellV > 9)
cellV = 9;
OLED::drawBattery(cellV + 1);
} else
OLED::drawSymbol(15); // Draw the DC Logo
@@ -166,7 +171,8 @@ static void gui_solderingTempAdjust() {
OLED::clearScreen();
OLED::setFont(0);
ButtonState buttons = getButtonState();
if (buttons) lastChange = xTaskGetTickCount();
if (buttons)
lastChange = xTaskGetTickCount();
switch (buttons) {
case BUTTON_NONE:
// stay
@@ -176,11 +182,14 @@ static void gui_solderingTempAdjust() {
return;
break;
case BUTTON_B_LONG:
if (xTaskGetTickCount() - autoRepeatTimer + autoRepeatAcceleration > PRESS_ACCEL_INTERVAL_MAX) {
if (xTaskGetTickCount() - autoRepeatTimer
+ autoRepeatAcceleration> PRESS_ACCEL_INTERVAL_MAX) {
if (systemSettings.ReverseButtonTempChangeEnabled) {
systemSettings.SolderingTemp += systemSettings.TempChangeLongStep;
systemSettings.SolderingTemp +=
systemSettings.TempChangeLongStep;
} else
systemSettings.SolderingTemp -= systemSettings.TempChangeLongStep;
systemSettings.SolderingTemp -=
systemSettings.TempChangeLongStep;
autoRepeatTimer = xTaskGetTickCount();
autoRepeatAcceleration += PRESS_ACCEL_STEP;
@@ -188,31 +197,40 @@ static void gui_solderingTempAdjust() {
break;
case BUTTON_B_SHORT:
if (systemSettings.ReverseButtonTempChangeEnabled) {
systemSettings.SolderingTemp += systemSettings.TempChangeShortStep;
systemSettings.SolderingTemp +=
systemSettings.TempChangeShortStep;
} else
systemSettings.SolderingTemp -= systemSettings.TempChangeShortStep;
systemSettings.SolderingTemp -=
systemSettings.TempChangeShortStep;
break;
case BUTTON_F_LONG:
if (xTaskGetTickCount() - autoRepeatTimer + autoRepeatAcceleration > PRESS_ACCEL_INTERVAL_MAX) {
if (xTaskGetTickCount() - autoRepeatTimer
+ autoRepeatAcceleration> PRESS_ACCEL_INTERVAL_MAX) {
if (systemSettings.ReverseButtonTempChangeEnabled) {
systemSettings.SolderingTemp -= systemSettings.TempChangeLongStep;
systemSettings.SolderingTemp -=
systemSettings.TempChangeLongStep;
} else
systemSettings.SolderingTemp += systemSettings.TempChangeLongStep;
systemSettings.SolderingTemp +=
systemSettings.TempChangeLongStep;
autoRepeatTimer = xTaskGetTickCount();
autoRepeatAcceleration += PRESS_ACCEL_STEP;
}
break;
case BUTTON_F_SHORT:
if (systemSettings.ReverseButtonTempChangeEnabled) {
systemSettings.SolderingTemp -= systemSettings.TempChangeShortStep; // add 10
systemSettings.SolderingTemp -=
systemSettings.TempChangeShortStep; // add 10
} else
systemSettings.SolderingTemp += systemSettings.TempChangeShortStep; // add 10
systemSettings.SolderingTemp +=
systemSettings.TempChangeShortStep; // add 10
break;
default:
break;
}
if ((PRESS_ACCEL_INTERVAL_MAX - autoRepeatAcceleration) < PRESS_ACCEL_INTERVAL_MIN) {
autoRepeatAcceleration = PRESS_ACCEL_INTERVAL_MAX - PRESS_ACCEL_INTERVAL_MIN;
if ((PRESS_ACCEL_INTERVAL_MAX - autoRepeatAcceleration)
< PRESS_ACCEL_INTERVAL_MIN) {
autoRepeatAcceleration = PRESS_ACCEL_INTERVAL_MAX
- PRESS_ACCEL_INTERVAL_MIN;
}
// constrain between 10-450 C
#ifdef ENABLED_FAHRENHEIT_SUPPORT
@@ -222,20 +240,27 @@ static void gui_solderingTempAdjust() {
} else
#endif
{
if (systemSettings.SolderingTemp > 450) systemSettings.SolderingTemp = 450;
if (systemSettings.SolderingTemp < 10) systemSettings.SolderingTemp = 10;
if (systemSettings.SolderingTemp > 450)
systemSettings.SolderingTemp = 450;
if (systemSettings.SolderingTemp < 10)
systemSettings.SolderingTemp = 10;
}
if (xTaskGetTickCount() - lastChange > 200) return; // exit if user just doesn't press anything for a bit
if (xTaskGetTickCount() - lastChange > 2000)
return; // exit if user just doesn't press anything for a bit
#ifdef MODEL_TS80
#ifdef OLED_FLIP
if (!OLED::getRotation()) {
#else
if (OLED::getRotation()) {
#endif
OLED::print(systemSettings.ReverseButtonTempChangeEnabled ? SymbolPlus : SymbolMinus);
OLED::print(
systemSettings.ReverseButtonTempChangeEnabled ?
SymbolPlus : SymbolMinus);
} else {
OLED::print(systemSettings.ReverseButtonTempChangeEnabled ? SymbolMinus : SymbolPlus);
OLED::print(
systemSettings.ReverseButtonTempChangeEnabled ?
SymbolMinus : SymbolPlus);
}
OLED::print(SymbolSpace);
@@ -249,14 +274,18 @@ static void gui_solderingTempAdjust() {
OLED::drawSymbol(1);
}
OLED::print(SymbolSpace);
#ifdef MODEL_TS80
#ifdef OLED_FLIP
if (!OLED::getRotation()) {
#else
if (OLED::getRotation()) {
#endif
OLED::print(systemSettings.ReverseButtonTempChangeEnabled ? SymbolMinus : SymbolPlus);
OLED::print(
systemSettings.ReverseButtonTempChangeEnabled ?
SymbolMinus : SymbolPlus);
} else {
OLED::print(systemSettings.ReverseButtonTempChangeEnabled ? SymbolPlus : SymbolMinus);
OLED::print(
systemSettings.ReverseButtonTempChangeEnabled ?
SymbolPlus : SymbolMinus);
}
OLED::refresh();
GUIDelay();
@@ -268,10 +297,16 @@ static int gui_SolderingSleepingMode(bool stayOff) {
for (;;) {
ButtonState buttons = getButtonState();
if (buttons) return 0;
if ((xTaskGetTickCount() > 100) && ((accelInit && (xTaskGetTickCount() - lastMovementTime < 100)) || (xTaskGetTickCount() - lastButtonTime < 100))) return 0; // user moved or pressed a button, go back to soldering
if (buttons)
return 0;
if ((xTaskGetTickCount() > 1000)
&& ((accelInit
&& (xTaskGetTickCount() - lastMovementTime < 1000))
|| (xTaskGetTickCount() - lastButtonTime < 1000)))
return 0; // user moved or pressed a button, go back to soldering
#ifdef MODEL_TS100
if (checkVoltageForExit()) return 1; // return non-zero on error
if (checkVoltageForExit())
return 1; // return non-zero on error
#endif
#ifdef ENABLED_FAHRENHEIT_SUPPORT
if (systemSettings.temperatureInF) {
@@ -279,7 +314,11 @@ static int gui_SolderingSleepingMode(bool stayOff) {
} else
#endif
{
currentTempTargetDegC = stayOff ? 0 : min(systemSettings.SleepTemp, systemSettings.SolderingTemp);
currentTempTargetDegC =
stayOff ?
0 :
min(systemSettings.SleepTemp,
systemSettings.SolderingTemp);
}
// draw the lcd
uint16_t tipTemp;
@@ -327,7 +366,8 @@ static int gui_SolderingSleepingMode(bool stayOff) {
}
if (systemSettings.ShutdownTime) // only allow shutdown exit if time > 0
if (lastMovementTime)
if (((uint32_t)(xTaskGetTickCount() - lastMovementTime)) > (uint32_t)(systemSettings.ShutdownTime * 60 * 100)) {
if (((uint32_t) (xTaskGetTickCount() - lastMovementTime))
> (uint32_t) (systemSettings.ShutdownTime * 60 * 1000)) {
// shutdown
currentTempTargetDegC = 0;
return 1; // we want to exit soldering mode
@@ -343,13 +383,15 @@ static void display_countdown(int sleepThres) {
* Print seconds or minutes (if > 99 seconds) until sleep
* mode is triggered.
*/
int lastEventTime = lastButtonTime < lastMovementTime ? lastMovementTime : lastButtonTime;
int lastEventTime =
lastButtonTime < lastMovementTime ?
lastMovementTime : lastButtonTime;
int downCount = sleepThres - xTaskGetTickCount() + lastEventTime;
if (downCount > 9900) {
OLED::printNumber(downCount / 6000 + 1, 2);
if (downCount > 99000) {
OLED::printNumber(downCount / 60000 + 1, 2);
OLED::print(SymbolMinutes);
} else {
OLED::printNumber(downCount / 100 + 1, 2);
OLED::printNumber(downCount / 1000 + 1, 2);
OLED::print(SymbolSeconds);
}
}
@@ -372,9 +414,9 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
uint32_t sleepThres = 0;
if (systemSettings.SleepTime < 6)
sleepThres = systemSettings.SleepTime * 10 * 100;
sleepThres = systemSettings.SleepTime * 10 * 1000;
else
sleepThres = (systemSettings.SleepTime - 5) * 60 * 100;
sleepThres = (systemSettings.SleepTime - 5) * 60 * 1000;
if (jumpToSleep) {
if (gui_SolderingSleepingMode(jumpToSleep == 2)) {
lastButtonTime = xTaskGetTickCount();
@@ -397,7 +439,8 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
break;
case BUTTON_F_LONG:
// if boost mode is enabled turn it on
if (systemSettings.boostModeEnabled) boostModeOn = true;
if (systemSettings.boostModeEnabled)
boostModeOn = true;
break;
case BUTTON_F_SHORT:
case BUTTON_B_SHORT: {
@@ -406,7 +449,8 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
if (oldTemp != systemSettings.SolderingTemp) {
saveSettings(); // only save on change
}
} break;
}
break;
default:
break;
}
@@ -430,9 +474,6 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
OLED::setCursor(0, 8);
OLED::print(SleepingTipAdvancedString);
// OLED::printNumber(
// TipThermoModel::convertTipRawADCTouV(getTipRawTemp(0)), 5); // Draw the tip temp out finally
gui_drawTipTemp(true);
OLED::print(SymbolSpace);
printVoltage();
@@ -500,17 +541,11 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
lastButtonTime = xTaskGetTickCount();
return;
}
#else
// on the TS80 we only want to check for over voltage to prevent tip damage
/*if (getInputVoltageX10(systemSettings.voltageDiv, 1) > 150) {
lastButtonTime = xTaskGetTickCount();
currentlyActiveTemperatureTarget = 0;
return; // Over voltage
}*/
#endif
if (systemSettings.sensitivity && systemSettings.SleepTime)
if (xTaskGetTickCount() - lastMovementTime > sleepThres && xTaskGetTickCount() - lastButtonTime > sleepThres) {
if (xTaskGetTickCount() - lastMovementTime > sleepThres
&& xTaskGetTickCount() - lastButtonTime > sleepThres) {
if (gui_SolderingSleepingMode(false)) {
return; // If the function returns non-0 then exit
}
@@ -558,7 +593,8 @@ void showDebugMenu(void) {
{
uint32_t temp = systemSettings.CalibrationOffset;
systemSettings.CalibrationOffset = 0;
OLED::printNumber(TipThermoModel::convertTipRawADCTouV(getTipRawTemp(1)), 6);
OLED::printNumber(
TipThermoModel::convertTipRawADCTouV(getTipRawTemp(1)), 6);
systemSettings.CalibrationOffset = temp;
}
break;
@@ -593,21 +629,33 @@ void showDebugMenu(void) {
GUIDelay();
}
}
uint8_t idleScreenBGF[sizeof(idleScreenBG)];
/* StartGUITask function */
void startGUITask(void const *argument __unused) {
uint8_t tempWarningState = 0;
bool buttonLockout = false;
bool tempOnDisplay = false;
{
//Generate the flipped screen into ram for later use
//flipped is generated by flipping each row
for (int row = 0; row < 2; row++) {
for (int x = 0; x < 84; x++) {
idleScreenBGF[(row * 84) + x] = idleScreenBG[(row * 84)
+ (83 - x)];
}
}
}
getTipRawTemp(1); // reset filter
OLED::setRotation(systemSettings.OrientationMode & 1);
uint32_t ticks = xTaskGetTickCount();
ticks += 400; // 4 seconds from now
ticks += 4000; // 4 seconds from now
while (xTaskGetTickCount() < ticks) {
if (showBootLogoIfavailable() == false) ticks = xTaskGetTickCount();
if (showBootLogoIfavailable() == false)
ticks = xTaskGetTickCount();
ButtonState buttons = getButtonState();
if (buttons) ticks = xTaskGetTickCount(); // make timeout now so we will exit
if (buttons)
ticks = xTaskGetTickCount(); // make timeout now so we will exit
GUIDelay();
}
@@ -618,9 +666,8 @@ void startGUITask(void const *argument __unused) {
OLED::setCursor(0, 0);
OLED::print(SettingsResetMessage);
OLED::refresh();
waitForButtonPressOrTimeout(1000);
waitForButtonPressOrTimeout(10000);
}
if (systemSettings.autoStartMode) {
// jump directly to the autostart mode
if (systemSettings.autoStartMode == 1) {
@@ -641,7 +688,8 @@ void startGUITask(void const *argument __unused) {
OLED::setDisplayState(OLED::DisplayState::ON);
OLED::setFont(0);
}
if (tempWarningState == 2) buttons = BUTTON_F_SHORT;
if (tempWarningState == 2)
buttons = BUTTON_F_SHORT;
if (buttons != BUTTON_NONE && buttonLockout)
buttons = BUTTON_NONE;
else
@@ -687,7 +735,11 @@ void startGUITask(void const *argument __unused) {
// button presses) in a while.
OLED::setDisplayState(OLED::DisplayState::ON);
if ((tipTemp < 50) && systemSettings.sensitivity && (((xTaskGetTickCount() - lastMovementTime) > MOVEMENT_INACTIVITY_TIME) && ((xTaskGetTickCount() - lastButtonTime) > BUTTON_INACTIVITY_TIME))) {
if ((tipTemp < 50) && systemSettings.sensitivity
&& (((xTaskGetTickCount() - lastMovementTime)
> MOVEMENT_INACTIVITY_TIME)
&& ((xTaskGetTickCount() - lastButtonTime)
> BUTTON_INACTIVITY_TIME))) {
OLED::setDisplayState(OLED::DisplayState::OFF);
}
@@ -711,7 +763,7 @@ void startGUITask(void const *argument __unused) {
} else {
OLED::setFont(0);
#ifdef MODEL_TS80
#ifdef OLED_FLIP
if (!OLED::getRotation()) {
#else
if (OLED::getRotation()) {
@@ -732,7 +784,7 @@ void startGUITask(void const *argument __unused) {
if (tempOnDisplay) {
// draw temp over the start soldering button
// Location changes on screen rotation
#ifdef MODEL_TS80
#ifdef OLED_FLIP
if (!OLED::getRotation()) {
#else
if (OLED::getRotation()) {
@@ -746,7 +798,9 @@ void startGUITask(void const *argument __unused) {
OLED::setCursor(0, 0);
}
// draw in the temp
if (!(systemSettings.coolingTempBlink && (xTaskGetTickCount() % 25 < 16))) gui_drawTipTemp(false); // draw in the temp
if (!(systemSettings.coolingTempBlink
&& (xTaskGetTickCount() % 25 < 16)))
gui_drawTipTemp(false); // draw in the temp
}
}
OLED::refresh();

View File

@@ -25,7 +25,6 @@ uint32_t lastMovementTime = 0;
void startMOVTask(void const *argument __unused) {
OLED::setRotation(systemSettings.OrientationMode & 1);
postRToSInit();
power_probe();
lastMovementTime = 0;
int16_t datax[MOVFilter] = { 0 };
int16_t datay[MOVFilter] = { 0 };
@@ -39,13 +38,20 @@ void startMOVTask(void const *argument __unused) {
for (;;) {
int32_t threshold = 1500 + (9 * 200);
threshold -= systemSettings.sensitivity * 200; // 200 is the step size
#ifdef ACCEL_LIS
if (PCBVersion == 2) {
LIS2DH12::getAxisReadings(tx, ty, tz);
rotation = LIS2DH12::getOrientation();
} else if (PCBVersion == 1) {
} else
#endif
#ifdef ACCEL_MMA
if (PCBVersion == 1) {
MMA8652FC::getAxisReadings(tx, ty, tz);
rotation = MMA8652FC::getOrientation();
}else
#endif
{
//do nothing :(
}
if (systemSettings.OrientationMode == 2) {
if (rotation != ORIENTATION_FLAT) {

View File

@@ -14,8 +14,8 @@
#include "FreeRTOS.h"
#include "task.h"
#include "Settings.h"
static TickType_t powerPulseRate = 1000;
static TickType_t powerPulseDuration = 50;
static TickType_t powerPulseRate = 10000;
static TickType_t powerPulseDuration = 250;
TaskHandle_t pidTaskNotification = NULL;
uint32_t currentTempTargetDegC = 0; // Current temperature target in C

View File

@@ -14,7 +14,7 @@ MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
/* LOCAL_BUILD*/
/*ROM (rx) : ORIGIN = 0x08000000, LENGTH = 46K*/
/*ROM (rx) : ORIGIN = 0x08000000, LENGTH = 48K*/
/* production*/
ROM (rx) : ORIGIN = 0x08004000, LENGTH = 46K

View File

@@ -22,6 +22,7 @@ FRTOS_CMIS_INC_DIR = ./Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS
FRTOS_INC_DIR = ./Middlewares/Third_Party/FreeRTOS/Source/include
FRTOS_GCC_INC_DIR = ./Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3
DRIVER_INC_DIR =./Core/Drivers
FUSB_DRIVERS_INC_DIR = ./Core/Drivers/FUSB302
BSP_INC_DIR = ./Core/BSP
MINIWARE_INC_DIR = ./Core/BSP/Miniware
THREADS_INC_DIR = ./Core/Threads
@@ -37,7 +38,8 @@ INCLUDES = -I$(APP_INC_DIR) \
-I$(DRIVER_INC_DIR) \
-I$(BSP_INC_DIR) \
-I$(MINIWARE_INC_DIR) \
-I$(THREADS_INC_DIR)
-I$(THREADS_INC_DIR) \
-I$(FUSB_DRIVERS_INC_DIR)
# output folder
HEXFILE_DIR=Hexfile

View File

@@ -6,7 +6,7 @@ TRANSLATION_SCRIPT="make_translation.py"
# AVAILABLE_LANGUAGES will be calculating according to json files in $TRANSLATION_DIR
AVAILABLE_LANGUAGES=()
BUILD_LANGUAGES=()
AVAILABLE_MODELS=("TS100" "TS80")
AVAILABLE_MODELS=("TS100" "TS80" "TS80P")
BUILD_MODELS=()
usage() {

View File

@@ -1,4 +1,5 @@
#pragma once
#include "Model_Config.h"
/**
* Configuration.h
* Define here your default pre settings for TS80 or TS100
@@ -76,7 +77,6 @@
#define DETAILED_SOLDERING 0 // 0: Disable 1: Enable - Default 0
#define DETAILED_IDLE 0 // 0: Disable 1: Enable - Default 0
#define CUT_OUT_SETTING 0 // default to no cut-off voltage (or 18W for TS80)
#define TEMPERATURE_INF 0 // default to 0
#define DESCRIPTION_SCROLL_SPEED 0 // 0: Slow 1: Fast - default to slow
@@ -94,6 +94,10 @@
#define OP_AMP_GAIN_STAGE_TS80 (1+(OP_AMP_Rf_TS80/OP_AMP_Rin_TS80))
//Deriving the Voltage div:
// Vin_max = (3.3*(r1+r2))/(r2)
//vdiv = (32768*4)/(vin_max*10)
#ifdef MODEL_TS100
#define VOLTAGE_DIV 467 // 467 - Default divider from schematic
#define CALIBRATION_OFFSET 900 // 900 - Default adc offset in uV
@@ -113,3 +117,30 @@
#define POWER_LIMIT_STEPS 2
#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_TS80
#endif
#ifdef MODEL_TS80P
#define VOLTAGE_DIV 650 // Default for TS80P with slightly different resistors
#define PID_POWER_LIMIT 35 // Sets the max pwm power limit
#define CALIBRATION_OFFSET 900 // 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
#endif
#ifdef MODEL_TS100
const int32_t tipMass = 45; // X10 watts to raise 1 deg C in 1 second
const uint8_t tipResistance = 85; //x10 ohms, 8.5 typical for ts100, 4.5 typical for ts80
#endif
#ifdef MODEL_TS80
const uint32_t tipMass = 40;
const uint8_t tipResistance = 45; //x10 ohms, 4.5 typical for ts80 tips
#endif
#ifdef MODEL_TS80P
const uint32_t tipMass = 40;
const uint8_t tipResistance = 45; //x10 ohms, 4.5 typical for ts80 tips
#endif