Example Source Code:
PIC 16F877 Using CCS C

ME-210 Unicoder Demo

Description

This example demonstrates communicating with the ME-210 Unicoder encoder using I2C.

The output of the program will look like this:

8K PICLOADER v1.31
©R.Farmer/R.Perdriau 1999
Current image = Unicoder
PIC>###############<
Jumping to user code
Nubotics Unicoder ME-210 CCS C Demo
Unicoder init:
Sync: ..........
Echo: ..........
Get status: 0xff10
Get name: Uc0E
Constants
0. 0x01
1. 0x00
2. 0x30
3. 0x04
4. 0x30
5. 0x02
6. 0x00
7. 0xfb
8. 0x0b
9. 0x0a
10. 0x01
Monitoring Unicoder
Time Distance Angle Velocity Period
101660772 -1638 642 0 0


 

Download

me210_picc_demo.c - C source
me210_picc_demo.h - header file
me210_picc_demo.pjt - CCS project file
me210_picc_demo.hex - Intel Hex file

 

Source Code


me210_picc_demo.c


//---------------------------------------------------
// Unicoder CCS C Demo
//
// Copyright 2009, Noetic Design, Inc.
//
// This demonstation program shows off the features
// of the Unicoder ME-210 encoder.
//---------------------------------------------------

#include "me210_picc_demo.h"


//*********************************************************
// DEFINE VARIABLES
//*********************************************************

// timer2 variables
int32 timer2_ticks;                                        // incremented once per 204us; wraps every 10 days
long control_loop_counter;                                 // counts up to 2441 to increment seconds_elapsed
long seconds_elapsed;
int32 incremental_distance;                                // distance managed by incremental inputs


/******************************************************************************/
/*  Setup the Hardware                                                        */
/******************************************************************************/
void setup()
{
   set_tris_a(TRIS_A_VAL);
   set_tris_b(TRIS_B_VAL);
   set_tris_c(TRIS_C_VAL);
   #if __device__==877
   set_tris_d(TRIS_D_VAL);
   set_tris_e(TRIS_E_VAL);
   #endif

   port_b_pullups(TRUE);
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_CLOCK_DIV_32);
   setup_psp(PSP_DISABLED);

   setup_counters(RTCC_EXT_L_TO_H, WDT_2304MS);            // set up timer 0 input T0CKI for Right CLK input
   set_timer0(255);                                        // max count so interrupts on first edge
   setup_timer_2(T2_DIV_BY_1,255,4);                       // 4883 Hz interrupt rate (204.8us period); 19531 Hz PWM frequency

   //enable_interrupts(INT_RTCC);                          // CLK
   enable_interrupts(INT_TIMER2);                          // time keeping
   enable_interrupts(global);

   printf("Nubotics Unicoder ME-210 CCS C Demo\r\n");

   delay_ms(10);
   delay_ms(10);
}


//************************************************************************
// debug : output status code
// =====
//
void debug(UINT8 code)
{
   printf("Debug: %u\r\n", (byte)code);
}


//************************************************************************
// exec_command : execute Unicoder command
// =====
//
void exec_command(UINT8 mode)
{
    int i, count;
    
    i2c_start();
    i2c_write(ME210_ID);
    for (i=0 ; i<UC_command.send_count ; i++)
        i2c_write(UC_command.cmd[i]);
        
    if (UC_command.get_count)
    {
        if (mode == MODE_RESTART)
        {
            if (UC_command.delay != 0)
                delay_ms(UC_command.delay);
            i2c_start();
        }
        else
        {                                                                                              // must be MODE_STOP_START
            i2c_stop();
            if (UC_command.delay != 0)
                delay_ms(UC_command.delay);
            i2c_start();
        }
        i2c_write(ME210_ID | 0x01);
        for (i = 0; i < (UC_command.get_count - 1); i++)
            UC_command.reply[i] = i2c_read(1);   // ack on all but last
        UC_command.reply[UC_command.get_count-1] = i2c_read(0); // no ack on last byte
    }
    i2c_stop();
}


//************************************************************************
// init_unicoder
// =======
//
UINT16 init_unicoder()
{
   int i;
   UINT16 res = OK;

   printf("Unicoder init:\r\nSync: ");
   for (i=0;i<10;i++)
   {
       printf(".");
       do_sync();
       res = UC_command.status;
       if (res != OK)
          debug(res);
       delay_ms(2);
   }
   if (res != OK)
      return res;
   printf("\r\nEcho: ");
   for (i=0;i<10;i++)
   {
       printf(".");
       do_echo();
       res = UC_command.status;
       if (res != OK)
          debug(res);
       delay_ms(2);
   }
   printf("\r\n");
   return res;
}

