<?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>lpc2148 - OCFreaks!</title>
	<atom:link href="https://www.ocfreaks.com/tag/lpc2148/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.ocfreaks.com/tag/lpc2148/</link>
	<description>Overclocking , Gaming , Technology , Robotics &#38; DIY!</description>
	<lastBuildDate>Sat, 28 Jul 2018 14:21:28 +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 Timer Capture Mode Tutorial with Frequency Counter Example</title>
		<link>https://www.ocfreaks.com/lpc2148-timer-capture-mode-tutorial-frequency-counter-example/</link>
					<comments>https://www.ocfreaks.com/lpc2148-timer-capture-mode-tutorial-frequency-counter-example/?noamp=mobile#respond</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Wed, 21 Mar 2018 17:26:23 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=3067</guid>

					<description><![CDATA[<p>In this tutorial we will cover how to program capture mode for timer module of ARM7 LPC2148 microcontroller along with a frequency counter example using capture input.</p>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-timer-capture-mode-tutorial-frequency-counter-example/">LPC2148 Timer Capture Mode Tutorial with Frequency Counter Example</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Following the request of some of our readers, in this tutorial we will cover how to program capture mode for timer module of ARM7 LPC2148 microcontroller along with a frequency counter example using capture input. In my previous <a href="https://www.ocfreaks.com/lpc2148-timer-tutorial/" target="_blank">LPC2148 Timer tutorial</a> we saw how to setup and program the timer module.</p>
<h4>A quick recap:</h4>
<p>ARM LPC214x MCUs have two 32-bit-Timer blocks. Each Timer block can be used as a ‘Timer’ or as a ‘Counter’. A timer has a Timer Counter(TC) and Prescale Register(PR) associated with it. TC stores the current value of the count and PR stores a prescalar value. When Timer is Reset and Enabled, TC is set to 0 and incremented by 1 every ‘PR+1’ clock cycles. When it reaches its maximum value it gets reset to 0 and hence restarts counting. PR is used to set the Timer Resolution. When the timer is used in &#8216;Counter&#8217; mode, and external signal is used to increment the value of TC. </p>
<h3>Capture Channels and Input pins</h3>
<p>Each timer block has 4 Capture Channels (CAPn.0 to CAPn.3, n=Timer number) associated with it. Using these Capture channels we can take a snapshot(i.e. capture) of the current value of TC when a signal edge is detected. These channels are mapped to device pins. This mapping of Capture Channel to pins is as given below:</p>
<table class="aligncenter ocf-table" style="margin-bottom:15px; margin-top:15px;">
<tr>
<th colspan="2">Timer0</th>
<th colspan="2">Timer1</th>
</tr>
<tr>
<td>Channel</td>
<td>Pin</td>
<td>Channel</td>
<td>Pin</td>
</tr>
<tr>
<td>CAP0.0</td>
<td>P0.2/P0.22/P0.30</td>
<td>CAP1.0</td>
<td>P0.10</td>
</tr>
<tr>
<td>CAP0.1</td>
<td>P0.4</td>
<td>CAP1.1</td>
<td>P0.11</td>
</tr>
<tr>
<td>CAP0.2</td>
<td>P0.6/P0.16/P0.28</td>
<td>CAP1.2</td>
<td>P0.17/P0.19</td>
</tr>
<tr>
<td>CAP0.3</td>
<td>P0.29</td>
<td>CAP1.3</td>
<td>P0.18/P0.21</td>
</tr>
</table>
<h3>Using Capture Inputs in ARM7 LPC214x</h3>
<p>When using Capture Inputs we use Timer Block in Normal &#8216;<strong>Timer Mode</strong>&#8216; or &#8216;<strong>Counter Mode</strong>&#8216;. </p>
<ol>
<li>In <strong>Timer Mode</strong>, the Peripheral clock is used as a clock source to increment the Timer Counter(TC) every ‘PR+1’ clock cycles. Whenever a signal edge(rising/falling/both) event is detected, the timestamp i.e. the current value of TC is loaded into corresponding Capture Register(CRx) and optionally we can also generate an interrupt every time Capture Register is loaded with a new value. This behavior is configured using CCR. </li>
<li>In <strong>Counter Mode</strong>, external signal is used to increment TC every time an edge(rising/falling/both) is detected. This behavior is configured using CTCR. Corresponding bits for Capture channel must be set to zero in CCR. (See register explanation given below)</li>
</ol>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/timer_module_with_capture_lpc2148.png" width="610px" height="330px" alt="ARM7 LPC2148 Timer Capture Block diagram" /></p>
<p>Here is a simple diagram depicting the capture process. Dashed arrows(for both diagrams) signifiy the events.</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/embedded/common/timer_capture_input_mode_process_lpc_mcu.png" width="520px" height="155px" alt="Microcontroller Input Capture diagram" /></p>
<h2 class="shead">Capture Related Registers:</h2>
<p>Before we start with programming, first lets see the registers. Since I have already discussed main Timer Registers in my <a href="https://www.ocfreaks.com/lpc2148-timer-tutorial/" target="_blank">LPC2148 Timer tutorial</a>, we will only have a look at registers relating to capture.</p>
<div class="highlight"><strong>1) <span class="doc_ref">CCR</span> &#8211; Capture Control Register</strong>: Used to select which type of Egde(rising/falling/both) is used to Capture Registers (CR0-CR3) and optionally to generate an interrupt when a capture event occurs.</p>
<p><strong>For <span class="doc_ref">CR0</span>:</strong></p>
<ul style="margin-bottom:0px;">
<li><strong>Bit 0:</strong> Capture on CAPn.0 rising edge. When set to 1, a transition of 0 to 1 on CAPn.0 will cause CR0 to be loaded with the contents of TC. Disabled when 0.</li>
<li><strong>Bit 1:</strong> Capture on CAPn.0 falling edge. When set to 1, a transition of 1 to 0 on CAPn.0 will cause CR0 to be loaded with the contents of TC. Disabled when 0.</li>
<li><strong>Bit 2:</strong> Interrupt on CAPn.0 event. When set to 1, a CR0 load due to a CAPn.0 event will generate an interrupt. Disabled when 0.</li>
</ul>
<p>Similarly bits 3-5, 6-8, 9-11 are for <strong>CR1, CR2 &#038; CR3</strong> respectively.</p>
<p><strong>2) <span class="doc_ref">CR0 &#8211; CR3</span> &#8211; Capture Registers:</strong> Each capture register is associated with a Capture Pin. Depending on the settings in CCR, CRn can be loaded with current value of TC when a specific event occurs.</p>
<p><strong>3) <span class="doc_ref">CTCR</span> Count Control Register: Used to select between Timer or Counter Mode.</strong><br />
<strong>Bits[1:0]</strong> &#8211; Used to select Timer mode or which Edges can increment TC in counter mode.</p>
<ul style="margin-bottom:0px;">
<li>[00]: Timer Mode. PC is incremented every Rising edge of PCLK.</li>
<li>[01]: Counter Mode. TC is incremented on Rising edges on the CAP input selected by Bits[3:2].</li>
<li>[10]: Counter Mode. TC is incremented on Falling edges on the CAP input selected by Bits[3:2].</li>
<li>[11]: Counter Mode. TC is incremented on Both edges on the CAP input selected by Bits[3:2].</li>
</ul>
<p><strong>Bits[3:2]</strong> &#8211; Count Input Select. Only applicable if above bits are not [00].</p>
<ul style="margin-bottom:0px;">
<li>[00]: Used to select CAPn.0 for TIMERn as Count input.</li>
<li>[01]: Used to select CAPn.1 for TIMERn as Count input.</li>
<li>[10] &#038; [11]: Reserved.</li>
</ul>
</div>
<h2 class="shead">Frequency Counter using LPC2148 Timer Capture</h2>
<h3>Methods to Measure frequency of an external signal</h3>
<h4>We will cover two methods of Measuring Unknown Signal Frequency:</h4>
<ol>
<li><strong>By Gating/Probing</strong> &#8211; In this method we define a Gating Interval in which we count the number of pulses. <strong>What is Gating Time?</strong> &#8211; Gating Time is amount of time for which we probe the input signal. Once we know the no.of. pulses, we can easily deduce the frequency using Gating time. Here we use the external signal as clock source to increment Timer Counter (TC). The value in TC gives the number of pulses counted per Gating Time. This method relies on selecting a proper Gating Time to get valid and accurate results. </li>
<li><strong>By measuring Period using Interrupts</strong> &#8211; In this method we use an Interrupt Service Routine to find out the time between 2 consecutive pulses i.e. period of the Input signal at that particular instant. This is done using an ISR, but ISR itself is main limiting factor for the maximum frequency which can be measured. Compared to first one, this method can give accurate results, given frequency is below measurement limit.</li>
</ol>
<div class="special sp_blue noteinfo">The maximum input signal frequency which can be reliably measured using first method is half of TIMERn_PCLK, since it takes two successive edges of TIMERn_PCLK to detect one edge of external signal. Hence, using a PCLK of 60Mhz we can measure upto 30Mhz signal properly. For second method we can only measure up to around 1 Mhz due to ISR execution delay &#038; context switching overhead.</div>
<p>For measuring Square wave Signal Frequency, external hardware is not required unless the Pulse HIGH Voltage level is > 3.3 Volts, in which case a Voltage divider or Buffer is mandatory. To measure other Signal types like Saw-tooth, Sine wave we will require something that can provide Hysteresis which will define the HIGH &#038; LOW Voltage Threshold, thereby converting it into a Square signal, to detect any of the edges of the unknown signal. This can be done using a Schmitt Trigger Buffer (either Inverting or Non-Inverting &#8211; doesn&#8217;t matter).</p>
<p>For both of examples given below, we will use Timer1 module to measure frequency. Capture Channel CAP1.0 is used as capture input. CAP1.0 is mapped to Pin P0.10 on LPC214x, hence we select CAP1.0 alternate function for P0.10. </p>
<p>To generate a square wave signal, I have used LPC214x&#8217;s inbuilt PWM module which is configured with 0.05 us resolution. PWM2 output channel is used which gives output on Pin P0.7. Refer my <a href="https://www.ocfreaks.com/lpc2148-pwm-programming-tutorial/" target="_blank"> LPC2148 PWM Tutorial </a> for more on PWM. You can also use any external source like a function generator or IC-555 based generator. The example project linked below also contains <a href="https://www.ocfreaks.com/retarget-redirect-printf-scanf-uart-keil/" target="_blank">retargeted printf() for KEIL</a> which redirects its output to UART0.</p>
<h4>Schematic</h4>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/lpc2148_input_capture_mode_eg_sch.png" width="425px" height="235px" alt="ARM7 LPC2148 Capture Mode Frequency Counter Measurement Example Schematic" /></p>
<h3>1. Frequency Counter Example using Gating/Probing:</h3>
<p>In this example we will, we use external signal as clock source for Timer1. Every positive going edge will increment the Timer1 Counter (<span class="code_var">T1TC</span>) by 1. To count the number of pulses, we start the timer and wait until Gating time. After that we stop the time and read the value in <span class="code_var">T1TC</span> which gives the no.of. pulses per gating time. For this example I have chosen a gating time of 1 seconds. Obviously, we can get more accurate results using a higher Gating time. For our purpose 1 second is enough to measure signals upto 30Mhz using PCLK=60Mhz. Given Gate time is in ms, the following equation can be used to find the frequency from counted pulses:</p>
<div class="equation">Measured Frequency in Khz = </p>
<div class="fraction"><span class="fup">Counted Pulses</span><span class="bar">/</span><span class="fdn">Gate Time in ms</span></div>
</div>
<p><strong>Source Code Snippet</strong></p>
<pre><code class="language-cpp">
/*(C) Umang Gajera- www.ocfreaks.com
ARM7 LPC2148 Input Capture Tutorial - Example 1 for frequency counter using ARM KEIL
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
License: GPL.*/

#include &lt;lpc214x.h&gt;
#include &lt;stdio.h&gt; //visit https://www.ocfreaks.com/retarget-redirect-printf-scanf-uart-keil/ 
#include "lib_funcs.h" //OCFreaks LPC214x Tutorials Library Header

void initPWM(void); //To generate Test Square wave
unsigned int pulses = 0;
#define GATE_TIME_MS 1000 //Probing/Gating Time in ms

