Add interrupts for samd changes

custom
jacqueline 2 years ago
parent 697ec3c584
commit cad7060540
  1. 2
      src/app_console/app_console.cpp
  2. 15
      src/drivers/gpios.cpp
  3. 5
      src/drivers/include/gpios.hpp
  4. 22
      src/drivers/include/samd.hpp
  5. 65
      src/drivers/samd.cpp
  6. 11
      src/main/main.cpp
  7. 4
      src/system_fsm/booting.cpp
  8. 7
      src/system_fsm/include/system_events.hpp
  9. 3
      src/system_fsm/include/system_fsm.hpp
  10. 27
      src/system_fsm/system_fsm.cpp

@ -496,7 +496,7 @@ int CmdSamd(int argc, char** argv) {
AppConsole::sSamd->ResetToFlashSamd(); AppConsole::sSamd->ResetToFlashSamd();
} else if (cmd == "charge") { } else if (cmd == "charge") {
auto res = AppConsole::sSamd->ReadChargeStatus(); auto res = AppConsole::sSamd->GetChargeStatus();
if (res) { if (res) {
switch (res.value()) { switch (res.value()) {
case drivers::Samd::ChargeStatus::kNoBattery: 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)); 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); SemaphoreHandle_t sem = reinterpret_cast<SemaphoreHandle_t>(arg);
xSemaphoreGive(sem); xSemaphoreGive(sem);
} }
@ -73,10 +75,7 @@ auto Gpios::Create() -> Gpios* {
return instance; return instance;
} }
Gpios::Gpios() Gpios::Gpios() : ports_(pack(kPortADefault, kPortBDefault)), inputs_(0) {
: ports_(pack(kPortADefault, kPortBDefault)),
inputs_(0),
read_pending_(xSemaphoreCreateBinary()) {
gpio_config_t config{ gpio_config_t config{
.pin_bit_mask = static_cast<uint64_t>(1) << GPIO_NUM_34, .pin_bit_mask = static_cast<uint64_t>(1) << GPIO_NUM_34,
.mode = GPIO_MODE_INPUT, .mode = GPIO_MODE_INPUT,
@ -87,6 +86,7 @@ Gpios::Gpios()
gpio_config(&config); gpio_config(&config);
gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED |
ESP_INTR_FLAG_IRAM); ESP_INTR_FLAG_IRAM);
gpio_isr_handler_add(GPIO_NUM_34, &interrupt_isr, sReadPending);
} }
Gpios::~Gpios() { Gpios::~Gpios() {
@ -141,8 +141,9 @@ auto Gpios::Read() -> bool {
return true; return true;
} }
auto Gpios::InstallReadPendingISR() -> void { auto Gpios::CreateReadPending() -> SemaphoreHandle_t {
gpio_isr_handler_add(GPIO_NUM_34, &interrupt_isr, read_pending_); sReadPending = xSemaphoreCreateBinary();
return sReadPending;
} }
} // namespace drivers } // namespace drivers

@ -107,8 +107,7 @@ class Gpios : public IGpios {
*/ */
auto Read(void) -> bool; auto Read(void) -> bool;
auto InstallReadPendingISR() -> void; static auto CreateReadPending() -> SemaphoreHandle_t;
auto IsReadPending() -> SemaphoreHandle_t { return read_pending_; }
// Not copyable or movable. There should usually only ever be once instance // Not copyable or movable. There should usually only ever be once instance
// of this class, and that instance will likely have a static lifetime. // 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> ports_;
std::atomic<uint16_t> inputs_; std::atomic<uint16_t> inputs_;
SemaphoreHandle_t read_pending_; static SemaphoreHandle_t sReadPending;
}; };
} // namespace drivers } // namespace drivers

