<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>programming - OCFreaks!</title>
	<atom:link href="https://www.ocfreaks.com/tag/programming/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.ocfreaks.com/tag/programming/</link>
	<description>Overclocking , Gaming , Technology , Robotics &#38; DIY!</description>
	<lastBuildDate>Mon, 21 Oct 2024 07:46:45 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.4</generator>
<site xmlns="com-wordpress:feed-additions:1">42777727</site>	<item>
		<title>LPC2148 ADC Programming Tutorial</title>
		<link>https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/</link>
					<comments>https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/?noamp=mobile#comments</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Sun, 20 Jul 2014 15:07:41 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[lpc2148]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=2283</guid>

					<description><![CDATA[<p>Hi again folks! Finally after a long time we have yet another LPC214x tutorial. This time its on Analog to Digital Converter(ADC) module in LPC214x MCUs.</p>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/">LPC2148 ADC Programming Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/adc/cover.jpg" /></p>
<p><span class="shead"><span class="sheadin">Introduction</span></span></p>
<p>In this tutorial we will go through ARM7 processor based LPC2148 ADC programming. Analog to Digital Conversion(i.e. ADC) , as the name suggests , is all about converting a given analog signal into its digital form or say a digital value. So, what does this mean? Well, basically its measuring the voltage of a given analog signal. The analog signal can be differential, single-ended unipolar, etc. The converted digital value represents the measured voltage. This conversion or measurement happens in presence of a fixed and accurate reference voltage. The analog signal is compared to this reference voltage and then estimations are made to get the final measured value. Note that, here the accuracy depends on how accurate the reference voltage is along with ADC&#8217;s internal architecture. ADCs come in many varieties, some of these are : Flash ADC , Sigma-Delta (SD) ADC , Successive Approximation (SAR) ADC , etc. There are many application for ADC like Sensor Interfacing, Voltage measurement, Current measurement using shunt , Converting an audio input to its digital form, etc.</p>
<h2 class="shead">Analog to Digital Convertor Block in LPC214x</h2>
<p>ADC on LPC214x is based on Successive Approximation(SAR) conversion technique. You can check the wikipedia article on SAR <a href="http://en.wikipedia.org/wiki/Successive_approximation_ADC">here</a>.</p>
<p>Here are the features of ADC module in ARM7 LPC214x :</p>
<ul>
<li>10 bit SAR ADC (1 in lpc2141/2 and 2 in lpc2144/6/8)</li>
<li>Supports Power down mode</li>
<li>Measurement range is from 0 volts to Vref (Reference Voltage)</li>
<li>10 bit conversion time is >= 2us</li>
<li>6(AD0) &#038; 8(AD1) Multiplexed input pins</li>
<li>Burst conversion support for Single or Multiple input</li>
</ul>
<h4>Pins relating to ADC Module of LPC214x :</h4>
<p><center></p>
<table>
<tr>
<th><strong>Pin</strong></th>
<th><strong>Description</strong></th>
</tr>
<tr>
<td><strong>AD0.1 to AD0.4</strong> (P0.28/29/30/25) and <strong>AD0.6, AD0.7</strong> (P0.4/5)</td>
<td>Analog input pins. <br /><strong>Note from Datasheet:</strong> &#8220;If ADC is used, signal levels on analog input pins must not be above the level of Vdda at any time. Otherwise, A/D converter readings will be invalid. If the A/D converter is not used in an application then the pins associated with A/D inputs can be used as 5V tolerant digital IO pins.&#8221; </td>
</tr>
<tr>
<td><strong>Vref</strong></td>
<td>This is the reference voltage pin. It must be connected to an accurate reference voltage source.</td>
</tr>
<tr>
<td><strong>Vdda, Vssa</strong></td>
<td>Vdda is Analog Power pin and Vssa is Ground pin used to power the ADC module. </td>
</tr>
</table>
<p></center></p>
<div class="special sp_blue notestar"><strong>Attention Plz :</strong> Note that the Analog Power Pin i.e Vdda must be properly isolated/decoupled from Vcc, at bare minimum using a ferrite bead and a decoupling capacitor, to suppress noise present on Vcc rail and also to suppress MCU&#8217;s switching noise which can be induced on the Vcc rail. If you have any confusion or query regarding ADC power pin isolation, just comment to this post below and I&#8217;ll try to help you out.</div>
<h2 class="shead">Registers used for ADC programming in LPC214x</h2>
<p><em><strong>(For AD1 registers replace 0 with 1 wherever applicable)</strong></em></p>
<div class="highlight">
<strong>1) <span class="doc_ref">AD0CR</span> &#8211; A/D Control Register : </strong>This is the main control register for AD0.</p>
<ol>
<li><strong>Bits[7 to 0] &#8211; SEL :</strong> This group of bits are used to select the pins(Channels) which will be used for sampling and conversion. Bit &#8216;x'(in this group) is used to select pin A0.x in case of AD0.</li>
<li><strong>Bits[15 to 8] &#8211; CLKDIV : </strong>These bits stores the value for CLKDIV which is used to generate the ADC clock. Peripheral clock i.e. PCLK is divided by CLKDIV+1 to get the ADC clock. Note that ADC clock speed must be <= 4.5Mhz! As per datasheet user must program the smallest value in this field which yields a clock speed of 4.5 MHz or a bit less.</li>
<li><strong>Bit 16 &#8211; BURST :</strong> Set this to 1 for doing repeated conversions. Set this bit to 0 for software controlled conversions , which take 11 clocks to finish. Here is a remark from datasheet : START bits must be set to 000 when BURST=1 or conversions will not start.  Refer datasheet for detailed info.</li>
<li><strong>Bits[19 to 17] &#8211; CLKS :</strong> These bits are used to select the number of clocks used for conversion in burst mode along with number of bits of accuracy of the result in RESULT bits of ADDR.<br />
		<center></p>
<table>
<tr>
<td><strong>Value</strong></td>
<td><strong>clocks / bits</strong></td>
</tr>
<tr>
<td>000</td>
<td>11 clocks / 10 bits </td>
</tr>
<tr>
<td>001</td>
<td>10 clock / 9 bits</td>
</tr>
<tr>
<td>010</td>
<td>9 clock / 8 bits</td>
</tr>
<tr>
<td>011</td>
<td>8 clock / 7 bits</td>
</tr>
<tr>
<td>100</td>
<td>7 clock / 6 bits</td>
</tr>
<tr>
<td>101</td>
<td>6 clock / 5 bits</td>
</tr>
<tr>
<td>110</td>
<td>5 clock / 4 bits</td>
</tr>
<tr>
<td>111</td>
<td>4 clock / 3 bits</td>
</tr>
</table>
<p></center></li>
<li><strong>Bit 21 &#8211; PDN :</strong> Set it to 1 for powering up the ADC and making it operational. Set it to 0 for bringing it in powerdown mode.</li>
<li><strong>Bits[26 to 24] &#8211; START : </strong>These bits are used to control the start of ADC conversion when BURST (bit 16) is set to 0. Below is the table as given in datasheet :<br />
		<center></p>
<table>
<tr>
<td><strong>Value</strong></td>
<td><strong>Significance</strong></td>
</tr>
<tr>
<td>000</td>
<td>No start (this value is to be used when clearing PDN to 0)</td>
</tr>
<tr>
<td>001</td>
<td>Start the conversion</td>
</tr>
<tr>
<td>010</td>
<td>Start conversion when the edge selected by bit 27 occurs on P0.16/EINT0/MAT0.2/CAP0.2 pin</td>
</tr>
<tr>
<td>011</td>
<td>Similar to above &#8211; for MAT0.0 pin</td>
</tr>
<tr>
<td>100</td>
<td>Similar to above &#8211; for MAT0.1 pin</td>
</tr>
<tr>
<td>101</td>
<td>Similar to above &#8211; for MAT0.3 pin</td>
</tr>
<tr>
<td>110</td>
<td>Similar to above &#8211; for MAT1.0 pin</td>
</tr>
<tr>
<td>111</td>
<td>Similar to above &#8211; for MAT1.1 pin</td>
</tr>
</table>
<p></center></li>
<li><strong>Bit 27 &#8211; EDGE : </strong>Set this bit to 1 to start the conversion on falling edge of the selected CAP/MAT signal and set this bit to 0 to start the conversion on rising edge of the selected signal. (Note: This bit is of used only in the case when the START contains a value between 010 to 111 as shown above.)</li>
<li>Other bits are reserved.</li>
</ol>
<p><strong>2) <span class="doc_ref">AD0GDR</span> &#8211; A/D Global Data Register :</strong> This is the global data register for the corresponding ADC module. It contains the ADC’s DONE bit and the result of the most recent A/D conversion.	</p>
<ol>
<li><strong>Bits[15 to 6] &#8211; RESULT :</strong> Given DONE(below) is set to 1 these bits give a binary fraction which represents the voltage on the pin selected by the SEL field, divided by the voltage on Vref pin i.e. =V/Vref. A value of zero indicates that voltage on the given pin was less than , equal to or greater than Vssa. And a value of 0x3FF means that the voltage on the given pin was close to, equal to or greater than the reference voltage.</li>
<li><strong>Bits[26 to 24] &#8211; CHN : </strong>It gives the channel from which RESULT bits were converted. 000 for channel 0 , 001 for channel 1 and so on.</li>
<li><strong>Bit 30 &#8211; OVERRUN :</strong> In burst mode this bit is 1 in case of an Overrun i.e. the result of previous conversion being lost(overwritten). This bit will be cleared after reading AD0GDR.</li>
<li><strong>Bit 31 &#8211; DONE :</strong> When ADC conversion completes this bit is 1. When this register(AD0GDR) is read and AD0CR is written, this bit gets cleared i.e. set to 0. If AD0CR is written while a conversion is in progress then this bit is set and a new conversion is started.</li>
<li>Other bits are reserved.</li>
</ol>
<p><strong>3) <span class="doc_ref">ADGSR</span> &#8211; A/D Global Start Register :</strong> This register is used to simultaneously start conversion process of both ADC modules.	</p>
<ol>
<li><strong>Bit 16 &#8211; BURST :</strong> Same as given for AD0CR.</li>
<li><strong>Bits[26 to 24] &#8211; START :</strong> Same as given for AD0CR.</li>
<li><strong>Bits 27 &#8211; EDGE :</strong> Same as shown for AD0CR.</li>
<li>Other bits are reserved.</li>
</ol>
<p><strong>4) <span class="doc_ref">AD0STAT</span> &#8211; A/D Status register :</strong> This register contains DONE and OVERRUN flags for all of the A/D channels along with A/D interrupt flag.</p>
<ol>
<li><strong>Bits[7 to 0] &#8211; DONE[7 to 0] :</strong> Here xth bit mirrors DONEx status flag from the result register for A/D channel x.</li>
<li><strong>Bits[15 to 8] &#8211; OVERRUN[7 to 0] :</strong> Even here the xth bit mirrors OVERRUNx status flag from the result register for A/D channel x </li>
<li><strong>Bit 16 &#8211; ADINT :</strong> This bit represents the A/D interrupt flag. It is 1 when any of the individual A/D channel DONE flags is asserted and enabled to contribute to the A/D interrupt via the ADINTEN(given below) register.</li>
<li>Other bits are reserved.</li>
</ol>
<p><strong>5) <span class="doc_ref">AD0INTEN</span> &#8211; A/D Interrupt Enable Register :</strong> Using this register interrupts can be enabled or disabled for any ADC channels.</p>
<ol>
<li><strong>Bits[0 to 8] &#8211; ADINTEN[0 to 8] :</strong> If xth bit is set to 1 then an interrupt will be generated on completion of A/D conversion on channel x. Similarly if this bit is set to 0 then an interrupt wont be generated.</li>
</ol>
<p><strong>6) <span class="doc_ref">AD0DR0 to AD0DR7</span> &#8211; A/D Data registers :</strong> This register contains the result of the most recent conversion completed on the corresponding channel [0 to 7].</p>
<ol>
<li><strong>Bit[15 to 6] :</strong> Same as given for AD0GDR.</li>
<li><strong>Bit 30 &#8211; OVERRUN :</strong> Same as given for AD0GDR.</li>
<li><strong>Bit 31 &#8211; DONE :</strong> Same as given for AD0GDR.</li>
<li>Other bits are reserved.</li>
</ol>
</div>
<h2 class="shead">ADC Input circuit and Overvoltage protection </h2>
<p>In our case we are going to work with simple Single-ended unipolar signal which varies(or is fixed i.e. DC) between 0V and Vref (Reference voltage). Note that ADC&#8217;s cannot measure voltages going above Vref. If a signal goes above Vref then it can lead to an incorrect reading. At the same time we also need to protect the input side of the ADC from over voltage condition. Here is a warning given in LPC214x User Manual:</p>
<div class="special sp_red noteinfo"><strong>LPC214x User Manual Warning :</strong> &#8220;While the ADC pins are specified as 5V tolerant, the analog multiplexing in the ADC block is not. More than 3.3 V (VDDA) +10 % should not be applied to any pin that is selected as an ADC input, or the ADC reading will be incorrect. If for example AD0.0 and AD0.1 are used as the ADC0 inputs and voltage on AD0.0 = 4.5 V while AD0.1 = 2.5 V, an excessive voltage on the AD0.0 can cause an incorrect reading of the AD0.1, although the AD0.1 input voltage is within the right range.&#8221;</div>
<p>One of the simplest and neat way to protect the input pins is using a Zener Diode. In our case i.e. in lpc214x overvoltage condition signifies a voltage greater than 3.3Volts. Hence for this we can use a <span class="doc_ref">3.3V zener diode</span>. The datasheet also suggests using a Voltage Source Interface Resistance i.e. Vrsi at the input side with value no more than 40K-Ohms. A typical value of 1.2K ohms can be used which will limit the zener current. In general the value of series resistor depends upon : max zener current, sample and hold capacitor value inside the ADC block and low pass input filter if applicable. If the resistance is too high than the sample and hold capacitor inside SAR ADC of lpc214x will take more time to charge up and we don&#8217;t want this to happen. You can also add a 100nF decoupling capacitor if you want to which will form a low pass RC filter. For general &#8216;DIY project&#8217; purposes, the input circuit schematic is as given below:</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/adc/adc_input_protection_circuit.png" /></p>
<p>Low pass anti-aliasing filter was discussed in LPC214x Sine PWM tutorial under &#8216;Filter Design&#8217; section which can be found <a href="https://www.ocfreaks.com/sine-wave-generator-using-pwm-lpc2148-microcontroller-tutorial/">here</a>.</p>
<p>To measure voltages greater than the reference voltage you can use a simply use a voltage divider configuration using 2 resistors with known values. The selection of these resistors depends on the maximum voltage that is to be measured.</p>
<h2 class="shead">LPC214x ADC Modes , Setup and Programming</h2>
<h4>ADC Operating modes in LPC214x :</h4>
<p><strong>1. Software controlled mode :</strong> In Software mode only one conversion will be done at a time. This conversion can be controlled in software. To perform another conversion you will need to re-initiate the process. In software mode only 1 bit in the SEL field of AD0CR can be 1 i.e. only 1 Channel(i.e. Pin) can be selected for conversion at a time. You can do conversions on multiple Channels (one at a time) by selecting a particular Channel along with appropriate bit in SEL field and then do the same for rest of the channels.<br />
 <strong><br />
2. Burst or Hardware mode :</strong> In Burst or Hardware mode conversions are performed continuously on the selected channels in round-robin fashion. Since the conversions cannot be controlled by software, Overrun may occur in this mode. Overrun is the case when a previous conversion result is replaced by new conversion result without previous result being read i.e. the conversion is lost. Usually an interrupt is used in Burst mode to get the latest conversion results. This interrupt is triggered when conversion in one of the selected channel ends.</p>
<h4>1. Setting up and configuring ADC Module for software controlled mode :</h4>
<p>First we will define some values which will help us setup the AD0CR register to configure the AD0 block before we can use it.</p>
<pre><code class="language-cpp">
#define CLKDIV (15-1) // 4Mhz ADC clock (ADC_CLOCK=PCLK/CLKDIV) where "CLKDIV-1" is actually used , in our case PCLK=60mhz  
 
#define BURST_MODE_OFF (0<<16) // 1 for on and 0 for off
 
#define PowerUP (1<<21) //setting it to 0 will power it down
 
#define START_NOW ((0<<26)|(0<<25)|(1<<24)) //001 for starting the conversion immediately
 
#define ADC_DONE (1<<31)
</code></pre>
<p>Here we define CLKDIV which is divided by PCLK to get the ADC clock <=4Mhz. In our case we will be using a PCLK of 60Mhz hence we divide 60Mhz by 15 to get 4Mhz. But note that the ADC module actually needs a value of (CLKDIV-1). This is because it adds "+1" to the value internally (in case if user uses a CLKDIV of 0 it will be still valid). For our purposes CLKDIV is a 'zero-indexed' value hence we must subtract it by 1 before using it. In our case we need to supply a value of 14 i.e. (15-1) to AD0CR. 

BURST_MODE_OFF(bit 16) , PowerUP(bit 21) and ADC_DONE(bit 31) are defined as required. CLKS_10bit has been defined for 10 bit resolution - you can change the bit combination as per your needs. Finally START_NOW is defined as "001" which is for starting the conversion 'now'.

Next we define AD0CR_setup which contains basic configuration for setting up the ADC Module. We feed CLKDIV , BURST_MODE_OFF and PowerUP into AD0CR_setup as follows :



<pre><code class="language-cpp">
unsigned long AD0CR_setup = (CLKDIV<<8) | BURST_MODE_OFF | PowerUP;
</code></pre>
<p>Now we assign AD0CR_setup to AD0CR along with channel selection information to select channels as required. Finally we assign(by ORing) START_NOW to AD0CR to start the conversion process as shown :</p>
<pre><code class="language-cpp">
AD0CR =  AD0CR_setup | SEL_AD06; 
AD0CR |= START_NOW;
</code></pre>
<p>Note that AD0CR can be assigned/setup in a single step. But I am doing it in three steps to keep things simpler.</p>
<h4>2. Setting up and configuring ADC Module for Burst mode :</h4>
<p>Configuring ADC Module is similar to what was done in software controlled mode except here we use the CLKS bits and don't use the START bits in AD0CR. ADC_DONE is also not applicable since we are using an ISR which gets triggered when a conversion completes on any of the enabled channels.</p>
<pre><code class="language-cpp">
#define CLKDIV (15-1) // 4Mhz ADC clock (ADC_CLOCK=PCLK/CLKDIV) where "CLKDIV-1" is actually used , in our case PCLK=60mhz  

#define BURST_MODE_ON (1<<16) // 1 for on and 0 for off

#define CLKS_10bit ((0<<19)|(0<<18)|(0<<17)) //10 bit resolution

#define PowerUP (1<<21) //setting it to 0 will power it down
</code></pre>
<p>Here CLKS_10bit is used to select 10 bit ADC resolution. We configure and setup the ADC module in a similar manner(as shown above) as follows :</p>
<pre><code class="language-cpp">
unsigned int AD0CR_setup = (CLKDIV<<8) | BURST_MODE_ON | CLKS_10bit | PowerUP;
AD0CR =  AD0CR_setup | SEL_AD06 | SEL_AD07; 
</code></pre>
<p>Note that in this case we can select multiple channels for conversion when setting up AD0CR. START bits are not applicable here since the conversions starts as soon we setup AD0CR register.</p>
<h4>3. Fetching the conversion result in software controlled mode :</h4>
<p>In software controlled mode we continuously monitor bit 31 in the corresponding channel data register ADDR. If bit 31 changes to 1 from 0 , it means that current conversion has been completed and the result is ready. For example , if we have used channel 6 of AD0 then we monitor for changes in bit 31 as follows :</p>
<pre><code class="language-cpp">
while( (AD0DR6 & ADC_DONE) == 0 ); //this loop will terminate when bit 31 of AD0DR6 changes to 1.
</code></pre>
<p>After this we extract the result which is stored in ADDR bits 6 to 15. Here we right shift ADDR by 6 places and force all other unrelated bits to 0 by using a 10-bit mask value of 0x3FF. 0x3FF is a mask containing 1's in bit locations 0 to 9. In our case with AD0DR6 being used , it can be done as follows :</p>
<pre><code class="language-cpp">
result = (AD0DR6>>6) & 0x3FF;
</code></pre>
<h4>4. Fetching the conversion result in Burst mode :</h4>
<p>In Burst mode we use an ISR which triggers at the completion of a conversion in any one of the channel. Now , we just need to find the Channel for which the conversion was done. For this we fetch the channel number from AD0GDR which also stores the conversion result. Bits 24 to 26 in AD0GDR contain the channel number. Hence , we shift it 24 places and use a 3bit mask value of 0xF as shown below :</p>
<pre><code class="language-cpp">
unsigned long AD0GDR_Read = AD0GDR;
int channel = (AD0GDR_Read>>24) & 0xF; //Extract Channel Number
</code></pre>
<p>After knowing the Channel number, we have 2 options to fetch the conversion result from. Either we can fetch it from AD0GDR or from AD0DRx of the corresponding channel. In the examples covered in 'examples section' of this tutorial I have used AD0GDR for extracting the conversion result as follows :</p>
<pre><code class="language-cpp">
int currentResult = (AD0GDR_Read>>6) & 0x3FF; //Extract Conversion Result
</code></pre>
<div class="special sp_red noteinfo"><strong>NOTE : </strong>In my experience, Reading AD0GDR clears the AD0 Interrupt Flag in Keil UV3 - But in Keil UV4 it doesn't. In Keil UV4 we need to read the Data Register(AD0DRx) of the corresponding channel as well to Clear the AD0 Interrupt Flag or else AD0 Interrupt won't get cleared! To make Interrupts and Uart work in Keil UV4 follow this simple guide @ <a href="https://www.ocfreaks.com/lpc2148-interrupt-problem-issue-fix">https://www.ocfreaks.com/lpc2148-interrupt-problem-issue-fix</a> </div>
<h2 class="shead">ARM7 LPC2148 ADC real world examples</h2>
<p>Here is a pic of my setup for testing the given examples(I had used 2x 10KOhms Potentiometer):</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/adc/lpc2148_adc_tutorial.jpg" /></p>
<p>Note that in this case no input protection & filtering was required hence I had skipped it. </p>
<h4>ADC Example 1 :</h4>
<p>This example uses Software Controlled mode for performing Analog to Digital conversion. In this example we use P0.4 as analog input for measuring the voltage. P0.4 corresponds to Channel 6 of AD0 i.e. AD06. For testing I had used a 10K potentiometer and connected the middle leg to P0.4 of my LPC2148 development board. The connections are as shown below :</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/adc/example1_adc_circuit.png" /></p>
<p><strong><br />
Source Code :</strong></p>
<pre><code class="language-cpp">
/*(C) Umang Gajera - www.ocfreaks.com
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
LPC2148 ADC Example 1 Source Code.
@ https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/
License : GPL.*/

#include &lt;lpc214x.h&gt;
#include "ocfreaks_sh.h" //OCFreaks LPC214x Tutorial Support Header

#define AD06 ((1<<9)|(1<<8)) //Select AD0.6 function for P0.4
#define SEL_AD06 (1<<6)

#define CLKDIV (15-1) // 4Mhz ADC clock (ADC_CLOCK=PCLK/CLKDIV) where "CLKDIV-1" is actually used , in our case PCLK=60mhz  
#define BURST_MODE_OFF (0<<16) // 1 for on and 0 for off
#define PowerUP (1<<21)
#define START_NOW ((0<<26)|(0<<25)|(1<<24)) //001 for starting the conversion immediately
#define ADC_DONE (1<<31)

#define VREF 3.3 //Reference Voltage at VREF pin

