Skip to content

ReynDaniel/msp430-encoder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MSP430 Shaft Encoder System

Daniel Reynolds — REYN Consultancy / La Trobe University, 2024

A bare-metal embedded C project implementing a shaft encoder measurement system on the TI MSP430 LaunchPad. Built as part of ELE3DEC — Digital Electronics and Controllers at La Trobe University.

This project was deliberately implemented at register level with no Arduino, no HAL abstraction, and no external libraries. The goal was to understand the embedded hardware, not abstract it away.


What This Is

An electronic shaft encoder system that measures rotational speed and position of a 3D-printed encoder wheel using a photo interrupter. Results are displayed on a 2-line LCD with three switchable display modes and a real-time spinning animation.


Why Register-Level?

Most microcontroller tutorials use Arduino or vendor HAL layers that hide the hardware behind function calls. This project configures every peripheral directly:

  • Timer registers set by hand for exact 500ms intervals
  • GPIO interrupt flags, edge select, and pull-up resistors configured at bit level
  • LCD HD44780 driven via 4-bit parallel interface — nibble writes, enable pulses, CGRAM character loading
  • Custom CGRAM characters defined as 5×8 pixel bitmaps written directly to LCD controller memory
  • ISR vectors defined explicitly with #pragma vector

This approach builds the foundational understanding that HAL-based development skips.


System Architecture

┌──────────────────────────────────────────┐
│         MSP430G2553 LaunchPad            │
│                                          │
│  PORT1 GPIO Interrupt                    │
│  ┌──────────────────┐                    │
│  │ Photo Interrupter │──── slot detect   │
│  │ (encoder wheel)   │    rising edge    │
│  └──────────────────┘    ISR: count++    │
│                                          │
│  TimerA0 — 500ms periodic interrupt      │
│  ┌─────────────────────────────────────┐ │
│  │ RPM = (slots × 60000)               │ │
│  │       ─────────────────             │ │
│  │       (slots/rev × interval_ms)     │ │
│  └─────────────────────────────────────┘ │
│                                          │
│  Push Buttons (active-low, pull-up)      │
│  PB1 → reset count                       │
│  PB2 → toggle display mode               │
│                                          │
│  Status LED — flashes on slot detection  │
│  Speaker    — GPIO frequency toggle      │
└──────────────────┬───────────────────────┘
                   │ 4-bit parallel
                   ▼
         ┌─────────────────┐
         │  HD44780 LCD    │
         │  Line 1: data   │
         │  Line 2: anim   │
         └─────────────────┘

Hardware

Component Details
Microcontroller MSP430G2553 (TI LaunchPad)
Encoder 3D-printed wheel, 10 slots, 40mm radius
Sensor Photo interrupter (slot detection)
Display 2×16 HD44780 LCD (4-bit interface)
Buttons 2× push button (active-low, internal pull-up)
LED Status indicator — flashes on each slot
Speaker GPIO-toggled audio (bonus feature)

Photo: docs/hardware_photo.jpg


Firmware Overview

firmware/main.c

Component Implementation
LCD driver 4-bit parallel, nibble writes, HD44780 init sequence
Custom characters CGRAM loaded at init — 4-frame spinning animation
Encoder sensing GPIO interrupt, rising edge, internal pull-up
Button input GPIO interrupt, falling edge, software debounce in main loop
RPM calculation TimerA0 500ms periodic ISR counts slots per interval
Display modes 3 modes cycled by PB2: Count → Distance → Speed
Bottom line Animated custom character when turning, blank when stopped
Speaker GPIO frequency toggling — brief chirp on each slot event
Sleep LPM0 between events — woken by ISR

RPM Calculation

RPM = (slots_in_500ms × 60000) / (slots_per_rev × 500)
    = slots_in_500ms × 12

Distance Calculation

Arc per slot = 2π × 40mm / 10 = 25.1mm ≈ 25mm
Distance (mm) = total_slot_count × 25

Key Embedded Concepts Demonstrated

Direct register access — no abstraction layer between code and hardware:

/* TimerA0: SMCLK source, /8 divider, Up mode, 500ms interval */
TA0CCR0 = 62500;
TA0CTL  = TASSEL_2 | ID_3 | MC_1;

/* GPIO interrupt: rising edge, pull-up enabled */
P1REN |= ENCODER_PIN;
P1OUT |= ENCODER_PIN;
P1IES &= ~ENCODER_PIN;
P1IE  |= ENCODER_PIN;

Interrupt Service Routines with explicit vector declarations:

#pragma vector=PORT1_VECTOR
__interrupt void PORT1_ISR(void) { ... }

#pragma vector=TIMER0_A0_VECTOR
__interrupt void TIMER0_A0_ISR(void) { ... }

Volatile variables for ISR-shared state — prevents compiler optimisation of variables modified in interrupt context:

volatile uint16_t slot_count        = 0;
volatile uint16_t slots_in_interval = 0;
volatile uint8_t  update_display    = 0;

CGRAM character loading — writing 5×8 pixel bitmaps directly to LCD controller character RAM:

lcd_cmd(0x40 | (char_index * 8)); /* CGRAM address */
for (j = 0; j < 8; j++) {
    lcd_data(pixel_row[j]);       /* 5 active bits per row */
}

LPM0 sleep — processor sleeps between events, woken by interrupts:

__bis_SR_register(LPM0_bits);     /* sleep */
__bic_SR_register_on_exit(LPM0_bits); /* wake from ISR */

Software debounce — buttons disable their own interrupt in ISR, debounced in main loop, re-enabled after confirmation:

/* In ISR — disable and flag */
P1IE &= ~BTN_MODE;
btn_mode_pressed = 1;

/* In main loop — debounce then re-enable */
__delay_cycles(20000);
if (!(P1IN & BTN_MODE)) { /* confirm still pressed */
    display_mode = (display_mode + 1) % NUM_MODES;
}
P1IE |= BTN_MODE;

GPIO audio output — speaker driven by toggling a GPIO pin at audio frequency. No DAC, no PWM peripheral — just deterministic bit toggling:

for (i = 0; i < 50; i++) {
    P1OUT ^= SPEAKER_PIN;
    __delay_cycles(500); /* ~1kHz at 1MHz SMCLK */
}

Display Modes

Mode Line 1 Content Toggled By
0 Count: NNN PB2
1 Dist: NNNN mm PB2
2 Speed: NNNN RPM PB2

Line 2 shows a 4-frame spinning animation using custom CGRAM characters while the wheel is turning, and is blank when stopped.

PB1 resets count and RPM to zero at any time.


Building

Requirements: Code Composer Studio (TI) or msp430-gcc

# With msp430-gcc
msp430-gcc -mmcu=msp430g2553 -O2 -o encoder.elf firmware/main.c
mspdebug rf2500 "prog encoder.elf"

CCS: Create new CCS project, target MSP430G2553, add main.c.


Bonus Features Implemented

Beyond the base assignment requirements:

  • Speaker audio feedback — GPIO frequency toggling generates a chirp on each encoder slot detection
  • LPM0 sleep mode — processor sleeps between events rather than busy-waiting, demonstrating power-aware embedded design
  • Splash screen — startup display showing system identity before entering scan mode

Context

This project complements the 3D Scanner repository:

  • The 3D scanner demonstrates multidisciplinary systems integration — MicroPython, BLE, Python GUI, sensor fusion, mechatronic design
  • The MSP430 encoder demonstrates low-level embedded foundations — register-level C, ISRs, timer peripherals, direct hardware interfacing

Together they show a range from bare-metal firmware through to full-stack mechatronic systems integration.


Acknowledgements

Developed as part of ELE3DEC — Digital Electronics and Controllers,
La Trobe University, Semester 1 2024.
Subject Coordinator: Associate Professor Robert Ross.


Contact

📧 Daniel@reyn.com.au
🔗 reyn.com.au
💼 github.com/ReynDaniel

About

Bare-metal MSP430 embedded C project demonstrating interrupts, timers, LCD interfacing, RPM calculation, and direct register-level microcontroller control.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages