From 94e2f6759dc9d6fdfeeb6a576698064ff36b17ab Mon Sep 17 00:00:00 2001 From: YukMingLaw Date: Fri, 8 Oct 2021 00:42:14 +0800 Subject: [PATCH 1/3] fix bugs of mulit axes condition effect --- src/DynamicHID/PIDReportHandler.cpp | 31 ++--- src/DynamicHID/PIDReportType.h | 39 +++--- src/FFBDescriptor.h | 5 +- src/Joystick.cpp | 195 +++++++++++++--------------- src/Joystick.h | 3 +- 5 files changed, 125 insertions(+), 148 deletions(-) diff --git a/src/DynamicHID/PIDReportHandler.cpp b/src/DynamicHID/PIDReportHandler.cpp index 7221d77..8bfcc62 100644 --- a/src/DynamicHID/PIDReportHandler.cpp +++ b/src/DynamicHID/PIDReportHandler.cpp @@ -169,17 +169,6 @@ void PIDReportHandler::SetEffect(USB_FFBReport_SetEffect_Output_Data_t* data) effect->effectType = data->effectType; effect->gain = data->gain; effect->enableAxis = data->enableAxis; - //Serial.print("dX: "); - //Serial.print(effect->directionX); - //Serial.print(" dX: "); - //Serial.print(effect->directionY); - //Serial.print(" eT: "); - //Serial.print(effect->effectType); - //Serial.print(" eA: "); - //Serial.println(effect->enableAxis); - // effect->triggerRepeatInterval; - // effect->samplePeriod; // 0..32767 ms - // effect->triggerButton; } void PIDReportHandler::SetEnvelope(USB_FFBReport_SetEnvelope_Output_Data_t* data, volatile TEffectState* effect) @@ -192,14 +181,14 @@ void PIDReportHandler::SetEnvelope(USB_FFBReport_SetEnvelope_Output_Data_t* data void PIDReportHandler::SetCondition(USB_FFBReport_SetCondition_Output_Data_t* data, volatile TEffectState* effect) { - uint8_t axis = data->parameterBlockOffset; - effect->conditions[axis].cpOffset = data->cpOffset; - effect->conditions[axis].positiveCoefficient = data->positiveCoefficient; - effect->conditions[axis].negativeCoefficient = data->negativeCoefficient; - effect->conditions[axis].positiveSaturation = data->positiveSaturation; - effect->conditions[axis].negativeSaturation = data->negativeSaturation; - effect->conditions[axis].deadBand = data->deadBand; - effect->conditionBlocksCount++; + uint8_t axis = data->parameterBlockOffset; + effect->conditions[axis].cpOffset = data->cpOffset; + effect->conditions[axis].positiveCoefficient = data->positiveCoefficient; + effect->conditions[axis].negativeCoefficient = data->negativeCoefficient; + effect->conditions[axis].positiveSaturation = data->positiveSaturation; + effect->conditions[axis].negativeSaturation = data->negativeSaturation; + effect->conditions[axis].deadBand = data->deadBand; + effect->conditionBlocksCount++; } void PIDReportHandler::SetPeriodic(USB_FFBReport_SetPeriodic_Output_Data_t* data, volatile TEffectState* effect) @@ -248,6 +237,8 @@ void PIDReportHandler::UppackUsbData(uint8_t* data, uint16_t len) //Serial.print("len:"); //Serial.println(len); uint8_t effectId = data[1]; // effectBlockIndex is always the second byte. + //Serial.println("eid:"); + //Serial.println(effectId); switch (data[0]) // reportID { case 1: @@ -328,4 +319,4 @@ uint8_t* PIDReportHandler::getPIDBlockLoad() uint8_t* PIDReportHandler::getPIDStatus() { return (uint8_t*)& pidState; -} +} \ No newline at end of file diff --git a/src/DynamicHID/PIDReportType.h b/src/DynamicHID/PIDReportType.h index dadd5f8..20db921 100644 --- a/src/DynamicHID/PIDReportType.h +++ b/src/DynamicHID/PIDReportType.h @@ -28,7 +28,7 @@ #define _PIDREPORTTYPE_H #define MAX_EFFECTS 14 -#define FFB_AXIS_COUNT 2 +#define MAX_FFB_AXIS_COUNT 0x02 #define SIZE_EFFECT sizeof(TEffectState) #define MEMORY_SIZE (uint16_t)(MAX_EFFECTS*SIZE_EFFECT) #define TO_LT_END_16(x) ((x<<8)&0xFF00)|((x>>8)&0x00FF) @@ -69,8 +69,8 @@ typedef struct//FFB: Set Envelope Output Report uint8_t effectBlockIndex; // 1..40 uint16_t attackLevel; uint16_t fadeLevel; - uint16_t attackTime; // ms - uint16_t fadeTime; // ms + uint32_t attackTime; // ms + uint32_t fadeTime; // ms } USB_FFBReport_SetEnvelope_Output_Data_t; typedef struct// FFB: Set Condition Output Report @@ -93,7 +93,7 @@ typedef struct//FFB: Set Periodic Output Report uint16_t magnitude; int16_t offset; uint16_t phase; // 0..255 (=0..359, exp-2) - uint16_t period; // 0..32767 ms + uint32_t period; // 0..32767 ms } USB_FFBReport_SetPeriodic_Output_Data_t; typedef struct//FFB: Set ConstantForce Output Report @@ -184,6 +184,16 @@ typedef struct// FFB: PID Pool Feature Report uint8_t memoryManagement; // Bits: 0=DeviceManagedPool, 1=SharedParameterBlocks } USB_FFBReport_PIDPool_Feature_Data_t; +typedef struct { + int16_t cpOffset; // -128..127 + int16_t positiveCoefficient; // -128..127 + int16_t negativeCoefficient; // -128..127 + uint16_t positiveSaturation; // -128..127 + uint16_t negativeSaturation; // -128..127 + uint16_t deadBand; // 0..255 + +} TEffectCondition; + ///effect #define USB_DURATION_INFINITE 0x7FFF @@ -213,34 +223,29 @@ typedef struct// FFB: PID Pool Feature Report #define INERTIA_DEADBAND 0x30 #define FRICTION_DEADBAND 0x30 -typedef struct { - int16_t cpOffset; // -128..127 - int16_t positiveCoefficient; // -128..127 - int16_t negativeCoefficient; // -128..127 - uint16_t positiveSaturation; // -128..127 - uint16_t negativeSaturation; // -128..127 - uint16_t deadBand; // 0..255 -} TEffectCondition; - typedef struct { volatile uint8_t state; // see constants uint8_t effectType; // int16_t offset; uint8_t gain; + //envelop int16_t attackLevel, fadeLevel; + uint16_t fadeTime, attackTime; + int16_t magnitude; + //direction uint8_t enableAxis; // bits: 0=X, 1=Y, 2=DirectionEnable uint8_t directionX; // angle (0=0 .. 255=360deg) uint8_t directionY; // angle (0=0 .. 255=360deg) uint8_t conditionBlocksCount; - - TEffectCondition conditions[FFB_AXIS_COUNT]; - + //condition + TEffectCondition conditions[MAX_FFB_AXIS_COUNT]; + //periodic uint16_t phase; // 0..255 (=0..359, exp-2) int16_t startMagnitude; int16_t endMagnitude; uint16_t period; // 0..32767 ms - uint16_t duration, fadeTime, attackTime, elapsedTime; + uint16_t duration, elapsedTime; uint64_t startTime; } TEffectState; #endif diff --git a/src/FFBDescriptor.h b/src/FFBDescriptor.h index b3d30dc..73a0c2c 100644 --- a/src/FFBDescriptor.h +++ b/src/FFBDescriptor.h @@ -183,7 +183,8 @@ static const uint8_t pidReportDescriptor[] PROGMEM= { 0x55, 0xFD, // Unit Exponent (FDh) (X10^-3 ==> Milisecond) 0x27, 0xFF, 0x7F, 0, 0, // Logical Maximum (4294967295) 0x47, 0xFF, 0x7F, 0, 0, // Physical Maximum (4294967295) - 0x75, 0x20, // Report Size (32) + 0x75, 0x20, // Report Size (16) + 0x95, 0x02, // Report Count (2) 0x91, 0x02, // Output (Data,Var,Abs) 0x45, 0x00, // Physical Maximum (0) 0x66, 0x00, 0x00, // Unit (0) @@ -292,7 +293,7 @@ static const uint8_t pidReportDescriptor[] PROGMEM= { 0x47, 0xFF, 0x7F, 0, 0, // Physical Maximum (32K) 0x66, 0x03, 0x10, // Unit (1003h) (English Linear, Seconds) 0x55, 0xFD, // Unit Exponent (FDh) (X10^-3 ==> Milisecond) - 0x75, 0x20, // Report Size (32) + 0x75, 0x20, // Report Size (16) 0x95, 0x01, // Report Count (1) 0x91, 0x02, // Output (Data,Var,Abs) 0x66, 0x00, 0x00, // Unit (0) diff --git a/src/Joystick.cpp b/src/Joystick.cpp index ab6bb60..c8c296a 100644 --- a/src/Joystick.cpp +++ b/src/Joystick.cpp @@ -105,7 +105,7 @@ Joystick_::Joystick_( static uint8_t tempHidReportDescriptor[150]; int hidReportDescriptorSize = 0; - // // USAGE_PAGE (Generic Desktop) + // USAGE_PAGE (Generic Desktop) tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; @@ -490,17 +490,15 @@ void Joystick_::getForce(int32_t* forces) { forceCalculator(forces); } -int32_t Joystick_::getEffectForce(volatile TEffectState& effect,Gains _gains,EffectParams _effect_params, uint8_t axis){ - - uint8_t direction; +int32_t Joystick_::getEffectForce(volatile TEffectState& effect, Gains _gains, EffectParams _effect_params, uint8_t axis){ + uint8_t direction; uint8_t condition; - - bool useForceDirectionForConditionEffect = (effect.enableAxis == DIRECTION_ENABLE && effect.conditionsCount == 1); + bool useForceDirectionForConditionEffect = (effect.enableAxis == DIRECTION_ENABLE && effect.conditionBlocksCount == 1); if (effect.enableAxis == DIRECTION_ENABLE) { direction = effect.directionX; - if (effect.conditionsCount > 1) { + if (effect.conditionBlocksCount > 1) { condition = axis; } else { condition = 0; // only one Condition Parameter Block is defined @@ -514,7 +512,6 @@ int32_t Joystick_::getEffectForce(volatile TEffectState& effect,Gains _gains,Eff float angle = (direction * 360.0 / 255.0) * DEG_TO_RAD; float angle_ratio = axis == 0 ? sin(angle) : -1 * cos(angle); - int32_t force = 0; switch (effect.effectType) { @@ -541,16 +538,16 @@ int32_t Joystick_::getEffectForce(volatile TEffectState& effect,Gains _gains,Eff break; case USB_EFFECT_SPRING://8 force = ConditionForceCalculator(effect, NormalizeRange(_effect_params.springPosition, _effect_params.springMaxPosition), condition) * _gains.springGain; - if (useForceDirectionForConditionEffect) { + if (useForceDirectionForConditionEffect) { force *= angle_ratio; } - break; + break; case USB_EFFECT_DAMPER://9 force = ConditionForceCalculator(effect, NormalizeRange(_effect_params.damperVelocity, _effect_params.damperMaxVelocity), condition) * _gains.damperGain; - if (useForceDirectionForConditionEffect) { + if (useForceDirectionForConditionEffect) { force *= angle_ratio; } - break; + break; case USB_EFFECT_INERTIA://10 if (_effect_params.inertiaAcceleration < 0 && _effect_params.frictionPositionChange < 0) { force = ConditionForceCalculator(effect, abs(NormalizeRange(_effect_params.inertiaAcceleration, _effect_params.inertiaMaxAcceleration)), condition) * _gains.inertiaGain; @@ -563,11 +560,11 @@ int32_t Joystick_::getEffectForce(volatile TEffectState& effect,Gains _gains,Eff } break; case USB_EFFECT_FRICTION://11 - force = ConditionForceCalculator(effect, NormalizeRange(_effect_params.frictionPositionChange, _effect_params.frictionMaxPositionChange), condition) * _gains.frictionGain; - if (useForceDirectionForConditionEffect) { - force *= angle_ratio; - } - break; + force = ConditionForceCalculator(effect, NormalizeRange(_effect_params.frictionPositionChange, _effect_params.frictionMaxPositionChange), condition) * _gains.frictionGain; + if (useForceDirectionForConditionEffect) { + force *= angle_ratio; + } + break; case USB_EFFECT_CUSTOM://12 break; } @@ -577,9 +574,8 @@ int32_t Joystick_::getEffectForce(volatile TEffectState& effect,Gains _gains,Eff void Joystick_::forceCalculator(int32_t* forces) { - forces[0] = 0; + forces[0] = 0; forces[1] = 0; - int32_t force = 0; for (int id = 0; id < MAX_EFFECTS; id++) { volatile TEffectState& effect = DynamicHID().pidReportHandler.g_EffectStates[id]; if ((effect.state == MEFFECTSTATE_PLAYING) && @@ -587,45 +583,34 @@ void Joystick_::forceCalculator(int32_t* forces) { (effect.duration == USB_DURATION_INFINITE)) && !DynamicHID().pidReportHandler.devicePaused) { - if (effect.enableAxis == DIRECTION_ENABLE - || effect.enableAxis & X_AXIS_ENABLE) - { - forces[0] += (int32_t)(getEffectForce(effect,m_gains[0], m_effect_params[0], 0)); - } - if (effect.enableAxis == DIRECTION_ENABLE - || effect.enableAxis & Y_AXIS_ENABLE) - { - forces[1] += (int32_t)(getEffectForce(effect,m_gains[1], m_effect_params[1], 1)); - } - + forces[0] += (int32_t)(getEffectForce(effect, m_gains[0], m_effect_params[0], 0)); + forces[1] += (int32_t)(getEffectForce(effect, m_gains[1], m_effect_params[1], 1)); } } - forces[0] = (int32_t)((float)1.00 * forces[0] * m_gains[0].totalGain / 10000); // each effect gain * total effect gain = 10000 - forces[1] = (int32_t)((float)1.00 * forces[1] * m_gains[1].totalGain / 10000); // each effect gain * total effect gain = 10000 - forces[0] = constrain(forces[0], -255, 255); - forces[1] = constrain(forces[1], -255, 255); + forces[0] = (int32_t)((float)1.0 * forces[0] * m_gains[0].totalGain / 10000); // each effect gain * total effect gain = 10000 + forces[1] = (int32_t)((float)1.0 * forces[1] * m_gains[1].totalGain / 10000); // each effect gain * total effect gain = 10000 + forces[0] = map(forces[0], -10000, 10000, -255, 255); + forces[1] = map(forces[1], -10000, 10000, -255, 255); } int32_t Joystick_::ConstantForceCalculator(volatile TEffectState& effect) { - float tempforce = (float)effect.magnitude * effect.gain / 255; - tempforce = map(tempforce, -10000, 10000, -255, 255); - return (int32_t)tempforce; + return ApplyEnvelope(effect, (int32_t)effect.magnitude); } int32_t Joystick_::RampForceCalculator(volatile TEffectState& effect) { - int32_t rampForce = effect.startMagnitude + effect.elapsedTime * (effect.endMagnitude - effect.startMagnitude) / effect.duration; - return rampForce; + int32_t tempforce = (int32_t)(effect.startMagnitude + effect.elapsedTime * 1.0 * (effect.endMagnitude - effect.startMagnitude) / effect.duration); + return ApplyEnvelope(effect, tempforce); } int32_t Joystick_::SquareForceCalculator(volatile TEffectState& effect) { - int32_t offset = effect.offset * 2; - uint32_t magnitude = effect.magnitude; - uint32_t elapsedTime = effect.elapsedTime; - uint32_t phase = effect.phase; - uint32_t period = effect.period; + int16_t offset = effect.offset * 2; + int16_t magnitude = effect.magnitude; + uint16_t phase = effect.phase; + uint16_t elapsedTime = effect.elapsedTime; + uint16_t period = effect.period; int32_t maxMagnitude = offset + magnitude; int32_t minMagnitude = offset - magnitude; @@ -640,35 +625,36 @@ int32_t Joystick_::SquareForceCalculator(volatile TEffectState& effect) int32_t Joystick_::SinForceCalculator(volatile TEffectState& effect) { - float offset = effect.offset * 2; - float magnitude = effect.magnitude; - float phase = effect.phase; - float timeTemp = effect.elapsedTime; - float period = effect.period; - // float angle = ((timeTemp / period) + (phase / 255) * period) * 2 * PI; - float angle = ((timeTemp / period) * 2 * PI + (float)(phase / 36000)); + int16_t offset = effect.offset * 2; + int16_t magnitude = effect.magnitude; + uint16_t phase = effect.phase; + uint16_t timeTemp = effect.elapsedTime; + uint16_t period = effect.period; + float angle = 0.0; + if(period != 0) + angle = ((timeTemp * 1.0 / period) * 2 * PI + (phase / 36000.0)); float sine = sin(angle); - float tempforce = sine * magnitude; + int32_t tempforce = (int32_t)(sine * magnitude); tempforce += offset; return ApplyEnvelope(effect, tempforce); } int32_t Joystick_::TriangleForceCalculator(volatile TEffectState& effect) { - float offset = effect.offset * 2; - float magnitude = effect.magnitude; - float elapsedTime = effect.elapsedTime; - uint32_t phase = effect.phase; - uint32_t period = effect.period; - float periodF = effect.period; + int16_t offset = effect.offset * 2; + int16_t magnitude = effect.magnitude; + uint16_t elapsedTime = effect.elapsedTime; + uint16_t phase = effect.phase; + uint16_t period = effect.period; + uint16_t periodF = effect.period; - float maxMagnitude = offset + magnitude; - float minMagnitude = offset - magnitude; - uint32_t phasetime = (phase * period) / 255; + int16_t maxMagnitude = offset + magnitude; + int16_t minMagnitude = offset - magnitude; + int32_t phasetime = (phase * period) / 255; uint32_t timeTemp = elapsedTime + phasetime; - float reminder = timeTemp % period; - float slope = ((maxMagnitude - minMagnitude) * 2) / periodF; - float tempforce = 0; + int32_t reminder = timeTemp % period; + int32_t slope = ((maxMagnitude - minMagnitude) * 2) / periodF; + int32_t tempforce = 0; if (reminder > (periodF / 2)) tempforce = slope * (periodF - reminder); else tempforce = slope * reminder; tempforce += minMagnitude; @@ -677,20 +663,20 @@ int32_t Joystick_::TriangleForceCalculator(volatile TEffectState& effect) int32_t Joystick_::SawtoothDownForceCalculator(volatile TEffectState& effect) { - float offset = effect.offset * 2; - float magnitude = effect.magnitude; - float elapsedTime = effect.elapsedTime; - float phase = effect.phase; - uint32_t period = effect.period; - float periodF = effect.period; + int16_t offset = effect.offset * 2; + int16_t magnitude = effect.magnitude; + uint16_t elapsedTime = effect.elapsedTime; + uint16_t phase = effect.phase; + uint16_t period = effect.period; + uint16_t periodF = effect.period; - float maxMagnitude = offset + magnitude; - float minMagnitude = offset - magnitude; + int16_t maxMagnitude = offset + magnitude; + int16_t minMagnitude = offset - magnitude; int32_t phasetime = (phase * period) / 255; uint32_t timeTemp = elapsedTime + phasetime; - float reminder = timeTemp % period; - float slope = (maxMagnitude - minMagnitude) / periodF; - float tempforce = 0; + int32_t reminder = timeTemp % period; + int32_t slope = (maxMagnitude - minMagnitude) / periodF; + int32_t tempforce = 0; tempforce = slope * (period - reminder); tempforce += minMagnitude; return ApplyEnvelope(effect, tempforce); @@ -698,20 +684,20 @@ int32_t Joystick_::SawtoothDownForceCalculator(volatile TEffectState& effect) int32_t Joystick_::SawtoothUpForceCalculator(volatile TEffectState& effect) { - float offset = effect.offset * 2; - float magnitude = effect.magnitude; - float elapsedTime = effect.elapsedTime; - uint32_t phase = effect.phase; - uint32_t period = effect.period; - float periodF = effect.period; + int16_t offset = effect.offset * 2; + int16_t magnitude = effect.magnitude; + uint16_t elapsedTime = effect.elapsedTime; + uint16_t phase = effect.phase; + uint16_t period = effect.period; + uint16_t periodF = effect.period; - float maxMagnitude = offset + magnitude; - float minMagnitude = offset - magnitude; + int16_t maxMagnitude = offset + magnitude; + int16_t minMagnitude = offset - magnitude; int32_t phasetime = (phase * period) / 255; uint32_t timeTemp = elapsedTime + phasetime; - float reminder = timeTemp % period; - float slope = (maxMagnitude - minMagnitude) / periodF; - float tempforce = 0; + int32_t reminder = timeTemp % period; + int32_t slope = (maxMagnitude - minMagnitude) / periodF; + int32_t tempforce = 0; tempforce = slope * reminder; tempforce += minMagnitude; return ApplyEnvelope(effect, tempforce); @@ -719,12 +705,12 @@ int32_t Joystick_::SawtoothUpForceCalculator(volatile TEffectState& effect) int32_t Joystick_::ConditionForceCalculator(volatile TEffectState& effect, float metric, uint8_t axis) { - float deadBand; - float cpOffset; - float negativeCoefficient; - float negativeSaturation; - float positiveSaturation; - float positiveCoefficient; + float deadBand; + float cpOffset; + float positiveCoefficient; + float negativeCoefficient; + float positiveSaturation; + float negativeSaturation; deadBand = effect.conditions[axis].deadBand; cpOffset = effect.conditions[axis].cpOffset; @@ -733,15 +719,14 @@ int32_t Joystick_::ConditionForceCalculator(volatile TEffectState& effect, float positiveSaturation = effect.conditions[axis].positiveSaturation; positiveCoefficient = effect.conditions[axis].positiveCoefficient; - float tempForce = 0; - - if (metric < (cpOffset - deadBand)) { + float tempForce = 0; + if (metric < (cpOffset - deadBand)) + { tempForce = (metric - (float)1.00*(cpOffset - deadBand)/10000) * negativeCoefficient; - // tempForce = ((float)1.00 * (cpOffset - deadBand) / 10000 - metric) * negativeCoefficient; - tempForce = (tempForce < -negativeSaturation ? -negativeSaturation : tempForce); // I dont know why negativeSaturation = 55536.00 after negativeSaturation = -effect.negativeSaturation; - // tempForce = (tempForce < (-effect.negativeCoefficient) ? (-effect.negativeCoefficient) : tempForce); + tempForce = (tempForce < -negativeSaturation ? -negativeSaturation : tempForce); } - else if (metric > (cpOffset + deadBand)) { + else if (metric > (cpOffset + deadBand)) + { tempForce = (metric - (float)1.00 * (cpOffset + deadBand) / 10000) * positiveCoefficient; tempForce = (tempForce > positiveSaturation ? positiveSaturation : tempForce); } @@ -760,7 +745,6 @@ int32_t Joystick_::ConditionForceCalculator(volatile TEffectState& effect, float default: break; } - tempForce = map(tempForce, -10000, 10000, -255, 255); return (int32_t)tempForce; } @@ -768,9 +752,9 @@ float Joystick_::NormalizeRange(int32_t x, int32_t maxValue) { return (float)x * 1.00 / maxValue; } -int32_t Joystick_::ApplyGain(uint8_t value, uint8_t gain) +int32_t Joystick_::ApplyGain(int16_t value, uint8_t gain) { - int32_t value_32 = (int16_t)value; + int32_t value_32 = value; return ((value_32 * gain) / 255); } @@ -787,8 +771,7 @@ int32_t Joystick_::ApplyEnvelope(volatile TEffectState& effect, int32_t value) if (elapsedTime < attackTime) { - newValue = (magnitude - attackLevel) * elapsedTime; - newValue /= attackTime; + newValue = (magnitude - attackLevel) * elapsedTime / attackTime; newValue += attackLevel; } if (elapsedTime > (duration - fadeTime)) @@ -797,9 +780,7 @@ int32_t Joystick_::ApplyEnvelope(volatile TEffectState& effect, int32_t value) newValue /= fadeTime; newValue += fadeLevel; } - - newValue *= value; - newValue /= 255; + newValue = newValue * value / magnitude; return newValue; } diff --git a/src/Joystick.h b/src/Joystick.h index 9aced73..bc09e28 100644 --- a/src/Joystick.h +++ b/src/Joystick.h @@ -58,7 +58,6 @@ #define DIRECTION_ENABLE 0x04 #define X_AXIS_ENABLE 0x01 #define Y_AXIS_ENABLE 0x02 -#define FFB_AXIS_COUNT 0x02 #define FORCE_FEEDBACK_MAXGAIN 100 #define DEG_TO_RAD ((float)((float)3.14159265359 / 180.0)) @@ -153,7 +152,7 @@ class Joystick_ ///force calculate funtion float NormalizeRange(int32_t x, int32_t maxValue); int32_t ApplyEnvelope(volatile TEffectState& effect, int32_t value); - int32_t ApplyGain(uint8_t value, uint8_t gain); + int32_t ApplyGain(int16_t value, uint8_t gain); int32_t ConstantForceCalculator(volatile TEffectState& effect); int32_t RampForceCalculator(volatile TEffectState& effect); int32_t SquareForceCalculator(volatile TEffectState& effect); From 449e01a49f1c29642f051a0fa9e70e7031807c3f Mon Sep 17 00:00:00 2001 From: YukMingLaw Date: Mon, 8 Jan 2024 11:34:27 +0800 Subject: [PATCH 2/3] add version info --- version.info | 1 + 1 file changed, 1 insertion(+) create mode 100644 version.info diff --git a/version.info b/version.info new file mode 100644 index 0000000..7965b36 --- /dev/null +++ b/version.info @@ -0,0 +1 @@ +v0.9.0 \ No newline at end of file From ed86e27e63f53214dbdee68f9977657e85ad4f7b Mon Sep 17 00:00:00 2001 From: YukMingLaw Date: Mon, 8 Jan 2024 15:25:01 +0800 Subject: [PATCH 3/3] release v1.0.0 --- Readme.md | 83 +++- .../SingleJoystickFFB/SingleJoystickFFB.ino | 15 +- .../YourFirstFFBController/DigitalWriteFast.h | 394 ++++++++++++++++++ .../YourFirstFFBController.ino | 136 ++++++ .../YourFirstFFBController/ffbcontroller.png | Bin 0 -> 84343 bytes src/DynamicHID/DynamicHID.cpp | 1 - src/DynamicHID/DynamicHID.h | 2 +- src/FFBDescriptor.h | 2 +- src/Joystick.cpp | 22 +- src/Joystick.h | 6 +- version.info | 2 +- 11 files changed, 644 insertions(+), 19 deletions(-) create mode 100644 examples/YourFirstFFBController/DigitalWriteFast.h create mode 100644 examples/YourFirstFFBController/YourFirstFFBController.ino create mode 100644 examples/YourFirstFFBController/ffbcontroller.png diff --git a/Readme.md b/Readme.md index 0bf9476..1a6e38b 100644 --- a/Readme.md +++ b/Readme.md @@ -2,12 +2,22 @@ ## Statement +### The v1.0.0 new version library is now available, with the following major updates: + +1.Fixed an issue where frame rates decrease in certain games after connecting a force feedback controller. Due to issues with the mechanism by which some games send force feedback data, the Arduino did not promptly notify the computer upon receiving the force feedback data, resulting in lost frames in these games. To resolve this problem, the reception of force feedback data has been moved to be handled within an ISR (Interrupt Service Routine) on the Arduino, which means that some interfaces have undergone changes compared to v0.9.0. Example code will be provided to demonstrate the corresponding modifications. This fix has been tested and confirmed working in games such as 'Horizon 4' and 'Horizon 5'. + +2.Corrected several errors in the force feedback calculation logic. + +3.A complete demonstration project has been provided for developers to reference. It can be found at the path: examples/YourFirstFFBController. + ### This is a joy library for Atmega32UX chip with force feedback, which can be used to make game handle with vibration, game steering wheel with force feedback, etc.Multi-axis-force-feedback feature is added. ## Usage -### 0. example:`examples/JoystickWithFFB/JoystickWithFFB.ino` +### 0. example: + +`examples\SingleJoystickFFB\SingleJoystickFFB.ino` ### 1. create `JoyStick` object @@ -60,7 +70,7 @@ struct Gains{ */ int8_t setGains(Gains* _gains); ``` -#### example code +#### example code p1 ``` #include "Joystick.h" @@ -118,7 +128,7 @@ struct EffectParams{ int8_t setEffectParams(EffectParams* _effect_params); ``` -#### example code +#### example code p2 ``` #include "Joystick.h" @@ -150,13 +160,12 @@ void loop(){ myeffectparams[1].springPosition = analogRead(A3); Joystick.setEffectParams(myeffectparams); - Joystick.getForce(forces); } ``` -### 4.Finally,get the force value with +### 4.Finally,open ISR to recevie the PID data, than get the force value with `JoyStick.getForce(int32_t* forces)` @@ -170,10 +179,74 @@ return type `void` range`[-255,255]` +#### example code p3 +``` +#include "Joystick.h" + +Joystick_ Joystick(); + +int32_t forces[2]={0}; + +Gains mygains[2]; +EffectParams myeffectparams[2]; + +//set X Axis gains +mygains[0].totalGain = 50; +mygains[0].springGain = 80; + +//set Y Axis gains +mygains[1].totalGain = 50; +mygains[1].springGain = 70; + +void setup(){ + //enable gains REQUIRED + Joystick.setGains(mygains); + + //set Timer3 + cli(); + TCCR3A = 0; //set TCCR1A 0 + TCCR3B = 0; //set TCCR1B 0 + TCNT3 = 0; //counter init + OCR3A = 399; + TCCR3B |= (1 << WGM32); //open CTC mode + TCCR3B |= (1 << CS31); //set CS11 1(8-fold Prescaler) + TIMSK3 |= (1 << OCIE3A); + sei(); +} + +//ISR +ISR(TIMER3_COMPA_vect){ + Joystick.getUSBPID(); +} + +void loop(){ + //set X Axis Spring Effect Param + myeffectparams[0].springMaxPosition = 1023; + myeffectparams[0].springPosition = analogRead(A2); + + //set Y Axis Spring Effect Param + myeffectparams[1].springMaxPosition = 1023; + myeffectparams[1].springPosition = analogRead(A3); + + Joystick.setEffectParams(myeffectparams); + + Joystick.getForce(forces); + Serial.print(forces[0]); + Serial.print(" "); + Serial.println(forces[1]); +} + +``` + #### **Pay Attention!** **`Joystick.setGains(mygains)` and `Joystick.setEffectParams(myeffectparams)` must be invoked before `JoyStick.getForce(int32_t* forces)`** +### 5.DEMO +`examples/YourFirstFFBController` + +![diagramm](examples/YourFirstFFBController/ffbcontroller.png) + ## Ref ### This library is based on [Heironimus](https://github.com/MHeironimus/ArduinoJoystickLibrary) and [hoantv](https://github.com/hoantv/VNWheel) 's work,very grateful for their work. diff --git a/examples/SingleJoystickFFB/SingleJoystickFFB.ino b/examples/SingleJoystickFFB/SingleJoystickFFB.ino index 604fba1..dd08a77 100644 --- a/examples/SingleJoystickFFB/SingleJoystickFFB.ino +++ b/examples/SingleJoystickFFB/SingleJoystickFFB.ino @@ -25,6 +25,20 @@ void setup(){ //enable gains REQUIRED Joystick.setGains(mygains); Joystick.begin(); + + cli(); + TCCR3A = 0; //set TCCR1A 0 + TCCR3B = 0; //set TCCR1B 0 + TCNT3 = 0; //counter init + OCR3A = 399; + TCCR3B |= (1 << WGM32); //open CTC mode + TCCR3B |= (1 << CS31); //set CS11 1(8-fold Prescaler) + TIMSK3 |= (1 << OCIE3A); + sei(); +} + +ISR(TIMER3_COMPA_vect){ + Joystick.getUSBPID(); } void loop(){ @@ -53,5 +67,4 @@ void loop(){ digitalWrite(7,LOW); analogWrite(9,abs(forces[0])); } - delay(1); } diff --git a/examples/YourFirstFFBController/DigitalWriteFast.h b/examples/YourFirstFFBController/DigitalWriteFast.h new file mode 100644 index 0000000..be6b7ec --- /dev/null +++ b/examples/YourFirstFFBController/DigitalWriteFast.h @@ -0,0 +1,394 @@ +/* + Optimized digital functions for AVR microcontrollers + by Watterott electronic (www.watterott.com) + based on http://code.google.com/p/digitalwritefast + */ + +#ifndef __digitalWriteFast_h_ +#define __digitalWriteFast_h_ 1 + +// general macros/defines +#ifndef BIT_READ +# define BIT_READ(value, bit) ((value) & (1UL << (bit))) +#endif +#ifndef BIT_SET +# define BIT_SET(value, bit) ((value) |= (1UL << (bit))) +#endif +#ifndef BIT_CLEAR +# define BIT_CLEAR(value, bit) ((value) &= ~(1UL << (bit))) +#endif +#ifndef BIT_WRITE +# define BIT_WRITE(value, bit, bitvalue) (bitvalue ? BIT_SET(value, bit) : BIT_CLEAR(value, bit)) +#endif + +#ifndef SWAP +#define SWAP(x,y) do{ (x)=(x)^(y); (y)=(x)^(y); (x)=(x)^(y); }while(0) +#endif + +#ifndef DEC +# define DEC (10) +#endif +#ifndef HEX +# define HEX (16) +#endif +#ifndef OCT +# define OCT (8) +#endif +#ifndef BIN +# define BIN (2) +#endif + + +// workarounds for ARM microcontrollers +#if (!defined(__AVR__) || \ + defined(ARDUINO_ARCH_SAM) || \ + defined(ARDUINO_ARCH_SAMD)) + +#ifndef PROGMEM +# define PROGMEM +#endif +#ifndef PGM_P +# define PGM_P const char * +#endif +#ifndef PSTR +# define PSTR(str) (str) +#endif + +#ifndef memcpy_P +# define memcpy_P(dest, src, num) memcpy((dest), (src), (num)) +#endif +#ifndef strcpy_P +# define strcpy_P(dst, src) strcpy((dst), (src)) +#endif +#ifndef strcat_P +# define strcat_P(dst, src) strcat((dst), (src)) +#endif +#ifndef strcmp_P +# define strcmp_P(a, b) strcmp((a), (b)) +#endif +#ifndef strcasecmp_P +# define strcasecmp_P(a, b) strcasecmp((a), (b)) +#endif +#ifndef strncmp_P +# define strncmp_P(a, b, n) strncmp((a), (b), (n)) +#endif +#ifndef strncasecmp_P +# define strncasecmp_P(a, b, n) strncasecmp((a), (b), (n)) +#endif +#ifndef strstr_P +# define strstr_P(a, b) strstr((a), (b)) +#endif +#ifndef strlen_P +# define strlen_P(a) strlen((a)) +#endif +#ifndef sprintf_P +# define sprintf_P(s, f, ...) sprintf((s), (f), __VA_ARGS__) +#endif + +#ifndef pgm_read_byte +# define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#endif +#ifndef pgm_read_word +# define pgm_read_word(addr) (*(const unsigned short *)(addr)) +#endif +#ifndef pgm_read_dword +# define pgm_read_dword(addr) (*(const unsigned long *)(addr)) +#endif + +#endif + + +// digital functions +//#ifndef digitalPinToPortReg +#define SPI_SW_SS_PIN (10) //SS on Uno (for software SPI) +#define SPI_SW_MOSI_PIN (11) //MOSI on Uno (for software SPI) +#define SPI_SW_MISO_PIN (12) //MISO on Uno (for software SPI) +#define SPI_SW_SCK_PIN (13) //SCK on Uno (for software SPI) + + +// --- Arduino Due and SAM3X8E based boards --- +#if (defined(ARDUINO_SAM_DUE) || \ + defined(__SAM3X8E__)) + +#define UART_RX_PIN (0) +#define UART_TX_PIN (1) + +#define I2C_SDA_PIN (20) +#define I2C_SCL_PIN (21) + +#define SPI_HW_SS_PIN (78) //SS0:77, SS1:87, SS2:86, SS3:78 +#define SPI_HW_MOSI_PIN (75) //75 +#define SPI_HW_MISO_PIN (74) //74 +#define SPI_HW_SCK_PIN (76) //76 + + +// --- Arduino Zero and SAMD21G18 based boards --- +#elif (defined(ARDUINO_SAMD_ZERO) || \ + defined(__SAMD21G18A__)) + +#define UART_RX_PIN (0) +#define UART_TX_PIN (1) + +#define I2C_SDA_PIN (16) +#define I2C_SCL_PIN (17) + +#define SPI_HW_SS_PIN (14) //14 +#define SPI_HW_MOSI_PIN (21) //21 +#define SPI_HW_MISO_PIN (18) //18 +#define SPI_HW_SCK_PIN (20) //20 + + +// --- Arduino Mega and ATmega128x/256x based boards --- +#elif (defined(ARDUINO_AVR_MEGA) || \ + defined(ARDUINO_AVR_MEGA1280) || \ + defined(ARDUINO_AVR_MEGA2560) || \ + defined(__AVR_ATmega1280__) || \ + defined(__AVR_ATmega1281__) || \ + defined(__AVR_ATmega2560__) || \ + defined(__AVR_ATmega2561__)) + +#define UART_RX_PIN (0) //PE0 +#define UART_TX_PIN (1) //PE1 + +#define I2C_SDA_PIN (20) +#define I2C_SCL_PIN (21) + +#define SPI_HW_SS_PIN (53) //PB0 +#define SPI_HW_MOSI_PIN (51) //PB2 +#define SPI_HW_MISO_PIN (50) //PB3 +#define SPI_HW_SCK_PIN (52) //PB1 + +#define __digitalPinToPortReg(P) \ +(((P) >= 22 && (P) <= 29) ? &PORTA : \ +((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PORTB : \ +(((P) >= 30 && (P) <= 37) ? &PORTC : \ +((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PORTD : \ +((((P) >= 0 && (P) <= 3) || (P) == 5) ? &PORTE : \ +(((P) >= 54 && (P) <= 61) ? &PORTF : \ +((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PORTG : \ +((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PORTH : \ +(((P) == 14 || (P) == 15) ? &PORTJ : \ +(((P) >= 62 && (P) <= 69) ? &PORTK : &PORTL)))))))))) + +#define __digitalPinToDDRReg(P) \ +(((P) >= 22 && (P) <= 29) ? &DDRA : \ +((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &DDRB : \ +(((P) >= 30 && (P) <= 37) ? &DDRC : \ +((((P) >= 18 && (P) <= 21) || (P) == 38) ? &DDRD : \ +((((P) >= 0 && (P) <= 3) || (P) == 5) ? &DDRE : \ +(((P) >= 54 && (P) <= 61) ? &DDRF : \ +((((P) >= 39 && (P) <= 41) || (P) == 4) ? &DDRG : \ +((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &DDRH : \ +(((P) == 14 || (P) == 15) ? &DDRJ : \ +(((P) >= 62 && (P) <= 69) ? &DDRK : &DDRL)))))))))) + +#define __digitalPinToPINReg(P) \ +(((P) >= 22 && (P) <= 29) ? &PINA : \ +((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PINB : \ +(((P) >= 30 && (P) <= 37) ? &PINC : \ +((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PIND : \ +((((P) >= 0 && (P) <= 3) || (P) == 5) ? &PINE : \ +(((P) >= 54 && (P) <= 61) ? &PINF : \ +((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PING : \ +((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PINH : \ +(((P) == 14 || (P) == 15) ? &PINJ : \ +(((P) >= 62 && (P) <= 69) ? &PINK : &PINL)))))))))) + +#define __digitalPinToBit(P) \ +(((P) >= 7 && (P) <= 9) ? (P) - 3 : \ +(((P) >= 10 && (P) <= 13) ? (P) - 6 : \ +(((P) >= 22 && (P) <= 29) ? (P) - 22 : \ +(((P) >= 30 && (P) <= 37) ? 37 - (P) : \ +(((P) >= 39 && (P) <= 41) ? 41 - (P) : \ +(((P) >= 42 && (P) <= 49) ? 49 - (P) : \ +(((P) >= 50 && (P) <= 53) ? 53 - (P) : \ +(((P) >= 54 && (P) <= 61) ? (P) - 54 : \ +(((P) >= 62 && (P) <= 69) ? (P) - 62 : \ +(((P) == 0 || (P) == 15 || (P) == 17 || (P) == 21) ? 0 : \ +(((P) == 1 || (P) == 14 || (P) == 16 || (P) == 20) ? 1 : \ +(((P) == 19) ? 2 : \ +(((P) == 5 || (P) == 6 || (P) == 18) ? 3 : \ +(((P) == 2) ? 4 : \ +(((P) == 3 || (P) == 4) ? 5 : 7))))))))))))))) + + +// --- Arduino 644 --- +#elif (defined(__AVR_ATmega644__) || \ + defined(__AVR_ATmega644P__)) + +#define UART_RX_PIN (8) //PD0 +#define UART_TX_PIN (9) //PD1 + +#define I2C_SDA_PIN (17) //PC1 +#define I2C_SCL_PIN (16) //PC0 + +#define SPI_HW_SS_PIN (4) //PB4 +#define SPI_HW_MOSI_PIN (5) //PB5 +#define SPI_HW_MISO_PIN (6) //PB6 +#define SPI_HW_SCK_PIN (7) //PB7 + +#define __digitalPinToPortReg(P) \ +(((P) >= 0 && (P) <= 7) ? &PORTB : (((P) >= 8 && (P) <= 15) ? &PORTD : (((P) >= 16 && (P) <= 23) ? &PORTC : &PORTA))) +#define __digitalPinToDDRReg(P) \ +(((P) >= 0 && (P) <= 7) ? &DDRB : (((P) >= 8 && (P) <= 15) ? &DDRD : (((P) >= 8 && (P) <= 15) ? &DDRC : &DDRA))) +#define __digitalPinToPINReg(P) \ +(((P) >= 0 && (P) <= 7) ? &PINB : (((P) >= 8 && (P) <= 15) ? &PIND : (((P) >= 8 && (P) <= 15) ? &PINC : &PINA))) +#define __digitalPinToBit(P) \ +(((P) >= 0 && (P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (P) - 24))) + + +// --- Arduino Leonardo and ATmega16U4/32U4 based boards --- +#elif (defined(ARDUINO_AVR_LEONARDO) || \ + defined(__AVR_ATmega16U4__) || \ + defined(__AVR_ATmega32U4__)) + +#define UART_RX_PIN (0) //PD2 +#define UART_TX_PIN (1) //PD3 + +#define I2C_SDA_PIN (2) //PD1 +#define I2C_SCL_PIN (3) //PD0 + +#define SPI_HW_SS_PIN (17) //PB0 +#define SPI_HW_MOSI_PIN (16) //PB2 +#define SPI_HW_MISO_PIN (14) //PB3 +#define SPI_HW_SCK_PIN (15) //PB1 + +#define __digitalPinToPortReg(P) \ +((((P) >= 0 && (P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &PORTD : (((P) == 5 || (P) == 13) ? &PORTC : (((P) >= 18 && (P) <= 23)) ? &PORTF : (((P) == 7) ? &PORTE : &PORTB))) +#define __digitalPinToDDRReg(P) \ +((((P) >= 0 && (P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &DDRD : (((P) == 5 || (P) == 13) ? &DDRC : (((P) >= 18 && (P) <= 23)) ? &DDRF : (((P) == 7) ? &DDRE : &DDRB))) +#define __digitalPinToPINReg(P) \ +((((P) >= 0 && (P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &PIND : (((P) == 5 || (P) == 13) ? &PINC : (((P) >= 18 && (P) <= 23)) ? &PINF : (((P) == 7) ? &PINE : &PINB))) +#define __digitalPinToBit(P) \ +(((P) >= 8 && (P) <= 11) ? (P) - 4 : (((P) >= 18 && (P) <= 21) ? 25 - (P) : (((P) == 0) ? 2 : (((P) == 1) ? 3 : (((P) == 2) ? 1 : (((P) == 3) ? 0 : (((P) == 4) ? 4 : (((P) == 6) ? 7 : (((P) == 13) ? 7 : (((P) == 14) ? 3 : (((P) == 15) ? 1 : (((P) == 16) ? 2 : (((P) == 17) ? 0 : (((P) == 22) ? 1 : (((P) == 23) ? 0 : (((P) == 24) ? 4 : (((P) == 25) ? 7 : (((P) == 26) ? 4 : (((P) == 27) ? 5 : 6 ))))))))))))))))))) + + +// --- Arduino Uno and ATmega168/328 based boards --- +#elif (defined(ARDUINO_AVR_UNO) || \ + defined(ARDUINO_AVR_DUEMILANOVE) || \ + defined(__AVR_ATmega168__) || \ + defined(__AVR_ATmega168A__) || \ + defined(__AVR_ATmega168PA__) || \ + defined(__AVR_ATmega328__) || \ + defined(__AVR_ATmega328P__) || \ + defined(__AVR_ATmega328PB__)) + +#define UART_RX_PIN (0) //PD0 +#define UART_TX_PIN (1) //PD1 + +#define I2C_SDA_PIN (18) //A4 +#define I2C_SCL_PIN (19) //A5 + +#define SPI_HW_SS_PIN (10) //PB0 +#define SPI_HW_MOSI_PIN (11) //PB2 +#define SPI_HW_MISO_PIN (12) //PB3 +#define SPI_HW_SCK_PIN (13) //PB1 + +#if defined(__AVR_ATmega328PB__) +#define __digitalPinToPortReg(P) \ +(((P) >= 0 && (P) <= 7) ? &PORTD : (((P) >= 8 && (P) <= 13) ? &PORTB : (((P) >= 14 && (P) <= 19) ? &PORTC : &PORTE))) +#define __digitalPinToDDRReg(P) \ +(((P) >= 0 && (P) <= 7) ? &DDRD : (((P) >= 8 && (P) <= 13) ? &DDRB : (((P) >= 14 && (P) <= 19) ? &DDRC : &DDRE))) +#define __digitalPinToPINReg(P) \ +(((P) >= 0 && (P) <= 7) ? &PIND : (((P) >= 8 && (P) <= 13) ? &PINB : (((P) >= 14 && (P) <= 19) ? &PINC : &PINE))) +#define __digitalPinToBit(P) \ +(((P) >= 0 && (P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P) - 8 : (((P) >= 14 && (P) <= 19) ? (P) - 14 : (((P) >= 20 && (P) <= 21) ? (P) - 18 : (P) - 22)))) +#else +#define __digitalPinToPortReg(P) \ +(((P) >= 0 && (P) <= 7) ? &PORTD : (((P) >= 8 && (P) <= 13) ? &PORTB : &PORTC)) +#define __digitalPinToDDRReg(P) \ +(((P) >= 0 && (P) <= 7) ? &DDRD : (((P) >= 8 && (P) <= 13) ? &DDRB : &DDRC)) +#define __digitalPinToPINReg(P) \ +(((P) >= 0 && (P) <= 7) ? &PIND : (((P) >= 8 && (P) <= 13) ? &PINB : &PINC)) +#define __digitalPinToBit(P) \ +(((P) >= 0 && (P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P) - 8 : (P) - 14)) +#endif + +// --- ATtinyX5 --- +#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) +// we have only PORTB +#define __digitalPinToPortReg(P) (&PORTB) +#define __digitalPinToDDRReg(P) (&DDRB) +#define __digitalPinToPINReg(P) (&PINB) +#define __digitalPinToBit(P) \ +(((P) >= 0 && (P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P) - 8 : (P) - 14)) + +// --- ATtinyX4 + ATtinyX7 --- +// ATtinyX4: PORTA for 0 to 7, PORTB for 8 to 11 +// ATtinyX7: PORTA for 0 to 7, PORTB for 8 to 15 +#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) +#define __digitalPinToPortReg(P) (((P) >= 0 && (P) <= 7) ? &PORTA : &PORTB) +#define __digitalPinToDDRReg(P) (((P) >= 0 && (P) <= 7) ? &DDRA : &DDRB) +#define __digitalPinToPINReg(P) (((P) >= 0 && (P) <= 7) ? &PINA : &PINB) +#define __digitalPinToBit(P) (((P) >= 0 && (P) <= 7) ? (P) : (P) - 8 ) + +// --- Other --- +#else + +#define I2C_SDA_PIN SDA +#define I2C_SCL_PIN SCL + +#define SPI_HW_SS_PIN SS +#define SPI_HW_MOSI_PIN MOSI +#define SPI_HW_MISO_PIN MISO +#define SPI_HW_SCK_PIN SCK + + +#endif +//#endif //#ifndef digitalPinToPortReg + + +#ifndef digitalWriteFast +#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) +#define digitalWriteFast(P, V) \ +if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \ + BIT_WRITE(*__digitalPinToPortReg(P), __digitalPinToBit(P), (V)); \ +} else { \ + digitalWrite((P), (V)); \ +} +#else +#define digitalWriteFast digitalWrite +#endif +#endif + + +#ifndef pinModeFast +#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) +#define pinModeFast(P, V) \ +if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \ + if (V == INPUT_PULLUP) {\ + BIT_WRITE(*__digitalPinToDDRReg(P), __digitalPinToBit(P), (INPUT)); \ + BIT_WRITE(*__digitalPinToPortReg(P), __digitalPinToBit(P), (HIGH)); \ + } else { \ + BIT_WRITE(*__digitalPinToDDRReg(P), __digitalPinToBit(P), (V)); \ + } \ +} else { \ + pinMode((P), (V)); \ +} +#else +#define pinModeFast pinMode +#endif +#endif + + +#ifndef digitalReadFast +#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) +#define digitalReadFast(P) ( (int) __digitalReadFast((P)) ) +#define __digitalReadFast(P ) \ + (__builtin_constant_p(P) ) ? ( \ + ( BIT_READ(*__digitalPinToPINReg(P), __digitalPinToBit(P))) ? HIGH:LOW ) : \ + digitalRead((P)) +#else +#define digitalReadFast digitalRead +#endif +#endif + + +#ifndef digitalToggleFast +#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) +#define digitalToggleFast(P) BIT_SET(*__digitalPinToPINReg(P), __digitalPinToBit(P)) +#endif +#endif + + +#endif //__digitalWriteFast_h_ \ No newline at end of file diff --git a/examples/YourFirstFFBController/YourFirstFFBController.ino b/examples/YourFirstFFBController/YourFirstFFBController.ino new file mode 100644 index 0000000..3674e8a --- /dev/null +++ b/examples/YourFirstFFBController/YourFirstFFBController.ino @@ -0,0 +1,136 @@ + +#include +#include "DigitalWriteFast.h" + +#define encoderPinA 2 +#define encoderPinB 3 + +#define motorPinA 7 +#define motorPinB 8 +#define motorPinPWM 9 + +#define ENCODER_MAX_VALUE 1200 +#define ENCODER_MIN_VALUE -1200 + +#define MAX_PWM 200 + +bool isOutOfRange = false; +int32_t forces[2]={0}; +Gains gains[2]; +EffectParams effectparams[2]; + +Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_JOYSTICK, + 8, 0, // Button Count, Hat Switch Count + true, true, false, // X and Y, but no Z Axis + true, true, false, // Rx, Ry, no Rz + false, false, // No rudder or throttle + false, false, false); // No accelerator, brake, or steering + +volatile long value = 0; +int32_t g_force = 0; + +int32_t currentPosition = 0; +volatile int8_t oldState = 0; +const int8_t KNOBDIR[] = { + 0, 1, -1, 0, + -1, 0, 0, 1, + 1, 0, 0, -1, + 0, -1, 1, 0 +}; + +void tick(void) +{ + int sig1 = digitalReadFast(encoderPinA); + int sig2 = digitalReadFast(encoderPinB); + int8_t thisState = sig1 | (sig2 << 1); + + if (oldState != thisState) { + currentPosition += KNOBDIR[thisState | (oldState<<2)]; + oldState = thisState; + } +} + +void setup() { + Serial.begin(115200); + attachInterrupt(digitalPinToInterrupt(encoderPinA),tick,CHANGE); + attachInterrupt(digitalPinToInterrupt(encoderPinB),tick,CHANGE); + Joystick.setRyAxisRange(0, 500); + Joystick.setRxAxisRange(0, 500); + Joystick.setYAxisRange(0, 500); + Joystick.setXAxisRange(ENCODER_MIN_VALUE, ENCODER_MAX_VALUE); + Joystick.setGains(gains); + Joystick.begin(true); + + pinMode(motorPinA, OUTPUT); + pinMode(motorPinB, OUTPUT); + pinMode(motorPinPWM, OUTPUT); + + pinMode(A0, INPUT_PULLUP); + + cli(); + TCCR3A = 0; //set TCCR1A 0 + TCCR3B = 0; //set TCCR1B 0 + TCNT3 = 0; //counter init + OCR3A = 399; + TCCR3B |= (1 << WGM32); //open CTC mode + TCCR3B |= (1 << CS31); //set CS11 1(8-fold Prescaler) + TIMSK3 |= (1 << OCIE3A); + sei(); + +} + +ISR(TIMER3_COMPA_vect){ + Joystick.getUSBPID(); +} + +unsigned int interval = 0; +void loop() { + value = currentPosition; + + if(value > ENCODER_MAX_VALUE) + { + isOutOfRange = true; + value = ENCODER_MAX_VALUE; + }else if(value < ENCODER_MIN_VALUE) + { + isOutOfRange = true; + value = ENCODER_MIN_VALUE; + }else{ + isOutOfRange = false; + } + + Joystick.setXAxis(value); + Joystick.setRxAxis(analogRead(A1)); + Joystick.setRyAxis(analogRead(A2)); + Joystick.setYAxis(analogRead(A3)); + + effectparams[0].springMaxPosition = ENCODER_MAX_VALUE; + effectparams[0].springPosition = value; + effectparams[1].springMaxPosition = 255; + effectparams[1].springPosition = 0; + Joystick.setEffectParams(effectparams); + Joystick.getForce(forces); + + + if(!isOutOfRange){ + if(forces[0] > 0) + { + digitalWrite(motorPinA, HIGH); + digitalWrite(motorPinB, LOW); + analogWrite(motorPinPWM, abs(forces[0])); + }else{ + digitalWrite(motorPinA, LOW); + digitalWrite(motorPinB, HIGH); + analogWrite(motorPinPWM, abs(forces[0])); + } + }else{ + if(value < 0){ + digitalWrite(motorPinA, LOW); + digitalWrite(motorPinB, HIGH); + }else{ + digitalWrite(motorPinA, HIGH); + digitalWrite(motorPinB, LOW); + } + analogWrite(motorPinPWM, MAX_PWM); + } +} diff --git a/examples/YourFirstFFBController/ffbcontroller.png b/examples/YourFirstFFBController/ffbcontroller.png new file mode 100644 index 0000000000000000000000000000000000000000..8753f85a7c7875d204db9cdb8d0ce11a2f43e3ee GIT binary patch literal 84343 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 0) { - uint8_t out_ffbdata[64]; uint16_t len = USB_Recv(PID_ENDPOINT_OUT, &out_ffbdata, 64); if (len >= 0) { pidReportHandler.UppackUsbData(out_ffbdata, len); diff --git a/src/DynamicHID/DynamicHID.h b/src/DynamicHID/DynamicHID.h index 4f373f8..34e7ba2 100644 --- a/src/DynamicHID/DynamicHID.h +++ b/src/DynamicHID/DynamicHID.h @@ -136,7 +136,7 @@ class DynamicHID_ : public PluggableUSBModule private: uint8_t epType[2]; - + uint8_t out_ffbdata[64]; DynamicHIDSubDescriptor* rootNode; uint16_t descriptorSize; diff --git a/src/FFBDescriptor.h b/src/FFBDescriptor.h index 73a0c2c..ac0e44a 100644 --- a/src/FFBDescriptor.h +++ b/src/FFBDescriptor.h @@ -387,7 +387,7 @@ static const uint8_t pidReportDescriptor[] PROGMEM= { 0x35, 0x00, // Physical Minimum (0) 0x46, 0xFF, 0x00, // Physical Maximum (255) 0x75, 0x08, // Report Size (8) - 0x95, 0x02, // Report Count (2) + 0x95, 0x01, // Report Count (2) 0x91, 0x02, // Output (Data,Var,Abs) 0xC0, //End Collection Datalink (Logical) (OK) diff --git a/src/Joystick.cpp b/src/Joystick.cpp index c8c296a..12866ea 100644 --- a/src/Joystick.cpp +++ b/src/Joystick.cpp @@ -41,6 +41,8 @@ #define JOYSTICK_INCLUDE_BRAKE B00001000 #define JOYSTICK_INCLUDE_STEERING B00010000 +unsigned int timecnt = 0; + Joystick_::Joystick_( uint8_t hidReportId, uint8_t joystickType, @@ -485,8 +487,13 @@ void Joystick_::begin(bool initAutoSendState) sendState(); } -void Joystick_::getForce(int32_t* forces) { +void Joystick_::getUSBPID() +{ DynamicHID().RecvfromUsb(); +} + +void Joystick_::getForce(int32_t* forces) +{ forceCalculator(forces); } @@ -580,8 +587,7 @@ void Joystick_::forceCalculator(int32_t* forces) { volatile TEffectState& effect = DynamicHID().pidReportHandler.g_EffectStates[id]; if ((effect.state == MEFFECTSTATE_PLAYING) && ((effect.elapsedTime <= effect.duration) || - (effect.duration == USB_DURATION_INFINITE)) && - !DynamicHID().pidReportHandler.devicePaused) + (effect.duration == USB_DURATION_INFINITE)) && !DynamicHID().pidReportHandler.devicePaused) { forces[0] += (int32_t)(getEffectForce(effect, m_gains[0], m_effect_params[0], 0)); forces[1] += (int32_t)(getEffectForce(effect, m_gains[1], m_effect_params[1], 1)); @@ -589,8 +595,8 @@ void Joystick_::forceCalculator(int32_t* forces) { } forces[0] = (int32_t)((float)1.0 * forces[0] * m_gains[0].totalGain / 10000); // each effect gain * total effect gain = 10000 forces[1] = (int32_t)((float)1.0 * forces[1] * m_gains[1].totalGain / 10000); // each effect gain * total effect gain = 10000 - forces[0] = map(forces[0], -10000, 10000, -255, 255); - forces[1] = map(forces[1], -10000, 10000, -255, 255); + forces[0] = map(forces[0], -10000, 10000, -250, 250); + forces[1] = map(forces[1], -10000, 10000, -250, 250); } int32_t Joystick_::ConstantForceCalculator(volatile TEffectState& effect) @@ -748,17 +754,17 @@ int32_t Joystick_::ConditionForceCalculator(volatile TEffectState& effect, float return (int32_t)tempForce; } -float Joystick_::NormalizeRange(int32_t x, int32_t maxValue) { +inline float Joystick_::NormalizeRange(int32_t x, int32_t maxValue) { return (float)x * 1.00 / maxValue; } -int32_t Joystick_::ApplyGain(int16_t value, uint8_t gain) +inline int32_t Joystick_::ApplyGain(int16_t value, uint8_t gain) { int32_t value_32 = value; return ((value_32 * gain) / 255); } -int32_t Joystick_::ApplyEnvelope(volatile TEffectState& effect, int32_t value) +inline int32_t Joystick_::ApplyEnvelope(volatile TEffectState& effect, int32_t value) { int32_t magnitude = ApplyGain(effect.magnitude, effect.gain); int32_t attackLevel = ApplyGain(effect.attackLevel, effect.gain); diff --git a/src/Joystick.h b/src/Joystick.h index bc09e28..ac083d4 100644 --- a/src/Joystick.h +++ b/src/Joystick.h @@ -149,6 +149,9 @@ class Joystick_ //force feedback effect params EffectParams* m_effect_params; + //lock data + bool is_calculating_force = true; + ///force calculate funtion float NormalizeRange(int32_t x, int32_t maxValue); int32_t ApplyEnvelope(volatile TEffectState& effect, int32_t value); @@ -267,7 +270,8 @@ class Joystick_ void setHatSwitch(int8_t hatSwitch, int16_t value); void sendState(); - + // get USB PID data + void getUSBPID(); //force feedback Interfaces void getForce(int32_t* forces); //set gain functions diff --git a/version.info b/version.info index 7965b36..60453e6 100644 --- a/version.info +++ b/version.info @@ -1 +1 @@ -v0.9.0 \ No newline at end of file +v1.0.0 \ No newline at end of file