int main(void)
{
	initClocks(); //CCLK and PCLK @ 60Mhz - defined in ocfreaks_sh.h
	initUART0(); //Initialize UART0 for printf - defined in ocfreaks_sh.h

	PINSEL0 |= AD06 ; //select AD0.6 for P0.4
	int result=0;
	float voltage = 0;
	unsigned long AD0CR_setup = (CLKDIV<<8) | BURST_MODE_OFF | PowerUP;
	
	printf("OCFreaks.com ADC Tutorial Example 1.\nSoftware Controlled Mode ADC on AD06 Channel.\n");
	
	while(1)
	{
		AD0CR =  AD0CR_setup | SEL_AD06; 
		AD0CR |= START_NOW; //Start new Conversion

		while( (AD0DR6 &#038; ADC_DONE) == 0 );
		
		result = (AD0DR6>>6) & 0x3ff;
		
		voltage = ((float)result*VREF)/1024; //Convert result to volts
		
		printf("AD06 = %dmV\n" , (int)( voltage*1000 )); //Display millivolts
		delayMS(250); //Slowing down Updates to 4 Updates per second
	}
	
	//return 0; //This won't execute normally
}
</code></pre>
<p><strong><br />
Serial Output :</strong></p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/adc/ex1_ss_.jpg" /></p>
<div class="highlight"><strong>Download Project Source for Example #1 @</strong> <a href='https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC214x_ADC_Tutorial_Example_1.zip'>LPC214x ADC Tutorial Example 1.zip</a> [Successfully tested on Keil UV4.70a] </div>
<h4>ADC Example 2 :</h4>
<p>This example is similar to Example 1. Here additionally we use P0.5 i.e. AD07 for Analog to Digital Conversion. Even in this case a 10K potentiometer was used for testing. The connections are similar to what given above.</p>
<p><strong>Source Code :</strong></p>
<pre><code class="language-cpp">
/*(C) Umang Gajera - www.ocfreaks.com
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
LPC2148 ADC Example 2 Source code.
@ https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/
License : GPL.*/

#include &lt;lpc214x.h&gt;
#include "ocfreaks_sh.h" //OCFreaks LPC214x Tutorial Support Header

#define AD06 ((1<<9)|(1<<8)) //Select AD0.6 function for P0.4
#define AD07 ((1<<11)|(1<<10)) //Select AD0.7 function for P0.5

#define SEL_AD06 (1<<6)
#define SEL_AD07 (1<<7)
#define CLKDIV (15-1) // 4Mhz ADC clock (ADC_CLOCK=PCLK/CLKDIV) where "CLKDIV-1" is actually used , in our case PCLK=60mhz  
#define BURST_MODE_OFF (0<<16) // 1 for on and 0 for off
#define PowerUP (1<<21)
#define START_NOW ((0<<26)|(0<<25)|(1<<24)) //001 for starting the conversion immediately
#define ADC_DONE (1<<31)
#define VREF 3.3 //Reference Voltage at VREF Pin

int main(void)
{
	initClocks(); //CCLK and PCLK @ 60Mhz - defined in ocfreaks_sh.h
	initUART0(); //Initialize UART0 for printf - defined in ocfreaks_sh.h

	PINSEL0 |= AD06 | AD07 ; //select AD0.6 and AD0.7 for P0.4 and P0.5
	int result=0;
	float voltage=0;
	unsigned long AD0CR_setup = (CLKDIV<<8) | BURST_MODE_OFF | PowerUP;
	
	printf("OCFreaks.com ADC Tutorial Example 1.\nSoftware Controlled Mode ADC on AD06 &#038; AD07 Channel.\n");
	
	while(1)
	{
		//Perform Conversion on Channel 6
		AD0CR =  AD0CR_setup | SEL_AD06; 
		AD0CR |= START_NOW; //Start new Conversion
		while( (AD0DR6 &#038; ADC_DONE) == 0 );
		result = (AD0DR6>>6) & 0x3ff;
		voltage = ((float)result*VREF)/1024; //Convert result to volts.
		printf("AD06 = %dmV | " , (int)( voltage*1000 )); //Display millivolts
		
		//Now Perform Conversion on Channel 7
		AD0CR =  AD0CR_setup | SEL_AD07; 
		AD0CR |= START_NOW; //Start new Conversion
		while( (AD0DR7 & ADC_DONE) == 0 );
		result = (AD0DR7>>6) & 0x3ff;
		voltage = ((float)result*VREF)/1024; //Convert result to volts.
		printf("AD07 = %dmV\n" , (int)( voltage*1000 )); //Display millivolts
		
		delayMS(250); //Slowing down Updates to 4 Updates per second
	}
	
	//return 0; //this won't execute normally
}
</code></pre>
<p>The serial output is similar to example 3 except that this was done in Software mode.</p>
<div class="highlight"><strong>Download Project Source for Example #1 @</strong> <a href='https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC214x_ADC_Tutorial_Example_2.zip'>LPC214x ADC Tutorial Example 2.zip</a> [Successfully tested on Keil UV4.70a] </div>
<h4>ADC Example 3 :</h4>
<p>Here Burst Mode is used for AD conversion. In this example we use an ISR (AD0ISR) which is invoked when a completion occurs in channel 6 or 7. In the ISR we first find the channel number and the extract the result. Here I've used global variables for storing the result so it can be accessed by the 'main()' function which is outside its scope.</p>
<p><strong>Source Code :</strong></p>
<pre><code class="language-cpp">
/*(C) Umang Gajera - www.ocfreaks.com
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
LPC2148 ADC Example 3 Source code.
@ https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/
License : GPL.*/

#include &lt;lpc214x.h&gt;
#include "ocfreaks_sh.h" //OCFreaks LPC214x Tutorial Support Header

#define AD06 ((1<<9)|(1<<8)) //Select AD0.6 function for P0.4
#define AD07 ((1<<11)|(1<<10)) //Select AD0.7 function for P0.5

#define ADINTEN6 (1<<6) //Interrupt Enable for Channel 6
#define ADINTEN7 (1<<7) //Interrupt Enable for Channel 7
#define SEL_AD06 (1<<6) //Select Channel 6  for A/D Conversion
#define SEL_AD07 (1<<7) //Select Channel 7 for A/D Conversion

#define CLKDIV (15-1) //4Mhz ADC clock (ADC_CLOCK=PCLK/CLKDIV) where "CLKDIV-1" is actually used , in our case PCLK=60mhz  
#define BURST_MODE_ON (1<<16) //1 for on and 0 for off
#define CLKS_10bit ((0<<19)|(0<<18)|(0<<17)) //10bit - used in burst mode only
#define PowerUP (1<<21)
#define VREF 3.3 //Reference Voltage at VREF Pin

void initClocks(void);
void initTimer0(void);
__irq void AD0ISR(void);

void setupPLL0(void);
void feedSeq(void);
void connectPLL0(void);

int AD06Result=0 , AD07Result=0; //Global Variables

int main(void)
{
	initClocks(); //CCLK and PCLK @ 60Mhz - defined in ocfreaks_sh.h
	initUART0(); //Initialize UART0 for printf - defined in ocfreaks_sh.h
	
	PINSEL0 |= AD06 | AD07 ; //Select AD0.6 and AD0.7 for P0.4 and P0.5 respectively
	
	VICIntEnable |= (1<<18) ;
	VICVectCntl0 = (1<<5) | 18 ;
	VICVectAddr0 = (unsigned) AD0ISR;
	
	printf("OCFreaks.com ADC Tutorial Example 3.\nHardware Controlled Mode(BURST Mode) ADC on AD06 &#038; AD07 Channel.\n");
	
	unsigned long AD0CR_setup = (CLKDIV<<8) | BURST_MODE_ON | CLKS_10bit | PowerUP; //Setup ADC
	AD0INTEN = ADINTEN6 | ADINTEN7 ; //Enable AD06 and AD07 Interrupts 
	AD0CR =  AD0CR_setup | SEL_AD06 | SEL_AD07; 
	
	delayMS(10); //Wait till initial conversions get finished
	
	float volt1=0,volt2=0;
	
	while(1)
	{
		volt1 = ((float)AD06Result*VREF)/1024; //Convert AD0.6 result to volts.
		volt2 = ((float)AD07Result*VREF)/1024; //Convert AD0.7 result to volts.
		printf("AD06 = %dmV | AD07 = %dmV\n",(int)(volt1*1000),(int)(volt2*1000)); //Display millivolts
		delayMS(250); //4 updates per second
	}
	
	//return 0; //this wont execute normally
}

__irq void AD0ISR(void) //AD0 Interrupt Function
{
	unsigned long dummyRead;
	unsigned long AD0GDR_Read = AD0GDR;
	
	int channel = (AD0GDR_Read>>24) & 0xF; //Extract Channel Number
	int currentResult = (AD0GDR_Read>>6) & 0x3FF; //Extract Conversion Result

	if(channel == 6)
	{ 
		dummyRead = AD0DR6; //Read to Clear Done flag , Also clears AD0 interrupt
		AD06Result = currentResult;
	}
	else if(channel == 7)
	{
		dummyRead = AD0DR7; //Read to Clear Done flag , Also clears AD0 interrupt
		AD07Result = currentResult;
	}
	
	VICVectAddr = 0x0; //Signal that ISR has finished
}
</code></pre>
<p><strong>Serial Output :</strong></p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/adc/ex3_ss.jpg" /></p>
<div class="highlight"><strong>Download Project Source for Example #1 @</strong> <a href='https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC214x_ADC_Tutorial_Example_3.zip'>LPC214x ADC Tutorial Example 3.zip</a> [Successfully tested on Keil UV4.70a] </div>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/">LPC2148 ADC Programming Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2283</post-id>	</item>
		<item>
		<title>LPC2148 UART Programming Tutorial</title>
		<link>https://www.ocfreaks.com/lpc2148-uart-programming-tutorial/</link>
					<comments>https://www.ocfreaks.com/lpc2148-uart-programming-tutorial/?noamp=mobile#comments</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Tue, 26 Nov 2013 14:33:40 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=1860</guid>

					<description><![CDATA[<p>Hi again guys! In this tutorial we will go through the basics of UART(SERIAL) programming for LPC214x family of microcontrollers. If you are new to UART, you can go through the basic UART tutorial which I&#8217;ve posted @ Basic Uart Tutorial. Introduction Here is a quick recap of UART basics : Uart uses TxD(Transmit) Pin [&#8230;]</p>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-uart-programming-tutorial/">LPC2148 UART Programming Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div><script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script><!-- Content_Start --><ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-3054360504591881"
     data-ad-slot="4043606851"
     data-ad-format="auto"></ins><br />
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script></div>
<p>Hi again guys! In this tutorial we will go through the basics of UART(SERIAL) programming for LPC214x family of microcontrollers. If you are new to UART, you can go through the basic UART tutorial which I&#8217;ve posted @ <a href="https://www.ocfreaks.com/uart-tutorial-basics/">Basic Uart Tutorial</a>.</p>
<p><span class="shead"><span class="sheadin">Introduction</span></span></p>
<p>Here is a quick recap of UART basics :</p>
<p>Uart uses TxD(Transmit) Pin for sending Data and RxD(Receive) Pin to get data. UART sends &#038; receives data in form of chunks or packets. These chunks or packets are also referred to as ‘transmission characters’. The structure of a UART data packet is as shown below :</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/uart/uart-data-packet-format.jpg" /></p>
<p>Here is an example of single data packet i.e transmission character :</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/uart/uart-example.jpg" /></p>
<p>Now , Lets start with the main Tutorial. LPC214x has 2 UART blocks which are UART0 and UART1. For UART0 the TxD pin is P0.0 and RxD pin is P0.1 and similarly for UART 1 the TxD pin is P0.8 and RxD pin is P0.9 as shown in the table below :</p>
<p><center></p>
<table>
<tr>
<td>Pins:</td>
<td><strong>TxD</strong></td>
<td><strong>RxD</strong></td>
</tr>
<tr>
<td>UART0</td>
<td> P0.0 </td>
<td>P0.1</td>
</tr>
<tr>
<td>UART1</td>
<td> P0.8</td>
<td> P0.9</td>
</tr>
</table>
<p></center></p>
<div class="special sp_red noteinfo"><strong>Note 1</strong>: Both UART0 &#038; UART1 blocks internally have a 16-byte FIFO (First In First Out) structure to hold the Rx and Tx data. Each byte in this FIFO represents a character which was sent or received <strong>in order</strong>. Both blocks also contain 2 registers each, for data access and assembly.<br />
<strong><br />
Tx has THR(Transmit Holding Register) and TSR(Transmit Shift Register)</strong> &#8211; When we write Data to be sent into THR it is then transferred to TSR which assembles the data to be transmitted via Tx Pin.<br />
<strong><br />
Similarly Rx has RSR(Receive Shift Register) and RBR(Receive Buffer Register)</strong> &#8211; When a valid data is Received at Rx Pin it is first assembled in RSR and then passed in to Rx FIFO which can be then accessed via RBR.</div>
<div class="special sp_red noteinfo">
<strong>Note 2: The 4 mentioned UART Pins on LPC214x are 5V tolerant .. still make sure you are using 3.3v Voltage-level for UART communication. Hence , if you have RS232 to serial TTL module make sure it has MAX3232 =OR= if you are using FTDI&#8217;s FT232R based USB to serial TTL module make sure it is configured for 3.3v I/O.</strong>
</div>
<div class="special sp_blue notestar"><strong>Note 3</strong>: UART1 on LPC214x features a full blown modem interface which includes additional Control and Status pins which are found on a PC&#8217;s RS-232(Serial) port. But sadly most new mid-end to high-end PCs don&#8217;t have RS-232 port these days and hence we have to use other options like FTDI&#8217;s USB to TTL converter so that we can interface our microcontroller to a PC or a laptop using USB.</div>
<h2 class="shead">Registers used for UART programming in LPC214x</h2>
<p>Before we can use these pins to transfer data , first we need to configure and initialize the UART block in our LPC214x microcontroller. But before doing that, lets go through some of the important registers: (<em>for UART1 registers replace 0 with 1</em>)</p>
<div class="highlight">
<h4><strong>Data Related Registers </strong>:</h4>
<p><b>1) U0RBR &#8211; Receiver Buffer Register (READ ONLY!):</b> This register contains the top most byte(8-bit data chunk) in the Rx FIFO i.e the oldest received data in FIFO. To properly read data from U0RBR , the DLAB(Divisor Latch Access) bit in U0LCR register must be first set to 0. Also , as per the user manual &#8220;The right approach for fetching the valid pair of received byte and its status bits is first to read the content of the U0LSR register, and then to read a byte from the U0RBR.&#8221; (<em><strong>Note </strong>: If you are using 7,6 or 5 Bits for data then other remaining bits are automatically padded with 0s</em>) </p>
<p><b>2) U0THR &#8211; Transmit Holding Register (WRITE ONLY!):</b> U0THR contains the top most byte in Tx FIFO and in this case its the newest(latest) transmitted data. As in the case with U0RBR , we must set DLAB=0 to access U0THR for write operation.</p>
<h4><strong> Baud Rate Setup related registers </strong>:</h4>
<p><strong>1) U0DLL and U0DLM &#8211; Divisor Latch registers:</strong> Both of them hold 8-bit values. These register together form a 16-bit divisor value which is used in baud rate generation which we will see in later section. U0DLM holds the upper 8-bits and U0DLL holds the lower 8-bits and the formation is &#8220;<strong>[U0DLM:U0DLL]</strong>&#8220;. Since these form a divisor value and division by zero is invalid, the starting value for U0DLL is 0x01 (and not 0x00) i.e the starting value in combined formation is &#8220;<strong>[0x00:0x01]</strong>&#8221; i.e <strong>0x0001</strong>. <strong>Please keep this in mind while doing baud-rate calculations. In order to access and use these registers properly, DLAB bit in U0LCR must be first set to 1</strong>. </p>
<p><strong>2) U0FDR &#8211; Fractional Divider Register :</strong> This register is used to set the prescale value for baud rate generation. The input clock is the peripheral clock and output is the desired clock defined by this register. This register actually holds to different 4-bit values (a divisor and a multiplier) for prescaling which are:</p>
<ol>
<li><strong>Bit [3 to 0] &#8211; DIVADDVAL :</strong> This is the prescale divisor value. If this value if 0 then fractional baud rate generator wont have any effect on Uart Baud rate.</li>
<li><strong>Bit [7 to 4] &#8211; MULVAL :</strong> This is prescale multiplier value. Even if fractional baud rate generator is not used the value in this register must be more than or equal to 1 else UART0 will not operate properly.</li>
<li>Other Bits reserved.</li>
</ol>
<p>Remark from Usermanual : &#8220;<strong><em>If the fractional divider is active (DIVADDVAL > 0) and DLM = 0, the value of the DLL register must be 2 or greater!</em></strong>&#8221;</p>
<h4><strong>Control and Status Registers :</strong></h4>
<p><strong>1) U0FCR &#8211; FIFO Control Register : </strong>Used to control Rx/Tx FIFO operations.</p>
<ol>
<li><strong>Bit 0 &#8211; FIFO Enable :</strong> 1 to Enable both Rx and Tx FIFOs and 0 to disable.</li>
<li><strong>Bit 1 &#8211; Rx FIFO Reset :</strong> Writing a 1 will clear and reset Rx FIFO.</li>
<li><strong>Bit 2 &#8211; Tx FIFO Reset :</strong> Writing a 1 will clear and reset Tx FIFO.</li>
<li><strong>Bits [7 to 6]</strong> :</strong> Used to determine that how many UART0 Rx FIFO characters must be written before an interrupt is activated.<br />
[00] (i.e trigger level 0) for 1 character.<br />
[01] (i.e trigger level 1) for 4 characters.<br />
[10] (i.e trigger level 2) for 8 characters.<br />
[11] (i.e trigger level 3) for 14 characters.</li>
<li>Others bits are reserved.</li>
</ol>
<p><strong>2) U0LCR &#8211; Line Control Register :</strong> Used to configure the UART block (i.e the data format used in transmission).</p>
<ol>
<li><strong>Bit [1 to 0] &#8211; Word Length Select :</strong> Used to select the length of an individual data chunk. [00] for 5 bit character length. Similarly [01] , [10] , [11] for 6 , 7 , 8 bit character lengths respectively.</li>
<li><strong>Bit 2 &#8211; Stop bit select :</strong>  0 for using 1 stop bit and 1 for using 2 stop bits.</li>
<li><strong>Bit 3 &#8211; Parity Enable :</strong> 0 to disabled Partiy generation &#038; checking and 1 to enable it.</li>
<li><strong>Bit [5 to 4] &#8211; Parity Select :</strong> [00] to Odd-parity , [01] for Even-parity , [10] for forced &#8220;1&#8221;(Mark) parity  and [11] for forced &#8220;0&#8221;(Space) parity.</li>
<li><strong>Bit 6 &#8211; Break Control :</strong> 0 to disable break transmission and 1 to enable it. TxD pin will be forced to logic 0 when this bit is 1!</li>
<li><strong>Bit 7 &#8211; Divisior Latch Access bit :</strong> 0 to disable access to divisor latches and 1 to enable access.</li>
</ol>
<p><strong>3) U0LSR &#8211; Line Status Register :</strong> used to read the status of Rx and Tx blocks.</p>
<ol>
<li><strong>Bit 0 &#8211; Receiver Data Ready(RDR) :</strong> 0 means U0RBR is empty(i.e Rx FIFO is empty) and 1 means U0RBR contains valid data.</li>
<li><strong>Bit 1 &#8211; Overrun Error(OE) :</strong> 0 means Overrun hasn&#8217;t occured and 1 means Overrun has occured. Overrun is the condition when RSR(Receive Shift Register)[See note 1] has new character assembled but the RBR FIFO is full and the new assembled character is eventually lost since no data is written into FIFO if its full. (<strong>Note:</strong> <em>Reading U0LSR clears this bit</em>)</li>
<li><strong>Bit 2 &#8211; Parity Error(PE) :</strong> 0 mean no parity error and 1 mean a parity error has occured. When the value of the parity bit in the recieved character is in wrong state then a parity error occurs. (Note: Reading U0LSR clears this bit)</li>
<li><strong>Bit 3 &#8211; Framing Error(FE) :</strong> 0 means no framing error has occured and 1 means that a framing error has taken place. Framing error occurs when the stop bit of a received character is zero. (<strong>Note:</strong> <em>Reading U0LSR clears this bit</em>)</li>
<li>
<strong>Bit 4 &#8211; Break Interrupt :</strong> 0 means no Break Interrupt occures and 1 means that it has occured. A Break Interrupt occurs when the RxD line is pulled low (i.e all 0s) i.e held in spacing state for 1 full character after which Rx Block goes into Idle state. Rx Block gets back to active state when RxD pin is pulled high (i.e all 1s) i.e held in marking state for 1 full character. (<strong>Note:</strong> <em>Reading U0LSR clears this bit</em>)</li>
<li>
<strong>Bit 5 &#8211; Transmit Holding Register Empty(THRE) :</strong> 0 means U0THR contains valid data and 1 means its empty.</li>
<li>
<strong>Bit 6 &#8211; Transmitter Empty (TEMT) :</strong> 0 means U0THR and/or U0RSR contains valid data and 1 means that both U0THR and U0RSR are empty.</li>
<li><strong>Bit 7 &#8211; Error in RX FIFO(RXFE) :</strong> 0 means that U0RBR has no Rx Errors or Rx FIFO is disabled(i.e 0th bit in U0FCR is 0) and 1 means that U0RBR has atleast one error. (<strong>Note:</strong> <em>This bit is cleared only if U0LSR is read and there are no other subsequent errors in Rx FIFO .. else this bit will stay 1</em>)</li>
</ol>
<p><strong>4) U0TER &#8211; Transmit Enable Register : </strong>This register is used to enable UART transmission. When bit-7 (i.e TXEN) is set to 1 Tx block will be enabled and will keep on transmitting data as soon as its ready. If bit-7 is set to 0 then Tx will stop transmission. Other bits are reserved.</p>
<h4><strong> Interrupt Related Registers :</strong> </h4>
<p><strong>1) U0IER &#8211; Interrupt Enable Register:</strong> Set a bit to 0 to disable and 1 to enable the corresponding interrupt.</p>
<ol>
<li><strong>Bit 0</strong> &#8211; RBR Interrupt Enable </li>
<li><strong>Bit 1 </strong>&#8211; THRE Interrupt Enable</li>
<li><strong>Bit 2</strong> &#8211; RX Line Status Interrupt Enable</li>
<li><strong>Bit 3</strong> &#8211; ATBOInt Enable </li>
<li>
<strong>Bit 4</strong> &#8211; ATEOInt Enable</li>
</ol>
<p>Where ATBOInt = Auto Baud Time-Out Interrupt , ATEO = End of Auto Baud Interrupt and rest of the bits are reserved.</p>
<p><strong>2) U0IIR &#8211; Interrupt Identification Register:</strong> Refer User Manual when in doubt. In some application the usage of this register might get a bit complicated.</p>
<p>This register is organized as follows:</p>
<ol>
<li><strong>Bit 0 &#8211; Interrupt Pending :</strong> 0 means atleast one interrupt is pending , 1 means no interrupts are pending. Note: This bit is ACTIVE LOW!</li>
<li><strong>Bits [3 to 1] &#8211; Interrupt Identification :</strong> [011] is for Receive Line Status(RLS) , [010] means Receive Data Available(RDA) , 110 is for Character Time-out Indicator(CTI) , [001] is for THRE Interrupt.</li>
<li><strong>Bits [7 to 6] &#8211; FIFO Enable.</strong></li>
<li><strong>Bit 8 &#8211; ABEOInt :</strong> 1 means Auto Baud Interrupt has successfully ended and 0 otherwise.</li>
<li><strong>Bit 9 &#8211; ABTOInt :</strong> 1 means Auto Baud Interrupt has Timed-out.</li>
<li>All others bits are reserved.</li>
</ol>
</div>
<h2 class="shead">UART Baud Rate Generation:</h2>
<div class="special sp_red notestar"> <strong>Note</strong> : In real world there are very less chances that you will get the actual baudrate same as the desired baudrate. In most cases the actual baudrate will drift a little above or below the desired baud and also, as the desired baudrate increases this drift or error will also increase &#8211; this is because of the equation itself and the limitations on MULVAL , DIVADDVAL! For e.g. if the desired baud rate is 9600 and you get a baud like 9590 , 9610 , 9685 , 9615 , etc.. then in almost all cases it will work as required. In short , a small amount of error in actual baudrate is generally tolerable in most systems.</div>
<div class="special sp_blue notestar">
<h4>The master formula for calculating baud rate is given as : </h4>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/uart/baudrate.png" alt="Baud rate formula lpc2148" width="565px" height="76px" /></p>
<p><strong>which can be further simplified to :</strong></p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/uart/baudrate2.png" alt="Baud rate formula lpc2148" width="550px" height="76px" /></p>
<p>with following conditions strictly applied :</p>
<p><strong>0 < MULVAL <= 15
0 <= DIVADDVAL <= 15
if DIVADDVAL > 0 &#038; DLM = 0 then, DLL >= 2<br />
</strong></p>
<p>As it can been seen this formula has 2 prominent parts which are : A <strong>Base value</strong> and a <strong>Fractional Multiplier</strong> i.e:</p>
<p><center><strong>BaudRate = [ Base ] x [ Fraction(i.e. Prescale) ]</strong></center></p>
<p>This Fractional Multiplier can be used to scale down or keep the base value as it is .. hence its very useful for fine-tuning and getting the baudrate as accurate as possible.  </p>
</div>
<p>Where PCLK is the Peripheral Clock value in Hz , U0DLM and U0DLL are the divisor registers which we saw earlier and finally DIVADDVAL and MULVAL are part of the Fractional baudrate generator register.</p>
<p><strong>Now we know the formula .. how we do actually start ?</strong></p>
<h4> 1) The Dirty and Simplest Method: </h4>
<p>The quickest and also the dirtiest(accuracy wise) method without using any algorithm or finetuning is to set DLM=0 and completely ignore the fraction by setting it to 1. In this case MULVAL=1 and DIVADDVAL=0 which makes the Fraction Multiplier = 1. Now we are left with only 1 unknown in the equation which is U0DLL and hence we simplify the equation and solve for U0DLL.</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/uart/u0dll.png" alt="Baud rate formula lpc2148" width="330px" height="76px" /></p>
<p><strong>But I&#8217;d recommend that stay away from above method, if possible, as it works only for particular bauds given a specific PCLK value and moreover U0DLL might get out of range i.e. > 255 in which case you have to start increasing DLM and recompute a new value for U0DLL.</strong></p>
<h4> 2) The more involved method(Recommended): </h4>
<p></strong>Now lets see three examples for calculating Baud Rates Manually using some finetuning :</strong></p>
<p>In these method we again start with DLM=0 , DIVADDVAL=0 and MULVAL=1 and get an initial value for DLM. If you are lucky you will get a very close baudrate to the desired one. If not then we perform some finetuning using DLM , MULVAL and DIVADDVAL and get a new value for DLM. There is on one single method to perform finetuning and one can also make an algorithm for computing the best match given the desired baudrate and PCLK. The finetuning method which I have given below is a basic one suitable for beginners(in my opinion :P). </p>
<div class="highlight">
<h4>Ex 1 : PCLK = 30 Mhz and Required Baud Rate is 9600 bauds.</h4>
<p>Lets start with<strong> U0DLM = 0 , DIVADDVAL = 0 and MULVAL = 1<br />
We have PCLK = 30 Mhz = 30 x 10<sup>6</sup> Hz<br />
</strong></p>
<p>So the equation now gets simplified and we can find U0DLL.</p>
<p>We get U0DLL  = 195.3125 , since it must be an integer we use 195. With U0DLL = 195 our actual baud rate will be = 9615.38 with an error of +15.28 from 9600. Now all we have to do is to get this error as low as possible. This can be done by multiplying it by a suitable fraction defined using MULVAL and DIVADDVAL &#8211; as given in equation. But since MULVAL &#038; DIVADDVAL can be maximum 15 we don&#8217;t have much control over the fraction&#8217;s value. <strong>Note: <em>As per my convention I calculate BaudRate Error as</em> = Actual BaudRate &#8211; Desired BaudRate.</strong></p>
<p>Lets see how :<br />
First lets compute the required fraction value given by : <strong>[Desired Baud / Actual Baud Rate]</strong>. In our case its 0.9984. In short this is the value that needs to be multiplied by baud value we got above to bring back it to ~9600. The closest downscale value possible(<1) to 0.9948 is 0.9375 when MULVAL=15 &#038; DIVADDVAL=1. But 0.9375 is too less because 9615.38x0.9375 = 9014.42 which is way too far. Now , one thing can be done is that we set MULVAL = 15 and now start decreasing U0DLL from 195. Luckily when U0DLL = 183 we get a baud rate of 9605.53 i.e ~9605! Yea!

Hence the final settings in this case will be :

<strong>PCLK = 30 x 10<sup>6</sup> Hz<br />
U0DLL = 183<br />
U0DLM = 0<br />
MULVAL = 15<br />
DIVADDVAL = 0</strong>
</div>
<div class="highlight">
<h4>Ex 2 : PCLK = 60 Mhz and Required Baud Rate is 9600 bauds.</h4>
<p>Again start with these :<br />
<strong>U0DLM = 0 , DIVADDVAL = 0 &#038; MULVAL = 1 with PCLK = 60 x 10<sup>6</sup> Hz </strong></p>
<p>Here we get U0DLL = 390.625 (i.e. ~390 ) but hats out of range since U0DLL is 8-bit! So , now we need to change our strategy. Now , we set U0DLM = 1 and find U0DLL. In our case with U0DLM=1 we got U0DLL which was in range but this might not be the case all the time and you might need to increase U0DLM to get U0DLL in range. Our new U0DLL = 135 which gives a buad of 9590.79 with an error of -9.21 . Now we have to do a similar thing that was done above. The fractional value we need to multiply our current baud to get back to ~9600 is 1.0009 but that&#8217;s out of range(>1) for a factor(.. now don&#8217;t get surprised that was expected since the error was negative). Hence, in such case we have to use MULVAL = 15 and find new value for U0DLL. Here we get U0DLL = 110 which gives a baud of 9605.53 which is ~9605!</p>
<p>Hence the final settings in this case will be :</p>
<p><strong>PCLK = 60 x 10<sup>6</sup> Hz<br />
U0DLL = 110<br />
U0DLM = 1<br />
MULVAL = 15<br />
DIVADDVAL = 0</strong>
</div>
<div class="special sp_blue noteinfo"><strong>Note</strong> : <strong>9600</strong> is pretty famous Baud rate in embedded projects. Some of the standard Baud rates that can be used are : 2400 , 4800 , 7200 , 9600 , 14400 , 19200 , 28800 , 38400 , 57600 , 115200 , 230400 , etc..  </div>
<h2 class="shead">Configuring and Initializing UART</h2>
<p>Once you are aware of how UART communication works , configuring and initializing UART is pretty straight forward. In Source Code Examples for this tutorial we will use the following configuration :</p>
<ul>
<li>BaudRate = 9600 buads (with PCLK=60Mhz)</li>
<li>Data Length = 8 bits</li>
<li>No Parity Bit</li>
<li>and 1 Stop Bit</li>
</ul>
<p><strong>Note: Its your responsibility to make it sure that configuration is same on both communication ends.</strong></p>
<p>Now , as seen in Ex. 2  &#8211; in order to get 9600(9605 actually) bauds at 60Mhz PCLK we must use the following settings for baud generation :<br />
<strong></p>
<div class="highlight">
U0DLL = 110 ; U0DLM = 1 ; MULVAL = 15 ; DIVADDVAL = 0<br />
(You can convert these values to Hex =or= direclty use it in decimal form)</strong>
</div>
<p>Now , lets make a function named &#8220;<strong>InitUART0()</strong>&#8221; which will configure and initialize UART0 as required :</p>
<pre><code class="language-cpp">
void InitUART0(void)
{
  PINSEL0 = 0x5;  /* Select TxD for P0.0 and RxD for P0.1 */ 
  U0LCR	= 3 | (1<<7) ; /* 8 bits, no Parity, 1 Stop bit | DLAB set to 1  */
  U0DLL	= 110;
  U0DLM = 1;	
  U0FDR = (MULVAL<<4) | DIVADDVAL; /* MULVAL=15(bits - 7:4) , DIVADDVAL=0(bits - 3:0)  */
  U0LCR &#038;= 0x0F; // Set DLAB=0 to lock MULVAL and DIVADDVAL
  //BaudRate is now ~9600 and we are ready for UART communication!
 
 }
</code></pre>
<p>Once this is done you are now ready to transfer data using U0RBR and U0THR registers.</p>
<h2 class="shead">Connections between MCU and PC or Laptop</h2>
<p>Here we can have 2 possible scenarios.</p>
<ul>
<li><strong>Scenario 1 :</strong> Your PC/Laptop already has a serial port. In this case you will need a RS232 to TTL converter. If your development board has on-board RS232 to TTL chip (like Max232) .. then you just need to plugin the serial cable on both sides. Just make sure that serial port is for UART0.</li>
<li>
<strong>Scenario 2 :</strong> Your PC/Laptop doesn't have a serial port and you are using USB to Serial converter based on FTDI's chip.</li>
</ul>
<div class="highlight">If you are using a separate converter then in both cases the TTL side of the converter will have 3 pins at bare minimum which are: TX , RX and GND. Connect your MCU RX to TX of converter and MCU TX to RX of converter. Finally connect GND on both sides. Make sure you module supports 3.3v voltage-levels.</div>
<p>Please refer to the connection diagrams & terminal software configuration(and COM ports) as given in <a href="https://www.ocfreaks.com/uart-tutorial-basics/">Basic Uart Tutorial</a>.</p>
<p>Here is a configuration screenshot for  terminal software PuttyTel which we will be using for examples :<br />
<img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/uart/puttytel-config.jpg" /></p>
<p><strong>Note</strong> : You can get PuttyTel from <a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html">Here</a>. Direct download link : <a href="http://the.earth.li/~sgtatham/putty/latest/x86/puttytel.exe">Here</a>. You can also use other similar terminal software as well.</p>
<p><span class="shead"><span class="sheadin"> Source Code Examples</span></span><br />
Now its time to actually use UART in real life! Lets do some communication between your lpc2148 MCU and PC/Laptop. Before we get into actual examples for LPC2148 , first lets define 2 functions which will be used to Read and Write Data from UART block.</p>
<h4>U0Read() - Read Data from UART0:</h4>
<pre><code class="language-cpp">
#define RDR (1<<0) // Receiver Data Ready
char U0Read(void)
{
     while( !(U0LSR &#038; RDR ) ); // wait till any data arrives into Rx FIFO
     return U0RBR; 
}
</code></pre>
<h4>U0Write() - Write Data to UART0:</h4>
<pre><code class="language-cpp">
#define THRE (1<<5) // Transmit Holding Register Empty
void U0Write(char data)
{
     while ( !(U0LSR &#038; THRE ) ); // wait till the THR is empty
     // now we can write to the Tx FIFO
     U0THR = data;
}
</code></pre>
<h4>Example 1 :</h4>
<p>If everything goes good then you will see a text saying "Hello from LPC2148!" on a new line repeatedly. </p>
<div class="special sp_blue noteinfo"> <strong>Attention Plz!</strong> : The source for function <strong>initClocks()</strong> and other functions used by it is given in the downloadable '.ZIP' Project File which is attached below. <strong>The given code below is just a snippet from the actual Project source file</strong>. This is also Valid for Example 2.</div>
<pre><code class="language-cpp">
/*
(C) Umang Gajera | Power_user_EX - www.ocfreaks.com 2011-13.
More Embedded tutorials @ www.ocfreaks.com/cat/embedded
LPC2148 Basic UART Tutorial - Example 1 Source Code.
License : GPL.
*/

#include &lt;lpc214x.h&gt;

#define PLOCK 0x00000400 // for PLL
#define THRE (1<<5) // Transmit Holding Register Empty
#define MULVAL 15
#define DIVADDVAL 1
#define NEW_LINE 0xA // Character for new line .. analogous to '\n'

void initUART0(void);
void U0Write(char data);
void initClocks(void); // code given in project files

int main(void)
{
	char msg[] = { 'H','e','l','l','o',' ','f','r','o','m',' ','L','P','C','2','1','4','8','\0' }; 
	int c=0; // counter
	
	initClocks(); // Set CCLK=60Mhz and PCLK=60Mhz 
	initUART0();
	
	for(;;)		
	{
		while( msg[c]!='\0' )
		{
			U0Write(msg[c]);
			c++;
		}
		U0Write(NEW_LINE); //get to the next line below
		c=0; // reset counter		
	}
	
	return 0;
}

void initUART0(void)
{
	PINSEL0 = 0x5;  /* Select TxD for P0.0 and RxD for P0.1 */
	U0LCR = 3 | (1<<7) ; /* 8 bits, no Parity, 1 Stop bit | DLAB set to 1  */
	U0DLL = 110;
	U0DLM = 1;   
	U0FDR = (MULVAL<<4) | DIVADDVAL; /* MULVAL=15(bits - 7:4) , DIVADDVAL=0(bits - 3:0)  */
	U0LCR &#038;= 0x0F; // Set DLAB=0 to lock MULVAL and DIVADDVAL
	//BaudRate is now ~9600 and we are ready for UART communication!
}

void U0Write(char data)
{
	while ( !(U0LSR &#038; THRE ) ); // wait till the THR is empty
	// now we can write to the Tx FIFO
	U0THR = data;
}
</code></pre>
<div class="highlight"><strong>Download Project Source for Example #1 @</strong> <a href='https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC214x_UART_Tutorial_Example_1.zip'>LPC214x UART Tutorial Example 1.zip</a> [Successfully tested on Keil UV4.70a] </div>
<h4>Example 2 :</h4>
<p>In this example you will see the characters that you type on your keyboard - seems like typing in an editor? NO!. The serial Terminal only displays the character that it receives via serial Port. When you type a character it is directly sent to the other side via serial port. So , in order for us to see the character which we typed, we need to send back the same character i.e. echo it back to PC. This is what the program below does. As soon as the MCU receives a character from the Rx pin .. it then sends the same character back to PC via the Tx Pin and it gets displayed in the Serial Terminal.</p>
<p>Here is a snippet of Example 2 : (full source code given in project files - attached below)</p>
<pre><code class="language-cpp">
/*
(C) Umang Gajera | Power_user_EX - www.ocfreaks.com 2011-13.
More Embedded tutorials @ www.ocfreaks.com/cat/embedded

LPC2148 Basic UART Tutorial - Example 2 Source Code.
License : GPL.
*/

#include &lt;lpc214x.h&gt;

#define THRE (1<<5) // Transmit Holding Register Empty
#define RDR (1<<0) // Receiver Data Ready
#define MULVAL 15
#define DIVADDVAL 1
#define NEW_LINE 0xA // Character for new line .. analogus '\n'
#define ENTER 0xD // Ascii code for Enter 

void initUART0(void); // same code as in example 1
void U0Write(char data); // same code as in example 1
char U0Read(void); 
void initClocks(void); // code given in project files

int main(void)
{
	initClocks(); // Set CCLK=60Mhz and PCLK=60Mhz 
	initUART0();
	
	while(1)		
	{
		char c = U0Read(); // Read Data from Rx
		if( c == ENTER ) // Check if user pressed Enter key
		{
			U0Write(NEW_LINE); // Send New Line ASCII code change line
		}
		else
		{
			U0Write(c); // Write it to Tx to send it back
		}
	}
	
	return 0;
}

char U0Read(void)
{
	while( !(U0LSR &#038; RDR ) ); // wait till any data arrives into Rx FIFO
	return U0RBR;
}
</code></pre>
<div class="highlight"><strong>Download Project Source for Example #1 @</strong> <a href='https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC214x_UART_Tutorial_Example_2.zip'>LPC214x UART Tutorial Example 2.zip</a> [Successfully tested on Keil UV4.70a] </div>
<p><strong>LPC2148 Development Boards that we Recommend:</strong></p>
<p><a href="https://www.amazon.in/NXP-ARM7-LPC2148-Header-Board/dp/B01LX5AN08/ref=as_li_ss_il?_encoding=UTF8&#038;psc=1&#038;refRID=1ATG383W1SGQG4FNZN0R&#038;linkCode=li2&#038;tag=o0cd4-21&#038;linkId=619cbbc7eb302c8ca4af6877b8c1152c" target="_blank"><img decoding="async" border="0" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&#038;ASIN=B01LX5AN08&#038;Format=_SL160_&#038;ID=AsinImage&#038;MarketPlace=IN&#038;ServiceVersion=20070822&#038;WS=1&#038;tag=o0cd4-21" ></a><img decoding="async" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&#038;l=li2&#038;o=31&#038;a=B01LX5AN08" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /><a href="https://www.amazon.in/Development-Board-LPC2148-USB-FT232-Driver-memory/dp/B00T04NLYE/ref=as_li_ss_il?ie=UTF8&#038;qid=1491577410&#038;sr=8-8&#038;keywords=lpc2148&#038;linkCode=li2&#038;tag=o0cd4-21&#038;linkId=8628d2ee5db112ddbaab7aaedc6ec22f" target="_blank"><img decoding="async" border="0" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&#038;ASIN=B00T04NLYE&#038;Format=_SL160_&#038;ID=AsinImage&#038;MarketPlace=IN&#038;ServiceVersion=20070822&#038;WS=1&#038;tag=o0cd4-21" ></a><img decoding="async" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&#038;l=li2&#038;o=31&#038;a=B00T04NLYE" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /><a href="https://www.amazon.in/LPC2148-Development-Board-Standard-Package/dp/B019QTWRVU/ref=as_li_ss_il?ie=UTF8&#038;qid=1491577410&#038;sr=8-1&#038;keywords=lpc2148&#038;linkCode=li2&#038;tag=o0cd4-21&#038;linkId=1f88a47711dbb1f232ece64e5a4a79d3" target="_blank"><img decoding="async" border="0" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&#038;ASIN=B019QTWRVU&#038;Format=_SL160_&#038;ID=AsinImage&#038;MarketPlace=IN&#038;ServiceVersion=20070822&#038;WS=1&#038;tag=o0cd4-21" ></a><img decoding="async" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&#038;l=li2&#038;o=31&#038;a=B019QTWRVU" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /><a href="https://www.amazon.in/Explore-Embedded-ARM7-LPC2148-Dev/dp/B018XJ0F5Y/ref=as_li_ss_il?ie=UTF8&#038;qid=1491577410&#038;sr=8-5&#038;keywords=lpc2148&#038;linkCode=li2&#038;tag=o0cd4-21&#038;linkId=bc5b00fa1663b938453ff3d765a3e165" target="_blank"><img decoding="async" border="0" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&#038;ASIN=B018XJ0F5Y&#038;Format=_SL160_&#038;ID=AsinImage&#038;MarketPlace=IN&#038;ServiceVersion=20070822&#038;WS=1&#038;tag=o0cd4-21" ></a><img loading="lazy" decoding="async" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&#038;l=li2&#038;o=31&#038;a=B018XJ0F5Y" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-uart-programming-tutorial/">LPC2148 UART Programming Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/lpc2148-uart-programming-tutorial/feed/</wfw:commentRss>
			<slash:comments>14</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1860</post-id>	</item>
		<item>
		<title>Sine Wave Generator using PWM with LPC2148 Microcontroller Tutorial</title>
		<link>https://www.ocfreaks.com/sine-wave-generator-using-pwm-lpc2148-microcontroller-tutorial/</link>
					<comments>https://www.ocfreaks.com/sine-wave-generator-using-pwm-lpc2148-microcontroller-tutorial/?noamp=mobile#comments</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Thu, 12 Sep 2013 17:12:09 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=1535</guid>

					<description><![CDATA[<p>Introduction I had received 3 requests for &#8220;how to generate sine PWM using lpc2148 microcontroller&#8220;. So , I planned to do it as soon as I could. In previous article I had posted a tutorial for PWM in LPC2148 @ Here. This tutorial is just an extension to that. The basic idea here is to [&#8230;]</p>
<p>The post <a href="https://www.ocfreaks.com/sine-wave-generator-using-pwm-lpc2148-microcontroller-tutorial/">Sine Wave Generator using PWM with LPC2148 Microcontroller Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/lpc2148-sine-pwm/cover.jpg" /></p>
<p><span class="shead"><span class="sheadin">Introduction</span></span></p>
<p>I had received 3 requests for &#8220;<strong>how to generate sine PWM using lpc2148 microcontroller</strong>&#8220;. So , I planned to do it as soon as I could. In previous article I had posted a tutorial for PWM in LPC2148 @ <a href="https://www.ocfreaks.com/lpc2148-pwm-programming-tutorial/">Here</a>. This tutorial is just an extension to that.</p>
<p>The basic idea here is to generate or synthesize a sine wave by passing a digitally generated PWM through a low pass filter. This is technically called &#8220;<strong>Direct Digital Synthesis</strong>&#8221; or <strong>DDS</strong> for short. This technique can be also used to generate other waveforms like sawtooth , square , etc.. Generally lookup tables are used to make it easier and faster. Also just by changing the lookup table we can change the wave form. I&#8217;ll explain lookup tables as we proceed.</p>
<p><span class="shead"><span class="sheadin">The Basics</span></span></p>
<p>When we pass a PWM signal through a corresponding low pass filter then we get an analog voltage(or say Signal) at the output which is proportional to the dutycycle and given by:</p>
<div class="special sp_blue notestar">
<h4>V<sub>out</sub> = V<sub>H</sub> x Dutycycle</h4>
<p>Where V<sub>H</sub> is Voltage for Logic HIGH for PMW pulse. In our case its 3.3V.
</div>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/lpc2148-sine-pwm/pwm-rc-filter.jpg" /></p>
<p>Now, when we change the dutcycle i.e the T-ON time of PWM in sine fashion we get a sine waveform at the filter output. The basic idea here is to divide the waveform we want , Sine wave in our case , into &#8216;x&#8217; number of divisions. For each division we have a single PWM cycle. The T-ON time or the duty cycle directly corresponds to the amplitude of the waveform in that division which is calculated using sin() function. Consider the diagram shown below :</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/lpc2148-sine-pwm/sine-pwm-basics.jpg" /></p>
<p>Here I&#8217;ve divided the Sine wave into 10 divisions. So here we will require 10 different PWM pulses increasing &#038; decreasing in sinusoidal manner. A PWM pulse with 0% Duty cycle will represent the min amplitude(0V) , the one with 100% duty cycle will represent max amplitude(3.3V). Since out PWM pulse has voltage swing between 0V to 3.3V , our sine wave too will swing between 0V to 3.3V.</p>
<p>It takes 360 degrees for a sine wave to complete one cycle. Hence for 10 divisions we will need to increase the angle in steps of 36 degrees. This is called the<strong> Angle Step Rate or Angle Resolution.<br />
</strong><br />
Now we can increase the number of divisions to get more accurate waveform. But as divisions increase we also need to increase the resolution .. but we cant increase the resolution after we reach the max resolution which depends on the Processor(&#038; Peripheral) clock.</p>
<div class="highlight"><strong>Resolution(PWM)</strong> : Its the minimum increment required to increase or decrease the pwm T-ON time i.e Duty Cycle. For e.g if we have a resolution of 1us then valid T-ON values would be 1,2,3,.. and if we have a resolution of 0.5us then valid T-ON values will be 1,1.5,2,2.5,.. (i.e now we get double the values)</p>
<p><strong>Note :</strong> A high(i.e more Fine) Resolution in our case means that the actual value is low and vice-verse. For eg. 0.25us is a higher Resolution as compared to 1us. &#8211; This is my convention others might use it in reverse.
</div>
<h2 class="shead">50 Hz Sinewave using Microcontroller based PWM</h2>
<p>The above was just a simple example to explain the basic stuff. Now lets the actually example that I&#8217;ll be using. <strong>Here we will be generating a 50Hz Sine wave with 200 divisions.</strong></p>
<p>To get the output waveform as smooth as possible we need to calculate the best possible divisions and resolution .. given we know the frequency of the output waveform. In our case will be generating a 50Hz sine wave using PWM signal generated by lpc2148 microcontroller.</p>
<p>Now for a 50Hz sine wave we get a period time 1/50 = 20 milliseconds which is the time required for the sine wave to complete 1 full cycle. Now our job is divide an interval of 20ms into &#8216;X&#8217; divisions. Hence the number of division i.e X will decide the period of PWM signal and hence the best possible resolution can be chosen. </p>
<div class="special sp_red noteinfo"><strong>Note : </strong>The first thing that we must keep in mind is that we need the resolution to be a rational number like 0.25 and not 0.1666666&#8230; to keep things more accurate and calculations straight forward. Given that maximum speed of lpc2148 is 60mhz it would take 1us(micro-second) for 60 ticks of CPU clock(assuming CPU and Peripheral Clocks are at same speed) , 0.1 us for 6 ticks , 0.05us for 3 ticks and 0.0166666&#8230; for 1 tick. Hence the maximum resolution will be 0.016666..us i.e 16.6666..ns(nano-seconds). But since its an irrational number the best maximum resolution we can use is 0.05 us or 50ns. But in our case we wont be using such fine resolution since our number or divisions are less. If our number of division would have been more than we could have used 50ns as our resolution. For our case 0.5us resolution is enough.</div>
<p>Now , <strong>since we will be using 200 divisions , the period of each division will be 20ms/200 = 0.1ms = 100us</strong>. This means that the period of the pwm signal will be 100us. Hence if we use a resolution of 1us then we will only have a set 100 values(actually 101 values if you consider 0 also) ranging from 1 to 100 to describe a full sine wave cycle. If we use a resolution of 0.5ms then we will have a set of 200 values. These values are the match values i.e the number of ticks of PWM counter. So if the resolution is 0.5us and period is 100 us then we will require 200 ticks to complete 100us. Similarly if resolution is 0.25us we will require 400 ticks for 100us.</p>
<div class="highlight"><center><strong>Max Number of PWM counter Ticks = PWM Period / Resolution</strong></center><br />
This gives us the Maximum value for the match register i.e 100% Dutycycle. </p>
<p><strong>NOTE: The higher this count is the more accurate the sine wave will be &#8211; given we have correspondingly high number of divisions!</strong>.
</div>
<h2 class="shead">Generating the Sine lookup table</h2>
<p>In our case since our period time is 100us we will require max 200 ticks @ 0.5us resolution. So in a lot of 200 divisions or PWM cycles the match value i.e the no.of ticks must vary from 0 to 200 in a sine fashion. This can done by finding the angle for the corresponding division. So now we must first find the angle step rate or the angle resolution , which can be given by:</p>
<div class="highlight"><center><strong>Angle step rate = 360 / No.of divisions</strong></center></div>
<p>In our case we get Angle step rate = <strong>1.8 degrees</strong>. Hence the 1st division represents 0 degrees , 2nd division 1.8 degs , 3rd division 3.6 degs and so on.</p>
<p>Now , by multiplying the sine of angle with 50% T-ON value we can get the T-ON values for a complete cycle. But since sine gives negative values for angle between 180 and 360 we need to scale it since PWM Match values cannot be negative. Again we use 50% T-ON and add it so all values are positive. This is given by :</p>
<pre><code class="language-cpp">
SineLookupTable[Y] = rint( 100 + 100 * sin( Angle_Step_Rate*Y ) ); 

//where Y=0,1,...X-1 & X=no.of Divisions
</code></pre>
<p><strong>Note</strong> that the function &#8220;<strong>rint(..)</strong>&#8221; is used to round the computed Match Value to the nearest integer. </p>
<p>Using the above equation the min amplitude will be 0V(match val=0) and max 3.3V(match val=200). In order to limit the amplitude between say 0.05V(match val=1) and 3.25V(match val=199) we can modify the equation by reducing the sine scale factor from 100 to 99. So , the equation in this case will be :</p>
<pre><code class="language-cpp">
SineLookupTable[Y] = rint( 100 + 99 * sin( Angle_Step_Rate*Y ) ); 

//where Y=0,1,...X-1 & X=no.of Divisions
</code></pre>
<div class="spaced">
</div>
<h2 class="shead">Basic Digital(PWM) to Analog Filter Design</h2>
<p>We need a <strong>Low Pass filter(LPF)</strong> which we will be using as <strong>Digital to Analog Converter i.e DAC</strong> for converting PWM into Sine. This Low Pass Filter will block the high frequency PWM signal and will let the &#8216;encoded&#8217; low frequency sine wave to pass through. To design this filter , first we must decide the Cut-Off or Corner frequency. Here we can follow a rule of thumb that the cut-off frequency must be 3 to 4 times the frequency we want at the output. Since we want 50hz sine at the output we will design a simple low pass RC filter with cut-off frequency of around 200hz.</p>
<div class="special sp_blue notestar">The Cut-off Frequency for a RC Filter is given by:</p>
<p><img decoding="async" class="aligncenter" src="http://localhost/imgs/lpc2148-tutorial/lpc2148-sine-pwm/rc_cut_off_frequency.png" alt="RC Cut off frequency" /></p>
<p>Here the Time Constant is <strong>T<sub>c</sub> = R * C</strong></p>
<p>So , from the above equation we get the equation for Time Constant as :</p>
<p><img decoding="async" class="aligncenter" src="http://localhost/imgs/lpc2148-tutorial/lpc2148-sine-pwm/rc_time_constant.png" alt="RC Time Constant" /></p>
<p>Now we can Find the Time Constant T<sub>c</sub> from F<sub>cut-off</sub> and then select <strong>appropriate</strong> values for R and C.
</div>
<p>So , In our case with <strong>F<sub>cut-off</sub>=200 , we get T<sub>c</sub>=796us</strong>. Hence in our case we can select a <strong>1.8K Resistor and a 0.47uF Capacitor</strong> which gives a Time constant of <strong>846us</strong>.</p>
<p>If you don&#8217;t have R &#038; C with those values and just want to check the Sine wave output &#8211;<strong> A Quick &#038; Dirty method to make an RC filter</strong> for the PWM in <strong>our case</strong> can be as follows &#8211; use a resistor between 1.8K and 5.6K and a Capacitor between 0.33uF and 1uF. For example you may use a 4.7K Resistor with a 0.47uF or 1uF Capacitor. </p>
<p>Note : If you need more better Sine Wave you can use a Low-Pass RC or Chebyeshev Filter of Higher order.</p>
<h2 class="shead">The Sine Wave Output in Oscilloscope</h2>
<p>Here is the output for the code(given in Source Code section below). It generates a Sine Wave having a frequency of 50Hz after filter of-course.</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/lpc2148-sine-pwm/sine-filter-output.jpg" /></p>
<p>Here is a closeup of the Sine wave :</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/lpc2148-sine-pwm/sine-closeup.jpg" /></p>
<p>As it can be seen the Sine wave it not completely smooth since I&#8217;ve used a simple RC filter. A better Sine wave get be obtained by using a good higher order filter.</p>
<h2 class="shead">Sinewave PWM Source code</h2>
<p>In the Code given Below First I am generating a Sine Lookup Table with 200 entries and then Setting up PWM ISR which keeps on updating the Match Value for Match Register 1 after the end of each PWM cycle. The function &#8220;<strong>initClocks()</strong>&#8221; initializes the CPU and Peripheral Clocks at 60Mhz &#8211; its code is given in attached files. [<strong>Note</strong> &#8211; The startup file i.e startup.s by itself initializes CLK and PLCK @ 60Mhz .. even then I am doing it again for those who might want to edit my code and change the clocks as required]</p>
<pre><code class="language-cpp">
/*
(C) Umang Gajera | Power_user_EX - www.ocfreaks.com 2011-13.
More Embedded tutorials @ www.ocfreaks.com/cat/embedded

Source for LPC2148 Sine PWM Tutorial
License : GPL.
*/
#include &lt;lpc214x.h&gt;
#include &lt;math.h&gt;
#define PLOCK 0x00000400
#define PWMPRESCALE 30 //30 PCLK cycles to increment TC by 1 i.e 0.5 Micro-second 
#define ANGLE_STEP_RATE 1.8 //i.e the Angle Resolution
#define PI 3.14159

void initPWM(void);

void initClocks(void);
void setupPLL0(void);
void feedSeq(void);
void connectPLL0(void);
void computeSineLookupTable(void);
void PWM_ISR(void) __irq;

char sineLookupTable[200];
int count=0;

int main(void)
{
	initClocks(); //Initialize CPU and Peripheral Clocks @ 60Mhz
	
	computeSineLookupTable(); //Compute Sine Lookup Table with 200 Divisions
	
	VICIntEnable |= (1<<8) ; 
	VICVectCntl0 = (1<<5) | 8 ;
	VICVectAddr0 = (unsigned) PWM_ISR;
	
	initPWM(); //Initialize PWM

	//Sinusoidal PWM Generation is Active Now!
   
	while(1){}
	
	//return 0; //normally this wont execute ever
}

void computeSineLookupTable(void)
{
	float angle;
	int i;
	for(i=0; i<200; i++)
	{
		angle = ( ((float)i) * ANGLE_STEP_RATE );
		sineLookupTable[i] = rint(100 + 99* sin( angle * (PI/180) )); //converting angle to radians
	}
}

void PWM_ISR(void) __irq
{
	long regVal = PWMIR;									
	
	PWMMR1 = sineLookupTable[count]; //current amplitude
	PWMLER = (1<<1); // set Latch Bit to High 
	count++;
	if(count>199) count = 0;
	
	PWMIR = regVal; // Clear Flags
	VICVectAddr = 0xFF; //End of ISR
}

void initPWM(void)
{
	/*Assuming that PLL0 has been setup with CCLK = 60Mhz and PCLK also = 60Mhz.*/

	PINSEL0 = (1<<1); // Select PWM1 output for Pin0.0
	PWMPCR = 0x0; //Select Single Edge PWM - by default its single Edged so this line can be removed
	PWMPR = PWMPRESCALE-1; // 0.5 micro-second resolution
	PWMMR0 = 200; // 200 ticks i.e 200x0.5=100ms period duration
	PWMMR1 = 100; // Initial pulse duration i.e width
	PWMMCR = (1<<1) | (1<<0); // Reset PWMTC &#038; Interrupt on PWMMR0 match
	PWMLER = (1<<1) | (1<<0); // update MR0 and MR1
	PWMPCR = (1<<9); // enable PWM output
	PWMTCR = (1<<1) ; //Reset PWM TC &#038; PR

	//Now , the final moment - enable everything
	PWMTCR = (1<<0) | (1<<3); // enable counters and PWM Mode

	//PWM Generation goes active now!!
	//Now you can get the PWM output at Pin P0.0!
}

</code></pre>
<div class="highlight"><strong>Download Project Source for Sine Wave Generator Using LPC2148 @</strong> <a href='https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC214x_Sine_PWM_Tutorial.zip'>LPC214x Sine PWM</a> [Successfully tested on Keil UV4.70a , UV4.00d & UV3.31 ] </div>
<p><strong>LPC2148 Development Boards that we Recommend:</strong></p>
<p><a href="https://www.amazon.in/NXP-ARM7-LPC2148-Header-Board/dp/B01LX5AN08/ref=as_li_ss_il?_encoding=UTF8&#038;psc=1&#038;refRID=1ATG383W1SGQG4FNZN0R&#038;linkCode=li2&#038;tag=o0cd4-21&#038;linkId=619cbbc7eb302c8ca4af6877b8c1152c" target="_blank"><img decoding="async" border="0" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&#038;ASIN=B01LX5AN08&#038;Format=_SL160_&#038;ID=AsinImage&#038;MarketPlace=IN&#038;ServiceVersion=20070822&#038;WS=1&#038;tag=o0cd4-21" ></a><img loading="lazy" decoding="async" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&#038;l=li2&#038;o=31&#038;a=B01LX5AN08" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /><a href="https://www.amazon.in/Development-Board-LPC2148-USB-FT232-Driver-memory/dp/B00T04NLYE/ref=as_li_ss_il?ie=UTF8&#038;qid=1491577410&#038;sr=8-8&#038;keywords=lpc2148&#038;linkCode=li2&#038;tag=o0cd4-21&#038;linkId=8628d2ee5db112ddbaab7aaedc6ec22f" target="_blank"><img decoding="async" border="0" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&#038;ASIN=B00T04NLYE&#038;Format=_SL160_&#038;ID=AsinImage&#038;MarketPlace=IN&#038;ServiceVersion=20070822&#038;WS=1&#038;tag=o0cd4-21" ></a><img loading="lazy" decoding="async" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&#038;l=li2&#038;o=31&#038;a=B00T04NLYE" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /><a href="https://www.amazon.in/LPC2148-Development-Board-Standard-Package/dp/B019QTWRVU/ref=as_li_ss_il?ie=UTF8&#038;qid=1491577410&#038;sr=8-1&#038;keywords=lpc2148&#038;linkCode=li2&#038;tag=o0cd4-21&#038;linkId=1f88a47711dbb1f232ece64e5a4a79d3" target="_blank"><img decoding="async" border="0" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&#038;ASIN=B019QTWRVU&#038;Format=_SL160_&#038;ID=AsinImage&#038;MarketPlace=IN&#038;ServiceVersion=20070822&#038;WS=1&#038;tag=o0cd4-21" ></a><img loading="lazy" decoding="async" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&#038;l=li2&#038;o=31&#038;a=B019QTWRVU" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /><a href="https://www.amazon.in/Explore-Embedded-ARM7-LPC2148-Dev/dp/B018XJ0F5Y/ref=as_li_ss_il?ie=UTF8&#038;qid=1491577410&#038;sr=8-5&#038;keywords=lpc2148&#038;linkCode=li2&#038;tag=o0cd4-21&#038;linkId=bc5b00fa1663b938453ff3d765a3e165" target="_blank"><img decoding="async" border="0" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&#038;ASIN=B018XJ0F5Y&#038;Format=_SL160_&#038;ID=AsinImage&#038;MarketPlace=IN&#038;ServiceVersion=20070822&#038;WS=1&#038;tag=o0cd4-21" ></a><img loading="lazy" decoding="async" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&#038;l=li2&#038;o=31&#038;a=B018XJ0F5Y" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>
<p>The post <a href="https://www.ocfreaks.com/sine-wave-generator-using-pwm-lpc2148-microcontroller-tutorial/">Sine Wave Generator using PWM with LPC2148 Microcontroller Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/sine-wave-generator-using-pwm-lpc2148-microcontroller-tutorial/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1535</post-id>	</item>
		<item>
		<title>LPC2148 PWM Programming Tutorial</title>
		<link>https://www.ocfreaks.com/lpc2148-pwm-programming-tutorial/</link>
					<comments>https://www.ocfreaks.com/lpc2148-pwm-programming-tutorial/?noamp=mobile#comments</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Wed, 21 Aug 2013 16:59:29 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<category><![CDATA[mcu]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=1462</guid>

					<description><![CDATA[<p>Introduction This Tutorial is for learning Pulse Width Modulation (PWM) programming for ARM7 based LPC214x MCUs. For this Tutorial I&#8217;ll be covering Single Edge PWM and how to use to control Motors like Servos. Also for the scope of this tutorial I won&#8217;t be going into stuff like MCU controlled Voltage Regulation using PWM since [&#8230;]</p>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-pwm-programming-tutorial/">LPC2148 PWM Programming Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2 class="shead"> Introduction </h2>
<p>This Tutorial is for learning <strong>Pulse Width Modulation</strong> (PWM) programming for ARM7 based <strong>LPC214x</strong> MCUs. For this Tutorial I&#8217;ll be covering Single Edge PWM and how to use to control Motors like Servos. Also for the scope of this tutorial I won&#8217;t be going into stuff like MCU controlled Voltage Regulation using PWM since I am planning a dedicated tutorial for that. I assume you are comfortable with Timers and its usage in LPC214x since PWM block is basically a Timer. I&#8217;ve posted a tutorial on Timers in LPC214x @ <a href="https://www.ocfreaks.com/lpc2148-timer-tutorial/">LPC2148 Timer Tutorial</a> &#8211; just in case if you need it.</p>
<h4> Pulse Width Modulation Basics : </h4>
<p>I&#8217;ve Posted a Beginner PWM tutorial @ <a href="https://www.ocfreaks.com/pulse-width-modulation-pwm-tutorial/">Basic PWM Tutorial</a>. If you are new to PWM please have a look there.</p>
<p>Here is Diagram that sums it up. It shows Single Edge PWM with T-ON , T-OFF &#038; Period. Duty Cycle is simply T-ON Divided by Period.</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/embedded/common/pwm_basics.png" alt="PWM timing showing T-on, T-off and Period" width="425px" height="180px" /></p>
<h2 class="shead"> PWM Programming in ARM7 LPC2148 </h2>
<p>LPC2148 supports 2 types of PWM :<br />
<strong>1) Single Edge PWM</strong> &#8211; Pulse starts with new Period i.e pulse is always at the beginning<br />
<strong>2) Double Edge PWM</strong> &#8211; Pulse can be anywhere within the Period</p>
<p>A PWM block , like a Timer block , has a Timer Counter and an associated Prescale Register along with Match Registers. These work exactly the same way as in the case of Timers. I&#8217;ve explained them in the introduction part of the Lpc214x Timer tutorial @ <a href="https://www.ocfreaks.com/lpc2148-timer-tutorial/">LPC2148 Timer Tutorial</a> . Match Registers 1 to 6 (except 0) are pinned on LPC214x i.e. the corresponding outputs are diverted to actual Pins on LPC214x MCU. The PWM function must be selected for these Pins to get the PWM output. These pins are :<br />
<center></p>
<table>
<tr>
<td> <strong>Output :</strong></td>
<td> PWM1</td>
<td>PWM2 </td>
<td>PWM3 </td>
<td>PWM4 </td>
<td>PWM5 </td>
<td>PWM6 </td>
</tr>
<tr>
<td><strong>Pin Name :</strong></td>
<td>P0.0 </td>
<td>P0.7 </td>
<td>P0.1 </td>
<td>P0.8 </td>
<td>P0.21 </td>
<td>P0.9 </td>
</tr>
</table>
<p></center></p>
<div class="special sp_blue noteinfo"><strong>Note :</strong> PWM1 output corresponds to PWM Match Register 1 i.e PWMMR1 , PWM2 output corresponds to PWMMR2 , and so on. Also Match Register 0 i.e PWMMR0 is NOT pinned because it is used to generate the PWM Period. Also the PWM function must be selected for the PINs mentioned above using appropriate PINSEL registers (PINSEL0 for PWM1,2,3,4,6 and PINSEL1 for PWM5).</div>
<p>In LPC214x we have 7 match registers inside the PWM block . Generally the first Match register PWMMR0 is used to generate PWM period and hence we are left with 6 Match Registers PWMMR1 to PWMMR6 to generate 6 Single Edge PWM signals or 3 Double Edge PWM signals. Double edge PWM uses 2 match registers hence we can get only 3 double edge outputs. The way this works in case of Single Edge PWM is as follows (and similarly for Double Edge):   </p>
<div class="intendb">
<ol>
<li>
Consider that our PWM Period duration is 5 milliseconds and TC increments every 1 millisecond using appropriate prescale value.
</li>
<li>Now we set the match value in PWMMR0 as 5 , <strong>Note : This period will be same for all PWM outputs!</strong></li>
<li>Then we configure PWM block such that when TC reaches value in PWMMR0 it is reset and a new Period begins.</li>
<li>Now lets say we want 2 PWM signals of Pulse widths 2ms and 4ms.</li>
<li>So .. we can use PWMMR1 and PWMMR2 to get the 2 outputs. For that we set PWMMR1 = 2 and PWMMR2 = 4.
</li>
<li>Then , Everytime a new period starts the Pin corresponding to PWMMR1 and PWMMR2 will be set High by default.</li>
<li>And .. Whenever the value in TC reaches PWMMR1 and PWMMR2 its output will be set to low respectively.</li>
<li>Their outputs will remain low until the next Period starts after which their outputs again become high .. hence giving us Single Edge PWM.</li>
</ol>
</div>
<p>This can be shown in the diagram below:</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/embedded/common/lpc_pwm_timing_diagram.png" alt="ARM LPC Microcontroller PWM Module Timing Diagram" width="525px" height="325px" /></p>
<div class="highlight">
<strong>PWM Rules: </strong>The LPC214x User Manual mentions Rules for using Single Edge and Double Edge PWM on page 261 . The Rules for Single Edged PWM are as :</p>
<ul>
<li>1) All single edged PWM outputs will go high at the beginning of a PWM cycle unless their match value is 0.</li>
<li>2) Each PWM output will go low when its match value is reached. Note : If no match occurs i.e Match value is greater than Period then the output will remain high!</li>
</ul>
</div>
<p>For Double Edge Rules you can refer the User Manual <strong>Pg. 261</strong>.</p>
<div class="special sp_red noteinfo"><strong>Note 1:</strong> The last 6 Match Registers i.e PWMMR1 to PWMMR6 have an associated PIN from which we get the PWM outputs. The PWM block controls the output of these pins depending on the value in its corresponding Match Register. For Single Edged PWM these Pins are set to High by default when a new Period starts i.e when TC is reset as per the PWM Rules given above.</p>
<p><strong>Note 2:</strong> Note that Here we are storing the width of the Pulse in the Match Register. We can also change the width of the pulse by changing the value in the corresponding Match register using a special register called Latch Enable Register which is used to control the way Match Registers get updated. I&#8217;ll explain this shortly. First Lets see all the Registers in PWM Block in LPC2148.</div>
<div class="highlight">
Now lets have a look at some important Registers :</p>
<h4>1) PWMTCR : PWM Timer Control Register</h4>
<p>This register is used to control the Timer Counter inside the PWM block. Only Bits: 0, 1  &#038; 3 are used rest are reserverd.</p>
<ul>
<li><strong>Bit 0 :</strong> This bit is used to Enable/Disable Counting. When 1 both PWM Timer counter and PWM Prescale counter are enabled. When 0 both are disabled.</li>
<li><strong>Bit 1 :</strong> This bit is used to Reset both Timer and Prescale counter inside the PWM block. When set to 1 it will reset both of them (at next edge of PCLK).</li>
<li><strong>Bit 3 :</strong> This is used to enable the PWM mode i.e the PWM outputs.</li>
<li>Other Bits : Reserved.</li>
</ul>
<h4>2) PWMPR : PWM Prescale Register</h4>
<p>PWMPR is used to control the resolution of the PWM outputs. The Timer Counter(TC) will increment every PWMPR+1 Peripheral Clock Cycles (PCLK).</p>
<h4>3) PWMMR0 &#8211; PWMMR6 : Match Registers</h4>
<p>These are the seven Match registers as explained above which contain <strong>Pulse Width Values i.e the Number of PWMTC Ticks</strong>.</p>
<h4>4) PWMMCR : PWM Match Control Registers</h4>
<p>The PWM Match Control Register is used to specify what operations can be done when the value in a particular Match register equals the value in TC. For each Match Register we have 3 options : Either generate an Interrupt , or Reset the TC , or Stop .. which stops the counters and disables PWM. Hence this register is divided into group of 3 bits. The first 3 bits are for Match Register 0 i.e PWMMR0 , next 3 for PWMMR1 , and so on :</p>
<ul>
<li><strong>1) Bit 0 :</strong> Interrupt on PWMMR0 Match &#8211; If set to 1 then it will generate an Interrupt else disable if set to 0.</li>
<li>
<strong>2) Bit 1 :</strong> Reset on PWMMR0 Match &#8211; If set to 1 it will reset the Timer Counter i.e PWMTC else disabled if set to 0.
</li>
<li><strong>3) Bit 2 :</strong> Stop on PWMMR0 Match &#8211; If this bit is set 1 then both PWMTC and PWMPC will be stopped and will also make Bit 0 in PWMTCR to 0 which in turn will disable the Counters.</li>
<li><strong>*)</strong> Similarly {Bits 3,4,5} for PWMMR1 , {Bits 6,7,8} for PWMMR2 , {Bits 9,10,11} for PWMMR3 ,{Bits 12,13,14} for PWMMR4 ,{Bits 15,16,17} for PWMMR5 , {Bits 18,19,20} for PWMMR6.</li>
</ul>
<h4>5) PWMIR : PWM Interrupt Register </h4>
<p>If an interrupt is generated by any of the Match Register then the corresponding bit in PWMIR will be set high. Writing a 1 to the corresponding location will clear that interrupt. Here :</p>
<ul>
<li><strong>1) Bits 0,1,2,3</strong> are for PWMMR0, PWMMR1, PWMMR2, PWMMR3 respectively and </li>
<li><strong>2) Bits 8,9,10</strong> are for PWMMR4 , PWMMR5 , PWMMR6 respectively. Other bits are reserved.</li>
</ul>
<h4>6) PWMLER : Latch Enable Register </h4>
<p>The PWM Latch Enable Register is used to control the way Match Registers are updated when PWM generation is active. When PWM mode is active and we apply new values to the Match Registers the new values won&#8217;t get applied immediately. Instead what happens is that the value is written to a &#8220;Shadow Register&#8221; .. it can be thought of as a duplicate Match Register. Each Match Register has a corresponding Shadow Register. The value in this Shadow Register is transferred to the actual Match Register when :</p>
<div class="intendb"><strong>1)</strong> PWMTC is reset (i.e at the beginning of the next period) ,<br />
<strong>2)</strong> And the corresponding Bit in PWMLER is 1.
</div>
<p>Hence only when these 2 conditions are satisfied the value is copied to Match Register. Bit &#8216;x&#8217; in PWMLER corresponds to match Register &#8216;x&#8217;. I.e Bit 0 is for PWMMR0 , Bit 1 for PWMMR1 , .. and so on. Using PWMLER will be covered in the examples section.</p>
<h4>7) PWMPCR : PWM Control Register </h4>
<p>This register is used for Selecting between Single Edged &#038; Double Edged outputs and also to Enable/Disable the 6 PWM outputs which go to their corresponding Pins. </p>
<ul style="margin-bottom:0px;">
<li>1) <strong>Bits 2 to 6</strong> are used to select between Single or Double Edge mode for PWM 2,3,4,5,6 outputs.
<div class="intendb">1) <strong>Bit 2 :</strong> If set to 1 then PWM2(i.e the one corresponding to PWMMR2) output is double edged else if set 0 then its Single Edged.<br />
2) Similarly <strong>{Bits 3,4,5,6}</strong> for PWM3 , PWM4 , PWM5 , PWM6 respectively.</div>
</li>
<li>2)<strong> Bits 9 to 14</strong> are used to Enable/Disable PWM outputs
<div class="intendb">1) <strong>Bit 9 : </strong>If set to 1 then PWM1 output is enabled , else disabled if set to 0.<br />
2)Similarly <strong>{Bit 10,11,12,13,14}</strong> for PWM2 , PWM3 , PWM4 , PWM5 , PWM6 respectively.</div>
</li>
</ul>
</div>
<h2 class="shead">Configuring and Initializing PWM:</h2>
<p>Configuring PWM is very much similar to Configuring Timer except, additionally, we need to enable the outputs and select PWM functions for the corresponding PIN on which output will be available. But first we need to do some basic Math Calculations for defining the Period time , the resolution using a prescaler value and then Pulse Widths. For this First we need to define the resolution of out PWM signal. Here the PWM resolution which I mean is the minimum increment that can use to increase or decrease the pulse width. More smaller the increment more fine will be the resolution. This resolution is defined using  an appropriate Prescale Value. The calculation for Prescale is the same which I had shown in the Timer Tutorial .. but here it is once again :</p>
<div class="special sp_blue noteinfo">
<h4>LPC2148 PWM Prescaler (PWMPR) Calculations:</h4>
<p>The delay or time required for &#8216;Y&#8217; clock cycles of PCLK at ‘X’ MHz is given by :</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/pwm/delay.png" alt="Delay for Y Clock Cycles at X Mhz" class="aligncenter" width="210px" height="65px"/></p>
<p>When we take PR into consideration we get Y = PR+1. Now , consider that PCLK is running at 60Mhz then X=60. Hence if we use Y=60 i.e PR=59 then we get a delay of exact 1 micro-second(s). </p>
<p>So with PR=59 and PCLK @ 60Mhz our formula reduces to :</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/pwm/delay_1us.png" alt="1us Delay at 60Mhz" class="aligncenter" width="290px" height="65px"/></p>
<p>Similarly when we set Y=60000 i.e PR = 59999 the delay will be 1 milli-second(s) :</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/pwm/delay_1ms_b.png" alt="1ms Delay at 60Mhz" class="aligncenter" width="310px" height="65px"/></p>
<p>This delay which is defined using Prescale will be the delay required for <strong>TC to increment by 1</strong> i.e <strong>TC will increment every &#8216;PR+1&#8217; &#8220;Peripheral&#8221; Clock Cycles (PCLK)</strong>.
</div>
<div class="highlight">
Now , After we&#8217;ve worked out the resolution we want .. we can now Set and Initialize the PWM device as per the following steps :</p>
<ol>
<li>Select the PWM function for the PIN on which you need the PWM output using PINSEL0/1 register.</li>
<li>Select Single Edge or Double Edge Mode using PWMPCR. By default its Single Edge Mode.</li>
<li>Assign the Calculated value to PR.</li>
<li>Set the Value for PWM Period in PWMMR0.</li>
<li>Set the Values for other Match Registers i.e the Pulse Widths.</li>
<li>
Set appropriate bit values in PWMMCR .. like for e.g. resetting PWMTC for PWMMR0 match and optionally generate interrupts if required
</li>
<li>Set Latch Enable Bits for the Match Registers that you&#8217;ve used. This is important!</li>
<li>Then Enable PWM outputs using PWMPCR.</li>
<li>Now Reset PWM Timer using PWMTCR.</li>
<li>Finally .. Enable Timer Counter and PWM Mode using PWMTCR.</li>
</ol>
<p><strong>Example :</strong></p>
<pre><code class="language-cpp">
PINSEL0 = ... ; // Select PWM Function for used Pins
PWMPCR = ... ; // Select PWM type - By default its Single Edged
PWMPR = ... ; //assign calculated PR value 
PWMMR0 = ... ; // Assign Period Duration
PWMMRx = ... ; // Assign pulse duration i.e widths for other Match Regs.. x=1 to 6
PWMMCR = (1<<1) | ... ; // Reset PWMTC on PWMMR0 match &#038; Other conditions
PWMLER = (1<<1) | ... ; // update MR0 and other Match Registers
PWMPCR = ... ; // enable PWM outputs as required
PWMTCR = (1<<1) ; //Reset PWM TC &#038; PR

