When getting started in embedded programming, GPIO (viz. General Purpose Input Output) pins are one of the first things played with. Its also quite evident that the most popular “hello world” program in embedded systems programming is Blinky i.e a LED connected to pin on the Microcontroller that keeps blinking. The use of GPIO is not limited to driving LEDS but can be also use for reading digital signal , generating triggers for external components , controlling external devices and what not. In this tutorial we see how to use and program GPIO Pins for lpc214x ARM 7 microcontrollers from NXP/Philips.
Before getting into this you need to have basic understanding of Binary and Hexadecimal system and Bitwise operations in C.
*=>Guide to Binary and Hexadecimal system is @ Hexadecimal and Binary Number System basics for Embedded Programming.
*=>Tutorial for Bitwise Operations in C is @ Tutorial : Embedded programming basics in C – bitwise operations.
I’ll use lpc2148 MCU (having 32-bit ARM 7 CPU) for explanation and programming examples. The Programs and Register names that I have shown are used in KEIL. You can download KEIL UV4 from here. If you are using a different IDE/Compiler then you’ll need to change the Register Names as required. Also do note that Integer Data-Type i.e. an ‘int’ is always 32 bits in KEIL when programming for 32bit ARM7 MCUs like lpc2148.
Most of the function oriented pins on lpc214x Microcontrollers are grouped into ports. lpc2148 has 2 ports viz. Port 0 and Port 1.
- Port 0 is a 32 bit wide I/O port (i.e it can be used for max 32 pins where each pin refers to a corresponding bit) and has dedicated direction bits for each of the pins present in the port. 28 out of the 32 pins can be used as bi-directional I/O (digital) pins. Pins P0.24 , P0.26 & P0.27 are unavailable for use and Pin P0.30 can be used as output pin only.
- Port 1 is also a 32 bit wide I/0 port but Pins 0 to 15 i.e P1.0 – P1.15 are unavailable for use and this port too has a dedicated direction bit for each of the usable pins.
In lpc214x MCUs most of the PINS are Multiplexed i.e. these pins can be configured to provide different functions. I’ll explain this in upcoming tutorial. For now Just keep in mind that by default : all functional pins i.e pins in port 0 & 1 are set as GPIO so we can direclty use them when learning GPIO usage.
Now , lets go through the registers used for GPIO programming.
2. IOxDIR : This is the GPIO direction control register. Setting a bit to 0 in this register will configure the corresponding pin to be used as an Input while setting it to 1 will configure it as Output.
3. IOxSET : This register can be used to drive an ‘output’ configured pin to Logic 1 i.e HIGH. Writing Zero does NOT have any effect and hence it cannot be used to drive a pin to Logic 0 i.e LOW. For driving pins LOW IOxCLR is used which is explained below.
4. IOxCLR : This register can be used to drive an ‘output’ configured pin to Logic 0 i.e LOW. Writing Zero does NOT have any effect and hence it cannot be used to drive a pin to Logic 1.
Registers Names defined in ‘lpc214x.h’ header file are basically pointers which point to actual register in Hardware. Since lpc214x MCUs are 32 bit , the size of the pointer is also 32 bits. Each bit in these registers mentioned above is directly linked to a corresponding Pin. Manipulating these bits changes the behavior or state of the pins. For e.g consider IOxDIR register. Bit 0 of IO0DIR corresponds to pin 0 or port 0 hence bit ‘y’ in IOxDIR corresponds to pin ‘y’ in port ‘x’.
Now setting PIN 2 of Port 0 i.e P0.2 as output can be done in various ways as show :
CASE 2. IO0DIR |= 0×0000004; // or 0×4; (hexadecimal – OR and assign: other pins not affected)
CASE 3. IO0DIR |= (1<<2); //(binary – OR and assign: other pins not affected)
First thing is to note that preceding Zeros in Hexadecimal Notation can be ignored since bcoz have no meaning since we are working with unsigned values here (positive only) which are assigned to Registers. For eg. ’0×32′ and ’0×032′ and ’0×0032′ all mean the same.
Also note that the BIT and PIN Numbers are Zero(0) indexed which is quite evident since Bit ‘x’ refers to (x-1)th location in the corresponding register.
Case 1 must be avoided since we are directly assigning a value to the register. So while we are making P0.2 ’1′ others are forced to be assigned a ’0′ which can be avoided by ORing and then assigning Value.
Case 2 can be used when bits need to be changed in bulk and
Case 3 when some or single bit needs to be changed.
Also Note: All GPIO pins are configured as Input after Reset by default!
Example #1)
Consider that we want to configure Pin 19 of Port 0 i.e P0.19 as Ouput and want to drive it High(Logic 1). This can be done as :
IO0SET |= (1<<19); // Make ouput High for P0.19
Example #2)
Making output configured Pin 15 High of Port 0 i.e P0.15 and then Low can be does as follows:
IO0SET |= (1<<15); // Output for P0.15 becomes High
IO0CLR |= (1<<15); // Output for P0.15 becomes Low
Example #3)
Configuring P0.13 and P0.19 as Ouput and Setting them High:
IO0SET |= (1<<13) | (1<<19); // Make ouput High for P0.13 and P0.19
Example #4)
Configuring 1st 16 Pins of Port 0 (P0.0 to P0.15) as Ouput and Setting them High:
IO0SET |= 0x0000FFFF; // Make ouput High for P0.0 to P0.15
Now lets play with some real world examples.
Example #5)
Blinky Example – Now we repeatedly make all pins in port 0 (P0.0 to P0.30) High then Low then High and so on. You can connect Led to some or all Pins on Port 0 to see it in action. Here we will introduce some delay between making all pins High and Low so it can be noticed.
void delay(void);
int main(void)
{
IO0DIR = 0xFFFFFFFF; // Configure all pins on Port 0 as Output
while(1)
{
IO0SET = 0xFFFFFFFF; // Turn on LEDs
delay();
IO0CLR = 0xFFFFFFFF; // Turn them off
delay();
}
return 0; // normally this wont execute
}
void delay(void)
{
int z,c;
c=0;
for(z=0; z<4000000; z++) // You can edit this as per your needs
{
c++; // something needs to be here else KEIL compiler will remove the for loop!
}
}
Example #6)
Configuring P0.7 as Input and monitoring it for a external event like connecting it to LOW or GND. P0.30 is configured as output and connected to LED. If Input for P0.7 is a ‘Low’ (GND) then output for P0.30 is made High which will activate the LED and make it glow (Since the other END of LED is connected to LOW i.e GND). Since internal Pull-ups are enabled the ‘default’ state of the pins configured as Input will be always ‘High’ unless it is explicitly made ‘Low’ by connecting it to Ground. Consider one end of a tactile switch connected to P0.7 and other to ground. When the switch is pressed a ‘LOW’ will be applied to P0.7. The setup is shown in the figure below:

