Initial Commit
This commit is contained in:
@@ -26,9 +26,7 @@
|
||||
#include "imu_i2c.h"
|
||||
#include "main.h"
|
||||
|
||||
/*==============================================================================
|
||||
* ADC 디바이스트리 설정
|
||||
*============================================================================*/
|
||||
/* ADC 디바이스트리 설정 */
|
||||
#define ZEPHYR_USER_NODE DT_PATH(zephyr_user)
|
||||
|
||||
/* 디바이스트리에서 ADC 채널 스펙 가져오기 (gain, ref, acq time, input, resolution, oversampling) */
|
||||
@@ -36,78 +34,79 @@ static const struct adc_dt_spec battery_adc = ADC_DT_SPEC_GET_BY_NAME(ZEPHYR_USE
|
||||
|
||||
static int16_t adc_buffer;
|
||||
|
||||
/*==============================================================================
|
||||
* Module variables
|
||||
*============================================================================*/
|
||||
/* 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;
|
||||
|
||||
/* 60초 내부 모니터 전용 카운터
|
||||
* - 앱 측정 명령(mbb?)이 없는 일반 상태에서만 사용
|
||||
* - mbb? 성공 시 연속 측정 판정으로 넘어가므로 0으로 초기화
|
||||
/*
|
||||
* 60초 내부 모니터 전용 카운터
|
||||
* - 앱 측정 명령(mbb?/mtb?)이 없는 일반 상태에서만 사용
|
||||
* - mbb? 성공 시 mbb? 측정 판정으로 넘어가므로 0으로 초기화
|
||||
*/
|
||||
static uint8_t periodic_low_battery_cnt;
|
||||
static uint8_t periodic_high_temperature_cnt;
|
||||
|
||||
/* 연속 측정(mbb?) 응답값 전용 카운터
|
||||
/*
|
||||
* 주기 측정(mbb?) 응답값 전용 카운터
|
||||
* - mbb? 응답에 포함된 배터리/IMU 온도 값으로 판정
|
||||
* - 측정 실패 시 호출되지 않으므로 기존 카운트 유지
|
||||
*/
|
||||
static uint8_t continuous_low_battery_cnt;
|
||||
static uint8_t continuous_high_temperature_cnt;
|
||||
|
||||
/* 정렬 상태 보호 판정 차단 플래그
|
||||
/*
|
||||
* 정렬 상태 보호 판정 차단 플래그
|
||||
* - mls?로 ALIGN_SEARCHING/ALIGN_COMPLETE 진입 시 true
|
||||
* - 정렬 중에는 배터리/온도 보호 판정 자체를 진행하지 않음
|
||||
*/
|
||||
static bool alignment_mode_active;
|
||||
|
||||
/* 최근 mbb? 성공 이후 내부 60초 모니터를 잠시 막는 기준 시각
|
||||
* - 연속 측정 중에는 mbb? 응답값으로 보호 판정
|
||||
/*
|
||||
* 최근 mbb? 성공 이후 내부 60초 모니터를 잠시 막는 기준 시각
|
||||
* - 주기 측정 중에는 mbb? 응답값으로 보호 판정
|
||||
* - 이 값보다 현재 시간이 작으면 60초 내부 모니터는 skip
|
||||
*/
|
||||
static int64_t continuous_monitor_active_until_ms;
|
||||
|
||||
#define BATTERY_MONITOR_LIMIT_COUNT 5U
|
||||
#define BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT 30U
|
||||
#define HIGH_TEMPERATURE_CDEG 4000 /* 40.00 C */
|
||||
#define CONTINUOUS_MONITOR_ACTIVE_MS 15000 /* mbb? 10초 주기 대비 여유 */
|
||||
#define BATTERY_MONITOR_LIMIT_COUNT 5U // 정렬모드(mtb?)나 주기 측정(mbb?)이 아닌 경우 60초 주기 5회 연속 저전압 감지 시 전원 OFF(5분)
|
||||
#define BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT 10U // 주기 측정 10회 연속 저전압 감지 시 전원 OFF(100초)
|
||||
#define HIGH_TEMPERATURE_CDEG 4000 // 40.00 C
|
||||
#define CONTINUOUS_MONITOR_ACTIVE_MS 15000 // mbb? 10초 주기 대비 여유
|
||||
|
||||
#define BATTERY_MONITOR_INTERVAL_MS 60000 /* 60초 주기 */
|
||||
#define BATTERY_MONITOR_INTERVAL_MS 60000 // 정렬모드(mtb?)나 주기 측정(mbb?)이 아닌 경우 저전압/고온 모니터 주기 60초
|
||||
|
||||
/*==============================================================================
|
||||
/*
|
||||
* 전압 변환 (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 */
|
||||
// 오버플로 방지: 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;
|
||||
}
|
||||
|
||||
static bool continuous_monitor_is_active(void)
|
||||
{
|
||||
/* 마지막 mbb? 성공 이후 CONTINUOUS_MONITOR_ACTIVE_MS 안쪽이면 연속 측정 중으로 간주 */
|
||||
return (continuous_monitor_active_until_ms > 0) &&
|
||||
(k_uptime_get() < continuous_monitor_active_until_ms);
|
||||
// 마지막 mbb? 성공 이후 CONTINUOUS_MONITOR_ACTIVE_MS 안쪽이면 주기 측정(mbb?) 중으로 간주
|
||||
return (continuous_monitor_active_until_ms > 0) && (k_uptime_get() < continuous_monitor_active_until_ms);
|
||||
}
|
||||
|
||||
static void record_battery_value(int batt_mv, uint8_t *cnt,
|
||||
uint8_t limit, const char *mode)
|
||||
/*
|
||||
* mode별 카운터만 전달받아 공통 판정 로직 사용
|
||||
* - periodic: 60초 내부 모니터, limit 5
|
||||
* - continuous: mbb? 응답값 기반, limit 10
|
||||
*/
|
||||
static void record_battery_value(int batt_mv, uint8_t *cnt, uint8_t limit, const char *mode)
|
||||
{
|
||||
/* mode별 카운터만 전달받아 공통 판정 로직 사용
|
||||
* - periodic: 60초 내부 모니터, limit 5
|
||||
* - continuous: mbb? 응답값 기반, limit 30
|
||||
*/
|
||||
if (batt_mv <= LOW_BATTERY_VOLTAGE)
|
||||
{
|
||||
if (*cnt < UINT8_MAX)
|
||||
@@ -115,8 +114,7 @@ static void record_battery_value(int batt_mv, uint8_t *cnt,
|
||||
(*cnt)++;
|
||||
}
|
||||
|
||||
DBG_PRINTF("[BATT] LOW! mode=%s cnt=%u/%u mv=%d\r\n",
|
||||
mode, *cnt, limit, batt_mv);
|
||||
DBG_PRINTF("[BATT] LOW! mode=%s cnt=%u/%u mv=%d\r\n", mode, *cnt, limit, batt_mv);
|
||||
|
||||
if (*cnt >= limit)
|
||||
{
|
||||
@@ -131,14 +129,14 @@ static void record_battery_value(int batt_mv, uint8_t *cnt,
|
||||
}
|
||||
}
|
||||
|
||||
static void record_temperature_value(int16_t temp_cdeg, uint8_t *cnt,
|
||||
uint8_t limit, const char *mode)
|
||||
/*
|
||||
* IMU 온도는 centi-degree 단위
|
||||
* - 4000 = 40.00 C
|
||||
* - 40.00 C 이상이면 고온 카운트 증가
|
||||
* - 기준 미만이면 해당 mode 카운터 초기화
|
||||
*/
|
||||
static void record_temperature_value(int16_t temp_cdeg, uint8_t *cnt, uint8_t limit, const char *mode)
|
||||
{
|
||||
/* IMU 온도는 centi-degree 단위
|
||||
* - 4000 = 40.00 C
|
||||
* - 40.00 C 이상이면 고온 카운트 증가
|
||||
* - 기준 미만이면 해당 mode 카운터 초기화
|
||||
*/
|
||||
if (temp_cdeg >= HIGH_TEMPERATURE_CDEG)
|
||||
{
|
||||
if (*cnt < UINT8_MAX)
|
||||
@@ -166,20 +164,17 @@ static void record_temperature_value(int16_t temp_cdeg, uint8_t *cnt,
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* Public functions
|
||||
*============================================================================*/
|
||||
|
||||
/* Public functions */
|
||||
void battery_adc_init(void)
|
||||
{
|
||||
/* ADC 디바이스 준비 상태 확인 */
|
||||
// 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)
|
||||
{
|
||||
@@ -195,7 +190,7 @@ void battery_adc_init(void)
|
||||
|
||||
int battery_read_mv(void)
|
||||
{
|
||||
/* 매번 채널 재설정 (다른 센서와 SAADC 공유 시 필요) */
|
||||
// 매번 채널 재설정 (다른 센서와 SAADC 공유 시 필요)
|
||||
int err = adc_channel_setup_dt(&battery_adc);
|
||||
if (err)
|
||||
{
|
||||
@@ -203,7 +198,7 @@ int battery_read_mv(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 디바이스트리에서 시퀀스 초기화 (channels, resolution, oversampling) */
|
||||
// 디바이스트리에서 시퀀스 초기화 (channels, resolution, oversampling)
|
||||
struct adc_sequence seq = {0};
|
||||
|
||||
err = adc_sequence_init_dt(&battery_adc, &seq);
|
||||
@@ -216,7 +211,7 @@ int battery_read_mv(void)
|
||||
seq.buffer = &adc_buffer;
|
||||
seq.buffer_size = sizeof(adc_buffer);
|
||||
|
||||
/* ADC 읽기 */
|
||||
// ADC 읽기
|
||||
err = adc_read_dt(&battery_adc, &seq);
|
||||
if (err)
|
||||
{
|
||||
@@ -233,13 +228,13 @@ int battery_read_mv(void)
|
||||
return mv;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 정렬 상태 진입/해제는 mls? 명령에서 전달
|
||||
* - enabled true: 보호 판정 중지 + 기존 누적값 초기화
|
||||
* - enabled false: 다음 조건부터 다시 판정 가능
|
||||
*/
|
||||
void battery_protection_set_alignment_mode(bool enabled)
|
||||
{
|
||||
/* 정렬 상태 진입/해제는 mls? 명령에서 전달
|
||||
* - enabled true: 보호 판정 중지 + 기존 누적값 초기화
|
||||
* - enabled false: 다음 조건부터 다시 판정 가능
|
||||
*/
|
||||
alignment_mode_active = enabled;
|
||||
if (enabled)
|
||||
{
|
||||
@@ -250,55 +245,43 @@ void battery_protection_set_alignment_mode(bool enabled)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* mbb? 측정 성공 시에만 호출
|
||||
* - 실패한 측정은 호출되지 않아서 continuous 카운트 유지
|
||||
* - 정렬 중에는 응답값이 있어도 보호 판정 제외
|
||||
* - mbb?가 들어온 직후에는 내부 60초 모니터 대신 응답값 기반 판정 사용
|
||||
*/
|
||||
void battery_protection_record_continuous(int batt_mv, int16_t temp_cdeg)
|
||||
{
|
||||
/* mbb? 측정 성공 시에만 호출
|
||||
* - 실패한 측정은 호출되지 않아서 continuous 카운트 유지
|
||||
* - 정렬 중에는 응답값이 있어도 보호 판정 제외
|
||||
*/
|
||||
// 정렬모드 중에는 continuous 보호 판정 제외
|
||||
if (alignment_mode_active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* mbb?가 들어온 직후에는 내부 60초 모니터 대신 응답값 기반 판정 사용
|
||||
* - periodic 카운터는 섞이지 않게 초기화
|
||||
* - continuous 카운터는 아래 record_* 함수에서 값에 따라 증가/초기화
|
||||
*/
|
||||
// 15초 후 일반 60초 주기 모니터 재개
|
||||
continuous_monitor_active_until_ms = k_uptime_get() + CONTINUOUS_MONITOR_ACTIVE_MS;
|
||||
|
||||
// periodic 모니터 카운트 초기화
|
||||
periodic_low_battery_cnt = 0;
|
||||
periodic_high_temperature_cnt = 0;
|
||||
|
||||
record_battery_value(batt_mv,
|
||||
&continuous_low_battery_cnt,
|
||||
BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT,
|
||||
"continuous");
|
||||
record_temperature_value(temp_cdeg,
|
||||
&continuous_high_temperature_cnt,
|
||||
BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT,
|
||||
"continuous");
|
||||
record_battery_value(batt_mv, &continuous_low_battery_cnt, BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT, "continuous");
|
||||
record_temperature_value(temp_cdeg, &continuous_high_temperature_cnt, BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT, "continuous");
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 주기 모니터링 타이머
|
||||
*============================================================================*/
|
||||
|
||||
/* k_work 핸들러: 스레드 컨텍스트에서 ADC 읽기 (adc_read는 블로킹이라 ISR 불가) */
|
||||
|
||||
static void battery_work_handler(struct k_work *work)
|
||||
{
|
||||
ARG_UNUSED(work);
|
||||
|
||||
/* 보호 판정 제외 구간
|
||||
* - 정렬 중: 정책상 보호 판정 미진행
|
||||
* - 최근 mbb? 성공 직후: mbb? 응답값 기반 continuous 판정이 담당
|
||||
*/
|
||||
// 일반 60초 주기 보호 판정 제외 구간: 정렬모드 또는 주기 측정
|
||||
if (alignment_mode_active || continuous_monitor_is_active())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 측정 처리 중이거나 IMU FIFO 사용 중(정렬모드)이면 I2C 접근 충돌 방지 */
|
||||
// I2C 접근 충돌 방지: 긴 측정 명령 처리 중이거나 IMU FIFO 동작 중
|
||||
if (processing || imu_fifo_is_active())
|
||||
{
|
||||
return;
|
||||
@@ -311,14 +294,8 @@ static void battery_work_handler(struct k_work *work)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 일반 상태의 60초 내부 모니터 판정
|
||||
* - 저전압이면 periodic_low_battery_cnt 증가
|
||||
* - 정상 전압이면 periodic_low_battery_cnt 초기화
|
||||
*/
|
||||
record_battery_value(batt,
|
||||
&periodic_low_battery_cnt,
|
||||
BATTERY_MONITOR_LIMIT_COUNT,
|
||||
"periodic");
|
||||
// 정렬모드나 주기 측정 중이 아닌 경우 60초 내부 모니터 판정(배터리)
|
||||
record_battery_value(batt, &periodic_low_battery_cnt, BATTERY_MONITOR_LIMIT_COUNT, "periodic");
|
||||
|
||||
int16_t temp_cdeg = 0;
|
||||
int temp_ret = imu_read_temperature_cdeg(&temp_cdeg);
|
||||
@@ -329,14 +306,8 @@ static void battery_work_handler(struct k_work *work)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 일반 상태의 IMU 온도 판정
|
||||
* - 고온이면 periodic_high_temperature_cnt 증가
|
||||
* - 정상 온도이면 periodic_high_temperature_cnt 초기화
|
||||
*/
|
||||
record_temperature_value(temp_cdeg,
|
||||
&periodic_high_temperature_cnt,
|
||||
BATTERY_MONITOR_LIMIT_COUNT,
|
||||
"periodic");
|
||||
// 정렬모드나 주기 측정 중이 아닌 경우 60초 내부 모니터 판정(온도)
|
||||
record_temperature_value(temp_cdeg, &periodic_high_temperature_cnt, BATTERY_MONITOR_LIMIT_COUNT, "periodic");
|
||||
}
|
||||
|
||||
/* 타이머 ISR → k_work 예약 */
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
#define ECHO_MISO_NODE DT_NODELABEL(echo_miso)
|
||||
#define ECHO_CS_NODE DT_NODELABEL(echo_cs)
|
||||
|
||||
/* SPIM END event 대기 최대 반복 횟수 */
|
||||
#define ECHO_ADC_SPIM_END_TIMEOUT_LOOPS 100000U
|
||||
|
||||
/* ADC SPI 핀 */
|
||||
static const struct gpio_dt_spec echo_sclk = GPIO_DT_SPEC_GET(ECHO_SCLK_NODE, gpios);
|
||||
static const struct gpio_dt_spec echo_miso = GPIO_DT_SPEC_GET(ECHO_MISO_NODE, gpios);
|
||||
@@ -42,7 +45,7 @@ static uint32_t gpio_abs_pin(const struct gpio_dt_spec *spec)
|
||||
return NRF_GPIO_PIN_MAP(port, spec->pin);
|
||||
}
|
||||
|
||||
/* SPIM RX 전송 */
|
||||
/* SPIM RX 전송: ADC wake/dummy read용 nrfx 경로 */
|
||||
static int echo_adc_transfer_rx(uint8_t *buf, size_t len)
|
||||
{
|
||||
nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_RX(buf, len);
|
||||
@@ -68,37 +71,7 @@ static void echo_adc_unpack_samples(uint16_t *samples, uint16_t num_samples)
|
||||
}
|
||||
}
|
||||
|
||||
/* 무응답 샘플 확인 */
|
||||
static bool echo_adc_samples_all_zero(const uint16_t *samples, uint16_t num_samples)
|
||||
{
|
||||
for (uint16_t i = 0; i < num_samples; i++)
|
||||
{
|
||||
if (samples[i] != 0U)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* 안정 우선 캡처 */
|
||||
static int echo_adc_capture_slow(uint16_t *samples, uint16_t num_samples)
|
||||
{
|
||||
for (uint16_t i = 0; i < num_samples; i++)
|
||||
{
|
||||
int err = echo_adc_transfer_rx(&raw_capture[i * 2U], 2U);
|
||||
if (err)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
echo_adc_unpack_samples(samples, num_samples);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 속도 우선 캡처 */
|
||||
/* 샘플마다 CS를 직접 제어하고 SPIM RX 2바이트를 수행 */
|
||||
static int echo_adc_capture_fast(uint16_t *samples, uint16_t num_samples)
|
||||
{
|
||||
NRF_SPIM_Type *spim = echo_spim.p_reg;
|
||||
@@ -116,9 +89,19 @@ static int echo_adc_capture_fast(uint16_t *samples, uint16_t num_samples)
|
||||
cs_port->OUTCLR = cs_mask;
|
||||
spim->TASKS_START = 1;
|
||||
|
||||
/* 전송 완료 대기 */
|
||||
/* 전송 완료 대기: SPIM이 꼬인 경우 IRQ lock 상태로 영구 정지하지 않도록 timeout 처리 */
|
||||
uint32_t timeout = ECHO_ADC_SPIM_END_TIMEOUT_LOOPS;
|
||||
while (!spim->EVENTS_END)
|
||||
{
|
||||
if (--timeout == 0U)
|
||||
{
|
||||
spim->EVENTS_STOPPED = 0;
|
||||
spim->TASKS_STOP = 1;
|
||||
cs_port->OUTSET = cs_mask;
|
||||
spim->EVENTS_END = 0;
|
||||
DBG_PRINTF("[ECHO] SPIM END timeout sample=%u\r\n", i);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
cs_port->OUTSET = cs_mask;
|
||||
@@ -130,14 +113,14 @@ static int echo_adc_capture_fast(uint16_t *samples, uint16_t num_samples)
|
||||
|
||||
int echo_adc_init(void)
|
||||
{
|
||||
/* 중복 초기화 방지 */
|
||||
// 중복 초기화 방지
|
||||
if (echo_adc_initialized || nrfx_spim_init_check(&echo_spim))
|
||||
{
|
||||
echo_adc_initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* GPIO 준비 확인 */
|
||||
// GPIO 준비 확인
|
||||
const struct gpio_dt_spec *pins[] = { &echo_sclk, &echo_miso, &echo_cs };
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(pins); i++)
|
||||
@@ -190,7 +173,7 @@ int echo_adc_wake(void)
|
||||
|
||||
int echo_adc_capture(uint16_t *samples, uint16_t num_samples)
|
||||
{
|
||||
/* 입력값 검사 */
|
||||
// 입력값 검사
|
||||
if (samples == NULL)
|
||||
{
|
||||
return -EINVAL;
|
||||
@@ -207,14 +190,5 @@ int echo_adc_capture(uint16_t *samples, uint16_t num_samples)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 빠른 캡처 우선 */
|
||||
err = echo_adc_capture_fast(samples, num_samples);
|
||||
if ((err == 0) && !echo_adc_samples_all_zero(samples, num_samples))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 실패 시 안정 캡처 */
|
||||
DBG_PRINTF("[ECHO] fast capture fallback err=%d\r\n", err);
|
||||
return echo_adc_capture_slow(samples, num_samples);
|
||||
return echo_adc_capture_fast(samples, num_samples);
|
||||
}
|
||||
|
||||
+36
-49
@@ -1,16 +1,7 @@
|
||||
/*******************************************************************************
|
||||
* @file imu_i2c.c
|
||||
* @brief ICM42670P IMU Driver (Zephyr I2C API)
|
||||
*
|
||||
* 동작 방식 (매 측정 시):
|
||||
* 1) GYRO_CONFIG0 = 0x09 → ±2000dps, 100Hz ODR
|
||||
* 2) ACCEL_CONFIG0 = 0x29 → ±4g, 100Hz ODR
|
||||
* 3) PWR_MGMT0 = 0x0F → accel(low-noise) + gyro(low-noise) ON
|
||||
* 4) 80ms 대기 (자이로 스타트업 최소 45ms + 여유)
|
||||
* 5) 0x0B부터 12바이트 연속 읽기: accel XYZ(6B) + gyro XYZ(6B)
|
||||
* 6) 빅엔디안 → int16_t 변환
|
||||
* 7) PWR_MGMT0 = 0x00 → 슬립 (전력 절감)
|
||||
*
|
||||
|
||||
* I2C 핀: SCL=P1.14, SDA=P1.15 (overlay에서 설정)
|
||||
******************************************************************************/
|
||||
|
||||
@@ -29,34 +20,34 @@
|
||||
* 디바이스트리 / I2C 설정
|
||||
*============================================================================*/
|
||||
#define IMU_I2C_NODE DT_NODELABEL(i2c0)
|
||||
#define IMU_I2C_ADDR 0x68 /* ICM42670P 기본 I2C 주소 (AD0=LOW) */
|
||||
#define IMU_I2C_ADDR 0x68 // ICM42670P 기본 I2C 주소 (AD0=LOW)
|
||||
|
||||
/*==============================================================================
|
||||
* ICM42670P 레지스터 주소
|
||||
*============================================================================*/
|
||||
#define REG_PWR_MGMT0 0x1F /* 전원 관리: accel/gyro 동작 모드 */
|
||||
#define REG_GYRO_CONFIG0 0x20 /* 자이로 FSR + ODR 설정 */
|
||||
#define REG_ACCEL_CONFIG0 0x21 /* 가속도 FSR + ODR 설정 */
|
||||
#define REG_GYRO_CONFIG1 0x23 /* 자이로 LN 필터 BW */
|
||||
#define REG_ACCEL_CONFIG1 0x24 /* 가속도 LN 필터 BW */
|
||||
#define REG_FIFO_CONFIG1 0x28 /* FIFO bypass/mode */
|
||||
#define REG_FIFO_CONFIG2 0x29 /* FIFO watermark LSB */
|
||||
#define REG_FIFO_CONFIG3 0x2A /* FIFO watermark MSB */
|
||||
#define REG_INTF_CONFIG0 0x35 /* FIFO count/endian 설정 */
|
||||
#define REG_FIFO_COUNTH 0x3D /* FIFO count high */
|
||||
#define REG_FIFO_DATA 0x3F /* FIFO data pop */
|
||||
#define REG_BLK_SEL_W 0x79 /* MREG write block */
|
||||
#define REG_MADDR_W 0x7A /* MREG write addr */
|
||||
#define REG_M_W 0x7B /* MREG write data */
|
||||
#define REG_BLK_SEL_R 0x7C /* MREG read block */
|
||||
#define REG_MADDR_R 0x7D /* MREG read addr */
|
||||
#define REG_M_R 0x7E /* MREG read data */
|
||||
#define REG_TEMP_DATA1 0x09 /* IMU 내부 온도 상위 바이트 */
|
||||
#define REG_ACCEL_DATA_X1 0x0B /* 가속도 X축 상위 바이트 (시작 레지스터) */
|
||||
#define REG_WHO_AM_I 0x75 /* WHOAMI 레지스터 */
|
||||
#define REG_MCLK_RDY 0x00 /* MCLK ready */
|
||||
#define REG_SIGNAL_PATH_RESET 0x02 /* FIFO flush */
|
||||
#define ICM_WHOAMI 0x67 /* ICM42670P 식별값 */
|
||||
#define REG_PWR_MGMT0 0x1F // 전원 관리: accel/gyro 동작 모드
|
||||
#define REG_GYRO_CONFIG0 0x20 // 자이로 FSR + ODR 설정
|
||||
#define REG_ACCEL_CONFIG0 0x21 // 가속도 FSR + ODR 설정
|
||||
#define REG_GYRO_CONFIG1 0x23 // 자이로 LN 필터 BW
|
||||
#define REG_ACCEL_CONFIG1 0x24 // 가속도 LN 필터 BW
|
||||
#define REG_FIFO_CONFIG1 0x28 // FIFO bypass/mode
|
||||
#define REG_FIFO_CONFIG2 0x29 // FIFO watermark LSB
|
||||
#define REG_FIFO_CONFIG3 0x2A // FIFO watermark MSB
|
||||
#define REG_INTF_CONFIG0 0x35 // FIFO count/endian 설정
|
||||
#define REG_FIFO_COUNTH 0x3D // FIFO count high
|
||||
#define REG_FIFO_DATA 0x3F // FIFO data pop
|
||||
#define REG_BLK_SEL_W 0x79 // MREG write block
|
||||
#define REG_MADDR_W 0x7A // MREG write addr
|
||||
#define REG_M_W 0x7B // MREG write data
|
||||
#define REG_BLK_SEL_R 0x7C // MREG read block
|
||||
#define REG_MADDR_R 0x7D // MREG read addr
|
||||
#define REG_M_R 0x7E // MREG read data
|
||||
#define REG_TEMP_DATA1 0x09 // IMU 내부 온도 상위 바이트
|
||||
#define REG_ACCEL_DATA_X1 0x0B // 가속도 X축 상위 바이트 (시작 레지스터)
|
||||
#define REG_WHO_AM_I 0x75 // WHOAMI 레지스터
|
||||
#define REG_MCLK_RDY 0x00 // MCLK ready
|
||||
#define REG_SIGNAL_PATH_RESET 0x02 // FIFO flush
|
||||
#define ICM_WHOAMI 0x67 // ICM42670P 식별값
|
||||
|
||||
/* 자이로 스타트업 대기 시간 (ms) — 스펙 최소 45ms, 80ms로 여유 확보 */
|
||||
#define IMU_GYRO_STARTUP_MS 80
|
||||
@@ -95,9 +86,7 @@ static const struct device *i2c_bus;
|
||||
static bool fifo_active;
|
||||
static uint8_t fifo_raw[IMU_FIFO_PACKET_BYTES * IMU_FIFO_MAX_RECORDS];
|
||||
|
||||
/*==============================================================================
|
||||
* 내부 I2C 래퍼
|
||||
*============================================================================*/
|
||||
/* 내부 I2C 래퍼 */
|
||||
|
||||
/* 레지스터 1바이트 쓰기: [reg, val] 2바이트 TX */
|
||||
static int imu_write_reg(uint8_t reg, uint8_t val)
|
||||
@@ -375,9 +364,7 @@ static uint16_t imu_fifo_compact_records(uint16_t record_count)
|
||||
return write_idx;
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 공개 API
|
||||
*============================================================================*/
|
||||
/* 공개 API */
|
||||
|
||||
int imu_init(void)
|
||||
{
|
||||
@@ -407,31 +394,31 @@ int imu_read(int16_t accel[3], int16_t gyro[3])
|
||||
uint8_t raw[12];
|
||||
int ret;
|
||||
|
||||
/* 자이로 설정: ±2000dps FSR, 100Hz ODR */
|
||||
// 자이로 설정: ±2000dps FSR, 100Hz ODR
|
||||
ret = imu_write_reg(REG_GYRO_CONFIG0, 0x09);
|
||||
if (ret) { DBG_PRINTF("[IMU] FAIL — gyro config write (ret=%d)\r\n", ret); return -1; }
|
||||
|
||||
/* 가속도 설정: ±4g FSR, 100Hz ODR */
|
||||
// 가속도 설정: ±4g FSR, 100Hz ODR
|
||||
ret = imu_write_reg(REG_ACCEL_CONFIG0, 0x29);
|
||||
if (ret) { DBG_PRINTF("[IMU] FAIL — accel config write (ret=%d)\r\n", ret); return -1; }
|
||||
|
||||
ret = imu_set_data_big_endian();
|
||||
if (ret) { DBG_PRINTF("[IMU] FAIL — endian config write (ret=%d)\r\n", ret); return -1; }
|
||||
|
||||
/* 전원 ON: accel(저잡음) + gyro(저잡음) 모두 활성화 */
|
||||
// 전원 ON: accel(저잡음) + gyro(저잡음) 모두 활성화
|
||||
ret = imu_write_reg(REG_PWR_MGMT0, 0x0F);
|
||||
if (ret) { DBG_PRINTF("[IMU] FAIL — power on write (ret=%d)\r\n", ret); return -1; }
|
||||
|
||||
/* 자이로 스타트업 대기 */
|
||||
// 자이로 스타트업 대기
|
||||
k_msleep(IMU_GYRO_STARTUP_MS);
|
||||
|
||||
/* ACCEL_DATA_X1(0x0B)부터 12바이트 연속 읽기
|
||||
* [0..5] = accel X,Y,Z (MSB first)
|
||||
* [6..11] = gyro X,Y,Z (MSB first) */
|
||||
// ACCEL_DATA_X1(0x0B)부터 12바이트 연속 읽기
|
||||
// [0..5] = accel X,Y,Z (MSB first)
|
||||
// [6..11] = gyro X,Y,Z (MSB first)
|
||||
ret = imu_read_regs(REG_ACCEL_DATA_X1, raw, 12);
|
||||
if (ret) { DBG_PRINTF("[IMU] FAIL — data read (ret=%d)\r\n", ret); return -2; }
|
||||
|
||||
/* 빅엔디안 → int16_t 변환 */
|
||||
// 빅엔디안 → int16_t 변환
|
||||
accel[0] = (int16_t)((raw[0] << 8) | raw[1]);
|
||||
accel[1] = (int16_t)((raw[2] << 8) | raw[3]);
|
||||
accel[2] = (int16_t)((raw[4] << 8) | raw[5]);
|
||||
@@ -441,7 +428,7 @@ int imu_read(int16_t accel[3], int16_t gyro[3])
|
||||
|
||||
//DBG_PRINTF("[IMU] msp: A=(%6d,%6d,%6d) G=(%6d,%6d,%6d)\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
|
||||
|
||||
/* 슬립 모드: 전력 절감 */
|
||||
// 슬립 모드: 전력 절감
|
||||
imu_write_reg(REG_PWR_MGMT0, 0x00);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -11,33 +11,25 @@
|
||||
#include "led_control.h"
|
||||
#include "debug_print.h"
|
||||
|
||||
/*==============================================================================
|
||||
* Devicetree LED specs
|
||||
*============================================================================*/
|
||||
/* 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
|
||||
*============================================================================*/
|
||||
/* Color constants */
|
||||
#define COLOR_NONE 0
|
||||
#define COLOR_GREEN 1
|
||||
#define COLOR_ORANGE 2
|
||||
|
||||
/*==============================================================================
|
||||
* Error pattern constants (State 7)
|
||||
*============================================================================*/
|
||||
/* 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
|
||||
*============================================================================*/
|
||||
/* Pattern table */
|
||||
typedef struct {
|
||||
uint32_t on_ms;
|
||||
uint32_t off_ms;
|
||||
@@ -56,9 +48,7 @@ static const led_pattern_t m_patterns[LED_STATE_COUNT] = {
|
||||
[LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true },
|
||||
};
|
||||
|
||||
/*==============================================================================
|
||||
* Module variables
|
||||
*============================================================================*/
|
||||
/* Module variables */
|
||||
static struct k_timer m_led_timer;
|
||||
|
||||
static led_state_t m_current_state = LED_STATE_OFF;
|
||||
@@ -68,9 +58,7 @@ static bool m_phase_on;
|
||||
static uint8_t m_error_blink_cnt;
|
||||
static uint8_t m_error_phase;
|
||||
|
||||
/*==============================================================================
|
||||
* GPIO helpers
|
||||
*============================================================================*/
|
||||
/* 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); }
|
||||
@@ -89,18 +77,14 @@ static void led_color_on(uint8_t color)
|
||||
else if (color == COLOR_ORANGE) function_led_on();
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* Timer helper
|
||||
*============================================================================*/
|
||||
/* 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
|
||||
*============================================================================*/
|
||||
/* Error pattern state machine */
|
||||
static void error_pattern_start(void)
|
||||
{
|
||||
m_error_blink_cnt = 0;
|
||||
@@ -113,12 +97,12 @@ static void error_pattern_tick(void)
|
||||
{
|
||||
switch (m_error_phase)
|
||||
{
|
||||
case 0: /* ON period done -> OFF */
|
||||
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 */
|
||||
case 1: // OFF period done
|
||||
m_error_blink_cnt++;
|
||||
if (m_error_blink_cnt < ERROR_BLINK_COUNT) {
|
||||
m_error_phase = 0;
|
||||
@@ -129,7 +113,7 @@ static void error_pattern_tick(void)
|
||||
timer_start_ms(ERROR_PAUSE_MS);
|
||||
}
|
||||
break;
|
||||
case 2: /* Pause done -> restart */
|
||||
case 2: // Pause done -> restart
|
||||
m_error_blink_cnt = 0;
|
||||
m_error_phase = 0;
|
||||
led_color_on(COLOR_ORANGE);
|
||||
@@ -140,9 +124,7 @@ static void error_pattern_tick(void)
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* Timer callback
|
||||
*============================================================================*/
|
||||
/* Timer callback */
|
||||
static void led_timer_handler(struct k_timer *timer)
|
||||
{
|
||||
ARG_UNUSED(timer);
|
||||
@@ -157,7 +139,7 @@ static void led_timer_handler(struct k_timer *timer)
|
||||
|
||||
if (m_phase_on)
|
||||
{
|
||||
/* ON -> OFF transition */
|
||||
// ON -> OFF transition
|
||||
led_all_off();
|
||||
m_phase_on = false;
|
||||
|
||||
@@ -171,12 +153,12 @@ static void led_timer_handler(struct k_timer *timer)
|
||||
led_all_off();
|
||||
m_current_state = LED_STATE_OFF;
|
||||
}
|
||||
/* POWER_ON: stays lit, no timer */
|
||||
// POWER_ON: stays lit, no timer
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* OFF -> ON transition */
|
||||
// OFF -> ON transition
|
||||
if (p->repeat)
|
||||
{
|
||||
led_color_on(p->color);
|
||||
@@ -186,10 +168,7 @@ static void led_timer_handler(struct k_timer *timer)
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* Public functions
|
||||
*============================================================================*/
|
||||
|
||||
/* Public functions */
|
||||
void led_init(void)
|
||||
{
|
||||
gpio_pin_configure_dt(&led_ble, GPIO_OUTPUT_INACTIVE);
|
||||
@@ -242,5 +221,5 @@ 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 */
|
||||
m_current_state = LED_STATE_OFF; // no pattern running
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
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_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;
|
||||
|
||||
|
||||
+26
-24
@@ -123,13 +123,13 @@ static void piezo_drive_idle(void)
|
||||
|
||||
int piezo_init(void)
|
||||
{
|
||||
/* 중복 초기화 방지 */
|
||||
// 중복 초기화 방지
|
||||
if (piezo_initialized)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 모든 제어 핀 목록 */
|
||||
// 모든 제어 핀 목록
|
||||
const struct gpio_dt_spec *pins[] =
|
||||
{
|
||||
&piezo_pwr, &piezo_pe, &piezo_p_out, &piezo_n_out,
|
||||
@@ -138,14 +138,14 @@ int piezo_init(void)
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(pins); i++)
|
||||
{
|
||||
/* GPIO 포트 준비 확인 */
|
||||
// GPIO 포트 준비 확인
|
||||
if (!device_is_ready(pins[i]->port))
|
||||
{
|
||||
DBG_PRINTF("[PIEZO] GPIO device not ready (%d)\r\n", (int)i);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* 초기 출력 Low */
|
||||
// 초기 출력 Low
|
||||
int err = gpio_pin_configure_dt(pins[i], GPIO_OUTPUT_INACTIVE);
|
||||
if (err)
|
||||
{
|
||||
@@ -154,7 +154,7 @@ int piezo_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* 직접 제어용 핀/마스크 캐시 */
|
||||
// 직접 제어용 핀/마스크 캐시
|
||||
piezo_pe_abs = gpio_abs_pin(&piezo_pe);
|
||||
piezo_p_out_abs = gpio_abs_pin(&piezo_p_out);
|
||||
piezo_n_out_abs = gpio_abs_pin(&piezo_n_out);
|
||||
@@ -172,32 +172,33 @@ int piezo_init(void)
|
||||
|
||||
void piezo_power_on(void)
|
||||
{
|
||||
/* 필요 시 자동 초기화 */
|
||||
// 필요 시 자동 초기화
|
||||
if (!piezo_initialized && piezo_init() != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 피에조 전원 인가 */
|
||||
// 피에조 전원 인가
|
||||
gpio_pin_set_dt(&piezo_pwr, 1);
|
||||
}
|
||||
|
||||
void piezo_power_off(void)
|
||||
{
|
||||
/* 필요 시 자동 초기화 */
|
||||
// 필요 시 자동 초기화
|
||||
if (!piezo_initialized && piezo_init() != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 출력 정지 후 전원 차단 */
|
||||
// 출력 정지 후 전원 차단
|
||||
piezo_drive_idle();
|
||||
gpio_pin_set_dt(&piezo_pwr, 0);
|
||||
}
|
||||
|
||||
/* Piezo 채널 선택 */
|
||||
int piezo_select_channel(uint8_t channel)
|
||||
{
|
||||
/* 필요 시 자동 초기화 */
|
||||
// 필요 시 자동 초기화
|
||||
if (!piezo_initialized)
|
||||
{
|
||||
int err = piezo_init();
|
||||
@@ -207,35 +208,36 @@ int piezo_select_channel(uint8_t channel)
|
||||
}
|
||||
}
|
||||
|
||||
/* 채널 범위 검사 */
|
||||
// 채널 범위 검사
|
||||
if (channel >= PIEZO_NUM_CHANNELS)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* MUX 채널 선택 */
|
||||
// MUX 채널 선택
|
||||
gpio_pin_set_dt(&mux_en_a, mux_map[channel].en_a);
|
||||
gpio_pin_set_dt(&mux_en_b, mux_map[channel].en_b);
|
||||
gpio_pin_set_dt(&mux_sel0, mux_map[channel].sel0);
|
||||
gpio_pin_set_dt(&mux_sel1, mux_map[channel].sel1);
|
||||
|
||||
/* MUX 안정화 대기 */
|
||||
// MUX 안정화 대기
|
||||
k_busy_wait(PIEZO_MUX_SETTLE_US);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Piezo Burst Switching : 현재는 2.1MHz 고정, 1.8~2.2MHz 추가 예정 */
|
||||
void piezo_burst_sw(uint8_t cycles)
|
||||
{
|
||||
/* 초기화/횟수 검사 */
|
||||
// 초기화/횟수 검사
|
||||
if (!piezo_initialized || cycles == 0U)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 타이밍 흔들림 방지 */
|
||||
// 타이밍 흔들림 방지
|
||||
unsigned int key = irq_lock();
|
||||
|
||||
/* P1 비제어 핀 상태 보존 */
|
||||
// P1 비제어 핀 상태 보존
|
||||
uint32_t saved_p1_out = NRF_P1->OUT;
|
||||
uint32_t p1_ctrl_mask = piezo_p_out_mask | piezo_n_out_mask | piezo_dmp_mask;
|
||||
uint32_t p1_all_low = saved_p1_out & ~p1_ctrl_mask;
|
||||
@@ -243,28 +245,28 @@ void piezo_burst_sw(uint8_t cycles)
|
||||
uint32_t p1_p_low_n_high = p1_all_low | piezo_n_out_mask;
|
||||
uint32_t p1_dmp_high = p1_all_low | piezo_dmp_mask;
|
||||
|
||||
/* 구동 Enable 전 초기 Low 정렬 */
|
||||
// 구동 Enable 전 초기 Low 정렬
|
||||
NRF_P0->OUTCLR = piezo_pe_mask;
|
||||
NRF_P1->OUT = p1_all_low;
|
||||
NRF_P0->OUTSET = piezo_pe_mask; /* PE High */
|
||||
NRF_P0->OUTSET = piezo_pe_mask; // PE High
|
||||
|
||||
for (uint8_t i = 0; i < cycles; i++)
|
||||
{
|
||||
/* P High / N Low */
|
||||
// P High / N Low
|
||||
NRF_P1->OUT = p1_p_high_n_low;
|
||||
nop_delay_14();
|
||||
|
||||
/* P Low / N High */
|
||||
// P Low / N High
|
||||
NRF_P1->OUT = p1_p_low_n_high;
|
||||
nop_delay_9();
|
||||
}
|
||||
|
||||
/* 잔류 전하 방전 */
|
||||
// 잔류 전하 방전
|
||||
NRF_P1->OUT = p1_dmp_high;
|
||||
nop_delay_32(); /* DMP High 폭 */
|
||||
nop_delay_32(); // DMP High 폭
|
||||
|
||||
/* 출력 Low 복귀 */
|
||||
// 출력 Low 복귀
|
||||
NRF_P1->OUT = p1_all_low;
|
||||
NRF_P0->OUTCLR = piezo_pe_mask; /* PE Low */
|
||||
NRF_P0->OUTCLR = piezo_pe_mask; // PE Low
|
||||
irq_unlock(key);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user