1
0
forked from me/IronOS

Merge branch 'master' into TweakTS80

This commit is contained in:
Ben V. Brown
2019-10-07 16:40:42 +11:00
262 changed files with 106266 additions and 307801 deletions

View File

@@ -0,0 +1,189 @@
/*
* FRToSI2C.cpp
*
* Created on: 14Apr.,2018
* Author: Ralim
*/
#include "hardware.h"
#include "FRToSI2C.hpp"
#define I2CUSESDMA
I2C_HandleTypeDef* FRToSI2C::i2c;
SemaphoreHandle_t FRToSI2C::I2CSemaphore;
StaticSemaphore_t FRToSI2C::xSemaphoreBuffer;
void FRToSI2C::CpltCallback() {
i2c->State = HAL_I2C_STATE_READY; // Force state reset (even if tx error)
if (I2CSemaphore) {
xSemaphoreGiveFromISR(I2CSemaphore, NULL);
}
}
void FRToSI2C::Mem_Read(uint16_t DevAddress, uint16_t MemAddress,
uint16_t MemAddSize, uint8_t* pData, uint16_t Size) {
if (I2CSemaphore == NULL) {
// no RToS, run blocking code
HAL_I2C_Mem_Read(i2c, DevAddress, MemAddress, MemAddSize, pData, Size,
5000);
} else {
// RToS is active, run threading
// Get the mutex so we can use the I2C port
// Wait up to 1 second for the mutex
if (xSemaphoreTake(I2CSemaphore, (TickType_t)50) == pdTRUE) {
#ifdef I2CUSESDMA
if (HAL_I2C_Mem_Read(i2c, DevAddress, MemAddress, MemAddSize, pData,
Size, 500) != HAL_OK) {
I2C1_ClearBusyFlagErratum();
xSemaphoreGive(I2CSemaphore);
}
xSemaphoreGive(I2CSemaphore);
#else
HAL_I2C_Mem_Read(i2c, DevAddress, MemAddress, MemAddSize, pData, Size,
5000);
xSemaphoreGive(I2CSemaphore);
#endif
} else {
}
}
}
void FRToSI2C::I2C_RegisterWrite(uint8_t address, uint8_t reg, uint8_t data) {
Mem_Write(address, reg, I2C_MEMADD_SIZE_8BIT, &data, 1);
}
uint8_t FRToSI2C::I2C_RegisterRead(uint8_t add, uint8_t reg) {
uint8_t tx_data[1];
Mem_Read(add, reg, I2C_MEMADD_SIZE_8BIT, tx_data, 1);
return tx_data[0];
}
void FRToSI2C::Mem_Write(uint16_t DevAddress, uint16_t MemAddress,
uint16_t MemAddSize, uint8_t* pData, uint16_t Size) {
if (I2CSemaphore == NULL) {
// no RToS, run blocking code
HAL_I2C_Mem_Write(i2c, DevAddress, MemAddress, MemAddSize, pData, Size,
5000);
} else {
// RToS is active, run threading
// Get the mutex so we can use the I2C port
// Wait up to 1 second for the mutex
if (xSemaphoreTake(I2CSemaphore, (TickType_t)50) == pdTRUE) {
#ifdef I2CUSESDMA
if (HAL_I2C_Mem_Write(i2c, DevAddress, MemAddress, MemAddSize,
pData, Size, 500) != HAL_OK) {
I2C1_ClearBusyFlagErratum();
xSemaphoreGive(I2CSemaphore);
}
xSemaphoreGive(I2CSemaphore);
#else
if (HAL_I2C_Mem_Write(i2c, DevAddress, MemAddress, MemAddSize, pData,
Size, 5000) != HAL_OK) {
}
xSemaphoreGive(I2CSemaphore);
#endif
} else {
}
}
}
void FRToSI2C::Transmit(uint16_t DevAddress, uint8_t* pData, uint16_t Size) {
if (I2CSemaphore == NULL) {
// no RToS, run blocking code
HAL_I2C_Master_Transmit(i2c, DevAddress, pData, Size, 5000);
} else {
// RToS is active, run threading
// Get the mutex so we can use the I2C port
// Wait up to 1 second for the mutex
if (xSemaphoreTake(I2CSemaphore, (TickType_t)50) == pdTRUE) {
#ifdef I2CUSESDMA
if (HAL_I2C_Master_Transmit_DMA(i2c, DevAddress, pData, Size)
!= HAL_OK) {
I2C1_ClearBusyFlagErratum();
xSemaphoreGive(I2CSemaphore);
}
#else
HAL_I2C_Master_Transmit(i2c, DevAddress, pData, Size, 5000);
xSemaphoreGive(I2CSemaphore);
#endif
} else {
}
}
}
void FRToSI2C::I2C1_ClearBusyFlagErratum() {
GPIO_InitTypeDef GPIO_InitStruct;
int timeout = 100;
int timeout_cnt = 0;
// 1. Clear PE bit.
i2c->Instance->CR1 &= ~(0x0001);
/**I2C1 GPIO Configuration
PB6 ------> I2C1_SCL
PB7 ------> I2C1_SDA
*/
// 2. Configure the SCL and SDA I/Os as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR).
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Pin = SCL_Pin;
HAL_GPIO_Init(SCL_GPIO_Port, &GPIO_InitStruct);
HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_SET);
GPIO_InitStruct.Pin = SDA_Pin;
HAL_GPIO_Init(SDA_GPIO_Port, &GPIO_InitStruct);
HAL_GPIO_WritePin(SDA_GPIO_Port, SDA_Pin, GPIO_PIN_SET);
while (GPIO_PIN_SET != HAL_GPIO_ReadPin(SDA_GPIO_Port, SDA_Pin)) {
//Move clock to release I2C
HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_RESET);
asm("nop");
asm("nop");
asm("nop");
asm("nop");
HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_SET);
timeout_cnt++;
if (timeout_cnt > timeout)
return;
}
// 12. Configure the SCL and SDA I/Os as Alternate function Open-Drain.
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Pin = SCL_Pin;
HAL_GPIO_Init(SCL_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SDA_Pin;
HAL_GPIO_Init(SDA_GPIO_Port, &GPIO_InitStruct);
HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(SDA_GPIO_Port, SDA_Pin, GPIO_PIN_SET);
// 13. Set SWRST bit in I2Cx_CR1 register.
i2c->Instance->CR1 |= 0x8000;
asm("nop");
// 14. Clear SWRST bit in I2Cx_CR1 register.
i2c->Instance->CR1 &= ~0x8000;
asm("nop");
// 15. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register
i2c->Instance->CR1 |= 0x0001;
// Call initialization function.
HAL_I2C_Init(i2c);
}

View File

@@ -0,0 +1,843 @@
/*
* GUIThread.cpp
*
* Created on: 19 Aug 2019
* Author: ralim
*/
#include <MMA8652FC.hpp>
#include <gui.hpp>
#include <main.hpp>
#include "LIS2DH12.hpp"
#include <history.hpp>
#include <power.hpp>
#include "Settings.h"
#include "Translation.h"
#include "cmsis_os.h"
#include "stdlib.h"
#include "stm32f1xx_hal.h"
#include "string.h"
extern uint8_t PCBVersion;
// File local variables
extern uint32_t currentlyActiveTemperatureTarget;
extern uint32_t lastMovementTime;
extern int16_t idealQCVoltage;
uint32_t lastButtonTime = 0;
extern osThreadId GUITaskHandle;
extern osThreadId MOVTaskHandle;
extern osThreadId PIDTaskHandle;
// TODO: express time constants in terms of dividends of portTICK_RATE_MS
#define MOVEMENT_INACTIVITY_TIME 6000
#define BUTTON_INACTIVITY_TIME 6000
static uint16_t min(uint16_t a, uint16_t b) {
if (a > b)
return b;
else
return a;
}
void printVoltage() {
uint32_t volt = getInputVoltageX10(systemSettings.voltageDiv, 0);
OLED::printNumber(volt / 10, 2);
OLED::print(SymbolDot);
OLED::printNumber(volt % 10, 1);
}
void GUIDelay() {
// Called in all UI looping tasks,
// This limits the re-draw rate to the LCD and also lets the DMA run
// As the gui task can very easily fill this bus with transactions, which will
// prevent the movement detection from running
osDelay(50);
}
void gui_drawTipTemp(bool symbol) {
// Draw tip temp handling unit conversion & tolerance near setpoint
uint16_t Temp = getTipRawTemp(0);
if (systemSettings.temperatureInF)
Temp = tipMeasurementToF(Temp);
else
Temp = tipMeasurementToC(Temp);
OLED::printNumber(Temp, 3); // Draw the tip temp out finally
if (symbol) {
if (systemSettings.temperatureInF)
OLED::print(SymbolDegF);
else
OLED::print(SymbolDegC);
}
}
ButtonState getButtonState() {
/*
* Read in the buttons and then determine if a state change needs to occur
*/
/*
* If the previous state was 00 Then we want to latch the new state if
* different & update time
* If the previous state was !00 Then we want to search if we trigger long
* press (buttons still down), or if release we trigger press
* (downtime>filter)
*/
static uint8_t previousState = 0;
static uint32_t previousStateChange = 0;
const uint16_t timeout = 40;
uint8_t currentState;
currentState = (
HAL_GPIO_ReadPin(KEY_A_GPIO_Port, KEY_A_Pin) == GPIO_PIN_RESET ?
1 : 0) << 0;
currentState |= (
HAL_GPIO_ReadPin(KEY_B_GPIO_Port, KEY_B_Pin) == GPIO_PIN_RESET ?
1 : 0) << 1;
if (currentState)
lastButtonTime = xTaskGetTickCount();
if (currentState == previousState) {
if (currentState == 0)
return BUTTON_NONE;
if ((xTaskGetTickCount() - previousStateChange) > timeout) {
// User has been holding the button down
// We want to send a buttong is held message
if (currentState == 0x01)
return BUTTON_F_LONG;
else if (currentState == 0x02)
return BUTTON_B_LONG;
else
return BUTTON_NONE; // Both being held case, we dont long hold this
} else
return BUTTON_NONE;
} else {
// A change in button state has occurred
ButtonState retVal = BUTTON_NONE;
if (currentState) {
// User has pressed a button down (nothing done on down)
if (currentState != previousState) {
// There has been a change in the button states
// If there is a rising edge on one of the buttons from double press we
// want to mask that out As users are having issues with not release
// both at once
if (previousState == 0x03)
currentState = 0x03;
}
} else {
// User has released buttons
// If they previously had the buttons down we want to check if they were <
// long hold and trigger a press
if ((xTaskGetTickCount() - previousStateChange) < timeout) {
// The user didn't hold the button for long
// So we send button press
if (previousState == 0x01)
retVal = BUTTON_F_SHORT;
else if (previousState == 0x02)
retVal = BUTTON_B_SHORT;
else
retVal = BUTTON_BOTH; // Both being held case
}
}
previousState = currentState;
previousStateChange = xTaskGetTickCount();
return retVal;
}
return BUTTON_NONE;
}
void waitForButtonPress() {
// we are just lazy and sleep until user confirms button press
// This also eats the button press event!
ButtonState buttons = getButtonState();
while (buttons) {
buttons = getButtonState();
GUIDelay();
}
while (!buttons) {
buttons = getButtonState();
GUIDelay();
}
}
void waitForButtonPressOrTimeout(uint32_t timeout) {
timeout += xTaskGetTickCount();
// calculate the exit point
ButtonState buttons = getButtonState();
while (buttons) {
buttons = getButtonState();
GUIDelay();
if (xTaskGetTickCount() > timeout)
return;
}
while (!buttons) {
buttons = getButtonState();
GUIDelay();
if (xTaskGetTickCount() > timeout)
return;
}
}
#ifdef MODEL_TS100
// returns true if undervoltage has occured
static bool checkVoltageForExit() {
uint16_t v = getInputVoltageX10(systemSettings.voltageDiv, 0);
//Dont check for first 1.5 seconds while the ADC stabilizes and the DMA fills the buffer
if (xTaskGetTickCount() > 150) {
if ((v < lookupVoltageLevel(systemSettings.cutoutSetting))) {
GUIDelay();
OLED::clearScreen();
OLED::setCursor(0, 0);
if (systemSettings.detailedSoldering) {
OLED::setFont(1);
OLED::print(UndervoltageString);
OLED::setCursor(0, 8);
OLED::print(InputVoltageString);
printVoltage();
OLED::print("V");
} else {
OLED::setFont(0);
OLED::print(UVLOWarningString);
}
OLED::refresh();
currentlyActiveTemperatureTarget = 0;
waitForButtonPress();
return true;
}
}
return false;
}
#endif
static void gui_drawBatteryIcon() {
#ifdef MODEL_TS100
if (systemSettings.cutoutSetting) {
// User is on a lithium battery
// we need to calculate which of the 10 levels they are on
uint8_t cellCount = systemSettings.cutoutSetting + 2;
uint32_t cellV = getInputVoltageX10(systemSettings.voltageDiv, 0)
/ cellCount;
// Should give us approx cell voltage X10
// Range is 42 -> 33 = 9 steps therefore we will use battery 1-10
if (cellV < 33)
cellV = 33;
cellV -= 33;// Should leave us a number of 0-9
if (cellV > 9)
cellV = 9;
OLED::drawBattery(cellV + 1);
} else
OLED::drawSymbol(15); // Draw the DC Logo
#else
// On TS80 we replace this symbol with the voltage we are operating on
// If <9V then show single digit, if not show duals
uint8_t V = getInputVoltageX10(systemSettings.voltageDiv, 0);
if (V % 10 >= 5)
V = V / 10 + 1; // round up
else
V = V / 10;
if (V >= 10) {
int16_t xPos = OLED::getCursorX();
OLED::setFont(1);
OLED::printNumber(1, 1);
OLED::setCursor(xPos, 8);
OLED::printNumber(V % 10, 1);
OLED::setFont(0);
OLED::setCursor(xPos + 12, 0); // need to reset this as if we drew a wide char
} else {
OLED::printNumber(V, 1);
}
#endif
}
static void gui_solderingTempAdjust() {
uint32_t lastChange = xTaskGetTickCount();
currentlyActiveTemperatureTarget = 0;
uint32_t autoRepeatTimer = 0;
uint8_t autoRepeatAcceleration = 0;
for (;;) {
OLED::setCursor(0, 0);
OLED::clearScreen();
OLED::setFont(0);
ButtonState buttons = getButtonState();
if (buttons)
lastChange = xTaskGetTickCount();
switch (buttons) {
case BUTTON_NONE:
// stay
break;
case BUTTON_BOTH:
// exit
return;
break;
case BUTTON_B_LONG:
if (xTaskGetTickCount() - autoRepeatTimer
+ autoRepeatAcceleration> PRESS_ACCEL_INTERVAL_MAX) {
systemSettings.SolderingTemp -= 10; // sub 10
autoRepeatTimer = xTaskGetTickCount();
autoRepeatAcceleration += PRESS_ACCEL_STEP;
}
break;
case BUTTON_F_LONG:
if (xTaskGetTickCount() - autoRepeatTimer
+ autoRepeatAcceleration> PRESS_ACCEL_INTERVAL_MAX) {
systemSettings.SolderingTemp += 10;
autoRepeatTimer = xTaskGetTickCount();
autoRepeatAcceleration += PRESS_ACCEL_STEP;
}
break;
case BUTTON_F_SHORT:
systemSettings.SolderingTemp += 10; // add 10
break;
case BUTTON_B_SHORT:
systemSettings.SolderingTemp -= 10; // sub 10
break;
default:
break;
}
if ((PRESS_ACCEL_INTERVAL_MAX - autoRepeatAcceleration)
< PRESS_ACCEL_INTERVAL_MIN) {
autoRepeatAcceleration = PRESS_ACCEL_INTERVAL_MAX
- PRESS_ACCEL_INTERVAL_MIN;
}
// constrain between 50-450 C
if (systemSettings.temperatureInF) {
if (systemSettings.SolderingTemp > 850)
systemSettings.SolderingTemp = 850;
if (systemSettings.SolderingTemp < 120)
systemSettings.SolderingTemp = 120;
} else {
if (systemSettings.SolderingTemp > 450)
systemSettings.SolderingTemp = 450;
if (systemSettings.SolderingTemp < 50)
systemSettings.SolderingTemp = 50;
}
if (xTaskGetTickCount() - lastChange > 200)
return; // exit if user just doesn't press anything for a bit
#ifdef MODEL_TS80
if (!OLED::getRotation())
#else
if (OLED::getRotation())
#endif
OLED::print(SymbolMinus);
else
OLED::print(SymbolPlus);
OLED::print(SymbolSpace);
OLED::printNumber(systemSettings.SolderingTemp, 3);
if (systemSettings.temperatureInF)
OLED::drawSymbol(0);
else
OLED::drawSymbol(1);
OLED::print(SymbolSpace);
#ifdef MODEL_TS80
if (!OLED::getRotation())
#else
if (OLED::getRotation())
#endif
OLED::print(SymbolPlus);
else
OLED::print(SymbolMinus);
OLED::refresh();
GUIDelay();
}
}
static int gui_SolderingSleepingMode() {
// Drop to sleep temperature and display until movement or button press
for (;;) {
ButtonState buttons = getButtonState();
if (buttons)
return 0;
if ((xTaskGetTickCount() - lastMovementTime < 100)
|| (xTaskGetTickCount() - lastButtonTime < 100))
return 0; // user moved or pressed a button, go back to soldering
#ifdef MODEL_TS100
if (checkVoltageForExit())
return 1; // return non-zero on error
#endif
if (systemSettings.temperatureInF) {
currentlyActiveTemperatureTarget = ftoTipMeasurement(
min(systemSettings.SleepTemp,
systemSettings.SolderingTemp));
} else {
currentlyActiveTemperatureTarget = ctoTipMeasurement(
min(systemSettings.SleepTemp,
systemSettings.SolderingTemp));
}
// draw the lcd
uint16_t tipTemp;
if (systemSettings.temperatureInF)
tipTemp = tipMeasurementToF(getTipRawTemp(0));
else
tipTemp = tipMeasurementToC(getTipRawTemp(0));
OLED::clearScreen();
OLED::setCursor(0, 0);
if (systemSettings.detailedSoldering) {
OLED::setFont(1);
OLED::print(SleepingAdvancedString);
OLED::setCursor(0, 8);
OLED::print(SleepingTipAdvancedString);
OLED::printNumber(tipTemp, 3);
if (systemSettings.temperatureInF)
OLED::print(SymbolDegF);
else
OLED::print(SymbolDegC);
OLED::print(SymbolSpace);
printVoltage();
OLED::print(SymbolVolts);
} else {
OLED::setFont(0);
OLED::print(SleepingSimpleString);
OLED::printNumber(tipTemp, 3);
if (systemSettings.temperatureInF)
OLED::drawSymbol(0);
else
OLED::drawSymbol(1);
}
if (systemSettings.ShutdownTime) // only allow shutdown exit if time > 0
if (lastMovementTime)
if (((uint32_t) (xTaskGetTickCount() - lastMovementTime))
> (uint32_t) (systemSettings.ShutdownTime * 60 * 100)) {
// shutdown
currentlyActiveTemperatureTarget = 0;
return 1; // we want to exit soldering mode
}
OLED::refresh();
GUIDelay();
}
return 0;
}
static void display_countdown(int sleepThres) {
/*
* Print seconds or minutes (if > 99 seconds) until sleep
* mode is triggered.
*/
int lastEventTime =
lastButtonTime < lastMovementTime ?
lastMovementTime : lastButtonTime;
int downCount = sleepThres - xTaskGetTickCount() + lastEventTime;
if (downCount > 9900) {
OLED::printNumber(downCount / 6000 + 1, 2);
OLED::print(SymbolMinutes);
} else {
OLED::printNumber(downCount / 100 + 1, 2);
OLED::print(SymbolSeconds);
}
}
static void gui_solderingMode(uint8_t jumpToSleep) {
/*
* * Soldering (gui_solderingMode)
* -> Main loop where we draw temp, and animations
* --> User presses buttons and they goto the temperature adjust screen
* ---> Display the current setpoint temperature
* ---> Use buttons to change forward and back on temperature
* ---> Both buttons or timeout for exiting
* --> Long hold front button to enter boost mode
* ---> Just temporarily sets the system into the alternate temperature for
* PID control
* --> Long hold back button to exit
* --> Double button to exit
*/
bool boostModeOn = false;
uint8_t badTipCounter = 0;
uint32_t sleepThres = 0;
if (systemSettings.SleepTime < 6)
sleepThres = systemSettings.SleepTime * 10 * 100;
else
sleepThres = (systemSettings.SleepTime - 5) * 60 * 100;
if (jumpToSleep) {
if (gui_SolderingSleepingMode()) {
lastButtonTime = xTaskGetTickCount();
return; // If the function returns non-0 then exit
}
}
for (;;) {
ButtonState buttons = getButtonState();
switch (buttons) {
case BUTTON_NONE:
// stay
boostModeOn = false;
break;
case BUTTON_BOTH:
// exit
return;
break;
case BUTTON_B_LONG:
return; // exit on back long hold
break;
case BUTTON_F_LONG:
// if boost mode is enabled turn it on
if (systemSettings.boostModeEnabled)
boostModeOn = true;
break;
case BUTTON_F_SHORT:
case BUTTON_B_SHORT: {
uint16_t oldTemp = systemSettings.SolderingTemp;
gui_solderingTempAdjust(); // goto adjust temp mode
if (oldTemp != systemSettings.SolderingTemp) {
saveSettings(); // only save on change
}
}
break;
default:
break;
}
// else we update the screen information
OLED::setCursor(0, 0);
OLED::clearScreen();
OLED::setFont(0);
uint16_t tipTemp = getTipRawTemp(0);
if (tipTemp > 32700) {
badTipCounter++; // Use a counter so that error has to persist for > 1 second continious so that peak errors dont trip it
} else {
badTipCounter = 0;
}
//Draw in the screen details
if (systemSettings.detailedSoldering) {
OLED::setFont(1);
OLED::print(SolderingAdvancedPowerPrompt); // Power:
OLED::printNumber(milliWattHistory[0] / 1000, 2);
OLED::print(SymbolDot);
OLED::printNumber(milliWattHistory[0] / 100 % 10, 1);
OLED::print(SymbolWatts);
if (systemSettings.sensitivity && systemSettings.SleepTime) {
OLED::print(SymbolSpace);
display_countdown(sleepThres);
}
OLED::setCursor(0, 8);
OLED::print(SleepingTipAdvancedString);
gui_drawTipTemp(true);
OLED::print(SymbolSpace);
printVoltage();
OLED::print(SymbolVolts);
} else {
// We switch the layout direction depending on the orientation of the
// OLED::
if (OLED::getRotation()) {
// battery
gui_drawBatteryIcon();
OLED::print(SymbolSpace); // Space out gap between battery <-> temp
gui_drawTipTemp(true); // Draw current tip temp
// We draw boost arrow if boosting, or else gap temp <-> heat
// indicator
if (boostModeOn)
OLED::drawSymbol(2);
else
OLED::print(SymbolSpace);
// Draw heating/cooling symbols
OLED::drawHeatSymbol(
milliWattsToPWM(milliWattHistory[0],
systemSettings.voltageDiv));
} else {
// Draw heating/cooling symbols
OLED::drawHeatSymbol(
milliWattsToPWM(milliWattHistory[0],
systemSettings.voltageDiv));
// We draw boost arrow if boosting, or else gap temp <-> heat
// indicator
if (boostModeOn)
OLED::drawSymbol(2);
else
OLED::print(SymbolSpace);
gui_drawTipTemp(true); // Draw current tip temp
OLED::print(SymbolSpace); // Space out gap between battery <-> temp
gui_drawBatteryIcon();
}
}
if (badTipCounter > 128) {
OLED::print(BadTipString);
OLED::refresh();
currentlyActiveTemperatureTarget = 0;
waitForButtonPress();
currentlyActiveTemperatureTarget = 0;
return;
}
OLED::refresh();
// Update the setpoints for the temperature
if (boostModeOn) {
if (systemSettings.temperatureInF)
currentlyActiveTemperatureTarget = ftoTipMeasurement(
systemSettings.BoostTemp);
else
currentlyActiveTemperatureTarget = ctoTipMeasurement(
systemSettings.BoostTemp);
} else {
if (systemSettings.temperatureInF)
currentlyActiveTemperatureTarget = ftoTipMeasurement(
systemSettings.SolderingTemp);
else
currentlyActiveTemperatureTarget = ctoTipMeasurement(
systemSettings.SolderingTemp);
}
#ifdef MODEL_TS100
// Undervoltage test
if (checkVoltageForExit()) {
lastButtonTime = xTaskGetTickCount();
return;
}
#else
// on the TS80 we only want to check for over voltage to prevent tip damage
/*if (getInputVoltageX10(systemSettings.voltageDiv, 1) > 150) {
lastButtonTime = xTaskGetTickCount();
currentlyActiveTemperatureTarget = 0;
return; // Over voltage
}*/
#endif
if (systemSettings.sensitivity && systemSettings.SleepTime)
if (xTaskGetTickCount() - lastMovementTime > sleepThres
&& xTaskGetTickCount() - lastButtonTime > sleepThres) {
if (gui_SolderingSleepingMode()) {
return; // If the function returns non-0 then exit
}
}
//slow down ui update rate
GUIDelay();
}
}
void showDebugMenu(void) {
uint8_t screen = 0;
ButtonState b;
for (;;) {
OLED::clearScreen(); // Ensure the buffer starts clean
OLED::setCursor(0, 0); // Position the cursor at the 0,0 (top left)
OLED::setFont(1); // small font
OLED::print(SymbolVersionNumber); // Print version number
OLED::setCursor(0, 8); // second line
OLED::print(DebugMenu[screen]);
switch (screen) {
case 0: //Just prints date
break;
case 1:
//High water mark for GUI
OLED::printNumber(uxTaskGetStackHighWaterMark(GUITaskHandle), 5);
break;
case 2:
//High water mark for the Movement task
OLED::printNumber(uxTaskGetStackHighWaterMark(MOVTaskHandle), 5);
break;
case 3:
//High water mark for the PID task
OLED::printNumber(uxTaskGetStackHighWaterMark(PIDTaskHandle), 5);
break;
case 4:
//system up time stamp
OLED::printNumber(xTaskGetTickCount() / 100, 5);
break;
case 5:
//Movement time stamp
OLED::printNumber(lastMovementTime / 100, 5);
break;
case 6:
//Raw Tip
OLED::printNumber(getTipRawTemp(0), 6);
break;
case 7:
//Temp in C
OLED::printNumber(tipMeasurementToC(getTipRawTemp(0)), 5);
break;
case 8:
//Handle Temp
OLED::printNumber(getHandleTemperature(), 3);
break;
case 9:
//Voltage input
printVoltage();
break;
case 10:
// Print PCB ID number
OLED::printNumber(PCBVersion, 1);
break;
default:
break;
}
OLED::refresh();
b = getButtonState();
if (b == BUTTON_B_SHORT)
return;
else if (b == BUTTON_F_SHORT) {
screen++;
screen = screen % 11;
}
GUIDelay();
}
}
/* StartGUITask function */
void startGUITask(void const *argument __unused) {
FRToSI2C::FRToSInit();
uint8_t tempWarningState = 0;
bool buttonLockout = false;
bool tempOnDisplay = false;
getTipRawTemp(1); // reset filter
OLED::setRotation(systemSettings.OrientationMode & 1);
uint32_t ticks = xTaskGetTickCount();
ticks += 400; // 4 seconds from now
while (xTaskGetTickCount() < ticks) {
if (showBootLogoIfavailable() == false)
ticks = xTaskGetTickCount();
ButtonState buttons = getButtonState();
if (buttons)
ticks = xTaskGetTickCount(); // make timeout now so we will exit
GUIDelay();
}
if (systemSettings.autoStartMode) {
// jump directly to the autostart mode
if (systemSettings.autoStartMode == 1)
gui_solderingMode(0);
if (systemSettings.autoStartMode == 2)
gui_solderingMode(1);
}
#if ACCELDEBUG
for (;;) {
HAL_IWDG_Refresh(&hiwdg);
osDelay(100);
}
//^ Kept here for a way to block this thread
#endif
for (;;) {
ButtonState buttons = getButtonState();
if (buttons != BUTTON_NONE) {
OLED::setDisplayState(OLED::DisplayState::ON);
OLED::setFont(0);
}
if (tempWarningState == 2)
buttons = BUTTON_F_SHORT;
if (buttons != BUTTON_NONE && buttonLockout)
buttons = BUTTON_NONE;
else
buttonLockout = false;
switch (buttons) {
case BUTTON_NONE:
// Do nothing
break;
case BUTTON_BOTH:
// Not used yet
// In multi-language this might be used to reset language on a long hold
// or some such
break;
case BUTTON_B_LONG:
// Show the version information
showDebugMenu();
break;
case BUTTON_F_LONG:
gui_solderingTempAdjust();
saveSettings();
break;
case BUTTON_F_SHORT:
gui_solderingMode(0); // enter soldering mode
buttonLockout = true;
break;
case BUTTON_B_SHORT:
enterSettingsMenu(); // enter the settings menu
saveSettings();
buttonLockout = true;
setCalibrationOffset(systemSettings.CalibrationOffset); // ensure cal offset is applied
break;
default:
break;
}
currentlyActiveTemperatureTarget = 0; // ensure tip is off
getInputVoltageX10(systemSettings.voltageDiv, 0);
uint16_t tipTemp = tipMeasurementToC(getTipRawTemp(0));
// Preemptively turn the display on. Turn it off if and only if
// the tip temperature is below 50 degrees C *and* motion sleep
// detection is enabled *and* there has been no activity (movement or
// button presses) in a while.
OLED::setDisplayState(OLED::DisplayState::ON);
if ((tipTemp < 50) && systemSettings.sensitivity &&
(((xTaskGetTickCount() - lastMovementTime) > MOVEMENT_INACTIVITY_TIME) &&
((xTaskGetTickCount() - lastButtonTime) > BUTTON_INACTIVITY_TIME))) {
OLED::setDisplayState(OLED::DisplayState::OFF);
}
// Clear the lcd buffer
OLED::clearScreen();
OLED::setCursor(0, 0);
if (systemSettings.detailedIDLE) {
OLED::setFont(1);
if (tipTemp > 470) {
OLED::print(TipDisconnectedString);
} else {
OLED::print(IdleTipString);
gui_drawTipTemp(false);
OLED::print(IdleSetString);
OLED::printNumber(systemSettings.SolderingTemp, 3);
}
OLED::setCursor(0, 8);
OLED::print(InputVoltageString);
printVoltage();
} else {
OLED::setFont(0);
#ifdef MODEL_TS80
if (!OLED::getRotation()) {
#else
if (OLED::getRotation()) {
#endif
OLED::drawArea(12, 0, 84, 16, idleScreenBG);
OLED::setCursor(0, 0);
gui_drawBatteryIcon();
} else {
OLED::drawArea(0, 0, 84, 16, idleScreenBGF); // Needs to be flipped so button ends up
// on right side of screen
OLED::setCursor(84, 0);
gui_drawBatteryIcon();
}
if (tipTemp > 55)
tempOnDisplay = true;
else if (tipTemp < 45)
tempOnDisplay = false;
if (tempOnDisplay) {
// draw temp over the start soldering button
// Location changes on screen rotation
#ifdef MODEL_TS80
if (!OLED::getRotation()) {
#else
if (OLED::getRotation()) {
#endif
// in right handed mode we want to draw over the first part
OLED::fillArea(55, 0, 41, 16, 0); // clear the area for the temp
OLED::setCursor(56, 0);
} else {
OLED::fillArea(0, 0, 41, 16, 0); // clear the area
OLED::setCursor(0, 0);
}
// draw in the temp
if (!(systemSettings.coolingTempBlink
&& (xTaskGetTickCount() % 25 < 16)))
gui_drawTipTemp(false); // draw in the temp
}
}
OLED::refresh();
GUIDelay();
}
}

View File

@@ -0,0 +1,50 @@
/*
* LIS2DH12.cpp
*
* Created on: 27Feb.,2018
* Author: Ralim
*/
#include <array>
#include "LIS2DH12.hpp"
#include "cmsis_os.h"
typedef struct {
const uint8_t reg;
const uint8_t value;
} LIS_REG;
static const LIS_REG i2c_registers[] = {
{LIS_CTRL_REG1, 0x17}, // 25Hz
{LIS_CTRL_REG2, 0b00001000}, // Highpass filter off
{LIS_CTRL_REG3, 0b01100000}, // Setup interrupt pins
{LIS_CTRL_REG4, 0b00001000}, // Block update mode off, HR on
{LIS_CTRL_REG5, 0b00000010},
{LIS_CTRL_REG6, 0b01100010},
//Basically setup the unit to run, and enable 4D orientation detection
{LIS_INT2_CFG, 0b01111110}, //setup for movement detection
{LIS_INT2_THS, 0x28},
{LIS_INT2_DURATION, 64},
{LIS_INT1_CFG, 0b01111110},
{LIS_INT1_THS, 0x28},
{LIS_INT1_DURATION, 64}
};
void LIS2DH12::initalize() {
for (size_t index = 0; index < (sizeof(i2c_registers) / sizeof(i2c_registers[0])); index++) {
FRToSI2C::I2C_RegisterWrite(LIS2DH_I2C_ADDRESS,i2c_registers[index].reg, i2c_registers[index].value);
}
}
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, I2C_MEMADD_SIZE_8BIT,
reinterpret_cast<uint8_t*>(sensorData.begin()),
sensorData.size() * sizeof(int16_t));
x = sensorData[0];
y = sensorData[1];
z = sensorData[2];
}

View File

@@ -0,0 +1,78 @@
/*
* MMA8652FC.cpp
*
* Created on: 31Aug.,2017
* Author: Ben V. Brown
*/
#include <array>
#include "MMA8652FC.hpp"
#include "cmsis_os.h"
typedef struct {
const uint8_t reg;
const uint8_t val;
} MMA_REG;
static const MMA_REG i2c_registers[] = { { CTRL_REG2, 0 }, //Normal mode
{ CTRL_REG2, 0x40 }, // Reset all registers to POR values
{ FF_MT_CFG_REG, 0x78 }, // Enable motion detection for X, Y, Z axis, latch disabled
{ PL_CFG_REG, 0x40 }, //Enable the orientation detection
{ PL_COUNT_REG, 200 }, //200 count debounce
{ PL_BF_ZCOMP_REG, 0b01000111 }, //Set the threshold to 42 degrees
{ P_L_THS_REG, 0b10011100 }, //Up the trip angles
{ CTRL_REG4, 0x01 | (1 << 4) }, // Enable dataready interrupt & orientation interrupt
{ CTRL_REG5, 0x01 }, // Route data ready interrupts to INT1 ->PB5 ->EXTI5, leaving orientation routed to INT2
{ CTRL_REG2, 0x12 }, //Set maximum resolution oversampling
{ XYZ_DATA_CFG_REG, (1 << 4) }, //select high pass filtered data
{ HP_FILTER_CUTOFF_REG, 0x03 }, //select high pass filtered data
{ CTRL_REG1, 0x19 } // ODR=12 Hz, Active mode
};
void MMA8652FC::initalize() {
size_t index = 0;
//send all the init commands to the unit
FRToSI2C::I2C_RegisterWrite(MMA8652FC_I2C_ADDRESS,i2c_registers[index].reg, i2c_registers[index].val);
index++;
FRToSI2C::I2C_RegisterWrite(MMA8652FC_I2C_ADDRESS,i2c_registers[index].reg, i2c_registers[index].val);
index++;
HAL_Delay(2); // ~1ms delay
while (index < (sizeof(i2c_registers) / sizeof(i2c_registers[0]))) {
FRToSI2C::I2C_RegisterWrite(MMA8652FC_I2C_ADDRESS,i2c_registers[index].reg, i2c_registers[index].val);
index++;
}
}
Orientation MMA8652FC::getOrientation() {
//First read the PL_STATUS register
uint8_t plStatus = FRToSI2C::I2C_RegisterRead(MMA8652FC_I2C_ADDRESS,PL_STATUS_REG);
if ((plStatus & 0b10000000) == 0b10000000) {
plStatus >>= 1; //We don't need the up/down bit
plStatus &= 0x03; //mask to the two lower bits
//0 == left handed
//1 == right handed
return static_cast<Orientation>(plStatus);
}
return ORIENTATION_FLAT;
}
void MMA8652FC::getAxisReadings(int16_t& x, int16_t& y, int16_t& z) {
std::array<int16_t, 3> sensorData;
FRToSI2C::Mem_Read(MMA8652FC_I2C_ADDRESS, OUT_X_MSB_REG, I2C_MEMADD_SIZE_8BIT,
reinterpret_cast<uint8_t*>(sensorData.begin()),
sensorData.size() * sizeof(int16_t));
x = static_cast<int16_t>(__builtin_bswap16(*reinterpret_cast<uint16_t*>(&sensorData[0])));
y = static_cast<int16_t>(__builtin_bswap16(*reinterpret_cast<uint16_t*>(&sensorData[1])));
z = static_cast<int16_t>(__builtin_bswap16(*reinterpret_cast<uint16_t*>(&sensorData[2])));
}

View File

