ProtoThreads on PIC microcontroller with CCS compiler

It is possible to use ProtoThreads on Microchip PIC microcontrollers using CCS compiler. CCS compiler does not support the trick with case statements inside a while loop, but needed functionality can be implemented with combination of label_address() and goto_address() functions. Thus "lc" header file shall look like:



/*
 * Copyright (c) 2004-2005, Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 ...
 */

/**
 * \file
 * Implementation of local continuations based on the label_address()
 * and goto_address() functions of CCS
 * \author
 * Igor Lesik
 *
 * This implementation of local continuations is based on
 * the label_address() and goto_address() functions of CCS. This
 * feature allows assigning pointers with the address of the code
 * corresponding to a particular C label.
 *
 * For more information, see the CCS documentation:
 * http://www.ccsinfo.com/downloads/ccs_c_manual.pdf
 *
 */

#ifndef __LC_ADDRLABELS_PIC_CCS_H__
#define __LC_ADDRLABELS_PIC_CCS_H__

/** \hideinitializer */
typedef void * lc_t;

#define LC_INIT(s) s = 0

#define LC_RESUME(s)				\
  do {						\
    if(s != 0) {				\
      goto_address(s);					\
    }						\
  } while(0)

#define LC_CONCAT2(s1, s2) s1##s2
#define LC_CONCAT(s1, s2) LC_CONCAT2(s1, s2)

#define LC_SET(s)				\
  do {						\
    LC_CONCAT(LC_LABEL, __LINE__):   	        \
    (s) = label_address(LC_CONCAT(LC_LABEL, __LINE__));	\
  } while(0)

#define LC_END(s)

#endif /* __LC_ADDRLABELS_PIC_CCS_H__ */


Below is an example of using Protothreads on PICKit2 board with 16F690 microcontroller. Compare to CCS RTOS tasks (#use rtos) Protothreads demonstrated significant generated code space improvement.



//
//
// author: Igor Lesik
//
// Example from http://eigenclass.org/hiki/threadring-with-protothreads
//
//
#include "PICKit2_app1.h"


#define LC_INCLUDE "lc-addrlabels-pic-ccs.h"
#include "../../../pt.h"
#include "../../../pt-sem.h"

// LEDs on PICKit2 board
#define LED1    PIN_C0
#define LED2    PIN_C1
#define LED3    PIN_C2
#define LED4    PIN_C3

#define toggle_led(pin, state) {if(state) output_high(pin); else output_low(pin);}

void switch_LED(int pin, int state){
   switch(pin){
   case 0: toggle_led(LED1, state); break;
   case 1: toggle_led(LED2, state); break;
   case 2: toggle_led(LED3, state); break;
   case 3: toggle_led(LED4, state); break;
   }
}

inline void setup()
{

   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_OFF);
   setup_spi(FALSE);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_oscillator(OSC_8MHZ);

   port_a_pullups(FALSE);

   enable_interrupts(INT_RTCC);
   enable_interrupts(GLOBAL);
}


#define NUM_THREADS (10)
#define SEED_TOKEN  (9)  // 0 <= N < NUM_THREADS


typedef struct pt pt_t;
typedef struct pt_sem pt_sem_t;

pt_t thread_context[NUM_THREADS];
int data[NUM_THREADS];
struct pt_sem mutex[NUM_THREADS];


PT_THREAD(thread(struct pt *pt, int id))
{
  static int token;
  static int r;
  static pt_sem_t* sem_ptr;

  PT_BEGIN(pt);
  while( 1 ) {
      sem_ptr=&mutex[id];
      PT_SEM_WAIT(pt, sem_ptr);
      token = data[id];
      r = (id + 1) % NUM_THREADS;
      if(token) {
          data[r] = token - 1;
          sem_ptr=&mutex[r];
          PT_SEM_SIGNAL(pt, sem_ptr);
      } else {
          switch_LED(id%4, 1); // switch LED on
          // GAME IS OVER, LED SEED_TOKEN%4 must be ON
      }

  }
  PT_END(pt);
}


void main()
{
   int i;
   pt_t* pt_ptr; pt_sem_t* sem_ptr;

   setup();

   data[0] = SEED_TOKEN % NUM_THREADS; // 0 <= N < NUM_THREADS

   for(i = 0; i < NUM_THREADS; i++) {
      sem_ptr=&mutex[i];
      PT_SEM_INIT(sem_ptr, 0);
      pt_ptr = &thread_context[i];
      PT_INIT(pt_ptr);
      switch_LED(i%4, 0); // switch all 4 LEDs off
      data[i]=data[0];
   }

   sem_ptr=&mutex[0];
   PT_SEM_INIT(sem_ptr, 1);

   while(1) {
      for(i = 0; i < NUM_THREADS; i++){
         pt_ptr = &thread_context[i];
         thread(pt_ptr, i);
      }
   }

}

Igor Lesik, 2008