@ -8,6 +8,9 @@
#include <optional> #include <optional>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
namespace drivers { namespace drivers {
class Samd { class Samd {
@ -32,7 +35,8 @@ class Samd {
kFullCharge, kFullCharge,
}; };
auto ReadChargeStatus() -> std::optional<ChargeStatus>; auto GetChargeStatus() -> std::optional<ChargeStatus>;
auto UpdateChargeStatus() -> void;
enum class UsbStatus { enum class UsbStatus {
// There is no compatible usb host attached. // There is no compatible usb host attached.
@ -44,9 +48,23 @@ class Samd {
kAttachedMounted, kAttachedMounted,
}; };
auto ReadUsbStatus() -> UsbStatus; auto GetUsbStatus() -> UsbStatus;
auto UpdateUsbStatus() -> void;
auto ResetToFlashSamd() -> 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 } // namespace drivers

@ -26,7 +26,24 @@ static const char kTag[] = "SAMD";
namespace drivers { namespace drivers {
SemaphoreHandle_t Samd::sReadPending;
static void interrupt_isr(void* arg) {
SemaphoreHandle_t sem = reinterpret_cast<SemaphoreHandle_t>(arg);
xSemaphoreGive(sem);
}
Samd::Samd() { 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 // 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 // will be able to, we begin by checking the I2C protocol version is
// compatible, and throw if it's not. // compatible, and throw if it's not.
@ -41,10 +58,17 @@ Samd::Samd() {
.stop(); .stop();
ESP_ERROR_CHECK(transaction.Execute()); ESP_ERROR_CHECK(transaction.Execute());
ESP_LOGI(kTag, "samd firmware rev: %u", raw_res); ESP_LOGI(kTag, "samd firmware rev: %u", raw_res);
UpdateChargeStatus();
UpdateUsbStatus();
} }
Samd::~Samd() {} 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; uint8_t raw_res;
I2CTransaction transaction; I2CTransaction transaction;
transaction.start() transaction.start()
@ -54,31 +78,39 @@ auto Samd::ReadChargeStatus() -> std::optional<ChargeStatus> {
.write_addr(kAddress, I2C_MASTER_READ) .write_addr(kAddress, I2C_MASTER_READ)
.read(&raw_res, I2C_MASTER_NACK) .read(&raw_res, I2C_MASTER_NACK)
.stop(); .stop();
ESP_LOGI(kTag, "checking charge status");
ESP_ERROR_CHECK(transaction.Execute()); ESP_ERROR_CHECK(transaction.Execute());
ESP_LOGI(kTag, "raw charge status: 0x%x", raw_res);
uint8_t usb_state = raw_res & 0b11; uint8_t usb_state = raw_res & 0b11;
uint8_t charge_state = (raw_res >> 2) & 0b111; uint8_t charge_state = (raw_res >> 2) & 0b111;
switch (charge_state) { switch (charge_state) {
case 0b000: case 0b000:
case 0b011: case 0b011:
return ChargeStatus::kNoBattery; charge_status_ = ChargeStatus::kNoBattery;
break;
case 0b001: case 0b001:
return usb_state == 1 ? ChargeStatus::kChargingRegular charge_status_ = usb_state == 1 ? ChargeStatus::kChargingRegular
: ChargeStatus::kChargingFast; : ChargeStatus::kChargingFast;
break;
case 0b010: case 0b010:
return ChargeStatus::kFullCharge; charge_status_ = ChargeStatus::kFullCharge;
break;
case 0b100: case 0b100:
return ChargeStatus::kBatteryCritical; charge_status_ = ChargeStatus::kBatteryCritical;
break;
case 0b101: case 0b101:
return ChargeStatus::kDischarging; charge_status_ = ChargeStatus::kDischarging;
break;
default: default:
return {}; charge_status_ = {};
break;
}
} }
auto Samd::GetUsbStatus() -> UsbStatus {
return usb_status_;
} }
auto Samd::ReadUsbStatus() -> UsbStatus { auto Samd::UpdateUsbStatus() -> void {
uint8_t raw_res; uint8_t raw_res;
I2CTransaction transaction; I2CTransaction transaction;
transaction.start() transaction.start()
@ -88,15 +120,13 @@ auto Samd::ReadUsbStatus() -> UsbStatus {
.write_addr(kAddress, I2C_MASTER_READ) .write_addr(kAddress, I2C_MASTER_READ)
.read(&raw_res, I2C_MASTER_NACK) .read(&raw_res, I2C_MASTER_NACK)
.stop(); .stop();
ESP_LOGI(kTag, "checking usb status");
ESP_ERROR_CHECK(transaction.Execute()); ESP_ERROR_CHECK(transaction.Execute());
ESP_LOGI(kTag, "raw usb status: 0x%x", raw_res);
if (!(raw_res & 0b1)) { if (!(raw_res & 0b1)) {
return UsbStatus::kDetached; usb_status_ = UsbStatus::kDetached;
} }
return (raw_res & 0b10) ? UsbStatus::kAttachedMounted usb_status_ =
: UsbStatus::kAttachedIdle; (raw_res & 0b10) ? UsbStatus::kAttachedMounted : UsbStatus::kAttachedIdle;
} }
auto Samd::ResetToFlashSamd() -> void { auto Samd::ResetToFlashSamd() -> void {
@ -108,4 +138,9 @@ auto Samd::ResetToFlashSamd() -> void {
ESP_ERROR_CHECK(transaction.Execute()); ESP_ERROR_CHECK(transaction.Execute());
} }
auto Samd::CreateReadPending() -> SemaphoreHandle_t {
sReadPending = xSemaphoreCreateBinary();
return sReadPending;
}
} // namespace drivers } // namespace drivers

@ -18,7 +18,8 @@
extern "C" void app_main(void) { extern "C" void app_main(void) {
ESP_ERROR_CHECK(drivers::init_i2c()); 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 // 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 // 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); QueueSetHandle_t set = xQueueCreateSet(2);
auto* event_queue = events::queues::SystemAndAudio(); auto* event_queue = events::queues::SystemAndAudio();
xQueueAddToSet(event_queue->has_events(), set); 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, tinyfsm::FsmList<system_fsm::SystemState, ui::UiState,
audio::AudioState>::start(); audio::AudioState>::start();
@ -36,9 +38,12 @@ extern "C" void app_main(void) {
QueueSetMemberHandle_t member = xQueueSelectFromSet(set, portMAX_DELAY); QueueSetMemberHandle_t member = xQueueSelectFromSet(set, portMAX_DELAY);
if (member == event_queue->has_events()) { if (member == event_queue->has_events()) {
event_queue->Service(0); event_queue->Service(0);
} else if (member == gpios->IsReadPending()) { } else if (member == gpios_semphr) {
xSemaphoreTake(member, 0); xSemaphoreTake(member, 0);
events::System().Dispatch(system_fsm::internal::GpioInterrupt{}); 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 // I2C and SPI are both always needed. We can't even power down or show an
// error without these. // error without these.
ESP_ERROR_CHECK(drivers::init_spi()); 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. // Start bringing up LVGL now, since we have all of its prerequisites.
sTrackQueue.reset(new audio::TrackQueue()); 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 // 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. // device. Make sure we don't immediately try to claim it.
if (sSamd && if (sSamd &&
sSamd->ReadUsbStatus() == drivers::Samd::UsbStatus::kAttachedMounted) { sSamd->GetUsbStatus() == drivers::Samd::UsbStatus::kAttachedMounted) {
transit<Unmounted>(); transit<Unmounted>();
} }
transit<Running>(); transit<Running>();

@ -60,6 +60,8 @@ struct HasPhonesChanged : tinyfsm::Event {
bool falling; bool falling;
}; };
struct ChargingStatusChanged : tinyfsm::Event {};
namespace internal { namespace internal {
/* /*
@ -68,11 +70,8 @@ namespace internal {
*/ */
struct ReadyToUnmount : tinyfsm::Event {}; 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 GpioInterrupt : tinyfsm::Event {};
struct SamdInterrupt : tinyfsm::Event {};
} // namespace internal } // namespace internal

@ -35,8 +35,6 @@ class SystemState : public tinyfsm::Fsm<SystemState> {
public: public:
virtual ~SystemState() {} virtual ~SystemState() {}
static auto early_init_gpios() -> drivers::Gpios*;
virtual void entry() {} virtual void entry() {}
virtual void exit() {} virtual void exit() {}
@ -45,6 +43,7 @@ class SystemState : public tinyfsm::Fsm<SystemState> {
void react(const FatalError&); void react(const FatalError&);
void react(const internal::GpioInterrupt&); void react(const internal::GpioInterrupt&);
void react(const internal::SamdInterrupt&);
virtual void react(const DisplayReady&) {} virtual void react(const DisplayReady&) {}
virtual void react(const BootComplete&) {} virtual void react(const BootComplete&) {}

@ -13,6 +13,8 @@
#include "tag_parser.hpp" #include "tag_parser.hpp"
#include "track_queue.hpp" #include "track_queue.hpp"
static const char kTag[] = "system";
namespace system_fsm { namespace system_fsm {
std::shared_ptr<drivers::Gpios> SystemState::sGpios; std::shared_ptr<drivers::Gpios> SystemState::sGpios;
@ -33,11 +35,6 @@ std::shared_ptr<audio::TrackQueue> SystemState::sTrackQueue;
console::AppConsole* SystemState::sAppConsole; 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) { void SystemState::react(const FatalError& err) {
if (!is_in_state<states::Error>()) { if (!is_in_state<states::Error>()) {
transit<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 } // namespace system_fsm
FSM_INITIAL_STATE(system_fsm::SystemState, system_fsm::states::Booting) FSM_INITIAL_STATE(system_fsm::SystemState, system_fsm::states::Booting)

Loading…
Cancel
Save