@@ -0,0 +1,324 @@
/*
* OLED.cpp
*
* Created on: 29Aug.,2017
* Author: Ben V. Brown
*/
#include <string.h>
#include <OLED.hpp>
#include <stdlib.h>
#include "Translation.h"
#include "cmsis_os.h"
const uint8_t* OLED::currentFont; // Pointer to the current font used for
// rendering to the buffer
uint8_t* OLED::firstStripPtr; // Pointers to the strips to allow for buffer
// having extra content
uint8_t* OLED::secondStripPtr; // Pointers to the strips
bool OLED::inLeftHandedMode; // Whether the screen is in left or not (used for
// offsets in GRAM)
OLED::DisplayState OLED::displayState;
uint8_t OLED::fontWidth, OLED::fontHeight;
int16_t OLED::cursor_x, OLED::cursor_y;
uint8_t OLED::displayOffset;
uint8_t OLED::screenBuffer[16 + (OLED_WIDTH * 2) + 10]; // The data buffer
/*Setup params for the OLED screen*/
/*http://www.displayfuture.com/Display/datasheet/controller/SSD1307.pdf*/
/*All commands are prefixed with 0x80*/
/*Data packets are prefixed with 0x40*/
uint8_t OLED_Setup_Array[] = {
/**/
0x80, 0xAE, /*Display off*/
0x80, 0xD5, /*Set display clock divide ratio / osc freq*/
0x80, 0x52, /*Divide ratios*/
0x80, 0xA8, /*Set Multiplex Ratio*/
0x80, 0x0F, /*16 == max brightness,39==dimmest*/
0x80, 0xC0, /*Set COM Scan direction*/
0x80, 0xD3, /*Set vertical Display offset*/
0x80, 0x00, /*0 Offset*/
0x80, 0x40, /*Set Display start line to 0*/
0x80, 0xA0, /*Set Segment remap to normal*/
0x80, 0x8D, /*Charge Pump*/
0x80, 0x14, /*Charge Pump settings*/
0x80, 0xDA, /*Set VCOM Pins hardware config*/
0x80, 0x02, /*Combination 2*/
0x80, 0x81, /*Contrast*/
0x80, 0x33, /*^51*/
0x80, 0xD9, /*Set pre-charge period*/
0x80, 0xF1, /*Pre charge period*/
0x80, 0xDB, /*Adjust VCOMH regulator ouput*/
0x80, 0x30, /*VCOM level*/
0x80, 0xA4, /*Enable the display GDDR*/
0x80, 0XA6, /*Normal display*/
0x80, 0x20, /*Memory Mode*/
0x80, 0x00, /*Wrap memory*/
0x80, 0xAF /*Display on*/
};
// Setup based on the SSD1307 and modified for the SSD1306
const uint8_t REFRESH_COMMANDS[17] = { 0x80, 0xAF, 0x80, 0x21, 0x80, 0x20, 0x80,
0x7F, 0x80, 0xC0, 0x80, 0x22, 0x80, 0x00, 0x80, 0x01, 0x40 };
void OLED::initialize() {
cursor_x = cursor_y = 0;
currentFont = USER_FONT_12;
fontWidth = 12;
inLeftHandedMode = false;
firstStripPtr = &screenBuffer[FRAMEBUFFER_START];
secondStripPtr = &screenBuffer[FRAMEBUFFER_START + OLED_WIDTH];
fontHeight = 16;
displayOffset = 0;
memcpy(&screenBuffer[0], &REFRESH_COMMANDS[0], sizeof(REFRESH_COMMANDS));
HAL_Delay(50);
HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_SET);
HAL_Delay(50);
// Set the display to be ON once the settings block is sent and send the
// initialisation data to the OLED.
setDisplayState(DisplayState::ON);
FRToSI2C::Transmit(DEVICEADDR_OLED, &OLED_Setup_Array[0],
sizeof(OLED_Setup_Array));
}
/*
* Prints a char to the screen.
* UTF font handling is done using the two input chars.
* Precursor is the command char that is used to select the table.
*/
void OLED::drawChar(char c) {
if (c == '\x01' && cursor_y == 0) { // 0x01 is used as new line char
cursor_x = 0;
cursor_y = 8;
return;
} else if (c == 0) {
return;
}
uint16_t index = c - 2; //First index is \x02
uint8_t* charPointer;
charPointer = ((uint8_t*) currentFont)
+ ((fontWidth * (fontHeight / 8)) * index);
drawArea(cursor_x, cursor_y, fontWidth, fontHeight, charPointer);
cursor_x += fontWidth;
}
void OLED::setRotation(bool leftHanded) {
#ifdef MODEL_TS80
leftHanded = !leftHanded;
#endif
if (inLeftHandedMode == leftHanded) {
return;
}
// send command struct again with changes
if (leftHanded) {
OLED_Setup_Array[11] = 0xC8; // c1?
OLED_Setup_Array[19] = 0xA1;
} else {
OLED_Setup_Array[11] = 0xC0;
OLED_Setup_Array[19] = 0xA0;
}
FRToSI2C::Transmit(DEVICEADDR_OLED, (uint8_t*) OLED_Setup_Array,
sizeof(OLED_Setup_Array));
inLeftHandedMode = leftHanded;
screenBuffer[5] = inLeftHandedMode ? 0 : 32; // display is shifted by 32 in left handed
// mode as driver ram is 128 wide
screenBuffer[7] = inLeftHandedMode ? 95 : 0x7F; // End address of the ram segment we are writing to (96 wide)
screenBuffer[9] = inLeftHandedMode ? 0xC8 : 0xC0;
}
// print a string to the current cursor location
void OLED::print(const char* str) {
while (str[0]) {
drawChar(str[0]);
str++;
}
}
void OLED::setFont(uint8_t fontNumber) {
if (fontNumber == 1) {
// small font
currentFont = USER_FONT_6x8;
fontHeight = 8;
fontWidth = 6;
} else if (fontNumber == 2) {
currentFont = ExtraFontChars;
fontHeight = 16;
fontWidth = 12;
} else {
currentFont = USER_FONT_12;
fontHeight = 16;
fontWidth = 12;
}
}
// maximum places is 5
void OLED::printNumber(uint16_t number, uint8_t places) {
char buffer[7] = { 0 };
if (places >= 5) {
buffer[5] = 2 + number % 10;
number /= 10;
}
if (places > 4) {
buffer[4] = 2 + number % 10;
number /= 10;
}
if (places > 3) {
buffer[3] = 2 + number % 10;
number /= 10;
}
if (places > 2) {
buffer[2] = 2 + number % 10;
number /= 10;
}
if (places > 1) {
buffer[1] = 2 + number % 10;
number /= 10;
}
buffer[0] = 2 + number % 10;
number /= 10;
print(buffer);
}
void OLED::debugNumber(int32_t val) {
if (abs(val) > 99999) {
OLED::print(SymbolSpace); // out of bounds
return;
}
if (val >= 0) {
OLED::print(SymbolSpace);
OLED::printNumber(val, 5);
} else {
OLED::print(SymbolMinus);
OLED::printNumber(-val, 5);
}
}
void OLED::drawSymbol(uint8_t symbolID) {
// draw a symbol to the current cursor location
setFont(2);
drawChar(symbolID + 2);
setFont(0);
}
// Draw an area, but y must be aligned on 0/8 offset
void OLED::drawArea(int16_t x, int8_t y, uint8_t wide, uint8_t height,
const uint8_t* ptr) {
// Splat this from x->x+wide in two strides
if (x <= -wide)
return; // cutoffleft
if (x > 96)
return; // cutoff right
uint8_t visibleStart = 0;
uint8_t visibleEnd = wide;
// trimming to draw partials
if (x < 0) {
visibleStart -= x; // subtract negative value == add absolute value
}
if (x + wide > 96) {
visibleEnd = 96 - x;
}
if (y == 0) {
// Splat first line of data
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
firstStripPtr[xx + x] = ptr[xx];
}
}
if (y == 8 || height == 16) {
// Splat the second line
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
secondStripPtr[x + xx] = ptr[xx + (height == 16 ? wide : 0)];
}
}
}
void OLED::fillArea(int16_t x, int8_t y, uint8_t wide, uint8_t height,
const uint8_t value) {
// Splat this from x->x+wide in two strides
if (x <= -wide)
return; // cutoffleft
if (x > 96)
return; // cutoff right
uint8_t visibleStart = 0;
uint8_t visibleEnd = wide;
// trimming to draw partials
if (x < 0) {
visibleStart -= x; // subtract negative value == add absolute value
}
if (x + wide > 96) {
visibleEnd = 96 - x;
}
if (y == 0) {
// Splat first line of data
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
firstStripPtr[xx + x] = value;
}
}
if (y == 8 || height == 16) {
// Splat the second line
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
secondStripPtr[x + xx] = value;
}
}
}
void OLED::drawFilledRect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1,
bool clear) {
// Draw this in 3 sections
// This is basically a N wide version of vertical line
// Step 1 : Draw in the top few pixels that are not /8 aligned
// LSB is at the top of the screen
uint8_t mask = 0xFF;
if (y0) {
mask = mask << (y0 % 8);
for (uint8_t col = x0; col < x1; col++)
if (clear)
firstStripPtr[(y0 / 8) * 96 + col] &= ~mask;
else
firstStripPtr[(y0 / 8) * 96 + col] |= mask;
}
// Next loop down the line the total number of solids
if (y0 / 8 != y1 / 8)
for (uint8_t col = x0; col < x1; col++)
for (uint8_t r = (y0 / 8); r < (y1 / 8); r++) {
// This gives us the row index r
if (clear)
firstStripPtr[(r * 96) + col] = 0;
else
firstStripPtr[(r * 96) + col] = 0xFF;
}
// Finally draw the tail
mask = ~(mask << (y1 % 8));
for (uint8_t col = x0; col < x1; col++)
if (clear)
firstStripPtr[(y1 / 8) * 96 + col] &= ~mask;
else
firstStripPtr[(y1 / 8) * 96 + col] |= mask;
}
void OLED::drawHeatSymbol(uint8_t state) {
// Draw symbol 14
// Then draw over it, the bottom 5 pixels always stay. 8 pixels above that are
// the levels masks the symbol nicely
state /= 31; // 0-> 8 range
// Then we want to draw down (16-(5+state)
uint8_t cursor_x_temp = cursor_x;
drawSymbol(14);
drawFilledRect(cursor_x_temp, 0, cursor_x_temp + 12, 2 + (8 - state), true);
}

View File

@@ -0,0 +1,121 @@
/*
* Settings.c
*
* Created on: 29 Sep 2016
* Author: Ralim
*
* This file holds the users settings and saves / restores them to the
* devices flash
*/
#include "Settings.h"
#include "Setup.h"
#define FLASH_ADDR \
(0x8000000 | \
0xFC00) /*Flash start OR'ed with the maximum amount of flash - 1024 bytes*/
#include "string.h"
volatile systemSettingsType systemSettings;
void saveSettings() {
// First we erase the flash
FLASH_EraseInitTypeDef pEraseInit;
pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
pEraseInit.Banks = FLASH_BANK_1;
pEraseInit.NbPages = 1;
pEraseInit.PageAddress = FLASH_ADDR;
uint32_t failingAddress = 0;
HAL_IWDG_Refresh(&hiwdg);
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGERR |
FLASH_FLAG_BSY);
HAL_FLASH_Unlock();
HAL_Delay(10);
HAL_IWDG_Refresh(&hiwdg);
HAL_FLASHEx_Erase(&pEraseInit, &failingAddress);
//^ Erase the page of flash (1024 bytes on this stm32)
// erased the chunk
// now we program it
uint16_t *data = (uint16_t *)&systemSettings;
HAL_FLASH_Unlock();
for (uint8_t i = 0; i < (sizeof(systemSettingsType) / 2); i++) {
HAL_IWDG_Refresh(&hiwdg);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, FLASH_ADDR + (i * 2),
data[i]);
}
HAL_FLASH_Lock();
}
void restoreSettings() {
// We read the flash
uint16_t *data = (uint16_t *)&systemSettings;
for (uint8_t i = 0; i < (sizeof(systemSettingsType) / 2); i++) {
data[i] = *((uint16_t *)(FLASH_ADDR + (i * 2)));
}
// if the version is correct were done
// if not we reset and save
if (systemSettings.version != SETTINGSVERSION) {
// probably not setup
resetSettings();
}
}
// Lookup function for cutoff setting -> X10 voltage
/*
* 0=DC
* 1=3S
* 2=4S
* 3=5S
* 4=6S
*/
uint8_t lookupVoltageLevel(uint8_t level) {
if (level == 0)
return 90; // 9V since iron does not function effectively below this
else
return (level * 33) + (33 * 2);
}
void resetSettings() {
memset((void *)&systemSettings, 0, sizeof(systemSettingsType));
systemSettings.SleepTemp =
150; // Temperature the iron sleeps at - default 150.0 C
systemSettings.SleepTime = 6; // How many seconds/minutes we wait until going
// to sleep - default 1 min
systemSettings.SolderingTemp = 320; // Default soldering temp is 320.0 C
systemSettings.cutoutSetting = 0; // default to no cut-off voltage (or 18W for TS80)
systemSettings.version =
SETTINGSVERSION; // Store the version number to allow for easier upgrades
systemSettings.detailedSoldering = 0; // Detailed soldering screen
systemSettings.detailedIDLE =
0; // Detailed idle screen (off for first time users)
systemSettings.OrientationMode = 2; // Default to automatic
systemSettings.sensitivity = 7; // Default high sensitivity
#ifdef MODEL_TS80
systemSettings.voltageDiv = 780; // Default divider from schematic
#else
systemSettings.voltageDiv = 467; // Default divider from schematic
#endif
systemSettings.ShutdownTime =
10; // How many minutes until the unit turns itself off
systemSettings.boostModeEnabled =
1; // Default to having boost mode on as most people prefer itF
systemSettings.BoostTemp = 420; // default to 400C
systemSettings.autoStartMode = 0; // Auto start off for safety
systemSettings.coolingTempBlink =
0; // Blink the temperature on the cooling screen when its > 50C
systemSettings.temperatureInF = 0; // default to 0
systemSettings.descriptionScrollSpeed = 0; // default to slow
systemSettings.PID_P = 42; // PID tuning constants
systemSettings.PID_I = 50;
systemSettings.PID_D = 15;
systemSettings.CalibrationOffset = 1400; // the adc offset
systemSettings.customTipGain =
0; // The tip type is either default or a custom gain
#ifdef MODEL_TS100
systemSettings.tipType = TS_B2; // Default to the B2 Tip
#endif
#ifdef MODEL_TS80
systemSettings.pidPowerLimit=24; // Sets the max pwm power limit
systemSettings.tipType = TS_B02; // Default to the B2 Tip
#endif
saveSettings(); // Save defaults
}

View File

@@ -0,0 +1,464 @@
/*
* Setup.c
*
* Created on: 29Aug.,2017
* Author: Ben V. Brown
*/
#include "Setup.h"
ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc2;
DMA_HandleTypeDef hdma_adc1;
I2C_HandleTypeDef hi2c1;
DMA_HandleTypeDef hdma_i2c1_rx;
DMA_HandleTypeDef hdma_i2c1_tx;
IWDG_HandleTypeDef hiwdg;
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;
uint16_t ADCReadings[64]; // room for 32 lots of the pair of readings
// Functions
static void SystemClock_Config(void);
static void MX_ADC1_Init(void);
static void MX_I2C1_Init(void);
static void MX_IWDG_Init(void);
static void MX_TIM3_Init(void);
static void MX_TIM2_Init(void);
static void MX_DMA_Init(void);
static void MX_GPIO_Init(void);
static void MX_ADC2_Init(void);
void Setup_HAL() {
SystemClock_Config();
#ifndef LOCAL_BUILD
__HAL_AFIO_REMAP_SWJ_DISABLE()
;
#else
__HAL_AFIO_REMAP_SWJ_NOJTAG();
#endif
MX_GPIO_Init();
MX_DMA_Init();
MX_I2C1_Init();
MX_ADC1_Init();
MX_ADC2_Init();
MX_TIM3_Init();
MX_TIM2_Init();
MX_IWDG_Init();
HAL_ADC_Start(&hadc2);
HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t*) ADCReadings, 64); // start DMA of normal readings
HAL_ADCEx_InjectedStart(&hadc1); // enable injected readings
HAL_ADCEx_InjectedStart(&hadc2); // enable injected readings
}
// channel 0 -> temperature sensor, 1-> VIN
uint16_t getADC(uint8_t channel) {
uint32_t sum = 0;
for (uint8_t i = 0; i < 32; i++)
sum += ADCReadings[channel + (i * 2)];
return sum >> 2;
}
/** System Clock Configuration
*/
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit;
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType =
RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16; // 64MHz
HAL_RCC_OscConfig(&RCC_OscInitStruct);
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK |
RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV16; // TIM
// 2,3,4,5,6,7,12,13,14
RCC_ClkInitStruct.APB2CLKDivider =
RCC_HCLK_DIV1; // 64 mhz to some peripherals and adc
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInit.AdcClockSelection =
RCC_ADCPCLK2_DIV6; // 6 or 8 are the only non overclocked options
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000);
/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);
}
/* ADC1 init function */
static void MX_ADC1_Init(void) {
ADC_MultiModeTypeDef multimode;
ADC_ChannelConfTypeDef sConfig;
ADC_InjectionConfTypeDef sConfigInjected;
/**Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 2;
HAL_ADC_Init(&hadc1);
/**Configure the ADC multi-mode
*/
multimode.Mode = ADC_DUALMODE_REGSIMULT_INJECSIMULT;
HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode);
/**Configure Regular Channel
*/
sConfig.Channel = TMP36_ADC1_CHANNEL;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
/**Configure Regular Channel
*/
sConfig.Channel = VIN_ADC1_CHANNEL;
sConfig.Rank = 2;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
/**Configure Injected Channel
*/
// F in = 10.66 MHz
/*
* Injected time is 1 delay clock + (12 adc cycles*4)+4*sampletime =~217
* clocks = 0.2ms Charge time is 0.016 uS ideally So Sampling time must be >=
* 0.016uS 1/10.66MHz is 0.09uS, so 1 CLK is *should* be enough
* */
sConfigInjected.InjectedChannel = TIP_TEMP_ADC1_CHANNEL;
sConfigInjected.InjectedRank = 1;
sConfigInjected.InjectedNbrOfConversion = 4;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_7CYCLES_5;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T2_CC1;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.InjectedOffset = 0;
HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected);
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_1CYCLE_5;
sConfigInjected.InjectedRank = 2;
HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected);
sConfigInjected.InjectedRank = 3;
HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected);
sConfigInjected.InjectedRank = 4;
HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected);
SET_BIT(hadc1.Instance->CR1, (ADC_CR1_JEOCIE)); // Enable end of injected conv irq
// Run ADC internal calibration
while (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK)
;
}
/* ADC2 init function */
static void MX_ADC2_Init(void) {
ADC_ChannelConfTypeDef sConfig;
ADC_InjectionConfTypeDef sConfigInjected;
/**Common config
*/
hadc2.Instance = ADC2;
hadc2.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc2.Init.ContinuousConvMode = ENABLE;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.NbrOfConversion = 2;
HAL_ADC_Init(&hadc2);
/**Configure Regular Channel
*/
sConfig.Channel = TIP_TEMP_ADC2_CHANNEL;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
HAL_ADC_ConfigChannel(&hadc2, &sConfig);
sConfig.Channel = VIN_ADC2_CHANNEL;
sConfig.Rank = ADC_REGULAR_RANK_2;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
HAL_ADC_ConfigChannel(&hadc2, &sConfig);
/**Configure Injected Channel
*/
sConfigInjected.InjectedChannel = TIP_TEMP_ADC2_CHANNEL;
sConfigInjected.InjectedRank = ADC_INJECTED_RANK_1;
sConfigInjected.InjectedNbrOfConversion = 4;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_7CYCLES_5;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T2_CC1;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.InjectedOffset = 0;
HAL_ADCEx_InjectedConfigChannel(&hadc2, &sConfigInjected);
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_1CYCLE_5;
sConfigInjected.InjectedRank = ADC_INJECTED_RANK_2;
HAL_ADCEx_InjectedConfigChannel(&hadc2, &sConfigInjected);
sConfigInjected.InjectedRank = ADC_INJECTED_RANK_3;
HAL_ADCEx_InjectedConfigChannel(&hadc2, &sConfigInjected);
sConfigInjected.InjectedRank = ADC_INJECTED_RANK_4;
HAL_ADCEx_InjectedConfigChannel(&hadc2, &sConfigInjected);
// Run ADC internal calibration
while (HAL_ADCEx_Calibration_Start(&hadc2) != HAL_OK)
;
}
/* I2C1 init function */
static void MX_I2C1_Init(void) {
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 75000;
// OLED doesnt handle >100k when its asleep (off).
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
}
/* IWDG init function */
static void MX_IWDG_Init(void) {
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_256;
hiwdg.Init.Reload = 100;
#ifndef LOCAL_BUILD
HAL_IWDG_Init(&hiwdg);
#endif
}
/* TIM3 init function */
static void MX_TIM3_Init(void) {
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
htim3.Instance = TIM3;
htim3.Init.Prescaler = 8;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 100; // 5 Khz PWM freq
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4; // 4mhz before div
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; //Preload the ARR register (though we dont use this)
HAL_TIM_Base_Init(&htim3);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig);
HAL_TIM_PWM_Init(&htim3);
HAL_TIM_OC_Init(&htim3);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 80; //80% duty cycle, that is AC coupled through the cap
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, PWM_Out_CHANNEL);
GPIO_InitTypeDef GPIO_InitStruct;
/**TIM3 GPIO Configuration
PWM_Out_Pin ------> TIM3_CH1
*/
GPIO_InitStruct.Pin = PWM_Out_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; //We would like sharp rising edges
HAL_GPIO_Init(PWM_Out_GPIO_Port, &GPIO_InitStruct);
#ifdef MODEL_TS100
// Remap TIM3_CH1 to be on PB4
__HAL_AFIO_REMAP_TIM3_PARTIAL()
;
#else
// No re-map required
#endif
HAL_TIM_PWM_Start(&htim3, PWM_Out_CHANNEL);
}
/* TIM3 init function */
static void MX_TIM2_Init(void) {
/*
* We use the channel 1 to trigger the ADC at end of PWM period
* And we use the channel 4 as the PWM modulation source using Interrupts
* */
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
// Timer 2 is fairly slow as its being used to run the PWM and trigger the ADC
// in the PWM off time.
htim2.Instance = TIM2;
htim2.Init.Prescaler = 4000; //1mhz tick rate/800 = 1.25 KHz tick rate
// pwm out is 10k from tim3, we want to run our PWM at around 10hz or slower on the output stage
// These values give a rate of around 8Hz
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 255 + 17;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4; // 4mhz before divide
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
htim2.Init.RepetitionCounter = 0;
HAL_TIM_Base_Init(&htim2);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);
HAL_TIM_PWM_Init(&htim2);
HAL_TIM_OC_Init(&htim2);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 255 + 13;//13 -> Delay of 5ms
//255 is the largest time period of the drive signal, and then offset ADC sample to be a bit delayed after this
/*
* It takes 4 milliseconds for output to be stable after PWM turns off.
* Assume ADC samples in 0.5ms
* We need to set this to 100% + 4.5ms
* */
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
sConfigOC.Pulse = 0; //default to entirely off
HAL_TIM_OC_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4);
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_4);
HAL_NVIC_SetPriority(TIM2_IRQn, 15, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void) {
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE()
;
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
/* DMA1_Channel6_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
/* DMA1_Channel7_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
}
/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
* Free pins are configured automatically as Analog
PB0 ------> ADCx_IN8
PB1 ------> ADCx_IN9
*/
static void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOD_CLK_ENABLE()
;
__HAL_RCC_GPIOA_CLK_ENABLE()
;
__HAL_RCC_GPIOB_CLK_ENABLE()
;
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_RESET);
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
/*Configure GPIO pins : PD0 PD1 */
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/*Configure peripheral I/O remapping */
__HAL_AFIO_REMAP_PD01_ENABLE()
;
//^ remap XTAL so that pins can be analog (all input buffers off).
// reduces power consumption
/*
* Configure All pins as analog by default
*/
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 |
GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 |
#ifdef MODEL_TS100
GPIO_PIN_3 |
#endif
GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 |
GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 |
GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
#ifdef MODEL_TS100
/* Pull USB lines low to disable, pull down debug too*/
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_14 | GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_14, GPIO_PIN_RESET);
#else
/* TS80 */
/* Leave USB lines open circuit*/
#endif
/*Configure GPIO pins : KEY_B_Pin KEY_A_Pin */
GPIO_InitStruct.Pin = KEY_B_Pin | KEY_A_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(KEY_B_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : OLED_RESET_Pin */
GPIO_InitStruct.Pin = OLED_RESET_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(OLED_RESET_GPIO_Port, &GPIO_InitStruct);
HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_RESET);
// Pull down LCD reset
HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_RESET);
HAL_Delay(30);
HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_SET);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
/**
******************************************************************************
* File Name : freertos.c
* Description : Code for freertos applications
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2017 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -0,0 +1,994 @@
/*
* gui.cpp
*
* Created on: 3Sep.,2017
* Author: Ben V. Brown
*/
#include "gui.hpp"
#include "Translation.h"
#include "cmsis_os.h"
#include "main.hpp"
#include "string.h"
extern uint32_t lastButtonTime;
void gui_Menu(const menuitem* menu);
#ifdef MODEL_TS100
static void settings_setInputVRange(void);
static void settings_displayInputVRange(void);
#else
static void settings_setInputPRange(void);
static void settings_displayInputPRange(void);
#endif
static void settings_setSleepTemp(void);
static void settings_displaySleepTemp(void);
static void settings_setSleepTime(void);
static void settings_displaySleepTime(void);
static void settings_setShutdownTime(void);
static void settings_displayShutdownTime(void);
static void settings_setSensitivity(void);
static void settings_displaySensitivity(void);
static void settings_setTempF(void);
static void settings_displayTempF(void);
static void settings_setAdvancedSolderingScreens(void);
static void settings_displayAdvancedSolderingScreens(void);
static void settings_setAdvancedIDLEScreens(void);
static void settings_displayAdvancedIDLEScreens(void);
static void settings_setScrollSpeed(void);
static void settings_displayScrollSpeed(void);
static void settings_setDisplayRotation(void);
static void settings_displayDisplayRotation(void);
static void settings_setBoostModeEnabled(void);
static void settings_displayBoostModeEnabled(void);
static void settings_setBoostTemp(void);
static void settings_displayBoostTemp(void);
static void settings_setAutomaticStartMode(void);
static void settings_displayAutomaticStartMode(void);
static void settings_setCoolingBlinkEnabled(void);
static void settings_displayCoolingBlinkEnabled(void);
static void settings_setResetSettings(void);
static void settings_displayResetSettings(void);
static void settings_setTipModel(void);
static void settings_displayTipModel(void);
static void settings_setCalibrate(void);
static void settings_displayCalibrate(void);
static void settings_setCalibrateVIN(void);
static void settings_displayCalibrateVIN(void);
// Calibration Menu
static void calibration_displaySimpleCal(void); // Hot water cal
static void calibration_enterSimpleCal(void);
static void calibration_displayAdvancedCal(void); // two point cal
static void calibration_enterAdvancedCal(void);
// Menu functions
static void settings_displaySolderingMenu(void);
static void settings_enterSolderingMenu(void);
static void settings_displayPowerMenu(void);
static void settings_enterPowerMenu(void);
static void settings_displayUIMenu(void);
static void settings_enterUIMenu(void);
static void settings_displayAdvancedMenu(void);
static void settings_enterAdvancedMenu(void);
/*
* Root Settings Menu
*
* Power Source
* Soldering
* Boost Mode Enabled
* Boost Mode Temp
* Auto Start
*
* Power Saving
* Sleep Temp
* Sleep Time
* Shutdown Time
* Motion Sensitivity
*
* UI
* // Language
* Scrolling Speed
* Temperature Unit
* Display orientation
* Cooldown blink
*
* Advanced
* Detailed IDLE
* Detailed Soldering
* Logo Time
* Calibrate Temperature
* Calibrate Input V
* Reset Settings
*
*/
const menuitem rootSettingsMenu[] {
/*
* Power Source
* Soldering Menu
* Power Saving Menu
* UI Menu
* Advanced Menu
* Exit
*/
#ifdef MODEL_TS100
{ (const char*)SettingsDescriptions[0],
{ settings_setInputVRange},
{ settings_displayInputVRange}}, /*Voltage input*/
#else
{ (const char*) SettingsDescriptions[20], { settings_setInputPRange }, {
settings_displayInputPRange } }, /*Voltage input*/
#endif
{ (const char*) NULL, { settings_enterSolderingMenu }, {
settings_displaySolderingMenu } }, /*Soldering*/
{ (const char*) NULL, { settings_enterPowerMenu }, {
settings_displayPowerMenu } }, /*Sleep Options Menu*/
{ (const char*) NULL, { settings_enterUIMenu },
{ settings_displayUIMenu } }, /*UI Menu*/
{ (const char*) NULL, { settings_enterAdvancedMenu }, {
settings_displayAdvancedMenu } }, /*Advanced Menu*/
{ NULL, { NULL }, { NULL } } // end of menu marker. DO NOT REMOVE
};
const menuitem solderingMenu[] = {
/*
* Boost Mode Enabled
* Boost Mode Temp
* Auto Start
*/
{ (const char*) SettingsDescriptions[8], { settings_setBoostModeEnabled }, {
settings_displayBoostModeEnabled } }, /*Enable Boost*/
{ (const char*) SettingsDescriptions[9], { settings_setBoostTemp }, {
settings_displayBoostTemp } }, /*Boost Temp*/
{ (const char*) SettingsDescriptions[10], { settings_setAutomaticStartMode }, {
settings_displayAutomaticStartMode } }, /*Auto start*/
{ NULL, { NULL }, { NULL } } // end of menu marker. DO NOT REMOVE
};
const menuitem UIMenu[] = {
/*
// Language
* Scrolling Speed
* Temperature Unit
* Display orientation
* Cooldown blink
*/
{ (const char*) SettingsDescriptions[5], { settings_setTempF }, {
settings_displayTempF } }, /* Temperature units*/
{ (const char*) SettingsDescriptions[7], { settings_setDisplayRotation }, {
settings_displayDisplayRotation } }, /*Display Rotation*/
{ (const char*) SettingsDescriptions[11], { settings_setCoolingBlinkEnabled }, {
settings_displayCoolingBlinkEnabled } }, /*Cooling blink warning*/
{ (const char*) SettingsDescriptions[16], { settings_setScrollSpeed }, {
settings_displayScrollSpeed } }, /*Scroll Speed for descriptions*/
{ NULL, { NULL }, { NULL } } // end of menu marker. DO NOT REMOVE
};
const menuitem PowerMenu[] = {
/*
* Sleep Temp
* Sleep Time
* Shutdown Time
* Motion Sensitivity
*/
{ (const char*) SettingsDescriptions[1], { settings_setSleepTemp }, {
settings_displaySleepTemp } }, /*Sleep Temp*/
{ (const char*) SettingsDescriptions[2], { settings_setSleepTime }, {
settings_displaySleepTime } }, /*Sleep Time*/
{ (const char*) SettingsDescriptions[3], { settings_setShutdownTime }, {
settings_displayShutdownTime } }, /*Shutdown Time*/
{ (const char*) SettingsDescriptions[4], { settings_setSensitivity }, {
settings_displaySensitivity } }, /* Motion Sensitivity*/
{ NULL, { NULL }, { NULL } } // end of menu marker. DO NOT REMOVE
};
const menuitem advancedMenu[] = {
/*
* Detailed IDLE
* Detailed Soldering
* Logo Time
* Calibrate Temperature
* Calibrate Input V
* Reset Settings
*/
{ (const char*) SettingsDescriptions[6], { settings_setAdvancedIDLEScreens }, {
settings_displayAdvancedIDLEScreens } }, /* Advanced idle screen*/
{ (const char*) SettingsDescriptions[15],
{ settings_setAdvancedSolderingScreens }, {
settings_displayAdvancedSolderingScreens } }, /* Advanced soldering screen*/
{ (const char*) SettingsDescriptions[13], { settings_setResetSettings }, {
settings_displayResetSettings } }, /*Resets settings*/
{ (const char*) SettingsDescriptions[17], { settings_setTipModel }, {
settings_displayTipModel } }, /*Select tip Model */
{ (const char*) SettingsDescriptions[12], { settings_setCalibrate }, {
settings_displayCalibrate } }, /*Calibrate tip*/
{ (const char*) SettingsDescriptions[14], { settings_setCalibrateVIN }, {
settings_displayCalibrateVIN } }, /*Voltage input cal*/
{ NULL, { NULL }, { NULL } } // end of menu marker. DO NOT REMOVE
};
const menuitem calibrationMenu[] { { (const char*) SettingsDescriptions[6], {
calibration_enterSimpleCal }, { calibration_displaySimpleCal } },
/* Simple Cal*/
{ (const char*) SettingsDescriptions[6], { calibration_enterAdvancedCal }, {
calibration_displayAdvancedCal } }, /* Advanced Cal */
{ NULL, { NULL }, { NULL } } };
static void printShortDescriptionSingleLine(uint32_t shortDescIndex) {
OLED::setFont(0);
OLED::setCharCursor(0, 0);
OLED::print(SettingsShortNames[shortDescIndex][0]);
}
static void printShortDescriptionDoubleLine(uint32_t shortDescIndex) {
OLED::setFont(1);
OLED::setCharCursor(0, 0);
OLED::print(SettingsShortNames[shortDescIndex][0]);
OLED::setCharCursor(0, 1);
OLED::print(SettingsShortNames[shortDescIndex][1]);
}
/**
* Prints two small lines of short description
* and prepares cursor in big font after it.
* @param shortDescIndex Index to of short description.
* @param cursorCharPosition Custom cursor char position to set after printing
* description.
*/
static void printShortDescription(uint32_t shortDescIndex,
uint16_t cursorCharPosition) {
// print short description (default single line, explicit double line)
if (SettingsShortNameType == SHORT_NAME_DOUBLE_LINE) {
printShortDescriptionDoubleLine(shortDescIndex);
} else {
printShortDescriptionSingleLine(shortDescIndex);
}
// prepare cursor for value
OLED::setFont(0);
OLED::setCharCursor(cursorCharPosition, 0);
}
static int userConfirmation(const char* message) {
uint16_t messageWidth = FONT_12_WIDTH * (strlen(message) + 7);
uint32_t messageStart = xTaskGetTickCount();
OLED::setFont(0);
OLED::setCursor(0, 0);
int16_t lastOffset = -1;
bool lcdRefresh = true;
for (;;) {
int16_t messageOffset = ((xTaskGetTickCount() - messageStart)
/ (systemSettings.descriptionScrollSpeed == 1 ? 1 : 2));
messageOffset %= messageWidth; // Roll around at the end
if (lastOffset != messageOffset) {
OLED::clearScreen();
//^ Rolling offset based on time
OLED::setCursor((OLED_WIDTH - messageOffset), 0);
OLED::print(message);
lastOffset = messageOffset;
lcdRefresh = true;
}
ButtonState buttons = getButtonState();
switch (buttons) {
case BUTTON_F_SHORT:
// User confirmed
return 1;
case BUTTON_NONE:
break;
default:
case BUTTON_BOTH:
case BUTTON_B_SHORT:
case BUTTON_F_LONG:
case BUTTON_B_LONG:
return 0;
}
if (lcdRefresh) {
OLED::refresh();
osDelay(40);
lcdRefresh = false;
}
}
return 0;
}
#ifdef MODEL_TS100
static void settings_setInputVRange(void) {
systemSettings.cutoutSetting = (systemSettings.cutoutSetting + 1) % 5;
}
static void settings_displayInputVRange(void) {
printShortDescription(0, 6);
if (systemSettings.cutoutSetting) {
OLED::printNumber(2 + systemSettings.cutoutSetting,1);
OLED::print(SymbolCellCount);
} else {
OLED::print(SymbolDC);
}
}
#else
static void settings_setInputPRange(void) {
systemSettings.cutoutSetting = (systemSettings.cutoutSetting + 1) % 2;
}
static void settings_displayInputPRange(void) {
printShortDescription(0, 5);
//0 = 9V, 1=12V (Fixed Voltages, these imply 1.5A limits)
//2 = 18W, 2=24W (Auto Adjusting V, estimated from the tip resistance???) # TODO
// Need to come back and look at these ^ as there were issues with voltage hunting
switch (systemSettings.cutoutSetting) {
case 0:
OLED::printNumber(9, 2);
OLED::print(SymbolVolts);
break;
case 1:
OLED::printNumber(12, 2);
OLED::print(SymbolVolts);
break;
default:
break;
}
}
#endif
static void settings_setSleepTemp(void) {
// If in C, 10 deg, if in F 20 deg
if (systemSettings.temperatureInF) {
systemSettings.SleepTemp += 20;
if (systemSettings.SleepTemp > 580)
systemSettings.SleepTemp = 120;
} else {
systemSettings.SleepTemp += 10;
if (systemSettings.SleepTemp > 300)
systemSettings.SleepTemp = 50;
}
}
static void settings_displaySleepTemp(void) {
printShortDescription(1, 5);
OLED::printNumber(systemSettings.SleepTemp, 3);
}
static void settings_setSleepTime(void) {
systemSettings.SleepTime++; // Go up 1 minute at a time
if (systemSettings.SleepTime >= 16) {
systemSettings.SleepTime = 0; // can't set time over 10 mins
}
// Remember that ^ is the time of no movement
if (PCBVersion == 3)
systemSettings.SleepTime = 0; // Disable sleep on no accel
}
static void settings_displaySleepTime(void) {
printShortDescription(2, 5);
if (systemSettings.SleepTime == 0) {
OLED::print(OffString);
} else if (systemSettings.SleepTime < 6) {
OLED::printNumber(systemSettings.SleepTime * 10, 2);
OLED::print(SymbolSeconds);
} else {
OLED::printNumber(systemSettings.SleepTime - 5, 2);
OLED::print(SymbolMinutes);
}
}
static void settings_setShutdownTime(void) {
systemSettings.ShutdownTime++;
if (systemSettings.ShutdownTime > 60) {
systemSettings.ShutdownTime = 0; // wrap to off
}
if (PCBVersion == 3)
systemSettings.ShutdownTime = 0; // Disable shutdown on no accel
}
static void settings_displayShutdownTime(void) {
printShortDescription(3, 5);
if (systemSettings.ShutdownTime == 0) {
OLED::print(OffString);
} else {
OLED::printNumber(systemSettings.ShutdownTime, 2);
OLED::print(SymbolMinutes);
}
}
static void settings_setTempF(void) {
systemSettings.temperatureInF = !systemSettings.temperatureInF;
if (systemSettings.temperatureInF) {
// Change sleep, boost and soldering temps to the F equiv
// C to F == F= ( (C*9) +160)/5
systemSettings.BoostTemp = ((systemSettings.BoostTemp * 9) + 160) / 5;
systemSettings.SolderingTemp =
((systemSettings.SolderingTemp * 9) + 160) / 5;
systemSettings.SleepTemp = ((systemSettings.SleepTemp * 9) + 160) / 5;
} else {
// Change sleep, boost and soldering temps to the C equiv
// F->C == C = ((F-32)*5)/9
systemSettings.BoostTemp = ((systemSettings.BoostTemp - 32) * 5) / 9;
systemSettings.SolderingTemp = ((systemSettings.SolderingTemp - 32) * 5)
/ 9;
systemSettings.SleepTemp = ((systemSettings.SleepTemp - 32) * 5) / 9;
}
// Rescale both to be multiples of 10
systemSettings.BoostTemp = systemSettings.BoostTemp / 10;
systemSettings.BoostTemp *= 10;
systemSettings.SolderingTemp = systemSettings.SolderingTemp / 10;
systemSettings.SolderingTemp *= 10;
systemSettings.SleepTemp = systemSettings.SleepTemp / 10;
systemSettings.SleepTemp *= 10;
}
static void settings_displayTempF(void) {
printShortDescription(5, 7);
OLED::print((systemSettings.temperatureInF) ? SymbolDegF : SymbolDegC);
}
static void settings_setSensitivity(void) {
systemSettings.sensitivity++;
systemSettings.sensitivity = systemSettings.sensitivity % 10;
}
static void settings_displaySensitivity(void) {
printShortDescription(4, 7);
OLED::printNumber(systemSettings.sensitivity, 1);
}
static void settings_setAdvancedSolderingScreens(void) {
systemSettings.detailedSoldering = !systemSettings.detailedSoldering;
}
static void settings_displayAdvancedSolderingScreens(void) {
printShortDescription(15, 7);
OLED::drawCheckbox(systemSettings.detailedSoldering);
}
static void settings_setAdvancedIDLEScreens(void) {
systemSettings.detailedIDLE = !systemSettings.detailedIDLE;
}
static void settings_displayAdvancedIDLEScreens(void) {
printShortDescription(6, 7);
OLED::drawCheckbox(systemSettings.detailedIDLE);
}
static void settings_setScrollSpeed(void) {
if (systemSettings.descriptionScrollSpeed == 0)
systemSettings.descriptionScrollSpeed = 1;
else
systemSettings.descriptionScrollSpeed = 0;
}
static void settings_displayScrollSpeed(void) {
printShortDescription(16, 7);
OLED::print(
(systemSettings.descriptionScrollSpeed) ?
SettingFastChar : SettingSlowChar);
}
static void settings_setDisplayRotation(void) {
systemSettings.OrientationMode++;
systemSettings.OrientationMode = systemSettings.OrientationMode % 3;
switch (systemSettings.OrientationMode) {
case 0:
OLED::setRotation(false);
break;
case 1:
OLED::setRotation(true);
break;
case 2:
// do nothing on auto
break;
default:
break;
}
}
static void settings_displayDisplayRotation(void) {
printShortDescription(7, 7);
switch (systemSettings.OrientationMode) {
case 0:
OLED::print(SettingRightChar);
break;
case 1:
OLED::print(SettingLeftChar);
break;
case 2:
OLED::print(SettingAutoChar);
break;
default:
OLED::print(SettingRightChar);
break;
}
}
static void settings_setBoostModeEnabled(void) {
systemSettings.boostModeEnabled = !systemSettings.boostModeEnabled;
}
static void settings_displayBoostModeEnabled(void) {
printShortDescription(8, 7);
OLED::drawCheckbox(systemSettings.boostModeEnabled);
}
static void settings_setBoostTemp(void) {
if (systemSettings.temperatureInF) {
systemSettings.BoostTemp += 20; // Go up 20F at a time
if (systemSettings.BoostTemp > 850) {
systemSettings.BoostTemp = 480; // loop back at 250
}
} else {
systemSettings.BoostTemp += 10; // Go up 10C at a time
if (systemSettings.BoostTemp > 450) {
systemSettings.BoostTemp = 250; // loop back at 250
}
}
}
static void settings_displayBoostTemp(void) {
printShortDescription(9, 5);
OLED::printNumber(systemSettings.BoostTemp, 3);
}
static void settings_setAutomaticStartMode(void) {
systemSettings.autoStartMode++;
systemSettings.autoStartMode %= 2;
}
static void settings_displayAutomaticStartMode(void) {
printShortDescription(10, 7);
OLED::drawCheckbox(systemSettings.autoStartMode);
}
static void settings_setCoolingBlinkEnabled(void) {
systemSettings.coolingTempBlink = !systemSettings.coolingTempBlink;
}
static void settings_displayCoolingBlinkEnabled(void) {
printShortDescription(11, 7);
OLED::drawCheckbox(systemSettings.coolingTempBlink);
}
static void settings_setResetSettings(void) {
if (userConfirmation(SettingsResetWarning)) {
resetSettings();
OLED::setFont(0);
OLED::setCursor(0, 0);
OLED::print(ResetOKMessage);
OLED::refresh();
waitForButtonPressOrTimeout(200); // 2 second timeout
}
}
static void settings_displayResetSettings(void) {
printShortDescription(13, 7);
}
static void settings_setTipModel(void) {
systemSettings.tipType++;
if(systemSettings.tipType==Tip_MiniWare)
systemSettings.tipType++;
#ifdef MODEL_TS100
if(systemSettings.tipType==Tip_Hakko)
systemSettings.tipType++;
#endif
systemSettings.tipType %= (Tip_Custom + 1); // Wrap after custom
}
static void settings_displayTipModel(void) {
printShortDescription(17, 4);
// Print in small text the tip model
OLED::setFont(1);
// set the cursor
// Print the mfg
OLED::setCursor(55, 0);
if (systemSettings.tipType == Tip_Custom) {
OLED::print(TipModelStrings[Tip_Custom]);
} else if (systemSettings.tipType < Tip_MiniWare) {
OLED::print(TipModelStrings[Tip_MiniWare]);
}
#ifdef MODEL_TS100
else if (systemSettings.tipType < Tip_Hakko) {
OLED::print(TipModelStrings[Tip_Hakko]);
}
#endif
OLED::setCursor(55, 8);
if (systemSettings.tipType != Tip_Custom)
OLED::print(TipModelStrings[systemSettings.tipType]);
}
static void calibration_displaySimpleCal(void) {
printShortDescription(18, 5);
}
static void setTipOffset() {
setCalibrationOffset(0); // turn off the current offset
// If the thermocouple at the end of the tip, and the handle are at
// equalibrium, then the output should be zero, as there is no temperature
// differential.
uint32_t offset = 0;
for (uint8_t i = 0; i < 15; i++) {
offset += getTipRawTemp(0);
// cycle through the filter a fair bit to ensure we're stable.
OLED::clearScreen();
OLED::setCursor(0, 0);
OLED::print(SymbolDot);
for (uint8_t x = 0; x < i / 4; x++)
OLED::print(SymbolDot);
OLED::refresh();
osDelay(100);
}
systemSettings.CalibrationOffset = offset / 15;
// Need to remove from this the ambient temperature offset
uint32_t ambientoffset = getHandleTemperature(); // Handle temp in C x10
ambientoffset *= 100;
ambientoffset /= tipGainCalValue;
systemSettings.CalibrationOffset -= ambientoffset;
setCalibrationOffset(systemSettings.CalibrationOffset); // store the error
OLED::clearScreen();
OLED::setCursor(0, 0);
OLED::drawCheckbox(true);
OLED::refresh();
osDelay(1000);
}
static void calibration_enterSimpleCal(void) {
// User has entered into the simple cal routine
if (userConfirmation(SettingsCalibrationWarning)) {
// User has confirmed their handle is at ambient
// So take the offset measurement
setTipOffset();
// Next we want the user to put the tip into 100C water so we can calculate
// their tip's gain Gain is the m term from rise/run plot of raw readings vs
// (tip-handle) Thus we want to calculate
// ([TipRawHot-TipRawCold])/(ActualHot-HandleHot)-(ActualCold-HandleCold)
// Thus we first need to store ->
// TiprawCold,HandleCold,ActualCold==HandleCold -> RawTipCold
uint32_t RawTipCold = getTipRawTemp(0) * 10;
OLED::clearScreen();
OLED::setCursor(0, 0);
OLED::setFont(1);
OLED::print("Please Insert Tip\nInto Boiling Water");
OLED::refresh();
osDelay(200);
waitForButtonPress();
// Now take the three hot measurements
// Assume water is boiling at 100C
uint32_t RawTipHot = getTipRawTemp(0) * 10;
uint32_t HandleTempHot = getHandleTemperature() / 10;
uint32_t gain = (RawTipHot - RawTipCold) / (100 - HandleTempHot);
// Show this to the user
OLED::clearScreen();
OLED::setCursor(0, 0);
OLED::print(YourGainMessage);
OLED::printNumber(gain, 6);
OLED::refresh();
osDelay(2000);
waitForButtonPress();
OLED::clearScreen();
OLED::setCursor(0, 0);
OLED::print(SymbolPlus);
OLED::printNumber(RawTipHot, 8);
OLED::setCursor(0, 8);
OLED::print(SymbolMinus);
OLED::printNumber(RawTipCold, 8);
OLED::refresh();
osDelay(2000);
waitForButtonPress();
}
}
static void calibration_displayAdvancedCal(void) {
printShortDescription(19, 5);
}
static void calibration_enterAdvancedCal(void) {
//Advanced cal
if (userConfirmation(SettingsCalibrationWarning)) {
//User has confirmed their handle is at ambient
//So take the offset measurement
setTipOffset();
//The tip now has a known ADC offset
//Head up until it is at 350C
//Then let the user adjust the gain value until it converges
systemSettings.customTipGain = 160; // start safe and high
bool exit = false;
while (exit == false) {
//Set tip to 350C
setTipType(Tip_Custom, systemSettings.customTipGain);
currentlyActiveTemperatureTarget = ctoTipMeasurement(350);
//Check if user has pressed button to change the gain
ButtonState buttons = getButtonState();
switch (buttons) {
case BUTTON_NONE:
break;
case BUTTON_BOTH:
case BUTTON_B_LONG:
case BUTTON_F_LONG:
exit = true;
break;
case BUTTON_F_SHORT:
systemSettings.customTipGain++;
break;
case BUTTON_B_SHORT: {
systemSettings.customTipGain--;
}
break;
default:
break;
}
if (systemSettings.customTipGain > 200)
systemSettings.customTipGain = 200;
else if (systemSettings.customTipGain <= 100)
systemSettings.customTipGain = 100;
OLED::setCursor(0, 0);
OLED::clearScreen();
OLED::setFont(0);
if (OLED::getRotation())
OLED::print(SymbolMinus);
else
OLED::print(SymbolPlus);
OLED::print(SymbolSpace);
OLED::printNumber(systemSettings.customTipGain, 4);
OLED::print(SymbolSpace);
if (OLED::getRotation())
OLED::print(SymbolPlus);
else
OLED::print(SymbolMinus);
OLED::refresh();
GUIDelay();
}
// Wait for the user to confirm the exit message that the calibration is done
userConfirmation(SettingsCalibrationDone);
}
}
//Provide the user the option to tune their own tip if custom is selected
//If not only do single point tuning as per usual
static void settings_setCalibrate(void) {
if (systemSettings.tipType == Tip_Custom) {
// Two types of calibration
// 1. Basic, idle temp + hot water (100C)
// 2. Advanced, 100C + 350C, we keep PID tracking to a temperature target
return gui_Menu(calibrationMenu);
}
// Else
// Ask user if handle is at the tip temperature
// Any error between handle and the tip will be a direct offset in the control
// loop
else if (userConfirmation(SettingsCalibrationWarning)) {
// User confirmed
// So we now perform the actual calculation
setTipOffset();
}
}
static void settings_displayCalibrate(void) {
printShortDescription(12, 5);
}
static void settings_setCalibrateVIN(void) {
// Jump to the voltage calibration subscreen
OLED::setFont(0);
OLED::clearScreen();
OLED::setCursor(0, 0);
for (;;) {
OLED::setCursor(0, 0);
OLED::printNumber(getInputVoltageX10(systemSettings.voltageDiv, 0) / 10,
2);
OLED::print(SymbolDot);
OLED::printNumber(getInputVoltageX10(systemSettings.voltageDiv, 0) % 10,
1);
OLED::print(SymbolVolts);
ButtonState buttons = getButtonState();
switch (buttons) {
case BUTTON_F_SHORT:
systemSettings.voltageDiv++;
break;
case BUTTON_B_SHORT:
systemSettings.voltageDiv--;
break;
case BUTTON_BOTH:
case BUTTON_F_LONG:
case BUTTON_B_LONG:
saveSettings();
return;
break;
case BUTTON_NONE:
default:
break;
}
OLED::refresh();
osDelay(40);
// Cap to sensible values
#ifdef MODEL_TS80
if (systemSettings.voltageDiv < 500) {
systemSettings.voltageDiv = 500;
} else if (systemSettings.voltageDiv > 900) {
systemSettings.voltageDiv = 900;
}
#else
if (systemSettings.voltageDiv < 360) {
systemSettings.voltageDiv = 360;
} else if (systemSettings.voltageDiv > 520) {
systemSettings.voltageDiv = 520;
}
#endif
}
}
static void displayMenu(size_t index) {
// Call into the menu
OLED::setFont(1);
OLED::setCursor(0, 0);
// Draw title
OLED::print(SettingsMenuEntries[index]);
// Draw symbol
// 16 pixel wide image
OLED::drawArea(96 - 16, 0, 16, 16, (&SettingsMenuIcons[(16 * 2) * index]));
}
static void settings_displayCalibrateVIN(void) {
printShortDescription(14, 5);
}
static void settings_displaySolderingMenu(void) {
displayMenu(0);
}
static void settings_enterSolderingMenu(void) {
gui_Menu(solderingMenu);
}
static void settings_displayPowerMenu(void) {
displayMenu(1);
}
static void settings_enterPowerMenu(void) {
gui_Menu(PowerMenu);
}
static void settings_displayUIMenu(void) {
displayMenu(2);
}
static void settings_enterUIMenu(void) {
gui_Menu(UIMenu);
}
static void settings_displayAdvancedMenu(void) {
displayMenu(3);
}
static void settings_enterAdvancedMenu(void) {
gui_Menu(advancedMenu);
}
void gui_Menu(const menuitem* menu) {
// Draw the settings menu and provide iteration support etc
uint8_t currentScreen = 0;
uint32_t autoRepeatTimer = 0;
uint8_t autoRepeatAcceleration = 0;
bool earlyExit = false;
uint32_t descriptionStart = 0;
int16_t lastOffset = -1;
bool lcdRefresh = true;
ButtonState lastButtonState = BUTTON_NONE;
while ((menu[currentScreen].draw.func != NULL) && earlyExit == false) {
OLED::setFont(0);
OLED::setCursor(0, 0);
// If the user has hesitated for >=3 seconds, show the long text
// Otherwise "draw" the option
if ((xTaskGetTickCount() - lastButtonTime < 300)
|| menu[currentScreen].description == NULL) {
OLED::clearScreen();
menu[currentScreen].draw.func();
lastOffset = -1;
lcdRefresh = true;
} else {
// Draw description
if (descriptionStart == 0)
descriptionStart = xTaskGetTickCount();
// lower the value - higher the speed
int16_t descriptionWidth =
FONT_12_WIDTH * (strlen(menu[currentScreen].description) + 7);
int16_t descriptionOffset =
((xTaskGetTickCount() - descriptionStart)
/ (systemSettings.descriptionScrollSpeed == 1 ?
1 : 2));
descriptionOffset %= descriptionWidth; // Roll around at the end
if (lastOffset != descriptionOffset) {
OLED::clearScreen();
//^ Rolling offset based on time
OLED::setCursor((OLED_WIDTH - descriptionOffset), 0);
OLED::print(menu[currentScreen].description);
lastOffset = descriptionOffset;
lcdRefresh = true;
}
}
ButtonState buttons = getButtonState();
if (buttons != lastButtonState) {
autoRepeatAcceleration = 0;
lastButtonState = buttons;
}
switch (buttons) {
case BUTTON_BOTH:
earlyExit = true; // will make us exit next loop
descriptionStart = 0;
break;
case BUTTON_F_SHORT:
// increment
if (descriptionStart == 0) {
if (menu[currentScreen].incrementHandler.func != NULL)
menu[currentScreen].incrementHandler.func();
else
earlyExit = true;
} else
descriptionStart = 0;
break;
case BUTTON_B_SHORT:
if (descriptionStart == 0)
currentScreen++;
else
descriptionStart = 0;
break;
case BUTTON_F_LONG:
if (xTaskGetTickCount() - autoRepeatTimer + autoRepeatAcceleration >
PRESS_ACCEL_INTERVAL_MAX) {
menu[currentScreen].incrementHandler.func();
autoRepeatTimer = xTaskGetTickCount();
descriptionStart = 0;
autoRepeatAcceleration += PRESS_ACCEL_STEP;
}
break;
case BUTTON_B_LONG:
if (xTaskGetTickCount() - autoRepeatTimer + autoRepeatAcceleration >
PRESS_ACCEL_INTERVAL_MAX) {
currentScreen++;
autoRepeatTimer = xTaskGetTickCount();
descriptionStart = 0;
autoRepeatAcceleration += PRESS_ACCEL_STEP;
}
break;
case BUTTON_NONE:
default:
break;
}
if ((PRESS_ACCEL_INTERVAL_MAX - autoRepeatAcceleration) <
PRESS_ACCEL_INTERVAL_MIN) {
autoRepeatAcceleration =
PRESS_ACCEL_INTERVAL_MAX - PRESS_ACCEL_INTERVAL_MIN;
}
if (lcdRefresh) {
OLED::refresh(); // update the LCD
osDelay(40);
lcdRefresh = false;
}
}
}
void enterSettingsMenu() {
gui_Menu(rootSettingsMenu); // Call the root menu
saveSettings();
}

View File

@@ -0,0 +1,479 @@
/*
* hardware.c
*
* Created on: 2Sep.,2017
* Author: Ben V. Brown
*/
// These are all the functions for interacting with the hardware
#include "hardware.h"
#include "FreeRTOS.h"
#include "stm32f1xx_hal.h"
#include "cmsis_os.h"
volatile uint16_t PWMSafetyTimer = 0;
volatile int16_t CalibrationTempOffset = 0;
uint16_t tipGainCalValue = 0;
void setTipType(enum TipType tipType, uint8_t manualCalGain) {
if (manualCalGain)
tipGainCalValue = manualCalGain;
else
tipGainCalValue = lookupTipDefaultCalValue(tipType);
}
void setCalibrationOffset(int16_t offSet) {
CalibrationTempOffset = offSet;
}
uint16_t getHandleTemperature() {
// We return the current handle temperature in X10 C
// TMP36 in handle, 0.5V offset and then 10mV per deg C (0.75V @ 25C for
// example) STM32 = 4096 count @ 3.3V input -> But We oversample by 32/(2^2) =
// 8 times oversampling Therefore 32768 is the 3.3V input, so 0.1007080078125
// mV per count So we need to subtract an offset of 0.5V to center on 0C
// (4964.8 counts)
//
int32_t result = getADC(0);
result -= 4965; // remove 0.5V offset
// 10mV per C
// 99.29 counts per Deg C above 0C
result *= 100;
result /= 993;
return result;
}
uint16_t tipMeasurementToC(uint16_t raw) {
//((Raw Tip-RawOffset) * calibrationgain) / 1000 = tip delta in CX10
// tip delta in CX10 + handleTemp in CX10 = tip absolute temp in CX10
// Div answer by 10 to get final result
uint32_t tipDelta = ((raw - CalibrationTempOffset) * tipGainCalValue)
/ 1000;
tipDelta += getHandleTemperature();
return tipDelta / 10;
}
uint16_t ctoTipMeasurement(uint16_t temp) {
//[ (temp-handle/10) * 10000 ]/calibrationgain = tip raw delta
// tip raw delta + tip offset = tip ADC reading
int32_t TipRaw = ((temp - (getHandleTemperature() / 10)) * 10000)
/ tipGainCalValue;
TipRaw += CalibrationTempOffset;
return TipRaw;
}
uint16_t tipMeasurementToF(uint16_t raw) {
// Convert result from C to F
return (tipMeasurementToC(raw) * 9) / 5 + 32;
}
uint16_t ftoTipMeasurement(uint16_t temp) {
// Convert the temp back to C from F
return ctoTipMeasurement(((temp - 32) * 5) / 9);
}
uint16_t getTipInstantTemperature() {
uint16_t sum;
sum = hadc1.Instance->JDR1;
sum += hadc1.Instance->JDR2;
sum += hadc1.Instance->JDR3;
sum += hadc1.Instance->JDR4;
sum += hadc2.Instance->JDR1;
sum += hadc2.Instance->JDR2;
sum += hadc2.Instance->JDR3;
sum += hadc2.Instance->JDR4;
return sum; // 8x over sample
}
/*
* Loopup table for the tip calibration values for
* the gain of the tip's
* This can be found by line of best fit of TipRaw on X, and TipTemp-handle on
* Y. Then take the m term * 10000
* */
uint16_t lookupTipDefaultCalValue(enum TipType tipID) {
#ifdef MODEL_TS100
switch (tipID) {
case TS_D24:
return 141;
break;
case TS_BC2:
return (133 + 129) / 2;
break;
case TS_C1:
return 133;
break;
case TS_B2:
return 133;
default:
return 132; // make this the average of all
break;
}
#else
switch (tipID) {
case TS_D25:
return 154;
break;
case TS_B02:
return 154;
break;
default:
return 154; // make this the average of all
break;
}
#endif
}
uint16_t getTipRawTemp(uint8_t refresh) {
static uint16_t lastSample = 0;
if (refresh) {
lastSample = getTipInstantTemperature();
}
return lastSample;
}
uint16_t getInputVoltageX10(uint16_t divisor, uint8_t sample) {
// ADC maximum is 32767 == 3.3V at input == 28.05V at VIN
// Therefore we can divide down from there
// Multiplying ADC max by 4 for additional calibration options,
// ideal term is 467
#define BATTFILTERDEPTH 32
static uint8_t preFillneeded = 10;
static uint32_t samples[BATTFILTERDEPTH];
static uint8_t index = 0;
if (preFillneeded) {
for (uint8_t i = 0; i < BATTFILTERDEPTH; i++)
samples[i] = getADC(1);
preFillneeded--;
}
if (sample) {
samples[index] = getADC(1);
index = (index + 1) % BATTFILTERDEPTH;
}
uint32_t sum = 0;
for (uint8_t i = 0; i < BATTFILTERDEPTH; i++)
sum += samples[i];
sum /= BATTFILTERDEPTH;
return sum * 4 / divisor;
}
#ifdef MODEL_TS80
uint8_t QCMode = 0;
uint8_t QCTries = 0;
void seekQC(int16_t Vx10, uint16_t divisor) {
if (QCMode == 5)
startQC(divisor);
if (QCMode == 0)
return; // NOT connected to a QC Charger
if (Vx10 < 45)
return;
if (Vx10 > 130)
Vx10 = 130; //Cap max value at 13V
// Seek the QC to the Voltage given if this adapter supports continuous mode
// try and step towards the wanted value
// 1. Measure current voltage
int16_t vStart = getInputVoltageX10(divisor, 0);
int difference = Vx10 - vStart;
// 2. calculate ideal steps (0.2V changes)
int steps = difference / 2;
if (QCMode == 3) {
while (steps < 0) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET); //D+0.6
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); //D-3.3V
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET); // D-3.3Vs
vTaskDelay(3);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); //-0.6V
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
HAL_Delay(1);
steps++;
}
while (steps > 0) {
// step once up
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
vTaskDelay(3);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_Delay(1);
steps--;
}
}
// Re-measure
/* Disabled due to nothing to test and code space of around 1k*/
#ifdef QC2_ROUND_DOWN
steps = vStart - getInputVoltageX10(195);
if (steps < 0) steps = -steps;
if (steps > (difference / 2)) {
// No continuous mode, so QC2
QCMode = 2;
// Goto nearest
if (Vx10 > 10.5) {
// request 12V
// D- = 0.6V, D+ = 0.6V
// Clamp PB3
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);// pull down D+
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
} else {
// request 9V
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
}
}
#endif
}
// Must be called after FreeRToS Starts
void startQC(uint16_t divisor) {
// Pre check that the input could be >5V already, and if so, dont both
// negotiating as someone is feeding in hv
uint16_t vin = getInputVoltageX10(divisor, 1);
if (vin > 150)
return; // Over voltage
if (vin > 100) {
QCMode = 1; // ALready at ~12V
return;
}
GPIO_InitTypeDef GPIO_InitStruct;
// Tries to negotiate QC for 9V
// This is a multiple step process.
// 1. Set around 0.6V on D+ for 1.25 Seconds or so
// 2. After this It should un-short D+->D- and instead add a 20k pulldown on
// D-
// 3. Now set D+ to 3.3V and D- to 0.6V to request 9V
// OR both at 0.6V for 12V request (if the adapter can do it).
// If 12V is implimented then should fallback to 9V after validation
// Step 1. We want to pull D+ to 0.6V
// Pull PB3 donwn to ground
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);// pull low to put 0.6V on D+
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);// pull low to put 0.6V on D+
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_14 | GPIO_PIN_13;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// Delay 1.25 seconds
uint8_t enteredQC = 0;
for (uint16_t i = 0; i < 130 && enteredQC == 0; i++) {
// HAL_Delay(10);
vTaskDelay(1);
}
// Check if D- is low to spot a QC charger
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == GPIO_PIN_RESET)
enteredQC = 1;
if (enteredQC) {
// We have a QC capable charger
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pin = GPIO_PIN_10 | GPIO_PIN_8;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
// Wait for frontend ADC to stabilise
QCMode = 4;
for (uint8_t i = 0; i < 10; i++) {
if (getInputVoltageX10(divisor, 1) > 80) {
// yay we have at least QC2.0 or QC3.0
QCMode = 3; // We have at least QC2, pray for 3
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
return;
}
vTaskDelay(10); // 100mS
}
QCMode = 5;
QCTries++;
if (QCTries > 10) // 10 goes to get it going
QCMode = 0;
} else {
// no QC
QCMode = 0;
}
if (QCTries > 10)
QCMode = 0;
}
// Get tip resistance in milliohms
uint32_t calculateTipR() {
static uint32_t lastRes = 0;
if (lastRes)
return lastRes;
// We inject a small current into the front end of the iron,
// By measuring the Vdrop over the tip we can calculate the resistance
// Turn PA0 into an output and drive high to inject (3.3V-0.6)/(6K8+Rtip)
// current PA0->Diode -> 6K8 -> Tip -> GND So the op-amp will amplify the
// small signal across the tip and convert this into an easily read voltage
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // Set low first
setTipPWM(0);
vTaskDelay(1);
uint32_t offReading = getTipRawTemp(1);
for (uint8_t i = 0; i < 49; i++) {
vTaskDelay(1); // delay to allow it to stabilize
HAL_IWDG_Refresh(&hiwdg);
offReading += getTipRawTemp(1);
}
// Turn on
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // Set hgih
vTaskDelay(1); // delay to allow it too stabilize
uint32_t onReading = getTipInstantTemperature();
for (uint8_t i = 0; i < 49; i++) {
vTaskDelay(1); // delay to allow it to stabilize
HAL_IWDG_Refresh(&hiwdg);
onReading += getTipRawTemp(1);
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // Turn the output off finally
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
uint32_t difference = onReading - offReading;
// V = IR, therefore I = V/R
// We can divide this reading by a known "gain" to get the resulting
// resistance This was determined emperically This tip is 4.688444162 ohms,
// 4688 milliohms (Measured using 4 terminal measurement) 25x oversampling
// reads this as around 47490 Almost perfectly 10x the milliohms value This
// will drift massively with tip temp However we really only need 10x ohms
lastRes = (difference / 21) + 1; // ceil
return lastRes;
}
static unsigned int sqrt32(unsigned long n) {
unsigned int c = 0x8000;
unsigned int g = 0x8000;
for (;;) {
if (g * g > n)
g ^= c;
c >>= 1;
if (c == 0)
return g;
g |= c;
}
}
int16_t calculateMaxVoltage(uint8_t useHP) {
// This measures the tip resistance, then it calculates the appropriate
// voltage To stay under ~18W. Mosfet is "9A", so no issues there
// QC3.0 supports up to 18W, which is 2A @9V and 1.5A @12V
uint32_t milliOhms = calculateTipR();
// Check no tip
if (milliOhms > 10000)
return -1;
//Because of tolerance, if a user has asked for the higher power mode, then just goto 12V and call it a day
if (useHP)
return 120;
//
// V = sqrt(18W*R)
// Convert this to sqrt(18W)*sqrt(milli ohms)*sqrt(1/1000)
uint32_t Vx = sqrt32(milliOhms);
if (useHP)
Vx *= 1549; //sqrt(24)*sqrt(1/1000)*10000
else
Vx *= 1342; // sqrt(18) * sqrt(1/1000)*10000
// Round to nearest 200mV,
// So divide by 100 to start, to get in Vxx
Vx /= 100;
if (Vx % 10 >= 5)
Vx += 10;
Vx /= 10;
// Round to nearest increment of 2
if (Vx % 2 == 1)
Vx++;
//Because of how bad the tolerance is on detecting the tip resistance is
//Its more functional to bin this
if (Vx < 90)
Vx = 90;
else if (Vx >= 105)
Vx = 120;
return Vx;
}
#endif
volatile uint8_t pendingPWM = 0;
void setTipPWM(uint8_t pulse) {
PWMSafetyTimer = 10; // This is decremented in the handler for PWM so that the tip pwm is
// disabled if the PID task is not scheduled often enough.
pendingPWM = pulse;
}
// These are called by the HAL after the corresponding events from the system
// timers.
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
// Period has elapsed
if (htim->Instance == TIM2) {
// we want to turn on the output again
PWMSafetyTimer--;
// We decrement this safety value so that lockups in the
// scheduler will not cause the PWM to become locked in an
// active driving state.
// While we could assume this could never happen, its a small price for
// increased safety
htim2.Instance->CCR4 = pendingPWM;
if (htim2.Instance->CCR4 && PWMSafetyTimer) {
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
} else {
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
}
} else if (htim->Instance == TIM1) {
// STM uses this for internal functions as a counter for timeouts
HAL_IncTick();
}
}
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) {
// This was a when the PWM for the output has timed out
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_4) {
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
}
}
void vApplicationIdleHook(void) {
HAL_IWDG_Refresh(&hiwdg);
}
/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize) {
*ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
*ppxIdleTaskStackBuffer = &xIdleStack[0];
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
/* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

View File

@@ -0,0 +1,479 @@
/*
* hardware.c
*
* Created on: 2Sep.,2017
* Author: Ben V. Brown
*/
// These are all the functions for interacting with the hardware
#include "hardware.h"
#include "FreeRTOS.h"
#include "stm32f1xx_hal.h"
#include "cmsis_os.h"
#include "history.hpp"
volatile uint16_t PWMSafetyTimer = 0;
volatile int16_t CalibrationTempOffset = 0;
uint16_t tipGainCalValue = 0;
void setTipType(enum TipType tipType, uint8_t manualCalGain) {
if (manualCalGain)
tipGainCalValue = manualCalGain;
else
tipGainCalValue = lookupTipDefaultCalValue(tipType);
}
void setCalibrationOffset(int16_t offSet) {
CalibrationTempOffset = offSet;
}
uint16_t getHandleTemperature() {
// We return the current handle temperature in X10 C
// TMP36 in handle, 0.5V offset and then 10mV per deg C (0.75V @ 25C for
// example) STM32 = 4096 count @ 3.3V input -> But We oversample by 32/(2^2) =
// 8 times oversampling Therefore 32768 is the 3.3V input, so 0.1007080078125
// mV per count So we need to subtract an offset of 0.5V to center on 0C
// (4964.8 counts)
//
int32_t result = getADC(0);
result -= 4965; // remove 0.5V offset
// 10mV per C
// 99.29 counts per Deg C above 0C
result *= 100;
result /= 993;
return result;
}
uint16_t tipMeasurementToC(uint16_t raw) {
//((Raw Tip-RawOffset) * calibrationgain) / 1000 = tip delta in CX10
// tip delta in CX10 + handleTemp in CX10 = tip absolute temp in CX10
// Div answer by 10 to get final result
uint32_t tipDelta = ((raw - CalibrationTempOffset) * tipGainCalValue)
/ 1000;
tipDelta += getHandleTemperature();
return tipDelta / 10;
}
uint16_t ctoTipMeasurement(uint16_t temp) {
//[ (temp-handle/10) * 10000 ]/calibrationgain = tip raw delta
// tip raw delta + tip offset = tip ADC reading
int32_t TipRaw = ((temp - (getHandleTemperature() / 10)) * 10000)
/ tipGainCalValue;
TipRaw += CalibrationTempOffset;
return TipRaw;
}
uint16_t tipMeasurementToF(uint16_t raw) {
// Convert result from C to F
return (tipMeasurementToC(raw) * 9) / 5 + 32;
}
uint16_t ftoTipMeasurement(uint16_t temp) {
// Convert the temp back to C from F
return ctoTipMeasurement(((temp - 32) * 5) / 9);
}
uint16_t getTipInstantTemperature() {
uint16_t sum = 0; // 12 bit readings * 8 -> 15 bits
uint16_t readings[8];
//Looking to reject the highest outlier readings.
//As on some hardware these samples can run into the op-amp recovery time
//Once this time is up the signal stabilises quickly, so no need to reject minimums
readings[0] = hadc1.Instance->JDR1;
readings[1] = hadc1.Instance->JDR2;
readings[2] = hadc1.Instance->JDR3;
readings[3] = hadc1.Instance->JDR4;
readings[4] = hadc2.Instance->JDR1;
readings[5] = hadc2.Instance->JDR2;
readings[6] = hadc2.Instance->JDR3;
readings[7] = hadc2.Instance->JDR4;
uint8_t minID = 0, maxID = 0;
for (int i = 0; i < 8; i++) {
if (readings[i] < readings[minID])
minID = i;
else if (readings[i] > readings[maxID])
maxID = i;
}
for (int i = 0; i < 8; i++) {
if (i != maxID)
sum += readings[i];
}
sum += readings[minID]; //Duplicate the min to make up for the missing max value
return sum; // 8x over sample
}
/*
* Loopup table for the tip calibration values for
* the gain of the tip's
* This can be found by line of best fit of TipRaw on X, and TipTemp-handle on
* Y. Then take the m term * 10000
* */
uint16_t lookupTipDefaultCalValue(enum TipType tipID) {
#ifdef MODEL_TS100
switch (tipID) {
case TS_D24:
return 141;
break;
case TS_BC2:
return (133 + 129) / 2;
break;
case TS_C1:
return 133;
break;
case TS_B2:
return 133;
default:
return 132; // make this the average of all
break;
}
#else
switch (tipID) {
case TS_D25:
return 154;
break;
case TS_B02:
return 154;
break;
default:
return 154; // make this the average of all
break;
}
#endif
}
//2 second filter (ADC is PID_TIM_HZ Hz)
history<uint16_t, PID_TIM_HZ*4> rawTempFilter = { { 0 }, 0, 0 };
uint16_t getTipRawTemp(uint8_t refresh) {
if (refresh) {
uint16_t lastSample = getTipInstantTemperature();
rawTempFilter.update(lastSample);
return lastSample;
} else {
return rawTempFilter.average();
}
}
uint16_t getInputVoltageX10(uint16_t divisor, uint8_t sample) {
// ADC maximum is 32767 == 3.3V at input == 28.05V at VIN
// Therefore we can divide down from there
// Multiplying ADC max by 4 for additional calibration options,
// ideal term is 467
#define BATTFILTERDEPTH 32
static uint8_t preFillneeded = 10;
static uint32_t samples[BATTFILTERDEPTH];
static uint8_t index = 0;
if (preFillneeded) {
for (uint8_t i = 0; i < BATTFILTERDEPTH; i++)
samples[i] = getADC(1);
preFillneeded--;
}
if (sample) {
samples[index] = getADC(1);
index = (index + 1) % BATTFILTERDEPTH;
}
uint32_t sum = 0;
for (uint8_t i = 0; i < BATTFILTERDEPTH; i++)
sum += samples[i];
sum /= BATTFILTERDEPTH;
return sum * 4 / divisor;
}
#ifdef MODEL_TS80
uint8_t QCMode = 0;
uint8_t QCTries = 0;
void seekQC(int16_t Vx10, uint16_t divisor) {
if (QCMode == 5)
startQC(divisor);
if (QCMode == 0)
return; // NOT connected to a QC Charger
if (Vx10 < 45)
return;
if (Vx10 > 130)
Vx10 = 130; //Cap max value at 13V
// Seek the QC to the Voltage given if this adapter supports continuous mode
// try and step towards the wanted value
// 1. Measure current voltage
int16_t vStart = getInputVoltageX10(divisor, 0);
int difference = Vx10 - vStart;
// 2. calculate ideal steps (0.2V changes)
int steps = difference / 2;
if (QCMode == 3) {
while (steps < 0) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET); //D+0.6
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); //D-3.3V
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET); // D-3.3Vs
vTaskDelay(3);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); //-0.6V
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
HAL_Delay(1);
steps++;
}
while (steps > 0) {
// step once up
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
vTaskDelay(3);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_Delay(1);
steps--;
}
}
// Re-measure
/* Disabled due to nothing to test and code space of around 1k*/
#ifdef QC2_ROUND_DOWN
steps = vStart - getInputVoltageX10(195);
if (steps < 0) steps = -steps;
if (steps > (difference / 2)) {
// No continuous mode, so QC2
QCMode = 2;
// Goto nearest
if (Vx10 > 10.5) {
// request 12V
// D- = 0.6V, D+ = 0.6V
// Clamp PB3
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);// pull down D+
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
} else {
// request 9V
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
}
}
#endif
}
// Must be called after FreeRToS Starts
void startQC(uint16_t divisor) {
// Pre check that the input could be >5V already, and if so, dont both
// negotiating as someone is feeding in hv
uint16_t vin = getInputVoltageX10(divisor, 1);
if (vin > 100) {
QCMode = 1; // ALready at ~12V
return;
}
GPIO_InitTypeDef GPIO_InitStruct;
// Tries to negotiate QC for 9V
// This is a multiple step process.
// 1. Set around 0.6V on D+ for 1.25 Seconds or so
// 2. After this It should un-short D+->D- and instead add a 20k pulldown on
// D-
// 3. Now set D+ to 3.3V and D- to 0.6V to request 9V
// OR both at 0.6V for 12V request (if the adapter can do it).
// If 12V is implimented then should fallback to 9V after validation
// Step 1. We want to pull D+ to 0.6V
// Pull PB3 donwn to ground
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);// pull low to put 0.6V on D+
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);// pull low to put 0.6V on D+
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_14 | GPIO_PIN_13;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// Delay 1.25 seconds
uint8_t enteredQC = 0;
for (uint16_t i = 0; i < 130 && enteredQC == 0; i++) {
// HAL_Delay(10);
vTaskDelay(1);
}
// Check if D- is low to spot a QC charger
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == GPIO_PIN_RESET)
enteredQC = 1;
if (enteredQC) {
// We have a QC capable charger
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pin = GPIO_PIN_10 | GPIO_PIN_8;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
// Wait for frontend ADC to stabilise
QCMode = 4;
for (uint8_t i = 0; i < 10; i++) {
if (getInputVoltageX10(divisor, 1) > 80) {
// yay we have at least QC2.0 or QC3.0
QCMode = 3; // We have at least QC2, pray for 3
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
return;
}
vTaskDelay(10); // 100mS
}
QCMode = 5;
QCTries++;
if (QCTries > 10) // 10 goes to get it going
QCMode = 0;
} else {
// no QC
QCMode = 0;
}
if (QCTries > 10)
QCMode = 0;
}
// Get tip resistance in milliohms
uint32_t calculateTipR() {
static uint32_t lastRes = 0;
if (lastRes)
return lastRes;
// We inject a small current into the front end of the iron,
// By measuring the Vdrop over the tip we can calculate the resistance
// Turn PA0 into an output and drive high to inject (3.3V-0.6)/(6K8+Rtip)
// current PA0->Diode -> 6K8 -> Tip -> GND So the op-amp will amplify the
// small signal across the tip and convert this into an easily read voltage
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // Set low first
setTipPWM(0);
vTaskDelay(1);
uint32_t offReading = getTipRawTemp(1);
for (uint8_t i = 0; i < 49; i++) {
vTaskDelay(1); // delay to allow it to stabilize
HAL_IWDG_Refresh(&hiwdg);
offReading += getTipRawTemp(1);
}
// Turn on
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // Set hgih
vTaskDelay(1); // delay to allow it too stabilize
uint32_t onReading = getTipInstantTemperature();
for (uint8_t i = 0; i < 49; i++) {
vTaskDelay(1); // delay to allow it to stabilize
HAL_IWDG_Refresh(&hiwdg);
onReading += getTipRawTemp(1);
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // Turn the output off finally
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
uint32_t difference = onReading - offReading;
// V = IR, therefore I = V/R
// We can divide this reading by a known "gain" to get the resulting
// resistance This was determined emperically This tip is 4.688444162 ohms,
// 4688 milliohms (Measured using 4 terminal measurement) 25x oversampling
// reads this as around 47490 Almost perfectly 10x the milliohms value This
// will drift massively with tip temp However we really only need 10x ohms
lastRes = (difference / 21) + 1; // ceil
return lastRes;
}
static unsigned int sqrt32(unsigned long n) {
unsigned int c = 0x8000;
unsigned int g = 0x8000;
for (;;) {
if (g * g > n)
g ^= c;
c >>= 1;
if (c == 0)
return g;
g |= c;
}
}
int16_t calculateMaxVoltage(uint8_t useHP) {
// This measures the tip resistance, then it calculates the appropriate
// voltage To stay under ~18W. Mosfet is "9A", so no issues there
// QC3.0 supports up to 18W, which is 2A @9V and 1.5A @12V
uint32_t milliOhms = calculateTipR();
// Check no tip
if (milliOhms > 10000)
return -1;
//Because of tolerance, if a user has asked for the higher power mode, then just goto 12V and call it a day
if (useHP)
return 120;
//
// V = sqrt(18W*R)
// Convert this to sqrt(18W)*sqrt(milli ohms)*sqrt(1/1000)
uint32_t Vx = sqrt32(milliOhms);
if (useHP)
Vx *= 1549; //sqrt(24)*sqrt(1/1000)*10000
else
Vx *= 1342; // sqrt(18) * sqrt(1/1000)*10000
// Round to nearest 200mV,
// So divide by 100 to start, to get in Vxx
Vx /= 100;
if (Vx % 10 >= 5)
Vx += 10;
Vx /= 10;
// Round to nearest increment of 2
if (Vx % 2 == 1)
Vx++;
//Because of how bad the tolerance is on detecting the tip resistance is
//Its more functional to bin this
if (Vx < 90)
Vx = 90;
else if (Vx >= 105)
Vx = 120;
return Vx;
}
#endif
volatile uint8_t pendingPWM = 0;
void setTipPWM(uint8_t pulse) {
PWMSafetyTimer = 10; // This is decremented in the handler for PWM so that the tip pwm is
// disabled if the PID task is not scheduled often enough.
pendingPWM = pulse;
}
// These are called by the HAL after the corresponding events from the system
// timers.
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
// Period has elapsed
if (htim->Instance == TIM2) {
// we want to turn on the output again
PWMSafetyTimer--;
// We decrement this safety value so that lockups in the
// scheduler will not cause the PWM to become locked in an
// active driving state.
// While we could assume this could never happen, its a small price for
// increased safety
htim2.Instance->CCR4 = pendingPWM;
if (htim2.Instance->CCR4 && PWMSafetyTimer) {
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
} else {
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
}
} else if (htim->Instance == TIM1) {
// STM uses this for internal functions as a counter for timeouts
HAL_IncTick();
}
}
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) {
// This was a when the PWM for the output has timed out
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_4) {
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
}
}

