diff --git a/.gitignore b/.gitignore index 1c25d3a0ae2a3c116a097855f681b805294ee1e7..84d239597667c4f4c6711cee1e1bad9a2f1088a8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ __pycache__/ *~ compile_commands.json /tags +/release-*/ diff --git a/CHANGELOG.md b/CHANGELOG.md index d947a8e4314479f503e0306eee4c38d3b8194d13..e580443a064b511fba178ad0f53a2330ced62723 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,18 +5,73 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] ### Added +- `tools/pycard10.py`: Tool to interact with card10's serial connection and + upload files directly: + ```bash + ./tools/pycard10.py path/to/python-script.py + ``` +- `epic_disp_print_adv` & `Display.print(font=...)`: Print with different + fonts! The following fonts are supported: `8px`, `12px`, `16px`, `20px`, + and `24px`. +- **pycardium**: Support for RAW REPL mode. +- **bhi160**: Function to disable all sensors (`bhi160.disable_all_sensors()`). +- `ls_cmsis_dap`: A tool to enumerate CMSIS-DAP debuggers. + +### Changed +- `main.py` was moved into an app to allow easier reconfiguration of the + default app. The new `main.py` points to the "old" one so behavior is not + changed. +- After a timeout, the menu will close and `main.py` will run again. + +### Removed +- Some unused font files. + +### Fixed +- Fixed a regression which made the ECG app no longer work. + + + +## [v1.8] - 2019-08-27 11:38 - [HabaneroChilli] +[HabaneroChilli]: https://card10.badge.events.ccc.de/release/card10-v1.8-HabaneroChilli.zip + +### Added +- API-call for direct light-sensor readout: `epic_light_sensor_read`. +- Pause mode in ECG-App. +- `bin` field in metatdata for an alternate entrypoint. +- `shell.nix`: Nix-Shell which installs patched OpenOCD and dependencies. +- Cool LED animation in default ECG app. + +### Changed +- No longer require locking the display for setting the backlight. + + +## [v1.7] - 2019-08-24 21:48 - [Garlic] +[Garlic]: https://card10.badge.events.ccc.de/release/card10-v1.7-Garlic.zip + +### Added +- **ESB**: Epic Serial Bus (Better than USB!), stability improvements of the + USB module. Preparation for mass-storage access in the Firmware. - Enabled the Hardware Watchdog; Card10 will reset itself if the firmware crashes +- Log messages when BLE is pairing / connected. - The name of the offending app is printed to the serial console, if an app crashes the metatdata parser. ### Changed - Improved log messages in cases of lock-contention. +- Menu will show an error message if a crash occurs. ### Fixed +- Fixed race-conditions in serial writes by using a queue. - "Card10 Nickname" crashing if only `nickname.txt` exists. - Lockup when debug prints are enabled. - Delayed BHI160 startup a bit so the PMIC task can check the battery first. - Relaxed the PMIC lock-timeouts so other task can take a little more time. +- Fixed off-by-one error in `gfx_line()`. +- Fixed the API interrupts sometimes getting stuck. +- Fixed binary building on MacOS. +- Fixed race-conditions in serial console prints by introducing a queue. +- Fixed API & MAX30001 mutexes being initialized late sometimes. +- Fixed wrong stripe width in bi flag. ## [v1.6] - 2019-08-23 20:30 - [Fennel] @@ -137,7 +192,9 @@ fbf7c8c0 fix(menu.py) Refactored menu.py based on !138 ## [v1.0] - 2019-08-21 00:50 Initial release. -[Unreleased]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.6...master +[Unreleased]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.8...master +[v1.8]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.7...v1.8 +[v1.7]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.6...v1.7 [v1.6]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.5...v1.6 [v1.5]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.4...v1.5 [v1.4]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.3...v1.4 diff --git a/Documentation/bluetooth/file-transfer.rst b/Documentation/bluetooth/file-transfer.rst index 439237b883ea69a2655f30511612949c64ab4b95..49191d2da8f25e610d0ef9f96696b73692a2d74f 100644 --- a/Documentation/bluetooth/file-transfer.rst +++ b/Documentation/bluetooth/file-transfer.rst @@ -77,9 +77,11 @@ CHUNK_ACK: ===== === 0 1-4 ----- --- - C CRC + C CRC(*) ===== === +CRC32 of the whole CHUNK packet including first byte, offset and payload. + FINISH: === === diff --git a/Documentation/pycardium/light-sensor.rst b/Documentation/pycardium/light-sensor.rst index 6ffcb05c866a6d8a6f2d20c1412173e69ace5bf8..57c6d1b949eeacd0baeee571dfce23fd131aded6 100644 --- a/Documentation/pycardium/light-sensor.rst +++ b/Documentation/pycardium/light-sensor.rst @@ -25,3 +25,13 @@ be fairly stable. .. py:function:: stop() Stop the ADC. + +.. py:function:: read() + + Direct readout of the light-sensor. + + Use this function for low latency readout. The time between polls can have + an effect on the values measures. If you do not need low latency, prefer + :py:func:`light_sensor.get_reading`. + + .. versionadded:: 1.8 diff --git a/Documentation/pycardium/overview.rst b/Documentation/pycardium/overview.rst index 0d3056c8bcf28062b8e51b47c6c34d4c636ac935..49370ada31cf7aa532e67928c5a21d3301cdbdb3 100644 --- a/Documentation/pycardium/overview.rst +++ b/Documentation/pycardium/overview.rst @@ -36,8 +36,46 @@ Baud-rate is 115200. Some options are: * **screen**: ``sudo screen /dev/ttyACM0 115200`` * **picocom**: ``sudo picocom -b 115200 /dev/ttyACM0`` -After connecting, reboot card10 and you should see the MicroPython REPL pop up. +After connecting, reboot reset the card10 via the power button (left upper +corner) and you should see the output of **menu.py** script (it's located in +*preload/menu.py*). You can press CTRL-C to interrupt the script and jump into +the MicroPython prompt. + +To switch on the blue fairy dust you must import the led python module:: + + import leds + +and power it on:: + + leds.set_rocket(0, 31) + + +REPL modes +^^^^^^^^^^ + +MicroPython supports a different REPL modes over the serial console. The modes +can be changed on every new line. + +Normal mode +""""""""""" +This is the mode you will first see. You can switch to it by pressing CTRL-B. +If you are in a other mode you can return to this mode by pressing CTRL-B too. + +Paste mode +"""""""""" +You can enter the paste mode by pressing CTRL-E and you can simple copy 'n' +paste your source code into the console and it will be interpreted and executed +line by line. Every new line will be reply by the prompt with **===**. + +RAW mode +"""""""" +The RAW mode to be intendend for the usage with tools. By pressing CTRL-A you +will enter the RAW REPL mode. The type in code will not printed. By pressing +CTRL-D the whole entered code will be evaluated and executed. The board will +reply with **OK** and print after that the output (print commands) of the code +or give you tracebacks if an error occured. + +You can use **pycard10** (tools/pycard10.py) to execute python files from your +PC directly on the card10. -.. todo:: - Getting Started Guide for people interested in writing Python code. diff --git a/epicardium/FreeRTOSConfig.h b/epicardium/FreeRTOSConfig.h index b92b12b9ec223da13082f23fcad849a1337ba316..f1d3aa46915e9c4373d281a1a4ab37707a918bf9 100644 --- a/epicardium/FreeRTOSConfig.h +++ b/epicardium/FreeRTOSConfig.h @@ -52,7 +52,7 @@ #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_uxTaskGetStackHighWaterMark 1 - +#define INCLUDE_xTimerPendFunctionCall 1 /* Allow static allocation of data structures */ #define configSUPPORT_STATIC_ALLOCATION 1 diff --git a/epicardium/ble/app/app_main.c b/epicardium/ble/app/app_main.c index f795c4e7222adfeb7cbe5b19e15f3c1659b7827c..b279c758bb5857eb3d649fe7cc8c36f7e16ef35f 100644 --- a/epicardium/ble/app/app_main.c +++ b/epicardium/ble/app/app_main.c @@ -37,6 +37,8 @@ #include "app_main.h" #include "app_ui.h" +#include "modules/log.h" + /************************************************************************************************** Global Variables **************************************************************************************************/ @@ -283,6 +285,8 @@ void AppHandleNumericComparison(dmSecCnfIndEvt_t *pCnfInd) { uint32_t confirm = DmSecGetCompareValue(pCnfInd->confirm); + LOG_INFO("ble", "Confirm Value: %ld", confirm); + /* display confirmation value */ AppUiDisplayConfirmValue(confirm); diff --git a/epicardium/ble/ble_main.c b/epicardium/ble/ble_main.c index af041f9cfd7c27fcd69774d3d7a9eaf882c990c6..07783d8619cc05c688db056ec0d17d06b72413dc 100644 --- a/epicardium/ble/ble_main.c +++ b/epicardium/ble/ble_main.c @@ -38,6 +38,8 @@ #include "hrps/hrps_api.h" #include "rscp/rscp_api.h" +#include "modules/log.h" + /************************************************************************************************** Macros **************************************************************************************************/ @@ -373,6 +375,7 @@ static void bleSetup(bleMsg_t *pMsg) static void bleProcMsg(bleMsg_t *pMsg) { uint8_t uiEvent = APP_UI_NONE; + hciLeConnCmplEvt_t *connOpen; switch(pMsg->hdr.event) { @@ -395,36 +398,88 @@ static void bleProcMsg(bleMsg_t *pMsg) break; case DM_ADV_START_IND: + LOG_INFO("ble", "Advertisement started"); uiEvent = APP_UI_ADV_START; break; case DM_ADV_STOP_IND: + LOG_INFO("ble", "Advertisement stopped"); uiEvent = APP_UI_ADV_STOP; break; case DM_CONN_OPEN_IND: + connOpen = &pMsg->dm.connOpen; + LOG_INFO("ble", "connection from %02X:%02X:%02X:%02X:%02X:%02X opened", + connOpen->peerAddr[0], connOpen->peerAddr[1], + connOpen->peerAddr[2], connOpen->peerAddr[3], + connOpen->peerAddr[4], connOpen->peerAddr[5]); BasProcMsg(&pMsg->hdr); uiEvent = APP_UI_CONN_OPEN; break; case DM_CONN_CLOSE_IND: + switch (pMsg->dm.connClose.reason) + { + case HCI_ERR_CONN_TIMEOUT: + LOG_INFO("ble", "Connection closed (0x%02X), Connection timeout", + pMsg->dm.connClose.reason); + break; + case HCI_ERR_LOCAL_TERMINATED: + LOG_INFO("ble", "Connection closed (0x%02X), Connection terminated by local host", + pMsg->dm.connClose.reason); + break; + case HCI_ERR_REMOTE_TERMINATED: + LOG_INFO("ble", "Connection closed (0x%02X), Remote user terminated connection", + pMsg->dm.connClose.reason); + break; + case HCI_ERR_CONN_FAIL: + LOG_INFO("ble", "Connection closed (0x%02X), Connection failed to be established", + pMsg->dm.connClose.reason); + break; + case HCI_ERR_MIC_FAILURE: + LOG_INFO("ble", "Connection closed (0x%02X), Connection terminated due to MIC failure", + pMsg->dm.connClose.reason); + break; + default: + LOG_INFO("ble", "Connection closed (0x%02X)", + pMsg->dm.connClose.reason); + break; + } bleClose(pMsg); uiEvent = APP_UI_CONN_CLOSE; break; case DM_SEC_PAIR_CMPL_IND: + LOG_INFO("ble", "Secure pairing successful, auth: 0x%02X", + pMsg->dm.pairCmpl.auth); uiEvent = APP_UI_SEC_PAIR_CMPL; break; case DM_SEC_PAIR_FAIL_IND: + switch (pMsg->hdr.status) { + case SMP_ERR_TIMEOUT: + LOG_INFO("ble", "Secure pairing failed (0x%02X), Transaction timeout", + pMsg->hdr.status); + break; + case SMP_ERR_ATTEMPTS: + LOG_INFO("ble", "Secure pairing failed (0x%02X), Repeated attempts", + pMsg->hdr.status); + break; + default: + LOG_INFO("ble", "Secure pairing failed (0x%02X)", + pMsg->hdr.status); + break; + } uiEvent = APP_UI_SEC_PAIR_FAIL; break; case DM_SEC_ENCRYPT_IND: + LOG_INFO("ble", "Encrypted handshake successful"); uiEvent = APP_UI_SEC_ENCRYPT; break; case DM_SEC_ENCRYPT_FAIL_IND: + LOG_INFO("ble", "Encrypted handshake failed"); uiEvent = APP_UI_SEC_ENCRYPT_FAIL; break; @@ -441,6 +496,7 @@ static void bleProcMsg(bleMsg_t *pMsg) break; case DM_HW_ERROR_IND: + LOG_ERR("ble", "HW Error"); uiEvent = APP_UI_HW_ERROR; break; diff --git a/epicardium/cdcacm.c b/epicardium/cdcacm.c deleted file mode 100644 index 1cbbb154452a9ac96b9300bdded5071353121260..0000000000000000000000000000000000000000 --- a/epicardium/cdcacm.c +++ /dev/null @@ -1,382 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved. - * - * 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 MAXIM INTEGRATED 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. - * - * Except as contained in this notice, the name of Maxim Integrated - * Products, Inc. shall not be used except as stated in the Maxim Integrated - * Products, Inc. Branding Policy. - * - * The mere transfer of this software does not imply any licenses - * of trade secrets, proprietary technology, copyrights, patents, - * trademarks, maskwork rights, or any other form of intellectual - * property whatsoever. Maxim Integrated Products, Inc. retains all - * ownership rights. - * - * $Id: main.c 32120 2017-11-28 23:51:11Z lorne.smith $ - * - ******************************************************************************* - */ - -/** - * @file main.c - * @brief USB CDC-ACM example - * @details This project creates a virtual COM port, which loops back data sent to it. - * Load the project, connect a cable from the PC to the USB connector - * on the Evaluation Kit, and observe that the PC now recognizes a new COM port. - * A driver for the COM port, if needed, is located in the Driver/ subdirectory. - * - */ - -#include <stdio.h> -#include <stddef.h> -#include "mxc_config.h" -#include "mxc_sys.h" -#include "mxc_delay.h" -#include "board.h" -#include "led.h" -#include "usb.h" -#include "usb_event.h" -#include "enumerate.h" -#include "cdc_acm.h" -#include "descriptors.h" - -#include "modules/modules.h" -#include "modules/log.h" -#include <errno.h> - -/***** Definitions *****/ -#define EVENT_ENUM_COMP MAXUSB_NUM_EVENTS - -#define BUFFER_SIZE 64 - -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) - -/***** Global Data *****/ -//SWYM: rename to CDC_xy or put into struct CDC_state -volatile int configured; //SWYM: actually unused... -volatile int suspended; -volatile unsigned int event_flags; -int remote_wake_en; - -/***** Function Prototypes *****/ -static int setconfig_callback(usb_setup_pkt *sud, void *cbdata); -static int setfeature_callback(usb_setup_pkt *sud, void *cbdata); -static int clrfeature_callback(usb_setup_pkt *sud, void *cbdata); -static int event_callback(maxusb_event_t evt, void *data); -static void usb_app_sleep(void); -static void usb_app_wakeup(void); -static int usb_read_callback(void); -//static void echo_usb(void); - -/***** File Scope Variables *****/ - -/* This EP assignment must match the Configuration Descriptor */ -static const acm_cfg_t acm_cfg = { - 1, /* EP OUT */ - MXC_USBHS_MAX_PACKET, /* OUT max packet size */ - 2, /* EP IN */ - MXC_USBHS_MAX_PACKET, /* IN max packet size */ - 3, /* EP Notify */ - MXC_USBHS_MAX_PACKET, /* Notify max packet size */ -}; - -static volatile int usb_read_complete; - -int usb_startup_cb() -{ - const sys_cfg_usbhs_t sys_usbhs_cfg = NULL; - return SYS_USBHS_Init(&sys_usbhs_cfg); -} - -int usb_shutdown_cb() -{ - return SYS_USBHS_Shutdown(); -} - -/* User-supplied function to delay usec micro-seconds */ -void delay_us(unsigned int usec) -{ - /* mxc_delay() takes unsigned long, so can't use it directly */ - mxc_delay(usec); -} - -/******************************************************************************/ -int cdcacm_init(void) -{ - maxusb_cfg_options_t usb_opts; - - /* Initialize state */ - configured = 0; - suspended = 0; - event_flags = 0; - remote_wake_en = 0; - - /* Start out in full speed */ - usb_opts.enable_hs = 0; - usb_opts.delay_us = - delay_us; /* Function which will be used for delays */ - usb_opts.init_callback = usb_startup_cb; - usb_opts.shutdown_callback = usb_shutdown_cb; - - /* Initialize the usb module */ - if (usb_init(&usb_opts) != 0) { - LOG_ERR("cdcacm", "usb_init() failed"); - return -EIO; - } - - /* Initialize the enumeration module */ - if (enum_init() != 0) { - LOG_ERR("cdcacm", "enum_init() failed"); - return -EIO; - } - - /* Register enumeration data */ - enum_register_descriptor( - ENUM_DESC_DEVICE, (uint8_t *)&device_descriptor, 0 - ); - enum_register_descriptor( - ENUM_DESC_CONFIG, (uint8_t *)&config_descriptor, 0 - ); - enum_register_descriptor(ENUM_DESC_STRING, lang_id_desc, 0); - enum_register_descriptor(ENUM_DESC_STRING, mfg_id_desc, 1); - enum_register_descriptor(ENUM_DESC_STRING, prod_id_desc, 2); - - /* Handle configuration */ - enum_register_callback(ENUM_SETCONFIG, setconfig_callback, NULL); - - /* Handle feature set/clear */ - enum_register_callback(ENUM_SETFEATURE, setfeature_callback, NULL); - enum_register_callback(ENUM_CLRFEATURE, clrfeature_callback, NULL); - - /* Initialize the class driver */ - if (acm_init(&config_descriptor.comm_interface_descriptor) != 0) { - LOG_ERR("cdcacm", "acm_init() failed"); - return -EIO; - } - - /* Register callbacks */ - usb_event_enable(MAXUSB_EVENT_NOVBUS, event_callback, NULL); - usb_event_enable(MAXUSB_EVENT_VBUS, event_callback, NULL); - acm_register_callback(ACM_CB_READ_READY, usb_read_callback); - usb_read_complete = 0; - - /* Start with USB in low power mode */ - usb_app_sleep(); - /* TODO: Fix priority */ - NVIC_SetPriority(USB_IRQn, 6); - NVIC_EnableIRQ(USB_IRQn); - - return 0; -} - -int cdcacm_num_read_avail(void) -{ - return acm_canread(); -} - -uint8_t cdcacm_read(void) -{ - while (acm_canread() <= 0) { - } - - uint8_t buf; - acm_read(&buf, 1); - return buf; -} - -void cdcacm_write(uint8_t *data, int len) -{ - static int lockup_disable = 0; - if (acm_present() && !lockup_disable) { - int ret = acm_write(data, len); - if (ret < 0) { - lockup_disable = 1; - LOG_ERR("cdcacm", "fifo lockup detected"); - } else if (ret != len) { - LOG_WARN( - "cdcacm", "write length mismatch, got %d", ret - ); - } - } -} - -/******************************************************************************/ -#if 0 -static void echo_usb(void) -{ - int chars; - uint8_t buffer[BUFFER_SIZE]; - - if ((chars = acm_canread()) > 0) { - - if (chars > BUFFER_SIZE) { - chars = BUFFER_SIZE; - } - - // Read the data from USB - if (acm_read(buffer, chars) != chars) { - printf("acm_read() failed\n"); - return; - } - - // Echo it back - if (acm_present()) { - if (acm_write(buffer, chars) != chars) { - printf("acm_write() failed\n"); - } - } - } -} -#endif -/******************************************************************************/ -static int setconfig_callback(usb_setup_pkt *sud, void *cbdata) -{ - /* Confirm the configuration value */ - if (sud->wValue == - config_descriptor.config_descriptor.bConfigurationValue) { - configured = 1; - MXC_SETBIT(&event_flags, EVENT_ENUM_COMP); - return acm_configure(&acm_cfg); /* Configure the device class */ - } else if (sud->wValue == 0) { - configured = 0; - return acm_deconfigure(); - } - - return -1; -} - -/******************************************************************************/ -static int setfeature_callback(usb_setup_pkt *sud, void *cbdata) -{ - if (sud->wValue == FEAT_REMOTE_WAKE) { - remote_wake_en = 1; - } else { - // Unknown callback - return -1; - } - - return 0; -} - -/******************************************************************************/ -static int clrfeature_callback(usb_setup_pkt *sud, void *cbdata) -{ - if (sud->wValue == FEAT_REMOTE_WAKE) { - remote_wake_en = 0; - } else { - // Unknown callback - return -1; - } - - return 0; -} - -/******************************************************************************/ -static void usb_app_sleep(void) -{ - /* TODO: Place low-power code here */ - suspended = 1; -} - -/******************************************************************************/ -static void usb_app_wakeup(void) -{ - /* TODO: Place low-power code here */ - suspended = 0; -} - -/******************************************************************************/ -static int event_callback(maxusb_event_t evt, void *data) -{ - /* Set event flag */ - MXC_SETBIT(&event_flags, evt); - - switch (evt) { - case MAXUSB_EVENT_NOVBUS: - usb_event_disable(MAXUSB_EVENT_BRST); - usb_event_disable(MAXUSB_EVENT_SUSP); - usb_event_disable(MAXUSB_EVENT_DPACT); - usb_disconnect(); - configured = 0; - enum_clearconfig(); - acm_deconfigure(); - usb_app_sleep(); - break; - case MAXUSB_EVENT_VBUS: - usb_event_clear(MAXUSB_EVENT_BRST); - usb_event_enable(MAXUSB_EVENT_BRST, event_callback, NULL); - usb_event_clear(MAXUSB_EVENT_SUSP); - usb_event_enable(MAXUSB_EVENT_SUSP, event_callback, NULL); - usb_connect(); - usb_app_sleep(); - break; - case MAXUSB_EVENT_BRST: - usb_app_wakeup(); - enum_clearconfig(); - acm_deconfigure(); - configured = 0; - suspended = 0; - break; - case MAXUSB_EVENT_SUSP: - usb_app_sleep(); - break; - case MAXUSB_EVENT_DPACT: - usb_app_wakeup(); - break; - default: - break; - } - - return 0; -} - -/******************************************************************************/ -static int usb_read_callback(void) -{ - usb_read_complete = 1; - return 0; -} - -/******************************************************************************/ -#include "FreeRTOS.h" -#include "task.h" - -void USB_IRQHandler(void) -{ - usb_event_handler(); - - if (serial_task_id != NULL) { - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - vTaskNotifyGiveFromISR( - serial_task_id, &xHigherPriorityTaskWoken - ); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - } -} - -/******************************************************************************/ -/* TODO: We probably need to fix something related to this */ -#if 0 -void SysTick_Handler(void) -{ - mxc_delay_handler(); -} -#endif /* 0 */ diff --git a/epicardium/cdcacm.h b/epicardium/cdcacm.h deleted file mode 100644 index 1c25d75027cebffd593f32364ebab7eec65fa7e6..0000000000000000000000000000000000000000 --- a/epicardium/cdcacm.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef CDCACM_H -#define CDCACM_H -#include <stdint.h> - -int cdcacm_init(void); -int cdcacm_num_read_avail(void); -uint8_t cdcacm_read(void); -void cdcacm_write(uint8_t *data, int len); - -#endif diff --git a/epicardium/descriptors.h b/epicardium/descriptors.h deleted file mode 100644 index c081c4010e65c7c86980ad94a096a81febb69f61..0000000000000000000000000000000000000000 --- a/epicardium/descriptors.h +++ /dev/null @@ -1,227 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved. - * - * 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 MAXIM INTEGRATED 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. - * - * Except as contained in this notice, the name of Maxim Integrated - * Products, Inc. shall not be used except as stated in the Maxim Integrated - * Products, Inc. Branding Policy. - * - * The mere transfer of this software does not imply any licenses - * of trade secrets, proprietary technology, copyrights, patents, - * trademarks, maskwork rights, or any other form of intellectual - * property whatsoever. Maxim Integrated Products, Inc. retains all - * ownership rights. - * - * Description: Communications Device Class ACM (Serial Port) over USB - * $Id: descriptors.h 36682 2018-08-06 21:14:03Z michael.bayern $ - * - ******************************************************************************* - */ - -#ifndef _DESCRIPTORS_H_ -#define _DESCRIPTORS_H_ - -#include <stdint.h> -#include "usb.h" -#include "hid_kbd.h" - -usb_device_descriptor_t __attribute__((aligned(4))) device_descriptor = { - 0x12, /* bLength = 18 */ - 0x01, /* bDescriptorType = Device */ - 0x0110, /* bcdUSB USB spec rev (BCD) */ - 0x02, /* bDeviceClass = comm class (2) */ - 0x00, /* bDeviceSubClass */ - 0x00, /* bDeviceProtocol */ - 0x40, /* bMaxPacketSize0 is 64 bytes */ - 0x0B6A, /* idVendor (Maxim Integrated) */ - 0x003C, /* idProduct */ - 0x0100, /* bcdDevice */ - 0x01, /* iManufacturer Descriptor ID */ - 0x02, /* iProduct Descriptor ID */ - 0x00, /* iSerialNumber = (0) No string */ - 0x01 /* bNumConfigurations */ -}; - -__attribute__((aligned(4))) -struct __attribute__((packed)) { - usb_configuration_descriptor_t config_descriptor; - usb_interface_descriptor_t comm_interface_descriptor; - uint8_t header_functional_descriptor[5]; - uint8_t call_management_descriptor[5]; - uint8_t acm_functional_descriptor[4]; - uint8_t union_functional_descriptor[5]; - usb_endpoint_descriptor_t endpoint_descriptor_3; - usb_interface_descriptor_t data_interface_descriptor; - usb_endpoint_descriptor_t endpoint_descriptor_1; - usb_endpoint_descriptor_t endpoint_descriptor_2; -} config_descriptor = -{ - { - 0x09, /* bLength = 9 */ - 0x02, /* bDescriptorType = Config (2) */ - 0x0043, /* wTotalLength(L/H) */ - 0x02, /* bNumInterfaces */ - 0x01, /* bConfigValue */ - 0x00, /* iConfiguration */ - 0xE0, /* bmAttributes (self-powered, remote wakeup) */ - 0x01, /* MaxPower is 2ma (units are 2ma/bit) */ - }, - { /* First Interface Descriptor For Comm Class Interface */ - 0x09, /* bLength = 9 */ - 0x04, /* bDescriptorType = Interface (4) */ - 0x00, /* bInterfaceNumber */ - 0x00, /* bAlternateSetting */ - 0x01, /* bNumEndpoints (one for OUT) */ - 0x02, /* bInterfaceClass = Communications Interface Class (2) */ - 0x02, /* bInterfaceSubClass = Abstract Control Model (2) */ - 0x01, /* bInterfaceProtocol = Common "AT" commands (1), no class specific protocol (0) */ - 0x00, /* iInterface */ - }, - { /* Header Functional Descriptor */ - 0x05, /* bFunctionalLength = 5 */ - 0x24, /* bDescriptorType */ - 0x00, /* bDescriptorSubtype */ - 0x10, 0x01, /* bcdCDC */ - }, - { /* Call Management Descriptor */ - 0x05, /* bFunctionalLength = 5 */ - 0x24, /* bDescriptorType */ - 0x01, /* bDescriptorSubtype */ - 0x03, /* bmCapabilities = Device handles call management itself (0x01), management over data class (0x02) */ - 0x01, /* bmDataInterface */ - }, - { /* Abstract Control Management Functional Descriptor */ - 0x04, /* bFunctionalLength = 4 */ - 0x24, /* bDescriptorType */ - 0x02, /* bDescriptorSubtype */ - 0x02, /* bmCapabilities */ - }, - { /* Union Functional Descriptor */ - 0x05, /* bFunctionalLength = 5 */ - 0x24, /* bDescriptorType */ - 0x06, /* bDescriptorSubtype */ - 0x00, /* bmMasterInterface */ - 0x01, /* bmSlaveInterface0 */ - }, - { /* IN Endpoint 3 (Descriptor #1) */ - 0x07, /* bLength */ - 0x05, /* bDescriptorType (Endpoint) */ - 0x83, /* bEndpointAddress (EP3-IN) */ - 0x03, /* bmAttributes (interrupt) */ - 0x0040, /* wMaxPacketSize */ - 0xff, /* bInterval (milliseconds) */ - }, - { /* Second Interface Descriptor For Data Interface */ - 0x09, /* bLength */ - 0x04, /* bDescriptorType (Interface) */ - 0x01, /* bInterfaceNumber */ - 0x00, /* bAlternateSetting */ - 0x02, /* bNumEndpoints */ - 0x0a, /* bInterfaceClass = Data Interface (10) */ - 0x00, /* bInterfaceSubClass = none (0) */ - 0x00, /* bInterfaceProtocol = No class specific protocol (0) */ - 0x00, /* biInterface = No Text String (0) */ - }, - { /* OUT Endpoint 1 (Descriptor #2) */ - 0x07, /* bLength */ - 0x05, /* bDescriptorType (Endpoint) */ - 0x01, /* bEndpointAddress (EP1-OUT) */ - 0x02, /* bmAttributes (bulk) */ - 0x0040, /* wMaxPacketSize */ - 0x00, /* bInterval (N/A) */ - }, - { /* IN Endpoint 2 (Descriptor #3) */ - 0x07, /* bLength */ - 0x05, /* bDescriptorType (Endpoint) */ - 0x82, /* bEndpointAddress (EP2-IN) */ - 0x02, /* bmAttributes (bulk) */ - 0x0040, /* wMaxPacketSize */ - 0x00 /* bInterval (N/A) */ - } -}; - -__attribute__((aligned(4))) -uint8_t lang_id_desc[] = { - 0x04, /* bLength */ - 0x03, /* bDescriptorType */ - 0x09, 0x04 /* bString = wLANGID (see usb_20.pdf 9.6.7 String) */ -}; - -__attribute__((aligned(4))) -uint8_t mfg_id_desc[] = { - 0x22, /* bLength */ - 0x03, /* bDescriptorType */ - 'M', 0, - 'a', 0, - 'x', 0, - 'i', 0, - 'm', 0, - ' ', 0, - 'I', 0, - 'n', 0, - 't', 0, - 'e', 0, - 'g', 0, - 'r', 0, - 'a', 0, - 't', 0, - 'e', 0, - 'd', 0, -}; - -__attribute__((aligned(4))) -uint8_t prod_id_desc[] = { - 0x22, /* bLength */ - 0x03, /* bDescriptorType */ - 'M', 0, - 'A', 0, - 'X', 0, - '3', 0, - '2', 0, - '6', 0, - '6', 0, - '5', 0, - ' ', 0, - 'C', 0, - 'D', 0, - 'C', 0, - '-', 0, - 'A', 0, - 'C', 0, - 'M', 0, -}; - -/* Not currently used (see device descriptor), but could be enabled if desired */ -__attribute__((aligned(4))) -uint8_t serial_id_desc[] = { - 0x14, /* bLength */ - 0x03, /* bDescriptorType */ - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '1', 0 -}; - -#endif /* _DESCRIPTORS_H_ */ diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index d394d6ce801d518e8c0146a17d00b87f550e43af..615440aff0caa2e42ce377c20ddcb55c0f812f08 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -54,6 +54,7 @@ typedef _Bool bool; #define API_DISP_PIXEL 0x28 #define API_DISP_FRAMEBUFFER 0x29 #define API_DISP_BACKLIGHT 0x2a +#define API_DISP_PRINT_ADV 0x2b /* API_BATTERY_VOLTAGE 0x30 */ #define API_BATTERY_CURRENT 0x31 @@ -102,6 +103,7 @@ typedef _Bool bool; #define API_LIGHT_SENSOR_RUN 0x80 #define API_LIGHT_SENSOR_GET 0x81 #define API_LIGHT_SENSOR_STOP 0x82 +#define API_LIGHT_SENSOR_READ 0x83 #define API_BUTTONS_READ 0x90 @@ -131,7 +133,11 @@ typedef _Bool bool; #define API_MAX86150_GET_DATA 0x0101 #define API_MAX86150_SET_LED_AMPLITUDE 0x0102 -#define API_WS2812_WRITE 0x0110 +#define API_USB_SHUTDOWN 0x110 +#define API_USB_STORAGE 0x111 +#define API_USB_CDCACM 0x112 + +#define API_WS2812_WRITE 0x0120 /* clang-format on */ @@ -1236,6 +1242,40 @@ API(API_DISP_PRINT, uint16_t bg) ); +/* + * Font Selection + */ +enum disp_font_name { + DISP_FONT8 = 0, + DISP_FONT12 = 1, + DISP_FONT16 = 2, + DISP_FONT20 = 3, + DISP_FONT24 = 4, +}; + +/** + * Prints a string into the display framebuffer with font type selectable + * + * :param fontName: number of font, use FontName enum + * :param posx: x position to print to. 0 <= x <= 160 + * :param posy: y position to print to. 0 <= y <= 80 + * :param pString: string to print + * :param fg: foreground color in rgb565 + * :param bg: background color in rgb565 + * :return: ``0`` on success or a negative value in case of an error: + * + * - ``-EBUSY``: Display was already locked from another task. + */ +API(API_DISP_PRINT_ADV, + int epic_disp_print_adv( + uint8_t font, + uint16_t posx, + uint16_t posy, + const char *pString, + uint16_t fg, + uint16_t bg) +); + /** * Fills the whole screen with one color * @@ -1349,12 +1389,12 @@ API(API_DISP_FRAMEBUFFER, int epic_disp_framebuffer(union disp_framebuffer *fb)) /** - * Set the backlight brightness value + * Set the backlight brightness. * - * :param brightness: brightness from 0 - 100 - * :return: ``0`` on success or negative value in case of an error: + * Note that this function does not require acquiring the display. * - * - ``-EBUSY``: Display was already locked from another task. + * :param brightness: brightness from 0 - 100 + * :return: ``0`` on success or negative value in case of an error */ API(API_DISP_BACKLIGHT, int epic_disp_backlight(uint16_t brightness)); @@ -1397,6 +1437,20 @@ API(API_LIGHT_SENSOR_GET, int epic_light_sensor_get(uint16_t* value)); */ API(API_LIGHT_SENSOR_STOP, int epic_light_sensor_stop()); +/** + * Get the light level directly. + * + * Each call has an intrinsic delay of about 240us, I recommend another + * 100-300us delay between calls. Whether or not the IR LED is fast enough is + * another issue. + * + * :return: Light level + * + * .. versionadded:: 1.8 + */ +API(API_LIGHT_SENSOR_READ, uint16_t epic_light_sensor_read(void)); + + /** * File * ==== @@ -1702,6 +1756,22 @@ API(API_MAX30001_DISABLE, int epic_max30001_disable_sensor( void )); +/** + * De-initialize the currently configured USB device (if any) + * + */ +API(API_USB_SHUTDOWN, int epic_usb_shutdown(void)); +/** + * Configure the USB peripheral to export the internal FLASH + * as a Mass Storage device + */ +API(API_USB_STORAGE, int epic_usb_storage(void)); +/** + * Configure the USB peripheral to provide card10's stdin/stdout + * on a USB CDC-ACM device + */ +API(API_USB_CDCACM, int epic_usb_cdcacm(void)); + /** * WS2812 * ====== @@ -1717,5 +1787,5 @@ void */ API(API_WS2812_WRITE, void epic_ws2812_write(uint8_t pin, uint8_t *pixels, uint32_t n_bytes)); - #endif /* _EPICARDIUM_H */ + diff --git a/epicardium/fs/filesystem_fat.c b/epicardium/fs/filesystem_fat.c index 7d95510360324ac5e105a53aa90a3a96ee0936c1..ccaec62157174cdfab14c957d157941b063acc64 100644 --- a/epicardium/fs/filesystem_fat.c +++ b/epicardium/fs/filesystem_fat.c @@ -16,6 +16,7 @@ #include <FreeRTOS.h> #include <semphr.h> +#include <timers.h> #include "fs/internal.h" #include "modules/filesystem.h" @@ -57,7 +58,7 @@ struct FatObject { struct EpicFileSystem { struct FatObject pool[EPIC_FAT_MAX_OPENED]; uint32_t generationCount; - bool initialized; + bool attached; FATFS FatFs; int lockCoreMask; }; @@ -102,6 +103,11 @@ static StaticSemaphore_t s_globalLockBuffer; static SemaphoreHandle_t s_globalLock = NULL; +static void cb_attachTimer(void *a, uint32_t b) +{ + fatfs_attach(); +} + void fatfs_init() { static volatile bool s_initCalled = false; @@ -117,6 +123,7 @@ void fatfs_init() #else s_globalLock = xSemaphoreCreateMutex(); #endif + s_globalFileSystem.generationCount = 1; fatfs_attach(); } @@ -137,11 +144,11 @@ int fatfs_attach() int rc = 0; if (globalLockAccquire()) { EpicFileSystem *fs = &s_globalFileSystem; - if (!fs->initialized) { + if (!fs->attached) { ff_res = f_mount(&fs->FatFs, "/", 0); if (ff_res == FR_OK) { - fs->initialized = true; - SSLOG_DEBUG("FatFs mounted\n"); + fs->attached = true; + SSLOG_INFO("attached\n"); } else { SSLOG_ERR( "f_mount error %s\n", @@ -158,25 +165,38 @@ int fatfs_attach() return rc; } +void fatfs_schedule_attach(void) +{ + //if we're running in thread context, cont't call the *FromISR version + if (xPortIsInsideInterrupt()) { + xTimerPendFunctionCallFromISR(cb_attachTimer, NULL, 0, NULL); + } else { + xTimerPendFunctionCall( + cb_attachTimer, NULL, 0, 1); //wait 1 tick + } +} + void fatfs_detach() { FRESULT ff_res; EpicFileSystem *fs; if (efs_lock_global(&fs) == 0) { - efs_close_all(fs, EPICARDIUM_COREMASK_BOTH); - - //unmount by passing NULL as fs object, will destroy our sync object via ff_del_syncobj - ff_res = f_mount(NULL, "/", 0); - if (ff_res != FR_OK) { - SSLOG_ERR( - "f_mount (unmount) error %s\n", - f_get_rc_string(ff_res) - ); - } + if (fs->attached) { + efs_close_all(fs, EPICARDIUM_COREMASK_BOTH); + + //unmount by passing NULL as fs object, will destroy our sync object via ff_del_syncobj + ff_res = f_mount(NULL, "/", 0); + if (ff_res != FR_OK) { + SSLOG_ERR( + "f_mount (unmount) error %s\n", + f_get_rc_string(ff_res) + ); + } - fs->initialized = false; - disk_deinitialize(); - SSLOG_INFO("detached\n"); + fs->attached = false; + disk_deinitialize(); + SSLOG_INFO("detached\n"); + } efs_unlock_global(fs); } } @@ -223,7 +243,7 @@ int efs_lock_global(EpicFileSystem **fs) if (!globalLockAccquire()) { return -EBUSY; } - if (!s_globalFileSystem.initialized) { + if (!s_globalFileSystem.attached) { globalLockRelease(); return -ENODEV; } diff --git a/epicardium/main.c b/epicardium/main.c index be3e35197329c723da10ca3562928b7a4f5e6a28..56639ea56ead01fc06138e0f2f712518193c475f 100644 --- a/epicardium/main.c +++ b/epicardium/main.c @@ -24,10 +24,10 @@ int main(void) * Version Splash */ const char *version_buf = CARD10_VERSION; - const int offset = (160 - (int)strlen(version_buf) * 14) / 2; - epic_disp_clear(0x3b7); - epic_disp_print(10, 20, "Epicardium", 0x290, 0x3b7); - epic_disp_print(offset > 0 ? offset : 0, 40, version_buf, 0x290, 0x3b7); + const int off = (160 - (int)strlen(version_buf) * 14) / 2; + epic_disp_clear(0x9dc0); + epic_disp_print(10, 20, "Epicardium", 0x6c20, 0x9dc0); + epic_disp_print(off > 0 ? off : 0, 40, version_buf, 0x6c20, 0x9dc0); epic_disp_update(); mxc_delay(2000000); @@ -39,7 +39,7 @@ int main(void) (const char *)"Serial", configMINIMAL_STACK_SIZE * 2, NULL, - tskIDLE_PRIORITY + 1, + tskIDLE_PRIORITY + 3, NULL) != pdPASS) { LOG_CRIT("startup", "Failed to create %s task!", "Serial"); abort(); @@ -134,6 +134,11 @@ int main(void) abort(); } + /* + * Initialize serial driver data structures. + */ + serial_init(); + LOG_DEBUG("startup", "Starting FreeRTOS ..."); vTaskStartScheduler(); diff --git a/epicardium/meson.build b/epicardium/meson.build index 1aa5556010941d2267f8b10aafcaf9ec7683c8a2..cf67023b9f7143336875d973108d8ee0a080ffd1 100644 --- a/epicardium/meson.build +++ b/epicardium/meson.build @@ -80,7 +80,9 @@ endif elf = executable( name + '.elf', - 'cdcacm.c', + 'usb/epc_usb.c', + 'usb/cdcacm.c', + 'usb/mass_storage.c', 'main.c', 'support.c', 'fs/filesystem_fat.c', diff --git a/epicardium/modules/dispatcher.c b/epicardium/modules/dispatcher.c index 1f504eca52e20bfb8c118281c772942e72ac7a18..03f8534cc0a1c02dccc61f8c03e486e2ab472ff8 100644 --- a/epicardium/modules/dispatcher.c +++ b/epicardium/modules/dispatcher.c @@ -13,14 +13,17 @@ TaskHandle_t dispatcher_task_id; static StaticSemaphore_t api_mutex_data; SemaphoreHandle_t api_mutex = NULL; +void dispatcher_mutex_init(void) +{ + api_mutex = xSemaphoreCreateMutexStatic(&api_mutex_data); +} + /* * API dispatcher task. This task will sleep until an API call is issued and * then wake up to dispatch it. */ void vApiDispatcher(void *pvParameters) { - api_mutex = xSemaphoreCreateMutexStatic(&api_mutex_data); - LOG_DEBUG("dispatcher", "Ready."); while (1) { if (api_dispatcher_poll()) { diff --git a/epicardium/modules/display.c b/epicardium/modules/display.c index c8dc51492ba51f3867405784e30719935282147b..fe89fc324b41f7f9f944b0de4ce087558ac5a45e 100644 --- a/epicardium/modules/display.c +++ b/epicardium/modules/display.c @@ -27,12 +27,40 @@ int epic_disp_print( const char *pString, uint16_t fg, uint16_t bg +) { + return epic_disp_print_adv(DISP_FONT20, posx, posy, pString, fg, bg); +} + +static const sFONT *font_map[] = { + [DISP_FONT8] = &Font8, [DISP_FONT12] = &Font12, + [DISP_FONT16] = &Font16, [DISP_FONT20] = &Font20, + [DISP_FONT24] = &Font24, +}; + +int epic_disp_print_adv( + uint8_t font, + uint16_t posx, + uint16_t posy, + const char *pString, + uint16_t fg, + uint16_t bg ) { int cl = check_lock(); + if (font >= (sizeof(font_map) / sizeof(sFONT *))) { + return -EINVAL; + } if (cl < 0) { return cl; } else { - gfx_puts(&Font20, &display_screen, posx, posy, pString, fg, bg); + gfx_puts( + font_map[font], + &display_screen, + posx, + posy, + pString, + fg, + bg + ); return 0; } } @@ -173,11 +201,7 @@ int epic_disp_framebuffer(union disp_framebuffer *fb) int epic_disp_backlight(uint16_t brightness) { - int cl = check_lock(); - if (cl < 0) { - return cl; - } - + /* TODO: lock? */ LCD_SetBacklight(brightness); return 0; } diff --git a/epicardium/modules/filesystem.h b/epicardium/modules/filesystem.h index 720147ecd04a8f07755052d05adb90871b360bd7..db54a2895621d0e316dee83c362faac737b1a9e2 100644 --- a/epicardium/modules/filesystem.h +++ b/epicardium/modules/filesystem.h @@ -16,9 +16,19 @@ void fatfs_init(void); /** * initialize and mount the FLASH storage + * + * NOTE: not safe to be called from an ISR */ int fatfs_attach(void); + +/** + * asynchronously attach the FLASH storage + * + * safe to be called from an ISR + */ +void fatfs_schedule_attach(void); + /** close all opened FDs, sync and deinitialize FLASH layer */ void fatfs_detach(void); diff --git a/epicardium/modules/hardware.c b/epicardium/modules/hardware.c index cb983dae9ad8a696596c28f5317f7587574f1e7b..17c707217283c41f61bfad79b9b6fb69d7b20d11 100644 --- a/epicardium/modules/hardware.c +++ b/epicardium/modules/hardware.c @@ -2,7 +2,7 @@ #include "api/dispatcher.h" #include "api/interrupt-sender.h" -#include "cdcacm.h" +#include "usb/epc_usb.h" #include "modules/filesystem.h" #include "modules/log.h" #include "modules/modules.h" @@ -152,8 +152,8 @@ int hardware_early_init(void) /* * USB-Serial */ - if (cdcacm_init() < 0) { - LOG_ERR("init", "USB-Serial unavailable"); + if (epic_usb_cdcacm() < 0) { + LOG_ERR("startup", "USB-Serial unavailable"); } /* @@ -177,6 +177,16 @@ int hardware_early_init(void) */ hwlock_init(); + /* + * API Dispatcher Mutex + */ + dispatcher_mutex_init(); + + /* + * MAX30001 mutex init + */ + max30001_mutex_init(); + return 0; } diff --git a/epicardium/modules/light_sensor.c b/epicardium/modules/light_sensor.c index ff2cd81c15b0984ba91239076dc11c418e254fd2..de20b6e2b21c20e269f8fb19b8dde76b6a13ffdb 100644 --- a/epicardium/modules/light_sensor.c +++ b/epicardium/modules/light_sensor.c @@ -27,6 +27,19 @@ static int light_sensor_init() return 0; } +uint16_t epic_light_sensor_read() +{ + if (hwlock_acquire(HWLOCK_ADC, pdMS_TO_TICKS(1000)) != 0) { + return 0; + } + + ADC_StartConvert(ADC_CH_7, 0, 0); + ADC_GetData(&last_value); + + hwlock_release(HWLOCK_ADC); + return last_value; +} + static void readAdcCallback() { if (hwlock_acquire(HWLOCK_ADC, 0) != 0) { diff --git a/epicardium/modules/max30001.c b/epicardium/modules/max30001.c index 5cc3ef3654e19b6ea298ab7ba8e019676dfc4332..811af4bf8aa274443abb1415a7586c2766660991 100644 --- a/epicardium/modules/max30001.c +++ b/epicardium/modules/max30001.c @@ -370,10 +370,14 @@ static void max300001_interrupt_callback(void *_) } /* }}} */ +void max30001_mutex_init(void) +{ + max30001_mutex = xSemaphoreCreateMutexStatic(&max30001_mutex_data); +} + void vMAX30001Task(void *pvParameters) { max30001_task_id = xTaskGetCurrentTaskHandle(); - max30001_mutex = xSemaphoreCreateMutexStatic(&max30001_mutex_data); int lockret = hwlock_acquire(HWLOCK_SPI_ECG, pdMS_TO_TICKS(100)); if (lockret < 0) { diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build index cdf89e5b6108cac80a11368f2b4fbbb6e2c971c4..f2ffa57893dc9e7f3bb8404d3afc16f7e154942d 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -21,5 +21,6 @@ module_sources = files( 'trng.c', 'vibra.c', 'watchdog.c', - 'ws2812.c', + 'usb.c', + 'ws2812.c' ) diff --git a/epicardium/modules/modules.h b/epicardium/modules/modules.h index 9eea0cb6130dbe3e94492e402620100714177af5..2d51f786373cea0bb108212b225df7badf0bdc25 100644 --- a/epicardium/modules/modules.h +++ b/epicardium/modules/modules.h @@ -9,6 +9,7 @@ /* ---------- Dispatcher --------------------------------------------------- */ void vApiDispatcher(void *pvParameters); +void dispatcher_mutex_init(void); extern SemaphoreHandle_t api_mutex; extern TaskHandle_t dispatcher_task_id; @@ -23,10 +24,18 @@ void return_to_menu(void); /* ---------- Serial ------------------------------------------------------- */ #define SERIAL_READ_BUFFER_SIZE 128 +#define SERIAL_WRITE_STREAM_BUFFER_SIZE 512 +void serial_init(); void vSerialTask(void *pvParameters); void serial_enqueue_char(char chr); extern TaskHandle_t serial_task_id; +// For the eSetBit xTaskNotify task semaphore trigger +enum serial_notify{ + SERIAL_WRITE_NOTIFY = 0x01, + SERIAL_READ_NOTIFY = 0x02, +}; + /* ---------- LED Animation / Personal States ------------------------------ */ #define PERSONAL_STATE_LED 14 void vLedTask(void *pvParameters); @@ -91,7 +100,9 @@ void disp_forcelock(); #define BHI160_MUTEX_WAIT_MS 50 void vBhi160Task(void *pvParameters); +/* ---------- MAX30001 ----------------------------------------------------- */ #define MAX30001_MUTEX_WAIT_MS 50 void vMAX30001Task(void *pvParameters); +void max30001_mutex_init(void); #endif /* MODULES_H */ diff --git a/epicardium/modules/serial.c b/epicardium/modules/serial.c index b7be0594f3fcacbee9ac3ed7ab6ff0163c74f1a8..3292ab9330c973a83243b9aad0c3e7b63a6d0200 100644 --- a/epicardium/modules/serial.c +++ b/epicardium/modules/serial.c @@ -4,46 +4,121 @@ #include "modules/modules.h" #include "max32665.h" -#include "cdcacm.h" +#include "usb/cdcacm.h" #include "uart.h" #include "FreeRTOS.h" #include "task.h" #include "queue.h" +#include "stream_buffer.h" #include <stdint.h> #include <stdio.h> +/* The serial console in use (UART0) */ +extern mxc_uart_regs_t *ConsoleUart; + /* Task ID for the serial handler */ TaskHandle_t serial_task_id = NULL; -/* The serial console in use (UART0) */ -extern mxc_uart_regs_t *ConsoleUart; /* Read queue, filled by both UART and CDCACM */ static QueueHandle_t read_queue; +/* Stream Buffer for handling all writes to serial */ +static StreamBufferHandle_t write_stream_buffer = NULL; + +void serial_init() +{ + /* Setup read queue */ + static uint8_t buffer[sizeof(char) * SERIAL_READ_BUFFER_SIZE]; + static StaticQueue_t read_queue_data; + read_queue = xQueueCreateStatic( + SERIAL_READ_BUFFER_SIZE, sizeof(char), buffer, &read_queue_data + ); + + /* Setup write queue */ + static uint8_t ucWrite_stream_buffer[SERIAL_WRITE_STREAM_BUFFER_SIZE]; + static StaticStreamBuffer_t xStream_buffer_struct; + write_stream_buffer = xStreamBufferCreateStatic( + sizeof(ucWrite_stream_buffer), + 1, + ucWrite_stream_buffer, + &xStream_buffer_struct + ); +} /* * API-call to write a string. Output goes to both CDCACM and UART */ void epic_uart_write_str(const char *str, intptr_t length) { - uint32_t basepri = __get_BASEPRI(); - if (xPortIsInsideInterrupt()) { - taskENTER_CRITICAL_FROM_ISR(); - } else { - taskENTER_CRITICAL(); + if (length == 0) { + return; } - UART_Write(ConsoleUart, (uint8_t *)str, length); + /* + * Check if the stream buffer is even initialized yet + */ + if (write_stream_buffer == NULL) { + UART_Write(ConsoleUart, (uint8_t *)str, length); + cdcacm_write((uint8_t *)str, length); + return; + } if (xPortIsInsideInterrupt()) { + BaseType_t resched1 = pdFALSE; + BaseType_t resched2 = pdFALSE; + + /* + * Enter a critial section so no other task can write to the + * stream buffer. + */ + uint32_t basepri = __get_BASEPRI(); + taskENTER_CRITICAL_FROM_ISR(); + + xStreamBufferSendFromISR( + write_stream_buffer, str, length, &resched1 + ); + taskEXIT_CRITICAL_FROM_ISR(basepri); + + if (serial_task_id != NULL) { + xTaskNotifyFromISR( + serial_task_id, + SERIAL_WRITE_NOTIFY, + eSetBits, + &resched2 + ); + } + + /* Yield if this write woke up a higher priority task */ + portYIELD_FROM_ISR(resched1 || resched2); } else { - taskEXIT_CRITICAL(); + size_t bytes_sent = 0; + size_t index = 0; + do { + taskENTER_CRITICAL(); + /* + * Wait time needs to be zero, because we are in a + * critical section. + */ + bytes_sent = xStreamBufferSend( + write_stream_buffer, + &str[index], + length - index, + 0 + ); + index += bytes_sent; + taskEXIT_CRITICAL(); + if (serial_task_id != NULL) { + xTaskNotify( + serial_task_id, + SERIAL_WRITE_NOTIFY, + eSetBits + ); + portYIELD(); + } + } while (index < length); } - - cdcacm_write((uint8_t *)str, length); - ble_uart_write((uint8_t *)str, length); } /* @@ -101,7 +176,12 @@ void UART0_IRQHandler(void) static void uart_callback(uart_req_t *req, int error) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; - vTaskNotifyGiveFromISR(serial_task_id, &xHigherPriorityTaskWoken); + xTaskNotifyFromISR( + serial_task_id, + SERIAL_READ_NOTIFY, + eSetBits, + &xHigherPriorityTaskWoken + ); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } @@ -122,16 +202,8 @@ void serial_enqueue_char(char chr) void vSerialTask(void *pvParameters) { - static uint8_t buffer[sizeof(char) * SERIAL_READ_BUFFER_SIZE]; - static StaticQueue_t read_queue_data; - serial_task_id = xTaskGetCurrentTaskHandle(); - /* Setup read queue */ - read_queue = xQueueCreateStatic( - SERIAL_READ_BUFFER_SIZE, sizeof(char), buffer, &read_queue_data - ); - /* Setup UART interrupt */ NVIC_ClearPendingIRQ(UART0_IRQn); NVIC_DisableIRQ(UART0_IRQn); @@ -145,6 +217,9 @@ void vSerialTask(void *pvParameters) .callback = uart_callback, }; + uint8_t rx_data[20]; + size_t received_bytes; + while (1) { int ret = UART_ReadAsync(ConsoleUart, &read_req); if (ret != E_NO_ERROR && ret != E_BUSY) { @@ -152,18 +227,55 @@ void vSerialTask(void *pvParameters) vTaskDelay(portMAX_DELAY); } - ulTaskNotifyTake(pdTRUE, portTICK_PERIOD_MS * 1000); + ret = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - if (read_req.num > 0) { - serial_enqueue_char(*read_req.data); - } + if (ret & SERIAL_WRITE_NOTIFY) { + do { + received_bytes = xStreamBufferReceive( + write_stream_buffer, + (void *)rx_data, + sizeof(rx_data), + 0 + ); + + if (received_bytes == 0) { + break; + } + + /* + * The SDK-driver for UART is not reentrant + * which means we need to perform UART writes + * in a critical section. + */ + taskENTER_CRITICAL(); + UART_Write( + ConsoleUart, + (uint8_t *)&rx_data, + received_bytes + ); + taskEXIT_CRITICAL(); - while (UART_NumReadAvail(ConsoleUart) > 0) { - serial_enqueue_char(UART_ReadByte(ConsoleUart)); + cdcacm_write( + (uint8_t *)&rx_data, received_bytes + ); + ble_uart_write( + (uint8_t *)&rx_data, received_bytes + ); + } while (received_bytes > 0); } - while (cdcacm_num_read_avail() > 0) { - serial_enqueue_char(cdcacm_read()); + if (ret & SERIAL_READ_NOTIFY) { + if (read_req.num > 0) { + serial_enqueue_char(*read_req.data); + } + + while (UART_NumReadAvail(ConsoleUart) > 0) { + serial_enqueue_char(UART_ReadByte(ConsoleUart)); + } + + while (cdcacm_num_read_avail() > 0) { + serial_enqueue_char(cdcacm_read()); + } } } } diff --git a/epicardium/modules/usb.c b/epicardium/modules/usb.c new file mode 100644 index 0000000000000000000000000000000000000000..e14032e4e3dccb91342630317786d8c35d363b3a --- /dev/null +++ b/epicardium/modules/usb.c @@ -0,0 +1,322 @@ +/** + * + * Implementation of public epic_usb_ interface + * + * Configuration and FLASH-specific implementation of USB mass storage device + * Configuration of USB CDCACM device + * + * USB descriptors for both are defined here. + * + */ + +#include "epicardium.h" + +#include "modules/filesystem.h" + +#include "usb/cdcacm.h" +#include "usb/mass_storage.h" +#include "usb/descriptors.h" +#include "usb/epc_usb.h" + +#include "modules/log.h" + +#include "mx25lba.h" +#include "msc.h" + +/* memory access callbacks for the mass storage (FLASH) device */ +static int mscmem_init(); +static uint32_t mscmem_size(void); +static int mscmem_read(uint32_t lba, uint8_t *buffer); +static int mscmem_write(uint32_t lba, uint8_t *buffer); +static int mscmem_start(); +static int mscmem_stop(); +static int mscmem_ready(); + +/* USB descriptors, definition at the end of this file */ +static usb_device_descriptor_t device_descriptor_cdcacm + __attribute__((aligned(4))); +static struct config_descriptor_cdcacm config_descriptor_cdcacm + __attribute__((aligned(4))); +static usb_device_descriptor_t device_descriptor_msc + __attribute__((aligned(4))); +static struct config_descriptor_msc config_descriptor_msc + __attribute__((aligned(4))); + +/* definitions of config structs */ +static const msc_mem_t s_mscCallbacks = { + mscmem_init, mscmem_start, mscmem_stop, mscmem_ready, + mscmem_size, mscmem_read, mscmem_write, +}; + +static struct esb_device_descriptors s_dd_cdcacm = { + .device = &device_descriptor_cdcacm, .cdcacm = &config_descriptor_cdcacm +}; + +static struct esb_device_descriptors s_dd_msc = { + .device = &device_descriptor_msc, .msc = &config_descriptor_msc +}; + +static struct esb_config s_cfg_cdcacm = { + .name = "cdcacm", + .init = esb_cdc_init, + .configure = esb_cdc_configure, + .deconfigure = esb_cdc_deconfigure, + .deinit = NULL, + + .descriptors = &s_dd_cdcacm, + .deviceData = NULL, +}; + +static struct esb_config s_cfg_msc = { + .name = "msc FLASH", + .init = esb_msc_init, + .configure = esb_msc_configure, + .deconfigure = esb_msc_deconfigure, + .deinit = NULL, + + .descriptors = &s_dd_msc, + .deviceData = (void *)&s_mscCallbacks, +}; + +/* function implementations */ + +static int dirty = 0; +static int mscmem_init() +{ + LOG_DEBUG("msc", "mem.init"); + fatfs_detach(); + return mx25_init(); +} + +static uint32_t mscmem_size(void) +{ + return mx25_size(); +} + +static int mscmem_read(uint32_t lba, uint8_t *buffer) +{ + return mx25_read(lba, buffer); +} + +static int mscmem_write(uint32_t lba, uint8_t *buffer) +{ + if (dirty == 0) { + //bootloader_dirty(); + } + dirty = 2; + return mx25_write(lba, buffer); +} + +static int mscmem_start() +{ + return mx25_start(); +} + +static int mscmem_stop() +{ + LOG_DEBUG("msc", "mem.stop"); + int ret = mx25_stop(); + fatfs_schedule_attach(); + return ret; +} + +static int mscmem_ready() +{ + if (dirty) { + dirty--; + if (dirty == 0) { + LOG_DEBUG("msc", "sync"); + mx25_sync(); + //bootloader_clean(); + } + } + return mx25_ready(); +} + +static bool s_fsDetached = false; +int epic_usb_shutdown(void) +{ + esb_deinit(); + if (s_fsDetached) { + fatfs_attach(); + } + return 0; +} + +int epic_usb_storage(void) +{ + esb_deinit(); + s_fsDetached = true; + return esb_init(&s_cfg_msc); +} + +int epic_usb_cdcacm(void) +{ + esb_deinit(); + if (s_fsDetached) { + fatfs_attach(); + } + return esb_init(&s_cfg_cdcacm); +} + +/* clang-format off */ +static usb_device_descriptor_t device_descriptor_cdcacm = { + .bLength = 0x12, + .bDescriptorType = DT_DEVICE, + .bcdUSB = 0x0110, + .bDeviceClass = CLS_COMM, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize = 0x40, + .idVendor = VNDR_MAXIM, + .idProduct = 0x003C, + .bcdDevice = 0x0100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x00, + .bNumConfigurations = 0x01 +}; + +static struct config_descriptor_cdcacm config_descriptor_cdcacm = { + .config = { + .bLength = 0x09, + .bDescriptorType = DT_CONFIG, + .wTotalLength = 0x0043, /* sizeof(struct config_descriptor_cdcacm) */ + .bNumInterfaces = 0x02, + .bConfigurationValue= 0x01, + .iConfiguration = 0x00, + .bmAttributes = 0xE0, /* (self-powered, remote wakeup) */ + .bMaxPower = 0x01, /* MaxPower is 2ma (units are 2ma/bit) */ + }, + .comm_interface = { /* First Interface Descriptor For Comm Class Interface */ + .bLength = 0x09, + .bDescriptorType = DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x01, + .bInterfaceClass = CLS_COMM, + .bInterfaceSubClass = SCLS_ACM, + .bInterfaceProtocol = PROT_AT_CMDS, + .iInterface = 0x00, + }, + .header_functional = { + 0x05, /* bFunctionalLength = 5 */ + DT_FUNCTIONAL,/* bDescriptorType */ + 0x00, /* bDescriptorSubtype */ + 0x10, 0x01, /* bcdCDC */ + }, + .call_management = { + 0x05, /* bFunctionalLength = 5 */ + DT_FUNCTIONAL,/* bDescriptorType */ + 0x01, /* bDescriptorSubtype */ + 0x03, /* bmCapabilities = Device handles call management itself (0x01), management over data class (0x02) */ + 0x01, /* bmDataInterface */ + }, + .acm_functional = { + 0x04, /* bFunctionalLength = 4 */ + DT_FUNCTIONAL,/* bDescriptorType */ + 0x02, /* bDescriptorSubtype */ + 0x02, /* bmCapabilities */ + }, + .union_functional = { + 0x05, /* bFunctionalLength = 5 */ + DT_FUNCTIONAL,/* bDescriptorType */ + 0x06, /* bDescriptorSubtype */ + 0x00, /* bmMasterInterface */ + 0x01, /* bmSlaveInterface0 */ + }, + .endpoint_notify = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = 0x80 | ESB_ENDPOINT_CDCACM_NOTIFY, + .bmAttributes = ATTR_INTERRUPT, + .wMaxPacketSize = 0x0040, + .bInterval = 0xff, + }, + .data_interface = { + .bLength = 0x09, + .bDescriptorType = DT_INTERFACE, + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = CLS_DATA, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00, //not 1? + }, + .endpoint_out = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = ESB_ENDPOINT_CDCACM_OUT, + .bmAttributes = ATTR_BULK, + .wMaxPacketSize = 0x0040, + .bInterval = 0x00, + }, + .endpoint_in = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = 0x80 | ESB_ENDPOINT_CDCACM_IN, + .bmAttributes = ATTR_BULK, + .wMaxPacketSize = 0x0040, + .bInterval = 0x00 + } +}; + +static usb_device_descriptor_t device_descriptor_msc = { + .bLength = 0x12, + .bDescriptorType = DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = CLS_UNSPECIFIED, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize = 0x40, + .idVendor = VNDR_MAXIM, + .idProduct = 0x4402, + .bcdDevice = 0x0100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01 +}; + +static struct config_descriptor_msc config_descriptor_msc = +{ + .config = { + .bLength = 0x09, + .bDescriptorType = DT_CONFIG, + .wTotalLength = 0x0020, + .bNumInterfaces = 0x01, + .bConfigurationValue= 0x01, + .iConfiguration = 0x00, + .bmAttributes = 0xC0, /* (self-powered, no remote wakeup) */ + .bMaxPower = 0x32, + }, + .msc_interface = { + .bLength = 0x09, + .bDescriptorType = DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = CLS_MASS_STOR, + .bInterfaceSubClass = SCLS_SCSI_CMDS, + .bInterfaceProtocol = PROT_BULK_TRANS, + .iInterface = 0x00, + }, + .endpoint_out = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = ESB_ENDPOINT_MSC_OUT, + .bmAttributes = ATTR_BULK, + .wMaxPacketSize = 0x0040, + .bInterval = 0x00, + }, + .endpoint_in = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = 0x80 | ESB_ENDPOINT_MSC_IN, + .bmAttributes = ATTR_BULK, + .wMaxPacketSize = 0x0040, + .bInterval = 0x00 + } +}; +/* clang-format on */ \ No newline at end of file diff --git a/epicardium/usb/cdcacm.c b/epicardium/usb/cdcacm.c new file mode 100644 index 0000000000000000000000000000000000000000..ea046721a99b672c114831b5c6da752a6579a5b6 --- /dev/null +++ b/epicardium/usb/cdcacm.c @@ -0,0 +1,130 @@ +/** + * + * epicardium-specific implemnetation of cdcacm device + * services CDCACM API + */ + +#include "usb/epc_usb.h" +#include "usb/cdcacm.h" +#include "usb/descriptors.h" + +#include <stdio.h> +#include <stddef.h> +#include <errno.h> + +#include "usb.h" +#include "usb_event.h" +#include "cdc_acm.h" + +#include "modules/log.h" +#include "modules/modules.h" + +#include "FreeRTOS.h" +#include "task.h" + +static inline struct config_descriptor_cdcacm * +descriptors(struct esb_config *self) +{ + return self->descriptors->cdcacm; +} + +/******************************************************************************/ +static volatile int usb_read_complete; // SWYM: only ever written to +static int cb_acm_read_ready(void) +{ + usb_read_complete = 1; + return 0; +} + +int esb_cdc_init(struct esb_config *self) +{ + LOG_DEBUG("cdcacm", "init"); + struct config_descriptor_cdcacm *dsc = descriptors(self); + usb_read_complete = 0; + acm_register_callback( + ACM_CB_READ_READY, + cb_acm_read_ready); //SWYM: actually not needed + return acm_init(&dsc->comm_interface); +} + +int esb_cdc_configure(struct esb_config *self) +{ + struct config_descriptor_cdcacm *dsc = descriptors(self); + + //acm_configure does not keep the amc_cfg_t pointer around + //so stack-local lifetime is fine + acm_cfg_t acm_cfg = { + .in_ep = dsc->endpoint_in.bEndpointAddress & 0x0f, + .in_maxpacket = MXC_USBHS_MAX_PACKET, + .out_ep = dsc->endpoint_out.bEndpointAddress, + .out_maxpacket = MXC_USBHS_MAX_PACKET, + .notify_ep = dsc->endpoint_notify.bEndpointAddress & 0x0f, + .notify_maxpacket = MXC_USBHS_MAX_PACKET, + }; + + LOG_DEBUG( + "cdcacm", + "configure, endpoints %d,%d,%d", + acm_cfg.in_ep, + acm_cfg.out_ep, + acm_cfg.notify_ep + ); + + return acm_configure(&acm_cfg); +} + +int esb_cdc_deconfigure(struct esb_config *self) +{ + LOG_DEBUG("cdcacm", "deconfigure"); + return acm_deconfigure(); +} + +int cdcacm_num_read_avail(void) +{ + return acm_canread(); +} + +uint8_t cdcacm_read(void) +{ + while (acm_canread() <= 0) { + } + + uint8_t buf; + acm_read(&buf, 1); + return buf; +} + +void cdcacm_write(uint8_t *data, int len) +{ + static int lockup_disable = 0; + if (acm_present() && !lockup_disable) { + int ret = acm_write(data, len); + if (ret < 0) { + LOG_ERR("cdcacm", "fifo lockup detected"); + lockup_disable = 1; + } else if (ret != len) { + LOG_WARN( + "cdcacm", "write length mismatch, got %d", ret + ); + } + } +} + +/******************************************************************************/ + +extern TaskHandle_t serial_task_id; +void USB_IRQHandler(void) +{ + usb_event_handler(); + + if (serial_task_id != NULL) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xTaskNotifyFromISR( + serial_task_id, + SERIAL_READ_NOTIFY, + eSetBits, + &xHigherPriorityTaskWoken + ); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } +} diff --git a/epicardium/usb/cdcacm.h b/epicardium/usb/cdcacm.h new file mode 100644 index 0000000000000000000000000000000000000000..6683e05558aa06ffef793e3f5a8ab4fe03df1a35 --- /dev/null +++ b/epicardium/usb/cdcacm.h @@ -0,0 +1,15 @@ +#ifndef CDCACM_H +#define CDCACM_H + +#include "usb/epc_usb.h" + +// callbacks for esb_config +int esb_cdc_init(struct esb_config* self); +int esb_cdc_configure(struct esb_config* self); +int esb_cdc_deconfigure(struct esb_config* self); + +int cdcacm_num_read_avail(void); +uint8_t cdcacm_read(void); +void cdcacm_write(uint8_t *data, int len); + +#endif diff --git a/epicardium/usb/descriptors.h b/epicardium/usb/descriptors.h new file mode 100644 index 0000000000000000000000000000000000000000..2fb2cc452ffec6cfe35106c5e311df1fc09a67ac --- /dev/null +++ b/epicardium/usb/descriptors.h @@ -0,0 +1,83 @@ +#ifndef _DESCRIPTORS_H_ +#define _DESCRIPTORS_H_ + +#include <stdint.h> +#include "usb.h" + +#define ESB_ENDPOINT_MSC_IN 2 +#define ESB_ENDPOINT_MSC_OUT 1 +#define ESB_ENDPOINT_CDCACM_NOTIFY 3 +#define ESB_ENDPOINT_CDCACM_IN 2 +#define ESB_ENDPOINT_CDCACM_OUT 1 + +/* device types */ +#define DT_DEVICE 0x01 +#define DT_CONFIG 0x02 +#define DT_INTERFACE 0x04 +#define DT_ENDPOINT 0x05 +#define DT_FUNCTIONAL 0x24 + +/* interface classes */ +#define CLS_UNSPECIFIED 0x00 +#define CLS_COMM 0x02 +#define CLS_MASS_STOR 0x08 +#define CLS_DATA 0x0a + +/* sub-classes */ +#define SCLS_NONE 0x00 +#define SCLS_ACM 0x02 +#define SCLS_SCSI_CMDS 0x06 + +/* interface protocols */ +#define PROT_AT_CMDS 0x01 +#define PROT_BULK_TRANS 0x50 + +/* endpoint attributes */ +#define ATTR_BULK 0x02 +#define ATTR_INTERRUPT 0x03 + + +#define VNDR_MAXIM 0x0B6A + +#if defined(__GNUC__) +#define PACKED_STRUCT struct __attribute__((packed)) +#else +#define PACKED_STRUCT __packed struct +#endif + + + + +PACKED_STRUCT +config_descriptor_cdcacm { + usb_configuration_descriptor_t config; + usb_interface_descriptor_t comm_interface; + uint8_t header_functional[5]; + uint8_t call_management[5]; + uint8_t acm_functional[4]; + uint8_t union_functional[5]; + usb_endpoint_descriptor_t endpoint_notify; + usb_interface_descriptor_t data_interface; + usb_endpoint_descriptor_t endpoint_out; + usb_endpoint_descriptor_t endpoint_in; +}; + +PACKED_STRUCT +config_descriptor_msc { + usb_configuration_descriptor_t config; + usb_interface_descriptor_t msc_interface; + usb_endpoint_descriptor_t endpoint_out; + usb_endpoint_descriptor_t endpoint_in; +}; + +struct esb_device_descriptors { + usb_device_descriptor_t* device; + union { + usb_configuration_descriptor_t* config; + struct config_descriptor_cdcacm* cdcacm; + struct config_descriptor_msc* msc; + }; +}; + + +#endif /* _DESCRIPTORS_H_ */ diff --git a/epicardium/usb/epc_usb.c b/epicardium/usb/epc_usb.c new file mode 100644 index 0000000000000000000000000000000000000000..b99c34a075689a1f470fd66119359f844547bd6a --- /dev/null +++ b/epicardium/usb/epc_usb.c @@ -0,0 +1,459 @@ +/** + * + * + * Core of the USB implementation: + * + * - device independent + * - handles setup of MAXUSB stack + * - handles events and state of the MAXUSB stack + * + * Also contains definitions of device-independent String descriptors + * + * + * + * + * + * swym's USB ramblings: + * + * setting up USB basically consists of two parts: + * + * - set up descriptors + * - initialize the corresponding MAXUSB stacks - acm, msc... + * + * at the moment, the descriptors are statically configured in + * descriptors.h several descriptors point to enumeration descriptors + * via iFooBar indices. Those enumeration descriptors are registered + * via enum_register_descriptor(type, desc, idx), where the idx passed + * corresponds to the iFooBar index mentioned above. + * + * There are several callbacks, some of which do not perform anything meaningful + * at the moment. For example usb_write_callback sets a global flag that is never + * read from. These will be removed later on. + * + * The initialization routines of acm & msc refer to the descriptors from which, + * among other things, they get their enpoint IDs. These are hard-coded right now + * but it would make sense to allocate these IDs at runtime. There is, AFAICT, no + * reason for these endpoint IDs to be 3, 1, 2 in the CDCACM case for example. + * + * Allocating those at runtime would also make the setup less rigid: we can have all + * combinations of {cdcacm, storage} interfaces without the awkward descriptor hackery + * in epc_usb_init. + * + * To generalize even further, the descriptors could be malloced. The total length of + * the descriptor structure can easily be derived from the number of interfaces to be + * initialized. The memory allocated will then be split up (with alignment of the single + * descriptor structs taken care of) in a simple bump-allocator strategy on demand. + * + * + */ +#include "usb/epc_usb.h" + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +#include <FreeRTOS.h> +#include <task.h> +#include <timers.h> + +#include "mxc_config.h" +#include "mxc_sys.h" +#include "mxc_delay.h" + +// MAXUSB includes +#include "usb.h" +#include "usb_event.h" +#include "enumerate.h" +#include "msc.h" +#include "cdc_acm.h" + +#include "usb/descriptors.h" +#include "modules/log.h" +#include "modules/filesystem.h" + +//#define USE_REMOTE_WAKE_ENABLE + +/***** Function Prototypes *****/ +static int cb_usb_setconfig(usb_setup_pkt *sud, void *cbdata); +#ifdef USE_REMOTE_WAKE_ENABLE +static int cb_usb_setfeature(usb_setup_pkt *sud, void *cbdata); +static int cb_usb_clrfeature(usb_setup_pkt *sud, void *cbdata); +#endif +static int cb_usb_event(maxusb_event_t evt, void *data); +static int cb_usb_shutdown(); +static int cb_usb_init(); +static void usb_app_sleep(void); +static void usb_app_wakeup(void); +/* needed for usb_opts. mxc_delay() takes unsigned long, so can't use it directly */ +static void delay_us(unsigned int usec); +static void device_deinit(); +static bool device_deconfigure(); +static int device_configure(); + +volatile int suspended; +#ifdef USE_REMOTE_WAKE_ENABLE +int remote_wake_en; //SWYM: unused, only written to! +#endif + +static StaticTimer_t x; +static TimerHandle_t timerWakeup = NULL; +static void cb_timerReset(TimerHandle_t t); + +static struct esb_config s_device = { 0 }; + +__attribute__((aligned(4))) uint8_t lang_id_desc[] = { + 0x04, /* bLength */ + 0x03, /* bDescriptorType */ + 0x09, + 0x04 /* bString = wLANGID (see usb_20.pdf 9.6.7 String) */ +}; + +__attribute__((aligned(4))) uint8_t mfg_id_desc[] = { + 0x22, /* bLength */ + 0x03, /* bDescriptorType */ + 'M', 0, 'a', 0, 'x', 0, 'i', 0, 'm', 0, ' ', 0, 'I', 0, 'n', 0, + 't', 0, 'e', 0, 'g', 0, 'r', 0, 'a', 0, 't', 0, 'e', 0, 'd', 0, +}; + +__attribute__((aligned(4))) uint8_t prod_id_desc[] = { + 0x24, /* bLength */ + 0x03, /* bDescriptorType */ + 'C', 0, 'A', 0, 'R', 0, 'D', 0, '1', 0, '0', 0, ' ', 0, 'U', 0, 'S', 0, + 'B', 0, ' ', 0, 'G', 0, 'A', 0, 'D', 0, 'G', 0, 'E', 0, 'T', 0, +}; + +/* Not currently used (see device descriptor), but could be enabled if desired */ +__attribute__((aligned(4))) +uint8_t serial_id_desc[] = { 0x14, /* bLength */ + 0x03, /* bDescriptorType */ + 'c', 0, 'a', 0, 'a', 0, 'a', 0, 'a', + 0, 'd', 0, '1', 0, '0', 0, '0', 0 }; + +int cb_usb_init() +{ + const sys_cfg_usbhs_t sys_usbhs_cfg = NULL; + LOG_DEBUG("usb", "init"); + return SYS_USBHS_Init(&sys_usbhs_cfg); +} + +volatile bool shutDownCompleted; +void do_usb_shutdown() +{ + LOG_DEBUG("usb", "shutting down..."); + shutDownCompleted = false; + usb_shutdown(); + for (int i = 0; i < 10000 && !shutDownCompleted; ++i) { + } +} +int cb_usb_shutdown() +{ + SYS_USBHS_Shutdown(); + SYS_Reset_Periph(SYS_RESET_USB); + device_deinit(); + LOG_DEBUG("usb", "shutdown complete"); + shutDownCompleted = true; + return 0; +} + +/* User-supplied function to delay usec micro-seconds */ +static void delay_us(unsigned int usec) +{ + /* mxc_delay() takes unsigned long, so can't use it directly */ + mxc_delay(usec); +} + +static void device_deinit() +{ + esb_cfg_handler deinit = s_device.deinit; + s_device.deinit = NULL; + s_device.configure = NULL; + s_device.deconfigure = NULL; + if (deinit) { + LOG_DEBUG("usb", "deinit"); + deinit(&s_device); + } +} + +//de-configure last device, if any +static volatile bool s_configured = false; +static bool device_deconfigure() +{ + if (s_configured) { + s_configured = false; + if (s_device.deconfigure) { + LOG_DEBUG("usb", "deconfigure"); + enum_clearconfig(); + s_device.deconfigure(&s_device); + return true; + } + } + return false; +} + +static int device_configure() +{ + if (!s_configured && s_device.configure) { + s_configured = true; + LOG_DEBUG("usb", "configure"); + return s_device.configure(&s_device); + } + return 0; +} + +static volatile bool s_connected = false; +static void disconnect() +{ + if (s_connected) { + LOG_DEBUG("usb", "disconnect"); + s_connected = false; + usb_disconnect(); + // SYS_Reset_Periph(SYS_RESET_USB); + mxc_delay(10000); + } +} +static void connect() +{ + s_connected = true; + usb_connect(); +} + +static volatile bool s_initialized = false; +void esb_deinit(void) +{ + if (s_initialized) { + s_initialized = false; + disconnect(); + device_deconfigure(); + do_usb_shutdown(); + device_deinit(); + } +} +int esb_init(struct esb_config *cfg) +{ + esb_deinit(); + if (cfg == NULL) { + return 0; + } + if (timerWakeup == NULL) { + timerWakeup = xTimerCreateStatic( + "timerWakeup", /* name */ + pdMS_TO_TICKS(50), /* period/time */ + pdFALSE, /* auto reload */ + NULL, /* timer ID */ + cb_timerReset, + &x); + } + + maxusb_cfg_options_t usb_opts; + + /* Initialize state */ + suspended = 0; +#ifdef USE_REMOTE_WAKE_ENABLE + remote_wake_en = 0; +#endif + + /* Start out in full speed */ + usb_opts.enable_hs = 0; + usb_opts.delay_us = delay_us; + usb_opts.init_callback = cb_usb_init; + usb_opts.shutdown_callback = cb_usb_shutdown; + + /* Initialize the usb module */ + if (usb_init(&usb_opts) != 0) { + LOG_ERR("usb", "usb_init() failed"); + return -EIO; + } + + /* Initialize the enumeration module */ + if (enum_init() != 0) { + do_usb_shutdown(); + LOG_ERR("usb", "enum_init() failed"); + return -EIO; + } + + LOG_INFO("usb", "initializing device %s", cfg->name); + if (cfg->init(cfg)) { + enum_clearconfig(); + do_usb_shutdown(); + LOG_ERR("usb", "device init failed"); + return -EIO; + } + + s_initialized = true; + s_device = *cfg; + + enum_register_descriptor( + ENUM_DESC_DEVICE, (uint8_t *)cfg->descriptors->device, 0 + ); + enum_register_descriptor( + ENUM_DESC_CONFIG, (uint8_t *)cfg->descriptors->config, 0 + ); + enum_register_descriptor(ENUM_DESC_STRING, lang_id_desc, 0); + enum_register_descriptor(ENUM_DESC_STRING, mfg_id_desc, 1); + enum_register_descriptor(ENUM_DESC_STRING, prod_id_desc, 2); + enum_register_descriptor(ENUM_DESC_STRING, serial_id_desc, 3); + //enum_register_descriptor(ENUM_DESC_STRING, cdcacm_func_desc, 4); + //enum_register_descriptor(ENUM_DESC_STRING, msc_func_desc, 5); + + /* Handle configuration */ + enum_register_callback(ENUM_SETCONFIG, cb_usb_setconfig, NULL); +#ifdef USE_REMOTE_WAKE_ENABLE + /* Handle feature set/clear */ + enum_register_callback(ENUM_SETFEATURE, cb_usb_setfeature, NULL); + enum_register_callback(ENUM_CLRFEATURE, cb_usb_clrfeature, NULL); +#endif + + /* Register callbacks */ + usb_event_enable(MAXUSB_EVENT_NOVBUS, cb_usb_event, NULL); + usb_event_enable(MAXUSB_EVENT_VBUS, cb_usb_event, NULL); + + /* Start with USB in low power mode */ + usb_app_sleep(); + /* TODO: Fix priority */ + NVIC_SetPriority(USB_IRQn, 6); + NVIC_EnableIRQ(USB_IRQn); + + return 0; +} + +static int cb_usb_setconfig(usb_setup_pkt *sud, void *cbdata) +{ + LOG_DEBUG("usb", "setconfig %d", sud->wValue); + if (sud->wValue == s_device.descriptors->config->bConfigurationValue) { + return device_configure(); + } else if (sud->wValue == 0) { + device_deconfigure(); + return 0; + } + return -1; +} + +#ifdef USE_REMOTE_WAKE_ENABLE +static int cb_usb_setfeature(usb_setup_pkt *sud, void *cbdata) +{ + if (sud->wValue == FEAT_REMOTE_WAKE) { + remote_wake_en = 1; + return 0; + } + return -1; +} + +static int cb_usb_clrfeature(usb_setup_pkt *sud, void *cbdata) +{ + if (sud->wValue == FEAT_REMOTE_WAKE) { + remote_wake_en = 0; + return 0; + } + return -1; +} +#endif + +/******************************************************************************/ +static void usb_app_sleep(void) +{ + /* TODO: Place low-power code here */ + suspended = 1; +} + +/******************************************************************************/ +static void usb_app_wakeup(void) +{ + /* TODO: Place low-power code here */ + suspended = 0; +} + +static void cb_timerReset(TimerHandle_t t) +{ + (void)t; + LOG_DEBUG("usb", "cb_timerReset %08x", usb_get_status()); + + LOG_DEBUG("usb", "SYS_USBHS_Shutdown"); + SYS_USBHS_Shutdown(); + LOG_DEBUG("usb", "SYS_Reset_Periph"); + SYS_Reset_Periph(SYS_RESET_USB); + + //copy-paste from esb_init(), need to refactor + maxusb_cfg_options_t usb_opts; + usb_opts.enable_hs = 0; + usb_opts.delay_us = delay_us; + usb_opts.init_callback = cb_usb_init; + usb_opts.shutdown_callback = cb_usb_shutdown; + + /* Initialize the usb module */ + if (usb_init(&usb_opts) != 0) { + LOG_ERR("usb", "usb_init() failed"); + return; + } + usb_event_enable(MAXUSB_EVENT_NOVBUS, cb_usb_event, NULL); + usb_event_enable(MAXUSB_EVENT_VBUS, cb_usb_event, NULL); +} + +static void scheduleReset() +{ + LOG_DEBUG("usb", "scheduleReset"); + xTimerChangePeriodFromISR(timerWakeup, pdMS_TO_TICKS(500), NULL); +} + +#if LOG_ENABLE_DEBUG +static const char *maxusb_event_string(maxusb_event_t evt) +{ + static const char *names[MAXUSB_NUM_EVENTS] = { + [MAXUSB_EVENT_DPACT] = "DPACT", + [MAXUSB_EVENT_RWUDN] = "RWUDN", + [MAXUSB_EVENT_BACT] = "BACT", + [MAXUSB_EVENT_BRST] = "BRST", + [MAXUSB_EVENT_SUSP] = "SUSP", + [MAXUSB_EVENT_NOVBUS] = "NOVBUS", + [MAXUSB_EVENT_VBUS] = "VBUS", + [MAXUSB_EVENT_BRSTDN] = "BRSTDN", + [MAXUSB_EVENT_SUDAV] = "SUDAV", + }; + return evt < MAXUSB_NUM_EVENTS ? names[evt] : "<INVALID>"; +} +#endif + +/******************************************************************************/ +static int cb_usb_event(maxusb_event_t evt, void *data) +{ + static int s_VBUS_SUSP_sequence = 0; + LOG_DEBUG("usb", "event %s (%d)\n", maxusb_event_string(evt), evt); + + switch (evt) { + case MAXUSB_EVENT_NOVBUS: + usb_event_disable(MAXUSB_EVENT_BRST); + usb_event_disable(MAXUSB_EVENT_SUSP); + usb_event_disable(MAXUSB_EVENT_DPACT); + usb_disconnect(); + device_deconfigure(); + usb_app_sleep(); + break; + case MAXUSB_EVENT_VBUS: + s_VBUS_SUSP_sequence = 1; + usb_event_clear(MAXUSB_EVENT_BRST); + usb_event_enable(MAXUSB_EVENT_BRST, cb_usb_event, NULL); + usb_event_clear(MAXUSB_EVENT_SUSP); + usb_event_enable(MAXUSB_EVENT_SUSP, cb_usb_event, NULL); + connect(); + usb_app_sleep(); + break; + case MAXUSB_EVENT_BRST: + usb_app_wakeup(); + device_deconfigure(); + suspended = 0; + break; + case MAXUSB_EVENT_SUSP: + if (!s_VBUS_SUSP_sequence) { + scheduleReset(); + } + s_VBUS_SUSP_sequence = 0; + //usb_app_sleep(); + break; + case MAXUSB_EVENT_DPACT: + usb_app_wakeup(); + break; + default: + break; + } + + return 0; +} diff --git a/epicardium/usb/epc_usb.h b/epicardium/usb/epc_usb.h new file mode 100644 index 0000000000000000000000000000000000000000..c6917bd328b270f240ab38a7c8e11f10aa4fea21 --- /dev/null +++ b/epicardium/usb/epc_usb.h @@ -0,0 +1,32 @@ +#ifndef EPICARDIUM_USB_EPC_USB_H_INCLUDED +#define EPICARDIUM_USB_EPC_USB_H_INCLUDED + +/** + * + * EPC - it's not just a Universal Serial Bus, + * it's an Epic Serial Bus! + */ + + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +struct esb_config; +typedef int (*esb_cfg_handler)(struct esb_config* self); + +struct esb_config { + const char* name; + esb_cfg_handler init; + esb_cfg_handler configure; + esb_cfg_handler deconfigure; + esb_cfg_handler deinit; + + struct esb_device_descriptors* descriptors; + void* deviceData; +}; + +int esb_init(struct esb_config* cfg); +void esb_deinit(void); + +#endif//EPICARDIUM_USB_EPC_USB_H_INCLUDED diff --git a/epicardium/usb/mass_storage.c b/epicardium/usb/mass_storage.c new file mode 100644 index 0000000000000000000000000000000000000000..b3c6c0f6cd04efb5cf02e8eb898b3eef7b02efe3 --- /dev/null +++ b/epicardium/usb/mass_storage.c @@ -0,0 +1,55 @@ +/** + * backend-independent implementation of the mass storage device + * services MSC API and esb_cfg API + */ + +#include "usb/mass_storage.h" +#include "usb/epc_usb.h" +#include "usb/descriptors.h" + +#include "modules/log.h" + +#include "msc.h" +#include "usb.h" +#include "max32665.h" + +#define VENDOR_STRING "CCC" +#define PRODUCT_STRING "card10" +#define VERSION_STRING "1.0" + +static inline struct config_descriptor_msc *descriptors(struct esb_config *self) +{ + return self->descriptors->msc; +} + +int esb_msc_configure(struct esb_config *self) +{ + LOG_DEBUG("msc", "configure"); + struct config_descriptor_msc *dsc = descriptors(self); + const msc_cfg_t msc_cfg = { + dsc->endpoint_out.bEndpointAddress, + MXC_USBHS_MAX_PACKET, /* OUT max packet size */ + dsc->endpoint_in.bEndpointAddress & 0x0f, + MXC_USBHS_MAX_PACKET, /* IN max packet size */ + }; + return msc_configure(&msc_cfg); +} + +int esb_msc_deconfigure(struct esb_config *self) +{ + LOG_DEBUG("msc", "deconfigure"); + (void)self; + return msc_deconfigure(); +} + +int esb_msc_init(struct esb_config *self) +{ + LOG_DEBUG("msc", "init"); + static const msc_idstrings_t ids = { VENDOR_STRING, + PRODUCT_STRING, + VERSION_STRING }; + + msc_mem_t *mem = self->deviceData; + struct config_descriptor_msc *dsc = descriptors(self); + return msc_init(&dsc->msc_interface, &ids, mem); +} diff --git a/epicardium/usb/mass_storage.h b/epicardium/usb/mass_storage.h new file mode 100644 index 0000000000000000000000000000000000000000..77c36bcecd433be56305a94a180945b6dba38dff --- /dev/null +++ b/epicardium/usb/mass_storage.h @@ -0,0 +1,10 @@ +#ifndef EPICARDIUM_USB_MSC_H_INCLUDED +#define EPICARDIUM_USB_MSC_H_INCLUDED + +#include "usb/epc_usb.h" + +int esb_msc_configure(struct esb_config* self); +int esb_msc_deconfigure(struct esb_config* self); +int esb_msc_init(struct esb_config* self); + +#endif//EPICARDIUM_USB_MSC_H_INCLUDED diff --git a/lib/gfx/Fonts/font12CN.c b/lib/gfx/Fonts/font12CN.c deleted file mode 100644 index 325617a3430dbe4f2ad08f013ccf8c05adbd6e83..0000000000000000000000000000000000000000 --- a/lib/gfx/Fonts/font12CN.c +++ /dev/null @@ -1,120 +0,0 @@ -/** - ****************************************************************************** - * @file Font12.c - * @author MCD Application Team - * @version V1.0.0 - * @date 18-February-2014 - * @brief This file provides text Font12 for STM32xx-EVAL's LCD driver. - ****************************************************************************** - * @attention - * - * <h2><center>© COPYRIGHT(c) 2014 STMicroelectronics</center></h2> - * - * 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. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * 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. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "fonts.h" - - -// -// Font data for Courier New 12pt -// - -const CH_CN Font12CN_Table[] = -{ -/*-- ����: �� --*/ -/*-- ���ź�12; �������¶�Ӧ�ĵ���Ϊ����x��=16x21 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1D,0xC0,0x1D,0x80,0x3B,0xFF,0x3B,0x07, -0x3F,0x77,0x7E,0x76,0xF8,0x70,0xFB,0xFE,0xFB,0xFE,0x3F,0x77,0x3F,0x77,0x3E,0x73, -0x38,0x70,0x38,0x70,0x3B,0xE0,0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�12; �������¶�Ӧ�ĵ���Ϊ����x��=16x21 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x73,0xFF,0x70,0x0F,0xFE,0x1E, -0x7E,0x3C,0x6E,0x38,0xEE,0x30,0xEF,0xFF,0xFC,0x30,0x7C,0x30,0x38,0x30,0x3E,0x30, -0x7E,0x30,0xE0,0x30,0xC1,0xF0,0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�12; �������¶�Ӧ�ĵ���Ϊ����x��=16x21 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x0E,0x30,0x0E,0x3F,0xEE,0x30,0xEE, -0xFC,0xFF,0x76,0xCE,0x77,0xFE,0x7B,0xFE,0xFF,0xFE,0xF3,0xDE,0xF3,0xCE,0x37,0xEE, -0x3E,0x6E,0x3C,0x0E,0x30,0x3E,0x00,0x00,0x00,0x00}, - -/*-- ����: ݮ --*/ -/*-- ���ź�12; �������¶�Ӧ�ĵ���Ϊ����x��=16x21 --*/ -{"ݮ", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x70,0xFF,0xFF,0x3E,0x70,0x38,0x00, -0x7F,0xFF,0xE0,0x00,0xFF,0xFC,0x3B,0x8C,0x39,0xCC,0xFF,0xFF,0x73,0x9C,0x71,0xDC, -0x7F,0xFF,0x00,0x1C,0x01,0xF8,0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�12; �������¶�Ӧ�ĵ���Ϊ����x��=16x21 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x1F,0xFF,0xF0,0x3E,0x00,0x0E,0x1F, -0xCF,0xFB,0xFF,0xF8,0x3F,0xFF,0x0F,0xFF,0x7F,0xD8,0x7F,0xDC,0x6F,0xCE,0xED,0xFF, -0xFD,0xF7,0xF9,0xC0,0x00,0x00,0x00,0x00,0x00,0x00}, - -/*-- ����: a --*/ -/*-- ���ź�12; �������¶�Ӧ�ĵ���Ϊ����x��=16x21 --*/ -{"a", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x3E,0x00,0x67,0x00,0x07,0x80,0x0F,0x80,0x7F,0x80,0xE3,0x80,0xE7,0x80,0xE7,0x80, -0x7F,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - -/*-- ����: b --*/ -/*-- ���ź�12; �������¶�Ӧ�ĵ���Ϊ����x��=16x21 --*/ -{"b", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00, -0x7F,0x00,0x7B,0x80,0x71,0xC0,0x71,0xC0,0x71,0xC0,0x71,0xC0,0x71,0xC0,0x7B,0x80, -0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - -/*-- ����: c --*/ -/*-- ���ź�12; �������¶�Ӧ�ĵ���Ϊ����x��=16x21 --*/ -{"c", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x3F,0x00,0x73,0x00,0xF0,0x00,0xE0,0x00,0xE0,0x00,0xE0,0x00,0xF0,0x00,0x73,0x00, -0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - -/*-- ����: A --*/ -/*-- ���ź�12; �������¶�Ӧ�ĵ���Ϊ����x��=16x21 --*/ -{"A", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x1F,0x00,0x1F,0x00, -0x1F,0x00,0x3B,0x80,0x3B,0x80,0x71,0x80,0x7F,0xC0,0x71,0xC0,0xE0,0xE0,0xE0,0xE0, -0xE0,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, -}; - -cFONT Font12CN = { - Font12CN_Table, - sizeof(Font12CN_Table)/sizeof(CH_CN), /*size of table*/ - 11, /* ASCII Width */ - 16, /* Width */ - 21, /* Height */ -}; - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/gfx/Fonts/font24CN.c b/lib/gfx/Fonts/font24CN.c deleted file mode 100644 index 70140e3012d94b7beea5adffbde12b224eab2f5e..0000000000000000000000000000000000000000 --- a/lib/gfx/Fonts/font24CN.c +++ /dev/null @@ -1,465 +0,0 @@ -/** - ****************************************************************************** - * @file Font12.c - * @author MCD Application Team - * @version V1.0.0 - * @date 18-February-2014 - * @brief This file provides text Font12 for STM32xx-EVAL's LCD driver. - ****************************************************************************** - * @attention - * - * <h2><center>© COPYRIGHT(c) 2014 STMicroelectronics</center></h2> - * - * 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. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * 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. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "fonts.h" - - -// -// Font data for Courier New 12pt -// - -const CH_CN Font24CN_Table[] = -{ -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xC1,0xC0,0x00, -0x01,0xE3,0xE0,0x00,0x03,0xE3,0xC0,0x00,0x03,0xC7,0x80,0x00,0x03,0xC7,0xFF,0xFF, -0x07,0x8F,0xFF,0xFF,0x07,0x8F,0x00,0x0F,0x0F,0x1E,0x00,0x1E,0x0F,0x3C,0x1E,0x1E, -0x1F,0x3C,0x1E,0x3E,0x1F,0x18,0x1E,0x3C,0x3F,0x00,0x1E,0x1C,0x7F,0x00,0x1E,0x00, -0x7F,0x07,0x9E,0x70,0xFF,0x07,0x9E,0xF0,0xEF,0x0F,0x9E,0x78,0x6F,0x0F,0x1E,0x78, -0x0F,0x0F,0x1E,0x3C,0x0F,0x1E,0x1E,0x3C,0x0F,0x1E,0x1E,0x1E,0x0F,0x3C,0x1E,0x1E, -0x0F,0x3C,0x1E,0x1F,0x0F,0x7C,0x1E,0x0F,0x0F,0x78,0x1E,0x0E,0x0F,0x00,0x1E,0x00, -0x0F,0x00,0x1E,0x00,0x0F,0x00,0x3C,0x00,0x0F,0x07,0xFC,0x00,0x0F,0x07,0xF8,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, -0x0F,0x07,0xFF,0xFE,0x0F,0x07,0xFF,0xFE,0x0F,0x00,0x00,0x3E,0x1E,0x00,0x00,0xFC, -0xFF,0xF8,0x01,0xF0,0xFF,0xF8,0x03,0xE0,0x1E,0x78,0x07,0xC0,0x1E,0x78,0x0F,0x80, -0x3C,0x78,0x0F,0x00,0x3C,0x78,0x0F,0x00,0x3C,0x78,0x0F,0x00,0x3C,0x78,0x0F,0x00, -0x3C,0x7F,0xFF,0xFF,0x78,0xFF,0xFF,0xFF,0x78,0xF0,0x0F,0x00,0x78,0xF0,0x0F,0x00, -0x3D,0xE0,0x0F,0x00,0x1F,0xE0,0x0F,0x00,0x0F,0xE0,0x0F,0x00,0x07,0xC0,0x0F,0x00, -0x07,0xE0,0x0F,0x00,0x07,0xF0,0x0F,0x00,0x0F,0xF8,0x0F,0x00,0x1E,0x7C,0x0F,0x00, -0x3C,0x38,0x0F,0x00,0x78,0x00,0x0F,0x00,0xF0,0x03,0xFF,0x00,0x60,0x01,0xFE,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x07,0x01,0xE0,0x07,0x87,0x01,0xE0, -0x07,0x07,0x01,0xC0,0x0F,0xF7,0x79,0xC0,0x1E,0xF7,0x7B,0xC0,0x1E,0xF7,0x7B,0x80, -0x3C,0xF7,0x7B,0xFF,0x78,0xF7,0x7B,0xFF,0xF8,0xF7,0x7F,0x9E,0xF7,0xFF,0xFF,0x9E, -0x67,0xFF,0xFF,0x9E,0x07,0x00,0x7F,0x9C,0x0F,0x00,0x0F,0x9C,0x1E,0x00,0x1F,0x9C, -0x1E,0x7F,0xFF,0xBC,0x3E,0x7F,0xF3,0xFC,0x3E,0x00,0x03,0xFC,0x7E,0x00,0x01,0xF8, -0xFE,0x00,0x01,0xF8,0xFE,0x7F,0xE1,0xF8,0xDE,0x7F,0xE1,0xF8,0x1E,0x78,0xE0,0xF0, -0x1E,0x78,0xEE,0xF0,0x1E,0x78,0xFF,0xF0,0x1E,0x78,0xFD,0xF8,0x1E,0x79,0xFB,0xFC, -0x1E,0xF1,0xF7,0xBC,0x1E,0xF0,0xEF,0x9E,0x1F,0xE0,0x0F,0x0F,0x1E,0xC0,0x1E,0x0F, -0x1E,0x00,0x0C,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x03,0xC0,0x78,0x00,0x07,0x80,0x78,0x00,0x07,0x80,0x78,0x00, -0x07,0x80,0xF0,0x00,0x0F,0x00,0xF0,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, -0x1E,0x03,0xC0,0x1F,0x1E,0x03,0xC0,0x1E,0x1F,0xE7,0x8F,0x3E,0x3D,0xE7,0x8F,0x3C, -0x3D,0xEF,0x0F,0x7C,0x3D,0xE7,0x0F,0x78,0x79,0xE0,0x0F,0x00,0x79,0xE0,0x0E,0x00, -0x7F,0xFE,0x0E,0x00,0x7F,0xFE,0x1F,0x00,0x01,0xE0,0x1F,0x00,0x01,0xE0,0x1F,0x00, -0x01,0xE0,0x1F,0x80,0x01,0xE0,0x1F,0x80,0x01,0xE0,0x3F,0x80,0x01,0xFF,0x3F,0xC0, -0x0F,0xFF,0x7B,0xC0,0xFF,0xF0,0x79,0xE0,0xF9,0xE0,0xF1,0xF0,0x01,0xE1,0xF0,0xF0, -0x01,0xE3,0xE0,0xF8,0x01,0xE7,0xC0,0x7C,0x01,0xFF,0x80,0x3F,0x01,0xFF,0x00,0x1F, -0x01,0xEC,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x77,0x00,0x00,0x00,0xFF,0x00, -0x7F,0xFC,0xF7,0x80,0x7F,0xFD,0xE3,0xC0,0x01,0xC1,0xE3,0xC0,0x01,0xC3,0xC1,0x80, -0x3D,0xC7,0xFF,0xFF,0x39,0xC7,0xFF,0xFF,0x39,0xCF,0x83,0x80,0x79,0xDF,0x83,0x80, -0x79,0xFF,0x83,0x80,0x79,0xDF,0x83,0x80,0x71,0xC3,0x83,0x80,0x7F,0xFF,0xFF,0xFE, -0x7F,0xFF,0xFF,0xFE,0x03,0xC3,0x83,0x80,0x07,0xC3,0x83,0x80,0x07,0xC3,0x83,0x80, -0x0F,0xC3,0x83,0x80,0x0F,0xC3,0x83,0x80,0x1F,0xC3,0xFF,0xFE,0x1D,0xC3,0xFF,0xFE, -0x3D,0xC3,0x83,0x80,0x79,0xC3,0x83,0x80,0xF1,0xC3,0x83,0x80,0xF1,0xC3,0x83,0x80, -0x61,0xC3,0x83,0x80,0x01,0xC3,0xFF,0xFF,0x03,0xC3,0xFF,0xFF,0x1F,0xC3,0x80,0x00, -0x1F,0x83,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x1F,0xFF,0xFF,0xFC,0x1F,0xFF,0xFF,0xFC,0x1E,0x03,0xC0,0x3C,0x1E,0xC3,0xC7,0x3C, -0x1F,0xE3,0xC7,0xBC,0x1E,0xF3,0xCF,0x3C,0x1E,0xFB,0xDF,0x3C,0x1E,0x7B,0xDE,0x3C, -0x1E,0x33,0xDC,0x3C,0x1E,0x03,0xC0,0x3C,0x1F,0xFF,0xFF,0xFC,0x1F,0xFF,0xFF,0xFC, -0x1E,0x03,0xC0,0x3C,0x00,0x03,0xC0,0x00,0x00,0x03,0xC0,0x00,0x3F,0xFF,0xFF,0xFC, -0x3F,0xFF,0xFF,0xFC,0x00,0x03,0xC0,0x00,0x00,0x03,0xC0,0x00,0x00,0x03,0xC0,0x00, -0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x1C,0x38,0x70,0x70, -0x3E,0x78,0xF8,0xF8,0x3C,0x7C,0x78,0x7C,0x7C,0x3C,0x3C,0x3E,0xF8,0x3E,0x3C,0x1F, -0xF0,0x1C,0x18,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x3C,0x00, -0x00,0x78,0x3C,0x00,0x00,0x78,0x3C,0x00,0x00,0x78,0x3C,0x00,0x00,0x78,0x3C,0x00, -0x00,0x78,0x3C,0x0C,0x3C,0x78,0x3C,0x1E,0x3C,0x78,0x3C,0x3F,0x3C,0x78,0x3C,0xF8, -0x3C,0x7F,0xFD,0xF0,0x3C,0x7F,0xFF,0xE0,0x3C,0x78,0x3F,0x80,0x3C,0x78,0x3E,0x00, -0x3C,0x78,0x3C,0x00,0x3C,0x78,0x3C,0x00,0x3C,0x78,0x3C,0x00,0x3C,0x78,0x3C,0x00, -0x3C,0x78,0x3C,0x00,0x3C,0x78,0x3C,0x00,0x3C,0x78,0x3C,0x0E,0x3C,0x78,0x3C,0x0F, -0x3C,0x78,0x3C,0x0F,0x3C,0x79,0xFC,0x0F,0x3C,0x7F,0xFC,0x0F,0x3F,0xFF,0x3C,0x0F, -0x3F,0xF0,0x3E,0x1E,0xFF,0x00,0x1F,0xFE,0xF0,0x00,0x0F,0xFC,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x03,0x80,0x00,0x00,0x07,0x80,0x00,0x00,0x03,0xC0,0x00, -0x00,0x03,0xE0,0x00,0x00,0x01,0xE0,0x00,0x7F,0xFF,0xFF,0xFE,0x7F,0xFF,0xFF,0xFE, -0x78,0x00,0x00,0x1E,0x78,0x00,0x00,0x1E,0x78,0x00,0x00,0x1E,0x78,0x00,0x00,0x1E, -0x7B,0xFF,0xFF,0xDE,0x03,0xFF,0xFF,0xC0,0x00,0x00,0x0F,0xC0,0x00,0x00,0x3F,0x00, -0x00,0x00,0x7E,0x00,0x00,0x01,0xF8,0x00,0x00,0x01,0xE0,0x00,0x00,0x01,0xE0,0x00, -0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,0xE0,0x00,0x00,0x01,0xE0,0x00, -0x00,0x01,0xE0,0x00,0x00,0x01,0xE0,0x00,0x00,0x01,0xE0,0x00,0x00,0x01,0xE0,0x00, -0x00,0x03,0xE0,0x00,0x00,0x03,0xC0,0x00,0x00,0xFF,0xC0,0x00,0x00,0xFF,0x80,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xC0,0x3C,0x00, -0x03,0xC0,0x3C,0x00,0x03,0xC0,0x3C,0x00,0x07,0x80,0x3C,0x00,0x07,0x80,0x3C,0x00, -0x07,0x80,0x3C,0x00,0x0F,0xFF,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x1F,0x01,0xFE,0x00, -0x1F,0x01,0xFF,0x00,0x3F,0x01,0xFF,0x00,0x3F,0x03,0xFF,0x00,0x7F,0x03,0xFF,0x80, -0x7F,0x07,0xBF,0x80,0xFF,0x07,0xBF,0xC0,0xEF,0x0F,0x3D,0xC0,0xCF,0x0F,0x3D,0xE0, -0x0F,0x1E,0x3D,0xE0,0x0F,0x1E,0x3C,0xF0,0x0F,0x3C,0x3C,0x78,0x0F,0x7C,0x3C,0x7C, -0x0F,0xF8,0x3C,0x3E,0x0F,0xF7,0xFF,0xDF,0x0F,0xE7,0xFF,0xCF,0x0F,0xC0,0x3C,0x06, -0x0F,0x00,0x3C,0x00,0x0F,0x00,0x3C,0x00,0x0F,0x00,0x3C,0x00,0x0F,0x00,0x3C,0x00, -0x0F,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00, -0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00, -0x00,0x0F,0xE0,0x00,0x00,0x0F,0xF8,0x00,0x00,0x0F,0xFC,0x00,0x00,0x0F,0xBF,0x00, -0x00,0x0F,0x9F,0x80,0x00,0x0F,0x87,0xE0,0x00,0x0F,0x83,0xF0,0x00,0x0F,0x80,0xF8, -0x00,0x0F,0x80,0x7C,0x00,0x0F,0x80,0x38,0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00, -0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00, -0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00,0x00,0x0F,0x80,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78, -0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x78,0x7F,0xFC,0x00,0x78,0x7F,0xFC,0x00,0x78, -0x00,0x3C,0x00,0x78,0x00,0x3F,0xFF,0xFF,0x30,0x3F,0xFF,0xFF,0x78,0x3C,0x00,0x78, -0x3C,0x38,0x00,0x78,0x3E,0x78,0x00,0x78,0x1E,0x78,0xC0,0x78,0x0F,0x79,0xE0,0x78, -0x0F,0xF0,0xF0,0x78,0x07,0xF0,0xF8,0x78,0x03,0xF0,0x78,0x78,0x01,0xE0,0x3C,0x78, -0x03,0xF0,0x3E,0x78,0x03,0xF0,0x18,0x78,0x07,0xF8,0x00,0x78,0x07,0xFC,0x00,0x78, -0x0F,0x3E,0x00,0x78,0x1F,0x1E,0x00,0x78,0x3E,0x1F,0x00,0x78,0x7C,0x0E,0x00,0xF8, -0xF8,0x00,0x00,0xF0,0xF0,0x00,0x3F,0xF0,0x60,0x00,0x3F,0xE0,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: Ӧ --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"Ӧ", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x01,0xC0,0x00,0x00,0x03,0xE0,0x00,0x00,0x01,0xE0,0x00, -0x00,0x01,0xF0,0x00,0x00,0x00,0xF0,0x00,0x1F,0xFF,0xFF,0xFF,0x1F,0xFF,0xFF,0xFF, -0x1E,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x1E,0x01,0xE0,0x78,0x1E,0x01,0xE0,0x78, -0x1E,0xE1,0xE0,0x78,0x1F,0xF1,0xF0,0xF8,0x1E,0xF0,0xF0,0xF0,0x1E,0xF0,0xF0,0xF0, -0x1E,0xF8,0xF0,0xF0,0x1E,0x78,0xF1,0xF0,0x1E,0x78,0xF9,0xE0,0x1E,0x78,0x79,0xE0, -0x1E,0x7C,0x7B,0xE0,0x1E,0x3C,0x7B,0xC0,0x1E,0x3C,0x7B,0xC0,0x1E,0x3C,0x7B,0xC0, -0x3C,0x3E,0x07,0x80,0x3C,0x1C,0x07,0x80,0x3C,0x00,0x07,0x80,0x3C,0x00,0x0F,0x00, -0x78,0x00,0x0F,0x00,0x7B,0xFF,0xFF,0xFF,0xF3,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00, -0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x80,0x3C,0x00,0x07,0xC0,0x3E,0x00, -0x07,0x80,0x3C,0x00,0x07,0x80,0x7C,0x00,0x0F,0x00,0x78,0x00,0x7F,0xFE,0x7F,0xFE, -0x7F,0xFE,0xFF,0xFE,0x78,0x1E,0xF0,0x1E,0x78,0x1F,0xE0,0x1E,0x78,0x1F,0xE0,0x1E, -0x78,0x1F,0xC0,0x1E,0x78,0x1F,0xC0,0x1E,0x78,0x1F,0xF0,0x1E,0x78,0x1E,0xF8,0x1E, -0x78,0x1E,0x7C,0x1E,0x7F,0xFE,0x3C,0x1E,0x7F,0xFE,0x1E,0x1E,0x78,0x1E,0x1F,0x1E, -0x78,0x1E,0x0F,0x9E,0x78,0x1E,0x07,0x9E,0x78,0x1E,0x07,0x1E,0x78,0x1E,0x00,0x1E, -0x78,0x1E,0x00,0x1E,0x78,0x1E,0x00,0x3E,0x78,0x1E,0x00,0x3C,0x78,0x1E,0x00,0x3C, -0x7F,0xFE,0x00,0x3C,0x7F,0xFE,0x00,0x7C,0x78,0x1E,0x3F,0xF8,0x78,0x1E,0x3F,0xF0, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xC0,0x00,0x00,0x03,0xC0,0x00, -0x00,0x03,0xC0,0x00,0x00,0x03,0xC0,0x00,0x00,0x03,0xFF,0xFF,0x00,0x03,0xFF,0xFF, -0x00,0x03,0xC0,0x00,0x00,0x03,0xC0,0x00,0x00,0x03,0xC0,0x00,0x00,0x03,0xC0,0x00, -0x0F,0xFF,0xFF,0xF8,0x0F,0xFF,0xFF,0xF8,0x0F,0x00,0x00,0x78,0x0F,0x00,0x00,0x78, -0x0F,0x00,0x00,0x78,0x0F,0x00,0x00,0x78,0x0F,0x00,0x00,0x78,0x0F,0x00,0x00,0x78, -0x0F,0xFF,0xFF,0xF8,0x0F,0xFF,0xFF,0xF8,0x0F,0x00,0x00,0x78,0x00,0x00,0x00,0x00, -0x0C,0x38,0x38,0x30,0x1E,0x7C,0x78,0x78,0x3E,0x3C,0x78,0x78,0x3C,0x3C,0x3C,0x3C, -0x7C,0x3E,0x3C,0x3E,0xF8,0x1E,0x3C,0x1E,0xF0,0x1E,0x1E,0x1F,0x70,0x1E,0x1C,0x0E, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x78,0x00, -0x7F,0xF0,0x78,0x00,0x7F,0xF0,0x78,0x00,0x79,0xFF,0xFF,0xFF,0x79,0xFF,0xFF,0xFF, -0x79,0xE1,0xE0,0x00,0x79,0xE1,0xE0,0x00,0x7B,0xC1,0xEF,0x80,0x7B,0xC3,0xCF,0x80, -0x7B,0xC3,0xCF,0x80,0x7F,0x87,0xCF,0x80,0x7F,0x87,0x8F,0x80,0x7F,0x87,0x8F,0x80, -0x7B,0xCF,0x0F,0x80,0x7B,0xCF,0xFF,0xFE,0x79,0xEF,0xFF,0xFE,0x79,0xE0,0x0F,0x80, -0x78,0xE0,0x0F,0x80,0x78,0xF0,0x0F,0x80,0x78,0xF0,0x0F,0x80,0x78,0xF0,0x0F,0x80, -0x78,0xFF,0xFF,0xFF,0x79,0xFF,0xFF,0xFF,0x7F,0xE0,0x0F,0x80,0x7F,0xC0,0x0F,0x80, -0x78,0x00,0x0F,0x80,0x78,0x00,0x0F,0x80,0x78,0x00,0x0F,0x80,0x78,0x00,0x0F,0x80, -0x78,0x00,0x0F,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: Ϊ --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"Ϊ", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x80,0x00, -0x0E,0x07,0x80,0x00,0x1F,0x07,0x80,0x00,0x0F,0x87,0x80,0x00,0x07,0xC7,0x80,0x00, -0x01,0xE7,0x80,0x00,0x00,0xC7,0x80,0x00,0x00,0x07,0x80,0x00,0x7F,0xFF,0xFF,0xFC, -0x7F,0xFF,0xFF,0xFC,0x00,0x07,0x80,0x3C,0x00,0x0F,0x80,0x3C,0x00,0x0F,0x00,0x3C, -0x00,0x0F,0x00,0x3C,0x00,0x0F,0x60,0x3C,0x00,0x1F,0xF0,0x3C,0x00,0x1E,0x78,0x3C, -0x00,0x3E,0x3C,0x3C,0x00,0x3C,0x3E,0x3C,0x00,0x7C,0x1F,0x3C,0x00,0x78,0x0F,0x3C, -0x00,0xF8,0x06,0x3C,0x01,0xF0,0x00,0x3C,0x03,0xE0,0x00,0x7C,0x07,0xC0,0x00,0x7C, -0x0F,0x80,0x00,0x78,0x1F,0x00,0x00,0xF8,0x3E,0x00,0xFF,0xF0,0x7C,0x00,0xFF,0xE0, -0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x38, -0x0F,0x00,0x00,0x38,0x0F,0x00,0x00,0x38,0x0F,0x3F,0xF8,0x38,0x0F,0x3F,0xF8,0x38, -0x0F,0x00,0x78,0x38,0xFF,0xE0,0x7F,0xFF,0xFF,0xE0,0x7F,0xFF,0x0F,0x00,0x70,0x38, -0x0F,0x18,0xF0,0x38,0x1F,0x3C,0xF0,0x38,0x1F,0x1C,0xFE,0x38,0x1F,0xDE,0xFE,0x38, -0x3F,0xEF,0xEF,0x38,0x3F,0xFF,0xEF,0x38,0x3F,0xF7,0xE7,0xB8,0x7F,0x67,0xC7,0xB8, -0x7F,0x03,0xC3,0xB8,0xFF,0x07,0xE0,0x38,0xEF,0x07,0xE0,0x38,0xEF,0x0F,0xF0,0x38, -0xCF,0x1F,0xF0,0x38,0x0F,0x1E,0x78,0x38,0x0F,0x3C,0x7C,0x38,0x0F,0x78,0x3C,0x38, -0x0F,0xF8,0x38,0x38,0x0F,0x60,0x00,0x78,0x0F,0x00,0x0F,0xF8,0x0F,0x00,0x07,0xF0, -0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: ݮ --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"ݮ", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x1E,0x00,0x00,0x3C,0x1E,0x00, -0x00,0x3C,0x1E,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x3C,0x1E,0x00, -0x07,0xBC,0x1E,0x00,0x07,0x80,0x00,0x00,0x0F,0xFF,0xFF,0xFC,0x0F,0xFF,0xFF,0xFC, -0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x7F,0xFF,0xFF,0xF0, -0xF7,0xFF,0xFF,0xF0,0x37,0x83,0x80,0xF0,0x07,0x87,0xC0,0xF0,0x07,0x83,0xF0,0xF0, -0x07,0x00,0xE0,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x00,0xE0, -0x0F,0x0F,0x81,0xE0,0x0E,0x03,0xE1,0xE0,0x1E,0x01,0xC1,0xE0,0x1F,0xFF,0xFF,0xFE, -0x1F,0xFF,0xFF,0xFE,0x00,0x00,0x01,0xE0,0x00,0x00,0x03,0xC0,0x00,0x00,0xFF,0xC0, -0x00,0x00,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x3E, -0x7C,0x00,0x3F,0xFE,0x3F,0x3F,0xFF,0xF0,0x1F,0xBF,0xE0,0x00,0x07,0xBC,0x00,0x00, -0x03,0x3C,0x00,0x00,0x00,0x3C,0x00,0x3C,0x00,0x3C,0x0F,0xFE,0x70,0x3D,0xFF,0xF8, -0xF8,0x3D,0xFF,0x00,0x7C,0x3D,0xE7,0x80,0x3F,0x3D,0xE7,0x80,0x1F,0x3D,0xE7,0x8E, -0x0E,0x3D,0xE7,0x9F,0x00,0x3D,0xE7,0xFE,0x00,0x39,0xE7,0xF8,0x00,0x39,0xE3,0xF0, -0x1C,0x39,0xE3,0xC0,0x1E,0x79,0xE3,0xC0,0x1E,0x79,0xE1,0xE0,0x1E,0x79,0xE1,0xE0, -0x3C,0x79,0xE0,0xF0,0x3C,0x79,0xE0,0xF8,0x3C,0xF1,0xE0,0x7C,0x3C,0xF1,0xE3,0x7C, -0x7D,0xF1,0xEF,0x3F,0x79,0xE1,0xFE,0x1F,0x7B,0xE1,0xF8,0x0E,0x7B,0xC3,0xE0,0x00, -0x79,0x81,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: A --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{ -"A", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x7C,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xFE,0x00,0x00, -0x01,0xFF,0x00,0x00,0x01,0xFF,0x00,0x00,0x01,0xEF,0x00,0x00,0x03,0xEF,0x80,0x00, -0x03,0xCF,0x80,0x00,0x07,0xC7,0x80,0x00,0x07,0xC7,0xC0,0x00,0x07,0x87,0xC0,0x00, -0x0F,0x83,0xE0,0x00,0x0F,0x83,0xE0,0x00,0x0F,0x01,0xE0,0x00,0x1F,0xFF,0xF0,0x00, -0x1F,0xFF,0xF0,0x00,0x3F,0xFF,0xF8,0x00,0x3E,0x00,0xF8,0x00,0x3C,0x00,0xF8,0x00, -0x7C,0x00,0x7C,0x00,0x7C,0x00,0x7C,0x00,0x78,0x00,0x3C,0x00,0xF8,0x00,0x3E,0x00, -0xF8,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: a --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"a", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xF8,0x00,0x00, -0x1F,0xFE,0x00,0x00,0x3F,0xFE,0x00,0x00,0x3E,0x3F,0x00,0x00,0x38,0x1F,0x00,0x00, -0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x03,0xFF,0x00,0x00,0x1F,0xFF,0x00,0x00, -0x3F,0x8F,0x00,0x00,0x7C,0x0F,0x00,0x00,0x7C,0x0F,0x00,0x00,0x78,0x1F,0x00,0x00, -0x7C,0x1F,0x00,0x00,0x7E,0x7F,0x00,0x00,0x7F,0xFF,0x00,0x00,0x3F,0xFF,0x00,0x00, -0x0F,0xCF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: b --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"b", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x00,0x00,0x00, -0x3C,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x3C,0x00,0x00,0x00, -0x3C,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x3C,0xFE,0x00,0x00, -0x3D,0xFF,0x80,0x00,0x3F,0xFF,0xC0,0x00,0x3F,0x8F,0xC0,0x00,0x3F,0x07,0xE0,0x00, -0x3E,0x03,0xE0,0x00,0x3E,0x03,0xE0,0x00,0x3C,0x01,0xE0,0x00,0x3C,0x01,0xE0,0x00, -0x3C,0x01,0xE0,0x00,0x3C,0x03,0xE0,0x00,0x3E,0x03,0xE0,0x00,0x3E,0x03,0xE0,0x00, -0x3F,0x07,0xC0,0x00,0x3F,0x8F,0xC0,0x00,0x3F,0xFF,0x80,0x00,0x3F,0xFF,0x00,0x00, -0x3C,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: c --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"c", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFC,0x00,0x00, -0x07,0xFE,0x00,0x00,0x1F,0xFE,0x00,0x00,0x3F,0x86,0x00,0x00,0x3E,0x00,0x00,0x00, -0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x78,0x00,0x00,0x00, -0x78,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7C,0x00,0x00,0x00, -0x3E,0x00,0x00,0x00,0x3F,0x86,0x00,0x00,0x1F,0xFE,0x00,0x00,0x0F,0xFE,0x00,0x00, -0x03,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x07,0x01,0xE0,0x07,0x87,0x01,0xE0, -0x07,0x07,0x01,0xC0,0x0F,0xF7,0x79,0xC0,0x1E,0xF7,0x7B,0xC0,0x1E,0xF7,0x7B,0x80, -0x3C,0xF7,0x7B,0xFF,0x78,0xF7,0x7B,0xFF,0xF8,0xF7,0x7F,0x9E,0xF7,0xFF,0xFF,0x9E, -0x67,0xFF,0xFF,0x9E,0x07,0x00,0x7F,0x9C,0x0F,0x00,0x0F,0x9C,0x1E,0x00,0x1F,0x9C, -0x1E,0x7F,0xFF,0xBC,0x3E,0x7F,0xF3,0xFC,0x3E,0x00,0x03,0xFC,0x7E,0x00,0x01,0xF8, -0xFE,0x00,0x01,0xF8,0xFE,0x7F,0xE1,0xF8,0xDE,0x7F,0xE1,0xF8,0x1E,0x78,0xE0,0xF0, -0x1E,0x78,0xEE,0xF0,0x1E,0x78,0xFF,0xF0,0x1E,0x78,0xFD,0xF8,0x1E,0x79,0xFB,0xFC, -0x1E,0xF1,0xF7,0xBC,0x1E,0xF0,0xEF,0x9E,0x1F,0xE0,0x0F,0x0F,0x1E,0xC0,0x1E,0x0F, -0x1E,0x00,0x0C,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: ѩ --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"ѩ", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x1F,0xFF,0xFF,0xF8,0x1F,0xFF,0xFF,0xF8,0x00,0x03,0xC0,0x00,0x00,0x03,0xC0,0x00, -0x7F,0xFF,0xFF,0xFE,0x7F,0xFF,0xFF,0xFE,0x78,0x03,0xC0,0x1E,0x78,0x03,0xC0,0x1E, -0x7F,0xFF,0xFF,0xFE,0x7F,0xFF,0xFF,0xFE,0x00,0x03,0xC0,0x00,0x00,0x03,0xC0,0x00, -0x07,0xFF,0xFF,0xE0,0x07,0xFF,0xFF,0xE0,0x00,0x03,0xC0,0x00,0x00,0x00,0x00,0x00, -0x1F,0xFF,0xFF,0xF8,0x1F,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x78, -0x1F,0xFF,0xFF,0xF8,0x1F,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x78, -0x00,0x00,0x00,0x78,0x3F,0xFF,0xFF,0xF8,0x3F,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x78, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x80,0x00,0x00,0x07,0x80,0x00, -0x00,0x07,0x80,0x00,0x00,0x07,0x80,0x00,0x7F,0xFF,0xFF,0xF8,0x7F,0xFF,0xFF,0xF8, -0x78,0x07,0x80,0xF8,0x78,0x07,0x80,0xF8,0x78,0x07,0x80,0xF8,0x78,0x07,0x80,0xF8, -0x78,0x07,0x80,0xF8,0x78,0x07,0x80,0xF8,0x7F,0xFF,0xFF,0xF8,0x7F,0xFF,0xFF,0xF8, -0x78,0x07,0x80,0xF8,0x78,0x07,0x80,0xF8,0x78,0x07,0x80,0xF8,0x78,0x07,0x80,0xF8, -0x78,0x07,0x80,0xF8,0x78,0x07,0x80,0xF8,0x7F,0xFF,0xFF,0xF8,0x7F,0xFF,0xFF,0xF8, -0x78,0x07,0x80,0x0E,0x78,0x07,0x80,0x0F,0x00,0x07,0x80,0x0F,0x00,0x07,0x80,0x0F, -0x00,0x07,0x80,0x1F,0x00,0x07,0x80,0x1E,0x00,0x03,0xFF,0xFE,0x00,0x01,0xFF,0xFC, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - -/*-- ����: �� --*/ -/*-- ���ź�24; �������¶�Ӧ�ĵ���Ϊ����x��=32x41 --*/ -{"��", -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x1F,0xFF,0xFF,0xF8,0x1F,0xFF,0xFF,0xF8,0x00,0x00,0x01,0xF8,0x00,0x00,0x07,0xE0, -0x00,0x00,0x0F,0xC0,0x00,0x00,0x1F,0x80,0x00,0x00,0x3E,0x00,0x00,0x00,0xFC,0x00, -0x00,0x01,0xF8,0x00,0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00, -0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00, -0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00, -0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00,0x00,0x03,0xE0,0x00, -0x00,0x03,0xE0,0x00,0x00,0x03,0xC0,0x00,0x01,0xFF,0xC0,0x00,0x00,0xFF,0x80,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00}, - - -}; - -cFONT Font24CN = { - Font24CN_Table, - sizeof(Font24CN_Table)/sizeof(CH_CN), /*size of table*/ - 24, /* ASCII Width */ - 32, /* Width */ - 41, /* Height */ -}; - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/gfx/Fonts/fonts.h b/lib/gfx/Fonts/fonts.h index 33509b472f63a805df060e2b9e6f5c108757a1e0..04117b2acbbb32724b7066155a6b0dd8a558f221 100644 --- a/lib/gfx/Fonts/fonts.h +++ b/lib/gfx/Fonts/fonts.h @@ -61,32 +61,12 @@ typedef struct _tFont } sFONT; -//GB2312 -typedef struct // ������ģ���ݽṹ -{ - unsigned char index[2]; // ������������ - const char matrix[MAX_HEIGHT_FONT*MAX_WIDTH_FONT/8]; // ���������� -}CH_CN; - - -typedef struct -{ - const CH_CN *table; - uint16_t size; - uint16_t ASCII_Width; - uint16_t Width; - uint16_t Height; - -}cFONT; - extern sFONT Font24; extern sFONT Font20; extern sFONT Font16; extern sFONT Font12; extern sFONT Font8; -extern cFONT Font12CN; -extern cFONT Font24CN; #ifdef __cplusplus } #endif diff --git a/lib/gfx/gfx.c b/lib/gfx/gfx.c index dd92ecdc0cd736ba26ab15640b9afd7066e3cbed..5af5bbb360248650fc40cb1ec445d9091c90575d 100644 --- a/lib/gfx/gfx.c +++ b/lib/gfx/gfx.c @@ -33,7 +33,7 @@ struct gfx_region gfx_screen(struct framebuffer *fb) return r; } -static inline int letter_bit(sFONT *font, char c, int x, int y) +static inline int letter_bit(const sFONT *font, char c, int x, int y) { if (x < 0 || y < 0) return 0; @@ -53,7 +53,7 @@ static inline int letter_bit(sFONT *font, char c, int x, int y) } void gfx_putchar( - sFONT *font, + const sFONT *font, struct gfx_region *r, int x, int y, @@ -78,7 +78,7 @@ void gfx_putchar( } void gfx_puts( - sFONT *font, + const sFONT *font, struct gfx_region *r, int x, int y, @@ -198,7 +198,7 @@ static void plot_line_low( int d = 2 * dy - dx; int y = y0; - for (int x = x0; x < x1; x++) { + for (int x = x0; x <= x1; x++) { if (t > 1) { gfx_circle_fill(reg, x, y, t, c); } else { @@ -231,7 +231,7 @@ static void plot_line_high( int d = 2 * dx - dy; int x = x0; - for (int y = y0; y < y1; y++) { + for (int y = y0; y <= y1; y++) { if (t > 1) { gfx_circle_fill(reg, x, y, t, c); } else { diff --git a/lib/gfx/gfx.h b/lib/gfx/gfx.h index 428b196c6a41465230538da60a6ff0ce3fccafb5..0f64a429b59170182077b7d01e2d1cd027743974 100644 --- a/lib/gfx/gfx.h +++ b/lib/gfx/gfx.h @@ -14,9 +14,9 @@ struct gfx_region { void gfx_setpixel(struct gfx_region *r, int x, int y, Color c); struct gfx_region gfx_screen(struct framebuffer *fb); -void gfx_putchar(sFONT *font, struct gfx_region *reg, int x, int y, char ch, +void gfx_putchar(const sFONT *font, struct gfx_region *reg, int x, int y, char ch, Color fg, Color bg); -void gfx_puts(sFONT *font, struct gfx_region *reg, int x, int y, +void gfx_puts(const sFONT *font, struct gfx_region *reg, int x, int y, const char *str, Color fg, Color bg); Color gfx_color_rgb_f(struct gfx_region *reg, float r, float g, float b); Color gfx_color_rgb(struct gfx_region *reg, uint8_t r, uint8_t g, uint8_t b); diff --git a/lib/gfx/meson.build b/lib/gfx/meson.build index 37b90e436950b8e2b164dcb0bede57373281d4ce..4df9f3c1c49f901652834e1d2a670932da52a3de 100644 --- a/lib/gfx/meson.build +++ b/lib/gfx/meson.build @@ -10,11 +10,9 @@ sources = files( './LCD/LCD_Driver.c', './Fonts/font8.c', './Fonts/font12.c', - './Fonts/font12CN.c', './Fonts/font16.c', './Fonts/font20.c', './Fonts/font24.c', - './Fonts/font24CN.c', 'framebuffer.c', 'gfx.c', 'textbuffer.c' diff --git a/preload/apps/analog_clock/__init__.py b/preload/apps/analog_clock/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c679a82ec20191acec37fa99519f1cb81e52fc71 --- /dev/null +++ b/preload/apps/analog_clock/__init__.py @@ -0,0 +1,331 @@ +# Adapted from https://github.com/muccc/flipdots/blob/master/scripts/clock.py +import display +from utime import sleep +import utime +import math +import leds +import buttons +import ujson +import os + +CONFIG_NAME = "clock.json" + + +class Time: + def __init__(self, start=0): + self.time = start + self.wait_time = 0.95 + + def tick(self): + sleep(self.wait_time) + self.time += 1 + + @property + def second(self): + return self.time % 60 + + @property + def minute(self): + return (self.time / 60) % 60 + + @property + def hour(self): + return (self.time / 3600) % 24 + + +class Clock: + def __init__( + self, + sizex=80, + sizey=80, + radius=38, + offsetx=30, + hour_hand=True, + minute_hand=True, + second_hand=True, + console_out=False, + run_once=False, + update_interval=0, + ): + self.sizex = sizex + self.sizey = sizey + self.radius = radius + self.center = (int(self.sizex / 2), int(self.sizey / 2)) + self.hour_hand = hour_hand + self.minute_hand = minute_hand + self.second_hand = second_hand + self.console_out = console_out + self.update_interval = ( + update_interval if update_interval != 0 else (1 if self.second_hand else 30) + ) + self.run_once = run_once + self.offsetx = offsetx + self.time = Time() + self.theme = 0 + self.default_themes = [ + { + "background": [0, 0, 0], + "center": [255, 255, 255], + "m1": [255, 255, 255], + "m5": [255, 255, 255], + "hour_hand": [255, 255, 255], + "minute_hand": [255, 255, 255], + "second_hand": [255, 255, 255], + }, + { + "background": [130, 30, 70], + "center": [255, 255, 255], + "m1": [255, 255, 255], + "m5": [255, 255, 255], + "hour_hand": [255, 255, 255], + "minute_hand": [255, 255, 255], + "second_hand": [255, 255, 255], + }, + { + "background": [0, 80, 0], + "center": [255, 255, 255], + "m1": [255, 255, 255], + "m5": [255, 255, 255], + "hour_hand": [255, 255, 255], + "minute_hand": [255, 255, 255], + "second_hand": [255, 255, 255], + }, + { + "background": [0, 80, 80], + "center": [255, 255, 255], + "m1": [255, 255, 255], + "m5": [255, 255, 255], + "hour_hand": [255, 255, 255], + "minute_hand": [255, 255, 255], + "second_hand": [255, 255, 255], + }, + { + "background": [255, 255, 255], + "center": [0, 0, 0], + "m1": [0, 0, 0], + "m5": [0, 0, 0], + "hour_hand": [0, 0, 0], + "minute_hand": [0, 0, 0], + "second_hand": [0, 0, 0], + }, + ] + self.themes = self.default_themes + + # check for config file + if CONFIG_NAME in os.listdir("."): + self.readConfig() + else: + self.writeConfig() + + # load colors + self.setTheme(self.theme) + + def readConfig(self): + with open(CONFIG_NAME, "r") as f: + try: + c = ujson.loads(f.read()) + if ( + "themes" in c + and len(c["themes"]) > 0 + and isinstance(c["themes"], list) + ): + self.themes = c["themes"] + if "theme" and isinstance(c["theme"], int): + self.theme = c["theme"] + except ValueError: + print("parsing %s failed" % CONFIG_NAME) + + def writeConfig(self): + with open(CONFIG_NAME, "w") as f: + f.write(ujson.dumps({"theme": self.theme, "themes": self.themes})) + + def setTheme(self, theme): + self.theme = theme % len(self.themes) + self.background_col = ( + self.themes[self.theme]["background"] + if "background" in self.themes[self.theme] + else self.default_themes[0]["background"] + ) + self.center_col = ( + self.themes[self.theme]["center"] + if "center" in self.themes[self.theme] + else self.default_themes[0]["center"] + ) + self.m1_col = ( + self.themes[self.theme]["m1"] + if "m1" in self.themes[self.theme] + else self.default_themes[0]["m1"] + ) + self.m5_col = ( + self.themes[self.theme]["m5"] + if "m5" in self.themes[self.theme] + else self.default_themes[0]["m5"] + ) + self.hour_hand_col = ( + self.themes[self.theme]["hour_hand"] + if "hour_hand" in self.themes[self.theme] + else self.default_themes[0]["hour_hand"] + ) + self.minute_hand_col = ( + self.themes[self.theme]["minute_hand"] + if "minute_hand" in self.themes[self.theme] + else self.default_themes[0]["minute_hand"] + ) + self.second_hand_col = ( + self.themes[self.theme]["second_hand"] + if "second_hand" in self.themes[self.theme] + else self.default_themes[0]["second_hand"] + ) + + def loop(self): + colored = False + try: + with display.open() as disp: + button_pressed = False + while True: + self.updateClock(disp) + if self.run_once: + break + + # check for button presses + v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT) + if v == 0: + button_pressed = False + + if not button_pressed and v & buttons.BOTTOM_LEFT != 0: + button_pressed = True + self.setTheme(self.theme - 1) + self.writeConfig() + elif not button_pressed and v & buttons.BOTTOM_RIGHT != 0: + button_pressed = True + self.setTheme(self.theme + 1) + self.writeConfig() + + except KeyboardInterrupt: + for i in range(11): + leds.set(i, (0, 0, 0)) + return + + def drawImage(self, image): + with display.open() as d: + d.clear() + for x in range(len(image)): + for y in range(len(image[x])): + d.pixel( + x + self.offsetx, + y, + col=(255, 255, 255) if image[x][y] else (0, 0, 0), + ) + d.update() + + def updateClock(self, disp): + disp.clear(self.background_col) + localtime = utime.localtime() + + disp.pixel(self.center[0] + self.offsetx, self.center[1], col=self.center_col) + hour_coords = self.circlePoint( + math.radians( + (((localtime[3] % 12) / 12.0) if localtime[3] else 0) * 360 + + 270 + + (localtime[4] / 2) + ) + ) + minute_coords = self.circlePoint(math.radians(localtime[4] * 6 + 270)) + second_coords = self.circlePoint(math.radians(localtime[5] * 6 + 270)) + + for i in range(60): + degree = i * 6 + 90 + radian = -math.radians(degree) + coords = self.circlePoint(radian) + + if not i % 5: + self.addLine(disp, coords, self.center, 3, 1, col=self.m5_col) + else: + self.addLine(disp, coords, self.center, 1, col=self.m1_col) + + if self.hour_hand: + self.addLine( + disp, + self.center, + hour_coords, + int(self.radius / 3), + 1, + col=self.hour_hand_col, + ) + if self.minute_hand: + self.addLine( + disp, + self.center, + minute_coords, + int(self.radius / 2), + col=self.minute_hand_col, + ) + if self.second_hand: + self.addLine( + disp, + self.center, + second_coords, + self.radius - int(self.radius / 8.0), + col=self.second_hand_col, + ) + + if self.console_out: + for y in range(self.radius * 2): + line = "" + for x in range(self.radius * 2): + line = line + ( + "." + if image[(self.center[1] - self.radius) + y][ + (self.center[0] - self.radius) + x + ] + else " " + ) + print(line) + + disp.update() + + def circlePoint(self, t): + return ( + int(round(self.radius * math.cos(t))) + self.center[0], + int(round(self.radius * math.sin(t))) + self.center[1], + ) + + def addLine(self, disp, source, aim, length, thickness=1, col=(255, 255, 255)): + vector = self.subVector(aim, source) + vector = self.normVector(vector) + destination = self.addVector(source, self.multiplyVector(vector, length)) + + disp.line( + round(source[0]) + self.offsetx, + round(source[1]), + round(destination[0]) + self.offsetx, + round(destination[1]), + col=col, + size=thickness, + ) + + def normVector(self, v): + length = math.sqrt(sum([i ** 2 for i in v])) + new_v = [] + for i in range(len(v)): + new_v.append(v[i] / length) + return tuple(new_v) + + def subVector(self, v1, v2): + res = [] + for i in range(len(v1)): + res.append(v1[i] - v2[i]) + return tuple(res) + + def addVector(self, v1, v2): + res = [] + for i in range(len(v1)): + res.append(v1[i] + v2[i]) + return tuple(res) + + def multiplyVector(self, v, multiplier): + return tuple([i * multiplier for i in v]) + + +clock = Clock() +clock.loop() diff --git a/preload/apps/analog_clock/metadata.json b/preload/apps/analog_clock/metadata.json new file mode 100644 index 0000000000000000000000000000000000000000..3b3303d5ce09cf9c89f63fe2b2174b1958b0a118 --- /dev/null +++ b/preload/apps/analog_clock/metadata.json @@ -0,0 +1 @@ +{"name":"Analog Clock","description":"Analog Clock","category":"graphics","author":"markus","revision":-1} diff --git a/preload/apps/ecg/__init__.py b/preload/apps/ecg/__init__.py index ccc38843d0201298e5933318c2f5124c220e732d..d8ff79e736d3a267a19d7c5e99697231ed9d6543 100644 --- a/preload/apps/ecg/__init__.py +++ b/preload/apps/ecg/__init__.py @@ -1,242 +1,342 @@ -import os -import display -import utime -import buttons -import max30001 -import math -import struct - -WIDTH = 160 -HEIGHT = 80 -OFFSET = 50 -ECG_RATE = 128 -HISTORY_MAX = ECG_RATE * 2 -DRAW_AFTER_SAMPLES = 5 -SCALE_FACTOR = 30 -MODE_USB = "USB" -MODE_FINGER = "Finger" -FILEBUFFERBLOCK = 4096 -COLOR_BACKGROUND = [0, 0, 0] -COLOR_LINE = [255, 255, 255] -COLOR_TEXT = [255, 255, 255] -COLOR_MODE_FINGER = [0, 255, 0] -COLOR_MODE_USB = [0, 0, 255] -COLOR_WRITE_FG = [255, 255, 255] -COLOR_WRITE_BG = [255, 0, 0] - -current_mode = MODE_FINGER -history = [] -filebuffer = bytearray() -write = 0 -bias = True -update_screen = 0 -pause_screen = 0 -sensor = 0 -disp = display.open() - - -def callback_ecg(datasets): - global update_screen, history, filebuffer, write - if len(datasets) > 0: - for value in datasets: - history.append(value) - update_screen += len(datasets) - if len(history) > HISTORY_MAX: - r = len(history) - HISTORY_MAX - for i in range(r): - history.pop(0) - - # buffer for writes - if write > 0: - if len(datasets) > 0: - for value in datasets: - filebuffer.extend(struct.pack("h", value)) - if len(filebuffer) >= FILEBUFFERBLOCK: - write_filebuffer() - - # don't update on every callback - if update_screen >= DRAW_AFTER_SAMPLES: - # print("history: %i, filebuffer: %i" % (len(history), len(filebuffer))) - draw_histogram() - update_screen = 0 - - -def write_filebuffer(): - global write, filebuffer - # write to file - chars = "" - lt = utime.localtime(write) - filename = "/ecg-%04d-%02d-%02d_%02d%02d%02d.log" % ( - lt[0], - lt[1], - lt[2], - lt[3], - lt[4], - lt[5], - ) - - # write stuff to disk - # print("Write %i bytes to %s" % (len(filebuffer), filename)) - try: - f = open(filename, "ab") - f.write(filebuffer) - f.close() - except OSError as e: - print("Please check the file or filesystem", e) - write = 0 - pause_screen = -1 - disp.clear(COLOR_BACKGROUND) - disp.print("IO Error", posy=0, fg=COLOR_TEXT) - disp.print("Please check", posy=20, fg=COLOR_TEXT) - disp.print("your", posy=40, fg=COLOR_TEXT) - disp.print("filesystem", posy=60, fg=COLOR_TEXT) - disp.update() - close_sensor() - except: - print("Unexpected error, stop writeing logfile") - write = 0 - - filebuffer = bytearray() - return - - -def open_sensor(): - global sensor - sensor = max30001.MAX30001( - usb=(False if current_mode == MODE_FINGER else True), - bias=bias, - sample_rate=ECG_RATE, - callback=callback_ecg, - ) - - -def close_sensor(): - global sensor - sensor.close() - - -def toggle_mode(): - global current_mode - close_sensor() - current_mode = MODE_USB if current_mode == MODE_FINGER else MODE_FINGER - open_sensor() - - -def toggle_bias(): - global bias - close_sensor() - bias = False if bias == True else True - open_sensor() - return - - -def toggle_write(): - global write, disp, pause_screen - pause_screen = utime.time_ms() + 1000 - disp.clear(COLOR_BACKGROUND) - if write > 0: - write_filebuffer() - write = 0 - disp.print("Stop", posx=50, posy=20, fg=COLOR_TEXT) - disp.print("logging", posx=30, posy=40, fg=COLOR_TEXT) - else: - filebuffer = bytearray() - write = utime.time() - disp.print("Start", posx=45, posy=20, fg=COLOR_TEXT) - disp.print("logging", posx=30, posy=40, fg=COLOR_TEXT) - - disp.update() - return - - -def draw_histogram(): - global disp, history, current_mode, bias, write, pause_screen - - # skip rendering due to message beeing shown - if pause_screen == -1: - return - elif pause_screen > 0: - t = utime.time_ms() - if t > pause_screen: - pause_screen = 0 - else: - return - - disp.clear(COLOR_BACKGROUND) - - # get max value and calc scale - value_max = 0 - for value in history: - if abs(value) > value_max: - value_max = abs(value) - scale = SCALE_FACTOR / (value_max if value_max > 0 else 1) - - # draw histogram - old = False - x = 0 - samples = len(history) - for i, value in enumerate(history): - if old == False: - old = (x, int(value * scale) + OFFSET) - x += 1 - continue - elif i > samples - WIDTH: - disp.line(old[0], old[1], x, int(value * scale) + OFFSET, col=COLOR_LINE) - old = (x, int(value * scale) + OFFSET) - x += 1 - - # draw text: mode/bias/write - disp.print( - current_mode + ("+Bias" if bias else ""), - posx=0, - posy=0, - fg=(COLOR_MODE_FINGER if current_mode == MODE_FINGER else COLOR_MODE_USB), - ) - - # announce writing ecg log - if write > 0: - t = utime.time() - if write > 0 and t % 2 == 0: - disp.print("LOG", posx=0, posy=60, fg=COLOR_WRITE_FG, bg=COLOR_WRITE_BG) - - disp.update() - - -def main(): - # show button layout - disp.clear(COLOR_BACKGROUND) - disp.print(" BUTTONS ", posx=0, posy=0, fg=COLOR_TEXT) - disp.print("Finger/USB>", posx=0, posy=20, fg=COLOR_MODE_FINGER) - disp.print(" Bias >", posx=0, posy=40, fg=COLOR_MODE_USB) - disp.print("< Write Log", posx=0, posy=60, fg=COLOR_WRITE_BG) - disp.update() - utime.sleep(3) - - # start ecg - open_sensor() - while True: - button_pressed = False - while True: - v = buttons.read( - buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT - ) - if v == 0: - button_pressed = False - - if not button_pressed and v & buttons.BOTTOM_LEFT != 0: - button_pressed = True - toggle_write() - - elif not button_pressed and v & buttons.BOTTOM_RIGHT != 0: - button_pressed = True - toggle_bias() - - elif not button_pressed and v & buttons.TOP_RIGHT != 0: - button_pressed = True - toggle_mode() - - pass - - -if __name__ == "__main__": - main() +import os +import display +import leds +import utime +import buttons +import max30001 +import math +import struct + +WIDTH = 160 +HEIGHT = 80 +OFFSET_Y = 49 +ECG_RATE = 128 +HISTORY_MAX = ECG_RATE * 4 +DRAW_AFTER_SAMPLES = 5 +SCALE_FACTOR = 30 +MODE_USB = "USB" +MODE_FINGER = "Finger" +FILEBUFFERBLOCK = 4096 +COLOR_BACKGROUND = [0, 0, 0] +COLOR_LINE = [255, 255, 255] +COLOR_TEXT = [255, 255, 255] +COLOR_MODE_FINGER = [0, 255, 0] +COLOR_MODE_USB = [0, 0, 255] +COLOR_WRITE_FG = [255, 255, 255] +COLOR_WRITE_BG = [255, 0, 0] + +current_mode = MODE_FINGER +history = [] +filebuffer = bytearray() +write = 0 +bias = True +update_screen = 0 +pause_screen = 0 +pause_histogram = False +histogram_offset = 0 +sensor = 0 +disp = display.open() + +leds.dim_top(1) +COLORS = [((23 + (15 * i)) % 360, 1.0, 1.0) for i in range(11)] +colors = COLORS + + +def callback_ecg(datasets): + global update_screen, history, filebuffer, write + update_screen += len(datasets) + + # update histogram datalist + if pause_histogram == False: + if len(datasets) > 0: + for value in datasets: + history.append(value) + if len(history) > HISTORY_MAX: + r = len(history) - HISTORY_MAX + for i in range(r): + history.pop(0) + + # buffer for writes + if write > 0: + if len(datasets) > 0: + for value in datasets: + filebuffer.extend(struct.pack("h", value)) + if len(filebuffer) >= FILEBUFFERBLOCK: + write_filebuffer() + + # don't update on every callback + if update_screen >= DRAW_AFTER_SAMPLES: + draw_histogram() + + +def write_filebuffer(): + global write, filebuffer + # write to file + chars = "" + lt = utime.localtime(write) + filename = "/ecg-%04d-%02d-%02d_%02d%02d%02d.log" % ( + lt[0], + lt[1], + lt[2], + lt[3], + lt[4], + lt[5], + ) + + # write stuff to disk + try: + f = open(filename, "ab") + f.write(filebuffer) + f.close() + except OSError as e: + print("Please check the file or filesystem", e) + write = 0 + pause_screen = -1 + disp.clear(COLOR_BACKGROUND) + disp.print("IO Error", posy=0, fg=COLOR_TEXT) + disp.print("Please check", posy=20, fg=COLOR_TEXT) + disp.print("your", posy=40, fg=COLOR_TEXT) + disp.print("filesystem", posy=60, fg=COLOR_TEXT) + disp.update() + close_sensor() + except: + print("Unexpected error, stop writeing logfile") + write = 0 + + filebuffer = bytearray() + return + + +def open_sensor(): + global sensor + sensor = max30001.MAX30001( + usb=(False if current_mode == MODE_FINGER else True), + bias=bias, + sample_rate=ECG_RATE, + callback=callback_ecg, + ) + + +def close_sensor(): + global sensor + sensor.close() + + +def toggle_mode(): + global current_mode + close_sensor() + current_mode = MODE_USB if current_mode == MODE_FINGER else MODE_FINGER + open_sensor() + + +def toggle_bias(): + global bias + close_sensor() + bias = False if bias == True else True + open_sensor() + return + + +def toggle_write(): + global write, disp, pause_screen + pause_screen = utime.time_ms() + 1000 + disp.clear(COLOR_BACKGROUND) + if write > 0: + write_filebuffer() + write = 0 + disp.print("Stop", posx=50, posy=20, fg=COLOR_TEXT) + disp.print("logging", posx=30, posy=40, fg=COLOR_TEXT) + else: + filebuffer = bytearray() + write = utime.time() + disp.print("Start", posx=45, posy=20, fg=COLOR_TEXT) + disp.print("logging", posx=30, posy=40, fg=COLOR_TEXT) + + disp.update() + return + + +def toggle_pause(): + global pause_histogram, histogram_offset, history + if pause_histogram == True: + pause_histogram = False + history = [] + else: + pause_histogram = True + histogram_offset = 0 + + +def draw_leds(val): + global colors + # val should be in [0, 11] + for i in range(11): + leds.prep_hsv(10 - i, COLORS[10 - i] if i < val else (0, 0, 0)) + leds.update() + + +def draw_histogram(): + global disp, history, current_mode, bias, write, pause_screen, update_screen + + # skip rendering due to message beeing shown + if pause_screen == -1: + return + elif pause_screen > 0: + t = utime.time_ms() + if t > pause_screen: + pause_screen = 0 + else: + return + + disp.clear(COLOR_BACKGROUND) + + # offset in pause_histogram mode + samples = len(history) + s_start = samples - (histogram_offset + (ECG_RATE * 2)) + s_end = samples - (histogram_offset + 1) + s_draw = s_end - (WIDTH - 1) + + # get max value and calc scale + value_max = 0 + for i, value in enumerate(history): + if i >= s_start and i <= s_end and abs(value) > value_max: + value_max = abs(value) + scale = SCALE_FACTOR / (value_max if value_max > 0 else 1) + + # draw histogram + old = False + x = 0 + for i, value in enumerate(history): + if old == False: + old = value + x += 1 + continue + elif i > s_start and i > s_draw and i < s_end: + + oldy = int(old * scale) + if oldy < -SCALE_FACTOR: + oldy = -SCALE_FACTOR + elif oldy > SCALE_FACTOR: + oldy = SCALE_FACTOR + + disp.line( + x - 1, oldy + OFFSET_Y, x, int(value * scale) + OFFSET_Y, col=COLOR_LINE + ) + old = value + x += 1 + + draw_leds((60 - int((max(history[-3:]) * scale + OFFSET_Y) - 20)) * 11 / 60) + # draw text: mode/bias/write + if pause_histogram == True: + disp.print( + "Pause" + + ( + " -%0.1fs" % (histogram_offset / ECG_RATE) + if histogram_offset > 0 + else "" + ), + posx=0, + posy=0, + fg=COLOR_TEXT, + ) + else: + disp.print( + current_mode + ("+Bias" if bias else ""), + posx=0, + posy=0, + fg=(COLOR_MODE_FINGER if current_mode == MODE_FINGER else COLOR_MODE_USB), + ) + + # announce writing ecg log + if write > 0: + t = utime.time() + if write > 0 and t % 2 == 0: + disp.print("LOG", posx=0, posy=60, fg=COLOR_WRITE_FG, bg=COLOR_WRITE_BG) + + disp.update() + update_screen = 0 + + +def main(): + global pause_histogram, histogram_offset + + # show button layout + disp.clear(COLOR_BACKGROUND) + disp.print(" BUTTONS ", posx=0, posy=0, fg=COLOR_TEXT) + disp.print("Finger/USB>", posx=0, posy=20, fg=COLOR_MODE_FINGER) + disp.print(" Bias >", posx=0, posy=40, fg=COLOR_MODE_USB) + disp.print("< Write Log", posx=0, posy=60, fg=COLOR_WRITE_BG) + disp.update() + utime.sleep(3) + + # start ecg + open_sensor() + while True: + button_pressed = {"BOTTOM_LEFT": 0, "BOTTOM_RIGHT": 0, "TOP_RIGHT": 0} + while True: + v = buttons.read( + buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT + ) + + # BUTTOM LEFT + + if button_pressed["BOTTOM_LEFT"] == 0 and v & buttons.BOTTOM_LEFT != 0: + button_pressed["BOTTOM_LEFT"] = utime.time_ms() + if pause_histogram == False: + toggle_write() + else: + l = len(history) + histogram_offset += ECG_RATE / 2 + if l - histogram_offset < WIDTH: + histogram_offset = l - WIDTH + + if button_pressed["BOTTOM_LEFT"] > 0 and v & buttons.BOTTOM_LEFT == 0: + duration = utime.time_ms() - button_pressed["BOTTOM_LEFT"] + button_pressed["BOTTOM_LEFT"] = 0 + + # BUTTOM RIGHT + + if button_pressed["BOTTOM_RIGHT"] == 0 and v & buttons.BOTTOM_RIGHT != 0: + button_pressed["BOTTOM_RIGHT"] = utime.time_ms() + if pause_histogram == False: + toggle_bias() + else: + histogram_offset -= ECG_RATE / 2 + histogram_offset -= histogram_offset % (ECG_RATE / 2) + if histogram_offset < 0: + histogram_offset = 0 + + if button_pressed["BOTTOM_RIGHT"] > 0 and v & buttons.BOTTOM_RIGHT == 0: + duration = utime.time_ms() - button_pressed["BOTTOM_RIGHT"] + button_pressed["BOTTOM_RIGHT"] = 0 + + # TOP RIGHT + + # down, and still pressed + if button_pressed["TOP_RIGHT"] > 0 and v & buttons.TOP_RIGHT != 0: + duration = utime.time_ms() - button_pressed["TOP_RIGHT"] + if duration > 1000: + button_pressed["TOP_RIGHT"] = -1 + toggle_pause() + + # register down event + elif button_pressed["TOP_RIGHT"] == 0 and v & buttons.TOP_RIGHT != 0: + button_pressed["TOP_RIGHT"] = utime.time_ms() + + # register up event but event already called + if button_pressed["TOP_RIGHT"] == -1 and v & buttons.TOP_RIGHT == 0: + button_pressed["TOP_RIGHT"] = 0 + + # register normal up event + elif button_pressed["TOP_RIGHT"] > 0 and v & buttons.TOP_RIGHT == 0: + duration = utime.time_ms() - button_pressed["TOP_RIGHT"] + button_pressed["TOP_RIGHT"] = 0 + if pause_histogram == True: + toggle_pause() + else: + toggle_mode() + + pass + + +if __name__ == "__main__": + main() diff --git a/preload/main.py b/preload/main.py index c679a82ec20191acec37fa99519f1cb81e52fc71..7c8417e9ae1375779ef5a7c2fd4fdcca35cd36b7 100644 --- a/preload/main.py +++ b/preload/main.py @@ -1,331 +1,18 @@ -# Adapted from https://github.com/muccc/flipdots/blob/master/scripts/clock.py -import display -from utime import sleep -import utime -import math -import leds -import buttons -import ujson import os -CONFIG_NAME = "clock.json" +def main(): + # Try loading analog clock + default_app = "apps/analog_clock/__init__.py" + try: + with open(default_app, "r"): + pass -class Time: - def __init__(self, start=0): - self.time = start - self.wait_time = 0.95 + print("main.py: Loading " + default_app) + os.exec(default_app) + finally: + os.exit(1) - def tick(self): - sleep(self.wait_time) - self.time += 1 - @property - def second(self): - return self.time % 60 - - @property - def minute(self): - return (self.time / 60) % 60 - - @property - def hour(self): - return (self.time / 3600) % 24 - - -class Clock: - def __init__( - self, - sizex=80, - sizey=80, - radius=38, - offsetx=30, - hour_hand=True, - minute_hand=True, - second_hand=True, - console_out=False, - run_once=False, - update_interval=0, - ): - self.sizex = sizex - self.sizey = sizey - self.radius = radius - self.center = (int(self.sizex / 2), int(self.sizey / 2)) - self.hour_hand = hour_hand - self.minute_hand = minute_hand - self.second_hand = second_hand - self.console_out = console_out - self.update_interval = ( - update_interval if update_interval != 0 else (1 if self.second_hand else 30) - ) - self.run_once = run_once - self.offsetx = offsetx - self.time = Time() - self.theme = 0 - self.default_themes = [ - { - "background": [0, 0, 0], - "center": [255, 255, 255], - "m1": [255, 255, 255], - "m5": [255, 255, 255], - "hour_hand": [255, 255, 255], - "minute_hand": [255, 255, 255], - "second_hand": [255, 255, 255], - }, - { - "background": [130, 30, 70], - "center": [255, 255, 255], - "m1": [255, 255, 255], - "m5": [255, 255, 255], - "hour_hand": [255, 255, 255], - "minute_hand": [255, 255, 255], - "second_hand": [255, 255, 255], - }, - { - "background": [0, 80, 0], - "center": [255, 255, 255], - "m1": [255, 255, 255], - "m5": [255, 255, 255], - "hour_hand": [255, 255, 255], - "minute_hand": [255, 255, 255], - "second_hand": [255, 255, 255], - }, - { - "background": [0, 80, 80], - "center": [255, 255, 255], - "m1": [255, 255, 255], - "m5": [255, 255, 255], - "hour_hand": [255, 255, 255], - "minute_hand": [255, 255, 255], - "second_hand": [255, 255, 255], - }, - { - "background": [255, 255, 255], - "center": [0, 0, 0], - "m1": [0, 0, 0], - "m5": [0, 0, 0], - "hour_hand": [0, 0, 0], - "minute_hand": [0, 0, 0], - "second_hand": [0, 0, 0], - }, - ] - self.themes = self.default_themes - - # check for config file - if CONFIG_NAME in os.listdir("."): - self.readConfig() - else: - self.writeConfig() - - # load colors - self.setTheme(self.theme) - - def readConfig(self): - with open(CONFIG_NAME, "r") as f: - try: - c = ujson.loads(f.read()) - if ( - "themes" in c - and len(c["themes"]) > 0 - and isinstance(c["themes"], list) - ): - self.themes = c["themes"] - if "theme" and isinstance(c["theme"], int): - self.theme = c["theme"] - except ValueError: - print("parsing %s failed" % CONFIG_NAME) - - def writeConfig(self): - with open(CONFIG_NAME, "w") as f: - f.write(ujson.dumps({"theme": self.theme, "themes": self.themes})) - - def setTheme(self, theme): - self.theme = theme % len(self.themes) - self.background_col = ( - self.themes[self.theme]["background"] - if "background" in self.themes[self.theme] - else self.default_themes[0]["background"] - ) - self.center_col = ( - self.themes[self.theme]["center"] - if "center" in self.themes[self.theme] - else self.default_themes[0]["center"] - ) - self.m1_col = ( - self.themes[self.theme]["m1"] - if "m1" in self.themes[self.theme] - else self.default_themes[0]["m1"] - ) - self.m5_col = ( - self.themes[self.theme]["m5"] - if "m5" in self.themes[self.theme] - else self.default_themes[0]["m5"] - ) - self.hour_hand_col = ( - self.themes[self.theme]["hour_hand"] - if "hour_hand" in self.themes[self.theme] - else self.default_themes[0]["hour_hand"] - ) - self.minute_hand_col = ( - self.themes[self.theme]["minute_hand"] - if "minute_hand" in self.themes[self.theme] - else self.default_themes[0]["minute_hand"] - ) - self.second_hand_col = ( - self.themes[self.theme]["second_hand"] - if "second_hand" in self.themes[self.theme] - else self.default_themes[0]["second_hand"] - ) - - def loop(self): - colored = False - try: - with display.open() as disp: - button_pressed = False - while True: - self.updateClock(disp) - if self.run_once: - break - - # check for button presses - v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT) - if v == 0: - button_pressed = False - - if not button_pressed and v & buttons.BOTTOM_LEFT != 0: - button_pressed = True - self.setTheme(self.theme - 1) - self.writeConfig() - elif not button_pressed and v & buttons.BOTTOM_RIGHT != 0: - button_pressed = True - self.setTheme(self.theme + 1) - self.writeConfig() - - except KeyboardInterrupt: - for i in range(11): - leds.set(i, (0, 0, 0)) - return - - def drawImage(self, image): - with display.open() as d: - d.clear() - for x in range(len(image)): - for y in range(len(image[x])): - d.pixel( - x + self.offsetx, - y, - col=(255, 255, 255) if image[x][y] else (0, 0, 0), - ) - d.update() - - def updateClock(self, disp): - disp.clear(self.background_col) - localtime = utime.localtime() - - disp.pixel(self.center[0] + self.offsetx, self.center[1], col=self.center_col) - hour_coords = self.circlePoint( - math.radians( - (((localtime[3] % 12) / 12.0) if localtime[3] else 0) * 360 - + 270 - + (localtime[4] / 2) - ) - ) - minute_coords = self.circlePoint(math.radians(localtime[4] * 6 + 270)) - second_coords = self.circlePoint(math.radians(localtime[5] * 6 + 270)) - - for i in range(60): - degree = i * 6 + 90 - radian = -math.radians(degree) - coords = self.circlePoint(radian) - - if not i % 5: - self.addLine(disp, coords, self.center, 3, 1, col=self.m5_col) - else: - self.addLine(disp, coords, self.center, 1, col=self.m1_col) - - if self.hour_hand: - self.addLine( - disp, - self.center, - hour_coords, - int(self.radius / 3), - 1, - col=self.hour_hand_col, - ) - if self.minute_hand: - self.addLine( - disp, - self.center, - minute_coords, - int(self.radius / 2), - col=self.minute_hand_col, - ) - if self.second_hand: - self.addLine( - disp, - self.center, - second_coords, - self.radius - int(self.radius / 8.0), - col=self.second_hand_col, - ) - - if self.console_out: - for y in range(self.radius * 2): - line = "" - for x in range(self.radius * 2): - line = line + ( - "." - if image[(self.center[1] - self.radius) + y][ - (self.center[0] - self.radius) + x - ] - else " " - ) - print(line) - - disp.update() - - def circlePoint(self, t): - return ( - int(round(self.radius * math.cos(t))) + self.center[0], - int(round(self.radius * math.sin(t))) + self.center[1], - ) - - def addLine(self, disp, source, aim, length, thickness=1, col=(255, 255, 255)): - vector = self.subVector(aim, source) - vector = self.normVector(vector) - destination = self.addVector(source, self.multiplyVector(vector, length)) - - disp.line( - round(source[0]) + self.offsetx, - round(source[1]), - round(destination[0]) + self.offsetx, - round(destination[1]), - col=col, - size=thickness, - ) - - def normVector(self, v): - length = math.sqrt(sum([i ** 2 for i in v])) - new_v = [] - for i in range(len(v)): - new_v.append(v[i] / length) - return tuple(new_v) - - def subVector(self, v1, v2): - res = [] - for i in range(len(v1)): - res.append(v1[i] - v2[i]) - return tuple(res) - - def addVector(self, v1, v2): - res = [] - for i in range(len(v1)): - res.append(v1[i] + v2[i]) - return tuple(res) - - def multiplyVector(self, v, multiplier): - return tuple([i * multiplier for i in v]) - - -clock = Clock() -clock.loop() +if __name__ == "__main__": + main() diff --git a/preload/menu.py b/preload/menu.py index 5063803fc81145b4c9ccb8d9fb5515c6aa31a5d2..b8479c1dbdf9600d2af700249a35a33a770e9ad1 100644 --- a/preload/menu.py +++ b/preload/menu.py @@ -14,8 +14,12 @@ import ujson import sys BUTTON_TIMER_POPPED = -1 -COLOR1, COLOR2 = (color.CHAOSBLUE_DARK, color.CHAOSBLUE) +COLOR_BG = color.CHAOSBLUE_DARK +COLOR_BG_SEL = color.CHAOSBLUE +COLOR_ARROW = color.COMMYELLOW +COLOR_TEXT = color.COMMYELLOW MAXCHARS = 11 +HOMEAPP = "main.py" def create_folders(): @@ -31,13 +35,13 @@ def read_metadata(app_folder): with open(info_file) as f: information = f.read() return ujson.loads(information) - except BaseException as e: + except Exception as e: print("Failed to read metadata for %s" % (app_folder)) sys.print_exception(e) return { "author": "", "name": app_folder, - "descriptionr": "", + "description": "", "category": "", "revision": 0, } @@ -49,10 +53,10 @@ def list_apps(): # add main application for mainFile in os.listdir("/"): - if mainFile == "main.py": + if mainFile == HOMEAPP: apps.append( [ - "/main.py", + "/%s" % HOMEAPP, { "author": "card10badge Team", "name": "Home", @@ -71,7 +75,12 @@ def list_apps(): # with or without metadata.json for appFolder in dirlist: if not (appFolder.endswith(".py") or appFolder.endswith(".elf")): - apps.append(["/apps/%s/__init__.py" % appFolder, read_metadata(appFolder)]) + metadata = read_metadata(appFolder) + if not metadata.get("bin", None): + fileName = "/apps/%s/__init__.py" % appFolder + else: + fileName = "/apps/%s/%s" % (appFolder, metadata["bin"]) + apps.append([fileName, metadata]) # list simple python scripts for pyFile in dirlist: @@ -167,7 +176,7 @@ def draw_menu(disp, applist, pos, appcount, lineoffset): (i - start) * 20, 159, (i - start) * 20 + 20, - col=COLOR1 if i == pos else COLOR2, + col=COLOR_BG_SEL if i == pos else COLOR_BG, ) line = app[1]["name"] @@ -187,10 +196,11 @@ def draw_menu(disp, applist, pos, appcount, lineoffset): disp.print( " " + line[off : (off + (MAXCHARS - 1))], posy=(i - start) * 20, - bg=COLOR1 if i == pos else COLOR2, + fg=COLOR_TEXT, + bg=COLOR_BG_SEL if i == pos else COLOR_BG, ) if i == pos: - disp.print(">", posy=(i - start) * 20, fg=color.COMMYELLOW, bg=COLOR1) + disp.print(">", posy=(i - start) * 20, fg=COLOR_ARROW, bg=COLOR_BG_SEL) if linelength > (MAXCHARS - 1) and off < linelength - (MAXCHARS - 1): triangle(disp, 153, (i - start) * 20 + 6, False, 6) @@ -214,23 +224,12 @@ def main(): timerscrollspeed = 1 timerstartscroll = 5 timercountpopped = 0 + timerinactivity = 100 for ev in button_events(10): if numapps == 0: - disp.clear(color.COMMYELLOW) - disp.print( - " No apps ", - posx=17, - posy=20, - fg=color.COMMYELLOW_DARK, - bg=color.COMMYELLOW, - ) - disp.print( - "available", - posx=17, - posy=40, - fg=color.COMMYELLOW_DARK, - bg=color.COMMYELLOW, - ) + disp.clear(COLOR_BG) + disp.print(" No apps ", posx=17, posy=20, fg=COLOR_TEXT, bg=COLOR_BG) + disp.print("available", posx=17, posy=40, fg=COLOR_TEXT, bg=COLOR_BG) disp.update() continue @@ -254,6 +253,16 @@ def main(): ): lineoffset += 1 + if applist[0][0] == "/%s" % HOMEAPP and timercountpopped >= timerinactivity: + print("Inactivity timer popped") + disp.clear().update() + disp.close() + try: + os.exec("/%s" % HOMEAPP) + except OSError as e: + print("Loading failed: ", e) + os.exit(1) + elif ev == buttons.TOP_RIGHT: # Select & start disp.clear().update() @@ -268,4 +277,14 @@ def main(): if __name__ == "__main__": - main() + try: + main() + except Exception as e: + sys.print_exception(e) + with display.open() as d: + d.clear(color.COMMYELLOW) + d.print("Menu", posx=52, posy=20, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW) + d.print("crashed", posx=31, posy=40, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW) + d.update() + utime.sleep(2) + os.exit(1) diff --git a/pycardium/main.c b/pycardium/main.c index 56a7575a2cafb27774ae3785b2390f40262f433e..b87b4a1e26cedd4da3ccfd744c0f66894be6b2d0 100644 --- a/pycardium/main.c +++ b/pycardium/main.c @@ -60,7 +60,18 @@ int main(void) } epic_uart_write_str(header, sizeof(header)); - pyexec_friendly_repl(); + + for (;;) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl() != 0) { + break; + } + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } mp_deinit(); } diff --git a/pycardium/modules/bhi160-sys.c b/pycardium/modules/bhi160-sys.c index db0a5c9a65eb42f9b8ee392ffa5650909877bfb0..7320885cd9268bacd0fdfdc68e92b177fe2d1c6d 100644 --- a/pycardium/modules/bhi160-sys.c +++ b/pycardium/modules/bhi160-sys.c @@ -66,6 +66,17 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1( mp_bhi160_disable_sensor_obj, mp_bhi160_disable_sensor ); +STATIC mp_obj_t mp_bhi160_disable_all_sensors() +{ + epic_bhi160_disable_all_sensors(); + + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_0( + mp_bhi160_disable_all_sensors_obj, mp_bhi160_disable_all_sensors +); + STATIC const mp_rom_map_elem_t bhi160_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys_bhi160) }, { MP_ROM_QSTR(MP_QSTR_enable_sensor), @@ -74,6 +85,8 @@ STATIC const mp_rom_map_elem_t bhi160_module_globals_table[] = { MP_ROM_PTR(&mp_bhi160_read_sensor_obj) }, { MP_ROM_QSTR(MP_QSTR_disable_sensor), MP_ROM_PTR(&mp_bhi160_disable_sensor_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_all_sensors), + MP_ROM_PTR(&mp_bhi160_disable_all_sensors_obj) }, }; STATIC MP_DEFINE_CONST_DICT(bhi160_module_globals, bhi160_module_globals_table); diff --git a/pycardium/modules/light_sensor.c b/pycardium/modules/light_sensor.c index ce8343aa72c0a0a5e02ee28cba9b764dcad8ef7d..a27791de1a98fef59e6964631eca81ff932e222f 100644 --- a/pycardium/modules/light_sensor.c +++ b/pycardium/modules/light_sensor.c @@ -42,11 +42,18 @@ static mp_obj_t mp_light_sensor_stop() } static MP_DEFINE_CONST_FUN_OBJ_0(light_sensor_stop_obj, mp_light_sensor_stop); +static mp_obj_t mp_light_sensor_read() +{ + return mp_obj_new_int_from_uint(epic_light_sensor_read()); +} +static MP_DEFINE_CONST_FUN_OBJ_0(light_sensor_read_obj, mp_light_sensor_read); + static const mp_rom_map_elem_t light_sensor_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_light_sensor) }, { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&light_sensor_start_obj) }, { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&light_sensor_stop_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_reading), MP_ROM_PTR(&light_sensor_get_obj) } + { MP_ROM_QSTR(MP_QSTR_get_reading), MP_ROM_PTR(&light_sensor_get_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&light_sensor_read_obj) }, }; static MP_DEFINE_CONST_DICT( light_sensor_module_globals, light_sensor_module_globals_table diff --git a/pycardium/modules/py/bhi160.py b/pycardium/modules/py/bhi160.py index f95874e544c6456ebc4712653ce05b053c5f97dc..5d28e1e61dfbb913c3ee4dfdc00064fc4bb1342a 100644 --- a/pycardium/modules/py/bhi160.py +++ b/pycardium/modules/py/bhi160.py @@ -5,6 +5,15 @@ import ucollections DataVector = ucollections.namedtuple("DataVector", ["x", "y", "z", "status"]) +def disable_all_sensors(): + """ + Disable all sensor also if they are already deactivated. + + :returns: None + """ + sys_bhi160.disable_all_sensors() + + class BHI160: def enable_sensor(self): interrupt.disable_callback(self.interrupt_id) diff --git a/pycardium/modules/py/display.py b/pycardium/modules/py/display.py index e957977d6fc3603889f3155e515d351dc8f1753b..7b51cef374f8006b550ca955230a5ef9f3652d6c 100644 --- a/pycardium/modules/py/display.py +++ b/pycardium/modules/py/display.py @@ -1,6 +1,13 @@ import sys_display import color +# font enumeration +FONT8 = 0 +FONT12 = 1 +FONT16 = 2 +FONT20 = 3 +FONT24 = 4 + class Display: """ @@ -61,7 +68,7 @@ class Display: sys_display.clear(col) return self - def print(self, text, *, fg=None, bg=None, posx=0, posy=0): + def print(self, text, *, fg=None, bg=None, posx=0, posy=0, font=FONT20): """ Prints a string on the display. Font size is locked to 20px @@ -70,11 +77,29 @@ class Display: :param bg: Background color (expects RGB triple) :param posx: X-Position of the first character, 0 <= posx <= 160 :param posy: Y-Position of the first character, 0 <= posy <= 80 + :param font: 0 <= font <= 4 (currently) selects a font + + Avaiable Fonts: + + - :py:data:`display.FONT8` + - :py:data:`display.FONT12` + - :py:data:`display.FONT16` + - :py:data:`display.FONT20` + - :py:data:`display.FONT24` + + **Example:** + + .. code-block:: python + + with display.open() as d: + d.clear() + d.print('Hello Earth!', font=display.FONT24) + d.update() """ fg = fg or color.WHITE bg = bg or color.BLACK - sys_display.print(text, posx, posy, fg, bg) + sys_display.print_adv(text, posx, posy, fg, bg, font) return self def pixel(self, x, y, *, col=None): diff --git a/pycardium/modules/py/leds.py b/pycardium/modules/py/leds.py index 0cbf2c3fe3351088e6c89d16fbf01730c5718d49..ad5563cfb2a3c8fca7bf4a4909ac460ba0e86808 100644 --- a/pycardium/modules/py/leds.py +++ b/pycardium/modules/py/leds.py @@ -41,7 +41,13 @@ def set_flashlight(on): This LED can serve as a flashlight if worn on the left wrist or as a rad tattoo illuminator if worn on the right wrist. - :param bool on: Side LED on if true. + .. warning:: + + Because of a small error in the Harmonic Board layout, we could not + populate the flashlight LEDs in the production run. You can handsolder + it, though you have to reverse the direction. + + :param bool on: Side LED on if ``True``. """ sys_leds.set_flashlight(on) diff --git a/pycardium/modules/py/pride.py b/pycardium/modules/py/pride.py index 3bfb29b601a41c77a921463908a0b1d5b509e105..867e16c05d27f0881765c070303a823e20dae95a 100644 --- a/pycardium/modules/py/pride.py +++ b/pycardium/modules/py/pride.py @@ -14,7 +14,7 @@ flags["trans"] = [ [247, 168, 184], [85, 205, 252], ] -flags["bi"] = [[214, 2, 112], [155, 79, 150], [0, 56, 168]] +flags["bi"] = [[214, 2, 112], [214, 2, 112], [155, 79, 150], [0, 56, 168], [0, 56, 168]] flags["ace"] = [[1, 1, 1], [164, 164, 164], [255, 255, 255], [150, 0, 150]] flags["greyace"] = [ [150, 0, 150], diff --git a/pycardium/modules/py/simple_menu.py b/pycardium/modules/py/simple_menu.py index 6450e6a1cafd0cf686a4ac1930a1b15ab65ce544..42b117cc7998ac94397bee646f1c3fa82538af11 100644 --- a/pycardium/modules/py/simple_menu.py +++ b/pycardium/modules/py/simple_menu.py @@ -167,7 +167,7 @@ class Menu: ) self.disp.line(4, 22, 11, 29, col=self.color_sel, size=2) - self.disp.line(3, 37, 11, 29, col=self.color_sel, size=2) + self.disp.line(4, 36, 11, 29, col=self.color_sel, size=2) self.disp.update() diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h index a75d3ee2b01ada3648ad273178ca14149f0644f5..a3a873cbdc01b9f7fd295ed72aa4159f338ccada 100644 --- a/pycardium/modules/qstrdefs.h +++ b/pycardium/modules/qstrdefs.h @@ -67,6 +67,7 @@ Q(RTC_ALARM) Q(sys_bhi160) Q(enable_sensor) Q(disable_sensor) +Q(disable_all_sensors) Q(read_sensor) Q(x) Q(y) @@ -85,6 +86,7 @@ Q(read_thermistor_voltage) Q(sys_display) Q(display) Q(print) +Q(print_adv) Q(pixel) Q(backlight) Q(line) @@ -97,6 +99,7 @@ Q(light_sensor) Q(start) Q(get_reading) Q(stop) +Q(read) /* bme680 */ Q(bme680) diff --git a/pycardium/modules/sys_display.c b/pycardium/modules/sys_display.c index f5523d54831695679965ddcb76e7df8c6fa2ba29..4f92ab92dcc2cfc8cede58018c8778fe9e345918 100644 --- a/pycardium/modules/sys_display.c +++ b/pycardium/modules/sys_display.c @@ -53,6 +53,30 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( display_print_obj, 5, 5, mp_display_print ); +/* print something on the display */ +static mp_obj_t mp_display_print_adv(size_t n_args, const mp_obj_t *args) +{ + if (!mp_obj_is_str_or_bytes(args[0])) { + mp_raise_TypeError("input text must be a string"); + } + GET_STR_DATA_LEN(args[0], print, print_len); + uint32_t posx = mp_obj_get_int(args[1]); + uint32_t posy = mp_obj_get_int(args[2]); + uint32_t fg = get_color(args[3]); + uint32_t bg = get_color(args[4]); + uint8_t fontName = mp_obj_get_int(args[5]); + int res = epic_disp_print_adv( + fontName, posx, posy, (const char *)print, fg, bg + ); + if (res < 0) { + mp_raise_OSError(-res); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( + display_print_adv_obj, 6, 6, mp_display_print_adv +); + /* draw pixel on the display */ static mp_obj_t mp_display_pixel(size_t n_args, const mp_obj_t *args) { @@ -234,6 +258,7 @@ static const mp_rom_map_elem_t display_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&display_open_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&display_close_obj) }, { MP_ROM_QSTR(MP_QSTR_print), MP_ROM_PTR(&display_print_obj) }, + { MP_ROM_QSTR(MP_QSTR_print_adv), MP_ROM_PTR(&display_print_adv_obj) }, { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&display_pixel_obj) }, { MP_ROM_QSTR(MP_QSTR_backlight), MP_ROM_PTR(&display_backlight_obj) }, { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&display_line_obj) }, diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000000000000000000000000000000000000..cdb5f3d5a787cdb4b16904da1fb1d442bc7bb3b8 --- /dev/null +++ b/shell.nix @@ -0,0 +1,60 @@ +# This nix expression describes the build and debug environment for the +# firmware. To use it, first install nix and then invoke the "nix-shell" +# command to activate the environment. It will install all dependencies for and +# build and install a patched openocd, before dropping you into a shell where +# everything required for compiling/debugging the firmware is available. + +{ pkgs ? import <nixpkgs> {} }: + +let + crc16 = { stdenv, buildPythonPackage, fetchPypi }: + buildPythonPackage rec { + pname = "crc16"; + version = "0.1.1"; + src = fetchPypi { + inherit pname version; + sha256 = "15nkx0pa4lskwin84flpk8fsw3jqg6wic6v3s83syjqg76h6my61"; + }; + }; + + ocd = pkgs.openocd.overrideAttrs(old: { + name = "openocd-cardio"; + + src = pkgs.fetchgit { + url = "https://git.card10.badge.events.ccc.de/card10/openocd.git"; + rev = "90d828185cea44b29cffb40f2c8aea19282b9130"; + sha256 = "092wg19kjapv9s70b23ckd4j5i8ykk3d7mcl4h8cgl2acwcw8myr"; + fetchSubmodules = true; + }; + + nativeBuildInputs = old.nativeBuildInputs ++ [ + pkgs.which + pkgs.libtool + pkgs.autoconf + pkgs.automake + ]; + + SKIP_SUBMODULE="1"; + + preConfigure = '' + ./bootstrap + ''; + }); + +in pkgs.mkShell { + + buildInputs = [ + pkgs.meson + pkgs.ninja + pkgs.git + pkgs.gcc-arm-embedded + pkgs.jq + (pkgs.python3.withPackages (ps: [ + ps.pillow + (ps.callPackage crc16 {}) + ])) + ocd + pkgs.gcc-arm-embedded + ]; + +} diff --git a/tools/ls_cmsis_dap/Makefile b/tools/ls_cmsis_dap/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..3a1fefe7fa6953d733c704fad77a47a0d34d8449 --- /dev/null +++ b/tools/ls_cmsis_dap/Makefile @@ -0,0 +1,16 @@ +.PHONY: all clean + +all: ls_cmsis_dap-hidraw ls_cmsis_dap-libusb + +clean: + -rm *.o ls_cmsis_dap-hidraw ls_cmsis_dap-libusb + +ls_cmsis_dap.o: ls_cmsis_dap.c + +ls_cmsis_dap-hidraw: LDFLAGS=-lhidapi-hidraw +ls_cmsis_dap-hidraw: ls_cmsis_dap.o + $(CC) $(LDFLAGS) -o $@ $< + +ls_cmsis_dap-libusb: LDFLAGS=-lhidapi-libusb +ls_cmsis_dap-libusb: ls_cmsis_dap.o + $(CC) $(LDFLAGS) -o $@ $< diff --git a/tools/ls_cmsis_dap/ls_cmsis_dap.c b/tools/ls_cmsis_dap/ls_cmsis_dap.c new file mode 100644 index 0000000000000000000000000000000000000000..bd39674f12a5b3174a220fc67f4d96c78a6efc23 --- /dev/null +++ b/tools/ls_cmsis_dap/ls_cmsis_dap.c @@ -0,0 +1,31 @@ +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <hidapi/hidapi.h> + +int main(int argc, char *argv[]) +{ + int rc = 0; + if ((rc = hid_init())) { + fprintf(stderr, "hid_init: %d\n", rc); + goto done; + } + + struct hid_device_info *hid_devs = hid_enumerate(0x0d28, 0x0204); + if (!hid_devs) { + fprintf(stderr, "hid_enumerate: NULL\n"); + rc = 1; + goto done; + } + + for (struct hid_device_info *dev = hid_devs; dev; dev = dev->next) { + fprintf(stdout, "%s\n", dev->path); + } + +done: + if (hid_devs) { + hid_free_enumeration(hid_devs); + } + hid_exit(); + return -1; +} diff --git a/tools/make-release.sh b/tools/make-release.sh new file mode 100755 index 0000000000000000000000000000000000000000..09946b6543c6371ac5688410cb7221bcb5f082c7 --- /dev/null +++ b/tools/make-release.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +set -e + +cd "$(dirname "$0")/.." + +# Script to aid in creating a new card10 release +args=$( + getopt -l "version:,name:,help" -o "v:n:h" -- "$@" +) +eval set -- $args + +while [ $# -ge 1 ]; do + case "$1" in + -n|--name) + name="$2" + shift + ;; + -v|--version) + version="$2" + shift + ;; + -h|--help) + echo "usage: $0 --name <release-name> --version x.x" >&2 + exit 0 + ;; + esac + shift +done + +[[ "$name" == "" ]] && { echo "name must be set" >&2; exit 1; } +[[ "$version" == "" ]] && { echo "version must be set" >&2; exit 1; } + +# add a leading v to the version +if [[ "$version" =~ ^[^v].*$ ]]; then + version="v$version" +fi + +message() { + echo "$(tput bold)>>> $(tput sgr0)$(tput setaf 6)$*$(tput sgr0)" +} + +release_name="card10-${version}-${name}" + +message "Building release \"$release_name\" ..." + +git_version="$(git describe --always --dirty)" +if [[ "$git_version" != "$version" ]]; then + echo "Git says version is \"$git_version\" instead of \"$version\"!" >&2 + exit 1 +fi + +release_dir="release-$version" +mkdir "$release_dir" + +message "Building (non-jailbreak) release version ..." +./bootstrap.sh +ninja -C build/ + +message "Creating (non-jailbreak) release archive ..." +mkdir "$release_dir/$release_name" +cp -r -t "$release_dir/$release_name" preload/* +cp build/pycardium/pycardium_epicardium.bin "$release_dir/$release_name/card10.bin" +( cd "$release_dir"; zip -r "$release_name"{.zip,}; ) + +# Copy ELFs +mkdir "$release_dir/elfs" +cp -t "$release_dir/elfs" build/epicardium/epicardium.elf build/pycardium/pycardium.elf build/bootloader/bootloader.elf + +message "Building (jailbreak) release version ..." +./bootstrap.sh -Djailbreak_card10=true +ninja -C build/ + +message "Creating (jailbreak) release archive ..." +mkdir "$release_dir/$release_name-jailbreak" +cp -r -t "$release_dir/$release_name-jailbreak" preload/* +cp build/pycardium/pycardium_epicardium.bin "$release_dir/$release_name-jailbreak/card10.bin" +( cd "$release_dir"; zip -r "$release_name-jailbreak"{.zip,}; ) + +# Copy ELFs +mkdir "$release_dir/elfs-jailbreak" +cp -t "$release_dir/elfs-jailbreak" build/epicardium/epicardium.elf build/pycardium/pycardium.elf + +message "Done!" +echo "Archive (non-jailbreak): $release_dir/$release_name.zip" +echo "Archive (jailbreak): $release_dir/$release_name-jailbreak.zip" diff --git a/tools/pyboard.py b/tools/pyboard.py new file mode 100755 index 0000000000000000000000000000000000000000..148e07d9cf48706c6e7cb6f175a42a32722e49b9 --- /dev/null +++ b/tools/pyboard.py @@ -0,0 +1,537 @@ +#!/usr/bin/env python +# +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2014-2016 Damien P. George +# Copyright (c) 2017 Paul Sokolovsky +# +# 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. + +""" +pyboard interface + +This module provides the Pyboard class, used to communicate with and +control a MicroPython device over a communication channel. Both real +boards and emulated devices (e.g. running in QEMU) are supported. +Various communication channels are supported, including a serial +connection, telnet-style network connection, external process +connection. + +Example usage: + + import pyboard + pyb = pyboard.Pyboard('/dev/ttyACM0') + +Or: + + pyb = pyboard.Pyboard('192.168.1.1') + +Then: + + pyb.enter_raw_repl() + pyb.exec('import pyb') + pyb.exec('pyb.LED(1).on()') + pyb.exit_raw_repl() + +Note: if using Python2 then pyb.exec must be written as pyb.exec_. +To run a script from the local machine on the board and print out the results: + + import pyboard + pyboard.execfile('test.py', device='/dev/ttyACM0') + +This script can also be run directly. To execute a local script, use: + + ./pyboard.py test.py + +Or: + + python pyboard.py test.py + +""" + +import sys +import time +import os + +try: + stdout = sys.stdout.buffer +except AttributeError: + # Python2 doesn't have buffer attr + stdout = sys.stdout + + +def stdout_write_bytes(b): + b = b.replace(b"\x04", b"") + stdout.write(b) + stdout.flush() + + +class PyboardError(Exception): + pass + + +class TelnetToSerial: + def __init__(self, ip, user, password, read_timeout=None): + self.tn = None + import telnetlib + + self.tn = telnetlib.Telnet(ip, timeout=15) + self.read_timeout = read_timeout + if b"Login as:" in self.tn.read_until(b"Login as:", timeout=read_timeout): + self.tn.write(bytes(user, "ascii") + b"\r\n") + + if b"Password:" in self.tn.read_until(b"Password:", timeout=read_timeout): + # needed because of internal implementation details of the telnet server + time.sleep(0.2) + self.tn.write(bytes(password, "ascii") + b"\r\n") + + if b"for more information." in self.tn.read_until( + b'Type "help()" for more information.', timeout=read_timeout + ): + # login successful + from collections import deque + + self.fifo = deque() + return + + raise PyboardError("Failed to establish a telnet connection with the board") + + def __del__(self): + self.close() + + def close(self): + if self.tn: + self.tn.close() + + def read(self, size=1): + while len(self.fifo) < size: + timeout_count = 0 + data = self.tn.read_eager() + if len(data): + self.fifo.extend(data) + timeout_count = 0 + else: + time.sleep(0.25) + if ( + self.read_timeout is not None + and timeout_count > 4 * self.read_timeout + ): + break + timeout_count += 1 + + data = b"" + while len(data) < size and len(self.fifo) > 0: + data += bytes([self.fifo.popleft()]) + return data + + def write(self, data): + self.tn.write(data) + return len(data) + + def inWaiting(self): + n_waiting = len(self.fifo) + if not n_waiting: + data = self.tn.read_eager() + self.fifo.extend(data) + return len(data) + else: + return n_waiting + + +class ProcessToSerial: + "Execute a process and emulate serial connection using its stdin/stdout." + + def __init__(self, cmd): + import subprocess + + self.subp = subprocess.Popen( + cmd, + bufsize=0, + shell=True, + preexec_fn=os.setsid, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) + + # Initially was implemented with selectors, but that adds Python3 + # dependency. However, there can be race conditions communicating + # with a particular child process (like QEMU), and selectors may + # still work better in that case, so left inplace for now. + # + # import selectors + # self.sel = selectors.DefaultSelector() + # self.sel.register(self.subp.stdout, selectors.EVENT_READ) + + import select + + self.poll = select.poll() + self.poll.register(self.subp.stdout.fileno()) + + def close(self): + import signal + + os.killpg(os.getpgid(self.subp.pid), signal.SIGTERM) + + def read(self, size=1): + data = b"" + while len(data) < size: + data += self.subp.stdout.read(size - len(data)) + return data + + def write(self, data): + self.subp.stdin.write(data) + return len(data) + + def inWaiting(self): + # res = self.sel.select(0) + res = self.poll.poll(0) + if res: + return 1 + return 0 + + +class ProcessPtyToTerminal: + """Execute a process which creates a PTY and prints slave PTY as + first line of its output, and emulate serial connection using + this PTY.""" + + def __init__(self, cmd): + import subprocess + import re + import serial + + self.subp = subprocess.Popen( + cmd.split(), + bufsize=0, + shell=False, + preexec_fn=os.setsid, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + pty_line = self.subp.stderr.readline().decode("utf-8") + m = re.search(r"/dev/pts/[0-9]+", pty_line) + if not m: + print("Error: unable to find PTY device in startup line:", pty_line) + self.close() + sys.exit(1) + pty = m.group() + # rtscts, dsrdtr params are to workaround pyserial bug: + # http://stackoverflow.com/questions/34831131/pyserial-does-not-play-well-with-virtual-port + self.ser = serial.Serial(pty, interCharTimeout=1, rtscts=True, dsrdtr=True) + + def close(self): + import signal + + os.killpg(os.getpgid(self.subp.pid), signal.SIGTERM) + + def read(self, size=1): + return self.ser.read(size) + + def write(self, data): + return self.ser.write(data) + + def inWaiting(self): + return self.ser.inWaiting() + + +class Pyboard: + def __init__( + self, device, baudrate=115200, user="micro", password="python", wait=0 + ): + if device.startswith("exec:"): + self.serial = ProcessToSerial(device[len("exec:") :]) + elif device.startswith("execpty:"): + self.serial = ProcessPtyToTerminal(device[len("qemupty:") :]) + elif ( + device + and device[0].isdigit() + and device[-1].isdigit() + and device.count(".") == 3 + ): + # device looks like an IP address + self.serial = TelnetToSerial(device, user, password, read_timeout=10) + else: + import serial + + delayed = False + for attempt in range(wait + 1): + try: + self.serial = serial.Serial( + device, baudrate=baudrate, interCharTimeout=1 + ) + break + except (OSError, IOError): # Py2 and Py3 have different errors + if wait == 0: + continue + if attempt == 0: + sys.stdout.write("Waiting {} seconds for pyboard ".format(wait)) + delayed = True + time.sleep(1) + sys.stdout.write(".") + sys.stdout.flush() + else: + if delayed: + print("") + raise PyboardError("failed to access " + device) + if delayed: + print("") + + def close(self): + self.serial.close() + + def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): + # if data_consumer is used then data is not accumulated and the ending must be 1 byte long + assert data_consumer is None or len(ending) == 1 + + data = self.serial.read(min_num_bytes) + if data_consumer: + data_consumer(data) + timeout_count = 0 + while True: + if data.endswith(ending): + break + elif self.serial.inWaiting() > 0: + new_data = self.serial.read(1) + if data_consumer: + data_consumer(new_data) + data = new_data + else: + data = data + new_data + timeout_count = 0 + else: + timeout_count += 1 + if timeout is not None and timeout_count >= 100 * timeout: + break + time.sleep(0.01) + return data + + def enter_raw_repl(self): + self.serial.write(b"\r\x03\x03") # ctrl-C twice: interrupt any running program + + # flush input (without relying on serial.flushInput()) + n = self.serial.inWaiting() + while n > 0: + self.serial.read(n) + n = self.serial.inWaiting() + + self.serial.write(b"\r\x01") # ctrl-A: enter raw REPL + data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n>") + if not data.endswith(b"raw REPL; CTRL-B to exit\r\n>"): + print(data) + raise PyboardError("could not enter raw repl") + + self.serial.write(b"\x04") # ctrl-D: soft reset + data = self.read_until(1, b"soft reboot\r\n") + if not data.endswith(b"soft reboot\r\n"): + print(data) + raise PyboardError("could not enter raw repl") + # By splitting this into 2 reads, it allows boot.py to print stuff, + # which will show up after the soft reboot and before the raw REPL. + data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n") + if not data.endswith(b"raw REPL; CTRL-B to exit\r\n"): + print(data) + raise PyboardError("could not enter raw repl") + + def exit_raw_repl(self): + self.serial.write(b"\r\x02") # ctrl-B: enter friendly REPL + + def follow(self, timeout, data_consumer=None): + # wait for normal output + data = self.read_until(1, b"\x04", timeout=timeout, data_consumer=data_consumer) + if not data.endswith(b"\x04"): + raise PyboardError("timeout waiting for first EOF reception") + data = data[:-1] + + # wait for error output + data_err = self.read_until(1, b"\x04", timeout=timeout) + if not data_err.endswith(b"\x04"): + raise PyboardError("timeout waiting for second EOF reception") + data_err = data_err[:-1] + + # return normal and error output + return data, data_err + + def exec_raw_no_follow(self, command): + if isinstance(command, bytes): + command_bytes = command + else: + command_bytes = bytes(command, encoding="utf8") + + # check we have a prompt + data = self.read_until(1, b">") + if not data.endswith(b">"): + raise PyboardError("could not enter raw repl") + + # write command + for i in range(0, len(command_bytes), 256): + self.serial.write(command_bytes[i : min(i + 256, len(command_bytes))]) + time.sleep(0.01) + self.serial.write(b"\x04") + + # check if we could exec command + data = self.serial.read(2) + if data != b"OK": + raise PyboardError("could not exec command (response: %r)" % data) + + def exec_raw(self, command, timeout=10, data_consumer=None): + self.exec_raw_no_follow(command) + return self.follow(timeout, data_consumer) + + def eval(self, expression): + ret = self.exec_("print({})".format(expression)) + ret = ret.strip() + return ret + + def exec_(self, command): + ret, ret_err = self.exec_raw(command) + if ret_err: + raise PyboardError("exception", ret, ret_err) + return ret + + def execfile(self, filename): + with open(filename, "rb") as f: + pyfile = f.read() + return self.exec_(pyfile) + + def get_time(self): + t = str(self.eval("pyb.RTC().datetime()"), encoding="utf8")[1:-1].split(", ") + return int(t[4]) * 3600 + int(t[5]) * 60 + int(t[6]) + + +# in Python2 exec is a keyword so one must use "exec_" +# but for Python3 we want to provide the nicer version "exec" +setattr(Pyboard, "exec", Pyboard.exec_) + + +def execfile( + filename, device="/dev/ttyACM0", baudrate=115200, user="micro", password="python" +): + pyb = Pyboard(device, baudrate, user, password) + pyb.enter_raw_repl() + output = pyb.execfile(filename) + stdout_write_bytes(output) + pyb.exit_raw_repl() + pyb.close() + + +def main(): + import argparse + + cmd_parser = argparse.ArgumentParser(description="Run scripts on the pyboard.") + cmd_parser.add_argument( + "--device", + default="/dev/ttyACM0", + help="the serial device or the IP address of the pyboard", + ) + cmd_parser.add_argument( + "-b", "--baudrate", default=115200, help="the baud rate of the serial device" + ) + cmd_parser.add_argument( + "-u", "--user", default="micro", help="the telnet login username" + ) + cmd_parser.add_argument( + "-p", "--password", default="python", help="the telnet login password" + ) + cmd_parser.add_argument("-c", "--command", help="program passed in as string") + cmd_parser.add_argument( + "-w", + "--wait", + default=0, + type=int, + help="seconds to wait for USB connected board to become available", + ) + cmd_parser.add_argument( + "--follow", + action="store_true", + help="follow the output after running the scripts [default if no scripts given]", + ) + cmd_parser.add_argument("files", nargs="*", help="input files") + args = cmd_parser.parse_args() + + # open the connection to the pyboard + try: + pyb = Pyboard(args.device, args.baudrate, args.user, args.password, args.wait) + except PyboardError as er: + print(er) + sys.exit(1) + + # run any command or file(s) + if args.command is not None or len(args.files): + # we must enter raw-REPL mode to execute commands + # this will do a soft-reset of the board + try: + pyb.enter_raw_repl() + except PyboardError as er: + print(er) + pyb.close() + sys.exit(1) + + def execbuffer(buf): + try: + ret, ret_err = pyb.exec_raw( + buf, timeout=None, data_consumer=stdout_write_bytes + ) + except PyboardError as er: + print(er) + pyb.close() + sys.exit(1) + except KeyboardInterrupt: + sys.exit(1) + if ret_err: + pyb.exit_raw_repl() + pyb.close() + stdout_write_bytes(ret_err) + sys.exit(1) + + # run the command, if given + if args.command is not None: + execbuffer(args.command.encode("utf-8")) + + # run any files + for filename in args.files: + with open(filename, "rb") as f: + pyfile = f.read() + execbuffer(pyfile) + + # exiting raw-REPL just drops to friendly-REPL mode + pyb.exit_raw_repl() + + # if asked explicitly, or no files given, then follow the output + if args.follow or (args.command is None and len(args.files) == 0): + try: + ret, ret_err = pyb.follow(timeout=None, data_consumer=stdout_write_bytes) + except PyboardError as er: + print(er) + sys.exit(1) + except KeyboardInterrupt: + sys.exit(1) + if ret_err: + pyb.close() + stdout_write_bytes(ret_err) + sys.exit(1) + + # close the connection to the pyboard + pyb.close() + + +if __name__ == "__main__": + main() diff --git a/tools/pycard10.py b/tools/pycard10.py new file mode 100755 index 0000000000000000000000000000000000000000..d5b51e7ed0572f62088201e075b6d4f5dc3d5d05 --- /dev/null +++ b/tools/pycard10.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# This file is part of the card10 project card10.badge.events.ccc.de +# +# The MIT License (MIT) +# +# Copyright (c) 2019 Alexander Böhm +# +# 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. + +""" +card10 interface + +This module provides is an extenstion of the Pyboard class, used to +communicate with the card10 badge over the serial connection via USB. + +For description of the card10 board look at the project website <https://card10.badge.events.ccc.de>. + +Python2 isn't supported. + +Example usage: + + import pycard10 + card10 = pycard10.PyCard10('/dev/ttyACM0') + +Then: + + card10.enter_raw_repl() + card10.exec('import leds') + card10.exec('leds.set_rocket(0, 31)') + card10.exit_raw_repl() + + import pycard10 + pycard10.execfile('test.py', device='/dev/ttyACM0') + +This script can also be run directly. To execute a local script, use: + + tools/pycard10.py test.py + +Or: + python3 tools/pycard10.py test.py + +""" + +__author__ = "Alexander Böhm" +__copyright__ = "Copyright 2019, Alexander Böhm" +__license__ = "MIT" +__email__ = "alexander.boehm@malbolge.net" + +import sys +import time +import os +from pyboard import ( + stdout_write_bytes, + PyboardError, + Pyboard, + ProcessPtyToTerminal, + ProcessToSerial, + TelnetToSerial, +) + + +class PyCard10(Pyboard): + """ + Python card10 connector. + """ + + def __init__(self, device, wait=0): + """ + Open a connection to the card10 over the serial device *device*. + """ + Pyboard.__init__( + self, device=device, baudrate=115200, user=None, password=None, wait=wait + ) + + def exec_raw_no_follow(self, command): + """ + Execute a the command 'command' on the card10. + + Parameters: + command (bytes): Command or multiple commands + + Returns: + None + """ + + if isinstance(command, bytes): + command_bytes = command + else: + command_bytes = bytes(command, encoding="utf8") + + data = self.read_until(1, b">") + if not data.endswith(b">"): + raise PyboardError("card10 not in raw repl mode: (response: %r)" % (data)) + + # write command + for i in range(0, len(command_bytes), 256): + self.serial.write(command_bytes[i : min(i + 256, len(command_bytes))]) + time.sleep(0.01) + + self.serial.write(b"\x04") + + # check if we could exec command + data = self.serial.read(2) + if data != b"OK": + raise PyboardError("could not exec command (response: %r)" % data) + + def enter_raw_repl(self): + """ + Enter the RAW repl mode. After the prompt character ('>') left in the buffer of the serial line. + + Returns: + None + """ + self.serial.write(b"\x03\x03") # ctrl-C twice: interrupt any running program + + # flush input (without relying on serial.flushInput()) + n = self.serial.inWaiting() + while n > 0: + self.serial.read(n) + n = self.serial.inWaiting() + + self.serial.write(b"\x01") # ctrl-A: enter raw REPL + data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n>") + if not data.endswith(b"raw REPL; CTRL-B to exit\r\n>"): + raise PyboardError("could not enter raw repl") + + self.serial.write(b"\r\x04") # execute nothing + data = self.serial.read(2) + if data != b"OK": + raise PyboardError("could not enter raw repl") + + def exec(self, command): + """ + Execute a command on the card10 and return the output of the card10. + + Parameters: + command (bytes): Command or multiple commands + + Returns: + data(bytes), data_err(bytes): data is standard out, data_err is standard error + """ + return self.exec_(command) + + def soft_reset(self): + """ + Doing a soft reset on the board and going to menu. + + Returns: + None + """ + + self.serial.write(b"\x03\x03") # ctrl-C twice: interrupt any running program + + self.serial.write(b"\x01") # ctrl-B: ensue it's the normal mode + + self.serial.write(b"\x04") # ctrl-D: do the reset + + n = self.serial.inWaiting() + while n > 0: + self.serial.read(n) + n = self.serial.inWaiting() + + +def execfile(filename, device="/dev/ttyACM0"): + """ + Execute python source from *filename* via the RAW repl mode on the card10 connected via serial line *device*. + + Parameters: + filename(str): Path text file with commands + device(str): Path to the card10 device. + + Returns: + None + """ + c = PyCard10(device) + c.enter_raw_repl() + output = c.execfile(filename) + stdout_write_bytes(output) + c.exit_raw_repl() + c.close() + + +def main(): + """ + The main method. + + Returns: + None + """ + + import argparse + + cmd_parser = argparse.ArgumentParser(description="Run scripts on the card10.") + cmd_parser.add_argument( + "--device", default="/dev/ttyACM0", help="the serial device of the card10" + ) + cmd_parser.add_argument("-c", "--command", help="program passed in as string") + cmd_parser.add_argument( + "-w", + "--wait", + default=0, + type=int, + help="seconds to wait for USB connected board to become available", + ) + cmd_parser.add_argument( + "--follow", + action="store_true", + help="follow the output after running the scripts [default if no scripts given]", + ) + cmd_parser.add_argument( + "--reset", action="store_true", help="Soft reseting the card10" + ) + cmd_parser.add_argument("files", nargs="*", help="input files") + args = cmd_parser.parse_args() + + # open the connection to the card10 + try: + card10 = PyCard10(args.device, args.wait) + except PyboardError as er: + print(er) + sys.exit(1) + + if args.reset: + card10.soft_reset() + + elif args.command is not None or len(args.files): + # we must enter raw-REPL mode to execute commands + # this will do a soft-reset of the board + try: + card10.enter_raw_repl() + except PyboardError as er: + print(er) + card10.close() + sys.exit(1) + + def execbuffer(buf): + try: + ret, ret_err = card10.exec_raw( + buf, timeout=None, data_consumer=stdout_write_bytes + ) + except PyboardError as er: + print(er) + card10.close() + sys.exit(1) + except KeyboardInterrupt: + sys.exit(1) + if ret_err: + card10.exit_raw_repl() + card10.close() + stdout_write_bytes(ret_err) + sys.exit(1) + + # run the command, if given + if args.command is not None: + execbuffer(args.command.encode("utf-8")) + + # run any files + for filename in args.files: + with open(filename, "rb") as f: + pyfile = f.read() + execbuffer(pyfile) + + # exiting raw-REPL just drops to friendly-REPL mode + card10.exit_raw_repl() + + # if asked explicitly, or no files given, then follow the output + elif args.follow or (args.command is None and len(args.files) == 0): + try: + ret, ret_err = card10.follow(timeout=None, data_consumer=stdout_write_bytes) + except PyboardError as er: + print(er) + sys.exit(1) + except KeyboardInterrupt: + sys.exit(1) + if ret_err: + card10.close() + stdout_write_bytes(ret_err) + sys.exit(1) + + # close the connection to the card10 + card10.close() + + +if __name__ == "__main__": + main()