DCM Lab 1: Open-loop speed control

DCM Lab 1: Open-loop speed control

Level: Beginner

Study points: A/D and PWM basics, C programming

While an industrial motion control application may prefer high performance brushless servomotors, one advantage of experimenting with brushed DC motor (DCM) in a lab is its simple dynamics and ease of setup, especially for the purpose of studying the fundamentals such as system modeling or PID control. Before doing anything else, a student should know how to send a speed command to a DCM in open-loop; that is, when there is no feedback to regulate the speed. This first lab in our DCM series, as diagrammed in Figure 1, requires only a Yaskawa MINERTIA series DC motor, microcontroller board (Microchip’s dsPIC30F2010), power supply, H-bridge DCM driver, a few electronics and wiring. The actual lab setup is shown in Figure 2.

Figure 1: A simplified open-loop DCM diagram

Figure 2: Experimental setup

Almost every unit in Figure 2 can be bought from hobby electronic shop, except the control board that is constructed on a proto PCB (Figure 3). If you are too lazy to do this, use some demo board available on the market. Just make sure that the A/D and PWM pins are not connected to other peripherals on the board you select. Only RB0(AN0), RE0 (DIR), OC1 (PWM)pins of dsPIC30F2010 are used.

Figure 3: Control board

Desired operation

The speed command is sent by using a small screwdriver to turn the blue trim pot, which adjusts Vcmd, the voltage at the wiper. Turning it fully clockwise (Vcmd = 4.096 V) drives the motor in clockwise direction at full speed. Reducing Vcmd by turning counter-clockwise makes the motor rotate slower, until around the middle (Vcmd = 2.048 V), the motor should stop. Continue turning counter-clockwise, the motor reverses to counter-clockwise rotation. At the end of counter-clockwise turn (Vcmd = 0), the motor rotates at full speed in the counter-clockwise direction.

As shown in Figure 3, 3 LED’s are installed, 2 as indication of rotational direction, and 1 for the speed (PWM strength).

How things work

From the diagram in Figure 1, we see that the speed command is simply a voltage from the wiper of potentiometer connected between 4.096 V and GND. We choose this voltage range because it is a convenient value for a 12-bit D/A output such as Microchip’s MCP4821, in case one wants to connect this velocity controller to another position controller with such output range. To process this command, the MCU must first convert the voltage on pin AN0 to digital value via its A/D module. Without a proper noise attenuation circuit, a common approach to get a reliable A/D value is to average the latest read with previous ones. This effectively acts like a low-pass filter to reject high frequency noise or spike that interferes with the real speed command.

Another key learning point of this lab is how to set up and use PWM module available in most MCUs. PWM stands for “Pulse-Width Modulation.” In essence, a continuous signal to drive a motor is converted to a binary signal, switching between low and high logic, with its duty cycle corresponding to the voltage level of the continuous signal. An easy way to generate PWM signal is shown in Figure 4, where the continuous (green) signal is compared with a sawtooth (red) signal. The resulting PWM has logic 1 when green > red, and 0 otherwise. In practice, the sawtooth signal is nothing but an output of a counter. The sawtooth (and hence the PWM) frequency is adjusted by the clock frequency it receives. The output-compare f unction of the dsPIC is conveniently used for generating PWM. All we have to do is to properly initialize the module and load the correct value to the right register. The detail can be studied from the source code.

Figure 4: PWM generation

Anything else is trivial. The flowchart for this open-loop speed control is shown in Figure 5. Note that the PWM driver (HDM10A module from AnyControl co.ltd. Cannot find their website) accepts 2 command inputs: PWM and DIR (direction) signals. The DIR bit controls the direction of rotation, 0 = clockwise and 1 = counter-clockwise. So the only task left is to convert the averaged voltage command to a corresponding pair of PWM and DIR values, to make the controller work as indicated in the desired operation section. The values such as ADMAX, ADMID are acquired from actual readout. For 10-bit A/D, the max voltage 5 V converts to value around 1024. Reading from A/D register in MPLAB watch window, we get for our command voltage range ADMAX (4.096 V) = 948, ADMID (2.048 V)= 474, ADMIN (0 V) = 0. For simplify programming, we then set PWMMAX = 474, PWMMID = 237, PWMMIN = 0 and initialize the PWM module accordingly. With these values, the flowchart in Figure 5 is straightforward to comprehend. The complete program is shown in Listing 1.

Figure 5: Flowchart for DCM open-loop speed control

Results and Discussion

Build theMPLABX project and load the program to the board. I used my mobile phone to make a short clip of the operation. It works as desired. I also demonstrated the inefficiency of this open loop speed command in the clip. There is no actual speed regulation via feedback. While it is turning slowly, one could try grabbing the shaft of the DCM to stop the rotation. We can do so easily.

Later on in this DCM lab series, we must try to implement a closed-loop speed control that reads actual speed from the encoder and compensates for load variation to maintain the commanded speed. In such case, we will see that when the shaft is grabbed, the DCM must generate more torque to fight with the force from our hand, hence it should be more difficult to stop the rotation.

MPLAB X project: oldcmX.zip

// MAIN.C Dr.Varodom Toochinda Mar 2012
// DC Motor Control. Accept 0-4.096 V DC input and command
// brushed DC servo motor with PWM.

#include < p30f2010.h > // Header file for dsPIC30F2010
#include < ports.h > // Module function for Interrupt configuration port
#include < adc10.h >

// Configuration bits

_FOSC(CSW_FSCM_OFF & XT_PLL4); //XT with 4xPLL oscillator, Failsafe clock off
_FWDT(WDT_OFF); //Watchdog timer disabled
_FBORPOR(PBOR_OFF & MCLR_EN); //Brown-out reset disabled, MCLR reset enabled
_FGS(CODE_PROT_OFF); //Code protect disabled


#define FCY 10000000 // xtal = 10Mhz; PLLx4

#define AINPUT 0xfffe // use only AD0
#define DIR _RE0 // direction output

// A/D range
#define ADMAX 948 // 4.096 V
#define ADMID 474 // 2.048 V
#define ADMIN 0

// PWM range
#define PWMMAX 474
#define PWMMID 237
#define PWMMIN 0

// PWM direction
#define CW 0
#define CCW 1

//#define PWM _RD0
// --------- Global variables ----------------------
unsigned int ADval; // new AD value
unsigned int ADval1, ADval2, ADval3; // old AD values
unsigned int ADval_avg; // Average value
unsigned int PWMval; // PWM value

// ------------------- function prototypes -------------------

void init(void);
void initADC(int);
void initPWM(void);
int readADC(int);
// -------------- function definition --------------------
void initADC(int amask)
ADval = 474; ADval1 = 474; ADval2 = 474; ADval3 = 474;
ADval_avg = 474;
_TRISB0 = 1; // set as input pin
ADPCFG = amask; // setlect analog input pins
ADCON1 = 0x00E0; // (internal counter) automatic conversion start after sampling
ADCSSL = 0; // no scanning required
ADCON2 = 0; // use MUXA, AVss and AVdd are used as Vref +/-
ADCON3 = 0x1F02; // Tsamp = 32 x Tad; Tad = 125 ns
ADCON1bits.ADON = 1; // turn ADC ON

int readADC(int ch)
ADCHS = ch; // 1. select analog input channel
ADCON1bits.SAMP = 1; // 2. start sampling
while (!ADCON1bits.DONE); //3. wait for the conversion to complete
return ADCBUF0;

void initPWM(void)
// init TMR3 to provide the timebase
T3CON = 0x8030; // enable TMR3, prescale 1:64, internal clock
PR3 = PWMMAX; // set the period for the given bitrate
_T3IF = 0; // clear interrupt flag
_T3IE = 1; // enable TMR2 interrupt
// init PWM
// set the initial duty cycles (master and slave)
OC1R = OC1RS = PWMMID; //init at 50% duty cycle
// activate the PWM module
OC1CON = 0x000E;

void _ISRFAST _T3Interrupt(void)
_T3IF = 0; // just clear interrupt flag and exit

void init(void)
_TRISE0 = 0; // DIR output

int main()

init(); // initialize all modules

while(1) {
ADval = readADC(0);
ADval_avg = (ADval+ADval1+ADval2+ADval3) >> 2; //averaging
if (ADval_avg > ADMID) { // Vcmd > 2.048 V
PWMval = ADval_avg - ADMID;
else { // Vcmd < 2.048 V DIR = CCW; PWMval = ADMID - ADval_avg; } if (PWMval > PWMMAX) PWMval = PWMMAX;
OC1RS = PWMval;

ADval3 = ADval2;
ADval2 = ADval1;
ADval1 = ADval;

return 0;

Listing 1: C code for DCM open-loop speed control



Comments are closed.