int main(void)
{
IO0DIR &= ~((1<<7)) ; // explicitly making P0.7 as Input - even though by default its already Input
IO0DIR |= (1<<30); // Configuring P0.30 as Output
while(1)
{
if( !(IO0PIN & (1<<7)) ) // Evaluates to True for a 'LOW' on P0.7
{
IO0SET |= (1<<30); // drive P0.30 High
}
}
return 0; // this wont execute ever :P
}
‘(1<<7)' is simply 7th bit '1'(i.e 0x00000080) & rest all bit are zeros. When ‘(1<<7)' is ANDed with IO0SET it will make all other bits except 7th bit to '0'. The Value of 7th bit in result will now depend on IO0PIN's 7th bit. If its 1 (which means input is High) then result after ANDing will be 0×00000080 which is greater than zero and hence will evaluate to ‘TRUE‘. Also when we use ‘Logical NOT‘ i.e ‘!‘ then ‘!(TRUE)‘ evaluates to FALSE hence code is not executed for High Input. When P0.7 is given ‘Low’ the corresponding bit in IO0PIN i.e 7th bit will be set to 0. In this case the result of ‘IO0PIN & (1<<7)‘ will be ’0×0′ which evaluates to FALSE. Hence ‘!(FALSE)‘ evalutes to TRUE and code inside ‘if’ block gets executed.
BTW:The above while loop can be re-written as :
{
IO0SET |= (1<<30); // drive P0.30 High
}
For now lets stick to the original while loop since it keeps things simple.
Example #7)
Now we will extended example 6 so that when the button is pressed the LED will glow and when released or not pressed the LED wont glow. Capturing inputs in this manner, using switches, leads to a phenomenon called ‘bouncing‘ which needs to be resolved using ‘debouncing‘ as explained below. We will a ‘flag’ variable to swtich between High and Low states.
Bouncing:
Usually in switches there are Metal contacts which Open and Close. Consider an Open switch. When it is closed the signal passes over initially and then briefly the contacts might loosen and possibly result in a Open-Circuit momentarily hence signal doesnt pass over for that short period of time. After this the contacts settle and signal passes over again ‘steadily’. This tendency of the metal contacts is referred to as ‘Bouncing’.
Debouncing:
Bouncing needs to be given special attention while processing/capturing inputs. Bouncing can be eliminated using ‘Debouncing’. Debouncing can be tackled at hardware or software level. In hardware it can be done using RC circuits , Latches , etc.. For the sake of this article I’ll show how to deal with it using software. In this we will sample(read) P0.7 status/state two times with a very brief delay in between. In our case this simple strategy is sufficient to prevent bouncing. Note that delay must be correctly chosen – it must not be too high nor too low. If the state of pin is same after taking the second sample then we can say that the new state of the pin is indeed stable.
I would recommend visiting the following links for understanding debouncing indepth.
1) www.ganssle.com/debouncing.htm
2) www.labbookpages.co.uk/electronics/debounce.html
3) www.eng.uwaterloo.ca/~tnaqvi/downloads/DOC/sd192/SwitchDebouncing.htm
void tiny_delay(void);
int main(void)
{
int flag=0, pinSamplePrevious, pinSampleCurrent;
IO0DIR &= ~((1<<7));
IO0DIR |= (1<<30);
pinSamplePrevious = IO0PIN & (1<<7); //Initial Sample
while(1)
{
pinSampleCurrent = IO0PIN & (1<<7); //New Sample
if( pinSampleCurrent != pinSamplePrevious )
{
//P0.7 might get low or high momentarily due to noise depending the external conditions or some other reason
//hence we again take a sample to insure its not due to noise
tiny_delay(); // momentary delay
// now we again read current status of P0.7 from IO0PIN
pinSampleCurrent = IO0PIN & (1<<7);
if( pinSampleCurrent != pinSamplePrevious )
{
//State of P0.7 has indeed changed
if(flag) //First time Flag will be = 0 hence else part will execute
{
IO0SET |= (1<<30); // drive P0.30 High
flag=0; //next time 'else' part will excute
}
else
{
IO0CLR |= (1<<30); // drive P0.30 Low
flag=1; //next time 'if' part will excute
}
//set current value as previous since it has been processed
pinSamplePrevious = pinSampleCurrent;
}
}
}
return 0; // this wont execute ever :P
}
void tiny_delay(void)
{
int z,c;
c=0;
for(z=0; z<1500; z++) //Higher value for higher clock speed
{
c++;
}
}
Initially P0.7 will be high since pull-ups are enabled. Hence ‘pinSamplePrevious‘ and ‘pinSampleCurrent‘ will have same value and ‘if‘ will evalute to false so code inside wont execute.
Event #1) Pressing switch: When switch is pressed P0.7 will now be ‘Low’ but this might be due to noise or glicthy contact in switch so we again read the value of P0.7 to confirm that the new state of P0.7 is stable. If it is stable then value of ‘pinSampleCurrent’ will differ from ‘pinSamplePrevious’ and the inner ‘if‘ block evalute to true and code inside will execute. When its executed first time ‘flag‘ will be 0. Hence, ‘if(flag)‘ will be false and ‘else‘ block will execute which will drive P0.30 Low and set flag to ’1′.
Event #2) Releasing switch: Now After the switch has been released the value of P0.7 will change to ’1′ i.e High. Previous state was ‘Low’ and current is ‘High’ hence pinSamplePrevious’ and ‘pinSampleCurrent’ will again differ and in similar manner as above the code inside inner ‘if’ block will execute. This time though , since flag is 1, ‘if(flag)‘ will evaluate to true and P0.30 will be set to High i.e ’1′ and flag will be set to 0.
Improvising play with Bits:
Using the left shift operation is not confusing but when too many are used together I find it a little bit out of order and affects code readability to some extent. For this I define a Macro ‘BIT(x)’ which replaces ‘(1<<x)’ as:
After that is defined we can directly use BIT(x). Using this Example 3 can be re-written as:
IO0SET |= BIT(13) | BIT(19); // Make ouput High for P0.13 and P0.19
Example 6 can be re-written as:
#define BIT(x) (1<<x)
int main(void)
{
IO0DIR &= ~(BIT(7));
IO0DIR |= BIT(31);
while(1)
{
if( !(IO0PIN & BIT(7)) )
{
IO0SET |= BIT(31);
}
}
return 0;
}
TODO: Add some diagrams so its easy to understand what I’ve tried to explain.
If any reader wants to contibute to this article PM me on forums @ www.ocfreaks.com/forums/ Link to my profile on forums is here. Reader will get full credit he/she deserves for contribution
No related posts.
Filed Under: Embedded