//Now , the final moment - enable everything
PWMTCR = (1<<0) | (1<<3); // enable counters and PWM Mode
//Done!
</code></pre>
</div>
<h4>Updating Match Registers i.e the Pulse Width</h4>
<p>Once PWM is initialized then you can update any of the Match Registers at anytime using PWMLER. This can be done as follows :</p>
<pre><code class="language-cpp">
PWMMR1 = 50; // Update Pulse Width
PWMLER = (1<<1); // set the corresponding bit to 1</code></pre>
<p>When you want to update Multiple Match Registers then this can be done as follows :</p>
<pre><code class="language-cpp">
PWMMR1 = 50;
PWMMR2 = 68;
PWMMR3 = 20;

PWMLER = (1<<1) | (1<<2) | (1<<3); // Update Latch Enable bit for all MRs together</code></pre>
<h2 class="shead"> Some Real World Examples with sample code</h2>
<p>I'll cover 2 examples using Single Edge PWM. These will be:<br />
<strong>1) Controlling RC Servo<br />
2) LED Dimming</strong></p>
<p>To stay within the scope of this article I'm not including a bit involved stuff like "RC Servo Speed Control" and "DC Motor Speed Control". I guess I'll do it in a separate article.</p>
<div class="special sp_blue notestar">Before we get into the Examples , I would like to mention a few words about a function named "<strong>initClocks()</strong>" which I've been using in all examples. This function basically configures PLL0 and Sets up CPU Clock at 60Mhz and Peripheral Clock also at 60Mhz. You can find the the source for initClocks() in the Attached Project Files. If you are not sure about how to configure PPL0 in LPC214x , you can find a simple tutorial @ <a href="https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/"><br />
LPC214x PLL Tutorial for CPU and Peripheral Clock</a>.</div>
<h4>Example #1) Simple RC Servo Control Using PWM : </h4>
<p>Here we will use PWM1 output from Pin P0.0 to control a RC Servo Motor. We will also be using 4 tactile switches to bring the Servo at 4 different positions. P0.1 , P0.2 , P0.3 , P0.4 will be used to take the input. One end of the tactile switches will be connected to each of these Pins and other end will be connected to ground. Here we'll also require external 5V-6V source to drive the servo. Servos can work with 3.3v voltage levels for input PWM signal , hence we just need to connect the MCU PWM Output to Servo PWM Input wire. The setup is as shown in the schematic below :</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/pwm/sch-ex1-pwm.png" /></p>
<p>When the user will press P0.1 the Pulse width will be 1ms , and similarly when the user presses P0.2 , P0.3 , P0.4 the Pulse Width will change to 1.25ms , 1.5ms and 1.75ms respectively and hence Servo will change its position correspondingly. </p>
<p><strong>Source for Example 1:</strong></p>
<pre><code class="language-cpp">
/*
(C) Umang Gajera | Power_user_EX - www.ocfreaks.com 2011-13.
More Embedded tutorials @ www.ocfreaks.com/cat/embedded

LPC2148 PWM Tutorial Example 1 - RC Servo Control using PWM.
License : GPL.
*/
#include &lt;lpc214x.h&gt;

#define PLOCK 0x00000400
#define PWMPRESCALE 60   //60 PCLK cycles to increment TC by 1 i.e 1 Micro-second 

void initPWM(void);
void initClocks(void);

