2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
24 #include "common/maths.h"
25 #include "common/printf.h"
27 #include "config/parameter_group.h"
28 #include "config/parameter_group_ids.h"
30 #include "drivers/1-wire.h"
31 #include "drivers/temperature/temperature.h"
32 #include "drivers/temperature/lm75.h"
33 #include "drivers/temperature/ds18b20.h"
35 #include "fc/runtime_config.h"
37 #include "sensors/sensors.h"
38 #include "sensors/temperature.h"
39 #include "sensors/gyro.h"
40 #include "sensors/barometer.h"
42 #include "scheduler/protothreads.h"
45 PG_REGISTER_ARRAY(tempSensorConfig_t
, MAX_TEMP_SENSORS
, tempSensorConfig
, PG_TEMP_SENSOR_CONFIG
, 2);
47 #define MPU_TEMP_VALID_BIT 0
48 #define BARO_TEMP_VALID_BIT 1
49 #define MPU_TEMP_VALID (mpuBaroTempValid & (1 << MPU_TEMP_VALID_BIT))
50 #define BARO_TEMP_VALID (mpuBaroTempValid & (1 << BARO_TEMP_VALID_BIT))
52 static uint16_t mpuTemperature
, baroTemperature
;
53 static uint8_t mpuBaroTempValid
= 0;
55 #ifdef USE_TEMPERATURE_SENSOR
56 static int16_t tempSensorValue
[MAX_TEMP_SENSORS
];
58 // Each bit corresponds to a sensor LSB = first sensor. 1 = valid value
59 static uint8_t sensorStatus
[MAX_TEMP_SENSORS
/ 8 + (MAX_TEMP_SENSORS
% 8 ? 1 : 0)];
61 #ifdef USE_TEMPERATURE_LM75
62 static temperatureDev_t lm75Dev
[8];
65 #ifdef DS18B20_DRIVER_AVAILABLE
66 static owDev_t
*owDev
;
69 static void newSensorCheckAndEnter(uint8_t type
, uint64_t addr
)
71 int8_t foundConfigIndex
= -1, firstFreeConfigSlot
= -1;
73 // try to find sensor or free config slot
74 for (uint8_t configIndex
= 0; configIndex
< MAX_TEMP_SENSORS
; ++configIndex
) {
76 const tempSensorConfig_t
*configSlot
= tempSensorConfig(configIndex
);
78 if ((configSlot
->type
== type
) && (configSlot
->address
== addr
)) {
79 foundConfigIndex
= configIndex
;
83 if ((firstFreeConfigSlot
== -1) && !configSlot
->type
) firstFreeConfigSlot
= configIndex
;
87 // if new sensor and have free config slot enter new sensor
88 if ((foundConfigIndex
== -1) && (firstFreeConfigSlot
!= -1)) {
89 tempSensorConfig_t
*configSlot
= tempSensorConfigMutable(firstFreeConfigSlot
);
90 configSlot
->type
= type
;
91 configSlot
->address
= addr
;
92 configSlot
->osdSymbol
= 0;
93 configSlot
->label
[0] = '\0';
94 configSlot
->alarm_min
= -200;
95 configSlot
->alarm_max
= 600;
99 void temperatureInit(void)
101 memset(sensorStatus
, 0, sizeof(sensorStatus
) * sizeof(*sensorStatus
));
103 sensorsSet(SENSOR_TEMP
);
105 #ifdef USE_TEMPERATURE_LM75
106 memset(lm75Dev
, 0, sizeof(lm75Dev
));
107 for (uint8_t lm75Addr
= 0; lm75Addr
< 8; ++lm75Addr
) {
108 if (lm75Detect(lm75Dev
+ lm75Addr
, lm75Addr
))
109 newSensorCheckAndEnter(TEMP_SENSOR_LM75
, lm75Addr
);
113 #ifdef DS18B20_DRIVER_AVAILABLE
116 uint64_t ds18b20_rom_table
[MAX_TEMP_SENSORS
];
117 uint8_t ds18b20_rom_count
= MAX_TEMP_SENSORS
;
118 ds18b20Enumerate(owDev
, ds18b20_rom_table
, &ds18b20_rom_count
);
120 for (uint8_t rom_index
= 0; rom_index
< ds18b20_rom_count
; ++rom_index
)
121 newSensorCheckAndEnter(TEMP_SENSOR_DS18B20
, ds18b20_rom_table
[rom_index
]);
126 for (uint8_t configIndex
= 0; configIndex
< MAX_TEMP_SENSORS
; ++configIndex
) {
127 const tempSensorConfig_t
*configSlot
= tempSensorConfig(configIndex
);
129 switch (configSlot
->type
) {
130 #ifdef DS18B20_DRIVER_AVAILABLE
131 case TEMP_SENSOR_DS18B20
:
133 tempSensorValue
[configIndex
] = TEMPERATURE_INVALID_VALUE
;
134 ds18b20Configure(owDev
, configSlot
->address
, DS18B20_CONFIG_9BIT
);
144 static bool temperatureSensorValueIsValid(uint8_t temperatureUpdateSensorIndex
)
146 uint8_t mask
= 1 << (temperatureUpdateSensorIndex
% 8);
147 uint8_t byteIndex
= temperatureUpdateSensorIndex
/ 8;
148 return sensorStatus
[byteIndex
] & mask
;
151 // returns decidegrees centigrade
152 bool getSensorTemperature(uint8_t temperatureUpdateSensorIndex
, int16_t *temperature
)
154 *temperature
= tempSensorValue
[temperatureUpdateSensorIndex
];
155 return temperatureSensorValueIsValid(temperatureUpdateSensorIndex
);
158 // Converts 64bit integer address to hex format
159 // hex_address must be at least 17 bytes long (16 chars + NULL)
160 void tempSensorAddressToString(uint64_t address
, char *hex_address
)
163 tfp_sprintf(hex_address
, "%d", (int)address
);
165 uint32_t *address32
= (uint32_t *)&address
;
166 tfp_sprintf(hex_address
, "%08lx%08lx", (unsigned long)address32
[1], (unsigned long)address32
[0]);
170 // Converts address string in hex format to unsigned integer
171 // the hex_address parameter must be NULL or space terminated
172 bool tempSensorStringToAddress(const char *hex_address
, uint64_t *address
)
174 uint16_t char_count
= 0;
176 while (*hex_address
&& (*hex_address
!= ' ')) {
177 if (++char_count
> 16) return false;
178 char byte
= *hex_address
++;
179 if (byte
>= '0' && byte
<= '9') byte
= byte
- '0';
180 else if (byte
>= 'a' && byte
<='f') byte
= byte
- 'a' + 10;
181 else if (byte
>= 'A' && byte
<='F') byte
= byte
- 'A' + 10;
183 *address
= (*address
<< 4) | (byte
& 0xF);
187 #endif /* USE_TEMPERATURE_SENSOR */
189 #ifdef USE_TEMPERATURE_SENSOR
191 static uint8_t temperatureUpdateSensorIndex
;
192 static bool temperatureUpdateValueValid
;
194 #ifdef DS18B20_DRIVER_AVAILABLE
195 static uint8_t temperatureUpdateIndex
;
196 static uint8_t temperatureUpdateBuf
[9];
199 #endif /* defined(USE_TEMPERATURE_SENSOR) */
201 PROTOTHREAD(temperatureUpdate
)
203 ptBegin(temperatureUpdate
);
207 if (gyroReadTemperature()) {
208 mpuTemperature
= gyroGetTemperature();
209 mpuBaroTempValid
|= (1 << MPU_TEMP_VALID_BIT
);
211 mpuBaroTempValid
&= ~(1 << MPU_TEMP_VALID_BIT
);
214 if (sensors(SENSOR_BARO
)) {
215 baroTemperature
= baroGetTemperature();
216 mpuBaroTempValid
|= (1 << BARO_TEMP_VALID_BIT
);
218 mpuBaroTempValid
&= ~(1 << BARO_TEMP_VALID_BIT
);
221 #ifdef USE_TEMPERATURE_SENSOR
223 temperatureUpdateSensorIndex
= 0;
225 const tempSensorConfig_t
*configSlot
= tempSensorConfig(temperatureUpdateSensorIndex
);
226 temperatureUpdateValueValid
= false;
228 #ifdef USE_TEMPERATURE_LM75
229 if (configSlot
->type
== TEMP_SENSOR_LM75
) {
230 if (configSlot
->address
< 8) {
231 temperatureDev_t
*dev
= lm75Dev
+ configSlot
->address
;
232 if (dev
->read
&& dev
->read(dev
, &tempSensorValue
[temperatureUpdateSensorIndex
])) temperatureUpdateValueValid
= true;
237 #ifdef DS18B20_DRIVER_AVAILABLE
238 if ((configSlot
->type
== TEMP_SENSOR_DS18B20
) && owDev
) {
239 bool ack
= owDev
->owResetCommand(owDev
);
240 if (!ack
) goto temperatureUpdateError
;
241 ptWait(owDev
->owBusReady(owDev
));
243 ack
= owDev
->owMatchRomCommand(owDev
);
244 if (!ack
) goto temperatureUpdateError
;
245 ptWait(owDev
->owBusReady(owDev
));
247 temperatureUpdateIndex
= 0;
249 ack
= owDev
->owWriteByteCommand(owDev
, ((uint8_t *)&tempSensorConfig(temperatureUpdateSensorIndex
)->address
)[temperatureUpdateIndex
]);
250 if (!ack
) goto temperatureUpdateError
;
251 ptWait(owDev
->owBusReady(owDev
));
252 } while (++temperatureUpdateIndex
< 8);
254 ack
= ds18b20ReadScratchpadCommand(owDev
);
255 if (!ack
) goto temperatureUpdateError
;
256 ptWait(owDev
->owBusReady(owDev
));
258 temperatureUpdateIndex
= 0;
260 ack
= owDev
->owReadByteCommand(owDev
);
261 if (!ack
) goto temperatureUpdateError
;
262 ptWait(owDev
->owBusReady(owDev
));
263 ack
= owDev
->owReadByteResult(owDev
, temperatureUpdateBuf
+ temperatureUpdateIndex
);
264 if (!ack
) goto temperatureUpdateError
;
265 } while (++temperatureUpdateIndex
< 9);
268 if (ds18b20ReadTemperatureFromScratchPadBuf(temperatureUpdateBuf
, &temperature
)) {
269 if (temperatureSensorValueIsValid(temperatureUpdateSensorIndex
) || (tempSensorValue
[temperatureUpdateSensorIndex
] == -1240)) {
270 tempSensorValue
[temperatureUpdateSensorIndex
] = temperature
;
271 temperatureUpdateValueValid
= true;
273 tempSensorValue
[temperatureUpdateSensorIndex
] = -1240;
275 tempSensorValue
[temperatureUpdateSensorIndex
] = TEMPERATURE_INVALID_VALUE
;
278 temperatureUpdateError
:;
281 uint8_t statusMask
= 1 << (temperatureUpdateSensorIndex
% 8);
282 uint8_t byteIndex
= temperatureUpdateSensorIndex
/ 8;
283 if (temperatureUpdateValueValid
)
284 sensorStatus
[byteIndex
] |= statusMask
;
286 sensorStatus
[byteIndex
] &= ~statusMask
;
290 } while (++temperatureUpdateSensorIndex
< MAX_TEMP_SENSORS
);
292 #ifdef DS18B20_DRIVER_AVAILABLE
294 bool ack
= owDev
->owResetCommand(owDev
);
295 if (!ack
) goto ds18b20StartConversionError
;
296 ptWait(owDev
->owBusReady(owDev
));
298 ack
= owDev
->owSkipRomCommand(owDev
);
299 if (!ack
) goto ds18b20StartConversionError
;
300 ptWait(owDev
->owBusReady(owDev
));
302 ds18b20StartConversionCommand(owDev
);
305 ds18b20StartConversionError
:;
308 #endif /* defined(USE_TEMPERATURE_SENSOR) */
310 ptDelayMs(100); // DS18B20 sensors take 94ms for a temperature conversion with 9bit resolution
317 // returns decidegrees centigrade
318 bool getIMUTemperature(int16_t *temperature
)
320 *temperature
= mpuTemperature
;
321 return MPU_TEMP_VALID
;
324 // returns decidegrees centigrade
325 bool getBaroTemperature(int16_t *temperature
)
327 *temperature
= baroTemperature
;
328 return BARO_TEMP_VALID
;
331 void resetTempSensorConfig(void)
333 memset(tempSensorConfigMutable(0), 0, sizeof(tempSensorConfig_t
) * MAX_TEMP_SENSORS
);