App accessor: init (#433)
This commit is contained in:
		
							parent
							
								
									dfcf0ea0eb
								
							
						
					
					
						commit
						c3350990c2
					
				
							
								
								
									
										176
									
								
								applications/accessor/accessor-app.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								applications/accessor/accessor-app.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,176 @@
 | 
			
		||||
#include "accessor-app.h"
 | 
			
		||||
#include <furi.h>
 | 
			
		||||
#include <api-hal.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
 | 
			
		||||
void AccessorApp::run(void) {
 | 
			
		||||
    AccessorEvent event;
 | 
			
		||||
    bool consumed;
 | 
			
		||||
    bool exit = false;
 | 
			
		||||
 | 
			
		||||
    notify_init();
 | 
			
		||||
 | 
			
		||||
    wiegand.begin();
 | 
			
		||||
    onewire_master.start();
 | 
			
		||||
 | 
			
		||||
    scenes[current_scene]->on_enter(this);
 | 
			
		||||
 | 
			
		||||
    while(!exit) {
 | 
			
		||||
        view.receive_event(&event);
 | 
			
		||||
 | 
			
		||||
        consumed = scenes[current_scene]->on_event(this, &event);
 | 
			
		||||
 | 
			
		||||
        if(!consumed) {
 | 
			
		||||
            if(event.type == AccessorEvent::Type::Back) {
 | 
			
		||||
                exit = switch_to_previous_scene();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    scenes[current_scene]->on_exit(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AccessorApp::AccessorApp()
 | 
			
		||||
    : onewire_master{&ibutton_gpio} {
 | 
			
		||||
    api_hal_power_insomnia_enter();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AccessorApp::~AccessorApp() {
 | 
			
		||||
    api_hal_power_insomnia_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AccessorAppViewManager* AccessorApp::get_view_manager() {
 | 
			
		||||
    return &view;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AccessorApp::switch_to_next_scene(Scene next_scene) {
 | 
			
		||||
    previous_scenes_list.push_front(current_scene);
 | 
			
		||||
 | 
			
		||||
    if(next_scene != Scene::Exit) {
 | 
			
		||||
        scenes[current_scene]->on_exit(this);
 | 
			
		||||
        current_scene = next_scene;
 | 
			
		||||
        scenes[current_scene]->on_enter(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AccessorApp::search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list) {
 | 
			
		||||
    Scene previous_scene = Scene::Start;
 | 
			
		||||
    bool scene_found = false;
 | 
			
		||||
 | 
			
		||||
    while(!scene_found) {
 | 
			
		||||
        previous_scene = get_previous_scene();
 | 
			
		||||
        for(Scene element : scenes_list) {
 | 
			
		||||
            if(previous_scene == element || previous_scene == Scene::Start) {
 | 
			
		||||
                scene_found = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    scenes[current_scene]->on_exit(this);
 | 
			
		||||
    current_scene = previous_scene;
 | 
			
		||||
    scenes[current_scene]->on_enter(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AccessorApp::switch_to_previous_scene(uint8_t count) {
 | 
			
		||||
    Scene previous_scene = Scene::Start;
 | 
			
		||||
 | 
			
		||||
    for(uint8_t i = 0; i < count; i++) {
 | 
			
		||||
        previous_scene = get_previous_scene();
 | 
			
		||||
        if(previous_scene == Scene::Exit) break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(previous_scene == Scene::Exit) {
 | 
			
		||||
        return true;
 | 
			
		||||
    } else {
 | 
			
		||||
        scenes[current_scene]->on_exit(this);
 | 
			
		||||
        current_scene = previous_scene;
 | 
			
		||||
        scenes[current_scene]->on_enter(this);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AccessorApp::Scene AccessorApp::get_previous_scene() {
 | 
			
		||||
    Scene scene = previous_scenes_list.front();
 | 
			
		||||
    previous_scenes_list.pop_front();
 | 
			
		||||
    return scene;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************** NOTIFY *******************************/
 | 
			
		||||
 | 
			
		||||
void AccessorApp::notify_init() {
 | 
			
		||||
    // TODO open record
 | 
			
		||||
    const GpioPin* vibro_record = &vibro_gpio;
 | 
			
		||||
    gpio_init(vibro_record, GpioModeOutputPushPull);
 | 
			
		||||
    gpio_write(vibro_record, false);
 | 
			
		||||
 | 
			
		||||
    GPIO_InitTypeDef GPIO_InitStruct = {0};
 | 
			
		||||
 | 
			
		||||
    GPIO_InitStruct.Pin = PB3_Pin;
 | 
			
		||||
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 | 
			
		||||
    GPIO_InitStruct.Pull = GPIO_NOPULL;
 | 
			
		||||
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
 | 
			
		||||
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
 | 
			
		||||
    HAL_GPIO_Init(PB3_GPIO_Port, &GPIO_InitStruct);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AccessorApp::notify_green_blink() {
 | 
			
		||||
    api_hal_light_set(LightGreen, 0xFF);
 | 
			
		||||
    delay(10);
 | 
			
		||||
    api_hal_light_set(LightGreen, 0x00);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AccessorApp::notify_green_on() {
 | 
			
		||||
    api_hal_light_set(LightGreen, 0xFF);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AccessorApp::notify_green_off() {
 | 
			
		||||
    api_hal_light_set(LightGreen, 0x00);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AccessorApp::notify_success() {
 | 
			
		||||
    api_hal_light_set(LightBacklight, 0xFF);
 | 
			
		||||
 | 
			
		||||
    hal_pwm_set(0.5, 1760 / 2, &htim2, TIM_CHANNEL_2);
 | 
			
		||||
    notify_green_on();
 | 
			
		||||
    delay(100);
 | 
			
		||||
    hal_pwm_stop(&htim2, TIM_CHANNEL_2);
 | 
			
		||||
    notify_green_off();
 | 
			
		||||
 | 
			
		||||
    delay(100);
 | 
			
		||||
 | 
			
		||||
    hal_pwm_set(0.5, 1760, &htim2, TIM_CHANNEL_2);
 | 
			
		||||
    notify_green_on();
 | 
			
		||||
    delay(100);
 | 
			
		||||
    hal_pwm_stop(&htim2, TIM_CHANNEL_2);
 | 
			
		||||
    notify_green_off();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*************************** TEXT STORE *****************************/
 | 
			
		||||
 | 
			
		||||
char* AccessorApp::get_text_store() {
 | 
			
		||||
    return text_store;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t AccessorApp::get_text_store_size() {
 | 
			
		||||
    return text_store_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AccessorApp::set_text_store(const char* text...) {
 | 
			
		||||
    va_list args;
 | 
			
		||||
    va_start(args, text);
 | 
			
		||||
 | 
			
		||||
    vsnprintf(text_store, text_store_size, text, args);
 | 
			
		||||
 | 
			
		||||
    va_end(args);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*************************** APP RESOURCES *****************************/
 | 
			
		||||
 | 
			
		||||
WIEGAND* AccessorApp::get_wiegand() {
 | 
			
		||||
    return &wiegand;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OneWireMaster* AccessorApp::get_one_wire() {
 | 
			
		||||
    return &onewire_master;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								applications/accessor/accessor-app.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								applications/accessor/accessor-app.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <list>
 | 
			
		||||
#include "accessor-view-manager.h"
 | 
			
		||||
 | 
			
		||||
#include "scene/accessor-scene-start.h"
 | 
			
		||||
 | 
			
		||||
#include "helpers/wiegand.h"
 | 
			
		||||
 | 
			
		||||
#include <one_wire_master.h>
 | 
			
		||||
 | 
			
		||||
class AccessorApp {
 | 
			
		||||
public:
 | 
			
		||||
    void run(void);
 | 
			
		||||
 | 
			
		||||
    AccessorApp();
 | 
			
		||||
    ~AccessorApp();
 | 
			
		||||
 | 
			
		||||
    enum class Scene : uint8_t {
 | 
			
		||||
        Exit,
 | 
			
		||||
        Start,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    AccessorAppViewManager* get_view_manager();
 | 
			
		||||
    void switch_to_next_scene(Scene index);
 | 
			
		||||
    void search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list);
 | 
			
		||||
    bool switch_to_previous_scene(uint8_t count = 1);
 | 
			
		||||
    Scene get_previous_scene();
 | 
			
		||||
 | 
			
		||||
    void notify_init();
 | 
			
		||||
    void notify_green_blink();
 | 
			
		||||
    void notify_green_on();
 | 
			
		||||
    void notify_green_off();
 | 
			
		||||
 | 
			
		||||
    void notify_success();
 | 
			
		||||
 | 
			
		||||
    char* get_text_store();
 | 
			
		||||
    uint8_t get_text_store_size();
 | 
			
		||||
    void set_text_store(const char* text...);
 | 
			
		||||
 | 
			
		||||
    WIEGAND* get_wiegand();
 | 
			
		||||
    OneWireMaster* get_one_wire();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::list<Scene> previous_scenes_list = {Scene::Exit};
 | 
			
		||||
    Scene current_scene = Scene::Start;
 | 
			
		||||
    AccessorAppViewManager view;
 | 
			
		||||
 | 
			
		||||
    std::map<Scene, AccessorScene*> scenes = {
 | 
			
		||||
        {Scene::Start, new AccessorSceneStart()},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    static const uint8_t text_store_size = 128;
 | 
			
		||||
    char text_store[text_store_size + 1];
 | 
			
		||||
 | 
			
		||||
    WIEGAND wiegand;
 | 
			
		||||
    OneWireMaster onewire_master;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										19
									
								
								applications/accessor/accessor-event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								applications/accessor/accessor-event.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
class AccessorEvent {
 | 
			
		||||
public:
 | 
			
		||||
    // events enum
 | 
			
		||||
    enum class Type : uint8_t {
 | 
			
		||||
        Tick,
 | 
			
		||||
        Back,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // payload
 | 
			
		||||
    union {
 | 
			
		||||
        uint32_t menu_index;
 | 
			
		||||
    } payload;
 | 
			
		||||
 | 
			
		||||
    // event type
 | 
			
		||||
    Type type;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										79
									
								
								applications/accessor/accessor-view-manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								applications/accessor/accessor-view-manager.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
			
		||||
#include "accessor-view-manager.h"
 | 
			
		||||
#include "accessor-event.h"
 | 
			
		||||
#include <callback-connector.h>
 | 
			
		||||
 | 
			
		||||
AccessorAppViewManager::AccessorAppViewManager() {
 | 
			
		||||
    event_queue = osMessageQueueNew(10, sizeof(AccessorEvent), NULL);
 | 
			
		||||
 | 
			
		||||
    view_dispatcher = view_dispatcher_alloc();
 | 
			
		||||
    auto callback = cbc::obtain_connector(this, &AccessorAppViewManager::previous_view_callback);
 | 
			
		||||
 | 
			
		||||
    // allocate views
 | 
			
		||||
    submenu = submenu_alloc();
 | 
			
		||||
    add_view(ViewType::Submenu, submenu_get_view(submenu));
 | 
			
		||||
 | 
			
		||||
    popup = popup_alloc();
 | 
			
		||||
    add_view(ViewType::Popup, popup_get_view(popup));
 | 
			
		||||
 | 
			
		||||
    gui = static_cast<Gui*>(furi_record_open("gui"));
 | 
			
		||||
    view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
 | 
			
		||||
 | 
			
		||||
    // set previous view callback for all views
 | 
			
		||||
    view_set_previous_callback(submenu_get_view(submenu), callback);
 | 
			
		||||
    view_set_previous_callback(popup_get_view(popup), callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AccessorAppViewManager::~AccessorAppViewManager() {
 | 
			
		||||
    // remove views
 | 
			
		||||
    view_dispatcher_remove_view(
 | 
			
		||||
        view_dispatcher, static_cast<uint32_t>(AccessorAppViewManager::ViewType::Submenu));
 | 
			
		||||
    view_dispatcher_remove_view(
 | 
			
		||||
        view_dispatcher, static_cast<uint32_t>(AccessorAppViewManager::ViewType::Popup));
 | 
			
		||||
 | 
			
		||||
    // free view modules
 | 
			
		||||
    submenu_free(submenu);
 | 
			
		||||
    popup_free(popup);
 | 
			
		||||
 | 
			
		||||
    // free dispatcher
 | 
			
		||||
    view_dispatcher_free(view_dispatcher);
 | 
			
		||||
 | 
			
		||||
    // free event queue
 | 
			
		||||
    osMessageQueueDelete(event_queue);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AccessorAppViewManager::switch_to(ViewType type) {
 | 
			
		||||
    view_dispatcher_switch_to_view(view_dispatcher, static_cast<uint32_t>(type));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Submenu* AccessorAppViewManager::get_submenu() {
 | 
			
		||||
    return submenu;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Popup* AccessorAppViewManager::get_popup() {
 | 
			
		||||
    return popup;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AccessorAppViewManager::receive_event(AccessorEvent* event) {
 | 
			
		||||
    if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) {
 | 
			
		||||
        event->type = AccessorEvent::Type::Tick;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AccessorAppViewManager::send_event(AccessorEvent* event) {
 | 
			
		||||
    osStatus_t result = osMessageQueuePut(event_queue, event, 0, 0);
 | 
			
		||||
    furi_check(result == osOK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t AccessorAppViewManager::previous_view_callback(void* context) {
 | 
			
		||||
    if(event_queue != NULL) {
 | 
			
		||||
        AccessorEvent event;
 | 
			
		||||
        event.type = AccessorEvent::Type::Back;
 | 
			
		||||
        send_event(&event);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return VIEW_IGNORE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AccessorAppViewManager::add_view(ViewType view_type, View* view) {
 | 
			
		||||
    view_dispatcher_add_view(view_dispatcher, static_cast<uint32_t>(view_type), view);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								applications/accessor/accessor-view-manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								applications/accessor/accessor-view-manager.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <furi.h>
 | 
			
		||||
#include <gui/view_dispatcher.h>
 | 
			
		||||
#include <gui/modules/submenu.h>
 | 
			
		||||
#include <gui/modules/popup.h>
 | 
			
		||||
#include "accessor-event.h"
 | 
			
		||||
 | 
			
		||||
class AccessorAppViewManager {
 | 
			
		||||
public:
 | 
			
		||||
    enum class ViewType : uint8_t {
 | 
			
		||||
        Submenu,
 | 
			
		||||
        Popup,
 | 
			
		||||
        Tune,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    osMessageQueueId_t event_queue;
 | 
			
		||||
 | 
			
		||||
    AccessorAppViewManager();
 | 
			
		||||
    ~AccessorAppViewManager();
 | 
			
		||||
 | 
			
		||||
    void switch_to(ViewType type);
 | 
			
		||||
 | 
			
		||||
    void receive_event(AccessorEvent* event);
 | 
			
		||||
    void send_event(AccessorEvent* event);
 | 
			
		||||
 | 
			
		||||
    Submenu* get_submenu();
 | 
			
		||||
    Popup* get_popup();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    ViewDispatcher* view_dispatcher;
 | 
			
		||||
    Gui* gui;
 | 
			
		||||
 | 
			
		||||
    uint32_t previous_view_callback(void* context);
 | 
			
		||||
    void add_view(ViewType view_type, View* view);
 | 
			
		||||
 | 
			
		||||
    // view elements
 | 
			
		||||
    Submenu* submenu;
 | 
			
		||||
    Popup* popup;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										10
									
								
								applications/accessor/accessor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								applications/accessor/accessor.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
#include "accessor-app.h"
 | 
			
		||||
 | 
			
		||||
// app enter function
 | 
			
		||||
extern "C" int32_t app_accessor(void* p) {
 | 
			
		||||
    AccessorApp* app = new AccessorApp();
 | 
			
		||||
    app->run();
 | 
			
		||||
    delete app;
 | 
			
		||||
 | 
			
		||||
    return 255;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										221
									
								
								applications/accessor/helpers/wiegand.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								applications/accessor/helpers/wiegand.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,221 @@
 | 
			
		||||
#include "wiegand.h"
 | 
			
		||||
#include <furi.h>
 | 
			
		||||
#include <api-hal.h>
 | 
			
		||||
 | 
			
		||||
volatile unsigned long WIEGAND::_cardTempHigh = 0;
 | 
			
		||||
volatile unsigned long WIEGAND::_cardTemp = 0;
 | 
			
		||||
volatile unsigned long WIEGAND::_lastWiegand = 0;
 | 
			
		||||
unsigned long WIEGAND::_code = 0;
 | 
			
		||||
unsigned long WIEGAND::_codeHigh = 0;
 | 
			
		||||
volatile int WIEGAND::_bitCount = 0;
 | 
			
		||||
int WIEGAND::_wiegandType = 0;
 | 
			
		||||
 | 
			
		||||
constexpr uint32_t clocks_in_ms = 64 * 1000;
 | 
			
		||||
 | 
			
		||||
WIEGAND::WIEGAND() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned long WIEGAND::getCode() {
 | 
			
		||||
    return _code;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned long WIEGAND::getCodeHigh() {
 | 
			
		||||
    return _codeHigh;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int WIEGAND::getWiegandType() {
 | 
			
		||||
    return _wiegandType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool WIEGAND::available() {
 | 
			
		||||
    bool ret;
 | 
			
		||||
    __disable_irq();
 | 
			
		||||
    ret = DoWiegandConversion();
 | 
			
		||||
    __enable_irq();
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void input_isr(void* _pin, void* _ctx) {
 | 
			
		||||
    // interrupt manager get us pin constant, so...
 | 
			
		||||
    uint32_t pin = (uint32_t)_pin;
 | 
			
		||||
    WIEGAND* _this = static_cast<WIEGAND*>(_ctx);
 | 
			
		||||
 | 
			
		||||
    if(pin == ext_pa6_gpio.pin) {
 | 
			
		||||
        _this->ReadD0();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(pin == ext_pa7_gpio.pin) {
 | 
			
		||||
        _this->ReadD1();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WIEGAND::begin() {
 | 
			
		||||
    _lastWiegand = 0;
 | 
			
		||||
    _cardTempHigh = 0;
 | 
			
		||||
    _cardTemp = 0;
 | 
			
		||||
    _code = 0;
 | 
			
		||||
    _wiegandType = 0;
 | 
			
		||||
    _bitCount = 0;
 | 
			
		||||
 | 
			
		||||
    const GpioPin* pinD0 = &ext_pa6_gpio;
 | 
			
		||||
    const GpioPin* pinD1 = &ext_pa7_gpio;
 | 
			
		||||
 | 
			
		||||
    gpio_init(pinD0, GpioModeInterruptFall); // Set D0 pin as input
 | 
			
		||||
    gpio_init(pinD1, GpioModeInterruptFall); // Set D1 pin as input
 | 
			
		||||
 | 
			
		||||
    api_interrupt_add(
 | 
			
		||||
        input_isr, InterruptTypeExternalInterrupt, this); // Hardware interrupt - high to low pulse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WIEGAND::ReadD0() {
 | 
			
		||||
    _bitCount++; // Increament bit count for Interrupt connected to D0
 | 
			
		||||
    if(_bitCount > 31) // If bit count more than 31, process high bits
 | 
			
		||||
    {
 | 
			
		||||
        _cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); //	shift value to high bits
 | 
			
		||||
        _cardTempHigh <<= 1;
 | 
			
		||||
        _cardTemp <<= 1;
 | 
			
		||||
    } else {
 | 
			
		||||
        _cardTemp <<= 1; // D0 represent binary 0, so just left shift card data
 | 
			
		||||
    }
 | 
			
		||||
    _lastWiegand = DWT->CYCCNT; // Keep track of last wiegand bit received
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WIEGAND::ReadD1() {
 | 
			
		||||
    _bitCount++; // Increment bit count for Interrupt connected to D1
 | 
			
		||||
    if(_bitCount > 31) // If bit count more than 31, process high bits
 | 
			
		||||
    {
 | 
			
		||||
        _cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits
 | 
			
		||||
        _cardTempHigh <<= 1;
 | 
			
		||||
        _cardTemp |= 1;
 | 
			
		||||
        _cardTemp <<= 1;
 | 
			
		||||
    } else {
 | 
			
		||||
        _cardTemp |= 1; // D1 represent binary 1, so OR card data with 1 then
 | 
			
		||||
        _cardTemp <<= 1; // left shift card data
 | 
			
		||||
    }
 | 
			
		||||
    _lastWiegand = DWT->CYCCNT; // Keep track of last wiegand bit received
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned long WIEGAND::GetCardId(
 | 
			
		||||
    volatile unsigned long* codehigh,
 | 
			
		||||
    volatile unsigned long* codelow,
 | 
			
		||||
    char bitlength) {
 | 
			
		||||
    if(bitlength == 26) // EM tag
 | 
			
		||||
        return (*codelow & 0x1FFFFFE) >> 1;
 | 
			
		||||
 | 
			
		||||
    if(bitlength == 24) return (*codelow & 0x7FFFFE) >> 1;
 | 
			
		||||
 | 
			
		||||
    if(bitlength == 34) // Mifare
 | 
			
		||||
    {
 | 
			
		||||
        *codehigh = *codehigh & 0x03; // only need the 2 LSB of the codehigh
 | 
			
		||||
        *codehigh <<= 30; // shift 2 LSB to MSB
 | 
			
		||||
        *codelow >>= 1;
 | 
			
		||||
        return *codehigh | *codelow;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(bitlength == 32) {
 | 
			
		||||
        return (*codelow & 0x7FFFFFFE) >> 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return *codelow; // EM tag or Mifare without parity bits
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char translateEnterEscapeKeyPress(char originalKeyPress) {
 | 
			
		||||
    switch(originalKeyPress) {
 | 
			
		||||
    case 0x0b: // 11 or * key
 | 
			
		||||
        return 0x0d; // 13 or ASCII ENTER
 | 
			
		||||
 | 
			
		||||
    case 0x0a: // 10 or # key
 | 
			
		||||
        return 0x1b; // 27 or ASCII ESCAPE
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        return originalKeyPress;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool WIEGAND::DoWiegandConversion() {
 | 
			
		||||
    unsigned long cardID;
 | 
			
		||||
    unsigned long sysTick = DWT->CYCCNT;
 | 
			
		||||
 | 
			
		||||
    if((sysTick - _lastWiegand) >
 | 
			
		||||
       (25 * clocks_in_ms)) // if no more signal coming through after 25ms
 | 
			
		||||
    {
 | 
			
		||||
        if((_bitCount == 24) || (_bitCount == 26) || (_bitCount == 32) || (_bitCount == 34) ||
 | 
			
		||||
           (_bitCount == 37) || (_bitCount == 40) || (_bitCount == 8) ||
 | 
			
		||||
           (_bitCount ==
 | 
			
		||||
            4)) // bitCount for keypress=4 or 8, Wiegand 26=24 or 26, Wiegand 34=32 or 34
 | 
			
		||||
        {
 | 
			
		||||
            _codeHigh = 0;
 | 
			
		||||
            // shift right 1 bit to get back the real value - interrupt done 1 left shift in advance
 | 
			
		||||
            _cardTemp >>= 1;
 | 
			
		||||
            // bit count more than 32 bits, shift high bits right to make adjustment
 | 
			
		||||
            if(_bitCount > 32) _cardTempHigh >>= 1;
 | 
			
		||||
 | 
			
		||||
            if(_bitCount == 8) // keypress wiegand with integrity
 | 
			
		||||
            {
 | 
			
		||||
                // 8-bit Wiegand keyboard data, high nibble is the "NOT" of low nibble
 | 
			
		||||
                // eg if key 1 pressed, data=E1 in binary 11100001 , high nibble=1110 , low nibble = 0001
 | 
			
		||||
                char highNibble = (_cardTemp & 0xf0) >> 4;
 | 
			
		||||
                char lowNibble = (_cardTemp & 0x0f);
 | 
			
		||||
                _wiegandType = _bitCount;
 | 
			
		||||
                _bitCount = 0;
 | 
			
		||||
                _cardTemp = 0;
 | 
			
		||||
                _cardTempHigh = 0;
 | 
			
		||||
 | 
			
		||||
                if(lowNibble ==
 | 
			
		||||
                   (~highNibble & 0x0f)) // check if low nibble matches the "NOT" of high nibble.
 | 
			
		||||
                {
 | 
			
		||||
                    _code = (int)translateEnterEscapeKeyPress(lowNibble);
 | 
			
		||||
                    return true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    _lastWiegand = sysTick;
 | 
			
		||||
                    _bitCount = 0;
 | 
			
		||||
                    _cardTemp = 0;
 | 
			
		||||
                    _cardTempHigh = 0;
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // TODO: Handle validation failure case!
 | 
			
		||||
            } else if(4 == _bitCount) {
 | 
			
		||||
                // 4-bit Wiegand codes have no data integrity check so we just
 | 
			
		||||
                // read the LOW nibble.
 | 
			
		||||
                _code = (int)translateEnterEscapeKeyPress(_cardTemp & 0x0000000F);
 | 
			
		||||
 | 
			
		||||
                _wiegandType = _bitCount;
 | 
			
		||||
                _bitCount = 0;
 | 
			
		||||
                _cardTemp = 0;
 | 
			
		||||
                _cardTempHigh = 0;
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            } else if(40 == _bitCount) {
 | 
			
		||||
                _cardTempHigh >>= 1;
 | 
			
		||||
 | 
			
		||||
                _code = _cardTemp;
 | 
			
		||||
                _codeHigh = _cardTempHigh;
 | 
			
		||||
 | 
			
		||||
                _wiegandType = _bitCount;
 | 
			
		||||
                _bitCount = 0;
 | 
			
		||||
                _cardTemp = 0;
 | 
			
		||||
                _cardTempHigh = 0;
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            } else {
 | 
			
		||||
                // wiegand 26 or wiegand 34
 | 
			
		||||
                cardID = GetCardId(&_cardTempHigh, &_cardTemp, _bitCount);
 | 
			
		||||
                _wiegandType = _bitCount;
 | 
			
		||||
                _bitCount = 0;
 | 
			
		||||
                _cardTemp = 0;
 | 
			
		||||
                _cardTempHigh = 0;
 | 
			
		||||
                _code = cardID;
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // well time over 25 ms and bitCount !=8 , !=26, !=34 , must be noise or nothing then.
 | 
			
		||||
            _lastWiegand = sysTick;
 | 
			
		||||
            _bitCount = 0;
 | 
			
		||||
            _cardTemp = 0;
 | 
			
		||||
            _cardTempHigh = 0;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    } else
 | 
			
		||||
        return false;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								applications/accessor/helpers/wiegand.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								applications/accessor/helpers/wiegand.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
class WIEGAND {
 | 
			
		||||
public:
 | 
			
		||||
    WIEGAND();
 | 
			
		||||
    void begin();
 | 
			
		||||
    bool available();
 | 
			
		||||
    unsigned long getCode();
 | 
			
		||||
    unsigned long getCodeHigh();
 | 
			
		||||
    int getWiegandType();
 | 
			
		||||
 | 
			
		||||
    static void ReadD0();
 | 
			
		||||
    static void ReadD1();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static bool DoWiegandConversion();
 | 
			
		||||
    static unsigned long
 | 
			
		||||
    GetCardId(volatile unsigned long* codehigh, volatile unsigned long* codelow, char bitlength);
 | 
			
		||||
 | 
			
		||||
    static volatile unsigned long _cardTempHigh;
 | 
			
		||||
    static volatile unsigned long _cardTemp;
 | 
			
		||||
    static volatile unsigned long _lastWiegand;
 | 
			
		||||
    static volatile int _bitCount;
 | 
			
		||||
    static int _wiegandType;
 | 
			
		||||
    static unsigned long _code;
 | 
			
		||||
    static unsigned long _codeHigh;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										13
									
								
								applications/accessor/scene/accessor-scene-generic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/accessor/scene/accessor-scene-generic.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "../accessor-app.h"
 | 
			
		||||
 | 
			
		||||
class AccessorApp;
 | 
			
		||||
 | 
			
		||||
class AccessorScene {
 | 
			
		||||
public:
 | 
			
		||||
    virtual void on_enter(AccessorApp* app) = 0;
 | 
			
		||||
    virtual bool on_event(AccessorApp* app, AccessorEvent* event) = 0;
 | 
			
		||||
    virtual void on_exit(AccessorApp* app) = 0;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										88
									
								
								applications/accessor/scene/accessor-scene-start.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								applications/accessor/scene/accessor-scene-start.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,88 @@
 | 
			
		||||
#include "../accessor-app.h"
 | 
			
		||||
#include "../accessor-view-manager.h"
 | 
			
		||||
#include "../accessor-event.h"
 | 
			
		||||
#include <callback-connector.h>
 | 
			
		||||
#include "accessor-scene-start.h"
 | 
			
		||||
 | 
			
		||||
void AccessorSceneStart::on_enter(AccessorApp* app) {
 | 
			
		||||
    AccessorAppViewManager* view_manager = app->get_view_manager();
 | 
			
		||||
    Popup* popup = view_manager->get_popup();
 | 
			
		||||
 | 
			
		||||
    popup_set_header(popup, "Accessor App", 64, 16, AlignCenter, AlignBottom);
 | 
			
		||||
    app->set_text_store("[??????]");
 | 
			
		||||
    popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop);
 | 
			
		||||
 | 
			
		||||
    view_manager->switch_to(AccessorAppViewManager::ViewType::Popup);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) {
 | 
			
		||||
    bool consumed = false;
 | 
			
		||||
 | 
			
		||||
    if(event->type == AccessorEvent::Type::Tick) {
 | 
			
		||||
        WIEGAND* wiegand = app->get_wiegand();
 | 
			
		||||
        Popup* popup = app->get_view_manager()->get_popup();
 | 
			
		||||
        OneWireMaster* onewire = app->get_one_wire();
 | 
			
		||||
 | 
			
		||||
        uint8_t data[8] = {0, 0, 0, 0, 0, 0, 0, 0};
 | 
			
		||||
        uint8_t type = 0;
 | 
			
		||||
 | 
			
		||||
        if(wiegand->available()) {
 | 
			
		||||
            type = wiegand->getWiegandType();
 | 
			
		||||
 | 
			
		||||
            for(uint8_t i = 0; i < 4; i++) {
 | 
			
		||||
                data[i] = wiegand->getCode() >> (i * 8);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for(uint8_t i = 4; i < 8; i++) {
 | 
			
		||||
                data[i] = wiegand->getCodeHigh() >> ((i - 4) * 8);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            __disable_irq();
 | 
			
		||||
            if(onewire->reset()) {
 | 
			
		||||
                type = 255;
 | 
			
		||||
                onewire->write(0x33);
 | 
			
		||||
                for(uint8_t i = 0; i < 8; i++) {
 | 
			
		||||
                    data[i] = onewire->read();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                for(uint8_t i = 0; i < 7; i++) {
 | 
			
		||||
                    data[i] = data[i + 1];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            __enable_irq();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(type > 0) {
 | 
			
		||||
            if(type == 255) {
 | 
			
		||||
                app->set_text_store(
 | 
			
		||||
                    "[%02X %02X %02X %02X %02X %02X DS]",
 | 
			
		||||
                    data[5],
 | 
			
		||||
                    data[4],
 | 
			
		||||
                    data[3],
 | 
			
		||||
                    data[2],
 | 
			
		||||
                    data[1],
 | 
			
		||||
                    data[0]);
 | 
			
		||||
            } else {
 | 
			
		||||
                app->set_text_store(
 | 
			
		||||
                    "[%02X %02X %02X %02X %02X %02X W%u]",
 | 
			
		||||
                    data[5],
 | 
			
		||||
                    data[4],
 | 
			
		||||
                    data[3],
 | 
			
		||||
                    data[2],
 | 
			
		||||
                    data[1],
 | 
			
		||||
                    data[0],
 | 
			
		||||
                    type);
 | 
			
		||||
            }
 | 
			
		||||
            popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop);
 | 
			
		||||
            app->notify_success();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return consumed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AccessorSceneStart::on_exit(AccessorApp* app) {
 | 
			
		||||
    Popup* popup = app->get_view_manager()->get_popup();
 | 
			
		||||
    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
 | 
			
		||||
    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								applications/accessor/scene/accessor-scene-start.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								applications/accessor/scene/accessor-scene-start.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "accessor-scene-generic.h"
 | 
			
		||||
 | 
			
		||||
class AccessorSceneStart : public AccessorScene {
 | 
			
		||||
public:
 | 
			
		||||
    void on_enter(AccessorApp* app) final;
 | 
			
		||||
    bool on_event(AccessorApp* app, AccessorEvent* event) final;
 | 
			
		||||
    void on_exit(AccessorApp* app) final;
 | 
			
		||||
};
 | 
			
		||||
@ -35,6 +35,7 @@ int32_t gui_test(void* p);
 | 
			
		||||
int32_t keypad_test(void* p);
 | 
			
		||||
int32_t scene_app(void* p);
 | 
			
		||||
int32_t passport(void* p);
 | 
			
		||||
int32_t app_accessor(void* p);
 | 
			
		||||
 | 
			
		||||
const FlipperApplication FLIPPER_SERVICES[] = {
 | 
			
		||||
#ifdef APP_CLI
 | 
			
		||||
@ -149,6 +150,10 @@ const FlipperApplication FLIPPER_SERVICES[] = {
 | 
			
		||||
    {.app = keypad_test, .name = "keypad_test", .icon = A_Plugins_14},
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef APP_ACCESSOR
 | 
			
		||||
    {.app = app_accessor, .name = "accessor", .stack_size = 4096, .icon = A_Plugins_14},
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication);
 | 
			
		||||
@ -230,6 +235,10 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
 | 
			
		||||
    {.app = keypad_test, .name = "keypad_test", .icon = A_Plugins_14},
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef BUILD_ACCESSOR
 | 
			
		||||
    {.app = app_accessor, .name = "accessor", .stack_size = 4096, .icon = A_Plugins_14},
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const size_t FLIPPER_DEBUG_APPS_COUNT = sizeof(FLIPPER_DEBUG_APPS) / sizeof(FlipperApplication);
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ BUILD_GPIO_DEMO = 1
 | 
			
		||||
BUILD_MUSIC_PLAYER = 1
 | 
			
		||||
BUILD_FLOOPPER_BLOOPPER = 1
 | 
			
		||||
BUILD_IBUTTON = 1
 | 
			
		||||
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
APP_DEBUG ?=0
 | 
			
		||||
@ -32,6 +33,7 @@ ifeq ($(APP_DEBUG), 1)
 | 
			
		||||
CFLAGS		+= -DAPP_DEBUG
 | 
			
		||||
BUILD_GUI_TEST = 1
 | 
			
		||||
BUILD_KEYPAD_TEST = 1
 | 
			
		||||
BUILD_ACCESSOR = 1
 | 
			
		||||
BUILD_SD_TEST = 1
 | 
			
		||||
BUILD_VIBRO_DEMO = 1
 | 
			
		||||
BUILD_SPEAKER_DEMO = 1
 | 
			
		||||
@ -209,6 +211,17 @@ CFLAGS		+= -DBUILD_KEYPAD_TEST
 | 
			
		||||
BUILD_KEYPAD_TEST = 1
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
APP_ACCESSOR ?= 0
 | 
			
		||||
ifeq ($(APP_ACCESSOR), 1)
 | 
			
		||||
CFLAGS		+= -DAPP_ACCESSOR
 | 
			
		||||
BUILD_ACCESSOR = 1
 | 
			
		||||
endif
 | 
			
		||||
BUILD_ACCESSOR ?= 0
 | 
			
		||||
ifeq ($(BUILD_ACCESSOR), 1)
 | 
			
		||||
CFLAGS		+= -DBUILD_ACCESSOR
 | 
			
		||||
BUILD_ACCESSOR = 1
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
APP_GPIO_DEMO ?= 0
 | 
			
		||||
ifeq ($(APP_GPIO_DEMO), 1)
 | 
			
		||||
CFLAGS		+= -DAPP_GPIO_DEMO
 | 
			
		||||
 | 
			
		||||
@ -42,3 +42,13 @@ const GpioPin gpio_spi_d_sck = { .port=SPI_D_SCK_GPIO_Port, .pin=SPI_D_SCK_Pin }
 | 
			
		||||
const GpioPin gpio_spi_r_miso = { .port=SPI_R_MISO_GPIO_Port, .pin=SPI_R_MISO_Pin };
 | 
			
		||||
const GpioPin gpio_spi_r_mosi = { .port=SPI_R_MOSI_GPIO_Port, .pin=SPI_R_MOSI_Pin };
 | 
			
		||||
const GpioPin gpio_spi_r_sck = { .port=SPI_R_SCK_GPIO_Port, .pin=SPI_R_SCK_Pin };
 | 
			
		||||
 | 
			
		||||
// external gpio's
 | 
			
		||||
const GpioPin ext_pc0_gpio = {.port = GPIOC, .pin = GPIO_PIN_0};
 | 
			
		||||
const GpioPin ext_pc1_gpio = {.port = GPIOC, .pin = GPIO_PIN_1};
 | 
			
		||||
const GpioPin ext_pc3_gpio = {.port = GPIOC, .pin = GPIO_PIN_3};
 | 
			
		||||
const GpioPin ext_pb2_gpio = {.port = GPIOB, .pin = GPIO_PIN_2};
 | 
			
		||||
const GpioPin ext_pb3_gpio = {.port = GPIOB, .pin = GPIO_PIN_3};
 | 
			
		||||
const GpioPin ext_pa4_gpio = {.port = GPIOA, .pin = GPIO_PIN_4};
 | 
			
		||||
const GpioPin ext_pa6_gpio = {.port = GPIOA, .pin = GPIO_PIN_6};
 | 
			
		||||
const GpioPin ext_pa7_gpio = {.port = GPIOA, .pin = GPIO_PIN_7};
 | 
			
		||||
@ -74,6 +74,14 @@ extern const GpioPin gpio_spi_r_miso;
 | 
			
		||||
extern const GpioPin gpio_spi_r_mosi;
 | 
			
		||||
extern const GpioPin gpio_spi_r_sck;
 | 
			
		||||
 | 
			
		||||
extern const GpioPin ext_pc0_gpio;
 | 
			
		||||
extern const GpioPin ext_pc1_gpio;
 | 
			
		||||
extern const GpioPin ext_pc3_gpio;
 | 
			
		||||
extern const GpioPin ext_pb2_gpio;
 | 
			
		||||
extern const GpioPin ext_pb3_gpio;
 | 
			
		||||
extern const GpioPin ext_pa4_gpio;
 | 
			
		||||
extern const GpioPin ext_pa6_gpio;
 | 
			
		||||
extern const GpioPin ext_pa7_gpio;
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user