int main(void)
{
	initClocks(); //Initialize CPU and Peripheral Clocks @ 60Mhz
	initPWM(); //Initialize PWM

    //IO0DIR = 0x1; This is not needed!
    //Also by default all pins are configured as Inputs after MCU Reset.
   
    while(1)
    {
		if( !((IO0PIN) & (1<<1)) ) // Check P0.1
		{
			PWMMR1 = 1000;
			PWMLER = (1<<1); //Update Latch Enable bit for PWMMR1
		}
		else if( !((IO0PIN) &#038; (1<<2)) ) // Check P0.2
		{
			PWMMR1 = 1250;
			PWMLER = (1<<1);
		}
		else if( !((IO0PIN) &#038; (1<<3)) ) // Check P0.3
		{
			PWMMR1 = 1500;
			PWMLER = (1<<1);
		}
		else if( !((IO0PIN) &#038; (1<<4)) ) // Check P0.4
		{
			PWMMR1 = 1750;
			PWMLER = (1<<1);
		}
    }
    //return 0; //normally this wont execute ever
}

void initPWM(void)
{
	/*Assuming that PLL0 has been setup with CCLK = 60Mhz and PCLK also = 60Mhz.*/
	/*This is a per the Setup &#038; Init Sequence given in the tutorial*/

	PINSEL0 = (1<<1); // Select PWM1 output for Pin0.0
	PWMPCR = 0x0; //Select Single Edge PWM - by default its single Edged so this line can be removed
	PWMPR = PWMPRESCALE-1; // 1 micro-second resolution
	PWMMR0 = 20000; // 20ms = 20k us - period duration
	PWMMR1 = 1000; // 1ms - pulse duration i.e width
	PWMMCR = (1<<1); // Reset PWMTC on PWMMR0 match
	PWMLER = (1<<1) | (1<<0); // update MR0 and MR1
	PWMPCR = (1<<9); // enable PWM output
	PWMTCR = (1<<1) ; //Reset PWM TC &#038; PR

	//Now , the final moment - enable everything
	PWMTCR = (1<<0) | (1<<3); // enable counters and PWM Mode

	//PWM Generation goes active now!!
	//Now you can get the PWM output at Pin P0.0!
}
</code></pre>
<div class="highlight"><strong>Download Project Source for Example #1 @</strong> <a href='https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC214x_PWM_Tutorial_Example_2.zip'>LPC214x PWM Tutorial Example 1.zip</a> [Successfully tested on Keil UV4.70a] </div>
<h4>Example #2) LED Dimming using PWM :</h4>
<p>The basic Setup here is same as in Example 1 except that here we replace Servo by a LED and we dont need external 5V source along with slightly modified Program. Here we'll just need to connect the anode of LED to P0.0 and cathode to GND. In this example will use a period of 10ms. In this case the Pulse Widths Corresponding to the Switches connected to P0.1/2/3/4 will be 2.5ms/5ms/7.5ms/10ms respectively. As the Pulse Width Increases LED will keep on getting Brighter and the reverse will happen if Pulse Width is decreased.</p>
<p><strong>Source for Example 2: </strong></p>
<pre><code class="language-cpp">
/*
(C) Umang Gajera | Power_user_EX - www.ocfreaks.com 2011-13.
More Embedded tutorials @ www.ocfreaks.com/cat/embedded

LPC2148 PWM Tutorial Example 2 - LED Dimming using PWM.
License : GPL.
*/
#include &lt;lpc214x.h&gt;

#define PLOCK 0x00000400
#define PWMPRESCALE 60   //60 PCLK cycles to increment TC by 1 i.e 1 Micro-second 

void initPWM(void);
void initClocks(void);

int main(void)
{
	initClocks(); //Initialize CPU and Peripheral Clocks @ 60Mhz
	initPWM(); //Initialize PWM

    //IO0DIR = 0x1; This is not needed!
    //Also by default all pins are configured as Inputs after MCU Reset.
   
    while(1)
    {
		if( !((IO0PIN) & (1<<1)) ) // Check P0.1
		{
			PWMMR1 = 2500; //T-ON=25% , Hence 25% Bright
			PWMLER = (1<<1); //Update Latch Enable bit for PWMMR1
		}
		else if( !((IO0PIN) &#038; (1<<2)) ) // Check P0.2
		{
			PWMMR1 = 5000; //50% Bright
			PWMLER = (1<<1);
		}
		else if( !((IO0PIN) &#038; (1<<3)) ) // Check P0.3
		{
			PWMMR1 = 7500; //75% Bright
			PWMLER = (1<<1);
		}
		else if( !((IO0PIN) &#038; (1<<4)) ) // Check P0.4
		{
			PWMMR1 = 10000; //100% Bright
			PWMLER = (1<<1);
		}
    }
    //return 0; //normally this wont execute ever
}

void initPWM(void)
{
	/*Assuming that PLL0 has been setup with CCLK = 60Mhz and PCLK also = 60Mhz.*/
	/*This is a per the Setup &#038; Init Sequence given in the tutorial*/

	PINSEL0 = (1<<1); // Select PWM1 output for Pin0.0
	PWMPCR = 0x0; //Select Single Edge PWM - by default its single Edged so this line can be removed
	PWMPR = PWMPRESCALE-1; // 1 micro-second resolution
	PWMMR0 = 10000; // 10ms period duration
	PWMMR1 = 2500; // 2.5ms - pulse duration i.e width (Brigtness level)
	PWMMCR = (1<<1); // Reset PWMTC on PWMMR0 match
	PWMLER = (1<<1) | (1<<0); // update MR0 and MR1
	PWMPCR = (1<<9); // enable PWM output
	PWMTCR = (1<<1) ; //Reset PWM TC &#038; PR

	//Now , the final moment - enable everything
	PWMTCR = (1<<0) | (1<<3); // enable counters and PWM Mode

	//PWM Generation goes active now - LED must be 25% Bright after Reset!!
	//Now you can get the PWM output at Pin P0.0!
}
</code></pre>
<div class="highlight"><strong>Download Project Source for Example #2 @</strong> <a href='https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC214x_PWM_Tutorial_Example_2.zip'>LPC214x PWM Tutorial Example 2.zip</a> [Successfully tested on Keil UV4.70a] </div>
<p><span class="shead"><span class="sheadin">More stuff using PWM</span></span><br />
I've had 2 requests for Sine Wave generation using PWM , <strike>which I'll be doing soon</strike> which you can find here : <a href="https://www.ocfreaks.com/sine-wave-generator-using-pwm-lpc2148-microcontroller-tutorial/">Sine Wave Generator using PWM with LPC2148 Microcontroller Tutorial</a>. I might also cover MCU controlled voltage Regulation along with double egde PWM 🙂</p>
<p><strong>Please do leave your Article Requests , Suggestions & Comments. </strong></p>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-pwm-programming-tutorial/">LPC2148 PWM Programming Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/lpc2148-pwm-programming-tutorial/feed/</wfw:commentRss>
			<slash:comments>12</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1462</post-id>	</item>
		<item>
		<title>LPC2148 Interrupt Tutorial</title>
		<link>https://www.ocfreaks.com/lpc2148-interrupt-tutorial/</link>
					<comments>https://www.ocfreaks.com/lpc2148-interrupt-tutorial/?noamp=mobile#comments</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Fri, 09 Aug 2013 13:52:25 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=1403</guid>

					<description><![CDATA[<p>On special request by our readers &#038; followers we have finally posted the LPC2148 Interrupt Programming Tutorial!</p>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-interrupt-tutorial/">LPC2148 Interrupt Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div><script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script><!-- Content_Start --><ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-3054360504591881"
     data-ad-slot="4043606851"
     data-ad-format="auto"></ins><br />
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script></div>
<h2 class="shead">Introduction to Interrupts</h2>
<p>This is a Basic Tutorial on Interrupts for LPC2148 ARM 7 MCUs and how to program them for those who are new to interrupts. To start with , first lets see : what <strong>interrupts, IRQs and ISRs</strong> are. As per wiki : &#8220;<strong>An interrupt is a signal sent to the CPU which indicates that a system event has a occurred which needs immediate attention</strong>&#8220;. An &#8216;<strong>Interrupt ReQuest</strong>&#8216; i.e an &#8216;<strong>IRQ</strong>&#8216; can be thought of as a special request to the CPU to execute a <em><strong>function</strong></em>(small piece of code) when an interrupt occurs. This <em><strong>function</strong></em> or &#8216;small piece of code&#8217; is technically called an &#8216;<strong>Interrupt Service Routine</strong>&#8216; or &#8216;<strong>ISR</strong>&#8216;. So when an IRQ arrives to the CPU , it stops executing the code current code and start executing the ISR. After the ISR execution has finished the CPU gets back to where it had stopped.</p>
<p>Interrupts in LPC214x are handled by <strong>Vectored Interrupt Controller (VIC)</strong> (which is specific to ARM based microcontrollers and CPUs) and are classified into 3 types based on the priority levels.(Though Interrupts can classified in many different ways as well)</p>
<div class="intendb">
<ol>
<li><strong>Fast Interrupt Request i.e FIQ</strong> : which has highest priority</li>
<li><strong>Vectored Interrupt Request i.e Vectored IRQ</strong> : which has &#8216;middle&#8217; or priority between FIQ and Non-Vectored IRQ.</li>
<li><strong>Non-Vectored IRQ</strong> : which has the lowest priority.</li>
</ol>
</div>
<p><strong>But I&#8217;d like to Classify them as 2 types :</strong></p>
<div class="intendb">
<ol>
<li><strong>Fast IRQs or FIQs</strong></li>
<li><strong>Normal IRQs or IRQs</strong> which can be further classified as : Vectored IRQ and Non-Vectored IRQ.</li>
</ol>
</div>
<p><strong>Okay .. so what does Vectored mean ?</strong></p>
<p>In computing the term &#8216;<strong>Vectored</strong>&#8216; means that the CPU is aware of the address of the ISR when the interrupt occurs and <strong>Non-Vectored</strong> means that CPU doesn&#8217;t know the address of the ISR  nor the source of the IRQ when the interrupt occurs and it needs to be supplied with the ISR address. For the Vectored Stuff , the System internally maintains a table called <strong>IVT or Interrupt Vector Table</strong> which contains the information about Interrupts sources and their corresponding ISR address.</p>
<div class="special sp_blue noteinfo">
Well , in my opinion you can literally use the meaning of the term &#8216;<strong>Vector</strong>&#8216; which comes from mathematics &#038; physics which indicates a quantity having direction and magnitude. In our case we can consider the &#8216;<strong>magnitude</strong>&#8216; as the ID of the interrupt source i.e the processor knows what is &#8216;source&#8217; of the interrupt request (IRQ). Similarly for <strong>direction</strong> you can consider that Vectored IRQ &#8216;Points to'(which is like direction) its own unique ISR. On the other hand each Non-Vectored ISRs doesn&#8217;t point to a unique ISR and instead the CPU needs to be supplied with the address of the &#8216;default&#8217; or say.. a &#8216;common&#8217; ISR that needs to be executed when the interrupt occurs. In LPC214x this is facilitated by a register called &#8216;<strong>VICDefVectAddr</strong>&#8216;. The user must assign the address of the default ISR to this register for handling Non-Vectored IRQs.
</div>
<p>If you are still confused than here it is in a nutshell : The difference between Vectored IRQ(VIRQ) and Non-Vectored IRQ(NVIRQ) is that VIRQ has dedicated IRQ service routine for each interrupt source which while NVIRQ has the same IRQ service routine for all Non-Vectored Interrupts. You will get a clear picture when I cover some examples in examples section. </p>
<div class="intendb"><strong>Note :</strong> VIC (in ARM CPUs &#038; MCUs) , as per its design , can take <strong>32</strong> interrupt request inputs but only <strong>16</strong> requests can be assigned to Vectored IRQ interrupts in its LCP2148 ARM7 Implementation. We are given a set of <strong>16 vectored IRQ slots</strong> to which we can assign any of the <strong>22</strong> requests that are available in LPC2148. The slot numbering goes from <strong>0 to 15</strong> with <strong>slot no. 0 having highest priority and slot no. 15 having lowest priority</strong>.</p>
<p><strong>Example:</strong> For example if you working with 2 interrupt sources say.. UART0 and TIMER0. Now if you want to give TIMER0 a higher priority than UART0 .. then assign TIMER0 interrupt a lower number slot than UART0 Like .. hook up TIMER0 to slot 0 and UART0 to slot 1 or TIMER0 to slot 4 and UART to slot 9 or whatever slots you like. The number of the slot doesn&#8217;t matter as long TIMER0 slot is lower than UART0 slot.
</div>
<div class="special sp_cyan notestar">VIC has plenty of registers. Most of the registers that are used to configure interrupts or read status have each bit corresponding to a particular interrupt source and <strong>this mapping is same for all of <em>these</em> registers</strong>. What I mean is that bit 0 in these registers corresponds to Watch dog timer interrupt , bit 4 corresponds to TIMER0 interrupt , bit 6 corresponds to UART0 interrupt .. and so on.</div>
<p><strong>Here is the complete table which says which bit corresponds to which interrupt source as given in Datasheet:</strong></p>
<p><center><strong>Table 1:</strong></p>
<table>
<tr>
<td><strong>Bit#</strong></td>
<td>22</td>
<td>21</td>
<td>20</td>
<td>19</td>
<td>18</td>
<td>17</td>
<td>16</td>
<td>15</td>
<td>14</td>
<td>13</td>
<td>12</td>
<td>11</td>
</tr>
<tr>
<td><strong>IRQ</strong></td>
<td>USB</td>
<td>AD1</td>
<td>BOD</td>
<td>I2C1</td>
<td>AD0</td>
<td>EINT3</td>
<td>EINT2</td>
<td>EINT1</td>
<td>EINT0</td>
<td>RTC</td>
<td>PLL</td>
<td>SPI1/SSP</td>
</tr>
<tr> </tr>
<tr>
<td><strong>Bit#</strong></td>
<td>10</td>
<td>9</td>
<td>8</td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
<td>1</td>
<td>0</td>
</tr>
<tr>
<td><strong>IRQ</strong></td>
<td>SPI0</td>
<td>I2C0</td>
<td>PWM</td>
<td>UART1</td>
<td>UART0</td>
<td>TIMR1</td>
<td>TIMR0</td>
<td>ARMC1</td>
<td>ARMC0</td>
<td>N/A</td>
<td>WDT</td>
</tr>
</table>
<p></center></p>
<p><strong>Note :</strong> TIMR0 = TIMER0 , TIMR1 = TIMER1 , ARMC1 = ARMCore1 , ARMC2 = ARMCore2. </p>
<h2 class="shead">LPC2148 ARM 7 Interrupt Related Registers</h2>
<p>Now we will have a look at some of the important Registers that are used to implement interrupts in lpc214x:</p>
<div class="highlight">
<h4>1) VICIntSelect (R/W) :</h4>
<p> This register is used to select an interrupt as IRQ or as FIQ. Writing a 0 at a given bit location(as given in Table 1) will make the corresponding interrupt as IRQ and writing a 1 will make it FIQ. For e.g if you make Bit 4 as 0 then the corresponding interrupt source i.e TIMER0 will be IRQ else if you make Bit 4 as 1 it will be FIQ instead. Note than by default all interrupts are selected as IRQ. Note that here IRQ applies for both Vectored and Non-Vectored IRQs. <em>[Refer Table 1]</em></p>
<h4>2) VICIntEnable (R/W) :</h4>
<p> This is used to enable interrupts. Writing a 1 at a given bit location will make the corresponding interrupt Enabled. If this register is read then 1&#8217;s will indicated enabled interrupts and 0&#8217;s as disabled interrupts. Writing 0&#8217;s has no effect. <em>[Refer Table 1]</em></p>
<h4>3) VICIntEnClr (R/W) :</h4>
<p> This register is used to disable interrupts. This is similar to VICIntEnable expect writing a 1 here will disabled the corresponding Interrupt. This has an effect on VICIntEnable since writing at bit given location will clear the corresponding bit in the VICIntEnable Register. Writing 0&#8217;s has no effect. <em>[Refer Table 1]</em></p>
<h4>4) VICIRQStatus (R) :</h4>
<p> This register is used for reading the current status of the enabled IRQ interrupts. If a bit location is read as 1 then it means that the corresponding interrupt is enabled and active. Reading a 0 is unless here lol.. <em>[Refer Table 1]</em></p>
<h4>5) VICFIQStatus (R) :</h4>
<p> Same as VICIRQStatus except it applies for FIQ. <em>[Refer Table 1]</em></p>
<h4>6) VICSoftInt :</h4>
<p> This register is used to generate interrupts using software i.e manually generating interrupts using code i.e the program itself. If you write a 1 at any bit location then the correspoding interrupt is triggered i.e. it forces the interrupt to occur. Writing 0 here has no effect. <em>[Refer Table 1]</em></p>
<h4>7) VICSoftIntClear :</h4>
<p> This register is used to clear the interrupt request that was triggered(forced) using VICSoftInt. Writing a 1 will release(or clear) the forcing of the corresponding interrupt. <em>[Refer Table 1]</em>  </p>
<h4>8) VICVectCntl0 to VICVectCntl15 (16 registers in all) :</h4>
<p> These are the Vector Control registers. These are used to assign a particular interrupt source to a particular slot. As mentioned before slot 0 i.e VICVectCntl0 has highest priority and VICVectCntl15 has the lowest. Each of this registers can be divided into 3 parts : <strong>{Bit0 to bit4} , {Bit 5} , {and rest of the bits}</strong>.</p>
<p>The first 5 bits i.e Bit 0 to Bit 4 contain the number of the interrupt request which is assigned to this slot. The interrupt source numbers are given in the table below :</p>
<p><center><strong>Table 2:</strong></p>
<table>
<tr>
<td><strong>Interrupt Source</strong></td>
<td><strong>Source number <br /> In Decimal</strong></td>
<td><strong>Interrupt Source</strong></td>
<td><strong>Source number <br /> In Decimal</strong></td>
</tr>
<tr>
<td>WDT </td>
<td> 0 </td>
<td>PLL </td>
<td> 12 </td>
</tr>
<tr>
<td>N/A </td>
<td> 1 </td>
<td>RTC </td>
<td> 13 </td>
</tr>
<tr>
<td>ARMCore0 </td>
<td> 2 </td>
<td>EINT0 </td>
<td> 14 </td>
</tr>
<tr>
<td>ARMCore1 </td>
<td> 3 </td>
<td>EINT1 </td>
<td> 15 </td>
</tr>
<tr>
<td>TIMER0 </td>
<td> 4 </td>
<td>EINT2 </td>
<td> 16 </td>
</tr>
<tr>
<td>TIMER1 </td>
<td> 5 </td>
<td>EINT3 </td>
<td> 17 </td>
</tr>
<tr>
<td>UART0 </td>
<td> 6 </td>
<td>ADC0 </td>
<td> 18 </td>
</tr>
<tr>
<td>UART1 </td>
<td> 7 </td>
<td>I2C1 </td>
<td> 19 </td>
</tr>
<tr>
<td>PWM </td>
<td> 8 </td>
<td>BOD </td>
<td> 20 </td>
</tr>
<tr>
<td>I2C0 </td>
<td> 9 </td>
<td>ADC1 </td>
<td> 21 </td>
</tr>
<tr>
<td>SPI0 </td>
<td> 10 </td>
<td>USB </td>
<td> 22 </td>
</tr>
<tr>
<td>SPI1 </td>
<td> 11 </td>
</tr>
</table>
<p></center></p>
<p>The <strong>5th bit</strong> is used to <strong>enable</strong> the vectored IRQ slot by writing a 1. </p>
<div class="special sp_blue notestar"><strong>Attention! :</strong> Note that if the vectored IRQ slot is disabled it will not disable the interrupt but will change the corresponding interrupt to Non-Vectored IRQ. Enabling the slot here means that it can generate the address of the &#8216;dedicated Interrupt handling function (ISR)&#8217; and disabling it will generate the address of the &#8216;common/default Interrupt handling function (ISR)&#8217; which is for Non-Vectored ISR. In simple words if the slot is enabled it points to &#8216;specific and dedicated interrupt handling function&#8217; and if its disable it will point to the &#8216;default function&#8217;. This will get more clear as we do some examples.</p>
<p><strong>Note : The Interrupt Source Number is also called as <em>VIC Channel Mask</em>.</strong> For this tutorial I&#8217;ll be calling it as &#8220;Interrupt Source Number&#8221; to keep things simple and clear. (^_^)
</div>
<p>The rest of the bits are reserved.</p>
<h4>9) VICVectAddr0 to VICVectAddr15 (16 registers in all) :</h4>
<p> For Vectored IRQs these register store the address of the function that must be called when an interrupt occurs. Note &#8211; If you assign slot 3 for TIMER0 IRQ then care must be taken that you assign the address of the interrupt function to corresponding address register .. i.e VICVectAddr3 in this example.</p>
<h4>10) VICVectAddr :</h4>
<p> This <strong>must not be confused</strong> with the above set of 16 VICVecAddrX registers. When an interrupt is Triggered this register holds the address of the associated ISR i.e the one which is currently active. Writing a value i.e dummy write to this register indicates to the VIC that current Interrupt has finished execution. In this tutorial the only place we&#8217;ll use this register .. is at the <strong>end of the ISR</strong> to signal end of ISR execution.</p>
<h4>11) VICDefVectAddr :</h4>
<p> This register stores the address of the &#8220;default/common&#8221; ISR that must be called when a Non-Vectored IRQ occurs.
</p></div>
<p>Now , that we&#8217;ve seen all the Registers , lets consider another Example with a Simple Diagram : Here we have 4 IRQs Configured. TIMER0 and SPI0 are configured as Vectored IRQs with TIMER0 having Highest Priority. UART0 and PWM IRQs are setup as Non-Vectored IRQs. The whole setup is as given below:</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/interrupts/lpc214x-interrupts.jpg" width=600 /></p>
<h2 class="shead">Configuring and Programming Interrupts (ISR)</h2>
<p>To explain How to configure Interrupts and define ISRs I&#8217;ll use Timers as an example , since this Timers are easy to play. Hence , I Assume you are comfortable with using timers with LPC214x ;). </p>
<p>First lets see how to define the <strong>ISR</strong> so that <strong>compiler handles it carefully</strong>. For this we need to explicitly tell the compiler that the function is not a normal function but an ISR. For this we&#8217;ll use a special keyword called &#8220;<strong>__irq</strong>&#8221; which is a <strong>function qualifier</strong>. If you use this keyword with the function definition then compiler will automatically treat it as an ISR. Here is an example on how to define an ISR in Keil :</p>
<pre><code class="language-cpp">
__irq void myISR (void)
{
 ...
}

//====OR Equivalently====

void myISR (void) __irq
{
 ...
}
</code></pre>
<p><strong>Note that both are perfectly valid ways of defining the ISR. I prefer to use <strong>__irq</strong> before the function name.</strong></p>
<p>Now lets see how to actually setup the interrupt for ARM based microcontrollers like lpc2148. Consider we wanna assign TIMER0 IRQ and ISR to slot X. Here is a simple procedure like thing to do that :</p>
<div class="highlight">
<h4>A Simple 3 Step Process to Setup / Enable a Vectored IRQ</h4>
<div class="intendb">
<ol>
<li>First we need to enable the TIMER0 IRQ itself! Hence , from <strong>Table 1</strong> we get the bit number to <strong>Enable</strong> TIMER0 Interrupt which is Bit number 4. Hence we must make bit 4 in VICIntEnable to &#8216;1&#8217;. </li>
<li>Next , from <strong>Table 2</strong> we get the interrupt source number for TIMER0 which is <strong>decimal</strong> 4 and <strong>OR</strong> it with <strong>(1&lt;&lt;5)</strong> [i.e 5th bit=1 which enables the slot] and assign it to VICVectCntlX.</li>
<li>Next assign the address of the related ISR to VICVectAddrX.</li>
</ol>
</div>
<h4>Here is a simple Template to do it:</h4>
<p> Replace <strong>X</strong> by the <strong>slot number</strong> you want .., then Replace <strong>Y</strong> by the <strong>Interrupt Source Number</strong> as given in <strong>Table 2</strong> and finally replace <strong>myISR</strong> with your own <strong>ISR&#8217;s function Name</strong> .. and you&#8217;r Done!</p>
<pre><code class="language-cpp">
VICIntEnable |= (1&lt;&lt;Y) ; 
VICVectCntlX = (1&lt;&lt;5) | Y ;
VICVectAddrX = (unsigned) myISR;
</code></pre>
</div>
<p>Using above steps we can Assign TIMER0 Interrupt to Slot number say.. 0 as follows : </p>
<pre><code class="language-cpp">
VICIntEnable |= (1&lt;&lt;4) ; // Enable TIMER0 IRQ
VICVectCntl0 = (1&lt;&lt;5) | 4 ; //5th bit must 1 to enable the slot (see the register definition above)
VICVectAddr0 = (unsigned) myISR;
//Vectored-IRQ for TIMER0 has been configured
</code></pre>
<div class="special sp_blue noteinfo">
<strong>Attention! :</strong> Note the Correspondence between the Bit number in Table 1 and the Source Number in Table 2. The <strong>Bit Number</strong> now becomes the <strong>Source Number in decimal</strong>.
</div>
<p>Having done that now its time to write the code for the ISR.</p>
<div class="special sp_red noteinfo">
<strong>More Attention plz! :</strong> First thing to note here is that each device(or block) in lpc214x has only 1 IRQ associated with it. But <strong>inside each device there may be different sources</strong> which can raise an interrupt (or an IRQ). Like the TIMER0 block(or device) has <strong>4 match + 4 capture registers</strong> and any one or more of them can be configured to <strong>trigger</strong> an interrupt. Hence such devices have a <strong>dedicated interrupt register</strong> which contains a <strong>flag bit</strong> for each of these source(For Timer block its &#8216;<strong>T0IR</strong>&#8216;). So , when the ISR is called first we need to identify the actual source of the interrupt using the Interrupt Register and then proceed accordingly. Also just before , when the main ISR code is finished we also need to acknowledge or signal that the ISR has finished executing for the current IRQ which triggered it. This is done by clearing the the <strong>flag(i.e the particular bit) in the device&#8217;s interrupt register</strong> and then by writing a zero to VICVectAddr register which signifies that interrupt has ISR has finished execution successfully.</div>
<h4>Programming the Interrupt Service Routine (<strong>ISR</strong>) :</h4>
<p>Okay to keep things keep simple lets consider two simple cases <strong>for coding an ISR</strong> (I&#8217;ll use TIMER0 for generating IRQs since its easy to understand and play with) :</p>
<ul>
<li><strong><span class="font_15px">Case #1)</span></strong> First when we have only one &#8216;internal&#8217; source of interrupt in TIMER0 i.e an MR0 match event which raises an IRQ.</li>
<li><strong><span class="font_15px">Case #2)</span></strong> And when we have multiple &#8216;internal&#8217; source of interrupt in TIMER0 i.e. say a match event for MR0 , MR1 &#038; MR2 which raise an IRQ.</li>
</ul>
<p>In <strong>case #1</strong> things are pretty straight forward. Since we know only one source is triggering an interrupt we don&#8217;t need to identify it &#8211; though its a good practice to explicitly identify it. The ISR then will be something like :</p>
<pre><code class="language-cpp">
__irq void myISR(void)
{
	long int regVal;
	regVal = T0IR; // read the current value in T0's Interrupt Register
	
	//... MR0 match event has occured .. do something here
	
	T0IR = regval; // write back to clear the interrupt flag
	VICVectAddr = 0x0; // The ISR has finished!
}
</code></pre>
<p>Even in <strong>case #2</strong> things are simple except we need to identify the <strong>&#8216;actual&#8217; source</strong> of interrupt. </p>
<pre><code class="language-cpp">
#define MR0I_FLAG (1&lt;&lt;0)
#define MR1I_FLAG (1&lt;&lt;1)
#define MR2I_FLAG (1&lt;&lt;2)

__irq void myISR(void)
{
	long int regVal;
	regVal = T0IR; // read the current value in T0's Interrupt Register
	
	if( T0IR & MR0I_FLAG )
	{
		//do something for MR0 match
	}
	else if ( T0IR & MR1I_FLAG )
	{
		//do something for MR1 match
	}
	else if ( T0IR & MR2I_FLAG )
	{
		//do something for MR2 match
	}
	
	T0IR = regVal; // write back to clear the interrupt flag
	VICVectAddr = 0x0; // Acknowledge that ISR has finished execution
}
</code></pre>
<div class="intendb">Okay&#8230; Did you Notice a Second use of <strong>Case #2</strong> ? If not then here it is .. : <strong>Case #2</strong> actually provides a general method of using Timers as PWM generators! You can use any one of the match registers as PWM Cycle generator and then use other 3 match registers to generate 3 PWM signals! Since LPC214x already has PWM generator blocks on chip I don&#8217;t see any use of Timers being used as PWM generators. But for MCUs which don&#8217;t have PWM generator blocks this is very useful.</div>
<p>Soo , Now its time to go one step further and pop-out Case #3 and Case #4 out of the blue :P. Both of them deal with IRQs from different blocks viz. TIMER0 and UART0.</p>
<ul>
<li><strong><span class="font_15px">Case #3)</span></strong> When we have Multiple <strong>Vectored IRQs</strong> from different Devices. Hence Priority comes into picture here. </li>
<li><strong><span class="font_15px">Case #4)</span></strong> Lastly when we have Multiple <strong>Non-Vectored IRQs</strong> from different Devices.</li>
</ul>
<p>Don&#8217;t worry if your not acquianted with with UARTs , I&#8217;ll be Posting a Tutorial on UART with LPC214x shortly &#8211; Its in the pipeline :).</p>
<p>For <strong>Case #3</strong> , Consider we have TIMER0 and UART0 generating interrupts with TIMER0 having higher priority. So in this case we&#8217;ll need to write 2 different Vectored ISRs &#8211; one for TIMER0 and one for UART0. To keep things simple lets assume that we have only 1 internal source inside both TIMER0 and UART0 which generates an interrupt. The ISRs will be something as given below :</p>
<pre><code class="language-cpp">
__irq void myTimer0_ISR(void)
{
	//Same as in case #1
}

__irq void myUart0_ISR(void)
{
	long int regVal;
	regVal = U0IIR; // read the current value in U0's Interrupt Register
				// which also clears it!
	
	//Something inside UART0 has raised an IRQ
	
	VICVectAddr = 0x0; // The ISR has finished!
}
</code></pre>
<p>For <strong>Case #4</strong> too we have TIMER0 and UART0 generating interrupts. But here both of them are Non-Vectored and hence will be serviced by a common Non-Vectored ISR. Hence, here we will need to check the actual source i.e device which triggered the interrupt and proceed accordingly. This is quite similar to Case #2. The default ISR in this case will be something like :</p>
<pre><code class="language-cpp">
__irq void myDefault_ISR(void)
{
	long int T0RegVal , U0RegVal;
	T0RegVal = T0IR; // read the current value in T0's Interrupt Register
	U0RegVal = U0IIR; // read the current value in U0's(Uart 0) Interrupt Identification Register
	
	if( T0IR )
	{
		//do something for TIMER0 Interrupt
		
		T0IR = T0RegVal; // write back to clear the interrupt flag for T0
	}
	
	if( ! (U0RegVal & 0x1) ) 
	{
		//do something for UART0 Interrupt
		//No need to write back to U0IIR since reading it clears it
	}
	 
	VICVectAddr = 0x0; // The ISR has finished!
}</code></pre>
<div class="special sp_blue noteinfo"><strong>Attention Plz!:</strong> Note than UART0&#8217;s Interrupt Register is a lot different than TIMER0&#8217;s. The first Bit in U0IIR indicates whether any interrupt is pending or not and its Active LOW! The next 3 bits give the Identification for any of the 4 Interrupts if enabled. There is more to it which I&#8217;ll explain in detail in Upcoming Dedicated Tutorial on Uarts and Interrupt Programming related to it.</div>
<h4> But Wait! What about FIQ ?</h4>
<p>Well , you can think FIQ as a promoted version of a Vectored IRQ. To promote or covert a Vectored IRQ to FIQ just make the bit for corresponding IRQ in <strong>VICIntSelect</strong> register to <strong>1</strong> and it will be become an FIQ. <em>[Refer Table 1]</em> Also <strong>Note</strong> that its recommended that you only have one FIQ in your system. FIQs have low latency than VIRQs and usually used in <strong>System Critical Interrupt Handling</strong>.</p>
<h4>In Case Interrupts are Not working in Keil UV4 / UV5 / Crossworks for ARM MCUs : </h4>
<p>For those of you who are using Keil UV Version 4 or 5 (or higher) you’ll need to edit Target Option settings and those who are using Crossworks for ARM etc .. you need to manually enable global interrupt. I&#8217;ve posted a solution for this @ <a href="https://www.ocfreaks.com/lpc2148-keil-uvision-arm7-interrupt-problem-issue-fix/">https://www.ocfreaks.com/lpc2148-keil-uvision-arm7-interrupt-problem-issue-fix/</a>. For uVision 5 you can have a look here @ <a href="https://www.ocfreaks.com/create-keil-uvision5-project-lpc2148-arm7-mcu/">https://www.ocfreaks.com/create-keil-uvision5-project-lpc2148-arm7-mcu/</a> Please go through that post to make Interrupts work.</p>
<p><span class="shead"><span class="sheadin">Example Source for all Cases:</span></span></p>
<h4>Example 1 &#8211; for Case #1:</h4>
<p>For 1st example I&#8217;ll cover an <strong>interrupt driven &#8216;blinky&#8217;</strong> using timers which I&#8217;ve also use in the LPC214x Timer Tutorial which can be found here : <a href="www.ocfreaks.com/lpc2148-timer-tutorial/">https://www.ocfreaks.com/lpc2148-timer-tutorial/</a> You can get the Complete Source and KeilUV4 Project files from the given link itself. Here is a snippet of the code for Case #1:</p>
<pre><code class="language-cpp">

/*
(C) Umang Gajera | Power_user_EX - www.ocfreaks.com 2011-13.
More Embedded tutorials @ www.ocfreaks.com/cat/embedded

Soruce for Interrupt Tutorial Case #1.
License : GPL.
*/

#include &lt;lpc214x.h&gt;

#define MR0I (1&lt;&lt;0) //Interrupt When TC matches MR0
#define MR0R (1&lt;&lt;1)	//Reset TC when TC matches MR0

#define DELAY_MS 500 //0.5 Second(s) Delay
#define PRESCALE 60000 //60000 PCLK clock cycles to increment TC by 1 

void initClocks(void);
void initTimer0(void);
__irq void T0ISR(void);
void initClocks(void);

int main(void)
{
	initClocks(); //Initialize CPU and Peripheral Clocks @ 60Mhz
	initTimer0(); //Initialize Timer0
	IO0DIR = 0xFFFFFFFF; //Configure all pins on Port 0 as Output
	IO0PIN = 0x0;
	
	T0TCR = 0x01; //Enable timer

	while(1); //Infinite Idle Loop

	//return 0; //normally this wont execute ever	:P
}

void initTimer0(void)
{
	/*Assuming that PLL0 has been setup with CCLK = 60Mhz and PCLK also = 60Mhz.*/
   
	//----------Configure Timer0-------------

	T0CTCR = 0x0;									   	
	
	T0PR = PRESCALE-1; //(Value in Decimal!) - Increment T0TC at every 60000 clock cycles
                     //Count begins from zero hence subtracting 1
                     //60000 clock cycles @60Mhz = 1 mS
	
	T0MR0 = DELAY_MS-1; //(Value in Decimal!) Zero Indexed Count - hence subtracting 1
	
	T0MCR = MR0I | MR0R; //Set bit0 & bit1 to High which is to : Interrupt & Reset TC on MR0  

	//----------Setup Timer0 Interrupt-------------

	VICVectAddr4 = (unsigned )T0ISR; //Pointer Interrupt Function (ISR)

	VICVectCntl4 = 0x20 | 4; //0x20 (i.e bit5 = 1) -> to enable Vectored IRQ slot
				  //0x4 (bit[4:0]) -> this the source number - here its timer0 which has VIC channel mask # as 4
				  //You can get the VIC Channel number from Lpc214x manual R2 - pg 58 / sec 5.5
	
	VICIntEnable = 0x10; //Enable timer0 int
	
	T0TCR = 0x02; //Reset Timer
}

__irq void T0ISR(void)
{
	long int regVal;
	regVal = T0IR; //Read current IR value
		
	IO0PIN = ~IO0PIN; //Toggle all pins in Port 0

	T0IR = regVal; //Write back to IR to clear Interrupt Flag
	VICVectAddr = 0x0; //This is to signal end of interrupt execution
}

void initClocks(void)
{
  // This function is used to config PPL0 and setup both
  // CPU and Peripheral clock @ 60Mhz
  // You can find its definition in the attached files or case #2 source  
}

</code></pre>
<div class="highlight"><strong>Download Project Source for Case #1 @</strong> <a href="https://www.ocfreaks.com/wp-content/uploads/2013/03/OCFreaks.com_LPC214x_TimerIRQ_Tutorial.zip">OCFreaks.com_LPC214x_TimerIRQ_Tutorial.zip</a> [Successfully tested on Keil UV4.70a] </div>
<h4>Example 2 &#8211; for Case #2:</h4>
<p>This is an extended version of blinky which uses 3 Match Registers i.e 3 Interrupts sources within TIMER0 itself. Each Match register Interrupt is used to Turn on and off an LED which is connected to a particular GPIO Pin. Here we have 3 LEDs connected to PIN0 , PIN1 and PIN2 of PORT0 of LPC2148. MR0 is used for PIN0 i.e first LED , similarly MR2 and MR3 for PIN1 and PIN2 i.e for second and thrid LED respectively. MR0 has been configured to Trigger an Interrupt when 500ms have elapsed after TC is reset. MR1 has been configured to Trigger an Interrupt when 1000ms have elapsed after TC is reset. MR2 has been configured to Trigger an Interrupt when 1500ms have elapsed at it Resets the TC so the cycle starts again. Given a period of 1.5 seconds i.e 1500ms here is what happens :</p>
<div class="intendb">
<strong>1) </strong>MR0 Toggles PIN0 of PORT0 i.e P0.0 at 500ms<br />
<strong>2)</strong> MR1 Toggles PIN1 of PORT0 i.e P0.1 at 1000ms<br />
<strong>3)</strong> MR2 Toggles PIN2 of PORT0 i.e P0.2 at 1500ms and also Resets the TC<br />
<strong>*)</strong> This cycle of Toggling each of the LEDs one by one keeps on repeating&#8230;</div>
<pre><code class="language-cpp">

/*
(C) Umang Gajera | Power_user_EX - www.ocfreaks.com 2011-13.
More Embedded tutorials @ www.ocfreaks.com/cat/embedded

LPC2148 Interrupt Example.
License : GPL.
*/

#include &lt;lpc214x.h&gt;

#define PLOCK 0x00000400
#define MR0I (1&lt;&lt;0) //Interrupt When TC matches MR0
#define MR1I (1&lt;&lt;3) //Interrupt When TC matches MR1
#define MR2I (1&lt;&lt;6) //Interrupt When TC matches MR2
#define MR2R (1&lt;&lt;7)	//Reset TC when TC matches MR2

#define MR0I_FLAG (1&lt;&lt;0) //Interrupt Flag for MR0
#define MR1I_FLAG (1&lt;&lt;1) //Interrupt Flag for MR1
#define MR2I_FLAG (1&lt;&lt;2) //Interrupt Flag for MR2

#define MR0_DELAY_MS 500 //0.5 Second(s) Delay
#define MR1_DELAY_MS 1000 //1 Second Delay
#define MR2_DELAY_MS 1500 //1.5 Second(s) Delay
#define PRESCALE 60000 //60000 PCLK clock cycles to increment TC by 1 

void delayMS(unsigned int milliseconds);
void initClocks(void);
void initTimer0(void);
__irq void myTimer0_ISR(void);

void setupPLL0(void);
void feedSeq(void);
void connectPLL0(void);


int main(void)
{
	initClocks(); //Initialize CPU and Peripheral Clocks @ 60Mhz
	initTimer0(); //Initialize Timer0
	IO0DIR = 0xFFFFFFFF; //Configure all pins on Port 0 as Output
	IO0PIN = 0x0;
	
	T0TCR = 0x01; //Enable timer

	while(1); //Infinite Idle Loop

	//return 0; //normally this wont execute ever	:P
}

void initTimer0(void)
{
	/*Assuming that PLL0 has been setup with CCLK = 60Mhz and PCLK also = 60Mhz.*/
   
	//----------Configure Timer0-------------

	T0CTCR = 0x0;	
	
	T0PR = PRESCALE-1; //60000 clock cycles @60Mhz = 1 mS
	
	T0MR0 = MR0_DELAY_MS-1; // 0.5sec (Value in Decimal!) Zero Indexed Count - hence subtracting 1

	T0MR1 = MR1_DELAY_MS-1;	// 1sec

	T0MR2 = MR2_DELAY_MS-1;	// 1.5secs
	
	T0MCR = MR0I | MR1I | MR2I | MR2R; //Set the Match control register 

	//----------Setup Timer0 Interrupt-------------

	//I've just randomly picked-up slot 4

	VICVectAddr4 = (unsigned)myTimer0_ISR; //Pointer Interrupt Function (ISR)

	VICVectCntl4 = 0x20 | 4; 

	VICIntEnable = 0x10; //Enable timer0 int
	
	T0TCR = 0x02; //Reset Timer
}

__irq void myTimer0_ISR(void)
{
	long int regVal;
	regVal = T0IR; // read the current value in T0's Interrupt Register

	if( T0IR & MR0I_FLAG )
	{
		//do something for MR0 match

		IO0PIN ^= (1&lt;&lt;0); // Toggle GPIO0 PIN0 .. P0.0 
	
	}
	else if ( T0IR & MR1I_FLAG )
	{
		//do something for MR1 match
		
		IO0PIN ^= (1&lt;&lt;1);// Toggle GPIO0 PIN1 .. P0.1
	
	}
	else if ( T0IR & MR2I_FLAG )
	{
		//do something for MR0 match
		
		IO0PIN ^= (1&lt;&lt;2);// Toggle GPIO0 PIN2 .. P0.2
	
	}
	
	T0IR = regVal; // write back to clear the interrupt flag
	VICVectAddr = 0x0; // Acknowledge that ISR has finished execution
}

void initClocks(void)
{
	setupPLL0();
	feedSeq(); //sequence for locking PLL to desired freq.
	connectPLL0();
	feedSeq(); //sequence for connecting the PLL as system clock
   
	//SysClock is now ticking @ 60Mhz!
       
	VPBDIV = 0x01; // PCLK is same as CCLK i.e 60Mhz

	//PLL0 Now configured!
}

//---------PLL Related Functions :---------------

//Using PLL settings as shown in : https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/

void setupPLL0(void)
{
	//Note : Assuming 12Mhz Xtal is connected to LPC2148.
	
	PLL0CON = 0x01; 
	PLL0CFG = 0x24; 
}

void feedSeq(void)
{
	PLL0FEED = 0xAA;
	PLL0FEED = 0x55;
}

void connectPLL0(void)
{
	while( !( PLL0STAT & PLOCK ));
	PLL0CON = 0x03;
}
</code></pre>
<p><strong>Note: TC is reset only when MR2 matches TC.</strong> Though this can be used using simple delay but the example presented here does this by purely using Interrupts!</p>
<div class="highlight"><strong>Download Project Source for Case #2 @</strong> <a href='https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC214x_Interrupt_Tutorial_Case2.zip'>OCFreaks.com_LPC214x_Interrupt_Tutorial_Case2.zip</a> [Successfully tested on Keil UV4.70a] </div>
<h4>Example 3 &#8211; for Case #3:</h4>
<div class="special sp_red noteinfo"> Note that Case #3 and Case #4 are just some Fictitious-Typo cases which I&#8217;ve randomly made to Explain Multiple Vectored and Non-Vectored IRQs. I haven&#8217;t tried them out on my Development Board yet , but since the Simulation seems to work perfect I assume both will work as expected if you try them out. If any doesn&#8217;t work please let me know.</div>
<p>Consider we have TIMER0 generating interrupt every 500ms and UART0 generates an interrupt when some data arrives i.e when you press a key in the terminal. TIMER0 ISR will Toggle P0.2 and UART0 ISR will Toggle P0.3. Here TIMER0 ISR will have a higher priority over UART0 ISR. Hence when TIMER0 interrupt occurs and at the same time UART0 interrupt occurs , first TIMER0 ISR will be serviced and then UART0.</p>
<p>Here I&#8217;m only giving the code to Enable both the Interrupts and its ISR. You can find the complete code in files attached below.</p>
<p><strong>Code for Configuring Both TIMER0 and UART0 Interrupts is as given Below:</strong></p>
<pre><code class="language-cpp">
//----------Setup TIMER0 Interrupt-------------

//Using Slot 0 for TIMER0

VICVectAddr0 = (unsigned)myTimer0_ISR; //Pointer Interrupt Function (ISR)

VICVectCntl0 = 0x20 | 4; 

VICIntEnable |= (1&lt;&lt;4); //Enable timer0 int , 4th bit=1

//----------Setup UART0 Interrupt-------------

//Any Slot with Lower Priority than TIMER0's slot will suffice

VICVectAddr1 = (unsigned)myUart0_ISR; //Pointer Interrupt Function (ISR)

VICVectCntl1 = 0x20 | 6; 

VICIntEnable |= (1&lt;&lt;6); //Enable Uart0 interrupt , 6th bit=1</code></pre>
<p><strong>Both the ISRs will be as follows :</strong></p>
<pre><code class="language-cpp">
__irq void myTimer0_ISR(void)
{
	long int regVal;
	regVal = T0IR; // read the current value in T0's Interrupt Register
	
	IO0PIN ^= (1&lt;&lt;2); // Toggle 3rd Pin in GPIO0 .. P0.2

	T0IR = regVal; // write back to clear the interrupt flag
	VICVectAddr = 0x0; // Acknowledge that ISR has finished execution
}

__irq void myUart0_ISR(void)
{
	long int regVal;
	regVal = U0IIR; // Reading U0IIR also clears it!
	
	//Recieve Data Available Interrupt has occured
	regVal = U0RBR; // dummy read
	IO0PIN ^= (1&lt;&lt;3); // Toggle 4th Pin in GPIO0 .. P0.3

	VICVectAddr = 0x0; // Acknowledge that ISR has finished execution
}</code></pre>
<div class="highlight"><strong>Download Project Source for Case #3 @</strong> <a href='https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC214x_Interrupt_Tutorial_Case3.zip'>OCFreaks.com_LPC214x_Interrupt_Tutorial_Case3.zip</a> [Successfully tested on Keil UV4.70a] </div>
<h4>Example 4 &#8211; for Case #4:</h4>
<p>Even Here I&#8217;ll give the main code and as with above you can find the complete code in the attached files.</p>
<p><strong>In the Non-Vectored Case the configuration code will be as shown below:</strong></p>
<pre><code class="language-cpp">
VICDefVectAddr = (unsigned)myDefault_ISR; //Pointer to Default ISR

//----------Enable (Non-Vectored) TIMER0 Interrupt-------------

VICIntEnable |= (1&lt;&lt;4); //Enable timer0 int , 4th bit=1

//----------Enable (Non-Vectored) UART0 Interrupt-------------

VICIntEnable |= (1&lt;&lt;6); //Enable Uart0 interrupt , 6th bit=1</code></pre>
<p><strong>And the code for the Non-Vectored ISR is as given Below:</strong></p>
<pre><code class="language-cpp">

__irq void myDefault_ISR(void)
{
	long int T0RegVal , U0RegVal;
	T0RegVal = T0IR; // read the current value in T0's Interrupt Register
	U0RegVal = U0IIR;
	
	if( T0IR )
	{
		IO0PIN ^= (1&lt;&lt;2); // Toggle 3rd Pin in GPIO0 .. P0.2
		T0IR = T0RegVal; // write back to clear the interrupt flag
	}
	
	if( !(U0RegVal & 0x1) )
	{
		//Recieve Data Available Interrupt has occured
		U0RegVal = U0RBR; // dummy read
		IO0PIN ^= (1&lt;&lt;3); // Toggle 4th Pin in GPIO0 .. P0.3
	}
	
	VICVectAddr = 0x0; // Acknowledge that ISR has finished execution
}</code></pre>
<div class="highlight"><strong>Download Project Source for Case #4 @</strong> <a href='https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC214x_Interrupt_Tutorial_Case4.zip'>OCFreaks.com_LPC214x_Interrupt_Tutorial_Case4.zip</a> [Successfully tested on Keil UV4.70a] </div>
<div class="special sp_red noteinfo">
<strong>FINAL Attention Plz! :</strong> When you play with Multiple Interrupts , Please take care of all the situations that may arise else in most cases Weird System Behavior might creep in giving you Debugging Nightmares. I&#8217;d say keep on experimenting with Interrupts .. the more you&#8217;ll do the more you&#8217;ll learn and the more clear it will become!</div>
</p>
<p><span class="shead"><span class="sheadin">Advanced Interrupt Handling:</span></span></p>
<h4>Nested Interrupts:</h4>
<p>Interrupts too can be <strong>nested</strong>. But we need to take some special care when dealing with nested interrupts since in this case the &#8220;<strong>context</strong>&#8221; and the &#8220;<strong>stack</strong>&#8221; come into picture. When a nested interrupt has finished execution we must get back to the correct context. Similarly too much of nesting and the stack will get overloaded. Covering Nested interrupt is not within the scope of this tutorial. Here are some useful documents which will surely help you out with interrupt nesting.</p>
<p>For more on Nested Interrupts you can refer the following documents : </p>
<ol>
<li><a href="http://www.nxp.com/documents/application_note/AN10381.pdf">NXP Application Note : AN10381</a></li>
<li><a href="http://www.keil.com/support/docs/3353.htm">http://www.keil.com/support/docs/3353.htm</a></li>
</ol>
<h4>Spurious Interrupts:</h4>
<p>I like to call &#8220;<strong>Spurious Interrupt</strong>&#8221; as &#8220;<strong>Zombie Interrupts</strong>&#8221; 😀 &#8211; in my opinion that suits it more. These can happen in virtually any interrupt driven system and must be identified and handled properly else <strong>undefined system behaviour</strong> may occur. Such interrupts happen when an IRQ is generated out of no where (Magic! lol..). Here the IRQ comes into exsistence even when none of the conditions for triggering that IRQ have been met. Normally speaking its an interrupt that was not required in the first place. There are many reasons as to why it can happen :</p>
<ul>
<li>1) Inherent bug/flaw in system design</li>
<li>2) Electrical Interference / Noise</li>
<li>3) Software Bug</li>
<li>4) Signal glitches on Level Sensitive interrupts</li>
<li>5) Faulty components </li>
<li>6) Misconfigured Hardware</li>
<li>
7) etc&#8230; (You&#8217;ll get plently of info just by googling the term)</li>
</ul>
<p>In respect to LPC214x(also LPC2000) MCUs , the probability of occurrence of Spurious Interrupts is high when Watch Dog Timer or UART is used. The datasheet(manual) has special note on Spurious interrupts on page 61. Covering the &#8220;handling of Spurious Interrupts&#8221; is too not in the scope of this article.  </p>
<p>For more information on handling of spurious interrupts I would like my readers to go through the following documents :</p>
<ol>
<li>Page 61 of LPC214x Usermanul Rev 2.0</li>
<li><a href="http://www.nxp.com/documents/application_note/AN10414.pdf">NXP Application Note : AN10414</a></li>
</ol>
<h2 class="shead">Understanding VIC in Detail:</h2>
<p>Finally , I&#8217;d like to share one of the best guide I&#8217;ve found for Understanding the general Architecture of VIC in detail .. which is the following document from ARM itself :</p>
<ol>
<li><a href="http://infocenter.arm.com/help/topic/com.arm.doc.ddi0181e/DDI0181.pdf">http://infocenter.arm.com/help/topic/com.arm.doc.ddi0181e/DDI0181.pdf</a></li>
</ol>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-interrupt-tutorial/">LPC2148 Interrupt Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/lpc2148-interrupt-tutorial/feed/</wfw:commentRss>
			<slash:comments>11</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1403</post-id>	</item>
		<item>
		<title>LPC2148 Timer Tutorial</title>
		<link>https://www.ocfreaks.com/lpc2148-timer-tutorial/</link>
					<comments>https://www.ocfreaks.com/lpc2148-timer-tutorial/?noamp=mobile#comments</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Thu, 28 Mar 2013 07:18:58 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=1072</guid>

					<description><![CDATA[<p>The Basics After writing the first blinky program using random delay , now its time to improvise and induce precise delay using timers! The real fun in embedded starts when you start playing with timers (&#038; UARTs , PWM , etc..). I&#8217;ll try to keep it simple and short so its easy to understand. LPC2148 [&#8230;]</p>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-timer-tutorial/">LPC2148 Timer Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div><script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script><!-- Content_Start --><ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-3054360504591881"
     data-ad-slot="4043606851"
     data-ad-format="auto"></ins><br />
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script></div>
<p><span class="shead"><span class="sheadin">The Basics</span></span><br />
After writing the first blinky program using random delay , now its time to improvise and induce precise delay using timers! The real fun in embedded starts when you start playing with timers (&#038; UARTs , PWM , etc..). I&#8217;ll try to keep it simple and short so its easy to understand.</p>
<p>LPC2148 comes loaded with two 32-bit-Timer blocks. Each Timer block can be used as a &#8216;Timer&#8217; (like for e.g. triggering an interrupt every &#8216;t&#8217; microseconds) or as a &#8216;Counter&#8217; and can be also used to demodulate PWM signals given as input.</p>
<p>A timer has a <strong>Timer Counter(TC)</strong> and <strong>Prescale Register(PR)</strong> associated with it. When Timer is Reset and Enabled TC is set to 0 and incremented by 1 every <strong>&#8216;PR+1&#8217;</strong> clock cycles. When it reaches its maximum value it gets reset to 0 and hence restarts counting. Prescale Register is used to define the <strong>resolution</strong> of the timer. If <strong>PR=0</strong> then TC is incremented every <strong>1 clock cycle of the peripheral clock</strong>. If <strong>PR=1</strong> then TC is incremented every <strong>2 clock cycles of peripheral clock and so on</strong>. By setting an appropriate value in PR we can make timer increment or count : every peripheral clock cycle or 1 microsecond or 1 millisecond or 1 second and so on.</p>
<p>Each Timer has <strong>four 32-bit Match Registers</strong> and <strong>four 32-bit Capture Registers</strong>.</p>
<h2 class="shead">The Register Specifics</h2>
<p><strong>What is a Match Register anyways ? </p>
<p>Ans: </strong>A Match Register is a Register which contains a specific value set by the user. When the Timer starts &#8211; every time after TC is incremented the value in TC is compared with match register. If it matches then it can Reset the Timer or can generate an interrupt as defined by the user. We are only concerned with match registers in this tutorial.</p>
<div class="intendb">
<strong>Match Registers can be used to:</strong></p>
<ul>
<li>Stop Timer on Match(i.e when the value in count register is same as than in Match register) and trigger an optional interrupt.</li>
<li>Reset Timer on Match and trigger an optional interrupt.</li>
<li>
To count continuously and trigger an interrupt on match.</li>
</ul>
</div>
<p><strong>What are Capture Registers ?</p>
<p>Ans:</strong> As the name suggests it is used to Capture Input signal. When a transition event occurs on a Capture pin , it can be used to copy the value of TC into any of the 4 Capture Register or to genreate an Interrupt. Hence these can be also used to demodulated PWM signals. We are not going to use them in this tutorial since we are only concerned with using Timer block as a &#8216;Timer&#8217;. We&#8217;ll see them in upcoming tutorial since it needs a dedicated tutorial.</p>
<div class="highlight">
<strong>Now lets see some of the important registers concerned mainly with timer operation.</strong></p>
<p><strong><span class="font_15px">1) PR : Prescale Register (32 bit)</span></strong> &#8211; Stores the maximum value of Prescale counter after which it is reset.</p>
<p><strong><span class="font_15px">2) PC : Prescale Counter Register (32 bit)</span></strong> &#8211; This register increments on every PCLK(Peripheral clock). This register controls the resolution of the timer. When PC reaches the value in PR , PC is reset back to 0 and Timer Counter is incremented by 1. Hence if PR=0 then Timer Counter Increments on every 1 PCLK. If PR=9 then Timer Counter Increments on every 10th cycle of PCLK. Hence by selecting an appropriate prescale value we can control the resolution of the timer. </p>
<p><strong><span class="font_15px">3) TC : Timer Counter Register (32 bit)</span></strong> &#8211; This is the main counting register. Timer Counter increments when PC reaches its maximum value as specified by PR. If timer is not reset explicitly(directly) or by using an interrupt then it will act as a free running counter which resets back to zero when it reaches its maximum value which is 0xFFFFFFFF.</p>
<p><strong><span class="font_15px">4) TCR : Timer Control Register</span></strong> &#8211; This register is used to enable , disable and reset TC. When bit0 is 1 timer is enabled and when 0 it is disabled. When bit1 is set to 1 TC and PC are set to zero together in sync on the next positive edge of PCLK. Rest of the bits of TCR are reserved.</p>
<p><strong><span class="font_15px">5) CTCR : Count Control register</span></strong> &#8211; Used to select Timer/Counter Mode. For our purpose we are always gonna use this in Timer Mode. When the value of the CTCR is set to 0x0 Timer Mode is selected.</p>
<p><strong><span class="font_15px">6) MCR : Match Control register</span></strong> &#8211; This register is used to control which all operations can be done when the value in MR matches the value in TC. Bits 0,1,2 are for MR0 , Bits 3,4,5 for MR1 and so on.. Heres a quick table which shows the usage:</p>
<div class="intendbhl"><strong>For MR0:</strong></p>
<p>Bit 0 : Interrupt on MR0 i.e trigger an interrupt when MR0 matches TC. Interrupts are enabled when set to 1 and disabled when set to 0.</p>
<p>Bit 1 : Reset on MR0. When set to 1 , TC will be reset when it matched MR0. Disabled when set to 0.</p>
<p>Bit 2 : Stop on MR0. When set to 1 , TC &#038; PC will stop when MR0 matches TC.</p>
<p>Similarly bits 3-5 , 6-8 , 9-11 are for MR1 , MR2 , MR3 respectively.
</p></div>
<p><strong><span class="font_15px">7) IR : Interrupt Register</span></strong> &#8211; It contains the interrupt flags for 4 match and 4 capture interrupts. Bit0 to bit3 are for MR0 to MR3 interrupts respectively. And similarly the next 4 for CR0-3 interrupts. when an interrupt is raised the corresponding bit in IR will be set to 1 and 0 otherwise. Writing a 1 to the corresponding bit location will reset the interrupt &#8211; which is used to acknowledge the completion of the corresponding ISR execution.
</div>
<h2 class="shead">Setting up &#038; configuring Timers in LPC2148</h2>
<p>To use timers we need to first configure them. We need to set appropriate values in TxCTCR , TxIR , TxPR and reset TxPC , TxTC. Finally we assign TxTCR = 0x01 which enables the timer. </p>
<div class="highlight">I would like to encourage the readers to use the following sequence for Setting up Timers:</p>
<ol>
<li>Set appropriate value in TxCTCR</li>
<li>Define the Prescale value in TxPR</li>
<li>Set Value(s) in Match Register(s) if required</li>
<li>Set appropriate value in TxMCR if using Match registers / Interrupts</li>
<li>Reset Timer &#8211; Which resets PR and TC</li>
<li>
Set TxTCR to 0x01 to Enable the Timer when required</li>
<li>
Reset TxTCR to 0x00 to Disable the Timer when required</li>
</ol>
</div>
<p>Now lets implement to basic function required for Timer Operation:<br />
<strong>1.</strong> void initTimer0(void);<br />
<strong>2.</strong> void delayMS(unsigned int milliseconds); </p>
<h4>#1) initTimer0(void); [Used in Example #1] </h4>
<div class="special sp_red notestar">
<strong>Attention Plz! :</strong> This function is used to setup and initialize the Timer block. Timer blocks use peripheral clock as their input and hence peripheral clock must be initialized before Timer is initialized. In our case it is assumed that LPC2148 is connected to 12Mhz XTAL and both CPU and Peripheral Clocks have been setup to tick at 60Mhz.
</div>
<pre><code class="language-cpp">
void initTimer0(void)
{
	/*Assuming that PLL0 has been setup with CCLK = 60Mhz and PCLK also = 60Mhz.*/

	T0CTCR = 0x0;
	T0PR = PRESCALE-1; //(Value in Decimal!) - Increment T0TC at every 60000 clock cycles
	//Count begins from zero hence subtracting 1
	//60000 clock cycles @60Mhz = 1 mS

	T0TCR = 0x02; //Reset Timer
}
</code></pre>
<div class="special sp_blue notestar">
<h4>Prescale (TxPR) Related Calculations:</h4>
<p>The delay or time required for 1 clock cycle at &#8216;X&#8217; MHz is given by :</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/timer/delay_1clock_x_mhz_timer.png" alt="1 Clock cycle Delay at X Mhz" class="aligncenter" width="210px" height="65px"/></p>
<p>Hence in our case when PR=0 i.e TC increments at every PCLK the delay required for TC to increment by 1 is:</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/timer/delay_1clock_cycle_timer.png" alt="1 Clock cycle Delay at 60 Mhz" class="aligncenter" width="220px" height="65px"/></p>
<p>Similarly when we set PR = 59999 the delay in this case will be:</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/pwm/delay_1ms.png" alt="1ms Delay at 60Mhz" class="aligncenter" width="310px" height="65px"/></p>
<p>&#8230; which boils down to<strong> 1/1000 = 0.001</strong> Seconds which is nothing but <strong>1 Milli-Second i.e mS</strong>. Hence the delay required for TC to increment by 1 will be 1mS.
</div>
<h4> #2) delayMS(unsigned int milliseconds); </h4>
<pre><code class="language-cpp">
void delayMS(unsigned int milliseconds) //Using Timer0
{
	T0TCR = 0x02; //Reset Timer

	T0TCR = 0x01; //Enable timer

	while(T0TC < milliseconds); //wait until timer counter reaches the desired delay

	T0TCR = 0x00; //Disable timer
}
</code></pre>
<div class="spaced">
</div>
<h2 class="shead">Real World LPC2148 Timer Examples with sample code</h2>
<h4> Example #1) - Basic Blinky example using Timer </h4>
<p>Now lets make a blinky program which flashes a LED every half a second. Since 0.5 second = 500 millisecond we will invoke 'delayMS' as delayMS(500).</p>
<pre><code class="language-cpp">
/*(C) Umang Gajera - www.ocfreaks.com
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
LPC2148 Basic Timer example.
License : GPL.*/
#include &lt;lpc214x.h&gt;

