Fix ESP32 OneWire driver

Fix ESP32 OneWire driver (#9302)
This commit is contained in:
Theo Arends 2020-09-27 18:26:30 +02:00
parent d0b2b9f70a
commit dc3d84b266
11 changed files with 385 additions and 90 deletions

View File

@ -32,6 +32,17 @@ private email about OneWire).
OneWire is now very mature code. No changes other than adding OneWire is now very mature code. No changes other than adding
definitions for newer hardware support are anticipated. definitions for newer hardware support are anticipated.
=======
Version 2.3.3 ESP32 Stickbreaker 06MAY2019
Add a #ifdef to isolate ESP32 mods
Version 2.3.1 ESP32 everslick 30APR2018
add IRAM_ATTR attribute to write_bit/read_bit to fix icache miss delay
https://github.com/espressif/arduino-esp32/issues/1335
Version 2.3 ESP32 stickbreaker 28DEC2017
adjust to use portENTER_CRITICAL(&mux) instead of noInterrupts();
adjust to use portEXIT_CRITICAL(&mux) instead of Interrupts();
Version 2.3: Version 2.3:
Unknown chip fallback mode, Roger Clark Unknown chip fallback mode, Roger Clark
Teensy-LC compatibility, Paul Stoffregen Teensy-LC compatibility, Paul Stoffregen
@ -141,6 +152,10 @@ sample code bearing this copyright.
#include "OneWire.h" #include "OneWire.h"
#ifdef ARDUINO_ARCH_ESP32
#define noInterrupts() {portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;portENTER_CRITICAL(&mux)
#define interrupts() portEXIT_CRITICAL(&mux);}
#endif
OneWire::OneWire(uint8_t pin) OneWire::OneWire(uint8_t pin)
{ {
@ -159,13 +174,16 @@ OneWire::OneWire(uint8_t pin)
// //
// Returns 1 if a device asserted a presence pulse, 0 otherwise. // Returns 1 if a device asserted a presence pulse, 0 otherwise.
// //
#ifdef ARDUINO_ARCH_ESP32
uint8_t IRAM_ATTR OneWire::reset(void)
#else
uint8_t OneWire::reset(void) uint8_t OneWire::reset(void)
#endif
{ {
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask; IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;
uint8_t r; uint8_t r;
uint8_t retries = 125; uint8_t retries = 125;
noInterrupts(); noInterrupts();
DIRECT_MODE_INPUT(reg, mask); DIRECT_MODE_INPUT(reg, mask);
interrupts(); interrupts();
@ -178,9 +196,7 @@ uint8_t OneWire::reset(void)
noInterrupts(); noInterrupts();
DIRECT_WRITE_LOW(reg, mask); DIRECT_WRITE_LOW(reg, mask);
DIRECT_MODE_OUTPUT(reg, mask); // drive output low DIRECT_MODE_OUTPUT(reg, mask); // drive output low
interrupts();
delayMicroseconds(480); delayMicroseconds(480);
noInterrupts();
DIRECT_MODE_INPUT(reg, mask); // allow it to float DIRECT_MODE_INPUT(reg, mask); // allow it to float
delayMicroseconds(70); delayMicroseconds(70);
r = !DIRECT_READ(reg, mask); r = !DIRECT_READ(reg, mask);
@ -193,7 +209,11 @@ uint8_t OneWire::reset(void)
// Write a bit. Port and bit is used to cut lookup time and provide // Write a bit. Port and bit is used to cut lookup time and provide
// more certain timing. // more certain timing.
// //
#ifdef ARDUINO_ARCH_ESP32
void IRAM_ATTR OneWire::write_bit(uint8_t v)
#else
void OneWire::write_bit(uint8_t v) void OneWire::write_bit(uint8_t v)
#endif
{ {
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask; IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;
@ -221,7 +241,11 @@ void OneWire::write_bit(uint8_t v)
// Read a bit. Port and bit is used to cut lookup time and provide // Read a bit. Port and bit is used to cut lookup time and provide
// more certain timing. // more certain timing.
// //
#ifdef ARDUINO_ARCH_ESP32
uint8_t IRAM_ATTR OneWire::read_bit(void)
#else
uint8_t OneWire::read_bit(void) uint8_t OneWire::read_bit(void)
#endif
{ {
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask; IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg; volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;
@ -391,7 +415,6 @@ uint8_t OneWire::search(uint8_t *newAddr, bool search_mode /* = true */)
LastFamilyDiscrepancy = 0; LastFamilyDiscrepancy = 0;
return FALSE; return FALSE;
} }
// issue the search command // issue the search command
if (search_mode == true) { if (search_mode == true) {
write(0xF0); // NORMAL SEARCH write(0xF0); // NORMAL SEARCH
@ -459,7 +482,6 @@ uint8_t OneWire::search(uint8_t *newAddr, bool search_mode /* = true */)
} }
} }
while(rom_byte_number < 8); // loop until through all ROM bytes 0-7 while(rom_byte_number < 8); // loop until through all ROM bytes 0-7
// if the search was successful then // if the search was successful then
if (!(id_bit_number < 65)) if (!(id_bit_number < 65))
{ {
@ -594,4 +616,10 @@ uint16_t OneWire::crc16(const uint8_t* input, uint16_t len, uint16_t crc)
} }
#endif #endif
#ifdef ARDUINO_ARCH_ESP32
#undef noInterrupts()
#undef interrupts()
#endif
#endif #endif

View File

@ -0,0 +1,11 @@
# OneWire library
A modification of the Arduino OneWire library maintained by @PaulStoffregen. This modifications supports the ESP32 under the Arduino-esp32 Environment.
No changes are required for compatibility with Arduino coding.
Original Source is Paul's 2.3 version. Forked 28DEC2017
@stickbreaker
V2.3.1 30APR2018 add IRAM_ATTR to read_bit() write_bit() to solve ICache miss timing failure.
thanks @everslick re: https://github.com/espressif/arduino-esp32/issues/1335
V2.3 28DEC2017 original mods to support ESP32

View File

@ -3,8 +3,8 @@ version=2.3.3
author=Jim Studt, Tom Pollard, Robin James, Glenn Trewitt, Jason Dangel, Guillermo Lovato, Paul Stoffregen, Scott Roberts, Bertrik Sikken, Mark Tillotson, Ken Butcher, Roger Clark, Love Nystrom author=Jim Studt, Tom Pollard, Robin James, Glenn Trewitt, Jason Dangel, Guillermo Lovato, Paul Stoffregen, Scott Roberts, Bertrik Sikken, Mark Tillotson, Ken Butcher, Roger Clark, Love Nystrom
maintainer=Paul Stoffregen maintainer=Paul Stoffregen
sentence=Access 1-wire temperature sensors, memory and other chips. sentence=Access 1-wire temperature sensors, memory and other chips.
paragraph= paragraph= Mod of Paul Stoffregen code to support ESP32
category=Communication category=Communication
url=http://www.pjrc.com/teensy/td_libs_OneWire.html url=http://www.pjrc.com/teensy/td_libs_OneWire.html
architectures=* architectures=esp32

View File

@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifdef ESP8266
#ifdef USE_DS18x20 #ifdef USE_DS18x20
/*********************************************************************************************\ /*********************************************************************************************\
* DS18B20 - Temperature - Multiple sensors * DS18B20 - Temperature - Multiple sensors
@ -547,3 +548,4 @@ bool Xsns05(uint8_t function)
} }
#endif // USE_DS18x20 #endif // USE_DS18x20
#endif // ESP8266

View File

@ -0,0 +1,254 @@
/*
xsns_05_ds18x20_esp32.ino - DS18x20 temperature sensor support for Tasmota
Copyright (C) 2019 Heiko Krupp and Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef ESP32
#ifdef USE_DS18x20
/*********************************************************************************************\
* DS18B20 - Temperature - Multiple sensors
\*********************************************************************************************/
#define XSNS_05 5
#define DS18S20_CHIPID 0x10 // +/-0.5C 9-bit
#define DS1822_CHIPID 0x22 // +/-2C 12-bit
#define DS18B20_CHIPID 0x28 // +/-0.5C 12-bit
#define MAX31850_CHIPID 0x3B // +/-0.25C 14-bit
#define W1_SKIP_ROM 0xCC
#define W1_CONVERT_TEMP 0x44
#define W1_READ_SCRATCHPAD 0xBE
#define DS18X20_MAX_SENSORS 8
const char kDs18x20Types[] PROGMEM = "DS18x20|DS18S20|DS1822|DS18B20|MAX31850";
uint8_t ds18x20_chipids[] = { 0, DS18S20_CHIPID, DS1822_CHIPID, DS18B20_CHIPID, MAX31850_CHIPID };
uint8_t ds18x20_address[DS18X20_MAX_SENSORS][8];
uint8_t ds18x20_index[DS18X20_MAX_SENSORS];
uint8_t ds18x20_valid[DS18X20_MAX_SENSORS];
uint8_t ds18x20_sensors = 0;
char ds18x20_types[12];
/********************************************************************************************/
#include <OneWire.h>
OneWire *ds = nullptr;
void Ds18x20Init(void) {
ds = new OneWire(Pin(GPIO_DSB));
Ds18x20Search();
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors);
}
void Ds18x20Search(void) {
uint8_t num_sensors=0;
uint8_t sensor = 0;
ds->reset_search();
for (num_sensors = 0; num_sensors < DS18X20_MAX_SENSORS; num_sensors) {
if (!ds->search(ds18x20_address[num_sensors])) {
ds->reset_search();
break;
}
// If CRC Ok and Type DS18S20, DS1822, DS18B20 or MAX31850
if ((OneWire::crc8(ds18x20_address[num_sensors], 7) == ds18x20_address[num_sensors][7]) &&
((ds18x20_address[num_sensors][0]==DS18S20_CHIPID) ||
(ds18x20_address[num_sensors][0]==DS1822_CHIPID) ||
(ds18x20_address[num_sensors][0]==DS18B20_CHIPID) ||
(ds18x20_address[num_sensors][0]==MAX31850_CHIPID))) {
num_sensors++;
}
}
for (uint32_t i = 0; i < num_sensors; i++) {
ds18x20_index[i] = i;
}
for (uint32_t i = 0; i < num_sensors; i++) {
for (uint32_t j = i + 1; j < num_sensors; j++) {
if (uint32_t(ds18x20_address[ds18x20_index[i]]) > uint32_t(ds18x20_address[ds18x20_index[j]])) {
std::swap(ds18x20_index[i], ds18x20_index[j]);
}
}
}
ds18x20_sensors = num_sensors;
}
void Ds18x20Convert(void) {
ds->reset();
ds->write(W1_SKIP_ROM); // Address all Sensors on Bus
ds->write(W1_CONVERT_TEMP); // start conversion, no parasite power on at the end
// delay(750); // 750ms should be enough for 12bit conv
}
bool Ds18x20Read(uint8_t sensor, float &t)
{
uint8_t data[12];
int8_t sign = 1;
t = NAN;
uint8_t index = ds18x20_index[sensor];
if (ds18x20_valid[index]) { ds18x20_valid[index]--; }
ds->reset();
ds->select(ds18x20_address[index]);
ds->write(W1_READ_SCRATCHPAD); // Read Scratchpad
for (uint32_t i = 0; i < 9; i++) {
data[i] = ds->read();
}
if (OneWire::crc8(data, 8) == data[8]) {
switch(ds18x20_address[index][0]) {
case DS18S20_CHIPID: {
int16_t tempS = (((data[1] << 8) | (data[0] & 0xFE)) << 3) | ((0x10 - data[6]) & 0x0F);
t = ConvertTemp(tempS * 0.0625 - 0.250);
ds18x20_valid[index] = SENSOR_MAX_MISS;
return true;
}
case DS1822_CHIPID:
case DS18B20_CHIPID: {
uint16_t temp12 = (data[1] << 8) + data[0];
if (temp12 > 2047) {
temp12 = (~temp12) +1;
sign = -1;
}
t = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16
ds18x20_valid[index] = SENSOR_MAX_MISS;
return true;
}
case MAX31850_CHIPID: {
int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC);
t = ConvertTemp(temp14 * 0.0625); // Divide by 16
ds18x20_valid[index] = SENSOR_MAX_MISS;
return true;
}
}
}
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR));
return false;
}
void Ds18x20Name(uint8_t sensor)
{
uint8_t index = sizeof(ds18x20_chipids);
while (index) {
if (ds18x20_address[ds18x20_index[sensor]][0] == ds18x20_chipids[index]) {
break;
}
index--;
}
GetTextIndexed(ds18x20_types, sizeof(ds18x20_types), index, kDs18x20Types);
if (ds18x20_sensors > 1) {
snprintf_P(ds18x20_types, sizeof(ds18x20_types), PSTR("%s%c%d"), ds18x20_types, IndexSeparator(), sensor +1);
}
}
/********************************************************************************************/
void Ds18x20EverySecond(void)
{
if (!ds18x20_sensors) { return; }
if (uptime & 1) {
// 2mS
// Ds18x20Search(); // Check for changes in sensors number
Ds18x20Convert(); // Start Conversion, takes up to one second
} else {
float t;
for (uint32_t i = 0; i < ds18x20_sensors; i++) {
// 12mS per device
if (!Ds18x20Read(i, t)) { // Read temperature
Ds18x20Name(i);
AddLogMissed(ds18x20_types, ds18x20_valid[ds18x20_index[i]]);
}
}
}
}
void Ds18x20Show(bool json)
{
float t;
uint8_t dsxflg = 0;
for (uint32_t i = 0; i < ds18x20_sensors; i++) {
if (Ds18x20Read(i, t)) { // Check if read failed
char temperature[33];
dtostrfd(t, Settings.flag2.temperature_resolution, temperature);
Ds18x20Name(i);
if (json) {
char address[17];
for (uint32_t j = 0; j < 6; j++) {
sprintf(address+2*j, "%02X", ds18x20_address[ds18x20_index[i]][6-j]); // Skip sensor type and crc
}
ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), ds18x20_types, address, temperature);
dsxflg++;
#ifdef USE_DOMOTICZ
if ((0 == tele_period) && (1 == dsxflg)) {
DomoticzSensor(DZ_TEMP, temperature);
}
#endif // USE_DOMOTICZ
#ifdef USE_KNX
if ((0 == tele_period) && (1 == dsxflg)) {
KnxSensor(KNX_TEMPERATURE, t);
}
#endif // USE_KNX
#ifdef USE_WEBSERVER
} else {
WSContentSend_PD(HTTP_SNS_TEMP, ds18x20_types, temperature, TempUnit());
#endif // USE_WEBSERVER
}
}
}
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xsns05(uint8_t function)
{
bool result = false;
if (PinUsed(GPIO_DSB)) {
switch (function) {
case FUNC_INIT:
Ds18x20Init();
break;
case FUNC_EVERY_SECOND:
Ds18x20EverySecond();
break;
case FUNC_JSON_APPEND:
Ds18x20Show(1);
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
Ds18x20Show(0);
break;
#endif // USE_WEBSERVER
}
}
return result;
}
#endif // USE_DS18x20
#endif // ESP32