System Management code
Dependencies: mbed CANBuffer Watchdog MODSERIAL mbed-rtos xbeeRelay IAP
Fork of SystemManagement by
Revision 38:8efacce315ae, committed 2015-02-07
- Comitter:
- pspatel321
- Date:
- Sat Feb 07 08:54:51 2015 +0000
- Parent:
- 37:2207b58b9a7f
- Child:
- 39:ddf38df9699e
- Commit message:
- Updated with profiles, operating info, etc. Just like the other programs. Awaiting test in car.
Changed in this revision
--- a/Constants.h Thu Jan 22 07:59:48 2015 +0000 +++ b/Constants.h Sat Feb 07 08:54:51 2015 +0000 @@ -1,10 +1,14 @@ #ifndef CONSTANTS_H #define CONSTATNS_H -#define BAUD 230400 // Serial port baud rate +#define NUM_STORED_PROFILES 3 +#define CAN_DEVICE_TIMEOUT 0.2 +#define SERIAL_BAUD 230400 // Serial port baud rate #define CHAR_TIME 0.00005 // Time to send 1 char @ above baud #define TX_SIZE 1000 // Serial buffer TX size #define RX_SIZE 60 // Serial buffer RX size +#define CAN_TX_SIZE 128 +#define CAN_RX_SIZE 64 #define XBEE_BAUD 250000 // 250k baud serial for xbees #define XBEE_TX_SIZE 1000 // Serial buffer TX size for xbees
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/DataStructures.cpp Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,277 @@
+#include "DataStructures.h"
+#include "DefaultProfile.h"
+#include "IAP.h"
+
+#define HEADER_SECTOR 27 // Where the NV_Data header information is stored
+#define HEADER_PTR (NV_Data*)((uint32_t*)(sector_start_adress[HEADER_SECTOR]))
+#define ID_MARKER 0x5a5a5a5a // Marker that identifies whether the flash was erased from programming a new .bin file
+#define FRAME_SECTOR 28 // First sector for freeze frame storage (swapspace)
+#define FRAME_PTR (FreezeFrame*)((uint32_t*)(sector_start_adress[FRAME_SECTOR]))
+#define PROFILE_SECTOR 29 // The flash sector where profiles are stored, 29 is the last 32kB size sector in memory
+#define PROFILE_START_PTR (Profile_checkSum*)((uint32_t*)(sector_start_adress[PROFILE_SECTOR])) // Pointer to the first profile object in flash
+
+// OPERATING DATA OBJECTS
+FreezeFrame frame; // Current working object in RAM, contains both param and freeze frame
+OperatingInfo *op = &(frame.op.op); // Handle to just the op section
+Profile *param = &(frame.param.param); // Handle to just the param section
+TemporaryData tempData; // Temporary data object in RAM
+
+IAP iap; // Access to In-Application-Programming --> flash memory programming routines
+Profile_checkSum copyBuffer[NUM_STORED_PROFILES]; // Scratch-space in RAM for saving profiles, since flash writes must erase the entire sector and only RAM is usable during flash write
+
+// Non-volatile data holder class
+class NV_Data
+{
+public:
+ NV_Data() {
+ id = ID_MARKER;
+ frameStoredFlag = 0;
+ usingProfile = 0;
+ }
+ int frameStoredFlag;
+ int usingProfile;
+
+ int id;
+};
+
+// Construct new freeze frames with empty OperatingInfo and default Profile
+FreezeFrame::FreezeFrame()
+{
+ memcpy(&(this->param), &defaultProfile, sizeof(Profile));
+ memset(&(this->op), 0, sizeof(OperatingInfo));
+}
+
+// Helper function to compute checksums using the BSD checksum algorithim for checking validity of flash contents
+// Arguments: start location of the block, size in bytes Returns: checksum
+uint32_t checksum(const void* ptr, unsigned int size)
+{
+ const char* start = (char*)(ptr);
+ uint32_t checksum = 0;
+ for (uint32_t i = 0; i < size; i++) { // Skip last 4 bytes which holds the checksum itsel
+ // BSD checksum algorithm
+ checksum = (checksum >> 1) + ((checksum & 1) << 15);
+ checksum += start[i]; // Accumulate
+ checksum &= 0xffff; // Time to 16 bits
+ }
+ return checksum;
+}
+
+// Helper function to streamline flash memory prep-erase-prep-write
+// Takes start pointer (RAM), size of object to copy, start pointer (flash), and sector number
+int flashWrite(void* start, int size, void* flash_start, int sector)
+{
+ for (int i = 256; i <= 8192; i *= 2) { // Count by powers of 2
+ if (size <= i) { // Stop when oversize or equal
+ size = i;
+ break;
+ }
+ }
+ if (size > 4096) return -1; // Chunk too big to copy
+ if (size == 2048) size = 4096; // 2048 does not exist, next size up
+
+ __disable_irq(); // Flash operations are non-interruptable
+
+ // Flash prepare, erase, prepare, write - (r == 0) means successful, non-zero = error code
+ int r = iap.prepare(sector, sector);
+ if (r == 0) {
+ r = iap.erase(sector, sector);
+ if (r == 0) {
+ r = iap.prepare(sector, sector);
+ if (r == 0) {
+ r = iap.write((char*)start, (char*)flash_start, size);
+ }
+ }
+ }
+ __enable_irq(); // Re-enable interrupts
+ return r; // 0 if success, otherwise see fault codes in LPC1768 User Manual
+}
+bool setProfile(int index)
+{
+ bool passed = false;
+ if (index == 0) passed = true;
+ else if (index == -1) passed = true;
+ else if (index <= NUM_STORED_PROFILES && index > 0) passed = true;
+
+ if (!passed) return false;
+ NV_Data temp;
+ NV_Data* header = HEADER_PTR;
+
+ __disable_irq();
+ memcpy(&temp, header, sizeof(NV_Data)); // Atomic copy to RAM
+ __enable_irq();
+
+ temp.usingProfile = index;
+
+ // Write it back to flash
+ int i = flashWrite(&temp, sizeof(NV_Data), sector_start_adress[HEADER_SECTOR], HEADER_SECTOR);
+ return (i == 0);
+}
+
+// Retrieve the index number of the currently active profile (last profile loaded/saved)
+int Profile::usingProfile()
+{
+ NV_Data* header = HEADER_PTR;
+ return header->usingProfile;
+}
+
+// Startup procedure run on chip reset to fetch last used profile
+bool Profile::loadStartUp()
+{
+ // Check health of the header
+ NV_Data *header = HEADER_PTR;
+ bool passed=true;
+ if (header->id != ID_MARKER) passed = false;
+ if (header->usingProfile > NUM_STORED_PROFILES || header->usingProfile < -1) passed = false;
+ if (header->frameStoredFlag != 0 && header->frameStoredFlag != ~0) passed = false;
+
+ // Write a new, blank header
+ if (!passed) {
+ NV_Data temp;
+ int i = flashWrite(&temp, sizeof(NV_Data), sector_start_adress[HEADER_SECTOR], HEADER_SECTOR);
+ }
+
+ if (loadProfile(usingProfile())) { // Try to fetch the profile that is currently marked
+ return true;
+ } else { // If fail, revert to default
+ loadProfile(0);
+ return false;
+ }
+}
+
+// Attempt to copy profile from flash to RAM object as indicated by int index
+bool Profile::loadProfile(int index)
+{
+ Profile *p;
+ if (!getProfile(&p, index)) return false;
+
+ __disable_irq();
+ memcpy(param, p, sizeof(Profile));
+ __enable_irq();
+
+ if (setProfile(index)) return true;
+ return false; // Bad sum, no copy was done
+}
+
+// Attempt to fetch the profile pointer only, no copy to RAM
+bool Profile::getProfile(Profile **ptr, int index)
+{
+ Profile_checkSum *p;
+
+ // If default profile requested
+ if (index == 0) {
+ *ptr = &defaultProfile; // Set pointer to default, return
+ return true;
+
+ // If freeze profile requested
+ } else if (index == -1) {
+ FreezeFrame *freeze;
+ if (!FreezeFrame::getFrame(&freeze)) { // Attempt to fetch frame
+ *ptr = NULL; // Not available, set NULL
+ return false;
+ }
+ p = &(freeze->param);
+
+ // Profile from flash storage requested
+ } else if (index <= NUM_STORED_PROFILES && index > 0) {
+ p = PROFILE_START_PTR + (index - 1); // Set a pointer to the profile
+
+ // Bad command, invalid index
+ } else {
+ *ptr = NULL;
+ return false;
+ }
+ // Validate contents by computing checksum
+ if (p->BSDchecksum == checksum(&(p->param), sizeof(Profile))) { // Check checksum
+ *ptr = &(p->param); // Fill out the pointer, checksum passed
+ return true;
+ }
+ *ptr = NULL;
+ return false; // Bad checksum, ptr set to NULL
+}
+// Take the current RAM object contents and save it to flash at location given by index
+// Flash write requires the sector to be erased first, so the function will copy out and re-wrtie the profiles
+bool Profile::saveProfile(int index)
+{
+ if (index > NUM_STORED_PROFILES || index <= 0) return false; // Index invalid
+ frame.param.BSDchecksum = checksum(param, sizeof(Profile)); // Add the checksum
+
+ // Copy out all of the old profiles
+ for (int i = 0; i < NUM_STORED_PROFILES; i++) {
+ if (i == (index - 1)) { // Exception, replace this slot with the RAM profile
+ __disable_irq();
+ memcpy(©Buffer[i], &(frame.param), sizeof(Profile_checkSum));
+ __enable_irq();
+ continue; // Skip to next
+ }
+ __disable_irq();
+ memcpy(©Buffer[i], PROFILE_START_PTR+i, sizeof(Profile_checkSum)); // Copy profile from flash to RAM
+ __enable_irq();
+ }
+ int r = flashWrite(copyBuffer, sizeof(copyBuffer), sector_start_adress[PROFILE_SECTOR], PROFILE_SECTOR);
+
+ if (r == 0) {
+ return setProfile(index);
+ }
+ return false;
+}
+
+// Fetch pointer to the last written freeze frame object if available, do not copy contents to RAM
+bool FreezeFrame::getFrame(FreezeFrame **frame)
+{
+ if (FreezeFrame::getError() == false) {
+ *frame = NULL; // No frame available, return
+ return false;
+ }
+
+ FreezeFrame* ptr = FRAME_PTR; // Set pointer to frame
+ uint32_t checksumOp = checksum(&(ptr->op.op), sizeof(OperatingInfo)); // Compute checksum for operatingInfo section
+ uint32_t checksumParam = checksum(&(ptr->param.param), sizeof(Profile)); // Compute checksum for profile section
+
+ if (checksumOp == ptr->op.BSDchecksum && checksumParam == ptr->param.BSDchecksum) { // Compare to stored checksum
+ *frame = ptr; // Checksum passed, pass out the ptr
+ return true;
+ }
+ *frame = NULL; // Checksum failed, write NULL
+ return false;
+}
+
+// Copy contents of RAM to flash, usually called on error to record the internal state upon enetering fault conditions
+bool FreezeFrame::writeFrame()
+{
+ // Compute and record the checksums
+ frame.param.BSDchecksum = checksum(&(frame.param.param), sizeof(Profile));
+ frame.op.BSDchecksum = checksum(&(frame.op.op), sizeof(OperatingInfo));
+
+ int r = flashWrite(&frame, sizeof(FreezeFrame), sector_start_adress[FRAME_SECTOR], FRAME_SECTOR);
+ if (r == 0) {
+ // Set the error flag in the header
+ NV_Data temp;
+ NV_Data* header = HEADER_PTR;
+ __disable_irq();
+ memcpy(&temp, header, sizeof(NV_Data));
+ __enable_irq();
+ temp.frameStoredFlag = ~0;
+ int i = flashWrite(&temp, sizeof(NV_Data), sector_start_adress[HEADER_SECTOR], HEADER_SECTOR);
+ return (i == 0);
+ }
+ return false; // False if flash operations failed
+}
+
+// Get the non-volatile freeze Frame recorded flag that indicates whether a freeze frame is available
+bool FreezeFrame::getError()
+{
+ NV_Data* header = HEADER_PTR;
+ return header->frameStoredFlag;
+}
+// Clear the frame error, for ex. when the frame is read by user
+bool FreezeFrame::clearError()
+{
+ NV_Data temp;
+ NV_Data* header = HEADER_PTR;
+ __disable_irq();
+ memcpy(&temp, header, sizeof(NV_Data));
+ __enable_irq();
+ temp.frameStoredFlag = 0;
+ int i = flashWrite(&temp, sizeof(NV_Data), sector_start_adress[HEADER_SECTOR], HEADER_SECTOR);
+ return (i == 0);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DataStructures/DataStructures.h Sat Feb 07 08:54:51 2015 +0000 @@ -0,0 +1,13 @@ +#ifndef DATA_STRUCTURES_H +#define DATA_STRUCTURES_H + +#include "FreezeFrame.h" +#include "TemporaryData.h" + +extern FreezeFrame frame; // Allow global access to the current RAM frame as well +extern Profile *param; // Allow global access to the current RAM profile inside of FreezeFrame frame +extern OperatingInfo *op; // Allow global access to the current RAM operating info inside of FreezeFrame frame + +extern TemporaryData tempData; // Allow global access to the RAM temporary data object for stuff that does not belong in a profile nor is it relevant to operating info in a freeze frame + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/Headers/DefaultProfile.h Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,35 @@
+#ifndef DEFAULT_PROFILE_H
+#define DEFAULT_PROFILE_H
+
+#include "Profile.h"
+
+// The default flash configuration object, used as backup/failsafe in case other profiles are corrupted
+Profile defaultProfile = {
+ -2, // GLV charging current limit
+ 5, // GLV discharging current limit
+ 1.4, // GLV battery nominal capacity (Ah)
+ 50, // GLV battery current filter taps
+
+ 1.0, // DC-DC turn-on threshold current
+ 20, // DC-DC over current limit
+ 1, // DC-DC start-up delay
+ 1, // DC-DC stop delay
+ 50, // DC-DC current filter taps
+
+ 10, // IMD Latch soft-delay on startup
+ 10, // AMS Latch soft-delay on startup
+ 60, // Interal over-temp
+
+ true, // CAN noack
+ true, // Extended serial display
+
+ CAN_TX_SIZE, // CAN tx size
+ CAN_RX_SIZE, // CAN rx size
+ SERIAL_BAUD, // Serial port baudrate
+ TX_SIZE, // Serial tx buffer size
+ XBEE_BAUD, // Xbee baudrate
+ XBEE_TX_SIZE, // Xbee tx buffer size
+ XBEE_RX_SIZE, // Xbee rx buffer size
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/Headers/FreezeFrame.h Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,24 @@
+#ifndef FREEZE_FRAME_H
+#define FREEZE_FRAME_H
+
+#include "OperatingInfo.h"
+#include "Profile.h"
+
+// Freeze frame class, containing everything that describes the current operation of the AMS
+class FreezeFrame
+{
+public:
+ FreezeFrame();
+ Profile_checkSum param; // Profile - contains parameters and configurable settings
+ OperatingInfo_checkSum op; // OperatingInfo - contains all the data gathered from sensors and processed outputs
+
+ static bool getFrame(FreezeFrame **frame); // Fetch pointer to the last stored freeze frame if available
+ static bool writeFrame(); // Write the current RAM frame to flash as a freeze frame
+ static bool prepare(); // Call on microcontroller startup, pre-erase a flash sector so flash write occurs very fast upon error
+
+ static bool getError(); // Get last marked error state
+ static bool setError(); // Mark error
+ static bool clearError(); // Reset (clear) the freeze frame error flag
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/Headers/OperatingInfo.h Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,102 @@
+#ifndef OPERATING_INFO_H
+#define OPERATING_INFO_H
+
+#include "Constants.h"
+#include <stdint.h>
+#include <time.h>
+
+enum OperatingMode {
+ // MODES
+ FAULT=0,
+ OKAY=1,
+};
+
+// Items here will prevent moving from STANDBY to IDLE
+enum TopLevelFaults {
+ WATCHDOG = 1<<0, // Last reset caused by WDT
+ BROWNOUT = 1<<1, // Last reset caused by Brownout
+ CAN_FAULT = 1<<2, // CAN buffer overflow
+
+ INT_OVER_TEMP = 1<<3, // Temp. sensor on Charge FET is over limit
+ IMD_LATCH = 1<<4, // Fault detected on the IMD latch circuit
+ AMS_LATCH = 1<<5, // Fault detected on the AMS latch circuit
+ IMD_FAULT = 1<<6, // IMD not okay or not running
+ DCDC_FAULT = 1<<7,
+ GLVBAT_FAULT = 1<<8,
+ FREEZE_FRAME = 1<<9, // There is an error freeze frame captured, waiting to be inspected by user
+};
+
+enum SignalFlags {
+ SHUTDOWN_CLOSED = 1<<0, // Shutdown circuit ready (closed)
+ AIRS_CLOSED = 1<<1, // AIRs Closed (CAN)
+ CHARGER_DET = 1<<2, // Charger detected (CAN)
+};
+
+// Contains all of the relevant operating data gathered from sensors and the prcoessed outputs
+// NOTE there are no mutexes/protection mechanisms here! Ensure single producer, single consumer for every item
+class OperatingInfo
+{
+public:
+ // Diagnostic data
+ char mode;
+ char signals;
+ uint16_t faultCode;
+ time_t SysTime;
+ time_t startTime;
+ signed char profileIndex;
+ bool profileModded;
+
+ struct { // GLV-Battery related
+ float current;
+ float SOC;
+ float Ah;
+ float capacity;
+ char error;
+ } glvBat;
+
+ struct { // DC-DC converter and PWM channels
+ char status;
+ float current;
+ struct {
+ float pump1;
+ float pump2;
+ float fan1;
+ float fan2;
+ } request;
+ struct {
+ float pump1;
+ float pump2;
+ float fan1;
+ float fan2;
+ } actual;
+ } dcdc;
+
+ struct { // IMD
+ char status;
+ float resistance;
+ char error;
+ } imd;
+
+ struct { // Latch circuit supervisor
+ char imd;
+ char ams;
+ } latch;
+
+ struct MsgCounter { // Telemetry tracking
+ unsigned int msgIn;
+ unsigned int msgOut;
+ float rateOut;
+ };
+ struct MsgCounter xbee1;
+ struct MsgCounter xbee2;
+
+ char switchState;
+ float internalTemp;
+};
+class OperatingInfo_checkSum
+{
+public:
+ OperatingInfo op;
+ uint32_t BSDchecksum;
+};
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/Headers/Profile.h Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,88 @@
+#ifndef _Profile_H
+#define _Profile_H
+
+// The defines below are used to create change functions
+// The change functions allow changing things in the Profile with limits on allowed values
+#define CHANGE_FUNC(NAME, LLIM, ULIM) \
+template<class T> \
+bool change_##NAME(T data) { \
+ if (data > ULIM || data < LLIM) return false; \
+ if (data == NAME) return false; \
+ NAME = data; \
+ return true; \
+}
+
+// Stores configurable parameters like limits, constants, etc.
+// NOTE there are no mutexes/protection mechanisms! Ensure single producer (inCommands.cpp - main thread), single consumer
+// To change a member of class Profile, use the change functions defined. They will enforce limits on the changes.
+class Profile
+{
+public:
+ // GLV Battery
+ float chargeCurrent;
+ float dischargeCurrent;
+ float nominalCapacity;
+ unsigned int glvBat_taps;
+ CHANGE_FUNC(chargeCurrent, -10, 0)
+ CHANGE_FUNC(dischargeCurrent, 0, 10)
+ CHANGE_FUNC(nominalCapacity, 0, 10)
+ CHANGE_FUNC(glvBat_taps, 1, 1000)
+
+ // DC-DC converter
+ float dcdcThreshold;
+ float dcdcOverCurrent;
+ float dcdcStartDelay;
+ float dcdcStopDelay;
+ unsigned int dcdc_taps;
+ CHANGE_FUNC(dcdcThreshold, 0, 10)
+ CHANGE_FUNC(dcdcOverCurrent, 0, 100)
+ CHANGE_FUNC(dcdcStartDelay, 0, 10)
+ CHANGE_FUNC(dcdcStopDelay, 0, 10)
+ CHANGE_FUNC(dcdc_taps, 1, 1000)
+
+ // Latch Circuits
+ float imdStartDelay;
+ float amsStartDelay;
+ CHANGE_FUNC(imdStartDelay, 0, 100)
+ CHANGE_FUNC(amsStartDelay, 0, 100)
+
+ // Over-temp
+ float internalOverTemp;
+ CHANGE_FUNC(internalOverTemp, 0, 100)
+
+ // Boolean states / modes
+ bool CANnoAck; // Is noAck mode on?
+ bool extendedSerial;
+ CHANGE_FUNC(CANnoAck, 0, 1)
+ CHANGE_FUNC(extendedSerial, 0, 1)
+
+ // Buffer sizes
+ unsigned int CANtxSize; // Size of CAN TX buffer
+ unsigned int CANrxSize; // Size of CAN RX buffer
+ unsigned int SerialBaud; // Serial port baudrate
+ unsigned int SerialTxSize; // Serial TX buffer size
+ unsigned int XbeeBaud; // Serial port baudrate
+ unsigned int XbeeTxSize; // Serial TX buffer size
+ unsigned int XbeeRxSize; // Serial TX buffer size
+ CHANGE_FUNC(CANtxSize, 1, 1000)
+ CHANGE_FUNC(CANrxSize, 1, 1000)
+ CHANGE_FUNC(SerialBaud, 9600, 921600)
+ CHANGE_FUNC(SerialTxSize, 1, 10000)
+ CHANGE_FUNC(XbeeBaud, 9600, 921600)
+ CHANGE_FUNC(XbeeTxSize, 1, 10000)
+ CHANGE_FUNC(XbeeRxSize, 1, 10000)
+
+ // Load / store / fetch functions
+ static bool loadProfile(int index); // Load profile from flash into the current RAM object, 0=default, -1=freeze frame
+ static bool saveProfile(int index); // Save the current RAM profile into flash to index, 0=default
+ static bool getProfile(Profile **ptr, int index); // Retrieve the handle to a profile
+ static bool loadStartUp(); // Load the profile on startup
+ static int usingProfile(); // Return the last profile loaded into RAM, 0=default, -1=freeze frame
+};
+class Profile_checkSum
+{
+public:
+ Profile param;
+ uint32_t BSDchecksum;
+};
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DataStructures/Headers/TemporaryData.h Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,29 @@
+#ifndef TEMPORARY_DATA_H
+#define TEMPORARY_DATA_H
+
+#include "FreezeFrame.h"
+#include <string.h>
+
+// For temporary data that changes frequently in operation for inter-task communication.
+// NOTE there are no mutexes/protection mechanisms. Ensure single producer, single consumer.
+class TemporaryData
+{
+public:
+ TemporaryData() {
+ memset(this, 0, sizeof(TemporaryData));
+ parseGoodChar = ' '; // Start with a blank after the "Command Input:" line
+ viewProfileNum = -2; // -2 = display live RAM profile
+ }
+ char inputStr[RX_SIZE+1]; // Stores the user-typed command input string for serial interface
+ char parseGoodChar; // Character to display for string parsed and successful operation
+
+ Profile* viewProfile; // Used by "profile view xxx" command, switch the profile display to this profile if not NULL, otherwise view the current RAM frame
+ int viewProfileNum; // Index of the profile being viewed
+
+ FreezeFrame* freeze; // Used by "view freeze" command, display this freezeFrame over serial if not NULL, otherwise view the current RAM frame
+
+ bool timeSynced; // Used to check whether or not RTC time has been synchronized with the AMS (master clock source)
+ void* wdtThreadId; // ID to access the watchdog check-in thread
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DataStructures/IAP.lib Sat Feb 07 08:54:51 2015 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/okano/code/IAP/#cee1a2a734c9
--- a/IOobjects/CAN_RxIDs.h Thu Jan 22 07:59:48 2015 +0000 +++ b/IOobjects/CAN_RxIDs.h Sat Feb 07 08:54:51 2015 +0000 @@ -1,18 +1,49 @@ #ifndef CAN_RXIDS_H #define CAN_RXIDS_H -#define BASE_ID 0x500 // Start at 0x500 for this device +#define RX_ID_BASE 0x580 // Start at 0x500 for this device -// Receive IDs -#define FAN_CONTROL_ID BASE_ID + 0x80 -#define PUMP_CONTROL_ID BASE_ID + 0x81 -#define DCDC_CONTROL_ID BASE_ID + 0x82 +// Receive Command IDs +// NOTE after each command is parsed, a message will return back with the same ID of length 1 with data[0]=1 for success and data[0]=0 for fail +#define RESET_RX_ID RX_ID_BASE + 0x01 // Reset over CAN +#define RESETCLEAR_RX_ID RX_ID_BASE + 0x02 // Clear all errors and then reset, clean slate startup +#define CAPTURE_RX_ID RX_ID_BASE + 0x03 // Artificially capture freeze frame +#define CLEAR_RX_ID RX_ID_BASE + 0x04 // Clear a freeze frame error (0), clear every fault (1) +#define TIME_RX_ID RX_ID_BASE + 0x05 // Change the time (MM DD YY HH MM SS - 6 bytes) +#define PROFILE_RX_ID RX_ID_BASE + 0x06 // Byte[0] = load/write Byte[1] = profile # +#define SOC_RX_ID RX_ID_BASE + 0x07 // Change the current SOC to some float from 0 to 1 +#define AH_RX_ID RX_ID_BASE + 0x08 // Change the current ampHours to some float from 0 to capacity + +#define FAN_CONTROL_ID RX_ID_BASE + 0x10 +#define PUMP_CONTROL_ID RX_ID_BASE + 0x11 -#define GLVBAT_SETSOC_ID BASE_ID + 0x90 -#define GLVBAT_SETAH_ID BASE_ID + 0x91 -#define GLVBAT_SETCAPAC_ID BASE_ID + 0x92 +// Profile Data Change Requests +#define PROFILE_CHARGECURRENT_RX_ID RX_ID_BASE + 0x20 +#define PROFILE_DISCHARGECURRENT_RX_ID RX_ID_BASE + 0x21 +#define PROFILE_NOMINALCAPACITY_RX_ID RX_ID_BASE + 0x22 +#define PROFILE_GLVBATTAPS_RX_ID RX_ID_BASE + 0x23 +#define PROFILE_DCDCONTHRESHOLD_RX_ID RX_ID_BASE + 0x24 +#define PROFILE_DCDCOVERCURRENT_RX_ID RX_ID_BASE + 0x25 +#define PROFILE_DCDCSTARTDELAY_RX_ID RX_ID_BASE + 0x26 +#define PROFILE_DCDCSTOPDELAY_RX_ID RX_ID_BASE + 0x27 +#define PROFILE_DCDC_TAPS_RX_ID RX_ID_BASE + 0x28 +#define PROFILE_IMDSTARTDELAY_RX_ID RX_ID_BASE + 0x29 +#define PROFILE_INTERNALOVERTEMP_RX_ID RX_ID_BASE + 0x2A +#define PROFILE_CANNOACK_RX_ID RX_ID_BASE + 0x2B +#define PROFILE_EXTENDSERIAL_RX_ID RX_ID_BASE + 0x2C +#define PROFILE_CANTXSIZE_RX_ID RX_ID_BASE + 0x2D +#define PROFILE_CANRXSIZE_RX_ID RX_ID_BASE + 0x2E +#define PROFILE_SERIALBAUD_RX_ID RX_ID_BASE + 0x2F +#define PROFILE_SERIALTXSIZE_RX_ID RX_ID_BASE + 0x30 +#define PROFILE_XBEEBAUD_RX_ID RX_ID_BASE + 0x31 +#define PROFILE_XBEETXSIZE_RX_ID RX_ID_BASE + 0x32 +#define PROFILE_XBEERXSIZE_RX_ID RX_ID_BASE + 0x33 -#define AMS_MODE_ID 0x301 -#define STEERING_RESET_ID 0x602 +#define RX_ID_END 0x5FF // End of SYS MGMT receive block + +// Messages from other devices in the car +#define CHARGER_ERR_ID 0x700 // Is the charger connected? +#define AMS_MODE_ID 0x301 // Contains AIRs data +#define GLOBAL_CAR_RESET_RX_ID 0x602 // Reset button from steering wheel #endif \ No newline at end of file
--- a/IOobjects/CAN_TxIDs.h Thu Jan 22 07:59:48 2015 +0000 +++ b/IOobjects/CAN_TxIDs.h Sat Feb 07 08:54:51 2015 +0000 @@ -1,20 +1,27 @@ #ifndef CAN_TXIDS_H #define CAN_TXIDS_H -#define BASE_ID 0x500 // Start at 0x500 for this device +#define BASE_ID 0x500 // Start at 0x500 for this device // Transmit IDs - System Mgmt Specific // Operating diagnostics #define SYS_ERROR_ID BASE_ID + 0x00 // Error frame - critical errors that require shutdown -#define SYS_XBEE1_ID BASE_ID + 0x01 // Message in/out counter for xbee1 -#define SYS_XBEE2_ID BASE_ID + 0x02 // Message in/out counter for xbee2 -#define SYS_TEMP_ID BASE_ID + 0x03 // Internal temperature of the glv battery chargerFET +#define SYS_MODE_ID BASE_ID + 0x01 // Operating mode +#define SYS_FLAGS_ID BASE_ID + 0x02 // Signals +#define SYS_PROFILE_ID BASE_ID + 0x03 // Profile being used +#define SYS_TIME_ID BASE_ID + 0x04 // SysTime and startup time +#define SYS_XBEE1_MSG_ID BASE_ID + 0x05 // Message in/out counter for xbee1 +#define SYS_XBEE1_RATE_ID BASE_ID + 0x06 // Message in/out counter for xbee1 +#define SYS_XBEE2_MSG_ID BASE_ID + 0x07 // Message in/out counter for xbee2 +#define SYS_XBEE2_RATE_ID BASE_ID + 0x08 // Message in/out counter for xbee2 +#define SYS_TEMP_ID BASE_ID + 0x09 // Internal temperature of the glv battery chargerFET // GLV Battery #define SYS_GLV_CURRENT_ID BASE_ID + 0x10 // GLV battery current #define SYS_GLV_CAPACITY_ID BASE_ID + 0x11 // GLV battery capacity setting #define SYS_GLV_AH_ID BASE_ID + 0x12 // GLV battery amphours #define SYS_GLV_SOC_ID BASE_ID + 0x13 // GLV battery SOC +#define SYS_GLV_ERROR_ID BASE_ID + 0x14 // GLV battery error byte // DC-DC Converter #define SYS_DCDC_CURRENT_ID BASE_ID + 0x20 // DC-DC current
--- a/IOobjects/IOobjects.cpp Thu Jan 22 07:59:48 2015 +0000
+++ b/IOobjects/IOobjects.cpp Sat Feb 07 08:54:51 2015 +0000
@@ -16,19 +16,14 @@
DigitalIn(P1_28, PullDown), // Sense11 - TSMS/tractive enable
};
PollSwitch switches(sw, sizeof(sw)/sizeof(sw[0])); // Shutdown switch sense pins (SWITCH PIN ARRAY, NUM PINS)
-CANBuffer can(CAN2, MEDIUM, P4_28); // Buffered CAN interface (PORT, SPEED, SILENT PIN)
-CoulombCounter glvBat(p19, FAST_LOOP*1000); // Coulomb counter battery monitor for GLV Battery (CURRENT SENSE PIN, INTEGRATION TIME)
+CANBuffer can(CAN2, MEDIUM, P4_28, 1, 1); // Buffered CAN interface (PORT, SPEED, SILENT PIN)
+CoulombCounter glvBat(p19, FAST_LOOP*1000, 1); // Coulomb counter battery monitor for GLV Battery (CURRENT SENSE PIN, INTEGRATION TIME)
IMD imd(P1_26); // IMD PWM sense channel to read status and resistance (IMD PWM PIN)
-LatchMonitor AMSlatch(P0_18, P0_22, START_DELAY*1000); // Supervisor for AMS hardware latch/reset circuit (OK PIN, FAULT PIN, STARTUP DELAY (ms))
-LatchMonitor IMDlatch(P0_17, P0_21, START_DELAY*1000); // Supervisor for IMD hardware latch/reset circuit (OK PIN, FAULT PIN, STARTUP DELAY (ms))
-MODSERIAL pc(USBTX, USBRX, TX_SIZE, RX_SIZE); // Serial to computer for diagnostics, 3kB output buffer, 256 byte input buffer
-DC_DC dcdc(p18, p20, p26, p25, p24, p23, 0.01, 1); // DC-DC converter & high-current load control (CONTROL PIN, CURRENT SENSE PIN, FAN1 PIN, FAN2 PIN, PUMP1 PIN, PUMP2 PIN, PWM PERIOD (sec), FULL-SCALE SLEW (sec))
+LatchMonitor AMSlatch(P0_18, P0_22); // Supervisor for AMS hardware latch/reset circuit (OK PIN, FAULT PIN, STARTUP DELAY (ms))
+LatchMonitor IMDlatch(P0_17, P0_21); // Supervisor for IMD hardware latch/reset circuit (OK PIN, FAULT PIN, STARTUP DELAY (ms))
+MODSERIAL pc(USBTX, USBRX, 1, RX_SIZE); // Serial to computer for diagnostics, 3kB output buffer, 256 byte input buffer
+DC_DC dcdc(p18, p20, p26, p25, p24, p23, 0.01, 5, 1);// DC-DC converter & high-current load control (CONTROL PIN, CURRENT SENSE PIN, FAN1 PIN, FAN2 PIN, PUMP1 PIN, PUMP2 PIN, PWM PERIOD (sec), FULL-SCALE SLEW (sec))
Temperature internalTmp(&NXFT15XH103_TABLE, p15); // Temperature conversion look-up table for internal temperature on the GLV bat charger FET (TABLE PTR, PIN)
-
-XbeeManager xbeeRelay(p9, p10, p13, p14, XBEE_BAUD, XBEE_TX_SIZE, XBEE_RX_SIZE);
+XbeeManager xbeeRelay(p9, p10, p13, p14, XBEE_BAUD, 1, 1);
//XbeeRelay xbee;
-
DigitalOut extras[] = {(p16), (p17)}; // Unused analog pins driven low
-Inputs data;
-CANinputs CANdata;
-TempData tempData;
\ No newline at end of file
--- a/IOobjects/IOobjects.h Thu Jan 22 07:59:48 2015 +0000
+++ b/IOobjects/IOobjects.h Sat Feb 07 08:54:51 2015 +0000
@@ -16,6 +16,7 @@
#include "Temperature.h"
#include "Watchdog.h"
#include "XbeeManager.h"
+#include "DataStructures.h"
//#include "XbeeRelay.h"
extern CANBuffer can;
@@ -31,58 +32,4 @@
//extern XbeeRelay xbee;
extern XbeeManager xbeeRelay;
-// Shared data updated by producer threads
-class Inputs {
-public:
- float glvCurrent;
- float glvAmphours;
- float glvSOC;
- float glvCapacity;
- bool glvOverCurrent; // Error bit, turn off car
-
- char dcdcStatus;
- bool dcdcError; // Error bit, turn off car
- float dcdcCurrent;
- float dcdcFan1Duty;
- float dcdcFan2Duty;
- float dcdcPump1Duty;
- float dcdcPump2Duty;
-
- char imdStatus;
- float imdResistance;
- bool imdError; // Error bit, turn off car
-
- char AMSlatchError; // Error bit, turn off car
- char IMDlatchError; // Error bit, turn off car
-
- char switchState;
-
- float internalTemp;
- bool internalOverTemp; // Error bit, turn off car
-
- bool watchdogReset; // Error bit, turn off car
- bool canFault; // Error bit, turn off car
-
- char errorFrame;
-};
-
-// Shared data updated by CAN messeges
-class CANinputs {
-public:
- bool airsClosed;
- float dcdcFan1Duty;
- float dcdcFan2Duty;
- float dcdcPump1Duty;
- float dcdcPump2Duty;
-};
-class TempData {
-public:
- char inputStr[RX_SIZE+1];
- char parseGoodChar;
- void* wdtThreadId;
-};
-
-extern CANinputs CANdata;
-extern Inputs data;
-extern TempData tempData;
-#endif
\ No newline at end of file
+#endif
--- a/Libs/CANBuffer.lib Thu Jan 22 07:59:48 2015 +0000 +++ b/Libs/CANBuffer.lib Sat Feb 07 08:54:51 2015 +0000 @@ -1,1 +1,1 @@ -http://developer.mbed.org/teams/Penn-Electric-Racing/code/CANBuffer/#b2b886bdb080 +http://developer.mbed.org/teams/Penn-Electric-Racing/code/CANBuffer/#bf7adaf90781
--- a/Libs/CoulombCounter/CoulombCounter.cpp Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/CoulombCounter/CoulombCounter.cpp Sat Feb 07 08:54:51 2015 +0000
@@ -1,65 +1,109 @@
#include "CoulombCounter.h"
const float MSEC_HRS = 2.77778e-7; // Multiplier to convert milliseconds to hours
-const float BAT_ISENSE_MULTIPLIER = /*6.2299*/6.640114; // Multiplier to convert float to amps, calibrated using 3 points with Fluke 87V meter and code on 1/9/15
-const float BAT_ISENSE_OFFSET = /*-0.5*BAT_ISENSE_MULTIPLIER*/-3.344968; // Offset to convert float to amps, calibrated using 3 points with Fluke 87V meter and code on 1/9/15
-const float BAT_ISENSE_LIMS = 3.0; // Over-current limit = +/- 3A
+const float BAT_ISENSE_MULTIPLIER = 6.640114; // Multiplier to convert float to amps, calibrated using 3 points with Fluke 87V meter and code on 1/9/15
+const float BAT_ISENSE_OFFSET = -3.344968; // Offset to convert float to amps, calibrated using 3 points with Fluke 87V meter and code on 1/9/15
-CoulombCounter::CoulombCounter(PinName _pin, int _mSec) : BatISense(_pin) {
+CoulombCounter::CoulombCounter(PinName _pin, int _mSec, unsigned int size) : BatISense(_pin)
+{
mSec = _mSec;
+ _size = 0;
+ errorPacket = 0;
+ overChargeCurrent = 0;
+ overDischargeCurrent = 0;
// Default capacity if blank or corrupted
- float capReg = store.read(rtcGPREG_capacity);
- if (capReg < 1.0 || capReg > 5.0) { // Bad, write default
- store.write(defaultAh, rtcGPREG_capacity);
+ float capReg = store.read(GPREG_AH_CAPACITY);
+ if (capReg < MIN_CAPACITY_SETTING || capReg > MAX_CAPACITY_SETTING) { // Bad, write default
+ store.write(DEFAULT_AH, GPREG_AH_CAPACITY);
+ errorPacket |= CAP_INIT;
}
-
+
// Default SOC if blank or corrupted
- float Ah = store.read(rtcGPREG_counter);
- if (Ah < 0 || Ah > defaultAh) { // Bad, write default
- store.write(defaultSOC*defaultAh, rtcGPREG_counter);
+ float Ah = store.read(GPREG_AH_COUNTER);
+ if (Ah < 0 || Ah > MAX_CAPACITY_SETTING) { // Bad, write default
+ store.write(DEFAULT_SOC*DEFAULT_AH, GPREG_AH_COUNTER);
+ errorPacket |= SOC_INIT;
}
-
+
+ // Allocate the buffer
+ buffArr = new float [size];
+ if (buffArr == 0) {
+ error("Coulomb Counter failed to allocate memory");
+ }
+ _size = size;
+ tracker = 0;
+
// Take the initial readings, fill the buffer
- for (int i = 0; i < CC_FILTER_TAPS; i++) {
+ for (int i = 0; i < _size; i++) {
buffArr[i] = BatISense.read()*BAT_ISENSE_MULTIPLIER+BAT_ISENSE_OFFSET;
}
updateAvg(); // Get avg and fill out overCurrent flag
- tracker=0;
+}
+void CoulombCounter::setup(float *charge, float *discharge) {
+ overChargeCurrent = charge;
+ overDischargeCurrent = discharge;
}
+bool CoulombCounter::size(unsigned int size)
+{
+ if (_size == size) return false;
-void CoulombCounter::sample() {
+ // Get a current sample, this will be used to fill the new buffer
+ updateAvg();
+ float avg = current();
+
+ float* newArr = new float [size];
+ if (newArr == 0) return false;
+ else {
+ delete[] buffArr; // Free old
+ __disable_irq();
+
+ buffArr = newArr; // Transfer
+ _size = size; // Set new size
+ for (int i = 0; i < _size; i++) buffArr[i] = avg; // Fill with old avg
+
+ __enable_irq();
+ return true;
+ }
+}
+void CoulombCounter::sample()
+{
// Take the reading
float currentSample = BatISense.read()*BAT_ISENSE_MULTIPLIER+BAT_ISENSE_OFFSET;
-
+
// Integrate
float f = ampHours()-currentSample*mSec*MSEC_HRS;
-
+
// Bound to valid range
float cap = capacity();
if (f > cap) f = cap;
if (f < 0) f = 0;
-
+
// Write
- store.write(f, rtcGPREG_counter);
+ store.write(f, GPREG_AH_COUNTER);
+
+ // Add to filter
+ buffArr[tracker++] = currentSample;
+ tracker %= _size;
- // Add to filter
- buffArr[tracker] = currentSample;
- tracker++;
- if (tracker >= CC_FILTER_TAPS) tracker = 0;
+ // Update the avg, check for over-current
updateAvg();
}
-void CoulombCounter::updateAvg() {
+void CoulombCounter::updateAvg()
+{
float avg = 0;
- for (int i = 0; i < CC_FILTER_TAPS; i++) {
- avg += buffArr[i];
+ for (int i = 0; i < _size; i++) {
+ avg += buffArr[i];
}
- avg /= CC_FILTER_TAPS;
- if (abs(avg) > BAT_ISENSE_LIMS) overCurrent = true;
+ avg /= _size;
currentFiltered = avg;
-}
+
+ errorPacket = 0;
+ if (overChargeCurrent == 0) return; // setup() not run yet
-bool CoulombCounter::overCurrentDetected() {
- return overCurrent;
+ if (avg > *overDischargeCurrent) errorPacket |= OVER_DISCHARGE_I;
+ else errorPacket &= ~OVER_DISCHARGE_I;
+ if (avg < *overChargeCurrent) errorPacket |= OVER_CHARGE_I;
+ else errorPacket &= ~OVER_CHARGE_I;
}
\ No newline at end of file
--- a/Libs/CoulombCounter/CoulombCounter.h Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/CoulombCounter/CoulombCounter.h Sat Feb 07 08:54:51 2015 +0000
@@ -2,58 +2,81 @@
#define _FILE_CURRENTMONITOR_H
#include "mbed.h"
-#include "CANBuffer.h"
#include "RTCStore.h"
-const float defaultAh = 1.5; // Default amphours of battery, in case store read is bad/empty
-const float defaultSOC = 0.5;
-const int CC_FILTER_TAPS = 50; // Nominal current draw with only Sys.Mgmt plugeed in should be 0.182A for calibration
-const int rtcGPREG_counter = 0; // rtc GPREG offset for the coulomb counter
-const int rtcGPREG_capacity = 1; // rtc GPREG offset for the capacity spec
-const float MIN_CAPACITY_SETTING = 0.5; // Lowest allowable capacity setting
-const float MAX_CAPACITY_SETTING = 10; // Largest allowable capacity setting
+#define GPREG_AH_COUNTER 0 // rtc GPREG offset for the coulomb counter
+#define GPREG_AH_CAPACITY 1 // rtc GPREG offset for the capacity spec
+
+const float DEFAULT_AH = 1.5; // Default amphours of battery, in case store read is bad/empty
+const float DEFAULT_SOC = 0.5; // Defualt SOC, in case of bad read/store
+
+const float MIN_CAPACITY_SETTING = 0.5; // Lowest allowable capacity setting
+const float MAX_CAPACITY_SETTING = 10; // Largest allowable capacity setting
-class CoulombCounter {
-
+enum GLV_BAT_ERROR {
+ OVER_CHARGE_I = 1<<0, // Charge current exceeded (after filter)
+ OVER_DISCHARGE_I = 1<<1, // Discharge current exceeded (after filter)
+ CAP_INIT = 1<<2, // Capacity RTC register was invalid on startup
+ SOC_INIT = 1<<3, // SOC RTC register was invalid on startup
+};
+
+class CoulombCounter
+{
+
public:
// Configures for a certain pin, millisecond sample period, and which GPREG in store to use to store the ampHours
- CoulombCounter(PinName _pin, int _mSec);
-
- bool overCurrentDetected(); // Sensor above range
- float current() { return currentFiltered; } // Last current reading in Amps
+ CoulombCounter(PinName _pin, int _mSec, unsigned int size=50);
+ void setup(float* overChargeCurrent, float* overDischargeCurrent);
+ bool size(unsigned int size);
+ char readError() {
+ return errorPacket;
+ }
+ void clearError() {
+ errorPacket = 0;
+ }
+ float current() {
+ return currentFiltered; // Last current reading in Amps
+ }
void sample();
-
- float capacity() { return store.read(rtcGPREG_capacity); }
- float SOC() { return ampHours()/capacity(); }
- float ampHours() { return store.read(rtcGPREG_counter); }
-
+
+ float capacity() {
+ return store.read(GPREG_AH_CAPACITY);
+ }
+ float SOC() {
+ return ampHours()/capacity();
+ }
+ float ampHours() {
+ return store.read(GPREG_AH_COUNTER);
+ }
bool changeCapacity(float _ampHours) {
if (_ampHours < MIN_CAPACITY_SETTING || _ampHours > MAX_CAPACITY_SETTING) return false;
- store.write(_ampHours, rtcGPREG_capacity);
+ store.write(_ampHours, GPREG_AH_CAPACITY);
return true;
}
bool resetToSOC(float _SOC) {
if (_SOC < 0 || _SOC > 1) return false;
- store.write(_SOC*capacity(), rtcGPREG_counter);
+ store.write(_SOC*capacity(), GPREG_AH_COUNTER);
return true;
}
bool resetToAh(float _ampHours) {
- if (_ampHours < 0 || _ampHours > capacity()) return false;
- store.write(_ampHours, rtcGPREG_counter);
+ if (_ampHours < 0 || _ampHours > capacity()) return false;
+ store.write(_ampHours, GPREG_AH_COUNTER);
return true;
}
private:
- //Ticker sampler; // Used to capture next sample and coulomb count
- RTCStore store;
- volatile bool overCurrent;
- void updateAvg(); // Used to get average current and update flags
- int mSec;
- volatile float currentFiltered;
- AnalogIn BatISense; // Analog input pin
+ RTCStore store; // Access to the RTC registers (battery-backed)
+ unsigned int _size; // Size of buffer
+ void updateAvg(); // Used to get average current and update flags
+ int mSec; // Integration time
+ float currentFiltered; // Filtered current, last result of updateAvg()
+ AnalogIn BatISense; // Analog input pin
+ char errorPacket; // Errors (over-current)
+ float *buffArr; // The buffer itself
+ unsigned int tracker; // Position to load next sample
- volatile float buffArr[CC_FILTER_TAPS];
- volatile unsigned int tracker;
+ float* overChargeCurrent; // Charge current limit
+ float* overDischargeCurrent; // Discharge current limit
};
#endif
\ No newline at end of file
--- a/Libs/DC_DC/DC_DC.cpp Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/DC_DC/DC_DC.cpp Sat Feb 07 08:54:51 2015 +0000
@@ -4,78 +4,118 @@
#define TURN_OFF dcdcControl = 1; isControlPinOn = false;
#define TURN_ON dcdcControl = 0; isControlPinOn = true;
-const float CURRENT_MULTIPLIER_DEFAULT = 41.35338345864663; // Full scale amps
-const float CURRENT_OFFSET_DEFAULT = 0.0909090909090909; // Float adc reading for 0 amps
+const float CURRENT_MULTIPLIER_DEFAULT = 41.35338345864663; // Full scale amps
+const float CURRENT_OFFSET_DEFAULT = 0.0909090909090909; // Float adc reading for 0 amps
-const float DC_DC_ON_THRESHOLD = 0.5; // Current draw required in order to declare it as on
-const int STARTUP_DELAY_MS = 1000; // DC-DC converter startup time in milliseconds
-const float OVER_CURRENT_THRESHOLD = 25; // Overcurrent threshold
-const float CURRENT_SENSOR_LLIM = -1.0; // Lowest allowable reading before declaring sensor broken
-const float CURRENT_SENSOR_ULIM = 33; // Sensor is at 5V rail, broken
-const int STOP_DELAY_MS = 1000; // Amount of time given to turn-off before flagging error
+const float CURRENT_SENSOR_LLIM = -1.0; // Lowest allowable reading before declaring sensor broken
+const float CURRENT_SENSOR_ULIM = 33; // Sensor is at 5V rail, broken
-DC_DC::DC_DC(PinName _dcdcPin, PinName _dcdcCurrent, PinName _fan1, PinName _fan2, PinName _pump1, PinName _pump2, float period, float slew) :
+DC_DC::DC_DC(PinName _dcdcPin, PinName _dcdcCurrent, PinName _fan1, PinName _fan2, PinName _pump1, PinName _pump2, float period, float slew, unsigned int size) :
dcdcControl(_dcdcPin, 1), dcdcCurrent(_dcdcCurrent), fan1(_fan1, period, slew), fan2(_fan2, period, slew), pump1(_pump1, period, slew), pump2(_pump2, period, slew)
{
TURN_OFF
currentOffset = CURRENT_OFFSET_DEFAULT;
- starting = false;
- stopping = false;
buffTracker = 0;
wait_ms(10); // Make sure Hall effect IC is ready
startTimer.reset();
stopTimer.reset();
status=0;
+ _size = 0;
+ onThreshold = 0;
+ startDelay = 0;
+ stopDelay = 0;
+ overCurrent = 0;
+
+ filterBuff = new float[size];
+ if (filterBuff == 0) {
+ error("DC-DC failed to allocate memory");
+ }
+ _size = size;
// Fill up the buffer
- for (int i = 0; i < DC_DC_FILTER_TAPS; i++) {
+ for (int i = 0; i < _size; i++) {
filterBuff[i] = (dcdcCurrent - currentOffset) * CURRENT_MULTIPLIER_DEFAULT;
wait_ms(10);
}
- updateCurrent(); // Compute avg
- sample(); // Use sample() function to check for errors
- if (!critError) { // If no errors, zero the sensor
- currentOffset = (current / CURRENT_MULTIPLIER_DEFAULT) + CURRENT_OFFSET_DEFAULT;
- }
+ // Compute the zero
+ currentOffset = (current / CURRENT_MULTIPLIER_DEFAULT) + CURRENT_OFFSET_DEFAULT;
// Correct the buffer for the new zero
- for (int i = 0; i < DC_DC_FILTER_TAPS; i++) {
+ for (int i = 0; i < _size; i++) {
filterBuff[i] = ((CURRENT_OFFSET_DEFAULT - currentOffset) * CURRENT_MULTIPLIER_DEFAULT) + filterBuff[i];
}
}
+void DC_DC::setup(float* _onThreshold, float* _overCurrent, float* _startDelay, float* _stopDelay) {
+ onThreshold = _onThreshold;
+ overCurrent = _overCurrent;
+ startDelay = _startDelay;
+ startDelay = _startDelay;
+}
+
+bool DC_DC::size(unsigned int size) {
+ if (_size == size) return false;
+
+ // Get recent
+ updateCurrent();
+ float avg = getCurrent();
+
+ // Allocate new
+ float* newBuff = new float[size];
+ if (newBuff == 0) return false; // Not found, nothing has been changed
+ else {
+ delete[] filterBuff; // Free old
+ __disable_irq();
+
+ _size = size; // Set size
+ filterBuff = newBuff; // Transfer to new
+ for (int i = 0; i < size; i++) filterBuff[i] = avg; // Pre-fill with old average
+
+ __enable_irq();
+ return true;
+ }
+}
void DC_DC::updateCurrent()
{
float avg=0;
- for (int i = 0; i < DC_DC_FILTER_TAPS; i++) {
+ for (int i = 0; i < _size; i++) {
avg += filterBuff[i];
}
- avg /= DC_DC_FILTER_TAPS;
+ avg /= _size;
current = avg;
}
void DC_DC::set(bool on)
{
+ if (onThreshold == 0) return; // Not setup yet
+
// Do nothing if already on
if (on && isControlPinOn) return;
- // Do nothing if already off
- if (!on && !isControlPinOn) return;
+ // Double check if already off
+ if (!on && !isControlPinOn) {
+ fan1.directOff();
+ fan2.directOff();
+ pump1.directOff();
+ pump2.directOff();
+ TURN_OFF
+ }
// If start requested and no error
- if (on && !critError) {
- starting = true;
+ if (on && !critError && !(status & POWER_DOWN)) {
+ status |= POWER_UP;
+ status &= ~POWER_DOWN;
TURN_ON
startTimer.reset();
startTimer.start();
stopTimer.stop();
stopTimer.reset();
- stopping = false;
- // If stop requested
+ // If stop requested
} else {
- stopping = true;
+ status &= ~POWER_UP;
+ status |= POWER_DOWN;
fan1.directOff();
fan2.directOff();
pump1.directOff();
@@ -85,7 +125,6 @@
stopTimer.start();
startTimer.stop();
startTimer.reset();
- starting = false;
}
}
@@ -94,61 +133,44 @@
// Get next current sample
float curr = (dcdcCurrent.read() - currentOffset) * CURRENT_MULTIPLIER_DEFAULT;
- filterBuff[buffTracker] = curr; // Add to buffer filter
- buffTracker++;
- if (buffTracker >= DC_DC_FILTER_TAPS) buffTracker = 0;
+ filterBuff[buffTracker++] = curr; // Add to buffer filter
+ buffTracker %= _size;
updateCurrent(); // Compute average
+ if (onThreshold == 0) return; // Not setup yet
+
// Update status
- register char stat = status & 0xF0; // The upper 4 bits (locking errors)
- if (current >= DC_DC_ON_THRESHOLD) stat |= ConvOn; // The converter is actually on (positive current)
- if (isControlPinOn) stat |= SetOn; // The converter is set on
+ char stat = 0;
+ if (current >= *onThreshold) stat |= CONV_ON; // The converter is actually on (positive current)
+ if (isControlPinOn) stat |= SET_ON; // The converter is set on
// During Timed Startup Phase
- if (starting) {
- stat |= PowerUp; // Indicate state in status byte
- if (startTimer.read_ms() >= STARTUP_DELAY_MS) { // Start time elapsed
- if (stat & ConvOn) { // Positive current detected
- startTimer.stop(); // Stop timer
- startTimer.reset(); // Reset to zero
- starting = false; // Indicate running
- } else {
- startTimer.stop();
+ if (stat & POWER_UP) {
+ if (startTimer.read() >= *startDelay) { // Start time elapsed
+ if (stat & CONV_ON) { // Positive current detected
+ startTimer.stop(); // Stop timer
startTimer.reset();
- stat |= FailStart; // Failed to start
+ stat &= ~POWER_UP; // Done power-up
}
}
- } else stat &= ~PowerUp;
+ }
// During Timed Stopping Phase
- if (stopping) {
- stat |= PowerDown; // Indicate state in status byte
- if (stopTimer.read_ms() >= STOP_DELAY_MS) { // Stop time elapsed
- if (stat & ConvOn) { // Converter still on
- stopTimer.stop();
+ if (stat & POWER_DOWN) {
+ if (stopTimer.read() >= *stopDelay) { // Stop time elapsed
+ if (!(stat & CONV_ON)) { // Current dropped, its off
+ stopTimer.stop(); // Stop timer
stopTimer.reset();
- stat |= FailStop; // It didn't turn off even after timer expired!
- } else { // Its actually off
- stopping = false;
- stopTimer.stop();
- stopTimer.reset();
+ stat &= ~POWER_DOWN; // Done power-down
}
}
- } else stat &= ~PowerDown;
-
- // While in steady state
- if (!stopping && !starting) {
- if ((stat & SetOn) && !(stat & ConvOn)) stat |= FailStart; // Should be running but its not
- else stat &= ~FailStart;
- if (!(stat & SetOn) && (stat & ConvOn)) stat |= FailStop; // Should be stopped but its not
- else stat &= ~FailStop;
}
- if (current < CURRENT_SENSOR_LLIM || current > CURRENT_SENSOR_ULIM) stat |= SensorFault; // Sensor out of range
- else if (current > OVER_CURRENT_THRESHOLD) stat |= OverCurrent; // Over current
+ if (current < CURRENT_SENSOR_LLIM || current > CURRENT_SENSOR_ULIM) stat |= SENSOR_FAULT; // Sensor out of range
+ else if (current > *overCurrent) stat |= OVER_CURRENT; // Over current
// Process critical error conditions
- if (stat & (SensorFault | OverCurrent)) critError = true;
+ if (stat & (SENSOR_FAULT | OVER_CURRENT)) critError = true;
else critError = false;
status = stat;
@@ -161,16 +183,26 @@
TURN_OFF
startTimer.stop();
startTimer.reset();
- starting = false;
+ }
+
+ // Don't allow to run if still powering on or off
+ if (stat & (POWER_UP | POWER_DOWN)) {
+ fan1.directOff();
+ fan2.directOff();
+ pump1.directOff();
+ pump2.directOff();
}
}
void DC_DC::setPwm(enum Channel_T chan, float duty)
{
- // Do nothing if error present, starting in startup, or DC-DC not actually on, or not set on
- if (critError || starting || stopping || !(status & ConvOn) || !isControlPinOn) return;
-
- else {
+ // Off if error present, starting in startup, or DC-DC not actually on, or not set on
+ if (!onThreshold || critError || !(status & CONV_ON) || !isControlPinOn || (status & (POWER_UP | POWER_DOWN))) {
+ fan1.directOff();
+ fan2.directOff();
+ pump1.directOff();
+ pump2.directOff();
+ } else {
if (chan == FAN1) fan1.write(duty);
if (chan == FAN2) fan2.write(duty);
if (chan == PUMP1) pump1.write(duty);
@@ -179,9 +211,9 @@
}
float DC_DC::readPwm(enum Channel_T chan)
{
- if (chan == FAN1) return fan1.readRaw();
- if (chan == FAN2) return fan2.readRaw();
- if (chan == PUMP1) return pump1.readRaw();
- if (chan == PUMP2) return pump2.readRaw();
+ if (chan == FAN1) return fan1.readRaw();
+ if (chan == FAN2) return fan2.readRaw();
+ if (chan == PUMP1) return pump1.readRaw();
+ if (chan == PUMP2) return pump2.readRaw();
else return 0;
}
\ No newline at end of file
--- a/Libs/DC_DC/DC_DC.h Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/DC_DC/DC_DC.h Sat Feb 07 08:54:51 2015 +0000
@@ -5,26 +5,26 @@
#include "FanPump.h"
enum Channel_T {
- FAN1=0,
- FAN2,
- PUMP1,
- PUMP2,
+ FAN1 = 0,
+ FAN2 = 1,
+ PUMP1 = 2,
+ PUMP2 = 3,
};
+
enum Status_Bits_T {
- ConvOn=1,
- SetOn=2,
- PowerUp=4,
- PowerDown=8,
- OverCurrent=16,
- SensorFault=32,
- FailStart=64,
- FailStop=128,
+ CONV_ON = 1<<0,
+ SET_ON = 1<<1,
+ POWER_UP = 1<<2,
+ POWER_DOWN = 1<<3,
+ OVER_CURRENT = 1<<4,
+ SENSOR_FAULT = 1<<5,
};
-const int DC_DC_FILTER_TAPS = 50;
class DC_DC{
public:
- DC_DC(PinName _dcdcPin, PinName _dcdcCurrent, PinName _fan1, PinName _fan2, PinName _pump1, PinName _pump2, float period, float slew);
+ DC_DC(PinName _dcdcPin, PinName _dcdcCurrent, PinName _fan1, PinName _fan2, PinName _pump1, PinName _pump2, float period, float slew, unsigned int size=50);
+ void setup(float* onThreshold, float* overCurrent, float* startDelay, float* stopDelay);
+ bool size(unsigned int size);
float getCurrent() { return current; }
void set(bool on);
void setPwm(enum Channel_T chan, float duty);
@@ -37,26 +37,25 @@
bit3 - 1 = DC-DC Power-down, its use is currently blocked until ready
bit4 - 1 = DC-DC over current (drawing more current than allowed according to sensor)
bit5 - 1 = DC-DC current sensor out of range (broken)
- bit6 - 1 = DC-DC failed to start
- bit7 - 1 = DC-DC failed to stop
*/
char getStatus() { return status; }
bool hasCritError() { return critError; } // An error exists according to status above
void sample(); // Attach this function in a 10ms RTOS timer
private:
+
Timer startTimer;
Timer stopTimer;
- volatile bool starting;
- volatile bool stopping;
volatile bool isControlPinOn;
DigitalOut dcdcControl;
AnalogIn dcdcCurrent;
- volatile float currentOffset;
- volatile float filterBuff[DC_DC_FILTER_TAPS];
- volatile int buffTracker;
+
+ float currentOffset;
+ float* filterBuff;
+ int buffTracker;
+ unsigned int _size;
+
void updateCurrent();
-
volatile float current;
volatile char status;
volatile bool critError;
@@ -65,6 +64,11 @@
FanPump fan2;
FanPump pump1;
FanPump pump2;
+
+ float* onThreshold;
+ float* overCurrent;
+ float* startDelay;
+ float* stopDelay;
};
#endif
\ No newline at end of file
--- a/Libs/IMD/IMD.cpp Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/IMD/IMD.cpp Sat Feb 07 08:54:51 2015 +0000
@@ -2,11 +2,14 @@
#include <math.h>
#include "pinmap.h"
-static IMD* instance[4] = { 0 }; // Access member from static frame, one IMD permitted per timer module (4 total IMD objects)
+
+const float ZERO_HZ_TIMEOUT = 0.21; // Time (sec) that must pass without an edge to call it 0 Hz, set to greater than the longest expected pulse width
+const float EXTRA = 0.02; // Margin on the IMD PWM limits
-const uint32_t PCLK = 24000000; // Timer counting clock = 24Mhz
+const uint32_t PCLK = 24000000; // Timer counting clock = 24Mhz
const uint32_t TIMEOUT_TICKS = PCLK*ZERO_HZ_TIMEOUT; // Zeroing timeout in clock ticks = seconds * PCLK
-const float EXTRA = 0.01; // Margin on the IMD PWM limits
+
+static IMD* instance[4] = { 0 }; // Access member from static frame, one IMD permitted per timer module (4 total IMD objects)
// Interrupt functions, must be static context
void tim0_IRQ() {
--- a/Libs/IMD/IMD.h Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/IMD/IMD.h Sat Feb 07 08:54:51 2015 +0000
@@ -5,16 +5,14 @@
#include "mbed.h"
-const float ZERO_HZ_TIMEOUT = 0.15; // Time (sec) that must pass without an edge to call it 0 Hz, set to greater than the longest expected pulse width
-
enum IMDstatus {
- OFF=0,
- NORMAL=1,
- UNDERVOLT=2,
- SPEEDSTART=3,
- ERROR=4,
- GROUNDERR=5,
- INVALID=6,
+ OFF = 0,
+ NORMAL = 1,
+ UNDERVOLT = 2,
+ SPEEDSTART = 3,
+ ERROR = 4,
+ GROUNDERR = 5,
+ INVALID = 6,
};
class IMD{
--- a/Libs/LatchMonitor/LatchMonitor.cpp Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/LatchMonitor/LatchMonitor.cpp Sat Feb 07 08:54:51 2015 +0000
@@ -1,30 +1,47 @@
#include "LatchMonitor.h"
-LatchMonitor::LatchMonitor(PinName _ok, PinName _fault, unsigned int startupDelay_ms):okPin(_ok, PullDown), faultPin(_fault, PullDown) {
-
+LatchMonitor::LatchMonitor(PinName _ok, PinName _fault) : okPin(_ok, PullDown), faultPin(_fault, PullDown)
+{
+ started = false;
+ lastStat = 0;
+ startDelay = 0;
+}
+void LatchMonitor::setup(float* _startDelay)
+{
+ startDelay = _startDelay;
+}
+void LatchMonitor::delayStart()
+{
+ lastStat = 0;
+ update();
+ if (startDelay == 0) return; // Not setup yet
+
// Power-on reset detected
if (LPC_SC->RSID & 1) {
- LPC_SC->RSID = 1; // Clear POR flag
- started = false; // Use the blocking startup timer
- startup.attach_us(this, &LatchMonitor::startupDelay, startupDelay_ms*1000);
+ LPC_SC->RSID = 1; // Clear POR flag
+ started = false; // Use the blocking startup timer
+ startup.attach(this, &LatchMonitor::startupDelay, *startDelay);
} else {
- started = true; // Not a power-cycle, do not use the timer
+ started = true; // Not a power-cycle, do not use the timer
}
}
-void LatchMonitor::startupDelay() {
- started = true;
+void LatchMonitor::startupDelay()
+{
+ started = true;
}
-char LatchMonitor::update() {
+char LatchMonitor::update()
+{
char ret = 0;
ret |= (!okPin << 0); // Mirror the ok pin
ret |= (faultPin << 1); // Copy the fault pin
-
+
if (started) {
ret |= !okPin << 2; // Mirror the ok pin when started only
if (!okPin && !faultPin) { // If started && okFault but not caught in hardware
- ret |= 1 << 3;
+ ret |= HARD_FAULT;
}
}
+ lastStat = ret;
return ret;
}
--- a/Libs/LatchMonitor/LatchMonitor.h Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/LatchMonitor/LatchMonitor.h Sat Feb 07 08:54:51 2015 +0000
@@ -4,26 +4,25 @@
#include "mbed.h"
enum LatchMon_Status_Bits {
- okPinF=1,
- faultPinF=2,
- softwareF=4,
- hardwareF=8,
+ OK_FAULT = 1<<0, // Device OK pin is low
+ LATCHED_HARD = 1<<1, // Circuit is in the tripped state
+ LATCHED_SOFT = 1<<2, // Software-timer has expired and decided to tag the fault
+ HARD_FAULT = 1<<3, // The circuit should have latched open, but it did not
};
-class LatchMonitor {
+class LatchMonitor
+{
public:
// Make this startup delay longer than the actual hardware circuit delay so that it can catch errors in the circuit
- LatchMonitor(PinName _ok, PinName _fault, unsigned int startupDelay_ms);
- /*
- char update() returns status encoded in a char
- bit0 = okPinFault - equals 1 when the OK pin from the monitored device is LOW indicating device fault
- bit1 = faultPinFault - equals 1 when the sys. mgmt. latch circuit is faulted with startup timer in hardware
- bit2 = softwareFault - equals 1 when the LatchMonitor fault occurs with startup timer in software
- bit3 = hardwareFault - the circuit is supposed to signal fault on the hardware pin but its not doing so!
- */
+ LatchMonitor(PinName _ok, PinName _fault);
+ void setup(float* startDelay);
+ void delayStart();
+
char update();
private:
+ char lastStat;
+ float* startDelay;
Timeout startup;
void startupDelay();
bool started;
--- a/Libs/XbeeManager/CAN-xbee/CAN-xbee.cpp Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/XbeeManager/CAN-xbee/CAN-xbee.cpp Sat Feb 07 08:54:51 2015 +0000
@@ -10,10 +10,8 @@
if ((msg.format == CANExtended) && ((msg.id & 0x1FFFFFFF) != msg.id)) return -1;
int i = 0;
- buff[i] = 'C'; // Start of message
- i++;
- /* buff[i] = (msg.format << 0) | (msg.type << 1) | ((msg.len & 0xf) << 2); // Header byte, message info on ID size, RTR, # data bytes
- i++;
+ buff[i++] = 'C'; // Start of message
+ buff[i++] = (msg.format << 0) | (msg.type << 1) | ((msg.len & 0xf) << 2); // Header byte, message info on ID size, RTR, # data bytes
if (msg.format == CANStandard) {
buff[i++] = (msg.id & 0x0FF); // Lower byte of ID
buff[i++] = (msg.id & 0x700) >> 8; // Upper byte of ID
@@ -27,7 +25,7 @@
buff[i++] = msg.data[j];
}
buff[i++] = '\n';
- */return i;
+ return i;
}
bool convert2msg(CANMessage &msg, char* buff) {
@@ -67,28 +65,20 @@
}
// Send a CAN message, first reformat it into a char array, then send over Xbees
-bool CANxbee::send(CANMessage &msg) {
+bool CANxbee::send(CANMessage &msg, unsigned int* length) {
char buff[15]; // Build the string-ified CAN message here
int size = convert2array(msg, buff);
+ *length = 0;
if (size == -1) return false; // Bad message, string not formed
- bool success=false;
- size=1;
int bytesLeft = serial.txBufferGetSize(0) - serial.txBufferGetCount();
- Timer t;
- // Begin thread-safe section
- NVIC_DisableIRQ(TIMER3_IRQn); // Timer3-->RTOS tick
-
if (bytesLeft >= size) {
for (int i = 0; i < size; i++) serial.putc(buff[i]); // Send the message out
- success = true;
+ *length = size;
+ return true;
}
-
- // End thread-safe section
- NVIC_EnableIRQ(TIMER3_IRQn); // Timer3-->RTOS tick
-
- return success;
+ return false;
}
// Continuously call this function in main program, when it returns true, it holds a newly received CAN message that came via Xbee
--- a/Libs/XbeeManager/CAN-xbee/CAN-xbee.h Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/XbeeManager/CAN-xbee/CAN-xbee.h Sat Feb 07 08:54:51 2015 +0000
@@ -19,9 +19,17 @@
public:
CANxbee(PinName tx, PinName rx, int baud, int txSize, int rxSize);
- bool send(CANMessage &msg); // Send a CANMessage object over the xbee (first converts to serial)
+ bool send(CANMessage &msg, unsigned int* length); // Send a CANMessage object over the xbee (first converts to serial)
bool receive(CANMessage &msg); // Receive a CANMessage object over the xbee (converts from serial message to CAN)
-
+ void baud(int baudrate) {
+ serial.baud(baudrate);
+ }
+ bool txSize(unsigned int size) {
+ return (serial.txBufferSetSize(size) == 0);
+ }
+ bool rxSize(unsigned int size) {
+ return (serial.rxBufferSetSize(size) == 0);
+ }
private:
MODSERIAL serial;
char rxBuff[15];
--- a/Libs/XbeeManager/XbeeManager.cpp Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/XbeeManager/XbeeManager.cpp Sat Feb 07 08:54:51 2015 +0000
@@ -3,27 +3,31 @@
XbeeManager::XbeeManager(PinName tx1, PinName rx1, PinName tx2, PinName rx2, int baud, int txSize, int rxSize) :
x1(tx1, rx1, baud, txSize, rxSize), x2(tx2, rx2, baud, txSize, rxSize)
{
-
alternate = false;
extraAvail = false;
counterX1in = 0;
counterX2in = 0;
counterX1out = 0;
counterX2out = 0;
+ bytesX1out = 0;
+ bytesX2out = 0;
}
bool XbeeManager::send(CANMessage &msg)
{
alternate = !alternate;
+ unsigned int length;
if (alternate) {
- if (x1.send(msg)) {
+ if (x1.send(msg, &length)) {
counterX1out++;
+ bytesX1out += length;
return true;
}
} else {
- if (x2.send(msg)) {
+ if (x2.send(msg, &length)) {
counterX2out++;
- return true;
+ bytesX2out += length;
+ return true;
}
}
return false;
--- a/Libs/XbeeManager/XbeeManager.h Thu Jan 22 07:59:48 2015 +0000
+++ b/Libs/XbeeManager/XbeeManager.h Sat Feb 07 08:54:51 2015 +0000
@@ -7,12 +7,25 @@
class XbeeManager {
public:
XbeeManager(PinName tx1, PinName rx1, PinName tx2, PinName rx2, int baud, int txSize, int rxSize);
+ void baud(int baudrate) {
+ x1.baud(baudrate);
+ x2.baud(baudrate);
+ }
+ bool txSize(unsigned int size) {
+ return (x1.txSize(size) && x2.txSize(size));
+ }
+ bool rxSize(unsigned int size) {
+ return (x1.rxSize(size) && x2.rxSize(size));
+ }
+
bool send(CANMessage &msg);
bool receive(CANMessage &msg);
unsigned int counterX1in;
unsigned int counterX2in;
unsigned int counterX1out;
unsigned int counterX2out;
+ unsigned int bytesX1out;
+ unsigned int bytesX2out;
private:
bool alternate;
--- a/Libs/mbed-rtos.lib Thu Jan 22 07:59:48 2015 +0000 +++ b/Libs/mbed-rtos.lib Sat Feb 07 08:54:51 2015 +0000 @@ -1,1 +1,1 @@ -http://developer.mbed.org/users/mbed_official/code/mbed-rtos/#28712e303960 +http://developer.mbed.org/users/mbed_official/code/mbed-rtos/#5448826aa700
--- a/inCommands/inCommands.cpp Thu Jan 22 07:59:48 2015 +0000
+++ b/inCommands/inCommands.cpp Sat Feb 07 08:54:51 2015 +0000
@@ -1,95 +1,19 @@
#include "inCommands.h"
-
-bool inCommands::serviceCAN(CANMessage* fromXbee)
-{
- CANMessage msg;
- if (fromXbee != NULL) {
- memcpy((void*)&msg, (void*)fromXbee, sizeof(CANMessage));
- } else {
- if (!can.rxRead(msg)) return false;
- }
-
- switch (msg.id) {
- case FAN_CONTROL_ID:
- if (msg.len != 2*sizeof(float)) break;
- memcpy((void*)&CANdata.dcdcFan1Duty, &msg.data[0], sizeof(float));
- memcpy((void*)&CANdata.dcdcFan2Duty, &msg.data[4], sizeof(float));
- dcdc.setPwm(FAN1, CANdata.dcdcFan1Duty);
- dcdc.setPwm(FAN2, CANdata.dcdcFan2Duty);
- break;
-
- case PUMP_CONTROL_ID:
- if (msg.len != 2*sizeof(float)) break;
- memcpy((void*)&CANdata.dcdcPump1Duty, &msg.data[0], sizeof(float));
- memcpy((void*)&CANdata.dcdcPump2Duty, &msg.data[4], sizeof(float));
- dcdc.setPwm(PUMP1, CANdata.dcdcPump1Duty);
- dcdc.setPwm(PUMP2, CANdata.dcdcPump2Duty);
- break;
-
- case DCDC_CONTROL_ID:
- if (msg.len != sizeof(char)) break;
- if (msg.data[0] == 1) dcdc.set(1);
- else dcdc.set(0);
- break;
-
- case AMS_MODE_ID:
- if (msg.len != sizeof(char)) break;
- if (msg.data[0] & 1<<2) { // AIRs closed?
- CANdata.airsClosed = true;
- dcdc.set(1);
- } else {
- CANdata.airsClosed = false;
- dcdc.set(0);
- }
- break;
-
- case GLVBAT_SETSOC_ID:
- if (msg.len != sizeof(float)) break;
- glvBat.resetToSOC(*((float*)(&msg.data[0])));
- break;
-
- case GLVBAT_SETAH_ID:
- if (msg.len != sizeof(float)) break;
- glvBat.resetToAh(*((float*)(&msg.data[0])));
- break;
-
- case GLVBAT_SETCAPAC_ID:
- if (msg.len != sizeof(float)) break;
- glvBat.changeCapacity(*((float*)(&msg.data[0])));
- break;
- case STEERING_RESET_ID:
- NVIC_SystemReset();
- break;
- default:
- break;
- }
-
- return true;
-
-}
-// Check for incoming messages from the xbees, relay them to the CAN function and send them out on the bus
-bool inCommands::receiveMsgXbee()
-{
- CANMessage msg;
- if (xbeeRelay.receive(msg)) { // Incoming CAN message string received
- if (!can.txWrite(msg)) data.canFault = true; // Send it out on the CAN bus
- serviceCAN(&msg); // Send it into the local serviceCAN routine
- return true;
- } else return false;
-}
+#include "runTime.h"
+#include "inMacros.h"
// Compare string to a word in the serial input, shorter to type
#define CMP(w, string) if (!strcasecmp(word[w-1], string))
// Serial input
-int inCommands::serviceSerial()
+int serviceSerial()
{
- static int end = 0; // End of string position
+ static int end = 0; // End of string position
int c=0;
if (pc.readable()) c = pc.getc();
- if (c == -1) return -2; // Invalid char, no char available
-
+ if (c == -1 || c == 0) return 0;
+
char b = c; // Casted to char type
bool process = false; // Is string complete (ready to parse)?
@@ -112,7 +36,7 @@
}
// Continue to parsing section only if flagged as complete and string not empty
if (!process || strlen((char*)tempData.inputStr) == 0) return 0;
-
+
static char word[3][RX_SIZE+1]; // Hold 3 words
int pieces = sscanf(tempData.inputStr, "%s %s %s", word[0], word[1], word[2]); // Populate words
tempData.inputStr[0] = 0; // Empty the string displayed on screen
@@ -125,17 +49,79 @@
NVIC_SystemReset();
return 1;
}
+ // Clear non-volatile fault flags, then reset the microcontroller
+ CMP(1, "resetClear") {
+ runTime::clearFaults();
+ NVIC_SystemReset();
+ return 1;
+ }
+ return -1;
}
// Two word commands
if (pieces == 2) {
- // Manual DC-DC on/off control
- CMP(1, "dcdc") {
- CMP(2, "on") {
- dcdc.set(1);
+ // Change the serial dashboard display mode
+ CMP(1, "display") {
+ CMP(2, "compact") {
+ if (param->change_extendedSerial(0)) {
+ op->profileModded=true;
+ return 1; // Change function defined by macro
+ }
+ }
+ CMP(2, "extended") {
+ if (param->change_extendedSerial(1)) {
+ op->profileModded=true;
+ return 1; // Change function defined by macro
+ }
+ }
+ }
+ // Change ACK setting for CAN (noAck allows testing of CAN without other nodes)
+ CMP(1, "CANack") {
+ CMP(2, "noack") { // NoAck test mode
+ if (param->change_CANnoAck(1)) {
+ op->profileModded=true;
+ return 1;
+ }
+ }
+ CMP(2, "ack") { // Normal CAN protocol
+ if (param->change_CANnoAck(0)) {
+ op->profileModded=true;
+ return 1;
+ }
+ }
+ return -1;
+ }
+ // Artificially capture a freeze frame, save to flash
+ CMP(1, "capture") {
+ CMP(2, "freeze") {
+ if (!FreezeFrame::getError()) { // Only allow capture if spot available (last error frame was cleared manually)
+ if (FreezeFrame::writeFrame()) { // Copy RAM frame to freezree sector in flash
+ return 1;
+ }
+ }
+ }
+ return -1;
+ }
+ // Clear fault conditions
+ CMP(1, "clear") {
+ CMP(2, "freeze") { // Clear the freeze frame (mark as read)
+ FreezeFrame::clearError(); // Clear the non-volatile error marker
return 1;
}
- CMP(2, "off") {
- dcdc.set(0);
+ CMP(2, "faults") { // Clear everything
+ runTime::clearFaults();
+ return 1;
+ }
+ return -1;
+ }
+ // Change the display contents
+ CMP(1, "view") {
+ CMP(2, "freeze") { // View the last stored freeze frame
+ if (FreezeFrame::getFrame(&tempData.freeze)) { // Fetch the pointer from flash
+ return 1;
+ }
+ }
+ CMP(2, "live") { // View live data from RAM
+ tempData.freeze = NULL; // Zero the pointer
return 1;
}
return -1;
@@ -172,6 +158,44 @@
}
return -1;
}
+
+ bool parsed=false;
+
+ CHANGE_VAR("GLVchar", chargeCurrent)
+ CHANGE_VAR("GLVdisch", dischargeCurrent)
+ CHANGE_VAR("GLVnomCap", nominalCapacity)
+ CHANGE_VAR("GLVtaps", glvBat_taps)
+ CHANGE_VAR("dcdcThres", dcdcThreshold)
+ CHANGE_VAR("dcdcOver", dcdcOverCurrent)
+ CHANGE_VAR("dcdcStart", dcdcStartDelay)
+ CHANGE_VAR("dcdcStop", dcdcStopDelay)
+ CHANGE_VAR("dcdcTaps", dcdc_taps)
+ CHANGE_VAR("imdStart", imdStartDelay)
+ CHANGE_VAR("amsStart", amsStartDelay)
+ CHANGE_VAR("IntOverT", internalOverTemp)
+ CHANGE_VAR("CANtxSize", CANtxSize)
+ CHANGE_VAR("CANrxSize", CANrxSize)
+ CHANGE_VAR("SerialBaud", SerialBaud)
+ CHANGE_VAR("SerialTx", SerialTxSize)
+ CHANGE_VAR("XbeeBaud", XbeeBaud)
+ CHANGE_VAR("XbeeTxSize", XbeeTxSize)
+ CHANGE_VAR("XbeeRxSize", XbeeRxSize)
+ CHANGE_VAR("CANack", CANnoAck)
+
+ if (!parsed) return -1;
+
+ CMP(1, "GLVnomCap") return glvBat.changeCapacity(param->nominalCapacity)?1:-1;
+ CMP(1, "GLVtaps") return glvBat.size(param->glvBat_taps)?1:-1;
+ CMP(1, "dcdcTaps") return dcdc.size(param->dcdc_taps)?1:-1;
+ CMP(1, "CANtxSize") return can.txSize(param->CANtxSize)?1:-1;
+ CMP(1, "CANrxSize") return can.rxSize(param->CANrxSize)?1:-1;
+ CMP(1, "SerialBaud") pc.baud(param->SerialBaud);
+ CMP(1, "SerialTx") return (pc.txBufferSetSize(param->SerialTxSize) == 0)?1:-1;
+ CMP(1, "XbeeBaud") xbeeRelay.baud(param->XbeeBaud);
+ CMP(1, "XbeeTxSize") return (xbeeRelay.txSize(param->XbeeTxSize))?1:-1;
+ CMP(1, "XbeeRxSize") return (xbeeRelay.rxSize(param->XbeeRxSize))?1:-1;
+
+ return 1;
}
// Three word commands
if (pieces == 3) {
@@ -181,8 +205,8 @@
if (*next == 0) {
float val2 = strtod(word[2], &next);
if (*next == 0) {
- dcdc.setPwm(FAN1, val1);
- dcdc.setPwm(FAN2, val2);
+ op->dcdc.request.fan1 = val1;
+ op->dcdc.request.fan2 = val2;
return 1;
}
}
@@ -195,24 +219,323 @@
if (*next == 0) {
float val2 = strtod(word[2], &next);
if (*next == 0) {
- dcdc.setPwm(FAN1, val1);
- dcdc.setPwm(FAN2, val2);
+ op->dcdc.request.pump1 = val1;
+ op->dcdc.request.pump2 = val2;
+ return 1;
+ }
+ }
+ return -1;
+ }
+ // Set the system time (RTC)
+ CMP(1, "Time") {
+ struct tm t; // Time & date struct
+ int ret = sscanf(word[1], "%d/%d/%d", &t.tm_mon, &t.tm_mday, &t.tm_year); // Populate date
+ t.tm_year = t.tm_year - 1900; // Year mod to fix 0 index
+ t.tm_mon = t.tm_mon - 1; // Month mod to fix 0 index
+ if (ret == 3) { // All 3 items found
+ ret = sscanf(word[2], "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec); // Populate time
+ if (ret == 3) { // All 3 items found
+ set_time(mktime(&t)); // Set the RTC
+ time_t diff = time(NULL) - op->SysTime; // Get change in time from old to new
+ op->startTime += diff; // Shift the startTime to new timebase
return 1;
}
}
return -1;
}
+
+ // Profile manipulations between RAM and flash
+ CMP(1, "Profile") {
+ // Write, copy RAM to a flash sector
+ CMP(2, "Write") {
+ unsigned int index = strtoul(word[2], &next, 10); // Get index from command "profile write xxx"
+ if (index <= NUM_STORED_PROFILES && index > 0 && *next == 0) { // Check within bounds
+ bool s = Profile::saveProfile(index); // Write to flash
+ if (s) { // Successful?
+ op->profileModded = false; // Mark it as a fresh, unmodified profile
+ op->profileIndex = Profile::usingProfile(); // Change the currently loaded profile marker
+ return 1;
+ }
+ }
+ return -1;
+ }
+ // Load, read from flash to RAM
+ CMP(2, "Load") {
+ CMP(3, "default") { // The hard-coded flash profile (found in FreezeFrame.cpp)
+ Profile::loadProfile(0); // Copy default to RAM
+ op->profileIndex = Profile::usingProfile(); // Change the currently loaded profile marker
+ op->profileModded = false; // Mark it as a fresh, unmodified profile
+ return 1;
+ }
+ CMP(3, "freeze") { // Get the profile portion of the last freeze frame captured
+ if(Profile::loadProfile(-1)) { // Attemp to retrieve and copy to RAM
+ op->profileModded = false; // Mark it as a fresh, unmodified profile
+ op->profileIndex = Profile::usingProfile(); // Change the currently loaded profile marker
+ return 1;
+ }
+ }
+ int index = strtol(word[2], &next, 10); // Command was "profile load xxx"
+ if (index <= NUM_STORED_PROFILES && index >= -1 && *next == 0) { // Valid index found?
+ if (Profile::loadProfile(index)) { // Attempt to retrieve and copy to RAM
+ op->profileModded = false; // Mark it as a fresh, unmodified profile
+ op->profileIndex = Profile::usingProfile(); // Change the currently loaded profile marker
+ return 1;
+ }
+ }
+ return -1;
+ }
+ // View the profile only (NOT loaded to RAM, just display changed)
+ CMP(2, "view") {
+ CMP(3, "default") { // View the hard-coded flash profile
+ if (Profile::getProfile(&tempData.viewProfile, 0)) { // Attempt to fetch pointer
+ tempData.viewProfileNum = 0; // Mark the index of the fetched profile
+ return 1;
+ }
+ }
+ CMP(3, "freeze") { // View the profile portion only of the last captured freeze frame
+ if (Profile::getProfile(&tempData.viewProfile, -1)) { // Attempt to fetch pointer
+ tempData.viewProfileNum = -1; // Mark the index of the fetched profile
+ return 1;
+ }
+ }
+ CMP(3, "live") { // Revert to normal, live view
+ tempData.viewProfileNum = -2; // Mark live
+ tempData.viewProfile = NULL; // Clear the pointer
+ return 1;
+ }
+
+ int index = strtol(word[2], &next, 10); // Command was "profile view xxx"
+ if (index <= NUM_STORED_PROFILES && index >= -1 && *next == 0) { // Valid index found?
+ if (Profile::getProfile(&tempData.viewProfile, index)) { // Attempt to fetch pointer
+ tempData.viewProfileNum = index; // Mark the index of the fetched profile
+ return 1;
+ }
+ }
+ return -1;
+ }
+ }
}
return -1;
}
+// Called when AMS AIRs Mode message stops coming in
+Timeout timer_AIRS_CLOSED;
+void timeout_AIRS_CLOSED()
+{
+ op->signals &= ~AIRS_CLOSED;
+}
+
+// Called when Charger CAN messages stop coming in
+Timeout timer_CHARGER_DET;
+void timeout_CHARGER_DET()
+{
+ op->signals &= ~CHARGER_DET;
+}
+
+// Called when PCM messages stop coming in
+Timeout timer_FANS;
+void timeout_FANS()
+{
+ op->dcdc.request.fan1 = 0;
+ op->dcdc.request.fan2 = 0;
+}
+Timeout timer_PUMPS;
+void timeout_PUMPS()
+{
+ op->dcdc.request.pump1 = 0;
+ op->dcdc.request.pump2 = 0;
+}
+
+#define REFRESH_TIMEOUT(NAME) \
+timer_##NAME.detach(); \
+timer_##NAME.attach(&timeout_##NAME, CAN_DEVICE_TIMEOUT);
+
+bool serviceCAN(CANMessage* fromXbee)
+{
+ CANMessage msg;
+ if (fromXbee != NULL) {
+ memcpy((void*)&msg, (void*)fromXbee, sizeof(CANMessage));
+ } else {
+ if (!can.rxRead(msg)) return false;
+ }
+ // Redirect global car reset
+ if (msg.id == GLOBAL_CAR_RESET_RX_ID) msg.id = RESETCLEAR_RX_ID;
+
+ switch (msg.id) {
+ // Reset microntroller
+ case (RESET_RX_ID):
+ if (msg.len == 0) { // Length must = 0
+ NVIC_SystemReset();
+ CAN_SUCCESS
+ }
+ CAN_FAIL
+
+
+ // Clear non-volatile fault flags, then reset microcontroller
+ case (RESETCLEAR_RX_ID):
+ if (msg.len == 0) { // Length must = 0
+ FreezeFrame::clearError();
+ NVIC_SystemReset();
+ CAN_SUCCESS
+ }
+ CAN_FAIL
+
+ // Artificially capture a freeze frame
+ case (CAPTURE_RX_ID):
+ if (msg.len == 0) { // Length must = 0
+ if (!FreezeFrame::getError()) { // Only allow capture if freeze frame from the last error was read
+ if (FreezeFrame::writeFrame()) { // Capture the RAM contents to flash
+ CAN_SUCCESS
+ }
+ }
+ }
+ CAN_FAIL
+
+ // Clear fault conditions
+ case (CLEAR_RX_ID):
+ if (msg.len == 1) { // One data byte
+ if (msg.data[0] == 0) { // Clear only freeze frame error if = 0
+ FreezeFrame::clearError(); // Clear non-volatile freeze frame marker
+ CAN_SUCCESS
+ }
+ if (msg.data[0] == 1) { // Clear everything if = 1
+ runTime::clearFaults();
+ op->faultCode = 0;
+ CAN_SUCCESS
+ }
+ }
+ CAN_FAIL
+
+ // Set the time (RTC)
+ case (TIME_RX_ID):
+ if (msg.len == 6*sizeof(char)) { // 6 Bytes
+ struct tm t; // Time & date struct
+ t.tm_mon = msg.data[0]; // Month in byte[0]
+ t.tm_mday = msg.data[1]; // Day
+ t.tm_year = msg.data[2]; // Year (offset from 2000)
+ t.tm_year = t.tm_year - 1900 + 2000; // Apply year index mod and offset
+ t.tm_mon = t.tm_mon - 1; // Month index mod
+ t.tm_hour = msg.data[3]; // Get hour of time in byte[3] (24 hr format)
+ t.tm_min = msg.data[4]; // Minutes
+ t.tm_sec = msg.data[5]; // Seconds
+ set_time(mktime(&t)); // Set RTC
+ time_t diff = time(NULL) - op->SysTime; // Old time to new time change
+ op->startTime += diff; // Shift the startTime to new timebase
+ CAN_SUCCESS
+ }
+ CAN_FAIL
+
+ // RAM and flash profile manipulations
+ case (PROFILE_RX_ID):
+ if (msg.len == 2*sizeof(char)) { // 2 command bytes
+ if (msg.data[0] == 0) { // Load profile from a flash location to RAM
+ int index = msg.data[1]; // Second byte contains profile index
+ if (msg.data[1] == 0xff) index = -1; // If freeze (special case)
+ if (Profile::loadProfile(index)) { // Attempt to load (copy flash to RAM)
+ op->profileIndex = Profile::usingProfile(); // Change the currently loaded profile marker
+ op->profileModded = false; // Mark it as a fresh, unmodified profile
+ CAN_SUCCESS
+ }
+ }
+ if (msg.data[0] == 1) { // Write profile to flash from RAM
+ int index = msg.data[1]; // Get which slot to write to from message
+ if (msg.data[1] == 0xff) index = -1; // If freeze (special case)
+ bool s = Profile::saveProfile(index); // Write profile to flash slot
+ if (s) {
+ op->profileIndex = Profile::usingProfile(); // Change the currently loaded profile marker
+ op->profileModded = false; // Mark it as a fresh, unmodified profile
+ CAN_SUCCESS
+ }
+ }
+ }
+ CAN_FAIL
+
+ case FAN_CONTROL_ID:
+ if (msg.len != 2*sizeof(float)) return false;
+ REFRESH_TIMEOUT(FANS)
+ op->dcdc.request.fan1 = *((float*)((void*)(&msg.data[0])));
+ op->dcdc.request.fan2 = *((float*)((void*)(&msg.data[4])));
+ return true;
+
+ case PUMP_CONTROL_ID:
+ if (msg.len != 2*sizeof(float)) return false;
+ REFRESH_TIMEOUT(PUMPS)
+ op->dcdc.request.pump1 = *((float*)((void*)(&msg.data[0])));
+ op->dcdc.request.pump2 = *((float*)((void*)(&msg.data[4])));
+ return true;
+
+ case AMS_MODE_ID:
+ if (msg.len != sizeof(char)) return false;
+ REFRESH_TIMEOUT(AIRS_CLOSED)
+ if (msg.data[0] & 1<<2) { // AIRs closed?
+ op->signals |= AIRS_CLOSED;
+ } else {
+ op->signals &= ~AIRS_CLOSED;
+ }
+ return true;
+ case CHARGER_ERR_ID:
+ REFRESH_TIMEOUT(CHARGER_DET)
+ op->signals |= CHARGER_DET;
+ return true;
+ default:
+ break;
+ }
+ bool parsed=false;
+
+ CAN_CHANGE(chargeCurrent, PROFILE_CHARGECURRENT_RX_ID )
+ CAN_CHANGE(dischargeCurrent, PROFILE_DISCHARGECURRENT_RX_ID )
+ CAN_CHANGE(nominalCapacity, PROFILE_NOMINALCAPACITY_RX_ID )
+ CAN_CHANGE(glvBat_taps, PROFILE_GLVBATTAPS_RX_ID )
+ CAN_CHANGE(dcdcThreshold, PROFILE_DCDCONTHRESHOLD_RX_ID )
+ CAN_CHANGE(dcdcOverCurrent, PROFILE_DCDCOVERCURRENT_RX_ID )
+ CAN_CHANGE(dcdcStartDelay, PROFILE_DCDCSTARTDELAY_RX_ID )
+ CAN_CHANGE(dcdcStopDelay, PROFILE_DCDCSTOPDELAY_RX_ID )
+ CAN_CHANGE(dcdc_taps, PROFILE_DCDC_TAPS_RX_ID )
+ CAN_CHANGE(imdStartDelay, PROFILE_IMDSTARTDELAY_RX_ID )
+ CAN_CHANGE(internalOverTemp, PROFILE_INTERNALOVERTEMP_RX_ID )
+ CAN_CHANGE(CANnoAck, PROFILE_CANNOACK_RX_ID )
+ CAN_CHANGE(extendedSerial, PROFILE_EXTENDSERIAL_RX_ID )
+ CAN_CHANGE(CANtxSize, PROFILE_CANTXSIZE_RX_ID )
+ CAN_CHANGE(CANrxSize, PROFILE_CANRXSIZE_RX_ID )
+ CAN_CHANGE(SerialBaud, PROFILE_SERIALBAUD_RX_ID )
+ CAN_CHANGE(SerialTxSize, PROFILE_SERIALTXSIZE_RX_ID )
+ CAN_CHANGE(XbeeBaud, PROFILE_XBEEBAUD_RX_ID )
+ CAN_CHANGE(XbeeTxSize, PROFILE_XBEETXSIZE_RX_ID )
+ CAN_CHANGE(XbeeRxSize, PROFILE_XBEERXSIZE_RX_ID )
+
+ if (!parsed) return false;
+
+ if (msg.id == PROFILE_NOMINALCAPACITY_RX_ID ) return glvBat.changeCapacity(param->nominalCapacity)?1:-1;
+ if (msg.id == PROFILE_GLVBATTAPS_RX_ID ) return glvBat.size(param->glvBat_taps)?1:-1;
+ if (msg.id == PROFILE_DCDC_TAPS_RX_ID ) return dcdc.size(param->dcdc_taps)?1:-1;
+ if (msg.id == PROFILE_CANTXSIZE_RX_ID ) return can.txSize(param->CANtxSize)?1:-1;
+ if (msg.id == PROFILE_CANRXSIZE_RX_ID ) return can.rxSize(param->CANrxSize)?1:-1;
+ if (msg.id == PROFILE_SERIALBAUD_RX_ID ) pc.baud(param->SerialBaud);
+ if (msg.id == PROFILE_SERIALTXSIZE_RX_ID ) return (pc.txBufferSetSize(param->SerialTxSize) == 0)?1:-1;
+ if (msg.id == PROFILE_XBEEBAUD_RX_ID ) xbeeRelay.baud(param->XbeeBaud);
+ if (msg.id == PROFILE_XBEETXSIZE_RX_ID ) return (xbeeRelay.txSize(param->XbeeTxSize))?1:-1;
+ if (msg.id == PROFILE_XBEERXSIZE_RX_ID ) return (xbeeRelay.rxSize(param->XbeeRxSize))?1:-1;
+
+ return true;
+}
+// Check for incoming messages from the xbees, relay them to the CAN function and send them out on the bus
+bool receiveMsgXbee()
+{
+ CANMessage msg;
+ if (xbeeRelay.receive(msg)) { // Incoming CAN message string received
+ if (!can.txWrite(msg)) op->faultCode |= CAN_FAULT; // Send it out on the CAN bus
+ serviceCAN(&msg); // Send it into the local serviceCAN routine
+ return true;
+ } else return false;
+}
+
void inCommands::thread_getInputs(void const* args)
{
while(1) {
- inCommands::serviceCAN();
- inCommands::receiveMsgXbee();
+ serviceCAN(0);
+ receiveMsgXbee();
- int ret = inCommands::serviceSerial();
+ int ret = serviceSerial();
if (ret == -1) tempData.parseGoodChar = 'x';
if (ret == 1) tempData.parseGoodChar = 251;
osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<2); // Signal watchdog thread
--- a/inCommands/inCommands.h Thu Jan 22 07:59:48 2015 +0000
+++ b/inCommands/inCommands.h Sat Feb 07 08:54:51 2015 +0000
@@ -3,12 +3,7 @@
#include "IOobjects.h"
-const int DEVICE_CAN_TIMEOUT = 0.25; // Comms. lost to external device if a message is not received within 200 ms
-
namespace inCommands {
- bool serviceCAN(CANMessage* fromXbee=0);
- bool receiveMsgXbee();
- int serviceSerial();
void thread_getInputs(void const* args);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inCommands/inMacros.h Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,58 @@
+#ifndef INMACROS_H
+#define INMACROS_H
+
+/*************** Serial String Input ****************/
+
+// Compare string to a word in the serial input, shorthand
+#define CMP(w, string) if (!strcasecmp(word[w-1], string))
+
+// Macro linking a serial string to a change function, 2 word command
+#define CHANGE_VAR(STR, NAME) \
+CMP(1, STR) { \
+ float f = strtof(word[1], &next); \
+ if (*next == 0) { \
+ if (param->change_##NAME(f)) { \
+ op->profileModded=true; \
+ parsed=true; \
+ } \
+ } \
+}
+
+/*************** CAN Message Profile Out ***************/
+#include "outMacros.h"
+
+// Send out an item from the profile
+#define CAN_PROFILE(DATA, ID) \
+SEND_CAN_SINGLE(param->DATA, ID);
+
+// Replies for commands that do not return data
+#define CAN_SUCCESS \
+msg.data[0]=1; \
+SEND_CAN(1, msg.id) \
+return 1;
+
+#define CAN_FAIL \
+msg.data[0]=0; \
+SEND_CAN(1, msg.id) \
+return 0;
+
+/*********************** CAN Bus Input ******************************/
+
+template <class Type>
+Type CAN_EXTRACT(Type type, CANMessage& msg) {
+ return *((Type*)((void*)(&msg.data[0])));
+}
+
+#define CAN_CHANGE(NAME, ID) \
+if (msg.id == ID) { \
+ if (msg.len == sizeof(param->NAME)) { \
+ if (param->change_##NAME(CAN_EXTRACT(param->NAME, msg))) { \
+ op->profileModded=true; \
+ parsed = true; \
+ } \
+ } \
+ CAN_PROFILE(NAME, ID) \
+}
+
+
+#endif
--- a/main.cpp Thu Jan 22 07:59:48 2015 +0000
+++ b/main.cpp Sat Feb 07 08:54:51 2015 +0000
@@ -3,64 +3,104 @@
#include "outDiagnostics.h"
#include "inCommands.h"
-//#define DEBUG_MODE
-
-void thread_watchDog(void const* args) {
+void thread_watchDog(void const* args)
+{
tempData.wdtThreadId = Thread::gettid();
-#ifdef DEBUG_MODE
- wdt.kick();
-#else
wdt.kick(WDT_TIME);
-#endif
while(1) {
- Thread::signal_wait(0x1F);
+ Thread::wait(100);
+ //Thread::signal_wait(0x1F);
wdt.kick();
- }
+ }
}
+// Startup code to initialize interfaces and fetch the profile
+void init()
+{
+ char str[100];
+ sprintf(str, "\033[2J\033[2J\r\n"); // Clear screen
-int main() {
- pc.baud(BAUD);
- pc.format(8, SerialBase::None, 2); // 2 Stop bits, reduce bad serial packets
- pc.printf("\033[2J\033[2J\r\n"); // Clear screen
-
- can.mode(FIFO);
+ // GENERATE RESET TYPE MESSAGE
+ bool normalReset = true;
+ if (wdt.checkFlag()) { // Did a watchdog reset occur?
+ wdt.clearFlag(); // Clear flag
+ op->faultCode |= WATCHDOG; // Record fault
+ sprintf(str, "System Mgmt Watchdog Reset\r\n");
+ normalReset=false;
+ }
+ if (LPC_SC->RSID & (1<<3)) { // Did a brownout reset occur?
+ LPC_SC->RSID = (1<<3); // Clear flag
+ op->faultCode |= BROWNOUT; // Record fault
+ sprintf(str, "System Mgmt Brownout Reset\r\n");
+ normalReset=false;
+ }
+ if (normalReset) sprintf(str, "System Mgmt Reset\r\n"); // Normal reset message
+
+ // SET INTERUPT PRIORITIES
NVIC_SetPriority(TIMER0_IRQn, 0);
NVIC_SetPriority(PWM1_IRQn, 1);
NVIC_SetPriority(CAN_IRQn, 2);
NVIC_SetPriority(UART0_IRQn, 3);
NVIC_SetPriority(TIMER3_IRQn, 4);
- bool normalReset = true;
- // Did a watchdog reset occur since last power cycle?
- if (wdt.checkFlag()) {
- wdt.clearFlag(); // Clear flag
- data.watchdogReset = true;
- pc.printf("Sys Mgmt Watchdog Reset\r\n");
- normalReset=false;
+ // SET SYSTEM TIME (RTC)
+ int t = time(NULL); // Get RTC time
+ if (t == 0 || t == -1) set_time(0); // Seed the timer if it was never setup
+ tempData.timeSynced = false; // Time unsynced on reset, use AMS as master clock
+ op->startTime = time(NULL); // Capture the startup time
+ op->SysTime = time(NULL); // Caputre the system time
+
+ // LOAD LAST-USED CONFIGURATION PROFILE
+ if (!Profile::loadStartUp()) {
+ strncat(str, "Profile load Failed, using default\r\n", 99);
}
- // Did a brownout reset occur since last power cycle?
- if (LPC_SC->RSID & (1<<3)) {
- LPC_SC->RSID = (1<<3); // Clear flag
- pc.printf("Sys Mgmt Brownout Reset\r\n");
- normalReset=false;
- }
- // Print normal reset string
- if (normalReset) pc.printf("Sys Mgmt Reset\r\n");
-
+ op->profileIndex = Profile::usingProfile();
+ wdt.kick(); // Kick watchdog timer before the wait
+ wait(0.5); // Wait so above messages aren't immediately over-written
+
+ // ALLOCATE/RESIZE BUFFERS
+ glvBat.size(param->glvBat_taps);
+ dcdc.size(param->dcdc_taps);
+ can.txSize(param->CANtxSize);
+ can.rxSize(param->CANrxSize);
+ pc.baud(param->SerialBaud);
+ pc.txBufferSetSize(param->SerialTxSize);
+ xbeeRelay.baud(param->XbeeBaud);
+ xbeeRelay.txSize(param->XbeeTxSize);
+ xbeeRelay.rxSize(param->XbeeRxSize);
+
+ // INITIAL SETUP, LINK PROFILE VARIABLES
+ glvBat.changeCapacity(param->nominalCapacity);
+ glvBat.setup(&(param->chargeCurrent), &(param->dischargeCurrent));
+ dcdc.setup(&(param->dcdcThreshold), &(param->dcdcOverCurrent), &(param->dcdcStartDelay), &(param->dcdcStopDelay));
+ AMSlatch.setup(&(param->amsStartDelay));
+ IMDlatch.setup(&(param->imdStartDelay));
+ AMSlatch.delayStart();
+ IMDlatch.delayStart();
+
+ // SETUP SERIAL PORT
+ pc.format(8, SerialBase::None, 2); // 2 Stop bits, reduce bad serial packets
+ str[99] = 0;
+ pc.printf(str);
+}
+
+int main()
+{
+ init();
+
// Start the watchdog check-in thread
Thread watchdogCheck(thread_watchDog, 0, osPriorityRealtime, 128);
-
+
// Start the 100Hz data timer (priotity high)
RtosTimer sample(runTime::thread_sample, osTimerPeriodic);
sample.start(FAST_LOOP*1000);
-
+
// Start the serial, CAN threads
Thread serial_out(outDiagnostics::thread_serialOut, 0, osPriorityBelowNormal);
Thread can_out(outDiagnostics::thread_canOut, 0, osPriorityBelowNormal, 512);
-
+
// Start the input polling thread
Thread getInputs(inCommands::thread_getInputs, 0, osPriorityLow);
-
+
// Main task
while(1) {
runTime::gather(0);
--- a/outDiagnostics/outDiagnostics.cpp Thu Jan 22 07:59:48 2015 +0000
+++ b/outDiagnostics/outDiagnostics.cpp Sat Feb 07 08:54:51 2015 +0000
@@ -1,13 +1,18 @@
#include "outDiagnostics.h"
+#include "outMacros.h"
-osThreadId serialID = 0; // RTOS thread ID of thread_serialOut
-const int max_charsPerLine = 80; // Max chars per line of printed information
+Timer serialLoop;
+Timer CANloop;
+int serialTime_ms = 0;
+int CANtime_ms = 0;
+const int max_charsPerLine = 80; // Max chars per line of printed information
// Use the txEmpty interrupt from MODSERIAL to pace the thread so it always runs as fast as possible
-void empty(MODSERIAL_IRQ_INFO *q) {
+osThreadId serialID=0;
+void empty(MODSERIAL_IRQ_INFO *q)
+{
osSignalSet(serialID, 1);
}
-
// Output a string to the MODSERIAL tx buffer, wait when buffer full
void AddtoBuffer(char *str, bool newline=true)
{
@@ -39,182 +44,347 @@
AddtoBuffer(line);
}
-
-// Macros for working with the strings
-#define ADD_SPRINTF_LINE padCenter(max_charsPerLine, temp, ' '); // Cetner the string, then add newlines, and add to chunk
-#define DIVIDER_LINE padCenter(max_charsPerLine, "", 196); // Generate a line full of divider char 196, add to chunk
-#define TITLE(string) padCenter(max_charsPerLine, string, 196); // Generate a title line (centered, surrounded by char 196), add to chunk
-#define BLANK_LINE padCenter(max_charsPerLine, "", ' '); // Generate a line full of spaces (blank), add to chunk
-#define BOOL(VAR) (VAR)?"ERR":"OK"
-
// Generates the serial dashboard, uses MODSERIAL, self-paced (thread yields when buffer is full, resumes when empty)
void outDiagnostics::thread_serialOut(void const *args)
{
- serialID = Thread::gettid(); // Record thread ID so empty() can signal this thread
- char temp[max_charsPerLine+5]; // String buffer to sprintf into, max 1 line
+ serialID = Thread::gettid(); // Record thread ID so empty() can signal this thread
+ char temp[max_charsPerLine+5]; // String buffer to sprintf into, max 1 line
+ pc.attach(&empty, MODSERIAL::TxEmpty); // Attach the tx empty interrupt which paces this thread
+ pc.printf("\033[2J"); // Clear the screen to get rid of reset message
- pc.attach(&empty, MODSERIAL::TxEmpty); // Attach the tx empty interrupt which paces this thread
+ // Use these bools to track changes in display mode between iterations
+ bool lastModeExtended = param->extendedSerial;
+ bool inExtendedMode = param->extendedSerial;
- tempData.parseGoodChar = ' ';
- tempData.inputStr[0] = 0;
-
- Timer serialLoop; // Timer to track the serial loop time, since this thread paces itself
- int serialTime_ms = 0;
+ // For determining where the data displayed is coming from
+ bool freeze = false; // Not a freeze frame
+ bool notLiveProfile = false; // Live data
+
+ const char barSpace[4] = { ' ', 179, ' ', 0 }; // Commonly used string with char 179
+ OperatingInfo* dashOp = op;
+ Profile* dashParam = param;
+
serialLoop.reset();
- serialLoop.start(); // Start the counter for tracking serial loop time
-
- //const char barSpace[4] = { ' ', 179, ' ', 0 }; // Commonly used string with char 179
+ serialLoop.start(); // Start the counter for tracking serial loop time
while(1) {
-
- serialTime_ms = serialLoop.read_ms(); // Update loop timer, reset for next loop
+ serialTime_ms = serialLoop.read_ms(); // Update loop timer, reset for next loop
serialLoop.reset();
+ // Update display mode, change detection used at end of while(1) loop
+ lastModeExtended = inExtendedMode;
+ inExtendedMode = param->extendedSerial;
+
+ // Determine whether to display freeze frame, change pointers accordingly
+ if (tempData.freeze != NULL) {
+ freeze = true; // Indicate showing freeze
+ dashOp = &(tempData.freeze->op.op); // Point to freeze
+ dashParam = &(tempData.freeze->param.param); // Point to freeze
+
+ // Determine whether to display a non-live profile, change pointers accordingly
+ } else if (tempData.viewProfile != NULL && tempData.viewProfileNum != -2) {
+ dashParam = tempData.viewProfile; // Point to other profile
+ notLiveProfile = true; // Indicate notLive
+
+ // Show live data only
+ } else {
+ freeze = false; // Not freeze
+ notLiveProfile = false; // Live profile
+ dashOp = op; // Point to live RAM data
+ dashParam = param; // Point to live RAM data
+ }
+
sprintf(temp, "\033[0;0H\033[0;0H");
- AddtoBuffer(temp, false); // Move to 0,0, do not append newline
-
+ AddtoBuffer(temp, false); // Move to 0,0, do not append newline
+
DIVIDER_LINE
TITLE(" Penn Electric Racing - REV0 System Management Controller Dashboard ")
DIVIDER_LINE
-
+
int tempLength=0;
tempLength += sprintf(temp, "Command Input:%c %s%c", tempData.parseGoodChar, tempData.inputStr, 176); // Command input: print header, reply, input string, and cursor marker
for (int i = 0; i < max_charsPerLine - tempLength - 1; i++) { // Fill in the rest of the line with blanks
tempLength += sprintf(temp+tempLength, " "); // Append spaces
}
AddtoBuffer(temp); // Add this to the chunk
-
- TITLE(" GLV Battery ")
- BLANK_LINE
- sprintf(temp, "Current: %4.3fA Capacity: %4.3fAh", data.glvCurrent, data.glvCapacity);
+
+ const char profile[NUM_STORED_PROFILES+2][8] = {"Freeze", "Default", "1", "2", "3"};
+ if (inExtendedMode) {
+
+ // Parameters Section
+ snprintf(temp, max_charsPerLine, " Configuration Parameters %s%s%s%s", freeze?"(Viewing Freeze of Last Fault) ":"", (notLiveProfile && !freeze)?" (Viewing Profile ":"", (notLiveProfile && !freeze)?profile[tempData.viewProfileNum+1]:"", (notLiveProfile && !freeze)?") ":"");
+ TITLE(temp)
+ snprintf(temp, max_charsPerLine, "GLVchar: %5.2fA%sGLVdisch: %5.2fA%sGLVnomCap: %5.2fAh%sGLVtaps: %3d", dashParam->chargeCurrent,barSpace, dashParam->dischargeCurrent,barSpace, dashParam->nominalCapacity,barSpace, dashParam->glvBat_taps);
+ ADD_SPRINTF_LINE
+ snprintf(temp, max_charsPerLine, "dcdcThres: %5.2fA%sdcdcOver: %5.2fA%sdcdcStart: %5.2fs %sdcdcStop: %5.2fs", dashParam->dcdcThreshold,barSpace, dashParam->dcdcOverCurrent,barSpace, dashParam->dcdcStartDelay,barSpace, dashParam->dcdcStopDelay);
+ ADD_SPRINTF_LINE
+ snprintf(temp, max_charsPerLine, "dcdcTaps: %3d %simdStart: %5.1fs%samsStart: %5.1fs %sIntOverT: %5.1fC", dashParam->dcdc_taps,barSpace, dashParam->imdStartDelay,barSpace, dashParam->amsStartDelay,barSpace, dashParam->internalOverTemp);
+ ADD_SPRINTF_LINE
+ snprintf(temp, max_charsPerLine, "CANtxSize: %4d %sCANrxSize: %4d %sSerialBaud: %6d%sSerialTx: %5d", dashParam->CANtxSize,barSpace, dashParam->CANrxSize,barSpace, dashParam->SerialBaud,barSpace, dashParam->SerialTxSize);
+ ADD_SPRINTF_LINE
+ snprintf(temp, max_charsPerLine, "XbeeBaud: %6d %sXbeeTxSize: %4d%sXbeeRxSize: %4d %s CANack: %5s", dashParam->XbeeBaud, barSpace, dashParam->XbeeTxSize,barSpace, dashParam->XbeeRxSize,barSpace, dashParam->CANnoAck?"NOACK":"ACK");
+ ADD_SPRINTF_LINE
+
+ BLANK_LINE
+ }
+ snprintf(temp, max_charsPerLine, " Operating Info %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+ TITLE(temp)
+
+ // Operating mode
+ const char modeStr[3][8] = {"FAULT", "OKAY", "INVALID"};
+ tempLength = 0;
+ tempLength += sprintf(temp+tempLength, "Sloop: %3dms CANloop: %3dms Time: %s", serialTime_ms, CANtime_ms, ctime(&dashOp->SysTime));
+ tempLength--;
+ char modeN = dashOp->mode;
+ if (modeN == FAULT) modeN=0;
+ else if (modeN == OKAY) modeN=1;
+ else modeN=2;
+ tempLength += sprintf(temp+tempLength, " Uptime: %5ds", dashOp->SysTime - dashOp->startTime);
ADD_SPRINTF_LINE
- sprintf(temp, "Amphours: %4.3fAh SOC: %5.3f Overcurrent: %s", data.glvAmphours, data.glvSOC, BOOL(data.glvOverCurrent));
+ tempLength = snprintf(temp, max_charsPerLine, "Profile: %7s%10s Mode: %7s%6s Faults: ", profile[dashOp->profileIndex+1], dashOp->profileModded?", MODIFIED":"", modeStr[modeN], dashOp->faultCode?"+ERROR":"");
+
+ // Fault codes
+ const char topErrStr[12][20] = {"WATCHDOG", "BROWNOUT", "CAN_FAULT", "INT_OVER_TEMP", "IMD_LATCH", "AMS_LATCH", "IMD_FAULT", "DCDC_FAULT", "GLVBAT_FAULT", "FREEZE_FRAME"};
+ int numFaults = 0;
+ for (int i = 0; i < sizeof(dashOp->faultCode)*8; i++) {
+ if (dashOp->faultCode & (1<<i)) numFaults++;
+ }
+ if (numFaults == 0) tempLength+=sprintf(temp+tempLength, "No Faults"); // Start new appending chain string, faults not present
+ else tempLength+=sprintf(temp+tempLength, "(%d 0x%04x)", numFaults, dashOp->faultCode); // Start new appending chain string, faults present
ADD_SPRINTF_LINE
+ tempLength = 0;
+ temp[0] = 0;
- BLANK_LINE
- TITLE(" DC-DC Converter ")
+ // Print max number of strings that will fit per line, then dump line and continue till finished all error flags
+ int num = 0;
+ for (int i = 0; i < 29; i++) {
+ if (dashOp->faultCode & (1<<i)) { // Fault found
+ // Room for fault string?
+ if (max_charsPerLine-tempLength > strlen(topErrStr[i])+2) {
+ // Yes, append
+ tempLength += sprintf(temp+tempLength, "%s ", topErrStr[i]);
+ num++;
+ } else {
+ // No, Dump then start new line
+ ADD_SPRINTF_LINE
+ tempLength = 0;
+ tempLength += sprintf(temp+tempLength, "%s ", topErrStr[i]);
+ num++;
+ }
+ }
+ if (num >= numFaults || numFaults == 0) {
+ // Done printing all faults
+ tempLength = 0;
+ num = 0;
+ if (numFaults != 0) {
+ ADD_SPRINTF_LINE
+ }
+ break;
+ }
+ }
BLANK_LINE
- char DCDC = data.dcdcStatus;
- char dcdcModesStr[5][12] = {"INVALID","POWER-UP","POWER-DOWN","ON","OFF"};
- int dcdcMode = 0;
- if (DCDC & PowerUp) dcdcMode = 1;
- else if (DCDC & PowerDown) dcdcMode = 2;
- else if (DCDC & SetOn) dcdcMode = 3;
- else if (!(DCDC & SetOn)) dcdcMode = 4;
- sprintf(temp, "Active: %3s Mode: %10s AIRS: %6s StatusByte: 0x%02x", (DCDC & ConvOn)?"YES":"NO", dcdcModesStr[dcdcMode], CANdata.airsClosed?"CLOSED":"OPEN", DCDC);
- ADD_SPRINTF_LINE
- sprintf(temp, "Current: %5.2fA Overcurrent: %3s SensorFault: %3s", data.dcdcCurrent, BOOL(DCDC & OverCurrent), BOOL(DCDC & SensorFault));
- ADD_SPRINTF_LINE
- sprintf(temp, "StartFault: %3s StopFault: %3s CritErrors: %3s", BOOL(DCDC & FailStart), BOOL(DCDC & FailStop), BOOL(data.dcdcError));
+ snprintf(temp, max_charsPerLine, "ShtdwnCrct: %6s %s AIRs: %6s %s Charger: %4s %s IntTemp: %5.1f", (dashOp->signals & SHUTDOWN_CLOSED)?"CLOSED":"OPEN", barSpace, (dashOp->signals & AIRS_CLOSED)?"CLOSED":"OPEN", barSpace, (dashOp->signals & CHARGER_DET)?"DET":"NDET", barSpace, dashOp->internalTemp);
ADD_SPRINTF_LINE
BLANK_LINE
- TITLE(" PWM Channels ")
+ snprintf(temp, max_charsPerLine, " GLV Battery %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+ TITLE(temp)
+ sprintf(temp, "Current: %4.3fA %s Cap: %4.3fAh %s Ah: %4.3fAh %s SOC: %5.3f", dashOp->glvBat.current, barSpace, dashOp->glvBat.capacity, barSpace, dashOp->glvBat.Ah, barSpace, dashOp->glvBat.SOC);
+ ADD_SPRINTF_LINE
+ // GLV Fault codes
+ const char glvBatErrStr[12][20] = {"OVER_CHARGE_I", "OVER_DISCHARGE_I", "CAP_INIT", "SOC_INIT" };
+ numFaults = 0;
+ for (int i = 0; i < sizeof(dashOp->glvBat.error)*8; i++) {
+ if (dashOp->glvBat.error & (1<<i)) numFaults++;
+ }
+ if (numFaults == 0) tempLength+=sprintf(temp+tempLength, "No Faults"); // Start new appending chain string, faults not present
+ else tempLength+=sprintf(temp+tempLength, "(%d 0x%04x)", numFaults, dashOp->glvBat.error); // Start new appending chain string, faults present
+ ADD_SPRINTF_LINE
+ tempLength = 0;
+ temp[0] = 0;
+
+ // Print max number of strings that will fit per line, then dump line and continue till finished all error flags
+ num = 0;
+ for (int i = 0; i < 29; i++) {
+ if (dashOp->glvBat.error & (1<<i)) { // Fault found
+ // Room for fault string?
+ if (max_charsPerLine-tempLength > strlen(glvBatErrStr[i])+2) {
+ // Yes, append
+ tempLength += sprintf(temp+tempLength, "%s ", glvBatErrStr[i]);
+ num++;
+ } else {
+ // No, Dump then start new line
+ ADD_SPRINTF_LINE
+ tempLength = 0;
+ tempLength += sprintf(temp+tempLength, "%s ", glvBatErrStr[i]);
+ num++;
+ }
+ }
+ if (num >= numFaults || numFaults == 0) {
+ // Done printing all faults
+ tempLength = 0;
+ num = 0;
+ if (numFaults != 0) {
+ ADD_SPRINTF_LINE
+ }
+ break;
+ }
+ }
+
BLANK_LINE
- sprintf(temp, "Actual: FAN1: %5.3f FAN2: %5.3f PUMP1: %5.3f PUMP2: %5.3f", data.dcdcFan1Duty, data.dcdcFan2Duty, data.dcdcPump1Duty, data.dcdcPump2Duty);
+ snprintf(temp, max_charsPerLine, " DC-DC Converter %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+ TITLE(temp)
+ char DCDC = dashOp->dcdc.status;
+ const char dcdcModesStr[5][12] = {"INVALID","POWER-UP","POWER-DOWN","SET-ON","SET-OFF"};
+ int dcdcMode = 0;
+ if (DCDC & POWER_UP) dcdcMode = 1;
+ else if (DCDC & POWER_DOWN) dcdcMode = 2;
+ else if (DCDC & SET_ON) dcdcMode = 3;
+ else if (!(DCDC & SET_ON)) dcdcMode = 4;
+ sprintf(temp, "Active: %3s %s Mode: %10s %s Status: 0x%02x", (DCDC & CONV_ON)?"YES":"NO", barSpace, dcdcModesStr[dcdcMode], barSpace, DCDC);
ADD_SPRINTF_LINE
- sprintf(temp, "Requestd: FAN1: %5.3f FAN2: %5.3f PUMP1: %5.3f PUMP2: %5.3f", CANdata.dcdcFan1Duty, CANdata.dcdcFan2Duty, CANdata.dcdcPump1Duty, CANdata.dcdcPump2Duty);
+ sprintf(temp, "Current: %5.2fA %s Overcurrent: %3s %s SensorFault: %3s", dashOp->dcdc.current,barSpace, (DCDC & OVER_CURRENT)?"ERR":"OK",barSpace, (DCDC & SENSOR_FAULT)?"ERR":"OK");
ADD_SPRINTF_LINE
BLANK_LINE
- TITLE(" IMD ")
- BLANK_LINE
- const char IMDstr[7][12] = {"OFF","NORMAL","UNDERVOLT","SPEEDSTART","ERROR","GROUNDFLT","INVALID"};
- sprintf(temp, "Status: %10s Resistance: %7.0fkohm CritError: %3s",IMDstr[data.imdStatus], data.imdResistance/1e3, BOOL(data.imdError));
+ snprintf(temp, max_charsPerLine, " DC-DC PWM Channels %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+ TITLE(temp)
+ sprintf(temp, "Actual: FAN1: %5.3f FAN2: %5.3f %s PUMP1: %5.3f PUMP2: %5.3f", dashOp->dcdc.actual.fan1, dashOp->dcdc.actual.fan2, barSpace, dashOp->dcdc.actual.pump1, dashOp->dcdc.actual.pump2);
+ ADD_SPRINTF_LINE
+ sprintf(temp, "Requested: FAN1: %5.3f FAN2: %5.3f %s PUMP1: %5.3f PUMP2: %5.3f", dashOp->dcdc.request.fan1, dashOp->dcdc.request.fan2, barSpace, dashOp->dcdc.request.pump1, dashOp->dcdc.request.pump2);
ADD_SPRINTF_LINE
BLANK_LINE
- TITLE(" Latch Circuit Monitors ")
- BLANK_LINE
- char AMSerr = data.AMSlatchError;
- char IMDerr = data.IMDlatchError;
- sprintf(temp, "AMS - OK: %4s Latch: %3s SoftFault: %3s HardFault: %3s", (AMSerr & 1)?"LOW":"HIGH", BOOL(AMSerr & 2), BOOL(AMSerr & 4), BOOL(AMSerr & 8));
- ADD_SPRINTF_LINE
- sprintf(temp, "IMD - OK: %4s Latch: %3s SoftFault: %3s HardFault: %3s", (IMDerr & 1)?"LOW":"HIGH", BOOL(IMDerr & 2), BOOL(IMDerr & 4), BOOL(IMDerr & 8));
+ snprintf(temp, max_charsPerLine, " IMD %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+ TITLE(temp)
+ const char IMDstr[7][12] = {"OFF","NORMAL","UNDERVOLT","SPEEDSTART","ERROR","GROUNDFLT","INVALID"};
+ sprintf(temp, "Status: %10s Resistance: %7.0fKohm Error: %3s", IMDstr[dashOp->imd.status], dashOp->imd.resistance/1.0e3, dashOp->imd.error?"ERR":"OK");
ADD_SPRINTF_LINE
BLANK_LINE
- TITLE(" Shutdown Switches ")
+ snprintf(temp, max_charsPerLine, " Latch Monitors %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+ TITLE(temp)
+ char AMSerr = dashOp->latch.ams;
+ char IMDerr = dashOp->latch.imd;
+ sprintf(temp, "AMS - OK: %4s Latch: %3s SoftFault: %3s HardFault: %3s", (AMSerr & OK_FAULT)?"LOW":"HIGH", (AMSerr & LATCHED_HARD)?"ERR":"OK", (AMSerr & LATCHED_SOFT)?"ERR":"OK", (AMSerr & HARD_FAULT)?"ERR":"OK");
+ ADD_SPRINTF_LINE
+ sprintf(temp, "IMD - OK: %4s Latch: %3s SoftFault: %3s HardFault: %3s", (IMDerr & OK_FAULT)?"LOW":"HIGH", (IMDerr & LATCHED_HARD)?"ERR":"OK", (IMDerr & LATCHED_SOFT)?"ERR":"OK", (IMDerr & HARD_FAULT)?"ERR":"OK");
+ ADD_SPRINTF_LINE
+
BLANK_LINE
- char switches = data.switchState;
+ snprintf(temp, max_charsPerLine, " Shutdown Switches %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+ TITLE(temp)
+ char switches = dashOp->switchState;
const char switchNames[12][26] = {"FUSE","AMS LATCH","IMD LATCH","PCM RELAY","BRAKE PLAUSIBILITY RELAY","LEFT E-STOP","INERTIA SWITCH","BRAKE OVER-TRAVEL SWITCH","COCKPIT E-STOP","RIGHT E-STOP","HVD","TSMS"};
if (switches == 0) sprintf(temp, "All switches are CLOSED.");
else sprintf(temp, "%s is OPEN.", switchNames[switches-1]);
ADD_SPRINTF_LINE
BLANK_LINE
- TITLE(" Telemetry ")
- BLANK_LINE
- sprintf(temp, "Channel 1 - MessagesIn: %5d MessagesOut: %5d", xbeeRelay.counterX1in, xbeeRelay.counterX1out);
+ snprintf(temp, max_charsPerLine, " Telemetry %s", freeze?"(Viewing Freeze of Last Fault) ":"");
+ TITLE(temp)
+ sprintf(temp, "Channel 1 - MsgIn: %5d MsgOut: %5d %5.2f", dashOp->xbee1.msgIn, dashOp->xbee1.msgOut, dashOp->xbee1.rateOut);
ADD_SPRINTF_LINE
- sprintf(temp, "Channel 2 - MessagesIn: %5d MessagesOut: %5d", xbeeRelay.counterX2in, xbeeRelay.counterX2out);
+ sprintf(temp, "Channel 2 - MsgIn: %5d MsgOut: %5d %5.2f", dashOp->xbee2.msgIn, dashOp->xbee2.msgOut, dashOp->xbee2.rateOut);
ADD_SPRINTF_LINE
- BLANK_LINE
- TITLE(" Miscellaneous ")
- BLANK_LINE
- sprintf(temp, "Temp: %5.1fC %3s canFault: %3s Watchdog: %3s ErrorCode: 0x%02x SerialTime: %3dms", data.internalTemp, BOOL(data.internalOverTemp), BOOL(data.canFault), BOOL(data.watchdogReset), data.errorFrame, serialTime_ms);
- ADD_SPRINTF_LINE
-
- // Erase screen every 5sec to remove glitches
- static int count=0;
- if (count%50==0) {
- pc.printf("\033[2J"); // Clear the screen
+ // Erase screen every few counts to remove glitches
+ static int count = 0;
+ if (count % 50 == 0 || lastModeExtended != inExtendedMode) {
+ sprintf(temp, "\033[2J");
+ AddtoBuffer(temp, false); // Clear the screen
}
count++;
}
}
-// Macros to streamline can bus message formatting and sending
-#define SEND_CAN(LEN, ID) msg.len=LEN; msg.id=ID; msg.type = CANData; msg.format = CANStandard; if (!can.txWrite(msg)) data.canFault = true; xbeeRelay.send(msg);
-#define FLOAT(DATA, ID) *((float*)((void*)&msg.data[0])) = DATA; SEND_CAN(sizeof(float), ID)
-#define UINT(DATA, ID) *((uint32_t*)((void*)&msg.data[0])) = DATA; SEND_CAN(sizeof(uint32_t), ID)
-#define FLOAT_PAIR(DATA1, DATA2, ID) *((float*)((void*)&msg.data[0])) = DATA1; *((float*)((void*)&msg.data[4])) = DATA2; SEND_CAN(2*sizeof(float), ID)
-#define UINT_PAIR(DATA1, DATA2, ID) *((uint32_t*)((void*)&msg.data[0])) = DATA1; *((uint32_t*)((void*)&msg.data[4])) = DATA2; SEND_CAN(2*sizeof(uint32_t), ID)
-#define CHAR(DATA, ID) msg.data[0] = DATA; SEND_CAN(sizeof(char), ID)
-
void outDiagnostics::thread_canOut(void const *args)
{
- CANMessage msg;
+ bool lastCANnoAck = param->CANnoAck; // Get initial noAck mode setting
+ bool thisCANnoAck = param->CANnoAck;
+
+ if (param->CANnoAck) { // Turn on noack mode
+ can.mode(NoAck);
+ } else { // Setup normal mode
+ can.mode(Normal);
+ }
+ can.mode(FIFO);
+
+ CANloop.reset();
+ CANloop.start();
+
while(1) {
- // Sys Mgmt Error Frame
- CHAR(data.errorFrame, SYS_ERROR_ID)
+ CANtime_ms = CANloop.read_ms();
+ CANloop.reset();
+
+ // Update last and this variables to check for changes in noAck mode
+ lastCANnoAck = thisCANnoAck;
+ thisCANnoAck = param->CANnoAck;
+ if (thisCANnoAck && !lastCANnoAck) { // NoAck mode turn on
+ can.mode(NoAck);
+ can.mode(FIFO);
+ can.txFlush(); // Must flush buffer when switching modes, or else buffer gets stuck
+ }
+ if (!thisCANnoAck && lastCANnoAck) { // NoAck mode turn off
+ can.mode(Normal);
+ can.mode(FIFO);
+ can.txFlush(); // Must flush buffer when switching modes, or else buffer gets stuck
+ }
+
+ // OPERATING DIAGNOSTICS
+ // Error Frame
+ CAN_SINGLE(faultCode, SYS_ERROR_ID)
+
+ // Mode
+ CAN_SINGLE(mode, SYS_MODE_ID)
+
+ // Flags
+ CAN_SINGLE(signals, SYS_FLAGS_ID)
+
+ // Profile
+ char byte = (op->profileIndex != -1) ? op->profileIndex : 1<<6; // Mark the second to last bit of the byte if using Freeze profile (data[0]=64 for freeze, data[0]=0 for default)
+ byte |= (op->profileModded) ? 1<<7 : 0; // Mark the last bit of the byte if the profile was modified (OR'd with profile id from above)
+ SEND_CAN_SINGLE(byte, SYS_PROFILE_ID);
+
+ // Time
+ CAN_PAIR(SysTime, startTime, SYS_TIME_ID)
// Xbee1 Counter
- UINT_PAIR(xbeeRelay.counterX1in, xbeeRelay.counterX1out, SYS_XBEE1_ID)
+ CAN_PAIR(xbee1.msgIn, xbee1.msgOut, SYS_XBEE1_MSG_ID)
// Xbee2 Counter
- UINT_PAIR(xbeeRelay.counterX2in, xbeeRelay.counterX2out, SYS_XBEE2_ID)
+ CAN_PAIR(xbee2.msgIn, xbee2.msgOut, SYS_XBEE2_MSG_ID)
// Internal temperature
- FLOAT(data.internalTemp, SYS_TEMP_ID)
+ CAN_SINGLE(internalTemp, SYS_TEMP_ID)
// GLV Battery
- FLOAT(data.glvCurrent, SYS_GLV_CURRENT_ID)
- FLOAT(data.glvCapacity, SYS_GLV_CAPACITY_ID)
- FLOAT(data.glvAmphours, SYS_GLV_AH_ID)
- FLOAT(data.glvSOC, SYS_GLV_SOC_ID)
-
+ CAN_SINGLE(glvBat.current, SYS_GLV_CURRENT_ID)
+ CAN_SINGLE(glvBat.capacity, SYS_GLV_CAPACITY_ID)
+ CAN_SINGLE(glvBat.Ah, SYS_GLV_AH_ID)
+ CAN_SINGLE(glvBat.SOC, SYS_GLV_SOC_ID)
+ CAN_SINGLE(glvBat.error, SYS_GLV_ERROR_ID)
+
// DC-DC Converter
- FLOAT(data.dcdcCurrent, SYS_DCDC_CURRENT_ID)
- CHAR(data.dcdcStatus, SYS_DCDC_STATUS_ID)
+ CAN_SINGLE(dcdc.current, SYS_DCDC_CURRENT_ID)
+ CAN_SINGLE(dcdc.status, SYS_DCDC_STATUS_ID)
// PWM Channels
- FLOAT_PAIR(data.dcdcFan1Duty, data.dcdcFan2Duty, SYS_PWM_FAN_ID)
- FLOAT_PAIR(data.dcdcPump1Duty, data.dcdcPump2Duty, SYS_PWM_PUMP_ID)
+ CAN_PAIR(dcdc.actual.fan1, dcdc.actual.fan2, SYS_PWM_FAN_ID)
+ CAN_PAIR(dcdc.actual.pump1, dcdc.actual.pump2, SYS_PWM_PUMP_ID)
// IMD
- FLOAT(data.imdStatus, SYS_IMD_STATUS_ID)
- CHAR(data.imdResistance, SYS_IMD_RESIST_ID)
+ CAN_SINGLE(imd.status, SYS_IMD_STATUS_ID)
+ CAN_SINGLE(imd.resistance, SYS_IMD_RESIST_ID)
// Latches
- CHAR(data.IMDlatchError, SYS_IMD_LATCH_ID)
- CHAR(data.AMSlatchError, SYS_AMS_LATCH_ID)
+ CAN_SINGLE(latch.imd, SYS_IMD_LATCH_ID)
+ CAN_SINGLE(latch.ams, SYS_AMS_LATCH_ID)
// Shutdown Switches
- CHAR(data.switchState, SYS_SWITCHES_ID)
-
+ CAN_SINGLE(switchState, SYS_SWITCHES_ID)
+
osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<4); // Signal watchdog thread
Thread::wait(CAN_LOOP*1000);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/outDiagnostics/outMacros.h Sat Feb 07 08:54:51 2015 +0000
@@ -0,0 +1,42 @@
+#ifndef OUT_MACROS_H
+#define OUT_MACROS_H
+
+/******************************* FOR WORKING WITH THE STRING BUFFER ***************************/
+
+
+// Macros for working with the strings
+#define ADD_SPRINTF_LINE temp[max_charsPerLine-1]=0; padCenter(max_charsPerLine, temp, ' '); // Cetner the string, then add newlines, and add to chunk
+#define DIVIDER_LINE padCenter(max_charsPerLine, "", 196); // Generate a line full of divider char 196, add to chunk
+#define TITLE(string) padCenter(max_charsPerLine, string, 196); // Generate a title line (centered, surrounded by char 196), add to chunk
+#define BLANK_LINE padCenter(max_charsPerLine, "", ' '); // Generate a line full of spaces (blank), add to chunk
+
+
+/********************************** FOR WORKING WITH CAN MESSAGES ******************************/
+
+#define SEND_CAN(LEN, ID) \
+msg.len = LEN; \
+msg.id = ID; \
+msg.type = CANData; \
+msg.format = CANStandard; \
+if (!can.txWrite(msg)) op->faultCode |= CAN_FAULT;
+
+template <class Type>
+void SEND_CAN_SINGLE(Type Data, int ID)
+{
+ CANMessage msg;
+ *((Type*)((void*)&msg.data[0])) = Data;
+ SEND_CAN(sizeof(Type), ID)
+}
+template <class Type>
+void SEND_CAN_PAIR(Type Data1, Type Data2, int ID)
+{
+ CANMessage msg;
+ *((Type*)((void*)&msg.data[0])) = Data1;
+ *((Type*)((void*)&msg.data[sizeof(Type)])) = Data2;
+ SEND_CAN(2*sizeof(Type), ID)
+}
+
+#define CAN_SINGLE(DATA, ID) SEND_CAN_SINGLE(op->DATA, ID);
+#define CAN_PAIR(DATA1, DATA2, ID) SEND_CAN_PAIR(op->DATA1, op->DATA2, ID);
+
+#endif
--- a/runTime/runTime.cpp Thu Jan 22 07:59:48 2015 +0000
+++ b/runTime/runTime.cpp Sat Feb 07 08:54:51 2015 +0000
@@ -1,49 +1,103 @@
#include "runTime.h"
-const float INTERNAL_OVER_TEMP_THRES = 60; // Overtemp at 60C
-
void runTime::gather(void const* args)
{
- // GLV battery coulomb counter
- data.glvCurrent = glvBat.current();
- data.glvSOC = glvBat.SOC();
- data.glvAmphours = glvBat.ampHours();
- data.glvCapacity = glvBat.capacity();
- data.glvOverCurrent = glvBat.overCurrentDetected();
+ static int counter = 0;
+
+ // POPULATE DATA
+ op->SysTime = time(0); // Tick time
+
+ // GLV Battery
+ op->glvBat.current = glvBat.current();
+ op->glvBat.SOC = glvBat.SOC();
+ op->glvBat.Ah = glvBat.ampHours();
+ op->glvBat.capacity = glvBat.capacity();
+ op->glvBat.error = glvBat.readError();
// DC-DC converter & PWM channels
- data.dcdcStatus = dcdc.getStatus();
- data.dcdcError = dcdc.hasCritError();
- data.dcdcCurrent = dcdc.getCurrent();
- data.dcdcFan1Duty = dcdc.readPwm(FAN1);
- data.dcdcFan2Duty = dcdc.readPwm(FAN2);
- data.dcdcPump1Duty = dcdc.readPwm(PUMP1);
- data.dcdcPump2Duty = dcdc.readPwm(PUMP2);
+ op->dcdc.status = dcdc.getStatus();
+ op->dcdc.current = dcdc.getCurrent();
+ op->dcdc.actual.pump1 = dcdc.readPwm(PUMP1);
+ op->dcdc.actual.pump2 = dcdc.readPwm(PUMP2);
+ op->dcdc.actual.fan1 = dcdc.readPwm(FAN1);
+ op->dcdc.actual.fan2 = dcdc.readPwm(FAN2);
// IMD PWM
- data.imdStatus = imd.status();
- data.imdResistance = imd.resistance();
- data.imdError = ((data.imdStatus != NORMAL) && (data.imdStatus != SPEEDSTART)) || (isnan(data.imdResistance));
-
+ op->imd.status = imd.status();
+ op->imd.resistance = imd.resistance();
+ static bool started=false;
+ if (counter > 30) started=true; // Give the IMD 3 seconds to turn on
+ if (started) {
+ op->imd.error = ((op->imd.status != NORMAL) && (op->imd.status != SPEEDSTART)) || (isnan(op->imd.resistance));
+ } else op->imd.error = 0;
+
// Reset latches
- data.AMSlatchError = AMSlatch.update();
- data.IMDlatchError = IMDlatch.update();
+ op->latch.ams = AMSlatch.update();
+ op->latch.imd = IMDlatch.update();
// Switches
- data.switchState = switches.poll();
+ op->switchState = switches.poll();
+ if (op->switchState == 0) op->signals |= SHUTDOWN_CLOSED;
+ else op->signals &= ~SHUTDOWN_CLOSED;
// Temperaure
- data.internalTemp = internalTmp.read();
- if (data.internalTemp > INTERNAL_OVER_TEMP_THRES) data.internalOverTemp = true;
- else data.internalOverTemp = false;
+ op->internalTemp = internalTmp.read();
- // Error frame
- data.errorFrame = data.canFault<<0 | data.watchdogReset<<1 | data.internalOverTemp<<2 | (data.IMDlatchError!=0)<<3 | (data.AMSlatchError!=0)<<4 | data.imdError<<5 | data.dcdcError<<6 | data.glvOverCurrent<<7;
+ // Telemetry trackers
+ op->xbee1.msgIn = xbeeRelay.counterX1in;
+ op->xbee1.msgOut = xbeeRelay.counterX1out;
+ op->xbee1.msgIn = xbeeRelay.counterX2in;
+ op->xbee2.msgOut = xbeeRelay.counterX2out;
+ if (counter++ % 10 == 0) { // Do every 1 second
+ op->xbee1.rateOut = xbeeRelay.bytesX1out / 1000.0; // Measure data rate in KB/s
+ op->xbee2.rateOut = xbeeRelay.bytesX2out / 1000.0;
+ xbeeRelay.bytesX1out = 0; // Clear every second
+ xbeeRelay.bytesX2out = 0;
+ }
+
+ // CATCH ERRORS
+ if (op->glvBat.error) op->faultCode |= GLVBAT_FAULT;
+ if (op->dcdc.status & (OVER_CURRENT | SENSOR_FAULT)) op->faultCode |= DCDC_FAULT;
+ if (op->internalTemp > param->internalOverTemp) op->faultCode |= INT_OVER_TEMP;
+ if (op->latch.ams & HARD_FAULT) op->faultCode |= AMS_LATCH;
+ if (op->latch.imd & HARD_FAULT) op->faultCode |= IMD_LATCH;
+ if (op->imd.error) op->faultCode |= IMD_FAULT;
+
+ if (FreezeFrame::getError()) op->faultCode |= FREEZE_FRAME;
+ else op->faultCode &= ~FREEZE_FRAME;
+
+ // UPDATE MODE
+ if (op->faultCode) op->mode = FAULT;
+ else op->mode = OKAY;
+
+ // ARREST ERROR
+ if (op->mode == FAULT) {
+ if (!FreezeFrame::getError()) { // Write freeze frame is slot available
+ if (FreezeFrame::writeFrame()) {
+ op->faultCode |= FREEZE_FRAME;
+ }
+ }
+ }
+
+ // Update DC-DC converter
+ if ((op->signals & SHUTDOWN_CLOSED) && (op->signals & AIRS_CLOSED) && !(op->signals & CHARGER_DET) && (op->mode == OKAY)) {
+ dcdc.set(1);
+ } else dcdc.set(0);
+
+ // Update PWM channels
+ dcdc.setPwm(PUMP1, op->dcdc.request.pump1);
+ dcdc.setPwm(PUMP2, op->dcdc.request.pump2);
+ dcdc.setPwm(FAN1, op->dcdc.request.fan1);
+ dcdc.setPwm(FAN2, op->dcdc.request.fan2);
}
-
+void runTime::clearFaults() {
+ op->faultCode = 0;
+ op->mode = OKAY;
+ FreezeFrame::clearError();
+}
void runTime::thread_sample(void const* args)
{
- osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<1); // Signal watchdog thread
glvBat.sample(); // Integrate next sample in GLV Battery coulomb counter
dcdc.sample(); // Handle dc-dc filter and errors
+ osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<1); // Signal watchdog thread
}
\ No newline at end of file
--- a/runTime/runTime.h Thu Jan 22 07:59:48 2015 +0000
+++ b/runTime/runTime.h Sat Feb 07 08:54:51 2015 +0000
@@ -7,6 +7,7 @@
namespace runTime {
void gather(void const* args);
void thread_sample(void const* args);
+ void clearFaults();
}
#endif
\ No newline at end of file
