EmbeddedRelated.com
Memfault Beyond the Launch

GPIO library

Niels Tjørnhøj-Thomsen March 23, 2013 Coded in C for the NXP LPC17xx series
//---------------------------------------------------------
// start of header file (gpio.h)
//---------------------------------------------------------

#if !defined(_GPIO_H_)
#define _GPIO_H_

typedef unsigned long dword;

typedef struct tGPIOpinTag
{
  dword dwReg;
  dword dwClr;
  dword dwSet;
  int iBit;
  dword dwFunction;
} tGPIOpin;

#define REG32                volatile dword *

//
// bit patterns for alternate pin functions.
//
#define PINFUNC_GPIO         ( 0x00000000 )
#define PINFUNC_ALT1         ( 0x55555555 )
#define PINFUNC_ALT2         ( 0xAAAAAAAA )
#define PINFUNC_ALT3         ( 0xFFFFFFFF )

//
// Active high and low works by swapping the SET and CLR register offsets round
//
#define GPIO_ACTIVE_L        FIO_SET_OFS, FIO_CLR_OFS
#define GPIO_ACTIVE_H        FIO_CLR_OFS, FIO_SET_OFS

//
// Start addresses of GPIO blocks of registers
//
#define LPC_GPIO_BASE        ( 0x2009C000UL )
#define LPC_GPIO0_BASE       ( LPC_GPIO_BASE + 0x00000 )
#define LPC_GPIO1_BASE       ( LPC_GPIO_BASE + 0x00020 )
#define LPC_GPIO2_BASE       ( LPC_GPIO_BASE + 0x00040 )
#define LPC_GPIO3_BASE       ( LPC_GPIO_BASE + 0x00060 )
#define LPC_GPIO4_BASE       ( LPC_GPIO_BASE + 0x00080 )

//
// Offsets to the direction register and PIN register
//
#define FIO_SET_OFS          ( 0x00000018 )
#define FIO_CLR_OFS          ( 0x0000001C )
#define FIO_DIR_OFS          ( 0x00000000 )
#define FIO_PIN_OFS          ( 0x00000014 )
#define FIO_MASK_OFS         ( 0x00000010 )

// Parameters for vGPIODDsetDirection
#define   PIN_IN             ( FALSE )
#define   PIN_OUT            ( TRUE )

//
// Macro for pin definition structure
//
#ifdef _INSIDE_GPIODD_
//
// this version is for use inside the gpio module
//
#define PINFUNC_DEF( name, port, bit, act, func )   \
  const tGPIOpin t##name = {                        \
    LPC_GPIO##port##_BASE,                          \
    GPIO_ACTIVE_##act,                              \
    bit,                                            \
    PINFUNC_##func                                  \
  };                                                \
  const tGPIOpin * const name = &t##name;
#else
//
// and this is how external modules see the pin definition
//
#define PINFUNC_DEF( name, port, bit, act, func )   \
  extern const tGPIOpin * const name;
#endif

//
// include the hardware pin allocations from another file
// (see example below)
//
#include "gpio_pindefs.h"

extern void vGPIODDsetPinFunction( const tGPIOpin * const psPin );
extern void vGPIODDsetPinDirection( const tGPIOpin * const psPin, 
                                    const bool boOutput );
extern void vGPIODDconfigurePin( const tGPIOpin * const psPin, 
                                 const bool boOutput );
extern void vGPIODDsetActive( const tGPIOpin * const psPin );
extern void vGPIODDsetInactive( const tGPIOpin * const psPin );
extern bool boGPIODDgetPin( const tGPIOpin * const psPin );

#endif // _GPIO_H_

//---------------------------------------------------------
// end of header file (gpio.h)
//---------------------------------------------------------

//---------------------------------------------------------
// start of example gpio_pindefs.h
//---------------------------------------------------------
 
#if !defined(_GPIO_PINDEFS_H_)
#define _GPIO_PINDEFS_H_

// LEDs
PINFUNC_DEF( GPIO_LED_GREEN,          1, 25, L, GPIO )

// USB interface
PINFUNC_DEF( GPIO_USB_VBUS,           1, 30, H, ALT1 )
PINFUNC_DEF( GPIO_USB_CONNECT,        2,  9, L, ALT1 )

// Serial Ports
PINFUNC_DEF( UART0_RX,                0,  3, H, ALT1 )
PINFUNC_DEF( UART0_TX,                0,  2, H, ALT1 )

PINFUNC_DEF( UART1_RX,                0, 16, H, ALT1 )
PINFUNC_DEF( UART1_TX,                0, 15, H, ALT1 )

// SPI port
PINFUNC_DEF( SPI_MOSI,                0,  9, H, ALT2 )
PINFUNC_DEF( SPI_MISO,                0,  8, H, ALT2 )
PINFUNC_DEF( SPI_SCK,                 0,  7, H, ALT2 )
PINFUNC_DEF( SPI_SSEL,                0,  6, L, ALT2 )

#endif // _GPIO_PINDEFS_H_

//---------------------------------------------------------
// end of example gpio_pindefs.h
//---------------------------------------------------------

//---------------------------------------------------------
// start of gpio.c
//---------------------------------------------------------

#define   _INSIDE_GPIODD_             ( 1 )
#include "gpio.h"

/* --------------------------------------------------------
   vGPIODDsetPinFunction
   ........................................................
   Description  : Set the pin connect block from a pin 
                  definition 

   Params : psPin - pin definition

   Returns : Nothing
   ----------------------------------------------------- */

void vGPIODDsetPinFunction( const tGPIOpin * const psPin )
{
  if ( psPin )
  {
    // Each PINSELXX register contains the settings 
    // for 16 port pins.  Each PINSEL register is 4 bytes 
    // above the previous one.  Base addresses for ports 
    // are 32 bytes apart.
    dword dwPinSel = LPC_PINCON_BASE + 
                     ((psPin->dwReg - LPC_GPIO0_BASE) / 4) + 
                     ((psPin->iBit / 16) * 4);
    dword dwMask = ( 0x00000003 << ( 2 * ( psPin->iBit % 16 ) ) );
    REG32 pdwPinSel = (REG32)dwPinSel;

    *pdwPinSel = ( *pdwPinSel & ~dwMask ) | 
                 ( psPin->dwFunction & dwMask );
  }
}

/* --------------------------------------------------------
   vGPIODDsetPinDirection
   ........................................................
   Description  : Sets the pin direction from a pin 
                  definition

   Params : psPin - pin definition
            boOutput - set to TRUE to turn the pin into an 
                       output

   Returns : Nothing
   ----------------------------------------------------- */

void vGPIODDsetPinDirection( const tGPIOpin * const psPin, 
                             const bool boOutput )
{
  if ( psPin )
  {
    if( boOutput )
    {
      (*((REG32) (psPin->dwReg + FIO_DIR_OFS))) |= ( 1 << psPin->iBit );
    }
    else
    {
      (*((REG32) (psPin->dwReg + FIO_DIR_OFS))) &= ~( 1 << psPin->iBit );
    }
  }
}

/* --------------------------------------------------------
   vGPIODDconfigurePin
   ........................................................
   Description  : Combination function to configure and set 
                  direction of GPIO pin

   Params : psPin - pin definition
            boOutput - set to TRUE to turn the pin into an 
                       output

   Returns : Nothing
   ----------------------------------------------------- */

void vGPIODDconfigurePin( const tGPIOpin * const psPin, 
                          const bool boOutput )
{
  if ( psPin )
  {
    vGPIODDsetInactive( psPin );
    vGPIODDsetPinFunction( psPin );
    vGPIODDsetPinDirection( psPin, boOutput );
  }
}

/* --------------------------------------------------------
   vGPIODDsetActive
   ........................................................
   Description  : Sets a pin to its active state

   Params : psPin - pin definition

   Returns : Nothing
   ----------------------------------------------------- */

void vGPIODDsetActive( const tGPIOpin * const psPin )
{
  if ( psPin )
  {
    // use the Set register to set a single bit
    (*((REG32) (psPin->dwReg + psPin->dwSet))) = ( 1 << psPin->iBit );
  }
}

/* --------------------------------------------------------
   vGPIODDsetInactive
   ........................................................
   Description  : Sets a pin to its inactive state

   Params : psPin - pin definition

   Returns : Nothing
   ----------------------------------------------------- */

void vGPIODDsetInactive( const tGPIOpin * const psPin )
{
  if ( psPin )
  {
    // use the Clr register to clear a single bit
    (*((REG32) (psPin->dwReg + psPin->dwClr))) = ( 1 << psPin->iBit );
  }
}

/* --------------------------------------------------------
   boGPIODDgetPin
   ........................................................
   Description  : Gets the current state of a pin

   Params : psPin - pin definition

   Returns : TRUE if the pin is in its active state, 
             FALSE otherwise.
   ----------------------------------------------------- */

bool boGPIODDgetPin( const tGPIOpin * const psPin )
{
  if ( psPin )
  {
    dword dwPins = *((REG32) (psPin->dwReg + FIO_PIN_OFS));

    if ( psPin->dwSet > psPin->dwClr )
    {
      dwPins = ~dwPins;
    }

    return ((dwPins & ( 1 << psPin->iBit )) != 0 );
  }
  else
  {
    return FALSE;
  }
}

//---------------------------------------------------------
// end of gpio.c
//---------------------------------------------------------

high resolution frequency counter implementation

Fabien Le Mentec March 23, 2013 Coded in C for the atmega328p
/* high resolution frequency counter implementation
*/

/* timer2 interrupt handler. timer1 is an extended
32 bits register (16 bits hard + 16 softs)
incremented once per:
1 / (fcpu / prescal) <=> prescal / fcpu
thus, it will overflow at:
2^16 * prescal / fcpu
on tim2 overflow, the interrupt handler is called
and stores the tim1 current value in tim1_cur_counter.
thus, the tim1 value integrated over the whole
tim1 period is:
(tim1_ovf_counter * 2^16) + tim1_cur_counter.
tim2_is_ovf is set to notify the application.
*/

static volatile uint8_t tim2_ovf_counter;
static volatile uint8_t tim2_is_ovf;
static volatile uint16_t tim1_cur_counter;

ISR(TIMER2_OVF_vect)
{
  if ((tim2_ovf_counter--) == 0)
  {
    /* disable tim1 before reading */
    TCCR1B = 0;
    tim1_cur_counter = TCNT1;

    /* disable tim2 */
    TCCR2B = 0;

    tim2_is_ovf = 1;
  }
}

/* timer2 interrupt handler. timer2 is a 8 bits counter
incremented by the input signal rising edges. since
8 bits are not enough to integrate, an auxiliary
register (tim2_ovf_counter) is updated on overflow.
tim2_ovf_counter is an 8 bits register, and will
overflow without any notice past 0xff.
*/

static volatile uint8_t tim1_ovf_counter;

ISR(TIMER1_OVF_vect)
{
  ++tim1_ovf_counter;
}

static void hfc_start(void)
{
  /* resolution: 1.907349 hz per tick */
  /* fmax: 500 khz */
  /* acquisition time: 0.524288 seconds */

  /* disable interrupts */
  TIMSK1 = 0;
  TIMSK2 = 0;

  /* reset stuff */
  tim1_ovf_counter = 0;
  tim1_cur_counter = 0;
  tim2_is_ovf = 0;

  /* 0x100 overflows make 16 bits */
  tim2_ovf_counter = 0xff;

  /* configure tim2
normal operation
prescaler 128
enable interrupt on overflow
*/
  TCNT2 = 0;
  TIMSK2 = 1 << 0;
  TCCR2A = 0;
  TCCR2B = 0;

  /* configure tim1
t1 pin (pd5) rising edge as external clock
*/
  DDRD &= ~(1 << 5);
  TCNT1 = 0;
  TIMSK1 = 1 << 0;
  TCCR1A = 0;
  TCCR1B = 0;

  /* start tim1, tim2 */
  TCCR1B = 7 << 0;
  TCCR2B = 5 << 0;
}

static uint8_t hfc_poll(void)
{
  return tim2_is_ovf;
}

static uint32_t hfc_wait(void)
{
  /* busy wait for tim1 to overflow. returns the resulting
16 bits counter, to be multiplied by the frequency
resolution (refer to hfc_start) to get the actual
frequency.
*/

  /* force inline, do not use hfc_poll */
  while (tim2_is_ovf == 0) ;

  return ((uint32_t)tim1_ovf_counter << 16) | (uint32_t)tim1_cur_counter;
}

static inline uint32_t hfc_start_wait(void)
{
  hfc_start();
  return hfc_wait();
}

static inline double hfc_to_hz(uint32_t counter)
{
  return 1.907349 * (double)counter;
}

Playing sound in DAC using simple 8051 MCU

March 23, 20134 comments Coded in C for the SiliconLabs 8051
/*System reads wav files by SPI from external memory . Samples read are sent to external DAC by other SPI to be played. It's nedeed ISR reception (not included) of SPI to manage info of this file. At the beginnig, this timing is not important, but after all info of wav file is read, timming is fitted to sampling time of wav to play.

This Timer ISR manages read samples by SPI_Memory and write in SPI_DAC in 8051 MCU. Because this MCU has only one SPI,the info transmited to DAC is managed by simple GPIOs */
void Timer2_ISR (void) interrupt 5
{     
   char desp=0;
   char temp=0;
        
   SFRPAGE_save = SFRPAGE;

   //! buffer to store 1 or 0 in bit content audio to convert	
   if(TF2H == 1)
   {
		/*When system starts, only reads from memory, to get wav header (sampletime, length, and because
		there are more than one wav file in memory,  the starting address of wav, In "configurating" the 
		timer period is not sampleTime*/
		if (configurating==TRUE)
		{
			SPI_Read_Memory(); /*it's nedeed ISR to manage recepcion*/
		}
		else
		{	
			/*pread counts samples of wav file reading*/
			if (pread<(audioLength+1))
			{	
				/*wavSampleRead is the last sample stored by SPI, taken in SPI ISR*/
				PCA0CPH0=wavSampleRead;
				desp=4;
				MOSI_SPI_SW=0;
				
				index++;
				if (index==256)
				{
					index=0;
				}
				CS_DAC=0; /*Chip Select of DAC. This SPI is controlled by GPIOs*/
				while (desp!=0)
				{
					CLK_SPI_SW=0;
					CLK_SPI_SW=1;
					desp--;
				}
					
				desp=0;
				while (desp!=8)
				{
					CLK_SPI_SW=0;
					
					if ((wavSampleRead&0x80)!=0)
					{
						MOSI_SPI_SW=1;
					}	
					else
					{
						MOSI_SPI_SW=0;
					}
					CLK_SPI_SW=1;
					desp++;
					wavSampleRead=wavSampleRead<<1;
				}
				CLK_SPI_SW=0;
				MOSI_SPI_SW=0;
				CLK_SPI_SW=1;
				CLK_SPI_SW=0;
				CLK_SPI_SW=1;					
				CLK_SPI_SW=0;
				CLK_SPI_SW=1;
				CLK_SPI_SW=0;
				CLK_SPI_SW=1;
				CLK_SPI_SW=0;
				CS_DAC=1;
				MOSI_SPI_SW=0;	
			
				/* starts new transmision on SPI to read new sample from memory*/
				SFRPAGE = ACTIVE_PAGE;
				SPI_transferData = NO_OK;
				SPI0DAT = TRANSFER_MEMORY_COMMAND;
				SFRPAGE = SFRPAGE_save;
				
				buffer_Index=0;
				pread++;
			}
			else 
			{  
				/*in configurating mode is stored a buffer with some samples. This buffer
				it's used to allow "clac" noises to reproduce the audioagain  (becuase the audio is 
				playing periodically. The audios are siren sounds, and the last sample must linked whit 
				the first sample, having a compled wave form)*/
				PCA0CPH0=buffer[buffer_Index];
				temp=buffer[buffer_Index];

				buffer_Index++;
				
				if (buffer_Index==1)
				{
					looping=1;
				}
				else if (buffer_Index==BUFFER_SIZE)
				{
					pread=BUFFER_SIZE+1;
				}	
					desp=4;
					MOSI_SPI_SW=0;
				
					CS_DAC=0;
					while (desp!=0)
					{
						CLK_SPI_SW=0;
						CLK_SPI_SW=1;
						desp--;
					}
					desp=0;
					while (desp!=8)
					{
						CLK_SPI_SW=0;
						if ((temp&0x80)!=0)
						{
							MOSI_SPI_SW=1;
						}	
						else
						{
							MOSI_SPI_SW=0;
						}
						//CLK_SPI_SW=0;
						CLK_SPI_SW=1;
						desp++;
						temp=temp<<1;
					}
					desp=4;
					while (desp!=0)
					{
						CLK_SPI_SW=0;
						MOSI_SPI_SW=0;
						//CLK_SPI_SW=0;
						CLK_SPI_SW=1;
						desp--;
					}
					CLK_SPI_SW=0;
					CS_DAC=1;
					MOSI_SPI_SW=0;
					CS_DAC=1;
				}
				if (pread==BUFFER_SIZE+1)
				{			
					/*First sample to begin the cicle*/
					SFRPAGE = ACTIVE_PAGE;
					SPI_transferData = NO_OK;
					SPI0DAT = TRANSFER_MEMORY_COMMAND;
					SFRPAGE = SFRPAGE_save;
				}
			}

		}
		TF2H = 0;
   }
}

