├── .clang-format ├── .gitmodules ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.MD ├── LICENSE ├── README.md ├── SECURITY.md ├── click ├── color.c ├── color.h ├── color_glue.c ├── tts.c ├── tts.h ├── tts_glue.c ├── tts_hal.c ├── tts_hal.h ├── tts_hw.c ├── tts_hw.h └── tts_image.h ├── client ├── README.md ├── client_internal.h ├── hclient.c ├── rolemgr.c ├── routing.c └── thr_pthread.c ├── dcfg └── srvcfg.d.ts ├── drivers ├── ads1115.c ├── aw86224fcr.c ├── cap1298.c ├── cps122.c ├── dps310.c ├── ds18b20.c ├── i2c_helpers.c ├── i2c_scan.c ├── kx023.c ├── kxtj3.c ├── lps33hwtr.c ├── lsm6ds.c ├── ltr390uv.c ├── max31855.c ├── max6675.c ├── mcp41010.c ├── mpl3115a2.c ├── ncv7726b.c ├── ncv7726b_daisy.c ├── qma7981.c ├── scd40.c ├── sgp30.c ├── sgpc3.c ├── sht30.c ├── shtc3.c ├── spiflash.c └── th02.c ├── inc ├── click │ ├── drv_digital_in.h │ ├── drv_digital_out.h │ ├── drv_i2c_master.h │ └── drv_name.h ├── interfaces │ ├── README.md │ ├── jd_alloc.h │ ├── jd_app.h │ ├── jd_hid.h │ ├── jd_hw.h │ ├── jd_lora.h │ ├── jd_rx.h │ ├── jd_tx.h │ └── jd_usb.h ├── jd_client.h ├── jd_config.h ├── jd_control.h ├── jd_dcfg.h ├── jd_dmesg.h ├── jd_drivers.h ├── jd_hclient.h ├── jd_io.h ├── jd_numfmt.h ├── jd_physical.h ├── jd_pipes.h ├── jd_protocol.h ├── jd_service_classes.h ├── jd_service_framework.h ├── jd_srvcfg.h ├── jd_thr.h └── jd_util.h ├── scripts ├── bump.sh ├── decode_multitouch_history.js ├── git-version.sh └── map-file-stats.js ├── services ├── accelerometer.c ├── analogsensor.c ├── barometer.c ├── braille_display.c ├── button.c ├── buzzer.c ├── color.c ├── dccurrentmeasurement.c ├── dcvoltagemeasurement.c ├── dotmatrix.c ├── eco2.c ├── flex.c ├── gamepad.c ├── gpio.c ├── gyroscope.c ├── hidjoystick.c ├── hidkeyboard.c ├── hidmouse.c ├── humidity.c ├── i2cserv.c ├── illuminance.c ├── interfaces │ ├── README.md │ ├── jd_adc.h │ ├── jd_disp.h │ ├── jd_flash.h │ ├── jd_hw_pwr.h │ ├── jd_oled.h │ ├── jd_pins.h │ ├── jd_pixel.h │ ├── jd_pwm.h │ ├── jd_sensor_api.h │ └── jd_spi.h ├── jd_console.h ├── jd_env.c ├── jd_sensor.c ├── jd_sensor.h ├── jd_services.h ├── jd_status_light.c ├── led_internal.h ├── leddisplay.c ├── ledsingle.c ├── ledstrip.c ├── lightbulb.c ├── logger.c ├── lorawan.c ├── magneticfieldlevel.c ├── motion.c ├── motor.c ├── multicaptouch.c ├── multitouch.c ├── oled.c ├── power.c ├── powersupply.c ├── relay.c ├── rotaryencoder.c ├── servo.c ├── settings.c ├── speechsynth.c ├── switch.c ├── temperature.c ├── touch.c ├── tvoc.c ├── uvindex.c └── vibrationmotor.c ├── source ├── interfaces │ ├── event_queue.c │ ├── simple_alloc.c │ ├── simple_rx.c │ └── tx_queue.c ├── jd_bqueue.c ├── jd_control.c ├── jd_dcfg.c ├── jd_dmesg.c ├── jd_fstor.c ├── jd_io.c ├── jd_ipipe.c ├── jd_numfmt.c ├── jd_opipe.c ├── jd_physical.c ├── jd_protocol.c ├── jd_queue.c ├── jd_send_util.c ├── jd_services.c ├── jd_srvcfg.c ├── jd_usb.c └── jd_util.c └── storage ├── crc32.c ├── ff ├── 00history.txt ├── 00readme.txt ├── LICENSE.txt ├── diskio.c_ ├── diskio.h ├── ff.c ├── ff.h └── ffconf.h ├── jd_storage.h └── lstore.c /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | UseTab: Never 4 | ColumnLimit: 100 5 | AllowShortFunctionsOnASingleLine: Inline 6 | SortIncludes: false 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "jacdac"] 2 | path = jacdac 3 | url = https://github.com/microsoft/jacdac 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT DEFINED JACDAC_USER_CONFIG_DIR) 2 | set(JACDAC_USER_CONFIG_DIR "..") 3 | endif() 4 | 5 | file(GLOB JDC_FILES 6 | services/*.c 7 | source/*.c 8 | drivers/*.c 9 | source/interfaces/*.c 10 | client/*.c 11 | storage/*.c 12 | storage/ff/*.c 13 | ) 14 | 15 | add_library(jacdac STATIC 16 | ${JDC_FILES} 17 | ) 18 | 19 | target_include_directories(jacdac PUBLIC 20 | ./inc 21 | . 22 | ${JACDAC_USER_CONFIG_DIR} 23 | ) 24 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.MD: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to 4 | agree to a Contributor License Agreement (CLA) declaring that you have the right to, 5 | and actually do, grant us the rights to use your contribution. For details, visit 6 | https://cla.microsoft.com. 7 | 8 | When you submit a pull request, a CLA-bot will automatically determine whether you need 9 | to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the 10 | instructions provided by the bot. You will only need to do this once across all repositories using our CLA. 11 | 12 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 13 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 14 | or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jacdac firmware library 2 | 3 | 4 | This library implements platform-agnostic aspects of the [Jacdac](https://aka.ms/jacdac) protocol. 5 | It's meant to be included as submodule, and use the build system of the parent repository. 6 | 7 | It's currently used in the following projects: 8 | * https://github.com/microsoft/devicescript 9 | * https://github.com/microsoft/jacdac-stm32x0 (which has some better docs on building) 10 | * https://github.com/microsoft/devicescript-esp32 11 | * https://github.com/microsoft/devicescript-pico 12 | * https://github.com/microsoft/devicescript-stm32 13 | 14 | This library is part of [Jacdac Device Development Kit](https://github.com/microsoft/jacdac-ddk). 15 | 16 | ## Adding new services 17 | 18 | It's best to start from an existing service, and do a search-replace (eg., `servo -> rocket`) 19 | * [services/servo.c](services/servo.c) has a simple example of registers 20 | * [services/buzzer.c](services/buzzer.c) has a simple example of how a command is handled (in `buzzer_handle_packet()`) 21 | * [services/thermometer.c](services/thermometer.c) is a very simple sensor 22 | * [services/power.c](services/power.c) is a more involved sensor (with custom registers) 23 | 24 | Once you add a service, make sure to add its `*_init()` function to 25 | [services/jd_services.h](services/jd_services.h). 26 | 27 | 28 | ## Contributing 29 | 30 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 31 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 32 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 33 | 34 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 35 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 36 | provided by the bot. You will only need to do this once across all repos using our CLA. 37 | 38 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 39 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 40 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 41 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /click/color_glue.c: -------------------------------------------------------------------------------- 1 | #include "color.h" 2 | #include "interfaces/jd_sensor_api.h" 3 | 4 | #ifdef MIKROBUS_AVAILABLE 5 | 6 | static color_t ctx; 7 | 8 | static void glue_color_init(void) { 9 | color_cfg_t cfg; 10 | color_cfg_setup(&cfg); 11 | COLOR_MAP_MIKROBUS(cfg, NA); 12 | if (color_init_(&ctx, &cfg)) 13 | JD_PANIC(); 14 | color_default_cfg(&ctx); 15 | color_write_byte(&ctx, COLOR_REG_RGBC_TIME, COLOR_RGBC_TIME_154ms); 16 | color_set_led(&ctx, 1, 1, 1); 17 | } 18 | 19 | static void glue_color_sleep(void) { 20 | color_set_led(&ctx, 0, 0, 0); 21 | // color_write_byte(&ctx, COLOR_REG_ENABLE, 0x00); 22 | } 23 | 24 | // this is what we get on "white" 25 | #define SCALE_R 50 26 | #define SCALE_G 34 27 | #define SCALE_B 24 28 | 29 | static void *glue_color_get_sample(void) { 30 | static uint32_t sample[4]; 31 | uint32_t r = color_read_data(&ctx, COLOR_COLOR_DATA_RED); 32 | uint32_t g = color_read_data(&ctx, COLOR_COLOR_DATA_GREEN); 33 | uint32_t b = color_read_data(&ctx, COLOR_COLOR_DATA_BLUE); 34 | uint32_t c = color_read_data(&ctx, COLOR_COLOR_DATA_CLEAR); 35 | sample[0] = r * ((1 << 16) / SCALE_R); 36 | sample[1] = g * ((1 << 16) / SCALE_G); 37 | sample[2] = b * ((1 << 16) / SCALE_B); 38 | sample[3] = c << 16; 39 | return sample; 40 | } 41 | 42 | const color_api_t color_click = { 43 | .init = glue_color_init, 44 | .get_reading = glue_color_get_sample, 45 | .sleep = glue_color_sleep, 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /click/tts_glue.c: -------------------------------------------------------------------------------- 1 | #include "color.h" 2 | #include "interfaces/jd_sensor_api.h" 3 | #include "tts.h" 4 | #include "services/jd_services.h" 5 | #include "tinyhw.h" 6 | 7 | #ifdef MIKROBUS_AVAILABLE 8 | 9 | void fatal_error( uint16_t *err ) { 10 | JD_PANIC(); 11 | } 12 | 13 | void msg_blocked( uint16_t *req, uint16_t *err ) { 14 | DMESG("MSG BLOCKED %d ERR %d", *req, *err); 15 | } 16 | 17 | void text_to_speech_volume(uint8_t volume) { 18 | // todo scale to decibels gain 19 | } 20 | 21 | bool text_to_speech_language(char* langId) { 22 | // convert from string to compatible lang id 23 | return false; 24 | } 25 | 26 | void text_to_speech_rate(uint32_t rate) { 27 | // scale to corresponding api 28 | } 29 | 30 | void text_to_speech_resume(void) { 31 | tts_unpause(); 32 | } 33 | 34 | void text_to_speech_pause(void) { 35 | tts_pause(); 36 | } 37 | 38 | void text_to_speech_cancel(void) { 39 | tts_stop(false); 40 | } 41 | 42 | void text_to_speech_speak(char *phrase) { 43 | tts_speak(phrase); 44 | } 45 | 46 | static inline int scale_rate(uint32_t rate) { 47 | // 0x004A ~ 0x0259 48 | return 0; 49 | } 50 | 51 | static inline int scale_volume(uint8_t vol) { 52 | if (vol > 100) 53 | vol = 100; 54 | 55 | const float DB_MIN = -49; 56 | const float DB_MAX = 19; 57 | int retval = ((float)vol / 100) * (DB_MAX - DB_MIN) + DB_MIN; 58 | return retval; 59 | } 60 | 61 | void text_to_speech_init(uint8_t volume, uint32_t rate, uint32_t pitch, char* language) { 62 | // chip select 63 | pin_setup_output(MIKROBUS_CS); 64 | pin_set(MIKROBUS_CS, 1); 65 | // mute 66 | pin_setup_output(MIKROBUS_AN); 67 | pin_set(MIKROBUS_AN, 0); 68 | // reset 69 | pin_setup_output(MIKROBUS_RST); 70 | pin_set(MIKROBUS_RST, 1); 71 | 72 | // data ready 73 | pin_setup_input(MIKROBUS_INT, PIN_PULL_NONE); 74 | 75 | sspi_init(1, 1, 1); 76 | 77 | tts_init(); 78 | tts_setup(); 79 | 80 | tts_msg_block_callback(msg_blocked); 81 | tts_fatal_err_callback(fatal_error); 82 | 83 | tts_config( 0x01, false, TTSV_US, 0x0100 ); 84 | tts_volume_set(19); 85 | } 86 | 87 | const speech_synth_api_t tts_click = { 88 | .init = text_to_speech_init, 89 | .speak = text_to_speech_speak, 90 | .set_volume=text_to_speech_volume, 91 | .set_rate=text_to_speech_rate, 92 | .set_language=text_to_speech_language, 93 | .pause=text_to_speech_pause, 94 | .resume=text_to_speech_resume, 95 | .cancel=text_to_speech_cancel 96 | }; 97 | 98 | #endif -------------------------------------------------------------------------------- /click/tts_hal.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Title : Text to Speech HAL 3 | * Filename : tts_hal.c 4 | * Author : MSV 5 | * Origin Date : 01/02/2016 6 | * Notes : None 7 | *******************************************************************************/ 8 | /*************** MODULE REVISION LOG ****************************************** 9 | * 10 | * Date Software Version Initials Description 11 | * 01/02/16 1.0.0 MSV Module Created. 12 | * 13 | *******************************************************************************/ 14 | /** 15 | * @file tts_hal.c 16 | * @brief

HAL layer

