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