Playing with Microcontroller (Explorer 16 Development Board) and Servo Motor

1.    Overview

Since mobile robot needs onboard control besides the console, micro controller has been used as an onboard controller. In this mobile robot there are many types of controlling such as: motion control, camera control. Therefore a micro controller will be overloaded if it controls all the robot’s parts. In order to reduce the load, usually a micro controller is used as a master (main) controller to other controllers such as motion controller. This article covers tutorials that have been done to know more about microcontroller’s usage.

2.    Microcontroller Tutorials

The following tutorials are about how a micro controller can control and interact with hardware, actuator and external (outside) software. The micro controller that has been used is called PIC32MX360F512L and the development board that has been used called Explorer 16 Development Board (for more details see Appendix A: Hardware List). C programming language has been used to program the microcontroller.

2.1.        LEDs and Buttons

The development board has LEDs and buttons (see Figure 1), in this tutorial buttons and LEDs should be linked via the micro controller i.e. if a button is pressed then one of the LEDs will be turned on and if another button is pressed the LED will turned off.

Figure 1: Explorer 16 Development Board (Buttons and LEDs)

In order to do this, a micro controller should know that a button has been pressed then the micro controller sends a high signal to the LED which makes it turned on.

2.2.        LCD and Buttons

In this tutorial, LCD library has been used. This library contains functions for writing on the LCD. LCD also is a part of Explorer 16 Development Board.

Figure 2: LCD

Buttons has been linked to the LCD through the micro controller, so that pressing a button is like pressing a key on a keyboard. Since there are only four buttons on the board, a combination between buttons has been made so that if a button is pressed it prints a character on the LCD and if another button is pressed it prints another character and if the two buttons are pressed at the same time a third character will be printed on the LCD and so on.

2.3.        Simple Servo Motor

In this tutorial a simple servo motor has been linked to a microcontroller. This motor is controlled by the microcontroller through a delay function i.e. the position of the motor is specified by the delay time.

Figure 3: Simple Servo Motor

2.4.        Two simple Servo motors

The aim of this tutorial is how to control multi motors in parallel using the same microcontroller. The microcontroller has been programmed to send two different control commands one for each motor at the same time.

2.5.        Serial Port (UART)

In this tutorial, Serial Port (UART) has been used as a bridge for communications between the microcontroller and a PC. The two simple motors tutorial has been modified to make the micro controller receive the controlling values from a PC through Serial Port (UART). In addition the LCD tutorial has been modified to make the input characters inserted by a PC’s keyboard and then sent to the microcontroller through Serial Port (UART).

2.6.        Controlling Two Simple Motors via Speech

The main idea in this additional work is to have a Micro controller program and C# program communicating using Serial Port (UART). C# program (running on PC) is responsible for tasks that can’t done on microcontroller (or it is complicated to make it on the microcontroller level) such as speech recognition.

Figure 4: Controlling Two Simple Motors via Speech

The C# program is responsible for recognizing the speech commands, and then converts it to controlling commands after that the program sends the controlling commands through the Serial Port. The microcontroller receives the controlling commands and sends it to the motors.

 

3.    Hardware and Software Lists

3.1.        Hardware List

3.1.1.     PIC 32 microcontroller

“The PIC32 is a 32-bit family of general purpose microcontrollers from Microchip Technology. It offers 80+ DMIPS performance with a wide variety of on-chip peripherals. It employs industry leading M4K MIPS32 core from MIPS Technologies, Inc. All members in the PIC32 family use programming interface similar to other Microchip PIC® microcontrollers. In addition, PIC32 microcontrollers are pin-to-pin compatible with the PIC24FJ128GA family of 16-bit microcontrollers. The PIC32 family includes scalable devices ranging from 32KB to 512KB of Flash memory. Also a rich set of peripherals – Five timers, 16 channels of 10-bit A/D Converters and communication interfaces: SPI, I2C™ and UART.” [http://www.microchip.com].

3.1.2.     Explorer 16 development Board

The Explorer 16 is a low cost, efficient development board to evaluate the features and performance of Microchip’s PIC32 Microcontroller.

3.1.3.     MPLAP ICD 3

ICD 3 is for porting from MPLAP to the PIC32 microcontroller. It is also an In Circuit Debugger (ICD).

3.1.4.     Simple Servo Motor ( Hitec servo)

Pulse Data

All Hitec servos require 3-5V peak to peak square wave pulse. Pulse duration is from 0.9mS to 2.1mS with 1.5mS as center. The pulse refreshes at 50Hz (20mS).

Voltage Range

All Hitec Servos can be operated within a 4.8V-6V. range. Only the HS-50 operates exclusively with 4 NiCad cells (4.8 volt).

Wire Color Meanings

On all Hitec servos the Black wire is ‘ground’, the Red wire ( center ) is ‘power’ and the third wire is ‘signal’.

Direction of Rotation

All Hitec servos turn Clockwise direction (CW).

3.2.        Software List

3.2.1.     MPLAB (IDE)

“MPLAB Integrated Development Environment (IDE) is a free, integrated toolset for the development of embedded applications employing Microchip’s PIC® and dsPIC® microcontrollers. MPLAB IDE runs as a 32-bit application on MS Windows®.” [http://www.microchip.com].

3.2.2.     MPLAB C Compiler for PIC32

“The MPLAB C Compiler for PIC32 is a full-featured ANSI compliant C compiler for Microchip’s PIC32 family of 32-bit microcontrollers. A free evaluation is available by downloading the Evaluation Edition. It has no code size limit and provides complete functionality for 60 days. The compiler is completely usable after 60 days other than certain optimization levels are disabled. The compiler is fully compatible with Microchip’s MPLAB IDE and popular third party IDEs.” [http://www.microchip.com].

3.2.3.     Visual Studio 2008

It is an IDE from Microsoft for developing specially for developing a .NET application in one of the .NET languages such as C# Programming Language. It has been used to develop the PC program (see 2.6.).

3.2.4.     SAPI (Speech Application Programming Interface)

SAPI, an interface designed by Microsoft, supports dynamic speech input and output, and is integrated in Microsoft’s current operating system. With the application programming interface (API), it is possible to develop speech enabled applications without caring about the details of synthesis and recognition.

In general, all versions of APIs have been designed such that a software developer can write an application to perform speech recognition and synthesis by using a standard set of interfaces, accessible from a variety of programming languages.

4.    Source Codes

This section contains a selected codes that covers most (or all) of the areas mentioned in the tutorials.

4.1.        LCD and Buttons Code

#include <p32xxxx.h>
#include <plib.h>
#include "explore.h"
#include "LCD.h"

void Delayms( unsigned t)
{
    T1CON = 0x8000;     // enable TMR1, Tpb, 1:1
    while (t--)
    {  // t x 1ms loop
        TMR1 = 0;
        while (TMR1 < FPB/1000);
//    int i;
//        for(i=0; i<20000; i++);
    }
} // Delayms

int main(void)
{
    initEX16();
    initLCD();
    while(1)
  {
    // 0 : Pressed   1 : Released


   	if(PORTDbits.RD6 == 0 && PORTDbits.RD7 == 0 && PORTDbits.RD13 ==0 && PORTAbits.RA7 == 0 )
    {
      	writeLCD(1,'b');
      
   	}
   	else
   	if(PORTDbits.RD6 == 0 && PORTDbits.RD7 == 0 && PORTDbits.RD13 ==0)
    {
      	writeLCD(1,'c');
      
   	}
   	else
   	if(PORTDbits.RD7 == 0 && PORTDbits.RD13 ==0 && PORTAbits.RA7 == 0 )
    {
      	writeLCD(1,' ');
      
   	}
   	else
   	if(PORTDbits.RD6 == 0 && PORTDbits.RD7 == 0 && PORTAbits.RA7 == 0 )
    {
      	writeLCD(1,'s');
      
   	}
   	else
   	if(PORTDbits.RD6 == 0 && PORTDbits.RD7 == 0 )
    {
      	writeLCD(1,'d');
      
   	}
   	else
    if(PORTDbits.RD6 == 0)
    {
      	writeLCD(1,'m');
      
   	}
   	else
   	if(PORTDbits.RD7 == 0)
    {
      	writeLCD(1,'a');
   	}
   	else
   	if(PORTAbits.RA7 == 0)
    {
      	writeLCD(1,'j');
      
   	}
   	else
   		if(PORTDbits.RD13 == 0)
    {
      	writeLCD(1,'e');
      
   	}
   	
   
   	
   	
   	
   	
   	Delayms(10);
  }    
  
  
  
   /*  
    writeLCD(1,'A');
    writeLCD(1,'J');
    writeLCD(1,'E');
    writeLCD(1,'D');
    */\  
    
 
    putsLCD("\n");
    putsLCD(" Q_Q");
    
    
    for(;;)
    {
        
        
    }    
    
    return 0;
    
}    
void initEX16( void)
{
    // 1. disable the JTAG port to make the LED bar 
    // available if not using the Starter Kit
/*
    _FICD(JTAGEN_OFF);
    #ifndef PIC32_STARTER_KIT
        mJTAGPortEnable( 0);
    #endif
*/  
      mJTAGPortEnable( 0);
    // 2. Sysytem config performance
    SYSTEMConfigPerformance( FCY);
    mOSCSetPBDIV(  OSC_PB_DIV_2 );      // use div_2 as default
   
    // 7. allow vectored interrupts
    INTEnableSystemMultiVectoredInt();   // Interrupt vectoring

    // 8. PORTA output LEDs0..6, make RA7 an input button
    LATA = 0;
    TRISA = 0xFF80;
    
} // initEX16

//
void _general_exception_handler( unsigned c, unsigned s)
{
    while (1);
} // exception handler
//

/*
** Simple Delay functions
**
** uses:    Timer1
** Notes:   Blocking function
*/




/*
**
**	Buttons read and debounce
*/

int readKEY( void)
{   // returns 0..F if keys pressed, 0 = none
    int c = 0;

    if ( !_RD6) // leftmost button 
        c |= 8;
    if ( !_RD7)
        c |= 4;
    if ( !_RA7)
        c |= 2;
    if ( !_RD13) // rightmost button
        c |= 1;

    return c;
} // readKEY


int getKEY( void)
{   // wait for a key pressed and debounce
    int i=0, r=0, j=0;
    int c;

    // 1. wait for a key pressed for at least .1sec
    do{
        Delayms( 10);
        if ( (c = readKEY()))
        {
            if ( c>r)       // if more than one button pressed
                r = c;      // take the new code
            i++;    
        }
        else 
            i=0;
    } while ( i<10);

    // 2. wait for key released for at least .1 sec
    i =0;
    do {
        Delayms( 10);   
        if ( (c = readKEY()))
        {
            if (c>r)        // if more then one button pressed 
                r = c;      // take the new code
            i=0;            
            j++;            // keep counting 
        }
        else 
            i++;
    } while ( i<10);        
    
    // 3. check if a button was pushed longer than 500ms
    if ( j>50)              
        r+=0x80;            // add a flag in bit 7 of the code

    // 4. return code
    return r;
} // getKEY

 

4.2.        Controlling Two Simple Motors via Speech (2.6. source codes)

4.2.1.     C# Source Code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Speech;

namespace MotorsTest
{
    public partial class Form1 : Form
    {
        private System.Speech.Recognition.SpeechRecognitionEngine _sr = new System.Speech.Recognition.SpeechRecognitionEngine();
        private System.Speech.Synthesis.SpeechSynthesizer _sp = new System.Speech.Synthesis.SpeechSynthesizer();

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            System.Speech.Recognition.Choices ch = new System.Speech.Recognition.Choices("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "clear the value", "move");
            _sr.LoadGrammar(new System.Speech.Recognition.Grammar(new System.Speech.Recognition.GrammarBuilder(ch)));
            _sr.SpeechRecognized += new EventHandler<System.Speech.Recognition.SpeechRecognizedEventArgs>(_sr_SpeechRecognized);
            _sr.SetInputToDefaultAudioDevice();
            _sr.RecognizeAsync(System.Speech.Recognition.RecognizeMode.Multiple);
            serialPort1.PortName = "COM3";

        }

        void _sr_SpeechRecognized(object sender, System.Speech.Recognition.SpeechRecognizedEventArgs e)
        {
            if (e.Result.Text == "clear the value")
            {
                textBox1.Text = string.Empty;
                _sp.Speak("the value has been cleaned");
            }
            else
                if (e.Result.Text == "move")
                {
                    serialPort1.Open();
                    serialPort1.Write(textBox1.Text);
                    serialPort1.Close();
                }
                else
                {
                    textBox1.Text += e.Result.Text;
                }

        }
    }
}

 

4.2.2.     C  (microcontroller) Source Code

#include "Explore.h"



char v[5];
char v2[5];
int ind = 0;
int value = 1000;
int value1 = 1000;
int value2 = 1000;
int bothDif = 0;
int bothSame = 0;
int firstMotor = 0;
int secondMotor = 0;


void runMotor(int t)
{
  /*
  int i = 50;
  int j;
  while(i--)
  {
    motor_delay(t);
    j = 100;
    while(j--);
  }
  */

  motor_delay(t);
}	


void TxData(unsigned char data)
{
    putcUART2((unsigned char)data);
}   

void initEX16( void)
{
    int	pbClk;
    
      mJTAGPortEnable( 0);
    // 2. Sysytem config performance
    SYSTEMConfigPerformance( FCY);
    mOSCSetPBDIV(  OSC_PB_DIV_2 );      // use div_2 as default
   
   pbClk=SYSTEMConfig(SYS_FREQ, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);
    
   OpenUART2(UART_EN, 		// Module is ON
    		  UART_RX_ENABLE | UART_TX_ENABLE,		// Enable TX & RX
    		  pbClk/16/DESIRED_BAUDRATE-1);	// 9600 bps, 8-N-1
    
    ConfigIntUART2(UART_INT_PR2 | UART_RX_INT_EN);
    
    
    // 7. allow vectored interrupts
    INTEnableSystemMultiVectoredInt();   // Interrupt vectoring
    
    mPORTDClearBits(BIT_1); 		// Turn off RD1 on startup.
    mPORTDSetPinsDigitalOut(BIT_1);	// Make RD1 as output.
    
    
    mPORTDClearBits(BIT_0); 		// Turn off RD0 on startup.
    mPORTDSetPinsDigitalOut(BIT_0);	// Make RD0 as output.


    // 8. PORTA output LEDs0..6, make RA7 an input button
//    LATA = 0;
//    TRISA = 0xFF80;
    
} // initEX16


void motor_delay(unsigned t)
{
    PORTDbits.RD1 = 1;
    Delayus(t);
    PORTDbits.RD1 = 0;
    Delayus(PULSE_WIDTH - t);    
}


void motor2_delay(unsigned t)
{
    PORTDbits.RD0 = 1;
    Delayus(t);
    PORTDbits.RD0 = 0;
    Delayus(PULSE_WIDTH - t);    
}    


int main(void)
{
    initEX16();
    
    int i=0;

    unsigned char uart2_rx = 0;

 
 
 
    
    while (1)
    {   
       /*
      if(PORTDbits.RD6 == 0)
    {
      		motor_delay(900);	
   	}
   	else
   	if(PORTDbits.RD7 == 0)
    {
      	motor_delay(1300);
   	}
   	else
   	if(PORTAbits.RA7 == 0)
    {
      	motor_delay(1700);
      
   	}
   	else
   		if(PORTDbits.RD13 == 0)
    {
      	motor_delay(2400);
     
   	}
   	
   	*/
         
   if(bothSame)
   {     
   	motor_delay(value);
   	motor2_delay(value);
   }
   else
   if(firstMotor)
   {
     motor_delay(value1);
   }
   else
   if(secondMotor)
   {
     motor2_delay(value2);
   }
   else
   if(bothDif)
   {
     motor_delay(value1);
     motor2_delay(value2);
   }   
               
       
    }    
    return 0;
}


void Delayus( unsigned t)
{
    T1CON = 0x8000;     // enable TMR1, Tpb, 1:1
    
    
    
    while (t--)
    { 
       TMR1 = 0;
      // while (TMR1 < FPB/1140000);
      while (TMR1 < 31);
    }   
} 
void Delayms( unsigned t)
{
    T1CON = 0x8000;     // enable TMR1, Tpb, 1:1
    while (t--)
    {
        TMR1 = 0;
        while (TMR1 < FPB/1000);
    }    
}

void __ISR(_UART2_VECTOR, ipl2) _uart2(void) 
{


    unsigned char uart2_rx = 0;

    uart2_rx = UARTGetDataByte(UART2);	
    
    
  
    if(uart2_rx == 'a')
    {
      firstMotor = 1;
    v[ind] = '\0';
    value1 = atoi(v);
    
  //	runMotor(value);
    ind = 0;
  char temp[5];
  sprintf(temp,"%d",value1);	
    
    int k;
    for(k=0;k<strlen(temp);k++)
      TxData(temp[k]);
  
  
    
  //	int k;
  //	for(k=0;k<strlen(v);k++)
  //		TxData(v[k]);

  }
  else
   if(uart2_rx == 'b')
    {
      secondMotor = 1;
    v[ind] = '\0';
    value2 = atoi(v);
    
  //	runMotor(value);
    ind = 0;
  char temp[5];
  sprintf(temp,"%d",value2);	
    
    int k;
    for(k=0;k<strlen(temp);k++)
      TxData(temp[k]);
  
  
    
  //	int k;
  //	for(k=0;k<strlen(v);k++)
  //		TxData(v[k]);

  }
  else
  if(uart2_rx == 'd')
    {
    v[ind] = '\0';
    value1 = atoi(v);
    
  //	runMotor(value);
    ind = 0;
  char temp[5];
  sprintf(temp,"%d",value1);	
    
    int k;
    for(k=0;k<strlen(temp);k++)
      TxData(temp[k]);
  
  
    
  //	int k;
  //	for(k=0;k<strlen(v);k++)
  //		TxData(v[k]);

  }
  else
    if(uart2_rx == 'e')
    {
    v[ind] = '\0';
    value2 = atoi(v);
    
  //	runMotor(value);
    ind = 0;
  char temp[5];
  sprintf(temp,"%d",value2);	
    
    int k;
    for(k=0;k<strlen(temp);k++)
      TxData(temp[k]);
  
  
    
  //	int k;
  //	for(k=0;k<strlen(v);k++)
  //		TxData(v[k]);

  }
  else
    if(uart2_rx == 'f')
    {
      bothDif = 1;	
    }
  else
    if(uart2_rx == 'c')
    {
      bothSame = 1;
      v[ind] = '\0';
      value = atoi(v);
    

      ind = 0;
      char temp[5];
      sprintf(temp,"%d",value);	
    
      int k;
      for(k=0;k<strlen(temp);k++)
        TxData(temp[k]);
  
    }		
  else
  {
    v[ind] = uart2_rx;
    ind++;
    bothSame = 0;
    bothDif = 0;
    firstMotor = 0;
    secondMotor = 0;
  }	
 
    
    
    
     
    TxData(uart2_rx);
     
  INTClearFlag(INT_SOURCE_UART_RX(UART2));		// Clear the RX interrupt Flag 
}