In addition, we suggest the following instruments:
The following programs must be installed on your development workstation:
Open-loop control involves one or more inputs, some type of conversion or means of combining the values of the inputs, and one or more outputs represented by the block diagram in Fig. 5.1. As was demonstrated in Unit 2, stepper motors are often used for open-loop control of position. A stepper motor rotates to one of a number of fixed positions, according to its internal construction. Sending a stream of electrical pulses to it causes it to rotate by exactly that many steps, hence the name. Such motors are often used, together with a simple initial datum sensor (a switch that is activated at the machine's home position), for the control of simple robotic machines such as the commonplace inkjet printer head. The drawback of open-loop control of steppers is that if the machine load is too high, or if the motor attempts to move too quickly, then steps may be skipped. The controller has no means of detecting this and so the machine continues to run slightly out of adjustment, until reset. For this reason, more complex robots and machine tools instead use servomotors rather than stepper motors, which incorporate position encoders and closed-loop controllers.1)
Figure 5.1. Open-loop motor control block diagram.
As illustrated in Fig. 5.1, open-loop control does not use feedback to the control processing that indicates how the output is actually working. The open-loop portion of this lab will use the potentiometer labeled Analog Control Input identified as R134 on the Basys MX3 schematic, as shown in Fig. 7.1. It will be used to set the percent PWM to operate the speed of the motor from 0 rpm to the maximum the motor can achieve given the motor performance and the supply voltage. Monitoring of specific signals will validate the control of the PWM output by the Analog Input Control potentiometer using the Analog Discovery 2 and a digital multimeter.
The potentiometer labeled the Analog Input Control (R134) on the Basys MX3 trainer board, as shown in Fig. 7.1, will be used as the input for controlling the motor speed. The potentiometer wiper terminal is connected to the PIC32MX370 processor Port B pin 2. Using the PIC32 analog-to-digital converter (ADC) is rather straightforward with the aid of the example code provided by the MPLAB X Help for the XC32 Peripheral Libraries. Listing B.1 in Appendix B shows how to initialize an ADC channel to continuously sample and convert analog data. Listing B.2 is example code to read the most recent ADC conversion and call functions to set the motor direction of rotation and speed. The motor control is discussed in section 7.4 below.
Figure 7.1. Analog Input Control schematic diagram.
Section 7.2.1 of the Unit 6 tutorial describes the details of measuring the period of the pulse output of a digital tachometer. Listing B.3 and B.4 of this text present the code for initializing the input capture to generate an interrupt on every positive transition and the interrupt service routine that computes the period between successive captures. Since the period measurement is instantaneous, it is very sensitive to input noise. One means of reducing the effects of noise on a measurement is to implement a low-pass filter using a moving average (MA) algorithm. This common smoothing algorithm is expressed in Eq. 7.1. The smooth output, y[i], requires the N-1 past inputs to be summed and that sum to be divided by the value of N. The filter expressed in Eq. 7.1 is a non-recursive implementation that only uses past inputs to generate the newest output average. It requires N addition operations and one division as well as N-1 memory locations.
$$y[i] = \frac{1}{N} \sum_{j}^{N-1} x[i+j] \qquad (Eq. 7.1)$$
Eq. 7.2 is a recursive implementation of the same moving average. The newest average is a function of only two inputs, the present input and the input captured N samples back, and the last computed moving average, y[n-1]. The advantage of Eq. 7.2 is that only two additions (one add and one subtract) and one division is needed regardless of the size of N, while requiring N+1 memory locations.
$$y[n] = \frac{(x[n] - x[n-N])}{N} + y[n-1] \qquad (Eq. 7.2)$$
Software timing tests reveal that 24 μs is required for an 8-point MA filter μs using Eq. 7.1, while only 8 μs is required for Eq. 7.2. The advantage of Eq. 7.2 is even greater when one realizes that the execution time of Eq. 7.2 does not depend on the value of N.
In this lab we will be using a permanent magnet brushed DC motor, as shown in Fig. 7.2, where the speed of a DC motor is proportional to the applied DC voltage. References 3 and 4 provide insight on the speed control characteristics of this type of electric motor. The characteristic of interest to us is that the speed of the motor is roughly proportional to the applied DC voltage. The challenge for Lab 6a is how best to generate a variable DC motor supply using the PIC32 processor.
Figure 7.2. DC Motor with digital tachometer.
The motor driver IC on the Basys MX3 processor platform is shown in Fig. 7.3. The DC motor with a Hall sensor tachometer that will be used for this lab is shown in Fig. 7.2. The motor wires used for power input and tachometer output are identified in Fig. 7.3. A 5.0 V external power supply is required to be connected to J11, marked as VBAR on the Basys MX3 processor board. A jumper must be used on JP1 to connect the 5V power, VBAR, to the motor supply, VM.
Figure 7.3. Basys MX3 Motor driver circuit.
Table 7.1. Motor driver IC7 connection to PIC32 processor.
Function | IC7 Pin | PIC32 Port Pin | Function | Motor Connector |
---|---|---|---|---|
AIN1 | 10 | RB3 | Motor Supply + | Red |
BIN1 | 8 | RE9 | Not Used - Set to zero | |
AIN2 | 9 | RE8 | Motor Supply - | Black |
BIN2 | 7 | RB5 | Not used – Set to zero | |
MODE | 11 | RF1 | Set to 0 | |
Pmod JA | ||||
Vdd | JA-12 | +3.3V | Brown | |
Gnd | JA-11 | Gnd | Green | |
SA | JA-10 | RG9 | Hall Sensor A | Blue |
SB | N/C | Hall Sensor B | Purple |
For the motor to turn clockwise, the A1 output must be held low and the A2 output must be PWM modulated. The opposite is true of turning the motor in the counterclockwise direction. A PWM output can be held at zero level simply by setting the output compare value to zero. Similarly, a PWM value can be held high by setting the output compare to a value equal to or greater than the timer period. Example code for configuring and implementing a bidirectional variable speed control of a DC motor is shown in Listing B.6 through B.8.
PWM output from a PIC32 requires the Output Compare and a reference timer. The OCMP example provided in the MPLAB XC32 Peripheral Library documentation provides an example of setting up a PWM output using Output Compare channel 1. For Lab 6a, we need only a 16-bit timer, hence we will use only Timer 2. If we want the PWM carrier frequency to be 1 kHz, we must set the PWM period to 1 ms.
The Output Compare channel that implements the PWM uses Timer 2 to set the period and duty cycle. To allow the motor to rotate in a given direction, PWM output for signal AIN1 will be set to the desired duty cycle while the duty cycle for AIN2 will be set to zero. To reverse the direction of rotation, AIN1 will be set to zero and AIN2 will be set to the desired duty cycle using the macro instructions shown in Listing B.6 as SetDCOC2PWM(a); where the variable “a” equals PWM_PERIOD * duty_cycle/100 and %duty cycle ranges in value from 0 to 100. Since the analog input varies between 0 and 1023 while the motor control varies between zero and 100%, the duty cycle variable is computed from the following equation.
$$% duty cycle = \bigg( \frac{AN2*100}{1023} \bigg) \quadd (Eq. 7.3)$$
Note that since we are using integer math, truncation errors will be minimized if multiplication is completed prior to the division operation, which is the C inherent precedence unless forced to do otherwise, provided that the multiplication does not exceed the maximum value for an unsigned integer. Listings B.6 through B.8 show the code needed to setup and control the two PWM channels.
Table 8.1. Motor control modes.
Slide Switch | LOW | HIGH |
---|---|---|
SW5 | Run the motor according to the direction set by SW7 | Coast operation |
SW6 | Run the motor according to the direction set by SW7 | Brake (STOP) |
SW7 | Rotate CCW at speed set by analog input control | Rotate CW at speed set by analog input control |
This design will be completed in two phases. The first phase results in the open loop motor control using the Digilent Analog Discovery 2 as the instrumentation to verify correct motor control. The second phase adds the tachometer to the design to allow the PIC32 to display the motor performance on the LCD.
Figure 8.1. H-Bridge inputs for CW motor rotation for 60% PWM.
Figure 8.2. H-Bridge inputs for CCW motor rotation for 60% PWM.
Figure 8.3. Example plot of motor speed vs. %PWM.
1. After plotting the data in Phase 2 testing part c, describe the resulting curve.
2. Explain whether Input Capture, Timer 2, or Timer 3 should be set to the higher interrupt level.
3. If Input Capture, Timer 2, and Timer 3 are set at the same priority level, explain which interrupt should be set to the higher sub interrupt level.
4. Explain the difference in interrupt operation between question 2 and question 3.
5. Consider the case when the motor draws more current than can be supplied by a single H-Bridge driver and the outputs from A1 must be in parallel with B1 and A2 in parallel with B2. What effect on motor operations does changing the primary output compare registers (OCxR) instead of the secondary output compare registers (OCxRS)?
Figure A.1. DC Motor connection to the Basys MX3 processor board.
void ADC10Init() { // define setup parameters for OpenADC10 // Turn module on | output integer | trigger mode auto | enable auto sample #define ADC_PARAM1 ADC_MODULE_ON | ADC_FORMAT_INTG | ADC_CLK_AUTO | ADC_AUTO_SAMPLING_ON // define setup parameters for OpenADC10 // ADC ref external | disable offset test | disable scan mode | //perform 2 samples | use dual buffers | use alternate mode #define ADC_PARAM2 ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | \\ ADC_SAMPLES_PER_INT_2 | ADC_ALT_BUF_ON | ADC_ALT_INPUT_ON // define setup parameters for OpenADC10 // use ADC internal clock | set sample time #define ADC_PARAM3 ADC_CONV_CLK_INTERNAL_RC | ADC_SAMPLE_TIME_15 // define setup parameters for OpenADC10 // do not assign channels to scan #define ADC_PARAM4 SKIP_SCAN_ALL // define setup parameters for OpenADC10 // set AN2 as analog inputs #define ADC_PARAM5 ENABLE_AN2_ANA Motor driver output pin assignments // wait for the first conversion to complete so there will be valid data // in ADC result registers while ( !AD1CON1bits.DONE ); // Start Timer 2 interrupts OpenTimer2((T2_ON | T2_SOURCE_INT | T2_PS_1_4), PWM_PERIOD-1); ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_1); INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR); INTEnableInterrupts(); }
int speed; // variable is required to be declared global void __ISR(_TIMER_2_VECTOR, IPL1SOFT) Timer2Handler(void) { unsigned int offset; unsigned int channel2; int // Determine buffer offset offset = 8 * ((~ReadActiveBufferADC10() & 0x01)); // Read the result of channel 2 conversion from the idle buffer channel2 = ReadADC10(offset); // Read the analog buffer speed = (channel2 * 100) / ADCMAX; // Convert to PWM in % // User supplied code to determine required motor control mode as per // Requirements and “MOTOR_CTRL” declaration in Listing 6 below. motor(motor_control_mode, speed); // Set motor rotation direction and speed mT2ClearIntFlag(); // Clear Timer 2 interrupt flag }
#define T3_TICK 0 // Maximum Timer 3 period #define TACHout BIT_6 // RF6 is for tachometer instrumentation tachInit(void) { PORTSetPinsDigitalOut(IOPORT_F, TACHout); // Instrumentation only // Enable Input Capture Module 1 // - Capture every rising edge // - Enable capture interrupts // - Use Timer 3 source // - Capture rising edge first ANSELGbits.ANSG9 = 0; // Set RG9 as digital IO TRISGbits.TRISG9 = 1; // Set RG9 as input ConfigCNGPullups(CNG9_PULLUP_ENABLE); // Enable pull-up resistor IC1R = 0b00000001; // Map RG9 to Input Capture 1 OpenCapture1( IC_EVERY_RISE_EDGE | IC_INT_1CAPTURE | IC_TIMER3_SRC |\ IC_FEDGE_RISE | IC_ON ); ConfigIntCapture1(IC_INT_ON | IC_INT_PRIOR_3 | IC_INT_SUB_PRIOR_0); // Timer 3 initialization mIC1ClearIntFlag(); ConfigIntTimer3(T3_INT_ON | T3_INT_PRIOR_2 | T3_INT_SUB_PRIOR_0); OpenTimer3( T3_ON | T3_PS_1_16, T3_TICK-1); mT3IntEnable(1); // EnableIntT3 – ISR for Timer 3 is required }
void __ISR( _INPUT_CAPTURE_1_VECTOR, IPL3SOFT) Capture1(void) { ReadCapture1(con_buf); // Read captures into buffer PORTToggleBits(IOPORT_F, TACHout); // For tachometer instrumentation // User supplied code to determine the period between two successive interrupts mIC1ClearIntFlag(); }
void __ISR(_TIMER_3_VECTOR, IPL2SOFT) Timer3Handler(void) { // User supplied code to blink LED 3 once each second for indication only mT3ClearIntFlag(); }
// Motor driver output pin assignments #define AIN1bit BIT_3 // RB3 #define AIN2bit BIT_8 // RE8 #define ENAbit AIN2bit // RE8 #define MODEbit BIT_1 // RF1 // Motor drive pin control macros #define setPHASEA1(a); {if(a) LATBSET = AIN1bit; else LATBCLR = AIN1bit;} #define setPHASEA2(a); {if(a) LATBSET = AIN2bit; else LATBCLR = AIN2bit;} #define setENA(a); {if(a) LATESET = ENAbit; else LATECLR = ENAbit;} // IO pin mapping constants #define PPS_RE8_OC2 0b00001011 // Map RE8 to OC2 for PWM #define PPS_RB3_OC4 0b00001011 // Map RB3 to OC4 for PWM // PWM period constants #define PWM_PERIOD (GetPeripheralClock()/1000)-1 // One ms PWM period #define PWM100 PWM_PERIOD // 100% PWM duty cycle // Motor control macros – the parameter “a” is the PWM percentage multiplied by the // PWM period and divided by 100. #define MOTOR_MODE(a) { if(a) {LATFSET = MODEbit; else LATBCLR = MODEbit;} #define MOTOR_COAST(); { SetDCOC2PWM(0); SetDCOC4PWM(0) } #define MOTOR_CW(a); { SetDCOC4PWM(a); SetDCOC2PWM(0) } #define MOTOR_CCW(a); { SetDCOC4PWM(0); SetDCOC2PWM(a) } #define MOTOR_STOP(); { SetDCOC2PWM(PWM100); SetDCOC4PWM(PWM100); } enum MOTOR_CTRL {COAST=0, CW, CCW, BRAKE} ;
void motor_init(void) { // Set all motor driver outputs zero setPHASEA1(0); setPHASEA2(0); // Make motor driver pins outputs PORTSetPinsDigitalOut(IOPORT_B, AIN1bit ); //RB3 PORTSetPinsDigitalOut(IOPORT_E, AIN2bit ); //RE8 // Map Port pins to output compare channels RPB3R = PPS_RB3_OC4; // Map RB3 to OC4 for PWM RPE8R = PPS_RE8_OC2; // Map RE8 to OC2 for PWM // Set motor driver IC for parallel outputs setMOTOR_MODE(0); // Initialize two PWM channels OpenTimer2((T2_ON | T2_SOURCE_INT | T2_PS_1_1), PWM_PERIOD); ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_1); OpenOC2((OC_ON|OC_TIMER_MODE16|OC_TIMER2_SRC|OC_PWM_FAULT_PIN_DISABLE), 0, 0); OpenOC4((OC_ON|OC_TIMER_MODE16|OC_TIMER2_SRC|OC_PWM_FAULT_PIN_DISABLE), 0, 0); // Enable all interrupts INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR); INTEnableInterrupts(); }
void motor(enum MOTOR_CTRL mc, unsigned int speed) { unsigned int pwm; pwm = ((speed * PWM_PD)/100) - 1; // Compute PWM setting values for Lab 6a only /* Insert code that implements the specifications for Lab 6b here */ switch (mc) // Determine direction of rotation { case COAST: MOTOR_COAST(); // Set all outputs off break; case CW: MOTOR_CW(pwm); // Set CW speed break; case CCW: MOTOR_CCW(pwm); // Set CCW speed break; case BRAKE: MOTOR_BRAKE(); // Set all outputs on to short motor inputs break; } }