ME 333 Lab 4
In this lab, we are going to use the PIC32 microcontroller to control the motor's position with a proportional feedback controller. A block diagram of the overview of this lab is shown below.
The PIC32 is going to generate a square wave reference signal (ref) that will cause the motor to alternate between two positions. This reference signal could be changed to any arbitrary signal such as a sinusoid or a triangle wave, but for this lab we are going to stick with a square wave. The period and amplitude of the square wave will be analog inputs to the PIC32.
The quadrature encoder attached to your motor creates two signals similar to those shown below. These signals will be sent to a encoder/decoder chip (LS7083) that converts the signals into up and down counts to be sent to the PIC32. The up and down counts will be read by the PIC32 and converted into a position for the motor (output). This algorithm will be explained in further detail in the encoder section.
The position error is the difference between the reference signal and the output. The error will be sent to a controller, in this case a proportional controller and converted into an input signal (u). This is shown in the equation below:
The input signal is then converted into a PWM to cause the motor to move and produce a new output.
So how do we do this?
First, we are going to write the code in which we will learn about the following programming topics:
- Timers
- Interrupts
- PWM
After programming the PIC32, you will construct the circuit for this motor controller. This circuit will include the following hardware pieces:
- H-bridge
- Motor with Encoder
- Encoder/Decoder Chip
After the circuit has been created, we will test the feedback control with different proportional gains (Kp).
Programming
Getting Started
This section details the code required for Feedback Control of Motor Position with the PIC32.
Overview: write something here
Create a new project folder and call it MotorPositionController. - Put HardwareProfile.h, HardwareProfile_NU32.h, and procdefs.ld. These are the same as in HelloWorld. The can be downloaded here - Create a new MPLAB project using the normal procedure - Make a new file in MPLAB and save as "MotorPositionController.c" without the quotes. This will be our main c file.
- Copy and paste the following lines of code that will serve as our template for the code.
/* Motor Position Control Lab 4 */ /** INCLUDES ***************************************************/ #include "HardwareProfile.h" /** Constants **************************************************/ #define TRUE 1 #define FALSE 0 /** Function Declarations **************************************/ /** Global Variables *******************************************/ /** Main Function **********************************************/ int main(void) { int pbClk; // Configure the proper PB frequency and the number of wait states pbClk = SYSTEMConfigPerformance(SYS_FREQ); // Allow vector interrupts INTEnableSystemMultiVectoredInt(); mInitAllLEDs(); while(1) { } } //end main /** Interrupt Handlers *****************************************/ /** Other Functions ********************************************/
All of the lines that have /*....*/ are just commented out lines referring to different sections of code. We will fill in these sections. The main.c function currently has SYSTEMConfigPerformance which optimizes the PB frequency and number of wait states. This function also returns the peripheral bus clock frequency, which we will need later. INTEnableSystemMultiVectoredInt() is a function that enables system wide interrupts. Interrupts will be discussed below. We are also initializing the LEDS on the NU32 board and creating an infinite while loop. In fact, this infinite while loop will remain empty for this entire lab. The rest of the code will be taken care of in interrupts (discussed below).
Motor PWM
The PIC32 Output Compare Module has 5 pins (OC1:OC5) that can be used for pulse-width modulution (PWM) output. PWM essentially creates variable voltage across the motor. The PWM period is based on a 16 bit period register of Timer 2 or Timer 3. These two timers can be combined to get a 32 bit period register. In this section, we are going to initialize PWM, Timer2 and create an interrupt based on Timer 3 to update the PWM duty cycle. Describe Interrupts...
All of our initializations could be placed in the main function, but to make our code more modular we are going to create functions to initialze different segments of our code.
In the "Function Declarations" section, put the following line of code:
void initMotorPWM();
This function will initialize everything we need for PWM.
For this lab, we are going to use 3 pins to control the motor.
- A2 - digital output for enable pin
- A3 - digital output for direction
- D0 - PWM pin
Since, we are using two digital outputs, define the following lines of code in the "Constants" section.Note that the PWM pin does not need a constant because it will be initialized as a PWM pin.
#define ENABLE_PIN LATAbits.LATA2 #define DIRECTION_PIN LATAbits.LATA3
We also want to define constants for the direction. Put these lines of code in the "Constants" section.
#define FORWARD 0 #define REVERSE 1
Copy and paste the following function into the "Other Functions" section. Note that this function is not complete because there are several 4 things that have X's in them.
void initMotorPWM(void) { //Set Enable and Direction Pins (A2, A3) as digital outputs // Initialize as low LATA |= 0xXXXX; TRISA &= 0xXXXX; // init OC1 module, on pin D0 OpenOC1( OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0); // init Timer2 mode and period (PR2) // produces 1ms period OpenTimer2( T2_ON | T2_PS_1_X | T2_SOURCE_INT, 0xXXXX); }
The first line of code needs to initialize pins A2 and A3 as digital outputs (see lab 2 for digital i/o information).
- Determine the Hex Number to set A2 and A3 as Low with LATA
- Determine the Hex Number to initialize A2 and A3 as digital outputs and leave everything else alone.
The second line of code (OpenOC1(), turns on the PWM for OC1 based on Timer2 with no fault pin.
Timer 2 is going to be the source of our PWM period. Timers basically increment a 16-bit variable TMRx where x is the Timer number. OpenTimerX() takes two inputs. The first input is the configuration constants and the second variable is known as the Period Register(PR). The configuration constants above turn on Timer 2, set a prescaler value and determine where the source of the clock is for the Timer. T2_SOURCE_INT indicates that the source of the clock will be internal, so the PB frequency is how fast the TMRx will be incremented. The combination of the prescaler value and PR determine the period of resetting TMRx back to zero. This resetting can be configured to trigger an interrupt flag. Interrupts are discussed in the next section. For PWM, this resetting sends the next pulse width, essentially creating a PWM period. The period of resetting is calculated using the following formula:
Period = [(PR + 1) Tpb (TMR_Prescaler_Value)] (convert to math) Frequency = 1 / Period where Tpb is the period of the peripheral bus (1/80Mhz for our PIC32)
To complete our initMotorPWM() function, we need to determine the prescalar value and the Period register. For PWM, the common frequencies are 5kHz - 40kHz. For this lab, we are going to use 20kHz as the frequency. Basically, a higher PR number results in higher resolution for the duty cycle. Therefore, we want to have the highest PR number we can afford, meaning that we want the lowest Prescalar Value.
- Calculate PR for a 20kHz frequency and a prescalar value of 1.
The PR number is a 16bit integer, so PR needs to be less than 2^16 - 1 (65536). If you calculate PR to be greater than this value, you will need to increase the prescalar value of 1. (Note that you don't need to for this section, but we will in the next section)
- With the prescalar value, fix the X in that constant.
- With the PR, put that number for the second input. (Its nice to convert to a 16bit hex number, so you remember that it can't be greater than 65536)
PR refers to the maximum number you can use for your duty cycle of PWM, therefore, we want to record what this number is.
- Put the following line of code in the "Constants" section
#define MAX_RESOLUTION 0xXXXX // Proportional to period of PWM
where 0xXXXX is the hex value for the calculated PR.