From 3ee5749d894b922e9e197301bd7129511ebe3f71 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Thu, 5 May 2022 08:12:16 -0500 Subject: [PATCH 01/16] add littlefs module --- .gitmodules | 3 +++ littlefs | 1 + 2 files changed, 4 insertions(+) create mode 160000 littlefs diff --git a/.gitmodules b/.gitmodules index 89e424a9..54c3e634 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "tinyusb"] path = tinyusb url = https://github.com/hathach/tinyusb.git +[submodule "littlefs"] + path = littlefs + url = https://github.com/littlefs-project/littlefs.git diff --git a/littlefs b/littlefs new file mode 160000 index 00000000..40dba4a5 --- /dev/null +++ b/littlefs @@ -0,0 +1 @@ +Subproject commit 40dba4a556e0d81dfbe64301a6aa4e18ceca896c From 21026c8eb750b35c07bb3ae9fb043fd01e502a06 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Thu, 5 May 2022 12:09:47 -0400 Subject: [PATCH 02/16] add room for flash filesystem in linker script --- watch-library/hardware/linker/saml22j18.ld | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/watch-library/hardware/linker/saml22j18.ld b/watch-library/hardware/linker/saml22j18.ld index a9801509..211e5346 100755 --- a/watch-library/hardware/linker/saml22j18.ld +++ b/watch-library/hardware/linker/saml22j18.ld @@ -32,11 +32,18 @@ OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) SEARCH_DIR(.) -/* Memory Spaces Definitions */ +/* Memory Space Definitions: + * 0x00000000-0x00002000: Bootloader (length 0x2000 or 8192 bytes) + * 0x00002000-0x0003C000: Firmware (length 0x3A000 or 237568 bytes) + * 0x0003C000-0x00040000: EEPROM Emulation (length 0x2000 or 8192 bytes) + * 0x20000000-0x20008000: RAM (length 0x8000 or 32768 bytes) + */ MEMORY { - rom (rx) : ORIGIN = 0x2000, LENGTH = 0x00040000-0x2000 - ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000 + bootloader (rx) : ORIGIN = 0x0, LENGTH = 0x2000 + rom (rx) : ORIGIN = 0x2000, LENGTH = 0x00040000-0x2000-0x2000 + eeprom (r) : ORIGIN = 0x00040000-0x2000, LENGTH = 0x2000 + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000 } /* The stack size used by the application. NOTE: you need to adjust according to your application. */ From d4ebe64af05e2fa163bcea0dd699ae9c8934cc6f Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Fri, 6 May 2022 17:06:27 -0400 Subject: [PATCH 03/16] add support for a small filesystem on the watch --- apps/eeprom-emulation-upgrade/Makefile | 10 ++ apps/eeprom-emulation-upgrade/app.c | 62 +++++++++ apps/flash-test/Makefile | 13 ++ apps/flash-test/app.c | 131 ++++++++++++++++++ make.mk | 2 + watch-library/hardware/watch/watch_storage.c | 94 +++++++++++++ watch-library/shared/watch/watch.h | 1 + watch-library/shared/watch/watch_storage.h | 92 ++++++++++++ watch-library/simulator/watch/watch_storage.c | 32 +++++ 9 files changed, 437 insertions(+) create mode 100755 apps/eeprom-emulation-upgrade/Makefile create mode 100644 apps/eeprom-emulation-upgrade/app.c create mode 100755 apps/flash-test/Makefile create mode 100644 apps/flash-test/app.c create mode 100644 watch-library/hardware/watch/watch_storage.c create mode 100644 watch-library/shared/watch/watch_storage.h create mode 100644 watch-library/simulator/watch/watch_storage.c diff --git a/apps/eeprom-emulation-upgrade/Makefile b/apps/eeprom-emulation-upgrade/Makefile new file mode 100755 index 00000000..5534c178 --- /dev/null +++ b/apps/eeprom-emulation-upgrade/Makefile @@ -0,0 +1,10 @@ +TOP = ../.. +include $(TOP)/make.mk + +INCLUDES += \ + -I./ + +SRCS += \ + ./app.c + +include $(TOP)/rules.mk diff --git a/apps/eeprom-emulation-upgrade/app.c b/apps/eeprom-emulation-upgrade/app.c new file mode 100644 index 00000000..edd73e44 --- /dev/null +++ b/apps/eeprom-emulation-upgrade/app.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include "watch.h" + +void app_init(void) { +} + +void app_wake_from_backup(void) { +} + +void app_setup(void) { + delay_ms(5000); + while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY)); + uint32_t user_row = (*((uint32_t *)NVMCTRL_AUX0_ADDRESS)); + uint8_t eeprom = (user_row >> NVMCTRL_FUSES_EEPROM_SIZE_Pos) & 7; + printf("User row read successfully: 0x%lx\n", user_row); + + if (eeprom != 1) { + user_row &= ~NVMCTRL_FUSES_EEPROM_SIZE_Msk; + user_row |= NVMCTRL_FUSES_EEPROM_SIZE(1); + if (NVMCTRL->STATUS.reg & NVMCTRL_STATUS_SB) { + printf("Secured bit was set; cannot perform upgrade.\n"); + return; + } + printf("EEPROM configuration was %d.\nApplying change...\n", eeprom); + + uint32_t temp = NVMCTRL->CTRLB.reg; // Backup settings + NVMCTRL->CTRLB.reg = temp | NVMCTRL_CTRLB_CACHEDIS; // Disable Cache + NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK; // Clear error flags + NVMCTRL->ADDR.reg = NVMCTRL_AUX0_ADDRESS / 2; // Set address, command will be issued elsewhere + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMD_EAR | NVMCTRL_CTRLA_CMDEX_KEY; // Erase the user page + while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY)); // Wait for NVM command to complete + NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK; // Clear error flags + NVMCTRL->ADDR.reg = NVMCTRL_AUX0_ADDRESS / 2; // Set address, command will be issued elsewhere + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMD_PBC | NVMCTRL_CTRLA_CMDEX_KEY; // Erase the page buffer before buffering new data + while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY)); // Wait for NVM command to complete + NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK; // Clear error flags + NVMCTRL->ADDR.reg = NVMCTRL_AUX0_ADDRESS / 2; // Set address, command will be issued elsewhere + *((uint32_t *)NVMCTRL_AUX0_ADDRESS) = user_row; // write the new fuse values to the memory buffer + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMD_WAP | NVMCTRL_CTRLA_CMDEX_KEY; // Write the user page + NVMCTRL->CTRLB.reg = temp; // Restore settings + + printf("Done! Resetting...\n"); + delay_ms(1000); + NVIC_SystemReset(); + } else { + printf("EEPROM configuration was %d (8192 bytes). Upgrade successful!\n", eeprom); + } + printf("%d %d\n", eeprom, NVMCTRL->PARAM.bit.RWWEEP); +} + +void app_prepare_for_standby(void) { +} + +void app_wake_from_standby(void) { +} + +bool app_loop(void) { + return true; +} diff --git a/apps/flash-test/Makefile b/apps/flash-test/Makefile new file mode 100755 index 00000000..1e552265 --- /dev/null +++ b/apps/flash-test/Makefile @@ -0,0 +1,13 @@ +TOP = ../.. +include $(TOP)/make.mk + +INCLUDES += \ + -I$(TOP)/littlefs/ \ + -I./ + +SRCS += \ + $(TOP)/littlefs/lfs.c \ + $(TOP)/littlefs/lfs_util.c \ + ./app.c + +include $(TOP)/rules.mk diff --git a/apps/flash-test/app.c b/apps/flash-test/app.c new file mode 100644 index 00000000..17b1d0aa --- /dev/null +++ b/apps/flash-test/app.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include "watch.h" +#include "lfs.h" +#include "hpl_flash.h" + +int lfs_storage_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size); +int lfs_storage_prog(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size); +int lfs_storage_erase(const struct lfs_config *cfg, lfs_block_t block); +int lfs_storage_sync(const struct lfs_config *cfg); + +int lfs_storage_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) { + (void) cfg; + return !watch_storage_read(block, off, (void *)buffer, size); +} + +int lfs_storage_prog(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) { + (void) cfg; + return !watch_storage_write(block, off, (void *)buffer, size); +} + +int lfs_storage_erase(const struct lfs_config *cfg, lfs_block_t block) { + (void) cfg; + return !watch_storage_erase(block); +} + +int lfs_storage_sync(const struct lfs_config *cfg) { + (void) cfg; + return !watch_storage_sync(); +} + +const struct lfs_config cfg = { + // block device operations + .read = lfs_storage_read, + .prog = lfs_storage_prog, + .erase = lfs_storage_erase, + .sync = lfs_storage_sync, + + // block device configuration + .read_size = 16, + .prog_size = NVMCTRL_PAGE_SIZE, + .block_size = NVMCTRL_ROW_SIZE, + .block_count = NVMCTRL_RWWEE_PAGES / 4, + .cache_size = NVMCTRL_PAGE_SIZE, + .lookahead_size = 16, + .block_cycles = 100, +}; + +lfs_t lfs; +lfs_file_t file; + +static int _traverse_df_cb(void *p, lfs_block_t block){ + (void) block; + uint32_t *nb = p; + *nb += 1; + return 0; +} + +static int get_free_space(void){ + int err; + + uint32_t free_blocks = 0; + err = lfs_fs_traverse(&lfs, _traverse_df_cb, &free_blocks); + if(err < 0){ + return err; + } + + uint32_t available = cfg.block_count * cfg.block_size - free_blocks * cfg.block_size; + + return available; +} + +static void cb_tick(void) { + watch_date_time date_time = watch_rtc_get_date_time(); + if (date_time.unit.second == 0) { + int err = lfs_mount(&lfs, &cfg); + if (err) { + printf("Mount failed: %d\n", err); + } + // read current count + uint32_t loop_count = 0; + lfs_file_open(&lfs, &file, "loop_count", LFS_O_RDWR | LFS_O_CREAT); + lfs_file_read(&lfs, &file, &loop_count, sizeof(loop_count)); + + // update loop count + loop_count += 1; + lfs_file_rewind(&lfs, &file); + lfs_file_write(&lfs, &file, &loop_count, sizeof(loop_count)); + + // remember the storage is not updated until the file is closed successfully + lfs_file_close(&lfs, &file); + + // release any resources we were using + lfs_unmount(&lfs); + + // print the boot count + printf("loop_count: %ld\n", loop_count); + printf("free space: %d\n", get_free_space()); + } +} + +void app_init(void) { +} + +void app_wake_from_backup(void) { +} + +void app_setup(void) { + // mount the filesystem + int err = lfs_mount(&lfs, &cfg); + + // reformat if we can't mount the filesystem + // this should only happen on the first boot + if (err) { + lfs_format(&lfs, &cfg); + lfs_mount(&lfs, &cfg); + } + watch_rtc_register_tick_callback(cb_tick); +} + +void app_prepare_for_standby(void) { +} + +void app_wake_from_standby(void) { +} + +bool app_loop(void) { + return true; +} diff --git a/make.mk b/make.mk index ac5be775..07c69337 100644 --- a/make.mk +++ b/make.mk @@ -87,6 +87,7 @@ SRCS += \ $(TOP)/watch-library/hardware/watch/watch_i2c.c \ $(TOP)/watch-library/hardware/watch/watch_spi.c \ $(TOP)/watch-library/hardware/watch/watch_uart.c \ + $(TOP)/watch-library/hardware/watch/watch_storage.c \ $(TOP)/watch-library/hardware/watch/watch_deepsleep.c \ $(TOP)/watch-library/hardware/watch/watch_private.c \ $(TOP)/watch-library/hardware/watch/watch.c \ @@ -158,6 +159,7 @@ SRCS += \ $(TOP)/watch-library/simulator/watch/watch_i2c.c \ $(TOP)/watch-library/simulator/watch/watch_spi.c \ $(TOP)/watch-library/simulator/watch/watch_uart.c \ + $(TOP)/watch-library/simulator/watch/watch_storage.c \ $(TOP)/watch-library/simulator/watch/watch_deepsleep.c \ $(TOP)/watch-library/simulator/watch/watch_private.c \ $(TOP)/watch-library/simulator/watch/watch.c \ diff --git a/watch-library/hardware/watch/watch_storage.c b/watch-library/hardware/watch/watch_storage.c new file mode 100644 index 00000000..0cd04483 --- /dev/null +++ b/watch-library/hardware/watch/watch_storage.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include "watch_storage.h" + +#define RWWEE_ADDR_START NVMCTRL_RWW_EEPROM_ADDR +#define RWWEE_ADDR_END (NVMCTRL_RWW_EEPROM_ADDR + NVMCTRL_PAGE_SIZE * NVMCTRL_RWWEE_PAGES) +#define NVM_MEMORY ((volatile uint16_t *)FLASH_ADDR) + +static bool _is_valid_address(uint32_t addr, uint32_t size) { + if ((addr < NVMCTRL_RWW_EEPROM_ADDR) || (addr > (NVMCTRL_RWW_EEPROM_ADDR + NVMCTRL_PAGE_SIZE * NVMCTRL_RWWEE_PAGES))) { + return false; + } + if ((addr + size > (NVMCTRL_RWW_EEPROM_ADDR + NVMCTRL_PAGE_SIZE * NVMCTRL_RWWEE_PAGES))) { + return false; + } + + return true; +} + +bool watch_storage_read(uint32_t row, uint32_t offset, uint8_t *buffer, uint32_t size) { + uint32_t address = RWWEE_ADDR_START + row * NVMCTRL_ROW_SIZE + offset; + if (!_is_valid_address(address, size)) return false; + + uint32_t nvm_address = address / 2; + uint32_t i; + uint16_t data; + + watch_storage_sync(); + + if (address % 2) { + data = NVM_MEMORY[nvm_address++]; + buffer[0] = data >> 8; + i = 1; + } else { + i = 0; + } + + while (i < size) { + data = NVM_MEMORY[nvm_address++]; + buffer[i] = (data & 0xFF); + if (i < (size - 1)) { + buffer[i + 1] = (data >> 8); + } + i += 2; + } + return true; +} + +bool watch_storage_write(uint32_t row, uint32_t offset, const uint8_t *buffer, uint32_t size) { + uint32_t address = RWWEE_ADDR_START + row * NVMCTRL_ROW_SIZE + offset; + if (!_is_valid_address(address, size)) return false; + + watch_storage_sync(); + + uint32_t nvm_address = address / 2; + uint16_t i, data; + + hri_nvmctrl_write_CTRLA_reg(NVMCTRL, NVMCTRL_CTRLA_CMD_PBC | NVMCTRL_CTRLA_CMDEX_KEY); + watch_storage_sync(); + + for (i = 0; i < size; i += 2) { + data = buffer[i]; + if (i < NVMCTRL_PAGE_SIZE - 1) { + data |= (buffer[i + 1] << 8); + } + NVM_MEMORY[nvm_address++] = data; + } + hri_nvmctrl_write_ADDR_reg(NVMCTRL, address / 2); + hri_nvmctrl_write_CTRLA_reg(NVMCTRL, NVMCTRL_CTRLA_CMD_RWWEEWP | NVMCTRL_CTRLA_CMDEX_KEY); + + return true; +} + +bool watch_storage_erase(uint32_t row) { + uint32_t address = RWWEE_ADDR_START + row * NVMCTRL_ROW_SIZE; + if (!_is_valid_address(address, NVMCTRL_ROW_SIZE)) return false; + + watch_storage_sync(); + hri_nvmctrl_write_ADDR_reg(NVMCTRL, address / 2); + hri_nvmctrl_write_CTRLA_reg(NVMCTRL, NVMCTRL_CTRLA_CMD_RWWEEER | NVMCTRL_CTRLA_CMDEX_KEY); + + return true; +} + +bool watch_storage_sync(void) { + while (!hri_nvmctrl_get_interrupt_READY_bit(NVMCTRL)) { + // wait for flash to become ready + } + + hri_nvmctrl_clear_STATUS_reg(NVMCTRL, NVMCTRL_STATUS_MASK); + + return true; +} diff --git a/watch-library/shared/watch/watch.h b/watch-library/shared/watch/watch.h index ce85eed3..1dd8e7f7 100644 --- a/watch-library/shared/watch/watch.h +++ b/watch-library/shared/watch/watch.h @@ -64,6 +64,7 @@ #include "watch_i2c.h" #include "watch_spi.h" #include "watch_uart.h" +#include "watch_storage.h" #include "watch_deepsleep.h" #include "watch_private.h" diff --git a/watch-library/shared/watch/watch_storage.h b/watch-library/shared/watch/watch_storage.h new file mode 100644 index 00000000..6026cbcf --- /dev/null +++ b/watch-library/shared/watch/watch_storage.h @@ -0,0 +1,92 @@ +/* + * MIT License + * + * Copyright (c) 2020 Joey Castillo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef _WATCH_STORAGE_H_INCLUDED +#define _WATCH_STORAGE_H_INCLUDED +////< @file watch_storage.h + +#include "watch.h" + +#ifndef NVMCTRL_ROW_SIZE +#define NVMCTRL_ROW_SIZE 256 +#endif +#ifndef NVMCTRL_PAGE_SIZE +#define NVMCTRL_PAGE_SIZE 64 +#endif +#ifndef NVMCTRL_RWWEE_PAGES +#define NVMCTRL_RWWEE_PAGES 128 +#endif + +/** @addtogroup storage Flash Storage + * @brief This section covers functions related to the SAM L22's 8 kilobyte EEPROM emulation area. + * @details The SAM L22 inside Sensor Watch has a 256 kilobyte Flash memory array that can be + * programmed with whatever data we want. We use most of it to store the bootloader + * and the application code that runs on your wrist. The bootloader region is read-only, + * and the main application area is only writable by the bootloader (when you drag new + * code onto the WATCHBOOT drive). However! there's also a special 8 kilobyte region + * at the end of the Flash memory called the EEPROM Emulation Area. This EEPROM emulation + * area can be written or erased while the main Flash array is being read. This makes it + * super easy to work with, and useful for storing a small amount of non-volatile data that + * persists across reboots, even when power is lost. + * The functions in this section are very basic, and only cover reading and writing data + * in this area. The region is laid out as 32 rows consisting of 4 pages of 64 bytes. + * 32*4*64 = 8192 bytes. The area can be written one page at a time, but it can only be + * erased one row at a time. You can read at arbitrary word-aligned offsets within a row. + * + * ┌──────────────┬──────────────┬──────────────┬──────────────┐ + * Row 0 │ 64 bytes │ 64 bytes │ 64 bytes │ 64 bytes │ + * ├──────────────┼──────────────┼──────────────┼──────────────┤ + * Row 1 │ 64 bytes │ 64 bytes │ 64 bytes │ 64 bytes │ + * ├──────────────┼──────────────┼──────────────┼──────────────┤ + * ... │ │ │ │ │ + * ├──────────────┼──────────────┼──────────────┼──────────────┤ + * Row 31 │ 64 bytes │ 64 bytes │ 64 bytes │ 64 bytes │ + * └──────────────┴──────────────┴──────────────┴──────────────┘ + */ +/// @{ +/** @brief Reads a range of bytes from the storage area. + * @param row The row you want to read. + * @param offset The offset from the beginning of the row. + * @param buffer A buffer of at least `size` bytes. + * @param size The number of bytes you wish to read. + */ +bool watch_storage_read(uint32_t row, uint32_t offset, uint8_t *buffer, uint32_t size); + +/** @brief Writes bytes to a page in the storage area. Note that the row should already be erased before writing. + * @param row The row containing the page you want to write. + * @param offset The offset from the beginning of the row. Must be a multiple of 64. + * @param buffer The buffer containing the bytes you wish to set. + * @param size The number of bytes you wish to write. + */ +bool watch_storage_write(uint32_t row, uint32_t offset, const uint8_t *buffer, uint32_t size); + +/** @brief Erases a row in the storage area, setting all its bytes to 0xFF. + * @param row The row you want to erase. + */ +bool watch_storage_erase(uint32_t row); + +/** @brief Waits for any pending writes to complete. + */ +bool watch_storage_sync(void); +/// @} +#endif diff --git a/watch-library/simulator/watch/watch_storage.c b/watch-library/simulator/watch/watch_storage.c new file mode 100644 index 00000000..27011807 --- /dev/null +++ b/watch-library/simulator/watch/watch_storage.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include "watch_storage.h" + +uint8_t storage[NVMCTRL_ROW_SIZE * NVMCTRL_RWWEE_PAGES]; + +bool watch_storage_read(uint32_t row, uint32_t offset, uint8_t *buffer, uint32_t size) { + // printf("read row %ld offset %ld size %ld\n", row, offset, size); + memcpy(buffer, storage + row * NVMCTRL_ROW_SIZE + offset, size); + + return true; +} + +bool watch_storage_write(uint32_t row, uint32_t offset, const uint8_t *buffer, uint32_t size) { + // printf("write row %ld offset %ld size %ld\n", row, offset, size); + memcpy(storage + row * NVMCTRL_ROW_SIZE + offset, buffer, size); + + return true; +} + +bool watch_storage_erase(uint32_t row) { + // printf("erase row %ld\n", row); + memset(storage + row * NVMCTRL_ROW_SIZE, 0xff, NVMCTRL_ROW_SIZE); + + return true; +} + +bool watch_storage_sync(void) { + // nothing to do here! + return true; +} From 838102a7e9ccfddf8f901def6cad35e66bf2d0fd Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Fri, 6 May 2022 17:12:51 -0400 Subject: [PATCH 04/16] bump library version --- Doxyfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doxyfile b/Doxyfile index 5d4a1fdb..d60bad9d 100644 --- a/Doxyfile +++ b/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "Sensor Watch" -PROJECT_NUMBER = "0.0.1" +PROJECT_NUMBER = "0.0.2" PROJECT_BRIEF = "A board replacement for the classic Casio F-91W wristwatch, powered by a Microchip SAM L22 microcontroller." PROJECT_LOGO = OUTPUT_DIRECTORY = "." From 661e2b6a731da3b4b309b331bfbd40a29a69d7e9 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sun, 8 May 2022 20:19:20 -0400 Subject: [PATCH 05/16] add ability to read from USB serial --- watch-library/hardware/watch/watch_private.c | 21 ++++++++++++++++++-- watch-library/shared/watch/watch.h | 8 ++++++++ watch-library/shared/watch/watch_private.h | 5 +++-- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/watch-library/hardware/watch/watch_private.c b/watch-library/hardware/watch/watch_private.c index e4a03926..a0525d3c 100644 --- a/watch-library/hardware/watch/watch_private.c +++ b/watch-library/hardware/watch/watch_private.c @@ -255,8 +255,15 @@ int _write(int file, char *ptr, int len) { return 0; } -// this method could be overridden to read stuff from the USB console? but no need rn. -int _read(void) { +char buf[256] = {0}; + +int _read(int file, char *ptr, int len) { + (void)file; + int actual_length = strlen(buf); + if (actual_length) { + memcpy(ptr, buf, min(len, actual_length)); + return actual_length; + } return 0; } @@ -264,8 +271,18 @@ void USB_Handler(void) { tud_int_handler(0); } +static void cdc_task(void) { + if (tud_cdc_n_available(0)) { + tud_cdc_n_read(0, buf, sizeof(buf)); + } else { + memset(buf, 0, 64); + // buf[0] = 0; + } +} + void TC0_Handler(void) { tud_task(); + cdc_task(); TC0->COUNT8.INTFLAG.reg |= TC_INTFLAG_OVF; } diff --git a/watch-library/shared/watch/watch.h b/watch-library/shared/watch/watch.h index 1dd8e7f7..b307feca 100644 --- a/watch-library/shared/watch/watch.h +++ b/watch-library/shared/watch/watch.h @@ -76,4 +76,12 @@ */ bool watch_is_buzzer_or_led_enabled(void); +/** @brief Reads up to len bytes from the USB serial. + * @param file ignored, you can pass in 0 + * @param ptr pointer to a buffer of at least len bytes + * @param len the number of bytes you wish to read, max 256. + * @return The number of bytes read, or zero if no bytes were read. + */ +int read(int file, char *ptr, int len); + #endif /* WATCH_H_ */ \ No newline at end of file diff --git a/watch-library/shared/watch/watch_private.h b/watch-library/shared/watch/watch_private.h index 7bb91d1f..9d55bc21 100644 --- a/watch-library/shared/watch/watch_private.h +++ b/watch-library/shared/watch/watch_private.h @@ -44,7 +44,8 @@ void _watch_enable_usb(void); // this function ends up getting called by printf to log stuff to the USB console. int _write(int file, char *ptr, int len); -// this method could be overridden to read stuff from the USB console? but no need rn. -int _read(void); +// i thought this would be called by gets but it doesn't? anyway it does get called by read() +// so that's our mechanism for reading data from the USB serial console. +int _read(int file, char *ptr, int len); #endif From a54220d95a1c5f862152d402e60afde4bc044533 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sun, 8 May 2022 20:19:44 -0400 Subject: [PATCH 06/16] add littlefs filesystem browser app --- apps/filesystem-browser/Makefile | 13 ++ apps/filesystem-browser/browser.c | 218 ++++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100755 apps/filesystem-browser/Makefile create mode 100644 apps/filesystem-browser/browser.c diff --git a/apps/filesystem-browser/Makefile b/apps/filesystem-browser/Makefile new file mode 100755 index 00000000..a37c98d7 --- /dev/null +++ b/apps/filesystem-browser/Makefile @@ -0,0 +1,13 @@ +TOP = ../.. +include $(TOP)/make.mk + +INCLUDES += \ + -I$(TOP)/littlefs/ \ + -I./ + +SRCS += \ + $(TOP)/littlefs/lfs.c \ + $(TOP)/littlefs/lfs_util.c \ + ./browser.c + +include $(TOP)/rules.mk diff --git a/apps/filesystem-browser/browser.c b/apps/filesystem-browser/browser.c new file mode 100644 index 00000000..daa77863 --- /dev/null +++ b/apps/filesystem-browser/browser.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include "watch.h" +#include "lfs.h" +#include "hpl_flash.h" + +int lfs_storage_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size); +int lfs_storage_prog(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size); +int lfs_storage_erase(const struct lfs_config *cfg, lfs_block_t block); +int lfs_storage_sync(const struct lfs_config *cfg); + +int lfs_storage_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) { + (void) cfg; + return !watch_storage_read(block, off, (void *)buffer, size); +} + +int lfs_storage_prog(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) { + (void) cfg; + return !watch_storage_write(block, off, (void *)buffer, size); +} + +int lfs_storage_erase(const struct lfs_config *cfg, lfs_block_t block) { + (void) cfg; + return !watch_storage_erase(block); +} + +int lfs_storage_sync(const struct lfs_config *cfg) { + (void) cfg; + return !watch_storage_sync(); +} + +const struct lfs_config cfg = { + // block device operations + .read = lfs_storage_read, + .prog = lfs_storage_prog, + .erase = lfs_storage_erase, + .sync = lfs_storage_sync, + + // block device configuration + .read_size = 16, + .prog_size = NVMCTRL_PAGE_SIZE, + .block_size = NVMCTRL_ROW_SIZE, + .block_count = NVMCTRL_RWWEE_PAGES / 4, + .cache_size = NVMCTRL_PAGE_SIZE, + .lookahead_size = 16, + .block_cycles = 100, +}; + +lfs_t lfs; +lfs_file_t file; + +static int _traverse_df_cb(void *p, lfs_block_t block) { + (void) block; + uint32_t *nb = p; + *nb += 1; + return 0; +} + +static int get_free_space(void){ + int err; + + uint32_t free_blocks = 0; + err = lfs_fs_traverse(&lfs, _traverse_df_cb, &free_blocks); + if(err < 0){ + return err; + } + + uint32_t available = cfg.block_count * cfg.block_size - free_blocks * cfg.block_size; + + return available; +} + +static int lfs_ls(lfs_t *lfs, const char *path) { + lfs_dir_t dir; + int err = lfs_dir_open(lfs, &dir, path); + if (err) { + return err; + } + + struct lfs_info info; + while (true) { + int res = lfs_dir_read(lfs, &dir, &info); + if (res < 0) { + return res; + } + + if (res == 0) { + break; + } + + switch (info.type) { + case LFS_TYPE_REG: printf("file "); break; + case LFS_TYPE_DIR: printf("dir "); break; + default: printf("? "); break; + } + + printf("%4ld bytes ", info.size); + + printf("%s\n", info.name); + } + + err = lfs_dir_close(lfs, &dir); + if (err) { + return err; + } + + return 0; +} + +void app_init(void) { +} + +void app_wake_from_backup(void) { +} + +void app_setup(void) { + // mount the filesystem + int err = lfs_mount(&lfs, &cfg); + + // reformat if we can't mount the filesystem + // this should only happen on the first boot + if (err) { + lfs_format(&lfs, &cfg); + lfs_mount(&lfs, &cfg); + } +} + +void app_prepare_for_standby(void) { +} + +void app_wake_from_standby(void) { +} + +bool app_loop(void) { + char line[256] = {0}; + char *buf; + struct lfs_info info; + + read(0, line, 256); + if (strlen(line)) { + printf("$ %s", line); + char *command = strtok(line, " \n"); + + if (strcmp(command, "ls") == 0) { + char *directory = strtok(NULL, " \n"); + if (directory == NULL) { + lfs_ls(&lfs, "/"); + } else { + printf("usage: ls\n"); + } + } else if (strcmp(command, "cat") == 0) { + char *filename = strtok(NULL, " \n"); + if (filename == NULL) { + printf("usage: cat file\n"); + } else { + info.type = 0; + lfs_stat(&lfs, filename, &info); + if (info.type == LFS_TYPE_REG) { + if (info.size > 0) { + buf = malloc(info.size); + lfs_file_open(&lfs, &file, filename, LFS_O_RDONLY); + lfs_file_read(&lfs, &file, buf, info.size); + printf("%s\n", buf); + free(buf); + lfs_file_close(&lfs, &file); + } else { + printf("\n"); + } + } else { + printf("cat: %s: No such file\n", filename); + } + } + } else if (strcmp(command, "df") == 0) { + printf("free space: %d bytes\n", get_free_space()); + } else if (strcmp(command, "rm") == 0) { + char *filename = strtok(NULL, " \n"); + if (filename == NULL) { + printf("usage: rm file\n"); + } else { + info.type = 0; + lfs_stat(&lfs, filename, &info); + if (info.type == LFS_TYPE_REG) { + lfs_remove(&lfs, filename); + } else { + printf("rm: %s: No such file\n", filename); + } + } + } else if (strcmp(command, "echo") == 0) { + buf = malloc(64); + memset(buf, 0, 64); + size_t pos = 0; + char *text = strtok(NULL, " \n"); + while (strcmp(text, ">")) { + sprintf(buf + pos, "%s ", text); + pos += strlen(text) + 1; + text = strtok(NULL, " \n"); + if (text == NULL) break; + } + buf[strlen(buf) - 1] = 0; + char *filename = strtok(NULL, " \n"); + if (filename == NULL) { + printf("usage: echo text > file\n"); + } else if (strchr(filename, '/') || strchr(filename, '\\')) { + printf("subdirectories are not supported\n"); + } else { + lfs_file_open(&lfs, &file, filename, LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC); + lfs_file_write(&lfs, &file, buf, strlen(buf)); + lfs_file_close(&lfs, &file); + } + free(buf); + } else { + printf("%s: command not found\n", command); + } + } + return false; +} From 0c87114ee13a0b9124c48699a63f6631974a5bf7 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 9 May 2022 10:54:13 -0400 Subject: [PATCH 07/16] simulator: fix _read function signature --- watch-library/simulator/watch/watch_private.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/watch-library/simulator/watch/watch_private.c b/watch-library/simulator/watch/watch_private.c index 4ddc2182..3425341a 100644 --- a/watch-library/simulator/watch/watch_private.c +++ b/watch-library/simulator/watch/watch_private.c @@ -63,7 +63,7 @@ int _write(int file, char *ptr, int len) { return 0; } -// this method could be overridden to read stuff from the USB console? but no need rn. -int _read(void) { +int _read(int file, char *ptr, int len) { + // TODO: hook to UI return 0; } From 03b616da62da4034aab1b4c3cade13d15cdecd77 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 9 May 2022 10:57:16 -0400 Subject: [PATCH 08/16] prevent buf from escaping into global namespace --- watch-library/hardware/watch/watch_private.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/watch-library/hardware/watch/watch_private.c b/watch-library/hardware/watch/watch_private.c index a0525d3c..e651f3eb 100644 --- a/watch-library/hardware/watch/watch_private.c +++ b/watch-library/hardware/watch/watch_private.c @@ -255,7 +255,7 @@ int _write(int file, char *ptr, int len) { return 0; } -char buf[256] = {0}; +static char buf[256] = {0}; int _read(int file, char *ptr, int len) { (void)file; From 32c6974c0f16882258e5b4c041a2a210e4802d0f Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 9 May 2022 10:59:15 -0400 Subject: [PATCH 09/16] usb serial: clear whole length of buffer --- watch-library/hardware/watch/watch_private.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/watch-library/hardware/watch/watch_private.c b/watch-library/hardware/watch/watch_private.c index e651f3eb..a197e522 100644 --- a/watch-library/hardware/watch/watch_private.c +++ b/watch-library/hardware/watch/watch_private.c @@ -275,8 +275,7 @@ static void cdc_task(void) { if (tud_cdc_n_available(0)) { tud_cdc_n_read(0, buf, sizeof(buf)); } else { - memset(buf, 0, 64); - // buf[0] = 0; + memset(buf, 0, 256); } } From 91b436237aaf39e2328b9380042aa40e06faa4cb Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 9 May 2022 12:03:24 -0400 Subject: [PATCH 10/16] fix warning re: buffer overrun --- .../watch_faces/sensor/accelerometer_data_acquisition_face.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c index 6ef9c6db..9da8b35c 100644 --- a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c +++ b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c @@ -282,7 +282,7 @@ static void update(accelerometer_data_acquisition_state_t *state) { } static void update_settings(accelerometer_data_acquisition_state_t *state) { - char buf[12]; + char buf[13]; watch_clear_colon(); if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL); else watch_clear_indicator(WATCH_INDICATOR_BELL); From 21ee056e26c3e158cf56f8577169f86b8d577a65 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 9 May 2022 12:14:42 -0400 Subject: [PATCH 11/16] first pass at filesystem in movement --- movement/filesystem.c | 243 +++++++++++++++++++++++++++++++++++++++++ movement/filesystem.h | 79 ++++++++++++++ movement/make/Makefile | 4 + 3 files changed, 326 insertions(+) create mode 100644 movement/filesystem.c create mode 100644 movement/filesystem.h diff --git a/movement/filesystem.c b/movement/filesystem.c new file mode 100644 index 00000000..2a1319fb --- /dev/null +++ b/movement/filesystem.c @@ -0,0 +1,243 @@ +#include +#include +#include +#include +#include "filesystem.h" +#include "watch.h" +#include "lfs.h" +#include "hpl_flash.h" + +int lfs_storage_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size); +int lfs_storage_prog(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size); +int lfs_storage_erase(const struct lfs_config *cfg, lfs_block_t block); +int lfs_storage_sync(const struct lfs_config *cfg); + +int lfs_storage_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) { + (void) cfg; + return !watch_storage_read(block, off, (void *)buffer, size); +} + +int lfs_storage_prog(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) { + (void) cfg; + return !watch_storage_write(block, off, (void *)buffer, size); +} + +int lfs_storage_erase(const struct lfs_config *cfg, lfs_block_t block) { + (void) cfg; + return !watch_storage_erase(block); +} + +int lfs_storage_sync(const struct lfs_config *cfg) { + (void) cfg; + return !watch_storage_sync(); +} + +const struct lfs_config cfg = { + // block device operations + .read = lfs_storage_read, + .prog = lfs_storage_prog, + .erase = lfs_storage_erase, + .sync = lfs_storage_sync, + + // block device configuration + .read_size = 16, + .prog_size = NVMCTRL_PAGE_SIZE, + .block_size = NVMCTRL_ROW_SIZE, + .block_count = NVMCTRL_RWWEE_PAGES / 4, + .cache_size = NVMCTRL_PAGE_SIZE, + .lookahead_size = 16, + .block_cycles = 100, +}; + +static lfs_t lfs; +static lfs_file_t file; +static struct lfs_info info; + +static int _traverse_df_cb(void *p, lfs_block_t block) { + (void) block; + uint32_t *nb = p; + *nb += 1; + return 0; +} + +int filesystem_get_free_space(void) { + int err; + + uint32_t free_blocks = 0; + err = lfs_fs_traverse(&lfs, _traverse_df_cb, &free_blocks); + if(err < 0){ + return err; + } + + uint32_t available = cfg.block_count * cfg.block_size - free_blocks * cfg.block_size; + + return available; +} + +static int filesystem_ls(lfs_t *lfs, const char *path) { + lfs_dir_t dir; + int err = lfs_dir_open(lfs, &dir, path); + if (err) { + return err; + } + + struct lfs_info info; + while (true) { + int res = lfs_dir_read(lfs, &dir, &info); + if (res < 0) { + return res; + } + + if (res == 0) { + break; + } + + switch (info.type) { + case LFS_TYPE_REG: printf("file "); break; + case LFS_TYPE_DIR: printf("dir "); break; + default: printf("? "); break; + } + + printf("%4ld bytes ", info.size); + + printf("%s\n", info.name); + } + + err = lfs_dir_close(lfs, &dir); + if (err) { + return err; + } + + return 0; +} + +bool filesystem_init(void) { + int err = lfs_mount(&lfs, &cfg); + + // reformat if we can't mount the filesystem + // this should only happen on the first boot + if (err) { + err = lfs_format(&lfs, &cfg); + if (err) return false; + err = lfs_mount(&lfs, &cfg) == LFS_ERR_OK; + } + + return err == LFS_ERR_OK; +} + +bool filesystem_file_exists(char *filename) { + info.type = 0; + lfs_stat(&lfs, filename, &info); + return info.type == LFS_TYPE_REG; +} + +bool filesystem_rm(char *filename) { + info.type = 0; + lfs_stat(&lfs, filename, &info); + if (filesystem_file_exists(filename)) { + return lfs_remove(&lfs, filename) == LFS_ERR_OK; + } else { + printf("rm: %s: No such file\n", filename); + return false; + } +} + +static int32_t filesystem_get_file_size(char *filename) { + if (filesystem_file_exists(filename)) { + return info.size; // info struct was just populated by filesystem_file_exists + } + + return -1; +} + +bool filesystem_read_file(char *filename, char *buf, int32_t length) { + memset(buf, 0, length); + int32_t file_size = filesystem_get_file_size(filename); + if (file_size > 0) { + int err = lfs_file_open(&lfs, &file, filename, LFS_O_RDONLY); + if (err) return false; + err = lfs_file_read(&lfs, &file, buf, min(length, file_size)); + if (err) return false; + return lfs_file_close(&lfs, &file) == LFS_ERR_OK; + } + + return false; +} + +static void filesystem_cat(char *filename) { + info.type = 0; + lfs_stat(&lfs, filename, &info); + if (filesystem_file_exists(filename)) { + if (info.size > 0) { + char *buf = malloc(info.size + 1); + filesystem_read_file(filename, buf, info.size); + printf("%s\n", buf); + free(buf); + } else { + printf("\n"); + } + } else { + printf("cat: %s: No such file\n", filename); + } +} + +bool filesystem_write_file(char *filename, char *text, int32_t length) { + int err = lfs_file_open(&lfs, &file, filename, LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC); + if (err) return false; + err = lfs_file_write(&lfs, &file, text, length); + if (err) return false; + return lfs_file_close(&lfs, &file) == LFS_ERR_OK; +} + +void filesystem_process_command(char *line) { + printf("$ %s", line); + char *command = strtok(line, " \n"); + + if (strcmp(command, "ls") == 0) { + char *directory = strtok(NULL, " \n"); + if (directory == NULL) { + filesystem_ls(&lfs, "/"); + } else { + printf("usage: ls\n"); + } + } else if (strcmp(command, "cat") == 0) { + char *filename = strtok(NULL, " \n"); + if (filename == NULL) { + printf("usage: cat file\n"); + } else { + filesystem_cat(filename); + } + } else if (strcmp(command, "df") == 0) { + printf("free space: %d bytes\n", filesystem_get_free_space()); + } else if (strcmp(command, "rm") == 0) { + char *filename = strtok(NULL, " \n"); + if (filename == NULL) { + printf("usage: rm file\n"); + } else { + filesystem_rm(filename); + } + } else if (strcmp(command, "echo") == 0) { + char *text = malloc(248); + memset(text, 0, 248); + size_t pos = 0; + char *word = strtok(NULL, " \n"); + while (strcmp(word, ">")) { + sprintf(text + pos, "%s ", word); + pos += strlen(word) + 1; + word = strtok(NULL, " \n"); + if (word == NULL) break; + } + text[strlen(text) - 1] = 0; + char *filename = strtok(NULL, " \n"); + if (filename == NULL) { + printf("usage: echo text > file\n"); + } else if (strchr(filename, '/') || strchr(filename, '\\')) { + printf("subdirectories are not supported\n"); + } else { + filesystem_write_file(filename, text, strlen(text)); + } + free(text); + } else { + printf("%s: command not found\n", command); + } +} diff --git a/movement/filesystem.h b/movement/filesystem.h new file mode 100644 index 00000000..c9a5804c --- /dev/null +++ b/movement/filesystem.h @@ -0,0 +1,79 @@ +/* + * MIT License + * + * Copyright (c) 2022 Joey Castillo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef FILESYSTEM_H_ +#define FILESYSTEM_H_ +#include +#include +#include "watch.h" + +/** @brief Initializes and mounts the tiny 8kb filesystem, formatting it if need be. + * @return true if the filesystem was mounted successfully. + */ +bool filesystem_init(void); + +/** @brief Gets the space available on the filesystem. + * @return the free space in bytes + */ +int filesystem_get_free_space(void); + +/** @brief Checks for the existence of a file on the filesystem. + * @param filename the file you wish to check + * @return true if the file exists; false otherwise + */ +bool filesystem_file_exists(char *filename); + +/** @brief Removes a file on the filesystem. + * @param filename the file you wish to remove + * @return true if the file was deleted successfully; false otherwise + */ +bool filesystem_rm(char *filename); + +/** @brief Reads a file from the filesystem into a buffer + * @param filename the file you wish to read + * @param buf A buffer of at least length bytes; the file will be read into this buffer + * @param length The number of bytes to read + * @return true if the read was successful; false otherwise + * @note This function will set buf to zero and read all bytes of the file into the buffer. + * If you are reading a raw value (say you wrote a uint32 to a file), you can read back + * the value by passing in the file's length for length. If you wish to treat the buffer + * as a null-terminated string, allocate a buffer one byte longer than the file's length, + * and the last byte will be guaranteed to be 0. + */ +bool filesystem_read_file(char *filename, char *buf, int32_t length); + +/** @brief Writes file to the filesystem + * @param filename the file you wish to write + * @param text The contents of the file + * @param length The number of bytes to write + * @return true if the write was successful; false otherwise + */ +bool filesystem_write_file(char *filename, char *text, int32_t length); + +/** @brief Handles the interactive file browser when Movement is plugged in to USB. + * @param line The command that the user typed into the serial console. + */ +void filesystem_process_command(char *line); + +#endif // FILESYSTEM_H_ diff --git a/movement/make/Makefile b/movement/make/Makefile index 0936a26c..1c880fc2 100755 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -16,6 +16,7 @@ INCLUDES += \ -I../watch_faces/complication/ \ -I../watch_faces/sensor/ \ -I../watch_faces/demo/ \ + -I../../littlefs/ \ -I../lib/TOTP-MCU/ \ -I../lib/sunriset/ \ -I../lib/vsop87/ \ @@ -33,7 +34,10 @@ SRCS += \ ../lib/sunriset/sunriset.c \ ../lib/vsop87/vsop87a_milli.c \ ../lib/astrolib/astrolib.c \ + ../../littlefs/lfs.c \ + ../../littlefs/lfs_util.c \ ../movement.c \ + ../filesystem.c \ ../watch_faces/clock/simple_clock_face.c \ ../watch_faces/clock/world_clock_face.c \ ../watch_faces/clock/beats_face.c \ From 22b1ac0283a6aed800ea86960305284199747cdc Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 9 May 2022 12:55:58 -0400 Subject: [PATCH 12/16] simulator: add USB serial input field --- movement/movement.c | 30 +++++++++++++++++++++++++++ watch-library/hardware/watch/watch.c | 4 ++++ watch-library/shared/watch/watch.h | 4 ++++ watch-library/simulator/shell.html | 9 ++++++++ watch-library/simulator/watch/watch.c | 4 ++++ 5 files changed, 51 insertions(+) diff --git a/movement/movement.c b/movement/movement.c index 4a5bc04e..72c087e9 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -25,7 +25,11 @@ #include #include #include +#include +#include +#include #include "watch.h" +#include "filesystem.h" #include "movement.h" #ifndef MOVEMENT_FIRMWARE @@ -423,6 +427,32 @@ bool app_loop(void) { } } + // if we are plugged into USB, handle the file browser tasks + if (watch_is_usb_enabled()) { + char line[256] = {0}; +#if __EMSCRIPTEN__ + // This is a terrible hack; ideally this should be handled deeper in the watch library. + // Alas, emscripten treats read() as something that should pop up an input box, so I + // wasn't able to implement this over there. I sense that this relates to read() being + // the wrong way to read data from USB (like we should be using fgets or something), but + // until I untangle that, this will have to do. + char *received_data = (char*)EM_ASM_INT({ + var len = lengthBytesUTF8(tx) + 1; + var s = _malloc(len); + stringToUTF8(tx, s, len); + return s; + }); + memcpy(line, received_data, min(255, strlen(received_data))); + free(received_data); + EM_ASM({ + tx = ""; + }); +#else + read(0, line, 256); +#endif + if (strlen(line)) printf(line); + } + event.subsecond = 0; return can_sleep && (movement_state.light_ticks == -1) && !movement_state.is_buzzing; diff --git a/watch-library/hardware/watch/watch.c b/watch-library/hardware/watch/watch.c index 32bbccbb..b3dc4e8d 100644 --- a/watch-library/hardware/watch/watch.c +++ b/watch-library/hardware/watch/watch.c @@ -41,3 +41,7 @@ void SYSTEM_Handler(void) { bool watch_is_buzzer_or_led_enabled(void){ return hri_mclk_get_APBCMASK_TCC0_bit(MCLK); } + +bool watch_is_usb_enabled(void) { + return USB->DEVICE.CTRLA.bit.ENABLE; +} diff --git a/watch-library/shared/watch/watch.h b/watch-library/shared/watch/watch.h index b307feca..b94d36fb 100644 --- a/watch-library/shared/watch/watch.h +++ b/watch-library/shared/watch/watch.h @@ -76,6 +76,10 @@ */ bool watch_is_buzzer_or_led_enabled(void); +/** @brief Returns true if USB is enabled. + */ +bool watch_is_usb_enabled(void); + /** @brief Reads up to len bytes from the USB serial. * @param file ignored, you can pass in 0 * @param ptr pointer to a buffer of at least len bytes diff --git a/watch-library/simulator/shell.html b/watch-library/simulator/shell.html index 2f560aba..80e1e2ea 100644 --- a/watch-library/simulator/shell.html +++ b/watch-library/simulator/shell.html @@ -323,6 +323,9 @@
+ + +