nice explaination . excellent for beginners
Thanks Arun! More tutorials on their way.
Hi Power_user_ex,
I just got started with lpc2148 mC and found your article very useful. Thanks man. In fact by seeing other post on the internet, i was actually doubt full whether i can study it or not. But after when i saw your post, i understood clearly.
To acknowledge and to mention, the basic needed tutorial for this, the c shift operator tutorial, you hosted as a separate link was awesome. Great man. Good idea of first identifying the basics and then diving into the core logic is great. Thanks once again.
Just wanted to acknowledge for your brilliant effort.
Good work.
Thanks Jayaraj! Your feedback has motivated me to add more tutorials relating to embedded stuff. I’ll try my best to keep things clear and would like to know your suggestions if you have any
At present I am working on LPC1768 MCU so will add tutorials for that too. Planning to make some of my projects open source. Stay in touch for more
-Regards ,
Power_user_EX
Hi Power_user_ex,
If possible, can you just write a tutorial on lpc2148 programming with lcd (16 x 2). I looked at some sample code but couldn’t understand anything. So, can you just post an lcd programming tutorial for the same.
Kindly post.
Awaiting for your post.
Thanking you.
Hi Jayaraj! Ill post a tutorial on LCD interfacing asap.
In the mean while if you need any help you can PM me or post a thread on OCFreaks Forums @ http://www.ocfreaks.com/forums/
We have added a dedicated section for robotics , diy , embedded , etc.. related discussions @ http://www.ocfreaks.com/forums/forumdisplay.php?f=93
My forum account to PM me is @ http://www.ocfreaks.com/forums/member.php?u=2
-Regards ,
Power_user_EX
Ya, sure. Appreciating your good work. Keep it up !
Sir,
i want to know how to deal with lpc2148 timers and getting interrupts from them after a match is found……
Ill post a tutorial on using timers and PWM block along with a tutorial on using interrupts .. soon
We have added a dedicated section for robotics , diy , embedded , etc.. related discussions @ http://www.ocfreaks.com/forums/forumdisplay.php?f=93 , You can contact me on forums @ http://www.ocfreaks.com/forums/member.php?u=2
-Regards ,
Power_user_EX
@Power_user_EX
pls post tutorial on lcd interfacing asap.. i need it urgently..
Sorry I was busy with my CNC project .. but as per your request I’ll post it asap .. Probably on 15th or 16th dec-12.
Hi Power_user_ex,
Your tutorials are really nice.It helped me a lot.
Thank you………
Hello neha , glad to see that it helped ya. Working on more tutorials for lpc214x and lpc176x and how to use them in robotics / embedded projects. If you need any help you post your query on our forums @ http://www.ocfreaks.com/forums/forumdisplay.php?f=93
-Regards
Thanks a lot sir !!!
this proved really helpful !!!
Hey Virang nice to see that you found it helpful. More tutorials on their way
If you have any trouble or need help .. you can post your query in the embedded section of our forums @ http://www.ocfreaks.com/forums/forumdisplay.php?f=93
-Regards
hii arun currently i would like to work on timers in LPC2148 can please help me how to work on timers
Hi Bhavya , this is Umang. I will be posting a tutorial on Timers in LPC2148. If you need any kind of help in embedded or lpc2148 you can post your query in embedded section on our forums @ http://www.ocfreaks.com/forums/
hi i want to interface 16×2 lcd with lpc2148. could u pls tell me wat changes should i make in lpc214x.h program to change the port 0 to output port?
Working on 16×2 LCD interfacing article now
Great article!
Guess what! Today is my first day in learning embedded stuff and I came across this article on my first day itself and that too I am learning on LPC2148 from NGX.
Please post more articles.
Thank you so much.
Welcome Anand … more articles relating to Timers , PWMs , Uarts , LCD interfacing , etc.. are in pipeline.
Please post an article related to interrupt covering FIQ, Vectored IRQ, non-vectored IRQ, SW interrupt. I am stuck with interrupt programming. Though I have done the same GPIO programs using interrupt, but still have to cover FIQ and SW interrupt.
Thank you.
Thank you for the tutorial. It was a good point for getting started on lpc2148.
But a question about IOxSET and IOxCLR, if writing 0s do not affect output, then instead of
IO0SET |= (1<<30);
and
IO0CLR |= (1<<30);
cant we use
IO0SET = (1<<30);
and
IO0CLR = (1<<30);
Thank you and more LPC2148 tutorials please.
Yes you are correct … Its only in the case of IOxPIN that we must using ORing , ANDing , etc.. so that other bits are not effected. I’ll update the tutorial. Thanks! Also I’ve posted a new tutorial on interfacing 16×2 LCD module with LPC2148 @ http://www.ocfreaks.com/interfacing-16×2-lcd-with-lpc2148-tutorial/
Thank you for this info. I too have been following the same trend i.e IOxSET |= (1<<Y).
Hi Power_user_EX!
This tutorial is AWESOME! THANK YOU SOOOOO MUCH! I have experience with PIC16F887 in assembly language, and when I faced the LPC2148 I was hopeless, I thought that it might be a problem to learn the tricks. But then I found your tutorials, THANK YOU FOR THE GREAT JOB YOU DID! They are great for beginners with LPC, beginners just like me.
Cheers! Thanks again!
We had almost given up working on LPC 2148 for our project at 2am last night.
And then we found this page.
Thanks a ton. Finely written and explained. More articles awaited!
I WANT TO SENSE LOGIC HIGH FOR LPC2148 HOW CAN I SENSE IT.
I HAVE MADE P0.4 AS INPUT
LIKE IODIR0&=~(1<<4);
if(IOPIN0 & (1<<4))
{
}
BUT WHEN I AM SENSING INPUT 'HIGH' FROM OUTSIDE THE BOARD WITH COMMAN GND IT WILL MAKE THAT HIGH LOGIC TO GND. WHATS THE PROBLEM?
PLZ RPLY..THANKS
Hi Yogesh!
If you want to sense logic level at any pin you need to continuously scan that particular pin using a simple while loop. A more better way of doing this is to setup Pins like EINT0 to trigger an interrupt when the logic level changes say.. from Low to high. I will write a simple code for that and will attach it in my upcoming “LPC214x Interrupt Tutorial” .. which will be posted next. In the mean while you can try this code :
while(1)
{
if(IOPIN0 & (1<<4))
{
IOSET0 = 0x1;
//Set P0.0 To high when Logic High is sensed on P0.4
}
}
If you need any other help regarding embedded stuff you can post a thread in embedded section of our forums.
thanks..
CAN I USE TRANSISTOR FOR SENSING INPUT FROM OUTSIDE .AND COLLECTOR OF IT TO IO PORT OF ARM.WITH COLLECTOR VOLTAGE OF 3.3V. WILL IT WORK?
RPLY THANKS