ME 333 Lab 4

From Mech
Jump to navigationJump to search

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.

We now need to use the initMotorPWM function in the main function.

  • Put the following line of code after mInitAllLEDs(); in your main function
initMotorPWM();

At this point, you have now initialized the PWM and a couple digital outputs for controlling the motor. We need to update the duty cycle, which we are going to do in an interrupt service routine in the next section.

Interrupt Controller

In this section, we are going to initialize and create an interrupt to control the main chunk of our code. Interrupt flags can be generated by many different things such as key strokes on the keyboard for RS232, Timer overflows, external pins, etc. When a interrupt flag is generated, the program jumps to an interrupt service routine (ISR) and carries out the lines of code in the ISR before returning to the original code. Essentially, it interrupts (stops) the main code and jumps somewhere else performs an action and then returns to the interrupted line of code. Our code will be sitting in the infinite while loop until an interrupt is generated. When the interrupt is generated, it will carry out several actions such as checking the encoder and updating the duty cycle before returning back to the infinite while loop.

Timer based interrupts are set up similar to the PWM discussed above. We are going to create a new function to initialize this interrupt.

  • Put the following line of code in the "Function Declarations" section:
void initInterruptController();
  • Put the following lines of code in the "Other Functions" section:
void initInterruptController(void)
{
	// init Timer3 mode and period (PR3) // produces 1ms period
	OpenTimer3( T3_ON | T3_PS_1_X | T3_SOURCE_INT, 0xXXXX);
	
	mT3SetIntPriority( 7); 	// set Timer3 Interrupt Priority
	mT3ClearIntFlag(); 		// clear interrupt flag
	mT3IntEnable( 1);		// enable timer3 interrupts
}


The first line of code is the same as that for PWM except for with timer 3.

  • Choose a prescalar value and calculate the PR value. (Remember that PR < 65536). Available prescalar constants for the X are 1, 2, 4, 8, 16, 32, 64, and 256 as shown in timer.h.

Encoder

RS232

Circuit

Feedback Control