//************************************************************************
// do_sync : execute sync command to check WheelCommander unit.
// =======
//
void do_sync(void)
{
    UC_command.cmd[0] = SYNC_CMD;
    UC_command.send_count = 1;
    UC_command.get_count = 1;
    UC_command.delay = 0;
    exec_command(MODE_RESTART);
    if (UC_command.reply[0] == '.')
    {
        UC_command.status = OK;
    }
    else
    {
        UC_command.status = SYNC_FAIL;
        debug(UC_command.status);
    }
}


//************************************************************************
// do_get_name : get type and version of device.
// =======
//
UINT16 do_get_name(char *buf)
{
   UC_command.cmd[0] = NAME_CMD;
   UC_command.send_count = 1;
   UC_command.get_count = 5;
   UC_command.delay = 0;
   exec_command(MODE_RESTART);
   if (UC_command.reply[0] == NAME_CMD)
   {
       UC_command.status = OK;
       buf[0] = UC_command.reply[1];
       buf[1] = UC_command.reply[2];
       buf[2] = UC_command.reply[3];
       buf[3] = UC_command.reply[4];
       buf[4] = '\0';
   }
   else
   {
       UC_command.status = GET_NAME_FAIL;
       debug(UC_command.status);
   }
   return ((UINT16)UC_command.reply[2]) + (((UINT16)UC_command.reply[3]) << 8);
}


//************************************************************************
// do_get_constant : configure a WheelCommander constant
// ===============
// Description
//    Execute an 'F' command to read a specified constant.
// Parameters
//    const_address : UINT8   : address of constant (2->34 at 01/03/06)
// Return values
//    Function returns read value : UINT8
// Globals
//    UC_command structure to hold command details and returned data.
//
UINT8  do_get_constant(UINT8 const_address)
{
    UC_command.cmd[0] = CONSTANT_CMD;
    UC_command.cmd[1] = const_address;
    UC_command.send_count = 2;
    UC_command.get_count = 3;
    UC_command.delay = 2;
    exec_command(MODE_STOP_START);
    if (UC_command.reply[0] == CONSTANT_CMD)
    {
        UC_command.status = OK;
        return UC_command.reply[2];
    }
    else
    {
        UC_command.status = GET_CONSTANT_FAIL;
        debug(UC_command.status);
    }
    return 0;
}

//************************************************************************
// do_set_constant : configure a WheelCommander constant
// ===============
//
void do_set_constant(UINT8 const_address, UINT8 new_value)
{
    UC_command.cmd[0] = CONSTANT_CMD;
    UC_command.cmd[1] = const_address;
    UC_command.cmd[2] = new_value;
    UC_command.send_count = 3;
    UC_command.get_count = 1;
    UC_command.delay = 0;
    exec_command(MODE_STOP_START);
    if (UC_command.reply[0] == 'a')
    {
        UC_command.status = OK;
    }
    else
    {
        UC_command.status = SET_CONSTANT_FAIL;
        debug(UC_command.status);
    }
}

//************************************************************************
// do_get_status : get current operating mode of WheelCommander
// =============
// Description
//    Execute an 'S' command to read 16-bit status word.
// Parameters
//    None
// Return values
//    None
// Globals
//    UC_command structure to hold command details and returned status data.
//      UC_command.reply[0] = 'S'
//      UC_command.reply[1] = low byte of status word
//      UC_command.reply[2] = high byte of status word
//
UINT16 do_get_status(void)
{
    UC_command.cmd[0] = GET_STATUS_CMD;
    UC_command.send_count = 1;
    UC_command.get_count = 3;
    UC_command.delay = 0;
    exec_command(MODE_RESTART);
    if (UC_command.reply[0] == GET_STATUS_CMD)
    {
        UC_command.status = OK;
    }
    else
    {
        UC_command.status = GET_STATUS_FAIL;
        debug(UC_command.status);
    }
    return ((UINT16)UC_command.reply[1]) + (((UINT16)UC_command.reply[2]) << 8);
}



