Compare commits

..

76 Commits

Author SHA1 Message Date
jh.chun b47e11dc08 프로젝트 정리: 미사용 파일 삭제 2026-04-15 14:18:54 +09:00
jh.chun fae5957c0c 프로젝트 정리: 미사용 파일 삭제 2026-04-15 12:01:03 +09:00
jh.chun 37aff21af8 프로젝트 정리: 미사용 드라이버 삭제 2026-04-15 11:49:02 +09:00
jh.chun 555a5df241 프로젝트 정리: gitignore 및 타 계정 uvguix 파일 정리 2026-04-15 11:38:04 +09:00
jh.chun 7db24d762f 프로젝트 정리: components/toolchain 삭제
- keil CMSIS 팩 사용
- GCC/IAR 파일 미사용
2026-04-15 11:19:11 +09:00
jh.chun a0eaa01d27 프로젝트 정리: components/softdevice/mbr 삭제
- 중복 헤더
- s140/headers/nrf52에 동일 파일 존재
2026-04-15 11:15:25 +09:00
jh.chun fc00d41293 프로젝트 정리: components/drivers_nrf/ 삭제
- 레거시 폴더(SoftDevice가 없을 때 필요한 파일 및 구형 SPI/TWI 마스터 드라이버 등)
- nrfx로 대체됨
2026-04-15 11:11:42 +09:00
jh.chun 9ed0e55b10 프로젝트 정리: components/ble 미사용 BLE 서비스 등 삭제
- ble_dfu, ble_nus만 사용
2026-04-15 11:06:57 +09:00
jh.chun 06b985e178 프로젝트 정리: components/libraries 미사용 파일 삭제 2026-04-15 11:02:11 +09:00
jh.chun 5d17bb7c6e BLE 송신 출력(TX power) 8 -> 4 테스트 2026-04-15 10:59:45 +09:00
jh.chun 59fe590ce4 프로젝트 정리: external 폴더 미사용 파일 삭제 2026-04-15 10:17:52 +09:00
jh.chun 9e4eed4ae6 프로젝트 정리: 미사용 파일 제거
- 루트 cp.bat
- 레거시 워크스페이스
- 백업본
2026-04-15 09:43:07 +09:00
jh.chun 750e1116cd 측정 데이터 BE 통일
- reb:/red:/rdb:/rdd:/raa: 패킷의 데이터 형식 BE
2026-04-15 09:32:19 +09:00
jh.chun 2feef0589d 파싱 BE 2026-04-14 20:00:10 +09:00
jh.chun af439862a0 기타 2026-04-14 19:59:36 +09:00
jhChun 3ec6afec36 MUX 전환 후 ADC S/H 커패시터 안정화를 위한 dummy read 2026-04-07 14:17:06 +09:00
jhChun 6cb927e831 DEBUG 빌드 해제
- 에러 처리 시 먹통 현상 = DEBUG 빌드
2026-04-03 16:43:29 +09:00
jhChun 47a31c1eb4 SAADC 타이밍 충돌로 인한 FW RESET 방지: 배터리 저전압 측정 주기 및 init 실패시 처리 변경
- 배터리 저전압 측정 주기 5초 -> 60초로 변경(충돌 확률 감소)
- SAADC init 실패 시 APP_ERROR_CHECK -> 측정 스킵 처리

***** 현재 에러 처리는 APP_ERROR_CHECK로 되어있는데, 에러 발생 시 APP_ERROR_CHECK에 걸리면 HardFault 무한 루프에 빠져서 기기가 먹통이 됨. 런타임 중 에러가 발생해도 안전하게 복구되도록 에러 처리 전환 필요함. *****
2026-04-02 11:27:28 +09:00
jhChun 5ee39f834e Slave Latency 4 -> 0 변경 2026-04-02 11:17:06 +09:00
jhChun 5128c44912 DR_ADC_ECHO_SAMPLES_MAX 100 -> 119 2026-04-01 16:22:42 +09:00
jhChun 8fdc153ed5 블루투스 스캐닝 LED 1초 간격 점멸 -> 0.5초 간격 점멸 2026-03-31 17:58:49 +09:00
jhChun d8ef9bcb41 dev 모드 BLE 연결 수정
- dev 모드인 경우 보안 x
2026-03-31 17:58:33 +09:00
jhChun 007e0b5683 Ver VBTFW0102 2026-03-31 14:27:14 +09:00
jhChun d63a9cfa3c BLE 보안 실패 시 자동 재페어링 및 버그 3건 수정
- 앱과 기기 사이 키가 불일치하는 경우 새로운 키 생성 요청(재페어링 허용)
- main.c: PM_EVT_CONN_SEC_CONFIG_REQ 중복 핸들러 제거
- main.c: DEV 모드에서 pm_peers_delete() 이중 호출 방지
- main.c: 활성 연결 중 advertising 재시작 방지(m_conn_handle 체크)
- ble_quick_security: 보안 실패 시 bond 삭제 + 자동 재페어링 시도
- ble_quick_security: allow_repairing = true (항상 재페어링 허용)
2026-03-31 14:17:35 +09:00
jhChun 217d741d31 LED 상태 설정 명령 추가
- BLE 명령으로 LED 상태 설정
2026-03-31 14:01:02 +09:00
jhChun f464838ed4 Connection Interval 30ms로 변경
- 기존: MIN=20, MAX=75
- 변경: MIN=30, MAX=30
- GAP Event Length=24(30ms)에 맞춰 변경함
- Piezo 6채널 ADC 데이터 7패킷 송신 시 3~4패킷/CI로, ~60ms 소요
2026-03-31 09:55:47 +09:00
jhChun a150d1da96 LED 직접 제어 모듈 추가(BSP 대체)
- 전원 ON/OFF, 블루투스 스캐닝 초록 LED 적용 완료
2026-03-30 18:00:38 +09:00
jhChun 11a0b3f11a FW 버전 업데이트 2026-03-30 17:10:17 +09:00
jhChun 5e27eb762d BLE Piezo 6채널 데이터 패킷 병합 (reb+red -> reb 단일 패킷)
- BLE_MTU_SIZE 240 -> 244 (ATT MTU 247 - 3 = 244, 기존 4B 낭비 해소)
- reb: 헤더 축소(14B → 6B) 후 데이터 병합 -> 119샘플까지 단일 패킷(현재 100샘플)
- reb: 헤더에서 peak_raw/peak_index/baseline_raw/버전마커 제거
  → PC에서 raw 데이터로 직접 계산, 단일 패킷이므로 버전마커 불필요
- 채널 간 딜레이 50ms -> 5ms (dr_binary_tx_safe 내부 재시도로 TX 보장)
- delta 전송(rdb/rdd)도 동일 방식 적용, 종료 패킷(ree:/rde:) 제거
- 채널 완료 판단: 종료 패킷(ree:) 제거, reb: 수신 시 채널 완료 (100샘플 기준 단일 패킷)
- 전체 완료는 기존과 동일하게 raa:로 판단
2026-03-30 16:37:41 +09:00
jhChun 689ad29aa6 BLE 명령 수신 시 Connection interval 재협상 요청 추가
- Windows BLE 스택이 장시간 연결 시 CI를 수초까지 늘려 응답이 지연되는 문제 대응
- 30초에 1회 20~75ms로 돌려달라고 요청
2026-03-30 15:27:22 +09:00
jhChun aaf22d942a BLE 연결 실패 발생 시 추적을 위한 RTT 로그 추가
- 0: IMU 값 송신(msp)- 1: 배터리 SAADC 시작- 2: 배터리 콜백 완료(battery_event_handler)- 3: 배터리 대기 루프 탈출- 4: IMU 읽기 완료- 5: Piezo RX/TX Active- 6: 온도 SAADC 시작- 7: 온도 콜백 완료(tmp235_voltage_handler)- 8: 온도 대기 루프 탈출- 9: rbb: 패킷 전송- 10: Piezo 캡처 시작(maa_async_start)- 11: 전체 종료(mbb)
2026-03-30 15:22:59 +09:00
jhChun 91e294da5a 2M PHY 능동 요청 추가
- BLE 5.0 2M PHY는 심볼 레이트를 1Msps -> 2Msps로 높여 동일 패킷을 절반 시간에 전송- 연결 즉시 Peripheral에서 2M PHY를 능동 요청- 2M PHY 미지원 Central인 경우 1M 유지
2026-03-30 14:49:34 +09:00
jhChun 856cf084b2 GAP Event Length 확장(6 -> 24) 및 RAM 주소 조정
- GAP Event Length: BLE 통신 시 Connection interval 마다 Central과 Peripheral이 데이터를 주고 받을 수 있는 시간 슬롯으로 단위는 1.25ms
- SoftDevice RAM 사용량 증가로 시작 주소 조정(0x20002AF8 → 0x20002C00)
2026-03-30 14:48:32 +09:00
jhChun 9e48c084b4 SAADC 안정성 개선: 버퍼/해제 순서 수정
- 배터리 SAADC 더블 버퍼 -> 싱글 버퍼(1회 측정 후 해제이므로 불필요)
- 콜백 내 buffer_convert 제거(해제 직전 다음 버퍼 등록 제거)
- uninit 순서 변경: channel_uninit -> uninit(채널 먼저 해제)
- 5초마다 배터리를 측정하는 battery_loop에 info4(전체 측정) 체크 추가(전체 측정 중 SAADC 동시 init 충돌 방지)
2026-03-30 12:10:53 +09:00
jhChun a9fa050bb0 SAADC 우선순위 변경
- NRFX_SAADC_CONFIG_IRQ_PRIORITY = 0 -> 6
- C:\jhChun\VesiScan-Basic_jhChun_new\components\libraries\util\app_util_platform.h
- 위 경로에 Cortex-M4(nRF52840) 우선순위 배분표가 정의되어 있음
- 0, 1, 4는 SoftDevice 전용, 앱 사용 가능한 우선순위는 2, 3, 5, 6(권장), 7
- 권장되는 우선순위인 6위로 변경
- SAADC 해상도 10 -> 12bit 변경 및 콜백 내 연산 int -> float 변경으로 ISR 처리 시간이 길어지고, SoftDevice와 충돌 가능성이 높아지는 것으로 추측
- 충돌이 발생하는 경우 콜백 지연 또는 누락
2026-03-30 11:41:59 +09:00
jhChun de9774a7ce 양산 모드인 경우 BLE 보안 실패 처리 개선
- PM 핸들러 중복 호출 제거
- conn_sec_failed 재페어링 로직 일원화
- NRF_ERROR_BUSY 크래시 수정: 보안 실패 시 보드 리셋되는 현상 개선
2026-03-27 16:47:46 +09:00
jhChun 3455bc2b38 DEFAULT_PASSKEY 매크로 통합 + advertising_start 조건 변경
- 본딩 삭제가 실행되지 않는 버그 개선
2026-03-27 16:17:09 +09:00
jhChun 153c4a027f 배터리, 온도 SAADC 해상도 12bit 전환 및 float 연산 적용
- SAADC 해상도 10bit -> 12bit
- int 연산 -> float 연산 -> BLE 전송 시 int
2026-03-27 16:12:31 +09:00
jhChun 7459fffc20 배터리 전압 TACQ 조정 및 콜백 대기 조건 변경
- 배터리, 온도 모두 오버샘플링 X4 및 TACQ=10us 적용
- 배터리 측정 시 SAADC 콜백 완료 최대 100ms 대기, 콜백 오면 즉시 탈출(온도 측정 시와 동일)
- 전체 측정 시 SAADC 점유 및 충돌 방지
2026-03-27 13:54:39 +09:00
jhChun 4c2948eb43 Firmware 버전 식별 코드 정의를 main.h로 이동
- parser.c: DR_DEVICE_VERSION 제거
2026-03-27 10:52:26 +09:00
jhChun 89cef54f86 cpd_eraseALL.bat 추가: 전체 erase 개발용 플래싱 스크립트 2026-03-27 10:50:56 +09:00
jhChun 923f29db82 Firmware 버전 식별 코드 정의를 main.h로 이동
- parser.c: DR_DEVICE_VERSION 제거
2026-03-27 10:05:46 +09:00
jhChun 92ebe8ebb4 기본 버전 정보 및 패스키 기본값을 fstorage.h 매크로로 통합 2026-03-27 09:43:50 +09:00
jhChun 5db067f483 FDS 영역 보존 erase 옵션 추가
- cpd.bat: 기존 erase 옵션 설정 x, DEFAULT=ERASE_ALL(칩 전체 삭제)
2026-03-27 08:53:27 +09:00
jhChun f666892987 배터리, 온도 전압 오버샘플링 및 TACQ 조정
- 배터리: 샘플링 4, TACQ 20us
- 온도: 샘플링 4, TACQ 10us(기본값)
2026-03-26 16:59:38 +09:00
jhChun 379bf9c294 배터리 전압 SAADC 오버샘플링 X4
- battery_saadc.c: 기존 1회 샘플링 -> 4회 샘플링 및 평균
- 배터리 전압 ADC 노이즈에 의한 측정값 흔들림 개선
2026-03-26 14:52:40 +09:00
jhChun 6fbd30244f 압력센서 관련 코드 정리
- battery_saadc.c/.h: 압력센서 미탑재로 관련 코드 제거
2026-03-26 12:23:40 +09:00
jhChun b820589883 GPIO 초기화 POWER_HOLD(P0.8) 중복 설정 제거
- main.c: power_hold_init()에서 전원버튼(P0.8) 설정 후 gpio_init()에서 중복 설정
- gpio_init()에서 중복 설정 부분 제거
2026-03-26 11:42:26 +09:00
jhChun af7afff117 EEPROM 레거시 코드 정리
- EEP_WP, eeprom_control, power_gpio_init 삭제
2026-03-26 11:39:12 +09:00
jhChun dd3bce4f94 부트 로그 수정 2026-03-26 11:24:13 +09:00
jhChun 9580bffa65 DEBUG_MINIMAL_BOOT 삭제
- main.c: DEBUG_MINIMAL_BOOT 삭제 및 분기문 정리
- main.c: minimal_gpio_init() + full_gpio_init() → gpio_init() 하나로 통합
- main.c: icm42670_init() 분기 제거, advertising_start()만 남김
- power_control.c: SW I2C/센서 전원 시퀀스 제거, p_order = 2로 즉시 완료
2026-03-26 11:12:26 +09:00
jhChun e461f59a5f 현재 IMU 센서는 폴링 방식 적용
- app_raw_main.c: 인터럽트 방식 적용을 위한 레거시 파일
2026-03-26 11:06:52 +09:00
jhChun ed3a8d7acd BLE TX 재시도 대기 시간 단축
- main.c: dr_binary_tx_safe 함수 nrf_delay_ms(5) × 최대 100회 -> nrf_delay_ms(2) × 최대 20회
- 전력 소모 방지
2026-03-25 16:47:48 +09:00
jhChun 7b8103a530 디버그 로그 통일(UART -> RTT)
- app_raw.c: printf() -> DBG_PRINTF(RTT)
- 전력 소모 방지: CPU 문자열 포맷팅 실행 제거
2026-03-25 16:07:28 +09:00
jhChun a805c0ab78 BLE Slave Latency 적용
- main.c: SLAVE_LATENCY 0 -> 4
- 1초 기준 연결 간격 75ms 마다 폴링이 오는데 데이터가 없는 경우 4번 건너뛰어 Radio On 횟수 줄임
- 전력 소모 방지: BLE 연결 중 Radio 절전
2026-03-25 15:53:56 +09:00
jhChun 0c44f32724 내부 전압 변환 방식 변경 LDO -> DC-DC
- sdk_config.h: NRFX_POWER_CONFIG_DEFAULT_DCDCEN 1
- sdk_config.h: POWER_CONFIG_DEFAULT_DCDCEN 1
- 전력 소모 방지
2026-03-25 15:46:22 +09:00
jhChun 5d507a05ac Piezo 측정 평균화를 위한 반복 횟수 기본값 3회로 변경
- fstorage.c: 5 -> 3
2026-03-25 15:44:19 +09:00
jhChun e1b4793d57 전원 켜질 때 Piezo TX/RX Active 제거
- main.c: main문의 Piezo 드라이버 초기화 + 전원 -> 초기화만 진행, 측정 시에만 Piezo TX/RX Active
- 전력 소모 방지
2026-03-25 14:26:12 +09:00
jhChun e29196c770 UART 비활성화
- main.c: main문의 uart_init(); 주석 처리
- 전력 소모 방지(약 1mA)
2026-03-25 11:25:55 +09:00
jhChun f3d6dd290d FDS write 콜백 무한 대기 조건 변경
- fstorage.c: FDS write 콜백 무한 대기 -> 3초 타임아웃
2026-03-24 18:25:50 +09:00
jhChun 1aa6585725 BLE 연결이 끊어지는 경우 비동기 측정 상태로 인한 먹통 현상 방지
- main.c: BLE disconnected -> maa_async_abort() 함수 호출
- dr_adc121s051c: maa_async_abort() 함수에서 상태를 IDLE로 초기화 및 정리
2026-03-24 18:18:52 +09:00
jhChun 1f97bee322 온도 센서 콜백 대기 조건 변경
- parser.c: 콜백 완료 플래그 무한대기 -> 최대 100ms 대기, 콜백 오면 즉시 탈출
2026-03-24 17:59:58 +09:00
jhChun 35e09eee33 온도 센서 콜백 완료 플래그 추가
- tmp235_q1.c: SAADC 완료 플래그(tmp235_saadc_done) 추가
- parser.c: all_sensors()에서 고정 1ms 대기 -> 콜백 완료 플래그 대기로 변경
2026-03-24 17:26:36 +09:00
jhChun be3ebc9a63 apply latest local changes 2026-03-23 16:22:25 +09:00
jhChun 35d6956de2 타이밍 최적화
- 온도 측정, Piezo 측정 사이 딜레이 줄이기
- Piezo 한 채널에서 평균화를 위한 반복 측정 사이 딜레이 줄이기

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 10:43:59 +09:00
jhChun 75ee2187d6 Bit-bang SPI(8MHz)에서 HW SPI(SPIM3, 16MHz)로 변경
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 18:04:04 +09:00
jhChun aa3c040ae0 - FDS 피에조 파라미터 구조체 변경 (pd_adc_cnt/pd_delay_us 삭제, piezo 5개 필드 추가)
- maa/mbb 앱 파라미터 수신 → FDS 저장 기능 추가
- magic_number 변경 (0x20260319), cycles 범위 3~7로 제한

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 18:20:52 +09:00
jhChun 3ecd81c252 - Piezo 6ch 측정 + 센서(배터리, IMU, 온도) 측정: mbb 명령어 추가
- Flash Memory Piezo 측정 파라미터 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 13:54:06 +09:00
jhChun 96a46a26dd maa samples 140->100 변경, mec/maa 수신 시 Piezo 자동 Active/Sleep
- MAA_NUM_SAMPLES 140에서 100으로 변경 (DR_ADC_ECHO_SAMPLES_MAX 제한)
- 단일(mec) 및 모든 채널(maa) 에코 캡처 명령 수신 시 Piezo 자동 Active, 응답 송신 후 Sleep
- Cmd_mpa 중복 호출 정리 (dr_piezo_system_init 내부에 power_on 포함)
- 채널별 디버그 로그 추가
- 코드 리뷰 주석 정리 및 기타 파일 업데이트

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:03:04 +09:00
jhChun 39b4ffe66a 레거시 cmd_parse.c 제거 및 dr_cmd_parser 직접 호출 구조로 전환
- main.c에서 received_command_process() 대신 dr_cmd_parser() 직접 호출
- cmd_parse.c 전역변수/함수(SERIAL_NO, HW_NO, param_error 등) main.c로 이동
- g_plat 초기화를 main.c 초기화 구간으로 이동, log를 RTT 출력으로 연결
- 미사용 명령 삭제: mta, mtr, mst, mxz, myz, mpn, mdc
- cmd_parse.c/h 삭제, 참조하던 5개 파일 include 정리

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 15:45:33 +09:00
jh.chun e0a504b969 cmd_parse.c에서 parser.c로 통합 준비: spz? / sqz? / sxz? / syz? -> mpz? / mqz? / mxz? / myz?
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 17:58:17 +09:00
jh.chun 831dbc2844 fix: BLE TX 먹통 해결 및 메모리 안전성 개선
- binary_tx_handler를 dr_binary_tx_safe로 전체 교체 (APP_ERROR_CHECK 제거)
- data_tx_handler APP_ERROR_CHECK → DBG_PRINTF 교체
- memset/memcpy 하드코딩 크기를 define 상수로 교체 (버퍼 오버런 수정)
- SERIAL_NO_LENGTH, HW_NO_LENGTH, PASSKEY_LENGTH를 main.h로 통합
- 미사용 HW 드라이버/EEPROM 코드 삭제, TWI를 i2c_manager.c로 통합
- EEPROM → FDS 전환, 코드 리뷰 현황 문서 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 16:39:26 +09:00
jh.chun a77919b4d3 IMU gyro/accel config fix and README piezo frequency table correction
- Add GYRO_CONFIG0/ACCEL_CONFIG0 register setup in imu_read_direct() to fix gyro always returning -32768
- Increase sensor startup delay from 2ms to 80ms for reliable first read
- Put IMU back to sleep after msp? read to save power
- Fix piezo frequency table: swap freq 0/1 values, remove unused freq 5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 17:27:17 +09:00
jh.chun 590922638c Add new parser commands and FDS reliability fix
- HW Number Read/Write (mwh?, mrh?)
- Serial Number Read/Write (mws?, mrs?)
- FW Version Read (mfv?)
- Piezo TX/RX Deactivate (mpb?)
- Fix config_save() to wait for previous FDS operation instead of skipping
- Disable legacy s-prefix commands (ssz, srz, siz, shz, ssv) in cmd_parse.c

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:05:58 +09:00
Charles Kwon b3adfd42e6 VesiScan BASIC origin: Piezo + IMU firmware initial code
- nRF52840 + SoftDevice S140 BLE firmware
- Piezo ultrasound TX driver (2MHz, 8ch MUX)
- ICM42670P IMU 6-axis driver
- Echo AFE chain (ADA2200 + ADC121S051)
- BLE NUS command parser (mpa/mpc/mdc/mec/maa/msp)
- FDS flash config storage
- pc_firm parser and ADC driver included

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:40:20 +09:00
Charles Kwon a8ba31871e Initial commit: MT firmware project
- BLE peripheral applications
- dr_piezo and bladder_patch projects

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 17:26:39 +09:00
113 changed files with 22486 additions and 22360 deletions
@@ -216,9 +216,9 @@ uint32_t ble_dfu_buttonless_char_add(ble_dfu_buttonless_t * p_dfu)
add_char_params.is_var_len = true;
add_char_params.max_len = BLE_GATT_ATT_MTU_DEFAULT;
add_char_params.cccd_write_access = SEC_MITM;
add_char_params.write_access = SEC_MITM;
add_char_params.read_access = SEC_MITM;
add_char_params.cccd_write_access = SEC_OPEN;
add_char_params.write_access = SEC_OPEN;
add_char_params.read_access = SEC_OPEN;
return characteristic_add(p_dfu->service_handle, &add_char_params, &p_dfu->control_point_char);
}
@@ -283,8 +283,8 @@ uint32_t ble_nus_init(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)
add_char_params.char_props.write = 1;
add_char_params.char_props.write_wo_resp = 1;
add_char_params.read_access = SEC_MITM;
add_char_params.write_access = SEC_MITM;
add_char_params.read_access = SEC_OPEN;
add_char_params.write_access = SEC_OPEN;
err_code = characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->rx_handles);
if (err_code != NRF_SUCCESS)
@@ -302,9 +302,9 @@ uint32_t ble_nus_init(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)
add_char_params.is_var_len = true;
add_char_params.char_props.notify = 1;
add_char_params.read_access = SEC_MITM;
add_char_params.write_access = SEC_MITM;
add_char_params.cccd_write_access = SEC_MITM;
add_char_params.read_access = SEC_OPEN;
add_char_params.write_access = SEC_OPEN;
add_char_params.cccd_write_access = SEC_OPEN;
return characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->tx_handles);
/**@snippet [Adding proprietary characteristic to the SoftDevice] */
+33
View File
@@ -0,0 +1,33 @@
# Compiled binaries
*.o
*.hex
*.bin
*.elf
*.map
*.lst
# Build directories
_build/
build/
output/
# IDE/Editor
.vscode/
*.swp
*.swo
*~
# Segger/J-Link
*.jlink
# nRF specific
*.zip
sdk_config.h.bak
# Windows
Thumbs.db
desktop.ini
# Temporary files
*.tmp
*.bak
@@ -1,218 +1,175 @@
/**
* @file ble_quick_security.c
* @brief Ultra-simple BLE Security Configuration Implementation
*
* Compatible with existing debug_print.h system.
*/
#include "ble_quick_security.h"
#include "peer_manager_handler.h"
#include "nrf_ble_lesc.h"
#include "app_error.h"
#include <string.h>
// Use existing debug system
#include "debug_print.h"
// Module state
static struct {
bool dev_mode;
bool bonds_delete_pending;
} m_state = {0};
static const char * sec_error_name(pm_sec_error_code_t error)
{
switch (error) {
case PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING:
return "PIN_OR_KEY_MISSING";
case PM_CONN_SEC_ERROR_MIC_FAILURE:
return "MIC_FAILURE";
case PM_CONN_SEC_ERROR_DISCONNECT:
return "DISCONNECT";
case PM_CONN_SEC_ERROR_SMP_TIMEOUT:
return "SMP_TIMEOUT";
default:
return "UNKNOWN";
}
}
/**
* @brief Initialize BLE security
*/
void ble_security_quick_init(bool development_mode)
{
ret_code_t err_code;
ble_gap_sec_params_t sec_params;
// Save mode
m_state.dev_mode = development_mode;
m_state.bonds_delete_pending = false;
// Initialize Peer Manager FIRST
err_code = pm_init();
APP_ERROR_CHECK(err_code);
// Initialize LESC module (ECDH P-256 key pair generation)
err_code = nrf_ble_lesc_init();
APP_ERROR_CHECK(err_code);
// Configure security parameters
memset(&sec_params, 0, sizeof(ble_gap_sec_params_t));
if (development_mode) {
// ===== DEVELOPMENT MODE: No security =====
sec_params.bond = 0; // No bonding
sec_params.mitm = 0; // No MITM
sec_params.lesc = 0; // No LESC
sec_params.keypress = 0;
sec_params.io_caps = BLE_GAP_IO_CAPS_NONE; // No passkey
sec_params.oob = 0;
sec_params.min_key_size = 7;
sec_params.max_key_size = 16;
DBG_PRINTF("DEV MODE: Security DISABLED - Fast connection\r\n");
// Delete all bonds (async)
err_code = pm_peers_delete();
if (err_code == NRF_SUCCESS) {
m_state.bonds_delete_pending = true;
DBG_PRINTF("DEV MODE: Deleting all bonds...\r\n");
}
} else {
// ===== PRODUCTION MODE: Full security =====
sec_params.bond = 1; // Enable bonding
sec_params.mitm = 1; // Enable MITM
sec_params.lesc = 1; // LE Secure Connections (ECDH P-256)
sec_params.keypress = 0;
sec_params.io_caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY; // Show passkey
sec_params.oob = 0;
sec_params.min_key_size = 7;
sec_params.max_key_size = 16;
sec_params.kdist_own.enc = 1;
sec_params.kdist_own.id = 1;
sec_params.kdist_peer.enc = 1;
sec_params.kdist_peer.id = 1;
DBG_PRINTF("PROD MODE: Security ENABLED - Full protection\r\n");
}
// Apply security parameters
err_code = pm_sec_params_set(&sec_params);
APP_ERROR_CHECK(err_code);
}
/**
* @brief Get current mode
*/
bool ble_security_is_dev_mode(void)
{
return m_state.dev_mode;
}
/**
* @brief PM event handler
*/
void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
{
ret_code_t err_code;
// DEV mode: do not forward security failure events to SDK handler (prevent disconnect)
if (m_state.dev_mode && p_evt->evt_id == PM_EVT_CONN_SEC_FAILED) {
DBG_PRINTF("Security failed: error=%d (%s)\r\n",
p_evt->params.conn_sec_failed.error,
sec_error_name(p_evt->params.conn_sec_failed.error));
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
return;
}
// Call standard handlers (required)
pm_handler_on_pm_evt(p_evt);
pm_handler_flash_clean(p_evt);
// Handle events
switch (p_evt->evt_id) {
case PM_EVT_CONN_SEC_SUCCEEDED:
if (m_state.dev_mode) {
DBG_PRINTF("DEV: Connected (no security)\r\n");
} else {
pm_conn_sec_status_t status;
if (pm_conn_sec_status_get(p_evt->conn_handle, &status) == NRF_SUCCESS) {
DBG_PRINTF("PROD: Link secured - LESC=%d MITM=%d bonded=%d\r\n",
status.lesc, status.mitm_protected, status.bonded);
} else {
DBG_PRINTF("PROD: Link secured (bonded)\r\n");
}
}
break;
case PM_EVT_CONN_SEC_FAILED:
DBG_PRINTF("Security failed: error=%d (%s)\r\n",
p_evt->params.conn_sec_failed.error,
sec_error_name(p_evt->params.conn_sec_failed.error));
if (m_state.dev_mode) {
// DEV mode: ignore security failure, keep connection
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
break;
}
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_DISCONNECT) {
// The peer/link already disconnected before security finished.
// There is no live connection to repair; BLE_GAP_EVT_DISCONNECTED
// will restart advertising.
DBG_PRINTF("Security ended by disconnect; waiting for reconnect\r\n");
break;
}
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_SMP_TIMEOUT) {
// The SDK cannot start another SMP procedure on this link.
pm_handler_disconnect_on_sec_failure(p_evt);
break;
}
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) {
// Key missing: attempt re-pairing, fall back to disconnect on failure
err_code = pm_conn_secure(p_evt->conn_handle, true);
if (err_code != NRF_ERROR_INVALID_STATE &&
err_code != NRF_ERROR_BUSY &&
err_code != BLE_ERROR_INVALID_CONN_HANDLE) {
APP_ERROR_CHECK(err_code);
}
if (err_code != NRF_SUCCESS) {
// Re-pairing not possible -> disconnect
pm_handler_disconnect_on_sec_failure(p_evt);
}
} else {
// Other security failure -> delete bond then attempt re-pairing
pm_peer_id_t peer_id;
if (pm_peer_id_get(p_evt->conn_handle, &peer_id) == NRF_SUCCESS
&& peer_id != PM_PEER_ID_INVALID) {
pm_peer_delete(peer_id);
}
err_code = pm_conn_secure(p_evt->conn_handle, true);
if (err_code != NRF_SUCCESS) {
pm_handler_disconnect_on_sec_failure(p_evt);
}
}
break;
case PM_EVT_CONN_SEC_CONFIG_REQ:
{
pm_conn_sec_config_t config = {
.allow_repairing = true
};
pm_conn_sec_config_reply(p_evt->conn_handle, &config);
}
break;
case PM_EVT_PEERS_DELETE_SUCCEEDED:
if (m_state.bonds_delete_pending) {
m_state.bonds_delete_pending = false;
DBG_PRINTF("DEV MODE: Bonds cleared!\r\n");
}
break;
default:
break;
}
}
/**
* @file ble_quick_security.c
* @brief Ultra-simple BLE Security Configuration Implementation
*
* Compatible with existing debug_print.h system.
*/
#include "ble_quick_security.h"
#include "peer_manager_handler.h"
#include "app_error.h"
#include <string.h>
// Use existing debug system
#include "debug_print.h"
// Module state
static struct {
bool dev_mode;
bool bonds_delete_pending;
} m_state = {0};
/**
* @brief Initialize BLE security
*/
void ble_security_quick_init(bool development_mode)
{
ret_code_t err_code;
ble_gap_sec_params_t sec_params;
// Save mode
m_state.dev_mode = development_mode;
m_state.bonds_delete_pending = false;
// Initialize Peer Manager FIRST
err_code = pm_init();
APP_ERROR_CHECK(err_code);
// Configure security parameters
memset(&sec_params, 0, sizeof(ble_gap_sec_params_t));
if (development_mode) {
// ===== DEVELOPMENT MODE: No security =====
sec_params.bond = 0; // No bonding
sec_params.mitm = 0; // No MITM
sec_params.lesc = 0; // No LESC
sec_params.keypress = 0;
sec_params.io_caps = BLE_GAP_IO_CAPS_NONE; // No passkey
sec_params.oob = 0;
sec_params.min_key_size = 7;
sec_params.max_key_size = 16;
DBG_PRINTF("DEV MODE: Security DISABLED - Fast connection\r\n");
// Delete all bonds (async)
err_code = pm_peers_delete();
if (err_code == NRF_SUCCESS) {
m_state.bonds_delete_pending = true;
DBG_PRINTF("DEV MODE: Deleting all bonds...\r\n");
}
} else {
// ===== PRODUCTION MODE: Full security =====
sec_params.bond = 1; // Enable bonding
sec_params.mitm = 1; // Enable MITM
sec_params.lesc = 0; // Standard pairing
sec_params.keypress = 0;
sec_params.io_caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY; // Show passkey
sec_params.oob = 0;
sec_params.min_key_size = 7;
sec_params.max_key_size = 16;
sec_params.kdist_own.enc = 1;
sec_params.kdist_own.id = 1;
sec_params.kdist_peer.enc = 1;
sec_params.kdist_peer.id = 1;
DBG_PRINTF("PROD MODE: Security ENABLED - Full protection\r\n");
}
// Apply security parameters
err_code = pm_sec_params_set(&sec_params);
APP_ERROR_CHECK(err_code);
}
/**
* @brief Get current mode
*/
bool ble_security_is_dev_mode(void)
{
return m_state.dev_mode;
}
/**
* @brief PM event handler
*/
void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
{
ret_code_t err_code;
// DEV 모드: 보안 실패 이벤트는 SDK 핸들러에 전달하지 않음 (disconnect 방지)
if (m_state.dev_mode && p_evt->evt_id == PM_EVT_CONN_SEC_FAILED) {
DBG_PRINTF("Security failed: error=%d\r\n",
p_evt->params.conn_sec_failed.error);
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
return;
}
// Call standard handlers (required)
pm_handler_on_pm_evt(p_evt);
pm_handler_flash_clean(p_evt);
// Handle events
switch (p_evt->evt_id) {
case PM_EVT_CONN_SEC_SUCCEEDED:
if (m_state.dev_mode) {
DBG_PRINTF("DEV: Connected (no security)\r\n");
} else {
DBG_PRINTF("PROD: Link secured (bonded)\r\n");
}
break;
case PM_EVT_CONN_SEC_FAILED:
DBG_PRINTF("Security failed: error=%d\r\n",
p_evt->params.conn_sec_failed.error);
if (m_state.dev_mode) {
// DEV 모드: 보안 실패 무시 — 연결 유지
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
break;
}
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) {
// Key missing: 재페어링 시도, 실패 시 disconnect로 폴백
err_code = pm_conn_secure(p_evt->conn_handle, true);
if (err_code != NRF_ERROR_INVALID_STATE &&
err_code != NRF_ERROR_BUSY &&
err_code != BLE_ERROR_INVALID_CONN_HANDLE) {
APP_ERROR_CHECK(err_code);
}
if (err_code != NRF_SUCCESS) {
// 재페어링 불가 → disconnect
pm_handler_disconnect_on_sec_failure(p_evt);
}
} else {
// 기타 보안 실패 → bond 삭제 후 재페어링 시도
pm_peer_id_t peer_id;
if (pm_peer_id_get(p_evt->conn_handle, &peer_id) == NRF_SUCCESS
&& peer_id != PM_PEER_ID_INVALID) {
pm_peer_delete(peer_id);
}
err_code = pm_conn_secure(p_evt->conn_handle, true);
if (err_code != NRF_SUCCESS) {
pm_handler_disconnect_on_sec_failure(p_evt);
}
}
break;
case PM_EVT_CONN_SEC_CONFIG_REQ:
{
pm_conn_sec_config_t config = {
.allow_repairing = true
};
pm_conn_sec_config_reply(p_evt->conn_handle, &config);
}
break;
case PM_EVT_PEERS_DELETE_SUCCEEDED:
if (m_state.bonds_delete_pending) {
m_state.bonds_delete_pending = false;
DBG_PRINTF("DEV MODE: Bonds cleared!\r\n");
}
break;
default:
break;
}
}
@@ -1,56 +1,56 @@
/**
* @file ble_quick_security.h
* @brief Ultra-simple BLE Security Configuration
*
* ONE function call to control entire security behavior.
* Works with existing debug_print.h system.
*/
#ifndef BLE_QUICK_SECURITY_H
#define BLE_QUICK_SECURITY_H
#include <stdint.h>
#include <stdbool.h>
#include "peer_manager.h"
/**
* @brief Initialize BLE security with ONE simple parameter
*
* @param[in] development_mode true (1) = Fast development (no security)
* false (0) = Production (full security)
*
* Development mode (1):
* - No pairing/bonding required
* - Auto-deletes all bonds on startup
* - Instant connection
* - Fast iteration
*
* Production mode (0):
* - Full security with passkey
* - Bonding preserved
* - MITM protection
* - Secure deployment
*
* Usage in main.c:
* #define BLE_DEV_MODE 1 // or 0
* ble_security_quick_init(BLE_DEV_MODE);
*/
void ble_security_quick_init(bool development_mode);
/**
* @brief Get current mode
* @return true if in development mode, false if production
*/
bool ble_security_is_dev_mode(void);
/**
* @brief Peer Manager event handler
*
* Call this from your pm_evt_handler() function.
* It handles all security events automatically.
*
* @param[in] p_evt Peer Manager event
*/
void ble_security_quick_pm_handler(pm_evt_t const *p_evt);
#endif // BLE_QUICK_SECURITY_H
/**
* @file ble_quick_security.h
* @brief Ultra-simple BLE Security Configuration
*
* ONE function call to control entire security behavior.
* Works with existing debug_print.h system.
*/
#ifndef BLE_QUICK_SECURITY_H
#define BLE_QUICK_SECURITY_H
#include <stdint.h>
#include <stdbool.h>
#include "peer_manager.h"
/**
* @brief Initialize BLE security with ONE simple parameter
*
* @param[in] development_mode true (1) = Fast development (no security)
* false (0) = Production (full security)
*
* Development mode (1):
* - No pairing/bonding required
* - Auto-deletes all bonds on startup
* - Instant connection
* - Fast iteration
*
* Production mode (0):
* - Full security with passkey
* - Bonding preserved
* - MITM protection
* - Secure deployment
*
* Usage in main.c:
* #define BLE_DEV_MODE 1 // or 0
* ble_security_quick_init(BLE_DEV_MODE);
*/
void ble_security_quick_init(bool development_mode);
/**
* @brief Get current mode
* @return true if in development mode, false if production
*/
bool ble_security_is_dev_mode(void);
/**
* @brief Peer Manager event handler
*
* Call this from your pm_evt_handler() function.
* It handles all security events automatically.
*
* @param[in] p_evt Peer Manager event
*/
void ble_security_quick_pm_handler(pm_evt_t const *p_evt);
#endif // BLE_QUICK_SECURITY_H
@@ -1,87 +1,79 @@
/*==============================================================================
* dr_util.c - BLE response formatting helpers
*
* Convenience wrappers used by command handlers to assemble and transmit
* short BLE responses. Each helper prepends a 4-char TAG and writes the
* supplied uint16 values in Big-Endian order.
*============================================================================*/
#include "dr_util.h"
#include "parser.h"
extern void single_format_data(uint8_t *buffer, const char *tag, uint16_t value);
extern void format_data(uint8_t *buffer, const char *tag, uint16_t *data, uint8_t length);
extern uint8_t ble_bin_buffer[];
extern void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length);
void dr_ble_return_1(const char *tag, uint16_t value)
{
single_format_data(ble_bin_buffer, tag, value);
dr_binary_tx_safe(ble_bin_buffer, 3); /* Use safe TX with retry */
}
void dr_ble_return_2(const char *tag, uint16_t v1, uint16_t v2)
{
ble_bin_buffer[0] = tag[0];
ble_bin_buffer[1] = tag[1];
ble_bin_buffer[2] = tag[2];
ble_bin_buffer[3] = tag[3];
ble_bin_buffer[4] = (uint8_t)(v1 >> 8);
ble_bin_buffer[5] = (uint8_t)(v1 & 0xFF);
ble_bin_buffer[6] = (uint8_t)(v2 >> 8);
ble_bin_buffer[7] = (uint8_t)(v2 & 0xFF);
dr_binary_tx_safe(ble_bin_buffer, 4); /* Use safe TX with retry */
}
void dr_ble_return_3(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3)
{
ble_bin_buffer[0] = tag[0];
ble_bin_buffer[1] = tag[1];
ble_bin_buffer[2] = tag[2];
ble_bin_buffer[3] = tag[3];
ble_bin_buffer[4] = (uint8_t)(v1 >> 8);
ble_bin_buffer[5] = (uint8_t)(v1 & 0xFF);
ble_bin_buffer[6] = (uint8_t)(v2 >> 8);
ble_bin_buffer[7] = (uint8_t)(v2 & 0xFF);
ble_bin_buffer[8] = (uint8_t)(v3 >> 8);
ble_bin_buffer[9] = (uint8_t)(v3 & 0xFF);
dr_binary_tx_safe(ble_bin_buffer, 5); /* Use safe TX with retry */
}
void dr_ble_debug(uint16_t point_id, uint16_t value)
{
/* Use dedicated buffer to avoid conflicts with ble_bin_buffer */
static uint8_t dbg_buffer[8] = {0};
dbg_buffer[0] = 'd';
dbg_buffer[1] = 'b';
dbg_buffer[2] = 'g';
dbg_buffer[3] = ':';
dbg_buffer[4] = (uint8_t)(point_id >> 8);
dbg_buffer[5] = (uint8_t)(point_id & 0xFF);
dbg_buffer[6] = (uint8_t)(value >> 8);
dbg_buffer[7] = (uint8_t)(value & 0xFF);
dr_binary_tx_safe(dbg_buffer, 4);
}
void dr_ble_return_piezo_1(const char *tag, uint16_t value)
{
/* Use dedicated buffer for piezo responses to avoid conflicts with ble_bin_buffer */
static uint8_t piezo_buffer[8] = {0};
piezo_buffer[0] = tag[0];
piezo_buffer[1] = tag[1];
piezo_buffer[2] = tag[2];
piezo_buffer[3] = tag[3];
piezo_buffer[4] = (uint8_t)(value >> 8);
piezo_buffer[5] = (uint8_t)(value & 0xFF);
dr_binary_tx_safe(piezo_buffer, 3); /* 6 bytes = 3 words */
}
#include "dr_util.h"
#include "parser.h"
extern void single_format_data(uint8_t *buffer, const char *tag, uint16_t value);
extern void format_data(uint8_t *buffer, const char *tag, uint16_t *data, uint8_t length);
extern uint8_t ble_bin_buffer[];
extern void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length);
void dr_ble_return_1(const char *tag, uint16_t value)
{
single_format_data(ble_bin_buffer, tag, value);
dr_binary_tx_safe(ble_bin_buffer, 3); /* Use safe TX with retry */
}
void dr_ble_return_2(const char *tag, uint16_t v1, uint16_t v2)
{
ble_bin_buffer[0] = tag[0];
ble_bin_buffer[1] = tag[1];
ble_bin_buffer[2] = tag[2];
ble_bin_buffer[3] = tag[3];
ble_bin_buffer[4] = (uint8_t)(v1 >> 8);
ble_bin_buffer[5] = (uint8_t)(v1 & 0xFF);
ble_bin_buffer[6] = (uint8_t)(v2 >> 8);
ble_bin_buffer[7] = (uint8_t)(v2 & 0xFF);
dr_binary_tx_safe(ble_bin_buffer, 4); /* Use safe TX with retry */
}
void dr_ble_return_3(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3)
{
ble_bin_buffer[0] = tag[0];
ble_bin_buffer[1] = tag[1];
ble_bin_buffer[2] = tag[2];
ble_bin_buffer[3] = tag[3];
ble_bin_buffer[4] = (uint8_t)(v1 >> 8);
ble_bin_buffer[5] = (uint8_t)(v1 & 0xFF);
ble_bin_buffer[6] = (uint8_t)(v2 >> 8);
ble_bin_buffer[7] = (uint8_t)(v2 & 0xFF);
ble_bin_buffer[8] = (uint8_t)(v3 >> 8);
ble_bin_buffer[9] = (uint8_t)(v3 & 0xFF);
dr_binary_tx_safe(ble_bin_buffer, 5); /* Use safe TX with retry */
}
void dr_ble_debug(uint16_t point_id, uint16_t value)
{
/* Use dedicated buffer to avoid conflicts with ble_bin_buffer */
static uint8_t dbg_buffer[8];
dbg_buffer[0] = 'd';
dbg_buffer[1] = 'b';
dbg_buffer[2] = 'g';
dbg_buffer[3] = ':';
dbg_buffer[4] = (uint8_t)(point_id >> 8);
dbg_buffer[5] = (uint8_t)(point_id & 0xFF);
dbg_buffer[6] = (uint8_t)(value >> 8);
dbg_buffer[7] = (uint8_t)(value & 0xFF);
dr_binary_tx_safe(dbg_buffer, 4);
}
void dr_ble_return_piezo_1(const char *tag, uint16_t value)
{
/* Use dedicated buffer for piezo responses to avoid conflicts with ble_bin_buffer */
static uint8_t piezo_buffer[8];
piezo_buffer[0] = tag[0];
piezo_buffer[1] = tag[1];
piezo_buffer[2] = tag[2];
piezo_buffer[3] = tag[3];
piezo_buffer[4] = (uint8_t)(value >> 8);
piezo_buffer[5] = (uint8_t)(value & 0xFF);
dr_binary_tx_safe(piezo_buffer, 3); /* 6 bytes = 3 words */
}
@@ -1,20 +1,18 @@
/*==============================================================================
* dr_util.h - BLE response helper API
*============================================================================*/
#ifndef DR_UTIL_H
#define DR_UTIL_H
#include <stdint.h>
void dr_ble_return_1(const char *tag, uint16_t value);
void dr_ble_return_2(const char *tag, uint16_t v1, uint16_t v2);
void dr_ble_return_3(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3);
void dr_ble_return_3_be(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3);
/* Piezo dedicated BLE return - uses a separate buffer to avoid conflicts. */
void dr_ble_return_piezo_1(const char *tag, uint16_t value);
/* BLE debug output - sends "dbg:" + point_id + value. */
void dr_ble_debug(uint16_t point_id, uint16_t value);
#endif /* DR_UTIL_H */
#ifndef DR_UTIL_H
#define DR_UTIL_H
#include <stdint.h>
void dr_ble_return_1(const char *tag, uint16_t value);
void dr_ble_return_2(const char *tag, uint16_t v1, uint16_t v2);
void dr_ble_return_3(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3);
void dr_ble_return_3_be(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3);
/* Piezo dedicated BLE return - uses separate buffer to avoid conflicts */
void dr_ble_return_piezo_1(const char *tag, uint16_t value);
/* BLE debug output - sends "dbg:" + point_id + value */
void dr_ble_debug(uint16_t point_id, uint16_t value);
#endif /* DR_UTIL_H */
+1370
View File
File diff suppressed because it is too large Load Diff
+24
View File
@@ -0,0 +1,24 @@
/* parser.h */
#ifndef PARSER_H
#define PARSER_H
#include <stdint.h>
#include <stdbool.h>
/* Platform-dependent function pointer set */
typedef struct {
void (*log)(const char *fmt, ...);
void (*tx_bin)(const uint8_t *buf, uint16_t len);
bool crc_check;
} dr_platform_if_t;
/* Global interface & log flag */
extern dr_platform_if_t g_plat;
extern bool g_log_enable;
/* Main parser entry point */
int dr_cmd_parser(const uint8_t *buf, uint8_t len);
#endif /* PARSER_H */
@@ -0,0 +1,11 @@
{
"folders": [
{
"path": "../../.."
},
{
"path": "../../../../pc_firm"
}
],
"settings": {}
}
@@ -0,0 +1,297 @@
/*******************************************************************************
* @file battery_saadc.c
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
******************************************************************************/
/*******************************************************************************
* [모듈 개요] 배터리 전압 ADC 측정 모듈
*
* nRF52840의 SAADC(Successive Approximation ADC)를 사용하여 배터리 전압을 측정:
* - AIN2 채널, 10bit 해상도
* - 5초 주기 타이머(battery_loop)로 반복 측정
* - 저전압(3500mV 이하) 10회 연속 감지 시 자동 전원 OFF
* - info4 모드(전체 센서 수집)에서는 info_batt에 저장 후 온도 측정으로 전환
*
* 배터리 전압 변환 공식:
* 전압(mV) = ADC값 x (600mV / 1023) x 6 x 1.42 (분압 저항 보정 계수)
*
* info4 모드 순서: 배터리 -> 온도(go_temp) -> IMU(motion_raw_data_enabled)
******************************************************************************/
#include "sdk_common.h"
#include <stdint.h>
#include <string.h>
#include "nrf.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_timer.h"
#include "ble_nus.h"
#include "nrf_log.h"
#include "main.h"
#include "app_timer.h"
//#include "fstorage.h"
#include "battery_saadc.h"
#include "main_timer.h"
#include "main.h"
#include "debug_print.h"
/* SAADC 내부 기준전압 600mV (부동소수점) */
#define BATTERY_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
/* 1/3 프리스케일링 보상 계수 (입력 전압을 1/3로 분압하므로 x3, 추가 x2 = 총 x6) (부동소수점) */
#define BATTERY_PRE_SCALING_COMPENSATION 6.0f /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
/* 12비트 ADC 최대 디지털 값 (부동소수점) */
#define BATTERY_ADC_RES_12BITS 4095.0f /**< Maximum digital value for 12-bit ADC conversion. */
/**@brief Macro to convert the result of ADC conversion in millivolts.
*
* @param[in] ADC_VALUE ADC result.
*
* @retval Result converted to millivolts.
*/
#define BATTERY_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
((((ADC_VALUE) * BATTERY_REF_VOLTAGE_IN_MILLIVOLTS) / BATTERY_ADC_RES_12BITS) * BATTERY_PRE_SCALING_COMPENSATION)
/* 배터리 측정용 싱글 버퍼 (1회 측정 후 uninit하므로 더블 버퍼 불필요) */
static nrf_saadc_value_t adc_buf;
//static nrf_saadc_value_t adc_bufs[2]; // 이전: 더블 버퍼 (연속 측정용)
/* 배터리 모니터링 반복 타이머 정의 */
APP_TIMER_DEF(m_battery_loop_timer_id);
/* 배터리 측정 주기: 5초 (밀리초 단위) */
#define BATTERY_LOOP_INTERVAL 60000
/* 저전압 체크 플래그 — battery_loop에서 true로 설정, 핸들러에서 소비 */
bool low_battery_check = false;
/* SAADC 콜백 완료 플래그 — all_sensors()에서 배터리 측정 완료 대기용 */
volatile bool battery_saadc_done = false;
/* info4: 전체 센서 데이터 수집 모드 플래그 */
extern bool info4; // main.c
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
/* true가 되면 main_timer에서 전원 OFF 시퀀스 실행 */
extern bool go_device_power_off;
/* 다른 작업(IMU 등) 처리 중이면 true — 배터리 측정 스킵용 */
extern volatile bool processing;
/* 현재 명령 소스: CMD_UART 또는 CMD_BLE */
extern which_cmd_t cmd_type_t;
extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] ;
/* info4 모드에서 배터리 전압을 임시 저장 (mV 단위) */
volatile uint16_t info_batt; //48_c
/* info4 순차 측정 제어 플래그: go_batt→ go_temp → motion */
extern bool go_temp; //
extern bool go_batt; //cmd_parse
extern bool motion_raw_data_enabled ;
extern bool ble_got_new_data;
extern bool motion_data_once ;
/**@brief Function for handling the ADC interrupt.
*
* @details This function will fetch the conversion result from the ADC, convert the value into
* percentage and send it to peer.
*/
/**
* @brief 배터리 전압 ADC 완료 콜백
*
* SAADC 변환 완료 시 호출된다.
* ADC 값을 실제 배터리 전압(mV)으로 변환하고, 동작 모드에 따라:
* - 저전압 체크 모드: 3500mV 이하 10회 연속이면 자동 전원 OFF
* - info4 모드: info_batt에 저장 후 온도 측정(go_temp)으로 전환
* - 일반 모드: BLE 또는 UART로 즉시 전송
*
* 전압 변환: ADC값 x (600/1023) x 6 = 기본 전압, x 1.42 = 분압 보정 후 실제 전압
*/
void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
{
/* 저전압 연속 감지 카운터 (static으로 호출 간 유지) */
static uint8_t low_battery_cnt = 0;
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
nrf_saadc_value_t register_val = 0;
float batt_lvl_in_milli_volt_0 = 0; /* 보정 전 전압 (부동소수점) */
float batt_lvl_in_milli_volt_1 = 0; /* 분압 보정 후 최종 전압 (부동소수점) */
/* ADC 변환 결과 읽기 */
register_val = p_event->data.done.p_buffer[0];
//err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, 1); // 이전: 다음 변환을 위해 버퍼 재등록
//APP_ERROR_CHECK(err_code);
/* SAADC 해제 — 다른 ADC 측정(온도, 압력)과 하드웨어 공유 */
/* 1회 측정 후 해제이므로 buffer_convert(다음 버퍼 등록) 불필요 */
nrf_drv_saadc_channel_uninit(0);
nrf_drv_saadc_uninit();
/* 콜백 완료 알림 (all_sensors 대기 해제용) */
if (info4) DBG_PRINTF("2");
battery_saadc_done = true;
/* ADC값 → mV 변환 (매크로: ADC x 600/1023 x 6) */
batt_lvl_in_milli_volt_0 = BATTERY_RESULT_IN_MILLI_VOLTS(register_val);
/* 분압 저항 보정 계수 1.42 적용 → 실제 배터리 전압 */
batt_lvl_in_milli_volt_1 = (batt_lvl_in_milli_volt_0) *1.42f;
/* === 저전압 체크 모드 (battery_loop 타이머에서 설정) === */
if(low_battery_check == true)
{
low_battery_check = false;
/* 배터리 전압이 LOW_BATTERY_VOLTAGE(3500mV) 이하인지 확인 */
if(batt_lvl_in_milli_volt_1 <= LOW_BATTERY_VOLTAGE)
{
/* 10회 연속 저전압 감지 시 전원 OFF 시퀀스 시작 */
if(low_battery_cnt >= 10)
{
low_battery_cnt = 0;
/*go to power off and fds save */
DBG_PRINTF("Save FDS parameters and then Power OFF\r\n");
go_device_power_off = true;
main_timer_start();
}
else
{
/* 아직 10회 미만 — 카운터 증가 후 경고 출력 */
low_battery_cnt++;
DBG_PRINTF("WARNING!!! low_battery cnt = %d, Batt = %d(mV)\r\n", low_battery_cnt, (int)batt_lvl_in_milli_volt_1);
}
}
}
/* === info4 모드: 전체 센서 수집(mbb) 중 배터리 값 저장 === */
else if (info4 == true)
{
info_batt = batt_lvl_in_milli_volt_1;
}
/* === 일반 모드: 단독 배터리 측정 요청(msn)에 대한 응답 전송 === */
else
{
if (cmd_type_t == CMD_UART)
{
DBG_PRINTF("Tn%d\r\n\r\n", (int)batt_lvl_in_milli_volt_1);
}
else if (cmd_type_t == CMD_BLE)
{
/* "rsn:" 헤더와 함께 배터리 전압을 바이너리로 BLE 전송 */
single_format_data(ble_bin_buffer, "rsn:", batt_lvl_in_milli_volt_1);
dr_binary_tx_safe(ble_bin_buffer,3);
//data_tx_handler(ble_tx_buffer);
}
}
}
}
/**
* @brief SAADC를 배터리 전압 측정용으로 설정
*
* AIN2 채널을 싱글엔드(SE) 모드, 1/3 프리스케일링으로 초기화한다.
* 더블 버퍼(adc_bufs[0], [1])를 등록하여 연속 측정이 가능하도록 한다.
* 콜백: battery_event_handler
*/
static void battery_configure(void)
{
/* SAADC 드라이버 초기화 (4x 오버샘플링으로 노이즈 저감) */
nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; // 10 -> 12bit
saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X;
ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, battery_event_handler);
if (err_code != NRF_SUCCESS) {
return; /* SAADC 사용 중 → 이번 측정 스킵, 다음 주기에 재시도 */
}
/* AIN2 채널 설정: 싱글엔드 입력, 1/6 gain, burst + TACQ 20μs */
nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
config.burst = NRF_SAADC_BURST_ENABLED;
config.acq_time = NRF_SAADC_ACQTIME_10US;
err_code = nrf_drv_saadc_channel_init(0, &config);
APP_ERROR_CHECK(err_code);
/* 싱글 버퍼 등록 (1회 측정 후 uninit) */
err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1);
APP_ERROR_CHECK(err_code);
//err_code = nrf_drv_saadc_buffer_convert(&adc_bufs[0], 1); // 이전: 더블 버퍼
//APP_ERROR_CHECK(err_code);
//err_code = nrf_drv_saadc_buffer_convert(&adc_bufs[1], 1);
//APP_ERROR_CHECK(err_code);
}
/**
* @brief 배터리 전압 1회 측정 시작
*
* SAADC를 배터리용으로 설정 후 샘플링을 트리거한다.
* 결과는 battery_event_handler 콜백에서 비동기로 처리된다.
*/
void battery_level_meas(void)
{
ret_code_t err_code;
battery_configure(); /* SAADC 배터리용 초기화 */
err_code = nrf_drv_saadc_sample(); /* ADC 샘플링 트리거 (비동기) */
APP_ERROR_CHECK(err_code);
}
/**
* @brief 배터리 모니터링 타이머 콜백 (5초 주기) - 주기 확인 필요(너무 짧음)
*
* 저전압 체크 플래그를 설정하고 배터리 측정을 시작한다.
* 다른 작업(IMU 등) 처리 중이면 측정을 건너뛴다.
*/
void battery_loop(void * p_context) /* For 1sec */
{
UNUSED_PARAMETER(p_context);
/* 다른 센서 처리 중 또는 MBB 센서 수집 중이면 배터리 측정 스킵 (SAADC 충돌 방지) */
if (processing == true || info4 == true)
{
processing = false ; // add 20241218
//low_battery_check = true;
return;
}
else
{
low_battery_check = true; /* 저전압 감지 모드로 측정 */
battery_level_meas(); /* 배터리 ADC 1회 측정 시작 */
}
}
/** @brief 배터리 모니터링 타이머 시작 (5초 반복) */
void battery_timer_start(void)
{
APP_ERROR_CHECK(app_timer_start(m_battery_loop_timer_id, APP_TIMER_TICKS(BATTERY_LOOP_INTERVAL), NULL));
}
/** @brief 배터리 모니터링 타이머 정지 */
void battery_timer_stop(void)
{
APP_ERROR_CHECK(app_timer_stop(m_battery_loop_timer_id));
}
/** @brief 배터리 모니터링 타이머 초기화 (반복 모드, 콜백: battery_loop) */
void battery_timer_init(void)
{
APP_ERROR_CHECK(app_timer_create(&m_battery_loop_timer_id, APP_TIMER_MODE_REPEATED, battery_loop));
}
@@ -0,0 +1,41 @@
/*******************************************************************************
* @file battery_saadc.h
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
******************************************************************************/
/*******************************************************************************
* [헤더 개요] 배터리 전압 SAADC 측정 인터페이스
*
* nRF52840 SAADC를 이용한 배터리 전압 측정 API를 선언한다.
*
* 주요 API:
* - battery_level_meas() : 배터리 전압 1회 측정 (AIN2)
* - battery_timer_init/start/stop() : 5초 주기 배터리 모니터링 타이머 제어
*
* LOW_BATTERY_VOLTAGE(3500mV) 이하가 10회 연속 감지되면 자동 전원 OFF
******************************************************************************/
#ifndef _BATTERY_SAADC_H_
#define _BATTERY_SAADC_H_
/* 저전압 판정 임계값 (mV) — 이 값 이하가 10회 연속이면 자동 전원 OFF */
#define LOW_BATTERY_VOLTAGE 3500 /* Low Battery 임계값 */
/** @brief 배터리 SAADC 콜백 완료 플래그 (all_sensors 대기용) */
extern volatile bool battery_saadc_done;
/** @brief 배터리 전압 1회 측정 시작 (비동기, 결과는 콜백에서 처리) */
void battery_level_meas(void);
/** @brief 배터리 모니터링 5초 반복 타이머 시작 */
void battery_timer_start(void);
/** @brief 배터리 모니터링 타이머 정지 */
void battery_timer_stop(void);
/** @brief 배터리 모니터링 타이머 초기화 (앱 시작 시 1회 호출) */
void battery_timer_init(void);
#endif //_BATTERY_SAADC_H_
@@ -1,93 +0,0 @@
/*==============================================================================
* cmd_common.h - Shared header for command handlers
*
* Bundles every include / extern declaration / macro used in common across
* the cmd_*.c modules. A handler module only needs this header plus its own
* area-specific driver headers.
*============================================================================*/
#ifndef CMD_COMMON_H
#define CMD_COMMON_H
#include "parser.h"
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "debug_print.h"
#include "dr_util.h"
#include "main.h"
#include "app_timer.h"
#include "fstorage.h" /* config_data_t */
/*------------------------------------------------------------------------------
* BLE transmission / formatting (defined in main.c)
*----------------------------------------------------------------------------*/
extern uint8_t ble_bin_buffer[];
extern void single_format_data(uint8_t *buffer, const char *tag, uint16_t value);
extern void ascii_format_data(uint8_t *buffer, const char *tag, const char *ascii, size_t length);
extern void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length);
extern void dr_binary_tx_safe(const uint8_t *buffer, uint16_t length); /* length: word count */
extern void dr_sd_delay_ms(uint32_t ms);
extern volatile bool data_tx_in_progress;
/*------------------------------------------------------------------------------
* Device state / flags
*----------------------------------------------------------------------------*/
extern bool device_status;
extern bool con_single;
extern bool lock_check;
extern uint8_t resetCount;
/*------------------------------------------------------------------------------
* Measurement functions (defined elsewhere)
*----------------------------------------------------------------------------*/
extern void battery_level_meas(void);
extern void pressure_all_level_meas(void);
extern void tmp235_voltage_level_meas(void);
extern int imu_read_direct(void);
extern void battery_timer_stop(void);
extern void main_timer_start(void);
extern void hw_i2c_init_once(void);
/*------------------------------------------------------------------------------
* info4 mode sensor cache (used to assemble the rbb: response in mbb?)
*----------------------------------------------------------------------------*/
extern bool info4;
extern bool ble_got_new_data;
extern bool go_batt;
extern bool motion_data_once;
extern bool motion_raw_data_enabled;
extern volatile uint16_t info_batt;
extern volatile uint16_t info_temp;
extern volatile uint16_t info_imu[6];
extern volatile bool tmp235_saadc_done;
extern volatile bool battery_saadc_done;
/*------------------------------------------------------------------------------
* Device identifiers / passkey (FDS-backed)
*----------------------------------------------------------------------------*/
extern char SERIAL_NO[12];
extern char HW_NO[12];
extern char m_static_passkey[6];
extern uint32_t m_life_cycle;
/*------------------------------------------------------------------------------
* Power / reset / bonding control
*----------------------------------------------------------------------------*/
extern bool go_device_power_off;
extern bool go_NVIC_SystemReset;
extern bool bond_data_delete;
extern uint8_t m_reset_status;
extern void config_save(void);
extern config_data_t m_config;
/*------------------------------------------------------------------------------
* AGC gain switch (P0.20)
*----------------------------------------------------------------------------*/
#define GAIN_SW_PIN NRF_GPIO_PIN_MAP(0, 20)
#define AGC_GAIN_SW(x) do { if(x) nrf_gpio_pin_set(GAIN_SW_PIN); else nrf_gpio_pin_clear(GAIN_SW_PIN); } while(0)
#endif /* CMD_COMMON_H */
@@ -1,62 +0,0 @@
/*==============================================================================
* cmd_table.c - Command table definition
*
* This file is the only linkage point between parser.c and the cmd_*.c handler modules.
* It pulls in every cmd_*.h to gather handler prototypes,
* builds the CmdEntry array, and injects the table into the parser via dr_parser_init() inside cmd_table_init().
*
* Adding a new command:
* 1) Implement the handler in the appropriate cmd_*.c
* 2) Add the prototype to the matching cmd_*.h
* 3) Append { "tag?", true, Cmd_xxx } to m_cmd_table[] below
*============================================================================*/
#include "parser.h"
#include "cmd_table.h"
#include "cmd_device.h"
#include "cmd_info.h"
#include "cmd_sensor.h"
#include "cmd_piezo.h"
static const CmdEntry m_cmd_table[] = {
/* A. Device state control */
{ "msq?", true, Cmd_msq },
{ "mss?", true, Cmd_mss },
#if FEATURE_SECURE_CONNECTION
{ "msr?", true, Cmd_msr },
#endif
{ "cmd?", true, Cmd_cmd },
/* B. Device information */
{ "mfv?", true, Cmd_mfv },
{ "mid?", true, Cmd_mid },
{ "mwh?", true, Cmd_mwh },
{ "mws?", true, Cmd_mws },
{ "mrh?", true, Cmd_mrh },
{ "mrs?", true, Cmd_mrs },
{ "mpz?", true, Cmd_mpz },
{ "mqz?", true, Cmd_mqz },
/* C. Sensor measurement */
{ "msn?", true, Cmd_msn },
{ "mst?", true, Cmd_mst },
{ "msp?", true, Cmd_msp },
/* D. Piezo ultrasound */
{ "mpa?", true, Cmd_mpa },
{ "mpb?", true, Cmd_mpb },
{ "mpc?", true, Cmd_mpc },
{ "mec?", true, Cmd_mec },
{ "maa?", true, Cmd_maa },
{ "mbb?", true, Cmd_mbb },
{ "mcf?", true, Cmd_mcf },
{ "mcs?", true, Cmd_mcs },
/* E. LED control (handler in cmd_device.c) */
{ "mls?", true, Cmd_mls },
};
void cmd_table_init(void)
{
dr_parser_init(m_cmd_table, sizeof(m_cmd_table) / sizeof(m_cmd_table[0]));
}
@@ -1,12 +0,0 @@
/*==============================================================================
* cmd_table.h - Command table registration entry point
*
* main.c calls cmd_table_init() at boot to inject the command table into
* the parser.
*============================================================================*/
#ifndef CMD_TABLE_H
#define CMD_TABLE_H
void cmd_table_init(void);
#endif /* CMD_TABLE_H */
@@ -1,177 +0,0 @@
/*==============================================================================
* cmd_device.c - Device state control handlers
*
* A. msq? -> rsq: device power OFF
* B. mss? -> rss: device soft reset
* C. msr? -> rsr: BLE bond delete + reset (FEATURE_SECURE_CONNECTION)
* D. cmd? -> rmd: direct GPIO pin control (debug / test only)
* E. mls? -> rls: set LED state
*============================================================================*/
#include "cmd_common.h"
#include "cmd_device.h"
#include "fstorage.h"
#include "led_control.h"
/*==============================================================================
* msq? -> rsq: Device power OFF
*
* Request: [TAG 4B "msq?"] [val 2B BE] [CRC 2B]
* Response: [TAG 4B "rsq:"] [val 2B BE] [CRC 2B]
*
* Sends BLE response first; the actual power-off is performed from the main loop.
* Powering off immediately would prevent the response from reaching the host, so a flag + timer pattern is used instead.
*============================================================================*/
int Cmd_msq(const ParsedCmd *cmd)
{
uint16_t val = 0;
dr_get_u16(cmd, 0, &val);
if (g_plat.log)
{
g_plat.log("[Cmd_msq] Power off val=%u\r\n", val);
}
single_format_data(ble_bin_buffer, "rsq:", val);
dr_binary_tx_safe(ble_bin_buffer, 2);
go_device_power_off = true;
main_timer_start();
return 1;
}
/*==============================================================================
* mss? -> rss: Device soft reset
*
* Request: [TAG 4B "mss?"] [val 2B BE] [CRC 2B]
* Response: [TAG 4B "rss:"] [val 2B BE] [CRC 2B]
*
* Resets without erasing bond information. Reset status code is persisted to FDS so the boot path can identify the cause.
*============================================================================*/
int Cmd_mss(const ParsedCmd *cmd)
{
uint16_t val = 0;
dr_get_u16(cmd, 0, &val);
single_format_data(ble_bin_buffer, "rss:", val);
dr_binary_tx_safe(ble_bin_buffer, 2);
m_reset_status = 2;
m_config.reset_status = m_reset_status;
config_save();
nrf_delay_ms(5);
go_NVIC_SystemReset = true;
main_timer_start();
return 1;
}
/*==============================================================================
* msr? -> rsr: Delete BLE bonding info + system reset
*
* Request: [TAG 4B "msr?"] [val 2B BE] [CRC 2B]
* Response: [TAG 4B "rsr:"] [val 2B BE] [CRC 2B]
*
* 1. Send BLE response
* 2. Persist bond-delete flag and reset status to FDS
* 3. Wait 5 ms for FDS write completion
* 4. Set NVIC system reset flag
*============================================================================*/
#if FEATURE_SECURE_CONNECTION
int Cmd_msr(const ParsedCmd *cmd)
{
uint16_t val = 0;
dr_get_u16(cmd, 0, &val);
single_format_data(ble_bin_buffer, "rsr:", val);
dr_binary_tx_safe(ble_bin_buffer, 2);
bond_data_delete = true;
m_config.bond_data_delete = (uint8_t)bond_data_delete;
m_reset_status = 2;
m_config.reset_status = m_reset_status;
config_save();
nrf_delay_ms(5);
go_NVIC_SystemReset = true;
main_timer_start();
return 1;
}
#endif
/*==============================================================================
* cmd? -> rmd: Direct GPIO pin control (debug / test)
*
* Request: [TAG 4B "cmd?"] [port 2B BE] [pin 2B BE] [state 2B BE] [CRC 2B]
* Response: [TAG 4B "rmd:"] [port 2B BE] [pin 2B BE] [state 2B BE] [CRC 2B]
* Error: rmd: + 0 (insufficient data)
*
* port : GPIO port (0 or 1)
* pin : pin number (0..31)
* state: 1=HIGH, 0=LOW
*
* Misuse can damage hardware - intended for debug only.
*============================================================================*/
int Cmd_cmd(const ParsedCmd *cmd)
{
uint16_t v1, v2, v3;
uint32_t pin_number;
if (cmd->data_len < 6)
{
dr_ble_return_1("rmd:", 0);
return 1;
}
if (!dr_get_u16(cmd, 0, &v1))
{
v1 = 0;
}
if (!dr_get_u16(cmd, 1, &v2))
{
v2 = 0;
}
if (!dr_get_u16(cmd, 2, &v3))
{
v3 = 0;
}
pin_number = NRF_GPIO_PIN_MAP(v1, v2);
nrf_gpio_cfg_output(pin_number);
if (v3 == 1)
{
nrf_gpio_pin_set(pin_number);
}
else
{
nrf_gpio_pin_clear(pin_number);
}
dr_ble_return_3("rmd:", v1, v2, v3);
return 1;
}
/*==============================================================================
* mls? -> rls: Set LED state (app -> device)
*
* Request: [TAG 4B "mls?"] [state 2B BE] [CRC 2B]
* state: led_state_t enum value: 0=OFF, 4=DETACH_WARNING, 5=ALIGN_SEARCHING, 6=ALIGN_COMPLETE
* Response: [TAG 4B "rls:"] [state 2B] [CRC 2B]
* Error: rls: + 0xFFFF (insufficient data)
* rls: + 0xFFFE (state out of range)
*============================================================================*/
int Cmd_mls(const ParsedCmd *cmd)
{
if (cmd->data_len < 2)
{
dr_ble_return_1("rls:", 0xFFFF);
return 1;
}
uint16_t state;
dr_get_u16(cmd, 0, &state);
if (state > LED_STATE_ERROR)
{
dr_ble_return_1("rls:", 0xFFFE);
return 1;
}
led_set_state((led_state_t)state);
dr_ble_return_1("rls:", state);
return 1;
}
@@ -1,17 +0,0 @@
/*==============================================================================
* cmd_device.h - Device state control handlers (power / reset / GPIO / LED)
*============================================================================*/
#ifndef CMD_DEVICE_H
#define CMD_DEVICE_H
#include "parser.h"
int Cmd_msq(const ParsedCmd *cmd); /* msq? -> rsq: device power OFF */
int Cmd_mss(const ParsedCmd *cmd); /* mss? -> rss: device soft reset */
#if FEATURE_SECURE_CONNECTION
int Cmd_msr(const ParsedCmd *cmd); /* msr? -> rsr: bond delete + reset */
#endif
int Cmd_cmd(const ParsedCmd *cmd); /* cmd? -> rmd: direct GPIO control */
int Cmd_mls(const ParsedCmd *cmd); /* mls? -> rls: set LED state */
#endif /* CMD_DEVICE_H */
@@ -1,173 +0,0 @@
/*==============================================================================
* cmd_info.c - Device information read/write handlers
*
* Commands that read or write FDS-persisted data: firmware version, hardware version, serial number, BLE passkey.
* Typically written once at the factory and read back later for identification.
*============================================================================*/
#include "cmd_common.h"
#include "cmd_info.h"
#include "fstorage.h"
/*==============================================================================
* mfv? -> rfv: Read firmware version
*
* Request: [TAG 4B "mfv?"] [CRC 2B]
* Response: [TAG 4B "rfv:"] [FW_VER 12B ASCII] [CRC 2B]
*============================================================================*/
int Cmd_mfv(const ParsedCmd *cmd)
{
(void)cmd;
ascii_format_data(ble_bin_buffer, "rfv:", FIRMWARE_VERSION, 12);
dr_binary_tx_safe(ble_bin_buffer, 8); /* 16 bytes = 8 words */
return 1;
}
/*==============================================================================
* mid? -> rid: Read HW number + serial number + FW version together
*
* Request: [TAG 4B "mid?"] [CRC 2B]
* Response: [TAG 4B "rid:"] [HW 12B] [SN 12B] [FW 12B] [CRC 2B]
*============================================================================*/
int Cmd_mid(const ParsedCmd *cmd)
{
uint8_t *buf = ble_bin_buffer;
(void)cmd;
memcpy(HW_NO, m_config.hw_no, 12);
memcpy(SERIAL_NO, m_config.serial_no, 12);
buf[0] = 'r'; buf[1] = 'i'; buf[2] = 'd'; buf[3] = ':';
memcpy(&buf[4], HW_NO, 12);
memcpy(&buf[16], SERIAL_NO, 12);
memcpy(&buf[28], FIRMWARE_VERSION, 12);
dr_binary_tx_safe(buf, 20); /* 40 bytes = 20 words */
return 1;
}
/*==============================================================================
* mwh? -> rwh: Write HW number to FDS
*
* Request: [TAG 4B "mwh?"] [HW 12B ASCII] [CRC 2B]
* Response: [TAG 4B "rwh:"] [HW 12B ASCII] [CRC 2B]
* Error: rwh: + 0xFFFF (insufficient data)
*============================================================================*/
int Cmd_mwh(const ParsedCmd *cmd)
{
char buf[13];
if (cmd->data_len < 12)
{
dr_ble_return_1("rwh:", 0xFFFF);
return 1;
}
dr_get_ascii(cmd, 0, buf, 12);
memcpy(HW_NO, buf, 12);
memcpy(m_config.hw_no, buf, 12);
config_save();
ascii_format_data(ble_bin_buffer, "rwh:", buf, 12);
dr_binary_tx_safe(ble_bin_buffer, 8);
return 1;
}
/*==============================================================================
* mws? -> rws: Write serial number to FDS
*
* Request: [TAG 4B "mws?"] [SN 12B ASCII] [CRC 2B]
* Response: [TAG 4B "rws:"] [SN 12B ASCII] [CRC 2B]
* Error: rws: + 0xFFFF (insufficient data)
*============================================================================*/
int Cmd_mws(const ParsedCmd *cmd)
{
char buf[13];
if (cmd->data_len < 12)
{
dr_ble_return_1("rws:", 0xFFFF);
return 1;
}
dr_get_ascii(cmd, 0, buf, 12);
memcpy(SERIAL_NO, buf, 12);
memcpy(m_config.serial_no, buf, 12);
config_save();
ascii_format_data(ble_bin_buffer, "rws:", buf, 12);
dr_binary_tx_safe(ble_bin_buffer, 8);
return 1;
}
/*==============================================================================
* mrh? -> rrh: Read HW number from FDS
*
* Request: [TAG 4B "mrh?"] [CRC 2B]
* Response: [TAG 4B "rrh:"] [HW 12B ASCII] [CRC 2B]
*============================================================================*/
int Cmd_mrh(const ParsedCmd *cmd)
{
(void)cmd;
memcpy(HW_NO, m_config.hw_no, 12);
ascii_format_data(ble_bin_buffer, "rrh:", HW_NO, 12);
dr_binary_tx_safe(ble_bin_buffer, 8);
return 1;
}
/*==============================================================================
* mrs? -> rrs: Read serial number from FDS
*
* Request: [TAG 4B "mrs?"] [CRC 2B]
* Response: [TAG 4B "rrs:"] [SN 12B ASCII] [CRC 2B]
*============================================================================*/
int Cmd_mrs(const ParsedCmd *cmd)
{
(void)cmd;
memcpy(SERIAL_NO, m_config.serial_no, 12);
ascii_format_data(ble_bin_buffer, "rrs:", SERIAL_NO, 12);
dr_binary_tx_safe(ble_bin_buffer, 8);
return 1;
}
/*==============================================================================
* mpz? -> rpz: Write BLE passkey to FDS
*
* Request: [TAG 4B "mpz?"] [passkey 6B ASCII] [CRC 2B]
* Response: [TAG 4B "rpz:"] [passkey 6B ASCII] [CRC 2B]
*============================================================================*/
int Cmd_mpz(const ParsedCmd *cmd)
{
if (m_config.factory_provisioned != 0)
{
dr_ble_return_1("rpz:", 0xFFFF);
return 1;
}
char passkey[7] = {0};
dr_get_ascii(cmd, 0, passkey, 6);
memcpy(m_static_passkey, passkey, 6);
memcpy(m_config.static_passkey, m_static_passkey, 6);
m_config.factory_provisioned = 1;
config_save();
ascii_format_data(ble_bin_buffer, "rpz:", passkey, 6);
dr_binary_tx_safe(ble_bin_buffer, 5);
return 1;
}
/*==============================================================================
* mqz? -> rqz: Read BLE passkey from FDS
*
* Request: [TAG 4B "mqz?"] [CRC 2B]
* Response: [TAG 4B "rqz:"] [passkey 6B ASCII] [CRC 2B]
*============================================================================*/
int Cmd_mqz(const ParsedCmd *cmd)
{
(void)cmd;
memcpy(m_static_passkey, m_config.static_passkey, 6);
ascii_format_data(ble_bin_buffer, "rqz:", m_static_passkey, 6);
dr_binary_tx_safe(ble_bin_buffer, 5);
return 1;
}
@@ -1,18 +0,0 @@
/*==============================================================================
* cmd_info.h - Device information read/write handlers
*============================================================================*/
#ifndef CMD_INFO_H
#define CMD_INFO_H
#include "parser.h"
int Cmd_mfv(const ParsedCmd *cmd); /* mfv? -> rfv: read firmware version */
int Cmd_mid(const ParsedCmd *cmd); /* mid? -> rid: read HW + SN + FW together */
int Cmd_mwh(const ParsedCmd *cmd); /* mwh? -> rwh: write HW number */
int Cmd_mws(const ParsedCmd *cmd); /* mws? -> rws: write serial number */
int Cmd_mrh(const ParsedCmd *cmd); /* mrh? -> rrh: read HW number */
int Cmd_mrs(const ParsedCmd *cmd); /* mrs? -> rrs: read serial number */
int Cmd_mpz(const ParsedCmd *cmd); /* mpz? -> rpz: write BLE passkey */
int Cmd_mqz(const ParsedCmd *cmd); /* mqz? -> rqz: read BLE passkey */
#endif /* CMD_INFO_H */
@@ -1,343 +0,0 @@
/*==============================================================================
* cmd_piezo.c - Piezo ultrasound measurement handlers
*
* mpa? -> rpa: TX/RX power ON
* mpb? -> rpb: TX/RX power OFF
* mpc? -> rpc: burst generation (test)
* mec? -> reb:+raa: single-channel burst + echo capture
* maa? -> reb:+raa: 6-channel asynchronous capture
* mbb? -> rbb:+reb:+raa: bulk sensor measurement + 6-channel capture
* mcf? -> rcf: read piezo parameters
* mcs? -> rcs: write piezo parameters
*============================================================================*/
#include "cmd_common.h"
#include "cmd_piezo.h"
#include "cmd_sensor.h" /* all_sensors() */
#include "dr_piezo.h"
#include "dr_adc121s051.h"
/*==============================================================================
* mpa? -> rpa: Enable piezo TX/RX circuit
*
* Request: [TAG 4B "mpa?"] [CRC 2B]
* Response: [TAG 4B "rpa:"] [1 2B] [CRC 2B]
*============================================================================*/
int Cmd_mpa(const ParsedCmd *cmd)
{
(void)cmd;
dr_piezo_power_on();
if (g_plat.tx_bin)
{
single_format_data(ble_bin_buffer, "rpa:", 1);
dr_binary_tx_safe(ble_bin_buffer, 3);
}
return 1;
}
/*==============================================================================
* mpb? -> rpb: Disable piezo TX/RX circuit
*
* Request: [TAG 4B "mpb?"] [CRC 2B]
* Response: [TAG 4B "rpb:"] [1 2B] [CRC 2B]
*
* Power saving once measurement is complete.
*============================================================================*/
int Cmd_mpb(const ParsedCmd *cmd)
{
(void)cmd;
dr_piezo_power_off();
if (g_plat.tx_bin)
{
single_format_data(ble_bin_buffer, "rpb:", 1);
dr_binary_tx_safe(ble_bin_buffer, 3);
}
return 1;
}
/*==============================================================================
* mpc? -> rpc: Piezo burst generation (test / debug)
*
* Request: [TAG 4B "mpc?"] [cycles 2B] [freq_option 2B] [piezo_ch 2B] [CRC 2B]
* cycles : 3..7 (default 5)
* freq_option : 0=1.8MHz, 1=2.1MHz(default), 2=2.0MHz, 3=1.7MHz, 4=2.2MHz
* piezo_ch : 0..7
* Response: [TAG 4B "rpc:"] [cycles 2B] [CRC 2B]
* Error: rpc: + 2 (cycles out of range)
*
* Generates the burst only - no echo capture.
*============================================================================*/
int Cmd_mpc(const ParsedCmd *cmd)
{
uint16_t cycles = 5;
uint16_t freq_option = 1;
uint16_t piezo_ch = 0;
(void)dr_get_u16(cmd, 0, &cycles);
(void)dr_get_u16(cmd, 1, &freq_option);
(void)dr_get_u16(cmd, 2, &piezo_ch);
if (piezo_ch >= MAA_NUM_CHANNELS)
{
piezo_ch = 0;
}
if (cycles < 3 || cycles > 7)
{
dr_ble_return_1("rpc:", 2);
return 1;
}
dr_piezo_select_channel((uint8_t)piezo_ch);
switch (freq_option)
{
case 0:
dr_piezo_burst_sw_18mhz((uint8_t)cycles);
break;
case 2:
dr_piezo_burst_sw_20mhz((uint8_t)cycles);
break;
case 3:
dr_piezo_burst_sw_17mhz((uint8_t)cycles);
break;
case 4:
dr_piezo_burst_sw_22mhz((uint8_t)cycles);
break;
default:
dr_piezo_burst_sw((uint8_t)cycles);
break;
}
dr_ble_return_1("rpc:", (uint8_t)cycles);
return 1;
}
/*==============================================================================
* mec? -> reb:+raa: Piezo burst + echo capture (16-bit raw)
*
* Request: [TAG 4B "mec?"] [freq 2B] [delay_us 2B] [num_samples 2B]
* [cycles 2B] [averaging 2B] [piezo_ch 2B] [CRC 2B]
* Response: reb: [num_samples 2B] [raw_data up to 238B] (red: follow-up if needed)
* raa: [status 2B]
* Error: rer: + (0xEE00|err) + num_samples
*
* Sends raw 16-bit ADC values directly. Suited for short-range (~25cm).
*============================================================================*/
int Cmd_mec(const ParsedCmd *cmd)
{
uint16_t freq_option = 0;
uint16_t delay_us = 20;
uint16_t num_samples = 140;
uint16_t cycles = 5;
uint16_t averaging = 1;
uint16_t piezo_ch = 0;
if (!dr_piezo_is_power_on())
{
dr_piezo_power_on();
}
(void)dr_get_u16(cmd, 0, &freq_option);
(void)dr_get_u16(cmd, 1, &delay_us);
(void)dr_get_u16(cmd, 2, &num_samples);
(void)dr_get_u16(cmd, 3, &cycles);
(void)dr_get_u16(cmd, 4, &averaging);
(void)dr_get_u16(cmd, 5, &piezo_ch);
if (averaging == 0)
{
averaging = 1;
}
if (averaging > 1000)
{
averaging = 1000;
}
if (piezo_ch >= MAA_NUM_CHANNELS)
{
piezo_ch = 0;
}
dr_adc_err_t err = dr_adc_burst_capture_transmit(
(uint8_t)freq_option, delay_us, num_samples, (uint8_t)cycles,
(uint16_t)averaging, (uint8_t)piezo_ch, ble_bin_buffer, 0);
if (err != DR_ADC_OK)
{
dr_ble_return_2("rer:", 0xEE00 | (uint16_t)err, num_samples);
}
dr_piezo_power_off();
return 1;
}
/*==============================================================================
* maa? -> reb:+raa: 6-channel asynchronous full capture
*
* Request: [TAG 4B "maa?"] [CRC 2B]
* Response: per channel reb: [num_samples 2B] [raw_data...]
* final raa: [status 2B]
* Error: raa: + 0xFFFE (previous capture in progress)
* raa: + (0xFF00|err) (start failed)
*
* Asynchronous state machine - the BLE_NUS_EVT_TX_RDY callback drives the
* follow-up packet transmissions. Parameters are loaded from m_config (FDS).
*============================================================================*/
int Cmd_maa(const ParsedCmd *cmd)
{
dr_adc_err_t err;
(void)cmd;
if (maa_async_is_busy())
{
dr_ble_return_1("raa:", 0xFFFE);
return 1;
}
if (!dr_piezo_is_power_on())
{
dr_piezo_power_on();
}
err = maa_async_start(
m_config.piezo_freq_option,
m_config.piezo_delay_us,
m_config.piezo_num_samples,
m_config.piezo_cycles,
m_config.piezo_averaging,
ble_bin_buffer
);
if (err != DR_ADC_OK)
{
if (g_plat.log)
{
g_plat.log("[Cmd_maa] start failed err=%d\r\n", err);
}
single_format_data(ble_bin_buffer, "raa:", (uint16_t)(0xFF00 | err));
dr_binary_tx_safe(ble_bin_buffer, 3);
dr_piezo_power_off();
return 1;
}
return 1;
}
/*==============================================================================
* mbb? -> rbb:+reb:+raa: Bulk sensor measurement + 6-channel full capture
*
* Request: [TAG 4B "mbb?"] [CRC 2B]
* Response: rbb: [batt 2B] [IMU 12B] [temp 2B]
* reb: [num_samples 2B] [raw_data...] (per channel)
* raa: [status 2B]
* Error: raa: + 0xFFFE (previous capture in progress)
* raa: + (0xFF00|err) (start failed)
*
* 1) all_sensors() measures battery / IMU / temperature and emits rbb:
* 2) Starts 6-channel capture in pre-capture-all mode
*============================================================================*/
int Cmd_mbb(const ParsedCmd *cmd)
{
dr_adc_err_t err;
(void)cmd;
all_sensors();
if (maa_async_is_busy())
{
dr_ble_return_1("raa:", 0xFFFE);
return 1;
}
maa_async_set_pre_capture_all(true);
err = maa_async_start(
m_config.piezo_freq_option,
m_config.piezo_delay_us,
m_config.piezo_num_samples,
m_config.piezo_cycles,
m_config.piezo_averaging,
ble_bin_buffer
);
if (err != DR_ADC_OK)
{
if (g_plat.log)
{
g_plat.log("[Cmd_mbb] start failed err=%d\r\n", err);
}
single_format_data(ble_bin_buffer, "raa:", (uint16_t)(0xFF00 | err));
dr_binary_tx_safe(ble_bin_buffer, 3);
dr_piezo_power_off();
return 1;
}
return 1;
}
/*==============================================================================
* mcf? -> rcf: Read piezo parameters from FDS
*
* Request: [TAG 4B "mcf?"] [CRC 2B]
* Response: [TAG 4B "rcf:"] [freq 2B] [cycles 2B] [avg 2B] [delay_us 2B] [num_samples 2B] [CRC 2B]
*============================================================================*/
int Cmd_mcf(const ParsedCmd *cmd)
{
(void)cmd;
uint8_t *buf = ble_bin_buffer;
buf[0] = 'r'; buf[1] = 'c'; buf[2] = 'f'; buf[3] = ':';
buf[4] = 0; buf[5] = m_config.piezo_freq_option;
buf[6] = 0; buf[7] = m_config.piezo_cycles;
buf[8] = (uint8_t)(m_config.piezo_averaging >> 8); buf[9] = (uint8_t)(m_config.piezo_averaging & 0xFF);
buf[10] = (uint8_t)(m_config.piezo_delay_us >> 8); buf[11] = (uint8_t)(m_config.piezo_delay_us & 0xFF);
buf[12] = (uint8_t)(m_config.piezo_num_samples >> 8); buf[13] = (uint8_t)(m_config.piezo_num_samples & 0xFF);
dr_binary_tx_safe(buf, 7); /* 14 bytes = 7 words */
return 1;
}
/*==============================================================================
* mcs? -> rcs: Write piezo parameters to FDS
*
* Request: [TAG 4B "mcs?"] [freq 2B] [cycles 2B] [avg 2B] [delay_us 2B] [num_samples 2B] [CRC 2B]
* Response: [TAG 4B "rcs:"] [stored 5 values] [CRC 2B]
* Error: rcs: + 0xFFFF (insufficient data)
*============================================================================*/
int Cmd_mcs(const ParsedCmd *cmd)
{
if (cmd->data_len < 10)
{
if (g_plat.log)
{
g_plat.log("[Cmd_mcs] missing params (data_len=%u)\r\n", cmd->data_len);
}
dr_ble_return_1("rcs:", 0xFFFF);
return 1;
}
uint16_t freq, cycles, averaging, delay_us, num_samples;
dr_get_u16(cmd, 0, &freq);
dr_get_u16(cmd, 1, &cycles);
dr_get_u16(cmd, 2, &averaging);
dr_get_u16(cmd, 3, &delay_us);
dr_get_u16(cmd, 4, &num_samples);
m_config.piezo_freq_option = (uint8_t)freq;
m_config.piezo_cycles = (uint8_t)cycles;
m_config.piezo_averaging = averaging;
m_config.piezo_delay_us = delay_us;
m_config.piezo_num_samples = num_samples;
config_save();
uint8_t *buf = ble_bin_buffer;
buf[0] = 'r'; buf[1] = 'c'; buf[2] = 's'; buf[3] = ':';
buf[4] = 0; buf[5] = m_config.piezo_freq_option;
buf[6] = 0; buf[7] = m_config.piezo_cycles;
buf[8] = (uint8_t)(m_config.piezo_averaging >> 8); buf[9] = (uint8_t)(m_config.piezo_averaging & 0xFF);
buf[10] = (uint8_t)(m_config.piezo_delay_us >> 8); buf[11] = (uint8_t)(m_config.piezo_delay_us & 0xFF);
buf[12] = (uint8_t)(m_config.piezo_num_samples >> 8); buf[13] = (uint8_t)(m_config.piezo_num_samples & 0xFF);
dr_binary_tx_safe(buf, 7);
return 1;
}
@@ -1,18 +0,0 @@
/*==============================================================================
* cmd_piezo.h - Piezo ultrasound measurement handlers
*============================================================================*/
#ifndef CMD_PIEZO_H
#define CMD_PIEZO_H
#include "parser.h"
int Cmd_mpa(const ParsedCmd *cmd); /* mpa? -> rpa: TX/RX power ON */
int Cmd_mpb(const ParsedCmd *cmd); /* mpb? -> rpb: TX/RX power OFF */
int Cmd_mpc(const ParsedCmd *cmd); /* mpc? -> rpc: burst generation */
int Cmd_mec(const ParsedCmd *cmd); /* mec? -> reb:+raa: single-channel capture */
int Cmd_maa(const ParsedCmd *cmd); /* maa? -> reb:+raa: 6-channel async capture */
int Cmd_mbb(const ParsedCmd *cmd); /* mbb? -> rbb:+reb:+raa: sensors + capture */
int Cmd_mcf(const ParsedCmd *cmd); /* mcf? -> rcf: read piezo parameters */
int Cmd_mcs(const ParsedCmd *cmd); /* mcs? -> rcs: write piezo parameters */
#endif /* CMD_PIEZO_H */
@@ -1,138 +0,0 @@
/*==============================================================================
* cmd_sensor.c - Sensor measurement handlers
*
* msn? -> rsn: battery ADC measurement
* mso? -> rso: TMP235 temperature reading
* msp? -> rsp: IMU 6-axis single read
* all_sensors() bulk-measurement helper used by the mbb? handler
*============================================================================*/
#include "cmd_common.h"
#include "cmd_sensor.h"
#include "dr_piezo.h"
/*==============================================================================
* msn? -> rsn: Battery level ADC measurement
*
* Request: [TAG 4B "msn?"] [CRC 2B]
* Response: rsn: + battery voltage in mV (sent from battery_saadc.c callback)
*============================================================================*/
int Cmd_msn(const ParsedCmd *cmd)
{
(void)cmd;
battery_level_meas();
return 1;
}
/*==============================================================================
* mst? -> rso: Temperature with piezo power cycle
*
* Request: [TAG 4B "mst?"] [CRC 2B]
* Response: [TAG 4B "rso:"] [temp_x100 2B BE] [CRC 2B]
*
* TMP235 shares the piezo TX/RX power rail. This command handles the full
* sequence: power ON -> measure -> power OFF, so the caller doesn't need
* to send mpa?/mpb? separately.
* Response is sent from the TMP235 SAADC callback (tmp235_voltage_handler).
*============================================================================*/
int Cmd_mst(const ParsedCmd *cmd)
{
uint32_t timeout_cnt;
(void)cmd;
if (!dr_piezo_is_power_on())
{
dr_piezo_power_on();
}
tmp235_saadc_done = false;
tmp235_voltage_level_meas();
for (timeout_cnt = 0; !tmp235_saadc_done && timeout_cnt < 100; timeout_cnt++)
{
dr_sd_delay_ms(1);
}
dr_piezo_power_off();
return 1;
}
/*==============================================================================
* msp? -> rsp: IMU 6-axis single read
*
* Request: [TAG 4B "msp?"] [CRC 2B]
* Response: rsp: + accel(xyz) + gyro(xyz) (transmitted inside imu_read_direct)
*
* Reads IMU registers directly over I2C and transmits over BLE. Synchronous.
*============================================================================*/
int Cmd_msp(const ParsedCmd *cmd)
{
(void)cmd;
hw_i2c_init_once();
imu_read_direct();
return 1;
}
/*==============================================================================
* all_sensors() - Bulk-measurement helper for the mbb? handler
*
* Stores sensor values into globals via info4 mode, then transmits them as a
* single rbb: packet. SAADC measurements run asynchronously (callback), so
* dr_sd_delay_ms() is used to wait for completion.
*
* Order: battery -> IMU -> (Piezo TX/RX ON) -> temperature
* Response: rbb: [batt 2B] [IMU 6x2B] [temp 2B] = 20 bytes = 10 words
*============================================================================*/
void all_sensors(void)
{
uint8_t *buf;
uint32_t timeout_cnt;
info4 = true;
/* 1. Battery voltage -> info_batt */
battery_saadc_done = false;
battery_level_meas();
for (timeout_cnt = 0; !battery_saadc_done && timeout_cnt < 100; timeout_cnt++)
{
dr_sd_delay_ms(1);
}
/* 2. IMU 6-axis single read -> info_imu[6] */
hw_i2c_init_once();
imu_read_direct();
/* 3. Temperature -> info_temp (TMP235 needs Piezo TX/RX power) */
if (!dr_piezo_is_power_on())
{
dr_piezo_power_on();
}
tmp235_saadc_done = false;
tmp235_voltage_level_meas();
for (timeout_cnt = 0; !tmp235_saadc_done && timeout_cnt < 100; timeout_cnt++)
{
dr_sd_delay_ms(1);
}
info4 = false;
/* Assemble and transmit the rbb: packet (dedicated buffer to avoid
collision with ble_bin_buffer used by the async ADC state machine) */
static uint8_t rbb_buf[20];
buf = rbb_buf;
buf[0] = 'r'; buf[1] = 'b'; buf[2] = 'b'; buf[3] = ':';
buf[4] = (uint8_t)(info_batt >> 8);
buf[5] = (uint8_t)(info_batt & 0xFF);
for (int i = 0; i < 6; i++)
{
buf[6 + i * 2] = (uint8_t)(info_imu[i] >> 8);
buf[6 + i * 2 + 1] = (uint8_t)(info_imu[i] & 0xFF);
}
buf[18] = (uint8_t)(info_temp >> 8);
buf[19] = (uint8_t)(info_temp & 0xFF);
dr_binary_tx_safe(buf, 10); /* 20 bytes = 10 words */
}
@@ -1,16 +0,0 @@
/*==============================================================================
* cmd_sensor.h - Sensor measurement handlers (battery / temperature / IMU)
*============================================================================*/
#ifndef CMD_SENSOR_H
#define CMD_SENSOR_H
#include "parser.h"
int Cmd_msn(const ParsedCmd *cmd); /* msn? -> rsn: battery ADC measurement */
int Cmd_mst(const ParsedCmd *cmd); /* mst? -> rso: TMP235 with piezo power cycle */
int Cmd_msp(const ParsedCmd *cmd); /* msp? -> rsp: IMU 6-axis single read */
/* Helper for the mbb? handler: sequentially measures battery / IMU / temperature, then emits a single rbb: response.
* Called from Cmd_mbb() in cmd_piezo.c. */
void all_sensors(void);
#endif /* CMD_SENSOR_H */
@@ -1,336 +0,0 @@
/*==============================================================================
* parser.c - BLE command parser / dispatcher
*
* Common module that parses binary command packets received over BLE and
* dispatches them to the appropriate handler from the command table.
*
* Key point: this file does not know which Cmd_* functions exist.
* cmd_table.c injects the table pointer at boot via dr_parser_init().
*
* Packet layout: [TAG 4B] [data N] [CRC16 2B (optional)]
* - TAG : 4-char ASCII identifier (e.g. "mid?")
* - data : Big-Endian uint16 or ASCII
* - CRC16: present when g_plat.crc_check is true; trailing 2 bytes (LE)
*
* Flow:
* 1) dr_cmd_parser() - external entry point
* 2) dr_parse_cmd() - length / CRC verification + TAG/data split
* 3) dr_cmd_dispatch() - walks the command table -> calls handler
*
* Error responses (all formatted as [err_tag 4B] [echo cmd_tag 4B] = 8 bytes):
* - rxs: Too short (< 4B, or < 7B with CRC enabled)
* - rxc: CRC fail
* - rxd: Disabled command
* - rxn: NULL handler
* - rxx: Unknown command
*============================================================================*/
#include "parser.h"
#include <string.h>
/*------------------------------------------------------------------------------
* Globals (declared extern in header)
*----------------------------------------------------------------------------*/
dr_platform_if_t g_plat = { 0, 0, 0 };
bool g_log_enable = false;
/*------------------------------------------------------------------------------
* Internal state - command table pointer injected via dr_parser_init()
*----------------------------------------------------------------------------*/
static const CmdEntry *m_cmd_table = NULL;
static uint16_t m_cmd_count = 0;
/*==============================================================================
* Internal utilities (static)
*============================================================================*/
/* Copy first 4 bytes of buffer into a null-terminated TAG string. */
static void dr_copy_tag(const uint8_t *buf, char *tag_out)
{
tag_out[0] = (char)buf[0];
tag_out[1] = (char)buf[1];
tag_out[2] = (char)buf[2];
tag_out[3] = (char)buf[3];
tag_out[4] = '\0';
}
/* Compare two 4-char TAGs (byte-by-byte, faster than memcmp here). */
static bool dr_tag_eq(const char *tag, const char *key4)
{
return (tag[0] == key4[0] &&
tag[1] == key4[1] &&
tag[2] == key4[2] &&
tag[3] == key4[3]);
}
/*==============================================================================
* Public utilities - used by handlers
*============================================================================*/
bool dr_get_u16(const ParsedCmd *cmd, uint8_t word_index, uint16_t *out)
{
uint8_t pos = (uint8_t)(word_index * 2);
if (cmd->data_len < (uint8_t)(pos + 2))
{
return false;
}
*out = (uint16_t)((uint16_t)cmd->data[pos] << 8) | (uint16_t)cmd->data[pos + 1];
return true;
}
void dr_get_ascii(const ParsedCmd *cmd, uint8_t offset, char *out, uint8_t max_len)
{
uint8_t i;
uint8_t remain;
if (offset >= cmd->data_len)
{
out[0] = '\0';
return;
}
remain = (uint8_t)(cmd->data_len - offset);
if (remain > max_len)
{
remain = max_len;
}
for (i = 0; i < remain; i++)
{
out[i] = (char)cmd->data[offset + i];
}
out[remain] = '\0';
}
/*==============================================================================
* CRC16 (Nordic SDK CRC-CCITT variant)
*============================================================================*/
uint16_t dr_crc16_compute(const uint8_t *p_data, uint32_t size, const uint16_t *p_crc)
{
uint32_t i;
uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc;
for (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;
}
static bool dr_crc16_check(const uint8_t *p_data, uint32_t data_len, uint16_t expected_crc)
{
return (dr_crc16_compute(p_data, data_len, NULL) == expected_crc);
}
/* Extract trailing 2 bytes (Little-Endian) as expected CRC and verify. */
static bool dr_crc16_check_packet(const uint8_t *packet, uint32_t packet_len)
{
uint16_t expected_crc;
uint32_t data_len;
if (packet_len < 2)
{
return false;
}
data_len = packet_len - 2;
expected_crc = (uint16_t)packet[packet_len - 2]
| ((uint16_t)packet[packet_len - 1] << 8);
return dr_crc16_check(packet, data_len, expected_crc);
}
/*==============================================================================
* Error response helper
*
* Packet: [err_tag 4B] [echo cmd_tag 4B] = 8 bytes = 4 words
* CRC16 is appended automatically by the tx_bin layer.
*============================================================================*/
static void dr_send_error(const char *err_tag, const char *cmd_tag)
{
if (g_plat.tx_bin)
{
uint8_t err_buf[8];
memcpy(&err_buf[0], err_tag, 4);
memcpy(&err_buf[4], cmd_tag, 4);
g_plat.tx_bin(err_buf, 4);
}
}
/*==============================================================================
* Packet parser - raw buffer -> ParsedCmd
*
* Error responses (rxs: / rxc:) are emitted directly from this function.
*============================================================================*/
static bool dr_parse_cmd(const uint8_t *buffer, uint8_t length, ParsedCmd *out)
{
uint8_t data_len;
/* Less than 4 bytes -> TAG cannot be identified */
if (length < 4)
{
dr_send_error("rxs:", "????");
if (g_plat.log && g_log_enable)
{
g_plat.log("[parser] too short (%u bytes) -> rxs:\r\n", length);
}
return false;
}
/* Extract TAG first (used in error echoes below) */
dr_copy_tag(buffer, out->tag);
/* CRC verification */
if (g_plat.crc_check)
{
if (length < 7)
{
dr_send_error("rxs:", out->tag);
if (g_plat.log && g_log_enable)
{
g_plat.log("[parser] CRC enabled but too short (%u) -> rxs:\r\n", length);
}
return false;
}
if (!dr_crc16_check_packet(buffer, length))
{
dr_send_error("rxc:", out->tag);
if (g_plat.log && g_log_enable)
{
g_plat.log("[parser] CRC mismatch '%s' -> rxc:\r\n", out->tag);
}
return false;
}
data_len = (uint8_t)(length - 4 - 2); /* strip TAG and CRC */
}
else
{
data_len = (uint8_t)(length - 4);
}
if (data_len > DR_MAX_DATA)
{
data_len = DR_MAX_DATA;
}
if (data_len > 0)
{
memcpy(out->data, buffer + 4, data_len);
}
out->data_len = data_len;
return true;
}
/*==============================================================================
* Command dispatcher
*
* Lower-cases the parsed TAG and walks the command table to find a matching
* handler. Emits the matching error response on miss / disabled / NULL handler.
*============================================================================*/
static int dr_cmd_dispatch(const ParsedCmd *cmd)
{
uint16_t i;
char tag_lower[5];
if (m_cmd_table == NULL)
{
dr_send_error("rxn:", cmd->tag);
if (g_plat.log)
{
g_plat.log("[parser] table not initialized -> rxn:\n");
}
return 0;
}
/* Case-insensitive matching */
for (i = 0; i < 4 && cmd->tag[i]; i++)
{
tag_lower[i] = (cmd->tag[i] >= 'A' && cmd->tag[i] <= 'Z') ? (cmd->tag[i] + 32) : cmd->tag[i];
}
tag_lower[i] = '\0';
for (i = 0; i < m_cmd_count; i++)
{
if (dr_tag_eq(tag_lower, m_cmd_table[i].tag))
{
if (!m_cmd_table[i].enabled)
{
dr_send_error("rxd:", cmd->tag);
if (g_plat.log && g_log_enable)
{
g_plat.log("Command '%s' disabled -> rxd:\n", cmd->tag);
}
return 0;
}
if (m_cmd_table[i].handler == NULL)
{
dr_send_error("rxn:", cmd->tag);
if (g_plat.log)
{
g_plat.log("[parser] NULL handler for '%s' -> rxn:\n", cmd->tag);
}
return 0;
}
return m_cmd_table[i].handler(cmd);
}
}
/* TAG not found in table */
dr_send_error("rxx:", cmd->tag);
if (g_plat.log && g_log_enable)
{
g_plat.log("Unknown TAG '%s' -> rxx:\n", cmd->tag);
}
return 0;
}
/*==============================================================================
* Public API
*============================================================================*/
/* Called by cmd_table.c at boot to inject the command table. */
void dr_parser_init(const CmdEntry *table, uint16_t count)
{
m_cmd_table = table;
m_cmd_count = count;
}
/* Entry point - called from BLE NUS / UART receive callbacks.
*
* Returns:
* 1 = command processed successfully
* 0 = unknown TAG / disabled command (error response already sent)
* -1 = parse failure (rxs: / rxc: error response already sent)
*/
int dr_cmd_parser(const uint8_t *buf, uint8_t len)
{
ParsedCmd cmd;
if (!dr_parse_cmd(buf, len, &cmd))
{
if (g_plat.log) g_plat.log("[PARSER] PARSE FAIL\r\n");
return -1;
}
return dr_cmd_dispatch(&cmd);
}
@@ -1,75 +0,0 @@
/*==============================================================================
* parser.h - BLE command parser public API
*
* Parser infrastructure (parsing/CRC/dispatch) and command handlers are
* decoupled:
* - Parser: parser.c (defines the functions here)
* - Handlers: handlers/cmd_device.c / cmd_info.c / ...
* - Table: cmd_table.c (injected via cmd_table_init)
*
* Boot sequence: main.c calls cmd_table_init() -> dr_parser_init() to inject
* the command table pointer into the parser.
*============================================================================*/
#ifndef PARSER_H
#define PARSER_H
#include <stdint.h>
#include <stdbool.h>
/* Maximum payload length (excludes TAG) */
#define DR_MAX_DATA 128
/*------------------------------------------------------------------------------
* Platform interface
*----------------------------------------------------------------------------*/
typedef struct {
void (*log)(const char *fmt, ...);
void (*tx_bin)(const uint8_t *buf, uint16_t len); /* len: word (uint16) count */
bool crc_check;
} dr_platform_if_t;
extern dr_platform_if_t g_plat;
extern bool g_log_enable;
/*------------------------------------------------------------------------------
* Parsed command - input passed to handlers
*----------------------------------------------------------------------------*/
typedef struct {
char tag[5]; /* Command TAG (e.g. "mid?") + '\0' */
uint8_t data[DR_MAX_DATA]; /* Payload bytes following the TAG */
uint8_t data_len; /* Valid length of data[] */
} ParsedCmd;
/*------------------------------------------------------------------------------
* Command table entry - defined in cmd_table.c
*----------------------------------------------------------------------------*/
typedef struct {
char tag[5]; /* TAG: 4 chars + '\0' */
bool enabled; /* false -> rxd: response */
int (*handler)(const ParsedCmd *cmd); /* 1=success, 0=failure */
} CmdEntry;
/*------------------------------------------------------------------------------
* Parser API
*----------------------------------------------------------------------------*/
/* Inject command table (called once at boot). */
void dr_parser_init(const CmdEntry *table, uint16_t count);
/* Entry point: invoked from BLE NUS / UART receive callbacks. */
int dr_cmd_parser(const uint8_t *buf, uint8_t len);
/*------------------------------------------------------------------------------
* Public utilities for handlers
*----------------------------------------------------------------------------*/
/* Extract Big-Endian uint16. Returns false if not enough data. */
bool dr_get_u16(const ParsedCmd *cmd, uint8_t word_index, uint16_t *out);
/* Extract null-terminated ASCII string. out must hold at least max_len+1. */
void dr_get_ascii(const ParsedCmd *cmd, uint8_t offset, char *out, uint8_t max_len);
/* CRC16 (Nordic SDK CRC-CCITT variant). p_crc=NULL -> initial 0xFFFF. */
uint16_t dr_crc16_compute(const uint8_t *p_data, uint32_t size, const uint16_t *p_crc);
#endif /* PARSER_H */
@@ -0,0 +1,33 @@
// file: debug_print.h
/*******************************************************************************
* [한국어 설명] 디버그 출력 매크로 (조건부 컴파일)
*
* SEGGER RTT(Real Time Transfer)를 이용한 디버그 출력 매크로.
* J-Link 디버거를 통해 실시간으로 로그를 PC에 전송한다.
* UART를 사용하지 않으므로 시스템 타이밍에 미치는 영향이 적다.
*
* === 조건부 컴파일 ===
* ENABLE_PRINTF = 1: DBG_PRINTF가 SEGGER_RTT_printf(채널0)로 치환됨
* -> 실제 로그 출력 (디버깅 시 사용)
* ENABLE_PRINTF = 0: DBG_PRINTF가 빈 매크로로 치환됨
* -> 코드에서 완전히 제거 (릴리스 빌드 시 사용)
*
* === 사용법 ===
* DBG_PRINTF("값: %d\r\n", value); // printf와 동일한 포맷 문자열
* 출력은 SEGGER RTT Viewer 또는 J-Link RTT Client에서 확인.
******************************************************************************/
#ifndef DEBUG_PRINT_H
#define DEBUG_PRINT_H
#define ENABLE_PRINTF 1 /* 1=디버그 출력 활성화, 0=전역 비활성화 */
#if ENABLE_PRINTF
#include "SEGGER_RTT.h"
/* SEGGER RTT 채널 0으로 포맷 문자열 출력 */
#define DBG_PRINTF(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#else
/* 빈 매크로: 컴파일러가 호출 코드를 완전히 제거 */
#define DBG_PRINTF(...) // Do nothing
#endif
#endif // DEBUG_PRINT_H
@@ -0,0 +1,496 @@
/*******************************************************************************
TEST medi50 Dec 23
******************************************************************************/
/**
* @file fstorage.c
* @brief FDS(Flash Data Storage) 기반 설정 저장 모듈
*
* 외부 EEPROM을 대체하여 nRF52840 내장 플래시에 장치 설정을 저장/로드한다.
*
* [레코드 관리]
* - CONFIG_FILE = 0x8010, CONFIG_REC_KEY = 0x7010 으로 단일 레코드를 관리한다.
*
* [config_data_t 구조체 필드]
* - magic(4B) : 포맷 확인용 매직 넘버 (0x20231226 -> 0x20260318(기본값 재생성을 위해 매직넘버 변경))
* - hw_no(12B) : 하드웨어 버전 (기본값: "")
* - serial_no(12B) : 시리얼 번호 (기본값: "VB026030000")
* - passkey(6B) : BLE 페어링용 정적 패스키(기본값: "123456")
* - bond_data_delete(1B) : 본딩 데이터 삭제 플래그(기본값: 1)
* - reset_status(1B) : 리셋 상태 값(기본값: 99)
* - life_cycle(4B) : 장치 사용 횟수(기본값: 0)
* - piezo_freq_option : Piezo 송신 펄스 주파수(기본값: 1=2.1MHz)
* - piezo_cycles : Piezo 송신 펄스 사이클 수 (기본값: 7)
* - piezo_averaging : Piezo 채널당 반복 측정 횟수 (기본값: 5)
* - piezo_delay_us : Piezo 송신 펄스 출력 후 ADC 시작 시까지 대기시간(us) (기본값: 10)
* - piezo_num_samples : Piezo 측정 ADC 샘플 개수 (기본값: 100)
*
* [매직 넘버 검증]
* - 플래시에서 로드한 데이터의 magic 값이 0x20231226과 일치하는지 확인하여
* 유효한 설정인지 판별한다. 불일치 시 기본값으로 초기화한다.
*
* [FDS 이벤트 후처리]
* - FDS 쓰기/업데이트 완료 이벤트 수신 후, 대기 중인 후처리를 수행한다:
* 전원 OFF (go_device_power_off), 슬립 진입 (go_sleep_mode_enter),
* 시스템 리셋 (go_NVIC_SystemReset)
*/
#include "sdk_config.h"
#include <string.h>
#include "app_error.h"
#include "boards.h"
#include "nrf_fstorage.h"
#include "nrf_soc.h"
#include "nrf_strerror.h"
#include "sdk_config.h"
#include "nrf_fstorage_sd.h"
#include "nrf_delay.h"
#include "ble_gap.h"
#include "fds.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "fstorage.h"
#include "nrf_pwr_mgmt.h"
#include "main.h"
#include "debug_print.h"
/* FDS 레코드 식별자: 파일 ID와 레코드 키로 단일 설정 레코드를 관리 */
#define CONFIG_FILE (0x8010)
#define CONFIG_REC_KEY (0x7010)
/* 매직 넘버: 플래시에 저장된 데이터가 유효한 설정인지 판별하는 데 사용 */
#define CONFIG_MAGIC_NUMBER_VALUE (0x20260319)
/* 전역 설정 데이터 구조체 인스턴스 */
config_data_t m_config;
/* FDS 쓰기 완료 후 수행할 후처리 플래그 (main.c에서 선언) */
extern bool go_device_power_off; /* 전원 OFF 요청 */
extern bool go_sleep_mode_enter; /* 슬립 모드 진입 요청 */
extern bool go_NVIC_SystemReset; /* 시스템 리셋 요청 */
/* FDS 초기화 완료 여부 플래그 (fds_evt_handler에서 true로 설정) */
static bool volatile m_fds_initialized;
/* FDS 쓰기 진행 중 플래그: true이면 쓰기 완료 대기 중 */
bool fds_flag_write = false;
/* FDS에 기록할 레코드 템플릿 (m_config 데이터를 가리킴) */
static fds_record_t const m_dummy_record =
{
.file_id = CONFIG_FILE,
.key = CONFIG_REC_KEY,
.data.p_data = (void const *)&m_config,
/* The length of a record is always expressed in 4-byte units (words). */
.data.length_words = (sizeof(m_config) + 3) / sizeof(uint32_t),
};
/* 기본 설정값 상수 */
int8_t reset_status_dflt = 99; /* 리셋 상태 기본값 */
uint8_t static_passkey_dflt[6] = DEFAULT_PASSKEY; /* BLE 패스키 기본값 */
/**
* @brief 기본 설정값 초기화
*
* m_config 구조체의 각 필드를 공장 초기값으로 설정한다.
* 플래시에 유효한 설정이 없거나 매직 넘버가 불일치할 때 호출된다.VB0HW0000
*/
void fds_default_value_set(void)
{
/* HW Number - default from HARDWARE_VERSION */
memset(m_config.hw_no, 0, 12);
memcpy(m_config.hw_no, HARDWARE_VERSION, strlen(HARDWARE_VERSION));
/* Serial Number - default from SERIAL_NUMBER */
memset(m_config.serial_no, 0, 12);
memcpy(m_config.serial_no, SERIAL_NUMBER, strlen(SERIAL_NUMBER));
/* Static Passkey */
memcpy(m_config.static_passkey, static_passkey_dflt, 6);
/* Bond data delete */
m_config.bond_data_delete = 1;
/* Reset status */
m_config.reset_status = reset_status_dflt;
/* Device usage count */
m_config.life_cycle = 0;
/* 피에조 측정 파라미터 기본값 */
m_config.piezo_freq_option = 1; /* 2.1MHz */
m_config.piezo_delay_us = 10; /* 버스트 후 10us */
m_config.piezo_num_samples = 100; /* 100샘플 */
m_config.piezo_cycles = 7; /* 7사이클 */
m_config.piezo_averaging = 3; /* 3회 평균화 */
}
/* 마지막 FDS 이벤트 ID 저장 (디버깅용) */
static volatile uint8_t fds_last_evt = 0xFF;
/**
* @brief FDS 이벤트 콜백 핸들러
*
* FDS 내부에서 비동기 작업이 완료될 때 호출된다.
* - FDS_EVT_INIT : FDS 초기화 완료 → m_fds_initialized 플래그 설정
* - FDS_EVT_WRITE : 새 레코드 쓰기 완료 → fds_flag_write 해제
* - FDS_EVT_UPDATE : 레코드 업데이트 완료 → fds_flag_write 해제 후
* 대기 중인 전원 OFF / 슬립 진입 / 시스템 리셋 수행
* - FDS_EVT_DEL_RECORD / FDS_EVT_DEL_FILE / FDS_EVT_GC : 현재 미사용
*/
static void fds_evt_handler( fds_evt_t const *p_evt )
{
fds_last_evt = p_evt->id;
switch( p_evt->id )
{
case FDS_EVT_INIT:
if( p_evt->result == NRF_SUCCESS )
{
m_fds_initialized = true;
}
break;
case FDS_EVT_WRITE:
{
fds_flag_write = false;
}
break;
case FDS_EVT_UPDATE:
{
fds_flag_write = false;
if(go_device_power_off == true) {
/* After flash writing completed, System Power Off */
device_power_off();
}
if(go_sleep_mode_enter == true) {
/* After flash writing completed, System go to Sleep Mode */
sleep_mode_enter();
}
if(go_NVIC_SystemReset == true) {
/* After flash writing completed, System Reset */
DBG_PRINTF("Off FDS_EVENT\r\n");
NVIC_SystemReset();
}
}
break;
case FDS_EVT_DEL_RECORD:
break;
case FDS_EVT_DEL_FILE:
break;
case FDS_EVT_GC:
break;
default:
break;
}
}
/**
* @brief FDS 초기화 완료 대기
*
* m_fds_initialized 플래그가 true가 될 때까지 대기한다.
* 최대 3초(3000ms) 타임아웃이 설정되어 있으며,
* 타임아웃 시 에러 로그를 출력하고 반환한다.
*/
static void wait_for_fds_ready( void )
{
uint32_t timeout = 0;
while( !m_fds_initialized )
{
nrf_pwr_mgmt_run();
nrf_delay_ms(1);
timeout++;
if (timeout > 3000) /* 3 second timeout */
{
DBG_PRINTF("[FDS] TIMEOUT!\r\n");
break;
}
}
}
/**
* @brief FDS에서 설정 로드
*
* 플래시에서 CONFIG_FILE/CONFIG_REC_KEY 레코드를 검색하여 m_config에 로드한다.
*
* 동작 흐름:
* 1. fds_record_find()로 레코드 검색 (실패 시 최대 10회 재시도, 100ms 간격)
* 2. 레코드 발견 시:
* - fds_record_open()으로 열기 (CRC 에러 시 삭제 후 기본값으로 재생성)
* - 데이터를 m_config로 복사
* - 매직 넘버 불일치 시 기존 레코드 삭제 → 기본값 설정 → 재기록
* 3. 레코드 미발견 시:
* - 기본값으로 새 레코드 생성 후 다시 로드
*/
void config_load( void )
{
ret_code_t rc;
fds_record_desc_t desc = { 0 };
fds_find_token_t tok = { 0 };
uint8_t cfg_retry = 0;
uint32_t fds_wait_cnt = 0; // FDS write 대기 카운터
cfg_load_start:
memset((char *)&desc, 0, sizeof(desc));
memset((char *)&tok, 0, sizeof(tok));
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
DBG_PRINTF("[FDS] find rc=%u\r\n", rc);
/* FDS may not be fully ready yet - retry before writing defaults */
if (rc != NRF_SUCCESS && cfg_retry < 10)
{
cfg_retry++;
DBG_PRINTF("[FDS] retry %u/10\r\n", cfg_retry);
nrf_delay_ms(100);
goto cfg_load_start;
}
if( rc == NRF_SUCCESS )
{
/* A config file is in flash. Let's update it. */
fds_flash_record_t config = { 0 };
/* Open the record and read its contents. */
rc = fds_record_open(&desc, &config);
if (rc != NRF_SUCCESS)
{
/* CRC error or corrupt record - delete and use defaults */
DBG_PRINTF("[FDS] open ERR=%u, deleting\r\n", rc);
(void)fds_record_delete(&desc);
fds_gc();
fds_default_value_set();
goto cfg_load_write_new;
}
/* Copy the configuration from flash into m_config. */
memcpy(&m_config, config.p_data, sizeof(config_data_t));
/* Close the record when done reading. */
rc = fds_record_close(&desc);
APP_ERROR_CHECK(rc);
DBG_PRINTF("[FDS] magic=0x%08X (expect 0x%08X)\r\n", m_config.magic_number, CONFIG_MAGIC_NUMBER_VALUE);
if( m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE )
{ // first init
DBG_PRINTF("[FDS] FORMAT! overwriting with defaults\r\n");
rc = fds_record_delete(&desc);
APP_ERROR_CHECK(rc);
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
// default....
fds_default_value_set();
/* Write the updated record to flash. */
rc = fds_record_update(&desc, &m_dummy_record);
if( (rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH) )
{
rc = fds_gc();
APP_ERROR_CHECK(rc);
}
else
{
APP_ERROR_CHECK(rc);
}
goto cfg_load_start;
}
DBG_PRINTF("[FDS] Loaded OK\r\n");
}
else
{
cfg_load_write_new:
DBG_PRINTF("[FDS] New - writing defaults\r\n");
/* System config not found (or corrupt); write a new one. */
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
// default....
fds_default_value_set();
fds_flag_write = true;
rc = fds_record_write(&desc, &m_dummy_record);
if (rc != NRF_SUCCESS)
{
DBG_PRINTF("[FDS] Write ERR=%u\r\n", rc);
fds_flag_write = false;
}
fds_wait_cnt = 0; //
while(fds_flag_write && fds_wait_cnt < 3000) // FDS write 최대 3초 타임아웃
{
nrf_pwr_mgmt_run();
nrf_delay_ms(1);
fds_wait_cnt++;
}
if(fds_flag_write) // FDS write 타임아웃 시 플래그 강제 해제
{
DBG_PRINTF("[FDS] write TIMEOUT! forcing flag clear\r\n");
fds_flag_write = false;
}
if( (rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH) )
{
rc = fds_gc();
APP_ERROR_CHECK(rc);
}
else
{
APP_ERROR_CHECK(rc);
}
NRF_LOG_FLUSH();
goto cfg_load_start;
}
}
/**
* @brief 현재 설정을 FDS에 저장
*
* m_config의 내용을 플래시에 기록한다.
*
* 동작 흐름:
* 1. 이전 FDS 쓰기 작업이 진행 중이면 최대 3초 대기
* 2. 매직 넘버가 올바르지 않으면 보정
* 3. 기존 레코드가 있으면 fds_record_update()로 갱신
* - 플래시 공간 부족 시 GC(가비지 컬렉션) 수행 후 재시도
* 4. 기존 레코드가 없으면 fds_record_write()로 새로 생성
*
* 참고: 쓰기 완료는 fds_evt_handler()에서 비동기로 처리되며,
* 완료 후 전원 OFF/슬립/리셋 등의 후처리가 수행될 수 있다.
*/
void config_save( void )
{
ret_code_t rc;
fds_record_desc_t desc = { 0 };
fds_find_token_t tok = { 0 };
DBG_PRINTF("[CFG_SAVE] start\r\n");
/* Wait for any previous FDS operation to complete */
if (fds_flag_write)
{
uint32_t wait_cnt = 0;
DBG_PRINTF("[CFG_SAVE] waiting for prev FDS op...\r\n");
while (fds_flag_write && wait_cnt < 3000)
{
nrf_pwr_mgmt_run();
nrf_delay_ms(1);
wait_cnt++;
}
if (fds_flag_write)
{
DBG_PRINTF("[CFG_SAVE] TIMEOUT! forcing flag clear\r\n");
fds_flag_write = false;
}
}
if( m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE )
{
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
}
memset((char *)&desc, 0, sizeof(desc));
memset((char *)&tok, 0, sizeof(tok));
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
DBG_PRINTF("[CFG_SAVE] find rc=%u\r\n", rc);
if( rc == NRF_SUCCESS )
{
fds_flag_write = true;
rc = fds_record_update(&desc, &m_dummy_record);
DBG_PRINTF("[CFG_SAVE] update rc=%u\r\n", rc);
if( rc == FDS_ERR_NO_SPACE_IN_FLASH )
{
fds_flag_write = false;
rc = fds_gc();
DBG_PRINTF("[CFG_SAVE] gc rc=%u, retry\r\n", rc);
fds_flag_write = true;
rc = fds_record_update(&desc, &m_dummy_record);
DBG_PRINTF("[CFG_SAVE] retry rc=%u\r\n", rc);
}
if( rc != NRF_SUCCESS )
{
DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc);
fds_flag_write = false;
}
}
else
{
DBG_PRINTF("[CFG_SAVE] not found, writing new\r\n");
fds_flag_write = true;
rc = fds_record_write(&desc, &m_dummy_record);
DBG_PRINTF("[CFG_SAVE] write rc=%u\r\n", rc);
if( rc != NRF_SUCCESS )
{
DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc);
fds_flag_write = false;
}
}
DBG_PRINTF("[CFG_SAVE] done\r\n");
}
/**
* @brief config_load()의 래퍼 함수
*
* 외부 모듈에서 설정 로드를 요청할 때 사용한다.
*/
void fs_set_value(void)
{
config_load();
}
/**
* @brief FDS 초기화
*
* 부팅 시 호출되어 FDS 모듈을 초기화한다.
* 1. fds_register()로 이벤트 핸들러 등록
* 2. fds_init()로 FDS 초기화 시작
* 3. wait_for_fds_ready()로 초기화 완료 대기 (최대 3초)
* 4. fds_stat()로 플래시 상태 확인
*/
void fs_storage_init(void)
{
ret_code_t rc;
/* Register first to receive an event when initialization is complete. */
rc = fds_register(fds_evt_handler);
APP_ERROR_CHECK(rc);
rc = fds_init();
APP_ERROR_CHECK(rc);
/* Wait for fds to initialize. */
wait_for_fds_ready();
fds_stat_t stat = { 0 };
rc = fds_stat(&stat);
APP_ERROR_CHECK(rc);
DBG_PRINTF("[FDS] OK\r\n");
}
@@ -0,0 +1,87 @@
/*******************************************************************************
* @file fstorage.h
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief FDS(Flash Data Storage) 기반 설정 저장 모듈 인터페이스
*******************************************************************************
*
* [헤더 개요]
* nRF52840 내장 플래시에 디바이스 설정을 저장/로드하는 FDS 모듈의 공용 API.
* 외부 EEPROM을 대체하며, SoftDevice와 공존하여 플래시를 안전하게 관리한다.
*
* [config_data_t 구조체] (45바이트, 패킹됨)
* magic_number(4B): 포맷 확인용 (0x20231226)
* hw_no(12B): 하드웨어 번호
* serial_no(12B): 시리얼 번호 (BLE 디바이스 이름으로도 사용)
* static_passkey(6B): BLE 페어링 패스키 (숫자 6자리)
* bond_data_delete(1B): 본딩 삭제 플래그
* reset_status(1B): 리셋 상태 코드
* pd_adc_cnt(1B): ADC 샘플링 횟수
* pd_delay_us(2B): PD 안정화 딜레이 (마이크로초)
* life_cycle(4B): 디바이스 사용 횟수
*
* [주요 API]
* fs_storage_init(): FDS 초기화 (부트 시 1회)
* config_load(): FDS에서 설정 로드 (없으면 기본값 생성)
* config_save(): 현재 설정을 FDS에 저장
*
******************************************************************************/
#ifndef IHP_FSTORAGE_H_
#define IHP_FSTORAGE_H_
#include "sdk_config.h"
#include "nordic_common.h"
#include <stdint.h>
/* -------------------------------------------------------------------------
* 기본 버전 정보 (FDS 기본값 및 빈 필드 복구 시 사용)
*
* 하드웨어 식별 코드
* - VBTHW0100 = 개발(시험)용 Ver 1.00
* - VB0HW0100 = 양산용 Ver 1.00
*
* Firmware 식별 코드
* - VBTFW0100 = 개발(시험)용 Ver 1.00
* - VB0FW0100 = 양산용 Ver 1.00
*
* 시리얼 넘버 식별 코드
* - VBT26030001 = 개발(시험)용 26년 3월 생산 1번
* - VB026030001 = 양산용 26년 3월 생산 1번
------------------------------------------------------------------------- */
#define HARDWARE_VERSION "VBTHW0100"
#define SERIAL_NUMBER "VBT26030001"
#define DEFAULT_PASSKEY "123456"
#pragma pack(1)
typedef struct
{
uint32_t magic_number; /* 4B - 포맷 확인용 매직 넘버 */
char hw_no[12]; /* 12B - HW Version */
char serial_no[12]; /* 12B - Serial Number */
uint8_t static_passkey[6]; /* 6B - BLE Passkey */
uint8_t bond_data_delete; /* 1B - Bond delete flag */
int8_t reset_status; /* 1B - Reset status */
uint32_t life_cycle; /* 4B - Device usage count */
/* Piezo 측정 파라미터 - 8B */
uint8_t piezo_freq_option; /* 1B - Frequency : 송신 펄스 주파수 (0=1.8M, 1=2.1M, 2=2.0M, 3=1.7M) */
uint8_t piezo_cycles; /* 1B - Burst Cycle : 송신 펄스 사이클 수 (3~7) */
uint16_t piezo_averaging; /* 2B - 평균화 수 : 채널당 반복 측정 횟수 (1~10) */
uint16_t piezo_delay_us; /* 2B - 대기 시간(Delay) : 송신 펄스 출력 후 ADC 시작 시까지 대기시간 (us) (0~30) */
uint16_t piezo_num_samples; /* 2B - 측정 ADC 샘플 개수 (80~140) */
} config_data_t; /* Total: 48 bytes - FDS에 저장하는 디바이스 설정 */
extern config_data_t m_config;
void fds_default_value_set(void);
void config_load( void );
void config_save( void );
void fs_set_value(void);
void fs_storage_init(void);
#endif /* IHP_FSTORAGE_H_ */
@@ -1,456 +0,0 @@
/*==============================================================================
* fstorage.c - FDS (Flash Data Storage) configuration module
*
* Stores and loads device configuration to/from nRF52840 internal flash,
* replacing external EEPROM. Coexists safely with the SoftDevice.
*
* Record management:
* CONFIG_FILE = 0x8010, CONFIG_REC_KEY = 0x7010 (single record)
*
* Magic number validation:
* Data loaded from flash is checked against CONFIG_MAGIC_NUMBER_VALUE.
* Mismatch -> reinitialise with factory defaults.
*
* FDS event post-processing:
* After a write/update completes, pending actions are executed:
* power-off (go_device_power_off), sleep (go_sleep_mode_enter),
* system reset (go_NVIC_SystemReset).
*============================================================================*/
#include "sdk_config.h"
#include <string.h>
#include "app_error.h"
#include "boards.h"
#include "nrf_fstorage.h"
#include "nrf_soc.h"
#include "nrf_strerror.h"
#include "sdk_config.h"
#include "nrf_fstorage_sd.h"
#include "nrf_delay.h"
#include "ble_gap.h"
#include "fds.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "fstorage.h"
#include "nrf_pwr_mgmt.h"
#include "main.h"
#include "debug_print.h"
/* FDS record identifiers */
#define CONFIG_FILE (0x8010)
#define CONFIG_REC_KEY (0x7010)
/* Magic number used to validate stored data */
#define CONFIG_MAGIC_NUMBER_VALUE (0x20260319)
/* Global configuration instance */
config_data_t m_config;
/* Post-processing flags (declared in main.c) */
extern bool go_device_power_off;
extern bool go_sleep_mode_enter;
extern bool go_NVIC_SystemReset;
/* FDS initialisation complete flag (set in fds_evt_handler) */
static bool volatile m_fds_initialized;
/* FDS write-in-progress flag */
bool fds_flag_write = false;
/* FDS record template pointing to m_config */
static fds_record_t const m_dummy_record =
{
.file_id = CONFIG_FILE,
.key = CONFIG_REC_KEY,
.data.p_data = (void const *)&m_config,
/* The length of a record is always expressed in 4-byte units (words). */
.data.length_words = (sizeof(m_config) + 3) / sizeof(uint32_t),
};
/* Default values */
int8_t reset_status_dflt = 99;
uint8_t static_passkey_dflt[6] = DEFAULT_PASSKEY;
/*==============================================================================
* fds_default_value_set - Initialise m_config with factory defaults
*
* Called when flash contains no valid configuration or the magic number
* does not match.
*============================================================================*/
void fds_default_value_set(void)
{
/* HW number */
memset(m_config.hw_no, 0, 12);
memcpy(m_config.hw_no, HARDWARE_VERSION, strlen(HARDWARE_VERSION));
/* Serial number */
memset(m_config.serial_no, 0, 12);
memcpy(m_config.serial_no, SERIAL_NUMBER, strlen(SERIAL_NUMBER));
/* Static passkey */
memcpy(m_config.static_passkey, static_passkey_dflt, 6);
/* Bond delete — default: no pending delete */
m_config.bond_data_delete = 0;
/* Reset status */
m_config.reset_status = reset_status_dflt;
/* Device usage count */
m_config.life_cycle = 0;
/* Piezo measurement parameter defaults */
m_config.piezo_freq_option = 1; /* 2.1 MHz */
m_config.piezo_delay_us = 10; /* 10 us after burst */
m_config.piezo_num_samples = 100; /* 100 samples */
m_config.piezo_cycles = 7; /* 7 cycles */
m_config.piezo_averaging = 3; /* 3x averaging */
/* Factory provisioning — default: not provisioned */
m_config.factory_provisioned = 0;
}
/* Last FDS event ID (for debugging) */
static volatile uint8_t fds_last_evt = 0xFF;
/*==============================================================================
* fds_evt_handler - FDS event callback
*
* FDS_EVT_INIT : initialisation complete -> set m_fds_initialized
* FDS_EVT_WRITE : new record written -> clear fds_flag_write
* FDS_EVT_UPDATE : record updated -> clear flag, then execute any
* pending power-off / sleep / system reset
*============================================================================*/
static void fds_evt_handler( fds_evt_t const *p_evt )
{
fds_last_evt = p_evt->id;
switch (p_evt->id)
{
case FDS_EVT_INIT:
if (p_evt->result == NRF_SUCCESS)
{
m_fds_initialized = true;
}
break;
case FDS_EVT_WRITE:
fds_flag_write = false;
break;
case FDS_EVT_UPDATE:
fds_flag_write = false;
if (go_device_power_off == true)
{
device_power_off();
}
if (go_sleep_mode_enter == true)
{
sleep_mode_enter();
}
if (go_NVIC_SystemReset == true)
{
DBG_PRINTF("Off FDS_EVENT\r\n");
NVIC_SystemReset();
}
break;
case FDS_EVT_DEL_RECORD:
break;
case FDS_EVT_DEL_FILE:
break;
case FDS_EVT_GC:
break;
default:
break;
}
}
/*==============================================================================
* wait_for_fds_ready - Block until FDS initialisation completes
*
* Times out after 3 seconds with an error log.
*============================================================================*/
static void wait_for_fds_ready( void )
{
uint32_t timeout = 0;
while(!m_fds_initialized)
{
nrf_pwr_mgmt_run();
nrf_delay_ms(1);
timeout++;
if (timeout > 3000)
{
DBG_PRINTF("[FDS] TIMEOUT!\r\n");
break;
}
}
}
/*==============================================================================
* config_load - Load configuration from FDS
*
* Flow:
* 1. Search for CONFIG_FILE / CONFIG_REC_KEY (retry up to 10x, 100 ms apart)
* 2. If found:
* - Open the record (on CRC error: delete and regenerate defaults)
* - Copy into m_config
* - Validate magic number; on mismatch: delete -> defaults -> rewrite
* 3. If not found:
* - Write factory defaults as a new record, then reload
*============================================================================*/
void config_load( void )
{
ret_code_t rc;
fds_record_desc_t desc = { 0 };
fds_find_token_t tok = { 0 };
uint8_t cfg_retry = 0;
uint32_t fds_wait_cnt = 0;
cfg_load_start:
memset((char *)&desc, 0, sizeof(desc));
memset((char *)&tok, 0, sizeof(tok));
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
DBG_PRINTF("[FDS] find rc=%u\r\n", rc);
/* FDS may not be fully ready yet - retry before writing defaults */
if (rc != NRF_SUCCESS && cfg_retry < 10)
{
cfg_retry++;
DBG_PRINTF("[FDS] retry %u/10\r\n", cfg_retry);
nrf_delay_ms(100);
goto cfg_load_start;
}
if (rc == NRF_SUCCESS)
{
fds_flash_record_t config = { 0 };
rc = fds_record_open(&desc, &config);
if (rc != NRF_SUCCESS)
{
/* CRC error or corrupt record - delete and use defaults */
DBG_PRINTF("[FDS] open ERR=%u, deleting\r\n", rc);
(void)fds_record_delete(&desc);
fds_gc();
fds_default_value_set();
goto cfg_load_write_new;
}
memcpy(&m_config, config.p_data, sizeof(config_data_t));
rc = fds_record_close(&desc);
APP_ERROR_CHECK(rc);
DBG_PRINTF("[FDS] magic=0x%08X (expect 0x%08X)\r\n", m_config.magic_number, CONFIG_MAGIC_NUMBER_VALUE);
if (m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE)
{
DBG_PRINTF("[FDS] FORMAT! overwriting with defaults\r\n");
rc = fds_record_delete(&desc);
APP_ERROR_CHECK(rc);
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
fds_default_value_set();
rc = fds_record_update(&desc, &m_dummy_record);
if ((rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH))
{
rc = fds_gc();
APP_ERROR_CHECK(rc);
}
else
{
APP_ERROR_CHECK(rc);
}
goto cfg_load_start;
}
DBG_PRINTF("[FDS] Loaded OK\r\n");
}
else
{
cfg_load_write_new:
DBG_PRINTF("[FDS] New - writing defaults\r\n");
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
fds_default_value_set();
fds_flag_write = true;
rc = fds_record_write(&desc, &m_dummy_record);
if (rc != NRF_SUCCESS)
{
DBG_PRINTF("[FDS] Write ERR=%u\r\n", rc);
fds_flag_write = false;
}
fds_wait_cnt = 0;
while (fds_flag_write && fds_wait_cnt < 3000) /* 3 second timeout */
{
nrf_pwr_mgmt_run();
nrf_delay_ms(1);
fds_wait_cnt++;
}
if (fds_flag_write)
{
DBG_PRINTF("[FDS] write TIMEOUT! forcing flag clear\r\n");
fds_flag_write = false;
}
if ((rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH))
{
rc = fds_gc();
APP_ERROR_CHECK(rc);
}
else
{
APP_ERROR_CHECK(rc);
}
NRF_LOG_FLUSH();
goto cfg_load_start;
}
}
/*==============================================================================
* config_save - Persist current configuration to FDS
*
* Flow:
* 1. If a previous FDS write is in progress, wait up to 3 seconds
* 2. Fix magic number if needed
* 3. If existing record found: fds_record_update()
* - On no-space: GC then retry
* 4. If not found: fds_record_write() (new record)
*
* Write completion is asynchronous (fds_evt_handler). Post-processing
* (power-off / sleep / reset) may follow.
*============================================================================*/
void config_save( void )
{
ret_code_t rc;
fds_record_desc_t desc = { 0 };
fds_find_token_t tok = { 0 };
DBG_PRINTF("[CFG_SAVE] start\r\n");
/* Wait for any previous FDS operation to complete */
if (fds_flag_write)
{
uint32_t wait_cnt = 0;
DBG_PRINTF("[CFG_SAVE] waiting for prev FDS op...\r\n");
while (fds_flag_write && wait_cnt < 3000)
{
nrf_pwr_mgmt_run();
nrf_delay_ms(1);
wait_cnt++;
}
if (fds_flag_write)
{
DBG_PRINTF("[CFG_SAVE] TIMEOUT! forcing flag clear\r\n");
fds_flag_write = false;
}
}
if (m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE)
{
m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE;
}
memset((char *)&desc, 0, sizeof(desc));
memset((char *)&tok, 0, sizeof(tok));
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
DBG_PRINTF("[CFG_SAVE] find rc=%u\r\n", rc);
if (rc == NRF_SUCCESS)
{
fds_flag_write = true;
rc = fds_record_update(&desc, &m_dummy_record);
DBG_PRINTF("[CFG_SAVE] update rc=%u\r\n", rc);
if (rc == FDS_ERR_NO_SPACE_IN_FLASH)
{
fds_flag_write = false;
rc = fds_gc();
DBG_PRINTF("[CFG_SAVE] gc rc=%u, retry\r\n", rc);
fds_flag_write = true;
rc = fds_record_update(&desc, &m_dummy_record);
DBG_PRINTF("[CFG_SAVE] retry rc=%u\r\n", rc);
}
if (rc != NRF_SUCCESS)
{
DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc);
fds_flag_write = false;
}
}
else
{
DBG_PRINTF("[CFG_SAVE] not found, writing new\r\n");
fds_flag_write = true;
rc = fds_record_write(&desc, &m_dummy_record);
DBG_PRINTF("[CFG_SAVE] write rc=%u\r\n", rc);
if ( rc != NRF_SUCCESS )
{
DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc);
fds_flag_write = false;
}
}
DBG_PRINTF("[CFG_SAVE] done\r\n");
}
/*==============================================================================
* fs_set_value - Wrapper for config_load()
*============================================================================*/
void fs_set_value(void)
{
config_load();
}
/*==============================================================================
* fs_storage_init - Initialise FDS
*
* Called once at boot:
* 1. Register event handler
* 2. Start FDS initialisation
* 3. Wait for completion (up to 3 seconds)
* 4. Verify flash stats
*============================================================================*/
void fs_storage_init(void)
{
ret_code_t rc;
rc = fds_register(fds_evt_handler);
APP_ERROR_CHECK(rc);
rc = fds_init();
APP_ERROR_CHECK(rc);
wait_for_fds_ready();
fds_stat_t stat = { 0 };
rc = fds_stat(&stat);
APP_ERROR_CHECK(rc);
DBG_PRINTF("[FDS] OK\r\n");
}
@@ -1,83 +0,0 @@
/*==============================================================================
* fstorage.h - FDS (Flash Data Storage) configuration module interface
*
* Stores and loads device configuration to/from the nRF52840 internal flash
* via the Nordic FDS library. Replaces external EEPROM and coexists safely
* with the SoftDevice.
*
* config_data_t (49 bytes, packed):
* magic_number (4B) : format validation (0x20231226)
* hw_no (12B): hardware version string
* serial_no (12B): serial number (also used as BLE device name)
* static_passkey(6B) : BLE pairing passkey (6-digit numeric)
* bond_data_delete(1B): bond-delete flag
* reset_status (1B) : reset cause code
* life_cycle (4B) : device usage count
* piezo_* (8B) : piezo measurement parameters
* factory_provisioned(1B): passkey provisioning lock flag
*
* API:
* fs_storage_init() : initialise FDS (once at boot)
* config_load() : load config from FDS (creates defaults if absent)
* config_save() : persist current config to FDS
*============================================================================*/
#ifndef IHP_FSTORAGE_H_
#define IHP_FSTORAGE_H_
#include "sdk_config.h"
#include "nordic_common.h"
#include <stdint.h>
/*------------------------------------------------------------------------------
* Default version identifiers (used for FDS defaults / empty field recovery)
*
* Hardware ID:
* VBTHW0100 = development / test Ver 1.00
* VB0HW0100 = production Ver 1.00
*
* Firmware ID:
* VBTFW0100 = development / test Ver 1.00
* VB0FW0100 = production Ver 1.00
*
* Serial number:
* VBT26030001 = dev/test, manufactured Mar 2026, unit #1
* VB026030001 = production, Mar 2026, unit #1
*----------------------------------------------------------------------------*/
#define HARDWARE_VERSION "VBTHW0100"
#define SERIAL_NUMBER "VBT26030001"
#define DEFAULT_PASSKEY "123456"
#pragma pack(1)
typedef struct
{
uint32_t magic_number; /* 4B - format validation magic */
char hw_no[12]; /* 12B - HW version */
char serial_no[12]; /* 12B - serial number */
uint8_t static_passkey[6]; /* 6B - BLE passkey */
uint8_t bond_data_delete; /* 1B - bond delete flag */
int8_t reset_status; /* 1B - reset status */
uint32_t life_cycle; /* 4B - device usage count */
/* Piezo measurement parameters - 8B */
uint8_t piezo_freq_option; /* 1B - TX pulse frequency (0=1.8M, 1=2.1M, 2=2.0M, 3=1.7M) */
uint8_t piezo_cycles; /* 1B - burst pulse cycle count (3..7) */
uint16_t piezo_averaging; /* 2B - averages per channel (1..10) */
uint16_t piezo_delay_us; /* 2B - delay from TX pulse to ADC start (us) (0..30) */
uint16_t piezo_num_samples; /* 2B - ADC sample count (80..140) */
/* Factory provisioning lock */
uint8_t factory_provisioned; /* 1B - 0=passkey not set, 1=passkey set (locked) */
} config_data_t; /* Total: 49 bytes */
extern config_data_t m_config;
void fds_default_value_set(void);
void config_load( void );
void config_save( void );
void fs_set_value(void);
void fs_storage_init(void);
#endif /* IHP_FSTORAGE_H_ */
@@ -1,125 +0,0 @@
/*==============================================================================
* i2c_manager.c - HW / SW I2C mutex switching logic
*
* Manages mutually-exclusive HW (TWI peripheral) and SW (bit-bang) I2C modes.
*
* HW I2C : nRF52840 TWI hardware, 400 kHz Fast Mode (ICM42670P IMU)
* SW I2C : GPIO bit-bang (legacy, currently unused)
*
* Pins:
* SCL = P1.14 (ICM42670_I2C_SCL_PIN)
* SDA = P1.15 (ICM42670_I2C_SDA_PIN)
*
* The two bool flags HW_I2C_FRQ and SW_I2C_FRQ track the current mode.
* Switching releases the old mode's resources before initialising the new one.
*============================================================================*/
#include "i2c_manager.h"
#include "debug_print.h"
#include "nrf_delay.h"
#include "nrf_drv_twi.h"
#include "nrfx_twi.h"
#include "boards.h"
#include "system_interface.h"
/* Current I2C mode flags */
bool HW_I2C_FRQ = true;
bool SW_I2C_FRQ = false;
/* TWI instance (nRF52840 supports TWI0 and TWI1) */
#define TWI_INSTANCE 0
const nrfx_twi_t m_twi = NRFX_TWI_INSTANCE(TWI_INSTANCE);
/* Disable and uninitialise the TWI peripheral, releasing GPIO pins. */
static void twi_uninitialize(void)
{
nrfx_twi_disable(&m_twi);
nrfx_twi_uninit(&m_twi);
}
/* Initialise the TWI peripheral (SCL/SDA pins, 400 kHz, blocking mode). */
static void twi_initialize(void)
{
ret_code_t err_code;
const nrfx_twi_config_t twi_config =
{
.scl = ICM42670_I2C_SCL_PIN,
.sda = ICM42670_I2C_SDA_PIN,
.frequency = NRF_TWI_FREQ_400K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
};
err_code = nrfx_twi_init(&m_twi, &twi_config, NULL, NULL);
APP_ERROR_CHECK(err_code);
nrfx_twi_enable(&m_twi);
}
/*==============================================================================
* hw_i2c_init_once - Switch to or initialise HW TWI mode
*
* If SW mode is active, its flag is cleared first.
* If HW mode is already active, returns immediately (no re-init).
*============================================================================*/
void hw_i2c_init_once(void)
{
if (SW_I2C_FRQ)
{
SW_I2C_FRQ = false;
nrf_delay_ms(2); /* mode-switch settling */
}
if (HW_I2C_FRQ)
{
return;
}
twi_initialize();
nrf_delay_ms(2);
HW_I2C_FRQ = true;
SW_I2C_FRQ = false;
}
/*==============================================================================
* sw_i2c_init_once - Switch to SW bit-bang mode (legacy, unused)
*
* If HW mode is active, TWI is released first.
* If SW mode is already active, returns immediately.
*============================================================================*/
void sw_i2c_init_once(void)
{
if (HW_I2C_FRQ)
{
nrfx_twi_disable(&m_twi);
nrfx_twi_uninit(&m_twi);
nrf_delay_ms(2);
HW_I2C_FRQ = false;
}
if (SW_I2C_FRQ)
{
return;
}
twi_uninitialize();
nrf_delay_ms(1);
SW_I2C_FRQ = true;
HW_I2C_FRQ = false;
}
/*==============================================================================
* i2c_reset_state - Clear all mode flags
*
* Forces re-initialisation on the next init call. Used for system reset
* or error recovery.
*============================================================================*/
void i2c_reset_state(void)
{
HW_I2C_FRQ = false;
SW_I2C_FRQ = false;
DBG_PRINTF("Flags reset\r\n");
}
@@ -1,31 +0,0 @@
/*==============================================================================
* i2c_manager.h - HW / SW I2C mutex control
*
* Manages the mutually-exclusive HW TWI and SW bit-bang I2C modes.
* Only one mode may be active at a time.
*
* Flags:
* HW_I2C_FRQ : true when HW TWI mode is active
* SW_I2C_FRQ : true when SW bit-bang mode is active
*============================================================================*/
#ifndef __I2C_MANAGER_H__
#define __I2C_MANAGER_H__
#include <stdbool.h>
#include "app_error.h"
extern bool HW_I2C_FRQ;
extern bool SW_I2C_FRQ;
/* Initialise HW I2C (TWI) mode. If SW mode is active it is released first.
* No-op if HW mode is already active. Call before ICM42670P IMU access. */
void hw_i2c_init_once(void);
/* Initialise SW I2C (bit-bang) mode (legacy, currently unused).
* If HW mode is active it is released first. */
void sw_i2c_init_once(void);
/* Reset both mode flags to false, forcing re-initialisation on next call. */
void i2c_reset_state(void);
#endif
@@ -0,0 +1,22 @@
set CURDIR=%cd%
copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex
copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex
nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex
mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex
mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex
nrfjprog --family NRF52 --recover
nrfjprog --family NRF52 --eraseall
nrfjprog --family NRF52 --program medithings_bladder_patch_dfu_merged_all.hex
nrfjprog --family NRF52 --reset
nrfjprog --family NRF52 --rbp ALL
pause
@@ -0,0 +1,21 @@
set CURDIR=%cd%
copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex
copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex
nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex
mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex
mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex
nrfjprog --family NRF52 --recover
nrfjprog --family NRF52 --eraseall
nrfjprog --family NRF52 --program medithings_bladder_patch_dfu_merged_all.hex
pause
@@ -0,0 +1,22 @@
set CURDIR=%cd%
copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex
copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex
nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex
mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex
mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex
nrfjprog --family NRF52 --eraseall
nrfjprog --family NRF52 --program medithings_bladder_patch_dfu_merged_all.hex
nrfjprog --family NRF52 --reset
nrfjprog --family NRF52 --rbp ALL
pause
@@ -1,30 +1,30 @@
set CURDIR=%cd%
copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex
copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex
nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex
mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex
mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex
nrfjprog --memrd 0x10001208
nrfjprog --family NRF52 --recover
nrfjprog --memrd 0x10001208
nrfjprog --family NRF52 --eraseall
nrfjprog --family NRF52 --program medithings_bladder_patch_dfu_merged_all.hex --verify
nrfjprog --family NRF52 --reset
nrfjprog --family NRF52 --rbp ALL
nrfjprog --memwr 0x10001208 --val 0x00
nrfjprog --memrd 0x10001208
set CURDIR=%cd%
copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex
copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex
nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex
mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex
mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex
nrfjprog --memrd 0x10001208
nrfjprog --family NRF52 --recover
nrfjprog --memrd 0x10001208
nrfjprog --family NRF52 --eraseall
nrfjprog --family NRF52 --program medithings_bladder_patch_dfu_merged_all.hex --verify
nrfjprog --family NRF52 --reset
nrfjprog --family NRF52 --rbp ALL
nrfjprog --memwr 0x10001208 --val 0x00
nrfjprog --memrd 0x10001208
pause
@@ -0,0 +1,20 @@
set CURDIR=%cd%
copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex
copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex
nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex
mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex
mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex
nrfjprog --family NRF52 --program medithings_bladder_patch_0001.hex
nrfjprog --family NRF52 --reset
pause
@@ -1,137 +1,137 @@
@echo off
setlocal enabledelayedexpansion
echo ==========================================
echo MEDiThings Bladder Patch Programming
echo (mergehex + nrfutil 8.x)
echo ==========================================
REM -----------------------------------------------------
REM Create hex output folder
REM -----------------------------------------------------
if not exist hex mkdir hex
REM -----------------------------------------------------
REM 1. Copy HEX files
REM -----------------------------------------------------
echo [1/7] Copying HEX files...
copy "..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex" hex\app.hex
copy "..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex" hex\boot.hex
if %errorlevel% neq 0 (
echo ERROR: failed copying files.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 2. Generate Bootloader DFU Settings
REM -----------------------------------------------------
echo [2/7] Generating Bootloader DFU settings...
nrfutil settings generate ^
--family NRF52840 ^
--application hex\app.hex ^
--application-version 1 ^
--bootloader-version 1 ^
--bl-settings-version 2 ^
hex\settings.hex
if %errorlevel% neq 0 (
echo ERROR: nrfutil settings failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 3. Merge HEX (SoftDevice + Application)
REM -----------------------------------------------------
echo [3/7] Merging SoftDevice + Application...
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex hex\app.hex --output hex\part1.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex part1 failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 4. Merge HEX (Bootloader + Settings)
REM -----------------------------------------------------
echo [4/7] Merging Bootloader + Settings...
mergehex.exe --merge hex\boot.hex hex\settings.hex --output hex\part2.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex part2 failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 5. Final HEX merge (Combine everything)
REM -----------------------------------------------------
echo [5/7] Creating final combined HEX...
mergehex.exe --merge hex\part1.hex hex\part2.hex --output hex\firmware_all.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex final merge failed.
pause
exit /b 1
)
echo Final merged HEX: hex\firmware_all.hex
REM -----------------------------------------------------
REM 6. Detect device SERIAL NUMBER
REM -----------------------------------------------------
echo [6/7] Detecting device serial number...
for /f %%A in ('
powershell -Command "(nrfutil device list --json | Select-String '\"type\":\"info\"' | ConvertFrom-Json).data.devices[0].serialNumber"
') do set SERIALNUMBER=%%A
if "%SERIALNUMBER%"=="" (
echo ERROR: No serial number found.
pause
exit /b 1
)
echo Using Serial Number: %SERIALNUMBER%
echo Flashing: recover → erase → program → reset
REM recover
nrfutil device recover --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
REM erase
nrfutil device erase --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
REM program
nrfutil device program --firmware hex\firmware_all.hex --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
REM reset
nrfutil device reset --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
goto flash_success
:flash_fail
echo ERROR: Flashing failed.
pause
exit /b 1
:flash_success
REM -----------------------------------------------------
REM 7. Set Readback Protection (RBP)
REM -----------------------------------------------------
echo [7/7] Setting Readback Protection (ALL)...
nrfutil device protection-set ALL --serial-number %SERIALNUMBER%
echo ==========================================
echo Programming Complete!
echo %date% %time%
echo ==========================================
pause
endlocal
@echo off
setlocal enabledelayedexpansion
echo ==========================================
echo MEDiThings Bladder Patch Programming
echo (mergehex + nrfutil 8.x)
echo ==========================================
REM -----------------------------------------------------
REM Create hex output folder
REM -----------------------------------------------------
if not exist hex mkdir hex
REM -----------------------------------------------------
REM 1. Copy HEX files
REM -----------------------------------------------------
echo [1/7] Copying HEX files...
copy "..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex" hex\app.hex
copy "..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex" hex\boot.hex
if %errorlevel% neq 0 (
echo ERROR: failed copying files.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 2. Generate Bootloader DFU Settings
REM -----------------------------------------------------
echo [2/7] Generating Bootloader DFU settings...
nrfutil settings generate ^
--family NRF52840 ^
--application hex\app.hex ^
--application-version 1 ^
--bootloader-version 1 ^
--bl-settings-version 2 ^
hex\settings.hex
if %errorlevel% neq 0 (
echo ERROR: nrfutil settings failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 3. Merge HEX (SoftDevice + Application)
REM -----------------------------------------------------
echo [3/7] Merging SoftDevice + Application...
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex hex\app.hex --output hex\part1.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex part1 failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 4. Merge HEX (Bootloader + Settings)
REM -----------------------------------------------------
echo [4/7] Merging Bootloader + Settings...
mergehex.exe --merge hex\boot.hex hex\settings.hex --output hex\part2.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex part2 failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 5. Final HEX merge (Combine everything)
REM -----------------------------------------------------
echo [5/7] Creating final combined HEX...
mergehex.exe --merge hex\part1.hex hex\part2.hex --output hex\firmware_all.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex final merge failed.
pause
exit /b 1
)
echo Final merged HEX: hex\firmware_all.hex
REM -----------------------------------------------------
REM 6. Detect device SERIAL NUMBER
REM -----------------------------------------------------
echo [6/7] Detecting device serial number...
for /f %%A in ('
powershell -Command "(nrfutil device list --json | Select-String '\"type\":\"info\"' | ConvertFrom-Json).data.devices[0].serialNumber"
') do set SERIALNUMBER=%%A
if "%SERIALNUMBER%"=="" (
echo ERROR: No serial number found.
pause
exit /b 1
)
echo Using Serial Number: %SERIALNUMBER%
echo Flashing: recover → erase → program → reset
REM recover
nrfutil device recover --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
REM erase
nrfutil device erase --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
REM program
nrfutil device program --firmware hex\firmware_all.hex --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
REM reset
nrfutil device reset --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
goto flash_success
:flash_fail
echo ERROR: Flashing failed.
pause
exit /b 1
:flash_success
REM -----------------------------------------------------
REM 7. Set Readback Protection (RBP)
REM -----------------------------------------------------
echo [7/7] Setting Readback Protection (ALL)...
nrfutil device protection-set ALL --serial-number %SERIALNUMBER%
echo ==========================================
echo Programming Complete!
echo %date% %time%
echo ==========================================
pause
endlocal
@@ -1,183 +1,183 @@
@echo off
setlocal enabledelayedexpansion
echo ==========================================
echo MEDiThings Bladder Patch Programming
echo (DEBUG MODE - No Readback Protection)
echo ==========================================
REM -----------------------------------------------------
REM Set script directory as base path
REM -----------------------------------------------------
cd /d "%~dp0"
echo Working directory: %CD%
REM -----------------------------------------------------
REM Create hex output folder
REM -----------------------------------------------------
if not exist hex mkdir hex
REM -----------------------------------------------------
REM 1. Copy HEX files (with verification)
REM -----------------------------------------------------
echo [1/6] Copying HEX files...
set "APP_SRC=..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex"
set "BOOT_SRC=..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex"
REM Check source files exist
if not exist "%APP_SRC%" (
echo ERROR: Application HEX not found: %APP_SRC%
pause
exit /b 1
)
if not exist "%BOOT_SRC%" (
echo ERROR: Bootloader HEX not found: %BOOT_SRC%
pause
exit /b 1
)
REM Copy with /Y to overwrite without prompt
copy /Y "%APP_SRC%" hex\app.hex
if %errorlevel% neq 0 (
echo ERROR: Failed to copy app.hex
pause
exit /b 1
)
echo - app.hex copied OK
copy /Y "%BOOT_SRC%" hex\boot.hex
if %errorlevel% neq 0 (
echo ERROR: Failed to copy boot.hex
pause
exit /b 1
)
echo - boot.hex copied OK
REM -----------------------------------------------------
REM 2. Generate Bootloader DFU Settings
REM -----------------------------------------------------
echo [2/6] Generating Bootloader DFU settings...
nrfutil settings generate ^
--family NRF52840 ^
--application hex\app.hex ^
--application-version 1 ^
--bootloader-version 1 ^
--bl-settings-version 2 ^
hex\settings.hex
if %errorlevel% neq 0 (
echo ERROR: nrfutil settings failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 3. Merge HEX (SoftDevice + Application)
REM -----------------------------------------------------
echo [3/6] Merging SoftDevice + Application...
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex hex\app.hex --output hex\part1.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex part1 failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 4. Merge HEX (Bootloader + Settings)
REM -----------------------------------------------------
echo [4/6] Merging Bootloader + Settings...
mergehex.exe --merge hex\boot.hex hex\settings.hex --output hex\part2.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex part2 failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 5. Final HEX merge (Combine everything)
REM -----------------------------------------------------
echo [5/6] Creating final combined HEX...
mergehex.exe --merge hex\part1.hex hex\part2.hex --output hex\firmware_all.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex final merge failed.
pause
exit /b 1
)
echo Final merged HEX: hex\firmware_all.hex
REM -----------------------------------------------------
REM 6. Detect device SERIAL NUMBER and Flash
REM -----------------------------------------------------
echo [6/6] Detecting device serial number...
for /f %%A in ('
powershell -Command "(nrfutil device list --json | Select-String '\"type\":\"info\"' | ConvertFrom-Json).data.devices[0].serialNumber"
') do set SERIALNUMBER=%%A
if "%SERIALNUMBER%"=="" (
echo ERROR: No serial number found.
pause
exit /b 1
)
echo Using Serial Number: %SERIALNUMBER%
echo Flashing: program → reset (NO erase, FDS preserved)
REM recover - SKIP to preserve internal flash data
REM erase - SKIP to preserve FDS/fstorage data
REM program (hex 데이터가 있는 영역만 erase, FDS 보존)
nrfutil device program --firmware hex\firmware_all.hex --options chip_erase_mode=ERASE_RANGES_TOUCHED_BY_FIRMWARE --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 (
echo.
echo [WARNING] Program failed - device may have Readback Protection enabled.
echo Recover will ERASE ALL flash including FDS data.
echo.
set /p RECOVER_YN="Recover and retry? (Y/N): "
if /i "!RECOVER_YN!"=="Y" (
echo Recovering device...
nrfutil device recover --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
echo Re-programming...
nrfutil device program --firmware hex\firmware_all.hex --options chip_erase_mode=ERASE_RANGES_TOUCHED_BY_FIRMWARE --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
) else (
goto flash_fail
)
)
REM reset
nrfutil device reset --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
goto flash_success
:flash_fail
echo ERROR: Flashing failed.
pause
exit /b 1
:flash_success
REM -----------------------------------------------------
REM NOTE: Readback Protection SKIPPED for debugging
REM -----------------------------------------------------
echo ==========================================
echo Programming Complete! (DEBUG MODE)
echo No Readback Protection Applied
echo Internal Flash (FDS) Preserved
echo %date% %time%
echo ==========================================
REM -----------------------------------------------------
REM Open J-Link RTT Viewer for RTT debugging
REM -----------------------------------------------------
echo Starting J-Link RTT Viewer...
endlocal
@echo off
setlocal enabledelayedexpansion
echo ==========================================
echo MEDiThings Bladder Patch Programming
echo (DEBUG MODE - No Readback Protection)
echo ==========================================
REM -----------------------------------------------------
REM Set script directory as base path
REM -----------------------------------------------------
cd /d "%~dp0"
echo Working directory: %CD%
REM -----------------------------------------------------
REM Create hex output folder
REM -----------------------------------------------------
if not exist hex mkdir hex
REM -----------------------------------------------------
REM 1. Copy HEX files (with verification)
REM -----------------------------------------------------
echo [1/6] Copying HEX files...
set "APP_SRC=..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex"
set "BOOT_SRC=..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex"
REM Check source files exist
if not exist "%APP_SRC%" (
echo ERROR: Application HEX not found: %APP_SRC%
pause
exit /b 1
)
if not exist "%BOOT_SRC%" (
echo ERROR: Bootloader HEX not found: %BOOT_SRC%
pause
exit /b 1
)
REM Copy with /Y to overwrite without prompt
copy /Y "%APP_SRC%" hex\app.hex
if %errorlevel% neq 0 (
echo ERROR: Failed to copy app.hex
pause
exit /b 1
)
echo - app.hex copied OK
copy /Y "%BOOT_SRC%" hex\boot.hex
if %errorlevel% neq 0 (
echo ERROR: Failed to copy boot.hex
pause
exit /b 1
)
echo - boot.hex copied OK
REM -----------------------------------------------------
REM 2. Generate Bootloader DFU Settings
REM -----------------------------------------------------
echo [2/6] Generating Bootloader DFU settings...
nrfutil settings generate ^
--family NRF52840 ^
--application hex\app.hex ^
--application-version 1 ^
--bootloader-version 1 ^
--bl-settings-version 2 ^
hex\settings.hex
if %errorlevel% neq 0 (
echo ERROR: nrfutil settings failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 3. Merge HEX (SoftDevice + Application)
REM -----------------------------------------------------
echo [3/6] Merging SoftDevice + Application...
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex hex\app.hex --output hex\part1.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex part1 failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 4. Merge HEX (Bootloader + Settings)
REM -----------------------------------------------------
echo [4/6] Merging Bootloader + Settings...
mergehex.exe --merge hex\boot.hex hex\settings.hex --output hex\part2.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex part2 failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 5. Final HEX merge (Combine everything)
REM -----------------------------------------------------
echo [5/6] Creating final combined HEX...
mergehex.exe --merge hex\part1.hex hex\part2.hex --output hex\firmware_all.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex final merge failed.
pause
exit /b 1
)
echo Final merged HEX: hex\firmware_all.hex
REM -----------------------------------------------------
REM 6. Detect device SERIAL NUMBER and Flash
REM -----------------------------------------------------
echo [6/6] Detecting device serial number...
for /f %%A in ('
powershell -Command "(nrfutil device list --json | Select-String '\"type\":\"info\"' | ConvertFrom-Json).data.devices[0].serialNumber"
') do set SERIALNUMBER=%%A
if "%SERIALNUMBER%"=="" (
echo ERROR: No serial number found.
pause
exit /b 1
)
echo Using Serial Number: %SERIALNUMBER%
echo Flashing: program → reset (NO erase, FDS preserved)
REM recover - SKIP to preserve internal flash data
REM erase - SKIP to preserve FDS/fstorage data
REM program (hex 데이터가 있는 영역만 erase, FDS 보존)
nrfutil device program --firmware hex\firmware_all.hex --options chip_erase_mode=ERASE_RANGES_TOUCHED_BY_FIRMWARE --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 (
echo.
echo [WARNING] Program failed - device may have Readback Protection enabled.
echo Recover will ERASE ALL flash including FDS data.
echo.
set /p RECOVER_YN="Recover and retry? (Y/N): "
if /i "!RECOVER_YN!"=="Y" (
echo Recovering device...
nrfutil device recover --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
echo Re-programming...
nrfutil device program --firmware hex\firmware_all.hex --options chip_erase_mode=ERASE_RANGES_TOUCHED_BY_FIRMWARE --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
) else (
goto flash_fail
)
)
REM reset
nrfutil device reset --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
goto flash_success
:flash_fail
echo ERROR: Flashing failed.
pause
exit /b 1
:flash_success
REM -----------------------------------------------------
REM NOTE: Readback Protection SKIPPED for debugging
REM -----------------------------------------------------
echo ==========================================
echo Programming Complete! (DEBUG MODE)
echo No Readback Protection Applied
echo Internal Flash (FDS) Preserved
echo %date% %time%
echo ==========================================
REM -----------------------------------------------------
REM Open J-Link RTT Viewer for RTT debugging
REM -----------------------------------------------------
echo Starting J-Link RTT Viewer...
endlocal
@@ -1,3 +1,3 @@
nrfjprog --family NRF52 --recover
nrfjprog --family NRF52 --eraseall
nrfjprog --family NRF52 --recover
nrfjprog --family NRF52 --eraseall
pause
@@ -1,3 +1,3 @@
nrfjprog --family NRF52 --rbp ALL
nrfjprog --family NRF52 --pinreset
nrfjprog --family NRF52 --rbp ALL
nrfjprog --family NRF52 --pinreset
pause
@@ -1,2 +1,2 @@
nrfjprog --family NRF52 --recover
nrfjprog --family NRF52 --recover
pause
@@ -1,2 +1,2 @@
nrfjprog --family NRF52 --pinreset
nrfjprog --family NRF52 --pinreset
pause
@@ -1,8 +1,8 @@
chcp 437
set CURDIR=%cd%
copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex
nrfutil pkg generate --application medithings_bladder_patch_0001.hex --application-version 1 --hw-version 52 --sd-req 0x0100 --sd-id 0x0100 --key-file private.key medithings_bladder_patch_dfu.zip
pause
chcp 437
set CURDIR=%cd%
copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex
nrfutil pkg generate --application medithings_bladder_patch_0001.hex --application-version 1 --hw-version 52 --sd-req 0x0100 --sd-id 0x0100 --key-file private.key medithings_bladder_patch_dfu.zip
pause
@@ -0,0 +1,3 @@
set path=d:\nrfutil\bin;%path%
d:
cd D:\mt_project\vesiscan\project\ble_peripheral\ble_app_bladder_patch\hex
@@ -0,0 +1,170 @@
/*******************************************************************************
* @file i2c_manager.c
* @brief Reliable HW↔SW I2C Switching Logic (with Mode Set Logging)
*******************************************************************************
*
* [모듈 개요]
* I2C 버스의 HW(하드웨어 TWI) / SW(소프트웨어 비트뱅) 모드 전환을 관리하는 모듈.
*
* - HW I2C: nRF52840 내장 TWI 하드웨어 주변장치를 사용 (ICM42670P IMU 센서 통신용, 400kHz)
* - SW I2C: GPIO 비트뱅 방식의 소프트웨어 I2C (현재 사용하지 않는 레거시 코드)
*
* [핀 설정]
* SCL = P1.14 (ICM42670_I2C_SCL_PIN)
* SDA = P1.15 (ICM42670_I2C_SDA_PIN)
*
* [주요 함수]
* hw_i2c_init_once() : SW→HW 전환 또는 HW 초기화 (이미 HW 모드면 중복 초기화 방지)
* sw_i2c_init_once() : HW→SW 전환 (TWI 해제 후 SW 모드 진입, 레거시)
* i2c_reset_state() : 모든 I2C 모드 플래그를 초기화 (HW/SW 모두 false)
*
* [동작 원리]
* HW_I2C_FRQ, SW_I2C_FRQ 두 개의 bool 플래그로 현재 모드를 추적하며,
* 한 번에 하나의 모드만 활성화되도록 상호 배제(mutex) 방식으로 관리한다.
* 모드 전환 시 기존 모드의 리소스를 먼저 해제한 후 새 모드를 초기화한다.
*
******************************************************************************/
#include "i2c_manager.h"
#include "debug_print.h"
#include "nrf_delay.h"
#include "nrf_drv_twi.h"
#include "nrfx_twi.h"
#include "boards.h"
#include "system_interface.h"
/* 현재 I2C 모드 상태 플래그 (true = 해당 모드 활성화) */
bool HW_I2C_FRQ = true; /* HW TWI 모드 활성 여부 (기본값: true, 초기 상태는 HW) */
bool SW_I2C_FRQ = false; /* SW 비트뱅 모드 활성 여부 */
/* TWI 인스턴스 번호 (nRF52840은 TWI0, TWI1 두 개 지원) */
#define TWI_INSTANCE 0
/* TWI (I2C) 하드웨어 인스턴스 : IMU 드라이버에서 사용 - jhChun 26.03.16 */
const nrfx_twi_t m_twi = NRFX_TWI_INSTANCE(TWI_INSTANCE);
/* TWI (I2C) 해제 - jhChun 26.03.16 */
/* TWI 하드웨어를 비활성화하고 초기화 해제하여 GPIO 핀을 반환한다 */
static void twi_uninitialize(void){
nrfx_twi_disable(&m_twi); /* TWI 주변장치 비활성화 */
nrfx_twi_uninit(&m_twi); /* TWI 초기화 해제 (핀 리소스 반환) */
}
/* TWI (I2C) 하드웨어 초기화 (SCL/SDA핀, 400kHz) - jhChun 26.03.16 */
/* SCL, SDA 핀을 설정하고 400kHz Fast Mode로 TWI를 초기화 및 활성화한다 */
static void twi_initialize(void){
ret_code_t err_code;
/* TWI 설정 구조체: 핀 번호, 클럭 속도, 인터럽트 우선순위 지정 */
const nrfx_twi_config_t twi_config = {
.scl = ICM42670_I2C_SCL_PIN, /* SCL 핀 (P1.14) */
.sda = ICM42670_I2C_SDA_PIN, /* SDA 핀 (P1.15) */
.frequency = NRF_TWI_FREQ_400K, /* 400kHz Fast Mode */
.interrupt_priority = APP_IRQ_PRIORITY_HIGH, /* 높은 인터럽트 우선순위 */
};
/* TWI 초기화 (이벤트 핸들러 NULL = 블로킹 모드) */
err_code = nrfx_twi_init(&m_twi, &twi_config, NULL, NULL);
APP_ERROR_CHECK(err_code);
nrfx_twi_enable(&m_twi); /* TWI 주변장치 활성화 → I2C 통신 가능 */
}
/* -------------------------------------------------------------------------- */
/* HW (TWI) 초기화 */
/* -------------------------------------------------------------------------- */
/*
* HW I2C 모드로 전환하거나 초기화하는 함수.
* - SW 모드가 활성화되어 있으면 SW 플래그를 해제하고 HW로 전환
* - 이미 HW 모드이면 중복 초기화를 방지하여 바로 리턴
* - 최초 HW 초기화 시 twi_initialize()를 호출하여 TWI 하드웨어 설정
*/
void hw_i2c_init_once(void)
{
// SW 모드일 경우 강제 해제 후 HW 전환
if (SW_I2C_FRQ)
{
//DBG_PRINTF("[I2C]SW→HW\r\n");
// SW 리소스 해제 (필요 시 추가)
SW_I2C_FRQ = false;
nrf_delay_ms(2); /* 모드 전환 안정화 대기 (2ms) */
}
/* 이미 HW 모드가 활성화되어 있으면 중복 초기화 방지를 위해 즉시 리턴 */
// 이미 HW면 스킵
if (HW_I2C_FRQ)
{
// DBG_PRINTF("[I2C] HW I2C set\r\n");
return;
}
/* HW TWI 하드웨어 초기화 수행 (SCL/SDA 핀 설정, 400kHz, 활성화) */
// 실제 HW 초기화
twi_initialize();
nrf_delay_ms(2); /* 초기화 후 안정화 대기 (2ms) */
/* 모드 플래그 갱신: HW 활성, SW 비활성 */
HW_I2C_FRQ = true;
SW_I2C_FRQ = false;
// DBG_PRINTF("[I2C] HW I2C Mode set!\r\n");
}
/* -------------------------------------------------------------------------- */
/* SW (Port Bang-Bang) 초기화 */
/* -------------------------------------------------------------------------- */
/*
* SW I2C(비트뱅) 모드로 전환하는 함수. (현재 레거시, 사용하지 않음)
* - HW 모드가 활성화되어 있으면 TWI를 해제하고 SW로 전환
* - 이미 SW 모드이면 중복 초기화를 방지하여 바로 리턴
* - power_control.c의 power_loop()에서 Step 0에서 호출됨
*/
void sw_i2c_init_once(void)
{
// HW 모드일 경우 강제 해제 후 SW 전환
if (HW_I2C_FRQ)
{
//DBG_PRINTF("[I2C]HW→SW\r\n");
nrfx_twi_disable(&m_twi); /* TWI 비활성화 */
nrfx_twi_uninit(&m_twi); /* TWI 초기화 해제 */
nrf_delay_ms(2); /* 모드 전환 안정화 대기 (2ms) */
HW_I2C_FRQ = false;
}
// 이미 SW 모드면 재실행 금지
if (SW_I2C_FRQ)
{
// DBG_PRINTF("[I2C] SWI2C already initialized\r\n");
return;
}
/* TWI 라인 완전 해제 후 SW 비트뱅 모드 진입 */
// 실제 SW 초기화
twi_uninitialize(); // TWI 라인 해제
nrf_delay_ms(1); /* 해제 후 안정화 대기 (1ms) */
/* 모드 플래그 갱신: SW 활성, HW 비활성 */
SW_I2C_FRQ = true;
HW_I2C_FRQ = false;
// DBG_PRINTF("[I2C] SW I2C Mode set!\r\n");
}
/* -------------------------------------------------------------------------- */
/* 전체 리셋 */
/* -------------------------------------------------------------------------- */
/*
* 모든 I2C 모드 플래그를 초기화하는 함수.
* HW/SW 모두 비활성 상태로 만들어, 다음 init 호출 시 강제로 재초기화되도록 한다.
* 주로 시스템 리셋이나 에러 복구 시 사용.
*/
void i2c_reset_state(void)
{
HW_I2C_FRQ = false; /* HW 모드 플래그 초기화 */
SW_I2C_FRQ = false; /* SW 모드 플래그 초기화 */
DBG_PRINTF("Flags reset\r\n");
}
@@ -0,0 +1,50 @@
/*******************************************************************************
* @file i2c_manager.h
* @brief Common header for HW/SW I2C mutex control
*******************************************************************************
*
* [헤더 개요]
* I2C 버스 HW/SW 모드 전환 관리자의 공용 인터페이스 헤더.
*
* - HW_I2C_FRQ: HW TWI 모드 활성 여부 (true = HW I2C 사용 중)
* - SW_I2C_FRQ: SW 비트뱅 모드 활성 여부 (true = SW I2C 사용 중)
*
* 두 플래그는 상호 배제적으로 동작하며, 동시에 true가 되지 않도록 관리된다.
*
******************************************************************************/
#ifndef __I2C_MANAGER_H__
#define __I2C_MANAGER_H__
#include <stdbool.h>
#include "app_error.h"
/* I2C 모드 상태 플래그 (외부 참조용) */
extern bool HW_I2C_FRQ; /* HW TWI 모드 활성 여부 */
extern bool SW_I2C_FRQ; /* SW 비트뱅 모드 활성 여부 */
/**
* @brief HW I2C(TWI) 모드 초기화 (중복 초기화 방지)
*
* SW 모드가 활성화되어 있으면 해제 후 HW로 전환한다.
* 이미 HW 모드이면 아무 동작 없이 리턴한다.
* ICM42670P IMU 센서 통신 전에 호출하여 HW I2C를 준비한다.
*/
void hw_i2c_init_once(void);
/**
* @brief SW I2C(비트뱅) 모드 초기화 (레거시, 현재 미사용)
*
* HW 모드가 활성화되어 있으면 TWI를 해제한 후 SW로 전환한다.
* 이미 SW 모드이면 아무 동작 없이 리턴한다.
*/
void sw_i2c_init_once(void);
/**
* @brief I2C 모드 플래그 전체 초기화
*
* HW_I2C_FRQ, SW_I2C_FRQ를 모두 false로 리셋한다.
* 다음 init 호출 시 강제로 재초기화가 수행된다.
*/
void i2c_reset_state(void);
#endif
@@ -1,103 +1,103 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#include "DataConverter.h"
uint8_t * inv_dc_int32_to_little8(int32_t x, uint8_t * little8)
{
little8[3] = (uint8_t)((x >> 24) & 0xff);
little8[2] = (uint8_t)((x >> 16) & 0xff);
little8[1] = (uint8_t)((x >> 8) & 0xff);
little8[0] = (uint8_t)(x & 0xff);
return little8;
}
uint8_t * inv_dc_int16_to_little8(int16_t x, uint8_t * little8)
{
little8[0] = (uint8_t)(x & 0xff);
little8[1] = (uint8_t)((x >> 8) & 0xff);
return little8;
}
uint8_t * inv_dc_int32_to_big8(int32_t x, uint8_t * big8)
{
big8[0] = (uint8_t)((x >> 24) & 0xff);
big8[1] = (uint8_t)((x >> 16) & 0xff);
big8[2] = (uint8_t)((x >> 8) & 0xff);
big8[3] = (uint8_t)(x & 0xff);
return big8;
}
int32_t inv_dc_little8_to_int32(const uint8_t * little8)
{
int32_t x = 0;
x |= ((int32_t)little8[3] << 24);
x |= ((int32_t)little8[2] << 16);
x |= ((int32_t)little8[1] << 8);
x |= ((int32_t)little8[0]);
return x;
}
int16_t inv_dc_big16_to_int16(uint8_t * data)
{
int16_t result;
result = (*data << 8);
data++;
result |= *data;
return result;
}
int16_t inv_dc_le_to_int16(const uint8_t * little8)
{
uint16_t x = 0;
x |= ((uint16_t)little8[0]);
x |= ((uint16_t)little8[1] << 8);
return (int16_t)x;
}
void inv_dc_sfix32_to_float(const int32_t * in, uint32_t len, uint8_t qx, float * out)
{
uint8_t i;
for(i = 0; i < len; ++i) {
out[i] = (float)in[i] / (1 << qx);
}
}
void inv_dc_float_to_sfix32(const float * in, uint32_t len, uint8_t qx, int32_t * out)
{
uint8_t i;
for(i = 0; i < len; ++i) {
out[i] = (int32_t)((in[i] * (1 << qx)) + ((in[i] >= 0) - 0.5f));
}
}
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#include "DataConverter.h"
uint8_t * inv_dc_int32_to_little8(int32_t x, uint8_t * little8)
{
little8[3] = (uint8_t)((x >> 24) & 0xff);
little8[2] = (uint8_t)((x >> 16) & 0xff);
little8[1] = (uint8_t)((x >> 8) & 0xff);
little8[0] = (uint8_t)(x & 0xff);
return little8;
}
uint8_t * inv_dc_int16_to_little8(int16_t x, uint8_t * little8)
{
little8[0] = (uint8_t)(x & 0xff);
little8[1] = (uint8_t)((x >> 8) & 0xff);
return little8;
}
uint8_t * inv_dc_int32_to_big8(int32_t x, uint8_t * big8)
{
big8[0] = (uint8_t)((x >> 24) & 0xff);
big8[1] = (uint8_t)((x >> 16) & 0xff);
big8[2] = (uint8_t)((x >> 8) & 0xff);
big8[3] = (uint8_t)(x & 0xff);
return big8;
}
int32_t inv_dc_little8_to_int32(const uint8_t * little8)
{
int32_t x = 0;
x |= ((int32_t)little8[3] << 24);
x |= ((int32_t)little8[2] << 16);
x |= ((int32_t)little8[1] << 8);
x |= ((int32_t)little8[0]);
return x;
}
int16_t inv_dc_big16_to_int16(uint8_t * data)
{
int16_t result;
result = (*data << 8);
data++;
result |= *data;
return result;
}
int16_t inv_dc_le_to_int16(const uint8_t * little8)
{
uint16_t x = 0;
x |= ((uint16_t)little8[0]);
x |= ((uint16_t)little8[1] << 8);
return (int16_t)x;
}
void inv_dc_sfix32_to_float(const int32_t * in, uint32_t len, uint8_t qx, float * out)
{
uint8_t i;
for(i = 0; i < len; ++i) {
out[i] = (float)in[i] / (1 << qx);
}
}
void inv_dc_float_to_sfix32(const float * in, uint32_t len, uint8_t qx, int32_t * out)
{
uint8_t i;
for(i = 0; i < len; ++i) {
out[i] = (int32_t)((in[i] * (1 << qx)) + ((in[i] >= 0) - 0.5f));
}
}
@@ -1,87 +1,87 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup DataConverter Data Converter
* @brief Helper functions to convert integer
* @ingroup EmbUtils
* @{
*/
#ifndef _INV_DATA_CONVERTER_H_
#define _INV_DATA_CONVERTER_H_
#include "InvExport.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/** @brief Converts a 32-bit long to a little endian byte stream
*/
uint8_t INV_EXPORT * inv_dc_int32_to_little8(int32_t x, uint8_t * little8);
/** @brief Converts a 16-bit integer to a little endian byte stream
*/
uint8_t INV_EXPORT * inv_dc_int16_to_little8(int16_t x, uint8_t * little8);
/** @brief Converts a 32-bit long to a big endian byte stream
*/
uint8_t INV_EXPORT * inv_dc_int32_to_big8(int32_t x, uint8_t *big8);
/** @brief Converts a little endian byte stream into a 32-bit integer
*/
int32_t INV_EXPORT inv_dc_little8_to_int32(const uint8_t * little8);
/** @brief Converts a little endian byte stream into a 16-bit integer
*/
int16_t INV_EXPORT inv_dc_le_to_int16(const uint8_t * little8);
/** @brief Converts big endian on 16 bits into an unsigned short
*/
int16_t INV_EXPORT inv_dc_big16_to_int16(uint8_t * data);
/** @brief Converts an array of 32-bit signed fixed-point integers to an array of floats
* @param[in] in Pointer to the first element of the array of 32-bit signed fixed-point integers
* @param[in] len Length of the array
* @param[in] qx Number of bits used to represent the decimal part of the fixed-point integers
* @param[out] out Pointer to the memory area where the output will be stored
*/
void INV_EXPORT inv_dc_sfix32_to_float(const int32_t * in, uint32_t len, uint8_t qx, float * out);
/** @brief Converts an array of floats to an array of 32-bit signed fixed-point integers
* @param[in] in Pointer to the first element of the array of floats
* @param[in] len Length of the array
* @param[in] qx Number of bits used to represent the decimal part of the fixed-point integers
* @param[out] out Pointer to the memory area where the output will be stored
*/
void INV_EXPORT inv_dc_float_to_sfix32(const float * in, uint32_t len, uint8_t qx, int32_t * out);
#ifdef __cplusplus
}
#endif
#endif /* _INV_DATA_CONVERTER_H_ */
/** @} */
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup DataConverter Data Converter
* @brief Helper functions to convert integer
* @ingroup EmbUtils
* @{
*/
#ifndef _INV_DATA_CONVERTER_H_
#define _INV_DATA_CONVERTER_H_
#include "InvExport.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/** @brief Converts a 32-bit long to a little endian byte stream
*/
uint8_t INV_EXPORT * inv_dc_int32_to_little8(int32_t x, uint8_t * little8);
/** @brief Converts a 16-bit integer to a little endian byte stream
*/
uint8_t INV_EXPORT * inv_dc_int16_to_little8(int16_t x, uint8_t * little8);
/** @brief Converts a 32-bit long to a big endian byte stream
*/
uint8_t INV_EXPORT * inv_dc_int32_to_big8(int32_t x, uint8_t *big8);
/** @brief Converts a little endian byte stream into a 32-bit integer
*/
int32_t INV_EXPORT inv_dc_little8_to_int32(const uint8_t * little8);
/** @brief Converts a little endian byte stream into a 16-bit integer
*/
int16_t INV_EXPORT inv_dc_le_to_int16(const uint8_t * little8);
/** @brief Converts big endian on 16 bits into an unsigned short
*/
int16_t INV_EXPORT inv_dc_big16_to_int16(uint8_t * data);
/** @brief Converts an array of 32-bit signed fixed-point integers to an array of floats
* @param[in] in Pointer to the first element of the array of 32-bit signed fixed-point integers
* @param[in] len Length of the array
* @param[in] qx Number of bits used to represent the decimal part of the fixed-point integers
* @param[out] out Pointer to the memory area where the output will be stored
*/
void INV_EXPORT inv_dc_sfix32_to_float(const int32_t * in, uint32_t len, uint8_t qx, float * out);
/** @brief Converts an array of floats to an array of 32-bit signed fixed-point integers
* @param[in] in Pointer to the first element of the array of floats
* @param[in] len Length of the array
* @param[in] qx Number of bits used to represent the decimal part of the fixed-point integers
* @param[out] out Pointer to the memory area where the output will be stored
*/
void INV_EXPORT inv_dc_float_to_sfix32(const float * in, uint32_t len, uint8_t qx, int32_t * out);
#ifdef __cplusplus
}
#endif
#endif /* _INV_DATA_CONVERTER_H_ */
/** @} */
@@ -1,47 +1,47 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#include "ErrorHelper.h"
const char * inv_error_str(int error)
{
switch(error) {
case INV_ERROR_SUCCESS: return "Success";
case INV_ERROR: return "Unspecified error";
case INV_ERROR_NIMPL: return "Not implemented";
case INV_ERROR_TRANSPORT: return "Transport error";
case INV_ERROR_TIMEOUT: return "Timeout, action did not complete in time";
case INV_ERROR_SIZE: return "Wrong size error";
case INV_ERROR_OS: return "Operating system failure";
case INV_ERROR_IO: return "Input/Output error";
case INV_ERROR_MEM: return "Bad allocation";
case INV_ERROR_HW: return "Hardware error";
case INV_ERROR_BAD_ARG: return "Invalid arguments";
case INV_ERROR_UNEXPECTED: return "Unexpected error";
case INV_ERROR_FILE: return "Invalid file format";
case INV_ERROR_PATH: return "Invalid file path";
case INV_ERROR_IMAGE_TYPE: return "Unknown image type";
case INV_ERROR_WATCHDOG: return "Watchdog error";
default: return "Unknown error";
}
}
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#include "ErrorHelper.h"
const char * inv_error_str(int error)
{
switch(error) {
case INV_ERROR_SUCCESS: return "Success";
case INV_ERROR: return "Unspecified error";
case INV_ERROR_NIMPL: return "Not implemented";
case INV_ERROR_TRANSPORT: return "Transport error";
case INV_ERROR_TIMEOUT: return "Timeout, action did not complete in time";
case INV_ERROR_SIZE: return "Wrong size error";
case INV_ERROR_OS: return "Operating system failure";
case INV_ERROR_IO: return "Input/Output error";
case INV_ERROR_MEM: return "Bad allocation";
case INV_ERROR_HW: return "Hardware error";
case INV_ERROR_BAD_ARG: return "Invalid arguments";
case INV_ERROR_UNEXPECTED: return "Unexpected error";
case INV_ERROR_FILE: return "Invalid file format";
case INV_ERROR_PATH: return "Invalid file path";
case INV_ERROR_IMAGE_TYPE: return "Unknown image type";
case INV_ERROR_WATCHDOG: return "Watchdog error";
default: return "Unknown error";
}
}
@@ -1,51 +1,51 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup ErrorHelper Error Helper
* @brief Helper functions related to error code
* @ingroup EmbUtils
* @{
*/
#ifndef _INV_ERROR_HELPER_H_
#define _INV_ERROR_HELPER_H_
#include "InvExport.h"
#include "InvError.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Returns string describing error number
* @sa enum inv_error
*/
const char INV_EXPORT * inv_error_str(int error);
#ifdef __cplusplus
}
#endif
#endif /* _INV_ERROR_HELPER_H_ */
/** @} */
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup ErrorHelper Error Helper
* @brief Helper functions related to error code
* @ingroup EmbUtils
* @{
*/
#ifndef _INV_ERROR_HELPER_H_
#define _INV_ERROR_HELPER_H_
#include "InvExport.h"
#include "InvError.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Returns string describing error number
* @sa enum inv_error
*/
const char INV_EXPORT * inv_error_str(int error);
#ifdef __cplusplus
}
#endif
#endif /* _INV_ERROR_HELPER_H_ */
/** @} */
@@ -1,82 +1,82 @@
/*
Copyright (c) 2014-2015 InvenSense Inc. Portions Copyright (c) 2014-2015 Movea. All rights reserved.
This software, related documentation and any modifications thereto (collectively "Software") is subject
to InvenSense and its licensors' intellectual property rights under U.S. and international copyright and
other intellectual property rights laws.
InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
and any use, reproduction, disclosure or distribution of the Software without an express license
agreement from InvenSense is strictly prohibited.
*/
#include "InvBasicMath.h"
#include <limits.h>
unsigned int InvBasicMath_log2u(unsigned int val)
{
unsigned int ret = UINT_MAX;
while (val != 0) {
val >>= 1;
ret++;
}
return ret;
}
int InvBasicMath_isAnOrthonormalMatrix(const float matrix[9])
{
// Check if matrix is orthogonal
// Matrix is orthogonal if transpose(Matrix) x Matrix = Identity
float transpose[9];
float mult[9];
int i, j;
// Compute Transpose(matrix)
for (i = 0; i < 3; i++) {
for(j = 0; j < 3; j++) {
transpose[i*3+j] = matrix[i+j*3];
}
}
// Multiply transpose x matrix
mult[0] = transpose[0]*matrix[0] + transpose[1]*matrix[3] + transpose[2]*matrix[6];
mult[1] = transpose[0]*matrix[1] + transpose[1]*matrix[4] + transpose[2]*matrix[7];
mult[2] = transpose[0]*matrix[2] + transpose[1]*matrix[5] + transpose[2]*matrix[8];
mult[3] = transpose[3]*matrix[0] + transpose[4]*matrix[3] + transpose[5]*matrix[6];
mult[4] = transpose[3]*matrix[1] + transpose[4]*matrix[4] + transpose[5]*matrix[7];
mult[5] = transpose[3]*matrix[2] + transpose[4]*matrix[5] + transpose[5]*matrix[8];
mult[6] = transpose[6]*matrix[0] + transpose[7]*matrix[3] + transpose[8]*matrix[6];
mult[7] = transpose[6]*matrix[1] + transpose[7]*matrix[4] + transpose[8]*matrix[7];
mult[8] = transpose[6]*matrix[2] + transpose[7]*matrix[5] + transpose[8]*matrix[8];
// Check that mult is identity
for (i = 0; i < 3; i++) {
for(j = 0; j < 3; j++) {
if (i == j) {
if (mult[i+j*3] != 1)
return 0;
}
else {
if (mult[i+j*3] != 0)
return 0;
}
}
}
return 1;
}
float InvBasicMath_computeMatrixDeterminant(const float matrix[9])
{
return matrix[0] * (matrix[4]*matrix[8] - matrix[7]*matrix[5])
-matrix[1] * (matrix[3]*matrix[8] - matrix[6]*matrix[5])
+matrix[2] * (matrix[3]*matrix[7] - matrix[4]*matrix[6]);
}
/*
Copyright (c) 2014-2015 InvenSense Inc. Portions Copyright (c) 2014-2015 Movea. All rights reserved.
This software, related documentation and any modifications thereto (collectively "Software") is subject
to InvenSense and its licensors' intellectual property rights under U.S. and international copyright and
other intellectual property rights laws.
InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
and any use, reproduction, disclosure or distribution of the Software without an express license
agreement from InvenSense is strictly prohibited.
*/
#include "InvBasicMath.h"
#include <limits.h>
unsigned int InvBasicMath_log2u(unsigned int val)
{
unsigned int ret = UINT_MAX;
while (val != 0) {
val >>= 1;
ret++;
}
return ret;
}
int InvBasicMath_isAnOrthonormalMatrix(const float matrix[9])
{
// Check if matrix is orthogonal
// Matrix is orthogonal if transpose(Matrix) x Matrix = Identity
float transpose[9];
float mult[9];
int i, j;
// Compute Transpose(matrix)
for (i = 0; i < 3; i++) {
for(j = 0; j < 3; j++) {
transpose[i*3+j] = matrix[i+j*3];
}
}
// Multiply transpose x matrix
mult[0] = transpose[0]*matrix[0] + transpose[1]*matrix[3] + transpose[2]*matrix[6];
mult[1] = transpose[0]*matrix[1] + transpose[1]*matrix[4] + transpose[2]*matrix[7];
mult[2] = transpose[0]*matrix[2] + transpose[1]*matrix[5] + transpose[2]*matrix[8];
mult[3] = transpose[3]*matrix[0] + transpose[4]*matrix[3] + transpose[5]*matrix[6];
mult[4] = transpose[3]*matrix[1] + transpose[4]*matrix[4] + transpose[5]*matrix[7];
mult[5] = transpose[3]*matrix[2] + transpose[4]*matrix[5] + transpose[5]*matrix[8];
mult[6] = transpose[6]*matrix[0] + transpose[7]*matrix[3] + transpose[8]*matrix[6];
mult[7] = transpose[6]*matrix[1] + transpose[7]*matrix[4] + transpose[8]*matrix[7];
mult[8] = transpose[6]*matrix[2] + transpose[7]*matrix[5] + transpose[8]*matrix[8];
// Check that mult is identity
for (i = 0; i < 3; i++) {
for(j = 0; j < 3; j++) {
if (i == j) {
if (mult[i+j*3] != 1)
return 0;
}
else {
if (mult[i+j*3] != 0)
return 0;
}
}
}
return 1;
}
float InvBasicMath_computeMatrixDeterminant(const float matrix[9])
{
return matrix[0] * (matrix[4]*matrix[8] - matrix[7]*matrix[5])
-matrix[1] * (matrix[3]*matrix[8] - matrix[6]*matrix[5])
+matrix[2] * (matrix[3]*matrix[7] - matrix[4]*matrix[6]);
}
@@ -1,94 +1,94 @@
/*
Copyright (c) 2014-2015 InvenSense Inc. Portions Copyright (c) 2014-2015 Movea. All rights reserved.
This software, related documentation and any modifications thereto (collectively "Software") is subject
to InvenSense and its licensors' intellectual property rights under U.S. and international copyright and
other intellectual property rights laws.
InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
and any use, reproduction, disclosure or distribution of the Software without an express license
agreement from InvenSense is strictly prohibited.
*/
/** @defgroup InvBasicMath InvBasicMath
@brief This file contains basic (overloadable) math functions and macros
@ingroup EmbUtils
@{
*/
#ifndef _INV_BASIC_MATH_H_
#define _INV_BASIC_MATH_H_
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Return absolute value of argument
*/
#ifndef INV_ABS
# define INV_ABS(a) ((a) < 0 ? -(a) : (a))
#endif
/** @brief Return minimum of two arguments
*/
#ifndef INV_MIN
# define INV_MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
/** @brief Return maximum of two arguments
*/
#ifndef INV_MAX
# define INV_MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
/** @brief Define value for pi
*/
#ifndef INV_PI
# define INV_PI 3.14159265358979
#endif
#ifndef M_PI
# define M_PI INV_PI
#endif
/** @brief Return saturated integer
*/
#ifndef INV_SATURATE
static inline long InvBasicMath_saturatel(long in, long min, long max)
{
if (in > max)
return max;
else if (in < min)
return min;
else
return in;
}
# define INV_SATURATE(a, min, max) InvBasicMath_saturatel(a, min, max)
#endif
/** @brief Compute log2 from integer
*/
#ifndef INV_LOG2
unsigned int InvBasicMath_log2u(unsigned int val);
# define INV_LOG2(a) InvBasicMath_log2u(a)
#endif
/** @brief Check if matrix is orthonormal
* @param [in] matrix 3x3 Matrix to be checked
* @return 1 if it is an orthonormal matrix, 0 otherwise
*/
int InvBasicMath_isAnOrthonormalMatrix(const float matrix[9]);
/** @brief Compute the determinant of the matrix
* @param [in] matrix 3x3 Matrix to be checked
* @return the determinant value
*/
float InvBasicMath_computeMatrixDeterminant(const float matrix[9]);
#ifdef __cplusplus
}
#endif
#endif /* _INV_BASIC_MATH_H_ */
/** @} */
/*
Copyright (c) 2014-2015 InvenSense Inc. Portions Copyright (c) 2014-2015 Movea. All rights reserved.
This software, related documentation and any modifications thereto (collectively "Software") is subject
to InvenSense and its licensors' intellectual property rights under U.S. and international copyright and
other intellectual property rights laws.
InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
and any use, reproduction, disclosure or distribution of the Software without an express license
agreement from InvenSense is strictly prohibited.
*/
/** @defgroup InvBasicMath InvBasicMath
@brief This file contains basic (overloadable) math functions and macros
@ingroup EmbUtils
@{
*/
#ifndef _INV_BASIC_MATH_H_
#define _INV_BASIC_MATH_H_
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Return absolute value of argument
*/
#ifndef INV_ABS
# define INV_ABS(a) ((a) < 0 ? -(a) : (a))
#endif
/** @brief Return minimum of two arguments
*/
#ifndef INV_MIN
# define INV_MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
/** @brief Return maximum of two arguments
*/
#ifndef INV_MAX
# define INV_MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
/** @brief Define value for pi
*/
#ifndef INV_PI
# define INV_PI 3.14159265358979
#endif
#ifndef M_PI
# define M_PI INV_PI
#endif
/** @brief Return saturated integer
*/
#ifndef INV_SATURATE
static inline long InvBasicMath_saturatel(long in, long min, long max)
{
if (in > max)
return max;
else if (in < min)
return min;
else
return in;
}
# define INV_SATURATE(a, min, max) InvBasicMath_saturatel(a, min, max)
#endif
/** @brief Compute log2 from integer
*/
#ifndef INV_LOG2
unsigned int InvBasicMath_log2u(unsigned int val);
# define INV_LOG2(a) InvBasicMath_log2u(a)
#endif
/** @brief Check if matrix is orthonormal
* @param [in] matrix 3x3 Matrix to be checked
* @return 1 if it is an orthonormal matrix, 0 otherwise
*/
int InvBasicMath_isAnOrthonormalMatrix(const float matrix[9]);
/** @brief Compute the determinant of the matrix
* @param [in] matrix 3x3 Matrix to be checked
* @return the determinant value
*/
float InvBasicMath_computeMatrixDeterminant(const float matrix[9]);
#ifdef __cplusplus
}
#endif
#endif /* _INV_BASIC_MATH_H_ */
/** @} */
@@ -1,389 +1,389 @@
/*
Copyright (c) 2014-2015 InvenSense Inc. Portions Copyright (c) 2014-2015 Movea. All rights reserved.
This software, related documentation and any modifications thereto (collectively "Software") is subject
to InvenSense and its licensors' intellectual property rights under U.S. and international copyright and
other intellectual property rights laws.
InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
and any use, reproduction, disclosure or distribution of the Software without an express license
agreement from InvenSense is strictly prohibited.
*/
/** \defgroup RingBuffer RingBuffer
\brief Macros to manage static circular buffer of any data type
\ingroup EmbUtils
\{
*/
#ifndef _RING_BUFFER_H_
#define _RING_BUFFER_H_
#include <stdint.h>
#include <string.h>
/** \brief Macro to declare a ring buffer
\param[in] type type of item contained in the ring buffer
\param[in] size number of items that can contain the ring buffer
To improve speed, size should be a power of 2
*/
#define RINGBUFFER_DECLARE(type, size) \
struct { \
uint16_t read, write; \
type buffer[size]; \
}
/** \brief Macro to declare a volatile ring buffer, i.e. modified within an interrupt context
\param[in] type type of item contained in the ring buffer
\param[in] size number of items that can contain the ring buffer
To improve speed, size should be a power of 2
*/
#define RINGBUFFER_VOLATILE_DECLARE(type, size) \
struct { \
volatile uint16_t read, write; \
volatile type buffer[size]; \
}
/** \brief Macro to declare a ring buffer
\param[in] name name of the circular buffer
\param[in] size number of items that can contain the ring buffer
To improve speed, size should be a power of 2
\param[in] type type of item contained in the ring buffer
*/
#define RINGBUFFER(name, size, type) RINGBUFFER_DECLARE(type, size) name
/** \brief Macro to declare a volatile ring buffer, i.e. modified within an interrupt context
\param[in] name name of the circular buffer
\param[in] size number of items that can contain the ring buffer
To improve speed, size should be a power of 2
\param[in] type type of item contained in the ring buffer
*/
#define RINGBUFFER_VOLATILE(name, size, type) RINGBUFFER_VOLATILE_DECLARE(type, size) name
/** \brief Macro to get maximum size of a ring buffer
\param[in] rb pointer to the ring buffer
\return maximum number of items that can contain the ringbuffer
*/
#define RINGBUFFER_MAXSIZE(rb) (sizeof((rb)->buffer)/sizeof((rb)->buffer[0]))
/** \brief Macro to get maximum size of a volatile ring buffer, i.e. modified within an interrupt context
\param[in] rb pointer to the ring buffer
\return maximum number of items that can contain the ringbuffer
*/
#define RINGBUFFER_VOLATILE_MAXSIZE(rb) RINGBUFFER_MAXSIZE(rb)
/** \brief Macro to get current size of a ring buffer
\param[in] rb pointer to the ring buffer
\return current number of items hold in the ringbuffer
*/
#define RINGBUFFER_SIZE(rb) ((uint16_t)((rb)->write - (rb)->read))
static inline uint16_t get_ringbuffer_volatile_size(void * rb)
{
struct { uint16_t read, write; } rb_var;
memcpy(&rb_var, rb, sizeof(rb_var));
return (rb_var.write - rb_var.read);
}
/** \brief Macro to get current size of a volatile ring buffer, i.e. modified within an interrupt context
\param[in] rb pointer to the ring buffer
\return current number of items hold in the ringbuffer
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_SIZE(rb) get_ringbuffer_volatile_size(rb)
/** \brief Macro to check if a ring buffer is full
\param[in] rb pointer to the ring buffer
\return 1 if there is no slot left in the ring buffer, 0 otherwise
*/
#define RINGBUFFER_FULL(rb) (RINGBUFFER_SIZE(rb) == RINGBUFFER_MAXSIZE(rb))
/** \brief Macro to check if a volatile ring buffer, i.e. modified within an interrupt context, is full
\param[in] rb pointer to the ring buffer
\return 1 if there is no slot left in the ring buffer, 0 otherwise
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_FULL(rb) (RINGBUFFER_VOLATILE_SIZE(rb) == RINGBUFFER_VOLATILE_MAXSIZE(rb))
/** \brief Macro to check if a ring buffer is empty
\param[in] rb pointer to the ring buffer
\return 1 if there is no item in the ring buffer, 0 otherwise
*/
#define RINGBUFFER_EMPTY(rb) (RINGBUFFER_SIZE(rb) == 0)
/** \brief Macro to check if a volatile ring buffer, i.e. modified within an interrupt context, is empty
\param[in] rb pointer to the ring buffer
\return 1 if there is no item in the ring buffer, 0 otherwise
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_EMPTY(rb) (RINGBUFFER_VOLATILE_SIZE(rb) == 0)
/** \brief Macro to get number of available slot in a ring buffer
\param[in] rb pointer to the ring buffer
\return number of empty slot in the ring buffer
*/
#define RINGBUFFER_AVAILABLE(rb) (RINGBUFFER_MAXSIZE(rb) - RINGBUFFER_SIZE(rb))
/** \brief Macro to get number of available slot in a volatile ring buffer, i.e. modified within an interrupt context
\param[in] rb pointer to the ring buffer
\return number of empty slot in the ring buffer
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_AVAILABLE(rb) (RINGBUFFER_VOLATILE_MAXSIZE(rb) - RINGBUFFER_VOLATILE_SIZE(rb))
/** \brief Macro to clear a ring buffer
\param[in] rb pointer to the ring buffer
*/
#define RINGBUFFER_CLEAR(rb) \
do { \
(rb)->read = 0, (rb)->write = 0; \
} while(0)
/** \brief Macro to clear a volatile ring buffer, i.e. modified within an interrupt context
\param[in] rb pointer to the ring buffer
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_CLEAR(rb) RINGBUFFER_CLEAR(rb)
/** \brief Push item by reference
\param[in] rb pointer to the ring buffer
\param[out] refData to available item slot
\warning There is no error checking done.
*/
#define RINGBUFFER_PUSHREF(rb, refData) \
do { \
refData = &(rb)->buffer[(rb)->write % RINGBUFFER_MAXSIZE(rb)]; \
++(rb)->write; \
} while(0)
/** \brief Push item by reference
\param[in] rb pointer to the ring buffer
\param[out] refData to available item slot
\warning There is no error checking done.
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_PUSHREF(rb, refData) \
do { \
uint16_t wr_ptr = (rb)->write; \
refData = &(rb)->buffer[wr_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
++(rb)->write; \
} while(0)
/** \brief Return reference to next available slot
No push is performed
\param[in] rb pointer to the ring buffer
\param[out] refData to available item slot
\warning There is no error checking done.
*/
#define RINGBUFFER_GETREFNEXT(rb, refData) \
do { \
refData = &(rb)->buffer[(rb)->write % RINGBUFFER_MAXSIZE(rb)]; \
} while(0)
/** \brief Return reference to next available slot
No push is performed
\param[in] rb pointer to the ring buffer
\param[out] refData to available item slot
\warning There is no error checking done.
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_GETREFNEXT(rb, refData) \
do { \
uint16_t wr_ptr = (rb)->write; \
refData = &(rb)->buffer[wr_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
} while(0)
/** \brief Increment write counter
Actually performed a push (assuming data were already copied)
\param[in] rb pointer to the ring buffer
\warning There is no error checking done.
*/
#define RINGBUFFER_INCREMENT(rb, refData) \
do { \
++(rb)->write; \
} while(0)
/** \brief Increment write counter
Actually performed a push (assuming data were already copied)
\param[in] rb pointer to the ring buffer
\warning There is no error checking done.
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_INCREMENT(rb, refData) \
do { \
++(rb)->write; \
} while(0)
/** \brief Return reference to youngest item
\param[in] rb pointer to the ring buffer
\param[out] refData reference to youngest item
\warning There is no error checking done.
*/
#define RINGBUFFER_BACK(rb, refData) \
do { \
refData = &(rb)->buffer[((rb)->write-1) % RINGBUFFER_MAXSIZE(rb)]; \
} while(0)
/** \brief Return reference to youngest item
\param[in] rb pointer to the ring buffer
\param[out] refData reference to youngest item
\warning There is no error checking done.
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_BACK(rb, refData) \
do { \
uint16_t wr_ptr = (rb)->write; \
refData = &(rb)->buffer[(wr_ptr-1) % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
} while(0)
/** \brief Macro to push an item to a ring buffer
\param[in] rb pointer to the ring buffer
\param[in] ptrData pointer to the item to push.
\warning There is no error checking done.
You must check for fullness before pushing data
*/
#define RINGBUFFER_PUSH(rb, ptrData) \
do { \
(rb)->buffer[(rb)->write % RINGBUFFER_MAXSIZE(rb)] = *ptrData; \
++(rb)->write; \
} while(0)
/** \brief Macro to push an item to a volatile ring buffer
\param[in] rb pointer to the ring buffer
\param[in] ptrData pointer to the item to push.
\warning There is no error checking done.
You must check for fullness before pushing data
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_PUSH(rb, ptrData) \
do { \
uint16_t wr_ptr = (rb)->write; \
(rb)->buffer[wr_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)] = *ptrData; \
++(rb)->write; \
} while(0)
/** \brief Macro to unpush an item to a ring buffer
\param[in] rb pointer to the ring buffer
\param[in] ptrData pointer to placeholder to hold unpushed item
\warning There is no error checking done.
You must check for emptiness before pushing data
*/
#define RINGBUFFER_UNPUSH(rb, ptrData) \
do { \
--(rb)->write; \
*ptrData = (rb)->buffer[(rb)->write % RINGBUFFER_MAXSIZE(rb)]; \
} while(0)
/** \brief Macro to unpush an item to a volatile ring buffer
\param[in] rb pointer to the ring buffer
\param[in] ptrData pointer to placeholder to hold unpushed item
\warning There is no error checking done.
You must check for emptiness before pushing data
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_UNPUSH(rb, ptrData) \
do { \
--(rb)->write; \
uint16_t wr_ptr = (rb)->write; \
*ptrData = (rb)->buffer[wr_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
} while(0)
/** \brief Return reference to oldest item
\param[in] rb pointer to the ring buffer
\param[out] refData reference to oldest item
\warning There is no error checking done.
*/
#define RINGBUFFER_FRONT(rb, refData) \
do { \
refData = &(rb)->buffer[(rb)->read % RINGBUFFER_MAXSIZE(rb)]; \
} while(0)
/** \brief Return reference to oldest item
\param[in] rb pointer to the ring buffer
\param[out] refData reference to oldest item
\warning There is no error checking done.
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_FRONT(rb, refData) \
do { \
uint16_t rd_ptr = (rb)->read; \
refData = &(rb)->buffer[rd_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
} while(0)
/** \brief Macro to pop an item from a ring buffer
\param[in] rb pointer to the ring buffer
\param[out] ptrData pointer to placeholder to hold popped item
\warning There is no error checking done.
You must check for emptiness before popping data
*/
#define RINGBUFFER_POP(rb, ptrData) \
do { \
*ptrData = (rb)->buffer[(rb)->read % RINGBUFFER_MAXSIZE(rb)]; \
++(rb)->read; \
} while(0)
/** \brief Macro to pop an item from a volatile ring buffer
\param[in] rb pointer to the ring buffer
\param[out] ptrData pointer to placeholder to hold popped item
\warning There is no error checking done.
You must check for emptiness before popping data
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_POP(rb, ptrData) \
do { \
uint16_t rd_ptr = (rb)->read; \
*ptrData = (rb)->buffer[rd_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
++(rb)->read; \
} while(0)
/** \brief Macro to pop an item from a ring buffer (data is not copied)
\param[in] rb pointer to the ring buffer
\warning There is no error checking done.
You must check for emptiness before popping data
*/
#define RINGBUFFER_POPNLOSE(rb) \
do { \
++(rb)->read; \
} while(0)
/** \brief Macro to pop an item from a volatile ring buffer (data is not copied)
\param[in] rb pointer to the ring buffer
\warning There is no error checking done.
You must check for emptiness before popping data
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_POPNLOSE(rb) \
do { \
++(rb)->read; \
} while(0)
/** \brief Macro to unpop an item to a ring buffer
\param[in] rb pointer to the ring buffer
\param[out] ptrData pointer to to the item to unpop.
\warning There is no error checking done.
You must check for fullness before unpopping data
*/
#define RINGBUFFER_UNPOP(rb, ptrData) \
do { \
--(rb)->read; \
(rb)->buffer[(rb)->read % RINGBUFFER_MAXSIZE(rb)] = *ptrData; \
} while(0)
/** \brief Macro to unpop an item to a volatile ring buffer
\param[in] rb pointer to the ring buffer
\param[out] ptrData pointer to to the item to unpop.
\warning There is no error checking done.
You must check for fullness before unpopping data
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_UNPOP(rb, ptrData) \
do { \
--(rb)->read; \
uint16_t rd_ptr = (rb)->read; \
(rb)->buffer[rd_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)] = *ptrData; \
} while(0)
#endif
/** \} */
/*
Copyright (c) 2014-2015 InvenSense Inc. Portions Copyright (c) 2014-2015 Movea. All rights reserved.
This software, related documentation and any modifications thereto (collectively "Software") is subject
to InvenSense and its licensors' intellectual property rights under U.S. and international copyright and
other intellectual property rights laws.
InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
and any use, reproduction, disclosure or distribution of the Software without an express license
agreement from InvenSense is strictly prohibited.
*/
/** \defgroup RingBuffer RingBuffer
\brief Macros to manage static circular buffer of any data type
\ingroup EmbUtils
\{
*/
#ifndef _RING_BUFFER_H_
#define _RING_BUFFER_H_
#include <stdint.h>
#include <string.h>
/** \brief Macro to declare a ring buffer
\param[in] type type of item contained in the ring buffer
\param[in] size number of items that can contain the ring buffer
To improve speed, size should be a power of 2
*/
#define RINGBUFFER_DECLARE(type, size) \
struct { \
uint16_t read, write; \
type buffer[size]; \
}
/** \brief Macro to declare a volatile ring buffer, i.e. modified within an interrupt context
\param[in] type type of item contained in the ring buffer
\param[in] size number of items that can contain the ring buffer
To improve speed, size should be a power of 2
*/
#define RINGBUFFER_VOLATILE_DECLARE(type, size) \
struct { \
volatile uint16_t read, write; \
volatile type buffer[size]; \
}
/** \brief Macro to declare a ring buffer
\param[in] name name of the circular buffer
\param[in] size number of items that can contain the ring buffer
To improve speed, size should be a power of 2
\param[in] type type of item contained in the ring buffer
*/
#define RINGBUFFER(name, size, type) RINGBUFFER_DECLARE(type, size) name
/** \brief Macro to declare a volatile ring buffer, i.e. modified within an interrupt context
\param[in] name name of the circular buffer
\param[in] size number of items that can contain the ring buffer
To improve speed, size should be a power of 2
\param[in] type type of item contained in the ring buffer
*/
#define RINGBUFFER_VOLATILE(name, size, type) RINGBUFFER_VOLATILE_DECLARE(type, size) name
/** \brief Macro to get maximum size of a ring buffer
\param[in] rb pointer to the ring buffer
\return maximum number of items that can contain the ringbuffer
*/
#define RINGBUFFER_MAXSIZE(rb) (sizeof((rb)->buffer)/sizeof((rb)->buffer[0]))
/** \brief Macro to get maximum size of a volatile ring buffer, i.e. modified within an interrupt context
\param[in] rb pointer to the ring buffer
\return maximum number of items that can contain the ringbuffer
*/
#define RINGBUFFER_VOLATILE_MAXSIZE(rb) RINGBUFFER_MAXSIZE(rb)
/** \brief Macro to get current size of a ring buffer
\param[in] rb pointer to the ring buffer
\return current number of items hold in the ringbuffer
*/
#define RINGBUFFER_SIZE(rb) ((uint16_t)((rb)->write - (rb)->read))
static inline uint16_t get_ringbuffer_volatile_size(void * rb)
{
struct { uint16_t read, write; } rb_var;
memcpy(&rb_var, rb, sizeof(rb_var));
return (rb_var.write - rb_var.read);
}
/** \brief Macro to get current size of a volatile ring buffer, i.e. modified within an interrupt context
\param[in] rb pointer to the ring buffer
\return current number of items hold in the ringbuffer
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_SIZE(rb) get_ringbuffer_volatile_size(rb)
/** \brief Macro to check if a ring buffer is full
\param[in] rb pointer to the ring buffer
\return 1 if there is no slot left in the ring buffer, 0 otherwise
*/
#define RINGBUFFER_FULL(rb) (RINGBUFFER_SIZE(rb) == RINGBUFFER_MAXSIZE(rb))
/** \brief Macro to check if a volatile ring buffer, i.e. modified within an interrupt context, is full
\param[in] rb pointer to the ring buffer
\return 1 if there is no slot left in the ring buffer, 0 otherwise
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_FULL(rb) (RINGBUFFER_VOLATILE_SIZE(rb) == RINGBUFFER_VOLATILE_MAXSIZE(rb))
/** \brief Macro to check if a ring buffer is empty
\param[in] rb pointer to the ring buffer
\return 1 if there is no item in the ring buffer, 0 otherwise
*/
#define RINGBUFFER_EMPTY(rb) (RINGBUFFER_SIZE(rb) == 0)
/** \brief Macro to check if a volatile ring buffer, i.e. modified within an interrupt context, is empty
\param[in] rb pointer to the ring buffer
\return 1 if there is no item in the ring buffer, 0 otherwise
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_EMPTY(rb) (RINGBUFFER_VOLATILE_SIZE(rb) == 0)
/** \brief Macro to get number of available slot in a ring buffer
\param[in] rb pointer to the ring buffer
\return number of empty slot in the ring buffer
*/
#define RINGBUFFER_AVAILABLE(rb) (RINGBUFFER_MAXSIZE(rb) - RINGBUFFER_SIZE(rb))
/** \brief Macro to get number of available slot in a volatile ring buffer, i.e. modified within an interrupt context
\param[in] rb pointer to the ring buffer
\return number of empty slot in the ring buffer
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_AVAILABLE(rb) (RINGBUFFER_VOLATILE_MAXSIZE(rb) - RINGBUFFER_VOLATILE_SIZE(rb))
/** \brief Macro to clear a ring buffer
\param[in] rb pointer to the ring buffer
*/
#define RINGBUFFER_CLEAR(rb) \
do { \
(rb)->read = 0, (rb)->write = 0; \
} while(0)
/** \brief Macro to clear a volatile ring buffer, i.e. modified within an interrupt context
\param[in] rb pointer to the ring buffer
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_CLEAR(rb) RINGBUFFER_CLEAR(rb)
/** \brief Push item by reference
\param[in] rb pointer to the ring buffer
\param[out] refData to available item slot
\warning There is no error checking done.
*/
#define RINGBUFFER_PUSHREF(rb, refData) \
do { \
refData = &(rb)->buffer[(rb)->write % RINGBUFFER_MAXSIZE(rb)]; \
++(rb)->write; \
} while(0)
/** \brief Push item by reference
\param[in] rb pointer to the ring buffer
\param[out] refData to available item slot
\warning There is no error checking done.
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_PUSHREF(rb, refData) \
do { \
uint16_t wr_ptr = (rb)->write; \
refData = &(rb)->buffer[wr_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
++(rb)->write; \
} while(0)
/** \brief Return reference to next available slot
No push is performed
\param[in] rb pointer to the ring buffer
\param[out] refData to available item slot
\warning There is no error checking done.
*/
#define RINGBUFFER_GETREFNEXT(rb, refData) \
do { \
refData = &(rb)->buffer[(rb)->write % RINGBUFFER_MAXSIZE(rb)]; \
} while(0)
/** \brief Return reference to next available slot
No push is performed
\param[in] rb pointer to the ring buffer
\param[out] refData to available item slot
\warning There is no error checking done.
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_GETREFNEXT(rb, refData) \
do { \
uint16_t wr_ptr = (rb)->write; \
refData = &(rb)->buffer[wr_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
} while(0)
/** \brief Increment write counter
Actually performed a push (assuming data were already copied)
\param[in] rb pointer to the ring buffer
\warning There is no error checking done.
*/
#define RINGBUFFER_INCREMENT(rb, refData) \
do { \
++(rb)->write; \
} while(0)
/** \brief Increment write counter
Actually performed a push (assuming data were already copied)
\param[in] rb pointer to the ring buffer
\warning There is no error checking done.
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_INCREMENT(rb, refData) \
do { \
++(rb)->write; \
} while(0)
/** \brief Return reference to youngest item
\param[in] rb pointer to the ring buffer
\param[out] refData reference to youngest item
\warning There is no error checking done.
*/
#define RINGBUFFER_BACK(rb, refData) \
do { \
refData = &(rb)->buffer[((rb)->write-1) % RINGBUFFER_MAXSIZE(rb)]; \
} while(0)
/** \brief Return reference to youngest item
\param[in] rb pointer to the ring buffer
\param[out] refData reference to youngest item
\warning There is no error checking done.
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_BACK(rb, refData) \
do { \
uint16_t wr_ptr = (rb)->write; \
refData = &(rb)->buffer[(wr_ptr-1) % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
} while(0)
/** \brief Macro to push an item to a ring buffer
\param[in] rb pointer to the ring buffer
\param[in] ptrData pointer to the item to push.
\warning There is no error checking done.
You must check for fullness before pushing data
*/
#define RINGBUFFER_PUSH(rb, ptrData) \
do { \
(rb)->buffer[(rb)->write % RINGBUFFER_MAXSIZE(rb)] = *ptrData; \
++(rb)->write; \
} while(0)
/** \brief Macro to push an item to a volatile ring buffer
\param[in] rb pointer to the ring buffer
\param[in] ptrData pointer to the item to push.
\warning There is no error checking done.
You must check for fullness before pushing data
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_PUSH(rb, ptrData) \
do { \
uint16_t wr_ptr = (rb)->write; \
(rb)->buffer[wr_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)] = *ptrData; \
++(rb)->write; \
} while(0)
/** \brief Macro to unpush an item to a ring buffer
\param[in] rb pointer to the ring buffer
\param[in] ptrData pointer to placeholder to hold unpushed item
\warning There is no error checking done.
You must check for emptiness before pushing data
*/
#define RINGBUFFER_UNPUSH(rb, ptrData) \
do { \
--(rb)->write; \
*ptrData = (rb)->buffer[(rb)->write % RINGBUFFER_MAXSIZE(rb)]; \
} while(0)
/** \brief Macro to unpush an item to a volatile ring buffer
\param[in] rb pointer to the ring buffer
\param[in] ptrData pointer to placeholder to hold unpushed item
\warning There is no error checking done.
You must check for emptiness before pushing data
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_UNPUSH(rb, ptrData) \
do { \
--(rb)->write; \
uint16_t wr_ptr = (rb)->write; \
*ptrData = (rb)->buffer[wr_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
} while(0)
/** \brief Return reference to oldest item
\param[in] rb pointer to the ring buffer
\param[out] refData reference to oldest item
\warning There is no error checking done.
*/
#define RINGBUFFER_FRONT(rb, refData) \
do { \
refData = &(rb)->buffer[(rb)->read % RINGBUFFER_MAXSIZE(rb)]; \
} while(0)
/** \brief Return reference to oldest item
\param[in] rb pointer to the ring buffer
\param[out] refData reference to oldest item
\warning There is no error checking done.
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_FRONT(rb, refData) \
do { \
uint16_t rd_ptr = (rb)->read; \
refData = &(rb)->buffer[rd_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
} while(0)
/** \brief Macro to pop an item from a ring buffer
\param[in] rb pointer to the ring buffer
\param[out] ptrData pointer to placeholder to hold popped item
\warning There is no error checking done.
You must check for emptiness before popping data
*/
#define RINGBUFFER_POP(rb, ptrData) \
do { \
*ptrData = (rb)->buffer[(rb)->read % RINGBUFFER_MAXSIZE(rb)]; \
++(rb)->read; \
} while(0)
/** \brief Macro to pop an item from a volatile ring buffer
\param[in] rb pointer to the ring buffer
\param[out] ptrData pointer to placeholder to hold popped item
\warning There is no error checking done.
You must check for emptiness before popping data
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_POP(rb, ptrData) \
do { \
uint16_t rd_ptr = (rb)->read; \
*ptrData = (rb)->buffer[rd_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)]; \
++(rb)->read; \
} while(0)
/** \brief Macro to pop an item from a ring buffer (data is not copied)
\param[in] rb pointer to the ring buffer
\warning There is no error checking done.
You must check for emptiness before popping data
*/
#define RINGBUFFER_POPNLOSE(rb) \
do { \
++(rb)->read; \
} while(0)
/** \brief Macro to pop an item from a volatile ring buffer (data is not copied)
\param[in] rb pointer to the ring buffer
\warning There is no error checking done.
You must check for emptiness before popping data
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_POPNLOSE(rb) \
do { \
++(rb)->read; \
} while(0)
/** \brief Macro to unpop an item to a ring buffer
\param[in] rb pointer to the ring buffer
\param[out] ptrData pointer to to the item to unpop.
\warning There is no error checking done.
You must check for fullness before unpopping data
*/
#define RINGBUFFER_UNPOP(rb, ptrData) \
do { \
--(rb)->read; \
(rb)->buffer[(rb)->read % RINGBUFFER_MAXSIZE(rb)] = *ptrData; \
} while(0)
/** \brief Macro to unpop an item to a volatile ring buffer
\param[in] rb pointer to the ring buffer
\param[out] ptrData pointer to to the item to unpop.
\warning There is no error checking done.
You must check for fullness before unpopping data
\warning it is advised to put this in a critical section
*/
#define RINGBUFFER_VOLATILE_UNPOP(rb, ptrData) \
do { \
--(rb)->read; \
uint16_t rd_ptr = (rb)->read; \
(rb)->buffer[rd_ptr % RINGBUFFER_VOLATILE_MAXSIZE(rb)] = *ptrData; \
} while(0)
#endif
/** \} */
@@ -1,47 +1,47 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @brief Custom definition for boolean type to avoid compiler discrepancies
* @{
*/
#ifndef _INV_BOOL_H_
#define _INV_BOOL_H_
typedef int inv_bool_t;
#ifndef __cplusplus
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
#endif /* __cplusplus */
#endif /* _INV_BOOL_H_ */
/** @} */
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @brief Custom definition for boolean type to avoid compiler discrepancies
* @{
*/
#ifndef _INV_BOOL_H_
#define _INV_BOOL_H_
typedef int inv_bool_t;
#ifndef __cplusplus
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
#endif /* __cplusplus */
#endif /* _INV_BOOL_H_ */
/** @} */
@@ -1,64 +1,64 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup InvError Error code
* @brief Common error code
*
* @ingroup EmbUtils
* @{
*/
#ifndef _INV_ERROR_H_
#define _INV_ERROR_H_
/** @brief Common error code definition
*/
enum inv_error
{
INV_ERROR_SUCCESS = 0, /**< no error */
INV_ERROR = -1, /**< unspecified error */
INV_ERROR_NIMPL = -2, /**< function not implemented for given
arguments */
INV_ERROR_TRANSPORT = -3, /**< error occurred at transport level */
INV_ERROR_TIMEOUT = -4, /**< action did not complete in the expected
time window */
INV_ERROR_SIZE = -5, /**< size/length of given arguments is not
suitable to complete requested action */
INV_ERROR_OS = -6, /**< error related to OS */
INV_ERROR_IO = -7, /**< error related to IO operation */
INV_ERROR_MEM = -9, /**< not enough memory to complete requested
action */
INV_ERROR_HW = -10, /**< error at HW level */
INV_ERROR_BAD_ARG = -11, /**< provided arguments are not good to
perform requested action */
INV_ERROR_UNEXPECTED = -12, /**< something unexpected happened */
INV_ERROR_FILE = -13, /**< cannot access file or unexpected format */
INV_ERROR_PATH = -14, /**< invalid file path */
INV_ERROR_IMAGE_TYPE = -15, /**< error when image type is not managed */
INV_ERROR_WATCHDOG = -16, /**< error when device doesn't respond
to ping */
};
#endif /* _INV_ERROR_H_ */
/** @} */
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup InvError Error code
* @brief Common error code
*
* @ingroup EmbUtils
* @{
*/
#ifndef _INV_ERROR_H_
#define _INV_ERROR_H_
/** @brief Common error code definition
*/
enum inv_error
{
INV_ERROR_SUCCESS = 0, /**< no error */
INV_ERROR = -1, /**< unspecified error */
INV_ERROR_NIMPL = -2, /**< function not implemented for given
arguments */
INV_ERROR_TRANSPORT = -3, /**< error occurred at transport level */
INV_ERROR_TIMEOUT = -4, /**< action did not complete in the expected
time window */
INV_ERROR_SIZE = -5, /**< size/length of given arguments is not
suitable to complete requested action */
INV_ERROR_OS = -6, /**< error related to OS */
INV_ERROR_IO = -7, /**< error related to IO operation */
INV_ERROR_MEM = -9, /**< not enough memory to complete requested
action */
INV_ERROR_HW = -10, /**< error at HW level */
INV_ERROR_BAD_ARG = -11, /**< provided arguments are not good to
perform requested action */
INV_ERROR_UNEXPECTED = -12, /**< something unexpected happened */
INV_ERROR_FILE = -13, /**< cannot access file or unexpected format */
INV_ERROR_PATH = -14, /**< invalid file path */
INV_ERROR_IMAGE_TYPE = -15, /**< error when image type is not managed */
INV_ERROR_WATCHDOG = -16, /**< error when device doesn't respond
to ping */
};
#endif /* _INV_ERROR_H_ */
/** @} */
@@ -1,39 +1,39 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#ifndef _INV_IDD_EXPORT_H_
#define _INV_IDD_EXPORT_H_
#if defined(_WIN32)
#if !defined(INV_EXPORT) && defined(INV_DO_DLL_EXPORT)
#define INV_EXPORT __declspec(dllexport)
#elif !defined(INV_EXPORT) && defined(INV_DO_DLL_IMPORT)
#define INV_EXPORT __declspec(dllimport)
#endif
#endif
#if !defined(INV_EXPORT)
#define INV_EXPORT
#endif
#endif /* _INV_IDD_EXPORT_H_ */
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#ifndef _INV_IDD_EXPORT_H_
#define _INV_IDD_EXPORT_H_
#if defined(_WIN32)
#if !defined(INV_EXPORT) && defined(INV_DO_DLL_EXPORT)
#define INV_EXPORT __declspec(dllexport)
#elif !defined(INV_EXPORT) && defined(INV_DO_DLL_IMPORT)
#define INV_EXPORT __declspec(dllimport)
#endif
#endif
#if !defined(INV_EXPORT)
#define INV_EXPORT
#endif
#endif /* _INV_IDD_EXPORT_H_ */
@@ -1,370 +1,370 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2017 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#include "inv_imu_defs.h"
#include "inv_imu_extfunc.h"
#include "inv_imu_driver.h"
#include "inv_imu_apex.h"
int inv_imu_apex_enable_ff(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
status |= inv_imu_start_dmp(s);
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_FF_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_FF_ENABLE_EN;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_disable_ff(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_FF_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_FF_ENABLE_DIS;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_enable_smd(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
status |= inv_imu_start_dmp(s);
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_SMD_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_SMD_ENABLE_EN;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_disable_smd(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_SMD_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_SMD_ENABLE_DIS;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_init_parameters_struct(struct inv_imu_device *s, inv_imu_apex_parameters_t *apex_inputs)
{
int status = 0;
(void)s;
/* Default parameters at POR */
apex_inputs->pedo_amp_th = APEX_CONFIG3_PEDO_AMP_TH_62_MG;
apex_inputs->pedo_step_cnt_th = 0x5;
apex_inputs->pedo_step_det_th = 0x2;
apex_inputs->pedo_sb_timer_th = APEX_CONFIG4_PEDO_SB_TIMER_TH_150_SAMPLES;
apex_inputs->pedo_hi_enrgy_th = APEX_CONFIG4_PEDO_HI_ENRGY_TH_104_MG;
apex_inputs->tilt_wait_time = APEX_CONFIG5_TILT_WAIT_TIME_4_S;
apex_inputs->power_save_time = APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_8_S;
apex_inputs->power_save = APEX_CONFIG0_DMP_POWER_SAVE_EN;
apex_inputs->sensitivity_mode = APEX_CONFIG9_SENSITIVITY_MODE_NORMAL;
apex_inputs->low_energy_amp_th = APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_80_MG;
apex_inputs->smd_sensitivity = APEX_CONFIG9_SMD_SENSITIVITY_0;
apex_inputs->ff_debounce_duration = APEX_CONFIG9_FF_DEBOUNCE_DURATION_2000_MS;
apex_inputs->ff_max_duration_cm = APEX_CONFIG12_FF_MAX_DURATION_204_CM;
apex_inputs->ff_min_duration_cm = APEX_CONFIG12_FF_MIN_DURATION_10_CM;
apex_inputs->lowg_peak_th = APEX_CONFIG10_LOWG_PEAK_TH_563_MG;
apex_inputs->lowg_peak_hyst = APEX_CONFIG5_LOWG_PEAK_TH_HYST_156_MG;
apex_inputs->lowg_samples_th = APEX_CONFIG10_LOWG_TIME_TH_1_SAMPLE;
apex_inputs->highg_peak_th = APEX_CONFIG11_HIGHG_PEAK_TH_2500_MG;
apex_inputs->highg_peak_hyst = APEX_CONFIG5_HIGHG_PEAK_TH_HYST_156_MG;
apex_inputs->highg_samples_th = APEX_CONFIG11_HIGHG_TIME_TH_1_SAMPLE;
return status;
}
int inv_imu_apex_configure_parameters(struct inv_imu_device *s, const inv_imu_apex_parameters_t *apex_inputs)
{
int status = 0;
uint8_t data;
uint8_t apexConfig[7];
APEX_CONFIG1_PED_ENABLE_t pedo_state;
APEX_CONFIG1_TILT_ENABLE_t tilt_state;
APEX_CONFIG1_FF_ENABLE_t ff_state;
APEX_CONFIG1_SMD_ENABLE_t smd_state;
/* DMP cannot be configured if it is running, hence make sure all APEX algorithms are off */
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &data);
pedo_state = (APEX_CONFIG1_PED_ENABLE_t)(data & APEX_CONFIG1_PED_ENABLE_MASK);
tilt_state = (APEX_CONFIG1_TILT_ENABLE_t)(data & APEX_CONFIG1_TILT_ENABLE_MASK);
ff_state = (APEX_CONFIG1_FF_ENABLE_t)(data & APEX_CONFIG1_FF_ENABLE_MASK);
smd_state = (APEX_CONFIG1_SMD_ENABLE_t)(data & APEX_CONFIG1_SMD_ENABLE_MASK);
if (pedo_state == APEX_CONFIG1_PED_ENABLE_EN)
return INV_ERROR;
if (tilt_state == APEX_CONFIG1_TILT_ENABLE_EN)
return INV_ERROR;
if (ff_state == APEX_CONFIG1_FF_ENABLE_EN)
return INV_ERROR;
if (smd_state == APEX_CONFIG1_SMD_ENABLE_EN)
return INV_ERROR;
status |= inv_imu_switch_on_mclk(s);
/* Power Save mode and low energy amplitude threshold (for Pedometer in Slow Walk mode) */
/* APEX_CONFIG2_MREG1 */
apexConfig[0] = (uint8_t)apex_inputs->power_save_time
| (uint8_t)apex_inputs->low_energy_amp_th;
/* Pedometer parameters */
/* APEX_CONFIG3_MREG1 */
apexConfig[1] = (uint8_t)apex_inputs->pedo_amp_th
| (apex_inputs->pedo_step_cnt_th & APEX_CONFIG3_PED_STEP_CNT_TH_SEL_MASK);
/* APEX_CONFIG4_MREG1 */
apexConfig[2] = ((apex_inputs->pedo_step_det_th << APEX_CONFIG4_PED_STEP_DET_TH_SEL_POS)
& APEX_CONFIG4_PED_STEP_DET_TH_SEL_MASK)
| (uint8_t)apex_inputs->pedo_sb_timer_th
| (uint8_t)apex_inputs->pedo_hi_enrgy_th;
/* Tilt, Lowg and highg parameters */
/* APEX_CONFIG5_MREG1 */
apexConfig[3] = (uint8_t)apex_inputs->tilt_wait_time
| (uint8_t)apex_inputs->lowg_peak_hyst
| (uint8_t)apex_inputs->highg_peak_hyst;
status |= inv_imu_write_reg(s, APEX_CONFIG2_MREG1, 4, &apexConfig[0]);
/* APEX_CONFIG0 */
status |= inv_imu_read_reg(s, APEX_CONFIG0, 1, &apexConfig[0]);
apexConfig[0] &= ~APEX_CONFIG0_DMP_POWER_SAVE_EN_MASK;
apexConfig[0] |= apex_inputs->power_save;
status |= inv_imu_write_reg(s, APEX_CONFIG0, 1, &apexConfig[0]);
/* free fall parameter, SMD parameter and parameters for Pedometer in Slow Walk mode */
/* APEX_CONFIG9_MREG1 */
apexConfig[0] = (uint8_t)apex_inputs->ff_debounce_duration
| (uint8_t)apex_inputs->smd_sensitivity
| (uint8_t)apex_inputs->sensitivity_mode;
/* Lowg and highg parameters and free fall parameters */
/* APEX_CONFIG10_MREG1 */
apexConfig[1] = (uint8_t)apex_inputs->lowg_peak_th
| (uint8_t)apex_inputs->lowg_samples_th;
/* APEX_CONFIG11_MREG1 */
apexConfig[2] = (uint8_t)apex_inputs->highg_peak_th
| (uint8_t)apex_inputs->highg_samples_th;
status |= inv_imu_write_reg(s, APEX_CONFIG9_MREG1, 3, &apexConfig[0]);
/* APEX_CONFIG12_MREG1 */
apexConfig[0] = (uint8_t)apex_inputs->ff_max_duration_cm
| (uint8_t)apex_inputs->ff_min_duration_cm;
status |= inv_imu_write_reg(s, APEX_CONFIG12_MREG1, 1, &apexConfig[0]);
status |= inv_imu_switch_off_mclk(s);
return status;
}
int inv_imu_apex_get_parameters(struct inv_imu_device *s, inv_imu_apex_parameters_t *apex_params)
{
int status = 0;
uint8_t data[7];
uint8_t value;
status |= inv_imu_read_reg(s, APEX_CONFIG0, 1, &value);
apex_params->power_save = (APEX_CONFIG0_DMP_POWER_SAVE_t)(value & APEX_CONFIG0_DMP_POWER_SAVE_EN_MASK);
/* Access continuous config registers (CONFIG2-CONFIG11) */
status |= inv_imu_read_reg(s, APEX_CONFIG2_MREG1, sizeof(data), &data[0]);
/* Get params from apex_config2 : dmp_power_save_time and low_energy_amp_th */
apex_params->power_save_time = (APEX_CONFIG2_DMP_POWER_SAVE_TIME_t)
(data[0] & APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_MASK);
apex_params->low_energy_amp_th = (APEX_CONFIG2_LOW_ENERGY_AMP_TH_t)
(data[0] & APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_MASK);
/* Get params from apex_config3 : pedo_amp_th and pedo_step_cnt_th */
apex_params->pedo_amp_th = (APEX_CONFIG3_PEDO_AMP_TH_t)
(data[1] & APEX_CONFIG3_PED_AMP_TH_SEL_MASK);
apex_params->pedo_step_cnt_th = (data[1] & APEX_CONFIG3_PED_STEP_CNT_TH_SEL_MASK)
>> APEX_CONFIG3_PED_STEP_CNT_TH_SEL_POS;
/* Get params from apex_config4 : pedo_step_det_th, pedo_sb_timer_th and pedo_hi_enrgy_th */
apex_params->pedo_step_det_th = (data[2] & APEX_CONFIG4_PED_STEP_DET_TH_SEL_MASK)
>> APEX_CONFIG4_PED_STEP_DET_TH_SEL_POS;
apex_params->pedo_sb_timer_th = (APEX_CONFIG4_PEDO_SB_TIMER_TH_t)
(data[2] & APEX_CONFIG4_PED_SB_TIMER_TH_SEL_MASK);
apex_params->pedo_hi_enrgy_th = (APEX_CONFIG4_PEDO_HI_ENRGY_TH_t)
(data[2] & APEX_CONFIG4_PED_HI_EN_TH_SEL_MASK);
/* Get params from apex_config5 : tilt_wait_time, lowg_peak_hyst and highg_peak_hyst */
apex_params->tilt_wait_time = (APEX_CONFIG5_TILT_WAIT_TIME_t)
(data[3] & APEX_CONFIG5_TILT_WAIT_TIME_SEL_MASK);
apex_params->lowg_peak_hyst = (APEX_CONFIG5_LOWG_PEAK_TH_HYST_t)
(data[3] & APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_MASK);
apex_params->highg_peak_hyst = (APEX_CONFIG5_HIGHG_PEAK_TH_HYST_t)
(data[3] & APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_MASK);
/* Get params from apex_config9 : ff_debounce_duration, smd_sensitivity and sensitivity_mode */
apex_params->ff_debounce_duration = (APEX_CONFIG9_FF_DEBOUNCE_DURATION_t)
(data[4] & APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_MASK);
apex_params->smd_sensitivity = (APEX_CONFIG9_SMD_SENSITIVITY_t)
(data[4] & APEX_CONFIG9_SMD_SENSITIVITY_SEL_MASK);
apex_params->sensitivity_mode = (APEX_CONFIG9_SENSITIVITY_MODE_t)
(data[4] & APEX_CONFIG9_SENSITIVITY_MODE_MASK);
/* Get params from apex_config10 : lowg_peak_th and lowg_samples_th */
apex_params->lowg_peak_th = (APEX_CONFIG10_LOWG_PEAK_TH_t)
(data[5] & APEX_CONFIG10_LOWG_PEAK_TH_SEL_MASK);
apex_params->lowg_samples_th = (APEX_CONFIG10_LOWG_TIME_TH_SAMPLES_t)
(data[5] & APEX_CONFIG10_LOWG_TIME_TH_SEL_MASK);
/* Get params from apex_config11 : highg_peak_th and highg_samples_th */
apex_params->highg_peak_th = (APEX_CONFIG11_HIGHG_PEAK_TH_t)
(data[6] & APEX_CONFIG11_HIGHG_PEAK_TH_SEL_MASK);
apex_params->highg_samples_th = (APEX_CONFIG11_HIGHG_TIME_TH_SAMPLES_t)
(data[6] & APEX_CONFIG11_HIGHG_TIME_TH_SEL_MASK);
/* Access apex reg 12 */
status |= inv_imu_read_reg(s, APEX_CONFIG12_MREG1, 1, &data[0]);
/* Get params from apex_config12 : ff_max_duration_cm and ff_min_duration_cm */
apex_params->ff_max_duration_cm = (APEX_CONFIG12_FF_MAX_DURATION_t)
(data[0] & APEX_CONFIG12_FF_MAX_DURATION_SEL_MASK);
apex_params->ff_min_duration_cm = (APEX_CONFIG12_FF_MIN_DURATION_t)
(data[0] & APEX_CONFIG12_FF_MIN_DURATION_SEL_MASK);
return status;
}
int inv_imu_apex_set_frequency(struct inv_imu_device *s, const APEX_CONFIG1_DMP_ODR_t frequency)
{
uint8_t value;
int status = 0;
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_DMP_ODR_MASK;
value |= frequency;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_enable_pedometer(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
status |= inv_imu_start_dmp(s);
/* Enable Pedometer */
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_PED_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_PED_ENABLE_EN;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_disable_pedometer(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
/* Disable Pedometer */
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_PED_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_PED_ENABLE_DIS;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_enable_tilt(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
status |= inv_imu_start_dmp(s);
/* Enable Tilt */
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_TILT_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_TILT_ENABLE_EN;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_disable_tilt(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
/* Disable Tilt */
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_TILT_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_TILT_ENABLE_DIS;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_get_data_activity(struct inv_imu_device *s, inv_imu_apex_step_activity_t *apex_activity)
{
uint8_t data[4];
int status = inv_imu_read_reg(s, APEX_DATA0, 4, data);
apex_activity->step_cnt = data[1] << 8 | data[0];
apex_activity->step_cadence = data[2];
apex_activity->activity_class = data[3] & APEX_DATA3_ACTIVITY_CLASS_MASK;
return status;
}
int inv_imu_apex_get_data_free_fall(struct inv_imu_device *s, uint16_t *freefall_duration)
{
uint8_t data[2];
int status = inv_imu_read_reg(s, APEX_DATA4, 2, &data[0]);
*freefall_duration = (data[1] << 8) | data[0];
return status;
}
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2017 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#include "inv_imu_defs.h"
#include "inv_imu_extfunc.h"
#include "inv_imu_driver.h"
#include "inv_imu_apex.h"
int inv_imu_apex_enable_ff(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
status |= inv_imu_start_dmp(s);
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_FF_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_FF_ENABLE_EN;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_disable_ff(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_FF_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_FF_ENABLE_DIS;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_enable_smd(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
status |= inv_imu_start_dmp(s);
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_SMD_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_SMD_ENABLE_EN;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_disable_smd(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_SMD_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_SMD_ENABLE_DIS;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_init_parameters_struct(struct inv_imu_device *s, inv_imu_apex_parameters_t *apex_inputs)
{
int status = 0;
(void)s;
/* Default parameters at POR */
apex_inputs->pedo_amp_th = APEX_CONFIG3_PEDO_AMP_TH_62_MG;
apex_inputs->pedo_step_cnt_th = 0x5;
apex_inputs->pedo_step_det_th = 0x2;
apex_inputs->pedo_sb_timer_th = APEX_CONFIG4_PEDO_SB_TIMER_TH_150_SAMPLES;
apex_inputs->pedo_hi_enrgy_th = APEX_CONFIG4_PEDO_HI_ENRGY_TH_104_MG;
apex_inputs->tilt_wait_time = APEX_CONFIG5_TILT_WAIT_TIME_4_S;
apex_inputs->power_save_time = APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_8_S;
apex_inputs->power_save = APEX_CONFIG0_DMP_POWER_SAVE_EN;
apex_inputs->sensitivity_mode = APEX_CONFIG9_SENSITIVITY_MODE_NORMAL;
apex_inputs->low_energy_amp_th = APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_80_MG;
apex_inputs->smd_sensitivity = APEX_CONFIG9_SMD_SENSITIVITY_0;
apex_inputs->ff_debounce_duration = APEX_CONFIG9_FF_DEBOUNCE_DURATION_2000_MS;
apex_inputs->ff_max_duration_cm = APEX_CONFIG12_FF_MAX_DURATION_204_CM;
apex_inputs->ff_min_duration_cm = APEX_CONFIG12_FF_MIN_DURATION_10_CM;
apex_inputs->lowg_peak_th = APEX_CONFIG10_LOWG_PEAK_TH_563_MG;
apex_inputs->lowg_peak_hyst = APEX_CONFIG5_LOWG_PEAK_TH_HYST_156_MG;
apex_inputs->lowg_samples_th = APEX_CONFIG10_LOWG_TIME_TH_1_SAMPLE;
apex_inputs->highg_peak_th = APEX_CONFIG11_HIGHG_PEAK_TH_2500_MG;
apex_inputs->highg_peak_hyst = APEX_CONFIG5_HIGHG_PEAK_TH_HYST_156_MG;
apex_inputs->highg_samples_th = APEX_CONFIG11_HIGHG_TIME_TH_1_SAMPLE;
return status;
}
int inv_imu_apex_configure_parameters(struct inv_imu_device *s, const inv_imu_apex_parameters_t *apex_inputs)
{
int status = 0;
uint8_t data;
uint8_t apexConfig[7];
APEX_CONFIG1_PED_ENABLE_t pedo_state;
APEX_CONFIG1_TILT_ENABLE_t tilt_state;
APEX_CONFIG1_FF_ENABLE_t ff_state;
APEX_CONFIG1_SMD_ENABLE_t smd_state;
/* DMP cannot be configured if it is running, hence make sure all APEX algorithms are off */
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &data);
pedo_state = (APEX_CONFIG1_PED_ENABLE_t)(data & APEX_CONFIG1_PED_ENABLE_MASK);
tilt_state = (APEX_CONFIG1_TILT_ENABLE_t)(data & APEX_CONFIG1_TILT_ENABLE_MASK);
ff_state = (APEX_CONFIG1_FF_ENABLE_t)(data & APEX_CONFIG1_FF_ENABLE_MASK);
smd_state = (APEX_CONFIG1_SMD_ENABLE_t)(data & APEX_CONFIG1_SMD_ENABLE_MASK);
if (pedo_state == APEX_CONFIG1_PED_ENABLE_EN)
return INV_ERROR;
if (tilt_state == APEX_CONFIG1_TILT_ENABLE_EN)
return INV_ERROR;
if (ff_state == APEX_CONFIG1_FF_ENABLE_EN)
return INV_ERROR;
if (smd_state == APEX_CONFIG1_SMD_ENABLE_EN)
return INV_ERROR;
status |= inv_imu_switch_on_mclk(s);
/* Power Save mode and low energy amplitude threshold (for Pedometer in Slow Walk mode) */
/* APEX_CONFIG2_MREG1 */
apexConfig[0] = (uint8_t)apex_inputs->power_save_time
| (uint8_t)apex_inputs->low_energy_amp_th;
/* Pedometer parameters */
/* APEX_CONFIG3_MREG1 */
apexConfig[1] = (uint8_t)apex_inputs->pedo_amp_th
| (apex_inputs->pedo_step_cnt_th & APEX_CONFIG3_PED_STEP_CNT_TH_SEL_MASK);
/* APEX_CONFIG4_MREG1 */
apexConfig[2] = ((apex_inputs->pedo_step_det_th << APEX_CONFIG4_PED_STEP_DET_TH_SEL_POS)
& APEX_CONFIG4_PED_STEP_DET_TH_SEL_MASK)
| (uint8_t)apex_inputs->pedo_sb_timer_th
| (uint8_t)apex_inputs->pedo_hi_enrgy_th;
/* Tilt, Lowg and highg parameters */
/* APEX_CONFIG5_MREG1 */
apexConfig[3] = (uint8_t)apex_inputs->tilt_wait_time
| (uint8_t)apex_inputs->lowg_peak_hyst
| (uint8_t)apex_inputs->highg_peak_hyst;
status |= inv_imu_write_reg(s, APEX_CONFIG2_MREG1, 4, &apexConfig[0]);
/* APEX_CONFIG0 */
status |= inv_imu_read_reg(s, APEX_CONFIG0, 1, &apexConfig[0]);
apexConfig[0] &= ~APEX_CONFIG0_DMP_POWER_SAVE_EN_MASK;
apexConfig[0] |= apex_inputs->power_save;
status |= inv_imu_write_reg(s, APEX_CONFIG0, 1, &apexConfig[0]);
/* free fall parameter, SMD parameter and parameters for Pedometer in Slow Walk mode */
/* APEX_CONFIG9_MREG1 */
apexConfig[0] = (uint8_t)apex_inputs->ff_debounce_duration
| (uint8_t)apex_inputs->smd_sensitivity
| (uint8_t)apex_inputs->sensitivity_mode;
/* Lowg and highg parameters and free fall parameters */
/* APEX_CONFIG10_MREG1 */
apexConfig[1] = (uint8_t)apex_inputs->lowg_peak_th
| (uint8_t)apex_inputs->lowg_samples_th;
/* APEX_CONFIG11_MREG1 */
apexConfig[2] = (uint8_t)apex_inputs->highg_peak_th
| (uint8_t)apex_inputs->highg_samples_th;
status |= inv_imu_write_reg(s, APEX_CONFIG9_MREG1, 3, &apexConfig[0]);
/* APEX_CONFIG12_MREG1 */
apexConfig[0] = (uint8_t)apex_inputs->ff_max_duration_cm
| (uint8_t)apex_inputs->ff_min_duration_cm;
status |= inv_imu_write_reg(s, APEX_CONFIG12_MREG1, 1, &apexConfig[0]);
status |= inv_imu_switch_off_mclk(s);
return status;
}
int inv_imu_apex_get_parameters(struct inv_imu_device *s, inv_imu_apex_parameters_t *apex_params)
{
int status = 0;
uint8_t data[7];
uint8_t value;
status |= inv_imu_read_reg(s, APEX_CONFIG0, 1, &value);
apex_params->power_save = (APEX_CONFIG0_DMP_POWER_SAVE_t)(value & APEX_CONFIG0_DMP_POWER_SAVE_EN_MASK);
/* Access continuous config registers (CONFIG2-CONFIG11) */
status |= inv_imu_read_reg(s, APEX_CONFIG2_MREG1, sizeof(data), &data[0]);
/* Get params from apex_config2 : dmp_power_save_time and low_energy_amp_th */
apex_params->power_save_time = (APEX_CONFIG2_DMP_POWER_SAVE_TIME_t)
(data[0] & APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_MASK);
apex_params->low_energy_amp_th = (APEX_CONFIG2_LOW_ENERGY_AMP_TH_t)
(data[0] & APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_MASK);
/* Get params from apex_config3 : pedo_amp_th and pedo_step_cnt_th */
apex_params->pedo_amp_th = (APEX_CONFIG3_PEDO_AMP_TH_t)
(data[1] & APEX_CONFIG3_PED_AMP_TH_SEL_MASK);
apex_params->pedo_step_cnt_th = (data[1] & APEX_CONFIG3_PED_STEP_CNT_TH_SEL_MASK)
>> APEX_CONFIG3_PED_STEP_CNT_TH_SEL_POS;
/* Get params from apex_config4 : pedo_step_det_th, pedo_sb_timer_th and pedo_hi_enrgy_th */
apex_params->pedo_step_det_th = (data[2] & APEX_CONFIG4_PED_STEP_DET_TH_SEL_MASK)
>> APEX_CONFIG4_PED_STEP_DET_TH_SEL_POS;
apex_params->pedo_sb_timer_th = (APEX_CONFIG4_PEDO_SB_TIMER_TH_t)
(data[2] & APEX_CONFIG4_PED_SB_TIMER_TH_SEL_MASK);
apex_params->pedo_hi_enrgy_th = (APEX_CONFIG4_PEDO_HI_ENRGY_TH_t)
(data[2] & APEX_CONFIG4_PED_HI_EN_TH_SEL_MASK);
/* Get params from apex_config5 : tilt_wait_time, lowg_peak_hyst and highg_peak_hyst */
apex_params->tilt_wait_time = (APEX_CONFIG5_TILT_WAIT_TIME_t)
(data[3] & APEX_CONFIG5_TILT_WAIT_TIME_SEL_MASK);
apex_params->lowg_peak_hyst = (APEX_CONFIG5_LOWG_PEAK_TH_HYST_t)
(data[3] & APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_MASK);
apex_params->highg_peak_hyst = (APEX_CONFIG5_HIGHG_PEAK_TH_HYST_t)
(data[3] & APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_MASK);
/* Get params from apex_config9 : ff_debounce_duration, smd_sensitivity and sensitivity_mode */
apex_params->ff_debounce_duration = (APEX_CONFIG9_FF_DEBOUNCE_DURATION_t)
(data[4] & APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_MASK);
apex_params->smd_sensitivity = (APEX_CONFIG9_SMD_SENSITIVITY_t)
(data[4] & APEX_CONFIG9_SMD_SENSITIVITY_SEL_MASK);
apex_params->sensitivity_mode = (APEX_CONFIG9_SENSITIVITY_MODE_t)
(data[4] & APEX_CONFIG9_SENSITIVITY_MODE_MASK);
/* Get params from apex_config10 : lowg_peak_th and lowg_samples_th */
apex_params->lowg_peak_th = (APEX_CONFIG10_LOWG_PEAK_TH_t)
(data[5] & APEX_CONFIG10_LOWG_PEAK_TH_SEL_MASK);
apex_params->lowg_samples_th = (APEX_CONFIG10_LOWG_TIME_TH_SAMPLES_t)
(data[5] & APEX_CONFIG10_LOWG_TIME_TH_SEL_MASK);
/* Get params from apex_config11 : highg_peak_th and highg_samples_th */
apex_params->highg_peak_th = (APEX_CONFIG11_HIGHG_PEAK_TH_t)
(data[6] & APEX_CONFIG11_HIGHG_PEAK_TH_SEL_MASK);
apex_params->highg_samples_th = (APEX_CONFIG11_HIGHG_TIME_TH_SAMPLES_t)
(data[6] & APEX_CONFIG11_HIGHG_TIME_TH_SEL_MASK);
/* Access apex reg 12 */
status |= inv_imu_read_reg(s, APEX_CONFIG12_MREG1, 1, &data[0]);
/* Get params from apex_config12 : ff_max_duration_cm and ff_min_duration_cm */
apex_params->ff_max_duration_cm = (APEX_CONFIG12_FF_MAX_DURATION_t)
(data[0] & APEX_CONFIG12_FF_MAX_DURATION_SEL_MASK);
apex_params->ff_min_duration_cm = (APEX_CONFIG12_FF_MIN_DURATION_t)
(data[0] & APEX_CONFIG12_FF_MIN_DURATION_SEL_MASK);
return status;
}
int inv_imu_apex_set_frequency(struct inv_imu_device *s, const APEX_CONFIG1_DMP_ODR_t frequency)
{
uint8_t value;
int status = 0;
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_DMP_ODR_MASK;
value |= frequency;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_enable_pedometer(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
status |= inv_imu_start_dmp(s);
/* Enable Pedometer */
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_PED_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_PED_ENABLE_EN;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_disable_pedometer(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
/* Disable Pedometer */
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_PED_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_PED_ENABLE_DIS;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_enable_tilt(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
status |= inv_imu_start_dmp(s);
/* Enable Tilt */
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_TILT_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_TILT_ENABLE_EN;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_disable_tilt(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
/* Disable Tilt */
status |= inv_imu_read_reg(s, APEX_CONFIG1, 1, &value);
value &= ~APEX_CONFIG1_TILT_ENABLE_MASK;
value |= (uint8_t)APEX_CONFIG1_TILT_ENABLE_DIS;
status |= inv_imu_write_reg(s, APEX_CONFIG1, 1, &value);
return status;
}
int inv_imu_apex_get_data_activity(struct inv_imu_device *s, inv_imu_apex_step_activity_t *apex_activity)
{
uint8_t data[4];
int status = inv_imu_read_reg(s, APEX_DATA0, 4, data);
apex_activity->step_cnt = data[1] << 8 | data[0];
apex_activity->step_cadence = data[2];
apex_activity->activity_class = data[3] & APEX_DATA3_ACTIVITY_CLASS_MASK;
return status;
}
int inv_imu_apex_get_data_free_fall(struct inv_imu_device *s, uint16_t *freefall_duration)
{
uint8_t data[2];
int status = inv_imu_read_reg(s, APEX_DATA4, 2, &data[0]);
*freefall_duration = (data[1] << 8) | data[0];
return status;
}
@@ -1,185 +1,185 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2017 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup DriverApex IMU driver high level functions related to APEX and the DMP
* @brief High-level function to setup an IMU device
* @ingroup Driver
* @{
*/
/** @file inv_imu_apex.h
* High-level function to setup an IMU device
*/
#ifndef _INV_IMU_APEX_H_
#define _INV_IMU_APEX_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "inv_imu_defs.h"
#include "InvError.h"
#include <stdint.h>
#include <string.h>
/* Forward declarations */
struct inv_imu_device;
/** @brief IMU APEX inputs parameters definition
*/
typedef struct {
APEX_CONFIG3_PEDO_AMP_TH_t pedo_amp_th;
uint8_t pedo_step_cnt_th;
uint8_t pedo_step_det_th;
APEX_CONFIG4_PEDO_SB_TIMER_TH_t pedo_sb_timer_th;
APEX_CONFIG4_PEDO_HI_ENRGY_TH_t pedo_hi_enrgy_th;
APEX_CONFIG5_TILT_WAIT_TIME_t tilt_wait_time;
APEX_CONFIG2_DMP_POWER_SAVE_TIME_t power_save_time;
APEX_CONFIG0_DMP_POWER_SAVE_t power_save;
APEX_CONFIG9_SENSITIVITY_MODE_t sensitivity_mode;
APEX_CONFIG2_LOW_ENERGY_AMP_TH_t low_energy_amp_th;
APEX_CONFIG9_SMD_SENSITIVITY_t smd_sensitivity;
APEX_CONFIG9_FF_DEBOUNCE_DURATION_t ff_debounce_duration;
APEX_CONFIG12_FF_MAX_DURATION_t ff_max_duration_cm;
APEX_CONFIG12_FF_MIN_DURATION_t ff_min_duration_cm;
APEX_CONFIG10_LOWG_PEAK_TH_t lowg_peak_th;
APEX_CONFIG5_LOWG_PEAK_TH_HYST_t lowg_peak_hyst;
APEX_CONFIG10_LOWG_TIME_TH_SAMPLES_t lowg_samples_th;
APEX_CONFIG11_HIGHG_PEAK_TH_t highg_peak_th;
APEX_CONFIG5_HIGHG_PEAK_TH_HYST_t highg_peak_hyst;
APEX_CONFIG11_HIGHG_TIME_TH_SAMPLES_t highg_samples_th;
} inv_imu_apex_parameters_t;
/** @brief APEX pedometer outputs
*/
typedef struct inv_imu_apex_step_activity {
uint16_t step_cnt; /**< Number of steps taken */
uint8_t step_cadence; /**< Walk/run cadence in number of samples.
Format is u6.2. E.g, At 50Hz and 2Hz walk frequency, if the cadency is 25 samples.
The register will output 100. */
uint8_t activity_class; /**< Detected activity unknown (0), walk (1) or run (2) */
} inv_imu_apex_step_activity_t;
/** @brief Enable Free Fall.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_enable_ff(struct inv_imu_device *s);
/** @brief Disable Free Fall.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_disable_ff(struct inv_imu_device *s);
/** @brief Enable Significant Motion Detection.
* note : SMD requests to have the accelerometer enabled to work.
* To have good performance, it's recommended to set accelerometer ODR (Output Data Rate) to 20ms
* and the accelerometer in Low Power Mode.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_enable_smd(struct inv_imu_device *s);
/** @brief Disable Significant Motion Detection.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_disable_smd(struct inv_imu_device *s);
/** @brief Fill the APEX parameters structure with all the default parameters for APEX algorithms (pedometer, tilt)
* @param[out] apex_inputs Default input parameters. See @sa inv_imu_apex_parameters_t
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_init_parameters_struct(struct inv_imu_device *s, inv_imu_apex_parameters_t *apex_inputs);
/** @brief Configures DMP parameters for APEX algorithms (pedometer, tilt, lowg, highg).
* This programmable parameters will be decoded and propagate to the SRAM to be executed at DMP start.
* @param[in] apex_inputs The requested input parameters. See @sa inv_imu_apex_parameters_t
* @warning APEX inputs can't change on the fly, this API should be called before enabling any APEX features.
* @warning APEX configuration can't be done too frequently, but only once every 10ms.
* Otherwise it can create unknown behavior.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_configure_parameters(struct inv_imu_device *s, const inv_imu_apex_parameters_t *apex_inputs);
/** @brief Returns current DMP parameters for APEX algorithms (pedometer, tilt).
* @param[out] apex_params The current parameter, fetched from registers. See @sa inv_imu_apex_parameters_t
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_get_parameters(struct inv_imu_device *s, inv_imu_apex_parameters_t *apex_params);
/** @brief Configure DMP Output Data Rate for APEX algorithms (pedometer, tilt)
* @param[in] frequency The requested frequency.
* @sa APEX_CONFIG1_DMP_ODR_t
* @warning DMP_ODR can change on the fly, and the DMP code will accommodate necessary modifications
* @warning The user needs to take care to set Accel frequency >= DMP frequency. This is a hard constraint
since HW will not handle incorrect setting.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_set_frequency(struct inv_imu_device *s, const APEX_CONFIG1_DMP_ODR_t frequency);
/** @brief Enable APEX algorithm Pedometer.
* note : Pedometer request to have the accelerometer enabled to works
* with accelerometer frequency less than dmp frequency.
* @return 0 on success, negative value on error.
* @warning Pedometer must be turned OFF to reconfigure it
*/
int inv_imu_apex_enable_pedometer(struct inv_imu_device *s);
/** @brief Disable APEX algorithm Pedometer.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_disable_pedometer(struct inv_imu_device *s);
/** @brief Enable APEX algorithm Tilt.
* note : Tilt request to have the accelerometer enabled to works
* with accelerometer frequency less than dmp frequency.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_enable_tilt(struct inv_imu_device *s);
/** @brief Disable APEX algorithm Tilt.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_disable_tilt(struct inv_imu_device *s);
/** @brief Retrieve APEX pedometer outputs and format them
* @param[out] apex_activity Apex step and activity data value.
* @return 0 in case of success, negative value on error. See enum inv_error
*/
int inv_imu_apex_get_data_activity(struct inv_imu_device *s, inv_imu_apex_step_activity_t *apex_activity);
/** @brief Retrieve APEX free fall outputs and format them
* @param[out] Free fall duration in number of sample.
* @return 0 in case of success, negative value on error. See enum inv_error
*/
int inv_imu_apex_get_data_free_fall(struct inv_imu_device *s, uint16_t *freefall_duration);
#ifdef __cplusplus
}
#endif
#endif /* _INV_IMU_APEX_H_ */
/** @} */
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2017 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup DriverApex IMU driver high level functions related to APEX and the DMP
* @brief High-level function to setup an IMU device
* @ingroup Driver
* @{
*/
/** @file inv_imu_apex.h
* High-level function to setup an IMU device
*/
#ifndef _INV_IMU_APEX_H_
#define _INV_IMU_APEX_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "inv_imu_defs.h"
#include "InvError.h"
#include <stdint.h>
#include <string.h>
/* Forward declarations */
struct inv_imu_device;
/** @brief IMU APEX inputs parameters definition
*/
typedef struct {
APEX_CONFIG3_PEDO_AMP_TH_t pedo_amp_th;
uint8_t pedo_step_cnt_th;
uint8_t pedo_step_det_th;
APEX_CONFIG4_PEDO_SB_TIMER_TH_t pedo_sb_timer_th;
APEX_CONFIG4_PEDO_HI_ENRGY_TH_t pedo_hi_enrgy_th;
APEX_CONFIG5_TILT_WAIT_TIME_t tilt_wait_time;
APEX_CONFIG2_DMP_POWER_SAVE_TIME_t power_save_time;
APEX_CONFIG0_DMP_POWER_SAVE_t power_save;
APEX_CONFIG9_SENSITIVITY_MODE_t sensitivity_mode;
APEX_CONFIG2_LOW_ENERGY_AMP_TH_t low_energy_amp_th;
APEX_CONFIG9_SMD_SENSITIVITY_t smd_sensitivity;
APEX_CONFIG9_FF_DEBOUNCE_DURATION_t ff_debounce_duration;
APEX_CONFIG12_FF_MAX_DURATION_t ff_max_duration_cm;
APEX_CONFIG12_FF_MIN_DURATION_t ff_min_duration_cm;
APEX_CONFIG10_LOWG_PEAK_TH_t lowg_peak_th;
APEX_CONFIG5_LOWG_PEAK_TH_HYST_t lowg_peak_hyst;
APEX_CONFIG10_LOWG_TIME_TH_SAMPLES_t lowg_samples_th;
APEX_CONFIG11_HIGHG_PEAK_TH_t highg_peak_th;
APEX_CONFIG5_HIGHG_PEAK_TH_HYST_t highg_peak_hyst;
APEX_CONFIG11_HIGHG_TIME_TH_SAMPLES_t highg_samples_th;
} inv_imu_apex_parameters_t;
/** @brief APEX pedometer outputs
*/
typedef struct inv_imu_apex_step_activity {
uint16_t step_cnt; /**< Number of steps taken */
uint8_t step_cadence; /**< Walk/run cadence in number of samples.
Format is u6.2. E.g, At 50Hz and 2Hz walk frequency, if the cadency is 25 samples.
The register will output 100. */
uint8_t activity_class; /**< Detected activity unknown (0), walk (1) or run (2) */
} inv_imu_apex_step_activity_t;
/** @brief Enable Free Fall.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_enable_ff(struct inv_imu_device *s);
/** @brief Disable Free Fall.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_disable_ff(struct inv_imu_device *s);
/** @brief Enable Significant Motion Detection.
* note : SMD requests to have the accelerometer enabled to work.
* To have good performance, it's recommended to set accelerometer ODR (Output Data Rate) to 20ms
* and the accelerometer in Low Power Mode.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_enable_smd(struct inv_imu_device *s);
/** @brief Disable Significant Motion Detection.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_disable_smd(struct inv_imu_device *s);
/** @brief Fill the APEX parameters structure with all the default parameters for APEX algorithms (pedometer, tilt)
* @param[out] apex_inputs Default input parameters. See @sa inv_imu_apex_parameters_t
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_init_parameters_struct(struct inv_imu_device *s, inv_imu_apex_parameters_t *apex_inputs);
/** @brief Configures DMP parameters for APEX algorithms (pedometer, tilt, lowg, highg).
* This programmable parameters will be decoded and propagate to the SRAM to be executed at DMP start.
* @param[in] apex_inputs The requested input parameters. See @sa inv_imu_apex_parameters_t
* @warning APEX inputs can't change on the fly, this API should be called before enabling any APEX features.
* @warning APEX configuration can't be done too frequently, but only once every 10ms.
* Otherwise it can create unknown behavior.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_configure_parameters(struct inv_imu_device *s, const inv_imu_apex_parameters_t *apex_inputs);
/** @brief Returns current DMP parameters for APEX algorithms (pedometer, tilt).
* @param[out] apex_params The current parameter, fetched from registers. See @sa inv_imu_apex_parameters_t
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_get_parameters(struct inv_imu_device *s, inv_imu_apex_parameters_t *apex_params);
/** @brief Configure DMP Output Data Rate for APEX algorithms (pedometer, tilt)
* @param[in] frequency The requested frequency.
* @sa APEX_CONFIG1_DMP_ODR_t
* @warning DMP_ODR can change on the fly, and the DMP code will accommodate necessary modifications
* @warning The user needs to take care to set Accel frequency >= DMP frequency. This is a hard constraint
since HW will not handle incorrect setting.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_set_frequency(struct inv_imu_device *s, const APEX_CONFIG1_DMP_ODR_t frequency);
/** @brief Enable APEX algorithm Pedometer.
* note : Pedometer request to have the accelerometer enabled to works
* with accelerometer frequency less than dmp frequency.
* @return 0 on success, negative value on error.
* @warning Pedometer must be turned OFF to reconfigure it
*/
int inv_imu_apex_enable_pedometer(struct inv_imu_device *s);
/** @brief Disable APEX algorithm Pedometer.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_disable_pedometer(struct inv_imu_device *s);
/** @brief Enable APEX algorithm Tilt.
* note : Tilt request to have the accelerometer enabled to works
* with accelerometer frequency less than dmp frequency.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_enable_tilt(struct inv_imu_device *s);
/** @brief Disable APEX algorithm Tilt.
* @return 0 on success, negative value on error.
*/
int inv_imu_apex_disable_tilt(struct inv_imu_device *s);
/** @brief Retrieve APEX pedometer outputs and format them
* @param[out] apex_activity Apex step and activity data value.
* @return 0 in case of success, negative value on error. See enum inv_error
*/
int inv_imu_apex_get_data_activity(struct inv_imu_device *s, inv_imu_apex_step_activity_t *apex_activity);
/** @brief Retrieve APEX free fall outputs and format them
* @param[out] Free fall duration in number of sample.
* @return 0 in case of success, negative value on error. See enum inv_error
*/
int inv_imu_apex_get_data_free_fall(struct inv_imu_device *s, uint16_t *freefall_duration);
#ifdef __cplusplus
}
#endif
#endif /* _INV_IMU_APEX_H_ */
/** @} */
@@ -1,64 +1,64 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2017 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup DriverExt IMU driver extern functions
* @brief Extern functions for IMU devices
* @ingroup Driver
* @{
*/
/** @file inv_imu_extfunc.h
* Extern functions for IMU devices
*/
#ifndef _INV_IMU_EXTFUNC_H_
#define _INV_IMU_EXTFUNC_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Hook for low-level high res system sleep() function to be implemented by upper layer
* ~100us resolution is sufficient
* @param[in] us number of us the calling thread should sleep
*/
extern void inv_imu_sleep_us(uint32_t us);
/** @brief Hook for low-level high res system get_time() function to be implemented by upper layer
* Value shall be on 64bit with a 1 us resolution
* @return The current time in us
*/
extern uint64_t inv_imu_get_time_us(void);
#ifdef __cplusplus
}
#endif
#endif /* _INV_IMU_EXTFUNC_H_ */
/** @} */
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2017 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup DriverExt IMU driver extern functions
* @brief Extern functions for IMU devices
* @ingroup Driver
* @{
*/
/** @file inv_imu_extfunc.h
* Extern functions for IMU devices
*/
#ifndef _INV_IMU_EXTFUNC_H_
#define _INV_IMU_EXTFUNC_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Hook for low-level high res system sleep() function to be implemented by upper layer
* ~100us resolution is sufficient
* @param[in] us number of us the calling thread should sleep
*/
extern void inv_imu_sleep_us(uint32_t us);
/** @brief Hook for low-level high res system get_time() function to be implemented by upper layer
* Value shall be on 64bit with a 1 us resolution
* @return The current time in us
*/
extern uint64_t inv_imu_get_time_us(void);
#ifdef __cplusplus
}
#endif
#endif /* _INV_IMU_EXTFUNC_H_ */
/** @} */
@@ -1,179 +1,179 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#include "inv_imu_selftest.h"
#include "inv_imu_extfunc.h"
#include "inv_imu_transport.h"
static int configure_selftest_parameters(struct inv_imu_device *s,
const inv_imu_selftest_parameters_t st_params);
int inv_imu_run_selftest(struct inv_imu_device *s,
const inv_imu_selftest_parameters_t st_params,
inv_imu_selftest_output_t *st_output)
{
int status = 0;
uint8_t value;
uint8_t data[2] = {0};
uint8_t st_done = 0;
int polling_timeout_ms = 1000;
/* Disables Gyro/Accel sensors */
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &value);
value &= ~(PWR_MGMT0_ACCEL_MODE_MASK | PWR_MGMT0_GYRO_MODE_MASK);
status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &value);
/* Enable RC oscillator */
status |= inv_imu_switch_on_mclk(s);
/* Clear DMP SRAM (1 ms wait included in `inv_imu_reset_dmp()`) */
status |= inv_imu_reset_dmp(s, APEX_CONFIG0_DMP_MEM_RESET_APEX_ST_EN);
/* Update `dmp_is_on` since APEX features will have to restart from scratch */
s->dmp_is_on = 0;
/* Load self-test data */
status |= inv_imu_load_selftest_data(s);
/* Set self-test parameters */
status |= configure_selftest_parameters(s, st_params);
/*
* Enable accel and/or gyro self-test.
* If both accel and gyro self-test are enabled,
* they should be set simultaneously in the same write access
*/
status |= inv_imu_read_reg(s, SELFTEST_MREG1, 1, &value);
value &= ~SELFTEST_EN;
value |= (uint8_t)st_params.st_control;
status |= inv_imu_write_reg(s, SELFTEST_MREG1, 1, &value);
/* Poll int_status_st_done bit */
do {
inv_imu_sleep_us(1000);
status |= inv_imu_read_reg(s, INT_STATUS, 1, &st_done);
st_done &= INT_STATUS_ST_INT_MASK;
if (0 == --polling_timeout_ms)
return (status | -1); /* Return error if timeout is reached */
} while ( !st_done /* Exit if ST_DONE */
&& !status /* Or if error is detected */);
/* Read self-test results */
status |= inv_imu_read_reg(s, ST_STATUS1_MREG1, 2, &data[0]);
st_output->accel_status = (data[0] & ST_STATUS1_ACCEL_ST_PASS_MASK) >> ST_STATUS1_ACCEL_ST_PASS_POS;
st_output->ax_status = (data[0] & ST_STATUS1_AX_ST_PASS_MASK) >> ST_STATUS1_AX_ST_PASS_POS;
st_output->ay_status = (data[0] & ST_STATUS1_AY_ST_PASS_MASK) >> ST_STATUS1_AY_ST_PASS_POS;
st_output->az_status = (data[0] & ST_STATUS1_AZ_ST_PASS_MASK) >> ST_STATUS1_AZ_ST_PASS_POS;
st_output->gyro_status = (data[1] & ST_STATUS2_GYRO_ST_PASS_MASK) >> ST_STATUS2_GYRO_ST_PASS_POS;
st_output->gyro_status |= ((data[1] & ST_STATUS2_ST_INCOMPLETE_MASK) >> ST_STATUS2_ST_INCOMPLETE_POS) << 1;
st_output->gx_status = (data[1] & ST_STATUS2_GX_ST_PASS_MASK) >> ST_STATUS2_GX_ST_PASS_POS;
st_output->gy_status = (data[1] & ST_STATUS2_GY_ST_PASS_MASK) >> ST_STATUS2_GY_ST_PASS_POS;
st_output->gz_status = (data[1] & ST_STATUS2_GZ_ST_PASS_MASK) >> ST_STATUS2_GZ_ST_PASS_POS;
/* Disable self-test */
status |= inv_imu_read_reg(s, SELFTEST_MREG1, 1, &value);
value &= ~SELFTEST_EN;
value |= (uint8_t)SELFTEST_DIS;
status |= inv_imu_write_reg(s, SELFTEST_MREG1, 1, &value);
/* Reset FIFO because ST data may have been pushed to it */
status |= inv_imu_reset_fifo(s);
/* Restore idle bit */
status |= inv_imu_switch_off_mclk(s);
return status;
}
int inv_imu_init_selftest_parameters_struct(struct inv_imu_device *s,
inv_imu_selftest_parameters_t *st_params)
{
(void)s;
st_params->st_num_samples = ST_CONFIG_16_SAMPLES;
st_params->st_control = (SELFTEST_ACCEL_GYRO_ST_EN_t)SELFTEST_EN;
return 0;
}
int inv_imu_load_selftest_data(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
/* Enable RC oscillator */
status |= inv_imu_switch_on_mclk(s);
/* Set up OTP controller to reload factory-trimmed self-test response into SRAM */
status |= inv_imu_read_reg(s, OTP_CONFIG_MREG1, 1, &value);
value &= ~OTP_CONFIG_OTP_COPY_MODE_MASK;
value |= (uint8_t)OTP_CONFIG_OTP_COPY_ST_DATA;
status |= inv_imu_write_reg(s, OTP_CONFIG_MREG1, 1, &value);
/* Take the OTP macro out of power-down mode */
status |= inv_imu_read_reg(s, OTP_CTRL7_MREG2, 1, &value);
value &= ~OTP_CTRL7_OTP_PWR_DOWN_MASK;
value |= (uint8_t)OTP_CTRL7_PWR_DOWN_DIS;
status |= inv_imu_write_reg(s, OTP_CTRL7_MREG2, 1, &value);
/* Wait for voltage generator to power on */
inv_imu_sleep_us(100);
/* Host should disable INT function first before kicking off OTP copy operation */
/* Trigger OTP to reload data (this time in self-test mode) */
status |= inv_imu_read_reg(s, OTP_CTRL7_MREG2, 1, &value);
value &= ~OTP_CTRL7_OTP_RELOAD_MASK;
value |= (uint8_t)OTP_CTRL7_OTP_RELOAD_EN;
status |= inv_imu_write_reg(s, OTP_CTRL7_MREG2, 1, &value);
/* Wait for OTP reload */
inv_imu_sleep_us(20);
/* Disable RC oscillator */
status |= inv_imu_switch_off_mclk(s);
return status;
}
static int configure_selftest_parameters(struct inv_imu_device *s,
const inv_imu_selftest_parameters_t st_params)
{
int status = 0;
uint8_t value;
/* Self-test configuration cannot be updated if it already running */
status |= inv_imu_read_reg(s, SELFTEST_MREG1, 1, &value);
if ((value & SELFTEST_EN) != SELFTEST_DIS)
return INV_ERROR_UNEXPECTED;
status |= inv_imu_read_reg(s, ST_CONFIG_MREG1, 1, &value);
value &= ~((uint8_t)ST_CONFIG_ST_NUMBER_SAMPLE_MASK
| (uint8_t)ST_CONFIG_ACCEL_ST_LIM_MASK
| (uint8_t)ST_CONFIG_GYRO_ST_LIM_MASK);
value |= (uint8_t)st_params.st_num_samples
| (uint8_t)ST_CONFIG_ACCEL_ST_LIM_50
| (uint8_t)ST_CONFIG_GYRO_ST_LIM_50;
status |= inv_imu_write_reg(s, ST_CONFIG_MREG1, 1, &value);
return status;
}
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#include "inv_imu_selftest.h"
#include "inv_imu_extfunc.h"
#include "inv_imu_transport.h"
static int configure_selftest_parameters(struct inv_imu_device *s,
const inv_imu_selftest_parameters_t st_params);
int inv_imu_run_selftest(struct inv_imu_device *s,
const inv_imu_selftest_parameters_t st_params,
inv_imu_selftest_output_t *st_output)
{
int status = 0;
uint8_t value;
uint8_t data[2] = {0};
uint8_t st_done = 0;
int polling_timeout_ms = 1000;
/* Disables Gyro/Accel sensors */
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &value);
value &= ~(PWR_MGMT0_ACCEL_MODE_MASK | PWR_MGMT0_GYRO_MODE_MASK);
status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &value);
/* Enable RC oscillator */
status |= inv_imu_switch_on_mclk(s);
/* Clear DMP SRAM (1 ms wait included in `inv_imu_reset_dmp()`) */
status |= inv_imu_reset_dmp(s, APEX_CONFIG0_DMP_MEM_RESET_APEX_ST_EN);
/* Update `dmp_is_on` since APEX features will have to restart from scratch */
s->dmp_is_on = 0;
/* Load self-test data */
status |= inv_imu_load_selftest_data(s);
/* Set self-test parameters */
status |= configure_selftest_parameters(s, st_params);
/*
* Enable accel and/or gyro self-test.
* If both accel and gyro self-test are enabled,
* they should be set simultaneously in the same write access
*/
status |= inv_imu_read_reg(s, SELFTEST_MREG1, 1, &value);
value &= ~SELFTEST_EN;
value |= (uint8_t)st_params.st_control;
status |= inv_imu_write_reg(s, SELFTEST_MREG1, 1, &value);
/* Poll int_status_st_done bit */
do {
inv_imu_sleep_us(1000);
status |= inv_imu_read_reg(s, INT_STATUS, 1, &st_done);
st_done &= INT_STATUS_ST_INT_MASK;
if (0 == --polling_timeout_ms)
return (status | -1); /* Return error if timeout is reached */
} while ( !st_done /* Exit if ST_DONE */
&& !status /* Or if error is detected */);
/* Read self-test results */
status |= inv_imu_read_reg(s, ST_STATUS1_MREG1, 2, &data[0]);
st_output->accel_status = (data[0] & ST_STATUS1_ACCEL_ST_PASS_MASK) >> ST_STATUS1_ACCEL_ST_PASS_POS;
st_output->ax_status = (data[0] & ST_STATUS1_AX_ST_PASS_MASK) >> ST_STATUS1_AX_ST_PASS_POS;
st_output->ay_status = (data[0] & ST_STATUS1_AY_ST_PASS_MASK) >> ST_STATUS1_AY_ST_PASS_POS;
st_output->az_status = (data[0] & ST_STATUS1_AZ_ST_PASS_MASK) >> ST_STATUS1_AZ_ST_PASS_POS;
st_output->gyro_status = (data[1] & ST_STATUS2_GYRO_ST_PASS_MASK) >> ST_STATUS2_GYRO_ST_PASS_POS;
st_output->gyro_status |= ((data[1] & ST_STATUS2_ST_INCOMPLETE_MASK) >> ST_STATUS2_ST_INCOMPLETE_POS) << 1;
st_output->gx_status = (data[1] & ST_STATUS2_GX_ST_PASS_MASK) >> ST_STATUS2_GX_ST_PASS_POS;
st_output->gy_status = (data[1] & ST_STATUS2_GY_ST_PASS_MASK) >> ST_STATUS2_GY_ST_PASS_POS;
st_output->gz_status = (data[1] & ST_STATUS2_GZ_ST_PASS_MASK) >> ST_STATUS2_GZ_ST_PASS_POS;
/* Disable self-test */
status |= inv_imu_read_reg(s, SELFTEST_MREG1, 1, &value);
value &= ~SELFTEST_EN;
value |= (uint8_t)SELFTEST_DIS;
status |= inv_imu_write_reg(s, SELFTEST_MREG1, 1, &value);
/* Reset FIFO because ST data may have been pushed to it */
status |= inv_imu_reset_fifo(s);
/* Restore idle bit */
status |= inv_imu_switch_off_mclk(s);
return status;
}
int inv_imu_init_selftest_parameters_struct(struct inv_imu_device *s,
inv_imu_selftest_parameters_t *st_params)
{
(void)s;
st_params->st_num_samples = ST_CONFIG_16_SAMPLES;
st_params->st_control = (SELFTEST_ACCEL_GYRO_ST_EN_t)SELFTEST_EN;
return 0;
}
int inv_imu_load_selftest_data(struct inv_imu_device *s)
{
int status = 0;
uint8_t value;
/* Enable RC oscillator */
status |= inv_imu_switch_on_mclk(s);
/* Set up OTP controller to reload factory-trimmed self-test response into SRAM */
status |= inv_imu_read_reg(s, OTP_CONFIG_MREG1, 1, &value);
value &= ~OTP_CONFIG_OTP_COPY_MODE_MASK;
value |= (uint8_t)OTP_CONFIG_OTP_COPY_ST_DATA;
status |= inv_imu_write_reg(s, OTP_CONFIG_MREG1, 1, &value);
/* Take the OTP macro out of power-down mode */
status |= inv_imu_read_reg(s, OTP_CTRL7_MREG2, 1, &value);
value &= ~OTP_CTRL7_OTP_PWR_DOWN_MASK;
value |= (uint8_t)OTP_CTRL7_PWR_DOWN_DIS;
status |= inv_imu_write_reg(s, OTP_CTRL7_MREG2, 1, &value);
/* Wait for voltage generator to power on */
inv_imu_sleep_us(100);
/* Host should disable INT function first before kicking off OTP copy operation */
/* Trigger OTP to reload data (this time in self-test mode) */
status |= inv_imu_read_reg(s, OTP_CTRL7_MREG2, 1, &value);
value &= ~OTP_CTRL7_OTP_RELOAD_MASK;
value |= (uint8_t)OTP_CTRL7_OTP_RELOAD_EN;
status |= inv_imu_write_reg(s, OTP_CTRL7_MREG2, 1, &value);
/* Wait for OTP reload */
inv_imu_sleep_us(20);
/* Disable RC oscillator */
status |= inv_imu_switch_off_mclk(s);
return status;
}
static int configure_selftest_parameters(struct inv_imu_device *s,
const inv_imu_selftest_parameters_t st_params)
{
int status = 0;
uint8_t value;
/* Self-test configuration cannot be updated if it already running */
status |= inv_imu_read_reg(s, SELFTEST_MREG1, 1, &value);
if ((value & SELFTEST_EN) != SELFTEST_DIS)
return INV_ERROR_UNEXPECTED;
status |= inv_imu_read_reg(s, ST_CONFIG_MREG1, 1, &value);
value &= ~((uint8_t)ST_CONFIG_ST_NUMBER_SAMPLE_MASK
| (uint8_t)ST_CONFIG_ACCEL_ST_LIM_MASK
| (uint8_t)ST_CONFIG_GYRO_ST_LIM_MASK);
value |= (uint8_t)st_params.st_num_samples
| (uint8_t)ST_CONFIG_ACCEL_ST_LIM_50
| (uint8_t)ST_CONFIG_GYRO_ST_LIM_50;
status |= inv_imu_write_reg(s, ST_CONFIG_MREG1, 1, &value);
return status;
}
@@ -1,101 +1,101 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup DriverST SelfTest IMU selftest
* @brief Low-level function to run selftest on a IMU device
* @ingroup Driver
* @{
*/
/** @file inv_imu_selftest.h
* Low-level function to run selftest on a IMU device
*/
#ifndef _INV_IMU_SELFTEST_H_
#define _INV_IMU_SELFTEST_H_
#include <stdint.h>
#include "InvExport.h"
#include "inv_imu_defs.h"
#include "inv_imu_driver.h"
#ifdef __cplusplus
extern "C" {
#endif
/* forward declaration */
struct inv_imu_device;
/** @brief Self-test input parameters
*/
typedef struct {
ST_CONFIG_NUM_SAMPLES_t st_num_samples; /**< Number of samples used to perform self-test */
SELFTEST_ACCEL_GYRO_ST_EN_t st_control; /**< Define which sensor is under self-test */
} inv_imu_selftest_parameters_t;
/** @brief Self-test routine outputs
*/
typedef struct {
int8_t accel_status; /**< global accelerometer self-test passed */
int8_t gyro_status; /**< global gyroscope self-test status: st_pass (bit0), st_incomplete (bit1) */
int8_t ax_status; /**< AX self-test status */
int8_t ay_status; /**< AY self-test status */
int8_t az_status; /**< AZ self-test status */
int8_t gx_status; /**< GX self-test status */
int8_t gy_status; /**< GY self-test status */
int8_t gz_status; /**< GZ self-test status */
} inv_imu_selftest_output_t;
/**
* @brief Perform hardware self-test for Accel and Gyro
* @param[in] Self-test parameters (see inv_imu_selftest_parameters_t)
* @param[out] Self-test results (see inv_imu_selftest_output_t)
* @return 0 on completion, negative number if intermediate errors occurred
*/
int inv_imu_run_selftest(struct inv_imu_device *s,
const inv_imu_selftest_parameters_t st_params,
inv_imu_selftest_output_t *st_output);
/** @brief Fill the self-test configuration structure with default configuration
* @param[in] selftest_params self-test parameters to be initialized
* @return 0 on success, negative return code otherwise
*/
int inv_imu_init_selftest_parameters_struct(struct inv_imu_device *s,
inv_imu_selftest_parameters_t *selftest_params);
/** @brief Load self-test data
* @return 0 on success, negative return code otherwise
*/
int inv_imu_load_selftest_data(struct inv_imu_device *s);
#ifdef __cplusplus
}
#endif
#endif /* _INV_IMU_SELFTEST_H_ */
/** @} */
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup DriverST SelfTest IMU selftest
* @brief Low-level function to run selftest on a IMU device
* @ingroup Driver
* @{
*/
/** @file inv_imu_selftest.h
* Low-level function to run selftest on a IMU device
*/
#ifndef _INV_IMU_SELFTEST_H_
#define _INV_IMU_SELFTEST_H_
#include <stdint.h>
#include "InvExport.h"
#include "inv_imu_defs.h"
#include "inv_imu_driver.h"
#ifdef __cplusplus
extern "C" {
#endif
/* forward declaration */
struct inv_imu_device;
/** @brief Self-test input parameters
*/
typedef struct {
ST_CONFIG_NUM_SAMPLES_t st_num_samples; /**< Number of samples used to perform self-test */
SELFTEST_ACCEL_GYRO_ST_EN_t st_control; /**< Define which sensor is under self-test */
} inv_imu_selftest_parameters_t;
/** @brief Self-test routine outputs
*/
typedef struct {
int8_t accel_status; /**< global accelerometer self-test passed */
int8_t gyro_status; /**< global gyroscope self-test status: st_pass (bit0), st_incomplete (bit1) */
int8_t ax_status; /**< AX self-test status */
int8_t ay_status; /**< AY self-test status */
int8_t az_status; /**< AZ self-test status */
int8_t gx_status; /**< GX self-test status */
int8_t gy_status; /**< GY self-test status */
int8_t gz_status; /**< GZ self-test status */
} inv_imu_selftest_output_t;
/**
* @brief Perform hardware self-test for Accel and Gyro
* @param[in] Self-test parameters (see inv_imu_selftest_parameters_t)
* @param[out] Self-test results (see inv_imu_selftest_output_t)
* @return 0 on completion, negative number if intermediate errors occurred
*/
int inv_imu_run_selftest(struct inv_imu_device *s,
const inv_imu_selftest_parameters_t st_params,
inv_imu_selftest_output_t *st_output);
/** @brief Fill the self-test configuration structure with default configuration
* @param[in] selftest_params self-test parameters to be initialized
* @return 0 on success, negative return code otherwise
*/
int inv_imu_init_selftest_parameters_struct(struct inv_imu_device *s,
inv_imu_selftest_parameters_t *selftest_params);
/** @brief Load self-test data
* @return 0 on success, negative return code otherwise
*/
int inv_imu_load_selftest_data(struct inv_imu_device *s);
#ifdef __cplusplus
}
#endif
#endif /* _INV_IMU_SELFTEST_H_ */
/** @} */
@@ -1,257 +1,257 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#include "inv_imu_extfunc.h"
#include "inv_imu_transport.h"
#include "inv_imu_regmap.h"
#include "InvError.h"
/* Function definition */
static uint8_t *get_register_cache_addr(struct inv_imu_device *s, uint32_t reg);
static int write_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, const uint8_t *buf);
static int read_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, uint8_t *buf);
static int write_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t wr_cnt, const uint8_t *buf);
static int read_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t rd_cnt, uint8_t *buf);
int inv_imu_init_transport(struct inv_imu_device *s)
{
int status = 0;
struct inv_imu_transport *t = (struct inv_imu_transport *)s;
status |= read_sreg(s, (uint8_t)PWR_MGMT0, 1, &(t->register_cache.pwr_mgmt0_reg));
status |= read_sreg(s, (uint8_t)GYRO_CONFIG0, 1, &(t->register_cache.gyro_config0_reg));
status |= read_sreg(s, (uint8_t)ACCEL_CONFIG0, 1, &(t->register_cache.accel_config0_reg));
status |= read_mclk_reg(s, (TMST_CONFIG1_MREG1 & 0xFFFF), 1, &(t->register_cache.tmst_config1_reg));
t->need_mclk_cnt = 0;
return status;
}
int inv_imu_read_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, uint8_t *buf)
{
uint32_t i;
int rc = 0;
for (i = 0; i < len; i++) {
uint8_t *cache_addr = get_register_cache_addr(s, reg + i);
if (cache_addr) {
buf[i] = *cache_addr;
} else {
if (!(reg & 0x10000)) {
rc |= read_mclk_reg(s, ((reg + i) & 0xFFFF), 1, &buf[i]);
} else {
rc |= read_sreg(s, (uint8_t)reg + i, len - i, &buf[i]);
break;
}
}
}
return rc;
}
int inv_imu_write_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, const uint8_t *buf)
{
uint32_t i;
int rc = 0;
for (i = 0; i < len; i++) {
uint8_t *cache_addr = get_register_cache_addr(s, reg + i);
if (cache_addr)
*cache_addr = buf[i];
if (!(reg & 0x10000))
rc |= write_mclk_reg(s, ((reg + i) & 0xFFFF), 1, &buf[i]);
}
if (reg & 0x10000)
rc |= write_sreg(s, (uint8_t)reg, len, buf);
return rc;
}
int inv_imu_switch_on_mclk(struct inv_imu_device *s)
{
int status = 0;
uint8_t data;
struct inv_imu_transport *t = (struct inv_imu_transport *)s;
/* set IDLE bit only if it is not set yet */
if (t->need_mclk_cnt == 0) {
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data);
data |= PWR_MGMT0_IDLE_MASK;
status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data);
if (status)
return status;
/* Check if MCLK is ready */
do {
status = inv_imu_read_reg(s, MCLK_RDY, 1, &data);
} while ((status != 0) || !(data & MCLK_RDY_MCLK_RDY_MASK));
} else {
/* Make sure it is already on */
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data);
if (0 == (data &= PWR_MGMT0_IDLE_MASK))
status |= INV_ERROR;
}
/* Increment the counter to keep track of number of MCLK requesters */
t->need_mclk_cnt++;
return status;
}
int inv_imu_switch_off_mclk(struct inv_imu_device *s)
{
int status = 0;
uint8_t data;
struct inv_imu_transport *t = (struct inv_imu_transport *)s;
/* Reset the IDLE but only if there is one requester left */
if (t->need_mclk_cnt == 1) {
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data);
data &= ~PWR_MGMT0_IDLE_MASK;
status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data);
} else {
/* Make sure it is still on */
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data);
if (0 == (data &= PWR_MGMT0_IDLE_MASK))
status |= INV_ERROR;
}
/* Decrement the counter */
t->need_mclk_cnt--;
return status;
}
/* Static function */
static uint8_t *get_register_cache_addr(struct inv_imu_device *s, uint32_t reg)
{
struct inv_imu_transport *t = (struct inv_imu_transport *)s;
switch(reg) {
case PWR_MGMT0: return &(t->register_cache.pwr_mgmt0_reg);
case GYRO_CONFIG0: return &(t->register_cache.gyro_config0_reg);
case ACCEL_CONFIG0: return &(t->register_cache.accel_config0_reg);
case TMST_CONFIG1_MREG1: return &(t->register_cache.tmst_config1_reg);
default: return (uint8_t *)0; // Not found
}
}
static int read_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, uint8_t *buf)
{
struct inv_imu_serif *serif = (struct inv_imu_serif *)s;
if (len > serif->max_read)
return INV_ERROR_SIZE;
if (serif->read_reg(serif, reg, buf, len) != 0)
return INV_ERROR_TRANSPORT;
return 0;
}
static int write_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, const uint8_t *buf)
{
struct inv_imu_serif *serif = (struct inv_imu_serif *)s;
if (len > serif->max_write)
return INV_ERROR_SIZE;
if (serif->write_reg(serif, reg, buf, len) != 0)
return INV_ERROR_TRANSPORT;
return 0;
}
static int read_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t rd_cnt, uint8_t *buf)
{
uint8_t data;
uint8_t blk_sel = (regaddr & 0xFF00) >> 8;
int status = 0;
// Have IMU not in IDLE mode to access MCLK domain
status |= inv_imu_switch_on_mclk(s);
// optimize by changing BLK_SEL only if not NULL
if (blk_sel)
status |= write_sreg(s, (uint8_t)BLK_SEL_R & 0xff, 1, &blk_sel);
data = (regaddr & 0x00FF);
status |= write_sreg(s, (uint8_t)MADDR_R, 1, &data);
inv_imu_sleep_us(10);
status |= read_sreg(s, (uint8_t)M_R, rd_cnt, buf);
inv_imu_sleep_us(10);
if (blk_sel) {
data = 0;
status |= write_sreg(s, (uint8_t)BLK_SEL_R, 1, &data);
}
// switch OFF MCLK if needed
status |= inv_imu_switch_off_mclk(s);
return status;
}
static int write_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t wr_cnt, const uint8_t *buf)
{
uint8_t data;
uint8_t blk_sel = (regaddr & 0xFF00) >> 8;
int status = 0;
// Have IMU not in IDLE mode to access MCLK domain
status |= inv_imu_switch_on_mclk(s);
// optimize by changing BLK_SEL only if not NULL
if (blk_sel)
status |= write_sreg(s, (uint8_t)BLK_SEL_W, 1, &blk_sel);
data = (regaddr & 0x00FF);
status |= write_sreg(s, (uint8_t)MADDR_W, 1, &data);
for (uint8_t i = 0; i < wr_cnt; i++) {
status |= write_sreg(s, (uint8_t)M_W, 1, &buf[i]);
inv_imu_sleep_us(10);
}
if (blk_sel) {
data = 0;
status = write_sreg(s, (uint8_t)BLK_SEL_W, 1, &data);
}
status |= inv_imu_switch_off_mclk(s);
return status;
}
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#include "inv_imu_extfunc.h"
#include "inv_imu_transport.h"
#include "inv_imu_regmap.h"
#include "InvError.h"
/* Function definition */
static uint8_t *get_register_cache_addr(struct inv_imu_device *s, uint32_t reg);
static int write_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, const uint8_t *buf);
static int read_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, uint8_t *buf);
static int write_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t wr_cnt, const uint8_t *buf);
static int read_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t rd_cnt, uint8_t *buf);
int inv_imu_init_transport(struct inv_imu_device *s)
{
int status = 0;
struct inv_imu_transport *t = (struct inv_imu_transport *)s;
status |= read_sreg(s, (uint8_t)PWR_MGMT0, 1, &(t->register_cache.pwr_mgmt0_reg));
status |= read_sreg(s, (uint8_t)GYRO_CONFIG0, 1, &(t->register_cache.gyro_config0_reg));
status |= read_sreg(s, (uint8_t)ACCEL_CONFIG0, 1, &(t->register_cache.accel_config0_reg));
status |= read_mclk_reg(s, (TMST_CONFIG1_MREG1 & 0xFFFF), 1, &(t->register_cache.tmst_config1_reg));
t->need_mclk_cnt = 0;
return status;
}
int inv_imu_read_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, uint8_t *buf)
{
uint32_t i;
int rc = 0;
for (i = 0; i < len; i++) {
uint8_t *cache_addr = get_register_cache_addr(s, reg + i);
if (cache_addr) {
buf[i] = *cache_addr;
} else {
if (!(reg & 0x10000)) {
rc |= read_mclk_reg(s, ((reg + i) & 0xFFFF), 1, &buf[i]);
} else {
rc |= read_sreg(s, (uint8_t)reg + i, len - i, &buf[i]);
break;
}
}
}
return rc;
}
int inv_imu_write_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, const uint8_t *buf)
{
uint32_t i;
int rc = 0;
for (i = 0; i < len; i++) {
uint8_t *cache_addr = get_register_cache_addr(s, reg + i);
if (cache_addr)
*cache_addr = buf[i];
if (!(reg & 0x10000))
rc |= write_mclk_reg(s, ((reg + i) & 0xFFFF), 1, &buf[i]);
}
if (reg & 0x10000)
rc |= write_sreg(s, (uint8_t)reg, len, buf);
return rc;
}
int inv_imu_switch_on_mclk(struct inv_imu_device *s)
{
int status = 0;
uint8_t data;
struct inv_imu_transport *t = (struct inv_imu_transport *)s;
/* set IDLE bit only if it is not set yet */
if (t->need_mclk_cnt == 0) {
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data);
data |= PWR_MGMT0_IDLE_MASK;
status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data);
if (status)
return status;
/* Check if MCLK is ready */
do {
status = inv_imu_read_reg(s, MCLK_RDY, 1, &data);
} while ((status != 0) || !(data & MCLK_RDY_MCLK_RDY_MASK));
} else {
/* Make sure it is already on */
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data);
if (0 == (data &= PWR_MGMT0_IDLE_MASK))
status |= INV_ERROR;
}
/* Increment the counter to keep track of number of MCLK requesters */
t->need_mclk_cnt++;
return status;
}
int inv_imu_switch_off_mclk(struct inv_imu_device *s)
{
int status = 0;
uint8_t data;
struct inv_imu_transport *t = (struct inv_imu_transport *)s;
/* Reset the IDLE but only if there is one requester left */
if (t->need_mclk_cnt == 1) {
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data);
data &= ~PWR_MGMT0_IDLE_MASK;
status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data);
} else {
/* Make sure it is still on */
status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data);
if (0 == (data &= PWR_MGMT0_IDLE_MASK))
status |= INV_ERROR;
}
/* Decrement the counter */
t->need_mclk_cnt--;
return status;
}
/* Static function */
static uint8_t *get_register_cache_addr(struct inv_imu_device *s, uint32_t reg)
{
struct inv_imu_transport *t = (struct inv_imu_transport *)s;
switch(reg) {
case PWR_MGMT0: return &(t->register_cache.pwr_mgmt0_reg);
case GYRO_CONFIG0: return &(t->register_cache.gyro_config0_reg);
case ACCEL_CONFIG0: return &(t->register_cache.accel_config0_reg);
case TMST_CONFIG1_MREG1: return &(t->register_cache.tmst_config1_reg);
default: return (uint8_t *)0; // Not found
}
}
static int read_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, uint8_t *buf)
{
struct inv_imu_serif *serif = (struct inv_imu_serif *)s;
if (len > serif->max_read)
return INV_ERROR_SIZE;
if (serif->read_reg(serif, reg, buf, len) != 0)
return INV_ERROR_TRANSPORT;
return 0;
}
static int write_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, const uint8_t *buf)
{
struct inv_imu_serif *serif = (struct inv_imu_serif *)s;
if (len > serif->max_write)
return INV_ERROR_SIZE;
if (serif->write_reg(serif, reg, buf, len) != 0)
return INV_ERROR_TRANSPORT;
return 0;
}
static int read_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t rd_cnt, uint8_t *buf)
{
uint8_t data;
uint8_t blk_sel = (regaddr & 0xFF00) >> 8;
int status = 0;
// Have IMU not in IDLE mode to access MCLK domain
status |= inv_imu_switch_on_mclk(s);
// optimize by changing BLK_SEL only if not NULL
if (blk_sel)
status |= write_sreg(s, (uint8_t)BLK_SEL_R & 0xff, 1, &blk_sel);
data = (regaddr & 0x00FF);
status |= write_sreg(s, (uint8_t)MADDR_R, 1, &data);
inv_imu_sleep_us(10);
status |= read_sreg(s, (uint8_t)M_R, rd_cnt, buf);
inv_imu_sleep_us(10);
if (blk_sel) {
data = 0;
status |= write_sreg(s, (uint8_t)BLK_SEL_R, 1, &data);
}
// switch OFF MCLK if needed
status |= inv_imu_switch_off_mclk(s);
return status;
}
static int write_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t wr_cnt, const uint8_t *buf)
{
uint8_t data;
uint8_t blk_sel = (regaddr & 0xFF00) >> 8;
int status = 0;
// Have IMU not in IDLE mode to access MCLK domain
status |= inv_imu_switch_on_mclk(s);
// optimize by changing BLK_SEL only if not NULL
if (blk_sel)
status |= write_sreg(s, (uint8_t)BLK_SEL_W, 1, &blk_sel);
data = (regaddr & 0x00FF);
status |= write_sreg(s, (uint8_t)MADDR_W, 1, &data);
for (uint8_t i = 0; i < wr_cnt; i++) {
status |= write_sreg(s, (uint8_t)M_W, 1, &buf[i]);
inv_imu_sleep_us(10);
}
if (blk_sel) {
data = 0;
status = write_sreg(s, (uint8_t)BLK_SEL_W, 1, &data);
}
status |= inv_imu_switch_off_mclk(s);
return status;
}
@@ -1,122 +1,122 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup Transport IMU transport
* @brief Low-level IMU SCLK register access
* @ingroup Driver
* @{
*/
/** @file inv_imu_transport.h
* Low-level IMU SCLK register access
*/
#ifndef _INV_IMU_TRANSPORT_H_
#define _INV_IMU_TRANSPORT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* forward declaration */
struct inv_imu_device;
/** @brief enumeration of serial interfaces available on IMU */
typedef enum
{
UI_I2C,
UI_SPI4,
UI_SPI3
} SERIAL_IF_TYPE_t;
/** @brief basesensor serial interface
*/
struct inv_imu_serif {
void *context;
int (*read_reg)(struct inv_imu_serif *serif, uint8_t reg, uint8_t *buf, uint32_t len);
int (*write_reg)(struct inv_imu_serif *serif, uint8_t reg, const uint8_t *buf, uint32_t len);
int (*configure)(struct inv_imu_serif *serif);
uint32_t max_read;
uint32_t max_write;
SERIAL_IF_TYPE_t serif_type;
};
/** @brief transport interface
*/
struct inv_imu_transport {
struct inv_imu_serif serif; /**< Warning : this field MUST be the first one of struct inv_imu_transport */
/** @brief Contains mirrored values of some IP registers */
struct register_cache {
uint8_t pwr_mgmt0_reg; /**< PWR_MGMT0, Bank: 0 */
uint8_t gyro_config0_reg; /**< GYRO_CONFIG0, Bank: 0 */
uint8_t accel_config0_reg; /**< ACCEL_CONFIG0, Bank: 0 */
uint8_t tmst_config1_reg; /**< TMST_CONFIG1, Bank: MREG_TOP1 */
} register_cache; /**< Store mostly used register values on SRAM */
uint8_t need_mclk_cnt; /**< internal counter to keep track of everyone that needs MCLK */
};
/** @brief Init cache variable.
* @return 0 in case of success, -1 for any error
*/
int inv_imu_init_transport(struct inv_imu_device *s);
/** @brief Reads data from a register on IMU.
* @param[in] reg register address to be read
* @param[in] len number of byte to be read
* @param[out] buf output data from the register
* @return 0 in case of success, -1 for any error
*/
int inv_imu_read_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, uint8_t *buf);
/** @brief Writes data to a register on IMU.
* @param[in] reg register address to be written
* @param[in] len number of byte to be written
* @param[in] buf input data to write
* @return 0 in case of success, -1 for any error
*/
int inv_imu_write_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, const uint8_t *buf);
/** @brief Enable MCLK so that MREG are clocked and system beyond SOI can be safely accessed
* @return 0 in case of success, -1 for any error
*/
int inv_imu_switch_on_mclk(struct inv_imu_device *s);
/** @brief Disable MCLK so that MREG are not clocked anymore, hence reducing power consumption
* @return 0 in case of success, -1 for any error
*/
int inv_imu_switch_off_mclk(struct inv_imu_device *s);
#ifdef __cplusplus
}
#endif
#endif /* _INV_IMU_TRANSPORT_H_ */
/** @} */
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively "Software") is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
/** @defgroup Transport IMU transport
* @brief Low-level IMU SCLK register access
* @ingroup Driver
* @{
*/
/** @file inv_imu_transport.h
* Low-level IMU SCLK register access
*/
#ifndef _INV_IMU_TRANSPORT_H_
#define _INV_IMU_TRANSPORT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* forward declaration */
struct inv_imu_device;
/** @brief enumeration of serial interfaces available on IMU */
typedef enum
{
UI_I2C,
UI_SPI4,
UI_SPI3
} SERIAL_IF_TYPE_t;
/** @brief basesensor serial interface
*/
struct inv_imu_serif {
void *context;
int (*read_reg)(struct inv_imu_serif *serif, uint8_t reg, uint8_t *buf, uint32_t len);
int (*write_reg)(struct inv_imu_serif *serif, uint8_t reg, const uint8_t *buf, uint32_t len);
int (*configure)(struct inv_imu_serif *serif);
uint32_t max_read;
uint32_t max_write;
SERIAL_IF_TYPE_t serif_type;
};
/** @brief transport interface
*/
struct inv_imu_transport {
struct inv_imu_serif serif; /**< Warning : this field MUST be the first one of struct inv_imu_transport */
/** @brief Contains mirrored values of some IP registers */
struct register_cache {
uint8_t pwr_mgmt0_reg; /**< PWR_MGMT0, Bank: 0 */
uint8_t gyro_config0_reg; /**< GYRO_CONFIG0, Bank: 0 */
uint8_t accel_config0_reg; /**< ACCEL_CONFIG0, Bank: 0 */
uint8_t tmst_config1_reg; /**< TMST_CONFIG1, Bank: MREG_TOP1 */
} register_cache; /**< Store mostly used register values on SRAM */
uint8_t need_mclk_cnt; /**< internal counter to keep track of everyone that needs MCLK */
};
/** @brief Init cache variable.
* @return 0 in case of success, -1 for any error
*/
int inv_imu_init_transport(struct inv_imu_device *s);
/** @brief Reads data from a register on IMU.
* @param[in] reg register address to be read
* @param[in] len number of byte to be read
* @param[out] buf output data from the register
* @return 0 in case of success, -1 for any error
*/
int inv_imu_read_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, uint8_t *buf);
/** @brief Writes data to a register on IMU.
* @param[in] reg register address to be written
* @param[in] len number of byte to be written
* @param[in] buf input data to write
* @return 0 in case of success, -1 for any error
*/
int inv_imu_write_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, const uint8_t *buf);
/** @brief Enable MCLK so that MREG are clocked and system beyond SOI can be safely accessed
* @return 0 in case of success, -1 for any error
*/
int inv_imu_switch_on_mclk(struct inv_imu_device *s);
/** @brief Disable MCLK so that MREG are not clocked anymore, hence reducing power consumption
* @return 0 in case of success, -1 for any error
*/
int inv_imu_switch_off_mclk(struct inv_imu_device *s);
#ifdef __cplusplus
}
#endif
#endif /* _INV_IMU_TRANSPORT_H_ */
/** @} */
@@ -1,37 +1,37 @@
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2019 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#ifndef _INV_IMU_VERSION_H_
#define _INV_IMU_VERSION_H_
#ifdef __cplusplus
extern "C" {
#endif
#define INV_IMU_VERSION_STRING "2.0.4"
#ifdef __cplusplus
}
#endif
#endif /* _INV_IMU_VERSION_H_ */
/*
* ________________________________________________________________________________________________________
* Copyright (c) 2019 InvenSense Inc. All rights reserved.
*
* This software, related documentation and any modifications thereto (collectively Software) is subject
* to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
* and other intellectual property rights laws.
*
* InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
* and any use, reproduction, disclosure or distribution of the Software without an express license agreement
* from InvenSense is strictly prohibited.
*
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
* INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THE SOFTWARE.
* ________________________________________________________________________________________________________
*/
#ifndef _INV_IMU_VERSION_H_
#define _INV_IMU_VERSION_H_
#ifdef __cplusplus
extern "C" {
#endif
#define INV_IMU_VERSION_STRING "2.0.4"
#ifdef __cplusplus
}
#endif
#endif /* _INV_IMU_VERSION_H_ */
@@ -1,153 +1,153 @@
//-----------------------------------------------------------------------------
/*
Copyright © 2014-2015 InvenSense Inc. Portions Copyright © 2014-2015 Movea. All rights reserved.
This software, related documentation and any modifications thereto (collectively Software) is subject
to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
and other intellectual property rights laws.
InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
and any use, reproduction, disclosure or distribution of the Software without an express license agreement
from InvenSense is strictly prohibited.
*/
//-----------------------------------------------------------------------------
#ifndef INVN_COMMON_INVN_TYPES_H_
#define INVN_COMMON_INVN_TYPES_H_
/**
* @defgroup invn_types Types
* @brief Motion Library - Type definitions.
* \details Definition of codes and error codes used within the MPL and
* returned to the user.
* Every function tries to return a meaningful error code basing
* on the occuring error condition. The error code is numeric.
*
* The available error codes and their associated values are:
* - (0) INV_SUCCESS
* - (32) INV_ERROR
* - (22 / EINVAL) INV_ERROR_INVALID_PARAMETER
* - (1 / EPERM) INV_ERROR_FEATURE_NOT_ENABLED
* - (36) INV_ERROR_FEATURE_NOT_IMPLEMENTED
* - (64) INV_ERROR_FIFO_READ_COUNT
* \todo Clean up the details documentation in order to use only the \\def one.
* \todo Add documentation to all the definitions
* \ingroup Common
* @file invn_types.h
*/
//=======================================//
//========= Integer Definition =========//
//=======================================//
#ifdef _MSC_VER
# include "inttypes.h"
#else
# include <stdint.h>
#endif
//=======================================//
//======= Fixed Point Conversion =======//
//=======================================//
//! \def INVN_FLT_TO_FXP
//! Convert the \a value from float to QN value. \ingroup invn_macro
#define INVN_FLT_TO_FXP(value, shift) ( (int32_t) ((float)(value)*(1ULL << (shift)) + ( (value>=0)-0.5f )) )
//! \def INVN_DBL_TO_FXP
//! Convert the \a value from double to QN value. \ingroup invn_macro
#define INVN_DBL_TO_FXP(value, shift) ( (int32_t) ((double)(value)*(1ULL << (shift)) + ( (value>=0)-0.5 )) )
//! \def INVN_FLT_TO_UFXP
//! Convert the \a value from float to unsigned QN value. \ingroup invn_macro
#define INVN_FLT_TO_UFXP(value, shift) ( (uint32_t) ((float)(value)*(1ULL << (shift)) + 0.5f) )
//! \def INVN_DBL_TO_UFXP
//! Convert the \a value from double to unsigned QN value. \ingroup invn_macro
#define INVN_DBL_TO_UFXP(value, shift) ( (uint32_t) ((double)(value)*(1ULL << (shift)) + 0.5) )
//! \def INVN_FXP_TO_FLT
//! Convert the \a value from QN value to float. \ingroup invn_macro
#define INVN_FXP_TO_FLT(value, shift) ( (float) (int32_t)(value) / (float)(1ULL << (shift)) )
//! \def INVN_FXP_TO_DBL
//! Convert the \a value from QN value to double. \ingroup invn_macro
#define INVN_FXP_TO_DBL(value, shift) ( (double) (int32_t)(value) / (double)(1ULL << (shift)) )
//! \def INVN_UFXP_TO_FLT
//! Convert the \a value from unsigned QN value to float. \ingroup invn_macro
#define INVN_UFXP_TO_FLT(value, shift) ( (float) (uint32_t)(value) / (float)(1ULL << (shift)) )
//! \def INVN_UFXP_TO_DBL
//! Convert the \a value from unsigned QN value to double. \ingroup invn_macro
#define INVN_UFXP_TO_DBL(value, shift) ( (double) (uint32_t)(value) / (double)(1ULL << (shift)) )
//! \def INVN_CONVERT_FLT_TO_FXP
//! Macro to convert float values from an address into QN values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_FLT_TO_FXP(fltptr, fixptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fixptr)[i] = INVN_FLT_TO_FXP((fltptr)[i], shift); }
//! \def INVN_CONVERT_FLT_TO_UFXP
//! Macro to convert float values from an address into unsigned QN values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_FLT_TO_UFXP(fltptr, fixptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fixptr)[i] = INVN_FLT_TO_UFXP((fltptr)[i], shift); }
//! \def INVN_CONVERT_DBL_TO_FXP
//! Macro to convert double values from an address into QN values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_DBL_TO_FXP(fltptr, fixptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fixptr)[i] = INVN_DBL_TO_FXP((fltptr)[i], shift); }
//! \def INVN_CONVERT_DBL_TO_UFXP
//! Macro to convert double values from an address into unsigned QN values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_DBL_TO_UFXP(fltptr, fixptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fixptr)[i] = INVN_DBL_TO_UFXP((fltptr)[i], shift); }
//! \def INVN_CONVERT_FXP_TO_FLT
//! Macro to convert QN values from an address into float values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_FXP_TO_FLT(fixptr, fltptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fltptr)[i] = INVN_FXP_TO_FLT((fixptr)[i], shift); }
//! \def INVN_CONVERT_UFXP_TO_FLT
//! Macro to convert unsigned QN values from an address into float values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_UFXP_TO_FLT(fixptr, fltptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fltptr)[i] = INVN_UFXP_TO_FLT((fixptr)[i], shift); }
//! \def INVN_CONVERT_FXP_TO_DBL
//! Macro to convert QN values from an address into double values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_FXP_TO_DBL(fixptr, fltptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fltptr)[i] = INVN_FXP_TO_DBL((fixptr)[i], shift); }
//! \def INVN_CONVERT_UFXP_TO_DBL
//! \brief Macro to convert unsigned QN values from an address into double values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_UFXP_TO_DBL(fixptr, fltptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fltptr)[i] = INVN_UFXP_TO_DBL((fixptr)[i], shift); }
//=====================================//
//========= Error Definition =========//
//=====================================//
#ifndef REMOVE_INV_ERROR_T
typedef int32_t inv_error_t; /*!< Type used for error definitions. \ingroup invn_types */
#endif
//typedef int32_t mpu_error_t;
typedef int64_t mpu_time_t; /*!< Type used for mpu time. \ingroup invn_types */
// Typically I2C addresses are 8-bit, but some specifications allow for a 10-bit address
// This definition allows the length to be optimally defined for the platform
typedef uint8_t inv_i2c_addr_t; /*!< Type used for I2C addresses. \ingroup invn_types */
#ifdef __IAR_SYSTEMS_ICC__
// These are defined in standard C errno.h
#define EINVAL (22)
#define EPERM (1)
#define ENOMEM (12)
#else
#include "errno.h"
#endif
#define INVN_SUCCESS (0) /*!< Constant definition for success. \ingroup invn_types */
#define INVN_ERROR_BASE (0x20) /*!< Constant definition for basic error. Value is \b 32 \ingroup invn_types */
#define INVN_ERROR (INVN_ERROR_BASE) /*!< Constant definition for error. Value is \b 32 \ingroup invn_types */
#define INVN_ERROR_FEATURE_NOT_ENABLED (EPERM) /*!< Constant definition for feature not enabled error. \ingroup invn_types */
#define INVN_ERROR_FEATURE_NOT_IMPLEMENTED (INVN_ERROR_BASE + 4) /*!< Constant definition for feature not implemented error. \ingroup invn_types */
#define INVN_ERROR_INVALID_PARAMETER (EINVAL) /*!< Constant definition for invalid parameter error. \ingroup invn_types */
#define INVN_ERROR_FILE_OPEN (INVN_ERROR_BASE + 14) /*!< Constant definition for opening file error. \ingroup invn_types */
#define INVN_ERROR_FILE_READ (INVN_ERROR_BASE + 15) /*!< Constant definition for reading file error. \ingroup invn_types */
#define INVN_ERROR_FILE_WRITE (INVN_ERROR_BASE + 16) /*!< Constant definition for writing file error. \ingroup invn_types */
#define INVN_ERROR_INVALID_CONFIGURATION (INVN_ERROR_BASE + 17) /*!< Constant definition for invalid configuration error. \ingroup invn_types */
/* Serial Communication */
#define INVN_ERROR_SERIAL_OPEN_ERROR (INVN_ERROR_BASE + 21) /*!< Constant definition for serial open error. \ingroup invn_types */
#define INVN_ERROR_SERIAL_READ (INVN_ERROR_BASE + 22) /*!< Constant definition for serial read error. \ingroup invn_types */
#define INVN_ERROR_SERIAL_WRITE (INVN_ERROR_BASE + 23) /*!< Constant definition for serial write error. \ingroup invn_types */
/* Fifo */
#define INVN_ERROR_FIFO_OVERFLOW (INVN_ERROR_BASE + 30) /*!< Constant definition for fifo overflow error. \ingroup invn_types */
#define INVN_ERROR_FIFO_FOOTER (INVN_ERROR_BASE + 31) /*!< Constant definition for fifo footer error. \ingroup invn_types */
#define INVN_ERROR_FIFO_READ_COUNT (INVN_ERROR_BASE + 32) /*!< Constant definition for fifo read count error. \ingroup invn_types */
#define INVN_ERROR_FIFO_READ_DATA (INVN_ERROR_BASE + 33) /*!< Constant definition for fifo read data error. \ingroup invn_types */
/* OS interface errors */
#define INVN_ERROR_OS_BAD_HANDLE (INVN_ERROR_BASE + 61) /*!< Constant definition for OS bad handle error. \ingroup invn_types */
#define INVN_ERROR_OS_CREATE_FAILED (INVN_ERROR_BASE + 62) /*!< Constant definition for OS create failed error. \ingroup invn_types */
#define INVN_ERROR_OS_LOCK_FAILED (INVN_ERROR_BASE + 63) /*!< Constant definition for OS lock failed error. \ingroup invn_types */
/* Warning */
#define INVN_WARNING_SEMAPHORE_TIMEOUT (INVN_ERROR_BASE + 86) /*!< Constant definition for semaphore timeout warning. \ingroup invn_types */
#endif // INVN_COMMON_INVN_TYPES_H_
//-----------------------------------------------------------------------------
/*
Copyright © 2014-2015 InvenSense Inc. Portions Copyright © 2014-2015 Movea. All rights reserved.
This software, related documentation and any modifications thereto (collectively Software) is subject
to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
and other intellectual property rights laws.
InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
and any use, reproduction, disclosure or distribution of the Software without an express license agreement
from InvenSense is strictly prohibited.
*/
//-----------------------------------------------------------------------------
#ifndef INVN_COMMON_INVN_TYPES_H_
#define INVN_COMMON_INVN_TYPES_H_
/**
* @defgroup invn_types Types
* @brief Motion Library - Type definitions.
* \details Definition of codes and error codes used within the MPL and
* returned to the user.
* Every function tries to return a meaningful error code basing
* on the occuring error condition. The error code is numeric.
*
* The available error codes and their associated values are:
* - (0) INV_SUCCESS
* - (32) INV_ERROR
* - (22 / EINVAL) INV_ERROR_INVALID_PARAMETER
* - (1 / EPERM) INV_ERROR_FEATURE_NOT_ENABLED
* - (36) INV_ERROR_FEATURE_NOT_IMPLEMENTED
* - (64) INV_ERROR_FIFO_READ_COUNT
* \todo Clean up the details documentation in order to use only the \\def one.
* \todo Add documentation to all the definitions
* \ingroup Common
* @file invn_types.h
*/
//=======================================//
//========= Integer Definition =========//
//=======================================//
#ifdef _MSC_VER
# include "inttypes.h"
#else
# include <stdint.h>
#endif
//=======================================//
//======= Fixed Point Conversion =======//
//=======================================//
//! \def INVN_FLT_TO_FXP
//! Convert the \a value from float to QN value. \ingroup invn_macro
#define INVN_FLT_TO_FXP(value, shift) ( (int32_t) ((float)(value)*(1ULL << (shift)) + ( (value>=0)-0.5f )) )
//! \def INVN_DBL_TO_FXP
//! Convert the \a value from double to QN value. \ingroup invn_macro
#define INVN_DBL_TO_FXP(value, shift) ( (int32_t) ((double)(value)*(1ULL << (shift)) + ( (value>=0)-0.5 )) )
//! \def INVN_FLT_TO_UFXP
//! Convert the \a value from float to unsigned QN value. \ingroup invn_macro
#define INVN_FLT_TO_UFXP(value, shift) ( (uint32_t) ((float)(value)*(1ULL << (shift)) + 0.5f) )
//! \def INVN_DBL_TO_UFXP
//! Convert the \a value from double to unsigned QN value. \ingroup invn_macro
#define INVN_DBL_TO_UFXP(value, shift) ( (uint32_t) ((double)(value)*(1ULL << (shift)) + 0.5) )
//! \def INVN_FXP_TO_FLT
//! Convert the \a value from QN value to float. \ingroup invn_macro
#define INVN_FXP_TO_FLT(value, shift) ( (float) (int32_t)(value) / (float)(1ULL << (shift)) )
//! \def INVN_FXP_TO_DBL
//! Convert the \a value from QN value to double. \ingroup invn_macro
#define INVN_FXP_TO_DBL(value, shift) ( (double) (int32_t)(value) / (double)(1ULL << (shift)) )
//! \def INVN_UFXP_TO_FLT
//! Convert the \a value from unsigned QN value to float. \ingroup invn_macro
#define INVN_UFXP_TO_FLT(value, shift) ( (float) (uint32_t)(value) / (float)(1ULL << (shift)) )
//! \def INVN_UFXP_TO_DBL
//! Convert the \a value from unsigned QN value to double. \ingroup invn_macro
#define INVN_UFXP_TO_DBL(value, shift) ( (double) (uint32_t)(value) / (double)(1ULL << (shift)) )
//! \def INVN_CONVERT_FLT_TO_FXP
//! Macro to convert float values from an address into QN values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_FLT_TO_FXP(fltptr, fixptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fixptr)[i] = INVN_FLT_TO_FXP((fltptr)[i], shift); }
//! \def INVN_CONVERT_FLT_TO_UFXP
//! Macro to convert float values from an address into unsigned QN values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_FLT_TO_UFXP(fltptr, fixptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fixptr)[i] = INVN_FLT_TO_UFXP((fltptr)[i], shift); }
//! \def INVN_CONVERT_DBL_TO_FXP
//! Macro to convert double values from an address into QN values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_DBL_TO_FXP(fltptr, fixptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fixptr)[i] = INVN_DBL_TO_FXP((fltptr)[i], shift); }
//! \def INVN_CONVERT_DBL_TO_UFXP
//! Macro to convert double values from an address into unsigned QN values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_DBL_TO_UFXP(fltptr, fixptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fixptr)[i] = INVN_DBL_TO_UFXP((fltptr)[i], shift); }
//! \def INVN_CONVERT_FXP_TO_FLT
//! Macro to convert QN values from an address into float values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_FXP_TO_FLT(fixptr, fltptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fltptr)[i] = INVN_FXP_TO_FLT((fixptr)[i], shift); }
//! \def INVN_CONVERT_UFXP_TO_FLT
//! Macro to convert unsigned QN values from an address into float values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_UFXP_TO_FLT(fixptr, fltptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fltptr)[i] = INVN_UFXP_TO_FLT((fixptr)[i], shift); }
//! \def INVN_CONVERT_FXP_TO_DBL
//! Macro to convert QN values from an address into double values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_FXP_TO_DBL(fixptr, fltptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fltptr)[i] = INVN_FXP_TO_DBL((fixptr)[i], shift); }
//! \def INVN_CONVERT_UFXP_TO_DBL
//! \brief Macro to convert unsigned QN values from an address into double values, and copy them to another address. \ingroup invn_macro
#define INVN_CONVERT_UFXP_TO_DBL(fixptr, fltptr, length, shift) { int32_t i; for(i=0; i<(length); ++i) (fltptr)[i] = INVN_UFXP_TO_DBL((fixptr)[i], shift); }
//=====================================//
//========= Error Definition =========//
//=====================================//
#ifndef REMOVE_INV_ERROR_T
typedef int32_t inv_error_t; /*!< Type used for error definitions. \ingroup invn_types */
#endif
//typedef int32_t mpu_error_t;
typedef int64_t mpu_time_t; /*!< Type used for mpu time. \ingroup invn_types */
// Typically I2C addresses are 8-bit, but some specifications allow for a 10-bit address
// This definition allows the length to be optimally defined for the platform
typedef uint8_t inv_i2c_addr_t; /*!< Type used for I2C addresses. \ingroup invn_types */
#ifdef __IAR_SYSTEMS_ICC__
// These are defined in standard C errno.h
#define EINVAL (22)
#define EPERM (1)
#define ENOMEM (12)
#else
#include "errno.h"
#endif
#define INVN_SUCCESS (0) /*!< Constant definition for success. \ingroup invn_types */
#define INVN_ERROR_BASE (0x20) /*!< Constant definition for basic error. Value is \b 32 \ingroup invn_types */
#define INVN_ERROR (INVN_ERROR_BASE) /*!< Constant definition for error. Value is \b 32 \ingroup invn_types */
#define INVN_ERROR_FEATURE_NOT_ENABLED (EPERM) /*!< Constant definition for feature not enabled error. \ingroup invn_types */
#define INVN_ERROR_FEATURE_NOT_IMPLEMENTED (INVN_ERROR_BASE + 4) /*!< Constant definition for feature not implemented error. \ingroup invn_types */
#define INVN_ERROR_INVALID_PARAMETER (EINVAL) /*!< Constant definition for invalid parameter error. \ingroup invn_types */
#define INVN_ERROR_FILE_OPEN (INVN_ERROR_BASE + 14) /*!< Constant definition for opening file error. \ingroup invn_types */
#define INVN_ERROR_FILE_READ (INVN_ERROR_BASE + 15) /*!< Constant definition for reading file error. \ingroup invn_types */
#define INVN_ERROR_FILE_WRITE (INVN_ERROR_BASE + 16) /*!< Constant definition for writing file error. \ingroup invn_types */
#define INVN_ERROR_INVALID_CONFIGURATION (INVN_ERROR_BASE + 17) /*!< Constant definition for invalid configuration error. \ingroup invn_types */
/* Serial Communication */
#define INVN_ERROR_SERIAL_OPEN_ERROR (INVN_ERROR_BASE + 21) /*!< Constant definition for serial open error. \ingroup invn_types */
#define INVN_ERROR_SERIAL_READ (INVN_ERROR_BASE + 22) /*!< Constant definition for serial read error. \ingroup invn_types */
#define INVN_ERROR_SERIAL_WRITE (INVN_ERROR_BASE + 23) /*!< Constant definition for serial write error. \ingroup invn_types */
/* Fifo */
#define INVN_ERROR_FIFO_OVERFLOW (INVN_ERROR_BASE + 30) /*!< Constant definition for fifo overflow error. \ingroup invn_types */
#define INVN_ERROR_FIFO_FOOTER (INVN_ERROR_BASE + 31) /*!< Constant definition for fifo footer error. \ingroup invn_types */
#define INVN_ERROR_FIFO_READ_COUNT (INVN_ERROR_BASE + 32) /*!< Constant definition for fifo read count error. \ingroup invn_types */
#define INVN_ERROR_FIFO_READ_DATA (INVN_ERROR_BASE + 33) /*!< Constant definition for fifo read data error. \ingroup invn_types */
/* OS interface errors */
#define INVN_ERROR_OS_BAD_HANDLE (INVN_ERROR_BASE + 61) /*!< Constant definition for OS bad handle error. \ingroup invn_types */
#define INVN_ERROR_OS_CREATE_FAILED (INVN_ERROR_BASE + 62) /*!< Constant definition for OS create failed error. \ingroup invn_types */
#define INVN_ERROR_OS_LOCK_FAILED (INVN_ERROR_BASE + 63) /*!< Constant definition for OS lock failed error. \ingroup invn_types */
/* Warning */
#define INVN_WARNING_SEMAPHORE_TIMEOUT (INVN_ERROR_BASE + 86) /*!< Constant definition for semaphore timeout warning. \ingroup invn_types */
#endif // INVN_COMMON_INVN_TYPES_H_
@@ -1,156 +1,156 @@
/*
$License:
Copyright (C) 2018 InvenSense Corporation, All Rights Reserved.
$
*/
#ifndef _INVN_ALGO_AGM_H_
#define _INVN_ALGO_AGM_H_
#include "invn_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \defgroup AGM AGM
* \brief Algorithm that provides device orientation. Algorithm inputs are raw Accelerometer, Gyroscope and Magnetometer data.
* Algorithm outputs: calibrated sensor and 9-axis sensor fusion.
* \warning supported sampling frequency [50 Hz-1000 Hz]
* \warning supported gyroscope FSR [250 dps, 500 dps, 1000 dps, 2000 dps, 4000 dps]
* \warning supported accelerometer FSR [1 g, 2 g, 4 g, 8 g, 16 g]
*/
#define INVN_ALGO_AGM_INPUT_MASK_ACC 1 ///< Raw Accel update mask
#define INVN_ALGO_AGM_INPUT_MASK_GYR 2 ///< Raw Gyro update mask
#define INVN_ALGO_AGM_INPUT_MASK_MAG 4 ///< Raw Mag update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_ACCEL_CAL 1 ///< Accel cal output update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_GYRO_CAL 2 ///< Gyro cal output update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_MAG_CAL 4 ///< Mag cal output update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_QUAT_AG 8 ///< Game Rotation Vector (Accel and Gyro Fusion) output update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_QUAT_AGM 16 ///< Rotation Vector (Accel, Gyro and Magnetometer Fusion) output update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_GRAVITY 32 ///< Gravity vector output update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_LINEARACC 64 ///< Linear acceleration vector output update mask
/* Forward declarations */
struct inv_icm426xx;
/*! \struct InvnAlgoAGMInput
* AGM input structure (raw data) \ingroup AGM
*/
typedef struct
{
int32_t mask; /*!< mask to specify updated inputs. */
int64_t sRimu_time_us; /*!< timestamp \f$ [\mu s]\f$ of raw accel and gyro */
int32_t sRacc_data[3]; /*!< raw accelerometer in high resolution mode. Expect Full Scale Value coded on 20 bit (i.e. +/- FSR g = 1<<19 LSB) */
int32_t sRgyr_data[3]; /*!< raw gyroscope in high resolution mode. Expect Full Scale Value coded on 20 bit (i.e. +/- FSR dps = 1<<19 LSB) */
int16_t sRtemp_data; /*!< raw temperature */
int64_t sRmag_time_us; /*!< timestamp of raw mag */
int32_t sRmag_data[3]; /*!< raw mag */
} InvnAlgoAGMInput;
/*! \struct InvnAlgoAGMOutput
* AGM output structure (calibrated sensors and fusion output) \ingroup AGM
*/
typedef struct
{
int32_t mask; /*!< mask to specify updated outputs */
int32_t acc_uncal_q16[3]; /*!< uncalibrated accelerometer (1 g = 1<<16) */
int32_t acc_cal_q16[3]; /*!< calibrated accelerometer (1 g = 1<<16) */
int32_t acc_bias_q16[3]; /*!< accelerometer bias (1 g = 1<<16)*/
int8_t acc_accuracy_flag; /*!< accelerometer accuracy from 0(non calibrated) to 3(well calibrated) */
int32_t gyr_uncal_q16[3]; /*!< uncalibrated gyroscope (1 dps = 1<<16) */
int32_t gyr_cal_q16[3]; /*!< calibrated gyroscope (1 dps = 1<<16) */
int32_t gyr_bias_q16[3]; /*!< gyro bias (1 dps = 1<<16)*/
int8_t gyr_accuracy_flag; /*!< gyro accuracy, from 0(non calibrated) to 3(well calibrated) */
int32_t mag_uncal_q16[3]; /*!< uncalibrated magnetometer (1uT = 1<<16) */
int32_t mag_cal_q16[3]; /*!< calibrated magnetometer (1uT = 1<<16) */
int32_t mag_bias_q16[3]; /*!< magnetometer bias (1uT = 1<<16) */
int8_t mag_accuracy_flag; /*!< magnetometer accuracy, from 0(non calibrated) to 3(well calibrated) */
int32_t grv_quat_q30[4]; /*!< 6-axis (accel and gyro fusion) quaternion */
int32_t rv_quat_q30[4]; /*!< 9-axis (accel, gyro and magnetometer fusion) quaternion */
int32_t rv_accuracy_q27; /*!< 9-axis (accel, gyro and magnetometer fusion) 3\sigma accuracy in rad */
int32_t gravity_q16[3]; /*!< gravity estimation in sensor frame */
int32_t linear_acc_q16[3]; /*!< linear acceleration estimation in sensor frame */
int32_t temp_degC_q16; /*!< temperature (1 \f$ [^{\circ}C]\f$ = 1<<16)*/
} InvnAlgoAGMOutput;
/*! \struct InvnAlgoAGMConfig
* AGM configuration structure (sensor related settings) \ingroup AGM
*/
typedef struct
{
int32_t * acc_bias_q16; /*!< Previously stored accel bias pointer. If pointer is NULL or 0, offset will be set to { 0, 0, 0} */
int32_t * gyr_bias_q16; /*!< Previously stored gyro bias pointer. If pointer is NULL or 0, offset will be set to { 0, 0, 0} */
int32_t * mag_bias_q16; /*!< mag_bias_q16 Previously stored mag bias pointer If pointer is NULL or 0, offset will be set to { 0, 0, 0} */
int8_t acc_accuracy; /*!< Previously stored accelerometer bias accuracy (0 to 3) */
int8_t gyr_accuracy; /*!< Previously stored gyroscope bias accuracy (0 to 3) */
int8_t mag_accuracy; /*!< Previously stored magnetometer bias accuracy (0 to 3) */
int32_t acc_fsr; /*!< accelerometer full scale range [g] */
int32_t gyr_fsr; /*!< gyroscope full scale range [dps] */
uint32_t acc_odr_us; /*!< accelerometer output data rate in \f$ [\mu s]\f$ */
uint32_t gyr_odr_us; /*!< gyroscope output data rate \f$ [\mu s]\f$ */
int32_t mag_sc_q16; /*!< magnetometer sensitivity (uT/LSB, e.g. mag_uT = (mag_sc_q16 * raw_mag_LSB)/65536) */
uint32_t mag_odr_us; /*!< magnetometer output data rate \f$ [\mu s]\f$ */
int32_t temp_sensitivity; /*!< temperature sensitivity in q30 (if temperature(\f$ ^{\circ}C \f$) = LSB * k + z, then temp_sensitivity = k) */
int32_t temp_offset; /*!< temperature offset in q16 (if temperature(\f$ ^{\circ}C \f$) = LSB * k + z, then temp_offset = z) */
} InvnAlgoAGMConfig;
/*!
* \brief Return library version x.y.z-suffix as a char array
* \retval library version a char array "x.y.z-suffix"
* \ingroup AGM
*/
const char * invn_algo_agm_version(void);
/*!
* \brief Initializes algorithms with default parameters and reset states.
* (\icm_device[in] Invensense ICM426XX device pointer. Only when gyro assisted is enabled.)
* \config[in] algo init parameters structure.
* \return initialization success indicator.
* \retval 0 Success
* \retval 1 Fail
* \ingroup AGM
*/
#ifdef WITH_GYRO_ASSIST
uint8_t invn_algo_agm_init_a(struct inv_icm426xx * icm_device, const InvnAlgoAGMConfig * config);
#else
uint8_t invn_algo_agm_init(const InvnAlgoAGMConfig * config);
#endif
/*!
* \brief Sets algo config structure.
* \config[in] config structure of the algo.
* \ingroup AGM
*/
void invn_algo_agm_set_config(const InvnAlgoAGMConfig * config);
/*!
* \brief Performs algorithm computation.
* \in inputs algorithm input. Input mask (inputs->mask) should be set with respect to new sensor data in InvnAlgoAGMInput.
* \out outputs algorithm output. Output mask (outputs->mask) reports updated outputs in InvnAlgoAGMOutput.
* \ingroup AGM
*/
void invn_algo_agm_process(const InvnAlgoAGMInput *inputs, InvnAlgoAGMOutput *outputs);
#ifdef __cplusplus
}
#endif
#endif
/*
$License:
Copyright (C) 2018 InvenSense Corporation, All Rights Reserved.
$
*/
#ifndef _INVN_ALGO_AGM_H_
#define _INVN_ALGO_AGM_H_
#include "invn_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \defgroup AGM AGM
* \brief Algorithm that provides device orientation. Algorithm inputs are raw Accelerometer, Gyroscope and Magnetometer data.
* Algorithm outputs: calibrated sensor and 9-axis sensor fusion.
* \warning supported sampling frequency [50 Hz-1000 Hz]
* \warning supported gyroscope FSR [250 dps, 500 dps, 1000 dps, 2000 dps, 4000 dps]
* \warning supported accelerometer FSR [1 g, 2 g, 4 g, 8 g, 16 g]
*/
#define INVN_ALGO_AGM_INPUT_MASK_ACC 1 ///< Raw Accel update mask
#define INVN_ALGO_AGM_INPUT_MASK_GYR 2 ///< Raw Gyro update mask
#define INVN_ALGO_AGM_INPUT_MASK_MAG 4 ///< Raw Mag update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_ACCEL_CAL 1 ///< Accel cal output update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_GYRO_CAL 2 ///< Gyro cal output update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_MAG_CAL 4 ///< Mag cal output update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_QUAT_AG 8 ///< Game Rotation Vector (Accel and Gyro Fusion) output update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_QUAT_AGM 16 ///< Rotation Vector (Accel, Gyro and Magnetometer Fusion) output update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_GRAVITY 32 ///< Gravity vector output update mask
#define INVN_ALGO_AGM_OUTPUT_MASK_LINEARACC 64 ///< Linear acceleration vector output update mask
/* Forward declarations */
struct inv_icm426xx;
/*! \struct InvnAlgoAGMInput
* AGM input structure (raw data) \ingroup AGM
*/
typedef struct
{
int32_t mask; /*!< mask to specify updated inputs. */
int64_t sRimu_time_us; /*!< timestamp \f$ [\mu s]\f$ of raw accel and gyro */
int32_t sRacc_data[3]; /*!< raw accelerometer in high resolution mode. Expect Full Scale Value coded on 20 bit (i.e. +/- FSR g = 1<<19 LSB) */
int32_t sRgyr_data[3]; /*!< raw gyroscope in high resolution mode. Expect Full Scale Value coded on 20 bit (i.e. +/- FSR dps = 1<<19 LSB) */
int16_t sRtemp_data; /*!< raw temperature */
int64_t sRmag_time_us; /*!< timestamp of raw mag */
int32_t sRmag_data[3]; /*!< raw mag */
} InvnAlgoAGMInput;
/*! \struct InvnAlgoAGMOutput
* AGM output structure (calibrated sensors and fusion output) \ingroup AGM
*/
typedef struct
{
int32_t mask; /*!< mask to specify updated outputs */
int32_t acc_uncal_q16[3]; /*!< uncalibrated accelerometer (1 g = 1<<16) */
int32_t acc_cal_q16[3]; /*!< calibrated accelerometer (1 g = 1<<16) */
int32_t acc_bias_q16[3]; /*!< accelerometer bias (1 g = 1<<16)*/
int8_t acc_accuracy_flag; /*!< accelerometer accuracy from 0(non calibrated) to 3(well calibrated) */
int32_t gyr_uncal_q16[3]; /*!< uncalibrated gyroscope (1 dps = 1<<16) */
int32_t gyr_cal_q16[3]; /*!< calibrated gyroscope (1 dps = 1<<16) */
int32_t gyr_bias_q16[3]; /*!< gyro bias (1 dps = 1<<16)*/
int8_t gyr_accuracy_flag; /*!< gyro accuracy, from 0(non calibrated) to 3(well calibrated) */
int32_t mag_uncal_q16[3]; /*!< uncalibrated magnetometer (1uT = 1<<16) */
int32_t mag_cal_q16[3]; /*!< calibrated magnetometer (1uT = 1<<16) */
int32_t mag_bias_q16[3]; /*!< magnetometer bias (1uT = 1<<16) */
int8_t mag_accuracy_flag; /*!< magnetometer accuracy, from 0(non calibrated) to 3(well calibrated) */
int32_t grv_quat_q30[4]; /*!< 6-axis (accel and gyro fusion) quaternion */
int32_t rv_quat_q30[4]; /*!< 9-axis (accel, gyro and magnetometer fusion) quaternion */
int32_t rv_accuracy_q27; /*!< 9-axis (accel, gyro and magnetometer fusion) 3\sigma accuracy in rad */
int32_t gravity_q16[3]; /*!< gravity estimation in sensor frame */
int32_t linear_acc_q16[3]; /*!< linear acceleration estimation in sensor frame */
int32_t temp_degC_q16; /*!< temperature (1 \f$ [^{\circ}C]\f$ = 1<<16)*/
} InvnAlgoAGMOutput;
/*! \struct InvnAlgoAGMConfig
* AGM configuration structure (sensor related settings) \ingroup AGM
*/
typedef struct
{
int32_t * acc_bias_q16; /*!< Previously stored accel bias pointer. If pointer is NULL or 0, offset will be set to { 0, 0, 0} */
int32_t * gyr_bias_q16; /*!< Previously stored gyro bias pointer. If pointer is NULL or 0, offset will be set to { 0, 0, 0} */
int32_t * mag_bias_q16; /*!< mag_bias_q16 Previously stored mag bias pointer If pointer is NULL or 0, offset will be set to { 0, 0, 0} */
int8_t acc_accuracy; /*!< Previously stored accelerometer bias accuracy (0 to 3) */
int8_t gyr_accuracy; /*!< Previously stored gyroscope bias accuracy (0 to 3) */
int8_t mag_accuracy; /*!< Previously stored magnetometer bias accuracy (0 to 3) */
int32_t acc_fsr; /*!< accelerometer full scale range [g] */
int32_t gyr_fsr; /*!< gyroscope full scale range [dps] */
uint32_t acc_odr_us; /*!< accelerometer output data rate in \f$ [\mu s]\f$ */
uint32_t gyr_odr_us; /*!< gyroscope output data rate \f$ [\mu s]\f$ */
int32_t mag_sc_q16; /*!< magnetometer sensitivity (uT/LSB, e.g. mag_uT = (mag_sc_q16 * raw_mag_LSB)/65536) */
uint32_t mag_odr_us; /*!< magnetometer output data rate \f$ [\mu s]\f$ */
int32_t temp_sensitivity; /*!< temperature sensitivity in q30 (if temperature(\f$ ^{\circ}C \f$) = LSB * k + z, then temp_sensitivity = k) */
int32_t temp_offset; /*!< temperature offset in q16 (if temperature(\f$ ^{\circ}C \f$) = LSB * k + z, then temp_offset = z) */
} InvnAlgoAGMConfig;
/*!
* \brief Return library version x.y.z-suffix as a char array
* \retval library version a char array "x.y.z-suffix"
* \ingroup AGM
*/
const char * invn_algo_agm_version(void);
/*!
* \brief Initializes algorithms with default parameters and reset states.
* (\icm_device[in] Invensense ICM426XX device pointer. Only when gyro assisted is enabled.)
* \config[in] algo init parameters structure.
* \return initialization success indicator.
* \retval 0 Success
* \retval 1 Fail
* \ingroup AGM
*/
#ifdef WITH_GYRO_ASSIST
uint8_t invn_algo_agm_init_a(struct inv_icm426xx * icm_device, const InvnAlgoAGMConfig * config);
#else
uint8_t invn_algo_agm_init(const InvnAlgoAGMConfig * config);
#endif
/*!
* \brief Sets algo config structure.
* \config[in] config structure of the algo.
* \ingroup AGM
*/
void invn_algo_agm_set_config(const InvnAlgoAGMConfig * config);
/*!
* \brief Performs algorithm computation.
* \in inputs algorithm input. Input mask (inputs->mask) should be set with respect to new sensor data in InvnAlgoAGMInput.
* \out outputs algorithm output. Output mask (outputs->mask) reports updated outputs in InvnAlgoAGMOutput.
* \ingroup AGM
*/
void invn_algo_agm_process(const InvnAlgoAGMInput *inputs, InvnAlgoAGMOutput *outputs);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,101 @@
/*******************************************************************************
* @file app_raw.h
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
******************************************************************************/
/*******************************************************************************
* [헤더 개요] ICM42670P IMU 드라이버 상위 레이어 선언
*
* IMU 센서의 초기화, 설정, 데이터 읽기를 위한 함수 프로토타입과
* 동작 모드 설정 매크로를 정의한다.
*
* 주요 설정 매크로:
* SERIF_TYPE - 통신 인터페이스 (UI_I2C)
* USE_LOW_NOISE_MODE - 1:저잡음(800Hz), 0:저전력(100Hz)
* USE_HIGH_RES_MODE - 1:20비트 고해상도, 0:16비트 표준
* USE_FIFO - 1:FIFO 사용, 0:레지스터 직접 읽기
******************************************************************************/
#ifndef _APP_RAW_H_
#define _APP_RAW_H_
#include "sdk_config.h"
#include <stdint.h>
#include "inv_imu_transport.h"
#include "inv_imu_defs.h"
#include "inv_imu_driver.h"
/*** 설정 매크로 ***/
/*
* MCU와 IMU 간 통신 인터페이스 선택
* UI_I2C: I2C 통신 사용 (기본)
*/
#define SERIF_TYPE UI_I2C
/*
* 전원 모드 설정
* 1: 저잡음 모드 — 800Hz ODR, 높은 정밀도, 높은 전력 소모
* 0: 저전력 모드 — 100Hz ODR, 낮은 전력 소모
* 주의: 12.5Hz 미만 ODR에서는 저잡음 모드 사용 불가
*/
#define USE_LOW_NOISE_MODE 1
/*
* FIFO 해상도 모드 선택
* 0: 저해상도 — 16비트 데이터 (기본)
* 1: 고해상도 — 20비트 데이터 (FSR이 16g/2000dps로 강제 고정됨)
*/
#define USE_HIGH_RES_MODE 0
/*
* 데이터 읽기 방식 선택
* 0: 레지스터 직접 읽기 (현재 사용 중)
* 1: FIFO에서 읽기
*/
#define USE_FIFO 0
/**
* \brief IMU 디바이스를 리셋하고 초기화한다. WHOAMI 확인 포함.
* 다른 IMU 접근 함수 호출 전에 반드시 성공적으로 실행되어야 한다.
*
* \return 0=성공, 음수=에러
*/
int setup_imu_device(struct inv_imu_serif *icm_serif);
/**
* \brief 자이로 및 가속도계 출력을 위한 디바이스 설정을 수행한다.
* FSR, ODR, 전원 모드, FIFO 설정 등을 적용한다.
* \return 0=성공, 음수=에러
*/
int configure_imu_device(void);
/**
* \brief FIFO 또는 레지스터에서 IMU 데이터를 추출한다.
* 내부적으로 imu_callback()이 호출되어 데이터를 처리한다.
* \return 0=성공, 음수=에러
*/
int get_imu_data(void);
/**
* \brief 센서 데이터 수신 콜백. 마운팅 매트릭스 적용 후
* info4/BLE/UART 모드에 따라 데이터를 출력한다.
* \param[in] event 하나의 센서 데이터 패킷을 담은 구조체
*/
void imu_callback(inv_imu_sensor_event_t *event);
/**
* \brief 드라이버 API를 우회한 직접 I2C 레지스터 읽기.
* DRDY 인터럽트 없이 즉시 센서 데이터를 읽어 BLE로 전송한다.
* 읽기 후 IMU를 슬립 모드로 전환하여 전력을 절감한다.
* \return 0=성공, 음수=에러
*/
int imu_read_direct(void);
#endif /* !_APP_RAW_H_ */
@@ -0,0 +1,290 @@
/*******************************************************************************
* @file app_raw_main.c
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
******************************************************************************/
/*******************************************************************************
* 2026.03.26 jhChun
* 현재 이 파일은 실제 런타임에 실행되지 않고 있음
* 인터럽트 방식 대신 app_raw.c imu_read_direct()에서 직접 레지스터 읽는 방식 사용 중
* 추후 필요 여부에 따라 정리 예정
******************************************************************************/
/*******************************************************************************
* [모듈 개요] ICM42670P 메인 초기화 및 폴링 루프
*
* ICM42670P IMU 센서의 전체 초기화 시퀀스와 메인 루프를 담당한다.
*
* 초기화 흐름 (icm42670_init):
* 1) setup_mcu() - I2C 시리얼 인터페이스 구조체 설정 및 TWI 초기화
* 2) setup_imu_device() - IMU 드라이버 초기화 + WHOAMI 확인
* 3) configure_imu_device() - 센서 파라미터 설정 (FSR, ODR, 전원 모드)
* 4) inv_gpio_sensor_irq_init() - INT1(P1.13) GPIO 인터럽트 설정
*
* 메인 루프 (icm42670_main):
* - INT1 인터럽트 발생 시 irq_from_device 플래그가 세팅됨
* - 메인 루프에서 플래그를 확인하고, 세팅되어 있으면 센서 데이터를 읽음
* - 인터럽트는 하강 에지(HITOLO)에서 발생 (INT1 핀 풀업 설정)
*
* 보조 함수:
* - inv_imu_sleep_us() - nrf_delay_us 래퍼 (IMU 드라이버가 사용)
* - inv_imu_get_time_us() - RTC1 카운터로 타임스탬프 제공
******************************************************************************/
#include "sdk_config.h"
#include "app_raw.h"
#include "app_raw_main.h"
#include "RingBuffer.h"
#include "inv_imu_driver.h"
#include "system_interface.h"
/* std */
#include <stdio.h>
#include "nrf.h"
#include "app_error.h"
#include "boards.h"
#include "nrfx_gpiote.h"
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "main.h" /* 2026-03-17: cmd_parse.h 삭제 → main.h */
#include "i2c_manager.h"
/* --------------------------------------------------------------------------------------
* Global variables
* -------------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------------
* Static variables
* -------------------------------------------------------------------------------------- */
/*
* IMU 인터럽트 플래그
* INT1 핀의 하강 에지 인터럽트 발생 시 1로 세팅된다.
* 메인 루프에서 이 플래그를 확인 후 데이터를 읽고 0으로 클리어한다.
* volatile: ISR에서 변경되므로 컴파일러 최적화 방지
*/
static volatile int irq_from_device;
/* --------------------------------------------------------------------------------------
* Forward declaration
* -------------------------------------------------------------------------------------- */
static int setup_mcu(struct inv_imu_serif *icm_serif);
/*!
* @brief Sensor general interrupt handler, calls specific handlers.
*
* This function is called when an external interrupt is triggered by the sensor,
* checks interrupt registers of InvenSense Sensor to determine the source and type of interrupt
* and calls the specific interrupt handler accordingly.
*
* @param[in] NULL
*
* @param[out] NULL
*
* @return NULL
*
*/
/*
* inv_gpio_sensor_interrupt_handler()
* INT1 핀 인터럽트 핸들러 (ISR).
* 센서가 새 데이터를 준비했을 때 호출되며, 플래그만 세팅하고 즉시 반환한다.
* 실제 데이터 처리는 메인 루프(icm42670_main)에서 수행한다.
*/
static void inv_gpio_sensor_interrupt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
irq_from_device = 1;
}
/*
* inv_gpio_sensor_irq_init()
* INT1(P1.13) GPIO 인터럽트를 초기화한다.
*
* 설정:
* - 트리거: 하강 에지 (HITOLO) — 센서가 INT를 Low로 끌어내릴 때
* - 풀업 저항: 내부 풀업 활성화
* - 핸들러: inv_gpio_sensor_interrupt_handler
* - GPIOTE 모듈이 미초기화 상태이면 먼저 초기화
*/
void inv_gpio_sensor_irq_init(void)
{
ret_code_t err_code;
/* GPIOTE 모듈 초기화 (이미 초기화되어 있으면 건너뜀) */
if (!nrfx_gpiote_is_init())
{
err_code = nrfx_gpiote_init();
APP_ERROR_CHECK(err_code);
}
/* 하강 에지 인터럽트 설정: High→Low 전환 시 트리거, 내부 풀업 사용 */
nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
in_config.pull = NRF_GPIO_PIN_PULLUP;
/* INT1 핀에 인터럽트 핸들러 등록 */
err_code = nrfx_gpiote_in_init(ICM42670_INT1_PIN, &in_config, inv_gpio_sensor_interrupt_handler);
APP_ERROR_CHECK(err_code);
/* 인터럽트 이벤트 활성화 */
nrfx_gpiote_in_event_enable(ICM42670_INT1_PIN, true);
}
/*
* inv_gpio_sensor_irq_uninit()
* INT1 GPIO 인터럽트를 비활성화하고 해제한다.
* 센서 비활성화 시 또는 재초기화 전에 호출된다.
*/
void inv_gpio_sensor_irq_uninit(void)
{
/* 인터럽트 이벤트 비활성화 */
nrfx_gpiote_in_event_disable(ICM42670_INT1_PIN);
/* INT1 핀 인터럽트 설정 해제 */
nrfx_gpiote_in_uninit(ICM42670_INT1_PIN);
/* GPIOTE 모듈 해제 (초기화된 경우에만) */
if (nrfx_gpiote_is_init())
{
nrfx_gpiote_uninit();
}
}
/* --------------------------------------------------------------------------------------
* Main
* -------------------------------------------------------------------------------------- */
/*
* icm42670_init()
* ICM42670P 전체 초기화 시퀀스를 수행한다.
*
* 초기화 순서:
* 1) setup_mcu() - I2C 인터페이스 구조체 설정 및 TWI 하드웨어 초기화
* 2) setup_imu_device() - IMU 드라이버 초기화, WHOAMI(0x67) 확인
* 3) configure_imu_device() - FSR, ODR, 전원 모드 설정
* 4) inv_gpio_sensor_irq_init() - INT1 인터럽트 활성화 (데이터 준비 알림)
*
* 반환값: 0=성공, -1=초기화 실패
*/
int icm42670_init(void)
{
int rc = 0;
struct inv_imu_serif icm_serif;
rc |= setup_mcu(&icm_serif);
rc |= setup_imu_device(&icm_serif);
rc |= configure_imu_device();
if(rc != 0){
printf("!!!error during initialization\r\n");
return -1;
}
/* 초기화 성공 후 INT1 인터럽트 활성화 — 이후 데이터 준비 시 ISR이 호출됨 */
inv_gpio_sensor_irq_init();
return rc;
}
/*
* icm42670_main()
* ICM42670P 메인 폴링 루프.
* 메인 애플리케이션 루프에서 주기적으로 호출되어야 한다.
*
* 동작:
* 1) I2C 하드웨어가 초기화되었는지 확인 (hw_i2c_init_once)
* 2) irq_from_device 플래그 확인 (ISR에서 세팅됨)
* 3) 플래그가 세팅되어 있으면 센서 데이터 읽기 (get_imu_data)
* 4) 데이터 읽기 완료 후 플래그 클리어
*
* 참고: 인터럽트 기반 폴링 방식으로, ISR에서는 플래그만 세팅하고 실제 I2C 통신은 메인 컨텍스트에서 수행한다.
*/
void icm42670_main(void)
{
int rc = 0;
hw_i2c_init_once();
/* 인터럽트 발생 여부 확인 후 데이터 읽기 */
if (irq_from_device) {
rc = get_imu_data();
if(rc < 0) {
printf("error while getting data\r\n");
}
/* 플래그 클리어 — 다음 인터럽트까지 대기 */
irq_from_device = 0;
}
}
/* --------------------------------------------------------------------------------------
* Functions definitions
* -------------------------------------------------------------------------------------- */
/*
* setup_mcu()
* MCU 측 시리얼 인터페이스를 설정한다.
*
* inv_imu_serif 구조체에 다음을 등록:
* - read_reg / write_reg : I2C 읽기/쓰기 콜백 함수 (system_interface.c에서 구현)
* - max_read / max_write : 최대 전송 크기 (32KB)
* - serif_type : 통신 타입 (UI_I2C)
*
* 설정 후 inv_io_hal_init()을 호출하여 실제 TWI 하드웨어를 초기화한다.
*/
static int setup_mcu(struct inv_imu_serif *icm_serif)
{
int rc = 0;
/* IMU 드라이버용 시리얼 인터페이스 구조체 설정 */
icm_serif->context = 0; /* 컨텍스트 미사용 */
icm_serif->read_reg = inv_io_hal_read_reg; /* 레지스터 읽기 콜백 */
icm_serif->write_reg = inv_io_hal_write_reg; /* 레지스터 쓰기 콜백 */
icm_serif->max_read = 1024*32; /* 1회 읽기 최대 바이트 수 */
icm_serif->max_write = 1024*32; /* 1회 쓰기 최대 바이트 수 */
icm_serif->serif_type = SERIF_TYPE; /* UI_I2C (app_raw.h에서 정의) */
/* TWI 하드웨어 초기화 */
rc |= inv_io_hal_init(icm_serif);
return rc;
}
/* --------------------------------------------------------------------------------------
* Extern functions definition
* -------------------------------------------------------------------------------------- */
/*
* inv_imu_sleep_us()
* IMU 드라이버가 사용하는 마이크로초 단위 슬립 함수.
* nrf_delay_us()를 래핑하여 플랫폼 독립적 인터페이스를 제공한다.
* 예: 자이로 스타트업 대기(GYR_STARTUP_TIME_US) 시 사용
*/
void inv_imu_sleep_us(uint32_t us)
{
nrf_delay_us(us);
}
/*
* inv_imu_get_time_us()
* IMU 드라이버가 사용하는 타임스탬프 함수.
* nRF52840의 RTC1 카운터 값을 반환한다.
*
* 주의: RTC1은 32.768kHz로 동작하므로, 반환값의 단위는 엄밀히
* 마이크로초가 아닌 RTC 틱(약 30.5us/tick)이다.
* 드라이버 내부에서 상대적 시간 비교 용도로 사용된다.
*/
uint64_t inv_imu_get_time_us(void)
{
return NRF_RTC1->COUNTER;
}
@@ -0,0 +1,31 @@
/*******************************************************************************
* @file app_raw_main.h
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
******************************************************************************/
/*******************************************************************************
* [헤더 개요] ICM42670P 메인 초기화/폴링 루프 선언
*
* ICM42670P IMU 센서의 전체 초기화 및 메인 루프 함수를 선언한다.
* - icm42670_init() : 전체 초기화 (MCU설정 → IMU초기화 → 센서설정 → 인터럽트 활성화)
* - icm42670_main() : 메인 폴링 루프 (INT1 인터럽트 확인 → 데이터 읽기)
* - icm42670_uninit() : 해제 (프로토타입만 선언, 구현은 별도)
******************************************************************************/
#ifndef _APP_RAW_MAIN_H_
#define _APP_RAW_MAIN_H_
#include "sdk_config.h"
/* ICM42670P 전체 초기화 — MCU I2C 설정 → IMU 드라이버 초기화 → 센서 설정 → 인터럽트 활성화 */
int icm42670_init(void);
/* ICM42670P 메인 폴링 루프 — INT1 인터럽트 플래그 확인 후 센서 데이터 읽기 */
void icm42670_main(void);
/* ICM42670P 해제 (프로토타입 선언) */
int icm42670_uninit(void);
#endif /* !_APP_RAW_MAIN_H_ */
@@ -1,334 +1,334 @@
/*******************************************************************************
* @file system_interface.c
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
******************************************************************************/
/*******************************************************************************
* [Module overview] ICM42670P IMU sensor I2C communication interface
*
* Low-level interface module for communicating with the ICM42670P IMU sensor
* via the nRF52840 TWI (I2C) hardware.
*
* - I2C slave address: 0x68 (ICM42670P default)
* - I2C pin config: SCL=P1.14, SDA=P1.15 (defined in system_interface.h)
* - TWI instance: NRFX_TWI_INSTANCE(0)
* - Bus speed: 100kHz (NRF_TWI_FREQ_100K)
*
* Main function flow:
* inv_io_hal_init() -> Initialize I2C or SPI (only I2C implemented)
* inv_io_hal_read_reg() -> Register read (TX address -> RX data)
* inv_io_hal_write_reg() -> Register write (TX address+data at once)
*
* Error handling: All I2C read/write operations retry once on failure
*
* Note: SPI4 code path exists but is not implemented;
* only I2C (UI_I2C) is used in production.
******************************************************************************/
/* board driver */
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#include "nrf.h"
#include "app_error.h"
#include "boards.h"
#include "nrfx_gpiote.h"
#include "nrfx_twi.h"
#include "system_interface.h"
#include "nrf_delay.h"
/* ICM42670P I2C slave address and max serial write byte count */
#define ICM_I2C_ADDR 0x68
#define INV_MAX_SERIAL_WRITE 16
/* TWI (I2C) instance — uses ICM42670_I2C_INSTANCE(0) from system_interface.h */
const nrfx_twi_t m_twi_icm42670 = NRFX_TWI_INSTANCE(ICM42670_I2C_INSTANCE);
/*
* inv_i2c_master_uninitialize()
* Disables the I2C bus and releases the TWI instance.
* Called before entering sleep mode or before re-initialization.
*/
void inv_i2c_master_uninitialize(void){
nrfx_twi_disable(&m_twi_icm42670);
nrfx_twi_uninit(&m_twi_icm42670);
}
/*
* inv_i2c_master_initialize()
* Initializes and enables the nRF52840 TWI hardware.
* - SCL: P1.14, SDA: P1.15
* - Speed: 100kHz
* - Interrupt priority: highest (APP_IRQ_PRIORITY_HIGH)
* - No event handler (blocking mode)
*/
void inv_i2c_master_initialize(void){
ret_code_t err_code;
const nrfx_twi_config_t twi_icm42670_config = {
.scl = ICM42670_I2C_SCL_PIN,
.sda = ICM42670_I2C_SDA_PIN,
.frequency = NRF_TWI_FREQ_100K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
};
/* Initialize TWI driver (event handler=NULL -> blocking mode) */
err_code = nrfx_twi_init(&m_twi_icm42670, &twi_icm42670_config, NULL, NULL);
APP_ERROR_CHECK(err_code);
/* Enable TWI hardware — tx/rx available after this */
nrfx_twi_enable(&m_twi_icm42670);
}
/*
* icm42670_twi_tx()
* I2C transmit wrapper. Calls nrfx_twi_tx to send data.
* If no_stop=true, STOP condition is omitted (used for Repeated START).
*/
uint32_t icm42670_twi_tx( uint8_t device_id,
uint8_t const * p_data,
uint8_t length,
bool no_stop)
{
ret_code_t ret;
ret = nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop);
return ret;
}
/*
* icm42670_twi_rx()
* I2C receive wrapper. Calls nrfx_twi_rx to receive data.
*/
uint32_t icm42670_twi_rx( uint8_t device_id,
uint8_t * p_data,
uint8_t length)
{
ret_code_t ret;
ret = nrfx_twi_rx(&m_twi_icm42670, device_id, p_data, length);
return ret;
}
/*
* inv_i2c_master_read_register()
* Reads data from a specific ICM42670P register.
*
* Sequence:
* 1) TX: Send 1-byte register address (no_stop=true -> prepare Repeated START)
* 2) RX: Receive data of specified length
*
* Error handling: Retries once on TX or RX failure.
*/
static unsigned long inv_i2c_master_read_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, unsigned char *RegisterValue){
//ret_code_t ret;
uint32_t ret;
uint8_t addr8 = (uint8_t)RegisterAddr;
/* Step 1: Send register address to read (no STOP -> uses Repeated START) */
ret = icm42670_twi_tx(Address, &addr8, 1, true);
if(ret != NRF_SUCCESS) {
/* Retry once on failure */
ret = icm42670_twi_tx(Address, &addr8, 1, true);
if(ret != NRF_SUCCESS) {
printf("ERR! i2c read-1\r\n");
}
}
/* Step 2: Receive data from the register */
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
if(ret != NRF_SUCCESS) {
/* Retry once on failure */
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
if(ret != NRF_SUCCESS) {
printf("ERR! i2c read-2\r\n");
}
}
return ret;
}
/*
* inv_i2c_master_write_register()
* Writes data to a specific ICM42670P register.
*
* Sequence:
* 1) Place register address in buffer[0], data in buffer[1..N]
* 2) TX: Send address+data at once (no_stop=false -> includes STOP condition)
*
* Error handling: Retries once on failure.
*/
static unsigned long inv_i2c_master_write_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, const unsigned char *RegisterValue){
uint32_t ret;
uint8_t buffer[1 + INV_MAX_SERIAL_WRITE]; /* register address (1) + data (max 16 bytes) */
/* Buffer layout: [register address][data bytes] */
buffer[0] = (uint8_t)RegisterAddr;
memcpy(buffer+1, RegisterValue, RegisterLen);
/* Send address+data at once */
ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false);
if(ret != NRF_SUCCESS) {
/* Retry once on failure */
ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false);
if(ret != NRF_SUCCESS) {
printf("ERR! i2c write\r\n");
}
}
return ret;
}
/*
* inv_io_hal_init()
* Initializes the serial interface (I2C or SPI) used by the IMU driver.
* Branches based on serif->serif_type; only I2C is currently implemented.
* Returns: 0=success, -1=unsupported interface type
*/
int inv_io_hal_init(struct inv_imu_serif *serif)
{
switch (serif->serif_type) {
case UI_SPI4:
{
/* SPI4 init — not implemented (only I2C is used) */
break;
}
case UI_I2C:
inv_i2c_master_initialize();
break;
default:
return -1;
}
return 0;
}
/*
* inv_io_hal_read_reg()
* IMU driver callback: reads data from the specified register.
* Performs I2C or SPI read depending on the serial type.
*/
int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen)
{
switch (serif->serif_type) {
case UI_SPI4:
return 0;
case UI_I2C:
return inv_i2c_master_read_register(ICM_I2C_ADDR, reg, rlen, rbuffer);
default:
return -1;
}
}
/*
* inv_io_hal_write_reg()
* IMU driver callback: writes data to the specified register.
* Performs I2C or SPI write depending on the serial type.
*/
int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen)
{
switch (serif->serif_type) {
case UI_SPI4:
return 0;
case UI_I2C:
return inv_i2c_master_write_register(ICM_I2C_ADDR, reg, wlen, wbuffer);
default:
return -1;
}
}
/*
* cat_read()
* Generic I2C read function (debug/legacy).
* Reads 8 bytes, returns the first byte, and prints the data to console.
* Note: Not used in production; kept for debug purposes.
*/
uint8_t cat_read(uint8_t device_id, uint8_t address, uint8_t *data)
{
uint8_t read_data = 0;
char adata[8];
ret_code_t err_code;
//address = 1|(address<<1);
address = (address & 0xFF);
/* Send register address (no STOP, prepare Repeated START) */
err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, &address, 1, true);
if (err_code != NRF_SUCCESS) {
// Handle error
// return;
}
/* Receive 8 bytes of data */
err_code = nrfx_twi_rx(&m_twi_icm42670, device_id, data, 8);
if (err_code != NRF_SUCCESS) {
// Handle error
return 0;
}
read_data = data[0];
memcpy(adata,data,8);
printf("Data %s . \r\n", adata);
return read_data;
}
/*
* cat_write()
* Generic I2C write function (debug/legacy).
* Sends 1 byte address + 1 byte data.
* Note: Copies 6 bytes into buffer, but only transmits 2 bytes.
*/
void cat_write(uint8_t device_id, uint8_t address, uint8_t *data){
uint8_t buffer[7]={0x00,0x00,0x00,0x00,0x00,0x00,0x00};
address = (address & 0xFF);
buffer[0] = (address);
//buffer[1] =(data & 0xFF);
memcpy(buffer+1,data,6);
ret_code_t err_code;
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, 0x00, 1, false);
/* Address (1 byte) + data (1 byte) = 2 bytes transmitted */
err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, buffer, 2, false);
// err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 2, false);
// nrfx_twi_rx(&m_twi_icm42670, device_id, p_data, length);
// nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop);
printf("Data %x %x %x %x. \r\n", buffer[0], buffer[1], buffer[2], buffer[3]);
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 6, false);
if (err_code != NRF_SUCCESS) {
printf("TWI Error.");
}
}
/*******************************************************************************
* @file system_interface.c
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
******************************************************************************/
/*******************************************************************************
* [ ] ICM42670P IMU I2C
*
* nRF52840의 TWI(I2C) ICM42670P IMU
* .
*
* - I2C : 0x68 (ICM42670P )
* - I2C : SCL=P1.14, SDA=P1.15 (system_interface.h에서 )
* - TWI : NRFX_TWI_INSTANCE(0)
* - : 100kHz (NRF_TWI_FREQ_100K)
*
* :
* inv_io_hal_init() I2C SPI ( I2C만 )
* inv_io_hal_read_reg() (TX로 RX로 )
* inv_io_hal_write_reg() (+ TX)
*
* : I2C / 1
*
* : SPI4 ,
* I2C(UI_I2C) .
******************************************************************************/
/* board driver */
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#include "nrf.h"
#include "app_error.h"
#include "boards.h"
#include "nrfx_gpiote.h"
#include "nrfx_twi.h"
#include "system_interface.h"
#include "nrf_delay.h"
/* ICM42670P I2C 슬레이브 주소 및 직렬 쓰기 최대 바이트 수 */
#define ICM_I2C_ADDR 0x68
#define INV_MAX_SERIAL_WRITE 16
/* TWI(I2C) 인스턴스 생성 — system_interface.h의 ICM42670_I2C_INSTANCE(0)을 사용 */
const nrfx_twi_t m_twi_icm42670 = NRFX_TWI_INSTANCE(ICM42670_I2C_INSTANCE);
/*
* inv_i2c_master_uninitialize()
* I2C TWI .
* .
*/
void inv_i2c_master_uninitialize(void){
nrfx_twi_disable(&m_twi_icm42670);
nrfx_twi_uninit(&m_twi_icm42670);
}
/*
* inv_i2c_master_initialize()
* nRF52840 TWI .
* - SCL: P1.14, SDA: P1.15
* - : 100kHz
* - : (APP_IRQ_PRIORITY_HIGH)
* - ( )
*/
void inv_i2c_master_initialize(void){
ret_code_t err_code;
const nrfx_twi_config_t twi_icm42670_config = {
.scl = ICM42670_I2C_SCL_PIN,
.sda = ICM42670_I2C_SDA_PIN,
.frequency = NRF_TWI_FREQ_100K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
};
/* TWI 드라이버 초기화 (이벤트 핸들러=NULL → 블로킹 모드) */
err_code = nrfx_twi_init(&m_twi_icm42670, &twi_icm42670_config, NULL, NULL);
APP_ERROR_CHECK(err_code);
/* TWI 하드웨어 활성화 — 이후 tx/rx 가능 */
nrfx_twi_enable(&m_twi_icm42670);
}
/*
* icm42670_twi_tx()
* I2C . nrfx_twi_tx를 .
* no_stop=true이면 STOP (Repeated START를 )
*/
uint32_t icm42670_twi_tx( uint8_t device_id,
uint8_t const * p_data,
uint8_t length,
bool no_stop)
{
ret_code_t ret;
ret = nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop);
return ret;
}
/*
* icm42670_twi_rx()
* I2C . nrfx_twi_rx를 .
*/
uint32_t icm42670_twi_rx( uint8_t device_id,
uint8_t * p_data,
uint8_t length)
{
ret_code_t ret;
ret = nrfx_twi_rx(&m_twi_icm42670, device_id, p_data, length);
return ret;
}
/*
* inv_i2c_master_read_register()
* ICM42670P의 .
*
* :
* 1) TX: 1 (no_stop=true Repeated START )
* 2) RX:
*
* : TX, RX 1 .
*/
static unsigned long inv_i2c_master_read_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, unsigned char *RegisterValue){
//ret_code_t ret;
uint32_t ret;
uint8_t addr8 = (uint8_t)RegisterAddr;
/* 1단계: 읽을 레지스터 주소를 전송 (STOP 없이 → Repeated START 사용) */
ret = icm42670_twi_tx(Address, &addr8, 1, true);
if(ret != NRF_SUCCESS) {
/* 실패 시 1회 재시도 */
ret = icm42670_twi_tx(Address, &addr8, 1, true);
if(ret != NRF_SUCCESS) {
printf("ERR! i2c read-1\r\n");
}
}
/* 2단계: 해당 레지스터에서 데이터 수신 */
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
if(ret != NRF_SUCCESS) {
/* 실패 시 1회 재시도 */
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
if(ret != NRF_SUCCESS) {
printf("ERR! i2c read-2\r\n");
}
}
return ret;
}
/*
* inv_i2c_master_write_register()
* ICM42670P의 .
*
* :
* 1) [0] , [1~N]
* 2) TX: + (no_stop=false STOP )
*
* : 1 .
*/
static unsigned long inv_i2c_master_write_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, const unsigned char *RegisterValue){
uint32_t ret;
uint8_t buffer[1 + INV_MAX_SERIAL_WRITE]; /* 레지스터 주소(1) + 데이터(최대 16바이트) */
/* 버퍼 구성: [레지스터 주소][데이터 바이트들] */
buffer[0] = (uint8_t)RegisterAddr;
memcpy(buffer+1, RegisterValue, RegisterLen);
/* 주소+데이터를 한번에 전송 */
ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false);
if(ret != NRF_SUCCESS) {
/* 실패 시 1회 재시도 */
ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false);
if(ret != NRF_SUCCESS) {
printf("ERR! i2c write\r\n");
}
}
return ret;
}
/*
* inv_io_hal_init()
* IMU (I2C SPI) .
* serif->serif_type에 , I2C만 .
* : 0=, -1=
*/
int inv_io_hal_init(struct inv_imu_serif *serif)
{
switch (serif->serif_type) {
case UI_SPI4:
{
/* SPI4 초기화 — 현재 미구현 (I2C만 사용) */
break;
}
case UI_I2C:
inv_i2c_master_initialize();
break;
default:
return -1;
}
return 0;
}
/*
* inv_io_hal_read_reg()
* IMU : .
* I2C SPI .
*/
int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen)
{
switch (serif->serif_type) {
case UI_SPI4:
return 0;
case UI_I2C:
return inv_i2c_master_read_register(ICM_I2C_ADDR, reg, rlen, rbuffer);
default:
return -1;
}
}
/*
* inv_io_hal_write_reg()
* IMU : .
* I2C SPI .
*/
int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen)
{
switch (serif->serif_type) {
case UI_SPI4:
return 0;
case UI_I2C:
return inv_i2c_master_write_register(ICM_I2C_ADDR, reg, wlen, wbuffer);
default:
return -1;
}
}
/*
* cat_read()
* I2C (/).
* 8 , .
* : , .
*/
uint8_t cat_read(uint8_t device_id, uint8_t address, uint8_t *data)
{
uint8_t read_data = 0;
char adata[8];
ret_code_t err_code;
//address = 1|(address<<1);
address = (address & 0xFF);
/* 레지스터 주소 전송 (STOP 없이, Repeated START 준비) */
err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, &address, 1, true);
if (err_code != NRF_SUCCESS) {
// Handle error
// return;
}
/* 8바이트 데이터 수신 */
err_code = nrfx_twi_rx(&m_twi_icm42670, device_id, data, 8);
if (err_code != NRF_SUCCESS) {
// Handle error
return 0;
}
read_data = data[0];
memcpy(adata,data,8);
printf("Data %s . \r\n", adata);
return read_data;
}
/*
* cat_write()
* I2C (/).
* 1 + 1 .
* : buffer에 6 , 2 .
*/
void cat_write(uint8_t device_id, uint8_t address, uint8_t *data){
uint8_t buffer[7]={0x00,0x00,0x00,0x00,0x00,0x00,0x00};
address = (address & 0xFF);
buffer[0] = (address);
//buffer[1] =(data & 0xFF);
memcpy(buffer+1,data,6);
ret_code_t err_code;
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, 0x00, 1, false);
/* 주소(1바이트) + 데이터(1바이트) = 2바이트 전송 */
err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, buffer, 2, false);
// err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 2, false);
// nrfx_twi_rx(&m_twi_icm42670, device_id, p_data, length);
// nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop);
printf("Data %x %x %x %x. \r\n", buffer[0], buffer[1], buffer[2], buffer[3]);
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 6, false);
if (err_code != NRF_SUCCESS) {
printf("TWI Error.");
}
}
@@ -1,73 +1,72 @@
/*******************************************************************************
* @file system_interface.h
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
******************************************************************************/
/*******************************************************************************
* [Header overview] ICM42670P I2C communication interface declarations
*
* Pin definitions and function prototypes for communicating with the
* ICM42670P IMU sensor via nRF52840 TWI hardware.
*
* Pin assignment:
* - I2C SCL : P1.14
* - I2C SDA : P1.15
* - INT1 : P1.13 (data-ready interrupt)
* - INT2 : P0.26 (auxiliary interrupt, currently unused)
*
* TWI instance: 0
******************************************************************************/
#ifndef _SYSTEM_INTERFACE_H_
#define _SYSTEM_INTERFACE_H_
#include "inv_imu_transport.h"
#include <stdbool.h>
/* TODO: Move that somewhere else */
#ifndef TO_MASK
#define TO_MASK(a) (1U << (unsigned)(a))
#endif
#define ICM42670_I2C_INSTANCE 0 /**< I2C (TWI) instance index */
#define ICM42670_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15) /**< SDA pin: P1.15 */
#define ICM42670_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,14) /**< SCL pin: P1.14 */
#define ICM42670_INT1_PIN NRF_GPIO_PIN_MAP(1,13) /**< INT1 pin: P1.13 (data-ready interrupt) */
#define ICM42670_INT2_PIN NRF_GPIO_PIN_MAP(0,26) /**< INT2 pin: P0.26 (auxiliary, currently unused) */
/* I2C transmit wrapper — if no_stop=true, STOP condition is omitted for Repeated START */
uint32_t icm42670_twi_tx( uint8_t device_id,
uint8_t const * p_data,
uint8_t length,
bool no_stop);
/* I2C receive wrapper */
uint32_t icm42670_twi_rx( uint8_t device_id,
uint8_t * p_data,
uint8_t length);
/* Generic I2C read (debug/legacy) — reads 8 bytes and returns the first byte */
uint8_t cat_read (uint8_t device_id, uint8_t address, uint8_t *data);
/* Generic I2C write (debug/legacy) — sends address+data 2 bytes */
void cat_write (uint8_t device_id, uint8_t address, uint8_t *data);
/* Release I2C hardware (called before sleep or re-initialization) */
void inv_i2c_master_uninitialize(void);
/* Initialize I2C hardware (100kHz, blocking mode) */
void inv_i2c_master_initialize(void);
/* Initialize serial interface for IMU driver (I2C/SPI branch) */
int inv_io_hal_init(struct inv_imu_serif *serif);
/* IMU driver callback: register read */
int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen);
/* IMU driver callback: register write */
int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen);
#endif /* !_SYSTEM_INTERFACE_H_ */
/*******************************************************************************
* @file system_interface.h
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
******************************************************************************/
/*******************************************************************************
* [ ] ICM42670P I2C
*
* nRF52840 TWI ICM42670P IMU ,
*
* :
* - I2C SCL : P1.14
* - I2C SDA : P1.15
* - INT1 : P1.13 ( )
* - INT2 : P0.26 ( , )
*
* TWI : 0
******************************************************************************/
#ifndef _SYSTEM_INTERFACE_H_
#define _SYSTEM_INTERFACE_H_
#include "inv_imu_transport.h"
#include <stdbool.h>
/* TODO: Move that somewhere else */
#ifndef TO_MASK
#define TO_MASK(a) (1U << (unsigned)(a))
#endif
#define ICM42670_I2C_INSTANCE 0 /**< I2C(TWI) 인스턴스 인덱스 */
#define ICM42670_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15) /**< SDA 핀: P1.15 */
#define ICM42670_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,14) /**< SCL 핀: P1.14 */
#define ICM42670_INT1_PIN NRF_GPIO_PIN_MAP(1,13) /**< INT1 핀: P1.13 (데이터 준비 인터럽트) */
#define ICM42670_INT2_PIN NRF_GPIO_PIN_MAP(0,26) /**< INT2 핀: P0.26 (보조, 현재 미사용) */
/* I2C 전송 래퍼 — no_stop=true이면 Repeated START를 위해 STOP 컨디션 생략 */
uint32_t icm42670_twi_tx( uint8_t device_id,
uint8_t const * p_data,
uint8_t length,
bool no_stop);
/* I2C 수신 래퍼 */
uint32_t icm42670_twi_rx( uint8_t device_id,
uint8_t * p_data,
uint8_t length);
/* 범용 I2C 읽기 (디버그/레거시용) — 8바이트를 읽어 첫 바이트 반환 */
uint8_t cat_read (uint8_t device_id, uint8_t address, uint8_t *data);
/* 범용 I2C 쓰기 (디버그/레거시용) — 주소+데이터 2바이트 전송 */
void cat_write (uint8_t device_id, uint8_t address, uint8_t *data);
/* I2C 하드웨어 해제 (슬립 또는 재초기화 전 호출) */
void inv_i2c_master_uninitialize(void);
/* I2C 하드웨어 초기화 (100kHz, 블로킹 모드) */
void inv_i2c_master_initialize(void);
/* IMU 드라이버용 시리얼 인터페이스 초기화 (I2C/SPI 분기) */
int inv_io_hal_init(struct inv_imu_serif *serif);
/* IMU 드라이버 콜백: 레지스터 읽기 */
int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen);
/* IMU 드라이버 콜백: 레지스터 쓰기 */
int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen);
#endif /* !_SYSTEM_INTERFACE_H_ */
@@ -1,11 +1,11 @@
/*******************************************************************************
* @file led_control.c
* @brief Direct LED control driver
* @brief LED (BSP )
* @date 2026-03-30
*
* Implements dual-color LED (green/orange) blink patterns with a single app_timer.
* Simple on/off states use immediate GPIO control without a timer.
* Complex patterns (e.g. error) are handled by a phase-based state machine.
* app_timer 1 2 LED(/) blink
* on/off GPIO
* ( ) phase state machine으로
******************************************************************************/
#include "led_control.h"
@@ -13,20 +13,20 @@
#include "app_timer.h"
/*==============================================================================
* Internal constants
*
*============================================================================*/
#define MS_TO_TICKS(ms) APP_TIMER_TICKS(ms)
/*==============================================================================
* Colors
*
*============================================================================*/
#define COLOR_NONE 0
#define COLOR_GREEN 1
#define COLOR_ORANGE 2
/*==============================================================================
* Error pattern constants (No.7)
* 3Hz blink x3 = 166ms on + 166ms off x 3 = ~1s, then off for 1s
* (No.7)
* 3Hz 3 = 166ms on + 166ms off × 3 = ~1, 1
*============================================================================*/
#define ERROR_BLINK_ON_MS 166
#define ERROR_BLINK_OFF_MS 166
@@ -34,42 +34,42 @@
#define ERROR_PAUSE_MS 1000
/*==============================================================================
* Pattern table (for simple blink)
* ( blink )
*============================================================================*/
typedef struct
{
uint32_t on_ms; /* LED on duration (ms) */
uint32_t off_ms; /* LED off duration (ms) */
uint8_t color; /* COLOR_GREEN or COLOR_ORANGE */
bool repeat; /* true: repeat forever, false: once then OFF */
uint32_t on_ms; /* LED 켜짐 시간 (ms) */
uint32_t off_ms; /* LED 꺼짐 시간 (ms) */
uint8_t color; /* COLOR_GREEN 또는 COLOR_ORANGE */
bool repeat; /* true: 무한 반복, false: 1회 후 OFF */
} 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 }, /* Green on 2s, stay on */
[LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false }, /* Green on 2s, then OFF */
[LED_STATE_ADVERTISING] = { 500, 500, COLOR_GREEN, true }, /* Green blink 1s interval */
[LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true }, /* Green 1s on / 3s off */
[LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true }, /* Orange blink 1s interval */
[LED_STATE_ALIGN_COMPLETE] = { 3000, 1000, COLOR_GREEN, true }, /* Green 3s on / 1s off */
[LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true } /* Separate state machine */
[LED_STATE_POWER_ON] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → 유지 */
[LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → OFF */
[LED_STATE_ADVERTISING] = { 500, 500, COLOR_GREEN, true }, /* 초록 점멸 1초 */
[LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true }, /* 초록 1 on / 3 off */
[LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true }, /* 주황 점멸 1초 */
[LED_STATE_ALIGN_COMPLETE] = { 3000, 1000, COLOR_GREEN, true }, /* 초록 3 on / 1 off */
[LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true } /* 별도 state machine */
};
/*==============================================================================
* Module variables
*
*============================================================================*/
APP_TIMER_DEF(m_led_timer);
static led_state_t m_current_state = LED_STATE_OFF;
static bool m_phase_on; /* true: LED on phase, false: off phase */
static bool m_phase_on; /* true: LED 켜진 구간, false: 꺼진 구간 */
/* Error pattern only */
static uint8_t m_error_blink_cnt; /* Blink count so far */
/* 에러 패턴 전용 */
static uint8_t m_error_blink_cnt; /* 현재까지 깜빡인 횟수 */
static uint8_t m_error_phase; /* 0: blink-on, 1: blink-off, 2: pause */
/*==============================================================================
* GPIO helpers
* GPIO
*============================================================================*/
static inline void led_green_on(void)
@@ -122,7 +122,7 @@ static void led_color_on(uint8_t color)
}
/*==============================================================================
* Timer start helper
*
*============================================================================*/
static void timer_start_ms(uint32_t ms)
{
@@ -131,7 +131,7 @@ static void timer_start_ms(uint32_t ms)
}
/*==============================================================================
* Error pattern state machine (No.7)
* state machine (No.7)
* phase 0: LED ON (166ms) phase 1
* phase 1: LED OFF (166ms) cnt++ cnt<3 ? phase 0 : phase 2
* phase 2: PAUSE (1000ms) cnt=0, phase 0
@@ -148,30 +148,30 @@ static void error_pattern_tick(void)
{
switch (m_error_phase)
{
case 0: /* ON phase done -> OFF */
case 0: /* ON 구간 끝 → OFF */
led_all_off();
m_error_phase = 1;
timer_start_ms(ERROR_BLINK_OFF_MS);
break;
case 1: /* OFF phase done */
case 1: /* OFF 구간 끝 */
m_error_blink_cnt++;
if (m_error_blink_cnt < ERROR_BLINK_COUNT)
{
/* Back to ON */
/* 다시 ON */
m_error_phase = 0;
led_color_on(COLOR_ORANGE);
timer_start_ms(ERROR_BLINK_ON_MS);
}
else
{
/* 3 blinks done -> pause */
/* 3회 완료 → pause */
m_error_phase = 2;
timer_start_ms(ERROR_PAUSE_MS);
}
break;
case 2: /* Pause done -> restart */
case 2: /* pause 끝 → 처음부터 */
m_error_blink_cnt = 0;
m_error_phase = 0;
led_color_on(COLOR_ORANGE);
@@ -184,13 +184,13 @@ static void error_pattern_tick(void)
}
/*==============================================================================
* Timer callback
*
*============================================================================*/
static void led_timer_handler(void * p_context)
{
(void)p_context;
/* Error state handled separately */
/* 에러 상태는 별도 처리 */
if (m_current_state == LED_STATE_ERROR)
{
error_pattern_tick();
@@ -201,7 +201,7 @@ static void led_timer_handler(void * p_context)
if (m_phase_on)
{
/* ON -> OFF transition */
/* ON→OFF 전환 */
led_all_off();
m_phase_on = false;
@@ -211,40 +211,40 @@ static void led_timer_handler(void * p_context)
}
else if (!p->repeat)
{
/* One-shot: if off_ms == 0, stay on (POWER_ON) or turn off (POWER_OFF) */
/* 1회성: off_ms == 0 이면 그냥 유지 (POWER_ON) 또는 끄기 (POWER_OFF) */
if (m_current_state == LED_STATE_POWER_OFF)
{
led_all_off();
m_current_state = LED_STATE_OFF;
}
/* POWER_ON: stay lit, no timer */
/* POWER_ON: 점등 유지 상태 → 타이머 x */
}
}
else
{
/* OFF -> ON transition */
/* OFF ON 전환 */
if (p->repeat)
{
led_color_on(p->color);
m_phase_on = true;
timer_start_ms(p->on_ms);
}
/* No current case where repeat == false && off_ms > 0 */
/* repeat == false && off_ms > 0 인 경우는 현재 없음 */
}
}
/*==============================================================================
* Public functions
*
*============================================================================*/
void led_init(void)
{
/* Configure GPIO outputs */
/* GPIO 출력 설정 */
nrf_gpio_cfg_output(LED_PIN_GREEN);
nrf_gpio_cfg_output(LED_PIN_ORANGE);
led_all_off();
/* Create timer (single-shot) */
/* 타이머 생성 (single-shot) */
app_timer_create(&m_led_timer, APP_TIMER_MODE_SINGLE_SHOT, led_timer_handler);
m_current_state = LED_STATE_OFF;
@@ -254,7 +254,7 @@ void led_set_state(led_state_t state)
{
if (state >= LED_STATE_COUNT) return;
/* Stop previous pattern */
/* 이전 패턴 중단 */
app_timer_stop(m_led_timer);
led_all_off();
@@ -266,15 +266,15 @@ void led_set_state(led_state_t state)
switch (state)
{
case LED_STATE_OFF:
/* Already all off */
/* 이미 all off */
break;
/* Error pattern: separate state machine */
/* 에러 패턴: 별도 state machine */
case LED_STATE_ERROR:
error_pattern_start();
break;
/* All others: start from ON phase */
/* 그 외: on 구간부터 시작 */
default:
led_color_on(p->color);
m_phase_on = true;
@@ -0,0 +1,52 @@
/*******************************************************************************
* @file led_control.h
* @brief LED 직접 제어 드라이버 (BSP 미사용)
* @date 2026-03-30
*
* 녹색(P0.12) + 주황(P0.29) 2색 LED를 app_timer 기반으로 제어
* 각 상태별 on/off 시간, 색상, 반복 패턴을 테이블로 관리
******************************************************************************/
#ifndef LED_CONTROL_H__
#define LED_CONTROL_H__
#include <stdint.h>
#include <stdbool.h>
/*==============================================================================
* LED 핀 정의
*============================================================================*/
#define LED_PIN_GREEN NRF_GPIO_PIN_MAP(0, 12) /* 녹색 LED */
#define LED_PIN_ORANGE NRF_GPIO_PIN_MAP(0, 29) /* 주황 LED */
#define LED_ACTIVE_LOW 1 /* 1 = Active Low (GPIO LOW에서 LED 켜짐) */
/*==============================================================================
* LED 상태 열거형
*============================================================================*/
typedef enum
{
LED_STATE_OFF = 0, /* 모든 LED 소등 */
LED_STATE_POWER_ON, /* 1: 전원 ON - 초록 소등 → 녹색 점등 2초 */
LED_STATE_POWER_OFF, /* 2: 전원 OFF - 초록 점등 2초 → 소등 */
LED_STATE_ADVERTISING, /* 3: 블루투스 스캐닝 - 초록 점멸 1초 간격(반복) */
LED_STATE_DETACH_WARNING, /* 4: 정상 작동중(미부착 시) - 초록 점등 1초 / 소등 3초 (반복) */
LED_STATE_ALIGN_SEARCHING, /* 5: 정렬모드(탐지중) - 주황 점멸 1초 간격 (반복) */
LED_STATE_ALIGN_COMPLETE, /* 6: 정렬모드(탐지 완료) - 초록 점등 3초 / 소등 1초 */
LED_STATE_ERROR, /* 7: 오류 발생 - 주황 3Hz 깜빡 3회 / 꺼짐 1초 (반복) */
LED_STATE_COUNT /* 배열 크기 */
} led_state_t;
/*==============================================================================
* 공개 함수
*============================================================================*/
/** @brief LED GPIO 초기화 + 타이머 생성 - main()에서 1회 호출 */
void led_init(void);
/** @brief LED 상태 변경 - 이전 패턴을 즉시 중단하고 새 패턴 시작 */
void led_set_state(led_state_t state);
/** @brief 현재 LED 상태 조회 */
led_state_t led_get_state(void);
#endif /* LED_CONTROL_H__ */
File diff suppressed because it is too large Load Diff
@@ -3,49 +3,43 @@
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief VesiScan BASIC main header file
* @brief VesiScan BASIC 메인 헤더 파일
*
* [System Overview]
* VesiScan BASIC is an nRF52840-based BLE bladder monitoring patch device.
* This header defines enums, function declarations, and global variables used system-wide.
* [시스템 개요]
* VesiScan BASIC nRF52840 기반 BLE 방광 모니터링 패치 디바이스이다.
* 본 헤더는 시스템 전역에서 사용하는 열거형, 함수 선언, 전역 변수를 정의한다.
*
* [Communication]
* - BLE NUS (Nordic UART Service): binary protocol with smartphone app
* - Physical UART (1Mbps): debug and factory testing
* [통신 방식]
* - BLE NUS (Nordic UART Service): 스마트폰 앱과 바이너리 프로토콜 통신
* - 물리 UART (1Mbps): 디버그 및 공장 테스트용
*
* [Data Transmission Flow]
* 1. Receive command from app/UART -> received_command_process()
* 2. Collect sensor data (battery, temperature, IMU, pressure)
* 3. Build binary packets via format_data() family
* 4. Transmit over BLE with CRC16 appended via dr_binary_tx_safe()
* [데이터 전송 흐름]
* 1. 앱/UART에서 명령 수신 → received_command_process()
* 2. 센서 데이터 수집 (배터리, 온도, IMU, 압력)
* 3. format_data() 계열 함수로 바이너리 패킷 생성
* 4. dr_binary_tx_safe()로 CRC16 추가 후 BLE 전송
******************************************************************************/
#ifndef MAIN_H__
#define MAIN_H__
/* -------------------------------------------------------------------------
* Firmware Identification Code
* - VBTFW0100 = Development (test) build Ver 1.00
* - VB0FW0100 = Production build Ver 1.00
* Firmware 식별 코드
* - VBTFW0100 = 개발(시험)용 Ver 1.00
* - VB0FW0100 = 양산용 Ver 1.00
*
* Firmware Version Update History
* - VBTFW0101 : Merged reb+red packets (single packet per channel), 260330 jhChun
* - VBTFW0102 : Added LED state command (msl) and re-pairing support, 260331 jhChun
* - VBTFW0103 260416 jhChun
* : Stabilized ADC measurement by clearing buffers before sampling.
* : Improved low-battery detection and automatic power-off handling.
* : Updated BLE security, bonding, and advertising timeout behavior.
* : Cleaned up command parsing and removed unused project files.
* - VBTFW0111 260421 jhChun
* - VBTFW0101 : reb+red 패킷 병합 (채널당 단일 패킷), 260330 jhChun
* - VBTFW0102 : LED 상태 설정 명령(msl) 추가 및 재페어링 허용 등 260331 jhChun
------------------------------------------------------------------------- */
#define FIRMWARE_VERSION "VBTFW0111"
#define FIRMWARE_VERSION "VBTFW0102"
/*==============================================================================
* Data Length Constants
* 데이터 길이 상수
*============================================================================*/
#define SERIAL_NO_LENGTH 12 /* Serial number length (e.g. "VB026030000") */
#define HW_NO_LENGTH 12 /* Hardware number (version) length */
#define PASSKEY_LENGTH 6 /* BLE pairing passkey length (6 digits) */
#define SERIAL_NO_LENGTH 12 /* 시리얼 번호 길이 (예: "VB026030000") */
#define HW_NO_LENGTH 12 /* 하드웨어 번호(버전) 길이 */
#define PASSKEY_LENGTH 6 /* BLE 페어링 패스키 길이 (숫자 6자리) */
#include <stdio.h>
#include <stdint.h>
@@ -55,127 +49,128 @@
#include "boards.h"
/*==============================================================================
* Enum Definitions
* 열거형 정의
*============================================================================*/
/* Device ON/OFF control enum (EEPROM, power, etc.) */
/* 디바이스 ON/OFF 제어용 열거형 (EEPROM, 전원 등) */
typedef enum
{
OFF = 0, /* Off */
ON = 1 /* On */
OFF = 0, /* 꺼짐 */
ON = 1 /* 켜짐 */
}on_off_cont_t;
/* Command source identifier (BLE or UART) */
/* 명령 수신 경로 구분 (BLE 또는 UART) */
typedef enum
{
CMD_BLE = 0, /* Command received via BLE NUS */
CMD_UART = 1 /* Command received via physical UART */
CMD_BLE = 0, /* BLE NUS를 통해 수신된 명령 */
CMD_UART = 1 /* 물리 UART를 통해 수신된 명령 */
}which_cmd_t;
/* Chamber auto-test mode (when FEATURE_CHAMBER_AUTO_TEST enabled) */
/* 챔버 자동 테스트 모드 (FEATURE_CHAMBER_AUTO_TEST 활성 시) */
#if FEATURE_CHAMBER_AUTO_TEST
typedef enum
{
SIMPLE_AUTO_MODE = 0, /* Simple auto mode */
HALF_AUTO_MODE = 1, /* Semi-auto mode */
FULL_AUTO_MODE = 2, /* Full auto mode */
NONE_AUTO_MODE = 3 /* No auto mode */
SIMPLE_AUTO_MODE = 0, /* 간단 자동 모드 */
HALF_AUTO_MODE = 1, /* 반자동 모드 */
FULL_AUTO_MODE = 2, /* 전체 자동 모드 */
NONE_AUTO_MODE = 3 /* 자동 모드 없음 */
}auto_meas_mode_t;
#endif
/* BLE connection state */
/* BLE 연결 상태 */
typedef enum
{
BLE_DISCONNECTED_ST = 0, /* BLE disconnected */
BLE_CONNECTED_ST = 1 /* BLE connected */
BLE_DISCONNECTED_ST = 0, /* BLE 미연결 */
BLE_CONNECTED_ST = 1 /* BLE 연결됨 */
}ble_status_t;
/*==============================================================================
* Function Declarations
* 함수 선언
*============================================================================*/
#if FEATURE_SECURE_CONNECTION
/* Start BLE advertising (if erase_bonds=true, delete bond info first) */
/* BLE 광고 시작 (erase_bonds=true이면 본딩 정보 삭제 후 시작) */
static void advertising_start(bool erase_bonds);
#endif
/* Enter sleep mode: show LED, then power off after POWER_OFF_DELAY (3s) */
/* 슬립 모드 진입: LED 표시 후 POWER_OFF_DELAY(3초) 후 전원 차단 */
void sleep_mode_enter(void);
/* Power-off timer callback: physically cut power after POWER_OFF_DELAY */
/* 전원 OFF 타이머 콜백: POWER_OFF_DELAY 경과 후 실제 전원 차단 */
static void t_power_off_timeout_handler(void * p_context);
/* Device power off: show LED, then delayed power cut via timer */
/* 디바이스 전원 OFF: LED 표시 후 타이머로 지연 전원 차단 */
void device_power_off(void);
/* Power control handler: physical power ON/OFF via POWER_HOLD pin */
/* 전원 제어 핸들러: POWER_HOLD 핀으로 물리적 전원 ON/OFF */
static void power_control_handler(on_off_cont_t device_power_st);
/* Power button state machine (timer callback, 5ms interval):
* - Short press (<1.5s): power OFF
* - Medium press (1.5s~10s): start boot sequence
* - Long press (>10s): factory reset (passkey reset + power OFF) */
/* 전원 버튼 상태머신 (타이머 콜백, 5ms 간격):
* - 짧은 눌림(<1.5): 전원 OFF
* - 중간 눌림(1.5~10): 부팅 시퀀스 시작
* - 긴 눌림(>10): 공장 초기화 (패스키 리셋 + 전원 OFF) */
static void main_s(void * p_context);
/* Peer Manager timer callback: force BLE disconnect if reset_status==5 */
/* Peer Manager 타이머 콜백: reset_status==5이면 BLE 연결 강제 해제 */
static void PM_s(void * p_context);
//static void main_re(void * p_context);
/* Main routine handler (legacy, currently unused) */
/* 메인 루틴 핸들러 (레거시, 현재 미사용) */
static void main_routine_handler(void * p_context);
/*------------------------------------------------------------------------------
* Data Transmission Functions
* 데이터 전송 함수
*----------------------------------------------------------------------------*/
/* Send ASCII text over BLE (up to '\r', CRC16 appended automatically) */
/* ASCII 텍스트 BLE 전송 ('\r'까지 전송, CRC16 자동 추가) */
void data_tx_handler(char const *p_data_to_send);
/* Safe binary data BLE transmission (CRC16 appended, with retry logic)
* @param ble_bin_buff Binary buffer to transmit
* @param length Data length in uint16_t words (actual bytes = length x 2) */
/* 바이너리 데이터 BLE 안전 전송 (CRC16 자동 추가, 재시도 로직 포함)
* @param ble_bin_buff 전송할 바이너리 버퍼
* @param length 데이터 길이 (uint16_t 워드 단위, 실제 바이트 = length × 2) */
void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length);
/* SoftDevice-compatible delay (nrf_delay_ms wrapper) */
/* SoftDevice 호환 딜레이 (nrf_delay_ms 래퍼) */
void dr_sd_delay_ms(uint32_t ms);
/*------------------------------------------------------------------------------
* Binary Packet Format Functions
* Packet structure: [4-byte tag][data][2-byte CRC16]
* 바이너리 패킷 포맷 함수
* 패킷 구조: [4바이트 태그][데이터][2바이트 CRC16]
*----------------------------------------------------------------------------*/
/* Format single uint16_t value: [tag 4B][value 2B] */
/* 단일 uint16_t 값 포맷: [tag 4B][value 2B] */
void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value) ;
/* Format uint16_t array: [tag 4B][data0 2B][data1 2B]... */
/* uint16_t 배열 포맷: [tag 4B][data0 2B][data1 2B]... */
void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length);
/* Format uint8_t byte array: [tag 4B][byte0][byte1]... */
/* uint8_t 바이트 배열 포맷: [tag 4B][byte0][byte1]... */
void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_array, size_t length);
/* Format ASCII string: [tag 4B][char0][char1]... */
/* ASCII 문자열 포맷: [tag 4B][char0][char1]... */
void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length);
/*==============================================================================
* Global Variables (extern)
* 전역 변수 (extern)
*============================================================================*/
extern volatile bool data_tx_in_progress; /* BLE TX in progress flag */
extern volatile bool ble_connection_st; /* BLE connection state (0=disconnected, 1=connected) */
extern volatile bool data_tx_in_progress; /* BLE TX 전송 진행 중 플래그 */
extern volatile bool ble_connection_st; /* BLE 연결 상태 (0=미연결, 1=연결) */
extern volatile bool processing; /* 센서 데이터 처리 중 플래그 (중복 명령 방지) */
/* 2026-03-17: Global variables moved from cmd_parse.c to main.c */
extern char SERIAL_NO[SERIAL_NO_LENGTH]; /* Serial number */
extern char HW_NO[HW_NO_LENGTH]; /* Hardware number */
extern char m_static_passkey[PASSKEY_LENGTH]; /* BLE static passkey */
extern bool bond_data_delete; /* Bond data delete request flag */
extern uint32_t m_life_cycle; /* Device life cycle counter */
extern uint8_t resetCount; /* Communication timeout counter */
extern bool info4; /* Measurement with extra info flag */
extern uint8_t m_reset_status; /* Reset status code */
/* 2026-03-17: cmd_parse.c에서 main.c로 이동한 전역변수 */
extern char SERIAL_NO[SERIAL_NO_LENGTH]; /* 시리얼 번호 */
extern char HW_NO[HW_NO_LENGTH]; /* 하드웨어 번호 */
extern char m_static_passkey[PASSKEY_LENGTH]; /* BLE 정적 패스키 */
extern bool bond_data_delete; /* 본딩 데이터 삭제 요청 플래그 */
extern uint32_t m_life_cycle; /* 디바이스 수명 사이클 카운터 */
extern uint8_t resetCount; /* 통신 타임아웃 카운터 */
extern bool info4; /* 추가 정보 포함 측정 플래그 */
extern uint8_t m_reset_status; /* 리셋 상태 코드 */
extern uint8_t ble_bin_buffer[]; /* BLE binary response buffer */
extern uint8_t ble_bin_buffer[]; /* BLE 바이너리 응답 버퍼 */
/* Send error response */
/* 에러 응답 전송 */
void param_error(const char *cmd);
#endif //MAIN_H__
@@ -0,0 +1,280 @@
/*******************************************************************************
TEST medi50 Dec 23
*******************************************************************************
*
* [모듈 개요]
* 메인 이벤트 루프 타이머 모듈 (10ms 간격, 싱글샷 모드).
*
* 센서 데이터 수집 및 시스템 제어 이벤트를 플래그 기반으로 디스패치한다.
* app_timer 싱글샷 모드를 사용하므로, 이벤트 처리 완료 후
* 필요 시 main_timer_start()로 수동 재시작해야 한다.
*
* [이벤트 플래그 및 처리 순서]
* motion_raw_data_enabled → IMU 데이터 읽기 (icm42670_main 호출)
* - motion_data_once == true: 단발성 읽기 (HW I2C 초기화 후 1회)
* - motion_data_once == false: 연속 읽기 (BLE 전송 대기 중이 아닐 때)
* go_batt → 배터리 전압 측정 (battery_level_meas)
* go_temp → 온도 측정 (tmp235_voltage_level_meas)
* go_device_power_off → 디바이스 전원 OFF (device_power_off)
* go_sleep_mode_enter → 슬립 모드 진입 (sleep_mode_enter)
* go_NVIC_SystemReset → NVIC 시스템 리셋
*
* [info4 모드 측정 순서]
* IMU 연속 읽기 → go_batt(배터리) → go_temp(온도) → motion_data_once(IMU 단발)
* 온도 측정 완료 시 motion_data_once=true로 설정하여 다시 IMU로 돌아간다.
*
* [타이머 설정]
* - 일반 모드: 10ms 간격 (MAIN_LOOP_INTERVAL)
* - FEATURE_DETAIL_VALUE_FULL 모드: 80ms 간격 (디테일 프린트아웃용)
*
******************************************************************************/
#include "sdk_common.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h"
#include "nrf_log.h"
#include "nrf_drv_gpiote.h"
#include "battery_saadc.h"
#include "app_timer.h"
#include "main.h"
#include "app_raw_main.h"
#include "main_timer.h"
#include "tmp235_q1.h"
//#include "fstorage.h"
#include "power_control.h"
#include "main.h" /* 2026-03-17: cmd_parse.h 삭제 → main.h */
#include "debug_print.h"
#include "i2c_manager.h" //add cj
/* 메인 루프 싱글샷 타이머 인스턴스 */
APP_TIMER_DEF(m_main_loop_timer_id);
#if FEATURE_DETAIL_VALUE_FULL
/* 디테일 프린트아웃 모드: 80ms 간격으로 메인 루프 실행 */
#define MAIN_LOOP_INTERVAL 80 /* 디테일 프린트아웃이 있을경우 Full_Mode Main Prosessing 수행하는 타이머 */
//extern bool pd_adc_full_a_start;
//extern bool pd_adc_full_b_start;
//extern bool pd_adc_full_c_start;
//extern bool pd_adc_full_d_start;
//extern bool pd_adc_full_end;
extern which_cmd_t cmd_type_t;
#else
/* 일반 모드: 10ms 간격으로 메인 루프 실행 */
#define MAIN_LOOP_INTERVAL 10
#endif
/* ========================================================================== */
/* 이벤트 플래그 (외부 모듈에서 설정, main_loop에서 처리) */
/* ========================================================================== */
bool go_batt= false; /* 배터리 측정 요청 플래그 */
bool go_temp= false; /* 온도 측정 요청 플래그 */
bool go_device_power_off = false; /* 디바이스 전원 OFF 요청 플래그 */
bool go_sleep_mode_enter = false; /* 슬립 모드 진입 요청 플래그 */
bool go_NVIC_SystemReset = false; /* 시스템 리셋 요청 플래그 */
bool motion_raw_data_enabled = false; /* IMU 모션 데이터 읽기 활성화 플래그 */
bool ble_got_new_data = false; /* BLE로 새 데이터 전송 완료 여부 */
bool motion_data_once = false; /* IMU 단발성 읽기 모드 (true: 1회만 읽기) */
/**
* @brief 메인 이벤트 루프 (싱글샷 타이머 콜백)
*
* 플래그 기반 이벤트 디스패처로, 설정된 플래그에 따라 해당 처리를 수행한다.
* 싱글샷 타이머이므로 연속 실행이 필요한 경우 처리 내부에서 main_timer_start()를
* 다시 호출하여 타이머를 재시작해야 한다.
*
* [처리 우선순위] (코드 순서대로 검사)
* 1. IMU 모션 데이터 (motion_raw_data_enabled)
* 2. 배터리 측정 (go_batt)
* 3. 온도 측정 (go_temp)
* 4. 전원 OFF (go_device_power_off)
* 5. 슬립 모드 (go_sleep_mode_enter)
* 6. 시스템 리셋 (go_NVIC_SystemReset)
*/
void main_loop(void * p_context) /* For x ms */
{
UNUSED_PARAMETER(p_context);
#if FEATURE_DETAIL_VALUE_FULL
// if(pd_adc_full_a_start == true) { // A mode
// main_timer_stop();
// printf("main_loop_A\r\n");
// full_adc_start();
// }else if(pd_adc_full_b_start == true) { // B mode
// main_timer_stop();
// printf("main_loop_B\r\n");
// full_adc_start();
// }else if(pd_adc_full_c_start == true) { // C mode
// main_timer_stop();
// printf("main_loop_C\r\n");
// full_adc_start();
// }else if(pd_adc_full_d_start == true) { // D mode
// main_timer_stop();
// printf("main_loop_D\r\n");
// full_adc_start();
// }else if(pd_adc_full_end == true) { // Completed
// pd_adc_full_end = false;
// main_timer_stop();
// printf("main_loop_END\r\n");
// if(cmd_type_t == CMD_BLE) {
// full_send_timer_start();
// }
// }
#endif
// For Motion Data Sampling
/* ---- IMU 모션 데이터 읽기 ---- */
/*
* motion_raw_data_enabled가 true이면 IMU(ICM42670P) 데이터를 읽는다.
*
* motion_data_once == true:
* 단발성 읽기 모드. HW I2C를 초기화한 후 icm42670_main()을 1회 호출.
* info4 모드에서 배터리/온도 측정 후 다시 IMU로 돌아올 때 사용.
*
* motion_data_once == false:
* 연속 읽기 모드. BLE 전송 대기 중(ble_got_new_data==false)이면
* icm42670_main()을 호출하고 10ms 후 타이머를 재시작하여 반복 실행.
*/
if(motion_raw_data_enabled == true) {
main_timer_stop(); /* 타이머 정지 (재진입 방지) */
if(motion_data_once == true)
{
/* 단발성 모드: HW I2C 초기화 후 IMU 데이터 1회 읽기 */
hw_i2c_init_once(); /* HW TWI 모드로 전환 (400kHz) */
icm42670_main(); /* IMU 데이터 읽기 및 처리 */
}
else{
/* 연속 모드: BLE 전송 대기 중이 아니면 반복 읽기 */
if(ble_got_new_data==false){
//for(uint16_t i=0 ; i<60 ;i++)
//{
DBG_PRINTF("IMU \r\n");
icm42670_main(); /* IMU 데이터 읽기 */
motion_raw_data_enabled = true; /* 플래그 유지 (연속 읽기) */
main_timer_start_ms(1000); /* 1초 후 다음 IMU 읽기 */
}
// else if(ble_got_new_data==true){
// motion_data_once = true;
// }
}
}
/* ---- 배터리 전압 측정 ---- */
/*
* go_batt 플래그가 true이면 배터리 레벨을 측정한다.
* info4 모드에서 IMU 연속 읽기 이후 호출되는 단계.
* 측정 완료 후 타이머가 정지된 상태로 유지된다.
*/
if(go_batt == true) {
DBG_PRINTF("IMU BATT\r\n");
main_timer_stop(); /* 타이머 정지 */
go_batt = false; /* 플래그 소비 (1회 실행) */
// go_temp = true;
battery_level_meas(); /* SAADC를 이용한 배터리 전압 측정 */
// nrf_delay_ms(20);
// m48_adc_start_init();
// main_timer_start();
}
/* ---- 온도 측정 ---- */
/*
* go_temp 플래그가 true이면 TMP235-Q1 센서로 온도를 측정한다.
* info4 모드에서 배터리 측정 이후 호출되는 단계.
* 측정 완료 후 motion_data_once=true로 설정하여
* 다음 IMU 읽기는 단발성 모드(HW I2C 재초기화)로 전환된다.
*/
if(go_temp == true) {
DBG_PRINTF("IMU Temp\r\n");
main_timer_stop(); /* 타이머 정지 */
// go_batt = false;
go_temp = false; /* 플래그 소비 (1회 실행) */
motion_data_once = true; /* 다음 IMU 읽기를 단발성 모드로 전환 */
tmp235_voltage_level_meas(); /* TMP235-Q1 온도 센서 전압 측정 */
// motion_raw_data_enabled = true;
// main_timer_start();
}
/* ---- 시스템 제어 이벤트 처리 ---- */
/* 디바이스 전원 OFF 처리 */
if(go_device_power_off == true){
main_timer_stop(); /* 타이머 정지 */
DBG_PRINTF("Off main_timer\r\n");
device_power_off(); /* 디바이스 전원 OFF 실행 */
}
/* 슬립 모드 진입 처리 */
if(go_sleep_mode_enter == true){
main_timer_stop(); /* 타이머 정지 */
DBG_PRINTF("sleep main timer\r\n");
sleep_mode_enter(); /* 슬립 모드 진입 실행 */
}
/* NVIC 시스템 리셋 처리 */
if(go_NVIC_SystemReset == true) {
main_timer_stop(); /* 타이머 정지 */
NVIC_SystemReset(); /* ARM Cortex-M4 시스템 리셋 */
}
}
/**
* @brief 메인 루프 타이머 시작
*
* 싱글샷 모드로 MAIN_LOOP_INTERVAL(10ms 또는 80ms) 후 main_loop()를 호출
*/
void main_timer_start(void)
{
APP_ERROR_CHECK(app_timer_start(m_main_loop_timer_id, APP_TIMER_TICKS(MAIN_LOOP_INTERVAL), NULL));
}
/**
* @brief 지정된 간격(ms)으로 메인 루프 타이머 시작
*
* IMU 연속 스트리밍 등 기본 간격과 다른 주기가 필요할 때 사용
*/
void main_timer_start_ms(uint32_t interval_ms)
{
APP_ERROR_CHECK(app_timer_start(m_main_loop_timer_id, APP_TIMER_TICKS(interval_ms), NULL));
}
/**
* @brief 메인 루프 타이머 정지
*/
void main_timer_stop(void)
{
APP_ERROR_CHECK(app_timer_stop(m_main_loop_timer_id));
}
/**
* @brief 메인 루프 타이머 초기화 (앱 시작 시 1회 호출)
*
* 싱글샷 모드 타이머를 생성하고, 콜백으로 main_loop()를 등록
* 싱글샷이므로 매 호출마다 main_timer_start()로 수동 재시작해야 함
*/
void main_timer_init(void)
{
APP_ERROR_CHECK(app_timer_create(&m_main_loop_timer_id, APP_TIMER_MODE_SINGLE_SHOT, main_loop));
}
@@ -0,0 +1,37 @@
/*******************************************************************************
* @file timer_routine.h
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
*******************************************************************************
*
* [헤더 개요]
* 메인 이벤트 루프 타이머의 공용 인터페이스 헤더.
*
* 10ms(일반) 또는 80ms(디테일) 간격의 싱글샷 타이머를 사용하여
* main_loop() 콜백에서 센서 데이터 수집 및 시스템 제어를 수행한다.
*
* [주요 함수]
* main_timer_start() : 타이머 시작 (싱글샷, 수동 재시작 필요)
* main_timer_stop() : 타이머 정지
* main_timer_init() : 타이머 초기화 (앱 시작 시 1회 호출)
*
******************************************************************************/
#ifndef TIMER_ROUTINE_H__
#define TIMER_ROUTINE_H__
/** @brief 메인 루프 타이머 시작 (싱글샷, MAIN_LOOP_INTERVAL 후 main_loop 호출) */
void main_timer_start(void);
/** @brief 지정된 간격(ms)으로 메인 루프 타이머 시작 */
void main_timer_start_ms(uint32_t interval_ms);
/** @brief 메인 루프 타이머 정지 */
void main_timer_stop(void);
/** @brief 메인 루프 타이머 초기화 (앱 시작 시 1회, 싱글샷 모드로 생성) */
void main_timer_init(void);
#endif //TIMER_ROUTINE_H__
@@ -1,292 +0,0 @@
/*==============================================================================
* battery_saadc.c - Battery voltage ADC measurement
*
* Measures battery voltage via nRF52840 SAADC on AIN2:
* - 12-bit resolution, 4x oversampling
* - Periodic safety check via battery_loop timer (60 s interval)
* - Sequential: battery -> temperature measurement
* - Auto power-off after 5 consecutive readings below 3500 mV or above 40 C
* - In info4 mode (bulk sensor collection): stores to info_batt
*
* Voltage conversion:
* mV = ADC_VALUE * (600 / 4095) * 6 * 1.42 (resistor divider correction)
*============================================================================*/
#include "sdk_common.h"
#include <stdint.h>
#include <string.h>
#include "nrf.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_timer.h"
#include "ble_nus.h"
#include "nrf_log.h"
#include "main.h"
#include "app_timer.h"
#include "battery_saadc.h"
#include "main_timer.h"
#include "tmp235_q1.h"
#include "dr_piezo.h"
#include "debug_print.h"
/* SAADC internal reference voltage (mV, float) */
#define BATTERY_REF_VOLTAGE_IN_MILLIVOLTS 600.0f
/* 1/3 prescaling compensation (input divided by 3, then x2 = total x6) */
#define BATTERY_PRE_SCALING_COMPENSATION 6.0f
/* 12-bit ADC maximum digital value */
#define BATTERY_ADC_RES_12BITS 4095.0f
/* Convert raw ADC value to millivolts */
#define BATTERY_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
((((ADC_VALUE) * BATTERY_REF_VOLTAGE_IN_MILLIVOLTS) / BATTERY_ADC_RES_12BITS) * BATTERY_PRE_SCALING_COMPENSATION)
/* Single ADC buffer (uninit after each measurement, no double-buffer needed) */
static nrf_saadc_value_t adc_buf;
/* Battery monitoring repeat timer */
APP_TIMER_DEF(m_battery_loop_timer_id);
/* Battery monitoring interval (ms) */
#define BATTERY_LOOP_INTERVAL 60000
/* Safety check consecutive count threshold */
#define SAFETY_CHECK_COUNT 5
/* Low-battery check flag — set by battery_loop, consumed by handler */
bool low_battery_check = false;
/* Safety check mode flag — set by battery handler, consumed by tmp235 handler */
bool safety_check_mode = false;
/* SAADC callback completion flag — used by all_sensors() to wait */
volatile bool battery_saadc_done = false;
/* Safety check: cached battery voltage for use in safety_check_complete() */
static float safety_batt_mv = 0;
/* Safety check: consecutive counters */
static uint8_t low_battery_cnt = 0;
static uint8_t over_temp_cnt = 0;
/* info4: bulk sensor collection mode flag */
extern bool info4;
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
extern bool go_device_power_off;
extern which_cmd_t cmd_type_t;
extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN];
/* info4 mode: cached battery voltage (mV) */
volatile uint16_t info_batt;
/* info4 sequential measurement control flags */
extern bool go_temp;
extern bool go_batt;
extern bool motion_raw_data_enabled;
extern bool ble_got_new_data;
extern bool motion_data_once;
/*==============================================================================
* safety_check_complete - Called by tmp235 handler after temperature measurement
*
* Checks both battery voltage and temperature against thresholds.
* 5 consecutive readings exceeding either threshold triggers power OFF.
*============================================================================*/
void safety_check_complete(float temp_c)
{
//DBG_PRINTF("[SAFETY] Batt=%d mV, Temp=%d.%d C\r\n",
// (int)safety_batt_mv, (int)temp_c, ((int)(temp_c * 10)) % 10);
/* Battery check */
if (safety_batt_mv <= LOW_BATTERY_VOLTAGE)
{
low_battery_cnt++;
DBG_PRINTF("[SAFETY] Low batt cnt=%d\r\n", low_battery_cnt);
}
else
{
low_battery_cnt = 0;
}
/* Temperature check */
if (temp_c >= OVER_TEMPERATURE_THRESHOLD)
{
over_temp_cnt++;
DBG_PRINTF("[SAFETY] Over temp cnt=%d\r\n", over_temp_cnt);
}
else
{
over_temp_cnt = 0;
}
/* Power OFF if either threshold exceeded 5 consecutive times */
if (low_battery_cnt >= SAFETY_CHECK_COUNT)
{
low_battery_cnt = 0;
DBG_PRINTF("[SAFETY] Low battery -> Power OFF\r\n");
go_device_power_off = true;
main_timer_start();
}
else if (over_temp_cnt >= SAFETY_CHECK_COUNT)
{
over_temp_cnt = 0;
DBG_PRINTF("[SAFETY] Over temperature -> Power OFF\r\n");
go_device_power_off = true;
main_timer_start();
}
dr_piezo_power_off();
}
/*==============================================================================
* battery_event_handler - SAADC conversion complete callback
*
* Converts the raw ADC value to battery voltage (mV) and then:
* - Low-battery check mode: store voltage, chain temperature measurement
* - info4 mode: store to info_batt (no BLE send)
* - Normal mode: send rsn: response over BLE or UART
*============================================================================*/
void battery_event_handler(nrf_drv_saadc_evt_t const * p_event)
{
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
nrf_saadc_value_t register_val = 0;
float batt_lvl_in_milli_volt_0 = 0;
float batt_lvl_in_milli_volt_1 = 0;
register_val = p_event->data.done.p_buffer[0];
/* Release SAADC — shared with temperature / pressure ADC */
nrf_drv_saadc_channel_uninit(0);
nrf_drv_saadc_uninit();
battery_saadc_done = true;
/* ADC -> mV conversion */
batt_lvl_in_milli_volt_0 = BATTERY_RESULT_IN_MILLI_VOLTS(register_val);
/* Resistor divider correction factor 1.42 */
batt_lvl_in_milli_volt_1 = batt_lvl_in_milli_volt_0 * 1.42f;
/* --- Safety check mode: store voltage, chain temperature measurement --- */
if (low_battery_check == true)
{
low_battery_check = false;
safety_batt_mv = batt_lvl_in_milli_volt_1;
safety_check_mode = true;
/* TMP235 shares piezo TX/RX power rail */
if (!dr_piezo_is_power_on())
{
dr_piezo_power_on();
}
tmp235_voltage_level_meas();
}
/* --- info4 mode: store value for mbb? bulk response --- */
else if (info4 == true)
{
info_batt = batt_lvl_in_milli_volt_1;
}
/* --- Normal mode: send rsn: BLE response --- */
else
{
if (cmd_type_t == CMD_UART)
{
DBG_PRINTF("Tn%d\r\n\r\n", (int)batt_lvl_in_milli_volt_1);
}
else if (cmd_type_t == CMD_BLE)
{
single_format_data(ble_bin_buffer, "rsn:", batt_lvl_in_milli_volt_1);
dr_binary_tx_safe(ble_bin_buffer, 3);
}
}
}
}
/*==============================================================================
* battery_configure - Set up SAADC for battery voltage measurement
*
* AIN2, single-ended, 1/6 gain, 12-bit, 4x oversampling, burst enabled.
* Registers a single buffer (uninit after one conversion).
*============================================================================*/
static void battery_configure(void)
{
nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;
saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X;
ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, battery_event_handler);
if (err_code != NRF_SUCCESS)
{
return; /* SAADC busy — skip this cycle, retry next */
}
nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
config.burst = NRF_SAADC_BURST_ENABLED;
config.acq_time = NRF_SAADC_ACQTIME_10US;
err_code = nrf_drv_saadc_channel_init(0, &config);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1);
APP_ERROR_CHECK(err_code);
}
/*==============================================================================
* battery_level_meas - Start a single battery voltage measurement
*
* Configures SAADC and triggers sampling. Result arrives asynchronously
* via battery_event_handler.
*============================================================================*/
void battery_level_meas(void)
{
ret_code_t err_code;
battery_configure();
err_code = nrf_drv_saadc_sample();
APP_ERROR_CHECK(err_code);
}
/*==============================================================================
* battery_loop - Periodic battery monitoring timer callback
*
* Sets the low-battery check flag and starts a measurement.
* Skips if info4 mode is active (SAADC conflict).
*============================================================================*/
void battery_loop(void * p_context)
{
UNUSED_PARAMETER(p_context);
if (info4 == true)
{
return;
}
low_battery_check = true;
battery_level_meas();
}
/* Start the periodic battery monitoring timer. */
void battery_timer_start(void)
{
APP_ERROR_CHECK(app_timer_start(m_battery_loop_timer_id, APP_TIMER_TICKS(BATTERY_LOOP_INTERVAL), NULL));
}
/* Stop the battery monitoring timer. */
void battery_timer_stop(void)
{
APP_ERROR_CHECK(app_timer_stop(m_battery_loop_timer_id));
}
/* Initialise the battery monitoring timer (repeated mode). */
void battery_timer_init(void)
{
APP_ERROR_CHECK(app_timer_create(&m_battery_loop_timer_id, APP_TIMER_MODE_REPEATED, battery_loop));
}
@@ -1,43 +0,0 @@
/*==============================================================================
* battery_saadc.h - Battery voltage SAADC measurement interface
*
* Uses the nRF52840 SAADC to measure battery voltage on AIN2.
*
* API:
* battery_level_meas() : one-shot measurement (async, result via callback)
* battery_timer_init/start/stop() : 60-second periodic monitoring timer
*
* Periodic safety check (every 60 s):
* Battery -> Temperature sequential measurement via SAADC.
* Auto power-off after 5 consecutive readings below LOW_BATTERY_VOLTAGE (3500 mV)
* or above OVER_TEMPERATURE_THRESHOLD (40 C).
*============================================================================*/
#ifndef _BATTERY_SAADC_H_
#define _BATTERY_SAADC_H_
/* Low-battery threshold (mV) — 5 consecutive readings below this -> power OFF */
#define LOW_BATTERY_VOLTAGE 3500
/* Over-temperature threshold (deg C) — 5 consecutive readings above this -> power OFF */
#define OVER_TEMPERATURE_THRESHOLD 40.0f
/* SAADC callback completion flag (used by all_sensors() to wait) */
extern volatile bool battery_saadc_done;
/* Safety check mode flag — set by battery_loop, consumed by tmp235 handler */
extern bool safety_check_mode;
/* Called by tmp235 handler when safety check temperature measurement completes */
void safety_check_complete(float temp_c);
/* Start a single async battery measurement. Result handled in callback. */
void battery_level_meas(void);
/* Start the 60-second periodic battery monitoring timer. */
void battery_timer_start(void);
/* Stop the battery monitoring timer. */
void battery_timer_stop(void);
/* Initialise the battery monitoring timer (call once at app start). */
void battery_timer_init(void);
#endif //_BATTERY_SAADC_H_
@@ -1,101 +0,0 @@
/*******************************************************************************
* @file app_raw.h
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
******************************************************************************/
/*******************************************************************************
* [Header overview] ICM42670P IMU driver application layer declarations
*
* Function prototypes and operating mode configuration macros for
* IMU sensor initialization, configuration, and data reading.
*
* Key configuration macros:
* SERIF_TYPE - Communication interface (UI_I2C)
* USE_LOW_NOISE_MODE - 1: low-noise (800Hz), 0: low-power (100Hz)
* USE_HIGH_RES_MODE - 1: 20-bit high-res, 0: 16-bit standard
* USE_FIFO - 1: use FIFO, 0: direct register read
******************************************************************************/
#ifndef _APP_RAW_H_
#define _APP_RAW_H_
#include "sdk_config.h"
#include <stdint.h>
#include "inv_imu_transport.h"
#include "inv_imu_defs.h"
#include "inv_imu_driver.h"
/*** Configuration macros ***/
/*
* MCU-IMU communication interface selection
* UI_I2C: use I2C communication (default)
*/
#define SERIF_TYPE UI_I2C
/*
* Power mode selection
* 1: Low-noise mode — 800Hz ODR, high precision, higher power consumption
* 0: Low-power mode — 100Hz ODR, lower power consumption
* Note: Low-noise mode cannot be used with ODR below 12.5Hz
*/
#define USE_LOW_NOISE_MODE 1
/*
* FIFO resolution mode selection
* 0: Low resolution — 16-bit data (default)
* 1: High resolution — 20-bit data (FSR locked to 16g/2000dps)
*/
#define USE_HIGH_RES_MODE 0
/*
* Data read method selection
* 0: Direct register read (currently in use)
* 1: Read from FIFO
*/
#define USE_FIFO 0
/**
* \brief Resets and initializes the IMU device. Includes WHOAMI verification.
* Must complete successfully before calling any other IMU access functions.
*
* \return 0=success, negative=error
*/
int setup_imu_device(struct inv_imu_serif *icm_serif);
/**
* \brief Configures the device for gyro and accel output.
* Applies FSR, ODR, power mode, and FIFO settings.
* \return 0=success, negative=error
*/
int configure_imu_device(void);
/**
* \brief Retrieves IMU data from FIFO or registers.
* Internally triggers imu_callback() for data processing.
* \return 0=success, negative=error
*/
int get_imu_data(void);
/**
* \brief Sensor data receive callback. Applies mounting matrix then
* outputs data according to info4/BLE/UART mode.
* \param[in] event Structure containing one sensor data packet
*/
void imu_callback(inv_imu_sensor_event_t *event);
/**
* \brief Direct I2C register read bypassing the driver API.
* Reads sensor data immediately without DRDY interrupt and sends via BLE.
* Switches IMU to sleep mode after reading to save power.
* \return 0=success, negative=error
*/
int imu_read_direct(void);
#endif /* !_APP_RAW_H_ */
@@ -1,292 +0,0 @@
/*******************************************************************************
* @file app_raw_main.c
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
******************************************************************************/
/*******************************************************************************
* 2026.03.26 jhChun
* This file is currently not executed at runtime.
* Instead of interrupt-driven reads, imu_read_direct() in app_raw.c
* reads registers directly. May be cleaned up later as needed.
******************************************************************************/
/*******************************************************************************
* [Module overview] ICM42670P main initialization and polling loop
*
* Handles the full initialization sequence and main loop for the ICM42670P
* IMU sensor.
*
* Init flow (icm42670_init):
* 1) setup_mcu() - Configure I2C serial interface struct and TWI init
* 2) setup_imu_device() - IMU driver init + WHOAMI verification
* 3) configure_imu_device() - Sensor parameter config (FSR, ODR, power mode)
* 4) inv_gpio_sensor_irq_init() - INT1 (P1.13) GPIO interrupt setup
*
* Main loop (icm42670_main):
* - irq_from_device flag is set when INT1 interrupt fires
* - Main loop checks the flag and reads sensor data when set
* - Interrupt triggers on falling edge (HITOLO) with INT1 pin pulled up
*
* Helper functions:
* - inv_imu_sleep_us() - nrf_delay_us wrapper (used by IMU driver)
* - inv_imu_get_time_us() - Provides timestamp via RTC1 counter
******************************************************************************/
#include "sdk_config.h"
#include "app_raw.h"
#include "app_raw_main.h"
#include "RingBuffer.h"
#include "inv_imu_driver.h"
#include "system_interface.h"
/* std */
#include <stdio.h>
#include "nrf.h"
#include "app_error.h"
#include "boards.h"
#include "nrfx_gpiote.h"
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "main.h" /* 2026-03-17: removed cmd_parse.h, using main.h */
#include "i2c_manager.h"
/* --------------------------------------------------------------------------------------
* Global variables
* -------------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------------
* Static variables
* -------------------------------------------------------------------------------------- */
/*
* IMU interrupt flag
* Set to 1 on falling-edge interrupt of INT1 pin.
* Main loop checks this flag, reads data, and clears it to 0.
* volatile: modified by ISR, prevents compiler optimization.
*/
static volatile int irq_from_device;
/* --------------------------------------------------------------------------------------
* Forward declaration
* -------------------------------------------------------------------------------------- */
static int setup_mcu(struct inv_imu_serif *icm_serif);
/*!
* @brief Sensor general interrupt handler, calls specific handlers.
*
* This function is called when an external interrupt is triggered by the sensor,
* checks interrupt registers of InvenSense Sensor to determine the source and type of interrupt
* and calls the specific interrupt handler accordingly.
*
* @param[in] NULL
*
* @param[out] NULL
*
* @return NULL
*
*/
/*
* inv_gpio_sensor_interrupt_handler()
* INT1 pin interrupt handler (ISR).
* Called when the sensor has new data ready; sets a flag and returns immediately.
* Actual data processing is done in the main loop (icm42670_main).
*/
static void inv_gpio_sensor_interrupt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
irq_from_device = 1;
}
/*
* inv_gpio_sensor_irq_init()
* Initializes INT1 (P1.13) GPIO interrupt.
*
* Configuration:
* - Trigger: falling edge (HITOLO) — when the sensor pulls INT low
* - Pull-up: internal pull-up enabled
* - Handler: inv_gpio_sensor_interrupt_handler
* - Initializes GPIOTE module first if not already initialized
*/
void inv_gpio_sensor_irq_init(void)
{
ret_code_t err_code;
/* Initialize GPIOTE module (skip if already initialized) */
if (!nrfx_gpiote_is_init())
{
err_code = nrfx_gpiote_init();
APP_ERROR_CHECK(err_code);
}
/* Falling-edge interrupt: trigger on High->Low transition, internal pull-up */
nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
in_config.pull = NRF_GPIO_PIN_PULLUP;
/* Register interrupt handler for INT1 pin */
err_code = nrfx_gpiote_in_init(ICM42670_INT1_PIN, &in_config, inv_gpio_sensor_interrupt_handler);
APP_ERROR_CHECK(err_code);
/* Enable interrupt event */
nrfx_gpiote_in_event_enable(ICM42670_INT1_PIN, true);
}
/*
* inv_gpio_sensor_irq_uninit()
* Disables and releases the INT1 GPIO interrupt.
* Called when deactivating the sensor or before re-initialization.
*/
void inv_gpio_sensor_irq_uninit(void)
{
/* Disable interrupt event */
nrfx_gpiote_in_event_disable(ICM42670_INT1_PIN);
/* Release INT1 pin interrupt configuration */
nrfx_gpiote_in_uninit(ICM42670_INT1_PIN);
/* Release GPIOTE module (only if initialized) */
if (nrfx_gpiote_is_init())
{
nrfx_gpiote_uninit();
}
}
/* --------------------------------------------------------------------------------------
* Main
* -------------------------------------------------------------------------------------- */
/*
* icm42670_init()
* Performs the full ICM42670P initialization sequence.
*
* Init order:
* 1) setup_mcu() - Configure I2C interface struct and TWI hardware init
* 2) setup_imu_device() - IMU driver init, WHOAMI (0x67) verification
* 3) configure_imu_device() - FSR, ODR, power mode configuration
* 4) inv_gpio_sensor_irq_init() - Enable INT1 interrupt (data-ready notification)
*
* Returns: 0=success, -1=initialization failure
*/
int icm42670_init(void)
{
int rc = 0;
struct inv_imu_serif icm_serif;
rc |= setup_mcu(&icm_serif);
rc |= setup_imu_device(&icm_serif);
rc |= configure_imu_device();
if(rc != 0){
printf("!!!error during initialization\r\n");
return -1;
}
/* Enable INT1 interrupt after successful init — ISR fires on data ready */
inv_gpio_sensor_irq_init();
return rc;
}
/*
* icm42670_main()
* ICM42670P main polling loop.
* Must be called periodically from the main application loop.
*
* Operation:
* 1) Check if I2C hardware is initialized (hw_i2c_init_once)
* 2) Check irq_from_device flag (set by ISR)
* 3) If flag is set, read sensor data (get_imu_data)
* 4) Clear flag after data read completes
*
* Note: Interrupt-based polling — ISR only sets the flag; actual I2C
* communication is done in the main context.
*/
void icm42670_main(void)
{
int rc = 0;
hw_i2c_init_once();
/* Check for interrupt and read data */
if (irq_from_device) {
rc = get_imu_data();
if(rc < 0) {
printf("error while getting data\r\n");
}
/* Clear flag — wait for next interrupt */
irq_from_device = 0;
}
}
/* --------------------------------------------------------------------------------------
* Functions definitions
* -------------------------------------------------------------------------------------- */
/*
* setup_mcu()
* Configures the MCU-side serial interface.
*
* Registers the following in the inv_imu_serif struct:
* - read_reg / write_reg : I2C read/write callbacks (implemented in system_interface.c)
* - max_read / max_write : Max transfer size (32KB)
* - serif_type : Communication type (UI_I2C)
*
* After configuration, calls inv_io_hal_init() to initialize the TWI hardware.
*/
static int setup_mcu(struct inv_imu_serif *icm_serif)
{
int rc = 0;
/* Configure serial interface struct for IMU driver */
icm_serif->context = 0; /* Context unused */
icm_serif->read_reg = inv_io_hal_read_reg; /* Register read callback */
icm_serif->write_reg = inv_io_hal_write_reg; /* Register write callback */
icm_serif->max_read = 1024*32; /* Max bytes per read */
icm_serif->max_write = 1024*32; /* Max bytes per write */
icm_serif->serif_type = SERIF_TYPE; /* UI_I2C (defined in app_raw.h) */
/* Initialize TWI hardware */
rc |= inv_io_hal_init(icm_serif);
return rc;
}
/* --------------------------------------------------------------------------------------
* Extern functions definition
* -------------------------------------------------------------------------------------- */
/*
* inv_imu_sleep_us()
* Microsecond sleep function used by the IMU driver.
* Wraps nrf_delay_us() to provide a platform-independent interface.
* Example: used for gyro startup delay (GYR_STARTUP_TIME_US).
*/
void inv_imu_sleep_us(uint32_t us)
{
nrf_delay_us(us);
}
/*
* inv_imu_get_time_us()
* Timestamp function used by the IMU driver.
* Returns the nRF52840 RTC1 counter value.
*
* Note: RTC1 runs at 32.768kHz, so the returned value is technically
* in RTC ticks (~30.5us/tick), not microseconds.
* Used for relative time comparisons within the driver.
*/
uint64_t inv_imu_get_time_us(void)
{
return NRF_RTC1->COUNTER;
}
@@ -1,32 +0,0 @@
/*******************************************************************************
* @file app_raw_main.h
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
******************************************************************************/
/*******************************************************************************
* [Header overview] ICM42670P main initialization/polling loop declarations
*
* Declares the full initialization and main loop functions for the
* ICM42670P IMU sensor.
* - icm42670_init() : Full init (MCU config -> IMU init -> sensor config -> enable IRQ)
* - icm42670_main() : Main polling loop (check INT1 interrupt -> read data)
* - icm42670_uninit() : Release (prototype only, implementation elsewhere)
******************************************************************************/
#ifndef _APP_RAW_MAIN_H_
#define _APP_RAW_MAIN_H_
#include "sdk_config.h"
/* ICM42670P full init — MCU I2C config -> IMU driver init -> sensor config -> enable IRQ */
int icm42670_init(void);
/* ICM42670P main polling loop — check INT1 interrupt flag, then read sensor data */
void icm42670_main(void);
/* ICM42670P release (prototype declaration) */
int icm42670_uninit(void);
#endif /* !_APP_RAW_MAIN_H_ */
@@ -1,125 +0,0 @@
/*==============================================================================
* dr_piezo.h - Piezo Transducer Driver (2 MHz Signal Generator)
*
* Hardware: nRF52840 + MD1822K6-G MOSFET Driver + TC7920K6-G MOSFET
* Output: +/-20V at 2 MHz, 3..7 cycles burst
*
* Timing Sequence:
* 1. PE = HIGH (enable)
* 2. P_OUT/N_OUT = 2 MHz pulses (3..7 cycles)
* 3. DMP = HIGH (dump residual energy)
* 4. DMP = LOW
* 5. PE = LOW (disable)
*
* Pin assignment:
* Power: DR_PIEZO_PWR_EN (P1.9) — DC/DC +/-20V enable
* TX: PE (P0.25), DMP (P1.0), P_OUT (P1.7), N_OUT (P1.6)
* MUX: EN_MUXA (P0.21), EN_MUXB (P0.23), SEL0 (P1.10), SEL1 (P0.28)
*
* MUX channel mapping (8ch):
* CH0=A0(1,0,0,0) CH1=A2(1,0,1,0) CH2=A1(1,0,0,1) CH3=A3(1,0,1,1)
* CH4=B0(0,1,1,1) CH5=B1(0,1,0,1) CH6=B2(0,1,1,0) CH7=B3(0,1,0,0)
*
* Two burst modes:
* 1) HW burst (dr_piezo_burst): Timer2 + PPI + GPIOTE, CPU-independent
* 2) SW burst (dr_piezo_burst_sw_XXmhz): CPU NOP-based precise timing
* Per-frequency functions: 1.7 / 1.8 / 1.9 / 2.0 / 2.1 / 2.2 MHz
*============================================================================*/
#ifndef DR_PIEZO_H
#define DR_PIEZO_H
#include <stdint.h>
#include <stdbool.h>
#include "nrf_gpio.h"
/*==============================================================================
* Power control pin (+/-20V DC/DC converter)
*============================================================================*/
#define DR_PIEZO_PWR_EN NRF_GPIO_PIN_MAP(1, 9)
/*==============================================================================
* TX signal pins (MOSFET driver)
* PE: Pulse Enable — activates the entire TX sequence
* DMP: Dump — discharges residual piezo energy after burst
* P_OUT: Positive output — drives piezo positive terminal
* N_OUT: Negative output — drives piezo negative terminal (inverted P_OUT)
*============================================================================*/
#define DR_PIEZO_PIN_PE NRF_GPIO_PIN_MAP(0, 25) /**< Pulse Enable */
#define DR_PIEZO_PIN_DMP NRF_GPIO_PIN_MAP(1, 0) /**< Dump control */
#define DR_PIEZO_PIN_P_OUT NRF_GPIO_PIN_MAP(1, 7) /**< Positive output */
#define DR_PIEZO_PIN_N_OUT NRF_GPIO_PIN_MAP(1, 6) /**< Negative output */
/*==============================================================================
* MUX control pins (echo signal path selection, 8 channels)
* MUXA handles CH0..CH3, MUXB handles CH4..CH7.
* Only one MUX is enabled at a time.
*============================================================================*/
#define DR_PIEZO_EN_MUXA NRF_GPIO_PIN_MAP(0, 21) /**< MUXA Enable */
#define DR_PIEZO_EN_MUXB NRF_GPIO_PIN_MAP(0, 23) /**< MUXB Enable */
#define DR_PIEZO_MUX_SEL0 NRF_GPIO_PIN_MAP(1, 10) /**< MUX Select 0 */
#define DR_PIEZO_MUX_SEL1 NRF_GPIO_PIN_MAP(0, 28) /**< MUX Select 1 */
/*==============================================================================
* Configuration
*============================================================================*/
#define DR_PIEZO_FREQ_HZ 2100000 /**< Target frequency (set PIEZO_FREQ_MHZ in .c) */
#define DR_PIEZO_DEFAULT_CYCLES 5 /**< Default burst cycles */
#define DR_PIEZO_MIN_CYCLES 3
#define DR_PIEZO_MAX_CYCLES 7
#define DR_PIEZO_MUX_SETTLING_US 1300 /**< MUX settling delay (us) */
/*==============================================================================
* Power control
*============================================================================*/
void dr_piezo_power_on(void);
void dr_piezo_power_off(void);
/** @return true if power is ON */
bool dr_piezo_is_power_on(void);
/*==============================================================================
* TX driver
*============================================================================*/
void dr_piezo_init(void);
void dr_piezo_uninit(void);
void dr_piezo_burst(uint8_t cycles);
void dr_piezo_pulse(void);
void dr_piezo_enable(void);
void dr_piezo_disable(void);
bool dr_piezo_is_busy(void);
void dr_piezo_set_frequency(uint32_t freq_hz);
void dr_piezo_test_pins(void);
void dr_piezo_mux_init(void);
/**
* @brief Select piezo channel (0..7) via 8ch MUX
* @note MUX settling time: ~1.3 ms delay after switching
*/
void dr_piezo_select_channel(uint8_t channel);
/*==============================================================================
* System functions (power + TX combined)
*============================================================================*/
void dr_piezo_system_init(void);
void dr_piezo_system_uninit(void);
void dr_piezo_transmit(uint8_t cycles);
/*==============================================================================
* Software burst — CPU NOP-based precise timing, no Timer/PPI
*
* Interrupts are disabled during burst for timing accuracy.
* Per-frequency functions (NOP count varies):
*============================================================================*/
void dr_piezo_burst_sw(uint8_t cycles); /**< 2.1 MHz (default) */
void dr_piezo_burst_sw_18mhz(uint8_t cycles); /**< 1.8 MHz */
void dr_piezo_burst_sw_20mhz(uint8_t cycles); /**< 2.0 MHz */
void dr_piezo_burst_sw_22mhz(uint8_t cycles); /**< 2.2 MHz */
void dr_piezo_burst_sw_17mhz(uint8_t cycles); /**< 1.7 MHz */
void dr_piezo_burst_sw_19mhz(uint8_t cycles); /**< 1.9 MHz */
#endif /* DR_PIEZO_H */
@@ -1,168 +0,0 @@
/*==============================================================================
* tmp235_q1.c - TMP235-Q1 analogue temperature sensor driver
*
* Reads the TMP235-Q1 analogue output via SAADC AIN3 and converts to deg C.
*
* Temperature conversion (piecewise linear, per datasheet):
* Vout <= 1500 mV (0..100 C) : Ta = (Vout - 500) / 10.0
* Vout <= 1750 mV (100..125 C): Ta = (Vout - 1500) / 10.1 + 100
* Vout <= 2000 mV (125..150 C): Ta = (Vout - 1752.5) / 10.6 + 125
* Vout > 2000 mV : out of sensor range
*============================================================================*/
#include "sdk_common.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_drv_saadc.h"
#include "ble_nus.h"
#include "tmp235_q1.h"
#include "main.h"
#include "main_timer.h"
#include "battery_saadc.h"
#include "debug_print.h"
/* SAADC internal reference (mV) */
#define TMP235_REF_VOLTAGE_IN_MILLIVOLTS 600.0f
/* 1/3 prescaling compensation (x6) */
#define TMP235_PRE_SCALING_COMPENSATION 6.0f
/* 12-bit ADC full scale */
#define TMP235_ADC_RES_12BITS 4096.0f
/* Convert raw ADC value to TMP235 output voltage (mV) */
#define TMP235_VOUT_IN_MILLI_VOLTS(ADC_VALUE)\
((((ADC_VALUE) * TMP235_REF_VOLTAGE_IN_MILLIVOLTS) / TMP235_ADC_RES_12BITS) * TMP235_PRE_SCALING_COMPENSATION)
static nrf_saadc_value_t adc_buf;
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN];
extern which_cmd_t cmd_type_t;
extern bool info4;
extern bool go_temp;
/* info4 mode: cached temperature (deg C x 100, integer) */
volatile uint16_t info_temp;
extern bool motion_raw_data_enabled;
/* SAADC completion flag — used by all_sensors() to wait */
volatile bool tmp235_saadc_done = false;
/*==============================================================================
* tmp235_voltage_handler - SAADC conversion complete callback
*
* ADC value -> Vout (mV) -> temperature (deg C), then:
* - info4 mode: store to info_temp (C x 100 integer)
* - Normal mode: send rso: response over BLE or UART
*============================================================================*/
void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event)
{
float led_temp;
float led_temp_16;
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
nrf_saadc_value_t adc_result;
float tmp235_voltage_in_milli_volts = 0;
adc_result = p_event->data.done.p_buffer[0];
/* Release SAADC — shared with battery / pressure ADC */
nrf_drv_saadc_channel_uninit(0);
nrf_drv_saadc_uninit();
/* ADC -> TMP235 output voltage (mV) */
tmp235_voltage_in_milli_volts = TMP235_VOUT_IN_MILLI_VOLTS(adc_result);
/* Vout -> temperature (piecewise linear per datasheet) */
if(tmp235_voltage_in_milli_volts <= 1500)
{
/* 0..100 C: slope 10.0 mV/C, offset 500 mV */
led_temp = (tmp235_voltage_in_milli_volts - 500.0f) / 10.0f + 0.0f;
}
else if(tmp235_voltage_in_milli_volts <= 1750)
{
/* 100..125 C: slope 10.1 mV/C */
led_temp = (tmp235_voltage_in_milli_volts - 1500.0f) / 10.1f + 100.0f;
}
else if(tmp235_voltage_in_milli_volts <= 2000)
{
/* 125..150 C: slope 10.6 mV/C */
led_temp = (tmp235_voltage_in_milli_volts - 1752.5f) / 10.6f + 125.0f;
}
else
{
/* Out of sensor range (>150 C) */
DBG_PRINTF("ERR!!! Temperature is over 150c\r\n");
}
/* --- Safety check mode: pass temperature to battery module for judgment --- */
if (safety_check_mode == true)
{
safety_check_mode = false;
safety_check_complete(led_temp);
}
/* --- info4 mode: store value for mbb? bulk response --- */
else if (info4 == true)
{
info_temp = (uint16_t)(led_temp * 100);
}
else if (cmd_type_t == CMD_UART)
{
DBG_PRINTF("To%.2f\r\n\r\n", led_temp);
}
else if (cmd_type_t == CMD_BLE)
{
led_temp_16 = led_temp * 100;
single_format_data(ble_bin_buffer, "rso:", (uint16_t)led_temp_16);
dr_binary_tx_safe(ble_bin_buffer, 3);
}
tmp235_saadc_done = true;
}
}
/*==============================================================================
* tmp235_init - Initialise SAADC for TMP235 and start measurement
*
* AIN3, single-ended, 12-bit, 4x oversampling, burst enabled.
* Triggers sampling immediately; result arrives via tmp235_voltage_handler.
*============================================================================*/
void tmp235_init(void)
{
nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;
saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X;
ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, tmp235_voltage_handler);
APP_ERROR_CHECK(err_code);
nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3);
config.burst = NRF_SAADC_BURST_ENABLED;
err_code = nrf_drv_saadc_channel_init(0, &config);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_sample();
APP_ERROR_CHECK(err_code);
}
/*==============================================================================
* tmp235_voltage_level_meas - External entry point for one-shot reading
*
* Calls tmp235_init() which both initialises and triggers sampling.
*============================================================================*/
void tmp235_voltage_level_meas(void)
{
tmp235_init();
}
@@ -1,22 +0,0 @@
/*==============================================================================
* tmp235_q1.h - TMP235-Q1 analogue temperature sensor driver interface
*
* Reads the TMP235-Q1 analogue voltage output via SAADC (AIN3) and converts
* it to temperature (deg C).
*
* Conversion: Ta(C) = (Vout_mV - 500) / 10.0 (valid 0..100 C)
*
* API:
* tmp235_init() : initialise SAADC + start measurement (internal)
* tmp235_voltage_level_meas() : one-shot temperature reading (external wrapper)
*============================================================================*/
#ifndef _TMP235_Q1_H_
#define _TMP235_Q1_H_
/* Initialise SAADC for TMP235 and start measurement (AIN3). */
void tmp235_init(void);
/* External entry point for a single temperature reading. */
void tmp235_voltage_level_meas(void);
#endif /* !_TMP235_Q1_H_ */
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More