Frc Demo.c Ukážkový program

From RoboWiki
Jump to: navigation, search
/******************************************************************************
*
* Freescale Semiconductor Inc.
* (c) Copyright 2008 Freescale Semiconductor, Inc.
* ALL RIGHTS RESERVED.
*
*******************************************************************************
*
* File      main.c
*
* Author    Milan Brejl
* 
* Version   0.2
* 
* Date      19-Nov-2008
* 
* Brief     Quick start with the Self-Driven Slot Car development
*
*******************************************************************************
* History:
* 0.1 (14-Oct-2008) Initial version
* 0.2 (19-Nov-2008) 
*******************************************************************************
*
* This file includes initialization code and macros enabling to quickly start
* with the development of a Self-Driven Slot Car.
*
* At the current state, the application branches into one of 2 modes, based on
* the slot car vertical position. If the car is placed on the track, it slowly
* starts up, drives the car by a 50% voltage, samples all analog signals (motor
* current, X, Y, and Z accelerations, and DC-Bus voltage) and stores the X
* acceleration into the EEPROM. In the wheels-up position, detected using the
* gravitational acceleration in Z axis, the data from EEPROM can be downloaded
* into an Excel sheet using the ReadEEPROM.pmp FreeMASTER project.
*
* Notes:
* 1) The MCU S08JM32 is configured to run at 48MHz CPU clock and 24MHz BUS 
*    clock. 
*    The OSBDM debugger can be used to program the flash and run the  
*    application, but it can not step and break the code at such a high rate.
* 2) The motor is controlled in one direction only, using a motorVoltage
*    unsigned int variable (range 0 to 6000) and a PWM signal of corresponding
*    duty-cycle. 
*    In order to control the motor in both directions (active breaking), the
*    initialization of the TPM1 module needs to be modified for a generation of
*    2 PWM signals. For more information refer to the MC33887 H-bridge manual.
* 3) The external EEPROM is handled by the Jaroslav Necesany's driver 
*    eeprom.c/.h, included into the project.
*
******************************************************************************/

#include <hidef.h>          /* for EnableInterrupts macro */
#include "derivative.h"     /* include peripheral declarations */
#include "slotcarutils.h"   /* utilities */
#include "eeprom.h"         /* driver for external EEPROM via IIC */

/******************************************************************************
* Constants and macros
******************************************************************************/

/* LEDs */
/* 
 * There are 4 LEds build into the head and break lights.
 *   HL = head light - left, HR = head light - right, 
 *   BL = break light - left, BR = break light - right 
 * The following macros control the LEDs state, ON or OFF.
 */
#define SET_LED_HL_ON          PTAD_PTAD0 = 1
#define SET_LED_HL_OFF         PTAD_PTAD0 = 0
#define SET_LED_HR_ON          PTAD_PTAD1 = 1
#define SET_LED_HR_OFF         PTAD_PTAD1 = 0
#define SET_LED_BL_ON          PTAD_PTAD2 = 1
#define SET_LED_BL_OFF         PTAD_PTAD2 = 0
#define SET_LED_BR_ON          PTAD_PTAD3 = 1
#define SET_LED_BR_OFF         PTAD_PTAD3 = 0

/* SWITCH */
/*
 * There is a 2-position switch.
 * The following macro reads the switch position, returning 1 or 0.
 */
#define GET_SWITCH_STATE       PTCD_PTCD6

/* ACCELEROMETER */
/*
 * There is a 3-axis accelerometer with analog outputs.
 * The accelerometer range is configurable for either +/- 1.5g or +/- 6g.
 * The accelerometer analog outputs can be sampled by the on-chip Analog-to-
 * -Digital Converter.
 * The following macros set the accelerometer range and define the ADC 
 * channels of the X, Y and Z accelerometer outputs:
 *   X ... left and right (+ left, - right)
 *   Y ... ahead and back (+ ahead, - back)
 *   Z ... up and down (+ down, - up)
 */
#define SET_ACC_RANGE_1_5_G    PTCD_PTCD2 = 0
#define SET_ACC_RANGE_6_G      PTCD_PTCD2 = 1
#define ACC_X                  2
#define ACC_Y                  1
#define ACC_Z                  0

