├── .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--