Distance Meter using MSP430 Launchpad and Ultrasonic Sensor HC-SR04

Here I summarize the hardware connections and source code to build a Distance meter using MSP430 Launchpad, Ultrasonic Sensor HC-SR04.

Project: Ultrasonice based Distance Meter with LCD Display
Microcontroller: MSP430G2231 on MSP-EXP430G2 Launchpad
Ultrasonic Ranging Module: HC-SR04
16×2 LCD Display: 1602K27-00

Hardware connections

Board jumper changes:
1. Isolate LEDs connected to P1.0 and P1.6 by removing Jumpers cap J5.
2. Isolate RX/TX connected to P1.1 and P1.2 by removing those Jumper cap in J3

Microcontroller and Ultrasonic sensor Connections:
P1.0 – Trigger
P1.1 – Echo – This should not be changed! – Why P1.1? – msp430g2231 datasheet mention this as – input for Timer A0 – Compare/Capture input

Microcontroller and LCD Connections
TP1 – Vcc (+5v)
TP3 – Vss (Gnd)
P1.2 – EN
P1.3 – RS
P1.4 – D4
P1.5 – D5
P1.6 – D6
P1.7 – D7
Gnd – RW
Gnd – Vee/Vdd – Connect to Gnd through a 1K Resistor – this value determines contrast – – i.e. without resistor all dots always visible,                                          whereas – higher resistor means dots not at all displayed.
Gnd – K (LED-)
Vcc – A (LED+) +5V – For Backlight
Clock: 1MHz

Distance Meter

Overview of Ultrasonic Sensor and LCD operations:

More details on build environment in Linux, sensor and LCD operations have been explained in following sections.

1. Build and Debug environment in Linux for MSP430

2. Interfacing Ultrasonic Sensor with MSP430

3. Interfacing 16×2 LCD display with MSP430

Source code

/********************************************************************/
/* Project: Ultrasonice based Distance Meter with LCD Display 		*/
/* Microcontroller: MSP430G2231 on MSP-EXP430G2 Launchpad			*/
/* Ultrasonic Ranging Module: HC-SR04								*/
/* 16x2 LCD Display: 1602K27-00										*/
/********************************************************************/
/* Build command on Linux:											
	$ msp430-gcc -mmcu=msp430g2231 -g -o msp_dist.elf msp_dist.c	*/
/********************************************************************/
/* Board jumper changes:
	1. Isolate LEDs connected to P1.0 and P1.6 by
	removing Jumpers cap J5.
	2. Isolate RX/TX connected to P1.1 and P1.2 by 
	removing those Jumper cap in J3									*/
/********************************************************************/
/* uC and Ultrasonic sensor Connections
	P1.0 - Trigger
	P1.1 - Echo - This should not be changed!
				- Why P1.1? - msp430g2231 datasheet mention this as
	  			- input for Timer A0 - Compare/Capture input		*/
/********************************************************************/
/* uC and LCD Connections
	TP1 - Vcc (+5v)
	TP3 - Vss (Gnd)
	P1.2 - EN
	P1.3 - RS
	P1.4 - D4
	P1.5 - D5
	P1.6 - D6
	P1.7 - D7
	Gnd  - RW
	Gnd  - Vee/Vdd - Connect to Gnd through a 1K Resistor
			- this value determines contrast -
			- i.e. without resistor all dots always visible, whereas
			- higher resistor means dots not at all displayed.
	Gnd  - K (LED-)
	Vcc  - A (LED+) +5V - For Backlight
	Clock: 1MHz														*/
/********************************************************************/

#include <msp430g2231.h>
#include <stdlib.h>
#include <string.h>

// uC GPIO Port assignment
#define UC_PORT    	P1OUT
#define UC_PORT_DIR	P1DIR

// Peripheral pin assignments
#define US_TRIG			BIT0
#define US_ECHO			BIT1
#define LCD_EN     		BIT2
#define LCD_RS      	BIT3
#define LCD_DATA		BIT4 | BIT5 | BIT6 | BIT7
#define LCD_D0_OFFSET	4	// D0 at BIT4, so it is 4
#define US_MASK			US_TRIG | US_ECHO
#define LCD_MASK		LCD_EN | LCD_RS | LCD_DATA

unsigned int up_counter;
unsigned int distance_cm;

/* Timer A0 Capture Interrupt routine
 P1.1 (echo) causes this routine to be called */
#pragma vector=TIMERA0_VECTOR
__interrupt void TimerA0(void)
{
	if (CCTL0 & CCI)			// Raising edge
	{
		up_counter = CCR0;		// Copy counter to variable
	}
	else						// Falling edge
	{
		// Formula: Distance in cm = (Time in uSec)/58
		distance_cm = (CCR0 - up_counter)/58;
	}
	TA0CTL &= ~TAIFG;			// Clear interrupt flag - handled
}

void timer_init()
{
	/* Timer A0 configure to read echo signal:
	Timer A Capture/Compare Control 0 =>
	capture mode: 1 - both edges +
	capture sychronize +
	capture input select 0 => P1.1 (CCI1A) +
    capture mode +
	capture compare interrupt enable */
	CCTL0 |= CM_3 + SCS + CCIS_0 + CAP + CCIE;

	/* Timer A Control configuration =>
	Timer A clock source select: 1 - SMClock +
	Timer A mode control: 2 - Continous up +
	Timer A clock input divider 0 - No divider */
 	TA0CTL |= TASSEL_2 + MC_2 + ID_0;

	// Global Interrupt Enable
	_BIS_SR(GIE);

}

void lcd_reset()
{
	UC_PORT = 0x00;
	__delay_cycles(20000);

	UC_PORT = (0x03 << LCD_D0_OFFSET) | LCD_EN;
	UC_PORT &= ~LCD_EN;	
	__delay_cycles(10000);

	UC_PORT = (0x03 << LCD_D0_OFFSET) | LCD_EN;
	UC_PORT &= ~LCD_EN;	
	__delay_cycles(1000);

	UC_PORT = (0x03 << LCD_D0_OFFSET) | LCD_EN;
	UC_PORT &= ~LCD_EN;	
	__delay_cycles(1000);

	UC_PORT = (0x02 << LCD_D0_OFFSET) | LCD_EN;
	UC_PORT &= ~LCD_EN;	
	__delay_cycles(1000);

}

void lcd_cmd (char cmd)
{
	// Send upper nibble
	UC_PORT = (((cmd >> 4) & 0x0F) << LCD_D0_OFFSET) | LCD_EN;
	UC_PORT &= ~LCD_EN;

	// Send lower nibble
	UC_PORT = ((cmd & 0x0F) << LCD_D0_OFFSET) | LCD_EN;
	UC_PORT &= ~LCD_EN;

	__delay_cycles(4000);
}

void lcd_data (unsigned char dat)
{
	// Send upper nibble
	UC_PORT = ((((dat >> 4) & 0x0F) << LCD_D0_OFFSET) | LCD_EN | LCD_RS);
	UC_PORT &= ~LCD_EN;

	// Send lower nibble
	UC_PORT = (((dat & 0x0F) << LCD_D0_OFFSET) | LCD_EN | LCD_RS);
	UC_PORT &= ~LCD_EN;

	__delay_cycles(4000); // a small delay may result in missing char display
}

void lcd_init ()
{
	lcd_reset();         // Call LCD reset

	lcd_cmd(0x28);       // 4-bit mode - 2 line - 5x7 font.
	lcd_cmd(0x0C);       // Display no cursor - no blink.
	lcd_cmd(0x06);       // Automatic Increment - No Display shift.
	lcd_cmd(0x80);       // Address DDRAM with 0 offset 80h.
	lcd_cmd(0x01);		 // Clear screen

}

void display_line(char *line)
{
	while (*line)
		lcd_data(*line++);
}

void display_distance(char *line, int len)
{
	while (len--)
		if (*line)
			lcd_data(*line++);
		else
			lcd_data(' ');
}
int main()
{
	char distance_string[4];

	WDTCTL = WDTPW + WDTHOLD;		// Stop Watch Dog Timer
	UC_PORT_DIR = LCD_MASK;			// Output direction for LCD connections
	UC_PORT_DIR |= US_TRIG;			// Output direction for trigger to sensor
	UC_PORT_DIR &= ~US_ECHO;		// Input direction for echo from sensor
	UC_PORT &= ~US_TRIG;			// keep trigger at low
	P1SEL = US_ECHO;				// set US_ECHO as trigger for Timer from Port-1

	// Initialize LCD
	lcd_init();

	// Initialize timer for Ultrasonice distance sensing
	timer_init();

	lcd_cmd(0x80); // select 1st line (0x80 + addr) - here addr = 0x00
	display_line(" Distance Meter");
	lcd_cmd(0xce); // select 2nd line (0x80 + addr) - here addr = 0x4e
	display_line("cm");

	while (1)
	{
		// measuring the distance
		UC_PORT ^= US_TRIG; 				// assert
		__delay_cycles(10);					// 10us wide
		UC_PORT ^= US_TRIG; 				// deassert
		//__delay_cycles(60000);			// 60ms measurement cycle
		__delay_cycles(500000);				// 0.5sec measurement cycle

		// displaying the current distance
		itoa(distance_cm, distance_string, 10);
		lcd_cmd(0xcb); // select 2nd line (0x80 + addr) - here addr = 0x4b
		display_distance(distance_string, 3);
	}

}

You can download source code here >> msp_dist.c

Here is the Distance meter in action 🙂

IMG_20150311_214406

 

Share this post

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *