Skip to content

Instantly share code, notes, and snippets.

@phoddie
Last active March 14, 2023 17:06
Show Gist options
  • Save phoddie/2438bfb7760feff74f8f91cbaec90b0b to your computer and use it in GitHub Desktop.
Save phoddie/2438bfb7760feff74f8f91cbaec90b0b to your computer and use it in GitHub Desktop.
patch for onewire support on esp8266
/*
* Copyright (c) 2016-2023 Moddable Tech, Inc.
*
* This file is part of the Moddable SDK Runtime.
*
* The Moddable SDK Runtime is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Moddable SDK Runtime 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the Moddable SDK Runtime. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "xsHost.h"
#include <ets_sys.h>
#include <osapi.h>
#include <gpio.h>
#include "esp8266_peri.h"
#include "modGPIO.h"
static const uint32_t gPixMuxAddr[] ICACHE_RODATA_ATTR = {
PERIPHS_IO_MUX_GPIO0_U,
PERIPHS_IO_MUX_U0TXD_U,
PERIPHS_IO_MUX_GPIO2_U,
PERIPHS_IO_MUX_U0RXD_U,
PERIPHS_IO_MUX_GPIO4_U,
PERIPHS_IO_MUX_GPIO5_U,
PERIPHS_IO_MUX_SD_CLK_U,
PERIPHS_IO_MUX_SD_DATA0_U,
PERIPHS_IO_MUX_SD_DATA1_U,
PERIPHS_IO_MUX_SD_DATA2_U,
PERIPHS_IO_MUX_SD_DATA3_U,
PERIPHS_IO_MUX_SD_CMD_U,
PERIPHS_IO_MUX_MTDI_U,
PERIPHS_IO_MUX_MTCK_U,
PERIPHS_IO_MUX_MTMS_U,
PERIPHS_IO_MUX_MTDO_U
};
static const uint8_t gPixMuxValue[] ICACHE_RODATA_ATTR = {
FUNC_GPIO0,
FUNC_GPIO1,
FUNC_GPIO2,
FUNC_GPIO3,
FUNC_GPIO4,
FUNC_GPIO5,
FUNC_GPIO6,
FUNC_GPIO7,
FUNC_GPIO8,
FUNC_GPIO9,
FUNC_GPIO10,
FUNC_GPIO11,
FUNC_GPIO12,
FUNC_GPIO13,
FUNC_GPIO14,
FUNC_GPIO15
};
/*
gpio
*/
#define GPIO_INIT_OUTPUT(index, opendrain) \
*(volatile uint32_t *)(PERIPHS_GPIO_BASEADDR + 0x10) |= (1 << index); \
GPC(index) = (GPC(index) & (0xF << GPCI)); \
if (opendrain) {GPC(index) |= (1 << GPCD);}
#define GPIO_INIT_INPUT(index) \
*(volatile uint32_t *)(PERIPHS_GPIO_BASEADDR + 0x10) &= ~(1 << index); \
GPEC = (1 << index); \
GPC(index) = (GPC(index) & (0xF << GPCI)) | (1 << GPCD);
#define GPIO_CLEAR(index) (GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << index))
#define GPIO_SET(index) (GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << index))
#define kUninitializedPin (255)
int modGPIOInit(modGPIOConfiguration config, const char *port, uint8_t pin, uint32_t mode)
{
int result;
if ((pin > 17) || port) {
config->pin = kUninitializedPin;
return -1;
}
config->pin = pin;
result = modGPIOSetMode(config, mode);
if (result) {
config->pin = kUninitializedPin;
return result;
}
return 0;
}
void modGPIOUninit(modGPIOConfiguration config)
{
config->pin = kUninitializedPin;
}
int modGPIOSetMode(modGPIOConfiguration config, uint32_t mode)
{
switch (mode) {
case kModGPIOInput:
case kModGPIOInputPullUp:
case kModGPIOInputPullDown:
if (config->pin < 16) {
PIN_FUNC_SELECT(gPixMuxAddr[config->pin], c_read8(&gPixMuxValue[config->pin]));
GPIO_INIT_INPUT(config->pin);
if (mode == kModGPIOInputPullUp)
*(volatile uint32_t *)gPixMuxAddr[config->pin] |= 1 << 7;
else if (mode == kModGPIOInputPullDown)
*(volatile uint32_t *)gPixMuxAddr[config->pin] |= 1 << 6;
}
else if (16 == config->pin) {
if (kModGPIOInput != mode)
return -1;
WRITE_PERI_REG(PAD_XPD_DCDC_CONF,
(READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xffffffbc) | (uint32)0x1); // mux configuration for XPD_DCDC and rtc_gpio0 connection
WRITE_PERI_REG(RTC_GPIO_CONF,
(READ_PERI_REG(RTC_GPIO_CONF) & (uint32)0xfffffffe) | (uint32)0x0); //mux configuration for out enable
WRITE_PERI_REG(RTC_GPIO_ENABLE,
READ_PERI_REG(RTC_GPIO_ENABLE) & (uint32)0xfffffffe); //out disable
}
else if (17 == config->pin) {
if (kModGPIOInput != mode)
return -1;
}
break;
case kModGPIOOutput:
case kModGPIOOutputOpenDrain:
if (config->pin < 16) {
PIN_FUNC_SELECT(gPixMuxAddr[config->pin], c_read8(&gPixMuxValue[config->pin]));
GPIO_INIT_OUTPUT(config->pin, kModGPIOOutputOpenDrain == mode);
GPIO_CLEAR(config->pin);
}
else if (16 == config->pin) {
if (kModGPIOOutputOpenDrain == mode)
return -1;
WRITE_PERI_REG(PAD_XPD_DCDC_CONF,
(READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xffffffbc) | (uint32)0x1); // mux configuration for XPD_DCDC to output rtc_gpio0
WRITE_PERI_REG(RTC_GPIO_CONF,
(READ_PERI_REG(RTC_GPIO_CONF) & (uint32)0xfffffffe) | (uint32)0x0); //mux configuration for out enable
WRITE_PERI_REG(RTC_GPIO_ENABLE,
(READ_PERI_REG(RTC_GPIO_ENABLE) & (uint32)0xfffffffe) | (uint32)0x1); //out enable
}
else if (17 == config->pin)
return -1;
break;
default:
return -1;
}
return 0;
}
uint8_t modGPIORead(modGPIOConfiguration config)
{
if (config->pin < 16)
return (GPIO_REG_READ(GPIO_IN_ADDRESS) >> config->pin) & 1;
if (16 == config->pin)
return READ_PERI_REG(RTC_GPIO_IN_DATA) & 1;
if (17 == config->pin)
return system_adc_read() >= 512;
return kModGPIOReadError;
}
void modGPIOWrite(modGPIOConfiguration config, uint8_t value)
{
if (config->pin < 16) {
if (value)
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << config->pin);
else
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << config->pin);
}
else if (16 == config->pin) {
WRITE_PERI_REG(RTC_GPIO_OUT,
(READ_PERI_REG(RTC_GPIO_OUT) & (uint32)0xfffffffe) | (uint32)(value & 1));
}
}
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
* Copyright (c) 2019/05/11 Wilberforce for use in Moddable SDK
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stddef.h>
#include <stdbool.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include "xsmc.h"
#include "xsHost.h"
#include "modGPIO.h"
#include "owb.h"
#include "owb_gpio.h"
#define PIN_TO_BASEREG(pin) ((volatile uint32_t*) GPO)
#define PIN_TO_BITMASK(pin) (1 << pin)
#define IO_REG_TYPE uint32_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
#define DIRECT_READ(base, mask) ((GPI & (mask)) ? 1 : 0) //GPIO_IN_ADDRESS
#define DIRECT_MODE_INPUT(base, mask) (GPE &= ~(mask)) //GPIO_ENABLE_W1TC_ADDRESS
#define DIRECT_MODE_OUTPUT(base, mask) (GPE |= (mask)) //GPIO_ENABLE_W1TS_ADDRESS
#define DIRECT_WRITE_LOW(base, mask) (GPOC = (mask)) //GPIO_OUT_W1TC_ADDRESS
#define DIRECT_WRITE_HIGH(base, mask) (GPOS = (mask)) //GPIO_OUT_W1TS_ADDRESS
// Use for now... sort later... Need to allocate
static modGPIOConfigurationRecord config;
/// @cond ignore
struct _OneWireBus_Timing
{
uint32_t A, B, C, D, E, F, G, H, I, J;
};
//// @endcond
// 1-Wire timing delays (standard) in microseconds.
// Labels and values are from https://www.maximintegrated.com/en/app-notes/index.mvp/id/126
// Mirror: http://www.pyfn.com/datasheets/maxim/AN126.pdf
static const struct _OneWireBus_Timing _StandardTiming = {
6, // A - read/write "1" master pull DQ low duration
64, // B - write "0" master pull DQ low duration
60, // C - write "1" master pull DQ high duration
10, // D - write "0" master pull DQ high duration
9, // E - read master pull DQ high duration
55, // F - complete read timeslot + 10ms recovery
0, // G - wait before reset
480, // H - master pull DQ low duration
70, // I - master pull DQ high duration
410, // J - complete presence timeslot + recovery
};
#define info_from_bus(owb) container_of(owb, owb_gpio_driver_info, bus)
/**
* @brief Generate a 1-Wire reset (initialization).
* @param[in] bus Initialised bus instance.
* @param[out] is_present true if device is present, otherwise false.
* @return status
*/
static owb_status _reset(const OneWireBus * bus, bool * is_present)
{
*is_present = false;
struct modGPIOConfigurationRecord *config = bus->config;
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = PIN_TO_BASEREG(config->pin);
uint8_t retries = 125;
modCriticalSectionBegin();
// DIRECT_MODE_INPUT(reg, PIN_TO_BITMASK(config->pin));
modGPIOSetMode(config, kModGPIOInput);
modCriticalSectionEnd();
// wait until the wire is high... just in case
while (!modGPIORead(config)) {
ets_delay_us(2);
if (--retries == 0) {
modLog("wire never high");
return OWB_STATUS_OK;
}
}
modCriticalSectionBegin();
modGPIOWrite(config, 0);
// DIRECT_MODE_OUTPUT(REG, PIN_TO_BITMASK(config->pin));
modGPIOSetMode(config, kModGPIOOutputOpenDrain); // drive output low
modCriticalSectionEnd();
ets_delay_us(bus->timing->H);
modCriticalSectionBegin();
// DIRECT_MODE_INPUT(reg, PIN_TO_BITMASK(config->pin)); // allow it to float
modGPIOSetMode(config, kModGPIOInput);
ets_delay_us(bus->timing->I);
if (!modGPIORead(config))
*is_present = true;
modCriticalSectionEnd();
ets_delay_us(bus->timing->J);
//if (!*is_present)
// modLog("device on bus");
//else
// modLog("no device on bus");
return OWB_STATUS_OK;
}
/**
* @brief Send a 1-Wire write bit, with recovery time.
* @param[in] bus Initialised bus instance.
* @param[in] bit The value to send.
*/
static void _write_bit(const OneWireBus * bus, int bit)
{
struct modGPIOConfigurationRecord *config = bus->config;
modCriticalSectionBegin();
modGPIOWrite(config, 0); // Drive DQ low
// DIRECT_MODE_OUTPUT(REG, PIN_TO_BITMASK(config->pin));
modGPIOSetMode(config, kModGPIOOutputOpenDrain);
if (bit) {
ets_delay_us(bus->timing->A);
modGPIOWrite(config, 1); // Release the bus
modCriticalSectionEnd();
ets_delay_us(bus->timing->B);
}
else {
ets_delay_us(bus->timing->C);
modGPIOWrite(config, 1); // Release the bus
modCriticalSectionEnd();
ets_delay_us(bus->timing->D);
}
}
/**
* @brief Read a bit from the 1-Wire bus and return the value, with recovery time.
* @param[in] bus Initialised bus instance.
*/
static int _read_bit(const OneWireBus * bus)
{
struct modGPIOConfigurationRecord *config = bus->config;
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = PIN_TO_BASEREG(config->pin);
modCriticalSectionBegin();
// DIRECT_MODE_OUTPUT(REG, PIN_TO_BITMASK(config->pin));
modGPIOSetMode(config, kModGPIOOutputOpenDrain);
modGPIOWrite(config, 0); // Drive DQ low
ets_delay_us(bus->timing->A);
// DIRECT_MODE_INPUT(reg, PIN_TO_BITMASK(config->pin)); // Release the bus
modGPIOSetMode(config, kModGPIOInput);
ets_delay_us(bus->timing->E);
int level = modGPIORead(config);
modCriticalSectionEnd();
ets_delay_us(bus->timing->F); // Complete the timeslot and 10us recovery
return level & 0x01;
}
/**
* @brief Write 1-Wire data byte.
* NOTE: The data is shifted out of the low bits, eg. it is written in the order of lsb to msb
* @param[in] bus Initialised bus instance.
* @param[in] data Value to write.
* @param[in] number_of_bits_to_read bits to write
*/
static owb_status _write_bits(const OneWireBus * bus, uint8_t data, int number_of_bits_to_write)
{
for (int i = 0; i < number_of_bits_to_write; ++i)
{
_write_bit(bus, data & 0x01);
data >>= 1;
}
return OWB_STATUS_OK;
}
/**
* @brief Read 1-Wire data byte from bus.
* NOTE: Data is read into the high bits, eg. each bit read is shifted down before the next bit is read
* @param[in] bus Initialised bus instance.
* @return Byte value read from bus.
*/
static owb_status _read_bits(const OneWireBus * bus, uint8_t *out, int number_of_bits_to_read)
{
uint8_t result = 0;
for (int i = 0; i < number_of_bits_to_read; ++i)
{
result >>= 1;
if (_read_bit(bus))
{
result |= 0x80;
}
}
*out = result;
return OWB_STATUS_OK;
}
static owb_status _uninitialize(const OneWireBus * bus)
{
modGPIOUninit(bus->config);
c_free(bus->config);
return OWB_STATUS_OK;
}
static const struct owb_driver gpio_function_table =
{
.name = "owb_gpio",
.uninitialize = _uninitialize,
.reset = _reset,
.write_bits = _write_bits,
.read_bits = _read_bits
};
OneWireBus* owb_gpio_initialize(owb_gpio_driver_info *driver_info, int pin)
{
driver_info->bus.driver = &gpio_function_table;
driver_info->bus.timing = &_StandardTiming;
driver_info->bus.config = c_malloc(sizeof(modGPIOConfigurationRecord));
if ( driver_info->bus.config == NULL ) {
return NULL;
}
if (modGPIOInit(driver_info->bus.config , NULL, pin, kModGPIOInput)) {
return NULL;
}
return &(driver_info->bus);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment