diff --git a/README.md b/README.md index 9e92396..cb3004d 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,7 @@ # Zephyr Example Application - - Documentation - - - API Documentation - - -This repository contains a Zephyr example application. The main purpose of this -repository is to serve as a reference on how to structure Zephyr-based -applications. Some of the features demonstrated in this example are: - -- Basic [Zephyr application][app_dev] skeleton -- [Zephyr workspace applications][workspace_app] -- [Zephyr modules][modules] -- [West T2 topology][west_t2] -- [Custom boards][board_porting] -- Custom [devicetree bindings][bindings] -- Out-of-tree [drivers][drivers] -- Out-of-tree libraries -- Example CI configuration (using Github Actions) -- Custom [west extension][west_ext] -- Doxygen and Sphinx documentation boilerplate - -This repository is versioned together with the [Zephyr main tree][zephyr]. This -means that every time that Zephyr is tagged, this repository is tagged as well -with the same version number, and the [manifest](west.yml) entry for `zephyr` -will point to the corresponding Zephyr tag. For example, the `example-application` -v2.6.0 will point to Zephyr v2.6.0. Note that the `main` branch always -points to the development branch of Zephyr, also `main`. - -[app_dev]: https://docs.zephyrproject.org/latest/develop/application/index.html -[workspace_app]: https://docs.zephyrproject.org/latest/develop/application/index.html#zephyr-workspace-app -[modules]: https://docs.zephyrproject.org/latest/develop/modules.html -[west_t2]: https://docs.zephyrproject.org/latest/develop/west/workspaces.html#west-t2 -[board_porting]: https://docs.zephyrproject.org/latest/guides/porting/board_porting.html -[bindings]: https://docs.zephyrproject.org/latest/guides/dts/bindings.html -[drivers]: https://docs.zephyrproject.org/latest/reference/drivers/index.html -[zephyr]: https://github.com/zephyrproject-rtos/zephyr -[west_ext]: https://docs.zephyrproject.org/latest/develop/west/extensions.html - ## Getting Started -Before getting started, make sure you have a proper Zephyr development -environment. Follow the official -[Zephyr Getting Started Guide](https://docs.zephyrproject.org/latest/getting_started/index.html). - ### Initialization The first step is to initialize the workspace folder (``my-workspace``) where @@ -65,21 +21,8 @@ west update To build the application, run the following command: ```shell -cd example-application -west build -b $BOARD app -``` - -where `$BOARD` is the target board. - -You can use the `custom_plank` board found in this -repository. Note that Zephyr sample boards may be used if an -appropriate overlay is provided (see `app/boards`). - -A sample debug configuration is also provided. To apply it, run the following -command: - -```shell -west build -b $BOARD app -- -DOVERLAY_CONFIG=debug.conf +cd mellifera_firmware +west build -b mellifera_rev1 app ``` Once you have built the application, run the following command to flash it: @@ -88,6 +31,30 @@ Once you have built the application, run the following command to flash it: west flash ``` +```shell +go install github.com/apache/mynewt-mcumgr-cli/mcumgr@latest +``` + +### Bootloader +```shell +# build and flash the bootloader +west build -p -b mellifera_rev1 ../mcuboot/boot/zephyr/ -- -DCONF_FILE=$(pwd)/boot.conf +west flash + +# define serial connection to device +~/go/bin/mcumgr conn add acm0 type="serial" connstring="dev=/dev/ttyACM0,baud=115200,mtu=512" +~/go/bin/mcumgr conn add ttyusb0 type="serial" connstring="dev=/dev/ttyUSB0,baud=115200,mtu=512" + +# hold button while power cycling to enter bootloading mode +~/go/bin/mcumgr -c usbtty0 image list + +# build the app +west build -p -b mellifera_rev1 app + +~/go/bin/mcumgr -c ttyusb0 image upload -e build/zephyr/zephyr.signed.bin +~/go/bin/mcumgr -c usbtty0 image list +``` + ### Testing To execute Twister integration tests, run the following command: diff --git a/app/prj.conf b/app/prj.conf index ac51ec3..f87ca25 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -5,3 +5,21 @@ CONFIG_SENSOR=y CONFIG_BLINK=y + +CONFIG_STDOUT_CONSOLE=y + +CONFIG_HEAP_MEM_POOL_SIZE=16384 + +CONFIG_DISPLAY=y + +CONFIG_LOG=y + +CONFIG_CFB_LOG_LEVEL_DBG=y +CONFIG_CHARACTER_FRAMEBUFFER=y +CONFIG_CBPRINTF_FP_SUPPORT=y + +CONFIG_BT=y +CONFIG_BT_DEVICE_NAME="Mellifera" + +CONFIG_BOOTLOADER_MCUBOOT=y +CONFIG_MCUBOOT_GENERATE_UNSIGNED_IMAGE=y \ No newline at end of file diff --git a/app/src/main.c b/app/src/main.c index 2c9c3ab..14753cd 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -6,77 +6,352 @@ #include #include #include +#include #include #include +#include +#include + LOG_MODULE_REGISTER(main, CONFIG_APP_LOG_LEVEL); #define BLINK_PERIOD_MS_STEP 100U -#define BLINK_PERIOD_MS_MAX 1000U +#define BLINK_PERIOD_MS_MAX 1000U + +#define DEVICE_NAME CONFIG_BT_DEVICE_NAME +#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) + +/* + * Set Advertisement data. Based on the Eddystone specification: + * https://github.com/google/eddystone/blob/master/protocol-specification.md + * https://github.com/google/eddystone/tree/master/eddystone-url + */ +static const struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR), + BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0xaa, 0xfe), + BT_DATA_BYTES(BT_DATA_SVC_DATA16, + 0xaa, 0xfe, /* Eddystone UUID */ + 0x10, /* Eddystone-URL frame type */ + 0x00, /* Calibrated Tx power at 0m */ + 0x00, /* URL Scheme Prefix http://www. */ + 'z', 'e', 'p', 'h', 'y', 'r', + 'p', 'r', 'o', 'j', 'e', 'c', 't', + 0x08) /* .org */ +}; + +/* Set Scan Response data */ +static const struct bt_data sd[] = { + BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), +}; + +static void bt_ready(int err) +{ + char addr_s[BT_ADDR_LE_STR_LEN]; + bt_addr_le_t addr = {0}; + size_t count = 1; + + if (err) + { + LOG_ERR("Bluetooth init failed (err %d)", err); + return; + } + + LOG_INF("Bluetooth initialized"); + + /* Start advertising */ + err = bt_le_adv_start(BT_LE_ADV_NCONN_IDENTITY, ad, ARRAY_SIZE(ad), + sd, ARRAY_SIZE(sd)); + if (err) + { + LOG_ERR("Advertising failed to start (err %d)", err); + return; + } + + /* For connectable advertising you would use + * bt_le_oob_get_local(). For non-connectable non-identity + * advertising an non-resolvable private address is used; + * there is no API to retrieve that. + */ + + bt_id_get(&addr, &count); + bt_addr_le_to_str(&addr, addr_s, sizeof(addr_s)); + + LOG_INF("Beacon started, advertising as %s", addr_s); +} int main(void) { - int ret; - unsigned int period_ms = BLINK_PERIOD_MS_MAX; - const struct device *sensor, *blink; - struct sensor_value last_val = { 0 }, val; + const struct device *dev; + uint16_t x_res; + uint16_t y_res; + uint16_t rows; + uint8_t ppt; + uint8_t font_width; + uint8_t font_height; - printk("Zephyr Example Application %s\n", APP_VERSION_STRING); + int err; - sensor = DEVICE_DT_GET(DT_NODELABEL(example_sensor)); - if (!device_is_ready(sensor)) { - LOG_ERR("Sensor not ready"); + printk("Starting Mellifera version %s...\n", APP_VERSION_STRING); + // printk("Board: %s\n", BOARD); + + LOG_INF("Starting Beacon Demo"); + + /* Initialize the Bluetooth Subsystem */ + err = bt_enable(bt_ready); + if (err) + { + LOG_ERR("Bluetooth init failed (err %d)", err); + } + + dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)); + if (!device_is_ready(dev)) + { + LOG_ERR("Device %s not ready", dev->name); return 0; } - blink = DEVICE_DT_GET(DT_NODELABEL(blink_led)); - if (!device_is_ready(blink)) { - LOG_ERR("Blink LED not ready"); + if (display_set_pixel_format(dev, PIXEL_FORMAT_MONO10) != 0) + { + if (display_set_pixel_format(dev, PIXEL_FORMAT_MONO01) != 0) + { + LOG_ERR("Failed to set required pixel format"); + return 0; + } + } + + LOG_INF("Initialized %s", dev->name); + + if (cfb_framebuffer_init(dev)) + { + LOG_ERR("Framebuffer initialization failed!"); return 0; } - ret = blink_off(blink); - if (ret < 0) { - LOG_ERR("Could not turn off LED (%d)", ret); + cfb_framebuffer_clear(dev, true); + + display_blanking_off(dev); + + x_res = cfb_get_display_parameter(dev, CFB_DISPLAY_WIDTH); + y_res = cfb_get_display_parameter(dev, CFB_DISPLAY_HEIGH); + rows = cfb_get_display_parameter(dev, CFB_DISPLAY_ROWS); + ppt = cfb_get_display_parameter(dev, CFB_DISPLAY_PPT); + + for (int idx = 0; idx < 42; idx++) + { + if (cfb_get_font_size(dev, idx, &font_width, &font_height)) + { + break; + } + cfb_framebuffer_set_font(dev, idx); + printf("font width %d, font height %d\n", + font_width, font_height); + } + cfb_framebuffer_set_font(dev, 0); + + printf("x_res %d, y_res %d, ppt %d, rows %d, cols %d\n", + x_res, + y_res, + ppt, + rows, + cfb_get_display_parameter(dev, CFB_DISPLAY_COLS)); + + cfb_set_kerning(dev, 3); + + const struct device *ina, *bmp, *bmx, *lis, *hdc; + + ina = DEVICE_DT_GET(DT_NODELABEL(ina231)); + if (!device_is_ready(ina)) + { + LOG_ERR("Device %s not ready", ina->name); return 0; } - blink_set_period_ms(blink, BLINK_PERIOD_MS_MAX); + bmp = DEVICE_DT_GET(DT_NODELABEL(bmp388)); + if (!device_is_ready(bmp)) + { + LOG_ERR("Device %s not ready", bmp->name); + return 0; + } - printk("Use the sensor to change LED blinking period\n"); + bmx = DEVICE_DT_GET(DT_NODELABEL(bmx055)); + if (!device_is_ready(bmx)) + { + LOG_ERR("Device %s not ready", bmx->name); + return 0; + } + LOG_INF("Initialized %s", bmx->name); - while (1) { - ret = sensor_sample_fetch(sensor); - if (ret < 0) { + lis = DEVICE_DT_GET(DT_NODELABEL(lis2dh)); + if (!device_is_ready(lis)) + { + LOG_ERR("Device %s not ready", lis->name); + return 0; + } + LOG_INF("Initialized %s", lis->name); + + hdc = DEVICE_DT_GET(DT_NODELABEL(hdc1080)); + if (!device_is_ready(hdc)) + { + LOG_ERR("Device %s not ready", hdc->name); + return 0; + } + + while (1) + { + int ret; + struct sensor_value voltage, current, pressure, temperature, accel_x, accel_y, accel_z, accel_z_ref, humidity; + char str_v[15] = {0}; + char str_i[15] = {0}; + char str_p[16] = {0}; + char str_t[16] = {0}; + char str_ax[16] = {0}; + char str_ay[16] = {0}; + char str_az[16] = {0}; + char str_az_ref[16] = {0}; + char str_h[16] = {0}; + + ret = sensor_sample_fetch(ina); + if (ret < 0) + { LOG_ERR("Could not fetch sample (%d)", ret); return 0; } + ret = sensor_channel_get(ina, SENSOR_CHAN_VOLTAGE, &voltage); + if (ret < 0) + { + LOG_ERR("Could not get voltage (%d)", ret); + return 0; + } + ret = sensor_channel_get(ina, SENSOR_CHAN_CURRENT, ¤t); + if (ret < 0) + { + LOG_ERR("Could not get current (%d)", ret); + return 0; + } - ret = sensor_channel_get(sensor, SENSOR_CHAN_PROX, &val); - if (ret < 0) { + ret = sensor_sample_fetch(bmp); + if (ret < 0) + { + LOG_ERR("Could not fetch sample (%d)", ret); + return 0; + } + ret = sensor_channel_get(bmp, SENSOR_CHAN_PRESS, &pressure); + if (ret < 0) + { + LOG_ERR("Could not get pressure (%d)", ret); + return 0; + } + // ret = sensor_channel_get(bmp, SENSOR_CHAN_AMBIENT_TEMP, &temperature); + // if (ret < 0) + // { + // LOG_ERR("Could not get temperature (%d)", ret); + // return 0; + // } + + ret = sensor_sample_fetch(bmx); + if (ret < 0) + { + LOG_ERR("Could not fetch sample (%d)", ret); + return 0; + } + ret = sensor_channel_get(bmx, SENSOR_CHAN_DIE_TEMP, &temperature); + if (ret < 0) + { + LOG_ERR("Could not get temperature (%d)", ret); + return 0; + } + ret = sensor_channel_get(bmx, SENSOR_CHAN_ACCEL_X, &accel_x); + if (ret < 0) + { + LOG_ERR("Could not get acceleration (%d)", ret); + return 0; + } + ret = sensor_channel_get(bmx, SENSOR_CHAN_ACCEL_Y, &accel_y); + if (ret < 0) + { + LOG_ERR("Could not get acceleration (%d)", ret); + return 0; + } + ret = sensor_channel_get(bmx, SENSOR_CHAN_ACCEL_Z, &accel_z); + if (ret < 0) + { + LOG_ERR("Could not get acceleration (%d)", ret); + return 0; + } + + ret = sensor_sample_fetch(lis); + if (ret < 0) + { + LOG_ERR("Could not fetch sample (%d)", ret); + return 0; + } + ret = sensor_channel_get(lis, SENSOR_CHAN_ACCEL_Z, &accel_z_ref); + if (ret < 0) + { + LOG_ERR("Could not get acceleration (%d)", ret); + return 0; + } + + ret = sensor_sample_fetch(hdc); + if (ret < 0) + { + LOG_ERR("Could not fetch sample (%d)", ret); + return 0; + } + ret = sensor_channel_get(hdc, SENSOR_CHAN_AMBIENT_TEMP, &temperature); + if (ret < 0) + { + LOG_ERR("Could not get sample (%d)", ret); + return 0; + } + ret = sensor_channel_get(hdc, SENSOR_CHAN_HUMIDITY, &humidity); + if (ret < 0) + { LOG_ERR("Could not get sample (%d)", ret); return 0; } - if ((last_val.val1 == 0) && (val.val1 == 1)) { - if (period_ms == 0U) { - period_ms = BLINK_PERIOD_MS_MAX; - } else { - period_ms -= BLINK_PERIOD_MS_STEP; - } + sprintf(str_v, "V :%7.5f", voltage.val1 + voltage.val2 * 1e-6); + sprintf(str_i, "I :%7.5f", current.val1 + current.val2 * 1e-6); + sprintf(str_p, "P :%7.4f", pressure.val1 + pressure.val2 * 1e-6); + sprintf(str_t, "T :%7.4f", temperature.val1 + temperature.val2 * 1e-6); + sprintf(str_ax, "X :%+7.3f", accel_x.val1 + accel_x.val2 * 1e-6); + sprintf(str_ay, "Y :%+7.3f", accel_y.val1 + accel_y.val2 * 1e-6); + sprintf(str_az, "Z :%+7.3f", accel_z.val1 + accel_z.val2 * 1e-6); + sprintf(str_az_ref, "Zr:%+7.3f", accel_z_ref.val1 + accel_z_ref.val2 * 1e-6); + sprintf(str_h, "H :%7.3f", humidity.val1 + humidity.val2 * 1e-6); - printk("Proximity detected, setting LED period to %u ms\n", - period_ms); - blink_set_period_ms(blink, period_ms); + // printf("%s\t%s\t%s\t%s\n", str_v, str_i, str_p, str_t); + + cfb_framebuffer_clear(dev, false); + if (cfb_print(dev, str_t, 0, 0)) + { + printf("Failed to print a string\n"); + continue; } + if (cfb_print(dev, str_h, 0, 16)) + { + printf("Failed to print a string\n"); + continue; + } + // if (cfb_print(dev, str_az, 0, 16 * 2)) + // { + // printf("Failed to print a string\n"); + // continue; + // } + // if (cfb_print(dev, str_az_ref, 0, 16 * 3)) + // { + // printf("Failed to print a string\n"); + // continue; + // } - last_val = val; - - k_sleep(K_MSEC(100)); + cfb_framebuffer_finalize(dev); +#if defined(CONFIG_ARCH_POSIX) + k_sleep(K_MSEC(20)); +#endif } return 0; } - diff --git a/boards/bh/mellifera_rev1/mellifera_rev1.dts b/boards/bh/mellifera_rev1/mellifera_rev1.dts index f1a9ce4..5ba820e 100644 --- a/boards/bh/mellifera_rev1/mellifera_rev1.dts +++ b/boards/bh/mellifera_rev1/mellifera_rev1.dts @@ -13,12 +13,14 @@ chosen { zephyr,console = &uart0; - // zephyr,shell-uart = &uart0; - // zephyr,uart-mcumgr = &uart0; - // zephyr,bt-mon-uart = &uart0; - // zephyr,bt-c2h-uart = &uart0; + zephyr,shell-uart = &uart0; + zephyr,uart-mcumgr = &uart0; + zephyr,bt-mon-uart = &uart0; + zephyr,bt-c2h-uart = &uart0; zephyr,sram = &sram0; zephyr,flash = &flash0; + zephyr,display = &ssd1306; + zephyr,code-partition = &slot0_partition; }; example_sensor: example-sensor { @@ -44,13 +46,86 @@ gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; label = "Yellow LED 3"; }; + led_rx: led_rx { + gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>; + label = "RX LED"; + }; + led_tx: led_tx { + gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>; + label = "TX LED"; + }; + led_rx_ok: led_rx_ok { + gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + label = "RX OK LED"; + }; + led_sfd: led_sfd { + gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; + label = "SFD LED"; + }; }; + buttons { + compatible = "gpio-keys"; + button0: button_0 { + gpios = <&gpio0 2 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Pushbutton"; + zephyr,code = ; + }; + int_drdy: int_drdy { + gpios = <&gpio0 22 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "DRDYM from BMX055 9-DOF"; + zephyr,code = ; + }; + int1: int_1 { + gpios = <&gpio0 12 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Interrupt 1 from BMX055 9-DOF"; + zephyr,code = ; + }; + int2: int_2 { + gpios = <&gpio0 14 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Interrupt 2 from BMX055 9-DOF"; + zephyr,code = ; + }; + int3: int_3 { + gpios = <&gpio0 10 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Interrupt 3 from BMX055 9-DOF"; + zephyr,code = ; + }; + int4: int_4 { + gpios = <&gpio0 9 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Interrupt 4 from BMX055 9-DOF"; + zephyr,code = ; + }; + int5: int_5 { + gpios = <&gpio0 30 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Interrupt 5 from BMX055 9-DOF"; + zephyr,code = ; + }; + }; + + blink_led: blink-led { compatible = "blink-gpio-led"; led-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>; blink-period-ms = <1000>; }; + + /* These aliases are provided for compatibility with samples */ + aliases { + led0 = &led0; + led1 = &led1; + led2 = &led2; + led3 = &led3; + // pwm-led0 = &pwm_led0; + sw0 = &button0; + // sw1 = &button1; + // sw2 = &button2; + // sw3 = &button3; + bootloader-led0 = &led0; + mcuboot-button0 = &button0; + mcuboot-led0 = &led0; + watchdog0 = &wdt0; + }; }; &uicr { @@ -80,6 +155,53 @@ pinctrl-0 = <&i2c0_default>; pinctrl-1 = <&i2c0_sleep>; pinctrl-names = "default", "sleep"; + + ssd1306: ssd1306@3d { + compatible = "solomon,ssd1306fb"; + reg = <0x3d>; + width = <128>; + height = <64>; + segment-offset = <0>; + page-offset = <0>; + display-offset = <0>; + multiplex-ratio = <63>; + segment-remap; + com-invdir; + prechargep = <0x22>; + }; + + ina231: ina231@45 { + compatible = "ti,ina230"; + reg = <0x45>; + // irq-gpios = <&gpio0 31 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + current-lsb-microamps = <7>; + rshunt-micro-ohms = <50000>; + adc-mode = "Bus and shunt voltage continuous"; + }; + + bmp388: bmp388@76 { + compatible = "bosch,bmp388"; + reg = <0x76>; + int-gpios = <&gpio0 27 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + // odr = "1.563"; + osr-press = <1>; + }; + + hdc1080: hdc1080@40 { + compatible = "ti,hdc1080"; + reg = <0x40>; + }; + + bmx055: bmx055@18 { + compatible = "bosch,bmx055"; + reg = <0x18>; + }; + + lis2dh: lis2dh@19 { + // This is built into the DWM1001 module + compatible = "st,lis2dh"; + reg = <0x19>; + }; }; &spi1 { @@ -89,3 +211,29 @@ pinctrl-1 = <&spi1_sleep>; pinctrl-names = "default", "sleep"; }; + +&flash0 { + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0xc000>; + }; + slot0_partition: partition@c000 { + label = "image-0"; + reg = <0x0000C000 0x37000>; + }; + slot1_partition: partition@43000 { + label = "image-1"; + reg = <0x00043000 0x37000>; + }; + storage_partition: partition@7a000 { + label = "storage"; + reg = <0x0007a000 0x00006000>; + }; + }; +}; diff --git a/boards/bh/mellifera_rev1/mellifera_rev1_defconfig b/boards/bh/mellifera_rev1/mellifera_rev1_defconfig index 2c54281..8728c1c 100644 --- a/boards/bh/mellifera_rev1/mellifera_rev1_defconfig +++ b/boards/bh/mellifera_rev1/mellifera_rev1_defconfig @@ -1,15 +1,19 @@ -# Copyright (c) 2021 Nordic Semiconductor ASA -# SPDX-License-Identifier: Apache-2.0 +# Enable MPU CONFIG_ARM_MPU=y + +# Enable hardware stack protection CONFIG_HW_STACK_PROTECTION=y -# Enable UART console on all configurations for convenience. This is usually -# done at board level upstream so that all samples output printed messages to -# the configured #zephyr,console. In production boards, this may be enabled at -# application level or in a debug configuration overlay. +# Enable RTT +CONFIG_USE_SEGGER_RTT=y + +# enable GPIO +CONFIG_GPIO=y + +# enable uart driver CONFIG_SERIAL=y + +# enable console CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y - -CONFIG_PINCTRL=y diff --git a/boot.conf b/boot.conf new file mode 100644 index 0000000..284435c --- /dev/null +++ b/boot.conf @@ -0,0 +1,43 @@ +CONFIG_PM=n + +CONFIG_BOOT_SIGNATURE_TYPE_NONE=y + +CONFIG_MAIN_STACK_SIZE=10240 +# CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" + +CONFIG_BOOT_SWAP_SAVE_ENCTLV=n +CONFIG_BOOT_ENCRYPT_IMAGE=n + +CONFIG_BOOT_UPGRADE_ONLY=n +CONFIG_BOOT_BOOTSTRAP=n + +### mbedTLS has its own heap +# CONFIG_HEAP_MEM_POOL_SIZE is not set + +### We never want Zephyr's copy of tinycrypt. If tinycrypt is needed, +### MCUboot has its own copy in tree. +# CONFIG_TINYCRYPT is not set +# CONFIG_TINYCRYPT_ECC_DSA is not set +# CONFIG_TINYCRYPT_SHA256 is not set + +CONFIG_FLASH=y + +### Various Zephyr boards enable features that we don't want. +# CONFIG_BT is not set +# CONFIG_BT_CTLR is not set +# CONFIG_I2C is not set + +CONFIG_LOG=y +CONFIG_LOG_MODE_MINIMAL=y # former CONFIG_MODE_MINIMAL +### Ensure Zephyr logging changes don't use more resources +CONFIG_LOG_DEFAULT_LEVEL=0 +### Use info log level by default +CONFIG_MCUBOOT_LOG_LEVEL_INF=y +### Decrease footprint by ~4 KB in comparison to CBPRINTF_COMPLETE=y +CONFIG_CBPRINTF_NANO=y +### Use the minimal C library to reduce flash usage +CONFIG_MINIMAL_LIBC=y + + +CONFIG_UART_CONSOLE=n +CONFIG_MCUBOOT_SERIAL=y diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index 72ed549..dde47f6 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -2,3 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 add_subdirectory_ifdef(CONFIG_EXAMPLE_SENSOR example_sensor) +add_subdirectory_ifdef(CONFIG_BMX055 bmx055) +add_subdirectory_ifdef(CONFIG_HDC1080 hdc1080) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 07b6c17..60499d0 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -3,4 +3,6 @@ if SENSOR rsource "example_sensor/Kconfig" +rsource "bmx055/Kconfig" +rsource "hdc1080/Kconfig" endif # SENSOR diff --git a/drivers/sensor/bmx055/CMakeLists.txt b/drivers/sensor/bmx055/CMakeLists.txt new file mode 100644 index 0000000..ad9d838 --- /dev/null +++ b/drivers/sensor/bmx055/CMakeLists.txt @@ -0,0 +1,2 @@ +zephyr_library() +zephyr_library_sources(bmx055.c) diff --git a/drivers/sensor/bmx055/Kconfig b/drivers/sensor/bmx055/Kconfig new file mode 100644 index 0000000..f384dad --- /dev/null +++ b/drivers/sensor/bmx055/Kconfig @@ -0,0 +1,17 @@ +config BMX055 + bool "BMX055" + default y + depends on DT_HAS_BOSCH_BMX055_ENABLED + help + Enable driver for BMX055. + +if BMX055 + +config BMX055_TRIGGER + bool "BMX055 trigger mode" + depends on BMX055 + help + Set to enable trigger mode using gpio interrupt, where + interrupts are configured to line ALERT PIN. + +endif # BMX055 diff --git a/drivers/sensor/bmx055/bmx055.c b/drivers/sensor/bmx055/bmx055.c new file mode 100644 index 0000000..2ae878f --- /dev/null +++ b/drivers/sensor/bmx055/bmx055.c @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT bosch_bmx055 + +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(bmx055, CONFIG_SENSOR_LOG_LEVEL); + +// Accelerometer registers +const uint8_t REG_BWG_CHIPID = 0x00; +const uint8_t REG_ACCD_X_LSB = 0x02; +const uint8_t REG_ACCD_X_MSB = 0x03; +const uint8_t REG_ACCD_Y_LSB = 0x04; +const uint8_t REG_ACCD_Y_MSB = 0x05; +const uint8_t REG_ACCD_Z_LSB = 0x06; +const uint8_t REG_ACCD_Z_MSB = 0x07; +const uint8_t REG_ACCD_TEMP = 0x08; +const uint8_t REG_INT_STATUS_0 = 0x09; +const uint8_t REG_INT_STATUS_1 = 0x0A; +const uint8_t REG_INT_STATUS_2 = 0x0B; +const uint8_t REG_INT_STATUS_3 = 0x0C; +const uint8_t REG_FIFO_STATUS = 0x0E; +const uint8_t REG_PMU_RANGE = 0x0F; +typedef enum pmu_range_t +{ + PMU_RANGE_2G = 0b0011, ///< DEFAULT + PMU_RANGE_4G = 0b0101, + PMU_RANGE_8G = 0b1000, + PMU_RANGE_16G = 0b1100, +} pmu_range_t; +const uint8_t REG_PMU_BW = 0x10; +typedef enum pmu_bw_t +{ + PMU_BW_8HZ = 0b01000, + PMU_BW_16HZ = 0b01001, + PMU_BW_31HZ = 0b01010, + PMU_BW_63HZ = 0b01011, + PMU_BW_125HZ = 0b01100, + PMU_BW_250HZ = 0b01101, + PMU_BW_500HZ = 0b01110, + PMU_BW_1000HZ = 0b01111, +} pmu_bw_t; +const uint8_t REG_PMU_LPW = 0x11; +const uint8_t REG_PMU_LOW_POWER = 0x12; +const uint8_t REG_ACCD_HBW = 0x13; +const uint8_t REG_BGW_SOFTRESET = 0x14; +const uint8_t BGW_SOFTRESET = 0xB6; ///< Soft reset occurs when this value is written to REG_BGW_SOFTRESET +const uint8_t REG_INT_EN_0 = 0x16; +const uint8_t REG_INT_EN_1 = 0x17; +const uint8_t REG_INT_EN_2 = 0x18; +const uint8_t REG_INT_MAP_0 = 0x19; +const uint8_t REG_INT_MAP_1 = 0x1A; +const uint8_t REG_INT_MAP_2 = 0x1B; +const uint8_t REG_INT_SRC = 0x1E; +const uint8_t REG_INT_OUT_CTRL = 0x20; +const uint8_t REG_INT_RST_LATCH = 0x21; +const uint8_t REG_INT_0 = 0x22; +const uint8_t REG_INT_1 = 0x23; +const uint8_t REG_INT_2 = 0x24; +const uint8_t REG_INT_3 = 0x25; +const uint8_t REG_INT_4 = 0x26; +const uint8_t REG_INT_5 = 0x27; +const uint8_t REG_INT_6 = 0x28; +const uint8_t REG_INT_7 = 0x29; +const uint8_t REG_INT_8 = 0x2A; +const uint8_t REG_INT_9 = 0x2B; +const uint8_t REG_INT_A = 0x2C; +const uint8_t REG_INT_B = 0x2D; +const uint8_t REG_INT_C = 0x2E; +const uint8_t REG_INT_D = 0x2F; +const uint8_t REG_FIFO_CONFIG_0 = 0x30; +const uint8_t REG_PMU_SELF_TEST = 0x32; +const uint8_t REG_TRIM_NVM_CTRL = 0x33; +const uint8_t REG_BGW_SPI3_WDT = 0x34; +const uint8_t REG_OFC_CTRL = 0x36; +const uint8_t REG_OFC_SETTING = 0x37; +const uint8_t REG_OFC_OFFSET_X = 0x38; +const uint8_t REG_OFC_OFFSET_Y = 0x39; +const uint8_t REG_OFC_OFFSET_Z = 0x3A; +const uint8_t REG_TRIM_GP0 = 0x3B; +const uint8_t REG_TRIM_GP1 = 0x3C; +const uint8_t REG_FIFO_CONFIG_1 = 0x3E; +const uint8_t REG_FIFO_DATA = 0x3F; + +struct bmx055_data +{ + int16_t accel_x; + int16_t accel_y; + int16_t accel_z; + int16_t gyro_x; + int16_t gyro_y; + int16_t gyro_z; + int8_t temperature; +}; + +struct bmx055_config +{ + struct i2c_dt_spec bus; +#ifdef CONFIG_INA230_TRIGGER + bool trig_enabled; + uint16_t mask; + const struct gpio_dt_spec alert_gpio; + uint16_t alert_limit; +#endif /* CONFIG_INA230_TRIGGER */ +}; + +static int bmx055_sample_fetch(const struct device *dev, + enum sensor_channel chan) +{ + const struct bmx055_config *config = dev->config; + struct bmx055_data *data = dev->data; + int ret; + + uint8_t accel[6]; + ret = i2c_burst_read_dt(&config->bus, REG_ACCD_X_LSB, accel, sizeof(accel)); + if (ret < 0) + { + LOG_ERR("Failed to read acceleration registers!"); + return ret; + } + data->accel_x = ((int16_t)sys_get_le16(&accel[0])) >> 4; + data->accel_y = ((int16_t)sys_get_le16(&accel[2])) >> 4; + data->accel_z = ((int16_t)sys_get_le16(&accel[4])) >> 4; + + ret = i2c_burst_read_dt(&config->bus, REG_ACCD_TEMP, &data->temperature, sizeof(data->temperature)); + if (ret < 0) + { + LOG_ERR("Failed to read temperature register!"); + return ret; + } + + return 0; +} + +static int bmx055_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct bmx055_data *data = dev->data; + + switch (chan) + { + case SENSOR_CHAN_DIE_TEMP: + // 0.5K/LSB, center temperature is 23C + val->val1 = 23 + data->temperature / 2; + val->val2 = 0; // TODO: don't throw out LSB + break; + case SENSOR_CHAN_ACCEL_X: + { + float accel = data->accel_x * 0.00098 * 9.80665; // to gees, to m/s^2 + val->val1 = accel; + val->val2 = (accel - val->val1) * 1000000; + break; + } + case SENSOR_CHAN_ACCEL_Y: + { + float accel = data->accel_y * 0.00098 * 9.80665; // to gees, to m/s^2 + val->val1 = accel; + val->val2 = (accel - val->val1) * 1000000; + break; + } + case SENSOR_CHAN_ACCEL_Z: + { + // For now assume 2g since that's the default value + // 2g 0.98mg/LSB + // 4g 1.95mg/LSB + // 8g 3.91mg/LSB + // 16g 7.81mg/LSB + // 1 g = 9.80665 m/s^2 + float accel = data->accel_z * 0.00098 * 9.80665; // to gees, to m/s^2 + val->val1 = accel; + val->val2 = (accel - val->val1) * 1000000; + break; + } + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + return -ENOTSUP; + case SENSOR_CHAN_MAGN_X: + case SENSOR_CHAN_MAGN_Y: + case SENSOR_CHAN_MAGN_Z: + return -ENOTSUP; + default: + return -ENOTSUP; + } + + return 0; +} + +static const struct sensor_driver_api bmx055_api = { + .sample_fetch = &bmx055_sample_fetch, + .channel_get = &bmx055_channel_get, +}; + +static int bmx055_init(const struct device *dev) +{ + const struct bmx055_config *const config = dev->config; + int ret; + + if (!device_is_ready(config->bus.bus)) + { + LOG_ERR("I2C bus %s is not ready", config->bus.bus->name); + return -ENODEV; + } + + uint8_t chip_id; + ret = i2c_burst_read_dt(&config->bus, REG_BWG_CHIPID, &chip_id, sizeof(chip_id)); + if (ret < 0) + { + LOG_ERR("Failed to read chip ID register!"); + return ret; + } + const uint8_t CHIP_ID = 0xfa; + if (chip_id != CHIP_ID) + { + LOG_ERR("Chip ID read from %s incorrect. Read 0x%02X, expected 0x%02X", dev->name, chip_id, CHIP_ID); + } + + // // Reset the sensor + // uint8_t reset_val = BGW_SOFTRESET; + // i2c_burst_write_dt(&config->bus, REG_BGW_SOFTRESET, &reset_val, sizeof(reset_val)); + + // // Wait for device to reset + + uint8_t lpw; + ret = i2c_burst_read_dt(&config->bus, REG_PMU_LPW, &lpw, sizeof(lpw)); + if (ret < 0) + { + LOG_ERR("Failed to read LPW register!"); + return ret; + } + LOG_INF("LPW register: 0x%02X", lpw); + + // Write configuration + uint8_t accel_range = PMU_RANGE_2G; + i2c_burst_write_dt(&config->bus, REG_PMU_RANGE, &accel_range, sizeof(accel_range)); + + uint8_t accel_bw = PMU_BW_8HZ; + i2c_burst_write_dt(&config->bus, REG_PMU_BW, &accel_bw, sizeof(accel_bw)); + + uint8_t hbw = (1 << 7); // data_high_bw (read filtered data) and enable lsb/msb shadowing + i2c_burst_write_dt(&config->bus, REG_ACCD_HBW, &hbw, sizeof(hbw)); + + return 0; +} + +#define BMX055_INIT(i) \ + static struct bmx055_data bmx055_data_##i; \ + \ + static const struct bmx055_config bmx055_config_##i = { \ + .bus = I2C_DT_SPEC_INST_GET(i), \ + }; \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(i, &bmx055_init, NULL, \ + &bmx055_data_##i, \ + &bmx055_config_##i, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &bmx055_api); + +DT_INST_FOREACH_STATUS_OKAY(BMX055_INIT) diff --git a/drivers/sensor/hdc1080/CMakeLists.txt b/drivers/sensor/hdc1080/CMakeLists.txt new file mode 100644 index 0000000..cafff00 --- /dev/null +++ b/drivers/sensor/hdc1080/CMakeLists.txt @@ -0,0 +1,2 @@ +zephyr_library() +zephyr_library_sources(hdc1080.c) diff --git a/drivers/sensor/hdc1080/Kconfig b/drivers/sensor/hdc1080/Kconfig new file mode 100644 index 0000000..6e337a7 --- /dev/null +++ b/drivers/sensor/hdc1080/Kconfig @@ -0,0 +1,6 @@ +config HDC1080 + bool "HDC1080" + default y + depends on DT_HAS_TI_HDC1080_ENABLED + help + Enable driver for HDC1080. diff --git a/drivers/sensor/hdc1080/hdc1080.c b/drivers/sensor/hdc1080/hdc1080.c new file mode 100644 index 0000000..aaa9557 --- /dev/null +++ b/drivers/sensor/hdc1080/hdc1080.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_hdc1080 + +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(hdc1080, CONFIG_SENSOR_LOG_LEVEL); + +const uint8_t REG_TEMPERATURE = 0x00; +const uint8_t REG_HUMIDITY = 0x01; +const uint8_t REG_CONFIGURATION = 0x02; +const uint8_t REG_SERIAL_ID_0 = 0xFB; +const uint8_t REG_SERIAL_ID_1 = 0xFC; +const uint8_t REG_SERIAL_ID_2 = 0xFD; +const uint8_t REG_MANUFACTURER_ID = 0xFE; +const uint8_t REG_DEVICE_ID = 0xFF; + +#define CONFIG_RST (1 << 15) ///< Software reset. This bit self clears +#define CONFIG_HEAT (1 << 13) ///< Enable heater +#define CONFIG_MODE_SEQUENTIAL (1 << 12) ///< Temperature and humidity measured in sequence, temperature first +#define CONFIG_BTST (1 << 11) ///< 1 = battery voltage < 2.8V +#define CONFIG_TRES_14BIT (0 << 10) ///< Temperature resolution +#define CONFIG_TRES_11BIT (1 << 10) ///< Temperature resolution +#define CONFIG_HRES_14BIT (0b00 << 8) ///< Humidity resolution +#define CONFIG_HRES_11BIT (0b01 << 8) ///< Humidity resolution +#define CONFIG_HRES_8BIT (0b10 << 8) ///< Humidity resolution + +struct hdc1080_data +{ + int16_t temperature; + uint16_t humidity; +}; + +struct hdc1080_config +{ + struct i2c_dt_spec bus; + uint8_t temperature_bits; + uint8_t humidity_bits; +}; + +static int hdc1080_sample_fetch(const struct device *dev, + enum sensor_channel chan) +{ + const struct hdc1080_config *config = dev->config; + struct hdc1080_data *data = dev->data; + int ret; + + // Trigger measurement + uint8_t reg_addr = REG_TEMPERATURE; + i2c_write_dt(&config->bus, ®_addr, 1); + + // Wait for conversion + float temperature_conversion_time; ///< microseconds + switch (config->temperature_bits) + { + case 11: + temperature_conversion_time = 3650; + break; + case 14: + temperature_conversion_time = 6350; + break; + default: + temperature_conversion_time = 0; + break; + } + float humidity_conversion_time; ///< microseconds + switch (config->humidity_bits) + { + case 8: + humidity_conversion_time = 2500; + break; + case 11: + humidity_conversion_time = 3850; + break; + case 14: + humidity_conversion_time = 6500; + break; + default: + humidity_conversion_time = 0; + break; + } + k_busy_wait(temperature_conversion_time + humidity_conversion_time); + + // Read data + uint8_t rdata[4]; + ret = i2c_read_dt(&config->bus, rdata, sizeof(rdata)); + if (ret < 0) + { + LOG_ERR("Failed to read temperature and humidity!"); + return ret; + } + data->temperature = sys_get_be16(&rdata[0]); + data->humidity = sys_get_be16(&rdata[2]); + + return 0; +} + +static int hdc1080_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct hdc1080_data *data = dev->data; + + switch (chan) + { + case SENSOR_CHAN_AMBIENT_TEMP: + { + double temperature = data->temperature * 160.0 / (1 << 16) - 40; + val->val1 = temperature; + val->val2 = (temperature - val->val1) * 1e6; + break; + } + case SENSOR_CHAN_HUMIDITY: + { + double humidity = data->humidity * 100.0 / (1 << 16); + val->val1 = humidity; + val->val2 = (humidity - val->val1) * 1e6; + break; + } + default: + return -ENOTSUP; + } + + return 0; +} + +static const struct sensor_driver_api hdc1080_api = { + .sample_fetch = &hdc1080_sample_fetch, + .channel_get = &hdc1080_channel_get, +}; + +static int hdc1080_init(const struct device *dev) +{ + const struct hdc1080_config *const config = dev->config; + int ret; + + if (!device_is_ready(config->bus.bus)) + { + LOG_ERR("I2C bus %s is not ready", config->bus.bus->name); + return -ENODEV; + } + + uint8_t id[2]; + ret = i2c_burst_read_dt(&config->bus, REG_MANUFACTURER_ID, id, sizeof(id)); + if (ret < 0) + { + LOG_ERR("Failed to read manufacturer/device ID registers"); + return ret; + } + uint16_t manufacturer_id = sys_get_be16(&id[0]); + const uint16_t MANUFACTURER_ID = 0x5449; + if (manufacturer_id != MANUFACTURER_ID) + { + LOG_ERR("Manufacturer ID read from %s incorrect. Read 0x%02X, expected 0x%02X", dev->name, manufacturer_id, MANUFACTURER_ID); + } + + ret = i2c_burst_read_dt(&config->bus, REG_DEVICE_ID, id, sizeof(id)); + if (ret < 0) + { + LOG_ERR("Failed to read manufacturer/device ID registers"); + return ret; + } + uint16_t device_id = sys_get_be16(&id[0]); + const uint16_t DEVICE_ID = 0x1050; + if (device_id != DEVICE_ID) + { + LOG_ERR("Device ID read from %s incorrect. Read 0x%02X, expected 0x%02X", dev->name, device_id, DEVICE_ID); + } + + // Write configuration + uint16_t cfg = 0; + switch (config->temperature_bits) + { + case 14: + cfg |= CONFIG_TRES_14BIT; + break; + case 11: + cfg |= CONFIG_TRES_11BIT; + break; + default: + LOG_ERR("Invalid temperature resolution: %d bits", config->temperature_bits); + return -1; + } + switch (config->humidity_bits) + { + case 14: + cfg |= CONFIG_HRES_14BIT; + break; + case 11: + cfg |= CONFIG_HRES_11BIT; + break; + case 8: + cfg |= CONFIG_HRES_8BIT; + break; + default: + LOG_ERR("Invalid humidity resolution: %d bits", config->humidity_bits); + return -1; + } + uint8_t wdata[2]; + sys_put_be16(cfg, wdata); + i2c_burst_write_dt(&config->bus, REG_CONFIGURATION, wdata, sizeof(wdata)); + + return 0; +} + +#define HDC1080_INIT(i) \ + static struct hdc1080_data hdc1080_data_##i; \ + \ + static const struct hdc1080_config hdc1080_config_##i = { \ + .bus = I2C_DT_SPEC_INST_GET(i), \ + .temperature_bits = 14, \ + .humidity_bits = 14, \ + }; \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(i, &hdc1080_init, NULL, \ + &hdc1080_data_##i, \ + &hdc1080_config_##i, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &hdc1080_api); + +DT_INST_FOREACH_STATUS_OKAY(HDC1080_INIT) diff --git a/dts/bindings/sensor/bosch,bmx055.yaml b/dts/bindings/sensor/bosch,bmx055.yaml new file mode 100644 index 0000000..37a2cfb --- /dev/null +++ b/dts/bindings/sensor/bosch,bmx055.yaml @@ -0,0 +1,12 @@ +description: | + TODO: add a description + +compatible: "bosch,bmx055" + +include: [sensor-device.yaml, i2c-device.yaml] + +# properties: +# input-gpios: +# type: phandle-array +# required: true +# description: Input GPIO to be sensed. diff --git a/dts/bindings/sensor/ti,hdc1080.yaml b/dts/bindings/sensor/ti,hdc1080.yaml new file mode 100644 index 0000000..c3ee95b --- /dev/null +++ b/dts/bindings/sensor/ti,hdc1080.yaml @@ -0,0 +1,7 @@ +description: | + TODO: add a description + +compatible: "ti,hdc1080" + +include: [sensor-device.yaml, i2c-device.yaml] + diff --git a/west.yml b/west.yml index 6bb551c..13c8dbe 100644 --- a/west.yml +++ b/west.yml @@ -8,11 +8,14 @@ manifest: remotes: - name: zephyrproject-rtos url-base: https://github.com/zephyrproject-rtos + - name: mcu-tools + url-base: https://github.com/mcu-tools projects: - name: zephyr remote: zephyrproject-rtos revision: main + # revision: v3.6.0 import: # By using name-allowlist we can clone only the modules that are # strictly needed by the application. @@ -20,3 +23,7 @@ manifest: - cmsis # required by the ARM port - hal_nordic # required by the custom_plank board (Nordic based) - hal_stm32 # required by the nucleo_f302r8 board (STM32 based) + - zcbor # required by mcuboot serial + - name: mcuboot + remote: mcu-tools + revision: main