Tuesday, July 18, 2017

Two DC motors control with NEC IR remote control


After controlling 2 DC motors speed and direction of rotation with 2 potentiometers, now let's make the same project but with IR remote control. First project is at the link below:
Two motors control using PIC16F887 and L293D
The microcontroller used in this project is PIC16F887 and the remote control is Car MP3 IR remote control which uses NEC protocol. Decoding of this remote control with PIC16F887 is done in the following project:
NEC Protocol decoder with PIC16F887 microcontroller
In this project 6 buttons are used for controlling the speed and rotation direction of the 2 motors, these buttons are shown in the following image:
Car MP3 NEC remote control button codes
The code of each button are as shown in the following table (these codes will be used later in the C code):

Button Number
Function
Code
1
Motor 1 Start/Toggle direction
0x40BF00FF
2
Motor 1 speed down
0x40BF807F
3
Motor 1 speed up
0x40BF40BF
4
Motor 2 Start/Toggle direction
0x40BF20DF
5
Motor 2 speed down
0x40BFA05F
6
Motor 2 speed up
0x40BF609F

Hardware Required:
  • PIC16F887 microcontroller
  • 2 x DC motor (I used 12V motors)
  • L293D motor driver
  • NEC IR remote control (I'm using Car MP3 as the one above)
  • IR receiver
  • 10K ohm resistor
  • 47µF capacitor
  • 5V and 12V voltage sources
  • Breadboard
  • Jumper wires
Two DC motors control with NEC IR remote control and PIC16F887 circuit:
Remote controlled 2 DC motors using PIC16F887 and L293D circuit
As shown in the circuit diagram the IR receiver output is connected to RB0 pin which is external interrupt pin of the PIC16F887 microcontroller.
The L293D IC is used to drive both motors in the two directions, the speed of the two motors is controlled the two PWM signals which come from the microcontroller. PWM1 controls motor 1 speed and PWM2 controls motor 2 speed. Motor 1 direction is controlled with IN1 and IN2 pins of the L293D, these pins are connected to RD0 and RD1 of the PIC16F887. Motor 2 is controlled with pin IN3 and IN4 of the L293D, IN3 is connected to RD2 and IN4 is connected to RD3 of the microcontroller. When IN1 = IN2 = 0, motor 1 stops, when IN1 = 1 and IN2 = 0 motor 1 moves in the first direction, when IN1 = 0 and IN2 = 1 motor 1 moves in the second direction. The same thing for motor 2 with pins IN3 and IN4.
The 10K ohm resistor is used to minimize the IR receiver output noise.
In the circuit there are two voltage sources, one with 5V which supplies most of the circuit and the other one with 12V which supplies only the L293D IC. The 12V source depends on the motors nominal voltage.
In this project the PIC16F887 uses its internal oscillator and MCLR pin function is disabled.

Two DC motors control with NEC IR remote control CCS C code:
Project C code is as shown below. It has been tested with CCS PIC C compiler version 5.051.
PIC16F887 hardware external interrupt and Timer1 are used to decode the IR remote control. Timer1 is used to measure pulses and spaces widths and its interrupt (Timer1 interrupt) is used to reset the decoding process in case of very long pulse or space (time out). Timer1 is configured to increment every 1µs using the following command line:
setup_timer_1( T1_INTERNAL | T1_DIV_BY_2 );
But Timer1 module will not start until the microcontroller receives an interrupt on pin RB0 (interrupt edge from high to low).
The motor speed changes whenever the duty cycle of the PWM signal changes, and thus if the microcontroller receives speed up button code it will increment the duty cycle, then motor speed will be increased, and if the microcontroller receives speed down button code, the duty cycle will be decreased which causes the motor to decrease its speed.
At start up , both motors are stopped because all PORTD pins are zeroes with the command output_d(0); and when start/toggle direction button is pressed, the motor will start (if there is sufficient duty cycle otherwise the duty cycle have to be increased), and the same button pressed again the motor will change its direction of rotation.
The full C code is shown below.
// 2 Motors control with NEC IR remote control CCS C code
// Used MCU: PIC16F887
// Internal oscillator used @ 8MHz
// Used remote control: Car MP3 IR remote control
// PWM1 and PWM2 modules are used to control motor 1 and motor 2 speeds respectively
// http://ccspicc.blogspot.com/
// electronnote@gmail.com

#include <16F887.h>
#fuses NOMCLR NOBROWNOUT NOLVP INTRC_IO
#use delay(clock = 8MHz)

short nec_ok = 0, repeated = 0, m1_dir = 0, m2_dir = 0;
unsigned int8 nec_state = 0, i, duty1 = 0, duty2 = 0;
unsigned int32 nec_code;
#INT_EXT                                         // External interrupt
void ext_isr(void){
  unsigned int16 time;
  if(nec_state != 0){
    time = get_timer1();                         // Store Timer1 value
    set_timer1(0);                               // Reset Timer1
  }
  switch(nec_state){
    case 0 :                                     // Start receiving IR data (we're at the beginning of 9ms pulse)
      setup_timer_1( T1_INTERNAL | T1_DIV_BY_2 );   // Enable Timer1 module with internal clock source and prescaler = 2
      set_timer1(0);                             // Reset Timer1 value
      nec_state = 1;                             // Next state: end of 9ms pulse (start of 4.5ms space)
      i = 0;
      ext_int_edge( L_TO_H );                    // Toggle external interrupt edge
      break;
    case 1 :                                     // End of 9ms pulse
      if((time > 9500) || (time < 8500)){        // Invalid interval ==> stop decoding and reset
        nec_state = 0;                           // Reset decoding process
        setup_timer_1(T1_DISABLED);              // Stop Timer1 module
      }
      else
        nec_state = 2;                           // Next state: end of 4.5ms space (start of 562µs pulse)
      ext_int_edge( H_TO_L );                    // Toggle external interrupt edge
      break;
    case 2 :                                     // End of 4.5ms space
      if((time > 5000) || (time < 1500)){        // Invalid interval ==> stop decoding and reset
        nec_state = 0;                           // Reset decoding process
        setup_timer_1(T1_DISABLED);              // Stop Timer1 module
        break;
      }
      nec_state = 3;                             // Next state: end of 562µs pulse (start of 562µs or 1687µs space)
      if(time < 3000)                            // Check if previous code is repeated
        repeated = 1;
      ext_int_edge( L_TO_H );                    // Toggle external interrupt edge
      break;
    case 3 :                                     // End of 562µs pulse
      if((time > 700) || (time < 400)){          // Invalid interval ==> stop decoding and reset
        nec_state = 0;                           // Reset decoding process
        setup_timer_1(T1_DISABLED);              // Disable Timer1 module
      }
      else{
        // Check if the repeated code is for buttons 2, 3, 5 or 6
        if(repeated && (nec_code == 0x40BF807F || nec_code == 0x40BF40BF ||
                        nec_code == 0x40BFA05F || nec_code == 0x40BF609F)){
          repeated = 0;
          nec_ok = 1;                            // Decoding process is finished with success
          disable_interrupts(INT_EXT);           // Disable the external interrupt
          break;
        }
        nec_state = 4;                           // Next state: end of 562µs or 1687µs space
        ext_int_edge( H_TO_L );                  // Toggle external interrupt edge
        break;
      }
    case 4 :                                     // End of 562µs or 1687µs space
      if((time > 1800) || (time < 400)){         // Invalid interval ==> stop decoding and reset
        nec_state = 0;                           // Reset decoding process
        setup_timer_1(T1_DISABLED);              // Disable Timer1 module
        break;
      }
      if( time > 1000)                           // If space width > 1ms (short space)
        bit_set(nec_code, (31 - i));             // Write 1 to bit (31 - i)
      else                                       // If space width < 1ms (long space)
        bit_clear(nec_code, (31 - i));           // Write 0 to bit (31 - i)
      i++;
      if(i > 31){                                // If all bits are received
        nec_ok = 1;                              // Decoding process is finished with success
        disable_interrupts(INT_EXT);             // Disable the external interrupt
      }
      nec_state = 3;                             // Next state: end of 562µs pulse (start of 562µs or 1687µs space)
      ext_int_edge( L_TO_H );                    // Toggle external interrupt edge
  }
}
#INT_TIMER1                                      // Timer1 interrupt (used for time out)
void timer1_isr(void){
  nec_state = 0;                                 // Reset decoding process
  ext_int_edge( H_TO_L );                        // External interrupt edge from high to low
  setup_timer_1(T1_DISABLED);                    // Disable Timer1 module
  clear_interrupt(INT_TIMER1);                   // Clear Timer1 interrupt flag bit
}
void main(){
  setup_oscillator(OSC_8MHZ);                    // Set internal oscillator to 8MHz
  output_d(0);
  enable_interrupts(GLOBAL);                     // Enable global interrupts
  enable_interrupts(INT_EXT_H2L);                // Enable external interrupt
  clear_interrupt(INT_TIMER1);                   // Clear Timer1 interrupt flag bit
  enable_interrupts(INT_TIMER1);                 // Enable Timer1 interrupt
  setup_timer_2(T2_DIV_BY_16, 255, 1);           // Set PWM frequency to 488Hz
  setup_ccp1(CCP_PWM);                           // Configure CCP1 module as PWM
  setup_ccp2(CCP_PWM);                           // Configure CCP2 module as PWM
  set_pwm1_duty(0);                              // Set PWM1 duty sycle
  set_pwm2_duty(0);                              // Set PWM2 duty sycle
  while(TRUE){
    if(nec_ok){                                  // If the MCU receives a message from the remote control
      nec_ok = 0;                                // Reset decoding process
      nec_state = 0;
      setup_timer_1(T1_DISABLED);                // Disable Timer1 module
      // Motor 1
      if(nec_code == 0x40BF00FF && m1_dir){      // If button 1 is pressed (toggle rotation direction of motor 1)
        m1_dir = 0;
        nec_code = 0;
        output_high(PIN_D0);
        output_low(PIN_D1);
      }
      if(nec_code == 0x40BF00FF && !m1_dir){      // If button 1 is pressed (toggle rotation direction of motor 1)
        m1_dir = 1;
        output_low(PIN_D0);
        output_high(PIN_D1);
      }
      if(nec_code == 0x40BF40BF && duty1 < 255){ // If button 3 is pressed (increase motor 1 speed)
        duty1++;
        set_pwm1_duty(duty1);
      }
      if(nec_code == 0x40BF807F && duty1 > 0){   // If button 2 is pressed (decrease motor 1 speed)
        duty1--;
        set_pwm1_duty(duty1);
      }
      // Motor 2
      if(nec_code == 0x40BF20DF && m2_dir){      // If button 4 is pressed (toggle rotation direction of motor 2)
        m2_dir = 0;
        nec_code = 0;
        output_high(PIN_D2);
        output_low(PIN_D3);
      }
      if(nec_code == 0x40BF20DF && !m2_dir){     // If button 4 is pressed (toggle rotation direction of motor 2)
        m2_dir = 1;
        output_low(PIN_D2);
        output_high(PIN_D3);
      }
      if(nec_code == 0x40BF609F && duty2 < 255){ // If button 6 is pressed (increase motor é speed)
        duty2++;
        set_pwm2_duty(duty2);
      }
      if(nec_code == 0x40BFA05F && duty2 > 0){   // If button 5 is pressed (decrease motor 2 speed)
        duty2--;
        set_pwm2_duty(duty2);
      }
      enable_interrupts(INT_EXT_H2L);            // Enable external interrupt
    }
  }
}
The following video shows a hardware circuit of our project:


Sunday, July 16, 2017

Two motors control using PIC16F887 and L293D


The L293D quadruple half-H drivers chip allows us to drive 2 motors in both directions, and with the two PWM modules on the PIC16F887 microcontroller we can easily control the rotation speed of the two motors. (PWM: Pulse Width Modulation).
This small example shows how to implement a control circuit which controls speed and direction of rotation using PIC16F887 microcontroller and L293D IC.
The microcontroller PIC16F887 has one ECCP (Enhanced Capture/Compare/PWM) module and one CCP module. The two modules can be configured as PWM modules to generate two independent PWM signals (always with the same frequency). The speed of each motor can be controlled with the variation of the duty cycle of the PWM signal. The output pins of PWM1 and PWM2 are RC2 and RC1 respectively.
Required Components:
  • PIC16F887 microcontroller
  • 2 x DC motor (I used motors of 12V)
  • L293D motor driver
  • 2 x 10K ohm potentiometer
  • 5V Power source
  • 12V Power source (In case of 12V DC motors)
  • Breadboard
  • Jumper wires
Two motors control using PIC16F887 and L293D circuit:
Example circuit diagram is shown below.
Two DC motors control using PIC16F887 and L293D circuit diagram
In the circuit there are two potentiometers POT1 and POT2 which are used to control the speed as well as the direction of rotation of motor 1 and motor 2 respectively. POT1 is connected to analog channel 0 (AN0) and POT2 is connected to analog channel 1 (AN1).
PWM1 pin (RC2) is connected to EN1,2 pin (#1) and PWM2 pin (RC1) is connected to EN2,3 pin (#9) of the L293D. The other L293D pins which are IN1, IN2, IN3 and IN4 are connected to RD0, RD1, RD2 and RD3 respectively.
Motor 1 rotation speed is controlled by PWM1 and its direction of rotation is controlled by pins IN1 and IN2. If IN1 and IN2 are zeroes the motor stops, if IN1 = 1 and IN2 = 0 the motor rotates in the one direction, if IN1 = 0 and IN2 = 1 the motor rotates in the other direction.
The same thing for motor 2 with pins PWM2, IN3 and IN4.
In the circuit there are two power supply sources, 5V and 12V. The 5V supplies most of the circuit including the microcontroller whereas the 12V supplies one pin of the L293D (VCC2). The 12V power supply source depends on the motor nominal voltage, for example if the motor voltage is 5V, VCC2 pin should be connected to +5V source.
In this example PIC16F887 uses its internal oscillator and MCLR pin function is disabled.
Two motors control using PIC16F887 and L293D CCS C code:
In this example we've two potentiometers POT1 and POT2 connected to AN0 and AN1. Each potentiometer controls speed and rotation direction of one motor. In the code there are three intervals after reading and saving the analog value. The first interval is [ 0, 500 [ which controls the motor speed in the first direction where the maximum speed is when the analog value = 0. The second interval is [ 500, 523 ], here the motor stops. The last interval is ] 523, 1023] where the motor speed is controlled in the other direction and the maximum speed when the analog value = 1023. 10-Bit ADC resolution is used.
Timer2 module is configured to generate PWM signals of 1KHz whith a resolution of 8.96 bits :
setup_timer_2(T2_DIV_BY_16, 124, 1);
Where: T2_DIV_BY_16 is Timer2 prescaler
              124 is Timer2 preload value
              1 is Timer2 postoscaler (not used in calculations)
The PWM frequency can be calculated using the following equation:
PWM Period = [(PR2) + 1] * 4 * TOSC * (TMR2 Prescale Value)
Where the PWM frequency = 1/ PWM period
PR2: Timer2 preload value
TOSC = 1/MCU frequency (in this example MCU frequency = 8MHz)
The resolution of the PWM signal can be calculated using the following equation:
               log[4(PR2 + 1)]
Resolution = ---------------------   bits
                  log(2)
// Control of 2 motors using PIC16F887 microcontroller CCS C code
// Internal oscillator used @ 8MHz
// http://ccspicc.blogspot.com/
// electronnote@gmail.com

#include <16F887.h>
#device ADC = 10
#fuses NOMCLR, NOBROWNOUT, NOLVP, INTRC_IO
#use delay(clock = 8MHz)

signed int16 i, j;
void main(){
  setup_oscillator(OSC_8MHZ);                    // Set internal oscillator to 8MHz
  setup_adc(ADC_CLOCK_INTERNAL);                 // ADC module uses its internal oscillator
  setup_adc_ports(sAN0 | sAN1);                  // Configure AN0 & AN1 as analog input pins
  setup_timer_2(T2_DIV_BY_16, 124, 1);           // Set PWM frequency to 1KHz with a resolution of 8.96-bit
  setup_ccp1(CCP_PWM);                           // Configure CCP1 module as PWM
  setup_ccp2(CCP_PWM);                           // Configure CCP2 module as PWM
  set_pwm1_duty(0);                              // Set PWM1 duty cycle
  set_pwm2_duty(0);                              // Set PWM2 duty cycle
  while(TRUE){
    set_adc_channel(0);                          // Select channel AN0
    delay_ms(100);
    i = read_adc();                              // Read analog value from channel '0' and store it in 'i'
    set_pwm1_duty(abs(i - 511));                 // Set PWM1 duty cycle (abs => absolute value
    set_adc_channel(1);                          // Select channel AN1
    delay_ms(100);
    j = read_adc();                              // Read analog value from channel '1' and store it in 'j'
    set_pwm2_duty(abs(j - 511));                 // Set PWM2 duty cycle (abs => absolute value
    if(i > 523){
      output_high(PIN_D0);
      output_low(PIN_D1);
    }
    else{
      if(i < 500){
        output_low(PIN_D0);
        output_high(PIN_D1);
      }
      else{
        output_low(PIN_D0);
        output_low(PIN_D1);
      }
    }
    if(j > 523){
      output_high(PIN_D2);
      output_low(PIN_D3);
    }
    else{ 
      if(j < 500){
        output_low(PIN_D2);
        output_high(PIN_D3);
      }
      else{
        output_low(PIN_D2);
        output_low(PIN_D3);
      }
    }
  }
}
Video:

Saturday, October 22, 2016

Bipolar stepper motor control using PIC12F1822 and L293D


In this blog there are several posts talking about bipolar stepper motor and how to drive it. The bipolar stepper motor has two windings and 4 wires and to drive this windings 2 H-bridge circuits are needed. L293D motor driver chip is a good choice for driving this type of motor because it's low cost and easy to use.
This post shows how to drive a cd-rom bipolar stepper motor using PIC12F1822 microcontroller and L293D.
To understand how this motor works read the following post:
Bipolar stepper motor control with PIC16F877A microcontroller
Bipolar stepper motor control using PIC12F1822 and L293D circuit:
CD-ROM Bipolar stepper motor control using PIC12F1822 and L293D
The two push buttons for moving the motor in direction 1 or direction 2.
PIC12F1822 internal oscillator is used and internal pull-ups are enabled for the 2 inputs.
The stepper motor voltage is 5V which is the same as the L293D chip VS and VSS.
Bipolar stepper motor control using PIC12F1822 and L293D CCS C code:
In this project the speed of the stepper motor is fixed by a variable called speed_delay = 10. If that number changed the speed of the motor will also change, if you increase that number the motor speed will decrease and if you decrease it the speed will increase.
// Bipolar stepper Motor control using PIC12F1822 and L293D CCS PIC C code
// http://ccspicc.blogspot.com/
// electronnote@gmail.com
// Use at your own risk

#include <12F1822.h>
#fuses NOMCLR INTRC_IO PLL_SW
#use delay(clock=32000000)
#use fast_io(A)

unsigned int8 step_number = 0, speed_delay = 10;
void stepper(int8 step){
  switch(step){
    case 0:
      output_a(0b010010);
    break;
    case 1:
      output_a(0b010001);
    break;
    case 2:
      output_a(0b100001);
    break;
    case 3:
      output_a(0b100010);
    break;
  }
}
void main() {
  setup_oscillator(OSC_8MHZ | OSC_PLL_ON);            // Set internal oscillator to 32MHz (8MHz and PLL)
  output_a(0);
  set_tris_a(0x0C);                                   // Configure RA2 & RA3 as inputs 
  port_a_pullups(0x0C);                               // Enable internal pull-ups for pins RA2 & RA3
  while(TRUE){
    output_a(0);
    while(!input(PIN_A2)){                            // If RA2 button pressed
      step_number++;
      if(step_number > 3) 
        step_number = 0;
      stepper(step_number);
      delay_ms(speed_delay);
    }
    while(!input(PIN_A3)){                            // If RA3 button pressed
      if(step_number < 1) 
        step_number = 4;
      step_number--;
      stepper(step_number);
      delay_ms(speed_delay);
    }
  }
}
Bipolar stepper motor control using PIC12F1822 and L293D video:
Project hardware video....

Friday, October 21, 2016

DC Motor control using PIC12F1822 and L293D


With the help of L239D we can easily control DC motor speed and direction of rotation.
The L293D can control two motors with the same nominal voltage independently and in this project we are going to use half of the chip to control just one motor.
The microcontroller used in this project is PIC12F1822, it's an 8-bit and 8 pins microcontroller. This microcontroller has 1 CCP module which can be used to generate a PWM signal.
The following topic shows how to use PIC12F1822 ADC and PWM modules:
PIC12F1822 ADC and PWM modules
DC Motor control using PIC12F1822 and L293D circuit:
DC Motor control circuit using PIC12F1822 and L293D
Motor speed is controlled from a 10k potentiometer connected to RA0 pin. Two push buttons connected to RA1 and RA3 are used to select direction of rotation and another push button connected to RA4 is used to stop the motor.
The DC motor voltage is 12V which is the same as L293D VS voltage (pin 8).
The PWM signal output pin is selected by the software (RA2 or RA5).
PIC12F1822 internal oscillator is used and internal pull-ups is enabled for the inputs.
Here the microcontroller reads the analog data comes from the potentiometer and uses the corresponding digital value to set the PWM duty cycle.
DC Motor control using PIC12F1822 and L293D CCS C code:
In this project PIC12F1822 internal oscillator is used @ 8MHz with PLL enabled (gives 32MHz) and internal pull-ups are enebled for the inputs RA1, RA3 and RA4.
Timer2 is configured to generate a PWM signal of 1.95KHz with a resolution of 10-bit.
setup_timer_2(T2_DIV_BY_16, 255, 1);
CCP Module is configured to work as PWM module and the output pin of the PWM signal can be selected using the following two lines:
setup_ccp1(CCP_PWM | CCP1_A2);
setup_ccp1(CCP_PWM | CCP1_A5);
The complete CCS C code is below.
// DC Motor control using PIC12F1822 and L293D CCS PIC C code
// http://ccspicc.blogspot.com/
// electronnote@gmail.com
// Use at your own risk

#include <12F1822.h>
#fuses NOMCLR INTRC_IO PLL_SW
#device ADC = 10
#use delay(clock=32000000)
#use fast_io(A)

int8 s;                                               // Used to know motor status
int16 i;
void main() {
  setup_oscillator(OSC_8MHZ | OSC_PLL_ON);            // Set internal oscillator to 32MHz (8MHz and PLL)
  output_a(0);
  set_tris_a(0x1B);                                   // Configure RA0, RA1, RA3 & RA4 as inputs 
  port_a_pullups(0x1A);                               // Enable internal pull-ups for pins RA1, RA3 & RA4
  setup_adc(ADC_CLOCK_DIV_32);                        // Set ADC conversion time to 32Tosc
  setup_adc_ports(sAN0);                              // Configure AN0 pin as analog
  set_adc_channel(0);                                 // Select analog channel AN0
  setup_ccp1(CCP_OFF);                                // CCP1 OFF
  setup_timer_2(T2_DIV_BY_16, 255, 1);                // Set PWM frequency to 1.95KHz with 10-bit resolution
  while(TRUE){
    if(s != 0){
      i = read_adc();                                 // Read from AN0 and store in i
      set_pwm1_duty(i);                               // Set pwm1 duty cycle
      delay_ms(10);                                   // Wait 10 ms
    }
    if(!input(PIN_A1) && (s != 1)){                   // If RA1 button pressed
      s = 1;
      setup_ccp1(CCP_OFF);                            // CCP1 OFF
      output_a(0);
      delay_ms(50);
      setup_ccp1(CCP_PWM | CCP1_A2);                  // Configure CCP1 as a PWM (output at RA2)
    }
    if(!input(PIN_A3) && (s != 2)){                   // If RA3 button pressed
      s = 2;
      setup_ccp1(CCP_OFF);                            // CCP1 OFF
      output_a(0);
      delay_ms(50);
      setup_ccp1(CCP_PWM | CCP1_A5);                  // Configure CCP1 as a PWM (output at RA5)
    }
    if(!input(PIN_A4) && (s != 0)){                   // If RA4 button pressed
      s = 0;
      setup_ccp1(CCP_OFF);                            // CCP1 OFF
      output_a(0);
    }
  }
}

Friday, July 22, 2016

Remote Controlled Bipolar Stepper Motor Using PIC16F877A


Each CD-ROM or DVD-ROM drive has a bipolar stepper motor. The bipolar stepper motor has 2 windings which means that this type of motors has 4 wires.
Bipolar stepper motor coils IR
In this blog there are some topics shows how to control the bipolar stepper motor as the following one:
Bipolar stepper motor control with PIC16F877A microcontroller
Now in this topic an IR remote control is used to control the bipolar stepper motor speed and direction of rotation. The remote control used in this project uses NEC protocol and to see how to decode NEC protocol using PIC16F877A microcontroller see the following post:
NEC Protocol IR remote control decoder with PIC16F877A microcontroller
To control the bipolar stepper motor we need two H-bridge circuits and for that L293D motor driver chip is used, this cheap chip can work as a dual H-bridge drivers.
Project circuit schematic is shown below:
IR Remote controlled cd-rom bipolar stepper motor using PIC16F877A and L293D CCS PIC C
Remote controlled stepper motor using PIC16F877A CCS C code:
The IR remote control used in this project is shown below with the used buttons and their codes which are used in the code.
NEC IR remote control codes for stepper motor 
External interrupt is used for reading IR signals.
// Remote controlled bipolar stepper motor using PIC16F877A and L293D CCS C code
// http://ccspicc.blogspot.com/
// electronnote@gmail.com

#include <16F877A.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock = 8000000)
#use fast_io(B)
#use fast_io(D)

unsigned int8 step_number = 0, speed_delay = 2;
unsigned int32 remote_code;
#INT_TIMER1                                  // Timer1 interrupt ISR
void timer1_isr(void){
  remote_code = 0;
  clear_interrupt(INT_TIMER1);
  disable_interrupts(INT_TIMER1);
}
#INT_EXT                                     // External interrupt ISR
void ext_isr(void){
  unsigned int8 count = 0, i;
  unsigned int32 ir_code;
  // Check 9ms pulse (remote control sends logic high)
  while((input(PIN_B0) == 0) && (count < 200)){
    count++;
    delay_us(50);}
  if( (count > 199) || (count < 160))        // NEC protocol?
    return;                          
  count = 0;
  // Check 4.5ms space or repeated code
  while((input(PIN_B0)) && (count < 100)){
    count++;
    delay_us(50);}
  if( (count > 99) || (count < 30))          // NEC protocol?
    return;
  // Check repeated code
  if(count < 60){
    count = 0;
    while((input(PIN_B0) == 0) && (count < 14)){
      count++;
      delay_us(50);}
    if( (count > 13) || (count < 8))         // NEC protocol?
      return;
    if((remote_code == 0x40BF50AF) || (remote_code == 0x40BF906F))
    set_timer1(0);
  }
  // Read message (32 bits)
  for(i = 0; i < 32; i++){
    count = 0;
    while((input(PIN_B0) == 0) && (count < 14)){
      count++;
      delay_us(50);}
    if( (count > 13) || (count < 8))         // NEC protocol?
      return;                          
    count = 0;
    while((input(PIN_B0)) && (count < 40)){
      count++;
      delay_us(50);}
    if( (count > 39) || (count < 8))         // NEC protocol?
      return;                           
    if( count > 20)                          // If space width > 1ms
      bit_set(ir_code, (31 - i));            // Write 1 to bit (31 - i)
    else                                     // If space width < 1ms
      bit_clear(ir_code, (31 - i));          // Write 0 to bit (31 - i)
  }
  if((ir_code == 0x40BF50AF) || (ir_code == 0x40BF906F)){
    set_timer1(0);
    clear_interrupt(INT_TIMER1);
    enable_interrupts(INT_TIMER1);}
  if(ir_code == 0x40BFA05F){
    speed_delay++;
    if(speed_delay > 20) speed_delay = 20;
    return;}
  if(ir_code == 0x40BF609F){
    speed_delay--;
    if(speed_delay < 2) speed_delay = 2;
    return;}
  remote_code = ir_code; 
}
void stepper(int8 step){
  switch(step){
    case 0:
      output_d(0b000000110);
    break;
    case 1:
      output_d(0b00000101);
    break;
    case 2:
      output_d(0b00001001);
    break;
    case 3:
      output_d(0b00001010);
    break;
  }
}
void main(){
  output_b(0);                                // PORTB initial state
  set_tris_b(0xF7);
  port_b_pullups(TRUE);                       // Enable PORTB internal pull-ups
  output_d(0);
  set_tris_d(0);
  setup_timer_1(T1_INTERNAL | T1_DIV_BY_4);   // Timer1 configuration
  enable_interrupts(GLOBAL);                  // Enable global interrupts
  enable_interrupts(INT_EXT_H2L);                 // Enable external interrupt
  while(TRUE){
    while(remote_code == 0);
    while((remote_code == 0x40BF40BF) || (remote_code == 0x40BF50AF)){
      step_number++;
      if(step_number > 3) 
        step_number = 0;
      stepper(step_number);
      delay_ms(speed_delay);
    }
    while((remote_code == 0x40BF807F) || (remote_code == 0x40BF906F)){
      if(step_number < 1) 
        step_number = 4;
      step_number--;
      stepper(step_number);
      delay_ms(speed_delay);
    }
  output_d(0);
  if((remote_code != 0x40BF40BF) && (remote_code != 0x40B807F))
  remote_code = 0;
  }
}

Remote controlled stepper motor using PIC16F877A video:
The following video shows a hardware circuit for this project.

Tuesday, July 19, 2016

CD-ROM BLDC motor controller using PIC18F4550 and L293D


BLDC Motor controller using PIC18F4550 and L293D
BLDC motor ESC using PIC18F4550 and L293D 
In the following topic URL we've seen how to control BLDC motor speed and direction of rotation using PIC18F4550 microcontroller and 3-phase bridge circuit:
CD-ROM Spindle motor (BLDC) control with PIC18F4550 microcontroller
This topic shows how to make the same controller using L293D motor driver instead of the 3-phase bridge circuit.
The 3 phase bridge is more complicated and expansive and while the L293D motor driver chip is a small, cheap and saves time.
In this project we need two L293D chips because the BLDC motor is a three phase motor, and at any time two windings energized while the third one floating.
The L293D has 4 inputs and 4 outputs with 2 enable pins, each enable pin controls 2 outputs as shown below:
L293D Motor driver chip pinout BLDC motor
Complete circuit schematic is shown below:
Interfacing sensored BLDC motor with PIC18F4550 microcontroller and L293D circuit CCS PIC C
In the circuit there are 3 buttons connected to RB0, RB1 and RB2. The buttons connected to RB1 and RB2 are used to start the BLDC motor and the other button is a stop button.
The BLDC motor speed is controlled using a potentiometer connected to AN0 channel.
There are 3 AND gates (HEF4081BP) in the circuit, these gates are used to get a 3 PWM signals from the original one which comes from RC2 pin using CCP1 module.
HEF4081BP has 4 independent 2-input AND gates, three of them are used. This IC needs a supply voltage of +5V between pins 7 (GND) and 14 (VCC).
PIC18F4550 microcontroller internal oscillator is used (8MHz).
BLDC Motor control using PIC18F4550 and L293D CCS PIC C code:

// Sensored BLDC motor controller using PIC18F4550 and L293D CCS C code
// http://ccspicc.blogspot.com/
// electronnote@gmail.com

#include <18F4550.h>
#device ADC = 10
#fuses NOMCLR INTRC_IO
#use delay(clock = 8000000)
#use fast_io(B)
#use fast_io(D)

int8 hall, Direction = 0;
int8 MoveTable1[8] = {0, 50, 11, 56, 44, 14, 35, 0};
int8 MoveTable2[8] = {0, 35, 14, 44, 56, 11, 50, 0};
#INT_RB                                       // RB port interrupt on change
void rb_isr(void){
  hall = (input_b() >> 4) & 7;
  if(Direction == 1)
    output_d(MoveTable1[hall]);
  else
    output_d(MoveTable2[hall]);
  clear_interrupt(INT_RB);
}
void main(){
  setup_oscillator(OSC_8MHZ);                 // Set internal oscillator to 8MHz
  setup_adc_ports(AN0);                       // Configure RA0 (AN0) pin as analog
  output_b(0);                                // PORTB initial state
  set_tris_b(0xF7);                           // TRISB configurartion
  port_b_pullups(TRUE);                       // Enable PORTB internal pull-ups
  output_d(0);                                // PORTD initial state
  set_tris_d(0);                              // Configure PORTD pins as outputs
  setup_adc(ADC_CLOCK_DIV_8);                 // Set ADC conversion time to 64Tosc
  set_adc_channel(0);                         // Select channel 0 input
  setup_timer_2(T2_DIV_BY_1, 199, 1);         // Timer2 configuration for PWM
  setup_ccp1(CCP_OFF);
  enable_interrupts(GLOBAL);                  // Enable global interrupts
  while(TRUE){
    if(!input(PIN_B1)){                       // If RB1 button pressed
      if(Direction == 0){
        Direction = 1;
        setup_ccp1(CCP_PWM);                  // Configure CCP1 as a PWM
        clear_interrupt(INT_RB);              // Clear RB IOC flag bit
        enable_interrupts(INT_RB);            // Enable PORTB IOC
        hall = (input_b() >> 4) & 7;
        output_d(MoveTable1[hall]);
      }
    }
    if(!input(PIN_B2)){                       // If RB1 button pressed
      if(Direction == 0){
        Direction = 2;
        setup_ccp1(CCP_PWM);                  // Configure CCP1 as a PWM
        clear_interrupt(INT_RB);              // Clear RB IOC flag bit
        enable_interrupts(INT_RB);            // Enable PORTB IOC
        hall = (input_b() >> 4) & 7;
        output_d(MoveTable2[hall]);
      }
    }
    while(Direction != 0){
     set_pwm1_duty(read_adc());
     if(!input(PIN_B0)){
       disable_interrupts(INT_RB);             // Disable PORTB IOC
       output_d(0);
       setup_ccp1(CCP_OFF);                    // CCP1 OFF
       Direction = 0;
     }
   }
  }
}

BLDC Motor controller using PIC18F4550 and L293D video:
The following video shows project hardware circuit.


References:
Microchip: Sensored BLDC Motor Control Using dsPIC30F2010 (AN957).
Microchip: Brushless DC Motor Control Made Easy (AN857).
L293D Datasheet.

BLDC Motor control using PIC16F877A and L293D


Brushless DC motor control with PIC16F877A microcontroller and L293D driver
In this project:
Sensored brushless DC (BLDC) motor control with PIC16F877A microcontroller
I made a sensored BLDC motor speed controller using PIC16F877A and 3 phase bridge circuit.
In this project we are going to see how to build a BLDC motor controller using the same microcontroller and L293D motor driver chip instead of the 3 phase bridge circuit.
The 3 phase bridge is more complicated and expansive and while the L293D motor driver chip is a small, cheap and saves time.
In this project we need two L293D chips because the BLDC motor is a three phase motor, and at any time two windings energized while the third one floating.
The L293D has 4 inputs and 4 outputs with 2 enable pins, each enable pin controls 2 outputs as shown below:
L293D Half H-Bridge motor driver pinout
Complete circuit schematic is shown below:
Interfacing CD-ROM BLDC motor with PIC16F877A and L293D circuit CCS PIC C
In the circuit there are 3 buttons connected to RB0, RB1 and RB2. The buttons connected to RB1 and RB2 are used to start the BLDC motor and the other button is a stop button.
The BLDC motor speed is controlled using a potentiometer connected to AN0 channel.
There are 3 AND gates (HEF4081BP) in the circuit, these gates are used to get a 3 PWM signals from the original one which comes from RC2 pin using CCP1 module.
HEF4081BP has 4 independent 2-input AND gates, three of them are used. This IC needs a supply voltage of +5V between pins 7 (GND) and 14 (VCC).
The CD-ROM BLDC motor pin configurations is shown in the following image:
CD-ROM DVD-ROM Brushless DC motor pin configuration PIC16F877A L293D
Each sensor outputs a digital high for 180 electrical degrees and outputs a digital low for the other 180 electrical degrees. The following figure shows the relationship between the sensors outputs and the required motor drive voltages for phases A, B and C.
Brushless DC motor timing PIC16F877A L293D
The 3 hall effect sensors needs 3 pins and for that RB4, RB5 and RB6 are used.
Two lookup tables are used for motor driver commutation according to the following two tables where table1 for direction 1 and table 2 for direction 2:
BLDC motor direction control table PIC16F877A L293D 
IN1, EN1, IN2 and EN2 are the 1st L293D pins which are respectively IN1, EN1, IN3, EN2.
IN3 and EN3 are the 2nd L293D IN1 and EN1.
BLDC Motor control using PIC16F877A and L293D CCS PIC C code:
// Sensored brushless DC motor control with PIC16F877A and L293D CCS C code
// http://ccspicc.blogspot.com/
// electronnote@gmail.com

#include <16F877A.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#device ADC = 10
#use delay(clock = 8000000)
#use fast_io(B)
#use fast_io(D)

int8 hall, Direction = 0;
int8 MoveTable1[8] = {0, 50, 11, 56, 44, 14, 35, 0};
int8 MoveTable2[8] = {0, 35, 14, 44, 56, 11, 50, 0};
#INT_RB                                       // RB port interrupt on change
void rb_isr(void){
  hall = (input_b() >> 4) & 7;
  if(Direction == 1)
    output_d(MoveTable1[hall]);
  else
    output_d(MoveTable2[hall]);
  clear_interrupt(INT_RB);
}
void main(){
  output_b(0);                                // PORTB initial state
  set_tris_b(0xF7);
  port_b_pullups(TRUE);                       // Enable PORTB internal pull-ups
  output_d(0);
  set_tris_d(0);
  setup_adc(ADC_CLOCK_DIV_16);                // Set ADC conversion time to 16Tosc
  setup_adc_ports(AN0);                       // Configure AN0 as analog  
  set_adc_channel(0);                         // Select channel 0 input
  setup_timer_2(T2_DIV_BY_1, 199, 1);         // Set PWM frequency to 10KHz
  setup_ccp1(CCP_OFF);                        // CCP1 OFF
  enable_interrupts(GLOBAL);
  while(TRUE){
    if(!input(PIN_B1)){                       // If RB1 button pressed
      if(Direction == 0){
        Direction = 1;
        setup_ccp1(CCP_PWM);                  // Configure CCP1 as a PWM
        clear_interrupt(INT_RB);              // Clear RB IOC flag bit
        enable_interrupts(INT_RB);            // Enable PORTB IOC
        hall = (input_b() >> 4) & 7;
        output_d(MoveTable1[hall]);
      }
    }
    if(!input(PIN_B2)){                       // If RB1 button pressed
      if(Direction == 0){
        Direction = 2;
        setup_ccp1(CCP_PWM);                  // Configure CCP1 as a PWM
        clear_interrupt(INT_RB);              // Clear RB IOC flag bit
        enable_interrupts(INT_RB);            // Enable PORTB IOC
        hall = (input_b() >> 4) & 7;
        output_d(MoveTable2[hall]);
      }
    }
    while(Direction != 0){
     set_pwm1_duty(read_adc());
     if(!input(PIN_B0)){
       disable_interrupts(INT_RB);             // Disable PORTB IOC
       output_d(0);
       setup_ccp1(CCP_OFF);                    // CCP1 OFF
       Direction = 0;
     }
   }
  }
}

CD-ROM BLDC Motor control using PIC16F877A and L293D:
The following video shows project hardware circuit.

References:
Microchip: Sensored BLDC Motor Control Using dsPIC30F2010 (AN957).
Microchip: Brushless DC Motor Control Made Easy (AN857).
L293D Datasheet.