diff --git a/README.markdown b/README.markdown index c6ddabc..d27d016 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,8 @@ Library for Gerry Stellenberg's [P-ROC](http://pinballcontrollers.com/) (Pinball ### License -The MIT License +#### The MIT License + Copyright (c) 2009 Gerry Stellenberg, Adam Preble Permission is hereby granted, free of charge, to any person diff --git a/examples/pinproctest/pinproctest.cpp b/examples/pinproctest/pinproctest.cpp index 932d924..5d4efa6 100644 --- a/examples/pinproctest/pinproctest.cpp +++ b/examples/pinproctest/pinproctest.cpp @@ -101,23 +101,14 @@ void ConfigureSwitches(PRHandle proc) { int i; - // Create a basic driver for all of the switches to default to: - PRDriverState defaultDriver; - memset(&defaultDriver, 0x0, sizeof(defaultDriver)); // Set all fields to 0. - for (i = 0; i <= kPRSwitchPhysicalLast; i++) { PRSwitchRule sw; - sw.switchNum = i; sw.notifyHost = true; - sw.changeOutput = false; - sw.linkActive = false; - sw.linkAddress = 0; sw.eventType = kPREventTypeSwitchClosedDebounced; - sw.driver = defaultDriver; - PRSwitchesUpdateRules(proc, &sw, 1); + PRSwitchesUpdateRule(proc, i, &sw, NULL, 0); sw.eventType = kPREventTypeSwitchOpenDebounced; - PRSwitchesUpdateRules(proc, &sw, 1); + PRSwitchesUpdateRule(proc, i, &sw, NULL, 0); } } diff --git a/include/pinproc.h b/include/pinproc.h index 7661e64..e83034e 100644 --- a/include/pinproc.h +++ b/include/pinproc.h @@ -185,17 +185,12 @@ PR_EXPORT PRResult PRDriverPatter(PRHandle handle, uint16_t driverNum, uint16_t /** @} */ typedef struct PRSwitchRule { - uint16_t switchNum; /**< Number of the physical switch, or for linked driver changes the virtual switch number (224 and up). */ PREventType eventType; /**< The event type that this rule generates. Determines closed/open, debounced/non-debounced. */ bool_t notifyHost; - bool_t changeOutput; /**< True if this switch rule should affect a driver output change. */ - bool_t linkActive; /**< True if this switch rule has additional linked driver updates. */ - uint32_t linkAddress; /**< Switch number of the linked driver update. */ - PRDriverState driver; /**< Driver state change to affect once this rule is triggered. */ } PRSwitchRule; -/** Updates the rules for the given switch and PREventType combinations. */ -PR_EXPORT PRResult PRSwitchesUpdateRules(PRHandle handle, PRSwitchRule *rules, int numRules); +/** Updates the rules for the given switch. */ +PR_EXPORT PRResult PRSwitchesUpdateRule(PRHandle handle, uint8_t switchNum, PRSwitchRule *rule, PRDriverState *linkedDrivers, int numDrivers); diff --git a/src/PRDevice.cpp b/src/PRDevice.cpp index c458875..2549e1f 100644 --- a/src/PRDevice.cpp +++ b/src/PRDevice.cpp @@ -83,10 +83,13 @@ void PRDevice::Reset() } for (i = 0; i < maxSwitchRules; i++) { - PRSwitchRule *switchRule = &switchRules[i]; + PRSwitchRuleInternal *switchRule = &switchRules[i]; memset(switchRule, 0x00, sizeof(PRSwitchRule)); - switchRule->switchNum = i; + uint32_t addr = (i << P_ROC_SWITCH_RULE_ADDR_SWITCH_NUM_SHIFT); + ParseSwitchAddress(addr, &switchRule->switchNum, &switchRule->eventType); switchRule->driver.polarity = defaultPolarity; + if (switchRule->switchNum >= kPRSwitchVirtualFirst && switchRule->switchNum <= kPRSwitchVirtualLast) + freeSwitchRules.push(addr); } unrequestedDataQueue.empty(); @@ -245,45 +248,91 @@ PRResult PRDevice::DriverPatter(uint16_t driverNum, uint16_t millisecondsOn, uin } -PRResult PRDevice::SwitchesUpdateRules(PRSwitchRule *rules, int numRules) +PRSwitchRuleInternal *PRDevice::GetSwitchRuleByAddress(uint32_t addr) { - int32_t i,rc; - DEBUG(PRLog("SwitchesUpdateRules: numRules: %d\n", numRules)); - - // Iterate through the array of rules, install each in the P-ROC - for (i=0; i < numRules; i++) { - uint16_t switchNum = rules[i].switchNum; - switchRules[switchNum] = rules[i]; - PRSwitchRule *rule = &switchRules[switchNum]; - PRSwitchRule *nextRule = NULL; - - // See if this is the last item. If not, need to add a link to the current item - if (i != numRules - 1) { - nextRule = &rules[i+1]; - - // Link address is the switch number assigned to the next rule as that's where - // the next rule will be installed - rule->linkAddress = nextRule->switchNum; - rule->linkActive = true; - } - else { - rule->linkActive = false; - rule->linkAddress = 0; - } - - DEBUG(PRLog("Installing switch rule: switchNum: %d, eventType: %d\n link: %d, link address: %d\n", - rule->switchNum, rule->eventType, rule->linkActive, rule->linkAddress)); - - uint32_t rule_burst[4]; - rc = CreateSwitchesUpdateRulesBurst (rule_burst, rule); - - DEBUG(PRLog("words: %d:%d:%d:%d\n", rule_burst[0], rule_burst[1], rule_burst[2], rule_burst[3])); - - rc = WriteData(rule_burst, 4); - } - return rc; + return &switchRules[addr>>P_ROC_SWITCH_RULE_ADDR_SWITCH_NUM_SHIFT]; } +PRResult PRDevice::SwitchesUpdateRule(uint8_t switchNum, PRSwitchRule *rule, PRDriverState *linkedDrivers, int numDrivers) +{ + // Updates a single rule with the associated linked driver state changes. + const int burstSize = 4; + uint32_t burst[burstSize]; + + if (switchNum > kPRSwitchPhysicalLast) // Always true due to data type. + { + DEBUG(PRLog("Switch rule out of range 0-%d\n", kPRSwitchPhysicalLast)); + return kPRFailure; + } + if (freeSwitchRules.size() < numDrivers-1) // -1 because the first switch rule holds the first driver. + { + DEBUG(PRLog("Not enough free switch rules: %d available, need %d\n", freeSwitchRules.size(), numDrivers)); + return kPRFailure; + } + + PRResult res = kPRSuccess; + uint32_t newRuleAddr = CreateSwitchRuleAddr(switchNum, rule->eventType); + + // First we need to check the linked rule to see if the indicated switchNum has any rules that need to be freed: + PRSwitchRuleInternal *oldRule = GetSwitchRuleByAddress(newRuleAddr); + while (oldRule->linkActive) + { + oldRule = GetSwitchRuleByAddress(oldRule->linkAddress); + freeSwitchRules.push(oldRule->linkAddress); + } + + // Now let's setup the first actual rule: + uint32_t firstRuleAddr = newRuleAddr; + PRSwitchRuleInternal *newRule = GetSwitchRuleByAddress(newRuleAddr); + newRule->eventType = rule->eventType; // This shouldn't be necessary. + newRule->notifyHost = rule->notifyHost; + newRule->changeOutput = false; + newRule->linkActive = false; + + while (numDrivers > 0) + { + newRule->changeOutput = true; + newRule->driver = linkedDrivers[0]; + + if (numDrivers > 1) + { + newRule->linkActive = true; + newRule->linkAddress = freeSwitchRules.front(); + freeSwitchRules.pop(); + + CreateSwitchesUpdateRulesBurst(burst, newRule); + + // Prepare for the next rule: + newRule = GetSwitchRuleByAddress(newRule->linkAddress); + } + else + { + newRule->linkActive = false; + CreateSwitchesUpdateRulesBurst(burst, newRule); + } + + // Write the actual rule: + res = WriteData(burst, burstSize); + if (res != kPRSuccess) + { + DEBUG(PRLog("Error while writing switch update, attempting to revert switch rule to a safe state...")); + newRule = GetSwitchRuleByAddress(firstRuleAddr); + newRule->changeOutput = false; + newRule->linkActive = false; + CreateSwitchesUpdateRulesBurst(burst, newRule); + if (WriteData(burst, burstSize) == kPRSuccess) + DEBUG(PRLog("Disabled successfully.\n")); + else + DEBUG(PRLog("Failed to disable.\n")); + return res; + } + + linkedDrivers++; + numDrivers--; + } + + return res; +} int32_t PRDevice::DMDUpdateGlobalConfig(PRDMDGlobalConfig *dmdGlobalConfig) { diff --git a/src/PRDevice.h b/src/PRDevice.h index af4086a..051614d 100644 --- a/src/PRDevice.h +++ b/src/PRDevice.h @@ -37,7 +37,7 @@ using namespace std; #define maxDriverGroups (26) #define maxDrivers (256) -#define maxSwitchRules (256) +#define maxSwitchRules (256<<2) // 8 bits of switchNum indicies plus bits for debounced and state. #ifdef NDEBUG # define DEBUG(block) @@ -69,7 +69,7 @@ public: PRResult DriverSchedule(uint16_t driverNum, uint32_t schedule, uint8_t cycleSeconds, bool now); PRResult DriverPatter(uint16_t driverNum, uint16_t millisecondsOn, uint16_t millisecondsOff, uint16_t originalOnTime); - PRResult SwitchesUpdateRules(PRSwitchRule *rules, int numRules); + PRResult SwitchesUpdateRule(uint8_t switchNum, PRSwitchRule *rule, PRDriverState *linkedDrivers, int numDrivers); PRResult DMDUpdateGlobalConfig(PRDMDGlobalConfig *dmdGlobalConfig); PRResult DMDDraw(uint8_t * dots, uint16_t columns, uint8_t rows, uint8_t numSubFrames); @@ -138,6 +138,8 @@ protected: PRDriverGlobalConfig driverGlobalConfig; PRDriverGroupConfig driverGroups[maxDriverGroups]; PRDriverState drivers[maxDrivers]; - PRSwitchRule switchRules[maxSwitchRules]; - + + PRSwitchRuleInternal switchRules[maxSwitchRules]; + queue freeSwitchRules; /**< Addresses of available switch rules. */ + PRSwitchRuleInternal *GetSwitchRuleByAddress(uint32_t addr); }; diff --git a/src/PRHardware.cpp b/src/PRHardware.cpp index 829ff6b..e5ba16c 100644 --- a/src/PRHardware.cpp +++ b/src/PRHardware.cpp @@ -125,19 +125,35 @@ int32_t CreateDriverUpdateBurst ( uint32_t * burst, PRDriverState *driver) { return kPRSuccess; } -int32_t CreateSwitchesUpdateRulesBurst ( uint32_t * burst, PRSwitchRule *rule_record) { - uint32_t addr; +int32_t CreateSwitchRuleAddr(uint8_t switchNum, PREventType eventType) +{ + uint32_t debounce = (eventType == kPREventTypeSwitchOpenDebounced) || (eventType == kPREventTypeSwitchClosedDebounced) ? 1 : 0; + uint32_t state = (eventType == kPREventTypeSwitchOpenDebounced) || (eventType == kPREventTypeSwitchOpenNondebounced) ? 1 : 0; + + uint32_t addr = ((debounce << P_ROC_SWITCH_RULE_ADDR_DEBOUNCE_SHIFT) | + (state << P_ROC_SWITCH_RULE_ADDR_STATE_SHIFT) | + (switchNum << P_ROC_SWITCH_RULE_ADDR_SWITCH_NUM_SHIFT) ); + return addr; +} + +void ParseSwitchAddress(uint32_t addr, uint8_t *switchNum, PREventType *eventType) +{ + *switchNum = (addr >> P_ROC_SWITCH_RULE_ADDR_SWITCH_NUM_SHIFT) & 0xff; + + bool open = ((addr >> P_ROC_SWITCH_RULE_ADDR_STATE_SHIFT) & 0x1) == 0x1; + bool debounce = ((addr >> P_ROC_SWITCH_RULE_ADDR_DEBOUNCE_SHIFT) & 0x1) == 0x1; + if (open) + *eventType = debounce ? kPREventTypeSwitchOpenDebounced : kPREventTypeSwitchOpenNondebounced; + else + *eventType = debounce ? kPREventTypeSwitchClosedDebounced : kPREventTypeSwitchClosedNondebounced; +} + +int32_t CreateSwitchesUpdateRulesBurst ( uint32_t * burst, PRSwitchRuleInternal *rule_record) { + uint32_t addr = CreateSwitchRuleAddr(rule_record->switchNum, rule_record->eventType); uint32_t driver_command[3]; CreateDriverUpdateBurst ( driver_command, &(rule_record->driver)); - uint32_t debounce = (rule_record->eventType == kPREventTypeSwitchOpenDebounced) || (rule_record->eventType == kPREventTypeSwitchClosedDebounced) ? 1 : 0; - uint32_t state = (rule_record->eventType == kPREventTypeSwitchOpenDebounced) || (rule_record->eventType == kPREventTypeSwitchOpenNondebounced) ? 1 : 0; - - addr = ( (debounce << P_ROC_SWITCH_RULE_ADDR_DEBOUNCE_SHIFT) | - (state << P_ROC_SWITCH_RULE_ADDR_STATE_SHIFT) | - (rule_record->switchNum << P_ROC_SWITCH_RULE_ADDR_SWITCH_NUM_SHIFT) ); - burst[0] = CreateBurstCommand (P_ROC_BUS_STATE_CHANGE_PROC_SELECT, addr, 3 ); burst[1] = driver_command[1]; burst[2] = driver_command[2]; diff --git a/src/PRHardware.h b/src/PRHardware.h index 875542b..86640e1 100644 --- a/src/PRHardware.h +++ b/src/PRHardware.h @@ -140,6 +140,15 @@ const uint32_t P_ROC_DMD_RCLK_LOW_CYCLES_SHIFT = 24; const uint32_t P_ROC_DMD_DOT_TABLE_BASE_ADDR = 0x1000; +typedef struct PRSwitchRuleInternal { + uint8_t switchNum; /**< Number of the physical switch, or for linked driver changes the virtual switch number (224 and up). */ + PREventType eventType; /**< The event type that this rule generates. Determines closed/open, debounced/non-debounced. */ + bool_t notifyHost; + bool_t changeOutput; /**< True if this switch rule should affect a driver output change. */ + bool_t linkActive; /**< True if this switch rule has additional linked driver updates. */ + uint32_t linkAddress; /**< Switch number of the linked driver update. */ + PRDriverState driver; /**< Driver state change to affect once this rule is triggered. */ +} PRSwitchRuleInternal; uint32_t CreateRegRequestWord( uint32_t select, uint32_t addr, uint32_t num_words); @@ -147,7 +156,10 @@ uint32_t CreateBurstCommand ( uint32_t select, uint32_t addr, uint32_t num_words int32_t CreateDriverUpdateGlobalConfigBurst ( uint32_t * burst, PRDriverGlobalConfig *driver_globals); int32_t CreateDriverUpdateGroupConfigBurst ( uint32_t * burst, PRDriverGroupConfig *driver_group); int32_t CreateDriverUpdateBurst ( uint32_t * burst, PRDriverState *driver); -int32_t CreateSwitchesUpdateRulesBurst ( uint32_t * burst, PRSwitchRule *rule_record); +int32_t CreateSwitchesUpdateRulesBurst ( uint32_t * burst, PRSwitchRuleInternal *rule_record); int32_t CreateDMDUpdateGlobalConfigBurst ( uint32_t * burst, PRDMDConfig *dmd_config); +void ParseSwitchAddress(uint32_t addr, uint8_t *switchNum, PREventType *eventType); +int32_t CreateSwitchRuleAddr(uint8_t switchNum, PREventType eventType); + #endif // _PROC_HARDWARE_H_ diff --git a/src/pinproc.cpp b/src/pinproc.cpp index a89b82d..f7843f1 100644 --- a/src/pinproc.cpp +++ b/src/pinproc.cpp @@ -126,9 +126,9 @@ PR_EXPORT PRResult PRDriverPatter(PRHandle handle, uint16_t driverNum, uint16_t // Switches -PR_EXPORT PRResult PRSwitchesUpdateRules(PRHandle handle, PRSwitchRule *rules, int numRules) +PR_EXPORT PRResult PRSwitchesUpdateRule(PRHandle handle, uint8_t switchNum, PRSwitchRule *rule, PRDriverState *linkedDrivers, int numDrivers) { - return handleAsDevice->SwitchesUpdateRules(rules, numRules); + return handleAsDevice->SwitchesUpdateRule(switchNum, rule, linkedDrivers, numDrivers); } PR_EXPORT int32_t PRDMDUpdateGlobalConfig(PRHandle handle, PRDMDGlobalConfig *dmdGlobalConfig)