Fast lookup plus interpolation computation of non-linear functions

March 23, 20131 comment Coded in C
// Fast integer arithmetic lookup+interpolation method for converting 
// barometric pressure readings to altitude readings.

// Each Lookup Table (LUT) entry is the altitude in centimeters above
// sea level, corresponding to an implicit pressure value, 
// calculated as [PA_INIT - 1024*LUTindex] in Pascals.
// The region of interest is approximately 460metres below sea level,
// to 10000 metres above sea level.

typedef signed long s32;
 
#define PZLUT_ENTRIES   80
#define PA_INIT         106956L
#define PA_DELTA        1024L

#define Z_LIMIT_LO   -99999L
#define Z_LIMIT_HI   99999L

const s32 gPZTbl[PZLUT_ENTRIES] = {
-45853,
-37662,
-29407,
-21087,
-12700,
-4245,
4279,
12874,
21540,
30279,
...  // values removed for brevity
959708,
984147,
1009345
};

// Calculates the altitude in centimeters above sea level, given the barometric
// sensor pressure reading in pascals. The nearest lower LUT index is computed.
// The altitude is then linearly interpolated from the corresponding altitude
// values at the lower and next higher LUT index. Computation is optimized by
// ensuring the difference between LUT entries are spaced by a power of 2, in
// this case 2^10 (1024), so no integer division is required.
// Returns the error values Z_LIMIT_LO or Z_LIMIT_HI if
// the pressure data exceeds the LUT index limits.

s32 sns_Pa2Cm(s32 pa)  {
   	s32 inx,pa1,z1,z2,z;
    
   	if (pa > PA_INIT) {  
      	z = Z_LIMIT_LO;  
      	}
   	else {
      	inx = (PA_INIT - pa)>>10;      
      	if (inx >= PZLUT_ENTRIES-1) {
         	z = Z_LIMIT_HI;
         	}
      	else {
         	pa1 = PA_INIT - (inx<<10);
         	z1 = gPZTbl[inx];
         	z2 = gPZTbl[inx+1];
         	z = z1 + (((pa1-pa)*(z2-z1))>>10);
         	}
      	}
   	return z;
   	}

Linear regression of samples in a circular buffer

March 23, 20132 comments Coded in C
// Linear regression of samples in a circular sample
// buffer. Uses only integer arithmetic, optimized for
// computation on 16bit microcontroller  with hardware 
// multiplier.  The linear regression computation is
// simplified considerably by subtracting out the rolling
// average of the buffer samples.
// This computation assumes the samples arrive at 
// regular intervals, and this sampling rate is known.
//   Usage : 
//   1. call lr_Init() to initialize gnLRDenominator,
//      gnNumSamples and gnSampleIndex
//   2. get first sample value and initialize gZBuffer 
//      with this value 
//   3. for each subsequent incoming sample 
//   	gZBuffer[gnSampleIndex] = lr_GetNewZSample();
//	gZAverage = lr_CalculateAverage(gZBuffer,gnNumSamples);
//	gSlope = lr_CalculateSlope(gZBuffer, gnNumSamples, gnSampleIndex, gZAverage);
//      gnSampleIndex++;
//	if (gnSampleIndex >= gnNumSamples) gnSampleIndex = 0;
//

typedef signed long    s32;

#define MAX_Z_SAMPLES   80
#define SENSOR_SAMPLES_PER_SEC	26L
#define MAX_SLOPE				2000L

#define CLAMP(x,min,max)       {if ((x) <= (min)) (x) = (min); else if ((x) >= (max)) (x) = (max);}

s32 gnLRDenominator;
int gnSampleIndex, gnNumSamples;
s32 gZBuffer[MAX_Z_SAMPLES];
s32 gZAverage;
s32 gSlope;

void lr_Init(int numSamples) {
	s32 zSample, sumT, sumT2;
	int inx;
	sumT = -(numSamples * (numSamples-1L))/2L;
	sumT2 = (numSamples * (numSamples-1L)*(2L*numSamples-1L))/6L;
	gnLRDenominator = (numSamples*sumT2) - (sumT*sumT);
	gnSampleIndex = 0;
	gnNumSamples = numSamples;
	zSample = lr_GetNewZSample(); // get a sample from the sensor
	inx = gnNumSamples;
	while (inx--) gZBuffer[inx] = zSample;  // fill the ZBuffer with first sample value
	}
 
 
s32 lr_CalculateAverage(s32* pZBuffer, int numSamples ) {
   int inx;
   s32 accumulator, average;
   inx = numSamples;
   accumulator = 0;
   while (inx--)  {
	  accumulator += pZBuffer[inx];
      }
   accumulator = (accumulator >= 0 ? accumulator +numSamples/2 : accumulator - numSamples/2); 
   average = accumulator/numSamples;  // rounded up average
   return average; 
   }

/// Linear regression of samples in buffer to calculate slope.

s32 lr_CalculateSlope(s32* pZBuffer, int numSamples, int currentSampleIndex, int zAverage)   {
   int inx,tRelative;
   s32 z, sumZT,slope;
   
   sumZT = 0;
   inx = numSamples;
   while (inx--)  {
      z = pZBuffer[inx] - zAverage;   // subtract out the average value to simplify the arithmetic
      tRelative = inx - currentSampleIndex; // time origin is the current sample in window
      if (tRelative > 0) {
         tRelative -= numSamples;
         }
      sumZT += ((s32)tRelative*z);
      }

   slope = (sumZT*(s32)(SENSOR_SAMPLES_PER_SEC*numSamples))/gnLRDenominator;
   CLAMP(slope,-MAX_SLOPE,MAX_SLOPE);
   return slope;
   }

High speed serial and port control for logic analysis

March 22, 2013 Coded in C for the Atmel AVR32
#ifndef TESTPOINTS_H
#define TESTPOINTS_H

#include "gpio.h"

class TpHw
{

public:

//===================================================================================
// TOG, CLR, SET macros provide a means to quickly clear, set or toggle a pin.
// Each macro compiles into a single machine instruction when optimized. 
// usage is: 
//			TOG(AVR32_PIN10);
//===================================================================================

	#define TOG(pin) {											\
	volatile avr32_gpio_port_t *gpio_pin = &AVR32_GPIO.port[pin >> 5];	\
		gpio_pin->ovrt = 1 << (pin & 0x1F); } 

	#define CLR(pin) {											\
	volatile avr32_gpio_port_t *gpio_pin = &AVR32_GPIO.port[pin >> 5];	\
		gpio_pin->ovrc = 1 << (pin & 0x1F); } 

	#define SET(pin) {											\
	volatile avr32_gpio_port_t *gpio_pin = &AVR32_GPIO.port[pin >> 5];	\
		gpio_pin->ovrs = 1 << (pin & 0x1F); } 
		
//===================================================================================
// TAG provides a means to insert static real time serial data to be displayed on a 
// logic analyzer. The TAG macro should be optimized for a baud rate that matches 
// the instruction cycle time of the compiler. Each letter compiles into 10 
// machine instructions when optimized. Use 8 data bits, no parity and one stop bit. 
// usage is:
//			TAG(AVR32_PIN10, 'H', 'E', 'L', 'L', 'O');
//===================================================================================

	// this macro builds the bit
	#define BT(pin, letter, bit) {											\
		volatile avr32_gpio_port_t *gpio_pin = &AVR32_GPIO.port[pin >> 5];	\
		if (letter & bit)													\
			gpio_pin->ovrs = 1 << (pin & 0x1F);								\
		else																\
			gpio_pin->ovrc = 1 << (pin & 0x1F); } 
 
	// this macro builds the letter
	#define LETTER(pin, letter) {		\
		BT(pin, letter, 0x00);			\
		BT(pin, letter, 0x01);			\
		BT(pin, letter, 0x02);			\
		BT(pin, letter, 0x04);			\
		BT(pin, letter, 0x08);			\
		BT(pin, letter, 0x10);			\
		BT(pin, letter, 0x20);			\
		BT(pin, letter, 0x40);			\
		BT(pin, letter, 0x80);			\
		BT(pin, 0xff, 0xff);			}
	 
	// these build the tag
	#define LETTER1(pin, letter)      LETTER(pin, letter)
	#define LETTER2(pin, letter, ...) LETTER1(pin, letter) LETTER1(pin, __VA_ARGS__)
	#define LETTER3(pin, letter, ...) LETTER1(pin, letter) LETTER2(pin, __VA_ARGS__)
	#define LETTER4(pin, letter, ...) LETTER1(pin, letter) LETTER3(pin, __VA_ARGS__)
	#define LETTER5(pin, letter, ...) LETTER1(pin, letter) LETTER4(pin, __VA_ARGS__)
	#define LETTER6(pin, letter, ...) LETTER1(pin, letter) LETTER5(pin, __VA_ARGS__)
	#define LETTER7(pin, letter, ...) LETTER1(pin, letter) LETTER6(pin, __VA_ARGS__)
	#define LETTER8(pin, letter, ...) LETTER1(pin, letter) LETTER7(pin, __VA_ARGS__)
	 
	// these calculate the number of arguments passed
	#define ARGS_N(x1, x2, x3, x4, x5, x6, x7, x8, N, ...) N
	#define RSEQ_N()         8, 7, 6, 5, 4, 3, 2, 1, 0
	#define NARG_N(...)      ARGS_N(__VA_ARGS__)
	#define NARG(...)        NARG_N(__VA_ARGS__, RSEQ_N())
	 
	// these build the macro to start with (T1 - T8) base on number of arguments
	#define PASTE(a, b)      a ## b
	#define XPASTE(a, b)     PASTE(a, b)
	#define T_(pin, M_, ...) M_(pin, __VA_ARGS__)
	#define TAG(pin, ...)    T_(pin, XPASTE(LETTER, NARG(__VA_ARGS__)), __VA_ARGS__) 

//=====================================================================================
// textOut provides a means to insert dynamic real time serial data to be displayed 
// on a logic analyzer. The code below should be optimized for a baud rate that matches 
// the instruction cycle time of the compiler by stuffing no ops where appropriate 
// to balance the bit output. Use 8 data bits, no parity and one stop bit. This 
// gives a character that should take 55 cycles and doesn't take any time to set up. 
// usage is:
//			TpHw::textOut(AVR32_PIN_PA22, "HELLO");
//=====================================================================================

	static void serialOut(uint32_t pin, uint8_t value)
	{
		volatile avr32_gpio_port_t *gpio_pin = &AVR32_GPIO.port[pin >> 5];
		volatile uint32_t out = 1 << (pin & 0x1F);

		// start bit
		gpio_pin->ovrc = out;
		asm("nop"); 
	               
		// bit 0
		if (value & (1 << 0))
		{
			gpio_pin->ovrs = out;	  
		} 
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}
	          
		// bit 1
		if (value & (1 << 1))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}
	    
		// bit 2
		if (value & (1 << 2))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}

		// bit 3
		if (value & (1 << 3))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}

		// bit 4
		if (value & (1 << 4))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}

		// bit 5
		if (value & (1 << 5))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}

		// bit 6
		if (value & (1 << 6))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}

		// bit 7
		if (value & (1 << 7))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}
	      
		// stop bit
		asm("nop");
		asm("nop");
		asm("nop");
		
		gpio_pin->ovrs = out;	  
	      
	}

	static void textOut(uint32_t pin, char *text)
	{
	   while (*text != 0)
	   {
		  serialOut(pin, *text++);
	   }
	}

};

#endif

ARM Cortex-M Bit Banding

March 22, 20136 comments Coded in C for the Cortex-M
#include <stdint.h>

//! @brief Convert word address + bit position to bitband address.
//!
//! @param address  Word address containing of bit
//! @param bit      Bit offset (0 = LSB,  31 = MSB)
//! @return Address of bit in bitband region.
volatile uint32_t* getBitBandAddress( volatile void* address, int bit )
{
    uint32_t addr = (uint32_t)address ;
    uint32_t word_band_base = addr & 0xf0000000 ;
    uint32_t bit_band_base = word_band_base | 0x02000000 ;
    uint32_t offset = addr - word_band_base ;

    return  (volatile uint32_t*)(bit_band_base + (offset * 32) + (bit * 4)) ;
}

--- EXAMPLES ---

// Example usage 1: 160 bit array
uint32_t physical_memory[5] = {0} ;
uint32_t* bit_array = getBitBandAddress( physical_memory, 0 ) ;
int bit_array_length = sizeof(physical_memory) * CHAR_BIT ;
for( i = 0; i < bit_array_len; i++ )
{
    bit_array[i] = rand() % 2;
}

// Example usage 2: STM32 USART1 Tx Int Enable/Disable
uint32_t* tx_intr_enable_bit_addr = getBitBandAddress( (&(USART1->CR1)), 7 ) ;

*tx_intr_enable_bit_addr = 0 ; // Disable Tx
*tx_intr_enable_bit_addr = 1 ; // Enable Tx

// Example usage 3: C++ reference version of example 2
uint32_t& tx_intr_enable_bit = *getBitBandAddress( (&(USART1->CR1)), 7 ) ;

tx_intr_enable_bit = 0 ; // Disable Tx
tx_intr_enable_bit = 1 ; // Enable Tx

Debug print trace macro

March 22, 2013 Coded in C
#if defined NDEBUG
    #define TRACE( format, ... )
#else
    #define TRACE( format, ... )   printf( "%s::%s(%d) " format, __FILE__, __FUNCTION__,  __LINE__, __VA_ARGS__ )
#endif

// Example usage
void example()
{
    unsigned var = 0xdeadbeef ;
    TRACE( "var=0x%x", var ) ;
}

// Resultant output example:
//    example.c::example(5) var=0xdeadbeef
//

Macros to get and set a bit field within a value

Ricky Bennett March 22, 2013 Coded in C
// Get a bit field from a value
#define GetField(Var, Mask, Shift) \
      (((Var) >> (Shift)) & (Mask))

// Set a bit field in a value
#define SetField(Var, Mask, Shift, Val) \
      (Var) = (((Var) & ~((Mask) << (Shift))) | (((Val) & (Mask)) << (Shift)))

Universal InfraRed remote pass-thru

Kenny Millar March 22, 2013 Coded in C for the Microchip PIC18
#define MAX_IR_INDEX 64
#define ESCAPE_COUNT 0x2200ul /* Could be reduced?*/

#pragma udata ircounts
unsigned int lcounts[MAX_IR_INDEX];
unsigned int hcounts[MAX_IR_INDEX];

#pragma	interrupt high_isr /*=section(".tmpdata")*/

void high_isr(void) {

	unsigned int l;
	unsigned int c;
	unsigned char th, tl;

	l = 0;
	TMR1H = 0; // Always write H before L
	TMR1L = 0;

	// Check that this is the INT0 interrupt
	// IRRX is int0
	if(INTCONbits.INT0IF)
	{
		// Disable the interrupt source
		INTCONbits.INT0IE = 0;

		// the code in the while needs to be balanced so that the ==0 and ==1 bits are the size number of cycles.
		while(lindex < MAX_IR_INDEX)
		{

			while(PORTBbits.RB0 == 0)
			{
				l++;
				if(l == ESCAPE_COUNT)
				{
					lcounts[lindex] = 0xffff;
					goto done;
				}
			}

			tl = TMR1L;
			th = TMR1H; // << 8 ;// Always read L before H
			lcounts[lindex++] = th * 256 + tl;

			l = 0;
			TMR1H = 0; // Always right H before L
			TMR1L = 0;
			while(PORTBbits.RB0 == 1)
			{
				l++;
				if(l == ESCAPE_COUNT)
				{
					hcounts[hindex] = 0xffff;
					goto done;
				}
			}

			tl = TMR1L;
			th = TMR1H; //<< 8 ;// Always read L before H
			hcounts[hindex++] = th * 256 + tl;

			l = 0;
			TMR1H = 0; // Always write H before L
			TMR1L = 0;

		}
	}

done:
	codeReady = 1;

	// reset interrupt status bit
	//INTCONbits.INT0IE = 1;
	//INTCONbits.INT0IF = 0;
	return;
}

void zeroIR() {
	unsigned char c;

	// Initialize the IR Count holders
	lindex = 0;
	hindex = 0;

	for(c = 0; c < MAX_IR_INDEX; c++)
	{
		lcounts[c] = 0xdead;
	}

	codeReady = 0;

	// reset interrupt status bit
	INTCONbits.INT0IE = 1;
	INTCONbits.INT0IF = 0;

}

void transmitIR(void) {

	unsigned char c;

	// First check that the code is valid by examining the first LCOUNT
	// if it is less than our defined threshold we will not transmit it.
	// The first lcount holds the low-going preamble, it must be a minimum length or it does not
	// represent a valid IR Transmission preamble.

	if(lcounts[0] < PREAMBLE_MINIMUM)
	{
		return;
	}
	
	sprintf(serTX, (CAST) "$%c%cIC%d,", id1, id2, lindex);
	rs485_puts(serTX);

	for(c = 0; c < lindex; c++)
	{
		sprintf(serTX, (CAST) "%04x,%04x, ", lcounts[c], hcounts[c]);
		rs485_puts(serTX);
	}

	sprintf(serTX, (CAST) "\n");
	rs485_puts(serTX);

}

Memfault Beyond the Launch