Add interrupts for samd changes
This commit is contained in:
@@ -496,7 +496,7 @@ int CmdSamd(int argc, char** argv) {
|
||||
|
||||
AppConsole::sSamd->ResetToFlashSamd();
|
||||
} else if (cmd == "charge") {
|
||||
auto res = AppConsole::sSamd->ReadChargeStatus();
|
||||
auto res = AppConsole::sSamd->GetChargeStatus();
|
||||
if (res) {
|
||||
switch (res.value()) {
|
||||
case drivers::Samd::ChargeStatus::kNoBattery:
|
||||
|
||||
@@ -58,7 +58,9 @@ constexpr std::pair<uint8_t, uint8_t> unpack(uint16_t ba) {
|
||||
return std::pair((uint8_t)ba, (uint8_t)(ba >> 8));
|
||||
}
|
||||
|
||||
void interrupt_isr(void* arg) {
|
||||
SemaphoreHandle_t Gpios::sReadPending;
|
||||
|
||||
static void interrupt_isr(void* arg) {
|
||||
SemaphoreHandle_t sem = reinterpret_cast<SemaphoreHandle_t>(arg);
|
||||
xSemaphoreGive(sem);
|
||||
}
|
||||
@@ -73,10 +75,7 @@ auto Gpios::Create() -> Gpios* {
|
||||
return instance;
|
||||
}
|
||||
|
||||
Gpios::Gpios()
|
||||
: ports_(pack(kPortADefault, kPortBDefault)),
|
||||
inputs_(0),
|
||||
read_pending_(xSemaphoreCreateBinary()) {
|
||||
Gpios::Gpios() : ports_(pack(kPortADefault, kPortBDefault)), inputs_(0) {
|
||||
gpio_config_t config{
|
||||
.pin_bit_mask = static_cast<uint64_t>(1) << GPIO_NUM_34,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
@@ -87,6 +86,7 @@ Gpios::Gpios()
|
||||
gpio_config(&config);
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED |
|
||||
ESP_INTR_FLAG_IRAM);
|
||||
gpio_isr_handler_add(GPIO_NUM_34, &interrupt_isr, sReadPending);
|
||||
}
|
||||
|
||||
Gpios::~Gpios() {
|
||||
@@ -141,8 +141,9 @@ auto Gpios::Read() -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Gpios::InstallReadPendingISR() -> void {
|
||||
gpio_isr_handler_add(GPIO_NUM_34, &interrupt_isr, read_pending_);
|
||||
auto Gpios::CreateReadPending() -> SemaphoreHandle_t {
|
||||
sReadPending = xSemaphoreCreateBinary();
|
||||
return sReadPending;
|
||||
}
|
||||
|
||||
} // namespace drivers
|
||||
|
||||
@@ -107,8 +107,7 @@ class Gpios : public IGpios {
|
||||
*/
|
||||
auto Read(void) -> bool;
|
||||
|
||||
auto InstallReadPendingISR() -> void;
|
||||
auto IsReadPending() -> SemaphoreHandle_t { return read_pending_; }
|
||||
static auto CreateReadPending() -> SemaphoreHandle_t;
|
||||
|
||||
// Not copyable or movable. There should usually only ever be once instance
|
||||
// of this class, and that instance will likely have a static lifetime.
|
||||
@@ -121,7 +120,7 @@ class Gpios : public IGpios {
|
||||
std::atomic<uint16_t> ports_;
|
||||
std::atomic<uint16_t> inputs_;
|
||||
|
||||
SemaphoreHandle_t read_pending_;
|
||||
static SemaphoreHandle_t sReadPending;
|
||||
};
|
||||
|
||||
} // namespace drivers
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
namespace drivers {
|
||||
|
||||
class Samd {
|
||||
@@ -32,7 +35,8 @@ class Samd {
|
||||
kFullCharge,
|
||||
};
|
||||
|
||||
auto ReadChargeStatus() -> std::optional<ChargeStatus>;
|
||||
auto GetChargeStatus() -> std::optional<ChargeStatus>;
|
||||
auto UpdateChargeStatus() -> void;
|
||||
|
||||
enum class UsbStatus {
|
||||
// There is no compatible usb host attached.
|
||||
@@ -44,9 +48,23 @@ class Samd {
|
||||
kAttachedMounted,
|
||||
};
|
||||
|
||||
auto ReadUsbStatus() -> UsbStatus;
|
||||
auto GetUsbStatus() -> UsbStatus;
|
||||
auto UpdateUsbStatus() -> void;
|
||||
|
||||
auto ResetToFlashSamd() -> void;
|
||||
|
||||
static auto CreateReadPending() -> SemaphoreHandle_t;
|
||||
|
||||
// Not copyable or movable. There should usually only ever be once instance
|
||||
// of this class, and that instance will likely have a static lifetime.
|
||||
Samd(const Samd&) = delete;
|
||||
Samd& operator=(const Samd&) = delete;
|
||||
|
||||
private:
|
||||
std::optional<ChargeStatus> charge_status_;
|
||||
UsbStatus usb_status_;
|
||||
|
||||
static SemaphoreHandle_t sReadPending;
|
||||
};
|
||||
|
||||
} // namespace drivers
|
||||
|
||||
+51
-16
@@ -26,7 +26,24 @@ static const char kTag[] = "SAMD";
|
||||
|
||||
namespace drivers {
|
||||
|
||||
SemaphoreHandle_t Samd::sReadPending;
|
||||
|
||||
static void interrupt_isr(void* arg) {
|
||||
SemaphoreHandle_t sem = reinterpret_cast<SemaphoreHandle_t>(arg);
|
||||
xSemaphoreGive(sem);
|
||||
}
|
||||
|
||||
Samd::Samd() {
|
||||
gpio_config_t config{
|
||||
.pin_bit_mask = static_cast<uint64_t>(1) << GPIO_NUM_35,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_NEGEDGE,
|
||||
};
|
||||
gpio_config(&config);
|
||||
gpio_isr_handler_add(GPIO_NUM_35, &interrupt_isr, sReadPending);
|
||||
|
||||
// Being able to interface with the SAMD properly is critical. To ensure we
|
||||
// will be able to, we begin by checking the I2C protocol version is
|
||||
// compatible, and throw if it's not.
|
||||
@@ -41,10 +58,17 @@ Samd::Samd() {
|
||||
.stop();
|
||||
ESP_ERROR_CHECK(transaction.Execute());
|
||||
ESP_LOGI(kTag, "samd firmware rev: %u", raw_res);
|
||||
|
||||
UpdateChargeStatus();
|
||||
UpdateUsbStatus();
|
||||
}
|
||||
Samd::~Samd() {}
|
||||
|
||||
auto Samd::ReadChargeStatus() -> std::optional<ChargeStatus> {
|
||||
auto Samd::GetChargeStatus() -> std::optional<ChargeStatus> {
|
||||
return charge_status_;
|
||||
}
|
||||
|
||||
auto Samd::UpdateChargeStatus() -> void {
|
||||
uint8_t raw_res;
|
||||
I2CTransaction transaction;
|
||||
transaction.start()
|
||||
@@ -54,31 +78,39 @@ auto Samd::ReadChargeStatus() -> std::optional<ChargeStatus> {
|
||||
.write_addr(kAddress, I2C_MASTER_READ)
|
||||
.read(&raw_res, I2C_MASTER_NACK)
|
||||
.stop();
|
||||
ESP_LOGI(kTag, "checking charge status");
|
||||
ESP_ERROR_CHECK(transaction.Execute());
|
||||
ESP_LOGI(kTag, "raw charge status: 0x%x", raw_res);
|
||||
|
||||
uint8_t usb_state = raw_res & 0b11;
|
||||
uint8_t charge_state = (raw_res >> 2) & 0b111;
|
||||
switch (charge_state) {
|
||||
case 0b000:
|
||||
case 0b011:
|
||||
return ChargeStatus::kNoBattery;
|
||||
charge_status_ = ChargeStatus::kNoBattery;
|
||||
break;
|
||||
case 0b001:
|
||||
return usb_state == 1 ? ChargeStatus::kChargingRegular
|
||||
: ChargeStatus::kChargingFast;
|
||||
charge_status_ = usb_state == 1 ? ChargeStatus::kChargingRegular
|
||||
: ChargeStatus::kChargingFast;
|
||||
break;
|
||||
case 0b010:
|
||||
return ChargeStatus::kFullCharge;
|
||||
charge_status_ = ChargeStatus::kFullCharge;
|
||||
break;
|
||||
case 0b100:
|
||||
return ChargeStatus::kBatteryCritical;
|
||||
charge_status_ = ChargeStatus::kBatteryCritical;
|
||||
break;
|
||||
case 0b101:
|
||||
return ChargeStatus::kDischarging;
|
||||
charge_status_ = ChargeStatus::kDischarging;
|
||||
break;
|
||||
default:
|
||||
return {};
|
||||
charge_status_ = {};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto Samd::ReadUsbStatus() -> UsbStatus {
|
||||
auto Samd::GetUsbStatus() -> UsbStatus {
|
||||
return usb_status_;
|
||||
}
|
||||
|
||||
auto Samd::UpdateUsbStatus() -> void {
|
||||
uint8_t raw_res;
|
||||
I2CTransaction transaction;
|
||||
transaction.start()
|
||||
@@ -88,15 +120,13 @@ auto Samd::ReadUsbStatus() -> UsbStatus {
|
||||
.write_addr(kAddress, I2C_MASTER_READ)
|
||||
.read(&raw_res, I2C_MASTER_NACK)
|
||||
.stop();
|
||||
ESP_LOGI(kTag, "checking usb status");
|
||||
ESP_ERROR_CHECK(transaction.Execute());
|
||||
ESP_LOGI(kTag, "raw usb status: 0x%x", raw_res);
|
||||
|
||||
if (!(raw_res & 0b1)) {
|
||||
return UsbStatus::kDetached;
|
||||
usb_status_ = UsbStatus::kDetached;
|
||||
}
|
||||
return (raw_res & 0b10) ? UsbStatus::kAttachedMounted
|
||||
: UsbStatus::kAttachedIdle;
|
||||
usb_status_ =
|
||||
(raw_res & 0b10) ? UsbStatus::kAttachedMounted : UsbStatus::kAttachedIdle;
|
||||
}
|
||||
|
||||
auto Samd::ResetToFlashSamd() -> void {
|
||||
@@ -108,4 +138,9 @@ auto Samd::ResetToFlashSamd() -> void {
|
||||
ESP_ERROR_CHECK(transaction.Execute());
|
||||
}
|
||||
|
||||
auto Samd::CreateReadPending() -> SemaphoreHandle_t {
|
||||
sReadPending = xSemaphoreCreateBinary();
|
||||
return sReadPending;
|
||||
}
|
||||
|
||||
} // namespace drivers
|
||||
|
||||
+8
-3
@@ -18,7 +18,8 @@
|
||||
|
||||
extern "C" void app_main(void) {
|
||||
ESP_ERROR_CHECK(drivers::init_i2c());
|
||||
drivers::Gpios* gpios = system_fsm::SystemState::early_init_gpios();
|
||||
SemaphoreHandle_t gpios_semphr = drivers::Gpios::CreateReadPending();
|
||||
SemaphoreHandle_t samd_semphr = drivers::Samd::CreateReadPending();
|
||||
|
||||
// Semaphores must be empty before being added to a queue set. Hence all this
|
||||
// weird early init stuff; by being explicit about initialisation order, we're
|
||||
@@ -27,7 +28,8 @@ extern "C" void app_main(void) {
|
||||
QueueSetHandle_t set = xQueueCreateSet(2);
|
||||
auto* event_queue = events::queues::SystemAndAudio();
|
||||
xQueueAddToSet(event_queue->has_events(), set);
|
||||
xQueueAddToSet(gpios->IsReadPending(), set);
|
||||
xQueueAddToSet(gpios_semphr, set);
|
||||
xQueueAddToSet(samd_semphr, set);
|
||||
|
||||
tinyfsm::FsmList<system_fsm::SystemState, ui::UiState,
|
||||
audio::AudioState>::start();
|
||||
@@ -36,9 +38,12 @@ extern "C" void app_main(void) {
|
||||
QueueSetMemberHandle_t member = xQueueSelectFromSet(set, portMAX_DELAY);
|
||||
if (member == event_queue->has_events()) {
|
||||
event_queue->Service(0);
|
||||
} else if (member == gpios->IsReadPending()) {
|
||||
} else if (member == gpios_semphr) {
|
||||
xSemaphoreTake(member, 0);
|
||||
events::System().Dispatch(system_fsm::internal::GpioInterrupt{});
|
||||
} else if (member == samd_semphr) {
|
||||
xSemaphoreTake(member, 0);
|
||||
events::System().Dispatch(system_fsm::internal::SamdInterrupt{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ auto Booting::entry() -> void {
|
||||
// I2C and SPI are both always needed. We can't even power down or show an
|
||||
// error without these.
|
||||
ESP_ERROR_CHECK(drivers::init_spi());
|
||||
sGpios->InstallReadPendingISR();
|
||||
sGpios.reset(drivers::Gpios::Create());
|
||||
|
||||
// Start bringing up LVGL now, since we have all of its prerequisites.
|
||||
sTrackQueue.reset(new audio::TrackQueue());
|
||||
@@ -97,7 +97,7 @@ auto Booting::react(const BootComplete& ev) -> void {
|
||||
// It's possible that the SAMD is currently exposing the SD card as a USB
|
||||
// device. Make sure we don't immediately try to claim it.
|
||||
if (sSamd &&
|
||||
sSamd->ReadUsbStatus() == drivers::Samd::UsbStatus::kAttachedMounted) {
|
||||
sSamd->GetUsbStatus() == drivers::Samd::UsbStatus::kAttachedMounted) {
|
||||
transit<Unmounted>();
|
||||
}
|
||||
transit<Running>();
|
||||
|
||||
@@ -60,6 +60,8 @@ struct HasPhonesChanged : tinyfsm::Event {
|
||||
bool falling;
|
||||
};
|
||||
|
||||
struct ChargingStatusChanged : tinyfsm::Event {};
|
||||
|
||||
namespace internal {
|
||||
|
||||
/*
|
||||
@@ -68,11 +70,8 @@ namespace internal {
|
||||
*/
|
||||
struct ReadyToUnmount : tinyfsm::Event {};
|
||||
|
||||
/*
|
||||
* Sent when the actual unmount operation should be performed. Always dispatched
|
||||
* by SysState in response to StoragePrepareToUnmount.
|
||||
*/
|
||||
struct GpioInterrupt : tinyfsm::Event {};
|
||||
struct SamdInterrupt : tinyfsm::Event {};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
|
||||
@@ -35,8 +35,6 @@ class SystemState : public tinyfsm::Fsm<SystemState> {
|
||||
public:
|
||||
virtual ~SystemState() {}
|
||||
|
||||
static auto early_init_gpios() -> drivers::Gpios*;
|
||||
|
||||
virtual void entry() {}
|
||||
virtual void exit() {}
|
||||
|
||||
@@ -45,6 +43,7 @@ class SystemState : public tinyfsm::Fsm<SystemState> {
|
||||
|
||||
void react(const FatalError&);
|
||||
void react(const internal::GpioInterrupt&);
|
||||
void react(const internal::SamdInterrupt&);
|
||||
|
||||
virtual void react(const DisplayReady&) {}
|
||||
virtual void react(const BootComplete&) {}
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "tag_parser.hpp"
|
||||
#include "track_queue.hpp"
|
||||
|
||||
static const char kTag[] = "system";
|
||||
|
||||
namespace system_fsm {
|
||||
|
||||
std::shared_ptr<drivers::Gpios> SystemState::sGpios;
|
||||
@@ -33,11 +35,6 @@ std::shared_ptr<audio::TrackQueue> SystemState::sTrackQueue;
|
||||
|
||||
console::AppConsole* SystemState::sAppConsole;
|
||||
|
||||
auto SystemState::early_init_gpios() -> drivers::Gpios* {
|
||||
sGpios.reset(drivers::Gpios::Create());
|
||||
return sGpios.get();
|
||||
}
|
||||
|
||||
void SystemState::react(const FatalError& err) {
|
||||
if (!is_in_state<states::Error>()) {
|
||||
transit<states::Error>();
|
||||
@@ -78,6 +75,26 @@ void SystemState::react(const internal::GpioInterrupt&) {
|
||||
}
|
||||
}
|
||||
|
||||
void SystemState::react(const internal::SamdInterrupt&) {
|
||||
auto prev_charge_status = sSamd->GetChargeStatus();
|
||||
auto prev_usb_status = sSamd->GetUsbStatus();
|
||||
|
||||
sSamd->UpdateChargeStatus();
|
||||
sSamd->UpdateUsbStatus();
|
||||
|
||||
auto charge_status = sSamd->GetChargeStatus();
|
||||
auto usb_status = sSamd->GetUsbStatus();
|
||||
|
||||
if (charge_status != prev_charge_status) {
|
||||
ChargingStatusChanged ev{};
|
||||
events::System().Dispatch(ev);
|
||||
events::Ui().Dispatch(ev);
|
||||
}
|
||||
if (usb_status != prev_usb_status) {
|
||||
ESP_LOGI(kTag, "usb status changed");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace system_fsm
|
||||
|
||||
FSM_INITIAL_STATE(system_fsm::SystemState, system_fsm::states::Booting)
|
||||
|
||||
Reference in New Issue
Block a user