Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT / Arduino

Communicating with Arduino UNO from a C# Program to Control the 16 Bits Timer

4.84/5 (7 votes)
15 Jan 2014CPOL6 min read 38K   2.4K  
This article illustrates how to send command to an Arduino UNO board from an USB serial port and control the wave from of the 16 bits timer/counter

Introduction

In a previous article I presented a simple library to control a stepper motor with the Arduino UNO board. In this article I demonstrate another aspect of the Atmel Micro-controller that powers the Arduino board: its 16 bits timer. The Atmel chip integrates a very powerful 16 bits timer that can be used for many purposes. One of the features is that it can be used to generate a wave form on PIN9 and 10 of the Arduino board and trigger an internal interruption on different conditions.

In order to understand all the possibilities of the 16 bits Timer of the Arduino I would advise you to read the specifications of the Atmel Chip.

The following demo shows how to configure the 16 bits Timer to generate a square wave signal on the PIN9/10 and send some command to control the frequency of the wave.

The 16 bits timer of the Atmega 328

The 16 bits timer of the Atmel chip can be used to generate a square wave signal on the PIN9/10 of the Arduino board and raise an interruption every time the counter reaches a given value. We set the PIN signal to change every time the counter value is reached.

The period between two interruption is given by the following formula, it is also the half period of the output signal on PIN9/10:

Poc = 2 * N * (1 + OCR) / Fclk

where:

  • N is the a counter divider (prescalar)
  • OCR is the counter value
  • Fclk is the clock frequency of the chip (16Mhz for the Arduino UNO)

For a given period (Poc) the value of the counter is given by the following formula:

OCR = (Poc * Fclk) / (2 * N) - 1

In this example I have chosen N = 256, so the formula for the counter is:

OCR = Poc * 31250 - 1, with Poc expressed in seconds.

The counter can be programmed using few registers:

  • Output Compare Registers (OCR1A/B) to set the counting value (16 bits)
  • Timer/Counter Control Registers (TCCR1A/B/C) to program the counter (8 bits)
  • Timer Interrupt Mask Register (TIMSK1) to control the interruption

Programming the Timer/Counter:

TCCR1A - Timer/Counter1 Control Register A

7 6 5 4 3 2 1 0
COM1A1 COM1A0 COM1B1 COM1B0 - - WGM11 WGM12
0 1 0 1     0 0

TCCR1B - Timer/Counter1 Control Register B

7 6 5 4 3 2 1 0
ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10
0 0   0 1 1 0 0

TCCR1C - Timer/Counter1 Control Register C

7 6 5 4 3 2 1 0
FOC1A FOC1B - - - - - -
0 1            

Compare Output Mode, non-PWM is set to Toggle OC1A/OC1B on Compare Match. Waveform generation is set to CTC, with TOP value compare to OCR1A and update of OCR1x is immediate.

Clock Select bits are set to 100 which divides the clock by 256 and finally the Force Output Compare for channel B is set to 1 so the state of PIN10 is always the opposite of PIN9.

The TimerCounter1 library is a very simple one, it is not intended to provide a complete Timer library but just to set-up the wave generator mode for the demo. This code was taken from the CmdrArduino library which uses the Arduino to create a DCC control station for model rail road.

C++
void TimerCounter1::setup_wave_generator(unsigned int counter)
{
  // Configure PortB 1 and 2 as output (PIN9, PIN10 of the Arduino board)
  DDRB |= (1<<DDB1) | (1<<DDB2);
    
  // Configure timer1 in CTC mode, for waveform generation, set to toggle OC1A, OC1B, 
  // at /256 prescalar, interrupt at CTC
  TCCR1A = (0<<COM1A1) | (1<<COM1A0) | (0<<COM1B1) | (1<<COM1B0) | (0<<WGM11) | (0<<WGM10);
  TCCR1B = (0<<ICNC1)  | (0<<ICES1)  | (0<<WGM13)  | (1<<WGM12)  | (1<<CS12)  | (0<<CS11) | 
           (0<<CS10);
    
  //Whenever we set OCR1A, we must also set OCR1B, or else pin OC1B will get out of sync with OC1A!
  OCR1A = OCR1B = counter; 
  TCNT1 = 0; //get the timer rolling (not really necessary? defaults to 0. Just in case.)
    
  //finally, force a toggle on OC1B so that pin OC1B will always complement pin OC1A
  TCCR1C |= (1<<FOC1B);
}

void TimerCounter1::enable_match_interrupt()
{
  //enable the compare match interrupt
  TIMSK1 |= (1<<OCIE1A);
}

The most important library in this simple demo is the Command library, it processes the command bytes sent to the sketch from the serial line to execute a command. The Command class is an abstract class that must be implemented represent an actual set of command. In this demo the PC application sends simple commands to control the period of the wave generator.

