tnc3-firmware/TNC/LEDIndicator.cpp

580 wiersze
11 KiB
C++

// Copyright 2017 Rob Riggs <rob@mobilinkd.com>
// All rights reserved.
#include "LEDIndicator.h"
#include "main.h"
#include <stm32l4xx_hal.h>
#include <stm32l4xx_hal_tim.h>
#include <stm32l4xx_hal_tim_ex.h>
#include <cmsis_os.h>
#include <functional>
#include <atomic>
#include <stdint.h>
extern TIM_HandleTypeDef htim1;
namespace mobilinkd {
namespace tnc {
/**
* No connection shows a low, slow breathing. Each breath inhale takes
* for 500ms, is held for 500ms, and exhaled in 500ms. This is repeated
* every 10 seconds. Maximum brightness is 20%.
*
* Each interrupt occurs at 10ms intervals.
*
* The sequence is:
* - ramp up 300ms(30)
* - hold 400ms (40)
* - ramp down 300ms (30)
* - wait 9000ms (900)
*
*
*/
struct NoConnection
{
enum STATE
{
RAMP_UP_1, WAIT_1, RAMP_DN_1, WAIT_2
};
int count { 0 };
int state { RAMP_UP_1 };
int operator()()
{
int result;
switch (state) {
case RAMP_UP_1:
result = count * 40;
if (count == 49)
{
count = 0;
state = WAIT_1;
}
else
{
++count;
}
break;
case WAIT_1:
result = 2000;
if (count == 49)
{
state = RAMP_DN_1;
count = 49;
}
else
{
++count;
}
break;
case RAMP_DN_1:
result = count * 40;
if (count == 0)
{
count = 0;
state = WAIT_2;
}
else
{
--count;
}
break;
case WAIT_2:
result = 0;
if (count == 849)
{
state = RAMP_UP_1;
count = 0;
}
else
{
++count;
}
break;
}
return result;
}
};
/**
* Bluetooth connection shows a double blip. Each blip lasts for 200ms
* and is separated by 200ms, and is repeated ever 5 seconds.
*
* Each interrupt occurs at 10ms intervals.
*
* The sequence is:
* - ramp up 100s(10)
* - ramp down 100ms (10)
* - wait 200ms (20)
* - ramp up 100ms (10)
* - ramp down 100ms (10)
* - wait 4400ms (440)
*
*
*/
struct BluetoothConnection
{
enum STATE
{
RAMP_UP_1, RAMP_DN_1, WAIT_1, RAMP_UP_2, RAMP_DN_2, WAIT_2
};
int count { 0 };
int pulse { 0 };
int state { RAMP_UP_1 };
int ramp[10] =
{ 1564, 3090, 4540, 5878, 7071, 8090, 8910, 9510, 9877, 9999 };
int operator()()
{
int result;
switch (state) {
case RAMP_UP_1:
result = ramp[count] / 2;
if (count == 9)
{
state = RAMP_DN_1;
}
else
{
++count;
}
break;
case RAMP_DN_1:
result = ramp[count] / 2;
if (count == 0)
{
state = WAIT_1;
}
else
{
--count;
}
break;
case WAIT_1:
result = 0;
if (count == 19)
{
state = RAMP_UP_2;
count = 0;
}
else
{
++count;
}
break;
case RAMP_UP_2:
result = ramp[count] / 2;
if (count == 9)
{
state = RAMP_DN_2;
}
else
{
++count;
}
break;
case RAMP_DN_2:
result = ramp[count] / 2;
if (count == 0)
{
state = WAIT_2;
}
else
{
--count;
}
break;
case WAIT_2:
result = 0;
if (count == 439)
{
state = RAMP_UP_1;
count = 0;
}
else
{
++count;
}
break;
}
return result;
}
};
/**
* USB connection shows a triple blip. Each blip lasts for 200ms. The
* first two are separated by 400ms. The third comes 200ms later. This
* is repeated ever 5 seconds.
*
* Each interrupt occurs at 10ms intervals.
*
* The sequence is:
* - ramp up 100s(10)
* - ramp down 100ms (10)
* - wait 200ms (20)
* - ramp up 100s(10)
* - ramp down 100ms (10)
* - wait 400ms (20)
* - ramp up 100ms (10)
* - ramp down 100ms (10)
* - wait 3800ms (440)
*
*
*/
struct USBConnection
{
enum STATE
{
RAMP_UP_1,
RAMP_DN_1,
WAIT_1,
RAMP_UP_2,
RAMP_DN_2,
WAIT_2,
RAMP_UP_3,
RAMP_DN_3,
WAIT_3
};
int count { 0 };
int pulse { 0 };
int state { RAMP_UP_1 };
int ramp[10] =
{ 1564, 3090, 4540, 5878, 7071, 8090, 8910, 9510, 9877, 9999 };
int operator()()
{
int result;
switch (state) {
case RAMP_UP_1:
result = ramp[count];
if (count == 9)
{
state = RAMP_DN_1;
}
else
{
++count;
}
break;
case RAMP_DN_1:
result = ramp[count];
if (count == 0)
{
state = WAIT_1;
}
else
{
--count;
}
break;
case WAIT_1:
result = 0;
if (count == 39)
{
state = RAMP_UP_2;
count = 0;
}
else
{
++count;
}
break;
case RAMP_UP_2:
result = ramp[count];
if (count == 9)
{
state = RAMP_DN_2;
}
else
{
++count;
}
break;
case RAMP_DN_2:
result = ramp[count];
if (count == 0)
{
state = WAIT_2;
}
else
{
--count;
}
break;
case WAIT_2:
result = 0;
if (count == 19)
{
state = RAMP_UP_3;
count = 0;
}
else
{
++count;
}
break;
case RAMP_UP_3:
result = ramp[count];
if (count == 9)
{
state = RAMP_DN_3;
}
else
{
++count;
}
break;
case RAMP_DN_3:
result = ramp[count];
if (count == 0)
{
state = WAIT_3;
}
else
{
--count;
}
break;
case WAIT_3:
result = 0;
if (count == 379)
{
state = RAMP_UP_1;
count = 0;
}
else
{
++count;
}
break;
}
return result;
}
};
struct Flash
{
enum class STATE
{
RAMP_UP, ON, RAMP_DN, OFF
};
typedef std::atomic<STATE> state_type;
typedef std::function<int(void)> function_type;
constexpr static const int ramp[10] =
{ 1564, 3090, 4540, 5878, 7071, 8090, 8910, 9510, 9877, 9999 };
constexpr static const uint32_t BLUE_CHANNEL = TIM_CHANNEL_1;
constexpr static const uint32_t GREEN_CHANNEL = TIM_CHANNEL_2;
constexpr static const uint32_t RED_CHANNEL = TIM_CHANNEL_3;
int gr_count { 9 };
state_type gr_state { STATE::OFF };
int rd_count { 9 };
state_type rd_state { STATE::OFF };
NoConnection noConnection;
BluetoothConnection btConnection;
USBConnection usbConnection;
function_type blue_func { noConnection };
int blue()
{
return blue_func();
}
int plain(state_type& state, int& counter, uint32_t channel)
{
int result = 0;
switch (state) {
case STATE::RAMP_UP:
result = ramp[rd_count] / 3;
if (counter == 9)
{
state = STATE::ON;
}
else
{
++counter;
}
break;
case STATE::ON:
result = ramp[counter] / 3;
break;
case STATE::RAMP_DN:
result = ramp[counter] / 3;
if (counter == 0)
{
state = STATE::OFF;
HAL_TIM_PWM_Stop(&htim1, channel);
}
else
{
--counter;
}
break;
case STATE::OFF:
result = 0;
break;
}
return result;
}
int green()
{
return plain(gr_state, gr_count, GREEN_CHANNEL);
}
int red()
{
return plain(rd_state, rd_count, RED_CHANNEL);
}
void dcd_on()
{
auto expected = STATE::OFF;
if (gr_state.compare_exchange_strong(expected, STATE::RAMP_UP))
{
HAL_TIM_PWM_Start(&htim1, GREEN_CHANNEL);
}
else
{
gr_state = STATE::RAMP_UP;
}
}
void dcd_off()
{
if (gr_state != STATE::OFF)
gr_state = STATE::RAMP_DN;
}
void tx_on()
{
auto expected = STATE::OFF;
if (rd_state.compare_exchange_strong(expected, STATE::RAMP_UP))
{
// PWM Channel must match
HAL_TIM_PWM_Start(&htim1, RED_CHANNEL);
}
else
{
rd_state = STATE::RAMP_UP;
}
}
void tx_off()
{
if (rd_state != STATE::OFF)
rd_state = STATE::RAMP_DN;
}
void disconnect()
{
blue_func = noConnection;
HAL_TIM_PWM_Start(&htim1, BLUE_CHANNEL);
}
void usb()
{
blue_func = usbConnection;
HAL_TIM_PWM_Start(&htim1, BLUE_CHANNEL);
}
void bt()
{
blue_func = btConnection;
HAL_TIM_PWM_Start(&htim1, BLUE_CHANNEL);
}
};
Flash& flash()
{
static Flash blinker;
return blinker;
}
}
} // mobilinkd::tnc
void HTIM1_PeriodElapsedCallback()
{
using mobilinkd::tnc::flash;
// CCR registers must match the TIM_CHANNEL used for each LED in Flash.
htim1.Instance->CCR1 = flash().blue();
htim1.Instance->CCR2 = flash().green();
htim1.Instance->CCR3 = flash().red();
}
void indicate_turning_on(void)
{
HAL_TIM_Base_Start_IT(&htim1);
tx_on();
rx_on();
}
void indicate_initializing_ble(void)
{
tx_off();
}
void indicate_on()
{
tx_off();
rx_off();
}
void indicate_waiting_to_connect(void)
{
mobilinkd::tnc::flash().disconnect();
}
void indicate_connected_via_usb(void)
{
mobilinkd::tnc::flash().usb();
}
void indicate_connected_via_ble(void)
{
mobilinkd::tnc::flash().bt();
}
void tx_on(void)
{
mobilinkd::tnc::flash().tx_on();
}
void tx_off(void)
{
mobilinkd::tnc::flash().tx_off();
}
// DCD is active.
void rx_on()
{
mobilinkd::tnc::flash().dcd_on();
}
// DCD is active.
void rx_off()
{
mobilinkd::tnc::flash().dcd_off();
}