View File

@@ -0,0 +1,376 @@
// By Ben V. Brown - V2.0 of the TS100 firmware
#include <MMA8652FC.hpp>
#include <gui.hpp>
#include <main.hpp>
#include "LIS2DH12.hpp"
#include <history.hpp>
#include <power.hpp>
#include "Settings.h"
#include "Translation.h"
#include "cmsis_os.h"
#include "stdlib.h"
#include "stm32f1xx_hal.h"
#include "string.h"
uint8_t PCBVersion = 0;
// File local variables
uint32_t currentlyActiveTemperatureTarget = 0;
uint32_t lastMovementTime = 0;
int16_t idealQCVoltage = 0;
// FreeRTOS variables
osThreadId GUITaskHandle;
static const size_t GUITaskStackSize = 1024/4;
uint32_t GUITaskBuffer[GUITaskStackSize];
osStaticThreadDef_t GUITaskControlBlock;
osThreadId PIDTaskHandle;
static const size_t PIDTaskStackSize =512 / 4;
uint32_t PIDTaskBuffer[PIDTaskStackSize];
osStaticThreadDef_t PIDTaskControlBlock;
osThreadId MOVTaskHandle;
static const size_t MOVTaskStackSize = 512/4;
uint32_t MOVTaskBuffer[MOVTaskStackSize];
osStaticThreadDef_t MOVTaskControlBlock;
static TaskHandle_t pidTaskNotification = NULL;
void startGUITask(void const *argument);
void startPIDTask(void const *argument);
void startMOVTask(void const *argument);
// End FreeRTOS
// Main sets up the hardware then hands over to the FreeRTOS kernel
int main(void) {
/* Reset of all peripherals, Initializes the Flash interface and the Systick.
*/
HAL_Init();
Setup_HAL(); // Setup all the HAL objects
HAL_IWDG_Refresh(&hiwdg);
setTipMilliWatts(0); // force tip off
FRToSI2C::init(&hi2c1);
OLED::initialize(); // start up the LCD
OLED::setFont(0); // default to bigger font
// Testing for which accelerometer is mounted
uint8_t buffer[1];
HAL_IWDG_Refresh(&hiwdg);
if (HAL_I2C_Mem_Read(&hi2c1, 29 << 1, 0x0F, I2C_MEMADD_SIZE_8BIT, buffer, 1,
1000) == HAL_OK) {
PCBVersion = 1;
MMA8652FC::initalize(); // this sets up the I2C registers
} else if (HAL_I2C_Mem_Read(&hi2c1, 25 << 1, 0x0F, I2C_MEMADD_SIZE_8BIT,
buffer, 1, 1000) == HAL_OK) {
PCBVersion = 2;
// Setup the ST Accelerometer
LIS2DH12::initalize(); // startup the accelerometer
} else {
PCBVersion = 3;
systemSettings.SleepTime = 0;
systemSettings.ShutdownTime = 0; // No accel -> disable sleep
systemSettings.sensitivity = 0;
}
HAL_IWDG_Refresh(&hiwdg);
restoreSettings(); // load the settings from flash
setCalibrationOffset(systemSettings.CalibrationOffset);
setTipType((enum TipType) systemSettings.tipType,
systemSettings.customTipGain); // apply tip type selection
HAL_IWDG_Refresh(&hiwdg);
/* Create the thread(s) */
/* definition and creation of GUITask */
osThreadStaticDef(GUITask, startGUITask, osPriorityBelowNormal, 0,
GUITaskStackSize, GUITaskBuffer, &GUITaskControlBlock);
GUITaskHandle = osThreadCreate(osThread(GUITask), NULL);
/* definition and creation of PIDTask */
osThreadStaticDef(PIDTask, startPIDTask, osPriorityRealtime, 0,
PIDTaskStackSize, PIDTaskBuffer, &PIDTaskControlBlock);
PIDTaskHandle = osThreadCreate(osThread(PIDTask), NULL);
if (PCBVersion < 3) {
osThreadStaticDef(MOVTask, startMOVTask, osPriorityNormal, 0,
MOVTaskStackSize, MOVTaskBuffer, &MOVTaskControlBlock);
MOVTaskHandle = osThreadCreate(osThread(MOVTask), NULL);
}
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
while (1) {
}
}
/* StartPIDTask function */
void startPIDTask(void const *argument __unused) {
/*
* We take the current tip temperature & evaluate the next step for the tip
* control PWM.
*/
setTipMilliWatts(0); // disable the output driver if the output is set to be off
#ifdef MODEL_TS80
idealQCVoltage = calculateMaxVoltage(systemSettings.cutoutSetting);
#endif
uint8_t rawC = ctoTipMeasurement(101) - ctoTipMeasurement(100); // 1*C change in raw.
#ifdef MODEL_TS80
//Set power management code to the tip resistance in ohms * 10
setupPower(calculateTipR() / 100);
TickType_t lastPowerPulse = 0;
#else
setupPower(85);
#endif
history<int32_t> tempError = { { 0 }, 0, 0 };
currentlyActiveTemperatureTarget = 0; // Force start with no output (off). If in sleep / soldering this will
// be over-ridden rapidly
pidTaskNotification = xTaskGetCurrentTaskHandle();
for (;;) {
if (ulTaskNotifyTake(pdTRUE, 2000)) {
// This is a call to block this thread until the ADC does its samples
uint16_t rawTemp = getTipRawTemp(1); // get instantaneous reading
if (currentlyActiveTemperatureTarget) {
// Cap the max set point to 450C
if (currentlyActiveTemperatureTarget > ctoTipMeasurement(450)) {
//Maximum allowed output
currentlyActiveTemperatureTarget = ctoTipMeasurement(450);
} else if (currentlyActiveTemperatureTarget > 32400) {
//Cap to max adc reading
currentlyActiveTemperatureTarget = 32400;
}
// As we get close to our target, temp noise causes the system
// to be unstable. Use a rolling average to dampen it.
// We overshoot by roughly 1/2 of 1 degree Fahrenheit.
// This helps stabilize the display.
int32_t tError = currentlyActiveTemperatureTarget - rawTemp
+ (rawC / 4);
tError = tError > INT16_MAX ? INT16_MAX : tError;
tError = tError < INT16_MIN ? INT16_MIN : tError;
tempError.update(tError);
// Now for the PID!
int32_t milliWattsOut = 0;
// P term - total power needed to hit target temp next cycle.
// thermal mass = 1690 milliJ/*C for my tip.
// = Watts*Seconds to raise Temp from room temp to +100*C, divided by 100*C.
// we divide milliWattsNeeded by 20 to let the I term dominate near the set point.
// This is necessary because of the temp noise and thermal lag in the system.
// Once we have feed-forward temp estimation we should be able to better tune this.
#ifdef MODEL_TS100
const uint16_t mass = 2020 / 20; // divide here so division is compile-time.
#endif
#ifdef MODEL_TS80
const uint16_t mass = 2020 / 50;
#endif
int32_t milliWattsNeeded = tempToMilliWatts(tempError.average(),
mass, rawC);
// note that milliWattsNeeded is sometimes negative, this counters overshoot
// from I term's inertia.
milliWattsOut += milliWattsNeeded;
// I term - energy needed to compensate for heat loss.
// We track energy put into the system over some window.
// Assuming the temp is stable, energy in = energy transfered.
// (If it isn't, P will dominate).
milliWattsOut += milliWattHistory.average();
// D term - use sudden temp change to counter fast cooling/heating.
// In practice, this provides an early boost if temp is dropping
// and counters extra power if the iron is no longer losing temp.
// basically: temp - lastTemp
// Unfortunately, our temp signal is too noisy to really help.
setTipMilliWatts(milliWattsOut);
} else {
#ifdef MODEL_TS80
//If its a TS80, we want to have the option of using an occasional pulse to keep the power bank on
// This is purely guesswork :'( as everyone implements stuff differently
if (xTaskGetTickCount() - lastPowerPulse < 10) {
// for the first 100mS turn on for a bit
setTipMilliWatts(5000); // typically its around 5W to hold the current temp, so this wont raise temp much
} else
setTipMilliWatts(0);
//Then wait until the next 0.5 seconds
if (xTaskGetTickCount() - lastPowerPulse > 50) {
lastPowerPulse = xTaskGetTickCount();
}
#else
setTipMilliWatts(0);
#endif
}
HAL_IWDG_Refresh(&hiwdg);
} else {
asm("bkpt");
//ADC interrupt timeout
setTipMilliWatts(0);
setTipPWM(0);
}
}
}
#define MOVFilter 8
void startMOVTask(void const *argument __unused) {
OLED::setRotation(true);
#ifdef MODEL_TS80
startQC(systemSettings.voltageDiv);
while (pidTaskNotification == 0)
osDelay(30); // To ensure we return after idealQCVoltage/tip resistance
seekQC(idealQCVoltage, systemSettings.voltageDiv); // this will move the QC output to the preferred voltage to start with
#else
osDelay(250); // wait for accelerometer to stabilize
#endif
OLED::setRotation(systemSettings.OrientationMode & 1);
lastMovementTime = 0;
int16_t datax[MOVFilter] = { 0 };
int16_t datay[MOVFilter] = { 0 };
int16_t dataz[MOVFilter] = { 0 };
uint8_t currentPointer = 0;
int16_t tx = 0, ty = 0, tz = 0;
int32_t avgx = 0, avgy = 0, avgz = 0;
if (systemSettings.sensitivity > 9)
systemSettings.sensitivity = 9;
#if ACCELDEBUG
uint32_t max = 0;
#endif
Orientation rotation = ORIENTATION_FLAT;
for (;;) {
int32_t threshold = 1500 + (9 * 200);
threshold -= systemSettings.sensitivity * 200; // 200 is the step size
if (PCBVersion == 2) {
LIS2DH12::getAxisReadings(tx, ty, tz);
rotation = LIS2DH12::getOrientation();
} else if (PCBVersion == 1) {
MMA8652FC::getAxisReadings(tx, ty, tz);
rotation = MMA8652FC::getOrientation();
}
if (systemSettings.OrientationMode == 2) {
if (rotation != ORIENTATION_FLAT) {
OLED::setRotation(rotation == ORIENTATION_LEFT_HAND); // link the data through
}
}
datax[currentPointer] = (int32_t) tx;
datay[currentPointer] = (int32_t) ty;
dataz[currentPointer] = (int32_t) tz;
currentPointer = (currentPointer + 1) % MOVFilter;
avgx = avgy = avgz = 0;
// calculate averages
for (uint8_t i = 0; i < MOVFilter; i++) {
avgx += datax[i];
avgy += datay[i];
avgz += dataz[i];
}
avgx /= MOVFilter;
avgy /= MOVFilter;
avgz /= MOVFilter;
// Sum the deltas
int32_t error = (abs(avgx - tx) + abs(avgy - ty) + abs(avgz - tz));
// So now we have averages, we want to look if these are different by more
// than the threshold
// If error has occurred then we update the tick timer
if (error > threshold) {
lastMovementTime = xTaskGetTickCount();
}
osDelay(100); // Slow down update rate
#ifdef MODEL_TS80
if (currentlyActiveTemperatureTarget) {
seekQC(idealQCVoltage, systemSettings.voltageDiv); // Run the QC seek again to try and compensate for cable V drop
}
#endif
}
}
#define FLASH_LOGOADDR \
(0x8000000 | 0xF800) /*second last page of flash set aside for logo image*/
bool showBootLogoIfavailable() {
// check if the header is there (0xAA,0x55,0xF0,0x0D)
// If so display logo
// TODO REDUCE STACK ON THIS ONE, USE DRAWING IN THE READ LOOP
uint16_t temp[98];
for (uint8_t i = 0; i < (98); i++) {
temp[i] = *(uint16_t *) (FLASH_LOGOADDR + (i * 2));
}
uint8_t temp8[98 * 2];
for (uint8_t i = 0; i < 98; i++) {
temp8[i * 2] = temp[i] >> 8;
temp8[i * 2 + 1] = temp[i] & 0xFF;
}
if (temp8[0] != 0xAA)
return false;
if (temp8[1] != 0x55)
return false;
if (temp8[2] != 0xF0)
return false;
if (temp8[3] != 0x0D)
return false;
OLED::drawArea(0, 0, 96, 16, (uint8_t *) (temp8 + 4));
OLED::refresh();
return true;
}
/*
* Catch the IRQ that says that the conversion is done on the temperature
* readings coming in Once these have come in we can unblock the PID so that it
* runs again
*/
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *hadc) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (hadc == &hadc1) {
if (pidTaskNotification) {
vTaskNotifyGiveFromISR(pidTaskNotification,
&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
}
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c __unused) {
FRToSI2C::CpltCallback();
}
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c __unused) {
FRToSI2C::CpltCallback();
}
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c __unused) {
FRToSI2C::CpltCallback();
}
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c __unused) {
asm("bkpt");
FRToSI2C::CpltCallback();
}
void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c __unused) {
//asm("bkpt");
FRToSI2C::CpltCallback();
}
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c __unused) {
FRToSI2C::CpltCallback();
}
void vApplicationStackOverflowHook(xTaskHandle *pxTask __unused,
signed portCHAR *pcTaskName __unused) {
asm("bkpt");
// We dont have a good way to handle a stack overflow at this point in time
NVIC_SystemReset();
}

View File

@@ -0,0 +1,76 @@
/*
* power.cpp
*
* Created on: 28 Oct, 2018
* Authors: Ben V. Brown, David Hilton
*/
#include <power.hpp>
#include <Settings.h>
#include <hardware.h>
const uint16_t powerPWM = 255;
const uint16_t totalPWM = 255 + 17; //htim2.Init.Period, the full PWM cycle
history<uint32_t, oscillationPeriod> milliWattHistory = { { 0 }, 0, 0 };
int32_t tempToMilliWatts(int32_t rawTemp, uint8_t rawC) {
// mass is in milliJ/*C, rawC is raw per degree C
// returns milliWatts needed to raise/lower a mass by rawTemp
// degrees in one cycle.
int32_t milliJoules = tipMass*10 * (rawTemp / rawC);
return milliJoules;
}
void setTipMilliWatts(int32_t mw) {
//Enforce Max Watts Limiter # TODO
int32_t output = milliWattsToPWM(mw, systemSettings.voltageDiv , 1);
setTipPWM(output);
uint32_t actualMilliWatts = PWMToMilliWatts(output,
systemSettings.voltageDiv , 0);
milliWattHistory.update(actualMilliWatts);
}
int32_t availableW10(uint8_t divisor, uint8_t sample) {
//P = V^2 / R, v*v = v^2 * 100
// R = R*10
// P therefore is in V^2*100/R*10 = W*10.
int32_t v = getInputVoltageX10(divisor, sample); // 100 = 10v
int32_t availableWattsX10 = (v * v) / tipResistance;
//However, 100% duty cycle is not possible as there is a dead time while the ADC takes a reading
//Therefore need to scale available milliwats by this
// avMw=(AvMw*powerPWM)/totalPWM.
availableWattsX10 = availableWattsX10 * powerPWM;
availableWattsX10 /= totalPWM;
//availableMilliWattsX10 is now an accurate representation
return availableWattsX10;
}
uint8_t milliWattsToPWM(int32_t milliWatts, uint8_t divisor, uint8_t sample) {
// Scale input milliWatts to the pwm rate
if (milliWatts < 10) // no pint driving tip
return 0;
//Calculate desired milliwatts as a percentage of availableW10
int32_t pwm = (powerPWM * milliWatts) / availableW10(divisor, sample);
if (pwm > powerPWM) {
pwm = powerPWM; //constrain to max PWM counter, shouldnt be possible, but small cost for safety to avoid wraps
} else if (pwm < 0) { //cannot go negative
pwm = 0;
}
return pwm;
}
int32_t PWMToMilliWatts(uint8_t pwm, uint8_t divisor, uint8_t sample) {
int32_t maxMW = availableW10(divisor, sample); //Get the milliwatts for the max pwm period
//Then convert pwm into percentage of powerPWM to get the percentage of the max mw
int32_t res = (pwm * maxMW) / powerPWM;
if (res < 0)
res = 0;
return res;
}

View File

@@ -0,0 +1,136 @@
#include <hardware.h>
#include "stm32f1xx_hal.h"
#include "Setup.h"
/**
* Initializes the Global MSP.
*/
void HAL_MspInit(void) {
__HAL_RCC_AFIO_CLK_ENABLE()
;
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* System interrupt init*/
/* MemoryManagement_IRQn interrupt configuration */
HAL_NVIC_SetPriority(MemoryManagement_IRQn, 0, 0);
/* BusFault_IRQn interrupt configuration */
HAL_NVIC_SetPriority(BusFault_IRQn, 0, 0);
/* UsageFault_IRQn interrupt configuration */
HAL_NVIC_SetPriority(UsageFault_IRQn, 0, 0);
/* SVCall_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SVCall_IRQn, 0, 0);
/* DebugMonitor_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DebugMonitor_IRQn, 0, 0);
/* PendSV_IRQn interrupt configuration */
HAL_NVIC_SetPriority(PendSV_IRQn, 15, 0);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc) {
GPIO_InitTypeDef GPIO_InitStruct;
if (hadc->Instance == ADC1) {
__HAL_RCC_ADC1_CLK_ENABLE()
;
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_VERY_HIGH;
HAL_DMA_Init(&hdma_adc1);
__HAL_LINKDMA(hadc, DMA_Handle, hdma_adc1);
/* ADC1 interrupt Init */
HAL_NVIC_SetPriority(ADC1_2_IRQn, 15, 0);
HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
} else {
__HAL_RCC_ADC2_CLK_ENABLE()
;
/**ADC2 GPIO Configuration
PB0 ------> ADC2_IN8
PB1 ------> ADC2_IN9
*/
GPIO_InitStruct.Pin = TIP_TEMP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* ADC2 interrupt Init */
HAL_NVIC_SetPriority(ADC1_2_IRQn, 15, 0);
HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
}
}
void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) {
GPIO_InitTypeDef GPIO_InitStruct;
/**I2C1 GPIO Configuration
PB6 ------> I2C1_SCL
PB7 ------> I2C1_SDA
*/
GPIO_InitStruct.Pin = SCL_Pin | SDA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Peripheral clock enable */
__HAL_RCC_I2C1_CLK_ENABLE()
;
/* I2C1 DMA Init */
/* I2C1_RX Init */
hdma_i2c1_rx.Instance = DMA1_Channel7;
hdma_i2c1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_i2c1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c1_rx.Init.Mode = DMA_NORMAL;
hdma_i2c1_rx.Init.Priority = DMA_PRIORITY_LOW;
HAL_DMA_Init(&hdma_i2c1_rx);
__HAL_LINKDMA(hi2c, hdmarx, hdma_i2c1_rx);
/* I2C1_TX Init */
hdma_i2c1_tx.Instance = DMA1_Channel6;
hdma_i2c1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_i2c1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c1_tx.Init.Mode = DMA_NORMAL;
hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
HAL_DMA_Init(&hdma_i2c1_tx);
__HAL_LINKDMA(hi2c, hdmatx, hdma_i2c1_tx);
/* I2C1 interrupt Init */
HAL_NVIC_SetPriority(I2C1_EV_IRQn, 15, 0);
HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
HAL_NVIC_SetPriority(I2C1_ER_IRQn, 15, 0);
HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base) {
if (htim_base->Instance == TIM3) {
/* Peripheral clock enable */
__HAL_RCC_TIM3_CLK_ENABLE()
;
} else if (htim_base->Instance == TIM2) {
/* Peripheral clock enable */
__HAL_RCC_TIM2_CLK_ENABLE()
;
}
}

View File

@@ -0,0 +1,158 @@
/**
******************************************************************************
* @file stm32f1xx_hal_timebase_TIM.c
* @brief HAL time base based on the hardware TIM.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2017 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx_hal.h"
#include "stm32f1xx_hal_tim.h"
/** @addtogroup STM32F7xx_HAL_Examples
* @{
*/
/** @addtogroup HAL_TimeBase
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim1;
uint32_t uwIncrementState = 0;
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/**
* @brief This function configures the TIM1 as a time base source.
* The time source is configured to have 1ms time base with a dedicated
* Tick interrupt priority.
* @note This function is called automatically at the beginning of program after
* reset by HAL_Init() or at any time when clock is configured, by HAL_RCC_ClockConfig().
* @param TickPriority: Tick interrupt priorty.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
RCC_ClkInitTypeDef clkconfig;
uint32_t uwTimclock = 0;
uint32_t uwPrescalerValue = 0;
uint32_t pFLatency;
/*Configure the TIM1 IRQ priority */
HAL_NVIC_SetPriority(TIM1_UP_IRQn, TickPriority ,0);
/* Enable the TIM1 global Interrupt */
HAL_NVIC_EnableIRQ(TIM1_UP_IRQn);
/* Enable TIM1 clock */
__HAL_RCC_TIM1_CLK_ENABLE();
/* Get clock configuration */
HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);
/* Compute TIM1 clock */
uwTimclock = HAL_RCC_GetPCLK2Freq();
/* Compute the prescaler value to have TIM1 counter clock equal to 1MHz */
uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000) - 1);
/* Initialize TIM1 */
htim1.Instance = TIM1;
/* Initialize TIMx peripheral as follow:
+ Period = [(TIM1CLK/1000) - 1]. to have a (1/1000) s time base.
+ Prescaler = (uwTimclock/1000000 - 1) to have a 1MHz counter clock.
+ ClockDivision = 0
+ Counter direction = Up
*/
htim1.Init.Period = (1000000 / 1000) - 1;
htim1.Init.Prescaler = uwPrescalerValue;
htim1.Init.ClockDivision = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
if(HAL_TIM_Base_Init(&htim1) == HAL_OK)
{
/* Start the TIM time Base generation in interrupt mode */
return HAL_TIM_Base_Start_IT(&htim1);
}
/* Return function status */
return HAL_ERROR;
}
/**
* @brief Suspend Tick increment.
* @note Disable the tick increment by disabling TIM1 update interrupt.
* @param None
* @retval None
*/
void HAL_SuspendTick(void)
{
/* Disable TIM1 update Interrupt */
__HAL_TIM_DISABLE_IT(&htim1, TIM_IT_UPDATE);
}
/**
* @brief Resume Tick increment.
* @note Enable the tick increment by Enabling TIM1 update interrupt.
* @param None
* @retval None
*/
void HAL_ResumeTick(void)
{
/* Enable TIM1 Update interrupt */
__HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE);
}
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@@ -0,0 +1,84 @@
// This is the stock standard STM interrupt file full of handlers
#include "stm32f1xx_hal.h"
#include "stm32f1xx.h"
#include "stm32f1xx_it.h"
#include "cmsis_os.h"
#include "Setup.h"
extern TIM_HandleTypeDef htim1; //used for the systick
/******************************************************************************/
/* Cortex-M3 Processor Interruption and Exception Handlers */
/******************************************************************************/
void NMI_Handler(void) {
}
//We have the assembly for a breakpoint trigger here to halt the system when a debugger is connected
// Hardfault handler, often a screwup in the code
void HardFault_Handler(void) {
}
// Memory management unit had an error
void MemManage_Handler(void) {
}
// Prefetcher or busfault occured
void BusFault_Handler(void) {
}
void UsageFault_Handler(void) {
}
void DebugMon_Handler(void) {
}
// Systick is used by FreeRTOS tick
void SysTick_Handler(void) {
osSystickHandler();
}
/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file. */
/******************************************************************************/
// DMA used to move the ADC readings into system ram
void DMA1_Channel1_IRQHandler(void) {
HAL_DMA_IRQHandler(&hdma_adc1);
}
//ADC interrupt used for DMA
void ADC1_2_IRQHandler(void) {
HAL_ADC_IRQHandler(&hadc1);
}
//Timer 1 has overflowed, used for HAL ticks
void TIM1_UP_IRQHandler(void) {
HAL_TIM_IRQHandler(&htim1);
}
//Timer 3 is used for the PWM output to the tip
void TIM3_IRQHandler(void) {
HAL_TIM_IRQHandler(&htim3);
}
//Timer 2 is used for co-ordination of PWM & ADC
void TIM2_IRQHandler(void) {
HAL_TIM_IRQHandler(&htim2);
}
void I2C1_EV_IRQHandler(void) {
HAL_I2C_EV_IRQHandler(&hi2c1);
}
void I2C1_ER_IRQHandler(void) {
HAL_I2C_ER_IRQHandler(&hi2c1);
}
void DMA1_Channel6_IRQHandler(void) {
HAL_DMA_IRQHandler(&hdma_i2c1_tx);
}
void DMA1_Channel7_IRQHandler(void) {
HAL_DMA_IRQHandler(&hdma_i2c1_rx);
}

View File

@@ -0,0 +1,317 @@
// This file was automatically generated by the STM Cube software
// And as such, is BSD licneced from STM
#include "stm32f1xx.h"
#if !defined (HSI_VALUE)
#define HSI_VALUE 8000000U /*!< Default value of the Internal oscillator in Hz.
This value can be provided and adapted by the user application. */
#endif /* HSI_VALUE */
/*!< Uncomment the following line if you need to use external SRAM */
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
/* #define DATA_IN_ExtSRAM */
#endif /* STM32F100xE || STM32F101xE || STM32F101xG || STM32F103xE || STM32F103xG */
#ifndef LOCAL_BUILD
#define VECT_TAB_OFFSET 0x00004000U /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
#else
#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
#warning LOCAL_BUILD SETUP
#endif
//We offset this by 0x4000 to because of the bootloader
/*******************************************************************************
* Clock Definitions
*******************************************************************************/
#if defined(STM32F100xB) ||defined(STM32F100xE)
uint32_t SystemCoreClock = 24000000U; /*!< System Clock Frequency (Core Clock) */
#else /*!< HSI Selected as System Clock source */
uint32_t SystemCoreClock = 64000000U; /*!< System Clock Frequency (Core Clock) */
#endif
const uint8_t AHBPrescTable[16U] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
const uint8_t APBPrescTable[8U] = {0, 0, 0, 0, 1, 2, 3, 4};
/**
* @brief Setup the microcontroller system
* Initialize the Embedded Flash Interface, the PLL and update the
* SystemCoreClock variable.
* @note This function should be used only after reset.
* @param None
* @retval None
*/
void SystemInit (void)
{
/* Reset the RCC clock configuration to the default reset state(for debug purpose) */
/* Set HSION bit */
RCC->CR |= 0x00000001U;
/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#if !defined(STM32F105xC) && !defined(STM32F107xC)
RCC->CFGR &= 0xF8FF0000U;
#else
RCC->CFGR &= 0xF0FF0000U;
#endif /* STM32F105xC */
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= 0xFEF6FFFFU;
/* Reset HSEBYP bit */
RCC->CR &= 0xFFFBFFFFU;
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
RCC->CFGR &= 0xFF80FFFFU;
#if defined(STM32F105xC) || defined(STM32F107xC)
/* Reset PLL2ON and PLL3ON bits */
RCC->CR &= 0xEBFFFFFFU;
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x00FF0000U;
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000U;
#elif defined(STM32F100xB) || defined(STM32F100xE)
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000U;
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000U;
#else
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000U;
#endif /* STM32F105xC */
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif
}
/**
* @brief Update SystemCoreClock variable according to Clock Register Values.
* The SystemCoreClock variable contains the core clock (HCLK), it can
* be used by the user application to setup the SysTick timer or configure
* other parameters.
*
* @note Each time the core clock (HCLK) changes, this function must be called
* to update SystemCoreClock variable value. Otherwise, any configuration
* based on this variable will be incorrect.
*
* @note - The system frequency computed by this function is not the real
* frequency in the chip. It is calculated based on the predefined
* constant and the selected clock source:
*
* - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
*
* - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
*
* - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
* or HSI_VALUE(*) multiplied by the PLL factors.
*
* (*) HSI_VALUE is a constant defined in stm32f1xx.h file (default value
* 8 MHz) but the real value may vary depending on the variations
* in voltage and temperature.
*
* (**) HSE_VALUE is a constant defined in stm32f1xx.h file (default value
* 8 MHz or 25 MHz, depending on the product used), user has to ensure
* that HSE_VALUE is same as the real frequency of the crystal used.
* Otherwise, this function may have wrong result.
*
* - The result of this function could be not correct when using fractional
* value for HSE crystal.
* @param None
* @retval None
*/
void SystemCoreClockUpdate (void)
{
uint32_t tmp = 0U, pllmull = 0U, pllsource = 0U;
#if defined(STM32F105xC) || defined(STM32F107xC)
uint32_t prediv1source = 0U, prediv1factor = 0U, prediv2factor = 0U, pll2mull = 0U;
#endif /* STM32F105xC */
#if defined(STM32F100xB) || defined(STM32F100xE)
uint32_t prediv1factor = 0U;
#endif /* STM32F100xB or STM32F100xE */
/* Get SYSCLK source -------------------------------------------------------*/
tmp = RCC->CFGR & RCC_CFGR_SWS;
switch (tmp)
{
case 0x00U: /* HSI used as system clock */
SystemCoreClock = HSI_VALUE;
break;
case 0x04U: /* HSE used as system clock */
SystemCoreClock = HSE_VALUE;
break;
case 0x08U: /* PLL used as system clock */
/* Get PLL clock source and multiplication factor ----------------------*/
pllmull = RCC->CFGR & RCC_CFGR_PLLMULL;
pllsource = RCC->CFGR & RCC_CFGR_PLLSRC;
#if !defined(STM32F105xC) && !defined(STM32F107xC)
pllmull = ( pllmull >> 18U) + 2U;
if (pllsource == 0x00U)
{
/* HSI oscillator clock divided by 2 selected as PLL clock entry */
SystemCoreClock = (HSI_VALUE >> 1U) * pllmull;
}
else
{
#if defined(STM32F100xB) || defined(STM32F100xE)
prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1U;
/* HSE oscillator clock selected as PREDIV1 clock entry */
SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull;
#else
/* HSE selected as PLL clock entry */
if ((RCC->CFGR & RCC_CFGR_PLLXTPRE) != (uint32_t)RESET)
{/* HSE oscillator clock divided by 2 */
SystemCoreClock = (HSE_VALUE >> 1U) * pllmull;
}
else
{
SystemCoreClock = HSE_VALUE * pllmull;
}
#endif
}
#else
pllmull = pllmull >> 18U;
if (pllmull != 0x0DU)
{
pllmull += 2U;
}
else
{ /* PLL multiplication factor = PLL input clock * 6.5 */
pllmull = 13U / 2U;
}
if (pllsource == 0x00U)
{
/* HSI oscillator clock divided by 2 selected as PLL clock entry */
SystemCoreClock = (HSI_VALUE >> 1U) * pllmull;
}
else
{/* PREDIV1 selected as PLL clock entry */
/* Get PREDIV1 clock source and division factor */
prediv1source = RCC->CFGR2 & RCC_CFGR2_PREDIV1SRC;
prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1U;
if (prediv1source == 0U)
{
/* HSE oscillator clock selected as PREDIV1 clock entry */
SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull;
}
else
{/* PLL2 clock selected as PREDIV1 clock entry */
/* Get PREDIV2 division factor and PLL2 multiplication factor */
prediv2factor = ((RCC->CFGR2 & RCC_CFGR2_PREDIV2) >> 4U) + 1U;
pll2mull = ((RCC->CFGR2 & RCC_CFGR2_PLL2MUL) >> 8U) + 2U;
SystemCoreClock = (((HSE_VALUE / prediv2factor) * pll2mull) / prediv1factor) * pllmull;
}
}
#endif /* STM32F105xC */
break;
default:
SystemCoreClock = HSI_VALUE;
break;
}
/* Compute HCLK clock frequency ----------------*/
/* Get HCLK prescaler */
tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4U)];
/* HCLK clock frequency */
SystemCoreClock >>= tmp;
}
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
/**
* @brief Setup the external memory controller. Called in startup_stm32f1xx.s
* before jump to __main
* @param None
* @retval None
*/
#ifdef DATA_IN_ExtSRAM
/**
* @brief Setup the external memory controller.
* Called in startup_stm32f1xx_xx.s/.c before jump to main.
* This function configures the external SRAM mounted on STM3210E-EVAL
* board (STM32 High density devices). This SRAM will be used as program
* data memory (including heap and stack).
* @param None
* @retval None
*/
void SystemInit_ExtMemCtl(void)
{
__IO uint32_t tmpreg;
/*!< FSMC Bank1 NOR/SRAM3 is used for the STM3210E-EVAL, if another Bank is
required, then adjust the Register Addresses */
/* Enable FSMC clock */
RCC->AHBENR = 0x00000114U;
/* Delay after an RCC peripheral clock enabling */
tmpreg = READ_BIT(RCC->AHBENR, RCC_AHBENR_FSMCEN);
/* Enable GPIOD, GPIOE, GPIOF and GPIOG clocks */
RCC->APB2ENR = 0x000001E0U;
/* Delay after an RCC peripheral clock enabling */
tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPDEN);
(void)(tmpreg);
/* --------------- SRAM Data lines, NOE and NWE configuration ---------------*/
/*---------------- SRAM Address lines configuration -------------------------*/
/*---------------- NOE and NWE configuration --------------------------------*/
/*---------------- NE3 configuration ----------------------------------------*/
/*---------------- NBL0, NBL1 configuration ---------------------------------*/
GPIOD->CRL = 0x44BB44BBU;
GPIOD->CRH = 0xBBBBBBBBU;
GPIOE->CRL = 0xB44444BBU;
GPIOE->CRH = 0xBBBBBBBBU;
GPIOF->CRL = 0x44BBBBBBU;
GPIOF->CRH = 0xBBBB4444U;
GPIOG->CRL = 0x44BBBBBBU;
GPIOG->CRH = 0x444B4B44U;
/*---------------- FSMC Configuration ---------------------------------------*/
/*---------------- Enable FSMC Bank1_SRAM Bank ------------------------------*/
FSMC_Bank1->BTCR[4U] = 0x00001091U;
FSMC_Bank1->BTCR[5U] = 0x00110212U;
}
#endif /* DATA_IN_ExtSRAM */
#endif /* STM32F100xE || STM32F101xE || STM32F101xG || STM32F103xE || STM32F103xG */
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/