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;$Nz4+f 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
Ui!HwV>y48&kg>z$I~@LC#wsdeCA`f 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℞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 RWMcdm#lY};3o8Qy0{|r--P`~G literal 0 HcmV?d00001 From 088f2e12a8e379e3bfa56ab8f97195a5f67deb1a Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind 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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 R@CPvlhSMMh6oceJOaL+5k*)v$ 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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 IdJ;qf0E>)bHUIzs 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 zcmb<-^>JflWMqH=Mh0dE1doB?3=>4g1Wai#a5Hd#Sui5B^J#ckaPu3TZdZ}+Ql8Xq zQ4t0PhR)CrhL^f)Il4n7I$iH{y9#ucs0bW)QDFgT>kLuh=yXxx>9kRK@#p{l|NPrx zCAwV&zTZChf7v5X>-qwuQ5>X^1!6VG z&=7<^g#Hhy#$b<>ax@>&iH?hpJq)%dZ34o-r98c^AG%`&Kt^}FavXR40QOvoia=+K z3J=I8c_f>JAlQJl??~+<^TWx-L4;A zCjS5bpTSDiNY5xj)kM!E0?cASB6+z$(iN&E6{npaW^;prNdf_WyI3=De3mANH}Nep_$B}EWA1I8*!&Cx4Kttes8OGzwAWY9}0 zE@sdx$_H^kstxrF8T69#b8}PkN*MI=@=H?n-2Fmzi%Sxdvq8a66b8i$D2=m0RXZ>+ zFx&> z#MWkD0OuY;>I|Uf8G$rG&C`RjK@`Yt5VnV^1BrpK7ZU>mD2hRB5C*9M(H>~(O3>T~ zQwPdYFt>s9fz(BS7*GsT2cnUUXklSs0407dsQ*Cz1%)9f>`=^OWnc(^ngyaj=74B) zj4aN=@bN#gJZk9#3Ug!|Kzxwx2p!A}tOyP`jzBpIA;iqU296Vu$U6oG1|bGU22cqG z;)5{AA2J{Y6odH6P&SAH@sYzE#J6UEgvV>Bd7dDJPz;i1$jnHs&`V~>NUccA%t?*U zFD+pxE-1>(D@kK0E-6Y*ECEqDsd)_X@!*slpPZ2$pO%=J1J_cJSX7+KP+Xjxn3u+& z4~kz_1_n@iVB%z8sD$#H7#J8p{D~l%i-BPiRD2hR&&j}W5=3(`FkA%D+zbq_Kr{~n ggA5}B10yQ~g9;-90}~$uLkW~$2j#DY^0z|y0OUA5b^rhX diff --git a/cpu/cc2430/dev/adc.c b/cpu/cc2430/dev/adc.c deleted file mode 100644 index eda932a2a..000000000 --- a/cpu/cc2430/dev/adc.c +++ /dev/null @@ -1,70 +0,0 @@ -/** - * \file - * ADC functions - * \author - * Anthony "Asterisk" Ambuehl - * - * ADC initialization routine, trigger and result conversion routines. - * - */ - -#include -#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"