forked from flipperdevices/flipperzero-firmware
-
-
Notifications
You must be signed in to change notification settings - Fork 545
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
370 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
non_catalog_apps/gb-pokemon-trading/lib/flipper-gblink/LICENSE
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
BSD 2-Clause License | ||
|
||
Copyright (c) 2023, KBEmbedded | ||
|
||
Redistribution and use in source and binary forms, with or without | ||
modification, are permitted provided that the following conditions are met: | ||
|
||
1. Redistributions 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. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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. |
25 changes: 25 additions & 0 deletions
25
non_catalog_apps/gb-pokemon-trading/lib/flipper-gblink/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Flipper Game Boy Game Link Cable API | ||
Simple API that can be included in projects to provide a flexible and easy way to handle data exchange over a Game Link Cable. | ||
|
||
Current Version: 0.5 | ||
|
||
Available from: https://github.com/kbembedded/flipper-gblink | ||
|
||
|
||
## Current feature status and future roadmap: | ||
- [x] Ability to use EXT clock source. i.e. connected device drives the clock line | ||
- [x] Callback on byte transfer completion | ||
- [x] Flexibility in IO pin selection at alloc time | ||
- [x] Ability to enable and disable interrupt on input clock | ||
- [x] Ability to set timeout in microseconds between clock edges. If exceeded, it is assumed the next clock is the first bit of a byte | ||
- [x] Set a NO\_DATA\_BYTE pattern. i.e. after a byte transfer is complete, a default byte is prepared to be sent out if no new data is provided before the transfer starts | ||
- [x] Supports communication to GBC | ||
- [x] Supports communication to GBA using GBC games | ||
- [ ] Supports communication to GB (untested, but should work) | ||
- [ ] Supports communication to GBA using GBA games | ||
- [ ] Function as INT clock source. i.e. Flipper Zero drives the clock line | ||
- [ ] Drive clock at varying speeds as GBC supports | ||
- [ ] Proper documentation | ||
|
||
## Use example | ||
See https://github.com/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading |
246 changes: 246 additions & 0 deletions
246
non_catalog_apps/gb-pokemon-trading/lib/flipper-gblink/gblink.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
// SPDX-License-Identifier: BSD-2-Clause | ||
// Copyright (c) 2023 KBEmbedded | ||
|
||
#include <furi.h> | ||
#include <furi_hal.h> | ||
|
||
#include "gblink.h" | ||
|
||
const struct gblink_pins common_pinouts[PINOUT_COUNT] = { | ||
/* Original */ | ||
{ | ||
&gpio_ext_pc3, | ||
&gpio_ext_pb3, | ||
&gpio_ext_pb2, | ||
&gpio_ext_pa4, | ||
}, | ||
/* MALVEKE EXT1 */ | ||
{ | ||
&gpio_ext_pa6, | ||
&gpio_ext_pa7, | ||
&gpio_ext_pb3, | ||
&gpio_ext_pa4, | ||
}, | ||
}; | ||
|
||
struct gblink { | ||
const GpioPin *serin; | ||
const GpioPin *serout; | ||
const GpioPin *clk; | ||
const GpioPin *sd; | ||
|
||
uint8_t in; | ||
uint8_t out; | ||
uint8_t out_buf; | ||
bool out_buf_valid; | ||
uint8_t shift; | ||
uint8_t nobyte; | ||
gblink_clk_source source; | ||
gblink_mode mode; | ||
gblink_speed speed; | ||
|
||
uint32_t time; | ||
|
||
uint32_t bitclk_timeout_us; | ||
/* Clocks idle between bytes is nominally 430 us long for burst data, | ||
* 15 ms for idle polling (e.g. waiting for menu selection), some oddball | ||
* 2 ms gaps that appears between one 0xFE byte from the Game Boy every trade; | ||
* clock period is nominally 122 us. | ||
* Therefore, if we haven't seen a clock in 500 us, reset our bit counter. | ||
* Note that, this should never actually be a concern, but it is an additional | ||
* safeguard against desyncing. | ||
*/ | ||
|
||
void (*callback)(void* cb_context, uint8_t in); | ||
void *cb_context; | ||
}; | ||
|
||
static void gblink_shift_in(struct gblink *gblink) | ||
{ | ||
const uint32_t time_ticks = furi_hal_cortex_instructions_per_microsecond() * gblink->bitclk_timeout_us; | ||
|
||
/* If we exceeded the bit clock timeout, reset all counters */ | ||
if ((DWT->CYCCNT - gblink->time) > time_ticks) { | ||
gblink->in = 0; | ||
gblink->shift = 0; | ||
} | ||
gblink->time = DWT->CYCCNT; | ||
|
||
gblink->in <<= 1; | ||
gblink->in |= furi_hal_gpio_read(gblink->serin); | ||
gblink->shift++; | ||
/* If 8 bits transfered, reset shift counter, call registered | ||
* callback, re-set nobyte in output buffer. | ||
*/ | ||
if (gblink->shift == 8) { | ||
gblink->shift = 0; | ||
|
||
/* Set up next out byte before calling the callback. | ||
* This is in case the callback itself sets a new out | ||
* byte which it will in most cases. It is up to the | ||
* main application at this time to ensure that | ||
* gblink_transfer() isn't called multiple times before | ||
* a byte has a chance to be sent out. | ||
*/ | ||
if (gblink->out_buf_valid) { | ||
gblink->out = gblink->out_buf; | ||
gblink->out_buf_valid = false; | ||
} else { | ||
gblink->out = gblink->nobyte; | ||
} | ||
gblink->callback(gblink->cb_context, gblink->in); | ||
} | ||
} | ||
|
||
static void gblink_shift_out(struct gblink *gblink) | ||
{ | ||
furi_hal_gpio_write(gblink->serout, !!(gblink->out & 0x80)); | ||
gblink->out <<= 1; | ||
} | ||
|
||
static void gblink_clk_callback(void *context) | ||
{ | ||
furi_assert(context); | ||
struct gblink *gblink = context; | ||
|
||
if (furi_hal_gpio_read(gblink->clk)) { | ||
/* Posedge Shift in data */ | ||
gblink_shift_in(gblink); | ||
} else { | ||
/* Negedge shift out data */ | ||
gblink_shift_out(gblink); | ||
} | ||
} | ||
|
||
void gblink_clk_source_set(void *handle, int source) | ||
{ | ||
furi_assert(handle); | ||
struct gblink *gblink = handle; | ||
|
||
gblink->source = source; | ||
gblink->shift = 0; | ||
} | ||
|
||
void gblink_speed_set(void *handle, gblink_speed speed) | ||
{ | ||
furi_assert(handle); | ||
struct gblink *gblink = handle; | ||
|
||
gblink->speed = speed; | ||
} | ||
|
||
/* default is set to 500 us */ | ||
void gblink_timeout_set(void *handle, uint32_t us) | ||
{ | ||
furi_assert(handle); | ||
struct gblink *gblink = handle; | ||
|
||
gblink->bitclk_timeout_us = us; | ||
} | ||
|
||
void gblink_transfer(void *handle, uint8_t val) | ||
{ | ||
furi_assert(handle); | ||
struct gblink *gblink = handle; | ||
|
||
/* This checks the value of gblink->shift which can change in the ISR. | ||
* Because of that, disable interrupts when checking gblink->shift and | ||
* setting gblink->out_buf_valid | ||
* If shift is 0, we're between bytes and can safely set the out byte. | ||
* If shift is nonzero, a byte is currently being transmitted. Set the | ||
* out_buf and set out_buf_valid. When the ISR is finished writing the | ||
* next byte it will check out_buf_valid and copy in out_buf. | ||
* | ||
* Realistically, this should only ever be called from the transfer | ||
* complete callback. There are few situations outside of that which | ||
* would make sense. | ||
* | ||
* Note that, this currently has no checks for if there is data already | ||
* pending to be transmitted. Calling this back to back can cause data | ||
* loss! | ||
*/ | ||
FURI_CRITICAL_ENTER(); | ||
if (gblink->shift == 0) { | ||
gblink->out = val; | ||
gblink->out_buf_valid = false; | ||
} else { | ||
gblink->out_buf = val; | ||
gblink->out_buf_valid = true; | ||
} | ||
FURI_CRITICAL_EXIT(); | ||
} | ||
|
||
void gblink_nobyte_set(void *handle, uint8_t val) | ||
{ | ||
struct gblink *gblink = handle; | ||
gblink->nobyte = val; | ||
} | ||
|
||
void gblink_int_enable(void *handle) | ||
{ | ||
furi_assert(handle); | ||
struct gblink *gblink = handle; | ||
|
||
furi_hal_gpio_enable_int_callback(gblink->clk); | ||
} | ||
|
||
void gblink_int_disable(void *handle) | ||
{ | ||
furi_assert(handle); | ||
struct gblink *gblink = handle; | ||
|
||
furi_hal_gpio_disable_int_callback(gblink->clk); | ||
} | ||
|
||
void *gblink_alloc(struct gblink_def *gblink_def) | ||
{ | ||
struct gblink *gblink; | ||
|
||
/* Allocate and zero struct */ | ||
gblink = malloc(sizeof(struct gblink)); | ||
|
||
/* Set struct values from function args */ | ||
gblink->serin = gblink_def->pins->serin; | ||
gblink->serout = gblink_def->pins->serout; | ||
gblink->clk = gblink_def->pins->clk; | ||
gblink->sd = gblink_def->pins->sd; | ||
gblink->source = gblink_def->source; | ||
gblink->speed = GBLINK_SPD_8192HZ; | ||
|
||
/* Set up timeout variables */ | ||
gblink->bitclk_timeout_us = 500; | ||
gblink->time = DWT->CYCCNT; | ||
|
||
/* Set up secondary callback */ | ||
gblink->callback = gblink_def->callback; | ||
gblink->cb_context = gblink_def->cb_context; | ||
|
||
/* Set up pins */ | ||
/* Currently assumes external clock source only */ | ||
/* XXX: This might actually be open-drain on real GB hardware */ | ||
furi_hal_gpio_write(gblink->serout, false); | ||
furi_hal_gpio_init(gblink->serout, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||
furi_hal_gpio_write(gblink->serin, false); | ||
furi_hal_gpio_init(gblink->serin, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh); | ||
furi_hal_gpio_init(gblink->clk, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh); | ||
|
||
/* Set up interrupt on clock */ | ||
/* This may not be needed after NFC refactor */ | ||
furi_hal_gpio_remove_int_callback(gblink->clk); | ||
furi_hal_gpio_add_int_callback(gblink->clk, gblink_clk_callback, gblink); | ||
|
||
return gblink; | ||
} | ||
|
||
void gblink_free(void *handle) | ||
{ | ||
furi_assert(handle); | ||
struct gblink *gblink = handle; | ||
|
||
/* Remove interrupt, set IO to sane state */ | ||
furi_hal_gpio_remove_int_callback(gblink->clk); | ||
furi_hal_gpio_init_simple(gblink->serin, GpioModeAnalog); | ||
furi_hal_gpio_init_simple(gblink->serout, GpioModeAnalog); | ||
furi_hal_gpio_init_simple(gblink->clk, GpioModeAnalog); | ||
free(gblink); | ||
} |
74 changes: 74 additions & 0 deletions
74
non_catalog_apps/gb-pokemon-trading/lib/flipper-gblink/gblink.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// SPDX-License-Identifier: BSD-2-Clause | ||
// Copyright (c) 2023 KBEmbedded | ||
|
||
#ifndef __GBLINK_H__ | ||
#define __GBLINK_H__ | ||
|
||
#pragma once | ||
|
||
typedef enum { | ||
/* Flipper drives the clock line */ | ||
/* Unsupported at this time */ | ||
GBLINK_INTERNAL_CLK, | ||
/* Game Boy drives the clock line */ | ||
GBLINK_EXTERNAL_CLK, | ||
} gblink_clk_source; | ||
|
||
/* Currently unused */ | ||
typedef enum { | ||
GBLINK_MODE_GBC, | ||
GBLINK_MODE_GBA, | ||
} gblink_mode; | ||
|
||
/* Should this just be a macro? */ | ||
/* This pretty much only applies to GBC, OG GB is 8192 Hz only */ | ||
/* This is only for TX */ | ||
typedef enum { | ||
GBLINK_SPD_8192HZ, | ||
GBLINK_SPD_16384HZ, | ||
GBLINK_SPD_262144HZ, | ||
GBLINK_SPD_524288HZ, | ||
} gblink_speed; | ||
|
||
struct gblink_pins { | ||
const GpioPin *serin; | ||
const GpioPin *serout; | ||
const GpioPin *clk; | ||
const GpioPin *sd; | ||
}; | ||
|
||
typedef enum { | ||
PINOUT_ORIGINAL, | ||
PINOUT_MALVEKE_EXT1, | ||
PINOUT_COUNT, | ||
} gblink_pinout; | ||
|
||
extern const struct gblink_pins common_pinouts[PINOUT_COUNT]; | ||
|
||
struct gblink_def { | ||
struct gblink_pins *pins; | ||
gblink_clk_source source; | ||
gblink_mode mode; | ||
void (*callback)(void* cb_context, uint8_t in); | ||
void *cb_context; | ||
}; | ||
|
||
void gblink_clk_source_set(void *handle, int clk_source); | ||
|
||
void gblink_speed_set(void *handle, gblink_speed speed); | ||
|
||
void gblink_timeout_set(void *handle, uint32_t us); | ||
|
||
void gblink_transfer(void *handle, uint8_t val); | ||
|
||
void gblink_nobyte_set(void *handle, uint8_t val); | ||
|
||
void gblink_int_enable(void *handle); | ||
|
||
void gblink_int_disable(void *handle); | ||
|
||
void *gblink_alloc(struct gblink_def *gblink_def); | ||
|
||
void gblink_free(void *handle); | ||
|
||
#endif // __GBLINK_H__ |