ON 20110709@10:22:24 PM at page: On a web page you were interested in at: http://techref.massmind.org/Techref/language/ccpp/iomacros-starlino.htm# James Newton[JMN-EFP-786] edited the page. Difference: http://techref.massmind.org/techref/diff.asp?url=\Techref\language\ccpp\iomacros-starlino.htm&version=0 ON 20110709@10:28:22 PM at page: On a web page you were interested in at: http://techref.massmind.org/Techref/language/ccpp/iomacros-starlino.htm#40733.9363657407 James Newton[JMN-EFP-786] Says I started working with MCUs using Basic Stamps 2. My first programs where written in PBASIC. I enjoyed the simplicity of this language but soon moved on to C. One particular thing that I was missing in the C enviroment was the ability to define a pin at the begining of the program and then perform various operations on it. (Make it an input or output, toggle the pin, read its level, output a high or low signal). PIC Microcontrollers make use of several registers to perform these operations, and if you change your pin you need to update all your lines of code that perform these operation.
To illustrate let's take a simple example. Let's say you have a button and a led, you'd like to create a simple application that will turn on the led when a momentary switch is pressed. The hardware setup will be very simple: the led is connected to an output pin of MCU (with a resitor in series to avoid burning the led); the switch is connected to another pin with an optional external pull-up resistor (some PIC and AVR can use an internal pull-up). Please note when the switch is pressed it will output 0 (LOW) and when it is not pressed it will output 1 (HIGH) due to the pull-up resistor.
[VDD 5V] | [RESISTOR 10K] | P1 —-[SWITCH] — [GND] P2 —-[RESISTOR ~ 200Ohm] —[LED]—[GND]Now here is the PBASIC code:
pinSwitch PIN 1 pinLed PIN 2 INPUT pinSwitch OUTPUT pinLed main: IF pinSwitch = 0 THEN HIGH pinLed ELSE LOW pinLed ENDIF GOTO mainIf you ever need to change the pins, let's say you want to swap the pins for led and switch all you need to change is the first two lines of code:
pinSwitch PIN 2 pinLed PIN 1All the remaining PBASIC code remains the same.
Now let's look at the C18/C30 code for the PIC microcontroller. Hardware setup is similar except we're going to use the internal pull-up.
RA2—-[SWITCH] — [GND] RB5 —-[RESISTOR ~ 200Ohm] —[LED]—[GND]To accomplish same thing we'd have to write something like this in C:
TRISAbits.TRISA2 = 1; //make switch pin an input WPUAbits.WPUA2 = 1; //turn on the pull-up TRISBbits.TRISB5 = 0; //make led pin an output while(1){ PORTBbits.RB5 = ! PORTAbits.RA2; }Now if you would need to swap the pins you'd have to update pretty much every line of C code. This is very inconvenient !!! This is why I came up with following macros for C18/C30 :
//——————————————————————— // MACROS FOR EASY PIN HANDLING IN PIC C18/C30 //——————————————————————— #define _TRIS(pin) pin(_TRIS_F) #define _TRIS_F(alpha,bit) (TRIS ## alpha ## bits.TRIS ## alpha ## bit) #define _PORT(pin) pin(_PORT_F) #define _PORT_F(alpha,bit) (PORT ## alpha ## bits.R ## alpha ## bit) #define _LAT(pin) pin(_LAT_F) #define _LAT_F(alpha,bit) (LAT ## alpha ## bits.LAT ## alpha ## bit) #define _WPU(pin) pin(_WPU_F) #define _WPU_F(alpha,bit) (WPU ## alpha ## bits.WPU ## alpha ## bit) //——————————————————————— // USAGE //——————————————————————— #define pinSwitch(f) f(A,2) //Switch, INPUT , pin RA2 #define pinLed(f) f(B,5) //Led, OUTPUT , pin RB5 _TRIS(pinSwitch) = 1; //make pin an input _WPU(pinSwitch) = 1; // turn on internal pull-up _TRIS(pinLed) = 0; // make pin an output while(1){ _PORT(pinLed) = ! _PORT(pinSwitch) }This code is much better. If you need to swap the pins all you need to do is update two lines of code:
#define pinSwitch(f) f(B,5) //Switch, INPUT, pin RB5 #define pinLed(f) f(A,2) //Led, OUTPUT, pin RA2The rest of the code remains the same. The only confusing thing in this code might be the macro definitions. I have arrived at them through many trials and errors. Each pin is defined as a macro function with a single parameter f – which is another macro function which we'd like to apply to this pin. Possible values for f are PORT_F , TRIS_F, LAT_F , WPU_F.
Here is an example how compiler interprets these statements:
_PORT(pinLed) => pinLed (PORT_F) => PORT_F( B, 5) => PORTBbits.RB5Now moving on to AVR controllers. I am using gcc-avr compiler (Windows version). I came up with following macros that seem to work in this compiler:
/* BASIC STAMPS STYLE COMMANDS FOR ATMEL GCC-AVR Usage Example: ———————————————– #define pinLed B,5 //define pins like this OUTPUT(pinLED); //compiles as DDRB |= (1<<5); HIGH(pinLed); //compiles as PORTB |= (1<<5); ———————————————– */ //these macros are used indirectly by other macros , mainly for string concatination #define _SET(type,name,bit) type ## name |= _BV(bit) #define _CLEAR(type,name,bit) type ## name &= ~ _BV(bit) #define _TOGGLE(type,name,bit) type ## name ^= _BV(bit) #define _GET(type,name,bit) ((type ## name >> bit) & 1) #define _PUT(type,name,bit,value) type ## name = ( type ## name & ( ~ _BV(bit)) ) | ( ( 1 & (unsigned char)value ) << bit ) //these macros are used by end user #define OUTPUT(pin) _SET(DDR,pin) #define INPUT(pin) _CLEAR(DDR,pin) #define HIGH(pin) _SET(PORT,pin) #define LOW(pin) _CLEAR(PORT,pin) #define TOGGLE(pin) _TOGGLE(PORT,pin) #define READ(pin) _GET(PIN,pin)As a bonus, below are some macros for calculating MIN/MAX or translating a value to a different range, something similar to the map() function that is already built-in into Arduino.
#define MIN(A,B) (((A)<(B)) ? (A) : (B) ) #define MAX(A,B) (((A)>(B)) ? (A) : (B) ) #define PUT_IN_RANGE(V,VMIN,VMAX) MAX(VMIN,MIN(VMAX,V)) #define MAP_TO_RANGE(V,VMIN0,VMAX0,VMIN1,VMAX1) ( (VMIN1) + ( (V) – (VMIN0) ) * ( (VMAX1) – (VMIN1) ) / ( (VMAX0) – (VMIN0) ) )Now , what do you do when you need your pin to actually be stored as a variable (it updates at run-time, or you might want to pass it as a parameter to a function). Let's say you would like to have 4 pins and perform some actions on those pins in bulk as well as define those pins are defined at run time ? The answer is pointers. Here I will illustrate with an example for C30 that you can adapt to your needs:
volatile unsigned int * tris_ptr[4]; unsigned char* pin_bit[4]; //define our pins as RA2 , RA3 , RB5 , RB7 at run time tris_ptr[0] = &TRISA; pin_bit[0] = 2; //RA2 tris_ptr[1] = &TRISA; pin_bit[1] = 3; //RA3 tris_ptr[3] = &TRISB; pin_bit[2] = 5; //RB5 tris_ptr[4] = &TRISB; pin_bit[3] = 7; //RB7 int i; //perform bulk operations on pins for(i=0;i<4;i++) if(i % 2){ (*tris_ptr[i]) |= 1<<tris_bit[i]; //set bit, make odd pins INPUT else (*tris_ptr[i]) ^= !(1U<<tris_bit[i]); //clear bit, make even pins OUTPUTIn the same way you could define port_ptr[] , lat_ptr[] , wpu_ptr[] arrays and perform operations on those ports. Even more smarter would be to group all these in a structure, and then create a single array of these structures. Using macros you could simplify assignment to these arrays in one simple statement. (I leave this as a homework for you) !
I hope you'll find this article useful. If you have any comments or suggestions do not hesitate to leave a comment below !
Happy coding !
//starlino//
This code copyright Starlino Electronics 2011. Used by permission with link to original content here: http://www.starlino.com/port_macro.html