feat: BNO085 IMU integration — SPICE + simulator yaw rate feed-forward
SPICE (6_bno085_imu.cir):
- BNO085 power supply with 10uF + 100nF decoupling on VDD
- Power-on reset RC circuit (R=10K, C=1uF, tau=10ms → deasserts at ~12ms)
- I2C Fast Mode 400kHz bus: 4.7K pull-ups, 50pF bus capacitance model
- Full I2C transaction: START + address 0x4A + R/W + BNO085 ACK + STOP
- INT pin (open-drain, 10K pull-up, 100Hz interrupt simulation)
- .meas directives: reset timing, SCL rise time, VDD stability
Simulator (esp32_sim.py):
- SimSnapshot.bno085_yaw_rate_dps field added
- _bno085_enabled / _bno085_noise_std_dps / _bno085_yaw_rate_dps state
- enable_bno085(noise_std_dps=0.02) public method
- disable_bno085() public method
- _run_physics: samples gyro at 50Hz with Gaussian noise model
- _run_outer_loop: uses BNO085 yaw rate for rot_ff_term when enabled
(replaces NMEA-derived ROT — lower latency ~4ms vs ~100-200ms)
Usage:
sim.enable_bno085() # activate gyro feed-forward
sim.enable_bno085(noise_std_dps=0.014) # with BNO085 spec noise
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+57
-1
@@ -126,6 +126,7 @@ class SimSnapshot:
|
|||||||
alarm_heading_lost: bool
|
alarm_heading_lost: bool
|
||||||
alarm_off_course: bool
|
alarm_off_course: bool
|
||||||
alarm_off_course_severe: bool
|
alarm_off_course_severe: bool
|
||||||
|
bno085_yaw_rate_dps: float = 0.0 # medición del giróscopo BNO085 (0 si desactivado)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -295,6 +296,15 @@ class ESP32Simulator:
|
|||||||
self._sea_wave_period: float = 8.0
|
self._sea_wave_period: float = 8.0
|
||||||
self._sea_rng: random.Random = random.Random(42)
|
self._sea_rng: random.Random = random.Random(42)
|
||||||
|
|
||||||
|
# -- BNO085 IMU (yaw rate de alta frecuencia) --------------------
|
||||||
|
# Cuando está activo, el rot_ff_term del outer PID usa la medición
|
||||||
|
# del giróscopo BNO085 (50 Hz, ruido ~0.02 °/s) en lugar del ROT
|
||||||
|
# derivado del NMEA compass (10 Hz, mayor latencia).
|
||||||
|
# Esto reduce el overshoot en maniobras y el cabeceo en olas.
|
||||||
|
self._bno085_enabled: bool = False
|
||||||
|
self._bno085_noise_std_dps: float = 0.02 # spec BNO085: ~0.014 °/s
|
||||||
|
self._bno085_yaw_rate_dps: float = 0.0
|
||||||
|
|
||||||
# -- Registro de telemetría para análisis ------------------------
|
# -- Registro de telemetría para análisis ------------------------
|
||||||
self.log: list[SimSnapshot] = []
|
self.log: list[SimSnapshot] = []
|
||||||
self.events: list[SimEvent] = []
|
self.events: list[SimEvent] = []
|
||||||
@@ -417,6 +427,36 @@ class ESP32Simulator:
|
|||||||
if max_rudder_deg is not None:
|
if max_rudder_deg is not None:
|
||||||
self._pid_outer.config.max_rudder_deg = float(max_rudder_deg)
|
self._pid_outer.config.max_rudder_deg = float(max_rudder_deg)
|
||||||
|
|
||||||
|
def enable_bno085(self, *, noise_std_dps: float = 0.02) -> None:
|
||||||
|
"""
|
||||||
|
Activa el BNO085 como fuente de yaw rate para el outer PID.
|
||||||
|
|
||||||
|
Con BNO085 activo el ``rot_ff_term`` del outer PID usa la medición
|
||||||
|
del giróscopo (50 Hz, ruido típico ~0.014 °/s) en lugar del ROT
|
||||||
|
derivado del bus NMEA 2000 (10 Hz, mayor latencia de bus + GPS).
|
||||||
|
|
||||||
|
Diferencia clave respecto al ROT por NMEA:
|
||||||
|
- Latencia: BNO085 ≈ 4 ms vs NMEA ROT ≈ 100-200 ms
|
||||||
|
- Ruido: BNO085 ≈ 0.02 °/s vs NMEA ROT ≈ 0.1-0.5 °/s
|
||||||
|
- Frecuencia: BNO085 muestrea a 250 Hz (gyro), outer PID consume a 10 Hz
|
||||||
|
|
||||||
|
En mal tiempo (B4-B5) el BNO085 permite que el ``rot_ff_term``
|
||||||
|
reaccione a los picos de guiñada generados por las olas antes de
|
||||||
|
que el error de heading se acumule — equivale a subir el knob
|
||||||
|
"COUNTER RUDDER" manteniendo la ganancia proporcional estable.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
noise_std_dps: Desviación estándar del ruido del giróscopo en °/s.
|
||||||
|
BNO085 spec: ~0.014 °/s típico. Default 0.02 °/s (conservador).
|
||||||
|
"""
|
||||||
|
self._bno085_enabled = True
|
||||||
|
self._bno085_noise_std_dps = float(noise_std_dps)
|
||||||
|
|
||||||
|
def disable_bno085(self) -> None:
|
||||||
|
"""Desactiva el BNO085; el outer PID vuelve a usar ROT del NMEA 2000."""
|
||||||
|
self._bno085_enabled = False
|
||||||
|
self._bno085_yaw_rate_dps = 0.0
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
# API pública — Interfaz Modbus
|
# API pública — Interfaz Modbus
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
@@ -648,10 +688,15 @@ class ESP32Simulator:
|
|||||||
sp_speed_raw = self._holdings[25] # PID_OUTER_SPEED_KN_REQ_X10
|
sp_speed_raw = self._holdings[25] # PID_OUTER_SPEED_KN_REQ_X10
|
||||||
sog_kn = (sp_speed_raw * 0.1) if sp_speed_raw > 0 else self._nmea_sog_kn
|
sog_kn = (sp_speed_raw * 0.1) if sp_speed_raw > 0 else self._nmea_sog_kn
|
||||||
|
|
||||||
|
# BNO085 activo → usa yaw rate del giróscopo (50 Hz, baja latencia).
|
||||||
|
# BNO085 inactivo → usa ROT del bus NMEA 2000 (10 Hz, GPS/compass).
|
||||||
|
rot_for_pid = (self._bno085_yaw_rate_dps if self._bno085_enabled
|
||||||
|
else self._nmea_rot_dps)
|
||||||
|
|
||||||
self._outer_rudder_sp = self._pid_outer.step(
|
self._outer_rudder_sp = self._pid_outer.step(
|
||||||
heading_setpoint_deg=self._heading_setpoint_deg,
|
heading_setpoint_deg=self._heading_setpoint_deg,
|
||||||
heading_measured_deg=self._nmea_heading_deg,
|
heading_measured_deg=self._nmea_heading_deg,
|
||||||
rate_of_turn_dps=self._nmea_rot_dps,
|
rate_of_turn_dps=rot_for_pid,
|
||||||
speed_kn=sog_kn,
|
speed_kn=sog_kn,
|
||||||
allowed=True,
|
allowed=True,
|
||||||
)
|
)
|
||||||
@@ -715,6 +760,16 @@ class ESP32Simulator:
|
|||||||
self._nmea_cog_deg = self._nmea_heading_deg
|
self._nmea_cog_deg = self._nmea_heading_deg
|
||||||
self._last_nmea_update_t = self._t
|
self._last_nmea_update_t = self._t
|
||||||
|
|
||||||
|
# BNO085: muestrea yaw rate a 50 Hz (mismo ritmo que el inner loop).
|
||||||
|
# El giróscopo del BNO085 tiene latencia ~4ms y ruido ~0.02 °/s,
|
||||||
|
# mucho mejor que el ROT calculado desde el GPS/compass NMEA (100-200ms).
|
||||||
|
# En el outer PID (10 Hz) esto significa que rot_ff_term reacciona a
|
||||||
|
# picos de guiñada por olas ~10x más rápido que con ROT por NMEA.
|
||||||
|
if self._bno085_enabled:
|
||||||
|
noise = (self._sea_rng.gauss(0.0, self._bno085_noise_std_dps)
|
||||||
|
if self._bno085_noise_std_dps > 0.0 else 0.0)
|
||||||
|
self._bno085_yaw_rate_dps = self._vessel.state.rate_of_turn_dps + noise
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
# Internos — alarmas
|
# Internos — alarmas
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
@@ -825,6 +880,7 @@ class ESP32Simulator:
|
|||||||
alarm_heading_lost=self._alarm_heading_lost,
|
alarm_heading_lost=self._alarm_heading_lost,
|
||||||
alarm_off_course=self._alarm_off_course,
|
alarm_off_course=self._alarm_off_course,
|
||||||
alarm_off_course_severe=self._alarm_off_course_severe,
|
alarm_off_course_severe=self._alarm_off_course_severe,
|
||||||
|
bno085_yaw_rate_dps=self._bno085_yaw_rate_dps,
|
||||||
))
|
))
|
||||||
|
|
||||||
def _add_event(self, kind: str, detail: str = "") -> None:
|
def _add_event(self, kind: str, detail: str = "") -> None:
|
||||||
|
|||||||
@@ -0,0 +1,293 @@
|
|||||||
|
* =======================================================================
|
||||||
|
* AR-Autopilot — IMU BNO085: Alimentacion, Reset e I2C
|
||||||
|
* Archivo: 6_bno085_imu.cir
|
||||||
|
* Tarjeta: Modulo compacto ESP32+CAN (CIRCUITO GIRO)
|
||||||
|
*
|
||||||
|
* CIRCUITO:
|
||||||
|
*
|
||||||
|
* 3.3V ──[C=10uF + C=100nF]──── BNO085 VDD (alimentacion principal)
|
||||||
|
* 3.3V ──[C=100nF]────────────── BNO085 VDDIO (nivel logico I2C = 3.3V)
|
||||||
|
*
|
||||||
|
* 3.3V ──[R=10K]──── NRST (BNO085 RESET, activo bajo)
|
||||||
|
* └── [C=1uF] ── GND → reset POR: NRST sube lento
|
||||||
|
*
|
||||||
|
* 3.3V ──[R=4.7K]──── SCL (I2C clock, 400 kHz Fast Mode)
|
||||||
|
* 3.3V ──[R=4.7K]──── SDA (I2C data, open-drain)
|
||||||
|
* 3.3V ──[R=10K] ──── INT (interrupcion data-ready, activo bajo OD)
|
||||||
|
*
|
||||||
|
* SA0 ── GND → direccion I2C = 0x4A
|
||||||
|
* BOOT ── GND → modo operacion normal (no bootloader USB)
|
||||||
|
*
|
||||||
|
* BNO085 (CEVA/Hillcrest BNO085):
|
||||||
|
* Alimentacion: VDD = 3.3V, VDDIO = 1.8V a 3.3V
|
||||||
|
* Corriente tipica: 6.5 mA (todos los sensores activos, 100 Hz)
|
||||||
|
* Reset: NRST activo bajo, minimo 10 us, startup: 50 ms tipico
|
||||||
|
* I2C: 100 kHz o 400 kHz (Fast Mode), max 1 MHz (Fast Mode Plus)
|
||||||
|
* Reportes configurables: Rotation Vector, Gyroscope, Accelerometer...
|
||||||
|
* Sensor Fusion DSP interno: 500 Hz internal fusion rate
|
||||||
|
* Giroscopio: ruido ~0.014 deg/s RMS, bias < 1 deg/s
|
||||||
|
* Magnetometro: compensacion hard/soft iron automatica
|
||||||
|
* Rango de temperatura operacion: -40 a +85 C (marino OK)
|
||||||
|
*
|
||||||
|
* REPORTES USADOS PARA EL AUTOPILOTO:
|
||||||
|
* ARVR Stabilized Rotation Vector → Heading (0-360°) a 100 Hz
|
||||||
|
* Gyroscope Calibrated → Yaw rate (°/s) a 250 Hz
|
||||||
|
* Linear Acceleration → para deteccion de impactos
|
||||||
|
*
|
||||||
|
* COMO USAR EN LTSPICE:
|
||||||
|
* Ver: V(nrst) → curva de reset POR (debe superar 0.7×VDD = 2.31V en ~12ms)
|
||||||
|
* V(vdd_bno) → tension de alimentacion BNO085 con decoupling
|
||||||
|
* V(scl) → reloj I2C 400 kHz con tiempos de subida correctos
|
||||||
|
* V(sda) → datos I2C con open-drain y pull-up
|
||||||
|
* V(int_pin) → interrupcion data-ready
|
||||||
|
* =======================================================================
|
||||||
|
|
||||||
|
.title AR-Autopilot BNO085 IMU Power, Reset and I2C Interface
|
||||||
|
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* ALIMENTACION 3.3V
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
V33 V33 GND 3.3V
|
||||||
|
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* DESACOPLO DE ALIMENTACION BNO085
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* BNO085 datasheet recomienda: 10uF (bulk) + 100nF (HF) en VDD
|
||||||
|
* y 100nF adicional en VDDIO, lo mas cerca posible del IC.
|
||||||
|
* A 22mm del inductor L2 del buck: el ruido de switching es bajo
|
||||||
|
* pero el decoupling sigue siendo critico para el magnetometro.
|
||||||
|
|
||||||
|
Cbulk V33 VDD_BNO 10u ; capacitor bulk 10uF ceramico (X5R)
|
||||||
|
Cdec1 VDD_BNO GND 100n ; 100nF ceramico junto al pin VDD del BNO085
|
||||||
|
Cdec2 V33 GND 100n ; 100nF en VDDIO (misma tension 3.3V)
|
||||||
|
|
||||||
|
* Inductancia del trace PCB entre condensador bulk y IC (~10mm de traza)
|
||||||
|
* L_trace a 1.4MHz (MP2338): XL = 2*pi*1.4e6*1e-9 = 8.8mOhm → despreciable
|
||||||
|
Rtrace V33 VDD_BNO 0.05 ; resistencia de traza PCB (~50mOhm para 10mm)
|
||||||
|
|
||||||
|
* Consumo del BNO085 (todos sensores + fusion a 100Hz)
|
||||||
|
Ibno085 VDD_BNO GND DC 6.5m ; 6.5 mA tipico (datasheet tabla 4.3)
|
||||||
|
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* CIRCUITO DE RESET (Power-On Reset)
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* NRST activo bajo: cuando VDD sube, NRST debe permanecer bajo
|
||||||
|
* hasta que VDD este estable, luego sube lentamente via RC.
|
||||||
|
* El BNO085 sale de reset cuando NRST > 0.7 * VDDIO = 2.31V
|
||||||
|
* Con R=10K, C=1uF: τ = 10ms → NRST cruza 2.31V en ~12ms
|
||||||
|
*
|
||||||
|
* Simulamos encendido: VDD sube en 1ms (soft-start del buck converter)
|
||||||
|
* Despues NRST sube lentamente → BNO085 en reset durante ~12ms ✓
|
||||||
|
|
||||||
|
* Resistencia de pull-up del RESET
|
||||||
|
Rrst V33 NRST_NODE 10k
|
||||||
|
|
||||||
|
* Condensador de reset (define el tiempo de reset)
|
||||||
|
Crst NRST_NODE GND 1u IC=0
|
||||||
|
|
||||||
|
* Diodo de descarga rapida (para re-reset rapido si VDD cae)
|
||||||
|
Drst GND NRST_NODE DRST_FAST
|
||||||
|
.model DRST_FAST D(Is=1e-12 N=1 Rs=1 Cjo=5p)
|
||||||
|
|
||||||
|
* Modelo del umbral de reset del BNO085 (schmitt trigger interno)
|
||||||
|
* NRST < 2.31V → en reset; NRST > 2.31V → operativo
|
||||||
|
Eres RESET_STATUS GND VALUE={IF(V(NRST_NODE) > 2.31, 3.3, 0)}
|
||||||
|
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* BUS I2C — RESISTENCIAS DE PULL-UP
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* Fast Mode (400 kHz): Rpullup maximo = Vcc/(3mA) = 3.3/0.003 = 1.1k
|
||||||
|
* Rpullup minimo = (Vcc - Voh)/(bus cap × slew rate) ≈ 1k
|
||||||
|
* Valor estandar: 4.7k (funciona bien hasta 300kHz con Cbus < 100pF)
|
||||||
|
* Para 400kHz con Cbus=50pF: tr = 0.8473 * 4700 * 50e-12 = 199ns OK (limite es 300ns)
|
||||||
|
*
|
||||||
|
* Si necesitas 400kHz garantizado con cable largo (Cbus > 100pF):
|
||||||
|
* Reducir a 2.2k → tr = 93ns ✓ (pero mayor consumo: 3.3/2.2k = 1.5mA por linea)
|
||||||
|
|
||||||
|
Rpull_scl V33 SCL 4.7k ; pull-up SCL
|
||||||
|
Rpull_sda V33 SDA 4.7k ; pull-up SDA
|
||||||
|
|
||||||
|
* Capacidad de bus (trazas PCB ~10cm + pines I2C de ESP32 y BNO085)
|
||||||
|
* ESP32 I2C input cap: ~5pF, BNO085 I2C input cap: ~5pF, traza: ~10pF/cm × 10cm = 100pF
|
||||||
|
Cbus_scl SCL GND 50p ; capacidad de bus SCL (solo 10cm de traza en PCB compacto)
|
||||||
|
Cbus_sda SDA GND 50p ; capacidad de bus SDA
|
||||||
|
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* ESP32 MASTER I2C — Genera transaccion I2C (direccion 0x4A, lectura)
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* Protocolo I2C 400kHz (Fast Mode):
|
||||||
|
* Periodo = 2.5us
|
||||||
|
* SCL alto = 0.6us min (spec), SCL bajo = 1.3us min
|
||||||
|
* Usamos: SCL alto = 0.9us, SCL bajo = 1.6us (simétrico aproximado)
|
||||||
|
*
|
||||||
|
* Secuencia simulada:
|
||||||
|
* t=0: Bus idle (SDA=HIGH, SCL=HIGH)
|
||||||
|
* t=5us: START condition (SDA baja mientras SCL alto)
|
||||||
|
* t=7us: SCL baja → comienzo de bits de direccion
|
||||||
|
* t=7-32us: 8 bits: direccion 0x4A + R/W=1 (lectura)
|
||||||
|
* 0x4A = 1001010, con R/W=1 → byte = 10010101 = 0x95
|
||||||
|
* t=32us: SCL sube → ACK del esclavo (BNO085 baja SDA)
|
||||||
|
* t=37us: SCL baja → BNO085 libera SDA (pull-up sube)
|
||||||
|
* t=40us: BNO085 comienza a enviar dato (primer byte de SHTP)
|
||||||
|
* t=60us: STOP condition
|
||||||
|
|
||||||
|
* SCL: generado por ESP32 (push-pull internamente, vista del bus = open-drain + pull-up)
|
||||||
|
* Modelamos el SCL como fuente de tension con resistencia baja (driver fuerte)
|
||||||
|
Vscl_drv SCL_DRV GND PWL(
|
||||||
|
+ 0 3.3
|
||||||
|
+ 4.9u 3.3
|
||||||
|
+ 5.0u 3.3 ; bus idle
|
||||||
|
+ 6.9u 3.3
|
||||||
|
+ 7.0u 0 ; SCL baja → START completado, primer bit
|
||||||
|
+ 8.5u 0
|
||||||
|
+ 8.6u 3.3 ; bit 7 (MSB): '1'
|
||||||
|
+ 9.9u 3.3
|
||||||
|
+ 10.0u 0
|
||||||
|
+ 11.4u 0
|
||||||
|
+ 11.5u 3.3 ; bit 6: '0'
|
||||||
|
+ 12.9u 3.3
|
||||||
|
+ 13.0u 0
|
||||||
|
+ 14.4u 0
|
||||||
|
+ 14.5u 3.3 ; bit 5: '0'
|
||||||
|
+ 15.9u 3.3
|
||||||
|
+ 16.0u 0
|
||||||
|
+ 17.4u 0
|
||||||
|
+ 17.5u 3.3 ; bit 4: '1'
|
||||||
|
+ 18.9u 3.3
|
||||||
|
+ 19.0u 0
|
||||||
|
+ 20.4u 0
|
||||||
|
+ 20.5u 3.3 ; bit 3: '0'
|
||||||
|
+ 21.9u 3.3
|
||||||
|
+ 22.0u 0
|
||||||
|
+ 23.4u 0
|
||||||
|
+ 23.5u 3.3 ; bit 2: '1'
|
||||||
|
+ 24.9u 3.3
|
||||||
|
+ 25.0u 0
|
||||||
|
+ 26.4u 0
|
||||||
|
+ 26.5u 3.3 ; bit 1: '0'
|
||||||
|
+ 27.9u 3.3
|
||||||
|
+ 28.0u 0
|
||||||
|
+ 29.4u 0
|
||||||
|
+ 29.5u 3.3 ; bit 0 (R/W=1, lectura)
|
||||||
|
+ 30.9u 3.3
|
||||||
|
+ 31.0u 0 ; SCL bajo para ACK
|
||||||
|
+ 32.4u 0
|
||||||
|
+ 32.5u 3.3 ; SCL sube: BNO085 debe mantener SDA baja (ACK)
|
||||||
|
+ 33.9u 3.3
|
||||||
|
+ 34.0u 0
|
||||||
|
+ 59.9u 0
|
||||||
|
+ 60.0u 3.3) ; ultimo SCL bajo → STOP
|
||||||
|
Rscl_drv SCL_DRV SCL 10 ; impedancia del driver I2C del ESP32
|
||||||
|
|
||||||
|
* SDA: ESP32 genera START y los bits de direccion
|
||||||
|
* BNO085 genera ACK (baja SDA durante ACK clock)
|
||||||
|
Vsda_drv SDA_DRV GND PWL(
|
||||||
|
+ 0 3.3
|
||||||
|
+ 4.9u 3.3
|
||||||
|
+ 5.0u 0 ; START: SDA baja mientras SCL alto
|
||||||
|
+ 5.9u 0
|
||||||
|
+ 6.0u 3.3 ; START completado: SDA sube (SCL ya bajo)
|
||||||
|
* bits de direccion 0x95 = 10010101 (MSB first)
|
||||||
|
* bit7=1: SDA alto (pull-up)
|
||||||
|
+ 6.9u 3.3
|
||||||
|
+ 7.0u 3.3 ; bit 7 = 1 (recesivo, pull-up mantiene alto)
|
||||||
|
+ 9.9u 3.3
|
||||||
|
+ 10.0u 0 ; bit 6 = 0 (ESP32 baja SDA)
|
||||||
|
+ 11.4u 0
|
||||||
|
+ 11.5u 0 ; bit 5 = 0
|
||||||
|
+ 13.9u 0
|
||||||
|
+ 14.0u 3.3 ; bit 4 = 1
|
||||||
|
+ 15.9u 3.3
|
||||||
|
+ 16.0u 0 ; bit 3 = 0
|
||||||
|
+ 17.9u 0
|
||||||
|
+ 18.0u 3.3 ; bit 2 = 1
|
||||||
|
+ 19.9u 3.3
|
||||||
|
+ 20.0u 0 ; bit 1 = 0
|
||||||
|
+ 21.9u 0
|
||||||
|
+ 22.0u 3.3 ; bit 0 = 1 (R/W=1)
|
||||||
|
+ 29.9u 3.3
|
||||||
|
+ 30.0u 0 ; ACK slot: ESP32 libera SDA (flota)
|
||||||
|
* BNO085 baja SDA para ACK → modelado como fuente separada
|
||||||
|
+ 34.0u 0
|
||||||
|
+ 34.1u 3.3 ; ESP32 retoma control del bus (post-ACK)
|
||||||
|
+ 59.9u 3.3
|
||||||
|
+ 60.0u 3.3) ; STOP: SDA sube mientras SCL alto
|
||||||
|
Rsda_drv SDA_DRV SDA 10
|
||||||
|
|
||||||
|
* ACK del BNO085: baja SDA durante el clock de ACK (t=32.5us a 34us)
|
||||||
|
Vsda_ack SDA_ACK GND PWL(
|
||||||
|
+ 0 3.3
|
||||||
|
+ 31.9u 3.3
|
||||||
|
+ 32.0u 0 ; BNO085 ACK: baja SDA
|
||||||
|
+ 33.9u 0
|
||||||
|
+ 34.0u 3.3) ; BNO085 libera SDA
|
||||||
|
Rack SDA_ACK SDA 50 ; el driver del BNO085 tiene impedancia finita
|
||||||
|
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* LINEA DE INTERRUPCION INT (data-ready, activo bajo, open-drain)
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* El BNO085 baja INT cuando tiene un reporte listo para leer.
|
||||||
|
* Con heading + yaw rate a 100Hz: INT pulsa cada 10ms
|
||||||
|
* El ESP32 lee el dato cuando detecta INT bajo (GPIO input con pull-up)
|
||||||
|
|
||||||
|
Rpull_int V33 INT_PIN 10k ; pull-up externo (ESP32 tiene pull-up interno tambien)
|
||||||
|
Cint INT_PIN GND 10p ; capacidad del pin
|
||||||
|
|
||||||
|
* Simula INT pulsando periodicamente (100 Hz = 10ms periodo, 100us de pulso bajo)
|
||||||
|
Vint_bno INT_DRV GND PULSE(3.3 0 5m 100n 100n 100u 10m)
|
||||||
|
Rint_drv INT_DRV INT_PIN 100 ; open-drain: BNO085 solo puede bajar, no subir
|
||||||
|
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* MEDICIONES AUTOMATICAS
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* Tiempo que tarda NRST en superar el umbral de reset (2.31V)
|
||||||
|
.meas TRAN t_reset_deassert WHEN V(nrst_node)=2.31 RISE=1
|
||||||
|
* Tension estable de alimentacion del BNO085
|
||||||
|
.meas TRAN Vvdd_stable AVG V(vdd_bno) FROM 20m TO 50m
|
||||||
|
* Tiempo de subida de SCL (10% → 90% de 3.3V = 0.33V → 2.97V)
|
||||||
|
.meas TRAN t_rise_scl TRIG V(scl)=0.33 RISE=1 TARG V(scl)=2.97 RISE=1
|
||||||
|
* Tension minima en VDD_BNO durante transient de corriente del BNO085
|
||||||
|
.meas TRAN Vvdd_min MIN V(vdd_bno) FROM 0 TO 50m
|
||||||
|
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* DIRECTIVAS DE SIMULACION
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* 50ms total: captura arranque completo + transaccion I2C + varios pulsos INT
|
||||||
|
.tran 0 50m 0 10n
|
||||||
|
|
||||||
|
.options reltol=0.001
|
||||||
|
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* VALORES ESPERADOS
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* t_reset_deassert ≈ 12ms → BNO085 sale de reset 12ms despues del arranque
|
||||||
|
* Vvdd_stable ≈ 3.28-3.30V → caida de tension por Rtrace=50mOhm + Ibno=6.5mA
|
||||||
|
* ΔV = 6.5mA × 50mOhm = 0.33mV (despreciable) ✓
|
||||||
|
* t_rise_scl ≈ 150-200ns → con Rpull=4.7k y Cbus=50pF: τ = 235ns
|
||||||
|
* tr(10%-90%) = 2.2τ × (80%) = 200ns < 300ns OK ✓
|
||||||
|
* (especificacion I2C Fast Mode: tr < 300ns)
|
||||||
|
*
|
||||||
|
* BNO085 en operacion normal:
|
||||||
|
* Corriente a 3.3V: 6.5mA tipico, 12mA maximo (fusion completa)
|
||||||
|
* Tiempo de startup tras reset: 50ms tipico (inicializacion DSP)
|
||||||
|
* Primer reporte disponible: ~100ms tras arranque
|
||||||
|
*
|
||||||
|
* CONEXION TIPICA A ESP32:
|
||||||
|
* GPIO21 → SDA (I2C SDA, con pull-up 4.7k externo)
|
||||||
|
* GPIO22 → SCL (I2C SCL, con pull-up 4.7k externo)
|
||||||
|
* GPIO34 → INT (input only, con pull-up 10k, interrupcion falling edge)
|
||||||
|
* GPIO13 → NRST (output, normalmente alto; pulsa bajo para hard reset)
|
||||||
|
*
|
||||||
|
* CONFIGURACION FIRMWARE:
|
||||||
|
* wire.begin(21, 22) → ESP32 Arduino I2C
|
||||||
|
* Wire.setClock(400000) → Fast Mode 400kHz
|
||||||
|
* BNO08x.begin(0x4A, Wire, GPIO34) → libreria SparkFun BNO08x
|
||||||
|
*
|
||||||
|
* REPORTES PARA AUTOPILOTO:
|
||||||
|
* setReports(ARVR_STABILIZED_RV, 0.01) → heading a 100Hz
|
||||||
|
* setReports(GYROSCOPE_CALIBRATED, 0.004) → yaw rate a 250Hz
|
||||||
|
* getRVheading() → degrees (0-360) con compensacion tilt
|
||||||
|
* getGyroZ() → deg/s (eje Z = yaw rate, positivo = estribor)
|
||||||
|
|
||||||
|
.backanno
|
||||||
|
.end
|
||||||
Reference in New Issue
Block a user