EditOperation
The powerbase is a minimalist device, focussed on ease of use... The controller provides one button and one bi-colour LED to communicate with the user. The button is used when programming the cars' ID, and the LED provides feedback on overload, powersave and programming.
6 jacks are located around the controller base to plug in up to 6 standard Scalextric Digital throttles. Throttles can be plugged into any jack, and their relationship with car ID is not important.
Power is provided from an external high-capacity 12-14 volt DC power supply. The supply should be capable of delivering 10-20 Amps without any noticible drop in output. A variable supply provides easy adjustment for the skill level of the drivers. The output should not exceed 16v due to limitations in the car chips.
Setting ID
The simplest programming of the cars is by pressing and holding the button on the controller, and then pressing and releasing the Lane-change button on the desired throttle. The light will flash orange/green to show programming is complete. The car will run with 'auto-brakes' at maximum braking. When the throttle is released, the brakes will automatically come on.
To program the car without 'auto-brakes', press and hold the controller button, and then press and release the Brake button on the desired throttle. The light will flash orange/green when programming is complete. Without auto-brakes, the car will not brake when the throttle is released, and instead the driver uses the Brake button on the controller.
To program car along with the
amount of auto-braking, with up to 5 distinct levels (0%, 33%, 50%, 66%, 100%); press and hold the the controller button, squeeze the throttle to the desired point, then press and release the Lane-change button. The orange/green light will flash from 1 to 4 times to report the level of braking applied during auto-braking. For the most amount of roll-on under braking, squeeze the throttle the most (ie its the least amount of braking).
Overload
When the controller detects an overload or short circuit, the system will shut down and the LED will flash RED until the controller is reset. Isolate the problem on the track and correct it, then Reset the controller by removing the power, wait 2 seconds, and powering again.
Powersave/Sleep Mode
If the system remains unused for about 10 minutes, the controller will go into power-save mode. In this situation, the power output to the track is shutdown. While in powersave mode, the controller slowly flashes the LED red then green.
Restart the controller by pressing the button on the controller. All systems will be powered up, and immediately ready for use.