C++
namespace core
{
  /**
   * The class Command is designed to wrap a simple command represented by an array
   * of 20 bytes maximum. This is just an illustration of a command mechanism.
   * Commands are intended to be transmitted on a serial port and received byte per byte.
   * A command starts with a 0xF0 and ends with a 0xFF. The 0xFF value must not appear within 
   * the command as it would be interpreted as a command END.
   */
  class Command
  {
  protected:
    static const byte MAX_DATA = 20;
    byte commandData[MAX_DATA];
    bool ready;
      
    virtual int extractCmd(byte* cmd);   
    virtual bool executeCmd(void) = 0;
      
  private:
    int idx;
            
  public:
    Command() : idx(0), ready(false)
    {
    }
            
    /**
     * Adds a character to the command
     * @param item Command item
     */
    bool add(byte item);
        
    /** 
     * @return true if the command is complete
     */
    bool isReady(void);
            
    /**
     * Get the command
     * @param command Pointer to the command to be returned
     * @return true if the command is complete
     */
    bool getCommand(byte* command);
            
    /**
     * Clear the current command
     */
    void clear(void);

    /** 
     * Execute the current command
     * @return true if executed, false otherwise
     */
    bool execute(void);

    static const byte START = 0xF0;
    static const byte END = 0xFF;
  };
}

This class has one pure virtual method that must be implemented by any class that inherits from it. This method interprets the command bytes that are given to the class and execute the corresponding command.

This class works in the following manner:

  • The main application receives bytes on the serial port
  • Each byte has to be added to the command when received using the add() method
  • The application must check the isReady() method which returns true when a command is ready

A command is represented by an array of bytes that starts with 0xF0 and ends with 0xFF. The bytes in between those markers are the command. A command can contain a 0xF0 value but it can't contain a 0xFF as 0xFF is interpreted as a end of command. This is a limitation but bare in mind that this is just a simple demo ... Another limit is that the command buffer is 20 bytes including the START and END delimiter.

C++
bool core::Command::add(byte item)
{
  bool ret = true;
  if (idx == 0 && item == START)
  {
    commandData[idx++] = item;
  }
  else if (item == END && idx > 0 && idx < MAX_DATA) 
  {
    commandData[idx++] = item;
    ready = true;
  }
  else if (!ready && idx > 0 && idx < MAX_DATA)
  {
    commandData[idx++] = item;
  }
  else
  {
    clear();
    ret = false;
  }
  
  return ret;
}

int core::Command::extractCmd(byte* cmd)
{
  int ret = -1;
  byte* ptrCmd = commandData;
  
  if (*(ptrCmd++) == START)
  {
    ret = 0;
    while(*ptrCmd != END && ret < (MAX_DATA - 1))
    {
      *(cmd++) = *(ptrCmd++);
      ++ret;
    }
  }
  
  return ret;
}

An improvement to the process would be to replace the polling in isReady() with a callback that would be invoked when a command is ready.

The application defines the following commands:

  • 'a': sets the period to 1s
  • 'b': sets the period to 1/2s
  • 'c': sets the period to 1/4s
  • 'p<bcd period>': sets the period to a value given in BCD. (Binary Code Decimal)

The code of the class that processes those commands is given below.

C++
/**
 * This class implements the class Command to process simple commands
 * to control the counter value of a Timer
 */
class TimerCTC_Cmd : public Command
{
private:
  static const byte PERIOD_CMD = 'p';
  static const byte ONE_SEC_CMD = 'a';
  static const byte HALF_SEC_CMD = 'b';
  static const byte QUATER_SEC_CMD = 'c';
  unsigned int period;
                
public:
  TimerCTC_Cmd()
  {
  }
  
  /**
   * Gets the period extracted from a command
   */  
  unsigned int getPeriod();

private:    
  void ProcessPeriodCommand(byte* cmd);

protected:
  /** 
   * Execute the current command
   */
  virtual bool executeCmd(void);
};

/**
 * The executeCmd of the TimerCTC_Cmd class extract a 16 bits value 
 * from the command that can be used to change a Timer counter given 
 * a prescalar of 256
 */
bool TimerCTC_Cmd::executeCmd(void)
{
  bool ret = false;
  byte command[10];
  int cmdLen = 1;

  cmdLen = extractCmd(command);
  if (cmdLen != -1)
  {
    switch (*command)
    {
      case ONE_SEC_CMD:  // Set period to 1 second
      {
        period = 31249;
        ret = true;
    break;
      }

      case HALF_SEC_CMD:  // Set period to 1/2 second
      {
        period = 15624;
        ret = true;
    break;
      }

      case QUATER_SEC_CMD:  // Set period to 1/4 second
      {
        period = 7812;
        ret = true;
    break;
      }
      
      case PERIOD_CMD:  // Set the period to a a given value (0 to 65535)
      {
    ProcessPeriodCommand(command);
        ret = true;
    break;
      }
    }
  }

  return ret;
}