17 | */ 18 | /****************************************************************************** 19 | * Includes 20 | *******************************************************************************/ 21 | #include "tts_hal.h" 22 | #include "drv_name.h" 23 | #include "tinyhw.h" 24 | #include "lib.h" 25 | 26 | /****************************************************************************** 27 | * Module Preprocessor Constants 28 | *******************************************************************************/ 29 | 30 | /****************************************************************************** 31 | * Module Preprocessor Macros 32 | *******************************************************************************/ 33 | 34 | /****************************************************************************** 35 | * Module Typedefs 36 | *******************************************************************************/ 37 | 38 | /****************************************************************************** 39 | * Module Variable Definitions 40 | *******************************************************************************/ 41 | 42 | /****************************************************************************** 43 | * Function Prototypes 44 | *******************************************************************************/ 45 | 46 | /****************************************************************************** 47 | * Function Definitions 48 | *******************************************************************************/ 49 | void tts_hal_cs_high(void) 50 | { 51 | pin_set(MIKROBUS_CS,1); 52 | } 53 | 54 | void tts_hal_cs_low(void) 55 | { 56 | pin_set(MIKROBUS_CS,0); 57 | } 58 | 59 | void tts_hal_mut_high(void) 60 | { 61 | pin_set(MIKROBUS_AN,1); 62 | } 63 | 64 | void tts_hal_mut_low(void) 65 | { 66 | pin_set(MIKROBUS_AN,0); 67 | } 68 | 69 | void tts_hal_reset( void ) 70 | { 71 | pin_set(MIKROBUS_RST,0); 72 | Delay_10ms(); 73 | pin_set(MIKROBUS_RST,1); 74 | Delay_ms( POR_TIME ); 75 | } 76 | 77 | bool tts_hal_msg_rdy( void ) 78 | { 79 | return pin_get(MIKROBUS_INT); 80 | } 81 | 82 | void tts_hal_init(void) 83 | { 84 | tts_hal_reset(); 85 | tts_hal_cs_low(); 86 | tts_hal_mut_low(); 87 | } 88 | 89 | void tts_hal_write( uint8_t *buffer, 90 | uint16_t count ) 91 | { 92 | sspi_tx(buffer, count); 93 | } 94 | 95 | void tts_hal_read( uint8_t *buffer, 96 | uint16_t count ) 97 | { 98 | sspi_rx(buffer, count); 99 | } 100 | 101 | /*************** END OF FUNCTIONS ***************************************************************************/ -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Jacdac Client SDK 2 | 3 | ## Multi-threading considerations 4 | 5 | Most of the functions are meant to be run from Jacdac thread. 6 | Some functions can be called from anywhere, including interrupts. 7 | 8 | If there's no RTOS there is only one thread and there's nothing to worry about. 9 | 10 | In normal circumstances, the Jacdac thread runs approximately every 10ms. 11 | This includes running servers' `*_process()` functions as well as client process 12 | function and processing any incoming packets. 13 | 14 | In non-RTOS environment any interrupt wakes the main thread breaking its 10ms sleep. 15 | This includes incoming packets and external events. 16 | When running under an RTOS, the macro `JD_WAKE_MAIN()` is meant to wake the Jacdac thread 17 | from its 10ms sleep. 18 | 19 | ## Service clients 20 | 21 | Service clients are meant to make consumption of Jacdac easier when running under an RTOS. 22 | In particular, service client methods are safe to call from any thread. 23 | The methods can also block until a response is available. 24 | 25 | ## Usage scenarios 26 | 27 | * on service-bound set some config registers 28 | * on service-unbound notify user 29 | * on incoming sample record it somewhere (and maybe interact with some other service) 30 | * on incoming event from service (change event, or sth) interact with other service or the same 31 | 32 | ## Thoughts 33 | 34 | * jacdac main thread + jacdac callback thread 35 | * figure out thread-safety of client functions - should be mostly all right? protect stuff with a mutex? 36 | 37 | * derive hclient -> hclient - reference count in the parent - for multi-threaded operation 38 | 39 | * short timeouts 40 | * connection timeout vs service response timeout 41 | * handlers shouldn't block too much; RTOS devs are probably used to this? 42 | 43 | * handlers blocking too much 44 | 45 | * race between creating client, setting userdata etc, and events firing on it -------------------------------------------------------------------------------- /client/client_internal.h: -------------------------------------------------------------------------------- 1 | #include "jd_client.h" 2 | 3 | void rolemgr_device_destroyed(jd_device_t *dev); 4 | void rolemgr_role_changed(jd_role_t *role); 5 | -------------------------------------------------------------------------------- /client/thr_pthread.c: -------------------------------------------------------------------------------- 1 | #include "jd_thr.h" 2 | 3 | #if JD_THR_PTHREAD 4 | 5 | #define CHK JD_CHK 6 | 7 | #pragma region mutexes 8 | static pthread_mutexattr_t prio_inherit_attr; 9 | static bool thr_inited; 10 | 11 | int jd_thr_init_mutex(jd_mutex_t *mutex) { 12 | JD_ASSERT(thr_inited); 13 | CHK(pthread_mutex_init(mutex, &prio_inherit_attr)); 14 | return 0; 15 | } 16 | 17 | void jd_thr_destroy_mutex(jd_mutex_t *mutex) { 18 | CHK(pthread_mutex_destroy(mutex)); 19 | } 20 | 21 | void jd_thr_lock(jd_mutex_t *mutex) { 22 | CHK(pthread_mutex_lock(mutex)); 23 | } 24 | 25 | void jd_thr_unlock(jd_mutex_t *mutex) { 26 | CHK(pthread_mutex_unlock(mutex)); 27 | } 28 | #pragma endregion 29 | 30 | jd_thread_t jd_thr_self(void) { 31 | return pthread_self(); 32 | } 33 | 34 | #pragma region suspend / resume thread 35 | typedef struct thread_cond { 36 | struct thread_cond *next; 37 | jd_thread_t thread; 38 | pthread_mutex_t lock; 39 | pthread_cond_t cond; 40 | bool suspended; 41 | } thread_cond_t; 42 | static thread_cond_t *first_thread; 43 | static pthread_mutex_t thread_mux = PTHREAD_MUTEX_INITIALIZER; 44 | 45 | static thread_cond_t *lookup_thread_cond(jd_thread_t thr, bool create) { 46 | thread_cond_t *p; 47 | jd_thr_lock(&thread_mux); 48 | for (p = first_thread; p; p = p->next) { 49 | if (p->thread == thr) 50 | break; 51 | } 52 | if (p == NULL && create) { 53 | p = jd_alloc(sizeof(*p)); 54 | jd_thr_init_mutex(&p->lock); 55 | pthread_cond_init(&p->cond, NULL); 56 | p->thread = thr; 57 | p->next = first_thread; 58 | first_thread = p; 59 | } 60 | jd_thr_unlock(&thread_mux); 61 | return p; 62 | } 63 | 64 | // note that these two are way simpler in an RTOS - it typically let's one just suspend/resume 65 | // threads 66 | void jd_thr_suspend_self(void) { 67 | thread_cond_t *c = lookup_thread_cond(pthread_self(), true); 68 | jd_thr_lock(&c->lock); 69 | JD_ASSERT(!c->suspended); 70 | c->suspended = true; 71 | do { 72 | CHK(pthread_cond_wait(&c->cond, &c->lock)); 73 | } while (c->suspended); 74 | jd_thr_unlock(&c->lock); 75 | } 76 | 77 | void jd_thr_resume(jd_thread_t t) { 78 | thread_cond_t *c = lookup_thread_cond(pthread_self(), false); 79 | JD_ASSERT(c != NULL); 80 | jd_thr_lock(&c->lock); 81 | JD_ASSERT(c->suspended); 82 | c->suspended = false; 83 | CHK(pthread_cond_signal(&c->cond)); 84 | jd_thr_unlock(&c->lock); 85 | } 86 | #pragma endregion 87 | 88 | void jd_thr_init(void) { 89 | JD_ASSERT(!thr_inited); 90 | thr_inited = true; 91 | CHK(pthread_mutexattr_init(&prio_inherit_attr)); 92 | CHK(pthread_mutexattr_setprotocol(&prio_inherit_attr, PTHREAD_PRIO_INHERIT)); 93 | } 94 | 95 | static jd_thread_t process_thread; 96 | static pthread_mutex_t process_mux = PTHREAD_MUTEX_INITIALIZER; 97 | static pthread_cond_t process_cond = PTHREAD_COND_INITIALIZER; 98 | static bool process_pending; 99 | 100 | void jd_thr_wake_main(void) { 101 | jd_thr_lock(&process_mux); 102 | process_pending = true; 103 | pthread_cond_signal(&process_cond); 104 | jd_thr_unlock(&process_mux); 105 | } 106 | 107 | static void process_worker(void *userdata) { 108 | for (;;) { 109 | jd_thr_lock(&process_mux); 110 | if (!process_pending) { 111 | struct timespec tenms = {.tv_sec = 0, .tv_nsec = 10 * 1000000}; 112 | pthread_cond_timedwait(&process_cond, &process_mux, &tenms); 113 | } 114 | process_pending = false; 115 | jd_thr_unlock(&process_mux); 116 | 117 | jd_process_everything(); 118 | } 119 | } 120 | 121 | void jd_thr_start_process_worker(void) { 122 | JD_ASSERT(process_thread == 0); 123 | process_thread = jd_thr_start_thread(process_worker, NULL); 124 | } 125 | 126 | jd_thread_t jd_thr_start_thread(void (*worker)(void *userdata), void *userdata) { 127 | jd_thread_t t; 128 | 129 | CHK(pthread_create(&t, NULL, (void *(*)(void *))worker, userdata)); 130 | CHK(pthread_detach(t)); 131 | 132 | return t; 133 | } 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /drivers/aw86224fcr.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | #include "services/jd_services.h" 3 | 4 | #define MAX_DURATION 200 5 | 6 | #define AW86224_FCR_ADDR 0x58 7 | 8 | #define AW86224_FCR_RESET_REG 0x00 9 | #define AW86224_FCR_RESET_VAL 0xaa 10 | 11 | #define AW86224_FCR_CONTCFG1_REG 0x18 12 | #define AW86224_FCR_CONTCFG2_REG 0x19 13 | #define AW86224_FCR_CONTCFG3_REG 0x1A 14 | // #define AW86224_FCR_CONTCFG4_REG 0x1B 15 | #define AW86224_FCR_CONTCFG5_REG 0x1C 16 | #define AW86224_FCR_CONTCFG6_REG 0x1D 17 | #define AW86224_FCR_CONTCFG7_REG 0x1E 18 | #define AW86224_FCR_DRV1_TIME_REG 0x1F // 4 19 | #define AW86224_FCR_DRV2_TIME_REG 0x20 // 6 20 | #define AW86224_FCR_BRK_TIME_REG 0x21 // 8 21 | #define AW86224_FCR_CONTCFG11_REG 0x22 22 | 23 | #define AW86224_FCR_PLAYCFG2_REG 0x07 24 | #define AW86224_FCR_PLAYCFG3_REG 0x08 25 | 26 | #define AW86224_FCR_PLAYCFG4_REG 0x09 27 | #define AW86224_FCR_PLAYCFG4_VAL_GO 0x01 28 | #define AW86224_FCR_PLAYCFG4_VAL_STOP 0x02 29 | 30 | #define AW86224_FCR_RTPDATA_REG 0x32 31 | 32 | #define AW86224_FCR_GLBRD5_REG 0x3f 33 | 34 | #define AW86224_FCR_SYSCTRL1_REG 0x43 35 | #define AW86224_FCR_SYSCTRL1_VAL_ENRAM 0x08 36 | 37 | #define AW86224_FCR_SYSCTRL2_REG 0x44 38 | #define AW86224_FCR_SYSCTRL2_VAL_12KHz 0x03 39 | 40 | static void write_reg(uint8_t reg, uint8_t v) { 41 | if (i2c_write_reg(AW86224_FCR_ADDR, reg, v) != 0) 42 | JD_PANIC(); 43 | } 44 | 45 | static void aw86224fcr_go(void) { 46 | // let's go! 47 | write_reg(AW86224_FCR_PLAYCFG4_REG, AW86224_FCR_PLAYCFG4_VAL_GO); 48 | target_wait_us(100); 49 | 50 | #if 0 51 | int retry = 0; 52 | 53 | // check the state machine of the part has moved into RTP mode. 54 | while (1) { 55 | uint8_t glb_state = i2c_read_reg(AW86224_FCR_ADDR, AW86224_FCR_GLBRD5_REG); 56 | 57 | if (glb_state == 0b1000) 58 | break; 59 | 60 | target_wait_us(50); 61 | 62 | retry++; 63 | 64 | if (retry > 500) 65 | target_reset(); 66 | } 67 | #endif 68 | } 69 | 70 | static int aw86224fcr_write_amplitude(uint8_t amplitude, uint32_t duration_ms) { 71 | if (amplitude == 0) 72 | return 0; 73 | 74 | uint8_t glb_state = i2c_read_reg(AW86224_FCR_ADDR, AW86224_FCR_GLBRD5_REG) & 0xf; 75 | if (glb_state != 0) 76 | return -1; // already doing something 77 | 78 | if (duration_ms > MAX_DURATION) 79 | return -2; 80 | 81 | // 2.5ms per DRVx_TIME 82 | int v = (duration_ms * 51) >> 8; 83 | write_reg(AW86224_FCR_DRV1_TIME_REG, v); 84 | write_reg(AW86224_FCR_DRV2_TIME_REG, v); 85 | aw86224fcr_go(); 86 | 87 | return 0; 88 | } 89 | 90 | static void aw86224fcr_reset(void) { 91 | target_wait_us(5000); 92 | i2c_write_reg(AW86224_FCR_ADDR, AW86224_FCR_RESET_REG, AW86224_FCR_RESET_VAL); 93 | target_wait_us(5000); 94 | } 95 | 96 | static void aw86224fcr_init(void) { 97 | i2c_init(); 98 | 99 | aw86224fcr_reset(); 100 | 101 | // set CONT mode 102 | write_reg(AW86224_FCR_PLAYCFG3_REG, 0b110); 103 | 104 | write_reg(AW86224_FCR_CONTCFG1_REG, 0b11100001); // 700Hz 105 | write_reg(AW86224_FCR_BRK_TIME_REG, 5); 106 | } 107 | 108 | const vibration_motor_api_t aw86224fcr = { // 109 | .init = aw86224fcr_init, 110 | .write_amplitude = aw86224fcr_write_amplitude, 111 | .max_duration = MAX_DURATION}; -------------------------------------------------------------------------------- /drivers/cap1298.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | #include "services/jd_services.h" 3 | 4 | #define CAP1298_ADDR 0x28 5 | 6 | #define PROD_ID_ADDR 0xFD 7 | #define PROD_ID_VAL 0x71 8 | 9 | #define MFR_ID_ADDR 0xFD 10 | #define MFR_ID_VAL 0x5D 11 | 12 | #define INT_EN_ADDR 0x27 13 | 14 | #define MAIN_CONTROL_ADDR 0x0 15 | #define GEN_STATUS_ADDR 0x2 16 | #define SEN_STATUS_ADDR 0x3 17 | 18 | #define REPEAT_ENABLE_ADDR 0x28 19 | #define MULTI_TOUCH_CFG_ADDR 0x2a 20 | 21 | static void writeReg(uint8_t reg, uint8_t val) { 22 | i2c_write_reg(CAP1298_ADDR, reg, val); 23 | } 24 | 25 | static void readData(uint8_t reg, uint8_t *dst, int len) { 26 | i2c_read_reg_buf(CAP1298_ADDR, reg, dst, len); 27 | } 28 | 29 | static int readReg(uint8_t reg) { 30 | uint8_t r = 0; 31 | readData(reg, &r, 1); 32 | return r; 33 | } 34 | 35 | static void clear_int(void) { 36 | writeReg(MAIN_CONTROL_ADDR, 0b00000000); // no multi-touch processing; we let app do that 37 | writeReg(REPEAT_ENABLE_ADDR, 0); 38 | } 39 | 40 | void *cap1298_read(void) { 41 | clear_int(); 42 | uint8_t gen_status; 43 | uint8_t sen_status; 44 | static uint16_t sample[8]; 45 | readData(GEN_STATUS_ADDR, &gen_status, 1); 46 | readData(SEN_STATUS_ADDR, &sen_status, 1); 47 | writeReg(MAIN_CONTROL_ADDR, 0b00000000); 48 | // DMESG("G: %x S: %x", gen_status, sen_status); 49 | for (int i = 0; i < 8; ++i) 50 | sample[i] = sen_status & (1 << i) ? 0xffff : 0x0000; 51 | return sample; 52 | } 53 | 54 | void cap1298_cfg(void) { 55 | clear_int(); 56 | writeReg(MULTI_TOUCH_CFG_ADDR, 0b00000000); 57 | } 58 | 59 | bool cap1298_is_present(void) { 60 | return i2c_read_reg(CAP1298_ADDR, PROD_ID_ADDR) == PROD_ID_VAL; 61 | } 62 | 63 | void cap1298_init(void) { 64 | 65 | i2c_init(); 66 | 67 | target_wait_us(1000); 68 | 69 | int v = readReg(PROD_ID_ADDR); 70 | DMESG("CAP1298 acc id: %x", v); 71 | 72 | if (v == PROD_ID_VAL) { 73 | // OK 74 | } else { 75 | DMESG("invalid chip"); 76 | JD_PANIC(); 77 | } 78 | 79 | cap1298_cfg(); 80 | } 81 | 82 | const captouch_api_t captouch_cap1298 = { 83 | .name = "cap1293", 84 | .init = cap1298_init, 85 | .get_reading = cap1298_read, 86 | .is_present = cap1298_is_present, 87 | }; -------------------------------------------------------------------------------- /drivers/cps122.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | 3 | // TODO this doesn't seem to work 4 | 5 | #define SAMPLING_MS 500 6 | #define PRECISION 10 7 | 8 | #ifndef CPS122_ADDR 9 | #define CPS122_ADDR 0x6D 10 | #endif 11 | 12 | #define CPS122_CTRL 0x30 13 | #define CPS122_MR 0x0A 14 | #define CPS122_GD 0x06 15 | 16 | typedef struct state { 17 | uint8_t inited; 18 | uint8_t read_issued; 19 | env_reading_t pressure; 20 | env_reading_t temperature; 21 | uint32_t nextsample; 22 | } ctx_t; 23 | static ctx_t state; 24 | 25 | static const int32_t pressure_error[] = {ERR_PRESSURE(300, 1), ERR_PRESSURE(1200, 1), ERR_END}; 26 | 27 | static const int32_t temperature_error[] = {ERR_TEMP(-40, 1.0), ERR_TEMP(85, 1.0), ERR_END}; 28 | 29 | static void send_cmd(uint16_t cmd) { 30 | if (i2c_write_reg_buf(CPS122_ADDR, cmd, NULL, 0)) 31 | JD_PANIC(); 32 | } 33 | 34 | static void cps122_init(void) { 35 | ctx_t *ctx = &state; 36 | if (ctx->inited) 37 | return; 38 | 39 | ctx->pressure.min_value = SCALE_PRESSURE(300); 40 | ctx->pressure.max_value = SCALE_PRESSURE(1200); 41 | ctx->temperature.min_value = SCALE_TEMP(-40); 42 | ctx->temperature.max_value = SCALE_TEMP(85); 43 | 44 | ctx->nextsample = now; 45 | 46 | ctx->inited = 1; 47 | i2c_init(); 48 | #if 0 49 | uint8_t buf[5]; 50 | int id = i2c_read_reg_buf(CPS122_ADDR, CPS122_GD, buf, sizeof(buf)); 51 | if (id < 0) 52 | JD_PANIC(); 53 | #endif 54 | } 55 | 56 | static void cps122_process(void) { 57 | ctx_t *ctx = &state; 58 | 59 | // the 10ms here is just for readings, we actually sample at SAMPLING_MS 60 | // the datasheet says max reading time is ~5ms; give a little more time 61 | if (jd_should_sample_delay(&ctx->nextsample, 10000)) { 62 | if (!ctx->read_issued) { 63 | ctx->read_issued = 1; 64 | send_cmd(CPS122_MR); 65 | } else { 66 | uint8_t data[5]; 67 | int r = i2c_read_reg_buf(CPS122_ADDR, CPS122_GD, data, sizeof(data)); 68 | if (r < 0) 69 | JD_PANIC(); 70 | 71 | int16_t rtemp = (data[3] << 8) | data[4]; 72 | int32_t temp = rtemp << 2; 73 | 74 | uint32_t pressure = (data[0] << 16) | (data[1] << 8) | data[2]; 75 | pressure = (pressure * 4) / 25; 76 | 77 | ctx->read_issued = 0; 78 | ctx->nextsample = now + SAMPLING_MS * 1000; 79 | 80 | env_set_value(&ctx->pressure, pressure, pressure_error); 81 | env_set_value(&ctx->temperature, temp, temperature_error); 82 | ctx->inited = 2; 83 | } 84 | } 85 | } 86 | 87 | static void *cps122_temperature(void) { 88 | ctx_t *ctx = &state; 89 | if (ctx->inited >= 2) 90 | return &ctx->temperature; 91 | return NULL; 92 | } 93 | 94 | static void *cps122_pressure(void) { 95 | ctx_t *ctx = &state; 96 | if (ctx->inited >= 2) 97 | return &ctx->pressure; 98 | return NULL; 99 | } 100 | 101 | const env_sensor_api_t temperature_cps122 = { 102 | .init = cps122_init, 103 | .process = cps122_process, 104 | .get_reading = cps122_temperature, 105 | }; 106 | 107 | const env_sensor_api_t pressure_cps122 = { 108 | .init = cps122_init, 109 | .process = cps122_process, 110 | .get_reading = cps122_pressure, 111 | }; 112 | -------------------------------------------------------------------------------- /drivers/ds18b20.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | 3 | #define SAMPLING_MS 200 // sampling at SAMPLING_MS+CONVERSION_MS 4 | #define CONVERSION_MS 800 // for 12 bits; less for less bits 5 | #define RESOLUTION 12 // bits 6 | 7 | #define PRECISION 10 // i22.10 format 8 | 9 | #define DS18B20_SKIP_ROM 0xCC 10 | #define DS18B20_READ_ROM 0x33 11 | #define DS18B20_CONVERT_T 0x44 12 | #define DS18B20_WRITE_SCRATCHPAD 0x4E 13 | #define DS18B20_READ_SCRATCHPAD 0xBE 14 | 15 | typedef struct state { 16 | uint8_t inited; 17 | uint8_t in_temp; 18 | env_reading_t temperature; 19 | uint32_t nextsample; 20 | } ctx_t; 21 | static ctx_t state; 22 | 23 | static const int32_t temperature_error[] = { 24 | ERR_TEMP(-55, 2), ERR_TEMP(-30, 1), ERR_TEMP(-10, 0.5), ERR_TEMP(85, 0.5), ERR_TEMP(100, 1), 25 | ERR_TEMP(125, 2), ERR_END}; 26 | 27 | static void ds18b20_cmd(uint8_t cmd) { 28 | if (one_reset() != 0) 29 | JD_PANIC(); 30 | if (cmd != DS18B20_READ_ROM) 31 | one_write(DS18B20_SKIP_ROM); 32 | one_write(cmd); 33 | } 34 | 35 | static int read_data(void) { 36 | ds18b20_cmd(DS18B20_READ_SCRATCHPAD); 37 | int16_t v = one_read(); 38 | v |= one_read() << 8; 39 | return v; 40 | } 41 | 42 | static void ds18b20_init(void) { 43 | ctx_t *ctx = &state; 44 | if (ctx->inited) 45 | return; 46 | ctx->inited = 1; 47 | one_init(); 48 | ctx->temperature.min_value = SCALE_TEMP(-55); 49 | ctx->temperature.max_value = SCALE_TEMP(125); 50 | 51 | ds18b20_cmd(DS18B20_READ_ROM); 52 | for (int i = 0; i < 8; ++i) 53 | DMESG("r[%d] = %x", i, one_read()); 54 | 55 | ds18b20_cmd(DS18B20_WRITE_SCRATCHPAD); 56 | one_write(0x00); // TH 57 | one_write(0x00); // TL 58 | one_write(((RESOLUTION - 9) << 5) | 0x1F); // Config 59 | } 60 | 61 | static void ds18b20_process(void) { 62 | ctx_t *ctx = &state; 63 | 64 | if (jd_should_sample_delay(&ctx->nextsample, CONVERSION_MS * 1000)) { 65 | if (ctx->in_temp) { 66 | int v = read_data(); 67 | ctx->in_temp = 0; 68 | env_set_value(&ctx->temperature, v << (PRECISION - 4), temperature_error); 69 | ctx->nextsample = now + SAMPLING_MS * 1000; 70 | // DMESG("t=%dC", ctx->temperature.value >> PRECISION); 71 | ctx->inited = 2; 72 | } else { 73 | ctx->in_temp = 1; 74 | ds18b20_cmd(DS18B20_CONVERT_T); 75 | // DMESG("conv"); 76 | } 77 | } 78 | } 79 | 80 | static void *ds18b20_temperature(void) { 81 | ctx_t *ctx = &state; 82 | if (ctx->inited < 2) 83 | return NULL; 84 | return &ctx->temperature; 85 | } 86 | 87 | const env_sensor_api_t temperature_ds18b20 = { 88 | .init = ds18b20_init, 89 | .process = ds18b20_process, 90 | .get_reading = ds18b20_temperature, 91 | }; 92 | -------------------------------------------------------------------------------- /drivers/i2c_helpers.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | 3 | #if JD_I2C_HELPERS 4 | 5 | #define CHECK_RET(call) \ 6 | do { \ 7 | int _r = call; \ 8 | if (_r < 0) \ 9 | return _r; \ 10 | } while (0) 11 | 12 | int i2c_write_ex(uint8_t device_address, const void *src, unsigned len, bool repeated) { 13 | return i2c_write_ex2(device_address, src, len, NULL, 0, repeated); 14 | } 15 | 16 | // 8-bit reg addr 17 | int i2c_write_reg_buf(uint8_t addr, uint8_t reg, const void *src, unsigned len) { 18 | return i2c_write_ex2(addr, ®, 1, src, len, false); 19 | } 20 | 21 | int i2c_write_reg(uint8_t addr, uint8_t reg, uint8_t val) { 22 | return i2c_write_reg_buf(addr, reg, &val, 1); 23 | } 24 | 25 | int i2c_read_reg_buf(uint8_t addr, uint8_t reg, void *dst, unsigned len) { 26 | CHECK_RET(i2c_write_ex(addr, ®, 1, true) < 0); 27 | return i2c_read_ex(addr, dst, len); 28 | } 29 | 30 | int i2c_read_reg(uint8_t addr, uint8_t reg) { 31 | uint8_t r = 0; 32 | CHECK_RET(i2c_read_reg_buf(addr, reg, &r, 1)); 33 | return r; 34 | } 35 | 36 | // 16-bit reg addr 37 | int i2c_write_reg16_buf(uint8_t addr, uint16_t reg, const void *src, unsigned len) { 38 | uint8_t regaddr[2] = {reg >> 8, reg & 0xff}; 39 | return i2c_write_ex2(addr, regaddr, 2, src, len, false); 40 | } 41 | 42 | int i2c_write_reg16(uint8_t addr, uint16_t reg, uint8_t val) { 43 | return i2c_write_reg16_buf(addr, reg, &val, 1); 44 | } 45 | 46 | int i2c_read_reg16_buf(uint8_t addr, uint16_t reg, void *dst, unsigned len) { 47 | uint8_t a[] = {reg >> 8, reg & 0xff}; 48 | CHECK_RET(i2c_write_ex(addr, a, 2, true)); 49 | return i2c_read_ex(addr, dst, len); 50 | } 51 | 52 | int i2c_read_reg16(uint8_t addr, uint16_t reg) { 53 | uint8_t r = 0; 54 | CHECK_RET(i2c_read_reg16_buf(addr, reg, &r, 1)); 55 | return r; 56 | } 57 | 58 | #endif -------------------------------------------------------------------------------- /drivers/i2c_scan.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | #include "services/jd_services.h" 3 | 4 | const accelerometer_api_t *i2c_accelerometers[] = { 5 | &accelerometer_lsm6ds, // STMicro LSM6DSxx 6 | &accelerometer_kxtj3, // Kionix KXTJ3-1057 7 | &accelerometer_kx023, // Kionix KX023-1025 8 | NULL, 9 | }; 10 | 11 | const gyroscope_api_t *i2c_gyro[] = { 12 | &gyroscope_lsm6ds, // STMicro LSM6DSxx 13 | NULL, 14 | }; 15 | 16 | const captouch_api_t *i2c_captouch[] = { 17 | &captouch_cap1298, // Microchip CAP1298 18 | NULL, 19 | }; 20 | 21 | // there are temp sensors on pressure, CO2, and humidity sensors 22 | const env_sensor_api_t *i2c_temperature[] = { 23 | &temperature_dps310, // Infineon DPS310 (pressure) 24 | &temperature_lps33hwtr, // STMicro LPS33HW (pressure) 25 | &temperature_mpl3115a2, // NXP MPL3115A2 (pressure) 26 | &temperature_scd40, // Sensirion SCD40 (CO2) 27 | &temperature_sht30, // Sensirion SHT30 (humidity) 28 | &temperature_shtc3, // Sensirion SHTC3 (humidity) 29 | &temperature_th02, // Seeed (?) TH02 (humidity) 30 | NULL, 31 | }; 32 | 33 | const env_sensor_api_t *i2c_pressure[] = { 34 | &pressure_dps310, // Infineon DPS310 35 | &pressure_lps33hwtr, // STMicro LPS33HW 36 | &pressure_mpl3115a2, // NXP MPL3115A2 37 | NULL, 38 | }; 39 | 40 | const env_sensor_api_t *i2c_humidity[] = { 41 | &humidity_scd40, 42 | &humidity_sht30, // Sensirion SHT30 43 | &humidity_shtc3, // Sensirion SHTC3 44 | &humidity_th02, // Seeed (?) TH02 45 | NULL, 46 | }; 47 | 48 | const env_sensor_api_t *i2c_co2[] = { 49 | &co2_scd40, // Sensirion SCD40 50 | &eco2_sgp30, // Sensirion SGP30 (eCO2) 51 | NULL, 52 | }; 53 | 54 | const env_sensor_api_t *i2c_tvoc[] = { 55 | &tvoc_sgp30, // Sensirion SGP30 (eCO2) 56 | NULL, 57 | }; 58 | 59 | const env_sensor_api_t *i2c_illuminance[] = { 60 | &illuminance_ltr390uv, // LITE-ON LTR-390UV-01 61 | NULL, 62 | }; 63 | 64 | const env_sensor_api_t *i2c_uvindex[] = { 65 | &uvindex_ltr390uv, // LITE-ON LTR-390UV-01 66 | NULL, 67 | }; 68 | 69 | int jd_scan_i2c(const char *label, const sensor_api_t **apis, void (*init)(const sensor_api_t *)) { 70 | int num = 0; 71 | if (i2c_init() == 0) { 72 | unsigned i; 73 | for (i = 0; apis[i] != NULL; ++i) { 74 | if (apis[i]->is_present()) { 75 | DMESG("I2C found '%s' %s", apis[i]->name, label); 76 | init(apis[i]); 77 | num++; 78 | } 79 | } 80 | // DMESG("I2C scanned %d %s, found %d", i, label, num); 81 | } 82 | return num; 83 | } 84 | 85 | int jd_scan_accelerometers(void) { 86 | return jd_scan_i2c("accel", i2c_accelerometers, accelerometer_init); 87 | } 88 | 89 | int jd_scan_gyroscopes(void) { 90 | return jd_scan_i2c("gyro", i2c_gyro, gyroscope_init); 91 | } 92 | 93 | int jd_scan_temperature(void) { 94 | return jd_scan_i2c("temp", i2c_temperature, temperature_init); 95 | } 96 | 97 | int jd_scan_pressure(void) { 98 | return jd_scan_i2c("pressure", i2c_pressure, barometer_init); 99 | } 100 | 101 | int jd_scan_humidity(void) { 102 | return jd_scan_i2c("humidity", i2c_humidity, humidity_init); 103 | } 104 | 105 | int jd_scan_co2(void) { 106 | return jd_scan_i2c("co2", i2c_co2, eco2_init); 107 | } 108 | 109 | int jd_scan_tvoc(void) { 110 | return jd_scan_i2c("tvoc", i2c_tvoc, tvoc_init); 111 | } 112 | 113 | int jd_scan_uvindex(void) { 114 | return jd_scan_i2c("uvi", i2c_uvindex, uvindex_init); 115 | } 116 | 117 | int jd_scan_illuminance(void) { 118 | return jd_scan_i2c("illum", i2c_illuminance, illuminance_init); 119 | } 120 | 121 | int jd_scan_all(void) { 122 | #if JD_DCFG 123 | if (!dcfg_get_bool("scanI2C")) 124 | return 0; 125 | #endif 126 | int r = 0; 127 | DMESG("start I2C scan"); 128 | r += jd_scan_accelerometers(); 129 | r += jd_scan_gyroscopes(); 130 | r += jd_scan_temperature(); 131 | r += jd_scan_humidity(); 132 | r += jd_scan_pressure(); 133 | r += jd_scan_co2(); 134 | r += jd_scan_tvoc(); 135 | r += jd_scan_uvindex(); 136 | r += jd_scan_illuminance(); 137 | DMESG("stop I2C scan; %d device(s)", r); 138 | return r; 139 | } 140 | -------------------------------------------------------------------------------- /drivers/kx023.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | 3 | #ifndef ACC_I2C_ADDR 4 | // alt address 0x1E 5 | #define ACC_I2C_ADDR 0x1F 6 | #endif 7 | 8 | #define WHO_AM_I 0x0F 9 | #define CNTL1 0x18 10 | #define ODCNTL 0x1B 11 | #define CTRL_REG2 0x1D 12 | #define INC1 0x1C 13 | #define INC4 0x1F 14 | 15 | #ifndef ACC_RANGE 16 | #define ACC_RANGE 8 17 | #endif 18 | 19 | #define RANGE_2G (0b00 << 3) 20 | #define RANGE_4G (0b01 << 3) 21 | #define RANGE_8G (0b10 << 3) 22 | 23 | #if ACC_RANGE == 8 24 | #define RANGE RANGE_8G 25 | #define ACC_SHIFT 8 26 | #else 27 | #error "untested range" 28 | #endif 29 | 30 | #ifdef PIN_ACC_CS 31 | #define CS_SET(v) pin_set(PIN_ACC_CS, v) 32 | #else 33 | #define CS_SET(v) ((void)0) 34 | #endif 35 | 36 | static uint8_t acc_addr = ACC_I2C_ADDR; 37 | 38 | static void writeReg(uint8_t reg, uint8_t val) { 39 | #ifdef ACC_SPI 40 | CS_SET(0); 41 | uint8_t buf[] = {reg, val}; 42 | bspi_send(buf, 2); 43 | CS_SET(1); 44 | #else 45 | i2c_write_reg(acc_addr, reg, val); 46 | #endif 47 | } 48 | 49 | static void readData(uint8_t reg, uint8_t *dst, int len) { 50 | #ifdef ACC_SPI 51 | CS_SET(0); 52 | reg |= 0x80; 53 | bspi_send(®, 1); 54 | bspi_recv(dst, len); 55 | CS_SET(1); 56 | #else 57 | i2c_read_reg_buf(acc_addr, reg, dst, len); 58 | #endif 59 | } 60 | 61 | static int readReg(uint8_t reg) { 62 | uint8_t r = 0; 63 | readData(reg, &r, 1); 64 | return r; 65 | } 66 | 67 | static void init_chip(void) { 68 | writeReg(CNTL1, 0); // disable everything 69 | 70 | #ifdef ACC_100HZ 71 | writeReg(ODCNTL, 0b0011); 72 | #else 73 | writeReg(ODCNTL, 0b0010); 74 | #endif 75 | 76 | uint8_t cntl1 = 0b11000000 | RANGE; 77 | 78 | #ifdef PIN_ACC_INT 79 | writeReg(INC1, 0b00111000); 80 | writeReg(INC4, 0b00010000); 81 | cntl1 |= 0b00100000; 82 | #endif 83 | 84 | writeReg(CNTL1, cntl1); // write CNTL1 last as it enables chip 85 | } 86 | 87 | static void *kx023_get_sample(void) { 88 | static int32_t sample[3]; 89 | int16_t data[3]; 90 | readData(0x06, (uint8_t *)data, 6); 91 | sample[0] = data[1] << ACC_SHIFT; 92 | sample[1] = -data[0] << ACC_SHIFT; 93 | sample[2] = -data[2] << ACC_SHIFT; 94 | return sample; 95 | } 96 | 97 | static void kx023_sleep(void) { 98 | writeReg(CNTL1, 0x00); 99 | } 100 | 101 | static bool kx023_is_present(void) { 102 | for (int i = 0x1E; i <= 0x1F; ++i) { 103 | int v = i2c_read_reg(i, WHO_AM_I); 104 | if (v == 0x15) { 105 | acc_addr = i; 106 | return 1; 107 | } 108 | } 109 | return 0; 110 | } 111 | 112 | static void kx023_init(void) { 113 | #ifdef ACC_SPI 114 | bspi_init(); 115 | #else 116 | i2c_init(); 117 | #endif 118 | 119 | int v = readReg(WHO_AM_I); 120 | DMESG("KX023 acc id: %x", v); 121 | 122 | if (v == 0x15) { 123 | // OK 124 | } else { 125 | DMESG("invalid chip"); 126 | JD_PANIC(); 127 | } 128 | 129 | init_chip(); 130 | } 131 | 132 | const accelerometer_api_t accelerometer_kx023 = { 133 | .name = "kx023", 134 | .init = kx023_init, 135 | .get_reading = kx023_get_sample, 136 | .sleep = kx023_sleep, 137 | .is_present = kx023_is_present, 138 | }; 139 | -------------------------------------------------------------------------------- /drivers/kxtj3.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | 3 | #ifndef ACC_I2C_ADDR 4 | #define ACC_I2C_ADDR 0x0E 5 | #endif 6 | 7 | #define WHO_AM_I 0x0F 8 | #define CTRL_REG1 0x1B 9 | #define CTRL_REG2 0x1D 10 | #define INT_CTRL_REG1 0x1E 11 | #define INT_CTRL_REG2 0x1F 12 | #define DATA_CTRL_REG 0x21 13 | 14 | #define ACCEL_RANGE(dps, cfg, scale) \ 15 | { dps * 1024 * 1024, cfg, scale } 16 | 17 | static uint8_t acc_addr = ACC_I2C_ADDR; 18 | 19 | static const sensor_range_t accel_ranges[] = { // 20 | ACCEL_RANGE(2, (0b000 << 2), 6), 21 | ACCEL_RANGE(4, (0b010 << 2), 7), 22 | ACCEL_RANGE(8, (0b100 << 2), 8), 23 | ACCEL_RANGE(16, (0b001 << 2), 9), 24 | {0, 0, 0}}; 25 | 26 | static const sensor_range_t *r_accel; 27 | 28 | static void writeReg(uint8_t reg, uint8_t val) { 29 | i2c_write_reg(acc_addr, reg, val); 30 | } 31 | 32 | static void readData(uint8_t reg, uint8_t *dst, int len) { 33 | i2c_read_reg_buf(acc_addr, reg, dst, len); 34 | } 35 | 36 | static int readReg(uint8_t reg) { 37 | uint8_t r = 0; 38 | readData(reg, &r, 1); 39 | return r; 40 | } 41 | 42 | static bool kxtj3_is_present(void) { 43 | for (int i = 0x0E; i <= 0x0F; ++i) { 44 | int v = i2c_read_reg(i, WHO_AM_I); 45 | if (v == 0x35) { 46 | acc_addr = i; 47 | return 1; 48 | } 49 | } 50 | return 0; 51 | } 52 | 53 | static void init_chip(void) { 54 | writeReg(CTRL_REG1, 0); // disable everything 55 | 56 | #ifdef ACC_100HZ 57 | writeReg(DATA_CTRL_REG, 0b0011); 58 | #else 59 | writeReg(DATA_CTRL_REG, 0b0010); 60 | #endif 61 | 62 | #ifdef PIN_ACC_INT 63 | writeReg(INT_CTRL_REG1, 0b00111000); 64 | #endif 65 | 66 | writeReg(CTRL_REG1, 0b11100000 | r_accel->config); // write ctrl_reg1 last as it enables chip 67 | } 68 | 69 | static int32_t kxtj3_accel_get_range(void) { 70 | return r_accel->range; 71 | } 72 | 73 | static int32_t kxtj3_accel_set_range(int32_t range) { 74 | r_accel = sensor_lookup_range(accel_ranges, range); 75 | init_chip(); 76 | return r_accel->range; 77 | } 78 | 79 | static void *kxtj3_get_sample(void) { 80 | int16_t data[3]; 81 | static int32_t sample[3]; 82 | readData(0x06, (uint8_t *)data, 6); 83 | int shift = r_accel->scale; 84 | sample[0] = data[1] << shift; 85 | sample[1] = -data[0] << shift; 86 | sample[2] = -data[2] << shift; 87 | return sample; 88 | } 89 | 90 | static void kxtj3_sleep(void) { 91 | writeReg(CTRL_REG1, 0x00); 92 | } 93 | 94 | static void kxtj3_init(void) { 95 | i2c_init(); 96 | 97 | int v = readReg(WHO_AM_I); 98 | DMESG("KXTJ3 acc id: %x", v); 99 | 100 | if (v == 0x35) { 101 | // OK 102 | } else { 103 | DMESG("invalid chip"); 104 | JD_PANIC(); 105 | } 106 | 107 | kxtj3_accel_set_range(8 << 20); 108 | } 109 | 110 | const accelerometer_api_t accelerometer_kxtj3 = { 111 | .name = "kxtj3", 112 | .init = kxtj3_init, 113 | .get_reading = kxtj3_get_sample, 114 | .sleep = kxtj3_sleep, 115 | .get_range = kxtj3_accel_get_range, 116 | .set_range = kxtj3_accel_set_range, 117 | .ranges = accel_ranges, 118 | .is_present = kxtj3_is_present, 119 | }; 120 | -------------------------------------------------------------------------------- /drivers/max31855.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | 3 | #ifdef PIN_SCS 4 | 5 | #define SAMPLING_MS 500 6 | 7 | typedef struct state { 8 | uint8_t inited; 9 | env_reading_t temperature; 10 | uint32_t nextsample; 11 | } ctx_t; 12 | static ctx_t state; 13 | 14 | static const int32_t temperature_error[] = {ERR_TEMP(-200, 2), ERR_TEMP(700, 2), ERR_TEMP(701, 4), 15 | ERR_TEMP(1350, 4), ERR_END}; 16 | 17 | static void max31855_init(void) { 18 | ctx_t *ctx = &state; 19 | if (ctx->inited) 20 | return; 21 | 22 | ctx->temperature.min_value = SCALE_TEMP(-200); 23 | ctx->temperature.max_value = SCALE_TEMP(1350); 24 | 25 | ctx->nextsample = now; 26 | 27 | pin_set(PIN_SCS, 1); 28 | pin_setup_output(PIN_SCS); 29 | 30 | sspi_init(1, 1, 1); 31 | 32 | ctx->inited = 1; 33 | } 34 | 35 | static void max31855_process(void) { 36 | ctx_t *ctx = &state; 37 | 38 | if (jd_should_sample(&ctx->nextsample, SAMPLING_MS * 1000)) { 39 | uint8_t data[4]; 40 | 41 | pin_set(PIN_SCS, 0); 42 | sspi_rx(data, sizeof(data)); 43 | pin_set(PIN_SCS, 1); 44 | 45 | int16_t rtemp = (data[0] << 8) | data[1]; 46 | if (rtemp & 1) { 47 | DMESG("fault! f=%d", data[3] & 7); 48 | JD_PANIC(); 49 | } 50 | 51 | rtemp &= ~3; 52 | int32_t temp = rtemp << 6; 53 | 54 | env_set_value(&ctx->temperature, temp, temperature_error); 55 | ctx->inited = 2; 56 | } 57 | } 58 | 59 | static void *max31855_temperature(void) { 60 | ctx_t *ctx = &state; 61 | if (ctx->inited >= 2) 62 | return &ctx->temperature; 63 | return NULL; 64 | } 65 | 66 | const env_sensor_api_t temperature_max31855 = { 67 | .init = max31855_init, 68 | .process = max31855_process, 69 | .get_reading = max31855_temperature, 70 | }; 71 | 72 | #endif -------------------------------------------------------------------------------- /drivers/max6675.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | 3 | #ifdef PIN_SCS 4 | 5 | #define SAMPLING_MS 500 6 | 7 | typedef struct state { 8 | uint8_t inited; 9 | env_reading_t temperature; 10 | uint32_t nextsample; 11 | } ctx_t; 12 | static ctx_t state; 13 | 14 | static const int32_t temperature_error[] = {ERR_TEMP(0, 2), ERR_TEMP(700, 2), ERR_TEMP(701, 4.25), 15 | ERR_TEMP(1024, 4.25), ERR_END}; 16 | 17 | static void max6675_init(void) { 18 | ctx_t *ctx = &state; 19 | if (ctx->inited) 20 | return; 21 | 22 | ctx->temperature.min_value = SCALE_TEMP(0); 23 | ctx->temperature.max_value = SCALE_TEMP(1024); 24 | 25 | ctx->nextsample = now; 26 | 27 | pin_set(PIN_SCS, 1); 28 | pin_setup_output(PIN_SCS); 29 | 30 | sspi_init(1, 1, 1); 31 | 32 | ctx->inited = 1; 33 | } 34 | 35 | static void max6675_process(void) { 36 | ctx_t *ctx = &state; 37 | 38 | if (jd_should_sample(&ctx->nextsample, SAMPLING_MS * 1000)) { 39 | uint8_t data[2]; 40 | 41 | pin_set(PIN_SCS, 0); 42 | sspi_rx(data, sizeof(data)); 43 | pin_set(PIN_SCS, 1); 44 | 45 | uint16_t rtemp = (data[0] << 8) | data[1]; 46 | if (rtemp & 4) { 47 | DMESG("fault! 0x%x", rtemp); 48 | JD_PANIC(); 49 | } 50 | 51 | rtemp &= ~7; 52 | int32_t temp = rtemp << 5; 53 | 54 | env_set_value(&ctx->temperature, temp, temperature_error); 55 | ctx->inited = 2; 56 | } 57 | } 58 | 59 | static void *max6675_temperature(void) { 60 | ctx_t *ctx = &state; 61 | if (ctx->inited >= 2) 62 | return &ctx->temperature; 63 | return NULL; 64 | } 65 | 66 | const env_sensor_api_t temperature_max6675 = { 67 | .init = max6675_init, 68 | .process = max6675_process, 69 | .get_reading = max6675_temperature, 70 | }; 71 | 72 | #endif -------------------------------------------------------------------------------- /drivers/mcp41010.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | 3 | #ifdef PIN_CS 4 | #include "services/jd_services.h" 5 | 6 | #define MCP_41010_WRITE_DATA 0x10 7 | #define MCP_41010_SHUTDOWN 0x20 8 | #define MCP_41010_POT_CHAN_SEL_0 0x01 9 | #define MCP_41010_POT_CHAN_SEL_1 0x02 10 | 11 | static void spi_tx(uint8_t *data, uint32_t len) { 12 | pin_set(PIN_CS, 0); 13 | sspi_tx(data, len); 14 | pin_set(PIN_CS, 1); 15 | } 16 | 17 | static void mcp41010_set_wiper(uint8_t channel, uint32_t wiper_value) { 18 | // mcp41010 only has two channels. 19 | JD_ASSERT(channel < 2); 20 | 21 | uint8_t data[] = {0, 0}; 22 | // DMESG("SET WIPER[%d]: %d", channel, wiper_value); 23 | 24 | data[0] = MCP_41010_WRITE_DATA | (1 << channel); 25 | data[1] = wiper_value; 26 | 27 | spi_tx(data, 2); 28 | } 29 | 30 | static void mcp41010_shutdown(uint8_t channel) { 31 | // mcp41010 only has two channels. 32 | JD_ASSERT(channel < 2); 33 | 34 | uint8_t data[] = {0, 0}; 35 | 36 | data[0] = MCP_41010_SHUTDOWN | (1 << channel); 37 | data[1] = 0; // data is do not care 38 | 39 | spi_tx(data, 2); 40 | } 41 | 42 | static void mcp41010_init(void) { 43 | sspi_init(true, 0, 0); 44 | pin_setup_output(PIN_CS); 45 | pin_set(PIN_CS, 1); 46 | } 47 | 48 | const dig_pot_api_t mcp41010 = { 49 | .init = mcp41010_init, .set_wiper = mcp41010_set_wiper, .shutdown = mcp41010_shutdown}; 50 | #endif -------------------------------------------------------------------------------- /drivers/ncv7726b_daisy.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | #include "services/jd_services.h" 3 | 4 | #define NUM_CHIPS 3 // TODO from JD_CONFIG 5 | 6 | #define CH_PER_CHIP 12 7 | #define NUM_CHANNELS (CH_PER_CHIP * NUM_CHIPS) 8 | 9 | #if defined(PIN_SCS) 10 | 11 | // #define DBG DMESG 12 | #define DBG(...) ((void)0) 13 | 14 | #define SRR (1 << 15) 15 | #define HB_SEL (1 << 14) 16 | #define ULDSC (1 << 13) 17 | #define OVLO (1 << 0) 18 | 19 | static uint16_t channel_en[NUM_CHIPS]; 20 | static uint16_t channel_hs[NUM_CHIPS]; 21 | 22 | static inline uint16_t flip(uint16_t v) { 23 | return ((v & 0xff00) >> 8) | ((v & 0x00ff) << 8); 24 | } 25 | 26 | static void ncv7726b_write_state(void) { 27 | uint16_t base = SRR; 28 | 29 | for (int shift = 0; shift <= 6; shift += 6) { 30 | pin_set(PIN_SCS, 0); 31 | target_wait_us(10); 32 | for (int no = NUM_CHIPS - 1; no >= 0; no--) { 33 | uint16_t en = (channel_en[no] >> shift) & 0x3f; 34 | uint16_t hs = (channel_hs[no] >> shift) & 0x3f; 35 | uint16_t cmd = base | (en << 7) | (hs << 1); 36 | cmd = flip(cmd); 37 | sspi_tx(&cmd, 2); 38 | } 39 | pin_set(PIN_SCS, 1); 40 | 41 | base |= HB_SEL; 42 | } 43 | } 44 | 45 | static int chip_no(uint8_t ch, uint16_t *mask) { 46 | JD_ASSERT(ch < NUM_CHANNELS); 47 | 48 | for (int i = 0; i < NUM_CHIPS; ++i) { 49 | if (ch < CH_PER_CHIP) { 50 | *mask = 1 << ch; 51 | return i; 52 | } 53 | ch -= CH_PER_CHIP; 54 | } 55 | 56 | JD_PANIC(); 57 | } 58 | 59 | static void ncv7726b_channel_set(uint8_t channel, int state) { 60 | uint16_t mask; 61 | int no = chip_no(channel, &mask); 62 | channel_en[no] |= mask; 63 | if (state) { 64 | channel_hs[no] |= mask; 65 | } else { 66 | channel_hs[no] &= ~mask; 67 | } 68 | } 69 | 70 | static void ncv7726b_channel_clear(uint8_t ch) { 71 | uint16_t mask; 72 | int no = chip_no(ch, &mask); 73 | channel_en[no] &= ~mask; 74 | } 75 | 76 | static void ncv7726b_clear_all(void) { 77 | memset(&channel_en, 0, sizeof(channel_en)); 78 | memset(&channel_hs, 0, sizeof(channel_hs)); // for clarity of trace 79 | } 80 | 81 | static void ncv7726b_init(void) { 82 | sspi_init(1, 0, 1); 83 | pin_set(PIN_SCS, 1); 84 | pin_setup_output(PIN_SCS); 85 | } 86 | 87 | const hbridge_api_t ncv7726b_daisy = {.init = ncv7726b_init, 88 | .channel_clear = ncv7726b_channel_clear, 89 | .channel_set = ncv7726b_channel_set, 90 | .clear_all = ncv7726b_clear_all, 91 | .write_channels = ncv7726b_write_state}; 92 | 93 | #endif -------------------------------------------------------------------------------- /drivers/sht30.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | 3 | #define SAMPLING_MS 500 4 | #define PRECISION 10 5 | 6 | #ifndef SHT30_ADDR 7 | #define SHT30_ADDR 0x44 8 | #endif 9 | 10 | static uint8_t sht30_addr = SHT30_ADDR; 11 | 12 | #define SHT30_MEASURE_HIGH_REP 0x2400 // no clock stretching 13 | #define SHT30_SOFTRESET 0x30A2 14 | #define SHT30_STATUS 0xF32D 15 | 16 | typedef struct state { 17 | uint8_t inited; 18 | uint8_t read_issued; 19 | env_reading_t humidity; 20 | env_reading_t temperature; 21 | uint32_t nextsample; 22 | } ctx_t; 23 | static ctx_t state; 24 | 25 | static const int32_t humidity_error[] = {ERR_HUM(0, 4), ERR_HUM(20, 2), ERR_HUM(80, 2), 26 | ERR_HUM(100, 4), ERR_END}; 27 | 28 | static const int32_t temperature_error[] = {ERR_TEMP(-40, 0.6), ERR_TEMP(0, 0.2), ERR_TEMP(65, 0.2), 29 | ERR_TEMP(125, 0.6), ERR_END}; 30 | 31 | static void send_cmd(uint16_t cmd) { 32 | if (i2c_write_reg16_buf(sht30_addr, cmd, NULL, 0)) 33 | JD_PANIC(); 34 | } 35 | 36 | static void wake(void) { 37 | // nothing to do on this chip 38 | } 39 | 40 | static bool sht30_is_present(void) { 41 | for (int i = 0x44; i <= 0x45; ++i) { 42 | if (jd_sgp_read_u16(i, SHT30_STATUS, 0) > 0) { 43 | sht30_addr = i; 44 | return 1; 45 | } 46 | } 47 | return 0; 48 | } 49 | 50 | static void sht30_init(void) { 51 | ctx_t *ctx = &state; 52 | if (ctx->inited) 53 | return; 54 | 55 | ctx->humidity.min_value = SCALE_HUM(0); 56 | ctx->humidity.max_value = SCALE_HUM(100); 57 | ctx->temperature.min_value = SCALE_TEMP(-40); 58 | ctx->temperature.max_value = SCALE_TEMP(125); 59 | 60 | ctx->nextsample = now; 61 | 62 | ctx->inited = 1; 63 | i2c_init(); 64 | // DMESG("SHT30 pres %d", sht30_is_present()); 65 | wake(); 66 | int id = i2c_read_reg16(sht30_addr, SHT30_STATUS); 67 | DMESG("SHT30 status=%x", id); 68 | if (id < 0) 69 | JD_PANIC(); 70 | } 71 | 72 | static void sht30_process(void) { 73 | ctx_t *ctx = &state; 74 | 75 | // the 20ms here is just for readings, we actually sample at SAMPLING_MS 76 | // the datasheet says max reading time is 15.5ms; give a little more time 77 | if (jd_should_sample_delay(&ctx->nextsample, 20000)) { 78 | if (!ctx->read_issued) { 79 | ctx->read_issued = 1; 80 | wake(); 81 | send_cmd(SHT30_MEASURE_HIGH_REP); 82 | } else { 83 | uint8_t data[6]; 84 | if (i2c_read_ex(sht30_addr, data, sizeof(data))) 85 | JD_PANIC(); 86 | uint16_t temp = (data[0] << 8) | data[1]; 87 | uint16_t hum = (data[3] << 8) | data[4]; 88 | ctx->read_issued = 0; 89 | ctx->nextsample = now + SAMPLING_MS * 1000; 90 | env_set_value(&ctx->humidity, 100 * hum >> 6, humidity_error); 91 | env_set_value(&ctx->temperature, (175 * temp >> 6) - (45 << 10), temperature_error); 92 | ctx->inited = 2; 93 | } 94 | } 95 | } 96 | 97 | static void *sht30_temperature(void) { 98 | ctx_t *ctx = &state; 99 | if (ctx->inited >= 2) 100 | return &ctx->temperature; 101 | return NULL; 102 | } 103 | 104 | static void *sht30_humidity(void) { 105 | ctx_t *ctx = &state; 106 | if (ctx->inited >= 2) 107 | return &ctx->humidity; 108 | return NULL; 109 | } 110 | 111 | const env_sensor_api_t temperature_sht30 = { 112 | .name = "sht30", 113 | .init = sht30_init, 114 | .process = sht30_process, 115 | .get_reading = sht30_temperature, 116 | .is_present = sht30_is_present, 117 | }; 118 | 119 | const env_sensor_api_t humidity_sht30 = { 120 | .name = "sht30", 121 | .init = sht30_init, 122 | .process = sht30_process, 123 | .get_reading = sht30_humidity, 124 | .is_present = sht30_is_present, 125 | }; 126 | -------------------------------------------------------------------------------- /drivers/shtc3.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | 3 | #define SAMPLING_MS 500 4 | #define PRECISION 10 5 | 6 | #ifndef SHTC3_ADDR 7 | #define SHTC3_ADDR 0x70 8 | #endif 9 | 10 | #define SHTC3_MEASURE_NORMAL 0x7866 11 | #define SHTC3_MEASURE_LOW_POWER 0x609C 12 | 13 | #define SHTC3_SOFTRESET 0x805D 14 | #define SHTC3_ID 0xEFC8 15 | #define SHTC3_SLEEP 0xB098 16 | #define SHTC3_WAKEUP 0x3517 17 | 18 | typedef struct state { 19 | uint8_t inited; 20 | uint8_t read_issued; 21 | env_reading_t humidity; 22 | env_reading_t temperature; 23 | uint32_t nextsample; 24 | } ctx_t; 25 | static ctx_t state; 26 | 27 | static const int32_t humidity_error[] = {ERR_HUM(0, 3.5), ERR_HUM(20, 2), ERR_HUM(80, 2), 28 | ERR_HUM(100, 3.5), ERR_END}; 29 | 30 | static const int32_t temperature_error[] = {ERR_TEMP(-40, 0.8), ERR_TEMP(5, 0.2), ERR_TEMP(60, 0.2), 31 | ERR_TEMP(125, 0.8), ERR_END}; 32 | 33 | static void send_cmd(uint16_t cmd) { 34 | if (i2c_write_reg16_buf(SHTC3_ADDR, cmd, NULL, 0)) 35 | JD_PANIC(); 36 | } 37 | 38 | static void wake(void) { 39 | send_cmd(SHTC3_WAKEUP); 40 | target_wait_us(300); // 200us seems minimum; play it safe with 300us 41 | } 42 | 43 | static bool shtc3_is_present(void) { 44 | if (i2c_write_reg16_buf(SHTC3_ADDR, SHTC3_WAKEUP, NULL, 0) != 0) 45 | return 0; 46 | target_wait_us(300); 47 | // addr fixed to 0x70 48 | return (jd_sgp_read_u16(SHTC3_ADDR, SHTC3_ID, 0) & 0x083f) == 0x0807; 49 | } 50 | 51 | static void shtc3_init(void) { 52 | ctx_t *ctx = &state; 53 | if (ctx->inited) 54 | return; 55 | 56 | ctx->humidity.min_value = SCALE_HUM(0); 57 | ctx->humidity.max_value = SCALE_HUM(100); 58 | ctx->temperature.min_value = SCALE_TEMP(-40); 59 | ctx->temperature.max_value = SCALE_TEMP(125); 60 | 61 | ctx->nextsample = now; 62 | 63 | ctx->inited = 1; 64 | i2c_init(); 65 | shtc3_is_present(); 66 | wake(); 67 | DMESG("SHTC3 id=%x", jd_sgp_read_u16(SHTC3_ADDR, SHTC3_ID, 0)); 68 | send_cmd(SHTC3_SLEEP); 69 | } 70 | 71 | static void shtc3_process(void) { 72 | ctx_t *ctx = &state; 73 | 74 | // the 20ms here is just for readings, we actually sample at SAMPLING_MS 75 | // the datasheet says max reading time is 12.1ms; give a little more time 76 | if (jd_should_sample_delay(&ctx->nextsample, 20000)) { 77 | if (!ctx->read_issued) { 78 | ctx->read_issued = 1; 79 | wake(); 80 | send_cmd(SHTC3_MEASURE_NORMAL); 81 | } else { 82 | uint8_t data[6]; 83 | if (i2c_read_ex(SHTC3_ADDR, data, sizeof(data))) 84 | JD_PANIC(); 85 | uint16_t temp = (data[0] << 8) | data[1]; 86 | uint16_t hum = (data[3] << 8) | data[4]; 87 | send_cmd(SHTC3_SLEEP); 88 | ctx->read_issued = 0; 89 | ctx->nextsample = now + SAMPLING_MS * 1000; 90 | env_set_value(&ctx->humidity, 100 * hum >> 6, humidity_error); 91 | env_set_value(&ctx->temperature, (175 * temp >> 6) - (45 << 10), temperature_error); 92 | ctx->inited = 2; 93 | } 94 | } 95 | } 96 | 97 | static void *shtc3_temperature(void) { 98 | ctx_t *ctx = &state; 99 | if (ctx->inited >= 2) 100 | return &ctx->temperature; 101 | return NULL; 102 | } 103 | 104 | static void *shtc3_humidity(void) { 105 | ctx_t *ctx = &state; 106 | if (ctx->inited >= 2) 107 | return &ctx->humidity; 108 | return NULL; 109 | } 110 | 111 | const env_sensor_api_t temperature_shtc3 = { 112 | .name = "shtc3", 113 | .init = shtc3_init, 114 | .process = shtc3_process, 115 | .get_reading = shtc3_temperature, 116 | .is_present = shtc3_is_present, 117 | }; 118 | 119 | const env_sensor_api_t humidity_shtc3 = { 120 | .name = "shtc3", 121 | .init = shtc3_init, 122 | .process = shtc3_process, 123 | .get_reading = shtc3_humidity, 124 | .is_present = shtc3_is_present, 125 | }; 126 | -------------------------------------------------------------------------------- /drivers/th02.c: -------------------------------------------------------------------------------- 1 | #include "jd_drivers.h" 2 | 3 | #define SAMPLING_MS 500 4 | #define PRECISION 10 5 | 6 | #ifndef TH02_ADDR 7 | #define TH02_ADDR 0x40 8 | #endif 9 | 10 | #define TH02_CFG_FAST (1 << 5) 11 | #define TH02_CFG_TEMP (1 << 4) 12 | #define TH02_CFG_HEAT (1 << 1) 13 | #define TH02_CFG_START (1 << 0) 14 | 15 | #define TH02_STATUS 0x00 16 | #define TH02_DATA_H 0x01 17 | #define TH02_DATA_L 0x02 18 | #define TH02_CONFIG 0x03 19 | #define TH02_ID 0x11 20 | 21 | typedef struct state { 22 | uint8_t inited; 23 | uint8_t in_temp; 24 | uint8_t in_humidity; 25 | env_reading_t humidity; 26 | env_reading_t temperature; 27 | uint32_t nextsample; 28 | } ctx_t; 29 | static ctx_t state; 30 | 31 | static const int32_t humidity_error[] = {ERR_HUM(0, 3), ERR_HUM(80, 3), ERR_HUM(100, 5.5), ERR_END}; 32 | 33 | static const int32_t temperature_error[] = {ERR_TEMP(-40, 3), ERR_TEMP(0, 0.5), ERR_TEMP(70, 0.5), 34 | ERR_TEMP(125, 1.2), ERR_END}; 35 | 36 | static int read_data(void) { 37 | uint8_t data[3]; 38 | if (i2c_read_reg_buf(TH02_ADDR, TH02_STATUS, data, 3) < 0) 39 | return -1; 40 | if (data[0] & 1) { 41 | // DMESG("miss"); 42 | return -1; 43 | } 44 | return (data[1] << 8) | data[2]; 45 | } 46 | 47 | static bool th02_is_present(void) { 48 | // addr is always 0x40; ID is 0x50 49 | return i2c_read_reg(TH02_ADDR, TH02_ID) == 0x50; 50 | } 51 | 52 | static void th02_init(void) { 53 | ctx_t *ctx = &state; 54 | if (ctx->inited) 55 | return; 56 | ctx->inited = 1; 57 | i2c_init(); 58 | int id = i2c_read_reg(TH02_ADDR, TH02_ID); 59 | DMESG("TH02 id=%x", id); 60 | if (id < 0) 61 | JD_PANIC(); 62 | ctx->temperature.min_value = SCALE_TEMP(-40); 63 | ctx->temperature.max_value = SCALE_TEMP(70); 64 | ctx->humidity.min_value = SCALE_TEMP(0); 65 | ctx->humidity.max_value = SCALE_TEMP(100); 66 | } 67 | 68 | static void th02_process(void) { 69 | ctx_t *ctx = &state; 70 | 71 | // the 50ms here is just for readings, we actually sample at SAMPLING_MS 72 | if (jd_should_sample_delay(&ctx->nextsample, 50000)) { 73 | if (ctx->in_temp) { 74 | int v = read_data(); 75 | if (v >= 0) { 76 | ctx->in_temp = 0; 77 | env_set_value(&ctx->temperature, ((v << PRECISION) >> 7) - (50 << PRECISION), 78 | temperature_error); 79 | ctx->in_humidity = 1; 80 | i2c_write_reg(TH02_ADDR, TH02_CONFIG, TH02_CFG_START); 81 | } 82 | } else if (ctx->in_humidity) { 83 | int v = read_data(); 84 | if (v >= 0) { 85 | ctx->in_humidity = 0; 86 | env_set_value(&ctx->humidity, ((v << PRECISION) >> 8) - (24 << PRECISION), 87 | humidity_error); 88 | ctx->nextsample = now + SAMPLING_MS * 1000; 89 | // DMESG("t=%dC h=%d%%", ctx->temp >> PRECISION, ctx->humidity >> PRECISION); 90 | ctx->inited = 2; 91 | } 92 | } else { 93 | ctx->in_temp = 1; 94 | i2c_write_reg(TH02_ADDR, TH02_CONFIG, TH02_CFG_START | TH02_CFG_TEMP); 95 | } 96 | } 97 | } 98 | 99 | static void *th02_temperature(void) { 100 | ctx_t *ctx = &state; 101 | if (ctx->inited < 2) 102 | return NULL; 103 | return &ctx->temperature; 104 | } 105 | 106 | static void *th02_humidity(void) { 107 | ctx_t *ctx = &state; 108 | if (ctx->inited < 2) 109 | return NULL; 110 | return &ctx->humidity; 111 | } 112 | 113 | const env_sensor_api_t temperature_th02 = { 114 | .name = "th02", 115 | .init = th02_init, 116 | .process = th02_process, 117 | .get_reading = th02_temperature, 118 | .is_present = th02_is_present, 119 | }; 120 | 121 | const env_sensor_api_t humidity_th02 = { 122 | .name = "th02", 123 | .init = th02_init, 124 | .process = th02_process, 125 | .get_reading = th02_humidity, 126 | .is_present = th02_is_present, 127 | }; 128 | -------------------------------------------------------------------------------- /inc/click/drv_digital_in.h: -------------------------------------------------------------------------------- 1 | #ifndef DRV_DIGITAL_IN_H 2 | #define DRV_DIGITAL_IN_H 3 | #include "drv_name.h" 4 | 5 | typedef struct { 6 | uint8_t pin; 7 | } digital_in_t; 8 | 9 | static inline int digital_in_init(digital_in_t *in, pin_name_t name) { 10 | pin_setup_input(name, PIN_PULL_NONE); 11 | in->pin = name; 12 | return 0; 13 | } 14 | 15 | static inline uint8_t digital_in_read(digital_in_t *in) { 16 | return pin_get(in->pin); 17 | } 18 | #endif 19 | -------------------------------------------------------------------------------- /inc/click/drv_digital_out.h: -------------------------------------------------------------------------------- 1 | #ifndef DRV_DIGITAL_OUT_H 2 | #define DRV_DIGITAL_OUT_H 3 | 4 | #include "drv_name.h" 5 | 6 | typedef struct { 7 | uint8_t pin; 8 | } digital_out_t; 9 | 10 | static inline int digital_out_init(digital_out_t *out, pin_name_t name) { 11 | out->pin = name; 12 | pin_setup_output(name); 13 | return 0; 14 | } 15 | 16 | static inline void digital_out_high(digital_out_t *out) { 17 | pin_set(out->pin, 1); 18 | } 19 | 20 | static inline void digital_out_low(digital_out_t *out) { 21 | pin_set(out->pin, 0); 22 | } 23 | 24 | static inline void digital_out_toggle(digital_out_t *out) { 25 | pin_toggle(out->pin); 26 | } 27 | 28 | static inline void digital_out_write(digital_out_t *out, uint8_t value) { 29 | pin_set(out->pin, value); 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /inc/click/drv_i2c_master.h: -------------------------------------------------------------------------------- 1 | #ifndef DRV_I2C_MASTER_H 2 | #define DRV_I2C_MASTER_H 3 | 4 | #include "drv_name.h" 5 | 6 | #define I2C_MASTER_SPEED_STANDARD 100000 7 | #define I2C_MASTER_ERROR -1 8 | 9 | typedef struct { 10 | uint8_t addr; 11 | 12 | // ignored 13 | pin_name_t sda; 14 | pin_name_t scl; 15 | uint32_t speed; 16 | 17 | // todo? 18 | uint16_t timeout_pass_count; 19 | } i2c_master_config_t; 20 | 21 | typedef struct { 22 | uint8_t address; 23 | uint16_t timeout_pass_count; 24 | } i2c_master_t; 25 | 26 | static inline void i2c_master_configure_default(i2c_master_config_t *config) { 27 | config->addr = 0; 28 | // the rest is ignored or needs to be set anyways 29 | config->timeout_pass_count = 10000; 30 | } 31 | 32 | static inline int i2c_master_open(i2c_master_t *obj, i2c_master_config_t *config) { 33 | obj->address = config->addr; 34 | obj->timeout_pass_count = config->timeout_pass_count; 35 | i2c_init(); 36 | return 0; 37 | } 38 | 39 | static inline int i2c_master_set_speed(i2c_master_t *obj, uint32_t speed) { 40 | return 0; 41 | } 42 | 43 | static inline int i2c_master_set_timeout(i2c_master_t *obj, uint16_t timeout_pass_count) { 44 | obj->timeout_pass_count = timeout_pass_count; 45 | return 0; 46 | } 47 | 48 | static inline int i2c_master_set_slave_address(i2c_master_t *obj, uint8_t address) { 49 | obj->address = address; 50 | return 0; 51 | } 52 | 53 | static inline int i2c_master_write(i2c_master_t *obj, const uint8_t *buf, size_t len) { 54 | return i2c_write_ex(obj->address, buf, len, false) < 0 ? -1 : 0; 55 | } 56 | 57 | static inline int i2c_master_read(i2c_master_t *obj, uint8_t *buf, size_t len) { 58 | return i2c_read_ex(obj->address, buf, len) < 0 ? -1 : 0; 59 | } 60 | 61 | static inline int i2c_master_write_then_read(i2c_master_t *obj, const uint8_t *write_buf, 62 | size_t len_write, uint8_t *read_buf, size_t len_read) { 63 | int r = i2c_write_ex(obj->address, write_buf, len_write, true); 64 | if (r < 0) 65 | return -1; 66 | return i2c_read_ex(obj->address, read_buf, len_read) < 0 ? -1 : 0; 67 | } 68 | 69 | static inline void i2c_master_close(i2c_master_t *obj) {} 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /inc/click/drv_name.h: -------------------------------------------------------------------------------- 1 | #ifndef DRV_NAME_H 2 | #define DRV_NAME_H 3 | 4 | #include "jd_drivers.h" 5 | 6 | typedef int pin_name_t; 7 | typedef int err_t; 8 | 9 | #define HAL_PIN_NC (-1) 10 | #if MIKROBUS_AVAILABLE 11 | #define MIKROBUS_SCL PIN_SCL 12 | #define MIKROBUS_SDA PIN_SDA 13 | #define MIKROBUS_AN PIN_AN 14 | #define MIKROBUS_PWM PIN_PWM 15 | #define MIKROBUS_INT PIN_INT 16 | #define MIKROBUS_SCK PIN_SCK 17 | #define MIKROBUS_RST PIN_RST 18 | 19 | #ifdef PIN_CS 20 | #define MIKROBUS_CS PIN_CS 21 | #endif 22 | #ifdef PIN_TX 23 | #define MIKROBUS_TX PIN_TX 24 | #endif 25 | #ifdef PIN_MISO 26 | #define MIKROBUS_MISO PIN_MISO 27 | #endif 28 | #ifdef PIN_RX 29 | #define MIKROBUS_RX PIN_RX 30 | #endif 31 | #ifdef PIN_MOSI 32 | #define MIKROBUS_MOSI PIN_MOSI 33 | #endif 34 | 35 | #ifdef PIN_TX_MISO 36 | #define MIKROBUS_TX PIN_TX_MISO 37 | #define MIKROBUS_MISO PIN_TX_MISO 38 | #endif 39 | 40 | #ifdef PIN_RX_MOSI 41 | #define MIKROBUS_RX PIN_RX_MOSI 42 | #define MIKROBUS_MOSI PIN_RX_MOSI 43 | #endif 44 | 45 | #ifdef PIN_RX_CS 46 | #ifndef MIKROBUS_CS 47 | #define MIKROBUS_CS PIN_RX_CS 48 | #endif 49 | 50 | #ifndef MIKROBUS_RX 51 | #define MIKROBUS_RX PIN_RX_CS 52 | #endif 53 | #endif 54 | 55 | #define MIKROBUS(mikrobus, pin) pin 56 | 57 | #else 58 | 59 | #define MIKROBUS_SCL NO_PIN 60 | #define MIKROBUS_SDA NO_PIN 61 | #define MIKROBUS_AN NO_PIN 62 | #define MIKROBUS_PWM NO_PIN 63 | #define MIKROBUS_INT NO_PIN 64 | #define MIKROBUS_SCK NO_PIN 65 | #define MIKROBUS_RST NO_PIN 66 | 67 | #define MIKROBUS_CS NO_PIN 68 | #define MIKROBUS_TX NO_PIN 69 | #define MIKROBUS_MISO NO_PIN 70 | #define MIKROBUS_RX NO_PIN 71 | #define MIKROBUS_MOSI NO_PIN 72 | 73 | #define MIKROBUS(mikrobus, pin) pin 74 | 75 | #endif 76 | 77 | static inline void Delay_1us(void) { 78 | jd_services_sleep_us(1); 79 | } 80 | static inline void Delay_10us(void) { 81 | jd_services_sleep_us(10); 82 | } 83 | static inline void Delay_22us(void) { 84 | jd_services_sleep_us(22); 85 | } 86 | static inline void Delay_50us(void) { 87 | jd_services_sleep_us(50); 88 | } 89 | static inline void Delay_80us(void) { 90 | jd_services_sleep_us(80); 91 | } 92 | static inline void Delay_500us(void) { 93 | jd_services_sleep_us(500); 94 | } 95 | static inline void Delay_1ms(void) { 96 | jd_services_sleep_us(1000); 97 | } 98 | static inline void Delay_5ms(void) { 99 | jd_services_sleep_us(5000); 100 | } 101 | static inline void Delay_8ms(void) { 102 | jd_services_sleep_us(8000); 103 | } 104 | static inline void Delay_10ms(void) { 105 | jd_services_sleep_us(10000); 106 | } 107 | static inline void Delay_100ms(void) { 108 | jd_services_sleep_us(100000); 109 | } 110 | static inline void Delay_1sec(void) { 111 | jd_services_sleep_us(1000000); 112 | } 113 | static inline void Delay_ms(int ms) { 114 | jd_services_sleep_us(ms * 1000); 115 | } 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /inc/interfaces/README.md: -------------------------------------------------------------------------------- 1 | # Platform-specific interfaces 2 | 3 | The functions specified in headers in this folder should be provided 4 | by platform-specific implementation. 5 | There are some generic implementations available in `source/interfaces/*.c`. 6 | -------------------------------------------------------------------------------- /inc/interfaces/jd_alloc.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #ifndef __JD_ALLOC_H 5 | #define __JD_ALLOC_H 6 | 7 | #include "jd_config.h" 8 | 9 | // This file outlines the APIs for allocator implementations. 10 | // Allocator implementations are only required if an app or transmission/reception requires dynamic 11 | // allocation Implementors can map these APIs onto custom malloc/free implementations A dummy 12 | // implementation can be optionally compiled in, and specific function overriden 13 | // (implementation/dummy/alloc.c) 14 | 15 | /** 16 | * This function is invoked by a call to jd_init. 17 | **/ 18 | void jd_alloc_init(void); 19 | 20 | #if JD_DEVICESCRIPT && JD_HW_ALLOC 21 | void jd_alloc_add_chunk(void *start, unsigned size); 22 | #endif 23 | 24 | /** 25 | * Any dynamic allocations made by the jacdac-c library will use this function. 26 | * It zeroes-out the memory and never returns NULL. 27 | **/ 28 | void *jd_alloc(uint32_t size); 29 | 30 | /** 31 | * Return the amount of memory left to allocated. 32 | */ 33 | uint32_t jd_available_memory(void); 34 | 35 | /** 36 | * Any corresponding free calls made by the jacdac-c library will use this function. 37 | * 38 | * Note: this is never used. 39 | **/ 40 | void jd_free(void *ptr); 41 | 42 | void jd_alloc_stack_check(void); 43 | void *jd_alloc_emergency_area(uint32_t size); 44 | 45 | #endif -------------------------------------------------------------------------------- /inc/interfaces/jd_app.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #ifndef JD_APP_H 5 | #define JD_APP_H 6 | 7 | #include "jd_config.h" 8 | 9 | /** 10 | * This function configures firmware for a given module. 11 | * It's typically the only function that changes between modules. 12 | */ 13 | void app_init_services(void); 14 | 15 | /** 16 | * This is an optionally implementable callback invoked in main.c. 17 | * It allows apps to perform meta-level configuration detection. 18 | * This is useful for devices that may need to dynamically detect 19 | * hardware changes at runtime (e.g. XAC module). 20 | */ 21 | void app_process(void); 22 | 23 | /** 24 | * Define in application to specify instance names for services. 25 | */ 26 | const char *app_get_instance_name(int service_idx); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /inc/interfaces/jd_hid.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #ifndef JD_HID_H 5 | #define JD_HID_H 6 | 7 | #include "jd_config.h" 8 | #include "jd_physical.h" 9 | 10 | #define JD_HID_MAX_KEYCODES 6 11 | 12 | typedef struct { 13 | uint8_t modifier; 14 | uint8_t keycode[JD_HID_MAX_KEYCODES]; 15 | } jd_hid_keyboard_report_t; 16 | 17 | typedef struct { 18 | uint8_t buttons; 19 | int8_t x, y; 20 | int8_t wheel, pan; 21 | } jd_hid_mouse_report_t; 22 | 23 | typedef struct { 24 | int8_t throttle0; 25 | int8_t throttle1; 26 | int8_t x0, y0; 27 | int8_t x1, y1; 28 | uint16_t buttons; 29 | } jd_hid_gamepad_report_t; 30 | 31 | void jd_hid_keyboard_set_report(const jd_hid_keyboard_report_t *report); 32 | void jd_hid_mouse_move(const jd_hid_mouse_report_t *report); 33 | void jd_hid_gamepad_set_report(const jd_hid_gamepad_report_t *report); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /inc/interfaces/jd_lora.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #ifndef JD_LORA_H 5 | #define JD_LORA_H 6 | 7 | #include "jd_config.h" 8 | #include "jd_physical.h" 9 | 10 | #if JD_LORA 11 | void jd_lora_process(void); 12 | void jd_lora_init(void); 13 | int jd_lora_send(const void *data, uint32_t datalen); 14 | bool jd_lora_in_timer(void); 15 | #endif 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /inc/interfaces/jd_rx.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | /* 5 | * A reception queue for JD packets. 6 | */ 7 | 8 | #ifndef JD_RX_H 9 | #define JD_RX_H 10 | 11 | #include "jd_service_framework.h" 12 | 13 | void jd_rx_init(void); 14 | int jd_rx_frame_received(jd_frame_t *frame); 15 | jd_frame_t *jd_rx_get_frame(void); 16 | void jd_rx_release_frame(jd_frame_t *frame); 17 | bool jd_rx_has_frame(void); 18 | 19 | #if JD_CLIENT || JD_BRIDGE 20 | // this will not forward the frame to the USB bridge 21 | int jd_rx_frame_received_loopback(jd_frame_t *frame); 22 | #else 23 | static inline int jd_rx_frame_received_loopback(jd_frame_t *frame) { 24 | return 0; 25 | } 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /inc/interfaces/jd_tx.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | /* 5 | * A transmission queue for JD packets. 6 | */ 7 | #ifndef JD_TX_H 8 | #define JD_TX_H 9 | 10 | #include "jd_service_framework.h" 11 | 12 | void jd_tx_init(void); 13 | void jd_tx_flush(void); 14 | int jd_tx_is_idle(void); 15 | jd_frame_t *jd_tx_get_frame(void); 16 | void jd_tx_frame_sent(jd_frame_t *frame); 17 | 18 | int jd_send(unsigned service_num, unsigned service_cmd, const void *data, unsigned service_size); 19 | 20 | // wrappers around jd_send() 21 | int jd_respond_u8(jd_packet_t *pkt, uint8_t v); 22 | int jd_respond_u16(jd_packet_t *pkt, uint16_t v); 23 | int jd_respond_u32(jd_packet_t *pkt, uint32_t v); 24 | int jd_respond_empty(jd_packet_t *pkt); 25 | int jd_respond_string(jd_packet_t *pkt, const char *str); 26 | int jd_send_not_implemented(jd_packet_t *pkt); 27 | 28 | /** 29 | * If pkt contains GET or SET on the given registers, send an error response and return 1. 30 | * Otherwise return 0. 31 | */ 32 | int jd_block_register(jd_packet_t *pkt, uint16_t reg_code); 33 | 34 | void jd_send_event_ext(srv_t *srv, uint32_t eventid, const void *data, uint32_t data_bytes); 35 | static inline void jd_send_event(srv_t *srv, uint32_t eventid) { 36 | jd_send_event_ext(srv, eventid, 0, 0); 37 | } 38 | void jd_process_event_queue(void); 39 | 40 | // this is needed for pipes and clients, not regular servers 41 | // this will send the frame on the wire and on the USB bridge 42 | int jd_send_frame(jd_frame_t *f); 43 | // this will not forward to USB port - it is called from USB code 44 | int jd_send_frame_raw(jd_frame_t *f); 45 | 46 | bool jd_need_to_send(jd_frame_t *f); 47 | bool jd_tx_will_fit(unsigned size); 48 | 49 | // wrapper around jd_send_frame() 50 | int jd_send_pkt(jd_packet_t *pkt); 51 | 52 | #if JD_RAW_FRAME 53 | extern uint8_t rawFrameSending; 54 | extern jd_frame_t *rawFrame; 55 | #endif 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /inc/interfaces/jd_usb.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #ifndef JD_USB_H 5 | #define JD_USB_H 6 | 7 | #include "jd_protocol.h" 8 | 9 | #if JD_USB_BRIDGE 10 | 11 | // true if any packets are pending in the queue 12 | bool jd_usb_is_pending(void); 13 | int jd_usb_send_frame(void *frame); 14 | void jd_usb_enable_serial(void); 15 | void jd_usb_proto_process(void); 16 | 17 | void jd_usb_panic_start(void); 18 | void jd_usb_panic_print_char(char c); 19 | void jd_usb_panic_print_str(const char *s); 20 | 21 | // USB interface 22 | // Defined by USB stack, called by jd_usb when there is new data to be pulled 23 | void jd_usb_pull_ready(void); 24 | // can be implemented by USB stack 25 | bool jd_usb_looks_connected(void); 26 | // can be re-defined by the USB stack, called from jd_process_everything() 27 | void jd_usb_process(void); 28 | // can be re-defined by the USB stack, called before sending dmesg to USB 29 | void jd_usb_flush_stdout(void); 30 | // can be re-defined in the application 31 | void jd_usb_write_serial_cb(const void *data, unsigned len); 32 | 33 | // Called by the USB stack, returns number of bytes to send over USB (placed in dst[]) 34 | int jd_usb_pull(uint8_t dst[64]); 35 | // called by USB stack to process incoming USB data 36 | void jd_usb_push(const uint8_t *buf, unsigned len); 37 | 38 | 39 | 40 | #endif 41 | 42 | void jd_net_disable_fwd(void); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /inc/jd_control.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #ifndef JD_CONTROL_H 5 | #define JD_CONTROL_H 6 | 7 | #include "jd_config.h" 8 | #include "jd_physical.h" 9 | #include "jd_service_framework.h" 10 | 11 | #include "jacdac/dist/c/_base.h" 12 | #include "jacdac/dist/c/_system.h" 13 | #include "jacdac/dist/c/_sensor.h" 14 | #include "jacdac/dist/c/control.h" 15 | 16 | #define JD_ADVERTISEMENT_0_COUNTER_MASK 0x0000000F 17 | 18 | #define JD_GET(reg) (JD_CMD_GET_REGISTER | (reg)) 19 | #define JD_SET(reg) (JD_CMD_SET_REGISTER | (reg)) 20 | 21 | #define JD_IS_GET(cmd) (((cmd) >> 12) == (JD_CMD_GET_REGISTER >> 12)) 22 | #define JD_IS_SET(cmd) (((cmd) >> 12) == (JD_CMD_SET_REGISTER >> 12)) 23 | #define JD_REG_CODE(cmd) ((cmd)&0xfff) 24 | 25 | void jd_ctrl_init(void); 26 | void jd_ctrl_process(srv_t *_state); 27 | void jd_ctrl_handle_packet(srv_t *_state, jd_packet_t *pkt); 28 | void dump_pkt(jd_packet_t *pkt, const char *msg); 29 | 30 | static inline bool jd_is_command(jd_packet_t *pkt) { 31 | return (pkt->flags & JD_FRAME_FLAG_COMMAND) != 0; 32 | } 33 | 34 | static inline bool jd_is_report(jd_packet_t *pkt) { 35 | return !jd_is_command(pkt); 36 | } 37 | 38 | static inline bool jd_is_event(jd_packet_t *pkt) { 39 | return jd_is_report(pkt) && pkt->service_index <= JD_SERVICE_INDEX_MAX_NORMAL && 40 | (pkt->service_command & JD_CMD_EVENT_MASK) != 0; 41 | } 42 | 43 | static inline int jd_event_code(jd_packet_t *pkt) { 44 | return jd_is_event(pkt) ? (pkt->service_command & JD_CMD_EVENT_CODE_MASK) : -1; 45 | } 46 | 47 | static inline bool jd_is_register_get(jd_packet_t *pkt) { 48 | return pkt->service_index <= JD_SERVICE_INDEX_MAX_NORMAL && JD_IS_GET(pkt->service_command); 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /inc/jd_dcfg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "jd_config.h" 4 | #include 5 | 6 | #define DCFG_MAGIC0 0x47464344 // DCFG 7 | #define DCFG_MAGIC1 0xcab49b0a // '\n' + random 8 | 9 | #define DCFG_KEYSIZE 15 10 | 11 | #define DCFG_TYPE_BITS 2 12 | #define DCFG_TYPE_MASK ((1 << DCFG_TYPE_BITS) - 1) 13 | #define DCFG_SIZE_BITS (16 - DCFG_TYPE_BITS) 14 | 15 | #define DCFG_HASH_BITS 16 16 | #define DCFG_HASH_JUMP_BITS 5 17 | #define DCFG_HASH_JUMP_ENTRIES (1 << DCFG_HASH_JUMP_BITS) 18 | #define DCFG_HASH_SHIFT (DCFG_HASH_BITS - DCFG_HASH_JUMP_BITS) 19 | 20 | #define DCFG_TYPE_U32 0 21 | #define DCFG_TYPE_I32 1 22 | #define DCFG_TYPE_STRING 2 23 | #define DCFG_TYPE_BLOB 3 24 | #define DCFG_TYPE_INVALID 0xff 25 | 26 | #define DCFG_HEADER_HASH_JUMP_OFFSET (6 * 4) 27 | #define DCFG_HEADER_SIZE (DCFG_HEADER_HASH_JUMP_OFFSET + 2 * DCFG_HASH_JUMP_ENTRIES) 28 | #define DCFG_ENTRY_SIZE (DCFG_KEYSIZE + 1 + 2 * 4) 29 | 30 | typedef struct { 31 | char key[DCFG_KEYSIZE + 1]; 32 | uint16_t hash; 33 | uint16_t type_size; 34 | uint32_t value; 35 | } dcfg_entry_t; 36 | 37 | static inline unsigned dcfg_entry_type(const dcfg_entry_t *e) { 38 | return e ? (e->type_size & DCFG_TYPE_MASK) : DCFG_TYPE_INVALID; 39 | } 40 | static inline unsigned dcfg_entry_size(const dcfg_entry_t *e) { 41 | return e->type_size >> DCFG_TYPE_BITS; 42 | } 43 | 44 | typedef struct { 45 | uint32_t magic0; 46 | uint32_t magic1; 47 | uint32_t total_bytes; // including the header and any data after entries[] 48 | uint16_t num_entries; 49 | uint16_t reserved16; 50 | uint64_t restart_hash; // hash of config entries that are only applied on restart 51 | // hash_jump[x] points to first entry where (entry.hash >> DCFG_HASH_SHIFT) >= x 52 | uint16_t hash_jump[DCFG_HASH_JUMP_ENTRIES]; 53 | dcfg_entry_t entries[]; 54 | } dcfg_header_t; 55 | 56 | #if JD_DCFG 57 | int dcfg_validate(const dcfg_header_t *hd); 58 | int dcfg_set_user_config(const dcfg_header_t *hd); 59 | const dcfg_entry_t *dcfg_get_entry(const char *key); 60 | const dcfg_entry_t *dcfg_get_next_entry(const char *prefix, const dcfg_entry_t *previous); 61 | int32_t dcfg_get_i32(const char *key, int32_t defl); 62 | uint32_t dcfg_get_u32(const char *key, uint32_t defl); 63 | const char *dcfg_get_string(const char *key, unsigned *sizep); 64 | static inline const uint8_t *dcfg_get_blob(const char *key, unsigned *sizep) { 65 | return (const uint8_t *)dcfg_get_string(key, sizep); 66 | } 67 | char *dcfg_idx_key(const char *prefix, unsigned idx, const char *suffix); 68 | uint8_t dcfg_get_pin(const char *key); 69 | static inline bool dcfg_get_bool(const char *key) { 70 | return !!dcfg_get_i32(key, 0); 71 | } 72 | #endif 73 | -------------------------------------------------------------------------------- /inc/jd_dmesg.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #ifndef JD_DMESG_H 5 | #define JD_DMESG_H 6 | 7 | #include "jd_config.h" 8 | 9 | #if JD_DMESG_BUFFER_SIZE > 0 10 | 11 | struct CodalLogStore { 12 | volatile uint32_t ptr; 13 | char buffer[JD_DMESG_BUFFER_SIZE]; 14 | }; 15 | extern struct CodalLogStore codalLogStore; 16 | 17 | __attribute__((format(printf, 1, 2))) void jd_dmesg(const char *format, ...); 18 | void jd_vdmesg(const char *format, va_list ap); 19 | void jd_dmesg_write(const char *data, unsigned len); 20 | 21 | // read-out data from dmesg buffer 22 | unsigned jd_dmesg_read(void *dst, unsigned space, uint32_t *state); 23 | // similar, but at most until next '\n' 24 | unsigned jd_dmesg_read_line(void *dst, unsigned space, uint32_t *state); 25 | // get the oldest possible starting point (for *state above) 26 | uint32_t jd_dmesg_startptr(void); 27 | static inline uint32_t jd_dmesg_currptr(void) { 28 | return codalLogStore.ptr; 29 | } 30 | 31 | #ifndef DMESG 32 | #define DMESG jd_dmesg 33 | #endif 34 | 35 | #else 36 | 37 | #ifndef DMESG 38 | #define DMESG(...) ((void)0) 39 | #endif 40 | 41 | #endif 42 | 43 | #ifndef JD_LOG 44 | #define JD_LOG DMESG 45 | #endif 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /inc/jd_drivers.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #ifndef JD_DRIVERS_H 5 | #define JD_DRIVERS_H 6 | 7 | #include "jd_service_framework.h" 8 | #include "jd_srvcfg.h" 9 | #include "interfaces/jd_hw.h" 10 | #include "services/interfaces/jd_sensor_api.h" 11 | #include "services/interfaces/jd_pins.h" 12 | #include "jd_util.h" 13 | 14 | // This macros to be used when a single sensor implements two (or more) services 15 | #define ENV_INIT_N(finit, fsleep, n) \ 16 | static void init##n(void) { \ 17 | uint8_t s = init_status; \ 18 | init_status |= (1 << n); \ 19 | if (!s) \ 20 | finit(); \ 21 | } \ 22 | static void sleep##n(void) { \ 23 | if (!init_status) \ 24 | return; \ 25 | init_status &= ~(1 << n); \ 26 | if (!init_status) \ 27 | fsleep(); \ 28 | } 29 | 30 | #define ENV_INIT_DUAL(init, sleep) \ 31 | static uint8_t init_status; \ 32 | ENV_INIT_N(init, sleep, 0); \ 33 | ENV_INIT_N(init, sleep, 1); 34 | 35 | #define ENV_INIT_TRIPLE(init, sleep) \ 36 | static uint8_t init_status; \ 37 | ENV_INIT_N(init, sleep, 0); \ 38 | ENV_INIT_N(init, sleep, 1); \ 39 | ENV_INIT_N(init, sleep, 2); 40 | 41 | #define ENV_INIT_PTRS(n) .init = init##n, .sleep = sleep##n 42 | 43 | #define ENV_DEFINE_GETTER(scd, val) \ 44 | static void *scd##_##val(void) { \ 45 | ctx_t *ctx = &state; \ 46 | if (ctx->inited >= 3) \ 47 | return &ctx->val; \ 48 | return NULL; \ 49 | } 50 | 51 | #define ENV_GETTER(scd, val, idx) \ 52 | .name = #scd, .process = scd##_process, .get_reading = scd##_##val, ENV_INIT_PTRS(idx) 53 | 54 | // Used by various Sensirion sensors 55 | uint8_t jd_sgp_crc8(const uint8_t *data, int len); 56 | int jd_sgp_read_u16(uint8_t dev_addr, uint16_t regaddr, unsigned wait); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /inc/jd_numfmt.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #ifndef JD_NUMFMT_H 5 | #define JD_NUMFMT_H 6 | 7 | #if JD_SHORT_FLOAT 8 | typedef float jd_float_t; 9 | #else 10 | typedef double jd_float_t; 11 | #endif 12 | 13 | #define JD_NUMFMT_U8 0b0000 14 | #define JD_NUMFMT_U16 0b0001 15 | #define JD_NUMFMT_U32 0b0010 16 | #define JD_NUMFMT_U64 0b0011 17 | #define JD_NUMFMT_I8 0b0100 18 | #define JD_NUMFMT_I16 0b0101 19 | #define JD_NUMFMT_I32 0b0110 20 | #define JD_NUMFMT_I64 0b0111 21 | #define JD_NUMFMT_F8 0b1000 // not supported 22 | #define JD_NUMFMT_F16 0b1001 // not supported 23 | #define JD_NUMFMT_F32 0b1010 24 | #define JD_NUMFMT_F64 0b1011 25 | #define JD_NUMFMT_SPECIAL 0b1100 26 | 27 | #define JD_NUMFMT(fmt, shift) (DEVS_NUMFMT_##fmt | ((shift) << 4)) 28 | 29 | static inline int jd_numfmt_bytes(unsigned numfmt) { 30 | return (1 << (numfmt & 0b11)); 31 | } 32 | 33 | static inline int jd_numfmt_is_plain_int(unsigned numfmt) { 34 | return (numfmt >> 3) == 0; 35 | } 36 | 37 | static inline int jd_numfmt_special_idx(unsigned fmt) { 38 | return (fmt & JD_NUMFMT_SPECIAL) == JD_NUMFMT_SPECIAL ? (int)((fmt >> 4) | ((fmt & 0b11) << 4)) 39 | : -1; 40 | } 41 | 42 | bool jd_numfmt_is_valid(unsigned fmt0); 43 | 44 | jd_float_t jd_numfmt_read_float(const void *src, unsigned numfmt); 45 | // this is optimized in some cases, otherwise falls back to jd_numfmt_read_float() 46 | int32_t jd_numfmt_read_i32(const void *src, unsigned numfmt); 47 | 48 | void jd_numfmt_write_float(void *dst, unsigned numfmt, jd_float_t v); 49 | void jd_numfmt_write_i32(void *dst, unsigned numfmt, int32_t v); 50 | 51 | // jd_shift_multiplier(10) = 1024 52 | // jd_shift_multiplier(0) = 1 53 | // jd_shift_multiplier(-10) = 1/1024 54 | jd_float_t jd_shift_multiplier(int shift); 55 | 56 | #endif // JD_NUMFMT_H -------------------------------------------------------------------------------- /inc/jd_physical.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #ifndef __JDPROTOCOL_H 5 | #define __JDPROTOCOL_H 6 | 7 | #include "jd_config.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | // 255 minus size of the serial header, rounded down to 4 14 | #define JD_SERIAL_PAYLOAD_SIZE 236 15 | #define JD_SERIAL_FULL_HEADER_SIZE 16 16 | // the COMMAND flag signifies that the device_identifier is the recipient 17 | // (i.e., it's a command for the peripheral); the bit clear means device_identifier is the source 18 | // (i.e., it's a report from peripheral or a broadcast message) 19 | #define JD_FRAME_FLAG_COMMAND 0x01 20 | // an ACK should be issued with CRC of this frame upon reception 21 | #define JD_FRAME_FLAG_ACK_REQUESTED 0x02 22 | // the device_identifier contains target service class number 23 | #define JD_FRAME_FLAG_IDENTIFIER_IS_SERVICE_CLASS 0x04 24 | #define JD_FRAME_FLAG_BROADCAST 0x04 25 | // set on frames not received from the JD-wire 26 | #define JD_FRAME_FLAG_LOOPBACK 0x40 27 | // when set, the packet may have different layout and should be dropped 28 | #define JD_FRAME_FLAG_VNEXT 0x80 29 | 30 | #define JD_FRAME_SIZE(pkt) ((pkt)->size + 12) 31 | 32 | #define JD_SERVICE_INDEX_CONTROL 0x00 33 | #define JD_SERVICE_INDEX_MASK 0x3f 34 | #define JD_SERVICE_INDEX_CRC_ACK 0x3f 35 | #define JD_SERVICE_INDEX_STREAM 0x3e 36 | #define JD_SERVICE_INDEX_BROADCAST 0x3d 37 | #define JD_SERVICE_INDEX_MAX_NORMAL 0x30 38 | 39 | #define JD_DEVICE_IDENTIFIER_BROADCAST_MARK 0xAAAAAAAA00000000 40 | 41 | #define JD_PIPE_COUNTER_MASK 0x001f 42 | #define JD_PIPE_CLOSE_MASK 0x0020 43 | #define JD_PIPE_METADATA_MASK 0x0040 44 | #define JD_PIPE_PORT_SHIFT 7 45 | 46 | #define JD_CMD_EVENT_MASK 0x8000 47 | #define JD_CMD_EVENT_CODE_MASK 0xff 48 | #define JD_CMD_EVENT_COUNTER_MASK 0x7f 49 | #define JD_CMD_EVENT_COUNTER_SHIFT 8 50 | #define JD_CMD_EVENT_MK(counter, code) \ 51 | (JD_CMD_EVENT_MASK | ((((counter)&JD_CMD_EVENT_COUNTER_MASK) << JD_CMD_EVENT_COUNTER_SHIFT)) | \ 52 | (code & JD_CMD_EVENT_CODE_MASK)) 53 | 54 | #define JDSPI_MAGIC 0x7ACD 55 | #define JDSPI_MAGIC_NOOP 0xB3CD 56 | 57 | typedef void (*cb_t)(void); 58 | typedef int (*intfn_t)(void *); 59 | 60 | struct _jd_packet_t { 61 | uint16_t crc; 62 | uint8_t _size; // of frame data[] 63 | uint8_t flags; 64 | 65 | uint64_t device_identifier; 66 | 67 | uint8_t service_size; 68 | uint8_t service_index; 69 | uint16_t service_command; 70 | 71 | uint8_t data[0]; 72 | } __attribute__((__packed__, aligned(4))); 73 | 74 | typedef struct _jd_packet_t jd_packet_t; 75 | 76 | struct _jd_frame_t { 77 | uint16_t crc; 78 | uint8_t size; 79 | uint8_t flags; 80 | 81 | uint64_t device_identifier; 82 | 83 | uint8_t data[JD_SERIAL_PAYLOAD_SIZE + 4]; 84 | } __attribute__((__packed__, aligned(4))); 85 | typedef struct _jd_frame_t jd_frame_t; 86 | 87 | struct _jd_pipe_cmd_t { 88 | uint64_t device_identifier; 89 | uint16_t port_num; 90 | uint16_t reserved; 91 | } __attribute__((__packed__, aligned(4))); 92 | typedef struct _jd_pipe_cmd_t jd_pipe_cmd_t; 93 | 94 | void jd_packet_ready(void); 95 | void jd_compute_crc(jd_frame_t *frame); 96 | bool jd_frame_crc_ok(jd_frame_t *frame); 97 | // these are to be called by uart implementation 98 | void jd_tx_completed(int errCode); 99 | void jd_rx_completed(int dataLeft); 100 | void jd_line_falling(void); 101 | int jd_is_running(void); 102 | int jd_is_busy(void); 103 | 104 | typedef struct { 105 | uint32_t bus_state; 106 | uint32_t bus_lo_error; 107 | uint32_t bus_uart_error; 108 | uint32_t bus_timeout_error; 109 | uint32_t packets_sent; 110 | uint32_t packets_received; 111 | uint32_t packets_dropped; 112 | } jd_diagnostics_t; 113 | jd_diagnostics_t *jd_get_diagnostics(void); 114 | 115 | #ifdef __cplusplus 116 | } 117 | #endif 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /inc/jd_pipes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Output pipes (data flowing from this device) 5 | // 6 | 7 | #define JD_PIPE_OK 0 8 | #define JD_PIPE_TRY_AGAIN 1 9 | #define JD_PIPE_TIMEOUT -1 10 | #define JD_PIPE_ERROR -2 11 | 12 | #define JD_OPIPE_MAX_RETRIES 4 13 | 14 | // note that this is around 260 bytes 15 | typedef struct jd_opipe_desc { 16 | // don't access members directly 17 | struct jd_opipe_desc *next; 18 | uint16_t counter; 19 | uint8_t status; 20 | uint8_t curr_retry; 21 | uint32_t retry_time; 22 | jd_frame_t frame; 23 | } jd_opipe_desc_t; 24 | 25 | int jd_opipe_open(jd_opipe_desc_t *str, uint64_t device_id, uint16_t port_num); 26 | int jd_opipe_open_cmd(jd_opipe_desc_t *str, jd_packet_t *cmd_pkt); 27 | int jd_opipe_open_report(jd_opipe_desc_t *str, jd_packet_t *report_pkt); 28 | 29 | // all these functions can return JD_PIPE_TRY_AGAIN 30 | // can be optionally called before jd_opipe_write*(); if this return JD_PIPE_OK, then write also 31 | // will 32 | int jd_opipe_check_space(jd_opipe_desc_t *str, unsigned len); 33 | int jd_opipe_write(jd_opipe_desc_t *str, const void *data, unsigned len); 34 | int jd_opipe_write_meta(jd_opipe_desc_t *str, const void *data, unsigned len); 35 | // flush is automatic when buffer full, or on close 36 | int jd_opipe_flush(jd_opipe_desc_t *str); 37 | // it's OK to closed a closed stream 38 | int jd_opipe_close(jd_opipe_desc_t *str); 39 | 40 | // the be called from top-level 41 | void jd_opipe_handle_packet(jd_packet_t *pkt); 42 | void jd_opipe_process(void); 43 | 44 | // 45 | // Input pipes (data flowing to this device) 46 | // 47 | 48 | typedef struct jd_ipipe_desc jd_ipipe_desc_t; 49 | typedef void (*jd_ipipe_handler_t)(jd_ipipe_desc_t *istr, jd_packet_t *pkt); 50 | struct jd_ipipe_desc { 51 | jd_ipipe_handler_t handler; 52 | jd_ipipe_handler_t meta_handler; 53 | struct jd_ipipe_desc *next; 54 | uint16_t counter; 55 | }; 56 | int jd_ipipe_open(jd_ipipe_desc_t *str, jd_ipipe_handler_t handler, 57 | jd_ipipe_handler_t meta_handler); 58 | void jd_ipipe_close(jd_ipipe_desc_t *str); 59 | void jd_ipipe_handle_packet(jd_packet_t *pkt); 60 | -------------------------------------------------------------------------------- /inc/jd_protocol.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #ifndef JD_PROTOCOL_H 5 | #define JD_PROTOCOL_H 6 | 7 | #include "jd_control.h" 8 | #include "jd_physical.h" 9 | #include "jd_service_framework.h" 10 | #include "jd_srvcfg.h" 11 | #include "jd_util.h" 12 | #include "jd_io.h" 13 | #include "jd_dmesg.h" 14 | #include "interfaces/jd_tx.h" 15 | #include "interfaces/jd_rx.h" 16 | #include "interfaces/jd_hw.h" 17 | #include "interfaces/jd_alloc.h" 18 | #include "interfaces/jd_app.h" 19 | 20 | void jd_init(void); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /inc/jd_service_classes.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #ifndef JD_SERVICE_CLASSES_H 5 | #define JD_SERVICE_CLASSES_H 6 | 7 | // legacy/temporary only 8 | #define JD_SERVICE_CLASS_MONO_DISPLAY 0x1f43e195 9 | #define JD_SERVICE_CLASS_TOUCHBUTTON 0x130cf5be 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /inc/jd_srvcfg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "jd_dcfg.h" 4 | 5 | #if JD_DCFG 6 | char *jd_srvcfg_key(const char *key); 7 | uint8_t jd_srvcfg_pin(const char *key); 8 | int32_t jd_srvcfg_i32(const char *key, int32_t defl); 9 | int32_t jd_srvcfg_u32(const char *key, int32_t defl); 10 | bool jd_srvcfg_has_flag(const char *key); 11 | srv_t *jd_srvcfg_last_service(void); 12 | 13 | void jd_srvcfg_run(void); 14 | const char *jd_srvcfg_instance_name(srv_t *srv); 15 | int jd_srvcfg_variant(srv_t *srv); 16 | #endif -------------------------------------------------------------------------------- /inc/jd_thr.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | #pragma once 5 | 6 | #include "jd_protocol.h" 7 | 8 | #if JD_THR_ANY 9 | 10 | #if JD_THR_PTHREAD 11 | #include 12 | typedef pthread_mutex_t jd_mutex_t; 13 | typedef pthread_t jd_thread_t; 14 | 15 | #elif JD_THR_AZURE_RTOS 16 | #error "TODO" 17 | 18 | #elif JD_THR_FREE_RTOS 19 | #error "TODO" 20 | 21 | #else 22 | #error "no threading impl. defined" 23 | #endif 24 | 25 | 26 | // jd_mutex_t can be a struct 27 | // jd_thread_t must be a pointer; it will be compared using '==' 28 | 29 | void jd_thr_init(void); 30 | 31 | void jd_thr_start_process_worker(void); 32 | void jd_thr_wake_main(void); 33 | 34 | jd_thread_t jd_thr_start_thread(void (*worker)(void *userdata), void *userdata); 35 | 36 | // priority inheritance should be enabled if available 37 | int jd_thr_init_mutex(jd_mutex_t *mutex); 38 | void jd_thr_destroy_mutex(jd_mutex_t *mutex); 39 | void jd_thr_lock(jd_mutex_t *mutex); 40 | void jd_thr_unlock(jd_mutex_t *mutex); 41 | 42 | jd_thread_t jd_thr_self(void); 43 | void jd_thr_suspend_self(void); 44 | void jd_thr_resume(jd_thread_t t); 45 | 46 | #endif -------------------------------------------------------------------------------- /scripts/bump.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "X`git status --porcelain --untracked-files=no`" != X ] ; then 4 | git status 5 | echo 6 | echo "*** You have local changes; cannot bump." 7 | exit 1 8 | fi 9 | 10 | git pull || exit 1 11 | 12 | eval `git describe --dirty --tags --match 'v[0-9]*' --always | sed -e 's/-.*//; s/v/v0=/; s/\./ v1=/; s/\./ v2=/'` 13 | defl=0.0.0 14 | if [ "X$v0" != X ] ; then 15 | defl=$v0.$v1.$(($v2 + 1)) 16 | fi 17 | set -e 18 | echo "Enter version [Enter = $defl; Ctrl-C to cancel]:" 19 | read ver 20 | if [ "X$ver" = "X" ] ; then 21 | ver="$defl" 22 | else 23 | ver=$(echo "$ver" | sed -e 's/v//i') 24 | fi 25 | if echo "$ver" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$' ; then 26 | : 27 | else 28 | echo "Invalid version: $ver" 29 | exit 1 30 | fi 31 | 32 | set -x 33 | git tag "v$ver" 34 | git push --tags 35 | git push 36 | -------------------------------------------------------------------------------- /scripts/decode_multitouch_history.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs") 2 | const screenWidth = 120 3 | 4 | function decode(fn) { 5 | const buf = fs.readFileSync(fn) 6 | const arr = [] 7 | for (let i = 0; i < buf.length; i += 2) { 8 | const v = buf[i] 9 | let cnt = buf[i + 1] 10 | if (cnt > 50) cnt = 50 11 | while (cnt--) 12 | arr.push(v) 13 | } 14 | 15 | return arr 16 | } 17 | 18 | function show(arr) { 19 | let r = "" 20 | let ch = [] 21 | 22 | reset() 23 | for (const v of arr) { 24 | for (let i = 0; i < 8; ++i) { 25 | if (v & (1 << i)) 26 | ch[i] += "#" 27 | else 28 | ch[i] += "-" 29 | } 30 | if (ch[0].length >= screenWidth) flush() 31 | } 32 | flush() 33 | 34 | console.log(r) 35 | 36 | function reset() { 37 | ch = [] 38 | for (let i = 0; i < 8; ++i) 39 | ch.push("") 40 | } 41 | 42 | function flush() { 43 | for (let i = 0; i < 8; ++i) 44 | r += ch[i] + "\n" 45 | r += "\n" 46 | reset() 47 | } 48 | } 49 | 50 | show(decode(process.argv[2])) 51 | -------------------------------------------------------------------------------- /scripts/git-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | D=`date +%Y%m%d-%H%M` 3 | TAG=`git describe --dirty --tags --match 'v[0-9]*' --always | sed -e 's/^v//; s/-dirty/-'"$D/"` 4 | if [[ "$TAG" =~ ^[0-9a-f]+-[0-9]{8}-[0-9]{4}$ ]]; then 5 | echo "$TAG" # use as is when based on commit i.e. v959x563-20250321-1122 (v--