Switched to Official SDK
This commit is contained in:
1 parent
224c50821c
commit
5cb31298d6
8 files changed
+257
-139
No files matched your search
Binary file not shown.
@@ -1,7 +1,9 @@
|
|||||||
# LYWSD03MMC 2025 B1.5 (pvvx B1.1) BTHome v2 beacon + LCD + OTA
|
# LYWSD03MMC 2025 B1.5 (pvvx B1.1) BTHome v2 beacon + LCD + OTA
|
||||||
|
|
||||||
Port of the nRF52840/Zephyr SHT41 beacon to the Xiaomi LYWSD03MMC
|
Port of the nRF52840/Zephyr SHT41 beacon to the Xiaomi LYWSD03MMC
|
||||||
(Telink TLSR8251). Same behaviour:
|
(Telink TLSR8251), built on the official Telink BLE SDK
|
||||||
|
([tc_ble_single_sdk](https://github.com/telink-semi/tc_ble_single_sdk)
|
||||||
|
V3.4.3.0). Same behaviour:
|
||||||
|
|
||||||
- every 60 s: measure temperature/humidity, read the battery right after the
|
- every 60 s: measure temperature/humidity, read the battery right after the
|
||||||
sensor load, update the advertisement
|
sensor load, update the advertisement
|
||||||
@@ -12,8 +14,9 @@ Port of the nRF52840/Zephyr SHT41 beacon to the Xiaomi LYWSD03MMC
|
|||||||
it in the scan response only, as pvvx does)
|
it in the scan response only, as pvvx does)
|
||||||
- a failed sensor read keeps advertising the last good T/RH; the voltage is
|
- a failed sensor read keeps advertising the last good T/RH; the voltage is
|
||||||
updated on every measurement
|
updated on every measurement
|
||||||
- Telink OTA service for over-the-air firmware updates, reachable only
|
- Telink OTA service (the SDK's OTA server) for over-the-air firmware
|
||||||
during the first 2 minutes after the battery is inserted (see Flashing)
|
updates, reachable only during the first 2 minutes after the battery is
|
||||||
|
inserted (see Flashing)
|
||||||
|
|
||||||
plus the display: temperature (0.1 °C, `°C`), humidity (`%`), battery icon
|
plus the display: temperature (0.1 °C, `°C`), humidity (`%`), battery icon
|
||||||
below 2.4 V, `-- --` until the first good reading. The LCD controller holds
|
below 2.4 V, `-- --` until the first good reading. The LCD controller holds
|
||||||
@@ -60,19 +63,53 @@ pvvx/ATC_MiThermometer.
|
|||||||
- The sensor is wired straight to the coin cell, so there is no power gating;
|
- The sensor is wired straight to the coin cell, so there is no power gating;
|
||||||
the SHTV3 is put to sleep explicitly after every measurement.
|
the SHTV3 is put to sleep explicitly after every measurement.
|
||||||
- Voltage is VDD (= CR2032), measured by driving the unbonded PB0 pad high
|
- Voltage is VDD (= CR2032), measured by driving the unbonded PB0 pad high
|
||||||
and sampling it with the ADC. Uses the typical 1175 mV reference, so expect
|
and sampling it with the ADC. The ADC reference is the chip's factory
|
||||||
a few % absolute error; adjust `ADC_VREF_MV` against a meter if needed.
|
calibration, which the SDK loads from the calibration sector or eFuse
|
||||||
|
(typical 1175 mV when there is none). To trim against a meter, set
|
||||||
|
`ADC_VREF_MV` to a fixed value in mV.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
|
Builds on Linux x86-64 only (the SDK's post-build tool is a Linux x86-64
|
||||||
|
binary). Official Telink SDK, used unmodified, plus the TC32 toolchain:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/pvvx/ATC_MiThermometer ../ATC_MiThermometer
|
git clone -b release/V3.4.3.0 https://github.com/telink-semi/tc_ble_single_sdk ../tc_ble_single_sdk
|
||||||
|
# apply the official patches as tc_ble_single_sdk_v3.4.3.0_Patch/patch_*/Patch_Guide.txt
|
||||||
|
# says (copy each patch folder over tc_ble_single_sdk/, 0001 then 0002)
|
||||||
# TC32 toolchain (Linux): e.g. https://github.com/flyskywhy/tc32 branch "linux"
|
# TC32 toolchain (Linux): e.g. https://github.com/flyskywhy/tc32 branch "linux"
|
||||||
ln -s /path/to/tc32 ../ATC_MiThermometer/SDK/tools/linux/tc32
|
make TC32_PATH=/path/to/tc32/bin/
|
||||||
make
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Produces `LYWSD03MMC-BTHome.bin` (with the Telink CRC appended).
|
`SDK_PATH` defaults to `../tc_ble_single_sdk/tc_ble_single_sdk`. The
|
||||||
|
makefile uses the same compiler flags, startup code
|
||||||
|
(`boot/B85/cstartup_825x.S`, `MCU_STARTUP_8251`: 32 KiB SRAM), linker
|
||||||
|
script (`project/tlsr_tc32/B85/boot.link`), libraries (`lt_825x`,
|
||||||
|
`lt_general_stack`) and post-build tool (`script/tl_check_fw/check_fw`)
|
||||||
|
as the SDK's own `825x_ble_sample` project. Because the SDK only
|
||||||
|
looks up `app_config.h` for its own demo projects, `src/app_config.h` is
|
||||||
|
force-included (`-include`) into every file, SDK sources included.
|
||||||
|
|
||||||
|
Produces `LYWSD03MMC-BTHome.bin` (with the Telink CRC appended) and prints
|
||||||
|
the memory use: about 54 KiB of image, 17.8 KiB of RAM code + retained data
|
||||||
|
(so 32 KiB deep-sleep retention, as before), 21 KiB of the 32 KiB SRAM.
|
||||||
|
|
||||||
|
### Changes from the pvvx-SDK build
|
||||||
|
|
||||||
|
- RF/clock bring-up, calibration loading
|
||||||
|
(`blc_app_loadCustomizedParameters_normal()` / `_deepRetn()`), retention
|
||||||
|
size selection and the OTA server API are the official ones.
|
||||||
|
- The power-on marker moved from `DEEP_ANA_REG0` to `DEEP_ANA_REG1`: the
|
||||||
|
official SDK reserves bit 1 of `DEEP_ANA_REG0` for its crystal start-up
|
||||||
|
failure flag.
|
||||||
|
- OTA: inactivity timeout 16 s (`OTA_PACKET_TIMEOUT_S`), whole upload at
|
||||||
|
most 1000 s (`OTA_PROCESS_TIMEOUT_S`; the SDK default would abort after
|
||||||
|
30 s); images up to 124 KiB (SDK limit). The OTA characteristic gained
|
||||||
|
NOTIFY and a CCC descriptor, as in the SDK sample, because the official
|
||||||
|
server answers the extended OTA protocol on it.
|
||||||
|
- Flash write protection is still cleared once per power-on (status
|
||||||
|
register := 0, as pvvx), now with the SDK's flash driver and only when a
|
||||||
|
protection bit is set.
|
||||||
|
|
||||||
## Flashing
|
## Flashing
|
||||||
|
|
||||||
@@ -81,7 +118,7 @@ Produces `LYWSD03MMC-BTHome.bin` (with the Telink CRC appended).
|
|||||||
"Select Firmware" → custom file. Units running the previous build of this
|
"Select Firmware" → custom file. Units running the previous build of this
|
||||||
firmware (no OTA) need one wired flash: USB-UART adapter on the SWS pad,
|
firmware (no OTA) need one wired flash: USB-UART adapter on the SWS pad,
|
||||||
`TlsrComProg`/`ATCtelink.py` from the pvvx repo.
|
`TlsrComProg`/`ATCtelink.py` from the pvvx repo.
|
||||||
- **Updates** (this firmware → any Telink image ≤ 128 KiB, including back to
|
- **Updates** (this firmware → any Telink image ≤ 124 KiB, including back to
|
||||||
pvvx or stock), with https://pvvx.github.io/ATC_MiThermometer/TelinkOTA.html:
|
pvvx or stock), with https://pvvx.github.io/ATC_MiThermometer/TelinkOTA.html:
|
||||||
1. Take the battery out, wait ~5 s, put it back. The BLE symbol on the
|
1. Take the battery out, wait ~5 s, put it back. The BLE symbol on the
|
||||||
LCD means the OTA window is open: for 2 minutes the device accepts
|
LCD means the OTA window is open: for 2 minutes the device accepts
|
||||||
@@ -97,12 +134,14 @@ Produces `LYWSD03MMC-BTHome.bin` (with the Telink CRC appended).
|
|||||||
|
|
||||||
The new image is written to the inactive flash bank and only activated
|
The new image is written to the inactive flash bank and only activated
|
||||||
after its CRC checks out, so an interrupted upload leaves the current
|
after its CRC checks out, so an interrupted upload leaves the current
|
||||||
firmware running. The transfer aborts after 16 s without data.
|
firmware running. The transfer aborts after 16 s without data or after
|
||||||
|
1000 s in total.
|
||||||
|
|
||||||
The window opens only on a real power cycle. Reboots without one
|
The window opens only on a real power cycle. Reboots without one
|
||||||
(low-battery sleep, OTA reboot, crash) keep it closed. After installing
|
(low-battery sleep, OTA reboot, crash) keep it closed. Exception: the
|
||||||
this firmware by OTA from pvvx firmware, power-cycle once before the next
|
first boot after installing this build by OTA from other firmware (stock,
|
||||||
update.
|
pvvx, or the earlier pvvx-SDK build of this firmware) opens the window
|
||||||
|
once, because the marker register was never set.
|
||||||
|
|
||||||
**OTA itself is unauthenticated**: anyone in Bluetooth range can upload
|
**OTA itself is unauthenticated**: anyone in Bluetooth range can upload
|
||||||
firmware while the window is open, so it is limited to 2 minutes after a
|
firmware while the window is open, so it is limited to 2 minutes after a
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
# LYWSD03MMC B1.5 BTHome v2 beacon + LCD
|
# LYWSD03MMC B1.5 BTHome v2 beacon + LCD + OTA
|
||||||
#
|
#
|
||||||
# Builds against the Telink TLSR825x BLE SDK as shipped in
|
# Builds against the official Telink BLE SDK for TLSR825x (B85m):
|
||||||
# pvvx/ATC_MiThermometer, reusing its startup code, linker script and
|
# git clone -b release/V3.4.3.0 https://github.com/telink-semi/tc_ble_single_sdk ../tc_ble_single_sdk
|
||||||
# post-build scripts:
|
# (apply the patches in tc_ble_single_sdk_v3.4.3.0_Patch/ as Patch_Guide.txt says)
|
||||||
# git clone https://github.com/pvvx/ATC_MiThermometer ../ATC_MiThermometer
|
# make TC32_PATH=/path/to/tc32/bin/
|
||||||
# make ATC_PATH=../ATC_MiThermometer
|
#
|
||||||
# TC32 toolchain: $(TEL_PATH)/tools/linux/tc32/bin (or set TC32_PATH)
|
# Same compiler flags, startup code, linker script, libraries and post-build
|
||||||
|
# step as the SDK's own 825x_ble_sample project (project/tlsr_tc32/B85/.cproject).
|
||||||
|
# The SDK itself is used unmodified.
|
||||||
|
|
||||||
PROJECT_NAME ?= LYWSD03MMC-BTHome
|
PROJECT_NAME ?= LYWSD03MMC-BTHome
|
||||||
ATC_PATH ?= ../ATC_MiThermometer
|
SDK_PATH ?= ../tc_ble_single_sdk/tc_ble_single_sdk
|
||||||
TEL_PATH ?= $(ATC_PATH)/SDK
|
TC32_PATH ?=
|
||||||
TC32_PATH ?= $(TEL_PATH)/tools/linux/tc32/bin/
|
|
||||||
PYTHON ?= python3
|
|
||||||
|
|
||||||
SRC_PATH := ./src
|
SRC_PATH := ./src
|
||||||
OUT_PATH := ./out
|
OUT_PATH := ./out
|
||||||
ATC_SRC := $(ATC_PATH)/src
|
|
||||||
|
|
||||||
ifeq ($(wildcard $(TEL_PATH)/components/drivers/8258/gpio_8258.c),)
|
ifneq ($(MAKECMDGOALS),clean)
|
||||||
$(error SDK not found: set ATC_PATH or TEL_PATH)
|
ifeq ($(wildcard $(SDK_PATH)/boot/B85/cstartup_825x.S),)
|
||||||
|
$(error Telink SDK not found: set SDK_PATH to the tc_ble_single_sdk/tc_ble_single_sdk directory)
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CC := $(TC32_PATH)tc32-elf-gcc
|
CC := $(TC32_PATH)tc32-elf-gcc
|
||||||
@@ -26,62 +27,77 @@ LD := $(TC32_PATH)tc32-elf-ld
|
|||||||
OBJCOPY := $(TC32_PATH)tc32-elf-objcopy
|
OBJCOPY := $(TC32_PATH)tc32-elf-objcopy
|
||||||
NM := $(TC32_PATH)tc32-elf-nm
|
NM := $(TC32_PATH)tc32-elf-nm
|
||||||
|
|
||||||
CFLAGS := -DCHIP_TYPE=CHIP_TYPE_8258 -DMCU_STARTUP_825X \
|
# TLSR8251: 32 KiB SRAM (stack top 0x848000)
|
||||||
|
STARTUP := MCU_STARTUP_8251
|
||||||
|
|
||||||
|
INCLUDES := -I$(SDK_PATH) -I$(SDK_PATH)/vendor/common -I$(SDK_PATH)/common \
|
||||||
|
-I$(SDK_PATH)/drivers/B85 -I$(SRC_PATH)
|
||||||
|
|
||||||
|
# -include: the SDK selects app_config.h only for its own demo projects
|
||||||
|
# (vendor/common/user_config.h), so ours is forced into every file
|
||||||
|
CFLAGS := -DCHIP_TYPE=CHIP_TYPE_825x -D$(STARTUP)=1 \
|
||||||
-ffunction-sections -fdata-sections -Wall -O2 -fpack-struct \
|
-ffunction-sections -fdata-sections -Wall -O2 -fpack-struct \
|
||||||
-fshort-enums -finline-small-functions -std=gnu99 -funsigned-char \
|
-fshort-enums -finline-small-functions -std=gnu99 \
|
||||||
-fshort-wchar -fms-extensions \
|
-fshort-wchar -fms-extensions \
|
||||||
-I$(TEL_PATH)/components -I$(SRC_PATH)
|
$(INCLUDES) -include $(SRC_PATH)/app_config.h
|
||||||
|
|
||||||
APP_SRCS := main.c ble.c i2c.c sensor.c battery.c lcd.c
|
ASFLAGS := -D$(STARTUP)=1 $(INCLUDES)
|
||||||
DRV_SRCS := adc.c analog.c bsp.c clock.c flash.c gpio_8258.c i2c.c lpc.c rf_pa.c timer.c uart.c
|
|
||||||
COM_SRCS := string.c utility.c
|
|
||||||
|
|
||||||
OBJS := \
|
APP_SRCS := $(wildcard $(SRC_PATH)/*.c)
|
||||||
$(OUT_PATH)/boot/cstartup_825x.o \
|
|
||||||
$(OUT_PATH)/boot/div_mod.o \
|
# Everything the SDK's ble_sample compiles from the SDK (minus USB/audio
|
||||||
$(addprefix $(OUT_PATH)/app/,$(APP_SRCS:.c=.o)) \
|
# code, which this device has no use for); --gc-sections keeps only what
|
||||||
$(addprefix $(OUT_PATH)/drv/,$(DRV_SRCS:.c=.o)) \
|
# is referenced
|
||||||
$(addprefix $(OUT_PATH)/common/,$(COM_SRCS:.c=.o)) \
|
SDK_DIRS := drivers/B85 drivers/B85/driver_ext drivers/B85/flash common vendor/common
|
||||||
$(OUT_PATH)/vendor/blt_common.o
|
SDK_SRCS := $(foreach d,$(SDK_DIRS),$(wildcard $(SDK_PATH)/$(d)/*.c))
|
||||||
|
SDK_ASMS := $(SDK_PATH)/boot/B85/cstartup_825x.S $(SDK_PATH)/common/div_mod.S
|
||||||
|
|
||||||
|
OBJS := $(patsubst $(SRC_PATH)/%.c,$(OUT_PATH)/app/%.o,$(APP_SRCS)) \
|
||||||
|
$(patsubst $(SDK_PATH)/%.c,$(OUT_PATH)/sdk/%.o,$(SDK_SRCS)) \
|
||||||
|
$(patsubst $(SDK_PATH)/%.S,$(OUT_PATH)/sdk/%.o,$(SDK_ASMS))
|
||||||
|
|
||||||
|
LDSCRIPT := $(SDK_PATH)/project/tlsr_tc32/B85/boot.link
|
||||||
|
LIBS := -L$(SDK_PATH)/proj_lib -llt_825x -llt_general_stack
|
||||||
|
|
||||||
ELF := $(OUT_PATH)/$(PROJECT_NAME).elf
|
ELF := $(OUT_PATH)/$(PROJECT_NAME).elf
|
||||||
BIN := $(PROJECT_NAME).bin
|
BIN := $(PROJECT_NAME).bin
|
||||||
|
|
||||||
|
# Post-build: the SDK's check_fw (Linux x86-64 binary) pads the image, sets
|
||||||
|
# the size and marker in the header and appends the CRC the OTA server
|
||||||
|
# checks. Copied to out/ because the SDK checkout does not mark it executable.
|
||||||
|
CHECK_FW = cp $(SDK_PATH)/script/tl_check_fw/check_fw $(OUT_PATH)/check_fw && \
|
||||||
|
chmod +x $(OUT_PATH)/check_fw && $(OUT_PATH)/check_fw $(BIN)
|
||||||
|
|
||||||
all: $(BIN)
|
all: $(BIN)
|
||||||
|
|
||||||
$(OUT_PATH)/app/%.o: $(SRC_PATH)/%.c $(wildcard $(SRC_PATH)/*.h)
|
$(OUT_PATH)/app/%.o: $(SRC_PATH)/%.c $(wildcard $(SRC_PATH)/*.h)
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(OUT_PATH)/drv/%.o: $(TEL_PATH)/components/drivers/8258/%.c
|
$(OUT_PATH)/sdk/%.o: $(SDK_PATH)/%.c $(SRC_PATH)/app_config.h
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
@echo CC $<
|
||||||
|
@$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(OUT_PATH)/common/%.o: $(TEL_PATH)/components/common/%.c
|
$(OUT_PATH)/sdk/%.o: $(SDK_PATH)/%.S
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(ASFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(OUT_PATH)/vendor/%.o: $(TEL_PATH)/components/vendor/common/%.c
|
|
||||||
@mkdir -p $(dir $@)
|
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
$(OUT_PATH)/boot/%.o: $(ATC_SRC)/%.S
|
|
||||||
@mkdir -p $(dir $@)
|
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
# Two-pass link: the second pass places code after the retention RAM area
|
|
||||||
$(ELF): $(OBJS)
|
$(ELF): $(OBJS)
|
||||||
$(LD) --gc-sections -L $(TEL_PATH)/components/proj_lib -T $(ATC_SRC)/boot.link \
|
$(LD) --gc-sections -T $(LDSCRIPT) -o $@ $(OBJS) $(LIBS)
|
||||||
-o $@ $(OBJS) -llt_8258
|
@$(NM) $@ | awk ' \
|
||||||
$(LD) --gc-sections -Ttext `$(PYTHON) $(ATC_SRC)/TlsrRetMemAddr.py -e $@ -t $(NM)` \
|
function hex(s, i, n) { n = 0; for (i = 1; i <= length(s); i++) \
|
||||||
-L $(TEL_PATH)/components/proj_lib -T $(ATC_SRC)/boot.link \
|
n = n * 16 + index("0123456789abcdef", substr(tolower(s), i, 1)) - 1; return n } \
|
||||||
-o $@ $(OBJS) -llt_8258
|
$$3 == "_rstored_" { r = hex($$1) } \
|
||||||
@$(PYTHON) $(ATC_SRC)/TlsrMemInfo.py -s 32768 -t $(NM) $@
|
$$3 == "_retention_size_" { t = hex($$1) } \
|
||||||
|
$$3 == "_bin_size_" { b = hex($$1) } \
|
||||||
|
$$3 == "_ram_use_end_" { e = hex($$1) - 8650752 } \
|
||||||
|
END { printf "RAM code %d B, retention (code + data) %d B -> %s deep retention, RAM used %d B of 32768, image %d B\n", \
|
||||||
|
r, t, (t < 16384 ? "16K" : "32K"), e, b }'
|
||||||
|
|
||||||
# Raw image + Telink CRC (required by the OTA bootloader)
|
|
||||||
$(BIN): $(ELF)
|
$(BIN): $(ELF)
|
||||||
$(OBJCOPY) -O binary $< $@
|
$(OBJCOPY) -O binary $< $@
|
||||||
$(PYTHON) $(ATC_PATH)/utils/tl_check_fw.py $@
|
$(CHECK_FW)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(OUT_PATH) $(BIN)
|
rm -rf $(OUT_PATH) $(BIN)
|
||||||
|
|||||||
+37
-14
@@ -11,16 +11,18 @@
|
|||||||
* to measure VDD (= CR2032 voltage)
|
* to measure VDD (= CR2032 voltage)
|
||||||
* PD7 LCD SPI-CLK
|
* PD7 LCD SPI-CLK
|
||||||
* PB7 LCD SPI-SDI
|
* PB7 LCD SPI-SDI
|
||||||
|
*
|
||||||
|
* Built against the official Telink tc_ble_single_sdk. The makefile
|
||||||
|
* force-includes this file into every translation unit (-include), SDK
|
||||||
|
* sources included, because the SDK's vendor/common/user_config.h only
|
||||||
|
* knows its own demo projects. That is how gpio_init() in the SDK sees the
|
||||||
|
* PB0 settings below.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/* System clock. vendor/common/app_common.h derives SYS_CLK_TYPE and
|
||||||
|
* CLOCK_SYS_CLOCK_1S/1MS/1US from this; do not define them here. */
|
||||||
#define CLOCK_SYS_CLOCK_HZ 24000000
|
#define CLOCK_SYS_CLOCK_HZ 24000000
|
||||||
#define SYS_CLK_TYPE SYS_CLK_24M_Crystal
|
|
||||||
enum {
|
|
||||||
CLOCK_SYS_CLOCK_1S = CLOCK_SYS_CLOCK_HZ,
|
|
||||||
CLOCK_SYS_CLOCK_1MS = (CLOCK_SYS_CLOCK_1S / 1000),
|
|
||||||
CLOCK_SYS_CLOCK_1US = (CLOCK_SYS_CLOCK_1S / 1000000),
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Low-power busy wait: CPU stalled, woken by timer0 */
|
/* Low-power busy wait: CPU stalled, woken by timer0 */
|
||||||
#define pm_wait_us(t) cpu_stall_wakeup_by_timer0((t) * CLOCK_SYS_CLOCK_1US)
|
#define pm_wait_us(t) cpu_stall_wakeup_by_timer0((t) * CLOCK_SYS_CLOCK_1US)
|
||||||
@@ -29,6 +31,19 @@ enum {
|
|||||||
/* Variables that must survive deep-sleep retention */
|
/* Variables that must survive deep-sleep retention */
|
||||||
#define RAM _attribute_data_retention_
|
#define RAM _attribute_data_retention_
|
||||||
|
|
||||||
|
/* ---- SDK feature switches ---- */
|
||||||
|
#define BLE_APP_PM_ENABLE 1
|
||||||
|
#define PM_DEEPSLEEP_RETENTION_ENABLE 1 /* also feeds the boot.link check */
|
||||||
|
#define BLE_APP_SECURITY_ENABLE 0 /* no pairing: OTA service only */
|
||||||
|
#define APP_FLASH_PROTECTION_ENABLE 0 /* see flash_clear_write_protection() */
|
||||||
|
#define UART_PRINT_DEBUG_ENABLE 0
|
||||||
|
#define MODULE_WATCHDOG_ENABLE 0
|
||||||
|
|
||||||
|
/* No Telink evaluation board: a value that matches none of the boards in
|
||||||
|
* vendor/common/boards, so default_config.h pulls in no board pin map */
|
||||||
|
#define BOARD_LYWSD03MMC_B15 0xB150
|
||||||
|
#define BOARD_SELECT BOARD_LYWSD03MMC_B15
|
||||||
|
|
||||||
/* ---- Application settings (equivalents of the Zephyr build) ---- */
|
/* ---- Application settings (equivalents of the Zephyr build) ---- */
|
||||||
#define DEV_NAME "LYWSD03MMC" /* CONFIG_BT_DEVICE_NAME */
|
#define DEV_NAME "LYWSD03MMC" /* CONFIG_BT_DEVICE_NAME */
|
||||||
#define ADV_INTERVAL 16000 /* 10000 ms / 0.625 ms */
|
#define ADV_INTERVAL 16000 /* 10000 ms / 0.625 ms */
|
||||||
@@ -52,12 +67,22 @@ enum {
|
|||||||
#define OTA_WINDOW_S 120
|
#define OTA_WINDOW_S 120
|
||||||
#define OTA_WINDOW_ADV_INTERVAL 1600 /* 1 s while the window is open */
|
#define OTA_WINDOW_ADV_INTERVAL 1600 /* 1 s while the window is open */
|
||||||
|
|
||||||
|
/* Power-on marker. DEEP_ANA_REG1 keeps its value through deep sleep and
|
||||||
|
* software reboots and is cleared only by a power cycle. (DEEP_ANA_REG0,
|
||||||
|
* used by the pvvx build, is reserved by the official SDK: its bit 1 is
|
||||||
|
* the crystal start-up failure flag.) */
|
||||||
|
#define POWER_ON_FLAG_REG DEEP_ANA_REG1
|
||||||
|
#define POWER_ON_FLAG 0x55
|
||||||
|
|
||||||
/* ---- Connection / OTA (values from pvvx ble.h / app.c) ---- */
|
/* ---- Connection / OTA (values from pvvx ble.h / app.c) ---- */
|
||||||
#define CONN_INTERVAL 16 /* 16 * 1.25 ms = 20 ms */
|
#define CONN_INTERVAL 16 /* 16 * 1.25 ms = 20 ms */
|
||||||
#define CONN_LATENCY 49 /* (49 + 1) * 20 ms = 1 s between radio events */
|
#define CONN_LATENCY 49 /* (49 + 1) * 20 ms = 1 s between radio events */
|
||||||
#define CONN_TIMEOUT 400 /* x10 ms = 4 s */
|
#define CONN_TIMEOUT 400 /* x10 ms = 4 s */
|
||||||
#define OTA_IDLE_TIMEOUT_US (16 * 1000000) /* abort OTA after 16 s without data */
|
/* Official OTA server timeouts. The packet timeout is an inactivity
|
||||||
|
* timeout (1..20 s); the process timeout limits the whole upload
|
||||||
|
* (5..1000 s, SDK default only 30 s). */
|
||||||
|
#define OTA_PACKET_TIMEOUT_S 16 /* abort OTA after 16 s without data */
|
||||||
|
#define OTA_PROCESS_TIMEOUT_S 1000
|
||||||
|
|
||||||
/* ---- I2C ---- */
|
/* ---- I2C ---- */
|
||||||
#define I2C_GROUP I2C_GPIO_GROUP_C2C3
|
#define I2C_GROUP I2C_GPIO_GROUP_C2C3
|
||||||
@@ -69,7 +94,10 @@ enum {
|
|||||||
/* ---- Battery ADC ---- */
|
/* ---- Battery ADC ---- */
|
||||||
#define GPIO_VBAT GPIO_PB0
|
#define GPIO_VBAT GPIO_PB0
|
||||||
#define ADC_VBAT_CHN B0P
|
#define ADC_VBAT_CHN B0P
|
||||||
#define ADC_VREF_MV 1175 /* typical internal 1.2 V ref, mV */
|
/* 0: use the chip's ADC reference calibration, loaded by the SDK from the
|
||||||
|
* calibration sector or eFuse (typical 1175 mV if there is none).
|
||||||
|
* Non-zero: fixed reference in mV, to trim against a meter. */
|
||||||
|
#define ADC_VREF_MV 0
|
||||||
/* PB0 (not bonded) is driven high permanently, as in pvvx: no toggling
|
/* PB0 (not bonded) is driven high permanently, as in pvvx: no toggling
|
||||||
* around each ADC read, and the pad never floats. Applied by gpio_init()
|
* around each ADC read, and the pad never floats. Applied by gpio_init()
|
||||||
* on every wake-up, including from deep-sleep retention. */
|
* on every wake-up, including from deep-sleep retention. */
|
||||||
@@ -89,9 +117,4 @@ enum {
|
|||||||
#define GPIO_KEY GPIO_PA5
|
#define GPIO_KEY GPIO_PA5
|
||||||
#define PULL_KEY PM_PIN_PULLUP_1M
|
#define PULL_KEY PM_PIN_PULLUP_1M
|
||||||
|
|
||||||
#define MODULE_WATCHDOG_ENABLE 0
|
|
||||||
|
|
||||||
/* Stock Xiaomi flash layout: factory MAC address lives here */
|
|
||||||
#define CFG_ADR_MAC 0x76000
|
|
||||||
|
|
||||||
#include "vendor/common/default_config.h"
|
#include "vendor/common/default_config.h"
|
||||||
+17
-2
@@ -3,7 +3,9 @@
|
|||||||
* kept driven high (= VDD) permanently by gpio_init() (see app_config.h,
|
* kept driven high (= VDD) permanently by gpio_init() (see app_config.h,
|
||||||
* same as pvvx) and sampled through the 1/8 prescaler against the
|
* same as pvvx) and sampled through the 1/8 prescaler against the
|
||||||
* 1.2 V reference. 8 samples are taken, sorted, and the middle 4 averaged.
|
* 1.2 V reference. 8 samples are taken, sorted, and the middle 4 averaged.
|
||||||
* ADC sequence follows pvvx/ATC_MiThermometer battery.c.
|
* ADC sequence follows pvvx/ATC_MiThermometer battery.c; the conversion
|
||||||
|
* uses the per-chip reference calibration of the official SDK (as its
|
||||||
|
* adc_sample_and_get_result() does) unless ADC_VREF_MV fixes it.
|
||||||
*/
|
*/
|
||||||
#include "tl_common.h"
|
#include "tl_common.h"
|
||||||
#include "drivers.h"
|
#include "drivers.h"
|
||||||
@@ -12,6 +14,11 @@
|
|||||||
|
|
||||||
#define ADC_SAMPLES 8
|
#define ADC_SAMPLES 8
|
||||||
|
|
||||||
|
/* Set by user_calib_adc_vref() from blc_app_loadCustomizedParameters_normal()
|
||||||
|
* (drivers/B85/adc.c, retention RAM, default 1175 mV / 0 mV) */
|
||||||
|
extern unsigned short adc_gpio_calib_vref;
|
||||||
|
extern signed char adc_gpio_calib_vref_offset;
|
||||||
|
|
||||||
/* ADC analog config is not kept across deep-sleep retention: set it up each time */
|
/* ADC analog config is not kept across deep-sleep retention: set it up each time */
|
||||||
static void vbat_adc_setup(void)
|
static void vbat_adc_setup(void)
|
||||||
{
|
{
|
||||||
@@ -61,8 +68,16 @@ u16 battery_read_mv(void)
|
|||||||
dfifo_disable_dfifo2();
|
dfifo_disable_dfifo2();
|
||||||
adc_power_on_sar_adc(0);
|
adc_power_on_sar_adc(0);
|
||||||
|
|
||||||
/* mV = avg/8192 * Vref * 8 = sum4 * Vref / 4096 */
|
/* mV = avg/8192 * Vref * 8 + offset = sum4 * Vref / 4096 + offset */
|
||||||
u32 sum = s[2] + s[3] + s[4] + s[5];
|
u32 sum = s[2] + s[3] + s[4] + s[5];
|
||||||
|
|
||||||
|
if (!sum) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#if ADC_VREF_MV
|
||||||
return (u16)((sum * ADC_VREF_MV + 2048) >> 12);
|
return (u16)((sum * ADC_VREF_MV + 2048) >> 12);
|
||||||
|
#else
|
||||||
|
return (u16)((s32)((sum * adc_gpio_calib_vref + 2048) >> 12) +
|
||||||
|
adc_gpio_calib_vref_offset);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
@@ -7,23 +7,23 @@
|
|||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
||||||
*
|
*
|
||||||
* OTA itself (packet handling, CRC, flash writes, swapping the boot bank
|
* OTA itself (packet handling, CRC, flash writes, swapping the boot bank
|
||||||
* and rebooting) is done by the Telink BLE library: otaWrite()/otaRead().
|
* and rebooting) is done by the OTA server of the official Telink BLE
|
||||||
* The new image is written to the bank that is not running (0x00000 or
|
* library (stack/ble/service/ota/ota_server.h, otaWrite()). It speaks the
|
||||||
* 0x20000, 128 KiB each), so a failed transfer leaves the old firmware
|
* legacy Telink OTA protocol (CMD_OTA_START / data / CMD_OTA_END) used by
|
||||||
* bootable.
|
* TelinkOTA.html, plus the extended one. The new image is written to the
|
||||||
|
* bank that is not running (0x00000 or 0x20000, 128 KiB each; at most
|
||||||
|
* 124 KiB of firmware, the SDK default), so a failed transfer leaves the old
|
||||||
|
* firmware bootable.
|
||||||
*/
|
*/
|
||||||
#include "tl_common.h"
|
#include "tl_common.h"
|
||||||
#include "drivers.h"
|
#include "drivers.h"
|
||||||
#include "stack/ble/ble.h"
|
#include "stack/ble/ble.h"
|
||||||
#include "stack/ble/service/ble_ll_ota.h"
|
|
||||||
#include "vendor/common/blt_common.h"
|
|
||||||
#include "app_config.h"
|
#include "app_config.h"
|
||||||
#include "ble.h"
|
#include "ble.h"
|
||||||
#include "lcd.h"
|
#include "lcd.h"
|
||||||
|
|
||||||
void bls_set_advertise_prepare(void *p); /* in the library, no header */
|
/* Peripheral Preferred Connection Parameters (not in the SDK headers;
|
||||||
|
* the SDK's ble_sample defines it locally too) */
|
||||||
/* Peripheral Preferred Connection Parameters (not in this SDK's headers) */
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u16 intervalMin;
|
u16 intervalMin;
|
||||||
u16 intervalMax;
|
u16 intervalMax;
|
||||||
@@ -46,9 +46,6 @@ RAM static u32 window_start;
|
|||||||
static void close_window(void);
|
static void close_window(void);
|
||||||
RAM static u8 conn_param_accepted;
|
RAM static u8 conn_param_accepted;
|
||||||
|
|
||||||
RAM static u8 mac_public[6];
|
|
||||||
static u8 mac_random_static[6];
|
|
||||||
|
|
||||||
/* Scan response: complete local name DEV_NAME */
|
/* Scan response: complete local name DEV_NAME */
|
||||||
#define NAME_LEN (sizeof(DEV_NAME) - 1)
|
#define NAME_LEN (sizeof(DEV_NAME) - 1)
|
||||||
RAM static u8 scan_rsp[2 + NAME_LEN];
|
RAM static u8 scan_rsp[2 + NAME_LEN];
|
||||||
@@ -74,6 +71,7 @@ enum {
|
|||||||
OTA_PS_H, /* Telink OTA */
|
OTA_PS_H, /* Telink OTA */
|
||||||
OTA_CD_H,
|
OTA_CD_H,
|
||||||
OTA_DP_H,
|
OTA_DP_H,
|
||||||
|
OTA_CCC_H,
|
||||||
OTA_DESC_H,
|
OTA_DESC_H,
|
||||||
|
|
||||||
ATT_END_H,
|
ATT_END_H,
|
||||||
@@ -120,27 +118,21 @@ static const u8 service_change_cd[5] = {
|
|||||||
U16_LO(GATT_UUID_SERVICE_CHANGE), U16_HI(GATT_UUID_SERVICE_CHANGE)
|
U16_LO(GATT_UUID_SERVICE_CHANGE), U16_HI(GATT_UUID_SERVICE_CHANGE)
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Telink OTA */
|
/* Telink OTA, laid out as in the SDK's ble_sample: the official OTA
|
||||||
static const u8 ota_service_uuid[16] = TELINK_OTA_UUID_SERVICE;
|
* server answers on the data characteristic itself (result and version
|
||||||
static const u8 ota_data_uuid[16] = TELINK_SPP_DATA_OTA;
|
* responses of the extended protocol), hence NOTIFY and the CCC. */
|
||||||
static u8 ota_data;
|
static const u8 ota_service_uuid[16] = WRAPPING_BRACES(TELINK_OTA_UUID_SERVICE);
|
||||||
|
static const u8 ota_data_uuid[16] = WRAPPING_BRACES(TELINK_SPP_DATA_OTA);
|
||||||
|
RAM static u8 ota_data;
|
||||||
|
RAM static u8 ota_ccc[2];
|
||||||
static const u8 ota_name[] = { 'O', 'T', 'A' };
|
static const u8 ota_name[] = { 'O', 'T', 'A' };
|
||||||
static const u8 ota_cd[19] = {
|
static const u8 ota_cd[19] = {
|
||||||
CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RSP,
|
CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RSP | CHAR_PROP_NOTIFY |
|
||||||
|
CHAR_PROP_WRITE,
|
||||||
U16_LO(OTA_DP_H), U16_HI(OTA_DP_H),
|
U16_LO(OTA_DP_H), U16_HI(OTA_DP_H),
|
||||||
0x12, 0x2B, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
TELINK_SPP_DATA_OTA
|
||||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Make the library's OTA timeout an inactivity timeout (pvvx otaWritePre):
|
|
||||||
* every OTA packet restarts it. */
|
|
||||||
static int ota_write_pre(void *p)
|
|
||||||
{
|
|
||||||
blt_ota_start_tick = clock_time() | 1;
|
|
||||||
return otaWrite(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* In RAM, as pvvx: the stack walks this table from interrupt context,
|
/* In RAM, as pvvx: the stack walks this table from interrupt context,
|
||||||
* which must not depend on flash reads while OTA is erasing/writing it. */
|
* which must not depend on flash reads while OTA is erasing/writing it. */
|
||||||
RAM static attribute_t attributes[] = {
|
RAM static attribute_t attributes[] = {
|
||||||
@@ -159,9 +151,10 @@ RAM static attribute_t attributes[] = {
|
|||||||
{ 0, ATT_PERMISSIONS_READ, 2, sizeof(service_change_val), (u8 *)&service_change_uuid, (u8 *)&service_change_val, 0, 0 },
|
{ 0, ATT_PERMISSIONS_READ, 2, sizeof(service_change_val), (u8 *)&service_change_uuid, (u8 *)&service_change_val, 0, 0 },
|
||||||
{ 0, ATT_PERMISSIONS_RDWR, 2, sizeof(service_change_ccc), (u8 *)&ccc_uuid, (u8 *)&service_change_ccc, 0, 0 },
|
{ 0, ATT_PERMISSIONS_RDWR, 2, sizeof(service_change_ccc), (u8 *)&ccc_uuid, (u8 *)&service_change_ccc, 0, 0 },
|
||||||
|
|
||||||
{ 4, ATT_PERMISSIONS_READ, 2, 16, (u8 *)&primary_service_uuid, (u8 *)ota_service_uuid, 0, 0 },
|
{ 5, ATT_PERMISSIONS_READ, 2, 16, (u8 *)&primary_service_uuid, (u8 *)ota_service_uuid, 0, 0 },
|
||||||
{ 0, ATT_PERMISSIONS_READ, 2, sizeof(ota_cd), (u8 *)&character_uuid, (u8 *)ota_cd, 0, 0 },
|
{ 0, ATT_PERMISSIONS_READ, 2, sizeof(ota_cd), (u8 *)&character_uuid, (u8 *)ota_cd, 0, 0 },
|
||||||
{ 0, ATT_PERMISSIONS_RDWR, 16, sizeof(ota_data), (u8 *)ota_data_uuid, &ota_data, &ota_write_pre, &otaRead },
|
{ 0, ATT_PERMISSIONS_RDWR, 16, sizeof(ota_data), (u8 *)ota_data_uuid, &ota_data, &otaWrite, 0 },
|
||||||
|
{ 0, ATT_PERMISSIONS_RDWR, 2, sizeof(ota_ccc), (u8 *)&ccc_uuid, ota_ccc, 0, 0 },
|
||||||
{ 0, ATT_PERMISSIONS_READ, 2, sizeof(ota_name), (u8 *)&user_desc_uuid, (u8 *)ota_name, 0, 0 },
|
{ 0, ATT_PERMISSIONS_READ, 2, sizeof(ota_name), (u8 *)&user_desc_uuid, (u8 *)ota_name, 0, 0 },
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -174,7 +167,6 @@ static void ota_start_cb(void)
|
|||||||
ota_active = 1;
|
ota_active = 1;
|
||||||
conn_param_accepted = 0;
|
conn_param_accepted = 0;
|
||||||
bls_pm_setManualLatency(0);
|
bls_pm_setManualLatency(0);
|
||||||
bls_ota_setTimeout(OTA_IDLE_TIMEOUT_US);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* After a successful OTA the library reboots into the new image itself;
|
/* After a successful OTA the library reboots into the new image itself;
|
||||||
@@ -233,12 +225,17 @@ static void suspend_exit_cb(u8 e, u8 *p, int n)
|
|||||||
void ble_init(const u8 *adv, int adv_len, int (*adv_prepare)(void *),
|
void ble_init(const u8 *adv, int adv_len, int (*adv_prepare)(void *),
|
||||||
int open_window)
|
int open_window)
|
||||||
{
|
{
|
||||||
/* Factory public address (A4:C1:38:...) from the stock flash layout */
|
u8 mac_public[6];
|
||||||
blc_initMacAddress(CFG_ADR_MAC, mac_public, mac_random_static);
|
u8 mac_random_static[6];
|
||||||
|
|
||||||
|
/* Factory public address (A4:C1:38:...) from the stock flash layout:
|
||||||
|
* flash_sector_mac_address = 0x76000 on the 512 KiB flash */
|
||||||
|
blc_initMacAddress(flash_sector_mac_address, mac_public,
|
||||||
|
mac_random_static);
|
||||||
|
|
||||||
memcpy(&scan_rsp[2], DEV_NAME, NAME_LEN);
|
memcpy(&scan_rsp[2], DEV_NAME, NAME_LEN);
|
||||||
scan_rsp[0] = NAME_LEN + 1;
|
scan_rsp[0] = NAME_LEN + 1;
|
||||||
scan_rsp[1] = GAP_ADTYPE_LOCAL_NAME_COMPLETE;
|
scan_rsp[1] = DT_COMPLETE_LOCAL_NAME;
|
||||||
|
|
||||||
/* Controller */
|
/* Controller */
|
||||||
blc_ll_initStandby_module(mac_public);
|
blc_ll_initStandby_module(mac_public);
|
||||||
@@ -246,23 +243,26 @@ void ble_init(const u8 *adv, int adv_len, int (*adv_prepare)(void *),
|
|||||||
blc_ll_initConnection_module();
|
blc_ll_initConnection_module();
|
||||||
blc_ll_initSlaveRole_module();
|
blc_ll_initSlaveRole_module();
|
||||||
|
|
||||||
/* Host */
|
/* Host (GAP first, as the SDK requires) */
|
||||||
blc_gap_peripheral_init();
|
blc_gap_peripheral_init();
|
||||||
bls_att_setAttributeTable((u8 *)attributes);
|
|
||||||
blc_l2cap_register_handler(blc_l2cap_packet_receive);
|
blc_l2cap_register_handler(blc_l2cap_packet_receive);
|
||||||
|
bls_att_setAttributeTable((u8 *)attributes);
|
||||||
blc_smp_setSecurityLevel(No_Security);
|
blc_smp_setSecurityLevel(No_Security);
|
||||||
|
|
||||||
|
/* OTA server. Its init also erases the inactive bank if it still
|
||||||
|
* holds an old image. */
|
||||||
|
blc_ota_initOtaServer_module();
|
||||||
|
blc_ota_setOtaProcessTimeout(OTA_PROCESS_TIMEOUT_S);
|
||||||
|
blc_ota_setOtaDataPacketTimeout(OTA_PACKET_TIMEOUT_S);
|
||||||
|
blc_ota_registerOtaStartCmdCb(ota_start_cb);
|
||||||
|
blc_ota_registerOtaResultIndicationCb(ota_result_cb);
|
||||||
|
|
||||||
rf_set_power_level_index(RF_TX_POWER);
|
rf_set_power_level_index(RF_TX_POWER);
|
||||||
bls_app_registerEventCallback(BLT_EV_FLAG_CONNECT, &connect_cb);
|
bls_app_registerEventCallback(BLT_EV_FLAG_CONNECT, &connect_cb);
|
||||||
bls_app_registerEventCallback(BLT_EV_FLAG_TERMINATE, &disconnect_cb);
|
bls_app_registerEventCallback(BLT_EV_FLAG_TERMINATE, &disconnect_cb);
|
||||||
bls_app_registerEventCallback(BLT_EV_FLAG_SUSPEND_EXIT, &suspend_exit_cb);
|
bls_app_registerEventCallback(BLT_EV_FLAG_SUSPEND_EXIT, &suspend_exit_cb);
|
||||||
blc_l2cap_registerConnUpdateRspCb(conn_param_rsp_cb);
|
blc_l2cap_registerConnUpdateRspCb(conn_param_rsp_cb);
|
||||||
|
|
||||||
/* OTA: erase the inactive bank if it still holds an old image */
|
|
||||||
bls_ota_clearNewFwDataArea();
|
|
||||||
bls_ota_registerStartCmdCb(ota_start_cb);
|
|
||||||
bls_ota_registerResultIndicateCb(ota_result_cb);
|
|
||||||
|
|
||||||
/* Advertising */
|
/* Advertising */
|
||||||
bls_ll_setAdvData((u8 *)adv, adv_len);
|
bls_ll_setAdvData((u8 *)adv, adv_len);
|
||||||
bls_ll_setScanRspData(scan_rsp, sizeof(scan_rsp));
|
bls_ll_setScanRspData(scan_rsp, sizeof(scan_rsp));
|
||||||
@@ -281,19 +281,18 @@ void ble_init(const u8 *adv, int adv_len, int (*adv_prepare)(void *),
|
|||||||
close_window();
|
close_window();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Power management (pvvx init_ble) */
|
/* Power management. Thresholds as pvvx init_ble; early wake-up and
|
||||||
|
* retention size as the SDK's ble_sample: 16 KiB retention when code
|
||||||
|
* + retained data fit, else 32 KiB. */
|
||||||
blc_ll_initPowerManagement_module();
|
blc_ll_initPowerManagement_module();
|
||||||
|
blc_app_setDeepsleepRetentionSramSize();
|
||||||
blc_pm_setDeepsleepRetentionThreshold(40, 18);
|
blc_pm_setDeepsleepRetentionThreshold(40, 18);
|
||||||
blc_pm_setDeepsleepRetentionEarlyWakeupTiming(240);
|
blc_pm_setDeepsleepRetentionEarlyWakeupTiming(270);
|
||||||
blc_pm_setDeepsleepRetentionType(DEEPSLEEP_MODE_RET_SRAM_LOW32K);
|
|
||||||
bls_pm_setSuspendMask(SUSPEND_ADV | DEEPSLEEP_RETENTION_ADV |
|
bls_pm_setSuspendMask(SUSPEND_ADV | DEEPSLEEP_RETENTION_ADV |
|
||||||
SUSPEND_CONN | DEEPSLEEP_RETENTION_CONN);
|
SUSPEND_CONN | DEEPSLEEP_RETENTION_CONN);
|
||||||
bls_pm_setManualLatency(0);
|
bls_pm_setManualLatency(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called every main-loop pass. During OTA: answer every connection event
|
|
||||||
* (no slave latency) and never use deep-sleep retention, which would drop
|
|
||||||
* the transfer state. Otherwise allow the deepest sleep everywhere. */
|
|
||||||
/* Window over: non-connectable advertising at the normal interval. In
|
/* Window over: non-connectable advertising at the normal interval. In
|
||||||
* this mode the radio does not listen after each packet. */
|
* this mode the radio does not listen after each packet. */
|
||||||
static void close_window(void)
|
static void close_window(void)
|
||||||
@@ -306,6 +305,9 @@ static void close_window(void)
|
|||||||
lcd_show_ble(0);
|
lcd_show_ble(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called every main-loop pass. During OTA: answer every connection event
|
||||||
|
* (no slave latency) and never use deep-sleep retention, which would drop
|
||||||
|
* the transfer state. Otherwise allow the deepest sleep everywhere. */
|
||||||
_attribute_ram_code_
|
_attribute_ram_code_
|
||||||
int ble_task(void)
|
int ble_task(void)
|
||||||
{
|
{
|
||||||
@@ -336,5 +338,6 @@ void ble_init_deep_retention(void)
|
|||||||
{
|
{
|
||||||
rf_set_power_level_index(RF_TX_POWER);
|
rf_set_power_level_index(RF_TX_POWER);
|
||||||
blc_ll_recoverDeepRetention();
|
blc_ll_recoverDeepRetention();
|
||||||
bls_ota_registerStartCmdCb(ota_start_cb); /* as pvvx */
|
/* OTA server state and callbacks live in retention RAM in the
|
||||||
|
* official library: nothing to re-register */
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,6 @@
|
|||||||
*/
|
*/
|
||||||
#include "tl_common.h"
|
#include "tl_common.h"
|
||||||
#include "drivers.h"
|
#include "drivers.h"
|
||||||
#include "drivers/8258/gpio_8258.h"
|
|
||||||
#include "app_config.h"
|
#include "app_config.h"
|
||||||
#include "lcd.h"
|
#include "lcd.h"
|
||||||
|
|
||||||
|
|||||||
+35
-12
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* BTHome v2 temperature / humidity beacon with LCD and OTA - Xiaomi
|
* BTHome v2 temperature / humidity beacon with LCD and OTA - Xiaomi
|
||||||
* LYWSD03MMC B1.5 (Telink TLSR8251, Telink BLE SDK, no RTOS)
|
* LYWSD03MMC B1.5 (Telink TLSR8251, official Telink tc_ble_single_sdk,
|
||||||
|
* no RTOS)
|
||||||
*
|
*
|
||||||
* Every 60 s: measure T/RH (SHTV3) and VDD, update the segment LCD and the
|
* Every 60 s: measure T/RH (SHTV3) and VDD, update the segment LCD and the
|
||||||
* BTHome v2 advertisement sent every 10 s. For OTA_WINDOW_S after the
|
* BTHome v2 advertisement sent every 10 s. For OTA_WINDOW_S after the
|
||||||
@@ -21,7 +22,6 @@
|
|||||||
#include "tl_common.h"
|
#include "tl_common.h"
|
||||||
#include "drivers.h"
|
#include "drivers.h"
|
||||||
#include "stack/ble/ble.h"
|
#include "stack/ble/ble.h"
|
||||||
#include "vendor/common/blt_common.h"
|
|
||||||
#include "app_config.h"
|
#include "app_config.h"
|
||||||
#include "sensor.h"
|
#include "sensor.h"
|
||||||
#include "battery.h"
|
#include "battery.h"
|
||||||
@@ -92,6 +92,22 @@ static void low_battery(u16 mv)
|
|||||||
deep_sleep_s(LOW_VBAT_SLEEP_S);
|
deep_sleep_s(LOW_VBAT_SLEEP_S);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The OTA server writes flash: clear block protection left by other
|
||||||
|
* firmware, once per power-on, as pvvx does. Same operation as the pvvx
|
||||||
|
* SDK's flash_unlock() (status register := 0), done with the official
|
||||||
|
* driver, but only when a protection bit is actually set. Works on any
|
||||||
|
* SPI flash, unlike the per-chip flash_prot.c, which only knows the
|
||||||
|
* flash types listed in the SDK. */
|
||||||
|
#define FLASH_SR_PROTECT_BITS 0xFC /* BP0..BP4, SRP0 */
|
||||||
|
|
||||||
|
static void flash_clear_write_protection(void)
|
||||||
|
{
|
||||||
|
if (flash_read_status(FLASH_READ_STATUS_CMD_LOWBYTE) &
|
||||||
|
FLASH_SR_PROTECT_BITS) {
|
||||||
|
flash_write_status(FLASH_TYPE_8BIT_STATUS, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_attribute_ram_code_
|
_attribute_ram_code_
|
||||||
static void store_result(s32 mc, s32 mrh)
|
static void store_result(s32 mc, s32 mrh)
|
||||||
{
|
{
|
||||||
@@ -173,6 +189,16 @@ static int adv_prepare_cb(void *p)
|
|||||||
|
|
||||||
static void user_init_normal(void)
|
static void user_init_normal(void)
|
||||||
{
|
{
|
||||||
|
/* Must come before any BLE stack initialisation (official order) */
|
||||||
|
random_generator_init();
|
||||||
|
|
||||||
|
/* Flash size -> sector map (MAC 0x76000, calibration 0x77000 on the
|
||||||
|
* 512 KiB part), then the factory calibration from that sector:
|
||||||
|
* crystal trim, flash supply and ADC reference. Read-only, so it is
|
||||||
|
* safe before the battery check, which needs the ADC calibration. */
|
||||||
|
blc_readFlashSize_autoConfigCustomFlashSector();
|
||||||
|
blc_app_loadCustomizedParameters_normal();
|
||||||
|
|
||||||
blc_ll_initBasicMCU();
|
blc_ll_initBasicMCU();
|
||||||
adc_power_on_sar_adc(0);
|
adc_power_on_sar_adc(0);
|
||||||
lpc_power_down();
|
lpc_power_down();
|
||||||
@@ -194,10 +220,7 @@ static void user_init_normal(void)
|
|||||||
low_battery(vbat_mv);
|
low_battery(vbat_mv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The library writes flash for OTA: clear write protection left by
|
flash_clear_write_protection();
|
||||||
* other firmware (as pvvx, once per power-on) */
|
|
||||||
flash_unlock();
|
|
||||||
random_generator_init();
|
|
||||||
|
|
||||||
#if ADV_NAME_IN_ADV
|
#if ADV_NAME_IN_ADV
|
||||||
memcpy(&adv[18], DEV_NAME, NAME_LEN);
|
memcpy(&adv[18], DEV_NAME, NAME_LEN);
|
||||||
@@ -217,11 +240,10 @@ static void user_init_normal(void)
|
|||||||
}
|
}
|
||||||
publish();
|
publish();
|
||||||
|
|
||||||
/* DEEP_ANA_REG0 survives deep sleep and software reboots and is
|
/* Cleared only by a power cycle, see app_config.h */
|
||||||
* cleared only by a power cycle (pvvx uses it the same way) */
|
int power_on = analog_read(POWER_ON_FLAG_REG) != POWER_ON_FLAG;
|
||||||
int power_on = analog_read(DEEP_ANA_REG0) != 0x55;
|
|
||||||
|
|
||||||
analog_write(DEEP_ANA_REG0, 0x55);
|
analog_write(POWER_ON_FLAG_REG, POWER_ON_FLAG);
|
||||||
|
|
||||||
bls_pm_registerAppWakeupLowPowerCb(measure_done_cb);
|
bls_pm_registerAppWakeupLowPowerCb(measure_done_cb);
|
||||||
ble_init(adv, sizeof(adv), adv_prepare_cb, power_on);
|
ble_init(adv, sizeof(adv), adv_prepare_cb, power_on);
|
||||||
@@ -231,6 +253,7 @@ static void user_init_normal(void)
|
|||||||
_attribute_ram_code_
|
_attribute_ram_code_
|
||||||
static void user_init_deepRetn(void)
|
static void user_init_deepRetn(void)
|
||||||
{
|
{
|
||||||
|
blc_app_loadCustomizedParameters_deepRetn(); /* crystal trim, VDD_F */
|
||||||
blc_ll_initBasicMCU();
|
blc_ll_initBasicMCU();
|
||||||
ble_init_deep_retention();
|
ble_init_deep_retention();
|
||||||
}
|
}
|
||||||
@@ -280,11 +303,11 @@ int main(void)
|
|||||||
|
|
||||||
int deep_ret = pm_is_MCU_deepRetentionWakeup();
|
int deep_ret = pm_is_MCU_deepRetentionWakeup();
|
||||||
|
|
||||||
|
/* Same order as the SDK's ble_sample */
|
||||||
|
rf_drv_ble_init();
|
||||||
gpio_init(!deep_ret);
|
gpio_init(!deep_ret);
|
||||||
clock_init(SYS_CLK_TYPE);
|
clock_init(SYS_CLK_TYPE);
|
||||||
rf_drv_init(RF_MODE_BLE_1M);
|
|
||||||
reg_clk_en0 = FLD_CLK0_SWIRE_EN; /* gate unused peripheral clocks */
|
reg_clk_en0 = FLD_CLK0_SWIRE_EN; /* gate unused peripheral clocks */
|
||||||
blc_app_loadCustomizedParameters(); /* factory crystal trim */
|
|
||||||
|
|
||||||
if (deep_ret) {
|
if (deep_ret) {
|
||||||
user_init_deepRetn();
|
user_init_deepRetn();
|
||||||
|
|||||||
Reference in new issue
Block a user