Monday, October 18, 2010

And now for something a little different.

Today’s post finds me just outside of Baltimore Maryland.  I am blessed to be attending a course that is directly applicable to what I do each and every day at work.  It is the Embedded Software Boot Camp, and so far it is looking like it will meet and exceed my expectations. 

I have been on training before, but it has always been either soft-skills development (the 7 habits of highly effective people), or application specific development (ECAD software training).  Since my time as a student at the University of Waterloo, I haven't been on a course that was specifically to refresh and advance my technical skills.  Today that has changed and I thank my employer for giving me the opportunity to attend this week of  training.

Over the last few months my ramblings have been pretty much one dimensional for the reason that my faith is and will always be the most important aspect in my life.  I don’t intend to stop writing about it, but I am a multi-dimensional being and I want to augment those discussions with some thoughts regarding other interests that make me uniquely me.  This week I want to write a bit about what I am being taught at the course.  Those that are technically minded might find this interesting, while those that are not might want to stop reading right about now.

As a point of interest, today I learned that there were approximately 12 billion processors sold in 2009.  Leaving aside what a huge number that is, the interesting statistic is that less then 2% of those actually end up in the PCs and Macs that we have on our desks.  The other 98% of the processors sold end up in embedded devices.   Actually it shouldn’t be that surprising when you take the time to compare the number of computers kicking around your house versus the number of embedded devices.  Let me entertain you with my list.

I have one general purpose computer with a Pentium 4 processor in it.

As for embedded devices, here is a list which I am sure is not exhaustive.  I have a mouse, keyboard, monitor, printer, USB flash drivers x4, external hard drive, internal hard drive, floppy drive, pen tablet, optical drives x3, sound card, graphics card, Ethernet card, router, modem, DSL modem, digital camera x2, external flash, digital video camera, microwave, stove, fridge, stereo, TV, DVD player, receiver, VCR, digital watch, clock radio x3, blackberry, cell phone, iPod x3, eBook reader, car stereo x2, car DVD player, car GPS, multiple automobile subsystems, daughters toys (TAG readers x2, toy computer x2, alphabet pal,  fridge letter toy, at least 3-4 other infant toys that make music and flash lights), smoke detectors x4, CO2 detector, garage door opener, garage door remote x2, TV remote control, DVD remote, receiver remote, VCR remote, digital picture frame, baby monitor base station, baby monitor receivers x2, guitar tuner, etc.

All of these things have one thing in common.  They are all electronic devices containing both a processor and embedded software.

Today in the course we covered 2 main topics.  First we reviewed the intricacies of the C programming language and second we learned how to write software which communicated directly with hardware.

Knowledge of the C programming language is pretty much an absolute requirement for anyone wanting to write embedded software.  This is in contrast to programming for desktop computers which have many languages to choose from.  Another difference with embedded programming is that you don’t write the software on the device you are programming.  When programming a PC, you write the software using the PC.  When writing software for a DVD player however, you can’t write the software on the DVD player, you have to use what is called a cross-compiler.  In other words, you write the program on a PC using a cross-compiler which doesn’t create a executable for the Intel X86 processor, instead it creates a file specific to the processor you are using on your embedded device.  This file can be then programmed into the chip on the embedded device.

The second thing we covered was hardware interfacing.  Specifically how to use the C language to communicate with hardware and control its operation.  The exercise today was to write a device driver for an ARM processor that communicated with a timer.  I present my solution here… 

/***********************************************************
DESCRIPTION:
- STR912F Device Driver

***********************************************************/
#include <stdint.h>
#include <includes.h>
#include "timer.h"

// Macros
#define BIT0  (1 << 0)
#define BIT1  (1 << 1)
#define BIT2  (1 << 2)
#define BIT3  (1 << 3)
#define BIT4  (1 << 4)
#define BIT5  (1 << 5)
#define BIT6  (1 << 6)
#define BIT7  (1 << 7)
#define BIT8  (1 << 8)
#define BIT9  (1 << 9)
#define BIT10 (1 << 10)
#define BIT11 (1 << 11)
#define BIT12 (1 << 12)
#define BIT13 (1 << 13)
#define BIT14 (1 << 14)
#define BIT15 (1 << 15)