/* MOTOR */
/*
 * There is an H-brigde driving the DC motor.
 * The motor drive can be enabled or disabled. When enabled, the applied motor
 * voltage is controlled by a PWM duty-cycle.
 * The H-brigde circuit returns a fault status in case of undervoltage,
 * short circuit, or overtemperature condition.
 * The motor current feedback can be sampled by the on-chip Analog-to-Digital
 * Converter.
 * The following macros enable or disable the motor drive, set the motor 
 * voltage, get the motor fault status and define the ADC channel of
 * the motor current.
 */
#define MOTOR_ENABLE           PTCD_PTCD4 = 1
#define MOTOR_DISABLE          PTCD_PTCD4 = 0
#define SET_MOTOR_VOLTAGE(x)   TPM1C2V = (x)
#define GET_MOTOR_VOLTAGE      TPM1C2V
#define GET_MOTOR_FAULT_STATUS PTGD_PTGD2
#define MOTOR_STATUS_OK        1
#define MOTOR_STATUS_FAULT     0
#define MOTOR_CURRENT          5

/* DC-BUS VOLTAGE */
/*
 * There is a circuit which enables to measure the DC-Bus voltage.
 * The DC-Bus Voltage Compensation technique enables to set a correct motor 
 * voltage and keep it independent of a the actual DC-Bus voltage.
 * The following macros defines the ADC channel of the DC-Bus voltage.
 */
#define DC_BUS_VOLTAGE         11

/* ADC */
/*
 * The following macros enable to initializes the sampling and 
 * conversion of an analog signal a defined read a conversion result - a 12-bit sample,
 * to check if the Analog-to-Digital Converter is busy by an active conversion, and
 * switch conversion resolution (8/10/12-bit mode)
 */
#define START_CONV(channel)    ADCSC1 = ADCSC1_AIEN_MASK | (channel)
#define READ_ADC_SAMPLE        ADCR
#define GET_ADC_ACTIVE_FLAG    ADCSC2_ADACT
#define SET_ADC_MODE_8_BIT     ADCCFG_MODE = 0
#define SET_ADC_MODE_10_BIT    ADCCFG_MODE = 2
#define SET_ADC_MODE_12_BIT    ADCCFG_MODE = 1

/* OUTAGE */
/*
 * There is a circuit which enables to detect a power supply outage
 * on the line change or crossover track pieces.
 */
#define GET_OUTAGE_DIR1        PTGD_PTGD0
#define GET_OUTAGE_DIR2        PTGD_PTGD1

/******************************************************************************
* Global variables
******************************************************************************/

/* application */
enum {
  APP_MODE_RUN,
  APP_MODE_DOWNLOAD
} appMode;

/* accelerometer */
unsigned int accX, accY, accZ;   /* 12-bit unsigned samples */

/* motor */
unsigned int motorVoltage;       /* range 0 to 6000 */
unsigned int motorCurrent;       /* 12-bit unsigned sample */

/* dc-bus */
unsigned int dcBus;              /* 12-bit unsigned sample */

/* global time */
unsigned int timeCounter;        /* global free-running 1/2ms counter */

/* extern global variables */
extern enum SEMAPHORE semaphore1;

/******************************************************************************
* Functions
******************************************************************************/

/******************************************************************************
* Interrupt Handlers
******************************************************************************/

/* Timer/PWM Interrupt */
interrupt VectorNumber_Vtpm1ovf void Vtpm1ovf_isr(void) 
{
  /* clear interrupt flag */
  TPM1SC_TOF = 0;
  
  /* update PWM duty-cycle according to the global variable motorVoltage */
  SET_MOTOR_VOLTAGE(motorVoltage);
  
  /* Start sequence of analog inputs sampling */
  START_CONV(MOTOR_CURRENT);
  
  /* Update the global 1/2ms timeCounter */
  timeCounter++;
}

/* ADC Conversion Complete Interrupt */
interrupt VectorNumber_Vadc void Vadc_isr(void)
{
  /* 
   * Read the new sample using GET_ADC_SAMPLE macro.
   * The reading also clears the interrupt flag.
   * Put all samples into the buffer.
   */
  switch(ADCSC1_ADCH)
  {
  case MOTOR_CURRENT:
    motorCurrent = READ_ADC_SAMPLE;
    START_CONV(ACC_X);
    break;
  case ACC_X:
    accX = READ_ADC_SAMPLE;
    START_CONV(ACC_Y);
    PutToBuffer16(accX);  /* store accX to EEPROM */
    break;
  case ACC_Y:
    accY = READ_ADC_SAMPLE;
    START_CONV(ACC_Z);
    break;
  case ACC_Z:
    accZ = READ_ADC_SAMPLE;
    START_CONV(DC_BUS_VOLTAGE);
    break;
  case DC_BUS_VOLTAGE:
    dcBus = READ_ADC_SAMPLE;
    break;
  }
}

/* Keyboard Interrupt */
interrupt VectorNumber_Vkeyboard void Vkeyboard_isr(void)
{
  /* clear interrupt flag */
  KBISC_KBACK=1;
}

/******************************************************************************
* Main
******************************************************************************/

