aboutsummaryrefslogtreecommitdiffhomepage
path: root/source/Core/Drivers
diff options
context:
space:
mode:
Diffstat (limited to 'source/Core/Drivers')
-rw-r--r--source/Core/Drivers/BMA223.cpp64
-rw-r--r--source/Core/Drivers/BMA223.hpp39
-rw-r--r--source/Core/Drivers/BMA223_defines.h68
-rw-r--r--source/Core/Drivers/Buttons.cpp115
-rw-r--r--source/Core/Drivers/Buttons.hpp36
-rw-r--r--source/Core/Drivers/FUSB302/fusb302b.h305
-rw-r--r--source/Core/Drivers/FUSB302/fusbpd.cpp28
-rw-r--r--source/Core/Drivers/FUSB302/fusbpd.h18
-rw-r--r--source/Core/Drivers/FUSB302/int_n.cpp80
-rw-r--r--source/Core/Drivers/FUSB302/int_n.h57
-rw-r--r--source/Core/Drivers/FUSB302/pd.h400
-rw-r--r--source/Core/Drivers/FUSB302/pdb_conf.h32
-rw-r--r--source/Core/Drivers/FUSB302/pdb_msg.h55
-rw-r--r--source/Core/Drivers/FUSB302/policy_engine.cpp692
-rw-r--r--source/Core/Drivers/FUSB302/policy_engine.h198
-rw-r--r--source/Core/Drivers/FUSB302/policy_engine_user.cpp227
-rw-r--r--source/Core/Drivers/FUSB302/protocol_rx.cpp189
-rw-r--r--source/Core/Drivers/FUSB302/protocol_rx.h64
-rw-r--r--source/Core/Drivers/FUSB302/protocol_tx.cpp298
-rw-r--r--source/Core/Drivers/FUSB302/protocol_tx.h97
-rw-r--r--source/Core/Drivers/Font.h181
-rw-r--r--source/Core/Drivers/I2CBB.cpp313
-rw-r--r--source/Core/Drivers/I2CBB.hpp51
-rw-r--r--source/Core/Drivers/I2C_Wrapper.hpp58
-rw-r--r--source/Core/Drivers/LIS2DH12.cpp43
-rw-r--r--source/Core/Drivers/LIS2DH12.hpp41
-rw-r--r--source/Core/Drivers/LIS2DH12_defines.hpp28
-rw-r--r--source/Core/Drivers/MMA8652FC.cpp63
-rw-r--r--source/Core/Drivers/MMA8652FC.hpp27
-rw-r--r--source/Core/Drivers/MMA8652FC_defines.h124
-rw-r--r--source/Core/Drivers/MSA301.cpp50
-rw-r--r--source/Core/Drivers/MSA301.h27
-rw-r--r--source/Core/Drivers/MSA301_defines.h34
-rw-r--r--source/Core/Drivers/OLED.cpp489
-rw-r--r--source/Core/Drivers/OLED.hpp116
-rw-r--r--source/Core/Drivers/README.md10
-rw-r--r--source/Core/Drivers/SC7A20.cpp69
-rw-r--r--source/Core/Drivers/SC7A20.hpp33
-rw-r--r--source/Core/Drivers/SC7A20_defines.h46
-rw-r--r--source/Core/Drivers/Si7210.cpp184
-rw-r--r--source/Core/Drivers/Si7210.h27
-rw-r--r--source/Core/Drivers/Si7210_defines.h91
-rw-r--r--source/Core/Drivers/TipThermoModel.cpp245
-rw-r--r--source/Core/Drivers/TipThermoModel.h42
44 files changed, 5454 insertions, 0 deletions
diff --git a/source/Core/Drivers/BMA223.cpp b/source/Core/Drivers/BMA223.cpp
new file mode 100644
index 00000000..4da4ae76
--- /dev/null
+++ b/source/Core/Drivers/BMA223.cpp
@@ -0,0 +1,64 @@
+/*
+ * BMA223.cpp
+ *
+ * Created on: 18 Sep. 2020
+ * Author: Ralim
+ */
+
+#include <BMA223.hpp>
+#include <array>
+
+bool BMA223::detect() {
+ if (FRToSI2C::probe(BMA223_ADDRESS)) {
+ //Read chip id to ensure its not an address collision
+ uint8_t id = 0;
+ if (FRToSI2C::Mem_Read(BMA223_ADDRESS, BMA223_BGW_CHIPID, &id, 1)) {
+ return id == 0b11111000;
+ }
+ }
+
+ return false;
+}
+
+static const FRToSI2C::I2C_REG i2c_registers[] = { //
+ //
+ { BMA223_PMU_RANGE, 0b00000011, 0 }, //2G range
+ { BMA223_PMU_BW, 0b00001101, 0 }, //250Hz filter
+ { BMA223_PMU_LPW, 0b00000000, 0 }, //Full power
+ { BMA223_ACCD_HBW, 0b00000000, 0 }, //filtered data out
+ { BMA223_INT_OUT_CTRL, 0b00001010, 0 }, //interrupt active low and OD to get it hi-z
+ { BMA223_INT_RST_LATCH, 0b10000000, 0 }, //interrupt active low and OD to get it hi-z
+ { BMA223_INT_EN_0, 0b01000000, 0 }, //Enable orientation
+ { BMA223_INT_A, 0b00100111, 0 }, //Setup orientation detection
+
+ //
+ };
+bool BMA223::initalize() {
+ //Setup acceleration readings
+ //2G range
+ //bandwidth = 250Hz
+ //High pass filter on (Slow compensation)
+ //Turn off IRQ output pins
+ //Orientation recognition in symmetrical mode
+ // Hysteresis is set to ~ 16 counts
+ //Theta blocking is set to 0b10
+
+ return FRToSI2C::writeRegistersBulk(BMA223_ADDRESS, i2c_registers, sizeof(i2c_registers) / sizeof(i2c_registers[0]));
+
+}
+
+void BMA223::getAxisReadings(int16_t &x, int16_t &y, int16_t &z) {
+ //The BMA is odd in that its output data width is only 8 bits
+ //And yet there are MSB and LSB registers _sigh_.
+ uint8_t sensorData[6] = { 0, 0, 0, 0, 0, 0 };
+
+ if (FRToSI2C::Mem_Read(BMA223_ADDRESS, BMA223_ACCD_X_LSB, sensorData, 6) == false) {
+ x = y = z = 0;
+ return;
+ }
+ //Shift 6 to make its range ~= the other accelerometers
+ x = sensorData[1] << 6;
+ y = sensorData[3] << 6;
+ z = sensorData[5] << 6;
+
+}
diff --git a/source/Core/Drivers/BMA223.hpp b/source/Core/Drivers/BMA223.hpp
new file mode 100644
index 00000000..85fa8576
--- /dev/null
+++ b/source/Core/Drivers/BMA223.hpp
@@ -0,0 +1,39 @@
+/*
+ * BMA223.hpp
+ *
+ * Created on: 18 Sep. 2020
+ * Author: Ralim
+ */
+
+#ifndef CORE_DRIVERS_BMA223_HPP_
+#define CORE_DRIVERS_BMA223_HPP_
+#include "I2C_Wrapper.hpp"
+#include "BSP.h"
+#include "BMA223_defines.h"
+
+class BMA223 {
+public:
+ static bool detect();
+ static bool initalize();
+ //1 = rh, 2,=lh, 8=flat
+ static Orientation getOrientation() {
+ uint8_t val = FRToSI2C::I2C_RegisterRead(BMA223_ADDRESS,
+ BMA223_INT_STATUS_3);
+ val >>= 4; //we dont need high values
+ val &= 0b11;
+ if(val &0b10){
+ return ORIENTATION_FLAT;
+ }else{
+ return static_cast<Orientation>(!val);
+ }
+ //0 = rhs
+ //1 =lhs
+ //2 & 3 == ignore
+
+ }
+ static void getAxisReadings(int16_t& x, int16_t& y, int16_t& z);
+
+private:
+};
+
+#endif /* CORE_DRIVERS_BMA223_HPP_ */
diff --git a/source/Core/Drivers/BMA223_defines.h b/source/Core/Drivers/BMA223_defines.h
new file mode 100644
index 00000000..c7322a56
--- /dev/null
+++ b/source/Core/Drivers/BMA223_defines.h
@@ -0,0 +1,68 @@
+/*
+ * BMA223_defines.h
+ *
+ * Created on: 18 Sep. 2020
+ * Author: Ralim
+ */
+
+#ifndef CORE_DRIVERS_BMA223_DEFINES_H_
+#define CORE_DRIVERS_BMA223_DEFINES_H_
+
+#define BMA223_ADDRESS 0x18<<1
+#define BMA223_BGW_CHIPID 0x00
+#define BMA223_ACCD_X_LSB 0x02
+#define BMA223_ACCD_X_MSB 0x03
+#define BMA223_ACCD_Y_LSB 0x04
+#define BMA223_ACCD_Y_MSB 0x05
+#define BMA223_ACCD_Z_LSB 0x06
+#define BMA223_ACCD_Z_MSB 0x07
+#define BMA223_ACCD_TEMP 0x08
+#define BMA223_INT_STATUS_0 0x09
+#define BMA223_INT_STATUS_1 0x0A
+#define BMA223_INT_STATUS_2 0x0B
+#define BMA223_INT_STATUS_3 0x0C
+#define BMA223_FIFO_STATUS 0x0E
+#define BMA223_PMU_RANGE 0x0F
+#define BMA223_PMU_BW 0x10
+#define BMA223_PMU_LPW 0x11
+#define BMA223_PMU_LOW_POWER 0x012
+#define BMA223_ACCD_HBW 0x13
+#define BMA223_BGW_SOFTRESET 0x14
+#define BMA223_INT_EN_0 0x16
+#define BMA223_INT_EN_1 0x17
+#define BMA223_INT_EN_2 0x18
+#define BMA223_INT_MAP_0 0x19
+#define BMA223_INT_MAP_1 0x1A
+#define BMA223_INT_MAP_2 0x1B
+#define BMA223_INT_SRC 0x1E
+#define BMA223_INT_OUT_CTRL 0x20
+#define BMA223_INT_RST_LATCH 0x21
+#define BMA223_INT_0 0x22
+#define BMA223_INT_1 0x23
+#define BMA223_INT_2 0x24
+#define BMA223_INT_3 0x25
+#define BMA223_INT_4 0x26
+#define BMA223_INT_5 0x27
+#define BMA223_INT_6 0x28
+#define BMA223_INT_7 0x29
+#define BMA223_INT_8 0x2A
+#define BMA223_INT_9 0x2B
+#define BMA223_INT_A 0x2C
+#define BMA223_INT_B 0x2D
+#define BMA223_INT_C 0x2E
+#define BMA223_INT_D 0x2F
+#define BMA223_FIFO_CONFIG_0 0x30
+#define BMA223_PMU_SELF_TEST 0x32
+#define BMA223_TRIM_NVM_CTRL 0x33
+#define BMA223_BGW_SPI3_WDT 0x34
+#define BMA223_OFC_CTRL 0x36
+#define BMA223_OFC_SETTING 0x37
+#define BMA223_OFC_OFFSET_X 0x38
+#define BMA223_OFC_OFFSET_Y 0x39
+#define BMA223_OFC_OFFSET_Z 0x3A
+#define BMA223_TRIM_GP0 0x3B
+#define BMA223_TRIM_GP1 0x3C
+#define BMA223_FIFO_CONFIG_1 0x3E
+#define BMA223_FIFO_DATA 0x3F
+
+#endif /* CORE_DRIVERS_BMA223_DEFINES_H_ */
diff --git a/source/Core/Drivers/Buttons.cpp b/source/Core/Drivers/Buttons.cpp
new file mode 100644
index 00000000..e252ab8c
--- /dev/null
+++ b/source/Core/Drivers/Buttons.cpp
@@ -0,0 +1,115 @@
+/*
+ * Buttons.c
+ *
+ * Created on: 29 May 2020
+ * Author: Ralim
+ */
+#include <Buttons.hpp>
+#include "FreeRTOS.h"
+#include "task.h"
+#include "gui.hpp"
+uint32_t lastButtonTime = 0;
+
+ButtonState getButtonState() {
+ /*
+ * Read in the buttons and then determine if a state change needs to occur
+ */
+
+ /*
+ * If the previous state was 00 Then we want to latch the new state if
+ * different & update time
+ * If the previous state was !00 Then we want to search if we trigger long
+ * press (buttons still down), or if release we trigger press
+ * (downtime>filter)
+ */
+ static uint8_t previousState = 0;
+ static uint32_t previousStateChange = 0;
+ const uint16_t timeout = 400;
+ uint8_t currentState;
+ currentState = (getButtonA()) << 0;
+ currentState |= (getButtonB()) << 1;
+
+ if (currentState)
+ lastButtonTime = xTaskGetTickCount();
+ if (currentState == previousState) {
+ if (currentState == 0)
+ return BUTTON_NONE;
+ if ((xTaskGetTickCount() - previousStateChange) > timeout) {
+ // User has been holding the button down
+ // We want to send a button is held message
+ if (currentState == 0x01)
+ return BUTTON_F_LONG;
+ else if (currentState == 0x02)
+ return BUTTON_B_LONG;
+ else
+ return BUTTON_BOTH_LONG; // Both being held case
+ } else
+ return BUTTON_NONE;
+ } else {
+ // A change in button state has occurred
+ ButtonState retVal = BUTTON_NONE;
+ if (currentState) {
+ // User has pressed a button down (nothing done on down)
+ if (currentState != previousState) {
+ // There has been a change in the button states
+ // If there is a rising edge on one of the buttons from double press we
+ // want to mask that out As users are having issues with not release
+ // both at once
+ if (previousState == 0x03)
+ currentState = 0x03;
+ }
+ } else {
+ // User has released buttons
+ // If they previously had the buttons down we want to check if they were <
+ // long hold and trigger a press
+ if ((xTaskGetTickCount() - previousStateChange) < timeout) {
+ // The user didn't hold the button for long
+ // So we send button press
+
+ if (previousState == 0x01)
+ retVal = BUTTON_F_SHORT;
+ else if (previousState == 0x02)
+ retVal = BUTTON_B_SHORT;
+ else
+ retVal = BUTTON_BOTH; // Both being held case
+ }
+ }
+ previousState = currentState;
+ previousStateChange = xTaskGetTickCount();
+ return retVal;
+ }
+ return BUTTON_NONE;
+}
+
+void waitForButtonPress() {
+ // we are just lazy and sleep until user confirms button press
+ // This also eats the button press event!
+ ButtonState buttons = getButtonState();
+ while (buttons) {
+ buttons = getButtonState();
+ GUIDelay();
+ }
+ while (!buttons) {
+ buttons = getButtonState();
+ GUIDelay();
+ }
+}
+
+void waitForButtonPressOrTimeout(uint32_t timeout) {
+ timeout += xTaskGetTickCount();
+ // calculate the exit point
+
+ ButtonState buttons = getButtonState();
+ while (buttons) {
+ buttons = getButtonState();
+ GUIDelay();
+ if (xTaskGetTickCount() > timeout)
+ return;
+ }
+ while (!buttons) {
+ buttons = getButtonState();
+ GUIDelay();
+ if (xTaskGetTickCount() > timeout)
+ return;
+ }
+}
diff --git a/source/Core/Drivers/Buttons.hpp b/source/Core/Drivers/Buttons.hpp
new file mode 100644
index 00000000..d894bf18
--- /dev/null
+++ b/source/Core/Drivers/Buttons.hpp
@@ -0,0 +1,36 @@
+/*
+ * Buttons.h
+ *
+ * Created on: 29 May 2020
+ * Author: Ralim
+ */
+#include "BSP.h"
+#ifndef INC_BUTTONS_H_
+#define INC_BUTTONS_H_
+
+extern uint32_t lastButtonTime;
+
+enum ButtonState {
+ BUTTON_NONE = 0, /* No buttons pressed / < filter time*/
+ BUTTON_F_SHORT = 1, /* User has pressed the front button*/
+ BUTTON_B_SHORT = 2, /* User has pressed the back button*/
+ BUTTON_F_LONG = 4, /* User is holding the front button*/
+ BUTTON_B_LONG = 8, /* User is holding the back button*/
+ BUTTON_BOTH = 16, /* User has pressed both buttons*/
+ BUTTON_BOTH_LONG = 32, /* User is holding both buttons*/
+
+/*
+ * Note:
+ * Pressed means press + release, we trigger on a full \__/ pulse
+ * holding means it has gone low, and been low for longer than filter time
+ */
+};
+
+//Returns what buttons are pressed (if any)
+ButtonState getButtonState();
+//Helpers
+void waitForButtonPressOrTimeout(uint32_t timeout);
+void waitForButtonPress();
+
+
+#endif /* INC_BUTTONS_H_ */
diff --git a/source/Core/Drivers/FUSB302/fusb302b.h b/source/Core/Drivers/FUSB302/fusb302b.h
new file mode 100644
index 00000000..1276e276
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/fusb302b.h
@@ -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
+ */
+bool fusb_setup();
+
+/*
+ * Reset the FUSB302B
+ */
+void fusb_reset();
+
+bool fusb_read_id();
+
+#endif /* PDB_FUSB302B_H */
diff --git a/source/Core/Drivers/FUSB302/fusbpd.cpp b/source/Core/Drivers/FUSB302/fusbpd.cpp
new file mode 100644
index 00000000..f8624fbe
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/fusbpd.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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"
+
+void fusb302_start_processing() {
+ /* Initialize the FUSB302B */
+ if (fusb_setup()) {
+ PolicyEngine::init();
+ ProtocolTransmit::init();
+ ProtocolReceive::init();
+ InterruptHandler::init();
+ }
+}
+#endif
diff --git a/source/Core/Drivers/FUSB302/fusbpd.h b/source/Core/Drivers/FUSB302/fusbpd.h
new file mode 100644
index 00000000..b48dfc4e
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/fusbpd.h
@@ -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_ */
diff --git a/source/Core/Drivers/FUSB302/int_n.cpp b/source/Core/Drivers/FUSB302/int_n.cpp
new file mode 100644
index 00000000..9250b4a0
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/int_n.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 "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;
+ 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);
+ }
+ /* 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);
+ }
+ if (status.interrupta & FUSB_INTERRUPTA_I_RETRYFAIL) {
+ ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_I_RETRYFAIL);
+ }
+
+ /* If the I_GCRCSENT flag is set, tell the Protocol RX thread */
+ //This means a message was recieved with a good CRC
+ if (status.interruptb & FUSB_INTERRUPTB_I_GCRCSENT) {
+ ProtocolReceive::notify(PDB_EVT_PRLRX_I_GCRCSENT);
+ }
+
+ /* If the I_OCP_TEMP and OVRTEMP flags are set, tell the Policy
+ * Engine thread */
+ if ((status.interrupta & FUSB_INTERRUPTA_I_OCP_TEMP) && (status.status1 & FUSB_STATUS1_OVRTEMP)) {
+ PolicyEngine::notify(PDB_EVT_PE_I_OVRTEMP);
+ }
+ }
+}
+void InterruptHandler::irqCallback() {
+ if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
+ if (TaskHandle != NULL) {
+ BaseType_t taskWoke = pdFALSE;
+ xTaskNotifyFromISR(TaskHandle, 0x01, eNotifyAction::eSetBits, &taskWoke);
+ portYIELD_FROM_ISR(taskWoke);
+ }
+ }
+}
diff --git a/source/Core/Drivers/FUSB302/int_n.h b/source/Core/Drivers/FUSB302/int_n.h
new file mode 100644
index 00000000..69c460f7
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/int_n.h
@@ -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 */
diff --git a/source/Core/Drivers/FUSB302/pd.h b/source/Core/Drivers/FUSB302/pd.h
new file mode 100644
index 00000000..60cbd26a
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/pd.h
@@ -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 */
diff --git a/source/Core/Drivers/FUSB302/pdb_conf.h b/source/Core/Drivers/FUSB302/pdb_conf.h
new file mode 100644
index 00000000..c2ca0f3f
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/pdb_conf.h
@@ -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 */
diff --git a/source/Core/Drivers/FUSB302/pdb_msg.h b/source/Core/Drivers/FUSB302/pdb_msg.h
new file mode 100644
index 00000000..2cfea250
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/pdb_msg.h
@@ -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 */
diff --git a/source/Core/Drivers/FUSB302/policy_engine.cpp b/source/Core/Drivers/FUSB302/policy_engine.cpp
new file mode 100644
index 00000000..fb726a8f
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/policy_engine.cpp
@@ -0,0 +1,692 @@
+/*
+ * 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 "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;
+ }
+ //So, we could send a hardreset here; however that will cause a power cycle on the PSU end.. Which will then reset this MCU
+ //So therefore we went get anywhere :)
+ /* 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. */
+
+ 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, TickType_t ticksToWait) {
+ return xEventGroupWaitBits(xEventGroupHandle, mask, mask, pdFALSE,
+ ticksToWait);
+
+}
+
+bool PolicyEngine::isPD3_0() {
+ return (hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0;
+}
+
diff --git a/source/Core/Drivers/FUSB302/policy_engine.h b/source/Core/Drivers/FUSB302/policy_engine.h
new file mode 100644
index 00000000..74b6ca08
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/policy_engine.h
@@ -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, TickType_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 */
diff --git a/source/Core/Drivers/FUSB302/policy_engine_user.cpp b/source/Core/Drivers/FUSB302/policy_engine_user.cpp
new file mode 100644
index 00000000..4e65cf85
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/policy_engine_user.cpp
@@ -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;
+}
diff --git a/source/Core/Drivers/FUSB302/protocol_rx.cpp b/source/Core/Drivers/FUSB302/protocol_rx.cpp
new file mode 100644
index 00000000..4ab58925
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/protocol_rx.cpp
@@ -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, TickType_t ticksToWait) {
+ if (xEventGroupHandle != NULL) {
+ return xEventGroupWaitBits(xEventGroupHandle, mask, mask,
+ pdFALSE, ticksToWait);
+ }
+ return 0;
+}
diff --git a/source/Core/Drivers/FUSB302/protocol_rx.h b/source/Core/Drivers/FUSB302/protocol_rx.h
new file mode 100644
index 00000000..7b9c6922
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/protocol_rx.h
@@ -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, TickType_t ticksToWait =
+ portMAX_DELAY);
+
+};
+
+#endif /* PDB_PROTOCOL_RX_H */
diff --git a/source/Core/Drivers/FUSB302/protocol_tx.cpp b/source/Core/Drivers/FUSB302/protocol_tx.cpp
new file mode 100644
index 00000000..9f014550
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/protocol_tx.cpp
@@ -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,
+ TickType_t ticksToWait) {
+ if (xEventGroupHandle) {
+ return (Notifications) xEventGroupWaitBits(xEventGroupHandle, mask,
+ mask,
+ pdFALSE, ticksToWait);
+ }
+ return (Notifications)0;
+}
diff --git a/source/Core/Drivers/FUSB302/protocol_tx.h b/source/Core/Drivers/FUSB302/protocol_tx.h
new file mode 100644
index 00000000..4ce1626d
--- /dev/null
+++ b/source/Core/Drivers/FUSB302/protocol_tx.h
@@ -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, TickType_t ticksToWait =
+ portMAX_DELAY);
+
+};
+
+#endif /* PDB_PROTOCOL_TX_H */
diff --git a/source/Core/Drivers/Font.h b/source/Core/Drivers/Font.h
new file mode 100644
index 00000000..450c59bd
--- /dev/null
+++ b/source/Core/Drivers/Font.h
@@ -0,0 +1,181 @@
+/*
+ * Font.h
+ *
+ * Created on: 17 Sep 2016
+ * Author: Ralim
+ *
+ * ... This file contains the font...
+ */
+
+#ifndef FONT_H_
+#define FONT_H_
+#include "Translation.h"
+
+#define FONT_12_WIDTH 12
+// THE MAIN FONTS ARE NO LONGER HERE, MOVED TO PYTHON AUTO GEN
+// THESE ARE ONLY THE SYMBOL FONTS
+
+const uint8_t ExtraFontChars[] = {
+//width = 12
+//height = 16
+ 0x00, 0x18, 0x24, 0x24, 0x18, 0xC0, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, // Degrees F
+ 0x00, 0x18, 0x24, 0x24, 0x18, 0x80, 0x40, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08, 0x10, 0x10, 0x10, 0x00, 0x00, // Degrees C
+ 0x00, 0x00, 0x20, 0x30, 0x38, 0xFC, 0xFE, 0xFC, 0x38, 0x30, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x00, // UP arrow
+
+ 0x00, 0xF0, 0x08, 0x0E, 0x02, 0x02, 0x02, 0x02, 0x0E, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x3F, 0x00, // Battery Empty
+ 0x00, 0xF0, 0x08, 0x0E, 0x02, 0x02, 0x02, 0x02, 0x0E, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0x40, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x40, 0x3F, 0x00, // Battery 1*/
+ 0x00, 0xF0, 0x08, 0x0E, 0x02, 0x02, 0x02, 0x02, 0x0E, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0x40, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x40, 0x3F, 0x00, // Battery 2*/
+ 0x00, 0xF0, 0x08, 0x0E, 0x02, 0x02, 0x02, 0x02, 0x0E, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0x40, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x40, 0x3F, 0x00, // Battery 3*/
+ 0x00, 0xF0, 0x08, 0x0E, 0x02, 0x02, 0x02, 0x02, 0x0E, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0x40, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x40, 0x3F, 0x00, // Battery 4*/
+ 0x00, 0xF0, 0x08, 0x0E, 0x02, 0x02, 0x02, 0x02, 0x0E, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0x40, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x40, 0x3F, 0x00, // Battery 5*/
+ 0x00, 0xF0, 0x08, 0x8E, 0x82, 0x82, 0x82, 0x82, 0x8E, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0x40, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x40, 0x3F, 0x00, // Battery 6*/
+ 0x00, 0xF0, 0x08, 0xCE, 0xC2, 0xC2, 0xC2, 0xC2, 0xCE, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0x40, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x40, 0x3F, 0x00, // Battery 7*/
+ 0x00, 0xF0, 0x08, 0xEE, 0xE2, 0xE2, 0xE2, 0xE2, 0xEE, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0x40, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x40, 0x3F, 0x00, // Battery 8*/
+ 0x00, 0xF0, 0x08, 0xEE, 0xE2, 0xF2, 0xF2, 0xE2, 0xEE, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0x40, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x40, 0x3F, 0x00, // Battery 9*/
+ 0x00, 0xF0, 0x08, 0xEE, 0xE2, 0xFA, 0xFA, 0xE2, 0xEE, 0x08, 0xF0, 0x00, 0x00, 0x3F, 0x40, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x40, 0x3F, 0x00, // Battery 10*/
+
+ 0x00, 0x00, 0x38, 0xC4, 0x00, 0x38, 0xC4, 0x00, 0x38, 0xC4, 0x00, 0x00, 0x00, 0x38, 0x3A, 0x39, 0x38, 0x3A, 0x39, 0x38, 0x3A, 0x39, 0x10, 0x10, // heating
+ 0x00, 0x60, 0xE0, 0xFE, 0xE0, 0xE0, 0xE0, 0xE0, 0xFE, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0xFF, 0xFF, 0x03, 0x01, 0x00, 0x00, 0x00, // AC
+
+ 0xFC, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x82, 0x62, 0x1A, 0x02, 0xFC, 0x3F, 0x40, 0x42, 0x46, 0x4C, 0x58, 0x46, 0x41, 0x40, 0x40, 0x40, 0x3F, // ☑ (check box on, menu true)
+ 0xFC, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0xFC, 0x3F, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x3F, // ☐ (check box off, menu false)
+
+ /*
+ 0x00,0x00,0x00,0x80,0x80,0xFE,0xFF,0x83,0x87,0x06,0x00,0x00,0x00,0x00,0x30,0x70,0x60,0x7F,0x3F,0x00,0x00,0x00,0x00,0x00, // Function?
+ 0x00,0x70,0xFA,0xDB,0xDB,0xDB,0xDB,0xDB,0xDB,0xFF,0xFE,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x00,0x00, // a_
+ 0x00,0x3C,0x7E,0xE7,0xC3,0xC3,0xC3,0xC3,0xE7,0x7E,0x3C,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x00,0x00, // 0_
+ 0x55,0x00,0xAA,0x00,0x55,0x00,0xAA,0x00,0x55,0x00,0xAA,0x00,0x55,0x00,0xAA,0x00,0x55,0x00,0xAA,0x00,0x55,0x00,0xAA,0x00, // 25% block
+ 0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55, // 50% pipe
+ 0xAA,0xFF,0x55,0xFF,0xAA,0xFF,0x55,0xFF,0xAA,0xFF,0x55,0xFF,0xAA,0xFF,0x55,0xFF,0xAA,0xFF,0x55,0xFF,0xAA,0xFF,0x55,0xFF, // 75% block
+ 0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00, // | pipe
+ 0x80,0x80,0x80,0x80,0x80,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00, // T pipe ,|
+ 0xC0,0xC0,0xFF,0xFF,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0xFE,0xFE,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00, // ,| double pipe
+ 0x00,0x00,0xFF,0xFF,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00, // || double pipe
+ 0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0xFE,0xFE,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00, // #NAME?//#NAME?
+ 0xC0,0xC0,0xFF,0xFF,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x06,0x07,0x07,0x00,0x00,0x00,0x00,0x00, // ,^ double pupe
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00, // #NAME?//#NAME?
+ 0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01, // ,> pipe
+ 0x80,0x80,0x80,0x80,0x80,0xFF,0xFF,0x80,0x80,0x80,0x80,0x80,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, // _|_ pipe
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x01,0x01,0x01,0x01,0x01,0xFF,0xFF,0x01,0x01,0x01,0x01,0x01, // ,|, pipe
+ 0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x01,0x01,0x01,0x01,0x01, // |, pipe
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, // #NAME?//#NAME?
+ 0x80,0x80,0x80,0x80,0x80,0xFF,0xFF,0x80,0x80,0x80,0x80,0x80,0x01,0x01,0x01,0x01,0x01,0xFF,0xFF,0x01,0x01,0x01,0x01,0x01, // #NAME?//#NAME?
+ 0x00,0x00,0xFF,0xFF,0x00,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00,0x07,0x07,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, // ,> double pipe
+ 0x00,0x00,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00,0xFF,0xFF,0x00,0xFE,0xFE,0x06,0x06,0x06,0x06,0x06, // ^, double pipe
+ 0xC0,0xC0,0xFF,0xFF,0x00,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xC0,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, // _|_ double pipe
+ 0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x06,0x06,0xFE,0xFE,0x00,0xFE,0xFE,0x06,0x06,0x06,0x06,0x06, // ,|, double pipe
+ 0x00,0x00,0xFF,0xFF,0x00,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00,0xFF,0xFF,0x00,0xFE,0xFE,0x06,0x06,0x06,0x06,0x06, // |, double pipe
+ 0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, // == double pipe
+ 0xC0,0xC0,0xFF,0xFF,0x00,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xC0,0x06,0x06,0xFE,0xFE,0x00,0xFE,0xFE,0x06,0x06,0x06,0x06,0x06, // #NAME?//#NAME?
+ 0x00,0x00,0x00,0x78,0xFC,0xCC,0x8C,0x0C,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x3E,0x33,0x33,0x3F,0x1E,0x00,0x00,0x00, // Delta lowercase
+ 0x00,0x00,0x00,0x00,0x00,0x7E,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 27 (')
+ 0x80,0x80,0x80,0x80,0x80,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00, // ,^ pipe
+ 0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x01,0x01,0x01,0x01,0x01, // | , pipe
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, // solid block
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, // half block bottom
+ 0x00,0x00,0x00,0x00,0x00,0xBF,0xBF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x3F,0x00,0x00,0x00,0x00,0x00, // 7C (|)
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // top half solid block
+ 0x00,0x00,0x0C,0xFC,0xFC,0x6C,0x60,0x60,0xE0,0xC0,0x00,0x00,0x00,0x00,0x30,0x3F,0x3F,0x36,0x06,0x06,0x07,0x03,0x00,0x00, // DE small
+ 0x00,0x00,0x03,0xFF,0xFF,0x1B,0x18,0x18,0xF8,0xF0,0x00,0x00,0x00,0x00,0x30,0x3F,0x3F,0x36,0x06,0x06,0x07,0x03,0x00,0x00, // DE large
+ 0x00,0x00,0x00,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ? (,)
+ 0x00,0x00,0x00,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x06,0x06,0x00,0x00,0x00, // =
+ 0x00,0x00,0x00,0x40,0x80,0x80,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // sideways comma
+ 0x00,0x00,0x80,0xC0,0x80,0x00,0x00,0x80,0xC0,0x80,0x00,0x00,0x00,0x00,0x01,0x03,0x01,0x00,0x00,0x01,0x03,0x01,0x00,0x00, // ..
+ 0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x01,0x00,0x00,0x00,0x00, // .
+ 0x00,0x00,0x02,0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // tiny 1
+ 0x00,0x00,0x00,0x00,0xF0,0xF0,0xF0,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00, // small block
+ */
+ };
+
+const uint8_t FontSymbols[] = { 0x00, 0x00, 0x00, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00,
+ 0x00, // Right block
+ 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x00, 0x00,
+ 0x00, // left block
+ 0x00, 0x00, 0x00, 0x10, 0x18, 0x1C, 0xFE, 0x1C, 0x18, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0C, 0x1C, 0x3F, 0x1C, 0x0C, 0x04, 0x00,
+ 0x00, // UD arrow
+ 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x37, 0x00, 0x00, 0x37, 0x37, 0x00, 0x00,
+ 0x00, // !!
+ 0x00, 0x38, 0x7C, 0xC6, 0x82, 0xFE, 0xFE, 0x02, 0xFE, 0xFE, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x3F, 0x00, 0x3F, 0x3F, 0x00,
+ 0x00, // paragraph
+ 0x00, 0x00, 0xDC, 0xFE, 0x22, 0x22, 0x22, 0x22, 0xE6, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x08, 0x19, 0x11, 0x11, 0x11, 0x11, 0x1F, 0x0E, 0x00,
+ 0x00, // section
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x00, // cursor
+ 0x00, 0x00, 0x00, 0x08, 0x0C, 0x0E, 0xFF, 0x0E, 0x0C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x4C, 0x5C, 0x7F, 0x5C, 0x4C, 0x44, 0x00,
+ 0x00, // UD arrow
+ 0x00, 0x00, 0x00, 0x10, 0x18, 0x1C, 0xFE, 0x1C, 0x18, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00,
+ 0x00, // UP arrow
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0C, 0x1C, 0x3F, 0x1C, 0x0C, 0x04, 0x00,
+ 0x00, // Down arrow
+ 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x01, 0x00,
+ 0x00, // right arrow
+ 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, // left arrow
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0x80, 0x80, 0x80,
+ 0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x01, 0x03, 0x07, 0x00, 0x00, 0x00, 0x07, 0x03, 0x01, 0x00, // LR arrow
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x04, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x04, // UP block
+ 0x00, 0x20, 0x60, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00 // Down block
+ };
+
+const uint8_t WarningBlock24[] = {
+ //width = 24
+ //height = 16
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x30, 0x0C, 0x02, 0xF1, 0xF1, 0xF1, 0x02, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xB0, 0x8C, 0x83, 0x80,
+ 0x80, 0x80, 0x80, 0xB3, 0xB3, 0xB3, 0x80, 0x80, 0x80, 0x80, 0x83, 0x8C, 0xB0, 0xC0, 0x00, 0x00 };
+
+const uint8_t idleScreenBG[] = {
+ //width = 84
+ //height = 16
+ 0x00, 0xE0, 0x18, 0x04, 0x02, 0x02, 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, 0x01, 0x02, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00, 0xE0, 0x18, 0x04, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x99, 0x65, 0x01, 0x01, 0x81, 0x41, 0x01, 0x02, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x07, 0x18, 0x20, 0x40, 0x40, 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, 0x80, 0x40, 0x40, 0x20, 0x18, 0x07, 0x00, 0x00, 0x07,
+ 0x18, 0x20, 0x40, 0x40, 0x80, 0x82, 0x87, 0x85, 0x85, 0x85, 0x85, 0x87, 0x87, 0x85, 0x87, 0x85, 0x87, 0x87, 0x82, 0x82, 0x82, 0x80, 0x82, 0x80, 0x82, 0x82, 0x82, 0x92, 0x8A, 0x84, 0x82, 0x81,
+ 0x80, 0x80, 0x80, 0x40, 0x40, 0x20, 0x18, 0x07 };
+
+const uint8_t disconnectedTipIcon[] = { //
+ //41 x 16
+ 0xE0, 0x18, 0x04, 0x02, 0x02, 0x01, 0x09, 0x11, 0x21, 0x41, 0x81, 0x81, 0x41, 0x21, 0x11, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xF9, 0x09, 0xF9, 0x01, 0xF9, 0x09, 0xF9,
+ 0x01, 0x01, 0xF9, 0x09, 0xF9, 0x01, 0x02, 0x02, 0x04, 0x18, 0xE0,
+ //
+ 0x07, 0x18, 0x20, 0x40, 0x40, 0x80, 0x90, 0x88, 0x84, 0x82, 0x81, 0x81, 0x82, 0x84, 0x88, 0x90, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xBB, 0xAA, 0xBB, 0x80, 0xBB, 0xAA, 0xBB,
+ 0x80, 0x80, 0xBB, 0xAA, 0xBB, 0x80, 0x40, 0x40, 0x20, 0x18, 0x07
+ //
+ };
+
+/*
+ * 16x16 icons
+ * */
+const uint8_t SettingsMenuIcons[] = {
+
+// Soldering
+//width = 16
+//height = 16
+ 0x00, 0x02, 0x04, 0x08, 0x12, 0x24, 0xC4, 0x42, 0x82, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x07, 0x0A, 0x14, 0x28, 0x50, 0x60, 0x00,
+
+ //Sleep
+ //width = 16
+ //height = 16
+ 0x00, 0xC6, 0xE6, 0xF6, 0xBE, 0x9E, 0x8E, 0x86, 0x00, 0x00, 0x40, 0x40, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x01, 0x01, 0x01, 0x45, 0x65, 0x75, 0x5D, 0x4C, 0x00, 0x06, 0x07, 0x07, 0x05, 0x04, 0x00,
+
+ //Menu
+ //width = 16
+ //height = 16
+ 0x00, 0x80, 0x06, 0x86, 0x46, 0x06, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x00, 0x00, 0x00, 0x61, 0x60, 0x00, 0x00, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x00,
+
+ //Wrench
+ ///width = 16
+ //height = 16
+ 0x00, 0x18, 0x30, 0x32, 0x7E, 0x7C, 0xF0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x0F, 0x3E, 0x7E, 0x4C, 0x0C, 0x18, 0x00,
+#ifdef NOTUSED
+ //Calibration (Not used, kept for future menu layouts)
+ //width = 16
+ //height = 16
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE8, 0x70,
+ 0x7A, 0x5E, 0x8E, 0x1C, 0x30, 0x00, 0x00, 0x10, 0x38, 0x1C,
+ 0x0E, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ #endif
+ };
+
+#endif /* FONT_H_ */
diff --git a/source/Core/Drivers/I2CBB.cpp b/source/Core/Drivers/I2CBB.cpp
new file mode 100644
index 00000000..200fecfe
--- /dev/null
+++ b/source/Core/Drivers/I2CBB.cpp
@@ -0,0 +1,313 @@
+/*
+ * 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) {
+ if (!lock())
+ return false;
+ start();
+ bool ack = send(address);
+ stop();
+ unlock();
+ 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
diff --git a/source/Core/Drivers/I2CBB.hpp b/source/Core/Drivers/I2CBB.hpp
new file mode 100644
index 00000000..6d733c90
--- /dev/null
+++ b/source/Core/Drivers/I2CBB.hpp
@@ -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_ */
diff --git a/source/Core/Drivers/I2C_Wrapper.hpp b/source/Core/Drivers/I2C_Wrapper.hpp
new file mode 100644
index 00000000..9d5996df
--- /dev/null
+++ b/source/Core/Drivers/I2C_Wrapper.hpp
@@ -0,0 +1,58 @@
+/*
+ * FRToSI2C.hpp
+ *
+ * Created on: 14Apr.,2018
+ * Author: Ralim
+ */
+
+#ifndef FRTOSI2C_HPP_
+#define FRTOSI2C_HPP_
+
+#include "cmsis_os.h"
+
+/*
+ * Wrapper class to work with the device I2C bus
+ *
+ * This provides mutex protection of the peripheral
+ * Also allows hardware to use DMA should it want to
+ *
+ *
+ */
+class FRToSI2C {
+public:
+
+ static void FRToSInit() {
+ if (I2CSemaphore == nullptr) {
+ I2CSemaphore = xSemaphoreCreateBinaryStatic(&xSemaphoreBuffer);
+ xSemaphoreGive(I2CSemaphore);
+ }
+ }
+
+ static void CpltCallback(); //Normal Tx Callback
+
+ static bool Mem_Read(uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, uint16_t Size);
+ static bool Mem_Write(uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, uint16_t Size);
+ //Returns true if device ACK's being addressed
+ static bool probe(uint16_t DevAddress);
+ static bool wakePart(uint16_t DevAddress);
+ static bool 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 bool I2C_RegisterWrite(uint8_t address, uint8_t reg, uint8_t data);
+ static uint8_t I2C_RegisterRead(uint8_t address, uint8_t reg);
+
+ typedef struct {
+ const uint8_t reg; // The register to write to
+ uint8_t val; // The value to write to this register
+ const uint8_t pause_ms; //How many ms to pause _after_ writing this reg
+ } I2C_REG;
+ static bool writeRegistersBulk(const uint8_t address, const I2C_REG* registers, const uint8_t registersLength);
+private:
+ static void unlock();
+ static bool lock();
+ static void I2C_Unstick();
+ static SemaphoreHandle_t I2CSemaphore;
+ static StaticSemaphore_t xSemaphoreBuffer;
+};
+
+#endif /* FRTOSI2C_HPP_ */
diff --git a/source/Core/Drivers/LIS2DH12.cpp b/source/Core/Drivers/LIS2DH12.cpp
new file mode 100644
index 00000000..02aad5b6
--- /dev/null
+++ b/source/Core/Drivers/LIS2DH12.cpp
@@ -0,0 +1,43 @@
+/*
+ * LIS2DH12.cpp
+ *
+ * Created on: 27Feb.,2018
+ * Author: Ralim
+ */
+
+#include <array>
+
+#include "LIS2DH12.hpp"
+#include "cmsis_os.h"
+
+static const FRToSI2C::I2C_REG i2c_registers[] = { { LIS_CTRL_REG1, 0x17, 0 }, // 25Hz
+ { LIS_CTRL_REG2, 0b00001000, 0 }, // Highpass filter off
+ { LIS_CTRL_REG3, 0b01100000, 0 }, // Setup interrupt pins
+ { LIS_CTRL_REG4, 0b00001000, 0 }, // Block update mode off, HR on
+ { LIS_CTRL_REG5, 0b00000010, 0 }, //
+ { LIS_CTRL_REG6, 0b01100010, 0 },
+ //Basically setup the unit to run, and enable 4D orientation detection
+ { LIS_INT2_CFG, 0b01111110, 0 }, //setup for movement detection
+ { LIS_INT2_THS, 0x28, 0 }, //
+ { LIS_INT2_DURATION, 64, 0 }, //
+ { LIS_INT1_CFG, 0b01111110, 0 }, //
+ { LIS_INT1_THS, 0x28, 0 }, //
+ { LIS_INT1_DURATION, 64, 0 } };
+
+bool LIS2DH12::initalize() {
+ return FRToSI2C::writeRegistersBulk(LIS2DH_I2C_ADDRESS, i2c_registers, sizeof(i2c_registers) / sizeof(i2c_registers[0]));
+}
+
+void LIS2DH12::getAxisReadings(int16_t &x, int16_t &y, int16_t &z) {
+ std::array<int16_t, 3> sensorData;
+
+ FRToSI2C::Mem_Read(LIS2DH_I2C_ADDRESS, 0xA8, reinterpret_cast<uint8_t*>(sensorData.begin()), sensorData.size() * sizeof(int16_t));
+
+ x = sensorData[0];
+ y = sensorData[1];
+ z = sensorData[2];
+}
+
+bool LIS2DH12::detect() {
+ return FRToSI2C::probe(LIS2DH_I2C_ADDRESS);
+}
diff --git a/source/Core/Drivers/LIS2DH12.hpp b/source/Core/Drivers/LIS2DH12.hpp
new file mode 100644
index 00000000..e4cccd48
--- /dev/null
+++ b/source/Core/Drivers/LIS2DH12.hpp
@@ -0,0 +1,41 @@
+/*
+ * LIS2DH12.hpp
+ *
+ * Created on: 27Feb.,2018
+ * Author: Ralim
+ */
+
+#ifndef LIS2DH12_HPP_
+#define LIS2DH12_HPP_
+#include "I2C_Wrapper.hpp"
+#include "LIS2DH12_defines.hpp"
+#include "BSP.h"
+
+class LIS2DH12 {
+public:
+ static bool detect();
+ static bool initalize();
+ //1 = rh, 2,=lh, 8=flat
+ static Orientation getOrientation() {
+#ifdef LIS_ORI_FLIP
+ uint8_t val = (FRToSI2C::I2C_RegisterRead(LIS2DH_I2C_ADDRESS,
+ LIS_INT2_SRC) >> 2);
+ if (val == 8)
+ val = 3;
+ else if (val == 1)
+ val = 1;
+ else if (val == 2)
+ val = 0;
+ else
+ val = 3;
+ return static_cast<Orientation>(val);
+#else
+ return static_cast<Orientation>((FRToSI2C::I2C_RegisterRead(LIS2DH_I2C_ADDRESS,LIS_INT2_SRC) >> 2) - 1);
+#endif
+ }
+ static void getAxisReadings(int16_t& x, int16_t& y, int16_t& z);
+
+private:
+};
+
+#endif /* LIS2DH12_HPP_ */
diff --git a/source/Core/Drivers/LIS2DH12_defines.hpp b/source/Core/Drivers/LIS2DH12_defines.hpp
new file mode 100644
index 00000000..c233aca5
--- /dev/null
+++ b/source/Core/Drivers/LIS2DH12_defines.hpp
@@ -0,0 +1,28 @@
+/*
+ * LIS2DH12_defines.hpp
+ *
+ * Created on: 27Feb.,2018
+ * Author: Ralim
+ */
+
+#ifndef LIS2DH12_DEFINES_HPP_
+#define LIS2DH12_DEFINES_HPP_
+
+
+#define LIS2DH_I2C_ADDRESS (25<<1)
+
+#define LIS_CTRL_REG1 0x20|0x80
+#define LIS_CTRL_REG2 0x21|0x80
+#define LIS_CTRL_REG3 0x22|0x80
+#define LIS_CTRL_REG4 0x23|0x80
+#define LIS_CTRL_REG5 0x24|0x80
+#define LIS_CTRL_REG6 0x25|0x80
+#define LIS_INT1_CFG 0xB0|0x80
+#define LIS_INT2_CFG 0xB4|0x80
+#define LIS_INT1_DURATION 0x33|0x80
+#define LIS_INT1_THS 0x32|0x80
+#define LIS_INT1_SRC 0x31|0x80
+#define LIS_INT2_DURATION 0x37|0x80
+#define LIS_INT2_THS 0x36|0x80
+#define LIS_INT2_SRC 0x35|0x80
+#endif /* LIS2DH12_DEFINES_HPP_ */
diff --git a/source/Core/Drivers/MMA8652FC.cpp b/source/Core/Drivers/MMA8652FC.cpp
new file mode 100644
index 00000000..fa8b91fe
--- /dev/null
+++ b/source/Core/Drivers/MMA8652FC.cpp
@@ -0,0 +1,63 @@
+/*
+ * MMA8652FC.cpp
+ *
+ * Created on: 31Aug.,2017
+ * Author: Ben V. Brown
+ */
+
+#include <array>
+
+#include "MMA8652FC.hpp"
+#include "cmsis_os.h"
+
+
+static const FRToSI2C::I2C_REG i2c_registers[] = { { CTRL_REG2, 0, 0 }, //Normal mode
+ { CTRL_REG2, 0x40, 2 }, // Reset all registers to POR values
+ { FF_MT_CFG_REG, 0x78, 0 }, // Enable motion detection for X, Y, Z axis, latch disabled
+ { PL_CFG_REG, 0x40, 0 }, //Enable the orientation detection
+ { PL_COUNT_REG, 200, 0 }, //200 count debounce
+ { PL_BF_ZCOMP_REG, 0b01000111, 0 }, //Set the threshold to 42 degrees
+ { P_L_THS_REG, 0b10011100, 0 }, //Up the trip angles
+ { CTRL_REG4, 0x01 | (1 << 4), 0 }, // Enable dataready interrupt & orientation interrupt
+ { CTRL_REG5, 0x01, 0 }, // Route data ready interrupts to INT1 ->PB5 ->EXTI5, leaving orientation routed to INT2
+ { CTRL_REG2, 0x12, 0 }, //Set maximum resolution oversampling
+ { XYZ_DATA_CFG_REG, (1 << 4), 0 }, //select high pass filtered data
+ { HP_FILTER_CUTOFF_REG, 0x03, 0 }, //select high pass filtered data
+ { CTRL_REG1, 0x19, 0 } // ODR=12 Hz, Active mode
+};
+
+bool MMA8652FC::initalize() {
+ return FRToSI2C::writeRegistersBulk(MMA8652FC_I2C_ADDRESS, i2c_registers, sizeof(i2c_registers) / sizeof(i2c_registers[0]));
+
+}
+
+Orientation MMA8652FC::getOrientation() {
+ //First read the PL_STATUS register
+ uint8_t plStatus = FRToSI2C::I2C_RegisterRead(MMA8652FC_I2C_ADDRESS,
+ PL_STATUS_REG);
+ if ((plStatus & 0b10000000) == 0b10000000) {
+ plStatus >>= 1; //We don't need the up/down bit
+ plStatus &= 0x03; //mask to the two lower bits
+
+ //0 == left handed
+ //1 == right handed
+
+ return static_cast<Orientation>(plStatus);
+ }
+
+ return ORIENTATION_FLAT;
+}
+
+void MMA8652FC::getAxisReadings(int16_t &x, int16_t &y, int16_t &z) {
+ std::array<int16_t, 3> sensorData;
+
+ FRToSI2C::Mem_Read(MMA8652FC_I2C_ADDRESS, OUT_X_MSB_REG, reinterpret_cast<uint8_t*>(sensorData.begin()), sensorData.size() * sizeof(int16_t));
+
+ x = static_cast<int16_t>(__builtin_bswap16(*reinterpret_cast<uint16_t*>(&sensorData[0])));
+ y = static_cast<int16_t>(__builtin_bswap16(*reinterpret_cast<uint16_t*>(&sensorData[1])));
+ z = static_cast<int16_t>(__builtin_bswap16(*reinterpret_cast<uint16_t*>(&sensorData[2])));
+}
+
+bool MMA8652FC::detect() {
+ return FRToSI2C::probe(MMA8652FC_I2C_ADDRESS);
+}
diff --git a/source/Core/Drivers/MMA8652FC.hpp b/source/Core/Drivers/MMA8652FC.hpp
new file mode 100644
index 00000000..9b5f44d1
--- /dev/null
+++ b/source/Core/Drivers/MMA8652FC.hpp
@@ -0,0 +1,27 @@
+/*
+ * MMA8652FC.h
+ *
+ * Created on: 31Aug.,2017
+ * Author: Ben V. Brown
+ */
+
+#ifndef MMA8652FC_HPP_
+#define MMA8652FC_HPP_
+#include "MMA8652FC_defines.h"
+#include "I2C_Wrapper.hpp"
+#include "BSP.h"
+
+class MMA8652FC {
+
+public:
+ //Returns true if this accelerometer is detected
+ static bool detect();
+ //Init any internal state
+ static bool initalize();
+ static Orientation getOrientation(); // Reads the I2C register and returns the orientation (true == left)
+ static void getAxisReadings(int16_t &x, int16_t &y, int16_t &z);
+
+private:
+};
+
+#endif /* MMA8652FC_HPP_ */
diff --git a/source/Core/Drivers/MMA8652FC_defines.h b/source/Core/Drivers/MMA8652FC_defines.h
new file mode 100644
index 00000000..d3526c02
--- /dev/null
+++ b/source/Core/Drivers/MMA8652FC_defines.h
@@ -0,0 +1,124 @@
+/*
+ * MMA8652FC_defines.h
+ *
+ * Created on: 31Aug.,2017
+ * Author: Ben V. Brown
+ */
+
+#ifndef MMA8652FC_DEFINES_H_
+#define MMA8652FC_DEFINES_H_
+
+//--------------MMA8652 Registers-------------------------------------------//
+
+#define STATUS_REG 0x00 // STATUS Register
+
+#define OUT_X_MSB_REG 0x01 // [7:0] are 8 MSBs of the 14-bit X-axis sample
+#define OUT_X_LSB_REG 0x02 // [7:2] are the 6 LSB of 14-bit X-axis sample
+#define OUT_Y_MSB_REG 0x03 // [7:0] are 8 MSBs of the 14-bit Y-axis sample
+#define OUT_Y_LSB_REG 0x04 // [7:2] are the 6 LSB of 14-bit Y-axis sample
+#define OUT_Z_MSB_REG 0x05 // [7:0] are 8 MSBs of the 14-bit Z-axis sample
+#define OUT_Z_LSB_REG 0x06 // [7:2] are the 6 LSB of 14-bit Z-axis sample
+
+#define F_SETUP_REG 0x09 // F_SETUP FIFO Setup Register
+#define TRIG_CFG_REG 0x0A // TRIG_CFG Map of FIFO data capture events
+#define SYSMOD_REG 0x0B // SYSMOD System Mode Register
+#define INT_SOURCE_REG 0x0C // INT_SOURCE System Interrupt Status Register
+#define WHO_AM_I_REG 0x0D // WHO_AM_I Device ID Register
+#define XYZ_DATA_CFG_REG 0x0E // XYZ_DATA_CFG Sensor Data Configuration Register
+#define HP_FILTER_CUTOFF_REG 0x0F // HP_FILTER_CUTOFF High Pass Filter Register
+
+#define PL_STATUS_REG 0x10 // PL_STATUS Portrait/Landscape Status Register
+#define PL_CFG_REG 0x11 // PL_CFG Portrait/Landscape Configuration Register
+#define PL_COUNT_REG 0x12 // PL_COUNT Portrait/Landscape Debounce Register
+#define PL_BF_ZCOMP_REG 0x13 // PL_BF_ZCOMP Back/Front and Z Compensation Register
+#define P_L_THS_REG 0x14 // P_L_THS Portrait to Landscape Threshold Register
+
+#define FF_MT_CFG_REG 0x15 // FF_MT_CFG Freefall and Motion Configuration Register
+#define FF_MT_SRC_REG 0x16 // FF_MT_SRC Freefall and Motion Source Register
+#define FF_MT_THS_REG 0x17 // FF_MT_THS Freefall and Motion Threshold Register
+#define FF_MT_COUNT_REG 0x18 // FF_MT_COUNT Freefall Motion Count Register
+
+#define TRANSIENT_CFG_REG 0x1D // TRANSIENT_CFG Transient Configuration Register
+#define TRANSIENT_SRC_REG 0x1E // TRANSIENT_SRC Transient Source Register
+#define TRANSIENT_THS_REG 0x1F // TRANSIENT_THS Transient Threshold Register
+#define TRANSIENT_COUNT_REG 0x20 // TRANSIENT_COUNT Transient Debounce Counter Register
+
+#define PULSE_CFG_REG 0x21 // PULSE_CFG Pulse Configuration Register
+#define PULSE_SRC_REG 0x22 // PULSE_SRC Pulse Source Register
+#define PULSE_THSX_REG 0x23 // PULSE_THS XYZ Pulse Threshold Registers
+#define PULSE_THSY_REG 0x24
+#define PULSE_THSZ_REG 0x25
+#define PULSE_TMLT_REG 0x26 // PULSE_TMLT Pulse Time Window Register
+#define PULSE_LTCY_REG 0x27 // PULSE_LTCY Pulse Latency Timer Register
+#define PULSE_WIND_REG 0x28 // PULSE_WIND Second Pulse Time Window Register
+
+#define ASLP_COUNT_REG 0x29 // ASLP_COUNT Auto Sleep Inactivity Timer Register
+
+#define CTRL_REG1 0x2A // CTRL_REG1 System Control 1 Register
+#define CTRL_REG2 0x2B // CTRL_REG2 System Control 2 Register
+#define CTRL_REG3 0x2C // CTRL_REG3 Interrupt Control Register
+#define CTRL_REG4 0x2D // CTRL_REG4 Interrupt Enable Register
+#define CTRL_REG5 0x2E // CTRL_REG5 Interrupt Configuration Register
+
+#define OFF_X_REG 0x2F // XYZ Offset Correction Registers
+#define OFF_Y_REG 0x30
+#define OFF_Z_REG 0x31
+
+//MMA8652FC 7-bit I2C address
+
+#define MMA8652FC_I2C_ADDRESS (0x1D<<1)
+
+//MMA8652FC Sensitivity
+
+#define SENSITIVITY_2G 1024
+#define SENSITIVITY_4G 512
+#define SENSITIVITY_8G 256
+
+#define STATUS_REG 0x00
+#define X_MSB_REG 0X01
+#define X_LSB_REG 0X02
+#define Y_MSB_REG 0X03
+#define Y_LSB_REG 0X04
+#define Z_MSB_REG 0X05
+#define Z_LSB_REG 0X06
+
+#define TRIG_CFG 0X0A
+#define SYSMOD 0X0B
+#define INT_SOURCE 0X0C
+#define DEVICE_ID 0X0D
+
+//-----STATUS_REG(0X00)-----Bit Define----------------------------------------//
+#define ZYXDR_BIT 0X08
+//----XYZ_DATA_CFG_REG(0xE)-Bit Define----------------------------------------//
+#define FS_MASK 0x03
+#define FULL_SCALE_2G 0x00 //2g=0x0,4g=0x1,8g=0x2
+#define FULL_SCALE_4G 0x01
+#define FULL_SCALE_8G 0x02
+//---------CTRL_REG1(0X2A)Bit Define------------------------------------------//
+#define ACTIVE_MASK 1<<0 //bit0
+#define DR_MASK 0x38 //bit D5,D4,D3
+#define FHZ800 0x0 //800hz
+#define FHZ400 0x1 //400hz
+#define FHZ200 0x2 //200hz
+#define FHZ100 0x3 //100hz
+#define FHZ50 0x4 //50hz
+#define FHZ2 0x5 //12.5hz
+#define FHZ1 0x6 //6.25hz
+#define FHZ0 0x7 //1.563hz
+
+//---------CTRL_REG2(0X2B)Bit Define------------------------------------------//
+#define MODS_MASK 0x03 //Oversampling Mode 4
+#define Normal_Mode 0x0 //Normal=0,Low Noise Low Power MODS=1,
+//HI RESOLUTION=2,LOW POWER MODS = 11
+//----CTRL_REG4---Interrupt Enable BIT ---------------------------------------//
+//0 interrupt is disabled (default)
+//1 interrupt is enabled
+#define INT_EN_ASLP 1<<7 //Auto-SLEEP/WAKE Interrupt Enable
+#define INT_EN_FIFO 1<<6 //FIFO Interrupt Enable
+#define INT_EN_TRANS 1<<5 //Transient Interrupt Enable
+#define INT_EN_LNDPRT 1<<4 //Orientation(Landscape/Portrait)Interrupt Enable
+#define INT_EN_PULSE 1<<3 //Pulse Detection Interrupt Enable
+#define INT_EN_FF_MT 1<<2 //Freefall/Motion Interrupt Enable
+#define INT_EN_DRDY 1<<0 //Data Ready Interrupt Enable
+
+#endif /* MMA8652FC_DEFINES_H_ */
diff --git a/source/Core/Drivers/MSA301.cpp b/source/Core/Drivers/MSA301.cpp
new file mode 100644
index 00000000..ceaf4522
--- /dev/null
+++ b/source/Core/Drivers/MSA301.cpp
@@ -0,0 +1,50 @@
+/*
+ * MSA301.cpp
+ *
+ * Created on: 3 Jan 2021
+ * Author: Ralim
+ */
+
+#include <MSA301.h>
+#include "MSA301_defines.h"
+#define MSA301_I2C_ADDRESS 0x4C
+bool MSA301::detect() {
+ return FRToSI2C::probe(MSA301_I2C_ADDRESS);
+}
+
+static const FRToSI2C::I2C_REG i2c_registers[] = { //
+ //
+ { MSA301_REG_ODR, 0b00001000, 1 }, //X/Y/Z enabled @ 250Hz
+ { MSA301_REG_POWERMODE, 0b0001001, 1 }, // Normal mode
+ { MSA301_REG_RESRANGE, 0b00000001, 0 }, // 14bit resolution @ 4G range
+ { MSA301_REG_ORIENT_HY, 0b01000000, 0 }, // 4*62.5mg hyst, no blocking, symmetrical
+ { MSA301_REG_INTSET0, 1 << 6, 0 }, // Turn on orientation detection (by enabling its interrupt)
+
+ };
+
+bool MSA301::initalize() {
+ return FRToSI2C::writeRegistersBulk(MSA301_I2C_ADDRESS, i2c_registers, sizeof(i2c_registers) / sizeof(i2c_registers[0]));
+}
+
+Orientation MSA301::getOrientation() {
+ uint8_t temp = 0;
+ FRToSI2C::Mem_Read(MSA301_I2C_ADDRESS, MSA301_REG_ORIENT_STATUS, &temp, 1);
+ switch (temp) {
+ case 112:
+ return Orientation::ORIENTATION_LEFT_HAND;
+ case 96:
+ return Orientation::ORIENTATION_RIGHT_HAND;
+ default:
+ return Orientation::ORIENTATION_FLAT;
+ }
+}
+
+void MSA301::getAxisReadings(int16_t &x, int16_t &y, int16_t &z) {
+ uint8_t temp[6];
+ //Bulk read all 6 regs
+ FRToSI2C::Mem_Read(MSA301_I2C_ADDRESS, MSA301_REG_OUT_X_L, temp, 6);
+ x = int16_t(((int16_t) temp[1]) << 8 | temp[0]) >> 2;
+ y = int16_t(((int16_t) temp[3]) << 8 | temp[2]) >> 2;
+ z = int16_t(((int16_t) temp[5]) << 8 | temp[4]) >> 2;
+
+}
diff --git a/source/Core/Drivers/MSA301.h b/source/Core/Drivers/MSA301.h
new file mode 100644
index 00000000..b77e500d
--- /dev/null
+++ b/source/Core/Drivers/MSA301.h
@@ -0,0 +1,27 @@
+/*
+ * MSA301.h
+ *
+ * Created on: 3 Jan 2021
+ * Author: Ralim
+ */
+
+#ifndef DRIVERS_MSA301_H_
+#define DRIVERS_MSA301_H_
+#include "I2C_Wrapper.hpp"
+#include "BSP.h"
+
+class MSA301 {
+public:
+ //Returns true if this accelerometer is detected
+ static bool detect();
+ //Init any internal state
+ static bool initalize();
+ // Reads the I2C register and returns the orientation
+ static Orientation getOrientation();
+ //Return the x/y/z axis readings as signed int16's
+ static void getAxisReadings(int16_t &x, int16_t &y, int16_t &z);
+
+private:
+};
+
+#endif /* DRIVERS_MSA301_H_ */
diff --git a/source/Core/Drivers/MSA301_defines.h b/source/Core/Drivers/MSA301_defines.h
new file mode 100644
index 00000000..12ebc62c
--- /dev/null
+++ b/source/Core/Drivers/MSA301_defines.h
@@ -0,0 +1,34 @@
+/*
+ * MSA301_defines.h
+ *
+ * Created on: 3 Jan 2021
+ * Author: Ralim
+ */
+
+#ifndef DRIVERS_MSA301_DEFINES_H_
+#define DRIVERS_MSA301_DEFINES_H_
+//Definitions from Adafruit <3
+
+#define MSA301_REG_PARTID 0x01 ///< Register that contains the part ID
+#define MSA301_REG_OUT_X_L 0x02 ///< Register address for X axis lower byte
+#define MSA301_REG_OUT_X_H 0x03 ///< Register address for X axis higher byte
+#define MSA301_REG_OUT_Y_L 0x04 ///< Register address for Y axis lower byte
+#define MSA301_REG_OUT_Y_H 0x05 ///< Register address for Y axis higher byte
+#define MSA301_REG_OUT_Z_L 0x06 ///< Register address for Z axis lower byte
+#define MSA301_REG_OUT_Z_H 0x07 ///< Register address for Z axis higher byte
+#define MSA301_REG_MOTIONINT 0x09 ///< Register address for motion interrupt
+#define MSA301_REG_DATAINT 0x0A ///< Register address for data interrupt
+#define MSA301_REG_CLICKSTATUS 0x0B ///< Register address for click/doubleclick status
+#define MSA301_REG_RESRANGE 0x0F ///< Register address for resolution range
+#define MSA301_REG_ODR 0x10 ///< Register address for data rate setting
+#define MSA301_REG_POWERMODE 0x11 ///< Register address for power mode setting
+#define MSA301_REG_INTSET0 0x16 ///< Register address for interrupt setting #0
+#define MSA301_REG_INTSET1 0x17 ///< Register address for interrupt setting #1
+#define MSA301_REG_INTMAP0 0x19 ///< Register address for interrupt map #0
+#define MSA301_REG_INTMAP1 0x1A ///< Register address for interrupt map #1
+#define MSA301_REG_TAPDUR 0x2A ///< Register address for tap duration
+#define MSA301_REG_TAPTH 0x2B ///< Register address for tap threshold
+#define MSA301_REG_ORIENT_HY 0x2C ///< Register address for orientation Hysteresis
+#define MSA301_REG_ORIENT_STATUS 0x0C ///< Register address for orientation hysteresis
+
+#endif /* DRIVERS_MSA301_DEFINES_H_ */
diff --git a/source/Core/Drivers/OLED.cpp b/source/Core/Drivers/OLED.cpp
new file mode 100644
index 00000000..776aa7b5
--- /dev/null
+++ b/source/Core/Drivers/OLED.cpp
@@ -0,0 +1,489 @@
+/*
+ * OLED.cpp
+ *
+ * Created on: 29Aug.,2017
+ * Author: Ben V. Brown
+ */
+
+#include <string.h>
+#include <OLED.hpp>
+#include <stdlib.h>
+#include "Translation.h"
+#include "cmsis_os.h"
+#include "../../configuration.h"
+
+const uint8_t *OLED::currentFont; // Pointer to the current font used for
+// rendering to the buffer
+uint8_t *OLED::firstStripPtr; // Pointers to the strips to allow for buffer
+// having extra content
+uint8_t *OLED::secondStripPtr; // Pointers to the strips
+bool OLED::inLeftHandedMode; // Whether the screen is in left or not (used for
+// offsets in GRAM)
+OLED::DisplayState OLED::displayState;
+uint8_t OLED::fontWidth, OLED::fontHeight;
+int16_t OLED::cursor_x, OLED::cursor_y;
+bool OLED::initDone = false;
+uint8_t OLED::displayOffset;
+uint8_t OLED::screenBuffer[16 + (OLED_WIDTH * 2) + 10]; // The data buffer
+uint8_t OLED::secondFrameBuffer[OLED_WIDTH * 2];
+
+/*Setup params for the OLED screen*/
+/*http://www.displayfuture.com/Display/datasheet/controller/SSD1307.pdf*/
+/*All commands are prefixed with 0x80*/
+/*Data packets are prefixed with 0x40*/
+FRToSI2C::I2C_REG OLED_Setup_Array[] = {
+/**/
+{ 0x80, 0xAE, 0 }, /*Display off*/
+{ 0x80, 0xD5, 0 }, /*Set display clock divide ratio / osc freq*/
+{ 0x80, 0x52, 0 }, /*Divide ratios*/
+{ 0x80, 0xA8, 0 }, /*Set Multiplex Ratio*/
+{ 0x80, 0x0F, 0 }, /*16 == max brightness,39==dimmest*/
+{ 0x80, 0xC0, 0 }, /*Set COM Scan direction*/
+{ 0x80, 0xD3, 0 }, /*Set vertical Display offset*/
+{ 0x80, 0x00, 0 }, /*0 Offset*/
+{ 0x80, 0x40, 0 }, /*Set Display start line to 0*/
+{ 0x80, 0xA0, 0 }, /*Set Segment remap to normal*/
+{ 0x80, 0x8D, 0 }, /*Charge Pump*/
+{ 0x80, 0x14, 0 }, /*Charge Pump settings*/
+{ 0x80, 0xDA, 0 }, /*Set VCOM Pins hardware config*/
+{ 0x80, 0x02, 0 }, /*Combination 2*/
+{ 0x80, 0x81, 0 }, /*Contrast*/
+{ 0x80, 0x33, 0 }, /*^51*/
+{ 0x80, 0xD9, 0 }, /*Set pre-charge period*/
+{ 0x80, 0xF1, 0 }, /*Pre charge period*/
+{ 0x80, 0xDB, 0 }, /*Adjust VCOMH regulator ouput*/
+{ 0x80, 0x30, 0 }, /*VCOM level*/
+{ 0x80, 0xA4, 0 }, /*Enable the display GDDR*/
+{ 0x80, 0XA6, 0 }, /*Normal display*/
+{ 0x80, 0x20, 0 }, /*Memory Mode*/
+{ 0x80, 0x00, 0 }, /*Wrap memory*/
+{ 0x80, 0xAF, 0 }, /*Display on*/
+};
+// Setup based on the SSD1307 and modified for the SSD1306
+
+const uint8_t REFRESH_COMMANDS[17] = { 0x80, 0xAF, 0x80, 0x21, 0x80, 0x20, 0x80, 0x7F, 0x80, 0xC0, 0x80, 0x22, 0x80, 0x00, 0x80, 0x01, 0x40 };
+
+/*
+ * Animation timing function that follows a bezier curve.
+ * @param t A given percentage value [0..<100]
+ * Returns a new percentage value with ease in and ease out.
+ * Original floating point formula: t * t * (3.0f - 2.0f * t);
+ */
+static uint8_t easeInOutTiming(uint8_t t) {
+ return t * t * (300 - 2 * t) / 10000;
+}
+
+/*
+ * Returns the value between a and b, using a percentage value t.
+ * @param a The value associated with 0%
+ * @param b The value associated with 100%
+ * @param t The percentage [0..<100]
+ */
+static uint8_t lerp(uint8_t a, uint8_t b, uint8_t t) {
+ return a + t * (b - a) / 100;
+}
+
+void OLED::initialize() {
+ cursor_x = cursor_y = 0;
+ currentFont = USER_FONT_12;
+ fontWidth = 12;
+ inLeftHandedMode = false;
+ firstStripPtr = &screenBuffer[FRAMEBUFFER_START];
+ secondStripPtr = &screenBuffer[FRAMEBUFFER_START + OLED_WIDTH];
+ fontHeight = 16;
+ displayOffset = 0;
+ memcpy(&screenBuffer[0], &REFRESH_COMMANDS[0], sizeof(REFRESH_COMMANDS));
+
+ // Set the display to be ON once the settings block is sent and send the
+ // initialisation data to the OLED.
+
+ for (int tries = 0; tries < 10; tries++) {
+ if (FRToSI2C::writeRegistersBulk(DEVICEADDR_OLED, OLED_Setup_Array, sizeof(OLED_Setup_Array) / sizeof(OLED_Setup_Array[0]))) {
+ return;
+ }
+ }
+ setDisplayState(DisplayState::ON);
+ initDone = true;
+}
+void OLED::setFramebuffer(uint8_t *buffer) {
+ if (buffer == NULL) {
+ firstStripPtr = &screenBuffer[FRAMEBUFFER_START];
+ secondStripPtr = &screenBuffer[FRAMEBUFFER_START + OLED_WIDTH];
+ return;
+ }
+
+ firstStripPtr = &buffer[0];
+ secondStripPtr = &buffer[OLED_WIDTH];
+}
+
+/*
+ * Prints a char to the screen.
+ * UTF font handling is done using the two input chars.
+ * Precursor is the command char that is used to select the table.
+ */
+void OLED::drawChar(char c) {
+ if (c == '\x01' && cursor_y == 0) { // 0x01 is used as new line char
+ cursor_x = 0;
+ cursor_y = 8;
+ return;
+ } else if (c == 0) {
+ return;
+ }
+ uint16_t index = c - 2; //First index is \x02
+ uint8_t *charPointer;
+ charPointer = ((uint8_t*) currentFont) + ((fontWidth * (fontHeight / 8)) * index);
+ drawArea(cursor_x, cursor_y, fontWidth, fontHeight, charPointer);
+ cursor_x += fontWidth;
+}
+
+/*
+ * Draws a one pixel wide scrolling indicator. y is the upper vertical position
+ * of the indicator in pixels (0..<16).
+ */
+void OLED::drawScrollIndicator(uint8_t y, uint8_t height) {
+ union u_type {
+ uint16_t whole;
+ uint8_t strips[2];
+ } column;
+
+ column.whole = (1 << height) - 1;
+ column.whole <<= y;
+
+ // Draw a one pixel wide bar to the left with a single pixel as
+ // the scroll indicator.
+ fillArea(OLED_WIDTH - 1, 0, 1, 8, column.strips[0]);
+ fillArea(OLED_WIDTH - 1, 8, 1, 8, column.strips[1]);
+}
+
+/**
+ * Plays a transition animation between two framebuffers.
+ * @param forwardNavigation Direction of the navigation animation.
+ *
+ * If forward is true, this displays a forward navigation to the second framebuffer contents.
+ * Otherwise a rewinding navigation animation is shown to the second framebuffer contents.
+ */
+void OLED::transitionSecondaryFramebuffer(bool forwardNavigation) {
+ uint8_t *firstBackStripPtr = &secondFrameBuffer[0];
+ uint8_t *secondBackStripPtr = &secondFrameBuffer[OLED_WIDTH];
+
+ uint32_t totalDuration = 50; // 500ms
+ uint32_t duration = 0;
+ uint32_t start = xTaskGetTickCount();
+ uint8_t offset = 0;
+
+ while (duration <= totalDuration) {
+ duration = xTaskGetTickCount() - start;
+ uint8_t progress = duration * TICKS_SECOND / totalDuration;
+ progress = easeInOutTiming(progress);
+ progress = lerp(0, OLED_WIDTH, progress);
+ if (progress > OLED_WIDTH) {
+ progress = OLED_WIDTH;
+ }
+
+// When forward, current contents move to the left out.
+// Otherwise the contents move to the right out.
+ uint8_t oldStart = forwardNavigation ? 0 : progress;
+ uint8_t oldPrevious = forwardNavigation ? progress - offset : offset;
+
+// Content from the second framebuffer moves in from the right (forward)
+// or from the left (not forward).
+ uint8_t newStart = forwardNavigation ? OLED_WIDTH - progress : 0;
+ uint8_t newEnd = forwardNavigation ? 0 : OLED_WIDTH - progress;
+
+ offset = progress;
+
+ memmove(&firstStripPtr[oldStart], &firstStripPtr[oldPrevious],
+ OLED_WIDTH - progress);
+ memmove(&secondStripPtr[oldStart], &secondStripPtr[oldPrevious],
+ OLED_WIDTH - progress);
+
+ memmove(&firstStripPtr[newStart], &firstBackStripPtr[newEnd], progress);
+ memmove(&secondStripPtr[newStart], &secondBackStripPtr[newEnd], progress);
+
+ refresh();
+ osDelay(40);
+ }
+}
+
+void OLED::useSecondaryFramebuffer(bool useSecondary) {
+ if (useSecondary) {
+ setFramebuffer(secondFrameBuffer);
+ } else {
+ setFramebuffer(NULL);
+ }
+}
+
+void OLED::setRotation(bool leftHanded) {
+#ifdef OLED_FLIP
+ leftHanded = !leftHanded;
+#endif
+ if (inLeftHandedMode == leftHanded) {
+ return;
+ }
+
+ // send command struct again with changes
+ if (leftHanded) {
+ OLED_Setup_Array[5].val = 0xC8; // c1?
+ OLED_Setup_Array[9].val = 0xA1;
+ } else {
+ OLED_Setup_Array[5].val = 0xC0;
+ OLED_Setup_Array[9].val = 0xA0;
+ }
+ FRToSI2C::writeRegistersBulk(DEVICEADDR_OLED, OLED_Setup_Array, sizeof(OLED_Setup_Array) / sizeof(OLED_Setup_Array[0]));
+
+ inLeftHandedMode = leftHanded;
+
+ screenBuffer[5] = inLeftHandedMode ? 0 : 32; // display is shifted by 32 in left handed
+ // mode as driver ram is 128 wide
+ screenBuffer[7] = inLeftHandedMode ? 95 : 0x7F; // End address of the ram segment we are writing to (96 wide)
+ screenBuffer[9] = inLeftHandedMode ? 0xC8 : 0xC0;
+}
+
+// print a string to the current cursor location
+void OLED::print(const char *str) {
+ while (str[0]) {
+ drawChar(str[0]);
+ str++;
+ }
+}
+
+void OLED::setFont(uint8_t fontNumber) {
+ if (fontNumber == 1) {
+// small font
+ currentFont = USER_FONT_6x8;
+ fontHeight = 8;
+ fontWidth = 6;
+ } else if (fontNumber == 2) {
+ currentFont = ExtraFontChars;
+ fontHeight = 16;
+ fontWidth = 12;
+ } else {
+ currentFont = USER_FONT_12;
+ fontHeight = 16;
+ fontWidth = 12;
+ }
+}
+uint8_t OLED::getFont() {
+ if (currentFont == USER_FONT_6x8)
+ return 1;
+ else if (currentFont == ExtraFontChars)
+ return 2;
+ else
+ return 0;
+}
+inline void stripLeaderZeros(char *buffer, uint8_t places) {
+ //Removing the leading zero's by swapping them to SymbolSpace
+ // Stop 1 short so that we dont blank entire number if its zero
+ for (int i = 0; i < (places - 1); i++) {
+ if (buffer[i] == 2) {
+ buffer[i] = SymbolSpace[0];
+ } else {
+ return;
+ }
+ }
+}
+// maximum places is 5
+void OLED::printNumber(uint16_t number, uint8_t places, bool noLeaderZeros) {
+ char buffer[7] = { 0 };
+
+ if (places >= 5) {
+ buffer[5] = 2 + number % 10;
+ number /= 10;
+ }
+ if (places > 4) {
+ buffer[4] = 2 + number % 10;
+ number /= 10;
+ }
+
+ if (places > 3) {
+ buffer[3] = 2 + number % 10;
+ number /= 10;
+ }
+
+ if (places > 2) {
+ buffer[2] = 2 + number % 10;
+ number /= 10;
+ }
+
+ if (places > 1) {
+ buffer[1] = 2 + number % 10;
+ number /= 10;
+ }
+
+ buffer[0] = 2 + number % 10;
+ if (noLeaderZeros)
+ stripLeaderZeros(buffer, places);
+ print(buffer);
+}
+
+void OLED::debugNumber(int32_t val) {
+ if (abs(val) > 99999) {
+ OLED::print(SymbolSpace); // out of bounds
+ return;
+ }
+ if (val >= 0) {
+ OLED::print(SymbolSpace);
+ OLED::printNumber(val, 5);
+ } else {
+ OLED::print(SymbolMinus);
+ OLED::printNumber(-val, 5);
+ }
+}
+
+void OLED::drawSymbol(uint8_t symbolID) {
+ // draw a symbol to the current cursor location
+ setFont(2);
+ drawChar(symbolID + 2);
+ setFont(0);
+}
+
+// Draw an area, but y must be aligned on 0/8 offset
+void OLED::drawArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uint8_t *ptr) {
+ // Splat this from x->x+wide in two strides
+ if (x <= -wide)
+ return; // cutoffleft
+ if (x > 96)
+ return; // cutoff right
+
+ uint8_t visibleStart = 0;
+ uint8_t visibleEnd = wide;
+
+ // trimming to draw partials
+ if (x < 0) {
+ visibleStart -= x; // subtract negative value == add absolute value
+ }
+ if (x + wide > 96) {
+ visibleEnd = 96 - x;
+ }
+
+ if (y == 0) {
+// Splat first line of data
+ for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
+ firstStripPtr[xx + x] = ptr[xx];
+ }
+ }
+ if (y == 8 || height == 16) {
+// Splat the second line
+ for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
+ secondStripPtr[x + xx] = ptr[xx + (height == 16 ? wide : 0)];
+ }
+ }
+}
+
+// Draw an area, but y must be aligned on 0/8 offset
+// For data which has octets swapped in a 16-bit word.
+void OLED::drawAreaSwapped(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uint8_t *ptr) {
+ // Splat this from x->x+wide in two strides
+ if (x <= -wide)
+ return; // cutoffleft
+ if (x > 96)
+ return; // cutoff right
+
+ uint8_t visibleStart = 0;
+ uint8_t visibleEnd = wide;
+
+ // trimming to draw partials
+ if (x < 0) {
+ visibleStart -= x; // subtract negative value == add absolute value
+ }
+ if (x + wide > 96) {
+ visibleEnd = 96 - x;
+ }
+
+ if (y == 0) {
+ // Splat first line of data
+ for (uint8_t xx = visibleStart; xx < visibleEnd; xx += 2) {
+ firstStripPtr[xx + x] = ptr[xx + 1];
+ firstStripPtr[xx + x + 1] = ptr[xx];
+ }
+ }
+ if (y == 8 || height == 16) {
+ // Splat the second line
+ for (uint8_t xx = visibleStart; xx < visibleEnd; xx += 2) {
+ secondStripPtr[x + xx] = ptr[xx + 1 + (height == 16 ? wide : 0)];
+ secondStripPtr[x + xx + 1] = ptr[xx + (height == 16 ? wide : 0)];
+ }
+ }
+}
+
+void OLED::fillArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uint8_t value) {
+ // Splat this from x->x+wide in two strides
+ if (x <= -wide)
+ return; // cutoffleft
+ if (x > 96)
+ return; // cutoff right
+
+ uint8_t visibleStart = 0;
+ uint8_t visibleEnd = wide;
+
+ // trimming to draw partials
+ if (x < 0) {
+ visibleStart -= x; // subtract negative value == add absolute value
+ }
+ if (x + wide > 96) {
+ visibleEnd = 96 - x;
+ }
+
+ if (y == 0) {
+// Splat first line of data
+ for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
+ firstStripPtr[xx + x] = value;
+ }
+ }
+ if (y == 8 || height == 16) {
+// Splat the second line
+ for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
+ secondStripPtr[x + xx] = value;
+ }
+ }
+}
+
+void OLED::drawFilledRect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, bool clear) {
+ // Draw this in 3 sections
+ // This is basically a N wide version of vertical line
+
+ // Step 1 : Draw in the top few pixels that are not /8 aligned
+ // LSB is at the top of the screen
+ uint8_t mask = 0xFF;
+ if (y0) {
+ mask = mask << (y0 % 8);
+ for (uint8_t col = x0; col < x1; col++)
+ if (clear)
+ firstStripPtr[(y0 / 8) * 96 + col] &= ~mask;
+ else
+ firstStripPtr[(y0 / 8) * 96 + col] |= mask;
+ }
+ // Next loop down the line the total number of solids
+ if (y0 / 8 != y1 / 8)
+ for (uint8_t col = x0; col < x1; col++)
+ for (uint8_t r = (y0 / 8); r < (y1 / 8); r++) {
+ // This gives us the row index r
+ if (clear)
+ firstStripPtr[(r * 96) + col] = 0;
+ else
+ firstStripPtr[(r * 96) + col] = 0xFF;
+ }
+
+ // Finally draw the tail
+ mask = ~(mask << (y1 % 8));
+ for (uint8_t col = x0; col < x1; col++)
+ if (clear)
+ firstStripPtr[(y1 / 8) * 96 + col] &= ~mask;
+ else
+ firstStripPtr[(y1 / 8) * 96 + col] |= mask;
+}
+
+void OLED::drawHeatSymbol(uint8_t state) {
+ // Draw symbol 14
+ // Then draw over it, the bottom 5 pixels always stay. 8 pixels above that are
+ // the levels masks the symbol nicely
+ state /= 31; // 0-> 8 range
+ // Then we want to draw down (16-(5+state)
+ uint8_t cursor_x_temp = cursor_x;
+ drawSymbol(14);
+ drawFilledRect(cursor_x_temp, 0, cursor_x_temp + 12, 2 + (8 - state), true);
+}
+
+bool OLED::isInitDone() {
+ return initDone;
+}
diff --git a/source/Core/Drivers/OLED.hpp b/source/Core/Drivers/OLED.hpp
new file mode 100644
index 00000000..db6a2c52
--- /dev/null
+++ b/source/Core/Drivers/OLED.hpp
@@ -0,0 +1,116 @@
+/*
+ * OLED.hpp
+ *
+ * Created on: 20Jan.,2017
+ * Author: Ben V. Brown <Ralim>
+ * Designed for the SSD1307
+ * Cleared for release for TS100 2017/08/20
+ */
+
+#ifndef OLED_HPP_
+#define OLED_HPP_
+#include <BSP.h>
+#include <stdbool.h>
+#include <string.h>
+#include "I2C_Wrapper.hpp"
+#include "Font.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "FreeRTOS.h"
+#ifdef __cplusplus
+}
+#endif
+#define DEVICEADDR_OLED (0x3c<<1)
+#define OLED_WIDTH 96
+#define OLED_HEIGHT 16
+#define FRAMEBUFFER_START 17
+
+class OLED {
+public:
+
+ enum DisplayState : bool {
+ OFF = false, ON = true
+ };
+
+ static void initialize(); // Startup the I2C coms (brings screen out of reset etc)
+ static bool isInitDone();
+ // Draw the buffer out to the LCD using the DMA Channel
+ static void refresh() {
+ FRToSI2C::Transmit( DEVICEADDR_OLED, screenBuffer,
+ FRAMEBUFFER_START + (OLED_WIDTH * 2));
+ //DMA tx time is ~ 20mS Ensure after calling this you delay for at least 25ms
+ //or we need to goto double buffering
+ }
+
+ static void setDisplayState(DisplayState state) {
+ displayState = state;
+ screenBuffer[1] = (state == ON) ? 0xAF : 0xAE;
+ }
+
+ static void setRotation(bool leftHanded); // Set the rotation for the screen
+ // Get the current rotation of the LCD
+ static bool getRotation() {
+ return inLeftHandedMode;
+ }
+ static int16_t getCursorX() {
+ return cursor_x;
+ }
+ static void print(const char *string); // Draw a string to the current location, with current font
+ // Set the cursor location by pixels
+ static void setCursor(int16_t x, int16_t y) {
+ cursor_x = x;
+ cursor_y = y;
+ }
+ //Set cursor location by chars in current font
+ static void setCharCursor(int16_t x, int16_t y) {
+ cursor_x = x * fontWidth;
+ cursor_y = y * fontHeight;
+ }
+ static void setFont(uint8_t fontNumber); // (Future) Set the font that is being used
+ static uint8_t getFont();
+ static void drawImage(const uint8_t *buffer, uint8_t x, uint8_t width) {
+ drawArea(x, 0, width, 16, buffer);
+ }
+ // Draws an image to the buffer, at x offset from top to bottom (fixed height renders)
+ static void printNumber(uint16_t number, uint8_t places, bool noLeaderZeros = true);
+ // Draws a number at the current cursor location
+ // Clears the buffer
+ static void clearScreen() {
+ memset(firstStripPtr, 0, OLED_WIDTH * 2);
+ }
+ // Draws the battery level symbol
+ static void drawBattery(uint8_t state) {
+ drawSymbol(3 + (state > 10 ? 10 : state));
+ }
+ // Draws a checkbox
+ static void drawCheckbox(bool state) {
+ drawSymbol((state) ? 16 : 17);
+ }
+ static void debugNumber(int32_t val);
+ static void drawSymbol(uint8_t symbolID); //Used for drawing symbols of a predictable width
+ static void drawArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uint8_t *ptr); //Draw an area, but y must be aligned on 0/8 offset
+ static void drawAreaSwapped(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uint8_t *ptr); //Draw an area, but y must be aligned on 0/8 offset
+ static void fillArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uint8_t value); //Fill an area, but y must be aligned on 0/8 offset
+ static void drawFilledRect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, bool clear);
+ static void drawHeatSymbol(uint8_t state);
+ static void drawScrollIndicator(uint8_t p, uint8_t h); // Draws a scrolling position indicator
+ static void transitionSecondaryFramebuffer(bool forwardNavigation);
+ static void useSecondaryFramebuffer(bool useSecondary);
+private:
+ static void drawChar(char c); // Draw a character to a specific location
+ static void setFramebuffer(uint8_t *buffer);
+ static const uint8_t *currentFont; // Pointer to the current font used for rendering to the buffer
+ static uint8_t *firstStripPtr; // Pointers to the strips to allow for buffer having extra content
+ static uint8_t *secondStripPtr; //Pointers to the strips
+ static bool inLeftHandedMode; // Whether the screen is in left or not (used for offsets in GRAM)
+ static bool initDone;
+ static DisplayState displayState;
+ static uint8_t fontWidth, fontHeight;
+ static int16_t cursor_x, cursor_y;
+ static uint8_t displayOffset;
+ static uint8_t screenBuffer[16 + (OLED_WIDTH * 2) + 10]; // The data buffer
+ static uint8_t secondFrameBuffer[OLED_WIDTH * 2];
+};
+
+#endif /* OLED_HPP_ */
diff --git a/source/Core/Drivers/README.md b/source/Core/Drivers/README.md
new file mode 100644
index 00000000..9b5ebf91
--- /dev/null
+++ b/source/Core/Drivers/README.md
@@ -0,0 +1,10 @@
+# Drivers
+
+Drivers are the classes used to represent physical hardware on the board in a more abstract way, that are more complex than just an IO
+
+* OLED Display
+* Accelerometers
+* Button handling logic
+* Tip thermo response modelling
+
+All drivers should be written with minimal hardware assumptions, and defer hardware related logic to the BSP folder where possible \ No newline at end of file
diff --git a/source/Core/Drivers/SC7A20.cpp b/source/Core/Drivers/SC7A20.cpp
new file mode 100644
index 00000000..7bebb29d
--- /dev/null
+++ b/source/Core/Drivers/SC7A20.cpp
@@ -0,0 +1,69 @@
+/*
+ * SC7A20.cpp
+ *
+ * Created on: 18 Sep. 2020
+ * Author: Ralim
+ */
+
+#include <SC7A20.hpp>
+#include <SC7A20_defines.h>
+#include <array>
+
+bool SC7A20::detect() {
+ if (FRToSI2C::probe(SC7A20_ADDRESS)) {
+ //Read chip id to ensure its not an address collision
+ uint8_t id = 0;
+ if (FRToSI2C::Mem_Read(SC7A20_ADDRESS, SC7A20_WHO_AMI_I, &id, 1)) {
+ return id == 0b00010001;
+ }
+ }
+
+ return false;
+}
+
+static const FRToSI2C::I2C_REG i2c_registers[] = { //
+ //
+ { SC7A20_CTRL_REG1, 0b01100111, 0 }, //200Hz, XYZ enabled
+ { SC7A20_CTRL_REG2, 0b00000000, 0 }, //Setup filter to 0x00 ??
+ { SC7A20_CTRL_REG3, 0b00000000, 0 }, //int1 off
+ { SC7A20_CTRL_REG4, 0b01001000, 0 }, //Block mode off,little-endian,2G,High-pres,self test off
+ { SC7A20_CTRL_REG5, 0b00000100, 0 }, //fifo off, D4D on int1
+ { SC7A20_CTRL_REG6, 0x00, 0 }, //INT2 off
+ //Basically setup the unit to run, and enable 4D orientation detection
+ { SC7A20_INT2_CFG, 0b01111110, 0 }, //setup for movement detection
+ { SC7A20_INT2_THS, 0x28, 0 }, //
+ { SC7A20_INT2_DURATION, 64, 0 }, //
+ { SC7A20_INT1_CFG, 0b01111110, 0 }, //
+ { SC7A20_INT1_THS, 0x28, 0 }, //
+ { SC7A20_INT1_DURATION, 64, 0 }
+
+ //
+ };
+bool SC7A20::initalize() {
+ //Setup acceleration readings
+ //2G range
+ //bandwidth = 250Hz
+ //High pass filter on (Slow compensation)
+ //Turn off IRQ output pins
+ //Orientation recognition in symmetrical mode
+ // Hysteresis is set to ~ 16 counts
+ //Theta blocking is set to 0b10
+
+ return FRToSI2C::writeRegistersBulk(SC7A20_ADDRESS, i2c_registers, sizeof(i2c_registers) / sizeof(i2c_registers[0]));
+
+}
+
+void SC7A20::getAxisReadings(int16_t &x, int16_t &y, int16_t &z) {
+ //We can tell the accelerometer to output in LE mode which makes this simple
+ uint16_t sensorData[3] = { 0, 0, 0 };
+
+ if (FRToSI2C::Mem_Read(SC7A20_ADDRESS, SC7A20_OUT_X_L, (uint8_t*) sensorData, 6) == false) {
+ x = y = z = 0;
+ return;
+ }
+ //Shift 6 to make its range ~= the other accelerometers
+ x = sensorData[0];
+ y = sensorData[1];
+ z = sensorData[2];
+
+}
diff --git a/source/Core/Drivers/SC7A20.hpp b/source/Core/Drivers/SC7A20.hpp
new file mode 100644
index 00000000..f58214da
--- /dev/null
+++ b/source/Core/Drivers/SC7A20.hpp
@@ -0,0 +1,33 @@
+/*
+ * BMA223.hpp
+ *
+ * Created on: 18 Sep. 2020
+ * Author: Ralim
+ */
+
+#ifndef CORE_DRIVERS_SC7A20_HPP_
+#define CORE_DRIVERS_SC7A20_HPP_
+#include "I2C_Wrapper.hpp"
+#include "BSP.h"
+#include "SC7A20_defines.h"
+
+class SC7A20 {
+public:
+ static bool detect();
+ static bool initalize();
+ //1 = rh, 2,=lh, 8=flat
+ static Orientation getOrientation() {
+ uint8_t val = ((FRToSI2C::I2C_RegisterRead(SC7A20_ADDRESS, SC7A20_INT2_SOURCE) >> 2) - 1);
+ if (val == 1)
+ return Orientation::ORIENTATION_LEFT_HAND;
+ else if (val == 4 || val == 0)
+ return Orientation::ORIENTATION_RIGHT_HAND;
+ else
+ return Orientation::ORIENTATION_FLAT;
+ }
+ static void getAxisReadings(int16_t &x, int16_t &y, int16_t &z);
+
+private:
+};
+
+#endif /* CORE_DRIVERS_BMA223_HPP_ */
diff --git a/source/Core/Drivers/SC7A20_defines.h b/source/Core/Drivers/SC7A20_defines.h
new file mode 100644
index 00000000..1b74063e
--- /dev/null
+++ b/source/Core/Drivers/SC7A20_defines.h
@@ -0,0 +1,46 @@
+/*
+ * BMA223_defines.h
+ *
+ * Created on: 18 Sep. 2020
+ * Author: Ralim
+ */
+
+#ifndef CORE_DRIVERS_SC7A20_DEFINES_H_
+#define CORE_DRIVERS_SC7A20_DEFINES_H_
+
+#define SC7A20_ADDRESS 0x18<<1
+#define SC7A20_WHO_AMI_I 0x0F
+#define SC7A20_CTRL_REG1 0x20
+#define SC7A20_CTRL_REG2 0x21
+#define SC7A20_CTRL_REG3 0x22
+#define SC7A20_CTRL_REG4 0x23
+#define SC7A20_CTRL_REG5 0x24
+#define SC7A20_CTRL_REG6 0x25
+#define SC7A20_REFERENCE 0x26
+#define SC7A20_STATUS_REG 0x27
+#define SC7A20_OUT_X_L 0x28
+#define SC7A20_OUT_X_H 0x29
+#define SC7A20_OUT_Y_L 0x2A
+#define SC7A20_OUT_Y_H 0x2B
+#define SC7A20_OUT_Z_L 0x2C
+#define SC7A20_OUT_Z_H 0x2D
+#define SC7A20_FIFO_CTRL 0x2E
+#define SC7A20_FIFO_SRC 0x2F
+#define SC7A20_INT1_CFG 0x30
+#define SC7A20_INT1_SOURCE 0x31
+#define SC7A20_INT1_THS 0x32
+#define SC7A20_INT1_DURATION 0x33
+#define SC7A20_INT2_CFG 0x34
+#define SC7A20_INT2_SOURCE 0x35
+#define SC7A20_INT2_THS 0x36
+#define SC7A20_INT2_DURATION 0x37
+#define SC7A20_CLICK_CFG 0x38
+#define SC7A20_CLICK_SRC 0x39
+#define SC7A20_CLICK_THS 0x3A
+#define SC7A20_TIME_LIMIT 0x3B
+#define SC7A20_TIME_LATENCY 0x3C
+#define SC7A20_TIME_WINDOW 0x3D
+#define SC7A20_ACT_THS 0x3E
+#define SC7A20_ACT_DURATION 0x3F
+
+#endif /* CORE_DRIVERS_BMA223_DEFINES_H_ */
diff --git a/source/Core/Drivers/Si7210.cpp b/source/Core/Drivers/Si7210.cpp
new file mode 100644
index 00000000..b3eae4ae
--- /dev/null
+++ b/source/Core/Drivers/Si7210.cpp
@@ -0,0 +1,184 @@
+/*
+ * Si7210.cpp
+ *
+ * Created on: 5 Oct. 2020
+ * Author: Ralim
+ *
+ * This is based on the very nice sample code by Sean Farrelly (@FARLY7)
+ * Over here : https://github.com/FARLY7/si7210-driver
+ *
+ * This class is licensed as MIT to match this code base
+ */
+
+#include <Si7210.h>
+#include "Si7210_defines.h"
+#include "I2C_Wrapper.hpp"
+bool Si7210::detect() {
+ return FRToSI2C::wakePart(SI7210_ADDRESS);
+
+}
+
+bool Si7210::init() {
+ //Turn on auto increment and sanity check ID
+ //Load OTP cal
+
+ uint8_t temp;
+ if (FRToSI2C::Mem_Read(SI7210_ADDRESS, SI7210_REG_ID, &temp, 1)) {
+ // We don't really care what model it is etc, just probing to check its probably this iC
+ if (temp != 0x00 && temp != 0xFF) {
+ temp = 0x00;
+
+ /* Set device and internal driver settings */
+ if (!write_reg( SI7210_CTRL1, (uint8_t) ~SW_LOW4FIELD_MASK, 0)) {
+ return false;
+ }
+
+ /* Disable periodic auto-wakeup by device, and tamper detect. */
+ if ((!write_reg(SI7210_CTRL3, (uint8_t) ~SL_TIMEENA_MASK, 0)))
+ return false;
+
+ /* Disable tamper detection by setting sw_tamper to 63 */
+ if (!write_reg(SI7210_CTRL3, SL_FAST_MASK | SL_TIMEENA_MASK, 63 << 2))
+ return false;
+
+ if (!set_high_range())
+ return false;
+
+ /* Stop the control loop by setting stop bit */
+ if (!write_reg( SI7210_POWER_CTRL, MEAS_MASK | USESTORE_MASK, STOP_MASK)) /* WARNING: Removed USE_STORE MASK */
+ return false;
+
+ /* Use a burst size of 128/4096 samples in FIR and IIR modes */
+ if (!write_reg(SI7210_CTRL4, 0, DF_BURSTSIZE_128 | DF_BW_4096))
+ return false;
+
+ /* Select field strength measurement */
+ if (!write_reg( SI7210_DSPSIGSEL, 0, DSP_SIGSEL_FIELD_MASK))
+ return false;
+
+ return true; //start_periodic_measurement();
+
+ }
+ }
+ return false;
+}
+
+int16_t Si7210::read() {
+ //Read the two regs
+ int16_t temp = 0;
+ if (!get_field_strength(&temp)) {
+ temp = 0;
+ }
+ return temp;
+}
+
+bool Si7210::write_reg(const uint8_t reg, const uint8_t mask, const uint8_t val) {
+ uint8_t temp = 0;
+ if (mask) {
+ if (!read_reg(reg, &temp)) {
+ return false;
+ }
+ temp &= mask;
+ }
+ temp |= val;
+ return FRToSI2C::Mem_Write(SI7210_ADDRESS, reg, &temp, 1);
+}
+
+bool Si7210::read_reg(const uint8_t reg, uint8_t* val) {
+ return FRToSI2C::Mem_Read(SI7210_ADDRESS, reg, val, 1);
+}
+
+bool Si7210::start_periodic_measurement() {
+ /* Enable periodic wakeup */
+ if (!write_reg(SI7210_CTRL3, (uint8_t) ~SL_TIMEENA_MASK, SL_TIMEENA_MASK))
+ return false;
+
+ /* Start measurement */
+ /* Change to ~STOP_MASK with STOP_MASK */
+ return write_reg( SI7210_POWER_CTRL, MEAS_MASK | USESTORE_MASK, 0);
+
+}
+
+bool Si7210::get_field_strength(int16_t* field) {
+ *field = 0;
+ uint8_t val = 0;
+ FRToSI2C::wakePart(SI7210_ADDRESS);
+
+ if (!write_reg( SI7210_POWER_CTRL, MEAS_MASK | USESTORE_MASK, STOP_MASK))
+ return false;
+
+ /* Read most-significant byte */
+ if (!read_reg( SI7210_DSPSIGM, &val))
+ return false;
+ *field = (val & DSP_SIGM_DATA_MASK) << 8;
+
+ /* Read least-significant byte of data */
+ if (!read_reg( SI7210_DSPSIGL, &val))
+ return false;
+
+ *field += val;
+ *field -= 16384U;
+ //field is now a +- measurement
+ //In units of 0.0125 mT
+ // Aka 12.5uT
+ //Clear flags
+ read_reg( SI7210_CTRL1, &val);
+ read_reg( SI7210_CTRL2, &val);
+//Start next one
+
+ /* Use a burst size of 128/4096 samples in FIR and IIR modes */
+ write_reg( SI7210_CTRL4, 0, DF_BURSTSIZE_128 | DF_BW_4096);
+
+ /* Selet field strength measurement */
+ write_reg( SI7210_DSPSIGSEL, 0, DSP_SIGSEL_FIELD_MASK);
+
+ /* Start measurement */
+ write_reg( SI7210_POWER_CTRL, MEAS_MASK | USESTORE_MASK, ONEBURST_MASK);
+
+ return true;
+}
+
+bool Si7210::set_high_range() {
+ //To set the unit into 200mT range, no magnet temperature calibration
+ // We want to copy OTP 0x27->0x2C into a0->a5
+ uint8_t base_addr = 0x27; // You can change this to pick the temp calibration
+ bool worked = true;
+ uint8_t val = 0;
+
+ /* Load A0 register */
+ worked &= write_reg( SI7210_OTP_ADDR, 0, base_addr);
+ worked &= write_reg( SI7210_OTP_CTRL, 0, OTP_READ_EN_MASK);
+ worked &= read_reg( SI7210_OTP_DATA, &val);
+ worked &= write_reg( SI7210_A0, 0, val);
+
+ /* Load A1 register */
+ worked &= write_reg( SI7210_OTP_ADDR, 0, base_addr + 1);
+ worked &= write_reg( SI7210_OTP_CTRL, 0, OTP_READ_EN_MASK);
+ worked &= read_reg( SI7210_OTP_DATA, &val);
+ worked &= write_reg( SI7210_A1, 0, val);
+
+ /* Load A2 register */
+ worked &= write_reg( SI7210_OTP_ADDR, 0, base_addr + 2);
+ worked &= write_reg( SI7210_OTP_CTRL, 0, OTP_READ_EN_MASK);
+ worked &= read_reg( SI7210_OTP_DATA, &val);
+ worked &= write_reg( SI7210_A2, 0, val);
+
+ /* Load A3 register */
+ worked &= write_reg( SI7210_OTP_ADDR, 0, base_addr + 3);
+ worked &= write_reg( SI7210_OTP_CTRL, 0, OTP_READ_EN_MASK);
+ worked &= read_reg( SI7210_OTP_DATA, &val);
+ worked &= write_reg( SI7210_A3, 0, val);
+
+ /* Load A4 register */
+ worked &= write_reg( SI7210_OTP_ADDR, 0, base_addr + 4);
+ worked &= write_reg( SI7210_OTP_CTRL, 0, OTP_READ_EN_MASK);
+ worked &= read_reg( SI7210_OTP_DATA, &val);
+ worked &= write_reg( SI7210_A4, 0, val);
+
+ /* Load A5 register */
+ worked &= write_reg( SI7210_OTP_ADDR, 0, base_addr + 5);
+ worked &= write_reg( SI7210_OTP_CTRL, 0, OTP_READ_EN_MASK);
+ worked &= read_reg( SI7210_OTP_DATA, &val);
+ worked &= write_reg( SI7210_A5, 0, val);
+ return worked;
+}
diff --git a/source/Core/Drivers/Si7210.h b/source/Core/Drivers/Si7210.h
new file mode 100644
index 00000000..ed99ba2b
--- /dev/null
+++ b/source/Core/Drivers/Si7210.h
@@ -0,0 +1,27 @@
+/*
+ * Si7210.h
+ *
+ * Created on: 5 Oct. 2020
+ * Author: Ralim
+ */
+
+#ifndef CORE_DRIVERS_SI7210_H_
+#define CORE_DRIVERS_SI7210_H_
+#include <stdint.h>
+class Si7210 {
+public:
+ //Return true if present
+ static bool detect();
+
+ static bool init();
+ static int16_t read();
+private:
+ static bool write_reg(const uint8_t reg,const uint8_t mask,const uint8_t val);
+ static bool read_reg(const uint8_t reg, uint8_t *val);
+ static bool start_periodic_measurement();
+ static bool get_field_strength(int16_t *field);
+ static bool set_high_range();
+
+};
+
+#endif /* CORE_DRIVERS_SI7210_H_ */
diff --git a/source/Core/Drivers/Si7210_defines.h b/source/Core/Drivers/Si7210_defines.h
new file mode 100644
index 00000000..a7123069
--- /dev/null
+++ b/source/Core/Drivers/Si7210_defines.h
@@ -0,0 +1,91 @@
+/*
+ * Si7210_defines.h
+ *
+ * Created on: 5 Oct. 2020
+ * Author: Ralim
+ */
+
+#ifndef CORE_DRIVERS_SI7210_DEFINES_H_
+#define CORE_DRIVERS_SI7210_DEFINES_H_
+
+#define SI7210_ADDRESS (0x30<<1)
+#define SI7210_REG_ID 0xC0
+
+/* Si7210 Register addresses */
+#define SI7210_HREVID 0xC0U
+#define SI7210_DSPSIGM 0xC1U
+#define SI7210_DSPSIGL 0xC2U
+#define SI7210_DSPSIGSEL 0xC3U
+#define SI7210_POWER_CTRL 0xC4U
+#define SI7210_ARAUTOINC 0xC5U
+#define SI7210_CTRL1 0xC6U
+#define SI7210_CTRL2 0xC7U
+#define SI7210_SLTIME 0xC8U
+#define SI7210_CTRL3 0xC9U
+#define SI7210_A0 0xCAU
+#define SI7210_A1 0xCBU
+#define SI7210_A2 0xCCU
+#define SI7210_CTRL4 0xCDU
+#define SI7210_A3 0xCEU
+#define SI7210_A4 0xCFU
+#define SI7210_A5 0xD0U
+#define SI7210_OTP_ADDR 0xE1U
+#define SI7210_OTP_DATA 0xE2U
+#define SI7210_OTP_CTRL 0xE3U
+#define SI7210_TM_FG 0xE4U
+
+/* Si7210 Register bit masks */
+#define CHIP_ID_MASK 0xF0U
+#define REV_ID_MASK 0x0FU
+#define DSP_SIGSEL_MASK 0x07U
+#define MEAS_MASK 0x80U
+#define USESTORE_MASK 0x08U
+#define ONEBURST_MASK 0x04U
+#define STOP_MASK 0x02U
+#define SLEEP_MASK 0x01U
+#define ARAUTOINC_MASK 0x01U
+#define SW_LOW4FIELD_MASK 0x80U
+#define SW_OP_MASK 0x7FU
+#define SW_FIELDPOLSEL_MASK 0xC0U
+#define SW_HYST_MASK 0x3FU
+#define SW_TAMPER_MASK 0xFCU
+#define SL_FAST_MASK 0x02U
+#define SL_TIMEENA_MASK 0x01U
+#define DF_BURSTSIZE_MASK 0xE0U
+#define DF_BW_MASK 0x1EU
+#define DF_IIR_MASK 0x01U
+#define OTP_READ_EN_MASK 0x02U
+#define OTP_BUSY_MASK 0x01U
+#define TM_FG_MASK 0x03U
+
+#define DSP_SIGM_DATA_FLAG 0x80U
+#define DSP_SIGM_DATA_MASK 0x7FU
+#define DSP_SIGSEL_TEMP_MASK 0x01U
+#define DSP_SIGSEL_FIELD_MASK 0x04U
+
+/* Burst sizes */
+#define DF_BW_1 0x0U << 1
+#define DF_BW_2 0x1U << 1
+#define DF_BW_4 0x2U << 1
+#define DF_BW_8 0x3U << 1
+#define DF_BW_16 0x4U << 1
+#define DF_BW_32 0x5U << 1
+#define DF_BW_64 0x6U << 1
+#define DF_BW_128 0x7U << 1
+#define DF_BW_256 0x8U << 1
+#define DF_BW_512 0x9U << 1
+#define DF_BW_1024 0xAU << 1
+#define DF_BW_2048 0xBU << 1
+#define DF_BW_4096 0xCU << 1
+#define DF_BURSTSIZE_1 0x0U << 5
+#define DF_BURSTSIZE_2 0x1U << 5
+#define DF_BURSTSIZE_4 0x2U << 5
+#define DF_BURSTSIZE_8 0x3U << 5
+#define DF_BURSTSIZE_16 0x4U << 5
+#define DF_BURSTSIZE_32 0x5U << 5
+#define DF_BURSTSIZE_64 0x6U << 5
+#define DF_BURSTSIZE_128 0x7U << 5
+
+
+
+#endif /* CORE_DRIVERS_SI7210_DEFINES_H_ */
diff --git a/source/Core/Drivers/TipThermoModel.cpp b/source/Core/Drivers/TipThermoModel.cpp
new file mode 100644
index 00000000..34d038c6
--- /dev/null
+++ b/source/Core/Drivers/TipThermoModel.cpp
@@ -0,0 +1,245 @@
+/*
+ * TipThermoModel.cpp
+ *
+ * Created on: 7 Oct 2019
+ * Author: ralim
+ */
+
+#include "TipThermoModel.h"
+#include "Settings.h"
+#include "BSP.h"
+#include "power.hpp"
+#include "../../configuration.h"
+#include "main.hpp"
+/*
+ * The hardware is laid out as a non-inverting op-amp
+ * There is a pullup of 39k(TS100) from the +ve input to 3.9V (1M pulup on TS100)
+ *
+ * The simplest case to model this, is to ignore the pullup resistors influence, and assume that its influence is mostly constant
+ * -> Tip resistance *does* change with temp, but this should be much less than the rest of the system.
+ *
+ * When a thermocouple is equal temperature at both sides (hot and cold junction), then the output should be 0uV
+ * Therefore, by measuring the uV when both are equal, the measured reading is the offset value.
+ * This is a mix of the pull-up resistor, combined with tip manufacturing differences.
+ *
+ * All of the thermocouple readings are based on this expired patent
+ * - > https://patents.google.com/patent/US6087631A/en
+ *
+ * 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)
+ // Then remove the calibration value that is stored as a tip offset
+ uint32_t vddRailmVX10 = 33000; //The vreg is +-2%, but we have no higher accuracy available
+ // 4096 * 8 readings for full scale
+ // Convert the input ADC reading back into mV times 10 format.
+ uint32_t rawInputmVX10 = (rawADC * vddRailmVX10) / (4096 * 8);
+
+ uint32_t valueuV = rawInputmVX10 * 100; // shift into uV
+ //Now to divide this down by the gain
+ valueuV /= OP_AMP_GAIN_STAGE;
+
+ if (systemSettings.CalibrationOffset) {
+ //Remove uV tipOffset
+ if (valueuV >= systemSettings.CalibrationOffset)
+ valueuV -= systemSettings.CalibrationOffset;
+ else
+ valueuV = 0;
+ }
+
+ return valueuV;
+}
+
+uint32_t TipThermoModel::convertTipRawADCToDegC(uint16_t rawADC) {
+ return convertuVToDegC(convertTipRawADCTouV(rawADC));
+}
+#ifdef ENABLED_FAHRENHEIT_SUPPORT
+uint32_t TipThermoModel::convertTipRawADCToDegF(uint16_t rawADC) {
+ return convertuVToDegF(convertTipRawADCTouV(rawADC));
+}
+#endif
+
+//Table that is designed to be walked to find the best sample for the lookup
+
+//Extrapolate between two points
+// [x1, y1] = point 1
+// [x2, y2] = point 2
+// x = input value
+// output is x's interpolated y value
+int32_t LinearInterpolate(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x) {
+ return y1 + (((((x - x1) * 1000) / (x2 - x1)) * (y2 - y1))) / 1000;
+}
+#ifdef TEMP_uV_LOOKUP_HAKKO
+const uint16_t uVtoDegC[] = { //
+ //
+ 0, 0, //
+ 266, 10, //
+ 522, 20, //
+ 770, 30, //
+ 1010, 40, //
+ 1244, 50, //
+ 1473, 60, //
+ 1697, 70, //
+ 1917, 80, //
+ 2135, 90, //
+ 2351, 100, //
+ 2566, 110, //
+ 2780, 120, //
+ 2994, 130, //
+ 3209, 140, //
+ 3426, 150, //
+ 3644, 160, //
+ 3865, 170, //
+ 4088, 180, //
+ 4314, 190, //
+ 4544, 200, //
+ 4777, 210, //
+ 5014, 220, //
+ 5255, 230, //
+ 5500, 240, //
+ 5750, 250, //
+ 6003, 260, //
+ 6261, 270, //
+ 6523, 280, //
+ 6789, 290, //
+ 7059, 300, //
+ 7332, 310, //
+ 7609, 320, //
+ 7889, 330, //
+ 8171, 340, //
+ 8456, 350, //
+ 8742, 360, //
+ 9030, 370, //
+ 9319, 380, //
+ 9607, 390, //
+ 9896, 400, //
+ 10183, 410, //
+ 10468, 420, //
+ 10750, 430, //
+ 11029, 440, //
+ 11304, 450, //
+ 11573, 460, //
+ 11835, 470, //
+ 12091, 480, //
+ 12337, 490, //
+ 12575, 500, //
+
+ };
+#endif
+
+#ifdef TEMP_uV_LOOKUP_TS80
+
+const uint16_t uVtoDegC[] = { //
+ //
+ 530 , 0, //
+ 1282 , 10, //
+ 2034 , 20, //
+ 2786 , 30, //
+ 3538 , 40, //
+ 4290 , 50, //
+ 5043 , 60, //
+ 5795 , 70, //
+ 6547 , 80, //
+ 7299 , 90, //
+ 8051 , 100, //
+ 8803 , 110, //
+ 9555 , 120, //
+ 10308 , 130, //
+ 11060 , 140, //
+ 11812 , 150, //
+ 12564 , 160, //
+ 13316 , 170, //
+ 14068 , 180, //
+ 14820 , 190, //
+ 15573 , 200, //
+ 16325 , 210, //
+ 17077 , 220, //
+ 17829 , 230, //
+ 18581 , 240, //
+ 19333 , 250, //
+ 20085 , 260, //
+ 20838 , 270, //
+ 21590 , 280, //
+ 22342 , 290, //
+ 23094 , 300, //
+ 23846 , 310, //
+ 24598 , 320, //
+ 25350 , 330, //
+ 26103 , 340, //
+ 26855 , 350, //
+ 27607 , 360, //
+ 28359 , 370, //
+ 29111 , 380, //
+ 29863 , 390, //
+ 30615 , 400, //
+ 31368 , 410, //
+ 32120 , 420, //
+ 32872 , 430, //
+ 33624 , 440, //
+ 34376 , 450, //
+ 35128 , 460, //
+ 35880 , 470, //
+ 36632 , 480, //
+ 37385 , 490, //
+ 38137 , 500, //
+ };
+#endif
+uint32_t TipThermoModel::convertuVToDegC(uint32_t tipuVDelta) {
+ if (tipuVDelta) {
+ int noItems = sizeof(uVtoDegC) / (2 * sizeof(uint16_t));
+ for (int i = 1; i < (noItems - 1); i++) {
+ //If current tip temp is less than current lookup, then this current lookup is the higher point to interpolate
+ if (tipuVDelta < uVtoDegC[i * 2]) {
+ return LinearInterpolate(uVtoDegC[(i - 1) * 2], uVtoDegC[((i - 1) * 2) + 1], uVtoDegC[i * 2], uVtoDegC[(i * 2) + 1], tipuVDelta);
+ }
+ }
+ return LinearInterpolate(uVtoDegC[(noItems - 2) * 2], uVtoDegC[((noItems - 2) * 2) + 1], uVtoDegC[(noItems - 1) * 2], uVtoDegC[((noItems - 1) * 2) + 1], tipuVDelta);
+ }
+ return 0;
+}
+
+#ifdef ENABLED_FAHRENHEIT_SUPPORT
+uint32_t TipThermoModel::convertuVToDegF(uint32_t tipuVDelta) {
+ return convertCtoF(convertuVToDegC(tipuVDelta));
+}
+
+uint32_t TipThermoModel::convertCtoF(uint32_t degC) {
+ //(Y °C × 9/5) + 32 =Y°F
+ return (32 + ((degC * 9) / 5));
+}
+
+uint32_t TipThermoModel::convertFtoC(uint32_t degF) {
+ //(Y°F − 32) × 5/9 = Y°C
+ if (degF < 32) {
+ return 0;
+ }
+ return ((degF - 32) * 5) / 9;
+}
+#endif
+
+uint32_t TipThermoModel::getTipInC(bool sampleNow) {
+ int32_t currentTipTempInC = TipThermoModel::convertTipRawADCToDegC(getTipRawTemp(sampleNow));
+ currentTipTempInC += getHandleTemperature() / 10; //Add handle offset
+ // Power usage indicates that our tip temp is lower than our thermocouple temp.
+ // I found a number that doesn't unbalance the existing PID, causing overshoot.
+ // This could be tuned in concert with PID parameters...
+ currentTipTempInC -= x10WattHistory.average() / 25;
+ if (currentTipTempInC < 0)
+ return 0;
+ return currentTipTempInC;
+}
+#ifdef ENABLED_FAHRENHEIT_SUPPORT
+uint32_t TipThermoModel::getTipInF(bool sampleNow) {
+ uint32_t currentTipTempInF = getTipInC(sampleNow);
+ currentTipTempInF = convertCtoF(currentTipTempInF);
+ return currentTipTempInF;
+}
+#endif
+
+uint32_t TipThermoModel::getTipMaxInC() {
+ uint32_t maximumTipTemp = TipThermoModel::convertTipRawADCToDegC(0x7FFF - (21 * 5)); //back off approx 5 deg c from ADC max
+ maximumTipTemp += getHandleTemperature() / 10; //Add handle offset
+ return maximumTipTemp - 1;
+}
diff --git a/source/Core/Drivers/TipThermoModel.h b/source/Core/Drivers/TipThermoModel.h
new file mode 100644
index 00000000..a4b0b368
--- /dev/null
+++ b/source/Core/Drivers/TipThermoModel.h
@@ -0,0 +1,42 @@
+/*
+ * TipThermoModel.h
+ *
+ * Created on: 7 Oct 2019
+ * Author: ralim
+ */
+
+#ifndef SRC_TIPTHERMOMODEL_H_
+#define SRC_TIPTHERMOMODEL_H_
+#include "stdint.h"
+#include "BSP.h"
+#include "unit.h"
+class TipThermoModel {
+public:
+ //These are the main two functions
+ static uint32_t getTipInC(bool sampleNow = false);
+#ifdef ENABLED_FAHRENHEIT_SUPPORT
+ static uint32_t getTipInF(bool sampleNow = false);
+#endif
+
+ //Calculates the maximum temperature can can be read by the ADC range
+ static uint32_t getTipMaxInC();
+
+ static uint32_t convertTipRawADCToDegC(uint16_t rawADC);
+#ifdef ENABLED_FAHRENHEIT_SUPPORT
+ static uint32_t convertTipRawADCToDegF(uint16_t rawADC);
+#endif
+ //Returns the uV of the tip reading before the op-amp compensating for pullups
+ static uint32_t convertTipRawADCTouV(uint16_t rawADC);
+#ifdef ENABLED_FAHRENHEIT_SUPPORT
+ static uint32_t convertCtoF(uint32_t degC);
+ static uint32_t convertFtoC(uint32_t degF);
+#endif
+
+private:
+ static uint32_t convertuVToDegC(uint32_t tipuVDelta);
+#ifdef ENABLED_FAHRENHEIT_SUPPORT
+ static uint32_t convertuVToDegF(uint32_t tipuVDelta);
+#endif
+};
+
+#endif /* SRC_TIPTHERMOMODEL_H_ */