Created
January 16, 2012 23:05
-
-
Save mrmekon/1623498 to your computer and use it in GitHub Desktop.
MSP430 circuit to measure and trick 1997 Jetta LD Pump
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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