There’s this kid who wants to control a tank from his standard remote control, but it’s been difficult to find the right parts at the right price, so I made a component for him.
It’s basically a motor controller, powered by an MSP430 that takes PWM input from a standard remote control’s receiver. It can drive back and forward. It’s based on the cheap value line MSP430 that comes with the Launchpad.
It receives the two PWM signals from a standard RF receiver, takes the time of the PWM pulse the receiver sends (typically to a servo), calculates whether it should be moving the motor forward or backward, generates two new PWM signals and control signals for a cheap motor controller called L293D to drive the two motors of the tank. It can go backward and forward. I’ve made code examples that uses two sticks, one for each motor, and one that has a more traditional left, right, forward, backward control. The MSP430 code is posted below. Feel free to use the code as you wish. Give me credit if appropriate.
#include
int pEnable1 = BIT1;
int pEnable2 = BIT2;
int pInA1 = BIT6;
int pInA2 = BIT7;
int pInB1 = BIT3;
int pInB2 = BIT0;
int pServo1 = BIT4;
int pServo2 = BIT5;
int time_pulse(int port);
void left_motor(int speed);
void right_motor(int speed);
void stop();
void handle_speed1();
void handle_speed2();
void handle_speed3();
int A = 0;
static int dutyA = 100;
static int dutyB = 300;
static int B = 0;
static int pulse_port = 0;
static int before_pulse = 0;
static int pulse_start = 0;
static int pulse_end = 0;
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
P1SEL |= 0x06; // P1.1 – P1.2 option select
P1DIR |= 0x07 | pInA1 | pInA2 | pInB1 | pInB2; // P1.0 – P1.2 outputs
CCTL0 = OUTMOD_5 + CCIE;
CCTL1 = OUTMOD_5 + CCIE;
TACTL = TASSEL_2 + MC_2 + TAIE; // SMCLK, Contmode, int enabled
_BIS_SR(GIE);
CCR0 = 100;
CCR1 = 150;
P1OUT = 0x00;
while (1) {
handle_speed2();
}
//_BIS_SR(LPM0_bits + GIE); // Enter LPM0 w/ interrupt
}
void handle_speed2()
{
// Left motor
int t = time_pulse(pServo1);
int turn = 1800-time_pulse(pServo2);
if (turn > -50 && turn < 50) { turn = 0; } if (t > 1750 && t < 1850) { left_motor(0+turn); right_motor(0-turn); } else if (t > 1800) {
// 1500 – 2200
int speed = (t – 1800)*3;
if (speed > 900) {
speed = 900;
}
left_motor(speed+turn);
right_motor(speed-turn);
}
else {
// 1500 – 2200
int speed = (1800 + t)*3;
if (speed > 900) {
speed = 900;
}
left_motor(-speed-turn);
right_motor(-speed+turn);
}
}
void handle_speed3()
{
// Left motor
int t = time_pulse(pServo1);
if (t > 1750 && t < 1850) { left_motor(0); } else if (t > 1800) {
// 1500 – 2200
int speed = (t – 1800)*3;
if (speed > 900) {
speed = 900;
}
left_motor(speed);
}
else {
// 1500 – 2200
int speed = (1800 + t)*3;
if (speed > 900) {
speed = 900;
}
left_motor(-speed);
}
// Right motor
t = time_pulse(pServo2);
if (t > 1750 && t < 1850) { right_motor(0); } else if (t > 1800) {
int speed = (t – 1800)*3;
if (speed > 900) {
speed = 900;
}
right_motor(speed);
}
else {
int speed = (1800 + t)*3;
if (speed > 900) {
speed = 900;
}
right_motor(-speed);
}
}
void left_motor(int speed)
{
if (speed == 0) {
P1OUT &= ~pInA1;
P1OUT &= ~pInA2;
dutyA = 0;
}
else if (speed > 0) {
P1OUT &= ~pInA1;
P1OUT |= pInA2;
dutyA = 1000-speed;
}
else {
P1OUT &= ~pInA2;
P1OUT |= pInA1;
dutyA = 1000+speed;
}
}
void right_motor(int speed)
{
if (speed == 0) {
P1OUT &= ~pInB1;
P1OUT &= ~pInB2;
dutyB = 0;
}
else if (speed > 0) {
P1OUT &= ~pInB1;
P1OUT |= pInB2;
dutyB = 1000-speed;
}
else {
P1OUT &= ~pInB2;
P1OUT |= pInB1;
dutyB = 1000+speed;
}
}
void stop()
{
P1OUT = 0;
}
// Timer A0 interrupt service routine
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A0 (void)
{
if (dutyA == 0) {
CCR0 += 1000 – dutyA;
CCTL0 = OUTMOD_5 + CCIE;
}
else if (A == 0) {
CCR0 += dutyA;
CCTL0 = OUTMOD_1 + CCIE;
A++;
}
else {
CCR0 += 1000 – dutyA;
CCTL0 = OUTMOD_5 + CCIE;
A = 0;
}
}
// Timer_A2 Interrupt Vector (TAIV) handler
#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A1(void)
{
switch( TAIV )
{
case 2: if (dutyB == 0) {
CCR0 += 1000 – dutyB;
CCTL1 = OUTMOD_5 + CCIE;
}
else if (B == 0) {
CCR1 += dutyB;
CCTL1 = OUTMOD_1 + CCIE;
B++;
}
else {
CCR1 += 1000 – dutyB;
CCTL1 = OUTMOD_5 + CCIE;
B = 0;
}
break;
case 10: ; //P1OUT ^= 0x01; // Timer_A3 overflow
break;
}
}
int time_pulse(int port)
{
pulse_port = port;
before_pulse = 0;
pulse_start = 0;
pulse_end = 0;
P1IES = port;
P1IE = port;
while (pulse_end == 0) {
;
}
P1IE = 0;
return (pulse_end – pulse_start);
}
#pragma vector=PORT1_VECTOR
__interrupt void port_1 (void)
{
if (P1IFG & pulse_port) {
if (before_pulse == 0) {
before_pulse = TAR;
P1IES = 0;
}
else if (pulse_start == 0) {
pulse_start = TAR;
P1IES = pulse_port;
}
else if (pulse_end == 0) {
pulse_end = TAR;
}
}
P1IFG = 0;
}