diff --git a/.gitignore b/.gitignore index 79e7608..9b759bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .idea/ .vscode/ -.vs/ \ No newline at end of file +.vs/ + +.DS_Store \ No newline at end of file diff --git a/firmware/application/Makefile b/firmware/application/Makefile index f248a1a..c0e0122 100644 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -11,6 +11,7 @@ SRC_FILES += \ $(PROJ_DIR)/app_cmd.c \ $(PROJ_DIR)/ble_main.c \ $(PROJ_DIR)/rfid_main.c \ + $(PROJ_DIR)/settings.c \ $(PROJ_DIR)/usb_main.c \ $(PROJ_DIR)/rgb_marquee.c \ $(PROJ_DIR)/bsp/bsp_delay.c \ diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index 2eb7559..27e2e52 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -12,6 +12,7 @@ #include "app_status.h" #include "tag_persistence.h" #include "nrf_pwr_mgmt.h" +#include "settings.h" #define NRF_LOG_MODULE_NAME app_cmd @@ -80,6 +81,26 @@ data_frame_tx_t* cmd_processor_get_device_address(uint16_t cmd, uint16_t status, return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 6, (uint8_t*)(&device_address[0])); } +data_frame_tx_t* cmd_processor_save_settings(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + status = settings_save_config(); + return data_frame_make(cmd, status, 0, NULL); +} + +data_frame_tx_t* cmd_processor_set_animation_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + if (length == 1) { + settings_set_animation_config(data[0]); + } + else { + status = STATUS_PAR_ERR; + } + return data_frame_make(cmd, status, 0, NULL); +} + +data_frame_tx_t* cmd_processor_get_animation_mode(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + uint8_t animation_mode = settings_get_animation_config(); + return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 1, (uint8_t *)(&animation_mode)); +} + #if defined(PROJECT_CHAMELEON_ULTRA) data_frame_tx_t* cmd_processor_14a_scan(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { @@ -524,6 +545,9 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_ENTER_BOOTLOADER, NULL, cmd_processor_enter_bootloader, NULL }, { DATA_CMD_GET_DEVICE_CHIP_ID, NULL, cmd_processor_get_device_chip_id, NULL }, { DATA_CMD_GET_DEVICE_ADDRESS, NULL, cmd_processor_get_device_address, NULL }, + { DATA_CMD_SAVE_SETTINGS, NULL, cmd_processor_save_settings, NULL }, + { DATA_CMD_SET_ANIMATION_MODE, NULL, cmd_processor_set_animation_mode, NULL }, + { DATA_CMD_GET_ANIMATION_MODE, NULL, cmd_processor_get_animation_mode, NULL }, #if defined(PROJECT_CHAMELEON_ULTRA) diff --git a/firmware/application/src/app_main.c b/firmware/application/src/app_main.c index 2c4a529..387bd5e 100644 --- a/firmware/application/src/app_main.c +++ b/firmware/application/src/app_main.c @@ -36,6 +36,8 @@ NRF_LOG_MODULE_REGISTER(); #include "usb_main.h" #include "rgb_marquee.h" +#include "settings.h" + // Defining soft timers APP_TIMER_DEF(m_button_check_timer); // Timer for button debounce @@ -219,22 +221,25 @@ static void system_off_enter(void) { for (uint8_t i = 0; i < RGB_LIST_NUM; i++) { nrf_gpio_pin_clear(p_led_array[i]); } - uint8_t slot = tag_emulation_get_slot(); - // Power off animation - uint8_t dir = slot > 3 ? 1 : 0; - uint8_t color = get_color_by_slot(slot); - if (m_reset_source & (NRF_POWER_RESETREAS_NFC_MASK | NRF_POWER_RESETREAS_LPCOMP_MASK)) { - if (m_reset_source & NRF_POWER_RESETREAS_NFC_MASK) { - color = 1; - } else { - color = 2; + uint8_t animation_config = settings_get_animation_config(); + if (animation_config == SETTINGS_ANIMATION_FULL) { + uint8_t slot = tag_emulation_get_slot(); + // Power off animation + uint8_t dir = slot > 3 ? 1 : 0; + uint8_t color = get_color_by_slot(slot); + if (m_reset_source & (NRF_POWER_RESETREAS_NFC_MASK | NRF_POWER_RESETREAS_LPCOMP_MASK)) { + if (m_reset_source & NRF_POWER_RESETREAS_NFC_MASK) { + color = 1; + } else { + color = 2; + } } + ledblink5(color, slot, dir ? 7 : 0); + ledblink4(color, dir, 7, 99, 75); + ledblink4(color, !dir, 7, 75, 50); + ledblink4(color, dir, 7, 50, 25); + ledblink4(color, !dir, 7, 25, 0); } - ledblink5(color, slot, dir ? 7 : 0); - ledblink4(color, dir, 7, 99, 75); - ledblink4(color, !dir, 7, 75, 50); - ledblink4(color, dir, 7, 50, 25); - ledblink4(color, !dir, 7, 25, 0); rgb_marquee_stop(); } @@ -346,9 +351,17 @@ static void check_wakeup_src(void) { advertising_start(); // Turn on Bluetooth radio // Button wake-up boot animation - ledblink2(color, !dir, 11); - ledblink2(color, dir, 11); - ledblink2(color, !dir, dir ? slot : 7 - slot); + uint8_t animation_config = settings_get_animation_config(); + if (animation_config == SETTINGS_ANIMATION_FULL) + { + ledblink2(color, !dir, 11); + ledblink2(color, dir, 11); + ledblink2(color, !dir, dir ? slot : 7 - slot); + } + else if (animation_config == SETTINGS_ANIMATION_MINIMAL) { + ledblink2(color, !dir, dir ? slot : 7 - slot); + } + // The indicator of the current card slot lights up at the end of the animation light_up_by_slot(); @@ -374,8 +387,11 @@ static void check_wakeup_src(void) { // 当前是模拟卡事件唤醒系统,我们可以让场强灯先亮起来 TAG_FIELD_LED_ON(); - // In the case of field wake-up, only one round of RGB is swept as the power-on animation - ledblink2(color, !dir, dir ? slot : 7 - slot); + uint8_t animation_config = settings_get_animation_config(); + if (animation_config == SETTINGS_ANIMATION_FULL) { + // In the case of field wake-up, only one round of RGB is swept as the power-on animation + ledblink2(color, !dir, dir ? slot : 7 - slot); + } set_slot_light_color(color); light_up_by_slot(); @@ -517,6 +533,8 @@ int main(void) { tag_emulation_init(); // Analog card initialization rgb_marquee_init(); // Light effect initialization + settings_load_config(); // Load settings from flash + // cmd callback register on_data_frame_complete(on_data_frame_received); diff --git a/firmware/application/src/data_cmd.h b/firmware/application/src/data_cmd.h index e5437ae..89284f2 100644 --- a/firmware/application/src/data_cmd.h +++ b/firmware/application/src/data_cmd.h @@ -20,6 +20,9 @@ #define DATA_CMD_ENTER_BOOTLOADER (1010) #define DATA_CMD_GET_DEVICE_CHIP_ID (1011) #define DATA_CMD_GET_DEVICE_ADDRESS (1012) +#define DATA_CMD_SAVE_SETTINGS (1013) +#define DATA_CMD_SET_ANIMATION_MODE (1014) +#define DATA_CMD_GET_ANIMATION_MODE (1015) // // ****************************************************************** diff --git a/firmware/application/src/rfid/nfctag/tag_emulation.c b/firmware/application/src/rfid/nfctag/tag_emulation.c index dfa1ff1..849cd03 100644 --- a/firmware/application/src/rfid/nfctag/tag_emulation.c +++ b/firmware/application/src/rfid/nfctag/tag_emulation.c @@ -3,6 +3,7 @@ #include "lf_tag_em.h" #include "nfc_mf1.h" #include "nfc_ntag.h" +#include "fds_ids.h" #include "fds_util.h" #include "tag_emulation.h" #include "tag_persistence.h" diff --git a/firmware/application/src/rfid/nfctag/tag_persistence.c b/firmware/application/src/rfid/nfctag/tag_persistence.c index 2bf7a38..4997be2 100644 --- a/firmware/application/src/rfid/nfctag/tag_persistence.c +++ b/firmware/application/src/rfid/nfctag/tag_persistence.c @@ -1,5 +1,5 @@ #include "tag_persistence.h" - +#include "fds_ids.h" #define NRF_LOG_MODULE_NAME tag_persistence #include "nrf_log.h" diff --git a/firmware/application/src/rfid/nfctag/tag_persistence.h b/firmware/application/src/rfid/nfctag/tag_persistence.h index d25982e..21b51d0 100644 --- a/firmware/application/src/rfid/nfctag/tag_persistence.h +++ b/firmware/application/src/rfid/nfctag/tag_persistence.h @@ -5,27 +5,6 @@ #include "tag_base_type.h" -/* - * 卡槽配置,只有一份,一致即可 - */ -#define FDS_CONFIG_RECORD_FILE_KEY 0x1066 -#define FDS_CONFIG_RECORD_FILE_ID 0x1066 - -/* - * 每个卡槽有高低频两种数据,其中key是跟卡槽走的,而id+n就等于数据索引,固定某个索引为指定类型即可 - * 每个slot的file_key都不一样 - * 每个slot有两种类型的卡片,因此有两个数据ID(当前) - */ -#define FDS_SLOT_TAG_DUMP_FILE_KEY 0x1067 -#define FDS_SLOT_TAG_DUMP_FILE_ID 0x1067 - -/* - * 每个卡槽有高低频两种数据,因此卡槽的昵称也要有两种,其中key是跟卡槽走的,而id+n就等于数据索引,固定某个索引为指定类型即可 - */ -#define FDS_SLOT_TAG_NICK_NAME_KEY 0x1068 -#define FDS_SLOT_TAG_NICK_NAME_ID 0x1068 - - typedef struct { uint16_t key; uint16_t id; diff --git a/firmware/application/src/settings.c b/firmware/application/src/settings.c new file mode 100644 index 0000000..1dc03f5 --- /dev/null +++ b/firmware/application/src/settings.c @@ -0,0 +1,63 @@ +#include +#include "crc_utils.h" +#include "app_status.h" +#include "settings.h" +#include "fds_ids.h" +#include "fds_util.h" + +#define NRF_LOG_MODULE_NAME settings +#include "nrf_log.h" +#include "nrf_log_ctrl.h" +#include "nrf_log_default_backends.h" +NRF_LOG_MODULE_REGISTER(); + +static settings_data_t config; + +static uint16_t m_config_crc; + +void settings_load_config(void) +{ + bool ret = fds_read_sync(FDS_SETTINGS_ID, FDS_SETTINGS_KEY, sizeof(config), (uint8_t *)&config); + if (ret) { + // After the reading is complete, we first save a copy of the current CRC, which can be used as a reference for comparison of changes when saving later + calc_14a_crc_lut((uint8_t *)&config, sizeof(config), (uint8_t *)&m_config_crc); + NRF_LOG_INFO("Load config done."); + } else { + NRF_LOG_INFO("config no exists."); + } +} + +uint8_t settings_save_config(void) +{ + // We are saving the configuration, we need to calculate the crc code of the current configuration to judge whether the following data is updated + uint16_t new_calc_crc; + calc_14a_crc_lut((uint8_t *)&config, sizeof(config), (uint8_t *)&new_calc_crc); + if (new_calc_crc != m_config_crc) { // Before saving, make sure that the configuration has changed + NRF_LOG_INFO("Save tag slot config start."); + bool ret = fds_write_sync(FDS_CONFIG_RECORD_FILE_ID, FDS_CONFIG_RECORD_FILE_KEY, sizeof(config) / 4, (uint8_t *)&config); + if (ret) { + NRF_LOG_INFO("Save tag slot config success."); + m_config_crc = new_calc_crc; // store new CRC so we know that we've updated the configuration + } + else + { + NRF_LOG_ERROR("Save tag slot config error."); + return STATUS_FLASH_WRITE_FAIL; + } + } else { + NRF_LOG_INFO("Tag slot config no change."); + } + + return STATUS_DEVICE_SUCCESS; +} + +uint8_t settings_get_animation_config() +{ + return config & 0x3; +} + +void settings_set_animation_config(uint8_t value) +{ + config &= ~(0x3); + config |= value; +} diff --git a/firmware/application/src/settings.h b/firmware/application/src/settings.h new file mode 100644 index 0000000..a42baa3 --- /dev/null +++ b/firmware/application/src/settings.h @@ -0,0 +1,21 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +#include + +#define SETTINGS_ANIMATION_FULL 0 +#define SETTINGS_ANIMATION_MINIMAL 1 +#define SETTINGS_ANIMATION_NONE 2 + +/* + * bits [0-1]: animation config + * bits [2-31]: reserved + */ +typedef uint32_t settings_data_t; + +void settings_load_config(void); +uint8_t settings_save_config(void); +uint8_t settings_get_animation_config(); +void settings_set_animation_config(uint8_t value); + +#endif \ No newline at end of file diff --git a/firmware/application/src/utils/fds_ids.h b/firmware/application/src/utils/fds_ids.h new file mode 100644 index 0000000..ca11e2e --- /dev/null +++ b/firmware/application/src/utils/fds_ids.h @@ -0,0 +1,30 @@ +#ifndef FDS_IDS_H +#define FDS_IDS_H + +/* + * 卡槽配置,只有一份,一致即可 + */ +#define FDS_CONFIG_RECORD_FILE_KEY 0x1066 +#define FDS_CONFIG_RECORD_FILE_ID 0x1066 + +/* + * 每个卡槽有高低频两种数据,其中key是跟卡槽走的,而id+n就等于数据索引,固定某个索引为指定类型即可 + * 每个slot的file_key都不一样 + * 每个slot有两种类型的卡片,因此有两个数据ID(当前) + */ +#define FDS_SLOT_TAG_DUMP_FILE_KEY 0x1067 +#define FDS_SLOT_TAG_DUMP_FILE_ID 0x1067 + +/* + * 每个卡槽有高低频两种数据,因此卡槽的昵称也要有两种,其中key是跟卡槽走的,而id+n就等于数据索引,固定某个索引为指定类型即可 + */ +#define FDS_SLOT_TAG_NICK_NAME_KEY 0x1068 +#define FDS_SLOT_TAG_NICK_NAME_ID 0x1068 + +/* + * Slot for settings like LED animation mode and future options + */ +#define FDS_SETTINGS_KEY 0x1069 +#define FDS_SETTINGS_ID 0x1069 + +#endif \ No newline at end of file diff --git a/software/script/chameleon_cli_main.py b/software/script/chameleon_cli_main.py index 83fba87..fe23a4e 100755 --- a/software/script/chameleon_cli_main.py +++ b/software/script/chameleon_cli_main.py @@ -87,6 +87,15 @@ class ChameleonCLI: 'help': "Emulation tag slot.", }, 'dfu': new_uint(chameleon_cli_unit.HWDFU, "Restart application to bootloader mode(Not yet implement dfu)."), + 'settings': { + 'animation': { + 'get': new_uint(chameleon_cli_unit.HWSettingsAnimationGet, "Get current animation mode value"), + 'set': new_uint(chameleon_cli_unit.HWSettingsAnimationSet, "Change chameleon animation mode"), + 'help': 'Manage wake-up and sleep animation mode' + }, + 'store': new_uint(chameleon_cli_unit.HWSettingsStore, "Store current settings to flash"), + 'help': "Chameleon settings management" + }, 'help': "hardware controller", }, 'hf': { diff --git a/software/script/chameleon_cli_unit.py b/software/script/chameleon_cli_unit.py index 4cb0ded..0654743 100644 --- a/software/script/chameleon_cli_unit.py +++ b/software/script/chameleon_cli_unit.py @@ -994,3 +994,41 @@ class HWDFU(DeviceRequiredUnit): print(" - Enter success @.@~") # let time for comm thread to send dfu cmd and close port time.sleep(0.1) + +class HWSettingsAnimationGet(DeviceRequiredUnit): + def args_parser(self) -> ArgumentParserNoExit or None: + return None + def on_exec(self, args: argparse.Namespace): + resp: chameleon_com.Response = self.cmd_standard.get_settings_animation() + if resp.data[0] == 0: + print("Full animation") + elif resp.data[0] == 1: + print("Minimal animation") + elif resp.data[0] == 2: + print("No animation") + else: + print("Unknown setting value, something failed.") + +class HWSettingsAnimationSet(DeviceRequiredUnit): + def args_parser(self) -> ArgumentParserNoExit or None: + parser = ArgumentParserNoExit() + parser.add_argument('-m', '--mode', type=int, required=True, help="0 is full (default), 1 is minimal (only single pass on button wakeup), 2 is none", choices=[0, 1, 2]) + return None + + def on_exec(self, args: argparse.Namespace): + mode = args.mode + self.cmd_standard.set_settings_animation(mode) + print("Animation mode change success. Do not forget to store your settings in flash!") + + +class HWSettingsStore(DeviceRequiredUnit): + def args_parser(self) -> ArgumentParserNoExit or None: + return None + + def on_exec(self, args: argparse.Namespace): + print("Storing settings...") + resp: chameleon_com.Response = self.cmd_standard.store_settings() + if resp.status == chameleon_status.Device.STATUS_DEVICE_SUCCESS: + print(" - Store success @.@~") + else: + print(" - Store failed") diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index 0f793d5..924ff33 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -20,6 +20,10 @@ DATA_CMD_ENTER_BOOTLOADER = 1010 DATA_CMD_GET_DEVICE_CHIP_ID = 1011 DATA_CMD_GET_DEVICE_ADDRESS = 1012 +DATA_CMD_SAVE_SETTINGS = 1013 +DATA_CMD_SET_ANIMATION_MODE = 1014 +DATA_CMD_GET_ANIMATION_MODE = 1015 + DATA_CMD_SCAN_14A_TAG = 2000 DATA_CMD_MF1_SUPPORT_DETECT = 2001 DATA_CMD_MF1_NT_LEVEL_DETECT = 2002 @@ -424,6 +428,24 @@ class BaseChameleonCMD: :return: """ return self.device.send_cmd_auto(DATA_CMD_ENTER_BOOTLOADER, 0x00, close=True) + + def get_settings_animation(self): + """ + Get animation mode value + """ + return self.device.send_cmd_sync(DATA_CMD_GET_ANIMATION_MODE, 0x00, None) + + def set_settings_animation(self, value: int): + """ + Set animation mode value + """ + return self.device.send_cmd_sync(DATA_CMD_SET_ANIMATION_MODE, 0x00, bytearray([value])) + + def store_settings(self): + """ + Store settings to flash memory + """ + return self.device.send_cmd_sync(DATA_CMD_SAVE_SETTINGS, 0x00) class NegativeResponseError(Exception): diff --git a/software/script/chameleon_status.py b/software/script/chameleon_status.py index bab7cde..675aaea 100644 --- a/software/script/chameleon_status.py +++ b/software/script/chameleon_status.py @@ -41,6 +41,8 @@ class Device(metaclass=MetaDevice): STATUS_INVALID_CMD = 0x67 # 无效的指令 STATUS_DEVICE_SUCCESS = 0x68 # 设备相关操作成功执行 STATUS_NOT_IMPLEMENTED = 0x69 # 调用了某些未实现的操作,属于开发者遗漏的错误 + STATUS_FLASH_WRITE_FAIL = 0x70 # flash写入失败 + STATUS_FLASH_READ_FAIL = 0x71 # flash读取失败 message = { @@ -68,4 +70,6 @@ message = { Device.STATUS_INVALID_CMD : "API request fail, cmd invalid", Device.STATUS_DEVICE_SUCCESS : "Device operation succeeded", Device.STATUS_NOT_IMPLEMENTED : "Some api not implemented", + Device.STATUS_FLASH_WRITE_FAIL : "Flash write failed", + Device.STATUS_FLASH_READ_FAIL : "Flash read failed" }