Update Stepper.md

This commit is contained in:
HomeSpan 2023-06-24 15:48:02 -05:00 committed by GitHub
parent fec2b3d9f7
commit d9de368a8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 23 additions and 160 deletions

View File

@ -8,13 +8,13 @@ The HomeSpan class that contains all the methods to control a stepper motor is c
* This class is used to operate stepper motors driven by a [Toshiba TB6612](www.adafruit.com/product/2448) chip, either with or without the use of ESP32 PWM pins * This class is used to operate stepper motors driven by a [Toshiba TB6612](www.adafruit.com/product/2448) chip, either with or without the use of ESP32 PWM pins
* To use, add the following to the top of your sketch: `#include "extras/Stepper_TB6612.h"` * To use, add the following to the top of your sketch: `#include "extras/Stepper_TB6612.h"`
* Contructors: * Contructors:
* *Stepper_TB6612(int AIN1, int AIN2, int BIN1, int BIN2)* - controls the driver board using only 4 digital pins * `Stepper_TB6612(int AIN1, int AIN2, int BIN1, int BIN2)` - controls the driver board using only 4 digital pins
* *Stepper_TB6612(int AIN1, int AIN2, int BIN1, int BIN2, int PWMA, int PWMB)* - adds PWM control of the motor, allowing for additional stepping modes * `Stepper_TB6612(int AIN1, int AIN2, int BIN1, int BIN2, int PWMA, int PWMB)` - adds PWM control of the motor, allowing for additional stepping modes
* **Stepper_A3967** * **Stepper_A3967**
* This class is used to operate stepper motors driven by an [Allegro A3967](https://www.sparkfun.com/products/12779) chip * This class is used to operate stepper motors driven by an [Allegro A3967](https://www.sparkfun.com/products/12779) chip
* To use, add the following to the top of your sketch: `#include "extras/Stepper_A3967.h"` * To use, add the following to the top of your sketch: `#include "extras/Stepper_A3967.h"`
* Contructor: *Stepper_A3967(int M1, int M2, int STEP, int DIR, int ENABLE)* * Contructor: `Stepper_A3967(int M1, int M2, int STEP, int DIR, int ENABLE)`
Click on either of the driver-specific classes above for complete details on how to wire and configure a particular driver board. Click on either of the driver-specific classes above for complete details on how to wire and configure a particular driver board.
@ -23,170 +23,33 @@ Click on either of the driver-specific classes above for complete details on how
The **StepperControl** class provides the following methods to operate and control a stepper motor object instantiated with one of the driver-specific classes described above: The **StepperControl** class provides the following methods to operate and control a stepper motor object instantiated with one of the driver-specific classes described above:
* `void enable()` * `void enable()`
* enables current flow to the stepper motor coils, actively holding the motor in its position
* enables current flow to the stepper motor coils, actively locking the position of the motor
* `void disable()` * `void disable()`
* disables current flow to the stepper motor coils and leaves in a state of high impedence, allowing the motor to turn freely
* disables current flow to the stepper motor coils and leaves in a state of high impedence, allowing the motor to turn freely
* `void brake()` * `void brake()`
* disables current flow to the stepper motor coils and leaves in a state of low impedence, creating "friction" that prevents the motor from freely turning
* disables current flow to the stepper motor coils and leaves in a state of low impedence, creating "friction" that prevents the motor from freely turning * applicable only for driver chips that support a "short brake" mode, otherwise has no effect
* applicable only for driver chips that support a "short brake" mode, otherwise has no effect
* `void move(int nSteps, uint32_t msDelay, endAction_t endAction=NONE)` * `void move(int nSteps, uint32_t msDelay, endAction_t endAction=NONE)`
* enables the stepper motor and moves it *nSteps* steps. Note this is a **non-blocking** function and returns immediately after being called while the stepper turns for *nSteps* steps in the background
* moves the stepper motor an absolute number of steps
* *nSteps* - the number of steps to move. A positive number moves the motor in one direction; a negative number moved the motor in the opposite direction; a value of zero causes the motor to *stop* if it is already moving
* *nSteps* - the number of steps to move. A positive number moves the motor in one direction; a negative number moved the motor in the opposite direction; a value of zero causes the motor to stop if it is already moving
* *msDelay* - the delay, in milliseconds, to pause between steps. Must be greater than zero. The lower the number, the faster the motor moves, subject to limitations of the motor itself * *msDelay* - the delay, in milliseconds, to pause between steps. Must be greater than zero. The lower the number, the faster the motor moves, subject to limitations of the motor itself
* *msDelay* -an optional action to be performed once the motor has finished moving *nStep* steps. Choices include in milliseconds, to pause between steps. Must be greater than zero. The lower the number, the faster the motor moves, subject to limitations of the motor itself * *endAction* - an optional action to be performed *after* the motor has finished moving *nSteps* steps. Choices include:
* **StepperControl::NONE** - no action is taken; the stepper motor is left in the enabled state (this is the default)
* disables current flow to the stepper motor coils and leaves in a state of high impedence, allowing the motor to turn freely * **StepperControl::DISABLE** - current to the stepper motor is disabled
* **StepperControl::BRAKE** - the stepper motor is placed in a brake state
* if this method is called while the stepper motor is moving, the number of steps to turn will be reset to the new *nSteps* value. It is okay to change the sign of *nSteps* to reverse the motor while it is moving, though this may not be desireable depending on what your motor is connected to in the real world
* calling this method with a value of *nSteps=0* causes the motor to stop, if it is moving. If the motor is not moving, calling this method with *nSteps=0* simply enables the motor and the immediately performs the *endAction* (if specified).
* example: `myMotor.move(200,5,StepperControl::BRAKE);` starts the motor turning for 200 steps with a delay of 5ms between steps. When the motor has completed all 200 steps, it is placed in the brake state where inductive "friction" holds it in place
Creating an instance of this **class** initializes the RF/IR signal generator and specifies the ESP32 *pin* to output the signal. You may create more than one instance of this class if driving more than one RF/IR transmitter (each connected to different *pin*), subject to the following limitations: ESP32 - 8 instances; ESP32-S2 and ESP32-S3 - 4 instances; ESP32-C3 - 2 instances. The optional parameter *refClock* is more fully described further below under the `start()` method.
Signals are defined as a sequence of HIGH and LOW phases that together form a pulse train where you specify the duration, in *ticks*, of each HIGH and LOW phase, shown respectively as H1-H4 and L1-L4 in the following diagram:
![Pulse Train](images/pulseTrain.png)
Since most RF/IR signals repeat the same train of pulses more than once, the duration of the last LOW phase should be extended to account for the delay between repeats of the pulse train. Pulse trains are encoded as sequential arrays of 32-bit words, where each 32-bit word represents an individual pulse using the following protocol:
* bits 0-14: the duration, in *ticks* from 0-32767, of the first part of the pulse to be transmitted
* bit 15: indicates whether the first part of the pulse to be trasnmitted is HIGH (1) or LOW (0)
* bits 16-30: the duration, in *ticks* from 0-32767, of the second part of the pulse to be transmitted
* bit 31: indicates whether the second part of the pulse to be trasnmitted is HIGH (1) or LOW (0)
HomeSpan provides two easy methods to create, store, and transmit a pulse train. The first method relies on the fact that each instance of RFControl maintains its own internal memory structure to store a pulse train of arbitrary length. The functions `clear()`, `add()`, and `pulse()`, described below, allow you to create a pulse train using this internal memory structure. The `start()` function is then used to begin transmission of the full pulse train. This method is generally used when pulse trains are to be created on-the-fly as needed, since each RFControl instance can only store a single pulse train at one time.
In the second method, you create one or more pulse trains in external arrays of 32-bit words using the protocol above. To begin transmission of a specific pulse train, call the `start()` function with a pointer reference to the external array containing that pulse train. This method is generally used when you want to pre-compute many different pulse trains and have them ready-to-transmit as needed. Note that this method requires the array to be stored in RAM, not PSRAM.
Details of each function are as follows:
* `void clear()`
* clears the pulse train memory structure of a specific instance of RFControl
* `void phase(uint32_t numTicks, uint8_t phase)`
* appends either a HIGH or LOW phase to the pulse train memory buffer for a specific instance of RFControl
* *numTicks* - the duration, in *ticks* of the pulse phase. Durations of greater than 32767 ticks allowed (the system automatically creates repeated pulses of a maximum of 32767 ticks each until the specified duration of *numTicks* is reached)
* *phase* - set to 0 to create a LOW phase; set to 1 (or any non-zero number) to create a HIGH phase * `int stepsRemaining()`
* returns the number of steps remaining to turn
* repeated phases of the same type (e.g. HIGH followed by another HIGH) are permitted and result in a single HIGH or LOW phase with a duration equal to the sum of the *numTicks* specified for each repeated phase (this is helpful when generating Manchester-encoded signals) * may be positive or negative depending on the direction the motor is turning
* returns zero when the motor is not turning
* `void add(uint32_t onTime, uint32_t offTime)` * example: `myMotor.move(200,5,StepperControl::BRAKE); while(myMotor.stepsRemaining()!=0);` starts the motor turning and then waits until is completes all 200 steps
* appends a single HIGH/LOW pulse with duration *onTime* followed by *offTime* to the pulse train of a specific instance of RFControl. This is functionally equivalent to calling `phase(onTime,HIGH);` followed by `phase(offTime,LOW);` as defined above
* `void enableCarrier(uint32_t freq, float duty=0.5)`
* enables modulation of the pulse train with a "square" carrier wave. In practice this is only used for IR signals (not RF)
* *freq* - the frequency, in Hz, of the carrier wave. If freq=0, carrier wave is disabled
* *duty* - the duty cycle of the carrier wave, from 0-1. Default is 0.5 if not specified
* RFControl will report an error if the combination of the specified frequency and duty cycle is outside the range of supported configurations
* `void disableCarrier()`
* disables the carrier wave. Equivalent to `enableCarrier(0);`
* `void start(uint8_t _numCycles, uint8_t tickTime)`
* `void start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTime)`
* in the first variation, this starts the transmission of the pulse train stored in the internal memory structure of a given instance of RFControl that was created using the `clear()`, `add()`, and `phase()` functions above. In the second variation, this starts the transmission of the pulse train stored in an external array *data* containing *nData* 32-bit words. The signal will be output on the pin specified when RFControl was instantiated. Note this is a blocking call—the method waits until transmission is completed before returning. This should not produce a noticeable delay in program operations since most RF/IR pulse trains are only a few tens-of-milliseconds long
* *numCycles* - the total number of times to transmit the pulse train (i.e. a value of 3 means the pulse train will be transmitted once, followed by 2 additional re-transmissions). This is an optional argument with a default of 1 if not specified.
* *tickTime* - the duration, in ***clock units***, of a *tick*. This is an optional argument with a default of 1 *clock unit* if not specified. Valid range is 1-255 *clock units*, or set to 0 for 256 *clock units*. The duration of a *clock unit* is determined by the *refClock* parameter (the second, optional argument, in the RFControl constructor described above). If *refClock* is set to true (the default), RFControl uses the ESP32's 1 MHz Reference Clock for timing so that each *clock unit* equals 1𝛍s. If *refClock* is set to false, RFControl uses the ESP32's faster 80 MHz APB Clock so that each *clock unit* equals 0.0125𝛍s (1/80 of microsecond)
* To aid in the creation of a pulse train stored in an external array of 32-bit words, RFControl includes the macro *RF_PULSE(highTicks,lowTicks)* that returns a properly-formatted 32-bit value representing a single HIGH/LOW pulse of duration *highTicks* followed by *lowTicks*. This is basically an analog to the `add()` function. For example, the following code snippet shows two ways of creating and transmitting the same 3-pulse pulse-train --- the only difference being that one uses the internal memory structure of RFControl, and the second uses an external array:
```C++
RFControl rf(11); // create an instance of RFControl
rf.clear(); // clear the internal memory structure
rf.add(100,50); // create pulse of 100 ticks HIGH followed by 50 ticks LOW
rf.add(100,50); // create a second pulse of 100 ticks HIGH followed by 50 ticks LOW
rf.add(25,500); // create a third pulse of 25 ticks HIGH followed by 500 ticks LOW
rf.start(4,1000); // start transmission of the pulse train; repeat for 4 cycles; one tick = 1000𝛍s
uint32_t pulseTrain[] = {RF_PULSE(100,50), RF_PULSE(100,50), RF_PULSE(25,500)}; // create the same pulse train in an external array
rf.start(pulseTrain,3,4,1000); // start transmission using the same parameters
```
#### Diagnostic Messages
The **RFControl** class outputs *Warning \[W\]* messages to the Serial Monitor based on the *Core Debug Level* selected when compiling the sketch using the Arduino IDE. A non-fatal warning message is produced when insufficient Channel resources prevent the creation of a new RFControl object. Calls to the `start()` method for objects that failed to be properly created are silently ignored.
## Example RFControl Sketch
Below is a complete sketch that produces two different pulse trains with the signal output linked to the ESP32 device's built-in LED (rather than an RF or IR transmitter). For illustrative purposes the tick duration has been set to a very long 100𝛍s, and pulse times range from of 1000-10,000 ticks, so that the individual pulses are easily discernable on the LED. Note this example sketch is also available in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → RemoteControl*](../examples/Other%20Examples/RemoteControl).
```C++
/* HomeSpan Remote Control Example */
#include "HomeSpan.h" // include the HomeSpan library
#include "extras/RFControl.h" // include RF Control Library
void setup() {
Serial.begin(115200); // start the Serial interface
Serial.flush();
delay(1000); // wait for interface to flush
Serial.print("\n\nHomeSpan RF Transmitter Example\n\n");
RFControl rf(13); // create an instance of RFControl with signal output to pin 13 on the ESP32
rf.clear(); // clear the pulse train memory buffer
rf.add(5000,5000); // create a pulse train with three 5000-tick high/low pulses
rf.add(5000,5000);
rf.add(5000,10000); // double duration of final low period
Serial.print("Starting 4 cycles of three 500 ms on pulses...");
rf.start(4,100); // start transmission of 4 cycles of the pulse train with 1 tick=100 microseconds
Serial.print("Done!\n");
delay(2000);
rf.clear();
for(int i=1000;i<10000;i+=1000)
rf.add(i,10000-i);
rf.add(10000,10000);
Serial.print("Starting 3 cycles of 100-1000 ms pulses...");
rf.start(3,100); // start transmission of 3 cycles of the pulse train with 1 tick=100 microseconds
Serial.print("Done!\n");
Serial.print("\nEnd Example");
} // end of setup()
void loop(){
} // end of loop()
```
--- ---