/**
 * Process the period command. The value is given in BCD format
 * @param cmd Command bytes
 */
void TimerCTC_Cmd::ProcessPeriodCommand(byte* cmd)
{
  period = -1;
  if (cmd[0] == 'p')
  {
    Converter::BCDValue bcd;
    for (int n = 0; n < sizeof(bcd.value); bcd.value[n] = cmd[1 + n++]);

    period = Converter::BCD2bin(bcd);
  }
}

Demo application: Send a command to the Arduino sketch to change the period of the wave generator

Hardware configuration

In order to run this sketch and see something you'll have to wire the following components

  • PIN9: a LED and its limiting resistor (220 to 470 ohms)
  • PIN10: a LED and its limiting resistor
  • PIN13: a LED or use the test LED of the board

Image 1

The sketch running in the Arduino is a very simple application that receives bytes sent on the serial port and pass them to the TimerCTC_Cmd class to process them. The LEDs on PIN9 and PIN10 are going to blink alternately at the given frequency. When PIN9 is high, PIN10 will be low and son on.

It contains as well a ISR (Interrupt Service Routine) that is called by the Timer1 every time the counter is matching the predefined value of OCR1A. This ISR is going to change this value according the command sent to the TimerCTC_Cmd using the method getPeriod().

The code is given below.

C++
#include <TimerCounter1.h>
#include <command.h>
#include <Converter.h>
#include "TimerCTC_Cmd.h"

#define HALF_SECOND    15624
#define    QUATER_SECOND    7812
#define ONE_SECOND      31249
#define BAUD_RATE       38400

volatile unsigned int cntr;
volatile unsigned int currcntr;
volatile byte state;

/**
 * Interrupt service routine called on every matched value of OCR1A
 * Use this routine to change the current OCR1Aand OCR1B value to adjust the 
 * counter period
 * PIN13 is set to blink at half the period of PIN9/10
 */
ISR(TIMER1_COMPA_vect)
{
  static volatile unsigned long counter = 0;
  
  // The original code uses if (PINB & (1 << PINB1) to check if the PIN is
  // low but it is apparently a bug
  if (!(PINB & (1 << PINB1)))
  {
    if (cntr != currcntr)
    {
      OCR1A = OCR1B = cntr;
      currcntr = cntr;
    }
  }
  
  if (counter % 2 == 0)
  {
    state = 1 - state;
    digitalWrite(13, state);
  }
  ++counter;
}

TimerCTC_Cmd command;

void setup() {
  Serial.begin(BAUD_RATE);
  TimerCounter1::setup_wave_generator(HALF_SECOND);
  TimerCounter1::enable_match_interrupt();  
  cntr = HALF_SECOND;
  currcntr = HALF_SECOND;
  pinMode(13, OUTPUT);
  state = 1;
}

/**
 * The main loop listens to characters on the default Serial port.
 * It passes the bytes read to the TimerCTC_Cmd command class.
 * When a command is complete, it is executed.
 * The command purpose is to change the period of the Timer1. This is 
 * done using the cntr global variable used by the ISR to change the 
 * timer OCR1A and OCR1B values.
 */
void loop() {
  if (Serial.available() > 0)
  {
    int inByte = Serial.read();
    command.add(inByte);
    if (command.isReady())
    {
      if (command.execute())
      {
        cntr = command.getPeriod();
      }
      
      command.clear();
    }
  }
}

In order to send commands to the Arduino we need a simple application. A .NET application can easily do the job.

Image 2

This application connects to the Arduino opening a serial channel on the COM port of the Arduino. By default the half period of the wave generator is set to 1/2 second. You can adjust this value with the buttons or the slider control or the numeric text box. The value you type is not in ms but is the actual value written to the OCR1A counter.

The following extension converts a ushort value into its BCD equivalent.

C++
public static class UintExtension
{
    /// <summary>
    /// Gets the BCD value of the given ushort parameter
    /// </summary>
    /// <param name="value">Value to convert</param>
    /// <returns>BCD bytes</returns>
    public static byte[] ToBCD(this ushort value)
    {
        byte[] bcdNumber = new byte[3];
        for (int n = 0; n < 3; n++)
        {
            bcdNumber[n] = (byte)(value % 10);
            value /= 10;
            bcdNumber[n] |= (byte)(value % 10 << 4);
            value /= 10;
        }

        return bcdNumber;
    }
}

Conclusion and Point of Interest

This is the second article I write about the Arduino. In this one I explored the usage of the Timer1 to generate a waveform and also communication with a PC. This is because I have in mind to use the CmdrArduino library, understand how it works and extend it to build a DCC control station that can receive commands from a PC.

This articles demonstrates two main features of the board, how powerful the built-in timer are and how easily you can communicate with the board.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)