int main(void)
{
	initClocks(); //Set PCLK = CCLK = 60Mhz - used by: UART, Timer & PWM
	initUART0();  //Initialize UART0 for retargeted printf()
	initTimer0(); //Init Timer for delay functions
	
	//Assuming PCLK = 60Mhz
	PINSEL0 |= (1<<21); //Select CAP1.0 for P0.10
	T1CTCR = 0x1; //Increment TC on rising edges of External Signal for CAP1.0
	T1PR = 0; //0.01667us res, 1 clock cycles @60Mhz = 0.01667 us
	T1TCR = 0x02; //Reset Timer
	T1CCR = (1<<0); //Capture on Rising Edge(0->1)
	T1TCR = 0x01; //Enable timer1

	initPWM(); //To generate square wave
	float FreqKhz = 0;
	printf("OCFreaks.com - Measuring Frequency using LCP2148 Timer Capture Ex 1:\n");
	
	while(1)
	{
		
	  T1TCR = 0x1; //Start Timer2
		delayMS(GATE_TIME_MS); //'Gate' signal for defined Time (1 second)
		T1TCR = 0x0; //Stop Timer2
		
		pulses = T1TC; //Read current value in TC, which contains  no.of. pulses counted in 1s
		T1TCR = 0x2; //Reset Timer2 TC
		
		FreqKhz = (double)pulses/GATE_TIME_MS;
		
		if(FreqKhz >= 1000.0) //Display Freq. In MHz
		{
			printf("Frequency = %0.4f MHz\n", FreqKhz/1000.0);
		}
		else //Display Freq. in KHz
		{
			printf("Frequency = %0.2f KHz\n", FreqKhz);
		}
	}

	//return 0; //This won't execute normally
}

void initPWM(void)
{
	//Refer : https://www.ocfreaks.com/lpc2148-pwm-programming-tutorial/
	/*Assuming that PLL0 has been setup with CCLK = 60Mhz and PCLK also = 60Mhz.*/
	/*This is as per the Setup & Init Sequence given in the tutorial*/

	PINSEL0 |= (1<<15); // Select PWM2 output for Pin0.7
	PWMPCR = 0x0; //Select Single Edge PWM - by default its single Edged so this line can be removed
	PWMPR = 3-1; //3 Clock cycles @60Mhz = 0.05us resoultion
	PWMMR0 = 4; //4x0.05 = 0.2us - period duration i.e. 5MHz frequency
	PWMMR2 = 2; //2x0.05 = 0.1us - pulse duration i.e. width
	PWMMCR = (1<<1); // Reset PWMTC on PWMMR0 match
	PWMLER = (1<<2) | (1<<0); // update MR0 and MR2
	PWMPCR = (1<<10); // enable PWM2 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!!
}
</code></pre>
<p><strong>Download Example 1 Project Files:</strong></p>
<div class="highlight"><strong>KEIL ARM uV5/uV4 Project for above example</strong> <a href="https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/Frequency_Counter/Example_1" target="_blank">ARM7 LPC214x Frequency Counter Example 1</a> [Successfully tested on Keil uV5.23], <a href="https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/Frequency_Counter/Example_1" target="_blank">Download Project Zip</a>. You can find the HEX file inside objects folder.</div>
<h3>2. Frequency Counter Example using Period Measurement:</h3>
<p>Here, a timer resolution of 0.05 micro-seconds is selected by using Prescaler value of 2 (3-1) and PCLK=CLK=60Mhz. We configure the CCR so that the capture occurs for rising edges and an interrupt is also generated. The Timer1 interrupt handler function is the at core of this example where we are measuring the period of the square wave signal. Refer my <a href="https://www.ocfreaks.com/lpc2148-interrupt-tutorial/" target="_blank">ARM7 LPC2148 Interrupt Tutorial</a> for more. Here we use 3 global variables viz.. <strong><span class="code_var">period</span>, <span class="code_var">previous</span> & <span class="code_var">current</span></strong>. We just update period with the difference of <strong><span class="code_var">current</span></strong> and <strong><span class="code_var">previous</span></strong>. But since the timer counter (TC) is free running, an overflow is bound to occur. So, we need to take care of this condition by detecting an overflow condition which is simply when <strong><span class="code_var">current</span></strong> is less than <strong><span class="code_var">previous</span></strong>. In this situation the time difference is calculated as:</p>
<div class="equation">Corrected Diff = (TC_MAX * OVF_CNT) + Current - Previous</div>
<p>where, <strong>TC_MAX</strong> = Maximum value of TC and <strong>OVF_CNT</strong> = Number of times overflow occurred. Since TC is 32bit, its max value in our case is 0xFFFFFFFF. Also, since we are not measuring extremely low frequency signals <strong>OVF_CNT</strong> will be at max 1. Another thing is that, if <strong>OVF_CNT</strong> is >=2 then we will need a datatype of <span class="code_ref"><strong>long long</strong></span> (8 bytes) to store the result since we won't be able to store the result in <span class="code_ref"><strong>int</strong></span> which is 4 bytes for KEIL ARM compiler.</p>
<p>Hence, the equation boils down to:</p>
<div class="equation">Corrected Diff = 0xFFFFFFFF + Current - Previous</div>
<p>Maximum value for frequency of external signal that can be correctly measured depends on the PCLK, Prescalar (PR) and the Latency of Timer Interrupt Routine execution. Out of the three, the main limiting factor is the interrupt latency. For example code given below, I was able to successfully able to measure square wave signals of upto 1Mhz. This frequency limit is more than enough for real-life applications like tachometer interfacing which I will cover in another tutorial.</p>
<p><strong>Source Code Snippet</strong></p>
<pre><code class="language-cpp">
/*(C) Umang Gajera- www.ocfreaks.com
ARM7 LPC2148 Input Capture Tutorial - Example 2 for frequency counter using ARM KEIL
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
License: GPL.*/

#include &lt;lpc214x.h&gt;
#include &lt;stdio.h&gt;   //visit https://www.ocfreaks.com/retarget-redirect-printf-scanf-uart-keil/ 
#include "lib_funcs.h" //OCFreaks LPC214x Tutorials Library Header

int main(void)
{
	initClocks(); //Set PCLK = CCLK = 60Mhz - used by: UART, Timer & PWM
	initUART0();  //Initialize UART0 for retargeted printf()
	initTimer0(); //Init Timer for delay functions
	
	//Assuming PCLK = 60Mhz
	PINSEL0 |= (1<<21); //Select CAP1.0 for P0.10
	T1CTCR = 0x0; //Run in Timer Mode
	T1PR = 3-1; //3 Clock cycles @60Mhz = 0.05us resolution
	T1TCR = 0x02; //Reset Timer
	T1CCR = (1<<0) | (1<<2); //Capture on Rising Edge(0->1) and generate an interrupt
	T1TCR = 0x01; //Enable timer1
	
	VICIntEnable |= (1<<5) ; //Enable TIMER1 IRQ
	VICVectCntl0 = (1<<5) | 5; //5th bit must 1 to enable the slot. Refer: https://www.ocfreaks.com/lpc2148-interrupt-tutorial/
	VICVectAddr0 = (unsigned) timer1ISR;

	initPWM(); //Generate Test square wave
	
	printf("OCFreaks.com - Measuring Frequency using Timer Capture\n");
	double frequencyMhz = 0;
	while(1)
	{
		frequencyMhz = (1.0 / (period*0.05) ) * 1000; //0.05 is Timer resolution in us
		printf("Frequency = %0.2f Khz\n",frequencyMhz);
		delayMS(500); //2 Udpates per second
	}
	
	//return 0; //This won't execute normally
}

__irq void timer1ISR(void)
{
	current = T1CR0;

	if(current < previous) //TC has overflowed
	{
		period = 0xFFFFFFFF + current - previous;
	}
	else
	{
		period = current - previous;
	}
	
	previous = T1CR0;
	
	T1IR = (1<<4); //write back to clear the interrupt flag
	VICVectAddr = 0x0; //Acknowledge that ISR has finished execution
}

void initPWM(void)
{
	//Refer : https://www.ocfreaks.com/lpc2148-pwm-programming-tutorial/
	/*Assuming that PLL0 has been setup with CCLK = 60Mhz and PCLK also = 60Mhz.*/
	/*This is as per the Setup &#038; Init Sequence given in the tutorial*/

	PINSEL0 |= (1<<15); // Select PWM2 output for Pin0.7
	PWMPCR = 0x0; //Select Single Edge PWM - by default its single Edged so this line can be removed
	PWMPR = 3-1; //3 Clock cycles @60Mhz = 0.05us
	PWMMR0 = 20; //20x0.05 = 1us - period duration i.e. 1MHz frequency
	PWMMR2 = 10; //10x0.05 = 0.5us - pulse duration i.e. width
	PWMMCR = (1<<1); // Reset PWMTC on PWMMR0 match
	PWMLER = (1<<2) | (1<<0); // update MR0 and MR2
	PWMPCR = (1<<10); // enable PWM2 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!!
}
</code></pre>
<p>In the Frequency Counter Program given above, you can increase the values for PWMMR0 and PWMMR2 to measure other lower frequencies. Trying measure frequencies >1Mhz will yield in incorrect measurement.</p>
<div class="highlight"><strong>KEIL ARM uV5/uV4 Project for above example</strong> <a href="https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/Frequency_Counter/Example_2" target="_blank">ARM7 LPC214x Frequency Counter Example 2</a> [Successfully tested on Keil uV5.23], <a href="https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/Frequency_Counter/Example_2" target="_blank">Download Project Zip</a>. You can find the HEX file inside objects folder.</div>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-timer-capture-mode-tutorial-frequency-counter-example/">LPC2148 Timer Capture Mode Tutorial with Frequency Counter Example</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/lpc2148-timer-capture-mode-tutorial-frequency-counter-example/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3067</post-id>	</item>
		<item>
		<title>Interfacing HC-SR04 Ultrasonic Distance Sensor with LPC2148</title>
		<link>https://www.ocfreaks.com/interfacing-hc-sr04-ultrasonic-distance-sensor-lpc2148/</link>
					<comments>https://www.ocfreaks.com/interfacing-hc-sr04-ultrasonic-distance-sensor-lpc2148/?noamp=mobile#respond</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Tue, 09 Jan 2018 11:10:50 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=3051</guid>

					<description><![CDATA[<p>In this tutorial we will see how to interface an Ultrasonic distance sensor (HC-SR04) with ARM7 LPC2148 microcontroller.  HC-SR04 Ultrasonic Distance/Ranging Sensor uses ultrasound to measure distance from a object ahead of the sensor. It is similar to SONAR in concept.</p>
<p>The post <a href="https://www.ocfreaks.com/interfacing-hc-sr04-ultrasonic-distance-sensor-lpc2148/">Interfacing HC-SR04 Ultrasonic Distance Sensor with LPC2148</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In this tutorial we will see how to interface an Ultrasonic distance sensor (HC-SR04) with ARM7 LPC2148 microcontroller. As the name suggests, the HC-SR04 Ultrasonic Distance/Ranging Sensor uses ultrasound to measure distance from a object ahead of the sensor. Ultrasound is a sound wave with frequency greater than the audible limit i.e. > 20Khz. HCSR-04 uses 40Khz ultrasound to measure distance between itself and any object ahead of it with a sensing range of 2 centimeters to 4 meters.</p>
<p><strong>Pinout:</strong> The module has got 4 pins viz. VCC(+5V), TRIG, ECHO, GND.</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/embedded/common/hcsr04_pinout.png" alt="HC-SR04 Sensor Pinout" width="275px" height="145px" /></p>
<h4>Working principle</h4>
<p>Ultrasonic Distance/Ranging Sensors are based on similar working principle to what is used in SONAR. It has got two transducers, one for transmitting ultrasound and second one for receiving the echo. Based on the time it takes for echo to arrive we can compute the distance, since we already know the speed of sound in air which is around 343 m/s or 1235 km/h (at 20°C).</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/embedded/common/hcsr04_working_principle.png" alt="HC-SR04 Ultrasonic Distance Sensor Working Principle" width="270px" height="230px" /></p>
<p><strong>Interfacing Steps:</strong></p>
<ol>
<li>To start distance measurement a short pulse of 10us is applied to Trigger pin.</li>
<li>After this the HC-SR04 Module will send a burst of 8 ultrasonic pulses at 40Khz.</li>
<li>It will then output a HIGH for the amount to time taken for the sound waves to reach back.</li>
<li>The pulse HIGH time is measured and then used to calculate the distance.</li>
</ol>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/embedded/common/hcsr04_timing_diagram.png" alt="HC-SR04 Timing Diagram waveform" width="560px" height="180px" /></p>
<h4>Distance calculations:</h4>
<p>We know speed of sound in air,</p>
<div class="equation">Vs = 343 m/s = 0.0343 cm/us</div>
<p>We also know the time it took to sound waves to emit and echo back, lets call it T. Now, by using basic distance formula we can find the distance as:</p>
<div class="equation">Distance Traveled = Speed x Time taken</div>
<div class="equation">D<sub>T</sub> = 343 <sub>m/s</sub> x T <sub>seconds</sub></div>
<p>Now, since we will be measuring ECHO ON-Time in microseconds and also to get distance in centimeters we can change the units as follows:</p>
<div class="equation">D<sub>T</sub> in cm = 0.0343 <sub>cm/us</sub> x T <sub>us</sub></div>
<p>After this we divide the computed value by 2 since the waves have traveled double distance.</p>
<div class="equation">D = </p>
<div class="fraction"><span class="fup">D<sub>T</sub></span><span class="bar">/</span><span class="fdn">2</span></div>
<p> = </p>
<div class="fraction"><span class="fup">0.0343 x T</span><span class="bar">/</span><span class="fdn">2</span></div>
<p> cm</p></div>
<h4>HC-SR04 Ultrasonic sensor Interfacing with ARM7 LPC2148 Example</h4>
<p>Now, lets do a programming exercise. For the interfacing example given below, P0.2 is configured as output and connected to TRIG pin and P0.3 is configured as input and connected to ECHO pin of the Ultrasonic Distance sensor. Timer0 module is used for generating delays with 1 us resolution. It is also used to measure time for ECHO pulse using two simple functions viz. <span class="code_var">startTimer0()</span> &#038; <span class="code_var">stopTimer0()</span>. You can check the project source code linked below for implementation details. The distance data is sent to Terminal via UART0. You can refer my <a href="https://www.ocfreaks.com/lpc2148-uart-programming-tutorial/" target="_blank">LPC2148 UART Tutorial</a> for more. The example source code also contains <a href="https://www.ocfreaks.com/retarget-redirect-printf-scanf-uart-keil/" target="_blank">retargeted printf() for KEIL</a> which redirects its output to UART0.</p>
<div class="special sp_blue noteinfo">The HC-SR04 Ultrasonic module operates on 5Volts and hence the output HIGH on ECHO pin is also 5V. We can directly interface this on any of the GPIO pin which has a 5V tolerant pad. But its better to use a voltage divider (using 2KΩ and 1KΩ resistors) to get input from ECHO pin for additional safety. Note that we don&#8217;t need to translate 3.3V to 5V for TRIG pin since 3.3V is already a HIGH for TTL compatible input pins.</div>
<p><strong>Schematic:</strong></p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/lpc214x_hcsr04_interface.png" alt="Interfacing schematic circuit diagram for HC-SR04 with ARM7 LPC2148" width="500px" height="290px" /></p>
<p><strong>Source Code:</strong></p>
<pre><code class="language-cpp">
/*(C) Umang Gajera- www.ocfreaks.com
Interfacing HC-SR04 Ultrasonic Distance/Ranging sensor with LPC2148 - Example Source Code for KEIL ARM.
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
License: GPL.*/

#include &lt;lpc214x.h&gt;
#include &lt;stdio.h&gt; //visit https://www.ocfreaks.com/retarget-redirect-printf-scanf-uart-keil/ 
#include "lib_funcs.h" //OCFreaks LPC214x Tutorials Library Header

#define TRIG (1<<2) //P0.2
#define ECHO (1<<3) //P0.3

int main(void)
{
	initClocks(); //Set PCLK = CCLK = 60Mhz - used by: UART, Timer and ADC
	initUART0();  //Initialize UART0 for retargeted printf()
	initTimer0(); //Init Timer for delay functions
	int echoTime=0;
	float distance=0;

	IO0DIR |= TRIG;    //Set P0.2(TRIG) as output
	IO0DIR &#038;= ~(ECHO); //Set P0.3(ECHO) as input (explicitly)
	IO0CLR |= TRIG;    //Set P0.2 LOW initially

	printf("OCFreaks.com LPC214x HC-SR04 Sensor Interfacing Tutorial\n");

	while(1)
	{
		//Output 10us HIGH on TRIG pin
		IO0SET |= TRIG;
		delayUS(10);
		IO0CLR |= TRIG;

		while(!(IO0PIN &#038; ECHO)); //Wait for a HIGH on ECHO pin
		startTimer0(); //Start counting
		while(IO0PIN &#038; ECHO); //Wait for a LOW on ECHO pin
		echoTime = stopTimer0(); //Stop counting and save value(us) in echoTime

		distance = (0.0343 * echoTime)/2; //Find the distance

		printf("Distance = %0.2fcm\n",distance);
		
		delayMS(1000); //wait 1 second for next update
	}
	
	//return 0; //This won't execute normally
}
</code></pre>
<p><strong>Screenshot:</strong></p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/lpc214x_hcsr04_ss.png" alt="HC-SR04 Demo Screenshot" width="501px" height="418px" /></p>
<p><strong>Download Project:</strong></p>
<div class="highlight"><strong>KEIL ARM uV5/uV4 Project for example given above is on GitHub @</strong> <a href="https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/HC-SR04_Interfacing" target="_blank">HCSR04 Ultrasonic Distance Sensor Interfacing with LPC2148</a> [Successfully tested on Keil uV5.23], <a href="https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/HC-SR04_Interfacing" target="_blank">Download Project Zip</a>. You can find the HEX file inside objects folder.</div>
<p>The post <a href="https://www.ocfreaks.com/interfacing-hc-sr04-ultrasonic-distance-sensor-lpc2148/">Interfacing HC-SR04 Ultrasonic Distance Sensor with LPC2148</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/interfacing-hc-sr04-ultrasonic-distance-sensor-lpc2148/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3051</post-id>	</item>
		<item>
		<title>Interfacing ADXL335 Accelerometer with LPC2148</title>
		<link>https://www.ocfreaks.com/interfacing-adxl335-accelerometer-lpc2148/</link>
					<comments>https://www.ocfreaks.com/interfacing-adxl335-accelerometer-lpc2148/?noamp=mobile#respond</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Wed, 06 Dec 2017 17:05:30 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=3030</guid>

					<description><![CDATA[<p>This time we will go through a tutorial on interfacing ADXL335 Accelerometer with ARM7 LPC2148 microcontroller. ADXL335 is a 3-axis acceleration sensor with analog outputs for each axis.</p>
<p>The post <a href="https://www.ocfreaks.com/interfacing-adxl335-accelerometer-lpc2148/">Interfacing ADXL335 Accelerometer with LPC2148</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>This time we will go through a tutorial on interfacing ADXL335 Accelerometer with ARM7 LPC2148 microcontroller. ADXL335 is a 3-axis acceleration sensor with analog outputs for each axis. The analog outputs are proportional to acceleration along each of the orthogonal axis. It has a minimum measurement range of <strong>±3<em>g</em></strong> where <strong><em>g</em></strong> is acceleration due to gravity at the earth&#8217;s surface (9.8m/s<sup>2</sup>). Most common applications include tilt, motion and shock sensing. The voltage supply range for ADXL335 is 1.8V to 3.6V. ADXL335 is available in 16-lead LFCSP_LQ package.</p>
<h4>Output Voltage:</h4>
<p>The output voltage(hence sensitivity) for each of the axis is directly proportional(ratiometric) with the supply voltage used. The <strong>Zero <em>g</em></strong> bias output is also proportional to the supply voltage. The <strong>Zero <em>g</em></strong> output voltage is typically <span class="doc_ref"><strong>Vs/2</strong></span> for any given valid supply voltage.</p>
<h4>Sensitivity:</h4>
<p>Sensitivity is also termed as scale factor for ADXL335 sensor is given in &#8220;<strong>milli-volts(or volts) per <em>g</em></strong>&#8221; i.e. <strong>mV/g(or V/g)</strong>. For a given Supply Voltage <strong>Vs</strong>, the sensitivity is typically <strong><span class="doc_ref">0.1xVs V/g</span></strong> or <strong><span class="doc_ref">Vsx100 mV/g</span></strong>. Hence for a <strong>Vs = 3.3V</strong> the sensitivity will be typically <strong>330mV/g</strong> or <strong>0.33V/g</strong>. We will use V/g as unit for sensitivity in the example shown later on.</p>
<h4>Converting ADC result to <em>g</em>:</h4>
<p>Lets start with the basic formula to convert a <strong>10-bit</strong> ADC result into voltage for a given VREF. The sensor output can be computed as:</p>
<div class="equation">ADC_Volts = </p>
<div class="fraction"><span class="fup">ADC_RESULT * VREF</span><span class="bar">/</span><span class="fdn">1024</span></div>
<p>V</p></div>
<p>Lets, also keep units for all voltages in volts. We get the <strong><em>g</em></strong> from ADC result as follows:</p>
<div class="equation"><strong><em>g</em></strong> = </p>
<div class="fraction"><span class="fup">ADC_Volts &#8211; Zero_G</span><span class="bar">/</span><span class="fdn">Sensitivity</span></div>
</div>
<p>Combining the above two equations, with VREF and Vs in Volts, we get,</p>
<div class="equation"><strong><em>g</em></strong> = </p>
<div class="fraction"><span class="fup">((ADC_Result*VREF)/1024) &#8211; (Vs/2)</span><span class="bar">/</span><span class="fdn">0.1*Vs</span></div>
</div>
<h4>Interfacing ADXL335 Accelerometer with ARM7 LPC2148 Example:</h4>
<p>For interfacing the Accelerometer we will use the ADC block of LPC2148. Since our sensor has 3 outputs we will use AD0.1, AD0.2, AD0.3 to read the output voltages. After fetching the ADC result we can then convert it to g for each axis. In the example given below, we will use UART0 to send the data to terminal. You can refer my <a href="https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/" target="_blank">LPC2148 ADC Tutorial</a> and <a href="https://www.ocfreaks.com/lpc2148-uart-programming-tutorial/" target="_blank">UART Tutorial</a> for more. The example project linked below also contains <a href="https://www.ocfreaks.com/retarget-redirect-printf-scanf-uart-keil/" target="_blank">retargeted printf() for KEIL</a> which redirects its output to UART0.</p>
<p><strong>Schematic:</strong></p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/adxl335_interfacing_lpc214x_schematic.png" alt="Schematic for Interfacing ADXL335 Accelerometer sensor with LPC2148" width="425px" height="285px" /></p>
<div class="special sp_red notewarning"><strong>Note:</strong> Some modules might have on board voltage regulator (and hence extra pins in interface header) which makes it easier to interface with 5V microcontrollers. Please make sure the power supply connections to the module is proper and also change the value of Vs appropriately in your program to get valid results.</div>
<p><strong>main.c source code:</strong></p>
<pre><code class="language-cpp">
/*(C) Umang Gajera- www.ocfreaks.com
Interfacing ADXL335 Accelerometer sensor with LPC2148 - Example Source Code for KEIL ARM.
Also see: https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
License: GPL.*/

#include &lt;lpc214x.h&gt;
#include &lt;stdio.h&gt; //visit https://www.ocfreaks.com/retarget-redirect-printf-scanf-uart-keil/ 
#include "lib_funcs.h" //OCFreaks LPC214x Tutorials Library Header

#define SEL_AD01 (1<<1) //Select AD0.1 Channel
#define SEL_AD02 (1<<2) //Select AD0.2 Channel
#define SEL_AD03 (1<<3) //Select AD0.3 Channel
#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  (1UL<<31)

#define VREF 3.3 //Reference Voltage at VREF pin
#define VS 	 3.3 //Supply Voltage for ADXL335
#define ZERO_G (VS/2) //Zero g bias

int main(void)
{
	initClocks(); //Set PCLK = CCLK = 60Mhz - used by: UART, Timer and ADC
	initUART0();  //Initialize UART0 for retargeted printf()
	initTimer0(); //Init Timer for delay functions
	
	PINSEL1 |= (1<<24) | (1<<26) | (1<<28); //select AD0.1/2/3 for P0.28/29/30.
	int adcX,adcY,adcZ;
	float resultVolts,Xg,Yg,Zg;
	unsigned long ADC0CR_Setup = (CLKDIV<<8) | BURST_MODE_OFF | PowerUP;
	printf("OCFreaks.com LPC214x ADXL335 Sensor Interfacing Tutorial\n");
	
	int skipFirst = 1;
	
	while(1)
	{
		AD0CR = ADC0CR_Setup | SEL_AD01 | START_NOW;//Start new Conversion on AD0.1
		while( (AD0DR1 &#038; ADC_DONE) == 0 );
		adcX = (AD0DR1>>6) & 0x3FF; //get the 10bit ADC result
		
		AD0CR = ADC0CR_Setup | SEL_AD02 | START_NOW; //Start new Conversion on AD0.2
		while( (AD0DR2 & ADC_DONE) == 0 );
		adcY = (AD0DR2>>6) & 0x3FF; //get the 10bit ADC result
		
		AD0CR = ADC0CR_Setup | SEL_AD03 | START_NOW; //Start new Conversion on AD0.3
		while( (AD0DR3 & ADC_DONE) == 0 );
		adcZ = (AD0DR3>>6) & 0x3FF; //get the 10bit ADC result
		
		if(skipFirst) //Ignore first ADC readings.
		{
			skipFirst = 0; 
			continue;
		}
		
		//Computing in 2 steps to keep things simple.
		resultVolts = ((float)adcX * VREF) / 1024; //ADC Result converted to millivolts
		Xg = (resultVolts - ZERO_G) / (VS*0.1);
		
		resultVolts = ((float)adcY * VREF) / 1024; //ADC Result converted to millivolts
		Yg = (resultVolts - ZERO_G) / (VS*0.1);
		
		resultVolts = ((float)adcZ * VREF) / 1024; //ADC Result converted to millivolts
		Zg = (resultVolts - ZERO_G) / (VS*0.1);
		
		printf("X=%0.2fg Y=%0.2fg Z=%0.2fg\n", Xg,Yg,Zg);
		
		delayMS(250); //4 Updates per second
	}
	
	//return 0; //This won't execute normally
}
</code></pre>
<p><strong>Screenshot:</strong></p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/adxl335_lpc214x_demo.png" alt="Demo Screenshot for Interfacing ADXL335 Accelerometer sensor with LPC2148" width="469px" height="338px" /></p>
<p><strong>Project Download:</strong></p>
<div class="highlight"><strong>KEIL ARM uV5/uV4 Project for example given above is on GitHub @</strong> <a href="https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/ADXL335_Interfacing" target="_blank">ADXL335 Accelerometer Interfacing with LPC2148</a> [Successfully tested on Keil uV5.23], <a href="https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/ADXL335_Interfacing" target="_blank">Download Project Zip</a>. You can find the HEX file inside objects folder.</div>
<p><strong>Reference(s):</strong></p>
<ul>
<li><a href="http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL335.pdf" target="_blank">ADXL335 Datasheet</a></li>
</ul>
<p>The post <a href="https://www.ocfreaks.com/interfacing-adxl335-accelerometer-lpc2148/">Interfacing ADXL335 Accelerometer with LPC2148</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/interfacing-adxl335-accelerometer-lpc2148/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3030</post-id>	</item>
		<item>
		<title>Interfacing LM35 Sensor with LPC2148</title>
		<link>https://www.ocfreaks.com/interfacing-lm35-sensor-lpc2148/</link>
					<comments>https://www.ocfreaks.com/interfacing-lm35-sensor-lpc2148/?noamp=mobile#respond</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Sat, 02 Dec 2017 12:46:39 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=3020</guid>

					<description><![CDATA[<p>In this tutorial we will see how to interface LM35 Temperature sensor with ARM7 LPC2148 Microcontroller and also cover a simple interfacing example using in-built ADC. </p>
<p>The post <a href="https://www.ocfreaks.com/interfacing-lm35-sensor-lpc2148/">Interfacing LM35 Sensor with LPC2148</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In this tutorial we will see how to interface LM35 Temperature sensor with ARM7 LPC2148 Microcontroller. LM35 is a well known low cost temperature sensor. It is directly calibrated in Degrees Celsius meaning that the output voltage is directly proportional to Degrees Celsius readings. Its measurement range is from -55°C to 150°C having typical accuracy(s) of 0.25°C at room temperature and 0.75°C for full range. LM35 also supports a wide range of supply voltage from 4V to 30V and is available in 4 different packages viz. TO-CAN, TO-92, SOIC and TO-220.</p>
<p><strong>LM35 Pinout for TO-92 Package:</strong></p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/embedded/common/lm35_to-92.png" alt="LM35 TO-92 Package" width="200px" height="160px" /></p>
<p>Pin 1 (+Vs) is the positive power supply pin, Pin 2 (V<sub>OUT</sub>) provides the output voltage linearly proportional to temperature and Pin 3 is for Ground.</p>
<p><strong>LM35 can be configured in two ways:</strong></p>
<ul style="margin-bottom:15px;">
<li>Basic Centigrade Temperature Sensor (2°C to 150°C)</li>
<li>Full Range Centigrade Temperature sensor (-55°C to 150°C)</li>
</ul>
<p>For the sake of simplicity, we will use LM35 in basic configuration. The first thing to note when interfacing LM35 with 3.3v MCUs is that LM35 has a supply voltage range of 4V to 30V. Hence, another supply of say 5V would be required to get proper readings. The second thing to note is that the output voltage has a scale factor of <strong>10mV per Degree Centigrade</strong> and this is fixed irrespective of what supply voltage you use between 4V to 30V. Hence, the output voltage can go max up to 1.5V and as low as -55mV when configured as Full Range Centigrade Sensor. </p>
<p>When using the Basic configuration we have an output swing of ~0V(20mV) to 1.5V. So, if we use a <strong>VREF of 3.3 Volts</strong> we get a resolution of 3.3V/1024 =  0.00322V or <strong>3.22mV</strong> since LPC2148 has a <strong>10-bit ADC</strong>. Considering that LM35&#8217;s typical accuracy at room temperature(25°C) is 0.25°C (worst case accuracy = 0.5°C @ 25°C) and scale factor of 10mV/°C, a resolution of 3.22mV barely fits the bill. To get better resolution we can use a lower voltage reference like 1.8V or 2V. For example, using 1.8V reference we get a resolution of 1.75mV. For the interfacing example given below, we will assume a voltage reference of 3.3V which is commonly used on development boards. If these connections are not present on your development board please make sure VREF pin is connected to 3.3V.</p>
<h4>LM35 Transfer Function &#038; Output Conversion to °C</h4>
<p>The formula for<strong> V<sub>OUT</sub></strong> is given as:</p>
<div class="equation">V<sub>OUT</sub>(mV) = 10mV/°C * T</div>
<p>Using the above formula, with VERF in Volts and Scale-factor in V/°C, we can compute the temperature(T) from <strong>10-bit</strong> ADC Result as:</p>
<div class="equation">T = </p>
<div class="fraction"><span class="fup">ADC_RESULT * VREF</span><span class="bar">/</span><span class="fdn">1024 * 0.01</span></div>
<p>°C</p></div>
<p>Which can be simplified as,</p>
<div class="equation">T = </p>
<div class="fraction"><span class="fup">ADC_RESULT * VREF * 100</span><span class="bar">/</span><span class="fdn">1024</span></div>
<p>°C</p></div>
<p>So, when using VREF = 3.3V we get,</p>
<div class="equation">T = </p>
<div class="fraction"><span class="fup">ADC_RESULT * 330</span><span class="bar">/</span><span class="fdn">1024</span></div>
<p>°C</p></div>
<h4>Example for LM35 Interfacing with ARM7 LPC2148</h4>
<p>Now lets cover a simple programming example. As obvious, we will use the ADC block of LPC214x for interfacing our temperature sensor using reference voltage as mentioned above. We will use UART0 to send the data to terminal. You can refer my <a href="https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/" target="_blank">LPC2148 ADC Tutorial</a> and <a href="https://www.ocfreaks.com/lpc2148-uart-programming-tutorial/" target="_blank">UART Tutorial</a> for more. The example project linked below also contains <a href="https://www.ocfreaks.com/retarget-redirect-printf-scanf-uart-keil/" target="_blank">retargeted printf() for KEIL</a> which redirects its output to UART0.</p>
<p><strong>Interfacing Schematic:</strong></p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/lm35_lpc2148_interface_schematic.png" alt="Schematic for Interfacing LM35 Temperature sensor with LPC2148" width="445px" height="315px" /></p>
<div class="special sp_blue noteinfo">Since we are interfacing with 5V powered device, I have included an optional ADC input protection using a 1K resistor and a 3.3V Zener Diode &#8211; just in case anything goes wrong while making the connections or with the temperature sensor.</div>
<p><strong>Source code:</strong></p>
<pre><code class="language-cpp">
/*(C) Umang Gajera- www.ocfreaks.com
Interfacing LM35 with LPC2148 - Example Source Code for KEIL ARM.
Also see: https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
License: GPL.*/

#include &lt;lpc214x.h&gt;
#include &lt;stdio.h&gt; //visit https://www.ocfreaks.com/retarget-redirect-printf-scanf-uart-keil/ 
#include "lib_funcs.h" //OCFreaks LPC214x Tutorials Library Header

#define AD06 ((1<<9)|(1<<8)) //Select AD0.6 function for P0.4
#define SEL_AD06 (1<<6) //Select AD0.6 Channel
#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 (1UL<<31)

#define VREF 3.3 //Reference Voltage at VREF pin

int main(void)
{
	initClocks(); //Set PCLK = CCLK = 60Mhz - used by: UART, Timer and ADC
	initUART0();  //Initialize UART0 for retargeted printf()
	initTimer0(); //Init Timer for delay functions
	
	PINSEL0 |= AD06; //select AD0.6 for P0.4
	unsigned long AD0CR_setup = (CLKDIV<<8) | BURST_MODE_OFF | PowerUP; 
	int result = 0;
	float temp = 0;
	
	printf("OCFreaks.com LPC214x LM35 Interfacing Tutorial\n");
	
	AD0CR = AD0CR_setup | SEL_AD06; //Setup ADC block
	
	AD0CR |= START_NOW; //Start new Conversion
	while( (AD0DR6 &#038; ADC_DONE) == 0 );
	//Ignore the first ADC reading.
	
	while(1)
	{
		AD0CR |= START_NOW; //Start new Conversion
		while( (AD0DR6 &#038; ADC_DONE) == 0 );
		
		result = (AD0DR6>>6) & 0x3FF; //get the 10bit ADC result
		
		temp = ((float)result * VREF * 100)/1024; //As per the Equation given in the tutorial

		printf("Temp = %0.1f Deg. Celsius\n" , temp);
		
		delayMS(1000); //1 Update per second
	}
	
	//return 0; //This won't execute normally
}
</code></pre>
<p><strong>Screenshot:</strong></p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/lm35_lpc214x_ex_ss.png" alt="Demo Screenshot for Interfacing LM35 Temperature sensor with LPC2148" width="533px" height="274px" /></p>
<div class="highlight"><strong>KEIL ARM uV5/uV4 Project for example given above is on GitHub @</strong> <a href="https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/LM35_Interfacing" target="_blank">LM35 Temperature Sensor Interfacing with LPC2148</a> [Successfully tested on Keil uV5.23], <a href="https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/LM35_Interfacing" target="_blank">Download Project Zip</a>. You can find the HEX file inside objects folder.</div>
<p><strong>Reference(s):</strong></p>
<ul>
<li><a href="http://www.ti.com/lit/ds/symlink/lm35.pdf" target="_blank">LM35 Temperature Sensor Datasheet</a></li>
</ul>
<p>The post <a href="https://www.ocfreaks.com/interfacing-lm35-sensor-lpc2148/">Interfacing LM35 Sensor with LPC2148</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/interfacing-lm35-sensor-lpc2148/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3020</post-id>	</item>
		<item>
		<title>Interfacing LDR with LPC2148</title>
		<link>https://www.ocfreaks.com/interfacing-ldr-lpc2148/</link>
					<comments>https://www.ocfreaks.com/interfacing-ldr-lpc2148/?noamp=mobile#respond</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Thu, 23 Nov 2017 17:08:10 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=3014</guid>

					<description><![CDATA[<p>This time we will go through a tutorial on Interfacing LDR with ARM7 LPC2148 microcontroller.  Light Dependent Resistor (i.e. LDR for short) is basically used to detect the intensity of light and hence, for example, we can detect if a room is dark or lit. We will also cover two interfacing examples using ADC block of LPC214x.</p>
<p>The post <a href="https://www.ocfreaks.com/interfacing-ldr-lpc2148/">Interfacing LDR with LPC2148</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>This time we will go through a tutorial on Interfacing LDR with ARM7 LPC2148 microcontroller. Light Dependent Resistor (i.e. LDR for short) is basically used to detect the intensity of light and hence, for example, we can detect if a room is dark or lit. They are also known as photoresistors or photoconductive cells. The resistance of LDR is inversely proportional to the intensity of light. Hence under dark conditions the resistance typically rises to around 500K to 5M. Under ambient light the resistance is generally around 2K to 10K. Under very bright light its resistance falls down to around few ohms, for example around 2 to 20 ohms.</p>
<p>For the examples covered in this tutorial, we will use the on-chip ADC module of LPC214x microcontroller to interface LDR. You can check my <a href="https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/" target="_blank">LPC2148 ADC Programming Tutorial</a> for reference. Since we cannot directly connect an LDR to the ADC&#8217;s input, we need to convert the resistance into an equivalent voltage. Once we do this it becomes very easy to interface them. By adding another fixed resistor in series we can form a potential divider network and can connect the common of the resister and LDR to ADC&#8217;s input pin. Lets call LDR as R1 and the fixed resistor as R2. The voltage V<sub>out</sub> at the common terminal is given as:</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/embedded/common/ldr_voltage_divider.png" alt="LDR Voltage Divider Circuit" width="310px" height="230px" /></p>
<p>Now, to implement this we need a suitable value for R2. The value of R2 in voltage-divider is primarily determined by:</p>
<ul style="margin-bottom:15px;">
<li>LDR&#8217;s maximum current</li>
<li>LDR&#8217;s Response curve [Resistance Vs LUX] (which factors in resistance in dark, bright light and ambient light)</li>
</ul>
<p>Most of the time using 3.3K or 4.7K or 10K resistor as R2 will do the job. But, always check the manufacturer&#8217;s datasheet for max operating current. In case you don&#8217;t have access to datasheet, then generally a couple of few milliamps must be safe to operate. For the ones which I have used (which have come in ~5mm package), generally 1mA to 5mA have not been an issue. Some LDR&#8217;s also allow 50mA to 75mA+ max current. In my case a resistor of 4.7K just works fine and gives a good full scale range. Given LPC2148 uses 3.3V as Vcc, a resistor of 4.7K will limit current to 0.7mA max through LDR under very bright conditions.</p>
<h4>Example 1 &#8211; Reading the voltage divider output:</h4>
<p>In this example we will read the voltage divider output, which changes as the intensity of light changes. The ADC will convert the input voltage to a proportional value between 0 and 1023. We can directly use this value(ADC Result) to detect the light intensity. A value of 0 means Dark condition and a value of 1023 means very bright light is present. With this example you can see the output values change as you change the intensity of light. Working with this example you can decide on a cut-off value or a cut-off range to trigger a function. In this example I have <a href="https://www.ocfreaks.com/retarget-redirect-printf-scanf-uart-keil/" target="_blank">retargeted printf() to redirect its output over UART</a>. Here is the schematic for interfacing LDR with ARM7 LPC2148:</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/lpc214x_ldr_ex1.png" alt="LPC214x LDR Interfacing Example 1 Schematic" width="300px" height="275px" /></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 LDR Interfacing Example 1 Source Code for KEIL ARM.
Also see: https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/
License: GPL.*/

#include &lt;lpc214x.h&gt;
#include &lt;stdio.h&gt; //For retargeted printf()
#include "lib_funcs.h" //OCFreaks LPC214x Tutorial Library 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 (1UL<<31)

#define VREF 3.3 //Reference Voltage at VREF pin

int main(void)
{
	initClocks(); //Set PCLK = CCLK = 60Mhz - used by: UART, Timer and ADC
	initUART0(); //Initialize UART0 for retargeted printf()
	initTimer0(); //Init Timer for delay functions
	
	PINSEL0 |= AD06 ; //select AD0.6 for P0.4
	int result = 0;
	unsigned long AD0CR_setup = (CLKDIV<<8) | BURST_MODE_OFF | PowerUP; //Setup ADC block
	
	printf("OCFreaks.com LPC214x LDR Interfacing Tutorial - Example 1.\n");
	
	while(1)
	{
		AD0CR =  AD0CR_setup | SEL_AD06; 
		AD0CR |= START_NOW; //Start new Conversion

		while( (AD0DR6 &#038; ADC_DONE) == 0 );
		
		result = (AD0DR6>>6) & 0x3FF; //get the 10bit ADC result

		printf("AD0.6 = %d\n" , result);
		delayMS(500); //Slowing down Updates to 2 Updates per second
	}
	
	//return 0; //This won't execute normally
}
</code></pre>
<div class="highlight"><strong>KEIL ARM uV5/uV4 Project for example given above on GitHub @</strong> <a href="https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/LDR_Interfacing/Example_1" target="_blank">LDR Interfacing with LPC2148 Example 1</a> [Successfully tested on Keil uV5.23], <a href="https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/LDR_Interfacing/Example_1" target="_blank">Download Project Zip</a>. You can find the HEX file inside objects folder.</div>
<h4>Example 2 &#8211; Turn on LED when its dark:</h4>
<p>This is a classic example for LDRs. Here we will turn-on an LED when we detect low light or dark conditions. Instead of an LED you can also connect a Relay which can then switch on a Bulb or something like heater during night in cold regions. Make sure you keep the LDR away from the LED or Bulb since we are only interested in detecting sunlight and not artificial light in this case. Schematic for this example is given as follows:</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/lpc214x_ldr_ex2.png" alt="LPC214x LDR Interfacing Example 2 Schematic" width="304px" height="275px" /></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 LDR Interfacing Example 2 Source Code for KEIL ARM.
Also see: https://www.ocfreaks.com/lpc2148-adc-programming-tutorial/
License: GPL.*/

#include &lt;lpc214x.h&gt;
#include "lib_funcs.h" //OCFreaks LPC214x Tutorial Library 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 (1UL<<31)
#define VREF 3.3 //Reference Voltage at VREF pin

#define CUT_OFF 200 //Define your cut-off value here

int main(void)
{
	initClocks(); //Set PCLK = CCLK = 60Mhz
	initTimer0(); //Init Timer for delay functions
	
	PINSEL0 |= AD06 ; //select AD0.6 for P0.4
	IO0DIR |= (1<<5); //Select P0.5 as output 
	int result = 0;
	unsigned long AD0CR_setup = (CLKDIV<<8) | BURST_MODE_OFF | PowerUP; //Setup ADC block
	
	while(1)
	{
		AD0CR =  AD0CR_setup | SEL_AD06; 
		AD0CR |= START_NOW; //Start new Conversion

		while( (AD0DR6 &#038; ADC_DONE) == 0 );
		
		result = (AD0DR6>>6) & 0x3FF; //get the 10bit ADC result
		
		if(result < CUT_OFF)
		{
			IO0SET = (1<<5); //LED ON
		}
		else
		{
			IO0CLR = (1<<5); //LED OFF
		}
		
		delayMS(100); //wait some time since LDRs don't react immediately.
	}
	//return 0; //This won't execute normally
}
</code></pre>
<div class="highlight"><strong>KEIL ARM uV5/uV4 Project for example given above on GitHub @</strong> <a href="https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/LDR_Interfacing/Example_2" target="_blank">LDR Interfacing with LPC2148 Example 2</a> [Successfully tested on Keil uV5.23], <a href="https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/OCFreaks/LPC2148-Tutorial-Examples/tree/master/LDR_Interfacing/Example_2" target="_blank">Download Project Zip</a>. You can find the HEX file inside objects folder.</div>
<p>The post <a href="https://www.ocfreaks.com/interfacing-ldr-lpc2148/">Interfacing LDR with LPC2148</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/interfacing-ldr-lpc2148/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3014</post-id>	</item>
		<item>
		<title>LPC2148 I2C Programming Tutorial</title>
		<link>https://www.ocfreaks.com/lpc2148-i2c-programming-tutorial/</link>
					<comments>https://www.ocfreaks.com/lpc2148-i2c-programming-tutorial/?noamp=mobile#respond</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Mon, 10 Apr 2017 14:42:46 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=2752</guid>

					<description><![CDATA[<p>In this tutorial we will go through LPC2148 I2C programming and learn how to program it for interfacing various I2C modules, sensors and other slave devices. For those who are new to I2C Bus &#38; Protocol I have posted an I2C Basics Tutorial @ https://www.ocfreaks.com/i2c-tutorial/ Introduction A quick Recap of I2C I2C was invented by [&#8230;]</p>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-i2c-programming-tutorial/">LPC2148 I2C Programming Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In this tutorial we will go through LPC2148 I2C programming and learn how to program it for interfacing various I2C modules, sensors and other slave devices. For those who are new to I2C Bus &amp; Protocol I have posted an I2C Basics Tutorial @ <a href="https://www.ocfreaks.com/i2c-tutorial/">https://www.ocfreaks.com/i2c-tutorial/</a></p>
<h2 class="shead">Introduction</h2>
<h4>A quick Recap of I2C</h4>
<p>I2C was invented by Philips in 1980s. I2C stands for Inter-Integrated Circuit and also sometimes also referred as TWI i.e. Two Wire Interface since it uses only 2 wires for data transmission and synchronization. The two wires of I2C Bus consists of:<br />
1. Data Line which is <strong>SDA i.e. Serial Data</strong><br />
2. Clock Line which is <strong>SCL i.e. Serial Clock</strong></p>
<p>I2C uses 7bit and 10bit addresses for each device connected to the bus. 10bit addressing was introduced later. In this tutorial we will use 7bit addressing since its common with sensors, eeproms, etc. A general I2C bus topology with multiple masters and multiple slaves connected to the bus at the same time is shown below:</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/electronics/i2c_tutorial/i2c_bus_general_topology.png" alt="I2C/TWI Bus Topology" width="582px" height="382px" /></p>
<p>I2C bus is a <strong>Byte Oriented bus</strong>. Only a byte can be transferred at a time. Communication(Write to &amp; Read from) is always initiated by a Master. The Master first sends a <b>START</b> condition and then writes the <b>Slave Address(SLA)</b> and the <b>Direction bit(Read=1/Write=0)</b> on bus and the corresponding Slave responds accordingly.</p>
<p>Format for I2C communication protocol is given as:</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/electronics/i2c_tutorial/i2c_communication_protocol.png" alt="I2C Protocol" width="558px" height="237px" /></p>
<h2 class="shead">I2C module in LPC2148 ARM7 Microcontrollers</h2>
<p>The I2C block in LPC2148 and other LPC2100 series ARM7 MCUs can be configured as either Master, Slave or both Master &amp; Slave. It also features a programmable clock which aids in using different transfer rates as required. The I2C block in LPC214x supports speeds up to 400kHz.</p>
<p class="compact_header"><strong>I2C has 4 operating modes:</strong></p>
<ol>
<li>Master Transmitter mode</li>
<li>Master Receiver mode</li>
<li>Slave Transmitter mode</li>
<li>Slave Receiver mode</li>
</ol>
<p>LPC2148 ARM7 Microcontroller supports all of these 4 modes, but in this tutorial we will go through Master Transmitter and Master Receiver modes only since implementing the Slave modes is easy once you understand the Master modes and also since Master mode is used for interfacing with Sensors, LCD Displays, and other I2C slave devices.</p>
<div class="special sp_yellow noteinfo">
<h4>Pins relating to I2C Module of LPC2148</h4>
<p class="compact_header">For I2C0 block the <strong>SLC(Clock)</strong> pin is <strong>P0.2</strong> and <strong>SDA(Data)</strong> Pin is <strong>P0.3</strong>, while for I2C1 block the <strong>SCL</strong> pin in <strong>P0.11</strong> and <strong>SDA</strong> pin is <strong>P0.14</strong>.</p>
</div>
<h2 class="shead">Registers used for programming LPC2148 I2C block</h2>
<p>Before we get into Operating mode details lets go through the registers used in I2C block of LPC214x:</p>
<p><strong>(Replace 0 with 1 for I2C1 block registers)</strong></p>
<div class="highlight"><strong>1) <span class="doc_ref">I2C0CONSET</span> (8 bit) &#8211; I2C control set register:</strong> The bits in this register control the operation of the I2C interface. Writing a 1 to a bit of this register causes the corresponding bit in the I2C control register(inside I2C block) to be set. Writing a 0 has no effect. This is a Read-Write register.</p>
<ol>
<li><strong>Bits[0 &amp; 1] :</strong> Reserved</li>
<li><strong>Bit 2 &#8211; AA &#8211; Assert Acknowledge Flag :</strong> When this bit is set to 1, an acknowledge (Logic low on SDA) will be returned when a data byte has been received in the master receiver mode. Similarly when AA is set to 0, a not acknowledge (Logic low on SDA) will be returned when a data byte has been received in the master receiver mode.</li>
<li><strong>Bit 3 &#8211; SI &#8211; I2C Interrupt Flag :</strong> This bit is set whenever the I2C state changes(Except for state code 0xF8). When SI is set the Low Period of the serial clock is stretched which is also termed as clock stretching. When SCL is HIGH, its not affected by the state of SI flag. SI must be reset using I2CONCLR register everytime.</li>
<li><strong>Bit 4 &#8211; STO &#8211; STOP Flag :</strong> When this bit is set to 1 the I2C interface will send a STOP condition.</li>
<li><strong>Bit 5 &#8211; STA &#8211; START Flag :</strong> When this bit is set to 1 the I2C interface is forced to enter Master mode and send a START Condition or send a Repeated START if its already in Master mode.</li>
<li><strong>Bit 6 &#8211; I2EN &#8211; I2C interface Enable :</strong> This bit is used to Enabled or Disable the I2C interface. When set to 1 the I2C interface is enabled and when set to 0 the I2C interface is disabled.</li>
<li><strong>Bit 7 </strong>&#8211; Reserved.</li>
</ol>
<p><strong>2) <span class="doc_ref">I2C0CONCLR</span> (8 bit) &#8211; I2C control clear register.</strong> This register is used to clear bits in I2C0CONSET register. Writing 0 no effect. The bit locations are same as that of I2C0CONSET register given above. Its a Write only register.</p>
<p><strong>3) <span class="doc_ref">I2C0STAT</span> (8 bit) </strong>&#8211; This gives the current state of I2C interface in form of state codes. This is a read only register.</p>
<p><strong>4) <span class="doc_ref">I2C0DAT</span> (8 bit)</strong> &#8211; This register contains the data that is to be transmitted or the latest received data. Data in this register is always shifted from right to left i.e. the first bit to be transmitted is the MSB (bit 7), and after a byte has been received, the first bit of received data is located at the MSB of I2C0DAT.</p>
<p><strong>5) <span class="doc_ref">I20SCLH</span> (16 bit)</strong> &#8211; This register is used to store the High time period of the SCL pulse.</p>
<p><strong>6) <span class="doc_ref">I20SCLL</span> (16 bit)</strong> &#8211; This register is used to store the Low time period of the SCL pulse.</p>
<p><strong>7) <span class="doc_ref">I2C0ADR</span> (8 bit)</strong> &#8211; I2C Slave Address register : Not applicable for master mode. Used to store the address in slave mode.</p>
</div>
<h2 class="shead">LPC2148 I2C Status Codes</h2>
<p>Before we start coding, first lets go through some status codes. Whenever an event occurs on the I2C bus a corresponding I2C status code will be set in I2CxSTAT register.</p>
<div class="special sp_cyan notestar"><strong>Status Codes Common to Master Transmitter &amp; Receiver Mode:</strong></p>
<table class="no_style">
<tbody>
<tr>
<td><span class="doc_ref">0x08</span></td>
<td>: A <strong>START condition</strong> has been transmitted. Load Slave Address + Read/Write (SLA+R/W) into I2CDAT to transmit it.</td>
</tr>
<tr>
<td><span class="doc_ref">0x10</span></td>
<td>: A <strong>REPEAT START condition</strong> has been transmitted. Load Slave Address + Read/Write (SLA+R/W) into I2CDAT to transmit it.</td>
</tr>
<tr>
<td><span class="doc_ref">0x18</span></td>
<td>: Previous state was State 0x08 or State 0x10, <b>SLA+R/W</b> has been transmitted, <b>ACK</b> has been received. The first data byte will be transmitted, an <b>ACK[Acknowledgment](AA=0)<b> bit will be received.</b></b></td>
</tr>
<tr>
<td><span class="doc_ref">0x20</span></td>
<td>: <b>SLA+R/W</b> has been transmitted, <b>NOT ACK(AA=1)</b> has been received. A <b>STOP</b> condition will be transmitted.</td>
</tr>
</tbody>
</table>
</div>
<div class="special sp_cyan notestar"><strong>Master Transmitter Status Codes:</strong></p>
<table class="no_style">
<tbody>
<tr>
<td><span class="doc_ref">0x28</span></td>
<td>: Data has been transmitted, <b>ACK(AA=0)</b> has been received. If the transmitted data was the last data byte then transmit a <b>STOP</b> condition, otherwise transmit the next data byte.</td>
</tr>
<tr>
<td><span class="doc_ref">0x30</span></td>
<td>: Data has been transmitted, <b>NOT ACK(AA=1)</b> received. A <b>STOP</b> condition will be transmitted.</td>
</tr>
<tr>
<td><span class="doc_ref">0x38</span></td>
<td>: Arbitration has been lost while sending <b>Slave Address + Write or Data</b>. The bus has been released and not addressed Slave mode is entered. A new <b>START</b> condition will be transmitted when the bus is free again.</td>
</tr>
</tbody>
</table>
</div>
<div class="special sp_cyan notestar"><strong>Master Receiver Status Codes:</strong></p>
<table class="no_style">
<tbody>
<tr>
<td><span class="doc_ref">0x40</span></td>
<td>: Previous state was State 0x08 or State 0x10. <b>Slave Address + Read (SLA+R)</b> has been transmitted, <b>ACK</b> has been received. Data will be received and <b>ACK</b> returned.</td>
</tr>
<tr>
<td><span class="doc_ref">0x48</span></td>
<td>: <b>Slave Address + Read (SLA+R)</b> has been transmitted, <b>NOT ACK</b> has been received. A <b>STOP</b> condition will be transmitted.</td>
</tr>
<tr>
<td><span class="doc_ref">0x50</span></td>
<td>: Data has been received, <b>ACK</b> has been returned. Data will be read from <b>I2DAT</b>. Additional data will be received. If this is the last data byte then <b>NOT ACK</b> will be returned, otherwise <b>ACK</b> will be returned.</td>
</tr>
<tr>
<td><span class="doc_ref">0x58</span></td>
<td>: Data has been received, <b>NOT ACK</b> has been returned. Data will be read from I2DAT. A <b>STOP</b> condition will be transmitted.</td>
</tr>
</tbody>
</table>
</div>
<div class="special sp_blue noteinfo">A Complete List of Status Codes, software response and what action is taken next by the I2C module on lpc214x is given in the <strong>Datasheet(UM10120 Rev. 02) from Page 152+ onwards</strong>. Refer the same whenever in doubt.</div>
<h2 class="shead">I2C Master Modes in LPC2148 ARM7 MCU:</h2>
<h3>I2C Master Transmitter Mode:</h3>
<p>To enter the Master Transmitted mode we set the <b>STA</b> bit to 1. After this the master will output a <b>START</b> condition(as soon as the bus is free) and the first byte is sent on the bus that will contain the address(7 bits) of the slave device along with the R/W bit. Here we set the R/W to 0 which means Write. After this the data sent a byte at a time. For each byte sent by master, the slave device sends a corresponding <b>Acknowledgement bit(AA=0)</b>. When slave is finished sending data or has no more data to send it will send a <b>&#8216;Not Acknowledge'(AA=1) bit</b> to indicate this. After this the master outputs a <b>STOP</b> condition. This is shown the figure below:</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/electronics/i2c_tutorial/i2c_master_transmitter_mode.png" alt="master transmitter mode format" width="558px" height="225px" /></p>
<h3>I2C Master Receiver Mode:</h3>
<p>In this mode a slave transmitter sends data to a Master Receiver. The initialization is same that we saw for Master Transmitter above, except here we set the R/W bit to 1 which means Read. The slave acknowledges it and sends data byte(s). For each byte sent by slave, the master device sends a corresponding <b>Acknowledgement bit(AA=0)</b>. If master wants to continue it can send an Acknowledge bit to salve or if master want to stop receiving data it will send a <b>&#8216;Not Acknowledge'(AA=1) bit</b> to indicate this. After this master will output a <b>STOP</b> condition. This is shown the figure below:</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/electronics/i2c_tutorial/i2c_master_receiver_mode.png" alt="master receiver mode format" width="558px" height="163px" /></p>
<div class="special sp_blue notestar">Each time any activity occurs on the bus the <b>I2C0STAT</b> will be loaded with a corresponding status code and the <b>SI</b> bit is also set which triggers the ISR if defined. Using these status codes we can check for successful transfers, errors on the bus or any other conditions and proceed accordingly. After taking appropriate actions(either inside ISR or outside of ISR) we must also clear the <b>SI</b> bit every time.</div>
<h3>Procedure for I2C communication in Master Transmitter Mode:</h3>
<div class="highlight">
<ol class="list_basic">
<li><strong>1.</strong> After Enabling I2C block Enter master mode by Setting <b>STA</b> bit which will send the <b>START</b> condition. If already in Master mode a <b>REPEAT START</b> will be send.</li>
<li><strong>2.</strong> After <b>START</b> has been sent the <b>SI</b> bit in I2C0CON will be set to 1 and value of <b>I2C0STAT</b> will be <span class="doc_ref">0x08</span> . For <b>REPEAT START</b> <b>I2C0STAT</b> will be <span class="doc_ref">0x10</span> .</li>
<li><strong>3.</strong> Now load <b>I2C0DAT</b> with 7 bit <b>Slave Address(SLA) + R/W bit i.e. SLA+RW</b>. Here it will be <b>SLA+W</b>(Note: W=0, R=1). Next clear <b>SI</b> to transfer this first byte.</li>
<li><strong>4.</strong> After <b>SLA+R/W</b> has been sent, an <b>Acknowledge/ACK(AA=0)</b> bit is received and <b>SI</b> bit is set again. At this point status code(s) <span class="doc_ref">0x28</span> , <span class="doc_ref">0x30</span> &amp; <span class="doc_ref">0x38</span> are possible.</li>
<li><strong>5.</strong> Now depending on the Status proceed further.
<ol class="list_basic">
<li><strong>5.1 </strong>In normal situation status code will be <span class="doc_ref">0x18</span> which means <b>SLA+W</b> has been Transmitted and <b>ACK(A=0)</b> has been Received.</li>
<li><strong>5.2 </strong>Next, load the data to be sent into <b>I2C0DAT</b> and then clear <b>STA</b>, <b>STO</b> and <b>SI</b> bits using <b>I2C0CONCLR</b> to transmit data.</li>
<li><strong>5.3</strong> When data has been sent and an <b>ACK</b> has been received <b>I2C0STAT</b> will be <span class="doc_ref">0x28</span> . To keep transmitting data goto step 5.2.
<ol class="list_basic">
<li><strong>5.3.1</strong> If slave sends a <b>Not Acknowledge/NACK(AA=1)</b> bit the Status code will be <span class="doc_ref">0x38</span> and a <b>STOP</b> condition will be transmitted.</li>
<li><strong>5.3.2</strong> Or you can stop the transmission with a <b>STOP</b> condition by setting <b>STO</b> bit in <b>I2C0CONSET</b>.</li>
</ol>
</li>
</ol>
</li>
</ol>
</div>
<div class="special sp_blue noteinfo">The <b>STO</b> bit in <b>I2C0CONSET</b> auto clears after the <b>STOP</b> condition is sent. So if you want to start the communication again you will need to wait till the <b>STO</b> bit clears. In code implementations not using ISR(like in our case) this is done by monitoring the <b>STO</b> bit. After <b>STO</b> bit is reset you send a <b>START</b> condition to transmit/receive data again.</div>
<h3>Procedure for I2C communication in Master Receiver Mode:</h3>
<div class="highlight">
<ol class="list_basic">
<li><strong>1.</strong> Same as in Master Transmitter Mode.</li>
<li><strong>2.</strong> Same as in Master Transmitter Mode.</li>
<li><strong>3.</strong> Here we load <b>I2C0DAT</b> with <b>Slave Address + Read bit (SLA+R)</b>. Clear <b>SI</b> to continue.</li>
<li><strong>4.</strong> After <b>SLA+R</b> has been sent, an <b>ACK(AA=0)</b> bit is received and <b>SI</b> bit is set again. At this point status code(s) <span class="doc_ref">0x38</span> , <span class="doc_ref">0x40</span> &amp; <span class="doc_ref">0x48</span> are possible.</li>
<li><strong>5.</strong> Now depending on the Status proceed further.
<ol>
<li><strong>5.1</strong> In normal situation <b>I2C0STAT</b> will be <span class="doc_ref">0x40</span> which means <b>SLA+R</b> has been Transmitted and <b>ACK</b> has been Received.</li>
<li><strong>5.2</strong> Now to receive data from Slave, set the <b>AA</b> bit and clear <b>SI</b> bit.</li>
<li><strong>5.3</strong> If Data has been sent and <b>ACK(AA=0)</b> has been returned the status code will be <span class="doc_ref">0x50</span>.
<ol>
<li><strong>5.3.1</strong> To keep on receiving data, keep on setting <b>AA</b> bit everytime.</li>
<li><strong>5.3.2</strong> To stop receiving data, send a <b>NOT ACK(AA=1)</b> by clearing <b>AA</b> bit after which status code will be <span class="doc_ref">0x58</span> which means Data has been send and <b>NOT ACK(A=1)</b> has been returned.</li>
</ol>
</li>
</ol>
</li>
</ol>
</div>
<h2 class="shead">LPC2148 I2C ARM7 Setup &amp; Programming</h2>
<div class="special sp_blue noteinfo"><strong>Implementation Note:</strong> We can either implement the I2C code using an ISR which handles every thing =or= we can implement a state driven code in which we do not use ISR but instead use functions to handle the events by waiting for <b>SI</b> bit to be set and then clearing it to trigger next action. We can also implement it using a mix of ISR and event functions.</p>
<p>In our case we will NOT implement any ISR but will be implement a state driven code. In my opinion this makes it easier to code and understand.</p></div>
<p>In order to communicate with any I2C device we need to set the <b>I2C clock frequency</b>. The I2C Clock/bit frequency is set using 2 registers: <b>I2CxSCLH and I2CxSCLL</b>. I2CxSCLH defines the number of PCLK(Peripheral Clock) cycles for the I2C Clock(SCL) High time while I2CxSCLL defines the number of PCLK cycles for the I2C Clock(SCL) Low time. It is given a simple formula as given below :</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/i2c/i2c_clock_speed_formula.png" width="400px" height="70px" /></p>
<p>Note that LPC214x ARM7 Microcontrollers support a maximum I2C frequency of 400KHz. In our case we will use a frequency of 100KHz. Given our PCLK is running at 60Mhz we need to set <b>I2C0SCLH</b> = <b>I2C0SCLL = 300 for IC20 Module</b>.</p>
<p class="compact_header">Now lets, define some bits which will help us setup the <b>I2C0ONCLR</b> and <b>I2C0CONSET</b> registers to initialize the I2C0 block before we can use it.</p>
<pre><code class="language-cpp">
#define I2EN (1&lt;&lt;6) //Enable/Disable bit
#define STA  (1&lt;&lt;5) //Start Set/Clear bit
#define STO  (1&lt;&lt;4) //Stop bit
#define SI   (1&lt;&lt;3) //Serial Interrupt Flag Clear bit
#define AA   (1&lt;&lt;2) //Assert Acknowledge Set/Clear bit
</code></pre>
<p>Now, we will define some basic &#8216;building block&#8217; functions which will help us in programming the I2C module without complicating it too much.</p>
<p class="compact_header"><strong>1. I2C initialization function &#8211; <span class="code_var">I2C0Init()</span>: </strong> It first select the I2C function of the respective pins. Then it configures the I2C bit rate(I2C bus clock), clears <span class="doc_ref">I2C0CONCLR</span> register and then finally enables the I2C0 block using <span class="doc_ref">I2C0CONSET</span> register.</p>
<pre><code class="language-cpp">
void I2C0Init(void) 
{
	PINSEL0 |= (0&lt;&lt;7)|(1&lt;&lt;6)|(0&lt;&lt;5)|(1&lt;&lt;4); //Select SCL0(P0.2) and SDA0(P0.3)
	I2C0SCLL = 300;
	I2C0SCLH = 300; //I2C0 @ 100Khz, given PCLK @ 60Mhz
	I2C0CONCLR = STA | STO | SI | AA; //Clear these bits
	I2C0CONSET = I2EN; //Enable I2C0
	//After this we are ready to communicate with any other device connected to the same bus.
}
</code></pre>
<p class="compact_header"><strong>2. SI wait function &#8211; <span class="code_var">I2C0WaitForSI(void)</span>:</strong> This functions waits for the SI bit to be set after any action taken by the I2C hardware. When this bit is set it indicates that the action taken by I2C module has been completed i.e. a new event has occurred which in turn changes the status code in <span class="doc_ref">I2C0STAT</span>.</p>
<pre><code class="language-cpp">
bool I2C0WaitForSI(void) //Wait till I2C0 block sets SI
{
	int timeout = 0;
	while ( !(I2C0CONSET &amp; SI) ) //Wait till SI bit is set. This is important!
	{
		timeout++;
		if (timeout &gt; 10000) return false; //In case we have some error on bus
	}
	return return; //SI has been set
}
</code></pre>
<p class="compact_header"><strong>3. Send START/Repeat-START function &#8211; <span class="code_var">I2C0SendStart()</span>:</strong> This functions sends a START condition as soon as the bus becomes free =or= sends a Repeat START is already in master mode and then waits till SI bit set.</p>
<pre><code class="language-cpp">
void I2C0SendStart(void)
{
	I2C0CONCLR = STA | STO | SI | AA; //Clear everything
	I2C0CONSET = STA; //Set start bit to send a start condition
	I2C0WaitForSI(); //Wait till the SI bit is set
}
</code></pre>
<p class="compact_header"><strong>4. Send STOP Function &#8211; <span class="code_var">I2C0SendStop()</span>:</strong> This function sends a STOP condition and then waits for the STO bit in <span class="doc_ref">I2C0CONSET</span> to auto clear which indicates was sent on the bus.</p>
<pre><code class="language-cpp">
void I2C0SendStop(void)
{
	int timeout = 0;
	I2C0CONSET = STO ; //Set stop bit to send a stop condition
	I2C0CONCLR = SI;
	while (I2C0CONSET &amp; STO) //Wait till STOP is send. This is important!
	{
		timeout++;
		if (timeout &gt; 10000) //In case we have some error on bus
		{
			printf("STOP timeout!\n");
			return;
		}
	}
}
</code></pre>
<p class="compact_header"><strong>5. I2C Transmit Byte function &#8211; <span class="code_var">I2C0TX_Byte(unsigned char)</span>:</strong> This function sends a byte on the I2C bus and then waits for SI to be set.</p>
<pre><code class="language-cpp">
void I2C0TX_Byte(unsigned char data)
{
 	I2C0DAT = data;
	I2C0CONCLR = STA | STO | SI; //Clear These to TX data
	I2C0WaitForSI(); //wait till TX is finished
}
</code></pre>
<p class="compact_header"><strong>6. I2C Receive Byte function &#8211; <span class="code_var">I2C0RX_Byte(bool)</span>:</strong></p>
<pre><code class="language-cpp">
unsigned char I2C0RX_Byte(bool isLast)
{
	if(isLast) I2C0CONCLR = AA; //Send NACK to stop; I2C block will send a STOP automatically, so no need to send STOP thereafter.
	else 	     I2C0CONSET = AA; //Send ACK to continue
	I2C0CONCLR = SI; //Clear SI to Start RX
	I2C0WaitForSI(); //wait till RX is finished
	return I2C0DAT;
}
</code></pre>
<p>Now armed with the I2C communication building block functions we can interface LPC2148 in Master Transmitter or Master Receiver mode with any slave device.</p>
<h2 class="shead">LPC2148 I2C Example: Interfacing 24LC64 EEPROM</h2>
<p>Now, lets do an I2C programming example where we Write and Read to an EEPROM. I&#8217;ll be using 24LC64 for this example. Make sure you refer its datasheet- just in case 😉</p>
<p>Here is the connection diagram between LPC2148 Microcontroller and EEPROM:</p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/i2c/i2c_24lc64_interface_schematic_arm7_lpc2148.png" alt="I2C 24LC64 EEPROM Interface schematic arm7 lpc2148" width="389px" height="306px" /></p>
<p>In order to Read and Write to EEPROM we will define 1 macro and 2 functions as follows:</p>
<p class="compact_header"><strong>1. <span class="code_var">checkStatus(int)</span> Macro function:</strong> This functions checks if the current value in <span class="doc_ref">I2C0STAT</span> is same as argument supplied to. If not it will send STOP condition and make the calling function return false which indicates an error condition.</p>
<pre><code class="language-cpp">
#define checkStatus(statusCode) \
if(I2C0STAT!=statusCode) \
{ \
	printf("Error! Expected status code: %i(decimal), Got: %i(decimal)\n",statusCode,I2C0STAT); \
	I2C0SendStop(); return false; \
}
</code></pre>
<p class="compact_header"><strong>2. <span class="code_var">I2C0WriteEEPROM(&#8230;)</span> function:</strong> This writes data from buffer to the I2C slave device. It takes 3 arguments : <span class="code_var">startDataAddress</span> &#8211; the starting address inside EEPROM where the writes must begin from , <span class="code_var">data</span> &#8211; a pointer to data buffer which contains data to be written to eeprom, <span class="code_var">length</span> &#8211; length of the data buffer.</p>
<pre><code class="language-cpp">
/*(C) Umang Gajera | Power_user_EX - www.ocfreaks.com 2011-17. LPC2148 I2C Tutorial
More Embedded tutorials @ www.ocfreaks.com/cat/embedded*/
bool I2C0WriteEEPROM(unsigned int startDataAddress, unsigned char *data, int length)
{	
	for(int count=0 ; count&lt; length ; count++ ) { I2C0SendStart(); //Send START on the Bus to Enter Master Mode checkStatus(0x08); //START sent I2C0TX_Byte(I2CSlaveAddr &amp; 0xFE); //Send SlaveAddress + 0 to indicate a write. checkStatus(0x18);//SLA+W sent and ack recevied I2C0TX_Byte((startDataAddress &amp; 0xFF00)&gt;&gt;8); //Send Word Address High byte first. (24xx64 needs 2 word address bytes)
		checkStatus(0x28); //High byte has been sent and ACK recevied

		I2C0TX_Byte(startDataAddress &amp; 0xFF); //Now send the Low byte of word Address
		checkStatus(0x28); //Low byte has been sent and ACK recevied

		I2C0TX_Byte(data[count]); //Finally send the data byte.
		checkStatus(0x28); //Data Byte has been sent and ACK recevied

		startDataAddress++; //Increment to next address
		I2C0SendStop(); //Send STOP since we are done.
		
		//Now initiate write acknowledge polling as given on page 9 of 24LC64's datasheet
		const int retryTimeout = 100;
		for(int i=0; i &lt; retryTimeout; i++)
		{
			I2C0SendStart();
			checkStatus(0x08);
			I2C0TX_Byte(I2CSlaveAddr &amp; 0xFE);
			if(I2C0STAT == 0x18) //ACK recieved which indicates completion of write cycle
			{
				I2C0SendStop();
				//printf("Write Completed! for data = %c\n",data[count]);
				goto OUT;
			}
			I2C0SendStop();
		}
		I2C0SendStop();
		printf("Warning: Write Poll Timeout! for data = %c\n",data[count]);

		OUT:; //Get us out of the loop.
	}
	return true;
}
</code></pre>
<p class="compact_header"><strong>3. <span class="code_var">I2C0ReadEEPROM(&#8230;)</span> function:</strong> This reads data to buffer from the I2C slave device. Arguments are similar to those of <span class="code_var">I2C0WriteEEPROM();</span></p>
<pre><code class="language-cpp">
/*(C) Umang Gajera | Power_user_EX - www.ocfreaks.com 2011-17. LPC2148 I2C Tutorial
More Embedded tutorials @ www.ocfreaks.com/cat/embedded*/
bool I2C0ReadEEPROM(unsigned int startDataAddress, unsigned char *data , int length)
{ 
	unsigned char RXData = 0;
	for(int i=0; i &lt; length;i++) { I2C0SendStart(); //Send START on the Bus to Enter Master Mode checkStatus(0x08); //START sent I2C0TX_Byte(I2CSlaveAddr &amp; 0xFE); //Send SlaveAddress + 0 to indicate a write. checkStatus(0x18);//SLA+W sent and ACK recevied I2C0TX_Byte((startDataAddress &amp; 0xFF00)&gt;&gt;8); //Send Word Address High byte first. (24xx64 needs 2 word address bytes)
		checkStatus(0x28); //High byte has been sent and ACK recevied

		I2C0TX_Byte(startDataAddress &amp; 0xFF); //Now send the Low byte of word Address
		checkStatus(0x28); //Low byte has been sent and ACK recevied

		startDataAddress++; //Increment to next address		

		I2C0SendStart(); //Send Repeat START, since we are already in Master mode
		checkStatus(0x10); //Repeat START sent

		I2C0TX_Byte(I2CSlaveAddr | 0x01); //This makes SLA-RW bit to 1 which indicates read.
		checkStatus(0x40); //SLA-R has been Transmitted and ACK received.

		if(i != length-1)	RXData = I2C0RX_Byte(false); //Send NACK for last byte to indicate we want to stop
		else RXData = I2C0RX_Byte(true); //Send ACK for byte other than last byte to indicate we want to continue.
		
		data[count++] = RXData; //Write recieved data to buffer
		printf("Data='%c' ",RXData);
		
	}
	return true;
}
</code></pre>
<p class="compact_header">Finally here is the code for <span class="code_var">main()</span> function:</p>
<div class="special special sp_blue noteinfo">Note that I have used UART0 to send the <span class="code_var">printf()</span> output to serial console hence in order to view the output you must connect the UART0 pins on your LPC214x to your computer/laptop using a suitable USB to Serial convertor (I am using FTDI(FT232) based USB to Serial module). The CPU clock and PCLK are both configured @ 60Mhz using <span class="code_var">initClocks()</span> function. Source code for these functions including <span class="code_var">initUART0()</span> are present in the header file <span class="code_var">ocfreaks_sh.h</span> which is the support header file for all my LPC2148 tutorials.</div>
<pre><code class="language-cpp">
/*(C) Umang Gajera | Power_user_EX - www.ocfreaks.com 2011-17. LPC2148 I2C Tutorial
More Embedded tutorials @ www.ocfreaks.com/cat/embedded*/
#include &lt;lpc214x.h&gt;
#include "ocfreaks_sh.h" //This contains code for UART, printf(), initClocks()
#include &lt;stdint.h&gt;

#define I2EN (1&lt;&lt;6) //Enable/Disable bit
#define STA  (1&lt;&lt;5) //Start Set/Clear bit
#define STO  (1&lt;&lt;4) //Stop bit
#define SI   (1&lt;&lt;3) //Serial Interrupt Flag Clear bit
#define AA   (1&lt;&lt;2) //Assert Acknowledge Set/Clear bit

void I2C0Init(void);
bool I2C0WaitForSI(void);
void I2C0SendStart(void);
void I2C0SendStop(void);
void I2C0TX_Byte(unsigned char data);
unsigned char I2C0RX_Byte(int isLast);

bool I2C0ReadEEPROM(unsigned int addresss, unsigned char *data, int length);
bool I2C0WriteEEPROM(unsigned int address, unsigned char *data, int length);

unsigned char I2CSlaveAddr = 0xA0; //Address of EEPROM (A2=0,A1=0,A0=0)

int main(void)
{
	initClocks();
	initUART0();
	I2C0Init();

	printf("(C) ocfreaks.com - LPC2148 I2C tutorial.\nExample: Interfacing 24LC64 EEPROM.\n\n");
	
	#define BUFF_SIZE 30 //29 Characters + 1 Termination Character = 30 Total
	unsigned char bufferWrite[BUFF_SIZE] = "www.ocfreaks.com I2C Tutorial"; 
	unsigned char bufferRead[BUFF_SIZE] = {0};

	//Write data from bufferWrite to EEPROM
	printf("Writing data to EEPROM... \n");
	if( !I2C0WriteEEPROM(0,bufferWrite,BUFF_SIZE) )
	{
		printf("Writing Error!\n");
	}
	printf("Write finished!\n\n");

	//Read EEPROM Data into bufferRead
	printf("Reading data from EEPROM: \n");
	if( !I2C0ReadEEPROM(0,bufferRead,BUFF_SIZE) )
	{
		printf("Reading Error!\n");
	}

	printf("\n\nData bytes recevied are as follows:\n");
	printf("\"");
	for(int i=0; i &lt; BUFF_SIZE; i++)
	{
		printf("%c",(char)bufferRead[i]);
	}
	printf("\"");
	printf("\n\nDone!\n");
	
	while(1); //Loop infinitely.
	return 0; //Normally, this won't execute.
}
</code></pre>
<p><b>Serial Output:</b></p>
<p><img decoding="async" class="aligncenter" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/i2c/lpc2148_i2c_eeprom_example_screenshot.png" alt="I2C 24LC64 EEPROM Interface lpc214x example output screenshot" width="589px" height="322px" /></p>
<div class="highlight"><strong>Download Project Source / I2C Sample code @</strong> <a href="https://www.ocfreaks.com/imgs/_downloads/lpc2148/OCFreaks.com_LPC214x_I2C_Tutorial.zip">LPC214x I2C Tutorial.zip</a> [Successfully tested on Keil UV5.23]</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&amp;psc=1&amp;refRID=1ATG383W1SGQG4FNZN0R&amp;linkCode=li2&amp;tag=o0cd4-21&amp;linkId=619cbbc7eb302c8ca4af6877b8c1152c" target="_blank" rel="noopener noreferrer"><img decoding="async" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B01LX5AN08&amp;Format=_SL160_&amp;ID=AsinImage&amp;MarketPlace=IN&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=o0cd4-21" border="0" /></a><img decoding="async" style="border: none !important; margin: 0px !important;" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&amp;l=li2&amp;o=31&amp;a=B01LX5AN08" alt="" width="1" height="1" border="0" /><a href="https://www.amazon.in/Development-Board-LPC2148-USB-FT232-Driver-memory/dp/B00T04NLYE/ref=as_li_ss_il?ie=UTF8&amp;qid=1491577410&amp;sr=8-8&amp;keywords=lpc2148&amp;linkCode=li2&amp;tag=o0cd4-21&amp;linkId=8628d2ee5db112ddbaab7aaedc6ec22f" target="_blank" rel="noopener noreferrer"><img decoding="async" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B00T04NLYE&amp;Format=_SL160_&amp;ID=AsinImage&amp;MarketPlace=IN&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=o0cd4-21" border="0" /></a><img decoding="async" style="border: none !important; margin: 0px !important;" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&amp;l=li2&amp;o=31&amp;a=B00T04NLYE" alt="" width="1" height="1" border="0" /><a href="https://www.amazon.in/LPC2148-Development-Board-Standard-Package/dp/B019QTWRVU/ref=as_li_ss_il?ie=UTF8&amp;qid=1491577410&amp;sr=8-1&amp;keywords=lpc2148&amp;linkCode=li2&amp;tag=o0cd4-21&amp;linkId=1f88a47711dbb1f232ece64e5a4a79d3" target="_blank" rel="noopener noreferrer"><img decoding="async" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B019QTWRVU&amp;Format=_SL160_&amp;ID=AsinImage&amp;MarketPlace=IN&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=o0cd4-21" border="0" /></a><img decoding="async" style="border: none !important; margin: 0px !important;" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&amp;l=li2&amp;o=31&amp;a=B019QTWRVU" alt="" width="1" height="1" border="0" /><a href="https://www.amazon.in/Explore-Embedded-ARM7-LPC2148-Dev/dp/B018XJ0F5Y/ref=as_li_ss_il?ie=UTF8&amp;qid=1491577410&amp;sr=8-5&amp;keywords=lpc2148&amp;linkCode=li2&amp;tag=o0cd4-21&amp;linkId=bc5b00fa1663b938453ff3d765a3e165" target="_blank" rel="noopener noreferrer"><img decoding="async" src="//ws-in.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B018XJ0F5Y&amp;Format=_SL160_&amp;ID=AsinImage&amp;MarketPlace=IN&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=o0cd4-21" border="0" /></a><img loading="lazy" decoding="async" style="border: none !important; margin: 0px !important;" src="https://ir-in.amazon-adsystem.com/e/ir?t=o0cd4-21&amp;l=li2&amp;o=31&amp;a=B018XJ0F5Y" alt="" width="1" height="1" border="0" /></p>
<p>The post <a href="https://www.ocfreaks.com/lpc2148-i2c-programming-tutorial/">LPC2148 I2C Programming Tutorial</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/lpc2148-i2c-programming-tutorial/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2752</post-id>	</item>
		<item>
		<title>Create Keil uVision5 Project for LPC2148 ARM7 MCU</title>
		<link>https://www.ocfreaks.com/create-keil-uvision5-project-lpc2148-arm7-mcu/</link>
					<comments>https://www.ocfreaks.com/create-keil-uvision5-project-lpc2148-arm7-mcu/?noamp=mobile#respond</comments>
		
		<dc:creator><![CDATA[Umang Gajera]]></dc:creator>
		<pubDate>Thu, 30 Mar 2017 13:19:07 +0000</pubDate>
				<category><![CDATA[Embedded]]></category>
		<category><![CDATA[LPC2148 Tutorials]]></category>
		<category><![CDATA[lpc2148]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">http://www.ocfreaks.com/?p=2705</guid>

					<description><![CDATA[<p>Many of us who are into embedded programming want to migrate or use the latest version of KEIL MDK which is uVision5 at the time of this post. In this tutorial we see how to create LPC214x ARM7 projects in KEIL uVision5. Time is whizzing by fast, and along the years we have programmed lpc2100 [&#8230;]</p>
<p>The post <a href="https://www.ocfreaks.com/create-keil-uvision5-project-lpc2148-arm7-mcu/">Create Keil uVision5 Project for LPC2148 ARM7 MCU</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Many of us who are into embedded programming want to migrate or use the latest version of KEIL MDK which is uVision5 at the time of this post. In this tutorial we see how to create LPC214x ARM7 projects in KEIL uVision5. Time is whizzing by fast, and along the years we have programmed lpc2100 ARM7 devices (lpc2148,etc..) using Keil uVision 2, uv3 then uv4 and now <strong>Keil uVision 5</strong>. But many times upgrading your IDE or toolchain might bring in the need for some necessary tweaks or &#8216;change of settings&#8217; so that your code compiles and executes properly as it use to on previous IDEs. Last time we saw how to fix the interrupt issue in Keil uVision4 for ARM7 MCUs where interrupts won&#8217;t trigger even if your code was correct @ <a href="https://www.ocfreaks.com/lpc2148-interrupt-problem-issue-fix/">Keil uVision ARM7 Interrupt Problem and Issue fix</a>. This time we take a look on <strong>how to build your LPC2148 ARM7 projects properly in Keil uVision5</strong>. ARM9 &#038; ARM7 based Microcontrollers like LPC2148, and other LPC3000 series MCUs etc.. are now &#8220;legacy stuff&#8221; in Keil uVision5 and is not available in default installation. To enable support for <strong>ARM7 LPC2148 and similar devices</strong> you need to install <strong>Legacy Support pack for ARM7, ARM9 &#038; Cortex-R</strong> after which you can create projects for the legacy MCU without any fuzz. This will also enable backwards compatibility for projects made using <strong>Keil MDK v4</strong>. Just follow the steps mentioned below to create a new project in Keil MDK uv5 or if your project is not working properly:</p>
<p>1. Download latest Keil MDK uVision5 from <a href="https://www.keil.com/demo/eval/arm.htm" target="_blank">Keil&#8217;s website</a>.<br />
2. Install Keil MDK uv5 to default path.<br />
3. Download Legacy Support pack for ARM7, ARM9 &#038; Cortex-R based Microcontroller for your Keil Version from <a href="http://www2.keil.com/mdk5/legacy" target="_blank"> MDK v4 Legacy Support page</a>.<br />
4. Next install the legacy pack to default path. You are ready to create projects for LPC214x, LPC3000 series Microcontroller in uv5 now.<br />
5. Open the Keil IDE, under main menu goto &#8220;Project->New uVision Project&#8230;&#8221; and a window prompt will open asking to save the new project. Type your desired project name and save.</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/keil_mdk_uv5/0_new_keil_project.png" class="aligncenter" alt="" /></p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/keil_mdk_uv5/0_save_new_project_keil.png" class="aligncenter" alt="" /></p>
<p>6. After that, a new window will appear as shown below. Click on the drop-down menu which reads &#8220;Software Packs&#8221;. Select &#8220;Legacy Device Database [no RTE]&#8221;</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/keil_mdk_uv5/1_select_legacy_device_keil.png" class="aligncenter" alt="" /></p>
<p>7. Now in the search box below type: &#8220;lpc2148&#8221; or the MCU which you want to create project on. In the Tree below the search box select &#8220;LPC2148&#8221; under &#8220;NXP&#8221; and click OK.</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/keil_mdk_uv5/2_select_device_target_keil.png" class="aligncenter" alt="" /></p>
<p>8. Next the following dialog box will appear. Click &#8220;Yes&#8221; to proceed.</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/keil_mdk_uv5/3_uv_prompt.png" class="aligncenter" alt="" /></p>
<p>9. Now click on &#8220;Options for Target&#8221; button as shown below:</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/keil_mdk_uv5/4_click_options_button_keil.png" class="aligncenter" alt="" /></p>
<p>10. Next the following window will appear and now click the &#8220;Linker&#8221; tab:</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/keil_mdk_uv5/5_options_for_target_keil.png" class="aligncenter" alt="" /></p>
<p>11. Under the Linker Tab put a tick on the checkbox option which says &#8220;Use Memory Layout from Target Dialog&#8221; and click OK.</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/keil_mdk_uv5/6_options_for_target_keil_linker.png" class="aligncenter" alt="" /></p>
<div class="special sp_blue notestar">For some guys including me the compiled code doesn&#8217;t not work as expected. By checking the above option your program must run as usual when you flash it to your microcontroller. Also in case if interrupts are not working in your keil this will most probably solve the issue as mentioned here : <a href="https://www.ocfreaks.com/lpc2148-interrupt-problem-issue-fix/">Keil uVision ARM7 Interrupt Problem and Issue fix</a></div>
<p>12. Now under the &#8220;Output&#8221; tab click on the check box that reads &#8220;Create HEX File&#8221;.</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/keil_mdk_uv5/7_create_hex_file_keil.png" class="aligncenter" alt="" /></p>
<p>13. Now, in the source navigation pane on the left area, right click on &#8220;Source Group 1&#8221; and select &#8220;Add New Item to Group &#8216;Source Group 1&#8242;&#8221;.</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/keil_mdk_uv5/8_add_new_item_keil.png" class="aligncenter" alt="" /></p>
<p>14. A new window will pop-up to add an item as shown below. Select C++ File (.cpp), then enter the name of the file in the text box to the left of &#8220;Name:&#8221; and click &#8220;Add&#8221;.</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/keil_mdk_uv5/9_create_new_cpp_source_keil.png" class="aligncenter" alt="" /></p>
<p>15. Now you can write your program in the editor. To compile your program Press &#8220;F7&#8221; key or in the main menu goto &#8220;Project->Build Target&#8221;. To check for any compilation errors you can have a look at the build output at the bottom of main window. A screenshot of the Keil uVision 5 is given below. The top red box shows the Source Tree Navigation and the bottom red box shows the build output.</p>
<p><img decoding="async" src="https://www.ocfreaks.com/imgs/lpc2148-tutorial/keil_mdk_uv5/10_keil_window.png" class="aligncenter" alt="" /></p>
<p>After this you are now ready to create your own <strong>LPC2148 ARM7 projects in Keil uVision5</strong>. If you are facing any problems/issues creating or compiling projects for lpc2148 in Keil uVision4 or uVision5 let me know by commenting to this post below. Best of Luck!</p>
<p>The post <a href="https://www.ocfreaks.com/create-keil-uvision5-project-lpc2148-arm7-mcu/">Create Keil uVision5 Project for LPC2148 ARM7 MCU</a> appeared first on <a href="https://www.ocfreaks.com">OCFreaks!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ocfreaks.com/create-keil-uvision5-project-lpc2148-arm7-mcu/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2705</post-id>	</item>
		<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 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/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>
	</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-05 23:39:28 by W3 Total Cache
-->