void main(void) 
{
  /********** Device Initialization **********/

  /* initialize the Multi-Purpose Clock Generator (MCG) 
     for 48MHz CPU clock and 24MHz bus clock */
  MCGC2 = 0x36;   /* MCGC2: BDIV=0,RANGE=1,HGO=1,LP=0,EREFS=1,ERCLKEN=1,EREFSTEN=0 */
  MCGC1 = 0xB8;   /* MCGC1: CLKS=2,RDIV=7,IREFS=0,IRCLKEN=0,IREFSTEN=0 */
  while(!MCGSC_OSCINIT);  /* Wait until external reference is stable */
  while(MCGSC_IREFST);    /* Wait until external reference is selected */
  while((MCGSC & 0x0C) != 0x08);  /* Wait until external clock is selected as a bus clock reference */
  MCGC2 = 0x3E;   /* MCGC2: BDIV=0,RANGE=1,HGO=1,LP=1,EREFS=1,ERCLKEN=1,EREFSTEN=0 */
  MCGC1 = 0x88;   /* MCGC1: CLKS=2,RDIV=1,IREFS=0,IRCLKEN=0,IREFSTEN=0 */
  MCGC3 = 0x46;   /* MCGC3: LOLIE=0,PLLS=1,CME=0,VDIV=6 */
  MCGC2 &= (unsigned char)~0x08;   /* MCGC2: LP=0 */
  while(!MCGSC_PLLST);    /* Wait until PLL is selected */
  while(!MCGSC_LOCK);     /* Wait until PLL is locked */
  MCGC1 = 0x08;   /* MCGC1: CLKS=0,RDIV=1,IREFS=0,IRCLKEN=0,IREFSTEN=0 */
  while((MCGSC & 0x0C) != 0x0C);  /* Wait until PLL clock is selected as a bus clock reference */

  /* disable the COP watchdog */
  SOPT1_COPT = 0;
  
  /* initialize the LEDs as outputs */
  PTADD_PTADD0 = 1; /* output to LED1 */
  PTADD_PTADD1 = 1; /* output to LED2 */
  PTADD_PTADD2 = 1; /* output to LED3 */
  PTADD_PTADD3 = 1; /* output to LED4 */

  /* initialize the SWITCH as an input with pull-up */
  PTCDD_PTCDD6 = 0; /* input from switch */
  PTCPE_PTCPE6 = 1; /* pull-up enable */

  /* initialize the ACCELEROMETER range select as an output */
  PTCDD_PTCDD2 = 1; /* output to G-select */
  
  /* initialize the MOTOR driver inputs as outputs */
  PTCDD_PTCDD4 = 1; /* output to EN */
  PTADD_PTADD5 = 1; /* output to D1 */
  PTADD_PTADD4 = 1; /* output to D2 */
  PTFDD_PTFDD2 = 1; /* output to IN2 */
  
  /* initialize the MOTOR driver fault status output as an input */
  PTFDD_PTFDD1 = 0; /* input from fault status */
  
  /* initialize TPM1 for PWM generation */
  TPM1SC = 0;
  TPM1MOD = 6000;     /* Modulo = 6000 => f = 24MHz/1/(6000*2) = 2kHz */
  TPM1SC_CLKSx = 1;   /* clock source = bus clock */
  TPM1SC_PS = 0;      /* prescaler divider = 1 */
  TPM1SC_CPWMS = 1;   /* center-aligned PWM */
  TPM1C2SC_ELS2x = 2; /* low-true pulses */
  TPM1SC_TOIE = 1;    /* enable overflow interrupt */

  /* initialize the ADC */
  ADCCFG_ADICLK = 1;/* input clock = bus clock divided by 2 */
  ADCCFG_ADIV = 1;  /* input clock div = 2 */
  ADCCFG_ADLSMP = 1;/* long sample time enabled */
  ADCCFG_MODE = 1;  /* 12-bit mode */
  ADCSC2_ADTRG = 0; /* software trigger */
  APCTL1_ADPC0 = 1; /* pin ADP0 is ADC input - accelerometer Z */
  APCTL1_ADPC1 = 1; /* pin ADP1 is ADC input - accelerometer Y */
  APCTL1_ADPC2 = 1; /* pin ADP2 is ADC input - accelerometer X */
  APCTL1_ADPC5 = 1; /* pin ADP5 is ADC input - motor current */
  APCTL2_ADPC11 = 1; /* pin ADP11 is ADC input - dc-bus voltage */
  
  /* initialize Keyboard Interrupt to detect outage */
  KBIES_KBEDG0 = 0;  /* detect falling edge from OUTAGE_DIR1 */
  KBIES_KBEDG1 = 0;  /* detect falling edge from OUTAGE_DIR2 */
  KBISC_KBMOD = 0;   /* detect edge only */
  KBIPE_KBIPE0 = 1;  /* enable OUTAGE_DIR1 pin interrupt */
  KBIPE_KBIPE1 = 1;  /* enable OUTAGE_DIR2 pin interrupt */
  KBISC_KBIE = 1;    /* enable interrupts */
  
  /* enable interrupts */
  EnableInterrupts;

  /************ Board HW Initialization *************/

  /* initialize the Accelerometr */
  SET_ACC_RANGE_1_5_G;

  /* initialize the Motor H-bridge driver */
  PTAD_PTAD5 = 0;  /* set D1 low */
  PTAD_PTAD4 = 1;  /* set D2 high */
  PTFD_PTFD2 = 0;  /* set IN2 low */
  MOTOR_DISABLE;
  motorVoltage = 0;

  /* initialize EEPROM */
  EepromInit(0x45);  /* 400kbit/sec at 24MHz Bus clock */

  /************* Start-up *************/

  /* indicate the actual switch position */
  Wait(1000);
  if(GET_SWITCH_STATE == 1)
  {
    /* blink front lights once */
    SET_LED_HL_ON;
    SET_LED_HR_ON;
    Wait(300);
  }
  else
  {
    /* blink front lights twice */
    SET_LED_HL_ON;
    SET_LED_HR_ON;
    Wait(100);
    SET_LED_HL_OFF;
    SET_LED_HR_OFF;
    Wait(100);
    SET_LED_HL_ON;
    SET_LED_HR_ON;
    Wait(100);
  }
  SET_LED_HL_OFF;
  SET_LED_HR_OFF;

 
  /* test if the car is in the wheels up position and set application mode */
  if (accZ > 2048)
  {
    appMode = APP_MODE_DOWNLOAD;
    DisableInterrupts;
  }
  else
  {
    appMode = APP_MODE_RUN;
    /* slowly speed up */
    MOTOR_ENABLE;
    while(motorVoltage <= 3000)
    {
      motorVoltage++;
      Wait(1);
    }
  }

  /************* Background Loop *************/
  for(;;) 
  {
    switch(appMode)
    {
    case APP_MODE_DOWNLOAD:
      /* Download buffer from EEPROM if empty */
      LoadBufferFromEeprom();
      /* Show page-by-page downloading on break LEDs */
      if (semaphore1)
      {
        SET_LED_BR_ON;
        SET_LED_BL_OFF;
      }
      else
      {
        SET_LED_BR_OFF;
        SET_LED_BL_ON;
      }
      break;

    case APP_MODE_RUN:
      /* Upload buffer to EEPROM if filled up */
      StoreBufferToEeprom();
      /* Check motor fault status */
      if(GET_MOTOR_FAULT_STATUS == MOTOR_STATUS_FAULT)
      {
        motorVoltage = 0;
        SET_LED_BL_ON;
        SET_LED_BR_ON;
      }
      break;
    }
  } /* loop forever */
}