OneWire - Fast pin mode defines

I’m trying to port this OneWire library to the Photon. The main things that need to be changed are the methods of fast digital writes, digital reads, and pinmode changes.

The PJRC’s Arduino library uses a set of defines like this to do the fast reads, writes, and pinmode changes:

#if defined(__AVR__)
#define PIN_TO_BASEREG(pin)             (portInputRegister(digitalPinToPort(pin)))
#define PIN_TO_BITMASK(pin)             (digitalPinToBitMask(pin))
#define IO_REG_TYPE uint8_t
#define IO_REG_ASM asm("r30")
#define DIRECT_READ(base, mask)         (((*(base)) & (mask)) ? 1 : 0)
#define DIRECT_MODE_INPUT(base, mask)   ((*((base)+1)) &= ~(mask))
#define DIRECT_MODE_OUTPUT(base, mask)  ((*((base)+1)) |= (mask))
#define DIRECT_WRITE_LOW(base, mask)    ((*((base)+2)) &= ~(mask))
#define DIRECT_WRITE_HIGH(base, mask)   ((*((base)+2)) |= (mask))

There is a port to the Spark Core by @tidwelltimj that replaces the defines with functions (and modifies the rest of the library to use these functions rather than the defines above):

void DIRECT_WRITE_LOW(void);
void DIRECT_MODE_OUTPUT(void);
void DIRECT_WRITE_HIGH(void);
void DIRECT_MODE_INPUT(void);
uint8_t DIRECT_READ(void);

These functions basically copy the internals of the Core’s wiring digital write/read and pin mode functions, but remove some of the checks to speed it up a little. They look like this:

// tidwelltimj's direct functions for the Core

void OneWire::DIRECT_WRITE_LOW(void)
{
PIN_MAP[_pin].gpio_peripheral->BRR = PIN_MAP[_pin].gpio_pin;
}    
void OneWire::DIRECT_MODE_OUTPUT(void)
{
GPIO_TypeDef *gpio_port = PIN_MAP[_pin].gpio_peripheral;
    uint16_t gpio_pin = PIN_MAP[_pin].gpio_pin;

        GPIO_InitTypeDef GPIO_InitStructure;

        if (gpio_port == GPIOA )
        {
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
        }
        else if (gpio_port == GPIOB )
        {
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
        }

    GPIO_InitStructure.GPIO_Pin = gpio_pin;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    PIN_MAP[_pin].pin_mode = OUTPUT;
    GPIO_Init(gpio_port, &GPIO_InitStructure);
}

// ...etc.

I did a really naive port of this to the Photon by replacing @tidwelltimj’s functions with:

// My naive "direct" functions for the Photon.

void OneWire::DIRECT_WRITE_LOW(void){
  HAL_GPIO_Write(_pin, LOW);
}

void OneWire::DIRECT_MODE_OUTPUT(void){
  HAL_Pin_Mode(_pin, OUTPUT);
}

void OneWire::DIRECT_WRITE_HIGH(void){
  HAL_GPIO_Write(_pin, HIGH);
}

void OneWire::DIRECT_MODE_INPUT(void){
  HAL_Pin_Mode(_pin, INPUT);
}

uint8_t OneWire::DIRECT_READ(void){
  return HAL_GPIO_Read(_pin);
}

This seems to work ok, but I was worried that it wasn’t going to be as reliable as the more direct methods.

In the port of the NeoPixel library, the following code is used to support fast writes on both the Core and the Photon:

#if PLATFORM_ID == 0 // Core
  #define pinLO(_pin) (PIN_MAP[_pin].gpio_peripheral->BRR = PIN_MAP[_pin].gpio_pin)
  #define pinHI(_pin) (PIN_MAP[_pin].gpio_peripheral->BSRR = PIN_MAP[_pin].gpio_pin)
#elif PLATFORM_ID == 6 // Photon
  #include "pinmap_impl.h"
  STM32_Pin_Info* PIN_MAP = HAL_Pin_Map(); // Pointer required for highest access speed
  #define pinLO(_pin) (PIN_MAP[_pin].gpio_peripheral->BSRRH = PIN_MAP[_pin].gpio_pin)
  #define pinHI(_pin) (PIN_MAP[_pin].gpio_peripheral->BSRRL = PIN_MAP[_pin].gpio_pin)
#else
  #error "*** PLATFORM_ID not supported by this library. PLATFORM should be Core or Photon ***"
#endif
// fast pin access
#define pinSet(_pin, _hilo) (_hilo ? pinHI(_pin) : pinLO(_pin))

This seems like it’s what I’m looking for, but it only does digital write. Is there an equivalently simple set of defines that get a fast digital read and pin mode for both the core and the photon?

Am I write to worry about using the various HAL_xxx functions not being “direct” enough? Using the HAL_ functions worked for reading a DS18B20 temperature sensor, but I wasn’t sure if it was a complete solution.

1 Like

Have a look at this thread: Photon and the PIN_MAP[] challenge! :smile:

Ok, I took a shot at getting this library up and running: https://github.com/balbano/OneWire-Photon

The relevant snippet is:

  #elif PLATFORM_ID == 6 // Photon
    STM32_Pin_Info* PIN_MAP = HAL_Pin_Map(); // Pointer required for highest access speed

    inline void digitalWriteFastLow() {
      PIN_MAP[_pin].gpio_peripheral->BSRRH = PIN_MAP[_pin].gpio_pin;
    }

    inline void digitalWriteFastHigh() {
      PIN_MAP[_pin].gpio_peripheral->BSRRL = PIN_MAP[_pin].gpio_pin;
    }

    inline void pinModeFastOutput(void){
      // This could probably be speed up by digging a little deeper past
      // the HAL_Pin_Mode function.
      HAL_Pin_Mode(_pin, OUTPUT);
    }

    inline void pinModeFastInput(void){
      // This could probably be speed up by digging a little deeper past
      // the HAL_Pin_Mode function.
      HAL_Pin_Mode(_pin, INPUT);
    }

    inline uint8_t digitalReadFast(void){
      // This could probably be speed up by digging a little deeper past
      // the HAL_GPIO_Read function.
      return HAL_GPIO_Read(_pin);
    }

Not sure if this is the best way to do this, but it seems to work for me.