Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ Pokéwalker. [How step counting works](stick/docs/motion.md) explains the detail
The original 96×64 screen appears at twice its size on the Stick's display.
You can rotate it and choose a light or dark appearance. The speaker plays the
original tone sequences. Your Pokémon, route and progress stay saved across
restarts and firmware updates.
restarts. Return your Pokémon to HGSS before updating pre-release firmware;
updates can require resetting game data.

Walking continues while the screen is off. Hold M for half a second to wake it.
With the default controls, M moves left, R moves right, and M+R together
confirms. L opens Stick Settings. See [controls and settings](stick/docs/controls.md)
With the default controls, L moves left, M confirms, and R moves right.
The screen starts left-side-down. Hold L+R for one second to open Stick Settings.
See [controls and settings](stick/docs/controls.md)
for the other button layouts.

## Help and contribute
Expand Down
2 changes: 1 addition & 1 deletion config/lint-suppressions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
// The first expression adds a page to an EEPROM address. The battle subtraction
// is unsigned and its subtrahend is capped at the current watt balance.
// See docs/build.md.
integerOverflow:src/startup/h8_resetprg.c:302
integerOverflow:src/startup/h8_resetprg.c:371
integerOverflowCond:src/application/pw_battle.c:443
12 changes: 8 additions & 4 deletions docs/adr/0002-m-only-dark-wake.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Use M to wake the screen

Holding physical M for 500 ms wakes the Stick in every layout and rotation.
Holding physical M through native Center qualification wakes the Stick in every
layout and rotation, roughly half a second. The native firmware also retains
its social-offer opportunity; the adapter no longer starts interactive mode
from a separate 500 ms wall-clock threshold.
L and R resume their configured roles after wake. Releasing M early cancels
panel preparation. A held L or R still allows M to wake the display.

Expand All @@ -10,8 +13,8 @@ and waits for all buttons to be released. A successful PMIC read is required
to establish that L has been released.

When the game or Settings is visible, the front buttons can wake the CPU and
sleep is capped at 100 ms for L polling. Awake polls run every 5 ms. Walking
and interactive game work normally supply a 62.5 ms deadline.
required button polls supply a 5 ms deadline. Walking and interactive game work
retain their native timing instead of accelerating to consume queued input.

## GPIO restoration

Expand All @@ -22,7 +25,8 @@ keep the CPU awake until a scheduled retry.

## Checks

Host tests cover the adapter branches and sleep API calls. The
Production-firmware simulation covers timeout, canceled and qualified M holds,
held-input suppression, RTC timer/EXT1 wake and digital GPIO restoration. The
[device test records](../../stick/docs/history/power-and-wake.md) record the
button, panel and boot trials. USB keeps the CPU awake during bench commands;
test physical sleep and wake on battery.
Expand Down
13 changes: 5 additions & 8 deletions docs/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ This requires the exact retail size and SHA-256 and writes `verified.json` on
success. Run it under **both 6.02.01 and 6.02.02** for accepted matching-source
changes. The build records also check that the image came from the current
source, assets and compiler files. They catch stale objects even when timestamps
have been preserved. Host tests and formatting complement this check.
have been preserved. Firmware E2E checks and formatting complement this check.

## Clean, recover and work offline

Expand Down Expand Up @@ -211,8 +211,9 @@ Cppcheck 2.21.0 separately, then run:
uv run --group dev -m tools.check
```

This checks C formatting, Python formatting and lint, Cppcheck with the H8 target
model, and the utility tests. To apply formatting:
This checks C formatting, Python formatting and lint, and Cppcheck with the H8
target model. Run `uv run python stick/check.py` separately for the production
firmware workflows. To apply formatting:

```sh
uv run --group dev -m tools.check --format
Expand All @@ -221,11 +222,7 @@ uv run --group dev -m tools.check --format
Formatting includes nested source and header directories. The accepted HEW
device header at `include/startup/iodefine.h` keeps its original formatting.
Cppcheck has two narrow suppressions for arithmetic whose evaluation follows
the target's 16-bit rules. To run only the utility tests:

```sh
uv run -m unittest discover -s tests
```
the target's 16-bit rules.

`uv` is a convenience for a reproducible host environment. An existing Python
environment with Ninja and Pillow can also run `python configure.py` and `ninja`;
Expand Down
4 changes: 2 additions & 2 deletions include/stick_wire_endian.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
* documented with a Le suffix remain raw forwarded bytes instead. */
static inline u32 StickReadBe32(const u8 *bytes)
{
return ((u32)bytes[0] << 24) | ((u32)bytes[1] << 16) |
((u32)bytes[2] << 8) | bytes[3];
return ((u32)bytes[0] << 24) | ((u32)bytes[1] << 16) | ((u32)bytes[2] << 8) |
bytes[3];
}

static inline u16 StickReadBe16(const u8 *bytes)
Expand Down
15 changes: 13 additions & 2 deletions src/application/pw_buzzer.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "application/pw_builtin.h"
#include "application/pw_eeprom_m95512.h"
#ifdef PW_STICK_S3
#include "stick/native_runtime.h"
#include "stick/sound_bridge.h"
#endif

Expand Down Expand Up @@ -54,6 +55,8 @@ void BeepInit(void)
g_ui.durationDivisor = 0x78;
g_ui.outputMode = 0;
#ifdef PW_STICK_S3
StickNativeTimerWDisable();
StickNativeTimerWSetCompare(0);
StickSoundInit();
#else
IO.PCR8 |= 0x0c;
Expand Down Expand Up @@ -92,7 +95,9 @@ void BeepLoadScore(u8 sequenceId)
if (g_ui.outputMode == 0) {
return;
}
#ifndef PW_STICK_S3
#ifdef PW_STICK_S3
StickNativeTimerWInterruptEnable(0);
#else
TW.TIERW.BIT.IMIEA = 0;
#endif
id = sequenceId;
Expand Down Expand Up @@ -126,7 +131,9 @@ void BeepLoadScore(u8 sequenceId)
g_ui.separatorPeriodsRemaining = 0;
}
}
#ifndef PW_STICK_S3
#ifdef PW_STICK_S3
StickNativeTimerWInterruptEnable(1);
#else
TW.TIERW.BIT.IMIEA = 1;
#endif
}
Expand All @@ -143,6 +150,7 @@ void BeepSelectScore(const Note *score)
void BeepEnableTimer(void)
{
#ifdef PW_STICK_S3
StickNativeTimerWEnable();
StickSoundEnable();
#else
CKSTPR2.BYTE |= 0x40;
Expand All @@ -161,6 +169,7 @@ void BeepEnableTimer(void)
void BeepDisableTimer(void)
{
#ifdef PW_STICK_S3
StickNativeTimerWDisable();
StickSoundDisable();
#else
TW.TIERW.BIT.IMIEA = 0;
Expand Down Expand Up @@ -190,6 +199,7 @@ void BeepSetOutputMode(u8 mode)
void BeepSetPeriod(u8 compareValue)
{
#ifdef PW_STICK_S3
StickNativeTimerWSetCompare(compareValue);
StickSoundPeriod(compareValue, g_ui.outputMode);
#else
switch (g_ui.outputMode) {
Expand Down Expand Up @@ -245,6 +255,7 @@ void BeepAdvance(void)
goto playNote;
} else {
#ifdef PW_STICK_S3
StickNativeTimerWSetCompare(BEEPER_SEPARATOR_COMPARE);
StickSoundSilencePeriod(BEEPER_SEPARATOR_COMPARE);
#else
TW.GRA = BEEPER_SEPARATOR_COMPARE;
Expand Down
29 changes: 24 additions & 5 deletions src/application/pw_friend.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,10 @@ void PeerAwardGift(void)
PeerInfo *peer;
Item *items;
u32 giftScore;
#ifdef PW_STICK_S3
u32 peerDailySteps;
u16 peerHourSteps;
#endif
u8 freeSlot;

ScratchReset();
Expand All @@ -111,9 +113,6 @@ void PeerAwardGift(void)
#ifdef PW_STICK_S3
peerDailySteps = StickReadBe32((const u8 *)&peer->dailySteps);
peerHourSteps = StickReadBe16((const u8 *)&peer->hourSteps);
#else
peerDailySteps = peer->dailySteps;
peerHourSteps = peer->hourSteps;
#endif
items = ScratchAlloc(sizeof(Item) * 10);
EepromRead(PW_EEPROM_MEMBER_ADDRESS(EEPROM_WALK, WalkData, friendItems),
Expand All @@ -123,8 +122,8 @@ void PeerAwardGift(void)
giftScore = g_state.dailySteps + peerDailySteps +
(u16)((g_state.hourSteps + peerHourSteps) * 10u);
#else
giftScore = g_state.dailySteps + peerDailySteps +
((g_state.hourSteps + peerHourSteps) * 10);
giftScore = g_state.dailySteps + peer->dailySteps +
((g_state.hourSteps + peer->hourSteps) * 10);
#endif
if (giftScore > 20000ul) {
giftScore = 20000ul;
Expand All @@ -151,7 +150,11 @@ void PeerAwardGift(void)
if (g_ui.view.peer.wattsAwarded != 0) {
return;
}
#ifdef PW_STICK_S3
if (g_state.dailySteps > peerDailySteps) {
#else
if (g_state.dailySteps > peer->dailySteps) {
#endif
g_ui.view.peer.itemIndex = 0;
} else {
g_ui.view.peer.itemIndex = 1;
Expand All @@ -164,7 +167,11 @@ void PeerAwardGift(void)
if (g_ui.view.peer.wattsAwarded != 0) {
return;
}
#ifdef PW_STICK_S3
if (g_state.dailySteps > peerDailySteps) {
#else
if (g_state.dailySteps > peer->dailySteps) {
#endif
g_ui.view.peer.itemIndex = 2;
} else {
g_ui.view.peer.itemIndex = 3;
Expand All @@ -177,7 +184,11 @@ void PeerAwardGift(void)
if (g_ui.view.peer.wattsAwarded != 0) {
return;
}
#ifdef PW_STICK_S3
if (g_state.dailySteps > peerDailySteps) {
#else
if (g_state.dailySteps > peer->dailySteps) {
#endif
g_ui.view.peer.itemIndex = 4;
} else {
g_ui.view.peer.itemIndex = 5;
Expand All @@ -190,7 +201,11 @@ void PeerAwardGift(void)
if (g_ui.view.peer.wattsAwarded != 0) {
return;
}
#ifdef PW_STICK_S3
if (g_state.dailySteps > peerDailySteps) {
#else
if (g_state.dailySteps > peer->dailySteps) {
#endif
g_ui.view.peer.itemIndex = 6;
} else {
g_ui.view.peer.itemIndex = 7;
Expand All @@ -203,7 +218,11 @@ void PeerAwardGift(void)
if (g_ui.view.peer.wattsAwarded != 0) {
return;
}
#ifdef PW_STICK_S3
if (g_state.dailySteps > peerDailySteps) {
#else
if (g_state.dailySteps > peer->dailySteps) {
#endif
g_ui.view.peer.itemIndex = 8;
} else {
g_ui.view.peer.itemIndex = 9;
Expand Down
9 changes: 0 additions & 9 deletions src/application/pw_home.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
#include "application/pw_player_input.h"
#include "support/lib_common.h"
#include "support/scratch.h"
#ifdef PW_STICK_S3
#include "stick/display_panel.h"
#endif

#define HOME_DISPLAY_HEIGHT_PIXELS 0x40
#define HOME_INITIAL_X 0x20
Expand Down Expand Up @@ -59,12 +56,6 @@ void TryBeginIr(void)
RenderBattery(0, 0);
}
DisplayToggleBank();
#ifdef PW_STICK_S3
/* The NT7508 bank switch is immediate on the original panel. The Stick's
* virtual display needs one physical transfer before IR takes ownership;
* presenting in the receive loop would delay packet replies. */
StickDisplayPresent();
#endif
set_ccr(0x80);
IrBegin();
InstallTask(IrProtocolTick);
Expand Down
9 changes: 5 additions & 4 deletions src/application/pw_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "types.h"
#ifdef PW_STICK_S3
#include "stick/motion_sample.h"
#include "stick/native_runtime.h"
#endif
#include "eeprom_address.h"
#include "startup/hardware.h"
Expand Down Expand Up @@ -579,7 +580,9 @@ void MainTick(void)
CaptureSample();
IENR1.BIT.IENRTC = 1;
#else
g_state.events.bits.lowPowerClock = 0;
CaptureSample();
StickNativeService();
#endif
InputScan();
if ((g_state.flags.byte & SYSTEM_MODE_MASK) == SYSTEM_MODE_INACTIVE) {
Expand Down Expand Up @@ -623,13 +626,9 @@ void MainTick(void)
g_state.centerHoldScans = 0;
}
} else {
#ifndef PW_STICK_S3
set_ccr(0x80);
#endif
BatteryUpdate();
#ifndef PW_STICK_S3
set_ccr(0);
#endif
MotionSessionIdleCheck();
}
}
Expand Down Expand Up @@ -658,6 +657,8 @@ void BeepTick(void)
if (TW.GRA != 0) {
sleep();
}
#else
g_state.events.bits.lowPowerClock = 1;
#endif
WatchdogService();
InputScan();
Expand Down
16 changes: 8 additions & 8 deletions src/application/pw_nt7508.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ void DisplayAddress(u8 x, u8 page)
while (SSU.SSSR.BIT.TDRE == 0) {
}
LCD_TX((((x / DISPLAY_COL_MSB_DIVISOR) & DISPLAY_COL_MSB_MASK) +
DISPLAY_COL_MSB_COMMAND));
DISPLAY_COL_MSB_COMMAND));
while (SSU.SSSR.BIT.TDRE == 0) {
}
LCD_TX((x & DISPLAY_COL_LSB_MASK));
Expand All @@ -194,7 +194,7 @@ void DisplayAddress(u8 x, u8 page)
while (SSU.SSSR.BIT.TDRE == 0) {
}
LCD_TX((g_displayBank.bytes.index * DISPLAY_BANK_PAGES + page +
DISPLAY_PAGE_COMMAND_BASE));
DISPLAY_PAGE_COMMAND_BASE));
while (SSU.SSSR.BIT.TEND == 0) {
}
}
Expand Down Expand Up @@ -274,7 +274,7 @@ void DisplayFill(u8 pixelValue)
while (SSU.SSSR.BIT.TDRE == 0) {
}
LCD_TX((g_displayBank.bytes.index * DISPLAY_BANK_PAGES + y +
DISPLAY_PAGE_COMMAND_BASE));
DISPLAY_PAGE_COMMAND_BASE));
while (SSU.SSSR.BIT.TEND == 0) {
}
IO.PDR1.BIT.B1 = 1;
Expand Down Expand Up @@ -330,7 +330,7 @@ void DisplayFill(u8 pixelValue)
do { \
while (SSU.SSSR.BIT.TDRE == 0) { \
} \
LCD_TX((value)); \
LCD_TX((value)); \
} while (0)
#define LCD_END() \
do { \
Expand Down Expand Up @@ -467,7 +467,7 @@ void DisplayClear(u8 height)
while (SSU.SSSR.BIT.TDRE == 0) {
}
LCD_TX((g_displayBank.bytes.index * DISPLAY_BANK_PAGES + y +
DISPLAY_PAGE_COMMAND_BASE));
DISPLAY_PAGE_COMMAND_BASE));
while (SSU.SSSR.BIT.TEND == 0) {
}
IO.PDR1.BIT.B1 = 1;
Expand Down Expand Up @@ -524,7 +524,7 @@ void DisplayBlit(u8 x, u8 y, u8 width, u8 height, const u8 *raster)
while (SSU.SSSR.BIT.TDRE == 0) {
}
LCD_TX((((left / DISPLAY_COL_MSB_DIVISOR) & DISPLAY_COL_MSB_MASK) +
DISPLAY_COL_MSB_COMMAND));
DISPLAY_COL_MSB_COMMAND));
while (SSU.SSSR.BIT.TDRE == 0) {
}
LCD_TX((left & DISPLAY_COL_LSB_MASK));
Expand All @@ -534,7 +534,7 @@ void DisplayBlit(u8 x, u8 y, u8 width, u8 height, const u8 *raster)
while (SSU.SSSR.BIT.TDRE == 0) {
}
LCD_TX((g_displayBank.bytes.index * DISPLAY_BANK_PAGES + row +
DISPLAY_PAGE_COMMAND_BASE));
DISPLAY_PAGE_COMMAND_BASE));
while (SSU.SSSR.BIT.TEND == 0) {
}
IO.PDR1.BIT.B1 = 1;
Expand Down Expand Up @@ -955,7 +955,7 @@ void DisplayMessageRule(void)
while (SSU.SSSR.BIT.TDRE == 0) {
}
LCD_TX((g_displayBank.bytes.index * DISPLAY_BANK_PAGES +
DISPLAY_PAGE_COMMAND_BASE + DISPLAY_RULE_PAGE_OFFSET));
DISPLAY_PAGE_COMMAND_BASE + DISPLAY_RULE_PAGE_OFFSET));
while (SSU.SSSR.BIT.TEND == 0) {
}
IO.PDR1.BIT.B1 = 1;
Expand Down
Loading
Loading