S60 Support (#1692)
* Mock S60 * cleanup * Start refactor of OLED init * Setup timers roughly * Set Vector table offset correctly Update system_stm32f1xx.c * Update OLED.cpp * Update stm32f1xx_hal_msp.c * Update configuration.h * I2C init before GPIO From Errata Update stm32f1xx_hal_msp.c Update Software_I2C.h Allow no hardware I2C * I2C BB run bus unlock at init * cleanups * Software I2C for now * Mildly more graceful Interpolate * Handle is powered by DC Update Power.cpp Update drawPowerSourceIcon.cpp Update configuration.h Update Setup.cpp * Cleanup HomeScreen * Segment remap oled at init * Cleanup * Update MOVThread.cpp * Fix PWM Init * Fix adc2 trigger * Update configs * Fixup warning * Saner default config * Update ThermoModel.cpp * Util for current@voltage * Hub238 warning * Add hub238 handling in power mode * Update USBPDDebug_FUSB.cpp * HUSB238 debug * Hook PSU Limit * Use wrapping section of GRAM for scroll Update OLED.hpp * Update NTC table * Fix HUB voltage picker * Cleanup * Larger tip filter * Calibrate in a bunch closer Update ThermoModel.cpp * Update configuration.h * Update HUB238.cpp * Update configuration.h * Movement Pin * Update BSP.cpp * tim2 irq * Rough timer conversion (adc broken) but movement working * Fix tim2 start * Faster base PWM * Ensure utils grabs config * Add wattage limiter tolerance for device * Speed up PWM and enable PWM current limiting * tune for 12v * Prevent start until PD done * Update configuration.h * Add HUB238 check for have re-negotiated * Adjust timer to avoid noise when its possible
This commit is contained in:
@@ -141,6 +141,22 @@ const uint8_t disconnectedTip[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
#endif
|
||||
|
||||
#if defined(MODEL_S60) > 0
|
||||
const uint8_t buttonA[] = {
|
||||
// width = 42
|
||||
// height = 16
|
||||
0x00, 0x00, 0x00, 0x00, 0xe0, 0x18, 0x04, 0x02, 0x02, 0x01, 0x81, 0x49, 0x31, 0x01, 0xc1, 0x25, 0x19, 0x01, 0xc1, 0x25, 0x19, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x04, 0x18, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x20, 0x40, 0x40, 0x80, 0x89, 0x8a, 0x88, 0x94,
|
||||
0x8c, 0x94, 0xae, 0x80, 0xbe, 0x8e, 0xa6, 0x8e, 0xa6, 0x8e, 0xa6, 0x8e, 0xa6, 0x8a, 0xa6, 0x8a, 0xa6, 0x8a, 0xa6, 0x8a, 0x46, 0x4a, 0x22, 0x18, 0x07, 0x00, 0x00, 0x00};
|
||||
|
||||
const uint8_t disconnectedTip[] = {
|
||||
// width = 42
|
||||
// height = 16
|
||||
0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0xc0, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xcc, 0x9c, 0x38, 0x70, 0xe0, 0xc0, 0x80, 0x20, 0x70, 0x38, 0x1c, 0xcc, 0x40,
|
||||
0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, 0x60, 0xe0, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x05, 0x00, 0x07, 0x01, 0x04, 0x01, 0x04, 0x01,
|
||||
0x04, 0x31, 0x38, 0x1c, 0x0e, 0x04, 0x01, 0x03, 0x07, 0x0e, 0x1c, 0x39, 0x30, 0x01, 0x03, 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x04, 0x09, 0x0f, 0x00};
|
||||
#endif
|
||||
|
||||
const uint8_t buttonB[] = {
|
||||
// width = 42
|
||||
// height = 16
|
||||
|
||||
220
source/Core/Drivers/HUB238.cpp
Normal file
220
source/Core/Drivers/HUB238.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
#include "HUB238.hpp"
|
||||
#include "I2CBB.hpp"
|
||||
#include "Utils.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#if POW_PD_EXT == 1
|
||||
bool hub238_probe() { return I2CBB::probe(HUB238_ADDR); }
|
||||
|
||||
extern int32_t powerSupplyWattageLimit;
|
||||
|
||||
uint16_t hub238_debug_state() {
|
||||
uint8_t status0 = 0;
|
||||
uint8_t status1 = 0;
|
||||
if (!I2CBB::Mem_Read(HUB238_ADDR, HUB238_REG_PD_STATUS0, &status0, 1)) {
|
||||
return 0xFFFF;
|
||||
}
|
||||
if (!I2CBB::Mem_Read(HUB238_ADDR, HUB238_REG_PD_STATUS1, &status1, 1)) {
|
||||
return 0xFFFF;
|
||||
}
|
||||
return status1 | (((uint16_t)status0) << 8);
|
||||
}
|
||||
uint16_t pdo_slot_to_currentx100(uint8_t temp) {
|
||||
temp = temp & 0b1111;
|
||||
switch (temp) {
|
||||
case 0b0000:
|
||||
return 50;
|
||||
case 0b0001:
|
||||
return 70;
|
||||
case 0b0010:
|
||||
return 100;
|
||||
case 0b0011:
|
||||
return 125;
|
||||
case 0b0100:
|
||||
return 150;
|
||||
case 0b0101:
|
||||
return 175;
|
||||
case 0b0110:
|
||||
return 200;
|
||||
case 0b0111:
|
||||
return 225;
|
||||
case 0b1000:
|
||||
return 250;
|
||||
case 0b1001:
|
||||
return 275;
|
||||
case 0b1010:
|
||||
return 300;
|
||||
case 0b1011:
|
||||
return 325;
|
||||
case 0b1100:
|
||||
return 350;
|
||||
case 0b1101:
|
||||
return 400;
|
||||
case 0b1110:
|
||||
return 450;
|
||||
case 0b1111:
|
||||
return 500;
|
||||
}
|
||||
}
|
||||
uint16_t hub238_getVoltagePDOCurrent(uint8_t voltage) {
|
||||
uint8_t reg = HUB238_REG_SRC_PDO_5V;
|
||||
switch (voltage) {
|
||||
case 5:
|
||||
reg = HUB238_REG_SRC_PDO_5V;
|
||||
break;
|
||||
case 9:
|
||||
reg = HUB238_REG_SRC_PDO_9V;
|
||||
break;
|
||||
case 12:
|
||||
reg = HUB238_REG_SRC_PDO_12V;
|
||||
break;
|
||||
case 15:
|
||||
reg = HUB238_REG_SRC_PDO_15V;
|
||||
break;
|
||||
case 18:
|
||||
reg = HUB238_REG_SRC_PDO_18V;
|
||||
break;
|
||||
case 20:
|
||||
reg = HUB238_REG_SRC_PDO_20V;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
uint8_t temp = 0;
|
||||
if (I2CBB::Mem_Read(HUB238_ADDR, reg, &temp, 1) == true) {
|
||||
if (temp & HUB238_PDO_DETECTED) {
|
||||
return pdo_slot_to_currentx100(temp);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
uint8_t findBestPDO() {
|
||||
uint8_t temp = 0;
|
||||
uint16_t ilim = 0;
|
||||
uint16_t minimumx10current = 0;
|
||||
#if USB_PD_VMAX >= 20
|
||||
ilim = hub238_getVoltagePDOCurrent(20);
|
||||
minimumx10current = Utils::RequiredCurrentForTipAtVoltage(200);
|
||||
if (ilim != 0 && ilim / 10 >= minimumx10current) {
|
||||
powerSupplyWattageLimit = ((20 * ilim) / 100) - 2; // We take off 2W for safety of overhead
|
||||
return 0b1010;
|
||||
}
|
||||
#endif
|
||||
#if USB_PD_VMAX >= 18
|
||||
ilim = hub238_getVoltagePDOCurrent(18);
|
||||
minimumx10current = Utils::RequiredCurrentForTipAtVoltage(180);
|
||||
if (ilim != 0 && ilim / 10 >= minimumx10current) {
|
||||
powerSupplyWattageLimit = ((18 * ilim) / 100) - 2; // We take off 2W for safety of overhead
|
||||
return 0b1001;
|
||||
}
|
||||
#endif
|
||||
#if USB_PD_VMAX >= 15
|
||||
ilim = hub238_getVoltagePDOCurrent(15);
|
||||
minimumx10current = Utils::RequiredCurrentForTipAtVoltage(150);
|
||||
if (ilim != 0 && ilim / 10 >= minimumx10current) {
|
||||
powerSupplyWattageLimit = ((15 * ilim) / 100) - 2; // We take off 2W for safety of overhead
|
||||
return 0b1000;
|
||||
}
|
||||
#endif
|
||||
#if USB_PD_VMAX >= 12
|
||||
ilim = hub238_getVoltagePDOCurrent(12);
|
||||
minimumx10current = Utils::RequiredCurrentForTipAtVoltage(120);
|
||||
if (ilim != 0 && (ilim / 10) >= minimumx10current) {
|
||||
powerSupplyWattageLimit = ((12 * ilim) / 100) - 2; // We take off 2W for safety of overhead
|
||||
return 0b0011;
|
||||
}
|
||||
#endif
|
||||
#if USB_PD_VMAX >= 9
|
||||
ilim = hub238_getVoltagePDOCurrent(9);
|
||||
minimumx10current = Utils::RequiredCurrentForTipAtVoltage(90);
|
||||
if (ilim != 0 && ilim / 10 >= minimumx10current) {
|
||||
powerSupplyWattageLimit = ((9 * ilim) / 100) - 2; // We take off 2W for safety of overhead
|
||||
return 0b0010;
|
||||
}
|
||||
#endif
|
||||
|
||||
powerSupplyWattageLimit = 10;
|
||||
return 0b0001; // 5V PDO
|
||||
}
|
||||
volatile uint8_t haveSelected = 0xFF;
|
||||
|
||||
void hub238_check_negotiation() {
|
||||
// Dont do anything for first 2 seconds as its internal state machine corrupts if we ask it to change too fast
|
||||
|
||||
if (xTaskGetTickCount() < 2000) {
|
||||
return;
|
||||
}
|
||||
// Want to check if there is a better PDO to be using
|
||||
// First, exit early if we already have changed _or_ no PD
|
||||
// Even if it negotiates the same voltage as we want, we still re-run it as that makes it ignore the resistor
|
||||
// and instead ask for max amps
|
||||
if (haveSelected != 0xFF || !hub238_has_negotiated() || hub238_source_voltage() == 0) {
|
||||
return;
|
||||
}
|
||||
uint8_t currentPDO = 0;
|
||||
vTaskDelay(5);
|
||||
|
||||
uint8_t bestPDO = findBestPDO();
|
||||
|
||||
if (I2CBB::Mem_Read(HUB238_ADDR, HUB238_REG_SRC_PDO, ¤tPDO, 1) == true) {
|
||||
currentPDO >>= 4; // grab upper bits
|
||||
if (currentPDO == bestPDO) {
|
||||
haveSelected = bestPDO;
|
||||
return;
|
||||
}
|
||||
currentPDO = bestPDO << 4;
|
||||
if (I2CBB::Mem_Write(HUB238_ADDR, HUB238_REG_SRC_PDO, ¤tPDO, 1) == true) {
|
||||
|
||||
currentPDO = 0x01; // request for new PDO
|
||||
if (I2CBB::Mem_Write(HUB238_ADDR, HUB238_REG_GO_COMMAND, ¤tPDO, 1) == true) {
|
||||
haveSelected = bestPDO;
|
||||
vTaskDelay(50);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool hub238_has_run_selection() { return haveSelected != 0xFF; }
|
||||
|
||||
bool hub238_has_negotiated() {
|
||||
uint8_t temp = 0;
|
||||
if (I2CBB::Mem_Read(HUB238_ADDR, HUB238_REG_PD_STATUS1, &temp, 1) == true) {
|
||||
temp >>= 3;
|
||||
return (temp & 0b111) == 0b001; // success
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return selected source voltage in V
|
||||
uint16_t hub238_source_voltage() {
|
||||
uint8_t temp = 0;
|
||||
if (I2CBB::Mem_Read(HUB238_ADDR, HUB238_REG_PD_STATUS0, &temp, 1) == true) {
|
||||
temp >>= 4;
|
||||
switch (temp) {
|
||||
case 0b0001:
|
||||
return 5;
|
||||
case 0b0010:
|
||||
return 9;
|
||||
case 0b0011:
|
||||
return 12;
|
||||
case 0b0100:
|
||||
return 15;
|
||||
case 0b0101:
|
||||
return 18;
|
||||
case 0b0110:
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// Return selected source current in Amps * 100
|
||||
uint8_t hub238_source_currentX100() {
|
||||
uint8_t temp = 0;
|
||||
if (I2CBB::Mem_Read(HUB238_ADDR, HUB238_REG_PD_STATUS0, &temp, 1) == true) {
|
||||
temp &= 0b1111;
|
||||
return pdo_slot_to_currentx100(temp);
|
||||
}
|
||||
return 10;//Failsafe to 0.1 amp
|
||||
}
|
||||
#endif
|
||||
51
source/Core/Drivers/HUB238.hpp
Normal file
51
source/Core/Drivers/HUB238.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _DRIVERS_HUB238_HPP_
|
||||
#define _DRIVERS_HUB238_HPP_
|
||||
#include "configuration.h"
|
||||
#if POW_PD_EXT == 1
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define HUB238_ADDR 0x08 << 1
|
||||
|
||||
#define HUB238_REG_PD_STATUS0 0x00
|
||||
#define HUB238_REG_PD_STATUS1 0x01
|
||||
#define HUB238_REG_SRC_PDO_5V 0x02
|
||||
#define HUB238_REG_SRC_PDO_9V 0x03
|
||||
#define HUB238_REG_SRC_PDO_12V 0x04
|
||||
#define HUB238_REG_SRC_PDO_15V 0x05
|
||||
#define HUB238_REG_SRC_PDO_18V 0x06
|
||||
#define HUB238_REG_SRC_PDO_20V 0x07
|
||||
#define HUB238_REG_SRC_PDO 0x08
|
||||
#define HUB238_REG_GO_COMMAND 0x09
|
||||
|
||||
#define HUB238_PDO_DETECTED (0x01 << 7)
|
||||
// The HUB238 is fairly simple device to interact to, with fairly few registers all in all
|
||||
// It only appears to support fixed PDO's up to 20V
|
||||
// And they have just dedicated registers to each potential option
|
||||
// Given a tip resistance we try and pick the best possible PDO option to suit that resistance
|
||||
// (Using I2C overrides any hardware strapping).
|
||||
|
||||
// Probe if the hub238 exists on the I2C bus
|
||||
bool hub238_probe();
|
||||
// If we have not manually picked a PDO,
|
||||
// but there is an active PD supply, try for our preference
|
||||
|
||||
void hub238_check_negotiation();
|
||||
|
||||
// Returns true when negotiation has finished
|
||||
bool hub238_has_negotiated();
|
||||
// Returns true when we have run selection and negotiated higher current
|
||||
bool hub238_has_run_selection();
|
||||
// Return an encoded state for debugging
|
||||
uint16_t hub238_debug_state();
|
||||
// Return selected source voltage in V
|
||||
uint16_t hub238_source_voltage();
|
||||
// Return selected source current in Amps * 100
|
||||
uint8_t hub238_source_currentX100();
|
||||
|
||||
uint16_t hub238_getVoltagePDOCurrent(uint8_t voltage);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -26,6 +26,19 @@ void I2CBB::init() {
|
||||
HAL_GPIO_Init(SCL2_GPIO_Port, &GPIO_InitStruct);
|
||||
SOFT_SDA_HIGH();
|
||||
SOFT_SCL_HIGH();
|
||||
// To ensure bus is unlocked; we toggle the Clock a bunch of times to make things error out
|
||||
for (int i = 0; i < 128; i++) {
|
||||
SOFT_SCL_LOW();
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
SOFT_SCL_HIGH();
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
}
|
||||
I2CSemaphore = xSemaphoreCreateMutexStatic(&xSemaphoreBuffer);
|
||||
unlock();
|
||||
}
|
||||
@@ -83,14 +96,12 @@ bool I2CBB::Mem_Write(uint16_t DevAddress, uint16_t MemAddress, const uint8_t *p
|
||||
bool ack = send(DevAddress);
|
||||
if (!ack) {
|
||||
stop();
|
||||
asm("bkpt");
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
ack = send(MemAddress);
|
||||
if (!ack) {
|
||||
stop();
|
||||
asm("bkpt");
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
@@ -99,7 +110,6 @@ bool I2CBB::Mem_Write(uint16_t DevAddress, uint16_t MemAddress, const uint8_t *p
|
||||
ack = send(pData[0]);
|
||||
if (!ack) {
|
||||
stop();
|
||||
asm("bkpt");
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
@@ -267,9 +277,7 @@ uint8_t I2CBB::read_bit() {
|
||||
void I2CBB::unlock() { xSemaphoreGive(I2CSemaphore); }
|
||||
|
||||
bool I2CBB::lock() {
|
||||
if (I2CSemaphore == NULL) {
|
||||
asm("bkpt");
|
||||
}
|
||||
if (I2CSemaphore == NULL) {}
|
||||
bool a = xSemaphoreTake(I2CSemaphore, (TickType_t)100) == pdTRUE;
|
||||
return a;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ OLED::DisplayState OLED::displayState;
|
||||
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::screenBuffer[16 + (OLED_WIDTH * (OLED_HEIGHT / 8)) + 10]; // The data buffer
|
||||
uint8_t OLED::secondFrameBuffer[OLED_WIDTH * 2];
|
||||
uint32_t OLED::displayChecksum;
|
||||
/*Setup params for the OLED screen*/
|
||||
@@ -32,31 +32,35 @@ uint32_t OLED::displayChecksum;
|
||||
/*Data packets are prefixed with 0x40*/
|
||||
I2C_CLASS::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}, /*Brightness*/
|
||||
{0x80, 0x00, 0}, /*^0*/
|
||||
{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*/
|
||||
{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, OLED_HEIGHT - 1, 0}, /*Multiplex ratio adjusts how far down the matrix it scans*/
|
||||
{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*/
|
||||
#ifdef OLED_SEGMENT_MAP_REVERSED
|
||||
{0x80, 0xA1, 0}, /*Set Segment remap to normal*/
|
||||
#else
|
||||
{0x80, 0xA0, 0}, /*Set Segment remap to normal*/
|
||||
#endif
|
||||
{0x80, 0x8D, 0}, /*Charge Pump*/
|
||||
{0x80, 0x14, 0}, /*Charge Pump settings*/
|
||||
{0x80, 0xDA, 0}, /*Set VCOM Pins hardware config*/
|
||||
{0x80, OLED_VCOM_LAYOUT, 0}, /*Combination 0x2 or 0x12 depending on OLED model*/
|
||||
{0x80, 0x81, 0}, /*Brightness*/
|
||||
{0x80, 0x00, 0}, /*^0*/
|
||||
{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
|
||||
|
||||
@@ -71,9 +75,9 @@ const uint8_t REFRESH_COMMANDS[17] = {
|
||||
0x80,
|
||||
0x21, // cmd
|
||||
0x80,
|
||||
0x20, // A
|
||||
OLED_GRAM_START, // A
|
||||
0x80,
|
||||
0x7F, // B
|
||||
OLED_GRAM_END, // B
|
||||
|
||||
// Set COM output scan direction (normal mode, COM0 to COM[N-1])
|
||||
0x80,
|
||||
@@ -87,7 +91,7 @@ const uint8_t REFRESH_COMMANDS[17] = {
|
||||
0x80,
|
||||
0x00, // A
|
||||
0x80,
|
||||
0x01, // B
|
||||
(OLED_HEIGHT / 8), // B
|
||||
|
||||
// Start of data
|
||||
0x40,
|
||||
@@ -217,7 +221,7 @@ void OLED::maskScrollIndicatorOnOLED() {
|
||||
// it from the screen buffer which is updated by `OLED::setRotation`.
|
||||
uint8_t rightmostColumn = screenBuffer[7];
|
||||
uint8_t maskCommands[] = {
|
||||
// Set column address:
|
||||
// Set column address:
|
||||
// A[6:0] - Column start address = rightmost column
|
||||
// B[6:0] - Column end address = rightmost column
|
||||
0x80,
|
||||
@@ -304,24 +308,26 @@ void OLED::useSecondaryFramebuffer(bool useSecondary) {
|
||||
* method, as doing so will overwrite the previous screen data. The caller
|
||||
* does not need to call `OLED::refresh()` after this function returns.
|
||||
*
|
||||
* **This function blocks until the transition has completed.**
|
||||
* **This function blocks until the transition has completed or user presses button**
|
||||
*/
|
||||
void OLED::transitionScrollDown() {
|
||||
// We want to draw the updated framebuffer to the next page downward.
|
||||
uint8_t const pageStart = screenBuffer[13];
|
||||
uint8_t const nextPage = (pageStart + 2) % 8;
|
||||
uint8_t const nextPage = (pageStart + (OLED_HEIGHT / 8)) % 8;
|
||||
// Change page start address:
|
||||
screenBuffer[13] = nextPage;
|
||||
// Change page end address:
|
||||
screenBuffer[15] = nextPage + 1;
|
||||
screenBuffer[15] = (nextPage + 1) % 8;
|
||||
|
||||
refresh();
|
||||
refresh(); // Now refresh to write out the contents to the new page
|
||||
osDelay(TICKS_100MS / 5);
|
||||
|
||||
uint8_t const startLine = pageStart * 8 + 1;
|
||||
uint8_t const scrollTo = (pageStart + 2) * 8;
|
||||
uint8_t startLine = (pageStart * 8) + 1;
|
||||
|
||||
uint8_t scrollTo = (pageStart + (OLED_HEIGHT / 8)) * 8;
|
||||
|
||||
// Scroll the screen by changing display start line.
|
||||
// This effectively scrolls off the bottom of the current page and into the next one
|
||||
for (uint8_t current = startLine; current <= scrollTo; current++) {
|
||||
if (getButtonState() != BUTTON_NONE) {
|
||||
current = scrollTo;
|
||||
@@ -337,6 +343,13 @@ void OLED::transitionScrollDown() {
|
||||
I2C_CLASS::I2C_RegisterWrite(DEVICEADDR_OLED, 0x80, scrollCommandByte);
|
||||
osDelay(TICKS_100MS / 7);
|
||||
}
|
||||
// Now that scroll is done, revert to default page to avoid wrap issues
|
||||
screenBuffer[13] = pageStart;
|
||||
screenBuffer[15] = (pageStart + 1) % 8;
|
||||
uint8_t scrollCommandByte = 0b01000000;
|
||||
OLED_Setup_Array[8].val = scrollCommandByte;
|
||||
I2C_CLASS::I2C_RegisterWrite(DEVICEADDR_OLED, 0x80, scrollCommandByte);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void OLED::setRotation(bool leftHanded) {
|
||||
@@ -346,22 +359,32 @@ void OLED::setRotation(bool leftHanded) {
|
||||
if (inLeftHandedMode == leftHanded) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef OLED_SEGMENT_MAP_REVERSED
|
||||
if (!leftHanded) {
|
||||
OLED_Setup_Array[9].val = 0xA1;
|
||||
} else {
|
||||
OLED_Setup_Array[9].val = 0xA0;
|
||||
}
|
||||
#else
|
||||
if (leftHanded) {
|
||||
OLED_Setup_Array[9].val = 0xA1;
|
||||
} else {
|
||||
OLED_Setup_Array[9].val = 0xA0;
|
||||
}
|
||||
#endif
|
||||
// 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;
|
||||
}
|
||||
I2C_CLASS::writeRegistersBulk(DEVICEADDR_OLED, OLED_Setup_Array, sizeof(OLED_Setup_Array) / sizeof(OLED_Setup_Array[0]));
|
||||
osDelay(TICKS_10MS);
|
||||
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[5] = inLeftHandedMode ? OLED_GRAM_START_FLIP : OLED_GRAM_START; // display is shifted by 32 in left handed
|
||||
// mode as driver ram is 128 wide
|
||||
screenBuffer[7] = inLeftHandedMode ? OLED_GRAM_END_FLIP : OLED_GRAM_END; // End address of the ram segment we are writing to (96 wide)
|
||||
screenBuffer[9] = inLeftHandedMode ? 0xC8 : 0xC0;
|
||||
// Force a screen refresh
|
||||
const int len = FRAMEBUFFER_START + (OLED_WIDTH * 2);
|
||||
|
||||
@@ -32,9 +32,32 @@ extern "C" {
|
||||
#include "I2C_Wrapper.hpp"
|
||||
#endif
|
||||
|
||||
#define DEVICEADDR_OLED (0x3c << 1)
|
||||
#define OLED_WIDTH 96
|
||||
#define OLED_HEIGHT 16
|
||||
#define DEVICEADDR_OLED (0x3c << 1)
|
||||
#ifdef OLED_128x32
|
||||
// TODO; for now just cropping in on the screen from 128x32 to 96x16
|
||||
#define OLED_WIDTH 96
|
||||
#define OLED_HEIGHT 16
|
||||
#define OLED_GRAM_START 0x10 // Should be 0x00 when we have full width
|
||||
#define OLED_GRAM_END 0x6F // Should be 0x7F when we have full width
|
||||
#define OLED_GRAM_START_FLIP 0
|
||||
#define OLED_GRAM_END_FLIP 95
|
||||
|
||||
#define OLED_VCOM_LAYOUT 0x12
|
||||
#define OLED_SEGMENT_MAP_REVERSED
|
||||
#warning "S60 Not fully supported"
|
||||
#else
|
||||
#define OLED_WIDTH 96
|
||||
#define OLED_HEIGHT 16
|
||||
#define OLED_VCOM_LAYOUT 0x02
|
||||
|
||||
#define OLED_GRAM_START 0x20
|
||||
#define OLED_GRAM_END 0x7F
|
||||
#define OLED_GRAM_START_FLIP 0
|
||||
#define OLED_GRAM_END_FLIP 95
|
||||
|
||||
#define OLED_SEGMENT_MAP 0xA0
|
||||
|
||||
#endif
|
||||
#define FRAMEBUFFER_START 17
|
||||
|
||||
enum class FontStyle {
|
||||
@@ -51,9 +74,9 @@ public:
|
||||
static bool isInitDone();
|
||||
// Draw the buffer out to the LCD if any content has changed.
|
||||
static void refresh() {
|
||||
|
||||
|
||||
if (checkDisplayBufferChecksum()) {
|
||||
const int len = FRAMEBUFFER_START + (OLED_WIDTH * 2);
|
||||
const int len = FRAMEBUFFER_START + (OLED_WIDTH * 2);
|
||||
I2C_CLASS::Transmit(DEVICEADDR_OLED, screenBuffer, len);
|
||||
// DMA tx time is ~ 20mS Ensure after calling this you delay for at least 25ms
|
||||
// or we need to goto double buffering
|
||||
@@ -114,17 +137,17 @@ public:
|
||||
static void transitionScrollDown();
|
||||
|
||||
private:
|
||||
static bool checkDisplayBufferChecksum(){
|
||||
uint32_t hash = 0;
|
||||
static bool checkDisplayBufferChecksum() {
|
||||
uint32_t hash = 0;
|
||||
const int len = FRAMEBUFFER_START + (OLED_WIDTH * 2);
|
||||
for (int i = 0; i < len; i++) {
|
||||
hash += (i * screenBuffer[i]);
|
||||
}
|
||||
|
||||
bool result = hash!=displayChecksum;
|
||||
displayChecksum= hash;
|
||||
bool result = hash != displayChecksum;
|
||||
displayChecksum = hash;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
static void drawChar(uint16_t charCode, FontStyle fontStyle); // Draw a character to the current cursor location
|
||||
static void setFramebuffer(uint8_t *buffer);
|
||||
static uint8_t *firstStripPtr; // Pointers to the strips to allow for buffer having extra content
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "USBPD.h"
|
||||
#include "configuration.h"
|
||||
#if POW_PD
|
||||
#ifdef POW_PD
|
||||
|
||||
#include "BSP_PD.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#if POW_PD
|
||||
#ifdef POW_PD
|
||||
class USBPowerDelivery {
|
||||
public:
|
||||
static bool start(); // Start the PD stack
|
||||
|
||||
@@ -5,19 +5,30 @@
|
||||
* Author: Ralim
|
||||
*/
|
||||
|
||||
#include "BSP_Power.h"
|
||||
#include "configuration.h"
|
||||
#include <Utils.h>
|
||||
|
||||
int32_t Utils::InterpolateLookupTable(const int32_t *lookupTable, const int noItems, const int32_t value) {
|
||||
if (value) {
|
||||
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 (value < lookupTable[i * 2]) {
|
||||
return LinearInterpolate(lookupTable[(i - 1) * 2], lookupTable[((i - 1) * 2) + 1], lookupTable[i * 2], lookupTable[(i * 2) + 1], value);
|
||||
}
|
||||
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 (value < lookupTable[i * 2]) {
|
||||
return LinearInterpolate(lookupTable[(i - 1) * 2], lookupTable[((i - 1) * 2) + 1], lookupTable[i * 2], lookupTable[(i * 2) + 1], value);
|
||||
}
|
||||
return LinearInterpolate(lookupTable[(noItems - 2) * 2], lookupTable[((noItems - 2) * 2) + 1], lookupTable[(noItems - 1) * 2], lookupTable[((noItems - 1) * 2) + 1], value);
|
||||
}
|
||||
return 0;
|
||||
return LinearInterpolate(lookupTable[(noItems - 2) * 2], lookupTable[((noItems - 2) * 2) + 1], lookupTable[(noItems - 1) * 2], lookupTable[((noItems - 1) * 2) + 1], value);
|
||||
}
|
||||
|
||||
int32_t Utils::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; }
|
||||
|
||||
uint16_t Utils::RequiredCurrentForTipAtVoltage(uint16_t voltageX10) {
|
||||
uint8_t tipResistancex10 = getTipResistanceX10() + 5;
|
||||
#ifdef MODEL_HAS_DCDC
|
||||
// If this device has step down DC/DC inductor to smooth out current spikes
|
||||
// We can instead ignore resistance and go for max voltage we can accept; and rely on the DC/DC regulation to keep under current limit
|
||||
tipResistancex10 = 255; // (Push to 25.5 ohms to effectively disable this check)
|
||||
#endif
|
||||
// V/R = I
|
||||
uint16_t currentX10 = (voltageX10 * 10) / tipResistancex10;
|
||||
return currentX10;
|
||||
}
|
||||
@@ -12,6 +12,10 @@ class Utils {
|
||||
public:
|
||||
static int32_t InterpolateLookupTable(const int32_t *lookupTable, const int noItems, const int32_t value);
|
||||
static int32_t LinearInterpolate(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x);
|
||||
|
||||
// Return the required current in X10 for the specified voltage
|
||||
static uint16_t RequiredCurrentForTipAtVoltage(uint16_t voltageX10);
|
||||
|
||||
};
|
||||
|
||||
#endif /* CORE_DRIVERS_UTILS_H_ */
|
||||
|
||||
Reference in New Issue
Block a user