#define PLOCK 0x00000400
#define PRESCALE 60000   //60000 PCLK clock cycles to increment TC by 1 

void delayMS(unsigned int milliseconds);
void initClocks(void);
void initTimer0(void);
void setupPLL0(void);
void feedSeq(void);
void connectPLL0(void);

int main(void)
{
	initClocks(); //Initialize CPU and Peripheral Clocks @ 60Mhz
	initTimer0(); //Initialize Timer0
	IO0DIR = 0xFFFFFFFF; //Configure all pins on Port 0 as Output

	while(1)
	{
		IO0SET = 0xFFFFFFFF; //Turn on LEDs
		delayMS(500); //0.5 Second(s) Delay
		IO0CLR = 0xFFFFFFFF; //Turn them off
		delayMS(500);
	}
	//return 0; //normally this wont execute ever
}

void initTimer0(void)
{
	/*Assuming that PLL0 has been setup with CCLK = 60Mhz and PCLK also = 60Mhz.*/

	T0CTCR = 0x0;
	T0PR = PRESCALE-1; //(Value in Decimal!) - Increment T0TC at every 60000 clock cycles
	//Count begins from zero hence subtracting 1
	//60000 clock cycles @60Mhz = 1 mS

	T0TCR = 0x02; //Reset Timer
}

void delayMS(unsigned int milliseconds) //Using Timer0
{
	T0TCR = 0x02; //Reset Timer

	T0TCR = 0x01; //Enable timer

	while(T0TC < milliseconds); //wait until timer counter reaches the desired delay

	T0TCR = 0x00; //Disable timer
}

void initClocks(void)
{
    setupPLL0();
    feedSeq(); //sequence for locking PLL to desired freq.
    connectPLL0();
    feedSeq(); //sequence for connecting the PLL as system clock
   
    //SysClock is now ticking @ 60Mhz!
       
    VPBDIV = 0x01; // PCLK is same as CCLK i.e 60Mhz
    
    //Using PLL settings as shown in : https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/
    //PLL0 Now configured!
}


//---------PLL Related Functions :---------------

//Using PLL settings as shown in : https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/

void setupPLL0(void)
{
	//Note : Assuming 12Mhz Xtal is connected to LPC2148.

	PLL0CON = 0x01; // PPLE=1 &#038; PPLC=0 so it will be enabled
					// but not connected after FEED sequence

	PLL0CFG = 0x24; // set the multipler to 5 (i.e actually 4)
					// i.e 12x5 = 60 Mhz (M - 1 = 4)!!!
					// Set P=2 since we want FCCO in range!!!
					// So , Assign PSEL =01 in PLL0CFG as per the table.
}

void feedSeq(void)
{
	PLL0FEED = 0xAA;
	PLL0FEED = 0x55;
}

void connectPLL0(void)
{
	// check whether PLL has locked on to the  desired freq by reading the lock bit
	// in the PPL0STAT register

	while( !( PLL0STAT &#038; PLOCK ));

	// now enable(again) and connect
	PLL0CON = 0x03;
}
</code></pre>
<div class="highlight"><strong>Download Project Source for Example #1 @</strong> <a href='https://www.ocfreaks.com/wp-content/uploads/2013/03/OCFreaks.com_LPC214x_Timer_Tutorial.zip'>OCFreaks.com_LPC214x_Timer_Tutorial.zip</a> [Successfully tested on Keil UV4.70a] </div>
<h4> Example #2) - Blinky example using Timer with Interrupt </h4>
<div class="special sp_red noteinfo">
<strong>Attention Plz! :</strong> Please take your time to go through "initTimer0()" function. Here I've used Match Register 0(T0MR0) and Match Control Register(T0MCR) and setup Interrupt handler which gets triggered when value in TC equals the value in T0MR0. You can find a detailed <strong>tutorial on using and configuring Interrupts on ARM7 LPC2148 Microcontrollers @</strong> <a href="https://www.ocfreaks.com/lpc2148-interrupt-tutorial/">https://www.ocfreaks.com/lpc2148-interrupt-tutorial/</a></p>
<p>Also if you are using Keil Version4(or higher) you'll need to edit Target Option settings and those Crossworks for ARM etc .. you need to manually enable global interrupts - A simple way to make interrupts working is @ <a href="https://www.ocfreaks.com/lpc2148-interrupt-problem-issue-fix">Here</a>
</div>
<pre><code class="language-cpp">
/*(C) Umang Gajera - www.ocfreaks.com
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
LPC2148 Timer example using Interrupt.
License : GPL.*/

#include &lt;lpc214x.h&gt;

#define PLOCK 0x00000400
#define MR0I (1<<0) //Interrupt When TC matches MR0
#define MR0R (1<<1)	//Reset TC when TC matches MR0

#define DELAY_MS 500 //0.5 Seconds Delay
#define PRESCALE 60000 //60000 PCLK clock cycles to increment TC by 1 

void delayMS(unsigned int milliseconds);
void initClocks(void);
void initTimer0(void);
__irq void T0ISR(void);

void setupPLL0(void);
void feedSeq(void);
void connectPLL0(void);


int main(void)
{
	initClocks(); //Initialize CPU and Peripheral Clocks @ 60Mhz
	initTimer0(); //Initialize Timer0

	IO0DIR = 0xFFFFFFFF; //Configure all pins on Port 0 as Output
	IO0PIN = 0xF;

	T0TCR = 0x01; //Enable timer

	while(1); //Infinite Idle Loop

	//return 0; //normally this wont execute ever
}

void initTimer0(void)
{
	/*Assuming that PLL0 has been setup with CCLK = 60Mhz and PCLK also = 60Mhz.*/

	//----------Configure Timer0-------------

	T0CTCR = 0x0;

	T0PR = PRESCALE-1; //(Value in Decimal!) - Increment T0TC at every 60000 clock cycles
	//Count begins from zero hence subtracting 1
	//60000 clock cycles @60Mhz = 1 mS

	T0MR0 = DELAY_MS-1; //(Value in Decimal!) Zero Indexed Count - hence subtracting 1

	T0MCR = MR0I | MR0R; //Set bit0 &#038; bit1 to High which is to : Interrupt &#038; Reset TC on MR0  

	//----------Setup Timer0 Interrupt-------------

	VICVectAddr4 = (unsigned )T0ISR; //Pointer Interrupt Function (ISR)

	VICVectCntl4 = 0x20 | 4; //0x20 (i.e bit5 = 1) -> to enable Vectored IRQ slot
	//0x4 (bit[4:0]) -> this the source number - here its timer0 which has VIC channel mask # as 4
	//You can get the VIC Channel number from Lpc214x manual R2 - pg 58 / sec 5.5

	VICIntEnable = 0x10; //Enable timer0 int

	T0TCR = 0x02; //Reset Timer
}

__irq void T0ISR(void)
{
	long int regVal;
	regVal = T0IR; //Read current IR value
		
	IO0PIN = ~IO0PIN; //Toggle the state of the Pins

	T0IR = regVal; //Write back to IR to clear Interrupt Flag
	VICVectAddr = 0x0; //This is to signal end of interrupt execution
}

void initClocks(void)
{
	setupPLL0();
	feedSeq(); //sequence for locking PLL to desired freq.
	connectPLL0();
	feedSeq(); //sequence for connecting the PLL as system clock

	//SysClock is now ticking @ 60Mhz!
	   
	VPBDIV = 0x01; // PCLK is same as CCLK i.e 60Mhz

	//PLL0 Now configured!
}

//---------PLL Related Functions :---------------

//Using PLL settings as shown in : https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/

void setupPLL0(void)
{
	//Note : Assuming 12Mhz Xtal is connected to LPC2148.

	PLL0CON = 0x01; // PPLE=1 & PPLC=0 so it will be enabled
					// but not connected after FEED sequence

	PLL0CFG = 0x24; // set the multipler to 5 (i.e actually 4)
					// i.e 12x5 = 60 Mhz (M - 1 = 4)!!!
					// Set P=2 since we want FCCO in range!!!
					// So , Assign PSEL =01 in PLL0CFG as per the table.
}

void feedSeq(void)
{
	PLL0FEED = 0xAA;
	PLL0FEED = 0x55;
}

void connectPLL0(void)
{
	// check whether PLL has locked on to the  desired freq by reading the lock bit
	// in the PPL0STAT register

	while( !( PLL0STAT & PLOCK ));

	// now enable(again) and connect
	PLL0CON = 0x03;
}
</code></pre>
<div class="highlight"><strong>Download Project Source for Example #2 @</strong> <a href='https://www.ocfreaks.com/wp-content/uploads/2013/03/OCFreaks.com_LPC214x_TimerIRQ_Tutorial.zip'>OCFreaks.com_LPC214x_TimerIRQ_Tutorial.zip</a> [Successfully tested on Keil UV4.70a] </div>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-timer-tutorial/">LPC2148 Timer Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/lpc2148-timer-tutorial/feed/</wfw:commentRss>
			<slash:comments>40</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1072</post-id>	</item>
		<item>
		<title>Interfacing 16X2 LCD with LPC2148 tutorial</title>
		<link>https://www.ocfreaks.com/interfacing-16x2-lcd-with-lpc2148-tutorial/</link>
					<comments>https://www.ocfreaks.com/interfacing-16x2-lcd-with-lpc2148-tutorial/?noamp=mobile#comments</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Wed, 19 Dec 2012 14:54:01 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[featured]]></category>
		<category><![CDATA[lpc2148]]></category>
		<category><![CDATA[mcu]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=734</guid>

					<description><![CDATA[<p>On special request of our readers today we are posting a new tutorial for interfacing HD44780U / KS0066U based 16x2 LCD Module with LPC2148 microcontroller.</p>
<p>The post <a href="https://www.ocfreaks.com/interfacing-16x2-lcd-with-lpc2148-tutorial/">Interfacing 16X2 LCD with LPC2148 tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><!-- img class="aligncenter" src="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/s/lpc2148-lcd-cover.jpg" /--></p>
<p><span class="shead"><span class="sheadin">Introduction</span></span><br />
Interfacing a 16X2 or 16X4 LCD module with a 3.3V MCU is not same as interfacing with MCUs like AVR which operate on 5Volts. As per request by some of the readers of previous articles on lpc2148 this article is on Interfacing a 5V LCD Module with LPC2148 MCU and in general for any ARM or 3.3V MCU .</p>
<p><strong>Whats next?</strong> : A simple Library for LCD interfacing with LPC214x and LPC176x MCUs. And more tutorials on Timer , PWM , UART .. for lpc214x , lpc176x.</p>
<p>For this article I&#8217;ve used the readily available Chinese 16X2 LCD Module :<strong> JHD-162A</strong>. The Data sheet for JHD-162A 16X2 LCD Module is located at : http://www.egochina.net.cn/eBay/Download/JHD162A.pdf I would strongly suggest that you keep that datasheet open when reading this article to avoid any sort of confusion.</p>
<p>Before Starting the Tutorial , as a motivation , I would like to show the final outcome of this tutorial. The pic below shows the LPC2148 development board interfaced with JHD162A LCD Module via 2x HCF4050B ICs. I&#8217;ll come to HCF4050B IC shortly .. at the moment lets get started with the  basics.</p>
<p><a href="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/lpc2148-lcd-1.png"><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/s/lpc2148-lcd-1.jpg" /></a></p>
<p><span class="shead"><span class="sheadin">16X2 LCD Basics :</span></span></p>
<p>This particular chinese LCD i.e JHD162A has KS0066U controller or similar to the famous HD44780U. It Consists of 2 Rows with 16 Characters on each. It has a 16 pin Interface. Operates on 5V and has LED backlight. Works in 2 Modes :</p>
<ul>
<li>1) <strong>Instruction Mode :</strong> Used for initializing and configuring LCD before we can use it &#038; during operation.</li>
<li>2) <strong>Data Mode :</strong> Displays the respective characters for codes supplied to it via Data Pins.</li>
</ul>
<p>Standard Pinout is as follows :</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/jhd162a-lcd-pinout.jpg" /></p>
<p>To keep things simple lets group the pins into 3 :<br />
<b>1) Power Pins :</b> Pin 1,2,3,15,16<br />
<b>2) Control Pins :</b> Pin 4,5,6<br />
<b>3) Data Pins :</b> Pin 7 to 14</p>
<div class="highlight">
<strong>Now lets see some of the important pins before we actually start writing programs for LCD:</strong></p>
<ul>
<li><strong>Contrast Voltage (VEE) :</strong> Instead of using a trim pot just connect a 1K resistor to VEE in series and ground it. That gives the best contrast of what I&#8217;ve seen.</li>
<p></p>
<li><strong>RS &#8211; short for Register select (Control Pin) :</strong> Used to switch been Instruction and Data Mode. RS = High for Instruction Mode and RS = Low for Data mode.</li>
<p></p>
<li><strong>R/W &#8211; Read or Write (Control Pin):</strong> R/W = High for Read Mode and R/W = Low for Write. Since we are going to use Write Mode only we will permanently ground it using a pull-down resistor of 4.7K Ohms. Caution : If you are planning to use Read Mode with 3.3V MCUs you&#8217;ll need a Bi-directional level shifter which can shift 5V to 3.3V and Vice-Versa.
</li>
<p></p>
<li><strong>Enable (Control Pin) :</strong> This is similar to a trigger pin. Each Data/Instruction is executed by the LCD module only when a pulse is applied to Enable pin. More specifically the it is executed at the falling edge of the pulse.</li>
</ul>
</div>
<p>If you want to know the fundamentals and internal operation of LCD Modules I would recommend visiting these links :</p>
<ul>
<li><a href="http://lcd-linux.sourceforge.net/pdfdocs/lcd1.pdf" target="_blank" rel="noopener">http://lcd-linux.sourceforge.net/pdfdocs/lcd1.pdf</a></li>
<li><a href="http://lcd-linux.sourceforge.net/pdfdocs/lcd2.pdf" target="_blank" rel="noopener">http://lcd-linux.sourceforge.net/pdfdocs/lcd2.pdf</a></li>
<li><a href="http://www.8051projects.net/lcd-interfacing/" target="_blank" rel="noopener">http://www.8051projects.net/lcd-interfacing/</a> (Covers a lot of basics .. I found it really helpful.)
</li>
</ul>
<p><span class="shead"><span class="sheadin">Interfacing 5V LCD Module with lpc214x:</span></span></p>
<p>5V LCD wont operate on 3.3V and also since lpc214x runs on 3.3V and 5Volts might fuse some of its GPIO pin we are gonna need a level shifter or translator that can shift 3.3Volts to 5Volts for LCD Module to be safe. Vikram Sharma M. has successfully interfaced JHD162A with MSP430 MCU using CD4050B IC as explained in his post @ <a href="http://msharmavikram.wordpress.com/2012/08/14/3-mistakes-of-lcd-with-msp430-solved/" target="_blank" rel="noopener">http://msharmavikram.wordpress.com/2012/08/14/3-mistakes-of-lcd-with-msp430-solved/</a> .</p>
<p>Some of the buffers / level shifters than can be used are : SN74AHC244N , SN74AC241N , CD74AC244E , CD4050B , etc.. out of which CD4050B is readily available. I purchased a few HCF4050 which is same as CD4050B except it is manufactured by ST Microelectronics and not Texas Instruments.</p>
<p>HCF4050B is a non-inverting Hex Buffer. In our case we&#8217;ll be needing 2 of them at minimum since we are going to use 8+2=10 pins from MCU which need to be shifted to 5Volts. There are going to be 8 Data pins and 2 control pins connected from MCU to the LCD Module using 2x HCF4050B.</p>
<div class="snippet_darkred">NOTE : As far as possible use the 1st eight consecutive pins(at any cost!) from any port of the MCU as Data Pins which will make sending character data or commands(Instructions) to LCD very easy. For e.g. in our case we will use P0.0 to P0.7 as data pins and P1.16 as RS pin and P1.17 as Enable pin.</div>
<p>Connections from lpc214x to LCD module will be as shown below. I&#8217;ve also made a PCB for the schematic if bread-board is not your cup of tea.(Frankly , since I was running out of time I did it using a bread-board. If some one is having any problems with PCB please let me know.)</p>
<p><a href="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/lcd-arm-interface.png"><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/s/lcd-arm-interface.jpg" /></a></p>
<div class="snippet_darkblue">
<strong>Pin Connections from MCU->HFC4050B->LCD:</strong></p>
<p>1)Pin Connections from MCU to HCF4050B ICs :<br />
IC1 : P1.16->Pin3 , P1.17->Pin5 , P0.0->Pin7 , P0.1->Pin9 , P0.2->Pin11 , P0.3->Pin14<br />
IC2 : P0.4->Pin3 , P0.5->Pin5 , P0.6->Pin7 , P0.7->Pin9 (Pins 11,14 of IC2 are NC)</p>
<p>2)Pin Connections from HCF4050B to LCD Module :<br />
IC1 : Pin2->RS , Pin4->Enable , Pin6->DB0 , Pin10->DB1 , Pin12->DB2 , Pin15->DB3<br />
IC2 : Pin2->DB4 , Pin4->DB5 , Pin6->DB6 , Pin10->DB7 (Pin 12,15 of IC2 are NC)
</p></div>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/pcb.png" /></p>
<h4>Download links to PCB pdf and Eagle 6.2+ (Schematic+Board) Design Files:</h4>
<p>1) <a href="https://www.ocfreaks.com/forums/attachment.php?attachmentid=1573&#038;d=1355926263">lcd_interface_arm_mcu_PCB-pdf.rar</a><br />
2) <a href="https://www.ocfreaks.com/forums/attachment.php?attachmentid=1574&amp;d=1355926264">lcd_interface_arm_mcu-EAGLE-6.2+design-files.rar</a> </p>
<p><b>Components required at bare minimum :</b></p>
<ul>
<li>LPC214x Development board</li>
<li>16X2 LCD Module</li>
<li>2x HFC4050B or CD4050B</li>
<li>5V Supply for LCD </li>
<li>Resistors : 1x 1K , 1x 4.4K , 1x 47 Ohms</li>
<li>Bread-Board / Self-made PCB</li>
<li>Berg Strips , Jumper wires</li>
</ul>
<div class="snippet_darkred"><strong>NOTE :</strong> You need to connect the &#8216;GND&#8217; from MCU Board to &#8216;LCD&#8217;s GND&#8217; else its not gonna work. Diode D1 and Decoupling Capacitors C1 &#038; C2 are optional. D1 can be replaced by a short and C1,C2 can be ignored.</div>
<div class="vspacer_20px"> </div>
<p><span class="shead"><span class="sheadin">Initializing LCD Module :</span></span></p>
<p>After you have cross-checked all your connections from MCU to HFC4050 to LCD Module its time now to Display text on LCD. But before that we need to initialize the LCD properly. Also NOTE that : as per the Datasheet &#8211; before initializing LCD we need to wait for a minimum time of about 15ms after the input voltage supply is stable and >4.5Volts. </p>
<p>The 1st thing required for this is that RS must be held LOW and Enable also LOW in the beginning. Now we must supply some commands using the Data Pins to LCD. But this command wont be executed until we supply a pulse to Enable Pin. After supplying the command we make enable High and then Low after a short delay(i.e a Pulse) and the command is executed.</p>
<p>Codes for various instructions can be obtained from below table : (Refer to page 12 of the Datasheet)</p>
<p><a href="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/lcd-instruction-set.jpg"><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/s/lcd-instruction-set.jpg" /></a></p>
<div class="highlight">For beginners we are only interested in following commands (which are also executed in given order during initialization):</p>
<ol>
<li>Function Set</li>
<li>
Display Switch</li>
<li>
Input Set</li>
<li>Screen Clear Command</li>
<li>DDRAM AD Set &#8211; Resposition Cursor Command</li>
</ol>
<ul>
<li>In our case we are going to use 8Bit Mode with 2Rows and 5&#215;10 Font style which gives Function set as 0x3C.</li>
<li>
Next we issue Display On , Cursor On , Blink On which gives Display Switch as 0x0F.</li>
<li>
Next we select Increment Mode which gives Input Set as 0x06.</li>
<li>
Now we clear the screen and set cursor at Home which gives Screen Clear as 0x01.</li>
<li>
Also the command to reposition the cursor at Home at anytime is 0x80 and to reposition it at the 1st column of the second row is 0xC0.</li>
</ul>
</div>
<p><b>To cut-short or in simple words:</b> We need to supply 5 commands in given order to the Data Pins with a small amount of delay in between to initialize the LCD properly. These commands are : 0x3C , 0x0F , 0x06 , 0x01 , 0x80(actually not required). Now since we have used the the 1st eight pins of port 0 on lpc214x we can directly assign these values to the IO0PIN register. Note that Bit 0 i.e LSB in IO0PIN is towards extreme right and the bit number increases as we move towards the left approaching the MSB. Also since we have connected P0.0 to Data Bit 0 pin , P0.1 to Data bit 1 and so on &#8230; we have a one to one consecutive mapping between them MCU pins and LCD Data pins.</p>
<div class="snippet_darkblue"> <b>NOTE:</b> During Initialization , issuing 0x80 command to reposition the cursor at home will be of no use since it would already be at Home after issuing Screen Clear Command. Also we need to Supply a Pulse to Enable after issuing each command and wait for a short while for the LCD controller to process it. Datasheet says a delay of 40us is required at minimum. But even a delay of 1ms wont hurt us to be on the safer side.</div>
<div class="snippet_darkyellow"><b><br />
Correct Sequence for Initializing LCD module (8 Bit Interface) is as given :</p>
<ol>
<li>After LCD Module is powered on and MCU boots wait for 20ms (Specifically >15ms)</li>
<p></p>
<li>Now make RS and Enable LOW making sure that R/W is permanently grounded.</li>
<p></p>
<li>Issue Function set command &#8211; 0x3C and Pulse Enable (wait for >40us after pulsing).</li>
<p></p>
<li>Issue Display Switch &#8211; 0x0F and Pulse Enable (wait for >40us after pulsing).</li>
<p></p>
<li>Issue Input Set &#8211; 0x06 and Pulse Enable (wait for >40us after pulsing).</li>
<p></p>
<li>Issue Screen Clear &#8211; 0x01 and Pulse Enable (wait for >1.64ms after pulsing).</li>
<p></p>
<li>Issue DDRAM AD Set &#8211; 0x80 and Pulse Enable (wait for >40us after pulsing).</li>
</ol>
<p></b></div>
<p>Now LCD is ready for Printing!</p>
<p>After Initializing the LCD you would see a Cursor Blinking at in 1st column at row 1 :</p>
<p><a href="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/2.jpg"><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/s/2.jpg" /></a></p>
<p><span class="shead"><span class="sheadin">Printing Characters on LCD Module :</span></span></p>
<p>Now that we have initialized LCD correctly its time to send some characters. For this we enter into Data Mode by making RS High. After that any number / code applied to Data pins will be converted into corresponding Character and displayed. The codes for the characters are ASCII Codes given by the font table as show below :</p>
<p><a href="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/lcd-font-table.jpg"><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/s/lcd-font-table.jpg" /></a></p>
<p>Hence to print a character on LCD we just need to apply the 8bit ASCII code to Data pins. This can be done easily by type casting the character into an integer (which is done automatically by the compiler &#8211; but we are doing it here explicitly). Refer to Page 14 of datasheet for font table.</p>
<p>Now , after printing 16 characters you might wanna print more characters in 2nd Row. This is done by entering into Instruction Mode by holding RS=LOW and giving command 0xC0 which repositions the cursor at 2nd Row &#038; 1st column. Needless to say that we need to again give a Pulse on Enable pin to process it. After that we put RS to high and get back in Data mode.</p>
<div class="snippet_darkblue"><b>Note on Repositioning the Cursor :</b> As we have seen , the command for repositioning the cursor at home is 0x80. Think of 0x80 as the base address of Display Data Ram(DDRAM). Hence 0x80 will set cursor at 1st Row, 1st Column. If you add 0x01 to 0x80 the cursor will go to 2nd column in 1st row. Likewise if you add 0x0F to 0x80 you&#8217;ll end up at 1st row , last column. Similar to this the base address for 2nd Row is 0x40 and not 0x10! 0x10 is the base address of 3rd row and 0x50 is base address for 4th row in the case of 16&#215;4 LCD Modules. So to position the cursor at 2nd row, 1st column we must add 0x40 to 0x80 which gives 0x80+0x40=0xC0. Hence after giving command &#8216;0xC0&#8217; cursor comes down to 2nd row &#038; 1st col.</p>
<p>More on DDRAM and CGROM @ <a href="http://www.8051projects.net/lcd-interfacing/basics.php" target="_blank" rel="noopener">http://www.8051projects.net/lcd-interfacing/basics.php</a>
</div>
<div class="vspacer_25px"> </div>
<p><span class="shead"><span class="sheadin">Programs for Lpc2148 to print Characters on LCD module :</span></span></p>
<p>1st Let me explain a few functions that I&#8217;ve made in the programs below:</p>
<ul>
<li><strong>initLCD() :</strong> Initializes the LCD.</li>
<li><strong>enable() : </strong>To generate a pulse on Enable pin.</li>
<li><strong>LCD_Cmd(int) :</strong> To issue LCD commands.</li>
<li><strong>LCD_WriteChar(char) : </strong>Print a Single Character.</li>
<li><strong>LCD_WriteString(string) : </strong>Print a String.</li>
<li><strong>delay() :</strong>Generate required delay.(For program1 Only!)</li>
<li><strong>delayMS(int) :</strong> Generate specificed delay.(For program2 Only!)</li>
</ul>
<h4>Program #1 : Basic Program without precise Timing &#038; no PLL setup.</h4>
<pre><code class="language-cpp">
/*
 (C) OCFreaks! | Umang Gajera 2012-13.
 More Embedded tutorials @ www.ocfreaks.com/cat/embedded
 >>Program for Interfacing 16X2 LCD Module with LPC2148
*/

#include &lt;lpc214x.h&gt;

/*
 Connections from LPC2148 to LCD Module:
 P0.0 to P0.7 used as Data bits.
 P1.16 connected to pin4 i.e. RS	- Command / Data
 P1.17 connected to pin6 i.e. E - Enable
 Pin5 of LCD Module i.e. 'R/W' connected to ground
*/	

void initLCD(void);
void enable(void);
void LCD_WriteChar(char c);
void LCD_WriteString(char * string);
void LCD_Cmd(unsigned int cmd);
void delay(void);


int main(void)
{
	initLCD(); //LCD Now intialized and ready to Print!
	LCD_WriteString(".: Welcome to :.");
	LCD_Cmd(0x80 + 0x40); //Come to 2nd Row
	LCD_WriteString("www.OCFreaks.com");
	while(1); // Loop forever	 
	return 0; //This won't execute :P
}

void initLCD(void)
{
	IO0DIR = 0xFF; //P0.0 to P0.7 configured as Output - Using 8 Bit mode
	IO1DIR |= (1<<16) | (1<<17); //P1.16 and P1.17 configured as Output - Control Pins
	IO0PIN = 0x0; //Reset Port0 to 0.	
	IO1PIN = 0x0; //Reset Port1 to 0 - Which also makes RS and Enable LOW.

	//LCD Initialization Sequence Now starts
	delay(); //Initial Delay
	LCD_Cmd(0x3C); //Function Set Command : 8 Bit Mode , 2 Rows , 5x10 Font Style
	LCD_Cmd(0x0F); //Display Switch Command : Display on , Cursor on , Blink on
	LCD_Cmd(0x06); //Input Set : Increment Mode 
	LCD_Cmd(0x01); //Screen Clear Command , Cursor at Home
	LCD_Cmd(0x80); //Not required the 1st time but needed to reposition the cursor at home after Clearing Screen 
	//Done!
}

void enable(void)
{
	delay();
	IO1PIN |=  (1<<17);//Enable=High
	delay();
	IO1PIN &#038;= ~(1<<17);//Enable=Low
	delay();
}

void LCD_WriteChar(char c)
{
	IO1PIN |= (1<<16); //Switch to Data Mode
	IO0PIN = (int) c; //Supply Character Code
	enable(); //Pulse Enable to process it
}

void LCD_WriteString(char * string)
{
	int c=0;
	while (string[c]!='\0')
	{
		LCD_WriteChar(string[c]);
		c++;
	}
}
		
void LCD_Cmd(unsigned int cmd)
{
	IO1PIN = 0x0; //Enter Instruction Mode
	IO0PIN = cmd; //Supply Instruction/Command Code
	enable(); //Pulse Enable to process it
}

void delay(void)
{
	int i=0,x=0;
	for(i=0; i<19999; i++){ x++; }
}
</code></pre>
<h4>Program #2 : Program with PLL* setup and precise Timing**. (Xtal=12Mhz , CCLK=60Mhz)</h4>
<p>*PLL tutorial for LPC2148 @ <a href="https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/" target="_blank" rel="noopener">https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/</a><br />
**Tutorial on Timer for LPC2148 @ <a href="https://www.ocfreaks.com/lpc2148-timer-tutorial/" target="_blank" rel="noopener">https://www.ocfreaks.com/lpc2148-timer-tutorial/</a></p>
<pre><code class="language-cpp">
/*
 (C) OCFreaks! | Umang Gajera 2012-13.
 More Embedded tutorials @ www.ocfreaks.com/cat/embedded
 >>Program2 for Interfacing 16X2 LCD Module with LPC2148
 >>using Timer0 and CPU @ 60Mhz
*/


#include &lt;lpc214x.h&gt;

/*
 XTAL Freq=12 Mhz , CCLK=60Mhz , PCLK=60Mhz
 Using common delay of 2ms for all operations
 	
 Connections from LPC2148 to LCD Module:
 P0.0 to P0.7 used as Data bits.
 P1.16 connected to pin4 i.e. RS    - Command / Data
 P1.17 connected to pin6 i.e. E - Enable
 Pin5 of LCD Module i.e. 'R/W' connected to ground
*/ 

#define PLOCK 0x00000400

void initLCD(void);
void enable(void);
void LCD_WriteChar(char c);
void LCD_WriteString(char * string);
void LCD_Cmd(unsigned int cmd);
void delayMS(unsigned int milliseconds);

void setupPLL0(void);
void feedSeq(void);
void connectPLL0(void);


int main(void)
{
    setupPLL0();
    feedSeq(); //sequence for locking PLL to desired freq.
    connectPLL0();
    feedSeq(); //sequence for connecting the PLL as system clock
   
    //SysClock is now ticking @ 60Mhz!
       
    VPBDIV = 0x01; // PCLK is same as CCLK i.e 60Mhz
    
	//PLL0 Now configured!

	initLCD(); //LCD Now intialized and ready to Print!
    LCD_WriteString(".: Welcome to :.");
    LCD_Cmd(0x80 + 0x40); //Come to 2nd Row
    LCD_WriteString("www.OCFreaks.com");
    while(1); // Loop forever    
    return 0; //This won't execute :P
}

void initLCD(void)
{
    IO0DIR = 0xFF; //P0.0 to P0.7 configured as Output - Using 8 Bit mode
    IO1DIR |= (1<<16) | (1<<17); //P1.16 and P1.17 configured as Output - Control Pins
    IO0PIN = 0x0; //Reset Port0 to 0.  
    IO1PIN = 0x0; //Reset Port1 to 0 - Which also makes RS and Enable LOW.

    //LCD Initialization Sequence Now starts
    delayMS(20); //Initial Delay
    LCD_Cmd(0x3C); //Function Set Command : 8 Bit Mode , 2 Rows , 5x10 Font Style
    LCD_Cmd(0x0F); //Display Switch Command : Display on , Cursor on , Blink on
    LCD_Cmd(0x06); //Input Set : Increment Mode
    LCD_Cmd(0x01); //Screen Clear Command , Cursor at Home
    LCD_Cmd(0x80); //Not required the 1st time but needed to reposition the cursor at home after Clearing Screen
    //Done!
}

void enable(void)
{
    //Using common delay of 2ms
	delayMS(2);
    IO1PIN |=  (1<<17);//Enable=High
    delayMS(2);
    IO1PIN &#038;= ~(1<<17);//Enable=Low
    delayMS(2);
}

void LCD_WriteChar(char c)
{
    IO1PIN |= (1<<16); //Switch to Data Mode
    IO0PIN = (int) c; //Supply Character Code
    enable(); //Pulse Enable to process it
}

void LCD_WriteString(char * string)
{
    int c=0;
    while (string[c]!='\0')
    {
        LCD_WriteChar(string[c]);
        c++;
    }
}
       
void LCD_Cmd(unsigned int cmd)
{
    IO1PIN = 0x0; //Enter Instruction Mode
    IO0PIN = cmd; //Supply Instruction/Command Code
    enable(); //Pulse Enable to process it
}

void delayMS(unsigned int milliseconds)
{
//Timers will be explained in detail in the very next tutorial @ www.ocfreaks.com/cat/embedded/
	
	T0IR = 0;
	T0CTCR = 0;
	T0PR = 60000; //60000 clock cycles @60Mhz = 1 mS
	T0PC = 0;
	T0TC = 0;
	T0TCR = 0x01; //enable timer
	
	//wait until timer counter reaches the desired dela1y	
	while(T0TC < milliseconds);
	
	T0TCR = 0x00; //disable timer
}

//---------PLL Related Functions :---------------

void setupPLL0(void)
{
    PLL0CON = 0x01; // PPLE=1 &#038; PPLC=0 so it will be enabled
                    // but not connected after FEED sequence
    PLL0CFG = 0x24; // set the multipler to 5 (i.e actually 4)
                    // i.e 12x5 = 60 Mhz (M - 1 = 4)!!!
                    // Set P=2 since we want FCCO in range!!!
                    // So , Assign PSEL =01 in PLL0CFG as per the table.
}

void feedSeq(void)
{
    PLL0FEED = 0xAA;
    PLL0FEED = 0x55;
}

void connectPLL0(void)
{
    // check whether PLL has locked on to the  desired freq by reading the lock bit
    // in the PPL0STAT register

    while( !( PLL0STAT &#038; PLOCK ));

    // now enable(again) and connect
    PLL0CON = 0x03;
}
</code></pre>
<p>Output after flashing any of the above code to MCU (Successfully Tested on Lpc2148 and Lpc1768) :</p>
<p><a href="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/lpc2148-lcd-2.jpg"><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/s/lpc2148-lcd-2.jpg" /></a></p>
<h4>Download Links to Source Code using KEIL UV4 Project IDE :</h4>
<p>1) Program #1 : <a href="https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC2148_LCD_Interfacing.rar">OCFreaks.com_LPC2148_LCD_Interfacing.rar</a><br />
2) Program #2 : <a href="https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC2148_LCD_Interfacing_precise.rar">OCFreaks.com_LPC2148_LCD_Interfacing_precise.rar</a></p>
<p><span class="shead"><span class="sheadin">Silence please : 2 LCDs were Harmed in the making</span></span><br />
While working on this article 2 of my Green LCDs were damaged. One got completely useless and other got partially damaged. In any case both of them are useless now.</p>
<p>LCD #1 : Display completely gone kaput..</p>
<p><a href="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/6.jpg"><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/s/6.jpg" /></a></p>
<p>LCD #2 : Display partially gone kaput..</p>
<p><a href="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/5.jpg"><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/arm-lpc-mcu-lcd-interfacing/s/5.jpg" /></a></p>
<p>The post <a href="https://www.ocfreaks.com/interfacing-16x2-lcd-with-lpc2148-tutorial/">Interfacing 16X2 LCD with LPC2148 tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/interfacing-16x2-lcd-with-lpc2148-tutorial/feed/</wfw:commentRss>
			<slash:comments>12</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">734</post-id>	</item>
		<item>
		<title>LPC214x PLL Tutorial for Cpu and Peripheral Clock</title>
		<link>https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/</link>
					<comments>https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/?noamp=mobile#comments</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Wed, 07 Nov 2012 19:08:03 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<category><![CDATA[mcu]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=496</guid>

					<description><![CDATA[<p>Introduction PLL stands for Phase Locked Loop and is used to generate clock pulse given a reference clock input which is generally from a crystal oscillator(or XTAL). Configuring and using PLL in lpc124x MCUs is pretty simple and straight forward. Lpc214x MCUs have 2 PLL blocks viz. PPL0 and PLL1. PLL0 is used to generate [&#8230;]</p>
<p>The post <a href="https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/">LPC214x PLL Tutorial for Cpu and Peripheral Clock</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><span class="shead"><span class="sheadin">Introduction</span></span></p>
<p>PLL stands for Phase Locked Loop and is used to generate clock pulse given a reference clock input which is generally from a crystal oscillator(or XTAL). Configuring and using PLL in lpc124x MCUs is pretty simple and straight forward.</p>
<p>Lpc214x MCUs have 2 PLL blocks viz. PPL0 and PLL1. PLL0 is used to generate the System Clock which goes to CPU and on-chip peripherals while PPL1 is strictly for USB. PLL interrupts are only available for PLL0 and not for PLL1. Input clock to both the PLLs must be between 10Mhz to 25Mhz strictly. This input clock is multiplied with a suitable multiplier and scaled accordingly. But here we have a upper limit of 60Mhz which is the maximum frequency of operation for lpc214x MCUs. PLLs also contain CCOs i.e current controlled oscillators which we are not much concerned about &#8230; atleast in the case of lpc214x MCUs. CCOs operate in range of 156Mhz to 320Mhz and there is also a divider to force CCOs to remain in their range.</p>
<p><span class="shead"><span class="sheadin">Basics</span></span></p>
<p>Lets define some symbols which are used in Datasheet and also which I&#8217;ll be using in this tutorial :</p>
<p><center></p>
<table>
<tr>
<td><strong>FOSC </strong> </td>
<td>=> frequency from the crystal oscillator(XTAL)/external clock</td>
</tr>
<tr>
<td><strong>FCCO </strong> </td>
<td>=> frequency of the PLL Current Controlled Oscillator(CCO)</td>
</tr>
<tr>
<td><strong>CCLK </strong> </td>
<td>=> PLL output frequency (CPU Clock)</td>
</tr>
<tr>
<td><strong>M </strong></td>
<td>=> PLL Multiplier value from the MSEL bits in the PLLCFG register</td>
</tr>
<tr>
<td><strong>P </strong> </td>
<td>=> PLL Divider value from the PSEL bits in the PLLCFG register</td>
</tr>
<tr>
<td><strong>PCLK </strong> </td>
<td>=> Peripheral Clock which is derived from CCLK</td>
</tr>
</table>
<p>[Table 1]<br />
</center></p>
<div class="special sp_blue noteinfo">
<span class="font_15px"><strong>Now , PLL output clock is given by the formula:</strong></span></p>
<ul>
<li><strong>CCLK = M x FOSC</strong> </ul>
</li>
<ul>
=or= </ul>
<ul>
<li><strong>CCLK = FCCO / (2 x P)</strong></ul>
</li>
<p><span class="font_15px"><strong>CCO output clock is given by:</strong></span></p>
<ul>
<li><strong>FCCO = CCLK x 2 x P </strong></ul>
</li>
<ul>
=or= </ul>
<ul>
<li><strong>FCCO = FOSC x M x 2 x P</strong></ul>
</li>
</div>
<p><strong><br />
Note from Datasheet:</strong></p>
<div class="special sp_blue noteinfo">The PLL inputs and settings must meet the following:</p>
<ul style="margin-bottom:0px">
<li>FOSC is in the range of 10 MHz to 25 MHz.</li>
<li>CCLK is in the range of 10 MHz to Fmax (the maximum allowed frequency for the microcontroller &#8211; determined by the system microcontroller is embedded in).</li>
<li>FCCO is in the range of 156 MHz to 320 MHz.</li>
</ul>
</div>
<div class="vspacer_25px"> </div>
<p><span class="shead"><span class="sheadin">Setting-up and using PLL</span></span></p>
<p>Here we&#8217;ll be focusing on PLL0. We&#8217;ll see PLL1 some other day since its not required at this moment. Explaining the internal working of PLLs and CCOs in detail is not in the scope of this tutorial. Here we are just concerned about its usage rather than knowing its internals. To play with PLLs we are given a Multiplier and a Divider which decide the output frequency/clock form PLL block. But we must play cautiously with PLLs since the CPU and other MCU peripherals operate on the clock provided buy it. So if PLL is deliberately or accidentally miss-configured the MCU may go nuts! Miss configuring it deliberately is the sole responsibility of the user and not us ;P Now .. to handle the accident part .. we have a got a FEED Sequence which needs to be issued whenever we wanna configure PPL. You can visualize this by imagining PLL to be enclosed in a safe or a locker. To access PLL we need to have a key to open the safe in order to use or configure it. The key here is the &#8216;Feed Sequence&#8217;. Feed Sequence is nothing but assignment of 2 particular &#8216;fixed&#8217; values to a register related to the PLL block. This register is called &#8216;PLL0FEED&#8217;. And those fixed values are 0xAA and 0x55 are &#8216;in order&#8217;!. Hence the code for feed sequence must be :</p>
<pre><code class="language-cpp">
PLL0FEED = 0xAA;
PLL0FEED = 0x55;
</code></pre>
<p>Now , lets see the other PLL Registers we can use:</p>
<div class="highlight">
<strong><span class="font_15px">1)PLLxCON:</span>(x= PLL block 0 or 1)</strong> This is the PLL Control register used to enable and &#8216;connect&#8217; the PLL. The first bit is used to &#8216;Enable&#8217; PLL. The second bit is used to &#8216;Connect&#8217; the PLL.</p>
<div class="special sp_cyan noteinfo"><strong>Note #1:</strong> Initially when the MCU boots-up it uses its internal RC Oscillator as clock source. When the PLL is setup and enabled we must switch from internal RC Oscillator to the PLL&#8217;s output clock. This is referred to as &#8216;Connecting&#8217; the PLL. PLL is only connected when both &#8216;Enable&#8217; and &#8216;Connect&#8217; bits are 1 and a valid feed sequence is applied.</div>
<p><strong><span class="font_15px">2)PLLxCGF:</span></strong> The multiplier and divider values are stored in this register. The 1st 5 bits (called <strong>MSEL</strong>) are used to store the Multiplier(M). Next 2 bits i.e 5,6 (called <strong>PSEL</strong>) are used to store the value of the Divider(P).</p>
<p><strong><span class="font_15px">3)PLLxSTAT:</span></strong> This is a read only register and contains current status of PLL enable , connect , M and P values. You can read more about the bits in datasheet &#8211; page #37. We are more interested in the 10th bit of PLLxSTAT since it contains the lock status. When we setup and enable the PLL it takes a while for PLL to latch on the target frequency. After PLL has latched or locked to target frequency this bit becomes 1. Only after this we can connect PLL to the CPU. Hence , we need to wait for PLL to lock on to target frequency.
</div>
<div class="special sp_red noteinfo">The <strong>Order</strong> of setting up PLLs is <strong>strictly</strong> as follows :</p>
<ol style="margin-bottom:0px">
<li>Setup PLL</li>
<li>Apply Feed Sequence</li>
<li>
Wait for PLL to lock and then Connect PLL</li>
<li>Apply Feed Sequence</li>
</ol>
</div>
<p><strong>Note that we have to apply Feed Sequence strictly as mentioned above.</strong></p>
<p><span class="shead"><span class="sheadin">Calculating PLL0 Settings</span></span></p>
<div class="highlight">
<ol style="margin-bottom:0px">
<li>Select the CPU Frequency. This selection may be based many factors and end application. On Chip peripheral devices may be running on a lower clock than the processor.</li>
<li>Choose an oscillator frequency (FOSC). CCLK must be the whole (non-fractional) multiple of FOSC. Which leaves us with a few values of Xtal for the selected CPU frequency (CCLK).</li>
<li>Calculate the value of M to configure the MSEL bits. M = CCLK / FOSC. M must be in the range of 1 to 32. The value written to the MSEL bits in PLLCFG is M &#8211; 1.</li>
<li>Find a value for P to configure the PSEL bits, such that FCCO is within its defined frequency limits. FCCO is calculated using the equation given above. P must have one of the values 1, 2, 4, or 8.</li>
</ol>
</div>
<p>Values of PSEL for P are :<br />
<center></p>
<table>
<tr>
<td><strong> P </strong></td>
<td><strong>Value</strong>(binary) in <strong>PSEL Bit(5,6)</strong></td>
</tr>
<tr>
<td> 1 </td>
<td> 00 </td>
</tr>
<tr>
<td> 2 </td>
<td> 01 </td>
</tr>
<tr>
<td> 4 </td>
<td> 10 </td>
</tr>
<tr>
<td> 8 </td>
<td> 11 </td>
</tr>
</table>
<p>[Table 2]<br />
</center></p>
<p>Now , Since there is no point in running the MCU at lower speeds than 60Mhz (except in some situations) ill put a table giving values for M and P for different crystal frequencies i.e FOSC. </p>
<div class="special sp_yellow noteinfo"><strong>The Value of P depends on the selection of CCLK. So , for CCLK=60Mhz we get P=2 from the equation P = FCCO/(2xCCLK).<br />
<em><br />
How did I get that ? Here&#8217;s how :</em> Since we want FCCO in range 156Mhz to 320Mhz first substitute FCCO = 156 and we get P = 1.3. Now substituting the maximum value for FCCO i.e 320Mhz we get P = 2.67. Now , P must be an integer between 1.3 and 2.67. So we will use P=2.</strong> </div>
<p><center></p>
<table>
<tr>
<td><strong>FOSC</strong></td>
<td><strong>M</strong></td>
<td><strong>Value in MSEL (M-1)</strong></td>
<td><strong>P</strong></td>
<td><strong>Value in PSEL</strong></td>
<td><strong>Final Value in PLL0CFG</strong></td>
</tr>
<tr>
<td>5Mhz</td>
<td>12</td>
<td>11 = [0xB]</td>
<td>2</td>
<td>01</td>
<td>0x2B</td>
</tr>
<tr>
<td>10Mhz</td>
<td>6</td>
<td>5 = [0x5]</td>
<td>2</td>
<td>01</td>
<td>0x25</td>
</tr>
<tr>
<td>12Mhz</td>
<td>5</td>
<td>4 = [0x4]</td>
<td>2</td>
<td>01</td>
<td>0x24</td>
</tr>
<tr>
<td>15Mhz</td>
<td>4</td>
<td>3 = [0x3]</td>
<td>2</td>
<td>01</td>
<td>0x23</td>
</tr>
<tr>
<td>20Mhz</td>
<td>3</td>
<td>2 = [0x2]</td>
<td>2</td>
<td>01</td>
<td>0x22</td>
</tr>
</table>
<p>[Table 3]<br />
</center></p>
<p><!-- 

<h4>#2) PLL1 for USB</h4>



[Will be added shortly]
--><br />
<span class="shead"><span class="sheadin">Setting-up Peripheral Clock (PCLK)</span></span></p>
<p>The Peripheral Clock i.e. PCLK is derived from CPU Clock i.e. CCLK. The APB Divider decides the operating frequency of PCLK. The input to APB Divider is CCLK and output is PCLK. </p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/pll/hm1.png" width=610px class="aligncenter" /></p>
<p>By Default PCLK runs at 1/4th the speed of CCLK. To control APB Divider we have a register called <strong>VPBDIV</strong>. The value in VPBDIV controls the division of CCLK to generate PCLK as shown below:<br />
<center></p>
<table>
<tr>
<td>VPBDIV=<strong>0x00;</strong></td>
<td>APB bus clock (PCLK) is one fourth of the processor clock (CCLK)</p>
<tr>
<td>VPBDIV=<strong>0x01;</strong></td>
<td>APB bus clock (PCLK) is the same as the processor clock (CCLK)</p>
<tr>
<td>VPBDIV=<strong>0x02;</strong></td>
<td>APB bus clock (PCLK) is one half of the processor clock (CCLK)</p>
<tr>
<td>VPBDIV=<strong>0x03;</strong></td>
<td>Reserved. If this value is written to the APBDIV register, it has no effect (the previous setting is retained).<br />
</table>
<p></center></p>
<p><span class="shead"><span class="sheadin">Example</span></span></p>
<p>Now , lets make it more simple and write a code to run MCU @ 60Mhz (CCLK,PCLK) when input clock from Crystal is 12Mhz (which is.. in most cases). If your development board has crystal of another frequency , then in that case , you need to change the values for multiplier(<strong>M</strong>) and divider(<strong>P</strong>) accordingly as per the equations.</p>
<div class="special sp_blue notewarning"><strong>Note #2:</strong> For PLLCFG register we have to use a value of <strong>(M-1)</strong> for MSEL Bits where M is the value obtained from the equations. For e.g. if we want to use <strong>M=5</strong> from the equation then we have to apply a value of <strong>(M-1)=(5-1)=4</strong> to the register. Similarly we have to use a specific value for PSEL bits as mentioned in table 2. Hence for <strong>P=1</strong> we have to assign <strong>00</strong> to PSEL , for <strong>P=2</strong> we have to assign <strong>01</strong> to PSEL and so on..</div>
<pre><code class="language-cpp">
/*
(c) Umang Gajera - www.ocfreaks.com
This e.g. shows how to set up , initialize 
and connect PLL0 to get CCLK & PCLK @ 60Mhz
Assumption : Input freq of 12MHZ from Crystal.
*/

#define PLOCK 0x400 //10th bit = 1

#include &lt;lpc214x.h&gt;

void setupPLL0(void);
void feedSeq(void);
void connectPLL0(void);
		
int main(void)
{
	setupPLL0();
	feedSeq(); //sequence for locking PLL to desired freq.
	connectPLL0();
	feedSeq(); //sequence for connecting the PLL as system clock

	//SysClock is now ticking @ 60Mhz!
	   
	VPBDIV = 0x01; // PCLK is same as CCLK i.e 60Mhz

	while(1);

	//return 0;
	}

void setupPLL0(void)
{
	PLL0CON = 0x01; // PPLE=1 & PPLC=0 so it will be enabled 
					// but not connected after FEED sequence
	PLL0CFG = 0x24; // set the multipler to 5 (i.e actually 4) 
					// i.e 12x5 = 60 Mhz (M - 1 = 4)!!!
					// Set P=2 since we want FCCO in range!!!
					// So , Assign PSEL =01 in PLL0CFG as per the table.
}

void feedSeq(void)
{
	PLL0FEED = 0xAA;
	PLL0FEED = 0x55;
}

void connectPLL0(void)
{
	// check whether PLL has locked on to the  desired freq by reading the lock bit
	// in the PPL0STAT register

	while( !( PLL0STAT & PLOCK ));

	// now enable(again) and connect
	PLL0CON = 0x03;
} 
</code></pre>
<p>The post <a href="https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/">LPC214x PLL Tutorial for Cpu and Peripheral Clock</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">496</post-id>	</item>
		<item>
		<title>LPC2148 GPIO Programming Tutorial</title>
		<link>https://www.ocfreaks.com/lpc2148-gpio-programming-tutorial/</link>
					<comments>https://www.ocfreaks.com/lpc2148-gpio-programming-tutorial/?noamp=mobile#comments</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Wed, 04 Jul 2012 14:31:03 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=290</guid>

					<description><![CDATA[<p>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 &#8220;hello world&#8221; 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 [&#8230;]</p>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-gpio-programming-tutorial/">LPC2148 GPIO Programming Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>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 &#8220;hello world&#8221; 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 used 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.</p>
<p>Before getting into this you need to have basic understanding of Binary and Hexadecimal system and Bitwise operations in C. </p>
<ul>
<li>
Guide to Binary and Hexadecimal system is @ <a href="https://www.ocfreaks.com/hexadecimal-and-binary-number-system-basics-for-embedded-programming/">Hexadecimal and Binary Number System basics for Embedded Programming</a></li>
<li>Tutorial for Bitwise Operations in C is @ <a href="https://www.ocfreaks.com/tutorial-embedded-programming-basics-in-c-bitwise-operations/">Tutorial : Embedded programming basics in C – bitwise operations</a></li>
</ul>
<p>I&#8217;ll use ARM7 lpc2148 MCU for explanation and programming examples. The Programs and Register names that I have shown are used in KEIL as defined in lpc214x header file. You can download KEIL UV4 from <a href="https://www.keil.com/demo/eval/arm.htm">here</a>. If you are using a different IDE/Compiler then you&#8217;ll might need to change the Register Names if required. Also do note that Integer Data-Type i.e. an &#8216;int&#8217; is always <b>32 bits</b> in KEIL (i.e the ARM Compiler which it uses) in case of <b>32bits ARM MCUs like lpc2148 and others</b>.</p>
<p>Most of the function oriented pins on lpc214x Microcontrollers are grouped into ports. lpc2148 has 2 ports viz. Port 0 and Port 1.</p>
<ul>
<li><b>Port 0</b> 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 <b>P0.24 , P0.26 &#038; P0.27</b> are unavailable for use and Pin <b>P0.30</b> can be used as output pin only.</li>
<div class="vspacer_15px"> </div>
<li><b>Port 1</b> is also a 32 bit wide I/0 port but Pins 0 to 15 i.e <b>P1.0 &#8211; P1.15</b> are unavailable for use and this port too has a dedicated direction bit for each of the usable pins.</li>
</ul>
<div class="vspacer_25px"> </div>
<div class="special sp_blue noteinfo"><strong>Note #1:</strong> The naming convention for Pins on MCU is <b>&#8216;Px.yz&#8217;</b> where <b>&#8216;x&#8217;</b> is the port number , 0 or 1 in our case since we have only 2 ports to play with in lpc214x , and <b>&#8216;yz&#8217;</b> is simply the pin number in port <b>&#8216;x&#8217;</b>. For example : P0.2 refers to Pin number 2 of Port 0 , P1.13 refers to Pin number 13 in Port 1.</div>
<div class="vspacer_25px"> </div>
<p>In lpc214x MCUs most of the PINS are <b>Multiplexed i.e. these pins can be configured to provide different functions.</b> I&#8217;ll explain this in upcoming tutorial. For now Just keep in mind that by default : all functional pins i.e pins in port 0 &#038; 1 are set as GPIO so we can directly use them when learning GPIO usage.</p>
<div class="vspacer_25px"> </div>
<div class="special sp_blue noteinfo"><strong>Note #2:</strong> The functions of the Pins in Port 0 &#038; 1 can be selected by manipulating appropriate bits in <b>PINSEL0/1/2</b> registers. Explaining this is outside the scope of this article and will be dealt in detail in another article. Just remember that assigning &#8216;0&#8217; to these registers forces the corresponding pins to be used as GPIO. Since by default all pins are configured as GPIOs we dont need to explicitly assign zero value to <b>PINSELx</b> registers in our programming examples.</div>
<div class="vspacer_25px"> </div>
<h4>Now , lets go through the registers used for GPIO programming.</h4>
<div class="highlight"><strong><span class="font_15px">1. IOxPIN</span> (x=port number) :</strong> This register can be used to Read or Write values directly to the pins. Regardless of the direction set for the particular pins it gives the current start of the GPIO pin when read.</p>
<p><strong><span class="font_15px">2. IOxDIR :</span> </strong>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.</p>
<p><strong><span class="font_15px">3. IOxSET :</span></strong> This register can be used to drive an &#8216;output&#8217; 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.</p>
<p><strong><span class="font_15px">4. IOxCLR :</span></strong> This register can be used to drive an &#8216;output&#8217; 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.</div>
<div class="vspacer_25px"> </div>
<div class="special sp_blue noteinfo"><strong>Note #3</strong>: Naming convention for GPIO related registers &#8211; Replace &#8216;x&#8217; with the port number to get the register name. For e.g <strong>IOxPIN becomes IO0PIN</strong> when used for <strong>Port 0</strong> and <strong>IO1PIN</strong> when used to <strong>port 1</strong>. These are defined in &#8216;lpc214x.h&#8217; header file for KIEL IDE.</div>
<div class="vspacer_25px"> </div>
<p>Registers Names defined in &#8216;lpc214x.h&#8217; 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 of port 0 hence <strong>bit &#8216;y&#8217;</strong> in IOxDIR corresponds to <strong>pin &#8216;y&#8217;</strong> in port &#8216;x&#8217;. </p>
<p>Now setting PIN 2 of Port 0 i.e P0.2 as output can be done in various ways as show :</p>
<pre><code class="language-cpp">
CASE 1. IO0DIR = (1&lt;&lt;2);//(binary - direct assign: other pins set to 0)

CASE 2. IO0DIR |= 0x0000004;// or 0x4; (hexadecimal - OR and assign: other pins not affected)

CASE 3. IO0DIR |= (1&lt;&lt;2);</strong> //(binary - OR and assign: other pins not affected)
</code></pre>
<p>First thing is to note that is preceding Zeros in Hexadecimal Notation can be ignored because they have no meaning since we are working with unsigned values here (positive only) which are assigned to Registers. For eg. &#8216;0x32&#8217; and &#8216;0x032&#8217; and &#8216;0x0032&#8217; all mean the same.</p>
<div class="vspacer_25px"> </div>
<div class="special sp_red noteinfo"><strong>Note #4:</strong> Note that bit 31 is MSB on extreme left and bit 0 is LSB on extreme right i.e <strong>Big Endian Format</strong>. Hence bit 0 is the 1st bit from right , bit 1 is the 2nd bit from right and so on.</p>
<p><strong>Also note that the BIT and PIN Numbers are Zero(0) indexed which is quite evident since Bit &#8216;x&#8217; refers to (x-1)th location in the corresponding register</strong>.</p>
<p>Case 1 must be avoided since we are directly assigning a value to the register. So while we are making P0.2 &#8216;1&#8217; others are forced to be assigned a &#8216;0&#8217; which can be avoided by ORing and then assigning Value.<br />
Case 2 can be used when bits need to be changed in bulk and<br />
Case 3 when some or single bit needs to be changed.</p>
<p>Also Note: <strong>All GPIO pins are configured as Input after Reset by default!</strong></div>
<div class="vspacer_25px"> </div>
<h5>Example #1)</h5>
<p>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 :</p>
<pre><code class="language-cpp">
IO0DIR |= (1<<19); // Config P0.19 as Ouput
IO0SET |= (1<<19); // Make ouput High for P0.19
</code></pre>
<h5>Example #2)</h5>
<p>Making output configured Pin 15 High of Port 0 i.e P0.15 and then Low can be does as follows:</p>
<pre><code class="language-cpp">
IO0DIR |= (1<<15); // P0.15 is Output pin
IO0SET |= (1<<15); // Output for P0.15 becomes High
IO0CLR |= (1<<15); // Output for P0.15 becomes Low
</code></pre>
<h5>Example  #3)</h5>
<p>Configuring P0.13 and P0.19 as Ouput and Setting them High:</p>
<pre><code class="language-cpp">
IO0DIR |= (1<<13) | (1<<19); // Config P0.13 and P0.19 as Ouput
IO0SET |= (1<<13) | (1<<19); // Make ouput High for P0.13 and P0.19
</code></pre>
<h5>Example  #4)</h5>
<p>Configuring 1st 16 Pins of Port 0 (P0.0 to P0.15) as Ouput and Setting them High:</p>
<pre><code class="language-cpp">
IO0DIR |= 0xFFFF; // Config P0.0 to P0.15 as Ouput
IO0SET |= 0xFFFF; // Make ouput High for P0.0 to P0.15 
</code></pre>
<h4>Now lets play with some real world examples.</h4>
<h5>Example  #5)</h5>
<p>Blinky Example - Now we repeatedly make first four pins in port 0 (P0.0 to P0.3) High then Low then High and so on. You can connect Led to any one of Pins used. Here we will introduce "hard-coded" delay between making the pins High and Low, so it can be noticed.</p>
<pre><code class="language-cpp">
#include &lt;lpc214x.h&gt;

void delay(void);

int main(void)
{
	IO0DIR = 0xF; // Configure pins(0 to 3) on Port 0 as Output
	
	while(1)
	{
		IO0SET = 0xF; // Turn on LEDs
		delay();
		IO0CLR = 0xF; // 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 ARM compiler will remove the for loop!
	}
}
</code></pre>
<h5>Example  #6)</h5>
<p>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 <b>internal Pull-ups are enabled</b> the 'default' state of the pins configured as Input <b>will be always 'High' unless it is explicitly made 'Low' by connecting it to Ground</b>. 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:</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/gpio/gpio.png" /></p>
<pre><code class="language-cpp">
#include &lt;lpc214x.h&gt;

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 &#038; (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
}	
</code></pre>
<div class="highlight">
<div style="letter-spacing:1px;"><b>Explanation:</b></p>
<p><b>'(1<<7)' is simply 7th bit '1'(i.e 0x00000080)</b> & 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 <b>0x00000080</b> which is greater than zero and hence will evaluate to '<b>TRUE</b>'. Also when we use '<b>Logical NOT</b>' i.e '<b>!</b>' then '<b>!(TRUE)</b>' 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 '<b>IO0PIN & (1<<7)</b>' will be '0x0' which evaluates to FALSE. Hence '<b>!(FALSE)</b>' evalutes to TRUE and code inside 'if' block gets executed.</div>
</div>
<div class="special sp_red noteinfo"><strong>Note #5:</strong> Since LPC214x runs on 3.3 Volts a High on any pin is (and must be) always 3.3 Volts while Low or GND is 0 Volts. Applying an input of more than 3.3 Volts like 5 Volts will permanently damage the pin! I have a couple of pins damaged on a few lpc214x boards due to this - that was when I was learning embedded stuff.</div>
<h5>Example #7) </h5>
<p>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 '<b>bouncing</b>' which needs to be resolved using '<b>debouncing</b>' as explained below.</p>
<div class="highlight">
<h4 id="pin_bouncing">Bouncing:</h4>
<p>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 doesn't 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'.</p>
<h4>Debouncing:</h4>
<p>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.</p>
<p>I would recommend visiting the following links for understanding debouncing in-depth.<br />
1) <a href="http://www.ganssle.com/debouncing.htm">www.ganssle.com/debouncing.htm</a><br />
2) <a href="http://www.labbookpages.co.uk/electronics/debounce.html">www.labbookpages.co.uk/electronics/debounce.html</a><br />
3) <a href="http://www.eng.uwaterloo.ca/~tnaqvi/downloads/DOC/sd192/SwitchDebouncing.htm">www.eng.uwaterloo.ca/~tnaqvi/downloads/DOC/sd192/SwitchDebouncing.htm</a> </div>
<p><strong>Note:</strong> The Example given below is very a crude implementation for debouncing using flags and previous states. It uses hard-coded delays. Proper algorithms are discussed in the links given above.</p>
<pre><code class="language-cpp">
#include &lt;lpc214x.h&gt;

void tiny_delay(void);

int main(void)
{
	int flag=0, pinSamplePrevious, pinSampleCurrent;
	IO0DIR &= ~((1<<7)); 
	IO0DIR |= (1<<30);
	
	pinSamplePrevious = IO0PIN &#038; (1<<7); //Initial Sample

	while(1)
	{
		pinSampleCurrent = IO0PIN &#038; (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 &#038; (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++;
	}
}
</code></pre>
<div class="highlight">
<div style="letter-spacing:1px;"><b>Explanation:</b> </p>
<p>Initially P0.7 will be high since pull-ups are enabled. Hence '<b>pinSamplePrevious</b>' and '<b>pinSampleCurrent</b>' will have same value and '<b>if</b>' will evalute to false so code inside wont execute.</p>
<p><b>Event #1) Pressing switch:</b>  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 '<b>if</b>' block evalute to true and code inside will execute. When its executed first time '<b>flag</b>' will be 0. Hence, '<b>if(flag)</b>' will be false and '<b>else</b>' block will execute which will drive P0.30 Low and set flag to '1'.</p>
<p><b>Event #2) Releasing switch:</b> 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, '<b>if(flag)</b>' will evaluate to true and P0.30 will be set to High i.e '1' and flag will be set to 0.</div>
</div>
<h4>Improvising play with Bits:</h4>
<p>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&lt;&lt;x)' as:</p>
<pre><code class="language-cpp">
#define BIT(x) (1&lt;&lt;x) </code></pre>
<p>After that is defined we can directly use BIT(x). Using this Example 3 can be re-written as:</p>
<pre><code class="language-cpp">
IO0DIR |= BIT(13) | BIT(19); // Config P0.13 and P0.19 as Ouput
IO0SET |= BIT(13) | BIT(19); // Make ouput High for P0.13 and P0.19
</code></pre>
<p>Example 6 can be re-written as:</p>
<pre><code class="language-cpp">
#include &lt;lpc214x.h&gt;

#define BIT(x) (1&lt;&lt;x)

int main(void)
{
	IO0DIR &= ~(BIT(7)); 
	IO0DIR |= BIT(31);

	while(1)
	{
		if( !(IO0PIN & BIT(7)) ) 
		{
			IO0SET |= BIT(31);
		}
	}
	return 0;
}	
</code></pre>
<p><strong>LPC2148 Development Boards that we Recommend:</strong></p>
<p><a href="https://www.amazon.in/NXP-ARM7-LPC2148-Header-Board/dp/B01LX5AN08/ref=as_li_ss_il?_encoding=UTF8&#038;psc=1&#038;refRID=1ATG383W1SGQG4FNZN0R&#038;linkCode=li2&#038;tag=o0cd4-21&#038;linkId=619cbbc7eb302c8ca4af6877b8c1152c" target="_blank" rel="noopener"><img decoding="async" border="0" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&#038;ASIN=B01LX5AN08&#038;Format=_SL160_&#038;ID=AsinImage&#038;MarketPlace=IN&#038;ServiceVersion=20070822&#038;WS=1&#038;tag=o0cd4-21" ></a><img loading="lazy" decoding="async" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&#038;l=li2&#038;o=31&#038;a=B01LX5AN08" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /><a href="https://www.amazon.in/Development-Board-LPC2148-USB-FT232-Driver-memory/dp/B00T04NLYE/ref=as_li_ss_il?ie=UTF8&#038;qid=1491577410&#038;sr=8-8&#038;keywords=lpc2148&#038;linkCode=li2&#038;tag=o0cd4-21&#038;linkId=8628d2ee5db112ddbaab7aaedc6ec22f" target="_blank" rel="noopener"><img decoding="async" border="0" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&#038;ASIN=B00T04NLYE&#038;Format=_SL160_&#038;ID=AsinImage&#038;MarketPlace=IN&#038;ServiceVersion=20070822&#038;WS=1&#038;tag=o0cd4-21" ></a><img loading="lazy" decoding="async" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&#038;l=li2&#038;o=31&#038;a=B00T04NLYE" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /><a href="https://www.amazon.in/LPC2148-Development-Board-Standard-Package/dp/B019QTWRVU/ref=as_li_ss_il?ie=UTF8&#038;qid=1491577410&#038;sr=8-1&#038;keywords=lpc2148&#038;linkCode=li2&#038;tag=o0cd4-21&#038;linkId=1f88a47711dbb1f232ece64e5a4a79d3" target="_blank" rel="noopener"><img decoding="async" border="0" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&#038;ASIN=B019QTWRVU&#038;Format=_SL160_&#038;ID=AsinImage&#038;MarketPlace=IN&#038;ServiceVersion=20070822&#038;WS=1&#038;tag=o0cd4-21" ></a><img loading="lazy" decoding="async" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&#038;l=li2&#038;o=31&#038;a=B019QTWRVU" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /><a href="https://www.amazon.in/Explore-Embedded-ARM7-LPC2148-Dev/dp/B018XJ0F5Y/ref=as_li_ss_il?ie=UTF8&#038;qid=1491577410&#038;sr=8-5&#038;keywords=lpc2148&#038;linkCode=li2&#038;tag=o0cd4-21&#038;linkId=bc5b00fa1663b938453ff3d765a3e165" target="_blank" rel="noopener"><img decoding="async" border="0" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&#038;ASIN=B018XJ0F5Y&#038;Format=_SL160_&#038;ID=AsinImage&#038;MarketPlace=IN&#038;ServiceVersion=20070822&#038;WS=1&#038;tag=o0cd4-21" ></a><img loading="lazy" decoding="async" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&#038;l=li2&#038;o=31&#038;a=B018XJ0F5Y" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>
<div class="special sp_yellow notestar">If any reader wants to contribute to this article let us know using <a href="https://www.ocfreaks.com/contact-us/">contact-us form</a>. Reader will get full credit he/she deserves for contribution 🙂</div>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-gpio-programming-tutorial/">LPC2148 GPIO Programming Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/lpc2148-gpio-programming-tutorial/feed/</wfw:commentRss>
			<slash:comments>76</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">290</post-id>	</item>
		<item>
		<title>Tutorial : Embedded programming basics in C &#8211; bitwise operations</title>
		<link>https://www.ocfreaks.com/tutorial-embedded-programming-basics-in-c-bitwise-operations/</link>
					<comments>https://www.ocfreaks.com/tutorial-embedded-programming-basics-in-c-bitwise-operations/?noamp=mobile#comments</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Wed, 27 Jun 2012 16:31:14 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=277</guid>

					<description><![CDATA[<p>Bitwise or bitlevel operations form the basis of embedded programming. A knowledge of Hexadecimal and Binary Numbering system is required along with conversion from binary to hex and vice-verse. A tutorial for that is located @ www.ocfreaks.com/hexadecimal-and-binary-number-system-basics-for-embedded-programming/. If you are less familiar with hexadecimal and binary number systems that will help you. Further , I [&#8230;]</p>
<p>The post <a href="https://www.ocfreaks.com/tutorial-embedded-programming-basics-in-c-bitwise-operations/">Tutorial : Embedded programming basics in C &#8211; bitwise operations</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div class="justify">
Bitwise or bitlevel operations form the basis of embedded programming. A knowledge of Hexadecimal and Binary Numbering system is required along with conversion from binary to hex and vice-verse. A tutorial for that is located @ <a href="https://www.ocfreaks.com/hexadecimal-and-binary-number-system-basics-for-embedded-programming/">www.ocfreaks.com/hexadecimal-and-binary-number-system-basics-for-embedded-programming/</a>. If you are less familiar with hexadecimal and binary number systems that will help you.</p>
<p>Further , I assume that the reader has a basic understanding of C programming language , Digital Logic , MCU(Registers,etc..).</p>
<h2 class="shead"><strong>Bit level Operations in C</strong></h2>
<p>Now getting armed with the knowledge of interconversion between Hexadecimal and Binary we can start with Bitwise(or bit level) operations in C. There are bascially 6 types of Bitwise operators. These are :<br />
1. Bitwise OR operator denoted by &#8216;<b>|</b>&#8216;<br />
2. Bitwise AND operator denoted by &#8216;<b>&#038;</b>&#8216;<br />
3. Bitwise Complement or Negation Operator denoted by &#8216;<b>~</b>&#8216;<br />
4. Bitwise Right Shift &#038; Left Shift denoted by &#8216;<b>>></b>&#8216; and &#8216;<b><<</b>&#8216; respectively<br />
5. Bitwise XOR operator denoted by &#8216;<b>^</b>&#8216;</p>
<div class="special sp_red notewarning"><b>Important Note:</b> Bitwise Operations are meant to be done on Integers only! No Float and No Double here plz!
</div>
<div class="vspacer_15px"></div>
<p>Below is the Truth table for OR , AND , XOR &#8211; each of them require 2 operands:</p>
<p><center></p>
<table>
<tr>
<td><strong>OP1</strong> (Operand 1)</td>
<td><strong>OP2</strong> (Operand 2)</td>
<td><strong>OP1 | OP2</strong> (OR)</td>
<td><strong>OP1 &#038; OP2</strong> (AND)</td>
<td><strong>OP1 ^ OP2</strong> (XOR)</td>
</tr>
<tr>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>1</td>
</tr>
<tr>
<td>0</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
</tr>
<tr>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>0</td>
</tr>
</table>
<p></center></p>
<div class="highlight">Hexadecimal Numbers in C/C++ program have a &#8216;0x&#8217; prefix. Without these prefix any number will be considered as a Decimal number by the compiler and hence we need to be explicit with numbers.</div>
<div class="special sp_blue notestar">
<b>Implied Declaration and Use of Numbers when working at bit level: </b></p>
<p>Consider: <em>unsigned int n = 0x7F2;</em> Here its is implied that n will be 0x000007F2 since n is a 32bit number. Here number will be automatically padded with Zeros form right hand side as required.</p>
<p>Consider: <em>unsigned int n = (1<<3);</em> Here the 3rd bit will be 1 and rest all will be made zeros by the compiler.</p>
<p>In short 0s are padded towards the left as required.</p></div>
<p>Consider there is a 32 bit register which is connected to 32 pins. Changing any bit to 1 will produce a HIGH (LOGIC 1) and making it &#8216;0&#8217; will produce a LOW i.e LOGIC 0 on the corresponding bit. If we assign bit number 19(from right) a &#8216;1&#8217; then pin 19 will produce a HIGH. </p>
<p>In C this can be done as :<br />
<center></p>
<pre><code class="language-cpp">
REG = 0x00080000; //hexadecimal
</code></pre>
<p>=or=</p>
<pre><code class="language-cpp">
REG = (1<<19); // sort of compact binary rep.
</code></pre>
<p></center><br />
As you can see using binary number directly is a headache - specially when there are like 32 bits to handle. Using Hexadecimal instead makes it a bit easier and using Left shift operator makes it super simple. Here '<<' is called the Left Shift Operator. Similar to this is ">>" viz. Right Shift operator. </p>
<p>"<strong>(1<<19)</strong>" Simply means '<b>Shift 1 towards the LEFT by 19 Places</b>'. Other bits will be Zero by default.</p>
<p>We generally use Hexadecimal when we need to change bits in bluk and Left shift operator when only few bits need to be changed or extracted. </p>
<h2 class="shead"><strong>ORing in C</strong></h2>
<p>This is the same exact thing as in Digital Logic i.e <b>1 ORed with 'x' is always 1 and 0 ORed with 'x' is always 'x' , where x is a bit</b>. Lets take two 4-bit numbers and OR them. Consider two 4-bit numbers n1=0100 & n2=1001. <b>Here the 1st bit of n1 will be ORed with 1st bit of n2 , 2nd bit of n1 will be ORed with 2nd bit of n2 and soo on</b>. In this case n1 is decimal 4 and n2 is decimal 9.</p>
<p><center></p>
<table>
<tr>
<td>n1 (4) => </td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>n2 (9) => </td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>1</td>
</tr>
<tr>
<td>ORed Result (13) => </td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>1</td>
</tr>
</table>
<p></center></p>
<div class="highlight">Hence we get 4 | 9 = 13.</div>
<div class="special sp_blue notestar">It can be seen that bitwise ORing is similar to addition but this is not always the case since bitwise OR doesnt deal with carry generated after adding(i.e ORing) two 1s. For e.g. 12 | 9 is also = 13. </div>
<p>Now , If we want to make bit 19 and 12 as '1' we can use the binary OR operator which is represented by a pipe i.e '|'.</p>
<pre><code class="language-cpp">
REG = (1<<19) | (1<<12); // 19th and 12th bits are set to '1' , rest are Zeros.
</code></pre>
<p>Now consider we want to make the first 21 bits (from right) to '1'. This is can be done using hexadecimal notation instead of using shift operator since we will need to write 21 left shift operations for each of the 21 bits. In this case just consider we have a 32 bit number which has all first 21 bits as '1'. Then convert this number to hexadecimal and use it!</p>
<pre><code class="language-cpp">
REG = 0x001FFFFF; //set first 21 bits to '1'
</code></pre>
<p>Hence using Hexadecimal or Binary operator depends on the situation.</p>
<h2 class="shead"><b>Bitwise 1's Complement / Negation in C :</b></h2>
<p>Now lets say.. we need to convert all 0s to 1s and vice-verse. This can be done using the Bitwise negation operator denoted by '~'. The result of this operation is called 1's Complement. Its also called a 'NOT' operation. '~' is a unary operator since it requires only 1 operator while rest all are binary operators.</p>
<p>1st lets take a 4bit example to keep it simple.<br />
Consider a binary number 1101. Its Negation will be<strong> ~(1101) => 0010</strong>. </p>
<p>Now lets get back to 32 bit numbers.. In C it can be done as follows:</p>
<pre><code class="language-cpp">
unsigned int x = 0x0FFF000F;
REG = ~(x); // REG is assigned 0xF000FFF0;</code></pre>
<p>=or=</p>
<pre><code class="language-cpp">
REG = ~(0x0FFF000F); // REG is assigned 0xF000FFF0;
</code></pre>
<h2 class="shead"><strong>ANDing in C</strong></h2>
<p>Binary AND operator in C is denoted by '&'. When 2 numbers are ANDed each pair of 'corresponding' bits in both numbers are ANDed. Consider two 4-bit binary numbers ANDed : 1010 & 1101 , here nth bit of both numbers are ANDed to get the result. Here the same truth table(already shown above) is followed as in Therotical Digital Logic i.e <b>1 ANDed with x is always 'x' (x is a binary number.. a bit) and 0 ANDed with 'x' is always 0</b>.</p>
<pre><code class="language-cpp">
unsigned char n1,n2,n3; //we can declare an 8-bit number as a char;
n1 = 0xF4; // binary n1 = 0b11110100;
n2 = 0x3A; // binary n2 = 0b00111010;
n3 = n1 & n2; // binary n3 = 0b00110000; i.e 0x30;
</code></pre>
<h2 class="shead"><strong>XORing in C</strong></h2>
<p>XOR is short for eXclusive-OR. By definition of XOR , the <b>result will be a '1' if both the input bits are different and result will be '0' if both are same</b> (as seen in the table few paragraphs above). XOR can be used to check the bit difference between 2 numbers or Registers.</p>
<pre><code class="language-cpp">
unsigned int n1,n2,n3;
n1 = 0x13; //binay n1 = 0b10011
n2 = 0x1A; //binay n2 = 0b11010;
n3 = n1^n2; // n2 = 0x09 which is binary 0b01001;
</code></pre>
<h2 class="shead"><strong>Working with Read/Write Registers in C</strong></h2>
<p>Generally its a bad Idea to assign a value directly to Registers since doing so may change the value of other bits which might be used to contol some other hardware. </p>
<p>Consider an 8 bit Register say <strong>REGT_8b is used to Start/Stop 8 different Timers</strong>. Bit 0 (from left) controls Timer 0 , Bit 1 Controls Timer 1 and so on... Writing a '1' to a bit will Start the timer and a '0' will Stop the Timer.</p>
<p>Now lets say Timers 7,6,5 are started and others are stopped. So the current value of <strong>REGT_8b</strong> will be '<strong>11100000</strong>'. Now assume that we want to Start Timer 2. We can do this in a manner which doesn't affect the other bits as follows :</p>
<pre><code class="language-cpp">
REGT_8b = REGT_8b | (1<<2); // which is 11100000 | 00000100 = 11100100
</code></pre>
<p>=or simply=</p>
<pre><code class="language-cpp">
REGT_8b |= (1<<2); // same as above
</code></pre>
<p>Now lets say we want to Stop Timer 6. This can be achieved as follows :</p>
<pre><code class="language-cpp">
REGT_8b = REGT_8b & (~(1<<6));
</code></pre>
<p>=or simply=</p>
<pre><code class="language-cpp">
REGT_8b &= ~(1<<6);
</code></pre>
<p>Here <strong>(1<<6) </strong>will be <strong>01000000</strong> (considering it 8bit.. for 32 bit 0s will be padding on left). Then <strong>~(1<<6) will be ~(01000000) = 10111111</strong> and finally when its ANDed with current value of REGT_8b 6th bit of REGT_8b will be set to '0' and others will remain as they were <strong>since ANDing any bit with 1 doesn't change its value. </strong></p>
<div class="highlight">
<h2 class="shead">More Examples</h2>
<p><b>For below examples assume current value of REGT_8b as '11100011' which is 8 bit.</p>
<p>1) Stop Timers 0 and 5 : </b></p>
<pre><code class="language-cpp">
REGT_8b &= ~( (1<<0) | (1<<5) );
</code></pre>
<p>=> ((1<<0) | (1<<5)) is = <strong>((00000001) | (00100000)) = 00100001</strong><br />
=> ~(00100001) is = 11011110<br />
=> Finally REGT_8b & 11011110 is = <strong>(11100011) & (11011110) = 11000010 </strong>=> Timers 0 and 5 Stopped!</p>
<hr />
<p><b>2) Start Timers 3 and 4:</b></p>
<pre><code class="language-cpp">
REGT_8b |= ( (1<<3) | (1<<4) );
</code></pre>
<p>=> ((1<<3) | (1<<4)) is = <strong>((00001000) | (00010000)) = 00011000;</strong><br />
=> Now REGT_8b | (00001000) is = <strong>(11100011) | (00011000) = 11111011</strong> => Timers 3 and 4 Started!</p>
<hr />
<p><b>3) Stop Timer 7 and Start timer 3:</b></p>
<pre><code class="language-cpp">
REGT_8b = (REGT_8b | (1<<3)) &#038; (~(1<<7));
</code></pre>
<p>Above complex expression can be avioded by doing it in 2 steps as:</p>
<pre><code class="language-cpp">
REGT_8b &= ~(1<<7); // Stop Timer 7
REGT_8b |= (1<<3); // Start Timer 3
</code></pre>
</div>
<h2 class="shead"><strong>Monitoring Specific bit change in Registers</strong></h2>
<p>Many times we need to read certain Flags in a register that denotes change in Hardware state. Consider a 32 bit Register REGX in which the 12th bit denotes the arrival of data from UART Receive pin into buffer. This data may be a command for the MCU to start or stop or do something. So we need to read the command then interpret it and call appropriate function. In simplest approach we can continuously scan for change in 12th bit of REGX as follows :</p>
<pre><code class="language-cpp">
while( REGX & (1<<12) ) //wait indefinitely until 12th bit changes from 0 to 1
{
//do something
//exit loop
}

/*=OR=*/

while(REGX &#038; (1<<12)); //wait indefinitely until 12th bit changes from 0 to 1
//do something

</code></pre>
<p>Unless the 12th bit of REGX is '1' the result of <strong>(REGX & (1<<12))</strong> will always be zero. When 12th bit is 1 then <strong>(REGX & (1<<12)) will be = (1<<12)</strong> which is obviously greater than 0 and hence is <strong>evaluated as TRUE</strong> condition and the code inside the while loop gets executed.</p>
<p>To monitor for the change in 12th bit from 1 to 0 we just Negate the condition inside while to : </p>
<pre><code class="language-cpp">
while ( !(REGX & (1<<12)) ) //wait indefinitely until 12th bit changes from 1 to 0
{
//do something
//exit loop
}

/*=OR=*/
while ( !(REGX &#038; (1<<12)) ); //wait indefinitely until 12th bit changes from 1 to 0
//do something

</code></pre>
<p><strong>A real life example would as follows:</strong></p>
<pre><code class="language-cpp">
#define RDR (1<<0) // Receiver Data Ready
char U0Read(void)
{
     while( !(U0LSR &#038; RDR) ); // wait till any data arrives into Rx FIFO
     return U0RBR; 
}
</code></pre>
<div class="special sp_red notewarning"> Note that you are forcing the CPU into an indefinite wait state. This can only useful in a few cases like, for e.g., you have made a command parser for a CNC machine which waits for a command and then processes it accordingly(or takes appropriate action) then again waits for new command.</div>
<h2 class="shead"><b>Extracting/Testing Bit(s) from a Register: </b></h2>
<p>To extract a bit from a register we can use a variable in which all other bit locations , except the one we are intereseted in , are forced to 0. This can be done using masks.</p>
<p>Lets assume we want to extract bit number 13. For this we first define a mask in which bit location 13 is 1 and rest are all zeros. Then we AND this mask with the register and save the result in a variable.</p>
<pre><code class="language-cpp">
unsigned int mask ,extract_n;
mask = (1<<13);
extract_n = REGX &#038; mask; // bit 13 goes to extract_n 
</code></pre>
<p>If the 13th bit of REGX was 1 then the 13th bit of extract_n will also be one. Similarly for 0. This can be easily extended to extract multiple bits.</p>
<p>To test whether bit 13 of REGX is 0 or 1 we can accomplish it as follows :</p>
<pre><code class="language-cpp">
if(REGX & (1<<13))
{
...
...
}
</code></pre>
<p>This is similar to what we did using mask and monitoring except that the result here is used as condition for 'if' construct. Here too '<b>(REGX & (1<<13))</b>' will evaluate to true if bit 13 is 1 and false if bit 13 is 0.<br />

</div>
<p>The post <a href="https://www.ocfreaks.com/tutorial-embedded-programming-basics-in-c-bitwise-operations/">Tutorial : Embedded programming basics in C &#8211; bitwise operations</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/tutorial-embedded-programming-basics-in-c-bitwise-operations/feed/</wfw:commentRss>
			<slash:comments>13</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">277</post-id>	</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/

Page Caching using Disk: Enhanced 

Served from: www.ocfreaks.com @ 2026-03-06 00:52:56 by W3 Total Cache
-->