Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
MSP430 circuit to measure and trick 1997 Jetta LD Pump
/* 1997 Volkwagen Jetta LD Pump Circuit tester/bypasser
* Trevor Bentley, September 2011
*
*
* Operation:
* - Displays welcome message on boot
* - Dumps 256 bytes of Informational Flash (if UART enabled)
* - Enters very low power state while waiting
* - Wakes up when a falling edge detected on P1.4, which should go to
* the power output of the ECM to the LD Pump solenoid. This requires
* extra circuitry.
* - Measures each edge as a 'pump', counts number of pumps.
* - Red LED (P1.0) blinks rapidly while pumping
* - When reach target number of pumps (PUMP_TARGET), green LED (P1.6)
* turns on, and SENSE line is pulled high (P1.5). This is held for
* SENSE_LINE_SECONDS.
* - Measured total number of pumps, and time to reach target number
* of pumps.
* - Red and Green LEDs both on while measurements written to Informational
* Flash.
* - Most recent measurements displayed (if UART enabled)
* - Back to very low power state while waiting.
*
*
*
* EXAMPLE CIRCUIT:
*
* BAT(-)-----------------------
* BAT(+)-----------[3.3REG] |
* | | |
* [C VCC]--|-^^^|--[ opto ]--^^^|----[P1.4 ]
* [A ] _ > [isolator] [ P1.0] - Red LED
* [R ] ^ > [ ] [ P1.6] - Green LED
* [ ] | > [ ] [ ]
* [E GND]--|----|--[ ] [ MSP430]
* [C ] [ ]
* [M SENSE]---^^^-----[transistor]-------[P1.5 ]
* | |
* BAT(+)----^^^-/ \--BAT(-)
*
*
* MSP430 powered by 3.3V regulator off of constant battery power.
*
* Car ECM connects to optoisolator via voltage divider (divide to 3V,
* and current limit). Safety diode from ground to power. Optoisolator
* connects to P1.4, pulled up to constant battery power. Car ECM will
* switch either VCC or GND on and off to pump the LD Pump, which this
* circuit replaces. This will result in toggling on P1.4.
*
* P1.5 controls base of transistor that pulls sense line up to constant
* battery power.
*
*/
#include <msp430x22x2.h>
#include <signal.h>
#include <string.h>
//------------------------------------------------------------------------------
// Hardware-related definitions
//------------------------------------------------------------------------------
#define UART_TXD 0x02 // TXD on P1.1 (Timer0_A.OUT0)
#define UART_RXD 0x04 // RXD on P1.2 (Timer0_A.CCI1A)
#define LDPUMP 0x10 // LDPump detect P1.4
#define SENSE 0x20 // Sense Line P1.5
#define RED_LED 0x01 // Red LED
#define GREEN_LED 0x40 // Green LED
//#define TEST_VALS
#ifndef TEST_VALS
#define SENSE_LINE_SECONDS 90U // Seconds to hold SENSE line
#define PUMP_TARGET 30U // Number of 'pumps' to detect
#else
#define SENSE_LINE_SECONDS 4U
#define PUMP_TARGET 10U
#endif
#define CLOCK_DIV (10U)
#define MS_PER_TICK (10*CLOCK_DIV) // Milliseconds per clock interrupt
// Number of clock interrupts to hold SENSE line for.
#define SENSE_TICKS (SENSE_LINE_SECONDS * (1000 / MS_PER_TICK))
// Base address of Informational Flash
#define INFO_FLASH_BASE 0x1000
//------------------------------------------------------------------------------
// Conditions for 9600 Baud SW UART, SMCLK = 1MHz
//------------------------------------------------------------------------------
#define UART_ENABLED
#ifdef UART_ENABLED
#define UART_TBIT_DIV_2 (1000000U / (9600 * 2))
#define UART_TBIT (1000000U / 9600)
#endif
//------------------------------------------------------------------------------
// Global variables used for full-duplex UART communication
//------------------------------------------------------------------------------
#ifdef UART_ENABLED
unsigned int txData = 0; // UART internal variable for TX
unsigned char rxBuffer = 0; // Received UART character
#endif
// Structure for storing measurements
typedef struct {
unsigned int pump_count;
unsigned int timer_count;
} measure_t;
// Number of elements in measurements[]
#define MEASUREMENT_COUNT (64/sizeof(measure_t))
// Mask for wrapping measurements[] index
#define MEASUREMENT_MASK (MEASUREMENT_COUNT-1)
// Array of measurement readings
measure_t measurements[MEASUREMENT_COUNT];
// Index of current measurement
unsigned int measurement_idx = 0;
// State machine variable
volatile enum tstate {
WAITING,
PUMPING,
HOLDING,
UARTTX
} timer_state = WAITING;
// LUT for hex characters
unsigned char hexvals[] = {
'0','1','2','3','4','5','6','7',
'8','9','A','B','C','D','E','F',
};
volatile unsigned int counter = 0; // Timer interrupt ticks
volatile unsigned int toggles = 0; // Toggles of pumps
volatile unsigned int counter_cycles_until_stop = 0; // IRQ ticks until done
//------------------------------------------------------------------------------
// Function prototypes
//------------------------------------------------------------------------------
void reset_ldpump(void);
void erase_flash(int addr);
void save_byte(char byte, char flash_offset);
void save_measurements(void);
void end_sense_hold(void);
void TimerA_Counter_init(void);
void Port1_ISR(void);
void Timer_A0_ISR(void);
#ifdef UART_ENABLED
void print_values(void);
void uart_isr(void);
void TimerA_UART_init(void);
void TimerA_UART_tx(unsigned char byte);
void TimerA_UART_print(char *string);
void TimerB_ISR(void);
#endif
//------------------------------------------------------------------------------
// main()
//------------------------------------------------------------------------------
void main(void)
{
#ifdef UART_ENABLED
int temp;
int i;
char str[4];
#endif
// Configure clocks
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
DCOCTL = 0x00; // Set DCOCLK to 1MHz
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;
// Configure Port 1
P1OUT = 0x00; // Initialize all GPIO
P1DIR = 0xFF & ~(UART_RXD | LDPUMP); // Set all pins but RXD to output
#ifdef UART_ENABLED
P1SEL = UART_TXD | UART_RXD; // Timer function for TXD/RXD pins
#endif
// P1.4 is interrupt -- LD Pump solenoid line
P1IE = 0x10; // Turn on P1.4 interrupts
P1IES = 0x10; // High->low edge detect
P1REN = 0x10; // Pull-down resistor enabled
// Clear measurements
memset(measurements, 0xFF, sizeof(measurements));
eint(); // Enable interrupts
#ifdef UART_ENABLED
// Print 256-bytes of informational flash
TimerA_UART_init();
TimerA_UART_print("\r\nLDP-TEST\r\n");
str[2] = ' ';
str[3] = 0;
for (i = 0; i < 256; i++) {
temp = *((volatile char*)INFO_FLASH_BASE+i);
str[0] = hexvals[temp >> 4 & 0xF];
str[1] = hexvals[temp & 0xF];
TimerA_UART_print(str);
}
TimerA_UART_print("\r\n$$$%%%$$$\r\n\r\n");
#endif
// Enable pump detector
TimerA_Counter_init();
for (;;)
{
// Sleep in super-low-power-mode
LPM3;
// Go to moderate-low-power-mode after PIO wakes us up
LPM0;
// Awoken after a pump session has finished and written to flash.
// Print status (if enabled), and reset state machine
#ifdef UART_ENABLED
print_values();
#endif
reset_ldpump();
}
}
// Write entire 'measurements' variable to information flash (erases first)
// Manages interrupts itself. Do NOT disable.
void save_measurements(void) {
int i;
static int count = 0;
static int offset = 0;
unsigned char *p = (unsigned char*)measurements;
dint();
FCTL3 = FWKEY; // Unlock
FCTL1 = FWKEY | ERASE ; // Erase
*((volatile char*)(INFO_FLASH_BASE + offset)) = 0;
FCTL1 = FWKEY | WRT; // Write
memcpy((void*)(INFO_FLASH_BASE + offset + i), p,
sizeof(measurements));
FCTL1 = FWKEY; // No-op
FCTL3 = FWKEY | LOCK; // Lock
// If we've written a full block...
if (++count == MEASUREMENT_COUNT) {
count = 0;
// Clear measurements
memset(measurements, 0xFF, sizeof(measurements));
// Next time, write to next block of FLASH. We have 3 64-byte blocks.
offset += 0x40;
if (offset == 0xc0) offset = 0;
}
eint();
}
void reset_ldpump(void) {
// Reset counters and state
counter = 0;
toggles = 0;
counter_cycles_until_stop = 0;
timer_state = WAITING;
P1OUT &= ~(RED_LED | GREEN_LED | SENSE); // Disable lights and sense
}
//------------------------------------------------------------------------------
// Turn on timer, interrupt ticks every 10ms
// Interrupt shared with UART, so only one can be enabled at a time.
//------------------------------------------------------------------------------
void TimerA_Counter_init(void) {
// Configure Timer_A
// Internal 1MHz timer divided by 8 (ID_3) == 125000 / s
// (12500 ticks/s) / 100 == 1250 ticks per 10 ms
// One interrupt per 10*CLOCK_DIV ms.
TACTL = MC_0; // Disable timer
TACCR0 = 1250*CLOCK_DIV; // ticks per interrupt
TACCTL0 = CCIE | OUTMOD_7; // Enable interrupts, reset on overflow
timer_state = WAITING;
TACTL = TASSEL_2 | MC_1 | TACLR | ID_3; // Set to 1MHz clock, turn on
}
//------------------------------------------------------------------------------
// Interrupt when edge detected on P1.4, signifying switching
// on LD Pump solenoid line.
//------------------------------------------------------------------------------
interrupt(PORT1_VECTOR) Port1_ISR(void) {
if (timer_state == WAITING) {
// We were waiting in super-low-power mode. Wake up!
LPM3_EXIT;
// No longer waiting, it's pump time
timer_state = PUMPING;
}
else if (++toggles == PUMP_TARGET) {
// We reached our target number of pumps, so trigger SENSE line.
P1OUT |= SENSE | GREEN_LED;
P1OUT &= ~(RED_LED);
counter_cycles_until_stop = SENSE_TICKS;
timer_state = HOLDING;
}
P1IFG = 0x00;
}
void end_sense_hold(void) {
volatile int i;
P1OUT |= RED_LED;
measurements[measurement_idx].pump_count = toggles;
measurements[measurement_idx].timer_count = counter;
measurement_idx = (measurement_idx + 1) & MEASUREMENT_MASK;
save_measurements();
P1OUT &= ~RED_LED;
}
//------------------------------------------------------------------------------
// Timer_A UART - Transmit Interrupt Handler
//------------------------------------------------------------------------------
enablenested interrupt (TIMERA0_VECTOR) Timer_A0_ISR(void)
{
static unsigned char txBitCnt = 10;
switch (timer_state) {
case WAITING: // Waiting to detect first pump from ECM
break;
case PUMPING: // Pumps detected, counting to target number
// Increment counter, toggle LED
if (++counter)
P1OUT ^= RED_LED;
break;
case HOLDING: // Target number of pumps reached, holding SENSE line
if (--counter_cycles_until_stop == 0) {
end_sense_hold();
// Wake up main. It will print status and reset state machine
LPM0_EXIT;
}
break;
#ifdef UART_ENABLED
case UARTTX:
goto uart;
#endif
default:
break;
}
return;
#ifdef UART_ENABLED
uart:
TACCR0 += UART_TBIT; // Add Offset to CCRx
if (txBitCnt == 0) { // All bits TXed?
TACCTL0 &= ~CCIE; // All bits TXed, disable interrupt
txBitCnt = 10; // Re-load bit counter
}
else {
if (txData & 0x01) {
TACCTL0 &= ~OUTMOD2; // TX Mark '1'
}
else {
TACCTL0 |= OUTMOD2; // TX Space '0'
}
txData >>= 1;
txBitCnt--;
}
#endif
}
#ifdef UART_ENABLED
void print_values(void) {
unsigned char mstr[10];
TimerA_UART_init();
mstr[0] = hexvals[toggles >> 12 & 0xF];
mstr[1] = hexvals[toggles >> 8 & 0xF];
mstr[2] = hexvals[toggles >> 4 & 0xF];
mstr[3] = hexvals[toggles & 0xF];
mstr[4] = ' ';
mstr[5] = hexvals[counter >> 12 & 0xf];
mstr[6] = hexvals[counter >> 8 & 0xF];
mstr[7] = hexvals[counter >> 4 & 0xF];
mstr[8] = hexvals[counter & 0xF];
mstr[9] = 0;
TimerA_UART_print(mstr);
TimerA_UART_print("\r\n\r\n");
TimerA_Counter_init();
}
//------------------------------------------------------------------------------
// Function configures Timer_A for full-duplex UART operation
//------------------------------------------------------------------------------
void TimerA_UART_init(void)
{
TACTL = MC_0; // Disable timer
TACCTL0 = OUT; // Set TXD Idle as Mark = '1'
TACCTL1 = SCS + CM1 + CAP + CCIE; // Sync, Neg Edge, Capture, Int
timer_state = UARTTX;
TACTL = TASSEL_2 + MC_2 + TACLR; // SMCLK, start in continuous mode
}
//------------------------------------------------------------------------------
// Outputs one byte using the Timer_A UART
//------------------------------------------------------------------------------
void TimerA_UART_tx(unsigned char byte)
{
while (TACCTL0 & CCIE); // Ensure last char got TX'd
TACCR0 = TAR; // Current state of TA counter
TACCR0 += UART_TBIT; // One bit time till first bit
TACCTL0 = OUTMOD0 + CCIE; // Set TXD on EQU0, Int
txData = byte; // Load global variable
txData |= 0x100; // Add mark stop bit to TXData
txData <<= 1; // Add space start bit
}
//------------------------------------------------------------------------------
// Prints a string over using the Timer_A UART
//------------------------------------------------------------------------------
void TimerA_UART_print(char *string)
{
while (*string) {
TimerA_UART_tx(*string++);
}
}
//------------------------------------------------------------------------------
// Timer_A UART - Receive Interrupt Handler
//------------------------------------------------------------------------------
interrupt (TIMERA1_VECTOR) Timer_A1_ISR(void)
{
}
//------------------------------------------------------------------------------
#endif //UART_ENABLED
all: default run
default:
msp430-gcc -I/usr/local/msp430-gcc-4.4.3//msp430/include/ ldpump_tester.c -save-temps -mendup-at=main -mmcu=msp430x2111 -Os
run:
mspdebug rf2500 "prog a.out"
dump:
mspdebug rf2500 "md 0x1000 256"
erase_info:
mspdebug rf2500 "erase segment 0x1000"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.