High speed serial and port control for logic analysis

March 23, 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, 20133 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);

}

Primitive exception processing using macros

Ricky Bennett March 22, 2013 Coded in C
#define Try \
      jmp_buf ExceptionBuffer; \
      int ExceptionStatus = setjmp (ExceptionBuffer); \
      if (ExceptionStatus == 0)

#define Catch(Value)    else if (ExceptionStatus == (int) (Value))

#define Finally         else

#define Throw(Value) \
      { \
         int Result = (int) (Value); \
         if (Result != 0) { \
            longjmp (ExceptionBuffer, Result); \
         } \
      }

An example using a function to read the OCR register on an SD card:

uint32_t SDCard_OCR (void) {
   SDCard_ConfigSSP ( );
   Try {

      // The card is asked to send its OCR.  The OCR is
      // placed in module variable mOCRValue by the
      // function.
      Throw (SDCard_SendCommand (CMD58_READ_OCR));
   }

   // Error processing.
   Catch (SD_TIMEOUT) {
      GUI_SendMessage (SDCARD_TIMEOUT);
   }
   Catch (SD_NO_CARD) {
      GUI_SendMessage (SDCARD_NOT_FOUND);
   }
   Finally {   // Unknown SD card error
      Throw (SD_NO_CARD);   // Rethrow the exception
   }

   // Executed even if an exception thrown:
   SDCard_DeconfigSSP ( );
   return mOCRValue;   // 0xFFFFFFFF if error
}

Simple Bit Manipulation Macros

Stephen Friederichs March 12, 20134 comments Coded in C
/* Basic bit manipulation macros
   No one should ever have to rewrite these
*/

//Set bit y (0-indexed) of x to '1' by generating a a mask with a '1' in the proper bit location and ORing x with the mask.

#define SET(x,y) x |= (1 << y)

//Set bit y (0-indexed) of x to '0' by generating a mask with a '0' in the y position and 1's elsewhere then ANDing the mask with x.

#define CLEAR(x,y) x &= ~(1<< y)

//Return '1' if the bit value at position y within x is '1' and '0' if it's 0 by ANDing x with a bit mask where the bit in y's position is '1' and '0' elsewhere and comparing it to all 0's.  Returns '1' in least significant bit position if the value of the bit is '1', '0' if it was '0'.

#define READ(x,y) ((0u == (x & (1<<y)))?0u:1u)

//Toggle bit y (0-index) of x to the inverse: '0' becomes '1', '1' becomes '0' by XORing x with a bitmask where the bit in position y is '1' and all others are '0'.

#define TOGGLE(x,y) (x ^= (1<<y))

Delay for MSP430

March 12, 20132 comments Coded in C for the TI MSP430
void configureClocks();
void delay_ms(unsigned int ms);
void delay_us(unsigned int us);

void configureClocks()
{
     WDTCTL = WDTPW + WDTHOLD;          // Stop WDT
     BCSCTL1 = CALBC1_1MHZ;
     DCOCTL = CALDCO_1MHZ;

 }

void delay_us(unsigned int us)
{
	while (us)
	{
		__delay_cycles(1); // 1 for 1 Mhz set 16 for 16 MHz
		us--;
	}
}

void delay_ms(unsigned int ms)
{
	while (ms)
	{
		__delay_cycles(1000); 1000 for 1MHz and 16000 for 16MHz
		ms--;
	}
}

Matlab code to plot values from port in real time

March 12, 2013 Coded in Matlab
clc
clear all
close all
s1 = serial('COM26', 'BaudRate', 57600);
set(s1,'Terminator',35);
    fopen(s1);
V=[];
val='';
time=200;
count=0;
f=0;
 %   **********************************************************************
 %    Read Serial values
tic
tt=toc;
while(tt<time)
    val=fscanf(s1);
    tt=toc;
  
%########################################################################
 A=[];
 B=[];
 C=[];
 b=[];
 x=[];
 j=1;
 

     if(j<numel(val))
         while(val(j)~='$')
             a=val(j);
             b=[b,a];
             j=j+1;
         end
         A=[A,str2num(b)];
         j=j+1;
         b=[];
         while(val(j)~='*')
             a=val(j);
             b=[b,a];
             j=j+1;
         end
         B=[B,str2num(b)];
         j=j+1;
         b=[];
         while(val(j)~='#')
             a=val(j);
             b=[b,a];
             j=j+1;
         end
         C=[C,str2num(b)];
         i=j;
         b=[];
         f=f+i;
     end
     
x=[x,round(toc)];
if(~(isempty(A)&&isempty(B)&&isempty(C)))
    subplot(1,2,1)
    plot(x,A,'--rs','LineWidth',2,'MarkerEdgeColor','k','MarkerFaceColor','r','MarkerSize',5);
    hold on % if u want this in different graph remove hold on and add "figure" command before each graph;
    plot(x,B,'--rs','LineWidth',2,'MarkerEdgeColor','k','MarkerFaceColor','g','MarkerSize',5);
    grid on;
    subplot(1,2,2)
    plot(x,(B/(A+B))*100,'--rs','LineWidth',2,'MarkerEdgeColor','k','MarkerFaceColor','m','MarkerSize',5);
    hold on
    plot(x,C,'--rs','LineWidth',2,'MarkerEdgeColor','k','MarkerFaceColor','b','MarkerSize',5);
    grid on;
    pause(.01);
end

end
hold off
fclose(s1);

PID (Floating Point)

March 5, 2013 Coded in C
/*! \details This structure holds the data to run a
 * floating point PID loop.
 */
typedef struct{
	float max /*! \brief Max manipulated value */;
	float min /*! \brief Miniumum manipulated value */;
	float e /*! \brief Error value */;
	float i /*! \brief Integrator value */;
	float kp /*! \brief Proportional constant */;
	float ki /*! \brief Integrator constant */;
	float kd /*! \brief Differential constant */;
} pid_f_t;

/*! \details This function initializes the data in a PID structure.
 *
 */
void pid_init_f(pid_f_t * ptr /*! A pointer to the PID data structure */,
		float min /*! The manipulated variable's minimum value */,
		float max /*! The manipulated variable's maximum value */){
	memset(ptr, 0, sizeof(pid_f_t));
	ptr->min = min;
	ptr->max = max;
}

/*! \details This function updates the value of the manipulated variable (MV)
 * based on the current state of the PID loop.
 */
float pid_update_f(float sp /*! The set point */,
		float pv /*! The process variable */,
		pid_f_t * ptr /*! A pointer to the PID constants */){
	float temp;
	float e;
	float p;
	float manp;
	float tmpi;
	//get the error from the last call
	e = ptr->e;
	//calculate the new error (set point - present value)
	ptr->e = sp - pv;
	//use a temp variable for the integrator
	tmpi = ptr->i + ptr->e;
	//update the manipulated process variable
	manp = ptr->kp * ptr->e + ptr->ki * tmpi + ptr->kd * (ptr->e - e);
	//the integrator is only updated if the manipulated process is within range
	//otherwise the system will likely become unstable
	if ( (manp < ptr->max) && (manp > ptr->min) ){
		ptr->i = tmpi;
	} else if ( manp > ptr->max ){
		manp = ptr->max;
	} else if ( manp < ptr->min ){
		manp = ptr->min;
	}
	return manp;
}