//************************************************************************
// do_get_velocity : get the speed of motion
// ===============
//
INT16 do_get_velocity(void)
{
    UC_command.cmd[0] = VELOCITY_CMD;
    UC_command.send_count = 1;
    UC_command.get_count = 3;
    UC_command.delay = 0;
    exec_command(MODE_RESTART);
    if (UC_command.reply[0] == VELOCITY_CMD)
    {
        UC_command.status = OK;
        return(INT16)(((UINT16)UC_command.reply[1]) + (((UINT16)UC_command.reply[2]) << 8));
    }
    else
    {
        UC_command.status = GET_VELOCITY_FAIL;
        debug(UC_command.status);
    }
    return 0;
}


//************************************************************************
// do_get_period : get the time between encoder ticks
// ===============
//
INT32 do_get_period(void)
{
    UC_command.cmd[0] = PERIOD_CMD;
    UC_command.send_count = 1;
    UC_command.get_count = 5;
    UC_command.delay = 0;
    exec_command(MODE_RESTART);
    if (UC_command.reply[0] == PERIOD_CMD)
    {
        UC_command.status = OK;
        return(INT32)(
        ((UINT32)UC_command.reply[1]) +
        (((UINT32)UC_command.reply[2]) << 8) +
        (((UINT32)UC_command.reply[3]) << 16) +
        (((UINT32)UC_command.reply[4]) << 24));
    }
    else
    {
        UC_command.status = GET_PERIOD_FAIL;
        debug(UC_command.status);
    }
    return 0;
}


//************************************************************************
// do_get_angle : get the angle we have rotated through
// ===============
//
INT16 do_get_angle(void)
{
    UC_command.cmd[0] = ANGLE_CMD;
    UC_command.send_count = 1;
    UC_command.get_count = 3;
    UC_command.delay = 0;
    exec_command(MODE_RESTART);
    if (UC_command.reply[0] == ANGLE_CMD)
    {
        UC_command.status = OK;
        return(INT16)(((UINT16)UC_command.reply[1]) + (((UINT16)UC_command.reply[2]) << 8));
    }
    else
    {
        UC_command.status = GET_ANGLE_FAIL;
        debug(UC_command.status);
    }
    return 0;
}



//************************************************************************
// do_get_distance : get the distance we have moved
// ===============
//
INT32 do_get_distance(void)
{
    UC_command.cmd[0] = DISTANCE_CMD;
    UC_command.send_count = 1;
    UC_command.get_count = 5;
    UC_command.delay = 0;
    exec_command(MODE_RESTART);
    if (UC_command.reply[0] == DISTANCE_CMD)
    {
        UC_command.status = OK;
        return(INT32)(
        ((UINT32)UC_command.reply[1]) +
        (((UINT32)UC_command.reply[2]) << 8) +
        (((UINT32)UC_command.reply[3]) << 16) +
        (((UINT32)UC_command.reply[4]) << 24));
    }
    else
    {
        UC_command.status = GET_DISTANCE_FAIL;
        debug(UC_command.status);
    }
    return 0;
}

//************************************************************************
// do_get_time : get current timestamp
// ===============
//
INT32 do_get_time(void)
{
    UC_command.cmd[0] = TIME_CMD;
    UC_command.send_count = 1;
    UC_command.get_count = 5;
    UC_command.delay = 0;
    exec_command(MODE_RESTART);
    if (UC_command.reply[0] == TIME_CMD)
    {
        UC_command.status = OK;
        return(INT32)(
        ((UINT32)UC_command.reply[1]) +
        (((UINT32)UC_command.reply[2]) << 8) +
        (((UINT32)UC_command.reply[3]) << 16) +
        (((UINT32)UC_command.reply[4]) << 24));
    }
    else
    {
        UC_command.status = GET_TIME_FAIL;
        debug(UC_command.status);
    }
    return 0;
}

//************************************************************************
// do_coast : switch off motor system
// ========
//
void do_coast(void)
{
    UC_command.cmd[0] = COAST_CMD;
    UC_command.send_count = 1;
    UC_command.get_count = 1;
    UC_command.delay = 100;
    exec_command(MODE_RESTART);                                                                 // PLS March 9 2006: must release bus before querying status of coast command.
    if (UC_command.reply[0] == 'a')
    {
        UC_command.status = OK;
    }
    else
    {
        UC_command.status = COAST_FAIL;
        debug(UC_command.status);
    }
}


