+/*
+ * Copyright (c) 2008, Titanium Mirror, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * - Neither the name of the Technische Universität Berlin nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * scp1000 implementation.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+#include "Scp1000.h"
+
+generic module Scp1000P() @safe()
+{
+ provides interface ReadRef<scp1000_t>;
+ uses {
+ interface GeneralIO as CSn;
+ interface GeneralIO as PD;
+ interface GeneralIO as DRDY;
+ interface GpioInterrupt as IntDRDY;
+ interface Resource;
+ interface SpiByte;
+ interface Timer<TMilli>;
+ }
+}
+implementation
+{
+ enum {
+ /* Data register names for SCP1000 */
+ OPERATION = 3,
+ STATUS = 7,
+ DATARD8 = 0x1f,
+ DATARD16 = 0x20,
+ TEMPOUT = 0x21,
+
+ /* Timer states */
+ TIMER_NONE = 0,
+ TIMER_POWER,
+ TIMER_CHECK,
+ TIMER_DRDY
+ };
+
+ scp1000_t* m_ptr;
+ uint8_t tstate;
+ uint8_t counter;
+
+ inline uint8_t addrOut(uint8_t addr, bool write)
+ {
+ return (addr << 2) + (write ? 2 : 0);
+ }
+
+ uint8_t readByte(uint8_t addr)
+ {
+ uint8_t data;
+
+ call CSn.clr();
+ call SpiByte.write(addrOut(addr, FALSE));
+ data = call SpiByte.write(0);
+ call CSn.set();
+ return data;
+ }
+
+ void writeByte(uint8_t addr, uint8_t data)
+ {
+ call CSn.clr();
+ call SpiByte.write(addrOut(addr, TRUE));
+ call SpiByte.write(data);
+ call CSn.set();
+ }
+
+ uint16_t readWord(uint8_t addr)
+ {
+ uint16_t data;
+
+ call CSn.clr();
+ call SpiByte.write(addrOut(addr, FALSE));
+ data = (call SpiByte.write(0) << 8);
+ data += call SpiByte.write(0);
+ call CSn.set();
+ return data;
+ }
+
+ void signalDone(error_t error)
+ {
+ scp1000_t* tmp = m_ptr;
+
+ call PD.set();
+ call Resource.release();
+ m_ptr = 0;
+ signal ReadRef.readDone(error, tmp);
+ }
+
+ command error_t ReadRef.read(scp1000_t* ptr)
+ {
+ if (m_ptr || !ptr)
+ return FAIL;
+ m_ptr = ptr;
+ call Resource.request();
+ return SUCCESS;
+ }
+
+ event void Resource.granted()
+ {
+ /* Begin powerup sequence */
+ call PD.clr();
+ tstate = TIMER_POWER;
+ call Timer.startOneShot(60);
+ }
+
+ task void check()
+ {
+ /* Abort if we've waited too long for ready state */
+ if (counter++ == 6) {
+ signalDone(FAIL);
+ return;
+ }
+
+ /* Poll to see if the sensor is ready */
+ if (readByte(STATUS) & 0x01) {
+ tstate = TIMER_CHECK;
+ call Timer.startOneShot(10);
+ return;
+ }
+
+ /* Verify EEPROM was loaded without error */
+ if (!(readByte(DATARD8) & 0x01)) {
+ signalDone(FAIL);
+ return;
+ }
+
+ /* We could use the low noise configuration by also doing:
+ * writeByte(0x2d, 0x03);
+ * wait_ms(100);
+ * writeByte(OPERATION, 0);
+ * wait_ms(10);
+ * writeByte(OPERATION, 0x0a); -- 0x0a is high resolution mode
+ * wait_ms(100);
+ */
+
+ /* Initiate a reading. DRDY will assert when done, ~ 500ms from now */
+ tstate = TIMER_DRDY;
+ call Timer.startOneShot(1024);
+ call IntDRDY.enableRisingEdge();
+ writeByte(OPERATION, 0x0c);
+ }
+
+ task void results();
+
+ async event void IntDRDY.fired()
+ {
+ call IntDRDY.disable();
+ post results();
+ }
+
+ task void results()
+ {
+ call Timer.stop();
+ m_ptr->temp = readWord(TEMPOUT); /* 20ths of a degree C */
+ m_ptr->pressure = (uint32_t)readByte(DATARD8) << 16;
+ m_ptr->pressure += readWord(DATARD16); /* 4ths of a Pa? */
+ signalDone(SUCCESS);
+ return;
+ }
+
+ event void Timer.fired()
+ {
+ uint8_t tmp = tstate;
+
+ tstate = TIMER_NONE;
+ switch (tmp) {
+ case TIMER_POWER:
+ counter = 0;
+ post check();
+ break;
+ case TIMER_CHECK:
+ post check();
+ break;
+ case TIMER_DRDY:
+ signalDone(FAIL);
+ break;
+ default:
+ }
+ }
+}