전원 + BLE + 배터리
This commit is contained in:
+16
-2
@@ -2,6 +2,20 @@
|
|||||||
|
|
||||||
cmake_minimum_required(VERSION 3.20.0)
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
project(blinky)
|
project(vesiscan)
|
||||||
|
|
||||||
target_sources(app PRIVATE src/main.c)
|
target_include_directories(app PRIVATE
|
||||||
|
src
|
||||||
|
src/ble
|
||||||
|
src/drivers/battery
|
||||||
|
src/drivers/led
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(app PRIVATE
|
||||||
|
src/main.c
|
||||||
|
src/parser.c
|
||||||
|
src/power_control.c
|
||||||
|
src/ble/ble_service.c
|
||||||
|
src/drivers/battery/battery_adc.c
|
||||||
|
src/drivers/led/led_control.c
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
# VesiScan BASIC - Zephyr (nRF Connect SDK)
|
||||||
|
|
||||||
|
nRF52840 기반 VesiScan BASIC 펌웨어의 Zephyr 포팅 프로젝트입니다.
|
||||||
|
|
||||||
|
## 현재 구현 상태
|
||||||
|
|
||||||
|
| Stage | 내용 | 상태 |
|
||||||
|
|-------|------|------|
|
||||||
|
| 1 | GPIO + 전원 래치 + 버튼 상태머신 | 완료 |
|
||||||
|
| 1 | LED 제어 (green/orange 패턴) | 완료 |
|
||||||
|
| 2 | BLE 스택 + NUS (advertising, 연결, 재연결) | 완료 |
|
||||||
|
| 3 | 배터리 ADC (AIN2, 12bit, 저전압 자동 OFF) | 진행중 |
|
||||||
|
| 3 | 커맨드 파서 (msn? 구현) | 진행중 |
|
||||||
|
| - | Flash 저장소 (NVS) | 미구현 |
|
||||||
|
| - | 온도 센서 (TMP235, AIN3) | 미구현 |
|
||||||
|
| - | IMU (ICM42670P, I2C) | 미구현 |
|
||||||
|
| - | 피에조 + ADC121S051 | 미구현 |
|
||||||
|
| - | BLE 보안 (본딩, 패스키) | 미구현 |
|
||||||
|
|
||||||
|
## 프로젝트 구조
|
||||||
|
|
||||||
|
```
|
||||||
|
blinky/
|
||||||
|
├── CMakeLists.txt # 빌드 설정 (소스 파일 등록)
|
||||||
|
├── prj.conf # Kconfig 설정 (기능 ON/OFF)
|
||||||
|
├── boards/
|
||||||
|
│ └── nrf52840dk_nrf52840.overlay # Devicetree Overlay (핀 매핑)
|
||||||
|
└── src/
|
||||||
|
├── main.c # 메인 진입점 + 부팅 시퀀스 + 버튼 상태머신
|
||||||
|
├── main.h # 전역 상수/변수/타입 정의
|
||||||
|
├── debug_print.h # 디버그 출력 매크로 (RTT)
|
||||||
|
├── parser.c / .h # BLE 커맨드 파서 (TAG+DATA+CRC16)
|
||||||
|
├── power_control.c / .h # 전원 시퀀스 상태머신
|
||||||
|
├── ble/
|
||||||
|
│ └── ble_service.c / .h # BLE NUS 서비스 (advertising, 연결, TX/RX)
|
||||||
|
└── drivers/
|
||||||
|
├── battery/
|
||||||
|
│ └── battery_adc.c / .h # 배터리 전압 ADC (AIN2, 12bit)
|
||||||
|
└── led/
|
||||||
|
└── led_control.c / .h # LED 패턴 제어 (k_timer 기반)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 빌드 방법
|
||||||
|
|
||||||
|
### 일반 빌드
|
||||||
|
소스 파일(.c/.h)만 변경한 경우.
|
||||||
|
|
||||||
|
### Pristine Build
|
||||||
|
아래 파일을 변경한 경우 반드시 Pristine Build:
|
||||||
|
- `prj.conf` (Kconfig 설정)
|
||||||
|
- `boards/*.overlay` (Devicetree 핀 매핑)
|
||||||
|
- `CMakeLists.txt` (소스/include 경로 추가)
|
||||||
|
|
||||||
|
nRF Connect for VS Code → Build 패널 → **Pristine Build** 또는 `build` 폴더 삭제 후 빌드.
|
||||||
|
|
||||||
|
## 핀 매핑 (Devicetree Overlay)
|
||||||
|
|
||||||
|
| 이름 | 핀 | 방향 | 역할 |
|
||||||
|
|------|-----|------|------|
|
||||||
|
| `PWR_HOLD` | P0.08 | Output, Active HIGH | 전원 래치 |
|
||||||
|
| `BUTTON_CHECK` | P1.08 | Input, Pull-up, Active LOW | 전원 버튼 |
|
||||||
|
| `LED_BLE` | P0.12 | Output, Active LOW | 초록 LED |
|
||||||
|
| `FUNCTION_LED` | P0.29 | Output, Active LOW | 주황 LED |
|
||||||
|
| ADC AIN2 | P0.04 | Analog Input | 배터리 전압 분압 |
|
||||||
|
|
||||||
|
> UART0은 P0.08 충돌 방지를 위해 비활성화 (`status = "disabled"`)
|
||||||
|
|
||||||
|
## 전원 버튼 동작
|
||||||
|
|
||||||
|
| 동작 | 조건 | 결과 |
|
||||||
|
|------|------|------|
|
||||||
|
| 부팅 (OFF→ON) | 2초 이상 홀드 | 전원 래치 + LED 깜빡임 + BLE advertising |
|
||||||
|
| 무시 | 부팅 후 2초 미만 릴리스 | 전원 OFF (래치 안 됨) |
|
||||||
|
| 전원 OFF (ON→OFF) | 버튼 놓은 뒤 다시 2초 홀드 | advertising 중지 + LED OFF + 3초 후 전원 차단 |
|
||||||
|
|
||||||
|
## BLE 커맨드 프로토콜
|
||||||
|
|
||||||
|
### 패킷 포맷
|
||||||
|
```
|
||||||
|
[TAG 4바이트] [DATA N바이트] [CRC16 2바이트]
|
||||||
|
```
|
||||||
|
- TAG: ASCII 4글자 (예: `msn?`, `mfv?`)
|
||||||
|
- DATA: uint16 little-endian 값들
|
||||||
|
- CRC16: CRC-CCITT (초기값 0xFFFF)
|
||||||
|
- 응답 TAG: 첫 글자 `m` → `r` (예: `msn?` → `rsn:`)
|
||||||
|
|
||||||
|
### 구현된 커맨드
|
||||||
|
|
||||||
|
| 커맨드 | 응답 | 기능 |
|
||||||
|
|--------|------|------|
|
||||||
|
| `msn?` | `rsn:` + uint16 mV | 배터리 전압 측정 |
|
||||||
|
|
||||||
|
### 전체 커맨드 목록 (구현 예정)
|
||||||
|
|
||||||
|
| # | 내용 | 명령 | 응답 | 비고 |
|
||||||
|
|---|------|------|------|------|
|
||||||
|
| 1 | 전원 OFF | `msq?` | `rsq:` | |
|
||||||
|
| 2 | 재부팅 | `mss?` | `rss:` | |
|
||||||
|
| 3 | 본딩 삭제 + 재부팅 | `msr?` | `rsr:` | |
|
||||||
|
| 4 | LED 상태 | `mls?` | - | |
|
||||||
|
| 5 | HW Version 읽기 | `mrh?` | `rrh:` | FDS |
|
||||||
|
| 6 | HW Version 쓰기 | `mwh?` | `rwh:` | FDS |
|
||||||
|
| 7 | Serial Number 읽기 | `mrs?` | `rrs:` | FDS |
|
||||||
|
| 8 | Serial Number 쓰기 | `mws?` | `rws:` | FDS |
|
||||||
|
| 9 | FW Version 읽기 | `mfv?` | `rfv:` | |
|
||||||
|
| 10 | Passkey 읽기 | `mqz?` | `rqz:` | FDS |
|
||||||
|
| 11 | Passkey 쓰기 | `mpz?` | `rpz:` | FDS |
|
||||||
|
| 12 | 배터리 측정 | `msn?` | `rsn:` | 구현 완료 |
|
||||||
|
| 13 | IMU 단발 측정 | `msp?` | `rsp:` | |
|
||||||
|
| 14 | 온도 측정 | `mso?` | `rso:` | |
|
||||||
|
| 15 | 단일 채널 측정 | `mec?` | `reb:` → `raa:` | 테스트용 |
|
||||||
|
| 16 | 모든 채널(6) 측정 | `maa?` | `reb:`(6개) → `raa:` | |
|
||||||
|
| 17 | 전체 측정 | `mbb?` | `rbb:` → `reb:`(6개) → `raa:` | 배터리+IMU+온도+피에조 |
|
||||||
|
| 18 | 측정 파라미터 읽기 | `mcf?` | `rcf:` | FDS |
|
||||||
|
| 19 | 측정 파라미터 쓰기 | `mcs?` | `rcs:` | FDS |
|
||||||
|
|
||||||
|
## 디버그 로그 (RTT)
|
||||||
|
|
||||||
|
- **VS Code**: nRF Connect 패널 → Connected Devices → 보드 선택 → 터미널 아이콘
|
||||||
|
- **J-Link RTT Viewer**: Connection: USB, Target Device: NRF52840_XXAA
|
||||||
|
|
||||||
|
### 로그 태그
|
||||||
|
| 태그 | 내용 |
|
||||||
|
|------|------|
|
||||||
|
| `[BTN]` | 버튼 이벤트 (2s 홀드, 릴리스) |
|
||||||
|
| `[PWR]` | 전원 래치 ON/OFF |
|
||||||
|
| `[BOOT]` | 부팅 시퀀스 완료 |
|
||||||
|
| `[BLE]` | BLE 연결/해제/advertising/파라미터 |
|
||||||
|
| `[BLE RX]` | NUS 수신 데이터 (hex dump) |
|
||||||
|
| `[NUS TX]` | NUS 송신 |
|
||||||
|
| `[CMD]` | 커맨드 파서 처리 결과 |
|
||||||
|
| `[BATT]` | 배터리 ADC 측정값 |
|
||||||
|
| `[SYS]` | 슬립 진입 |
|
||||||
|
|
||||||
|
## prj.conf 주요 설정
|
||||||
|
|
||||||
|
| 설정 | 값 | 역할 |
|
||||||
|
|------|-----|------|
|
||||||
|
| `CONFIG_GPIO` | y | GPIO 드라이버 |
|
||||||
|
| `CONFIG_LOG` | y | 로깅 시스템 |
|
||||||
|
| `CONFIG_LOG_BACKEND_RTT` | y | RTT 로그 출력 |
|
||||||
|
| `CONFIG_SERIAL` | n | UART 비활성화 (P0.08 충돌 방지) |
|
||||||
|
| `CONFIG_BT` | y | BLE 스택 |
|
||||||
|
| `CONFIG_BT_NUS` | y | Nordic UART Service |
|
||||||
|
| `CONFIG_BT_L2CAP_TX_MTU` | 247 | MTU 크기 |
|
||||||
|
| `CONFIG_BT_CTLR_TX_PWR_PLUS_8` | y | TX power +8dBm |
|
||||||
|
| `CONFIG_ADC` | y | ADC 드라이버 (배터리, 온도) |
|
||||||
-97
@@ -1,97 +0,0 @@
|
|||||||
.. zephyr:code-sample:: blinky
|
|
||||||
:name: Blinky
|
|
||||||
:relevant-api: gpio_interface
|
|
||||||
|
|
||||||
Blink an LED forever using the GPIO API.
|
|
||||||
|
|
||||||
Overview
|
|
||||||
********
|
|
||||||
|
|
||||||
The Blinky sample blinks an LED forever using the :ref:`GPIO API <gpio_api>`.
|
|
||||||
|
|
||||||
The source code shows how to:
|
|
||||||
|
|
||||||
#. Get a pin specification from the :ref:`devicetree <dt-guide>` as a
|
|
||||||
:c:struct:`gpio_dt_spec`
|
|
||||||
#. Configure the GPIO pin as an output
|
|
||||||
#. Toggle the pin forever
|
|
||||||
|
|
||||||
See :zephyr:code-sample:`pwm-blinky` for a similar sample that uses the PWM API instead.
|
|
||||||
|
|
||||||
.. _blinky-sample-requirements:
|
|
||||||
|
|
||||||
Requirements
|
|
||||||
************
|
|
||||||
|
|
||||||
Your board must:
|
|
||||||
|
|
||||||
#. Have an LED connected via a GPIO pin (these are called "User LEDs" on many of
|
|
||||||
Zephyr's :ref:`boards`).
|
|
||||||
#. Have the LED configured using the ``led0`` devicetree alias.
|
|
||||||
|
|
||||||
Building and Running
|
|
||||||
********************
|
|
||||||
|
|
||||||
Build and flash Blinky as follows, changing ``reel_board`` for your board:
|
|
||||||
|
|
||||||
.. zephyr-app-commands::
|
|
||||||
:zephyr-app: samples/basic/blinky
|
|
||||||
:board: reel_board
|
|
||||||
:goals: build flash
|
|
||||||
:compact:
|
|
||||||
|
|
||||||
After flashing, the LED starts to blink and messages with the current LED state
|
|
||||||
are printed on the console. If a runtime error occurs, the sample exits without
|
|
||||||
printing to the console.
|
|
||||||
|
|
||||||
Build errors
|
|
||||||
************
|
|
||||||
|
|
||||||
You will see a build error at the source code line defining the ``struct
|
|
||||||
gpio_dt_spec led`` variable if you try to build Blinky for an unsupported
|
|
||||||
board.
|
|
||||||
|
|
||||||
On GCC-based toolchains, the error looks like this:
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
error: '__device_dts_ord_DT_N_ALIAS_led_P_gpios_IDX_0_PH_ORD' undeclared here (not in a function)
|
|
||||||
|
|
||||||
Adding board support
|
|
||||||
********************
|
|
||||||
|
|
||||||
To add support for your board, add something like this to your devicetree:
|
|
||||||
|
|
||||||
.. code-block:: DTS
|
|
||||||
|
|
||||||
/ {
|
|
||||||
aliases {
|
|
||||||
led0 = &myled0;
|
|
||||||
};
|
|
||||||
|
|
||||||
leds {
|
|
||||||
compatible = "gpio-leds";
|
|
||||||
myled0: led_0 {
|
|
||||||
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
The above sets your board's ``led0`` alias to use pin 13 on GPIO controller
|
|
||||||
``gpio0``. The pin flags :c:macro:`GPIO_ACTIVE_HIGH` mean the LED is on when
|
|
||||||
the pin is set to its high state, and off when the pin is in its low state.
|
|
||||||
|
|
||||||
Tips:
|
|
||||||
|
|
||||||
- See :dtcompatible:`gpio-leds` for more information on defining GPIO-based LEDs
|
|
||||||
in devicetree.
|
|
||||||
|
|
||||||
- If you're not sure what to do, check the devicetrees for supported boards which
|
|
||||||
use the same SoC as your target. See :ref:`get-devicetree-outputs` for details.
|
|
||||||
|
|
||||||
- See :zephyr_file:`include/zephyr/dt-bindings/gpio/gpio.h` for the flags you can use
|
|
||||||
in devicetree.
|
|
||||||
|
|
||||||
- If the LED is built in to your board hardware, the alias should be defined in
|
|
||||||
your :ref:`BOARD.dts file <devicetree-in-out-files>`. Otherwise, you can
|
|
||||||
define one in a :ref:`devicetree overlay <set-devicetree-overlays>`.
|
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* VesiScan BASIC - Custom board overlay
|
||||||
|
* 프로젝트에서 사용하는 하드웨어 핀 정의
|
||||||
|
*
|
||||||
|
* Pin mapping:
|
||||||
|
* POWER_HOLD: P0.08 (output, active high - latches power)
|
||||||
|
* POWER_BTN: P1.08 (input, active low - pressed = LOW)
|
||||||
|
* LED_BLE: P0.12 (output, active low)
|
||||||
|
* LED_ORANGE: P0.29 (output, active low)
|
||||||
|
* BATT_ADC: P0.04 (AIN2, battery voltage measurement)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/dt-bindings/adc/adc.h>
|
||||||
|
#include <zephyr/dt-bindings/adc/nrf-saadc.h>
|
||||||
|
|
||||||
|
/* P0.08이 DK 기본 UART0 RX로 잡혀있어서 GPIO 출력 충돌 → 비활성화 */
|
||||||
|
&uart0
|
||||||
|
{
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 배터리 ADC 채널 설정 (AIN2 = P0.04) */
|
||||||
|
&adc
|
||||||
|
{
|
||||||
|
status = "okay";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
/* 배터리 전압 측정 채널
|
||||||
|
* 1/6 gain + 0.6V 내부 레퍼런스 → 풀스케일 3.6V
|
||||||
|
* 12bit 해상도, 4X 오버샘플링, 40us 샘플링 */
|
||||||
|
channel@2
|
||||||
|
{
|
||||||
|
reg = <2>;
|
||||||
|
zephyr,gain = "ADC_GAIN_1_6";
|
||||||
|
zephyr,reference = "ADC_REF_INTERNAL";
|
||||||
|
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40)>;
|
||||||
|
zephyr,input-positive = <NRF_SAADC_AIN2>;
|
||||||
|
zephyr,resolution = <12>;
|
||||||
|
zephyr,oversampling = <2>; /* 2^2 = 4X */
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/* P0.04(AIN2)가 DK Arduino 헤더에 GPIO로 잡혀있어서 SAADC 충돌 → 비활성화 */
|
||||||
|
&arduino_adc
|
||||||
|
{
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
|
/ {
|
||||||
|
/* ADC 채널을 코드에서 참조하기 위한 zephyr,user 노드 */
|
||||||
|
zephyr,user
|
||||||
|
{
|
||||||
|
io-channels = <&adc 2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
led
|
||||||
|
{
|
||||||
|
compatible = "gpio-leds";
|
||||||
|
|
||||||
|
LED_BLE: LED_BLE
|
||||||
|
{
|
||||||
|
gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
|
||||||
|
label = "Green LED";
|
||||||
|
};
|
||||||
|
|
||||||
|
FUNCTION_LED: FUNCTION_LED
|
||||||
|
{
|
||||||
|
gpios = <&gpio0 29 GPIO_ACTIVE_LOW>;
|
||||||
|
label = "Orange LED";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pin
|
||||||
|
{
|
||||||
|
compatible = "gpio-keys";
|
||||||
|
|
||||||
|
PWR_HOLD: PWR_HOLD
|
||||||
|
{
|
||||||
|
gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>;
|
||||||
|
label = "Power Hold Latch";
|
||||||
|
};
|
||||||
|
|
||||||
|
BUTTON_CHECK: BUTTON_CHECK
|
||||||
|
{
|
||||||
|
gpios = <&gpio1 8 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||||
|
label = "Power Button";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: Apache-2.0 */
|
|
||||||
|
|
||||||
/ {
|
|
||||||
aliases {
|
|
||||||
led0 = &led0;
|
|
||||||
};
|
|
||||||
|
|
||||||
leds {
|
|
||||||
compatible = "gpio-leds";
|
|
||||||
led0: led_0 {
|
|
||||||
gpios = <&gpio9 0 GPIO_ACTIVE_HIGH>;
|
|
||||||
label = "Green LED 0";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
&gpiote130 {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&gpio9 {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
&led0 {
|
|
||||||
gpios = <&hpf_gpio 9 GPIO_ACTIVE_HIGH>;
|
|
||||||
};
|
|
||||||
Binary file not shown.
@@ -1 +1,52 @@
|
|||||||
|
# VesiScan BASIC - Zephyr configuration
|
||||||
|
# Stage 1: GPIO + Power Control + LED + Debug Logging
|
||||||
|
# Stage 2: BLE (NUS)
|
||||||
|
|
||||||
CONFIG_GPIO=y
|
CONFIG_GPIO=y
|
||||||
|
|
||||||
|
# Logging via RTT
|
||||||
|
CONFIG_LOG=y
|
||||||
|
CONFIG_LOG_MODE_DEFERRED=y
|
||||||
|
CONFIG_LOG_BACKEND_RTT=y
|
||||||
|
CONFIG_LOG_BACKEND_UART=n
|
||||||
|
CONFIG_USE_SEGGER_RTT=y
|
||||||
|
CONFIG_PRINTK=y
|
||||||
|
CONFIG_CONSOLE=y
|
||||||
|
CONFIG_RTT_CONSOLE=y
|
||||||
|
CONFIG_SERIAL=n
|
||||||
|
CONFIG_UART_CONSOLE=n
|
||||||
|
|
||||||
|
# BLE stack
|
||||||
|
CONFIG_BT=y
|
||||||
|
CONFIG_BT_PERIPHERAL=y
|
||||||
|
CONFIG_BT_DEVICE_NAME="VB026030000"
|
||||||
|
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
|
||||||
|
CONFIG_BT_DEVICE_NAME_MAX=16
|
||||||
|
|
||||||
|
# BLE NUS (Nordic UART Service)
|
||||||
|
CONFIG_BT_NUS=y
|
||||||
|
|
||||||
|
# GATT / MTU
|
||||||
|
CONFIG_BT_GATT_CLIENT=y
|
||||||
|
CONFIG_BT_L2CAP_TX_MTU=247
|
||||||
|
CONFIG_BT_BUF_ACL_TX_SIZE=251
|
||||||
|
CONFIG_BT_BUF_ACL_RX_SIZE=251
|
||||||
|
|
||||||
|
# Connection parameters
|
||||||
|
CONFIG_BT_PERIPHERAL_PREF_MIN_INT=24
|
||||||
|
CONFIG_BT_PERIPHERAL_PREF_MAX_INT=24
|
||||||
|
CONFIG_BT_PERIPHERAL_PREF_LATENCY=0
|
||||||
|
CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400
|
||||||
|
|
||||||
|
# TX power
|
||||||
|
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
|
||||||
|
|
||||||
|
# Security (dev mode - disabled for now)
|
||||||
|
CONFIG_BT_SMP=n
|
||||||
|
|
||||||
|
# ADC (battery, temperature)
|
||||||
|
CONFIG_ADC=y
|
||||||
|
|
||||||
|
# System
|
||||||
|
CONFIG_HEAP_MEM_POOL_SIZE=2048
|
||||||
|
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
|
||||||
|
|||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
sample:
|
|
||||||
name: Blinky Sample
|
|
||||||
tests:
|
|
||||||
sample.basic.blinky:
|
|
||||||
tags:
|
|
||||||
- LED
|
|
||||||
- gpio
|
|
||||||
filter: dt_enabled_alias_with_parent_compat("led0", "gpio-leds")
|
|
||||||
depends_on: gpio
|
|
||||||
harness: led
|
|
||||||
integration_platforms:
|
|
||||||
- frdm_k64f
|
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file ble_service.c
|
||||||
|
* @brief BLE NUS service module (Zephyr port)
|
||||||
|
*
|
||||||
|
* Original VesiScan-Basic BLE implementation ported to Zephyr/NCS:
|
||||||
|
* - NUS (Nordic UART Service) for data exchange
|
||||||
|
* - Advertising: 40ms interval, 3-min timeout
|
||||||
|
* - Connection: 30ms interval, 0 latency, 4s supervision timeout
|
||||||
|
* - TX power +8 dBm, 2M PHY preferred
|
||||||
|
* - Dev mode: no security / Production mode: bonding + passkey (TODO)
|
||||||
|
******************************************************************************/
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/bluetooth/bluetooth.h>
|
||||||
|
#include <zephyr/bluetooth/hci.h>
|
||||||
|
#include <zephyr/bluetooth/conn.h>
|
||||||
|
#include <zephyr/bluetooth/uuid.h>
|
||||||
|
#include <zephyr/bluetooth/gatt.h>
|
||||||
|
#include <bluetooth/services/nus.h>
|
||||||
|
|
||||||
|
#include "ble_service.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "debug_print.h"
|
||||||
|
#include "led_control.h"
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(ble_svc, LOG_LEVEL_INF);
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Module variables
|
||||||
|
*============================================================================*/
|
||||||
|
static struct bt_conn *current_conn;
|
||||||
|
static ble_data_rx_cb_t rx_callback;
|
||||||
|
|
||||||
|
/* BLE API는 ISR/콜백에서 직접 호출하면 안전하지 않을 수 있으므로 k_work 사용 */
|
||||||
|
static struct k_work adv_restart_work;
|
||||||
|
|
||||||
|
static void adv_restart_handler(struct k_work *work)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(work);
|
||||||
|
ble_advertising_start();
|
||||||
|
led_set_state(LED_STATE_ADVERTISING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Advertising data
|
||||||
|
*============================================================================*/
|
||||||
|
static const struct bt_data ad[] =
|
||||||
|
{
|
||||||
|
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||||
|
BT_DATA(BT_DATA_NAME_COMPLETE, SERIAL_NUMBER, sizeof(SERIAL_NUMBER) - 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct bt_data sd[] =
|
||||||
|
{
|
||||||
|
BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
|
||||||
|
};
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Connection callbacks
|
||||||
|
*============================================================================*/
|
||||||
|
static void connected(struct bt_conn *conn, uint8_t err)
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BLE] Connect failed (err %d) -> re-adv\r\n", err);
|
||||||
|
k_work_submit(&adv_restart_work);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_conn = bt_conn_ref(conn);
|
||||||
|
ble_connection_st = true;
|
||||||
|
|
||||||
|
/* 연결 정보 출력 */
|
||||||
|
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||||
|
bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
|
||||||
|
DBG_PRINTF("[BLE] Peer: %s\r\n", addr_str);
|
||||||
|
|
||||||
|
/* Connection parameters (30ms interval) */
|
||||||
|
struct bt_le_conn_param conn_param =
|
||||||
|
{
|
||||||
|
.interval_min = BLE_MIN_CONN_INTERVAL,
|
||||||
|
.interval_max = BLE_MAX_CONN_INTERVAL,
|
||||||
|
.latency = BLE_SLAVE_LATENCY,
|
||||||
|
.timeout = BLE_CONN_SUP_TIMEOUT,
|
||||||
|
};
|
||||||
|
bt_conn_le_param_update(conn, &conn_param);
|
||||||
|
|
||||||
|
led_set_state(LED_STATE_OFF);
|
||||||
|
DBG_PRINTF("[BLE] Connected\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||||
|
{
|
||||||
|
if (current_conn)
|
||||||
|
{
|
||||||
|
bt_conn_unref(current_conn);
|
||||||
|
current_conn = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_connection_st = false;
|
||||||
|
DBG_PRINTF("[BLE] Disconnected (reason 0x%02x)\r\n", reason);
|
||||||
|
|
||||||
|
/* 워크큐에서 advertising 재시작 */
|
||||||
|
k_work_submit(&adv_restart_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void le_param_updated(struct bt_conn *conn, uint16_t interval,
|
||||||
|
uint16_t latency, uint16_t timeout)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BLE] Params: interval=%d latency=%d timeout=%d\r\n",
|
||||||
|
interval, latency, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_CONN_CB_DEFINE(conn_callbacks) =
|
||||||
|
{
|
||||||
|
.connected = connected,
|
||||||
|
.disconnected = disconnected,
|
||||||
|
.le_param_updated = le_param_updated,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* NUS callbacks
|
||||||
|
*============================================================================*/
|
||||||
|
static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len)
|
||||||
|
{
|
||||||
|
if (rx_callback)
|
||||||
|
{
|
||||||
|
rx_callback(data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nus_sent(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[NUS TX] complete\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bt_nus_cb nus_cb =
|
||||||
|
{
|
||||||
|
.received = nus_received,
|
||||||
|
.sent = nus_sent,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Public functions
|
||||||
|
*============================================================================*/
|
||||||
|
int ble_service_init(ble_data_rx_cb_t rx_cb)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
rx_callback = rx_cb;
|
||||||
|
|
||||||
|
k_work_init(&adv_restart_work, adv_restart_handler);
|
||||||
|
|
||||||
|
/* Enable BLE stack */
|
||||||
|
err = bt_enable(NULL);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BLE] bt_enable failed (err %d)\r\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
DBG_PRINTF("[BLE] Stack enabled\r\n");
|
||||||
|
|
||||||
|
/* Initialize NUS */
|
||||||
|
err = bt_nus_init(&nus_cb);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BLE] NUS init failed (err %d)\r\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
DBG_PRINTF("[BLE] NUS initialized\r\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ble_advertising_start(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
struct bt_le_adv_param adv_param = BT_LE_ADV_PARAM_INIT(
|
||||||
|
BT_LE_ADV_OPT_CONN,
|
||||||
|
APP_ADV_INTERVAL, /* min interval */
|
||||||
|
APP_ADV_INTERVAL + 16, /* max interval (slight window) */
|
||||||
|
NULL /* undirected */
|
||||||
|
);
|
||||||
|
|
||||||
|
err = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BLE] Adv start failed (err %d)\r\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_PRINTF("[BLE] Advertising started\r\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ble_advertising_stop(void)
|
||||||
|
{
|
||||||
|
int err = bt_le_adv_stop();
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BLE] Adv stop failed (err %d)\r\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_PRINTF("[BLE] Advertising stopped\r\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ble_data_send(const uint8_t *data, uint16_t len)
|
||||||
|
{
|
||||||
|
if (!current_conn)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[NUS TX] not connected\r\n");
|
||||||
|
return -ENOTCONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_PRINTF("[NUS TX] %d bytes\r\n", len);
|
||||||
|
int err = bt_nus_send(current_conn, data, len);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[NUS TX] failed (err %d)\r\n", err);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_is_connected(void)
|
||||||
|
{
|
||||||
|
return (current_conn != NULL);
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file ble_service.h
|
||||||
|
* @brief BLE NUS service module (Zephyr port)
|
||||||
|
*
|
||||||
|
* Nordic UART Service (NUS) based BLE communication.
|
||||||
|
* Original: SoftDevice S140 + ble_nus SDK module
|
||||||
|
* Ported: Zephyr BLE + NCS bt_nus
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef BLE_SERVICE_H__
|
||||||
|
#define BLE_SERVICE_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* BLE configuration
|
||||||
|
*============================================================================*/
|
||||||
|
#define BLE_DEV_MODE 1 /* 1: Dev (no security), 0: Production */
|
||||||
|
|
||||||
|
#define APP_ADV_INTERVAL 64 /* 64 x 0.625ms = 40ms */
|
||||||
|
#define APP_ADV_DURATION 18000 /* 18000 x 10ms = 180s (3 min) */
|
||||||
|
|
||||||
|
#define BLE_MIN_CONN_INTERVAL 24 /* 30ms / 1.25ms = 24 */
|
||||||
|
#define BLE_MAX_CONN_INTERVAL 24 /* 30ms / 1.25ms = 24 */
|
||||||
|
#define BLE_SLAVE_LATENCY 0
|
||||||
|
#define BLE_CONN_SUP_TIMEOUT 400 /* 4000ms / 10ms = 400 */
|
||||||
|
|
||||||
|
#define BLE_TX_POWER 8 /* +8 dBm */
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Callback types
|
||||||
|
*============================================================================*/
|
||||||
|
typedef void (*ble_data_rx_cb_t)(const uint8_t *data, uint16_t len);
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Public functions
|
||||||
|
*============================================================================*/
|
||||||
|
int ble_service_init(ble_data_rx_cb_t rx_cb);
|
||||||
|
int ble_advertising_start(void);
|
||||||
|
int ble_advertising_stop(void);
|
||||||
|
int ble_data_send(const uint8_t *data, uint16_t len);
|
||||||
|
bool ble_is_connected(void);
|
||||||
|
|
||||||
|
#endif /* BLE_SERVICE_H__ */
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file debug_print.h
|
||||||
|
* @brief Debug output macros (Zephyr LOG backend)
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef DEBUG_PRINT_H
|
||||||
|
#define DEBUG_PRINT_H
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DBG_PRINTF maps to printk for RTT/UART console output.
|
||||||
|
* For module-level logging use LOG_INF/LOG_DBG etc.
|
||||||
|
*/
|
||||||
|
#define ENABLE_PRINTF 1
|
||||||
|
|
||||||
|
#if ENABLE_PRINTF
|
||||||
|
#include <zephyr/sys/printk.h>
|
||||||
|
#define DBG_PRINTF(...) printk(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define DBG_PRINTF(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* DEBUG_PRINT_H */
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file battery_adc.c
|
||||||
|
* @brief Battery voltage ADC measurement (Zephyr devicetree 기반)
|
||||||
|
*
|
||||||
|
* ADC 채널 설정은 디바이스트리 overlay에서 관리:
|
||||||
|
* - AIN2 (P0.04), Single-ended
|
||||||
|
* - 12-bit 해상도, 1/6 gain, 4X oversample, 40us acq time
|
||||||
|
*
|
||||||
|
* 변환 공식: mV = (ADC × 600 / 4095) × 6 × 1.42
|
||||||
|
* → 프로젝트 고유 분압비(1.42x) 때문에 adc_raw_to_millivolts_dt() 사용 불가
|
||||||
|
*
|
||||||
|
* 동작 모드:
|
||||||
|
* - 단독 측정 (msn? 커맨드) → battery_read_mv() 호출
|
||||||
|
* - 주기 모니터링 (60초) → 저전압 10회 연속 시 자동 전원 OFF
|
||||||
|
* - info4 모드 (mbb?) → info_batt에 저장
|
||||||
|
******************************************************************************/
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/drivers/adc.h>
|
||||||
|
#include <zephyr/devicetree.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "battery_adc.h"
|
||||||
|
#include "debug_print.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* ADC 디바이스트리 설정
|
||||||
|
*============================================================================*/
|
||||||
|
#define ZEPHYR_USER_NODE DT_PATH(zephyr_user)
|
||||||
|
|
||||||
|
/* 디바이스트리에서 ADC 채널 스펙 가져오기 (gain, ref, acq time, input, resolution, oversampling) */
|
||||||
|
static const struct adc_dt_spec battery_adc = ADC_DT_SPEC_GET(ZEPHYR_USER_NODE);
|
||||||
|
|
||||||
|
static int16_t adc_buffer;
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Module variables
|
||||||
|
*============================================================================*/
|
||||||
|
volatile bool battery_saadc_done = false;
|
||||||
|
volatile uint16_t info_batt = 0;
|
||||||
|
|
||||||
|
static struct k_timer battery_timer;
|
||||||
|
static struct k_work battery_work;
|
||||||
|
static uint8_t low_battery_cnt = 0;
|
||||||
|
|
||||||
|
#define BATTERY_MONITOR_INTERVAL_MS 60000 /* 60초 주기 */
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 전압 변환
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/** @brief ADC raw → mV 변환
|
||||||
|
* 공식: (raw × 600 / 4095) × 6 × 1.42
|
||||||
|
* 정수 연산: raw × 600 × 6 × 142 / (4095 × 100) */
|
||||||
|
|
||||||
|
static int adc_raw_to_mv(int16_t raw)
|
||||||
|
{
|
||||||
|
if (raw < 0)
|
||||||
|
{
|
||||||
|
raw = 0;
|
||||||
|
}
|
||||||
|
/* 오버플로 방지: 600 × 6 × 142 = 511,200 → raw 최대 4095 → 4095 × 511200 = ~2B → int32 OK */
|
||||||
|
int32_t mv = ((int32_t)raw * 600 * 6 * 142) / (4095 * 100);
|
||||||
|
return (int)mv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Public functions
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
void battery_adc_init(void)
|
||||||
|
{
|
||||||
|
/* ADC 디바이스 준비 상태 확인 */
|
||||||
|
if (!adc_is_ready_dt(&battery_adc))
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BATT] ADC device not ready\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 디바이스트리 설정으로 채널 구성 */
|
||||||
|
int err = adc_channel_setup_dt(&battery_adc);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BATT] Channel setup failed (err %d)\r\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_PRINTF("[BATT] ADC init OK (DT-based, ch=%d, res=%d, os=%d)\r\n",
|
||||||
|
battery_adc.channel_id,
|
||||||
|
battery_adc.resolution,
|
||||||
|
battery_adc.oversampling);
|
||||||
|
}
|
||||||
|
|
||||||
|
int battery_read_mv(void)
|
||||||
|
{
|
||||||
|
/* 매번 채널 재설정 (다른 센서와 SAADC 공유 시 필요) */
|
||||||
|
int err = adc_channel_setup_dt(&battery_adc);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BATT] Channel setup failed (err %d)\r\n", err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 디바이스트리에서 시퀀스 초기화 (channels, resolution, oversampling) */
|
||||||
|
struct adc_sequence seq = {0};
|
||||||
|
|
||||||
|
err = adc_sequence_init_dt(&battery_adc, &seq);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BATT] Sequence init failed (err %d)\r\n", err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
seq.buffer = &adc_buffer;
|
||||||
|
seq.buffer_size = sizeof(adc_buffer);
|
||||||
|
|
||||||
|
/* ADC 읽기 */
|
||||||
|
err = adc_read_dt(&battery_adc, &seq);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BATT] Read failed (err %d)\r\n", err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t raw = adc_buffer;
|
||||||
|
|
||||||
|
int mv = adc_raw_to_mv(raw);
|
||||||
|
//DBG_PRINTF("[BATT] raw=%d mv=%d\r\n", raw, mv);
|
||||||
|
|
||||||
|
battery_saadc_done = true;
|
||||||
|
return mv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 주기 모니터링 타이머
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/** @brief k_work 핸들러 — 스레드 컨텍스트에서 ADC 읽기 (adc_read는 블로킹이라 ISR 불가) */
|
||||||
|
|
||||||
|
static void battery_work_handler(struct k_work *work)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(work);
|
||||||
|
|
||||||
|
if (processing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int batt = battery_read_mv();
|
||||||
|
|
||||||
|
if (batt < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 배터리 저전압 */
|
||||||
|
if (batt <= LOW_BATTERY_VOLTAGE)
|
||||||
|
{
|
||||||
|
low_battery_cnt++;
|
||||||
|
DBG_PRINTF("[BATT] LOW! cnt=%d mv=%d\r\n", low_battery_cnt, batt);
|
||||||
|
|
||||||
|
if (low_battery_cnt >= 10)
|
||||||
|
{
|
||||||
|
low_battery_cnt = 0;
|
||||||
|
DBG_PRINTF("[BATT] 10x low -> Power OFF\r\n");
|
||||||
|
sleep_mode_enter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
low_battery_cnt = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 타이머 ISR → k_work 예약 */
|
||||||
|
static void battery_timer_handler(struct k_timer *timer)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(timer);
|
||||||
|
k_work_submit(&battery_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
void battery_timer_init(void)
|
||||||
|
{
|
||||||
|
k_work_init(&battery_work, battery_work_handler);
|
||||||
|
k_timer_init(&battery_timer, battery_timer_handler, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void battery_timer_start(void)
|
||||||
|
{
|
||||||
|
k_timer_start(&battery_timer, K_MSEC(BATTERY_MONITOR_INTERVAL_MS), K_MSEC(BATTERY_MONITOR_INTERVAL_MS));
|
||||||
|
}
|
||||||
|
|
||||||
|
void battery_timer_stop(void)
|
||||||
|
{
|
||||||
|
k_timer_stop(&battery_timer);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file battery_adc.h
|
||||||
|
* @brief Battery voltage ADC measurement (Zephyr port)
|
||||||
|
*
|
||||||
|
* AIN2 채널, 12-bit, 1/6 gain, 4X oversample
|
||||||
|
* 저전압(3500mV) 10회 연속 감지 시 자동 전원 OFF
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef BATTERY_ADC_H__
|
||||||
|
#define BATTERY_ADC_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define LOW_BATTERY_VOLTAGE 3500 /* 저전압 임계값 (mV) */
|
||||||
|
|
||||||
|
/* 배터리 측정 완료 플래그 (all_sensors 대기용) */
|
||||||
|
extern volatile bool battery_saadc_done;
|
||||||
|
|
||||||
|
/* info4 모드에서 배터리 전압 저장 (mV) */
|
||||||
|
extern volatile uint16_t info_batt;
|
||||||
|
|
||||||
|
void battery_adc_init(void);
|
||||||
|
int battery_read_mv(void);
|
||||||
|
void battery_timer_start(void);
|
||||||
|
void battery_timer_stop(void);
|
||||||
|
void battery_timer_init(void);
|
||||||
|
|
||||||
|
#endif /* BATTERY_ADC_H__ */
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file led_control.c
|
||||||
|
* @brief LED direct control driver (Zephyr port)
|
||||||
|
*
|
||||||
|
* k_timer based 2-color LED (green/orange) pattern control
|
||||||
|
* Simple on/off states use immediate GPIO, complex patterns use state machine
|
||||||
|
******************************************************************************/
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
#include <zephyr/devicetree.h>
|
||||||
|
#include "led_control.h"
|
||||||
|
#include "debug_print.h"
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Devicetree LED specs
|
||||||
|
*============================================================================*/
|
||||||
|
#define LED_BLE_NODE DT_NODELABEL(led_ble)
|
||||||
|
#define FUNCTION_LED_NODE DT_NODELABEL(function_led)
|
||||||
|
|
||||||
|
static const struct gpio_dt_spec led_ble = GPIO_DT_SPEC_GET(LED_BLE_NODE, gpios);
|
||||||
|
static const struct gpio_dt_spec function_led = GPIO_DT_SPEC_GET(FUNCTION_LED_NODE, gpios);
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Color constants
|
||||||
|
*============================================================================*/
|
||||||
|
#define COLOR_NONE 0
|
||||||
|
#define COLOR_GREEN 1
|
||||||
|
#define COLOR_ORANGE 2
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Error pattern constants (State 7)
|
||||||
|
*============================================================================*/
|
||||||
|
#define ERROR_BLINK_ON_MS 166
|
||||||
|
#define ERROR_BLINK_OFF_MS 166
|
||||||
|
#define ERROR_BLINK_COUNT 3
|
||||||
|
#define ERROR_PAUSE_MS 1000
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Pattern table
|
||||||
|
*============================================================================*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t on_ms;
|
||||||
|
uint32_t off_ms;
|
||||||
|
uint8_t color;
|
||||||
|
bool repeat;
|
||||||
|
} led_pattern_t;
|
||||||
|
|
||||||
|
static const led_pattern_t m_patterns[LED_STATE_COUNT] = {
|
||||||
|
[LED_STATE_OFF] = { 0, 0, COLOR_NONE, false },
|
||||||
|
[LED_STATE_POWER_ON] = { 2000, 0, COLOR_GREEN, false },
|
||||||
|
[LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false },
|
||||||
|
[LED_STATE_ADVERTISING] = { 500, 500, COLOR_GREEN, true },
|
||||||
|
[LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true },
|
||||||
|
[LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true },
|
||||||
|
[LED_STATE_ALIGN_COMPLETE] = { 3000, 1000, COLOR_GREEN, true },
|
||||||
|
[LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true },
|
||||||
|
};
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Module variables
|
||||||
|
*============================================================================*/
|
||||||
|
static struct k_timer m_led_timer;
|
||||||
|
|
||||||
|
static led_state_t m_current_state = LED_STATE_OFF;
|
||||||
|
static bool m_phase_on;
|
||||||
|
|
||||||
|
/* Error pattern state machine */
|
||||||
|
static uint8_t m_error_blink_cnt;
|
||||||
|
static uint8_t m_error_phase;
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* GPIO helpers
|
||||||
|
*============================================================================*/
|
||||||
|
static inline void led_ble_on(void) { gpio_pin_set_dt(&led_ble, 1); }
|
||||||
|
static inline void led_ble_off(void) { gpio_pin_set_dt(&led_ble, 0); }
|
||||||
|
static inline void function_led_on(void) { gpio_pin_set_dt(&function_led, 1); }
|
||||||
|
static inline void function_led_off(void) { gpio_pin_set_dt(&function_led, 0); }
|
||||||
|
|
||||||
|
static void led_all_off(void)
|
||||||
|
{
|
||||||
|
led_ble_off();
|
||||||
|
function_led_off();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void led_color_on(uint8_t color)
|
||||||
|
{
|
||||||
|
led_all_off();
|
||||||
|
if (color == COLOR_GREEN) led_ble_on();
|
||||||
|
else if (color == COLOR_ORANGE) function_led_on();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Timer helper
|
||||||
|
*============================================================================*/
|
||||||
|
static void timer_start_ms(uint32_t ms)
|
||||||
|
{
|
||||||
|
if (ms == 0) return;
|
||||||
|
k_timer_start(&m_led_timer, K_MSEC(ms), K_NO_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Error pattern state machine
|
||||||
|
*============================================================================*/
|
||||||
|
static void error_pattern_start(void)
|
||||||
|
{
|
||||||
|
m_error_blink_cnt = 0;
|
||||||
|
m_error_phase = 0;
|
||||||
|
led_color_on(COLOR_ORANGE);
|
||||||
|
timer_start_ms(ERROR_BLINK_ON_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void error_pattern_tick(void)
|
||||||
|
{
|
||||||
|
switch (m_error_phase)
|
||||||
|
{
|
||||||
|
case 0: /* ON period done -> OFF */
|
||||||
|
led_all_off();
|
||||||
|
m_error_phase = 1;
|
||||||
|
timer_start_ms(ERROR_BLINK_OFF_MS);
|
||||||
|
break;
|
||||||
|
case 1: /* OFF period done */
|
||||||
|
m_error_blink_cnt++;
|
||||||
|
if (m_error_blink_cnt < ERROR_BLINK_COUNT) {
|
||||||
|
m_error_phase = 0;
|
||||||
|
led_color_on(COLOR_ORANGE);
|
||||||
|
timer_start_ms(ERROR_BLINK_ON_MS);
|
||||||
|
} else {
|
||||||
|
m_error_phase = 2;
|
||||||
|
timer_start_ms(ERROR_PAUSE_MS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: /* Pause done -> restart */
|
||||||
|
m_error_blink_cnt = 0;
|
||||||
|
m_error_phase = 0;
|
||||||
|
led_color_on(COLOR_ORANGE);
|
||||||
|
timer_start_ms(ERROR_BLINK_ON_MS);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Timer callback
|
||||||
|
*============================================================================*/
|
||||||
|
static void led_timer_handler(struct k_timer *timer)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(timer);
|
||||||
|
|
||||||
|
if (m_current_state == LED_STATE_ERROR)
|
||||||
|
{
|
||||||
|
error_pattern_tick();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const led_pattern_t *p = &m_patterns[m_current_state];
|
||||||
|
|
||||||
|
if (m_phase_on)
|
||||||
|
{
|
||||||
|
/* ON -> OFF transition */
|
||||||
|
led_all_off();
|
||||||
|
m_phase_on = false;
|
||||||
|
|
||||||
|
if (p->off_ms > 0)
|
||||||
|
{
|
||||||
|
timer_start_ms(p->off_ms);
|
||||||
|
}
|
||||||
|
else if (!p->repeat)
|
||||||
|
{
|
||||||
|
if (m_current_state == LED_STATE_POWER_OFF) {
|
||||||
|
led_all_off();
|
||||||
|
m_current_state = LED_STATE_OFF;
|
||||||
|
}
|
||||||
|
/* POWER_ON: stays lit, no timer */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* OFF -> ON transition */
|
||||||
|
if (p->repeat)
|
||||||
|
{
|
||||||
|
led_color_on(p->color);
|
||||||
|
m_phase_on = true;
|
||||||
|
timer_start_ms(p->on_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Public functions
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
void led_init(void)
|
||||||
|
{
|
||||||
|
gpio_pin_configure_dt(&led_ble, GPIO_OUTPUT_INACTIVE);
|
||||||
|
gpio_pin_configure_dt(&function_led, GPIO_OUTPUT_INACTIVE);
|
||||||
|
led_all_off();
|
||||||
|
|
||||||
|
k_timer_init(&m_led_timer, led_timer_handler, NULL);
|
||||||
|
|
||||||
|
m_current_state = LED_STATE_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_set_state(led_state_t state)
|
||||||
|
{
|
||||||
|
if (state >= LED_STATE_COUNT) return;
|
||||||
|
|
||||||
|
|
||||||
|
k_timer_stop(&m_led_timer);
|
||||||
|
led_all_off();
|
||||||
|
|
||||||
|
m_current_state = state;
|
||||||
|
m_phase_on = false;
|
||||||
|
|
||||||
|
const led_pattern_t *p = &m_patterns[state];
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case LED_STATE_OFF:
|
||||||
|
break;
|
||||||
|
case LED_STATE_ERROR:
|
||||||
|
error_pattern_start();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
led_color_on(p->color);
|
||||||
|
m_phase_on = true;
|
||||||
|
|
||||||
|
if (p->on_ms > 0)
|
||||||
|
{
|
||||||
|
timer_start_ms(p->on_ms);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
led_state_t led_get_state(void)
|
||||||
|
{
|
||||||
|
return m_current_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_ble_solid(void)
|
||||||
|
{
|
||||||
|
k_timer_stop(&m_led_timer);
|
||||||
|
led_all_off();
|
||||||
|
led_ble_on();
|
||||||
|
m_current_state = LED_STATE_OFF; /* no pattern running */
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file led_control.h
|
||||||
|
* @brief LED direct control driver (Zephyr port)
|
||||||
|
*
|
||||||
|
* Green (P0.12) + Orange (P0.29) 2-color LED with k_timer based patterns
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef LED_CONTROL_H__
|
||||||
|
#define LED_CONTROL_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* LED state enumeration
|
||||||
|
*============================================================================*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
LED_STATE_OFF = 0,
|
||||||
|
LED_STATE_POWER_ON, /* 1: Green ON 2s -> stay on */
|
||||||
|
LED_STATE_POWER_OFF, /* 2: Green ON 2s -> OFF */
|
||||||
|
LED_STATE_ADVERTISING, /* 3: Green blink 500ms/500ms */
|
||||||
|
LED_STATE_DETACH_WARNING, /* 4: Green 1s on / 3s off */
|
||||||
|
LED_STATE_ALIGN_SEARCHING, /* 5: Orange blink 1s/1s */
|
||||||
|
LED_STATE_ALIGN_COMPLETE, /* 6: Green 3s on / 1s off */
|
||||||
|
LED_STATE_ERROR, /* 7: Orange 3Hz x3 / 1s off */
|
||||||
|
LED_STATE_COUNT
|
||||||
|
} led_state_t;
|
||||||
|
|
||||||
|
void led_init(void);
|
||||||
|
void led_set_state(led_state_t state);
|
||||||
|
led_state_t led_get_state(void);
|
||||||
|
void led_ble_solid(void);
|
||||||
|
|
||||||
|
#endif /* LED_CONTROL_H__ */
|
||||||
+324
-32
@@ -1,48 +1,340 @@
|
|||||||
/*
|
/*******************************************************************************
|
||||||
* Copyright (c) 2016 Intel Corporation
|
* @file main.c
|
||||||
|
* @brief VesiScan BASIC - Zephyr 포팅 (Stage 1: GPIO + 전원 + LED + BLE)
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 부팅 시퀀스:
|
||||||
*/
|
* Phase 1: 전원 유지 핀, GPIO, 타이머, LED 초기화
|
||||||
|
* Phase 2: BLE 스택 + NUS 초기화, advertising 시작
|
||||||
#include <stdio.h>
|
* Phase 3+: (이후 단계: 센서 등)
|
||||||
|
*
|
||||||
|
* 전원 버튼 상태머신 (5ms 폴링, k_timer):
|
||||||
|
* [OFF→ON] 버튼 2초 이상 유지 → P0.08 래치 ON + 녹색 LED 깜빡임
|
||||||
|
* 2초 미만 놓으면 전원 OFF (래치 안 됨)
|
||||||
|
* [ON→OFF] 버튼 2초 이상 유지 → sleep_mode_enter() → 전원 차단
|
||||||
|
******************************************************************************/
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
#include <zephyr/drivers/gpio.h>
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
#include <zephyr/sys/printk.h>
|
||||||
|
|
||||||
/* 1000 msec = 1 sec */
|
#include "main.h"
|
||||||
#define SLEEP_TIME_MS 1000
|
#include "debug_print.h"
|
||||||
|
#include "power_control.h"
|
||||||
|
#include "led_control.h"
|
||||||
|
#include "ble_service.h"
|
||||||
|
#include "battery_adc.h"
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
/* The devicetree node identifier for the "led0" alias. */
|
LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF);
|
||||||
#define LED0_NODE DT_ALIAS(led0)
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Devicetree GPOP Settings
|
||||||
|
*============================================================================*/
|
||||||
|
#define POWER_HOLD_NODE DT_NODELABEL(pwr_hold) // 전원 래치
|
||||||
|
#define POWER_BTN_NODE DT_NODELABEL(button_check) // 전원 버튼
|
||||||
|
|
||||||
|
static const struct gpio_dt_spec power_hold = GPIO_DT_SPEC_GET(POWER_HOLD_NODE, gpios);
|
||||||
|
static const struct gpio_dt_spec power_btn = GPIO_DT_SPEC_GET(POWER_BTN_NODE, gpios);
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Timer
|
||||||
|
*============================================================================*/
|
||||||
|
static struct k_timer m_power_on_delay_timer; /* 전원 버튼 폴링용 (5ms 싱글샷) */
|
||||||
|
static struct k_timer m_power_off_delay_timer; /* 전원 OFF 지연용 (3초 후 전원 차단) */
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 전역 변수
|
||||||
|
*============================================================================*/
|
||||||
|
volatile bool ble_connection_st = false; /* BLE 연결 상태 */
|
||||||
|
volatile bool processing = false; /* 센서 데이터 처리 중 플래그 */
|
||||||
|
bool bond_data_delete = true; /* 본딩 데이터 삭제 요청 */
|
||||||
|
uint32_t m_life_cycle = 0; /* 디바이스 수명 카운터 */
|
||||||
|
uint8_t m_reset_status = 1; /* 리셋 상태 코드 */
|
||||||
|
char SERIAL_NO[SERIAL_NO_LENGTH]; /* 시리얼 번호 */
|
||||||
|
char HW_NO[HW_NO_LENGTH]; /* 하드웨어 번호 */
|
||||||
|
char m_static_passkey[PASSKEY_LENGTH]; /* BLE 정적 패스키 */
|
||||||
|
|
||||||
|
static uint16_t cnt_s; /* 전원 버튼 폴링 카운터 (5ms 단위) */
|
||||||
|
static bool device_on = false; /* 디바이스 전원 상태 (래치 완료 여부) */
|
||||||
|
static bool boot_btn_released = false; /* 부팅 후 버튼 놓았는지 여부 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A build error on this line means your board is unsupported.
|
* BLE advertising 제어용 워크 아이템
|
||||||
* See the sample documentation for information on how to fix this.
|
*
|
||||||
|
* BLE HCI 명령(bt_le_adv_start/stop 등)은 내부적으로 세마포어 대기가 필요하므로
|
||||||
|
* 인터럽트 컨텍스트(타이머 콜백 등)에서 직접 호출하면 커널 oops가 발생한다.
|
||||||
|
* (증상: "Controller unresponsive, command opcode 0x2006 timeout")
|
||||||
|
*
|
||||||
|
* k_work를 사용하면 시스템 워크큐 스레드에서 실행되므로 안전하게 BLE API 호출 가능.
|
||||||
|
* main_s() 타이머 콜백에서 k_work_submit()으로 예약하여 사용한다.
|
||||||
*/
|
*/
|
||||||
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
|
static struct k_work adv_start_work;
|
||||||
|
static struct k_work adv_stop_work;
|
||||||
|
|
||||||
|
/* 시스템 워크큐에서 BLE advertising 시작 */
|
||||||
|
static void adv_start_work_handler(struct k_work *work)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(work);
|
||||||
|
ble_advertising_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 시스템 워크큐에서 BLE advertising 중지 */
|
||||||
|
static void adv_stop_work_handler(struct k_work *work)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(work);
|
||||||
|
ble_advertising_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* BLE RX 콜백
|
||||||
|
*============================================================================*/
|
||||||
|
static void ble_rx_handler(const uint8_t *data, uint16_t len)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BLE RX] %d bytes: ", len);
|
||||||
|
for (uint16_t i = 0; i < len && i < 32; i++)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("%02X ", data[i]);
|
||||||
|
}
|
||||||
|
if (len > 32)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("...");
|
||||||
|
}
|
||||||
|
DBG_PRINTF("\r\n");
|
||||||
|
dr_parser(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 전원 유지 (POWER_HOLD) 제어
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/* 전원 유지 핀(P0.08) 초기화 - 아직 래치하지 않음
|
||||||
|
* 버튼을 물리적으로 누르고 있는 동안만 전원 유지됨
|
||||||
|
* 2초 후 main_s()에서 래치(HIGH) 설정 */
|
||||||
|
static void power_hold_init(void)
|
||||||
|
{
|
||||||
|
gpio_pin_configure_dt(&power_hold, GPIO_OUTPUT_INACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 전원 ON/OFF 제어 - P0.08 핀으로 물리적 전원 래치/해제 */
|
||||||
|
static void power_control_handler(on_off_cont_t device_power_st)
|
||||||
|
{
|
||||||
|
if (device_power_st == OFF) {
|
||||||
|
gpio_pin_set_dt(&power_hold, 0); /* P0.08 LOW → 전원 래치 해제 → 전원 차단 */
|
||||||
|
DBG_PRINTF("[PWR] OFF\r\n");
|
||||||
|
} else {
|
||||||
|
gpio_pin_set_dt(&power_hold, 1); /* P0.08 HIGH → 전원 유지 */
|
||||||
|
DBG_PRINTF("[PWR] ON\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* GPIO 초기화
|
||||||
|
*============================================================================*/
|
||||||
|
static void gpio_init(void)
|
||||||
|
{
|
||||||
|
gpio_pin_configure_dt(&power_btn, GPIO_INPUT); /* 전원 버튼(P1.08) 입력 설정 */
|
||||||
|
DBG_PRINTF("[GPIO] OK (BTN=%d)\r\n", gpio_pin_get_dt(&power_btn));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 기본 설정값 로드
|
||||||
|
*============================================================================*/
|
||||||
|
static void load_default_config(void)
|
||||||
|
{
|
||||||
|
memset(SERIAL_NO, 0, SERIAL_NO_LENGTH);
|
||||||
|
memcpy(SERIAL_NO, SERIAL_NUMBER, strlen(SERIAL_NUMBER));
|
||||||
|
|
||||||
|
memset(m_static_passkey, 0, PASSKEY_LENGTH);
|
||||||
|
memcpy(m_static_passkey, DEFAULT_PASSKEY, PASSKEY_LENGTH);
|
||||||
|
|
||||||
|
m_reset_status = 1;
|
||||||
|
bond_data_delete = true;
|
||||||
|
|
||||||
|
DBG_PRINTF("[CFG] Default (S/N=%s)\r\n", SERIAL_NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 전원 OFF 타임아웃 콜백
|
||||||
|
* LED 표시 후 3초(POWER_OFF_DELAY) 경과 시 호출되어 물리적 전원 차단
|
||||||
|
*============================================================================*/
|
||||||
|
static void t_power_off_timeout_handler(struct k_timer *timer)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(timer);
|
||||||
|
DBG_PRINTF("[PWR] Off timeout\r\n");
|
||||||
|
led_set_state(LED_STATE_OFF);
|
||||||
|
power_control_handler(OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 슬립 모드 / 전원 OFF
|
||||||
|
* LED로 사용자에게 알린 후 3초 뒤 전원 차단
|
||||||
|
*============================================================================*/
|
||||||
|
void sleep_mode_enter(void)
|
||||||
|
{
|
||||||
|
led_set_state(LED_STATE_POWER_ON);
|
||||||
|
DBG_PRINTF("[SYS] Sleep\r\n");
|
||||||
|
k_timer_start(&m_power_off_delay_timer, K_MSEC(POWER_OFF_DELAY), K_NO_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void device_power_off(void)
|
||||||
|
{
|
||||||
|
led_set_state(LED_STATE_POWER_OFF);
|
||||||
|
k_timer_start(&m_power_off_delay_timer, K_MSEC(POWER_OFF_DELAY), K_NO_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 전원 버튼 상태머신 (5ms 폴링)
|
||||||
|
*
|
||||||
|
* [부팅 시퀀스] (device_on == false)
|
||||||
|
* 1. 버튼 누름 → 전원 공급 → MCU 부팅 → 5ms마다 폴링 시작
|
||||||
|
* 2. cnt_s < 400 (2초 미만)에서 놓으면 → 전원 OFF (래치 안 됨)
|
||||||
|
* 3. cnt_s == 400 (2초) 도달 → P0.08 래치 ON + 녹색 LED 깜빡임
|
||||||
|
* 4. 버튼 놓으면 → 깜빡임 유지, 폴링 계속
|
||||||
|
*
|
||||||
|
* [전원 OFF 시퀀스] (device_on == true)
|
||||||
|
* 1. 버튼 2초 이상 누름 → sleep_mode_enter() → 전원 차단
|
||||||
|
* 2. 버튼 2초 미만 놓으면 → 카운터 리셋, 무시
|
||||||
|
*============================================================================*/
|
||||||
|
#define BOOT_THRESHOLD 400 /* 5ms x 400 = 2초 */
|
||||||
|
|
||||||
|
static void main_s(struct k_timer *timer)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(timer);
|
||||||
|
|
||||||
|
bool button_pressed = (gpio_pin_get_dt(&power_btn) == 1);
|
||||||
|
|
||||||
|
if (!device_on) /* ── 부팅 시퀀스 (OFF → ON) ── */
|
||||||
|
{
|
||||||
|
if (!button_pressed) /* 버튼 놓음 → 2초 미만이면 전원 OFF */
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BTN] Short press (%d) -> OFF\r\n", cnt_s);
|
||||||
|
power_control_handler(OFF);
|
||||||
|
cnt_s = 0;
|
||||||
|
}
|
||||||
|
else /* 버튼 계속 누르고 있음 */
|
||||||
|
{
|
||||||
|
cnt_s++;
|
||||||
|
|
||||||
|
if (cnt_s == BOOT_THRESHOLD) /* 2초 도달: 래치 + 부팅 완료 */
|
||||||
|
{
|
||||||
|
device_on = true;
|
||||||
|
cnt_s = 0; /* 카운터 리셋: 안 하면 다음 틱에서 ON→OFF 분기가
|
||||||
|
* cnt_s >= 400 조건을 즉시 만족하여 전원 OFF됨 */
|
||||||
|
power_control_handler(ON);
|
||||||
|
led_set_state(LED_STATE_ADVERTISING);
|
||||||
|
k_work_submit(&adv_start_work);
|
||||||
|
battery_timer_start();
|
||||||
|
m_reset_status = 1;
|
||||||
|
DBG_PRINTF("[BTN] 2s -> Power latched, LED blink\r\n");
|
||||||
|
DBG_PRINTF("[BOOT] Complete, device ON\r\n");
|
||||||
|
DBG_PRINTF("[DEV] device_on=%d\r\n", device_on);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else /* ── 전원 OFF 시퀀스 (ON → OFF) ── */
|
||||||
|
{
|
||||||
|
if (!boot_btn_released) /* 부팅 시 눌렀던 버튼을 아직 안 놓음 → 대기 */
|
||||||
|
{
|
||||||
|
if (!button_pressed)
|
||||||
|
{
|
||||||
|
boot_btn_released = true;
|
||||||
|
DBG_PRINTF("[BTN] Boot button released\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (button_pressed) /* 버튼 새로 누르고 있음 */
|
||||||
|
{
|
||||||
|
cnt_s++;
|
||||||
|
|
||||||
|
if (cnt_s >= BOOT_THRESHOLD) /* 2초 이상 → 전원 OFF */
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BTN] 2s long press -> Power OFF\r\n");
|
||||||
|
battery_timer_stop();
|
||||||
|
k_work_submit(&adv_stop_work);
|
||||||
|
device_on = false;
|
||||||
|
boot_btn_released = false;
|
||||||
|
cnt_s = 0;
|
||||||
|
sleep_mode_enter();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else /* 버튼 놓음 → 카운터 리셋 */
|
||||||
|
{
|
||||||
|
if (cnt_s > 0)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[BTN] Short press (%d) -> ignored\r\n", cnt_s);
|
||||||
|
}
|
||||||
|
cnt_s = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
k_timer_start(&m_power_on_delay_timer, K_MSEC(POWER_ON_DELAY), K_NO_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 타이머 초기화 / 시작
|
||||||
|
*============================================================================*/
|
||||||
|
static void timers_init(void)
|
||||||
|
{
|
||||||
|
k_timer_init(&m_power_on_delay_timer, main_s, NULL);
|
||||||
|
k_timer_init(&m_power_off_delay_timer, t_power_off_timeout_handler, NULL);
|
||||||
|
k_work_init(&adv_start_work, adv_start_work_handler);
|
||||||
|
k_work_init(&adv_stop_work, adv_stop_work_handler);
|
||||||
|
power_timer_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 전원 버튼 폴링 시작 (5ms 후 main_s 콜백) */
|
||||||
|
static void timers_start(void)
|
||||||
|
{
|
||||||
|
k_timer_start(&m_power_on_delay_timer, K_MSEC(POWER_ON_DELAY), K_NO_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 메인 함수
|
||||||
|
*============================================================================*/
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
int ret;
|
/*── Phase 1: 하드웨어 기본 초기화 ──*/
|
||||||
bool led_state = true;
|
power_hold_init();
|
||||||
|
cnt_s = 0;
|
||||||
|
|
||||||
if (!gpio_is_ready_dt(&led)) {
|
DBG_PRINTF("\r\n========================================\r\n");
|
||||||
return 0;
|
DBG_PRINTF(" VesiScan BASIC %s (Zephyr)\r\n", FIRMWARE_VERSION);
|
||||||
}
|
DBG_PRINTF("========================================\r\n");
|
||||||
|
|
||||||
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
|
DBG_PRINTF("[1] HW Init\r\n");
|
||||||
if (ret < 0) {
|
gpio_init();
|
||||||
return 0;
|
timers_init();
|
||||||
}
|
load_default_config();
|
||||||
|
led_init();
|
||||||
|
battery_adc_init();
|
||||||
|
battery_timer_init();
|
||||||
|
DBG_PRINTF(" gpio/timer/config/led/batt OK\r\n");
|
||||||
|
|
||||||
while (1) {
|
/*── Phase 2: BLE 스택 + NUS ──*/
|
||||||
ret = gpio_pin_toggle_dt(&led);
|
DBG_PRINTF("[2] BLE Init\r\n");
|
||||||
if (ret < 0) {
|
if (ble_service_init(ble_rx_handler) == 0)
|
||||||
return 0;
|
{
|
||||||
}
|
DBG_PRINTF(" ble/nus OK\r\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG_PRINTF(" ble FAIL\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
led_state = !led_state;
|
/*── Phase 3: FDS/NVS (TODO) ──*/
|
||||||
printf("LED state: %s\n", led_state ? "ON" : "OFF");
|
/*── Phase 4: 애플리케이션 (TODO) ──*/
|
||||||
k_msleep(SLEEP_TIME_MS);
|
|
||||||
}
|
DBG_PRINTF("\r\n========================================\r\n");
|
||||||
return 0;
|
DBG_PRINTF(" READY [%s]\r\n", SERIAL_NO);
|
||||||
|
DBG_PRINTF("========================================\r\n\r\n");
|
||||||
|
|
||||||
|
/* 전원 버튼 상태머신 시작 (부팅 시 버튼이 눌려있는 상태) */
|
||||||
|
timers_start();
|
||||||
|
|
||||||
|
/* 메인 루프 - idle */
|
||||||
|
for (;;) {
|
||||||
|
k_msleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file main.h
|
||||||
|
* @brief VesiScan BASIC - Zephyr port main header
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MAIN_H__
|
||||||
|
#define MAIN_H__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Firmware identification
|
||||||
|
*============================================================================*/
|
||||||
|
#define FIRMWARE_VERSION "VBTFW0102"
|
||||||
|
#define HARDWARE_VERSION "VB0HW0000"
|
||||||
|
#define SERIAL_NUMBER "VB026030000"
|
||||||
|
#define DEFAULT_PASSKEY "123456"
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Data length constants
|
||||||
|
*============================================================================*/
|
||||||
|
#define SERIAL_NO_LENGTH 12
|
||||||
|
#define HW_NO_LENGTH 12
|
||||||
|
#define PASSKEY_LENGTH 6
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Enumerations
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
OFF = 0,
|
||||||
|
ON = 1
|
||||||
|
} on_off_cont_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
CMD_BLE = 0,
|
||||||
|
CMD_UART = 1
|
||||||
|
} which_cmd_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
BLE_DISCONNECTED_ST = 0,
|
||||||
|
BLE_CONNECTED_ST = 1
|
||||||
|
} ble_status_t;
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Timing constants
|
||||||
|
*============================================================================*/
|
||||||
|
#define POWER_ON_DELAY 5 /* Power button poll interval (ms) */
|
||||||
|
#define POWER_OFF_DELAY 3000 /* LED display before power off (ms) */
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Function declarations
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
void sleep_mode_enter(void);
|
||||||
|
void device_power_off(void);
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Global variables (extern)
|
||||||
|
*============================================================================*/
|
||||||
|
extern volatile bool ble_connection_st;
|
||||||
|
extern volatile bool processing;
|
||||||
|
extern char SERIAL_NO[SERIAL_NO_LENGTH];
|
||||||
|
extern char HW_NO[HW_NO_LENGTH];
|
||||||
|
extern char m_static_passkey[PASSKEY_LENGTH];
|
||||||
|
extern bool bond_data_delete;
|
||||||
|
extern uint32_t m_life_cycle;
|
||||||
|
extern uint8_t m_reset_status;
|
||||||
|
|
||||||
|
#endif /* MAIN_H__ */
|
||||||
+171
@@ -0,0 +1,171 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file parser.c
|
||||||
|
* @brief BLE command parser (Zephyr port)
|
||||||
|
*
|
||||||
|
* 원본: pc_firm/parser.c
|
||||||
|
* 패킷 포맷: [TAG 4B] [DATA NB] [CRC16 2B]
|
||||||
|
*
|
||||||
|
* 현재 구현된 커맨드: msn? (배터리 측정), mls? (LED 상태 설정)
|
||||||
|
******************************************************************************/
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "parser.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "debug_print.h"
|
||||||
|
#include "ble_service.h"
|
||||||
|
#include "battery_adc.h"
|
||||||
|
#include "led_control.h"
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* CRC16 (CRC-CCITT, Nordic SDK 호환)
|
||||||
|
*============================================================================*/
|
||||||
|
static uint16_t dr_crc16_compute(const uint8_t *p_data, uint32_t size)
|
||||||
|
{
|
||||||
|
uint16_t crc = 0xFFFF;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
crc = (uint8_t)(crc >> 8) | (crc << 8);
|
||||||
|
crc ^= p_data[i];
|
||||||
|
crc ^= (uint8_t)(crc & 0xFF) >> 4;
|
||||||
|
crc ^= (crc << 8) << 4;
|
||||||
|
crc ^= ((crc & 0xFF) << 4) << 1;
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 응답 패킷 전송
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/* TAG(4B) + uint16 값(2B) + CRC16(2B) = 8바이트 전송 */
|
||||||
|
static void send_response_u16(const char *tag, uint16_t value)
|
||||||
|
{
|
||||||
|
uint8_t buf[8];
|
||||||
|
|
||||||
|
buf[0] = tag[0];
|
||||||
|
buf[1] = tag[1];
|
||||||
|
buf[2] = tag[2];
|
||||||
|
buf[3] = tag[3];
|
||||||
|
buf[4] = (uint8_t)(value >> 8);
|
||||||
|
buf[5] = (uint8_t)(value & 0xFF);
|
||||||
|
|
||||||
|
uint16_t crc = dr_crc16_compute(buf, 6);
|
||||||
|
buf[6] = (uint8_t)(crc & 0xFF);
|
||||||
|
buf[7] = (uint8_t)(crc >> 8);
|
||||||
|
|
||||||
|
ble_data_send(buf, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 커맨드 핸들러
|
||||||
|
*============================================================================*/
|
||||||
|
|
||||||
|
/* msn? → 배터리 전압 측정 → rsn: + mV */
|
||||||
|
|
||||||
|
static int cmd_msn(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
int mv = battery_read_mv();
|
||||||
|
if (mv < 0)
|
||||||
|
{
|
||||||
|
mv = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_response_u16("rsn:", (uint16_t)mv);
|
||||||
|
DBG_PRINTF("[CMD] msn -> %d mV\r\n", mv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mls? → LED 상태 변경 → rls: + state echo back
|
||||||
|
* 파라미터: [state(2B LE)] — led_state_t enum 값
|
||||||
|
* 0=OFF, 4=DETACH_WARNING, 5=ALIGN_SEARCHING, 6=ALIGN_COMPLETE, 7=ERROR
|
||||||
|
* 에러 응답: 0xFFFF=파라미터 없음, 0xFFFE=범위 초과 */
|
||||||
|
static int cmd_mls(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
/* 파라미터 부족 → 에러 코드 0xFFFF 에코 */
|
||||||
|
if (data_len < 2)
|
||||||
|
{
|
||||||
|
send_response_u16("rls:", 0xFFFF);
|
||||||
|
DBG_PRINTF("[CMD] mls: no data\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 리틀엔디안으로 읽기 (앱이 LE로 전송) */
|
||||||
|
uint16_t state = (uint16_t)(data[0]) | ((uint16_t)(data[1]) << 8);
|
||||||
|
|
||||||
|
/* 범위 초과 → 에러 코드 0xFFFE 에코 */
|
||||||
|
if (state > LED_STATE_ERROR)
|
||||||
|
{
|
||||||
|
send_response_u16("rls:", 0xFFFE);
|
||||||
|
DBG_PRINTF("[CMD] mls: invalid state %d\r\n", state);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
led_set_state((led_state_t)state);
|
||||||
|
send_response_u16("rls:", state);
|
||||||
|
DBG_PRINTF("[CMD] mls -> LED state=%d\r\n", state);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 커맨드 테이블
|
||||||
|
*============================================================================*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char tag[5];
|
||||||
|
int (*handler)(const uint8_t *data, uint8_t data_len);
|
||||||
|
} cmd_entry_t;
|
||||||
|
|
||||||
|
static const cmd_entry_t cmd_table[] =
|
||||||
|
{
|
||||||
|
{ "msn?", cmd_msn },
|
||||||
|
{ "mls?", cmd_mls },
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CMD_TABLE_SIZE (sizeof(cmd_table) / sizeof(cmd_table[0]))
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* 파서 엔트리
|
||||||
|
*============================================================================*/
|
||||||
|
int dr_parser(const uint8_t *buf, uint16_t len)
|
||||||
|
{
|
||||||
|
/* 최소 4바이트 TAG 필요 */
|
||||||
|
if (len < 4)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[CMD] Too short (%d)\r\n", len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CRC16 검증 (6바이트 이상이면 마지막 2바이트가 CRC) */
|
||||||
|
if (len >= 6)
|
||||||
|
{
|
||||||
|
uint16_t calc_crc = dr_crc16_compute(buf, len - 2);
|
||||||
|
uint16_t recv_crc = (uint16_t)(buf[len - 2]) | ((uint16_t)(buf[len - 1]) << 8);
|
||||||
|
|
||||||
|
if (calc_crc != recv_crc)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[CMD] CRC fail (calc=0x%04X recv=0x%04X)\r\n", calc_crc, recv_crc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TAG 추출 (4바이트) */
|
||||||
|
char tag[5] = { buf[0], buf[1], buf[2], buf[3], '\0' };
|
||||||
|
|
||||||
|
/* 데이터 부분 (TAG 이후, CRC 이전) */
|
||||||
|
const uint8_t *data = buf + 4;
|
||||||
|
uint8_t data_len = (len >= 6) ? (len - 4 - 2) : (len - 4);
|
||||||
|
|
||||||
|
/* 테이블 검색 + 디스패치 */
|
||||||
|
for (int i = 0; i < CMD_TABLE_SIZE; i++)
|
||||||
|
{
|
||||||
|
if (memcmp(tag, cmd_table[i].tag, 4) == 0)
|
||||||
|
{
|
||||||
|
return cmd_table[i].handler(data, data_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_PRINTF("[CMD] Unknown: %s\r\n", tag);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file parser.h
|
||||||
|
* @brief BLE command parser (Zephyr port)
|
||||||
|
*
|
||||||
|
* 4바이트 TAG + DATA + CRC16 패킷 파싱 및 디스패치
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef CMD_PARSER_H__
|
||||||
|
#define CMD_PARSER_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int dr_parser(const uint8_t *buf, uint16_t len);
|
||||||
|
|
||||||
|
#endif /* CMD_PARSER_H__ */
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file power_control.c
|
||||||
|
* @brief Device power sequence control (Zephyr port)
|
||||||
|
*
|
||||||
|
* Power-up sequence state machine with k_timer (single-shot 20ms intervals).
|
||||||
|
******************************************************************************/
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include "main.h"
|
||||||
|
#include "power_control.h"
|
||||||
|
#include "debug_print.h"
|
||||||
|
|
||||||
|
#define POWER_LOOP_INTERVAL 20 /* ms */
|
||||||
|
|
||||||
|
static struct k_timer m_power_timer;
|
||||||
|
static uint8_t p_order;
|
||||||
|
static bool lock_check = false;
|
||||||
|
|
||||||
|
extern volatile bool processing;
|
||||||
|
|
||||||
|
static void power_loop_expiry(struct k_timer *timer)
|
||||||
|
{
|
||||||
|
power_loop(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int device_sleep_mode(void)
|
||||||
|
{
|
||||||
|
k_msleep(2);
|
||||||
|
DBG_PRINTF("Device_Sleep_Mode OK!\r\n");
|
||||||
|
k_msleep(10);
|
||||||
|
processing = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int device_activated(void)
|
||||||
|
{
|
||||||
|
p_order = 0;
|
||||||
|
lock_check = true;
|
||||||
|
power_timer_start();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void power_loop(struct k_timer *timer)
|
||||||
|
{
|
||||||
|
power_timer_stop();
|
||||||
|
|
||||||
|
/* Sensor init not needed - imu_read_direct() handles it per measurement */
|
||||||
|
p_order = 2;
|
||||||
|
|
||||||
|
if (p_order < 2)
|
||||||
|
{
|
||||||
|
p_order++;
|
||||||
|
power_timer_start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[PWR] Device Activated OK!\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int device_reactivated(void)
|
||||||
|
{
|
||||||
|
/* sw_i2c_init_once() will be added in Stage 3 */
|
||||||
|
k_msleep(10);
|
||||||
|
lock_check = true;
|
||||||
|
p_order = 0;
|
||||||
|
power_timer_start();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void power_timer_start(void)
|
||||||
|
{
|
||||||
|
k_timer_start(&m_power_timer, K_MSEC(POWER_LOOP_INTERVAL), K_NO_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void power_timer_stop(void)
|
||||||
|
{
|
||||||
|
k_timer_stop(&m_power_timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void power_timer_init(void)
|
||||||
|
{
|
||||||
|
k_timer_init(&m_power_timer, power_loop_expiry, NULL);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file power_control.h
|
||||||
|
* @brief Device power sequence control (Zephyr port)
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef POWER_CONTROL_H__
|
||||||
|
#define POWER_CONTROL_H__
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
int device_sleep_mode(void);
|
||||||
|
int device_activated(void);
|
||||||
|
int device_reactivated(void);
|
||||||
|
void power_loop(struct k_timer *timer);
|
||||||
|
void power_timer_start(void);
|
||||||
|
void power_timer_stop(void);
|
||||||
|
void power_timer_init(void);
|
||||||
|
|
||||||
|
#endif /* POWER_CONTROL_H__ */
|
||||||
Reference in New Issue
Block a user