From 063bb12bd0b95c85ac2cb21874a8f4f1abcda8e4 Mon Sep 17 00:00:00 2001 From: Matthias Kovatsch Date: Mon, 19 Mar 2012 18:37:25 +0100 Subject: [PATCH 01/14] Fixed debug/test prints. --- examples/er-rest-example/plugtest-server.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/er-rest-example/plugtest-server.c b/examples/er-rest-example/plugtest-server.c index 79a07be6d..2994cb6f7 100644 --- a/examples/er-rest-example/plugtest-server.c +++ b/examples/er-rest-example/plugtest-server.c @@ -99,14 +99,13 @@ test_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_ uint8_t method = REST.get_method_type(request); - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - REST.set_response_payload(response, buffer, snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "Type: %u\nCode: %u\nMID: %u", coap_req->type, coap_req->code, coap_req->mid)); - PRINTF("/test "); if (method & METHOD_GET) { PRINTF("GET "); /* Code 2.05 CONTENT is default. */ + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + REST.set_response_payload(response, buffer, snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "Type: %u\nCode: %u\nMID: %u", coap_req->type, coap_req->code, coap_req->mid)); } else if (method & METHOD_POST) { @@ -215,7 +214,7 @@ separate_handler(void* request, void* response, uint8_t *buffer, uint16_t prefer } else { - PRINTF("ACKED "); + PRINTF("STORED "); separate_active = 1; /* Take over and skip response by engine. */ @@ -237,7 +236,7 @@ separate_periodic_handler(resource_t *resource) coap_transaction_t *transaction = NULL; if ( (transaction = coap_new_transaction(separate_store->request_metadata.mid, &separate_store->request_metadata.addr, separate_store->request_metadata.port)) ) { - PRINTF("RESPONSE"); + PRINTF("RESPONSE (%s %u)\n", separate_store->request_metadata.type==COAP_TYPE_CON?"CON":"NON", separate_store->request_metadata.mid); coap_packet_t response[1]; /* This way the packet can be treated as pointer as usual. */ @@ -262,7 +261,7 @@ separate_periodic_handler(resource_t *resource) return 1; } else { - PRINTF("ERROR (transaction)"); + PRINTF("ERROR (transaction)\n"); } } /* if (separate_active) */ From 090d77c5a20d71556836aa3916857f4ac224bdc2 Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind Date: Wed, 21 Mar 2012 16:56:04 +0100 Subject: [PATCH 02/14] added jsyntaxpane library, to be used by MspCodeWatcher and the Contiki Test Editor plugins --- tools/cooja/build.xml | 12 +++++++++++- tools/cooja/lib/JSYNTAXPANE_LICENSE | 4 ++++ tools/cooja/lib/jsyntaxpane.jar | Bin 0 -> 397327 bytes 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tools/cooja/lib/JSYNTAXPANE_LICENSE create mode 100644 tools/cooja/lib/jsyntaxpane.jar diff --git a/tools/cooja/build.xml b/tools/cooja/build.xml index 3ad01fb00..ac98ecce7 100644 --- a/tools/cooja/build.xml +++ b/tools/cooja/build.xml @@ -50,6 +50,7 @@ The COOJA Simulator + @@ -62,6 +63,7 @@ The COOJA Simulator + @@ -74,6 +76,7 @@ The COOJA Simulator + @@ -104,6 +107,7 @@ The COOJA Simulator + @@ -118,6 +122,7 @@ The COOJA Simulator + @@ -131,6 +136,7 @@ The COOJA Simulator + @@ -144,6 +150,7 @@ The COOJA Simulator + @@ -156,6 +163,7 @@ The COOJA Simulator + @@ -170,6 +178,7 @@ The COOJA Simulator + @@ -190,6 +199,7 @@ The COOJA Simulator + @@ -214,7 +224,7 @@ The COOJA Simulator - + diff --git a/tools/cooja/lib/JSYNTAXPANE_LICENSE b/tools/cooja/lib/JSYNTAXPANE_LICENSE new file mode 100644 index 000000000..01f0c7c35 --- /dev/null +++ b/tools/cooja/lib/JSYNTAXPANE_LICENSE @@ -0,0 +1,4 @@ +Downloaded from http://code.google.com/p/jsyntaxpane/. + +Apache License 2.0: +http://www.apache.org/licenses/LICENSE-2.0 diff --git a/tools/cooja/lib/jsyntaxpane.jar b/tools/cooja/lib/jsyntaxpane.jar new file mode 100644 index 0000000000000000000000000000000000000000..8bca1f6ec3179cc6f4ec903e59f0a90e0f405580 GIT binary patch literal 397327 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%0A-G=yaHB;pZ^ak2AwS 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ʅf&_UB0EP747!ma-A1TOche~}F(GNYMX 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�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 Date: Wed, 21 Mar 2012 16:56:32 +0100 Subject: [PATCH 03/14] simplified usage of Watchpoint and WatchpointMote interfaces --- .../cooja/java/se/sics/cooja/Watchpoint.java | 28 ++++++++-------- .../java/se/sics/cooja/WatchpointMote.java | 32 +++++++++++-------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/tools/cooja/java/se/sics/cooja/Watchpoint.java b/tools/cooja/java/se/sics/cooja/Watchpoint.java index f266d5194..c65182a94 100644 --- a/tools/cooja/java/se/sics/cooja/Watchpoint.java +++ b/tools/cooja/java/se/sics/cooja/Watchpoint.java @@ -32,24 +32,26 @@ package se.sics.cooja; import java.awt.Color; +import java.io.File; /** * @author Fredrik Osterlind */ public interface Watchpoint { - - /** - * @return Short watchpoint description - */ - public String getDescription(); - /** - * @return Mote - */ - public Mote getMote(); - - /** - * @return Color - */ + public WatchpointMote getMote(); + public Color getColor(); + public void setColor(Color newColor); + + public String getDescription(); + public void setUserMessage(String msg); + public String getUserMessage(); + + public File getCodeFile(); + public int getLineNumber(); + public int getExecutableAddress(); + + public void setStopsSimulation(boolean b); + public boolean stopsSimulation(); } diff --git a/tools/cooja/java/se/sics/cooja/WatchpointMote.java b/tools/cooja/java/se/sics/cooja/WatchpointMote.java index 970c96a40..ba5886213 100644 --- a/tools/cooja/java/se/sics/cooja/WatchpointMote.java +++ b/tools/cooja/java/se/sics/cooja/WatchpointMote.java @@ -31,12 +31,17 @@ package se.sics.cooja; -import java.awt.event.ActionListener; +import java.io.File; /** * @author Fredrik Osterlind */ -public interface WatchpointMote { +public interface WatchpointMote extends Mote { + + public interface WatchpointListener { + public void watchpointTriggered(Watchpoint watchpoint); + public void watchpointsChanged(); + } /** * Adds a breakpoint listener. @@ -44,28 +49,27 @@ public interface WatchpointMote { * * @param listener Action listener */ - public void addWatchpointListener(ActionListener listener); + public void addWatchpointListener(WatchpointListener listener); /** * Removes previously registered listener. - * + * * @param listener Listeners */ - public void removeWatchpointListener(ActionListener listener); + public void removeWatchpointListener(WatchpointListener listener); /** * @return All registered listeners */ - public ActionListener[] getWatchpointListeners(); + public WatchpointListener[] getWatchpointListeners(); - /** - * @return Last triggered watchpoint - */ - public Watchpoint getLastWatchpoint(); + public Watchpoint addBreakpoint(File codeFile, int lineNr, int address); + public void removeBreakpoint(Watchpoint watchpoint); + public Watchpoint[] getBreakpoints(); - /** - * @return Mote - */ - public Mote getMote(); + public boolean breakpointExists(int address); + public boolean breakpointExists(File file, int lineNr); + + public Integer getExecutableAddressOf(File file, int lineNr); } From 2e583c733ebd3402277eebe900e313b4881558e2 Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind Date: Wed, 21 Mar 2012 16:57:04 +0100 Subject: [PATCH 04/14] mspsim motes now implements new WatchpointMote interface, simplified code --- .../src/se/sics/cooja/mspmote/ESBMote.java | 4 +- .../src/se/sics/cooja/mspmote/MspMote.java | 230 ++++++++++---- .../cooja/mspmote/plugins/MspBreakpoint.java | 200 +++++++------ .../plugins/MspBreakpointContainer.java | 280 ------------------ 4 files changed, 274 insertions(+), 440 deletions(-) delete mode 100644 tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspBreakpointContainer.java diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/ESBMote.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/ESBMote.java index c3e5475e5..8c4fb6608 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/ESBMote.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/ESBMote.java @@ -32,10 +32,10 @@ package se.sics.cooja.mspmote; import java.io.File; + import org.apache.log4j.Logger; -import se.sics.cooja.MoteInterfaceHandler; + import se.sics.cooja.Simulation; -import se.sics.cooja.interfaces.*; import se.sics.mspsim.platform.esb.ESBNode; /** diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/MspMote.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/MspMote.java index d2d2e154f..ecc77fafa 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/MspMote.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/MspMote.java @@ -31,12 +31,13 @@ package se.sics.cooja.mspmote; -import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; +import java.util.Enumeration; +import java.util.Hashtable; import java.util.Observable; import org.apache.log4j.Logger; @@ -56,14 +57,12 @@ import se.sics.cooja.interfaces.IPAddress; import se.sics.cooja.motes.AbstractEmulatedMote; import se.sics.cooja.mspmote.interfaces.MspSerial; import se.sics.cooja.mspmote.plugins.CodeVisualizerSkin; -import se.sics.cooja.mspmote.plugins.MspBreakpointContainer; -import se.sics.cooja.plugins.BufferListener.BufferAccess; +import se.sics.cooja.mspmote.plugins.MspBreakpoint; import se.sics.cooja.plugins.Visualizer; import se.sics.mspsim.cli.CommandContext; import se.sics.mspsim.cli.CommandHandler; import se.sics.mspsim.cli.LineListener; import se.sics.mspsim.cli.LineOutputStream; -import se.sics.mspsim.core.CPUMonitor; import se.sics.mspsim.core.EmulationException; import se.sics.mspsim.core.MSP430; import se.sics.mspsim.core.MSP430Constants; @@ -95,7 +94,7 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc private MspMoteMemory myMemory = null; private MoteInterfaceHandler myMoteInterfaceHandler = null; public ComponentRegistry registry = null; - + /* Stack monitoring variables */ private boolean stopNextInstruction = false; private boolean monitorStackUsage = false; @@ -103,15 +102,11 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc private int heapStartAddress; private StackOverflowObservable stackOverflowObservable = new StackOverflowObservable(); - private MspBreakpointContainer breakpointsContainer; - public MspMote() { myMoteType = null; myCpu = null; myMemory = null; myMoteInterfaceHandler = null; - - /* Scheduled from setConfigXML */ } public MspMote(MspMoteType moteType, Simulation simulation) { @@ -121,7 +116,7 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc /* Schedule us immediately */ requestImmediateWakeup(); } - + protected void initMote() { if (myMoteType != null) { initEmulator(myMoteType.getContikiFirmwareFile()); @@ -130,9 +125,8 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc /* TODO Setup COOJA-specific window manager */ registry.registerComponent("windowManager", new JFrameWindowManager()); - /* Create watchpoint container */ try { - breakpointsContainer = new MspBreakpointContainer(this, ((MspMoteType)getType()).getFirmwareDebugInfo()); + debuggingInfo = ((MspMoteType)getType()).getFirmwareDebugInfo(); } catch (IOException e) { throw (RuntimeException) new RuntimeException("Error: " + e.getMessage()).initCause(e); } @@ -300,10 +294,9 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc if (stopNextInstruction) { stopNextInstruction = false; - /*sendCLICommandAndPrint("trace 1000");*/ /* TODO Enable */ scheduleNextWakeup(t); throw new RuntimeException("MSPSim requested simulation stop"); - } + } if (lastExecute < 0) { /* Always execute one microsecond the first time */ @@ -312,12 +305,12 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc if (t < lastExecute) { throw new RuntimeException("Bad event ordering: " + lastExecute + " < " + t); } - + /* Execute MSPSim-based mote */ /* TODO Try-catch overhead */ try { - nextExecute = - t + duration + + nextExecute = + t + duration + myCpu.stepMicros(t - lastExecute, duration); lastExecute = t; } catch (EmulationException e) { @@ -332,8 +325,12 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc } /*logger.debug(t + ": Schedule next wakeup at " + nextExecute);*/ scheduleNextWakeup(nextExecute); - - + + if (stopNextInstruction) { + stopNextInstruction = false; + throw new RuntimeException("MSPSim requested simulation stop"); + } + /* XXX TODO Reimplement stack monitoring using MSPSim internals */ /*if (monitorStackUsage) { int newStack = cpu.reg[MSP430.SP]; @@ -349,7 +346,7 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc } }*/ } - + public String getStackTrace() { return executeCLICommand("stacktrace"); } @@ -357,7 +354,7 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc public int executeCLICommand(String cmd, CommandContext context) { return commandHandler.executeCommand(cmd, context); } - + public String executeCLICommand(String cmd) { final StringBuilder sb = new StringBuilder(); LineListener ll = new LineListener() { @@ -369,14 +366,14 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc CommandContext c = new CommandContext(commandHandler, null, "", new String[0], 1, null); c.out = po; c.err = po; - + if (0 != executeCLICommand(cmd, c)) { sb.append("\nWarning: command failed"); } - + return sb.toString(); } - + public int getCPUFrequency() { return myCpu.getDCOFrequency(); } @@ -384,16 +381,15 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc public int getID() { return getInterfaces().getMoteID().getMoteID(); } - + public boolean setConfigXML(Simulation simulation, Collection configXML, boolean visAvailable) { setSimulation(simulation); if (myMoteInterfaceHandler == null) { myMoteInterfaceHandler = createMoteInterfaceHandler(); } - /* Create watchpoint container */ try { - breakpointsContainer = new MspBreakpointContainer(this, ((MspMoteType)getType()).getFirmwareDebugInfo()); + debuggingInfo = ((MspMoteType)getType()).getFirmwareDebugInfo(); } catch (IOException e) { throw (RuntimeException) new RuntimeException("Error: " + e.getMessage()).initCause(e); } @@ -404,7 +400,7 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc if (name.equals("motetype_identifier")) { /* Ignored: handled by simulation */ } else if ("breakpoints".equals(element.getName())) { - breakpointsContainer.setConfigXML(element.getChildren(), visAvailable); + setWatchpointConfigXML(element.getChildren(), visAvailable); } else if (name.equals("interface_config")) { String intfClass = element.getText().trim(); if (intfClass.equals("se.sics.cooja.mspmote.interfaces.MspIPAddress")) { @@ -440,7 +436,7 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc /* Breakpoints */ element = new Element("breakpoints"); - element.addContent(breakpointsContainer.getConfigXML()); + element.addContent(getWatchpointConfigXML()); config.add(element); // Mote interfaces @@ -458,41 +454,15 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc return config; } - - /* Watchpoints: Forward to breakpoint container */ - public void addWatchpointListener(ActionListener listener) { - breakpointsContainer.addWatchpointListener(listener); - } - - public Watchpoint getLastWatchpoint() { - return breakpointsContainer.getLastWatchpoint(); - } - - public Mote getMote() { - return breakpointsContainer.getMote(); - } - - public ActionListener[] getWatchpointListeners() { - return breakpointsContainer.getWatchpointListeners(); - } - - public void removeWatchpointListener(ActionListener listener) { - breakpointsContainer.removeWatchpointListener(listener); - } - - public MspBreakpointContainer getBreakpointsContainer() { - return breakpointsContainer; - } - public String getExecutionDetails() { return executeCLICommand("stacktrace"); } public String getPCString() { int pc = myCpu.getPC(); - ELF elf = (ELF)myCpu.getRegistry().getComponent(ELF.class); + ELF elf = myCpu.getRegistry().getComponent(ELF.class); DebugInfo di = elf.getDebugInfo(pc); - + /* Following code examples from MSPsim, DebugCommands.java */ if (di == null) { di = elf.getDebugInfo(pc + 1); @@ -510,7 +480,7 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc } } String name = mapEntry.getName(); - return file + ":?:" + name; + return file + ":?:" + name; } return String.format("*%02x", myCpu.reg[MSP430Constants.PC]); } catch (Exception e) { @@ -525,7 +495,7 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc /* strip path */ file = file.substring(file.lastIndexOf('/')+1, file.length()); } - + String function = di.getFunction(); function = function==null?"":function; if (function.contains(":")) { @@ -536,7 +506,147 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc function = "?"; } return file + ":" + lineNo + ":" + function; - + /*return executeCLICommand("line " + myCpu.getPC());*/ } + + + /* WatchpointMote */ + private ArrayList watchpointListeners = new ArrayList(); + private ArrayList watchpoints = new ArrayList(); + private Hashtable> debuggingInfo = null; + + public void addWatchpointListener(WatchpointListener listener) { + watchpointListeners.add(listener); + } + public void removeWatchpointListener(WatchpointListener listener) { + watchpointListeners.remove(listener); + } + public WatchpointListener[] getWatchpointListeners() { + return watchpointListeners.toArray(new WatchpointListener[0]); + } + + public Watchpoint addBreakpoint(File codeFile, int lineNr, int address) { + MspBreakpoint bp = new MspBreakpoint(this, address, codeFile, new Integer(lineNr)); + watchpoints.add(bp); + + for (WatchpointListener listener: watchpointListeners) { + listener.watchpointsChanged(); + } + return bp; + } + public void removeBreakpoint(Watchpoint watchpoint) { + ((MspBreakpoint)watchpoint).unregisterBreakpoint(); + watchpoints.remove(watchpoint); + + for (WatchpointListener listener: watchpointListeners) { + listener.watchpointsChanged(); + } + } + public Watchpoint[] getBreakpoints() { + return watchpoints.toArray(new Watchpoint[0]); + } + + public boolean breakpointExists(int address) { + if (address < 0) { + return false; + } + for (Watchpoint watchpoint: watchpoints) { + if (watchpoint.getExecutableAddress() == address) { + return true; + } + } + return false; + } + public boolean breakpointExists(File file, int lineNr) { + for (Watchpoint watchpoint: watchpoints) { + if (watchpoint.getCodeFile() == null) { + continue; + } + if (watchpoint.getCodeFile().compareTo(file) != 0) { + continue; + } + if (watchpoint.getLineNumber() != lineNr) { + continue; + } + return true; + } + return false; + } + + public Integer getExecutableAddressOf(File file, int lineNr) { + if (file == null || lineNr < 0 || debuggingInfo == null) { + return null; + } + + /* Match file */ + Hashtable lineTable = debuggingInfo.get(file); + if (lineTable == null) { + Enumeration fileEnum = debuggingInfo.keys(); + while (fileEnum.hasMoreElements()) { + File f = fileEnum.nextElement(); + if (f != null && f.getName().equals(file.getName())) { + lineTable = debuggingInfo.get(f); + break; + } + } + } + if (lineTable == null) { + return null; + } + + /* Match line number */ + Integer address = lineTable.get(lineNr); + if (address != null) { + Enumeration lineEnum = lineTable.keys(); + while (lineEnum.hasMoreElements()) { + Integer l = lineEnum.nextElement(); + if (l != null && l.intValue() == lineNr) { + /* Found line address */ + return lineTable.get(l); + } + } + } + + return null; + } + + public void signalBreakpointTrigger(MspBreakpoint b) { + if (b.stopsSimulation() && getSimulation().isRunning()) { + /* Stop simulation immediately */ + stopNextInstruction(); + } + + /* Notify listeners */ + WatchpointListener[] listeners = getWatchpointListeners(); + for (WatchpointListener listener: listeners) { + listener.watchpointTriggered(b); + } + } + + public Collection getWatchpointConfigXML() { + ArrayList config = new ArrayList(); + Element element; + + for (MspBreakpoint breakpoint: watchpoints) { + element = new Element("breakpoint"); + element.addContent(breakpoint.getConfigXML()); + config.add(element); + } + + return config; + } + public boolean setWatchpointConfigXML(Collection configXML, boolean visAvailable) { + for (Element element : configXML) { + if (element.getName().equals("breakpoint")) { + MspBreakpoint breakpoint = new MspBreakpoint(this); + if (!breakpoint.setConfigXML(element.getChildren(), visAvailable)) { + logger.warn("Could not restore breakpoint: " + breakpoint); + } else { + watchpoints.add(breakpoint); + } + } + } + return true; + } } diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspBreakpoint.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspBreakpoint.java index 7f2c18a81..e6d881e97 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspBreakpoint.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspBreakpoint.java @@ -34,129 +34,151 @@ package se.sics.cooja.mspmote.plugins; import java.awt.Color; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; -import java.util.Vector; import org.apache.log4j.Logger; import org.jdom.Element; import se.sics.cooja.Watchpoint; import se.sics.cooja.mspmote.MspMote; +import se.sics.cooja.util.StringUtils; import se.sics.mspsim.core.CPUMonitor; /** - * Breakpoint. - * Contains meta data such source code file and line number. + * Mspsim watchpoint. * * @author Fredrik Osterlind */ public class MspBreakpoint implements Watchpoint { private static Logger logger = Logger.getLogger(MspBreakpoint.class); - private MspBreakpointContainer breakpoints; private MspMote mspMote; + private int address = -1; /* Binary address */ + private File codeFile = null; /* Source code, may be null*/ + private int lineNr = -1; /* Source code line number, may be null */ + private CPUMonitor cpuMonitor = null; private boolean stopsSimulation = true; - private Integer address = null; /* Binary address */ - - private File codeFile = null; /* Source code, may be null*/ - private Integer lineNr = null; /* Source code line number, may be null */ - private String msg = null; private Color color = Color.BLACK; - public MspBreakpoint(MspBreakpointContainer breakpoints, MspMote mote) { - this.breakpoints = breakpoints; + private String contikiCode = null; + + public MspBreakpoint(MspMote mote) { this.mspMote = mote; + /* expects setConfigXML(..) */ } - public MspBreakpoint(MspBreakpointContainer breakpoints, MspMote mote, Integer address) { - this(breakpoints, mote); + public MspBreakpoint(MspMote mote, Integer address, File codeFile, Integer lineNr) { + this(mote); this.address = address; + this.codeFile = codeFile; + this.lineNr = lineNr; createMonitor(); } - public MspBreakpoint(MspBreakpointContainer breakpoints, MspMote mote, Integer address, File codeFile, Integer lineNr) { - this(breakpoints, mote, address); - this.codeFile = codeFile; - this.lineNr = lineNr; - } - - /** - * @return MSP mote - */ public MspMote getMote() { return mspMote; } - /** - * @return Executable address - */ - public Integer getExecutableAddress() { - return address; + public Color getColor() { + return color; } - - /** - * @return Source code file - */ + public void setColor(Color color) { + this.color = color; + } + + public String getDescription() { + String desc = ""; + if (codeFile != null) { + desc += codeFile.getPath() + ":" + lineNr + " (0x" + Integer.toHexString(address) + ")"; + } else if (address >= 0) { + desc += "0x" + Integer.toHexString(address); + } + if (msg != null) { + desc += "\n\n" + msg; + } + return desc; + } + public void setUserMessage(String msg) { + this.msg = msg; + } + public String getUserMessage() { + return msg; + } + public File getCodeFile() { return codeFile; } - - /** - * @return Source code file line number - */ - public Integer getLineNumber() { + public int getLineNumber() { return lineNr; } - - public boolean stopsSimulation() { - return stopsSimulation; + public int getExecutableAddress() { + return address; } public void setStopsSimulation(boolean stops) { stopsSimulation = stops; } + public boolean stopsSimulation() { + return stopsSimulation; + } private void createMonitor() { cpuMonitor = new CPUMonitor() { public void cpuAction(int type, int adr, int data) { - breakpoints.signalBreakpointTrigger(MspBreakpoint.this); + if (type != CPUMonitor.EXECUTE) { + return; + } + + mspMote.signalBreakpointTrigger(MspBreakpoint.this); } }; mspMote.getCPU().addWatchPoint(address, cpuMonitor); + + + /* Remember Contiki code, to verify it when reloaded */ + if (contikiCode == null) { + final String code = StringUtils.loadFromFile(codeFile); + if (code != null) { + String[] lines = code.split("\n"); + if (lineNr-1 < lines.length) { + contikiCode = lines[lineNr-1].trim(); + } + } + } + } - + public void unregisterBreakpoint() { mspMote.getCPU().removeWatchPoint(address, cpuMonitor); } public Collection getConfigXML() { - Vector config = new Vector(); + ArrayList config = new ArrayList(); Element element; - element = new Element("address"); - element.setText(address.toString()); - config.add(element); - element = new Element("stops"); element.setText("" + stopsSimulation); config.add(element); - if (codeFile != null) { - element = new Element("codefile"); - File file = mspMote.getSimulation().getGUI().createPortablePath(codeFile); - element.setText(file.getPath().replaceAll("\\\\", "/")); - config.add(element); - } + element = new Element("codefile"); + File file = mspMote.getSimulation().getGUI().createPortablePath(codeFile); + element.setText(file.getPath().replaceAll("\\\\", "/")); + config.add(element); - if (lineNr != null) { - element = new Element("line"); - element.setText(lineNr.toString()); + element = new Element("line"); + element.setText("" + lineNr); + config.add(element); + + if (contikiCode != null) { + element = new Element("contikicode"); + element.setText(contikiCode); config.add(element); } @@ -177,7 +199,7 @@ public class MspBreakpoint implements Watchpoint { public boolean setConfigXML(Collection configXML, boolean visAvailable) { /* Already knows mote and breakpoints */ - + for (Element element : configXML) { if (element.getName().equals("codefile")) { File file = new File(element.getText()); @@ -193,8 +215,23 @@ public class MspBreakpoint implements Watchpoint { } } else if (element.getName().equals("line")) { lineNr = Integer.parseInt(element.getText()); - } else if (element.getName().equals("address")) { - address = Integer.parseInt(element.getText()); + } else if (element.getName().equals("contikicode")) { + String lastContikiCode = element.getText().trim(); + + /* Verify that Contiki code did not change */ + final String code = StringUtils.loadFromFile(codeFile); + if (code != null) { + String[] lines = code.split("\n"); + if (lineNr-1 < lines.length) { + contikiCode = lines[lineNr-1].trim(); + } + } + + if (!lastContikiCode.equals(contikiCode)) { + logger.warn("Detected modified Contiki code at breakpoint: " + codeFile.getPath() + ":" + lineNr + "."); + logger.warn("From: '" + lastContikiCode + "'"); + logger.warn(" To: '" + contikiCode + "'"); + } } else if (element.getName().equals("msg")) { msg = element.getText(); } else if (element.getName().equals("color")) { @@ -204,51 +241,18 @@ public class MspBreakpoint implements Watchpoint { } } - if (address == null) { + /* Update executable address */ + address = mspMote.getExecutableAddressOf(codeFile, lineNr); + if (address < 0) { + logger.fatal("Could not restore breakpoint, did source code change?"); return false; } - - /* TODO Save source code line */ - - if (codeFile != null && lineNr != null) { - /* Update executable address */ - address = mspMote.getBreakpointsContainer().getExecutableAddressOf(codeFile, lineNr); - if (address == null) { - logger.fatal("Could not restore breakpoint, did source code change?"); - address = 0; - } - } - createMonitor(); + return true; } - public void setUserMessage(String msg) { - this.msg = msg; + public String toString() { + return getMote() + ": " + getDescription(); } - public String getUserMessage() { - return msg; - } - - public void setColor(Color color) { - this.color = color; - } - - public String getDescription() { - String desc = ""; - if (codeFile != null) { - desc += codeFile.getPath() + ":" + lineNr + " (0x" + Integer.toHexString(address.intValue()) + ")"; - } else if (address != null) { - desc += "0x" + Integer.toHexString(address.intValue()); - } - if (msg != null) { - desc += "\n\n" + msg; - } - return desc; - } - - public Color getColor() { - return color; - } - } diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspBreakpointContainer.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspBreakpointContainer.java deleted file mode 100644 index 27c721615..000000000 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspBreakpointContainer.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (c) 2009, Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. - * - * $Id: MspBreakpointContainer.java,v 1.3 2010/01/21 22:32:32 fros4943 Exp $ - */ - -package se.sics.cooja.mspmote.plugins; - -import java.awt.event.ActionListener; -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Vector; - -import org.apache.log4j.Logger; -import org.jdom.Element; - -import se.sics.cooja.WatchpointMote; -import se.sics.cooja.mspmote.MspMote; - -/** - * Breakpoint collection - * - * @author Fredrik Osterlind - */ -public class MspBreakpointContainer implements WatchpointMote { - private static Logger logger = Logger.getLogger(MspBreakpointContainer.class); - - private Hashtable> debuggingInfo = null; - private MspMote mspMote; - - private ArrayList breakpoints = new ArrayList(); - private ArrayList listeners = new ArrayList(); - private MspBreakpoint lastTriggeredBreakpoint = null; - - /** - * @param debuggingInfo Debugging information read from firmware file - * @param mote Mote - */ - public MspBreakpointContainer(MspMote mote, Hashtable> debuggingInfo) { - this.mspMote = mote; - this.debuggingInfo = debuggingInfo; - } - - /** - * Add breakpoint at given address. - * - * @param address Executable address - */ - public void addBreakpoint(Integer address) { - addBreakpoint((File) null, (Integer) null, address); - } - - /** - * Add breakpoint at given address with given meta data. - * - * @param codeFile Source code file - * @param lineNr Source code file line number - * @param address Executable address - * @return Added breakpoint - */ - public MspBreakpoint addBreakpoint(File codeFile, int lineNr, Integer address) { - MspBreakpoint bp = new MspBreakpoint(this, mspMote, address, codeFile, new Integer(lineNr)); - breakpoints.add(bp); - - /* Notify listeners */ - lastTriggeredBreakpoint = null; - for (ActionListener listener: listeners) { - listener.actionPerformed(null); - } - return bp; - } - - /** - * Remove breakpoint at given address. - * - * @param address Executable address - */ - public MspBreakpoint removeBreakpoint(Integer address) { - MspBreakpoint breakpointToRemove = null; - for (MspBreakpoint breakpoint: breakpoints) { - if (breakpoint.getExecutableAddress().intValue() == address.intValue()) { - breakpointToRemove = breakpoint; - break; - } - } - if (breakpointToRemove == null) { - return null; - } - - breakpointToRemove.unregisterBreakpoint(); - breakpoints.remove(breakpointToRemove); - - /* Notify listeners */ - lastTriggeredBreakpoint = null; - for (ActionListener listener: listeners) { - listener.actionPerformed(null); - } - return breakpointToRemove; - } - - /** - * Checks if a breakpoint exists at given address. - * - * @param address Executable address - * @return True if breakpoint exists, false otherwise - */ - public boolean breakpointExists(Integer address) { - if (address == null) { - return false; - } - - for (MspBreakpoint breakpoint: breakpoints) { - if (breakpoint.getExecutableAddress().intValue() == address.intValue()) { - return true; - } - } - return false; - } - - public boolean breakpointExists(File file, int lineNr) { - for (MspBreakpoint breakpoint: breakpoints) { - if (breakpoint.getCodeFile() == null) { - continue; - } - if (breakpoint.getCodeFile().compareTo(file) != 0) { - continue; - } - if (breakpoint.getLineNumber().intValue() != lineNr) { - continue; - } - return true; - } - return false; - } - - /** - * @return All breakpoints - */ - public MspBreakpoint[] getBreakpoints() { - return breakpoints.toArray(new MspBreakpoint[0]); - } - - public int getBreakpointsCount() { - return breakpoints.size(); - } - - public void addWatchpointListener(ActionListener listener) { - listeners.add(listener); - } - - public void removeWatchpointListener(ActionListener listener) { - listeners.remove(listener); - } - - public ActionListener[] getWatchpointListeners() { - return listeners.toArray(new ActionListener[0]); - } - - protected void signalBreakpointTrigger(MspBreakpoint b) { - if (b.stopsSimulation() && mspMote.getSimulation().isRunning()) { - /* Stop simulation immediately */ - mspMote.stopNextInstruction(); - } - - /* Notify listeners */ - lastTriggeredBreakpoint = b; - ActionListener[] arr = getWatchpointListeners(); - for (ActionListener listener: arr) { - listener.actionPerformed(null); - } - } - - public MspMote getMote() { - return mspMote; - } - - public MspBreakpoint getLastWatchpoint() { - return lastTriggeredBreakpoint; - } - - /** - * Tries to calculate the executable address of given file. - * Using debugging information from firmware file, - * - * @param file Source code file - * @param lineNr Source code file line number - * @return Executable address or null if not found - */ - public Integer getExecutableAddressOf(File file, int lineNr) { - if (file == null || lineNr < 0 || debuggingInfo == null) { - return null; - } - - /* Match file */ - Hashtable lineTable = debuggingInfo.get(file); - if (lineTable == null) { - Enumeration fileEnum = debuggingInfo.keys(); - while (fileEnum.hasMoreElements()) { - File f = fileEnum.nextElement(); - if (f != null && f.getName().equals(file.getName())) { - lineTable = debuggingInfo.get(f); - break; - } - } - } - if (lineTable == null) { - return null; - } - - /* Match line number */ - Integer address = lineTable.get(lineNr); - if (address != null) { - Enumeration lineEnum = lineTable.keys(); - while (lineEnum.hasMoreElements()) { - Integer l = lineEnum.nextElement(); - if (l != null && l.intValue() == lineNr) { - /* Found line address */ - return lineTable.get(l); - } - } - } - - return null; - } - - public Collection getConfigXML() { - Vector config = new Vector(); - Element element; - - for (MspBreakpoint breakpoint: breakpoints) { - element = new Element("breakpoint"); - element.addContent(breakpoint.getConfigXML()); - config.add(element); - } - - return config; - } - - public boolean setConfigXML(Collection configXML, boolean visAvailable) { - for (Element element : configXML) { - if (element.getName().equals("breakpoint")) { - MspBreakpoint breakpoint = new MspBreakpoint(this, mspMote); - if (!breakpoint.setConfigXML(element.getChildren(), visAvailable)) { - logger.warn("Could not restore breakpoint: " + breakpoint); - } else { - breakpoints.add(breakpoint); - } - } - } - return true; - } -} From 7cfa8e28d3cbf0785b4774cb08a4e900ca81bdef Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind Date: Wed, 21 Mar 2012 16:58:26 +0100 Subject: [PATCH 05/14] reworked MspCodeWatcher plugin: using jsyntaxpane to display Contiki code, using tabs instead of splitpanes, easier to configure watch-/breakpoints, lots of bug fixes and minor improvements --- .../cooja/mspmote/plugins/BreakpointsUI.java | 330 ++++----- .../se/sics/cooja/mspmote/plugins/CodeUI.java | 663 ++++++------------ .../cooja/mspmote/plugins/MspCodeWatcher.java | 263 +++---- .../sics/cooja/util/JSyntaxAddBreakpoint.java | 74 ++ .../cooja/util/JSyntaxRemoveBreakpoint.java | 77 ++ 5 files changed, 616 insertions(+), 791 deletions(-) create mode 100644 tools/cooja/java/se/sics/cooja/util/JSyntaxAddBreakpoint.java create mode 100644 tools/cooja/java/se/sics/cooja/util/JSyntaxRemoveBreakpoint.java diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/BreakpointsUI.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/BreakpointsUI.java index 7a9d0bdb9..ede9fc111 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/BreakpointsUI.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/BreakpointsUI.java @@ -31,26 +31,32 @@ package se.sics.cooja.mspmote.plugins; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; -import java.util.ArrayList; -import javax.swing.*; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JColorChooser; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JSeparator; +import javax.swing.JTable; +import javax.swing.SwingUtilities; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableCellEditor; import org.apache.log4j.Logger; import se.sics.cooja.GUI; -import se.sics.cooja.Mote; -import se.sics.cooja.MoteType; -import se.sics.cooja.Simulation; -import se.sics.cooja.mspmote.MspMote; +import se.sics.cooja.Watchpoint; +import se.sics.cooja.WatchpointMote; /** * Displays a set of breakpoints. @@ -60,27 +66,27 @@ import se.sics.cooja.mspmote.MspMote; public class BreakpointsUI extends JPanel { private static Logger logger = Logger.getLogger(BreakpointsUI.class); - private static final int COLUMN_INFO = 0; - private static final int COLUMN_ADDRESS = 1; - private static final int COLUMN_FILELINE = 2; + private static final int COLUMN_ADDRESS = 0; + private static final int COLUMN_FILELINE = 1; + private static final int COLUMN_INFO = 2; private static final int COLUMN_STOP = 3; - private static final int COLUMN_REMOVE = 4; private static final String[] COLUMN_NAMES = { - "Info", "Address", - "File", - "Stop", - "Remove" + "Source", + "Info", + "Stops simulation" }; - private MspBreakpointContainer breakpoints = null; + private WatchpointMote mote; + private MspCodeWatcher codeWatcher; private JTable table = null; - private MspBreakpoint popupBreakpoint = null; + private Watchpoint selectedWatchpoint = null; - public BreakpointsUI(MspBreakpointContainer breakpoints, final MspCodeWatcher codeWatcher) { - this.breakpoints = breakpoints; + public BreakpointsUI(WatchpointMote mote, final MspCodeWatcher codeWatcher) { + this.mote = mote; + this.codeWatcher = codeWatcher; /* Breakpoints table */ table = new JTable(tableModel) { @@ -93,15 +99,20 @@ public class BreakpointsUI extends JPanel { int realColumnIndex = table.convertColumnIndexToModel(colIndex); if (realColumnIndex == COLUMN_FILELINE) { - MspBreakpoint[] allBreakpoints = BreakpointsUI.this.breakpoints.getBreakpoints(); + Watchpoint[] allBreakpoints = BreakpointsUI.this.mote.getBreakpoints(); if (rowIndex < 0 || rowIndex >= allBreakpoints.length) { return null; } - File file = allBreakpoints[rowIndex].getCodeFile(); + Watchpoint watchpoint = allBreakpoints[rowIndex]; + File file = watchpoint.getCodeFile(); if (file == null) { - return null; + return String.format("[unknown @ 0x%04x]", watchpoint.getExecutableAddress()); } - return file.getPath() + ":" + allBreakpoints[rowIndex].getLineNumber(); + Integer line = watchpoint.getLineNumber(); + if (line == null) { + return file.getPath() + ":?"; + } + return file.getPath() + ":" + line; } if (realColumnIndex == COLUMN_INFO) { @@ -111,53 +122,41 @@ public class BreakpointsUI extends JPanel { if (realColumnIndex == COLUMN_STOP) { return "Indicates whether the watchpoint will stop the simulation when triggered"; } - - if (realColumnIndex == COLUMN_REMOVE) { - return "Remove breakpoint from this mote only. (Right-click for more options)"; - } return null; } }; - table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - table.getColumnModel().getColumn(COLUMN_ADDRESS).setPreferredWidth(60); /* XXX */ - table.getColumnModel().getColumn(COLUMN_ADDRESS).setMaxWidth(60); - table.getColumnModel().getColumn(COLUMN_INFO).setPreferredWidth(60); - table.getColumnModel().getColumn(COLUMN_INFO).setMaxWidth(60); - table.getColumnModel().getColumn(COLUMN_INFO).setCellRenderer( - new DefaultTableCellRenderer() { - public Component getTableCellRendererComponent(JTable table, Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - Component c = super.getTableCellRendererComponent( - table, value, isSelected, hasFocus, row, column); - if (column != COLUMN_INFO) { - return c; - } - - MspBreakpoint[] allBreakpoints = BreakpointsUI.this.breakpoints.getBreakpoints(); - if (row < 0 || row >= allBreakpoints.length) { - return c; - } - MspBreakpoint breakpoint = allBreakpoints[row]; - if (breakpoint.getColor() == null) { - return c; - } - - /* Use watchpoint color */ - c.setForeground(breakpoint.getColor()); - return c; - } - }); - table.getColumnModel().getColumn(COLUMN_STOP).setPreferredWidth(60); - table.getColumnModel().getColumn(COLUMN_STOP).setMaxWidth(60); - table.getColumnModel().getColumn(COLUMN_REMOVE).setPreferredWidth(60); - table.getColumnModel().getColumn(COLUMN_REMOVE).setMaxWidth(60); + table.getColumnModel().getColumn(COLUMN_INFO).setCellRenderer(new DefaultTableCellRenderer() { + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent( + table, value, isSelected, hasFocus, row, column); + if (column != COLUMN_INFO) { + return c; + } + + Watchpoint[] allBreakpoints = BreakpointsUI.this.mote.getBreakpoints(); + if (row < 0 || row >= allBreakpoints.length) { + return c; + } + Watchpoint breakpoint = allBreakpoints[row]; + if (breakpoint.getColor() == null) { + return c; + } + + /* Use watchpoint color */ + c.setBackground(Color.WHITE); + c.setForeground(breakpoint.getColor()); + return c; + } + }); /* Popup menu: register on all motes */ final JPopupMenu popupMenu = new JPopupMenu(); - popupMenu.add(new JMenuItem(addToMoteTypeAction)); - popupMenu.add(new JMenuItem(delFromMoteTypeAction)); + popupMenu.add(new JMenuItem(gotoCodeAction)); + popupMenu.add(new JSeparator()); + popupMenu.add(new JMenuItem(removeWatchpointAction)); + popupMenu.add(new JMenuItem(configureWatchpointAction)); - /* Show source file on breakpoint mouse click */ table.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { java.awt.Point p = e.getPoint(); @@ -167,63 +166,32 @@ public class BreakpointsUI extends JPanel { if (realColumnIndex != COLUMN_ADDRESS && realColumnIndex != COLUMN_FILELINE - && realColumnIndex != COLUMN_REMOVE && realColumnIndex != COLUMN_INFO) { return; } - MspBreakpoint[] allBreakpoints = BreakpointsUI.this.breakpoints.getBreakpoints(); + Watchpoint[] allBreakpoints = BreakpointsUI.this.mote.getBreakpoints(); if (rowIndex < 0 || rowIndex >= allBreakpoints.length) { return; } - MspBreakpoint breakpoint = allBreakpoints[rowIndex]; + Watchpoint breakpoint = allBreakpoints[rowIndex]; if (e.isPopupTrigger() || SwingUtilities.isRightMouseButton(e)) { - popupBreakpoint = breakpoint; + selectedWatchpoint = breakpoint; popupMenu.show(table, e.getX(), e.getY()); return; } if (realColumnIndex == COLUMN_INFO) { - String msg = JOptionPane.showInputDialog( - GUI.getTopParentContainer(), - "Enter description", - "Watchpoint Description", - JOptionPane.QUESTION_MESSAGE); - if (msg != null) { - breakpoint.setUserMessage(msg); - } - Color newColor = JColorChooser.showDialog( - GUI.getTopParentContainer(), - "Watchpoint Color", - breakpoint.getColor()); - if (newColor != null) { - breakpoint.setColor(newColor); - } + configureWatchpointInfo(breakpoint); return; } - File file = allBreakpoints[rowIndex].getCodeFile(); + /*File file = allBreakpoints[rowIndex].getCodeFile(); int line = allBreakpoints[rowIndex].getLineNumber(); if (file == null) { return; - } - - /* Display source code */ - codeWatcher.displaySourceFile(file, line); - } - }); - - /* Update when breakpoints are triggered/added/removed */ - breakpoints.addWatchpointListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - MspBreakpoint triggered = BreakpointsUI.this.breakpoints.getLastWatchpoint(); - if (triggered == null) { - table.repaint(); - return; - } - - flashBreakpoint(triggered); + }*/ } }); @@ -232,24 +200,41 @@ public class BreakpointsUI extends JPanel { add(BorderLayout.CENTER, table); } - private void flashBreakpoint(MspBreakpoint breakpoint) { - /* Locate breakpoints table index */ - int index = -1; - MspBreakpoint[] all = breakpoints.getBreakpoints(); - for (int i=0; i < breakpoints.getBreakpointsCount(); i++) { - if (breakpoint == all[i]) { - index = i; - break; - } - } - if (index < 0) { + private void configureWatchpointInfo(Watchpoint breakpoint) { + String msg = (String) JOptionPane.showInputDialog( + GUI.getTopParentContainer(), + "Enter description;", + "Watchpoint description", + JOptionPane.QUESTION_MESSAGE, null, null, breakpoint.getUserMessage()); + if (msg == null) { return; } + breakpoint.setUserMessage(msg); + Color newColor = JColorChooser.showDialog( + GUI.getTopParentContainer(), + "Watchpoint color", + breakpoint.getColor()); + if (newColor == null) { + return; + } + breakpoint.setColor(newColor); + } - final int breakpointIndex = index; + public void selectBreakpoint(final Watchpoint breakpoint) { + if (breakpoint == null) { + return; + } + /* Locate breakpoints table index */ SwingUtilities.invokeLater(new Runnable() { public void run() { - table.setRowSelectionInterval(breakpointIndex, breakpointIndex); + Watchpoint[] watchpoints = mote.getBreakpoints(); + for (int i=0; i < watchpoints.length; i++) { + if (breakpoint == watchpoints[i]) { + /* Select */ + table.setRowSelectionInterval(i, i); + return; + } + } } }); } @@ -259,18 +244,18 @@ public class BreakpointsUI extends JPanel { return COLUMN_NAMES[col].toString(); } public int getRowCount() { - return breakpoints.getBreakpointsCount(); + return mote.getBreakpoints().length; } public int getColumnCount() { return COLUMN_NAMES.length; } public Object getValueAt(int row, int col) { - MspBreakpoint breakpoint = breakpoints.getBreakpoints()[row]; + Watchpoint breakpoint = mote.getBreakpoints()[row]; /* Executable address in hexadecimal */ if (col == COLUMN_ADDRESS) { Integer address = breakpoint.getExecutableAddress(); - return "0x" + Integer.toHexString(address.intValue()); + return String.format("0x%04x", address.intValue()); } /* Source file + line number */ @@ -300,7 +285,7 @@ public class BreakpointsUI extends JPanel { return getColumnClass(col) == Boolean.class; } public void setValueAt(Object value, int row, int col) { - MspBreakpoint breakpoint = breakpoints.getBreakpoints()[row]; + Watchpoint breakpoint = mote.getBreakpoints()[row]; if (col == COLUMN_STOP) { /* Toggle stop state */ @@ -308,109 +293,36 @@ public class BreakpointsUI extends JPanel { fireTableCellUpdated(row, col); return; } - - if (col == COLUMN_REMOVE) { - /* Remove breakpoint */ - Integer address = breakpoint.getExecutableAddress(); - breakpoints.removeBreakpoint(address); - fireTableCellUpdated(row, col); - return; - } } public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } }; - private Action addToMoteTypeAction = new AbstractAction("Register on all motes (mote type)") { + private Action gotoCodeAction = new AbstractAction("Show in source code") { public void actionPerformed(ActionEvent e) { - if (popupBreakpoint == null) { - logger.fatal("No breakpoint to add"); + if (selectedWatchpoint == null) { + return; } - - /* Extract all motes of the same mote type */ - Simulation sim = popupBreakpoint.getMote().getSimulation(); - MoteType type = popupBreakpoint.getMote().getType(); - ArrayList motes = new ArrayList(); - for (Mote m: sim.getMotes()) { - if (m.getType() == type) { - if (!(m instanceof MspMote)) { - logger.fatal("At least one mote was not a MSP mote: " + m); - return; - } - - motes.add((MspMote)m); - } - } - - /* Register breakpoints */ - int reregistered = 0; - for (MspMote m: motes) { - /* Avoid duplicates (match executable addresses) */ - MspBreakpointContainer container = m.getBreakpointsContainer(); - MspBreakpoint[] breakpoints = container.getBreakpoints(); - for (MspBreakpoint w: breakpoints) { - if (popupBreakpoint.getExecutableAddress().intValue() == - w.getExecutableAddress().intValue()) { - logger.info("Reregistering breakpoint at mote: " + m); - container.removeBreakpoint(w.getExecutableAddress()); - reregistered++; - } - } - - MspBreakpoint newBreakpoint = container.addBreakpoint( - popupBreakpoint.getCodeFile(), - popupBreakpoint.getLineNumber(), - popupBreakpoint.getExecutableAddress()); - newBreakpoint.setUserMessage(popupBreakpoint.getUserMessage()); - newBreakpoint.setColor(popupBreakpoint.getColor()); - newBreakpoint.setStopsSimulation(popupBreakpoint.stopsSimulation()); - } - - JOptionPane.showMessageDialog(GUI.getTopParentContainer(), - "Registered " + motes.size() + " breakpoints (" + reregistered + " re-registered)", - "Breakpoints added", JOptionPane.INFORMATION_MESSAGE); + codeWatcher.displaySourceFile(selectedWatchpoint.getCodeFile(), selectedWatchpoint.getLineNumber(), false); } }; - private Action delFromMoteTypeAction = new AbstractAction("Delete from all motes (mote type)") { + private Action removeWatchpointAction = new AbstractAction("Remove watchpoint") { public void actionPerformed(ActionEvent e) { - if (popupBreakpoint == null) { - logger.fatal("No breakpoint to delete"); + if (selectedWatchpoint == null) { + return; } - - /* Extract all motes of the same mote type */ - Simulation sim = popupBreakpoint.getMote().getSimulation(); - MoteType type = popupBreakpoint.getMote().getType(); - ArrayList motes = new ArrayList(); - for (Mote m: sim.getMotes()) { - if (m.getType() == type) { - if (!(m instanceof MspMote)) { - logger.fatal("At least one mote was not a MSP mote: " + m); - return; - } - - motes.add((MspMote)m); - } + mote.removeBreakpoint(selectedWatchpoint); + table.invalidate(); + table.repaint(); + } + }; + private Action configureWatchpointAction = new AbstractAction("Configure watchpoint information") { + public void actionPerformed(ActionEvent e) { + if (selectedWatchpoint == null) { + return; } - - /* Delete breakpoints */ - int deleted = 0; - for (MspMote m: motes) { - /* Avoid duplicates (match executable addresses) */ - MspBreakpointContainer container = m.getBreakpointsContainer(); - MspBreakpoint[] breakpoints = container.getBreakpoints(); - for (MspBreakpoint w: breakpoints) { - if (popupBreakpoint.getExecutableAddress().intValue() == - w.getExecutableAddress().intValue()) { - container.removeBreakpoint(w.getExecutableAddress()); - deleted++; - } - } - } - - JOptionPane.showMessageDialog(GUI.getTopParentContainer(), - "Deleted " + deleted + " breakpoints", - "Breakpoints deleted", JOptionPane.INFORMATION_MESSAGE); + configureWatchpointInfo(selectedWatchpoint); } }; } diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/CodeUI.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/CodeUI.java index c034d6f0a..e58257b66 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/CodeUI.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/CodeUI.java @@ -34,32 +34,33 @@ package se.sics.cooja.mspmote.plugins; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; -import java.awt.Font; import java.awt.Point; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.io.File; import java.util.ArrayList; -import java.util.Iterator; -import java.util.Vector; +import java.util.HashMap; -import javax.swing.AbstractListModel; -import javax.swing.JLabel; -import javax.swing.JList; +import javax.swing.JEditorPane; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; -import javax.swing.JSeparator; -import javax.swing.ListCellRenderer; +import javax.swing.JScrollPane; import javax.swing.SwingUtilities; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter; +import javax.swing.text.Highlighter.HighlightPainter; + +import jsyntaxpane.DefaultSyntaxKit; +import jsyntaxpane.components.Markers.SimpleMarker; import org.apache.log4j.Logger; -import se.sics.mspsim.extutil.highlight.CScanner; -import se.sics.mspsim.extutil.highlight.Token; -import se.sics.mspsim.extutil.highlight.TokenTypes; +import se.sics.cooja.Watchpoint; +import se.sics.cooja.WatchpointMote; +import se.sics.cooja.util.JSyntaxAddBreakpoint; +import se.sics.cooja.util.JSyntaxRemoveBreakpoint; +import se.sics.cooja.util.StringUtils; /** * Displays source code and allows a user to add and remove breakpoints. @@ -69,184 +70,231 @@ import se.sics.mspsim.extutil.highlight.TokenTypes; public class CodeUI extends JPanel { private static Logger logger = Logger.getLogger(CodeUI.class); - private JPanel panel = null; - private JList codeList = null; + { + DefaultSyntaxKit.initKit(); + } - private MspBreakpointContainer breakpoints = null; + private JEditorPane codeEditor = null; + private HashMap codeEditorLines = null; protected File displayedFile = null; - private Token tokensArray[][] = null; - private int tokensStartPos[] = null; + private static final HighlightPainter CURRENT_LINE_MARKER = new SimpleMarker(Color.ORANGE); + private static final HighlightPainter SELECTED_LINE_MARKER = new SimpleMarker(Color.GREEN); + private static final HighlightPainter BREAKPOINTS_MARKER = new SimpleMarker(Color.LIGHT_GRAY); + private final Object currentLineTag; + private final Object selectedLineTag; + private final ArrayList breakpointsLineTags = new ArrayList(); - /** - * @param breakpoints Breakpoints - */ - public CodeUI(MspBreakpointContainer breakpoints) { - this.breakpoints = breakpoints; + private JSyntaxAddBreakpoint actionAddBreakpoint = null; + private JSyntaxRemoveBreakpoint actionRemoveBreakpoint = null; + + private WatchpointMote mote; + + public CodeUI(WatchpointMote mote) { + this.mote = mote; + + { + /* Workaround to configure jsyntaxpane */ + JEditorPane e = new JEditorPane(); + new JScrollPane(e); + e.setContentType("text/c"); + DefaultSyntaxKit kit = (DefaultSyntaxKit) e.getEditorKit(); + kit.setProperty("Action.addbreakpoint", JSyntaxAddBreakpoint.class.getName()); + kit.setProperty("Action.removebreakpoint", JSyntaxRemoveBreakpoint.class.getName()); + kit.setProperty("PopupMenu", "copy-to-clipboard,-,find,find-next,goto-line,-,addbreakpoint,removebreakpoint"); + } setLayout(new BorderLayout()); + codeEditor = new JEditorPane(); + add(new JScrollPane(codeEditor), BorderLayout.CENTER); + doLayout(); - panel = new JPanel(new BorderLayout()); - add(panel, BorderLayout.CENTER); - displayNoCode(); + codeEditorLines = new HashMap(); + codeEditor.setContentType("text/c"); + DefaultSyntaxKit kit = (DefaultSyntaxKit) codeEditor.getEditorKit(); + kit.setProperty("Action.addbreakpoint", JSyntaxAddBreakpoint.class.getName()); + kit.setProperty("Action.removebreakpoint", JSyntaxRemoveBreakpoint.class.getName()); + kit.setProperty("PopupMenu", "copy-to-clipboard,-,find,find-next,goto-line,-,addbreakpoint,removebreakpoint"); - breakpoints.addWatchpointListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - /* Only update code list if simulation is not running */ - if (CodeUI.this.breakpoints.getMote().getSimulation().isRunning() || - CodeUI.this.breakpoints.getLastWatchpoint() != null) { + JPopupMenu p = codeEditor.getComponentPopupMenu(); + for (Component c: p.getComponents()) { + if (c instanceof JMenuItem) { + if (((JMenuItem) c).getAction() != null && + ((JMenuItem) c).getAction() instanceof JSyntaxAddBreakpoint) { + actionAddBreakpoint = (JSyntaxAddBreakpoint)(((JMenuItem) c).getAction()); + actionAddBreakpoint.setMenuText("Add breakpoint"); + } + if (((JMenuItem) c).getAction() != null && + ((JMenuItem) c).getAction() instanceof JSyntaxRemoveBreakpoint) { + actionRemoveBreakpoint = (JSyntaxRemoveBreakpoint)(((JMenuItem) c).getAction()); + actionRemoveBreakpoint.setMenuText("Remove breakpoint"); + } + } + } + + codeEditor.setText(""); + codeEditorLines.clear(); + codeEditor.setEditable(false); + + Highlighter hl = codeEditor.getHighlighter(); + Object o = null; + try { + o = hl.addHighlight(0, 0, CURRENT_LINE_MARKER); + } catch (BadLocationException e1) { + } + currentLineTag = o; + + o = null; + try { + o = hl.addHighlight(0, 0, SELECTED_LINE_MARKER); + } catch (BadLocationException e1) { + } + selectedLineTag = o; + + codeEditor.getComponentPopupMenu().addPopupMenuListener(new PopupMenuListener() { + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + /* Disable breakpoint actions */ + actionAddBreakpoint.setEnabled(false); + actionRemoveBreakpoint.setEnabled(false); + + int line = getCodeEditorMouseLine(); + if (line < 1) { return; } - - SwingUtilities.invokeLater(new Runnable() { - public void run() { - if (codeList != null) { - codeList.updateUI(); - } - } - }); + + /* Configure breakpoint menu options */ + Integer address = + CodeUI.this.mote.getExecutableAddressOf(displayedFile, line); + if (address == null) { + return; + } + final int start = codeEditorLines.get(line); + int end = codeEditorLines.get(line+1); + Highlighter hl = codeEditor.getHighlighter(); + try { + hl.changeHighlight(selectedLineTag, start, end); + } catch (BadLocationException e1) { + } + boolean hasBreakpoint = + CodeUI.this.mote.breakpointExists(address); + if (!hasBreakpoint) { + actionAddBreakpoint.setEnabled(true); + actionAddBreakpoint.putValue("WatchpointMote", CodeUI.this.mote); + actionAddBreakpoint.putValue("WatchpointFile", displayedFile); + actionAddBreakpoint.putValue("WatchpointLine", new Integer(line)); + actionAddBreakpoint.putValue("WatchpointAddress", new Integer(address)); + } else { + actionRemoveBreakpoint.setEnabled(true); + actionRemoveBreakpoint.putValue("WatchpointMote", CodeUI.this.mote); + actionRemoveBreakpoint.putValue("WatchpointFile", displayedFile); + actionRemoveBreakpoint.putValue("WatchpointLine", new Integer(line)); + actionRemoveBreakpoint.putValue("WatchpointAddress", new Integer(address)); + } + } + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + Highlighter hl = codeEditor.getHighlighter(); + try { + hl.changeHighlight(selectedLineTag, 0, 0); + } catch (BadLocationException e1) { + } + } + public void popupMenuCanceled(PopupMenuEvent e) { } }); + + displayNoCode(true); + } + + public void updateBreakpoints() { + Highlighter hl = codeEditor.getHighlighter(); + + for (Object breakpointsLineTag: breakpointsLineTags) { + hl.removeHighlight(breakpointsLineTag); + } + breakpointsLineTags.clear(); + + for (Watchpoint w: mote.getBreakpoints()) { + if (!w.getCodeFile().equals(displayedFile)) { + continue; + } + + final int start = codeEditorLines.get(w.getLineNumber()); + int end = codeEditorLines.get(w.getLineNumber()+1); + try { + breakpointsLineTags.add(hl.addHighlight(start, end, BREAKPOINTS_MARKER)); + } catch (BadLocationException e1) { + } + } + } + + private int getCodeEditorMouseLine() { + if (codeEditorLines == null) { + return -1; + } + Point mousePos = codeEditor.getMousePosition(); + if (mousePos == null) { + return -1; + } + int modelPos = codeEditor.viewToModel(mousePos); + int line = 1; + while (codeEditorLines.containsKey(line+1)) { + int next = codeEditorLines.get(line+1); + if (modelPos < next) { + return line; + } + line++; + } + return -1; } /** * Remove any shown source code. */ - public void displayNoCode() { - // Display "no code" message + public void displayNoCode(final boolean markCurrent) { SwingUtilities.invokeLater(new Runnable() { public void run() { - panel.removeAll(); - panel.repaint(); + displayedFile = null; + codeEditor.setText(null); + codeEditorLines.clear(); + displayLine(-1, markCurrent); } }); - displayedFile = null; - return; - } - - private void createTokens(String[] codeData) { - - /* Merge code lines */ - StringBuilder sb = new StringBuilder(); - for (String line: codeData) { - sb.append(line); - sb.append('\n'); - } - String code = sb.toString(); - - /* Scan code */ - CScanner cScanner = new CScanner(); - cScanner.change(0, 0, code.length()); - int nrTokens; - nrTokens = cScanner.scan(code.toCharArray(), 0, code.length()); - - /* Extract tokens */ - ArrayList codeTokensVector = new ArrayList(); - for (int i=0; i < nrTokens; i++) { - Token token = cScanner.getToken(i); - codeTokensVector.add(token); - } - - /* Create new line token array */ - Token newTokensArray[][] = new Token[codeData.length][]; - int[] newTokensStartPos = new int[codeData.length]; - int lineStart=0, lineEnd=-1; - Iterator tokens = codeTokensVector.iterator(); - Token currentToken = tokens.next(); - for (int i=0; i < newTokensArray.length; i++) { - lineStart = lineEnd + 1; - lineEnd = lineStart + codeData[i].length(); - - newTokensStartPos[i] = lineStart;; - - /* Advance tokens until correct line */ - while (currentToken.position + currentToken.symbol.name.length() < lineStart) { - if (!tokens.hasNext()) { - break; - } - currentToken = tokens.next(); - } - - /* Advance tokens until last token on line */ - Vector lineTokens = new Vector(); - while (currentToken.position < lineEnd) { - lineTokens.add(currentToken); - - if (!tokens.hasNext()) { - break; - } - currentToken = tokens.next(); - } - - if (currentToken == null) { - break; - } - - /* Store line tokens */ - Token[] lineTokensArray = new Token[lineTokens.size()]; - for (int j=0; j < lineTokens.size(); j++) { - lineTokensArray[j] = lineTokens.get(j); - } - newTokensArray[i] = lineTokensArray; - } - - /* Start using tokens array */ - tokensArray = newTokensArray; - tokensStartPos = newTokensStartPos; } /** * Display given source code and mark given line. * * @param codeFile Source code file - * @param codeData Source code * @param lineNr Line numer */ - public void displayNewCode(File codeFile, String[] codeData, final int lineNr) { - displayedFile = codeFile; + public void displayNewCode(final File codeFile, final int lineNr, final boolean markCurrent) { + if (!codeFile.equals(displayedFile)) { + /* Read from disk */ + final String data = StringUtils.loadFromFile(codeFile); + if (data == null || data.length() == 0) { + displayNoCode(markCurrent); + return; + } - if (codeData == null || codeData.length == 0) { - displayNoCode(); - return; + String[] lines = data.split("\n"); + logger.info("Opening " + codeFile + " (" + lines.length + " lines)"); + int length = 0; + codeEditorLines.clear(); + for (int line=1; line-1 < lines.length; line++) { + codeEditorLines.put(line, length); + length += lines[line-1].length()+1; + } + codeEditor.setText(data.toString()); + displayedFile = codeFile; + updateBreakpoints(); } - logger.info("Opening " + codeFile + " (" + codeData.length + " lines)"); - - /* Create new list */ - final JList newList = new JList(new CodeListModel(codeData)); - newList.setBackground(Color.WHITE); - newList.setFont(new Font("courier", 0, 12)); - newList.setCellRenderer(new CodeCellRenderer(lineNr)); - ((CodeCellRenderer)newList.getCellRenderer()).setNice(false); - newList.setFixedCellHeight(12); - newList.addMouseListener(new MouseAdapter() { - public void mousePressed(MouseEvent e) { - handleMouseEvent(e); - } - public void mouseReleased(MouseEvent e) { - handleMouseEvent(e); - } - public void mouseEntered(MouseEvent e) { - handleMouseEvent(e); - } - public void mouseExited(MouseEvent e) { - handleMouseEvent(e); - } - public void mouseClicked(MouseEvent e) { - handleMouseEvent(e); - } - }); - createTokens(codeData); - SwingUtilities.invokeLater(new Runnable() { public void run() { - panel.removeAll(); - codeList = newList; - panel.add(codeList); - panel.validate(); - displayLine(lineNr); + displayLine(lineNr, markCurrent); } }); + } /** @@ -255,290 +303,35 @@ public class CodeUI extends JPanel { * * @param lineNumber Line number */ - public void displayLine(int lineNumber) { - if (codeList == null) { - return; - } - - ((CodeCellRenderer) codeList.getCellRenderer()).setNice(false); - ((CodeCellRenderer) codeList.getCellRenderer()).changeCurrentLine(lineNumber); - ((CodeCellRenderer) codeList.getCellRenderer()).validate(); - - if (lineNumber > 0) { - int index = lineNumber - 1; - codeList.setSelectedIndex(index); - codeList.ensureIndexIsVisible(Math.max(0, index-3)); - codeList.ensureIndexIsVisible(Math.min(index+3, codeList.getModel().getSize())); - codeList.ensureIndexIsVisible(index); - } - - codeList.updateUI(); - SwingUtilities.invokeLater(new Runnable() { - public void run() { - ((CodeCellRenderer) codeList.getCellRenderer()).setNice(true); - codeList.repaint(); - } - }); - } - - private void handleMouseEvent(MouseEvent event) { - if (event.isPopupTrigger()) { - Point menuLocation = codeList.getPopupLocation(event); - if (menuLocation == null) { - menuLocation = new Point( - codeList.getLocationOnScreen().x + event.getX(), - codeList.getLocationOnScreen().y + event.getY()); + private void displayLine(int lineNumber, boolean markCurrent) { + try { + if (markCurrent) { + /* remove previous highlight */ + Highlighter hl = codeEditor.getHighlighter(); + hl.changeHighlight(currentLineTag, 0, 0); } - final int currentLine = codeList.locationToIndex(new Point(event.getX(), event.getY())) + 1; - codeList.setSelectedIndex(currentLine - 1); - JPopupMenu popupMenu = createPopupMenu(displayedFile, currentLine); - - popupMenu.setLocation(menuLocation); - popupMenu.setInvoker(codeList); - popupMenu.setVisible(true); - } - } - - private JPopupMenu createPopupMenu(final File codeFile, final int lineNr) { - final Integer executableAddress = breakpoints.getExecutableAddressOf(codeFile, lineNr); - boolean breakpointExists = breakpoints.breakpointExists(codeFile, lineNr); - - JPopupMenu menuMotePlugins = new JPopupMenu(); - JMenuItem headerMenuItem = new JMenuItem("Breakpoints:"); - headerMenuItem.setEnabled(false); - menuMotePlugins.add(headerMenuItem); - menuMotePlugins.add(new JSeparator()); - - JMenuItem addBreakpointMenuItem = new JMenuItem("Add breakpoint on line " + lineNr); - if (executableAddress == null || breakpointExists) { - addBreakpointMenuItem.setEnabled(false); - } else { - addBreakpointMenuItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - breakpoints.addBreakpoint(codeFile, lineNr, executableAddress); + if (lineNumber >= 0) { + final int start = codeEditorLines.get(lineNumber); + int end = codeEditorLines.get(lineNumber+1); + if (markCurrent) { + /* highlight code */ + Highlighter hl = codeEditor.getHighlighter(); + hl.changeHighlight(currentLineTag, start, end); } - }); - } - menuMotePlugins.add(addBreakpointMenuItem); - JMenuItem delBreakpointMenuItem = new JMenuItem("Delete breakpoint on line " + lineNr); - if (executableAddress == null || !breakpointExists) { - delBreakpointMenuItem.setEnabled(false); - } else { - delBreakpointMenuItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - breakpoints.removeBreakpoint(executableAddress); - } - }); - } - menuMotePlugins.add(delBreakpointMenuItem); - - return menuMotePlugins; - } - - private class CodeListModel extends AbstractListModel { - private String[] codeData; - - public CodeListModel(String[] codeData) { - super(); - this.codeData = codeData; - } - - public int getSize() { - if (codeData == null || codeData.length == 0) { - return 0; - } - - return codeData.length; - } - - public Object getElementAt(int index) { - if (codeData == null || codeData.length == 0) { - return "No code to display"; - } - - return codeData[index]; - } - } - - /* FROM: http://www.rgagnon.com/javadetails/java-0306.html, 03/19/2008 */ - private static String stringToHTMLString(String string) { - StringBuffer sb = new StringBuffer(string.length()); - boolean lastWasBlankChar = false; - int len = string.length(); - char c; - - for (int i = 0; i < len; i++) - { - c = string.charAt(i); - if (c == ' ') { - if (lastWasBlankChar) { - lastWasBlankChar = false; - sb.append(" "); - } - else { - lastWasBlankChar = true; - sb.append(' '); - } - } - else { - lastWasBlankChar = false; - // - // HTML Special Chars - if (c == '"') { - sb.append("""); - } else if (c == '&') { - sb.append("&"); - } else if (c == '<') { - sb.append("<"); - } else if (c == '>') { - sb.append(">"); - } else if (c == '\n') { - // Handle Newline - sb.append("<br/>"); - } else { - int ci = 0xffff & c; - if (ci < 160 ) { - // nothing special only 7 Bit - sb.append(c); - } else { - // Not 7 Bit use the unicode system - sb.append("&#"); - sb.append(new Integer(ci).toString()); - sb.append(';'); + /* ensure visible */ + SwingUtilities.invokeLater(new Runnable() { + public void run() { + try { + codeEditor.scrollRectToVisible(codeEditor.modelToView(start)); + } catch (BadLocationException e) { + } } - } + }); } + } catch (Exception e) { + logger.warn("Error when highlighting current line: " + e.getMessage(), e); } - return sb.toString(); } - - private class CodeCellRenderer extends JLabel implements ListCellRenderer { - private int currentIndex; - private boolean nice = true; - - public CodeCellRenderer(int currentLineNr) { - this.currentIndex = currentLineNr - 1; - } - - public void setNice(boolean b) { - nice = b; - } - - public void changeCurrentLine(int currentLineNr) { - this.currentIndex = currentLineNr - 1; - } - - private String getColoredLabelText(int lineNr, int lineStartPos, Token[] tokens, String code) { - StringBuilder sb = new StringBuilder(); - sb.append(""); - - /* Add line number */ - String lineString = "0000" + Integer.toString(lineNr); - lineString = lineString.substring(lineString.length() - 4); - sb.append(""); - sb.append(lineString); - sb.append(": "); - - /* Add code */ - if (tokens == null || tokens.length == 0 || lineStartPos < 0) { - sb.append(""); - sb.append(code); - sb.append(""); - } else { - for (int i=tokens.length-1; i >= 0; i--) { - Token subToken = tokens[i]; - - String colorString = "000000"; - - /* Determine code color */ - final int type = subToken.symbol.type; - switch (type) { - case TokenTypes.COMMENT: - case TokenTypes.START_COMMENT: - case TokenTypes.MID_COMMENT: - case TokenTypes.END_COMMENT: - colorString = "00AA00"; - break; - case TokenTypes.STRING: - colorString = "0000AA"; - break; - case TokenTypes.KEYWORD: - case TokenTypes.KEYWORD2: - colorString = "AA0000"; - break; - } - - /* Extract part of token residing in current line */ - int tokenLinePos; - String subCode; - if (subToken.position < lineStartPos) { - subCode = subToken.symbol.name.substring(lineStartPos - subToken.position); - tokenLinePos = 0; - } else if (subToken.position + subToken.symbol.name.length() > lineStartPos + code.length()) { - subCode = subToken.symbol.name.substring(0, code.length() + lineStartPos - subToken.position); - tokenLinePos = subToken.position - lineStartPos; - } else { - subCode = subToken.symbol.name; - tokenLinePos = subToken.position - lineStartPos; - } - - subCode = stringToHTMLString(subCode); - String firstPart = code.substring(0, tokenLinePos); - String coloredSubCode = "" + subCode + ""; - String lastPart = - tokenLinePos + subToken.symbol.name.length() >= code.length()? - "":code.substring(tokenLinePos + subToken.symbol.name.length()); - - code = firstPart + coloredSubCode + lastPart; - } - - code = code.replace(" ", "  "); - sb.append(code); - } - - sb.append(""); - return sb.toString(); - } - - public Component getListCellRendererComponent( - JList list, - Object value, - int index, - boolean isSelected, - boolean cellHasFocus) - { - int lineNr = index + 1; - if (!nice) { - setText((String) value); - } else if (tokensArray != null && index < tokensArray.length && tokensArray[index] != null) { - setText(getColoredLabelText(lineNr, tokensStartPos[index], tokensArray[index], (String) value)); - } else { - setText(getColoredLabelText(lineNr, 0, null, (String) value)); - } - - if (index == currentIndex) { - setBackground(Color.green); - } else if (isSelected) { - setBackground(list.getSelectionBackground()); - setForeground(list.getSelectionForeground()); - } else { - setBackground(list.getBackground()); - setForeground(list.getForeground()); - } - setEnabled(list.isEnabled()); - - if (breakpoints.breakpointExists(displayedFile, lineNr)) { - setFont(list.getFont().deriveFont(Font.BOLD)); - } else { - setFont(list.getFont()); - } - - setOpaque(true); - - return this; - } - } - } diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCodeWatcher.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCodeWatcher.java index 4ad0ca2e1..a11e31592 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCodeWatcher.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCodeWatcher.java @@ -30,18 +30,16 @@ package se.sics.cooja.mspmote.plugins; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Observable; import java.util.Observer; -import java.util.Vector; import javax.swing.AbstractAction; import javax.swing.Action; @@ -54,8 +52,7 @@ import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.filechooser.FileFilter; @@ -72,11 +69,12 @@ import se.sics.cooja.MotePlugin; import se.sics.cooja.PluginType; import se.sics.cooja.Simulation; import se.sics.cooja.VisPlugin; +import se.sics.cooja.Watchpoint; +import se.sics.cooja.WatchpointMote; +import se.sics.cooja.WatchpointMote.WatchpointListener; import se.sics.cooja.mspmote.MspMote; import se.sics.cooja.mspmote.MspMoteType; -import se.sics.cooja.util.StringUtils; import se.sics.mspsim.core.EmulationException; -import se.sics.mspsim.core.MSP430; import se.sics.mspsim.ui.DebugUI; import se.sics.mspsim.util.DebugInfo; import se.sics.mspsim.util.ELFDebug; @@ -85,25 +83,31 @@ import se.sics.mspsim.util.ELFDebug; @PluginType(PluginType.MOTE_PLUGIN) public class MspCodeWatcher extends VisPlugin implements MotePlugin { private static final long serialVersionUID = -8463196456352243367L; + + private static final int SOURCECODE = 0; + private static final int BREAKPOINTS = 2; + private static Logger logger = Logger.getLogger(MspCodeWatcher.class); private Simulation simulation; private Observer simObserver; - private MspMote mspMote; private File currentCodeFile = null; private int currentLineNumber = -1; - private JSplitPane leftSplitPane, rightSplitPane; private DebugUI assCodeUI; private CodeUI sourceCodeUI; private BreakpointsUI breakpointsUI; - private MspBreakpointContainer breakpoints = null; + private MspMote mspMote; /* currently the only supported mote */ + private WatchpointMote watchpointMote; + private WatchpointListener watchpointListener; private JComboBox fileComboBox; private String[] debugInfoMap = null; private File[] sourceFiles; - + + private JTabbedPane mainPane; + /** * Mini-debugger for MSP Motes. * Visualizes instructions, source code and allows a user to manipulate breakpoints. @@ -113,15 +117,13 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { * @param gui Simulator */ public MspCodeWatcher(Mote mote, Simulation simulationToVisualize, GUI gui) { - super("Msp Code Watcher", gui); - this.mspMote = (MspMote) mote; + super("Msp Code Watcher - " + mote, gui); simulation = simulationToVisualize; + this.mspMote = (MspMote) mote; + this.watchpointMote = (WatchpointMote) mote; getContentPane().setLayout(new BorderLayout()); - /* Breakpoints */ - breakpoints = mspMote.getBreakpointsContainer(); - /* Create source file list */ fileComboBox = new JComboBox(); fileComboBox.addActionListener(new ActionListener() { @@ -149,58 +151,68 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { } }); updateFileComboBox(); - + /* Browse code control (north) */ - JButton currentFileButton = new JButton(currentFileAction); - JButton mapButton = new JButton(mapAction); - - Box browseBox = Box.createHorizontalBox(); - browseBox.add(Box.createHorizontalStrut(10)); - browseBox.add(new JLabel("Program counter: ")); - browseBox.add(currentFileButton); - browseBox.add(Box.createHorizontalGlue()); - browseBox.add(new JLabel("Browse: ")); - browseBox.add(fileComboBox); - browseBox.add(Box.createHorizontalStrut(10)); - browseBox.add(mapButton); - browseBox.add(Box.createHorizontalStrut(10)); + Box sourceCodeControl = Box.createHorizontalBox(); + sourceCodeControl.add(new JButton(stepAction)); + sourceCodeControl.add(Box.createHorizontalStrut(10)); + sourceCodeControl.add(new JLabel("Current location: ")); + sourceCodeControl.add(new JButton(currentFileAction)); + sourceCodeControl.add(Box.createHorizontalGlue()); + sourceCodeControl.add(new JLabel("Source files: ")); + sourceCodeControl.add(fileComboBox); + sourceCodeControl.add(Box.createHorizontalStrut(5)); + sourceCodeControl.add(new JButton(mapAction)); + sourceCodeControl.add(Box.createHorizontalStrut(10)); - /* Execution control panel (south) */ - JPanel controlPanel = new JPanel(); - JButton button = new JButton(stepAction); - controlPanel.add(button); + /* Execution control panel (south of source code panel) */ + + /* Layout */ + mainPane = new JTabbedPane(); + + sourceCodeUI = new CodeUI(watchpointMote); + JPanel sourceCodePanel = new JPanel(new BorderLayout()); + sourceCodePanel.add(BorderLayout.CENTER, sourceCodeUI); + sourceCodePanel.add(BorderLayout.SOUTH, sourceCodeControl); + mainPane.addTab("Source code", null, sourceCodePanel, null); /* SOURCECODE */ - - /* Main components: assembler and C code + breakpoints (center) */ assCodeUI = new DebugUI(this.mspMote.getCPU(), true); - breakpointsUI = new BreakpointsUI(breakpoints, this); - sourceCodeUI = new CodeUI(breakpoints); - leftSplitPane = new JSplitPane( - JSplitPane.HORIZONTAL_SPLIT, - new JScrollPane(assCodeUI), - new JScrollPane(breakpointsUI) - ); - leftSplitPane.setOneTouchExpandable(true); - leftSplitPane.setDividerLocation(0.0); - rightSplitPane = new JSplitPane( - JSplitPane.HORIZONTAL_SPLIT, - leftSplitPane, - new JScrollPane(sourceCodeUI) - ); - rightSplitPane.setOneTouchExpandable(true); - rightSplitPane.setDividerLocation(0.0); + for (Component c: assCodeUI.getComponents()) { + c.setBackground(Color.WHITE); + } + mainPane.addTab("Instructions", null, assCodeUI, null); - add(BorderLayout.NORTH, browseBox); - add(BorderLayout.CENTER, rightSplitPane); - add(BorderLayout.SOUTH, controlPanel); + breakpointsUI = new BreakpointsUI(mspMote, this); + mainPane.addTab("Breakpoints", null, breakpointsUI, "Right-click source code to add"); /* BREAKPOINTS */ + + add(BorderLayout.CENTER, mainPane); + + /* Listen for breakpoint changes */ + watchpointMote.addWatchpointListener(watchpointListener = new WatchpointListener() { + public void watchpointTriggered(final Watchpoint watchpoint) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + logger.info("Watchpoint triggered: " + watchpoint); + if (simulation.isRunning()) { + return; + } + breakpointsUI.selectBreakpoint(watchpoint); + sourceCodeUI.updateBreakpoints(); + showCurrentPC(); + } + }); + } + public void watchpointsChanged() { + sourceCodeUI.updateBreakpoints(); + } + }); - /* Observe when simulation starts/stops */ simulation.addObserver(simObserver = new Observer() { public void update(Observable obs, Object obj) { if (!simulation.isRunning()) { stepAction.setEnabled(true); - updateInfo(); + showCurrentPC(); } else { stepAction.setEnabled(false); } @@ -208,7 +220,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { }); setSize(750, 500); - updateInfo(); + showCurrentPC(); } private void updateFileComboBox() { @@ -221,25 +233,12 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { fileComboBox.setSelectedIndex(0); } - public void displaySourceFile(File file, final int line) { - if (file != null && - sourceCodeUI.displayedFile != null && - file.compareTo(sourceCodeUI.displayedFile) == 0) { - /* No need to reload source file */ - SwingUtilities.invokeLater(new Runnable() { - public void run() { - sourceCodeUI.displayLine(line); - } - }); - return; - } - - /* Load source file from disk */ - String[] codeData = readTextFile(file); - if (codeData == null) { - return; - } - sourceCodeUI.displayNewCode(file, codeData, line); + public void displaySourceFile(final File file, final int line, final boolean markCurrent) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + mainPane.setSelectedIndex(SOURCECODE); /* code */ + sourceCodeUI.displayNewCode(file, line, markCurrent); + }}); } private void sourceFileSelectionChanged() { @@ -249,32 +248,40 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { } File selectedFile = sourceFiles[index-1]; - displaySourceFile(selectedFile, -1); + displaySourceFile(selectedFile, -1, false); } - private void updateInfo() { + private void showCurrentPC() { /* Instructions */ assCodeUI.updateRegs(); assCodeUI.repaint(); /* Source */ updateCurrentSourceCodeFile(); - if (currentCodeFile == null) { + File file = currentCodeFile; + Integer line = currentLineNumber; + if (file == null || line == null) { currentFileAction.setEnabled(false); currentFileAction.putValue(Action.NAME, "[unknown]"); currentFileAction.putValue(Action.SHORT_DESCRIPTION, null); return; } currentFileAction.setEnabled(true); - currentFileAction.putValue(Action.NAME, currentCodeFile.getName() + ":" + currentLineNumber); - currentFileAction.putValue(Action.SHORT_DESCRIPTION, currentCodeFile.getAbsolutePath() + ":" + currentLineNumber); - fileComboBox.setSelectedItem(currentCodeFile.getName()); + currentFileAction.putValue(Action.NAME, file.getName() + ":" + line); + currentFileAction.putValue(Action.SHORT_DESCRIPTION, file.getAbsolutePath() + ":" + line); + fileComboBox.setSelectedItem(file.getName()); - displaySourceFile(currentCodeFile, currentLineNumber); + displaySourceFile(file, line, true); } public void closePlugin() { + watchpointMote.removeWatchpointListener(watchpointListener); + watchpointListener = null; + simulation.deleteObserver(simObserver); + simObserver = null; + + /* TODO XXX Unregister breakpoints? */ } private void updateCurrentSourceCodeFile() { @@ -285,8 +292,12 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { if (debug == null) { return; } - DebugInfo debugInfo = debug.getDebugInfo(mspMote.getCPU().reg[MSP430.PC]); + int pc = mspMote.getCPU().getPC(); + DebugInfo debugInfo = debug.getDebugInfo(pc); if (debugInfo == null) { + if (pc != 0) { + logger.warn("No sourcecode info at " + String.format("0x%04x", mspMote.getCPU().getPC())); + } return; } @@ -321,6 +332,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { private void tryMapDebugInfo() { final String[] debugFiles; try { + ELFDebug debug = ((MspMoteType)mspMote.getType()).getELF().getDebug(); debugFiles = debug != null ? debug.getSourceFiles() : null; if (debugFiles == null) { @@ -343,7 +355,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { "\"Next File\" proceeds to the next source file in the debug info.\n\n" + debugFiles[counter] + " (" + (counter+1) + '/' + debugFiles.length + ')', "Select source file to locate", JOptionPane.YES_NO_CANCEL_OPTION, - JOptionPane.QUESTION_MESSAGE, null, + JOptionPane.QUESTION_MESSAGE, null, new String[] { "Next File", "Locate File", "Cancel"}, "Next File"); if (n == JOptionPane.CANCEL_OPTION || n == JOptionPane.CLOSED_OPTION) { return null; @@ -421,14 +433,14 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { optionPane.setOptions(new String[] { "OK" }); optionPane.setInitialValue("OK"); JDialog dialog = optionPane.createDialog( - GUI.getTopParentContainer(), - "Mapping debug info to real sources"); + GUI.getTopParentContainer(), + "Mapping debug info to real sources"); dialog.setVisible(true); - + replace = replaceInput.getText(); replacement = replacementInput.getText(); } - + replace = replace.replace('\\', '/'); replacement = replacement.replace('\\', '/'); return new String[] { replace, replacement }; @@ -444,7 +456,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { updateFileComboBox(); } } - + private static File[] getSourceFiles(MspMote mote, String[] map) { final String[] sourceFiles; try { @@ -465,7 +477,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { } catch (IOException e1) { } } - + /* Verify that files exist */ ArrayList existing = new ArrayList(); for (String sourceFile: sourceFiles) { @@ -512,7 +524,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { "Make sure the source files were not moved after the firmware compilation.\n" + "\n" + "If you want to manually locate the sources, click \"Map\" button.", - "No source files found", + "No source files found", JOptionPane.WARNING_MESSAGE); return true; } @@ -530,7 +542,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { } sorted.add(index, file); } - + /* Add Contiki source first */ if (contikiSource != null && contikiSource.exists()) { sorted.add(0, contikiSource); @@ -539,63 +551,21 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { return sorted.toArray(new File[0]); } - /** - * Tries to open and read given text file. - * - * @param file File - * @return Line-by-line text in file - */ - public static String[] readTextFile(File file) { - if (GUI.isVisualizedInApplet()) { - /* Download from web server instead */ - String path = file.getPath(); - - /* Extract Contiki build path */ - String contikiBuildPath = GUI.getExternalToolsSetting("PATH_CONTIKI_BUILD"); - String contikiWebPath = GUI.getExternalToolsSetting("PATH_CONTIKI_WEB"); - - if (!path.startsWith(contikiBuildPath)) { - return null; - } - - try { - /* Replace Contiki parent path with web server code base */ - path = contikiWebPath + '/' + path.substring(contikiBuildPath.length()); - path = path.replace('\\', '/'); - URL url = new URL(GUI.getAppletCodeBase(), path); - String data = StringUtils.loadFromURL(url); - return data!=null?data.split("\n"):null; - } catch (MalformedURLException e) { - logger.warn("Failure to read source code: " + e); - return null; - } - } - - String data = StringUtils.loadFromFile(file); - return data!=null?data.split("\n"):null; - } - public Collection getConfigXML() { - Vector config = new Vector(); + ArrayList config = new ArrayList(); Element element; - - element = new Element("split_1"); - element.addContent("" + leftSplitPane.getDividerLocation()); + + element = new Element("tab"); + element.addContent("" + mainPane.getSelectedIndex()); config.add(element); - - element = new Element("split_2"); - element.addContent("" + rightSplitPane.getDividerLocation()); - config.add(element); - + return config; } public boolean setConfigXML(Collection configXML, boolean visAvailable) { for (Element element : configXML) { - if (element.getName().equals("split_1")) { - leftSplitPane.setDividerLocation(Integer.parseInt(element.getText())); - } else if (element.getName().equals("split_2")) { - rightSplitPane.setDividerLocation(Integer.parseInt(element.getText())); + if (element.getName().equals("tab")) { + mainPane.setSelectedIndex(Integer.parseInt(element.getText())); } } return true; @@ -608,11 +578,11 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { if (currentCodeFile == null) { return; } - displaySourceFile(currentCodeFile, currentLineNumber); + displaySourceFile(currentCodeFile, currentLineNumber, true); } }; - private AbstractAction mapAction = new AbstractAction("Map") { + private AbstractAction mapAction = new AbstractAction("Locate sources") { private static final long serialVersionUID = -3929432830596292495L; public void actionPerformed(ActionEvent e) { @@ -622,14 +592,13 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin { private AbstractAction stepAction = new AbstractAction("Step instruction") { private static final long serialVersionUID = 3520750710757816575L; - public void actionPerformed(ActionEvent e) { try { mspMote.getCPU().stepInstructions(1); } catch (EmulationException ex) { logger.fatal("Error: ", ex); } - updateInfo(); + showCurrentPC(); } }; diff --git a/tools/cooja/java/se/sics/cooja/util/JSyntaxAddBreakpoint.java b/tools/cooja/java/se/sics/cooja/util/JSyntaxAddBreakpoint.java new file mode 100644 index 000000000..b2e394c8c --- /dev/null +++ b/tools/cooja/java/se/sics/cooja/util/JSyntaxAddBreakpoint.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2012, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * $Id: CodeUI.java,v 1.8 2009/09/23 08:16:06 fros4943 Exp $ + */ + +package se.sics.cooja.util; + +import java.awt.event.ActionEvent; +import java.io.File; + +import javax.swing.Action; +import javax.swing.JMenuItem; +import javax.swing.text.JTextComponent; + +import jsyntaxpane.SyntaxDocument; +import jsyntaxpane.actions.DefaultSyntaxAction; + +import org.apache.log4j.Logger; + +import se.sics.cooja.WatchpointMote; + +public class JSyntaxAddBreakpoint extends DefaultSyntaxAction { + private static Logger logger = Logger.getLogger(JSyntaxAddBreakpoint.class); + + public JSyntaxAddBreakpoint() { + super("addbreakpoint"); + } + + public void actionPerformed(ActionEvent e) { + JMenuItem menuItem = (JMenuItem) e.getSource(); + Action action = menuItem.getAction(); + WatchpointMote watchpointMote = (WatchpointMote) action.getValue("WatchpointMote"); + if (watchpointMote == null) { + logger.warn("Error: No source, cannot configure breakpoint"); + return; + } + + File file = (File) action.getValue("WatchpointFile"); + Integer line = (Integer) action.getValue("WatchpointLine"); + Integer address = (Integer) action.getValue("WatchpointAddress"); + if (file == null || line == null || address == null) { + logger.warn("Error: Bad breakpoint info, cannot add breakpoint"); + return; + } + + watchpointMote.addBreakpoint(file, line, address); + } +} diff --git a/tools/cooja/java/se/sics/cooja/util/JSyntaxRemoveBreakpoint.java b/tools/cooja/java/se/sics/cooja/util/JSyntaxRemoveBreakpoint.java new file mode 100644 index 000000000..85bb3245d --- /dev/null +++ b/tools/cooja/java/se/sics/cooja/util/JSyntaxRemoveBreakpoint.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * $Id: CodeUI.java,v 1.8 2009/09/23 08:16:06 fros4943 Exp $ + */ + +package se.sics.cooja.util; + +import java.awt.event.ActionEvent; +import java.io.File; + +import javax.swing.Action; +import javax.swing.JMenuItem; + +import jsyntaxpane.actions.DefaultSyntaxAction; + +import org.apache.log4j.Logger; + +import se.sics.cooja.Watchpoint; +import se.sics.cooja.WatchpointMote; + +public class JSyntaxRemoveBreakpoint extends DefaultSyntaxAction { + private static Logger logger = Logger.getLogger(JSyntaxRemoveBreakpoint.class); + + public JSyntaxRemoveBreakpoint() { + super("removebreakpoint"); + } + + public void actionPerformed(ActionEvent e) { + JMenuItem menuItem = (JMenuItem) e.getSource(); + Action action = menuItem.getAction(); + WatchpointMote watchpointMote = (WatchpointMote) action.getValue("WatchpointMote"); + if (watchpointMote == null) { + logger.warn("Error: No source, cannot configure breakpoint"); + return; + } + + File file = (File) action.getValue("WatchpointFile"); + Integer line = (Integer) action.getValue("WatchpointLine"); + Integer address = (Integer) action.getValue("WatchpointAddress"); + if (file == null || line == null || address == null) { + logger.warn("Error: Bad breakpoint info, cannot remove breakpoint"); + return; + } + for (Watchpoint w: watchpointMote.getBreakpoints()) { + if (file.equals(w.getCodeFile()) && line.equals(w.getLineNumber()) && address.equals(w.getExecutableAddress())) { + watchpointMote.removeBreakpoint(w); + } + } + + } +} From dcd0460e0bc3f2acb6d45eb2f5ff4e3b6e5be499 Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind Date: Wed, 21 Mar 2012 16:59:08 +0100 Subject: [PATCH 06/14] using jsyntaxpane as javascript editor, added ui controls to link test scripts to a file on disk --- .../se/sics/cooja/plugins/ScriptRunner.java | 275 ++++++++++-------- 1 file changed, 159 insertions(+), 116 deletions(-) diff --git a/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java b/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java index 89fd6ca9e..278bde825 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java +++ b/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java @@ -32,6 +32,7 @@ package se.sics.cooja.plugins; import java.awt.BorderLayout; +import java.awt.Component; import java.awt.Dimension; import java.awt.Insets; import java.awt.Window; @@ -53,9 +54,12 @@ import java.util.Observer; import javax.script.ScriptException; import javax.swing.AbstractAction; +import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JDialog; +import javax.swing.JEditorPane; +import javax.swing.JFileChooser; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; @@ -63,8 +67,10 @@ import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextArea; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; +import javax.swing.filechooser.FileFilter; + +import jsyntaxpane.DefaultSyntaxKit; +import jsyntaxpane.actions.DefaultSyntaxAction; import org.apache.log4j.Logger; import org.jdom.Element; @@ -83,6 +89,10 @@ public class ScriptRunner extends VisPlugin { private static final long serialVersionUID = 7614358340336799109L; private static Logger logger = Logger.getLogger(ScriptRunner.class); + { + DefaultSyntaxKit.initKit(); + } + final String[] EXAMPLE_SCRIPTS = new String[] { "basic.js", "Various commands", "helloworld.js", "Wait for 'Hello, world'", @@ -97,24 +107,19 @@ public class ScriptRunner extends VisPlugin { private static BufferedWriter logWriter = null; /* For non-GUI tests */ - private JTextArea scriptTextArea = null; + private JEditorPane codeEditor = null; private JTextArea logTextArea = null; private JButton toggleButton = null; private JButton examplesButton = null; - private int scriptFirstLinesNumber; + private JSyntaxLinkFile actionLinkFile = null; + private File linkedFile = null; public ScriptRunner(Simulation simulation, GUI gui) { super("Contiki Test Editor", gui, false); this.simulation = simulation; - final JTextArea lineTextArea = new JTextArea(); - lineTextArea.setEnabled(false); - lineTextArea.setMargin(new Insets(5,0,5,0)); - - scriptFirstLinesNumber = 1; - /* Examples popup menu */ final JPopupMenu popupMenu = new JPopupMenu(); JMenuItem moteItem; @@ -148,65 +153,19 @@ public class ScriptRunner extends VisPlugin { } }); + { + /* Workaround to configure jsyntaxpane */ + JEditorPane e = new JEditorPane(); + new JScrollPane(e); + e.setContentType("text/javascript"); + DefaultSyntaxKit kit = (DefaultSyntaxKit) e.getEditorKit(); + kit.setProperty("PopupMenu", "copy-to-clipboard,-,find,find-next,goto-line,-,linkfile"); + kit.setProperty("Action.linkfile", JSyntaxLinkFile.class.getName()); + } + /* Script area */ - scriptTextArea = new JTextArea(12,50); - scriptTextArea.getDocument().addDocumentListener(new DocumentListener() { - private int lastLines = -1; - - private void update() { - int lines = scriptTextArea.getLineCount() + scriptFirstLinesNumber; - if (lines == lastLines) { - return; - } - lastLines = lines; - - String txt = ""; - for (int i=scriptFirstLinesNumber; i < 10; i++) { - if (i > lines) { - break; - } - txt += "00" + i + "\n"; - } - for (int i=10; i < 100; i++) { - if (i > lines) { - break; - } - txt += "0" + i + "\n"; - } - for (int i=100; i < 1000; i++) { - if (i > lines) { - break; - } - txt += i + "\n"; - } - lineTextArea.setText(txt); - - /*ScriptParser parser; - try { - parser = new ScriptParser(scriptTextArea.getText()); - String tooltip = parser.getJSCode(); - tooltip = tooltip.replaceAll("\n", "
"); - tooltip = "Generated code:

" + tooltip + ""; - lineTextArea.setToolTipText(tooltip); - } catch (ScriptSyntaxErrorException e) { - lineTextArea.setToolTipText("Unable to generate code: " + e.getMessage()); - }*/ - } - - public void changedUpdate(DocumentEvent e) { - update(); - } - public void insertUpdate(DocumentEvent e) { - update(); - } - public void removeUpdate(DocumentEvent e) { - update(); - } - }); - scriptTextArea.setMargin(new Insets(5,0,5,5)); - scriptTextArea.setEditable(true); - scriptTextArea.setCursor(null); - scriptTextArea.setTabSize(1); + setLayout(new BorderLayout()); + codeEditor = new JEditorPane(); logTextArea = new JTextArea(12,50); logTextArea.setMargin(new Insets(5,5,5,5)); @@ -231,22 +190,36 @@ public class ScriptRunner extends VisPlugin { } }); - JPanel scriptArea = new JPanel(new BorderLayout()); - scriptArea.setEnabled(false); - scriptArea.add(BorderLayout.WEST, lineTextArea); - scriptArea.add(BorderLayout.CENTER, scriptTextArea); - + doLayout(); JSplitPane centerPanel = new JSplitPane( JSplitPane.VERTICAL_SPLIT, - new JScrollPane(scriptArea), + new JScrollPane(codeEditor), new JScrollPane(logTextArea) ); + + codeEditor.setContentType("text/javascript"); + DefaultSyntaxKit kit = (DefaultSyntaxKit) codeEditor.getEditorKit(); + kit.setProperty("PopupMenu", "copy-to-clipboard,-,find,find-next,goto-line,-,linkfile"); + kit.setProperty("Action.linkfile", JSyntaxLinkFile.class.getName()); + + JPopupMenu p = codeEditor.getComponentPopupMenu(); + for (Component c: p.getComponents()) { + if (c instanceof JMenuItem) { + if (((JMenuItem) c).getAction() != null && + ((JMenuItem) c).getAction() instanceof JSyntaxLinkFile) { + actionLinkFile = (JSyntaxLinkFile)(((JMenuItem) c).getAction()); + actionLinkFile.setMenuText("Link script to disk file"); + actionLinkFile.putValue("ScriptRunner", this); + } + } + } + centerPanel.setOneTouchExpandable(true); centerPanel.setResizeWeight(0.5); JPanel buttonPanel = new JPanel(new BorderLayout()); buttonPanel.add(BorderLayout.CENTER, toggleButton); - buttonPanel.add(BorderLayout.WEST, examplesButton); + buttonPanel.add(BorderLayout.WEST, examplesButton); buttonPanel.add(BorderLayout.EAST, runTestButton); JPanel southPanel = new JPanel(new BorderLayout()); @@ -271,21 +244,43 @@ public class ScriptRunner extends VisPlugin { } } - public void setScriptActive(boolean active) { - /* Reload script from file */ - if (scriptFile != null) { - String script = StringUtils.loadFromFile(scriptFile); - if (script == null) { - logger.fatal("Failed to load script from: " + scriptFile.getAbsolutePath()); - } else { - updateScript(script); - } + public void setLinkFile(File source) { + linkedFile = source; + if (source == null) { + updateScript(""); + + actionLinkFile.setMenuText("Link script to disk file"); + actionLinkFile.putValue("JavascriptSource", null); + + codeEditor.setEditable(true); + } else { + updateScript(linkedFile); + + actionLinkFile.setMenuText("Unlink script: " + source.getName()); + actionLinkFile.putValue("JavascriptSource", source); + + codeEditor.setEditable(false); } - + updateTitle(); + } + + public void setScriptActive(boolean active) { if (active) { + /* setScriptActive(true) */ + /* Free any resources */ setScriptActive(false); + /* Reload script from file */ + if (linkedFile != null) { + String script = StringUtils.loadFromFile(linkedFile); + if (script == null) { + logger.fatal("Failed to load script from: " + linkedFile.getAbsolutePath()); + } else { + updateScript(script); + } + } + /* Create new engine */ engine = new LogScriptEngine(simulation); if (GUI.isVisualized()) { @@ -331,14 +326,14 @@ public class ScriptRunner extends VisPlugin { /* Activate engine */ try { - engine.activateScript(scriptTextArea.getText()); + engine.activateScript(codeEditor.getText()); + actionLinkFile.setEnabled(false); toggleButton.setText("Deactivate"); examplesButton.setEnabled(false); logTextArea.setText(""); - scriptTextArea.setEnabled(false); - setTitle("Contiki Test Editor (ACTIVE) " - + (scriptFile==null?"":" (" + scriptFile.getName() + ")")); + codeEditor.setEnabled(false); + updateTitle(); logger.info("Test script activated"); @@ -355,19 +350,19 @@ public class ScriptRunner extends VisPlugin { } } else { - if (engine == null) { - return; - } + /* setScriptActive(false) */ - /* Deactivate script */ - engine.deactivateScript(); - engine.setScriptLogObserver(null); - engine = null; + if (engine != null) { + /* Deactivate script */ + engine.deactivateScript(); + engine.setScriptLogObserver(null); + engine = null; + } if (logWriter != null) { try { logWriter.write( - "Test ended at simulation time: " + + "Test ended at simulation time: " + (simulation!=null?simulation.getSimulationTime():"?") + "\n"); logWriter.flush(); logWriter.close(); @@ -376,15 +371,26 @@ public class ScriptRunner extends VisPlugin { logWriter = null; } + actionLinkFile.setEnabled(true); toggleButton.setText("Activate"); - examplesButton.setEnabled(true); - scriptTextArea.setEnabled(scriptFile==null?true:false); + examplesButton.setEnabled(linkedFile==null?true:false); + codeEditor.setEnabled(true); logger.info("Test script deactivated"); - setTitle("Contiki Test Editor" - + (scriptFile==null?"":" (" + scriptFile.getName() + ")")); + updateTitle(); } } + private void updateTitle() { + String title = "Contiki Test Editor "; + if (linkedFile != null) { + title += ": " + linkedFile.getName() + " "; + } + if (isActive()) { + title += "(ACTIVE) "; + } + setTitle(title); + } + private void exportAndRun() { /* Save simulation config */ File configFile = simulation.getGUI().doSaveConfig(true); @@ -432,7 +438,7 @@ public class ScriptRunner extends VisPlugin { String s1 = "Start"; String s2 = "Cancel"; int n = JOptionPane.showOptionDialog(GUI.getTopParentContainer(), - "Starting COOJA in " + coojaBuild.getPath() + ":\n" + + "Starting COOJA in " + coojaBuild.getPath() + ":\n" + " " + command[0] + " " + command[1] + " " + command[2] + " " + command[3] + "\n", "Starting COOJA without GUI", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[] { s1, s2 }, s1); @@ -558,7 +564,7 @@ public class ScriptRunner extends VisPlugin { return; } - scriptTextArea.setText(script); + codeEditor.setText(script); logTextArea.setText(""); } @@ -566,29 +572,32 @@ public class ScriptRunner extends VisPlugin { ArrayList config = new ArrayList(); Element element; - if (scriptFile != null) { + if (linkedFile != null) { element = new Element("scriptfile"); - element.setText(simulation.getGUI().createPortablePath(scriptFile).getPath().replace('\\', '/')); + element.setText(simulation.getGUI().createPortablePath(linkedFile).getPath().replace('\\', '/')); config.add(element); /*StringUtils.saveToFile(scriptFile, scriptTextArea.getText());*/ } else { element = new Element("script"); - element.setText(scriptTextArea.getText()); + element.setText(codeEditor.getText()); config.add(element); } element = new Element("active"); - element.setText("" + (engine != null)); + element.setText("" + isActive()); config.add(element); return config; } + public boolean isActive() { + return engine != null; + + } public void closePlugin() { setScriptActive(false); } - private File scriptFile = null; public boolean setConfigXML(Collection configXML, boolean visAvailable) { for (Element element : configXML) { String name = element.getName(); @@ -598,14 +607,7 @@ public class ScriptRunner extends VisPlugin { } } else if ("scriptfile".equals(name)) { File file = simulation.getGUI().restorePortablePath(new File(element.getText().trim())); - String script = StringUtils.loadFromFile(file); - if (script == null) { - logger.fatal("Failed to load script from: " + file.getAbsolutePath()); - } else { - updateScript(script); - } - scriptFile = file; - scriptTextArea.setEnabled(false); + setLinkFile(file); } else if ("active".equals(name)) { boolean active = Boolean.parseBoolean(element.getText()); if (GUI.isVisualized()) { @@ -627,4 +629,45 @@ public class ScriptRunner extends VisPlugin { return StringUtils.loadFromURL(ScriptRunner.class.getResource("/scripts/" + file)); } + public static class JSyntaxLinkFile extends DefaultSyntaxAction { + private static Logger logger = Logger.getLogger(JSyntaxLinkFile.class); + + public JSyntaxLinkFile() { + super("linkfile"); + } + + public void actionPerformed(ActionEvent e) { + JMenuItem menuItem = (JMenuItem) e.getSource(); + Action action = menuItem.getAction(); + ScriptRunner scriptRunner = (ScriptRunner) action.getValue("ScriptRunner"); + File currentSource = (File) action.getValue("JavascriptSource"); + + if (currentSource != null) { + scriptRunner.setLinkFile(null); + return; + } + + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setCurrentDirectory(new java.io.File(".")); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooser.setDialogTitle("Select script file"); + fileChooser.setFileFilter(new FileFilter() { + public boolean accept(File file) { + if (file.isDirectory()) { return true; } + if (file.getName().endsWith(".js")) { + return true; + } + return false; + } + public String getDescription() { + return "Javascript"; + } + }); + if (fileChooser.showOpenDialog(scriptRunner) != JFileChooser.APPROVE_OPTION) { + logger.debug("cancel"); + return; + } + scriptRunner.setLinkFile(fileChooser.getSelectedFile()); + } + } } From 042c75e52ccefe2d0e496d147a071e2835d6d389 Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind Date: Wed, 21 Mar 2012 16:59:42 +0100 Subject: [PATCH 07/14] ensure mouse-triggered event popups are not outside screen, updated to use new watchpoint interface --- .../java/se/sics/cooja/plugins/TimeLine.java | 264 ++++++++++-------- 1 file changed, 148 insertions(+), 116 deletions(-) diff --git a/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java b/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java index 6244740a8..4aefbc074 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java +++ b/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java @@ -88,11 +88,12 @@ import se.sics.cooja.GUI; import se.sics.cooja.Mote; import se.sics.cooja.Plugin; import se.sics.cooja.PluginType; +import se.sics.cooja.SimEventCentral.MoteCountListener; import se.sics.cooja.Simulation; import se.sics.cooja.VisPlugin; import se.sics.cooja.Watchpoint; import se.sics.cooja.WatchpointMote; -import se.sics.cooja.SimEventCentral.MoteCountListener; +import se.sics.cooja.WatchpointMote.WatchpointListener; import se.sics.cooja.interfaces.LED; import se.sics.cooja.interfaces.Radio; import se.sics.cooja.interfaces.Radio.RadioEvent; @@ -100,7 +101,7 @@ import se.sics.cooja.motes.AbstractEmulatedMote; /** * Shows events such as mote logs, LEDs, and radio transmissions, in a timeline. - * + * * @author Fredrik Osterlind */ @ClassDescription("Timeline") @@ -118,13 +119,13 @@ public class TimeLine extends VisPlugin { private double currentPixelDivisor = 200; - private static final long[] ZOOM_LEVELS = { - 1, 2, 5, 10, - 20, 50, 100, 200, 500, 1000, - 2000, 5000, 10000, 20000, 50000, 100000 }; + private static final long[] ZOOM_LEVELS = { + 1, 2, 5, 10, + 20, 50, 100, 200, 500, 1000, + 2000, 5000, 10000, 20000, 50000, 100000 }; private boolean needZoomOut = false; - + private static Logger logger = Logger.getLogger(TimeLine.class); private int paintedMoteHeight = EVENT_PIXEL_HEIGHT; @@ -137,7 +138,7 @@ public class TimeLine extends VisPlugin { private JComponent timeline; private Box eventCheckboxes; private JSplitPane splitPane; - + private Observer moteHighlightObserver = null; private ArrayList highlightedMotes = new ArrayList(); private final static Color HIGHLIGHT_COLOR = Color.CYAN; @@ -162,9 +163,9 @@ public class TimeLine extends VisPlugin { public TimeLine(final Simulation simulation, final GUI gui) { super("Timeline (Add motes to observe by clicking +)", gui); this.simulation = simulation; - + currentPixelDivisor = ZOOM_LEVELS[ZOOM_LEVELS.length/2]; - + /* Box: events to observe */ eventCheckboxes = Box.createVerticalBox(); JCheckBox eventCheckBox; @@ -281,7 +282,7 @@ public class TimeLine extends VisPlugin { for (Mote m: simulation.getMotes()) { addMote(m); } - + /* Update timeline for the duration of the plugin */ repaintTimelineTimer.start(); @@ -351,12 +352,12 @@ public class TimeLine extends VisPlugin { public void actionPerformed(ActionEvent e) { JComponent b = (JComponent) e.getSource(); Mote m = (Mote) b.getClientProperty("mote"); - + /* Sort by distance */ ArrayList sortedMoteEvents = new ArrayList(); for (MoteEvents me: allMoteEvents.toArray(new MoteEvents[0])) { double d = me.mote.getInterfaces().getPosition().getDistanceTo(m); - + int i=0; for (i=0; i < sortedMoteEvents.size(); i++) { double d2 = m.getInterfaces().getPosition().getDistanceTo(sortedMoteEvents.get(i).mote); @@ -365,7 +366,7 @@ public class TimeLine extends VisPlugin { } } sortedMoteEvents.add(i, me); - + } allMoteEvents = sortedMoteEvents; numberMotesWasUpdated(); @@ -376,7 +377,7 @@ public class TimeLine extends VisPlugin { public void actionPerformed(ActionEvent e) { JComponent b = (JComponent) e.getSource(); Mote m = (Mote) b.getClientProperty("mote"); - + /* Sort by distance */ MoteEvents mEvent = null; for (MoteEvents me: allMoteEvents.toArray(new MoteEvents[0])) { @@ -441,7 +442,7 @@ public class TimeLine extends VisPlugin { int leftPixel = (int) (focusTime/currentPixelDivisor - focusCenter*w); Rectangle r = new Rectangle( - leftPixel, 0, + leftPixel, 0, w, 1 ); @@ -456,7 +457,7 @@ public class TimeLine extends VisPlugin { } }); } - + private Action zoomInAction = new AbstractAction("Zoom in (Ctrl+)") { private static final long serialVersionUID = -2592452356547803615L; public void actionPerformed(ActionEvent e) { @@ -476,13 +477,13 @@ public class TimeLine extends VisPlugin { if (currentPixelDivisor <= ZOOM_LEVELS[zoomLevel]) break; zoomLevel++; } - + if (zoomLevel > 0) { zoomLevel--; /* zoom in */ } currentPixelDivisor = ZOOM_LEVELS[zoomLevel]; logger.info("Zoom level: " + currentPixelDivisor + " microseconds/pixel " + ((zoomLevel==0)?"(MIN)":"")); - + forceRepaintAndFocus(centerTime, 0.5); } }; @@ -516,7 +517,7 @@ public class TimeLine extends VisPlugin { forceRepaintAndFocus(centerTime, 0.5); } }; - + private Action zoomSliderAction = new AbstractAction("Zoom slider (Ctrl+Mouse)") { private static final long serialVersionUID = -4288046377707363837L; public void actionPerformed(ActionEvent e) { @@ -532,27 +533,27 @@ public class TimeLine extends VisPlugin { zoomSlider.setPaintLabels(false); final long centerTime = (long) (popupLocation.x*currentPixelDivisor); - + zoomSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { int zoomLevel = zoomSlider.getValue(); - + currentPixelDivisor = ZOOM_LEVELS[zoomLevel]; logger.info("Zoom level: " + currentPixelDivisor + " microseconds/pixel " + ((zoomLevel==ZOOM_LEVELS.length-1)?"(MAX)":"")); forceRepaintAndFocus(centerTime, 0.5); } }); - + final JPopupMenu zoomPopup = new JPopupMenu(); zoomPopup.add(zoomSlider); zoomPopup.show(TimeLine.this, TimeLine.this.getWidth()/2, 0); zoomSlider.requestFocus(); } }; - + /** - * Save logged raw data to file for post-processing. + * Save logged raw data to file for post-processing. */ private Action saveDataAction = new AbstractAction("Save raw data to file") { private static final long serialVersionUID = 975176793514425718L; @@ -641,15 +642,15 @@ public class TimeLine extends VisPlugin { } repaint(); } - - + + private class MoteStatistics { Mote mote; long onTimeRedLED = 0, onTimeGreenLED = 0, onTimeBlueLED = 0; int nrLogs = 0; long radioOn = 0; long onTimeRX = 0, onTimeTX = 0, onTimeInterfered = 0; - + public String toString() { return toString(true, true, true, true); } @@ -685,7 +686,7 @@ public class TimeLine extends VisPlugin { logger.info(extractStatistics()); } }; - + public String extractStatistics() { return extractStatistics(true, true, true, true); } @@ -743,7 +744,7 @@ public class TimeLine extends VisPlugin { stats.nrLogs++; } } - + /* TODO Radio channels */ if (radioHW) { @@ -760,7 +761,7 @@ public class TimeLine extends VisPlugin { } } } - + if (radioRXTX) { for (MoteEvent ev: moteEvents.radioRXTXEvents) { if (!(ev instanceof RadioRXTXEvent)) continue; @@ -790,7 +791,7 @@ public class TimeLine extends VisPlugin { } } } - + /* TODO Watchpoints */ output.append(stats.toString(logs, leds, radioHW, radioRXTX)); @@ -799,7 +800,7 @@ public class TimeLine extends VisPlugin { if (allStats.size() == 0) { return output.toString(); } - + /* Average */ MoteStatistics average = new MoteStatistics(); for (MoteStatistics stats: allStats) { @@ -822,7 +823,7 @@ public class TimeLine extends VisPlugin { output.append(average.toString(logs, leds, radioHW, radioRXTX)); return output.toString(); } - + public void trySelectTime(final long toTime) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { @@ -841,23 +842,23 @@ public class TimeLine extends VisPlugin { forceRepaintAndFocus(toTime, 0.5, false); } - }); + }); } - + private Action radioLoggerAction = new AbstractAction("in Radio Logger") { private static final long serialVersionUID = 7690116136861949864L; public void actionPerformed(ActionEvent e) { if (popupLocation == null) { return; } - long time = (long) ((double)popupLocation.x*currentPixelDivisor); + long time = (long) (popupLocation.x*currentPixelDivisor); Plugin[] plugins = simulation.getGUI().getStartedPlugins(); for (Plugin p: plugins) { if (!(p instanceof RadioLogger)) { continue; } - + /* Select simulation time */ RadioLogger plugin = (RadioLogger) p; plugin.trySelectTime(time); @@ -870,14 +871,14 @@ public class TimeLine extends VisPlugin { if (popupLocation == null) { return; } - long time = (long) ((double)popupLocation.x*currentPixelDivisor); + long time = (long) (popupLocation.x*currentPixelDivisor); Plugin[] plugins = simulation.getGUI().getStartedPlugins(); for (Plugin p: plugins) { if (!(p instanceof LogListener)) { continue; } - + /* Select simulation time */ LogListener plugin = (LogListener) p; plugin.trySelectTime(time); @@ -929,8 +930,8 @@ public class TimeLine extends VisPlugin { private Mote mote; private WatchpointMote watchpointMote; /* XXX */ - private ActionListener watchpointListener; /* XXX */ - + private WatchpointListener watchpointListener; /* XXX */ + public MoteObservation(Mote mote, Observable observable, Observer observer) { this.mote = mote; this.observable = observable; @@ -938,12 +939,12 @@ public class TimeLine extends VisPlugin { } /* XXX Special case, should be generalized */ - public MoteObservation(Mote mote, WatchpointMote watchpointMote, ActionListener listener) { + public MoteObservation(Mote mote, WatchpointMote watchpointMote, WatchpointListener listener) { this.mote = mote; this.watchpointMote = watchpointMote; this.watchpointListener = listener; } - + public Mote getMote() { return mote; } @@ -958,7 +959,7 @@ public class TimeLine extends VisPlugin { observable = null; observer = null; } - + /* XXX */ if (watchpointMote != null) { watchpointMote.removeWatchpointListener(watchpointListener); @@ -971,7 +972,7 @@ public class TimeLine extends VisPlugin { private void addMoteObservers(final Mote mote, final MoteEvents moteEvents) { /* TODO Log: final Log moteLog = mote.getInterfaces().getLog(); */ /* TODO Unknown state event */ - + /* LEDs */ final LED moteLEDs = mote.getInterfaces().getLED(); if (moteLEDs != null) { @@ -1021,7 +1022,7 @@ public class TimeLine extends VisPlugin { return; } lastChannel = nowChannel; - + RadioHWEvent ev = new RadioHWEvent( simulation.getSimulationTime(), moteRadio.isReceiverOn()); if (radioChannels) { @@ -1067,7 +1068,7 @@ public class TimeLine extends VisPlugin { radioEv == RadioEvent.RECEPTION_STARTED || radioEv == RadioEvent.RECEPTION_INTERFERED || radioEv == RadioEvent.RECEPTION_FINISHED) { - + RadioRXTXEvent ev; /* Override events, instead show state */ if (moteRadio.isTransmitting()) { @@ -1086,9 +1087,9 @@ public class TimeLine extends VisPlugin { ev = new RadioRXTXEvent( simulation.getSimulationTime(), RXTXRadioEvent.IDLE); } - + moteEvents.addRadioRXTX(ev); - + if (executionDetails && mote instanceof AbstractEmulatedMote) { String details = ((AbstractEmulatedMote) mote).getExecutionDetails(); if (details != null) { @@ -1106,22 +1107,26 @@ public class TimeLine extends VisPlugin { moteRadio.addObserver(observer); activeMoteObservers.add(new MoteObservation(mote, moteRadio, observer)); } - + /* XXX Experimental: Watchpoints */ if (mote instanceof WatchpointMote) { final WatchpointMote watchpointMote = ((WatchpointMote)mote); - ActionListener listener = new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (watchpointMote.getLastWatchpoint() == null) { - return; + WatchpointListener listener = new WatchpointListener() { + public void watchpointTriggered(Watchpoint watchpoint) { + WatchpointEvent ev = new WatchpointEvent(simulation.getSimulationTime(), watchpoint); + + if (executionDetails && mote instanceof AbstractEmulatedMote) { + String details = ((AbstractEmulatedMote) mote).getExecutionDetails(); + if (details != null) { + details = "
" + details.replace("\n", "
"); + ev.details = details; + } } - WatchpointEvent ev = new WatchpointEvent( - simulation.getSimulationTime(), - watchpointMote.getLastWatchpoint() - ); moteEvents.addWatchpoint(ev); } + public void watchpointsChanged() { + } }; watchpointMote.addWatchpointListener(listener); @@ -1209,7 +1214,7 @@ public class TimeLine extends VisPlugin { } simulation.getEventCentral().removeMoteCountListener(newMotesListener); - + /* Remove active mote interface observers */ for (MoteObservation o: activeMoteObservers) { o.dispose(); @@ -1295,7 +1300,7 @@ public class TimeLine extends VisPlugin { for (MoteEvents moteEvents: allMoteEventsArr) { removeMote(moteEvents.mote); } - + for (Element element : configXML) { String name = element.getName(); if ("mote".equals(name)) { @@ -1349,20 +1354,20 @@ public class TimeLine extends VisPlugin { return true; } - + private int mousePixelPositionX = -1; private int mousePixelPositionY = -1; - private int mouseDownPixelPositionX = -1; + private int mouseDownPixelPositionX = -1; class Timeline extends JComponent { private static final long serialVersionUID = 2206491823778169359L; public Timeline() { setLayout(null); setToolTipText(null); setBackground(COLOR_BACKGROUND); - + addMouseListener(mouseAdapter); addMouseMotionListener(mouseAdapter); - + /* Popup menu */ final JPopupMenu popupMenu = new JPopupMenu(); @@ -1379,7 +1384,7 @@ public class TimeLine extends VisPlugin { popupMenu.add(new JMenuItem(saveDataAction)); popupMenu.add(new JMenuItem(statisticsAction)); popupMenu.add(new JMenuItem(clearAction)); - + popupMenu.addSeparator(); JMenu focusMenu = new JMenu("Show in"); @@ -1417,7 +1422,7 @@ public class TimeLine extends VisPlugin { popupLocation = e.getPoint(); showInAllAction.actionPerformed(null); - long time = (long) ((double)popupLocation.x*currentPixelDivisor); + long time = (long) (popupLocation.x*currentPixelDivisor); Plugin[] plugins = simulation.getGUI().getStartedPlugins(); for (Plugin p: plugins) { if (!(p instanceof TimeLine)) { @@ -1486,7 +1491,7 @@ public class TimeLine extends VisPlugin { forceRepaintAndFocus(zoomCenterTime, zoomCenter); return; } - + if (mousePixelPositionX >= 0) { mousePixelPositionX = e.getX(); mousePixelPositionY = e.getY(); @@ -1507,7 +1512,7 @@ public class TimeLine extends VisPlugin { zoomCenterTime = (long) (e.getX()*currentPixelDivisor); return; } - + if (popUpToolTip != null) { popUpToolTip.hide(); popUpToolTip = null; @@ -1524,7 +1529,34 @@ public class TimeLine extends VisPlugin { if (t.getTipText() == null || t.getTipText().equals("")) { return; } - popUpToolTip = PopupFactory.getSharedInstance().getPopup(timeline, t, e.getXOnScreen(), e.getYOnScreen()); + t.validate(); + + /* Check tooltip width */ + Rectangle screenBounds = timeline.getGraphicsConfiguration().getBounds(); + int x; + { + int tooltip = e.getLocationOnScreen().x + t.getPreferredSize().width; + int screen = screenBounds.x + screenBounds.width; + if (tooltip > screen) { + x = e.getLocationOnScreen().x - (tooltip-screen); + } else { + x = e.getLocationOnScreen().x; + } + } + + /* Check tooltip height */ + int y; + { + int tooltip = e.getLocationOnScreen().y + t.getPreferredSize().height; + int screen = screenBounds.y + screenBounds.height; + if (tooltip > screen) { + y = e.getLocationOnScreen().y - (tooltip-screen); + } else { + y = e.getLocationOnScreen().y; + } + } + + popUpToolTip = PopupFactory.getSharedInstance().getPopup(null, t, x, y); popUpToolTip.show(); } } @@ -1544,7 +1576,7 @@ public class TimeLine extends VisPlugin { public void paintComponent(Graphics g) { Rectangle bounds = g.getClipBounds(); /*logger.info("Clip bounds: " + bounds);*/ - + if (needZoomOut) { /* Need zoom out */ g.setColor(Color.RED); @@ -1556,12 +1588,12 @@ public class TimeLine extends VisPlugin { FontMetrics fm = g.getFontMetrics(); int msgWidth = fm.stringWidth(msg); int msgHeight = fm.getHeight(); - g.drawString(msg, + g.drawString(msg, vis.x + vis.width/2 - msgWidth/2, vis.y + vis.height/2 + msgHeight/2); return; } - + long intervalStart = (long)(bounds.x*currentPixelDivisor); long intervalEnd = (long) (intervalStart + bounds.width*currentPixelDivisor); @@ -1584,12 +1616,12 @@ public class TimeLine extends VisPlugin { int lineHeightOffset = FIRST_MOTE_PIXEL_OFFSET; boolean dark = true; for (int mIndex = 0; mIndex < allMoteEvents.size(); mIndex++) { - + /* Mote separators */ if (dark) { g.setColor(SEPARATOR_COLOR); g.fillRect( - 0, lineHeightOffset-2, + 0, lineHeightOffset-2, getWidth(), paintedMoteHeight ); } @@ -1680,23 +1712,23 @@ public class TimeLine extends VisPlugin { while (time <= end) { if (time % (100*Simulation.MILLISECOND) == 0) { g.drawLine( - (int) (time/currentPixelDivisor), (int)0, - (int) (time/currentPixelDivisor), (int)TIME_MARKER_PIXEL_HEIGHT); + (int) (time/currentPixelDivisor), 0, + (int) (time/currentPixelDivisor), TIME_MARKER_PIXEL_HEIGHT); } else { g.drawLine( - (int) (time/currentPixelDivisor), (int)0, - (int) (time/currentPixelDivisor), (int)TIME_MARKER_PIXEL_HEIGHT/2); - } + (int) (time/currentPixelDivisor), 0, + (int) (time/currentPixelDivisor), TIME_MARKER_PIXEL_HEIGHT/2); + } time += (10*Simulation.MILLISECOND); } } private void drawMouseTime(Graphics g, long start, long end) { if (mousePixelPositionX >= 0) { - long time = (long) ((double)mousePixelPositionX*currentPixelDivisor); - long diff = (long) ((double)Math.abs(mouseDownPixelPositionX-mousePixelPositionX)*currentPixelDivisor); - String str = - "Time (ms): " + (double)time/Simulation.MILLISECOND + + long time = (long) (mousePixelPositionX*currentPixelDivisor); + long diff = (long) (Math.abs(mouseDownPixelPositionX-mousePixelPositionX)*currentPixelDivisor); + String str = + "Time (ms): " + (double)time/Simulation.MILLISECOND + " (" + (double)diff/Simulation.MILLISECOND + ")"; int h = g.getFontMetrics().getHeight(); @@ -1707,21 +1739,21 @@ public class TimeLine extends VisPlugin { /* Line */ g.setColor(Color.GRAY); g.drawLine( - mousePixelPositionX, 0, + mousePixelPositionX, 0, mousePixelPositionX, getHeight()); /* Text box */ g.setColor(Color.DARK_GRAY); g.fillRect( - mousePixelPositionX-delta, y, + mousePixelPositionX-delta, y, w, h); g.setColor(Color.BLACK); g.drawRect( - mousePixelPositionX-delta, y, + mousePixelPositionX-delta, y, w, h); g.setColor(Color.WHITE); - g.drawString(str, - mousePixelPositionX+3-delta, + g.drawString(str, + mousePixelPositionX+3-delta, y+h-1); } } @@ -1738,7 +1770,7 @@ public class TimeLine extends VisPlugin { int mote = (event.getPoint().y-FIRST_MOTE_PIXEL_OFFSET)/paintedMoteHeight; if (mote < 0 || mote >= allMoteEvents.size()) { return null; - } + } String tooltip = "Mote: " + allMoteEvents.get(mote).mote + "
"; /* Time */ @@ -1789,7 +1821,7 @@ public class TimeLine extends VisPlugin { MoteEvent ev = getFirstIntervalEvent(events, time); if (ev != null && time >= ev.time) { tooltip += ev + "
"; - + if (ev.details != null) { tooltip += "Details:
" + ev.details; } @@ -1846,7 +1878,7 @@ public class TimeLine extends VisPlugin { private Mote getMote(Point p) { if (p.y < FIRST_MOTE_PIXEL_OFFSET) { - return null; + return null; } int m = (p.y-FIRST_MOTE_PIXEL_OFFSET)/paintedMoteHeight; if (m < allMoteEvents.size()) { @@ -1899,9 +1931,9 @@ public class TimeLine extends VisPlugin { /** * Used by the default paint method to color events. * The event is not painted if the returned color is null. - * + * * @see #paintInterval(Graphics, int, long) - * @return Event color or null + * @return Event color or null */ public abstract Color getEventColor(); @@ -1937,7 +1969,7 @@ public class TimeLine extends VisPlugin { g.setColor(color); g.fillRect( - (int)(ev.time/currentPixelDivisor), lineHeightOffset, + (int)(ev.time/currentPixelDivisor), lineHeightOffset, w, EVENT_PIXEL_HEIGHT ); @@ -1987,7 +2019,7 @@ public class TimeLine extends VisPlugin { if (state == RXTXRadioEvent.IDLE) { return "Radio idle from " + time + "
"; } else if (state == RXTXRadioEvent.TRANSMITTING) { - return "Radio transmitting from " + time + "
"; + return "Radio transmitting from " + time + "
"; } else if (state == RXTXRadioEvent.RECEIVING) { return "Radio receiving from " + time + "
"; } else if (state == RXTXRadioEvent.INTERFERED) { @@ -2007,18 +2039,18 @@ public class TimeLine extends VisPlugin { } /* TODO Which colors? */ - private final static Color[] CHANNEL_COLORS = new Color[] { - Color.decode("0x008080"), Color.decode("0x808080"), Color.decode("0xC00000"), - Color.decode("0x000020"), Color.decode("0x202000"), Color.decode("0x200020"), + private final static Color[] CHANNEL_COLORS = new Color[] { + Color.decode("0x008080"), Color.decode("0x808080"), Color.decode("0xC00000"), + Color.decode("0x000020"), Color.decode("0x202000"), Color.decode("0x200020"), Color.decode("0x002020"), Color.decode("0x202020"), Color.decode("0x006060"), - Color.decode("0x606060"), Color.decode("0xA00000"), Color.decode("0x00A000"), + Color.decode("0x606060"), Color.decode("0xA00000"), Color.decode("0x00A000"), Color.decode("0x0000A0"), Color.decode("0x400040"), Color.decode("0x004040"), Color.decode("0x404040"), Color.decode("0x200000"), Color.decode("0x002000"), - Color.decode("0xA0A000"), Color.decode("0xA000A0"), Color.decode("0x00A0A0"), + Color.decode("0xA0A000"), Color.decode("0xA000A0"), Color.decode("0x00A0A0"), Color.decode("0xA0A0A0"), Color.decode("0xE00000"), Color.decode("0x600000"), - Color.decode("0x000040"), Color.decode("0x404000"), Color.decode("0xFF0000"), - Color.decode("0x00FF00"), Color.decode("0x0000FF"), Color.decode("0xFFFF00"), - Color.decode("0xFF00FF"), Color.decode("0x808000"), Color.decode("0x800080"), + Color.decode("0x000040"), Color.decode("0x404000"), Color.decode("0xFF0000"), + Color.decode("0x00FF00"), Color.decode("0x0000FF"), Color.decode("0xFFFF00"), + Color.decode("0xFF00FF"), Color.decode("0x808000"), Color.decode("0x800080"), }; class RadioHWEvent extends MoteEvent { boolean on; @@ -2098,21 +2130,21 @@ public class TimeLine extends VisPlugin { if (color.getRed() > 0) { g.setColor(new Color(color.getRed(), 0, 0)); g.fillRect( - (int)(ev.time/currentPixelDivisor), lineHeightOffset, + (int)(ev.time/currentPixelDivisor), lineHeightOffset, w, LED_PIXEL_HEIGHT ); } if (color.getGreen() > 0) { g.setColor(new Color(0, color.getGreen(), 0)); g.fillRect( - (int)(ev.time/currentPixelDivisor), lineHeightOffset+LED_PIXEL_HEIGHT, + (int)(ev.time/currentPixelDivisor), lineHeightOffset+LED_PIXEL_HEIGHT, w, LED_PIXEL_HEIGHT ); } if (color.getBlue() > 0) { g.setColor(new Color(0, 0, color.getBlue())); g.fillRect( - (int)(ev.time/currentPixelDivisor), lineHeightOffset+2*LED_PIXEL_HEIGHT, + (int)(ev.time/currentPixelDivisor), lineHeightOffset+2*LED_PIXEL_HEIGHT, w, LED_PIXEL_HEIGHT ); } @@ -2120,7 +2152,7 @@ public class TimeLine extends VisPlugin { } } public String toString() { - return + return "LED state:
" + "Red = " + (red?"ON":"OFF") + "
" + "Green = " + (green?"ON":"OFF") + "
" + @@ -2151,7 +2183,7 @@ public class TimeLine extends VisPlugin { public String toString() { String desc = watchpoint.getDescription(); desc = desc.replace("\n", "
"); - return + return "Watchpoint triggered at time (ms): " + time/Simulation.MILLISECOND + ".
" + desc + "
"; } @@ -2171,7 +2203,7 @@ public class TimeLine extends VisPlugin { g.setColor(color); g.fillRect( - (int)(ev.time/currentPixelDivisor), lineHeightOffset, + (int)(ev.time/currentPixelDivisor), lineHeightOffset, w, EVENT_PIXEL_HEIGHT ); @@ -2184,7 +2216,7 @@ public class TimeLine extends VisPlugin { ArrayList radioRXTXEvents; ArrayList radioChannelEvents; ArrayList radioHWEvents; - ArrayList ledEvents; + ArrayList ledEvents; ArrayList logEvents; ArrayList watchpointEvents; @@ -2245,7 +2277,7 @@ public class TimeLine extends VisPlugin { watchpointEvents.add(lastWatchpointEvent); } } - + public void addRadioRXTX(RadioRXTXEvent ev) { /* Link with previous events */ if (lastRadioRXTXEvent != null) { @@ -2317,7 +2349,7 @@ public class TimeLine extends VisPlugin { if (now == lastRepaintSimulationTime) { return; } - lastRepaintSimulationTime = now; + lastRepaintSimulationTime = now; /* Update timeline size */ int newWidth; @@ -2330,10 +2362,10 @@ public class TimeLine extends VisPlugin { needZoomOut = false; } - Rectangle visibleRectangle = timeline.getVisibleRect(); + Rectangle visibleRectangle = timeline.getVisibleRect(); boolean isTracking = visibleRectangle.x + visibleRectangle.width >= timeline.getWidth(); - int newHeight = (int) (FIRST_MOTE_PIXEL_OFFSET + paintedMoteHeight * allMoteEvents.size()); + int newHeight = (FIRST_MOTE_PIXEL_OFFSET + paintedMoteHeight * allMoteEvents.size()); timeline.setPreferredSize(new Dimension( newWidth, newHeight @@ -2348,7 +2380,7 @@ public class TimeLine extends VisPlugin { /* Update visible rectangle */ if (isTracking) { Rectangle r = new Rectangle( - newWidth-1, visibleRectangle.y, + newWidth-1, visibleRectangle.y, 1, 1); timeline.scrollRectToVisible(r); } From d57904b28968a30a6067bbcfa2f657b0b8617e17 Mon Sep 17 00:00:00 2001 From: Matthias Kovatsch Date: Wed, 21 Mar 2012 21:27:52 +0100 Subject: [PATCH 08/14] Fixed bug in IPv6 address print function. --- platform/minimal-net/contiki-main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/platform/minimal-net/contiki-main.c b/platform/minimal-net/contiki-main.c index cf09c2718..64301d38f 100644 --- a/platform/minimal-net/contiki-main.c +++ b/platform/minimal-net/contiki-main.c @@ -140,6 +140,7 @@ sprint_ip6(uip_ip6addr_t addr) unsigned char i = 0; unsigned char zerocnt = 0; unsigned char numprinted = 0; + unsigned char notskipped = 0; char thestring[40]; char *result = thestring; @@ -149,10 +150,11 @@ sprint_ip6(uip_ip6addr_t addr) while(addr.u16[zerocnt + i] == 0) { zerocnt++; } - if(zerocnt == 1) { + if(zerocnt == 1 && notskipped) { *result++ = '0'; numprinted++; - break; + notskipped = 1; + continue; } i += zerocnt; numprinted += zerocnt; @@ -290,7 +292,7 @@ main(void) { uint8_t i; for(i = 0; i < UIP_DS6_ADDR_NB; i++) { - if(uip_ds6_if.addr_list[i].isused) { + if(uip_ds6_if.addr_list[i].isused) { printf("IPV6 Addresss: "); sprint_ip6(uip_ds6_if.addr_list[i].ipaddr); printf("\n"); From a9e36b03657b5074590337085b1e605236354a9e Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind Date: Fri, 23 Mar 2012 09:55:36 +0100 Subject: [PATCH 09/14] quickfix to allow running in headless mode --- .../se/sics/cooja/plugins/ScriptRunner.java | 67 ++++++++++++------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java b/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java index 278bde825..4acb1b165 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java +++ b/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java @@ -34,6 +34,7 @@ package se.sics.cooja.plugins; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; +import java.awt.GraphicsEnvironment; import java.awt.Insets; import java.awt.Window; import java.awt.event.ActionEvent; @@ -89,8 +90,13 @@ public class ScriptRunner extends VisPlugin { private static final long serialVersionUID = 7614358340336799109L; private static Logger logger = Logger.getLogger(ScriptRunner.class); + static boolean headless; { - DefaultSyntaxKit.initKit(); + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + headless = ge.isHeadless(); + if (!headless) { + DefaultSyntaxKit.initKit(); + } } final String[] EXAMPLE_SCRIPTS = new String[] { @@ -158,9 +164,11 @@ public class ScriptRunner extends VisPlugin { JEditorPane e = new JEditorPane(); new JScrollPane(e); e.setContentType("text/javascript"); - DefaultSyntaxKit kit = (DefaultSyntaxKit) e.getEditorKit(); - kit.setProperty("PopupMenu", "copy-to-clipboard,-,find,find-next,goto-line,-,linkfile"); - kit.setProperty("Action.linkfile", JSyntaxLinkFile.class.getName()); + if (e.getEditorKit() instanceof DefaultSyntaxKit) { + DefaultSyntaxKit kit = (DefaultSyntaxKit) e.getEditorKit(); + kit.setProperty("PopupMenu", "copy-to-clipboard,-,find,find-next,goto-line,-,linkfile"); + kit.setProperty("Action.linkfile", JSyntaxLinkFile.class.getName()); + } } /* Script area */ @@ -198,18 +206,22 @@ public class ScriptRunner extends VisPlugin { ); codeEditor.setContentType("text/javascript"); - DefaultSyntaxKit kit = (DefaultSyntaxKit) codeEditor.getEditorKit(); - kit.setProperty("PopupMenu", "copy-to-clipboard,-,find,find-next,goto-line,-,linkfile"); - kit.setProperty("Action.linkfile", JSyntaxLinkFile.class.getName()); + if (codeEditor.getEditorKit() instanceof DefaultSyntaxKit) { + DefaultSyntaxKit kit = (DefaultSyntaxKit) codeEditor.getEditorKit(); + kit.setProperty("PopupMenu", "copy-to-clipboard,-,find,find-next,goto-line,-,linkfile"); + kit.setProperty("Action.linkfile", JSyntaxLinkFile.class.getName()); + } JPopupMenu p = codeEditor.getComponentPopupMenu(); - for (Component c: p.getComponents()) { - if (c instanceof JMenuItem) { - if (((JMenuItem) c).getAction() != null && - ((JMenuItem) c).getAction() instanceof JSyntaxLinkFile) { - actionLinkFile = (JSyntaxLinkFile)(((JMenuItem) c).getAction()); - actionLinkFile.setMenuText("Link script to disk file"); - actionLinkFile.putValue("ScriptRunner", this); + if (p != null) { + for (Component c: p.getComponents()) { + if (c instanceof JMenuItem) { + if (((JMenuItem) c).getAction() != null && + ((JMenuItem) c).getAction() instanceof JSyntaxLinkFile) { + actionLinkFile = (JSyntaxLinkFile)(((JMenuItem) c).getAction()); + actionLinkFile.setMenuText("Link script to disk file"); + actionLinkFile.putValue("ScriptRunner", this); + } } } } @@ -328,12 +340,14 @@ public class ScriptRunner extends VisPlugin { try { engine.activateScript(codeEditor.getText()); - actionLinkFile.setEnabled(false); - toggleButton.setText("Deactivate"); - examplesButton.setEnabled(false); - logTextArea.setText(""); - codeEditor.setEnabled(false); - updateTitle(); + if (!headless) { + actionLinkFile.setEnabled(false); + toggleButton.setText("Deactivate"); + examplesButton.setEnabled(false); + logTextArea.setText(""); + codeEditor.setEnabled(false); + updateTitle(); + } logger.info("Test script activated"); @@ -371,12 +385,14 @@ public class ScriptRunner extends VisPlugin { logWriter = null; } - actionLinkFile.setEnabled(true); - toggleButton.setText("Activate"); - examplesButton.setEnabled(linkedFile==null?true:false); - codeEditor.setEnabled(true); + if (!headless) { + actionLinkFile.setEnabled(true); + toggleButton.setText("Activate"); + examplesButton.setEnabled(linkedFile==null?true:false); + codeEditor.setEnabled(true); + updateTitle(); + } logger.info("Test script deactivated"); - updateTitle(); } } @@ -429,6 +445,7 @@ public class ScriptRunner extends VisPlugin { String command[] = { "java", + "-Djava.awt.headless=true", "-jar", "../dist/cooja.jar", "-nogui=" + configFile.getAbsolutePath() From c78b5bad5cc7281185ffb1f860a5a4a9472adde6 Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind Date: Fri, 23 Mar 2012 15:14:24 +0100 Subject: [PATCH 10/14] some bugfixes regarding timeouts in test scripts, with simplified code --- tools/cooja/java/se/sics/cooja/GUI.java | 145 ++++----- .../sics/cooja/plugins/LogScriptEngine.java | 291 +++++++++--------- .../se/sics/cooja/plugins/ScriptRunner.java | 35 ++- 3 files changed, 246 insertions(+), 225 deletions(-) diff --git a/tools/cooja/java/se/sics/cooja/GUI.java b/tools/cooja/java/se/sics/cooja/GUI.java index 091592634..3d2227f8e 100644 --- a/tools/cooja/java/se/sics/cooja/GUI.java +++ b/tools/cooja/java/se/sics/cooja/GUI.java @@ -34,6 +34,7 @@ import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dialog; +import java.awt.Dialog.ModalityType; import java.awt.Dimension; import java.awt.Frame; import java.awt.GraphicsDevice; @@ -41,7 +42,6 @@ import java.awt.GraphicsEnvironment; import java.awt.Point; import java.awt.Rectangle; import java.awt.Window; -import java.awt.Dialog.ModalityType; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; @@ -271,7 +271,7 @@ public class GUI extends Observable { "COMMAND_DATA_START", "COMMAND_DATA_END", "COMMAND_BSS_START", "COMMAND_BSS_END", "COMMAND_COMMON_START", "COMMAND_COMMON_END", - + "HIDE_WARNINGS" }; @@ -299,7 +299,7 @@ public class GUI extends Observable { private Vector startedPlugins = new Vector(); private ArrayList guiActions = new ArrayList(); - + // Platform configuration variables // Maintained via method reparseProjectConfig() private ProjectConfig projectConfig; @@ -380,12 +380,12 @@ public class GUI extends Observable { quickHelpScroll = new JScrollPane(quickHelpTextPane, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); quickHelpScroll.setPreferredSize(new Dimension(200, 0)); quickHelpScroll.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(Color.GRAY), + BorderFactory.createLineBorder(Color.GRAY), BorderFactory.createEmptyBorder(0, 3, 0, 0) )); quickHelpScroll.setVisible(false); loadQuickHelp("KEYBOARD_SHORTCUTS"); - + // Load default and overwrite with user settings (if any) loadExternalToolsDefaultSettings(); loadExternalToolsUserSettings(); @@ -597,7 +597,7 @@ public class GUI extends Observable { menu.add(lastItem); } } - + private void doLoadConfigAsync(final boolean ask, final boolean quick, final File file) { new Thread(new Runnable() { public void run() { @@ -646,7 +646,7 @@ public class GUI extends Observable { for (GUIAction a: guiActions) { a.setEnabled(a.shouldBeEnabled()); } - + /* Mote and mote type menues */ if (menuMoteTypeClasses != null) { menuMoteTypeClasses.setEnabled(getSimulation() != null); @@ -655,7 +655,7 @@ public class GUI extends Observable { menuMoteTypes.setEnabled(getSimulation() != null); } } - + private JMenuBar createMenuBar() { JMenuBar menuBar = new JMenuBar(); JMenu menu; @@ -673,7 +673,7 @@ public class GUI extends Observable { guiActions.add(startStopSimulationAction); guiActions.add(removeAllMotesAction); guiActions.add(showBufferSettingsAction); - + /* File menu */ menu = new JMenu("File"); menu.addMenuListener(new MenuListener() { @@ -733,7 +733,7 @@ public class GUI extends Observable { }); menu.setMnemonic(KeyEvent.VK_S); menuBar.add(menu); - + menu.add(new JMenuItem(startStopSimulationAction)); GUIAction guiAction = new StartPluginGUIAction("Control panel"); @@ -929,7 +929,7 @@ public class GUI extends Observable { menuItem.setEnabled(false); menuItem.setToolTipText("Not available in applet version"); } - + menuItem = new JMenuItem("Contiki mote configuration wizard"); menuItem.setActionCommand("configuration wizard"); menuItem.addActionListener(guiEventHandler); @@ -940,7 +940,7 @@ public class GUI extends Observable { } menu.add(new JMenuItem(showBufferSettingsAction)); - + /* Help */ menu = new JMenu("Help"); menu.setMnemonic(KeyEvent.VK_H); @@ -950,7 +950,7 @@ public class GUI extends Observable { menu.add(checkBox); menuBar.add(menu); - menu.addSeparator(); + menu.addSeparator(); menuItem = new JMenuItem("Java version: " + System.getProperty("java.version") + " (" @@ -965,7 +965,7 @@ public class GUI extends Observable { + System.getProperty("sun.arch.data.model")); menuItem.setEnabled(false); menu.add(menuItem); - + // Mote plugins popup menu (not available via menu bar) if (menuMotePluginClasses == null) { menuMotePluginClasses = new Vector>(); @@ -1356,7 +1356,7 @@ public class GUI extends Observable { unregisterPositioners(); unregisterRadioMediums(); projectDirClassLoader = null; - + /* Build cooja configuration */ try { projectConfig = new ProjectConfig(true); @@ -1381,7 +1381,7 @@ public class GUI extends Observable { "Error when reading project config: " + e.getMessage()).initCause(e); } } - + /* Create project class loader */ try { projectDirClassLoader = createClassLoader(currentProjects); @@ -1585,7 +1585,7 @@ public class GUI extends Observable { * Same as the {@link #startPlugin(Class, GUI, Simulation, Mote)} method, * but does not throw exceptions. If COOJA is visualised, an error dialog * is shown if plugin could not be started. - * + * * @see #startPlugin(Class, GUI, Simulation, Mote) * @param pluginClass Plugin class * @param argGUI Plugin GUI argument @@ -1609,7 +1609,7 @@ public class GUI extends Observable { return null; } } while (cause != null && (cause=cause.getCause()) != null); - + logger.fatal("Error when starting plugin", ex); } } @@ -1622,15 +1622,15 @@ public class GUI extends Observable { } public Plugin startPlugin(final Class pluginClass, - final GUI argGUI, final Simulation argSimulation, final Mote argMote) - throws PluginConstructionException + final GUI argGUI, final Simulation argSimulation, final Mote argMote) + throws PluginConstructionException { return startPlugin(pluginClass, argGUI, argSimulation, argMote, true); } /** * Starts given plugin. If visualized, the plugin is also shown. - * + * * @see PluginType * @param pluginClass Plugin class * @param argGUI Plugin GUI argument @@ -1665,7 +1665,7 @@ public class GUI extends Observable { throw new PluginConstructionException("No mote argument for mote plugin"); } - plugin = + plugin = pluginClass.getConstructor(new Class[] { Mote.class, Simulation.class, GUI.class }) .newInstance(argMote, argSimulation, argGUI); @@ -1678,7 +1678,7 @@ public class GUI extends Observable { throw new PluginConstructionException("No simulation argument for simulation plugin"); } - plugin = + plugin = pluginClass.getConstructor(new Class[] { Simulation.class, GUI.class }) .newInstance(argSimulation, argGUI); @@ -1717,7 +1717,7 @@ public class GUI extends Observable { if (activate && plugin.getGUI() != null) { myGUI.showPlugin(plugin); } - + return plugin; } @@ -1807,7 +1807,7 @@ public class GUI extends Observable { // Create 'start plugin'-menu item JMenuItem menuItem; String tooltip = ""; - + /* Sort menu according to plugin type */ int itemIndex=0; if (pluginType == PluginType.COOJA_PLUGIN || pluginType == PluginType.COOJA_STANDARD_PLUGIN) { @@ -1892,7 +1892,7 @@ public class GUI extends Observable { /** * Returns started plugin that ends with given class name, if any. - * + * * @param classname Class name * @return Plugin instance */ @@ -1904,10 +1904,10 @@ public class GUI extends Observable { } return null; } - + /** * Returns started plugin with given class name, if any. - * + * * @param classname Class name * @return Plugin instance * @deprecated @@ -1977,21 +1977,21 @@ public class GUI extends Observable { /** * Creates a new mote type of the given mote type class. * This may include displaying a dialog for user configurations. - * + * * If mote type is created successfully, the add motes dialog will appear. - * + * * @param moteTypeClass Mote type class */ public void doCreateMoteType(Class moteTypeClass) { doCreateMoteType(moteTypeClass, true); } - + /** * Creates a new mote type of the given mote type class. * This may include displaying a dialog for user configurations. - * + * * @param moteTypeClass Mote type class - * @param addMotes Show add motes dialog after successfully adding mote type + * @param addMotes Show add motes dialog after successfully adding mote type */ public void doCreateMoteType(Class moteTypeClass, boolean addMotes) { if (mySimulation == null) { @@ -2236,7 +2236,7 @@ public class GUI extends Observable { PROGRESS_WARNINGS.clear(); newSim = loadSimulationConfig(fileToLoad, quick); myGUI.setSimulation(newSim, false); - + /* Optionally show compilation warnings */ boolean hideWarn = Boolean.parseBoolean( GUI.getExternalToolsSetting("HIDE_WARNINGS", "false") @@ -2245,7 +2245,7 @@ public class GUI extends Observable { showWarningsDialog(frame, PROGRESS_WARNINGS.toArray(new String[0])); } PROGRESS_WARNINGS.clear(); - + } catch (UnsatisfiedLinkError e) { shouldRetry = showErrorDialog(GUI.getTopParentContainer(), "Simulation load error", e, true); } catch (SimulationCreationException e) { @@ -2276,7 +2276,7 @@ public class GUI extends Observable { if (warnMemory()) { return; } - + final JDialog progressDialog = new JDialog(frame, "Reloading", true); final Thread loadThread = new Thread(new Runnable() { public void run() { @@ -2305,7 +2305,7 @@ public class GUI extends Observable { if (autoStart) { newSim.startSimulation(); } - + /* Optionally show compilation warnings */ boolean hideWarn = Boolean.parseBoolean( GUI.getExternalToolsSetting("HIDE_WARNINGS", "false") @@ -2314,7 +2314,7 @@ public class GUI extends Observable { showWarningsDialog(frame, PROGRESS_WARNINGS.toArray(new String[0])); } PROGRESS_WARNINGS.clear(); - + } catch (UnsatisfiedLinkError e) { shouldRetry = showErrorDialog(frame, "Simulation reload error", e, true); @@ -2459,7 +2459,7 @@ public class GUI extends Observable { return saveFile; } else { JOptionPane.showMessageDialog( - getTopParentContainer(), "No write access to " + saveFile, "Save failed", + getTopParentContainer(), "No write access to " + saveFile, "Save failed", JOptionPane.ERROR_MESSAGE); logger.fatal("No write access to file: " + saveFile.getAbsolutePath()); } @@ -2757,8 +2757,8 @@ public class GUI extends Observable { ExternalToolsDialog.showDialog(GUI.getTopParentContainer()); } else if (e.getActionCommand().equals("manage projects")) { COOJAProject[] newProjects = ProjectDirectoriesDialog.showDialog( - GUI.getTopParentContainer(), - GUI.this, + GUI.getTopParentContainer(), + GUI.this, getProjects() ); if (newProjects != null) { @@ -3029,7 +3029,7 @@ public class GUI extends Observable { System.exit(1); } GUI gui = sim.getGUI(); - + /* Make sure at least one test editor is controlling the simulation */ boolean hasEditor = false; for (Plugin startedPlugin : gui.startedPlugins) { @@ -3051,7 +3051,12 @@ public class GUI extends Observable { System.exit(1); } plugin.updateScript(scriptFile); - plugin.setScriptActive(true); + try { + plugin.setScriptActive(true); + } catch (Exception e) { + logger.fatal("Error: " + e.getMessage(), e); + System.exit(1); + } sim.setDelayTime(0); sim.startSimulation(); } else { @@ -3059,7 +3064,7 @@ public class GUI extends Observable { System.exit(1); } } - + } else if (args.length > 0 && args[0].startsWith("-applet")) { String tmpWebPath=null, tmpBuildPath=null, tmpEsbFirmware=null, tmpSkyFirmware=null; @@ -3258,11 +3263,11 @@ public class GUI extends Observable { // Create and write to document Document doc = new Document(extractSimulationConfig()); OutputStream out = new FileOutputStream(file); - + if (file.getName().endsWith(".gz")) { out = new GZIPOutputStream(out); } - + XMLOutputter outputter = new XMLOutputter(); outputter.setFormat(Format.getPrettyFormat()); outputter.output(doc, out); @@ -3274,7 +3279,7 @@ public class GUI extends Observable { e.printStackTrace(); } } - + public Element extractSimulationConfig() { // Create simulation config Element root = new Element("simconf"); @@ -3391,7 +3396,7 @@ public class GUI extends Observable { projectFile = projectFile.getCanonicalFile(); } catch (IOException e) { } - + boolean found = false; for (COOJAProject currentProject: currentProjects) { if (projectFile.getPath().replaceAll("\\\\", "/"). @@ -3473,7 +3478,7 @@ public class GUI extends Observable { /* Activate plugin */ startedPlugin.startPlugin(); - + /* If Cooja not visualized, ignore window configuration */ if (startedPlugin.getGUI() == null) { continue; @@ -3511,7 +3516,7 @@ public class GUI extends Observable { pluginGUI.setIcon(true); } catch (PropertyVetoException e) { } - }; + }; }); } } @@ -3618,7 +3623,7 @@ public class GUI extends Observable { list.addPopupMenuItem(null, true); tabbedPane.addTab("Contiki error", new JScrollPane(list)); } - + /* Compilation output */ MessageList compilationOutput = null; if (exception instanceof MoteTypeCreationException @@ -3704,7 +3709,7 @@ public class GUI extends Observable { private static void showWarningsDialog(final Frame parent, final String[] warnings) { new RunnableInEDT() { public Boolean work() { - final JDialog dialog = new JDialog((Frame)parent, "Compilation warnings", false); + final JDialog dialog = new JDialog(parent, "Compilation warnings", false); Box buttonBox = Box.createHorizontalBox(); /* Warnings message list */ @@ -3748,7 +3753,7 @@ public class GUI extends Observable { } }.invokeAndWait(); } - + /** * Runs work method in event dispatcher thread. * Worker method returns a value. @@ -3885,9 +3890,9 @@ public class GUI extends Observable { /** * Tries to convert given file to be "portable". * The portable path is either relative to Contiki, or to the configuration (.csc) file. - * + * * If this method fails, it returns the original file. - * + * * @param file Original file * @return Portable file, or original file is conversion failed */ @@ -3897,7 +3902,7 @@ public class GUI extends Observable { public File createPortablePath(File file, boolean allowConfigRelativePaths) { File portable = null; - + portable = createContikiRelativePath(file); if (portable != null) { /*logger.info("Generated Contiki relative path '" + file.getPath() + "' to '" + portable.getPath() + "'");*/ @@ -3911,7 +3916,7 @@ public class GUI extends Observable { return portable; } } - + logger.warn("Path is not portable: '" + file.getPath()); return file; } @@ -3919,7 +3924,7 @@ public class GUI extends Observable { /** * Tries to restore a previously "portable" file to be "absolute". * If the given file already exists, no conversion is performed. - * + * * @see #createPortablePath(File) * @param file Portable file * @return Absolute file @@ -3942,7 +3947,7 @@ public class GUI extends Observable { /*logger.info("Restored config relative path '" + file.getPath() + "' to '" + absolute.getPath() + "'");*/ return absolute; } - + /*logger.info("Portable path was not restored: '" + file.getPath());*/ return file; } @@ -3962,10 +3967,10 @@ public class GUI extends Observable { /* Replace Contiki's canonical path with Contiki identifier */ String portablePath = fileCanonical.replaceFirst( - java.util.regex.Matcher.quoteReplacement(contikiCanonical), + java.util.regex.Matcher.quoteReplacement(contikiCanonical), java.util.regex.Matcher.quoteReplacement(PATH_CONTIKI_IDENTIFIER)); File portable = new File(portablePath); - + /* Verify conversion */ File verify = restoreContikiRelativePath(portable); if (verify == null || !verify.exists()) { @@ -4028,10 +4033,10 @@ public class GUI extends Observable { /* Replace config's canonical path with config identifier */ String portablePath = fileCanonical.replaceFirst( - java.util.regex.Matcher.quoteReplacement(configCanonical), + java.util.regex.Matcher.quoteReplacement(configCanonical), java.util.regex.Matcher.quoteReplacement(id)); File portable = new File(portablePath); - + /* Verify conversion */ File verify = restoreConfigRelativePath(portable); if (verify == null || !verify.exists()) { @@ -4087,11 +4092,11 @@ public class GUI extends Observable { */ public String getQuickHelp(); } - + /** * Load quick help for given object or identifier. Note that this method does not * show the quick help pane. - * + * * @param obj If string: help identifier. Else, the class name of the argument * is used as help identifier. */ @@ -4106,7 +4111,7 @@ public class GUI extends Observable { } else { key = obj.getClass().getName(); } - + String help = null; if (obj instanceof HasQuickHelp) { help = ((HasQuickHelp) obj).getQuickHelp(); @@ -4292,13 +4297,13 @@ public class GUI extends Observable { } outputFile.delete(); } - + final File finalOutputFile = outputFile; setExternalToolsSetting("EXECUTE_JAR_LAST", outputFile.getPath()); new Thread() { public void run() { try { - ExecuteJAR.buildExecutableJAR(GUI.this, finalOutputFile); + ExecuteJAR.buildExecutableJAR(GUI.this, finalOutputFile); } catch (RuntimeException ex) { JOptionPane.showMessageDialog(GUI.getTopParentContainer(), ex.getMessage(), @@ -4359,7 +4364,7 @@ public class GUI extends Observable { public void actionPerformed(final ActionEvent e) { new Thread(new Runnable() { public void run() { - Class pluginClass = + Class pluginClass = (Class) ((JMenuItem) e.getSource()).getClientProperty("class"); Mote mote = (Mote) ((JMenuItem) e.getSource()).getClientProperty("mote"); tryStartPlugin(pluginClass, myGUI, mySimulation, mote); @@ -4434,5 +4439,5 @@ public class GUI extends Observable { return mySimulation != null; } }; - + } diff --git a/tools/cooja/java/se/sics/cooja/plugins/LogScriptEngine.java b/tools/cooja/java/se/sics/cooja/plugins/LogScriptEngine.java index 5a6fd82fd..2e867da80 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/LogScriptEngine.java +++ b/tools/cooja/java/se/sics/cooja/plugins/LogScriptEngine.java @@ -64,9 +64,9 @@ public class LogScriptEngine { private static Logger logger = Logger.getLogger(LogScriptEngine.class); private static final long DEFAULT_TIMEOUT = 20*60*1000*Simulation.MILLISECOND; /* 1200s = 20 minutes */ - private ScriptEngine engine = + private ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); - + /* Log output listener */ private LogOutputListener logOutputListener = new LogOutputListener() { public void moteWasAdded(Mote mote) { @@ -97,6 +97,11 @@ public class LogScriptEngine { private boolean scriptActive = false; + private long timeout; + private long startTime; + private long startRealTime; + private long nextProgress; + private interface ScriptLog { public void log(String log); public void testOK(); @@ -104,6 +109,10 @@ public class LogScriptEngine { public void generateMessage(long delay, String msg); } + public LogScriptEngine(Simulation simulation) { + this.simulation = simulation; + } + /* Only called from the simulation loop */ private void stepScript() { /* Release script - halt simulation */ @@ -130,10 +139,6 @@ public class LogScriptEngine { } } - public LogScriptEngine(Simulation simulation) { - this.simulation = simulation; - } - /* Only called from the simulation loop */ private void handleNewMoteOutput(Mote mote, int id, long time, String msg) { try { @@ -195,37 +200,31 @@ public class LogScriptEngine { } scriptActive = false; - if (semaphoreScript == null) { - /*logger.warn("semaphoreScript is not initialized");*/ - } - if (semaphoreSim == null) { - /*logger.warn("semaphoreSim is not initialized");*/ - } - if (scriptThread == null) { - /*logger.warn("scriptThread is not initialized");*/ - } - timeoutEvent.remove(); timeoutProgressEvent.remove(); - + simulation.getEventCentral().removeLogOutputListener(logOutputListener); engine.put("SHUTDOWN", true); try { - semaphoreScript.release(100); + if (semaphoreScript != null) { + semaphoreScript.release(100); + } } catch (Exception e) { } finally { semaphoreScript = null; } try { - semaphoreSim.release(100); + if (semaphoreSim != null) { + semaphoreSim.release(100); + } } catch (Exception e) { } finally { semaphoreSim = null; } - if (scriptThread != null && + if (scriptThread != null && scriptThread != Thread.currentThread() /* XXX May deadlock */ ) { try { scriptThread.join(); @@ -235,7 +234,6 @@ public class LogScriptEngine { } } scriptThread = null; - } public void activateScript(String scriptCode) throws ScriptException { @@ -245,43 +243,29 @@ public class LogScriptEngine { scriptActive = true; if (semaphoreScript != null) { - logger.warn("semaphoreScript is already initialized"); + logger.warn("Semaphores were not reset correctly"); + semaphoreScript.release(100); + semaphoreScript = null; } if (semaphoreSim != null) { - logger.warn("semaphoreSim is already initialized"); - } - if (scriptThread != null) { - logger.warn("scriptThread is already initialized"); + logger.warn("Semaphores were not reset correctly"); + semaphoreSim.release(100); + semaphoreSim = null; } + scriptThread = null; /* Parse current script */ ScriptParser parser = new ScriptParser(scriptCode); String jsCode = parser.getJSCode(); - long timeoutTime = parser.getTimeoutTime(); - if (timeoutTime < 0) { - logger.info("No timeout defined, using default (us): " + DEFAULT_TIMEOUT); - timeoutTime = DEFAULT_TIMEOUT; + timeout = parser.getTimeoutTime(); + if (timeout < 0) { + timeout = DEFAULT_TIMEOUT; + logger.info("Default script timeout in " + (timeout/Simulation.MILLISECOND) + " ms"); + } else { + logger.info("Script timeout in " + (timeout/Simulation.MILLISECOND) + " ms"); } - final long duration = timeoutTime; - simulation.invokeSimulationThread(new Runnable() { - public void run() { - final long startTime = simulation.getSimulationTime(); - final long interval = (long) (0.01*5*duration); - - simulation.scheduleEvent(timeoutProgressEvent = new TimeEvent(0) { - public void execute(long t) { - int percent = (int) (5*(t-startTime)/interval); - logger.info("Test script at " + percent + "%"); - simulation.scheduleEvent(this, t+interval); - } - }, startTime+interval); - - simulation.scheduleEvent(timeoutEvent, startTime + duration); - } - }); - engine.eval(jsCode); /* Setup script control */ @@ -352,95 +336,7 @@ public class LogScriptEngine { simulation.getEventCentral().addLogOutputListener(logOutputListener); /* Create script output logger */ - engine.put("log", new ScriptLog() { - public void log(String msg) { - if (scriptLogObserver != null) { - scriptLogObserver.update(null, msg); - } - } - public void append(String filename, String msg) { - try{ - FileWriter fstream = new FileWriter(filename, true); - BufferedWriter out = new BufferedWriter(fstream); - out.write(msg); - out.close(); - } catch (Exception e) { - logger.warn("Test append failed: " + filename + ": " + e.getMessage()); - } - } - public void testOK() { - log("TEST OK\n"); - - if (GUI.isVisualized()) { - log("[if test was run without visualization, COOJA would now have been terminated]\n"); - stopSimulation = true; - simulation.invokeSimulationThread(stopSimulationRunnable); - } else { - quitCooja = true; - simulation.invokeSimulationThread(quitRunnable); - } - - timeoutEvent.remove(); - timeoutProgressEvent.remove(); - - semaphoreSim.release(100); - throw new RuntimeException("test script killed"); - } - public void writeFile(String filename, String msg) { - try{ - FileWriter fstream = new FileWriter(filename, false); - BufferedWriter out = new BufferedWriter(fstream); - out.write(msg); - out.close(); - } catch (Exception e) { - logger.warn("Write file failed: " + filename + ": " + e.getMessage()); - } - } - public void testFailed() { - log("TEST FAILED\n"); - - if (GUI.isVisualized()) { - log("[if test was run without visualization, COOJA would now have been terminated]\n"); - stopSimulation = true; - simulation.invokeSimulationThread(stopSimulationRunnable); - } else { - quitCooja = true; - simulation.invokeSimulationThread(quitRunnable); - } - - semaphoreSim.release(100); - throw new RuntimeException("test script killed"); - } - - public void generateMessage(final long delay, final String msg) { - final Mote currentMote = (Mote) engine.get("mote"); - final TimeEvent generateEvent = new TimeEvent(0) { - public void execute(long t) { - if (scriptThread == null || - !scriptThread.isAlive()) { - logger.info("script thread not alive. try deactivating script."); - /*scriptThread.isInterrupted()*/ - return; - } - - /* Update script variables */ - engine.put("mote", currentMote); - engine.put("id", currentMote.getID()); - engine.put("time", currentMote.getSimulation().getSimulationTime()); - engine.put("msg", msg); - - stepScript(); - } - }; - simulation.invokeSimulationThread(new Runnable() { - public void run() { - simulation.scheduleEvent( - generateEvent, - simulation.getSimulationTime() + delay*Simulation.MILLISECOND); - } - }); - } - }); + engine.put("log", scriptLog); Hashtable hash = new Hashtable(); engine.put("global", hash); @@ -449,27 +345,53 @@ public class LogScriptEngine { scriptMote = new ScriptMote(); engine.put("node", scriptMote); + + Runnable activate = new Runnable() { + public void run() { + startRealTime = System.currentTimeMillis(); + startTime = simulation.getSimulationTime(); + long endTime = startTime + timeout; + nextProgress = startTime + (endTime - startTime)/20; + + timeoutProgressEvent.remove(); + simulation.scheduleEvent(timeoutProgressEvent, nextProgress); + timeoutEvent.remove(); + simulation.scheduleEvent(timeoutEvent, endTime); + } + }; + if (simulation.isRunning()) { + simulation.invokeSimulationThread(activate); + } else { + activate.run(); + } } private TimeEvent timeoutEvent = new TimeEvent(0) { public void execute(long t) { - if (!scriptActive) { - return; - } + if (!scriptActive) { + return; + } logger.info("Timeout event @ " + t); engine.put("TIMEOUT", true); stepScript(); } }; private TimeEvent timeoutProgressEvent = new TimeEvent(0) { - public void execute(long t) { } - }; - + public void execute(long t) { + nextProgress = t + timeout/20; + simulation.scheduleEvent(this, nextProgress); + + double progress = 1.0*(t - startTime)/timeout; + long realDuration = System.currentTimeMillis()-startRealTime; + double estimatedLeft = 1.0*realDuration/progress - realDuration; + if (estimatedLeft == 0) estimatedLeft = 1; + logger.info(String.format("Test script at %2.2f%%, done in %2.1f sec", 100*progress, estimatedLeft/1000)); + } + }; + private Runnable stopSimulationRunnable = new Runnable() { public void run() { simulation.stopSimulation(); - timeoutEvent.remove(); - timeoutProgressEvent.remove(); } }; private Runnable quitRunnable = new Runnable() { @@ -491,4 +413,83 @@ public class LogScriptEngine { } }; + private ScriptLog scriptLog = new ScriptLog() { + public void log(String msg) { + if (scriptLogObserver != null) { + scriptLogObserver.update(null, msg); + } + } + public void append(String filename, String msg) { + try{ + FileWriter fstream = new FileWriter(filename, true); + BufferedWriter out = new BufferedWriter(fstream); + out.write(msg); + out.close(); + } catch (Exception e) { + logger.warn("Test append failed: " + filename + ": " + e.getMessage()); + } + } + public void writeFile(String filename, String msg) { + try{ + FileWriter fstream = new FileWriter(filename, false); + BufferedWriter out = new BufferedWriter(fstream); + out.write(msg); + out.close(); + } catch (Exception e) { + logger.warn("Write file failed: " + filename + ": " + e.getMessage()); + } + } + + public void testOK() { + log("TEST OK\n"); + deactive(); + } + public void testFailed() { + log("TEST FAILED\n"); + deactive(); + } + private void deactive() { + deactivateScript(); + + if (GUI.isVisualized()) { + log("[if test was run without visualization, COOJA would now have been terminated]\n"); + stopSimulation = true; + simulation.invokeSimulationThread(stopSimulationRunnable); + } else { + quitCooja = true; + simulation.invokeSimulationThread(quitRunnable); + } + + throw new RuntimeException("test script killed"); + } + + public void generateMessage(final long delay, final String msg) { + final Mote currentMote = (Mote) engine.get("mote"); + final TimeEvent generateEvent = new TimeEvent(0) { + public void execute(long t) { + if (scriptThread == null || + !scriptThread.isAlive()) { + logger.info("script thread not alive. try deactivating script."); + /*scriptThread.isInterrupted()*/ + return; + } + + /* Update script variables */ + engine.put("mote", currentMote); + engine.put("id", currentMote.getID()); + engine.put("time", currentMote.getSimulation().getSimulationTime()); + engine.put("msg", msg); + + stepScript(); + } + }; + simulation.invokeSimulationThread(new Runnable() { + public void run() { + simulation.scheduleEvent( + generateEvent, + simulation.getSimulationTime() + delay*Simulation.MILLISECOND); + } + }); + } + }; } diff --git a/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java b/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java index 4acb1b165..d67d86384 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java +++ b/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java @@ -92,8 +92,7 @@ public class ScriptRunner extends VisPlugin { static boolean headless; { - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - headless = ge.isHeadless(); + headless = GraphicsEnvironment.isHeadless(); if (!headless) { DefaultSyntaxKit.initKit(); } @@ -183,10 +182,14 @@ public class ScriptRunner extends VisPlugin { toggleButton = new JButton("Activate"); toggleButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { - if (toggleButton.getText().equals("Activate")) { - setScriptActive(true); - } else { - setScriptActive(false); + try { + if (!isActive()) { + setScriptActive(true); + } else { + setScriptActive(false); + } + } catch (Exception e) { + logger.fatal("Error: " + e.getMessage(), e); } } }); @@ -276,7 +279,8 @@ public class ScriptRunner extends VisPlugin { updateTitle(); } - public void setScriptActive(boolean active) { + public void setScriptActive(boolean active) + throws Exception { if (active) { /* setScriptActive(true) */ @@ -612,7 +616,10 @@ public class ScriptRunner extends VisPlugin { } public void closePlugin() { - setScriptActive(false); + try { + setScriptActive(false); + } catch (Exception e) { + } } public boolean setConfigXML(Collection configXML, boolean visAvailable) { @@ -628,14 +635,22 @@ public class ScriptRunner extends VisPlugin { } else if ("active".equals(name)) { boolean active = Boolean.parseBoolean(element.getText()); if (GUI.isVisualized()) { - setScriptActive(active); + try { + setScriptActive(active); + } catch (Exception e) { + logger.fatal("Error: " + e.getMessage(), e); + } } } } if (!GUI.isVisualized()) { /* Automatically activate script */ - setScriptActive(true); + try { + setScriptActive(true); + } catch (Exception e) { + logger.fatal("Error: " + e.getMessage(), e); + } simulation.setDelayTime(0); simulation.startSimulation(); } From b7674c3636cfe74af52d63d4c9b845d26f5270d6 Mon Sep 17 00:00:00 2001 From: George Oikonomou Date: Mon, 5 Mar 2012 16:28:06 +0000 Subject: [PATCH 11/14] Reincarnate the sensinode/cc2430 port --- cpu/cc2430/8051def.h | 58 +- cpu/cc2430/Makefile.cc2430 | 206 ++- cpu/cc2430/bank-alloc.py | 228 +++ cpu/cc2430/cc2430_sfr.h | 1415 +++++++++-------- cpu/cc2430/converter/converter | Bin 7156 -> 0 bytes cpu/cc2430/converter/converter.c | 25 +- cpu/cc2430/converter/converter.h | 2 +- cpu/cc2430/converter/converter.o | Bin 4740 -> 0 bytes cpu/cc2430/converter/ihex.o | Bin 1604 -> 0 bytes cpu/cc2430/dev/adc.c | 70 - cpu/cc2430/dev/adc.h | 41 - cpu/cc2430/dev/banked.h | 40 - cpu/cc2430/dev/bus.c | 8 +- cpu/cc2430/dev/bus.h | 9 +- cpu/cc2430/dev/cc2430_rf.c | 937 ++++++----- cpu/cc2430/dev/cc2430_rf.h | 53 +- cpu/cc2430/dev/cc2430_rf_intr.c | 89 +- cpu/cc2430/dev/clock.c | 34 +- cpu/cc2430/dev/dma.c | 221 +-- cpu/cc2430/dev/dma.h | 228 +-- cpu/cc2430/dev/dma_intr.c | 19 +- cpu/cc2430/dev/hwconf.h | 74 +- cpu/cc2430/dev/lpm.h | 44 +- cpu/cc2430/dev/random.c | 135 ++ cpu/cc2430/dev/rs232.c | 76 - cpu/cc2430/dev/rs232.h | 88 - cpu/cc2430/dev/uart.c | 41 - cpu/cc2430/dev/uart.h | 50 +- cpu/cc2430/dev/uart0.c | 69 + cpu/cc2430/dev/uart0.h | 31 + cpu/cc2430/dev/uart1.c | 74 + cpu/cc2430/dev/uart1.h | 39 + cpu/cc2430/dev/uart_init.c | 133 -- cpu/cc2430/dev/uart_intr.c | 27 +- cpu/cc2430/dev/watchdog-cc2430.c | 124 ++ cpu/cc2430/dev/watchdog-cc2430.h | 72 + cpu/cc2430/io.h | 10 - cpu/cc2430/mtarch.c | 192 --- cpu/cc2430/mtarch.h | 33 +- cpu/cc2430/rtimer-arch.c | 122 ++ cpu/cc2430/rtimer-arch.h | 21 +- cpu/cc2430/segment.rules | 47 +- cpu/cc2430/segment.rules.pl | 11 - cpu/cc2430/uip_arch-asm.S | 221 --- cpu/cc2430/uip_arch.c | 216 --- examples/sensinode/Makefile | 10 + examples/sensinode/README | 28 +- examples/sensinode/blink-hello.c | 91 ++ examples/sensinode/border-router/Makefile | 19 + examples/sensinode/border-router/README | 16 + .../sensinode/border-router/border-router.c | 134 ++ .../sensinode/border-router/project-conf.h | 50 + .../sensinode/border-router/slip-bridge.c | 104 ++ examples/sensinode/broadcast-rime.c | 115 ++ .../sensinode/cc2431-location-engine/Makefile | 17 + .../cc2431-location-engine/blind-node.c | 276 ++++ .../cc2431-location-engine/cc2431_loc_eng.h | 108 ++ examples/sensinode/clock_test.c | 4 +- examples/sensinode/disco/Makefile | 18 + examples/sensinode/disco/disco-example.c | 53 + examples/sensinode/energy-scan/Makefile | 16 + examples/sensinode/energy-scan/energy-scan.c | 108 ++ examples/sensinode/energy-scan/netstack.c | 50 + examples/sensinode/energy-scan/project-conf.h | 50 + examples/sensinode/energy-scan/stub-rdc.c | 91 ++ examples/sensinode/event-post/Makefile | 17 + examples/sensinode/event-post/event-post.c | 132 ++ examples/sensinode/event-post/event-post.h | 22 + examples/sensinode/sensors-ipv6/Makefile | 19 + .../sensinode/sensors-ipv6/project-conf.h | 51 + .../sensinode/sensors-ipv6/sensors-driver.c | 246 +++ .../sensinode/sensors-ipv6/sensors-ipv6.c | 154 ++ examples/sensinode/sensors/Makefile | 17 + examples/sensinode/sensors/sensors-example.c | 372 +++++ examples/sensinode/sensors/sensors-example.h | 53 + examples/sensinode/serial-flash/Makefile | 13 + examples/sensinode/serial-flash/flash.c | 268 ++++ examples/sensinode/sniffer/Makefile | 16 + examples/sensinode/sniffer/README | 14 + examples/sensinode/sniffer/netstack.c | 50 + examples/sensinode/sniffer/project-conf.h | 52 + examples/sensinode/sniffer/sniffer.c | 54 + examples/sensinode/sniffer/stub-rdc.c | 91 ++ examples/sensinode/timer-test.c | 123 ++ examples/sensinode/udp-ipv6/Makefile | 19 + examples/sensinode/udp-ipv6/client.c | 212 +++ examples/sensinode/udp-ipv6/ping6.c | 147 ++ examples/sensinode/udp-ipv6/project-conf.h | 50 + examples/sensinode/udp-ipv6/server.c | 197 +++ platform/sensinode/Makefile.sensinode | 66 +- .../sensinode/apps/batmon/Makefile.batmon | 3 + platform/sensinode/apps/batmon/batmon.c | 230 +++ platform/sensinode/contiki-conf.h | 265 ++- platform/sensinode/contiki-sensinode-main.c | 400 ++++- platform/sensinode/dev/adc-sensor.c | 212 +++ platform/sensinode/dev/button-sensor.c | 229 ++- platform/sensinode/dev/button-sensor.h | 48 + platform/sensinode/dev/leds-arch.c | 55 +- platform/sensinode/dev/m25p16.c | 316 ++++ platform/sensinode/dev/m25p16.h | 293 ++++ platform/sensinode/dev/models.c | 95 ++ platform/sensinode/dev/models.h | 36 +- platform/sensinode/dev/n740.c | 186 +++ platform/sensinode/dev/n740.h | 75 + platform/sensinode/dev/sensinode-sensors.c | 81 + platform/sensinode/dev/sensinode-sensors.h | 166 ++ platform/sensinode/dev/slip-arch.c | 50 + platform/sensinode/disco.c | 347 ++++ platform/sensinode/disco.h | 129 ++ platform/sensinode/putchar.c | 39 + platform/sensinode/segment.rules | 13 + platform/sensinode/sensinode-debug.c | 46 + platform/sensinode/sensinode-debug.h | 54 + platform/sensinode/viztool.c | 298 ++++ 114 files changed, 10044 insertions(+), 3068 deletions(-) create mode 100644 cpu/cc2430/bank-alloc.py delete mode 100755 cpu/cc2430/converter/converter delete mode 100644 cpu/cc2430/converter/converter.o delete mode 100644 cpu/cc2430/converter/ihex.o delete mode 100644 cpu/cc2430/dev/adc.c delete mode 100644 cpu/cc2430/dev/adc.h delete mode 100644 cpu/cc2430/dev/banked.h create mode 100644 cpu/cc2430/dev/random.c delete mode 100644 cpu/cc2430/dev/rs232.c delete mode 100644 cpu/cc2430/dev/rs232.h delete mode 100644 cpu/cc2430/dev/uart.c create mode 100644 cpu/cc2430/dev/uart0.c create mode 100644 cpu/cc2430/dev/uart0.h create mode 100644 cpu/cc2430/dev/uart1.c create mode 100644 cpu/cc2430/dev/uart1.h delete mode 100644 cpu/cc2430/dev/uart_init.c create mode 100644 cpu/cc2430/dev/watchdog-cc2430.c create mode 100644 cpu/cc2430/dev/watchdog-cc2430.h delete mode 100644 cpu/cc2430/io.h delete mode 100644 cpu/cc2430/mtarch.c create mode 100644 cpu/cc2430/rtimer-arch.c delete mode 100644 cpu/cc2430/segment.rules.pl delete mode 100644 cpu/cc2430/uip_arch-asm.S delete mode 100644 cpu/cc2430/uip_arch.c create mode 100644 examples/sensinode/blink-hello.c create mode 100644 examples/sensinode/border-router/Makefile create mode 100644 examples/sensinode/border-router/README create mode 100644 examples/sensinode/border-router/border-router.c create mode 100644 examples/sensinode/border-router/project-conf.h create mode 100644 examples/sensinode/border-router/slip-bridge.c create mode 100644 examples/sensinode/broadcast-rime.c create mode 100644 examples/sensinode/cc2431-location-engine/Makefile create mode 100644 examples/sensinode/cc2431-location-engine/blind-node.c create mode 100644 examples/sensinode/cc2431-location-engine/cc2431_loc_eng.h create mode 100644 examples/sensinode/disco/Makefile create mode 100644 examples/sensinode/disco/disco-example.c create mode 100644 examples/sensinode/energy-scan/Makefile create mode 100644 examples/sensinode/energy-scan/energy-scan.c create mode 100644 examples/sensinode/energy-scan/netstack.c create mode 100644 examples/sensinode/energy-scan/project-conf.h create mode 100644 examples/sensinode/energy-scan/stub-rdc.c create mode 100644 examples/sensinode/event-post/Makefile create mode 100644 examples/sensinode/event-post/event-post.c create mode 100644 examples/sensinode/event-post/event-post.h create mode 100644 examples/sensinode/sensors-ipv6/Makefile create mode 100644 examples/sensinode/sensors-ipv6/project-conf.h create mode 100644 examples/sensinode/sensors-ipv6/sensors-driver.c create mode 100644 examples/sensinode/sensors-ipv6/sensors-ipv6.c create mode 100644 examples/sensinode/sensors/Makefile create mode 100644 examples/sensinode/sensors/sensors-example.c create mode 100644 examples/sensinode/sensors/sensors-example.h create mode 100644 examples/sensinode/serial-flash/Makefile create mode 100644 examples/sensinode/serial-flash/flash.c create mode 100644 examples/sensinode/sniffer/Makefile create mode 100644 examples/sensinode/sniffer/README create mode 100644 examples/sensinode/sniffer/netstack.c create mode 100644 examples/sensinode/sniffer/project-conf.h create mode 100644 examples/sensinode/sniffer/sniffer.c create mode 100644 examples/sensinode/sniffer/stub-rdc.c create mode 100644 examples/sensinode/timer-test.c create mode 100644 examples/sensinode/udp-ipv6/Makefile create mode 100644 examples/sensinode/udp-ipv6/client.c create mode 100644 examples/sensinode/udp-ipv6/ping6.c create mode 100644 examples/sensinode/udp-ipv6/project-conf.h create mode 100644 examples/sensinode/udp-ipv6/server.c create mode 100644 platform/sensinode/apps/batmon/Makefile.batmon create mode 100644 platform/sensinode/apps/batmon/batmon.c create mode 100644 platform/sensinode/dev/button-sensor.h create mode 100644 platform/sensinode/dev/m25p16.c create mode 100644 platform/sensinode/dev/m25p16.h create mode 100644 platform/sensinode/dev/models.c create mode 100644 platform/sensinode/dev/n740.c create mode 100644 platform/sensinode/dev/n740.h create mode 100644 platform/sensinode/dev/sensinode-sensors.c create mode 100644 platform/sensinode/dev/sensinode-sensors.h create mode 100644 platform/sensinode/dev/slip-arch.c create mode 100644 platform/sensinode/disco.c create mode 100644 platform/sensinode/disco.h create mode 100644 platform/sensinode/putchar.c create mode 100644 platform/sensinode/segment.rules create mode 100644 platform/sensinode/sensinode-debug.c create mode 100644 platform/sensinode/sensinode-debug.h create mode 100644 platform/sensinode/viztool.c diff --git a/cpu/cc2430/8051def.h b/cpu/cc2430/8051def.h index add1f7eda..069a42c79 100644 --- a/cpu/cc2430/8051def.h +++ b/cpu/cc2430/8051def.h @@ -4,12 +4,34 @@ * Modified from z80 port for cc2430 port. * * \author - * Takahide Matsutsuka + * Takahide Matsutsuka (Original) + * George Oikonomou - + * (recent updates for the sensinode/cc2430 port) */ #ifndef __8051_DEF_H__ #define __8051_DEF_H__ +#include + +/* + * lint - style defines to help syntax parsers with sdcc-specific 8051 code + * They don't interfere with actual compilation + */ +#if !defined(__SDCC_mcs51) && !defined(SDCC_mcs51) +#define __data +#define __xdata +#define __code +#define __bit bool +#define __sfr volatile unsigned char +#define __sbit volatile bool +#define __critical +#define __at(x) +#define __using(x) +#define __interrupt(x) +#define __naked +#endif + #define CC_CONF_FUNCTION_POINTER_ARGS 1 #define CC_CONF_FASTCALL #define CC_CONF_VA_ARGS 1 @@ -18,30 +40,24 @@ #define CC_CONF_FUNCTION_POINTER_KEYWORD __reentrant /* Generic types. */ -typedef signed char int8_t; -typedef unsigned char uint8_t; -typedef signed short int16_t; -typedef unsigned short uint16_t; -typedef unsigned long uint32_t; typedef unsigned char u8_t; /* 8 bit type */ typedef unsigned short u16_t; /* 16 bit type */ typedef unsigned long u32_t; /* 32 bit type */ typedef signed long s32_t; /* 32 bit type */ typedef unsigned short uip_stats_t; -typedef signed long int32_t; /* 32 bit type */ -#ifndef _SIZE_T_DEFINED -#define _SIZE_T_DEFINED -typedef unsigned int size_t; -#endif + + +/* Time type. */ +typedef unsigned short clock_time_t; +#define MAX_TICKS (~((clock_time_t)0) / 2) /* Compiler configurations */ #define CCIF #define CLIF -#define CC_CONF_CONST_FUNCTION_BUG /* Critical section management */ -#define DISABLE_INTERRUPTS() EA = 0; -#define ENABLE_INTERRUPTS() EA = 1; +#define DISABLE_INTERRUPTS() do {EA = 0;} while(0) +#define ENABLE_INTERRUPTS() do {EA = 1;} while(0) #define ENTER_CRITICAL() \ { \ @@ -64,20 +80,12 @@ typedef unsigned int size_t; __endasm; \ } -/* - * Enable architecture-depend checksum calculation - * for uIP configuration. - * @see uip_arch.h - * @see uip_arch-asm.S - */ -/* - * DO NOT USE UIP_ARCH flags! - * uip_arch code was copied from z80 directory but NOT ported - */ +/* Macro for a soft reset. In many respects better than H/W reboot via W/D */ +#define SOFT_RESET() {((void (__code *) (void)) 0x0000) ();} +/* We don't provide architecture-specific checksum calculations */ #define UIP_ARCH_ADD32 0 #define UIP_ARCH_CHKSUM 0 -#define UIP_ARCH_IPCHKSUM #define CC_CONF_ASSIGN_AGGREGATE(dest, src) \ memcpy(dest, src, sizeof(*dest)) diff --git a/cpu/cc2430/Makefile.cc2430 b/cpu/cc2430/Makefile.cc2430 index 9b54ffc14..3863f6775 100644 --- a/cpu/cc2430/Makefile.cc2430 +++ b/cpu/cc2430/Makefile.cc2430 @@ -2,120 +2,176 @@ CC = sdcc LD = sdcc AS = sdcc -AR = sdcclib +AR = sdcclib OBJCOPY = objcopy STRIP = strip +PACKIHX = packihx +BANK_ALLOC = $(CONTIKI)/cpu/cc2430/bank-alloc.py +SEGMENT_RULES = $(OBJECTDIR)/segment.rules + +CFLAGS += --model-$(MEMORY_MODEL) --stack-auto -DSDCC_CC2430 --std-c99 + +LDFLAGS += --model-$(MEMORY_MODEL) --stack-auto -DSDCC_CC2430 --out-fmt-ihx +LDFLAGS += --xram-loc 0xE000 --xram-size 0x1F00 +LDFLAGS += --code-loc $(START_ADDR) --code-size $(CODE_SIZE) -CFLAGS += --std-c99 --model-large --stack-auto -DSDCC_CC2430 ASFLAGS += -plosgff -LDFLAGS += --model-large --stack-auto -DSDCC_CC2430 --out-fmt-ihx -LDFLAGS += --xram-loc 57344 --xram-size 8192 -##LDFLAGS += -L /home/user/local/share/sdcc/lib/large-stack-auto -##LDFLAGS += --verbose -##LDFLAGS += -V -AROPTS = -a -##HAVE_BANKING=1 -ifeq ($(HAVE_BANKING),1) -#banking -LDFLAGS += --code-size 0x20000 -LDFLAGS += -Wl-bCSEG=0x000000 -LDFLAGS += -Wl-bBANK1=0x018000 -LDFLAGS += -Wl-bBANK2=0x028000 -LDFLAGS += -Wl-bBANK3=0x038000 -#relocated code (for bank switching ) -LDFLAGS += -Wl-r -CFLAGS += -DHAVE_SDCC_BANKING -#use this in $(call code_segment,$<) to get segment for a source file. -code_segment = --codeseg $(word 1,$(shell cat ${OBJECTDIR}/segment.rules | perl ${CONTIKI_CPU}/segment.rules.pl $1 ) CSEG ) +AROPTS = -a + +### Our object files are .rel, so we can't use the default finalize dependency +### generation. Override here. +define FINALIZE_SDCC_DEPENDENCY +cp $(@:.rel=.d) $(@:.rel=.$$$$); \ +sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:.rel=.$$$$) >> $(@:.rel=.d); \ +rm -f $(@:.rel=.$$$$) +endef + +### Banking Guesswork: +### Examples outside examples/sensinode do not specify banking. +### We automatically turn it on if its unspecified and if we are building with +### UIP_CONF_IPV6 +ifndef HAVE_BANKING + ifeq ($(UIP_CONF_IPV6),1) + HAVE_BANKING=1 + else + HAVE_BANKING=0 + endif +endif + +### Does the project want us to offset the firmware? +### define start address and max code size accordingly, turn Disco on +ifeq ($(OFFSET_FIRMWARE),1) + START_ADDR = 0x01000 + HOME_START = 00001000 + ifeq ($(HAVE_BANKING),1) + CODE_SIZE = 0x1F000 + else + CODE_SIZE = 0x0F000 + endif else -#no banking -LDFLAGS += --code-loc 0000 --code-size 65500 -code_segment = + START_ADDR = 0x00000 + HOME_START = 00000000 + ifeq ($(HAVE_BANKING),1) + CODE_SIZE = 0x20000 + else + CODE_SIZE = 0x10000 + endif +endif + +### Are we building with BANKing supoprt? +ifeq ($(HAVE_BANKING),1) + ## Yes + MEMORY_MODEL=huge + LDFLAGS += -Wl-bBANK1=0x018000 + LD_POST_FLAGS += -Wl-bBANK2=0x028000 + LD_POST_FLAGS += -Wl-bBANK3=0x038000 + LDFLAGS += -Wl-r + CFLAGS += -DHAVE_SDCC_BANKING + #use this in $(call c_seg,$<) to get segment for a source file. + c_seg = --codeseg $(shell python $(BANK_ALLOC) $1 $(SEGMENT_RULES) $2) +else + ## No banking + MEMORY_MODEL=large + c_seg = endif ### CPU-dependent cleanup files -CLEAN += *.lnk *.sym *.lib *.ihx *.rel *.mem *.rst *.asm *_linear.hex +CLEAN += *.lnk *.lk *.sym *.lib *.ihx *.rel *.mem *.rst *.asm *.hex *.sensinode +CLEAN += *.omf *.cdb *.banks +CLEAN += symbols.c symbols.h ### CPU-dependent directories CONTIKI_CPU_DIRS = . dev ### CPU-dependent source files -CONTIKI_SOURCEFILES += bus.c clock.c uart.c cc2430_rf.c dma.c -CONTIKI_SOURCEFILES += uart_init.c uart_intr.c cc2430_rf_intr.c dma_intr.c adc.c +CONTIKI_SOURCEFILES += bus.c clock.c uart0.c uart1.c cc2430_rf.c dma.c +CONTIKI_SOURCEFILES += uart_intr.c cc2430_rf_intr.c dma_intr.c +CONTIKI_SOURCEFILES += watchdog-cc2430.c rtimer-arch.c CONTIKI_ASMFILES += -CONTIKI_ASMOBJECTFILES = ${addprefix $(OBJECTDIR)/,$(CONTIKI_ASMFILES:.S=.o)} +CONTIKI_ASMOBJECTFILES = $(addprefix $(OBJECTDIR)/,$(CONTIKI_ASMFILES:.S=.rel)) -CONTIKI_CASMOBJECTFILES = ${addprefix $(OBJECTDIR)/,$(CONTIKI_CASMFILES:.cS=.o)} +CONTIKI_CASMOBJECTFILES = $(addprefix $(OBJECTDIR)/, \ + $(CONTIKI_CASMFILES:.cS=.rel)) CONTIKI_PLATFORM_DIRS = $(PLATFORM_APPDIRS) \ - ${addprefix $(CONTIKI)/platform/$(TARGET)/, $(CONTIKI_TARGET_DIRS)} + $(addprefix $(CONTIKI)/platform/$(TARGET)/, $(CONTIKI_TARGET_DIRS)) -CONTIKI_CPU_DIRS_LIST = ${addprefix $(CONTIKI_CPU)/, \ - $(CONTIKI_CPU_DIRS)} +CONTIKI_CPU_DIRS_LIST = $(addprefix $(CONTIKI_CPU)/, \ + $(CONTIKI_CPU_DIRS)) + +oname = $(patsubst %.c,%.rel,$(patsubst %.S,%.rel,$(1))) + +CONTIKI_OBJECTFILES = $(addprefix $(OBJECTDIR)/, \ + $(call oname, $(CONTIKI_SOURCEFILES))) + +PROJECT_OBJECTFILES = $(addprefix $(OBJECTDIR)/, \ + $(call oname, $(PROJECT_SOURCEFILES))) ### Compilation rules +SEGMENT_RULE_FILES = $(foreach dir, . $(CONTIKI_PLATFORM_DIRS) \ + $(CONTIKI_CPU_DIRS_LIST), $(wildcard $(dir)/segment.rules) ) -SEGMENT_RULE_FILES = ${foreach dir, ${CONTIKI_PLATFORM_DIRS} ${CONTIKI_CPU_DIRS_LIST}, ${wildcard $(dir)/segment.rules} } -${OBJECTDIR}/segment.rules: ${SEGMENT_RULE_FILES} - echo ${SEGMENT_RULE_FILES} - cat ${SEGMENT_RULE_FILES} > $@ - +$(SEGMENT_RULES): $(SEGMENT_RULE_FILES) + cat $(SEGMENT_RULE_FILES) | \ + sed -e 's/#.*$$//' -e 's/^\s*//' -e '/^$$/d' > $@ +CUSTOM_RULE_LINK=1 CUSTOM_RULE_C_TO_OBJECTDIR_O=1 CUSTOM_RULE_ALLOBJS_TO_TARGETLIB=1 -ifdef CUSTOM_RULE_C_TO_OBJECTDIR_O -ifeq ($(HAVE_BANKING),1) -$(OBJECTDIR)/%.o: %.c ${OBJECTDIR}/segment.rules - @echo "Compile:"$<" to segment " $(call code_segment,$<) - $(CC) $(CFLAGS) -MM -c $< > $(@:.o=.d) - $(CC) $(call code_segment,$<) $(CFLAGS) -c $< -o $@ - @$(FINALIZE_DEPENDENCY) -else -$(OBJECTDIR)/%.o: %.c - $(CC) $(CFLAGS) -c $< -o $@ - $(CC) $(CFLAGS) -MM -c $< > $(@:.o=.d) - @$(FINALIZE_DEPENDENCY) -endif -endif +$(OBJECTDIR)/%.rel: %.c $(SEGMENT_RULES) + $(CC) $(call c_seg,$<,$@) $(CFLAGS) -c $< -o $@ -Wp,-MMD,$(@:.rel=.d),-MQ,$@ + @$(FINALIZE_SDCC_DEPENDENCY) -$(OBJECTDIR)/%.rel: $(OBJECTDIR)/%.o - cp $< $@ - - -ifdef CUSTOM_RULE_CS_TO_OBJECTDIR_O -$(OBJECTDIR)/%.o: %.cS +$(OBJECTDIR)/%.rel: %.cS cp $< $(OBJECTDIR)/$*.c $(CC) $(CFLAGS) -E $(OBJECTDIR)/$*.c > $(OBJECTDIR)/tmp perl -pe "s/^#(.*)/;$$1/" $(OBJECTDIR)/tmp > $(OBJECTDIR)/$*.S $(AS) $(ASFLAGS) -o $@ $(OBJECTDIR)/$*.S rm -f $(OBJECTDIR)/tmp -endif -#CUSTOM_RULE_ALLOBJS_TO_TARGETLIB -contiki-$(TARGET).lib: $(CONTIKI_OBJECTFILES) $(PROJECT_OBJECTFILES) $(CONTIKI_ASMOBJECTFILES) $(CONTIKI_CASMOBJECTFILES) +contiki-$(TARGET).lib: $(CONTIKI_OBJECTFILES) $(PROJECT_OBJECTFILES) \ + $(CONTIKI_ASMOBJECTFILES) $(CONTIKI_CASMOBJECTFILES) rm -f $@ for target in $^; do echo $$target >> $@; done -%.$(TARGET): %.ihx %_linear.hex +.PRECIOUS: %.$(TARGET) %.hex -# .rel is the object file default suffix under sdcc -%.rel: %.co - mv $< $@ +# build app/example local object files. We need a separate rule so that we can +# pass -DAUTOSTART_ENABLE for those files only +$(OBJECTDIR)/%.app.rel: %.c $(SEGMENT_RULES) + $(CC) $(call c_seg,$<,$@) -DAUTOSTART_ENABLE $(CFLAGS) -c $< -o $@ # .ihx is the sdcc binary output file -.PRECIOUS: %.ihx %.rel %_linear.hex +%.ihx: $(OBJECTDIR)/%.app.rel $(CONTIKI_TARGET_MAIN) contiki-$(TARGET).lib +# Automatic bank relocation when building banked code +ifeq ($(HAVE_BANKING),1) + @echo "\nFirst Link" + @echo "===============" + $(CC) $(LDFLAGS) -o $@ $(CONTIKI_TARGET_MAIN) $(OBJECTDIR)/$*.app.rel -llibsdcc.lib -lcontiki-$(TARGET).lib > /dev/null + @echo "\nBank Allocation" + @echo "===============" + python $(BANK_ALLOC) $(basename $(@F)) $(SEGMENT_RULES) $(OFFSET_FIRMWARE) + @echo "\nFinal Link" + @echo "===============" +endif + $(CC) $(LDFLAGS) $(LD_POST_FLAGS) -o $@ $(CONTIKI_TARGET_MAIN) $(OBJECTDIR)/$*.app.rel -llibsdcc.lib -lcontiki-$(TARGET).lib > /dev/null -# .ihx is the sdcc binary output file -%.ihx: %.rel $(CONTIKI_TARGET_MAIN:.o=.rel) contiki-$(TARGET).lib - $(CC) $(LDFLAGS) -o $@ $(CONTIKI_TARGET_MAIN:.o=.rel) $*.rel -llibsdcc.lib -lcontiki-$(TARGET).lib - -%_linear.hex: %.ihx - $(CONTIKI)/cpu/cc2430/converter/converter -f $< $@ - -# Force the compilation of %.$(TARGET) to compile the %.ihx file. -%.$(TARGET): %.ihx %_linear.hex - @ +# Pack the hex file for programmers which dislike SDCC output hex format +%.hex: %.ihx + @echo "\nPack hex file" + @echo "===============" +ifeq ($(HAVE_BANKING),1) + srec_cat -disable_sequence_warnings $< -intel -crop 0x10000 0x1FFFF -offset -0x10000 -o bank1.hex -intel + srec_cat -disable_sequence_warnings $< -intel -crop 0x20000 0x2FFFF -offset -0x18000 -o bank2.hex -intel + srec_cat -disable_sequence_warnings $< -intel -crop 0x30000 0x3FFFF -offset -0x20000 -o bank3.hex -intel + srec_cat -disable_sequence_warnings $< -intel -crop 0x00000 0x0FFFF -o home.ihx -intel + srec_cat home.ihx -intel bank1.hex -intel bank2.hex -intel bank3.hex -intel -o $@ -intel + rm -f home.ihx bank1.hex bank2.hex bank3.hex +else + $(PACKIHX) $< > $@ +endif diff --git a/cpu/cc2430/bank-alloc.py b/cpu/cc2430/bank-alloc.py new file mode 100644 index 000000000..10e452577 --- /dev/null +++ b/cpu/cc2430/bank-alloc.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python + +# Copyright (c) 2010, Loughborough University - Computer Science +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. +# +# This file is part of the Contiki operating system. + +# \file +# Automatic allocation of modules to code segments for bankable builds +# with SDCC's huge memory model. +# +# \author +# George Oikonomou - +import sys +import re +import operator +import fileinput +import os + +# Open a module object file (.rel) and read it's code size +def retrieve_module_size(file_name): + size_pat = re.compile('^A\s+(?:HOME|BANK[0-9])\s+size\s+([1-9A-F][0-9A-F]*)') + for code_line in open(file_name): + matches = size_pat.search(code_line) + if matches is not None: + return int(matches.group(1), 16) + return 0 + +# Searches for a code segment rule for file_name in the segment_rules file +# If there is a rule, we respect it. Otherwise, we can move the file around +def get_source_seg(source_file, object_file, segment_rules): + for line in open(segment_rules): + tokens = line.split(None) + match = re.search(tokens[1], source_file) + if match is not None: + # Save it in basename.seg + base, ext = os.path.splitext(object_file) + of = open(base + '.seg', 'w') + of.write(tokens[0] + '\n') + of.close + return tokens[0] + return None + +# If segment.rules specified a rule for a source file, the respective object +# file's banking requirement will be stored in object_file.seg +def get_object_seg(object_file): + base, ext = os.path.splitext(object_file) + seg = base + '.seg' + bank = None + if os.path.isfile(seg) is True: + of = open(base + '.seg', 'r') + bank = of.readline().strip() + of.close() + return bank + +# Open project.mem and retreive the project's total code footprint +def get_total_size(project): + mem_file = project + '.mem' + pat = re.compile('FLASH\s+(0x[0-9a-f]+\s+){2}([0-9]+)') + for line in open(mem_file): + l = pat.search(line) + if l is not None: + return int(l.group(2)) + +# Open project.map and retrieve the list of modules linked in +# This will only consider contiki sources, not SDCC libraries +# NB: Sometimes object filenames get truncated: +# contiki-sensinode.lib [ obj_sensinode/watchdog-cc2430.re ] +# See how for this file the 'l' in 'rel' is missing. For that reason, we retrieve +# the filaname until the last '.' but without the extension and we append 'rel' +# As long as the filename doesn't get truncated, we're good +def populate(project, modules, segment_rules, bins): + bankable_total = 0 + user_total = 0 + + map_file = project + '.map' + file_pat = re.compile('obj_sensinode[^ ]+\.') + for line in open(map_file): + file_name = file_pat.search(line) + if file_name is not None: + mod = file_name.group(0) + 'rel' + code_size = retrieve_module_size(mod) + seg = get_object_seg(mod) + if seg is not None: + # This module has been assigned to a bank by the user + #print 'In', seg, file_name.group(0), 'size', code_size + bins[seg][0] += code_size + user_total += code_size + else: + # We are free to allocate this module + modules.append([mod, code_size, "NONE"]) + bankable_total += code_size + return bankable_total, user_total + +# Allocate bankable modules to banks according to a simple +# 'first fit, decreasing' bin packing heuristic. +def bin_pack(modules, bins, offset, log): + if offset==1: + bins['HOME'][1] -= 4096 + + # Sort by size, descending, in=place + modules.sort(key=operator.itemgetter(1), reverse=True) + + for module in modules: + # We want to iterate in a specific order and dict.keys() won't do that + for bin_id in ['BANK1', 'BANK2', 'BANK3', 'HOME']: + if bins[bin_id][0] + module[1] < bins[bin_id][1]: + bins[bin_id][0] += module[1] + module[2] = bin_id + log.writelines(' '.join([module[2].ljust(8), \ + str(module[1]).rjust(5), module[0], '\n'])) + break + else: + if bin_id == 'HOME': + print "Failed to allocate", module[0], "with size", module[1], \ + "to a code bank. This is fatal" + return 1 + return 0 + +# Hack the new bank directly in the .rel file +def relocate(module, bank): + code_pat = re.compile('(A\s+)(?:HOME|BANK[0-9])(\s+size\s+[1-9A-F][0-9A-F]*.+\n)') + + for line in fileinput.input(module, inplace=1): + m = code_pat.search(line) + if m is not None: + line = m.group(1) + bank + m.group(2) + sys.stdout.write(line) + return + +if len(sys.argv) < 3: + print 'Usage:' + print 'bank-alloc.py project path_to_segment_rules [offset]' + print 'bank-alloc.py source_file path_to_segment_rules object_file' + sys.exit(1) + +modules = list() +file_name = sys.argv[1] +segment_rules = sys.argv[2] + +# Magic: Guess whether we want to determine the code bank for a code file +# or whether we want to bin-pack +basename, ext = os.path.splitext(file_name) +if ext == '.c': + # Code Segment determination + if len(sys.argv) < 4: + print 'Usage:' + print 'bank-alloc.py project path_to_segment_rules [offset]' + print 'bank-alloc.py source_file path_to_segment_rules object_file' + sys.exit(1) + object_file = sys.argv[3] + seg = get_source_seg(file_name, object_file, segment_rules) + if seg is None: + print "BANK1" + else: + print seg + exit() + +# Bin-Packing +offset = 0 +if len(sys.argv) > 3 and sys.argv[3] is not None: + offset = int(sys.argv[3]) + +sizes = {'total': 0, 'bankable': 0, 'user': 0, 'libs': 0} + +# Name : [Allocated, capacity] +bins = { + 'HOME': [0, 32768], + 'BANK1': [0, 32768], + 'BANK2': [0, 32768], + 'BANK3': [0, 30720] +} + +sizes['total'] = get_total_size(basename) +sizes['bankable'], sizes['user'] = populate(basename, modules, segment_rules, bins) +sizes['libs'] = sizes['total'] - sizes['bankable'] - sizes['user'] + +print 'Total Size =', sizes['total'], 'bytes (' + \ + str(sizes['bankable']), 'bankable,', \ + str(sizes['user']), 'user-allocated,', \ + str(sizes['libs']), 'const+libs)' + +bins['HOME'][0] += sizes['libs'] + +print 'Preallocations: HOME=' + str(bins['HOME'][0]) + \ + ', BANK1=' + str(bins['BANK1'][0]) + ', BANK2=' + str(bins['BANK2'][0]) + \ + ', BANK3=' + str(bins['BANK3'][0]) + +# Open a log file +of = open(basename + '.banks', 'w') +pack = bin_pack(modules, bins, offset, of) +of.close() + +print "Bin-Packing results (target allocation):" +print "Segment - max - alloc" +for bin_id in ['HOME', 'BANK1', 'BANK2', 'BANK3']: + print bin_id.rjust(7), str(bins[bin_id][1]).rjust(6), str(bins[bin_id][0]).rjust(6) + +if pack > 0: + sys.exit(1) + +# If we reach here we seem to have a sane allocation. Start changing .rel files +for module in modules: + relocate(module[0], module[2]) diff --git a/cpu/cc2430/cc2430_sfr.h b/cpu/cc2430/cc2430_sfr.h index d464bc690..53581cd29 100644 --- a/cpu/cc2430/cc2430_sfr.h +++ b/cpu/cc2430/cc2430_sfr.h @@ -1,706 +1,709 @@ -/** - * - * \file cc2430_sfr.h - * \brief CC2430 registers header file for CC2430. - * - * Definitions for CC2430 SFR registers. - * - * - */ - -#ifndef REG_CC2430_H -#define REG_CC2430_H - -/* BYTE Register */ - -__sfr __at (0x80) P0 ; -/* P0 */ -__sbit __at (0x87) P0_7 ; -__sbit __at (0x86) P0_6 ; -__sbit __at (0x85) P0_5 ; -__sbit __at (0x84) P0_4 ; -__sbit __at (0x83) P0_3 ; -__sbit __at (0x82) P0_2 ; -__sbit __at (0x81) P0_1 ; -__sbit __at (0x80) P0_0 ; - -__sfr __at (0x81) SP ; -__sfr __at (0x82) DPL0 ; -__sfr __at (0x83) DPH0 ; -/*DPL and DPH correspond DPL0 and DPH0 (82-83)*/ -__sfr __at (0x84) DPL1; -__sfr __at (0x85) DPH1; -__sfr __at (0x86) U0CSR; -#define U_MODE 0x80 -#define U_RE 0x40 -#define U_SLAVE 0x20 -#define U_FE 0x10 -#define U_ERR 0x08 -#define U_RXB 0x04 -#define U_TXB 0x02 -#define U_ACTIVE 0x01 - -__sfr __at (0x87) PCON ; -/* PCON (0x87) */ -#define IDLE 0x01 - -__sfr __at (0x88) TCON ; -/* TCON (0x88) */ -__sbit __at (0x8F) TCON_URX1IF; -/*__sbit __at (0x8E) RES;*/ -__sbit __at (0x8D) TCON_ADCIF; -/*__sbit __at (0x8C) RES;*/ -__sbit __at (0x8B) TCON_URX0IF; -__sbit __at (0x8A) TCON_IT1; -__sbit __at (0x89) TCON_RFERRIF; -__sbit __at (0x88) TCON_IT0; - - -__sfr __at (0x89) P0IFG; -__sfr __at (0x8A) P1IFG; -__sfr __at (0x8B) P2IFG; -__sfr __at (0x8C) PICTL; -/*PICTL bits*/ -#define PADSC 0x40 -#define P2IEN 0x20 -#define P0IENH 0x10 -#define P0IENL 0x08 -#define P2ICON 0x04 -#define P1ICON 0x02 -#define P0ICON 0x01 - -__sfr __at (0x8D) P1IEN; -__sfr __at (0x8F) P0INP; - -__sfr __at (0x90) P1 ; -/* P1 */ -__sbit __at (0x90) P1_0 ; -__sbit __at (0x91) P1_1 ; -__sbit __at (0x92) P1_2 ; -__sbit __at (0x93) P1_3 ; -__sbit __at (0x94) P1_4 ; -__sbit __at (0x95) P1_5 ; -__sbit __at (0x96) P1_6 ; -__sbit __at (0x97) P1_7 ; - -__sfr __at (0x91) RFIM; -__sfr __at (0x92) DPS; -__sfr __at (0x93) _XPAGE; /*MPAGE as paging register for sdcc*/ -__sfr __at (0x94) T2CMP; -__sfr __at (0x95) ST0; -__sfr __at (0x96) ST1; -__sfr __at (0x97) ST2; -__sfr __at (0x98) S0CON ; - -__sbit __at (0x99) S0CON_ENCIF_1; -__sbit __at (0x98) S0CON_ENCIF_0; - -__sfr __at (0x99) HSRC; -__sfr __at (0x9A) IEN2; -/*IEN2 bits*/ -#define WDTIE 0x20 -#define P1IE 0x10 -#define UTX1IE 0x08 -#define UTX0IE 0x04 -#define P2IE 0x02 -#define RFIE 0x01 -__sfr __at (0x9B) S1CON; -/*S1CON bits*/ -#define RFIF_1 0x02 -#define RFIF_0 0x01 -__sfr __at (0x9C) T2PEROF0; -__sfr __at (0x9D) T2PEROF1; -__sfr __at (0x9E) T2PEROF2; -/*T2PEROF2 bits*/ -#define CMPIM 0x80 -#define PERIM 0x40 -#define OFCMPIM 0x20 - -#define PEROF23 0x08 -#define PEROF22 0x04 -#define PEROF21 0x02 -#define PEROF20 0x01 - -__sfr __at (0x9F) FMAP; -__sfr __at (0x9F) PSBANK; - -__sfr __at (0xA0) P2 ; -/* P2 */ -__sbit __at (0xA0) P2_0 ; -__sbit __at (0xA1) P2_1 ; -__sbit __at (0xA2) P2_2 ; -__sbit __at (0xA3) P2_3 ; -__sbit __at (0xA4) P2_4 ; -/*__sbit __at (0xA5) P2_5 ; -__sbit __at (0xA6) P2_6 ; -__sbit __at (0xA7) P2_7 ;*/ - -__sfr __at (0xA1) T2OF0; -__sfr __at (0xA2) T2OF1; -__sfr __at (0xA3) T2OF2; -__sfr __at (0xA4) T2CAPLPL; -__sfr __at (0xA5) T2CAPHPH; -__sfr __at (0xA6) T2TLD; -__sfr __at (0xA7) T2THD; - -__sfr __at (0xA8) IE ; -__sfr __at (0xA8) IEN0; -/*IEN0 bits*/ -#define IEN0_EA_MASK 0x80 -#define STIE 0x20 -#define ENCIE 0x10 -#define URX1IE 0x08 -#define URX0IE 0x04 -#define ADCIE 0x02 -#define RFERRIE 0x01 -/* IEN0 (0xA8) */ -__sbit __at (0xAF) EA; -__sbit __at (0xAF) IEN0_EA; -/*__sbit __at (0xAE) RES;*/ -__sbit __at (0xAD) IEN0_STIE; -__sbit __at (0xAC) IEN0_ENCIE; -__sbit __at (0xAB) IEN0_URX1IE; -__sbit __at (0xAA) IEN0_URX0IE; -__sbit __at (0xA9) IEN0_ADCIE; -__sbit __at (0xA8) IEN0_RFERRIE; - -__sfr __at (0xA9) IP0; -/*IP0 bits*/ -#define IP0_5 0x20 -#define IP0_4 0x10 -#define IP0_3 0x08 -#define IP0_2 0x04 -#define IP0_1 0x02 -#define IP0_0 0x01 -__sfr __at (0xAB) FWT; -__sfr __at (0xAC) FADDRL; -__sfr __at (0xAD) FADDRH; - -__sfr __at (0xAE) FCTL; -#define F_BUSY 0x80 -#define F_SWBSY 0x40 -#define F_CONTRD 0x10 -#define F_WRITE 0x02 -#define F_ERASE 0x01 -__sfr __at (0xAF) FWDATA; - -/*No port 3 (0xB0)*/ -__sfr __at (0xB1) ENCDI; -__sfr __at (0xB2) ENCDO; -__sfr __at (0xB3) ENCCS; -#define CCS_MODE2 0x40 -#define CCS_MODE1 0x20 -#define CCS_MODE0 0x10 -#define CCS_RDY 0x08 -#define CCS_CMD1 0x04 -#define CCS_CMD0 0x02 -#define CCS_ST 0x01 -__sfr __at (0xB4) ADCCON1; -/*ADCCON1 bits*/ -#define ADEOC 0x80 -#define ADST 0x40 -#define ADSTS1 0x20 -#define ADSTS0 0x10 -#define ADRCTRL1 0x08 -#define ADRCTRL0 0x04 -__sfr __at (0xB5) ADCCON2; -/*ADCCON2 bits*/ -#define ADSREF1 0x80 -#define ADSREF0 0x40 -#define ADSDIV1 0x20 -#define ADSDIV0 0x10 -#define ADSCH3 0x08 -#define ADSCH2 0x04 -#define ADSCH1 0x02 -#define ADSCH0 0x01 -__sfr __at (0xB6) ADCCON3; -/*ADCCON3 bits*/ -#define ADEREF1 0x80 -#define ADEREF0 0x40 -#define ADEDIV1 0x20 -#define ADEDIV0 0x10 -#define ADECH3 0x08 -#define ADECH2 0x04 -#define ADECH1 0x02 -#define ADECH0 0x01 - -__sfr __at (0xB7) RCCTL; -#undef IP /*this is 0xb8 in base core*/ - -__sfr __at (0xB8) IEN1; -/*IEN1 bits*/ -#define P0IE 0x20 -#define T4IE 0x10 -#define T3IE 0x08 -#define T2IE 0x04 -#define T1IE 0x02 -#define DMAIE 0x01 -/* IEN1 (0xB8) */ -/*__sbit __at (0xBF) IEN1_RES;*/ -/*__sbit __at (0xBE) RES;*/ -__sbit __at (0xBD) IEN1_P0IE; -__sbit __at (0xBC) IEN1_T4IE; -__sbit __at (0xBB) IEN1_T3IE; -__sbit __at (0xBA) IEN1_T2IE; -__sbit __at (0xB9) IEN1_T1IE; -__sbit __at (0xB8) IEN1_DMAIE; - -__sfr __at (0xB9) IP1; -/*IP1 bits*/ -#define IP1_5 0x20 -#define IP1_4 0x10 -#define IP1_3 0x08 -#define IP1_2 0x04 -#define IP1_1 0x02 -#define IP1_0 0x01 - -__sfr __at (0xBA) ADCL; -__sfr __at (0xBB) ADCH; -__sfr __at (0xBC) RNDL; -__sfr __at (0xBD) RNDH; - -__sfr __at (0xBE) SLEEP; -#define XOSC_STB 0x40 -#define HFRC_STB 0x20 -#define RST1 0x10 -#define RST0 0x08 -#define OSC_PD 0x04 -#define SLEEP_MODE1 0x02 -#define SLEEP_MODE0 0x01 - -__sfr __at (0xC0) IRCON; -/*IRCON bits*/ -#define STIF 0x80 -#define P0IF 0x20 -#define T4IF 0x10 -#define T3IF 0x08 -#define T2IF 0x04 -#define T1IF 0x02 -#define DMAIF 0x01 -/* IRCON */ -__sbit __at (0xC7) IRCON_STIF ; -/*__sbit __at (0x86) IRCON_6 ;*/ -__sbit __at (0xC5) IRCON_P0IF; -__sbit __at (0xC4) IRCON_T4IF; -__sbit __at (0xC3) IRCON_T3IF; -__sbit __at (0xC2) IRCON_T2IF; -__sbit __at (0xC1) IRCON_T1IF; -__sbit __at (0xC0) IRCON_DMAIF; - -__sfr __at (0xC1) U0BUF; - -__sfr __at (0xC2) U0BAUD; -__sfr __at (0xC3) T2CNF; -/*T2SEL bits*/ -#define CMPIF 0x80 -#define PERIF 0x40 -#define OFCMPIF 0x20 - -#define CMSEL 0x08 - -#define SYNC 0x02 -#define RUN 0x01 - -__sfr __at (0xC4) U0UCR; -#define U_FLUSH 0x80 -#define U_FLOW 0x40 -#define U_D9 0x20 -#define U_BIT9 0x10 -#define U_PARITY 0x08 -#define U_SPB 0x04 -#define U_STOP 0x02 -#define U_START 0x01 - -__sfr __at (0xC5) U0GCR; -#define U_CPOL 0x80 -#define U_CPHA 0x40 -#define U_ORDER 0x20 -#define U_BAUD_E4 0x10 -#define U_BAUD_E3 0x08 -#define U_BAUD_E2 0x04 -#define U_BAUD_E1 0x02 -#define U_BAUD_E0 0x01 - -__sfr __at (0xC6) CLKCON; -#define OSC32K 0x80 -#define OSC 0x40 -#define TICKSPD2 0x20 -#define TICKSPD1 0x10 -#define TICKSPD0 0x08 -#define CLKSPD 0x01 - -__sfr __at (0xC7) MEMCTR; -#define MUNIF 0x40 -__sfr __at (0xC8) T2CON; - -__sfr __at (0xC9) WDCTL; -#define WDT_CLR3 0x80 -#define WDT_CLR2 0x40 -#define WDT_CLR1 0x20 -#define WDT_CLR0 0x10 -#define WDT_EN 0x08 -#define WDT_MODE 0x04 -#define WDT_INT1 0x02 -#define WDT_INT0 0x01 - -__sfr __at (0xCA) T3CNT; - -__sfr __at (0xCB) T3CTL; -/*T3CTL bits*/ -#define T3DIV2 0x80 -#define T3DIV1 0x40 -#define T3DIV0 0x20 -#define T3START 0x10 -#define T3OVFIM 0x08 -#define T3CLR 0x04 -#define T3MODE1 0x02 -#define T3MODE0 0x01 - -__sfr __at (0xCC) T3CCTL0; -/*T3CCTL0 bits*/ -#define T3IM 0x40 -#define T3CMP2 0x20 -#define T3CMP1 0x10 -#define T3CMP0 0x08 -#define T3MODE 0x04 -#define T3CAP1 0x02 -#define T3CAP0 0x01 - -__sfr __at (0xCD) T3CC0; -__sfr __at (0xCE) T3CCTL1; -/*T3CCTL0 bits apply*/ -__sfr __at (0xCF) T3CC1; - -__sfr __at (0xD0) PSW ; -/* PSW */ -__sbit __at (0xD0) P ; -__sbit __at (0xD1) F1 ; -__sbit __at (0xD2) OV ; -__sbit __at (0xD3) RS0 ; -__sbit __at (0xD4) RS1 ; -__sbit __at (0xD5) F0 ; -__sbit __at (0xD6) AC ; -__sbit __at (0xD7) CY ; - -__sfr __at (0xD1) DMAIRQ; -/*DMAIRQ bits*/ -#define DMAIF4 0x10 -#define DMAIF3 0x08 -#define DMAIF2 0x04 -#define DMAIF1 0x02 -#define DMAIF0 0x01 - -__sfr __at (0xD2) DMA1CFGL; -__sfr __at (0xD3) DMA1CFGH; -__sfr __at (0xD4) DMA0CFGL; -__sfr __at (0xD5) DMA0CFGH; - -__sfr __at (0xD6) DMAARM; -/*DMAARM bits*/ -#define ABORT 0x80 -#define DMAARM4 0x10 -#define DMAARM3 0x08 -#define DMAARM2 0x04 -#define DMAARM1 0x02 -#define DMAARM0 0x01 - -__sfr __at (0xD7) DMAREQ; -/*DMAREQ bits*/ -#define DMAREQ4 0x10 -#define DMAREQ3 0x08 -#define DMAREQ2 0x04 -#define DMAREQ1 0x02 -#define DMAREQ0 0x01 - -__sfr __at (0xD8) TIMIF; -/*TIMIF bits*/ -#define OVFIM 0x40 -#define T4CH1IF 0x20 -#define T4CH0IF 0x10 -#define T4OVFIF 0x08 -#define T3CH1IF 0x04 -#define T3CH0IF 0x02 -#define T3OVFIF 0x01 - -__sfr __at (0xD9) RFD; -__sfr __at (0xDA) T1CC0L; -__sfr __at (0xDB) T1CC0H; -__sfr __at (0xDC) T1CC1L; -__sfr __at (0xDD) T1CC1H; -__sfr __at (0xDE) T1CC2L; -__sfr __at (0xDF) T1CC2H; - -__sfr __at (0xE0) ACC; -__sfr __at (0xE1) RFST; -__sfr __at (0xE2) T1CNTL; -__sfr __at (0xE3) T1CNTH; - -__sfr __at (0xE4) T1CTL; -/*T1CTL bits*/ -#define CH2IF 0x80 -#define CH1IF 0x40 -#define CH0IF 0x20 -#define OVFIF 0x10 -#define T1DIV1 0x08 -#define T1DIV0 0x04 -#define T1MODE1 0x02 -#define T1MODE0 0x01 - -__sfr __at (0xE5) T1CCTL0; -/*T1CCTL0 bits*/ -#define T1CPSEL 0x80 -#define T1IM 0x40 -#define T1CMP2 0x20 -#define T1CMP1 0x10 -#define T1CMP0 0x08 -#define T1MODE 0x04 -#define T1CAP1 0x02 -#define T1CAP0 0x01 - -__sfr __at (0xE6) T1CCTL1; -/*Bits defined in T1CCTL0 */ -__sfr __at (0xE7) T1CCTL2; -/*Bits defined in T1CCTL0 */ -__sfr __at (0xE8) IRCON2; -/*IRCON2 bits*/ -#define WDTIF 0x10 -#define P1IF 0x08 -#define UTX1IF 0x04 -#define UTX0IF 0x02 -#define P2IF 0x01 -/* IRCON 2 */ -/*__sbit __at (0xEF) IRCON2_P1_7 ; -__sbit __at (0xEE) IRCON2_P1_6 ; -__sbit __at (0xED) IRCON2_P1_5 ;*/ -__sbit __at (0xEC) IRCON2_WDTIF ; -__sbit __at (0xEB) IRCON2_P1IF ; -__sbit __at (0xEA) IRCON2_UTX1IF ; -__sbit __at (0xE9) IRCON2_UTX0IF ; -__sbit __at (0xE8) IRCON2_P2IF; - - -__sfr __at (0xE9) RFIF; -/*RFIF bits*/ -#define IRQ_RREG_ON 0x80 -#define IRQ_TXDONE 0x40 -#define IRQ_FIFOP 0x20 -#define IRQ_SFD 0x10 -#define IRQ_CCA 0x08 -#define IRQ_CSP_WT 0x04 -#define IRQ_CSP_STOP 0x02 -#define IRQ_CSP_INT 0x01 - -__sfr __at (0xEA) T4CNT; -__sfr __at (0xEB) T4CTL; -/*T4CTL bits*/ -#define T4DIV2 0x80 -#define T4DIV1 0x40 -#define T4DIV0 0x20 -#define T4START 0x10 -#define T4OVFIM 0x08 -#define T4CLR 0x04 -#define T4MODE1 0x02 -#define T4MODE0 0x01 - -__sfr __at (0xEC) T4CCTL0; -/*T4CCTL0 bits*/ -#define T4IM 0x40 -#define T4CMP2 0x20 -#define T4CMP1 0x10 -#define T4CMP0 0x08 -#define T4MODE 0x04 -#define T4CAP1 0x02 -#define T4CAP0 0x01 - -__sfr __at (0xED) T4CC0; -__sfr __at (0xEE) T4CCTL1; -/*T4CCTL0 bits apply*/ -__sfr __at (0xEF) T4CC1; - -__sfr __at (0xF0) B ; -__sfr __at (0xF1) PERCFG; -/*PERCFG bits*/ -#define T1CFG 0x40 -#define T3CFG 0x20 -#define T4CFG 0x10 -#define U1CFG 0x02 -#define U0CFG 0x01 - -__sfr __at (0xF2) ADCCFG; -/*ADCCFG bits*/ -#define ADC7EN 0x80 -#define ADC6EN 0x40 -#define ADC5EN 0x20 -#define ADC4EN 0x10 -#define ADC3EN 0x08 -#define ADC2EN 0x04 -#define ADC1EN 0x02 -#define ADC0EN 0x01 - -__sfr __at (0xF3) P0SEL; -__sfr __at (0xF4) P1SEL; -__sfr __at (0xF5) P2SEL; -/*P2SEL bits*/ -#define PRI3P1 0x40 -#define PRI2P1 0x20 -#define PRI1P1 0x10 -#define PRI0P1 0x08 -#define SELP2_4 0x04 -#define SELP2_3 0x02 -#define SELP2_0 0x01 - -__sfr __at (0xF6) P1INP; - -__sfr __at (0xF7) P2INP; -/*P2INP bits*/ -#define PDUP2 0x80 -#define PDUP1 0x40 -#define PDUP0 0x20 -#define MDP2_4 0x10 -#define MDP2_3 0x08 -#define MDP2_2 0x04 -#define MDP2_1 0x02 -#define MDP2_0 0x01 - -__sfr __at (0xF8) U1CSR; -__sfr __at (0xF9) U1BUF; -__sfr __at (0xFA) U1BAUD; -__sfr __at (0xFB) U1UCR; -__sfr __at (0xFC) U1GCR; -__sfr __at (0xFD) P0DIR; -__sfr __at (0xFE) P1DIR; - -__sfr __at (0xFF) P2DIR; -/*P2DIR bits*/ -#define PRI1P0 0x80 -#define PRI0P0 0x40 -#define DIRP2_4 0x10 -#define DIRP2_3 0x08 -#define DIRP2_2 0x04 -#define DIRP2_1 0x02 -#define DIRP2_0 0x01 - -/* IEN0 */ -/*__sbit __at (0xA8) EA ; -__sbit __at (0x99) TI ; -__sbit __at (0x9A) RB8 ; -__sbit __at (0x9B) TB8 ; -__sbit __at (0x9C) REN ; -__sbit __at (0x9D) SM2 ; -__sbit __at (0x9E) SM1 ; -__sbit __at (0x9F) SM0 ;*/ - - - -/* Interrupt numbers: address = (number * 8) + 3 */ -/*#undef IE0_VECTOR -#undef TF0_VECTOR -#undef IE1_VECTOR -#undef TF1_VECTOR -#undef SI0_VECTOR*/ - -/* CC2430 interrupt vectors */ -#define RFERR_VECTOR 0 -#define ADC_VECTOR 1 -#define URX0_VECTOR 2 -#define URX1_VECTOR 3 -#define ENC_VECTOR 4 -#define ST_VECTOR 5 -#define P2INT_VECTOR 6 -#define UTX0_VECTOR 7 -#define DMA_VECTOR 8 -#define T1_VECTOR 9 -#define T2_VECTOR 10 -#define T3_VECTOR 11 -#define T4_VECTOR 12 -#define P0INT_VECTOR 13 -#define UTX1_VECTOR 14 -#define P1INT_VECTOR 15 -#define RF_VECTOR 16 -#define WDT_VECTOR 17 - -/* RF control registers*/ -__xdata __at (0xDF02) unsigned char MDMCTRL0H; -__xdata __at (0xDF03) unsigned char MDMCTRL0L; -__xdata __at (0xDF04) unsigned char MDMCTRL1H; -__xdata __at (0xDF05) unsigned char MDMCTRL1L; -__xdata __at (0xDF06) unsigned char RSSIH; -__xdata __at (0xDF07) unsigned char RSSIL; -__xdata __at (0xDF08) unsigned char SYNCWORDH; -__xdata __at (0xDF09) unsigned char SYNCWORDL; -__xdata __at (0xDF0A) unsigned char TXCTRLH; -__xdata __at (0xDF0B) unsigned char TXCTRLL; -__xdata __at (0xDF0C) unsigned char RXCTRL0H; -__xdata __at (0xDF0D) unsigned char RXCTRL0L; -__xdata __at (0xDF0E) unsigned char RXCTRL1H; -__xdata __at (0xDF0F) unsigned char RXCTRL1L; -__xdata __at (0xDF10) unsigned char FSCTRLH; -__xdata __at (0xDF11) unsigned char FSCTRLL; -__xdata __at (0xDF12) unsigned char CSPX; -__xdata __at (0xDF13) unsigned char CSPY; -__xdata __at (0xDF14) unsigned char CSPZ; -__xdata __at (0xDF15) unsigned char CSPCTRL; -__xdata __at (0xDF16) unsigned char CSPT; -__xdata __at (0xDF17) unsigned char RFPWR; -#define ADI_RADIO_PD 0x10 -#define RREG_RADIO_PD 0x08 -#define RREG_DELAY_MASK 0x07 - -__xdata __at (0xDF20) unsigned char FSMTCH; -__xdata __at (0xDF21) unsigned char FSMTCL; -__xdata __at (0xDF22) unsigned char MANANDH; -__xdata __at (0xDF23) unsigned char MANANDL; -__xdata __at (0xDF24) unsigned char MANORH; -__xdata __at (0xDF25) unsigned char MANORL; -__xdata __at (0xDF26) unsigned char AGCCTRLH; -__xdata __at (0xDF27) unsigned char AGCCTRLL; - -__xdata __at (0xDF39) unsigned char FSMSTATE; -__xdata __at (0xDF3A) unsigned char ADCTSTH; -__xdata __at (0xDF3B) unsigned char ADCTSTL; -__xdata __at (0xDF3C) unsigned char DACTSTH; -__xdata __at (0xDF3D) unsigned char DACTSTL; - -__xdata __at (0xDF43) unsigned char IEEE_ADDR0; -__xdata __at (0xDF44) unsigned char IEEE_ADDR1; -__xdata __at (0xDF45) unsigned char IEEE_ADDR2; -__xdata __at (0xDF46) unsigned char IEEE_ADDR3; -__xdata __at (0xDF47) unsigned char IEEE_ADDR4; -__xdata __at (0xDF48) unsigned char IEEE_ADDR5; -__xdata __at (0xDF49) unsigned char IEEE_ADDR6; -__xdata __at (0xDF4A) unsigned char IEEE_ADDR7; -__xdata __at (0xDF4B) unsigned char PANIDH; -__xdata __at (0xDF4C) unsigned char PANIDL; -__xdata __at (0xDF4D) unsigned char SHORTADDRH; -__xdata __at (0xDF4E) unsigned char SHORTADDRL; -__xdata __at (0xDF4F) unsigned char IOCFG0; -__xdata __at (0xDF50) unsigned char IOCFG1; -__xdata __at (0xDF51) unsigned char IOCFG2; -__xdata __at (0xDF52) unsigned char IOCFG3; -__xdata __at (0xDF53) unsigned char RXFIFOCNT; -__xdata __at (0xDF54) unsigned char FSMTC1; -#define ABORTRX_ON_SRXON 0x20 -#define RX_INTERRUPTED 0x10 -#define AUTO_TX2RX_OFF 0x08 -#define RX2RX_TIME_OFF 0x04 -#define PENDING_OR 0x02 -#define ACCEPT_ACKPKT 0x01 - -__xdata __at (0xDF60) unsigned char CHVER; -__xdata __at (0xDF61) unsigned char CHIPID; -__xdata __at (0xDF62) unsigned char RFSTATUS; -#define TX_ACTIVE 0x10 -#define FIFO 0x08 -#define FIFOP 0x04 -#define SFD 0x02 -#define CCA 0x01 - -__xdata __at (0xDFC1) unsigned char U0BUF_SHADOW; - -__xdata __at (0xDFD9) unsigned char RFD_SHADOW; - -__xdata __at (0xDFF9) unsigned char U1BUF_SHADOW; - -__xdata __at (0xDFBA) unsigned int ADC_SHADOW; - -#endif /*REG_CC2430*/ +/** + * + * \file cc2430_sfr.h + * \brief CC2430 registers header file for CC2430. + * + * Definitions for CC2430 SFR registers. + * + * + */ + +#ifndef REG_CC2430_H +#define REG_CC2430_H + +#include "8051def.h" + +/* BYTE Register */ + +__sfr __at (0x80) P0 ; +/* P0 */ +__sbit __at (0x87) P0_7 ; +__sbit __at (0x86) P0_6 ; +__sbit __at (0x85) P0_5 ; +__sbit __at (0x84) P0_4 ; +__sbit __at (0x83) P0_3 ; +__sbit __at (0x82) P0_2 ; +__sbit __at (0x81) P0_1 ; +__sbit __at (0x80) P0_0 ; + +__sfr __at (0x81) SP ; +__sfr __at (0x82) DPL0 ; +__sfr __at (0x83) DPH0 ; +/*DPL and DPH correspond DPL0 and DPH0 (82-83)*/ +__sfr __at (0x84) DPL1; +__sfr __at (0x85) DPH1; +__sfr __at (0x86) U0CSR; +#define U_MODE 0x80 +#define U_RE 0x40 +#define U_SLAVE 0x20 +#define U_FE 0x10 +#define U_ERR 0x08 +#define U_RXB 0x04 +#define U_TXB 0x02 +#define U_ACTIVE 0x01 + +__sfr __at (0x87) PCON ; +/* PCON (0x87) */ +#define IDLE 0x01 + +__sfr __at (0x88) TCON ; +/* TCON (0x88) */ +__sbit __at (0x8F) TCON_URX1IF; +/*__sbit __at (0x8E) RES;*/ +__sbit __at (0x8D) TCON_ADCIF; +/*__sbit __at (0x8C) RES;*/ +__sbit __at (0x8B) TCON_URX0IF; +__sbit __at (0x8A) TCON_IT1; +__sbit __at (0x89) TCON_RFERRIF; +__sbit __at (0x88) TCON_IT0; + + +__sfr __at (0x89) P0IFG; +__sfr __at (0x8A) P1IFG; +__sfr __at (0x8B) P2IFG; +__sfr __at (0x8C) PICTL; +/*PICTL bits*/ +#define PADSC 0x40 +#define P2IEN 0x20 +#define P0IENH 0x10 +#define P0IENL 0x08 +#define P2ICON 0x04 +#define P1ICON 0x02 +#define P0ICON 0x01 + +__sfr __at (0x8D) P1IEN; +__sfr __at (0x8F) P0INP; + +__sfr __at (0x90) P1 ; +/* P1 */ +__sbit __at (0x90) P1_0 ; +__sbit __at (0x91) P1_1 ; +__sbit __at (0x92) P1_2 ; +__sbit __at (0x93) P1_3 ; +__sbit __at (0x94) P1_4 ; +__sbit __at (0x95) P1_5 ; +__sbit __at (0x96) P1_6 ; +__sbit __at (0x97) P1_7 ; + +__sfr __at (0x91) RFIM; +__sfr __at (0x92) DPS; +__sfr __at (0x93) _XPAGE; /*MPAGE as paging register for sdcc*/ +__sfr __at (0x94) T2CMP; +__sfr __at (0x95) ST0; +__sfr __at (0x96) ST1; +__sfr __at (0x97) ST2; +__sfr __at (0x98) S0CON ; + +__sbit __at (0x99) S0CON_ENCIF_1; +__sbit __at (0x98) S0CON_ENCIF_0; + +__sfr __at (0x99) HSRC; +__sfr __at (0x9A) IEN2; +/*IEN2 bits*/ +#define WDTIE 0x20 +#define P1IE 0x10 +#define UTX1IE 0x08 +#define UTX0IE 0x04 +#define P2IE 0x02 +#define RFIE 0x01 +__sfr __at (0x9B) S1CON; +/*S1CON bits*/ +#define RFIF_1 0x02 +#define RFIF_0 0x01 +__sfr __at (0x9C) T2PEROF0; +__sfr __at (0x9D) T2PEROF1; +__sfr __at (0x9E) T2PEROF2; +/*T2PEROF2 bits*/ +#define CMPIM 0x80 +#define PERIM 0x40 +#define OFCMPIM 0x20 + +#define PEROF23 0x08 +#define PEROF22 0x04 +#define PEROF21 0x02 +#define PEROF20 0x01 + +__sfr __at (0x9F) FMAP; +__sfr __at (0x9F) PSBANK; + +__sfr __at (0xA0) P2 ; +/* P2 */ +__sbit __at (0xA0) P2_0 ; +__sbit __at (0xA1) P2_1 ; +__sbit __at (0xA2) P2_2 ; +__sbit __at (0xA3) P2_3 ; +__sbit __at (0xA4) P2_4 ; +/*__sbit __at (0xA5) P2_5 ; +__sbit __at (0xA6) P2_6 ; +__sbit __at (0xA7) P2_7 ;*/ + +__sfr __at (0xA1) T2OF0; +__sfr __at (0xA2) T2OF1; +__sfr __at (0xA3) T2OF2; +__sfr __at (0xA4) T2CAPLPL; +__sfr __at (0xA5) T2CAPHPH; +__sfr __at (0xA6) T2TLD; +__sfr __at (0xA7) T2THD; + +__sfr __at (0xA8) IE ; +__sfr __at (0xA8) IEN0; +/*IEN0 bits*/ +#define IEN0_EA_MASK 0x80 +#define STIE 0x20 +#define ENCIE 0x10 +#define URX1IE 0x08 +#define URX0IE 0x04 +#define ADCIE 0x02 +#define RFERRIE 0x01 +/* IEN0 (0xA8) */ +__sbit __at (0xAF) EA; +__sbit __at (0xAF) IEN0_EA; +/*__sbit __at (0xAE) RES;*/ +__sbit __at (0xAD) IEN0_STIE; +__sbit __at (0xAC) IEN0_ENCIE; +__sbit __at (0xAB) IEN0_URX1IE; +__sbit __at (0xAA) IEN0_URX0IE; +__sbit __at (0xA9) IEN0_ADCIE; +__sbit __at (0xA8) IEN0_RFERRIE; + +__sfr __at (0xA9) IP0; +/*IP0 bits*/ +#define IP0_5 0x20 +#define IP0_4 0x10 +#define IP0_3 0x08 +#define IP0_2 0x04 +#define IP0_1 0x02 +#define IP0_0 0x01 +__sfr __at (0xAB) FWT; +__sfr __at (0xAC) FADDRL; +__sfr __at (0xAD) FADDRH; + +__sfr __at (0xAE) FCTL; +#define F_BUSY 0x80 +#define F_SWBSY 0x40 +#define F_CONTRD 0x10 +#define F_WRITE 0x02 +#define F_ERASE 0x01 +__sfr __at (0xAF) FWDATA; + +/*No port 3 (0xB0)*/ +__sfr __at (0xB1) ENCDI; +__sfr __at (0xB2) ENCDO; +__sfr __at (0xB3) ENCCS; +#define CCS_MODE2 0x40 +#define CCS_MODE1 0x20 +#define CCS_MODE0 0x10 +#define CCS_RDY 0x08 +#define CCS_CMD1 0x04 +#define CCS_CMD0 0x02 +#define CCS_ST 0x01 +__sfr __at (0xB4) ADCCON1; +/*ADCCON1 bits*/ +#define ADEOC 0x80 +#define ADST 0x40 +#define ADSTS1 0x20 +#define ADSTS0 0x10 +#define ADRCTRL1 0x08 +#define ADRCTRL0 0x04 +__sfr __at (0xB5) ADCCON2; +/*ADCCON2 bits*/ +#define ADSREF1 0x80 +#define ADSREF0 0x40 +#define ADSDIV1 0x20 +#define ADSDIV0 0x10 +#define ADSCH3 0x08 +#define ADSCH2 0x04 +#define ADSCH1 0x02 +#define ADSCH0 0x01 +__sfr __at (0xB6) ADCCON3; +/*ADCCON3 bits*/ +#define ADEREF1 0x80 +#define ADEREF0 0x40 +#define ADEDIV1 0x20 +#define ADEDIV0 0x10 +#define ADECH3 0x08 +#define ADECH2 0x04 +#define ADECH1 0x02 +#define ADECH0 0x01 + +__sfr __at (0xB7) RCCTL; +#undef IP /*this is 0xb8 in base core*/ + +__sfr __at (0xB8) IEN1; +/*IEN1 bits*/ +#define P0IE 0x20 +#define T4IE 0x10 +#define T3IE 0x08 +#define T2IE 0x04 +#define T1IE 0x02 +#define DMAIE 0x01 +/* IEN1 (0xB8) */ +/*__sbit __at (0xBF) IEN1_RES;*/ +/*__sbit __at (0xBE) RES;*/ +__sbit __at (0xBD) IEN1_P0IE; +__sbit __at (0xBC) IEN1_T4IE; +__sbit __at (0xBB) IEN1_T3IE; +__sbit __at (0xBA) IEN1_T2IE; +__sbit __at (0xB9) IEN1_T1IE; +__sbit __at (0xB8) IEN1_DMAIE; + +__sfr __at (0xB9) IP1; +/*IP1 bits*/ +#define IP1_5 0x20 +#define IP1_4 0x10 +#define IP1_3 0x08 +#define IP1_2 0x04 +#define IP1_1 0x02 +#define IP1_0 0x01 + +__sfr __at (0xBA) ADCL; +__sfr __at (0xBB) ADCH; +__sfr __at (0xBC) RNDL; +__sfr __at (0xBD) RNDH; + +__sfr __at (0xBE) SLEEP; +#define OSC32K_CALDIS 0x80 +#define XOSC_STB 0x40 +#define HFRC_STB 0x20 +#define RST1 0x10 +#define RST0 0x08 +#define OSC_PD 0x04 +#define SLEEP_MODE1 0x02 +#define SLEEP_MODE0 0x01 + +__sfr __at (0xC0) IRCON; +/*IRCON bits*/ +#define STIF 0x80 +#define P0IF 0x20 +#define T4IF 0x10 +#define T3IF 0x08 +#define T2IF 0x04 +#define T1IF 0x02 +#define DMAIF 0x01 +/* IRCON */ +__sbit __at (0xC7) IRCON_STIF ; +/*__sbit __at (0x86) IRCON_6 ;*/ +__sbit __at (0xC5) IRCON_P0IF; +__sbit __at (0xC4) IRCON_T4IF; +__sbit __at (0xC3) IRCON_T3IF; +__sbit __at (0xC2) IRCON_T2IF; +__sbit __at (0xC1) IRCON_T1IF; +__sbit __at (0xC0) IRCON_DMAIF; + +__sfr __at (0xC1) U0BUF; + +__sfr __at (0xC2) U0BAUD; +__sfr __at (0xC3) T2CNF; +/*T2SEL bits*/ +#define CMPIF 0x80 +#define PERIF 0x40 +#define OFCMPIF 0x20 + +#define CMSEL 0x08 + +#define SYNC 0x02 +#define RUN 0x01 + +__sfr __at (0xC4) U0UCR; +#define U_FLUSH 0x80 +#define U_FLOW 0x40 +#define U_D9 0x20 +#define U_BIT9 0x10 +#define U_PARITY 0x08 +#define U_SPB 0x04 +#define U_STOP 0x02 +#define U_START 0x01 + +__sfr __at (0xC5) U0GCR; +#define U_CPOL 0x80 +#define U_CPHA 0x40 +#define U_ORDER 0x20 +#define U_BAUD_E4 0x10 +#define U_BAUD_E3 0x08 +#define U_BAUD_E2 0x04 +#define U_BAUD_E1 0x02 +#define U_BAUD_E0 0x01 + +__sfr __at (0xC6) CLKCON; +#define OSC32K 0x80 +#define OSC 0x40 +#define TICKSPD2 0x20 +#define TICKSPD1 0x10 +#define TICKSPD0 0x08 +#define CLKSPD 0x01 + +__sfr __at (0xC7) MEMCTR; +#define MUNIF 0x40 +__sfr __at (0xC8) T2CON; + +__sfr __at (0xC9) WDCTL; +#define WDT_CLR3 0x80 +#define WDT_CLR2 0x40 +#define WDT_CLR1 0x20 +#define WDT_CLR0 0x10 +#define WDT_EN 0x08 +#define WDT_MODE 0x04 +#define WDT_INT1 0x02 +#define WDT_INT0 0x01 + +__sfr __at (0xCA) T3CNT; + +__sfr __at (0xCB) T3CTL; +/*T3CTL bits*/ +#define T3DIV2 0x80 +#define T3DIV1 0x40 +#define T3DIV0 0x20 +#define T3START 0x10 +#define T3OVFIM 0x08 +#define T3CLR 0x04 +#define T3MODE1 0x02 +#define T3MODE0 0x01 + +__sfr __at (0xCC) T3CCTL0; +/*T3CCTL0 bits*/ +#define T3IM 0x40 +#define T3CMP2 0x20 +#define T3CMP1 0x10 +#define T3CMP0 0x08 +#define T3MODE 0x04 +#define T3CAP1 0x02 +#define T3CAP0 0x01 + +__sfr __at (0xCD) T3CC0; +__sfr __at (0xCE) T3CCTL1; +/*T3CCTL0 bits apply*/ +__sfr __at (0xCF) T3CC1; + +__sfr __at (0xD0) PSW ; +/* PSW */ +__sbit __at (0xD0) P ; +__sbit __at (0xD1) F1 ; +__sbit __at (0xD2) OV ; +__sbit __at (0xD3) RS0 ; +__sbit __at (0xD4) RS1 ; +__sbit __at (0xD5) F0 ; +__sbit __at (0xD6) AC ; +__sbit __at (0xD7) CY ; + +__sfr __at (0xD1) DMAIRQ; +/*DMAIRQ bits*/ +#define DMAIF4 0x10 +#define DMAIF3 0x08 +#define DMAIF2 0x04 +#define DMAIF1 0x02 +#define DMAIF0 0x01 + +__sfr __at (0xD2) DMA1CFGL; +__sfr __at (0xD3) DMA1CFGH; +__sfr __at (0xD4) DMA0CFGL; +__sfr __at (0xD5) DMA0CFGH; + +__sfr __at (0xD6) DMAARM; +/*DMAARM bits*/ +#define ABORT 0x80 +#define DMAARM4 0x10 +#define DMAARM3 0x08 +#define DMAARM2 0x04 +#define DMAARM1 0x02 +#define DMAARM0 0x01 + +__sfr __at (0xD7) DMAREQ; +/*DMAREQ bits*/ +#define DMAREQ4 0x10 +#define DMAREQ3 0x08 +#define DMAREQ2 0x04 +#define DMAREQ1 0x02 +#define DMAREQ0 0x01 + +__sfr __at (0xD8) TIMIF; +/*TIMIF bits*/ +#define OVFIM 0x40 +#define T4CH1IF 0x20 +#define T4CH0IF 0x10 +#define T4OVFIF 0x08 +#define T3CH1IF 0x04 +#define T3CH0IF 0x02 +#define T3OVFIF 0x01 + +__sfr __at (0xD9) RFD; +__sfr __at (0xDA) T1CC0L; +__sfr __at (0xDB) T1CC0H; +__sfr __at (0xDC) T1CC1L; +__sfr __at (0xDD) T1CC1H; +__sfr __at (0xDE) T1CC2L; +__sfr __at (0xDF) T1CC2H; + +__sfr __at (0xE0) ACC; +__sfr __at (0xE1) RFST; +__sfr __at (0xE2) T1CNTL; +__sfr __at (0xE3) T1CNTH; + +__sfr __at (0xE4) T1CTL; +/*T1CTL bits*/ +#define CH2IF 0x80 +#define CH1IF 0x40 +#define CH0IF 0x20 +#define OVFIF 0x10 +#define T1DIV1 0x08 +#define T1DIV0 0x04 +#define T1MODE1 0x02 +#define T1MODE0 0x01 + +__sfr __at (0xE5) T1CCTL0; +/*T1CCTL0 bits*/ +#define T1CPSEL 0x80 +#define T1IM 0x40 +#define T1CMP2 0x20 +#define T1CMP1 0x10 +#define T1CMP0 0x08 +#define T1MODE 0x04 +#define T1CAP1 0x02 +#define T1CAP0 0x01 + +__sfr __at (0xE6) T1CCTL1; +/*Bits defined in T1CCTL0 */ +__sfr __at (0xE7) T1CCTL2; +/*Bits defined in T1CCTL0 */ +__sfr __at (0xE8) IRCON2; +/*IRCON2 bits*/ +#define WDTIF 0x10 +#define P1IF 0x08 +#define UTX1IF 0x04 +#define UTX0IF 0x02 +#define P2IF 0x01 +/* IRCON 2 */ +/*__sbit __at (0xEF) IRCON2_P1_7 ; +__sbit __at (0xEE) IRCON2_P1_6 ; +__sbit __at (0xED) IRCON2_P1_5 ;*/ +__sbit __at (0xEC) IRCON2_WDTIF ; +__sbit __at (0xEB) IRCON2_P1IF ; +__sbit __at (0xEA) IRCON2_UTX1IF ; +__sbit __at (0xE9) IRCON2_UTX0IF ; +__sbit __at (0xE8) IRCON2_P2IF; + + +__sfr __at (0xE9) RFIF; +/*RFIF bits*/ +#define IRQ_RREG_ON 0x80 +#define IRQ_TXDONE 0x40 +#define IRQ_FIFOP 0x20 +#define IRQ_SFD 0x10 +#define IRQ_CCA 0x08 +#define IRQ_CSP_WT 0x04 +#define IRQ_CSP_STOP 0x02 +#define IRQ_CSP_INT 0x01 + +__sfr __at (0xEA) T4CNT; +__sfr __at (0xEB) T4CTL; +/*T4CTL bits*/ +#define T4DIV2 0x80 +#define T4DIV1 0x40 +#define T4DIV0 0x20 +#define T4START 0x10 +#define T4OVFIM 0x08 +#define T4CLR 0x04 +#define T4MODE1 0x02 +#define T4MODE0 0x01 + +__sfr __at (0xEC) T4CCTL0; +/*T4CCTL0 bits*/ +#define T4IM 0x40 +#define T4CMP2 0x20 +#define T4CMP1 0x10 +#define T4CMP0 0x08 +#define T4MODE 0x04 +#define T4CAP1 0x02 +#define T4CAP0 0x01 + +__sfr __at (0xED) T4CC0; +__sfr __at (0xEE) T4CCTL1; +/*T4CCTL0 bits apply*/ +__sfr __at (0xEF) T4CC1; + +__sfr __at (0xF0) B ; +__sfr __at (0xF1) PERCFG; +/*PERCFG bits*/ +#define T1CFG 0x40 +#define T3CFG 0x20 +#define T4CFG 0x10 +#define U1CFG 0x02 +#define U0CFG 0x01 + +__sfr __at (0xF2) ADCCFG; +/*ADCCFG bits*/ +#define ADC7EN 0x80 +#define ADC6EN 0x40 +#define ADC5EN 0x20 +#define ADC4EN 0x10 +#define ADC3EN 0x08 +#define ADC2EN 0x04 +#define ADC1EN 0x02 +#define ADC0EN 0x01 + +__sfr __at (0xF3) P0SEL; +__sfr __at (0xF4) P1SEL; +__sfr __at (0xF5) P2SEL; +/*P2SEL bits*/ +#define PRI3P1 0x40 +#define PRI2P1 0x20 +#define PRI1P1 0x10 +#define PRI0P1 0x08 +#define SELP2_4 0x04 +#define SELP2_3 0x02 +#define SELP2_0 0x01 + +__sfr __at (0xF6) P1INP; + +__sfr __at (0xF7) P2INP; +/*P2INP bits*/ +#define PDUP2 0x80 +#define PDUP1 0x40 +#define PDUP0 0x20 +#define MDP2_4 0x10 +#define MDP2_3 0x08 +#define MDP2_2 0x04 +#define MDP2_1 0x02 +#define MDP2_0 0x01 + +__sfr __at (0xF8) U1CSR; +__sfr __at (0xF9) U1BUF; +__sfr __at (0xFA) U1BAUD; +__sfr __at (0xFB) U1UCR; +__sfr __at (0xFC) U1GCR; +__sfr __at (0xFD) P0DIR; +__sfr __at (0xFE) P1DIR; + +__sfr __at (0xFF) P2DIR; +/*P2DIR bits*/ +#define PRI1P0 0x80 +#define PRI0P0 0x40 +#define DIRP2_4 0x10 +#define DIRP2_3 0x08 +#define DIRP2_2 0x04 +#define DIRP2_1 0x02 +#define DIRP2_0 0x01 + +/* IEN0 */ +/*__sbit __at (0xA8) EA ; +__sbit __at (0x99) TI ; +__sbit __at (0x9A) RB8 ; +__sbit __at (0x9B) TB8 ; +__sbit __at (0x9C) REN ; +__sbit __at (0x9D) SM2 ; +__sbit __at (0x9E) SM1 ; +__sbit __at (0x9F) SM0 ;*/ + + + +/* Interrupt numbers: address = (number * 8) + 3 */ +/*#undef IE0_VECTOR +#undef TF0_VECTOR +#undef IE1_VECTOR +#undef TF1_VECTOR +#undef SI0_VECTOR*/ + +/* CC2430 interrupt vectors */ +#define RFERR_VECTOR 0 +#define ADC_VECTOR 1 +#define URX0_VECTOR 2 +#define URX1_VECTOR 3 +#define ENC_VECTOR 4 +#define ST_VECTOR 5 +#define P2INT_VECTOR 6 +#define UTX0_VECTOR 7 +#define DMA_VECTOR 8 +#define T1_VECTOR 9 +#define T2_VECTOR 10 +#define T3_VECTOR 11 +#define T4_VECTOR 12 +#define P0INT_VECTOR 13 +#define UTX1_VECTOR 14 +#define P1INT_VECTOR 15 +#define RF_VECTOR 16 +#define WDT_VECTOR 17 + +/* RF control registers*/ +__xdata __at (0xDF02) unsigned char MDMCTRL0H; +__xdata __at (0xDF03) unsigned char MDMCTRL0L; +__xdata __at (0xDF04) unsigned char MDMCTRL1H; +__xdata __at (0xDF05) unsigned char MDMCTRL1L; +__xdata __at (0xDF06) unsigned char RSSIH; +__xdata __at (0xDF07) unsigned char RSSIL; +__xdata __at (0xDF08) unsigned char SYNCWORDH; +__xdata __at (0xDF09) unsigned char SYNCWORDL; +__xdata __at (0xDF0A) unsigned char TXCTRLH; +__xdata __at (0xDF0B) unsigned char TXCTRLL; +__xdata __at (0xDF0C) unsigned char RXCTRL0H; +__xdata __at (0xDF0D) unsigned char RXCTRL0L; +__xdata __at (0xDF0E) unsigned char RXCTRL1H; +__xdata __at (0xDF0F) unsigned char RXCTRL1L; +__xdata __at (0xDF10) unsigned char FSCTRLH; +__xdata __at (0xDF11) unsigned char FSCTRLL; +__xdata __at (0xDF12) unsigned char CSPX; +__xdata __at (0xDF13) unsigned char CSPY; +__xdata __at (0xDF14) unsigned char CSPZ; +__xdata __at (0xDF15) unsigned char CSPCTRL; +__xdata __at (0xDF16) unsigned char CSPT; +__xdata __at (0xDF17) unsigned char RFPWR; +#define ADI_RADIO_PD 0x10 +#define RREG_RADIO_PD 0x08 +#define RREG_DELAY_MASK 0x07 + +__xdata __at (0xDF20) unsigned char FSMTCH; +__xdata __at (0xDF21) unsigned char FSMTCL; +__xdata __at (0xDF22) unsigned char MANANDH; +__xdata __at (0xDF23) unsigned char MANANDL; +__xdata __at (0xDF24) unsigned char MANORH; +__xdata __at (0xDF25) unsigned char MANORL; +__xdata __at (0xDF26) unsigned char AGCCTRLH; +__xdata __at (0xDF27) unsigned char AGCCTRLL; + +__xdata __at (0xDF39) unsigned char FSMSTATE; +__xdata __at (0xDF3A) unsigned char ADCTSTH; +__xdata __at (0xDF3B) unsigned char ADCTSTL; +__xdata __at (0xDF3C) unsigned char DACTSTH; +__xdata __at (0xDF3D) unsigned char DACTSTL; + +__xdata __at (0xDF43) unsigned char IEEE_ADDR0; +__xdata __at (0xDF44) unsigned char IEEE_ADDR1; +__xdata __at (0xDF45) unsigned char IEEE_ADDR2; +__xdata __at (0xDF46) unsigned char IEEE_ADDR3; +__xdata __at (0xDF47) unsigned char IEEE_ADDR4; +__xdata __at (0xDF48) unsigned char IEEE_ADDR5; +__xdata __at (0xDF49) unsigned char IEEE_ADDR6; +__xdata __at (0xDF4A) unsigned char IEEE_ADDR7; +__xdata __at (0xDF4B) unsigned char PANIDH; +__xdata __at (0xDF4C) unsigned char PANIDL; +__xdata __at (0xDF4D) unsigned char SHORTADDRH; +__xdata __at (0xDF4E) unsigned char SHORTADDRL; +__xdata __at (0xDF4F) unsigned char IOCFG0; +__xdata __at (0xDF50) unsigned char IOCFG1; +__xdata __at (0xDF51) unsigned char IOCFG2; +__xdata __at (0xDF52) unsigned char IOCFG3; +__xdata __at (0xDF53) unsigned char RXFIFOCNT; +__xdata __at (0xDF54) unsigned char FSMTC1; +#define ABORTRX_ON_SRXON 0x20 +#define RX_INTERRUPTED 0x10 +#define AUTO_TX2RX_OFF 0x08 +#define RX2RX_TIME_OFF 0x04 +#define PENDING_OR 0x02 +#define ACCEPT_ACKPKT 0x01 + +__xdata __at (0xDF60) unsigned char CHVER; +__xdata __at (0xDF61) unsigned char CHIPID; +__xdata __at (0xDF62) unsigned char RFSTATUS; +#define TX_ACTIVE 0x10 +#define FIFO 0x08 +#define FIFOP 0x04 +#define SFD 0x02 +#define CCA 0x01 + +__xdata __at (0xDFC1) unsigned char U0BUF_SHADOW; + +__xdata __at (0xDFD9) unsigned char RFD_SHADOW; + +__xdata __at (0xDFF9) unsigned char U1BUF_SHADOW; + +__xdata __at (0xDFBA) unsigned int ADC_SHADOW; + +#endif /*REG_CC2430*/ diff --git a/cpu/cc2430/converter/converter b/cpu/cc2430/converter/converter deleted file mode 100755 index 9b47ea14a8e731ded251319838859d807a74274b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7156 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 Yv1lOZ63g8Af@St^fc4 diff --git a/cpu/cc2430/converter/converter.c b/cpu/cc2430/converter/converter.c index 7ce442bae..56d12ecc9 100644 --- a/cpu/cc2430/converter/converter.c +++ b/cpu/cc2430/converter/converter.c @@ -13,7 +13,7 @@ void usage(char *prg_name) { printf("\nUsage: %s -f ihex file\n", prg_name); printf("General options:\n"); - printf(" -V/--version Get converter version\n"); + printf(" -v/--version Get converter version\n"); } conf_opts_t conf_opts; @@ -22,11 +22,11 @@ static int option_index = 0; int do_exit = 0; -#define OPTIONS_STRING "Vhf:" +#define OPTIONS_STRING "vhf:" /* long option list */ static struct option long_options[] = { - {"version", 0, NULL, 'V'}, + {"version", 0, NULL, 'v'}, {"file", 1, NULL, 'f'}, {"help", 0, NULL, 'h'}, {0, 0, 0, 0} @@ -43,7 +43,7 @@ int parse_opts(int count, char* param[]) { switch(opt) { - case 'V': + case 'v': conf_opts.target_type = VERSION; break; @@ -73,6 +73,7 @@ int main(int argc, char *argv[]) conf_opts.target_type = 0; + printf("Sensinode hex file converter "CONVERTER_VERSION "\n"); if ( (argc < 1) || (error = parse_opts(argc, argv)) ) { usage(argv[0]); @@ -133,14 +134,14 @@ int main(int argc, char *argv[]) } else if (memcmp(&buffer[7], "01", 2) == 0) { /*end file*/ - printf("\nFile read complete.\n"); + printf("File read complete.\n"); break; } else if (memcmp(&buffer[7], "04", 2) == 0) { sscanf((char *)&(buffer[3]),"%4hx", &addr); sscanf((char *)&(buffer[9]),"%4lx", &ext_addr); - printf("\rExtended page address: 0x%8.8lX\r", ext_addr*0x8000 ); + /*printf("Extended page address: 0x%8.8lX\n", ext_addr*0x8000 );*/ if (ext_addr >= 0x0002) sdcc_file = 1; @@ -166,7 +167,7 @@ int main(int argc, char *argv[]) *ptr = 0; } } - strcat(conf_opts.ihex_file, "_linear.hex"); + strcat(conf_opts.ihex_file, ".hex"); printf("Output file: %s.\n", conf_opts.ihex_file); ihex = fopen(conf_opts.ihex_file, "wb"); ext_addr=0; @@ -180,7 +181,7 @@ int main(int argc, char *argv[]) addr = (i & 0x1F) * 2048; if ( ((i / 32) * 0x10000) != ext_addr) { /*write out ext addr*/ - printf("Ext: %4.4X\n", ((i / 32) * 0x10000)); + /*printf("Ext: %4.4X\n", ((i / 32) * 0x10000));*/ ext_addr = (i / 32) * 0x10000; fprintf(ihex, ":02000004%4.4X%2.2X\r\n", (int)(ext_addr>>16), (int)(0xFA-(ext_addr>>16))); @@ -188,7 +189,7 @@ int main(int argc, char *argv[]) if (page_table[i] != 0) { - printf("%4.4X", addr & 0xF800); + /*printf("%4.4X", addr & 0xF800);*/ for (j=0; j<2048; j++) { addr =(i & 0x1F) * 2048 + j; @@ -208,8 +209,10 @@ int main(int argc, char *argv[]) } } } + /* if ((i & 0x07) == 0x07) printf("\n"); else printf(" "); + */ } fprintf(ihex, ":00000001FF\r\n"); printf("Write complete.\n"); @@ -220,9 +223,5 @@ int main(int argc, char *argv[]) { usage(argv[0]); } - else - { - printf("\nSensinode hex file converter "CONVERTER_VERSION "\n"); - } return error; } diff --git a/cpu/cc2430/converter/converter.h b/cpu/cc2430/converter/converter.h index 8457b1809..cde769b72 100644 --- a/cpu/cc2430/converter/converter.h +++ b/cpu/cc2430/converter/converter.h @@ -1,7 +1,7 @@ #ifndef CONVERTER_H #define CONVERTER_H -#define CONVERTER_VERSION "v1.3" +#define CONVERTER_VERSION "v1.4" typedef struct { int target_type; diff --git a/cpu/cc2430/converter/converter.o b/cpu/cc2430/converter/converter.o deleted file mode 100644 index fa67f8fe1494b7817cf9d26eb6722913e808ac15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4740 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 diff --git a/cpu/cc2430/converter/ihex.o b/cpu/cc2430/converter/ihex.o deleted file mode 100644 index 451888a2bb519ba8a191cf6c26784dd457279e45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1604 zcma)6&ubGw6rOFOu5D_ftyr~S!40IfT}{lTwP2wgt0E%U)q=*bO*b^OO-iy$(}O=S z7F?Eq{vm=0hT=inrnD41)Lz>|E<&t9Ru9^P5Z~MEm~AimVCH+@n>X*xn|ZsB>5VHK z#}OtRagjPBGnmO*#3h=x?^I;H>c7M6Qy+w2E#p@^Q~gd0!b zMt3bW1|6yItM50Ji&h~TpIhm^_aGW6T6y9x>h)@#JT;Ao4?`Q2U!$f~92BbG7d)8h zj3bjFnZ^-aDj6-gU6YlZ1c`*a0-3b zTiEaxXoXw$&X0q^pyn4Dko5UOVrbMK7Q>^KMjUfvUFT>u}9AA*5`@x+>H&A5i+vW=Dzrt9E` zPs{{;P_DvYI&yX2`PdqMQtWKZZxj_;4u#51@EZ2m?VSRdXC-XY%8o_KJ7x4_1P z#bcbI)9xnUtIUqL_wR(J0pO| z&+g;SJID1)FiaR -#include "banked.h" -#include "contiki.h" -#include "sys/clock.h" - -#include "cc2430_sfr.h" -#include "dev/adc.h" -#include "dev/dma.h" - -#ifdef HAVE_DMA -xDMAHandle adc_dma=0xff; -unsigned int *adc_dma_dest; -#endif - -/*---------------------------------------------------------------------------*/ -void adc_init(void) __banked -{ - unsigned char jj; - while (!SLEEP&(HFRC_STB)) {} - /* power both 32MHz crystal and 15MHz RC */ - SLEEP &= ~(OSC_PD); - /* printf("SLEEP 1 %x\n",SLEEP); */ - /* then wait for it to stabilize */ - while (!SLEEP&(XOSC_STB)) {} - /* printf("SLEEP 2 %x\n",SLEEP); */ - /* then wait 64uS more */ - clock_delay(150); - /* switch to 32MHz clock */ - /* printf("switch to 32MHz %x\n",CLKCON); */ - CLKCON &= ~(OSC); - /* printf("switched to 32MHz %x\n",CLKCON); */ - /* power down 15MHz RC clock */ - SLEEP |= OSC_PD; - /* printf("pwr down hfrc\n",SLEEP); */ -#ifdef HAVE_DMA - /* preconfigure adc_dma before calling adc_init if a different dma type is desired. */ - if (adc_dma==0xff) { - dma_init(); - /* config DMA channel to copy results to single location */ - adc_dma=dma_config2(ADC_DMA_CONFIG_CHANNEL, &ADC_SHADOW, DMA_NOINC, adc_dma_dest, DMA_NOINC, 1, 1, DMA_VLEN_LEN, DMA_RPT, DMA_T_ADC_CHALL, 0); - } -#endif -} -/* single sample trigger */ -void adc_single_shot(void) __banked -{ - ADCCON1 |= 0x73; -} -/* convert adc results */ -int16_t adc_convert_result(int16_t data) __banked { - data = (0xfffc&data)>>2; - return data; -} -/* read/convert last conversion result */ -int16_t adc_get_last_conv() __banked { - int16_t result; - result = (ADCH<<8)|(ADCL); - result = (0xfffc&result)>>2; - return result; -} diff --git a/cpu/cc2430/dev/adc.h b/cpu/cc2430/dev/adc.h deleted file mode 100644 index 5a8ea431d..000000000 --- a/cpu/cc2430/dev/adc.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * \file - * Header file for ADC. - * \author - * Anthony "Asterisk" Ambuehl - * - */ - -#ifndef __ADC_H -#define __ADC_H -#define ADC_DMA_CONFIG_CHANNEL 1 -#define ADC_CHANNELS 8 -#include "cc2430_sfr.h" -#include "dma.h" -#include "banked.h" - -typedef struct adc_result -{ - uint16_t adc:14; - uint16_t unused:2; -} adc_result_t; - -typedef enum adc_stsel_t -{ - EXT = 0, /* externally triggered by P2_0 */ - CONTINUOUS = 1, /* continuous full speed conversion */ - TIMER1 = 2, /* Timer 1 channel 0 compare event */ - ST = 3 /* ADCCON1.ST = 1 */ -} adc_stsel_t; - -extern void adc_init(void) __banked; -extern void adc_single_shot(void) __banked; -extern int16_t adc_convert_result(int16_t ptr) __banked; -extern int16_t adc_get_last_conv() __banked; -extern void adc_dma_callback(void) __banked; -#ifdef HAVE_DMA -extern xDMAHandle adc_dma; -extern unsigned int *adc_dma_dest; -#endif - -#endif /*__ADC_H*/ diff --git a/cpu/cc2430/dev/banked.h b/cpu/cc2430/dev/banked.h deleted file mode 100644 index d5cb55ca2..000000000 --- a/cpu/cc2430/dev/banked.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * \file - * - * SDCC bank switching macro define file - * - * \author - * - * Anthony "Asterisk" Ambuehl - * - * SDCC (small device cross compiler) has built-in support for bank switching using predefined macros __banked. - * To avoid compilation issues on other compilers include this file which will replace __banked with the empty string on unsupported compilers. - * - * In addition, the file can add the codeseg pragma to place code into specific banks, if specific macro is set. - * However the same result can be achieved by using the segment.rules file. - * - */ - -#ifndef __BANKED_H -#ifdef SDCC -#ifndef HAVE_SDCC_BANKING -#define __banked -#else -#ifdef BANKED_IN_HOME -#pragma codeseg HOME -#endif -#ifdef BANKED_IN_BANK1 -#pragma codeseg BANK1 -#endif -#ifdef BANKED_IN_BANK2 -#pragma codeseg BANK2 -#endif -#ifdef BANKED_IN_BANK3 -#pragma codeseg BANK3 -#endif -#endif -#else -#define __banked -#endif - -#endif /*__BANKED_H*/ diff --git a/cpu/cc2430/dev/bus.c b/cpu/cc2430/dev/bus.c index 48470f7f7..0274ce3d6 100644 --- a/cpu/cc2430/dev/bus.c +++ b/cpu/cc2430/dev/bus.c @@ -38,14 +38,14 @@ * Adam Dunkels */ -#include "banked.h" #include "cc2430_sfr.h" #include "dev/bus.h" #include "sys/clock.h" +#include "contiki-conf.h" /*---------------------------------------------------------------------------*/ void -bus_init (void) __banked +bus_init (void) { CLKCON = (0x00 | OSC32K); /* 32k internal */ while(CLKCON != (0x00 | OSC32K)); @@ -65,8 +65,9 @@ bus_init (void) __banked * \param buffer buffer to store data * \param size number of bytes to read */ +#if !SHORTCUTS_CONF_FLASH_READ void -flash_read (uint8_t *buf, uint32_t address, uint8_t size) __banked +flash_read(uint8_t *buf, uint32_t address, uint8_t size) { buf; /*dptr0*/ address; /*stack-6*/ @@ -130,4 +131,5 @@ lp1: ENABLE_INTERRUPTS(); DPL1 = *buf++; } +#endif /*---------------------------------------------------------------------------*/ diff --git a/cpu/cc2430/dev/bus.h b/cpu/cc2430/dev/bus.h index 746fc637d..6c20dac54 100644 --- a/cpu/cc2430/dev/bus.h +++ b/cpu/cc2430/dev/bus.h @@ -44,11 +44,14 @@ #include "cc2430_sfr.h" #include "8051def.h" +#include "contiki-conf.h" #define inline -void bus_init(void) __banked; -void flash_read(uint8_t *buf, uint32_t address, uint8_t size) __banked; -void cc2430_clock_ISR( void ) __interrupt (ST_VECTOR); +void bus_init(void); +#if !SHORTCUTS_CONF_FLASH_READ +void flash_read(uint8_t *buf, uint32_t address, uint8_t size); +#endif +void clock_ISR( void ) __interrupt (ST_VECTOR); #endif /* __BUS_H__ */ diff --git a/cpu/cc2430/dev/cc2430_rf.c b/cpu/cc2430/dev/cc2430_rf.c index ed1d92bba..934bb7a23 100644 --- a/cpu/cc2430/dev/cc2430_rf.c +++ b/cpu/cc2430/dev/cc2430_rf.c @@ -2,7 +2,10 @@ * \file * CC2430 RF driver * \author - * Zach Shelby + * Zach Shelby (Original) + * George Oikonomou - + * (port to the netstack API, hexdump output, RX FIFO overflow fixes + * code cleanup, ...) * * bankable code for cc2430 rf driver. this code can be placed in any bank. * @@ -15,17 +18,31 @@ #include "dev/cc2430_rf.h" #include "cc2430_sfr.h" #include "sys/clock.h" +#include "sys/rtimer.h" #include "net/packetbuf.h" #include "net/rime/rimestats.h" +#include "net/netstack.h" -extern void (* receiver_callback)(const struct radio_driver *); -#ifndef RF_DEFAULT_POWER -#define RF_DEFAULT_POWER 100 +#define CC2430_RF_TX_POWER_RECOMMENDED 0x5F +#ifdef CC2430_RF_CONF_TX_POWER +#define CC2430_RF_TX_POWER CC2430_RF_CONF_TX_POWER +#else +#define CC2430_RF_TX_POWER CC2430_RF_TX_POWER_RECOMMENDED #endif -#ifndef RF_DEFAULT_CHANNEL -#define RF_DEFAULT_CHANNEL 18 +#ifdef CC2430_RF_CONF_CHANNEL +#define CC2430_RF_CHANNEL CC2430_RF_CONF_CHANNEL +#else +#define CC2430_RF_CHANNEL 18 +#endif /* CC2430_RF_CONF_CHANNEL */ +#define CC2430_CHANNEL_MIN 11 +#define CC2430_CHANNEL_MAX 26 + +#ifdef CC2430_RF_CONF_AUTOACK +#define CC2430_RF_AUTOACK CC2430_RF_CONF_AUTOACK +#else +#define CC2430_RF_AUTOACK 1 #endif #ifndef CC2430_CONF_CHECKSUM @@ -42,15 +59,15 @@ extern void (* receiver_callback)(const struct radio_driver *); /* moved leds code to BANK1 to make space for cc2430_rf_process in HOME */ /* can't call code in BANK1 from alternate banks unless it is marked with __banked */ #include "dev/leds.h" -#define RF_RX_LED_ON() leds_on(LEDS_RED); -#define RF_RX_LED_OFF() leds_off(LEDS_RED); -#define RF_TX_LED_ON() leds_on(LEDS_GREEN); -#define RF_TX_LED_OFF() leds_off(LEDS_GREEN); +#define RF_RX_LED_ON() leds_on(LEDS_RED); +#define RF_RX_LED_OFF() leds_off(LEDS_RED); +#define RF_TX_LED_ON() leds_on(LEDS_GREEN); +#define RF_TX_LED_OFF() leds_off(LEDS_GREEN); #else -#define RF_RX_LED_ON() -#define RF_RX_LED_OFF() -#define RF_TX_LED_ON() -#define RF_TX_LED_OFF() +#define RF_RX_LED_ON() +#define RF_RX_LED_OFF() +#define RF_TX_LED_ON() +#define RF_TX_LED_OFF() #endif #define DEBUG 0 #if DEBUG @@ -62,220 +79,48 @@ extern void (* receiver_callback)(const struct radio_driver *); #define RX_ACTIVE 0x80 #define TX_ACK 0x40 #define TX_ON_AIR 0x20 +#define WAS_OFF 0x10 #define RX_NO_DMA +/* Bits of the last byte in the RX FIFO */ +#define CRC_BIT_MASK 0x80 +#define LQI_BIT_MASK 0x7F + +/* 192 ms, radio off -> on interval */ +#define ONOFF_TIME ((RTIMER_ARCH_SECOND / 3125) + 4) + +#if CC2430_RF_CONF_HEXDUMP +#include "uart1.h" +static const uint8_t magic[] = { 0x53, 0x6E, 0x69, 0x66 }; /* Snif */ +#endif #ifdef HAVE_RF_ERROR uint8_t rf_error = 0; #endif -uint8_t rf_initialized = 0; -uint8_t rf_tx_power; -uint8_t rf_flags; -uint8_t rf_channel = 0; -rf_address_mode_t rf_addr_mode; -uint16_t rf_manfid; -uint8_t rf_softack; -uint16_t rf_panid; - /*---------------------------------------------------------------------------*/ -PROCESS_NAME(cc2430_rf_process); +#if !SHORTCUTS_CONF_NETSTACK +PROCESS(cc2430_rf_process, "CC2430 RF driver"); +#endif /*---------------------------------------------------------------------------*/ +static uint8_t rf_initialized = 0; +static uint8_t __data rf_flags; -const struct radio_driver cc2430_rf_driver = - { - cc2430_rf_send, - cc2430_rf_read, - cc2430_rf_set_receiver, - cc2430_rf_on, - cc2430_rf_off, - }; - +static int on(void); /* prepare() needs our prototype */ +static int off(void); /* transmit() needs our prototype */ +static int channel_clear(void); /* transmit() needs our prototype */ /*---------------------------------------------------------------------------*/ -void -cc2430_rf_init(void) __banked -{ - if(rf_initialized) { - return; - } - - PRINTF("cc2430_rf_init called\n"); - - RFPWR &= ~RREG_RADIO_PD; /*make sure it's powered*/ - while((RFPWR & ADI_RADIO_PD) == 1); - while((RFIF & IRQ_RREG_ON) == 0); /*wait for power up*/ - SLEEP &= ~OSC_PD; /*Osc on*/ - while((SLEEP & XOSC_STB) == 0); /*wait for power up*/ - - rf_flags = 0; - rf_softack = 0; - - FSMTC1 = 1; /*don't abort reception, if enable called, accept ack, auto rx after tx*/ - - MDMCTRL0H = 0x02; /* Generic client, standard hysteresis, decoder on 0x0a */ - MDMCTRL0L = 0xE2; /* automatic ACK and CRC, standard CCA and preamble 0xf2 */ - - MDMCTRL1H = 0x30; /* Defaults */ - MDMCTRL1L = 0x0; - - RXCTRL0H = 0x32; /* RX tuning optimized */ - RXCTRL0L = 0xf5; - - /* get ID for MAC */ - rf_manfid = CHVER; - rf_manfid <<= 8; - rf_manfid += CHIPID; - cc2430_rf_channel_set(RF_DEFAULT_CHANNEL); - cc2430_rf_command(ISFLUSHTX); - cc2430_rf_command(ISFLUSHRX); - - cc2430_rf_set_addr(0xffff, 0x0000, NULL); - cc2430_rf_address_decoder_mode(RF_DECODER_NONE); - - RFIM = IRQ_FIFOP; - RFIF &= ~(IRQ_FIFOP); - - S1CON &= ~(RFIF_0 | RFIF_1); - IEN2 |= RFIE; - - RF_TX_LED_OFF(); - RF_RX_LED_OFF(); - rf_initialized = 1; - process_start(&cc2430_rf_process, NULL); -} -/*---------------------------------------------------------------------------*/ -int -cc2430_rf_send_b(void *payload, unsigned short payload_len) __banked -{ - uint8_t i, counter; - - if(rf_flags & TX_ACK) { - return -1; - } - if((rf_flags & RX_ACTIVE) == 0) { - cc2430_rf_rx_enable(); - } - /* Check packet attributes */ - /*printf("packetbuf_attr: txpower = %d\n", packetbuf_attr(PACKETBUF_ATTR_RADIO_TXPOWER));*/ - /* Should set TX power according to this if > 0 */ - - PRINTF("cc2430_rf: sending %ud byte payload\n", payload_len); - - RIMESTATS_ADD(lltx); - - cc2430_rf_command(ISFLUSHTX); - PRINTF("cc2430_rf: sent = "); - /* Send the phy length byte first */ - RFD = payload_len+CHECKSUM_LEN; /* Payload plus FCS */ - PRINTF("(%d)", payload_len+CHECKSUM_LEN); - for(i = 0 ; i < payload_len; i++) { - RFD = ((unsigned char*)(payload))[i]; - PRINTF("%02X", ((unsigned char*)(payload))[i]); - } - PRINTF("\n"); - - /* Leave space for the FCS */ - RFD = 0; - RFD = 0; - - if(cc2430_rf_cca_check(0,0) == -1) { - return -1; - } - - /* Start the transmission */ - - RFIF &= ~IRQ_TXDONE; - cc2430_rf_command(ISTXON); - counter = 0; - while(!(RFSTATUS & TX_ACTIVE) && (counter++ < 3)) { - clock_delay(10); - } - - if(!(RFSTATUS & TX_ACTIVE)) { - PRINTF("cc2430_rf: TX never active.\n"); - cc2430_rf_command(ISFLUSHTX); - return -1; - } else { - RF_RX_LED_OFF(); - RF_TX_LED_ON(); - // rf_flags |= TX_ON_AIR; - } - return 1; -} -/*---------------------------------------------------------------------------*/ -int -cc2430_rf_read_banked(void *buf, unsigned short bufsize) __banked -{ - uint8_t i, len; -#if CC2420_CONF_CHECKSUM - uint16_t checksum; -#endif /* CC2420_CONF_CHECKSUM */ - - /* RX interrupt polled the cc2430_rf_process, now read the RX FIFO */ - - /* Check the length */ - len = RFD; - - /* Check for validity */ - if(len > CC2430_MAX_PACKET_LEN) { - /* Oops, we must be out of sync. */ - PRINTF("error: bad sync\n"); - cc2430_rf_command(ISFLUSHRX); - RIMESTATS_ADD(badsynch); - return 0; - } - - if(len <= CC2430_MIN_PACKET_LEN) { - PRINTF("error: too short\n"); - cc2430_rf_command(ISFLUSHRX); - RIMESTATS_ADD(tooshort); - return 0; - } - - if(len - CHECKSUM_LEN > bufsize) { - PRINTF("error: too long\n"); - cc2430_rf_command(ISFLUSHRX); - RIMESTATS_ADD(toolong); - return 0; - } - - /* Read the buffer */ - PRINTF("cc2430_rf: read = "); - PRINTF("(%d)", len); - for(i = 0; i < (len - CHECKSUM_LEN); i++) { - ((unsigned char*)(buf))[i] = RFD; - PRINTF("%02X", ((unsigned char*)(buf))[i]); - } - PRINTF("\n"); - -#if CC2430_CONF_CHECKSUM - /* Deal with the checksum */ - checksum = RFD * 256; - checksum += RFD; -#endif /* CC2430_CONF_CHECKSUM */ - packetbuf_set_attr(PACKETBUF_ATTR_RSSI, ((int8_t) RFD) - 45); - packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, RFD); - - RFIF &= ~IRQ_FIFOP; - RFSTATUS &= ~FIFO; - cc2430_rf_command(ISFLUSHRX); - cc2430_rf_command(ISFLUSHRX); - RF_RX_LED_OFF(); - - RIMESTATS_ADD(llrx); - - return (len - CHECKSUM_LEN); -} /** * Execute a single CSP command. * * \param command command to execute * */ -void cc2430_rf_command(uint8_t command) __banked +void +cc2430_rf_command(uint8_t command) { - if(command >= 0xE0) { /*immediate strobe*/ + if(command >= 0xE0) { /*immediate strobe*/ uint8_t fifo_count; - switch(command) { /*hardware bug workaround*/ + switch (command) { /*hardware bug workaround*/ case ISRFOFF: case ISRXON: case ISTXON: @@ -283,8 +128,8 @@ void cc2430_rf_command(uint8_t command) __banked RFST = command; clock_delay(2); if(fifo_count != RXFIFOCNT) { - RFST = ISFLUSHRX; - RFST = ISFLUSHRX; + RFST = ISFLUSHRX; + RFST = ISFLUSHRX; } break; @@ -292,17 +137,31 @@ void cc2430_rf_command(uint8_t command) __banked RFST = command; } } else if(command == SSTART) { - RFIF &= ~IRQ_CSP_STOP; /*clear IRQ flag*/ - RFST = SSTOP; /*make sure there is a stop in the end*/ - RFST = ISSTART; /*start execution*/ + RFIF &= ~IRQ_CSP_STOP; /*clear IRQ flag*/ + RFST = SSTOP; /*make sure there is a stop in the end*/ + RFST = ISSTART; /*start execution*/ while((RFIF & IRQ_CSP_STOP) == 0); } else { - RFST = command; /*write command*/ + RFST = command; /*write command*/ } } /*---------------------------------------------------------------------------*/ -/*---------------------------------------------------------------------------*/ +static void +flush_rx() +{ + cc2430_rf_command(ISFLUSHRX); + cc2430_rf_command(ISFLUSHRX); +#if !SHORTCUTS_CONF_NETSTACK + IEN2 |= RFIE; +#endif +#if CC2430_RFERR_INTERRUPT + IEN0 |= RFERRIE; +#endif + + RFIF &= ~IRQ_FIFOP; +} +/*---------------------------------------------------------------------------*/ /** * Select RF channel. * @@ -323,11 +182,11 @@ cc2430_rf_channel_set(uint8_t channel) return -1; } - cc2430_rf_command(ISSTOP); /*make sure CSP is not running*/ + cc2430_rf_command(ISSTOP); /*make sure CSP is not running*/ cc2430_rf_command(ISRFOFF); /* Channel values: 11-26 */ freq = (uint16_t) channel - 11; - freq *= 5; /*channel spacing*/ + freq *= 5; /*channel spacing*/ freq += 357; /*correct channel range*/ freq |= 0x4000; /*LOCK_THR = 1*/ FSCTRLH = (freq >> 8); @@ -335,189 +194,77 @@ cc2430_rf_channel_set(uint8_t channel) cc2430_rf_command(ISRXON); - rf_channel = channel; - return (int8_t) channel; } /*---------------------------------------------------------------------------*/ -/*PA_LEVEL TXCTRL register Output Power [dBm] Current Consumption [mA] - 31 0xA0FF 0 17.4 - 27 0xA0FB -1 16.5 - 23 0xA0F7 -3 15.2 - 19 0xA0F3 -5 13.9 - 15 0xA0EF -7 12.5 - 11 0xA0EB -10 11.2 - 7 0xA0E7 -15 9.9 - 3 0xA0E3 -25 8.5*/ - /** * Select RF transmit power. * - * \param new_power new power level (in per cent) + * \param new_power new power level * - * \return new level or negative (value out of range) + * \return new level */ - -int8_t +uint8_t cc2430_rf_power_set(uint8_t new_power) { - uint16_t power; - - if(new_power > 100) { - return -1; - } - - power = 31 * new_power; - power /= 100; - power += 0xA160; - /* Set transmitter power */ - TXCTRLH = (power >> 8); - TXCTRLL = (uint8_t)power; + TXCTRLL = new_power; - rf_tx_power = (int8_t) new_power; - return rf_tx_power; -} -/*---------------------------------------------------------------------------*/ -/** - * Enable RF receiver. - * - * - * \return pdTRUE - * \return pdFALSE bus not free - */ -int8_t -cc2430_rf_rx_enable(void) __banked -{ - PRINTF("cc2430_rf_rx_enable called\n"); - if(!(rf_flags & RX_ACTIVE)) { - IOCFG0 = 0x7f; // Set the FIFOP threshold 127 - RSSIH = 0xd2; /* -84dbm = 0xd2 default, 0xe0 -70 dbm */ - rf_flags |= RX_ACTIVE; - - RFPWR &= ~RREG_RADIO_PD; /*make sure it's powered*/ - while((RFIF & IRQ_RREG_ON) == 0); /*wait for power up*/ - SLEEP &= ~OSC_PD; /*Osc on*/ - while((SLEEP & XOSC_STB) == 0); /*wait for power up*/ - - cc2430_rf_command(ISRXON); - cc2430_rf_command(ISFLUSHRX); - } - PRINTF("cc2430_rf_rx_enable done\n"); - return 1; -} -/*---------------------------------------------------------------------------*/ -/** - * Disable RF receiver. - * - * - * \return pdTRUE - * \return pdFALSE bus not free - */ -int8_t cc2430_rf_rx_disable(void) __banked -{ - cc2430_rf_command(ISSTOP); /*make sure CSP is not running*/ - cc2430_rf_command(ISRFOFF); - - RFPWR |= RREG_RADIO_PD; /*RF powerdown*/ - - rf_flags = 0; - return 1; + return TXCTRLL; } /*---------------------------------------------------------------------------*/ +#if 0 /* unused */ /** * Enable RF transmitter. * * * \return pdTRUE - * \return pdFALSE bus not free + * \return pdFALSE bus not free */ -int8_t +int cc2430_rf_tx_enable(void) { - DMAARM = 0x80 + (1 << 0); /*ABORT + channel bit*/ + DMAARM = 0x80 + (1 << 0); /*ABORT + channel bit*/ return 1; } - +#endif +/*---------------------------------------------------------------------------*/ /** - * Set MAC addresses - * - * \param pan The PAN address to set - * \param adde The short address to set - * \param ieee_addr The 64-bit IEEE address to set - */ + * Set MAC addresses + * + * \param pan The PAN address to set + * \param adde The short address to set + * \param ieee_addr The 64-bit IEEE address to set + */ void cc2430_rf_set_addr(unsigned pan, unsigned addr, const uint8_t *ieee_addr) { - uint8_t f; - __xdata unsigned char *ptr; + uint8_t f; + __xdata unsigned char *ptr; - rf_panid = pan; - PANIDH = pan >> 8; - PANIDL = pan & 0xff; + PANIDH = pan >> 8; + PANIDL = pan & 0xff; - SHORTADDRH = addr >> 8; - SHORTADDRL = addr & 0xff; + SHORTADDRH = addr >> 8; + SHORTADDRL = addr & 0xff; - if(ieee_addr != NULL) { - ptr = &IEEE_ADDR0; - /* LSB first, MSB last for 802.15.4 addresses in CC2420 */ - for (f = 0; f < 8; f++) { - *ptr++ = ieee_addr[f]; - } - } -} - -/*---------------------------------------------------------------------------*/ -/** - * Set address decoder on/off. - * - * \param param 1=on 0=off. - * \return pdTRUE operation successful - */ -int8_t -cc2430_rf_address_decoder_mode(rf_address_mode_t mode) -{ - int8_t retval = -1; - - rf_softack = 0; - /* set oscillator on*/ - switch(mode) { - case RF_SOFTACK_MONITOR: - rf_softack = 1; - case RF_MONITOR: - MDMCTRL0H |= 0x10; /*Address-decode off , coordinator*/ - MDMCTRL0L &= ~0x10; /*no automatic ACK */ - break; - - case RF_DECODER_COORDINATOR: - MDMCTRL0H |= 0x18; /*Address-decode on , coordinator*/ - MDMCTRL0L |= 0x10; /*automatic ACK */ - break; - - case RF_DECODER_ON: - MDMCTRL0H |= 0x08; /*Address-decode on */ - MDMCTRL0L &= ~0x10; /* no automatic ACK */ - break; - - default: - MDMCTRL0H &= ~0x18; /* Generic client */ - MDMCTRL0L &= ~0x10; /* no automatic ACK */ - break; + if(ieee_addr != NULL) { + ptr = &IEEE_ADDR7; + /* LSB first, MSB last for 802.15.4 addresses in CC2420 */ + for (f = 0; f < 8; f++) { + *ptr-- = ieee_addr[f]; + } } - rf_addr_mode = mode; - - retval = 1; - return retval; } +#if 0 /* currently unused */ /*---------------------------------------------------------------------------*/ /** * Channel energy detect. * * Coordinator use this function detect best channel for PAN-network. * \return RSSI-energy level dBm. - * \return 0 operation failed. + * \return 0 operation failed. */ int8_t @@ -530,54 +277,7 @@ cc2430_rf_analyze_rssi(void) retval -= 45; return retval; } -/*---------------------------------------------------------------------------*/ -/** - * Clear channel assesment check. - * - * \return pdTRUE CCA clear - * \return pdFALSE CCA reserved - */ -int8_t -cc2430_rf_cca_check(uint8_t backoff_count, uint8_t slotted) -{ - uint8_t counter, cca = 1; - int8_t retval = 1; - backoff_count; - cc2430_rf_command(ISRXON); - - clock_delay(64); - switch(slotted) { - case 1: - - if(RFSTATUS & CCA) { - counter = 0; - cca = 1; - while(cca != 0) { - if(counter > 1) { - cca = 0; - } - clock_delay(256); - if(!(RFSTATUS & CCA)) { - cca = 0; - retval = -1; - } - counter++; - } - } else { - retval = -1; - } - break; - - case 0: - if(!(RFSTATUS & CCA)) { - retval = -1; - } else { - - } - break; - } - return retval; -} +#endif /* currently unused */ /*---------------------------------------------------------------------------*/ /** * Send ACK. @@ -585,7 +285,7 @@ cc2430_rf_cca_check(uint8_t backoff_count, uint8_t slotted) *\param pending set up pending flag if pending > 0. */ void -cc2430_rf_send_ack(uint8_t pending) __banked +cc2430_rf_send_ack(uint8_t pending) { if(pending) { cc2430_rf_command(ISACKPEND); @@ -594,3 +294,418 @@ cc2430_rf_send_ack(uint8_t pending) __banked } } /*---------------------------------------------------------------------------*/ +/* Netstack API radio driver functions */ +/*---------------------------------------------------------------------------*/ +static int +init(void) +{ + if(rf_initialized) { + return 0; + } + + PRINTF("cc2430_rf_init called\n"); + + RFPWR &= ~RREG_RADIO_PD; /*make sure it's powered*/ + while((RFPWR & ADI_RADIO_PD) == 1); + while((RFIF & IRQ_RREG_ON) == 0); /*wait for power up*/ + SLEEP &= ~OSC_PD; /*Osc on*/ + while((SLEEP & XOSC_STB) == 0); /*wait for power up*/ + + rf_flags = 0; + + FSMTC1 = 1; /*don't abort reception, if enable called, accept ack, auto rx after tx*/ + + MDMCTRL0H = 0x0A; /* Generic client, standard hysteresis, decoder on 0x0a */ + MDMCTRL0L = 0xE2; /* automatic CRC, standard CCA and preamble 0xE2 */ +#if CC2430_RF_AUTOACK + MDMCTRL0L |= 0x10; +#endif + + MDMCTRL1H = 0x30; /* Defaults */ + MDMCTRL1L = 0x0; + + RXCTRL0H = 0x32; /* RX tuning optimized */ + RXCTRL0L = 0xf5; + + cc2430_rf_channel_set(CC2430_RF_CHANNEL); + cc2430_rf_command(ISFLUSHTX); + cc2430_rf_command(ISFLUSHRX); + + /* Temporary values, main() will sort this out later on */ + cc2430_rf_set_addr(0xffff, 0x0000, NULL); + + RFIM = IRQ_FIFOP; + RFIF &= ~(IRQ_FIFOP); + + S1CON &= ~(RFIF_0 | RFIF_1); +#if !SHORTCUTS_CONF_NETSTACK + IEN2 |= RFIE; +#endif + + /* If contiki-conf.h turns on the RFERR interrupt, enable it here */ +#if CC2430_RFERR_INTERRUPT + IEN0 |= RFERRIE; +#endif + + RF_TX_LED_OFF(); + RF_RX_LED_OFF(); + rf_initialized = 1; + +#if !SHORTCUTS_CONF_NETSTACK + process_start(&cc2430_rf_process, NULL); +#endif + + cc2430_rf_power_set(CC2430_RF_TX_POWER); + + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +prepare(const void *payload, unsigned short payload_len) +{ + uint8_t i; + /* + * When we transmit in very quick bursts, make sure previous transmission + * is not still in progress before re-writing in the TX FIFO + */ + while(RFSTATUS & TX_ACTIVE); + + if(rf_flags & TX_ACK) { + return -1; + } + + if((rf_flags & RX_ACTIVE) == 0) { + on(); + } + + PRINTF("cc2430_rf: sending %u byte payload\n", payload_len); + + cc2430_rf_command(ISFLUSHTX); + PRINTF("cc2430_rf: data = "); + /* Send the phy length byte first */ + RFD = payload_len + CHECKSUM_LEN; /* Payload plus FCS */ + PRINTF("(%d)", payload_len+CHECKSUM_LEN); + for(i = 0; i < payload_len; i++) { + RFD = ((unsigned char*) (payload))[i]; + PRINTF("%02X", ((unsigned char*)(payload))[i]); + } + PRINTF("\n"); + + /* Leave space for the FCS */ + RFD = 0; + RFD = 0; + + return 0; +} +/*---------------------------------------------------------------------------*/ +static int +transmit(unsigned short transmit_len) +{ + uint8_t counter; + int ret = RADIO_TX_ERR; + + if(!(rf_flags & RX_ACTIVE)) { + on(); + rf_flags |= WAS_OFF; + } + + if(channel_clear() == CC2430_CCA_BUSY) { + RIMESTATS_ADD(contentiondrop); + return RADIO_TX_COLLISION; + } + + /* + * prepare() double checked that TX_ACTIVE is low. If SFD is high we are + * receiving. Abort transmission and bail out with RADIO_TX_COLLISION + */ + if(RFSTATUS & SFD) { + RIMESTATS_ADD(contentiondrop); + return RADIO_TX_COLLISION; + } + + /* Start the transmission */ + ENERGEST_OFF(ENERGEST_TYPE_LISTEN); + ENERGEST_ON(ENERGEST_TYPE_TRANSMIT); + + cc2430_rf_command(ISTXON); + counter = 0; + while(!(RFSTATUS & TX_ACTIVE) && (counter++ < 3)) { + clock_delay(10); + } + + if(!(RFSTATUS & TX_ACTIVE)) { + PRINTF("cc2430_rf: TX never active.\n"); + cc2430_rf_command(ISFLUSHTX); + ret = RADIO_TX_ERR; + } else { + /* Wait for the transmission to finish */ + while(RFSTATUS & TX_ACTIVE); + RF_RX_LED_OFF(); + RF_TX_LED_ON(); + ret = RADIO_TX_OK; + // rf_flags |= TX_ON_AIR; + } + ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT); + ENERGEST_ON(ENERGEST_TYPE_LISTEN); + + if(rf_flags & WAS_OFF){ + off(); + } + + RIMESTATS_ADD(lltx); + /* OK, sent. We are now ready to send more */ + return ret; +} +/*---------------------------------------------------------------------------*/ +static int +send(void *payload, unsigned short payload_len) +{ + prepare(payload, payload_len); + return transmit(payload_len); +} +/*---------------------------------------------------------------------------*/ +static int +read(void *buf, unsigned short bufsize) +{ + uint8_t i; + uint8_t len; + uint8_t crc_corr; + int8_t rssi; +#if CC2420_CONF_CHECKSUM + uint16_t checksum; +#endif /* CC2420_CONF_CHECKSUM */ + + /* Don't interrupt us while emptying the FIFO */ +#if !SHORTCUTS_CONF_NETSTACK + IEN2 &= ~RFIE; +#endif +#if CC2430_RFERR_INTERRUPT + IEN0 &= ~RFERRIE; +#endif + + /* RX interrupt polled the cc2430_rf_process, now read the RX FIFO */ + /* Check the length */ + len = RFD; + + /* Check for validity */ + if(len > CC2430_MAX_PACKET_LEN) { + /* Oops, we must be out of sync. */ + PRINTF("error: bad sync\n"); + + RIMESTATS_ADD(badsynch); + flush_rx(); + return 0; + } + + if(len <= CC2430_MIN_PACKET_LEN) { + PRINTF("error: too short\n"); + + RIMESTATS_ADD(tooshort); + flush_rx(); + return 0; + } + + if(len - CHECKSUM_LEN > bufsize) { + PRINTF("error: too long\n"); + + RIMESTATS_ADD(toolong); + flush_rx(); + return 0; + } + +#if CC2430_RF_CONF_HEXDUMP + /* If we reach here, chances are the FIFO is holding a valid frame */ + uart1_writeb(magic[0]); + uart1_writeb(magic[1]); + uart1_writeb(magic[2]); + uart1_writeb(magic[3]); + uart1_writeb(len); +#endif + + PRINTF("cc2430_rf: read = "); + PRINTF("(%d)", len); + len -= CHECKSUM_LEN; + for(i = 0; i < len; ++i) { + ((unsigned char*)(buf))[i] = RFD; +#if CC2430_RF_CONF_HEXDUMP + uart1_writeb(((unsigned char*)(buf))[i]); +#endif + PRINTF("%02X", ((unsigned char*)(buf))[i]); + } + PRINTF("\n"); + +#if CC2430_CONF_CHECKSUM + /* Deal with the checksum */ + checksum = RFD * 256; + checksum += RFD; +#endif /* CC2430_CONF_CHECKSUM */ + + /* Read the RSSI and CRC/Corr bytes */ + rssi = ((int8_t) RFD) - 45; + crc_corr = RFD; + +#if CC2430_RF_CONF_HEXDUMP + uart1_writeb(rssi); + uart1_writeb(crc_corr); +#endif + + /* MS bit CRC OK/Not OK, 7 LS Bits, Correlation value */ + if(crc_corr & CRC_BIT_MASK) { + packetbuf_set_attr(PACKETBUF_ATTR_RSSI, rssi); + packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, crc_corr & LQI_BIT_MASK); + RIMESTATS_ADD(llrx); + } else { + RIMESTATS_ADD(badcrc); + flush_rx(); + return 0; +} + + /* If FIFOP==1 and FIFO==0 then we had a FIFO overflow at some point. */ + if((RFSTATUS & (FIFO | FIFOP)) == FIFOP) { + /* + * If we reach here means that there might be more intact packets in the + * FIFO despite the overflow. This can happen with bursts of small packets. + * + * Only flush if the FIFO is actually empty. If not, then next pass we will + * pick up one more packet or flush due to an error. + */ + if(!RXFIFOCNT) { + flush_rx(); + } + } + + RF_RX_LED_OFF(); + +#if !SHORTCUTS_CONF_NETSTACK + IEN2 |= RFIE; +#endif +#if CC2430_RFERR_INTERRUPT + IEN0 |= RFERRIE; +#endif + + RFIF &= ~IRQ_FIFOP; + + return (len); +} +/*---------------------------------------------------------------------------*/ +static int +channel_clear(void) +{ + if(!(RFSTATUS & CCA)) { + return CC2430_CCA_BUSY; + } + return CC2430_CCA_CLEAR; +} +/*---------------------------------------------------------------------------*/ +static int +receiving_packet(void) +{ + /* + * SFD high while transmitting and receiving. + * TX_ACTIVE high only when transmitting + * + * RFSTATUS & (TX_ACTIVE | SFD) == SFD <=> receiving + */ + return (RFSTATUS & (TX_ACTIVE | SFD) == SFD); +} +/*---------------------------------------------------------------------------*/ +static int +pending_packet(void) +{ + return (RFSTATUS & FIFOP); +} +/*---------------------------------------------------------------------------*/ +/** + * Enable RF receiver. + * + * + * \return pdTRUE + * \return pdFALSE bus not free + */ +static int +on(void) +{ + rtimer_clock_t t0; + PRINTF("cc2430_rf_rx_enable called\n"); + if(!(rf_flags & RX_ACTIVE)) { + t0 = RTIMER_NOW(); + rf_flags |= RX_ACTIVE; + IOCFG0 = 0x7f; /* Set the FIFOP threshold 127 */ + RSSIH = 0xd2; /* -84dbm = 0xd2 default, 0xe0 -70 dbm */ + + RFPWR &= ~RREG_RADIO_PD; /* make sure it's powered */ + while ((RFIF & IRQ_RREG_ON) == 0); /* wait for power up */ + + /* Make sure the RREG On Interrupt Flag is 0 next time we get called */ + RFIF &= ~IRQ_RREG_ON; + + cc2430_rf_command(ISRXON); + cc2430_rf_command(ISFLUSHRX); + while (RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + ONOFF_TIME)); + + } + PRINTF("cc2430_rf_rx_enable done\n"); + ENERGEST_ON(ENERGEST_TYPE_LISTEN); + return 1; +} +/*---------------------------------------------------------------------------*/ +/** + * Disable RF receiver. + * + * + * \return pdTRUE + * \return pdFALSE bus not free + */ + +static int +off(void) +{ + cc2430_rf_command(ISSTOP); /* make sure CSP is not running */ + cc2430_rf_command(ISRFOFF); + + RFPWR |= RREG_RADIO_PD; /* RF powerdown */ + + /* Clear the RREG On Interrupt Flag */ + RFIF &= ~IRQ_RREG_ON; + + rf_flags &= ~RX_ACTIVE; + rf_flags &= ~WAS_OFF; + ENERGEST_OFF(ENERGEST_TYPE_LISTEN); + return 1; +} +/*---------------------------------------------------------------------------*/ +const struct radio_driver cc2430_rf_driver = +{ + init, + prepare, + transmit, + send, + read, + channel_clear, + receiving_packet, + pending_packet, + on, + off, +}; +/*---------------------------------------------------------------------------*/ +#if !SHORTCUTS_CONF_NETSTACK +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(cc2430_rf_process, ev, data) +{ + int len; + PROCESS_BEGIN(); + while(1) { + PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL); + + packetbuf_clear(); + len = read(packetbuf_dataptr(), PACKETBUF_SIZE); + if(len > 0) { + packetbuf_set_datalen(len); + NETSTACK_RDC.input(); + } + } + + PROCESS_END(); +} +#endif +/*---------------------------------------------------------------------------*/ diff --git a/cpu/cc2430/dev/cc2430_rf.h b/cpu/cc2430/dev/cc2430_rf.h index edaf1e0f5..f64129e09 100644 --- a/cpu/cc2430/dev/cc2430_rf.h +++ b/cpu/cc2430/dev/cc2430_rf.h @@ -8,11 +8,12 @@ #ifndef __CC2430_RF_H__ #define __CC2430_RF_H__ -#include "banked.h" #include "contiki.h" #include "dev/radio.h" #include "cc2430_sfr.h" +#if HAVE_RF_DMA #include "dev/dma.h" +#endif /* Constants */ typedef enum rf_address_mode_t @@ -26,20 +27,20 @@ typedef enum rf_address_mode_t }rf_address_mode_t; /*CSP command set*/ -#define SSTOP 0xDF +#define SSTOP 0xDF /*this is not a real command but a way of having rf_command wait until the script is done*/ #define SSTART 0xDE -#define SNOP 0xC0 +#define SNOP 0xC0 #define STXCALN 0xC1 -#define SRXON 0xC2 -#define STXON 0xC3 +#define SRXON 0xC2 +#define STXON 0xC3 #define STXONCCA 0xC4 #define SRFOFF 0xC5 #define SFLUSHRX 0xC6 #define SFLUSHTX 0xC7 -#define SACK 0xC8 +#define SACK 0xC8 #define SACKPEND 0xC9 #define ISTXCALN 0xE1 @@ -49,7 +50,7 @@ typedef enum rf_address_mode_t #define ISRFOFF 0xE5 #define ISFLUSHRX 0xE6 #define ISFLUSHTX 0xE7 -#define ISACK 0xE8 +#define ISACK 0xE8 #define ISACKPEND 0xE9 #define ISSTOP 0xFF @@ -60,39 +61,31 @@ typedef enum rf_address_mode_t #define CC2430_MAX_PACKET_LEN 127 #define CC2430_MIN_PACKET_LEN 4 +#define CC2430_CCA_CLEAR 1 +#define CC2430_CCA_BUSY 0 + +#ifdef CC2430_CONF_RFERR_INTERRUPT +#define CC2430_RFERR_INTERRUPT CC2430_CONF_RFERR_INTERRUPT +#else +#define CC2430_RFERR_INTERRUPT 0 +#endif + extern const struct radio_driver cc2430_rf_driver; -/* radio_driver functions */ -void cc2430_rf_set_receiver(void (* recv)(const struct radio_driver *)); -int cc2430_rf_on(void); -int cc2430_rf_off(void); -int cc2430_rf_read(void *buf, unsigned short bufsize); -int cc2430_rf_read_banked (void *buf, unsigned short bufsize) __banked; -int cc2430_rf_send(void *data, unsigned short len); -int cc2430_rf_send_b (void *data, unsigned short len) __banked; -extern unsigned short cc2430_rf_payload_len; -extern void *cc2430_rf_payload; - -/* RF driver functions */ -void cc2430_rf_init(void) __banked; -void cc2430_rf_command(uint8_t command) __banked; +void cc2430_rf_command(uint8_t command); int8_t cc2430_rf_channel_set(uint8_t channel); -int8_t cc2430_rf_power_set(uint8_t new_power); -int8_t cc2430_rf_rx_enable(void) __banked; -int8_t cc2430_rf_rx_disable(void) __banked; -int8_t cc2430_rf_tx_enable(void); -int8_t cc2430_rf_address_decoder_mode(rf_address_mode_t mode); -int8_t cc2430_rf_analyze_rssi(void); -int8_t cc2430_rf_cca_check(uint8_t backoff_count, uint8_t slotted); -void cc2430_rf_send_ack(uint8_t pending); +uint8_t cc2430_rf_power_set(uint8_t new_power); void cc2430_rf_set_addr(unsigned pan, unsigned addr, const uint8_t *ieee_addr); +#if !SHORTCUTS_CONF_NETSTACK extern void cc2430_rf_ISR( void ) __interrupt (RF_VECTOR); +#endif +#if CC2430_RFERR_INTERRUPT extern void cc2430_rf_error_ISR( void ) __interrupt (RFERR_VECTOR); +#endif #ifdef HAVE_RF_DMA void rf_dma_callback_isr(void); #endif - #endif /* __CC2430_RF_H__ */ diff --git a/cpu/cc2430/dev/cc2430_rf_intr.c b/cpu/cc2430/dev/cc2430_rf_intr.c index 2c4a26fb0..296024115 100644 --- a/cpu/cc2430/dev/cc2430_rf_intr.c +++ b/cpu/cc2430/dev/cc2430_rf_intr.c @@ -2,11 +2,12 @@ * \file * CC2430 RF driver * \author - * Zach Shelby + * Zach Shelby (Original) + * George Oikonomou - + * (recent updates for the contiki cc2430 port) * * Non-bankable code for cc2430 rf driver. - * Interrupt routine and code called through function pointers - * must be placed into the HOME bank. + * Interrupt routines must be placed into the HOME bank. * */ @@ -23,6 +24,7 @@ #include "net/packetbuf.h" #include "net/rime/rimestats.h" +#include "net/netstack.h" #define DEBUG 0 #if DEBUG #define PRINTF(...) printf(__VA_ARGS__) @@ -46,10 +48,9 @@ uint8_t rf_error = 0; #endif +PROCESS_NAME(cc2430_rf_process); -/*---------------------------------------------------------------------------*/ -PROCESS(cc2430_rf_process, "CC2430 RF driver"); - +#if !SHORTCUTS_CONF_NETSTACK /*---------------------------------------------------------------------------*/ /** * RF interrupt service routine. @@ -59,29 +60,27 @@ void cc2430_rf_ISR( void ) __interrupt (RF_VECTOR) { EA = 0; - if(RFIF & IRQ_TXDONE) { - RF_TX_LED_OFF(); - RFIF &= ~IRQ_TXDONE; - cc2430_rf_command(ISFLUSHTX); - } + ENERGEST_ON(ENERGEST_TYPE_IRQ); + /* + * We only vector here if RFSTATUS.FIFOP goes high. + * Just double check the flag. + */ if(RFIF & IRQ_FIFOP) { - if(RFSTATUS & FIFO) { RF_RX_LED_ON(); /* Poll the RF process which calls cc2430_rf_read() */ process_poll(&cc2430_rf_process); - } else { - cc2430_rf_command(ISFLUSHRX); - cc2430_rf_command(ISFLUSHRX); - RFIF &= ~IRQ_FIFOP; - } } S1CON &= ~(RFIF_0 | RFIF_1); + + ENERGEST_OFF(ENERGEST_TYPE_IRQ); EA = 1; } +#endif /*---------------------------------------------------------------------------*/ +#if CC2430_RFERR_INTERRUPT /** * RF error interrupt service routine. - * + * Turned off by default, can be turned on in contiki-conf.h */ void cc2430_rf_error_ISR( void ) __interrupt (RFERR_VECTOR) @@ -99,57 +98,5 @@ cc2430_rf_error_ISR( void ) __interrupt (RFERR_VECTOR) RF_TX_LED_OFF(); EA = 1; } - -void (* receiver_callback)(const struct radio_driver *); - -void -cc2430_rf_set_receiver(void (* recv)(const struct radio_driver *)) -{ - receiver_callback = recv; -} +#endif /*---------------------------------------------------------------------------*/ -/* - * non-banked functions called through function pointers then call banked code - */ -int -cc2430_rf_off(void) -{ - return cc2430_rf_rx_disable(); -} -/*---------------------------------------------------------------------------*/ -int -cc2430_rf_on(void) -{ - return cc2430_rf_rx_enable(); -} -/*---------------------------------------------------------------------------*/ -int -cc2430_rf_send(void *payload, unsigned short payload_len) -{ - return cc2430_rf_send_b(payload, payload_len); -} -/*---------------------------------------------------------------------------*/ -int -cc2430_rf_read(void *buf, unsigned short bufsize) -{ - return cc2430_rf_read_banked(buf, bufsize); -} -/*---------------------------------------------------------------------------*/ -/*---------------------------------------------------------------------------*/ -PROCESS_THREAD(cc2430_rf_process, ev, data) -{ - PROCESS_BEGIN(); - while(1) { - PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL); - - if(receiver_callback != NULL) { - PRINTF("cc2430_rf_process: calling receiver callback\n"); - receiver_callback(&cc2430_rf_driver); - } else { - PRINTF("cc2430_rf_process: no receiver callback\n"); - cc2430_rf_command(ISFLUSHRX); - } - } - - PROCESS_END(); -} diff --git a/cpu/cc2430/dev/clock.c b/cpu/cc2430/dev/clock.c index 00513596c..a748f2853 100644 --- a/cpu/cc2430/dev/clock.c +++ b/cpu/cc2430/dev/clock.c @@ -46,20 +46,24 @@ #include "sys/clock.h" #include "sys/etimer.h" #include "cc2430_sfr.h" - +#include "sys/energest.h" /*Sleep timer runs on the 32k RC osc. */ /* One clock tick is 7.8 ms */ -#define TICK_VAL (32768/128) - -#define MAX_TICKS (~((clock_time_t)0) / 2) +#define TICK_VAL (32768/128) /* 256 */ /* Used in sleep timer interrupt for calculating the next interrupt time */ static unsigned long timer_value; /*starts calculating the ticks right after reset*/ -static volatile clock_time_t count = 0; +#if CLOCK_CONF_ACCURATE +static volatile __data clock_time_t count = 0; +#else +volatile __data clock_time_t count = 0; +/* accurate clock is stack hungry */ +volatile __bit sleep_flag; +#endif /*calculates seconds*/ -static volatile clock_time_t seconds = 0; +static volatile __data clock_time_t seconds = 0; /*---------------------------------------------------------------------------*/ /** @@ -103,7 +107,7 @@ clock_seconds(void) void clock_init(void) { - CLKCON = OSC32K | TICKSPD2|TICKSPD1|TICKSPD0; /*tickspeed 250 kHz*/ + CLKCON = OSC32K | TICKSPD2|TICKSPD1; /*tickspeed 500 kHz for timers[1-4]*/ /*Initialize tick value*/ timer_value = ST0; /*sleep timer 0. low bits [7:0]*/ @@ -118,9 +122,18 @@ clock_init(void) } /*---------------------------------------------------------------------------*/ void -cc2430_clock_ISR( void ) __interrupt (ST_VECTOR) +clock_ISR( void ) __interrupt (ST_VECTOR) { IEN0_EA = 0; /*interrupt disable*/ + ENERGEST_ON(ENERGEST_TYPE_IRQ); + + /* + * If the Sleep timer throws an interrupt while we are powering down to + * PM1, we need to abort the power down. Clear SLEEP.MODE, this will signal + * main() to abort the PM1 transition + */ + SLEEP &= 0xFC; + /* When using the cooperative scheduler the timer 2 ISR is only required to increment the RTOS tick count. */ @@ -148,12 +161,17 @@ cc2430_clock_ISR( void ) __interrupt (ST_VECTOR) ++seconds; } +#if CLOCK_CONF_ACCURATE if(etimer_pending() && (etimer_next_expiration_time() - count - 1) > MAX_TICKS) { /*core/sys/etimer.c*/ etimer_request_poll(); } +#else + sleep_flag = 1; +#endif IRCON &= ~STIF; /*IRCON.STIF=Sleep timer interrupt flag. This flag called this interrupt func, now reset it*/ + ENERGEST_OFF(ENERGEST_TYPE_IRQ); IEN0_EA = 1; /*interrupt enable*/ } /*---------------------------------------------------------------------------*/ diff --git a/cpu/cc2430/dev/dma.c b/cpu/cc2430/dev/dma.c index f5b6ca3d7..d549850c5 100644 --- a/cpu/cc2430/dev/dma.c +++ b/cpu/cc2430/dev/dma.c @@ -1,216 +1,69 @@ /** * \file - * DMA driver + * Driver for the cc2430 DMA controller. Can be assigned to any bank + * * \author * Original: Martti Huttunen * Port: Zach Shelby + * Further Modifications: + * George Oikonomou * - * bankable DMA functions */ -#include - #include "contiki.h" -#include "banked.h" - #include "dev/dma.h" #include "cc2430_sfr.h" -dma_config_t dma_conf[4]; -struct process * dma_callback[4]; - +#if DMA_ON +struct dma_config dma_conf[DMA_CHANNEL_COUNT]; /* DMA Descriptors */ +struct process * dma_callback[DMA_CHANNEL_COUNT]; /*---------------------------------------------------------------------------*/ void -dma_init(void) __banked +dma_init(void) { uint16_t tmp_ptr; - memset(dma_conf, 0, 4*sizeof(dma_config_t)); - for(tmp_ptr = 0; tmp_ptr < 4; tmp_ptr++) { + + memset(dma_conf, 0, 4 * sizeof(dma_config_t)); + + for(tmp_ptr = 0; tmp_ptr < DMA_CHANNEL_COUNT; tmp_ptr++) { dma_callback[tmp_ptr] = 0; } - tmp_ptr = (uint16_t) &(dma_conf[0]); + /* The address of the descriptor for Channel 0 is configured separately */ + tmp_ptr = (uint16_t) &(dma_conf[0]); + DMA0CFGH = tmp_ptr >> 8; + DMA0CFGL = tmp_ptr; + + /* + * Descriptors for Channels 1-4 must be consecutive in RAM. + * We write the address of the 1st one to the register and the rest are + * derived by the SoC + */ +#if (DMA_CHANNEL_COUNT > 1) + tmp_ptr = (uint16_t) &(dma_conf[1]); DMA1CFGH = tmp_ptr >> 8; DMA1CFGL = tmp_ptr; - IEN1_DMAIE = 1; /*enable DMA interrupts*/ -} -/*---------------------------------------------------------------------------*/ -#ifdef HAVE_DMA -/** - * Configure a DMA channel except word mode. - * - * \param channel channel ID; - * \param src source address; - * \param src_inc source increment mode; - * \param dst dest address; - * \param dst_inc dest increment mode; - * \param length maximum length; - * \param vlen_mode variable length mode; - * \param t_mode DMA transfer mode; - * \param trigger DMA trigger; - * \param proc process that is upon interrupt; - * - * \return Handle to DMA channel - * \return 0 invalid channel - */ -/* IMPLEMENTED dma_config as macro to reduce stack/code space -xDMAHandle -dma_config(uint8_t channel, void *src, dma_inc_t src_inc, void *dst, dma_inc_t dst_inc, - uint16_t length, dma_vlen_t vlen_mode, dma_type_t t_mode, dma_trigger_t trigger, - struct process * proc) __banked -{ - return dma_config2(channel,src,src_inc, dst, dst_inc, length, 0, vlen_mode, t_mode, trigger, proc); -} -*/ -/*---------------------------------------------------------------------------*/ -/** - * Configure a DMA channel. - * - * \param channel channel ID; - * \param src source address; - * \param src_inc source increment mode; - * \param dst dest address; - * \param dst_inc dest increment mode; - * \param length maximum length; - * \param word_mode set to 1 for 16-bits per transfer; - * \param vlen_mode variable length mode; - * \param t_mode DMA transfer mode; - * \param trigger DMA trigger; - * \param proc process that is upon interrupt; - * - * \return Handle to DMA channel - * \return 0 invalid channel - */ -xDMAHandle -dma_config2(uint8_t channel, void *src, dma_inc_t src_inc, void *dst, dma_inc_t dst_inc, - uint16_t length, uint8_t word_mode, dma_vlen_t vlen_mode, dma_type_t t_mode, dma_trigger_t trigger, - struct process * proc) __banked -{ - unsigned char jj; - if((!channel) || (channel > 4)) { - return 0; - } +#endif - DMAIRQ &= ~(1 << channel); - - channel--; - - dma_conf[channel].src_h = ((uint16_t) src) >> 8; - dma_conf[channel].src_l = ((uint16_t) src); - dma_conf[channel].dst_h = ((uint16_t) dst) >> 8; - dma_conf[channel].dst_l = ((uint16_t) dst); - dma_conf[channel].len_h = vlen_mode + (length >> 8); - dma_conf[channel].len_l = length; - dma_conf[channel].t_mode = ((word_mode&0x1)<<7) | (t_mode << 5) | trigger; - dma_conf[channel].addr_mode = (src_inc << 6) + (dst_inc << 4) + 2; /*DMA has priority*/ - - /*Callback is defined*/ - if(proc) { - dma_conf[channel].addr_mode |= 8; /*set IRQMASK*/ - IEN1_DMAIE = 1; /*enable DMA interrupts*/ - } - dma_callback[channel] = proc; - - return (xDMAHandle)channel + 1; + IEN1_DMAIE = 1; /* Enable DMA interrupts */ } /*---------------------------------------------------------------------------*/ -/** - * Arm a DMA channel. - * - * \param channel channel handle; - * - * \return pdTRUE - * \return pdFALSE semaphore creation failed +/* + * Associate process p with DMA channel c. When a transfer on that channel + * completes, the ISR will poll this process. */ -uint8_t -dma_arm(xDMAHandle channel) __banked -{ - uint8_t ch_id = ((uint8_t)channel); - if(!ch_id || (ch_id > 4)) { - return 0; - } - DMAARM |= (1 << ch_id); - return 1; -} -/*---------------------------------------------------------------------------*/ -/** - * Stop a DMA channel. - * - * \param channel channel handle; - * - * \return pdTRUE - * \return pdFALSE semaphore creation failed - */ -uint8_t -dma_abort(xDMAHandle channel) __banked -{ - uint8_t ch_id = ((uint8_t) channel); - if(!ch_id || (ch_id > 4)) { - return 0; - } - DMAARM = 0x80 + (1 << ch_id); /*ABORT + channel bit*/ - return 1; -} -/*---------------------------------------------------------------------------*/ -/** - * Trigger a DMA channel. - * - * \param channel channel handle; - * - * \return pdTRUE - * \return pdFALSE semaphore creation failed - */ -uint8_t -dma_trigger(xDMAHandle channel) __banked -{ - uint8_t ch_id = ((uint8_t) channel); - if(!ch_id || (ch_id > 4)) { - return 0; - } - DMAREQ |= (1 << ch_id); - return 1; -} -/*---------------------------------------------------------------------------*/ -/** - * Get DMA state. - * - * \param channel channel handle; - * - * \return pdTRUE active - * \return pdFALSE not active - */ -uint8_t -dma_state(xDMAHandle channel) __banked -{ - uint8_t ch_id = ((uint8_t)channel); - if(!ch_id || (ch_id > 4)) { - return 0; - } - if((DMAIRQ &(1 << ch_id)) == 0) { - return 1; - } - return 0; -} -/*---------------------------------------------------------------------------*/ void -dma_config_print(xDMAHandle channel) __banked +dma_associate_process(struct process * p, uint8_t c) { - uint8_t ch_id = channel - 1; - - if(ch_id > 4) { + if((!c) || (c >= DMA_CHANNEL_COUNT)) { return; } - - printf("DMA channel %d @ %x %x ", ch_id, (uint16_t) &(dma_conf[ch_id]) >> 8, (uint16_t) &(dma_conf[ch_id]) & 0xFF); - { - uint8_t i; - uint8_t *ptr = (uint8_t *)&(dma_conf[ch_id]); - for(i = 0; i< 8; i++) { - if(i != 0) { - printf(":%02x", *ptr++); - } - } - printf("\n"); + + if(p) { + dma_conf[c].inc_prio |= 8; /* Enable interrupt generation */ + IEN1_DMAIE = 1; /* Make sure DMA interrupts are acknowledged */ } + dma_callback[c] = p; } +/*---------------------------------------------------------------------------*/ #endif diff --git a/cpu/cc2430/dev/dma.h b/cpu/cc2430/dev/dma.h index f8ac02802..d88167734 100644 --- a/cpu/cc2430/dev/dma.h +++ b/cpu/cc2430/dev/dma.h @@ -1,122 +1,148 @@ /** * \file - * DMA driver header + * Header file for the cc2430 DMA controller + * * \author * Original: Martti Huttunen * Port: Zach Shelby + * Further Modifications: + * George Oikonomou */ #ifndef __DMA_H #define __DMA_H -#include "banked.h" #include "cc2430_sfr.h" -/** DMA triggers */ -typedef enum dma_trigger_t -{ - DMA_T_NONE=0, /*! -extern uint8_t p0ien; -extern uint8_t p2ien; #define HWCONF_PIN(name, port, bit) \ static CC_INLINE void name##_SELECT() {P##port##SEL &= ~(1 << bit);} \ @@ -51,10 +49,10 @@ static CC_INLINE void name##_MAKE_INPUT() {P##port##DIR &= ~(1 << bit); } #define HWCONF_IRQ_XXX(name, port, bit) \ static CC_INLINE void name##_ENABLE_IRQ() { \ - if ( port == 2 ) { PICTL |= P2IEN; p2ien |= 1<=4)) { PICTL |= P0IENH; p0ien |= 1<=4)) { PICTL |= P0IENH; p0ien |= 1<=4 ) { PICTL |= P0IENH; p0ien |= 1<=4) { \ + p0ien &= ~(1< */ #ifndef __LPM_H__ #define __LPM_H__ -#include #include "contiki-conf.h" -#ifdef LPM_CONF_ON -#define LPM_ON LPM_CONF_ON +#define LPM_MODE_NONE 0 /* No LPM - Always on */ +#define LPM_MODE_IDLE 1 /* Set MCU Idle as part of the main loop */ +#define LPM_MODE_PM2 2 /* Drop to PM1 - causes radio packet losses for now */ + +#ifdef LPM_CONF_MODE +#define LPM_MODE LPM_CONF_MODE #else -#define LPM_ON LPM1 -#endif /* LPM_CONF_ON */ - -#ifdef LPM_CONF_OFF -#define LPM_OFF LPM_CONF_OFF -#else -#define LPM_OFF LPM1_EXIT -#endif /* LPM_CONF_OFF */ - -#define LPM_SLEEP() do { if(lpm_status == LPM_STATUS_ON) LPM_ON; } while(0) -#define LPM_AWAKE() do { if(lpm_status == LPM_STATUS_ON) LPM_OFF; } while(0) - -extern unsigned char lpm_status; - -void lpm_on(void); -void lpm_off(void); - -#define LPM_STATUS_OFF 0 -#define LPM_STATUS_ON 1 - +#define LPM_MODE LPM_MODE_IDLE +#endif /* LPM_CONF_MODE */ #endif /* __LPM_H__ */ diff --git a/cpu/cc2430/dev/random.c b/cpu/cc2430/dev/random.c new file mode 100644 index 000000000..639f384e5 --- /dev/null +++ b/cpu/cc2430/dev/random.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Random number generator routines exploiting the cc2430 hardware + * capabilities. + * + * This file overrides core/lib/random.c. + * + * \author + * George Oikonomou - + */ +#include "cc2430_sfr.h" +#include "dev/cc2430_rf.h" +/*---------------------------------------------------------------------------*/ +/** + * \brief Generates a new random number using the cc2430 RNG. + * \return The random number. + */ +unsigned short +random_rand(void) +{ + /* Clock the RNG LSFR once */ + ADCCON1 |= ADRCTRL0; + + return (RNDL | (RNDH << 8)); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Seed the cc2430 random number generator. + * \param seed Seed value for the RNG. + * + * If the SEED argument is 0, seed the RNG with IF_ADC as + * discussed in the cc2430 datasheet (rev. 2.1), section 13.11.2.2, + * page 134. Seeding with this method should not be done during + * normal radio operation. Thus, use this function before + * initialising the network. + * + * If the SEED is provided, seed with this value instead. This will + * result in the same sequence of random numbers each time the node + * reboots. So, don't use it unless you have a reason (e.g. tests) + */ +void +random_init(unsigned short seed) +{ + int i; + + /* Comment out this if() block to save a nice 16 bytes of code size */ + if(seed) { + /* If the caller provides a seed, write the high-byte first and then + * write the low byte */ + RNDL = seed >> 8; /* High byte first */ + RNDL = seed & 0xFF; + return; + } + + /* + * cc2430 Datasheet: + * "When a true random value is required, the LFSR should be seeded by + * writing RNDL with random values from the IF_ADC in the RF receive path." + * + * "To use this seeding method, the radio must first be powered on by + * enabling the voltage regulator" + */ + RFPWR &= ~RREG_RADIO_PD; /* Turn on the voltage regulator */ + while(!(RFIF & IRQ_RREG_ON)); /* Wait for power up*/ + + /* OK, it's powered. The respective interrupt flag has been set, clear it */ + RFIF &= ~IRQ_RREG_ON; + + /* + * "The radio should be placed in infinite TX state, to avoid possible sync + * detect in RX state." + * + * Judging by old chipcon cc2430 code examples as well as by the way cc2530 + * works, this is very likely to be "RX state" (i.e. a typo in the datasheet) + * + * With infinite TX, ADCTSTx always read as 0 so we'll use infinite RX + */ + MDMCTRL1L = 0x02; /* RX mode 10 - RX_INFINITE state */ + + /* "Enter RX State - Immediate" command strobe */ + cc2430_rf_command(ISRXON); + + /* Make sure the RNG is on */ + ADCCON1 &= ~(ADRCTRL1 | ADRCTRL0); + + /* Wait for IF_ADC I-branch and Q-branch values */ + while(!(ADCTSTH & ADCTSTL)); + + /* 32 times as per the chipcon example. This seems to increase randomness */ + for(i = 0; i < 32; i++) { + /* Seed the RNG by writing into RNDL twice with values from ADCTSTx */ + RNDL = ADCTSTH; + RNDL = ADCTSTL; + + /* Clock the RNG LSFR once */ + ADCCON1 |= ADRCTRL0; + } + + /* + * Exit RX state. Just shut down, network initialisation will take care of + * properly starting the radio for us. + */ + RFPWR |= RREG_RADIO_PD; +} diff --git a/cpu/cc2430/dev/rs232.c b/cpu/cc2430/dev/rs232.c deleted file mode 100644 index 1dc233348..000000000 --- a/cpu/cc2430/dev/rs232.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2007, Takahide Matsutsuka. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. - * - * $Id: rs232.c,v 1.1 2009/09/08 20:07:35 zdshelby Exp $ - * - */ -/* - * \file - * This is RS-232C process based on polling. - * Note that rs232.c and rs232-slip.c cannot be used at the same time. - * \author - * Takahide Matsutsuka - */ - -#include "contiki.h" -#include "dev/slip.h" -#include "dev/serial.h" -#include "dev/rs232.h" - -PROCESS(rs232_process, "RS-232C polling process"); -/*---------------------------------------------------------------------------*/ -PROCESS_THREAD(rs232_process, ev, data) -{ - static struct etimer timer; - char ch; - unsigned char i, stat; - PROCESS_BEGIN(); - - rs232_arch_init(RS232_BAUD_RATE); - etimer_set(&timer, CLOCK_SECOND / 16); - - while(1) { - PROCESS_WAIT_EVENT(); - - if (etimer_expired(&timer)) { - for (i = 0; i < RS232_BUFSIZE; i++) { - ch = rs232_arch_poll(&stat); - if (stat == 0) { - break; - } - /* We have an input data */ - RS232_CALLBACK(ch); - } - etimer_reset(&timer); - } - } - - PROCESS_END(); -} -/*---------------------------------------------------------------------------*/ diff --git a/cpu/cc2430/dev/rs232.h b/cpu/cc2430/dev/rs232.h deleted file mode 100644 index 865e8a415..000000000 --- a/cpu/cc2430/dev/rs232.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2007, Takahide Matsutsuka. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. - * - * $Id: rs232.h,v 1.1 2009/09/08 20:07:35 zdshelby Exp $ - * - */ -/* - * \file - * This is RS-232C process based on polling. - * \author - * Takahide Matsutsuka - */ - -#ifndef __RS232_H__ -#define __RS232_H__ - -/* - * Implement the following methods for each platform. - */ - -/* - * An architecture-depend implementation of RS-232C initialization. - */ -void rs232_arch_init(unsigned long ubr); - -/* - * An architecture-depend implementation of RS-232C polling. - * @return character, stat == zero if no input. - */ -unsigned char rs232_arch_poll(unsigned char* stat); - -/* - * An architecture-depend implementation of RS-232C writing a byte. - */ -void rs232_arch_writeb(unsigned char ch); - -PROCESS_NAME(rs232_process); - -/* - * if you want to use simple serial communication, - * define RS232_CONF_CALLBACK as serial_input_byte. - * The default is SLIP. - */ -#ifdef RS232_CONF_CALLBACK -#define RS232_CALLBACK RS232_CONF_CALLBACK -#else /* RS232_CONF_CALLBACK */ -#define RS232_CALLBACK slip_input_byte -#endif /* RS232_CONF_CALLBACK */ - -#ifdef RS232_CONF_BUFISZE -#define RS232_BUFSIZE RS232_CONF_BUFISZE -#else /* RS232_CONF_BUFISZE */ -#define RS232_BUFSIZE 64 -#endif /* RS232_CONF_BUFISZE */ - -#ifdef RS232_CONF_BAUD_RATE -#define RS232_BAUD_RATE RS232_CONF_BAUD_RATE -#else /* RS232_CONF_BAUD_RATE */ -#define RS232_BAUD_RATE 9600 -#endif /* RS232_CONF_BAUD_RATE */ - -#endif /* __RS232_H__ */ diff --git a/cpu/cc2430/dev/uart.c b/cpu/cc2430/dev/uart.c deleted file mode 100644 index 86fc8fc48..000000000 --- a/cpu/cc2430/dev/uart.c +++ /dev/null @@ -1,41 +0,0 @@ -/** - * \file - * - * uart write routines - * - * \author - * - * Anthony "Asterisk" Ambuehl - * - * non-interrupt routines which may be called from ISR's and therefore should be in HOME bank. - * - */ -#include -#include - -#include "cc2430_sfr.h" - -#include "dev/leds.h" -#include "dev/uart.h" - -/*---------------------------------------------------------------------------*/ -/* Write one byte over the UART. */ -void -uart0_writeb(uint8_t byte) -{ - IRCON2_UTX0IF = 0; - U0BUF = byte; - while(!IRCON2_UTX0IF); /* Wait until byte has been transmitted. */ - IRCON2_UTX0IF = 0; -} -/*---------------------------------------------------------------------------*/ -/* Write one byte over the UART. */ -void -uart1_writeb(uint8_t byte) -{ - IRCON2_UTX1IF = 0; - U1BUF = byte; - while(!IRCON2_UTX1IF); /* Wait until byte has been transmitted. */ - IRCON2_UTX1IF = 0; -} -/*---------------------------------------------------------------------------*/ diff --git a/cpu/cc2430/dev/uart.h b/cpu/cc2430/dev/uart.h index 8b39cff3a..e230c3bcd 100644 --- a/cpu/cc2430/dev/uart.h +++ b/cpu/cc2430/dev/uart.h @@ -2,24 +2,42 @@ #define UART_H #include "contiki-conf.h" -#include "banked.h" #include "cc2430_sfr.h" +#include "8051def.h" -void uart0_init(uint32_t speed) __banked; -void uart0_writeb(uint8_t byte); +/*---------------------------------------------------------------------------*/ +/* UART BAUD Rates */ +/* + * Macro to set speed of UART N by setting the UnBAUD SFR to M and the + * UnGCR SRF to E. See the cc2430 datasheet for possible values of M and E + */ +#define UART_SET_SPEED(N, M, E) do{ U##N##BAUD = M; U##N##GCR = E; } while(0) -void uart0_set_input(int (*input)(unsigned char c)); +/* + * Sample Values for M and E in the macro above to achieve some common BAUD + * rates. For more values, see the cc2430 datasheet + */ +/* 2000000 - cc2430 theoretical MAX when using the 32MHz clock */ +#define UART_2K_M 0 +#define UART_2K_E 16 +/* 1000000 - cc2430 theoretical MAX when using the 16MHz clock */ +#define UART_1K_M 0 +#define UART_1K_E 15 +/* 921600 */ +#define UART_921_M 216 +#define UART_921_E 14 +/* 460800 Higher values lead to problems when the node needs to RX */ +#define UART_460_M 216 +#define UART_460_E 13 +/* 115200 */ +#define UART_115_M 216 +#define UART_115_E 11 +/* 38400 */ +#define UART_38_M 59 +#define UART_38_E 10 +/* 9600 */ +#define UART_9_M 59 +#define UART_9_E 8 -void uart0_rxISR( void ) __interrupt (URX0_VECTOR); -void uart0_txISR( void ) __interrupt (UTX0_VECTOR); - -void uart1_init(uint32_t speed) __banked; -void uart1_writeb(uint8_t byte); - -void uart1_set_input(int (*input)(unsigned char c)); - -void uart1_rxISR( void ) __interrupt (URX1_VECTOR); -void uart1_txISR( void ) __interrupt (UTX1_VECTOR); - -#endif /*UART_H*/ +#endif /* UART_H */ diff --git a/cpu/cc2430/dev/uart0.c b/cpu/cc2430/dev/uart0.c new file mode 100644 index 000000000..254dba765 --- /dev/null +++ b/cpu/cc2430/dev/uart0.c @@ -0,0 +1,69 @@ +/** + * \file + * + * uart0 write routines + * + * \author + * + * Anthony "Asterisk" Ambuehl + * + */ +#include +#include + +#include "cc2430_sfr.h" +#include "dev/uart0.h" + +#if UART_ZERO_ENABLE +/*---------------------------------------------------------------------------*/ +void +uart0_init() +{ + UART_SET_SPEED(0, UART_115_M, UART_115_E); + +#ifdef UART0_ALTERNATIVE_2 + PERCFG |= U0CFG; /*alternative port 2 = P1.5-2*/ +#ifdef UART0_RTSCTS + P1SEL |= 0x3C; /*peripheral select for TX and RX, RTS, CTS*/ +#else + P1SEL |= 0x30; /*peripheral select for TX and RX*/ + P1 &= ~0x08; /*RTS down*/ +#endif + P1DIR |= 0x28; /*RTS, TX out*/ + P1DIR &= ~0x14; /*CTS & RX in*/ +#else + PERCFG &= ~U0CFG; /*alternative port 1 = P0.5-2*/ +#ifdef UART0_RTSCTS + P0SEL |= 0x3C; /*peripheral select for TX and RX, RTS, CTS*/ +#else + P0SEL |= 0x0C; /*peripheral select for TX and RX*/ + P0 &= ~0x20; /*RTS down*/ +#endif + P0DIR |= 0x28; /*RTS & TX out*/ + P0DIR &= ~0x14; /*CTS & RX in*/ +#endif + + +#ifdef UART0_RTSCTS + U0UCR = 0x42; /*defaults: 8N1, RTS/CTS, high stop bit*/ +#else + U0UCR = 0x02; /*defaults: 8N1, no flow control, high stop bit*/ +#endif + + U0CSR = U_MODE | U_RE | U_TXB; /*UART mode, receiver enable, TX done*/ + + /*set priority group of group 3 to highest, so the UART won't miss bytes*/ + IP1 |= IP1_3; + IP0 |= IP0_3; +} +/*---------------------------------------------------------------------------*/ +/* Write one byte over the UART. */ +void +uart0_writeb(uint8_t byte) +{ + IRCON2_UTX0IF = 0; + U0BUF = byte; + while(!IRCON2_UTX0IF); /* Wait until byte has been transmitted. */ + IRCON2_UTX0IF = 0; +} +#endif diff --git a/cpu/cc2430/dev/uart0.h b/cpu/cc2430/dev/uart0.h new file mode 100644 index 000000000..7ac56e9f6 --- /dev/null +++ b/cpu/cc2430/dev/uart0.h @@ -0,0 +1,31 @@ +#ifndef UART_0_H +#define UART_0_H + +#include "contiki-conf.h" + +#include "cc2430_sfr.h" +#include "8051def.h" +#include "uart.h" + +/*---------------------------------------------------------------------------*/ +/* UART0 Enable - Disable */ +#ifdef UART_ZERO_CONF_ENABLE +#define UART_ZERO_ENABLE UART_ZERO_CONF_ENABLE +#else +#define UART_ZERO_ENABLE 0 +#endif +/*---------------------------------------------------------------------------*/ +/* UART0 Function Declarations */ +#if UART_ZERO_ENABLE +void uart0_init(); +void uart0_writeb(uint8_t byte); + +void uart0_set_input(int (*input)(unsigned char c)); + +void uart0_rx_ISR( void ) __interrupt (URX0_VECTOR); +void uart0_tx_ISR( void ) __interrupt (UTX0_VECTOR); +/* Macro to turn on / off UART RX Interrupt */ +#define UART0_RX_INT(v) IEN0_URX0IE = v +#endif + +#endif /* UART_0_H */ diff --git a/cpu/cc2430/dev/uart1.c b/cpu/cc2430/dev/uart1.c new file mode 100644 index 000000000..7f388c5b6 --- /dev/null +++ b/cpu/cc2430/dev/uart1.c @@ -0,0 +1,74 @@ +/** + * \file + * + * uart1 write routines + * + * \author + * + * Anthony "Asterisk" Ambuehl + * + */ +#include +#include + +#include "cc2430_sfr.h" +#include "dev/uart1.h" + +#if UART_ONE_ENABLE +/*---------------------------------------------------------------------------*/ +/* UART1 initialization */ +void +uart1_init() +{ +#ifdef UART1_ALTERNATIVE_1 + PERCFG &= ~U1CFG; /*alternative port 1 = P0.5-2*/ +#ifdef UART1_RTSCTS + P0SEL |= 0x3C; /*peripheral select for TX and RX, RTS, CTS*/ +#else + P0SEL |= 0x30; /*peripheral select for TX and RX*/ + P0 &= ~0x08; /*RTS down*/ +#endif + P0DIR |= 0x18; /*RTS, TX out*/ + P0DIR &= ~0x24; /*CTS, RX in*/ +#else + PERCFG |= U1CFG; /*alternative port 2 = P1.7-4*/ +#ifdef UART1_RTSCTS + P1SEL |= 0xF0; /*peripheral select for TX and RX*/ +#else + P1SEL |= 0xC0; /*peripheral select for TX and RX*/ + P1 &= ~0x20; /*RTS down*/ +#endif + P1DIR |= 0x60; /*RTS, TX out*/ + P1DIR &= ~0x90; /*CTS, RX in*/ +#endif + +#if UART_ONE_CONF_HIGH_SPEED + UART_SET_SPEED(1, UART_460_M, UART_460_E); +#else + UART_SET_SPEED(1, UART_115_M, UART_115_E); +#endif + +#ifdef UART1_RTSCTS + U1UCR = 0x42; /*defaults: 8N1, RTS/CTS, high stop bit*/ +#else + U1UCR = 0x02; /*defaults: 8N1, no flow control, high stop bit*/ +#endif + + U1CSR = U_MODE | U_RE; /* UART mode, receiver enable */ + + /*set priority group of group 3 to highest, so the UART won't miss bytes*/ + IP1 |= IP1_3; + IP0 |= IP0_3; +} +/*---------------------------------------------------------------------------*/ +/* Write one byte over the UART. */ +void +uart1_writeb(uint8_t byte) +{ + IRCON2_UTX1IF = 0; + U1BUF = byte; + while(!IRCON2_UTX1IF); /* Wait until byte has been transmitted. */ + IRCON2_UTX1IF = 0; +} +/*---------------------------------------------------------------------------*/ +#endif diff --git a/cpu/cc2430/dev/uart1.h b/cpu/cc2430/dev/uart1.h new file mode 100644 index 000000000..53022a902 --- /dev/null +++ b/cpu/cc2430/dev/uart1.h @@ -0,0 +1,39 @@ +#ifndef UART_1_H +#define UART_1_H + +#include "contiki-conf.h" + +#include "cc2430_sfr.h" +#include "8051def.h" +#include "uart.h" + +/*---------------------------------------------------------------------------*/ +/* UART1 Enable - Disable */ +#ifdef UART_ONE_CONF_ENABLE +#define UART_ONE_ENABLE UART_ONE_CONF_ENABLE +#else +#define UART_ONE_ENABLE 0 +#endif +/*---------------------------------------------------------------------------*/ +/* UART1 Function Declarations */ +#if UART_ONE_ENABLE +void uart1_init(); +void uart1_writeb(uint8_t byte); + +void uart1_set_input(int (*input)(unsigned char c)); +#if UART_ONE_CONF_WITH_INPUT +void uart1_rx_ISR( void ) __interrupt (URX1_VECTOR); +void uart1_tx_ISR( void ) __interrupt (UTX1_VECTOR); +/* Macro to turn on / off UART RX Interrupt */ +#define UART1_RX_INT(v) IEN0_URX1IE = v +#else +#define UART1_RX_INT(v) +#endif /* UART_ONE_CONF_WITH_INPUT */ +#else +#define uart1_init(...) +#define uart1_writeb(...) +#define uart1_set_input(...) +#define UART1_RX_INT(v) +#endif /* UART_ONE_ENABLE */ + +#endif /* UART_1_H */ diff --git a/cpu/cc2430/dev/uart_init.c b/cpu/cc2430/dev/uart_init.c deleted file mode 100644 index 95d5f7284..000000000 --- a/cpu/cc2430/dev/uart_init.c +++ /dev/null @@ -1,133 +0,0 @@ -/** - * \file - * - * uart initialization routines - * - * \author - * - * Anthony "Asterisk" Ambuehl - * - * non-interrupt routines typically only called once, stored in any bank. - * - */ -#include -#include - -#include "banked.h" -#include "cc2430_sfr.h" - -#include "dev/leds.h" -#include "dev/uart.h" - -/*---------------------------------------------------------------------------*/ -void -uart0_init(uint32_t speed) __banked -{ - if(speed == 115200) { - U0BAUD=216; /*115200*/ - U0GCR =11; /*LSB first and 115200*/ - } - else if(speed == 38400) { - U0BAUD=59; /*38400*/ - U0GCR =10; /*LSB first and 38400*/ - } - else if(speed == 9600) { - U0BAUD= 59; /* 9600 */ - U0GCR = 8; /*LSB first and 9600*/ - } - else { return; } - -#ifdef UART0_ALTERNATIVE_2 - PERCFG |= U0CFG; /*alternative port 2 = P1.5-2*/ -#ifdef UART0_RTSCTS - P1SEL |= 0x3C; /*peripheral select for TX and RX, RTS, CTS*/ -#else - P1SEL |= 0x30; /*peripheral select for TX and RX*/ - P1 &= ~0x08; /*RTS down*/ -#endif - P1DIR |= 0x28; /*RTS, TX out*/ - P1DIR &= ~0x14; /*CTS & RX in*/ -#else - PERCFG &= ~U0CFG; /*alternative port 1 = P0.5-2*/ -#ifdef UART0_RTSCTS - P0SEL |= 0x3C; /*peripheral select for TX and RX, RTS, CTS*/ -#else - P0SEL |= 0x0C; /*peripheral select for TX and RX*/ - P0 &= ~0x20; /*RTS down*/ -#endif - P0DIR |= 0x28; /*RTS & TX out*/ - P0DIR &= ~0x14; /*CTS & RX in*/ -#endif - - -#ifdef UART0_RTSCTS - U0UCR = 0x42; /*defaults: 8N1, RTS/CTS, high stop bit*/ -#else - U0UCR = 0x02; /*defaults: 8N1, no flow control, high stop bit*/ -#endif - - U0CSR = U_MODE | U_RE | U_TXB; /*UART mode, receiver enable, TX done*/ - - /*set priority group of group 3 to highest, so the UART won't miss bytes*/ - IP1 |= IP1_3; - IP0 |= IP0_3; - - IEN0_URX0IE = 1; -} -/*---------------------------------------------------------------------------*/ -/* UART1 initialization */ -void -uart1_init(uint32_t speed) __banked -{ -#ifdef UART1_ALTERNATIVE_1 - PERCFG &= ~U1CFG; /*alternative port 1 = P0.5-2*/ -#ifdef UART1_RTSCTS - P0SEL |= 0x3C; /*peripheral select for TX and RX, RTS, CTS*/ -#else - P0SEL |= 0x30; /*peripheral select for TX and RX*/ - P0 &= ~0x08; /*RTS down*/ -#endif - P0DIR |= 0x18; /*RTS, TX out*/ - P0DIR &= ~0x24; /*CTS, RX in*/ -#else - PERCFG |= U1CFG; /*alternative port 2 = P1.7-4*/ -#ifdef UART1_RTSCTS - P1SEL |= 0xF0; /*peripheral select for TX and RX*/ -#else - P1SEL |= 0xC0; /*peripheral select for TX and RX*/ - P1 &= ~0x20; /*RTS down*/ -#endif - P1DIR |= 0x60; /*RTS, TX out*/ - P1DIR &= ~0x90; /*CTS, RX in*/ -#endif - - if(speed == 115200) { - U1BAUD=216; /*115200*/ - U1GCR =11; /*LSB first and 115200*/ - } - - if(speed == 38400) { - U1BAUD=59; /*38400*/ - U1GCR =10; /*LSB first and 38400*/ - } - - if(speed == 9600) { - U1BAUD= 59; /* 9600 */ - U1GCR = 8; /*LSB first and 9600*/ - } - -#ifdef UART1_RTSCTS - U1UCR = 0x42; /*defaults: 8N1, RTS/CTS, high stop bit*/ -#else - U1UCR = 0x02; /*defaults: 8N1, no flow control, high stop bit*/ -#endif - - U1CSR = U_MODE | U_RE | U_TXB; /*UART mode, receiver enable, TX done*/ - - /*set priority group of group 3 to highest, so the UART won't miss bytes*/ - IP1 |= IP1_3; - IP0 |= IP0_3; - - IEN0_URX1IE = 1; /* Enable the RX interrupt */ -} -/*---------------------------------------------------------------------------*/ diff --git a/cpu/cc2430/dev/uart_intr.c b/cpu/cc2430/dev/uart_intr.c index c0f8a7f5f..27803f2a6 100644 --- a/cpu/cc2430/dev/uart_intr.c +++ b/cpu/cc2430/dev/uart_intr.c @@ -16,10 +16,18 @@ #include "cc2430_sfr.h" #include "dev/leds.h" -#include "dev/uart.h" +#include "dev/uart0.h" +#include "dev/uart1.h" +#include "sys/energest.h" +#if UART_ZERO_ENABLE static int (*uart0_input_handler)(unsigned char c); +#endif +#if UART_ONE_ENABLE static int (*uart1_input_handler)(unsigned char c); +#endif + +#if UART_ZERO_ENABLE /*---------------------------------------------------------------------------*/ void uart0_set_input(int (*input)(unsigned char c)) @@ -29,18 +37,22 @@ uart0_set_input(int (*input)(unsigned char c)) /*---------------------------------------------------------------------------*/ void -uart0_rxISR(void) __interrupt (URX0_VECTOR) +uart0_rx_ISR(void) __interrupt (URX0_VECTOR) { + ENERGEST_ON(ENERGEST_TYPE_IRQ); TCON_URX0IF = 0; if(uart0_input_handler != NULL) { uart0_input_handler(U0BUF); } + ENERGEST_OFF(ENERGEST_TYPE_IRQ); } /*---------------------------------------------------------------------------*/ void -uart0_txISR( void ) __interrupt (UTX0_VECTOR) +uart0_tx_ISR( void ) __interrupt (UTX0_VECTOR) { } +#endif /* UART_ZERO_ENABLE */ +#if UART_ONE_ENABLE /*---------------------------------------------------------------------------*/ void uart1_set_input(int (*input)(unsigned char c)) @@ -48,17 +60,22 @@ uart1_set_input(int (*input)(unsigned char c)) uart1_input_handler = input; } /*---------------------------------------------------------------------------*/ +#if UART_ONE_CONF_WITH_INPUT void -uart1_rxISR(void) __interrupt (URX1_VECTOR) +uart1_rx_ISR(void) __interrupt (URX1_VECTOR) { + ENERGEST_ON(ENERGEST_TYPE_IRQ); TCON_URX1IF = 0; if(uart1_input_handler != NULL) { uart1_input_handler(U1BUF); } + ENERGEST_OFF(ENERGEST_TYPE_IRQ); } /*---------------------------------------------------------------------------*/ void -uart1_txISR( void ) __interrupt (UTX1_VECTOR) +uart1_tx_ISR( void ) __interrupt (UTX1_VECTOR) { } /*---------------------------------------------------------------------------*/ +#endif /* UART_ONE_CONF_WITH_INPUT */ +#endif /* UART_ONE_ENABLE */ diff --git a/cpu/cc2430/dev/watchdog-cc2430.c b/cpu/cc2430/dev/watchdog-cc2430.c new file mode 100644 index 000000000..2d59d2923 --- /dev/null +++ b/cpu/cc2430/dev/watchdog-cc2430.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Hardware-dependent functions used for the cc2430 watchdog timer. + * + * This file contains an ISR and must reside in the HOME bank. + * + * \author + * George Oikonomou - + */ + +#include "sys/energest.h" +#include "cc2430_sfr.h" +#include "contiki-conf.h" +#include "dev/watchdog-cc2430.h" + +/*---------------------------------------------------------------------------*/ +/* The watchdog only throws interrupts in timer mode */ +#if WDT_TIMER_MODE +void +cc4230_watchdog_ISR(void) __interrupt (WDT_VECTOR) +{ + EA = 0; + ENERGEST_ON(ENERGEST_TYPE_IRQ); + /* Do something */ + IRCON2 &= ~WDTIF; + ENERGEST_OFF(ENERGEST_TYPE_IRQ); + EA = 1; +} +#endif +/*---------------------------------------------------------------------------*/ +void +watchdog_init(void) +{ + WDCTL = WDT_TIMER_MODE | WDT_INTERVAL; + +#if WDT_TIMER_MODE + /* Enable the watchdog interrupts in timer mode */ + IEN2 |= WDTIE; +#endif + return; +} +/*---------------------------------------------------------------------------*/ +void +watchdog_start(void) +{ + WDCTL |= WDT_EN; +} +/*---------------------------------------------------------------------------*/ +void +watchdog_periodic(void) +{ +#if WDT_TIMER_MODE + /* In timer mode, all we need to do is write 1 to WDT:CLR[0] */ + WDCTL |= WDT_CLR0; +#else + /* Write the 'clear' sequence while maintaining mode and interval setting */ + WDCTL = (WDCTL & 0x0F) | WDT_CLR3 | WDT_CLR1; + WDCTL = (WDCTL & 0x0F) | WDT_CLR2 | WDT_CLR0; +#endif +} +/*---------------------------------------------------------------------------*/ +void +watchdog_stop(void) +{ +#if WDT_TIMER_MODE + /* In timer mode, the watchdog can actually be stopped */ + WDCTL &= ~WDT_EN; + IRCON2 &= ~WDTIF; +#else + /* In watchdog mode, stopping is impossible so we just reset the timer */ + watchdog_periodic(); +#endif +} +/*---------------------------------------------------------------------------*/ +void +watchdog_reboot(void) +{ +#if WDT_TIMER_MODE + /* Switch modes to watchdog, minimum interval, enable */ + WDCTL = WDT_EN | WDT_TIMEOUT_2_MSEC; +#else + /* Let's get this over with ASAP */ + WDCTL = WDT_TIMEOUT_2_MSEC; +#endif + /* Dis-acknowledge all interrupts while we wait for the dog to bark */ + DISABLE_INTERRUPTS(); + /* NOP till the dog barks... */ + while(1) { + __asm + nop + __endasm; + } +} diff --git a/cpu/cc2430/dev/watchdog-cc2430.h b/cpu/cc2430/dev/watchdog-cc2430.h new file mode 100644 index 000000000..8ea81fa2c --- /dev/null +++ b/cpu/cc2430/dev/watchdog-cc2430.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Hardware-dependent header file for the cc2430 watchdog timer. + * + * The interrupt service routine's prototype must be included by the + * file containing main(). + * + * \author + * George Oikonomou - + */ + +#ifndef WATCHDOG_CC2430_H_ +#define WATCHDOG_CC2430_H_ + +#include "dev/watchdog.h" +#include "cc2430_sfr.h" +#include "contiki-conf.h" + +#define WDT_TIMEOUT_1_SEC 0x00 +#define WDT_TIMEOUT_250_MSEC WDT_INT0 +#define WDT_TIMEOUT_15_MSEC WDT_INT1 +#define WDT_TIMEOUT_2_MSEC WDT_INT1 | WDT_INT0 + +#if WDT_CONF_TIMER_MODE +#define WDT_TIMER_MODE WDT_MODE /* Timer */ +#else +#define WDT_TIMER_MODE 0x00 /* Watchdog */ +#endif + +#ifdef WDT_CONF_INTERVAL +#define WDT_INTERVAL WDT_CONF_INTERVAL +#else +#define WDT_INTERVAL WDT_TIMEOUT_1_SEC /* 2 secs */ +#endif + +/* The watchdog only throws interrupts in timer mode */ +#if WDT_TIMER_MODE +void cc4230_watchdog_ISR(void) __interrupt (WDT_VECTOR); +#endif + +#endif /* WATCHDOG_CC2430_H_ */ diff --git a/cpu/cc2430/io.h b/cpu/cc2430/io.h deleted file mode 100644 index c14ed21f0..000000000 --- a/cpu/cc2430/io.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __IO_H__ -#define __IO_H__ - -#include - -#ifndef BV -#define BV(x) (1<<(x)) -#endif - -#endif /* __IO_H__ */ diff --git a/cpu/cc2430/mtarch.c b/cpu/cc2430/mtarch.c deleted file mode 100644 index 50f9959dc..000000000 --- a/cpu/cc2430/mtarch.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2007, Takahide Matsutsuka. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. - * - */ - /* - * \file - * Z80 machine-specific implementation for supporting multithread. - * \author - * Takahide Matsutsuka - */ -#include "sys/mt.h" -#include "mtarch.h" - -/*--------------------------------------------------------------------------*/ -void -mtarch_init(void) -{ -} -/*--------------------------------------------------------------------------*/ -void -mtarch_start(struct mtarch_thread *t, - void (*function)(void *), void *data) -{ - uint16_t i; - - for(i = 0; i < MTARCH_STACKSIZE; i++) { - t->stack[i] = i; - } - - t->sp = &t->stack[MTARCH_STACKSIZE - 1]; - - - /* A parameter for method for thread function. */ - *t->sp = (uint16_t)data; - --t->sp; - - /* This will be a return address of thread function. */ - *t->sp = (uint16_t)mt_exit; - --t->sp; - - /* The thread function, is used as a return address of mtarch_switch. */ - *t->sp = (uint16_t)function; - --t->sp; - - /* - * Space for registers. - * af, bc, de, hl, ix, iy, af', bc', de', hl' - */ - /* - * Z80 stack basis: - * push stores the data AFTER decrementing sp. - * pop reads the data BEFORE incrementing sp. - */ - - t->sp = t->sp - 9; -} -/*--------------------------------------------------------------------------*/ -static struct mtarch_thread *running_thread; -static uint16_t *sptmp; -static void -mtarch_switch() -{ - __asm - di ; disable interrupt - ; normal registers - push af - push bc - push de - push hl - push ix - push iy - - ; back registers - ex af,af' - push af - exx - push bc - push de - push hl - - ; swap between running_thread->sp and SP reg - ; _running_thread in asembler below points running_thread->sp - ; sptmp = sp; - ld (_sptmp),sp - - ; sp = *(running_thread->sp); - ld ix,(_running_thread) - ld l,0(ix) - ld h,1(ix) - ld sp,hl - - ; running_thread->sp = sptmp; - ld hl,(_sptmp) - ld 0(ix),l - ld 1(ix),h - - ; back registers - pop hl - pop de - pop bc - exx - pop af - ex af,af' - - ; normal registers - pop iy - pop ix - pop hl - pop de - pop bc - pop af - ei ; enable interrupt - __endasm; - // here sp indicates the address that point the function -} -/*--------------------------------------------------------------------------*/ -void -mtarch_exec(struct mtarch_thread *t) -{ - running_thread = t; - mtarch_switch(); - running_thread = NULL; -} -/*--------------------------------------------------------------------------*/ -void -mtarch_remove() -{ -} -/*--------------------------------------------------------------------------*/ -void -mtarch_yield() -{ - if (running_thread == NULL) { - /* ERROR! we have no runnning thread. */ - return; - } - mtarch_switch(); -} -/*--------------------------------------------------------------------------*/ -void mtarch_stop(struct mtarch_thread *thread) -{ -} -/*--------------------------------------------------------------------------*/ -void -mtarch_pstop() -{ -} -/*--------------------------------------------------------------------------*/ -void -mtarch_pstart() -{ -} -/*--------------------------------------------------------------------------*/ -int -mtarch_stack_usage(struct mtarch_thread *t) -{ - uint16_t i; - for (i = 0; i < MTARCH_STACKSIZE; i++) { - if (t->stack[i] != i) { - return MTARCH_STACKSIZE - i; - } - } - - return 0; -} -/*--------------------------------------------------------------------------*/ diff --git a/cpu/cc2430/mtarch.h b/cpu/cc2430/mtarch.h index f5d2b80b2..b228150ab 100644 --- a/cpu/cc2430/mtarch.h +++ b/cpu/cc2430/mtarch.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Takahide Matsutsuka. + * Copyright (c) 2010, Loughborough University - Computer Science * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,38 +28,23 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + /* * \file - * Z80 machine-specific difinitions for supporting multithread. - * + * Stub header file for cc2430 multi-threading. It doesn't do anything, it + * just exists so that mt.c can compile cleanly. + * + * This is based on the original mtarch.h for z80 by Takahide Matsutsuka + * * \author - * Takahide Matsutsuka + * George Oikonomou - */ #ifndef __MTARCH_H__ #define __MTARCH_H__ -/* Unit of the stack is 2byte wide. */ -#ifndef MTARCH_STACKSIZE -#define MTARCH_STACKSIZE 128 -#endif /* MTARCH_STACKSIZE */ - struct mtarch_thread { - /* - * On top of the mtarch_thread must be the address for the stack pointer. - * See details at mtarch_switch in mtarch.c - */ - uint16_t *sp; - /* - * Stack is 2-byte wide, so please note that you need 2 * MTARCH_STACKSIZE - * bytes for the stack area for each thread. - */ - uint16_t stack[MTARCH_STACKSIZE]; + unsigned char *sp; }; -/* - * A function for debugging purpose, placed here by following other implementations. - */ -int mtarch_stack_usage(struct mtarch_thread *t); - #endif /* __MTARCH_H__ */ diff --git a/cpu/cc2430/rtimer-arch.c b/cpu/cc2430/rtimer-arch.c new file mode 100644 index 000000000..517886037 --- /dev/null +++ b/cpu/cc2430/rtimer-arch.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +/** + * \file + * Hardware-dependent functions used to support the + * contiki rtimer module. + * + * clock and etimer are using the sleep timer on the cc2430 + * + * clock_init() has set our tick speed prescaler already, so we + * are ticking with 500 kHz freq. + * + * rtimer_clock_t is unsigned short (16bit on the cc2430) + * It thus makes sense to use the 16bit clock (Timer 1) + * + * This file contains an ISR and must reside in the HOME bank + * + * \author + * George Oikonomou - + */ + +#include "sys/rtimer.h" /* Includes rtimer-arch.h for us */ +#include "cc2430_sfr.h" +#include "sys/energest.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +/*---------------------------------------------------------------------------*/ +void +rtimer_arch_init(void) +{ + PRINTF("rtimer_arch_init() "); + /* + * - Free running mode + * - Prescale by 32: + * Tick Speed has been prescaled to 500 kHz already in clock_init() + * We further prescale by 32 resulting in 15625 Hz for this timer. + */ + T1CTL = (T1DIV1 | T1MODE0); /* 00001001 */ + PRINTF("T1CTL=0x%02x\n", T1CTL); + /* Acknowledge Timer 1 Interrupts */ + IEN1_T1IE = 1; + PRINTF("IEN1_T1IE=0x%02x\n", IEN1_T1IE); + + /* Timer 1, Channel 1. Compare Mode (0x04), Interrupt mask on (0x40) */ + T1CCTL1 = T1MODE + T1IM; + PRINTF("T1CCTL1=0x%02x\n", T1CCTL1); + + /* Interrupt Mask Flags: No interrupt on overflow */ + TIMIF &= ~OVFIM; + PRINTF("TIMIF=0x%02x\n", TIMIF); + + PRINTF("done\n"); +} +/*---------------------------------------------------------------------------*/ +void +rtimer_arch_schedule(rtimer_clock_t t) +{ + PRINTF("rtimer_arch_schedule(%u)\n", t); + + /* set the compare mode values so we can get an interrupt after t */ + T1CC1L = (unsigned char) t; + T1CC1H = (unsigned char) (t >> 8); + PRINTF("T1CC1=%u, t=%u\n", (T1CC1L + (T1CC1H << 8)), t); + + /* Turn on compare mode interrupt */ + PRINTF("T1CTL=0x%02x\n", T1CTL); + T1CCTL1 |= T1IM; +} +/*---------------------------------------------------------------------------*/ +void +cc2430_timer_1_ISR(void) __interrupt (T1_VECTOR) +{ + IEN1_T1IE = 0; /* Ignore Timer 1 Interrupts */ + ENERGEST_ON(ENERGEST_TYPE_IRQ); + /* No more interrupts from Channel 1 till next rtimer_arch_schedule() call. + * Setting the mask will instantly generate an interrupt so we clear the + * flag first. */ + T1CTL &= ~(CH1IF); + T1CCTL1 &= ~T1IM; + + rtimer_run_next(); + + ENERGEST_OFF(ENERGEST_TYPE_IRQ); + IEN1_T1IE = 1; /* Acknowledge Timer 1 Interrupts */ +} diff --git a/cpu/cc2430/rtimer-arch.h b/cpu/cc2430/rtimer-arch.h index be2aec319..89579b37a 100644 --- a/cpu/cc2430/rtimer-arch.h +++ b/cpu/cc2430/rtimer-arch.h @@ -33,18 +33,31 @@ /** * \file - * A brief description of what this file is. + * Hardware-dependent function declarations used to + * support the contiki rtimer module. + * + * We use Timer 1 on the cc2431. + * * \author - * Adam Dunkels + * Zach Shelby (Original) + * George Oikonomou - + * (rtimer-arch implementation for cc2430) */ #ifndef __RTIMER_ARCH_H__ #define __RTIMER_ARCH_H__ #include "contiki-conf.h" +#include "cc2430_sfr.h" -#define RTIMER_ARCH_SECOND 1000 +/* + * 32 MHz clock, prescaled down to 500 kHz for all 4 timers in clock_init(). + * Further prescaled factor 32 for T1, thus T1 is 15625 Hz + */ +#define RTIMER_ARCH_SECOND (15625U) -#define rtimer_arch_now() clock_time() +#define rtimer_arch_now() ((rtimer_clock_t)(T1CNTL + (T1CNTH << 8))) + +void cc2430_timer_1_ISR(void) __interrupt (T1_VECTOR); #endif /* __RTIMER_ARCH_H__ */ diff --git a/cpu/cc2430/segment.rules b/cpu/cc2430/segment.rules index 1534b9e26..3b4ffbdfa 100644 --- a/cpu/cc2430/segment.rules +++ b/cpu/cc2430/segment.rules @@ -1,25 +1,26 @@ -# segment.rules file assigns source code modules to specific banks -# segment.rules is constructed from any segment.rules found in the search path -# the search path is defined in Makefile.cc2430 -# segment.rules get processed by a perl snippet listed in the Makefile.cc2430 -# the processed output is put into the obj_* directory +# segment.rules files assign source code modules to specific banks +# These files are only used when we build code with banking (HAVE_BANKING=1) +# The final segment.rules file is constructed from any segment.rules found in +# the search path, defined in Makefile.cc2430 +# When building bankable code, the bank-alloc.py script automatically allocates +# modules to banks. segment.rules files provide hints, instructing the script +# as to which files are safe to move around and which files to leave alone +# In other words, only specify a rule for a file if you need to # comments starting with "#" are supported -# perl regular expression matching can be used on the file name specification -# -# general rules -- -# some code must be placed in all banks (or SDCC/aslink will complain at link time) -# code called from function pointers must be in HOME/CSEG/BANK0 -# interrupt code must also be in HOME/CSEG/BANK0 -# code not marked with __banked can only be called from code in the same bank +# The file spec in rules is actually interpreted as a python regex so you can +# write a rule that will match multiple files # -HOME intr.c # All interrupt code must live in HOME/BANK0 -HOME bus.c # flash_read cannot be banked. -HOME clock.c # cannot bank clock.c code because header file is part of core. -HOME uart.c -BANK3 uart_init.c -BANK2 dma.c -CSEG cc2430/dev/bus.c -CSEG autostart.c -BANK2 cc2430/dev/cc2430_rf.c -BANK1 cc2430/ -BANK1 . +# general rules -- +# This file is only used when the Makefile defines HAVE_BANKING=1 +# SDCC's standard libraries will always go in CSEG - We don't touch them +# Interrupt code must be in HOME. Specify all files with an ISR here +# All files without an associated rule get allocated to a bank automatically + +# Files with ISRs must be in HOME +HOME intr.c # Match all files ending in intr.c (e.g. uart_intr.c) +HOME rtimer-arch.c +HOME watchdog-cc2430.c +HOME clock.c + +# Some cc2430 files which need special treatment +HOME bus.c # bus.c::flash_read() must be run from HOME (if compiled in) diff --git a/cpu/cc2430/segment.rules.pl b/cpu/cc2430/segment.rules.pl deleted file mode 100644 index 213dd8fa2..000000000 --- a/cpu/cc2430/segment.rules.pl +++ /dev/null @@ -1,11 +0,0 @@ - $match_me = pop @ARGV; - #print "searching for $match_me\n"; - while (<>) { - s/#.*$//; # filter out comments - s/^\s*//; # filter out leading white space - @F=split /\s+/; # split on white space - if (($_ =~ m/\S\s+\S/) && ($match_me =~ m/$F[1]/)) { - print $F[0]."\n"; - exit; #return only first match - } - } diff --git a/cpu/cc2430/uip_arch-asm.S b/cpu/cc2430/uip_arch-asm.S deleted file mode 100644 index e64ddf45a..000000000 --- a/cpu/cc2430/uip_arch-asm.S +++ /dev/null @@ -1,221 +0,0 @@ -;;; -;;; -;;; uip_arch-asm.S -;;; -;;; \file -;;; Z80 architecture-depend uip module -;;; for calculating checksums -;;; -;;; \author -;;; Takahide Matsutsuka -;;; - .module uip_arch-asm - - ;; export symbols - .globl _uip_add32 - .globl _uip_arch_chksum - .globl _uip_chksum - - ;; import symbols - .globl _uip_acc32 - .globl _uip_buf - - .area _DATA - - .area _GSINIT - - .area _CODE - - ;; --------------------------------- - ;; void uip_add32(uint8_t *op32, uint16_t op16); - ;; Stack; retl reth op32l op32h op16l op16h - ;; ABCDEHL____ - ;; return void - ;; _uip_acc32 = op32 + op16 - ;; --------------------------------- -_uip_add32_start:: -_uip_add32: - ;; HL = #_op32l - ld hl, #2 - add hl, sp - - ;; DE = #(_op32) - ld e, (hl) - inc hl - ld d, (hl) - inc hl - - ;; BC = op16 - ld c, (hl) - inc hl - ld b, (hl) - - ;; HL = #(_op32) + 3 - ld hl, #3 - add hl, de - - ;; DE = #_uip_acc32 + 3 - ld de, #_uip_acc32 + 3 - - ;; uip_acc32[3] = op32[3] + op16l; - ld a, (hl) - add a, c - ld (de), a - - ;; uip_acc32[2] = op32[2] + op16h + carry; - dec hl - dec de - ld a, (hl) - adc a, b - ld (de), a - jr nc, _uip_add32_nocarry1 - - ;; uip_acc32[1] - dec hl - dec de - ld a, (hl) - inc a - ld (de), a - jr nz, _uip_add32_nocarry0 - - ;; uip_acc32[0] - dec hl - dec de - ld a, (hl) - inc a - ld (de), a - ret -_uip_add32_nocarry1: - ;; uip_acc32[1] - dec hl - dec de - ld a, (hl) - ld (de), a - -_uip_add32_nocarry0: - ;; uip_acc32[0] - dec hl - dec de - ld a, (hl) - ld (de), a - ret -_uip_add32_end:: - - ;; --------------------------------- - ;; static uint16_t chksum(uint16_t sum, const uint8_t *data, uint16_t len) - ;; Stack; retl reth suml sumh datal datah lenl lenh - ;; ABCDEHL____ - ;; return HL - ;; --------------------------------- -_uip_arch_chksum_start:: -_uip_arch_chksum: - push ix - ;; IX = #_suml - ld ix, #4 - add ix, sp - ;; BC = sum - ld c, 0(ix) - ld b, 1(ix) - ;; DE = #data - ld e, 2(ix) - ld d, 3(ix) - - ;; (lenl, lenh) <- dataptr + len - 1 (last address) - ;; (len) + DE - 1 -> (len) - ld l, 4(ix) - ld h, 5(ix) - add hl, de - dec hl - ld 4(ix), l - ld 5(ix), h - -_uip_arch_chksum_loop: - ;; compare HL(last address) and DE(dataptr) - ;; HL - DE - ;; if (HL < DE) C,NZ else if (HL = DE) NC,Z=1 otherwise NC,NZ - ;; HL = last address, DE = current pointer - ld l, 4(ix) - ld h, 5(ix) - - ld a, h - sub d - jr nz, _uip_arch_chksum_compared - ld a, l - sub e - ;; if (last address == dataptr) _uip_arch_chksum_loop_exit_add_trailing - jr z, _uip_arch_chksum_loop_exit_add_trailing -_uip_arch_chksum_compared: - ;; if (last address > dataptr) _uip_arch_chksum_loop_exit - jr c, _uip_arch_chksum_loop_exit - ;; bc = dataptr[0],dataptr[1] + bc - ld a, (de) - ld h, a - inc de - ld a, (de) - ld l, a - push hl - add hl, bc - inc de - ld b, h - ld c, l - ;; HL = t - pop hl - ;; BC - HL - ;; if (sumBC < tHL) sum++ - ld a, b - sub h - jr nz, _uip_arch_chksum_compared_t - ld a, c - sub l -_uip_arch_chksum_compared_t: - jr nc, _uip_arch_chksum_nocarry_t - inc bc -_uip_arch_chksum_nocarry_t: - jr _uip_arch_chksum_loop -_uip_arch_chksum_loop_exit_add_trailing: - ;; HL = last address - ;; bc = bc + (last address)<<8 - ld a, b - add a, (hl) - ld b, a - jr nc, _uip_arch_chksum_loop_exit - inc bc -_uip_arch_chksum_loop_exit: - ld l, c - ld h, b - pop ix - ret -_uip_arch_chksum_end:: - - ;; --------------------------------- - ;; uint16_t uip_chksum(void); - ;; Stack; retl reth datal datah lenl lenh - ;; ABCDEHL____ - ;; return HL - ;; return htons(chksum(0, (uint8_t *)data, len)); - ;; --------------------------------- -_uip_chksum_start:: -_uip_chksum: - ld hl, #5 - add hl, sp - ;; HL indicates #_lenh - ld b, #2 -_uip_chksum_loop: - ld d, (hl) - dec hl - ld e, (hl) - dec hl - push de - djnz _uip_chksum_loop - ld bc, #0 - push bc - call _uip_arch_chksum - pop af - pop af - pop af - ;; convert to BIG ENDIAN (htons) - ld a, l - ld l, h - ld h, a - ret -_uip_chksum_end:: diff --git a/cpu/cc2430/uip_arch.c b/cpu/cc2430/uip_arch.c deleted file mode 100644 index afa6b601e..000000000 --- a/cpu/cc2430/uip_arch.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2007, Takahide Matsutsuka. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. - * - * $Id: uip_arch.c,v 1.2 2010/10/19 18:29:04 adamdunkels Exp $ - * - */ - /* - * \file - * Z80 architecture-depend uip module - * for calculating checksums - * \author - * Takahide Matsutsuka - */ - -#include -#include "uip_arch.h" - -static const uint16_t sizeof_uip_ipaddr_t = sizeof(uip_ipaddr_t); -static const uint16_t offset_tcpip_hdr_len = offsetof(struct uip_tcpip_hdr, len); -static const uint16_t offset_tcpip_hdr_srcipaddr = offsetof(struct uip_tcpip_hdr, srcipaddr); - -/*--------------------------------------------------------------------------*/ -static void upper_layer_chksum() { -__asm - ;; --------------------------------- - ;; static uint16_t upper_layer_chksum(uint8_t proto); - ;; Stack; retl reth - ;; @param C proto - ;; ABCDEHL____ - ;; --------------------------------- - ;; HL = BUF = &uip_buf[UIP_LLH_LEN] - ld hl, #_uip_buf - ld de, #UIP_LLH_LEN - add hl, de - push hl - - ;; HL = BUF->len[0] - push ix - ld ix, #_offset_tcpip_hdr_len - ld e, 0(ix) - ld d, 1(ix) - add hl, de - pop ix - - ;; DE = upper layer length - ld d, (hl) - inc hl - ld e, (hl) -#if UIP_CONF_IPV6 -#else - ld a, e - sub a, #UIP_IPH_LEN - ld e, a - jr nc, _upper_layer_chksum_setlen2 - dec d -_upper_layer_chksum_setlen2: -#endif - ;; bc = upper_leyer_len + proto - ld b, d - ld a, e - add a, c - ld c, a - jr nc, _upper_layer_chksum_setlen3 - inc b -_upper_layer_chksum_setlen3: - pop hl ; BUF - push de - push ix - ld ix, #_offset_tcpip_hdr_srcipaddr - ld e, 0(ix) - ld d, 1(ix) - add hl, de - ld e, l - ld d, h - ld ix, #_sizeof_uip_ipaddr_t - ld l, 0(ix) - ld h, 1(ix) - pop ix - sla l - rl h - push hl - push de - push bc - call _uip_arch_chksum ; hl = sum - pop af - pop af - pop af - ;; de is still stacked - - ld b, h - ld c, l - ld hl, #_uip_buf - ld de, #UIP_IPH_LEN - add hl, de -_upper_layer_chksum_call: - ld de, #UIP_LLH_LEN - add hl, de - push hl - push bc - call _uip_arch_chksum - pop af - pop af - pop af - - ld a, h - or a, l - jr nz, _upper_layer_uip_htons - ld hl, #0xffff - jr _upper_layer_ret -_upper_layer_uip_htons: - ld a, l - ld l, h - ld h, a -_upper_layer_ret: -__endasm; -} - -/*--------------------------------------------------------------------------*/ -uint16_t -uip_ipchksum(void) -{ -__asm - ;; --------------------------------- - ;; uint16_t uip_ipchksum(void); - ;; Stack; retl reth - ;; ABCDEHL____ - ;; return HL - ;; --------------------------------- - ld hl, #UIP_IPH_LEN - push hl - ;; HL = BUF = &uip_buf[UIP_LLH_LEN] - ld hl, #_uip_buf - ;; BC = sum = 0 - ld bc, #0 - jp _upper_layer_chksum_call -__endasm; -} - -/*--------------------------------------------------------------------------*/ -#if UIP_CONF_IPV6 -uint16_t -uip_icmp6chksum(void) -{ -__asm - ;; --------------------------------- - ;; uint16_t uip_icmp6chksum(void); - ;; Stack; retl reth - ;; ABCDEHL____ - ;; return HL - ;; --------------------------------- - ld c, #UIP_PROTO_ICMP6 - jp _upper_layer_chksum -__endasm; -} -#endif /* UIP_CONF_IPV6 */ - -/*--------------------------------------------------------------------------*/ -uint16_t -uip_tcpchksum(void) -{ -__asm - ;; --------------------------------- - ;; uint16_t uip_tcpchksum(void); - ;; Stack; retl reth - ;; ABCDEHL____ - ;; return HL - ;; --------------------------------- - ld c, #UIP_PROTO_TCP - jp _upper_layer_chksum -__endasm; -} - -/*--------------------------------------------------------------------------*/ -#if UIP_UDP_CHKSUMS -uint16_t -uip_udpchksum(void) -{ -__asm - ;; --------------------------------- - ;; uint16_t uip_udpchksum(void); - ;; Stack; retl reth - ;; ABCDEHL____ - ;; return HL - ;; --------------------------------- - ld c, #UIP_PROTO_UDP - jp _upper_layer_chksum -__endasm; -} -#endif /* UIP_UDP_CHKSUMS */ -/*--------------------------------------------------------------------------*/ diff --git a/examples/sensinode/Makefile b/examples/sensinode/Makefile index ef9bc6965..c3b33f6e4 100644 --- a/examples/sensinode/Makefile +++ b/examples/sensinode/Makefile @@ -2,7 +2,17 @@ ifndef TARGET TARGET=sensinode endif +# Make absolutely certain that you specify your device here +DEFINES+=MODEL_N740 + +# These examples don't need code banking so we turn it off +#HAVE_BANKING=1 + CONTIKI_PROJECT = hello_world clock_test rf_test_rx rf_test_tx + +# New examples added by George Oikonomou - Loughborough University +CONTIKI_PROJECT += timer-test blink-hello broadcast-rime + all: $(CONTIKI_PROJECT) CONTIKI = ../.. diff --git a/examples/sensinode/README b/examples/sensinode/README index a9bac42e1..04901ca45 100644 --- a/examples/sensinode/README +++ b/examples/sensinode/README @@ -1,6 +1,11 @@ Sensinode platform example and test applications - by Zach Shelby (zach@sensinode.com) +Some more examples by George Oikonomou - Loughborough University + cc2431-location-engine, udp-ipv6, broadcast-rime + blink-hello, event-post, timer-test + + This directory contains example and test applications for Sensinode CC2430 based devices. By default it is set to use the sensinode platform: @@ -32,8 +37,21 @@ These make options are defined in /platform/sensinode/Makefile.sensinode Descriptions of applications: -hello_world A simple hello world app. -clock_test Test and example of sys/clock.h related features. -rf_test_tx Test for transmitting packets -rf_test_rc Test for receiving packets - \ No newline at end of file +hello_world A simple hello world app. +clock_test Test and example of sys/clock.h related features. +rf_test_tx Test for transmitting packets +rf_test_rc Test for receiving packets + +Recent Additions: +udp-ipv6 UDP client-server example over uIPv6. Uses link-local and global + addresses. Button 1 on the client will send an echo request. +broadcast-rime Just a broadcast rime example, slightly modified +sensors Demonstrating button and ADC functionality +cc2431-location-engine + Example demonstrating the usage cc2431 location engine (blind node) + N.B. Not all sensinode devides have a cc2431 +event-post Demonstrating the interaction between two processes with custom events +blink-hello Hello World with LED blinking. +timer-test Same as clock_test above + testing the rtimer-arch code +border-router 802.15.4 to SLIP bridge example. The node will forward packets + from the 15.4 network to its UART (and thus a connected PC over SLIP) \ No newline at end of file diff --git a/examples/sensinode/blink-hello.c b/examples/sensinode/blink-hello.c new file mode 100644 index 000000000..593589041 --- /dev/null +++ b/examples/sensinode/blink-hello.c @@ -0,0 +1,91 @@ +/* This is a very simple hello_world program. + * It aims to demonstrate the co-existence of two processes: + * One of them prints a hello world message and the other blinks the LEDs + * + * It is largely based on hello_world in $(CONTIKI)/examples/sensinode + * + * Author: George Oikonomou + */ + +#include "contiki.h" +#include "dev/leds.h" + +#include /* For printf() */ +/*---------------------------------------------------------------------------*/ +/* We declare the two processes */ +PROCESS(hello_world_process, "Hello world process"); +PROCESS(blink_process, "LED blink process"); + +/* We require the processes to be started automatically */ +AUTOSTART_PROCESSES(&hello_world_process, &blink_process); +/*---------------------------------------------------------------------------*/ +/* Implementation of the first process */ +PROCESS_THREAD(hello_world_process, ev, data) +{ + /* variables are declared static to ensure their values are maintained + between subsequent calls. + All the code between PROCESS_THREAD and PROCESS_BEGIN() runs each time + the process is invoked. */ + static struct etimer timer; + static int count; + + /* any process must start with this. */ + PROCESS_BEGIN(); + + /* set the etimer module to generate an event in one second. + CLOCK_CONF_SECOND is #define as 128 */ + etimer_set(&timer, CLOCK_CONF_SECOND * 4); + count = 0; + /* Don't declare variables after PROCESS_BEGIN. + * While it will compile fine with TARGET=native (gcc is happy), + * SDCC doesn't like it. Soon as you try TARGET=sensinode you will get: + * syntax error: token -> 'int' ; + * Try uncommenting the line below and observe the results */ + /* int whoops = 0; + * whoops = 0; */ + while (1) + { + /* wait here for an event to happen */ + PROCESS_WAIT_EVENT(); + /* This achieves the same + * PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER); */ + + /* if the event is the timer event as expected... */ + if(ev == PROCESS_EVENT_TIMER) + { + /* do the process work */ + printf("Sensor says no... #%d\r\n", count); + count ++; + + /* reset the timer so it will generate an other event + * the exact same time after it expired (periodicity guaranteed) */ + etimer_reset(&timer); + } + + /* and loop */ + } + /* any process must end with this, even if it is never reached. */ + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/* Implementation of the second process */ +PROCESS_THREAD(blink_process, ev, data) +{ + static struct etimer timer; + PROCESS_BEGIN(); + + while (1) + { + /* we set the timer from here every time */ + etimer_set(&timer, CLOCK_CONF_SECOND); + + /* and wait until the event we receive is the one we're waiting for */ + PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER); + + printf("Blink... (state %0.2X).\r\n", leds_get()); + /* update the LEDs */ + leds_toggle(LEDS_GREEN); + } + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/border-router/Makefile b/examples/sensinode/border-router/Makefile new file mode 100644 index 000000000..b77479824 --- /dev/null +++ b/examples/sensinode/border-router/Makefile @@ -0,0 +1,19 @@ +ifndef TARGET +TARGET=sensinode +endif + +# Make absolutely certain that you specify your device here +DEFINES+=MODEL_N601,PROJECT_CONF_H + +# We need uIPv6, therefore we also need banking +HAVE_BANKING=1 +UIP_CONF_IPV6=1 + +PROJECT_SOURCEFILES += slip-bridge.c + +CONTIKI_PROJECT = border-router +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/sensinode/border-router/README b/examples/sensinode/border-router/README new file mode 100644 index 000000000..060c48ea5 --- /dev/null +++ b/examples/sensinode/border-router/README @@ -0,0 +1,16 @@ +border-router example for sensinode devices. + +This example is meant to be used with tunslip6 in tools/ + +- Build the code and load it onto your node +- Connect your node to your PC over USB +- run: + sudo ./tunslip6

/ + + This will setup tun0 on your PC over device /dev/ttyUSBx. The address + argument should contain the v6 address that you want to assign to tun0 + The node will use this address to obtain the network prefix + + for example: + sudo ./tunslip6 aaaa::1/64 + This will use aaaa:: / 64 as the prefix for the 15.4 network. diff --git a/examples/sensinode/border-router/border-router.c b/examples/sensinode/border-router/border-router.c new file mode 100644 index 000000000..c9a616aac --- /dev/null +++ b/examples/sensinode/border-router/border-router.c @@ -0,0 +1,134 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" + +#include + +#define DEBUG DEBUG_PRINT +#include "net/uip-debug.h" +#include "net/rpl/rpl.h" +#include "dev/watchdog.h" +#include "dev/slip.h" +#include "dev/leds.h" + +#ifndef CC2430_RF_CONF_CHANNEL +#define CC2430_RF_CONF_CHANNEL 0xFF +#endif + +static uint8_t prefix_set; +/*---------------------------------------------------------------------------*/ +PROCESS(border_router_process, "Border Router process"); +AUTOSTART_PROCESSES(&border_router_process); +/*---------------------------------------------------------------------------*/ +static void +print_local_addresses(void) +{ + int i; + uint8_t state; + + PRINTF("Router's IPv6 addresses:\n"); + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + state = uip_ds6_if.addr_list[i].state; + if(uip_ds6_if.addr_list[i].isused && (state == ADDR_TENTATIVE || state + == ADDR_PREFERRED)) { + PRINTF(" "); + PRINT6ADDR(&uip_ds6_if.addr_list[i].ipaddr); + PRINTF("\n"); + if (state == ADDR_TENTATIVE) { + uip_ds6_if.addr_list[i].state = ADDR_PREFERRED; + } + } + } +} +/*---------------------------------------------------------------------------*/ +void +request_prefix(void) { + /* mess up uip_buf with a dirty request... */ + uip_buf[0] = '?'; + uip_buf[1] = 'P'; + uip_len = 2; + slip_send(); + uip_len = 0; +} +/*---------------------------------------------------------------------------*/ +/* Set our prefix when we receive one over SLIP */ +void +set_prefix_64(uip_ipaddr_t *prefix_64) { + rpl_dag_t *dag; + uip_ipaddr_t ipaddr; + memcpy(&ipaddr, prefix_64, 16); + prefix_set = 1; + uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr); + uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF); + + /* Become root of a new DODAG with ID our global v6 address */ + dag = rpl_set_root(RPL_DEFAULT_INSTANCE, &ipaddr); + if(dag != NULL) { + rpl_set_prefix(dag, &ipaddr, 64); + PRINTF("Created a new RPL dag with ID: "); + PRINT6ADDR(&dag->dag_id); + PRINTF("\n"); + } +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(border_router_process, ev, data) +{ + static struct etimer et; + + PROCESS_BEGIN(); + PRINTF("Border Router started\n"); + prefix_set = 0; + + leds_on(LEDS_RED); + + /* Request prefix until it has been received */ + while(!prefix_set) { + leds_on(LEDS_GREEN); + PRINTF("Prefix request.\n"); + etimer_set(&et, CLOCK_SECOND); + request_prefix(); + leds_off(LEDS_GREEN); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + } + + /* We have created a new DODAG when we reach here */ + PRINTF("On Channel %u\n", CC2430_RF_CONF_CHANNEL); + + print_local_addresses(); + + leds_off(LEDS_RED); + + PROCESS_EXIT(); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/border-router/project-conf.h b/examples/sensinode/border-router/project-conf.h new file mode 100644 index 000000000..f0fdb71fd --- /dev/null +++ b/examples/sensinode/border-router/project-conf.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Project specific configuration defines for the border router / + * slip bridge example. + * + * We make sure that SLIP is turned on + * + * \author + * George Oikonomou - + */ + +#ifndef PROJECT_CONF_H_ +#define PROJECT_CONF_H_ + +#define VIZTOOL_MAX_PAYLOAD_LEN 120 +#define SLIP_ARCH_CONF_ENABLE 1 +#define LPM_CONF_MODE 0 + +#endif /* PROJECT_CONF_H_ */ diff --git a/examples/sensinode/border-router/slip-bridge.c b/examples/sensinode/border-router/slip-bridge.c new file mode 100644 index 000000000..82ec40920 --- /dev/null +++ b/examples/sensinode/border-router/slip-bridge.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2010, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * $Id: slip-bridge.c,v 1.3 2010/06/08 19:53:49 nifi Exp $ + */ + +/** + * \file + * Slip fallback interface + * \author + * Niclas Finne + * Joakim Eriksson + * Joel Hoglund + * Nicolas Tsiftes + */ + +#include "net/uip.h" +#include "net/uip-ds6.h" +#include "net/rpl/rpl.h" +#include "dev/slip.h" +#include "dev/uart1.h" +#include + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) + +#define DEBUG DEBUG_NONE +#include "net/uip-debug.h" + +void set_prefix_64(uip_ipaddr_t *); + +static uip_ipaddr_t last_sender; +/*---------------------------------------------------------------------------*/ +static void +slip_input_callback(void) +{ + PRINTF("SIN: %u\n", uip_len); + if((char) uip_buf[0] == '!') { + PRINTF("Got configuration message of type %c\n", uip_buf[1]); + uip_len = 0; + if((char)uip_buf[1] == 'P') { + uip_ipaddr_t prefix; + /* Here we set a prefix !!! */ + memset(&prefix, 0, 16); + memcpy(&prefix, &uip_buf[2], 8); + PRINTF("Setting prefix "); + PRINT6ADDR(&prefix); + PRINTF("\n"); + set_prefix_64(&prefix); + } + } + /* Save the last sender received over SLIP to avoid bouncing the + packet back if no route is found */ + uip_ipaddr_copy(&last_sender, &UIP_IP_BUF->srcipaddr); +} +/*---------------------------------------------------------------------------*/ +static void +init(void) +{ + process_start(&slip_process, NULL); + slip_set_input_callback(slip_input_callback); +} +/*---------------------------------------------------------------------------*/ +static void +output(void) +{ + if(uip_ipaddr_cmp(&last_sender, &UIP_IP_BUF->srcipaddr)) { + /* Do not bounce packets back over SLIP if the packet was received + over SLIP */ + PRINTF("slip-bridge: Destination off-link but no route\n"); + } else { + PRINTF("SUT: %u\n", uip_len); + slip_send(); + } +} +/*---------------------------------------------------------------------------*/ +const struct uip_fallback_interface slip_interface = { + init, output +}; +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/broadcast-rime.c b/examples/sensinode/broadcast-rime.c new file mode 100644 index 000000000..adcb479eb --- /dev/null +++ b/examples/sensinode/broadcast-rime.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2007, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + * $Id: example-broadcast.c,v 1.2 2009/11/19 17:29:41 nifi Exp $ + */ + +/** + * \file + * Testing the broadcast layer in Rime + * \author + * Adam Dunkels + */ + +#include "contiki.h" +#include "net/rime.h" +#include "lib/random.h" +#include "net/rime/rimestats.h" +#include "dev/leds.h" +#include "dev/models.h" + +#define DEBUG 1 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +#define BROADCAST_CHANNEL 129 + +/*---------------------------------------------------------------------------*/ +PROCESS(example_broadcast_process, "BROADCAST example"); +AUTOSTART_PROCESSES(&example_broadcast_process); +/*---------------------------------------------------------------------------*/ +static void broadcast_recv(struct broadcast_conn *c, const rimeaddr_t *from) +{ + leds_toggle(LEDS_RED); + PRINTF("broadcast message received from %02x.%02x\n", from->u8[0], from->u8[1]); + PRINTF("Size=0x%02x: '0x%04x'\n", packetbuf_datalen(), *(uint16_t *) packetbuf_dataptr()); +} + +static void print_rime_stats() +{ + PRINTF("\nNetwork Stats\n"); + PRINTF(" TX=%lu , RX=%lu\n", rimestats.tx, rimestats.rx); + PRINTF("LL-TX=%lu , LL-RX=%lu\n", rimestats.lltx, rimestats.llrx); + PRINTF(" Long=%lu , Short=%lu\n", rimestats.toolong, rimestats.tooshort); + PRINTF("T/Out=%lu , CCA-Err=%lu\n", rimestats.timedout, + rimestats.contentiondrop); +} + +static const struct broadcast_callbacks bc_rx = { broadcast_recv }; +static struct broadcast_conn broadcast; +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(example_broadcast_process, ev, data) +{ + static struct etimer et; + static uint16_t counter; + + PROCESS_EXITHANDLER(broadcast_close(&broadcast);) + + PROCESS_BEGIN(); + + PRINTF("Start\n"); + broadcast_open(&broadcast, BROADCAST_CHANNEL, &bc_rx); + PRINTF("Open Broadcast Connection, channel %u\n", BROADCAST_CHANNEL); + // leds_off(LEDS_ALL); + while(1) { + + /* Delay 2-4 seconds */ + etimer_set(&et, CLOCK_SECOND * 2); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + leds_on(LEDS_GREEN); + packetbuf_copyfrom(&counter, sizeof(counter)); + PRINTF("Sending %u bytes: 0x%04x\n", packetbuf_datalen(), *(uint16_t *) packetbuf_dataptr()); + if (broadcast_send(&broadcast) == 0) { + PRINTF("Error Sending\n"); + } + + print_rime_stats(); + PRINTF("===================================\n"); + counter++; + leds_off(LEDS_GREEN); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/cc2431-location-engine/Makefile b/examples/sensinode/cc2431-location-engine/Makefile new file mode 100644 index 000000000..8fc75d484 --- /dev/null +++ b/examples/sensinode/cc2431-location-engine/Makefile @@ -0,0 +1,17 @@ +ifndef TARGET +TARGET=sensinode +endif + +# Make absolutely certain that you specify your device here +DEFINES+=MODEL_N740 + +# This example doesn't need code banking so we turn it off +#HAVE_BANKING=1 + +CONTIKI_PROJECT = blind-node + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/sensinode/cc2431-location-engine/blind-node.c b/examples/sensinode/cc2431-location-engine/blind-node.c new file mode 100644 index 000000000..d50865522 --- /dev/null +++ b/examples/sensinode/cc2431-location-engine/blind-node.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example demonstrating the cc2431 location engine. + * + * This file contains code for the blind node. The blind node must be + * equipped with a cc2431 SoC (as opposed to reference nodes which + * don't need to have a Loc. Eng.) + * + * The blind node receives co-ordinates of reference nodes over + * broadcast rime. Once it has enough data (3+ reference nodes), it + * will calculate its own position. + * + * We calculate with all potential values for parameter 'n' to + * demonstrate how 'n' influences the result of the calculation. + * + * Optionally, send the result of the calculation to a collection node + * + * More information on the cc2431 Location Engine can be found in: + * - cc2431 Datasheet + * - Texas Instruments Application Note 42 + * + * \author + * George Oikonomou - + */ + +#include "contiki.h" +#include "net/rime.h" +#include "cc2431_loc_eng.h" +#include "cc2430_sfr.h" + +#include +#include + +#define MAX_REF_NODES 16 /* Do not change */ + +#define SAMPLE_RSSI 100 /* Used for testing */ +#define SAMPLE_ALPHA 101 + +static struct meas_params parameters; +static struct refcoords ref_coords[MAX_REF_NODES]; + +/* Store our current location here to be transmitted to a collector node */ +static uint8_t coords[2]; + +/*---------------------------------------------------------------------------*/ +PROCESS(blindnode_bcast_rec, "Blind Node"); +AUTOSTART_PROCESSES(&blindnode_bcast_rec); +/*---------------------------------------------------------------------------*/ +/* + * This handles the calculation cycle. Returns non-zero on error, 0 on success. + * + * When we move this outside the example, we will perhaps want to pass + * struct refcoords *, struct meas_params * + * instead of exposing our own data structures. If this happens, we will need + * to add checks to our code to detect non-sane values + */ +static uint8_t +calculate() +{ + static int j, x; + uint8_t valid_rssi = 0; + + /* Turn on the Engine */ + LOCENG = LOCENG_EN; + while(!(LOCENG & LOCENG_EN)); + + /* Reference Coordinate Load Stage */ + LOCENG |= LOCENG_REFLD; + while(!(LOCENG & LOCENG_REFLD)); + + for(j = 0; j < MAX_REF_NODES; j++) { + /* Write the Reference Node x,y into the engine */ + REFCOORD = ref_coords[j].x; + REFCOORD = ref_coords[j].y; + } + + /* Reference Coordinate Load Stage Done. Proceed with measured params */ + LOCENG &= ~LOCENG_REFLD; + LOCENG |= LOCENG_PARLD; + + /* Load Parameters */ + MEASPARM = parameters.alpha; + MEASPARM = parameters.n; + MEASPARM = parameters.x_min; + MEASPARM = parameters.x_delta; + MEASPARM = parameters.y_min; + MEASPARM = parameters.y_delta; + + /* Load Neighbor RSSIs */ + for(j = 0; j < MAX_REF_NODES; j++) { + if(parameters.rssi[j] != 0) { + /* Range-check for the RSSI here, can only be in [-95 dBm , -40 dBm] + * so we only accept 80 <= rssi <= 190*/ + if(parameters.rssi[j] >= 80 && parameters.rssi[j] <= 190) { + valid_rssi++; + } + } + /* Write the value, even if it's zero */ + MEASPARM = parameters.rssi[j]; + } + + /* Done with measured parameters too */ + LOCENG &= ~LOCENG_PARLD; + + /* Only Calculate if we have 3+ reference nodes (non-zero RSSIs) */ + if(valid_rssi >= 3) { + LOCENG |= LOCENG_RUN; + } else { + LOCENG = 0; + printf("some error\n"); + return 1; + } + + /* Block on the calculation, between 50us and 13ms */ + while(!(LOCENG & LOCENG_DONE)); + + /* + * LOCX contains an offset. Remove it to obtain our actual X value. + * cc2431 datasheet, section 2.1.3 + */ + x = (LOCX - parameters.x_min + 1) % (parameters.x_delta + 1) + + parameters.x_min; + coords[0] = x; + coords[1] = LOCY; /* No offset here */ + printf("n=%2u: X=%3u, Y=%3u\n", parameters.n, LOCX, LOCY); + + /* Turn it off */ + LOCENG = 0; + + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* + * We receive X, Y from reference nodes. + * We store this in location J of the ref_coords array, where J is the LSB + * of the reference node's rime address. So we can only accept data from nodes + * with rime address ending in [0 , 15] + */ +static void +broadcast_recv(struct broadcast_conn *c, const rimeaddr_t *from) +{ + packetbuf_attr_t rssi; /* Careful here, this is uint16_t */ + + if(from->u8[1] < MAX_REF_NODES) { + memset(&ref_coords[from->u8[1] - 1], 0, sizeof(struct refcoords)); + + /* Obtain incoming message's RSSI from contiki */ + rssi = packetbuf_attr(PACKETBUF_ATTR_RSSI); + /* Convert RSSI to the loc. eng. format */ + parameters.rssi[from->u8[1] - 1] = (-2 * rssi); + /* Raw dump the packetbuf into the ref_coords struct */ + memcpy(&ref_coords[from->u8[1] - 1], packetbuf_dataptr(), 2 * sizeof(uint8_t)); + } + + return; +} +/* + * Imaginary nodes to test functionality + * All nodes at 1 meter distance, rssi = -40 (80) + * Since the rssi at 1 meter = -40 (A), the blind node should think it's at + * 5,5 + */ +/*---------------------------------------------------------------------------*/ +static void +set_imaginary_ref_nodes() { + ref_coords[0].x = 1; + ref_coords[0].y = 5; + parameters.rssi[0] = SAMPLE_RSSI; + + ref_coords[1].x = 5; + ref_coords[1].y = 1; + parameters.rssi[1] = SAMPLE_RSSI; + + ref_coords[2].x = 5; + ref_coords[2].y = 9; + parameters.rssi[2] = SAMPLE_RSSI; + + ref_coords[3].x = 9; + ref_coords[3].y = 5; + parameters.rssi[3] = SAMPLE_RSSI; +} +/*---------------------------------------------------------------------------*/ +static const struct broadcast_callbacks broadcast_call = { broadcast_recv }; +static struct broadcast_conn broadcast; +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(blindnode_bcast_rec, ev, data) +{ + static struct etimer et; + static uint8_t n; + int i; + + PROCESS_EXITHANDLER(broadcast_close(&broadcast)); + + PROCESS_BEGIN(); + + printf("Reading Chip ID: 0x%02x\n", CHIPID); + /* Read our chip ID. If we are not cc2431, bail out */ + if(CHIPID != CC2431_CHIP_ID) { + printf("Hardware does not have a location engine. Exiting.\n"); + PROCESS_EXIT(); + } + + /* OK, we are cc2431. Do stuff */ + n = 0; + + /* Initalise our structs and parameters */ + memset(ref_coords, 0, sizeof(struct refcoords) * MAX_REF_NODES); + memset(¶meters, 0, sizeof(struct meas_params)); + + /* + * Just hard-coding measurement parameters here. + * Ideally, this should be part of a calibration mechanism + */ + parameters.alpha=SAMPLE_ALPHA; + parameters.x_min=0; + parameters.x_delta=255; + parameters.y_min=0; + parameters.y_delta=255; + + set_imaginary_ref_nodes(); + + broadcast_open(&broadcast, 129, &broadcast_call); + + while(1) { + + etimer_set(&et, CLOCK_SECOND); + + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + + /* + * With the hard-coded parameters and locations, we will calculate + * for all possible values of n [0 , 31] + */ + parameters.n=n; + calculate(); + n++; + if(n==32) { n=0; } + + /* Send our calculated location to some monitoring node */ + packetbuf_copyfrom(&coords, 2*sizeof(uint8_t)); + broadcast_send(&broadcast); + } + PROCESS_END(); +} diff --git a/examples/sensinode/cc2431-location-engine/cc2431_loc_eng.h b/examples/sensinode/cc2431-location-engine/cc2431_loc_eng.h new file mode 100644 index 000000000..b5489797c --- /dev/null +++ b/examples/sensinode/cc2431-location-engine/cc2431_loc_eng.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Header file used by the example demonstrating the cc2431 location + * engine. + * + * This file contains declarations of the location engine registers and + * the LOCENG register bits. It also contains some data structures used + * to store calculation parameters and reference node coordinates. + * + * This file only needs to be included bye the blind node code file. + * + * More information on the cc2431 Location Engine can be found in: + * - cc2431 Datasheet + * - K. Aamodt, "CC2431 Location Engine", Texas Instruments Application + * Note 42. + * + * \author + * George Oikonomou - + */ + +#include "8051def.h" +#include /* For syntax parsers */ + +/* Location Engine Registers on the cc2431 */ +__xdata __at (0xDF55) unsigned char REFCOORD; +__xdata __at (0xDF56) unsigned char MEASPARM; +__xdata __at (0xDF57) unsigned char LOCENG; +__xdata __at (0xDF58) unsigned char LOCX; +__xdata __at (0xDF59) unsigned char LOCY; + +/* LOCENG Register Bits */ +#define LOCENG_RUN 0x01 +#define LOCENG_REFLD 0x02 +#define LOCENG_PARLD 0x04 +#define LOCENG_DONE 0x08 +#define LOCENG_EN 0x10 + +/* cc2431 chips report 0x89 when the CHIPID register is read */ +#define CC2431_CHIP_ID 0x89 + +/* + * Struct for the Calculation Parameters. + * Values stored here feed the MEASPARM register. + * + * Values should be stored here in Location Engine format: + * RSSI: 0.5 Precision, without the minus sign. All 16 must be used. Use 0 + * to reduce the number of ref. nodes used in the calculation. + * Value range [-95 dBm , -40 dBm] + * A: 0.5 Precision. Value range [30.0 , 50.0] (Thus [60 , 100] decimal) + * n: Use the n Index value [0 , 31] - See cc2431 datasheet, Table 2. + * delta: Must be present. If we want the calculation to be unrestricted, + * use 0xFF + * + */ +struct meas_params { + uint8_t alpha; + uint8_t n; + uint8_t x_min; + uint8_t x_delta; + uint8_t y_min; + uint8_t y_delta; + uint8_t rssi[16]; +}; + +/* + * Store the reference node coordinates here. + * This will feed REFCOORD. + * + * Values should be stored here in Location Engine format: + * 2 LS bits for the fractional part, 0.25 precision + * 6 MS bits for the integral part. + * Value range [0 , 63.75] (thus [0 , 255]) + */ +struct refcoords { + uint8_t x; + uint8_t y; +}; diff --git a/examples/sensinode/clock_test.c b/examples/sensinode/clock_test.c index dfbe3d23d..758b4d040 100644 --- a/examples/sensinode/clock_test.c +++ b/examples/sensinode/clock_test.c @@ -19,7 +19,7 @@ PROCESS_THREAD(clock_test_process, ev, data) static struct etimer et; static clock_time_t count, start_count, end_count, diff; static unsigned long sec; - static uint8_t i; + static u8_t i; PROCESS_BEGIN(); @@ -56,7 +56,7 @@ PROCESS_THREAD(clock_test_process, ev, data) etimer_reset(&et); sec = clock_seconds(); - printf("%u seconds\n", (uint16_t) sec); + printf("%u seconds\n", (u16_t) sec); leds_toggle(LEDS_GREEN); i++; diff --git a/examples/sensinode/disco/Makefile b/examples/sensinode/disco/Makefile new file mode 100644 index 000000000..51b7be77f --- /dev/null +++ b/examples/sensinode/disco/Makefile @@ -0,0 +1,18 @@ +ifndef TARGET +TARGET=sensinode +endif + +# Make absolutely certain that you specify your device here +DEFINES+=MODEL_N740 + +HAVE_BANKING=1 +UIP_CONF_IPV6=1 +OFFSET_FIRMWARE=1 + +CONTIKI_PROJECT = disco-example + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/sensinode/disco/disco-example.c b/examples/sensinode/disco/disco-example.c new file mode 100644 index 000000000..8f97a13fc --- /dev/null +++ b/examples/sensinode/disco/disco-example.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Stub project source file. We just need to build contiki with + * OFFSET_FIRMWARE, Makefile does so. + * + * \author + * George Oikonomou - + */ + +#include "contiki.h" + +/*---------------------------------------------------------------------------*/ +PROCESS(stub_process, "Stub process"); +AUTOSTART_PROCESSES(&stub_process); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(stub_process, ev, data) +{ + PROCESS_BEGIN(); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/energy-scan/Makefile b/examples/sensinode/energy-scan/Makefile new file mode 100644 index 000000000..9020155e3 --- /dev/null +++ b/examples/sensinode/energy-scan/Makefile @@ -0,0 +1,16 @@ +ifndef TARGET +TARGET=sensinode +endif + +# Make absolutely certain that you specify your device here +DEFINES+=MODEL_N740,PROJECT_CONF_H + +PROJECT_SOURCEFILES += stub-rdc.c + +CONTIKI_PROJECT = energy-scan + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/sensinode/energy-scan/energy-scan.c b/examples/sensinode/energy-scan/energy-scan.c new file mode 100644 index 000000000..07393d507 --- /dev/null +++ b/examples/sensinode/energy-scan/energy-scan.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Minimalistic channel energy detection. + * + * \author + * George Oikonomou - + */ + +#include "contiki.h" +#include "cc2430_sfr.h" + +#include "sensinode-debug.h" +#include "dev/cc2430_rf.h" +#include + +static uint8_t channel; +static int8_t j; +static int8_t cmax; +static int8_t rssi; +static struct etimer et; +static rtimer_clock_t t0; + +#define RSSI_BASE -50 +#define RSSI_SAMPLES 30 +#define SAMPLE_INTERVAL (CLOCK_SECOND) +#define CHANNEL_MIN 11 +#define CHANNEL_MAX 26 +/* ToDo: Do this in infinite RX. Take more samples */ +/*---------------------------------------------------------------------------*/ +PROCESS(energy_scan, "Energy Scanner"); +AUTOSTART_PROCESSES(&energy_scan); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(energy_scan, ev, data) +{ + + PROCESS_BEGIN(); + + printf("Energy Scanner\n"); + printf("CCA Threshold: %d\n", (int8_t)RSSIH); + printf("Channel scan range: [%u , %u]\n", CHANNEL_MIN, CHANNEL_MAX); + printf("%u samples per channel, interval %u ticks\n", + RSSI_SAMPLES, SAMPLE_INTERVAL); + + channel = CHANNEL_MIN; + while(1) { + cmax = RSSI_BASE; + cc2430_rf_channel_set(channel); + clock_delay(200); + + for(j = 0; j < RSSI_SAMPLES; j++) { + t0 = RTIMER_NOW(); + rssi = RSSIL; + if(rssi > cmax) { + cmax = rssi; + } + while(RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + 25)); + } + printf("%u [%3d]: ", channel, cmax); + for(j = RSSI_BASE; j <= cmax; j++) { + printf("#"); + } + printf("\n"); + if(channel == CHANNEL_MAX) { + printf("===============\n"); + channel = CHANNEL_MIN; + } else { + channel++; + } + + etimer_set(&et, SAMPLE_INTERVAL); + PROCESS_YIELD(); + + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/energy-scan/netstack.c b/examples/sensinode/energy-scan/netstack.c new file mode 100644 index 000000000..2551f82fe --- /dev/null +++ b/examples/sensinode/energy-scan/netstack.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Stub file overriding core/net/netstack.c. What we want to achieve + * here is call netstack_init from main without initialising the RDC, + * MAC and Network layers. It will just turn on the radio instead. + * + * \author + * George Oikonomou - + */ + +#include "netstack.h" +/*---------------------------------------------------------------------------*/ +void +netstack_init(void) +{ + NETSTACK_RADIO.init(); +} +/*---------------------------------------------------------------------------*/ + diff --git a/examples/sensinode/energy-scan/project-conf.h b/examples/sensinode/energy-scan/project-conf.h new file mode 100644 index 000000000..91c1917e4 --- /dev/null +++ b/examples/sensinode/energy-scan/project-conf.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Project specific configuration defines for the sniffer example. + * + * We make sure that the radio driver outputs all packets in hexdump + * format. + * + * \author + * George Oikonomou - + */ + +#ifndef PROJECT_CONF_H_ +#define PROJECT_CONF_H_ + +#define NETSTACK_CONF_RDC stub_rdc_driver +#define ADC_SENSOR_CONF_ON 0 +#define LPM_CONF_MODE 0 + +#endif /* PROJECT_CONF_H_ */ diff --git a/examples/sensinode/energy-scan/stub-rdc.c b/examples/sensinode/energy-scan/stub-rdc.c new file mode 100644 index 000000000..605153d1b --- /dev/null +++ b/examples/sensinode/energy-scan/stub-rdc.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Definition of a fake RDC driver to be used with passive + * examples. The code will never send packets and it will never + * push incoming packets up the stack. We do this by defining this + * driver as our RDC. We then drop everything + * + * \author + * George Oikonomou - + */ + +#include "net/mac/mac.h" +#include "net/mac/rdc.h" +/*---------------------------------------------------------------------------*/ +static void +send(mac_callback_t sent, void *ptr) +{ + if(sent) { + sent(ptr, MAC_TX_OK, 1); + } +} +/*---------------------------------------------------------------------------*/ +static void +input(void) +{ +} +/*---------------------------------------------------------------------------*/ +static int +on(void) +{ + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +off(int keep_radio_on) +{ + return 1; +} +/*---------------------------------------------------------------------------*/ +static unsigned short +cca(void) +{ + return 0; +} +/*---------------------------------------------------------------------------*/ +static void +init(void) +{ +} +/*---------------------------------------------------------------------------*/ +const struct rdc_driver stub_rdc_driver = { + "stub-rdc", + init, + send, + input, + on, + off, + cca, +}; +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/event-post/Makefile b/examples/sensinode/event-post/Makefile new file mode 100644 index 000000000..15113d249 --- /dev/null +++ b/examples/sensinode/event-post/Makefile @@ -0,0 +1,17 @@ +ifndef TARGET +TARGET=sensinode +endif + +# Make absolutely certain that you specify your device here +DEFINES+=MODEL_N740 + +# This example doesn't need code banking so we turn it off +#HAVE_BANKING=1 + +CONTIKI_PROJECT = event-post + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/sensinode/event-post/event-post.c b/examples/sensinode/event-post/event-post.c new file mode 100644 index 000000000..ed249eeed --- /dev/null +++ b/examples/sensinode/event-post/event-post.c @@ -0,0 +1,132 @@ +/* This file demonstrates the interaction between two processes via events. + * + * - "Sensor process": Throws an event periodically. This can be for example a + * sensor value. + * - "Print process" : Waits for events from "Sensor" and prints a message when + * an event occurs (e.g. prints the sensor value) + * + * This example is derived from the contiki code examples for the WSN430 + * (SensTools - Inria: http://senstools.gforge.inria.fr/) + * + * Author: George Oikonomou + */ + +#include "contiki.h" +//#include "dev/leds.h" +#include +#include /* For printf() */ +#include "event-post.h" + +/* This is our event type */ +static process_event_t event_data_ready; + +/*---------------------------------------------------------------------------*/ +/* Declare the two processes here */ +PROCESS(sensor_process, "Sensor process"); +PROCESS(print_process, "Print process"); + +/* Tell Contiki that we want them to start automatically */ +AUTOSTART_PROCESSES(&sensor_process, &print_process); + +/*---------------------------------------------------------------------------*/ +/* Implementation "Sensor Process" */ +PROCESS_THREAD(sensor_process, ev, data) +{ + /* static variables to preserve values across consecutive calls of this + * process. */ + /* Set an etimer */ + static struct etimer timer; + /* And the 'sensor' monitoring variable */ + static struct event_struct es; + + PROCESS_BEGIN(); + + /* Set some near-the-limit initial values */ + /* signed primitives */ + es.s_val = SHRT_MAX-2; + es.i_val = INT_MAX-2; + es.l_val = LONG_MAX-2; + /* sizeof(long long) == sizeof(long) on sensinodes - see other examples*/ + es.ll_val = LONG_MAX-2; + /* and some typedef-ed unsigned variables */ + es.u8_val = UCHAR_MAX-2; + es.u16_val = USHRT_MAX-2; + es.u32_val = ULONG_MAX-2; + + /* allocate the required event */ + event_data_ready = process_alloc_event(); + + /* process_event_t is actually a u_char. What did the OS allocate for us? */ + printf("Contiki allocated event ID %d.\r\n", event_data_ready); + + /* Set a timer here. We will generate an event every times this timer expires + * etimer_set accepts clock ticks as its 2nd argument. + * CLOCK_CONF_SECOND is the number of ticks per second. + * This CLOCK_CONF_SECOND * N = N seconds */ + etimer_set(&timer, CLOCK_CONF_SECOND * 2); + + while (1) + { + printf("Sensor process: Wait for timer event...\r\n"); + /* Wait on our timer */ + PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER); + + /* blip */ + /* leds_toggle(LEDS_BLUE); */ + + /* Set the 'sensor' value before throwing the event */ + printf("Sensor Process: Incrementing values...\r\n"); + es.s_val++; + es.i_val++; + es.l_val++; + es.ll_val++; + es.u8_val++; + es.u16_val++; + es.u32_val++; + + /* Post our event. + * N.B. es is declared static. + * Try passing a volatile variable and observe the results... */ + printf("Sensor Process: Generating 'Data Ready' event.\r\n"); + process_post(&print_process, event_data_ready, &es); + + /* reset the timer so we can wait on it again */ + etimer_reset(&timer); + + } + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/* Implementation of "Print Process" */ +PROCESS_THREAD(print_process, ev, data) +{ + + struct event_struct * sd; + + PROCESS_BEGIN(); + + while (1) + { + /* Stop here and wait until "event_data_ready" occurs */ + PROCESS_WAIT_EVENT_UNTIL(ev == event_data_ready); + + /* When the event occurs, the incoming data will be stored in + * process_data_t data (careful, this is void *) + * + * Print away... + * es is volatile, we need to set it = data again and dereference it. */ + sd = data; + printf("Print Process - Data Ready:\r\n"); + printf(" s: %d\r\n", sd->s_val); + printf(" i: %d\r\n", sd->i_val); + printf(" l: %ld\r\n", sd->l_val); + printf(" ll: %lld\r\n", sd->ll_val); + printf(" u8: %u\r\n", sd->u8_val); + printf(" u16: %u\r\n", sd->u16_val); + printf(" u32: %lu\r\n", sd->u32_val); + + /* aaaaand back to waiting for the next event */ + } + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/event-post/event-post.h b/examples/sensinode/event-post/event-post.h new file mode 100644 index 000000000..2d640441a --- /dev/null +++ b/examples/sensinode/event-post/event-post.h @@ -0,0 +1,22 @@ +/* + * event-post.h + * Header file for the event_post example + * + * Created on: 30 Mar 2010 + * Author: George Oikonomou + */ + +#ifndef EVENT_POST_H_ +#define EVENT_POST_H_ + +struct event_struct { + short s_val; + int i_val; + long l_val; + long long ll_val; + u8_t u8_val; + u16_t u16_val; + u32_t u32_val; +}; + +#endif /* EVENT_POST_H_ */ diff --git a/examples/sensinode/sensors-ipv6/Makefile b/examples/sensinode/sensors-ipv6/Makefile new file mode 100644 index 000000000..608de5c0d --- /dev/null +++ b/examples/sensinode/sensors-ipv6/Makefile @@ -0,0 +1,19 @@ +ifndef TARGET +TARGET=sensinode +endif + +# Make absolutely certain that you specify your device here +DEFINES+=MODEL_N740,PROJECT_CONF_H + +# This example won't fit in flash without banking so we turn it on +HAVE_BANKING=1 +UIP_CONF_IPV6=1 + +CONTIKI_SOURCEFILES += sensors-driver.c + +CONTIKI_PROJECT = sensors-ipv6 +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/sensinode/sensors-ipv6/project-conf.h b/examples/sensinode/sensors-ipv6/project-conf.h new file mode 100644 index 000000000..baa6edfc4 --- /dev/null +++ b/examples/sensinode/sensors-ipv6/project-conf.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Project specific configuration defines for the UDP client/server + * example. + * + * We make sure that buttons and ADC are on. We also demonstrate the + * new LPM functionality + * + * \author + * George Oikonomou - + */ + +#ifndef PROJECT_CONF_H_ +#define PROJECT_CONF_H_ + +#define BUTTON_SENSOR_CONF_ON 1 +#define ADC_SENSOR_CONF_ON 1 +#define LPM_CONF_MODE 1 + +#endif /* PROJECT_CONF_H_ */ diff --git a/examples/sensinode/sensors-ipv6/sensors-driver.c b/examples/sensinode/sensors-ipv6/sensors-driver.c new file mode 100644 index 000000000..01e118eb2 --- /dev/null +++ b/examples/sensinode/sensors-ipv6/sensors-driver.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * This file handles the sensor readings and float conversions + * for sensors-example. We keep this separate in order to place it + * to a higher BANK. + * + * Bankable + * + * \author + * George Oikonomou - + */ + +#include "contiki-conf.h" +#include "uip.h" /* for htons / htonl */ +#include "dev/leds.h" +#if CONTIKI_TARGET_SENSINODE +#include "dev/sensinode-sensors.h" +#include "sensinode-debug.h" +#endif + +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif +#include +#include + +#define SENSOR_OK 0 +#define SENSOR_ADC_OFF -1 +#define SENSOR_UNKNOWN -2 + +/* Request Bits */ +#define REQUEST_BIT_P0_GET 0x0400 +#define REQUEST_BIT_L2_SET 0x0200 +#define REQUEST_BIT_L1_SET 0x0100 +#define REQUEST_BIT_LED_GET 0x0080 +#define REQUEST_BIT_ACC 0x0040 +#define REQUEST_BIT_BAT 0x0020 +#define REQUEST_BIT_VDD 0x0010 +#define REQUEST_BIT_TEMP 0x0008 +#define REQUEST_BIT_LIGHT 0x0004 +#define REQUEST_BIT_UPTIME 0x0002 +#define REQUEST_BIT_CHIPID 0x0001 + +/*---------------------------------------------------------------------------*/ +int8_t +read_sensor(char * rs) +{ + /* Sensor Values */ + static int rv; + static struct sensors_sensor * sensor; + + /* Those 3 variables are only used for debugging */ +#if DEBUG + static float sane = 0; + static int dec; + static float frac; +#endif + uint16_t r; + uint8_t len = 0; + + sensor = sensors_find(ADC_SENSOR); + if (!sensor) { + PRINTF("ADC not found\n"); + return (SENSOR_ADC_OFF); + } + + /* Fetch the request bytes */ + memcpy(&r, rs, 2); + r = uip_ntohs(r); + PRINTF("R=%u\n", r); + + if (r & REQUEST_BIT_CHIPID) { + uint8_t chipid = CHIPID; + memcpy(rs + len, &chipid, sizeof(chipid)); + len += sizeof(chipid); + PRINTF("ChipID=0x%02x\n", chipid); + } + if (r & REQUEST_BIT_UPTIME) { + /* Uptime */ + unsigned long l; + + l = uip_htonl(clock_seconds()); + memcpy(rs + len, &l, sizeof(l)); + len += sizeof(l); + PRINTF("Uptime=%lu secs\n", uip_ntohl(l)); + } + if (r & REQUEST_BIT_LIGHT) { + rv = sensor->value(ADC_SENSOR_TYPE_LIGHT); + if(rv != -1) { +#if DEBUG + sane = (float)(rv * 0.4071); + dec = sane; + frac = sane - dec; + PRINTF(" Light=%d.%02ulux (%d)\n", dec, (unsigned int)(frac*100), rv); +#endif + memcpy(rs + len, &rv, sizeof(rv)); + len += sizeof(rv); + } + } + if (r & REQUEST_BIT_TEMP) { + rv = sensor->value(ADC_SENSOR_TYPE_TEMP); + if(rv != -1) { +#if DEBUG + sane = ((rv * 0.61065 - 773) / 2.45); + dec = sane; + frac = sane - dec; + PRINTF(" Temp=%d.%02u C (%d)\n", dec, (unsigned int)(frac*100), rv); +#endif + memcpy(rs + len, &rv, sizeof(rv)); + len += sizeof(rv); + } + } + if (r & (REQUEST_BIT_VDD | REQUEST_BIT_BAT)) { + /* We want VDD for both cases */ + rv = sensor->value(ADC_SENSOR_TYPE_VDD); + if(rv != -1) { +#if DEBUG + sane = rv * 3.75 / 2047; + dec = sane; + frac = sane - dec; + PRINTF("Supply=%d.%02uV (%d)\n", dec, (unsigned int)(frac*100), rv); + /* Store rv temporarily in dec so we can use it for the battery */ + dec = rv; +#endif + memcpy(rs + len, &rv, sizeof(rv)); + len += sizeof(rv); + } + /* And then carry on with battery if needed */ + if (r & REQUEST_BIT_BAT) { + rv = sensor->value(ADC_SENSOR_TYPE_BATTERY); + if(rv != -1) { +#if DEBUG + sane = (11.25 * rv * dec) / (0x7FE002); + dec = sane; + frac = sane - dec; + PRINTF(" Batt.=%d.%02uV (%d)\n", dec, (unsigned int)(frac*100), rv); +#endif + memcpy(rs + len, &rv, sizeof(rv)); + len += sizeof(rv); + } + } + } + if (r & REQUEST_BIT_ACC) { + rv = sensor->value(ADC_SENSOR_TYPE_ACC_X); + if(rv != -1) { +#if DEBUG + sane = ((rv * 3.75 / 2047) - 1.65) / 0.44; + dec = sane; + frac = sane - dec; + frac = (frac < 0) ? -frac : frac; + + PRINTF(" AccX="); + if(sane < 0 && dec == 0) { + PRINTF('-'); + } + PRINTF("%d.%02ug (%d)\n", dec, (unsigned int)(frac*100), rv); +#endif + memcpy(rs + len, &rv, sizeof(rv)); + len += sizeof(rv); + } + rv = sensor->value(ADC_SENSOR_TYPE_ACC_Y); + if(rv != -1) { +#if DEBUG + sane = ((rv * 3.75 / 2047) - 1.65) / 0.44; + dec = sane; + frac = sane - dec; + frac = (frac < 0) ? -frac : frac; + PRINTF(" AccY="); + if(sane < 0 && dec == 0) { + PRINTF('-'); + } + PRINTF("%d.%02ug (%d)\n", dec, (unsigned int)(frac*100), rv); +#endif + memcpy(rs + len, &rv, sizeof(rv)); + len += sizeof(rv); + } + rv = sensor->value(ADC_SENSOR_TYPE_ACC_Z); + if(rv != -1) { +#if DEBUG + sane = ((rv * 3.75 / 2047) - 1.65) / 0.44; + dec = sane; + frac = sane - dec; + frac = (frac < 0) ? -frac : frac; + PRINTF(" AccZ="); + if(sane < 0 && dec == 0) { + PRINTF('-'); + } + PRINTF("%d.%02ug (%d)\n", dec, (unsigned int)(frac*100), rv); +#endif + memcpy(rs + len, &rv, sizeof(rv)); + len += sizeof(rv); + } + } + if (r & REQUEST_BIT_L1_SET) { + leds_toggle(LEDS_GREEN); + } + if (r & REQUEST_BIT_L2_SET) { + leds_toggle(LEDS_RED); + } + if (r & REQUEST_BIT_LED_GET) { + uint8_t leds = leds_get(); + memcpy(rs + len, &leds, sizeof(leds)); + len += sizeof(leds); + PRINTF(" LED 2=%u\n", leds); + } + if (r & REQUEST_BIT_P0_GET) { + uint8_t p0 = P0_3; + memcpy(rs + len, &p0, sizeof(p0)); + len += sizeof(p0); + } + return len; +} diff --git a/examples/sensinode/sensors-ipv6/sensors-ipv6.c b/examples/sensinode/sensors-ipv6/sensors-ipv6.c new file mode 100644 index 000000000..ca9589fb4 --- /dev/null +++ b/examples/sensinode/sensors-ipv6/sensors-ipv6.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example to demonstrate-test the sensors functionality on + * sensinode/cc2430 devices. + * + * A UDP/IPv6 process waits for requests from a monitoring station + * and responds with sensor values. + * + * The message exchange is based on a custom protocol. + * Check sensors-driver.c for protocol details. + * + * \author + * George Oikonomou - + */ + +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" + +#include + +#define DEBUG DEBUG_NONE +#include "net/uip-debug.h" +#include "dev/watchdog.h" +#include "dev/leds.h" +#include "net/rpl/rpl.h" +#include + +#if CONTIKI_TARGET_SENSINODE +#include "sensinode-debug.h" +#include "dev/sensinode-sensors.h" +#else +#define putstring(s) +#endif + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) + +#define MAX_PAYLOAD_LEN 120 + +static struct uip_udp_conn *server_conn; +static char buf[MAX_PAYLOAD_LEN]; +static uint16_t len; + +#define SERVER_PORT 60000 + +#define SENSOR_OK 0 +#define SENSOR_ADC_OFF 1 +#define SENSOR_UNKNOWN 2 + +int8_t read_sensor(char * rs); +/*---------------------------------------------------------------------------*/ +extern const struct sensors_sensor adc_sensor; +/*---------------------------------------------------------------------------*/ +PROCESS(udp_server_process, "UDP server process"); +AUTOSTART_PROCESSES(&udp_server_process); +/*---------------------------------------------------------------------------*/ +static void +tcpip_handler(void) +{ + memset(buf, 0, MAX_PAYLOAD_LEN); + + if(uip_newdata()) { + len = uip_datalen(); + memcpy(buf, uip_appdata, len); + PRINTF("%u bytes from [", len); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("]:%u\n", UIP_HTONS(UIP_UDP_BUF->srcport)); + len = read_sensor(buf); + if( len ) { + server_conn->rport = UIP_UDP_BUF->srcport; + uip_ipaddr_copy(&server_conn->ripaddr, &UIP_IP_BUF->srcipaddr); + uip_udp_packet_send(server_conn, buf, len); + PRINTF("Sent %u bytes\n", len); + } + + /* Restore server connection to allow data from any node */ + uip_create_unspecified(&server_conn->ripaddr); + server_conn->rport = 0; + } + return; +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(udp_server_process, ev, data) +{ +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) + static struct sensors_sensor *b1; + static struct sensors_sensor *b2; +#endif + + PROCESS_BEGIN(); + putstring("Starting UDP server\n"); + +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) + putstring("Button X: Toggle LED X\n"); +#endif + + server_conn = udp_new(NULL, UIP_HTONS(0), NULL); + udp_bind(server_conn, UIP_HTONS(SERVER_PORT)); + +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) + b1 = sensors_find(BUTTON_1_SENSOR); + b2 = sensors_find(BUTTON_2_SENSOR); +#endif + + while(1) { + PROCESS_YIELD(); + if(ev == tcpip_event) { + tcpip_handler(); +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) + } else if(ev == sensors_event && data != NULL) { + if(data == b1) { + leds_toggle(LEDS_GREEN); + } else if(data == b2) { + leds_toggle(LEDS_RED); + } +#endif /* (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) */ + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/sensors/Makefile b/examples/sensinode/sensors/Makefile new file mode 100644 index 000000000..90c05ae80 --- /dev/null +++ b/examples/sensinode/sensors/Makefile @@ -0,0 +1,17 @@ +ifndef TARGET +TARGET=sensinode +endif + +# Make absolutely certain that you specify your device here +DEFINES+=MODEL_N740 + +# This example doesn't need code banking so we turn it off +#HAVE_BANKING=1 + +CONTIKI_PROJECT = sensors-example + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/sensinode/sensors/sensors-example.c b/examples/sensinode/sensors/sensors-example.c new file mode 100644 index 000000000..ecbaddb17 --- /dev/null +++ b/examples/sensinode/sensors/sensors-example.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example to demonstrate-test the sensors functionality on + * sensinode/cc2430 devices. + * + * B1 turns L2 on and off. + * B2 reboots the node via the watchdog. + * + * The node takes readings from the various sensors every x seconds and + * prints out the results. + * + * We use floats here to translate the AD conversion results to + * meaningful values. However, our printf does not have %f support so + * we use an ugly hack to print out the value by extracting the integral + * part and then the fractional part. Don't try this at home. + * + * Temperature: + * Math is correct, the sensor needs calibration per device. + * I currently use default values for the math which may result in + * very incorrect values in degrees C. + * See TI Design Note DN102 about the offset calibration. + * + * Supply Voltage (VDD) and Battery Sensor: + * For VDD, math is correct, conversion is correct. See DN101 for details if + * interested. + * Battery reports different values when we run it many times + * in succession. The cause is unknown. + * I am fairly confident that I have captured the connections on the + * device correctly. I am however accepting input/feedback + * + * Light Sensor (Vishay Semiconductors TEPT4400): + * I am uncertain about the math. This needs testing. All I know is + * that 600lux = 0.9V and that the relation is linear. See inline for + * more details + * + * Accelerometer (Freescale Semiconductor MMA7340L): + * Math is correct but the sensor needs calibration. I've not + * attempted one cause the reported values differ per device. + * Place the N740 with the logo facing down to get 1g on the Z axis. + * Place the antenna side facing down to get 1g on the Y axis + * Place the N740 on its longer side while looking at the antenna and + * the D connector. Antenna on the bottom, D connector on the top. + * This should give you 1g on the X axis. + * + * Make sure you enable/disable things in contiki-conf.h + * + * \author + * George Oikonomou - + */ + +#include "contiki.h" +#include "contiki-conf.h" +#include "net/rime.h" +#include "dev/leds.h" +#include "dev/watchdog.h" +#include "lib/random.h" + +#if CONTIKI_TARGET_SENSINODE +#include "dev/sensinode-sensors.h" +#else +#include "lib/sensors.h" +#endif + +#define DEBUG 1 +#if DEBUG +#include +#if CONTIKI_TARGET_SENSINODE +#include "sensinode-debug.h" +#endif /* CONTIKI_TARGET_SENSINODE */ +#define PRINTF(...) printf(__VA_ARGS__) +#else /* DEBUG */ +/* We overwrite (read as annihilate) all output functions here */ +#define PRINTF(...) +#define putstring(...) +#define putchar(...) +#endif /* DEBUG */ + + +#define SEND_BATTERY_INFO 0 +#if SEND_BATTERY_INFO +#include "sensors-example.h" +static void bc_rx(struct broadcast_conn *c, const rimeaddr_t *from) { + return; +} + +static const struct broadcast_callbacks bc_cb = { bc_rx }; +static struct broadcast_conn bc_con; +#endif + +#if BUTTON_SENSOR_ON +extern const struct sensors_sensor button_1_sensor, button_2_sensor; +#endif + +/*---------------------------------------------------------------------------*/ +PROCESS(sensors_test_process, "Sensor Test Process"); +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) +PROCESS(buttons_test_process, "Button Test Process"); +AUTOSTART_PROCESSES(&sensors_test_process, &buttons_test_process); +#else +AUTOSTART_PROCESSES(&sensors_test_process); +#endif +/*---------------------------------------------------------------------------*/ +#if BUTTON_SENSOR_ON +PROCESS_THREAD(buttons_test_process, ev, data) +{ + struct sensors_sensor *sensor; + + PROCESS_BEGIN(); + + while (1) { + + PROCESS_WAIT_EVENT_UNTIL(ev == sensors_event); + + /* If we woke up after a sensor event, inform what happened */ + sensor = (struct sensors_sensor *)data; + if(sensor == &button_1_sensor) { + leds_toggle(LEDS_GREEN); + } else if(sensor == &button_2_sensor) { + watchdog_reboot(); + } + } + + PROCESS_END(); +} +#endif +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(sensors_test_process, ev, data) +{ + static struct etimer et; +#if SEND_BATTERY_INFO + /* Node Time */ + static struct sensor_data sd; +#endif + + /* Sensor Values */ + static int rv; + static struct sensors_sensor * sensor; + static float sane = 0; + static int dec; + static float frac; + +#if SEND_BATTERY_INFO + PROCESS_EXITHANDLER(broadcast_close(&bc_con);) +#endif + + PROCESS_BEGIN(); + + putstring("========================\n"); + putstring("Starting Sensor Example.\n"); + putstring("========================\n"); + +#if SEND_BATTERY_INFO + broadcast_open(&bc_con, BATTERY_RIME_CHANNEL, &bc_cb); +#endif + + /* Set an etimer. We take sensor readings when it expires and reset it. */ + etimer_set(&et, CLOCK_SECOND * 2); + + while (1) { + + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + + /* + * Request some ADC conversions + * Return value -1 means sensor not available or turned off in conf + */ + sensor = sensors_find(ADC_SENSOR); + if (sensor) { + putstring("------------------\n"); + leds_on(LEDS_RED); + /* + * Temperature: + * Using 1.25V ref. voltage (1250mV). + * Typical Voltage at 0°C : 743 mV + * Typical Co-efficient : 2.45 mV/°C + * Offset at 25°C : 30 (this varies and needs calibration) + * + * Thus, at 12bit resolution: + * + * ADC x 1250 / 2047 - (743 + 30) 0.61065 x ADC - 773 + * T = ------------------------------ ~= ------------------- °C + * 2.45 2.45 + */ + rv = sensor->value(ADC_SENSOR_TYPE_TEMP); + if(rv != -1) { + sane = ((rv * 0.61065 - 773) / 2.45); + dec = sane; + frac = sane - dec; + PRINTF(" Temp=%d.%02u C (%d)\n", dec, (unsigned int)(frac*100), rv); + } + /* + * Accelerometer: Freescale Semiconductor MMA7340L + * Using 1.25V ref. voltage. + * Sensitivity: 0.44 mV/g in ±3g mode. + * 0.1175 mV/g in ±11g mode. + * Typical 0g Vout = 1.65V (both modes, Vdd=3.3V, T=25°C) + * ADC Input Voltage is 1/3 Accelerometer Output Voltage + * + * +3g -> 2.97V Acc Out -> 0.9900V ADC Input -> 1621 + * +1g -> 2.09V Acc Out -> 0.6967V ADC Input -> 1141 + * 0g -> 1.65V Acc Out -> 0.5500V ADC Input -> 901 + * -1g -> 1.21V Acc Out -> 0.4033V ADC Input -> 660 + * -3g -> 0.33V Acc Out -> 0.1100V ADC Input -> 180 + * + * Thus, at 12bit resolution, ±3g mode: + * ADC x 1.25 x 3 + * Vout = -------------- V + * 2047 + * + * Vout - 0g Vout - 1.65 + * Acc = ----------- = ----------- g + * Sensitivity 0.44 + * + * Similar calc. for ±11g with 0.1175V increments + * + * This is only valid if you set ACC_SENSOR_CONF_GSEL 0 in contiki-conf.h + */ + rv = sensor->value(ADC_SENSOR_TYPE_ACC_X); + if(rv != -1) { + sane = ((rv * 3.75 / 2047) - 1.65) / 0.44; + dec = sane; + frac = sane - dec; + frac = (frac < 0) ? -frac : frac; + + /* + * This will fail for numbers like -0.xyz (since there is no such thing + * as -0. We manually add a minus sign in the printout if sane is neg + * and dec is 0. + * This is the wrong way to do it... + */ + putstring(" AccX="); + if(sane < 0 && dec == 0) { + putchar('-'); + } + PRINTF("%d.%02ug (%d)\n", dec, (unsigned int)(frac*100), rv); + } + rv = sensor->value(ADC_SENSOR_TYPE_ACC_Y); + if(rv != -1) { + sane = ((rv * 3.75 / 2047) - 1.65) / 0.44; + dec = sane; + frac = sane - dec; + frac = (frac < 0) ? -frac : frac; + putstring(" AccY="); + if(sane < 0 && dec == 0) { + putchar('-'); + } + PRINTF("%d.%02ug (%d)\n", dec, (unsigned int)(frac*100), rv); + } + rv = sensor->value(ADC_SENSOR_TYPE_ACC_Z); + if(rv != -1) { + sane = ((rv * 3.75 / 2047) - 1.65) / 0.44; + dec = sane; + frac = sane - dec; + frac = (frac < 0) ? -frac : frac; + putstring(" AccZ="); + if(sane < 0 && dec == 0) { + putchar('-'); + } + PRINTF("%d.%02ug (%d)\n", dec, (unsigned int)(frac*100), rv); + } + /* + * Light: Vishay Semiconductors TEPT4400 + * Using 1.25V ref. voltage. + * For 600 Lux illuminance, the sensor outputs 1mA current (0.9V ADC In) + * 600 lux = 1mA output => 1473 ADC value at 12 bit resolution) + * + * Thus, at 12bit resolution: + * 600 x 1.25 x ADC + * Lux = ---------------- ~= ADC * 0.4071 + * 2047 x 0.9 + */ + rv = sensor->value(ADC_SENSOR_TYPE_LIGHT); + if(rv != -1) { + sane = (float)(rv * 0.4071); + dec = sane; + frac = sane - dec; + PRINTF(" Light=%d.%02ulux (%d)\n", dec, (unsigned int)(frac*100), rv); + } + /* + * Power Supply Voltage. + * Using 1.25V ref. voltage. + * AD Conversion on VDD/3 + * + * Thus, at 12bit resolution: + * + * ADC x 1.25 x 3 + * Supply = -------------- V + * 2047 + */ + rv = sensor->value(ADC_SENSOR_TYPE_VDD); +#if SEND_BATTERY_INFO + sd.vdd = rv; +#endif + if(rv != -1) { + sane = rv * 3.75 / 2047; + dec = sane; + frac = sane - dec; + PRINTF("Supply=%d.%02uV (%d)\n", dec, (unsigned int)(frac*100), rv); + /* Store rv temporarily in dec so we can use it for the battery */ + dec = rv; + } + /* + * Battery Voltage - Only 2/3 of the actual voltage reach the ADC input + * Using 1.25V ref. voltage would result in 2047 AD conversions all the + * time since ADC-in would be gt 1.25. We thus use AVDD_SOC as ref. + * + * Thus, at 12bit resolution (assuming VDD is 3.3V): + * + * ADC x 3.3 x 3 ADC x 4.95 + * Battery = ------------- = ---------- V + * 2047 x 2 2047 + * + * Replacing the 3.3V with an ADC reading of the actual VDD would yield + * better accuracy. See monitor-node.c for an example. + * + * 3 x ADC x VDD x 3.75 ADC x VDD x 11.25 + * Battery = -------------------- = ----------------- V + * 2 x 2047 x 2047 0x7FE002 + * + */ + rv = sensor->value(ADC_SENSOR_TYPE_BATTERY); + if(rv != -1) { + /* Instead of hard-coding 3.3 here, use the latest VDD (stored in dec) + * (slightly inaccurate still, but better than crude 3.3) */ + sane = (11.25 * rv * dec) / (0x7FE002); + dec = sane; + frac = sane - dec; + PRINTF(" Batt.=%d.%02uV (%d)\n", dec, (unsigned int)(frac*100), rv); +#if SEND_BATTERY_INFO + sd.bat = rv; + packetbuf_copyfrom(&sd, sizeof(sd)); + broadcast_send(&bc_con); +#endif + } + leds_off(LEDS_RED); + } + etimer_reset(&et); + } + PROCESS_END(); + } +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/sensors/sensors-example.h b/examples/sensinode/sensors/sensors-example.h new file mode 100644 index 000000000..432d25073 --- /dev/null +++ b/examples/sensinode/sensors/sensors-example.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Header file for the sensors example. Must be included by the + * sensing node as well as the monitor node. + * + * \author + * George Oikonomou - + */ + +#ifndef SENSORSTEST_H_ +#define SENSORSTEST_H_ + +#define BATTERY_RIME_CHANNEL 222 + +/* This is what our PDU looks like */ +struct sensor_data { + int vdd; + int bat; +}; + + +#endif /* SENSORSTEST_H_ */ diff --git a/examples/sensinode/serial-flash/Makefile b/examples/sensinode/serial-flash/Makefile new file mode 100644 index 000000000..b8c8d2fbe --- /dev/null +++ b/examples/sensinode/serial-flash/Makefile @@ -0,0 +1,13 @@ +ifndef TARGET +TARGET=sensinode +endif + +# Make absolutely certain that you specify your device here +DEFINES+=MODEL_N740 + +CONTIKI_PROJECT = flash + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. +include $(CONTIKI)/Makefile.include diff --git a/examples/sensinode/serial-flash/flash.c b/examples/sensinode/serial-flash/flash.c new file mode 100644 index 000000000..6593de3ee --- /dev/null +++ b/examples/sensinode/serial-flash/flash.c @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * + * Example demonstrating the flash memory functionality on + * sensinode N740s + * + * \author + * George Oikonomou - + */ + +#include "contiki.h" +#include "dev/leds.h" +#include "cc2430_sfr.h" +#include "8051def.h" +#include "dev/m25p16.h" +#include "dev/n740.h" + +#define DEBUG 1 +#if DEBUG +#include +#include "sensinode-debug.h" +#define PRINTF(...) printf(__VA_ARGS__) +#define PUTBIN(b) putbin(b) +#else +#define PRINTF(...) +#define PUTBIN(b) +#endif + +static struct m25p16_rdid id; + +#define USE_SECTOR 0x10 +#define MAX_READ_CHUNK 10 +static uint8_t r_addr[3]; /* Read address: {USE_SECTOR, 0, 0} */ +static uint8_t d_buf[MAX_READ_CHUNK]; +static uint8_t rv; +static uint8_t counter; +/*---------------------------------------------------------------------------*/ +PROCESS(serial_flash_process, "Serial Flash example"); +AUTOSTART_PROCESSES(&serial_flash_process); +/*---------------------------------------------------------------------------*/ +static void +rdsr() +{ + rv = 0; + + n740_analog_deactivate(); + rv = m25p16_rdsr(); + n740_analog_activate(); + + PRINTF("RDSR: "); + putbin(rv); + PRINTF("\n"); +} +/*---------------------------------------------------------------------------*/ +static void +rdid() +{ + uint8_t i; + memset(&id, 0, sizeof(struct m25p16_rdid)); + + n740_analog_deactivate(); + m25p16_rdid(&id); + n740_analog_activate(); + + PRINTF("RDID: 0x%02x\n", id.man_id); + PRINTF("Type: 0x%02x\n", id.mem_type); + PRINTF("Size: 0x%02x\n", id.mem_size); + PRINTF("ULen: 0x%02x\n", id.uid_len); + PRINTF(" UID:"); + for(i = 0; i < id.uid_len; i++) { + PRINTF(" %02x", id.uid[i]); + } + PRINTF("\n"); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(serial_flash_process, ev, data) +{ + static struct etimer et; + uint8_t i; + + PROCESS_BEGIN(); + + PRINTF("Start\n"); + + memset(r_addr, 0, 3); + r_addr[0] = USE_SECTOR; + counter = 1; + + while(1) { + + /* Delay */ + etimer_set(&et, CLOCK_SECOND * 2); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + + leds_on(LEDS_GREEN); + + if(counter == 0) { + n740_analog_deactivate(); + rv = m25p16_rdsr(); + n740_analog_activate(); + /* If counter==0, we started Bulk Erasing earlier. Check if we still are */ + if(rv & M25P16_SR_WIP) { + PRINTF("Yield [%02x]\n", rv); + } else { + counter = 1; + } + } + if(counter) { + /* + * Take us out of Deep Power Down - On first power-on, the device will + * go to stand by mode (which is not DP). However, we drop to DP at the + * end of every loop. RES must be 0x14. This is the old style signature + * and is only still there for backward compatibility. + */ + n740_analog_deactivate(); + rv = m25p16_res_res(); + n740_analog_activate(); + + PRINTF(" RES: 0x%02x\n", rv); + + n740_analog_deactivate(); + rv = M25P16_WIP(); + n740_analog_activate(); + + PRINTF("========\n"); + memset(d_buf, 0, MAX_READ_CHUNK); + + + /* + * Read Device ID: Return values must be: + * man_id: 0x20 (Numonyx) + * mem_type: 0x20 + * mem_size: 0x15 (2 ^ 0x15 bytes = 2MB) + * uid_len: number of bytes in UID + * uid: Either all zeroes or a customized factory data content + * */ + rdid(); + + /* Check the value of our Status Register (SR) */ + rdsr(); + + /* Enable Write: Set Bit 1 in the SR to 1 (bit WEL) */ + PRINTF("WREN\n"); + n740_analog_deactivate(); + m25p16_wren(); + n740_analog_activate(); + + /* Confirm: SR & 0x02 must be 1 */ + rdsr(); + + /* Disable the WEL bit */ + PRINTF("WRDI\n"); + n740_analog_deactivate(); + m25p16_wrdi(); + n740_analog_activate(); + + /* Confirm: SR & 0x02 must be 0 */ + rdsr(); + + /* Write something to the SR. We don't need to explicitly set WEL, wrsr() + * will do it for us. When the cycle ends, WEL will go low */ + PRINTF("WRSR\n"); + n740_analog_deactivate(); + + /* For instance, let's protect sector 31 (that's the highest one) */ + m25p16_wrsr(M25P16_SR_BP0); + + /* + * While this is running, WEL should remain high and WIP (bit 0) should + * also be high. When this ends, WIP and WEL will go low. + * + * While the write is in ongoing, we can still read the SR to check the + * cycle's progress + */ + while(M25P16_WIP()); + + n740_analog_activate(); + + /* Confirm: SR & 0x02 must be 0 */ + rdsr(); + + /* Read MAX_READ_CHUNK bytes from Page 0x000000 */ + memset(d_buf, 0, MAX_READ_CHUNK); + n740_analog_deactivate(); + m25p16_read(r_addr, d_buf, MAX_READ_CHUNK); + n740_analog_activate(); + + PRINTF("READ:"); + for(i = 0; i < MAX_READ_CHUNK; i++) { + PRINTF(" %02x", d_buf[i]); + } + PRINTF("\n"); + + /* Write MAX_READ_CHUNK bytes to the same Page */ + PRINTF("WRITE\n"); + for(i = 0; i < MAX_READ_CHUNK; i++) { + d_buf[i] = i; + } + n740_analog_deactivate(); + + /* We don't need to wren() explicitly, pp() will do that for us */ + m25p16_pp(r_addr, d_buf, MAX_READ_CHUNK); + + /* Wait for the cycle */ + while(M25P16_WIP()); + + /* Trash our data buffer */ + memset(d_buf, 0, MAX_READ_CHUNK); + + PRINTF("ERASE\n"); + n740_analog_deactivate(); + + /* Bulk erase every 4 loops, sector erase otherwise */ + + /* Bulk Erase: This takes a few seconds so we can't really block on it. + * It'd be a bad thing to do and the watchdog would bark anyway. + * Bulk Erase will only be accepted if all SR_BP[2:0] == 0 */ + if((counter % 4) == 0) { + m25p16_wrsr(0); + while(M25P16_WIP()); + m25p16_be(); + counter = 0; + } else { + m25p16_se(USE_SECTOR); + while(M25P16_WIP()); + /* Drop to Deep Power Down */ + m25p16_dp(); + counter ++; + } + n740_analog_activate(); + } + leds_off(LEDS_GREEN); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/sniffer/Makefile b/examples/sensinode/sniffer/Makefile new file mode 100644 index 000000000..07f074069 --- /dev/null +++ b/examples/sensinode/sniffer/Makefile @@ -0,0 +1,16 @@ +ifndef TARGET +TARGET=sensinode +endif + +# Make absolutely certain that you specify your device here +DEFINES+=MODEL_N601,PROJECT_CONF_H + +PROJECT_SOURCEFILES += stub-rdc.c + +CONTIKI_PROJECT = sniffer + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/sensinode/sniffer/README b/examples/sensinode/sniffer/README new file mode 100644 index 000000000..d51ad2ce8 --- /dev/null +++ b/examples/sensinode/sniffer/README @@ -0,0 +1,14 @@ +A very simple sniffer for sensinode devices. + +The cc2430 RF driver supports outputting all captured packets in hexdump +format. We turn this on, and turn everything else off. We use a stub RDC +driver to make sure no incoming packet ever goes up the stack and no packet is +ever sent out. + +We only initialise the radio driver instead of the entire stack by over-riding +the default netstack.c with the one in this directory. + +You can then pipe the sniffer's output to the n601-cap util, which will convert +the hexdumps to pcap format, which can in turn be piped to wireshark. This is +handy if we want live capture of the lowpan traffic from wireshark. See the +README in n601-cap for more details diff --git a/examples/sensinode/sniffer/netstack.c b/examples/sensinode/sniffer/netstack.c new file mode 100644 index 000000000..2551f82fe --- /dev/null +++ b/examples/sensinode/sniffer/netstack.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Stub file overriding core/net/netstack.c. What we want to achieve + * here is call netstack_init from main without initialising the RDC, + * MAC and Network layers. It will just turn on the radio instead. + * + * \author + * George Oikonomou - + */ + +#include "netstack.h" +/*---------------------------------------------------------------------------*/ +void +netstack_init(void) +{ + NETSTACK_RADIO.init(); +} +/*---------------------------------------------------------------------------*/ + diff --git a/examples/sensinode/sniffer/project-conf.h b/examples/sensinode/sniffer/project-conf.h new file mode 100644 index 000000000..3ed5ec99e --- /dev/null +++ b/examples/sensinode/sniffer/project-conf.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Project specific configuration defines for the sniffer example. + * + * We make sure that the radio driver outputs all packets in hexdump + * format. + * + * \author + * George Oikonomou - + */ + +#ifndef PROJECT_CONF_H_ +#define PROJECT_CONF_H_ + +#define CC2430_RF_CONF_HEXDUMP 1 +#define CC2430_RF_CONF_AUTOACK 0 +#define NETSTACK_CONF_RDC stub_rdc_driver +#define ADC_SENSOR_CONF_ON 0 +#define LPM_CONF_MODE 0 + +#endif /* PROJECT_CONF_H_ */ diff --git a/examples/sensinode/sniffer/sniffer.c b/examples/sensinode/sniffer/sniffer.c new file mode 100644 index 000000000..ffde21209 --- /dev/null +++ b/examples/sensinode/sniffer/sniffer.c @@ -0,0 +1,54 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +#include "contiki.h" +#include "cc2430_sfr.h" + +#define DEBUG DEBUG_NONE +#include "net/uip-debug.h" + +/*---------------------------------------------------------------------------*/ +PROCESS(sniffer_process, "Sniffer process"); +AUTOSTART_PROCESSES(&sniffer_process); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(sniffer_process, ev, data) +{ + + PROCESS_BEGIN(); + + PRINTF("Sniffer started\n"); + + /* Turn off cc2430 Address Recognition - We need to accept all frames */ + MDMCTRL0H &= ~0x08; + + PROCESS_EXIT(); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/sniffer/stub-rdc.c b/examples/sensinode/sniffer/stub-rdc.c new file mode 100644 index 000000000..603552c97 --- /dev/null +++ b/examples/sensinode/sniffer/stub-rdc.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Definition of a fake RDC driver to be used with passive + * examples. The sniffer will never send packets and it will never + * push incoming packets up the stack. We do this by defining this + * driver as our RDC. We then drop everything + * + * \author + * George Oikonomou - + */ + +#include "net/mac/mac.h" +#include "net/mac/rdc.h" +/*---------------------------------------------------------------------------*/ +static void +send(mac_callback_t sent, void *ptr) +{ + if(sent) { + sent(ptr, MAC_TX_OK, 1); + } +} +/*---------------------------------------------------------------------------*/ +static void +input(void) +{ +} +/*---------------------------------------------------------------------------*/ +static int +on(void) +{ + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +off(int keep_radio_on) +{ + return keep_radio_on; +} +/*---------------------------------------------------------------------------*/ +static unsigned short +cca(void) +{ + return 0; +} +/*---------------------------------------------------------------------------*/ +static void +init(void) +{ +} +/*---------------------------------------------------------------------------*/ +const struct rdc_driver stub_rdc_driver = { + "stub-rdc", + init, + send, + input, + on, + off, + cca, +}; +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/timer-test.c b/examples/sensinode/timer-test.c new file mode 100644 index 000000000..163574326 --- /dev/null +++ b/examples/sensinode/timer-test.c @@ -0,0 +1,123 @@ +/** + * \file + * Tests related to clocks and timers + * + * This is clock_test.c plus a small addition by George Oikonomou + * (Loughborough University)in order to test the rtimer + * + * \author + * Zach Shelby (Original) + * George Oikonomou - (rtimer code) + * + */ + +#include "contiki.h" +#include "sys/clock.h" +#include "sys/rtimer.h" +#include "dev/leds.h" + +#include +/*---------------------------------------------------------------------------*/ +#define TEST_CLOCK_DELAY 1 +#define TEST_RTIMER 1 +#define TEST_ETIMER 1 +#define TEST_CLOCK_SECONDS 1 +/*---------------------------------------------------------------------------*/ +PROCESS(clock_test_process, "Clock test process"); +AUTOSTART_PROCESSES(&clock_test_process); +/*---------------------------------------------------------------------------*/ +#if TEST_RTIMER +void +rt_callback(struct rtimer *t, void *ptr) { + printf("Task called at %u\n", RTIMER_NOW()); +} +#endif +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(clock_test_process, ev, data) +{ + static struct etimer et; + +#if TEST_CLOCK_DELAY + static clock_time_t start_count, end_count, diff; +#endif +#if TEST_CLOCK_SECONDS + static unsigned long sec; +#endif +#if TEST_ETIMER + static clock_time_t count; +#endif +#if TEST_RTIMER + uint16_t rt_now, rt_for; + static struct rtimer rt; +#endif + static uint8_t i; + + PROCESS_BEGIN(); + +#if TEST_CLOCK_DELAY + printf("Clock delay test (10 x (10,000xi) cycles):\n"); + i = 1; + while(i < 6) { + start_count = clock_time(); + clock_delay(10000 * i); + end_count = clock_time(); + diff = end_count - start_count; + printf("Delayed %u = %u ticks = ~%u ms\n", 10000 * i, diff, diff * 8); + i++; + } +#endif + +#if TEST_RTIMER + printf("Rtimer Test (10 x 1s):\n"); + i = 0; + while(i < 10) { + etimer_set(&et, 2*CLOCK_SECOND); + puts("======================="); + rt_now = RTIMER_NOW(); + rt_for = rt_now + RTIMER_SECOND; + printf("%Now=%u - For=%u\n", rt_now, rt_for); + if (rtimer_set(&rt, rt_for, 1, + (void (*)(struct rtimer *, void *))rt_callback, NULL) != RTIMER_OK) { + printf("Error setting\n"); + } + + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + i++; + } +#endif + +#if TEST_ETIMER + printf("Clock tick and etimer test (10 x 1s):\n"); + i = 0; + while(i < 10) { + etimer_set(&et, CLOCK_SECOND); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + etimer_reset(&et); + + count = clock_time(); + printf("%u ticks\n", count); + + leds_toggle(LEDS_RED); + i++; + } +#endif + +#if TEST_CLOCK_SECONDS + printf("Clock seconds test (10 x 5s):\n"); + i = 0; + while(i < 10) { + etimer_set(&et, 5 * CLOCK_SECOND); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + etimer_reset(&et); + + sec = clock_seconds(); + printf("%u seconds\n", (u16_t) sec); + + leds_toggle(LEDS_GREEN); + i++; + } +#endif + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/udp-ipv6/Makefile b/examples/sensinode/udp-ipv6/Makefile new file mode 100644 index 000000000..00a04ec2d --- /dev/null +++ b/examples/sensinode/udp-ipv6/Makefile @@ -0,0 +1,19 @@ +ifndef TARGET +TARGET=sensinode +endif + +# Make absolutely certain that you specify your device here +DEFINES+=MODEL_N740,PROJECT_CONF_H + +# This example won't fit in flash without banking so we turn it on +HAVE_BANKING=1 +UIP_CONF_IPV6=1 + +CONTIKI_SOURCEFILES += ping6.c + +CONTIKI_PROJECT = client server +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/sensinode/udp-ipv6/client.c b/examples/sensinode/udp-ipv6/client.c new file mode 100644 index 000000000..c20643c54 --- /dev/null +++ b/examples/sensinode/udp-ipv6/client.c @@ -0,0 +1,212 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" + +#include +#include "dev/leds.h" + +#if CONTIKI_TARGET_SENSINODE +#include "dev/sensinode-sensors.h" +#include "sensinode-debug.h" +#else +#define putstring(s) +#define puthex(s) +#define putchar(s) +#endif + +#define DEBUG DEBUG_NONE +#include "net/uip-debug.h" + +#define SEND_INTERVAL 2 * CLOCK_SECOND +#define MAX_PAYLOAD_LEN 40 + +static char buf[MAX_PAYLOAD_LEN]; + +/* Our destinations and udp conns. One link-local and one global */ +#define LOCAL_CONN_PORT 3001 +static struct uip_udp_conn *l_conn; +#if UIP_CONF_ROUTER +#define GLOBAL_CONN_PORT 3002 +static struct uip_udp_conn *g_conn; +#endif + +/*---------------------------------------------------------------------------*/ +PROCESS(udp_client_process, "UDP client process"); +#if BUTTON_SENSOR_ON +PROCESS_NAME(ping6_process); +AUTOSTART_PROCESSES(&udp_client_process, &ping6_process); +#else +AUTOSTART_PROCESSES(&udp_client_process); +#endif +/*---------------------------------------------------------------------------*/ +static void +tcpip_handler(void) +{ + leds_on(LEDS_GREEN); + if(uip_newdata()) { + putstring("0x"); + puthex(uip_datalen()); + putstring(" bytes response=0x"); + puthex((*(uint16_t *) uip_appdata) >> 8); + puthex((*(uint16_t *) uip_appdata) & 0xFF); + putchar('\n'); + } + leds_off(LEDS_GREEN); + return; +} +/*---------------------------------------------------------------------------*/ +static void +timeout_handler(void) +{ + static int seq_id; + struct uip_udp_conn * this_conn; + + leds_on(LEDS_RED); + memset(buf, 0, MAX_PAYLOAD_LEN); + seq_id++; + +#if UIP_CONF_ROUTER + /* evens / odds */ + if(seq_id & 0x01) { + this_conn = l_conn; + } else { + this_conn = g_conn; + } +#else + this_conn = l_conn; +#endif + + PRINTF("Client to: "); + PRINT6ADDR(&this_conn->ripaddr); + + memcpy(buf, &seq_id, sizeof(seq_id)); + + PRINTF(" Remote Port %u,", UIP_HTONS(this_conn->rport)); + PRINTF(" (msg=0x%04x), %u bytes\n", *(uint16_t *) buf, sizeof(seq_id)); + +#if SEND_TOO_LARGE_PACKET_TO_TEST_FRAGMENTATION + uip_udp_packet_send(this_conn, buf, UIP_APPDATA_SIZE); +#else /* SEND_TOO_LARGE_PACKET_TO_TEST_FRAGMENTATION */ + uip_udp_packet_send(this_conn, buf, sizeof(seq_id)); +#endif /* SEND_TOO_LARGE_PACKET_TO_TEST_FRAGMENTATION */ + leds_off(LEDS_RED); +} +/*---------------------------------------------------------------------------*/ +static void +print_local_addresses(void) +{ + int i; + uint8_t state; + + PRINTF("Client IPv6 addresses:\n"); + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + state = uip_ds6_if.addr_list[i].state; + if(uip_ds6_if.addr_list[i].isused && (state == ADDR_TENTATIVE || state + == ADDR_PREFERRED)) { + PRINT6ADDR(&uip_ds6_if.addr_list[i].ipaddr); + if (state == ADDR_TENTATIVE) { + uip_ds6_if.addr_list[i].state = ADDR_PREFERRED; + } + PRINTF(" state: %u.\n", uip_ds6_if.addr_list[i].state); + } + } + return; +} +/*---------------------------------------------------------------------------*/ +#if UIP_CONF_ROUTER +static void +set_global_address(void) +{ + uip_ipaddr_t ipaddr; + + uip_ip6addr(&ipaddr, 0x2001, 0x630, 0x301, 0x6453, 0, 0, 0, 0); + uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr); + uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF); +} +#endif /* UIP_CONF_ROUTER */ +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(udp_client_process, ev, data) +{ + static struct etimer et; + uip_ipaddr_t ipaddr; + + PROCESS_BEGIN(); + PRINTF("UDP client process started\n"); + +#if UIP_CONF_ROUTER + set_global_address(); +#endif + + print_local_addresses(); + + uip_ip6addr(&ipaddr,0xfe80,0,0,0,0x0215,0x2000,0x0002,0x0302); + /* new connection with remote host */ + l_conn = udp_new(&ipaddr, UIP_HTONS(3000), NULL); + if(!l_conn) { + PRINTF("udp_new l_conn error.\n"); + } + udp_bind(l_conn, UIP_HTONS(LOCAL_CONN_PORT)); + + PRINTF("Link-Local connection with "); + PRINT6ADDR(&l_conn->ripaddr); + PRINTF(" local/remote port %u/%u\n", + UIP_HTONS(l_conn->lport), UIP_HTONS(l_conn->rport)); + +#if UIP_CONF_ROUTER + uip_ip6addr(&ipaddr,0x2001,0x630,0x301,0x6453,0x0215,0x2000,0x0002,0x0302); + g_conn = udp_new(&ipaddr, UIP_HTONS(3000), NULL); + if(!g_conn) { + PRINTF("udp_new g_conn error.\n"); + } + udp_bind(g_conn, UIP_HTONS(GLOBAL_CONN_PORT)); + + PRINTF("Global connection with "); + PRINT6ADDR(&g_conn->ripaddr); + PRINTF(" local/remote port %u/%u\n", + UIP_HTONS(g_conn->lport), UIP_HTONS(g_conn->rport)); +#endif + + etimer_set(&et, SEND_INTERVAL); + + while(1) { + PROCESS_YIELD(); + if(etimer_expired(&et)) { + timeout_handler(); + etimer_restart(&et); + } else if(ev == tcpip_event) { + tcpip_handler(); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/udp-ipv6/ping6.c b/examples/sensinode/udp-ipv6/ping6.c new file mode 100644 index 000000000..a88ebc7dc --- /dev/null +++ b/examples/sensinode/udp-ipv6/ping6.c @@ -0,0 +1,147 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" +#include +#include + +#if CONTIKI_TARGET_SENSINODE +#include "dev/sensinode-sensors.h" +#include "sensinode-debug.h" +#endif + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",lladdr->addr[0], lladdr->addr[1], lladdr->addr[2], lladdr->addr[3],lladdr->addr[4], lladdr->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#endif + +#define PING6_NB 5 +#define PING6_DATALEN 16 + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_ICMP_BUF ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) + +static struct etimer ping6_periodic_timer; +static u8_t count = 0; +static u16_t addr[8]; +static uip_ipaddr_t dest_addr; + +PROCESS(ping6_process, "PING6 process"); +/*---------------------------------------------------------------------------*/ +static void +ping6handler() +{ + if(count < PING6_NB) { + UIP_IP_BUF->vtc = 0x60; + UIP_IP_BUF->tcflow = 1; + UIP_IP_BUF->flow = 0; + UIP_IP_BUF->proto = UIP_PROTO_ICMP6; + UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit; + uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &dest_addr); + uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr); + + UIP_ICMP_BUF->type = ICMP6_ECHO_REQUEST; + UIP_ICMP_BUF->icode = 0; + /* set identifier and sequence number to 0 */ + memset((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN, 0, 4); + /* put one byte of data */ + memset((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN + UIP_ICMP6_ECHO_REQUEST_LEN, + count, PING6_DATALEN); + + + uip_len = UIP_ICMPH_LEN + UIP_ICMP6_ECHO_REQUEST_LEN + UIP_IPH_LEN + PING6_DATALEN; + UIP_IP_BUF->len[0] = (u8_t)((uip_len - 40) >> 8); + UIP_IP_BUF->len[1] = (u8_t)((uip_len - 40) & 0x00FF); + + UIP_ICMP_BUF->icmpchksum = 0; + UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum(); + + + PRINTF("Echo Request to"); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("from"); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("\n"); + UIP_STAT(++uip_stat.icmp.sent); + + tcpip_ipv6_output(); + + count++; + etimer_set(&ping6_periodic_timer, 3 * CLOCK_SECOND); + } else { + count = 0; + } +} + +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(ping6_process, ev, data) +{ + +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) + static struct sensors_sensor * btn; +#endif + + PROCESS_BEGIN(); + PRINTF("ping6 running.\n"); + PRINTF("Button 1: 5 pings 16 byte payload.\n"); + + uip_ip6addr(&dest_addr,0x2001,0x470,0x55,0,0x0215,0x2000,0x0002,0x0302); + count = 0; + + /* Check if we have buttons */ +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) + btn = sensors_find(BUTTON_1_SENSOR); +#endif + + while(1) { + PROCESS_YIELD(); + +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) + if(ev == sensors_event) { + if(data == btn && count == 0) { + ping6handler(); + } + } +#endif + if(etimer_expired(&ping6_periodic_timer)) { + ping6handler(); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/sensinode/udp-ipv6/project-conf.h b/examples/sensinode/udp-ipv6/project-conf.h new file mode 100644 index 000000000..672ac522b --- /dev/null +++ b/examples/sensinode/udp-ipv6/project-conf.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + * $Id$ + */ + +/** + * \file + * Project specific configuration defines for the UDP client/server + * example. + * + * We just turn on buttons + * + * \author + * George Oikonomou - + */ + +#ifndef PROJECT_CONF_H_ +#define PROJECT_CONF_H_ + +#define BUTTON_SENSOR_CONF_ON 1 + +#endif /* PROJECT_CONF_H_ */ diff --git a/examples/sensinode/udp-ipv6/server.c b/examples/sensinode/udp-ipv6/server.c new file mode 100644 index 000000000..24e490ff3 --- /dev/null +++ b/examples/sensinode/udp-ipv6/server.c @@ -0,0 +1,197 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" + +#include + +#define DEBUG DEBUG_PRINT +#include "net/uip-debug.h" +#include "dev/watchdog.h" +#include "dev/leds.h" +#include "net/rpl/rpl.h" + +#if CONTIKI_TARGET_SENSINODE +#include "dev/sensinode-sensors.h" +#include "sensinode-debug.h" +#else +#define putstring(s) +#endif + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) + +#define MAX_PAYLOAD_LEN 120 + +static struct uip_udp_conn *server_conn; +static char buf[MAX_PAYLOAD_LEN]; +static uint16_t len; + +#if UIP_CONF_ROUTER +static uip_ipaddr_t ipaddr; +#endif + +#define SERVER_REPLY 1 + +/* Should we act as RPL root? */ +#define SERVER_RPL_ROOT 1 +/*---------------------------------------------------------------------------*/ +extern const struct sensors_sensor adc_sensor; +/*---------------------------------------------------------------------------*/ +PROCESS(udp_server_process, "UDP server process"); +AUTOSTART_PROCESSES(&udp_server_process); +/*---------------------------------------------------------------------------*/ +static void +tcpip_handler(void) +{ + memset(buf, 0, MAX_PAYLOAD_LEN); + if(uip_newdata()) { + leds_on(LEDS_RED); + len = uip_datalen(); + memcpy(buf, uip_appdata, len); + PRINTF("%u bytes from [", len, *(uint16_t *)buf); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("]:%u", UIP_HTONS(UIP_UDP_BUF->srcport)); + PRINTF(" V=%u", *buf); + PRINTF(" I=%u", *(buf + 1)); + PRINTF(" T=%u", *(buf + 2)); + PRINTF(" Val=%u\n", *(uint16_t *)(buf + 3)); +#if SERVER_REPLY + uip_ipaddr_copy(&server_conn->ripaddr, &UIP_IP_BUF->srcipaddr); + server_conn->rport = UIP_UDP_BUF->srcport; + + uip_udp_packet_send(server_conn, buf, len); + /* Restore server connection to allow data from any node */ + uip_create_unspecified(&server_conn->ripaddr); + server_conn->rport = 0; +#endif + } + leds_off(LEDS_RED); + PRINTF("sent\n"); + return; +} +/*---------------------------------------------------------------------------*/ +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON && (DEBUG==DEBUG_PRINT)) +static void +print_stats() +{ + PRINTF("tl=%lu, ts=%lu, bs=%lu, bc=%lu\n", + rimestats.toolong, rimestats.tooshort, rimestats.badsynch, rimestats.badcrc); + PRINTF("llrx=%lu, lltx=%lu, rx=%lu, tx=%lu\n", + rimestats.llrx, rimestats.lltx, rimestats.rx, rimestats.tx); +} +#else +#define print_stats() +#endif +/*---------------------------------------------------------------------------*/ +static void +print_local_addresses(void) +{ + int i; + uint8_t state; + + PRINTF("Server IPv6 addresses:\n"); + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + state = uip_ds6_if.addr_list[i].state; + if(uip_ds6_if.addr_list[i].isused && (state == ADDR_TENTATIVE || state + == ADDR_PREFERRED)) { + PRINTF(" "); + PRINT6ADDR(&uip_ds6_if.addr_list[i].ipaddr); + PRINTF("\n"); + if (state == ADDR_TENTATIVE) { + uip_ds6_if.addr_list[i].state = ADDR_PREFERRED; + } + } + } +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(udp_server_process, ev, data) +{ +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) + static struct sensors_sensor *b1; + static struct sensors_sensor *b2; +#endif +#if SERVER_RPL_ROOT + rpl_dag_t *dag; +#endif + PROCESS_BEGIN(); + putstring("Starting UDP server\n"); + +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) + putstring("Button 1: Print RIME stats\n"); + putstring("Button 2: Reboot\n"); +#endif + +#if SERVER_RPL_ROOT + uip_ip6addr(&ipaddr, 0x2001, 0x630, 0x301, 0x6453, 0, 0, 0, 0); + uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr); + uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF); + + print_local_addresses(); + + dag = rpl_set_root(RPL_DEFAULT_INSTANCE, &uip_ds6_get_global(ADDR_PREFERRED)->ipaddr); + if(dag != NULL) { + uip_ip6addr(&ipaddr, 0x2001, 0x630, 0x301, 0x6453, 0, 0, 0, 0); + rpl_set_prefix(dag, &ipaddr, 64); + PRINTF("Created a new RPL dag with ID: "); + PRINT6ADDR(&dag->dag_id); + PRINTF("\n"); + } +#endif /* SERVER_RPL_ROOT */ + + server_conn = udp_new(NULL, UIP_HTONS(0), NULL); + udp_bind(server_conn, UIP_HTONS(3000)); + + PRINTF("Listen port: 3000, TTL=%u\n", server_conn->ttl); + +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) + b1 = sensors_find(BUTTON_1_SENSOR); + b2 = sensors_find(BUTTON_2_SENSOR); +#endif + + while(1) { + PROCESS_YIELD(); + if(ev == tcpip_event) { + tcpip_handler(); +#if (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) + } else if(ev == sensors_event && data != NULL) { + if(data == b1) { + print_stats(); + } else if(data == b2) { + watchdog_reboot(); + } +#endif /* (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) */ + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/sensinode/Makefile.sensinode b/platform/sensinode/Makefile.sensinode index baf3c92e9..a1daf96e6 100644 --- a/platform/sensinode/Makefile.sensinode +++ b/platform/sensinode/Makefile.sensinode @@ -1,20 +1,32 @@ # Sensinode CC2430 platform makefile # Supported products: N100, N600, N601, N710, N711 +# Support for N740 is experimental. -# /dev supports defines for product models using the following format +# We support defines for product models using the following format # e.g. MODEL_N601. Run make TARGET=sensinode DEFINES=MODEL_N601 to # automatically configure the correct LED, button, UART etc. settings -# for that product model. If no model is defined, MODEL_N100 is chosen by default. -# Models settings are defined in /dev/models.h +# for that product model. If undefined, MODEL_N100 is chosen by default. +# Model settings are defined in /dev/models.h # make sensinode.upload - Will use nano_programmer to upload file using D2xx Devboard # make sensinode.serialdump - Will use the Contiki serialdump tool on the default UART +# make foo.model - Will copy foo.ihx to foo-XYZ.ihx (e.g. foo-n740.ihx) +PATH:=$(CONTIKI)/platform/$(TARGET)/tools/bin:$(PATH) +export PATH ifndef CONTIKI $(error CONTIKI not defined! You must specify where CONTIKI resides!) endif -CFLAGS += -DRIME_CONF_NO_POLITE_ANNOUCEMENTS -RIME_CONF_NO_POLITE_ANNOUCEMENTS = 1 + +# Determine our model and (later on) add it as part of the .ihx filename +# Handy when building for various models so we can easily tell which ihx +# is for what model. +# Defaults to N100 (which is what the contiki code does as well) +MODEL_SUFFIX=n100 +ifdef DEFINES + MODEL_SUFFIX=$(patsubst MODEL_N%,n%, \ + $(filter MODEL_%,$(subst $(COMMA), ,$(DEFINES)))) +endif # Define the default UART for tools and tool commands DEFUART = /dev/ttyUSB0 @@ -22,16 +34,48 @@ PROG = $(CONTIKI)/tools/sensinode/nano_programmer/nano_programmer -d $(DEFUART) SERIALDUMP = $(CONTIKI)/tools/sky/serialdump-linux -b115200 $(DEFUART) CONTIKI_TARGET_DIRS = . dev -CONTIKI_TARGET_MAIN = ${addprefix $(OBJECTDIR)/,contiki-sensinode-main.o} +CONTIKI_TARGET_MAIN = $(addprefix $(OBJECTDIR)/,contiki-sensinode-main.rel) -CONTIKI_TARGET_SOURCEFILES = contiki-sensinode-main.c \ - leds.c leds-arch.c serial-line.c +CONTIKI_TARGET_SOURCEFILES = contiki-sensinode-main.c +CONTIKI_TARGET_SOURCEFILES += leds.c leds-arch.c serial-line.c sensors.c +CONTIKI_TARGET_SOURCEFILES += sensinode-sensors.c button-sensor.c adc-sensor.c +CONTIKI_TARGET_SOURCEFILES += n740.c models.c m25p16.c slip-arch.c slip.c +CONTIKI_TARGET_SOURCEFILES += putchar.c sensinode-debug.c CONTIKI_SOURCEFILES += $(CONTIKI_TARGET_SOURCEFILES) -.SUFFIXES: +ifdef UIP_CONF_IPV6 + ifeq ($(OFFSET_FIRMWARE),1) + CFLAGS += -DDISCO_ENABLED=1 + CONTIKI_TARGET_SOURCEFILES += disco.c + endif + CONTIKI_TARGET_SOURCEFILES += viztool.c + CONTIKI_SOURCEFILES += rpl-of0.c +endif -%.upload: %.ihx +FORCE: + +# .sensinode target so we can behave similar to other targets +# Lastly, it will create a %-$(MODEL).ihx file +%.$(TARGET): %.hex FORCE + cp $< $(<:.hex=.$(TARGET)) + if [ -f $(<:.hex=.ihx) ] ; then \ + cp $(<:.hex=.ihx) $(<:.hex=-$(MODEL_SUFFIX).ihx); fi + @echo "\nReport" + @echo "===============" + @echo 'Code footprint:' + @echo 'Area Addr Size' \ + ' Decimal' + @echo '---------------------------------- -------- --------' \ + ' --------' + @echo -n 'HOME,CSEG,CONST,XINIT,GS* $(HOME_START) ' + @egrep ',CODE\)' $(<:.hex=.map) | egrep -v '(^BANK[1-9][^=])' | uniq | \ + awk '{ SUM += $$5 } END { printf "%08X = %8d", SUM, SUM }' + @echo '. bytes (REL,CON,CODE)' + @egrep '(^BANK[1-9][^=])' $(<:.hex=.map) | uniq | sort + @egrep -A 5 'Other memory' $(<:.hex=.mem) + +%.upload: %.hex $(PROG) -P $< sensinode.serialdump: @@ -41,4 +85,4 @@ sensinode.serialdump: CONTIKI_CPU=$(CONTIKI)/cpu/cc2430 include $(CONTIKI)/cpu/cc2430/Makefile.cc2430 -contiki-$(TARGET).a:# ${addprefix $(OBJECTDIR)/,symbols.o} +contiki-$(TARGET).a:# $(addprefix $(OBJECTDIR)/,symbols.rel) diff --git a/platform/sensinode/apps/batmon/Makefile.batmon b/platform/sensinode/apps/batmon/Makefile.batmon new file mode 100644 index 000000000..1ad4809da --- /dev/null +++ b/platform/sensinode/apps/batmon/Makefile.batmon @@ -0,0 +1,3 @@ +batmon_src = batmon.c + +CFLAGS += -DBATMON_CONF_ON=1 \ No newline at end of file diff --git a/platform/sensinode/apps/batmon/batmon.c b/platform/sensinode/apps/batmon/batmon.c new file mode 100644 index 000000000..5f8797ef7 --- /dev/null +++ b/platform/sensinode/apps/batmon/batmon.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Sources for the BATtery MONitor app. It dumps a log entry to the + * external flash periodically as well as upon external trigger. + * + * It started off as a VDD and battery logger but now it also stores + * energest values and other goodies. + * + * \author + * George Oikonomou - + */ + +#include "contiki.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +#include "sys/etimer.h" +#include "sys/energest.h" +#include "dev/sensinode-sensors.h" +#include "dev/n740.h" +#include "dev/m25p16.h" + +#define BATMON_LOG_PERIOD 60 /* in seconds */ +/*---------------------------------------------------------------------------*/ +static const uint8_t magic[3] = { 0x0B, 0xEE, 0xF0 }; +/*---------------------------------------------------------------------------*/ +struct record { + uint8_t magic[3]; + uint8_t trigger; + unsigned long c; /* uptime */ + int v; /* VDD (reference) */ + int b; /* Voltage ADC */ +#if ENERGEST_CONF_ON + unsigned long mcu; + unsigned long lpm; + unsigned long irq; + unsigned long tx; + unsigned long rx; + unsigned long f_write; + unsigned long f_read; +#endif +}; + +#define RECORD_SIZE 64 +#define LAST_WRITE (0xFFFF - RECORD_SIZE) + +#define LOG_TRIGGER_PERIODIC 0xFF +/*---------------------------------------------------------------------------*/ +struct flash_address { + uint8_t s; /* sector */ + uint8_t p; /* page */ + uint8_t a; /* address */ +}; +static struct flash_address f; + +static struct record r; +static struct sensors_sensor * s; +static struct etimer et; +#define FLASH_START_ADDR 0x1E0000 +#define FLASH_END_ADDR 0x1FFFFF +/*---------------------------------------------------------------------------*/ +PROCESS(batmon_process, "Logger Process"); +/*---------------------------------------------------------------------------*/ +static int +find_gap() +{ + uint8_t seq[3]; + uint32_t address = FLASH_START_ADDR; + memset(&f, 0, sizeof(f)); + + for(address = FLASH_START_ADDR; address <= FLASH_END_ADDR; address += + RECORD_SIZE) { + n740_analog_deactivate(); + f.s = ((address & 0xFF0000) >> 16); + f.p = ((address & 0xFF00) >> 8); + f.a = address & 0xFF; + m25p16_read_fast((uint8_t *)&f, seq, sizeof(magic)); + n740_analog_activate(); + if(memcmp(seq, magic, sizeof(magic)) != 0) { + PRINTF("BatMon: Resume write @ 0x%02x%02x%02x\n", f.s, f.p, f.a); + return 1; + } + } + + /* If we reach here, we ran out of flash */ + return -1; +} +/*---------------------------------------------------------------------------*/ +static void +abort() +{ + PRINTF("BatMon: Abort\n"); + etimer_stop(&et); + process_exit(&batmon_process); +} +/*---------------------------------------------------------------------------*/ +void +batmon_log(uint8_t trigger) +{ + uint32_t next; + + /* Only continue if the process (us) is running */ + if(!process_is_running(&batmon_process)) { + return; + } + + next = f.a; + next |= (((uint32_t) f.p) << 8); + next |= (((uint32_t) f.s) << 16); + + memcpy(r.magic, magic, sizeof(magic)); + r.trigger = trigger; + r.c = clock_seconds(); + + /* Read VDD and use as ADC reference */ + r.v = s->value(ADC_SENSOR_TYPE_VDD); + + /* And then carry on with battery */ + r.b = s->value(ADC_SENSOR_TYPE_BATTERY); + +#if ENERGEST_CONF_ON + /* ENERGEST values */ + r.mcu = energest_type_time(ENERGEST_TYPE_CPU); + r.lpm = energest_type_time(ENERGEST_TYPE_LPM); + r.irq = energest_type_time(ENERGEST_TYPE_IRQ); + r.tx = energest_type_time(ENERGEST_TYPE_TRANSMIT); + r.rx = energest_type_time(ENERGEST_TYPE_LISTEN); + r.f_write = energest_type_time(ENERGEST_TYPE_FLASH_WRITE); + r.f_read = energest_type_time(ENERGEST_TYPE_FLASH_READ); +#endif + + n740_analog_deactivate(); + /* Make sure we're on */ + if(M25P16_WIP()) { + m25p16_res(); + } + m25p16_pp((uint8_t *)&f, (uint8_t *)&r, sizeof(r)); + n740_analog_activate(); + + PRINTF("BatMon: @%lu [%u] ", r.c, r.trigger); + PRINTF("BatMon: 0x%02x%02x%02x\n", f.s, f.p, f.a); + + next += RECORD_SIZE; + + if(next >= FLASH_END_ADDR) { + abort(); + return; + } + + f.s = ((next & 0xFF0000) >> 16); + f.p = ((next & 0xFF00) >> 8); + f.a = next & 0xFF; + + if(trigger == LOG_TRIGGER_PERIODIC) { + etimer_reset(&et); + } +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(batmon_process, ev, data) +{ + + PROCESS_BEGIN(); + + PRINTF("BatMon\n", sizeof(r)); + + s = sensors_find(ADC_SENSOR); + if (!s) { + PRINTF("BatMon: ADC not found\n"); + PROCESS_EXIT(); + } + + n740_analog_deactivate(); + m25p16_res(); + n740_analog_activate(); + + /* Find last written location */ + if(find_gap() == -1) { + PRINTF("BatMon: Flash storage full\n"); + PROCESS_EXIT(); + } + + etimer_set(&et, BATMON_LOG_PERIOD * CLOCK_SECOND); + + while(1) { + PROCESS_YIELD(); + if(ev == PROCESS_EVENT_TIMER && etimer_expired(&et)) { + batmon_log(LOG_TRIGGER_PERIODIC); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/sensinode/contiki-conf.h b/platform/sensinode/contiki-conf.h index 2f8148ae5..894c2174a 100644 --- a/platform/sensinode/contiki-conf.h +++ b/platform/sensinode/contiki-conf.h @@ -1,21 +1,23 @@ - #ifndef __CONTIKI_CONF_H__ #define __CONTIKI_CONF_H__ #include "8051def.h" #include "sys/cc.h" -#include #include -#include "log.h" -/* Time type. */ -/*typedef unsigned long clock_time_t;*/ -typedef unsigned short clock_time_t; +/* Include Project Specific conf */ +#ifdef PROJECT_CONF_H +#include "project-conf.h" +#endif /* PROJECT_CONF_H */ /* Defines tick counts for a second. */ #define CLOCK_CONF_SECOND 128 -#define rtimer_arch_now() clock_time() +/* The clock ISR is stack-hungry and may cause crashes. + * Define this as 0 if you are suffering from frequent stack overflows */ +#ifndef CLOCK_CONF_ACCURATE +#define CLOCK_CONF_ACCURATE 1 +#endif /* Memory filesystem RAM size. */ #define CFS_RAM_CONF_SIZE 512 @@ -23,7 +25,256 @@ typedef unsigned short clock_time_t; /* Logging.. */ #define LOG_CONF_ENABLED 0 +/* Energest Module */ +#ifndef ENERGEST_CONF_ON +#define ENERGEST_CONF_ON 0 +#endif + +/* Verbose Startup? Turning this off saves 700+ bytes of CODE in HOME */ +#define STARTUP_CONF_VERBOSE 0 + +/* More CODE space savings by turning off process names */ +#define PROCESS_CONF_NO_PROCESS_NAMES 1 + +/* + * UARTs: 1=>Enabled, 0=>Disabled. Default: Both Disabled (see uart.h) + * Disabling UART0 saves ~200 bytes of CODE. + * Disabling UART1 saves ~500 bytes of CODE but also disables all debugging + * output. Should be used when nodes are meant to run on batteries + * + * On N740, by enabling UART1, you are also enabling an ugly hack which aims + * to detect the USB connection during execution. It will then turn on/off + * UART1 RX interrupts accordingly. This seems to work but you have been warned + * If you start seeing random crashes when on battery, this is where to look. + */ +#ifndef UART_ONE_CONF_ENABLE +#define UART_ONE_CONF_ENABLE 1 +#endif +#ifndef UART_ONE_CONF_WITH_INPUT +#define UART_ONE_CONF_WITH_INPUT 0 +#endif +#define UART_ZERO_CONF_ENABLE 0 + +#ifndef UART_ONE_CONF_HIGH_SPEED +#define UART_ONE_CONF_HIGH_SPEED 0 +#endif + +/* Are we a SLIP bridge? */ +#if SLIP_ARCH_CONF_ENABLE +/* Make sure UART1 is enabled, with interrupts */ +#undef UART_ONE_CONF_ENABLE +#undef UART_ONE_CONF_WITH_INPUT +#define UART_ONE_CONF_ENABLE 1 +#define UART_ONE_CONF_WITH_INPUT 1 +#define UIP_FALLBACK_INTERFACE slip_interface +#endif + +/* Output all captured frames over the UART in hexdump format */ +#ifndef CC2430_RF_CONF_HEXDUMP +#define CC2430_RF_CONF_HEXDUMP 0 +#endif + +#if CC2430_RF_CONF_HEXDUMP +/* We need UART1 output */ +#undef UART_ONE_CONF_ENABLE +#define UART_ONE_CONF_ENABLE 1 +#endif + +/* Code Shortcuts */ +/* + * When set, the RF driver is no longer a contiki process and the RX ISR is + * disabled. Instead of polling the radio process when data arrives, we + * periodically check for data by directly invoking the driver from main() + + * When set, this directive also configures the following bypasses: + * - process_post_synch() in tcpip_input() (we call packet_input()) + * - process_post_synch() in tcpip_uipcall (we call the relevant pthread) + * - mac_call_sent_callback() is replaced with sent() in various places + * + * These are good things to do, we reduce stack usage, RAM size and code size + */ +#define SHORTCUTS_CONF_NETSTACK 1 + +/* + * Directly read mac from flash with a __code pointer, instead of using the + * generic flash_read() routine. This reduces HOME code size + */ +#define SHORTCUTS_CONF_FLASH_READ 1 + +/* + * Sensors + * It is harmless to #define XYZ 1 + * even if the sensor is not present on our device + */ +#ifndef BUTTON_SENSOR_CONF_ON +#define BUTTON_SENSOR_CONF_ON 1 /* Buttons */ +#endif +/* ADC - Turning this off will disable everything below */ +#ifndef ADC_SENSOR_CONF_ON +#define ADC_SENSOR_CONF_ON 1 +#endif +#define TEMP_SENSOR_CONF_ON 1 /* Temperature */ +#define BATTERY_SENSOR_CONF_ON 1 /* Battery */ +#define VDD_SENSOR_CONF_ON 1 /* Supply Voltage */ +#define ACC_SENSOR_CONF_ON 1 /* Accelerometer */ +#define ACC_SENSOR_CONF_GSEL 0 /* Acc. g-Select => 1: +/-11g, 0: +/-3g */ +#define LIGHT_SENSOR_CONF_ON 1 /* Light */ + +/* Watchdog */ +#define WDT_CONF_INTERVAL 0 +#define WDT_CONF_TIMER_MODE 0 /* 0 or undefined for watchdog mode */ + +/* Low Power Modes - We only support PM0/Idle and PM1 */ +#ifndef LPM_CONF_MODE +#define LPM_CONF_MODE 1 /* 0: no LPM, 1: MCU IDLE, 2: Drop to PM1 */ +#endif + +/* DMA Configuration */ +#ifndef DMA_CONF_ON +#define DMA_CONF_ON 0 +#endif + +/* N740 Serial Flash */ +#ifndef M25P16_CONF_ON +#define M25P16_CONF_ON 1 +#endif + /* XXX argh, ugly hack to make stuff compile! */ #define snprintf(BUF, SIZE, ...) sprintf(BUF, __VA_ARGS__) +/* Sensinode-Specific Tools and APPs */ +/* Viztool on by default for IPv6 builds */ +#if UIP_CONF_IPV6 +#ifndef VIZTOOL_CONF_ON +#define VIZTOOL_CONF_ON 1 +#endif /* VIZTOOL_CONF_ON */ +#endif /* UIP_CONF_IPV6 */ + +/* BatMon off by default unless we build with APPS += batmon */ +#ifndef BATMON_CONF_ON +#define BATMON_CONF_ON 0 +#endif + +/* Network Stack */ +#if UIP_CONF_IPV6 +#define NETSTACK_CONF_NETWORK sicslowpan_driver +#else +#define NETSTACK_CONF_NETWORK rime_driver +#endif + +#ifndef NETSTACK_CONF_MAC +#define NETSTACK_CONF_MAC csma_driver +#endif + +#ifndef NETSTACK_CONF_RDC +#define NETSTACK_CONF_RDC nullrdc_driver +#define NULLRDC_802154_AUTOACK 1 +#define NULLRDC_802154_AUTOACK_HW 1 +#endif + +#ifndef NETSTACK_CONF_RDC_CHANNEL_CHECK_RATE +#define NETSTACK_CONF_RDC_CHANNEL_CHECK_RATE 8 +#endif + +#define NETSTACK_CONF_FRAMER framer_802154 +#define NETSTACK_CONF_RADIO cc2430_rf_driver + +/* RF Config */ +#define IEEE802154_CONF_PANID 0x4C55 /* LU */ + +#ifndef CC2430_RF_CONF_CHANNEL +#define CC2430_RF_CONF_CHANNEL 25 +#endif /* CC2430_RF_CONF_CHANNEL */ + +#ifndef CC2430_RF_CONF_TX_POWER +#define CC2430_RF_CONF_TX_POWER 0x5F /* Datasheet recommended value */ +#endif + +#ifndef CC2430_RF_CONF_AUTOACK +#define CC2430_RF_CONF_AUTOACK 1 +#endif /* CC2430_CONF_AUTOACK */ + +#if UIP_CONF_IPV6 +/* Addresses, Sizes and Interfaces */ +/* 8-byte addresses here, 2 otherwise */ +#define RIMEADDR_CONF_SIZE 8 +#define UIP_CONF_LL_802154 1 +#define UIP_CONF_LLH_LEN 0 +#define UIP_CONF_NETIF_MAX_ADDRESSES 3 + +/* TCP, UDP, ICMP */ +#define UIP_CONF_TCP 0 +#define UIP_CONF_UDP 1 +#define UIP_CONF_UDP_CHECKSUMS 1 + +/* ND and Routing */ +#define UIP_CONF_ROUTER 1 +#define UIP_CONF_IPV6_RPL 1 +#define UIP_CONF_ND6_SEND_RA 0 +#define UIP_CONF_IP_FORWARD 0 +#define RPL_CONF_STATS 0 +#define RPL_CONF_MAX_DAG_ENTRIES 1 +#ifndef RPL_CONF_OF +#define RPL_CONF_OF rpl_of_etx +#endif + +#define UIP_CONF_ND6_REACHABLE_TIME 600000 +#define UIP_CONF_ND6_RETRANS_TIMER 10000 + +#ifndef UIP_CONF_DS6_NBR_NBU +#define UIP_CONF_DS6_NBR_NBU 4 /* Handle n Neighbors */ +#endif +#ifndef UIP_CONF_DS6_ROUTE_NBU +#define UIP_CONF_DS6_ROUTE_NBU 4 /* Handle n Routes */ +#endif + +/* uIP */ +#define UIP_CONF_BUFFER_SIZE 240 +#define UIP_CONF_IPV6_QUEUE_PKT 0 +#define UIP_CONF_IPV6_CHECKS 1 +#define UIP_CONF_IPV6_REASSEMBLY 0 + +/* 6lowpan */ +#define SICSLOWPAN_CONF_COMPRESSION SICSLOWPAN_COMPRESSION_HC06 +#ifndef SICSLOWPAN_CONF_FRAG +#define SICSLOWPAN_CONF_FRAG 0 /* About 2KB of CODE if 1 */ +#endif +#define SICSLOWPAN_CONF_MAXAGE 8 + +/* Define our IPv6 prefixes/contexts here */ +#define SICSLOWPAN_CONF_MAX_ADDR_CONTEXTS 2 +#define SICSLOWPAN_CONF_ADDR_CONTEXT_0 { \ + addr_contexts[0].prefix[0] = 0x20; \ + addr_contexts[0].prefix[1] = 0x01; \ + addr_contexts[0].prefix[2] = 0x06; \ + addr_contexts[0].prefix[3] = 0x30; \ + addr_contexts[0].prefix[4] = 0x03; \ + addr_contexts[0].prefix[5] = 0x01; \ + addr_contexts[0].prefix[6] = 0x64; \ + addr_contexts[0].prefix[7] = 0x53; \ +} +#define SICSLOWPAN_CONF_ADDR_CONTEXT_1 { \ + addr_contexts[1].prefix[0] = 0x20; \ + addr_contexts[1].prefix[1] = 0x01; \ + addr_contexts[1].prefix[2] = 0x06; \ + addr_contexts[1].prefix[3] = 0x30; \ + addr_contexts[1].prefix[4] = 0x03; \ + addr_contexts[1].prefix[5] = 0x01; \ + addr_contexts[1].prefix[6] = 0x11; \ + addr_contexts[1].prefix[7] = 0x00; \ +} + +#define MAC_CONF_CHANNEL_CHECK_RATE 8 +#ifndef QUEUEBUF_CONF_NUM +#define QUEUEBUF_CONF_NUM 6 +#endif + +#else /* UIP_CONF_IPV6 */ +/* Network setup for non-IPv6 (rime). */ +#define UIP_CONF_IP_FORWARD 1 +#define UIP_CONF_BUFFER_SIZE 108 +#define RIME_CONF_NO_POLITE_ANNOUCEMENTS 0 +#define QUEUEBUF_CONF_NUM 8 +#endif /* UIP_CONF_IPV6 */ + #endif /* __CONTIKI_CONF_H__ */ diff --git a/platform/sensinode/contiki-sensinode-main.c b/platform/sensinode/contiki-sensinode-main.c index dc59c6cb7..a1f9abb9d 100644 --- a/platform/sensinode/contiki-sensinode-main.c +++ b/platform/sensinode/contiki-sensinode-main.c @@ -1,44 +1,83 @@ -#include - #include "contiki.h" #include "sys/clock.h" #include "sys/autostart.h" #include "dev/serial-line.h" +#include "dev/slip.h" #include "dev/bus.h" #include "dev/leds.h" -#include "dev/uart.h" +#include "dev/uart1.h" +#include "dev/dma.h" #include "dev/models.h" #include "dev/cc2430_rf.h" -#include "net/mac/sicslowmac.h" -#include "net/mac/frame802154.h" +#include "dev/watchdog.h" +#include "dev/lpm.h" #include "net/rime.h" +#include "net/netstack.h" +#include "net/mac/frame802154.h" +#include "sensinode-debug.h" +#include "dev/watchdog-cc2430.h" +#include "dev/sensinode-sensors.h" +#include "disco.h" +#include "contiki-lib.h" +#include "contiki-net.h" -volatile int i, a; unsigned short node_id = 0; /* Manually sets MAC address when > 0 */ -/*---------------------------------------------------------------------------*/ -static void -print_processes(struct process * const processes[]) -{ - printf("Starting"); - while(*processes != NULL) { - printf(" '%s'", (*processes)->name); - processes++; - } - printf("\n"); -} -/*---------------------------------------------------------------------------*/ -void -putchar(char c) -{ - /* UART1 used for debugging on Sensinode products. */ - uart1_writeb(c); -} +#if VIZTOOL_CONF_ON +PROCESS_NAME(viztool_process); +#endif + +#if BATMON_CONF_ON +PROCESS_NAME(batmon_process); +#endif + +#if SHORTCUTS_CONF_NETSTACK +static __data int len; +#endif + +#ifdef STARTUP_CONF_VERBOSE +#define STARTUP_VERBOSE STARTUP_CONF_VERBOSE +#else +#define STARTUP_VERBOSE 0 +#endif + +#if STARTUP_VERBOSE +#define PUTSTRING(...) putstring(__VA_ARGS__) +#define PUTHEX(...) puthex(__VA_ARGS__) +#define PUTBIN(...) putbin(__VA_ARGS__) +#define PUTCHAR(...) putchar(__VA_ARGS__) +#else +#define PUTSTRING(...) do {} while(0) +#define PUTHEX(...) do {} while(0) +#define PUTBIN(...) do {} while(0) +#define PUTCHAR(...) do {} while(0) +#endif + + +#if !CLOCK_CONF_ACCURATE +extern volatile __data clock_time_t count; +/* accurate clock is stack hungry */ +extern volatile __bit sleep_flag; +#endif + +extern rimeaddr_t rimeaddr_node_addr; +static __data int r; +#if ENERGEST_CONF_ON +static unsigned long irq_energest = 0; +#define ENERGEST_IRQ_SAVE(a) do { \ + a = energest_type_time(ENERGEST_TYPE_IRQ); } while(0) +#define ENERGEST_IRQ_RESTORE(a) do { \ + energest_type_set(ENERGEST_TYPE_IRQ, a); } while(0) +#else +#define ENERGEST_IRQ_SAVE(a) do {} while(0) +#define ENERGEST_IRQ_RESTORE(a) do {} while(0) +#endif /*---------------------------------------------------------------------------*/ static void fade(int l) { + volatile int i, a; int k, j; for(k = 0; k < 400; ++k) { j = k > 200? 400 - k: k; @@ -57,52 +96,86 @@ fade(int l) static void set_rime_addr(void) { - rimeaddr_t addr; - uint8_t ft_buffer[8]; uint8_t *addr_long = NULL; uint16_t addr_short = 0; - int i; + char i; - /* TODO: This flash_read routine currently gets a different address - * than nano_programmer -m... something broken or misconfigured... - */ - - flash_read(&ft_buffer[0], 0x1FFF8, 8); - - printf("Read MAC from flash: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - ft_buffer[0], ft_buffer[1], ft_buffer[2], ft_buffer[3], - ft_buffer[4], ft_buffer[5], ft_buffer[6], ft_buffer[7]); - - memset(&addr, 0, sizeof(rimeaddr_t)); - -#if UIP_CONF_IPV6 - memcpy(addr.u8, ft_buffer, sizeof(addr.u8)); +#if SHORTCUTS_CONF_FLASH_READ + __code unsigned char * macp; #else - if(node_id == 0) { - for(i = 0; i < sizeof(rimeaddr_t); ++i) { - addr.u8[i] = ft_buffer[7 - i]; - } - } else { - printf("Setting manual address from node_id\n"); - addr.u8[1] = node_id >> 8; - addr.u8[0] = node_id & 0xff; - } + static uint8_t ft_buffer[8]; #endif - rimeaddr_set_node_addr(&addr); - printf("Rime configured with address "); - for(i = (sizeof(addr.u8)) - 1; i > 0; i--) { - printf("%02x:", addr.u8[i]); + PUTSTRING("Rime is 0x"); + PUTHEX(sizeof(rimeaddr_t)); + PUTSTRING(" bytes long\n"); + + if(node_id == 0) { + PUTSTRING("Reading MAC from flash\n"); +#if SHORTCUTS_CONF_FLASH_READ + /* + * The MAC is always stored in 0x1FFF8 of our flash. This maps to address + * 0xFFF8 of our CODE segment, when BANK3 is selected. + * Switch to BANK3, read 8 bytes starting at 0xFFF8 and restore last BANK + * Since we are called from main(), this MUST be BANK1 or something is very + * wrong. This code can be used even without banking + */ + + /* Don't interrupt us to make sure no BANK switching happens while working */ + DISABLE_INTERRUPTS(); + + /* Switch to BANK3, map CODE: 0x8000 – 0xFFFF to FLASH: 0x18000 – 0x1FFFF */ + FMAP = 3; + + /* Set our pointer to the correct address and fetch 8 bytes of MAC */ + macp = (__code unsigned char *) 0xFFF8; + + for(i = (RIMEADDR_SIZE - 1); i >= 0; --i) { + rimeaddr_node_addr.u8[i] = *macp; + macp++; + } + + /* Remap 0x8000 – 0xFFFF to BANK1 */ + FMAP = 1; + ENABLE_INTERRUPTS(); +#else + /* + * Or use the more generic flash_read() routine which can read from any + * address of our flash + */ + flash_read(ft_buffer, 0x1FFF8, 8); + + /* Flip the byte order and store MSB first */ + for(i = (RIMEADDR_SIZE - 1); i >= 0; --i) { + rimeaddr_node_addr.u8[RIMEADDR_SIZE - 1 - i] = ft_buffer[i]; + } +#endif + + } else { + PUTSTRING("Setting manual address from node_id\n"); + rimeaddr_node_addr.u8[RIMEADDR_SIZE - 1] = node_id >> 8; + rimeaddr_node_addr.u8[RIMEADDR_SIZE - 2] = node_id & 0xff; } - printf("%02x\n", addr.u8[i]); + + /* Now the address is stored MSB first */ +#if STARTUP_VERBOSE + PUTSTRING("Rime configured with address "); + for(i = 0; i < RIMEADDR_SIZE - 1; i++) { + PUTHEX(rimeaddr_node_addr.u8[i]); + PUTCHAR(':'); + } + PUTHEX(rimeaddr_node_addr.u8[i]); + PUTCHAR('\n'); +#endif /* Set the cc2430 RF addresses */ - if (sizeof(addr.u8) == 6) - addr_long = (uint8_t *) addr.u8; - else - addr_short = (addr.u8[1] * 256) + addr.u8[0]; - - cc2430_rf_set_addr(0xffff, addr_short, addr_long); +#if (RIMEADDR_SIZE==8) + addr_short = (rimeaddr_node_addr.u8[6] * 256) + rimeaddr_node_addr.u8[7]; + addr_long = (uint8_t *) &rimeaddr_node_addr; +#else + addr_short = (rimeaddr_node_addr.u8[0] * 256) + rimeaddr_node_addr.u8[1]; +#endif + cc2430_rf_set_addr(IEEE802154_PANID, addr_short, addr_long); } /*---------------------------------------------------------------------------*/ int @@ -111,37 +184,220 @@ main(void) /* Hardware initialization */ bus_init(); + rtimer_init(); + /* model-specific h/w init. */ + model_init(); + + /* Init LEDs here */ leds_init(); fade(LEDS_GREEN); - uart1_init(115200); - uart1_set_input(serial_line_input_byte); - /* initialize process manager. */ process_init(); + /* Init UART1 */ + uart1_init(); + +#if DMA_ON + dma_init(); +#endif + +#if SLIP_ARCH_CONF_ENABLE + /* On cc2430, the argument is not used */ + slip_arch_init(0); +#else + uart1_set_input(serial_line_input_byte); serial_line_init(); +#endif - printf("\n" CONTIKI_VERSION_STRING " started\n"); - printf("model: " SENSINODE_MODEL "\n\n"); + PUTSTRING("##########################################\n"); + putstring(CONTIKI_VERSION_STRING "\n"); + putstring(SENSINODE_MODEL " (CC24"); + puthex(((CHIPID >> 3) | 0x20)); + putstring("-" FLASH_SIZE ")\n"); - /* initialize the radio driver */ +#if STARTUP_VERBOSE +#ifdef HAVE_SDCC_BANKING + PUTSTRING(" With Banking.\n"); +#endif /* HAVE_SDCC_BANKING */ +#ifdef SDCC_MODEL_LARGE + PUTSTRING(" --model-large\n"); +#endif /* SDCC_MODEL_LARGE */ +#ifdef SDCC_MODEL_HUGE + PUTSTRING(" --model-huge\n"); +#endif /* SDCC_MODEL_HUGE */ +#ifdef SDCC_STACK_AUTO + PUTSTRING(" --stack-auto\n"); +#endif /* SDCC_STACK_AUTO */ - cc2430_rf_init(); - rime_init(sicslowmac_init(&cc2430_rf_driver)); - set_rime_addr(); + PUTCHAR('\n'); + + PUTSTRING(" Net: "); + PUTSTRING(NETSTACK_NETWORK.name); + PUTCHAR('\n'); + PUTSTRING(" MAC: "); + PUTSTRING(NETSTACK_MAC.name); + PUTCHAR('\n'); + PUTSTRING(" RDC: "); + PUTSTRING(NETSTACK_RDC.name); + PUTCHAR('\n'); + + PUTSTRING("##########################################\n"); +#endif + + watchdog_init(); + + /* Initialise the cc2430 RNG engine. */ + random_init(0); /* start services */ process_start(&etimer_process, NULL); + ctimer_init(); + + /* initialize the netstack */ + netstack_init(); + set_rime_addr(); + +#if BUTTON_SENSOR_ON || ADC_SENSOR_ON + process_start(&sensors_process, NULL); + sensinode_sensors_activate(); +#endif + +#if UIP_CONF_IPV6 + memcpy(&uip_lladdr.addr, &rimeaddr_node_addr, sizeof(uip_lladdr.addr)); + queuebuf_init(); + process_start(&tcpip_process, NULL); + +#if DISCO_ENABLED + process_start(&disco_process, NULL); +#endif /* DISCO_ENABLED */ + +#if VIZTOOL_CONF_ON + process_start(&viztool_process, NULL); +#endif + +#if (!UIP_CONF_IPV6_RPL) + { + uip_ipaddr_t ipaddr; + + uip_ip6addr(&ipaddr, 0x2001, 0x630, 0x301, 0x6453, 0, 0, 0, 0); + uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr); + uip_ds6_addr_add(&ipaddr, 0, ADDR_TENTATIVE); + } +#endif /* UIP_CONF_IPV6_RPL */ +#endif /* UIP_CONF_IPV6 */ + + /* + * Acknowledge the UART1 RX interrupt + * now that we're sure we are ready to process it + */ + model_uart_intr_en(); + + energest_init(); + ENERGEST_ON(ENERGEST_TYPE_CPU); fade(LEDS_RED); +#if BATMON_CONF_ON + process_start(&batmon_process, NULL); +#endif + autostart_start(autostart_processes); + watchdog_start(); + while(1) { - process_run(); - etimer_request_poll(); + do { + /* Reset watchdog and handle polls and events */ + watchdog_periodic(); + + /**/ +#if !CLOCK_CONF_ACCURATE + if(sleep_flag) { + if(etimer_pending() && + (etimer_next_expiration_time() - count - 1) > MAX_TICKS) { /*core/sys/etimer.c*/ + etimer_request_poll(); + } + sleep_flag = 0; + } +#endif + r = process_run(); + } while(r > 0); +#if SHORTCUTS_CONF_NETSTACK + len = NETSTACK_RADIO.pending_packet(); + if(len) { + packetbuf_clear(); + len = NETSTACK_RADIO.read(packetbuf_dataptr(), PACKETBUF_SIZE); + if(len > 0) { + packetbuf_set_datalen(len); + NETSTACK_RDC.input(); + } + } +#endif + +#if LPM_MODE +#if (LPM_MODE==LPM_MODE_PM2) + SLEEP &= ~OSC_PD; /* Make sure both HS OSCs are on */ + while(!(SLEEP & HFRC_STB)); /* Wait for RCOSC to be stable */ + CLKCON |= OSC; /* Switch to the RCOSC */ + while(!(CLKCON & OSC)); /* Wait till it's happened */ + SLEEP |= OSC_PD; /* Turn the other one off */ +#endif /* LPM_MODE==LPM_MODE_PM2 */ + + /* + * Set MCU IDLE or Drop to PM1. Any interrupt will take us out of LPM + * Sleep Timer will wake us up in no more than 7.8ms (max idle interval) + */ + SLEEP = (SLEEP & 0xFC) | (LPM_MODE - 1); + +#if (LPM_MODE==LPM_MODE_PM2) + /* + * Wait 3 NOPs. Either an interrupt occurred and SLEEP.MODE was cleared or + * no interrupt occurred and we can safely power down + */ + __asm + nop + nop + nop + __endasm; + + if (SLEEP & SLEEP_MODE0) { +#endif /* LPM_MODE==LPM_MODE_PM2 */ + + ENERGEST_OFF(ENERGEST_TYPE_CPU); + ENERGEST_ON(ENERGEST_TYPE_LPM); + + /* We are only interested in IRQ energest while idle or in LPM */ + ENERGEST_IRQ_RESTORE(irq_energest); + + /* Go IDLE or Enter PM1 */ + PCON |= IDLE; + + /* First instruction upon exiting PM1 must be a NOP */ + __asm + nop + __endasm; + + /* Remember energest IRQ for next pass */ + ENERGEST_IRQ_SAVE(irq_energest); + + ENERGEST_ON(ENERGEST_TYPE_CPU); + ENERGEST_OFF(ENERGEST_TYPE_LPM); + +#if (LPM_MODE==LPM_MODE_PM2) + SLEEP &= ~OSC_PD; /* Make sure both HS OSCs are on */ + while(!(SLEEP & XOSC_STB)); /* Wait for XOSC to be stable */ + CLKCON &= ~OSC; /* Switch to the XOSC */ + /* + * On occasion the XOSC is reported stable when in reality it's not. + * We need to wait for a safeguard of 64us or more before selecting it + */ + clock_delay(10); + while(CLKCON & OSC); /* Wait till it's happened */ + } +#endif /* LPM_MODE==LPM_MODE_PM2 */ +#endif /* LPM_MODE */ } } /*---------------------------------------------------------------------------*/ diff --git a/platform/sensinode/dev/adc-sensor.c b/platform/sensinode/dev/adc-sensor.c index e69de29bb..8de2deb04 100644 --- a/platform/sensinode/dev/adc-sensor.c +++ b/platform/sensinode/dev/adc-sensor.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ADC sensor module for sensinode devices. + * + * This file respects configuration in contiki-conf.h. It also turns + * off features which are not present in the model that we are + * building for. + * + * \author + * George Oikonomou - + */ +#include "dev/sensinode-sensors.h" + +#if ADC_SENSOR_ON +SENSORS_SENSOR(adc_sensor, ADC_SENSOR, value, configure, status); + +static uint8_t ready; + +/*---------------------------------------------------------------------------*/ +static int +value(int type) +{ + uint16_t reading; + /* + * For single-shot AD conversions, we may only write to ADCCON3[3:0] once + * (This write triggers the conversion). We thus use the variable 'command' + * to store intermediate steps (reference, decimation rate, input channel) + */ + uint8_t command; + + ADCCFG = 0; /* Enables/Disables Input Channel */ + + /* 1.25V ref, max decimation rate */ + command = ADEDIV1 | ADEDIV0; + + /* Clear the Interrupt Flag */ + TCON_ADCIF = 0; + + /* Depending on the desired reading, append the input bits to 'command' and + * enable the corresponding input channel in ADCCFG if necessary */ + switch(type) { +#if TEMP_SENSOR_ON + case ADC_SENSOR_TYPE_TEMP: + command |= ADECH3 | ADECH2 | ADECH1; + break; +#endif +#if ACC_SENSOR_ON + case ADC_SENSOR_TYPE_ACC_X: + ADCCFG = ADC5EN; + command |= ADECH2 | ADECH0; + break; + case ADC_SENSOR_TYPE_ACC_Y: + ADCCFG = ADC6EN; + command |= ADECH2 | ADECH1; + break; + case ADC_SENSOR_TYPE_ACC_Z: + ADCCFG = ADC7EN; + command |= ADECH2 | ADECH1 | ADECH0; + break; +#endif +#if VDD_SENSOR_ON + case ADC_SENSOR_TYPE_VDD: + command |= ADECH3 | ADECH2 | ADECH1 | ADECH0; + break; +#endif +#if LIGHT_SENSOR_ON + case ADC_SENSOR_TYPE_LIGHT: + ADCCFG = ADC0EN; + break; +#endif +#if BATTERY_SENSOR_ON + case ADC_SENSOR_TYPE_BATTERY: + ADCCFG = ADC1EN; + command |= ADECH0 | ADEREF1; /* AVDD_SOC reference */ + break; +#endif + default: + /* If the sensor is not present or disabled in conf, return -1 */ + return -1; + } + + /* Writing in bits 3:0 of ADCCON3 will trigger a single conversion */ + ADCCON3 = command; + + /* + * When the conversion is complete, the ADC interrupt flag is set. We don't + * use an ISR here, we just wait on the flag and clear it afterwards. + */ + while(!TCON_ADCIF); + + /* Clear the Interrupt Flag */ + TCON_ADCIF = 0; + + reading = 0; + reading = ADCL; + reading |= (((uint8_t) ADCH) << 8); + /* 12-bit decimation rate: 4 LS bits are noise */ + reading >>= 4; + + return reading; +} +/*---------------------------------------------------------------------------*/ +static int +status(int type) +{ + return ready; + } +/*---------------------------------------------------------------------------*/ +/* + * On N740 we can control Ill and Acc individually: + * ADC_VAL_OTHERS 0x01 + * ADC_VAL_LIGHT_ON 0x04 + * ADC_VAL_ACC_ON 0x08 + * ADC_VAL_ACC_GSEL 0x10 + * + * Return Value is always light | acc | acc_gsel + * + * SENSORS_ACTIVE: + * - 1: Activate everything, use default setting for ACC G-select + * - 0: Turn everything off + * - xyz: Mask with the defines above and act accordingly. + * + * SENSORS_READY: + * - Return Status (0: all off or a value based on the defines above) + */ +static int +configure(int type, int value) +{ +#ifdef MODEL_N740 + /* + * Read current state of the ser-par, ignoring current sensor settings + * Those will be set all over depending on VALUE + */ + uint8_t ser_par_val = n740_ser_par_get() & 0xF2; +#endif /* MODEL_N740 */ + + /* 'Others' are either compiled in or not. Can't be turned on/off */ + ready = ADC_VAL_ALL; + + switch(type) { + case SENSORS_HW_INIT: + case SENSORS_ACTIVE: +#ifdef MODEL_N740 + if(value == ADC_VAL_ALL) { + value = ADC_VAL_ACC_ON | ADC_VAL_LIGHT_ON; +#if ACC_SENSOR_GSEL + value |= ADC_VAL_ACC_GSEL; +#endif /* ACC_SENSOR_GSEL */ + } +#endif /* MODEL_N740 */ + + /* OK, Now value definitely specifies our bits, start masking + * We will refuse to turn things on if they are specified OFF in conf. */ +#ifdef MODEL_N740 +#if ACC_SENSOR_ON + if(value & ADC_VAL_ACC_ON) { + P0SEL |= 0x80 | 0x40 | 0x20; + ser_par_val |= N740_SER_PAR_ACC; + ready |= ADC_VAL_ACC_ON; +#if ACC_SENSOR_GSEL + if(value & ADC_VAL_ACC_GSEL) { + ser_par_val |= N740_SER_PAR_ACC_GSEL; + ready |= ADC_VAL_ACC_GSEL; + } +#endif /*ACC_SENSOR_GSEL */ + } +#endif /* ACC_SENSOR_ON */ + +#if LIGHT_SENSOR_ON + if(value & ADC_VAL_LIGHT_ON) { + ser_par_val |= N740_SER_PAR_LIGHT; + ready |= ADC_VAL_LIGHT_ON; + } +#endif /* LIGHT_SENSOR_ON */ + n740_ser_par_set(ser_par_val); +#endif /* MODEL_N740 */ + } + return ready; +} + +#endif /* ADC_SENSOR_ON */ diff --git a/platform/sensinode/dev/button-sensor.c b/platform/sensinode/dev/button-sensor.c index deccf2290..ac239fe09 100644 --- a/platform/sensinode/dev/button-sensor.c +++ b/platform/sensinode/dev/button-sensor.c @@ -27,87 +27,174 @@ * SUCH DAMAGE. * * This file is part of the Contiki operating system. - * - * @(#)$Id: button-sensor.c,v 1.1 2009/09/08 20:06:28 zdshelby Exp $ */ -#include "lib/sensors.h" -/*#include "dev/hwconf.h"*/ -#include "dev/button-sensor.h" -#include "dev/leds.h" - -const struct sensors_sensor button_sensor; - -static struct timer debouncetimer; - /* -HWCONF_PIN(BUTTON, 2, 7); -HWCONF_IRQ(BUTTON, 2, 7); -*/ + * Portions of this file build on button-sensor.c in platforms sky and esb + * This file contains ISRs: Keep it in the HOME bank. + */ + +#include "dev/models.h" +#include "lib/sensors.h" +#include "dev/hwconf.h" +#include "dev/sensinode-sensors.h" + +#if BUTTON_SENSOR_ON +static uint8_t p0ien; +static uint8_t p2ien; +static __data struct timer debouncetimer[2]; + +#ifdef MODEL_N740 +HWCONF_PIN(BUTTON_1, 1, 0) +HWCONF_PORT_1_IRQ(BUTTON_1, 0) +HWCONF_PIN(BUTTON_2, 0, 4) +HWCONF_PORT_0_IRQ(BUTTON_2, 4) +#endif /* MODEL_N740 */ + +#ifdef MODEL_N711 +HWCONF_PIN(BUTTON_1, 0, 6) +HWCONF_PORT_0_IRQ(BUTTON_1, 6) +HWCONF_PIN(BUTTON_2, 0, 7) +HWCONF_PORT_0_IRQ(BUTTON_2, 7) +#endif /* MODEL_N711 */ /*---------------------------------------------------------------------------*/ -static void -init(void) +static +int value_b1(int type) { - timer_set(&debouncetimer, 0); - BUTTON_IRQ_EDGE_SELECTD(); - - BUTTON_SELECT(); - BUTTON_MAKE_INPUT(); + return BUTTON_1_READ() || !timer_expired(&debouncetimer[0]); } /*---------------------------------------------------------------------------*/ -static int -irq(void) +static +int status_b1(int type) { - if(BUTTON_CHECK_IRQ()) { - if(timer_expired(&debouncetimer)) { - timer_set(&debouncetimer, CLOCK_SECOND / 4); - sensors_changed(&button_sensor); - return 1; + switch (type) { + case SENSORS_ACTIVE: + case SENSORS_READY: + return BUTTON_1_IRQ_ENABLED(); + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static +int configure_b1(int type, int value) +{ + switch(type) { + case SENSORS_HW_INIT: + /* Generates INT when pressed */ + BUTTON_1_IRQ_EDGE_SELECTD(); + BUTTON_1_SELECT(); + BUTTON_1_MAKE_INPUT(); + return 1; + case SENSORS_ACTIVE: + if(value) { + if(!BUTTON_1_IRQ_ENABLED()) { + timer_set(&debouncetimer[0], 0); + BUTTON_1_IRQ_FLAG_OFF(); + BUTTON_1_ENABLE_IRQ(); +} + } else { + BUTTON_1_DISABLE_IRQ(); + } + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static +int value_b2(int type) +{ + return BUTTON_2_READ() || !timer_expired(&debouncetimer[1]); +} +/*---------------------------------------------------------------------------*/ +static +int status_b2(int type) +{ + switch (type) { + case SENSORS_ACTIVE: + case SENSORS_READY: + return BUTTON_2_IRQ_ENABLED(); + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static +int configure_b2(int type, int value) +{ + switch(type) { + case SENSORS_HW_INIT: + /* Generates INT when released */ + /* BUTTON_2_IRQ_EDGE_SELECTD(); */ + BUTTON_2_SELECT(); + BUTTON_2_MAKE_INPUT(); + return 1; + case SENSORS_ACTIVE: + if(value) { + if(!BUTTON_2_IRQ_ENABLED()) { + timer_set(&debouncetimer[1], 0); + BUTTON_2_IRQ_FLAG_OFF(); + BUTTON_2_ENABLE_IRQ(); + } + } else { + BUTTON_2_DISABLE_IRQ(); + } + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +void +port_0_ISR(void) __interrupt (P0INT_VECTOR) +{ + EA = 0; + ENERGEST_ON(ENERGEST_TYPE_IRQ); + + /* This ISR is for the entire port. Check if the interrupt was caused by our + * button's pin. */ + /* Check B1 for N711 */ +#ifdef MODEL_N711 + if(BUTTON_1_CHECK_IRQ()) { + if(timer_expired(&debouncetimer[0])) { + timer_set(&debouncetimer[0], CLOCK_SECOND / 4); + sensors_changed(&button_1_sensor); } } +#endif /* MODEL_N711 */ + if(BUTTON_2_CHECK_IRQ()) { + if(timer_expired(&debouncetimer[1])) { + timer_set(&debouncetimer[1], CLOCK_SECOND / 4); + sensors_changed(&button_2_sensor); + } + } + P0IFG = 0; + IRCON_P0IF = 0; + ENERGEST_OFF(ENERGEST_TYPE_IRQ); + EA = 1; +} +/*---------------------------------------------------------------------------*/ +/* We only need this ISR for N740 */ +#ifdef MODEL_N740 +void +port_1_ISR(void) __interrupt (P1INT_VECTOR) +{ + EA = 0; + ENERGEST_ON(ENERGEST_TYPE_IRQ); - return 0; + /* This ISR is for the entire port. Check if the interrupt was caused by our + * button's pin. This can only be B1 for N740 */ + if(BUTTON_1_CHECK_IRQ()) { + if(timer_expired(&debouncetimer[0])) { + timer_set(&debouncetimer[0], CLOCK_SECOND / 4); + sensors_changed(&button_1_sensor); + } + } + P1IFG = 0; + IRCON2_P1IF = 0; + ENERGEST_OFF(ENERGEST_TYPE_IRQ); + EA = 1; } -/*---------------------------------------------------------------------------*/ -static void -activate(void) -{ - sensors_add_irq(&button_sensor, BUTTON_IRQ_PORT()); - BUTTON_ENABLE_IRQ(); -} -/*---------------------------------------------------------------------------*/ -static void -deactivate(void) -{ - BUTTON_DISABLE_IRQ(); - sensors_remove_irq(&button_sensor, BUTTON_IRQ_PORT()); -} -/*---------------------------------------------------------------------------*/ -static int -active(void) -{ - return BUTTON_IRQ_ENABLED(); -} -/*---------------------------------------------------------------------------*/ -static unsigned int -value(int type) -{ - return BUTTON_READ() || !timer_expired(&debouncetimer); -} -/*---------------------------------------------------------------------------*/ -static int -configure(int type, void *c) -{ - return 0; -} -/*---------------------------------------------------------------------------*/ -static void * -status(int type) -{ - return NULL; -} -/*---------------------------------------------------------------------------*/ -SENSORS_SENSOR(button_sensor, BUTTON_SENSOR, - init, irq, activate, deactivate, active, - value, configure, status); +#endif /* MODEL_N740 */ + +SENSORS_SENSOR(button_1_sensor, BUTTON_1_SENSOR, value_b1, configure_b1, status_b1); +SENSORS_SENSOR(button_2_sensor, BUTTON_2_SENSOR, value_b2, configure_b2, status_b2); +#endif /* BUTTON_SENSOR_ON */ diff --git a/platform/sensinode/dev/button-sensor.h b/platform/sensinode/dev/button-sensor.h new file mode 100644 index 000000000..df3ee6e19 --- /dev/null +++ b/platform/sensinode/dev/button-sensor.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005, Swedish Institute of Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Override core/dev/button-sensor.h + * + * We simply include "dev/sensinode-sensors.h". We do this so that apps + * and examples including button-sensor.h will compile for sensinodes + * + * \author + * George Oikonomou - + */ + +#ifndef __BUTTON_SENSOR_H__ +#define __BUTTON_SENSOR_H__ + +#include "dev/sensinode-sensors.h" + +#endif /* __BUTTON_SENSOR_H__ */ diff --git a/platform/sensinode/dev/leds-arch.c b/platform/sensinode/dev/leds-arch.c index 08616f98b..5d4154d2f 100644 --- a/platform/sensinode/dev/leds-arch.c +++ b/platform/sensinode/dev/leds-arch.c @@ -6,14 +6,22 @@ /* * Sensinode v1.0 HW products have 2 red LEDs, LED1 is mapped to the Contiki - * LEDS_RED and LED2 is mapped to LEDS_GREEN. + * LEDS_GREEN and LED2 is mapped to LEDS_RED. */ /*---------------------------------------------------------------------------*/ void leds_arch_init(void) { +#ifdef MODEL_N740 + /* + * We don't need explicit led initialisation for N740s. They are controlled + * by the ser/par chip which is initalised already + */ + return; +#else P0DIR |= 0x30; +#endif } /*---------------------------------------------------------------------------*/ unsigned char @@ -21,29 +29,62 @@ leds_arch_get(void) { unsigned char l = 0; - if(LED1_PIN) { - l |= LEDS_RED; - } - if(LED2_PIN) { +#ifdef MODEL_N740 + /* Read the current ser-par chip status */ + uint8_t ser_par; + ser_par = n740_ser_par_get(); + /* Check bits 7 & 8, ignore the rest */ + if(ser_par & N740_SER_PAR_LED_GREEN) { l |= LEDS_GREEN; } + if(ser_par & N740_SER_PAR_LED_RED) { + l |= LEDS_RED; + } +#else + if(LED1_PIN) { + l |= LEDS_GREEN; + } + if(LED2_PIN) { + l |= LEDS_RED; + } +#endif return l; } /*---------------------------------------------------------------------------*/ void leds_arch_set(unsigned char leds) { +#ifdef MODEL_N740 + /* Read the current ser-par chip status - we want to change bits 7 & 8 but + * the remaining bit values should be retained */ + uint8_t ser_par; + ser_par = n740_ser_par_get(); + if(leds & LEDS_GREEN) { + ser_par |= N740_SER_PAR_LED_GREEN; /* Set bit 7 */ + } else { + ser_par &= ~N740_SER_PAR_LED_GREEN; /* Unset bit 7 */ + } + if(leds & LEDS_RED) { + ser_par |= N740_SER_PAR_LED_RED; /* Set bit 8 */ + } else { + ser_par &= ~N740_SER_PAR_LED_RED; /* Unset bit 8 */ + } + + /* Write the new status back to the chip */ + n740_ser_par_set(ser_par); +#else + if(leds & LEDS_GREEN) { LED1_PIN = 1; } else { LED1_PIN = 0; } - if(leds & LEDS_GREEN) { + if(leds & LEDS_RED) { LED2_PIN = 1; } else { LED2_PIN = 0; } - +#endif } /*---------------------------------------------------------------------------*/ diff --git a/platform/sensinode/dev/m25p16.c b/platform/sensinode/dev/m25p16.c new file mode 100644 index 000000000..5239dbf74 --- /dev/null +++ b/platform/sensinode/dev/m25p16.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * This file provides functions to control the M25P16 on sensinode N740s. + * This is a Numonyx Forte Serial Flash Memory (16Mbit) + * The S signal (Chip Select) is controlled via 0x02 on the 74HC595D + * The other instructions and timing are performed with bit bang + * + * We can enable, disable, read/write data, erase pages, hold, enter/exit + * deep sleep etc. + * + * Clock (C) => P1_5, + * Ser. I (D) => P1_6, + * Ser. O (Q) => P1_7, + * Hold => Pull Up, + * Write Prot => Pull Up, + * Chip Sel => 74HC595D (0x02) + * + * This file can be placed in any bank. + * + * \author + * George Oikonomou - + */ + +#include "dev/n740.h" +#include "dev/m25p16.h" +#include "sys/clock.h" +#include "sys/energest.h" +#include "cc2430_sfr.h" + +#define CLOCK_RISING() {M25P16_PIN_CLOCK = 1; M25P16_PIN_CLOCK = 0;} +#define CLOCK_FALLING() {M25P16_PIN_CLOCK = 0; M25P16_PIN_CLOCK = 1;} +/*---------------------------------------------------------------------------*/ +/* Bit-Bang write a byte to the chip */ +static void +bit_bang_write(uint8_t byte) +{ + uint8_t i; + uint8_t bit; + + /* bit-by-bit */ + for(i = 0x80; i > 0; i >>= 1) { + /* Is the bit set? */ + bit = 0; + if(i & byte) { + /* If it was set, we want to send 1 */ + bit = 1; + } + /* Send the bit */ + M25P16_PIN_SER_I = bit; + /* Clock - Rising */ + CLOCK_RISING(); + } +} +/*---------------------------------------------------------------------------*/ +/* Bit-Bang read a byte from the chip */ +static uint8_t +bit_bang_read() +{ + int8_t i; + uint8_t bits = 0; + + /* bit-by-bit */ + for(i = 7; i >= 0; i--) { + /* Clock - Falling */ + CLOCK_FALLING(); + + /* Read the bit */ + bits |= (M25P16_PIN_SER_O << i); + } + return bits; +} +/*---------------------------------------------------------------------------*/ +static void +select() +{ + /* Read current ser/par value */ + uint8_t ser_par = n740_ser_par_get(); + + M25P16_PIN_CLOCK = 0; + + ser_par &= ~N740_SER_PAR_CHIP_SEL; /* Select Flash */ + + /* Write the new status back to the ser/par */ + n740_ser_par_set(ser_par); +} +/*---------------------------------------------------------------------------*/ +static void +deselect() +{ + /* Read current ser/par value */ + uint8_t ser_par = n740_ser_par_get(); + + ser_par |= N740_SER_PAR_CHIP_SEL; /* De-Select Flash */ + + /* Write the new status back to the ser/par */ + n740_ser_par_set(ser_par); +} +/*---------------------------------------------------------------------------*/ +void +m25p16_wren() +{ + select(); + bit_bang_write(M25P16_I_WREN); + deselect(); + + while(!M25P16_WEL()); +} +/*---------------------------------------------------------------------------*/ +void +m25p16_wrdi() +{ + select(); + bit_bang_write(M25P16_I_WRDI); + deselect(); +} +/*---------------------------------------------------------------------------*/ +void +m25p16_rdid(struct m25p16_rdid * rdid) +{ + uint8_t i; + + select(); + bit_bang_write(M25P16_I_RDID); + + rdid->man_id = bit_bang_read(); + rdid->mem_type = bit_bang_read(); /* Device ID MSB */ + rdid->mem_size = bit_bang_read(); /* Device ID LSB */ + rdid->uid_len = bit_bang_read(); + for(i = 0; i < rdid->uid_len; i++) { + rdid->uid[i] = bit_bang_read(); + } + deselect(); +} +/*---------------------------------------------------------------------------*/ +uint8_t +m25p16_rdsr() +{ + uint8_t rv; + + select(); + bit_bang_write(M25P16_I_RDSR); + rv = bit_bang_read(); + deselect(); + + return rv; +} +/*---------------------------------------------------------------------------*/ +void +m25p16_wrsr(uint8_t val) +{ + m25p16_wren(); /* Write Enable */ + + select(); + ENERGEST_ON(ENERGEST_TYPE_FLASH_WRITE); + bit_bang_write(M25P16_I_WRSR); + bit_bang_write(val); + ENERGEST_OFF(ENERGEST_TYPE_FLASH_WRITE); + deselect(); +} +/*---------------------------------------------------------------------------*/ +void +m25p16_read(uint8_t * addr, uint8_t * buff, uint8_t buff_len) +{ + uint8_t i; + + select(); + ENERGEST_ON(ENERGEST_TYPE_FLASH_READ); + +#if M25P16_READ_FAST + bit_bang_write(M25P16_I_FAST_READ); +#else + bit_bang_write(M25P16_I_READ); +#endif + + /* Write the address, MSB in addr[0], bits [7:5] of the MSB: 'don't care' */ + for(i = 0; i < 3; i++) { + bit_bang_write(addr[i]); + } + + /* For FAST_READ, send the dummy byte */ +#if M25P16_READ_FAST + bit_bang_write(M25P16_DUMMY_BYTE); +#endif + + for(i = 0; i < buff_len; i++) { + buff[i] = ~bit_bang_read(); + } + ENERGEST_OFF(ENERGEST_TYPE_FLASH_READ); + deselect(); +} +/*---------------------------------------------------------------------------*/ +void +m25p16_pp(uint8_t * addr, uint8_t * buff, uint8_t buff_len) +{ + uint8_t i; + + m25p16_wren(); /* Write Enable */ + + select(); + ENERGEST_ON(ENERGEST_TYPE_FLASH_WRITE); + bit_bang_write(M25P16_I_PP); + + /* Write the address, MSB in addr[0] */ + for(i = 0; i < 3; i++) { + bit_bang_write(addr[i]); + } + + /* Write the bytes */ + for(i=0; i + */ + +#ifndef M25P16_H_ +#define M25P16_H_ + +/* Instruction Set */ +#define M25P16_I_WREN 0x06 /* Write Enable */ +#define M25P16_I_WRDI 0x04 /* Write Disable */ +#define M25P16_I_RDID 0x9F /* Read Identification */ +#define M25P16_I_RDSR 0x05 /* Read Status Register */ +#define M25P16_I_WRSR 0x01 /* Write Status Register */ +#define M25P16_I_READ 0x03 /* Read Data Bytes */ +#define M25P16_I_FAST_READ 0x0B /* Read Data Bytes at Higher Speed */ +#define M25P16_I_PP 0x02 /* Page Program */ +#define M25P16_I_SE 0xD8 /* Sector Erase */ +#define M25P16_I_BE 0xC7 /* Bulk Erase */ +#define M25P16_I_DP 0xB9 /* Deep Power-down */ +#define M25P16_I_RES 0xAB /* Release from Deep Power-down */ + +/* Dummy Byte - Used in FAST_READ and RES */ +#define M25P16_DUMMY_BYTE 0x00 + +/* Pins */ +#define M25P16_PIN_CLOCK P1_5 +#define M25P16_PIN_SER_I P1_6 +#define M25P16_PIN_SER_O P1_7 + +/* Status Register Bits */ +#define M25P16_SR_SRWD 0x80 /* Status Register Write Disable */ +#define M25P16_SR_BP2 0x10 /* Block Protect 2 */ +#define M25P16_SR_BP1 0x08 /* Block Protect 1 */ +#define M25P16_SR_BP0 0x04 /* Block Protect 0 */ +#define M25P16_SR_BP 0x1C /* All Block Protect Bits */ +#define M25P16_SR_WEL 0x02 /* Write Enable Latch */ +#define M25P16_SR_WIP 0x01 /* Write in Progress */ + +/* Do we use READ or FAST_READ to read? Fast by default */ +#ifdef M25P16_CONF_READ_FAST +#define M25P16_READ_FAST M25P16_CONF_READ_FAST +#else +#define M25P16_READ_FAST 1 +#endif +/*---------------------------------------------------------------------------*/ +/** \brief Device Identifier + * + * Holds the value of the device identifier, returned by the RDID instruction. + * + * After a correct RDID, this structure should hold the following values: + * man_id = 0x20, mem_type = 0x20, mem_size = 0x15, uid_len = 0x10. + * + * UID holds optional Customized Factory Data (CFD) content. The CFD bytes are + * read-only and can be programmed with customers data upon their request. + * If the customers do not make requests, the devices are shipped with all the + * CFD bytes programmed to 0x00. + */ +struct m25p16_rdid { + uint8_t man_id; /** Manufacturer ID */ + uint8_t mem_type; /** Memory Type */ + uint8_t mem_size; /** Memory Size */ + uint8_t uid_len; /** Unique ID length */ + uint8_t uid[16]; /** Unique ID */ +}; +/*---------------------------------------------------------------------------*/ +/** + * \brief Retrieve Block Protect Bits from the status register + * + * This macro returns the software block protect status on the device + * by reading the value of the BP bits ([5:3]) in the Status Register + */ +#define M25P16_BP() (m25p16_rdsr() & M25P16_SR_BP) +/** + * \brief Check for Write in Progress + * \retval 1 Write in progress + * \retval 0 Write not in progress + * + * This macro checks if the device is currently in the middle of a write cycle + * by reading the value of the WIP bit (bit 0) in the Status Register + */ +#define M25P16_WIP() (m25p16_rdsr() & M25P16_SR_WIP) +/** + * \brief Check for Write-Enable + * \retval 1 Write enabled + * \retval 0 Write disabled + * + * This macro checks if the device is ready to accept a write instruction + * by reading the value of the WEL bit (bit 1) in the Status Register + */ +#define M25P16_WEL() (m25p16_rdsr() & M25P16_SR_WEL) +/*---------------------------------------------------------------------------*/ +/** + * \brief Write Enable (WREN) instruction. + * + * Completing a WRDI, PP, SE, BE and WRSR + * resets the write enable latch bit, so this instruction should be used every + * time before trying to write. + */ +void m25p16_wren(); + +/** + * \brief Write Disable (WRDI) instruction + */ +void m25p16_wrdi(); + +/** + * \brief Read Identifier (RDID)instruction + * + * \param rdid Pointer to a struct which will hold the information returned + * by the RDID instruction + */ +void m25p16_rdid(struct m25p16_rdid * rdid); + +/** + * \brief Read Status Register (RDSR) instruction + * + * \return Value of the status register + * + * Reads and returns the value of the status register on the Flash Chip + */ +uint8_t m25p16_rdsr(); + +/** + * \brief Write Status Register (WRSR) instruction + * \param val Value to be written to the status register + * + * This instruction does not afect bits 6, 5, 1 and 0 of the SR. + */ +void m25p16_wrsr(uint8_t val); + +/** + * \brief Read Data Bytes (READ) instruction + * \param addr 3 byte array holding the read start address. MSB stored in + * addr[0] and LSB in addr[2] + * \param buff Pointer to a buffer to hold the read bytes. + * \param buff_len Number of bytes to read. buff must be long enough to hold + * buff_len bytes + * + * The bytes will be inverted after being read, so that a value of 0xFF (empty) + * in the flash will read as 0x00 + */ +void m25p16_read(uint8_t * addr, uint8_t * buff, uint8_t buff_len); + +/** + * \brief Program Page (PP) instruction + * \param addr 3 byte array holding the write start address. MSB stored in + * addr[0] and LSB in addr[2] + * \param buff Pointer to a buffer with the data to be written + * \param buff_len Number of bytes to write, Maximum 256 bytes. + * + * Write BUFF_LEN bytes stored in BUFF to flash, starting from location + * ADDR. BUFF_LEN may not exceed 256. ADDR should point to a 3 byte array, + * with the address MSB stored in position 0 and LSB in position 2 + * + * If the start address + buff_len exceed page boundaries, the write will + * wrap to the start of the same page (the page at addr[2:1]). + * + * The bytes will be inverted before being written, so that a value of 0xFF + * will be written as 0x00 (and subsequently correctly read as 0xFF by READ) + * + * This function will set the WEL bit on the SR before attempting to write, + * so the calling function doesn't need to worry about this. + * + * This call is asynchronous. It will return before the write cycle has + * completed. Thus, user software must check the WIP bit Write In Progress) + * before sending further instructions. This can take up to 5 msecs (typical + * duration for a 256 byte write is 640 usec) + */ +void m25p16_pp(uint8_t * addr, uint8_t * buff, uint8_t buff_len); + +/** + * \brief Sector Erase (SE) instruction + * \param s The number of the sector to be erased + * + * Delete the entire sector number s, by setting it's contents to all 0xFF + * (which will read as 0x00 by READ). The flash is broken down into 32 sectors, + * 64 KBytes each. + * + * This function will set the WEL bit on the SR before attempting to write, + * so the calling function doesn't need to worry about this. + * + * This call is asynchronous. It will return before the write cycle has + * completed. Thus, user software must check the WIP bit Write In Progress) + * before sending further instructions. This can take up to 3 secs (typical + * duration 600 msec) + */ +void m25p16_se(uint8_t s); /* Sector Erase */ + + +/** + * \brief Bulk Erase (SE) instruction + * + * Delete the entire memory, by setting it's contents to all 0xFF + * (which will read as 0x00 by READ). + * + * This function will set the WEL bit on the SR before attempting to write, + * so the calling function doesn't need to worry about this. + * + * This call is asynchronous. It will return before the write cycle has + * completed. Thus, user software must check the WIP bit Write In Progress) + * before sending further instructions. + * + * This instructions takes a very long time to complete and must be used with + * care. It can take up to 40 secs (yes, secs). A typical duration is 13 secs + */ +void m25p16_be(); + +/** + * \brief Deep Power Down (DP) instruction + * + * Puts the device into its lowers power consumption mode (This is not the same + * as the stand-by mode caused by de-selecting the device). While the device + * is in DP, it will accept no instruction except a RES (Release from DP). + * + * This call is asynchronous and will return as soon as the instruction + * sequence has been written but before the device has actually entered DP + * + * Dropping to DP takes 3usec and Resuming from DP takes at least 1.8usec, so + * this sequence should not be used when the sleep interval is estimated to be + * short (read as: don't DP then RES then DP repeatedly) + */ +void m25p16_dp(); /* Deep Power down */ + +/** + * \brief Release from Deep Power Down (RES) instruction + * + * Take the device out of the Deep Power Down mode and bring it to standby. + * Does not read the electronic signature. + * + * This call is synchronous. When it returns the device will be in standby + * mode. + * + * Dropping to DP takes 3usec and Resuming from DP takes at least 1.8usec, so + * this sequence should not be used when the sleep interval is estimated to be + * short (read as: don't DP then RES then DP repeatedly) + */ +void m25p16_res(); + +/** + * \brief Release from Deep Power Down (RES) and Read Electronic + * Signature instruction + * + * \return The value of the electronic signature. This is provided for backward + * compatibility and must always be 0x14 + * + * Take the device out of the Deep Power Down mode and bring it to standby. + * Does not read the electronic signature. + * + * This call is synchronous. When it returns the device will be in standby + * mode. + * + * Dropping to DP takes 3usec and Resuming from DP takes at least 1.8usec, so + * this sequence should not be used when the sleep interval is estimated to be + * short (read as: don't DP then RES then DP repeatedly) + */ +uint8_t m25p16_res_res(); + +#endif /* M25P16_H_ */ diff --git a/platform/sensinode/dev/models.c b/platform/sensinode/dev/models.c new file mode 100644 index 000000000..15b21fffa --- /dev/null +++ b/platform/sensinode/dev/models.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Model-specific functions for Sensinode devices. + * + * Bankable + * + * \author + * George Oikonomou - + */ + +#include "dev/models.h" +#include "dev/uart1.h" +#include "dev/m25p16.h" +/*---------------------------------------------------------------------------*/ +void +model_init() +{ +#ifdef MODEL_N740 + /* + * We want to prevent the dongle from controlling the state of the + * analog switch on the N740s. + * + * Set P0_3 as out and start with P0_3=0 (USB and Light selected) + */ + P0DIR |= 0x08; /* P0_3 out */ + P0_3 = 0; + + /* Init the serial-parallel chip for N740s. This will also 'init' LEDs */ + n740_ser_par_init(); + + /* Put the Serial Flash in Deep Power mode */ + n740_analog_deactivate(); + +#if M25P16_CONF_ON + m25p16_dp(); +#endif /* SERIAL_FLASH_CONF_ON */ + + n740_ser_par_set(0); +#endif +} +/*---------------------------------------------------------------------------*/ +void +model_uart_intr_en() +{ +#ifdef MODEL_N740 + /* + * Dirty, ugly hack for the N740 USART1 RX issue: + * When the USB is for whatever reason disabled (either disconnected or the + * analog switch has switched to the D-connector), RX starts flowing down + * pin 1.7 (and the line stays low), resulting in non-stop UART1_RX + * interrupts. So, we only acknowledge the interrupt when the line is + * high and the dongle is connected (thus we are on USB). + * + * For all other models, just turn the interrupt on + * + * Interrupts will only turn on if UART_ONE_CONF_WITH_INPUT is defined 1 + */ + if(P1_7 == 1 && P0_3 == 0) { + UART1_RX_INT(1); + } +#else + UART1_RX_INT(1); +#endif +} diff --git a/platform/sensinode/dev/models.h b/platform/sensinode/dev/models.h index 22e1e99f5..500932b17 100644 --- a/platform/sensinode/dev/models.h +++ b/platform/sensinode/dev/models.h @@ -3,31 +3,44 @@ /* Define model text */ #ifdef MODEL_N100 -#define SENSINODE_MODEL "N100 Module (CC2431-F128)" +#define SENSINODE_MODEL "N100 Module" #endif #ifdef MODEL_N600 -#define SENSINODE_MODEL "N600 NanoRouter USB (CC2430-F128)" +#define SENSINODE_MODEL "N600 NanoRouter USB" #endif #ifdef MODEL_N601 -#define SENSINODE_MODEL "N601 NanoRouter USB (CC2431-F128)" +#define SENSINODE_MODEL "N601 NanoRouter USB" #endif #ifdef MODEL_N710 -#define SENSINODE_MODEL "N710 NanoSensor (CC2430-F128)" +#define SENSINODE_MODEL "N710 NanoSensor" #endif #ifdef MODEL_N711 -#define SENSINODE_MODEL "N711 NanoSensor (CC2431-F128)" +#define SENSINODE_MODEL "N711 NanoSensor" +#endif +#ifdef MODEL_N740 +#define SENSINODE_MODEL "N740 NanoSensor" #endif #ifndef SENSINODE_MODEL -#define MODEL_N100 -#define SENSINODE_MODEL "Sensinode N100 (CC2431-F128)" +#define SENSINODE_MODEL "N100 Module" #endif -/* All current models use these LED pins */ +#ifndef FLASH_SIZE +#define FLASH_SIZE "F128" +#endif + +/* + * N740 has a serial-parallel chip onboard + * Defines and functions to control it + */ +#ifdef MODEL_N740 +#include "dev/n740.h" + +#else +/* All other models use these LED pins */ #define LED1_PIN P0_4 #define LED2_PIN P0_5 - -/* Buttons */ +#endif #ifdef MODEL_N711 #define BUTTON1_PIN P0_6 @@ -41,4 +54,7 @@ #define TEMP_PIN P0_1 #endif +/* Model-Specific startup functions */ +void model_init(); +void model_uart_intr_en(); #endif /* __MODELS_H__ */ diff --git a/platform/sensinode/dev/n740.c b/platform/sensinode/dev/n740.c new file mode 100644 index 000000000..d47aa3f6a --- /dev/null +++ b/platform/sensinode/dev/n740.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * This file provides functions to control various chips on the + * Sensinode N740s: + * + * - The 74HC595D is an 8-bit serial in-parallel out shift register. + * LEDs are connected to this chip. It also serves other functions such as + * enabling/disabling the Accelerometer (see n740.h). + * - The 74HC4053D is a triple, 2-channel analog mux/de-mux. + * It switches I/O between the USB and the D-Connector. + * It also controls P0_0 input source (Light Sensor / External I/O) + * + * Mux/De-mux: Connected to P0_3 (set to output in models.c + * Changing the state of the mux/demux can have catastrophic (tm) results + * on our software. If we are not careful, we risk entering a state where + * UART1 RX interrupts are being generated non-stop. Only change its state + * via the function in this file. + * + * Shift Register: + * For the shift register we can: + * - write a new instruction + * - remember and retrieve the last instruction sent + * + * The chip is connected to CPU pins as follows: + * - P0_2: Serial Data Input + * - P1_3: Shift Register Clock Input + * - P1_1: Storage Register Clock + * + * This file can be placed in any bank. + * + * \author + * George Oikonomou - + */ + +#include "dev/n740.h" +#include "dev/uart1.h" + +/* + * This variable stores the most recent instruction sent to the ser-par chip. + * We declare it as static and return its value through n740_ser_par_get(). + */ +static __data uint8_t ser_par_status; + +/*---------------------------------------------------------------------------*/ +/* Init the serial-parallel chip: + * - Set I/O direction for all 3 pins (P0_2, P1_1 and P1_3) to output + */ +void +n740_ser_par_init() +{ + /* bus_init and uart1_init also touch the I/O direction for those pins */ + P1DIR |= 0x0A; + P0DIR |= 0x04; +} +/*---------------------------------------------------------------------------*/ +/* + * Send a command to the N740 serial-parallel chip. Each command is a single + * byte, each bit controls a different feature on the sensor. + */ +void +n740_ser_par_set(uint8_t data) +{ + uint8_t i; + uint8_t mask = 1; + uint8_t temp = 0; + + DISABLE_INTERRUPTS(); + /* bit-by-bit */ + for(i = 0; i < 8; i++) { + temp = (data & mask); + /* Is the bit set? */ + if(i && temp) { + /* If it was set, we want to send 1 */ + temp >>= i; + } + /* Send the bit */ + P0_2 = temp; + /* Shift */ + P1_3 = 1; + P1_3 = 0; + mask <<= 1; + } + /* Move to Par-Out */ + P1_1 = 1; + P1_1 = 0; + ENABLE_INTERRUPTS(); + + /* Right, we're done. Save the new status in ser_par_status */ + ser_par_status = data; +} +/*---------------------------------------------------------------------------*/ +/* This function returns the last value sent to the ser-par chip on the N740. + * + * The caveat here is that we must always use n740_set_ser_par() to send + * commands to the ser-par chip, never write directly. + * + * If any other function sends a command directly, ser_par_status and the + * actual status will end up out of sync. + */ +uint8_t +n740_ser_par_get() +{ + return ser_par_status; +} +/*---------------------------------------------------------------------------*/ +void +n740_analog_switch(uint8_t state) +{ + /* Turn off the UART RX interrupt before switching */ + DISABLE_INTERRUPTS(); + UART1_RX_INT(0); + + /* Switch */ + P0_3 = state; + + /* If P0_3 now points to the USB and nothing is flowing down P1_7, + * enable the interrupt again */ + if(P1_7 == 1 && P0_3 == N740_ANALOG_SWITCH_USB) { + UART1_RX_INT(1); + } + ENABLE_INTERRUPTS(); +} +/*---------------------------------------------------------------------------*/ +/* + * Activate the the 74HC4053D analog switch U5 on the N740 and at the same + * time set relevant pins to Peripheral I/O mode. This stops communications + * with the serial flash and enables UART1 I/O + */ +void +n740_analog_activate() +{ + uint8_t ser_par = n740_ser_par_get(); + ser_par &= ~N740_SER_PAR_U5_ENABLE; /* Turn on */ + + N740_PINS_PER_IO(); + + n740_ser_par_set(ser_par); +} +/*---------------------------------------------------------------------------*/ +/* + * De-Activate the the 74HC4053D analog switch U5 on the N740 and at the same + * time set relevant pins to GP I/O mode. This effectively prepares us to + * start talking with the serial flash chip + */ +void +n740_analog_deactivate() +{ + uint8_t ser_par = n740_ser_par_get(); + ser_par |= N740_SER_PAR_U5_ENABLE; /* Turn off */ + + n740_ser_par_set(ser_par); + + N740_PINS_GPIO(); +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/sensinode/dev/n740.h b/platform/sensinode/dev/n740.h new file mode 100644 index 000000000..3af1f2821 --- /dev/null +++ b/platform/sensinode/dev/n740.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Header File for the module which controls the Sensinode N740 + * 8-bit serial-in/serial or parallel-out shift register. + * + * This is where the Accelerometer, Leds, Light and Battery Sensors + * are connected. + * + * \author + * George Oikonomou - + */ + +#ifndef N740SERPAR_H_ +#define N740SERPAR_H_ + +#include "8051def.h" + +#define N740_SER_PAR_ACC_GSEL 0x01 /* Acceleration Sensor g-Select */ +#define N740_SER_PAR_CHIP_SEL 0x02 /* Flash Chip Select */ +#define N740_SER_PAR_LIGHT 0x04 /* Light Sensor */ +#define N740_SER_PAR_ACC 0x08 /* Acceleration Sensor */ +#define N740_SER_PAR_RF_IN_GAIN 0x10 /* Receiver Amplifier, best not set */ +#define N740_SER_PAR_U5_ENABLE 0x20 /* U5 analog switch enable */ +#define N740_SER_PAR_LED_GREEN 0x40 /* Led 1 */ +#define N740_SER_PAR_LED_RED 0x80 /* Led 2 */ + +#define N740_ANALOG_SWITCH_USB 0 +#define N740_ANALOG_SWITCH_SERIAL 1 + +#define N740_PINS 0xE0 +#define N740_PINS_GPIO() {P1SEL &= ~N740_PINS;} +#define N740_PINS_PER_IO() {P1SEL |= N740_PINS;} + +/* Serial/Parallel Shift Register (74HC595D) Functions */ +void n740_ser_par_init(void); +void n740_ser_par_set(uint8_t data) ; +uint8_t n740_ser_par_get(void); + +/* Analog Switch (U5 - 74HC4053D) Functions */ +void n740_analog_switch(uint8_t state); +void n740_analog_activate(); +void n740_analog_deactivate(); + +#endif /* N740SERPAR_H_ */ diff --git a/platform/sensinode/dev/sensinode-sensors.c b/platform/sensinode/dev/sensinode-sensors.c new file mode 100644 index 000000000..f94878077 --- /dev/null +++ b/platform/sensinode/dev/sensinode-sensors.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * This module centrally controls all sensors on sensinode devices + * + * It respects configuration in contiki-conf.h + * + * \author + * George Oikonomou - + */ + +#include "dev/sensinode-sensors.h" +#include "sys/energest.h" + +const struct sensors_sensor *sensors[] = { +#if ADC_SENSOR_ON + &adc_sensor, +#endif +#if BUTTON_SENSOR_ON + &button_1_sensor, + &button_2_sensor, +#endif + 0 +}; + +unsigned char sensors_flags[(sizeof(sensors) / sizeof(struct sensors_sensor *))]; + +/*---------------------------------------------------------------------------*/ +void +sensinode_sensors_activate() +{ + struct sensors_sensor *sensor; + sensor = sensors_first(); + while (sensor) { + sensor->configure(SENSORS_ACTIVE, 1); + sensor = sensors_next(sensor); + } + ENERGEST_ON(ENERGEST_TYPE_SENSORS); +} +/*---------------------------------------------------------------------------*/ +void +sensinode_sensors_deactivate() +{ + struct sensors_sensor *sensor; + sensor = sensors_first(); + while (sensor) { + sensor->configure(SENSORS_ACTIVE, 0); + sensor = sensors_next(sensor); + } + ENERGEST_OFF(ENERGEST_TYPE_SENSORS); +} diff --git a/platform/sensinode/dev/sensinode-sensors.h b/platform/sensinode/dev/sensinode-sensors.h new file mode 100644 index 000000000..503499ec8 --- /dev/null +++ b/platform/sensinode/dev/sensinode-sensors.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Defines for the sensors on the various Sensinode models. + * + * Sensors will be off by default, unless turned on explicitly + * in contiki-conf.h + * + * If you enable sensors generating interrupts, make sure you include + * this file in the file containing main(). + * + * \author + * George Oikonomou - + */ + +#ifndef SENSINODE_SENSORS_H_ +#define SENSINODE_SENSORS_H_ + +#include "cc2430_sfr.h" +#include "contiki-conf.h" +#include "dev/models.h" +#include "lib/sensors.h" + +void sensinode_sensors_activate(); +void sensinode_sensors_deactivate(); + +/* ADC Sensor Types */ +#define ADC_SENSOR "ADC" + +#define ADC_SENSOR_TYPE_TEMP 0 +#define ADC_SENSOR_TYPE_ACC_X 1 +#define ADC_SENSOR_TYPE_ACC_Y 2 +#define ADC_SENSOR_TYPE_ACC_Z 3 +#define ADC_SENSOR_TYPE_VDD 4 +#define ADC_SENSOR_TYPE_LIGHT 5 +#define ADC_SENSOR_TYPE_BATTERY 6 + +/* Defines to help us control Acc and Ill individually */ +#define ADC_VAL_NONE 0x00 +#define ADC_VAL_ALL 0x01 +#define ADC_VAL_LIGHT_ON 0x04 +#define ADC_VAL_ACC_ON 0x08 +#define ADC_VAL_ACC_GSEL 0x10 + +#ifdef ADC_SENSOR_CONF_ON +#define ADC_SENSOR_ON ADC_SENSOR_CONF_ON +#endif /* ADC_SENSOR_CONF_ON */ + +#if ADC_SENSOR_ON +extern const struct sensors_sensor adc_sensor; +#endif /* ADC_SENSOR_ON */ + +/* + * Accelerometer. Available on N740 only. + * This is a Freescale Semiconductor MMA7340L (3 axis, 3/11g) + * X: P0_5 + * Y: P0_6 + * Z: P0_7 + */ +#ifdef MODEL_N740 +#ifdef ACC_SENSOR_CONF_ON +#define ACC_SENSOR_ON ACC_SENSOR_CONF_ON +#endif /* ACC_SENSOR_CONF_ON */ + +/* Accelerometer g-Select - Define for +/-11g, +/-3g Otherwise */ +#if ACC_SENSOR_ON +#ifdef ACC_SENSOR_CONF_GSEL +#define ACC_SENSOR_GSEL ACC_SENSOR_CONF_GSEL +#endif /* ACC_SENSOR_CONF_GSEL */ +#endif /* ACC_SENSOR_ON */ +#endif /* MODEL_N740 */ + +/* + * Buttons + * N740: P1_0, P0_4 + * N711: P0_6, P0_7 + * N710: Unknown. This will mainly influence which ISRs to declare here + */ +#if defined(MODEL_N740) || defined(MODEL_N711) +#ifdef BUTTON_SENSOR_CONF_ON +#define BUTTON_SENSOR_ON BUTTON_SENSOR_CONF_ON +#endif /* BUTTON_SENSOR_CONF_ON */ +#endif /* defined(MODEL_FOO) */ + +#define BUTTON_1_SENSOR "Button 1" +#define BUTTON_2_SENSOR "Button 2" +#define BUTTON_SENSOR BUTTON_1_SENSOR + +#if BUTTON_SENSOR_ON +extern const struct sensors_sensor button_1_sensor; +extern const struct sensors_sensor button_2_sensor; +#define button_sensor button_1_sensor + +/* Port 0 ISR needed for both models */ +void port_0_ISR(void) __interrupt (P0INT_VECTOR); + +/* Only declare the Port 1 ISR for N740 */ +#ifdef MODEL_N740 +void port_1_ISR(void) __interrupt (P1INT_VECTOR); +#endif /* MODEL_N740 */ +#endif /* BUTTON_SENSOR_ON */ + +/* + * Light - N710, N711, N740 + * N740: P0_0 + * N711: P0_0 + * N710: P?_? + */ +#if defined(MODEL_N740) || defined(MODEL_N711) || defined(MODEL_N710) +#ifdef LIGHT_SENSOR_CONF_ON +#define LIGHT_SENSOR_ON LIGHT_SENSOR_CONF_ON +#endif /* LIGHT_SENSOR_CONF_ON */ +#endif /* defined(MODEL_FOO) */ + +/* + * Battery - N711, N740, (N710 likely) + * N740: P0_1 + * Unknown for other models + */ +#if defined(MODEL_N740) || defined(MODEL_N711) || defined(MODEL_N710) +#ifdef BATTERY_SENSOR_CONF_ON +#define BATTERY_SENSOR_ON BATTERY_SENSOR_CONF_ON +#endif /* BATTERY_SENSOR_CONF_ON */ +#endif /* defined(MODEL_FOO) */ + +/* Temperature - Available on all cc2430 devices */ +#ifdef TEMP_SENSOR_CONF_ON +#define TEMP_SENSOR_ON TEMP_SENSOR_CONF_ON +#endif /* TEMP_SENSOR_CONF_ON */ + +/* Supply Voltage - Available on all cc2430 devices*/ +#ifdef VDD_SENSOR_CONF_ON +#define VDD_SENSOR_ON VDD_SENSOR_CONF_ON +#endif /* VDD_SENSOR_CONF_ON */ + +#endif /* SENSINODE_SENSORS_H_ */ diff --git a/platform/sensinode/dev/slip-arch.c b/platform/sensinode/dev/slip-arch.c new file mode 100644 index 000000000..bf6e1b291 --- /dev/null +++ b/platform/sensinode/dev/slip-arch.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * @(#)$Id: slip_uart1.c,v 1.8 2008/02/03 20:59:35 adamdunkels Exp $ + */ + +/* + * Sensinode/cc2430 SLIP routines (over UART1). + */ + +#include "dev/slip.h" +#include "dev/uart1.h" +/*---------------------------------------------------------------------------*/ +void +slip_arch_writeb(unsigned char c) +{ + uart1_writeb(c); +} +/*---------------------------------------------------------------------------*/ +void +slip_arch_init(unsigned long ubr) +{ + uart1_set_input(slip_input_byte); +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/sensinode/disco.c b/platform/sensinode/disco.c new file mode 100644 index 000000000..12c1146cb --- /dev/null +++ b/platform/sensinode/disco.c @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Disco server sources + * (embedded part of the DISCOBALL project) + * + * It objective is to receive a code file over UDP, store it in + * external flash and disseminate it to other nodes of the + * 6LoWPAN network. + * + * For this to work, the image must be co-hosted with the BooTTY! + * bootloader, which will move the image from external to internal + * flash. + * + * To link this application in your contiki image, all you need to + * do is to add this line: + * OFFSET_FIRMWARE=1 + * to your project's makefile + * + * \author + * George Oikonomou - + */ + +#include "contiki.h" +#include "contiki-net.h" +#include "sys/clock.h" +#include "sys/ctimer.h" +#include "dev/watchdog.h" + +#include "dev/n740.h" +#include "dev/m25p16.h" + +#include "disco.h" +/*---------------------------------------------------------------------------*/ +#define DEBUG DEBUG_NONE +#include "net/uip-debug.h" +/*---------------------------------------------------------------------------*/ +#if BATMON_CONF_ENABLED +void batmon_log(uint8_t trigger); + +#define LOG_TRIGGER_OAP_DISCO_START 0x01 +#define LOG_TRIGGER_OAP_DISCO_DONE 0x02 +#define LOG_TRIGGER_OAP_DISCO_ABORT 0x03 +#else +#define batmon_log(t) do { } while(0); +#endif +/*---------------------------------------------------------------------------*/ +static struct uip_udp_conn *server_conn; +static struct disco_request_pdu * req; +static struct disco_response_pdu resp; +static struct disco_seed seed; +static uint8_t state; +static uint8_t sector; +static uint16_t interval; +static struct ctimer disco_timer; + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) + +extern uint16_t uip_len; +extern void *uip_appdata; + +__xdata __at(BOOTTY_CMD_LOCATION) static uint8_t bd; +/*---------------------------------------------------------------------------*/ +static void timer_handler(void * p); +/*---------------------------------------------------------------------------*/ +static void +abort() +{ + PRINTF("Disco: Abort @ %lu\n", clock_seconds()); + n740_analog_deactivate(); + m25p16_dp(); + n740_analog_activate(); + state = DISCO_STATE_LISTENING; + memset(&seed, 0, sizeof(seed)); + ctimer_stop(&disco_timer); + batmon_log(LOG_TRIGGER_OAP_DISCO_ABORT); +} +/*---------------------------------------------------------------------------*/ +static void +restart_timer(uint16_t t) +{ + interval = t; + ctimer_stop(&disco_timer); + ctimer_set(&disco_timer, interval, timer_handler, &state); +} +/*---------------------------------------------------------------------------*/ +static void +timer_handler(void * p) +{ + uint8_t * s = p; + uint8_t wip; + + PRINTF("Disco: @ %lu, s: %u\n", clock_seconds(), *s); + + if(*s == DISCO_STATE_PREPARING) { + n740_analog_deactivate(); + wip = M25P16_WIP(); + n740_analog_activate(); + + if(wip) { + restart_timer(DISCO_TIMEOUT_PREPARE); + } else { + PRINTF("Disco: Erased %u\n", sector); + if((sector & 1) == 0) { + sector++; + PRINTF("Disco: Next %u\n", sector); + n740_analog_deactivate(); + m25p16_se(sector); + n740_analog_activate(); + restart_timer(DISCO_TIMEOUT_PREPARE); + } else { + PRINTF("Disco: Ready\n"); + *s = DISCO_STATE_READY; + resp.status = DISCO_CMD_INIT; + restart_timer(DISCO_TIMEOUT_ABORT); + server_conn->rport = seed.port; + uip_ipaddr_copy(&server_conn->ripaddr, &seed.addr); + uip_udp_packet_send(server_conn, &resp, DISCO_RESP_LEN_INIT); + + /* Restore server connection to allow data from any node */ + uip_create_unspecified(&server_conn->ripaddr); + server_conn->rport = 0; + } + } + } else if(*s == DISCO_STATE_READY) { + abort(); + } else if(*s == DISCO_STATE_REBOOTING) { + watchdog_reboot(); + } +} +/*---------------------------------------------------------------------------*/ +static uint8_t is_protected(uint8_t a) { + uint8_t bp = M25P16_BP() >> 2; + + if(bp > 5) { + return SECTOR_PROTECTED; + } + + bp -= 1; + + if(a >= (32 - (1 << bp))) { + return SECTOR_PROTECTED; + } + return SECTOR_UNPROTECTED; +} +/*---------------------------------------------------------------------------*/ +static uint8_t +cmd_init() +{ + PRINTF("Disco: Init 0x%02x\n", req->addr[0]); + if(uip_datalen() != DISCO_LEN_INIT) { + PRINTF("Disco: Bad len (%u)\n", uip_datalen()); + resp.status = DISCO_ERR_BAD_LEN; + return DISCO_RESP_LEN_ERR; + } + n740_analog_deactivate(); + m25p16_res(); + sector = 2 * req->addr[0]; + if(is_protected(sector) == SECTOR_PROTECTED + || is_protected(sector + 1) == SECTOR_PROTECTED) { + resp.status = DISCO_ERR_PROTECTED; + n740_analog_activate(); + return DISCO_RESP_LEN_ERR; + } + m25p16_se(sector); + n740_analog_activate(); + state = DISCO_STATE_PREPARING; + restart_timer(DISCO_TIMEOUT_PREPARE); + + /* Store the sender's address/port so we can reply when ready */ + seed.port = UIP_UDP_BUF->srcport; + uip_ipaddr_copy(&seed.addr, &UIP_IP_BUF->srcipaddr); + PRINTF("Disco: OK\n"); + + batmon_log(LOG_TRIGGER_OAP_DISCO_START); + + return DISCO_RESPONSE_NONE; +} +/*---------------------------------------------------------------------------*/ +static uint8_t +cmd_write() +{ + PRINTF("Disco: Write 0x%02x%02x%02x\n", req->addr[0], req->addr[1], req->addr[2]); + if(uip_datalen() != DISCO_LEN_WRITE) { + resp.status = DISCO_ERR_BAD_LEN; + return DISCO_RESP_LEN_ERR; + } + restart_timer(DISCO_TIMEOUT_ABORT); + n740_analog_deactivate(); + m25p16_pp(req->addr, req->data, DISCO_FLEN_DATA); + watchdog_periodic(); + while(M25P16_WIP()); + n740_analog_activate(); + resp.status = DISCO_CMD_WRITE; + memcpy(resp.addr, req->addr, DISCO_FLEN_ADDR); + return DISCO_RESP_LEN_WRITE; +} +/*---------------------------------------------------------------------------*/ +static uint8_t +cmd_switch() +{ + PRINTF("Disco: Switch 0x%02x\n", req->addr[0]); + if(uip_datalen() != DISCO_LEN_SWITCH) { + resp.status = DISCO_ERR_BAD_LEN; + return DISCO_RESP_LEN_ERR; + } + if(req->addr[0] > 15) { + resp.status = DISCO_ERR_BAD_OFFSET; + return DISCO_RESP_LEN_ERR; + } + + bd = BOOTTY_CMD_COPY_IMAGE; + bd |= req->addr[0]; + + resp.status = DISCO_CMD_SWITCH; + resp.addr[0] = req->addr[0]; + + restart_timer(DISCO_TIMEOUT_REBOOT); + state = DISCO_STATE_REBOOTING; + + return DISCO_RESP_LEN_SWITCH; +} +/*---------------------------------------------------------------------------*/ +static uint8_t +cmd_done() +{ + PRINTF("Disco: Done\n"); + if(uip_datalen() != DISCO_LEN_DONE) { + resp.status = DISCO_ERR_BAD_LEN; + return DISCO_RESP_LEN_ERR; + } + resp.status = DISCO_CMD_DONE; + + batmon_log(LOG_TRIGGER_OAP_DISCO_DONE); + + return DISCO_RESP_LEN_DONE; +} +/*---------------------------------------------------------------------------*/ +static uint8_t +event_handler(process_event_t ev) +{ + uint8_t rv = DISCO_RESPONSE_NONE; + + if(ev != tcpip_event) { + return rv; + } + + /* Always accept CMD_DONE */ + if(req->cmd == DISCO_CMD_DONE) { + return cmd_done(); + } + + /* Always accept switch too */ + if(req->cmd == DISCO_CMD_SWITCH) { + return cmd_switch(); + } + + switch(state) { + case DISCO_STATE_LISTENING: + req = uip_appdata; + if(req->cmd == DISCO_CMD_INIT) { + rv = cmd_init(); + } + break; + case DISCO_STATE_PREPARING: + PRINTF("Disco: Not Ready\n"); + resp.status = DISCO_ERR_NOT_READY; + rv = DISCO_RESP_LEN_ERR; + break; + case DISCO_STATE_READY: + req = uip_appdata; + if(req->cmd == DISCO_CMD_WRITE) { + rv = cmd_write(); + } else if(req->cmd == DISCO_CMD_INIT) { + resp.status = DISCO_ERR_INIT_DONE; + rv = DISCO_RESP_LEN_ERR; + } else if(req->cmd == DISCO_CMD_SWITCH) { + rv = cmd_switch(); + } + break; + } + return rv; +} +/*---------------------------------------------------------------------------*/ +PROCESS(disco_process, "Disco Server Process"); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(disco_process, ev, data) +{ + uint8_t len; + + PROCESS_BEGIN(); + + PRINTF("Disco Server\n"); + + server_conn = udp_new(NULL, UIP_HTONS(0), NULL); + udp_bind(server_conn, UIP_HTONS(DISCO_UDP_PORT)); + + state = DISCO_STATE_LISTENING; + + while(1) { + PROCESS_YIELD(); + len = event_handler(ev); + + if(len > 0) { + server_conn->rport = UIP_UDP_BUF->srcport; + uip_ipaddr_copy(&server_conn->ripaddr, &UIP_IP_BUF->srcipaddr); + uip_udp_packet_send(server_conn, &resp, len); + /* Restore server connection to allow data from any node */ + uip_create_unspecified(&server_conn->ripaddr); + server_conn->rport = 0; + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/sensinode/disco.h b/platform/sensinode/disco.h new file mode 100644 index 000000000..81549ab8d --- /dev/null +++ b/platform/sensinode/disco.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Header file for the Disco server + * (embedded part of the DISCOBALL project) + * + * \author + * George Oikonomou - + */ + +#ifndef DISCO_H_ +#define DISCO_H_ + +#include "contiki.h" +#include "contiki-net.h" +/*---------------------------------------------------------------------------*/ +#define DISCO_UDP_PORT 60002 +#define DISCO_DESCRIPTORS_LOC /* In external Flash */ + +#define DATA_CHUNK_LEN 64 + +/* Intervals - Timeouts */ +#define DISCO_TIMEOUT_PREPARE (CLOCK_SECOND / 2) +#define DISCO_TIMEOUT_ABORT (CLOCK_SECOND * 10) +#define DISCO_TIMEOUT_REBOOT CLOCK_SECOND + +/* Disco State Machine */ +#define DISCO_STATE_LISTENING 0x00 /* Waiting for a transaction to start */ +#define DISCO_STATE_PREPARING 0x01 /* Erasing Sectors */ +#define DISCO_STATE_READY 0x02 +#define DISCO_STATE_REBOOTING 0x03 /* Reboot to BooTTY and copy new image */ + +/* Instructions */ +#define DISCO_CMD_INIT 0x00 /* Prepare flash area for writes */ +#define DISCO_CMD_SWITCH 0x01 /* Copy image from ext. to int. flash */ +#define DISCO_CMD_WRITE 0x02 /* Write Image to Ext Flash */ +#define DISCO_CMD_DONE 0x03 /* All Done */ + +/* Error Codes */ +#define DISCO_ERR_GENERIC 0xFF /* Generic Error */ +#define DISCO_ERR_BAD_LEN 0xFE /* Incorrect Length */ +#define DISCO_ERR_NOT_READY 0xFD /* Not Initialised */ +#define DISCO_ERR_BAD_OFFSET 0xFC /* Bad Offset */ +#define DISCO_ERR_PROTECTED 0xFB /* Target sector is protected */ +#define DISCO_ERR_INIT_DONE 0xFA /* Already Initialized */ + +/* Message Sizes */ +#define DISCO_FLEN_CMD 1 +#define DISCO_FLEN_IMG 1 +#define DISCO_FLEN_ADDR 3 +#define DISCO_FLEN_DATA 64 + +/* Request Lengths */ +#define DISCO_LEN_INIT (DISCO_FLEN_CMD + DISCO_FLEN_IMG) +#define DISCO_LEN_DONE DISCO_FLEN_CMD +#define DISCO_LEN_WRITE (DISCO_FLEN_CMD + DISCO_FLEN_ADDR + DISCO_FLEN_DATA) +#define DISCO_LEN_SWITCH (DISCO_FLEN_CMD + DISCO_FLEN_IMG) + +/* Response Lengths */ +#define DISCO_RESPONSE_NONE 0 +#define DISCO_RESP_LEN_ERR DISCO_FLEN_CMD +#define DISCO_RESP_LEN_INIT DISCO_FLEN_CMD +#define DISCO_RESP_LEN_DONE DISCO_FLEN_CMD +#define DISCO_RESP_LEN_WRITE (DISCO_FLEN_CMD + DISCO_FLEN_ADDR) +#define DISCO_RESP_LEN_SWITCH (DISCO_FLEN_CMD + DISCO_FLEN_IMG) + +/* Tell BooTTy! what to do after we jump: + * BOOTY_CMD + * [7:5]: Command + * [5:4]: Reserved + * [4:0]: Image number + */ +#define BOOTTY_CMD_LOCATION 0xFEFF + +#define BOOTTY_CMD_JUMP_TO_APP 0x80 +#define BOOTTY_CMD_COPY_IMAGE 0x40 + +#define SECTOR_UNPROTECTED 0 +#define SECTOR_PROTECTED 1 +/*---------------------------------------------------------------------------*/ +PROCESS_NAME(disco_process); +/*---------------------------------------------------------------------------*/ +struct disco_request_pdu { + uint8_t cmd; + uint8_t addr[3]; + uint8_t data[DATA_CHUNK_LEN]; +}; + +struct disco_response_pdu { + uint8_t status; + uint8_t addr[3]; +}; + +struct disco_seed { + uip_ipaddr_t addr; + uint16_t port; +}; +/*---------------------------------------------------------------------------*/ +#endif /* DISCO_H_ */ diff --git a/platform/sensinode/putchar.c b/platform/sensinode/putchar.c new file mode 100644 index 000000000..8d19ef4d3 --- /dev/null +++ b/platform/sensinode/putchar.c @@ -0,0 +1,39 @@ +/** + * \file + * hardware-specific putchar() routine for sensinode motes + * + * \author + * George Oikonomou - + */ + +#include "contiki-conf.h" +#include "dev/uart1.h" + +/*---------------------------------------------------------------------------*/ +void +putchar(char c) +{ +#if SLIP_ARCH_CONF_ENABLE +#define SLIP_END 0300 + static char debug_frame = 0; + + if(!debug_frame) { /* Start of debug output */ + uart1_writeb(SLIP_END); + uart1_writeb('\r'); /* Type debug line == '\r' */ + debug_frame = 1; + } +#endif + + uart1_writeb((char)c); + +#if SLIP_ARCH_CONF_ENABLE + /* + * Line buffered output, a newline marks the end of debug output and + * implicitly flushes debug output. + */ + if(c == '\n') { + uart1_writeb(SLIP_END); + debug_frame = 0; + } +#endif +} diff --git a/platform/sensinode/segment.rules b/platform/sensinode/segment.rules new file mode 100644 index 000000000..0dc7a4a60 --- /dev/null +++ b/platform/sensinode/segment.rules @@ -0,0 +1,13 @@ +# segment.rules - platform/sensinode + +# segment.rules file for code in platform/sensinode +# Please see cpu/cc2430/segment.rules for more info on code segments +# and for rules of thumb on what to do and what not to do + +# Keep main() in HOME +HOME contiki-sensinode-main.c + +# Files with ISRs must be in HOME +HOME platform/sensinode/dev/button-sensor.c + +# segment.rules - platform/sensinode - end diff --git a/platform/sensinode/sensinode-debug.c b/platform/sensinode/sensinode-debug.c new file mode 100644 index 000000000..031e0ccb5 --- /dev/null +++ b/platform/sensinode/sensinode-debug.c @@ -0,0 +1,46 @@ +/** + * \file + * + * Definition of some debugging functions for the sensinode port. + * + * This file is bankable. + * + * putstring() and puthex() are from msp430/watchdog.c + * + * \author + * George Oikonomou - + */ + +#include "cc2430_sfr.h" +#include "8051def.h" +#include "sensinode-debug.h" + +static const char hexconv[] = "0123456789abcdef"; +static const char binconv[] = "01"; + +/*---------------------------------------------------------------------------*/ +void +putstring(char *s) +{ + while(*s) { + putchar(*s++); + } +} +/*---------------------------------------------------------------------------*/ +void +puthex(uint8_t c) +{ + putchar(hexconv[c >> 4]); + putchar(hexconv[c & 0x0f]); +} +/*---------------------------------------------------------------------------*/ +void +putbin(uint8_t c) +{ + unsigned char i = 0x80; + while(i) { + putchar(binconv[(c & i) != 0]); + i >>= 1; + } +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/sensinode/sensinode-debug.h b/platform/sensinode/sensinode-debug.h new file mode 100644 index 000000000..683f1ba44 --- /dev/null +++ b/platform/sensinode/sensinode-debug.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005, Swedish Institute of Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +/** + * \file + * Header file for debugging functions used by the sensinode port. + * + * putstring() and puthex() are from msp430/watchdog.c + * + * \author + * George Oikonomou - + */ + +#ifndef SENSINODE_DEBUG_H_ +#define SENSINODE_DEBUG_H_ + +#include "8051def.h" +#include "dev/uart1.h" + +void putchar(char c); +void putstring(char *s); +void puthex(uint8_t c); +void putbin(uint8_t c); + +#endif /* SENSINODE_DEBUG_H_ */ diff --git a/platform/sensinode/viztool.c b/platform/sensinode/viztool.c new file mode 100644 index 000000000..777ffc855 --- /dev/null +++ b/platform/sensinode/viztool.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Small UDP app used to retrieve neighbor cache and routing table + * entries and send them to an external endpoint + * + * \author + * George Oikonomou - + */ + +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" + +#include + +#define DEBUG DEBUG_NONE +#include "net/uip-debug.h" + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) + +#ifndef VIZTOOL_MAX_PAYLOAD_LEN +#define VIZTOOL_MAX_PAYLOAD_LEN 60 +#endif + +static struct uip_udp_conn *server_conn; +static unsigned char buf[VIZTOOL_MAX_PAYLOAD_LEN]; +static int8_t len; + +#define VIZTOOL_UDP_PORT 60001 + +/* Request Bits */ +#define REQUEST_TYPE_ND 1 +#define REQUEST_TYPE_RT 2 +#define REQUEST_TYPE_DRT 3 +#define REQUEST_TYPE_ADDR 4 +#define REQUEST_TYPE_TOTALS 0xFF + +extern uip_ds6_netif_t uip_ds6_if; +extern uip_ds6_route_t uip_ds6_routing_table[UIP_DS6_ROUTE_NB]; +extern uip_ds6_nbr_t uip_ds6_nbr_cache[UIP_DS6_NBR_NB]; +extern uip_ds6_defrt_t uip_ds6_defrt_list[UIP_DS6_DEFRT_NB]; +extern u16_t uip_len; +/*---------------------------------------------------------------------------*/ +static uint8_t +process_request() +{ + uint8_t len; + uint8_t count; /* How many did we pack? */ + uint8_t i; + uint8_t left; + uint8_t entry_size; + + left = VIZTOOL_MAX_PAYLOAD_LEN - 1; + len = 2; /* start filling the buffer from position [2] */ + count = 0; + if(buf[0] == REQUEST_TYPE_ND) { + /* Neighbors */ + PRINTF("Neighbors\n"); + for(i = buf[1]; i < UIP_DS6_NBR_NB; i++) { + if(uip_ds6_nbr_cache[i].isused) { + entry_size = sizeof(i) + sizeof(uip_ipaddr_t) + sizeof(uip_lladdr_t) + + sizeof(uip_ds6_nbr_cache[i].state); + PRINTF("%02u: ", i); + PRINT6ADDR(&uip_ds6_nbr_cache[i].ipaddr); + PRINTF(" - "); + PRINTLLADDR(&uip_ds6_nbr_cache[i].lladdr); + PRINTF(" - %u\n", uip_ds6_nbr_cache[i].state); + + memcpy(buf + len, &i, sizeof(i)); + len += sizeof(i); + memcpy(buf + len, &uip_ds6_nbr_cache[i].ipaddr, sizeof(uip_ipaddr_t)); + len += sizeof(uip_ipaddr_t); + memcpy(buf + len, &uip_ds6_nbr_cache[i].lladdr, sizeof(uip_lladdr_t)); + len += sizeof(uip_lladdr_t); + memcpy(buf + len, &uip_ds6_nbr_cache[i].state, + sizeof(uip_ds6_nbr_cache[i].state)); + len += sizeof(uip_ds6_nbr_cache[i].state); + + count++; + left -= entry_size; + + if(left < entry_size) { + break; + } + } + } + } else if(buf[0] == REQUEST_TYPE_RT) { + uint32_t flip = 0; + PRINTF("Routing table\n"); + for(i = buf[1]; i < UIP_DS6_ROUTE_NB; i++) { + if(uip_ds6_routing_table[i].isused) { + entry_size = sizeof(i) + sizeof(uip_ds6_routing_table[i].ipaddr) + + sizeof(uip_ds6_routing_table[i].length) + + sizeof(uip_ds6_routing_table[i].metric) + + sizeof(uip_ds6_routing_table[i].nexthop) + + sizeof(uip_ds6_routing_table[i].state.lifetime) + + sizeof(uip_ds6_routing_table[i].state.learned_from); + + memcpy(buf + len, &i, sizeof(i)); + len += sizeof(i); + memcpy(buf + len, &uip_ds6_routing_table[i].ipaddr, + sizeof(uip_ds6_routing_table[i].ipaddr)); + len += sizeof(uip_ds6_routing_table[i].ipaddr); + memcpy(buf + len, &uip_ds6_routing_table[i].length, + sizeof(uip_ds6_routing_table[i].length)); + len += sizeof(uip_ds6_routing_table[i].length); + memcpy(buf + len, &uip_ds6_routing_table[i].metric, + sizeof(uip_ds6_routing_table[i].metric)); + len += sizeof(uip_ds6_routing_table[i].metric); + memcpy(buf + len, &uip_ds6_routing_table[i].nexthop, + sizeof(uip_ds6_routing_table[i].nexthop)); + len += sizeof(uip_ds6_routing_table[i].nexthop); + + PRINT6ADDR(&uip_ds6_routing_table[i].ipaddr); + PRINTF(" - %02x", uip_ds6_routing_table[i].length); + PRINTF(" - %02x", uip_ds6_routing_table[i].metric); + PRINTF(" - "); + PRINT6ADDR(&uip_ds6_routing_table[i].nexthop); + + flip = uip_htonl(uip_ds6_routing_table[i].state.lifetime); + memcpy(buf + len, &flip, sizeof(flip)); + len += sizeof(flip); + PRINTF(" - %08lx", uip_ds6_routing_table[i].state.lifetime); + + memcpy(buf + len, &uip_ds6_routing_table[i].state.learned_from, + sizeof(uip_ds6_routing_table[i].state.learned_from)); + len += sizeof(uip_ds6_routing_table[i].state.learned_from); + + PRINTF(" - %02x [%u]\n", uip_ds6_routing_table[i].state.learned_from, + entry_size); + + count++; + left -= entry_size; + + if(left < entry_size) { + break; + } + } + } + } else if (buf[0] == REQUEST_TYPE_DRT) { + uint32_t flip = 0; + PRINTF("Default Routes\n"); + for(i = buf[1]; i < UIP_DS6_DEFRT_NB; i++) { + if(uip_ds6_defrt_list[i].isused) { + entry_size = sizeof(i) + sizeof(uip_ds6_defrt_list[i].ipaddr) + + sizeof(uip_ds6_defrt_list[i].isinfinite); + + memcpy(buf + len, &i, sizeof(i)); + len += sizeof(i); + memcpy(buf + len, &uip_ds6_defrt_list[i].ipaddr, + sizeof(uip_ds6_defrt_list[i].ipaddr)); + len += sizeof(uip_ds6_defrt_list[i].ipaddr); + memcpy(buf + len, &uip_ds6_defrt_list[i].isinfinite, + sizeof(uip_ds6_defrt_list[i].isinfinite)); + len += sizeof(uip_ds6_defrt_list[i].isinfinite); + + PRINT6ADDR(&uip_ds6_defrt_list[i].ipaddr); + PRINTF(" - %u\n", uip_ds6_defrt_list[i].isinfinite); + count++; + left -= entry_size; + + if(left < entry_size) { + break; + } + } + } + } else if (buf[0] == REQUEST_TYPE_ADDR) { + PRINTF("Unicast Addresses\n"); + for(i = buf[1]; i < UIP_DS6_ADDR_NB; i++) { + if(uip_ds6_if.addr_list[i].isused) { + entry_size = sizeof(i) + sizeof(uip_ds6_if.addr_list[i].ipaddr); + + memcpy(buf + len, &i, sizeof(i)); + len += sizeof(i); + memcpy(buf + len, &uip_ds6_if.addr_list[i].ipaddr, + sizeof(uip_ds6_if.addr_list[i].ipaddr)); + len += sizeof(uip_ds6_if.addr_list[i].ipaddr); + + PRINT6ADDR(&uip_ds6_if.addr_list[i].ipaddr); + PRINTF("\n"); + count++; + left -= entry_size; + + if(left < entry_size) { + break; + } + } + } + } else if (buf[0] == REQUEST_TYPE_TOTALS) { + memset(&buf[2], 0, 4); + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + if(uip_ds6_if.addr_list[i].isused) { + buf[2]++; + } + } + for(i = 0; i < UIP_DS6_NBR_NB; i++) { + if(uip_ds6_nbr_cache[i].isused) { + buf[3]++; + } + } + for(i = 0; i < UIP_DS6_ROUTE_NB; i++) { + if(uip_ds6_routing_table[i].isused) { + buf[4]++; + } + } + for(i = 0; i < UIP_DS6_DEFRT_NB; i++) { + if(uip_ds6_defrt_list[i].isused) { + buf[5]++; + } + } + len += 4; + count = 4; + } else { + return 0; + } + buf[1] = count; + return len; +} +/*---------------------------------------------------------------------------*/ +PROCESS(viztool_process, "Network Visualization Tool Process"); +/*---------------------------------------------------------------------------*/ +static void +tcpip_handler(void) +{ + if(uip_newdata()) { + memset(buf, 0, VIZTOOL_MAX_PAYLOAD_LEN); + + PRINTF("%u bytes from [", uip_datalen()); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("]:%u\n", UIP_HTONS(UIP_UDP_BUF->srcport)); + + memcpy(buf, uip_appdata, uip_datalen()); + + len = process_request(); + if(len) { + server_conn->rport = UIP_UDP_BUF->srcport; + uip_ipaddr_copy(&server_conn->ripaddr, &UIP_IP_BUF->srcipaddr); + uip_udp_packet_send(server_conn, buf, len); + PRINTF("Sent %u bytes\n", len); + } + + /* Restore server connection to allow data from any node */ + uip_create_unspecified(&server_conn->ripaddr); + server_conn->rport = 0; + } + return; +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(viztool_process, ev, data) +{ + + PROCESS_BEGIN(); + + server_conn = udp_new(NULL, UIP_HTONS(0), NULL); + udp_bind(server_conn, UIP_HTONS(VIZTOOL_UDP_PORT)); + + while(1) { + PROCESS_YIELD(); + if(ev == tcpip_event) { + tcpip_handler(); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ From ad256e50148ab7ca44fd73bac7cc3e9fce2be47c Mon Sep 17 00:00:00 2001 From: George Oikonomou Date: Mon, 5 Mar 2012 15:47:01 +0000 Subject: [PATCH 12/14] New platform: TI cc2530 Development Kit This commits adds support for TI's SmartRF05 Eval. Board with cc2530 EMs Some initial support for cc2531 USB dongles --- cpu/cc253x/8051def.h | 79 +++ cpu/cc253x/Makefile.cc253x | 198 ++++++ cpu/cc253x/bank-alloc.py | 245 +++++++ cpu/cc253x/cc253x.h | 669 ++++++++++++++++++ cpu/cc253x/dev/cc2530-rf.c | 489 +++++++++++++ cpu/cc253x/dev/cc2530-rf.h | 127 ++++ cpu/cc253x/dev/clock-isr.h | 46 ++ cpu/cc253x/dev/clock.c | 182 +++++ cpu/cc253x/dev/dma.c | 69 ++ cpu/cc253x/dev/dma.h | 148 ++++ cpu/cc253x/dev/dma_intr.c | 68 ++ cpu/cc253x/dev/lpm.h | 57 ++ cpu/cc253x/dev/port.h | 145 ++++ cpu/cc253x/dev/random.c | 111 +++ cpu/cc253x/dev/uart-intr.c | 70 ++ cpu/cc253x/dev/uart.h | 43 ++ cpu/cc253x/dev/uart0.c | 75 ++ cpu/cc253x/dev/uart0.h | 41 ++ cpu/cc253x/dev/uart1.c | 74 ++ cpu/cc253x/dev/uart1.h | 38 + cpu/cc253x/dev/watchdog.c | 88 +++ cpu/cc253x/mtarch.h | 50 ++ cpu/cc253x/rtimer-arch.c | 113 +++ cpu/cc253x/rtimer-arch.h | 61 ++ cpu/cc253x/segment.rules | 22 + cpu/cc253x/sfr-bits.h | 195 +++++ cpu/cc253x/soc.c | 54 ++ cpu/cc253x/soc.h | 46 ++ examples/cc2530dk/Makefile | 6 + examples/cc2530dk/Makefile.target | 1 + examples/cc2530dk/blink-hello.c | 62 ++ examples/cc2530dk/border-router/Makefile | 15 + .../cc2530dk/border-router/Makefile.target | 1 + examples/cc2530dk/border-router/README | 16 + .../cc2530dk/border-router/border-router.c | 131 ++++ .../cc2530dk/border-router/project-conf.h | 48 ++ examples/cc2530dk/border-router/slip-bridge.c | 105 +++ examples/cc2530dk/hello-world.c | 23 + examples/cc2530dk/sensors-demo.c | 193 +++++ examples/cc2530dk/sniffer/Makefile | 10 + examples/cc2530dk/sniffer/Makefile.target | 1 + examples/cc2530dk/sniffer/netstack.c | 50 ++ examples/cc2530dk/sniffer/project-conf.h | 50 ++ examples/cc2530dk/sniffer/sniffer.c | 54 ++ examples/cc2530dk/sniffer/stub-rdc.c | 91 +++ examples/cc2530dk/timer-test.c | 139 ++++ examples/cc2530dk/udp-ipv6/Makefile | 14 + examples/cc2530dk/udp-ipv6/Makefile.target | 1 + examples/cc2530dk/udp-ipv6/client.c | 159 +++++ examples/cc2530dk/udp-ipv6/ping6.c | 139 ++++ examples/cc2530dk/udp-ipv6/project-conf.h | 49 ++ examples/cc2530dk/udp-ipv6/server.c | 174 +++++ platform/cc2530dk/Makefile.cc2530dk | 52 ++ platform/cc2530dk/contiki-conf.h | 225 ++++++ platform/cc2530dk/contiki-main.c | 333 +++++++++ platform/cc2530dk/debug.c | 42 ++ platform/cc2530dk/debug.h | 54 ++ platform/cc2530dk/dev/adc-sensor.c | 123 ++++ platform/cc2530dk/dev/adc-sensor.h | 82 +++ platform/cc2530dk/dev/button-sensor.c | 105 +++ platform/cc2530dk/dev/button-sensor.h | 80 +++ platform/cc2530dk/dev/leds-arch.c | 87 +++ platform/cc2530dk/dev/leds-arch.h | 53 ++ platform/cc2530dk/dev/slip-arch.c | 50 ++ platform/cc2530dk/dev/smartrf-sensors.c | 54 ++ platform/cc2530dk/putchar.c | 38 + platform/cc2530dk/segment.rules | 13 + platform/cc2530dk/viztool.c | 298 ++++++++ 68 files changed, 6824 insertions(+) create mode 100644 cpu/cc253x/8051def.h create mode 100644 cpu/cc253x/Makefile.cc253x create mode 100644 cpu/cc253x/bank-alloc.py create mode 100644 cpu/cc253x/cc253x.h create mode 100644 cpu/cc253x/dev/cc2530-rf.c create mode 100644 cpu/cc253x/dev/cc2530-rf.h create mode 100644 cpu/cc253x/dev/clock-isr.h create mode 100644 cpu/cc253x/dev/clock.c create mode 100644 cpu/cc253x/dev/dma.c create mode 100644 cpu/cc253x/dev/dma.h create mode 100644 cpu/cc253x/dev/dma_intr.c create mode 100644 cpu/cc253x/dev/lpm.h create mode 100644 cpu/cc253x/dev/port.h create mode 100644 cpu/cc253x/dev/random.c create mode 100644 cpu/cc253x/dev/uart-intr.c create mode 100644 cpu/cc253x/dev/uart.h create mode 100644 cpu/cc253x/dev/uart0.c create mode 100644 cpu/cc253x/dev/uart0.h create mode 100644 cpu/cc253x/dev/uart1.c create mode 100644 cpu/cc253x/dev/uart1.h create mode 100644 cpu/cc253x/dev/watchdog.c create mode 100644 cpu/cc253x/mtarch.h create mode 100644 cpu/cc253x/rtimer-arch.c create mode 100644 cpu/cc253x/rtimer-arch.h create mode 100644 cpu/cc253x/segment.rules create mode 100644 cpu/cc253x/sfr-bits.h create mode 100644 cpu/cc253x/soc.c create mode 100644 cpu/cc253x/soc.h create mode 100644 examples/cc2530dk/Makefile create mode 100644 examples/cc2530dk/Makefile.target create mode 100644 examples/cc2530dk/blink-hello.c create mode 100644 examples/cc2530dk/border-router/Makefile create mode 100644 examples/cc2530dk/border-router/Makefile.target create mode 100644 examples/cc2530dk/border-router/README create mode 100644 examples/cc2530dk/border-router/border-router.c create mode 100644 examples/cc2530dk/border-router/project-conf.h create mode 100644 examples/cc2530dk/border-router/slip-bridge.c create mode 100644 examples/cc2530dk/hello-world.c create mode 100644 examples/cc2530dk/sensors-demo.c create mode 100644 examples/cc2530dk/sniffer/Makefile create mode 100644 examples/cc2530dk/sniffer/Makefile.target create mode 100644 examples/cc2530dk/sniffer/netstack.c create mode 100644 examples/cc2530dk/sniffer/project-conf.h create mode 100644 examples/cc2530dk/sniffer/sniffer.c create mode 100644 examples/cc2530dk/sniffer/stub-rdc.c create mode 100644 examples/cc2530dk/timer-test.c create mode 100644 examples/cc2530dk/udp-ipv6/Makefile create mode 100644 examples/cc2530dk/udp-ipv6/Makefile.target create mode 100644 examples/cc2530dk/udp-ipv6/client.c create mode 100644 examples/cc2530dk/udp-ipv6/ping6.c create mode 100644 examples/cc2530dk/udp-ipv6/project-conf.h create mode 100644 examples/cc2530dk/udp-ipv6/server.c create mode 100644 platform/cc2530dk/Makefile.cc2530dk create mode 100644 platform/cc2530dk/contiki-conf.h create mode 100644 platform/cc2530dk/contiki-main.c create mode 100644 platform/cc2530dk/debug.c create mode 100644 platform/cc2530dk/debug.h create mode 100644 platform/cc2530dk/dev/adc-sensor.c create mode 100644 platform/cc2530dk/dev/adc-sensor.h create mode 100644 platform/cc2530dk/dev/button-sensor.c create mode 100644 platform/cc2530dk/dev/button-sensor.h create mode 100644 platform/cc2530dk/dev/leds-arch.c create mode 100644 platform/cc2530dk/dev/leds-arch.h create mode 100644 platform/cc2530dk/dev/slip-arch.c create mode 100644 platform/cc2530dk/dev/smartrf-sensors.c create mode 100644 platform/cc2530dk/putchar.c create mode 100644 platform/cc2530dk/segment.rules create mode 100644 platform/cc2530dk/viztool.c diff --git a/cpu/cc253x/8051def.h b/cpu/cc253x/8051def.h new file mode 100644 index 000000000..7314edcd7 --- /dev/null +++ b/cpu/cc253x/8051def.h @@ -0,0 +1,79 @@ +/* + * \file + * This file contains a set of configuration for using SDCC as a compiler. + * Modified from z80 port for cc2430 port. + * + * \author + * Takahide Matsutsuka (Original) + * George Oikonomou - + * (updates for the cc2530 ports) + */ + +#ifndef __8051_DEF_H__ +#define __8051_DEF_H__ + +#include + +/* + * lint - style defines to help syntax parsers with sdcc-specific 8051 code + * They don't interfere with actual compilation + */ +#if !defined(__SDCC_mcs51) && !defined(SDCC_mcs51) +#define __data +#define __xdata +#define __code +#define __bit bool +#define __sfr volatile unsigned char +#define __sbit volatile bool +#define __critical +#define __at(x) +#define __using(x) +#define __interrupt(x) +#define __naked +#endif + +#define CC_CONF_FUNCTION_POINTER_ARGS 1 +#define CC_CONF_FASTCALL +#define CC_CONF_VA_ARGS 1 +#define CC_CONF_UNSIGNED_CHAR_BUGS 0 +#define CC_CONF_REGISTER_ARGS 0 +#define CC_CONF_FUNCTION_POINTER_KEYWORD __reentrant + +/* Generic types. */ +typedef unsigned char u8_t; /* 8 bit type */ +typedef unsigned short u16_t; /* 16 bit type */ +typedef unsigned long u32_t; /* 32 bit type */ +typedef signed long s32_t; /* 32 bit type */ +typedef unsigned short uip_stats_t; + +/* Compiler configurations */ +#define CCIF +#define CLIF + +/* Single asm instruction without messing up syntax highlighting */ +#if defined SDCC_mcs51 +#define ASM(x) __asm \ + x \ + __endasm +#else +#define ASM(x) +#endif + +/* Critical section management */ +#define DISABLE_INTERRUPTS() do {EA = 0;} while(0) +#define ENABLE_INTERRUPTS() do {EA = 1;} while(0) + +/* Macro for a soft reset. In many respects better than H/W reboot via W/D */ +#define SOFT_RESET() {((void (__code *) (void)) 0x0000) ();} + +/* We don't provide architecture-specific checksum calculations */ +#define UIP_ARCH_ADD32 0 +#define UIP_ARCH_CHKSUM 0 + +#define CC_CONF_ASSIGN_AGGREGATE(dest, src) \ + memcpy(dest, src, sizeof(*dest)) + +#define uip_ipaddr_copy(dest, src) \ + memcpy(dest, src, sizeof(*dest)) + +#endif /* __8051_DEF_H__ */ diff --git a/cpu/cc253x/Makefile.cc253x b/cpu/cc253x/Makefile.cc253x new file mode 100644 index 000000000..568d8f2d9 --- /dev/null +++ b/cpu/cc253x/Makefile.cc253x @@ -0,0 +1,198 @@ +### Compiler definitions +CC = sdcc +LD = sdcc +AS = sdcc +AR = sdcclib +OBJCOPY = objcopy +STRIP = strip + +### Hex file conversions +PACKIHX = packihx +SREC_CAT = srec_cat +SREC_FLAGS = -disable_sequence_warnings + +BANK_ALLOC = $(CONTIKI_CPU)/bank-alloc.py +SEGMENT_RULES = $(OBJECTDIR)/segment.rules + +CFLAGS += --model-$(MEMORY_MODEL) --stack-auto --std-c99 + +LDFLAGS += --model-$(MEMORY_MODEL) --stack-auto --out-fmt-ihx +LDFLAGS += --xram-loc 0x0000 --xram-size 0x1F00 +LDFLAGS += --code-loc $(START_ADDR) --code-size $(CODE_SIZE) + +ASFLAGS += -plosgff + +AROPTS = -a + +### Our object files are .rel, so we can't use the default finalize dependency +### generation. Override here. +define FINALIZE_SDCC_DEPENDENCY +cp $(@:.rel=.d) $(@:.rel=.$$$$); \ +sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:.rel=.$$$$) >> $(@:.rel=.d); \ +rm -f $(@:.rel=.$$$$) +endef + +### Banking Guesswork: +### Generic examples do not specify banking. +### We automatically turn it on if its unspecified and if we are building with +### UIP_CONF_IPV6 +ifndef HAVE_BANKING + ifeq ($(UIP_CONF_IPV6),1) + HAVE_BANKING=1 + else + HAVE_BANKING=0 + endif +endif + +### Does the project want us to offset the firmware? +### define start address and max code size accordingly +ifeq ($(OFFSET_FIRMWARE),1) + START_ADDR = 0x01000 + HOME_START = 00001000 + ifeq ($(HAVE_BANKING),1) + CODE_SIZE = 0x3F000 + else + CODE_SIZE = 0x0F000 + endif +else + START_ADDR = 0x00000 + HOME_START = 00000000 + ifeq ($(HAVE_BANKING),1) + CODE_SIZE = 0x40000 + else + CODE_SIZE = 0x10000 + endif +endif + +### Are we building with BANKing supoprt? +ifeq ($(HAVE_BANKING),1) + ## Yes + MEMORY_MODEL=huge + LDFLAGS += -Wl-r + LD_PRE_FLAGS += -Wl-bBANK1=0x018000 + CFLAGS += -DHAVE_SDCC_BANKING + #use this in $(call c_seg,$<) to get segment for a source file. + c_seg = --codeseg $(shell python $(BANK_ALLOC) $1 $(SEGMENT_RULES) $2) +else + ## No banking + MEMORY_MODEL=large + c_seg = +endif + +### CPU-dependent cleanup files +CLEAN += *.lnk *.lk *.sym *.lib *.ihx *.rel *.mem *.rst *.asm *.hex +CLEAN += *.omf *.cdb *.banks *.flags *.banked-hex +CLEAN += symbols.c symbols.h + +### CPU-dependent directories +CONTIKI_CPU_DIRS = . dev + +### CPU-dependent source files +CONTIKI_SOURCEFILES += soc.c clock.c +CONTIKI_SOURCEFILES += uart0.c uart1.c uart-intr.c +CONTIKI_SOURCEFILES += dma.c dma_intr.c +CONTIKI_SOURCEFILES += cc2530-rf.c +CONTIKI_SOURCEFILES += watchdog.c rtimer-arch.c +CONTIKI_ASMFILES += + +CONTIKI_ASMOBJECTFILES = $(addprefix $(OBJECTDIR)/,$(CONTIKI_ASMFILES:.S=.rel)) + +CONTIKI_CASMOBJECTFILES = $(addprefix $(OBJECTDIR)/, \ + $(CONTIKI_CASMFILES:.cS=.rel)) + +CONTIKI_PLATFORM_DIRS = $(PLATFORM_APPDIRS) \ + $(addprefix $(CONTIKI)/platform/$(TARGET)/, $(CONTIKI_TARGET_DIRS)) + +CONTIKI_CPU_DIRS_LIST = $(addprefix $(CONTIKI_CPU)/, \ + $(CONTIKI_CPU_DIRS)) + +oname = $(patsubst %.c,%.rel,$(patsubst %.S,%.rel,$(1))) + +CONTIKI_OBJECTFILES = $(addprefix $(OBJECTDIR)/, \ + $(call oname, $(CONTIKI_SOURCEFILES))) + +PROJECT_OBJECTFILES = $(addprefix $(OBJECTDIR)/, \ + $(call oname, $(PROJECT_SOURCEFILES))) + +### Compilation rules + +SEGMENT_RULE_FILES = $(foreach dir, . $(CONTIKI_PLATFORM_DIRS) \ + $(CONTIKI_CPU_DIRS_LIST), $(wildcard $(dir)/segment.rules) ) + +$(SEGMENT_RULES): $(SEGMENT_RULE_FILES) + cat $(SEGMENT_RULE_FILES) | \ + sed -e 's/#.*$$//' -e 's/^\s*//' -e '/^$$/d' > $@ + +CUSTOM_RULE_LINK=1 +CUSTOM_RULE_C_TO_OBJECTDIR_O=1 +CUSTOM_RULE_ALLOBJS_TO_TARGETLIB=1 + +$(OBJECTDIR)/%.rel: %.c $(SEGMENT_RULES) + $(CC) $(call c_seg,$<,$@) $(CFLAGS) -c $< -o $@ -Wp,-MMD,$(@:.rel=.d),-MQ,$@ + @$(FINALIZE_SDCC_DEPENDENCY) + +$(OBJECTDIR)/%.rel: %.cS + cp $< $(OBJECTDIR)/$*.c + $(CC) $(CFLAGS) -E $(OBJECTDIR)/$*.c > $(OBJECTDIR)/tmp + perl -pe "s/^#(.*)/;$$1/" $(OBJECTDIR)/tmp > $(OBJECTDIR)/$*.S + $(AS) $(ASFLAGS) -o $@ $(OBJECTDIR)/$*.S + rm -f $(OBJECTDIR)/tmp + +contiki-$(TARGET).lib: $(CONTIKI_OBJECTFILES) $(PROJECT_OBJECTFILES) \ + $(CONTIKI_ASMOBJECTFILES) $(CONTIKI_CASMOBJECTFILES) + rm -f $@ + for target in $^; do echo $$target >> $@; done + +.PRECIOUS: %.$(TARGET) %.hex + +# build app/example local object files. We need a separate rule so that we can +# pass -DAUTOSTART_ENABLE for those files only +$(OBJECTDIR)/%.app.rel: %.c $(SEGMENT_RULES) + $(CC) $(call c_seg,$<,$@) -DAUTOSTART_ENABLE $(CFLAGS) -c $< -o $@ + +# .ihx is the sdcc binary output file +ifeq ($(HAVE_BANKING),1) +### Build bankable firmware +%.ihx: $(OBJECTDIR)/%.app.rel $(CONTIKI_TARGET_MAIN) contiki-$(TARGET).lib + @echo "\nFirst Link" + @echo "===============" + $(CC) $(LDFLAGS) $(LD_PRE_FLAGS) -o $@ $(CONTIKI_TARGET_MAIN) $(OBJECTDIR)/$*.app.rel -llibsdcc.lib -lcontiki-$(TARGET).lib > /dev/null + +%.flags: %.ihx +### Allocate modules to banks and relocate object files + @echo "\nBank Allocation" + @echo "===============" + python $(BANK_ALLOC) $(basename $(@F)) $(SEGMENT_RULES) $(OFFSET_FIRMWARE) + +%.banked-hex: %.flags +### Link again with new bank allocations + @echo "\nFinal Link" + @echo "===============" + $(CC) $(LDFLAGS) $(shell cat $<) -o $@ $(CONTIKI_TARGET_MAIN) $(OBJECTDIR)/$*.app.rel -llibsdcc.lib -lcontiki-$(TARGET).lib > /dev/null + +%.hex: %.banked-hex +### Post-process the hex file for programmers which dislike SDCC output hex format + $(eval BANKS := $(shell egrep '(^BANK[0-9])=' $(@:.hex=.map) | sed -e 's/BANK\([0-9]\).*/\1/' | uniq)) + $(eval FILES := $(addsuffix .hex,$(addprefix bank,$(BANKS)))) + @echo "\nPack hex file" + @echo "===============" + @for bank in $(BANKS); do \ + echo $(SREC_CAT) $(SREC_FLAGS) $(@:.hex=.banked-hex) -intel \ + -crop 0x"$$bank"8000 "0x"$$bank"FFFF" \ + -offset -$$((0x8000 * $$bank + 0x08000)) -o bank"$$bank".hex -intel; \ + $(SREC_CAT) $(SREC_FLAGS) $(@:.hex=.banked-hex) -intel \ + -crop 0x"$$bank"8000 "0x"$$bank"FFFF" \ + -offset -$$((0x8000 * $$bank + 0x08000)) -o bank"$$bank".hex -intel; \ + done + $(SREC_CAT) $(SREC_FLAGS) $(@:.hex=.banked-hex) -intel -crop 0x00000 0x07FFF -o home.ihx -intel + srec_cat home.ihx -intel $(foreach file,$(FILES),$(file) -intel) -o $@ -intel + rm -f home.ihx $(FILES) +else +### Build non-banked firmware +%.ihx: $(OBJECTDIR)/%.app.rel $(CONTIKI_TARGET_MAIN) contiki-$(TARGET).lib + $(CC) $(LDFLAGS) -o $@ $(CONTIKI_TARGET_MAIN) $(OBJECTDIR)/$*.app.rel -llibsdcc.lib -lcontiki-$(TARGET).lib > /dev/null + +%.hex: %.ihx + $(PACKIHX) $< > $@ +endif diff --git a/cpu/cc253x/bank-alloc.py b/cpu/cc253x/bank-alloc.py new file mode 100644 index 000000000..f2d264538 --- /dev/null +++ b/cpu/cc253x/bank-alloc.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python + +# Copyright (c) 2010, Loughborough University - Computer Science +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. +# +# This file is part of the Contiki operating system. + +# \file +# Automatic allocation of modules to code segments for bankable builds +# with SDCC's huge memory model. +# +# \author +# George Oikonomou - +import sys +import re +import operator +import fileinput +import os + +# Open a module object file (.rel) and read it's code size +def retrieve_module_size(file_name): + size_pat = re.compile('^A\s+(?:HOME|BANK[0-9])\s+size\s+([1-9A-F][0-9A-F]*)') + for code_line in open(file_name): + matches = size_pat.search(code_line) + if matches is not None: + return int(matches.group(1), 16) + return 0 + +# Searches for a code segment rule for file_name in the segment_rules file +# If there is a rule, we respect it. Otherwise, we can move the file around +def get_source_seg(source_file, object_file, segment_rules): + for line in open(segment_rules): + tokens = line.split(None) + match = re.search(tokens[1], source_file) + if match is not None: + # Save it in basename.seg + base, ext = os.path.splitext(object_file) + of = open(base + '.seg', 'w') + of.write(tokens[0] + '\n') + of.close + return tokens[0] + return None + +# If segment.rules specified a rule for a source file, the respective object +# file's banking requirement will be stored in object_file.seg +def get_object_seg(object_file): + base, ext = os.path.splitext(object_file) + seg = base + '.seg' + bank = None + if os.path.isfile(seg) is True: + of = open(base + '.seg', 'r') + bank = of.readline().strip() + of.close() + return bank + +# Open project.mem and retreive the project's total code footprint +def get_total_size(project): + mem_file = project + '.mem' + pat = re.compile('FLASH\s+(0x[0-9a-f]+\s+){2}([0-9]+)') + for line in open(mem_file): + l = pat.search(line) + if l is not None: + return int(l.group(2)) + +# Open project.map and retrieve the list of modules linked in +# This will only consider contiki sources, not SDCC libraries +# NB: Sometimes object filenames get truncated: +# contiki-sensinode.lib [ obj_sensinode/watchdog-cc2430.re ] +# See how for this file the 'l' in 'rel' is missing. For that reason, we retrieve +# the filaname until the last '.' but without the extension and we append 'rel' +# As long as the filename doesn't get truncated, we're good +def populate(project, modules, segment_rules, bins): + bankable_total = 0 + user_total = 0 + + map_file = project + '.map' + file_pat = re.compile('obj_cc2530dk[^ ]+\.') + for line in open(map_file): + file_name = file_pat.search(line) + if file_name is not None: + mod = file_name.group(0) + 'rel' + code_size = retrieve_module_size(mod) + seg = get_object_seg(mod) + if seg is not None: + # This module has been assigned to a bank by the user + #print 'In', seg, file_name.group(0), 'size', code_size + bins[seg][0] += code_size + user_total += code_size + else: + # We are free to allocate this module + modules.append([mod, code_size, "NONE"]) + bankable_total += code_size + return bankable_total, user_total + +# Allocate bankable modules to banks according to a simple +# 'first fit, decreasing' bin packing heuristic. +def bin_pack(modules, bins, offset, log): + if offset==1: + bins['HOME'][1] -= 4096 + + # Sort by size, descending, in=place + modules.sort(key=operator.itemgetter(1), reverse=True) + + for module in modules: + # We want to iterate in a specific order and dict.keys() won't do that + for bin_id in ['HOME', 'BANK1', 'BANK2', 'BANK3', 'BANK4', 'BANK5', 'BANK6', 'BANK7']: + if bins[bin_id][0] + module[1] < bins[bin_id][1]: + bins[bin_id][0] += module[1] + module[2] = bin_id + log.writelines(' '.join([module[2].ljust(8), \ + str(module[1]).rjust(5), module[0], '\n'])) + break + else: + if bin_id == 'BANK7': + print "Failed to allocate", module[0], "with size", module[1], \ + "to a code bank. This is fatal" + return 1 + return 0 + +# Hack the new bank directly in the .rel file +def relocate(module, bank): + code_pat = re.compile('(A\s+)(?:HOME|BANK[0-9])(\s+size\s+[1-9A-F][0-9A-F]*.+\n)') + + for line in fileinput.input(module, inplace=1): + m = code_pat.search(line) + if m is not None: + line = m.group(1) + bank + m.group(2) + sys.stdout.write(line) + return + +if len(sys.argv) < 3: + print 'Usage:' + print 'bank-alloc.py project path_to_segment_rules [offset]' + print 'bank-alloc.py source_file path_to_segment_rules object_file' + sys.exit(1) + +modules = list() +file_name = sys.argv[1] +segment_rules = sys.argv[2] + +# Magic: Guess whether we want to determine the code bank for a code file +# or whether we want to bin-pack +basename, ext = os.path.splitext(file_name) +if ext == '.c': + # Code Segment determination + if len(sys.argv) < 4: + print 'Usage:' + print 'bank-alloc.py project path_to_segment_rules [offset]' + print 'bank-alloc.py source_file path_to_segment_rules object_file' + sys.exit(1) + object_file = sys.argv[3] + seg = get_source_seg(file_name, object_file, segment_rules) + if seg is None: + print "BANK1" + else: + print seg + exit() + +# Bin-Packing +offset = 0 +if len(sys.argv) > 3 and sys.argv[3] is not None: + offset = int(sys.argv[3]) + +sizes = {'total': 0, 'bankable': 0, 'user': 0, 'libs': 0} + +# Name : [Allocated, capacity, start_addr] +bins = { + 'HOME': [0, 32768, '0x000000'], + 'BANK1': [0, 32768, '0x018000'], + 'BANK2': [0, 32768, '0x028000'], + 'BANK3': [0, 32768, '0x038000'], + 'BANK4': [0, 32768, '0x048000'], + 'BANK5': [0, 32768, '0x058000'], + 'BANK6': [0, 32768, '0x068000'], + 'BANK7': [0, 32768, '0x078000'], +} + +sizes['total'] = get_total_size(basename) +sizes['bankable'], sizes['user'] = populate(basename, modules, segment_rules, bins) +sizes['libs'] = sizes['total'] - sizes['bankable'] - sizes['user'] + +print 'Total Size =', sizes['total'], 'bytes (' + \ + str(sizes['bankable']), 'bankable,', \ + str(sizes['user']), 'user-allocated,', \ + str(sizes['libs']), 'const+libs)' + +bins['HOME'][0] += sizes['libs'] + +print 'Preallocations: HOME=' + str(bins['HOME'][0]), +for bin_id in ['BANK1', 'BANK2', 'BANK3', 'BANK4', 'BANK5', 'BANK6', 'BANK7']: + if bins[bin_id][0] > 0: + print ", " + bin_id + "=" + str(bins[bin_id][0]), +print + +# Open a log file +of = open(basename + '.banks', 'w') +pack = bin_pack(modules, bins, offset, of) +of.close() + +print "Bin-Packing results (target allocation):" +print "Segment - max - alloc" +for bin_id in ['HOME', 'BANK1', 'BANK2', 'BANK3', 'BANK4', 'BANK5', 'BANK6', 'BANK7']: + if bins[bin_id][0] > 0: + print bin_id.rjust(7), str(bins[bin_id][1]).rjust(6), str(bins[bin_id][0]).rjust(6) + +if pack > 0: + sys.exit(1) + +# If we reach here we seem to have a sane allocation. Start changing .rel files +for module in modules: + relocate(module[0], module[2]) + +flags = "" +# Export LD_POST_FLAGS +for bin_id in ['BANK1', 'BANK2', 'BANK3', 'BANK4', 'BANK5', 'BANK6', 'BANK7']: + if bins[bin_id][0] > 0: + flags += "-Wl-b" + bin_id + "=" + bins[bin_id][2] + " " +# Write LD_POST_FLAGS in project.flags +of = open(basename + '.flags', 'w') +of.write(flags + '\n') +of.close() diff --git a/cpu/cc253x/cc253x.h b/cpu/cc253x/cc253x.h new file mode 100644 index 000000000..27c022eec --- /dev/null +++ b/cpu/cc253x/cc253x.h @@ -0,0 +1,669 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Definitions for TI/Chipcon cc2530, cc2531 and cc2533 SFR registers. + * + * Based on information in: + * "CC253x System-on-Chip Solution for 2.4-GHz IEEE 802.15.4 and ZigBee® + * Applications" + * Literature Number: SWRU191B. April 2009–Revised September 2010 + * + * \author + * George Oikonomou - + */ + +#ifndef __CC253X_H__ +#define __CC253X_H__ + +/*---------------------------------------------------------------------------*/ +/* Compiler Abstraction */ +#include +/*--------------------------------------------------------------------------- + * Interrupt Vectors + * (Table 2.5, page 40) + *---------------------------------------------------------------------------*/ +#define RFERR_VECTOR 0 /* RF TXFIFO underflow and RXFIFO overflow. */ +#define ADC_VECTOR 1 /* ADC end of conversion */ +#define URX0_VECTOR 2 /* USART 0 RX complete */ +#define URX1_VECTOR 3 /* USART 1 RX complete */ +#define ENC_VECTOR 4 /* AES encryption/decryption complete */ +#define ST_VECTOR 5 /* Sleep Timer compare */ +#define P2INT_VECTOR 6 /* Port 2 inputs/USB/I2C */ +#define UTX0_VECTOR 7 /* USART 0 TX complete */ +#define DMA_VECTOR 8 /* DMA transfer complete */ +#define T1_VECTOR 9 /* Timer 1 (16-bit) capture/compare/overflow */ +#define T2_VECTOR 10 /* Timer 2 (MAC Timer) */ +#define T3_VECTOR 11 /* Timer 3 (8-bit) compare/overflow */ +#define T4_VECTOR 12 /* Timer 4 (8-bit) compare/overflow */ +#define P0INT_VECTOR 13 /* Port 0 inputs */ +#define UTX1_VECTOR 14 /* USART1 TX complete */ +#define P1INT_VECTOR 15 /* Port 1 inputs */ +#define RF_VECTOR 16 /* RF general interrupts */ +#define WDT_VECTOR 17 /* Watchdog overflow in timer mode */ +/*--------------------------------------------------------------------------- + * Special Function Registers and BITs + * (Table 2.1, page 27) + *---------------------------------------------------------------------------*/ +/* 8051 Internals */ +SFR(P0, 0x80); /* Port 0 */ + SBIT(P0_7, 0x80, 7); /* Port 0 bit 7 */ + SBIT(P0_6, 0x80, 6); /* Port 0 bit 6 */ + SBIT(P0_5, 0x80, 5); /* Port 0 bit 5 */ + SBIT(P0_4, 0x80, 4); /* Port 0 bit 4 */ + SBIT(P0_3, 0x80, 3); /* Port 0 bit 3 */ + SBIT(P0_2, 0x80, 2); /* Port 0 bit 2 */ + SBIT(P0_1, 0x80, 1); /* Port 0 bit 1 */ + SBIT(P0_0, 0x80, 0); /* Port 0 bit 0 */ +SFR(SP, 0x81); /* Stack pointer */ +SFR(DPL0, 0x82); /* Data pointer 0 low byte */ +SFR(DPH0, 0x83); /* Data pointer 0 high byte */ +SFR(DPL1, 0x84); /* Data pointer 1 low byte */ +SFR(DPH1, 0x85); /* Data pointer 1 high byte */ +SFR(PCON, 0x87); /* Power mode control */ +SFR(TCON, 0x88); /* Interrupt flags */ + SBIT(URX1IF, 0x88, 7); /* USART1 RX interrupt flag */ + SBIT(ADCIF, 0x88, 5); /* ADC interrupt flag */ + SBIT(URX0IF, 0x88, 3); /* USART0 RX interrupt flag */ + SBIT(IT1, 0x88, 2); /* Reserved. Must always be set to 1 */ + SBIT(RFERRIF, 0x88, 1); /* RF TXFIFO/RXFIFO interrupt flag */ + SBIT(IT0, 0x88, 0); /* Reserved. Must always be set to 1 */ +SFR(P1, 0x90); /* Port 1 */ + SBIT(P1_7, 0x90, 7); /* Port 1 bit 7 */ + SBIT(P1_6, 0x90, 6); /* Port 1 bit 6 */ + SBIT(P1_5, 0x90, 5); /* Port 1 bit 5 */ + SBIT(P1_4, 0x90, 4); /* Port 1 bit 4 */ + SBIT(P1_3, 0x90, 3); /* Port 1 bit 3 */ + SBIT(P1_2, 0x90, 2); /* Port 1 bit 2 */ + SBIT(P1_1, 0x90, 1); /* Port 1 bit 1 */ + SBIT(P1_0, 0x90, 0); /* Port 1 bit 0 */ +SFR(DPS, 0x92); /* Data pointer select */ +SFR(S0CON, 0x98); /* Interrupt flags 2 */ + SBIT(ENCIF_1, 0x98, 1); /* AES Interrupt flag 1 */ + SBIT(ENCIF_0, 0x98, 0); /* AES Interrupt flag 0 */ +SFR(IEN2, 0x9A); /* Interrupt enable 2 */ +SFR(S1CON, 0x9B); /* Interrupt flags 3 */ +SFR(P2, 0xA0); /* Port 2 */ + SBIT(P2_7, 0xA0, 7); /* Port 2 bit 7 */ + SBIT(P2_6, 0xA0, 6); /* Port 2 bit 6 */ + SBIT(P2_5, 0xA0, 5); /* Port 2 bit 5 */ + SBIT(P2_4, 0xA0, 4); /* Port 2 bit 4 */ + SBIT(P2_3, 0xA0, 3); /* Port 2 bit 3 */ + SBIT(P2_2, 0xA0, 2); /* Port 2 bit 2 */ + SBIT(P2_1, 0xA0, 1); /* Port 2 bit 1 */ + SBIT(P2_0, 0xA0, 0); /* Port 2 bit 0 */ +SFR(IEN0, 0xA8); /* Interrupt enable 0 */ + SBIT(EA, 0xA8, 7); /* All interrupts - enable/disable */ + SBIT(STIE, 0xA8, 5); /* Sleep Timer interrupt enable */ + SBIT(ENCIE, 0xA8, 4); /* AES encryption/decryption interrupt enable */ + SBIT(URX1IE, 0xA8, 3); /* USART1 RX interrupt enable */ + SBIT(URX0IE, 0xA8, 2); /* USART0 RX interrupt enable */ + SBIT(ADCIE, 0xA8, 1); /* ADC interrupt enable */ + SBIT(RFERRIE, 0xA8, 0); /* RF TXFIFO/RXFIFO interrupt enable */ +SFR(IP0, 0xA9); /* Interrupt priority 0 */ +SFR(IEN1, 0xB8); /* Interrupt enable 1 */ + SBIT(P0IE, 0xB8, 5); /* Port 0 interrupt enable */ + SBIT(T4IE, 0xB8, 4); /* Timer 4 interrupt enable */ + SBIT(T3IE, 0xB8, 3); /* Timer 3 interrupt enable */ + SBIT(T2IE, 0xB8, 2); /* Timer 2 interrupt enable */ + SBIT(T1IE, 0xB8, 1); /* Timer 1 interrupt enable */ + SBIT(DMAIE, 0xB8, 0); /* DMA Transfer interrupt enable */ +SFR(IP1, 0xB9); /* Interrupt priority 1 */ +SFR(IRCON, 0xC0); /* Interrupt flags 4 */ + SBIT(STIF, 0xC0, 7); /* Sleep Timer interrupt flag */ + SBIT(P0IF, 0xC0, 5); /* Port 0 interrupt flag */ + SBIT(T4IF, 0xC0, 4); /* Timer 4 interrupt flag */ + SBIT(T3IF, 0xC0, 3); /* Timer 3 interrupt flag */ + SBIT(T2IF, 0xC0, 2); /* Timer 2 interrupt flag */ + SBIT(T1IF, 0xC0, 1); /* Timer 1 interrupt flag */ + SBIT(DMAIF, 0xC0, 0); /* DMA-complete interrupt flag */ +SFR(PSW, 0xD0); /* Program status word */ + SBIT(CY, 0xD0, 7); /* Carry flag */ + SBIT(AC, 0xD0, 6); /* Auxiliary carry flag */ + SBIT(F0, 0xD0, 5); /* User-defined flag 1, bit addressable */ + SBIT(RS1, 0xD0, 4); /* Register bank select, bit 1 */ + SBIT(RS0, 0xD0, 3); /* Register bank select, bit 0 */ + SBIT(OV, 0xD0, 2); /* Overflow flag */ + SBIT(F1, 0xD0, 1); /* User-defined flag 0, bit addressable */ + SBIT(P, 0xD0, 0); /* Parity flag */ +SFR(ACC, 0xE0); /* Accumulator */ + SBIT(ACC_7, 0xE0, 7); /* Accumulator bit 7 */ + SBIT(ACC_6, 0xE0, 6); /* Accumulator bit 6 */ + SBIT(ACC_5, 0xE0, 5); /* Accumulator bit 5 */ + SBIT(ACC_4, 0xE0, 4); /* Accumulator bit 4 */ + SBIT(ACC_3, 0xE0, 3); /* Accumulator bit 3 */ + SBIT(ACC_2, 0xE0, 2); /* Accumulator bit 2 */ + SBIT(ACC_1, 0xE0, 1); /* Accumulator bit 1 */ + SBIT(ACC_0, 0xE0, 0); /* Accumulator bit 0 */ +SFR(IRCON2, 0xE8); /* Interrupt flags 5 */ + SBIT(WDTIF, 0xE8, 4); /* Watchdog Timer interrupt flag */ + SBIT(P1IF, 0xE8, 3); /* Port 1 Interrupt flag */ + SBIT(UTX1IF, 0xE8, 2); /* USART1 TX interrupt flag */ + SBIT(UTX0IF, 0xE8, 1); /* USART0 TX interrupt flag */ + SBIT(P2IF, 0xE8, 0); /* Port 2 interrupt flag */ +SFR(B, 0xF0); /* B Register */ + SBIT(B_7, 0xF0, 7); /* Register B bit 7 */ + SBIT(B_6, 0xF0, 6); /* Register B bit 6 */ + SBIT(B_5, 0xF0, 5); /* Register B bit 5 */ + SBIT(B_4, 0xF0, 4); /* Register B bit 4 */ + SBIT(B_3, 0xF0, 3); /* Register B bit 3 */ + SBIT(B_2, 0xF0, 2); /* Register B bit 2 */ + SBIT(B_1, 0xF0, 1); /* Register B bit 1 */ + SBIT(B_0, 0xF0, 0); /* Register B bit 0 */ + +/* ADC */ +SFR(ADCCON1, 0xB4); /* ADC control 1 */ +SFR(ADCCON2, 0xB5); /* ADC control 2 */ +SFR(ADCCON3, 0xB6); /* ADC control 3 */ +SFR(ADCL, 0xBA); /* ADC data low */ +SFR(ADCH, 0xBB); /* ADC data high */ +SFR(RNDL, 0xBC); /* Random number generator data low */ +SFR(RNDH, 0xBD); /* Random number generator data high */ + +/* AES Coprocessor */ +SFR(ENCDI, 0xB1); /* Encryption/decryption input data */ +SFR(ENCDO, 0xB2); /* Encryption/decryption output data */ +SFR(ENCCS, 0xB3); /* Encryption/decryption control and status */ + +/* DMA Controller */ +SFR(DMAIRQ, 0xD1); /* DMA interrupt flag */ +SFR(DMA1CFGL, 0xD2); /* DMA channel 1–4 configuration address low */ +SFR(DMA1CFGH, 0xD3); /* DMA channel 1–4 configuration address high */ +SFR(DMA0CFGL, 0xD4); /* DMA channel 0 configuration address low */ +SFR(DMA0CFGH, 0xD5); /* DMA channel 0 configuration address high */ +SFR(DMAARM, 0xD6); /* DMA channel armed */ +SFR(DMAREQ, 0xD7); /* DMA channel start request and status */ + +/* I/O */ +SFR(P0IFG, 0x89); /* Port 0 interrupt status flag */ +SFR(P1IFG, 0x8A); /* Port 1 interrupt status flag */ +SFR(P2IFG, 0x8B); /* Port 2 interrupt status flag */ +SFR(PICTL, 0x8C); /* Port pins interrupt mask and edge */ +SFR(P0IEN, 0xAB); /* Port 0 interrupt mask */ +SFR(P1IEN, 0x8D); /* Port 1 interrupt mask */ +SFR(P2IEN, 0xAC); /* Port 2 interrupt mask */ +SFR(P0INP, 0x8F); /* Port 0 input Mode */ +SFR(PERCFG, 0xF1); /* Peripheral I/O control */ +SFR(APCFG, 0xF2); /* Analog peripheral I/O configuration */ +SFR(P0SEL, 0xF3); /* Port 0 function select */ +SFR(P1SEL, 0xF4); /* Port 1 function select */ +SFR(P2SEL, 0xF5); /* Port 2 function select */ +SFR(P1INP, 0xF6); /* Port 1 input mode */ +SFR(P2INP, 0xF7); /* Port 2 input mode */ +SFR(P0DIR, 0xFD); /* Port 0 direction */ +SFR(P1DIR, 0xFE); /* Port 1 direction */ +SFR(P2DIR, 0xFF); /* Port 2 direction */ +SFR(PMUX, 0xAE); /* Power-down signal mux */ + +/* Memory */ +SFR(MPAGE, 0x93); /* Memory page select */ +SFR(_XPAGE, 0x93); /* Memory page select - SDCC name */ +SFR(MEMCTR, 0xC7); /* Memory system control */ +SFR(FMAP, 0x9F); /* Flash-memory bank mapping */ +SFR(PSBANK, 0x9F); /* Flash-memory bank mapping - SDCC name */ + +/* RF */ +SFR(RFIRQF1, 0x91); /* RF interrupt flags MSB */ +SFR(RFD, 0xD9); /* RF data */ +SFR(RFST, 0xE1); /* RF command strobe */ +SFR(RFIRQF0, 0xE9); /* RF interrupt flags LSB */ +SFR(RFERRF, 0xBF); /* RF error interrupt flags */ + +/* Sleep Timer */ +SFR(ST0, 0x95); /* Sleep Timer 0 */ +SFR(ST1, 0x96); /* Sleep Timer 1 */ +SFR(ST2, 0x97); /* Sleep Timer 2 */ +SFR(STLOAD, 0xAD); /* Sleep-timer load status */ +SFR(SLEEPCMD, 0xBE); /* Sleep-mode control command */ +SFR(SLEEPSTA, 0x9D); /* Sleep-mode control status */ + +/* Power Management and Clocks */ +SFR(CLKCONCMD, 0xC6); /* Clock control command */ +SFR(CLKCONSTA, 0x9E); /* Clock control status */ + +/* Timer 1 */ +SFR(T1CC0L, 0xDA); /* Timer 1 channel 0 capture/compare value low */ +SFR(T1CC0H, 0xDB); /* Timer 1 channel 0 capture/compare value high */ +SFR(T1CC1L, 0xDC); /* Timer 1 channel 1 capture/compare value low */ +SFR(T1CC1H, 0xDD); /* Timer 1 channel 1 capture/compare value high */ +SFR(T1CC2L, 0xDE); /* Timer 1 channel 2 capture/compare value low */ +SFR(T1CC2H, 0xDF); /* Timer 1 channel 2 capture/compare value high */ +SFR(T1CNTL, 0xE2); /* Timer 1 counter low */ +SFR(T1CNTH, 0xE3); /* Timer 1 counter high */ +SFR(T1CTL, 0xE4); /* Timer 1 control and status */ +SFR(T1CCTL0, 0xE5); /* Timer 1 channel 0 capture/compare control */ +SFR(T1CCTL1, 0xE6); /* Timer 1 channel 1 capture/compare control */ +SFR(T1CCTL2, 0xE7); /* Timer 1 channel 2 capture/compare control */ +SFR(T1STAT, 0xAF); /* Timer 1 status */ + +/* Timer 2 (MAC Timer) */ +SFR(T2CTRL, 0x94); /* Timer 2 control */ +SFR(T2EVTCFG, 0x9C); /* Timer 2 event configuration */ +SFR(T2IRQF, 0xA1); /* Timer 2 interrupt flags */ +SFR(T2M0, 0xA2); /* Timer 2 multiplexed register 0 */ +SFR(T2M1, 0xA3); /* Timer 2 multiplexed register 1 */ +SFR(T2MOVF0, 0xA4); /* Timer 2 multiplexed overflow register 0 */ +SFR(T2MOVF1, 0xA5); /* Timer 2 multiplexed overflow register 1 */ +SFR(T2MOVF2, 0xA6); /* Timer 2 multiplexed overflow register 2 */ +SFR(T2IRQM, 0xA7); /* Timer 2 interrupt mask */ +SFR(T2MSEL, 0xC3); /* Timer 2 multiplex select */ + +/* Timer 3 */ +SFR(T3CNT, 0xCA); /* Timer 3 counter */ +SFR(T3CTL, 0xCB); /* Timer 3 control */ +SFR(T3CCTL0, 0xCC); /* Timer 3 channel 0 compare control */ +SFR(T3CC0, 0xCD); /* Timer 3 channel 0 compare value */ +SFR(T3CCTL1, 0xCE); /* Timer 3 channel 1 compare control */ +SFR(T3CC1, 0xCF); /* Timer 3 channel 1 compare value */ + +/* Timer 4 */ +SFR(T4CNT, 0xEA); /* Timer 4 counter */ +SFR(T4CTL, 0xEB); /* Timer 4 control */ +SFR(T4CCTL0, 0xEC); /* Timer 4 channel 0 compare control */ +SFR(T4CC0, 0xED); /* Timer 4 channel 0 compare value */ +SFR(T4CCTL1, 0xEE); /* Timer 4 channel 1 compare control */ +SFR(T4CC1, 0xEF); /* Timer 4 channel 1 compare value */ + +/* Timer 1, 3, 4 join Interrupts */ +SFR(TIMIF, 0xD8); /* Timers 1/3/4 joint interrupt mask/flags */ + SBIT(OVFIM, 0xD8, 6); /* Timer 1 overflow interrupt mask */ + SBIT(T4CH1IF, 0xD8, 5); /* Timer 4 channel 1 interrupt flag */ + SBIT(T4CH0IF, 0xD8, 4); /* Timer 4 channel 0 interrupt flag */ + SBIT(T4OVFIF, 0xD8, 3); /* Timer 4 overflow interrupt flag */ + SBIT(T3CH1IF, 0xD8, 2); /* Timer 3 channel 1 interrupt flag */ + SBIT(T3CH0IF, 0xD8, 1); /* Timer 3 channel 0 interrupt flag */ + SBIT(T3OVFIF, 0xD8, 0); /* Timer 3 overflow interrupt flag */ + +/* USART 0 */ +SFR(U0CSR, 0x86); /* USART 0 control and status */ +SFR(U0DBUF, 0xC1); /* USART 0 receive/transmit data buffer */ +SFR(U0BAUD, 0xC2); /* USART 0 baud-rate control */ +SFR(U0UCR, 0xC4); /* USART 0 UART control */ +SFR(U0GCR, 0xC5); /* USART 0 generic control */ + +/* USART 1 */ +SFR(U1CSR, 0xF8); /* USART 1 control and status */ + SBIT(MODE, 0xF8, 7); /* USART mode select */ + SBIT(RE, 0xF8, 6); /* UART receiver enable */ + SBIT(SLAVE, 0xF8, 5); /* SPI master- or slave mode select */ + SBIT(FE, 0xF8, 4); /* UART framing error status */ + SBIT(ERR, 0xF8, 3); /* UART parity error status */ + SBIT(RX_BYTE, 0xF8, 2); /* Receive byte status */ + SBIT(TX_BYTE, 0xF8, 1); /* Transmit byte status */ + SBIT(ACTIVE, 0xF8, 0); /* USART transmit/receive active status */ +SFR(U1DBUF, 0xF9); /* USART 1 receive/transmit data buffer */ +SFR(U1BAUD, 0xFA); /* USART 1 baud-rate control */ +SFR(U1UCR, 0xFB); /* USART 1 UART control */ +SFR(U1GCR, 0xFC); /* USART 1 Generic control */ + +/* Watchdog Timer */ +SFR(WDCTL, 0xC9); /* Watchdog Timer Control */ +/*--------------------------------------------------------------------------- + * XREG Registers (0x6000–0x63FF), excluding RF and USB registers + * (Table 2.2, page 31) + *---------------------------------------------------------------------------*/ +SFRX(MONMUX , 0x61A6); /* Operational amplifier mode control (cc2530/31) */ +SFRX(OPAMPMC, 0x61A6); /* Battery monitor MUX (cc2533) */ +/* I2C registers - cc2533 only */ +SFRX(I2CCFG, 0x6230); /* I2C control */ +SFRX(I2CSTAT, 0x6231); /* I2C status */ +SFRX(I2CDATA, 0x6232); /* I2C data */ +SFRX(I2CADDR, 0x6233); /* I2C own slave address */ +SFRX(I2CWC, 0x6234); /* Wrapper Control */ +SFRX(I2CIO, 0x6235); /* GPIO */ +/* End I2C registers */ +SFRX(OBSSEL0, 0x6243); /* Observation output control - register 0 */ +SFRX(OBSSEL1, 0x6244); /* Observation output control - register 1 */ +SFRX(OBSSEL2, 0x6245); /* Observation output control - register 2 */ +SFRX(OBSSEL3, 0x6246); /* Observation output control - register 3 */ +SFRX(OBSSEL4, 0x6247); /* Observation output control - register 4 */ +SFRX(OBSSEL5, 0x6248); /* Observation output control - register 5 */ +SFRX(CHVER, 0x6249); /* Chip version */ +SFRX(CHIPID, 0x624A); /* Chip identification */ +SFRX(TR0, 0x624B); /* Test register 0 */ +SFRX(DBGDATA, 0x6260); /* Debug interface write data */ +SFRX(SRCRC, 0x6262); /* Sleep reset CRC */ +SFRX(BATTMON, 0x6264); /* Battery monitor */ +SFRX(IVCTRL, 0x6265); /* Analog control register */ +SFRX(FCTL, 0x6270); /* Flash control */ +SFRX(FADDRL, 0x6271); /* Flash address low */ +SFRX(FADDRH, 0x6272); /* Flash address high */ +SFRX(FWDATA, 0x6273); /* Flash write data */ +SFRX(CHIPINFO0, 0x6276); /* Chip information byte 0 */ +SFRX(CHIPINFO1, 0x6277); /* Chip information byte 1 */ +SFRX(IRCTL, 0x6281); /* Timer 1 IR generation control */ +SFRX(CLD, 0x6290); /* Clock-loss detection */ +SFRX(XX_T1CCTL0, 0x62A0); /* Timer 1 channel 0 capture/compare control (additional XREG mapping of SFR) */ +SFRX(XX_T1CCTL1, 0x62A1); /* Timer 1 channel 1 capture/compare control (additional XREG mapping of SFR register) */ +SFRX(XX_T1CCTL2, 0x62A2); /* Timer 1 channel 2 capture/compare control (additional XREG mapping of SFR register) */ +SFRX(T1CCTL3, 0x62A3); /* Timer 1 channel 3 capture/compare control */ +SFRX(T1CCTL4, 0x62A4); /* Timer 1 channel 4 capture/compare control */ +SFRX(XX_T1CC0L, 0x62A6); /* Timer 1 channel 0 capture/compare value low (additional XREG mapping of SFR register) */ +SFRX(XX_T1CC0H, 0x62A7); /* Timer 1 channel 0 capture/compare value high (additional XREG mapping of SFR register) */ +SFRX(XX_T1CC1L, 0x62A8); /* Timer 1 channel 1 capture/compare value low (additional XREG mapping of SFR register) */ +SFRX(XX_T1CC1H, 0x62A9); /* Timer 1 channel 1 capture/compare value high (additional XREG mapping of SFR register) */ +SFRX(XX_T1CC2L, 0x62AA); /* Timer 1 channel 2 capture/compare value low (additional XREG mapping of SFR register) */ +SFRX(XX_T1CC2H, 0x62AB); /* Timer 1 channel 2 capture/compare value high (additional XREG mapping of SFR register) */ +SFRX(T1CC3L, 0x62AC); /* Timer 1 channel 3 capture/compare value low */ +SFRX(T1CC3H, 0x62AD); /* Timer 1 channel 3 capture/compare value high */ +SFRX(T1CC4L, 0x62AE); /* Timer 1 channel 4 capture/compare value low */ +SFRX(T1CC4H, 0x62AF); /* Timer 1 channel 4 capture/compare value high */ +SFRX(STCC, 0x62B0); /* Sleep Timer capture control */ +SFRX(STCS, 0x62B1); /* Sleep Timer capture status */ +SFRX(STCV0, 0x62B2); /* Sleep Timer capture value byte 0 */ +SFRX(STCV1, 0x62B3); /* Sleep Timer capture value byte 1 */ +SFRX(STCV2, 0x62B4); /* Sleep Timer capture value byte 2 */ +SFRX(OPAMPC, 0x62C0); /* Operational amplifier control */ +SFRX(OPAMPS, 0x62C1); /* Operational amplifier status */ +SFRX(CMPCTL, 0x62D0); /* Analog comparator control and status */ +/*--------------------------------------------------------------------------- + * Radio Registers + * (Sec. 23, page 211) + *---------------------------------------------------------------------------*/ +SFRX(RFCORE_RAM, 0x6000); /* RF Core Memory Map (0x6000 to 0x0617F) */ +SFRX(RXFIFO, 0x6000); /* TXFIFO Direct Access (0x6000 to 0x607F) */ +SFRX(TXFIFO, 0x6080); /* TXFIFO Direct Access (0x6080 to 0x60FF) */ + +SFRX(SRC_ADDR_TABLE, 0x6100); /* Source Address Table Start */ + +/* Source Address Matching Result */ +SFRX(SRCRESMASK0, 0x6160); /* Extended address matching */ +SFRX(SRCRESMASK1, 0x6161); /* Short address matching */ +SFRX(SRCRESMASK2, 0x6162); /* Source address match - 24-bit mask */ +SFRX(SRCRESINDEX, 0x6163); /* Bit index of least-significant 1 in SRCRESMASK */ + +/* Source Address Matching Control */ +SFRX(SRCEXTPENDEN0, 0x6164); /* Ext. Address bit-mask 0 (LSB) */ +SFRX(SRCEXTPENDEN1, 0x6165); /* Ext. Address bit-mask 1 */ +SFRX(SRCEXTPENDEN2, 0x6166); /* Ext. Address bit-mask 2 (MSB) */ +SFRX(SRCSHORTPENDEN0, 0x6167); /* Short Address bit-mask 0 (LSB) */ +SFRX(SRCSHORTPENDEN1, 0x6168); /* Short Address bit-mask 1 */ +SFRX(SRCSHORTPENDEN2, 0x6169); /* Short Address bit-mask 2 (MSB) */ + +/* Local Address Information (used during destination address filtering) */ +SFRX(EXT_ADDR0, 0x616A); /* IEEE extended address 0 */ +SFRX(EXT_ADDR1, 0x616B); /* IEEE extended address 1 */ +SFRX(EXT_ADDR2, 0x616C); /* IEEE extended address 2 */ +SFRX(EXT_ADDR3, 0x616D); /* IEEE extended address 3 */ +SFRX(EXT_ADDR4, 0x616E); /* IEEE extended address 4 */ +SFRX(EXT_ADDR5, 0x616F); /* IEEE extended address 5 */ +SFRX(EXT_ADDR6, 0x6170); /* IEEE extended address 6 */ +SFRX(EXT_ADDR7, 0x6171); /* IEEE extended address 7 */ +SFRX(PAN_ID0, 0x6172); /* PAN ID 0 */ +SFRX(PAN_ID1, 0x6173); /* PAN ID 1 */ +SFRX(SHORT_ADDR0, 0x6174); /* Short Address 0 */ +SFRX(SHORT_ADDR1, 0x6175); /* Short Address 1 */ + +SFRX(FRMFILT0, 0x6180); /* Frame Filtering 0 */ +SFRX(FRMFILT1, 0x6181); /* Frame Filtering 1 */ +SFRX(SRCMATCH, 0x6182); /* Source Address Matching and Pending Bits */ +SFRX(SRCSHORTEN0, 0x6183); /* Short Address Matching 0 */ +SFRX(SRCSHORTEN1, 0x6184); /* Short Address Matching 1 */ +SFRX(SRCSHORTEN2, 0x6185); /* Short Address Matching 2 */ +SFRX(SRCEXTEN0, 0x6186); /* Extended Address Matching 0 */ +SFRX(SRCEXTEN1, 0x6187); /* Extended Address Matching 1 */ +SFRX(SRCEXTEN2, 0x6188); /* Extended Address Matching 2 */ +SFRX(FRMCTRL0, 0x6189); /* Frame Handling */ +SFRX(FRMCTRL1, 0x618A); /* Frame Handling */ +SFRX(RXENABLE, 0x618B); /* RX Enabling */ +SFRX(RXMASKSET, 0x618C); /* RX Enabling */ +SFRX(RXMASKCLR, 0x618D); /* RX Disabling */ +SFRX(FREQTUNE, 0x618E); /* Crystal Oscillator Frequency Tuning */ +SFRX(FREQCTRL, 0x618F); /* RF Frequency Control */ +SFRX(TXPOWER, 0x6190); /* Controls the Output Power */ +SFRX(TXCTRL, 0x6191); /* Controls the TX Settings */ +SFRX(FSMSTAT0, 0x6192); /* Radio Status Register */ +SFRX(FSMSTAT1, 0x6193); /* Radio Status Register */ +SFRX(FIFOPCTRL, 0x6194); /* FIFOP Threshold */ +SFRX(FSMCTRL, 0x6195); /* FSM Options */ +SFRX(CCACTRL0, 0x6196); /* CCA Threshold */ +SFRX(CCACTRL1, 0x6197); /* Other CCA Options */ +SFRX(RSSI, 0x6198); /* RSSI Status Register */ +SFRX(RSSISTAT, 0x6199); /* RSSI Valid Status Register */ +SFRX(RXFIRST, 0x619A); /* First Byte in RXFIFO */ +SFRX(RXFIFOCNT, 0x619B); /* Number of Bytes in RXFIFO */ +SFRX(TXFIFOCNT, 0x619C); /* Number of Bytes in TXFIFO */ +SFRX(RXFIRST_PTR, 0x619D); /* RXFIFO Pointer */ +SFRX(RXLAST_PTR, 0x619E); /* RXFIFO Pointer */ +SFRX(RXP1_PTR, 0x619F); /* RXFIFO Pointer */ +SFRX(TXFIRST_PTR, 0x61A1); /* TXFIFO Pointer */ +SFRX(TXLAST_PTR, 0x61A2); /* TXFIFO Pointer */ +SFRX(RFIRQM0, 0x61A3); /* RF Interrupt Masks 0 */ +SFRX(RFIRQM1, 0x61A4); /* RF Interrupt Masks 1 */ +SFRX(RFERRM, 0x61A5); /* RF Error Interrupt Mask */ +SFRX(RFRND, 0x61A7); /* Random Data */ +SFRX(MDMCTRL0, 0x61A8); /* Controls Modem 0 */ +SFRX(MDMCTRL1, 0x61A9); /* Controls Modem 1 */ +SFRX(FREQEST, 0x61AA); /* Estimated RF Frequency Offset */ +SFRX(RXCTRL, 0x61AB); /* Tune Receive Section */ +SFRX(FSCTRL, 0x61AC); /* Tune Frequency Synthesizer */ +SFRX(FSCAL0, 0x61AD); /* Tune Frequency Calibration 0 */ +SFRX(FSCAL1, 0x61AE); /* Tune Frequency Calibration 1 */ +SFRX(FSCAL2, 0x61AF); /* Tune Frequency Calibration 2 */ +SFRX(FSCAL3, 0x61B0); /* Tune Frequency Calibration 3 */ +SFRX(AGCCTRL0, 0x61B1); /* AGC Dynamic Range Control */ +SFRX(AGCCTRL1, 0x61B2); /* AGC Reference Level */ +SFRX(AGCCTRL2, 0x61B3); /* AGC Gain Override */ +SFRX(AGCCTRL3, 0x61B4); /* AGC Control */ +SFRX(ADCTEST0, 0x61B5); /* ADC Tuning 0 */ +SFRX(ADCTEST1, 0x61B6); /* ADC Tuning 1 */ +SFRX(ADCTEST2, 0x61B7); /* ADC Tuning 2 */ +SFRX(MDMTEST0, 0x61B8); /* Test Register for Modem 0 */ +SFRX(MDMTEST1, 0x61B9); /* Test Register for Modem 1 */ +SFRX(DACTEST0, 0x61BA); /* DAC Override Value */ +SFRX(DACTEST1, 0x61BB); /* DAC Override Value */ +SFRX(DACTEST2, 0x61BC); /* DAC Test Setting */ +SFRX(ATEST, 0x61BD); /* Analog Test Control */ +SFRX(PTEST0, 0x61BE); /* Override Power-Down Register 0 */ +SFRX(PTEST1, 0x61BF); /* Override Power-Down Register 1 */ +SFRX(TXFILTCFG, 0x61FA); /* TX Filter Configuration */ +SFRX(RFC_OBS_CTRL0, 0x61EB); /* RF Observation Mux Control 0 */ +SFRX(RFC_OBS_CTRL1, 0x61EC); /* RF Observation Mux Control 1 */ +SFRX(RFC_OBS_CTRL2, 0x61ED); /* RF Observation Mux Control 2 */ + +/* Command Strobe/CSMA-CA Processor Registers */ +SFRX(CSPPROG0, 0x61C0); /* CSP Program Memory, Byte 0 */ +SFRX(CSPPROG1, 0x61C1); /* CSP Program Memory, Byte 1 */ +SFRX(CSPPROG2, 0x61C2); /* CSP Program Memory, Byte 2 */ +SFRX(CSPPROG3, 0x61C3); /* CSP Program Memory, Byte 3 */ +SFRX(CSPPROG4, 0x61C4); /* CSP Program Memory, Byte 4 */ +SFRX(CSPPROG5, 0x61C5); /* CSP Program Memory, Byte 5 */ +SFRX(CSPPROG6, 0x61C6); /* CSP Program Memory, Byte 6 */ +SFRX(CSPPROG7, 0x61C7); /* CSP Program Memory, Byte 7 */ +SFRX(CSPPROG8, 0x61C8); /* CSP Program Memory, Byte 8 */ +SFRX(CSPPROG9, 0x61C9); /* CSP Program Memory, Byte 9 */ +SFRX(CSPPROG10, 0x61CA); /* CSP Program Memory, Byte 10 */ +SFRX(CSPPROG11, 0x61CB); /* CSP Program Memory, Byte 11 */ +SFRX(CSPPROG12, 0x61CC); /* CSP Program Memory, Byte 12 */ +SFRX(CSPPROG13, 0x61CD); /* CSP Program Memory, Byte 13 */ +SFRX(CSPPROG14, 0x61CE); /* CSP Program Memory, Byte 14 */ +SFRX(CSPPROG15, 0x61CF); /* CSP Program Memory, Byte 15 */ +SFRX(CSPPROG16, 0x61D0); /* CSP Program Memory, Byte 16 */ +SFRX(CSPPROG17, 0x61D1); /* CSP Program Memory, Byte 17 */ +SFRX(CSPPROG18, 0x61D2); /* CSP Program Memory, Byte 18 */ +SFRX(CSPPROG19, 0x61D3); /* CSP Program Memory, Byte 19 */ +SFRX(CSPPROG20, 0x61D4); /* CSP Program Memory, Byte 20 */ +SFRX(CSPPROG21, 0x61D5); /* CSP Program Memory, Byte 21 */ +SFRX(CSPPROG22, 0x61D6); /* CSP Program Memory, Byte 22 */ +SFRX(CSPPROG23, 0x61D7); /* CSP Program Memory, Byte 23 */ +SFRX(CSPCTRL, 0x61E0); /* CSP Control Bit */ +SFRX(CSPSTAT, 0x61E1); /* CSP Status Register */ +SFRX(CSPX, 0x61E2); /* CSP X Register */ +SFRX(CSPY, 0x61E3); /* CSP Y Register */ +SFRX(CSPZ, 0x61E4); /* CSP Z Register */ +SFRX(CSPT, 0x61E5); /* CSP T Register */ +/*--------------------------------------------------------------------------- + * cc2531 USB Registers + * (Sec. 21.12, page 196) + *---------------------------------------------------------------------------*/ +SFRX(USBADDR, 0x6200); /* Function Address */ +SFRX(USBPOW, 0x6201); /* Power/Control Register */ +SFRX(USBIIF, 0x6202); /* IN Endpoints and EP0 Interrupt Flags */ +SFRX(USBOIF, 0x6204); /* OUT-Endpoint Interrupt Flags */ +SFRX(USBCIF, 0x6206); /* Common USB Interrupt Flags */ +SFRX(USBIIE, 0x6207); /* IN Endpoints and EP0 Interrupt-Enable Mask */ +SFRX(USBOIE, 0x6209); /* Out Endpoints Interrupt Enable Mask */ +SFRX(USBCIE, 0x620B); /* Common USB Interrupt-Enable Mask */ +SFRX(USBFRML, 0x620C); /* Current Frame Number (Low Byte) */ +SFRX(USBFRMH, 0x620D); /* Current Frame Number (High Byte) */ +SFRX(USBINDEX, 0x620E); /* Current-Endpoint Index Register */ +SFRX(USBCTRL, 0x620F); /* USB Control Register */ +SFRX(USBMAXI, 0x6210); /* Max. Packet Size for IN Endpoint{1–5} */ +SFRX(USBCS0, 0x6211); /* EP0 Control and Status (USBINDEX = 0) */ +SFRX(USBCSIL, 0x6211); /* IN EP{1–5} Control and Status, Low */ +SFRX(USBCSIH, 0x6212); /* IN EP{1–5} Control and Status, High */ +SFRX(USBMAXO, 0x6213); /* Max. Packet Size for OUT EP{1–5} */ +SFRX(USBCSOL, 0x6214); /* OUT EP{1–5} Control and Status, Low */ +SFRX(USBCSOH, 0x6215); /* OUT EP{1–5} Control and Status, High */ +SFRX(USBCNT0, 0x6216); /* Number of Received Bytes in EP0 FIFO (USBINDEX = 0) */ +SFRX(USBCNTL, 0x6216); /* Number of Bytes in EP{1–5} OUT FIFO, Low */ +SFRX(USBCNTH, 0x6217); /* Number of Bytes in EP{1–5} OUT FIFO, High */ +SFRX(USBF0, 0x6220); /* Endpoint-0 FIFO */ +SFRX(USBF1, 0x6222); /* Endpoint-1 FIFO */ +SFRX(USBF2, 0x6224); /* Endpoint-2 FIFO */ +SFRX(USBF3, 0x6226); /* Endpoint-3 FIFO */ +SFRX(USBF4, 0x6228); /* Endpoint-4 FIFO */ +SFRX(USBF5, 0x622A); /* Endpoint-5 FIFO */ +/*--------------------------------------------------------------------------- + * SFR Access via XDATA (0x7080 - 0x70FF) + *---------------------------------------------------------------------------*/ +SFRX(X_P0, 0x7080); /* Port 0 - Read Only */ +SFRX(X_U0CSR, 0x7086); /* USART 0 control and status */ +SFRX(X_P0IFG, 0x7089); /* Port 0 interrupt status flag */ +SFRX(X_P1IFG, 0x708A); /* Port 1 interrupt status flag */ +SFRX(X_P2IFG, 0x708B); /* Port 2 interrupt status flag */ +SFRX(X_PICTL, 0x708C); /* Port pins interrupt mask and edge */ +SFRX(X_P1IEN, 0x708D); /* Port 1 interrupt mask */ +SFRX(X_P0INP, 0x708F); /* Port 0 input Mode */ +SFRX(X_P1, 0x7090); /* Port 1 - Read Only */ +SFRX(X_RFIRQF1, 0x7091); /* RF interrupt flags MSB */ +SFRX(X_MPAGE, 0x7093); /* Memory page select */ +SFRX(X__XPAGE, 0x7093); /* Memory page select - SDCC name */ +SFRX(X_T2CTRL, 0x7094); /* Timer 2 control */ +SFRX(X_ST0, 0x7095); /* Sleep Timer 0 */ +SFRX(X_ST1, 0x7096); /* Sleep Timer 1 */ +SFRX(X_ST2, 0x7097); /* Sleep Timer 2 */ +SFRX(X_T2EVTCFG, 0x709C); /* Timer 2 event configuration */ +SFRX(X_SLEEPSTA, 0x709D); /* Sleep-mode control status */ +SFRX(X_CLKCONSTA, 0x709E); /* Clock control status */ +SFRX(X_FMAP, 0x709F); /* Flash-memory bank mapping */ +SFRX(X_PSBANK, 0x709F); /* Flash-memory bank mapping - SDCC name */ +SFRX(X_P2, 0x70A0); /* Port 2 - Read Only */ +SFRX(X_T2IRQF, 0x70A1); /* Timer 2 interrupt flags */ +SFRX(X_T2M0, 0x70A2); /* Timer 2 multiplexed register 0 */ +SFRX(X_T2M1, 0x70A3); /* Timer 2 multiplexed register 1 */ +SFRX(X_T2MOVF0, 0x70A4); /* Timer 2 multiplexed overflow register 0 */ +SFRX(X_T2MOVF1, 0x70A5); /* Timer 2 multiplexed overflow register 1 */ +SFRX(X_T2MOVF2, 0x70A6); /* Timer 2 multiplexed overflow register 2 */ +SFRX(X_T2IRQM, 0x70A7); /* Timer 2 interrupt mask */ +SFRX(X_P0IEN, 0x70AB); /* Port 0 interrupt mask */ +SFRX(X_P2IEN, 0x70AC); /* Port 2 interrupt mask */ +SFRX(X_STLOAD, 0x70AD); /* Sleep-timer load status */ +SFRX(X_PMUX, 0x70AE); /* Power-down signal mux */ +SFRX(X_T1STAT, 0x70AF); /* Timer 1 status */ +SFRX(X_ENCDI, 0x70B1); /* Encryption/decryption input data */ +SFRX(X_ENCDO, 0x70B2); /* Encryption/decryption output data */ +SFRX(X_ENCCS, 0x70B3); /* Encryption/decryption control and status */ +SFRX(X_ADCCON1, 0x70B4); /* ADC control 1 */ +SFRX(X_ADCCON2, 0x70B5); /* ADC control 2 */ +SFRX(X_ADCCON3, 0x70B6); /* ADC control 3 */ +SFRX(X_ADCL, 0x70BA); /* ADC data low */ +SFRX(X_ADCH, 0x70BB); /* ADC data high */ +SFRX(X_RNDL, 0x70BC); /* Random number generator data low */ +SFRX(X_RNDH, 0x70BD); /* Random number generator data high */ +SFRX(X_SLEEPCMD, 0x70BE); /* Sleep-mode control command */ +SFRX(X_RFERRF, 0x70BF); /* RF error interrupt flags */ +SFRX(X_U0DBUF, 0x70C1); /* USART 0 receive/transmit data buffer */ +SFRX(X_U0BAUD, 0x70C2); /* USART 0 baud-rate control */ +SFRX(X_T2MSEL, 0x70C3); /* Timer 2 multiplex select */ +SFRX(X_U0UCR, 0x70C4); /* USART 0 UART control */ +SFRX(X_U0GCR, 0x70C5); /* USART 0 generic control */ +SFRX(X_CLKCONCMD, 0x70C6); /* Clock control command */ +SFRX(X_MEMCTR, 0x70C7); /* Memory system control */ +SFRX(X_WDCTL, 0x70C9); /* Watchdog Timer Control */ +SFRX(X_T3CNT, 0x70CA); /* Timer 3 counter */ +SFRX(X_T3CTL, 0x70CB); /* Timer 3 control */ +SFRX(X_T3CCTL0, 0x70CC); /* Timer 3 channel 0 compare control */ +SFRX(X_T3CC0, 0x70CD); /* Timer 3 channel 0 compare value */ +SFRX(X_T3CCTL1, 0x70CE); /* Timer 3 channel 1 compare control */ +SFRX(X_T3CC1, 0x70CF); /* Timer 3 channel 1 compare value */ +SFRX(X_DMAIRQ, 0x70D1); /* DMA interrupt flag */ +SFRX(X_DMA1CFGL, 0x70D2); /* DMA channel 1–4 configuration address low */ +SFRX(X_DMA1CFGH, 0x70D3); /* DMA channel 1–4 configuration address high */ +SFRX(X_DMA0CFGL, 0x70D4); /* DMA channel 0 configuration address low */ +SFRX(X_DMA0CFGH, 0x70D5); /* DMA channel 0 configuration address high */ +SFRX(X_DMAARM, 0x70D6); /* DMA channel armed */ +SFRX(X_DMAREQ, 0x70D7); /* DMA channel start request and status */ +SFRX(X_TIMIF, 0x70D8); /* Timers 1/3/4 joint interrupt mask/flags */ +SFRX(X_RFD, 0x70D9); /* RF data */ +SFRX(X_T1CC0L, 0x70DA); /* Timer 1 channel 0 capture/compare value low */ +SFRX(X_T1CC0H, 0x70DB); /* Timer 1 channel 0 capture/compare value high */ +SFRX(X_T1CC1L, 0x70DC); /* Timer 1 channel 1 capture/compare value low */ +SFRX(X_T1CC1H, 0x70DD); /* Timer 1 channel 1 capture/compare value high */ +SFRX(X_T1CC2L, 0x70DE); /* Timer 1 channel 2 capture/compare value low */ +SFRX(X_T1CC2H, 0x70DF); /* Timer 1 channel 2 capture/compare value high */ +SFRX(X_RFST, 0x70E1); /* RF command strobe */ +SFRX(X_T1CNTL, 0x70E2); /* Timer 1 counter low */ +SFRX(X_T1CNTH, 0x70E3); /* Timer 1 counter high */ +SFRX(X_T1CTL, 0x70E4); /* Timer 1 control and status */ +SFRX(X_T1CCTL0, 0x70E5); /* Timer 1 channel 0 capture/compare control */ +SFRX(X_T1CCTL1, 0x70E6); /* Timer 1 channel 1 capture/compare control */ +SFRX(X_T1CCTL2, 0x70E7); /* Timer 1 channel 2 capture/compare control */ +SFRX(X_RFIRQF0, 0x70E9); /* RF interrupt flags LSB */ +SFRX(X_T4CNT, 0x70EA); /* Timer 4 counter */ +SFRX(X_T4CTL, 0x70EB); /* Timer 4 control */ +SFRX(X_T4CCTL0, 0x70EC); /* Timer 4 channel 0 compare control */ +SFRX(X_T4CC0, 0x70ED); /* Timer 4 channel 0 compare value */ +SFRX(X_T4CCTL1, 0x70EE); /* Timer 4 channel 1 compare control */ +SFRX(X_T4CC1, 0x70EF); /* Timer 4 channel 1 compare value */ +SFRX(X_PERCFG, 0x70F1); /* Peripheral I/O control */ +SFRX(X_APCFG, 0x70F2); /* Analog peripheral I/O configuration */ +SFRX(X_P0SEL, 0x70F3); /* Port 0 function select */ +SFRX(X_P1SEL, 0x70F4); /* Port 1 function select */ +SFRX(X_P2SEL, 0x70F5); /* Port 2 function select */ +SFRX(X_P1INP, 0x70F6); /* Port 1 input mode */ +SFRX(X_P2INP, 0x70F7); /* Port 2 input mode */ +SFRX(X_U1CSR, 0x70F8); /* USART 1 control and status */ +SFRX(X_U1DBUF, 0x70F9); /* USART 1 receive/transmit data buffer */ +SFRX(X_U1BAUD, 0x70FA); /* USART 1 baud-rate control */ +SFRX(X_U1UCR, 0x70FB); /* USART 1 UART control */ +SFRX(X_U1GCR, 0x70FC); /* USART 1 Generic control */ +SFRX(X_P0DIR, 0x70FD); /* Port 0 direction */ +SFRX(X_P1DIR, 0x70FE); /* Port 1 direction */ +SFRX(X_P2DIR, 0x70FF); /* Port 2 direction */ +/*--------------------------------------------------------------------------- + * Information Page (Read Only) + *---------------------------------------------------------------------------*/ +SFRX(X_INFOPAGE, 0x7800); /* Start of Information Page */ +SFRX(X_IEEE_ADDR, 0x780C); /* Start of unique IEEE Address */ + +#endif /* __CC253X_H__ */ diff --git a/cpu/cc253x/dev/cc2530-rf.c b/cpu/cc253x/dev/cc2530-rf.c new file mode 100644 index 000000000..fc01d8a37 --- /dev/null +++ b/cpu/cc253x/dev/cc2530-rf.c @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Implementation of the cc2530 RF driver + * + * \author + * George Oikonomou - + */ +#include "contiki.h" +#include "dev/radio.h" +#include "dev/cc2530-rf.h" +#include "cc253x.h" +#include "sfr-bits.h" +#include "sys/clock.h" +#include "sys/rtimer.h" + +#include "net/packetbuf.h" +#include "net/rime/rimestats.h" +#include "net/netstack.h" + +#include +/*---------------------------------------------------------------------------*/ +#define CHECKSUM_LEN 2 +/*---------------------------------------------------------------------------*/ +#if CC2530_RF_CONF_LEDS +#define CC2530_RF_LEDS CC2530_RF_CONF_LEDS +#else +#define CC2530_RF_LEDS 0 +#endif + +#if CC2530_RF_LEDS +#include "dev/leds.h" +#define RF_RX_LED_ON() leds_on(LEDS_RED); +#define RF_RX_LED_OFF() leds_off(LEDS_RED); +#define RF_TX_LED_ON() leds_on(LEDS_GREEN); +#define RF_TX_LED_OFF() leds_off(LEDS_GREEN); +#else +#define RF_RX_LED_ON() +#define RF_RX_LED_OFF() +#define RF_TX_LED_ON() +#define RF_TX_LED_OFF() +#endif +/*---------------------------------------------------------------------------*/ +#define DEBUG 0 +#if DEBUG +#include "debug.h" +#define PUTSTRING(...) putstring(__VA_ARGS__) +#define PUTHEX(...) puthex(__VA_ARGS__) +#else +#define PUTSTRING(...) +#define PUTHEX(...) +#endif +/*---------------------------------------------------------------------------*/ +/* Local RF Flags */ +#define RX_ACTIVE 0x80 +#define WAS_OFF 0x10 +#define RF_ON 0x01 + +/* Bit Masks for the last byte in the RX FIFO */ +#define CRC_BIT_MASK 0x80 +#define LQI_BIT_MASK 0x7F + +/* 192 ms, radio off -> on interval */ +#define ONOFF_TIME RTIMER_ARCH_SECOND / 3125 +/*---------------------------------------------------------------------------*/ +#if CC2530_RF_CONF_HEXDUMP +#include "uart0.h" +static const uint8_t magic[] = { 0x53, 0x6E, 0x69, 0x66 }; /* Snif */ +#endif +/*---------------------------------------------------------------------------*/ +#ifdef CC2530_RF_CONF_AUTOACK +#define CC2530_RF_AUTOACK CC2530_RF_CONF_AUTOACK +#else +#define CC2530_RF_AUTOACK 1 +#endif +/*---------------------------------------------------------------------------*/ +static uint8_t __data rf_flags; + +static int on(void); /* prepare() needs our prototype */ +static int off(void); /* transmit() needs our prototype */ +static int channel_clear(void); /* transmit() needs our prototype */ +/*---------------------------------------------------------------------------*/ +int8_t +cc2530_rf_channel_set(uint8_t channel) +{ + PUTSTRING("RF: Set Chan\n"); + + if((channel < CC2530_RF_CHANNEL_MIN) || (channel > CC2530_RF_CHANNEL_MAX)) { + return -1; + } + + /* Changes to FREQCTRL take effect after the next recalibration */ + off(); + FREQCTRL = (CC2530_RF_CHANNEL_MIN + + (channel - CC2530_RF_CHANNEL_MIN) * CC2530_RF_CHANNEL_SPACING); + on(); + + return (int8_t) channel; +} +/*---------------------------------------------------------------------------*/ +uint8_t +cc2530_rf_power_set(uint8_t new_power) +{ + PUTSTRING("RF: Set Power\n"); + /* off() */ + TXPOWER = new_power; + /* on() */ + + return TXPOWER; +} +/*---------------------------------------------------------------------------*/ +void +cc2530_rf_set_addr(uint16_t pan) +{ + PAN_ID0 = pan & 0xFF; + PAN_ID1 = pan >> 8; + + SHORT_ADDR0 = ((uint8_t *)&X_IEEE_ADDR)[0]; + SHORT_ADDR1 = ((uint8_t *)&X_IEEE_ADDR)[1]; + + memcpy(&EXT_ADDR0, &X_IEEE_ADDR, 8); +} +/*---------------------------------------------------------------------------*/ +/* Netstack API radio driver functions */ +/*---------------------------------------------------------------------------*/ +static int +init(void) +{ + PUTSTRING("RF: Init\n"); + + if(rf_flags & RF_ON) { + return 0; + } + +#ifdef CC2530_RF_LOW_POWER_RX + /* Reduce RX power consumption current to 20mA at the cost of sensitivity */ + RXCTRL = 0x00; + FSCTRL = 0x50; +#else + RXCTRL = 0x3F; + FSCTRL = 0x55; +#endif /* CC2530_RF_LOW_POWER_RX */ + + CCACTRL0 = CC2530_RF_CCA_THRES; + + /* + * According to the user guide, these registers must be updated from their + * defaults for optimal performance + * + * Table 23-6, Sec. 23.15.1, p. 259 + */ + TXFILTCFG = 0x09; /* TX anti-aliasing filter */ + AGCCTRL1 = 0x15; /* AGC target value */ + FSCAL1 = 0x00; /* Reduce the VCO leakage */ + + /* Auto ACKs and CRC calculation, default RX and TX modes with FIFOs */ + FRMCTRL0 = FRMCTRL0_AUTOCRC; +#if CC2530_RF_AUTOACK + FRMCTRL0 |= FRMCTRL0_AUTOACK; +#endif + + /* Disable source address matching and autopend */ + SRCMATCH = 0; /* investigate */ + + /* MAX FIFOP threshold */ + FIFOPCTRL = CC2530_RF_MAX_PACKET_LEN; + + cc2530_rf_power_set(CC2530_RF_TX_POWER); + cc2530_rf_channel_set(CC2530_RF_CHANNEL); + + RF_TX_LED_OFF(); + RF_RX_LED_OFF(); + + rf_flags |= RF_ON; + + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +prepare(const void *payload, unsigned short payload_len) +{ + uint8_t i; + + PUTSTRING("RF: Prepare 0x"); + PUTHEX(payload_len + CHECKSUM_LEN); + PUTSTRING(" bytes\n"); + + /* + * When we transmit in very quick bursts, make sure previous transmission + * is not still in progress before re-writing to the TX FIFO + */ + while(FSMSTAT1 & FSMSTAT1_TX_ACTIVE); + + if((rf_flags & RX_ACTIVE) == 0) { + on(); + } + + CC2530_CSP_ISFLUSHTX(); + + PUTSTRING("RF: data = "); + /* Send the phy length byte first */ + RFD = payload_len + CHECKSUM_LEN; /* Payload plus FCS */ + for(i = 0; i < payload_len; i++) { + RFD = ((unsigned char*) (payload))[i]; + PUTHEX(((unsigned char*)(payload))[i]); + } + PUTSTRING("\n"); + + /* Leave space for the FCS */ + RFD = 0; + RFD = 0; + + return 0; +} +/*---------------------------------------------------------------------------*/ +static int +transmit(unsigned short transmit_len) +{ + uint8_t counter; + int ret = RADIO_TX_ERR; + rtimer_clock_t t0; + transmit_len; /* hush the warning */ + + if(!(rf_flags & RX_ACTIVE)) { + t0 = RTIMER_NOW(); + on(); + rf_flags |= WAS_OFF; + while (RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + ONOFF_TIME)); + } + + if(channel_clear() == CC2530_RF_CCA_BUSY) { + RIMESTATS_ADD(contentiondrop); + return RADIO_TX_COLLISION; + } + + /* + * prepare() double checked that TX_ACTIVE is low. If SFD is high we are + * receiving. Abort transmission and bail out with RADIO_TX_COLLISION + */ + if(FSMSTAT1 & FSMSTAT1_SFD) { + RIMESTATS_ADD(contentiondrop); + return RADIO_TX_COLLISION; + } + + /* Start the transmission */ + RF_TX_LED_ON(); + ENERGEST_OFF(ENERGEST_TYPE_LISTEN); + ENERGEST_ON(ENERGEST_TYPE_TRANSMIT); + + CC2530_CSP_ISTXON(); + + counter = 0; + while(!(FSMSTAT1 & FSMSTAT1_TX_ACTIVE) && (counter++ < 3)) { + clock_delay(10); + } + + if(!(FSMSTAT1 & FSMSTAT1_TX_ACTIVE)) { + PUTSTRING("RF: TX never active.\n"); + CC2530_CSP_ISFLUSHTX(); + ret = RADIO_TX_ERR; + } else { + /* Wait for the transmission to finish */ + while(FSMSTAT1 & FSMSTAT1_TX_ACTIVE); + ret = RADIO_TX_OK; + } + ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT); + ENERGEST_ON(ENERGEST_TYPE_LISTEN); + + if(rf_flags & WAS_OFF){ + off(); + } + + RIMESTATS_ADD(lltx); + + RF_TX_LED_OFF(); + + /* OK, sent. We are now ready to send more */ + return ret; +} +/*---------------------------------------------------------------------------*/ +static int +send(void *payload, unsigned short payload_len) +{ + prepare(payload, payload_len); + return transmit(payload_len); +} +/*---------------------------------------------------------------------------*/ +static int +read(void *buf, unsigned short bufsize) +{ + uint8_t i; + uint8_t len; + uint8_t crc_corr; + int8_t rssi; + + PUTSTRING("RF: Read\n"); + + /* Check the length */ + len = RFD; + + /* Check for validity */ + if(len > CC2530_RF_MAX_PACKET_LEN) { + /* Oops, we must be out of sync. */ + PUTSTRING("RF: bad sync\n"); + + RIMESTATS_ADD(badsynch); + CC2530_CSP_ISFLUSHRX(); + return 0; + } + + if(len <= CC2530_RF_MIN_PACKET_LEN) { + PUTSTRING("RF: too short\n"); + + RIMESTATS_ADD(tooshort); + CC2530_CSP_ISFLUSHRX(); + return 0; + } + + if(len - CHECKSUM_LEN > bufsize) { + PUTSTRING("RF: too long\n"); + + RIMESTATS_ADD(toolong); + CC2530_CSP_ISFLUSHRX(); + return 0; + } + +#if CC2530_RF_CONF_HEXDUMP + /* If we reach here, chances are the FIFO is holding a valid frame */ + uart0_writeb(magic[0]); + uart0_writeb(magic[1]); + uart0_writeb(magic[2]); + uart0_writeb(magic[3]); + uart0_writeb(len); +#endif + + RF_RX_LED_ON(); + + PUTSTRING("RF: read (0x"); + PUTHEX(len); + PUTSTRING(" bytes) = "); + len -= CHECKSUM_LEN; + for(i = 0; i < len; ++i) { + ((unsigned char*)(buf))[i] = RFD; +#if CC2530_RF_CONF_HEXDUMP + uart0_writeb(((unsigned char*)(buf))[i]); +#endif + PUTHEX(((unsigned char*)(buf))[i]); + } + PUTSTRING("\n"); + + /* Read the RSSI and CRC/Corr bytes */ + rssi = ((int8_t) RFD) - 45; + crc_corr = RFD; + +#if CC2530_RF_CONF_HEXDUMP + uart0_writeb(rssi); + uart0_writeb(crc_corr); +#endif + + /* MS bit CRC OK/Not OK, 7 LS Bits, Correlation value */ + if(crc_corr & CRC_BIT_MASK) { + packetbuf_set_attr(PACKETBUF_ATTR_RSSI, rssi); + packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, crc_corr & LQI_BIT_MASK); + RIMESTATS_ADD(llrx); + } else { + RIMESTATS_ADD(badcrc); + CC2530_CSP_ISFLUSHRX(); + RF_RX_LED_OFF(); + return 0; + } + + /* If FIFOP==1 and FIFO==0 then we had a FIFO overflow at some point. */ + if((FSMSTAT1 & (FSMSTAT1_FIFO | FSMSTAT1_FIFOP)) == FSMSTAT1_FIFOP) { + /* + * If we reach here means that there might be more intact packets in the + * FIFO despite the overflow. This can happen with bursts of small packets. + * + * Only flush if the FIFO is actually empty. If not, then next pass we will + * pick up one more packet or flush due to an error. + */ + if(!RXFIFOCNT) { + CC2530_CSP_ISFLUSHRX(); + } + } + + RF_RX_LED_OFF(); + + return (len); +} +/*---------------------------------------------------------------------------*/ +static int +channel_clear(void) +{ + if(FSMSTAT1 & FSMSTAT1_CCA) { + return CC2530_RF_CCA_CLEAR; + } + return CC2530_RF_CCA_BUSY; +} +/*---------------------------------------------------------------------------*/ +static int +receiving_packet(void) +{ + PUTSTRING("RF: Receiving\n"); + + /* + * SFD high while transmitting and receiving. + * TX_ACTIVE high only when transmitting + * + * FSMSTAT1 & (TX_ACTIVE | SFD) == SFD <=> receiving + */ + return (FSMSTAT1 & (FSMSTAT1_TX_ACTIVE | FSMSTAT1_SFD) == FSMSTAT1_SFD); +} +/*---------------------------------------------------------------------------*/ +static int +pending_packet(void) +{ + return (FSMSTAT1 & FSMSTAT1_FIFOP); +} +/*---------------------------------------------------------------------------*/ +static int +on(void) +{ + if(!(rf_flags & RX_ACTIVE)) { + CC2530_CSP_ISFLUSHRX(); + CC2530_CSP_ISRXON(); + + rf_flags |= RX_ACTIVE; + } + + ENERGEST_ON(ENERGEST_TYPE_LISTEN); + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +off(void) +{ + CC2530_CSP_ISRFOFF(); + CC2530_CSP_ISFLUSHRX(); + + rf_flags = 0; + + ENERGEST_OFF(ENERGEST_TYPE_LISTEN); + return 1; +} +/*---------------------------------------------------------------------------*/ +const struct radio_driver cc2530_rf_driver = +{ + init, + prepare, + transmit, + send, + read, + channel_clear, + receiving_packet, + pending_packet, + on, + off, +}; +/*---------------------------------------------------------------------------*/ diff --git a/cpu/cc253x/dev/cc2530-rf.h b/cpu/cc253x/dev/cc2530-rf.h new file mode 100644 index 000000000..397b952a3 --- /dev/null +++ b/cpu/cc253x/dev/cc2530-rf.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Implementation of the cc2530 RF driver + * + * \author + * George Oikonomou - + */ +#ifndef __CC2530_RF_H__ +#define __CC2530_RF_H__ + +#include "contiki.h" +#include "dev/radio.h" +#include "cc253x.h" +/*--------------------------------------------------------------------------- + * RF Config + *---------------------------------------------------------------------------*/ +#define CC2530_RF_TX_POWER_RECOMMENDED 0xD5 +#ifdef CC2530_RF_CONF_TX_POWER +#define CC2530_RF_TX_POWER CC2530_RF_CONF_TX_POWER +#else +#define CC2530_RF_TX_POWER CC2530_RF_TX_POWER_RECOMMENDED +#endif /* CC2530_RF_CONF_TX_POWER */ + +#ifdef CC2530_RF_CONF_CCA_THRES +#define CC2530_RF_CCA_THRES CC2530_RF_CONF_CCA_THRES +#else +#define CC2530_RF_CCA_THRES CCA_THRES_USER_GUIDE /* User guide recommendation */ +#endif /* CC2530_RF_CONF_CCA_THRES */ + +#ifdef CC2530_RF_CONF_CHANNEL +#define CC2530_RF_CHANNEL CC2530_RF_CONF_CHANNEL +#else +#define CC2530_RF_CHANNEL 18 +#endif /* CC2530_RF_CONF_CHANNEL */ +#define CC2530_RF_CHANNEL_MIN 11 +#define CC2530_RF_CHANNEL_MAX 26 +#define CC2530_RF_CHANNEL_SPACING 5 + +#ifdef CC2530_RF_CONF_AUTOACK +#define CC2530_RF_AUTOACK CC2530_RF_CONF_AUTOACK +#else +#define CC2530_RF_AUTOACK 1 +#endif /* CC2530_RF_CONF_AUTOACK */ + +#ifdef CC2530_RF_CONF_LOW_POWER_RX +#define CC2530_RF_LOW_POWER_RX CC2530_RF_CONF_LOW_POWER_RX +#else +#define CC2530_RF_LOW_POWER_RX 0 +#endif /* CC2530_RF_CONF_LOW_POWER_RX */ +/*---------------------------------------------------------------------------*/ +#define CCA_THRES_USER_GUIDE 0xF8 +#define CCA_THRES_ALONE 0xFC /* -4-76=-80dBm when CC2530 operated alone or with CC2591 in LGM */ +#define CCA_THR_HGM 0x06 /* 6-76=-70dBm when CC2530 operated with CC2591 in HGM */ +#define CORR_THR 0x14 +/*---------------------------------------------------------------------------*/ +#define CC2530_RF_MAX_PACKET_LEN 127 +#define CC2530_RF_MIN_PACKET_LEN 4 +/*---------------------------------------------------------------------------*/ +#define CC2530_RF_CCA_CLEAR 1 +#define CC2530_RF_CCA_BUSY 0 + +/* Wait for RSSI to be valid. */ +#define CC2530_RF_CCA_VALID_WAIT() while(!(RSSISTAT & RSSIST)) +/*--------------------------------------------------------------------------- + * Command Strobe Processor + *---------------------------------------------------------------------------*/ +/* OPCODES */ +#define CSP_OP_ISRXON 0xE3 +#define CSP_OP_ISTXON 0xE9 +#define CSP_OP_ISTXONCCA 0xEA +#define CSP_OP_ISRFOFF 0xEF +#define CSP_OP_ISFLUSHRX 0xED +#define CSP_OP_ISFLUSHTX 0xEE + +#define CC2530_CSP_ISRXON() do { RFST = CSP_OP_ISRXON; } while(0) +#define CC2530_CSP_ISTXON() do { RFST = CSP_OP_ISTXON; } while(0) +#define CC2530_CSP_ISTXONCCA() do { RFST = CSP_OP_ISTXONCCA; } while(0) +#define CC2530_CSP_ISRFOFF() do { RFST = CSP_OP_ISRFOFF; } while(0) + +/* OP x 2 for flushes */ +#define CC2530_CSP_ISFLUSHRX() do { \ + RFST = CSP_OP_ISFLUSHRX; \ + RFST = CSP_OP_ISFLUSHRX; \ +} while(0) +#define CC2530_CSP_ISFLUSHTX() do { \ + RFST = CSP_OP_ISFLUSHTX; \ + RFST = CSP_OP_ISFLUSHTX; \ +} while(0) +/*---------------------------------------------------------------------------*/ +extern const struct radio_driver cc2530_rf_driver; +/*---------------------------------------------------------------------------*/ +int8_t cc2530_rf_channel_set(uint8_t channel); +uint8_t cc2530_rf_power_set(uint8_t new_power); +void cc2530_rf_set_addr(uint16_t pan); +/*---------------------------------------------------------------------------*/ +#endif /* __CC2530_RF_H__ */ diff --git a/cpu/cc253x/dev/clock-isr.h b/cpu/cc253x/dev/clock-isr.h new file mode 100644 index 000000000..005ac38c6 --- /dev/null +++ b/cpu/cc253x/dev/clock-isr.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Declaration of the Sleep timer ISR, used by the clock module + * \author + * George Oikonomou - + */ +#ifndef __CLOCK_ISR_H__ +#define __CLOCK_ISR_H__ + +#include "contiki-conf.h" +#include "cc253x.h" + +void clock_isr(void) __interrupt(ST_VECTOR); + +#endif /* __CLOCK_ISR_H__ */ diff --git a/cpu/cc253x/dev/clock.c b/cpu/cc253x/dev/clock.c new file mode 100644 index 000000000..6d9a5fdc1 --- /dev/null +++ b/cpu/cc253x/dev/clock.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2009, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + * $Id: clock.c,v 1.1 2009/09/08 20:07:35 zdshelby Exp $ + */ + +/** + * \file + * Implementation of the clock functions for the 8051 CPU + * \author + * Zach Shelby (zach@sensinode.com) - original + * George Oikonomou - - cc2530 port + */ +#include "sfr-bits.h" +#include "sys/clock.h" +#include "sys/etimer.h" +#include "cc253x.h" +#include "sys/energest.h" + +/* Sleep timer runs on the 32k RC osc. */ +/* One clock tick is 7.8 ms */ +#define TICK_VAL (32768/128) /* 256 */ + +#define MAX_TICKS (~((clock_time_t)0) / 2) +/*---------------------------------------------------------------------------*/ +/* Do NOT remove the absolute address and do NOT remove the initialiser here */ +__xdata __at(0x0000) static unsigned long timer_value = 0; + +static volatile __data clock_time_t count = 0; /* Uptime in ticks */ +static volatile __data clock_time_t seconds = 0; /* Uptime in secs */ +/*---------------------------------------------------------------------------*/ +/** + * One delay is about 0.6 us, so this function delays for len * 0.6 us + */ +void +clock_delay(unsigned int len) +{ + unsigned int i; + for(i = 0; i< len; i++) { + ASM(nop); + } +} +/*---------------------------------------------------------------------------*/ +/** + * Wait for a multiple of ~8 ms (a tick) + */ +void +clock_wait(int i) +{ + clock_time_t start; + + start = clock_time(); + while(clock_time() - start < (clock_time_t)i); +} +/*---------------------------------------------------------------------------*/ +CCIF clock_time_t +clock_time(void) +{ + return count; +} +/*---------------------------------------------------------------------------*/ +CCIF unsigned long +clock_seconds(void) +{ + return seconds; +} +/*---------------------------------------------------------------------------*/ +/* + * There is some ambiguity between TI cc2530 software examples and information + * in the datasheet. + * + * TI examples appear to be writing to SLEEPCMD, initialising hardware in a + * fashion semi-similar to cc2430 + * + * However, the datasheet claims that those bits in SLEEPCMD are reserved + * + * The code here goes by the datasheet (ignore TI examples) and seems to work. + */ +void +clock_init(void) +{ + /* Make sure we know where we stand */ + CLKCONCMD = CLKCONCMD_OSC32K | CLKCONCMD_OSC; + + /* Stay with 32 KHz RC OSC, Chance System Clock to 32 MHz */ + CLKCONCMD &= ~CLKCONCMD_OSC; + while(CLKCONSTA & CLKCONCMD_OSC); + + /* Tickspeed 500 kHz for timers[1-4] */ + CLKCONCMD |= CLKCONCMD_TICKSPD2 | CLKCONCMD_TICKSPD1; + while(CLKCONSTA != CLKCONCMD); + + /*Initialize tick value*/ + timer_value = ST0; + timer_value += ((unsigned long int) ST1) << 8; + timer_value += ((unsigned long int) ST2) << 16; + timer_value += TICK_VAL; + ST2 = (unsigned char) (timer_value >> 16); + ST1 = (unsigned char) (timer_value >> 8); + ST0 = (unsigned char) timer_value; + + STIE = 1; /* IEN0.STIE interrupt enable */ +} +/*---------------------------------------------------------------------------*/ +void +clock_isr(void) __interrupt(ST_VECTOR) +{ + DISABLE_INTERRUPTS(); + ENERGEST_ON(ENERGEST_TYPE_IRQ); + + /* + * If the Sleep timer throws an interrupt while we are powering down to + * PM1, we need to abort the power down. Clear SLEEP.MODE, this will signal + * main() to abort the PM1 transition + * + * On cc2430 this would be: + * SLEEPCMD &= 0xFC; + */ + + /* + * Read value of the ST0:ST1:ST2, add TICK_VAL and write it back. + * Next interrupt occurs after the current time + TICK_VAL + */ + timer_value = ST0; + timer_value += ((unsigned long int) ST1) << 8; + timer_value += ((unsigned long int) ST2) << 16; + timer_value += TICK_VAL; + ST2 = (unsigned char) (timer_value >> 16); + ST1 = (unsigned char) (timer_value >> 8); + ST0 = (unsigned char) timer_value; + + ++count; + + /* Make sure the CLOCK_CONF_SECOND is a power of two, to ensure + that the modulo operation below becomes a logical and and not + an expensive divide. Algorithm from Wikipedia: + http://en.wikipedia.org/wiki/Power_of_two */ +#if (CLOCK_CONF_SECOND & (CLOCK_CONF_SECOND - 1)) != 0 +#error CLOCK_CONF_SECOND must be a power of two (i.e., 1, 2, 4, 8, 16, 32, 64, ...). +#error Change CLOCK_CONF_SECOND in contiki-conf.h. +#endif + if(count % CLOCK_CONF_SECOND == 0) { + ++seconds; + } + + if(etimer_pending() + && (etimer_next_expiration_time() - count - 1) > MAX_TICKS) { + etimer_request_poll(); + } + + STIF = 0; /* IRCON.STIF */ + ENERGEST_OFF(ENERGEST_TYPE_IRQ); + ENABLE_INTERRUPTS(); +} +/*---------------------------------------------------------------------------*/ diff --git a/cpu/cc253x/dev/dma.c b/cpu/cc253x/dev/dma.c new file mode 100644 index 000000000..992c3dd57 --- /dev/null +++ b/cpu/cc253x/dev/dma.c @@ -0,0 +1,69 @@ +/** + * \file + * Driver for the cc2430 DMA controller. Can be assigned to any bank + * + * \author + * Original: Martti Huttunen + * Port: Zach Shelby + * Further Modifications: + * George Oikonomou + * + */ + +#include "contiki.h" +#include "dev/dma.h" +#include "cc253x.h" + +#if DMA_ON +struct dma_config dma_conf[DMA_CHANNEL_COUNT]; /* DMA Descriptors */ +struct process * dma_callback[DMA_CHANNEL_COUNT]; +/*---------------------------------------------------------------------------*/ +void +dma_init(void) +{ + uint16_t tmp_ptr; + + memset(dma_conf, 0, 4 * sizeof(dma_config_t)); + + for(tmp_ptr = 0; tmp_ptr < DMA_CHANNEL_COUNT; tmp_ptr++) { + dma_callback[tmp_ptr] = 0; + } + + /* The address of the descriptor for Channel 0 is configured separately */ + tmp_ptr = (uint16_t) &(dma_conf[0]); + DMA0CFGH = tmp_ptr >> 8; + DMA0CFGL = tmp_ptr; + + /* + * Descriptors for Channels 1-4 must be consecutive in RAM. + * We write the address of the 1st one to the register and the rest are + * derived by the SoC + */ +#if (DMA_CHANNEL_COUNT > 1) + tmp_ptr = (uint16_t) &(dma_conf[1]); + DMA1CFGH = tmp_ptr >> 8; + DMA1CFGL = tmp_ptr; +#endif + + IEN1_DMAIE = 1; /* Enable DMA interrupts */ +} +/*---------------------------------------------------------------------------*/ +/* + * Associate process p with DMA channel c. When a transfer on that channel + * completes, the ISR will poll this process. + */ +void +dma_associate_process(struct process * p, uint8_t c) +{ + if((!c) || (c >= DMA_CHANNEL_COUNT)) { + return; + } + + if(p) { + dma_conf[c].inc_prio |= 8; /* Enable interrupt generation */ + IEN1_DMAIE = 1; /* Make sure DMA interrupts are acknowledged */ + } + dma_callback[c] = p; +} +/*---------------------------------------------------------------------------*/ +#endif diff --git a/cpu/cc253x/dev/dma.h b/cpu/cc253x/dev/dma.h new file mode 100644 index 000000000..83e283a7a --- /dev/null +++ b/cpu/cc253x/dev/dma.h @@ -0,0 +1,148 @@ +/** + * \file + * Header file for the cc2430 DMA controller + * + * \author + * Original: Martti Huttunen + * Port: Zach Shelby + * Further Modifications: + * George Oikonomou + */ + +#ifndef __DMA_H +#define __DMA_H +#include "cc253x.h" + +/* DMA triggers */ +#define DMA_T_NONE 0 /* None, DMAREQ.DMAREQx bits start transfer */ +#define DMA_T_PREV 1 /* completion of previous channel */ +#define DMA_T_T1_CH0 2 /* Timer 1, compare, channel 0 */ +#define DMA_T_T1_CH1 3 /* Timer 1, compare, channel 1 */ +#define DMA_T_T1_CH2 4 /* Timer 1, compare, channel 2 */ +#define DMA_T_T2_COMP 5 /* Timer 2, compare */ +#define DMA_T_T2_OVFL 6 /* Timer 2, overflow */ +#define DMA_T_T3_CH0 7 /* Timer 3, compare, channel 0 */ +#define DMA_T_T3_CH1 8 /* Timer 3, compare, channel 1 */ +#define DMA_T_T4_CH0 9 /* Timer 4, compare, channel 0 */ +#define DMA_T_T4_CH1 10 /* Timer 4, compare, channel 1 */ +#define DMA_T_ST 11 /* Sleep Timer compare */ +#define DMA_T_IOC_0 12 /* Port 0 I/O pin input transition */ +#define DMA_T_IOC_1 13 /* Port 1 I/O pin input transition */ +#define DMA_T_URX0 14 /* USART0 RX complete */ +#define DMA_T_UTX0 15 /* USART0 TX complete */ +#define DMA_T_URX1 16 /* USART1 RX complete */ +#define DMA_T_UTX1 17 /* USART1 TX complete */ +#define DMA_T_FLASH 18 /* Flash data write complete */ +#define DMA_T_RADIO 19 /* RF packet byte received/transmit */ +#define DMA_T_ADC_CHALL 20 /* ADC end of a conversion in a sequence */ +#define DMA_T_ADC_CH11 21 /* ADC end of conversion channel 0 in sequence */ +#define DMA_T_ADC_CH21 22 /* ADC end of conversion channel 1 in sequence */ +#define DMA_T_ADC_CH32 23 /* ADC end of conversion channel 2 in sequence */ +#define DMA_T_ADC_CH42 24 /* ADC end of conversion channel 3 in sequence */ +#define DMA_T_ADC_CH53 25 /* ADC end of conversion channel 4 in sequence */ +#define DMA_T_ADC_CH63 26 /* ADC end of conversion channel 5 in sequence */ +#define DMA_T_ADC_CH74 27 /* ADC end of conversion channel 6 in sequence */ +#define DMA_T_ADC_CH84 28 /* ADC end of conversion channel 7 in sequence */ +#define DMA_T_ENC_DW 29 /* AES processor requests download input data */ +#define DMA_T_ENC_UP 30 /* AES processor requests upload output data */ + +/* variable DMA length modes (VLEN) */ +#define DMA_VLEN_LEN (0 << 5) /* Use LEN for transfer count*/ +/* + * Transfer the number of bytes/words specified by first byte/word + 1 + * (up to a maximum specified by LEN). + * Thus transfer count excludes length byte/word. + */ +#define DMA_VLEN_N1 (1 << 5) + /* + * Transfer the number of bytes/words specified by first byte/word + * (up to a maximum specified by LEN). + * Thus transfer count includes length byte/word. + */ +#define DMA_VLEN_N (2 << 5) + /* + * Transfer the number of bytes/words specified by first byte/word + 2 + * (up to a maximum specified by LEN). + */ +#define DMA_VLEN_N2 (3 << 5) + /* + * Transfer the number of bytes/words specified by first byte/word + 3 + * (up to a maximum specified by LEN). + */ +#define DMA_VLEN_N3 (4 << 5) +#define DMA_VLEN_RES1 (5 << 5) /* reserved */ +#define DMA_VLEN_RES2 (6 << 5) /* reserved */ +#define DMA_VLEN_LEN2 (7 << 5) /* Use LEN for transfer count */ + +/* Transfer Types (Byte 6 [6:5]) */ +#define DMA_SINGLE 0x00 /* Single */ +#define DMA_BLOCK 0x20 /* Block */ +#define DMA_RPT_SINGLE 0x40 /* Repeated single */ +#define DMA_RPT_BLOCK 0x60 /* Repeated block */ + +/* Source Increment Modes (Byte 7 [7:6])*/ +#define DMA_SRC_INC_NO 0x00 /* Source No increment */ +#define DMA_SRC_INC_1 0x40 /* Source Increment 1 */ +#define DMA_SRC_INC_2 0x80 /* Source Increment 2 */ +#define DMA_SRC_DEC 0xC0 /* Source Decrement 1 */ +/* Source Increment Modes (Byte 7 [5:4])*/ +#define DMA_DST_INC_NO 0x00 /* DestinationNo increment */ +#define DMA_DST_INC_1 0x10 /* Destination Increment 1 */ +#define DMA_DST_INC_2 0x20 /* Destination Increment 2 */ +#define DMA_DST_DEC 0x30 /* Destination Decrement 1 */ + +/* Descriptor Byte 7, Bits[3:0] */ +#define DMA_IRQ_MASK_ENABLE 0x08 +#define DMA_MODE_7_BIT 0x04 +#define DMA_PRIO_HIGHEST 0x03 +#define DMA_PRIO_HIGH 0x02 +#define DMA_PRIO_GUARANTEED 0x01 +#define DMA_PRIO_LOW 0x00 + +/** DMA configuration structure */ +typedef struct dma_config { + uint8_t src_h; /* source address high byte*/ + uint8_t src_l; /* source address low byte*/ + uint8_t dst_h; /* dest. address high byte*/ + uint8_t dst_l; /* dest. address low byte*/ + uint8_t len_h; /* [7:5] VLEN, [4:0] length high byte, 5 lowest bits*/ + uint8_t len_l; /* length low byte*/ + uint8_t wtt; /* 7: wordsize, [6:5] transfer mode, [4:0] trigger */ + /* [7:6] src inc, [5:4] dst_inc, 3: IRQ, 2: M8(vlen), [1-0] prio */ + uint8_t inc_prio; +} dma_config_t; + +#ifdef DMA_CONF_ON +#define DMA_ON DMA_CONF_ON +#else +#define DMA_ON 0 +#endif + +/* Number of DMA Channels and their Descriptors */ +#if DMA_ON +#define DMA_CHANNEL_COUNT 2 +extern dma_config_t dma_conf[DMA_CHANNEL_COUNT]; +#endif + +/* DMA-Related Macros */ +#define DMA_ARM(c) (DMAARM |= (1 << c)) /* Arm DMA Channel C */ +#define DMA_TRIGGER(c) (DMAREQ |= (1 << c)) /* Trigger DMA Channel C */ +/* + * Check Channel C for Transfer Status + * 1: Complete, Pending Interrupt, 0: Incomplete + */ +#define DMA_STATUS(c) (DMAIRQ &(1 << c)) +/* Abort Ongoing DMA Transfers on Channel C */ +#define DMA_ABORT(c) (DMAARM = ABORT | (1 << c)) +#define DMA_ABORT_ALL() (DMAARM = 0x9F) /* Abort ALL Ongoing DMA Transfers */ + +/* Functions Declarations */ +void dma_init(void); +void dma_associate_process (struct process * p, uint8_t c); + +/* Only link the ISR when DMA_ON is .... on */ +#if DMA_ON +void dma_isr( void ) __interrupt (DMA_VECTOR); +#endif + +#endif /*__DMA_H*/ diff --git a/cpu/cc253x/dev/dma_intr.c b/cpu/cc253x/dev/dma_intr.c new file mode 100644 index 000000000..9f2336293 --- /dev/null +++ b/cpu/cc253x/dev/dma_intr.c @@ -0,0 +1,68 @@ +/** + * \file + * DMA driver ISRs + * \author + * Original: Martti Huttunen + * Port: Zach Shelby + * + * DMA interrupt routines, must be stored in HOME bank + */ + +#include + +#include "contiki.h" + +#include "dev/dma.h" +#include "cc253x.h" + +#if DMA_ON +extern struct process * dma_callback[DMA_CHANNEL_COUNT]; +#endif + +/*---------------------------------------------------------------------------*/ +#ifdef HAVE_RF_DMA +extern void rf_dma_callback_isr(void); +#endif +#ifdef SPI_DMA_RX +extern void spi_rx_dma_callback(void); +#endif +/*---------------------------------------------------------------------------*/ +/** + * DMA interrupt service routine. + * + * if callback defined a poll is made to that process + */ +void +dma_isr(void) __interrupt (DMA_VECTOR) +{ +#if DMA_ON + uint8_t i; +#endif + EA=0; + DMAIF = 0; +#ifdef HAVE_RF_DMA + if((DMAIRQ & 1) != 0) { + DMAIRQ &= ~1; + DMAARM=0x81; + rf_dma_callback_isr(); + } +#endif +#ifdef SPI_DMA_RX + if((DMAIRQ & 0x08) != 0) { + DMAIRQ &= ~(1 << 3); + spi_rx_dma_callback(); + } +#endif +#if DMA_ON + for(i = 0; i < DMA_CHANNEL_COUNT; i++) { + if((DMAIRQ & (1 << i)) != 0) { + DMAIRQ &= ~(1 << i); + if(dma_callback[i] != 0) { + process_poll(dma_callback[i]); + } + } + } +#endif + EA = 1; +} +/*---------------------------------------------------------------------------*/ diff --git a/cpu/cc253x/dev/lpm.h b/cpu/cc253x/dev/lpm.h new file mode 100644 index 000000000..4a8f14710 --- /dev/null +++ b/cpu/cc253x/dev/lpm.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Header file for the cc2430 Low Power Modes (LPM) + * We currently support the following: + * - Set MCU IDLE while in PM0. This is working as intended + * - Drop to PM1. This results in incoming radio packet losses. + * + * \author + * George Oikonomou - + */ +#ifndef __LPM_H__ +#define __LPM_H__ + +#include "contiki-conf.h" + +#define LPM_MODE_NONE 0 /* No LPM - Always on */ +#define LPM_MODE_IDLE 1 /* Set MCU Idle as part of the main loop */ +#define LPM_MODE_PM2 2 /* Drop to PM1 - causes radio packet losses for now */ + +#ifdef LPM_CONF_MODE +#define LPM_MODE LPM_CONF_MODE +#else +#define LPM_MODE LPM_MODE_IDLE +#endif /* LPM_CONF_MODE */ + +#endif /* __LPM_H__ */ diff --git a/cpu/cc253x/dev/port.h b/cpu/cc253x/dev/port.h new file mode 100644 index 000000000..6fc69d10c --- /dev/null +++ b/cpu/cc253x/dev/port.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * + * \author + * George Oikonomou - + */ + + +#ifndef PORT_H_ +#define PORT_H_ + +#include "cc253x.h" +#include "sfr-bits.h" +/*---------------------------------------------------------------------------*/ +/* Use these to configure your platform's hardware */ +#define PORT_FUNC_GPIO(port,pin) PORT_FUNC_GPIO_X(port,pin) +#define PORT_FUNC_PER(port,pin) PORT_FUNC_PER_X(port,pin) +#define PORT0_ANALOG_IO(pin) PORT0_ANALOG_IO_X(pin) +#define PORT0_DIGITAL_IO(pin) PORT0_DIGITAL_IO_X(pin) +#define PORT_SET(port,pin) PORT_SET_X(port,pin) +#define PORT_CLEAR(port,pin) PORT_CLEAR_X(port,pin) PORT_CLEAR_X(port,pin) +#define PORT_TOGGLE(port,pin) PORT_TOGGLE_X(port,pin) PORT_TOGGLE_X(port,pin) +#define PORT_READ(port,pin) PORT_READ_X(port,pin) +#define PORT_WRITE(port,pin,v) PORT_WRITE_X(port,pin,v) +#define PORT_DIR_OUTPUT(port,pin) PORT_DIR_OUTPUT_X(port,pin) +#define PORT_DIR_INPUT(port,pin) PORT_DIR_INPUT_X(port,pin) +#define PORT_IRQ_ENABLE(port,pin) PORT_IRQ_ENABLE_X(port,pin) +#define PORT_IRQ_DISABLE(port,pin) PORT_IRQ_DISABLE_X(port,pin) +#define PORT_IRQ_ENABLED(port,pin) PORT_IRQ_ENABLED_X(port,pin) +#define PORT_IRQ_CHECK(port,pin) PORT_IRQ_CHECK_X(port,pin) +#define PORT_IRQ_EDGE_FALL(port,pin) PORT_IRQ_EDGE_FALL_X(port,pin) +#define PORT_IRQ_EDGE_RISE(port,pin) PORT_IRQ_EDGE_RISE_X(port,pin) +#define PORT_IRQ_FLAG_OFF(port,pin) PORT_IRQ_FLAG_OFF_X(port,pin) +/*---------------------------------------------------------------------------*/ +/* Second Round of Macro Substitutions. Normally, you can stop reading here */ +/*---------------------------------------------------------------------------*/ +#define PORT_FUNC_GPIO_X(port,pin) do { P##port##SEL &= ~(1 << pin); } while(0) +#define PORT_FUNC_PER_X(port,pin) do { P##port##SEL |= 1 << pin; } while(0) +#define PORT0_ANALOG_IO_X(port,pin) do { APCFG |= 1 << pin; } while(0) +#define PORT0_DIGITAL_IO_X(port,pin) do { APCFG &= ~(1 << pin); } while(0) +#define PORT_SET_X(port,pin) do { P##port##_##pin = 1; } while(0) +#define PORT_CLEAR_X(port,pin) do { P##port##_##pin = 0; } while(0) +#define PORT_TOGGLE_X(port,pin) do { P##port##_##pin ^= 1; } while(0) +#define PORT_READ_X(port,pin) (P##port##_##pin) +#define PORT_WRITE_X(port,pin,v) do { P##port##_##pin = v;} while(0) +#define PORT_DIR_OUTPUT_X(port,pin) do { P##port##DIR |= 1 << pin; } while(0) +#define PORT_DIR_INPUT_X(port,pin) do { P##port##DIR &= ~(1 << pin); } while(0) +#define PORT_IRQ_ENABLE_X(port,pin) do { \ + P##port##IEN |= 1 << pin; \ + PORT##port##_IRQ_ENABLE(); \ +} while(0) +#define PORT_IRQ_DISABLE_X(port,pin) do { \ + P##port##IEN &= ~(1 << pin); \ + PORT##port##_IRQ_DISABLE(); \ +} while(0) +#define PORT_IRQ_ENABLED_X(port,pin) (P##port##IEN & (1 << pin)) +#define PORT_IRQ_CHECK_X(port,pin) (P##port##IFG & (1 << pin)) +#define PORT_IRQ_EDGE_FALL_X(port,pin) PORT##port##_IRQ_EDGE_FALL(pin) +#define PORT_IRQ_EDGE_RISE_X(port,pin) PORT##port##_IRQ_EDGE_RISE(pin) +#define PORT_IRQ_FLAG_OFF_X(port,pin) do { \ + P##port##IFG &= ~(1 << pin); \ + P##port##IF = 0; \ +} while(0) +/*---------------------------------------------------------------------------*/ +/* To handle SFR diversities + * - P0IE is in IEN1, which is bit-addressable, + * P1IE and P2IE are in IEN2, which is not bit-addressable + * - Edge detection (rising / falling) config is uniform for all pins in + * P0 and P2. For P1, low and high nibble bits are configured separately + * - Pullup/Pulldown/Tristate is quite different for each port + * + * You won't have to invoke these macros directly + */ +#define PORT0_IRQ_ENABLE() do { P0IE = 1; } while(0) +#define PORT0_IRQ_DISABLE() do { P0IE = 0; } while(0) +#define PORT1_IRQ_ENABLE() PORT_IRQ_EN_X(1) +#define PORT1_IRQ_DISABLE() PORT_IRQ_DIS_X(1) +#define PORT2_IRQ_ENABLE() PORT_IRQ_EN_X(2) +#define PORT2_IRQ_DISABLE() PORT_IRQ_DIS_X(2) + +#define PORT_IRQ_EN_X(port) do { IEN2 |= IEN2_P##port##IE; } while(0) +#define PORT_IRQ_DIS_X(port) do { IEN2 &= ~IEN2_P##port##IE; } while(0) +/*---------------------------------------------------------------------------*/ +#define PORT0_IRQ_EDGE_FALL(pin) PORT_IRQ_EDGE_F_X(0) +#define PORT0_IRQ_EDGE_RISE(pin) PORT_IRQ_EDGE_R_X(0) +#define PORT1_IRQ_EDGE_FALL(pin) PORT1_##pin##_IRQ_EDGE_F_X() +#define PORT1_IRQ_EDGE_RISE(pin) PORT1_##pin##_IRQ_EDGE_R_X() +#define PORT2_IRQ_EDGE_FALL(pin) PORT_IRQ_EDGE_F_X(2) +#define PORT2_IRQ_EDGE_RISE(pin) PORT_IRQ_EDGE_R_X(2) + +/* Ports 0 & 2 */ +#define PORT_IRQ_EDGE_F_X(port) do { PICTL |= PICTL_P##port##ICON; } while(0) +#define PORT_IRQ_EDGE_R_X(port) do { PICTL &= ~PICTL_P##port##ICON; } while(0) +/* Port 1 - High Nibble */ +#define PORT1_7_IRQ_EDGE_F_X() do { PICTL |= PICTL_P1ICONH; } while(0) +#define PORT1_7_IRQ_EDGE_R_X() do { PICTL &= ~PICTL_P1ICONH; } while(0) +#define PORT1_6_IRQ_EDGE_F_X() do { PICTL |= PICTL_P1ICONH; } while(0) +#define PORT1_6_IRQ_EDGE_R_X() do { PICTL &= ~PICTL_P1ICONH; } while(0) +#define PORT1_5_IRQ_EDGE_F_X() do { PICTL |= PICTL_P1ICONH; } while(0) +#define PORT1_5_IRQ_EDGE_R_X() do { PICTL &= ~PICTL_P1ICONH; } while(0) +#define PORT1_4_IRQ_EDGE_F_X() do { PICTL |= PICTL_P1ICONH; } while(0) +#define PORT1_4_IRQ_EDGE_R_X() do { PICTL &= ~PICTL_P1ICONH; } while(0) +/* Port 1 - Low Nibble */ +#define PORT1_3_IRQ_EDGE_F_X() do { PICTL |= PICTL_P1ICONL; } while(0) +#define PORT1_3_IRQ_EDGE_R_X() do { PICTL &= ~PICTL_P1ICONL; } while(0) +#define PORT1_2_IRQ_EDGE_F_X() do { PICTL |= PICTL_P1ICONL; } while(0) +#define PORT1_2_IRQ_EDGE_R_X() do { PICTL &= ~PICTL_P1ICONL; } while(0) +#define PORT1_1_IRQ_EDGE_F_X() do { PICTL |= PICTL_P1ICONL; } while(0) +#define PORT1_1_IRQ_EDGE_R_X() do { PICTL &= ~PICTL_P1ICONL; } while(0) +#define PORT1_0_IRQ_EDGE_F_X() do { PICTL |= PICTL_P1ICONL; } while(0) +#define PORT1_0_IRQ_EDGE_R_X() do { PICTL &= ~PICTL_P1ICONL; } while(0) +/*---------------------------------------------------------------------------*/ + +#endif /* __PORT_H__ */ diff --git a/cpu/cc253x/dev/random.c b/cpu/cc253x/dev/random.c new file mode 100644 index 000000000..cc99bbfcf --- /dev/null +++ b/cpu/cc253x/dev/random.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Random number generator routines exploiting the cc2530 hardware + * capabilities. + * + * This file overrides core/lib/random.c. + * + * \author + * George Oikonomou - + */ +#include "cc253x.h" +#include "sfr-bits.h" +#include "dev/cc2530-rf.h" +/*---------------------------------------------------------------------------*/ +/** + * \brief Generates a new random number using the cc253x RNG. + * \return The random number. + */ +unsigned short +random_rand(void) +{ + /* Clock the RNG LSFR once */ + ADCCON1 |= ADCCON1_RCTRL0; + + return (RNDL | (RNDH << 8)); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Seed the cc253x random number generator. + * \param seed Ignored. It's here because the function prototype is in core. + * + * We form a seed for the RNG by sampling IF_ADC as + * discussed in the user guide. + * Seeding with this method should not be done during + * normal radio operation. Thus, use this function before + * initialising the network. + */ +void +random_init(unsigned short seed) +{ + int i; + + /* Make sure the RNG is on */ + ADCCON1 &= ~(ADCCON1_RCTRL1 | ADCCON1_RCTRL0); + + /* Infinite RX */ + FRMCTRL0 = FRMCTRL0_RX_MODE1; + + /* Turn RF on */ + CC2530_CSP_ISRXON(); + + /* Wait until (user guide sec. 23.12, p 239) "the chip has been in RX long + * enough for the transients to have died out. A convenient way to do this is + * to wait for the RSSI-valid signal to go high." */ + while(!(RSSISTAT & RSSISTAT_RSSI_VALID)); + + /* + * Form the seed by concatenating bits from IF_ADC in the RF receive path. + * Keep sampling until we have read at least 16 bits AND the seed is valid + * + * Invalid seeds are 0x0000 and 0x8003 - User Guide (sec. 14.2.2 p. 146): + * "Note that a seed value of 0x0000 or 0x8003 always leads to an unchanged + * value in the LFSR after clocking, as no values are pushed in via in_bit + * (see Figure 14-1); hence, neither of these seed values should not be used + * for random-number generation." + */ + i = 0; + while(i < 16 || (seed == 0x0000 || seed == 0x8003)) { + seed = (seed << 1) | (RFRND & RFRND_IRND); + seed <<= 1; + i++; + } + + /* High byte first */ + RNDL = seed >> 8; + RNDL = seed & 0xFF; + + /* RF Off. NETSTACK_RADIO.init() will sort out normal RF operation */ + CC2530_CSP_ISRFOFF(); +} diff --git a/cpu/cc253x/dev/uart-intr.c b/cpu/cc253x/dev/uart-intr.c new file mode 100644 index 000000000..b70acfb03 --- /dev/null +++ b/cpu/cc253x/dev/uart-intr.c @@ -0,0 +1,70 @@ +/** + * \file + * + * uart write routines + * + * \author + * + * Anthony "Asterisk" Ambuehl + * + * interrupt routines which must be in HOME bank. handles received data from UART. + * + */ +#include "cc253x.h" + +#include "dev/uart0.h" +#include "dev/uart1.h" +#include "sys/energest.h" +#include "dev/leds.h" + +#if UART0_ENABLE +static int (*uart0_input_handler)(unsigned char c); +#endif +#if UART1_ENABLE +static int (*uart1_input_handler)(unsigned char c); +#endif + +#if UART0_ENABLE +/*---------------------------------------------------------------------------*/ +void +uart0_set_input(int (*input)(unsigned char c)) +{ + uart0_input_handler = input; +} +/*---------------------------------------------------------------------------*/ +#if UART0_CONF_WITH_INPUT +void +uart0_rx_isr(void) __interrupt (URX0_VECTOR) +{ + ENERGEST_ON(ENERGEST_TYPE_IRQ); + leds_toggle(LEDS_YELLOW); + URX0IF = 0; + if(uart0_input_handler != NULL) { + uart0_input_handler(U0DBUF); + } + ENERGEST_OFF(ENERGEST_TYPE_IRQ); +} +#endif +#endif /* UART0_ENABLE */ +#if UART1_ENABLE +/*---------------------------------------------------------------------------*/ +void +uart1_set_input(int (*input)(unsigned char c)) +{ + uart1_input_handler = input; +} +/*---------------------------------------------------------------------------*/ +#if UART_ONE_CONF_WITH_INPUT +void +uart1_rx_isr(void) __interrupt (URX1_VECTOR) +{ + ENERGEST_ON(ENERGEST_TYPE_IRQ); + URX1IF = 0; + if(uart1_input_handler != NULL) { + uart1_input_handler(U1DBUF); + } + ENERGEST_OFF(ENERGEST_TYPE_IRQ); +} +/*---------------------------------------------------------------------------*/ +#endif /* UART_ONE_CONF_WITH_INPUT */ +#endif /* UART1_ENABLE */ diff --git a/cpu/cc253x/dev/uart.h b/cpu/cc253x/dev/uart.h new file mode 100644 index 000000000..1a20fd530 --- /dev/null +++ b/cpu/cc253x/dev/uart.h @@ -0,0 +1,43 @@ +#ifndef UART_H +#define UART_H + +#include "contiki-conf.h" + +#include "cc253x.h" +#include "8051def.h" + +/*---------------------------------------------------------------------------*/ +/* UART BAUD Rates */ +/* + * Macro to set speed of UART N by setting the UnBAUD SFR to M and the + * UnGCR SRF to E. See the cc2530 datasheet for possible values of M and E + */ +#define UART_SET_SPEED(N, M, E) do{ U##N##BAUD = M; U##N##GCR = E; } while(0) + +/* + * Sample Values for M and E in the macro above to achieve some common BAUD + * rates. For more values, see the cc2430 datasheet + */ +/* 2000000 - cc2430 theoretical MAX when using the 32MHz clock */ +#define UART_2K_M 0 +#define UART_2K_E 16 +/* 1000000 - cc2430 theoretical MAX when using the 16MHz clock */ +#define UART_1K_M 0 +#define UART_1K_E 15 +/* 921600 */ +#define UART_921_M 216 +#define UART_921_E 14 +/* 460800 Higher values lead to problems when the node needs to RX */ +#define UART_460_M 216 +#define UART_460_E 13 +/* 115200 */ +#define UART_115_M 216 +#define UART_115_E 11 +/* 38400 */ +#define UART_38_M 59 +#define UART_38_E 10 +/* 9600 */ +#define UART_9_M 59 +#define UART_9_E 8 + +#endif /* UART_H */ diff --git a/cpu/cc253x/dev/uart0.c b/cpu/cc253x/dev/uart0.c new file mode 100644 index 000000000..ba7856e77 --- /dev/null +++ b/cpu/cc253x/dev/uart0.c @@ -0,0 +1,75 @@ +/** + * \file + * + * uart0 write routines + * + * \author + * + * Anthony "Asterisk" Ambuehl + * + */ +#include +#include + +#include "cc253x.h" +#include "sfr-bits.h" +#include "dev/uart0.h" + +#if UART0_ENABLE +/*---------------------------------------------------------------------------*/ +void +uart0_init() +{ +#if UART0_CONF_HIGH_SPEED + UART_SET_SPEED(0, UART_460_M, UART_460_E); +#else + UART_SET_SPEED(0, UART_115_M, UART_115_E); +#endif + +#ifdef UART0_ALTERNATIVE_2 + PERCFG |= PERCFG_U0CFG; / *alternative port 2 = P1.5-2 */ +#ifdef UART0_RTSCTS + P1SEL |= 0x3C; /* peripheral select for TX and RX, RTS, CTS */ +#else + P1SEL |= 0x30; /* peripheral select for TX and RX */ + P1 &= ~0x08; /* RTS down */ +#endif + P1DIR |= 0x28; /* RTS, TX out */ + P1DIR &= ~0x14; /* CTS & RX in */ +#else + PERCFG &= ~PERCFG_U0CFG; /* alternative port 1 = P0.5-2 */ +#ifdef UART0_RTSCTS + P0SEL |= 0x20 | 0x10; /* peripheral select for TX and RX */ +#else + P0SEL |= 0x0C; /* peripheral select for TX and RX */ + P0 &= ~0x20; /* RTS down */ +#endif + P0DIR |= 0x28; /* RTS, TX out */ + P0DIR &= ~0x14; /* CTS, RX in */ +#endif + + +#ifdef UART0_RTSCTS + U0UCR = 0x42; /*defaults: 8N1, RTS/CTS, high stop bit*/ +#else + U0UCR = 0x02; /*defaults: 8N1, no flow control, high stop bit*/ +#endif + + U0CSR = UCSR_MODE; /* UART mode */ + U0UCR = 0x80; /* Flush */ + UART0_RX_EN(); + + UART0_RX_INT(1); + U0DBUF = 0; +} +/*---------------------------------------------------------------------------*/ +/* Write one byte over the UART. */ +void +uart0_writeb(uint8_t byte) +{ + UTX0IF = 0; + U0DBUF = byte; + while(!UTX0IF); /* Wait until byte has been transmitted. */ + UTX0IF = 0; +} +#endif diff --git a/cpu/cc253x/dev/uart0.h b/cpu/cc253x/dev/uart0.h new file mode 100644 index 000000000..45839e120 --- /dev/null +++ b/cpu/cc253x/dev/uart0.h @@ -0,0 +1,41 @@ +#ifndef UART_0_H +#define UART_0_H + +#include "contiki-conf.h" + +#include "cc253x.h" +#include "8051def.h" +#include "uart.h" + +/*---------------------------------------------------------------------------*/ +/* UART0 Enable - Disable */ +#ifdef UART0_CONF_ENABLE +#define UART0_ENABLE UART0_CONF_ENABLE +#else +#define UART0_ENABLE 0 +#endif +/*---------------------------------------------------------------------------*/ +/* UART0 Function Declarations */ +#if UART0_ENABLE +void uart0_init(); +void uart0_writeb(uint8_t byte); + +void uart0_set_input(int (*input)(unsigned char c)); + +#if UART0_CONF_WITH_INPUT +void uart0_rx_isr( void ) __interrupt (URX0_VECTOR); +/* Macro to turn on / off UART RX Interrupt */ +#define UART0_RX_INT(v) do { URX0IE = v; } while(0) +#define UART0_RX_EN() do { U0CSR |= UCSR_RE; } while(0) +#else +#define UART0_RX_INT(v) +#define UART0_RX_EN() +#endif /* UART0_CONF_WITH_INPUT */ +#else +#define uart0_init(...) +#define uart0_writeb(...) +#define uart0_set_input(...) +#define UART0_RX_INT(v) +#define UART0_RX_EN() +#endif /* UART0_ENABLE */ +#endif /* UART_0_H */ diff --git a/cpu/cc253x/dev/uart1.c b/cpu/cc253x/dev/uart1.c new file mode 100644 index 000000000..8b3006c99 --- /dev/null +++ b/cpu/cc253x/dev/uart1.c @@ -0,0 +1,74 @@ +/** + * \file + * + * uart1 write routines + * + * \author + * + * Anthony "Asterisk" Ambuehl + * + */ +#include +#include + +#include "cc253x.h" +#include "dev/uart1.h" + +#if UART1_ENABLE +/*---------------------------------------------------------------------------*/ +/* UART1 initialization */ +void +uart1_init() +{ +#ifdef UART1_ALTERNATIVE_1 + PERCFG &= ~PERCFG_U1CFG; /*alternative port 1 = P0.5-2*/ +#ifdef UART1_RTSCTS + P0SEL |= 0x3C; /*peripheral select for TX and RX, RTS, CTS*/ +#else + P0SEL |= 0x30; /*peripheral select for TX and RX*/ + P0 &= ~0x08; /*RTS down*/ +#endif + P0DIR |= 0x18; /*RTS, TX out*/ + P0DIR &= ~0x24; /*CTS, RX in*/ +#else + PERCFG |= PERCFG_U1CFG; /*alternative port 2 = P1.7-4*/ +#ifdef UART1_RTSCTS + P1SEL |= 0xF0; /*peripheral select for TX and RX*/ +#else + P1SEL |= 0xC0; /*peripheral select for TX and RX*/ + P1 &= ~0x20; /*RTS down*/ +#endif + P1DIR |= 0x60; /*RTS, TX out*/ + P1DIR &= ~0x90; /*CTS, RX in*/ +#endif + +#if UART_ONE_CONF_HIGH_SPEED + UART_SET_SPEED(1, UART_460_M, UART_460_E); +#else + UART_SET_SPEED(1, UART_115_M, UART_115_E); +#endif + +#ifdef UART1_RTSCTS + U1UCR = 0x42; /*defaults: 8N1, RTS/CTS, high stop bit*/ +#else + U1UCR = 0x02; /*defaults: 8N1, no flow control, high stop bit*/ +#endif + + U1CSR = UCSR_MODE; /* UART mode */ + U1UCR = 0x80; /* Flush */ + + UART1_RX_INT(1); + U0DBUF = 0; +} +/*---------------------------------------------------------------------------*/ +/* Write one byte over the UART. */ +void +uart1_writeb(uint8_t byte) +{ + UTX1IF = 0; + U1DBUF = byte; + while(!UTX1IF); /* Wait until byte has been transmitted. */ + UTX1IF = 0; +} +/*---------------------------------------------------------------------------*/ +#endif diff --git a/cpu/cc253x/dev/uart1.h b/cpu/cc253x/dev/uart1.h new file mode 100644 index 000000000..aea8bec48 --- /dev/null +++ b/cpu/cc253x/dev/uart1.h @@ -0,0 +1,38 @@ +#ifndef UART_1_H +#define UART_1_H + +#include "contiki-conf.h" + +#include "cc253x.h" +#include "8051def.h" +#include "uart.h" + +/*---------------------------------------------------------------------------*/ +/* UART1 Enable - Disable */ +#ifdef UART1_CONF_ENABLE +#define UART1_ENABLE UART1_CONF_ENABLE +#else +#define UART1_ENABLE 0 +#endif +/*---------------------------------------------------------------------------*/ +/* UART1 Function Declarations */ +#if UART1_ENABLE +void uart1_init(); +void uart1_writeb(uint8_t byte); + +void uart1_set_input(int (*input)(unsigned char c)); +#if UART1_CONF_WITH_INPUT +void uart1_rx_isr( void ) __interrupt (URX1_VECTOR); +/* Macro to turn on / off UART RX Interrupt */ +#define UART1_RX_INT(v) do { URX1IE = v; } while(0) +#else +#define UART1_RX_INT(v) +#endif /* UART1_CONF_WITH_INPUT */ +#else +#define uart1_init(...) +#define uart1_writeb(...) +#define uart1_set_input(...) +#define UART1_RX_INT(v) +#endif /* UART1_ENABLE */ + +#endif /* UART_1_H */ diff --git a/cpu/cc253x/dev/watchdog.c b/cpu/cc253x/dev/watchdog.c new file mode 100644 index 000000000..92cf40f16 --- /dev/null +++ b/cpu/cc253x/dev/watchdog.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Hardware-dependent functions for the cc253x watchdog. + * + * This file contains an ISR and must reside in the HOME bank. + * + * \author + * George Oikonomou - + */ + +#include "sys/energest.h" +#include "cc253x.h" +#include "sfr-bits.h" +#include "contiki-conf.h" + +#define WDT_TIMEOUT_MIN (WDCTL_INT1 | WDCTL_INT0) +/*---------------------------------------------------------------------------*/ +void +watchdog_init(void) +{ + WDCTL = 0; /* IDLE, Max Interval */ +} +/*---------------------------------------------------------------------------*/ +void +watchdog_start(void) +{ + WDCTL |= WDCTL_MODE1; /* Start in Watchdog mode */ +} +/*---------------------------------------------------------------------------*/ +void +watchdog_periodic(void) +{ + /* Write the 'clear' sequence while maintaining mode and interval setting */ + WDCTL = (WDCTL & 0x0F) | WDCTL_CLR3 | WDCTL_CLR1; + WDCTL = (WDCTL & 0x0F) | WDCTL_CLR2 | WDCTL_CLR0; +} +/*---------------------------------------------------------------------------*/ +void +watchdog_stop(void) +{ + /* In watchdog mode, stopping is impossible so we just reset the timer */ + watchdog_periodic(); +} +/*---------------------------------------------------------------------------*/ +void +watchdog_reboot(void) +{ + WDCTL = WDT_TIMEOUT_MIN; + /* Dis-acknowledge all interrupts while we wait for the dog to bark */ + DISABLE_INTERRUPTS(); + /* NOP till the dog barks... */ + while(1) { + __asm + nop + __endasm; + } +} diff --git a/cpu/cc253x/mtarch.h b/cpu/cc253x/mtarch.h new file mode 100644 index 000000000..89d7766e2 --- /dev/null +++ b/cpu/cc253x/mtarch.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + + /* + * \file + * Stub header file for multi-threading. It doesn't do anything, it + * just exists so that mt.c can compile cleanly. + * + * This is based on the original mtarch.h for z80 by Takahide Matsutsuka + * + * \author + * George Oikonomou - + */ +#ifndef __MTARCH_H__ +#define __MTARCH_H__ + +struct mtarch_thread { + unsigned char *sp; +}; + +#endif /* __MTARCH_H__ */ + diff --git a/cpu/cc253x/rtimer-arch.c b/cpu/cc253x/rtimer-arch.c new file mode 100644 index 000000000..01512a724 --- /dev/null +++ b/cpu/cc253x/rtimer-arch.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +/** + * \file + * Hardware-dependent functions used to support the + * contiki rtimer module. + * + * clock_init() has set our tick speed prescaler already, so we + * are ticking with 500 kHz freq. + * + * Contiki typedefs rtimer_clock_t as unsigned short (16bit) + * It thus makes sense to use the 16bit timer (Timer 1) + * + * This file contains an ISR and must reside in the HOME bank + * + * \author + * George Oikonomou - + */ + +#include "sys/rtimer.h" +#include "sfr-bits.h" +#include "cc253x.h" +#include "sys/energest.h" + +#include "debug.h" +#include + +#define RT_MODE_COMPARE() do { T1CCTL1 |= T1CCTL_MODE; } while(0) +#define RT_MODE_CAPTURE() do { T1CCTL1 &= ~T1CCTL_MODE; } while(0) +/*---------------------------------------------------------------------------*/ +void +rtimer_arch_init(void) +{ + /* + * - Free running mode + * - Prescale by 32: + * Tick Speed has been prescaled to 500 kHz already in clock_init() + * We further prescale by 32 resulting in 15625 Hz for this timer. + */ + T1CTL = (T1CTL_DIV1 | T1CTL_MODE0); + + T1STAT = 0; + + /* Timer 1, Channel 1. Compare Mode (0x04), Interrupt mask on (0x40) */ + T1CCTL1 = T1CCTL_MODE | T1CCTL_IM; + + /* Interrupt Mask Flags: No interrupt on overflow */ + OVFIM = 0; + + /* Acknowledge Timer 1 Interrupts */ + T1IE = 1; +} +/*---------------------------------------------------------------------------*/ +void +rtimer_arch_schedule(rtimer_clock_t t) +{ + /* Switch to capture mode before writing T1CC1x and + * set the compare mode values so we can get an interrupt after t */ + RT_MODE_CAPTURE(); + T1CC1L = (unsigned char) t; + T1CC1H = (unsigned char) (t >> 8); + RT_MODE_COMPARE(); + + /* Turn on compare mode interrupt */ + T1STAT = 0; + T1CCTL1 |= T1CCTL_IM; +} +/*---------------------------------------------------------------------------*/ +void +rtimer_isr(void) __interrupt(T1_VECTOR) +{ + T1IE = 0; /* Ignore Timer 1 Interrupts */ + ENERGEST_ON(ENERGEST_TYPE_IRQ); + + /* No more interrupts from Channel 1 till next rtimer_arch_schedule() call */ + T1STAT &= ~T1STAT_CH1IF; + T1CCTL1 &= ~T1CCTL_IM; + + rtimer_run_next(); + + ENERGEST_OFF(ENERGEST_TYPE_IRQ); + T1IE = 1; /* Acknowledge Timer 1 Interrupts */ +} diff --git a/cpu/cc253x/rtimer-arch.h b/cpu/cc253x/rtimer-arch.h new file mode 100644 index 000000000..d261a95e9 --- /dev/null +++ b/cpu/cc253x/rtimer-arch.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2007, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + * $Id: rtimer-arch.h,v 1.1 2009/09/08 20:07:35 zdshelby Exp $ + */ + +/** + * \file + * Hardware-dependent function declarations used to + * support the contiki rtimer module. + * + * \author + * Zach Shelby (Original) + * George Oikonomou - + * (rtimer-arch implementation for cc2430 and then cc253x) + */ + +#ifndef __RTIMER_ARCH_H__ +#define __RTIMER_ARCH_H__ + +#include "contiki-conf.h" +#include "cc253x.h" + +/* + * 32 MHz clock, prescaled down to 500 kHz for all 4 timers in clock_init(). + * Further prescaled factor 32 for T1, thus T1 is 15625 Hz + */ +#define RTIMER_ARCH_SECOND (15625U) + +#define rtimer_arch_now() (T1CNTL + (T1CNTH << 8)) + +void rtimer_isr(void) __interrupt(T1_VECTOR); + +#endif /* __RTIMER_ARCH_H__ */ diff --git a/cpu/cc253x/segment.rules b/cpu/cc253x/segment.rules new file mode 100644 index 000000000..594675d03 --- /dev/null +++ b/cpu/cc253x/segment.rules @@ -0,0 +1,22 @@ +# segment.rules files assign source code modules to specific banks +# These files are only used when we build code with banking (HAVE_BANKING=1) +# The final segment.rules file is constructed from any segment.rules found in +# the search path, defined in the CPU Makefile +# When building bankable code, the bank-alloc.py script automatically allocates +# modules to banks. segment.rules files provide hints, instructing the script +# as to which files are safe to move around and which files to leave alone +# In other words, only specify a rule for a file if you need to +# comments starting with "#" are supported +# The file spec in rules is actually interpreted as a python regex so you can +# write a rule that will match multiple files +# +# general rules -- +# This file is only used when the Makefile defines HAVE_BANKING=1 +# SDCC's standard libraries will always go in CSEG - We don't touch them +# Interrupt code must be in HOME. Specify all files with an ISR here +# All files without an associated rule get allocated to a bank automatically + +# Files with ISRs must be in HOME +HOME intr.c # Match all files ending in intr.c (e.g. uart-intr.c) +HOME rtimer-arch.c +HOME clock.c diff --git a/cpu/cc253x/sfr-bits.h b/cpu/cc253x/sfr-bits.h new file mode 100644 index 000000000..a3a48be95 --- /dev/null +++ b/cpu/cc253x/sfr-bits.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Header file with definitions of bit masks for some cc2530 SFRs + * + * \author + * George Oikonomou - + */ + + +#ifndef SFR_BITS_H_ +#define SFR_BITS_H_ + +/* CLKCON */ +#define CLKCONCMD_OSC32K 0x80 +#define CLKCONCMD_OSC 0x40 +#define CLKCONCMD_TICKSPD2 0x20 +#define CLKCONCMD_TICKSPD1 0x10 +#define CLKCONCMD_TICKSPD0 0x08 +#define CLKCONCMD_CLKSPD2 0x04 +#define CLKCONCMD_CLKSPD1 0x02 +#define CLKCONCMD_CLKSPD0 0x01 + +/* SLEEPCMD and SLEEPSTA */ +#define SLEEP_OSC32K_CALDIS 0x80 +#define SLEEP_XOSC_STB 0x40 +#define SLEEP_HFRC_STB 0x20 +#define SLEEP_RST1 0x10 /* SLEEPSTA only */ +#define SLEEP_RST0 0x08 /* SLEEPSTA only */ +#define SLEEP_OSC_PD 0x04 +#define SLEEP_MODE1 0x02 +#define SLEEP_MODE0 0x01 + +/* PCON */ +#define PCON_IDLE 0x01 + +/* T1CTL */ +#define T1CTL_DIV1 0x08 +#define T1CTL_DIV0 0x04 +#define T1CTL_MODE1 0x02 +#define T1CTL_MODE0 0x01 + +/* T1CCTLx */ +#define T1CCTL_RFIRQ 0x80 +#define T1CCTL_IM 0x40 +#define T1CCTL_CMP2 0x20 +#define T1CCTL_CMP1 0x10 +#define T1CCTL_CMP0 0x08 +#define T1CCTL_MODE 0x04 +#define T1CCTL_CAP1 0x02 +#define T1CCTL_CAP0 0x01 + +/* T1STAT */ +#define T1STAT_OVFIF 0x20 +#define T1STAT_CH4IF 0x10 +#define T1STAT_CH3IF 0x08 +#define T1STAT_CH2IF 0x04 +#define T1STAT_CH1IF 0x02 +#define T1STAT_CH0IF 0x01 + +/* WDCTL */ +#define WDCTL_CLR3 0x80 +#define WDCTL_CLR2 0x40 +#define WDCTL_CLR1 0x20 +#define WDCTL_CLR0 0x10 +#define WDCTL_MODE1 0x04 +#define WDCTL_MODE0 0x04 +#define WDCTL_INT1 0x02 +#define WDCTL_INT0 0x01 + +/* ADCCON1 */ +#define ADCCON1_EOC 0x80 +#define ADCCON1_ST 0x40 +#define ADCCON1_STSEL1 0x20 +#define ADCCON1_STSEL0 0x10 +/* ADCCON1 - RNG bits */ +#define ADCCON1_RCTRL1 0x08 +#define ADCCON1_RCTRL0 0x04 + +/* ADCCON3 */ +#define ADCCON3_EREF1 0x80 +#define ADCCON3_EREF0 0x40 +#define ADCCON3_EDIV1 0x20 +#define ADCCON3_EDIV0 0x10 +#define ADCCON3_ECH3 0x08 +#define ADCCON3_ECH2 0x04 +#define ADCCON3_ECH1 0x02 +#define ADCCON3_ECH0 0x01 + +/* PERCFG */ +#define PERCFG_T1CFG 0x40 +#define PERCFG_T3CFG 0x20 +#define PERCFG_T4CFG 0x10 +#define PERCFG_U1CFG 0x02 +#define PERCFG_U0CFG 0x01 + +/* UxCSR */ +#define UCSR_MODE 0x80 +#define UCSR_RE 0x40 +#define UCSR_SLAVE 0x20 +#define UCSR_FE 0x10 +#define UCSR_ERR 0x08 +#define UCSR_RX_BYTE 0x04 +#define UCSR_TX_BYTE 0x02 +#define UCSR_ACTIVE 0x01 + +/* IEN2 */ +#define IEN2_WDTIE 0x20 +#define IEN2_P1IE 0x10 +#define IEN2_UTX1IE 0x08 +#define IEN2_UTX0IE 0x04 +#define IEN2_P2IE 0x02 +#define IEN2_RFIE 0x01 + +/* PICTL */ +#define PICTL_PADSC 0x40 +#define PICTL_P2ICON 0x08 +#define PICTL_P1ICONH 0x04 +#define PICTL_P1ICONL 0x02 +#define PICTL_P0ICON 0x01 +/*--------------------------------------------------------------------------- + * XREG bits, excluding RF and USB + *---------------------------------------------------------------------------*/ +/* FCTL */ +#define FCTL_BUSY 0x80 +#define FCTL_FULL 0x40 +#define FCTL_ABORT 0x20 +#define FCTL_CM1 0x08 +#define FCTL_CM0 0x04 +#define FCTL_WRITE 0x02 +#define FCTL_ERASE 0x01 +/*--------------------------------------------------------------------------- + * Radio Register Bits + *---------------------------------------------------------------------------*/ +/* FRMCTRL0 */ +#define FRMCTRL0_APPEND_DATA_MODE 0x80 +#define FRMCTRL0_AUTOCRC 0x40 +#define FRMCTRL0_AUTOACK 0x20 +#define FRMCTRL0_ENERGY_SCAN 0x10 +#define FRMCTRL0_RX_MODE1 0x08 +#define FRMCTRL0_RX_MODE0 0x04 +#define FRMCTRL0_TX_MODE1 0x02 +#define FRMCTRL0_TX_MODE0 0x01 + +/* FRMCTRL1 */ +#define FRMCTRL1_PENDING_OR 0x04 +#define FRMCTRL1_IGNORE_TX_UNDERF 0x02 +#define FRMCTRL1_SET_RXENMASK_ON_TX 0x01 + +/* FSMSTAT1 */ +#define FSMSTAT1_FIFO 0x80 +#define FSMSTAT1_FIFOP 0x40 +#define FSMSTAT1_SFD 0x20 +#define FSMSTAT1_CCA 0x10 +#define FSMSTAT1_TX_ACTIVE 0x02 +#define FSMSTAT1_RX_ACTIVE 0x01 + +/* RSSISTAT */ +#define RSSISTAT_RSSI_VALID 0x01 + +/* RFRND */ +#define RFRND_QRND 0x02 +#define RFRND_IRND 0x01 + +#endif /* SFR_BITS_H_ */ diff --git a/cpu/cc253x/soc.c b/cpu/cc253x/soc.c new file mode 100644 index 000000000..36d9531e4 --- /dev/null +++ b/cpu/cc253x/soc.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Init routine for the cc2530 SoC + * + * Bankable + * + * \author + * George Oikonomou - + */ +#include "cc253x.h" +#include "8051def.h" +#include "sfr-bits.h" + +void +soc_init() +{ + /* Flash: Cache with Pre-fetch */ + FCTL = FCTL_CM0; + + /* Enable Global Interrupt */ + ENABLE_INTERRUPTS(); +} + diff --git a/cpu/cc253x/soc.h b/cpu/cc253x/soc.h new file mode 100644 index 000000000..81888e2ab --- /dev/null +++ b/cpu/cc253x/soc.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Header file for cc253x SoC hardware init routines + * + * \author + * George Oikonomou - + */ + + +#ifndef __SOC_H__ +#define __SOC_H__ + +void soc_init(); + +#endif /* __SOC_H__ */ diff --git a/examples/cc2530dk/Makefile b/examples/cc2530dk/Makefile new file mode 100644 index 000000000..acffeada8 --- /dev/null +++ b/examples/cc2530dk/Makefile @@ -0,0 +1,6 @@ +CONTIKI_PROJECT = hello-world blink-hello timer-test sensors-demo + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../.. +include $(CONTIKI)/Makefile.include diff --git a/examples/cc2530dk/Makefile.target b/examples/cc2530dk/Makefile.target new file mode 100644 index 000000000..70609bbdb --- /dev/null +++ b/examples/cc2530dk/Makefile.target @@ -0,0 +1 @@ +TARGET = cc2530dk diff --git a/examples/cc2530dk/blink-hello.c b/examples/cc2530dk/blink-hello.c new file mode 100644 index 000000000..98ed76b10 --- /dev/null +++ b/examples/cc2530dk/blink-hello.c @@ -0,0 +1,62 @@ +/* This is a very simple hello_world program. + * It aims to demonstrate the co-existence of two processes: + * One of them prints a hello world message and the other blinks the LEDs + * + * It is largely based on hello_world in $(CONTIKI)/examples/sensinode + * + * Author: George Oikonomou - + */ + +#include "contiki.h" +#include "dev/leds.h" + +#include /* For printf() */ +/*---------------------------------------------------------------------------*/ +static struct etimer et_hello; +static struct etimer et_blink; +static uint16_t count; +static uint8_t blinks; +/*---------------------------------------------------------------------------*/ +PROCESS(hello_world_process, "Hello world process"); +PROCESS(blink_process, "LED blink process"); +AUTOSTART_PROCESSES(&hello_world_process, &blink_process); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(hello_world_process, ev, data) +{ + PROCESS_BEGIN(); + + etimer_set(&et_hello, CLOCK_SECOND * 4); + count = 0; + + while(1) { + PROCESS_WAIT_EVENT(); + + if(ev == PROCESS_EVENT_TIMER) { + printf("Sensor says #%u\n", count); + count ++; + + etimer_reset(&et_hello); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(blink_process, ev, data) +{ + PROCESS_BEGIN(); + + while(1) { + etimer_set(&et_blink, CLOCK_SECOND); + + PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER); + + blinks = leds_get(); + leds_off(LEDS_ALL); + leds_on((blinks + 1) & LEDS_ALL); + printf("Blink... (state %0.2X)\n", leds_get()); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/cc2530dk/border-router/Makefile b/examples/cc2530dk/border-router/Makefile new file mode 100644 index 000000000..eb8b69c2b --- /dev/null +++ b/examples/cc2530dk/border-router/Makefile @@ -0,0 +1,15 @@ +DEFINES+=PROJECT_CONF_H=\"project-conf.h\" + +# We need uIPv6, therefore we also need banking +HAVE_BANKING=1 +UIP_CONF_IPV6=1 + +PROJECT_SOURCEFILES += slip-bridge.c + +CONTIKI_PROJECT = border-router + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/cc2530dk/border-router/Makefile.target b/examples/cc2530dk/border-router/Makefile.target new file mode 100644 index 000000000..70609bbdb --- /dev/null +++ b/examples/cc2530dk/border-router/Makefile.target @@ -0,0 +1 @@ +TARGET = cc2530dk diff --git a/examples/cc2530dk/border-router/README b/examples/cc2530dk/border-router/README new file mode 100644 index 000000000..3df598278 --- /dev/null +++ b/examples/cc2530dk/border-router/README @@ -0,0 +1,16 @@ +border-router example for the TI SmartRF05EB with a cc2530EM. + +This example is meant to be used with tunslip6 in tools/ + +- Build the code and load it onto your node +- Connect your node to your PC +- run: + sudo ./tunslip6 -s /dev/ttyUSBx
/ + + This will setup tun0 on your PC over device /dev/ttyUSBx. The address + argument should contain the v6 address that you want to assign to tun0 + The node will use this address to obtain the network prefix + + for example: + sudo ./tunslip6 aaaa::1/64 + This will use aaaa:: / 64 as the prefix for the 15.4 network. diff --git a/examples/cc2530dk/border-router/border-router.c b/examples/cc2530dk/border-router/border-router.c new file mode 100644 index 000000000..50de6462a --- /dev/null +++ b/examples/cc2530dk/border-router/border-router.c @@ -0,0 +1,131 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" + +#include + +#define DEBUG DEBUG_PRINT +#include "net/uip-debug.h" +#include "net/rpl/rpl.h" +#include "dev/watchdog.h" +#include "dev/slip.h" +#include "dev/leds.h" +#include "cc253x.h" + +static uint8_t prefix_set; +/*---------------------------------------------------------------------------*/ +PROCESS(border_router_process, "Border Router process"); +AUTOSTART_PROCESSES(&border_router_process); +/*---------------------------------------------------------------------------*/ +static void +print_local_addresses(void) +{ + int i; + uint8_t state; + + PRINTF("Router's IPv6 addresses:\n"); + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + state = uip_ds6_if.addr_list[i].state; + if(uip_ds6_if.addr_list[i].isused && (state == ADDR_TENTATIVE || state + == ADDR_PREFERRED)) { + PRINTF(" "); + PRINT6ADDR(&uip_ds6_if.addr_list[i].ipaddr); + PRINTF("\n"); + if (state == ADDR_TENTATIVE) { + uip_ds6_if.addr_list[i].state = ADDR_PREFERRED; + } + } + } +} +/*---------------------------------------------------------------------------*/ +void +request_prefix(void) { + /* mess up uip_buf with a dirty request... */ + uip_buf[0] = '?'; + uip_buf[1] = 'P'; + uip_len = 2; + slip_send(); + uip_len = 0; +} +/*---------------------------------------------------------------------------*/ +/* Set our prefix when we receive one over SLIP */ +void +set_prefix_64(uip_ipaddr_t *prefix_64) { + rpl_dag_t *dag; + uip_ipaddr_t ipaddr; + memcpy(&ipaddr, prefix_64, 16); + prefix_set = 1; + uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr); + uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF); + + /* Become root of a new DODAG with ID our global v6 address */ + dag = rpl_set_root(RPL_DEFAULT_INSTANCE, &ipaddr); + if(dag != NULL) { + rpl_set_prefix(dag, &ipaddr, 64); + PRINTF("Created a new RPL dag with ID: "); + PRINT6ADDR(&dag->dag_id); + PRINTF("\n"); + } +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(border_router_process, ev, data) +{ + static struct etimer et; + + PROCESS_BEGIN(); + PRINTF("Border Router started\n"); + prefix_set = 0; + + leds_on(LEDS_RED); + + /* Request prefix until it has been received */ + while(!prefix_set) { + leds_on(LEDS_GREEN); + PRINTF("Prefix request.\n"); + etimer_set(&et, CLOCK_SECOND); + request_prefix(); + leds_off(LEDS_GREEN); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + } + + /* We have created a new DODAG when we reach here */ + PRINTF("On Channel %u\n", (uint8_t)((FREQCTRL + 44) / 5)); + + print_local_addresses(); + + leds_off(LEDS_RED); + + PROCESS_EXIT(); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/cc2530dk/border-router/project-conf.h b/examples/cc2530dk/border-router/project-conf.h new file mode 100644 index 000000000..7f4c6aab1 --- /dev/null +++ b/examples/cc2530dk/border-router/project-conf.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Project specific configuration defines for the border router / + * slip bridge example for cc253x. + * + * \author + * George Oikonomou - + */ + +#ifndef PROJECT_CONF_H_ +#define PROJECT_CONF_H_ + +#define VIZTOOL_MAX_PAYLOAD_LEN 120 +#define SLIP_ARCH_CONF_ENABLE 1 +#define LPM_CONF_MODE 0 + +#endif /* PROJECT_CONF_H_ */ diff --git a/examples/cc2530dk/border-router/slip-bridge.c b/examples/cc2530dk/border-router/slip-bridge.c new file mode 100644 index 000000000..dc8db1ab9 --- /dev/null +++ b/examples/cc2530dk/border-router/slip-bridge.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * $Id: slip-bridge.c,v 1.3 2010/06/08 19:53:49 nifi Exp $ + */ + +/** + * \file + * Slip fallback interface + * \author + * Niclas Finne + * Joakim Eriksson + * Joel Hoglund + * Nicolas Tsiftes + */ + +#include "net/uip.h" +#include "net/uip-ds6.h" +#include "net/rpl/rpl.h" +#include "dev/slip.h" +#include + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) + +#define DEBUG DEBUG_NONE +#include "net/uip-debug.h" + +void set_prefix_64(uip_ipaddr_t *); + +static uip_ipaddr_t last_sender; +/*---------------------------------------------------------------------------*/ +static void +slip_input_callback(void) +{ + PRINTF("SIN: %u\n", uip_len); + if((char) uip_buf[0] == '!') { + PRINTF("Got configuration message of type %c\n", uip_buf[1]); + uip_len = 0; + if((char)uip_buf[1] == 'P') { + uip_ipaddr_t prefix; + /* Here we set a prefix !!! */ + memset(&prefix, 0, 16); + memcpy(&prefix, &uip_buf[2], 8); + PRINTF("Setting prefix "); + PRINT6ADDR(&prefix); + PRINTF("\n"); + set_prefix_64(&prefix); + } + } + /* Save the last sender received over SLIP to avoid bouncing the + packet back if no route is found */ + uip_ipaddr_copy(&last_sender, &UIP_IP_BUF->srcipaddr); +} +#include "debug.h" + +/*---------------------------------------------------------------------------*/ +static void +init(void) +{ + process_start(&slip_process, NULL); + slip_set_input_callback(slip_input_callback); +} +/*---------------------------------------------------------------------------*/ +static void +output(void) +{ + if(uip_ipaddr_cmp(&last_sender, &UIP_IP_BUF->srcipaddr)) { + /* Do not bounce packets back over SLIP if the packet was received + over SLIP */ + PRINTF("slip-bridge: Destination off-link but no route\n"); + } else { + PRINTF("SUT: %u\n", uip_len); + slip_send(); + } +} +/*---------------------------------------------------------------------------*/ +struct uip_fallback_interface slip_interface = { + init, output +}; +/*---------------------------------------------------------------------------*/ diff --git a/examples/cc2530dk/hello-world.c b/examples/cc2530dk/hello-world.c new file mode 100644 index 000000000..ec3d2c3d6 --- /dev/null +++ b/examples/cc2530dk/hello-world.c @@ -0,0 +1,23 @@ +/** + * \file + * Basic hello world example + * \author + * Zach Shelby + */ + +#include "contiki.h" +#include /* For printf() */ +/*---------------------------------------------------------------------------*/ +PROCESS(hello_world_process, "Hello world process"); +AUTOSTART_PROCESSES(&hello_world_process); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(hello_world_process, ev, data) +{ + + PROCESS_BEGIN(); + + printf("Hello World!\n"); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/cc2530dk/sensors-demo.c b/examples/cc2530dk/sensors-demo.c new file mode 100644 index 000000000..5707fc96b --- /dev/null +++ b/examples/cc2530dk/sensors-demo.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example to demonstrate-test cc2530 sensor functionality + * + * B1 turns LED_GREEN on and off. + * + * The node takes readings from the various sensors every x seconds and + * prints out the results. + * + * We use floats here to translate the AD conversion results to + * meaningful values. However, our printf does not have %f support so + * we use an ugly hack to print out the value by extracting the integral + * part and then the fractional part. Don't try this at home. + * + * Temperature: + * Math is correct, the sensor needs calibration per device. + * I currently use default values for the math which may result in + * very incorrect values in degrees C. + * See TI Design Note DN102 about the offset calibration. + * + * Supply Voltage (VDD): + * For VDD, math is correct, conversion is correct. + * See DN101 for details. + * + * Make sure you enable/disable things in contiki-conf.h + * + * \author + * George Oikonomou - + */ + +#include "contiki.h" +#include "contiki-conf.h" +#include "dev/leds.h" + +#include "dev/button-sensor.h" +#include "dev/adc-sensor.h" + +#define DEBUG 1 + +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else /* DEBUG */ +/* We overwrite (read as annihilate) all output functions here */ +#define PRINTF(...) +#endif /* DEBUG */ +/*---------------------------------------------------------------------------*/ +PROCESS(sensors_test_process, "Sensor Test Process"); +#if BUTTON_SENSOR_ON +PROCESS(buttons_test_process, "Button Test Process"); +AUTOSTART_PROCESSES(&sensors_test_process, &buttons_test_process); +#else +AUTOSTART_PROCESSES(&sensors_test_process); +#endif +/*---------------------------------------------------------------------------*/ +#if BUTTON_SENSOR_ON +PROCESS_THREAD(buttons_test_process, ev, data) +{ + struct sensors_sensor *sensor; + + PROCESS_BEGIN(); + + while (1) { + + PROCESS_WAIT_EVENT_UNTIL(ev == sensors_event); + + /* If we woke up after a sensor event, inform what happened */ + sensor = (struct sensors_sensor *)data; + if(sensor == &button_sensor) { + PRINTF("Button Press\n"); + leds_toggle(LEDS_GREEN); + } + } + + PROCESS_END(); +} +#endif +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(sensors_test_process, ev, data) +{ + static struct etimer et; + + /* Sensor Values */ + static int rv; + static struct sensors_sensor * sensor; + static float sane = 0; + static int dec; + static float frac; + + PROCESS_BEGIN(); + + PRINTF("========================\n"); + PRINTF("Starting Sensor Example.\n"); + PRINTF("========================\n"); + + /* Set an etimer. We take sensor readings when it expires and reset it. */ + etimer_set(&et, CLOCK_SECOND * 2); + + while (1) { + + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + + /* + * Request some ADC conversions + * Return value -1 means sensor not available or turned off in conf + */ + sensor = sensors_find(ADC_SENSOR); + if (sensor) { + PRINTF("------------------\n"); + leds_on(LEDS_RED); + /* + * Temperature: + * Using 1.25V ref. voltage (1250mV). + * Typical AD Output at 25°C: 1480 + * Typical Co-efficient : 4.5 mV/°C + * + * Thus, at 12bit decimation (and ignoring the VDD co-efficient as well + * as offsets due to lack of calibration): + * + * AD - 1480 + * T = 25 + --------- + * 4.5 + */ + rv = sensor->value(ADC_SENSOR_TYPE_TEMP); + if(rv != -1) { + sane = 25 + ((rv - 1480) / 4.5); + dec = sane; + frac = sane - dec; + PRINTF(" Temp=%d.%02u C (%d)\n", dec, (unsigned int)(frac*100), rv); + } + /* + * Power Supply Voltage. + * Using 1.25V ref. voltage. + * AD Conversion on VDD/3 + * + * Thus, at 12bit resolution: + * + * ADC x 1.25 x 3 + * Supply = -------------- V + * 2047 + */ + rv = sensor->value(ADC_SENSOR_TYPE_VDD); + if(rv != -1) { + sane = rv * 3.75 / 2047; + dec = sane; + frac = sane - dec; + PRINTF("Supply=%d.%02u V (%d)\n", dec, (unsigned int)(frac*100), rv); + /* Store rv temporarily in dec so we can use it for the battery */ + dec = rv; + } + /* + * Battery Voltage - ToDo + * rv = sensor->value(ADC_SENSOR_TYPE_BATTERY); + */ + + leds_off(LEDS_RED); + } + etimer_reset(&et); + } + PROCESS_END(); + } +/*---------------------------------------------------------------------------*/ diff --git a/examples/cc2530dk/sniffer/Makefile b/examples/cc2530dk/sniffer/Makefile new file mode 100644 index 000000000..d9491a92c --- /dev/null +++ b/examples/cc2530dk/sniffer/Makefile @@ -0,0 +1,10 @@ +DEFINES+=PROJECT_CONF_H +PROJECT_SOURCEFILES += stub-rdc.c + +CONTIKI_PROJECT = sniffer + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/cc2530dk/sniffer/Makefile.target b/examples/cc2530dk/sniffer/Makefile.target new file mode 100644 index 000000000..70609bbdb --- /dev/null +++ b/examples/cc2530dk/sniffer/Makefile.target @@ -0,0 +1 @@ +TARGET = cc2530dk diff --git a/examples/cc2530dk/sniffer/netstack.c b/examples/cc2530dk/sniffer/netstack.c new file mode 100644 index 000000000..2551f82fe --- /dev/null +++ b/examples/cc2530dk/sniffer/netstack.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Stub file overriding core/net/netstack.c. What we want to achieve + * here is call netstack_init from main without initialising the RDC, + * MAC and Network layers. It will just turn on the radio instead. + * + * \author + * George Oikonomou - + */ + +#include "netstack.h" +/*---------------------------------------------------------------------------*/ +void +netstack_init(void) +{ + NETSTACK_RADIO.init(); +} +/*---------------------------------------------------------------------------*/ + diff --git a/examples/cc2530dk/sniffer/project-conf.h b/examples/cc2530dk/sniffer/project-conf.h new file mode 100644 index 000000000..564f4d760 --- /dev/null +++ b/examples/cc2530dk/sniffer/project-conf.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Project specific configuration defines for the sniffer example. + * + * \author + * George Oikonomou - + */ + +#ifndef PROJECT_CONF_H_ +#define PROJECT_CONF_H_ + +#define CC2530_RF_CONF_HEXDUMP 1 +#define CC2530_RF_CONF_AUTOACK 0 +#define NETSTACK_CONF_RDC stub_rdc_driver +#define ADC_SENSOR_CONF_ON 0 +#define LPM_CONF_MODE 0 +#define UART0_CONF_HIGH_SPEED 1 + +#endif /* PROJECT_CONF_H_ */ diff --git a/examples/cc2530dk/sniffer/sniffer.c b/examples/cc2530dk/sniffer/sniffer.c new file mode 100644 index 000000000..2183f88d7 --- /dev/null +++ b/examples/cc2530dk/sniffer/sniffer.c @@ -0,0 +1,54 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +#include "contiki.h" +#include "cc253x.h" + +#define DEBUG DEBUG_NONE +#include "net/uip-debug.h" + +/*---------------------------------------------------------------------------*/ +PROCESS(sniffer_process, "Sniffer process"); +AUTOSTART_PROCESSES(&sniffer_process); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(sniffer_process, ev, data) +{ + + PROCESS_BEGIN(); + + PRINTF("Sniffer started\n"); + + /* Turn off RF Address Recognition - We need to accept all frames */ + FRMFILT0 &= ~0x01; + + PROCESS_EXIT(); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/cc2530dk/sniffer/stub-rdc.c b/examples/cc2530dk/sniffer/stub-rdc.c new file mode 100644 index 000000000..603552c97 --- /dev/null +++ b/examples/cc2530dk/sniffer/stub-rdc.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Definition of a fake RDC driver to be used with passive + * examples. The sniffer will never send packets and it will never + * push incoming packets up the stack. We do this by defining this + * driver as our RDC. We then drop everything + * + * \author + * George Oikonomou - + */ + +#include "net/mac/mac.h" +#include "net/mac/rdc.h" +/*---------------------------------------------------------------------------*/ +static void +send(mac_callback_t sent, void *ptr) +{ + if(sent) { + sent(ptr, MAC_TX_OK, 1); + } +} +/*---------------------------------------------------------------------------*/ +static void +input(void) +{ +} +/*---------------------------------------------------------------------------*/ +static int +on(void) +{ + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +off(int keep_radio_on) +{ + return keep_radio_on; +} +/*---------------------------------------------------------------------------*/ +static unsigned short +cca(void) +{ + return 0; +} +/*---------------------------------------------------------------------------*/ +static void +init(void) +{ +} +/*---------------------------------------------------------------------------*/ +const struct rdc_driver stub_rdc_driver = { + "stub-rdc", + init, + send, + input, + on, + off, + cca, +}; +/*---------------------------------------------------------------------------*/ diff --git a/examples/cc2530dk/timer-test.c b/examples/cc2530dk/timer-test.c new file mode 100644 index 000000000..6843e4df3 --- /dev/null +++ b/examples/cc2530dk/timer-test.c @@ -0,0 +1,139 @@ +/** + * \file + * Tests related to clocks and timers + * + * This is clock_test.c plus a small addition by George Oikonomou + * (Loughborough University) in order to test the rtimer + * + * \author + * Zach Shelby (Original) + * George Oikonomou - (rtimer code) + * + */ + +#include "contiki.h" +#include "sys/clock.h" +#include "sys/rtimer.h" +#include "dev/leds.h" + +#include + +/*---------------------------------------------------------------------------*/ +#define TEST_CLOCK_DELAY 1 +#define TEST_RTIMER 1 +#define TEST_ETIMER 1 +#define TEST_CLOCK_SECONDS 1 +/*---------------------------------------------------------------------------*/ +static struct etimer et; + +#if TEST_CLOCK_DELAY +static clock_time_t start_count, end_count, diff; +#endif + +#if TEST_CLOCK_SECONDS +static unsigned long sec; +#endif + +#if TEST_ETIMER +static clock_time_t count; +#endif + +#if TEST_RTIMER +static struct rtimer rt; +rtimer_clock_t rt_now, rt_for; +static clock_time_t ct; +#endif + +static uint8_t i; +/*---------------------------------------------------------------------------*/ +PROCESS(clock_test_process, "Clock test process"); +AUTOSTART_PROCESSES(&clock_test_process); +/*---------------------------------------------------------------------------*/ +#if TEST_RTIMER +void +rt_callback(struct rtimer *t, void *ptr) { + rt_now = RTIMER_NOW(); + ct = clock_time(); + printf("Task called at %u (clock = %u)\n", rt_now, ct); +} +#endif +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(clock_test_process, ev, data) +{ + + PROCESS_BEGIN(); + + etimer_set(&et, 2 * CLOCK_SECOND); + + PROCESS_YIELD(); + +#if TEST_CLOCK_DELAY + printf("Clock delay test, (10,000 x i) cycles:\n"); + i = 1; + while(i < 6) { + start_count = clock_time(); + clock_delay(10000 * i); + end_count = clock_time(); + diff = end_count - start_count; + printf("Delayed %u = %u ticks = ~%u ms\n", 10000 * i, diff, diff * 8); + i++; + } +#endif + +#if TEST_RTIMER + printf("Rtimer Test, 1 sec (%u rtimer ticks):\n", RTIMER_SECOND); + i = 0; + while(i < 5) { + etimer_set(&et, 2*CLOCK_SECOND); + printf("=======================\n"); + ct = clock_time(); + rt_now = RTIMER_NOW(); + rt_for = rt_now + RTIMER_SECOND; + printf("Now=%u (clock = %u) - For=%u\n", rt_now, ct, rt_for); + if (rtimer_set(&rt, rt_for, 1, + (void (*)(struct rtimer *, void *))rt_callback, NULL) != RTIMER_OK) { + printf("Error setting\n"); + } + + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + i++; + } +#endif + +#if TEST_ETIMER + printf("Clock tick and etimer test, 1 sec (%u clock ticks):\n", CLOCK_SECOND); + i = 0; + while(i < 10) { + etimer_set(&et, CLOCK_SECOND); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + etimer_reset(&et); + + count = clock_time(); + printf("%u ticks\n", count); + + leds_toggle(LEDS_RED); + i++; + } +#endif + +#if TEST_CLOCK_SECONDS + printf("Clock seconds test (5s):\n"); + i = 0; + while(i < 10) { + etimer_set(&et, 5 * CLOCK_SECOND); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + etimer_reset(&et); + + sec = clock_seconds(); + printf("%lu seconds\n", sec); + + leds_toggle(LEDS_GREEN); + i++; + } +#endif + + printf("Done!\n"); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/cc2530dk/udp-ipv6/Makefile b/examples/cc2530dk/udp-ipv6/Makefile new file mode 100644 index 000000000..f21c02ca2 --- /dev/null +++ b/examples/cc2530dk/udp-ipv6/Makefile @@ -0,0 +1,14 @@ +DEFINES+=PROJECT_CONF_H=\"project-conf.h\" + +HAVE_BANKING=1 +UIP_CONF_IPV6=1 + +PROJECT_SOURCEFILES += ping6.c + +CONTIKI_PROJECT = client server + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/cc2530dk/udp-ipv6/Makefile.target b/examples/cc2530dk/udp-ipv6/Makefile.target new file mode 100644 index 000000000..70609bbdb --- /dev/null +++ b/examples/cc2530dk/udp-ipv6/Makefile.target @@ -0,0 +1 @@ +TARGET = cc2530dk diff --git a/examples/cc2530dk/udp-ipv6/client.c b/examples/cc2530dk/udp-ipv6/client.c new file mode 100644 index 000000000..224a775af --- /dev/null +++ b/examples/cc2530dk/udp-ipv6/client.c @@ -0,0 +1,159 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" + +#include +#include "dev/leds.h" +#include "dev/button-sensor.h" +#include "debug.h" + +#define DEBUG DEBUG_PRINT +#include "net/uip-debug.h" + +#define SEND_INTERVAL 2 * CLOCK_SECOND +#define MAX_PAYLOAD_LEN 40 + +static char buf[MAX_PAYLOAD_LEN]; + +/* Our destinations and udp conns. One link-local and one global */ +#define LOCAL_CONN_PORT 3001 +static struct uip_udp_conn *l_conn; +#if UIP_CONF_ROUTER +#define GLOBAL_CONN_PORT 3002 +static struct uip_udp_conn *g_conn; +#endif + +/*---------------------------------------------------------------------------*/ +PROCESS(udp_client_process, "UDP client process"); +#if BUTTON_SENSOR_ON +PROCESS_NAME(ping6_process); +AUTOSTART_PROCESSES(&udp_client_process, &ping6_process); +#else +AUTOSTART_PROCESSES(&udp_client_process); +#endif +/*---------------------------------------------------------------------------*/ +static void +tcpip_handler(void) +{ + leds_on(LEDS_GREEN); + if(uip_newdata()) { + putstring("0x"); + puthex(uip_datalen()); + putstring(" bytes response=0x"); + puthex((*(uint16_t *) uip_appdata) >> 8); + puthex((*(uint16_t *) uip_appdata) & 0xFF); + putchar('\n'); + } + leds_off(LEDS_GREEN); + return; +} +/*---------------------------------------------------------------------------*/ +static void +timeout_handler(void) +{ + static int seq_id; + struct uip_udp_conn * this_conn; + + leds_on(LEDS_RED); + memset(buf, 0, MAX_PAYLOAD_LEN); + seq_id++; + + /* evens / odds */ + if(seq_id & 0x01) { + this_conn = l_conn; + } else { + this_conn = g_conn; + if(uip_ds6_get_global(ADDR_PREFERRED) == NULL) { + return; + } + } + + PRINTF("Client to: "); + PRINT6ADDR(&this_conn->ripaddr); + + memcpy(buf, &seq_id, sizeof(seq_id)); + + PRINTF(" Remote Port %u,", UIP_HTONS(this_conn->rport)); + PRINTF(" (msg=0x%04x), %u bytes\n", *(uint16_t *) buf, sizeof(seq_id)); + + uip_udp_packet_send(this_conn, buf, sizeof(seq_id)); + leds_off(LEDS_RED); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(udp_client_process, ev, data) +{ + static struct etimer et; + uip_ipaddr_t ipaddr; + + PROCESS_BEGIN(); + PRINTF("UDP client process started\n"); + + uip_ip6addr(&ipaddr,0xfe80,0,0,0,0x0215,0x2000,0x0002,0x2145); + /* new connection with remote host */ + l_conn = udp_new(&ipaddr, UIP_HTONS(3000), NULL); + if(!l_conn) { + PRINTF("udp_new l_conn error.\n"); + } + udp_bind(l_conn, UIP_HTONS(LOCAL_CONN_PORT)); + + PRINTF("Link-Local connection with "); + PRINT6ADDR(&l_conn->ripaddr); + PRINTF(" local/remote port %u/%u\n", + UIP_HTONS(l_conn->lport), UIP_HTONS(l_conn->rport)); + + uip_ip6addr(&ipaddr,0xaaaa,0,0,0,0x0215,0x2000,0x0002,0x2145); + g_conn = udp_new(&ipaddr, UIP_HTONS(3000), NULL); + if(!g_conn) { + PRINTF("udp_new g_conn error.\n"); + } + udp_bind(g_conn, UIP_HTONS(GLOBAL_CONN_PORT)); + + PRINTF("Global connection with "); + PRINT6ADDR(&g_conn->ripaddr); + PRINTF(" local/remote port %u/%u\n", + UIP_HTONS(g_conn->lport), UIP_HTONS(g_conn->rport)); + + etimer_set(&et, SEND_INTERVAL); + + while(1) { + PROCESS_YIELD(); + if(etimer_expired(&et)) { + timeout_handler(); + etimer_restart(&et); + } else if(ev == tcpip_event) { + tcpip_handler(); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/cc2530dk/udp-ipv6/ping6.c b/examples/cc2530dk/udp-ipv6/ping6.c new file mode 100644 index 000000000..f4cc073ab --- /dev/null +++ b/examples/cc2530dk/udp-ipv6/ping6.c @@ -0,0 +1,139 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" +#include +#include + +#include "dev/button-sensor.h" +#include "debug.h" + +#define DEBUG 1 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",lladdr->addr[0], lladdr->addr[1], lladdr->addr[2], lladdr->addr[3],lladdr->addr[4], lladdr->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#endif + +#define PING6_NB 5 +#define PING6_DATALEN 16 + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_ICMP_BUF ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) + +static struct etimer ping6_periodic_timer; +static u8_t count = 0; +static u16_t addr[8]; +static uip_ipaddr_t dest_addr; + +PROCESS(ping6_process, "PING6 process"); +/*---------------------------------------------------------------------------*/ +static void +ping6handler() +{ + if(count < PING6_NB) { + UIP_IP_BUF->vtc = 0x60; + UIP_IP_BUF->tcflow = 1; + UIP_IP_BUF->flow = 0; + UIP_IP_BUF->proto = UIP_PROTO_ICMP6; + UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit; + uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &dest_addr); + uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr); + + UIP_ICMP_BUF->type = ICMP6_ECHO_REQUEST; + UIP_ICMP_BUF->icode = 0; + /* set identifier and sequence number to 0 */ + memset((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN, 0, 4); + /* put one byte of data */ + memset((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN + UIP_ICMP6_ECHO_REQUEST_LEN, + count, PING6_DATALEN); + + + uip_len = UIP_ICMPH_LEN + UIP_ICMP6_ECHO_REQUEST_LEN + UIP_IPH_LEN + PING6_DATALEN; + UIP_IP_BUF->len[0] = (u8_t)((uip_len - 40) >> 8); + UIP_IP_BUF->len[1] = (u8_t)((uip_len - 40) & 0x00FF); + + UIP_ICMP_BUF->icmpchksum = 0; + UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum(); + + + PRINTF("Echo Request to"); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("from"); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("\n"); + UIP_STAT(++uip_stat.icmp.sent); + + tcpip_ipv6_output(); + + count++; + etimer_set(&ping6_periodic_timer, 3 * CLOCK_SECOND); + } else { + count = 0; + } +} + +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(ping6_process, ev, data) +{ + + PROCESS_BEGIN(); + PRINTF("ping6 running.\n"); + PRINTF("Button 1: 5 pings 16 byte payload.\n"); + + uip_ip6addr(&dest_addr,0xaaaa,0,0,0,0x0215,0x2000,0x0002,0x2145); + count = 0; + + icmp6_new(NULL); + + while(1) { + PROCESS_YIELD(); + +#if BUTTON_SENSOR_ON + if(ev == sensors_event && data == &button_sensor && count == 0) { + ping6handler(); + } +#endif + if(ev == PROCESS_EVENT_TIMER && etimer_expired(&ping6_periodic_timer)) { + ping6handler(); + } + if(ev == tcpip_icmp6_event && *(uint8_t *)data == ICMP6_ECHO_REPLY) { + PRINTF("Echo Reply\n"); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/cc2530dk/udp-ipv6/project-conf.h b/examples/cc2530dk/udp-ipv6/project-conf.h new file mode 100644 index 000000000..990a02082 --- /dev/null +++ b/examples/cc2530dk/udp-ipv6/project-conf.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + * $Id$ + */ + +/** + * \file + * Project specific configuration defines for the UDP client/server + * example. + * + * \author + * George Oikonomou - + */ + +#ifndef PROJECT_CONF_H_ +#define PROJECT_CONF_H_ + +#define BUTTON_SENSOR_CONF_ON 1 +#define UIP_CONF_ICMP6 1 + +#endif /* PROJECT_CONF_H_ */ diff --git a/examples/cc2530dk/udp-ipv6/server.c b/examples/cc2530dk/udp-ipv6/server.c new file mode 100644 index 000000000..2d9ff70b7 --- /dev/null +++ b/examples/cc2530dk/udp-ipv6/server.c @@ -0,0 +1,174 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" + +#include + +#define DEBUG DEBUG_PRINT +#include "net/uip-debug.h" +#include "dev/watchdog.h" +#include "dev/leds.h" +#include "net/rpl/rpl.h" +#include "dev/button-sensor.h" +#include "debug.h" + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) + +#define MAX_PAYLOAD_LEN 120 + +static struct uip_udp_conn *server_conn; +static char buf[MAX_PAYLOAD_LEN]; +static uint16_t len; + +#if UIP_CONF_ROUTER +static uip_ipaddr_t ipaddr; +#endif + +#define SERVER_REPLY 1 + +/* Should we act as RPL root? */ +#define SERVER_RPL_ROOT 1 +/*---------------------------------------------------------------------------*/ +PROCESS(udp_server_process, "UDP server process"); +AUTOSTART_PROCESSES(&udp_server_process); +/*---------------------------------------------------------------------------*/ +static void +tcpip_handler(void) +{ + memset(buf, 0, MAX_PAYLOAD_LEN); + if(uip_newdata()) { + leds_on(LEDS_RED); + len = uip_datalen(); + memcpy(buf, uip_appdata, len); + PRINTF("%u bytes from [", len, *(uint16_t *)buf); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("]:%u\n", UIP_HTONS(UIP_UDP_BUF->srcport)); +#if SERVER_REPLY + uip_ipaddr_copy(&server_conn->ripaddr, &UIP_IP_BUF->srcipaddr); + server_conn->rport = UIP_UDP_BUF->srcport; + + uip_udp_packet_send(server_conn, buf, len); + /* Restore server connection to allow data from any node */ + uip_create_unspecified(&server_conn->ripaddr); + server_conn->rport = 0; +#endif + } + leds_off(LEDS_RED); + return; +} +/*---------------------------------------------------------------------------*/ +#if (BUTTON_SENSOR_ON && (DEBUG==DEBUG_PRINT)) +static void +print_stats() +{ + PRINTF("tl=%lu, ts=%lu, bs=%lu, bc=%lu\n", + rimestats.toolong, rimestats.tooshort, rimestats.badsynch, rimestats.badcrc); + PRINTF("llrx=%lu, lltx=%lu, rx=%lu, tx=%lu\n", + rimestats.llrx, rimestats.lltx, rimestats.rx, rimestats.tx); +} +#else +#define print_stats() +#endif +/*---------------------------------------------------------------------------*/ +static void +print_local_addresses(void) +{ + int i; + uint8_t state; + + PRINTF("Server IPv6 addresses:\n"); + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + state = uip_ds6_if.addr_list[i].state; + if(uip_ds6_if.addr_list[i].isused && (state == ADDR_TENTATIVE || state + == ADDR_PREFERRED)) { + PRINTF(" "); + PRINT6ADDR(&uip_ds6_if.addr_list[i].ipaddr); + PRINTF("\n"); + if (state == ADDR_TENTATIVE) { + uip_ds6_if.addr_list[i].state = ADDR_PREFERRED; + } + } + } +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(udp_server_process, ev, data) +{ +#if BUTTON_SENSOR_ON + static struct sensors_sensor *b1; +#endif +#if SERVER_RPL_ROOT + rpl_dag_t *dag; +#endif + PROCESS_BEGIN(); + putstring("Starting UDP server\n"); + +#if BUTTON_SENSOR_ON + putstring("Button 1: Print RIME stats\n"); +#endif + +#if SERVER_RPL_ROOT + uip_ip6addr(&ipaddr, 0xaaaa, 0, 0, 0, 0, 0, 0, 0); + uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr); + uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF); + + print_local_addresses(); + + dag = rpl_set_root(RPL_DEFAULT_INSTANCE, &uip_ds6_get_global(ADDR_PREFERRED)->ipaddr); + if(dag != NULL) { + uip_ip6addr(&ipaddr, 0xaaaa, 0, 0, 0, 0, 0, 0, 0); + rpl_set_prefix(dag, &ipaddr, 64); + PRINTF("Created a new RPL dag with ID: "); + PRINT6ADDR(&dag->dag_id); + PRINTF("\n"); + } +#endif /* SERVER_RPL_ROOT */ + + server_conn = udp_new(NULL, UIP_HTONS(0), NULL); + udp_bind(server_conn, UIP_HTONS(3000)); + + PRINTF("Listen port: 3000, TTL=%u\n", server_conn->ttl); + + while(1) { + PROCESS_YIELD(); + if(ev == tcpip_event) { + tcpip_handler(); +#if BUTTON_SENSOR_ON + } else if(ev == sensors_event && data == &button_sensor) { + print_stats(); +#endif /* (CONTIKI_TARGET_SENSINODE && BUTTON_SENSOR_ON) */ + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/cc2530dk/Makefile.cc2530dk b/platform/cc2530dk/Makefile.cc2530dk new file mode 100644 index 000000000..12001350f --- /dev/null +++ b/platform/cc2530dk/Makefile.cc2530dk @@ -0,0 +1,52 @@ +# cc2530dk platform makefile + +ifndef CONTIKI + $(error CONTIKI not defined! You must specify where CONTIKI resides!) +endif + +CONTIKI_TARGET_DIRS = . dev +CONTIKI_TARGET_MAIN = $(addprefix $(OBJECTDIR)/,contiki-main.rel) + +CONTIKI_TARGET_SOURCEFILES = contiki-main.c +CONTIKI_TARGET_SOURCEFILES += leds.c leds-arch.c +CONTIKI_TARGET_SOURCEFILES += sensors.c smartrf-sensors.c +CONTIKI_TARGET_SOURCEFILES += button-sensor.c adc-sensor.c smartrf-sensors.c +CONTIKI_TARGET_SOURCEFILES += serial-line.c slip-arch.c slip.c #serial-flash.c +CONTIKI_TARGET_SOURCEFILES += putchar.c debug.c + +CONTIKI_SOURCEFILES += $(CONTIKI_TARGET_SOURCEFILES) + +CLEAN += *.cc2530dk + +ifdef UIP_CONF_IPV6 + CONTIKI_TARGET_SOURCEFILES += viztool.c +endif + +FORCE: + +# .sensinode target so we can behave similar to other targets +%.$(TARGET): %.hex FORCE + cp $< $(<:.hex=.$(TARGET)) + @echo "\nReport" + @echo "===============" + @echo 'Code footprint:' + @echo 'Area Addr Size' \ + ' Decimal' + @echo '---------------------------------- -------- --------' \ + ' --------' + @echo -n 'HOME,CSEG,CONST,XINIT,GS* $(HOME_START) ' + @egrep ',CODE\)' $(<:.hex=.map) | egrep -v '(^BANK[1-9][^=])' | uniq | \ + awk '{ SUM += $$5 } END { printf "%08X = %8d", SUM, SUM }' + @echo '. bytes (REL,CON,CODE)' + @egrep '(^BANK[1-9][^=])' $(<:.hex=.map) | uniq | sort + @egrep -A 5 'Other memory' $(<:.hex=.mem) + +%.upload: %.hex + $(PROG) -P $< + +sensinode.serialdump: + $(SERIALDUMP) + +### Define the CPU directory +CONTIKI_CPU=$(CONTIKI)/cpu/cc253x +include $(CONTIKI_CPU)/Makefile.cc253x diff --git a/platform/cc2530dk/contiki-conf.h b/platform/cc2530dk/contiki-conf.h new file mode 100644 index 000000000..4eae96016 --- /dev/null +++ b/platform/cc2530dk/contiki-conf.h @@ -0,0 +1,225 @@ +#ifndef __CONTIKI_CONF_H__ +#define __CONTIKI_CONF_H__ + +#include "8051def.h" +#include "sys/cc.h" +#include + +/* Include Project Specific conf */ +#ifdef PROJECT_CONF_H +#include "project-conf.h" +#endif /* PROJECT_CONF_H */ + +/* Time type. */ +typedef unsigned short clock_time_t; + +/* Defines tick counts for a second. */ +#define CLOCK_CONF_SECOND 128 + +/* Energest Module */ +#ifndef ENERGEST_CONF_ON +#define ENERGEST_CONF_ON 0 +#endif + +/* Verbose Startup? Turning this off saves plenty of bytes of CODE in HOME */ +#define STARTUP_CONF_VERBOSE 0 + +/* More CODE space savings by turning off process names */ +#define PROCESS_CONF_NO_PROCESS_NAMES 1 + +/* + * USARTs: + * SmartRF RS232 -> USART0 / Alternative 1 (UART) + * SmartRF LCD -> USART1 / Alternative 2 (SPI) + */ +#define UART_ON_USART 0 + +#define UART1_CONF_ENABLE 0 + +#ifndef UART0_CONF_ENABLE +#define UART0_CONF_ENABLE 1 +#endif +#ifndef UART0_CONF_WITH_INPUT +#define UART0_CONF_WITH_INPUT 0 +#endif + +#ifndef UART0_CONF_HIGH_SPEED +#define UART0_CONF_HIGH_SPEED 0 +#endif + +/* Are we a SLIP bridge? */ +#if SLIP_ARCH_CONF_ENABLE +/* Make sure the UART is enabled, with interrupts */ +#undef UART0_CONF_ENABLE +#undef UART0_CONF_WITH_INPUT +#define UART0_CONF_ENABLE 1 +#define UART0_CONF_WITH_INPUT 1 +#define UIP_FALLBACK_INTERFACE slip_interface +#endif + +/* Output all captured frames over the UART in hexdump format */ +#ifndef CC2530_RF_CONF_HEXDUMP +#define CC2530_RF_CONF_HEXDUMP 0 +#endif + +#if CC2530_RF_CONF_HEXDUMP +/* We need UART1 output */ +#undef UART_ZERO_CONF_ENABLE +#define UART_ZERO_CONF_ENABLE 1 +#endif + +/* Code Shortcuts */ +/* + * When set, the RF driver is no longer a contiki process and the RX ISR is + * disabled. Instead of polling the radio process when data arrives, we + * periodically check for data by directly invoking the driver from main() + + * When set, this directive also configures the following bypasses: + * - process_post_synch() in tcpip_input() (we call packet_input()) + * - process_post_synch() in tcpip_uipcall (we call the relevant pthread) + * - mac_call_sent_callback() is replaced with sent() in various places + * + * These are good things to do, we reduce stack usage, RAM size and code size + */ +#define SHORTCUTS_CONF_NETSTACK 1 + +/* + * Sensors + * It is harmless to #define XYZ 1 + * even if the sensor is not present on our device + */ +#ifndef BUTTON_SENSOR_CONF_ON +#define BUTTON_SENSOR_CONF_ON 1 /* Buttons */ +#endif +/* ADC - Turning this off will disable everything below */ +#ifndef ADC_SENSOR_CONF_ON +#define ADC_SENSOR_CONF_ON 1 +#endif +#define TEMP_SENSOR_CONF_ON 1 /* Temperature */ +#define VDD_SENSOR_CONF_ON 1 /* Supply Voltage */ +#define BATTERY_SENSOR_CONF_ON 0 /* Battery */ + +/* Low Power Modes - We only support PM0/Idle and PM1 */ +#ifndef LPM_CONF_MODE +#define LPM_CONF_MODE 0 /* 0: no LPM, 1: MCU IDLE, 2: Drop to PM1 */ +#endif + +/* Some files include leds.h before us */ +#undef LEDS_YELLOW +#undef LEDS_RED +#define LEDS_YELLOW 4 +#define LEDS_RED 2 + +/* DMA Configuration */ +#ifndef DMA_CONF_ON +#define DMA_CONF_ON 0 +#endif + +/* Viztool on by default for IPv6 builds */ +#if UIP_CONF_IPV6 +#ifndef VIZTOOL_CONF_ON +#define VIZTOOL_CONF_ON 1 +#endif /* VIZTOOL_CONF_ON */ +#endif /* UIP_CONF_IPV6 */ + +/* Network Stack */ +#if UIP_CONF_IPV6 +#define NETSTACK_CONF_NETWORK sicslowpan_driver +#else +#define NETSTACK_CONF_NETWORK rime_driver +#endif + +#ifndef NETSTACK_CONF_MAC +#define NETSTACK_CONF_MAC csma_driver +#endif + +#ifndef NETSTACK_CONF_RDC +#define NETSTACK_CONF_RDC nullrdc_driver +#define NULLRDC_802154_AUTOACK 1 +#define NULLRDC_802154_AUTOACK_HW 1 +#endif + +#ifndef NETSTACK_CONF_RDC_CHANNEL_CHECK_RATE +#define NETSTACK_CONF_RDC_CHANNEL_CHECK_RATE 8 +#endif + +#define NETSTACK_CONF_FRAMER framer_802154 +#define NETSTACK_CONF_RADIO cc2530_rf_driver + +/* RF Config */ +#define IEEE802154_CONF_PANID 0x5449 /* TI */ + +#ifndef CC2530_RF_CONF_CHANNEL +#define CC2530_RF_CONF_CHANNEL 25 +#endif /* CC2530_RF_CONF_CHANNEL */ + +#ifndef CC2530_RF_CONF_AUTOACK +#define CC2530_RF_CONF_AUTOACK 1 +#endif /* CC2530_CONF_AUTOACK */ + +#if UIP_CONF_IPV6 +/* Addresses, Sizes and Interfaces */ +/* 8-byte addresses here, 2 otherwise */ +#define RIMEADDR_CONF_SIZE 8 +#define UIP_CONF_LL_802154 1 +#define UIP_CONF_LLH_LEN 0 +#define UIP_CONF_NETIF_MAX_ADDRESSES 3 + +/* TCP, UDP, ICMP */ +#define UIP_CONF_TCP 0 +#define UIP_CONF_UDP 1 +#define UIP_CONF_UDP_CHECKSUMS 1 + +/* ND and Routing */ +#define UIP_CONF_ROUTER 1 +#define UIP_CONF_IPV6_RPL 1 +#define UIP_CONF_ND6_SEND_RA 0 +#define UIP_CONF_IP_FORWARD 0 +#define RPL_CONF_STATS 0 +#define RPL_CONF_MAX_DAG_ENTRIES 1 +#ifndef RPL_CONF_OF +#define RPL_CONF_OF rpl_of_etx +#endif + +#define UIP_CONF_ND6_REACHABLE_TIME 600000 +#define UIP_CONF_ND6_RETRANS_TIMER 10000 + +#ifndef UIP_CONF_DS6_NBR_NBU +#define UIP_CONF_DS6_NBR_NBU 4 /* Handle n Neighbors */ +#endif +#ifndef UIP_CONF_DS6_ROUTE_NBU +#define UIP_CONF_DS6_ROUTE_NBU 4 /* Handle n Routes */ +#endif + +/* uIP */ +#define UIP_CONF_BUFFER_SIZE 240 +#define UIP_CONF_IPV6_QUEUE_PKT 0 +#define UIP_CONF_IPV6_CHECKS 1 +#define UIP_CONF_IPV6_REASSEMBLY 0 + +/* 6lowpan */ +#define SICSLOWPAN_CONF_COMPRESSION SICSLOWPAN_COMPRESSION_HC06 +#ifndef SICSLOWPAN_CONF_FRAG +#define SICSLOWPAN_CONF_FRAG 0 /* About 2KB of CODE if 1 */ +#endif +#define SICSLOWPAN_CONF_MAXAGE 8 + +/* Define our IPv6 prefixes/contexts here */ +#define SICSLOWPAN_CONF_MAX_ADDR_CONTEXTS 1 +#define SICSLOWPAN_CONF_ADDR_CONTEXT_0 { \ + addr_contexts[0].prefix[0] = 0xaa; \ + addr_contexts[0].prefix[1] = 0xaa; \ +} + +#define MAC_CONF_CHANNEL_CHECK_RATE 8 +#define QUEUEBUF_CONF_NUM 6 + +#else /* UIP_CONF_IPV6 */ +/* Network setup for non-IPv6 (rime). */ +#define UIP_CONF_IP_FORWARD 1 +#define UIP_CONF_BUFFER_SIZE 108 +#define RIME_CONF_NO_POLITE_ANNOUCEMENTS 0 +#define QUEUEBUF_CONF_NUM 8 +#endif /* UIP_CONF_IPV6 */ + +#endif /* __CONTIKI_CONF_H__ */ diff --git a/platform/cc2530dk/contiki-main.c b/platform/cc2530dk/contiki-main.c new file mode 100644 index 000000000..e5b89a682 --- /dev/null +++ b/platform/cc2530dk/contiki-main.c @@ -0,0 +1,333 @@ +#include "contiki.h" +#include "soc.h" +#include "sys/clock.h" +#include "sys/autostart.h" +#include "dev/serial-line.h" +#include "dev/slip.h" +#include "dev/leds.h" +#include "dev/uart0.h" +#include "dev/dma.h" +#include "dev/cc2530-rf.h" +#include "dev/watchdog.h" +#include "dev/clock-isr.h" +#include "dev/lpm.h" +#include "dev/button-sensor.h" +#include "dev/adc-sensor.h" +#include "dev/leds-arch.h" +#include "net/rime.h" +#include "net/netstack.h" +#include "net/mac/frame802154.h" +#include "debug.h" +#include "cc253x.h" +#include "sfr-bits.h" +#include "contiki-lib.h" +#include "contiki-net.h" +/*---------------------------------------------------------------------------*/ +#if VIZTOOL_CONF_ON +PROCESS_NAME(viztool_process); +#endif +/*---------------------------------------------------------------------------*/ +#ifdef STARTUP_CONF_VERBOSE +#define STARTUP_VERBOSE STARTUP_CONF_VERBOSE +#else +#define STARTUP_VERBOSE 0 +#endif + +#if STARTUP_VERBOSE +#define PUTSTRING(...) putstring(__VA_ARGS__) +#define PUTHEX(...) puthex(__VA_ARGS__) +#define PUTBIN(...) putbin(__VA_ARGS__) +#define PUTCHAR(...) putchar(__VA_ARGS__) +#else +#define PUTSTRING(...) do {} while(0) +#define PUTHEX(...) do {} while(0) +#define PUTBIN(...) do {} while(0) +#define PUTCHAR(...) do {} while(0) +#endif +/*---------------------------------------------------------------------------*/ +extern rimeaddr_t rimeaddr_node_addr; +static __data int r; +static __data int len; +/*---------------------------------------------------------------------------*/ +#if ENERGEST_CONF_ON +static unsigned long irq_energest = 0; +#define ENERGEST_IRQ_SAVE(a) do { \ + a = energest_type_time(ENERGEST_TYPE_IRQ); } while(0) +#define ENERGEST_IRQ_RESTORE(a) do { \ + energest_type_set(ENERGEST_TYPE_IRQ, a); } while(0) +#else +#define ENERGEST_IRQ_SAVE(a) do {} while(0) +#define ENERGEST_IRQ_RESTORE(a) do {} while(0) +#endif +/*---------------------------------------------------------------------------*/ +static void +fade(int l) +{ + volatile int i, a; + int k, j; + for(k = 0; k < 400; ++k) { + j = k > 200? 400 - k: k; + + leds_on(l); + for(i = 0; i < j; ++i) { + a = i; + } + leds_off(l); + for(i = 0; i < 200 - j; ++i) { + a = i; + } + } +} +/*---------------------------------------------------------------------------*/ +static void +set_rime_addr(void) +{ + uint8_t *addr_long = NULL; + uint16_t addr_short = 0; + char i; + + __xdata unsigned char * macp = &X_IEEE_ADDR; + + PUTSTRING("Rime is 0x"); + PUTHEX(sizeof(rimeaddr_t)); + PUTSTRING(" bytes long\n"); + + PUTSTRING("Reading MAC from Info Page\n"); + + for(i = (RIMEADDR_SIZE - 1); i >= 0; --i) { + rimeaddr_node_addr.u8[i] = *macp; + macp++; + } + + /* Now the address is stored MSB first */ +#if STARTUP_VERBOSE + PUTSTRING("Rime configured with address "); + for(i = 0; i < RIMEADDR_SIZE - 1; i++) { + PUTHEX(rimeaddr_node_addr.u8[i]); + PUTCHAR(':'); + } + PUTHEX(rimeaddr_node_addr.u8[i]); + PUTCHAR('\n'); +#endif + + cc2530_rf_set_addr(IEEE802154_PANID); +} +/*---------------------------------------------------------------------------*/ +int +main(void) +{ + /* Hardware initialization */ + clock_init(); + soc_init(); + rtimer_init(); + + /* Init LEDs here */ + leds_init(); + leds_off(LEDS_ALL); + fade(LEDS_GREEN); + + /* initialize process manager. */ + process_init(); + + /* Init UART */ + uart0_init(); + +#if DMA_ON + dma_init(); +#endif + +#if SLIP_ARCH_CONF_ENABLE + slip_arch_init(0); +#else + uart0_set_input(serial_line_input_byte); + serial_line_init(); +#endif + fade(LEDS_RED); + + PUTSTRING("##########################################\n"); + putstring(CONTIKI_VERSION_STRING "\n"); + putstring("TI SmartRF05 EB\n"); + switch(CHIPID) { + case 0xA5: + putstring("cc2530"); + break; + case 0xB5: + putstring("cc2531"); + break; + case 0x95: + putstring("cc2533"); + break; + case 0x8D: + putstring("cc2540"); + break; + } + + putstring("-F"); + switch(CHIPINFO0 & 0x70) { + case 0x40: + putstring("256, "); + break; + case 0x30: + putstring("128, "); + break; + case 0x20: + putstring("64, "); + break; + case 0x10: + putstring("32, "); + break; + } + puthex(CHIPINFO1 + 1); + putstring("KB SRAM\n"); + + PUTSTRING("\nSDCC Build:\n"); +#if STARTUP_VERBOSE +#ifdef HAVE_SDCC_BANKING + PUTSTRING(" With Banking.\n"); +#endif /* HAVE_SDCC_BANKING */ +#ifdef SDCC_MODEL_LARGE + PUTSTRING(" --model-large\n"); +#endif /* SDCC_MODEL_LARGE */ +#ifdef SDCC_MODEL_HUGE + PUTSTRING(" --model-huge\n"); +#endif /* SDCC_MODEL_HUGE */ +#ifdef SDCC_STACK_AUTO + PUTSTRING(" --stack-auto\n"); +#endif /* SDCC_STACK_AUTO */ + + PUTCHAR('\n'); + + PUTSTRING(" Net: "); + PUTSTRING(NETSTACK_NETWORK.name); + PUTCHAR('\n'); + PUTSTRING(" MAC: "); + PUTSTRING(NETSTACK_MAC.name); + PUTCHAR('\n'); + PUTSTRING(" RDC: "); + PUTSTRING(NETSTACK_RDC.name); + PUTCHAR('\n'); + + PUTSTRING("##########################################\n"); +#endif + + watchdog_init(); + + /* Initialise the H/W RNG engine. */ + random_init(0); + + /* start services */ + process_start(&etimer_process, NULL); + ctimer_init(); + + /* initialize the netstack */ + netstack_init(); + set_rime_addr(); + +#if BUTTON_SENSOR_ON || ADC_SENSOR_ON + process_start(&sensors_process, NULL); + BUTTON_SENSOR_ACTIVATE(); + ADC_SENSOR_ACTIVATE(); +#endif + +#if UIP_CONF_IPV6 + memcpy(&uip_lladdr.addr, &rimeaddr_node_addr, sizeof(uip_lladdr.addr)); + queuebuf_init(); + process_start(&tcpip_process, NULL); +#endif /* UIP_CONF_IPV6 */ + +#if VIZTOOL_CONF_ON + process_start(&viztool_process, NULL); +#endif + + energest_init(); + ENERGEST_ON(ENERGEST_TYPE_CPU); + + autostart_start(autostart_processes); + + watchdog_start(); + + fade(LEDS_YELLOW); + + while(1) { + do { + /* Reset watchdog and handle polls and events */ + watchdog_periodic(); + r = process_run(); + } while(r > 0); +#if SHORTCUTS_CONF_NETSTACK + len = NETSTACK_RADIO.pending_packet(); + if(len) { + packetbuf_clear(); + len = NETSTACK_RADIO.read(packetbuf_dataptr(), PACKETBUF_SIZE); + if(len > 0) { + packetbuf_set_datalen(len); + NETSTACK_RDC.input(); + } + } +#endif + +#if LPM_MODE +#if (LPM_MODE==LPM_MODE_PM2) + SLEEP &= ~OSC_PD; /* Make sure both HS OSCs are on */ + while(!(SLEEP & HFRC_STB)); /* Wait for RCOSC to be stable */ + CLKCON |= OSC; /* Switch to the RCOSC */ + while(!(CLKCON & OSC)); /* Wait till it's happened */ + SLEEP |= OSC_PD; /* Turn the other one off */ +#endif /* LPM_MODE==LPM_MODE_PM2 */ + + /* + * Set MCU IDLE or Drop to PM1. Any interrupt will take us out of LPM + * Sleep Timer will wake us up in no more than 7.8ms (max idle interval) + */ + SLEEPCMD = (SLEEPCMD & 0xFC) | (LPM_MODE - 1); + +#if (LPM_MODE==LPM_MODE_PM2) + /* + * Wait 3 NOPs. Either an interrupt occurred and SLEEP.MODE was cleared or + * no interrupt occurred and we can safely power down + */ + __asm + nop + nop + nop + __endasm; + + if(SLEEPCMD & SLEEP_MODE0) { +#endif /* LPM_MODE==LPM_MODE_PM2 */ + + ENERGEST_OFF(ENERGEST_TYPE_CPU); + ENERGEST_ON(ENERGEST_TYPE_LPM); + + /* We are only interested in IRQ energest while idle or in LPM */ + ENERGEST_IRQ_RESTORE(irq_energest); + + /* Go IDLE or Enter PM1 */ + PCON |= PCON_IDLE; + + /* First instruction upon exiting PM1 must be a NOP */ + __asm + nop + __endasm; + + /* Remember energest IRQ for next pass */ + ENERGEST_IRQ_SAVE(irq_energest); + + ENERGEST_ON(ENERGEST_TYPE_CPU); + ENERGEST_OFF(ENERGEST_TYPE_LPM); + +#if (LPM_MODE==LPM_MODE_PM2) + SLEEPCMD &= ~SLEEP_OSC_PD; /* Make sure both HS OSCs are on */ + while(!(SLEEPCMD & SLEEP_XOSC_STB)); /* Wait for XOSC to be stable */ + CLKCONCMD &= ~CLKCONCMD_OSC; /* Switch to the XOSC */ + /* + * On occasion the XOSC is reported stable when in reality it's not. + * We need to wait for a safeguard of 64us or more before selecting it + */ + clock_delay(10); + while(CLKCONCMD & CLKCONCMD_OSC); /* Wait till it's happened */ + } +#endif /* LPM_MODE==LPM_MODE_PM2 */ +#endif /* LPM_MODE */ + } +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/cc2530dk/debug.c b/platform/cc2530dk/debug.c new file mode 100644 index 000000000..29efccf06 --- /dev/null +++ b/platform/cc2530dk/debug.c @@ -0,0 +1,42 @@ +/** + * \file + * + * Definition of some debugging functions. + * + * putstring() and puthex() are from msp430/watchdog.c + * + * \author + * George Oikonomou - + */ + +#include "8051def.h" +#include "debug.h" + +static const char hexconv[] = "0123456789abcdef"; +static const char binconv[] = "01"; +/*---------------------------------------------------------------------------*/ +void +putstring(char *s) +{ + while(*s) { + putchar(*s++); + } +} +/*---------------------------------------------------------------------------*/ +void +puthex(uint8_t c) +{ + putchar(hexconv[c >> 4]); + putchar(hexconv[c & 0x0f]); +} +/*---------------------------------------------------------------------------*/ +void +putbin(uint8_t c) +{ + unsigned char i = 0x80; + while(i) { + putchar(binconv[(c & i) != 0]); + i >>= 1; + } +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/cc2530dk/debug.h b/platform/cc2530dk/debug.h new file mode 100644 index 000000000..a3c5cb01a --- /dev/null +++ b/platform/cc2530dk/debug.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005, Swedish Institute of Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +/** + * \file + * Header file for debugging functions used by the sensinode port. + * + * putstring() and puthex() are from msp430/watchdog.c + * + * \author + * George Oikonomou - + */ + +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#include "8051def.h" +#include "dev/uart1.h" + +void putchar(char c); +void putstring(char *s); +void puthex(uint8_t c); +void putbin(uint8_t c); + +#endif /* __DEBUG_H__ */ diff --git a/platform/cc2530dk/dev/adc-sensor.c b/platform/cc2530dk/dev/adc-sensor.c new file mode 100644 index 000000000..005852d96 --- /dev/null +++ b/platform/cc2530dk/dev/adc-sensor.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ADC sensor module for TI SmartRF05EB devices. + * + * \author + * George Oikonomou - + */ +#include "sfr-bits.h" +#include "cc253x.h" +#include "adc-sensor.h" + +#if ADC_SENSOR_ON +/*---------------------------------------------------------------------------*/ +static int +value(int type) +{ + uint16_t reading; + /* + * For single-shot AD conversions, we may only write to ADCCON3[3:0] once + * (This write triggers the conversion). We thus use the variable 'command' + * to store intermediate steps (reference, decimation rate, input channel) + */ + uint8_t command; + + /* 1.25V ref, max decimation rate */ + command = ADCCON3_EDIV1 | ADCCON3_EDIV0; + + /* Clear the Interrupt Flag */ + ADCIF = 0; + + /* Depending on the desired reading, append the input bits to 'command' and + * enable the corresponding input channel in ADCCFG if necessary */ + switch(type) { +#if TEMP_SENSOR_ON + case ADC_SENSOR_TYPE_TEMP: + command |= ADCCON3_ECH3 | ADCCON3_ECH2 | ADCCON3_ECH1; + break; +#endif +#if VDD_SENSOR_ON + case ADC_SENSOR_TYPE_VDD: + command |= ADCCON3_ECH3 | ADCCON3_ECH2 | ADCCON3_ECH1 | ADCCON3_ECH0; + break; +#endif + default: + /* If the sensor is not present or disabled in conf, return -1 */ + return -1; + } + + /* Writing in bits 3:0 of ADCCON3 will trigger a single conversion */ + ADCCON3 = command; + + /* + * When the conversion is complete, the ADC interrupt flag is set. We don't + * use an ISR here, we just wait on the flag and clear it afterwards. + */ + while(!ADCIF); + + /* Clear the Interrupt Flag */ + ADCIF = 0; + + reading = ADCL; + reading |= (((uint8_t) ADCH) << 8); + /* 12-bit decimation rate: 4 LS bits are noise */ + reading >>= 4; + + return reading; +} +/*---------------------------------------------------------------------------*/ +static int +status(int type) +{ + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +configure(int type, int value) +{ + switch(type) { + case SENSORS_HW_INIT: +#if TEMP_SENSOR_ON + /* Connect temperature sensor to the SoC */ + ATEST = 1; + TR0 = 1; +#endif + APCFG = 0; /* Disables Input Channels */ + break; + } + return 1; +} +/*---------------------------------------------------------------------------*/ +SENSORS_SENSOR(adc_sensor, ADC_SENSOR, value, configure, status); +#endif /* ADC_SENSOR_ON */ diff --git a/platform/cc2530dk/dev/adc-sensor.h b/platform/cc2530dk/dev/adc-sensor.h new file mode 100644 index 000000000..45b0a9985 --- /dev/null +++ b/platform/cc2530dk/dev/adc-sensor.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Header file for ADC sensors on the SmartRF05EB. + * + * Sensors will be off by default, unless turned on explicitly + * in contiki-conf.h + * + * \author + * George Oikonomou - + */ + +#ifndef __ADC_SENSOR_H__ +#define __ADC_SENSOR_H__ + +#include "cc253x.h" +#include "contiki-conf.h" +#include "lib/sensors.h" + +/* ADC Sensor Types */ +#define ADC_SENSOR "ADC" + +#define ADC_SENSOR_TYPE_TEMP 0 +#define ADC_SENSOR_TYPE_VDD 4 + +#ifdef ADC_SENSOR_CONF_ON +#define ADC_SENSOR_ON ADC_SENSOR_CONF_ON +#endif /* ADC_SENSOR_CONF_ON */ + +#if ADC_SENSOR_ON +extern const struct sensors_sensor adc_sensor; +#define ADC_SENSOR_ACTIVATE() adc_sensor.configure(SENSORS_ACTIVE, 1) +#else +#define ADC_SENSOR_ACTIVATE() +#endif /* ADC_SENSOR_ON */ + +/* Battery - SmartRF stuff */ +#ifdef BATTERY_SENSOR_CONF_ON +#define BATTERY_SENSOR_ON BATTERY_SENSOR_CONF_ON +#endif /* BATTERY_SENSOR_CONF_ON */ + +/* Temperature - Available on all devices */ +#ifdef TEMP_SENSOR_CONF_ON +#define TEMP_SENSOR_ON TEMP_SENSOR_CONF_ON +#endif /* TEMP_SENSOR_CONF_ON */ + +/* Supply Voltage (VDD / 3) - Available on all devices*/ +#ifdef VDD_SENSOR_CONF_ON +#define VDD_SENSOR_ON VDD_SENSOR_CONF_ON +#endif /* VDD_SENSOR_CONF_ON */ + +#endif /* __ADC_SENSOR_H__ */ diff --git a/platform/cc2530dk/dev/button-sensor.c b/platform/cc2530dk/dev/button-sensor.c new file mode 100644 index 000000000..d2d9856d2 --- /dev/null +++ b/platform/cc2530dk/dev/button-sensor.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2005, Swedish Institute of Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/* + * This file contains ISRs: Keep it in the HOME bank. + */ +#include "dev/port.h" +#include "dev/button-sensor.h" +/*---------------------------------------------------------------------------*/ +#if BUTTON_SENSOR_ON +static __data struct timer debouncetimer; +/*---------------------------------------------------------------------------*/ +static +int value(int type) +{ + return BUTTON_READ() || !timer_expired(&debouncetimer); +} +/*---------------------------------------------------------------------------*/ +static +int status(int type) +{ + switch (type) { + case SENSORS_ACTIVE: + case SENSORS_READY: + return BUTTON_IRQ_ENABLED(); + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static +int configure(int type, int value) +{ + switch(type) { + case SENSORS_HW_INIT: + P0INP |= 2; /* Tri-state */ + BUTTON_IRQ_ON_PRESS(); + BUTTON_FUNC_GPIO(); + BUTTON_DIR_INPUT(); + return 1; + case SENSORS_ACTIVE: + if(value) { + if(!BUTTON_IRQ_ENABLED()) { + timer_set(&debouncetimer, 0); + BUTTON_IRQ_FLAG_OFF(); + BUTTON_IRQ_ENABLE(); + } + } else { + BUTTON_IRQ_DISABLE(); + } + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +void +port_0_isr(void) __interrupt(P0INT_VECTOR) +{ + EA = 0; + ENERGEST_ON(ENERGEST_TYPE_IRQ); + + /* This ISR is for the entire port. Check if the interrupt was caused by our + * button's pin. */ + if(BUTTON_IRQ_CHECK()) { + if(timer_expired(&debouncetimer)) { + timer_set(&debouncetimer, CLOCK_SECOND / 8); + sensors_changed(&button_sensor); + } + } + + BUTTON_IRQ_FLAG_OFF(); + + ENERGEST_OFF(ENERGEST_TYPE_IRQ); + EA = 1; +} +/*---------------------------------------------------------------------------*/ +SENSORS_SENSOR(button_sensor, BUTTON_SENSOR, value, configure, status); +#endif /* BUTTON_SENSOR_ON */ diff --git a/platform/cc2530dk/dev/button-sensor.h b/platform/cc2530dk/dev/button-sensor.h new file mode 100644 index 000000000..9eb13b89e --- /dev/null +++ b/platform/cc2530dk/dev/button-sensor.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Override core/dev/button-sensor.h + * + * \author + * George Oikonomou - + */ + +#ifndef __BUTTON_SENSOR_H__ +#define __BUTTON_SENSOR_H__ + +#include "contiki-conf.h" +#include "lib/sensors.h" + +#define BUTTON_PORT 0 +#define BUTTON_PIN 1 + +#define BUTTON_SENSOR "Button" + +/* + * SmartRF Buttons + * B1: P0_1, B2: Not Connected + */ +#ifdef BUTTON_SENSOR_CONF_ON +#define BUTTON_SENSOR_ON BUTTON_SENSOR_CONF_ON +#endif /* BUTTON_SENSOR_CONF_ON */ + +#if BUTTON_SENSOR_ON +extern const struct sensors_sensor button_sensor; +/* Button 1: P0_1 - Port 0 ISR needed */ +void port_0_isr(void) __interrupt(P0INT_VECTOR); +#define BUTTON_SENSOR_ACTIVATE() button_sensor.configure(SENSORS_ACTIVE, 1) +#else +#define BUTTON_SENSOR_ACTIVATE() +#endif /* BUTTON_SENSOR_ON */ + +/* Define macros for button 1 */ +#define BUTTON_READ() PORT_READ(BUTTON_PORT, BUTTON_PIN) +#define BUTTON_FUNC_GPIO() PORT_FUNC_GPIO(BUTTON_PORT, BUTTON_PIN) +#define BUTTON_DIR_INPUT() PORT_DIR_INPUT(BUTTON_PORT, BUTTON_PIN) +#define BUTTON_IRQ_ENABLED() PORT_IRQ_ENABLED(BUTTON_PORT, BUTTON_PIN) +#define BUTTON_IRQ_CHECK() PORT_IRQ_CHECK(BUTTON_PORT, BUTTON_PIN) +#define BUTTON_IRQ_ENABLE() PORT_IRQ_ENABLE(BUTTON_PORT, BUTTON_PIN) +#define BUTTON_IRQ_DISABLE() PORT_IRQ_DISABLE(BUTTON_PORT, BUTTON_PIN) +#define BUTTON_IRQ_FLAG_OFF() PORT_IRQ_FLAG_OFF(BUTTON_PORT, BUTTON_PIN) +#define BUTTON_IRQ_ON_PRESS() PORT_IRQ_EDGE_RISE(BUTTON_PORT, BUTTON_PIN) +#define BUTTON_IRQ_ON_RELEASE() PORT_IRQ_EDGE_FALL(BUTTON_PORT, BUTTON_PIN) + +#endif /* __BUTTON_SENSOR_H__ */ diff --git a/platform/cc2530dk/dev/leds-arch.c b/platform/cc2530dk/dev/leds-arch.c new file mode 100644 index 000000000..fb583a5c2 --- /dev/null +++ b/platform/cc2530dk/dev/leds-arch.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Platform-specific led driver for the TI SmartRF05 Eval. Board. + * + * \author + * George Oikonomou - + */ +#include "contiki-conf.h" +#include "dev/leds.h" +#include "dev/leds-arch.h" +#include "cc253x.h" + +/* + * LEDS + * 1: P1_0 + * 2: P1_1 + * 3: P1_4 + * 4: P0_1 (LED4 shares port/pin with B1 and is currently unused) + */ + +/* H/W Connections */ +#define LED1_PIN P1_0 +#define LED2_PIN P1_1 +#define LED3_PIN P1_4 + +/* P0DIR and P0SEL masks */ +#define LED1_MASK 0x01 +#define LED2_MASK 0x02 +#define LED3_MASK 0x10 +#define LED4_MASK 0x02 +/*---------------------------------------------------------------------------*/ +void +leds_arch_init(void) +{ + P1SEL &= ~(LED1_MASK | LED2_MASK | LED3_MASK); + P1DIR |= (LED1_MASK | LED2_MASK | LED3_MASK); +} +/*---------------------------------------------------------------------------*/ +unsigned char +leds_arch_get(void) +{ + unsigned char v; + + v = (unsigned char) (LED1_PIN | (LED2_PIN << 1) | (LED3_PIN << 2)); + + return v; +} +/*---------------------------------------------------------------------------*/ +void +leds_arch_set(unsigned char leds) +{ + LED1_PIN = leds & 0x01; + LED2_PIN = (leds & 0x02) >> 1; + LED3_PIN = (leds & 0x04) >> 2; +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/cc2530dk/dev/leds-arch.h b/platform/cc2530dk/dev/leds-arch.h new file mode 100644 index 000000000..b19d5cf9d --- /dev/null +++ b/platform/cc2530dk/dev/leds-arch.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Header file for platform-specific led functionality + * + * \author + * George Oikonomou - + */ + + +#ifndef __LEDS_ARCH_H__ +#define __LEDS_ARCH_H__ + +#include "dev/port.h" + +/* Led 4 on the SmartRF05EB is multiplexed with Button 1 on P0_1 */ +#define LED4_READ() PORT_READ(LED4_PORT, LED4_PIN) +#define LED4_WRITE(v) PORT_WRITE(LED4_PORT, LED4_PIN, v) +#define LED4_FUNC_GPIO() PORT_FUNC_GPIO(LED4_PORT, LED4_PIN) +#define LED4_DIR_INPUT() PORT_DIR_INPUT(LED4_PORT, LED4_PIN) +#define LED4_DIR_OUTPUT() PORT_DIR_OUTPUT(LED4_PORT, LED4_PIN) + +#endif /* __LEDS_ARCH_H__ */ diff --git a/platform/cc2530dk/dev/slip-arch.c b/platform/cc2530dk/dev/slip-arch.c new file mode 100644 index 000000000..1b0437494 --- /dev/null +++ b/platform/cc2530dk/dev/slip-arch.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * @(#)$Id: slip_uart1.c,v 1.8 2008/02/03 20:59:35 adamdunkels Exp $ + */ + +/* + * Machine dependent cc2530eb SLIP routines for UART1. + */ + +#include "dev/slip.h" +#include "dev/uart0.h" +/*---------------------------------------------------------------------------*/ +void +slip_arch_writeb(unsigned char c) +{ + uart0_writeb(c); +} +/*---------------------------------------------------------------------------*/ +void +slip_arch_init(unsigned long ubr) +{ + uart0_set_input(slip_input_byte); +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/cc2530dk/dev/smartrf-sensors.c b/platform/cc2530dk/dev/smartrf-sensors.c new file mode 100644 index 000000000..c40cf101b --- /dev/null +++ b/platform/cc2530dk/dev/smartrf-sensors.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011, George Oikonomou - + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Data structures for SmartRF05EB sensing elements + * + * \author + * George Oikonomou - + */ + +#include "dev/button-sensor.h" +#include "dev/adc-sensor.h" +#include "sys/energest.h" + +const struct sensors_sensor *sensors[] = { +#if ADC_SENSOR_ON + &adc_sensor, +#endif +#if BUTTON_SENSOR_ON + &button_sensor, +#endif + 0 +}; + +unsigned char sensors_flags[(sizeof(sensors) / sizeof(struct sensors_sensor *))]; diff --git a/platform/cc2530dk/putchar.c b/platform/cc2530dk/putchar.c new file mode 100644 index 000000000..18c4b04a1 --- /dev/null +++ b/platform/cc2530dk/putchar.c @@ -0,0 +1,38 @@ +/** + * \file + * hardware-specific putchar() routine for TI SmartRF05EB + * + * \author + * George Oikonomou - + */ + +#include "contiki-conf.h" +#include "dev/uart0.h" +/*---------------------------------------------------------------------------*/ +void +putchar(char c) +{ +#if SLIP_ARCH_CONF_ENABLE +#define SLIP_END 0300 + static char debug_frame = 0; + + if(!debug_frame) { /* Start of debug output */ + uart0_writeb(SLIP_END); + uart0_writeb('\r'); /* Type debug line == '\r' */ + debug_frame = 1; + } +#endif + + uart0_writeb((char)c); + +#if SLIP_ARCH_CONF_ENABLE + /* + * Line buffered output, a newline marks the end of debug output and + * implicitly flushes debug output. + */ + if(c == '\n') { + uart0_writeb(SLIP_END); + debug_frame = 0; + } +#endif +} diff --git a/platform/cc2530dk/segment.rules b/platform/cc2530dk/segment.rules new file mode 100644 index 000000000..4810b5129 --- /dev/null +++ b/platform/cc2530dk/segment.rules @@ -0,0 +1,13 @@ +# segment.rules - platform + +# segment.rules file for platform code +# Please see cpu/cc2430/segment.rules for more info on code segments +# and for rules of thumb on what to do and what not to do + +# Keep main() in HOME +HOME contiki-main.c + +# Files with ISRs must be in HOME +HOME button-sensor.c + +# segment.rules - platform - end diff --git a/platform/cc2530dk/viztool.c b/platform/cc2530dk/viztool.c new file mode 100644 index 000000000..777ffc855 --- /dev/null +++ b/platform/cc2530dk/viztool.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2010, Loughborough University - Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Small UDP app used to retrieve neighbor cache and routing table + * entries and send them to an external endpoint + * + * \author + * George Oikonomou - + */ + +#include "contiki.h" +#include "contiki-lib.h" +#include "contiki-net.h" + +#include + +#define DEBUG DEBUG_NONE +#include "net/uip-debug.h" + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) + +#ifndef VIZTOOL_MAX_PAYLOAD_LEN +#define VIZTOOL_MAX_PAYLOAD_LEN 60 +#endif + +static struct uip_udp_conn *server_conn; +static unsigned char buf[VIZTOOL_MAX_PAYLOAD_LEN]; +static int8_t len; + +#define VIZTOOL_UDP_PORT 60001 + +/* Request Bits */ +#define REQUEST_TYPE_ND 1 +#define REQUEST_TYPE_RT 2 +#define REQUEST_TYPE_DRT 3 +#define REQUEST_TYPE_ADDR 4 +#define REQUEST_TYPE_TOTALS 0xFF + +extern uip_ds6_netif_t uip_ds6_if; +extern uip_ds6_route_t uip_ds6_routing_table[UIP_DS6_ROUTE_NB]; +extern uip_ds6_nbr_t uip_ds6_nbr_cache[UIP_DS6_NBR_NB]; +extern uip_ds6_defrt_t uip_ds6_defrt_list[UIP_DS6_DEFRT_NB]; +extern u16_t uip_len; +/*---------------------------------------------------------------------------*/ +static uint8_t +process_request() +{ + uint8_t len; + uint8_t count; /* How many did we pack? */ + uint8_t i; + uint8_t left; + uint8_t entry_size; + + left = VIZTOOL_MAX_PAYLOAD_LEN - 1; + len = 2; /* start filling the buffer from position [2] */ + count = 0; + if(buf[0] == REQUEST_TYPE_ND) { + /* Neighbors */ + PRINTF("Neighbors\n"); + for(i = buf[1]; i < UIP_DS6_NBR_NB; i++) { + if(uip_ds6_nbr_cache[i].isused) { + entry_size = sizeof(i) + sizeof(uip_ipaddr_t) + sizeof(uip_lladdr_t) + + sizeof(uip_ds6_nbr_cache[i].state); + PRINTF("%02u: ", i); + PRINT6ADDR(&uip_ds6_nbr_cache[i].ipaddr); + PRINTF(" - "); + PRINTLLADDR(&uip_ds6_nbr_cache[i].lladdr); + PRINTF(" - %u\n", uip_ds6_nbr_cache[i].state); + + memcpy(buf + len, &i, sizeof(i)); + len += sizeof(i); + memcpy(buf + len, &uip_ds6_nbr_cache[i].ipaddr, sizeof(uip_ipaddr_t)); + len += sizeof(uip_ipaddr_t); + memcpy(buf + len, &uip_ds6_nbr_cache[i].lladdr, sizeof(uip_lladdr_t)); + len += sizeof(uip_lladdr_t); + memcpy(buf + len, &uip_ds6_nbr_cache[i].state, + sizeof(uip_ds6_nbr_cache[i].state)); + len += sizeof(uip_ds6_nbr_cache[i].state); + + count++; + left -= entry_size; + + if(left < entry_size) { + break; + } + } + } + } else if(buf[0] == REQUEST_TYPE_RT) { + uint32_t flip = 0; + PRINTF("Routing table\n"); + for(i = buf[1]; i < UIP_DS6_ROUTE_NB; i++) { + if(uip_ds6_routing_table[i].isused) { + entry_size = sizeof(i) + sizeof(uip_ds6_routing_table[i].ipaddr) + + sizeof(uip_ds6_routing_table[i].length) + + sizeof(uip_ds6_routing_table[i].metric) + + sizeof(uip_ds6_routing_table[i].nexthop) + + sizeof(uip_ds6_routing_table[i].state.lifetime) + + sizeof(uip_ds6_routing_table[i].state.learned_from); + + memcpy(buf + len, &i, sizeof(i)); + len += sizeof(i); + memcpy(buf + len, &uip_ds6_routing_table[i].ipaddr, + sizeof(uip_ds6_routing_table[i].ipaddr)); + len += sizeof(uip_ds6_routing_table[i].ipaddr); + memcpy(buf + len, &uip_ds6_routing_table[i].length, + sizeof(uip_ds6_routing_table[i].length)); + len += sizeof(uip_ds6_routing_table[i].length); + memcpy(buf + len, &uip_ds6_routing_table[i].metric, + sizeof(uip_ds6_routing_table[i].metric)); + len += sizeof(uip_ds6_routing_table[i].metric); + memcpy(buf + len, &uip_ds6_routing_table[i].nexthop, + sizeof(uip_ds6_routing_table[i].nexthop)); + len += sizeof(uip_ds6_routing_table[i].nexthop); + + PRINT6ADDR(&uip_ds6_routing_table[i].ipaddr); + PRINTF(" - %02x", uip_ds6_routing_table[i].length); + PRINTF(" - %02x", uip_ds6_routing_table[i].metric); + PRINTF(" - "); + PRINT6ADDR(&uip_ds6_routing_table[i].nexthop); + + flip = uip_htonl(uip_ds6_routing_table[i].state.lifetime); + memcpy(buf + len, &flip, sizeof(flip)); + len += sizeof(flip); + PRINTF(" - %08lx", uip_ds6_routing_table[i].state.lifetime); + + memcpy(buf + len, &uip_ds6_routing_table[i].state.learned_from, + sizeof(uip_ds6_routing_table[i].state.learned_from)); + len += sizeof(uip_ds6_routing_table[i].state.learned_from); + + PRINTF(" - %02x [%u]\n", uip_ds6_routing_table[i].state.learned_from, + entry_size); + + count++; + left -= entry_size; + + if(left < entry_size) { + break; + } + } + } + } else if (buf[0] == REQUEST_TYPE_DRT) { + uint32_t flip = 0; + PRINTF("Default Routes\n"); + for(i = buf[1]; i < UIP_DS6_DEFRT_NB; i++) { + if(uip_ds6_defrt_list[i].isused) { + entry_size = sizeof(i) + sizeof(uip_ds6_defrt_list[i].ipaddr) + + sizeof(uip_ds6_defrt_list[i].isinfinite); + + memcpy(buf + len, &i, sizeof(i)); + len += sizeof(i); + memcpy(buf + len, &uip_ds6_defrt_list[i].ipaddr, + sizeof(uip_ds6_defrt_list[i].ipaddr)); + len += sizeof(uip_ds6_defrt_list[i].ipaddr); + memcpy(buf + len, &uip_ds6_defrt_list[i].isinfinite, + sizeof(uip_ds6_defrt_list[i].isinfinite)); + len += sizeof(uip_ds6_defrt_list[i].isinfinite); + + PRINT6ADDR(&uip_ds6_defrt_list[i].ipaddr); + PRINTF(" - %u\n", uip_ds6_defrt_list[i].isinfinite); + count++; + left -= entry_size; + + if(left < entry_size) { + break; + } + } + } + } else if (buf[0] == REQUEST_TYPE_ADDR) { + PRINTF("Unicast Addresses\n"); + for(i = buf[1]; i < UIP_DS6_ADDR_NB; i++) { + if(uip_ds6_if.addr_list[i].isused) { + entry_size = sizeof(i) + sizeof(uip_ds6_if.addr_list[i].ipaddr); + + memcpy(buf + len, &i, sizeof(i)); + len += sizeof(i); + memcpy(buf + len, &uip_ds6_if.addr_list[i].ipaddr, + sizeof(uip_ds6_if.addr_list[i].ipaddr)); + len += sizeof(uip_ds6_if.addr_list[i].ipaddr); + + PRINT6ADDR(&uip_ds6_if.addr_list[i].ipaddr); + PRINTF("\n"); + count++; + left -= entry_size; + + if(left < entry_size) { + break; + } + } + } + } else if (buf[0] == REQUEST_TYPE_TOTALS) { + memset(&buf[2], 0, 4); + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + if(uip_ds6_if.addr_list[i].isused) { + buf[2]++; + } + } + for(i = 0; i < UIP_DS6_NBR_NB; i++) { + if(uip_ds6_nbr_cache[i].isused) { + buf[3]++; + } + } + for(i = 0; i < UIP_DS6_ROUTE_NB; i++) { + if(uip_ds6_routing_table[i].isused) { + buf[4]++; + } + } + for(i = 0; i < UIP_DS6_DEFRT_NB; i++) { + if(uip_ds6_defrt_list[i].isused) { + buf[5]++; + } + } + len += 4; + count = 4; + } else { + return 0; + } + buf[1] = count; + return len; +} +/*---------------------------------------------------------------------------*/ +PROCESS(viztool_process, "Network Visualization Tool Process"); +/*---------------------------------------------------------------------------*/ +static void +tcpip_handler(void) +{ + if(uip_newdata()) { + memset(buf, 0, VIZTOOL_MAX_PAYLOAD_LEN); + + PRINTF("%u bytes from [", uip_datalen()); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("]:%u\n", UIP_HTONS(UIP_UDP_BUF->srcport)); + + memcpy(buf, uip_appdata, uip_datalen()); + + len = process_request(); + if(len) { + server_conn->rport = UIP_UDP_BUF->srcport; + uip_ipaddr_copy(&server_conn->ripaddr, &UIP_IP_BUF->srcipaddr); + uip_udp_packet_send(server_conn, buf, len); + PRINTF("Sent %u bytes\n", len); + } + + /* Restore server connection to allow data from any node */ + uip_create_unspecified(&server_conn->ripaddr); + server_conn->rport = 0; + } + return; +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(viztool_process, ev, data) +{ + + PROCESS_BEGIN(); + + server_conn = udp_new(NULL, UIP_HTONS(0), NULL); + udp_bind(server_conn, UIP_HTONS(VIZTOOL_UDP_PORT)); + + while(1) { + PROCESS_YIELD(); + if(ev == tcpip_event) { + tcpip_handler(); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ From 80002e8fd10b5fb6a4a321b3b5be410fd39f35d2 Mon Sep 17 00:00:00 2001 From: George Oikonomou Date: Fri, 23 Mar 2012 16:48:09 +0000 Subject: [PATCH 13/14] Fixed the stub-rdc driver used by various cc2x30 examples --- examples/cc2530dk/sniffer/stub-rdc.c | 9 +++++++++ examples/sensinode/energy-scan/stub-rdc.c | 9 +++++++++ examples/sensinode/sniffer/stub-rdc.c | 9 +++++++++ 3 files changed, 27 insertions(+) diff --git a/examples/cc2530dk/sniffer/stub-rdc.c b/examples/cc2530dk/sniffer/stub-rdc.c index 603552c97..063a43659 100644 --- a/examples/cc2530dk/sniffer/stub-rdc.c +++ b/examples/cc2530dk/sniffer/stub-rdc.c @@ -52,6 +52,14 @@ send(mac_callback_t sent, void *ptr) } /*---------------------------------------------------------------------------*/ static void +send_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *list) +{ + if(sent) { + sent(ptr, MAC_TX_OK, 1); + } +} +/*---------------------------------------------------------------------------*/ +static void input(void) { } @@ -83,6 +91,7 @@ const struct rdc_driver stub_rdc_driver = { "stub-rdc", init, send, + send_list, input, on, off, diff --git a/examples/sensinode/energy-scan/stub-rdc.c b/examples/sensinode/energy-scan/stub-rdc.c index 605153d1b..c131d953d 100644 --- a/examples/sensinode/energy-scan/stub-rdc.c +++ b/examples/sensinode/energy-scan/stub-rdc.c @@ -52,6 +52,14 @@ send(mac_callback_t sent, void *ptr) } /*---------------------------------------------------------------------------*/ static void +send_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *list) +{ + if(sent) { + sent(ptr, MAC_TX_OK, 1); + } +} +/*---------------------------------------------------------------------------*/ +static void input(void) { } @@ -83,6 +91,7 @@ const struct rdc_driver stub_rdc_driver = { "stub-rdc", init, send, + send_list, input, on, off, diff --git a/examples/sensinode/sniffer/stub-rdc.c b/examples/sensinode/sniffer/stub-rdc.c index 603552c97..063a43659 100644 --- a/examples/sensinode/sniffer/stub-rdc.c +++ b/examples/sensinode/sniffer/stub-rdc.c @@ -52,6 +52,14 @@ send(mac_callback_t sent, void *ptr) } /*---------------------------------------------------------------------------*/ static void +send_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *list) +{ + if(sent) { + sent(ptr, MAC_TX_OK, 1); + } +} +/*---------------------------------------------------------------------------*/ +static void input(void) { } @@ -83,6 +91,7 @@ const struct rdc_driver stub_rdc_driver = { "stub-rdc", init, send, + send_list, input, on, off, From 2755e261bf32d6e5bb4657fd1847475ff6540057 Mon Sep 17 00:00:00 2001 From: George Oikonomou Date: Fri, 23 Mar 2012 16:49:49 +0000 Subject: [PATCH 14/14] Turn off printf for sensinode examples which will otherwise not fit our flash --- examples/sensinode/border-router/border-router.c | 2 +- examples/sensinode/udp-ipv6/server.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/sensinode/border-router/border-router.c b/examples/sensinode/border-router/border-router.c index c9a616aac..a59ce6e0b 100644 --- a/examples/sensinode/border-router/border-router.c +++ b/examples/sensinode/border-router/border-router.c @@ -33,7 +33,7 @@ #include -#define DEBUG DEBUG_PRINT +#define DEBUG DEBUG_NONE #include "net/uip-debug.h" #include "net/rpl/rpl.h" #include "dev/watchdog.h" diff --git a/examples/sensinode/udp-ipv6/server.c b/examples/sensinode/udp-ipv6/server.c index 24e490ff3..f3ab93202 100644 --- a/examples/sensinode/udp-ipv6/server.c +++ b/examples/sensinode/udp-ipv6/server.c @@ -33,7 +33,7 @@ #include -#define DEBUG DEBUG_PRINT +#define DEBUG DEBUG_NONE #include "net/uip-debug.h" #include "dev/watchdog.h" #include "dev/leds.h"