333 lines
10 KiB
C
333 lines
10 KiB
C
/*
|
|
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
|
|
*
|
|
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
* This file is designed to be parsed during the build process
|
|
*
|
|
* JavaScript OneWire Functions
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
#include "jswrap_onewire.h"
|
|
#include "jsdevices.h"
|
|
#include "jsinteractive.h"
|
|
|
|
/*JSON{ "type":"class",
|
|
"class" : "OneWire",
|
|
"description" : ["This class provides a software-defined OneWire master. It is designed to be similar to Arduino's OneWire library." ]
|
|
}*/
|
|
|
|
static inline Pin onewire_getpin(JsVar *parent) {
|
|
return jshGetPinFromVarAndUnLock(jsvObjectGetChild(parent, "pin", 0));
|
|
}
|
|
|
|
|
|
/** Reset one-wire, return true if a device was present */
|
|
static bool NO_INLINE OneWireReset(Pin pin) {
|
|
jshPinSetState(pin, JSHPINSTATE_GPIO_OUT_OPENDRAIN);
|
|
//jshInterruptOff();
|
|
jshPinSetValue(pin, 0);
|
|
jshDelayMicroseconds(500);
|
|
jshPinSetValue(pin, 1);
|
|
jshDelayMicroseconds(80);
|
|
bool hasDevice = !jshPinGetValue(pin);
|
|
//jshInterruptOn();
|
|
jshDelayMicroseconds(420);
|
|
return hasDevice;
|
|
}
|
|
|
|
/** Write 'bits' bits, and return what was read (to read, you must send all 1s) */
|
|
static JsVarInt NO_INLINE OneWireRead(Pin pin, int bits) {
|
|
jshPinSetState(pin, JSHPINSTATE_GPIO_OUT_OPENDRAIN);
|
|
JsVarInt result = 0;
|
|
JsVarInt mask = 1;
|
|
while (bits-- > 0) {
|
|
jshInterruptOff();
|
|
jshPinSetValue(pin, 0);
|
|
jshDelayMicroseconds(3);
|
|
jshPinSetValue(pin, 1);
|
|
jshDelayMicroseconds(10); // leave time to let it rise
|
|
if (jshPinGetValue(pin))
|
|
result = result | mask;
|
|
jshInterruptOn();
|
|
jshDelayMicroseconds(53);
|
|
mask = mask << 1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Write 'bits' bits, and return what was read (to read, you must send all 1s) */
|
|
static void NO_INLINE OneWireWrite(Pin pin, int bits, JsVarInt data) {
|
|
jshPinSetState(pin, JSHPINSTATE_GPIO_OUT_OPENDRAIN);
|
|
JsVarInt mask = 1;
|
|
while (bits-- > 0) {
|
|
if (data & mask) { // short pulse
|
|
jshInterruptOff();
|
|
jshPinSetValue(pin, 0);
|
|
jshDelayMicroseconds(10);
|
|
jshPinSetValue(pin, 1);
|
|
jshInterruptOn();
|
|
jshDelayMicroseconds(55);
|
|
} else { // long pulse
|
|
jshInterruptOff();
|
|
jshPinSetValue(pin, 0);
|
|
jshDelayMicroseconds(65);
|
|
jshPinSetValue(pin, 1);
|
|
jshInterruptOn();
|
|
jshDelayMicroseconds(5);
|
|
}
|
|
mask = mask << 1;
|
|
}
|
|
}
|
|
|
|
/*JSON{ "type":"constructor", "class": "OneWire", "name": "OneWire",
|
|
"description" : "Create a software OneWire implementation on the given pin",
|
|
"generate" : "jswrap_onewire_constructor",
|
|
"params" : [ [ "pin", "pin", "The pin to implement OneWire on" ] ],
|
|
"return" : [ "JsVar", "A OneWire object" ]
|
|
|
|
}*/
|
|
JsVar *jswrap_onewire_constructor(Pin pin) {
|
|
JsVar *ow = jspNewObject(jsiGetParser(), 0, "OneWire");
|
|
if (ow) {
|
|
JsVar *pinVar = jsvFindChildFromString(ow, "pin", true);
|
|
if (pinVar) {
|
|
JsVar *pv = jsvNewFromPin(pin);
|
|
jsvSetValueOfName(pinVar, pv);
|
|
jsvUnLock(pv);
|
|
}
|
|
jsvUnLock(pinVar);
|
|
}
|
|
return ow;
|
|
}
|
|
|
|
|
|
/*JSON{ "type":"method", "class": "OneWire", "name" : "reset",
|
|
"description" : "Perform a reset cycle",
|
|
"generate" : "jswrap_onewire_reset",
|
|
"return" : [ "bool", "True is a device was present (it held the bus low)" ]
|
|
}*/
|
|
bool jswrap_onewire_reset(JsVar *parent) {
|
|
Pin pin = onewire_getpin(parent);
|
|
if (!jshIsPinValid(pin)) return 0;
|
|
return OneWireReset(pin);
|
|
}
|
|
|
|
/*JSON{ "type":"method", "class": "OneWire", "name" : "select",
|
|
"description" : "Select a ROM - reset needs to be done first",
|
|
"generate" : "jswrap_onewire_select",
|
|
"params" : [ [ "rom", "int", "The rom to select" ] ]
|
|
}*/
|
|
void jswrap_onewire_select(JsVar *parent, JsVarInt rom) {
|
|
Pin pin = onewire_getpin(parent);
|
|
if (!jshIsPinValid(pin)) return;
|
|
OneWireWrite(pin, 8, 0x55);
|
|
OneWireWrite(pin, 64, rom);
|
|
}
|
|
|
|
/*JSON{ "type":"method", "class": "OneWire", "name" : "skip",
|
|
"description" : "Skip a ROM",
|
|
"generate" : "jswrap_onewire_skip"
|
|
}*/
|
|
void jswrap_onewire_skip(JsVar *parent) {
|
|
Pin pin = onewire_getpin(parent);
|
|
if (!jshIsPinValid(pin)) return;
|
|
OneWireWrite(pin, 8, 0xCC);
|
|
}
|
|
|
|
/*JSON{ "type":"method", "class": "OneWire", "name" : "write",
|
|
"description" : "Write a byte",
|
|
"generate" : "jswrap_onewire_write",
|
|
"params" : [ [ "data", "int", "A byte to write" ],
|
|
[ "power", "bool", "Whether to leave power on after write (default is false)" ] ]
|
|
}*/
|
|
void jswrap_onewire_write(JsVar *parent, int data, bool leavePowerOn) {
|
|
Pin pin = onewire_getpin(parent);
|
|
if (!jshIsPinValid(pin)) return;
|
|
OneWireWrite(pin, 8, data);
|
|
|
|
if (!leavePowerOn) {
|
|
jshPinSetState(pin, JSHPINSTATE_GPIO_IN);
|
|
jshPinSetValue(pin, 0);
|
|
}
|
|
}
|
|
|
|
/*JSON{ "type":"method", "class": "OneWire", "name" : "read",
|
|
"description" : "Read a byte",
|
|
"generate" : "jswrap_onewire_read",
|
|
"return" : [ "int", "The byte that was read" ]
|
|
}*/
|
|
JsVarInt jswrap_onewire_read(JsVar *parent) {
|
|
Pin pin = onewire_getpin(parent);
|
|
if (!jshIsPinValid(pin)) return -1;
|
|
return OneWireRead(pin, 8);
|
|
}
|
|
|
|
|
|
/*JSON{ "type":"method", "class": "OneWire", "name" : "search",
|
|
"description" : "Search for devices",
|
|
"generate" : "jswrap_onewire_search",
|
|
"return" : [ "JsVar", "An array of devices that were found" ]
|
|
}*/
|
|
JsVar *jswrap_onewire_search(JsVar *parent) {
|
|
// search - code from http://www.maximintegrated.com/app-notes/index.mvp/id/187
|
|
Pin pin = onewire_getpin(parent);
|
|
if (!jshIsPinValid(pin)) return 0;
|
|
|
|
JsVar *array = jsvNewWithFlags(JSV_ARRAY);
|
|
if (!array) return 0;
|
|
|
|
// global search state
|
|
unsigned char ROM_NO[8];
|
|
int LastDiscrepancy;
|
|
int LastFamilyDiscrepancy;
|
|
int LastDeviceFlag;
|
|
|
|
// reset the search state
|
|
LastDiscrepancy = 0;
|
|
LastDeviceFlag = FALSE;
|
|
LastFamilyDiscrepancy = 0;
|
|
|
|
int search_result = true;
|
|
|
|
while (search_result) {
|
|
|
|
int id_bit_number;
|
|
int last_zero, rom_byte_number;
|
|
unsigned char id_bit, cmp_id_bit;
|
|
unsigned char rom_byte_mask, search_direction;
|
|
|
|
// initialize for search
|
|
id_bit_number = 1;
|
|
last_zero = 0;
|
|
rom_byte_number = 0;
|
|
rom_byte_mask = 1;
|
|
search_result = 0;
|
|
|
|
// if the last call was not the last one
|
|
if (!LastDeviceFlag)
|
|
{
|
|
// 1-Wire reset
|
|
if (!OneWireReset(pin))
|
|
{
|
|
// reset the search
|
|
LastDiscrepancy = 0;
|
|
LastDeviceFlag = FALSE;
|
|
LastFamilyDiscrepancy = 0;
|
|
return array;
|
|
}
|
|
|
|
// issue the search command
|
|
OneWireWrite(pin, 8, 0xF0);
|
|
|
|
// loop to do the search
|
|
do
|
|
{
|
|
// read a bit and its complement
|
|
id_bit = (unsigned char)OneWireRead(pin, 1);
|
|
cmp_id_bit = (unsigned char)OneWireRead(pin, 1);
|
|
|
|
// check for no devices on 1-wire
|
|
if ((id_bit == 1) && (cmp_id_bit == 1))
|
|
break;
|
|
else
|
|
{
|
|
// all devices coupled have 0 or 1
|
|
if (id_bit != cmp_id_bit)
|
|
search_direction = id_bit; // bit write value for search
|
|
else
|
|
{
|
|
// if this discrepancy if before the Last Discrepancy
|
|
// on a previous next then pick the same as last time
|
|
if (id_bit_number < LastDiscrepancy)
|
|
search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0);
|
|
else
|
|
// if equal to last pick 1, if not then pick 0
|
|
search_direction = (id_bit_number == LastDiscrepancy);
|
|
|
|
// if 0 was picked then record its position in LastZero
|
|
if (search_direction == 0)
|
|
{
|
|
last_zero = id_bit_number;
|
|
|
|
// check for Last discrepancy in family
|
|
if (last_zero < 9)
|
|
LastFamilyDiscrepancy = last_zero;
|
|
}
|
|
}
|
|
|
|
// set or clear the bit in the ROM byte rom_byte_number
|
|
// with mask rom_byte_mask
|
|
if (search_direction == 1)
|
|
ROM_NO[rom_byte_number] |= rom_byte_mask;
|
|
else
|
|
ROM_NO[rom_byte_number] &= (unsigned char)~rom_byte_mask;
|
|
|
|
// serial number search direction write bit
|
|
OneWireWrite(pin, 1, search_direction);
|
|
|
|
// increment the byte counter id_bit_number
|
|
// and shift the mask rom_byte_mask
|
|
id_bit_number++;
|
|
rom_byte_mask = (unsigned char)(rom_byte_mask << 1);
|
|
|
|
// if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
|
|
if (rom_byte_mask == 0)
|
|
{
|
|
rom_byte_number++;
|
|
rom_byte_mask = 1;
|
|
}
|
|
}
|
|
}
|
|
while(rom_byte_number < 8); // loop until through all ROM bytes 0-7
|
|
|
|
// if the search was successful then
|
|
if (!((id_bit_number < 65)))
|
|
{
|
|
// search successful so set LastDiscrepancy,LastDeviceFlag,search_result
|
|
LastDiscrepancy = last_zero;
|
|
|
|
// check for last device
|
|
if (LastDiscrepancy == 0)
|
|
LastDeviceFlag = TRUE;
|
|
|
|
search_result = TRUE;
|
|
}
|
|
}
|
|
|
|
// if no device found then reset counters so next 'search' will be like a first
|
|
if (!search_result || !ROM_NO[0])
|
|
{
|
|
LastDiscrepancy = 0;
|
|
LastDeviceFlag = FALSE;
|
|
LastFamilyDiscrepancy = 0;
|
|
search_result = FALSE;
|
|
}
|
|
|
|
if (search_result) {
|
|
JsVar *val = jsvNewFromInteger(
|
|
(((JsVarInt)ROM_NO[7])<<(7*8)) |
|
|
(((JsVarInt)ROM_NO[6])<<(6*8)) |
|
|
(((JsVarInt)ROM_NO[5])<<(5*8)) |
|
|
(((JsVarInt)ROM_NO[4])<<(4*8)) |
|
|
(((JsVarInt)ROM_NO[3])<<(3*8)) |
|
|
(((JsVarInt)ROM_NO[2])<<(2*8)) |
|
|
(((JsVarInt)ROM_NO[1])<<(1*8)) |
|
|
((JsVarInt)ROM_NO[0])
|
|
);
|
|
if (val) jsvArrayPushAndUnLock(array, val);
|
|
}
|
|
|
|
NOT_USED(LastFamilyDiscrepancy);
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|