Merge branch 'dev' into epr-pinecil-og
This commit is contained in:
@@ -93,7 +93,7 @@ const uint8_t WarningBlock24[] = {
|
||||
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};
|
||||
|
||||
#if defined(MODEL_TS100) + defined(MODEL_Pinecil) + defined(MODEL_Pinecilv2) > 0
|
||||
#if defined(MODEL_TS100) + defined(MODEL_Pinecil) + defined(MODEL_Pinecilv2) +defined(MODEL_TS101) > 0
|
||||
const uint8_t buttonA[] = {
|
||||
// width = 42
|
||||
// height = 16
|
||||
@@ -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 "I2CBB2.hpp"
|
||||
#include "Utils.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#if POW_PD_EXT == 1
|
||||
bool hub238_probe() { return I2CBB2::probe(HUB238_ADDR); }
|
||||
|
||||
extern int32_t powerSupplyWattageLimit;
|
||||
|
||||
uint16_t hub238_debug_state() {
|
||||
uint8_t status0 = 0;
|
||||
uint8_t status1 = 0;
|
||||
if (!I2CBB2::Mem_Read(HUB238_ADDR, HUB238_REG_PD_STATUS0, &status0, 1)) {
|
||||
return 0xFFFF;
|
||||
}
|
||||
if (!I2CBB2::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 (I2CBB2::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 (I2CBB2::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 (I2CBB2::Mem_Write(HUB238_ADDR, HUB238_REG_SRC_PDO, ¤tPDO, 1) == true) {
|
||||
|
||||
currentPDO = 0x01; // request for new PDO
|
||||
if (I2CBB2::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 (I2CBB2::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 (I2CBB2::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 (I2CBB2::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
|
||||
317
source/Core/Drivers/I2CBB1.cpp
Normal file
317
source/Core/Drivers/I2CBB1.cpp
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* I2CBB1.cpp
|
||||
*
|
||||
* Created on: 12 Jun 2020
|
||||
* Author: Ralim
|
||||
*/
|
||||
#include "configuration.h"
|
||||
#ifdef I2C_SOFT_BUS_1
|
||||
#include "FreeRTOS.h"
|
||||
#include <I2CBB1.hpp>
|
||||
SemaphoreHandle_t I2CBB1::I2CSemaphore = NULL;
|
||||
StaticSemaphore_t I2CBB1::xSemaphoreBuffer;
|
||||
void I2CBB1::init() {
|
||||
// Set GPIO's to output open drain
|
||||
GPIO_InitTypeDef GPIO_InitStruct;
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
|
||||
GPIO_InitStruct.Pin = SDA_Pin;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
|
||||
GPIO_InitStruct.Pull = GPIO_PULLUP;
|
||||
HAL_GPIO_Init(SDA_GPIO_Port, &GPIO_InitStruct);
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
|
||||
GPIO_InitStruct.Pin = SCL_Pin;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
|
||||
GPIO_InitStruct.Pull = GPIO_PULLUP;
|
||||
HAL_GPIO_Init(SCL_GPIO_Port, &GPIO_InitStruct);
|
||||
SOFT_SDA1_HIGH();
|
||||
SOFT_SCL1_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_SCL1_LOW();
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
SOFT_SCL1_HIGH();
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
}
|
||||
I2CSemaphore = xSemaphoreCreateMutexStatic(&xSemaphoreBuffer);
|
||||
unlock();
|
||||
}
|
||||
|
||||
bool I2CBB1::probe(uint8_t address) {
|
||||
if (!lock())
|
||||
return false;
|
||||
start();
|
||||
bool ack = send(address);
|
||||
stop();
|
||||
unlock();
|
||||
return ack;
|
||||
}
|
||||
|
||||
bool I2CBB1::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_SCL1_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 I2CBB1::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();
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
ack = send(MemAddress);
|
||||
if (!ack) {
|
||||
stop();
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
while (Size) {
|
||||
resetWatchdog();
|
||||
ack = send(pData[0]);
|
||||
if (!ack) {
|
||||
stop();
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
pData++;
|
||||
Size--;
|
||||
}
|
||||
stop();
|
||||
unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
void I2CBB1::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 I2CBB1::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 I2CBB1::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 I2CBB1::start() {
|
||||
/* I2C Start condition, data line goes low when clock is high */
|
||||
SOFT_SCL1_HIGH();
|
||||
SOFT_SDA1_HIGH();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SDA1_LOW();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SCL1_LOW();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SDA1_HIGH();
|
||||
}
|
||||
|
||||
void I2CBB1::stop() {
|
||||
/* I2C Stop condition, clock goes high when data is low */
|
||||
SOFT_SDA1_LOW();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SCL1_HIGH();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SDA1_HIGH();
|
||||
SOFT_I2C_DELAY();
|
||||
}
|
||||
|
||||
bool I2CBB1::send(uint8_t value) {
|
||||
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
write_bit(value & 0x80); // write the most-significant bit
|
||||
value <<= 1;
|
||||
}
|
||||
|
||||
SOFT_SDA1_HIGH();
|
||||
bool ack = (read_bit() == 0);
|
||||
return ack;
|
||||
}
|
||||
|
||||
uint8_t I2CBB1::read(bool ack) {
|
||||
uint8_t B = 0;
|
||||
|
||||
uint8_t i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
B <<= 1;
|
||||
B |= read_bit();
|
||||
}
|
||||
|
||||
SOFT_SDA1_HIGH();
|
||||
if (ack)
|
||||
write_bit(0);
|
||||
else
|
||||
write_bit(1);
|
||||
return B;
|
||||
}
|
||||
|
||||
uint8_t I2CBB1::read_bit() {
|
||||
uint8_t b;
|
||||
|
||||
SOFT_SDA1_HIGH();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SCL1_HIGH();
|
||||
SOFT_I2C_DELAY();
|
||||
|
||||
if (SOFT_SDA1_READ())
|
||||
b = 1;
|
||||
else
|
||||
b = 0;
|
||||
|
||||
SOFT_SCL1_LOW();
|
||||
return b;
|
||||
}
|
||||
|
||||
void I2CBB1::unlock() { xSemaphoreGive(I2CSemaphore); }
|
||||
|
||||
bool I2CBB1::lock() {
|
||||
if (I2CSemaphore == NULL) {}
|
||||
bool a = xSemaphoreTake(I2CSemaphore, (TickType_t)100) == pdTRUE;
|
||||
return a;
|
||||
}
|
||||
|
||||
bool I2CBB1::I2C_RegisterWrite(uint8_t address, uint8_t reg, uint8_t data) { return Mem_Write(address, reg, &data, 1); }
|
||||
|
||||
uint8_t I2CBB1::I2C_RegisterRead(uint8_t address, uint8_t reg) {
|
||||
uint8_t temp = 0;
|
||||
Mem_Read(address, reg, &temp, 1);
|
||||
return temp;
|
||||
}
|
||||
|
||||
void I2CBB1::write_bit(uint8_t val) {
|
||||
if (val) {
|
||||
SOFT_SDA1_HIGH();
|
||||
} else {
|
||||
SOFT_SDA1_LOW();
|
||||
}
|
||||
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SCL1_HIGH();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SCL1_LOW();
|
||||
}
|
||||
|
||||
bool I2CBB1::writeRegistersBulk(const uint8_t address, const I2C_REG *registers, const uint8_t registersLength) {
|
||||
for (int index = 0; index < registersLength; index++) {
|
||||
if (!I2C_RegisterWrite(address, registers[index].reg, registers[index].val)) {
|
||||
return false;
|
||||
}
|
||||
if (registers[index].pause_ms)
|
||||
delay_ms(registers[index].pause_ms);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -1,14 +1,14 @@
|
||||
/*
|
||||
* I2CBB.hpp
|
||||
* I2CBB1.hpp
|
||||
*
|
||||
* Created on: 12 Jun 2020
|
||||
* Author: Ralim
|
||||
*/
|
||||
|
||||
#ifndef BSP_MINIWARE_I2CBB_HPP_
|
||||
#define BSP_MINIWARE_I2CBB_HPP_
|
||||
#ifndef BSP_MINIWARE_I2CBB1_HPP_
|
||||
#define BSP_MINIWARE_I2CBB1_HPP_
|
||||
#include "configuration.h"
|
||||
#ifdef I2C_SOFT
|
||||
#ifdef I2C_SOFT_BUS_1
|
||||
#include "BSP.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "Pins.h"
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "Software_I2C.h"
|
||||
#include "semphr.h"
|
||||
|
||||
class I2CBB {
|
||||
class I2CBB1 {
|
||||
public:
|
||||
static void init();
|
||||
// Probe if device ACK's address or not
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* I2CBB.cpp
|
||||
* I2CBB2.cpp
|
||||
*
|
||||
* Created on: 12 Jun 2020
|
||||
* Author: Ralim
|
||||
*/
|
||||
#include "configuration.h"
|
||||
#ifdef I2C_SOFT
|
||||
#ifdef I2C_SOFT_BUS_2
|
||||
#include "FreeRTOS.h"
|
||||
#include <I2CBB.hpp>
|
||||
SemaphoreHandle_t I2CBB::I2CSemaphore = NULL;
|
||||
StaticSemaphore_t I2CBB::xSemaphoreBuffer;
|
||||
void I2CBB::init() {
|
||||
#include <I2CBB2.hpp>
|
||||
SemaphoreHandle_t I2CBB2::I2CSemaphore = NULL;
|
||||
StaticSemaphore_t I2CBB2::xSemaphoreBuffer;
|
||||
void I2CBB2::init() {
|
||||
// Set GPIO's to output open drain
|
||||
GPIO_InitTypeDef GPIO_InitStruct;
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
@@ -24,13 +24,26 @@ void I2CBB::init() {
|
||||
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();
|
||||
SOFT_SDA2_HIGH();
|
||||
SOFT_SCL2_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_SCL2_LOW();
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
SOFT_SCL2_HIGH();
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
asm("nop");
|
||||
}
|
||||
I2CSemaphore = xSemaphoreCreateMutexStatic(&xSemaphoreBuffer);
|
||||
unlock();
|
||||
}
|
||||
|
||||
bool I2CBB::probe(uint8_t address) {
|
||||
bool I2CBB2::probe(uint8_t address) {
|
||||
if (!lock())
|
||||
return false;
|
||||
start();
|
||||
@@ -40,7 +53,7 @@ bool I2CBB::probe(uint8_t address) {
|
||||
return ack;
|
||||
}
|
||||
|
||||
bool I2CBB::Mem_Read(uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, uint16_t Size) {
|
||||
bool I2CBB2::Mem_Read(uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, uint16_t Size) {
|
||||
if (!lock())
|
||||
return false;
|
||||
start();
|
||||
@@ -56,7 +69,7 @@ bool I2CBB::Mem_Read(uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, u
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
SOFT_SCL_LOW();
|
||||
SOFT_SCL2_LOW();
|
||||
SOFT_I2C_DELAY();
|
||||
// stop();
|
||||
start();
|
||||
@@ -76,21 +89,19 @@ bool I2CBB::Mem_Read(uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, u
|
||||
return true;
|
||||
}
|
||||
|
||||
bool I2CBB::Mem_Write(uint16_t DevAddress, uint16_t MemAddress, const uint8_t *pData, uint16_t Size) {
|
||||
bool I2CBB2::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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -111,7 +121,7 @@ bool I2CBB::Mem_Write(uint16_t DevAddress, uint16_t MemAddress, const uint8_t *p
|
||||
return true;
|
||||
}
|
||||
|
||||
void I2CBB::Transmit(uint16_t DevAddress, uint8_t *pData, uint16_t Size) {
|
||||
void I2CBB2::Transmit(uint16_t DevAddress, uint8_t *pData, uint16_t Size) {
|
||||
if (!lock())
|
||||
return;
|
||||
start();
|
||||
@@ -135,7 +145,7 @@ void I2CBB::Transmit(uint16_t DevAddress, uint8_t *pData, uint16_t Size) {
|
||||
unlock();
|
||||
}
|
||||
|
||||
void I2CBB::Receive(uint16_t DevAddress, uint8_t *pData, uint16_t Size) {
|
||||
void I2CBB2::Receive(uint16_t DevAddress, uint8_t *pData, uint16_t Size) {
|
||||
if (!lock())
|
||||
return;
|
||||
start();
|
||||
@@ -154,7 +164,7 @@ void I2CBB::Receive(uint16_t DevAddress, uint8_t *pData, uint16_t Size) {
|
||||
unlock();
|
||||
}
|
||||
|
||||
void I2CBB::TransmitReceive(uint16_t DevAddress, uint8_t *pData_tx, uint16_t Size_tx, uint8_t *pData_rx, uint16_t Size_rx) {
|
||||
void I2CBB2::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)
|
||||
@@ -196,41 +206,41 @@ void I2CBB::TransmitReceive(uint16_t DevAddress, uint8_t *pData_tx, uint16_t Siz
|
||||
unlock();
|
||||
}
|
||||
|
||||
void I2CBB::start() {
|
||||
void I2CBB2::start() {
|
||||
/* I2C Start condition, data line goes low when clock is high */
|
||||
SOFT_SCL_HIGH();
|
||||
SOFT_SDA_HIGH();
|
||||
SOFT_SCL2_HIGH();
|
||||
SOFT_SDA2_HIGH();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SDA_LOW();
|
||||
SOFT_SDA2_LOW();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SCL_LOW();
|
||||
SOFT_SCL2_LOW();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SDA_HIGH();
|
||||
SOFT_SDA2_HIGH();
|
||||
}
|
||||
|
||||
void I2CBB::stop() {
|
||||
void I2CBB2::stop() {
|
||||
/* I2C Stop condition, clock goes high when data is low */
|
||||
SOFT_SDA_LOW();
|
||||
SOFT_SDA2_LOW();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SCL_HIGH();
|
||||
SOFT_SCL2_HIGH();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SDA_HIGH();
|
||||
SOFT_SDA2_HIGH();
|
||||
SOFT_I2C_DELAY();
|
||||
}
|
||||
|
||||
bool I2CBB::send(uint8_t value) {
|
||||
bool I2CBB2::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();
|
||||
SOFT_SDA2_HIGH();
|
||||
bool ack = (read_bit() == 0);
|
||||
return ack;
|
||||
}
|
||||
|
||||
uint8_t I2CBB::read(bool ack) {
|
||||
uint8_t I2CBB2::read(bool ack) {
|
||||
uint8_t B = 0;
|
||||
|
||||
uint8_t i;
|
||||
@@ -239,7 +249,7 @@ uint8_t I2CBB::read(bool ack) {
|
||||
B |= read_bit();
|
||||
}
|
||||
|
||||
SOFT_SDA_HIGH();
|
||||
SOFT_SDA2_HIGH();
|
||||
if (ack)
|
||||
write_bit(0);
|
||||
else
|
||||
@@ -247,55 +257,53 @@ uint8_t I2CBB::read(bool ack) {
|
||||
return B;
|
||||
}
|
||||
|
||||
uint8_t I2CBB::read_bit() {
|
||||
uint8_t I2CBB2::read_bit() {
|
||||
uint8_t b;
|
||||
|
||||
SOFT_SDA_HIGH();
|
||||
SOFT_SDA2_HIGH();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SCL_HIGH();
|
||||
SOFT_SCL2_HIGH();
|
||||
SOFT_I2C_DELAY();
|
||||
|
||||
if (SOFT_SDA_READ())
|
||||
if (SOFT_SDA2_READ())
|
||||
b = 1;
|
||||
else
|
||||
b = 0;
|
||||
|
||||
SOFT_SCL_LOW();
|
||||
SOFT_SCL2_LOW();
|
||||
return b;
|
||||
}
|
||||
|
||||
void I2CBB::unlock() { xSemaphoreGive(I2CSemaphore); }
|
||||
void I2CBB2::unlock() { xSemaphoreGive(I2CSemaphore); }
|
||||
|
||||
bool I2CBB::lock() {
|
||||
if (I2CSemaphore == NULL) {
|
||||
asm("bkpt");
|
||||
}
|
||||
bool I2CBB2::lock() {
|
||||
if (I2CSemaphore == NULL) {}
|
||||
bool a = xSemaphoreTake(I2CSemaphore, (TickType_t)100) == pdTRUE;
|
||||
return a;
|
||||
}
|
||||
|
||||
bool I2CBB::I2C_RegisterWrite(uint8_t address, uint8_t reg, uint8_t data) { return Mem_Write(address, reg, &data, 1); }
|
||||
bool I2CBB2::I2C_RegisterWrite(uint8_t address, uint8_t reg, uint8_t data) { return Mem_Write(address, reg, &data, 1); }
|
||||
|
||||
uint8_t I2CBB::I2C_RegisterRead(uint8_t address, uint8_t reg) {
|
||||
uint8_t I2CBB2::I2C_RegisterRead(uint8_t address, uint8_t reg) {
|
||||
uint8_t temp = 0;
|
||||
Mem_Read(address, reg, &temp, 1);
|
||||
return temp;
|
||||
}
|
||||
|
||||
void I2CBB::write_bit(uint8_t val) {
|
||||
void I2CBB2::write_bit(uint8_t val) {
|
||||
if (val) {
|
||||
SOFT_SDA_HIGH();
|
||||
SOFT_SDA2_HIGH();
|
||||
} else {
|
||||
SOFT_SDA_LOW();
|
||||
SOFT_SDA2_LOW();
|
||||
}
|
||||
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SCL_HIGH();
|
||||
SOFT_SCL2_HIGH();
|
||||
SOFT_I2C_DELAY();
|
||||
SOFT_SCL_LOW();
|
||||
SOFT_SCL2_LOW();
|
||||
}
|
||||
|
||||
bool I2CBB::writeRegistersBulk(const uint8_t address, const I2C_REG *registers, const uint8_t registersLength) {
|
||||
bool I2CBB2::writeRegistersBulk(const uint8_t address, const I2C_REG *registers, const uint8_t registersLength) {
|
||||
for (int index = 0; index < registersLength; index++) {
|
||||
if (!I2C_RegisterWrite(address, registers[index].reg, registers[index].val)) {
|
||||
return false;
|
||||
53
source/Core/Drivers/I2CBB2.hpp
Normal file
53
source/Core/Drivers/I2CBB2.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* I2CBB2.hpp
|
||||
*
|
||||
* Created on: 12 Jun 2020
|
||||
* Author: Ralim
|
||||
*/
|
||||
|
||||
#ifndef BSP_MINIWARE_I2CBB2_HPP_
|
||||
#define BSP_MINIWARE_I2CBB2_HPP_
|
||||
#include "configuration.h"
|
||||
#ifdef I2C_SOFT_BUS_2
|
||||
#include "BSP.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "Pins.h"
|
||||
#include "Setup.h"
|
||||
#include "Software_I2C.h"
|
||||
#include "semphr.h"
|
||||
|
||||
class I2CBB2 {
|
||||
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 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 SemaphoreHandle_t I2CSemaphore;
|
||||
static StaticSemaphore_t xSemaphoreBuffer;
|
||||
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_ */
|
||||
@@ -10,26 +10,26 @@
|
||||
#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}};
|
||||
static const ACCEL_I2C_CLASS::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])); }
|
||||
bool LIS2DH12::initalize() { return ACCEL_I2C_CLASS::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));
|
||||
ACCEL_I2C_CLASS::Mem_Read(LIS2DH_I2C_ADDRESS, 0xA8, reinterpret_cast<uint8_t *>(sensorData.begin()), sensorData.size() * sizeof(int16_t));
|
||||
|
||||
x = sensorData[0];
|
||||
y = sensorData[1];
|
||||
@@ -37,13 +37,21 @@ void LIS2DH12::getAxisReadings(int16_t &x, int16_t &y, int16_t &z) {
|
||||
}
|
||||
|
||||
bool LIS2DH12::detect() {
|
||||
if (!FRToSI2C::probe(LIS2DH_I2C_ADDRESS)) {
|
||||
if (!ACCEL_I2C_CLASS::probe(LIS2DH_I2C_ADDRESS)) {
|
||||
return false;
|
||||
}
|
||||
// Read chip id to ensure its not an address collision
|
||||
uint8_t id = 0;
|
||||
if (FRToSI2C::Mem_Read(LIS2DH_I2C_ADDRESS, LIS2DH_WHOAMI_REG, &id, 1)) {
|
||||
return id == LIS2DH_WHOAMI_ID;
|
||||
if (ACCEL_I2C_CLASS::Mem_Read(LIS2DH_I2C_ADDRESS, LIS2DH_WHOAMI_REG, &id, 1)) {
|
||||
return (id == LIS2DH_WHOAMI_ID) || (id == LIS2DH_CLONE_WHOAMI_ID);
|
||||
}
|
||||
return false; // cant read ID
|
||||
}
|
||||
|
||||
bool LIS2DH12::isClone() {
|
||||
uint8_t id = 0;
|
||||
if (ACCEL_I2C_CLASS::Mem_Read(LIS2DH_I2C_ADDRESS, LIS2DH_WHOAMI_REG, &id, 1)) {
|
||||
return (id == LIS2DH_CLONE_WHOAMI_ID);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -8,17 +8,19 @@
|
||||
#ifndef LIS2DH12_HPP_
|
||||
#define LIS2DH12_HPP_
|
||||
#include "BSP.h"
|
||||
#include "I2C_Wrapper.hpp"
|
||||
|
||||
#include "LIS2DH12_defines.hpp"
|
||||
#include "accelerometers_common.h"
|
||||
|
||||
class LIS2DH12 {
|
||||
public:
|
||||
static bool detect();
|
||||
static bool isClone();
|
||||
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);
|
||||
uint8_t val = (ACCEL_I2C_CLASS::I2C_RegisterRead(LIS2DH_I2C_ADDRESS, LIS_INT2_SRC) >> 2);
|
||||
if (val == 8)
|
||||
val = 3;
|
||||
else if (val == 1)
|
||||
@@ -29,7 +31,7 @@ public:
|
||||
val = 3;
|
||||
return static_cast<Orientation>(val);
|
||||
#else
|
||||
return static_cast<Orientation>((FRToSI2C::I2C_RegisterRead(LIS2DH_I2C_ADDRESS, LIS_INT2_SRC) >> 2) - 1);
|
||||
return static_cast<Orientation>((ACCEL_I2C_CLASS::I2C_RegisterRead(LIS2DH_I2C_ADDRESS, LIS_INT2_SRC) >> 2) - 1);
|
||||
#endif
|
||||
}
|
||||
static void getAxisReadings(int16_t &x, int16_t &y, int16_t &z);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#define LIS2DH_I2C_ADDRESS (25 << 1)
|
||||
#define LIS2DH_WHOAMI_REG 0x0F
|
||||
#define LIS2DH_WHOAMI_ID (0b00110011)
|
||||
#define LIS2DH_CLONE_WHOAMI_ID 0x11
|
||||
#define LIS_CTRL_REG1 0x20 | 0x80
|
||||
#define LIS_CTRL_REG2 0x21 | 0x80
|
||||
#define LIS_CTRL_REG3 0x22 | 0x80
|
||||
|
||||
@@ -14,17 +14,16 @@
|
||||
#include <string.h>
|
||||
|
||||
// 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
|
||||
uint8_t *OLED::stripPointers[4]; // Pointers to the strips to allow for buffer having extra content
|
||||
|
||||
bool OLED::inLeftHandedMode; // Whether the screen is in left or not (used for
|
||||
// offsets in GRAM)
|
||||
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::secondFrameBuffer[OLED_WIDTH * 2];
|
||||
uint8_t OLED::screenBuffer[16 + (OLED_WIDTH * (OLED_HEIGHT / 8)) + 10]; // The data buffer
|
||||
uint8_t OLED::secondFrameBuffer[16 + (OLED_WIDTH * (OLED_HEIGHT / 8)) + 10];
|
||||
uint32_t OLED::displayChecksum;
|
||||
/*Setup params for the OLED screen*/
|
||||
/*http://www.displayfuture.com/Display/datasheet/controller/SSD1307.pdf*/
|
||||
@@ -32,31 +31,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, 0xAE, 0}, /*Display off*/
|
||||
{0x80, OLED_DIVIDER, 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*/
|
||||
{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*/
|
||||
#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 +74,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 +90,7 @@ const uint8_t REFRESH_COMMANDS[17] = {
|
||||
0x80,
|
||||
0x00, // A
|
||||
0x80,
|
||||
0x01, // B
|
||||
(OLED_HEIGHT / 8) - 1, // B
|
||||
|
||||
// Start of data
|
||||
0x40,
|
||||
@@ -99,7 +102,7 @@ const uint8_t REFRESH_COMMANDS[17] = {
|
||||
* 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; }
|
||||
static uint16_t easeInOutTiming(uint16_t t) { return t * t * (300 - 2 * t) / 10000; }
|
||||
|
||||
/*
|
||||
* Returns the value between a and b, using a percentage value t.
|
||||
@@ -107,15 +110,24 @@ static uint8_t easeInOutTiming(uint8_t t) { return t * t * (300 - 2 * t) / 10000
|
||||
* @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; }
|
||||
static uint16_t lerp(uint16_t a, uint16_t b, uint16_t t) { return a + t * (b - a) / 100; }
|
||||
|
||||
void OLED::initialize() {
|
||||
cursor_x = cursor_y = 0;
|
||||
inLeftHandedMode = false;
|
||||
firstStripPtr = &screenBuffer[FRAMEBUFFER_START];
|
||||
secondStripPtr = &screenBuffer[FRAMEBUFFER_START + OLED_WIDTH];
|
||||
displayOffset = 0;
|
||||
#ifdef OLED_128x32
|
||||
stripPointers[0] = &screenBuffer[FRAMEBUFFER_START];
|
||||
stripPointers[1] = &screenBuffer[FRAMEBUFFER_START + OLED_WIDTH];
|
||||
stripPointers[2] = &screenBuffer[FRAMEBUFFER_START + 2 * OLED_WIDTH];
|
||||
stripPointers[3] = &screenBuffer[FRAMEBUFFER_START + 3 * OLED_WIDTH];
|
||||
|
||||
#else
|
||||
stripPointers[0] = &screenBuffer[FRAMEBUFFER_START];
|
||||
stripPointers[1] = &screenBuffer[FRAMEBUFFER_START + OLED_WIDTH];
|
||||
#endif
|
||||
displayOffset = 0;
|
||||
memcpy(&screenBuffer[0], &REFRESH_COMMANDS[0], sizeof(REFRESH_COMMANDS));
|
||||
memcpy(&secondFrameBuffer[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.
|
||||
@@ -129,14 +141,13 @@ void OLED::initialize() {
|
||||
initDone = true;
|
||||
}
|
||||
void OLED::setFramebuffer(uint8_t *buffer) {
|
||||
if (buffer == NULL) {
|
||||
firstStripPtr = &screenBuffer[FRAMEBUFFER_START];
|
||||
secondStripPtr = &screenBuffer[FRAMEBUFFER_START + OLED_WIDTH];
|
||||
return;
|
||||
}
|
||||
stripPointers[0] = &buffer[FRAMEBUFFER_START];
|
||||
stripPointers[1] = &buffer[FRAMEBUFFER_START + OLED_WIDTH];
|
||||
|
||||
firstStripPtr = &buffer[0];
|
||||
secondStripPtr = &buffer[OLED_WIDTH];
|
||||
#ifdef OLED_128x32
|
||||
stripPointers[2] = &buffer[FRAMEBUFFER_START + (2 * OLED_WIDTH)];
|
||||
stripPointers[3] = &buffer[FRAMEBUFFER_START + (3 * OLED_WIDTH)];
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -217,7 +228,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,
|
||||
@@ -229,7 +240,10 @@ void OLED::maskScrollIndicatorOnOLED() {
|
||||
|
||||
// Start of data
|
||||
0x40,
|
||||
|
||||
#ifdef OLED_128x32
|
||||
0x00,
|
||||
0x00,
|
||||
#endif
|
||||
// Clears two 8px strips
|
||||
0x00,
|
||||
0x00,
|
||||
@@ -245,19 +259,26 @@ void OLED::maskScrollIndicatorOnOLED() {
|
||||
* 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];
|
||||
uint8_t *stripBackPointers[4];
|
||||
stripBackPointers[0] = &secondFrameBuffer[FRAMEBUFFER_START + 0];
|
||||
stripBackPointers[1] = &secondFrameBuffer[FRAMEBUFFER_START + OLED_WIDTH];
|
||||
|
||||
#ifdef OLED_128x32
|
||||
stripBackPointers[2] = &secondFrameBuffer[OLED_WIDTH * 2];
|
||||
stripBackPointers[3] = &secondFrameBuffer[OLED_WIDTH * 3];
|
||||
#endif
|
||||
|
||||
TickType_t totalDuration = TICKS_100MS * 5; // 500ms
|
||||
TickType_t duration = 0;
|
||||
TickType_t start = xTaskGetTickCount();
|
||||
uint8_t offset = 0;
|
||||
|
||||
TickType_t startDraw = xTaskGetTickCount();
|
||||
while (duration <= totalDuration) {
|
||||
duration = xTaskGetTickCount() - start;
|
||||
uint8_t progress = ((duration * 100) / totalDuration); // Percentage of the period we are through for animation
|
||||
progress = easeInOutTiming(progress);
|
||||
progress = lerp(0, OLED_WIDTH, progress);
|
||||
duration = xTaskGetTickCount() - start;
|
||||
uint16_t progress = ((duration * 100) / totalDuration); // Percentage of the period we are through for animation
|
||||
progress = easeInOutTiming(progress);
|
||||
progress = lerp(0, OLED_WIDTH, progress);
|
||||
// Constrain
|
||||
if (progress > OLED_WIDTH) {
|
||||
progress = OLED_WIDTH;
|
||||
}
|
||||
@@ -274,14 +295,22 @@ void OLED::transitionSecondaryFramebuffer(bool forwardNavigation) {
|
||||
|
||||
offset = progress;
|
||||
|
||||
memmove(&firstStripPtr[oldStart], &firstStripPtr[oldPrevious], OLED_WIDTH - progress);
|
||||
memmove(&secondStripPtr[oldStart], &secondStripPtr[oldPrevious], OLED_WIDTH - progress);
|
||||
memmove(&stripPointers[0][oldStart], &stripPointers[0][oldPrevious], OLED_WIDTH - progress);
|
||||
memmove(&stripPointers[1][oldStart], &stripPointers[1][oldPrevious], OLED_WIDTH - progress);
|
||||
#ifdef OLED_128x32
|
||||
memmove(&stripPointers[2][oldStart], &stripPointers[2][oldPrevious], OLED_WIDTH - progress);
|
||||
memmove(&stripPointers[3][oldStart], &stripPointers[3][oldPrevious], OLED_WIDTH - progress);
|
||||
#endif
|
||||
|
||||
memmove(&firstStripPtr[newStart], &firstBackStripPtr[newEnd], progress);
|
||||
memmove(&secondStripPtr[newStart], &secondBackStripPtr[newEnd], progress);
|
||||
memmove(&stripPointers[0][newStart], &stripBackPointers[0][newEnd], progress);
|
||||
memmove(&stripPointers[1][newStart], &stripBackPointers[1][newEnd], progress);
|
||||
#ifdef OLED_128x32
|
||||
memmove(&stripPointers[2][newStart], &stripBackPointers[2][newEnd], progress);
|
||||
memmove(&stripPointers[3][newStart], &stripBackPointers[3][newEnd], progress);
|
||||
#endif
|
||||
|
||||
refresh();
|
||||
osDelay(TICKS_100MS / 7);
|
||||
refresh(); // Now refresh to write out the contents to the new page
|
||||
vTaskDelayUntil(&startDraw, TICKS_100MS / 7);
|
||||
if (getButtonState() != BUTTON_NONE) {
|
||||
return;
|
||||
}
|
||||
@@ -292,50 +321,62 @@ void OLED::useSecondaryFramebuffer(bool useSecondary) {
|
||||
if (useSecondary) {
|
||||
setFramebuffer(secondFrameBuffer);
|
||||
} else {
|
||||
setFramebuffer(NULL);
|
||||
setFramebuffer(screenBuffer);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Plays a transition animation of scrolling downward. Note this does *not*
|
||||
* use the secondary framebuffer.
|
||||
* This assumes that the current display output buffer has the current on screen contents
|
||||
* Then the secondary buffer has the "new" contents to be slid up onto the screen
|
||||
* Sadly we cant use the hardware scroll as some devices with the 128x32 screens dont have the GRAM for holding both screens at once
|
||||
*
|
||||
* This transition relies on the previous screen data already in the OLED
|
||||
* RAM. The caller shall not call `OLED::refresh()` before calling this
|
||||
* 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;
|
||||
// Change page start address:
|
||||
screenBuffer[13] = nextPage;
|
||||
// Change page end address:
|
||||
screenBuffer[15] = nextPage + 1;
|
||||
TickType_t startDraw = xTaskGetTickCount();
|
||||
|
||||
refresh();
|
||||
osDelay(TICKS_100MS / 5);
|
||||
for (uint8_t heightPos = 0; heightPos < OLED_HEIGHT; heightPos++) {
|
||||
// For each line, we shuffle all bits up a row
|
||||
for (uint8_t xPos = 0; xPos < OLED_WIDTH; xPos++) {
|
||||
const uint16_t firstStripPos = FRAMEBUFFER_START + xPos;
|
||||
const uint16_t secondStripPos = firstStripPos + OLED_WIDTH;
|
||||
#ifdef OLED_128x32
|
||||
// For 32 pixel high OLED's we have four strips to tailchain
|
||||
const uint16_t thirdStripPos = secondStripPos + OLED_WIDTH;
|
||||
const uint16_t fourthStripPos = thirdStripPos + OLED_WIDTH;
|
||||
// Move the MSB off the first strip, and pop MSB from second strip onto the first strip
|
||||
screenBuffer[firstStripPos] = (screenBuffer[firstStripPos] >> 1) | ((screenBuffer[secondStripPos] & 0x01) << 7);
|
||||
// Now shuffle off the second strip
|
||||
screenBuffer[secondStripPos] = (screenBuffer[secondStripPos] >> 1) | ((screenBuffer[thirdStripPos] & 0x01) << 7);
|
||||
// Now shuffle off the third strip
|
||||
screenBuffer[thirdStripPos] = (screenBuffer[thirdStripPos] >> 1) | ((screenBuffer[fourthStripPos] & 0x01) << 7);
|
||||
// Now forth strip gets the start of the new buffer
|
||||
screenBuffer[fourthStripPos] = (screenBuffer[fourthStripPos] >> 1) | ((secondFrameBuffer[firstStripPos] & 0x01) << 7);
|
||||
// Now cycle all the secondary buffers
|
||||
|
||||
uint8_t const startLine = pageStart * 8 + 1;
|
||||
uint8_t const scrollTo = (pageStart + 2) * 8;
|
||||
|
||||
// Scroll the screen by changing display start line.
|
||||
for (uint8_t current = startLine; current <= scrollTo; current++) {
|
||||
if (getButtonState() != BUTTON_NONE) {
|
||||
current = scrollTo;
|
||||
secondFrameBuffer[firstStripPos] = (secondFrameBuffer[firstStripPos] >> 1) | ((secondFrameBuffer[secondStripPos] & 0x01) << 7);
|
||||
secondFrameBuffer[secondStripPos] = (secondFrameBuffer[secondStripPos] >> 1) | ((secondFrameBuffer[thirdStripPos] & 0x01) << 7);
|
||||
secondFrameBuffer[thirdStripPos] = (secondFrameBuffer[thirdStripPos] >> 1) | ((secondFrameBuffer[fourthStripPos] & 0x01) << 7);
|
||||
// Finally on the bottom row; we shuffle it up ready
|
||||
secondFrameBuffer[fourthStripPos] >>= 1;
|
||||
#else
|
||||
// Move the MSB off the first strip, and pop MSB from second strip onto the first strip
|
||||
screenBuffer[firstStripPos] = (screenBuffer[firstStripPos] >> 1) | ((screenBuffer[secondStripPos] & 0x01) << 7);
|
||||
// Now shuffle off the second strip MSB, and replace it with the MSB of the secondary buffer
|
||||
screenBuffer[secondStripPos] = (screenBuffer[secondStripPos] >> 1) | ((secondFrameBuffer[firstStripPos] & 0x01) << 7);
|
||||
// Finally, do the shuffle on the second frame buffer
|
||||
secondFrameBuffer[firstStripPos] = (secondFrameBuffer[firstStripPos] >> 1) | ((secondFrameBuffer[secondStripPos] & 0x01) << 7);
|
||||
// Finally on the bottom row; we shuffle it up ready
|
||||
secondFrameBuffer[secondStripPos] >>= 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Set display start line (0x40~0x7F):
|
||||
// X[5:0] - display start line value
|
||||
uint8_t scrollCommandByte = 0b01000000 | (current & 0b00111111);
|
||||
|
||||
// Also update setup command for "set display start line":
|
||||
OLED_Setup_Array[8].val = scrollCommandByte;
|
||||
|
||||
I2C_CLASS::I2C_RegisterWrite(DEVICEADDR_OLED, 0x80, scrollCommandByte);
|
||||
osDelay(TICKS_100MS / 7);
|
||||
if (getButtonState() != BUTTON_NONE) {
|
||||
// Exit early, but have to transition whole buffer
|
||||
memcpy(screenBuffer + FRAMEBUFFER_START, secondFrameBuffer + FRAMEBUFFER_START, sizeof(screenBuffer) - FRAMEBUFFER_START);
|
||||
refresh(); // Now refresh to write out the contents to the new page
|
||||
return;
|
||||
}
|
||||
refresh(); // Now refresh to write out the contents to the new page
|
||||
vTaskDelayUntil(&startDraw, TICKS_100MS / 7);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,25 +387,35 @@ 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);
|
||||
const int len = FRAMEBUFFER_START + (OLED_WIDTH * (OLED_HEIGHT / 8));
|
||||
I2C_CLASS::Transmit(DEVICEADDR_OLED, screenBuffer, len);
|
||||
osDelay(TICKS_10MS);
|
||||
checkDisplayBufferChecksum();
|
||||
@@ -381,14 +432,14 @@ void OLED::setInverseDisplay(bool inverse) {
|
||||
I2C_CLASS::I2C_RegisterWrite(DEVICEADDR_OLED, 0x80, normalInverseCmd);
|
||||
}
|
||||
|
||||
// print a string to the current cursor location
|
||||
void OLED::print(const char *const str, FontStyle fontStyle) {
|
||||
// print a string to the current cursor location, len chars MAX
|
||||
void OLED::print(const char *const str, FontStyle fontStyle, uint8_t len) {
|
||||
const uint8_t *next = reinterpret_cast<const uint8_t *>(str);
|
||||
if (next[0] == 0x01) {
|
||||
fontStyle = FontStyle::LARGE;
|
||||
next++;
|
||||
}
|
||||
while (next[0]) {
|
||||
while (next[0] && len--) {
|
||||
uint16_t index;
|
||||
if (next[0] <= 0xF0) {
|
||||
index = next[0];
|
||||
@@ -517,15 +568,16 @@ void OLED::drawArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uin
|
||||
if (y == 0) {
|
||||
// Splat first line of data
|
||||
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
|
||||
firstStripPtr[xx + x] = ptr[xx];
|
||||
stripPointers[0][xx + x] = ptr[xx];
|
||||
}
|
||||
}
|
||||
if (y == 8 || height == 16) {
|
||||
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)];
|
||||
stripPointers[1][x + xx] = ptr[xx + (height == 16 ? wide : 0)];
|
||||
}
|
||||
}
|
||||
// TODO NEEDS HEIGHT HANDLERS for 24/32
|
||||
}
|
||||
|
||||
// Draw an area, but y must be aligned on 0/8 offset
|
||||
@@ -551,15 +603,15 @@ void OLED::drawAreaSwapped(int16_t x, int8_t y, uint8_t wide, uint8_t height, co
|
||||
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];
|
||||
stripPointers[0][xx + x] = ptr[xx + 1];
|
||||
stripPointers[0][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)];
|
||||
stripPointers[1][x + xx] = ptr[xx + 1 + (height == 16 ? wide : 0)];
|
||||
stripPointers[1][x + xx + 1] = ptr[xx + (height == 16 ? wide : 0)];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -585,13 +637,13 @@ void OLED::fillArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uin
|
||||
if (y == 0) {
|
||||
// Splat first line of data
|
||||
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
|
||||
firstStripPtr[xx + x] = value;
|
||||
stripPointers[0][xx + x] = value;
|
||||
}
|
||||
}
|
||||
if (y == 8 || height == 16) {
|
||||
// Splat the second line
|
||||
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
|
||||
secondStripPtr[x + xx] = value;
|
||||
stripPointers[1][x + xx] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -607,9 +659,9 @@ void OLED::drawFilledRect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, bool c
|
||||
mask = mask << (y0 % 8);
|
||||
for (uint8_t col = x0; col < x1; col++)
|
||||
if (clear)
|
||||
firstStripPtr[(y0 / 8) * 96 + col] &= ~mask;
|
||||
stripPointers[0][(y0 / 8) * 96 + col] &= ~mask;
|
||||
else
|
||||
firstStripPtr[(y0 / 8) * 96 + col] |= mask;
|
||||
stripPointers[0][(y0 / 8) * 96 + col] |= mask;
|
||||
}
|
||||
// Next loop down the line the total number of solids
|
||||
if (y0 / 8 != y1 / 8)
|
||||
@@ -617,18 +669,18 @@ void OLED::drawFilledRect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, bool c
|
||||
for (uint8_t r = (y0 / 8); r < (y1 / 8); r++) {
|
||||
// This gives us the row index r
|
||||
if (clear)
|
||||
firstStripPtr[(r * 96) + col] = 0;
|
||||
stripPointers[0][(r * 96) + col] = 0;
|
||||
else
|
||||
firstStripPtr[(r * 96) + col] = 0xFF;
|
||||
stripPointers[0][(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;
|
||||
stripPointers[0][(y1 / 8) * 96 + col] &= ~mask;
|
||||
else
|
||||
firstStripPtr[(y1 / 8) * 96 + col] |= mask;
|
||||
stripPointers[0][(y1 / 8) * 96 + col] |= mask;
|
||||
}
|
||||
|
||||
void OLED::drawHeatSymbol(uint8_t state) {
|
||||
|
||||
@@ -24,17 +24,42 @@ extern "C" {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OLED_I2CBB
|
||||
#include "I2CBB.hpp"
|
||||
#define I2C_CLASS I2CBB
|
||||
#if defined(OLED_I2CBB2)
|
||||
#include "I2CBB2.hpp"
|
||||
#define I2C_CLASS I2CBB2
|
||||
#elif defined(OLED_I2CBB1)
|
||||
#include "I2CBB1.hpp"
|
||||
#define I2C_CLASS I2CBB1
|
||||
#else
|
||||
#define I2C_CLASS FRToSI2C
|
||||
#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
|
||||
#define OLED_WIDTH 128
|
||||
#define OLED_HEIGHT 32
|
||||
#define OLED_GRAM_START 0x00 // Should be 0x00 when we have full width
|
||||
#define OLED_GRAM_END 0x7F // Should be 0x7F when we have full width
|
||||
#define OLED_GRAM_START_FLIP 0
|
||||
#define OLED_GRAM_END_FLIP 0x7F
|
||||
|
||||
#define OLED_VCOM_LAYOUT 0x12
|
||||
#define OLED_SEGMENT_MAP_REVERSED
|
||||
#define OLED_DIVIDER 0xD3
|
||||
#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_DIVIDER 0xD5
|
||||
#define OLED_SEGMENT_MAP 0xA0
|
||||
|
||||
#endif
|
||||
#define FRAMEBUFFER_START 17
|
||||
|
||||
enum class FontStyle {
|
||||
@@ -51,9 +76,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 * (OLED_HEIGHT / 8));
|
||||
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
|
||||
@@ -82,7 +107,7 @@ public:
|
||||
static void setBrightness(uint8_t contrast);
|
||||
static void setInverseDisplay(bool inverted);
|
||||
static int16_t getCursorX() { return cursor_x; }
|
||||
static void print(const char *string, FontStyle fontStyle); // Draw a string to the current location, with selected font
|
||||
static void print(const char *string, FontStyle fontStyle, uint8_t length = 255); // Draw a string to the current location, with selected font; optionally - with MAX length only
|
||||
static void printWholeScreen(const char *string);
|
||||
// Set the cursor location by pixels
|
||||
static void setCursor(int16_t x, int16_t y) {
|
||||
@@ -94,7 +119,7 @@ public:
|
||||
// Draws a number at the current cursor location
|
||||
static void printNumber(uint16_t number, uint8_t places, FontStyle fontStyle, bool noLeaderZeros = true);
|
||||
// Clears the buffer
|
||||
static void clearScreen() { memset(firstStripPtr, 0, OLED_WIDTH * 2); }
|
||||
static void clearScreen() { memset(stripPointers[0], 0, OLED_WIDTH * (OLED_HEIGHT / 8)); }
|
||||
// Draws the battery level symbol
|
||||
static void drawBattery(uint8_t state) { drawSymbol(3 + (state > 10 ? 10 : state)); }
|
||||
// Draws a checkbox
|
||||
@@ -114,29 +139,28 @@ public:
|
||||
static void transitionScrollDown();
|
||||
|
||||
private:
|
||||
static bool checkDisplayBufferChecksum(){
|
||||
uint32_t hash = 0;
|
||||
const int len = FRAMEBUFFER_START + (OLED_WIDTH * 2);
|
||||
static bool checkDisplayBufferChecksum() {
|
||||
uint32_t hash = 0;
|
||||
const int len = sizeof(screenBuffer);
|
||||
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
|
||||
static uint8_t *secondStripPtr; // Pointers to the strips
|
||||
static uint8_t *stripPointers[4]; // Pointers to the strips to allow for buffer having extra content
|
||||
static bool inLeftHandedMode; // Whether the screen is in left or not (used for offsets in GRAM)
|
||||
static bool initDone;
|
||||
static DisplayState displayState;
|
||||
static int16_t cursor_x, cursor_y;
|
||||
static uint8_t displayOffset;
|
||||
static uint32_t displayChecksum;
|
||||
static uint8_t screenBuffer[16 + (OLED_WIDTH * 2) + 10]; // The data buffer
|
||||
static uint8_t secondFrameBuffer[OLED_WIDTH * 2];
|
||||
static uint8_t screenBuffer[16 + (OLED_WIDTH * (OLED_HEIGHT / 8)) + 10]; // The data buffer
|
||||
static uint8_t secondFrameBuffer[16 + OLED_WIDTH * (OLED_HEIGHT / 8) + 10];
|
||||
};
|
||||
|
||||
#endif /* OLED_HPP_ */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "USBPD.h"
|
||||
#include "configuration.h"
|
||||
#if POW_PD
|
||||
#ifdef POW_PD
|
||||
|
||||
#include "BSP_PD.h"
|
||||
#include "FreeRTOS.h"
|
||||
@@ -152,7 +152,7 @@ bool parseCapabilitiesArray(const uint8_t numCaps, uint8_t *bestIndex, uint16_t
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ((lastCapabilities[i] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED && (((lastCapabilities[i] & PD_APDO_TYPE) == PD_APDO_TYPE_PPS)) && getSettingValue(SettingsOptions::PDVpdoEnabled)) {
|
||||
} else if ((lastCapabilities[i] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED && (((lastCapabilities[i] & PD_APDO_TYPE) == PD_APDO_TYPE_PPS)) && getSettingValue(SettingsOptions::PDVpdo)) {
|
||||
// If this is a PPS slot, calculate the max voltage in the PPS range that can we be used and maintain
|
||||
uint16_t max_voltage = PD_PAV2MV(PD_APDO_PPS_MAX_VOLTAGE_GET(lastCapabilities[i]));
|
||||
// uint16_t min_voltage = PD_PAV2MV(PD_APDO_PPS_MIN_VOLTAGE_GET(lastCapabilities[i]));
|
||||
@@ -179,7 +179,7 @@ bool parseCapabilitiesArray(const uint8_t numCaps, uint8_t *bestIndex, uint16_t
|
||||
}
|
||||
}
|
||||
#ifdef POW_EPR
|
||||
else if ((lastCapabilities[i] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED && (((lastCapabilities[i] & PD_APDO_TYPE) == PD_APDO_TYPE_AVS)) && getSettingValue(SettingsOptions::PDVpdoEnabled)) {
|
||||
else if ((lastCapabilities[i] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED && (((lastCapabilities[i] & PD_APDO_TYPE) == PD_APDO_TYPE_AVS)) && getSettingValue(SettingsOptions::PDVpdo)) {
|
||||
uint16_t max_voltage = PD_PAV2MV(PD_APDO_AVS_MAX_VOLTAGE_GET(lastCapabilities[i]));
|
||||
uint8_t max_wattage = PD_APDO_AVS_MAX_POWER_GET(lastCapabilities[i]);
|
||||
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
15
source/Core/Drivers/accelerometers_common.h
Normal file
15
source/Core/Drivers/accelerometers_common.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef CORE_DRIVERS_ACCELEROMTERS_COMMON_H_
|
||||
#define CORE_DRIVERS_ACCELEROMTERS_COMMON_H_
|
||||
|
||||
#if defined(ACCEL_I2CBB2)
|
||||
#include "I2CBB2.hpp"
|
||||
#define ACCEL_I2C_CLASS I2CBB2
|
||||
#elif defined(ACCEL_I2CBB1)
|
||||
#include "I2CBB1.hpp"
|
||||
#define ACCEL_I2C_CLASS I2CBB1
|
||||
#else
|
||||
#include "I2C_Wrapper.hpp"
|
||||
#define ACCEL_I2C_CLASS FRToSI2C
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user