//************************************************************************
// do_get_pulsewidth : get the servo pulse generator pulsewidth (+/-2047 from neutral)
// ===============
//
INT16 do_get_pulsewidth(void)
{
    UC_command.cmd[0] = MOTOR_CMD;
    UC_command.send_count = 1;
    UC_command.get_count = 3;
    UC_command.delay = 0;
    exec_command(MODE_STOP_START);
    if (UC_command.reply[0] == MOTOR_CMD)
    {
        UC_command.status = OK;
        return(INT16)(((UINT16)UC_command.reply[1]) + (((UINT16)UC_command.reply[2]) << 8));
    }
    else
    {
        UC_command.status = GET_PULSEWIDTH_FAIL;
        debug(UC_command.status);
    }
    return 0;
}


//************************************************************************
// do_set_pulsewidth : set the servo pulse generator pulsewidth (+/-2047 from neutral)
// ===============
//
void do_set_pulsewidth(INT16 pw)
{
    UC_command.cmd[0] = MOTOR_CMD;
    UC_command.cmd[1] = pw & 0x0ff;
    UC_command.cmd[2] = (pw >> 8) & 0x0ff;
    UC_command.send_count = 3;
    UC_command.get_count = 1;
    UC_command.delay = 0;
    exec_command(MODE_STOP_START);
    if (UC_command.reply[0] == 'a')
    {
        UC_command.status = OK;
    }
    else
    {
        UC_command.status = SET_PULSEWIDTH_FAIL;
        debug(UC_command.status);
    }
}


//************************************************************************
// do_reset : reset all motion parameters
// ========
//
void do_reset(void)
{
    UC_command.cmd[0] = RESET_CMD;
    UC_command.send_count = 1;
    UC_command.get_count = 1;
    UC_command.delay = 100;
    exec_command(MODE_STOP_START);
    if (UC_command.reply[0] == 'a')
    {
        UC_command.status = OK;
    }
    else
    {
        UC_command.status = RESET_FAIL;
        debug(UC_command.status);
    }
}

//************************************************************************
// do_echo : test comms channel
// =======
//
void do_echo(void)
{
    UC_command.cmd[0] = ECHO_CMD;
    UC_command.cmd[1] = 0x0F;
    UC_command.send_count = 2;
    UC_command.get_count = 2;
    UC_command.delay = 0;
    exec_command(MODE_RESTART);
    if (UC_command.reply[1] == 0xF0)
    {
        UC_command.status = OK;
    }
    else
    {
        UC_command.status = ECHO_FAIL;
        debug(UC_command.status);
    }
}


/******************************************************************************/
/* Timer0 ISR (Right Encoder Clk)                                             */
/*                                                                            */
/******************************************************************************/
#int_timer0
void timer0_isr()
{
   set_timer0(255);                                        // max count so first tick gives us an interrupt
   if (input(ENC_DIR))
      incremental_distance++;
   else
      incremental_distance--;
}



/******************************************************************************/
/* Timer 2 Interrupt Handler                                                  */
/*                                                                            */
/* this keeps track of time and updates the servos                            */
/* on a fixed interval                                                        */
/******************************************************************************/
#int_timer2
void timer2_isr()
{
   timer2_ticks++;
   control_loop_counter++;
   if (control_loop_counter == CONTROL_LOOP_COUNT)         // once per 40.96ms = 24.4 Hz
   {
      seconds_elapsed++;                                   // count timer ticks, one per 40.96 ms, 24.4 Hz
      control_loop_counter = 0;
   }
}



/******************************************************************************/
/* Main Program Entry Point                                                   */
/*                                                                            */
/******************************************************************************/

#zero_ram                                                  // don't need to bother to set variables to zero initially; this does it for us
void main()
{
   char buf[5];
   UINT8 i;

   setup();
   if (init_unicoder() != OK)
   {
      printf("Failed to detect Unicoder.  Halting.\r\n");
      for (;;);
   }
   else
   {
      printf("Get status: 0x%lx\r\n", do_get_status());
      do_coast();
      do_get_name(buf);
      printf("Get name: %s\r\n", buf);
      
      printf("Constants\r\n");
      for (i = 0; i < 11; i++)
      {
         printf("%d. 0x%02x\r\n", i, do_get_constant(i));
         delay_ms(1);
      }
      
      printf("Monitoring Unicoder\r\n");
      printf("Time Distance Angle Velocity Period\r\n");
      for (;;)
      {
         printf("%ld %ld ", do_get_time(), do_get_distance());
         printf("%ld %ld ", do_get_angle(), do_get_velocity());
         printf("%ld         \r", do_get_period());
         delay_ms(100);
      }
   }
}




me210_picc_demo.h


//-----------------------------------------
// define target processor and resources
//-----------------------------------------

#include <16F877.h>
#device ADC=8
#device *=16
//#device ICD=FALSE
#use delay(clock=20000000)
#use rs232(baud=38400,parity=n,bits=8,xmit=PIN_C6,rcv=PIN_C7)
#fuses HS,NOWDT,PUT,NOLVP,NOBROWNOUT
#use I2C(master, sda=PIN_C4, scl=PIN_C3, FORCE_HW)


//-----------------------------------------
// define software structures and constants
//-----------------------------------------

typedef    unsigned char  UINT8;
typedef    unsigned long UINT16;
typedef    unsigned int32  UINT32;

//
// commands
//
#define     SYNC_CMD             '.'
#define     NAME_CMD             'N'
#define     CONSTANT_CMD         'F'
#define     COAST_CMD            'C'
#define     RESET_CMD            'R'
#define     GET_STATUS_CMD       'S'
#define     ECHO_CMD             'E'
#define     VELOCITY_CMD         'V'
#define     ANGLE_CMD            'W'
#define     DISTANCE_CMD         'D'
#define     TIME_CMD             'T'
#define     MOTOR_CMD            'M'
#define     PERIOD_CMD           'P'

//
// error codes
//
#define     OK                         1
#define     ERROR                    128
#define     SYNC_FAIL            ERROR+1
#define     GET_CONSTANT_FAIL    ERROR+2
#define     SET_CONSTANT_FAIL    ERROR+3
#define     COAST_FAIL           ERROR+5
#define     RESET_FAIL           ERROR+6
#define     ECHO_FAIL            ERROR+7
#define     GET_STATUS_FAIL      ERROR+8
#define     GET_VELOCITY_FAIL    ERROR+11
#define     GET_ANGLE_FAIL       ERROR+14
#define     GET_DISTANCE_FAIL    ERROR+16
#define     GET_TIME_FAIL        ERROR+17
#define     GET_PULSEWIDTH_FAIL  ERROR+18
#define     SET_PULSEWIDTH_FAIL  ERROR+19
#define     GET_PERIOD_FAIL      ERROR+20
#define     GET_NAME_FAIL        ERROR+21

//
// UC_command holds the details of a full I2C command and the returned data.
//
struct
{
    UINT8 cmd[6];                                                                                  // command byte list
    int   send_count;                                                                              // number of bytes to be sent
    UINT8 reply[6];                                                                                // list of byte values received
    int   get_count;                                                                               // number of bytes to be read
    UINT8 delay;                                                                                   // delay in mS between command write and data read
    UINT8 status;                                                                                  // error code or OK
} UC_command;


// constants
#define CONTROL_LOOP_COUNT 200                             // number of timer2 overflows per control loop period (200 = 24.4Hz, because overflows at 4883 Hz)
#define ME210_ID           0x30
#define MODE_STOP_START    0x0F
#define MODE_RESTART       0xF0


//-----------------------------------------
// define hardware
//-----------------------------------------
#define ENC_CLK    PIN_A4                                // I/O 1, TOCKI
#define ENC_DIR    PIN_B3                                // decoded direction

#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)

#define TRIS_A_VAL 0x1B                                    // 1 = input; A2 and A5 are outputs
#define TRIS_B_VAL 0xFF
#define TRIS_C_VAL 0xFF
#define TRIS_D_VAL 0xFF
#define TRIS_E_VAL 0xFF


// function prototypes
UINT16 init_unicoder();
void do_sync(void);
UINT16 do_get_name(char *buf);
void do_set_constant(UINT8 const_address, UINT8 new_value);
UINT8 do_get_constant(UINT8 const_address);
UINT16 do_get_status(void);
void do_coast(void);
void do_reset(void);
void do_echo(void);
void exec_command(UINT8 mode);
INT16 do_get_velocity(void);
INT32 do_get_period(void);
INT16 do_get_angle(void);
INT32 do_get_time(void);
INT16 do_get_pulsewidth(void);
void do_set_pulsewidth(INT16 pw);