diff --git a/CMakeLists.txt b/CMakeLists.txt index e619546..87eeeeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,6 @@ link_directories(/usr/local/lib $ENV{EXTRA_LINK}) set(FILES src/pinproc.cpp src/PRDevice.cpp src/PRHardware.cpp) add_library(pinproc ${FILES}) -add_executable(pinproctest examples/pinproctest/pinproctest.cpp) +add_executable(pinproctest examples/pinproctest/pinproctest.cpp examples/pinproctest/drivers.cpp examples/pinproctest/dmd.cpp examples/pinproctest/switches.cpp) target_link_libraries(pinproctest pinproc usb ftdi yaml-cpp) diff --git a/examples/pinproctest/dmd.cpp b/examples/pinproctest/dmd.cpp new file mode 100644 index 0000000..568ba9d --- /dev/null +++ b/examples/pinproctest/dmd.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2009 Gerry Stellenberg, Adam Preble + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "pinproctest.h" + + +void ConfigureDMD(PRHandle proc) +{ + int i; + + // Create the structure that holds the DMD settings + PRDMDConfig dmdConfig; + memset(&dmdConfig, 0x0, sizeof(dmdConfig)); + + dmdConfig.numRows = kDMDRows; + dmdConfig.numColumns = kDMDColumns; + dmdConfig.numSubFrames = kDMDSubFrames; + + for (i = 0; i < kDMDSubFrames; i++) + { + dmdConfig.rclkLowCycles[i] = 15; + dmdConfig.latchHighCycles[i] = 15; + dmdConfig.dotclkHalfPeriod[i] = 1; + } + + dmdConfig.deHighCycles[0] = 250; + dmdConfig.deHighCycles[1] = 400; + dmdConfig.deHighCycles[2] = 180; + dmdConfig.deHighCycles[3] = 800; + + PRDMDUpdateConfig(proc, &dmdConfig); +} + +// Display a simple pattern to verify DMD functionality. +// 16 diagonal lines will rotate to the right. Every two rows will get brighter, +// starting with dim dots at the top. +void UpdateDots( unsigned char * dots, unsigned int dotOffset ) +{ + int row,col,subFrame,color,mappedColor,loopCtr,byte_shifter; + const int rate_reduction_divisor = 1; + + loopCtr = dotOffset/rate_reduction_divisor; + color = pow(2,kDMDSubFrames) - 1; + byte_shifter = 0x80; + + // Slow it down just a tad + if (dotOffset%rate_reduction_divisor == 0) + { + // Set up byte_shifter to rotate pattern to the right. + byte_shifter = pow(2,(loopCtr%8)); + + // Clear the DMD dots every time the rotation occurs + memset(dots,0,((kDMDColumns*kDMDRows)/8)*kDMDSubFrames); + + // Loop through all of the rows + for (row = kDMDRows - 1; row >= 0; row--) + { + // Map the color index to the DMD's physical color map + int mappedColors[] = {0, 2, 8, 10, 1, 3, 9, 11, 4, 6, 12, 14, 5, 7, 13, 15}; + mappedColor = mappedColors[color]; + + // Loop through each of 16 bytes in a row + for (col = 0; col < kDMDColumns / 8; col++) + { + // Loop through each subframe + for (subFrame = 0; subFrame < kDMDSubFrames; subFrame++) + { + // Turn on the byte in each sub-frame that's enabled + // active for the color code. + if ((mappedColor >> subFrame) & 1 == 1) + dots[subFrame*(kDMDColumns*kDMDRows/8)+((row%kDMDRows)*(kDMDColumns / 8))+col] = byte_shifter; + } + } + // Determine where to change the color in order to progress from row 0 = color 0 + // to the last row being the last color. + if (row % (int)((kDMDRows/pow(2,kDMDSubFrames))) == 0) color--; + if (byte_shifter == 1) byte_shifter = 0x80; + else byte_shifter = byte_shifter >> 1; + } + } +} \ No newline at end of file diff --git a/examples/pinproctest/drivers.cpp b/examples/pinproctest/drivers.cpp new file mode 100644 index 0000000..dff8e8e --- /dev/null +++ b/examples/pinproctest/drivers.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2009 Gerry Stellenberg, Adam Preble + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "pinproctest.h" + + + +void ConfigureDrivers(PRHandle proc, PRMachineType machineType, YAML::Node& yamlDoc) +{ + int i; + + const int WPCDriverLoopTime = 4; // milliseconds + const int SternDriverLoopTime = 2; // milliseconds + + const int mappedWPCDriverGroupEnableIndex[] = {0, 0, 0, 0, 0, 2, 4, 3, 1, 5, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0}; + const int mappedSternDriverGroupEnableIndex[] = {0, 0, 0, 0, 1, 0, 2, 3, 0, 0, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9}; + const int mappedWPCDriverGroupSlowTime[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 400, 400, 400, 400, 400, 400, 400, 400, 0, 0, 0, 0, 0, 0, 0, 0}; + const int mappedSternDriverGroupSlowTime[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200}; + const int mappedWPCDriverGroupActivateIndex[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0}; + const int mappedSternDriverGroupActivateIndex[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7}; + + const int watchdogResetTime = 1000; // milliseconds + + int mappedDriverGroupEnableIndex[kPRDriverGroupsMax]; + int mappedDriverGroupSlowTime[kPRDriverGroupsMax]; + int mappedDriverGroupActivateIndex[kPRDriverGroupsMax]; + int rowEnableIndex1; + int rowEnableIndex0; + bool tickleSternWatchdog; + bool globalPolarity; + bool activeLowMatrixRows; + int driverLoopTime; + int slowGroupTime; + int numMatrixGroups; + bool encodeEnables; + int rowEnableSelect; + + switch (machineType) + { + case kPRMachineWPC: + { + memcpy(mappedDriverGroupEnableIndex,mappedWPCDriverGroupEnableIndex, + sizeof(mappedDriverGroupEnableIndex)); + rowEnableIndex1 = 6; // Unused in WPC + rowEnableIndex0 = 6; + tickleSternWatchdog = false; + globalPolarity = false; + activeLowMatrixRows = true; + driverLoopTime = 4; // milliseconds + memcpy(mappedDriverGroupSlowTime,mappedWPCDriverGroupSlowTime, + sizeof(mappedDriverGroupSlowTime)); + memcpy(mappedDriverGroupActivateIndex,mappedWPCDriverGroupActivateIndex, + sizeof(mappedDriverGroupActivateIndex)); + numMatrixGroups = 8; + encodeEnables = false; + rowEnableSelect = 0; + break; + } + + case kPRMachineStern: + { + memcpy(mappedDriverGroupEnableIndex,mappedSternDriverGroupEnableIndex, + sizeof(mappedDriverGroupEnableIndex)); + rowEnableIndex1 = 6; // Unused in Stern + rowEnableIndex0 = 10; + tickleSternWatchdog = true; + globalPolarity = true; + activeLowMatrixRows = false; + driverLoopTime = 2; // milliseconds + memcpy(mappedDriverGroupSlowTime,mappedSternDriverGroupSlowTime, + sizeof(mappedDriverGroupSlowTime)); + memcpy(mappedDriverGroupActivateIndex,mappedSternDriverGroupActivateIndex, + sizeof(mappedDriverGroupActivateIndex)); + numMatrixGroups = 16; + encodeEnables = true; + rowEnableSelect = 0; + break; + } + } + + PRDriverGlobalConfig globals; + globals.enableOutputs = false; + globals.globalPolarity = globalPolarity; + globals.useClear = false; + globals.strobeStartSelect = false; + globals.startStrobeTime = driverLoopTime; // milliseconds per output loop + globals.matrixRowEnableIndex1 = rowEnableIndex1; + globals.matrixRowEnableIndex0 = rowEnableIndex0; + globals.activeLowMatrixRows = activeLowMatrixRows; + globals.tickleSternWatchdog = tickleSternWatchdog; + globals.encodeEnables = encodeEnables; + globals.watchdogExpired = false; + globals.watchdogEnable = true; + globals.watchdogResetTime = watchdogResetTime; + + // We want to start up safely, so we'll update the global driver config twice. + // When we toggle enableOutputs like this P-ROC will reset the polarity: + + // Enable now without the outputs enabled: + PRDriverUpdateGlobalConfig(proc, &globals); + + // Now enable the outputs: (TODO: Why?) + globals.enableOutputs = true; + PRDriverUpdateGlobalConfig(proc, &globals); + + // Configure the groups. Each group corresponds to 8 consecutive drivers, starting + // with driver #32. The following 6 groups are configured for coils/flashlamps. + + PRDriverGroupConfig group; + for (i = 4; i < 10; i++) + { + PRDriverGetGroupConfig(proc, i, &group); + group.slowTime = 0; + group.enableIndex = mappedDriverGroupEnableIndex[i]; + group.rowActivateIndex = 0; + group.rowEnableSelect = 0; + group.matrixed = false; + group.polarity = globalPolarity; + group.active = 1; + group.disableStrobeAfter = false; + + PRDriverUpdateGroupConfig(proc, &group); + } + + // The following 8 groups are configured for the feature lamp matrix. + for (i = 10; i < 10 + numMatrixGroups; i++) { + PRDriverGetGroupConfig(proc, i, &group); + group.slowTime = mappedDriverGroupSlowTime[i]; + group.enableIndex = mappedDriverGroupEnableIndex[i]; + group.rowActivateIndex = mappedDriverGroupActivateIndex[i]; + group.rowEnableSelect = rowEnableSelect; + group.matrixed = 1; + group.polarity = globalPolarity; + group.active = 1; + group.disableStrobeAfter = mappedDriverGroupSlowTime[i] != 0; + PRDriverUpdateGroupConfig(proc, &group); + } +} diff --git a/examples/pinproctest/pinproctest.cpp b/examples/pinproctest/pinproctest.cpp index d45d768..1e4ca48 100644 --- a/examples/pinproctest/pinproctest.cpp +++ b/examples/pinproctest/pinproctest.cpp @@ -1,5 +1,4 @@ /* - * The MIT License * Copyright (c) 2009 Gerry Stellenberg, Adam Preble * * Permission is hereby granted, free of charge, to any person @@ -27,31 +26,11 @@ * pinproctest.cpp * libpinproc */ -#include -#include -#include -#include -#include -#include "../../include/pinproc.h" // Include libpinproc's header. -#include -#include -#include +#include "pinproctest.h" -#define kFlippersSection "PRFlippers" -#define kBumpersSection "PRBumpers" -#define kCoilsSection "PRCoils" -#define kSwitchesSection "PRSwitches" -#define kNumberField "number" - -#define kFlipperPulseTime (34) // 34 ms -#define kBumperPulseTime (25) // 25 ms - -#define kDMDColumns (128) -#define kDMDRows (32) -#define kDMDSubFrames (4) // For color depth of 16 /** Demonstration of the custom logging callback. */ -void TestLogger(const char *text) +void TestLogger(PRLogLevel level, const char *text) { fprintf(stderr, "TEST: %s", text); } @@ -91,253 +70,6 @@ PRResult LoadConfiguration(YAML::Node& yamlDoc, const char *yamlFilePath) return kPRSuccess; } -void ConfigureDrivers(PRHandle proc, PRMachineType machineType, YAML::Node& yamlDoc) -{ - int i; - int mappedDriverGroupEnableIndex[kPRDriverGroupsMax]; - int mappedWPCDriverGroupEnableIndex[] = {0, 0, 0, 0, 0, 2, 4, 3, 1, 5, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0}; - int rowEnableIndex1; - int rowEnableIndex0; - bool tickleSternWatchdog; - bool globalPolarity; - bool activeLowMatrixRows; - int driverLoopTime; - int watchdogResetTime; - int slowGroupTime; - - switch (machineType) - { - case kPRMachineWPC: { - memcpy(mappedDriverGroupEnableIndex,mappedWPCDriverGroupEnableIndex, sizeof(mappedDriverGroupEnableIndex)); - rowEnableIndex1 = 6; // Unused in WPC - rowEnableIndex0 = 6; - tickleSternWatchdog = false; - globalPolarity = false; - activeLowMatrixRows = true; - driverLoopTime = 4; // milliseconds - watchdogResetTime = 1000; // milliseconds - slowGroupTime = driverLoopTime * 100; // microseconds - break; - } - } - - PRDriverGlobalConfig globals; - globals.enableOutputs = false; - globals.globalPolarity = globalPolarity; - globals.useClear = false; - globals.strobeStartSelect = false; - globals.startStrobeTime = driverLoopTime; // milliseconds per output loop - globals.matrixRowEnableIndex1 = rowEnableIndex1; - globals.matrixRowEnableIndex0 = rowEnableIndex0; - globals.activeLowMatrixRows = activeLowMatrixRows; - globals.tickleSternWatchdog = tickleSternWatchdog; - globals.encodeEnables = false; - globals.watchdogExpired = false; - globals.watchdogEnable = true; - globals.watchdogResetTime = watchdogResetTime; - - // We want to start up safely, so we'll update the global driver config twice. - // When we toggle enableOutputs like this P-ROC will reset the polarity: - - // Enable now without the outputs enabled: - PRDriverUpdateGlobalConfig(proc, &globals); - - // Now enable the outputs: (TODO: Why?) - globals.enableOutputs = true; - PRDriverUpdateGlobalConfig(proc, &globals); - - // Configure the groups. Each group corresponds to 8 consecutive drivers, starting - // with driver #32. The following 6 groups are configured for coils/flashlamps. - - PRDriverGroupConfig group; - for (i = 4; i < 10; i++) - { - PRDriverGetGroupConfig(proc, i, &group); - group.slowTime = 0; - group.enableIndex = mappedDriverGroupEnableIndex[i]; - group.rowActivateIndex = 0; - group.rowEnableSelect = 0; - group.matrixed = false; - group.polarity = false; - group.active = 1; - group.disableStrobeAfter = false; - - PRDriverUpdateGroupConfig(proc, &group); - } - - // The following 8 groups are configured for the feature lamp matrix. - for (i = 10; i < 18; i++) { - PRDriverGetGroupConfig(proc, i, &group); - group.slowTime = slowGroupTime; - group.enableIndex = mappedDriverGroupEnableIndex[i]; - group.rowActivateIndex = i - 10; - group.rowEnableSelect = 0; - group.matrixed = 1; - group.polarity = 0; - group.active = 1; - group.disableStrobeAfter = 1; - PRDriverUpdateGroupConfig(proc, &group); - } -} - -void ConfigureSwitches(PRHandle proc, YAML::Node& yamlDoc) -{ - // Configure switch controller registers (if the defaults aren't acceptable) - PRSwitchConfig switchConfig; - switchConfig.clear = false; - switchConfig.directMatrixScanLoopTime = 2; // milliseconds - switchConfig.pulsesBeforeCheckingRX = 10; - switchConfig.inactivePulsesAfterBurst = 12; - switchConfig.pulsesPerBurst = 6; - switchConfig.pulseHalfPeriodTime = 13; // milliseconds - PRSwitchUpdateConfig(proc, &switchConfig); -} - -void ConfigureWPCFlipperSwitchRule (PRHandle proc, int swNum, int mainCoilNum, int holdCoilNum, int pulseTime) -{ - const int numDriverRules = 2; - PRDriverState drivers[numDriverRules]; - PRSwitchRule sw; - - // Flipper on rules - PRDriverGetState(proc, mainCoilNum, &drivers[0]); - PRDriverStatePulse(&drivers[0],pulseTime); // Pulse coil for 34ms. - PRDriverGetState(proc, holdCoilNum, &drivers[1]); - PRDriverStatePulse(&drivers[1],0); // Turn on indefintely (set pulse for 0ms) - sw.notifyHost = false; - PRSwitchUpdateRule(proc, swNum, kPREventTypeSwitchClosedNondebounced, &sw, drivers, numDriverRules); - sw.notifyHost = true; - PRSwitchUpdateRule(proc, swNum, kPREventTypeSwitchClosedDebounced, &sw, NULL, 0); - - // Flipper off rules - PRDriverGetState(proc, mainCoilNum, &drivers[0]); - PRDriverStateDisable(&drivers[0]); // Disable main coil - PRDriverGetState(proc, holdCoilNum, &drivers[1]); - PRDriverStateDisable(&drivers[1]); // Disable hold coil - sw.notifyHost = false; - PRSwitchUpdateRule(proc, swNum, kPREventTypeSwitchOpenNondebounced, &sw, drivers, numDriverRules); - sw.notifyHost = true; - PRSwitchUpdateRule(proc, swNum, kPREventTypeSwitchOpenDebounced, &sw, NULL, 0); -} - -void ConfigureBumperRule (PRHandle proc, int swNum, int coilNum, int pulseTime) -{ - const int numDriverRules = 1; - PRDriverState drivers[numDriverRules]; - PRSwitchRule sw; - - // Lower Right Flipper On - PRDriverGetState(proc, coilNum, &drivers[0]); - PRDriverStatePulse(&drivers[0],pulseTime); // Pulse coil for 34ms. - sw.notifyHost = false; - PRSwitchUpdateRule(proc, swNum, kPREventTypeSwitchClosedNondebounced, &sw, drivers, numDriverRules); - sw.notifyHost = true; - PRSwitchUpdateRule(proc, swNum, kPREventTypeSwitchClosedDebounced, &sw, NULL, 0); -} - -void ConfigureSwitchRules(PRHandle proc, YAML::Node& yamlDoc) -{ - // WPC Flippers - const YAML::Node& flippers = yamlDoc[kFlippersSection]; - for (YAML::Iterator flippersIt = flippers.begin(); flippersIt != flippers.end(); ++flippersIt) - { - int swNum, coilMain, coilHold; - std::string flipperName; - *flippersIt >> flipperName; - yamlDoc[kSwitchesSection][flipperName][kNumberField] >> swNum; - yamlDoc[kCoilsSection][flipperName + "Main"][kNumberField] >> coilMain; - yamlDoc[kCoilsSection][flipperName + "Hold"][kNumberField] >> coilHold; - ConfigureWPCFlipperSwitchRule (proc, swNum, coilMain, coilHold, kFlipperPulseTime); - } - - const YAML::Node& bumpers = yamlDoc[kBumpersSection]; - for (YAML::Iterator bumpersIt = bumpers.begin(); bumpersIt != bumpers.end(); ++bumpersIt) - { - int swNum, coilNum; - // WPC Slingshots - std::string bumperName; - *bumpersIt >> bumperName; - yamlDoc[kSwitchesSection][bumperName][kNumberField] >> swNum; - yamlDoc[kCoilsSection][bumperName][kNumberField] >> coilNum; - ConfigureBumperRule (proc, swNum, coilNum, kBumperPulseTime); - } -} - -void ConfigureDMD(PRHandle proc) -{ - int i; - - // Create the structure that holds the DMD settings - PRDMDConfig dmdConfig; - memset(&dmdConfig, 0x0, sizeof(dmdConfig)); - - dmdConfig.numRows = kDMDRows; - dmdConfig.numColumns = kDMDColumns; - dmdConfig.numSubFrames = kDMDSubFrames; - - for (i = 0; i < kDMDSubFrames; i++) - { - dmdConfig.rclkLowCycles[i] = 15; - dmdConfig.latchHighCycles[i] = 15; - dmdConfig.dotclkHalfPeriod[i] = 1; - } - - dmdConfig.deHighCycles[0] = 250; - dmdConfig.deHighCycles[1] = 400; - dmdConfig.deHighCycles[2] = 180; - dmdConfig.deHighCycles[3] = 800; - - PRDMDUpdateConfig(proc, &dmdConfig); -} - -// Display a simple pattern to verify DMD functionality. -// 16 diagonal lines will rotate to the right. Every two rows will get brighter, -// starting with dim dots at the top. -void UpdateDots( unsigned char * dots, unsigned int dotOffset ) -{ - int row,col,subFrame,color,mappedColor,loopCtr,byte_shifter; - const int rate_reduction_divisor = 1; - - loopCtr = dotOffset/rate_reduction_divisor; - color = pow(2,kDMDSubFrames) - 1; - byte_shifter = 0x80; - - // Slow it down just a tad - if (dotOffset%rate_reduction_divisor == 0) - { - // Set up byte_shifter to rotate pattern to the right. - byte_shifter = pow(2,(loopCtr%8)); - - // Clear the DMD dots every time the rotation occurs - memset(dots,0,((kDMDColumns*kDMDRows)/8)*kDMDSubFrames); - - // Loop through all of the rows - for (row = kDMDRows - 1; row >= 0; row--) - { - // Map the color index to the DMD's physical color map - int mappedColors[] = {0, 2, 8, 10, 1, 3, 9, 11, 4, 6, 12, 14, 5, 7, 13, 15}; - mappedColor = mappedColors[color]; - - // Loop through each of 16 bytes in a row - for (col = 0; col < kDMDColumns / 8; col++) - { - // Loop through each subframe - for (subFrame = 0; subFrame < kDMDSubFrames; subFrame++) - { - // Turn on the byte in each sub-frame that's enabled - // active for the color code. - if ((mappedColor >> subFrame) & 1 == 1) - dots[subFrame*(kDMDColumns*kDMDRows/8)+((row%kDMDRows)*(kDMDColumns / 8))+col] = byte_shifter; - } - } - // Determine where to change the color in order to progress from row 0 = color 0 - // to the last row being the last color. - if (row % (int)((kDMDRows/pow(2,kDMDSubFrames))) == 0) color--; - if (byte_shifter == 1) byte_shifter = 0x80; - else byte_shifter = byte_shifter >> 1; - } - } -} time_t startTime; bool runLoopRun = true; @@ -427,7 +159,10 @@ int main(int argc, const char **argv) // Finally instantiate the P-ROC device: PRHandle proc = PRCreate(machineType); if (proc == kPRHandleInvalid) + { + fprintf(stderr, "Error during PRCreate: %s\n", PRGetLastErrorText()); return 1; + } PRReset(proc, kPRResetFlagUpdateDevice); // Reset the device structs and write them into the device. @@ -444,7 +179,7 @@ int main(int argc, const char **argv) // Pulse a coil for testing purposes. //PRDriverPulse(proc, 53, 100); // Schedule a feature lamp for testing purposes. - //PRDriverSchedule(proc, 80, 0xFF00FF00, 0, 0); + PRDriverSchedule(proc, 80, 0xFF00FF00, 0, 0); // Pitter-patter a feature lamp for testing purposes. //PRDriverPatter(proc, 84, 127, 127, 0); diff --git a/examples/pinproctest/pinproctest.h b/examples/pinproctest/pinproctest.h new file mode 100644 index 0000000..c4e339e --- /dev/null +++ b/examples/pinproctest/pinproctest.h @@ -0,0 +1,59 @@ +/* + * The MIT License + * Copyright (c) 2009 Gerry Stellenberg, Adam Preble + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +/* + * pinproctest.cpp + * libpinproc + */ +#include +#include +#include +#include +#include +#include "../../include/pinproc.h" // Include libpinproc's header. +#include +#include +#include + +#define kFlippersSection "PRFlippers" +#define kBumpersSection "PRBumpers" +#define kCoilsSection "PRCoils" +#define kSwitchesSection "PRSwitches" +#define kNumberField "number" + +#define kFlipperPulseTime (34) // 34 ms +#define kBumperPulseTime (25) // 25 ms + +#define kDMDColumns (128) +#define kDMDRows (32) +#define kDMDSubFrames (4) // For color depth of 16 + +void ConfigureDrivers(PRHandle proc, PRMachineType machineType, YAML::Node& yamlDoc); + +void ConfigureSwitches(PRHandle proc, YAML::Node& yamlDoc); +void ConfigureSwitchRules(PRHandle proc, YAML::Node& yamlDoc); + +void ConfigureDMD(PRHandle proc); +void UpdateDots(unsigned char * dots, unsigned int dotOffset); diff --git a/examples/pinproctest/switches.cpp b/examples/pinproctest/switches.cpp new file mode 100644 index 0000000..8ab1d52 --- /dev/null +++ b/examples/pinproctest/switches.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2009 Gerry Stellenberg, Adam Preble + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "pinproctest.h" + + +void ConfigureSwitches(PRHandle proc, YAML::Node& yamlDoc) +{ + // Configure switch controller registers (if the defaults aren't acceptable) + PRSwitchConfig switchConfig; + switchConfig.clear = false; + switchConfig.directMatrixScanLoopTime = 2; // milliseconds + switchConfig.pulsesBeforeCheckingRX = 10; + switchConfig.inactivePulsesAfterBurst = 12; + switchConfig.pulsesPerBurst = 6; + switchConfig.pulseHalfPeriodTime = 13; // milliseconds + PRSwitchUpdateConfig(proc, &switchConfig); +} + +void ConfigureWPCFlipperSwitchRule (PRHandle proc, int swNum, int mainCoilNum, int holdCoilNum, int pulseTime) +{ + const int numDriverRules = 2; + PRDriverState drivers[numDriverRules]; + PRSwitchRule sw; + + // Flipper on rules + PRDriverGetState(proc, mainCoilNum, &drivers[0]); + PRDriverStatePulse(&drivers[0],pulseTime); // Pulse coil for 34ms. + PRDriverGetState(proc, holdCoilNum, &drivers[1]); + PRDriverStatePulse(&drivers[1],0); // Turn on indefintely (set pulse for 0ms) + sw.notifyHost = false; + PRSwitchUpdateRule(proc, swNum, kPREventTypeSwitchClosedNondebounced, &sw, drivers, numDriverRules); + sw.notifyHost = true; + PRSwitchUpdateRule(proc, swNum, kPREventTypeSwitchClosedDebounced, &sw, NULL, 0); + + // Flipper off rules + PRDriverGetState(proc, mainCoilNum, &drivers[0]); + PRDriverStateDisable(&drivers[0]); // Disable main coil + PRDriverGetState(proc, holdCoilNum, &drivers[1]); + PRDriverStateDisable(&drivers[1]); // Disable hold coil + sw.notifyHost = false; + PRSwitchUpdateRule(proc, swNum, kPREventTypeSwitchOpenNondebounced, &sw, drivers, numDriverRules); + sw.notifyHost = true; + PRSwitchUpdateRule(proc, swNum, kPREventTypeSwitchOpenDebounced, &sw, NULL, 0); +} + +void ConfigureBumperRule (PRHandle proc, int swNum, int coilNum, int pulseTime) +{ + const int numDriverRules = 1; + PRDriverState drivers[numDriverRules]; + PRSwitchRule sw; + + // Lower Right Flipper On + PRDriverGetState(proc, coilNum, &drivers[0]); + PRDriverStatePulse(&drivers[0],pulseTime); // Pulse coil for 34ms. + sw.notifyHost = false; + PRSwitchUpdateRule(proc, swNum, kPREventTypeSwitchClosedNondebounced, &sw, drivers, numDriverRules); + sw.notifyHost = true; + PRSwitchUpdateRule(proc, swNum, kPREventTypeSwitchClosedDebounced, &sw, NULL, 0); +} + +void ConfigureSwitchRules(PRHandle proc, YAML::Node& yamlDoc) +{ + // WPC Flippers + const YAML::Node& flippers = yamlDoc[kFlippersSection]; + for (YAML::Iterator flippersIt = flippers.begin(); flippersIt != flippers.end(); ++flippersIt) + { + int swNum, coilMain, coilHold; + std::string flipperName; + *flippersIt >> flipperName; + yamlDoc[kSwitchesSection][flipperName][kNumberField] >> swNum; + yamlDoc[kCoilsSection][flipperName + "Main"][kNumberField] >> coilMain; + yamlDoc[kCoilsSection][flipperName + "Hold"][kNumberField] >> coilHold; + ConfigureWPCFlipperSwitchRule (proc, swNum, coilMain, coilHold, kFlipperPulseTime); + } + + const YAML::Node& bumpers = yamlDoc[kBumpersSection]; + for (YAML::Iterator bumpersIt = bumpers.begin(); bumpersIt != bumpers.end(); ++bumpersIt) + { + int swNum, coilNum; + // WPC Slingshots + std::string bumperName; + *bumpersIt >> bumperName; + yamlDoc[kSwitchesSection][bumperName][kNumberField] >> swNum; + yamlDoc[kCoilsSection][bumperName][kNumberField] >> coilNum; + ConfigureBumperRule (proc, swNum, coilNum, kBumperPulseTime); + } +} \ No newline at end of file diff --git a/include/pinproc.h b/include/pinproc.h index c99f215..ccaf50b 100644 --- a/include/pinproc.h +++ b/include/pinproc.h @@ -71,9 +71,20 @@ typedef int32_t PRResult; /**< See: #kPRSuccess and #kPRFailure. */ typedef void * PRHandle; /**< Opaque type used to reference an individual P-ROC device. Created with PRCreate() and destroyed with PRDelete(). This value is used as the first parameter to all P-ROC API function calls. */ #define kPRHandleInvalid (0) /**< Value returned by PRCreate() on failure. Indicates an invalid #PRHandle. */ -typedef void (*PRLogCallback)(const char *text); /**< Function pointer type for a custom logging callback. See: PRLogSetCallback(). */ +typedef enum PRLogLevel { + kPRLogVerbose, + kPRLogInfo, + kPRLogWarning, + kPRLogError +} PRLogLevel; + +typedef void (*PRLogCallback)(PRLogLevel level, const char *text); /**< Function pointer type for a custom logging callback. See: PRLogSetCallback(). */ PR_EXPORT void PRLogSetCallback(PRLogCallback callback); /**< Replaces the default logging handler with the given callback function. */ +PR_EXPORT void PRLogSetLevel(PRLogLevel level); + +PR_EXPORT const char *PRGetLastErrorText(); + /** * @defgroup device Device Creation & Deletion * @{ @@ -145,7 +156,7 @@ typedef struct PRDriverGroupConfig { typedef struct PRDriverState { uint16_t driverNum; - uint32_t outputDriveTime; + uint8_t outputDriveTime; bool_t polarity; bool_t state; bool_t waitForFirstTimeSlot; @@ -179,7 +190,7 @@ PR_EXPORT PRResult PRDriverDisable(PRHandle handle, uint16_t driverNum); * Pulses the given driver for a number of milliseconds. * This function is provided for convenience. See PRDriverStatePulse() for a full description. */ -PR_EXPORT PRResult PRDriverPulse(PRHandle handle, uint16_t driverNum, int milliseconds); +PR_EXPORT PRResult PRDriverPulse(PRHandle handle, uint16_t driverNum, uint8_t milliseconds); /** * Assigns a repeating schedule to the given driver. * This function is provided for convenience. See PRDriverStateSchedule() for a full description. @@ -203,7 +214,7 @@ PR_EXPORT void PRDriverStateDisable(PRDriverState *driverState); * @param milliseconds Number of milliseconds to pulse the driver for. * @note The driver state structure must be applied using PRDriverUpdateState() or linked to a switch rule using PRSwitchUpdateRule() to have any effect. */ -PR_EXPORT void PRDriverStatePulse(PRDriverState *driverState, int milliseconds); +PR_EXPORT void PRDriverStatePulse(PRDriverState *driverState, uint8_t milliseconds); /** * Changes the given #PRDriverState to reflect a scheduled state. * Assigns a repeating schedule to the given driver. diff --git a/libpinproc.xcodeproj/project.pbxproj b/libpinproc.xcodeproj/project.pbxproj index c9e816e..244d4a9 100644 --- a/libpinproc.xcodeproj/project.pbxproj +++ b/libpinproc.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 66640E5E0FD1CFB6003135D8 /* dmd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 66640E5B0FD1CFB6003135D8 /* dmd.cpp */; }; + 66640E5F0FD1CFB6003135D8 /* drivers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 66640E5C0FD1CFB6003135D8 /* drivers.cpp */; }; + 66640E600FD1CFB6003135D8 /* switches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 66640E5D0FD1CFB6003135D8 /* switches.cpp */; }; 6682492C0FC079050051560E /* pinproc.h in Headers */ = {isa = PBXBuildFile; fileRef = 6682492B0FC079050051560E /* pinproc.h */; }; 6682493A0FC07B2A0051560E /* pinproc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 668249390FC07B2A0051560E /* pinproc.cpp */; }; 668249420FC07D900051560E /* PRDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 668249400FC07D900051560E /* PRDevice.h */; }; @@ -29,6 +32,10 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 66640E5B0FD1CFB6003135D8 /* dmd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dmd.cpp; path = examples/pinproctest/dmd.cpp; sourceTree = ""; }; + 66640E5C0FD1CFB6003135D8 /* drivers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = drivers.cpp; path = examples/pinproctest/drivers.cpp; sourceTree = ""; }; + 66640E5D0FD1CFB6003135D8 /* switches.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = switches.cpp; path = examples/pinproctest/switches.cpp; sourceTree = ""; }; + 66640E610FD1D010003135D8 /* pinproctest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pinproctest.h; path = examples/pinproctest/pinproctest.h; sourceTree = ""; }; 6682492B0FC079050051560E /* pinproc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pinproc.h; path = include/pinproc.h; sourceTree = ""; }; 668249390FC07B2A0051560E /* pinproc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = pinproc.cpp; path = src/pinproc.cpp; sourceTree = ""; }; 668249400FC07D900051560E /* PRDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PRDevice.h; path = src/PRDevice.h; sourceTree = ""; }; @@ -105,7 +112,11 @@ 668249D30FC0A2C10051560E /* pinproctest */ = { isa = PBXGroup; children = ( + 66640E610FD1D010003135D8 /* pinproctest.h */, 668249E20FC0A3960051560E /* pinproctest.cpp */, + 66640E5B0FD1CFB6003135D8 /* dmd.cpp */, + 66640E5C0FD1CFB6003135D8 /* drivers.cpp */, + 66640E5D0FD1CFB6003135D8 /* switches.cpp */, ); name = pinproctest; sourceTree = ""; @@ -192,6 +203,9 @@ buildActionMask = 2147483647; files = ( 668249E30FC0A3960051560E /* pinproctest.cpp in Sources */, + 66640E5E0FD1CFB6003135D8 /* dmd.cpp in Sources */, + 66640E5F0FD1CFB6003135D8 /* drivers.cpp in Sources */, + 66640E600FD1CFB6003135D8 /* switches.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/PRCommon.h b/src/PRCommon.h index 6beb403..b3a541f 100644 --- a/src/PRCommon.h +++ b/src/PRCommon.h @@ -31,6 +31,7 @@ # define DEBUG(block) block #endif -extern void PRLog(const char *format, ...); +void PRLog(PRLogLevel level, const char *format, ...); +void PRSetLastErrorText(const char *format, ...); #endif // _PRCOMMON_H_ diff --git a/src/PRDevice.cpp b/src/PRDevice.cpp index 9e6f541..b4a2163 100644 --- a/src/PRDevice.cpp +++ b/src/PRDevice.cpp @@ -47,13 +47,13 @@ PRDevice* PRDevice::Create(PRMachineType machineType) if (dev == NULL) { - DEBUG(PRLog("Error allocating memory for P-ROC device\n")); + DEBUG(PRLog(kPRLogError, "Error allocating memory for P-ROC device\n")); return NULL; } if (!dev->Open()) { - DEBUG(PRLog("Error opening P-ROC device.\n")); + DEBUG(PRLog(kPRLogError, "Error opening P-ROC device.\n")); delete dev; return NULL; } @@ -150,7 +150,7 @@ PRResult PRDevice::DriverUpdateGlobalConfig(PRDriverGlobalConfig *driverGlobalCo uint32_t burst[burstWords]; int32_t rc; - DEBUG(PRLog("Installing driver globals\n")); + DEBUG(PRLog(kPRLogInfo, "Installing driver globals\n")); this->driverGlobalConfig = *driverGlobalConfig; rc = CreateDriverUpdateGlobalConfigBurst(burst, driverGlobalConfig); @@ -158,8 +158,8 @@ PRResult PRDevice::DriverUpdateGlobalConfig(PRDriverGlobalConfig *driverGlobalCo driverGlobalConfig->watchdogEnable, driverGlobalConfig->watchdogResetTime); - DEBUG(PRLog("Driver Global words: %x %x\n", burst[0], burst[1])); - DEBUG(PRLog("Watchdog words: %x %x\n", burst[2], burst[3])); + DEBUG(PRLog(kPRLogVerbose, "Driver Global words: %x %x\n", burst[0], burst[1])); + DEBUG(PRLog(kPRLogVerbose, "Watchdog words: %x %x\n", burst[2], burst[3])); return PrepareWriteData(burst, burstWords); } @@ -176,10 +176,10 @@ PRResult PRDevice::DriverUpdateGroupConfig(PRDriverGroupConfig *driverGroupConfi int32_t rc; driverGroups[driverGroupConfig->groupNum] = *driverGroupConfig; - DEBUG(PRLog("Installing driver group\n")); + DEBUG(PRLog(kPRLogInfo, "Installing driver group\n")); rc = CreateDriverUpdateGroupConfigBurst(burst, driverGroupConfig); - DEBUG(PRLog("Words: %x %x\n", burst[0], burst[1])); + DEBUG(PRLog(kPRLogVerbose, "Words: %x %x\n", burst[0], burst[1])); return PrepareWriteData(burst, burstWords); } @@ -195,18 +195,18 @@ PRResult PRDevice::DriverUpdateState(PRDriverState *driverState) uint32_t burst[burstWords]; int32_t rc; - DEBUG(PRLog("Updating driver #%d\n", driverState->driverNum)); + DEBUG(PRLog(kPRLogInfo, "Updating driver #%d\n", driverState->driverNum)); if (driverState->polarity != drivers[driverState->driverNum].polarity && machineType != kPRMachineCustom) { - DEBUG(PRLog("Refusing to update driver #%d; polarity differs on non-custom machine.\n", driverState->driverNum)); + PRSetLastErrorText("Refusing to update driver #%d; polarity differs on non-custom machine.", driverState->driverNum); return kPRFailure; } drivers[driverState->driverNum] = *driverState; rc = CreateDriverUpdateBurst(burst, &drivers[driverState->driverNum]); - DEBUG(PRLog("Words: %x %x %x\n", burst[0], burst[1], burst[2])); + DEBUG(PRLog(kPRLogVerbose, "Words: %x %x %x\n", burst[0], burst[1], burst[2])); return PrepareWriteData(burst, burstWords); } @@ -241,8 +241,8 @@ PRResult PRDevice::SwitchUpdateConfig(PRSwitchConfig *switchConfig) this->switchConfig = *switchConfig; CreateSwitchUpdateConfigBurst(burst, switchConfig); - DEBUG(PRLog("Configuring Switch Logic")); - DEBUG(PRLog("Words: %x %x\n",burst[0],burst[1])); + DEBUG(PRLog(kPRLogInfo, "Configuring Switch Logic")); + DEBUG(PRLog(kPRLogVerbose, "Words: %x %x\n",burst[0],burst[1])); rc = PrepareWriteData(burst, burstWords); return rc; @@ -256,7 +256,7 @@ PRResult PRDevice::SwitchUpdateRule(uint8_t switchNum, PREventType eventType, PR if (switchNum > kPRSwitchPhysicalLast) // Always true due to data type. { - DEBUG(PRLog("Switch rule out of range 0-%d\n", kPRSwitchPhysicalLast)); + PRSetLastErrorText("Switch rule out of range 0-%d", kPRSwitchPhysicalLast); return kPRFailure; } @@ -264,7 +264,7 @@ PRResult PRDevice::SwitchUpdateRule(uint8_t switchNum, PREventType eventType, PR // the links. if (numDrivers > 0 && freeSwitchRuleIndexes.size() < numDrivers-1) // -1 because the first switch rule holds the first driver. { - DEBUG(PRLog("Not enough free switch rule indexes: %d available, need %d\n", freeSwitchRuleIndexes.size(), numDrivers)); + PRSetLastErrorText("Not enough free switch rule indexes: %d available, need %d", freeSwitchRuleIndexes.size(), numDrivers); return kPRFailure; } @@ -283,7 +283,7 @@ PRResult PRDevice::SwitchUpdateRule(uint8_t switchNum, PREventType eventType, PR uint16_t firstRuleIndex = newRuleIndex; PRSwitchRuleInternal *newRule = GetSwitchRuleByIndex(newRuleIndex); if (newRule->eventType != eventType) - DEBUG(PRLog("Unexpected state: switch rule at 0x%x has event type 0x%x (expected 0x%x).\n", newRuleIndex, newRule->eventType, eventType)); + DEBUG(PRLog(kPRLogWarning, "Unexpected state: switch rule at 0x%x has event type 0x%x (expected 0x%x).\n", newRuleIndex, newRule->eventType, eventType)); newRule->notifyHost = rule->notifyHost; newRule->changeOutput = false; newRule->linkActive = false; @@ -313,20 +313,20 @@ PRResult PRDevice::SwitchUpdateRule(uint8_t switchNum, PREventType eventType, PR CreateSwitchUpdateRulesBurst(burst, newRule); } - DEBUG(PRLog("Rule Words: %x %x %x %x\n", burst[0],burst[1],burst[2],burst[3])); + DEBUG(PRLog(kPRLogVerbose, "Rule Words: %x %x %x %x\n", burst[0],burst[1],burst[2],burst[3])); // Write the rule: res = PrepareWriteData(burst, burstSize); if (res != kPRSuccess) { - DEBUG(PRLog("Error while writing switch update, attempting to revert switch rule to a safe state...")); + DEBUG(PRLog(kPRLogError, "Error while writing switch update, attempting to revert switch rule to a safe state...")); newRule = GetSwitchRuleByIndex(firstRuleIndex); newRule->changeOutput = false; newRule->linkActive = false; CreateSwitchUpdateRulesBurst(burst, newRule); if (PrepareWriteData(burst, burstSize) == kPRSuccess) - DEBUG(PRLog("Disabled successfully.\n")); + DEBUG(PRLog(kPRLogError, "Disabled successfully.\n")); else - DEBUG(PRLog("Failed to disable.\n")); + DEBUG(PRLog(kPRLogError, "Failed to disable.\n")); return res; } @@ -337,7 +337,7 @@ PRResult PRDevice::SwitchUpdateRule(uint8_t switchNum, PREventType eventType, PR else { CreateSwitchUpdateRulesBurst(burst, newRule); - DEBUG(PRLog("Rule Words: %x %x %x %x\n", burst[0],burst[1],burst[2],burst[3])); + DEBUG(PRLog(kPRLogVerbose, "Rule Words: %x %x %x %x\n", burst[0],burst[1],burst[2],burst[3])); // Write the rule: res = PrepareWriteData(burst, burstSize); @@ -355,8 +355,8 @@ int32_t PRDevice::DMDUpdateConfig(PRDMDConfig *dmdConfig) this->dmdConfig = *dmdConfig; CreateDMDUpdateConfigBurst(burst, dmdConfig); - DEBUG(PRLog("Configuring DMD")); - DEBUG(PRLog("Words: %x %x %x %x %x %x %x\n",burst[0],burst[1],burst[2],burst[3], + DEBUG(PRLog(kPRLogInfo, "Configuring DMD")); + DEBUG(PRLog(kPRLogVerbose, "Words: %x %x %x %x %x %x %x\n",burst[0],burst[1],burst[2],burst[3], burst[4],burst[5],burst[6])); rc = PrepareWriteData(burst, burstWords); @@ -417,24 +417,21 @@ PRResult PRDevice::Open() { // Try to verify the P-ROC IS in the FPGA before initializing the FPGA's FTDI interface // just in case it was already initialized from a previous application execution. - DEBUG(PRLog("Verifying P-ROC ID: \n")); + DEBUG(PRLog(kPRLogInfo, "Verifying P-ROC ID: \n")); if (VerifyChipID() == kPRFailure) { // Since the FPGA didn't appear to be responding properly, send the FPGA's FTDI // initialization sequence. This is a set of bytes the FPGA is waiting to receive // before it allows access deeper into the chip. This keeps garbage from getting // in and wreaking havoc before software is up and running. - DEBUG(PRLog("Initializing P-ROC...\n")); + DEBUG(PRLog(kPRLogInfo, "Initializing P-ROC...\n")); res = FlushReadBuffer(); uint32_t temp_word = P_ROC_INIT_PATTERN_A; res = WriteData(&temp_word, 1); temp_word = P_ROC_INIT_PATTERN_B; res = WriteData(&temp_word, 1); res = VerifyChipID(); - } - else - { - DEBUG(PRLog("Failed to verify chip ID.")); - res = kPRFailure; + if (res == kPRFailure) + DEBUG(PRLog(kPRLogWarning, "Unable to read Chip ID - P-ROC could not be initialized.\n")); } } @@ -474,19 +471,20 @@ PRResult PRDevice::VerifyChipID() if (wordsRead == 5) { //std::cout << rc << " words read. \n" - DEBUG(PRLog("FPGA Chip ID: 0x%x\n", buffer[1])); - DEBUG(PRLog("FPGA Chip Version/Rev: %d.%d\n", buffer[2] >> 16, buffer[2] & 0xffff)); - DEBUG(PRLog("Watchdog Settings: 0x%x\n", buffer[3])); - DEBUG(PRLog("Switches: 0x%x\n", buffer[4])); + DEBUG(PRLog(kPRLogInfo, "FPGA Chip ID: 0x%x\n", buffer[1])); + DEBUG(PRLog(kPRLogInfo, "FPGA Chip Version/Rev: %d.%d\n", buffer[2] >> 16, buffer[2] & 0xffff)); + DEBUG(PRLog(kPRLogInfo, "Watchdog Settings: 0x%x\n", buffer[3])); + DEBUG(PRLog(kPRLogInfo, "Switches: 0x%x\n", buffer[4])); rc = kPRSuccess; } else { - DEBUG(PRLog("Error reading Chip IP and Version. Incorrect number of bytes received from read_data().\n")); + DEBUG(PRLog(kPRLogError, "Error reading Chip IP and Version. Incorrect number of bytes received from read_data().\n")); rc = kPRFailure; } } - else { - DEBUG(PRLog("Unable to read Chip ID - P-ROC not yet initialized.\n")); + else + { + // Return failure without logging; calling function must log. rc = kPRFailure; } return (rc); @@ -502,7 +500,7 @@ PRResult PRDevice::PrepareWriteData(uint32_t * words, int32_t numWords) { if (numWords > maxWriteWords) { - DEBUG(PRLog("%d words Exceeds write capabilities. Restrict write requests to %d words.", numWords, maxWriteWords)); + PRSetLastErrorText("%d words Exceeds write capabilities. Restrict write requests to %d words.", numWords, maxWriteWords); return kPRFailure; } @@ -511,7 +509,7 @@ PRResult PRDevice::PrepareWriteData(uint32_t * words, int32_t numWords) if (numPreparedWriteWords + numWords > maxWriteWords) { if (FlushWriteData() == kPRFailure); - return kPRFailure; + return kPRFailure; } memcpy(preparedWriteWords + numPreparedWriteWords, words, numWords * 4); @@ -559,7 +557,7 @@ PRResult PRDevice::WriteData(uint32_t * words, int32_t numWords) if (bytesWritten != bytesToWrite) { - DEBUG(PRLog("Error in WriteData: wrote %d of %d bytes\n", bytesWritten, bytesToWrite)); + PRSetLastErrorText("Error in WriteData: wrote %d of %d bytes", bytesWritten, bytesToWrite); return kPRFailure; } else @@ -596,7 +594,7 @@ int32_t PRDevice::ReadData(uint32_t *buffer, int32_t num_words) else { rc = 0; } - DEBUG(PRLog("Read num bytes: %d\n", rc)); + DEBUG(PRLog(kPRLogVerbose, "Read num bytes: %d\n", rc)); return (rc); } @@ -628,7 +626,7 @@ int32_t PRDevice::CollectReadData() num_collected_bytes += rc; if (rc > 0) { - DEBUG(PRLog("Collected bytes: %d\n", rc)); + DEBUG(PRLog(kPRLogVerbose, "Collected bytes: %d\n", rc)); } return (rc); } @@ -643,7 +641,7 @@ PRResult PRDevice::SortReturningData() while (num_words >= 2) { rc = ReadData(rd_buffer, 1); - DEBUG(PRLog("New returning word: 0x%x\n", rd_buffer[0])); + DEBUG(PRLog(kPRLogVerbose, "New returning word: 0x%x\n", rd_buffer[0])); switch ( (rd_buffer[0] & P_ROC_COMMAND_MASK) >> P_ROC_COMMAND_SHIFT) { @@ -659,7 +657,7 @@ PRResult PRDevice::SortReturningData() } case P_ROC_UNREQUESTED_DATA: { ReadData(rd_buffer,1); - DEBUG(PRLog("Pushing onto unreq Q 0x%x\n", rd_buffer[0])); + DEBUG(PRLog(kPRLogVerbose, "Pushing onto unreq Q 0x%x\n", rd_buffer[0])); unrequestedDataQueue.push(rd_buffer[0]); break; } diff --git a/src/PRHardware.cpp b/src/PRHardware.cpp index 886441d..3460012 100644 --- a/src/PRHardware.cpp +++ b/src/PRHardware.cpp @@ -143,7 +143,6 @@ int32_t CreateWatchdogConfigBurst ( uint32_t * burst, bool_t watchdogExpired, int32_t CreateSwitchUpdateConfigBurst ( uint32_t * burst, PRSwitchConfig *switchConfig) { uint32_t addr; - uint32_t i; addr = 0; burst[0] = CreateBurstCommand (P_ROC_BUS_SWITCH_CTRL_SELECT, addr, 1 ); @@ -265,7 +264,7 @@ PRResult PRHardwareOpen() // Open the FTDI device if (ftdi_init(&ftdic) != 0) { - DEBUG(PRLog("Failed to initialize FTDI.\n")); + PRSetLastErrorText("Failed to initialize FTDI."); return kPRFailure; } @@ -279,22 +278,22 @@ PRResult PRHardwareOpen() // We first enumerate all of the devices: int numDevices = ftdi_usb_find_all(&ftdic, &devlist, FTDI_VENDOR_ID, FTDI_FT245RL_PRODUCT_ID); if (numDevices < 0) { - DEBUG(PRLog("ftdi_usb_find_all failed: %d: %s\n", numDevices, ftdi_get_error_string(&ftdic))); + PRSetLastErrorText("ftdi_usb_find_all failed: %d: %s", numDevices, ftdi_get_error_string(&ftdic)); ftdi_deinit(&ftdic); return kPRFailure; } else { - DEBUG(PRLog("Number of FTDI devices found: %d\n", numDevices)); + DEBUG(PRLog(kPRLogInfo, "Number of FTDI devices found: %d\n", numDevices)); for (curdev = devlist; curdev != NULL; i++) { - DEBUG(PRLog("Checking device %d\n", i)); + DEBUG(PRLog(kPRLogInfo, "Checking device %d\n", i)); if ((rc = (int32_t)ftdi_usb_get_strings(&ftdic, curdev->dev, manufacturer, 128, description, 128, NULL, 0)) < 0) { - DEBUG(PRLog(" ftdi_usb_get_strings failed: %d: %s\n", rc, ftdi_get_error_string(&ftdic))); + DEBUG(PRLog(kPRLogInfo, " ftdi_usb_get_strings failed: %d: %s\n", rc, ftdi_get_error_string(&ftdic))); } else { - DEBUG(PRLog(" Device #%d:\n", i)); - DEBUG(PRLog(" Manufacturer: %s\n", manufacturer)); - DEBUG(PRLog(" Description: %s\n", description)); + DEBUG(PRLog(kPRLogInfo, " Device #%d:\n", i)); + DEBUG(PRLog(kPRLogInfo, " Manufacturer: %s\n", manufacturer)); + DEBUG(PRLog(kPRLogInfo, " Description: %s\n", description)); } curdev = curdev->next; } @@ -306,7 +305,7 @@ PRResult PRHardwareOpen() if ((rc = (int32_t)ftdi_usb_open(&ftdic, FTDI_VENDOR_ID, FTDI_FT245RL_PRODUCT_ID)) < 0) { - DEBUG(PRLog("ERROR: Unable to open ftdi device: %d: %s\n", rc, ftdi_get_error_string(&ftdic))); + PRSetLastErrorText("Unable to open ftdi device: %d: %s", rc, ftdi_get_error_string(&ftdic)); return kPRFailure; } else @@ -315,7 +314,7 @@ PRResult PRHardwareOpen() if (ftdic.type == TYPE_R) { uint32_t chipid; ftdi_read_chipid(&ftdic,&chipid); - DEBUG(PRLog("FTDI chip_id = 0x%x\n", chipid)); + DEBUG(PRLog(kPRLogInfo, "FTDI chip_id = 0x%x\n", chipid)); // Set some defaults: ftdi_read_data_set_chunksize(&ftdic, 4096); ftdi_set_latency_timer(&ftdic, 2); // This helps make reads much faster. 16 appeared to be the default. @@ -324,7 +323,7 @@ PRResult PRHardwareOpen() } else { - DEBUG(PRLog("FTDI type != TYPE_R: 0x%x\n", ftdic.type)); + PRSetLastErrorText("FTDI type != TYPE_R: 0x%x", ftdic.type); return kPRFailure; } } diff --git a/src/pinproc.cpp b/src/pinproc.cpp index 8ed6d80..d8bb823 100644 --- a/src/pinproc.cpp +++ b/src/pinproc.cpp @@ -31,19 +31,24 @@ #include "../include/pinproc.h" #include "PRDevice.h" -typedef void (*PRLogCallback)(const char *text); +#define MAX_TEXT (1024) + +typedef void (*PRLogCallback)(PRLogLevel level, const char *text); PRLogCallback logCallback = NULL; +PRLogLevel logLevel = kPRLogError; -void PRLog(const char *format, ...) +void PRLog(PRLogLevel level, const char *format, ...) { - const int maxLogLineLength = 1024; - char line[maxLogLineLength]; + if (level < logLevel) + return; + + char line[MAX_TEXT]; va_list ap; va_start(ap, format); - vsnprintf(line, maxLogLineLength, format, ap); + vsnprintf(line, MAX_TEXT, format, ap); if (logCallback) - logCallback(line); + logCallback(level, line); else fprintf(stderr, line); } @@ -53,6 +58,25 @@ void PRLogSetCallback(PRLogCallback callback) logCallback = callback; } +void PRLogSetLevel(PRLogLevel level) +{ + logLevel = level; +} + +char lastErrorText[MAX_TEXT]; + +void PRSetLastErrorText(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vsnprintf(lastErrorText, MAX_TEXT, format, ap); + PRLog(kPRLogError, "%s\n", lastErrorText); +} + +PR_EXPORT const char *PRGetLastErrorText() +{ + return lastErrorText; +} #define handleAsDevice ((PRDevice*)handle) @@ -124,7 +148,7 @@ PR_EXPORT PRResult PRDriverDisable(PRHandle handle, uint16_t driverNum) PRDriverStateDisable(&driver); return handleAsDevice->DriverUpdateState(&driver); } -PR_EXPORT PRResult PRDriverPulse(PRHandle handle, uint16_t driverNum, int milliseconds) +PR_EXPORT PRResult PRDriverPulse(PRHandle handle, uint16_t driverNum, uint8_t milliseconds) { PRDriverState driver; handleAsDevice->DriverGetState(driverNum, &driver); @@ -160,7 +184,7 @@ PR_EXPORT void PRDriverStateDisable(PRDriverState *driver) driver->patterOffTime = 0; driver->patterEnable = false; } -PR_EXPORT void PRDriverStatePulse(PRDriverState *driver, int milliseconds) +PR_EXPORT void PRDriverStatePulse(PRDriverState *driver, uint8_t milliseconds) { driver->state = 1; driver->timeslots = 0;