SSD Powerbase Circuit Diagram
| Parts |
|---|
| U1 | PIC 18F2550 | | U2 | 7805 | | U3,U4 | BTS7960B | | D1,D2 | SMBJ30 | | X1 | 4MHz crystal | | LED1 | 5mm bi-colour Red/Green LED |
|
| Parts |
|---|
| R1 | 100kΩ | | R2-R7 | 18kΩ 1% | | R8 | 1MΩ | | R9 | 390Ω | | R10 | 470Ω | | R11 | 10kΩ | | R12 | 680Ω | | R13-R16 | 10kΩ |
|
| Parts |
|---|
| C1 | 100μF Elect | | C2 | 1μF tantalum | | C3 | 100nF | | C4,C5 | 15pF ceramic | | C6 | 220nF | | C7,C8 | 1μF | | SW1 | Pushbutton, N.O. | | F1 | 500mA fuse |
|
EditCircuit Description
The circuit is centred around the Microchip PIC18F2550. This easy to interface device provides to this project
- 7 ADC for reading throttles and track output load
- Outputs for feeding to the high power H-Bridge
- Serial and SPI for interconnectivity
- USB for future computer connectivity
- In-circuit, Flash-based, programming to allow quick and easy evaluation of code changes
The microcontroller leverages a relatively low-speed crystal oscillator of 4 MHz, with C4 amp; C5 for stability and R8 to increase the Q-factor. The micro leverages its internal PLL to upscale the oscilator to provide an internal oscillator of 48 MHz for use by the USB port. This gives a 12 MIPS device, more than enough for the controller tasks.
The circuit and CPU power is derived from the main system power, leveraging a standard 78 series 5v regulator mounted on a small heatsink. Smoothing on the input, and oscillation suppression on the output assist in providing a reliable power source to the CPU under any extreme momentary 'brown outs' from the power supply. C3 provides decoupling of the CPU and is placed right next to the voltage inputs on the chip.
The system is broadly broken into 3 areas - throttle reading, user interface, power output. Power for the system is provided by a high-performance high-output external power supply.
Throttles
The Scalextric Digital throttles, when plugged in, combine with its associated resistor and creates a simple voltage divider. The microcontroller reads the values from this divider, and interprets the levels for delivery to the output stage. The resistor is chosen to match the throttle trigger at maximum squeeze, to provide a ADC value of just over $3f - which equates to maximum speed.
Pressing the button for either the Lane Change or Brakes, continues to increase the resistance presented by the throttle, and causes the ADC to return larger values, however these are not truly proportional and require massaging in code.
When the throttle remains unplugged, the ADC reports a value of $ff as the input it pulled high.
Power H-Bridge
The power H-Bridge is created with a pair of Infineon BTS7960B drivers. These high-performance devices provide excellent 'on resistance' and include anti-short protection and current monitoring; and optionally slew-rate control for RF interferance reduction. These must be provided with suitable heatsinking for maximum continuous output.
Output protection is provided by suppression capacitors C7 & C8, while the transient voltage suppressors provide high-level voltage for the upper limits of the driver devices.
The microcontroller provides 3 control signals - Enable, to both drivers; and phase signals to each driver. All outputs are provided through high value resistors as recommended by Infineon. The drivers return current monitoring, with the sum of the outputs, which are sent to an ADC for monitoring. The microcontroller detects the values, and when excess current is supplied for a long period (½ sec) automatically shuts down the bridge. The current trip is approximately 10A, but can be changed in code.
The power drive section can be replaced with a commercial system, such as Super-H from Robot Power (however slew rate control is not available on that device)
Interface
Bi-colour LED provides operational feedback to the user - green for all ok, red for fault, orange for programming. Basic current-limited resistors to the LED are provided to even the light-intensity for the LED in daylight usage.
The single push-button switch is used when programming the car IDs and also to bring the controller out of sleep mode.
USB and Serial
The PIC18F2550 provides USB inteconnection with a suitable USB host. USB voltage is decoupled through C6 - this can be increased up to 1μF if necessary. While USB is present in the circuit, the example firmware code does not implement this interface. This is left as an exercise for the experienced user.
Serial (RS232) data can be lifted from pins 17 && 18, however this is 0-5v only and there is no firmware code provided for this interface.
EditPIC C Code - Controller.c
This C code for the PIC 18F2550 MCU will read throttles and drives digital track data.
/*
* Controller.c
*
* 6-way Powerbase controller for Scalextric slot cars
*
* (c) Electric Images, 2009
*
* Uses PIC18F2550, for future USB connectivity (to be implemented)
* OSC is 4Mhz crystal
*
* RA0: Throttle 1
* RA1: Throttle 2
* RA2: Throttle 3
* RA3: Throttle 4
* RA4: Red LED -hi on
* RA5: Load detect
* RB0: (SSP in)
* RB1: (SSP clk out)
* RB2: Throttle 5
* RB3: Throttle 6
* RB4: Panel button -normally hi
* RB5:
* RB6:
* RB7: Green LED -hi on
* RC0: H Driver- Enabled when hi
* RC1: H Driver- Phase 0
* RC2: H Driver- Phase 1
* RC3: (USB vref)
* RC4: (USB D-)
* RC5: (USB D+)
* RC6: (RS232 out)
* RC7: (SSP out)
*
*/
#include <p18cxxx.h>
#include "GenericTypeDefs.h" // file from Microchip
#define NOCOMMS
#include "UART.h" // stub for serial operations
// =========================================
// Configuration bits for the PIC18F2550
// for 8Mhz clock crystal...
//#pragma config PLLDIV=2
//#pragma config CPUDIV=OSC1_PLL2
// for 4Mhz clock crystal...
#pragma config PLLDIV=1
#pragma config CPUDIV=OSC1_PLL2
#pragma config USBDIV=2
#pragma config FOSC=HSPLL_HS
#pragma config FCMEN=OFF
#pragma config IESO=OFF
#pragma config PWRT=OFF
#pragma config BOR=OFF
#pragma config BORV=1
#pragma config VREGEN=ON
#pragma config WDT=OFF
#pragma config WDTPS=1
#pragma config MCLRE=ON
#pragma config LPT1OSC=OFF
#pragma config PBADEN=OFF
#pragma config CCP2MX=ON
#pragma config LVP=OFF
#pragma config XINST=OFF
#pragma config CP0=OFF
#pragma config CP1=OFF
#pragma config CP2=OFF
#pragma config CP3=OFF
#pragma config CPB=OFF
#pragma config CPD=OFF
#pragma config WRT0=OFF
#pragma config WRT1=OFF
#pragma config WRT2=OFF
#pragma config WRT3=OFF
#pragma config WRTB=OFF
#pragma config WRTC=OFF
#pragma config WRTD=OFF
#pragma config EBTR0=OFF
#pragma config EBTR1=OFF
#pragma config EBTR2=OFF
#pragma config EBTR3=OFF
#pragma config EBTRB=OFF
// =========================================
// Mappings of hardware to function
#define RED_LED PORTAbits.RA4
#define GREEN_LED PORTBbits.RB7
#define BUTTON_UP PORTBbits.RB4
// =========================================
#define HALF_1_BIT ((57L * 12) -31) // total 1 bit length is 116uS
#define HALF_0_BIT ((114L * 12) -31) // total 0 bit duration is 232uS
//#define HALF_1_BIT ((62L * 12) -31) // total 1 bit length is 116uS
//#define HALF_0_BIT ((132L * 12) -31) // total 0 bit duraiton is 232uS
#define PKT_RESET 0x00
#define PKT_PROGRAM 0x01
#define PKT_SPEED 0x02
// time for the clock to give hundreds of sec, less num of cycles inside the calc segment
#define HUNDSEC_COUNT (60000 -(13 /2))
// =========================================
// Output management
volatile union {
struct {
BYTE all:8;
};
struct {
BYTE nextbit:1; // this MUST be bit zero!
BYTE phase:1;
BYTE preamble:1;
};
} _live_out;
BYTE _live_crc;
BYTE _live_data;
BYTE _live_nbit;
BYTE _live_nbyte;
volatile BYTE _live_pkt; // constantly rolling packet counter (upwards)
volatile BYTE _live_triple; // constantly rolling packet triple-counter (downwards, 3..2..1)
volatile BYTE live_buf[7]; // EXCLUDES preamble and checksum
// ------------------------------------------
// These are macros to reduce compiler overhead, due to extra management code for calling
// methods from inside interrupts
//
// loads the output buffer with proper values, taken from preset buf
// --- this is NORMALLY called only from inside the interrupt (except for initial setup)
#define LOAD_PREAMBLE \
_live_nbit = 14; \
_live_nbyte = 0; \
_live_out.all = 0xff; \
_live_pkt++; \
if (--_live_triple == 0) _live_triple = 3;
// sets all the output off, in case of Overload or just sleep mode
// --- this is normally called inside the interrupts
#define SLEEP_OUTPUT \
INTCONbits.TMR0IE = 0; \
PORTC = 0; \
GREEN_LED = 0; \
RED_LED = 0;
// ------------------------------------------
// Throttle Commands
volatile typedef union {
BYTE all;
struct {
BYTE speed:6;
BYTE change:1;
BYTE brakes:1;
} bits;
} THROTTLE;
BYTE ad_throttle;
BYTE ad_toread;
// ------------------------------------------
// Brakes management
volatile typedef union {
BYTE all;
struct {
BYTE pressure:2;
BYTE :2;
BYTE autoon:1;
BYTE :2;
BYTE on:1;
} bits;
} BRAKES;
BRAKES car_brakes[6]; // set to 0xFF for always 'on', or 0(off),1,2,3(max) for variable
// ------------------------------------------
// ID setting management
enum
{
ID_off = 0,
ID_acquire,
ID_set,
ID_sync,
ID_level,
ID_awaitup
} eID;
BYTE id_state; // state engine - from enum above
BYTE id_set; // car id to set up & flash count
BYTE id_glow; // car's braking level indicator
// ------------------------------------------
// clock timings
volatile BYTE bcd_hs;
volatile BYTE bcd_s;
volatile BYTE bcd_m;
volatile unsigned int ticks;
volatile BYTE overload_ticks; // tick for overloading check
volatile unsigned int quiet_ticks; // ticks for no activity 'till shutdown
#define SLEEP_TICKS (unsigned int)(10 * 60L * 100) // 10 minutes
// ==========================================
// Declarations
void ResetSleep(void);
void StartOutput(void);
void WriteFlash(int loc, unsigned char val);
unsigned char ReadFlash(int loc);
// Interrupt service hooks
//
void drive_irq (void);
void low_irq(void);
#pragma code high_vector_section=0x8
void high_vector (void)
{
_asm GOTO drive_irq _endasm
}
#pragma code low_vector=0x18
void interrupt_at_low_vector(void)
{
_asm GOTO low_irq _endasm
}
#pragma code /* return to the default code section */
// ==========================================
// Main code sections
//
void main (void)
{
unsigned int last_tick = 0;
BYTE fl = 0, fh = 0;
PORTB = 0x80;
TRISB = 0x3f; // only RB7 (Green LED), RB6 output
PORTA = 0;
TRISA = 0xef; // only RA4 (Red LED) out
// set up the initial output to the H-Bridge
PORTC = 0;
TRISC = 0xB8; // RC0: enable out; RC1,2: signal out to H; RC4,5: USB; RC6,7: serial
// set the various analog states
ADCON1 = 0x05; // AN0->AN9 analog
ADCON2 = (0 << 7) + (0x7 <<3) + 0x2;
ADCON0 = 1; // code would be: (ad_toread << 2) | 1;
// set up the initial values in the output buffer
_live_pkt = 0;
_live_triple = 2;
live_buf[0] = PKT_SPEED;
live_buf[1] =
live_buf[2] =
live_buf[3] =
live_buf[4] =
live_buf[5] =
live_buf[6] = 0;
car_brakes[0].all = ReadFlash(0);
car_brakes[1].all = ReadFlash(1);
car_brakes[2].all = ReadFlash(2);
car_brakes[3].all = ReadFlash(3);
car_brakes[4].all = ReadFlash(4);
car_brakes[5].all = ReadFlash(5);
// set up the timer for the driver generator
INTCON2 = 0xf4; // (tmr0 is Hi priority)
T0CON = 0x88; // (tmr running, on instruction clock)
TMR0H = (-HALF_1_BIT >> 8);
TMR0L = -HALF_1_BIT;
RCONbits.IPEN = 1; // use prioritised interrupts
INTCON = 0xc0; // (both mixed priority interrupts on)
StartOutput();
ad_throttle = 0;
ad_toread = 0;
// set up the timer for the time-clock
IPR1 = 0; // all to low priority
T1CON = (1 << 7) | (0 << 6) | (1 << 4) | (0 << 3) | (0 << 1) | 1;
TMR1H = ((65536 -HUNDSEC_COUNT) / 256);
TMR1L = ((65536 -HUNDSEC_COUNT) % 256);
PIE1bits.TMR1IE = 1;
ticks = overload_ticks = quiet_ticks =
bcd_hs = bcd_s = bcd_m = 0;
// set up the state engine
id_state = ID_off;
// Open the Uart, if enabled
UARTOpen();
// prestart the ADC on the way in
ADCON0bits.GO = 1;
do {
BYTE ad_result, car;
THROTTLE th_cmd, car_cmd;
// wait for the ADC to complete - although in reality it should already have
while (ADCON0bits.GO)
;
ad_result = ADRESH; // hang onto the result
car = ad_throttle; // (note: car is ZERO base, and car 7 is overload
if (++ad_throttle == 7)
{
ad_throttle = 0;
ad_toread = 0;
}
else if (ad_throttle == 6)
{
ad_toread = 4; // check for overload
}
else
{
if (++ad_toread == 4) // AN4 is overload, so skip in first loop
ad_toread = 8;
}
// set up the next ADC conversion
ADCON0 = (ad_toread << 2) | 1;
// now process the _previous_ ADC result
if (car < 6)
{
//
// Process a car throttle level
//
BRAKES brake = { 0 };
th_cmd.all = car_cmd.all = 0;
// work out the car command, based upon each throttle range
if (ad_result >= 0xf0)
car_cmd.all = 0xff; // throttle disconnected, so create a small (all-1s) slot
else
{
if (ad_result & 0x80)
{
if (ad_result >= 0x8b) // work out rough throttle speed, and range-limit
{
th_cmd.all = (ad_result -0x8b) << 2;
if (th_cmd.all > 0x3f)
th_cmd.all = 0x3f;
}
th_cmd.bits.brakes = 1; // brakes on
brake.bits.on = 1;
}
else if (ad_result >= 0x50)
{
if (ad_result >= 0x5a)
{
th_cmd.all = ((ad_result -0x5a) *2); // work out rough throttle speed, and range-limit
if (th_cmd.all & 0x20)
th_cmd.all += 2;
if (th_cmd.all & 0x10)
th_cmd.all++;
if (th_cmd.all > 0x3f)
th_cmd.all = 0x3f;
}
th_cmd.bits.change = 1; // lane change pressed
}
else if (ad_result > 0x3f)
{
th_cmd.bits.speed = 0x3f; // range limit the throttle
}
else
{
if (ad_result > 1) // minimum trim the throttle
th_cmd.bits.speed = ad_result;
}
// if the resulting throttle is 'active' then reset the sleep timer
if (th_cmd.all > 0)
ResetSleep();
// decide the brakes status - but we don't want to force the brakes right now.
if (!th_cmd.bits.brakes)
car_cmd.all = th_cmd.all & 0x7f;
if (car_cmd.bits.speed == 0)
if (car_brakes[car].bits.autoon)
brake.bits.on = 1;
}
if (!id_state) // update buffer ONLY if not sending IDs out
{
live_buf[car +1] = car_cmd.all;
car_brakes[car].bits.on = brake.bits.on;
}
}
else
{
//
// process the overload channel ADC
//
if (ad_result > 37) // this equates to about 10amp - 55 is 15amp - 74 is 20a?
{
if (overload_ticks == 0)
overload_ticks = 1;
}
else
{
if ((overload_ticks & 0x80) == 0) // if not reached time-out, then its OK to reset
{
if (overload_ticks)
overload_ticks--;
}
}
}
// start the [next] AD conversion
ADCON0bits.GO = 1;
//
// now process the various outputs
//
if (overload_ticks & 0x80)
{
// overload occurred!
if (ticks & 16) // flash a light
RED_LED = 1;
else
RED_LED = 0;
}
else if (quiet_ticks > SLEEP_TICKS)
{
// turn output on if sleeping and button is pressed
if (!BUTTON_UP)
StartOutput();
else
{
// while sleeping, slowly flash the LEDs, alternating between red and green
BYTE t;
if (ticks != last_tick)
{
last_tick = ticks;
if (++fl == 0)
fh++;
}
t = TMR1L;
if (fh & 0x1)
{
if (fl & 0x80)
{
if ( TMR1H < -fl )
GREEN_LED = 1;
else
GREEN_LED = 0;
}
else
{
if ( TMR1H < fl )
GREEN_LED = 1;
else
GREEN_LED = 0;
}
}
else
{
if (fl & 0x80)
{
if ( TMR1H < -fl )
RED_LED = 1;
else
RED_LED = 0;
}
else
{
if ( TMR1H < fl )
RED_LED = 1;
else
RED_LED = 0;
}
}
}
}
else
{
// Finally, check for console button down (gone lo when pressed)
if (BUTTON_UP && (id_state <= ID_acquire))
{
// button is up, so a normal speed packet goes out
GREEN_LED = 1;
RED_LED = 0;
id_state = ID_off;
live_buf[0] = PKT_SPEED;
}
else if (car < 6)
{
// button is down, so look for controller button then program the car id when found
switch (id_state)
{
case ID_off: // button has just gone down, set up to program car
RED_LED = 1;
GREEN_LED = 1;
id_state++;
id_set = 0xff; // (mark that a Set is to come)
live_buf[0] = PKT_SPEED;
live_buf[1] =
live_buf[2] =
live_buf[3] =
live_buf[4] =
live_buf[5] =
live_buf[6] = 0x80;
break;
case ID_acquire: // find the controller for the car id - either brake or change button pressed
if (th_cmd.bits.brakes || th_cmd.bits.change)
{
BRAKES br;
id_set = car +1;
br.all = th_cmd.bits.speed >> 4;
id_glow = (3 -br.all +1) *2;
if (!th_cmd.bits.brakes) // set auto-on if brake button NOT used
br.bits.autoon = 1;
WriteFlash(car, br.all);
car_brakes[car].all = br.all;
id_state++;
}
break;
case ID_set: // program the id on the car
while (!_live_out.preamble && BUTTON_UP == 0) // wait for preamble to start
;
while (_live_out.preamble && BUTTON_UP == 0) // wait for preamble to finish
;
live_buf[0] = PKT_RESET;
live_buf[1] =
live_buf[2] =
live_buf[3] =
live_buf[4] =
live_buf[5] =
live_buf[6] = id_set;
while (!_live_out.preamble && BUTTON_UP == 0) // wait for preamble to start
;
while (_live_out.preamble && BUTTON_UP == 0) // wait for preamble to finish
;
live_buf[0] = PKT_PROGRAM;
while (!_live_out.preamble && BUTTON_UP == 0) // wait for preamble to start
;
while (_live_out.preamble && BUTTON_UP == 0) // wait for preamble to finish
;
live_buf[0] = PKT_RESET;
id_state++;
break;
case ID_sync: // wait for counters to sync up before starting the flashes
if ((ticks & 15) == 0)
{
id_state++;
RED_LED = 0;
GREEN_LED = 1;
}
break;
case ID_level: // tell the level of the brakes - 4 flash for max, 1 flash for least
if (!(id_glow & 1) ^ !(ticks & 16))
if (id_glow-- == 0)
id_state++;
if (id_glow & 1)
{
RED_LED = 1;
GREEN_LED = 0;
}
else
{
GREEN_LED = 1;
RED_LED = 0;
}
break;
case ID_awaitup: // now wait for the button to come back up
GREEN_LED = 1;
RED_LED = 0;
if (BUTTON_UP)
id_state = ID_off;
break;
}
}
}
} while (1);
}
//
// Output management
//
// NOTE that the macro SLEEP_OUTPUT is part of this suite of methods
//
void ResetSleep()
{
if (quiet_ticks < SLEEP_TICKS)
quiet_ticks = 0;
}
void StartOutput()
{
LOAD_PREAMBLE
PORTC = 0x05;
quiet_ticks = 0;
INTCONbits.TMR0IE = 1;
GREEN_LED = 1;
}
// -----------------------------------
// Flash Memory tools
//
void WriteFlash(int loc, unsigned char val)
{
PIR2bits.EEIF = 0;
EEADR = loc;
EEDATA = val;
EECON1 = 0;
EECON1bits.WREN = 1;
// this block cannot be changed
INTCONbits.GIE = 0;
EECON2 = 0x55;
EECON2 = 0xaa;
EECON1bits.WR = 1;
INTCONbits.GIE = 1;
// end block
while (PIR2bits.EEIF == 0)
;
EECON1bits.WREN = 0;
}
unsigned char ReadFlash(int loc)
{
EEADR = loc;
EECON1 = 0;
EECON1bits.RD = 1;
return EEDATA;
}
// ---------------------------------------------------------------
// The hi-priority interrupt must and ONLY be used for the drive signal
//
// !!! DO NOT CALL EXTERNAL METHOD FROM INSIDE HERE as it
// significantly increases the IRQ ovehead !!!
//
BRAKES _brake; // use a 'global' instead of in-code auto variable,
// as it reduces compiler overhead for the interrupt
#pragma interrupt drive_irq
void drive_irq(void)
{
// START TIME CRITICAL
if (_live_out.phase)
{
_live_out.phase = 0;
PORTCbits.RC1 = 0;
PORTCbits.RC2 = 1;
}
else
{
Nop();
PORTCbits.RC1 = 1;
PORTCbits.RC2 = 0;
_live_out.phase = 1;
}
if (_live_out.nextbit)
{
TMR0H = (-HALF_1_BIT >>8);
TMR0L = -HALF_1_BIT;
}
else
{
TMR0H = (-HALF_0_BIT >>8);
TMR0L = -HALF_0_BIT;
}
// END time critical
INTCONbits.TMR0IF = 0;
// if we have just started the 'last' phase of the clock, work out what the next bit will be
if (_live_out.phase)
{
// are we doing the preamble or the live data?
if (_live_out.preamble)
{ // its preamble, send all 1 bits until exhausted, then set up for 8 bit live data
PORTBbits.RB6 = 1;
if (--_live_nbit == 0)
{
_live_out.preamble = 0;
_live_nbit = 8;
_live_out.nextbit = 0;
_live_data = live_buf[0];
_live_crc = _live_data ^ 0xff;
PORTBbits.RB6 = 0;
}
}
else
{ // its live data, decide if it was the last bit in the byte
if (_live_nbit-- == 0)
{ // last bit has gone, now need to get next byte or new packet
if (++_live_nbyte > 7)
{ // all 7 (!) bytes gone, reload everything (via macro)
LOAD_PREAMBLE
}
else
{
_live_out.nextbit = 0; // flag more data to come
_live_nbit = 8; // and start another byte
if (_live_nbyte == 7)
{
_live_data = _live_crc;
}
else
{
_live_data = live_buf[_live_nbyte];
// now set the braking level (must use car id, not output id)
_brake.all = car_brakes[_live_nbyte -1].all;
if (_brake.bits.on)
{
switch (_brake.bits.pressure)
{
case 3: // quite light pressure (brake for packet 3 of 3 only)
if (_live_triple & 0x2)
_live_data |= 0x80;
break;
case 2: // half pressure (brake for odd packets only)
if (_live_pkt & 0x1)
_live_data |= 0x80;
break;
case 1: // quite heavy pressure (brake for packets 1 & 3 only)
if (_live_triple & 0x1)
_live_data |= 0x80;
break;
case 0: // full pressure
_live_data |= 0x80;
break;
}
}
_live_crc ^= _live_data;
}
}
}
else
{
if (_live_data & 0x80)
_live_out.nextbit = 1;
else
_live_out.nextbit = 0;
_live_data <<= 1;
}
}
}
}
// ----------------------------------------------
// General IRQ process stack
//
union {
struct {
unsigned char l;
unsigned char h;
};
unsigned short w;
} _tmr; // again, use a 'global' instead of auto variable.
#pragma interruptlow low_irq
void low_irq(void)
{
to_top:
// ALWAYS check the tick timer First
// - this fires 100 times per second, and the jitter is corrected below
if (PIR1bits.TMR1IF)
{
// The interrupt may not be serviced exactly when it it triggered, for many reasons.
// So adjust the next time count, by removing the time already consumed before the
// processor could get around to handling this one - this maintains consistency over
// time.
INTCONbits.GIE = 0;
_tmr.l = TMR1L; // This process can be made more efficient by rewriting in asm
_tmr.h = TMR1H;
_tmr.w += (65536 -HUNDSEC_COUNT);
TMR1H = _tmr.h;
TMR1L = _tmr.l;
INTCONbits.GIE = 1;
PIR1bits.TMR1IF = 0;
// bump the BCD counters
_asm
movlw 1
addwf bcd_hs, 0, BANKED
daw
movwf bcd_hs, BANKED
movlw 0
addwfc bcd_s, 0, BANKED
daw
movwf bcd_s, BANKED
_endasm
if (bcd_s >= 0x60)
{
bcd_s = 0;
bcd_m++;
}
// bump the constantly rolling tick counter
ticks++;
// check the overload tick-time - if more than 1/2 second, shut it all down!
if (overload_ticks)
{
if (++overload_ticks >= 40)
{
overload_ticks = 0xf0;
SLEEP_OUTPUT;
}
}
// check the quiet tick-time - if more than 10 mins, shut it all down
if (++quiet_ticks > SLEEP_TICKS)
{
quiet_ticks = 0xfff0;
SLEEP_OUTPUT;
}
goto to_top;
}
UART_IRQ_CODE(to_top) // link the UART code, if enabled
}
// End of Controller.c
EditPIC C Code - UART.h
This C code is the stub file for serial buffer support.
/*
* Send some data out serial
*
* (c) Electric Images, 2009
*/
#ifdef NOCOMMS
#define UARTOpen()
#define UARTClose()
#define UARTSendByte(c)
#define UARTSendHex(x)
#define UARTSendUInt(n)
#define UARTSendEOL()
#define UART_IRQ_CODE(recheck)
#else
#define OUT_RING ((unsigned char)128)
extern unsigned char out_ring[OUT_RING];
extern unsigned char out_front;
extern unsigned char out_back;
// (code removed)
#endif