// Type definitions
typedef struct
{
   uint16_t IC1R;   // Input Capture Register 1
   uint16_t const pad1;
   uint16_t IC2R;   // Input Capture Register
   uint16_t const pad2;
   uint16_t OC1R;   // Output Compare Register 1
   uint16_t const pad3;
   uint16_t OC2R;   // Output Compare Register 2
   uint16_t const pad4;
   uint16_t CNTR;   // Counter Register
   uint16_t const pad5;
   uint16_t CR1;    // Control Register 1
   uint16_t const pad6;
   uint16_t CR2;    // Control R7egister 2
   uint16_t const pad7;
   uint16_t SR;     // Status Register
   uint16_t const pad8;
}TIMER_t;

typedef struct
{
   uint32_t CLKCNTR;
   uint32_t PLLCONF;
   uint32_t SYSSTATUS;
   uint32_t PWRMNG;
   uint32_t ITCMSK;
   uint32_t PCGR0;
   uint32_t PCGR1;
   uint32_t PRR0;
   uint32_t PRR1;
   uint32_t MGR0;
   uint32_t MGR1;
   uint32_t PECGR0;
   uint32_t PECGR1;
   uint32_t SCR0;
   uint32_t SCR1;
   uint32_t SCR2;
   // Other stuff I don’t care about
}SCU_t;

// Public Prototypes
void timer_init(void);
void timer_oneshot(uint16_t ms);
int timer_done_yet(void);

// Private Declarations
// Declare a read_only pointer to a volatile Timer structure
//   and set the pointer equal to the address 0x58002000
static TIMER_t volatile * const pTimer =
   (TIMER_t *) 0x58002000;
#define Timer (*pTimer)

// Declare a read_only pointer to a volatile System Control
//   Unit structure and set the pointer equal to the address
     0x58002000
static SCU_t volatile * const pSCU = (SCU_t *) 0x5C002000;
#define Scu (*pSCU)

/***********************************************************
FUNCTION: timer_init

DESCRIPTION:
- Initialize timer hardware

PARAMETERS:
- None

RETURNS:
- Void
***********************************************************/
void timer_init(void)
{
   Timer.CR1 &= (~BIT15);  // Stop the timer
   Timer.CR1 |= BIT0;      // Set the External Clock Enable
   Scu.SCR1 = 48000;       // Set the clock divisor
}

/***********************************************************
FUNCTION: timer_oneshot

DESCRIPTION:
- Starts the one shot timer

PARAMETERS:
- ms, number of milliseconds to run the timer for

RETURNS:
- Void
***********************************************************/
void timer_oneshot(uint16_t ms)
{
   uint16_t target;

   Timer.CR1 &= (~BIT15);        // Stop the timer
   Timer.CNTR = 1;               // Reset the counter
   Timer.SR &= (~BIT14);         // Reset the Compare Flag

   /* The PLL Configuration Register sets a master clock
      of 96 MHz.  This 96 MHz clock is divided by 48,000
      using SCU Configuration
Register 1, therefore the
      timer input clock is running at 2 KHz, so the number
      of clock ticks is 2x the desired number of
      milliseconds. 
Also, resetting the counter yields a
      bizarre initial value of 0xFFFC.  We have to adjust
      our target count to compensate for this.
   */

   if(ms > 0x8000)
   {
      // The number of ms requested was too large
      // set to maximum
      target = 0xFFFB;
   }
   else
   {
      target = 2 * ms;

      if(target == 0)
      {
         // Setup the timer to be minimum = 0.5ms
         target = 0xFFFD;
      }
      else if(target == 2)
      {
         // 1 ms delay requested; handle this special case.
         target = 0xFFFE;
      }
      else
      {
         // Set the timer compare value
         target =- 4;  
      }
   }
   Timer.OC1R = target;

   // Start the timer
   Timer.CR1 |= BIT15;          
}

/***********************************************************
FUNCTION: timer_done_yet

DESCRIPTION:
-  Checks to see if the one-shot timer is still running

PARAMETERS:
- None

RETURNS:
- 0 if the timer is still running
- 1 if the timer is done
***********************************************************/
int timer_done_yet(void)
{
   // If bit 14 (OCF1) is set we have reached the count
   if(Timer.SR & BIT14) 
   {
      return 1;
   }
   else
   {
      return 0;
   }
}

No comments: