Thursday, February 9, 2012

The Scalextric Digital Power Base project (Part 3)

The PIC software


The software running in the PIC processor is of course taken from the same site as the electronic schema, i.e. Electric Images. I didn't get it to work right away, but after some tinkering with linker scripts (that also takes care of not writing over memory space used by the bootloader) etc, I finally got it compiled and running on the device.

USB Interface

Next challenge was to implement the USB communication. Using this blog I got some inspiration and hints on how to make it work.  After some troubleshooting trying to get data sent from the device to the computer, I realized that I still had some errors in the linker script. This time regarding the memory position of the USB I/O buffers. After fixing that, the communication worked like a charm. Next step was to implement some functionality for the USB communication. In the ProcessUsbIO(void) function I added the following two commands:

switch(ReceivedDataBuffer[0])
{
  case 0x80: // Set remote controlling
    use_remote_data = ReceivedDataBuffer[1];
    break;

  case 0x81: // Send/Receive track/car data
.
.
}


The first command, 0x80 sets a boolean variable that tells the base station to use USB data for sending data to the cars. The other command, 0x81, sends the current handcontroller states to the computer and receives commands to be written to the cars.

Overriding the hand controllers

In the original digital base controller, when connecting it to a computer, the computer takes complete control over the power base. That means that all data from the hand controllers are flowing through the computer before being sent to the cars. So I made some changes to the PIC code so that it would send the commands through the USB interface and read them from there to be sent to the cars. To my surprise it was a quite easy task and did work pretty well without any notice of delays or lags, using 10ms read/write intervals.
The logic in the PIC to handle the data to and from the computer (command 0x81) looks something like this:

  case 0x81: // Send/Receive track/car data
    ResetToSendDataBuffer();

    ToSendDataBuffer[0] = 0x81;            // repeat command id
    ToSendDataBuffer[1] = PORTCbits.RC0;// track power enabled
    ToSendDataBuffer[2] = amps_read;    // amps used on track (raw ad value)
    ToSendDataBuffer[3] = live_buf[0];    // track packet type
 

    // loop through each car data
    for (car = 0; car < 6; car++)
    {
      BYTE breakData;
      if (pre_car_brakes[car].bits.on)
        breakData = 0x80;
      else
        breakData = 0x00;

      ToSendDataBuffer[car+4] = pre_live_buf[car];
      ToSendDataBuffer[car+4] |= breakData;
    }

    TransmitDataToHost();

    // only write to live_buf if remote data is set to be used and speed packets are written to the track
    if (use_remote_data && live_buf[0] == PKT_SPEED)
    {
      for (car = 1; car < 7; car++)
      {
        live_buf[car] = ReceivedDataBuffer[car];
        car_brakes[car-1].bits.on = ReceivedDataBuffer[car] & 0x80;
      }
    }
               
    break;


The basic idea here is to save the hand controller states in temporary variables called pre_live_buf and pre_car_brakes. From there they are sent to the computer. When the computer then sends the commands for the cars, they are directly written to the original live_buf and car_brakes variables used in the original code.

I'll post the full code at some later time as it's not so nice looking at the moment as it's still in a proof of concept state.