1 // 2 // Copyright (c) 2010-2020 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Linq; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Peripherals.Sensor; 11 using Antmicro.Renode.Peripherals.I2C; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Core; 14 using Antmicro.Renode.Core.Structure.Registers; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.Sensors 18 { 19 public class BMP180 : II2CPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, ITemperatureSensor 20 { BMP180()21 public BMP180() 22 { 23 RegistersCollection = new ByteRegisterCollection(this); 24 DefineRegisters(); 25 } 26 Reset()27 public void Reset() 28 { 29 RegistersCollection.Reset(); 30 registerAddress = 0; 31 this.Log(LogLevel.Noisy, "Reset registers"); 32 } 33 Write(byte[] data)34 public void Write(byte[] data) 35 { 36 if(data.Length == 0) 37 { 38 this.Log(LogLevel.Warning, "Unexpected write with no data"); 39 return; 40 } 41 42 this.Log(LogLevel.Noisy, "Write with {0} bytes of data: {1}", data.Length, Misc.PrettyPrintCollectionHex(data)); 43 registerAddress = (Registers)data[0]; 44 45 if(data.Length > 1) 46 { 47 // skip the first byte as it contains register address 48 foreach(var b in data.Skip(1)) 49 { 50 this.Log(LogLevel.Noisy, "Writing 0x{0:X} to register {1} (0x{1:X})", b, registerAddress); 51 RegistersCollection.Write((byte)registerAddress, b); 52 } 53 } 54 else 55 { 56 this.Log(LogLevel.Noisy, "Preparing to read register {0} (0x{0:X})", registerAddress); 57 } 58 } 59 Read(int count)60 public byte[] Read(int count) 61 { 62 this.Log(LogLevel.Noisy, "Reading {0} bytes from register {1} (0x{1:X})", count, registerAddress); 63 var result = new byte[count]; 64 for(var i = 0; i < result.Length; i++) 65 { 66 result[i] = RegistersCollection.Read((byte)registerAddress); 67 this.Log(LogLevel.Noisy, "Read value {0} from register {1} (0x{1:X})", result[i], registerAddress); 68 RegistersAutoIncrement(); 69 } 70 return result; 71 } 72 FinishTransmission()73 public void FinishTransmission() 74 { 75 } 76 77 public decimal Temperature 78 { 79 get => temperature; 80 set 81 { 82 if(value < MinTemperature | value > MaxTemperature) 83 { 84 this.Log(LogLevel.Warning, "Temperature is out of range. Supported range: {0} - {1}", MinTemperature, MaxTemperature); 85 } 86 else 87 { 88 temperature = value; 89 this.Log(LogLevel.Noisy, "Sensor temperature set to {0}", temperature); 90 } 91 } 92 } 93 94 public int UncompensatedPressure { get; set; } 95 96 public ByteRegisterCollection RegistersCollection { get; } 97 DefineRegisters()98 private void DefineRegisters() 99 { 100 Registers.CoefficientCalibrationAA.Define(this, 0x1B); //RO 101 Registers.CoefficientCalibrationAB.Define(this, 0xCB); //RO 102 Registers.CoefficientCalibrationAC.Define(this, 0xFB); //RO 103 Registers.CoefficientCalibrationAD.Define(this, 0xCB); //RO 104 Registers.CoefficientCalibrationAE.Define(this, 0xC6); //RO 105 Registers.CoefficientCalibrationAF.Define(this, 0x91); //RO 106 Registers.CoefficientCalibrationB0.Define(this, 0x7B); //RO 107 Registers.CoefficientCalibrationB1.Define(this, 0xA8); //RO 108 109 Registers.CoefficientCalibrationB2.Define(this, 0x7F) 110 .WithValueField(0, 8, out coeffCalibB2, FieldMode.Read, name: "AC5[15-8]"); 111 112 Registers.CoefficientCalibrationB3.Define(this, 0x75) 113 .WithValueField(0, 8, out coeffCalibB3, FieldMode.Read, name: "AC5[7-0]"); 114 115 Registers.CoefficientCalibrationB4.Define(this, 0x5A) 116 .WithValueField(0, 8, out coeffCalibB4, FieldMode.Read, name: "AC6[15-8]"); 117 118 Registers.CoefficientCalibrationB5.Define(this, 0x71) 119 .WithValueField(0, 8, out coeffCalibB5, FieldMode.Read, name: "AC6[7-0]"); 120 121 Registers.CoefficientCalibrationB6.Define(this, 0x15); //RO 122 Registers.CoefficientCalibrationB7.Define(this, 0x7A); //RO 123 Registers.CoefficientCalibrationB8.Define(this, 0x0); //RO 124 Registers.CoefficientCalibrationB9.Define(this, 0x38); //RO 125 Registers.CoefficientCalibrationBA.Define(this, 0x80); //RO 126 Registers.CoefficientCalibrationBB.Define(this, 0x0); //RO 127 128 Registers.CoefficientCalibrationBC.Define(this, unchecked((byte)(calibMB >> 8))) 129 .WithValueField(0, 8, out coeffCalibBC, FieldMode.Read, name: "MC[15-8]"); 130 131 Registers.CoefficientCalibrationBD.Define(this, unchecked((byte)calibMB)) 132 .WithValueField(0, 8, out coeffCalibBD, FieldMode.Read, name: "MC[7-0]"); 133 134 Registers.CoefficientCalibrationBE.Define(this, 0x0B) 135 .WithValueField(0, 8, out coeffCalibBE, FieldMode.Read, name: "MD[15-8]"); 136 137 Registers.CoefficientCalibrationBF.Define(this, 0x34) 138 .WithValueField(0, 8, out coeffCalibBF, FieldMode.Read, name: "MD[7-0]"); 139 140 Registers.ChipID.Define(this, 0x55); //RO 141 142 Registers.SoftReset.Define(this, 0x0) //WO 143 .WithWriteCallback((_, val) => 144 { 145 if(val == resetCommand) 146 { 147 Reset(); 148 } 149 }); 150 151 Registers.CtrlMeasurement.Define(this, 0x0) //RW 152 .WithValueField(0, 5, out ctrlMeasurement , name: "CTRL_MEAS") 153 .WithFlag(5, out startConversion, name: "SCO") 154 .WithValueField(6, 2, out controlOversampling, name: "OSS") 155 .WithWriteCallback((_, __) => HandleMeasurement()); 156 157 Registers.OutMSB.Define(this, 0x80) 158 .WithValueField(0, 8, out outMSB, FieldMode.Read, name: "OUT_MSB"); 159 160 Registers.OutLSB.Define(this, 0x0) 161 .WithValueField(0, 8, out outLSB, FieldMode.Read, name: "OUT_LSB"); 162 163 Registers.OutXLSB.Define(this, 0x0) 164 .WithValueField(0, 8, out outXLSB, FieldMode.Read, name: "OUT_XLSB"); 165 } 166 RegistersAutoIncrement()167 private void RegistersAutoIncrement() 168 { 169 if((registerAddress >= Registers.CoefficientCalibrationAA && 170 registerAddress < Registers.CoefficientCalibrationBF) || 171 (registerAddress >= Registers.OutMSB && registerAddress < Registers.OutXLSB)) 172 { 173 registerAddress = (Registers)((int)registerAddress + 1); 174 this.Log(LogLevel.Noisy, "Auto-incrementing to the next register 0x{0:X} - {0}", registerAddress); 175 } 176 } 177 GetUncompensatedTemperature()178 private int GetUncompensatedTemperature() 179 { 180 ushort ac5 = (ushort)((coeffCalibB2.Value << 8) + coeffCalibB3.Value); 181 ushort ac6 = (ushort)((coeffCalibB4.Value << 8) + coeffCalibB5.Value); 182 short mc = (short)((coeffCalibBC.Value << 8) + coeffCalibBD.Value); 183 short md = (short)((coeffCalibBE.Value << 8) + coeffCalibBF.Value); 184 // T = (B5+8)/2^4 => B5 = 16T-8 185 int b5 = (int)(((uint)(temperature * 10) << 4) - 8); 186 // B5 = X1 + X2 => X1 = B5-X2 187 // X2 = (MC*2^11)/(X1+MD) = (MC*2^11)/(B5-X2+MD) 188 // X2^2+X2(-B5-MD)+2^11MC = 0 => delta = (-B5-MD)^2-2^13MC 189 int delta = (int)(Math.Pow(-b5 - md, 2) - (mc << 13)); 190 // X2 = (-(-B5-MD)+sqrt(delta))/2 = (B5+MD)+sqrt(delta))/2 191 int x2 = (int)((int)(b5 + md + Math.Sqrt(delta)) >> 1); 192 // X1 = B5-X2 193 // X1 = (UT-AC6)*AC5/2^15 => UT = ((2^15X1)/AC5)+AC6 = (2^15(B5-X2)/AC5)+AC6 194 return (int)((((b5-x2) << 15)/ac5)+ac6); 195 } 196 HandleMeasurement()197 private void HandleMeasurement() 198 { 199 this.Log(LogLevel.Noisy, "HandleMeasurement set {0}", (MeasurementModes)ctrlMeasurement.Value); 200 switch((MeasurementModes)ctrlMeasurement.Value) 201 { 202 case MeasurementModes.Temperature: 203 var uncompensatedTemp = GetUncompensatedTemperature(); 204 outMSB.Value = (byte)((uncompensatedTemp >> 8) & 0xFF); 205 outLSB.Value = (byte)(uncompensatedTemp & 0xFF); 206 break; 207 case MeasurementModes.Pressure: 208 var uPressure = UncompensatedPressure << (byte)(8 - controlOversampling.Value); 209 outMSB.Value = (byte)((uPressure >> 16) & 0xFF); 210 outLSB.Value = (byte)((uPressure >> 8) & 0xFF); 211 outXLSB.Value = (byte)(uPressure & 0xFF); 212 break; 213 default: 214 break; 215 } 216 // Clear SCO bit (start of conversion) 217 startConversion.Value = false; 218 this.Log(LogLevel.Noisy, "Conversion is complete"); 219 } 220 221 private IFlagRegisterField startConversion; 222 private IValueRegisterField controlOversampling; 223 private IValueRegisterField outMSB; 224 private IValueRegisterField outLSB; 225 private IValueRegisterField outXLSB; 226 private IValueRegisterField ctrlMeasurement; 227 private Registers registerAddress; 228 229 private IValueRegisterField coeffCalibB2; 230 private IValueRegisterField coeffCalibB3; 231 private IValueRegisterField coeffCalibB4; 232 private IValueRegisterField coeffCalibB5; 233 private IValueRegisterField coeffCalibBC; 234 private IValueRegisterField coeffCalibBD; 235 private IValueRegisterField coeffCalibBE; 236 private IValueRegisterField coeffCalibBF; 237 238 private decimal temperature; 239 private const decimal MinTemperature = -40; 240 private const decimal MaxTemperature = 85; 241 private const byte resetCommand = 0xB6; 242 private const short calibMB = -8711; 243 244 private enum MeasurementModes 245 { 246 Temperature = 0x0E, 247 Pressure = 0x14, 248 } 249 250 private enum Registers 251 { 252 CoefficientCalibrationAA = 0xAA, // Read-Only 253 CoefficientCalibrationAB = 0xAB, 254 CoefficientCalibrationAC = 0xAC, 255 CoefficientCalibrationAD = 0xAD, 256 CoefficientCalibrationAE = 0xAE, 257 CoefficientCalibrationAF = 0xAF, 258 CoefficientCalibrationB0 = 0xB0, 259 CoefficientCalibrationB1 = 0xB1, 260 CoefficientCalibrationB2 = 0xB2, 261 CoefficientCalibrationB3 = 0xB3, 262 CoefficientCalibrationB4 = 0xB4, 263 CoefficientCalibrationB5 = 0xB5, 264 CoefficientCalibrationB6 = 0xB6, 265 CoefficientCalibrationB7 = 0xB7, 266 CoefficientCalibrationB8 = 0xB8, 267 CoefficientCalibrationB9 = 0xB9, 268 CoefficientCalibrationBA = 0xBA, 269 CoefficientCalibrationBB = 0xBB, 270 CoefficientCalibrationBC = 0xBC, 271 CoefficientCalibrationBD = 0xBD, 272 CoefficientCalibrationBE = 0xBE, 273 CoefficientCalibrationBF = 0xBF, 274 ChipID = 0xD0, // Read-Only 275 SoftReset = 0xE0, // Write-Only 276 CtrlMeasurement = 0xF4, // Read-Write 277 OutMSB = 0xF6, // Read-Only 278 OutLSB = 0xF7, // Read-Only 279 OutXLSB = 0xF8 // Read-Only 280 } 281 } 282 } 283 284