<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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" version="2.0">

<channel>
	<title>ermicroblog</title>
	
	<link>http://www.ermicro.com/blog</link>
	<description>Microcontrollers and Electronics Project Blog</description>
	<pubDate>Sat, 04 Sep 2010 06:12:08 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.3</generator>
	<language>en</language>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/Ermicroblog" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="ermicroblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Integrating Wiznet W5100, WIZ811MJ network module with Atmel AVR Microcontroller</title>
		<link>http://www.ermicro.com/blog/?p=1773</link>
		<comments>http://www.ermicro.com/blog/?p=1773#comments</comments>
		<pubDate>Sat, 31 Jul 2010 08:59:56 +0000</pubDate>
		<dc:creator>rwb</dc:creator>
		
		<category><![CDATA[Microcontroller]]></category>

		<category><![CDATA[ADC]]></category>

		<category><![CDATA[ARDUINO]]></category>

		<category><![CDATA[ATMEGA328]]></category>

		<category><![CDATA[AVR]]></category>

		<category><![CDATA[ethernet]]></category>

		<category><![CDATA[SPI]]></category>

		<category><![CDATA[W5100]]></category>

		<category><![CDATA[WIZ811MJ]]></category>

		<category><![CDATA[wiznet]]></category>

		<guid isPermaLink="false">http://www.ermicro.com/blog/?p=1773</guid>
		<description><![CDATA[The rapid penetration of the internet networks into many of today&#8217;s modern homes and personal gadgets (e.g. smart phone and smart pads) opening a tremendous useful and interesting embedded system application that could be integrated into our house or known as the intelligent house. For example by putting a small embedded system web server in [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/exobcBpjp59XPKQBGnF0nGf_bDo/0/da"><img src="http://feedads.g.doubleclick.net/~a/exobcBpjp59XPKQBGnF0nGf_bDo/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/exobcBpjp59XPKQBGnF0nGf_bDo/1/da"><img src="http://feedads.g.doubleclick.net/~a/exobcBpjp59XPKQBGnF0nGf_bDo/1/di" border="0" ismap="true"></img></a></p><p>The rapid penetration of the internet networks into many of today&#8217;s modern homes and personal gadgets (e.g. smart phone and smart pads) opening a tremendous useful and interesting embedded system application that could be integrated into our house or known as the intelligent house. For example by putting a small embedded system web server in our house, we could easily monitor such as alarm, temperature or even turn on/off the lamp or the garden&#8217;s water sprinkle; eventually from any remote location through the wireless personal gadget; Or perhaps you just want to impress your relative or friend with a very accurate digital clock which automatically synchronized the time through the Network Time Protocol (NTP) over the internet at your home or office.<span id="more-1773"></span></p>
<p>All of these interesting and challenging embedded system applications could be accomplished by integrating the Ethernet protocol which is formed the basic of the communication protocol used in the internet into the embedded system. Currently there are several approaches for this solution but basically they could be divided into two categories wired e.g. Wiznet W5100, W5300, Microchip ENC28J60 and wireless such as the Microchip ZG2100MC Wi-Fi module (recently is acquired by Microchip from the Zero-G Wireless on Jan 11, 2010).</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_01.jpg"><img class="alignnone size-full wp-image-1774" title="ethernet_01" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_01.jpg" alt="" width="583" height="404" /></a></p>
<p>On this tutorial we are going to build the embedded web server using the Wiznet WIZ811MJ network module which is based on the Wiznet well known W5100 TCP/IP hardwired chip that include the ethernet controller physical layer (PHY). The WIZ811MJ network module comes with the Wiznet W5100 chip, MAG-JACK (RJ45) together with the glued logic needed to communicate with the microcontroller through the SPI or bus interface.</p>
<p>The reason I choose the Wiznet 5100 based chip on this tutorial because this chip has the TCP/IP hardwired on it; therefore it will make developing the TCP/IP protocol stack based application much easier and could be implemented on the small RAM size microcontroller class compared to the firmware TCP/IP protocols stack based implementation approach (you don&#8217;t have to know everything about the TCP/IP protocol stack in order to be able to use this chip). The other reason is because the Wiznet 5100 chip has been around for a few years in the market and has already being matured. This chip is used in many commercial applications such as the Arduino framework on their standard Arduino Ethernet shield as shown on this following picture.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_02.jpg"><img class="alignnone size-full wp-image-1775" title="ethernet_02" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_02.jpg" alt="" width="583" height="433" /></a></p>
<p>Ok, now lets list down the necessary electronics components and supported software for this tutorial and make sure you have the AVR ATMega328 microcontroller datasheet near you:</p>
<ul>
<li>Resistors: 10K Ohm (1), 1K Ohm (1) and 470 Ohm (2)</li>
<li>Capacitors: 10uF/16v (2) and 0.1uF (2)</li>
<li>LEDS: 3 mm Blue LED (2)</li>
<li>Transistor: 2N3904 (1)</li>
<li>Voltage Regulator IC: LM1086 - 3.3 Volt</li>
<li>One momentary push button</li>
<li>One 30&#215;60 mm Prototype board</li>
<li>Two 10 pins male double header and 5 pins male single header</li>
<li>One 2 pins male single polarized header</li>
<li><a title="AVRJazz Mega328 Development Board" href="http://www.ermicro.com/blog/?p=1" target="_blank">AVRJazz Mega328 board</a> and <a title="Powering Your Microcontroller’s Base Project" href="http://www.ermicro.com/blog/?p=820" target="_blank">JazzMate 5 Volt voltage regulator board</a> from <a title="Microcontroller and Electronics parts e-shop " href="http://www.ermicro.com" target="_blank">ermicro</a></li>
<li>Wiznet WIZ811MJ Network Module</li>
<li>Atmel AVR Studio version 4.17 IDE</li>
<li>WinAVR AVR-GCC 4.3.2; avr-libc 1.6.6 (WinAVR 20090313)</li>
<li>Reference Document: W5100 Datasheet, WIZ811MJ Datasheet, W5100 Porting Guide, Atmel AVR ATMega328 Datasheet.</li>
</ul>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_00.jpg"><img class="alignnone size-full wp-image-1776" title="ethernet_00" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_00.jpg" alt="" width="576" height="399" /></a></p>
<p><a title="Integrating Wiznet W5100, WIZ811MJ network module with Atmel AVR Microcontroller by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4870753809/"><img src="http://farm5.static.flickr.com/4079/4870753809_3636d97a19.jpg" alt="Integrating Wiznet W5100, WIZ811MJ network module with Atmel AVR Microcontroller" width="575" height="427" /></a></p>
<p><strong>The Wiznet W5100 Hardwired TCP/IP Protocol Chip</strong></p>
<p>Basically the Wiznet W5100 implements a full-featured of standard IEEE 802.3 (Ethernet physical and data link layer) and powerful TCP/IP stack inside the chip; this make the Wiznet W5100 chip is suitable choice for integrating the embedded system into the internet.  Programming the Wiznet W5100 chip is also easier as we just need to write and read to and from the W5100 internal registers in order to use the build-in TCP/IP protocol features.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_03.jpg"><img class="alignnone size-full wp-image-1779" title="ethernet_03" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_03.jpg" alt="" width="577" height="395" /></a></p>
<p>The Wiznet W5100 chip come with three method of controlling its internal registers; this first two is to use the parallel direct or indirect bus, the last one is to use a well known embedded serial data transfer known as the SPI (serial peripheral interface), on this tutorial we are going to use the SPI to control the Wiznet W5100 chip. You could read more about SPI on my previous posted blog <a title="Using Serial Peripheral Interface (SPI) Master and Slave with Atmel AVR Microcontroller" href="http://www.ermicro.com/blog/?p=1050" target="_blank">Using Serial Peripheral Interface (SPI) Master and Slave with Atmel AVR Microcontroller</a>. The basic SPI connection between the Wiznet WIZ811MJ network module and Atmel AVR ATMega328 microcontroller is shown on this following picture.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_04.jpg"><img class="alignnone size-full wp-image-1783" title="ethernet_04" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_04.jpg" alt="" width="578" height="449" /></a></p>
<p>The Wiznet W5100 will act as the SPI slave device controlled by Atmel AVR ATMega328 microcontroller as the SPI Master. The SPI protocol need at least four signal i.e. MOSI (Master Out Serial In), MISO (Master In Serial Out), SCK (signal clock provided by the master) and CS (the SPI slave chip select). Although the AVR ATMega328 microcontroller support all the SPI modes (i.e. 0,1,2 and 3) but the Wiznet W5100 chip support the most SPI common modes (mode 0 and mode 3) where it will sample a data on rising edge clock and outputting on the falling edge clock. The W5100 chip also provides the interrupt pin, but on this tutorial we don&#8217;t use the interrupt feature, instead we use a pooling method to control the W5100 operation.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_05.jpg"><img class="alignnone size-full wp-image-1785" title="ethernet_05" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_05.jpg" alt="" width="576" height="354" /></a></p>
<p>In order to understand of how we control the Wiznet W5100 as the SPI slave device, we are going to create the W5100 initialization program; so it will response to the simple &#8220;<strong>ping</strong>&#8221; network command (ICMP protocol). The following is the complete C code called &#8220;<strong>wiznetping.c</strong>&#8221; for initializing the Wiznet W5100 chip.</p>
<pre>/*****************************************************************************
//  File Name    : wiznetping.c
//  Version      : 1.0
//  Description  : Wiznet W5100
//  Author       : RWB
//  Target       : AVRJazz Mega168 Board
//  Compiler     : AVR-GCC 4.3.2; avr-libc 1.6.6 (WinAVR 20090313)
//  IDE          : Atmel AVR Studio 4.17
//  Programmer   : AVRJazz Mega168 STK500 v2.0 Bootloader
//               : AVR Visual Studio 4.17, STK500 programmer
//  Last Updated : 01 July 2010
*****************************************************************************/
#include &lt;avr/io.h&gt;
#include &lt;string.h&gt;
#include &lt;stdio.h&gt;
#include &lt;util/delay.h&gt;

#define BAUD_RATE 19200</pre>
<pre>// AVRJazz Mega168/328 SPI I/O
#define SPI_PORT PORTB
#define SPI_DDR  DDRB
#define SPI_CS   PORTB2</pre>
<pre>// Wiznet W5100 Op Code</pre>
<pre>#define WIZNET_WRITE_OPCODE 0xF0
#define WIZNET_READ_OPCODE 0x0F</pre>
<pre>// Wiznet W5100 Register Addresses
#define MR   0x0000   // Mode Register
#define GAR  0x0001   // Gateway Address: 0x0001 to 0x0004
#define SUBR 0x0005   // Subnet mask Address: 0x0005 to 0x0008
#define SAR  0x0009   // Source Hardware Address (MAC): 0x0009 to 0x000E
#define SIPR 0x000F   // Source IP Address: 0x000F to 0x0012
#define RMSR 0x001A   // RX Memory Size Register
#define TMSR 0x001B   // TX Memory Size Register</pre>
<pre>void uart_init(void)
{
  UBRR0H = (((F_CPU/BAUD_RATE)/16)-1)&gt;&gt;8;	// set baud rate
  UBRR0L = (((F_CPU/BAUD_RATE)/16)-1);
  UCSR0B = (1&lt;&lt;RXEN0)|(1&lt;&lt;TXEN0); 		// enable Rx &amp; Tx
  UCSR0C=  (1&lt;&lt;UCSZ01)|(1&lt;&lt;UCSZ00);  	       // config USART; 8N1
}</pre>
<pre>void uart_flush(void)
{
  unsigned char dummy;</pre>
<pre>  while (UCSR0A &amp; (1&lt;&lt;RXC0)) dummy = UDR0;
}</pre>
<pre>int uart_putch(char ch,FILE *stream)
{
   if (ch == '\n')
    uart_putch('\r', stream);</pre>
<pre>   while (!(UCSR0A &amp; (1&lt;&lt;UDRE0)));
   UDR0=ch;</pre>
<pre>   return 0;
}</pre>
<pre>int uart_getch(FILE *stream)
{
   unsigned char ch;</pre>
<pre>   while (!(UCSR0A &amp; (1&lt;&lt;RXC0)));
   ch=UDR0;  

   /* Echo the Output Back to terminal */
   uart_putch(ch,stream);       

   return ch;
}</pre>
<pre>void ansi_cl(void)
{
  // ANSI clear screen: cl=\E[H\E[J
  putchar(27);
  putchar('[');
  putchar('H');
  putchar(27);
  putchar('[');
  putchar('J');
}</pre>
<pre>void ansi_me(void)
{
  // ANSI turn off all attribute: me=\E[0m
  putchar(27);
  putchar('[');
  putchar('0');
  putchar('m');
}</pre>
<pre>void SPI_Write(unsigned int addr,unsigned char data)
{
  // Activate the CS pin
  SPI_PORT &amp;= ~(1&lt;&lt;SPI_CS);</pre>
<pre>  // Start Wiznet W5100 Write OpCode transmission
  SPDR = WIZNET_WRITE_OPCODE;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));</pre>
<pre>  // Start Wiznet W5100 Address High Bytes transmission
  SPDR = (addr &amp; 0xFF00) &gt;&gt; 8;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));</pre>
<pre>  // Start Wiznet W5100 Address Low Bytes transmission
  SPDR = addr &amp; 0x00FF;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));   

  // Start Data transmission
  SPDR = data;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));</pre>
<pre>  // CS pin is not active
  SPI_PORT |= (1&lt;&lt;SPI_CS);
}</pre>
<pre>unsigned char SPI_Read(unsigned int addr)
{
  // Activate the CS pin
  SPI_PORT &amp;= ~(1&lt;&lt;SPI_CS);</pre>
<pre>  // Start Wiznet W5100 Read OpCode transmission
  SPDR = WIZNET_READ_OPCODE;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));</pre>
<pre>  // Start Wiznet W5100 Address High Bytes transmission
  SPDR = (addr &amp; 0xFF00) &gt;&gt; 8;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));</pre>
<pre>  // Start Wiznet W5100 Address Low Bytes transmission
  SPDR = addr &amp; 0x00FF;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));   

  // Send Dummy transmission for reading the data
  SPDR = 0x00;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));  

  // CS pin is not active
  SPI_PORT |= (1&lt;&lt;SPI_CS);</pre>
<pre>  return(SPDR);
}</pre>
<pre>void W5100_Init(void)
{
  // Ethernet Setup
  unsigned char mac_addr[] = {0x00,0x16,0x36,0xDE,0x58,0xF6};
  unsigned char ip_addr[] = {192,168,2,10};
  unsigned char sub_mask[] = {255,255,255,0};
  unsigned char gtw_addr[] = {192,168,2,1};</pre>
<pre>  // Setting the Wiznet W5100 Mode Register: 0x0000
  SPI_Write(MR,0x80);            // MR = 0b10000000;
  _delay_ms(1);
  printf("Reading MR: %d\n\n",SPI_Read(MR));</pre>
<pre>  // Setting the Wiznet W5100 Gateway Address (GAR): 0x0001 to 0x0004
  printf("Setting Gateway Address %d.%d.%d.%d\n",gtw_addr[0],gtw_addr[1],\
          gtw_addr[2],gtw_addr[3]);
  SPI_Write(GAR + 0,gtw_addr[0]);
  SPI_Write(GAR + 1,gtw_addr[1]);
  SPI_Write(GAR + 2,gtw_addr[2]);
  SPI_Write(GAR + 3,gtw_addr[3]);
  _delay_ms(1);</pre>
<pre>  printf("Reading GAR: %d.%d.%d.%d\n\n",SPI_Read(GAR + 0),SPI_Read(GAR + 1),\
          SPI_Read(GAR + 2),SPI_Read(GAR + 3));</pre>
<pre>  // Setting the Wiznet W5100 Source Address Register (SAR): 0x0009 to 0x000E
  printf("Setting Source Address %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",mac_addr[0],mac_addr[1],\
          mac_addr[2],mac_addr[3],mac_addr[4],mac_addr[5]);
  SPI_Write(SAR + 0,mac_addr[0]);
  SPI_Write(SAR + 1,mac_addr[1]);
  SPI_Write(SAR + 2,mac_addr[2]);
  SPI_Write(SAR + 3,mac_addr[3]);
  SPI_Write(SAR + 4,mac_addr[4]);
  SPI_Write(SAR + 5,mac_addr[5]);
  _delay_ms(1);</pre>
<pre>  printf("Reading SAR: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n\n",SPI_Read(SAR + 0),SPI_Read(SAR + 1),\
          SPI_Read(SAR + 2),SPI_Read(SAR + 3),SPI_Read(SAR + 4),SPI_Read(SAR + 5));</pre>
<pre>  // Setting the Wiznet W5100 Sub Mask Address (SUBR): 0x0005 to 0x0008
  printf("Setting Sub Mask Address %d.%d.%d.%d\n",sub_mask[0],sub_mask[1],\
          sub_mask[2],sub_mask[3]);
  SPI_Write(SUBR + 0,sub_mask[0]);
  SPI_Write(SUBR + 1,sub_mask[1]);
  SPI_Write(SUBR + 2,sub_mask[2]);
  SPI_Write(SUBR + 3,sub_mask[3]);
  _delay_ms(1);</pre>
<pre>  printf("Reading SUBR: %d.%d.%d.%d\n\n",SPI_Read(SUBR + 0),SPI_Read(SUBR + 1),\
          SPI_Read(SUBR + 2),SPI_Read(SUBR + 3));</pre>
<pre>  // Setting the Wiznet W5100 IP Address (SIPR): 0x000F to 0x0012
  printf("Setting IP Address %d.%d.%d.%d\n",ip_addr[0],ip_addr[1],\
          ip_addr[2],ip_addr[3]);
  SPI_Write(SIPR + 0,ip_addr[0]);
  SPI_Write(SIPR + 1,ip_addr[1]);
  SPI_Write(SIPR + 2,ip_addr[2]);
  SPI_Write(SIPR + 3,ip_addr[3]);
  _delay_ms(1);</pre>
<pre>  printf("Reading SIPR: %d.%d.%d.%d\n\n",SPI_Read(SIPR + 0),SPI_Read(SIPR + 1),\
          SPI_Read(SIPR + 2),SPI_Read(SIPR + 3));

  // Setting the Wiznet W5100 RX and TX Memory Size, we use 2KB for Rx/Tx 4 channels
  printf("Setting Wiznet RMSR and TMSR\n\n");
  SPI_Write(RMSR,0x55);
  SPI_Write(TMSR,0x55);</pre>
<pre>  printf("Done Wiznet W5100 Initialized!\n");
}</pre>
<pre>// Assign I/O stream to UART
FILE uart_str = FDEV_SETUP_STREAM(uart_putch, uart_getch, _FDEV_SETUP_RW);</pre>
<pre>int main(void){
  // Set the PORTD as Output:
  DDRD=0xFF;
  PORTD=0x00;</pre>
<pre>   // Define Output/Input Stream
  stdout = stdin = &amp;uart_str;</pre>
<pre>  // Initial UART Peripheral
  uart_init();</pre>
<pre>  // Clear Screen
  ansi_me();
  ansi_cl();
  ansi_me();
  ansi_cl();
  uart_flush();</pre>
<pre>  // Initial the AVR ATMega168/328 SPI Peripheral
  // Set MOSI (PORTB3),SCK (PORTB5) and PORTB2 (SS) as output, others as input
  SPI_DDR = (1&lt;&lt;PORTB3)|(1&lt;&lt;PORTB5)|(1&lt;&lt;PORTB2);</pre>
<pre>  // CS pin is not active
  SPI_PORT |= (1&lt;&lt;SPI_CS);</pre>
<pre>  // Enable SPI, Master Mode 0, set the clock rate fck/2
  SPCR = (1&lt;&lt;SPE)|(1&lt;&lt;MSTR);
  SPSR |= (1&lt;&lt;SPI2X);</pre>
<pre>  // Initial the Wiznet W5100
  printf("Wiznet W5100 Init\n\n");
  W5100_Init();</pre>
<pre>  // Loop forever
  for(;;){
  }
  return 0;
}</pre>
<pre>/* EOF: wiznetping.c */</pre>
<p>After compiling and downloading the HEX program into the AVRJazz Mega328 board; connect the RJ45 connector UTP ethernet cable to your hubs/switch or you could connect directly with the cross configuration cable to your computer. Use the serial terminal such as Hyperterminal, puTTY or Tera Term and configure it to accept the serial connection with 19200 baud rate, 8-bit data with No Parity Check.</p>
<p>Now you are ready to test your first embedded Ethernet by using the &#8220;<strong>ping</strong>&#8221; command as shown on these following pictures:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_06.jpg"><img class="alignnone size-full wp-image-1786" title="ethernet_06" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_06.jpg" alt="" width="576" height="420" /></a></p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_07.jpg"><img class="alignnone size-full wp-image-1787" title="ethernet_07" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_07.jpg" alt="" width="575" height="326" /></a><br />
<strong></strong></p>
<p><strong>Wiznet W5100 SPI Initialization </strong></p>
<p>To initialize the W5100 chip, we need to write on each of the W5100 common registers named <strong>MR</strong> (Mode Register), <strong>SUBR</strong> (Subnet mask Register), <strong>SAR</strong> (Source Hardware Register), <strong>SIPR</strong> (Source IP Register), <strong>RMSR</strong> (Receive Memory Size Register) and <strong>TMSR</strong> (Transmit Memory Size Register).</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_08.jpg"><img class="alignnone size-full wp-image-1789" title="ethernet_08" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_08.jpg" alt="" width="580" height="407" /></a></p>
<p>All the Wiznet W5100 registers address has 16-bits wide and the register it self is 8-bits wide; because we use 8-bit AVR ATMega328 microcontroller SPI, therefore in order to perform write or read operation we need to pass the first 8-bit MSB (most significant byte) and follow by the 8-bit LSB (least significant byte) of the W5100 register address. The Wiznet W5100 also use two operant commands to differentiate between the WRITE (<strong>0xF0</strong>) and READ (<strong>0&#215;0F</strong>) operation. The Wiznet W5100 SPI write and read routine is implemented in the <strong>SPI_Write()</strong> and <strong>SPI_Read()</strong> functions on the above C code.</p>
<p>The SAR registers is also known as the MAC (Media Access Control) address, this W5100 register will represent the unique hardware identification in the network. The MAC address is assigned and managed by Institute of Electrical and Electronics Engineers (IEEE) for each NIC (Network Interface Card) manufacturer where the first 3 bytes of 6 bytes MAC address is used to identify the organization that issued the identifier and are known as the OUI (Organizationally Unique Identifier). For example the following are the list of Atmel, Microchip and Wiznet OUI:</p>
<pre>00-04-25   (hex)      Atmel Corporation
000425     (base 16)  Atmel Corporation
                      Multimedia &amp; Communications Group
                      2200 Gateway Centre, Suite 201
                      Morrisville NC 27560
                      UNITED STATES</pre>
<pre>00-04-A3   (hex)      Microchip Technology, Inc.
0004A3     (base 16)  Microchip Technology, Inc.
                      2355 W. Chandler Blvd.
                      Chandler AZ 85224
                      UNITED STATES</pre>
<pre>00-08-DC   (hex)      Wiznet
0008DC     (base 16)  Wiznet
                      5F Simmtech bldg., 228-3,
                      Nonyhun, Kangnam
                      Seoul  135-830
                      KOREA, REPUBLIC OF</pre>
<p>Now the question is how you could get your own MAC address because the Wiznet W5100 chip is shipped without its own MAC address; the answer is you could either register your own MAC address (OUI) to IEEE (of course this is not recommended for the hobbyist as this will be very expensive unless you could effort it) or you could simply use your own computer NIC&#8217;s MAC address and just change the last byte of the 6 byte MAC address and cross you finger, hopping this MAC address will be unique within your network as I did. By using the &#8220;<strong>ipconfig /all</strong>&#8221; command in the window command prompt, you could get the information of your computer MAC address in hex notation i.e. <strong>00-16-36-DE-58-F5</strong> (the MAC address of my computer); now by adding one to the last byte you will get the W5100 MAC address that I used in this tutorial (<strong>00-16-36-DE-58-F6</strong>).</p>
<p>The Wiznet W5100 support up to four simultaneous channels or sockets, each of the channels has its own registers address to control the operation. All of these channels is supported by 8 KB memory of the transmit buffer and 8 KB memory of the receive buffer. We could adjust this memory size on each channel by assigning the <strong>RMSR</strong> and <strong>TMSR</strong> register (the default is 2 KB for each channel) as shown on this following C code:</p>
<pre>  // Setting the Wiznet W5100 RX and TX Memory Size, we use 2KB for Rx/Tx 4 channels
  printf("Setting Wiznet RMSR and TMSR\n\n");
  SPI_Write(RMSR,0x55);
  SPI_Write(TMSR,0x55);</pre>
<p>All the W5100 initialization routine is implemented in <strong>W5100_Init()</strong> function, where we perform both writing and reading in order to understand how we could control the W5100 register through the SPI. The following is the summary of how we initialized the W5100 register. For the complete information about the W5100 registers please refer to the Wiznet W5100 datasheet (consider it as your best friend in this tutorial):</p>
<ul>
<li>Write <strong>0&#215;80</strong> to W5100 <strong>MR</strong> (Mode Register) on address: 0&#215;0000 to soft reset the chip</li>
<li>Assign four bytes of the gateway IP address to the W5100 <strong>GAR</strong> (Gateway Address Register) on address 0&#215;0001 to 0&#215;0004</li>
<li>Assign four bytes of the sub mask address to the W5100 <strong>SUBR</strong> (Sub Mask Address Register) on address 0&#215;0005 to 0&#215;0008</li>
<li>Assign six bytes of the MAC address to the W5100 <strong>SAR</strong> (Source Address Register) on address 0&#215;0009 to 0&#215;000E</li>
<li>Assign four bytes of the IP address to the W5100 <strong>SIPR</strong> (Source IP Register) on address 0&#215;000F to 0&#215;0012</li>
<li>The last is to allocate the transmit and receive buffer size to each of <strong>RMSR</strong> (RX Memory Size Register) address: 0&#215;001A and <strong>TMSR</strong> (TX Memory Size Register) on address 0&#215;001B.</li>
<li>Because we use the default value <strong>0&#215;07D0</strong> (200 ms, where 1 mean 100 us) of the <strong>RTR</strong> (Retry Time Value Register) and default value <strong>0&#215;08</strong> (8 times retry before generating interrupt) of the <strong>RCR</strong> (Retry Count Register), therefore we don&#8217;t need to set these registers</li>
</ul>
<p>After performing all the W5100 required initialization routine you could examine whether it work or not by sending the &#8220;<strong>ping</strong>&#8221; command to the W5100 build in ICMP (Internet Control Message Protocol) package responder. If everything works, then you will get the ICMP reply from the Wiznet W5100 chip. With this understanding now we are ready to continue this tutorial and make our first simple embedded web server.</p>
<p><strong>The Embedded Web Server</strong></p>
<p>The advantage of using the Hypertext Transfer Protocol (HTTP) server in the embedded system is; you don&#8217;t have to develop a special client application to communicate with your embedded system. All you need is to use any standard browser that comes with your personal computer operating system or gadget to talk to your embedded system. The HTTP server uses a simple text called Hypertext Markup Language (HTML) to interact with the browser (client application) through the TCP/IP protocol.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_09.jpg"><img class="alignnone size-full wp-image-1790" title="ethernet_09" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_09.jpg" alt="" width="582" height="408" /></a></p>
<p>The HTTP server work by listening to any request from the client (browser) for any HTTP &#8220;<strong>GET</strong>&#8221; or &#8220;<strong>POST</strong>&#8221; request through the TCP/IP port <strong>80</strong> (standard HTTP server port). Once the client sends this request to the HTTP server, then the HTTP server will response to this client request by sending the HTTP response header (<strong>HTTP/1.0 200 OK</strong> and <strong>Content-Type: text/html</strong>) follow by the blank line and the HTML text to the client, after transfer all the HTML text to the client; the HTTP server will automatically disconnect the established connection with the client. The following is the example of the client request and the HTML text response transmitted by the embedded HTTP server:</p>
<p><strong>Client Request:</strong></p>
<pre>GET / HTTP/1.1
Host: 192.168.2.101
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20
100401 Firefox/3.6.3
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive</pre>
<p><strong>HTTP Server Response:</strong></p>
<pre>HTTP/1.0 200 OK
Content-Type: text/html</pre>
<pre>&lt;html&gt;
&lt;body&gt;
&lt;span style="color:#0000A0"&gt;
&lt;h1&gt;Embedded Web Server&lt;/h1&gt;
&lt;h3&gt;AVRJazz Mega328 and WIZ811MJ&lt;/h3&gt;
&lt;p&gt;&lt;form method="POST"&gt;
&lt;strong&gt;Temp: &lt;input type="text" size=2 value="26"&gt; &lt;sup&gt;O&lt;/sup&gt;C
&lt;p&gt;&lt;input type="radio" name="radio" value="0" &gt;Blinking LED
&lt;br&gt;&lt;input type="radio" name="radio" value="1" checked&gt;Scanning LED
&lt;/strong&gt;&lt;p&gt;
&lt;input type="submit"&gt;
&lt;/form&gt;&lt;/span&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>The client then will translate this received HTML text and display the information on the browser screen such as room&#8217;s temperature and the output LED status. By submitting different LED setting from the browser (POST request) to the HTTP server, now we could easily give the needed instruction to the AVR ATMega328 microcontroller that also functioned as the embedded web server.</p>
<p>Now as you understand the basic principal of how the HTTP server protocol work, its time to implement it on the AVR ATMega328 Microcontroller and Wiznet W5100 chip. The following is the complete C code called &#8220;<strong>wiznetweb.c</strong>&#8221; for our embedded web server:</p>
<pre>/*****************************************************************************
//  File Name    : wiznetweb.c
//  Version      : 1.0
//  Description  : AVRJazz Mega328 and Wiznet W5100 Web Server
//  Author       : RWB
//  Target       : AVRJazz Mega328 Board
//  Compiler     : AVR-GCC 4.3.2; avr-libc 1.6.6 (WinAVR 20090313)
//  IDE          : Atmel AVR Studio 4.17
//  Programmer   : AVRJazz Mega328 STK500 v2.0 Bootloader
//               : AVR Visual Studio 4.17, STK500 programmer
//  Last Updated : 20 July 2010
*****************************************************************************/
#include &lt;avr/io.h&gt;
#include &lt;string.h&gt;
#include &lt;stdio.h&gt;
#include &lt;util/delay.h&gt;
#include &lt;avr/interrupt.h&gt;
#include &lt;avr/pgmspace.h&gt;</pre>
<pre>// AVRJazz Mega328 SPI I/O
#define SPI_PORT PORTB
#define SPI_DDR  DDRB
#define SPI_CS   PORTB2</pre>
<pre>// Wiznet W5100 Op Code
#define WIZNET_WRITE_OPCODE 0xF0
#define WIZNET_READ_OPCODE 0x0F</pre>
<pre>// Wiznet W5100 Register Addresses
#define MR         0x0000      // Mode Register
#define GAR        0x0001      // Gateway Address: 0x0001 to 0x0004
#define SUBR       0x0005      // Subnet mask Address: 0x0005 to 0x0008
#define SAR        0x0009      // Source Hardware Address (MAC): 0x0009 to 0x000E
#define SIPR       0x000F      // Source IP Address: 0x000F to 0x0012
#define RMSR       0x001A      // RX Memory Size Register
#define TMSR       0x001B      // TX Memory Size Register</pre>
<pre>#define S0_MR	   0x0400      // Socket 0: Mode Register Address
#define S0_CR	   0x0401      // Socket 0: Command Register Address
#define S0_IR	   0x0402      // Socket 0: Interrupt Register Address
#define S0_SR	   0x0403      // Socket 0: Status Register Address
#define S0_PORT    0x0404      // Socket 0: Source Port: 0x0404 to 0x0405
#define SO_TX_FSR  0x0420      // Socket 0: Tx Free Size Register: 0x0420 to 0x0421
#define S0_TX_RD   0x0422      // Socket 0: Tx Read Pointer Register: 0x0422 to 0x0423
#define S0_TX_WR   0x0424      // Socket 0: Tx Write Pointer Register: 0x0424 to 0x0425
#define S0_RX_RSR  0x0426      // Socket 0: Rx Received Size Pointer Register: 0x0425 to 0x0427
#define S0_RX_RD   0x0428      // Socket 0: Rx Read Pointer: 0x0428 to 0x0429</pre>
<pre>#define TXBUFADDR  0x4000      // W5100 Send Buffer Base Address
#define RXBUFADDR  0x6000      // W5100 Read Buffer Base Address</pre>
<pre>// S0_MR values
#define MR_CLOSE	  0x00    // Unused socket
#define MR_TCP		  0x01    // TCP
#define MR_UDP		  0x02    // UDP
#define MR_IPRAW	  0x03	  // IP LAYER RAW SOCK
#define MR_MACRAW	  0x04	  // MAC LAYER RAW SOCK
#define MR_PPPOE	  0x05	  // PPPoE
#define MR_ND		  0x20	  // No Delayed Ack(TCP) flag
#define MR_MULTI	  0x80	  // support multicating</pre>
<pre>// S0_CR values
#define CR_OPEN          0x01	  // Initialize or open socket
#define CR_LISTEN        0x02	  // Wait connection request in tcp mode(Server mode)
#define CR_CONNECT       0x04	  // Send connection request in tcp mode(Client mode)
#define CR_DISCON        0x08	  // Send closing reqeuset in tcp mode
#define CR_CLOSE         0x10	  // Close socket
#define CR_SEND          0x20	  // Update Tx memory pointer and send data
#define CR_SEND_MAC      0x21	  // Send data with MAC address, so without ARP process
#define CR_SEND_KEEP     0x22	  // Send keep alive message
#define CR_RECV          0x40	  // Update Rx memory buffer pointer and receive data</pre>
<pre>// S0_SR values
#define SOCK_CLOSED      0x00     // Closed
#define SOCK_INIT        0x13	  // Init state
#define SOCK_LISTEN      0x14	  // Listen state
#define SOCK_SYNSENT     0x15	  // Connection state
#define SOCK_SYNRECV     0x16	  // Connection state
#define SOCK_ESTABLISHED 0x17	  // Success to connect
#define SOCK_FIN_WAIT    0x18	  // Closing state
#define SOCK_CLOSING     0x1A	  // Closing state
#define SOCK_TIME_WAIT	 0x1B	  // Closing state
#define SOCK_CLOSE_WAIT  0x1C	  // Closing state
#define SOCK_LAST_ACK    0x1D	  // Closing state
#define SOCK_UDP         0x22	  // UDP socket
#define SOCK_IPRAW       0x32	  // IP raw mode socket
#define SOCK_MACRAW      0x42	  // MAC raw mode socket
#define SOCK_PPPOE       0x5F	  // PPPOE socket</pre>
<pre>#define TX_BUF_MASK      0x07FF   // Tx 2K Buffer Mask:
#define RX_BUF_MASK      0x07FF   // Rx 2K Buffer Mask:
#define NET_MEMALLOC     0x05     // Use 2K of Tx/Rx Buffer</pre>
<pre>#define TCP_PORT         80       // TCP/IP Port</pre>
<pre>// Debugging Mode, 0 - Debug OFF, 1 - Debug ON
#define _DEBUG_MODE      0</pre>
<pre>#if _DEBUG_MODE
  #define BAUD_RATE 19200
#endif</pre>
<pre>// Define W5100 Socket Register and Variables Used
uint8_t sockreg;</pre>
<pre>#define MAX_BUF 512</pre>
<pre>uint8_t buf[MAX_BUF];
int tempvalue;
uint8_t ledmode,ledeye,ledsign;</pre>
<pre>#if _DEBUG_MODE
void uart_init(void)
{
  UBRR0H = (((F_CPU/BAUD_RATE)/16)-1)&gt;&gt;8;		// set baud rate
  UBRR0L = (((F_CPU/BAUD_RATE)/16)-1);
  UCSR0B = (1&lt;&lt;RXEN0)|(1&lt;&lt;TXEN0); 				// enable Rx &amp; Tx
  UCSR0C=  (1&lt;&lt;UCSZ01)|(1&lt;&lt;UCSZ00);  	        // config USART; 8N1
}</pre>
<pre>void uart_flush(void)
{
  unsigned char dummy;</pre>
<pre>  while (UCSR0A &amp; (1&lt;&lt;RXC0)) dummy = UDR0;
}</pre>
<pre>int uart_putch(char ch,FILE *stream)
{
   if (ch == '\n')
    uart_putch('\r', stream);</pre>
<pre>   while (!(UCSR0A &amp; (1&lt;&lt;UDRE0)));
   UDR0=ch;</pre>
<pre>   return 0;
}</pre>
<pre>int uart_getch(FILE *stream)
{
   unsigned char ch;</pre>
<pre>   while (!(UCSR0A &amp; (1&lt;&lt;RXC0)));
   ch=UDR0;  

   /* Echo the Output Back to terminal */
   uart_putch(ch,stream);       

   return ch;
}</pre>
<pre>void ansi_cl(void)
{
  // ANSI clear screen: cl=\E[H\E[J
  putchar(27);
  putchar('[');
  putchar('H');
  putchar(27);
  putchar('[');
  putchar('J');
}</pre>
<pre>void ansi_me(void)
{
  // ANSI turn off all attribute: me=\E[0m
  putchar(27);
  putchar('[');
  putchar('0');
  putchar('m');
}
#endif</pre>
<pre>void SPI_Write(uint16_t addr,uint8_t data)
{
  // Activate the CS pin
  SPI_PORT &amp;= ~(1&lt;&lt;SPI_CS);</pre>
<pre>  // Start Wiznet W5100 Write OpCode transmission
  SPDR = WIZNET_WRITE_OPCODE;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));</pre>
<pre>  // Start Wiznet W5100 Address High Bytes transmission
  SPDR = (addr &amp; 0xFF00) &gt;&gt; 8;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));</pre>
<pre>  // Start Wiznet W5100 Address Low Bytes transmission
  SPDR = addr &amp; 0x00FF;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));   

  // Start Data transmission
  SPDR = data;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));</pre>
<pre>  // CS pin is not active
  SPI_PORT |= (1&lt;&lt;SPI_CS);
}</pre>
<pre>unsigned char SPI_Read(uint16_t addr)
{
  // Activate the CS pin
  SPI_PORT &amp;= ~(1&lt;&lt;SPI_CS);</pre>
<pre>  // Start Wiznet W5100 Read OpCode transmission
  SPDR = WIZNET_READ_OPCODE;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));</pre>
<pre>  // Start Wiznet W5100 Address High Bytes transmission
  SPDR = (addr &amp; 0xFF00) &gt;&gt; 8;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));</pre>
<pre>  // Start Wiznet W5100 Address Low Bytes transmission
  SPDR = addr &amp; 0x00FF;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));   

  // Send Dummy transmission for reading the data
  SPDR = 0x00;</pre>
<pre>  // Wait for transmission complete
  while(!(SPSR &amp; (1&lt;&lt;SPIF)));  

  // CS pin is not active
  SPI_PORT |= (1&lt;&lt;SPI_CS);</pre>
<pre>  return(SPDR);
}</pre>
<pre>void W5100_Init(void)
{
  // Ethernet Setup
  unsigned char mac_addr[] = {0x00,0x16,0x36,0xDE,0x58,0xF6};
  unsigned char ip_addr[] = {192,168,2,10};
  unsigned char sub_mask[] = {255,255,255,0};
  unsigned char gtw_addr[] = {192,168,2,1};</pre>
<pre>  // Setting the Wiznet W5100 Mode Register: 0x0000
  SPI_Write(MR,0x80);            // MR = 0b10000000;</pre>
<pre>  // Setting the Wiznet W5100 Gateway Address (GAR): 0x0001 to 0x0004
  SPI_Write(GAR + 0,gtw_addr[0]);
  SPI_Write(GAR + 1,gtw_addr[1]);
  SPI_Write(GAR + 2,gtw_addr[2]);
  SPI_Write(GAR + 3,gtw_addr[3]);</pre>
<pre>  // Setting the Wiznet W5100 Source Address Register (SAR): 0x0009 to 0x000E
  SPI_Write(SAR + 0,mac_addr[0]);
  SPI_Write(SAR + 1,mac_addr[1]);
  SPI_Write(SAR + 2,mac_addr[2]);
  SPI_Write(SAR + 3,mac_addr[3]);
  SPI_Write(SAR + 4,mac_addr[4]);
  SPI_Write(SAR + 5,mac_addr[5]);</pre>
<pre>  // Setting the Wiznet W5100 Sub Mask Address (SUBR): 0x0005 to 0x0008
  SPI_Write(SUBR + 0,sub_mask[0]);
  SPI_Write(SUBR + 1,sub_mask[1]);
  SPI_Write(SUBR + 2,sub_mask[2]);
  SPI_Write(SUBR + 3,sub_mask[3]);</pre>
<pre>  // Setting the Wiznet W5100 IP Address (SIPR): 0x000F to 0x0012
  SPI_Write(SIPR + 0,ip_addr[0]);
  SPI_Write(SIPR + 1,ip_addr[1]);
  SPI_Write(SIPR + 2,ip_addr[2]);
  SPI_Write(SIPR + 3,ip_addr[3]);    

  // Setting the Wiznet W5100 RX and TX Memory Size (2KB),
  SPI_Write(RMSR,NET_MEMALLOC);
  SPI_Write(TMSR,NET_MEMALLOC);
}</pre>
<pre>void close(uint8_t sock)
{
   if (sock != 0) return;

   // Send Close Command
   SPI_Write(S0_CR,CR_CLOSE);</pre>
<pre>   // Waiting until the S0_CR is clear
   while(SPI_Read(S0_CR));
}</pre>
<pre>void disconnect(uint8_t sock)
{
   if (sock != 0) return;</pre>
<pre>   // Send Disconnect Command
   SPI_Write(S0_CR,CR_DISCON);</pre>
<pre>   // Wait for Disconecting Process
   while(SPI_Read(S0_CR));
}</pre>
<pre>uint8_t socket(uint8_t sock,uint8_t eth_protocol,uint16_t tcp_port)
{
    uint8_t retval=0;</pre>
<pre>    if (sock != 0) return retval;

    // Make sure we close the socket first
    if (SPI_Read(S0_SR) == SOCK_CLOSED) {
      close(sock);
    }</pre>
<pre>    // Assigned Socket 0 Mode Register
    SPI_Write(S0_MR,eth_protocol);

    // Now open the Socket 0
    SPI_Write(S0_PORT,((tcp_port &amp; 0xFF00) &gt;&gt; 8 ));
    SPI_Write(S0_PORT + 1,(tcp_port &amp; 0x00FF));
    SPI_Write(S0_CR,CR_OPEN);                   // Open Socket</pre>
<pre>    // Wait for Opening Process
    while(SPI_Read(S0_CR));</pre>
<pre>    // Check for Init Status
    if (SPI_Read(S0_SR) == SOCK_INIT)
      retval=1;
    else
      close(sock);

    return retval;
}</pre>
<pre>uint8_t listen(uint8_t sock)
{
   uint8_t retval = 0;</pre>
<pre>   if (sock != 0) return retval;</pre>
<pre>   if (SPI_Read(S0_SR) == SOCK_INIT) {
     // Send the LISTEN Command
     SPI_Write(S0_CR,CR_LISTEN);

     // Wait for Listening Process
     while(SPI_Read(S0_CR));</pre>
<pre>     // Check for Listen Status
     if (SPI_Read(S0_SR) == SOCK_LISTEN)
       retval=1;
     else
       close(sock);
    }
    return retval;
}</pre>
<pre>uint16_t send(uint8_t sock,const uint8_t *buf,uint16_t buflen)
{
    uint16_t ptr,offaddr,realaddr,txsize,timeout;   

    if (buflen &lt;= 0 || sock != 0) return 0;</pre>
<pre>#if _DEBUG_MODE
    printf("Send Size: %d\n",buflen);
#endif</pre>
<pre>    // Make sure the TX Free Size Register is available
    txsize=SPI_Read(SO_TX_FSR);
    txsize=(((txsize &amp; 0x00FF) &lt;&lt; 8 ) + SPI_Read(SO_TX_FSR + 1));</pre>
<pre>#if _DEBUG_MODE
    printf("TX Free Size: %d\n",txsize);
#endif</pre>
<pre>    timeout=0;
    while (txsize &lt; buflen) {
      _delay_ms(1);</pre>
<pre>     txsize=SPI_Read(SO_TX_FSR);
     txsize=(((txsize &amp; 0x00FF) &lt;&lt; 8 ) + SPI_Read(SO_TX_FSR + 1));</pre>
<pre>     // Timeout for approx 1000 ms
     if (timeout++ &gt; 1000) {
#if _DEBUG_MODE
       printf("TX Free Size Error!\n");
#endif
       // Disconnect the connection
       disconnect(sock);
       return 0;
     }
   }	

   // Read the Tx Write Pointer
   ptr = SPI_Read(S0_TX_WR);
   offaddr = (((ptr &amp; 0x00FF) &lt;&lt; 8 ) + SPI_Read(S0_TX_WR + 1));
#if _DEBUG_MODE
    printf("TX Buffer: %x\n",offaddr);
#endif	

    while(buflen) {
      buflen--;
      // Calculate the real W5100 physical Tx Buffer Address
      realaddr = TXBUFADDR + (offaddr &amp; TX_BUF_MASK);</pre>
<pre>      // Copy the application data to the W5100 Tx Buffer
      SPI_Write(realaddr,*buf);
      offaddr++;
      buf++;
    }

    // Increase the S0_TX_WR value, so it point to the next transmit
    SPI_Write(S0_TX_WR,(offaddr &amp; 0xFF00) &gt;&gt; 8 );
    SPI_Write(S0_TX_WR + 1,(offaddr &amp; 0x00FF));	

    // Now Send the SEND command
    SPI_Write(S0_CR,CR_SEND);

    // Wait for Sending Process
    while(SPI_Read(S0_CR));	

    return 1;
}</pre>
<pre>uint16_t recv(uint8_t sock,uint8_t *buf,uint16_t buflen)
{
    uint16_t ptr,offaddr,realaddr;   	

    if (buflen &lt;= 0 || sock != 0) return 1;   

    // If the request size &gt; MAX_BUF,just truncate it
    if (buflen &gt; MAX_BUF)
      buflen=MAX_BUF - 2;</pre>
<pre>    // Read the Rx Read Pointer
    ptr = SPI_Read(S0_RX_RD);
    offaddr = (((ptr &amp; 0x00FF) &lt;&lt; 8 ) + SPI_Read(S0_RX_RD + 1));
#if _DEBUG_MODE
    printf("RX Buffer: %x\n",offaddr);
#endif	

    while(buflen) {
      buflen--;
      realaddr=RXBUFADDR + (offaddr &amp; RX_BUF_MASK);
      *buf = SPI_Read(realaddr);
      offaddr++;
      buf++;
    }
    *buf='\0';        // String terminated character

    // Increase the S0_RX_RD value, so it point to the next receive
    SPI_Write(S0_RX_RD,(offaddr &amp; 0xFF00) &gt;&gt; 8 );
    SPI_Write(S0_RX_RD + 1,(offaddr &amp; 0x00FF));	

    // Now Send the RECV command
    SPI_Write(S0_CR,CR_RECV);
    _delay_us(5);    // Wait for Receive Process

    return 1;
}</pre>
<pre>uint16_t recv_size(void)
{
  return ((SPI_Read(S0_RX_RSR) &amp; 0x00FF) &lt;&lt; 8 ) + SPI_Read(S0_RX_RSR + 1);
}</pre>
<pre>int strindex(char *s,char *t)
{
  uint16_t i,n;

  n=strlen(t);
  for(i=0;*(s+i); i++) {
    if (strncmp(s+i,t,n) == 0)
      return i;
  }
  return -1;
}</pre>
<pre>ISR(TIMER0_OVF_vect)
{
  static unsigned char tenms=1;</pre>
<pre>  tenms++;                  // Read ADC every 20 x 10ms = 200 milisecond
  if (tenms &gt;= 20) {
    cli();                                // Disable Interupt</pre>
<pre>    // Select the LED Mode here
    if (ledmode == 1) {
      if (ledeye &lt;= 0) ledeye=0x01;</pre>
<pre>      if (ledsign == 0) {
	PORTD=ledeye;
	ledeye=ledeye &lt;&lt; 1;
	if (ledeye &gt;= 0x80) ledsign=1;
      } else {
	PORTD=ledeye;
	ledeye=ledeye &gt;&gt; 1;
	if (ledeye &lt;= 0x01) ledsign=0;
      }
    } else {
      if (ledsign == 0) {
	PORTD=0x00;
	ledsign=1;
      } else {
	PORTD=0xFF;
	ledsign=0;
      }
    }</pre>
<pre>    // Set ADMUX Channel for LM35DZ Input
    ADMUX=0x01;</pre>
<pre>    // Start conversion by setting ADSC on ADCSRA Register
    ADCSRA |= (1&lt;&lt;ADSC);</pre>
<pre>    // wait until convertion complete ADSC=0 -&gt; Complete
    while (ADCSRA &amp; (1&lt;&lt;ADSC));</pre>
<pre>    // Get the ADC Result
    tempvalue = ADCW;</pre>
<pre>    // ADC = (Vin x 1024) / Vref, Vref = 1 Volt, LM35DZ Out = 10mv/C
    tempvalue = (int)(tempvalue) / 10.24;

    tenms=1;	 

    sei();                            // Enable Interupt
  }</pre>
<pre>  // Start counter from 0x94, overflow at 10 mSec
  TCNT0=0x94;
}</pre>
<pre>#if _DEBUG_MODE
// Assign I/O stream to UART
FILE uart_str = FDEV_SETUP_STREAM(uart_putch, uart_getch, _FDEV_SETUP_RW);
#endif</pre>
<pre>int main(void){
  uint8_t sockstat;
  uint16_t rsize;
  char radiostat0[10],radiostat1[10],temp[4];
  int getidx,postidx;

  // Reset Port D
  DDRD = 0xFF;       // Set PORTD as Output
  PORTD = 0x00;	     

#if _DEBUG_MODE
  // Define Output/Input Stream
  stdout = stdin = &amp;uart_str;</pre>
<pre>  // Initial UART Peripheral
  uart_init();</pre>
<pre>  // Clear Screen
  ansi_me();
  ansi_cl();
  ansi_me();
  ansi_cl();
  uart_flush();
#endif</pre>
<pre>  // Initial ATMega386 ADC Peripheral
  ADCSRA = (1&lt;&lt;ADEN) | (1&lt;&lt;ADPS2) | (1&lt;&lt;ADPS1);</pre>
<pre>  // Free running ADC Mode
  ADCSRB = 0x00;</pre>
<pre>  // Initial the AVR ATMega328 SPI Peripheral
  // Set MOSI (PORTB3),SCK (PORTB5) and PORTB2 (SS) as output, others as input
  SPI_DDR = (1&lt;&lt;PORTB3)|(1&lt;&lt;PORTB5)|(1&lt;&lt;PORTB2);</pre>
<pre>  // CS pin is not active
  SPI_PORT |= (1&lt;&lt;SPI_CS);</pre>
<pre>  // Enable SPI, Master Mode 0, set the clock rate fck/2
  SPCR = (1&lt;&lt;SPE)|(1&lt;&lt;MSTR);
  SPSR |= (1&lt;&lt;SPI2X);  

  // Initial ATMega368 Timer/Counter0 Peripheral
  TCCR0A=0x00;                  // Normal Timer0 Operation
  TCCR0B=(1&lt;&lt;CS02)|(1&lt;&lt;CS00);   // Use maximum prescaller: Clk/1024
  TCNT0=0x94;                   // Start counter from 0x94, overflow at 10 mSec
  TIMSK0=(1&lt;&lt;TOIE0);            // Enable Counter Overflow Interrupt
  sei();                        // Enable Interrupt

  // Initial the W5100 Ethernet
  W5100_Init();</pre>
<pre>  // Initial variable used
  sockreg=0;
  tempvalue=0;
  ledmode=1;
  ledeye=0x01;                  // Initial LED Eye Variables
  ledsign=0;</pre>
<pre>#if _DEBUG_MODE
  printf("WEB Server Debug Mode\n\n");
#endif

  // Loop forever
  for(;;){
    sockstat=SPI_Read(S0_SR);
    switch(sockstat) {
     case SOCK_CLOSED:
        if (socket(sockreg,MR_TCP,TCP_PORT) &gt; 0) {
	  // Listen to Socket 0
	  if (listen(sockreg) &lt;= 0)
	    _delay_ms(1);
#if _DEBUG_MODE
          printf("Socket Listen!\n");
#endif
	}
	break;</pre>
<pre>     case SOCK_ESTABLISHED:
	// Get the client request size
        rsize=recv_size();
#if _DEBUG_MODE
	printf("Size: %d\n",rsize);
#endif
	if (rsize &gt; 0) {
	  // Now read the client Request
	  if (recv(sockreg,buf,rsize) &lt;= 0) break;
#if _DEBUG_MODE
  	  printf("Content:\n%s\n",buf);
#endif
          // Check the Request Header
	  getidx=strindex((char *)buf,"GET /");
	  postidx=strindex((char *)buf,"POST /");</pre>
<pre>	  if (getidx &gt;= 0 || postidx &gt;= 0) {
#if _DEBUG_MODE
	    printf("Req. Check!\n");
#endif
            // Now check the Radio Button for POST request
	    if (postidx &gt;= 0) {
	      if (strindex((char *)buf,"radio=0") &gt; 0)
	        ledmode=0;</pre>
<pre>	      if (strindex((char *)buf,"radio=1") &gt; 0)
	        ledmode=1;
            }
#if _DEBUG_MODE
	    printf("Req. Send!\n");
#endif
	    // Create the HTTP Response	Header
	    strcpy_P((char *)buf,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"));
	    strcat_P((char *)buf,PSTR("&lt;html&gt;&lt;body&gt;&lt;span style=\"color:#0000A0\"&gt;\r\n"));
	    strcat_P((char *)buf,PSTR("&lt;h1&gt;Embedded Web Server&lt;/h1&gt;\r\n"));
	    strcat_P((char *)buf,PSTR("&lt;h3&gt;AVRJazz Mega328 and WIZ811MJ&lt;/h3&gt;\r\n"));
	    strcat_P((char *)buf,PSTR("&lt;p&gt;&lt;form method=\"POST\"&gt;\r\n"));</pre>
<pre>	    // Now Send the HTTP Response
	    if (send(sockreg,buf,strlen((char *)buf)) &lt;= 0) break;

	    // Create the HTTP Temperature Response
	    sprintf((char *)temp,"%d",tempvalue);        // Convert temperature value to string</pre>
<pre>	    strcpy_P((char *)buf,PSTR("&lt;strong&gt;Temp: &lt;input type=\"text\" size=2 value=\""));
	    strcat((char *)buf,temp);
	    strcat_P((char *)buf,PSTR("\"&gt; &lt;sup&gt;O&lt;/sup&gt;C\r\n"));									

	    if (ledmode == 1) {
	      strcpy(radiostat0,"");
	      strcpy_P(radiostat1,PSTR("checked"));
	    } else {
	      strcpy_P(radiostat0,PSTR("checked"));
	      strcpy(radiostat1,"");
	    }</pre>
<pre>            // Create the HTTP Radio Button 0 Response
	    strcat_P((char *)buf,PSTR("&lt;p&gt;&lt;input type=\"radio\" name=\"radio\" value=\"0\" "));
	    strcat((char *)buf,radiostat0);
	    strcat_P((char *)buf,PSTR("&gt;Blinking LED\r\n"));
	    strcat_P((char *)buf,PSTR("&lt;br&gt;&lt;input type=\"radio\" name=\"radio\" value=\"1\" "));
	    strcat((char *)buf,radiostat1);
	    strcat_P((char *)buf,PSTR("&gt;Scanning LED\r\n"));
 	    strcat_P((char *)buf,PSTR("&lt;/strong&gt;&lt;p&gt;\r\n"));
	    strcat_P((char *)buf,PSTR("&lt;input type=\"submit\"&gt;\r\n"));
	    strcat_P((char *)buf,PSTR("&lt;/form&gt;&lt;/span&gt;&lt;/body&gt;&lt;/html&gt;\r\n"));</pre>
<pre>            // Now Send the HTTP Remaining Response
	    if (send(sockreg,buf,strlen((char *)buf)) &lt;= 0) break;
          }</pre>
<pre>	  // Disconnect the socket
	  disconnect(sockreg);
        } else
	  _delay_us(10);    // Wait for request</pre>
<pre>	break;</pre>
<pre>      case SOCK_FIN_WAIT:
      case SOCK_CLOSING:
      case SOCK_TIME_WAIT:
      case SOCK_CLOSE_WAIT:
      case SOCK_LAST_ACK:
        // Force to close the socket
	close(sockreg);
#if _DEBUG_MODE
	printf("Socket Close!\n");
#endif
	break;
    }
  }
  return 0;
}</pre>
<pre>/* EOF: wiznetweb.c */</pre>
<p>From the C code above you&#8217;ve noticed that I used many of the Atmel AVR ATMega328 microcontroller build in peripherals features such as SPI, ADC, TIMER and the UART at the same time in order to achieve the project&#8217;s goal, you could read more information of how to use all of these peripherals on my previous posted blog <a title="Analog to Digital Converter AVR C Programming" href="http://www.ermicro.com/blog/?p=121" target="_blank">Analog to Digital Converter AVR C Programming</a> and <a title="Working with AVR microcontroller Communication Port Project" href="http://www.ermicro.com/blog/?p=325" target="_blank">Working with AVR microcontroller Communication Port Project</a>.<br />
<strong></strong></p>
<p><strong>The W5100 HTTP Server </strong></p>
<p>To build the HTTP server we need to listen for the client request, read the client request and send the HTML response to the client through the TCP/IP protocol. To configure and control the Wiznet W5100 as the HTTP server basically we need to write and read to and from the Wiznet W5100 Socket Control Register and the TX and RX buffer memory; for the purpose of this tutorial (easier to understand) I only implement one channel (socket 0) of the four available channels (socket 0, 1, 2 and 3) supported by the W5100 chip. But once you understand the basic principal of how the W5100 work on this tutorial it would be easy to implement the remaining channels.</p>
<p>The Wiznet W5100 socket 0 control registers start at address 0&#215;0400 to 0&#215;04FF, the following is the list of W5100 socket 0 control registers with the 2 KB of TX and RX memory buffer used to make the HTTP server in this tutorial (again please refer to the W5100 datasheet for detail information).</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_10.jpg"><img class="alignnone size-full wp-image-1793" title="ethernet_10" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_10.jpg" alt="" width="583" height="348" /></a></p>
<p>Now fasten your seat belt as we are going through the code algorithm behind this W5100 HTTP server program!</p>
<p>Basically we use the command register (<strong>S0_CR</strong>) to instruct the W5100 chip to write or read to or from the W5100 memory buffer to make a simple HTTP server that response to the client request. The following diagram show of how we could achieve this objective:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_11.jpg"><img class="alignnone size-full wp-image-1794" title="ethernet_11" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_11.jpg" alt="" width="576" height="440" /></a></p>
<p>From the diagram above after we initialized the W5100 (i.e. it could response to the &#8220;<strong>ping</strong>&#8221; command request) we continue with opening the TCP/IP port 80 and listen to this port for any client request. Assigning the TCP/IP protocol and opening the port 80 routine is implemented in the <strong>socket()</strong> function as shown on this following C code:</p>
<pre>...
// Assigned Socket 0 Mode Register
SPI_Write(S0_MR,eth_protocol);

// Now open the Socket 0
SPI_Write(S0_PORT,((tcp_port &amp; 0xFF00) &gt;&gt; 8 ));
SPI_Write(S0_PORT + 1,(tcp_port &amp; 0x00FF));
SPI_Write(S0_CR,CR_OPEN);                   // Open Socket</pre>
<pre>// Wait for Opening Process
while(SPI_Read(S0_CR));
...</pre>
<p>After we wrote the <strong>CR_OPEN</strong> (<strong>0&#215;01</strong>) command on the <strong></strong>socket 0 command register (<strong>S0_CR</strong>), then the W5100 will automatically clear this register; therefore we could take advantage of this behavior by reading this register again and examine its contents. Upon completion of the command execution process the contents of the <strong>SO_CR</strong> register will be reset to <strong>0&#215;00</strong> and the socket 0 status register (<strong>S0_SR</strong>) will be set to the <strong>SOCK_INIT</strong> (<strong>0&#215;13</strong>) status.</p>
<p>Listening to the opening TCP/IP port (i.e. <strong>80</strong>) could be done by sending the <strong>CR_LISTEN</strong> (<strong>0&#215;02</strong>) command to the socket 0 command register (<strong>S0_CR</strong>), this will change the socket 0 status register (<strong>S0_SR</strong>) to the <strong>SOCK_LISTEN</strong> (<strong>0&#215;14</strong>) status. The listening routine is implemented in the <strong>listen()</strong> function. Now the Wiznet W5100 is ready to receive the request from client (browser).</p>
<p>When the client established the connection with the HTTP server, it will send the standard HTTP request protocol to the HTTP server. We could examine whether the connection has been established with the client by reading the status register (<strong>SO_CR</strong>) for <strong>SOCK_ESTABLISHED</strong> (<strong>0&#215;17</strong>) status. Next we examine the RX receive data size by reading the socket 0 received size register (<strong>SO_RX_RSR</strong>); this routine is implemented in <strong>recv_size()</strong> function bellow:</p>
<pre>uint16_t recv_size(void)
{
  return ((SPI_Read(S0_RX_RSR) &amp; 0x00FF) &lt;&lt; 8 ) + SPI_Read(S0_RX_RSR + 1);
}</pre>
<p>If the received data exist in the RX buffer memory then we continue to read the RX memory buffer content which is implemented in <strong>recv()</strong> function. The reading process is accomplished by first calculating the received data physical address location at the 2 KB (2048 or 0&#215;800 in hex notations) RX memory buffer boundary and then start to read the data from this location. The following picture show of how we determine the physical address of the received data.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_12.jpg"><img class="alignnone size-full wp-image-1798" title="ethernet_12" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_12.jpg" alt="" width="578" height="421" /></a></p>
<p>To determine the Wiznet W5100 RX memory buffer physical address where we start to read the received data we have to mask the value returned by the socket 0 RX read pointer (<strong>S0_RX_RD</strong>) with the <strong>0&#215;7FF</strong> (2 KB minus one in hex notations) and add the result to the RX memory buffer base address <strong>0&#215;6000</strong>. Next we use this address result to retrieve the data from RX memory buffer as shown on this following C code:</p>
<pre>...
// Read the Rx Read Pointer
ptr = SPI_Read(S0_RX_RD);
offaddr = (((ptr &amp; 0x00FF) &lt;&lt; 8 ) + SPI_Read(S0_RX_RD + 1));

while(buflen) {
  buflen--;
  realaddr=RXBUFADDR + (offaddr &amp; RX_BUF_MASK);
  *buf = SPI_Read(realaddr);
  offaddr++;
  buf++;
}
*buf='\0';        // String terminated character
...</pre>
<p>After reading all the data we need to write the last pointer value back to the socket 0 RX read pointer register (<strong>S0_RX_RD</strong>) so the W5100 RX received circular buffer mechanism will know where to put the next received data on the RX memory buffer as shown on this following C code:</p>
<pre>...
// Increase the S0_RX_RD value, so it point to the next receive
SPI_Write(S0_RX_RD,(offaddr &amp; 0xFF00) &gt;&gt; 8 );
SPI_Write(S0_RX_RD + 1,(offaddr &amp; 0x00FF));
...</pre>
<p>Finally we give the <strong>CR_RECV</strong> (<strong>0&#215;40</strong>) to the W5100 socket 0 command register (<strong>S0_CR</strong>) to complete the W5100 receiving process (updating the RX memory buffer pointer and  received data).</p>
<pre>...
// Now Send the RECV command
SPI_Write(S0_CR,CR_RECV);
_delay_us(5);    // Wait for Receive Process
...</pre>
<p>The HTTP server will send the response after examining the client request; on this tutorial the response will be the room&#8217;s temperature read by the AVR ATMega328 microcontroller ADC peripheral through the AVRJazz Mega328 on board LM35DZ temperature sensor and setting the LED display. In order to save the microcontroller RAM; all the static HTML data is stored in the flash program memory (flash RAM) use a special AVR-GCC <strong>PSTR</strong> macro and string manipulation functions such as <strong>strcpy_P()</strong> and <strong>strcat_P()</strong> to work with static string data stored in the flash program memory as shown on this following C code:</p>
<pre>...
// Create the HTTP Response Header
strcpy_P((char *)buf,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"));
strcat_P((char *)buf,PSTR("&lt;html&gt;&lt;body&gt;&lt;span style=\"color:#0000A0\"&gt;\r\n"));
strcat_P((char *)buf,PSTR("&lt;h1&gt;Embedded Web Server&lt;/h1&gt;\r\n"));
...</pre>
<p>After copying all the HTML response to the application buffer data (<strong>buf</strong>), now we are ready to send the response to the client. This function is implemented in the <strong>send()</strong> function. Before sending we need to check the availability of the W5100 TX memory buffer by reading the W5100 socket 0 free size register (<strong>S0_TX_FSR</strong>); on normal condition this register should return the value of 2 KB (<strong>0&#215;0200</strong>) free. After examining this register we are ready to send data in the application buffer and use the same principal as receiving data to determine the actual TX buffer physical address as shown on this following diagram:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_13.jpg"><img class="alignnone size-full wp-image-1799" title="ethernet_13" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_13.jpg" alt="" width="578" height="432" /></a></p>
<p>Similar to the read operation; to determine the Wiznet W5100 TX memory buffer physical address where we start to write the data we have to mask the value returned by the socket 0 TX write pointer (<strong>S0_TX_WR</strong>) with the <strong>0&#215;7FF</strong> (2 KB minus one in hex notations) and add the result to the TX memory buffer base address <strong>0&#215;4000</strong>. Next we use this address result to put the data to TX memory buffer as shown on this following C code:</p>
<pre>...
// Read the Tx Write Pointer
ptr = SPI_Read(S0_TX_WR);
offaddr = (((ptr &amp; 0x00FF) &lt;&lt; 8 ) + SPI_Read(S0_TX_WR + 1));

while(buflen) {
  buflen--;
  // Calculate the real W5100 physical Tx Buffer Address
  realaddr = TXBUFADDR + (offaddr &amp; TX_BUF_MASK);</pre>
<pre>  // Copy the application data to the W5100 Tx Buffer
  SPI_Write(realaddr,*buf);
  offaddr++;
  buf++;
}
...</pre>
<p>After putting all the data to the TX memory buffer, we need to write the last pointer value back to the socket 0 TX write pointer register (<strong>S0_TX_WR</strong>) for the next TX memory buffer writing as shown on this following C code:</p>
<pre>...
// Increase the S0_TX_WR value, so it point to the next transmit
SPI_Write(S0_TX_WR,(offaddr &amp; 0xFF00) &gt;&gt; 8 );
SPI_Write(S0_TX_WR + 1,(offaddr &amp; 0x00FF));
...</pre>
<p>Next we need to write the <strong>CR_SEND</strong> (<strong>0&#215;20</strong>) command to the W5100 socket 0 command register (<strong>S0_CR</strong>) to instruct the W5100 chip to send the HTML response data in the TX memory buffer.</p>
<pre>...
// Now Send the SEND command
SPI_Write(S0_CR,CR_SEND);
...</pre>
<p>To comply with the HTTP protocol requirement, after sending the HTML response to the client we need to disconnect and close the connection with the client. These routines are implemented in the <strong>disconnect()</strong> and <strong>close()</strong> functionsThese two functions simply send the<strong> CR_DISCON</strong> (<strong>0&#215;08</strong>) and <strong>CR_CLOSE</strong> (<strong>0&#215;10</strong>) to the W5100 socket 0 command register (<strong>S0_CR</strong>) respectively as shown on this following C code:</p>
<pre>...
// Send Disconnect Command
SPI_Write(S0_CR,CR_DISCON);
...</pre>
<pre>...
// Send Close Command
SPI_Write(S0_CR,CR_CLOSE);
...</pre>
<p>Next the infinite loop routine in main program will start opening and listening to the new client request and the whole process is repeated again.</p>
<p><strong>The Temperature and LED Display Indicator</strong></p>
<p>Before executing the <strong>for()</strong> infinite loop, first we initialized all the needed peripherals such as PORTD for displaying LED, UART for debugging, ADC for reading temperature sensor, SPI for communicating with the Wiznet W5100 chip and last TIMER0 for ADC conversion and controlling the LED display.</p>
<p>The HTTP server protocol handshaking is implemented inside the infinite loop and I use the AVR ATMega328 microcontroller TIMER0 to implement the temperature reading through the ADC peripheral and to control the LED display indicator. The AVR ATMega328 TIMER0 peripheral is being set to execute the TIMER0 interrupt routine <strong>ISR(TIMER0_OVF_vect)</strong> on every 10 ms. Using the <strong>tenms</strong> counter variable, we reduce down to about 200 ms to make the LED display appear nicely in our eyes and at the same time we do the ADC conversion from the National Semiconductor LM35DZ precision centigrade temperature sensor. You could read more about reading the LM35DZ from my previous posted blog <a title="AVR LCD Thermometer Using ADC and PWM Project" href="http://www.ermicro.com/blog/?p=519" target="_blank">AVR LCD Thermometer Using ADC and PWM Project</a>.</p>
<p>You could easily activate the debugging mode by changing the <strong>_DEBUG_MODE</strong> definition value to <strong>1</strong> before compiling the program and use the serial terminal program to watch the result.</p>
<pre>// Debugging Mode, 0 - Debug OFF, 1 - Debug ON
#define _DEBUG_MODE   1</pre>
<p>This simple embedded Web server C code only took about 6196 bytes of the flash RAM and 816 bytes of the SRAM (debug mode turn off). The whole C code in this project is designed to be just a single file (i.e. <strong>wiznetweb.c</strong>) and all you need is the standard Win-AVR GCC includes file in order to make this program compiled successfully in the Atmel AVR Studio environment as shown on this following picture:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_15.jpg"><img class="alignnone size-full wp-image-1800" title="ethernet_15" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_15.jpg" alt="" width="575" height="354" /></a></p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_16.jpg"><img class="alignnone size-full wp-image-1825" title="ethernet_16" src="http://www.ermicro.com/blog/wp-content/uploads/2010/08/ethernet_16.jpg" alt="" width="575" height="470" /></a></p>
<p>Now you could enjoy the embedded web server project video on this tutorial. In this video I used both acer aspire notebook and BlackBerry Javelin 8900 smart phone to access the embedded web server which is connected to the Linksys Wireless-G 2.4GHz broadband router:</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/6Nmy1arDQl8&amp;hl=en_US&amp;fs=1" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/6Nmy1arDQl8&amp;hl=en_US&amp;fs=1" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p><strong>The Final Thought</strong></p>
<p>Actually the W5100 socket driver for the Atmel AVR microcontroller families has been provided by the Wiznet (version 1.5, as I published this blog), you could download the driver from their site (<strong>http://www.wiznet.co.kr</strong>). This driver (version 1.4) is adapted and used in the Arduino framework environment, known as the Arduino Ethernet Shield library.</p>
<p>Of course if I use the Wiznet socket driver in this project then you won&#8217;t learn anything, therefore to honor the spirit of experimenting inside every electronics hobbyists I decided to write my own version of the Wiznet W5100 socket driver for this project. Hopefully this project will give you a better understanding of how to integrate the Wiznet W5100 TCP/IP hardwire chip in your next embedded system application.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ermicro.com/blog/?feed=rss2&amp;p=1773</wfw:commentRss>
		</item>
		<item>
		<title>Stepping Into the 16-bit World with the Microchip 16-bit PIC24F16KA102 Family Microcontroller</title>
		<link>http://www.ermicro.com/blog/?p=1669</link>
		<comments>http://www.ermicro.com/blog/?p=1669#comments</comments>
		<pubDate>Mon, 31 May 2010 14:29:54 +0000</pubDate>
		<dc:creator>rwb</dc:creator>
		
		<category><![CDATA[Microcontroller]]></category>

		<category><![CDATA[ADC]]></category>

		<category><![CDATA[C30]]></category>

		<category><![CDATA[PIC]]></category>

		<category><![CDATA[PIC24]]></category>

		<category><![CDATA[PIC24K16KA102]]></category>

		<category><![CDATA[PWM]]></category>

		<guid isPermaLink="false">http://www.ermicro.com/blog/?p=1669</guid>
		<description><![CDATA[One of the commonly asked questions when we move to the bigger and powerful 16-bit microcontroller is do we really need it? As the 8-bit microcontroller is already suite almost all of our needs from a simple blinking LED to more sophisticated embedded application such as robotics.  Despite the debate whether to use the [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/PwUMCFCGk51Btu2pmmc74dxzvCU/0/da"><img src="http://feedads.g.doubleclick.net/~a/PwUMCFCGk51Btu2pmmc74dxzvCU/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/PwUMCFCGk51Btu2pmmc74dxzvCU/1/da"><img src="http://feedads.g.doubleclick.net/~a/PwUMCFCGk51Btu2pmmc74dxzvCU/1/di" border="0" ismap="true"></img></a></p><p>One of the commonly asked questions when we move to the bigger and powerful 16-bit microcontroller is do we really need it? As the 8-bit microcontroller is already suite almost all of our needs from a simple blinking LED to more sophisticated embedded application such as robotics.  Despite the debate whether to use the 8-Bit or 16-Bit microcontroller or perhaps just go straight to 32-bit microcontroller in our embedded system design, first I will show you the Microchip PIC18F25J11 (8-Bit) and PIC24F16KA102 (16-bit) basic comparison.<span id="more-1669"></span></p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_01.jpg"><img class="alignnone size-full wp-image-1670" title="pic24_intro_01" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_01.jpg" alt="" width="578" height="438" /></a></p>
<p>I use the Microchip PIC18F25J11 for the comparison as this microcontroller is considered as the 8-Bit highest end class microcontroller equipped with Microchip nanoWatt XLP technology while the Microchip PIC24F16KA102 is considered as the 16-bit entry level class microcontroller also equipped with Microchip nanoWatt XLP technology. Both microcontrollers have a build in hardware RTCC (Real Time Clock and Calendar), I2C, SPI and CTMU (Charge Time Measurement Unit) for capacitance sensing.</p>
<p>Now here my opinion from the electronics hobbyist perspective, things that we always consider when learning a new microcontroller are how easy to quick prototyping it on the breadboard, how much the cost and the last is the development tools to be used. From the basic comparison above you&#8217;ve noticed that for the learning purpose these two types of microcontroller are almost identical. On the other hand let&#8217;s consider the advantages of learning the 16-bit microcontroller; the PIC24F16KA102 microcontroller families share the same architecture as the well known Microchip dsPIC (digital signal controlling) microcontroller families, therefore by learning the 16-bit PIC24F16KA102 microcontroller family will open your knowledge and richer your experiences to more advance application of the embedded world system and surely you will love and enjoy it.</p>
<p><em>&#8230;to explore a new 16-bit microcontroller world&#8230; new datasheet&#8230; and &#8230;new programming&#8230; to boldly go when the only true electronics hobbyists dare to go&#8230;</em></p>
<p>On this tutorial we are going to step into the 16-bit embedded world and I will use the Microchip PIC24F16KA102 microcontroller as our learning tools. Actually Microchip has a nice product to help you quickly learn the PIC24F16KA102 families features (the Microchip nanoWatt XLP 16-bit Development Board - DM240311) as shown on the following picture:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_00.jpg"><img class="alignnone size-full wp-image-1671" title="pic24_intro_00" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_00.jpg" alt="" width="577" height="450" /></a></p>
<p>But for showing the basic PIC24F16KA102 microcontroller peripheral features in this tutorial, we could easily prototype it on the breadboard.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_03.jpg"><img class="alignnone size-full wp-image-1672" title="pic24_intro_03" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_03.jpg" alt="" width="579" height="187" /></a><br />
Ok now let list down the necessary electronics components and supported software for our stepping into 16-bit world tutorial and make sure you have the Microchip PIC24F16KA102 microcontroller datasheet near you:</p>
<p>1.	Resistors: 330 Ohm (9), 470 Ohm (1) and 10K (2)<br />
2.	Capacitors: 0.01uF (2) and 0.1uF (2)<br />
3.	LEDS: 3 mm Blue LED (8) and 3 mm Red LED (1)<br />
4.	One Microchip 16-bit PIC24F16KA102 microcontroller<br />
5.	Two momentary push button<br />
6.	One Breadboard and some breadboard&#8217;s jumper cables<br />
7.	One 2xAA Battery Holder with the 2x AA Alkaline battery (3 volt) for powering the circuit<br />
8.	Microchip PICKit3 programmer (used in this project)<br />
9.	Microchip MPLAB IDE version 8.47 and Microchip C30 Compiler version 3.30<br />
10.	Microchip PIC24 Reference Document: PIC24F16LA102/101 datasheet, AN39703a (CPU), AN39711b (I/O), AN39704a (Timer), AN39705b (ADC)</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_02.jpg"><img class="alignnone size-full wp-image-1675" title="pic24_intro_02" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_02.jpg" alt="" width="579" height="360" /></a></p>
<p>For quick prototyping this project on the breadboard I used the SIL LED display and SIL push button modules which you could read more about it in my previous posted blog <a title="Single In Line (SIL) LED Display for your Microcontroller Based Project" href="http://www.ermicro.com/blog/?p=1044" target="_blank">Single In Line (SIL) LED Display for your Microcontroller Project</a></p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_06.jpg"><img class="alignnone size-full wp-image-1676" title="pic24_intro_06" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_06.jpg" alt="" width="579" height="363" /></a><br />
<strong>The 16-bit PIC24 Family Microcontroller</strong></p>
<p>With 16-bit CPU internal data path, the PIC24 family microcontroller is being referred as the 16-bit class microcontroller. This means this microcontroller could process the 16-bit or 8-bit data with just a single instruction. As the result compared to the 8-bit class microcontroller the 16-bit class microcontroller will perform better for intensive arithmetic calculation application such as GUI (graphics user interface) display, TCP/IP application, USB embedded host and digital signal controlling (dsPIC microcontroller family).</p>
<p>Unlike the Microchip 8-bit (PIC12, PIC16 and PIC18) families microcontroller, the Microchip 16-bit PIC24 microcontroller families has a linear memory addressing design, therefore we don&#8217;t have to switch back and forth between memory &#8220;BANK&#8221; to access different peripheral registers as we do in the Microchip 8-bit families microcontroller; you could read more about programming the Microchip 8-bit microcontroller on my previous posted blog <a title=" Introduction to Microchip PIC Assembler Language – Part 1" href="http://www.ermicro.com/blog/?p=875" target="_blank">Introduction to Microchip PIC Assembler Language – Part 1</a>.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_08.jpg"><img class="alignnone size-full wp-image-1682" title="pic24_intro_08" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_08.jpg" alt="" width="576" height="404" /></a></p>
<p>The 16-bit Microchip PIC microcontroller also has 16 x 16-bit array working register known as W0 through W15 compared to only one working register in Microchip PIC 8-bit (WREG), this make the programming code is more efficient comparing to the PIC 8-bit microcontroller because all the arithmetic operation should go through the working register in order to be processed. The Microchip PIC24 microcontroller class also only use 2 cycles clock per instruction compared to 4 cycles clock per instruction on the PIC12/16/18 microcontroller class; this make the PIC24 at least twice more faster than PIC12/16/18 microcontroller. And the best of all, we still use the same Microchip powerful MPLAB integrated development environment for debugging, compiling and programming this 16-bit class microcontroller.</p>
<p>For more detail information about the Microchip 16-bit microcontroller families, I suggest to read more about it in the Microchip 16-bit microcontroller home <a title="Microchip 16-bit Home Page" href="http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;nodeId=2629&amp;param=en533462" target="_blank">website</a>. Ok let&#8217;s start with the hello 16-bit world C program, where we simply blink one LED using the PIC24F16KA102 microcontroller output port as shown on this following C code:</p>
<pre>/* ***************************************************************************
**  File Name    : helloworld.c
**  Version      : 1.0
**  Description  : First 16-bit PIC C Programming
**  Author       : RWB
**  Target       : PIC24F16KA102
**  Compiler     : MPLAB C30 C Compiler v3.23
**  IDE          : Microchip MPLAB IDE v8.46
**  Programmer   : PICKit3 (Firmware Suite Version 01.25.20)
**  Last Updated : 08 April 2010
** ***************************************************************************/
#include &lt;p24fxxxx.h&gt;</pre>
<pre>// PIC24F16KA102 Configuration bits
/*
** --------------------------------------------------------------------
** _FBS (Register FBS: 0xF80000)
**  BWRP_OFF - Boot Segment Write Protect Disable
**  BSS_OFF  - Boot Segment Code Protect Disable
** --------------------------------------------------------------------
** _FGS (Register FGS: 0xF80004)
**  GWRP_OFF - General Segment Write Protect Disable
**  GCP_OFF  - General Segment Code Protect Disable
** --------------------------------------------------------------------
** _FOSCSEL (Register FOSCSEL: 0xF80006)
**  FNOSC_FRCDIV - Fast RC Oscillator With Postscale (FRCDIV)
**  IESO_OFF - Two Speed Start-up Disable
** --------------------------------------------------------------------
** _FOSC (Register FOSC: 0xF80008)
**  FCKSM_CSECMD - Clock Switching Enabled, Clock Monitor Disabled
**  POSCFREQ_MS  - Mid Speed (100kHz - 8MHz)
**  OSCIOFNC_ON  - OSCO Pin Has Digital I/O Function (RA3)
**  POSCMOD_NONE - Primary disabled
**  SOSCSEL_SOSCLP - Low Power Secondary Oscillator
** --------------------------------------------------------------------
** _FWDT (Register FWDT (0xF8000A)
**  FWDTEN_OFF - Watchdog Timer (WDT) Disable
**  WINDIS_OFF - Non-Window WDT Mode
**  FWPSA_PR128 - WDT Prescale 1:128
**  WDTPS_PS32768 - WDT Postscale 1:32,768
** --------------------------------------------------------------------
** _FPOR (Register FPOR: 0xF8000C)
**  MCLRE_OFF - MCLR Disabled, RA5 Enabled
**  BORV_LPBOR - Low Power BOR (Brown Out Voltage)
**  BOREN_BOR3 - Enabled in hardware, SBOREN bit disabled
**  I2C1SEL_PRI - Use SCL1/SDA1 Pins For I2C1
**  PWRTEN_OFF - Power Up Timer Disabled
** --------------------------------------------------------------------
** _FICD (Register FICD: 0xF8000E)
**  BKBUG_OFF - Background Debugger Disabled
**  ICS_PGx3 - EMUC/EMUD share PGC3/PGD3
** --------------------------------------------------------------------
** _FDS (Register FDS: 0xF80010)
**  DSWDTEN_OFF - Deep Sleep WDT Disabled
**  DSBOREN_ON - Deep Sleep BOR Enabled
**  RTCOSC_SOSC - RTCC Oscillator select: Secondary Oscillator (SOSC)
**  DSWDTOSC_SOSC - Deep Sleep WDT Low Power RC Oscillator (LPRC)
**  DSWDTPS_DSWDTPSF - Deep Sleep WDT Postscale 1:2,147,483,648
**                     (25.7 Days)
** --------------------------------------------------------------------
*/
_FBS(BWRP_OFF &amp; BSS_OFF)
_FGS(GWRP_OFF &amp; GCP_OFF)
_FOSCSEL(FNOSC_FRCDIV &amp; IESO_OFF)
_FOSC(FCKSM_CSECMD &amp; POSCFREQ_MS &amp; OSCIOFNC_ON &amp; POSCMOD_NONE &amp; SOSCSEL_SOSCLP)
_FWDT(FWDTEN_OFF &amp; WINDIS_OFF &amp; FWPSA_PR128 &amp; WDTPS_PS32768)
_FPOR(MCLRE_ON &amp; BORV_LPBOR &amp; BOREN_BOR3 &amp; I2C1SEL_PRI &amp; PWRTEN_OFF)
_FICD(BKBUG_OFF &amp; ICS_PGx3)
_FDS(DSWDTEN_OFF &amp; DSBOREN_ON &amp; RTCOSC_SOSC &amp; DSWDTOSC_SOSC &amp; DSWDTPS_DSWDTPSF)</pre>
<pre>int main ( void )
{
  unsigned int i;

  // Use Internal 8 MHz Clock
  OSCCON = 0x0000;
  CLKDIV = 0x0000;</pre>
<pre>  // Initial the Ports Used
  TRISB  = 0xFF00;         // Set all RB0-RB7 Output, RB8-RB15 Input
  AD1PCFG = 0xffff;        // Set all to digital output
  ODCB   = 0x0000;         // Do not use open drain Output
  LATB   = 0x0000;         // Reset All PORT B</pre>
<pre>  for(;;) {
    PORTBbits.RB0=1;           // Turn On RB0
    for (i=0; i&lt;0xffff; i++);  // Delay Loop
    PORTBbits.RB0=0;           // Turn Off RB0
    for (i=0; i&lt;0xffff; i++);  // Delay Loop
  }</pre>
<pre>  return 0;
}</pre>
<pre>/* EOF: helloworld.c */</pre>
<p><a title="Stepping Into the 16-bit World with the Microchip 16-bit PIC24F16KA102 Family Microcontroller  by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4703937928/"><img src="http://farm5.static.flickr.com/4011/4703937928_f5f39953dc.jpg" alt="Stepping Into the 16-bit World with the Microchip 16-bit PIC24F16KA102 Family Microcontroller " width="578" height="440" /></a></p>
<p><strong></strong></p>
<p><strong>The PIC24F16KA102 Output Port</strong></p>
<p>One of the advantages using the PIC2F16KA102 microcontroller is that each of the output ports could be configured as the open drain output using the Open Drain Control Register (e.g. <strong>ODCB</strong>).  By setting this 16-bit register to logical &#8220;<strong>1</strong>&#8221; we could configure the port as the open drain output where the P-channel MOSFET transistor is disconnect from the microcontroller output port and leave the N-Channel MOSFET transistor with the open drain (not connected to Vcc). In order for the open drain output to work you need to supply the Pull-up resistor to power the N-Channel MOSFET transistor. Remember when you configure as the open drain output, the output port only could sink current through the N-Channel MOSFET transistor.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_04.jpg"><img class="alignnone size-full wp-image-1678" title="pic24_intro_04" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_04.jpg" alt="" width="583" height="288" /></a></p>
<p>The advantages of open drain output feature is, it has a very nice application in terms of making the microcontroller electronics support circuit design simpler such as wire ORing and it could be used as the logical voltage shifter to interface from 3.3 volt logic circuit to 5 volt circuit as shown on this following picture:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_05.jpg"><img class="alignnone size-full wp-image-1679" title="pic24_intro_05" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_05.jpg" alt="" width="579" height="309" /></a></p>
<p>Before using the PIC24F16KA102 microcontroller I/O port, first we have to set whether to use it as input or output port by setting the tri state register for PORTB (i.e. <strong>TRISB</strong>) as follow:</p>
<pre>// Initial the Ports Used
TRISB  = 0xFF00;         // Set all RB0-RB7 Output, RB8-RB15 Input
AD1PCFG = 0xffff;        // Set all to digital output
ODCB   = 0x0000;         // Do not use open drain Output
LATB   = 0x0000;         // Reset All PORT B</pre>
<p>By setting the <strong>TRISB </strong>register bits (<strong>TRISB1</strong> to <strong>TRISB15</strong>) to logical &#8220;<strong>1</strong>&#8221; we define the port as input (you could see it as &#8220;<strong>I</strong>&#8221; for <strong>input</strong>) and by setting it to logical &#8220;<strong>0</strong>&#8221; we define the port as output (you could see it as &#8220;<strong>O</strong>&#8221; for <strong>output</strong>), therefore by setting the TRISB register bits to <strong>0xFF00</strong>, mean we set the <strong>RB0</strong> to <strong>RB7</strong> as output ports while the <strong>RB8</strong> to <strong>RB15</strong> as the input ports.</p>
<p>Next we configure the ADC (analog to digital conversion) pin configuration register <strong>AD1PCFG</strong> bits to logical &#8220;<strong>0</strong>&#8220;, this mean we configure all of the 10 ADC channel port available in PIC24F16KA102 microcontroller as the digital I/O port. By setting the <strong>ODCB</strong> register bits to logical &#8220;<strong>0</strong>&#8221; we disable the output port open drain feature which we need it in this project to drive the display LED. The last is we simply set the PORTB latch register (<strong>LATB</strong>) to reset all the display LED. Writing to the <strong>PORTB</strong> latch register (<strong>LATB</strong>) will give the same result as we write directly to the <strong>PORTB</strong> register.</p>
<p><strong>Using the TIMER Peripheral for Generating Delay</strong></p>
<p>From the above example you&#8217;ve noticed that I used simply loops to create delay for displaying the LED; the more elegant way to make the delay is to take advantage of the PIC24F16KA102 microcontroller TIMER peripherals.  To show how the TIMER peripheral work I modified the above code and add the capabilities to read input from SW2 as well as using the TIMER1 peripheral as the base of our delay routine. Now let&#8217;s examine the following C code (ledseq.c):</p>
<pre>/* ***************************************************************************
**  File Name    : ledseq.c
**  Version      : 1.0
**  Description  : LED Sequence 16-bit PIC C Programming
**  Author       : RWB
**  Target       : PIC24F16KA102
**  Compiler     : MPLAB C30 C Compiler v3.23
**  IDE          : Microchip MPLAB IDE v8.46
**  Programmer   : PICKit3 (Firmware Suite Version 01.25.20)
**  Last Updated : 08 April 2010
** ***************************************************************************/
#include &lt;p24fxxxx.h&gt;</pre>
<pre>// PIC24F16KA102 Configuration bits
_FBS(BWRP_OFF &amp; BSS_OFF)
_FGS(GWRP_OFF &amp; GCP_OFF)
_FOSCSEL(FNOSC_FRCDIV &amp; IESO_OFF)
_FOSC(FCKSM_CSECMD &amp; POSCFREQ_MS &amp; OSCIOFNC_ON &amp; POSCMOD_NONE &amp; SOSCSEL_SOSCLP)
_FWDT(FWDTEN_OFF &amp; WINDIS_OFF &amp; FWPSA_PR128 &amp; WDTPS_PS32768)
_FPOR(MCLRE_ON &amp; BORV_LPBOR &amp; BOREN_BOR3 &amp; I2C1SEL_PRI &amp; PWRTEN_OFF)
_FICD(BKBUG_OFF &amp; ICS_PGx3)
_FDS(DSWDTEN_OFF &amp; DSBOREN_ON &amp; RTCOSC_SOSC &amp; DSWDTOSC_SOSC &amp; DSWDTPS_DSWDTPSF)</pre>
<pre>void delay_ms(unsigned short ms)
{
  unsigned short i;</pre>
<pre>  // TIMER1 Period = PR1 x 2 x Tosc x Prescale second
  // TIMER1 Period = 500 x 2 x 1/8000000 x 8 = 0.001 second = 1 ms

  PR1=500;                      // Maximum Counter
  for (i=0; i &lt;= ms;i++) {
    TMR1=0;                     // Reset TIMER1 Counter
    IFS0bits.T1IF=0;            // Clear TIMER1 Interrupt Flag
    T1CONbits.TON=1;            // Turn On TIMER1
    while(IFS0bits.T1IF != 1);  // Wait until TMR1 &gt; PR1 (Overflow)
    T1CONbits.TON=0;            // Turn Off TIMER1
  }
}</pre>
<pre>int main ( void )
{
  unsigned char mode,count,led_pointer;
  char led_pattern0[10]= {0b00000011,0b00001100,0b00110000,0b11000000,0b00000000,
                          0b11000000,0b00110000,0b00001100,0b00000011,0b00000000};
  char led_pattern1[10]= {0b00000001,0b00000100,0b00010000,0b01000000,0b00000000,
                          0b10000000,0b00100000,0b00001000,0b00000010,0b00000000};                                             

  // Use Internal 8 Mhz Clock
  OSCCON = 0x0000;
  CLKDIV = 0x0000;</pre>
<pre>  // Initial the Ports Used
  TRISB  = 0xFF00;         // Set all RB0-RB7 Output, RB8-RB15 Input
  CNPU1bits.CN11PUE = 0;   // Disable Week Pull Up on RB15/CN11 Input
  AD1PCFG = 0xFFFF;        // Set all to digital I/O
  ODCB   = 0x0000;         // Do not use open drain Output
  LATB   = 0x0000;         // Reset All PORT B</pre>
<pre>  // Initial TIMER1 for Delay
  T1CON=0x0010;            // TIMER1 Off, Prescale 1:8 using the internal clock</pre>
<pre>  // Initial Variables Used
  mode=0;
  count=0;
  led_pointer=0;</pre>
<pre>  for(;;) {
    // Reading the Switch Input on RB15
    if (PORTBbits.RB15 == 0) {
      if (count++ &gt; 5) {   // The Debouce Count
        count=0;
        mode=~mode;        // Toggle the mode variable
      }
    } 

    // Display the LED Pattern
    if (mode)
      PORTB=led_pattern1[led_pointer];
    else
      PORTB=led_pattern0[led_pointer];      

    if (led_pointer++ &gt; 9)
      led_pointer=0;</pre>
<pre>    delay_ms(50);          // Call Delay Function
  }
  return 0;
}</pre>
<pre>/* EOF: ledseq.c */</pre>
<p>The Microchip PIC24F16KA102 microcontroller have build in three independent 16-bit TIMER (TIMER1, TIMER2 and TIMER3) where TIMER2 and TIMER3 could be configured as a single 32-bit TIMER. On this tutorial we will use the 16-bit TIMER1 as our delay routine base.</p>
<p>As implied by its name, you could look the TIMER1 as an ordinary 16-bit counter named <strong>TMR1</strong>; where you could use the external or internal clock to pulse this counter controlled by the TIMER1 control register (<strong>T1CON</strong>). As the counter value is incremented by the pulse, the 16-bit counter output is constantly compared to the <strong>PR1</strong> register when <strong>TMR1</strong> reach the <strong>PR1</strong> value than the the TIMER1 will automatically set the TIMER1 interrupt flag (<strong>T1IF</strong>) bit on Intterupt Flag Status 0 register (<strong>IFS0</strong>).</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_09.jpg"><img class="alignnone size-full wp-image-1683" title="pic24_intro_09" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_09.jpg" alt="" width="581" height="409" /></a></p>
<p>By using the correct TIMER1 clock prescale and <strong>PR1</strong> value we could calculate the TIMER1 period using this following formula:</p>
<pre><strong>TIMER1 Period = PR1 x 2 x Tosc x Prescale </strong></pre>
<p>By assigning the <strong>PR1</strong> register to <strong>500</strong>, using 8 MHz internal system clock and with 1:8 prescale than the TIMER1 period could be calculated as follow:</p>
<pre><strong>TIMER1 Period = 500 x 2 x 1/8000000 x 8 = 0.001 second = 1 ms</strong></pre>
<p>Therefore by waiting for the <strong>T1IF</strong> bit to become logical &#8220;<strong>1</strong>&#8221; by the TIMER1 comparator inside the loop, we could create configurable delay function base on the TIMER1 as show on this following C code:</p>
<pre>....
PR1=500;                      // Maximum Counter
for (i=0; i &lt;= ms;i++) {
  TMR1=0;                     // Reset TIMER1 Counter
  IFS0bits.T1IF=0;            // Clear TIMER1 Interrupt Flag
  T1CONbits.TON=1;            // Turn On TIMER1
  while(IFS0bits.T1IF != 1);  // Wait until TMR1 &gt; PR1 (Overflow)
  T1CONbits.TON=0;            // Turn Off TIMER1
}</pre>
<p><strong>Reading the Input Port</strong></p>
<p>Reading the input port is done by first configured the I/O port as the input port. When we configure as the input port in the PIC24F16KA102 microcontroller, you could activate the weak pull-up resistor as shown on this following picture:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_10.jpg"><img class="alignnone size-full wp-image-1681" title="pic24_intro_10" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_10.jpg" alt="" width="578" height="286" /></a><br />
To enable or disable the weak pull-up resistor we could set the <strong>CNxPUE</strong> corresponding bits to either &#8220;<strong>1</strong>&#8221; (enable) or &#8220;<strong>0</strong>&#8221; (disable) in the PIC24F16KA102 microcontroller pull-up control register <strong>CNPU1</strong> or <strong>CNPU2</strong>. In this tutorial because we use the external pull-up resistor, therefore we disable this feature.</p>
<pre>TRISB  = 0xFF00;         // Set all RB0-RB7 Output, RB8-RB15 Input
CNPU1bits.CN11PUE = 0;   // Disable Week Pull Up on RB15/CN11 Input</pre>
<p>Now by reading the switch on <strong>RB15</strong> on PORTB and using simple debounce logic we could easily read the switch input status whether it being pressed or not. This status later on will toggle the mode variable to control how we display the LED pattern data on the PORTB output as shown on this following C code:</p>
<pre>// Reading the Switch Input on RB15
if (PORTBbits.RB15 == 0) {
  if (count++ &gt; 5) {   // The Debounce Count
    count=0;
    mode=~mode;        // Toggle the mode variable
  }
}</pre>
<p><strong>Using the ADC (Analog to Digital Converter) Peripheral</strong></p>
<p>On the last of this introduction to 16-bit world tutorial, I&#8217;m going to explain one of the most important peripheral features on every modern microcontroller which is the Analog to Digital Conversion (ADC) peripheral. Again by slightly modify the example code above I added the capability to adjust the speed of the LED display pattern by using the trimport. Now let&#8217;s take a look on this following C code (adcled.c):</p>
<pre>* ***************************************************************************
**  File Name    : adcled.c
**  Version      : 1.0
**  Description  : ADC LED Sequence 16-bit PIC C Programming
**  Author       : RWB
**  Target       : PIC24F16KA102
**  Compiler     : MPLAB C30 C Compiler v3.23
**  IDE          : Microchip MPLAB IDE v8.46
**  Programmer   : PICKit3 (Firmware Suite Version 01.25.20)
**  Last Updated : 10 April 2010
** ***************************************************************************/
#include &lt;p24fxxxx.h&gt;</pre>
<pre>// PIC24F16KA102 Configuration bits
_FBS(BWRP_OFF &amp; BSS_OFF)
_FGS(GWRP_OFF &amp; GCP_OFF)
_FOSCSEL(FNOSC_FRCDIV &amp; IESO_OFF)
_FOSC(FCKSM_CSECMD &amp; POSCFREQ_MS &amp; OSCIOFNC_ON &amp; POSCMOD_NONE &amp; SOSCSEL_SOSCLP)
_FWDT(FWDTEN_OFF &amp; WINDIS_OFF &amp; FWPSA_PR128 &amp; WDTPS_PS32768)
_FPOR(MCLRE_ON &amp; BORV_LPBOR &amp; BOREN_BOR3 &amp; I2C1SEL_PRI &amp; PWRTEN_OFF)
_FICD(BKBUG_OFF &amp; ICS_PGx3)
_FDS(DSWDTEN_OFF &amp; DSBOREN_ON &amp; RTCOSC_SOSC &amp; DSWDTOSC_SOSC &amp; DSWDTPS_DSWDTPSF)</pre>
<pre>void delay_ms(unsigned short ms)
{
  unsigned short i;</pre>
<pre>  // TIMER1 Period = PR1 x 2 x Tosc x Prescale second
  // TIMER1 Period = 500 x 2 x 1/8000000 x 8 = 0.001 second = 1 ms

  PR1=500;                      // Maximum Counter
  for (i=0; i &lt;= ms;i++) {
    TMR1=0;                     // Reset TIMER1 Counter
    IFS0bits.T1IF=0;            // Clear TIMER1 Interrupt Flag
    T1CONbits.TON=1;            // Turn On TIMER1
    while(IFS0bits.T1IF != 1);  // Wait until TMR1 &gt; PR1 (Overflow)
    T1CONbits.TON=0;            // Turn Off TIMER1
  }
}</pre>
<pre>int main ( void )
{
  unsigned char mode,count,led_pointer;
  char led_pattern0[10]= {0b00000011,0b00001100,0b00110000,0b11000000,0b00000000,
                          0b11000000,0b00110000,0b00001100,0b00000011,0b00000000};
  char led_pattern1[10]= {0b00000001,0b00000100,0b00010000,0b01000000,0b00000000,
                          0b10000000,0b00100000,0b00001000,0b00000010,0b00000000};
  unsigned int iDelay;

  // Use Internal 8 Mhz Clock
  OSCCON = 0x0000;
  CLKDIV = 0x0000;</pre>
<pre>  // Initial the Ports Used
  TRISB  = 0xFF00;         // Set all RB0-RB7 Output, RB8-RB15 Input
  CNPU1bits.CN11PUE = 0;   // Disable Week Pull Up on RB15/CN11 Input
  AD1PCFG = 0xFFFF;        // Set all to digital I/O
  ODCB   = 0x0000;         // Do not use open drain Output
  LATB   = 0x0000;         // Reset All PORT B</pre>
<pre>  // Initial TIMER1 for Delay
  T1CON=0x0010;            // TIMER1 Off, Prescale 1:8</pre>
<pre>  // Initial the ADC Peripheral
  AD1PCFGbits.PCFG12 = 0;  // Set AN10/RB12 as Analog Input
  AD1CON1 = 0x00E0;        // Auto convert after end of sampling
  AD1CON2 = 0x0000;        // Use MUXA, Internal AVdd and AVss as reference, 16-word buffer
  AD1CON3 = 0x1F02;        // Use System clock and Max sample time = 31 TAD, TAD = 3 Tcy
  AD1CSSL = 0x0000;        // No inputs are scanned
  AD1CON1bits.ADON=1;      // Turn ON ADC Peripheral</pre>
<pre>  // Initial Variables Used
  mode=0;
  count=0;
  led_pointer=0;
  iDelay=50;</pre>
<pre>  for(;;) {
    // Reading the Switch Input on RB15
    if (PORTBbits.RB15 == 0) {
      if (count++ &gt; 5) {       // The Debounce Count
        count=0;
        mode=~mode;            // Toggle the mode variable
      }
    }</pre>
<pre>    // Start ADC Conversion
    AD1CHS = 0x000C;               // Read Trimport on Channel 12 (AN12)
    AD1CON1bits.SAMP=1;            // Now Start the ADC Sampling
    while (AD1CON1bits.DONE != 1); // Wait for ADC conversion
    iDelay=ADC1BUF0;               // Get the 10-bit ADC Result

    // Display the LED Pattern
    if (mode)
      PORTB=led_pattern1[led_pointer];
    else
      PORTB=led_pattern0[led_pointer];      

    if (led_pointer++ &gt; 9)
      led_pointer=0;</pre>
<pre>    delay_ms(iDelay);          // Call Delay Function
  }
  return 0;
}</pre>
<pre>/* EOF: adcled.c */</pre>
<p>Differ from the 8-bit PIC12/16/18 microcontroller families, the 16-bit PIC24 microcontroller ADC peripheral support many advance features such as two independent multiplexing analog channels, multiple single channel A/D conversion or multiple multi channel A/D conversion for single interrupt, four type of A/D conversion data format result (integer, signed integer, fractional and signed fractional) and multiple trigger A/D data conversion method.</p>
<p>The PIC24 families A/D conversion required one A/D clock cycle (TAD) plus 2 additional clock cycles, thus the PIC24 SAR need at least 12 TAD cycles to do the 10-bit conversion. For correct A/D conversions, the A/D conversion clock (TAD) must be selected to ensure a minimum TAD time of 75 ns.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_11.jpg"><img class="alignnone size-full wp-image-1684" title="pic24_intro_11" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_11.jpg" alt="" width="579" height="334" /></a></p>
<p>Finally when the conversion is done, the result is loaded into the sixteen words of A/D result buffer array registers (<strong>ADC1BUF0</strong> to <strong>ADC1BUFF</strong>). On this tutorial I used one channel (<strong>AN12</strong>) with manual sample start A/D conversion method.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_12.jpg"><img class="alignnone size-full wp-image-1685" title="pic24_intro_12" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_12.jpg" alt="" width="577" height="339" /></a><br />
The ADC peripheral initialization begin by setting the input pin <strong>AN12</strong> (<strong>RB12</strong>) to be functioned as the analog input by setting <strong>PCFG12</strong> bit to logical &#8220;<strong>1</strong>&#8221; on ADC pin configuration register (<strong>AD1PCFG</strong>). Then we continue to ADC peripheral control registers <strong>AD1CON1</strong>, <strong>AD1CON2</strong> and <strong>AD1CON3</strong> as shown on this following C code:</p>
<pre>// Initial the ADC Peripheral
AD1PCFGbits.PCFG12 = 0;  // Set AN10/RB12 as Analog Input
AD1CON1 = 0x00E0;        // Auto convert after end of sampling
AD1CON2 = 0x0000;        // Use MUXA, Internal AVdd and AVss as reference, 16-word buffer
AD1CON3 = 0x1F02;        // Use internal RC clock and Max sample time = 31 TAD, TAD = 3 Tcy
AD1CSSL = 0x0000;        // No inputs are scanned
AD1CON1bits.ADON=1;      // Turn ON ADC Peripheral</pre>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_13.jpg"><img class="alignnone size-full wp-image-1686" title="pic24_intro_13" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_13.jpg" alt="" width="574" height="475" /></a></p>
<p>Because in this tutorial I used automatic A/D conversion after the sample and hold amplifier captured the analog signal input, therefore we have to set the A/D conversion source trigger bits (<strong>SSRC&lt;2:0&gt;</strong>) on <strong>AD1CON1</strong> register to &#8220;<strong>111</strong>&#8221; to use internal counter for automatic conversion after sampling and set the <strong>ASAM</strong> bits to logical &#8220;<strong>0</strong>&#8221; to instruct the sample and hold amplifier to begin the sampling when the <strong>SAMP</strong> bit on register <strong>AD1CON1</strong> is set to logical &#8220;<strong>1</strong>&#8220;.</p>
<p>Next on the <strong>AD1CON2</strong> register we choose the PIC24K16KA102 power supply as our A/D reference voltage by setting the voltage configuration bits (<strong>VCFG&lt;2:0&gt;</strong>) to &#8220;<strong>000</strong>&#8221; and configure the A/D result buffer as single word (16-bit) by setting the buffer mode bit (<strong>BUFM</strong>) to logical &#8220;<strong>0</strong>&#8220;. Finally on the <strong>AD1CON3</strong> register we configure the A/D conversion clock, because we need at least 75 ns for A/D conversion time (TAD), therefore by setting the conversion period selection bits (<strong>ADCS&lt;7:0&gt;</strong>) to &#8220;<strong>00000010</strong>&#8221; (<strong>ADCS = 2</strong>) we could calculate the TAD for 8MHz internal clock source (FOSC) as follow:</p>
<pre><strong>TAD = (ADCS + 1) x TCY</strong></pre>
<p>Because the instruction cycle clock is FOSC/2, therefore the instruction cycle period (TCY) will be 2/FOSC</p>
<pre><strong>TAD = 3 x 1 / 8000000 x 2 = 750 ns</strong></pre>
<p>Before starting the A/D conversion we have to turn on the ADC peripheral by setting the <strong>ADON</strong> bit on register <strong>AD1CON1</strong> to logical &#8220;<strong>1</strong>&#8221; then next selecting the analog input channel using the A/D channel selection register (<strong>AD1CHS</strong>) to <strong>AN12</strong> pin (<strong>0&#215;000C</strong>):</p>
<pre>...
// Start ADC Conversion
AD1CHS = 0x000C;               // Read Trimport on Channel 12 (AN12)
AD1CON1bits.SAMP=1;            // Now Start the ADC Sampling
while (AD1CON1bits.DONE != 1); // Wait for ADC conversion
iDelay=ADC1BUF0;               // Get the 10-bit ADC Result
...</pre>
<p>After starting the sample and hold amplifier for sampling the analog input, the ADC peripheral will automatically do the conversion after 31 TAD and by examining the <strong>DONE</strong> bit on the <strong>AD1CON</strong> register we could get the A/D conversion status and later read the A/D conversion result on the <strong>ADC1BUF0</strong> register. This A/D conversion result than will be used as the delay parameter for displaying the LED, therefore by adjusting the 10K trimport we could adjust the LED display speed.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_14.jpg"><img class="alignnone size-full wp-image-1687" title="pic24_intro_14" src="http://www.ermicro.com/blog/wp-content/uploads/2010/06/pic24_intro_14.jpg" alt="" width="578" height="347" /></a></p>
<p>After compiling and downloading all the HEX code into the PIC24F16KA102 microcontroller you will have similar result as shown on this following video:</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/l8HY8OOMZDg&amp;hl=en_US&amp;fs=1&amp;" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/l8HY8OOMZDg&amp;hl=en_US&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p><strong></strong></p>
<p><strong>The Final Thought</strong></p>
<p>As you walk through this 16-bit microcontroller world tutorial you will noticed that although the inside architecture between the 8-bit (Harvard) and 16-bit (Modified Harvard) Microchip PIC microcontroller is different, but they shared the same designed pattern such as the peripheral register name, the pins name and they all use the Microchip MPLAB integrated development environment. This make move from the 8-bit to the 16-bit class microcontroller become straightforward and easy. By using the 16-bit class microcontroller, you could bring broader range of application into the embedded world.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ermicro.com/blog/?feed=rss2&amp;p=1669</wfw:commentRss>
		</item>
		<item>
		<title>Building your own Simple Laser Projector using the Microchip PIC12F683 Microcontroller</title>
		<link>http://www.ermicro.com/blog/?p=1622</link>
		<comments>http://www.ermicro.com/blog/?p=1622#comments</comments>
		<pubDate>Mon, 12 Apr 2010 17:00:53 +0000</pubDate>
		<dc:creator>rwb</dc:creator>
		
		<category><![CDATA[Microcontroller]]></category>

		<category><![CDATA[ADC]]></category>

		<category><![CDATA[ASSEMBLER]]></category>

		<category><![CDATA[PIC12F683]]></category>

		<category><![CDATA[PWM]]></category>

		<guid isPermaLink="false">http://www.ermicro.com/blog/?p=1622</guid>
		<description><![CDATA[The 8 pins PIC12F683 microcontroller is one of the smallest members of the Microchip 8-bit microcontroller families but equipped with powerful peripherals such as ADC and PWM capabilities. This make this tiny microcontroller is suitable for controlling the DC motor speed. In order to demonstrate the PIC12F683 capabilities and to make this tutorial more attractive, [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/z9tQb8dYz7QiDo9tR0VkfGtrljU/0/da"><img src="http://feedads.g.doubleclick.net/~a/z9tQb8dYz7QiDo9tR0VkfGtrljU/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/z9tQb8dYz7QiDo9tR0VkfGtrljU/1/da"><img src="http://feedads.g.doubleclick.net/~a/z9tQb8dYz7QiDo9tR0VkfGtrljU/1/di" border="0" ismap="true"></img></a></p><p>The 8 pins PIC12F683 microcontroller is one of the smallest members of the Microchip 8-bit microcontroller families but equipped with powerful peripherals such as ADC and PWM capabilities. This make this tiny microcontroller is suitable for controlling the DC motor speed. In order to demonstrate the PIC12F683 capabilities and to make this tutorial more attractive, I decided to use the PIC12F683 microcontroller to generate simple and yet fascinating laser light show from a cheap keychain laser pointer.<span id="more-1622"></span></p>
<p>The basic of laser light shown in many entertainments club or park mostly use two method; the first one is to beam the laser shower on the spectators and the second one is to display the laser drawing pattern on the screen. On this tutorial we are going to build the laser projector that displays the spirograph pattern on the screen using the tiny Microchip PIC12F683 microcontroller.</p>
<p><a title="Building your own Simple Laser Projector using the Microchip PIC12F683 Microcontroller - 1 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4515039836/"><img src="http://farm3.static.flickr.com/2274/4515039836_a548fbba2c.jpg" alt="Building your own Simple Laser Projector using the Microchip PIC12F683 Microcontroller - 1" width="577" height="432" /></a></p>
<p>The principal of making the spirograph laser projector is to use at least two DC motors with the attached mirror on it, these mirrors then will deflect the laser beam from one DC motor mirror to the second DC motor mirror and then finally to the screen. By controlling each of the DC motors spinning speed we could generate a fascinating laser spirograph pattern on the screen as shown on this following picture.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_01.jpg"><img class="alignnone size-full wp-image-1623" title="laserlight_01" src="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_01.jpg" alt="" width="578" height="385" /></a></p>
<p>The best way to control the DC motor speed is to use the PWM (pulse wave modulation) signal to drive the DC Motor and because we want to change the DC motor speed manually, therefore we need to use the trimport or potentiometer to control each of the DC motors speed. Hmm, this sound like an appropriate job for the microcontroller but could we use this tiny 8 pins PIC12F683 microcontroller to handle this task?</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_03.jpg"><img class="alignnone size-full wp-image-1624" title="laserlight_03" src="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_03.jpg" alt="" width="577" height="185" /></a></p>
<p>From the datasheet you will notice that the Microchip PIC12F683 microcontroller only has one PWM output (<strong>CCP1</strong>) and four ADC input channel (<strong>AN0, AN1, AN2</strong> and <strong>AN3</strong>). Because we need two PWM output, therefore instead of using the PIC12F683 microcontroller build in PWM peripheral, in this tutorial I will show you how to generate the PWM signal base on the PIC12F683 microcontroller <strong>TIMER0</strong> peripheral. The following is the complete electronic schematic for the laser projector project.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_00.jpg"><img class="alignnone size-full wp-image-1625" title="laserlight_00" src="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_00.jpg" alt="" width="581" height="389" /></a></p>
<p>Ok before we go further with the detail; let&#8217;s list down the supporting peripherals needed to complete this laser projector project:</p>
<ul>
<li>Hot glue gun</li>
<li>Keychain laser pointer or any available laser pointer</li>
<li>3xAA, 4.5 volt battery holder for powering the laser pointer, please use the same voltage rate used by your laser pointer.</li>
<li>Two DC motor taken from the discarded PS2 Dual shock joystick</li>
<li>Two toy&#8217;s car tire taken from tamiya racing car</li>
<li>CD/DVD for the mirror, use a kitchen scissor to cut the CD/DVD into the two circle shape mirror with approximately 38 mm in diameter</li>
<li>Some toys plastic bricks for holding the DC motor</li>
<li>Breadboard</li>
<li>Hardboard or acrylic is used for the base of our laser projector</li>
<li>Double Tape</li>
</ul>
<p><a title="Building your own Simple Laser Projector using the Microchip PIC12F683 Microcontroller - 2 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4514403895/"><img src="http://farm3.static.flickr.com/2379/4514403895_f31dcf4892.jpg" alt="Building your own Simple Laser Projector using the Microchip PIC12F683 Microcontroller - 2" width="579" height="434" /></a></p>
<p><a title="Building your own Simple Laser Projector using the Microchip PIC12F683 Microcontroller - 3 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4515041300/"><img src="http://farm3.static.flickr.com/2779/4515041300_bc380b3a8c.jpg" alt="Building your own Simple Laser Projector using the Microchip PIC12F683 Microcontroller - 3" width="577" height="434" /></a></p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_13.jpg"><img class="alignnone size-full wp-image-1661" title="laserlight_13" src="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_13.jpg" alt="" width="578" height="434" /></a></p>
<p>The following are the electronic parts and the software development tools that I used to make this laser projector project:</p>
<ul>
<li>Resistor: 330 (3), 1K (5) and 10K (1)</li>
<li>Trimport: 10K (2)</li>
<li>Capacitor: 100nF (2) and 10nF (1)</li>
<li>One 100uH Inductor</li>
<li>Two 1N4148 Diodes</li>
<li>Two Blue and one Red Light Emitting Diode (LED)</li>
<li>Two 2N2222A transistors</li>
<li>One Mini Push Button Switch</li>
<li>One Microchip PIC12F683 Microcontroller</li>
<li>Microchip MPLAB v8.46 IDE (Integrated Development Environment)</li>
<li>Microchip Macro Assembler MPASMWIN.exe v5.35, mplink.exe v4.35</li>
<li>HI-TECH C Compiler for PIC10/12/16 MCUs (Lite Mode) V9.70</li>
<li>Microchip PICKit3 Programmer (Firmware Suite Version: 01.25.20)</li>
</ul>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_07.jpg"><img class="alignnone size-full wp-image-1626" title="laserlight_07" src="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_07.jpg" alt="" width="579" height="395" /></a></p>
<p>This project is aim as the continuing lessons to my previous posted blog <a title="Introduction to Microchip PIC Assembler Language – Part 1" href="http://www.ermicro.com/blog/?p=875" target="_blank">Introduction to PIC Assembly Language Part-1</a> and <a title="Introduction to Microchip PIC Assembler Language – Part 2" href="http://www.ermicro.com/blog/?p=909" target="_blank">Introduction to PIC Assembly Language Part-2</a>, therefore I used the same PIC12F683 board presented in the part 2 which you could down load both of the electronic schematic and the PCB layout designed in Eagle CAD format. The other interesting feature of this laser projector project is; besides the PIC assembly code I also provide the C language version of this project for the C language lover and is compiled with the HI-TECH C Compiler (recently the HI-TECH Software has been acquired by Microchip). This C language version could be used for learning as well as the embedded system programming language comparison.</p>
<p>In this project I also use a new Microchip PICKit3 programmer but of course you could use the Microchip PICKit2 programmer to download the hex code to the PIC12F683 microcontroller flash.</p>
<p>The following is the Laser Projector code in PIC Assembly Language:</p>
<pre>;******************************************************************************
;  File Name    : laserlight.asm
;  Version      : 1.0
;  Description  : Laser Light Show Project
;  Author       : RWB
;  Target       : Microchip PIC12F683 Microcontroller
;  Compiler     : Microchip Assembler (MPASMWIN.exe v5.35, mplink.exe v4.35)
;  IDE          : Microchip MPLAB IDE v8.46
;  Programmer   : PICKit3 (Firmware Suite Version: 01.25.20)
;  Last Updated : 01 April 2010
; *****************************************************************************
#include &lt;p12F683.inc&gt;</pre>
<pre>__config (_INTRC_OSC_NOCLKOUT &amp; _WDT_OFF &amp; _PWRTE_OFF &amp; _MCLRE_OFF &amp; _CP_OFF &amp; _IESO_OFF &amp; _FCMEN_OFF)</pre>
<pre>#define MAX_TMR0 0xFB
#define MAX_COUNT .200
#define MAX_DEBOUNCE 0x0A
#define MAX_TBLINDEX 0x0A</pre>
<pre>; Define variables used
     cblock 0x20
Delay:2                      ; Define two registers for the Delay and Delay + 1
mode                         ; Operation Mode
pwm_count                    ; Hold the Main PWM Counter
pwm_m1                       ; Hold the PWM width for Motor 1
pwm_m2                       ; Hold the PWM width for Motor 2
keycount                     ; Debounce Count
tableindex                   ; Table Index for Auto PWM
     endc</pre>
<pre>; Define variable use for storing STATUS and WREG register
     cblock 0x70             ; Use unbanked RAM, available both in Bank0 and Bank1
saved_w
saved_status
     endc</pre>
<pre>; Start the Light show Assembler Code here
     org 0x00                ; We always start at flash address 0
     goto Main               ; Jump to Main</pre>
<pre>     org 0x04                ; 0x04: Start PIC Interrupt Address
PIC_ISR:                     ; Start the PIC Interrupt Service Routine
     movwf   saved_w         ; Save Working Register
     movf    STATUS,w        ; Save Status Register
     movwf   saved_status</pre>
<pre>; Check the TIMER0 Interrupt here
     btfss   INTCON,T0IF
     goto    ExitISR         ; If (T0IF != 1) then Exit ISR
     bcf     STATUS,RP0      ; Select Registers at Bank 0
     incf    pwm_count       ; pwm_count++
     movlw   MAX_COUNT
     subwf   pwm_count,w     ; if (pwm_count &lt; MAX_COUNT) then CheckPWM
     btfss   STATUS,C        ; else clear GP1 and GP2
     goto    CheckPWM
     bcf     GPIO,GP1        ; GPIO1=0
     bcf     GPIO,GP2        ; GPIO2=0
     goto    ExitPWM</pre>
<pre>CheckPWM:
     movf    pwm_m1,w
     subwf   pwm_count,w
     btfsc   STATUS,Z        ; if (pwm_count == pwm_m1) then Set GP1
     bsf     GPIO,GP1        ; Set GP1 Bit
CheckM2:
     movf    pwm_m2,w
     subwf   pwm_count,w
     btfsc   STATUS,Z        ; if (pwm_count == pwm_m2) then Set GP2
     bsf     GPIO,GP2        ; Set GP2 bit
ExitPWM:
     bcf     INTCON,T0IF     ; clear the TIMER0 interrupt flag
     movlw   MAX_TMR0
     movwf   TMR0            ; TMR0 = MAX_TMR0</pre>
<pre>ExitISR:
     movf    saved_status,w
     movwf   STATUS          ; Restore STATUS Register
     swapf   saved_w,f
     swapf   saved_w,w       ; Restore W Register
     retfie                  ; Return from Interrupt</pre>
<pre>Main:
     bsf     STATUS,RP0      ; Select Registers at Bank 1
     movlw   0x70
     movwf   OSCCON          ; Set the internal clock speed to 8 MHz
     movlw   0x39            ; GP1 and GP2 Output, GP0,GP3,GP4 and GP5 as Input
     movwf   TRISIO          ; TRISIO = 0x39        

     bcf     STATUS,RP0      ; Select Registers at Bank 0
     movlw   0x07
     movwf   CMCON0          ; Turn off Comparator (GP0, GP1, GP2)
     clrf    GPIO

; Now we Set the ADC Peripheral
     bsf     STATUS,RP0      ; Select Registers at Bank 1
     movlw   0x79            ; Set AN0 (GP0) and AN3 (GP4) as Analog Input
     movwf   ANSEL           ; Using the Internal Clock (FRC)  

; Now we set the TIMER0 Peripheral
; TIMER0 Period = 1/FSOC x 4 x Prescale x TMR0
     movlw   0x00            ; Use TIMER0 Prescaler 1:2, Internal Clock
     movwf   OPTION_REG      ; OPTION_REG = 0x00</pre>
<pre>     bcf     STATUS,RP0      ; Select Registers at Bank 0
     movlw   MAX_TMR0
     movwf   TMR0            ; TMR0=MAX_TMR0      

; Initial the variables used
     clrf    mode            ; Default mode = 0, Light Show Off
     clrf    pwm_count       ; pwm_count = 0
     clrf    pwm_m1          ; pwm_m1 = 0
     clrf    pwm_m2          ; pwm_m2 = 0
     clrf    keycount        ; keycount = 0
     clrf    tableindex      ; tableindex = 0</pre>
<pre>; Activate the Interrupt
     bsf     INTCON,GIE      ; Enable Global Interrupt</pre>
<pre>MainLoop:
     btfsc   GPIO,GP5        ; Now we check the Button
     goto    CheckMode       ; if (GP5 != 0) goto CheckMode
     movlw   0x01
     addwf   keycount        ; keycount=keycount + 1
     movf    keycount,w
     sublw   MAX_DEBOUNCE
     btfss   STATUS,C        ; if (keycount &gt; MAX_DEBOUNCE) goto KeyPressed
     goto    KeyPressed
     goto    CheckMode       ; else CheckMode</pre>
<pre>KeyPressed:
     clrf    keycount        ; keycount=0
     incf    mode            ; mode++
     movlw   0x03
     subwf   mode,w          ; W = mode - 0x03
     btfsc   STATUS,C        ; if (mode &gt;= 0x03)
     clrf    mode            ; mode=0;
     movlw   0x01            ; else check the mode
     subwf   mode,w
     btfss   STATUS,C        ; if (mode &gt;= 0x01) goto TurnOn
     goto    TurnOff         ; else goto TurnOff
     goto    TurnOn</pre>
<pre>TurnOff:
     bcf     INTCON,T0IE     ; Disable TIMER0 Interrupt
     clrf    pwm_count       ; pwm_count = 0
     clrf    pwm_m1          ; pwm_m1 = 0
     clrf    pwm_m2          ; pwm_m2 = 0
     bcf     GPIO,GP1
     bcf     GPIO,GP2
     movlw   .250
     call    DelayMs         ; DelayMs(250)
     movlw   .250
     call    DelayMs         ; DelayMs(250)
     goto    CheckMode</pre>
<pre>TurnOn:
     bsf     INTCON,T0IE     ; Enable TIMER0 Interrupt                    

CheckMode:
     movlw   0x01
     subwf   mode,w
     btfss   STATUS,Z        ; if (mode == 1) goto ShowMode1
     goto    CheckMode2
     goto    ShowMode1</pre>
<pre>CheckMode2:
     movlw   0x02
     subwf   mode,w
     btfss   STATUS,Z        ; if (mode == 2) goto ShowMode2
     goto    KeepLoop
     goto    ShowMode2</pre>
<pre>ShowMode1:                   ; Used ADC for PWM
     movlw   B'00000001'     ; Left Justify and turn on the ADC peripheral, channel 0 (AN0)
     movwf   ADCON0          ; Vreff=Vdd
     bsf     ADCON0,GO       ; Start the ADC Conversion on channel 0 (AN0)
     btfss   ADCON0,GO       ; while(GO == 1)
     goto    $-1             ; Keep Loop
     call    Delay1ms</pre>
<pre>     movlw   B'00000001'     ; Left Justify and turn on the ADC peripheral, channel 0 (AN0)
     movwf   ADCON0          ; Vreff=Vdd
     bsf     ADCON0,GO       ; Start the ADC Conversion on channel 0 (AN0)
     btfss   ADCON0,GO       ; while(GO == 1)
     goto    $-1             ; Keep Loop
     movf    ADRESH,w        ; Conversion Done, Read ADRESH
     movwf   pwm_m1          ; pwm_m1 = ADRESH
     call    Delay1ms

     movlw   B'00001101'     ; Left Justify and turn on the ADC peripheral, channel 3 (AN3)
     movwf   ADCON0          ; Vreff=Vdd
     bsf     ADCON0,GO       ; Start the ADC Conversion on channel 3 (AN3)
     btfss   ADCON0,GO       ; while(GO == 1)
     goto    $-1             ; Keep Test
     call    Delay1ms</pre>
<pre>     movlw   B'00001101'     ; Left Justify and turn on the ADC peripheral, channel 3 (AN3)
     movwf   ADCON0          ; Vreff=Vdd
     bsf     ADCON0,GO       ; Start the ADC Conversion on channel 3 (AN3)
     btfss   ADCON0,GO       ; while(GO == 1)
     goto    $-1             ; Keep Test
     movf    ADRESH,w        ; Conversion Done, Read ADRESH
     movwf   pwm_m2          ; pwm_m2 = ADRESH
     call    Delay1ms
     goto    KeepLoop</pre>
<pre>ShowMode2:                   ; Used Predefined Value for PWM
     movf    tableindex,w
     call    tablepwm1       ; Call tablepwm1
     movwf   pwm_m1          ; Assigned it to pwm_m1
     movlw   .30
     call    DelayMs         ; DelayMs(30)

     movf    tableindex,w
     call    tablepwm2       ; Call tablepwm2
     movwf   pwm_m2          ; Assigned it to pwm_m2
     movlw   .30
     call    DelayMs         ; DelayMs(30)</pre>
<pre>     incf    tableindex      ; tableindex++
     movlw   MAX_TBLINDEX
     subwf   tableindex,w
     btfss   STATUS,C        ; if (tableindex &gt;= 0x0A) then tableindex = 0
     goto    KeepLoop
     clrf    tableindex      ; tableindex = 0</pre>
<pre>KeepLoop:
     goto    MainLoop        ; Goto MainLoop</pre>
<pre>; Predefined value table for Automatic PWM
tablepwm1:
     addwf   PCL,f
     retlw   0x10
     retlw   0x5A
     retlw   0x9A
     retlw   0x20
     retlw   0x40
     retlw   0x8A
     retlw   0x82
     retlw   0x30
     retlw   0x58
     retlw   0xAA</pre>
<pre>tablepwm2:
     addwf   PCL,f
     retlw   0x70
     retlw   0x8A
     retlw   0x2A
     retlw   0x30
     retlw   0x1C
     retlw   0x2A
     retlw   0x4B
     retlw   0xA0
     retlw   0x18
     retlw   0x2A</pre>
<pre>;----------------- DelayMs: Millisecond Delay Subroutine ----------------------
; Paramater: WREG = delay amount in milisecond, max: 255 millisecond
DelayMs:
     movwf   Delay + 1
DelayLoop:
     call    Delay1ms
     decfsz  Delay + 1,f     ; Decrease Delay + 1, If zero skip the next instruction
     goto    DelayLoop       ; Not zero goto DelayLoop
     return                  ; return to the caller</pre>
<pre>;----------------- Delay1ms: 1 ms Delay Subroutine ---------------------------
Delay1ms:                    ; Total Delay: 1998 x 0.5us ~ 1 ms
     movlw   0x99
     movwf   Delay
DelayLoop1:
     decfsz  Delay,f         ; Decrease Delay, If zero skip the next instruction
     goto    DelayLoop1
DelayLoop2:
     decfsz  Delay,f         ; Decrease Delay, If zero skip the next instruction
     goto    DelayLoop2      ; Not zero goto DelayLoop2
DelayLoop3:
     decfsz  Delay,f         ; Decrease Delay, If zero skip the next instruction
     goto    DelayLoop3      ; Not zero goto DelayLoop2
     return                  ; Return to the caller</pre>
<pre>     end
; EOF: laserlight.asm</pre>
<p><a title="Building your own Simple Laser Projector using the Microchip PIC12F683 Microcontroller - 4 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4514405509/"><img src="http://farm3.static.flickr.com/2082/4514405509_4d21ed9fb7.jpg" alt="Building your own Simple Laser Projector using the Microchip PIC12F683 Microcontroller - 4" width="577" height="431" /></a></p>
<p>The following is the Laser Projector Project code in C Language version:</p>
<pre>// ***************************************************************************
//  File Name    : laserlight.c
//  Version      : 1.0
//  Description  : Laser Light Show Project
//  Author       : RWB
//  Target       : Microchip PIC12F683 Microcontroller
//  Compiler     : HI-TECH C PIC10/12/16 MCUs (Lite Mode) V9.70
//  IDE          : Microchip MPLAB IDE v8.46
//  Programmer   : PICKit3 (Firmware Suite Version: 01.25.20)
//  Last Updated : 03 April 2010
// ***************************************************************************
#include &lt;pic.h&gt;</pre>
<pre>/*   PIC Configuration Bit:
**   INTIO     - Using Internal RC No Clock
**   WDTDIS    - Wacthdog Timer Disable
**   PWRTEN    - Power Up Timer Enable
**   MCLRDIS   - Master Clear Disable
**   UNPROTECT - Code Un-Protect
**   UNPROTECT - Data EEPROM Read Un-Protect
**   BORDIS    - Borwn Out Detect Disable
**   IESODIS   - Internal External Switch Over Mode Disable
**   FCMDIS    - Monitor Clock Fail Safe Disable
*/
__CONFIG(INTIO &amp; WDTDIS &amp; PWRTEN &amp; MCLRDIS &amp; UNPROTECT \
  &amp; UNPROTECT &amp; BORDIS &amp; IESODIS &amp; FCMDIS);</pre>
<pre>// Using Internal Clock of 8 MHz
#define FOSC 8000000L</pre>
<pre>#define MAX_COUNT 200
#define MAX_TMR0 0xFB
#define MAX_DEBOUNCE 0x0A
#define MAX_TBLINDEX 0x0A</pre>
<pre>unsigned char pwm_count=0;
unsigned char pwm_m1=0;
unsigned char pwm_m2=0;
unsigned char tablepwm1[10]={0x10,0x5A,0x9A,0x20,0x40,0x8A,0x82,0x30,0x58,0xAA};
unsigned char tablepwm2[10]={0x70,0x8A,0x2A,0x30,0x1C,0x2A,0x4B,0xA0,0x18,0x2A};
unsigned char tableindex=0;</pre>
<pre>/* The Delay Function */
#define	delay_us(x) { unsigned char us; \
			        us = (x)/(12000000/FOSC)|1; \
			        while(--us != 0) continue; }</pre>
<pre>void delay_ms(unsigned int ms)
{
  unsigned char i;
  do {
    i = 4;
    do {
      delay_us(164);
    } while(--i);
  } while(--ms);
}</pre>
<pre>static void interrupt isr(void)
{
  if(T0IF) {	       // TIMER0 Interrupt Flag
    pwm_count++;       // PWM Count Increment
    if (pwm_count &gt;= MAX_COUNT) {
      pwm_count=0;
      GPIO1=0;         // Turn off GP1
      GPIO2=0;         // Turn off GP2
    }</pre>
<pre>    if (pwm_count == pwm_m1) {
      GPIO1=1;         // Turn On GP1
    }    

    if (pwm_count == pwm_m2) {
      GPIO2=1;         // Turn On GP2
    }      

    TMR0 = MAX_TMR0;   // Initial Value for TIMER0 Interrupt
    T0IF = 0;	       // Clear TIMER0 interrupt flag
  }
}</pre>
<pre>void main(void)
{
  unsigned char mode,keycount;</pre>
<pre>  OSCCON=0x70;         // Select 8 MHz internal clock</pre>
<pre>  /* Initial Port Used */
  TRISIO = 0x39;       // GP1 and GP2 Output, GP0,GP3,GP4 and GP5 as Input
  CMCON0 = 0x07;       // Turn off Comparator (GP0, GP1, GP2)
  GPIO = 0x00;         // Turn Off all IO</pre>
<pre>  /* Init ADC Peripheral */
  ANSEL = 0x79;        // Set AN0 (GP0) and AN3 (GP4) as Analog Input, Internal Clock</pre>
<pre>  /* Init TIMER0: TIMER0 Period = 1/FSOC x 4 x Prescale x TMR0*/
  OPTION = 0b00000000; // 1:2 Prescale
  TMR0=MAX_TMR0 ;</pre>
<pre>  /* Init Variable Used */
  pwm_count=0;
  pwm_m1=0;
  pwm_m2=0;
  mode=0;
  keycount=0;
  tableindex=0;</pre>
<pre>  GIE =1;		       // Enable Global Interrupt

  for(;;) {
    // Display the LED
    if (GPIO5 == 0) {
      keycount++;
      if (keycount &gt; MAX_DEBOUNCE) {
        keycount=0;
        mode = mode + 1;
        if (mode &gt; 2) mode = 0;</pre>
<pre>        if (mode &gt;= 0x01) {
          T0IE = 1;	       // Enable TIMER0 Interrupt on Overflow
        } else {
          T0IE = 0;	       // Disable TIMER0 Intterupt on Overflow
          pwm_count=0;
          pwm_m1=0;
          pwm_m2=0;
          GPIO1=0;             // Turn off GP1
          GPIO2=0;             // Turn off GP2
          delay_ms(500);
        }
      }
    }</pre>
<pre>    if (mode == 1) {
      /* Read the ADC here */
      ADCON0=0b00000001;       // select left justify result. ADC port channel AN0
      GODONE=1;	               // initiate conversion on the channel 0</pre>
<pre>      while(GODONE) continue;  // Wait for ldr_left conversion done
      pwm_m1=ADRESH;           // Read 8 bits MSB, Ignore 2 bits LSB in ADRESL</pre>
<pre>      delay_ms(1);</pre>
<pre>      /* Read the ADC here */
      ADCON0=0b00001101;       // select left justify result. ADC port channel AN3
      GODONE=1;	         // initiate conversion on the channel 4</pre>
<pre>      while(GODONE) continue;  // Wait for ldr_left conversion done
      pwm_m2=ADRESH;           // Read 8 bits MSB, Ignore 2 bits LSB in ADRESL     

      delay_ms(1);
    }</pre>
<pre>    if (mode == 2) {
      pwm_m1=tablepwm1[tableindex];
      delay_ms(10);
      pwm_m2=tablepwm2[tableindex];
      delay_ms(10);
      tableindex++;
      if (tableindex &gt;= MAX_TBLINDEX)
        tableindex = 0;
    }
  }
}</pre>
<pre>/* EOF: laserlight.c */</pre>
<p><strong>Generating the PWM (Pulse Width Modulation)</strong></p>
<p>The simple way to generate the PWM output is to take advantage of the PIC12F683 microcontroller PWM build in feature, but because the PIC12F683 only support one PWM output therefore we need to find a way to imitate this peripheral behavior in the firmware. Most of microcontroller PWM peripheral use TIMER peripheral to generate the pulse; in the PIC12F683 the PWM generator use the <strong>TIMER2</strong> to generate the PWM output (<strong>CCP1</strong>) as shown on this following picture:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_08.jpg"><img class="alignnone size-full wp-image-1628" title="laserlight_08" src="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_08.jpg" alt="" width="580" height="301" /></a></p>
<p>When <strong>TIMER2</strong> start counting, the value of the <strong>TMR2</strong> register (<strong>TIMER2</strong> counter register) is constantly compared to both the <strong>PR2</strong> and <strong>CCPR1L</strong> registers. When <strong>TMR2</strong> equal to <strong>CCPR1L</strong> value the <strong>CCP1</strong> output will be reset and when <strong>TMR2</strong> equal to <strong>PR2</strong> value it will set the <strong>CCP1</strong> output. Therefore by changing the <strong>CCPR1L</strong> register value as shown on the picture above, we could change the PWM duty cycle (pulse width). You could read more about the principal of using the PIC microcontroller PWM peripheral on my previous posted blog <a title="H-Bridge Microchip PIC Microcontroller PWM Motor Controller" href="http://www.ermicro.com/blog/?p=706" target="_blank">H-Bridge Microchip PIC Microcontroller PWM Motor Controller.</a></p>
<p>Using the same principal I made the PWM generator in firmware base on the Microchcip PIC12F683 microcontroller <strong>TIMER0</strong> peripheral as this following block diagram:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_09.jpg"><img class="alignnone size-full wp-image-1629" title="laserlight_09" src="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_09.jpg" alt="" width="578" height="318" /></a></p>
<p>The basic principal of the <strong>TIMER0</strong> base PWM generator is to use the <strong>TIMER0</strong> overflow interrupt to increase our PWM counter variable <strong>pwm_count</strong> and if this variable reaches the <strong>MAX_COUNT</strong> (200) then we will reset both the <strong>GP1</strong> and <strong>GP2</strong> output pins. If not then we will compare it with the PWM duty cycle control variables <strong>pwm_m1</strong> and <strong>pwm_m2</strong>, if equal then we simply set the <strong>GP1</strong> or <strong>GP2</strong> output pins. Using this principal now we could easily generate an efficient PWM signal base on the PIC12F683 microcontroller <strong>TIMER0</strong> peripheral.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_10.jpg"><img class="alignnone size-full wp-image-1630" title="laserlight_10" src="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_10.jpg" alt="" width="576" height="396" /></a></p>
<p>Now take a look at the PIC assembler code that implements the PIC12F683 microcontroller <strong>TIMER0 </strong>peripheral base PWM generator:</p>
<pre>; Check the TIMER0 Interrupt here
     btfss   INTCON,T0IF
     goto    ExitISR         ; If (T0IF != 1) then Exit ISR
     bcf     STATUS,RP0      ; Select Registers at Bank 0
     incf    pwm_count       ; pwm_count++
     movlw   MAX_COUNT
     subwf   pwm_count,w     ; if (pwm_count &lt; MAX_COUNT) then CheckPWM
     btfss   STATUS,C        ; else clear GP1 and GP2
     goto    CheckPWM
     bcf     GPIO,GP1        ; GPIO1=0
     bcf     GPIO,GP2        ; GPIO2=0
     goto    ExitPWM</pre>
<p>This routine first examine if this interrupt is generated by <strong>TIMER0</strong> peripheral by checking the <strong>T0IF</strong> bit on the <strong>INTCON</strong> register, if this interrupt come from <strong>TIMER0</strong> then the <strong>T0IF</strong> bit will be set (logical &#8220;<strong>1</strong>&#8220;) then we continue to increase the <strong>pwm_count</strong> variable and if it reach the <strong>MAX_COUNT</strong> we reset the <strong>GP1</strong> and <strong>GP2</strong> output pins.  Next if the <strong>pwm_count</strong> is not reaches the <strong>MAX_COUNT</strong>, then compare its value to the <strong>pwm_m1</strong> and <strong>pwm_m2 </strong>variables as shown on this following code:</p>
<pre>CheckPWM:
     movf    pwm_m1,w
     subwf   pwm_count,w
     btfsc   STATUS,Z        ; if (pwm_count == pwm_m1) then Set GP1
     bsf     GPIO,GP1        ; Set GP1 Bit
CheckM2:
     movf    pwm_m2,w
     subwf   pwm_count,w
     btfsc   STATUS,Z        ; if (pwm_count == pwm_m2) then Set GP2
     bsf     GPIO,GP2        ; Set GP2 bit</pre>
<p>When the <strong>pwm_count</strong> equal to <strong>pwm_m1</strong> or <strong>pwm_m2</strong> then we set (logical &#8220;<strong>1</strong>&#8220;) each of the <strong>GP1</strong> and <strong>GP2</strong> output pins.</p>
<p>To calculate the PWM period, first we have to calculate the <strong>TIMER0</strong> period using this following formula:</p>
<p><strong>TIMER0 Period = 1/Fosc x 4 x Prescale x (TMR0 + 1)</strong></p>
<p>In this tutorial we use 1:2 prescale, TMR0 = 251 (0xFB) and 8 MHz internal oscillator, therefore the <strong>TIMER0</strong> will be interrupted every:</p>
<p>TIMER0 Period = 1/8000000 x 4 x 2 x (256 - 251) = <strong>0.000005 second</strong></p>
<p>Every time  the <strong>TIMER0</strong> interrupted (<strong>TMR0</strong> overflow),  we will increase the <strong>pwm_count</strong> variable until it reach the <strong>MAX_COUNT</strong> (200), then we will reset each of the <strong>GP1</strong> and <strong>GP2</strong> output pins. Therefore the approximately PWM period could be calculated as follow:</p>
<p>PWM Period = MAX_COUNT x 0.000005 seconds = 200 x 0.000005 = <strong>0.001 second</strong><br />
PWM Frequency = 1 / T = 1/0.001 = 1000 Hz = <strong>1KHz</strong></p>
<p><strong>Changing the DC Motor Speed</strong></p>
<p>This laser projector project has two mode for controlling both of the DC motors speed, the first one is to use the trimports (VR1 and VR2) where we read the voltage level produce by these trimports (voltage divider circuit) using the PIC12F683 microcontroller Analog to Digital Converter (ADC) peripheral, then apply these ADC values to change each of the DC motor speed.  The second one is to use the fixed value stored in the lookup table and then assign these values to change each of the DC motor speed.</p>
<p>The Microchip PIC12F683 microcontroller has four ADC channels available; on this project we use the channel 0 (<strong>AN0</strong>) and the channel 4 (<strong>AN3</strong>). By choosing the require ADC channel on the <strong>ADCON0</strong> register (the ADC control register) and selecting the left justify result (<strong>ADFM=0</strong>) we could read the voltage value produced by each of the VR1 and VR2 trimports in <strong>ADRESH</strong> register and assign these value to <strong>pwm_m1</strong> and <strong>pwm_m2</strong> variables as shown on this following PIC assembly code:</p>
<pre>ShowMode1:                   ; Used ADC for PWM
     movlw   B'00000001'     ; Left Justify and turn on the ADC peripheral, channel 0 (AN0)
     movwf   ADCON0          ; Vreff=Vdd
     bsf     ADCON0,GO       ; Start the ADC Conversion on channel 0 (AN0)
     btfss   ADCON0,GO       ; while(GO == 1)
     goto    $-1             ; Keep Loop
     call    Delay1ms</pre>
<pre>     movlw   B'00000001'     ; Left Justify and turn on the ADC peripheral, channel 0 (AN0)
     movwf   ADCON0          ; Vreff=Vdd
     bsf     ADCON0,GO       ; Start the ADC Conversion on channel 0 (AN0)
     btfss   ADCON0,GO       ; while(GO == 1)
     goto    $-1             ; Keep Loop
     movf    ADRESH,w        ; Conversion Done, Read ADRESH
     movwf   pwm_m1          ; pwm_m1 = ADRESH
     call    Delay1ms

     movlw   B'00001101'     ; Left Justify and turn on the ADC peripheral, channel 3 (AN3)
     movwf   ADCON0          ; Vreff=Vdd
     bsf     ADCON0,GO       ; Start the ADC Conversion on channel 3 (AN3)
     btfss   ADCON0,GO       ; while(GO == 1)
     goto    $-1             ; Keep Test
     call    Delay1ms</pre>
<pre>     movlw   B'00001101'     ; Left Justify and turn on the ADC peripheral, channel 3 (AN3)
     movwf   ADCON0          ; Vreff=Vdd
     bsf     ADCON0,GO       ; Start the ADC Conversion on channel 3 (AN3)
     btfss   ADCON0,GO       ; while(GO == 1)
     goto    $-1             ; Keep Test
     movf    ADRESH,w        ; Conversion Done, Read ADRESH
     movwf   pwm_m2          ; pwm_m2 = ADRESH
     call    Delay1ms
     goto    KeepLoop</pre>
<p>For more information about using the PIC ADC peripheral you could read my previous posted blog <a title="PIC Analog to Digital Converter C Programming" href="http://www.ermicro.com/blog/?p=660" target="_blank">PIC Analog to Digital Converter C Programming</a>.</p>
<p>Next is the automatic mode, this mode will assign the predetermined value for each PWM value on the PIC program flash. By using the PIC assembler &#8220;<strong>retlw k</strong>&#8221; (return literal value k on the <strong>W</strong> register) instruction we could easily retrieve these values. The following PIC assembly code shows how we do it:</p>
<pre>ShowMode2:                   ; Used Predefined Value for PWM
     movf    tableindex,w
     call    tablepwm1       ; Call tablepwm1
     movwf   pwm_m1          ; Assigned it to pwm_m1
     movlw   .30
     call    DelayMs         ; DelayMs(30)
     movf    tableindex,w
     call    tablepwm2       ; Call tablepwm2
     movwf   pwm_m2          ; Assigned it to pwm_m2
     movlw   .30
     call    DelayMs         ; DelayMs(30)

     incf    tableindex      ; tableindex++
     movlw   MAX_TBLINDEX
     subwf   tableindex,w
     btfss   STATUS,C        ; if (tableindex &gt;= 0x0A) then tableindex = 0
     goto    KeepLoop
     clrf    tableindex      ; tableindex = 0
...
...
; Predefined value table for Automatic PWM
tablepwm1:
     addwf   PCL,f
     retlw   0x10
     retlw   0x5A
     retlw   0x9A
     retlw   0x20
     retlw   0x40
     retlw   0x8A
     retlw   0x82
     retlw   0x30
     retlw   0x58
     retlw   0xAA</pre>
<pre>tablepwm2:
     addwf   PCL,f
     retlw   0x70
     retlw   0x8A
     retlw   0x2A
     retlw   0x30
     retlw   0x1C
     retlw   0x2A
     retlw   0x4B
     retlw   0xA0
     retlw   0x18
     retlw   0x2A</pre>
<p>The principal here is to modify the <strong>PCL</strong> (program counter loading) register. The <strong>PCL</strong> is the PIC12F683 microcontroller program counter least significant byte and together with the <strong>PCLATH</strong> register forming the PIC12F683 microcontroller 13-bits wide program counter. The &#8220;<strong>goto</strong>&#8221; command behavior could be achieved by manipulating the <strong>PCL</strong> content, for example:</p>
<pre>     movlw   0x01
     call    tablepwm2
     nop</pre>
<pre>tablepwm2:
     addwf   PCL,f
     retlw   0x70
     retlw   0x8A
     retlw   0x2A</pre>
<p>When the PIC microcontroller run the &#8220;<strong>call tablepwm</strong>&#8221; instruction then it immediately change the program counter (<strong>PCL</strong>) to the program address pointed by &#8220;<strong>tablepwm2</strong>&#8221; label which is the &#8220;<strong>addwf PCL,f</strong>&#8221; instrunction. When the PIC microcontroller execute this instruction it will add the content of <strong>W</strong> register (0&#215;01) to the <strong>PCL</strong> register and the PIC microcontroller immediately jump to the &#8220;<strong>PCL + 0&#215;01</strong>&#8221; program address. Next it will execute the &#8220;<strong>retlw 0&#215;8A</strong>&#8221; instruction and it simply return to the caller with <strong>0&#215;8A</strong> value on the <strong>W</strong> register.</p>
<p>Therefore by increasing the <strong>tableindex</strong> (the lookup table pointer) variables we could easily assigned the predetermined value on each of the <strong>pwm_m1</strong> and <strong>pwm_m2</strong> variables.</p>
<p><strong>The basic of PIC Assembly if condition</strong></p>
<p>One of the confusing part in understanding the PIC assembly instruction especially for the beginner is &#8220;<em>how to implement the if condition</em>&#8221; in the assembly code. In this laser projector project PIC assembly code you noticed that it use quite a lot of &#8220;<em>if condition</em>&#8221; in order to make it work. The basic of PIC assembler &#8220;<em>if condition</em>&#8221; could be constructed using this following first template code:</p>
<pre>movf   var_1,w   ; w = var_1
subwf  var_2,w   ; w = var_2 - var_1
btfss  STATUS,C  ; if (var_2 &gt;= var_1) goto label_2
goto   label_1   ; else goto label_1
goto   label_2</pre>
<p>The first code (<strong>movf var_1,w</strong>)  instructs the PIC microcontroller to put the content of <strong>var_1</strong> into the <strong>W</strong> (working) register. The second code (<strong>subwf var_2,w</strong>) instructs the PIC microcontroller to subtract the content of <strong>var_2</strong> variable with the content of <strong>W</strong> register and put the result in the <strong>W</strong> register or we could write down as:</p>
<pre><strong>W = var_2 - var_1</strong></pre>
<p>The third code (<strong>btfss</strong> - <strong>b</strong>it <strong>t</strong>est <strong>f</strong> register, <strong>s</strong>kip if <strong>s</strong>et) instructs the PIC microcontroller to examine if the <strong>C</strong> (carry) bit in the <strong>STATUS</strong> register is set (logical &#8220;<strong>1</strong>&#8220;), if it set then skip one instruction (the PIC microcontroller actually will automatically replace the next instruction <strong>goto label_1</strong> with the <strong>nop</strong> instruction) and it will execute the &#8220;<strong>goto label_2</strong>&#8221; code. If the <strong>C</strong> bit is clear (logical &#8220;<strong>0</strong>&#8220;) then it will execute the &#8220;<strong>goto label_1</strong>&#8221; code.</p>
<p>The <strong>C</strong> (carry) bit in <strong>STATUS</strong> register will always set (logical &#8220;<strong>1</strong>&#8220;) if the value of <strong>var_2</strong> variable is greater or equal to <strong>var_1</strong> variable. Next, the second &#8220;<em>if condition</em>&#8221; template is shown on this following code:</p>
<pre>movf   var_1,w   ; w = var_1
subwf  var_2,w   ; w = var_2 - var_1
btfss  STATUS,Z  ; if (var_2 == var_1) goto label_2
goto   label_1   ; else goto label_1
goto   label_2</pre>
<p>This time we will examine the <strong>Z</strong> (zero) bit in the <strong>STATUS</strong> register, The <strong>Z</strong> (zero) bit will always set (logical &#8220;<strong>1</strong>&#8220;) when the <strong>var_2</strong> value equal to <strong>var_1</strong> value. Last, the third &#8220;<em>if condition</em>&#8221; template is shown on this following code:</p>
<pre>movlw  0x08      ; w = 0x08
subwf  var_1,w   ; w = var_1 - 0x08
btfss  STATUS,C  ; if (var_1 &gt;= 0x08) goto label_2
goto   label_1   ; else goto label_1
goto   label_2</pre>
<p>This third template is just a variation of the first template, this time we load the literal value <strong>0&#215;08</strong> on the <strong>W </strong>register and compare it with the <strong>var_1</strong> variable. By using just these three &#8220;<em>if condition</em>&#8221; templates, you could easily implement many types of the &#8220;<em>if condition</em>&#8221; in your code. The key to understand and implement the PIC assembly &#8220;<em>if condition</em>&#8221; instruction successfully in your code is to use these templates consistently, once you understand than you could modify or combine it with the <strong>btfsc</strong> (<strong>b</strong>it <strong>t</strong>est <strong>f</strong> register, <strong>s</strong>kip if <strong>c</strong>lear) instruction or any PIC assembler instruction that effecting the <strong>C</strong> and <strong>Z</strong> bits in the <strong>STATUS</strong> register to make your assembly code more efficient.</p>
<p>To make the PIC assembly code easier to understand, in this laser projector tutorial I put many comments in the code and you could also compare it with the equivalent C language code provided for this project. The C language code uses the same variable name as in the PIC assembly code for easier comparison purpose.</p>
<p><strong>Inside the PIC Assembler Code</strong></p>
<p>The laser projector project use the PIC12F683 microcontroller TIMER0 and ADC peripheral to control each of the DC motors speed. These following instructions are used to initial the PIC12F683 peripherals:</p>
<pre>Main:
     bsf     STATUS,RP0      ; Select Registers at Bank 1
     movlw   0x70
     movwf   OSCCON          ; Set the internal clock speed to 8 MHz
     movlw   0x39            ; GP1 and GP2 Output, GP0,GP3,GP4 and GP5 as Input
     movwf   TRISIO          ; TRISIO = 0x39        

     bcf     STATUS,RP0      ; Select Registers at Bank 0
     movlw   0x07
     movwf   CMCON0          ; Turn off Comparator (GP0, GP1, GP2)
     clrf    GPIO

; Now we Set the ADC Peripheral
     bsf     STATUS,RP0      ; Select Registers at Bank 1
     movlw   0x79            ; Set AN0 (GP0) and AN3 (GP4) as Analog Input
     movwf   ANSEL           ; Using the Internal Clock (FRC)  

; Now we set the TIMER0 Peripheral
; TIMER0 Period = 1/FSOC x 4 x Prescale x TMR0
     movlw   0x00            ; Use TIMER0 Prescaler 1:2, Internal Clock
     movwf   OPTION_REG      ; OPTION_REG = 0x00</pre>
<pre>     bcf     STATUS,RP0      ; Select Registers at Bank 0
     movlw   MAX_TMR0
     movwf   TMR0            ; TMR0=MAX_TMR0     

; Initial the variables used
     clrf    mode            ; Default mode = 0, Light Show Off
     clrf    pwm_count       ; pwm_count = 0
     clrf    pwm_m1          ; pwm_m1 = 0
     clrf    pwm_m2          ; pwm_m2 = 0
     clrf    keycount        ; keycount = 0
     clrf    tableindex      ; tableindex = 0</pre>
<pre>; Activate the Interrupt
     bsf     INTCON,GIE      ; Enable Global Interrupt</pre>
<p>The first instruction is to set the <strong>OSCCON</strong> (oscillator control) register to use the 8MHz internal clock, next we set the <strong>TRISIO</strong> (the three state I/O controller buffer) register for the input and output port and turn off the comparator peripheral on <strong>CMCON0</strong> (comparator control) register. After resetting the <strong>GPIO</strong> (general purpose I/O) register we continue to setting the <strong>ANSEL</strong> (analog select) register for the ADC peripheral and <strong>OPTION_REG</strong> for the <strong>TIMER0</strong> peripheral and initial all the variables used in the program by clearing them.</p>
<p>Right before entering the infinite loop we activate the <strong>GIE</strong> (global interrupt enable) bit in the <strong>INTCON</strong> (interrupt control) register so the <strong>TIMER0</strong> interrupt could occur when the <strong>TMR0</strong> (<strong>TIMER0</strong> counter) register overflow (more than 255).</p>
<p>Inside the infinite loop (the MainLoop label), we continuously checked the <strong>mode</strong> variable; the <strong>mode</strong> value is used to determine the program behavior. When the <strong>mode</strong> value is <strong>0</strong> then the program simply continuously loop, when the <strong>mode</strong> value is <strong>1</strong> then the program will use the ADC value to control each of the DC motor speed and when the <strong>mode</strong> value is <strong>2</strong> then the program will use the look-up table to control the PWM signal (automatic mode).</p>
<p><strong> The PIC Assembly Code and the C Code Comparison</strong></p>
<p>Since in this project I use the PIC Assembly language and the C language for the firmware; it would be interest to compare both the HEX program sizes. The following is the HEX code produced by the Microchip PIC Assembler and HI-TECH C PRO (Lite Mode) compiler.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_11.jpg"><img class="alignnone size-full wp-image-1633" title="laserlight_11" src="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_11.jpg" alt="" width="578" height="464" /></a></p>
<p>The HEX code produce by the HI-TECH C PRO compiler is 356 Word (623 Byte) in &#8220;<em>Lite Mode</em>&#8220;, but if the C code is compiled in &#8220;<em>PRO Mode</em>&#8221; the size will be 40% smaller or it&#8217;s about 214 Word (374.5 Bytes), on the other hand the Microchip Macro Assembler produce 180 Word (315 Bytes) for the HEX code. Now you could see, that if we use the professional C language compiler it could produce a very small HEX code where it almost equal to the HEX code produced by the Assembly language.<br />
<strong></strong></p>
<p><strong>Downloading the HEX Code</strong></p>
<p>After compiling and simulating your code it&#8217;s time to download the code using the Microchip PICKit3 programmer, connect the PICKit3 ICSP (Microchip In Circuit Serial Programming) port to your PIC12F683 microcontroller pins then from the Programmer menu Select <strong>Programmer -&gt;</strong> <strong>PicKit3</strong> and the select the <strong>Programmer -&gt; Program</strong> menu to download your HEX code into the PIC12F683 microcontroller flash.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_12.jpg"><img class="alignnone size-full wp-image-1634" title="laserlight_12" src="http://www.ermicro.com/blog/wp-content/uploads/2010/04/laserlight_12.jpg" alt="" width="577" height="451" /></a></p>
<p>Now it&#8217;s time to enjoy the laser projector project presented in this following video:</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/hJHFHMFqtGc&amp;hl=en_US&amp;fs=1&amp;" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/hJHFHMFqtGc&amp;hl=en_US&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p><strong></strong></p>
<p><strong>The Final Though</strong></p>
<p>In this laser projector project both the PIC Assembly and C code demonstrate that the 2048 bytes of the 8-bit Microchip PIC12F683 microcontroller flash is still adequate to handle more advanced application such as the PICAXE-08M microcontroller from the <strong>Revolution Education Ltd</strong>, this microcontroller is based on the Microchip PIC12F683 microcontroller and they put a powerful BASIC (Beginners for All Purpose Symbolic Code) interpreter inside, so the PICAXE-08M could be used as a complete development framework for the embedded system application. You could read more information about the PICAXE microcontroller in my previous posted blog <a title="Introduction to the Embedded System with PICAXE Microcontroller" href="http://www.ermicro.com/blog/?p=1334" target="_blank">Introduction to the embedded system with PICAXE microcontroller</a>.</p>
<p>Using the TIMER peripheral to generate the PWM signal as shown on this project could be applied to almost any microcontrollers available today. The basic principal shown here also could be easily modified to drive the multiple servos output as the servo use the PWM signal to control its movement.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ermicro.com/blog/?feed=rss2&amp;p=1622</wfw:commentRss>
		</item>
		<item>
		<title>Working with the Comparator Circuit</title>
		<link>http://www.ermicro.com/blog/?p=1578</link>
		<comments>http://www.ermicro.com/blog/?p=1578#comments</comments>
		<pubDate>Mon, 15 Mar 2010 14:28:58 +0000</pubDate>
		<dc:creator>rwb</dc:creator>
		
		<category><![CDATA[Electronics]]></category>

		<category><![CDATA[ASSEMBLER]]></category>

		<category><![CDATA[ATTINY25]]></category>

		<category><![CDATA[AVR]]></category>

		<category><![CDATA[COMPARATOR]]></category>

		<category><![CDATA[LM339]]></category>

		<category><![CDATA[OP-AMPS]]></category>

		<guid isPermaLink="false">http://www.ermicro.com/blog/?p=1578</guid>
		<description><![CDATA[Sometimes in the embedded system world we need to process the analog world and sending the signal to the microcontroller when the analog signal exceed some predetermine limit we&#8217;ve set.  Some example of this situation is to send the interrupt signal to the microcontroller operation when the temperature is already exceeds certain limit or [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/ssgE2ebbalWxij8oiNdOd2OM0W4/0/da"><img src="http://feedads.g.doubleclick.net/~a/ssgE2ebbalWxij8oiNdOd2OM0W4/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/ssgE2ebbalWxij8oiNdOd2OM0W4/1/da"><img src="http://feedads.g.doubleclick.net/~a/ssgE2ebbalWxij8oiNdOd2OM0W4/1/di" border="0" ismap="true"></img></a></p><p>Sometimes in the embedded system world we need to process the analog world and sending the signal to the microcontroller when the analog signal exceed some predetermine limit we&#8217;ve set.  Some example of this situation is to send the interrupt signal to the microcontroller operation when the temperature is already exceeds certain limit or the light intensity exceeds certain bright level. This is when the comparator circuit becomes handy as it&#8217;s designed specially for this purpose.<span id="more-1578"></span></p>
<p>Today the modern comparator is easily found in the form of integrated circuit such as the popular National Semiconductor LM339 quad (four) comparator 14 pins dual in line (DIL) package bellow:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_00.jpg"><img class="alignnone size-full wp-image-1579" title="comp_00" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_00.jpg" alt="" width="584" height="390" /></a><br />
When you look closely to the comparator symbol, some of you will recognize it as the Op-Amp (Operational Amplifier) symbol, so what make this comparator differ from its big brother; Op-Amps is designed to accept the analog signal and outputting the analog signal while the comparator only outputting the digital signal; although the ordinary Op-Amp could be used as the comparator such as the popular National Semiconductor LM324 quad Op-Amps, but the real comparator is designed to have a faster switching time comparing to the multipurpose Op-Amps. Therefore you could say that the comparator is the modified version of the Op-Amps which specially designed to give the digital output.</p>
<p><strong>1. Basic Comparator Circuit</strong></p>
<p>The comparator circuit work by simply taking two analog inputs, comparing them and produce the logical output high &#8220;<strong>1</strong>&#8221; or low &#8220;<strong>0</strong>&#8220;.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_01.jpg"><img class="alignnone size-full wp-image-1580" title="comp_01" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_01.jpg" alt="" width="583" height="305" /></a></p>
<p>By applying the analog signal to the comparator <strong>+ input</strong> called &#8220;<strong>non-inverting</strong>&#8221; and <strong>- input</strong> called &#8220;<strong>inverting</strong>&#8220;, the comparator circuit will compared this two analog signal, if the analog input on <strong>+ input</strong> is greater than the analog input on <strong>- input</strong> (inverting) then the output will swing to the logical &#8220;<strong>1</strong>&#8221; and this will make the open collector transistor Q8 on the LM339 equivalent circuit above to turn ON. When the analog input on <strong>+ input</strong> (non inverting) is less than the analog input on <strong>- input</strong> (inverting) then the comparator output will swing to the logical &#8220;<strong>0</strong>&#8221; (the Q8 transistor turn OFF). Furthermore on this tutorial, I will use the term <strong>non-inverting</strong> and <strong>inverting</strong> instead of <strong>+ input</strong> and <strong>- input</strong> on the comparator analog input.</p>
<p>As you&#8217;ve seen from the LM339 equivalent circuit picture above, the LM339 use an open collector transistor <strong>Q8</strong> for its output, therefore we have to use what is called &#8220;<strong>pull-up</strong>&#8221; resistor connected to the <strong>Q8</strong> collector lead with the Vcc in order to make this <strong>Q8</strong> transistor work. The maximum current that could flow on this <strong>Q8</strong> transistor (output sink current) according to the LM339 datasheet is about 18 mA.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_02.jpg"><img class="alignnone size-full wp-image-1581" title="comp_02" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_02.jpg" alt="" width="575" height="448" /></a></p>
<p>The testing circuit simply connects the comparator inverting input to the voltage divider R1 and R2, with 5 Volt voltage supply the <strong>V-</strong> could be calculated as follow:</p>
<p><strong>V- = (R2 / (R1 + R2)) x Vcc<br />
V- = (10 / 20) x 5 volt = 2.5 volt</strong></p>
<p>For more information about the voltage divider circuit please refer to my previous posted blog<a title="Basic Resistor Circuit" href="http://www.ermicro.com/blog/?p=104" target="_blank"> Basic Resistor Circuit</a>.</p>
<p>The comparator non-inverting input is connected to the 10 K trimport which is also forming the voltage divider circuit where we could adjust the <strong>V+</strong> voltage start from 5 volt down to 0 volt. First when the <strong>V+</strong> is equal to Vcc (5 volt) the comparator output will swing to the logical high (Vout = Vcc) because the <strong>V+</strong> is greater than <strong>V-</strong> (2.5 volt). This will switch the <strong>Q8</strong> transistor OFF and the LED will turn OFF. When the voltage <strong>V+</strong> drop bellow 2.5 volt, the comparator output will swing to the logical low (Vout = GND) and this will switch the <strong>Q8</strong> transistor ON and the LED will turn ON.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_06.jpg"><img class="alignnone size-full wp-image-1583" title="comp_06" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_06.jpg" alt="" width="581" height="262" /></a></p>
<p>By swapping the analog input; the R1 and R2 voltage divider connected to the non-inverting input (<strong>V+</strong>) and the trimport connected to the inverting input (<strong>V-</strong>) we will get the opposite output result.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_03.jpg"><img class="alignnone size-full wp-image-1584" title="comp_03" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_03.jpg" alt="" width="580" height="302" /></a></p>
<p>Again using the voltage divider principal the voltage on the non-inverting input (<strong>V+</strong>) is about 2.5 volt, therefore if we start the inverting input voltage (<strong>V-</strong>) at 5 volt; the <strong>V+</strong> is lower than the <strong>V-</strong>, this will make the comparator output will swing to the logical low (the <strong>Q8</strong> transistor ON) and the LED will turn ON. When we adjust the <strong>V-</strong> down bellow the 2.5 volt (<strong>V+</strong>) than the comparator output will swing to the logical high (the <strong>Q8</strong> transistor OFF)  because the <strong>V+</strong> now is greater than <strong>V-</strong> and the LED will turn OFF. You could see all of this experiment on the video at the end of this tutorial.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_07.jpg"><img class="alignnone size-full wp-image-1585" title="comp_07" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_07.jpg" alt="" width="579" height="261" /></a></p>
<p>As you&#8217;ve seen from the experiment above, the comparator is usually used to compare one variable analog signal with predetermined analog signal or we could call it as the reference voltage. Therefore by placing the reference voltage to the inverting or non-inverting input we could manage the comparator output accordance to our need. This comparator circuit is also known as the <strong>zero crossing detector</strong> circuit.</p>
<p>The following schema show you how we utilized the zero crossing detector circuit in the infra-red reflective detector usually found in many robotics projects.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_08.jpg"><img class="alignnone size-full wp-image-1586" title="comp_08" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_08.jpg" alt="" width="581" height="312" /></a></p>
<p>When the infra-red LED beam reflected back to the photo transistor, the photo transistor will turn ON; this will make the reference voltage (<strong>V+</strong>) become greater than the input voltage (<strong>V-</strong>). This condition will make the comparator give a logical high to the microcontroller I/O port. When the infra-red LED beam is blocked, the photo transistor will turn OFF; this will make the input voltage (<strong>V-</strong>) become greater than the reference voltage (<strong>V+</strong>) and the comparator will give a logical low to the microcontroller I/O port.</p>
<p>Hmm, by applying the circuit schema above to all the comparators in the LM339 package (four comparators), we could make a very sensitive infrared reflector sensors for sensing the black/white line used in many line follower or line maze solver robot.</p>
<p><strong>2. Comparator with the Positive Feedback</strong></p>
<p>One of the drawbacks with the zero crossing detector on the circuit above is; it&#8217;s very sensitive to the noise signal (small interference signal) on its input as shown on this following picture:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_09.jpg"><img class="alignnone size-full wp-image-1588" title="comp_09" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_09.jpg" alt="" width="579" height="289" /></a></p>
<p>Because the comparator basically is an uncompensated high gain operational amplifier, therefore this could make the comparator to start oscillate or continuously giving high and low output. To compensate this behavior we could apply a small amount of voltage feedback to the non-inverting input as shown on this following schema:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_10.jpg"><img class="alignnone size-full wp-image-1589" title="comp_10" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_10.jpg" alt="" width="580" height="313" /></a></p>
<p>To analyze this circuit first we assume the output voltage is high; in order to meet this condition the <strong>V-</strong> (inverting input) voltage should be lower than the <strong>V+</strong> (non-inverting input) voltage. When the <strong>V-</strong> voltage increases toward the <strong>V+</strong> voltage, this will drive the Vout toward ground which will also shift the <strong>V+</strong> voltage through the R3 resistor. Because the <strong>V+</strong> is the non-inverting input to the comparator; this feedback will help to drive down the Vout to ground even faster. The opposite condition also occur when the <strong>V-</strong> voltage decrease, because of the R3 resistor feedback on the <strong>V+</strong> input; this feedback will shift the <strong>V+</strong> voltage input will ensure a faster switching result to logical high (Vcc) on the Vout.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_11.jpg"><img class="alignnone size-full wp-image-1590" title="comp_11" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_11.jpg" alt="" width="573" height="364" /></a></p>
<p>The V+ voltage upper threshold (<strong>VUT</strong>) to make the Vout voltage to swing to ground could be calculated as this following formula.</p>
<p><strong>VUT = (R2 / (R2 + R1||R3)) x Vout</strong></p>
<p>We assume the Vout equal to Vcc when the output is high then we could have this following formula:</p>
<p><strong>VUT = (R2 / (R2 + R1||R3)) x Vcc</strong></p>
<p>and</p>
<p><strong>R1||R3 = (R1 x R3) / (R1 + R3).</strong></p>
<p>Substituting the R1||R3 (R1 parallel R3) inside this equation we will get this following formula:</p>
<p><strong>VUT = (Vcc x R2 x (R1 + R3)) / (R1 x R2 + R1 x R3 + R2 x R3).</strong></p>
<p>The V+ voltage lower threshold (VLT) to make the Vout voltage to swing to high (Vcc) could be calculated as this following formula.</p>
<p><strong>VLT = (R2||R3 / (R2||R3 + R1)) x Vcc</strong><br />
and</p>
<p><strong>R2||R3 = (R2 x R3) / (R2 + R3).</strong></p>
<p>Again by substituting the R2||R3 (R2 parallel R3) inside this equation we will get this following formula:</p>
<p><strong>VLT = (Vcc x R2 x R3) / (R1 x R2 + R1 x R3 + R2 x R3).</strong></p>
<p>The different between the upper and lower threshold voltage is called the hysteresis voltage:</p>
<p><strong>VH = VUT - VLT</strong></p>
<p>Therefore by applying the upper and lower threshold formula, we could get the hysteresis voltage as follow:</p>
<p><strong>VH = (Vcc x R1 x R2) / (R1 x R2 + R1 x R3 + R2 x R3).</strong></p>
<p>To insure that Vout will swing between Vcc and ground we have to choose:</p>
<p><strong>R3 &gt; R pull-up </strong>and<strong> R pull-up &lt; R load</strong></p>
<p>Therefore by assigning the R pull-up = 10K, R1 = R2 = R3 = 100K and using the 5 volt supply we could calculate the upper and lower threshold voltage as follow:</p>
<p><strong>VUT = 2/3 Vcc and VLT = 1/3 Vcc</strong></p>
<p>The different between the upper and lower threshold is called the hysteresis voltage:</p>
<p><strong>VH = 1/3 Vcc </strong></p>
<p>The common way to represent the hysteresis graph instead of using two graphs as shown on the above picture is by assigning the Vout on the Y axis and V- (in) on the X axis, now we could get this following picture:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_12.jpg"><img class="alignnone size-full wp-image-1591" title="comp_12" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_12.jpg" alt="" width="580" height="299" /></a></p>
<p>Therefore by shifting the V+ voltage with the R3 feedback resistor we could compensate the comparator output to become more immune to the noise signal. This hysteresis comparator circuit also could be used as a <strong>Schmitt Trigger</strong> with the adjustable threshold point by just changing the value of R1, R2 and R3 resistors respectively.</p>
<p>One of the interesting usages of this hysteresis behavior is to use the comparator as the pulse generator or oscillator; where you could easily make a simple LED blinker as shown on this following circuit bellow:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_13.jpg"><img class="alignnone size-full wp-image-1592" title="comp_13" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_13.jpg" alt="" width="581" height="375" /></a></p>
<p>Again to analyze this circuit first we assume the Vout is high, this will turn on the TR1 transistor and the LED will be ON. In order to meet this condition the <strong>V-</strong> should be less than <strong>V+</strong> (<strong>V- </strong>almost zero) or we could say that the C1 is now being charged through the R4 resistor. Now the <strong>V-</strong> will slowly increase and because of the voltage shift provided by the R3 feedback resistor on the <strong>V+</strong> input; this will make the Vout rapidly swing to the logical low (LED OFF) and this time the C1 will discharge its energy through the R4 resistor and the V- will decrease; when it reach bellow the <strong>V+</strong> then the process will repeat again. You could easily change the LED blink rate by changing the R4 and C1 value respectively.</p>
<p>The output frequency could be calculated using this following formula (taken from AN-74: National Semiconductor Application Note 74):</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_16.jpg"><img class="alignnone size-full wp-image-1594" title="comp_16" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_16.jpg" alt="" width="249" height="82" /></a><br />
Where R4 is the resistance in Ohm, C1 is the capacitance in Farad and freq. is the pulse frequency in Hertz. Therefore using this formula, we could calculate the frequency of the LED blinker circuit above as follow:</p>
<p><strong>1 / Freq. = 2 (0.694) x 470000 x 0.0000001 = 0.65236<br />
Frequency = 1 / 0.65236 = 1.53 Hertz</strong></p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_14.jpg"><img class="alignnone size-full wp-image-1593" title="comp_14" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_14.jpg" alt="" width="575" height="450" /></a></p>
<p>By using different R4 and C1 values on each comparator in the LM339 package (four comparators) you could assembly four independent LED blinker with each of them have a different blink rate. By hooking up to 4 LEDs on each 2N2222 transistor you could get a very nice random LED blink effect similar to the main computer panel shown on many sci-fi movies.</p>
<p><strong>3. The Limit Window Comparator</strong></p>
<p>Using a single comparator we could only have one voltage reference value, if we want to have two voltage reference values; where we could set the upper and lower voltage level limit value to determine the output; we could used what is called the <strong>limit window comparator</strong> circuit or also known as <strong>dual edge limit detector </strong>circuit. On our next experiment we will build the light sensor circuit with the LDR (light dependent resistor) as the sensor and using the limit window comparator circuit. This circuit will light up the LED according to the light intensity detected by LDR. The complete example circuit is shown on this following schema:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_04.jpg"><img class="alignnone size-full wp-image-1595" title="comp_04" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_04.jpg" alt="" width="579" height="384" /></a></p>
<p>To analyze the circuit above; first we assume that the VR2 is equal to 10K; Again the R1, VR2 and R2 will form the voltage divider circuit to both V1 and V2:</p>
<p><strong>V1 = ((R2 + VR2) / (R1 + VR2 + R2)) x Vcc = 2/3 Vcc<br />
V2 = (R2 / (R1 + VR2 + R2)) x Vcc = 1/3 Vcc </strong></p>
<p>The LDR and the VR1 are also forming the voltage divider circuit on the Vin:</p>
<p><strong>Vin = (VR1 / (VR1 + LDR) x Vcc</strong></p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_15.jpg"><img class="alignnone size-full wp-image-1596" title="comp_15" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_15.jpg" alt="" width="574" height="363" /></a></p>
<p>Therefore if Vin is greater than V1 and V2 (Vin &gt; 2/3 Vcc) then the comparator CMP1 output will swing to the logical low and make the TR2 to turn OFF (LED2 OFF), while the comparator CMP2 output will swing to the logical high, this will power the TR1 base make the TR1 to turn ON (LED2 ON).</p>
<p>When the Vin is less then V1 but greater than V2 (1/3 Vcc &lt; Vin &lt; 2/3 Vcc) then the comparator CMP1 output will swing to logical high and make the TR2 to turn ON (LED2 ON). The comparator CMP2 output also will swing to the logical high, this will provide the necessary voltage input on the TR1 base to make it ON (LED1 ON).</p>
<p>The last when the Vin voltage goes bellow the V1 and V2 (Vin &lt; 1/3 Vcc) then the comparator CMP1 output will swing to logical high and make the TR2 to turn ON (LED2 ON), while the comparator CMP2 output will swing to the logical low and make the TR1 to turn OFF (LED1 OFF).</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_05.jpg"><img class="alignnone size-full wp-image-1598" title="comp_05" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_05.jpg" alt="" width="578" height="403" /></a></p>
<p>Therefore by adjusting the VR1 and VR2 you could easily adjust the Vin and set the upper and lower voltage reference level limit as necessary.</p>
<p>By connecting both comparator open collector output (wired-OR) we could get the desired response, where the output only swing to the logical high (&#8221;<strong>1</strong>&#8220;) when Vin voltage is within the Lower and Upper limit voltage.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_17.jpg"><img class="alignnone size-full wp-image-1599" title="comp_17" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_17.jpg" alt="" width="581" height="388" /></a></p>
<p>The limit window comparator mostly used to detect two conditions such as for the temperature sensor, when the temperature exceed the upper limit or way down below the lower limit; the limit window comparator output will send the warning or control signal to the microcontroller I/O port<br />
<strong></strong></p>
<p><strong>4. Comparator inside the Microcontroller</strong></p>
<p>On this last comparator tutorial we will take a look at the comparator circuit inside the microcontroller. Most of today&#8217;s modern microcontroller has already equipped with this feature. Usually it being used as the <strong>zero crossing detector</strong> circuit, where it takes two analog inputs and compares them; then produce the interrupt according to the voltage input level on both input.</p>
<p>Now let&#8217;s take a look at the Atmel AVR ATTiny25 microcontroller; it&#8217;s one of the smallest and yet powerful 8-bit 8-pins AVR family microcontroller which has a build in one powerful analog comparator circuit inside.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_18.jpg"><img class="alignnone size-full wp-image-1600" title="comp_18" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_18.jpg" alt="" width="578" height="408" /></a></p>
<p>The <strong>AIN0</strong> (<strong>PB0</strong>) could be used as the comparator&#8217;s non-inverting input and the <strong>AIN1</strong> (<strong>PB1</strong>) could be used as the comparator inverting input. We also could choose to use the AVR ATTiny25 microcontroller internal reference voltage 1.1 volt connected to the non-inverting input. To understand how this analog comparator works, I use a simple circuit to demonstrate how we could use the microcontroller analog comparator as the zero crossing detectors. This time I will take advantage of the internal reference voltage on the non-inverting input and connect the comparator inverting input to the LDR and trimport. This following schema shows you the example of light detector using the ATTiny25 microcontroller analog comparator feature.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_19.jpg"><img class="alignnone size-full wp-image-1601" title="comp_19" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_19.jpg" alt="" width="576" height="346" /></a></p>
<p>The following is the AVR microcontroller assembler code that demonstrates this capability:</p>
<pre>;***************************************************************************
; File Name    : comparator.asm
; Version      : 1.0
; Description. : Zero Crossing Detector Examples
; Author       : RWB
; Target       : AVR ATTiny25
; Compiler     : AVR macro assembler 2.1.41 (build 1792)
; IDE          : Atmel AVR Studio 4.17
; Programmer   : Atmel AVRISPmkII
; Last Updated : 01 March 2010
;***************************************************************************
.include "tn25def.inc"</pre>
<pre>; Use AVR ATTiny25 Default Frequency Clock
.equ F_CPU = 8000000</pre>
<pre>.cseg</pre>
<pre>; Start on the flash ram's address 0
.org 0
        rjmp  main             ; Jump to the Main Program</pre>
<pre>.org    ACIaddr                ; 0x0007 - Analog Comparator Interrupt Vector
        rjmp  a_comp           ; Jump to Analog Comparator Interrupt Handler</pre>
<pre>; Analog Comparator Interrupt Routine Handler
a_comp: in    R0,SREG          ; Save the SREG Status
        sbic  ACSR,ACO         ; if (AC0 == 0), V- &gt; 1.1V, Turn Off LED
        rjmp  led_on           ; else, V- &lt; 1.1 V, Turn On LED
        cbi   PORTB,PB4        ; Turn Off LED on PB4
        rjmp  ex_int           ; Goto Exit Interrupt
led_on: sbi   PORTB,PB4        ; Turn On LED on PB4
ex_int: out   SREG,R0          ; Restore the SREG Status
        reti                   ; Return from Interrupt Handler</pre>
<pre>; Start the Main Program
main:   ldi   R24,RAMEND       ; Initial Stack Pointer
        out   SPL,R24          ; SP = RAMEND</pre>
<pre>; Initial the I/O Used
        ldi   R16,0b00010000   ; Set PB4=Output, PB0,PB1,PB2,PB3,PB5 and PB6 as Input
        out   DDRB,R16         ; DDRB=0x10</pre>
<pre>; Initial the Analog Comparator
        cbi   ADCSRB,ACME      ; Disable Multiplex Inverting input, just use AN1 (PB1)
        cbi   ACSR,ACD         ; Enable Analog Comparator
        sbi   ACSR,ACBG        ; Use 1.1V (Band Gap) Internal Reference on AN0-Non Inverting Input
        cbi   ACSR,ACIS1       ; Use Interrupt Output Toggle
        cbi   ACSR,ACIS0</pre>
<pre>        ldi   R16,0b00000011   ; Disable Digital Input on AN0 (PB0) and AN1 (PB1)
        out   DIDR0,R16</pre>
<pre>        sbi   ACSR,ACIE        ; Enable Analog Comparator Interrupt
        sei                    ; Enable Global Interrupt</pre>
<pre>loop:   rjmp  loop             ; Loop Forever and Let the Interrupt do the work</pre>
<pre>.exit</pre>
<pre>; EOF: comparator.asm</pre>
<p>The analog comparator inside the Atmel AVR ATTiny25 microcontroller is capable to use multiplex inverting analog input from <strong>ADC0, ADC1, ADC2</strong> and <strong>ADC3</strong>, but on this tutorial I will use the <strong>AN1</strong> (<strong>PB1</strong>) as the inverting input which connected to the LDR and 10 K trimport voltage divider circuit. By clearing the <strong>ACME</strong> (Analog Comparator Multiplex Enable) bit on the <strong>ADCSRB</strong> register, we simply disable this feature.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_23.jpg"><img class="alignnone size-full wp-image-1602" title="comp_23" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_23.jpg" alt="" width="585" height="403" /></a></p>
<p>The non-inverting input is designed to use the 1.1 volt internal reference voltage by enabling the analog comparator band gap (<strong>ACBG</strong>) bit on the <strong>ACSR</strong> register. By enabling the analog comparator interrupt (<strong>ACIE</strong>) bit on the <strong>ACSR</strong> register, we could instruct the AVR ATTiny25 microcontroller to generate interrupt on every output toggle by clearing the <strong>ACIS1</strong> and <strong>ACIS0</strong> bits on the <strong>ACSR</strong> register. Therefore by examining the analog comparator output (<strong>ACO</strong>) bit on the <strong>ACSR</strong> register we could easily determine whether the <strong>V-</strong> inverting voltage (<strong>AN1</strong>) is less or greater than the <strong>V+</strong> (<strong>AN0</strong>) inverting voltage and use this information to toggle the <strong>PB4</strong> output port to make the LED ON and OFF. You could read more about programming the AVR assembler code on my previous posted blog <a title="Beginners AVR Assembler Language Programming 1" href="http://www.ermicro.com/blog/?p=139" target="_blank">Beginners AVR Assembler Language Programming 1</a></p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_20a.jpg"><img class="alignnone size-full wp-image-1603" title="comp_20a" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_20a.jpg" alt="" width="578" height="383" /></a></p>
<p>Using the powerful Atmel AVR Studio v4.17 integrated development environment you could easily compile and debug this AVR assembler code, and then down load the hex code into the ATTiny25 microcontroller flash ram using the AVR microcontroller programmer; on this tutorial I used Atmel AVRISPmkII programmer.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_20b.jpg"><img class="alignnone size-full wp-image-1604" title="comp_20b" src="http://www.ermicro.com/blog/wp-content/uploads/2010/03/comp_20b.jpg" alt="" width="578" height="368" /></a></p>
<p>Now you could enjoy this following video which showing all the experiments we&#8217;ve already done on this tutorial</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/n4uFWSeLkz8&amp;hl=en_US&amp;fs=1&amp;" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/n4uFWSeLkz8&amp;hl=en_US&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p><strong></strong></p>
<p><strong>The Final Thought</strong></p>
<p>The comparator or op-amps in general is one of the breakthroughs in the integrated circuit development as this tiny smart circuit has tremendous applications in electronics industries. Therefore understanding of how the comparator work will help you take the advantage of this amazing circuit in your next electronics or embedded system project.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ermicro.com/blog/?feed=rss2&amp;p=1578</wfw:commentRss>
		</item>
		<item>
		<title>Build Your Own Simple and Easy PICAXE Microcontroller Based Photovore Robot</title>
		<link>http://www.ermicro.com/blog/?p=1549</link>
		<comments>http://www.ermicro.com/blog/?p=1549#comments</comments>
		<pubDate>Thu, 04 Feb 2010 11:34:43 +0000</pubDate>
		<dc:creator>rwb</dc:creator>
		
		<category><![CDATA[Robotics]]></category>

		<category><![CDATA[ADC]]></category>

		<category><![CDATA[PIC16F886]]></category>

		<category><![CDATA[PICAXE]]></category>

		<category><![CDATA[SERVO]]></category>

		<guid isPermaLink="false">http://www.ermicro.com/blog/?p=1549</guid>
		<description><![CDATA[Building a simple and easy microcontroller based robot is always a fascinating topic to be discussed, especially for the robotics newbie enthusiast. On this tutorial I will show you how to build your own microcontroller based robot which known as a photovore or you could call it as the light chaser robot using the simplest [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/fZYwqx52Gv6BiHUIIqJLg_xCh2Q/0/da"><img src="http://feedads.g.doubleclick.net/~a/fZYwqx52Gv6BiHUIIqJLg_xCh2Q/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/fZYwqx52Gv6BiHUIIqJLg_xCh2Q/1/da"><img src="http://feedads.g.doubleclick.net/~a/fZYwqx52Gv6BiHUIIqJLg_xCh2Q/1/di" border="0" ismap="true"></img></a></p><p>Building a simple and easy microcontroller based robot is always a fascinating topic to be discussed, especially for the robotics newbie enthusiast. On this tutorial I will show you how to build your own microcontroller based robot which known as a photovore or you could call it as the light chaser robot using the simplest possible circuit for the microcontroller based robot brain, locomotion motor and the sensor.<span id="more-1549"></span></p>
<p>One of the most frustrating parts when building your first microcontroller based robot is to program it and to download it into the microcontroller flash ram. On this tutorial this kind of &#8220;trouble maker&#8221; is being reduced as we will use the PICAXE programming editor from the <strong>Revolution Education Ltd</strong> (<strong>http://www.rev-ed.co.uk/picaxe</strong>) as our Integrated Development Environment (IDE) to program our robot brain using the BASIC (<em>Beginners All Purpose Symbolic Instruction Code</em>) language and to download the program into the PICAXE 28X1 microcontroller.</p>
<p>The PICAXE 28X1 microcontrollers actually is based on the popular Microchip 8-bit 28 pins PIC16F886 microcontroller that have a preload PICAXE BASIC interpreter firmware inside, in fact when you buy it its looks the same as the usual Microchip PIC16F886 microcontroller. Together with the free PICAXE Programming Editor and simple serial cable connector for the program downloader makes this PICAXE framework suitable for beginners and even for the professional.</p>
<p><a title="Build Your Own Simple and Easy PICAXE Microcontroller Based Photovore Robot  01 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4328241328/"><img src="http://farm3.static.flickr.com/2680/4328241328_176cc9994b.jpg" alt="Build Your Own Simple and Easy PICAXE Microcontroller Based Photovore Robot  01" width="575" height="430" /></a></p>
<p>You could read more information about the PICAXE microcontroller on my previous posted blog &#8220;<a title="Introduction to the Embedded System with PICAXE Microcontroller" href="http://www.ermicro.com/blog/?p=1334" target="_blank">Introduction to the Embedded system with PICAXE Microcontroller</a>&#8220;.</p>
<p>On this tutorial I will refer this photovore robot as the BRAM-AXE as this is a simplify version of the more advanced version of its big brother BRAM (Beginners Robot Autonomous Mobile) which you could read more information about it on my previous posted blog &#8220;<a title="Building BRAM your first Autonomous Mobile Robot using Microchip PIC Microcontroller – Part 1" href="http://www.ermicro.com/blog/?p=983" target="_blank">Building BRAM your first Autonomous Mobile Robot using Microchip PIC Microcontroller – Part 1</a>&#8221; and &#8220;<a title="Behavior Based Artificial Intelligent Mobile Robot with Sharp GP2D120 Distance Measuring Sensor - BRAM Part 2" href="http://www.ermicro.com/blog/?p=1016" target="_blank">Behavior Based Artificial Intelligent Mobile Robot with Sharp GP2D120 Distance Measuring Sensor - BRAM Part 2</a>&#8220;.</p>
<p>The BRAM-AXE robot simply use its light sensitive sensor to navigate to the light source, when it being placed in the dark environment it will automatically switch to the light search mode and try to find the light source, when it encounter the light source it will navigate to the light source and when the light source intensity is bright enough it will stop and enjoying the light. You could see all this behavior on the video at the end of this tutorial. Ok now let&#8217;s list down all the electronics parts, robot chassis materials and software needed to build this interesting robot.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_05.jpg"><img class="alignnone size-full wp-image-1550" title="bram_axebot_05" src="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_05.jpg" alt="" width="574" height="407" /></a></p>
<ul>
<li>BRAM-AXE board: based on the PICAXE 28X1 microcontrollers together with the PICAXE serial downloader cable which you could easily build from my previous posted blog &#8220;<a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method" href="http://www.ermicro.com/blog/?p=1526" target="_blank">Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method</a>&#8220;.</li>
<li>Two LDR and Two 1K Ohm 0.25 watt resistor</li>
<li>Two Continues Servo (on this tutorial I use the Parallax Continues Servo)</li>
<li>Two toys tire to be attached to the servo&#8217;s arms</li>
<li>Enough cables and Tubing (1mm and 3mm)</li>
<li>3 x AA battery holder with 3 x AA alkaline battery</li>
<li>1 CD or DVD</li>
<li>A good double tape or epoxy glue for the permanent robot</li>
<li>One paper clips and neck less beads for the robot caster (the robot&#8217;s third wheel)</li>
<li>Enough bolts and nuts for the caster</li>
<li>PICAXE Programming Editor from <strong>Revolution Education Ltd</strong> which you could download from their website</li>
</ul>
<p><a title="Build Your Own Simple and Easy PICAXE Microcontroller Based Photovore Robot  02 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4327509763/"><img src="http://farm5.static.flickr.com/4032/4327509763_015e611695.jpg" alt="Build Your Own Simple and Easy PICAXE Microcontroller Based Photovore Robot  02" width="573" height="428" /></a></p>
<p><strong>The BRAM-AXE Chassis Assembly</strong></p>
<p>The BRAM-AXE main chassis is made from the CD/DVD which you could find easily at home or just use the blank one as I did if you don&#8217;t have any discarding CD/DVD at home. For the wheel you could use any toy&#8217;s wheel or you could use anything that has a circle form as long as the diameter is greater or equal to the servo&#8217;s arms.</p>
<p>The assembly of BRAM-AXE chassis is straight forward; you just need to drill two holes for placing the caster. Attached the two continues servo on the button of the CD/DVD with the double tapes and attached the wheel to the servo&#8217;s arms with the double tape or you could use the epoxy for permanent one. You could see the whole process on these following pictures:</p>
<p><a title="Build Your Own Simple and Easy PICAXE Microcontroller Based Photovore Robot 03 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4328241716/"><img src="http://farm5.static.flickr.com/4067/4328241716_46335a39f4.jpg" alt="Build Your Own Simple and Easy PICAXE Microcontroller Based Photovore Robot 03" width="576" height="459" /></a></p>
<p>Now attached the 3 x AA battery holder and BRAM-AXE board on top of the CD; again using our flexible friend the double tape as shown on these following pictures.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_08.jpg"><img class="alignnone size-full wp-image-1552" title="bram_axebot_08" src="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_08.jpg" alt="" width="580" height="247" /></a></p>
<p>Connect the power socket to the BRAM-AXE polarized power pin and the servo&#8217;s socket to the BRAM-AXE board output <strong>0</strong> and output <strong>1</strong> three pins socket respectively as shown on this following servo&#8217;s circuit diagram.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_04.jpg"><img class="alignnone size-full wp-image-1553" title="bram_axebot_04" src="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_04.jpg" alt="" width="579" height="405" /></a></p>
<p>The BRAM-AXE board output pins is design to be attached directly with the servo, every output pins provide both power and ground needed to power the servo. Connect the left servo connector to the BRAM-AXE output <strong>0</strong> and the right servo connector to the BRAM-AXE output <strong>1</strong>. After finishing all this steps now it&#8217;s the time to test the robot steering mechanism.</p>
<p><strong>The BRAM-AXE Steering Method</strong></p>
<p>The BRAM-AXE steering use the simple method called the &#8220;<strong>differential drive</strong>&#8220;; when the two servos rotate on the same direction the robot will move forward or backward, and when they rotate on different direction (counter rotation to each other) the robot will rotate to left or right.</p>
<p>Servo basically is a high quality geared DC motor with electronic circuit for controlling the DC motor rotation direction and position; its being used widely in model hobbyist such as car R/C model for steering and acceleration control or airplane R/C model for moving the rudder, ailerons, elevators and acceleration control. Typically there are two type of servo available on the market the first one is &#8220;standard&#8221; servo which could rotate between 120 to 180 degree and the second one is &#8220;continues&#8221; servo which could rotate 360 degree.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/02/servo_00.jpg"><img class="alignnone size-full wp-image-1554" title="servo_00" src="http://www.ermicro.com/blog/wp-content/uploads/2010/02/servo_00.jpg" alt="" width="581" height="239" /></a><br />
By using the continues servo, the locomotion circuit become very simple as we only connect the servo directly to the BRAM-AXE board and supply the correct PWM (Pulse Width Modulation) signal in order to make it rotate. The PICAXE BASIC language has a build in servo commands that we could use to make the servo rotate.</p>
<pre>servo output_pin, n
servopos output_pin, n</pre>
<p>The <strong>servo</strong> command will initial the PICAXE 28X1 (Microchip PIC16F886) internal TIMER which is used to generate the PWM signal to the servo and after initialization you have to use the <strong>servopos</strong> command in order to control the servo. The <strong>n</strong> is the number between <strong>75</strong> and <strong>255</strong>, where this number will determine the servo rotation direction (clock wise or counter clock wise). The <strong>output_pin</strong> is the PICAXE 28X1 output pin which range from <strong>0</strong> to <strong>7</strong>.</p>
<p><a title="Build Your Own Simple and Easy PICAXE Microcontroller Based Photovore Robot  04 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4328242398/"><img src="http://farm5.static.flickr.com/4062/4328242398_dfab3fca3e.jpg" alt="Build Your Own Simple and Easy PICAXE Microcontroller Based Photovore Robot  04" width="577" height="432" /></a></p>
<p>Now connect the serial programming cable from the BRAM-AXE board to your computer COM/RS232 port or you could use the USB to RS232 adapter as I did and download this following servo testing program using the PICAXE Programming Editor:</p>
<pre>'***************************************************************************
'  File Name    : bram-axe_servo_test.bas
'  Version      : 1.0
'  Description  : BRAM-AXE Photovore Servo Testing
'  Author       : RWB
'  Target       : BRAM-AXE Learning Board - PICAXE 28x1
'  Intepreter   : PICAXE Firmware version A.6
'  IDE          : PICAXE Programming Editor Version 5.2.6
'  Programmer   : PICAXE Programming Editor Version 5.2.6
'  Last Updated : 03 January 2010
'***************************************************************************</pre>
<pre>' Use PICAXE 28x1 with 4 MHz Internal Clock
#picaxe 28x1
SetFreq m4</pre>
<pre>' Assigned Variables
symbol cnt_val = b0
symbol move_mode = b1</pre>
<pre>let pins = %00000000 ' Reset all the Output Pins</pre>
<pre>' Initial the Servo
gosub BRAM_ServoInit</pre>
<pre>move_mode=1
cnt_val=1</pre>
<pre>main:
  select move_mode
    case 1
      gosub BRAM_MoveForward
    case 2
      gosub BRAM_MoveBackward
    case 3
      gosub BRAM_RotateRight
    case 4
      gosub BRAM_RotateLeft
    case 5
      gosub BRAM_Stop
    case 6
      gosub BRAM_ServoInit
  end select

  pause 10        ' Pause 10 Second
  cnt_val=cnt_val + 1
  if cnt_val &gt; 150 then
    move_mode=move_mode + 1
    if move_mode &gt; 6 then
      move_mode=1
    endif
    cnt_val=1
  endif

  goto main      ' Back to main loop
  end</pre>
<pre>' BRAM Steering Subroutines
BRAM_ServoInit:
  servo 0,125        ' Left Servo
  servo 1,200        ' Right Servo
return</pre>
<pre>BRAM_MoveForward:
  servopos 0,125     ' Left Servo Forward
  servopos 1,200     ' Right Servo Backward
return</pre>
<pre>BRAM_MoveBackward:
  servopos 0,200     ' Left Servo Backward
  servopos 1,125     ' Right Servo Forward
return</pre>
<pre>BRAM_RotateRight:
  servopos 0,125     ' Left Servo Forward
  servopos 1,125     ' Right Servo Forward
return</pre>
<pre>BRAM_RotateLeft:
  servopos 0,200     ' Left Servo Backward
  servopos 1,200     ' Right Servo Backward
return</pre>
<pre>BRAM_Stop:
  settimer off       ' TIMER Off
return</pre>
<pre>' EOF: bram-axe_servo_test.bas</pre>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_10.jpg"><img class="alignnone size-full wp-image-1555" title="bram_axebot_10" src="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_10.jpg" alt="" width="581" height="379" /></a></p>
<p>Usually the servo is designed to have a wide tolerance on the incoming PWM signal, but if your servo doesn&#8217;t work with the above value (<strong>125</strong> and <strong>200</strong>) try to experiment with other value as long as the value range is between <strong>75</strong> and <strong>255</strong>. For more information about controlling the servo you could read my previous posted blog &#8220;<a title="Basic Servo Motor Controlling with Microchip PIC Microcontroller" href="http://www.ermicro.com/blog/?p=771" target="_blank">Basic Servo Motor Controlling with Microchip PIC Microcontroller</a>&#8220;.</p>
<p><strong>The BRAM-AXE Sensors</strong></p>
<p>In order to make the complete photovore robot, we need to use the light sensitive sensor; the simple and cheapest one is to use a special made resistor (made from Cadmium Sulfide) called Light Dependent Resistor or LDR for short.  The LDR will vary its resistance according to the light intensity fall on its surface; the bright light intensity will make its resistance decrease significantly (about 1K Ohm to 5 K Ohm) while on the completely dark its resistance will increase as high as 100 K Ohm.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_02.jpg"><img class="alignnone size-full wp-image-1556" title="bram_axebot_02" src="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_02.jpg" alt="" width="577" height="401" /></a></p>
<p>By connecting the LDR in series with 1 K Ohm resistor, we could get the simplest possible light sensor circuit that will give us the variable voltage output according to the light intensity as shown on this following picture.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_03.jpg"><img class="alignnone size-full wp-image-1557" title="bram_axebot_03" src="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_03.jpg" alt="" width="578" height="345" /></a></p>
<p>This simple circuit is known as the voltage divider circuit, you could read more information about the voltage divider circuit on my previous posted blog &#8220;<a title="Basic Resistor Circuit" href="http://www.ermicro.com/blog/?p=104" target="_blank">Basic Resistor Circuit</a>&#8220;.</p>
<p>Connect the sensor circuit output directly to the PICAXE 28X1 microcontroller analog to digital conversion (ADC) input port (<strong>ADC 0</strong> and <strong>ADC 1</strong>) and use the power taken from the BRAM-AXE board output pin as shown on these following pictures:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_12.jpg"><img class="alignnone size-full wp-image-1558" title="bram_axebot_12" src="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_12.jpg" alt="" width="575" height="249" /></a></p>
<p>With the PICAXE ADC feature, now we could easily read the numeric representation of the light intensity on each sensor (left LDR and right LDR) using this following PICAXE BASIC commands:</p>
<pre>readadc adc_channel, variable</pre>
<p>The <strong>readadc </strong>command will read the ADC input port (<strong>0</strong> to <strong>3</strong> on PICAXE 28X1 microcontroller) and assigned the 8-bit data value to the <strong>variable</strong>. By examining the ADC value we could command our robot to follow the light as shown on this following <strong>BRAM-AXE_photovore.bas</strong> program:</p>
<pre>'***************************************************************************
'  File Name    : bram-axe_photovore.bas
'  Version      : 1.0
'  Description  : BRAM-AXE Photovore
'  Author       : RWB
'  Target       : BRAM-AXE Learning Board - PICAXE 28x1
'  Intepreter   : PICAXE Firmware version A.6
'  IDE          : PICAXE Programming Editor Version 5.2.6
'  Programmer   : PICAXE Programming Editor Version 5.2.6
'  Last Updated : 03 January 2010
'***************************************************************************</pre>
<pre>' Use PICAXE 28x1 with 4 Mhz Internal Clock
#picaxe 28x1
SetFreq m4</pre>
<pre>' Assigned Variable
symbol ldr_left = b0
symbol ldr_right = b1
symbol diff_value = b2
symbol mode = b3
symbol cnt = b4
symbol stop_stat = b5</pre>
<pre>let pins = %00000000 ' Reset all Output Pins</pre>
<pre>' Initial variable used
mode=0
cnt=0
stop_stat=0</pre>
<pre>main:
  ' Read the LDR ADC Value
  readadc 0,ldr_left
  readadc 1,ldr_right</pre>
<pre>  ' For Debugging the ADC Value
  ' sertxd("LDR Left: ",#ldr_left,13,10)
  ' sertxd("LDR Right: ",#ldr_right,13,10)
  ' pause 100 

  ' Searching Mode if both LDR less or equal to 5
  if ldr_left &lt;= 5 and ldr_right &lt;= 5 then
    cnt=cnt + 1
    if cnt &gt; 80 then
      cnt=0
      mode=mode + 1
      if mode &gt; 5 then
        mode = 0
      endif
    endif

    select mode
      case 1
        gosub BRAM_Stop
      case 2
        gosub BRAM_RotateRight
      case 3
        gosub BRAM_MoveBackward
      case 4
        gosub BRAM_RotateLeft
      case 5
        gosub BRAM_MoveForward
    end select

    pause 10
    goto main
  endif</pre>
<pre>  ' STOP both LDR greater or equal to 90
  if ldr_left &gt;= 90 and ldr_right &gt;= 90 then
    gosub BRAM_Stop
    pause 10
    goto main
  endif

  ' BRAM-AXE Bang-Bang steer control
  if ldr_left &gt; ldr_right then
    diffvalue=ldr_left - ldr_right
    if diff_value &gt;= 20 then
      gosub BRAM_RotateRight
    else
      gosub BRAM_MoveForward
    endif
  endif

  if ldr_right &gt; ldr_left then
    diffvalue=ldr_right - ldr_left
    if diff_value &gt;= 20 then
      gosub BRAM_RotateLeft
    else
      gosub BRAM_MoveForward
    endif
  endif

  pause 10       ' Pause 10 seconds  

  goto main      ' Back to main loop
  end</pre>
<pre>' BRAM Steering Subroutines
BRAM_MoveForward:
  if stop_stat = 1 then
    servo 0,125        ' Init Left Servo Forward
    servo 1,200        ' Init Right Servo Backward
    stop_stat=0
  else
    servopos 0,125     ' Left Servo Forward
    servopos 1,200     ' Right Servo Backward
  endif
return</pre>
<pre>BRAM_MoveBackward:
  if stop_stat = 1 then
    servo 0,200        ' Init Left Servo Backward
    servo 1,125        ' Init Right Servo Forward
    stop_stat=0
  else
    servopos 0,200     ' Left Servo Backward
    servopos 1,125     ' Right Servo Forward
  endif
return</pre>
<pre>BRAM_RotateRight:
  if stop_stat = 1 then
    servo 0,125        ' Init Left Servo Forward
    servo 1,125        ' Init Right Servo Forward
    stop_stat=0
  else
    servopos 0,125     ' Left Servo Forward
    servopos 1,125     ' Right Servo Forward
  endif
return</pre>
<pre>BRAM_RotateLeft:
  if stop_stat = 1 then
    servo 0,200        ' Init Left Servo Backward
    servo 1,200        ' Init Right Servo Backward
    stop_stat=0
  else
    servopos 0,200     ' Left Servo Backward
    servopos 1,200     ' Right Servo Backward
  endif
return</pre>
<pre>BRAM_Stop:
  stop_stat=1
  settimer off
return</pre>
<pre>' EOF: bram-axe_photovore.bas</pre>
<p>The key for this photovore program to work properly is to place the two LDR sensors about 45 degree from the center of the robot chassis as shown on these following pictures:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_11.jpg"><img class="alignnone size-full wp-image-1559" title="bram_axebot_11" src="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_11.jpg" alt="" width="577" height="316" /></a></p>
<p>When the two LDR sensors get an equal light intensity than we command the BRAM-AXE to move forward, when the light intensity is bright enough than we make it stop as shown on this PICAXE BASIC code:</p>
<pre>' STOP both LDR greater or equal to 90
if ldr_left &gt;= 90 and ldr_right &gt;= 90 then
  gosub BRAM_Stop
  pause 10
  goto main
endif</pre>
<p>When one of the LDR sensors is brighter then the other than we command the BRAM-AXE to rotate right or rotate left:</p>
<pre>' BRAM-AXE Bang-Bang steer control
if ldr_left &gt; ldr_right then
    diffvalue=ldr_left - ldr_right
    if diff_value &gt;= 20 then
      gosub BRAM_RotateRight
    else
      gosub BRAM_MoveForward
    endif
endif

if ldr_right &gt; ldr_left then
    diffvalue=ldr_right - ldr_left
    if diff_value &gt;= 20 then
      gosub BRAM_RotateLeft
    else
      gosub BRAM_MoveForward
    endif
endif</pre>
<p>When both LDR sensors is quite dark than we command the BRAM-AXE to enter the light search mode, this light search program algorithm will keep the BRAM-AXE to move forward, backward, rotate left and right until it locate the light source.</p>
<p>The BRAM-AXE photovore program algorithm use the simplest steering method control called the bang-bang control or on/off control which is the simplest closed loops control method usually used in the embedded system. For more information about the bang-bang control method you could read my previous posted blog &#8220;<a title="Basic Servo Motor Controlling with Microchip PIC Microcontroller" href="http://www.ermicro.com/blog/?p=771" target="_blank">Basic Servo Motor Controlling with Microchip PIC Microcontroller</a>&#8220;.</p>
<p>You could adjust all the bang-bang control numeric values to suite your need, in order to do that you have to know the ADC value before making your adjustment. This could be done by opening the commented PICAXE BASIC serial data transmit and pause command on these following lines:</p>
<pre>' For Debugging the ADC Value
sertxd("LDR Left: ",#ldr_left,13,10)
sertxd("LDR Right: ",#ldr_right,13,10)
pause 100</pre>
<p>You could display these value directly to the PICAXE Programming Editor build in serial terminal, which you could access from menu <strong>PICAXE -&gt; Terminal</strong> or you could press the <strong>F8</strong> key, re-down load the code again to run this monitor. Do not disconnect the serial programmer cable from the BRAM-AXE board as the PICAXE BASIC <strong>sertxd</strong> command use this serial connection to transmit the data and make sure you set the baud rate to 4800 as shown on this following picture:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_13.jpg"><img class="alignnone size-full wp-image-1560" title="bram_axebot_13" src="http://www.ermicro.com/blog/wp-content/uploads/2010/02/bram_axebot_13.jpg" alt="" width="581" height="441" /></a></p>
<p>Now as you have completely build your own photovore robot its time to watch this cute and simple robot in action:</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/SqAbRDk-pug&amp;hl=en_US&amp;fs=1&amp;" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/SqAbRDk-pug&amp;hl=en_US&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p><strong>The Final Though</strong></p>
<p>As you&#8217;ve learned to build this simple and easy to build photovore robot hopefully this will trigger your passion to learn more about robotics and the embedded system. Learning by experiences is the key to success in learning the embedded system, keep experimenting as this will build your understanding and confidence in developing your own embedded system project.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ermicro.com/blog/?feed=rss2&amp;p=1549</wfw:commentRss>
		</item>
		<item>
		<title>Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method</title>
		<link>http://www.ermicro.com/blog/?p=1526</link>
		<comments>http://www.ermicro.com/blog/?p=1526#comments</comments>
		<pubDate>Sun, 03 Jan 2010 16:55:30 +0000</pubDate>
		<dc:creator>rwb</dc:creator>
		
		<category><![CDATA[Electronics]]></category>

		<category><![CDATA[ADC]]></category>

		<category><![CDATA[LED]]></category>

		<category><![CDATA[PCB]]></category>

		<category><![CDATA[PICAXE]]></category>

		<guid isPermaLink="false">http://www.ermicro.com/blog/?p=1526</guid>
		<description><![CDATA[As the electronics hobbyist one of knowledge that we have to be familiar with is how to make our own printed circuit board (PCB). Making our own simple single side PCB actually is not require a sophisticated technique and technology as you might think, instead most of the required materials is already available at your [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/uZXR6FCq_7FtJae0v9vpjHBUKMs/0/da"><img src="http://feedads.g.doubleclick.net/~a/uZXR6FCq_7FtJae0v9vpjHBUKMs/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/uZXR6FCq_7FtJae0v9vpjHBUKMs/1/da"><img src="http://feedads.g.doubleclick.net/~a/uZXR6FCq_7FtJae0v9vpjHBUKMs/1/di" border="0" ismap="true"></img></a></p><p>As the electronics hobbyist one of knowledge that we have to be familiar with is how to make our own printed circuit board (PCB). Making our own simple single side PCB actually is not require a sophisticated technique and technology as you might think, instead most of the required materials is already available at your home. I&#8217;ve started make my first single side through-hole PCB for a simple two transistors astable multivibrator project using just a water proof marker and draw the PCB layout directly on the PCB copper surface. <span id="more-1526"></span></p>
<p>On those days the CAD (computer aided design) for electronic schematic and PCB are expensive and it runs on the expensive computer hardware too. That is why most of the electronics hobbyist on those days rely only on the hand draw method and occasionally combine it with a special made sticker for drawing the IC parts.</p>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 01 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4241177752/"><img src="http://farm3.static.flickr.com/2777/4241177752_9ccfcdd654.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 01" width="579" height="434" /></a></p>
<p>Nowadays as the technology evolve making a decent quality home made single side PCB is become possible and easy. On this tutorial we are going to learn of how to make a through-hole single side PCB and later on use this home made PCB for many of the blog up-coming projects or you could simply use it for your project.</p>
<p><strong>The BRAM-AXE Microcontroller Board</strong></p>
<p>The BRAM-AXE microcontroller board is based on the PICAXE 28&#215;1 microcontrollers from the <strong>Revolution Education Ltd</strong> (<strong>http://www.rev-ed.co.uk/picaxe</strong>). The PICAXE 28&#215;1 microcontroller actually is based on the midrange 8-bit Microchip PIC16F886 microcontroller with preload PICAXE BASIC (<em>Beginner&#8217;s All-Purpose Symbolic Instruction Code</em>) interpreter firmware inside which is suitable for both beginners and advanced usage. Now let&#8217;s take a look at the BRAM-AXE microcontroller board electronic schematic which is specially designed to be used on this PCB tutorial.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_01.jpg"><img class="alignnone size-full wp-image-1527" title="diypcb_01" src="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_01.jpg" alt="" width="582" height="321" /></a></p>
<p>The PICAXE 2&#215;1 microcontroller has a build in program loader, therefore this board could be easily reprogrammed using the PICAXE programming editor by connecting the board COM/RS232 port to your computer serial COM/RS232 port or USB port with the USB to COM/RS232 adapter.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_11.jpg"><img class="alignnone size-full wp-image-1529" title="diypcb_11" src="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_11.jpg" alt="" width="577" height="450" /></a></p>
<p>The PICAXE 28&#215;1 microcontroller support up to 1000 lines of code, for more advanced application you could replace the PICAXE 28&#215;1 microcontroller with the PICAXE 28&#215;2 microcontroller which based on Microchip advanced 8-bit PIC18F2520 microcontroller which support up to 4000 lines of code.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_09.jpg"><img class="alignnone size-full wp-image-1530" title="diypcb_09" src="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_09.jpg" alt="" width="576" height="244" /></a></p>
<p>For more information about the PICAXE microcontroller you could read my previous posted blog <a title="Introduction to the Embedded System with PICAXE Microcontroller" href="http://www.ermicro.com/blog/?p=1334" target="_blank">Introduction to the Embedded System with PICAXE microcontroller</a>.</p>
<p>The following is the list of electronic parts required to assembly the BRAM-AXE board:</p>
<p>1.	Resistor ¼ watt: 180 Ohm (1), 470 Ohm (1), 4K7 (1), 10K (1) and 22K (1)<br />
2.	Capacitor: 0.1 uF (1) and 0.01 uF (1)<br />
3.	One PICAXE 28&#215;1 microcontroller<br />
4.	One 28 pin IC Socket<br />
5.	One mini switch button<br />
6.	One blue 3mm LED<br />
7.	One 2 pin polarized socket for the power supply<br />
8.	One 3 pin polarized socket for the COM/RS232<br />
9.	One COM/RS232 DB-9 female connector and cable<br />
10.	Optional USB to COM/RS232 adapter<br />
11.	One 1&#215;40 male header<br />
12.	3xAA battery holder and 3xAA alkaline battery for powering the board</p>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 02 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4240410349/"><img src="http://farm5.static.flickr.com/4032/4240410349_5a680c8a09.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 02" width="573" height="428" /></a></p>
<p><strong>The BRAM-AXE Board PCB Layout </strong></p>
<p>The BRAM-AXE board schematic is created with EAGLE version 5.6.0 Lite Edition; it&#8217;s a freeware version of the powerful CAD software made by <strong>CADSOFT</strong> (<strong>http:// www.cadsoft.de</strong>) for creating electronics schema and generating the PCB layout. This following picture is the PCB layout generated by this software:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_03.jpg"><img class="alignnone size-full wp-image-1531" title="diypcb_03" src="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_03.jpg" alt="" width="581" height="458" /></a></p>
<p>For the purpose of this tutorial I will not describe of how to use the EAGLE software, all of the EAGLE software project files and the PDF print out could download from <a title="BRAM-EXE PCB Project" href="/blog/wp-content/downloads/bram-axe.zip" target="_blank">BRAM-AXE PCB</a> on this blog. You could visit the <strong>CADSOFT</strong> website above for more information and tutorial of how to use the EAGLE software for creating electronic schematic and generating the PCB layout from it.</p>
<p>Ok know let&#8217;s list down all the material required to make this BRAM-AXE board PCB:</p>
<p>1.	A piece of glossy magazine paper, brochure, catalog for the toner transfer method medium.<br />
2.	Laser Printer with adequate toner for printing the PCB to the glossy magazine paper or you could also use a copy machine<br />
3.	60&#215;45 mm Copper clad laminated printed circuit board (PCB)<br />
4.	Kitchen scrub with soap to wash the PCB<br />
5.	Hacksaw and sandpaper for cutting the PCB and smoothing<br />
6.	The house hold iron for doing the actual toner transfer from the glossy magazine paper to the PCB<br />
7.	Ferry Chloride for etching<br />
8.	Plastic container for etching and washing<br />
9.	A pair of disposal rubber hands glove and eyes protection goggle<br />
10.	Nail Polish Remover to remove the toner from the PCB<br />
11.	Water proof marker<br />
12.	Hand drill with 1mm and 3mm drill bits for drilling the through-hole PCB</p>
<p><strong>The Toner Transfer Method</strong></p>
<p>The toner transfer method is actually based on the same principal as the professional PCB making process, but instead of using the expensive photo film and photosensitive material to transfer the PCB drawing layout to PCB copper; we use the laser jet printer toner to do the job.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_04.jpg"><img class="alignnone size-full wp-image-1532" title="diypcb_04" src="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_04.jpg" alt="" width="578" height="234" /></a></p>
<p>The laser jet printer toner actually contain a plastic material and not an ink as the deskjet printer does; therefore when we print this PCB drawing layout on a glossy paper, the black toner plastic material will be attached only on the surface of this glossy paper; by using enough hot from the house hold iron we could make this black toner plastic material melt and reattached to the PCB copper surface.</p>
<p>Now as the magazine glossy paper and the black toner plastic material attached to the PCB copper surface, we could easily remove the paper part by submerging it in the water; once the paper dissolve and become pulp what is left is the black toner plastic material on the PCB copper surface.</p>
<p>Using the etching process the uncovered area on the PCB copper surface will chemically react with the Ferry Chloride (FeCl3) solution and become dissolved but the covered area will not be dissolved as it being protected by the black toner plastic material. The last step is to remove this black toner plastic material using the nail polish remover and drill the board.</p>
<p><strong>The Making of BRAM-AXE Board PCB</strong></p>
<p>Now as you understand how this toner transfers method work; let&#8217;s make the BRAM-AXE board PCB.</p>
<p><strong>The Preperation</strong></p>
<ul>
<li>Cut the copper clad laminated PCB to 60&#215;45mm size and smooth it&#8217;s surrounding with the sandpaper.</li>
</ul>
<ul>
<li>Wash and scrub the PCB using the kitchen scrub and soap to remove any dirt and grease on the PCB copper surface</li>
</ul>
<ul>
<li>Cut the glossy magazine paper to about half of A4 paper length size and full width of A4 paper size; try to use the paper with less printed image or letter on it, so you could easily examine the printed PCB layout. In fact you could use any glossy paper; but do not use the glossy photo paper as it hard to remove the glossy coating material on the PCB copper surface when the toner has been transferred; just safe it for printing your photo.</li>
</ul>
<ul>
<li>Print it with the laser jet printer on the glossy magazine paper. The other alternative is to print it with other type of printer (e.g. deskjet printer) and use the real copier machine to copy the PCB layout to the glossy magazine paper because the copier machine use the same toner material as the laser jet printer. On this tutorial I used the HP LaserJet P1006 to print this PCB layout.</li>
</ul>
<ul>
<li>When printing the PCB layout you could use the EAGLE software directly or you could use the PDF version of the PCB layout provided here.</li>
</ul>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 03 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4241183490/"><img src="http://farm5.static.flickr.com/4001/4241183490_9f350c5de9.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 03" width="580" height="435" /></a></p>
<p><strong>The Toner Transfer Process</strong></p>
<ul>
<li>Next aligned the glossy magazine paper so the printed PCB layout is facing toward the PCB copper surface and you could lock this paper corner with the tape so it won&#8217;t move. Cover the glossy magazine paper with another paper to protect it again the over heating (usually this glossy magazine paper is quite thin).</li>
</ul>
<ul>
<li>Power on the house hold iron and set it to its maximum heat, let it become hot for a while.</li>
</ul>
<ul>
<li>Pre-head the PCB copper surface for about 15 seconds, this will ensure the toner from the glossy magazine paper will transfer perfectly.</li>
</ul>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 04 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4240406517/"><img src="http://farm5.static.flickr.com/4031/4240406517_eca90150c6.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 04" width="581" height="437" /></a></p>
<ul>
<li>Flip the glossy paper magazine toward the PCB precisely; apply pressure and move gently the iron on the top of the paper for about 45 second, make sure you cover the whole PCB layout. Wait until it become cold, do not remove it while it is hot as it could harm your hand and the black toner plastic material will not transfer perfectly to the PCB copper surface.</li>
</ul>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 05 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4240407949/"><img src="http://farm5.static.flickr.com/4028/4240407949_bd62533c66.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 05" width="577" height="431" /></a></p>
<ul>
<li>Trim the glossy magazine paper so it has the same size as the PCB. Next immerse the PCB in the water and wait for a couple of minutes than gently pealing all the paper until it all being removed.</li>
</ul>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 06 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4241180830/"><img src="http://farm5.static.flickr.com/4054/4241180830_dd57ca779f.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 06" width="581" height="434" /></a></p>
<ul>
<li>Examine the PCB layout, and make sure all the connection is perfectly covered by the black toner. You could use the marker to repair the tiny un-perfectly covered area or you could repeat the whole process again by first removing all the black toner using the nail polish remover.</li>
</ul>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 07 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4241185004/"><img src="http://farm3.static.flickr.com/2749/4241185004_39ae28b57b.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 07" width="579" height="433" /></a></p>
<p><strong>The Etching Process</strong></p>
<ul>
<li>Prepare the etching solution in the plastic container by mixing four part of water with one part of Ferry Chloride (FeCl3) and it would be better to use the hot water about 40 to 50 centigrade degree because the chemical reaction will be faster at higher temperature.</li>
</ul>
<ul>
<li>Make sure you are wearing the disposal rubber hand glove and the eye protection goggle when working with this Ferry Chloride solution because this chemical acid solution is very corrosive and poison; you should always take extra precaution when working with this Ferry Chloride solution. If you accidentally exposed with this acid solution, make sure you wash it with plenty of water and soap.</li>
</ul>
<ul>
<li>Next immerse the PCB in the Ferry Chloride solution and gently move backward and forward the plastic container so the chemical solution could react with the PCB uncovered copper effectively. Keep watching until all the uncovered copper area becomes dissolved.</li>
</ul>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 08 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4240413955/"><img src="http://farm5.static.flickr.com/4018/4240413955_97c87ec46c.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 08" width="581" height="437" /></a></p>
<ul>
<li>Wash the PCB with plenty of water and soap; make sure you mix the Ferry Chloride solution with the water and soap solution to neutralize this remaining acid solution before you discard it. Usually the Ferry Chloride solution could only be used once, therefore use just enough amount as needed to etch your PCB. As a rule of thumb the acid solution area size should be a least twice as your PCB copper surface area size and the acid solution deep is about 10 mm to 20 mm.</li>
</ul>
<ul>
<li>The last process is to remove the black toner on the PCB copper surface with the nail polish remover (acetone); you could examine the black toner material while you are removing it and you will find out that it&#8217;s made from a plastic material.</li>
</ul>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 09 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4240415475/"><img src="http://farm5.static.flickr.com/4010/4240415475_1b19a64f5f.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 09" width="582" height="435" /></a></p>
<p><strong>The Finishing</strong></p>
<ul>
<li>Drill the PCB components hole with the 1mm drill bit size and the PCB stand off with the 3mm drill bit size. On this tutorial I&#8217;m using the general purpose hand drill to drill this PCB board.</li>
</ul>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 10 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4241189734/"><img src="http://farm3.static.flickr.com/2695/4241189734_61365b7971.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 10" width="579" height="433" /></a></p>
<ul>
<li>Again wash and scrub the PCB with water and soap to remove any dirt and grease before you start to solder the electronic components on it.</li>
</ul>
<p><strong>The BRAM-AXE Board Assembly</strong></p>
<p>To complete this tutorial we will assembly all the electronic components on the BRAM-AXE board, solder all the electronic components on it include the 28 pin IC socket and just leave it empty without the PICAXE 28&#215;1 microcontroller.</p>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 11 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4241191348/"><img src="http://farm5.static.flickr.com/4027/4241191348_8d364fab51.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 11" width="578" height="430" /></a></p>
<p>Now connect the 3xAA battery power to the board and make sure the power LED is lid; if not, disconnect the power and examine your 3xAA power connector make sure the positive and the ground pins are connect correctly to the board. Again examine all the electronics components if it ok, then disconnect the power and you could put the PICAXE 28&#215;1 microcontroller on the 28 pin IC socket.</p>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 12 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4240420555/"><img src="http://farm3.static.flickr.com/2722/4240420555_1014ee1d23.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 12" width="577" height="432" /></a></p>
<p><strong>The BRAM-AXE Board PCB Test</strong></p>
<p>After finish assembly all the electronic components now we are going to test this BRAM-AXE board I used this following test circuit to test both PICAXE 28&#215;1 microcontroller input and output:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_19.jpg"><img class="alignnone size-full wp-image-1534" title="diypcb_19" src="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_19.jpg" alt="" width="581" height="413" /></a></p>
<p>For the PICAXE 28&#215;1 microcontroller output I used the SIL (single in line) LED as shown on my previous posted blog <a title="Single In Line (SIL) LED Display for your Microcontroller Based Project" href=" http://www.ermicro.com/blog/?p=1044" target="_blank">Single In Line (SIL) LED Display for your Microcontroller Based Project</a> and for the PICAXE 28&#215;1 digital input I used a mini push button switch and 10K trimport for the PICAXE 28&#215;1 ADC (Analog to Digital Converter) input.</p>
<p><a title="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 13 by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4240421779/"><img src="http://farm5.static.flickr.com/4034/4240421779_1e0f233c4a.jpg" alt="Make your own Microcontroller Printed Circuit Board (PCB) using the Toner Transfer Method 13" width="577" height="431" /></a></p>
<p>The following is the PICAXE BASIC program code for testing the BRAM-AXE board.</p>
<pre>'***************************************************************************
'  File Name    : bram-axe.bas
'  Version      : 1.0
'  Description  : BRAM-AXE Board Testing Program
'  Author       : RWB
'  Target       : BRAM-AXE Learning Board - PICAXE 28x1
'  Intepreter   : PICAXE Firmware version A.6
'  IDE          : PICAXE Programming Editor Version 5.2.6
'  Programmer   : PICAXE Programming Editor Version 5.2.6
'  Last Updated : 03 January 2010
'***************************************************************************</pre>
<pre>' Use PICAXE 28x1 with 8 Mhz Internal Clock
#picaxe 28x1
SetFreq m8</pre>
<pre>' Assigned Variables
symbol adc_val = b0
symbol high_val = b1
symbol low_val = b2
symbol sign = b3</pre>
<pre>let pins = %00000000 ' Reset all Output Pins
sign = 0             ' Default Running LED</pre>
<pre>main:
  ' Read Mini Switch on IN0
  if pin0 = 0 then
    sign=sign XOR 1
  endif

  ' Read 10K Trimport ADC Value on ADC0
  readadc 0,adc_val  

  if sign = 1 then
    for high_val = 0 to 7
      low_val=high_val - 1
      low low_val
      high high_val    

      pause adc_val
    next high_val

    for high_val = 6 to 0 step -1
      low_val=high_val + 1
      low low_val
      high high_val    

      pause adc_val
    next high_val
  else
    let pins = %10101010
    pause adc_val
    let pins = %01010101
    pause adc_val
  endif

  goto main      ' Back to main loop
  end</pre>
<pre>' EOF: bram-axe.bas</pre>
<p>Connect the DB-9 female COM/RS232 connector to your computer COM/RS232 port or you could use the USB to COM/RS232 adapter to connect the board to the computer USB port as I did. Now power the board and run the PICAXE Programming Editor, paste the above code and save it as <em>bram-axe.bas</em>.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_20.jpg"><img class="alignnone size-full wp-image-1538" title="diypcb_20" src="http://www.ermicro.com/blog/wp-content/uploads/2010/01/diypcb_20.jpg" alt="" width="580" height="383" /></a></p>
<p>Click the &#8220;<strong>Syntax</strong>&#8221; button in the PICAXE Programming Editor to check the PICAXE BASIC program syntax and if it ok than you could continue to compile and download the code to the board by choosing the &#8220;<strong>Program</strong>&#8221; button.</p>
<p>The eight LED blink pattern will change as you press the mini switch button, while the 10K trimport is used to adjust the eight LED blink speed as it take advantage of the PICAXE 28&#215;1 microcontroller ADC capability to read the voltage value from the ADC 0 input.</p>
<p>Now you could enjoy this following video that shows all the BRAM-AXE Board PCB making process that we&#8217;ve been going through:</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/lchC-7SXWd0&amp;hl=en_US&amp;fs=1&amp;" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/lchC-7SXWd0&amp;hl=en_US&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p><strong>The Final Though</strong></p>
<p>As you&#8217;ve learned through this tutorial, making a decent quality single side PCB actually is not a very difficult task; it&#8217;s not required a complex process like the conventional PCB making method (i.e. using the film and photosensitive method). The more you practice the whole process the more you gain the experiences from it.</p>
<p>Either you are an electronics hobbyist or a professional making your own PCB is one of the important knowledge that you should acquired; just imagine this situation one day in the middle of the night suddenly you get an idea of a very cool electronics circuit and you eagerly want to prototype this circuit as soon as possible; now your experiences in making the PCB become handy as you could make your own prototype circuit PCB right away.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ermicro.com/blog/?feed=rss2&amp;p=1526</wfw:commentRss>
		</item>
		<item>
		<title>The 2009 Year End Notes</title>
		<link>http://www.ermicro.com/blog/?p=1503</link>
		<comments>http://www.ermicro.com/blog/?p=1503#comments</comments>
		<pubDate>Fri, 18 Dec 2009 04:59:36 +0000</pubDate>
		<dc:creator>rwb</dc:creator>
		
		<category><![CDATA[Blognote]]></category>

		<category><![CDATA[ADC]]></category>

		<category><![CDATA[AVR]]></category>

		<category><![CDATA[I2C]]></category>

		<category><![CDATA[LCD]]></category>

		<category><![CDATA[PIC]]></category>

		<category><![CDATA[PWM]]></category>

		<category><![CDATA[SPI]]></category>

		<guid isPermaLink="false">http://www.ermicro.com/blog/?p=1503</guid>
		<description><![CDATA[First of all I would like to thank to all the ermicroblog readers. I started to write this blog about a year ago; precisely on December 2008, during this year I received a lot of comments and questions and some of it were on my email. For me there are no such a silly questions [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/Sbz0yKZhcc2Zw0-qi-4qQ6F6ekQ/0/da"><img src="http://feedads.g.doubleclick.net/~a/Sbz0yKZhcc2Zw0-qi-4qQ6F6ekQ/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/Sbz0yKZhcc2Zw0-qi-4qQ6F6ekQ/1/da"><img src="http://feedads.g.doubleclick.net/~a/Sbz0yKZhcc2Zw0-qi-4qQ6F6ekQ/1/di" border="0" ismap="true"></img></a></p><p style="text-align: left;">First of all I would like to thank to all the ermicroblog readers. I started to write this blog about a year ago; precisely on December 2008, during this year I received a lot of comments and questions and some of it were on my email. For me there are no such a silly questions as I believe that as long as we live in this world we always in the continues learning mode state, therefore what seems silly to some people actually is a really serious question to the other people. I would do my best to answer all the questions regarding this blog contents; as answering the people&#8217;s question also is a part of the learning process.<span id="more-1503"></span></p>
<p style="text-align: left;">
<p style="text-align: left;"><a href="http://www.ermicro.com/blog/wp-content/uploads/2010/01/christ_02a.jpg"><img class="alignnone size-full wp-image-1523" title="christ_02a" src="http://www.ermicro.com/blog/wp-content/uploads/2010/01/christ_02a.jpg" alt="" width="596" height="202" /></a></p>
<p style="text-align: left;">
<p style="text-align: left;">On this first year I tried to post most of the Microcontroller&#8217;s foundation knowledge such as ADC, PWM, Interrupt Services, SPI and I2C, where this important knowledge is needed for understanding some of the next year advanced project. On the next year, I also plan to put some of my most popular posted articles and extent it with more examples and experiments into the ebook (PDF formatted).</p>
<p style="text-align: left;">I do hope this blog could bring a better understanding of how we could make use of the microcontroller in our learning stage or just for hobby and fun. Again thank you for your support and encouragement.</p>
<p style="text-align: center;"><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/12/christmas_card_2009.jpg"><img class="size-full wp-image-1504 aligncenter" title="christmas_card_2009" src="http://www.ermicro.com/blog/wp-content/uploads/2009/12/christmas_card_2009.jpg" alt="" width="521" height="402" /></a></p>
<h3 style="text-align: center;">&#8220;Glory to God in the highest,<br />
and on earth peace to men on whom his favor rests - Luke 2:14&#8243;</h3>
<h3 style="text-align: center;">Merry Christmas 25 December 2009 and Happy New Year 1 January 2010</h3>
<h3 style="text-align: center;">God Bless You</h3>
]]></content:encoded>
			<wfw:commentRss>http://www.ermicro.com/blog/?feed=rss2&amp;p=1503</wfw:commentRss>
		</item>
		<item>
		<title>PIC18 Pulse Width Modulation (PWM) DC Motor Speed Controller with the RPM Counter Project</title>
		<link>http://www.ermicro.com/blog/?p=1461</link>
		<comments>http://www.ermicro.com/blog/?p=1461#comments</comments>
		<pubDate>Wed, 09 Dec 2009 10:05:46 +0000</pubDate>
		<dc:creator>rwb</dc:creator>
		
		<category><![CDATA[Microcontroller]]></category>

		<category><![CDATA[ADC]]></category>

		<category><![CDATA[LCD]]></category>

		<category><![CDATA[PIC18F14K50]]></category>

		<category><![CDATA[PICKit2]]></category>

		<category><![CDATA[PWM]]></category>

		<category><![CDATA[RPM]]></category>

		<guid isPermaLink="false">http://www.ermicro.com/blog/?p=1461</guid>
		<description><![CDATA[Equipped with sophisticated Enhanced Capture/Compare/PWM (ECCP) peripheral the Microchip PIC18F14K50 microcontroller could produce up to four PWM channels output. The enhanced PWM (Pulse Width Modulation) mode in ECCP peripheral is capable to drive the full bridge DC Motor circuit directly both in forward or reverse direction. It also could generate single PWM output on the [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/4Pj9r1hnoGThhtbJWUE01GgeuQo/0/da"><img src="http://feedads.g.doubleclick.net/~a/4Pj9r1hnoGThhtbJWUE01GgeuQo/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/4Pj9r1hnoGThhtbJWUE01GgeuQo/1/da"><img src="http://feedads.g.doubleclick.net/~a/4Pj9r1hnoGThhtbJWUE01GgeuQo/1/di" border="0" ismap="true"></img></a></p><p>Equipped with sophisticated Enhanced Capture/Compare/PWM (ECCP) peripheral the Microchip PIC18F14K50 microcontroller could produce up to four PWM channels output. The enhanced PWM (Pulse Width Modulation) mode in ECCP peripheral is capable to drive the full bridge DC Motor circuit directly both in forward or reverse direction. It also could generate single PWM output on the selectable PIC18F14K50 pins when it configured in pulse steering mode.<span id="more-1461"></span> In this tutorial we will take advantage of PIC18F14K50 pulse steering mode to drive the DC Motor and at the same time we will build the RPM (Rotation per Minute) counter to observe the PWM effect on the DC Motor speed and display it on the 2&#215;16 LCD.</p>
<p><a title="PIC18 Pulse Width Modulation (PWM) DC Motor Speed Controller with the RPM Counter Project (1) by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4170763643/"><img src="http://farm3.static.flickr.com/2709/4170763643_f253a5ee7d.jpg" alt="PIC18 Pulse Width Modulation (PWM) DC Motor Speed Controller with the RPM Counter Project (1)" width="575" height="431" /></a></p>
<p><strong>The PWM and RPM Counter Project</strong></p>
<p>On this project we will use the HITEC C PRO PIC18 MCU Family Version 9.63PL3 and Microchip MPLAB IDE version 8.40 as our development tools platform. This project also serves as the learning tools of how to use many of the Microchip PIC18 advanced peripherals simultaneously to accomplish the project goal. You could see the complete project demonstrated on the video at the end of this tutorial; Ok now let&#8217;s list down all the project interesting features:</p>
<ul>
<li>Using Advanced 8-bit Microchip PIC18F14K50 microcontroller with <a title="PICJazz 20PIN Board" href="http://www.ermicro.com/blog/?p=15" target="_blank">PICJazz 20PIN</a> development board</li>
<li>Driving the HD44780U 2&#215;16 LCD in 4-bit data mode</li>
<li>Use DC Motor taken from discarded dual shock PS2 Playstation joystick and the Tamiya racing car tire for measuring the DC Motor RPM</li>
<li>Simple and easy to build RPM sensor with the infra red reflective object sensor</li>
<li>Use the ADC peripheral to read the trimport value for adjusting the DC Motor Speed and display the PWM duty cycle on the LCD</li>
<li>Use the PIC18F14K50 external interrupt and 16-bit TIMER0 counter to measure the RPM and display it on the LCD.</li>
</ul>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_01.jpg"><img class="alignnone size-full wp-image-1462" title="pic18_pwm_01" src="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_01.jpg" alt="" width="577" height="375" /></a></p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_02.jpg"><img class="alignnone size-full wp-image-1463" title="pic18_pwm_02" src="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_02.jpg" alt="" width="581" height="375" /></a></p>
<p>The following is the C code that makes this thing happens:</p>
<pre>/* ***************************************************************************
**  File Name    : pwmrpm.c
**  Version      : 1.0
**  Description  : PIC18 Pulse Width Modulation with RPM Counter
**  Author       : RWB
**  Target       : PICJazz 20PIN Board: PIC18F14K50
**  Compiler     : HI-TECH C PRO PIC18 MCU Family(Lite) Version 9.63PL3
**  IDE          : Microchip MPLAB IDE v8.40
**  Programmer   : PICKit2
**  Last Updated : 28 Nov 2009
** ***************************************************************************/
#include &lt;pic18.h&gt;</pre>
<pre>/*
** PIC18F14K50 Configuration Bit:
**
** FCMDIS     - Fail-Safe Clock Monitor disabled
** CPUDIV_0   - No CPU System Clock divide
** RCIO       - Internal RC Oscillator
** PLLDIS     - PLL is under software control
** ----------------------------------------------------------------------
** BORDIS     - Brown-out Reset disabled in hardware and software
** WDTDIS     - WDT is controlled by SWDTEN bit of the WDTCON register
** ----------------------------------------------------------------------
** MCLREN     - MCLR pin enabled, RE3 input pin disabled
** ----------------------------------------------------------------------
** XINSTDIS   - Disable extended instruction set (Legacy mode)
** LVPDIS     - Single-Supply ICSP disabled
*/</pre>
<pre>__CONFIG(1, FCMDIS &amp; CPUDIV_0 &amp; RCIO &amp; PLLDIS);
__CONFIG(2, BORDIS &amp; WDTDIS);
__CONFIG(3, MCLREN);
__CONFIG(4, XINSTDIS &amp; LVPDIS);
__CONFIG(5, 0xFFFF);
__CONFIG(6, 0xFFFF);
__CONFIG(7, 0xFFFF);</pre>
<pre>// LCD Definition
#define LCD_HOME 0x02
#define LCD_NEXT_LINE 0xC0
#define LCD_CLEAR 0x01
#define LCD_1CYCLE 0
#define LCD_2CYCLE 1</pre>
<pre>// RPM Counter Variable
volatile unsigned int rpm_value;
char sdigit[6]={'0','0','0','0','0','\0'};</pre>
<pre>/* Delay Function */
#define FOSC 16000000UL  // Using Internal Clock of 16 MHz
#define	delay_us(x) { unsigned char _dcnt; \
		       _dcnt = (x)/(24000000UL/FOSC)|1; \
		       while(--_dcnt != 0) continue; \
                    }</pre>
<pre>void delay_ms(unsigned int cnt)
{
  unsigned char i;
  do {
    i = 5;
    do {
      delay_us(164);
    } while(--i);
  } while(--cnt);
}</pre>
<pre>// PIC18 High-priority Interrupt Service
void interrupt high_isr(void){
  static unsigned char pulse_state=0;
  unsigned int rpm_timer;</pre>
<pre>  if (TMR0IF) {                     // Check for TIMER0 Overflow Interrupt
    rpm_value = 0;                  // Reset the RPM Value
    TMR0IF=0;                       // Clear TIMER0 interrupt flag
  }</pre>
<pre>  if (INT0IF){                      // Check for External INT0 Interrupt
    switch(pulse_state) {
      case 0:                       // First Low to High Pulse
        TMR0H = 0;                  // Zero the high byte in TMR0H Buffer
        TMR0L = 0;                  // Clear 16-bit TIMER0 Counter
        pulse_state=1;
        break;
      case 1:                       // Second Low to High Pulse
        rpm_timer=TMR0L;            // Get the first 8-bit TIMER0 Counter
        rpm_timer+=(TMR0H &lt;&lt; 8);    // Get the last 8-bit TIMER0 Counter</pre>
<pre>        // Calculate RPM = 60 x (1/Period)
        // RPM Value = 60000 (1 / (0.032 ms x rpm_timer))
        rpm_value = (int) (60000.0 / (0.032 * rpm_timer));
        pulse_state=0;
    }
    INT0IF = 0;                     // Clear INT0 interrupt flag
  }
}</pre>
<pre>/*
** LCD Routine
** LCD Data RB7,RB6,RB5,RB4
** LCD Control: RC7 -&gt; E-Enable, RC6 -&gt; RS-Register Select, R/W-Always 0
*/
void LCD_putcmd(unsigned char data,unsigned char cmdtype)
{
  // Put the Upper 4 bits data
  PORTB = data &amp; 0xF0;
  RC6=0;         // RS = 0
  RC7=1;         // E = 1</pre>
<pre>  // E=0; write data
  RC7=0;
  delay_us(1);    // Delay 1us for 16 MHz Internal Clock    

  // cmdtype = 0; One cycle write, cmdtype = 1; Two cycle writes
  if (cmdtype) {
    // Put the Lower 4 bits data
    PORTB = (data &amp; 0x0F) &lt;&lt; 4;
    RC6=0;       // RS = 0
    RC7=1;       // E = 1   

    // E=0; write data
    RC7=0;
    delay_us(1); // Delay 1us for 16 MHz Internal Clock
  }
  delay_ms(5);             // Wait for busy flag (BF)
}</pre>
<pre>void LCD_putch(unsigned char data)
{
  // Put the Upper 4 bits data
  PORTB = data &amp; 0xF0;
  RC6=1;         // RS = 1
  RC7=1;         // E = 1</pre>
<pre>  // E=0; write data
  RC7=0;
  delay_us(1);   // Delay 1us for 16 MHz Internal Clock       

  // Put the Lower 4 bits data
  PORTB = (data &amp; 0x0F) &lt;&lt; 4;
  RC6=1;         // RS = 1
  RC7=1;         // E = 1 

  // E=0; write data
  RC7=0;
  delay_ms(5);             // Wait for busy flag (BF)
}</pre>
<pre>void LCD_init(void)
{
  // Wait for more than 15 ms after VCC rises to 4.5 V
  delay_ms(30);</pre>
<pre>  // Send Command 0x30
  LCD_putcmd(0x30,LCD_1CYCLE);</pre>
<pre>  // Wait for more than 4.1 ms
  delay_ms(8);</pre>
<pre>  // Send Command 0x30
  LCD_putcmd(0x30,LCD_1CYCLE);</pre>
<pre>  // Wait for more than 100 us
  delay_us(200);          // Delay 250us for 16 MHz Internal Clock  ;</pre>
<pre>  // Send Command 0x30
  LCD_putcmd(0x30,LCD_1CYCLE);</pre>
<pre>  // Function set: Set interface to be 4 bits long (only 1 cycle write).
  LCD_putcmd(0x20,LCD_1CYCLE);</pre>
<pre>  // Function set: DL=0;Interface is 4 bits, N=1; 2 Lines, F=0; 5x8 dots font)
  LCD_putcmd(0x28,LCD_2CYCLE);</pre>
<pre>  // Display Off: D=0; Display off, C=0; Cursor Off, B=0; Blinking Off
  LCD_putcmd(0x08,LCD_2CYCLE);</pre>
<pre>  // Display Clear
  LCD_putcmd(0x01,LCD_2CYCLE);</pre>
<pre>  // Entry Mode Set: I/D=1; Increament, S=0; No shift
  LCD_putcmd(0x06,LCD_2CYCLE);</pre>
<pre>  // Display On, Cursor Off
  LCD_putcmd(0x0C,LCD_2CYCLE);
}</pre>
<pre>void LCD_puts(const char *s)
{
  while(*s != 0) {      // While not Null
    if (*s == '\n')
      LCD_putcmd(LCD_NEXT_LINE,LCD_2CYCLE);  // Goto Second Line
    else
      LCD_putch(*s);
    s++;
  }
}</pre>
<pre>// Implementing integer value from 0 to 65530
char *num2str(unsigned int number,unsigned char start_digit)
{
   unsigned char digit;</pre>
<pre>   if (number &gt; 65530) number = 0;    

   digit = '0';                       // Start with ASCII '0'
   while(number &gt;= 10000)             // Keep Looping for larger than 10000
   {
     digit++;                         // Increase ASCII character
     number -= 10000;                 // Subtract number with 10000
   }

   sdigit[0]='0';                     // Default first Digit to '0'
   if (digit != '0') sdigit[0]=digit; // Put the first digit</pre>
<pre>   digit = '0';                       // Start with ASCII '0'
   while(number &gt;= 1000)              // Keep Looping for larger than 1000
   {
     digit++;                         // Increase ASCII character
     number -= 1000;                  // Subtract number with 1000
   }</pre>
<pre>   sdigit[1]='0';                     // Default Second Digit to '0'
   if (digit != '0') sdigit[1]=digit; // Put the Second digit</pre>
<pre>   digit = '0';                       // Start with ASCII '0'
   while(number &gt;= 100)               // Keep Looping for larger than 100
   {
     digit++;                         // Increase ASCII character
     number -= 100;                   // Subtract number with 100
   }</pre>
<pre>   sdigit[2]='0';                     // Default Second Digit to '0'
   if (digit != '0') sdigit[2]=digit; // Put the Second digit</pre>
<pre>   digit = '0';                       // Start with ASCII '0'
   while(number &gt;= 10)                // Keep Looping for larger than 10
   {
     digit++;                         // Increase ASCII character
     number -= 10;                    // Subtract number with 10
   }</pre>
<pre>   sdigit[3]='0';                     // Default Second Digit to '0'
   if (digit != '0') sdigit[3]=digit; // Put the Second digit</pre>
<pre>   sdigit[4]='0' + number;
   return(sdigit + start_digit);
}</pre>
<pre>void main(void)
{
  unsigned char motor_stat,duty_cycle;</pre>
<pre>  OSCCON=0x70;         /* Select 16 MHz internal clock */</pre>
<pre>  // Initial PORT
  TRISA = 0x30;        // Input for RA4 and RA5
  TRISC = 0x01;        // Set RC0 as Input, RC&lt;7:1&gt; on PORTC as Output
  PORTC = 0x00;        // Initial Port C
  TRISB = 0x00;        // Set PORTB as Output
  PORTB = 0x00;        // Initial Port B
  TRISB = 0x00;	       // Set All on PORTB as Output
  ANSEL = 0x08;        // Set PORT AN3 to analog input
  ANSELH = 0x00;       // Set PORT AN8 to AN11 as Digital I/O</pre>
<pre>  // Initial LCD using 4 bits data interface
  LCD_init();
  LCD_puts("PICJazz 20-PIN\n");</pre>
<pre>  // Init ADC
  ADCON0=0b00001101;   // ADC port channel 3 (AN3), Enable ADC
  ADCON1=0b00000000;   // Use Internal Voltage Reference (Vdd and Vss)
  ADCON2=0b00101011;   // Left justify result, 12 TAD, Select the FRC for 16 MHz 

  // Init TIMER0: Period: 4 x Tosc x Prescale for each counter
  // Tosc = 1/16 Mhz = 0.0000000625
  // TIMER0 Period: 4 x 0.0000000625 x 128 = 0.000032 Second = 0.032 ms
  T0CON = 0b10000110;   // TIMER0 Enable, use 16-bit timer and prescale 1:128
  TMR0H = 0;            // Zero the high byte in TMR0H Buffer
  TMR0L = 0;            // Clear 16-bit TIMER0 Counter
  TMR0IE = 1;           // Enable TIMER0 Overflow Interrupt</pre>
<pre>  // Set the External Interrupt on INT0 (RC0) Port
  INT0IE = 1;          // Enables the INT0 external interrupt
  INTEDG0 = 1;         // Interrupt on rising edge

  // Init PWM for Single Output
  CCP1CON=0b00001100;  // Single PWM mode; P1A, P1C active-high; P1B, P1D active-high
  CCPR1L=0;            // Start with zero Duty Cycle
  PSTRCON=0b00000100;  // Enable PIC Pulse Steering PWM on RC3 Port</pre>
<pre>  // PWM Period = 4 x Tosc x (PR2 + 1) x TMR2 Prescale Value
  // Tosc = 1/16 Mhz = 0.0000000625
  // PWM Period = 4 x 0.0000000625 x 201 x 4 = 0.000201
  // PWM Frequency = 1/PWM Period = 1/0.000201 = 4.975 kHz
  T2CON=0b00000101;    // Postscale: 1:1, Timer2=On, Prescale = 1:4
  PR2=200;             // Frequency: 4.975 kHz
  TMR2=0;              // Start with zero Counter   

  // Initial Variable used
  rpm_value=0;
  motor_stat=0;        // Motor Off Condition
  duty_cycle=0;        // 0 Duty Cycle
  </pre>
<pre>  // Now Enable the Interrupt
  IPEN = 1;           // Enable High Priority Interrupt
  GIEH = 1;           // Global Interrupt Enable (High Priority)</pre>
<pre>  for(;;) {
    if (RA5 == 0) {   // Read Switch
      delay_ms(1);
      if (RA5 == 0) { // Read again for Simple Debounce
        motor_stat ^= 0x01;
      }
    }     

    if (motor_stat) {
      GODONE=1;
      while (GODONE) continue; // Wait conversion done
      duty_cycle=ADRESH;       // Get the High byte ADC 8-bit result
    } else {
      duty_cycle=0;
    }

    // Assign duty cycle to the PWM CCPR1L register
    CCPR1L = duty_cycle;</pre>
<pre>    // Display the Information on the LCD
    LCD_putcmd(LCD_HOME,LCD_2CYCLE);      // LCD Home
    LCD_puts("Duty Cycle: "); LCD_puts(num2str((int)((duty_cycle/255.0) * 100.0),3));
    LCD_puts(" %");
    LCD_putcmd(LCD_NEXT_LINE,LCD_2CYCLE); // Goto Second Line
    LCD_puts("RPM: "); LCD_puts(num2str(rpm_value,1));

    // Put the delay here
    delay_ms(10);
  }
}</pre>
<pre>/* EOF: pwmrpm.c */</pre>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_15.jpg"><img class="alignnone size-full wp-image-1465" title="pic18_pwm_15" src="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_15.jpg" alt="" width="575" height="404" /></a></p>
<p><strong>The PIC18 Pulse Steering PWM mode</strong></p>
<p>The heart of the PIC18F14K50 pulse steering PWM mode is rely on the TIMER2 peripheral, where it used as the basic counter generator for the PWM signal. The TIMER2 counter clock (<strong>TMR2</strong>) is supplied by selectable prescale clock, this prescale circuit will divide the system clock by 1, 4 or 16 respectively. The prescale could be selected by assigning the <strong>T2CKPS1</strong> and <strong>T2CKPS0</strong> bits in the <strong>T2CON</strong> register.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_07.jpg"><img class="alignnone size-full wp-image-1466" title="pic18_pwm_07" src="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_07.jpg" alt="" width="578" height="375" /></a></p>
<p>The <strong>TMR2</strong> register value is continuously compared to the <strong>PR2</strong> register which determine the TOP value of the <strong>TMR2</strong> counter register. When the <strong>TMR2</strong> register value reach the <strong>PR2</strong> value, then the <strong>TMR2</strong> counter register value will be reset to <strong>0</strong>.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_08.jpg"><img class="alignnone size-full wp-image-1467" title="pic18_pwm_08" src="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_08.jpg" alt="" width="577" height="291" /></a></p>
<p>At the same time the value of <strong>TMR2</strong> counter register is also being compared to the <strong>CCPR1L</strong> register value (actually with the <strong>CCPR1H</strong> register value, since the <strong>CCPR1H</strong> equal to <strong>CCPR1L</strong> than we could say <strong>CCPR1L</strong>), when the <strong>TMR2</strong> reach the <strong>CCPR1L</strong> value than the PWM peripheral circuit will reset the <strong>CCP1</strong> output (logical &#8220;<strong>0</strong>&#8220;) and when the <strong>TMR2</strong> counter register equal to the <strong>PR2</strong> register value than it will set the <strong>CCP1</strong> output (logical &#8220;<strong>1</strong>&#8220;). Therefore by changing the <strong>PR2</strong> value we could change the PWM period and this mean changing the PWM frequency as well. The PWM period could be calculated using this following formula:</p>
<p><strong>PWM period = 4 x Tosc x ( PR2 + 1)  x  (TMR2 prescale value)</strong> second</p>
<p>Where Tosc is the system clock period in second</p>
<p><strong>PWM frequency = 1 / PWM Period</strong> Hz</p>
<p>By assigning the <strong>PR2</strong> register with <strong>200</strong> and select the prescale to <strong>4</strong>; and applying all these values to the formula above, we could determine the PWM frequency for our DC Motor base on the internal system oscillator of 16 MHz as follow:</p>
<p>PWM period = 4 x (1 / 16.000.000) x 201 x 4 = <strong>0.000201 second</strong></p>
<p>Therefore the PWM frequency is:</p>
<p>PWM frequency = 1 / 0.000201 = <strong>4.975 kHz</strong></p>
<p>The <strong>T2CON</strong> (TIMER2 Control) register is used select the postscale (<strong>T2OUTPS&lt;3:0&gt;</strong>), activate the TIMER2 peripheral (<strong>TMR2ON</strong>) and set the prescale clock used by the <strong>TMR2</strong> counter register. BY setting the <strong>T2CKPS1=0</strong> and <strong>T2CKPS0=1</strong> in the <strong>T2CON</strong> register we select the 1:4 prescale; and by setting the <strong>TMR2ON</strong> to logical &#8220;<strong>1</strong>&#8221; we activate the TIMER2 peripheral.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_09.jpg"><img class="alignnone size-full wp-image-1470" title="pic18_pwm_09" src="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_09.jpg" alt="" width="579" height="79" /></a><br />
The following is the C code to initialize the TIMER2 peripheral:</p>
<pre>// PWM Period = 4 x Tosc x (PR2 + 1) x TMR2 Prescale Value
// Tosc = 1/16 Mhz = 0.0000000625
// PWM Period = 4 x 0.0000000625 x 201 x 4 = 0.000201
// PWM Frequency = 1/PWM Period = 1/0.000201 = 4.975 kHz
T2CON=0b00000101;    // Postscale: 1:1, Timer2=On, Prescale = 1:4
PR2=200;             // Frequency: 4.975 kHz
TMR2=0;              // Start with zero Counter</pre>
<p>By setting <strong>P1M1=0</strong> and <strong>P1M0=0</strong> bits in the <strong>CCP1CON</strong> register we select the single output PWM; setting the <strong>CCP1M3=1, CCP1M2=1, CCP1M1=0</strong> and <strong>CCP1M0=0</strong> in the <strong>CCP1CON</strong> register we select the PWM mode with P1A, P1C active-high; P1B, P1D active-high.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_10.jpg"><img class="alignnone size-full wp-image-1471" title="pic18_pwm_10" src="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_10.jpg" alt="" width="580" height="260" /></a></p>
<p>On this tutorial we just set the additional 2 LSB extended bits (<strong>DC1B1</strong> and <strong>DC1B0</strong>) to all zero (logical &#8220;<strong>0</strong>&#8220;) for the <strong>CCPR1L</strong> register (10-bit wide). We start by setting the<strong> CCPR1L</strong> to zero mean we start with zero duty cycle (no PWM output yet). In single PWM mode we could select the PWM output to PIC18F14K50 <strong>RC3</strong> output port by setting the <strong>STRC</strong> bit on <strong>PSTRCON</strong> (Pulse Steer Control) register to the logical &#8220;<strong>1</strong>&#8221; while other bits is set to logical &#8220;<strong>0</strong>&#8220;. The following is the C code:</p>
<pre>// Init PWM for Single Output
CCP1CON=0b00001100;  // Single PWM mode; P1A, P1C active-high; P1B, P1D active-high
CCPR1L=0;            // Start with zero Duty Cycle
PSTRCON=0b00000100;  // Enable PIC Pulse Steering PWM on RC3 Port</pre>
<p>By applying the analog value read from the 10K trimport connected to the PIC18F14K50 <strong>RA4</strong> pins, we could easily varying the PWM duty cycle by changing the voltage divider output formed by the 10K trimport. The following C code shows how we use the PIC18F14K50 microcontroller ADC peripheral to change the PWM duty cycle; for more information about using the ADC peripheral on PIC18 families you could read my previous posted blog <a title="PIC18 Microcontroller Analog to Digital Converter with Microchip C18 Compiler" href="http://www.ermicro.com/blog/?p=1408" target="_blank">PIC18 Microcontroller Analog to Digital Converter with Microchip C18 Compiler</a>:</p>
<pre>// Init ADC
ADCON0=0b00001101;   // ADC port channel 3 (AN3), Enable ADC
ADCON1=0b00000000;   // Use Internal Voltage Reference (Vdd and Vss)
ADCON2=0b00101011;   // Left justify result, 12 TAD, Select the FRC for 16 MHz
...
...</pre>
<pre>if (motor_stat) {
  GODONE=1;
  while (GODONE) continue; // Wait conversion done
  duty_cycle=ADRESH;       // Get the High byte ADC 8-bit result
} else {
  duty_cycle=0;
}

// Assign duty cycle to the PWM CCPR1L register
CCPR1L = duty_cycle;</pre>
<p>The enhanced PWM feature on the PIC18 families actually is almost identical to the PIC16 families series, therefore you could read more about the enhanced PWM feature if you want to drive the H-bridge DC motor circuit from my previous posted blog <a title="H-Bridge Microchip PIC Microcontroller PWM Motor Controller" href="http://www.ermicro.com/blog/?p=706" target="_blank">H-Bridge Microchip PIC Microcontroller PWM motor Controller</a>.<br />
<strong></strong></p>
<p><strong>The RPM Sensor</strong></p>
<p>To count the DC motor rotation per minute (RPM), I decided to use the infra red reflective object sensor Junye JY209-01 or you could replace it with Fairchild QRE00034; as this type of sensor will make sensing the DC motor rotation become easier.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_11.jpg"><img class="alignnone size-full wp-image-1472" title="pic18_pwm_11" src="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_11.jpg" alt="" width="575" height="411" /></a></p>
<p>The infra red reflective object sensor work by simply emitting the infra red beam and when it encounter the white object surface than the infra red beam will be reflected back to the phototransistor; next the phototransistor and the 2N3904 transistor which formed the Darlington pair will start to conduct and will generate enough voltage across the 470 Ohm resistor to be considered by the PIC18F14K50 microcontroller build in Schmitt trigger <strong>RC0</strong> input port as the logical &#8220;<strong>1</strong>&#8220;. When the infra red beam encounters the black tire surface than both of the phototransistor and 2N3904 transistor will turn off; and the voltage across 470 Ohm resistor will drop to zero volt (logical &#8220;<strong>0</strong>&#8220;).</p>
<p>Therefore by timing the generated pulse period by the infra red reflective object sensor we could easily calculate the RPM using this following formula:</p>
<p><strong>Frequency = 1/T</strong> Hz; T is the generated pulse period in second.</p>
<p><strong>RPM (Rotation per Minute) = Frequency x 60</strong></p>
<p>The following pictures show in detail of how I put the PS2 Playstation dual shock DC motor, the Tamiya racing car tire with the white sticker and the infra red reflective object sensor for this project.</p>
<p><a title="PIC18 Pulse Width Modulation (PWM) DC Motor Speed Controller with the RPM Counter Project (2) by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4170762705/"><img src="http://farm3.static.flickr.com/2701/4170762705_c08b55081c.jpg" alt="PIC18 Pulse Width Modulation (PWM) DC Motor Speed Controller with the RPM Counter Project (2)" width="576" height="431" /></a></p>
<p><a title="PIC18 Pulse Width Modulation (PWM) DC Motor Speed Controller with the RPM Counter Project (3) by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4170765217/"><img src="http://farm3.static.flickr.com/2545/4170765217_c66f1acb59.jpg" alt="PIC18 Pulse Width Modulation (PWM) DC Motor Speed Controller with the RPM Counter Project (3)" width="579" height="433" /></a></p>
<p><strong>The PIC18 External Interrupt</strong></p>
<p>As I mention before that in order to calculate the DC motor RPM, we could simply calculate the period of the pulse generated by the RPM sensor shown above. One of the methods to measure the pulse period is to use the PIC18F14K50 microcontroller ECCP (Enhanced Capture/Compare/PWM) peripheral in the capture mode to calculate the period; in the capture mode we could easily use the 16-bit TIMER1 or TIMER3 to count the pulse period by feeding the pulse directly to the <strong>CCP1</strong> pins (<strong>RC5</strong>). The capture interrupt will be generated every rising edge of the pulse (or falling edge), therefore by knowing the exact TIMER1 or TIMER3 counter clock time period and get the timer 16-bit counted value between the two rising edge pulse we could calculate the RPM.</p>
<p>Unfortunately we could not use the Microchip PIC18F14K50 microcontroller ECCP peripheral as this peripheral has already being used to generate the PWM signal for the DC motor in our project, but using the same principal we could make use of the PIC18F14K50 external interrupt peripheral on pin <strong>INT0</strong> (<strong>RC0</strong>) or <strong>INT1</strong> (<strong>RC1</strong>). This external interrupt peripheral will generate interrupt on every rising edge (or falling edge) of the pulse; therefore by combining it with the 16-bit TIMER0 counter mode now we could calculate the RPM as shown on the following diagram.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_12.jpg"><img class="alignnone size-full wp-image-1474" title="pic18_pwm_12" src="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_12.jpg" alt="" width="576" height="309" /></a></p>
<p>As shown on the above picture, first we have to activate the PIC18F14K50 microcontroller external interrupt and configure it to detect the pulse rising edge; next we configure the TIMER0 peripheral for the RPM period counter.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_13.jpg"><img class="alignnone size-full wp-image-1475" title="pic18_pwm_13" src="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_13.jpg" alt="" width="578" height="321" /></a></p>
<p>By setting the <strong>TMR0E</strong> and <strong>INT0IE</strong> bits to logical &#8220;<strong>1</strong>&#8221; on the PIC18F14K50 microcontroller interrupt control register (<strong>INTCON</strong>) and <strong>TMT0ON</strong> bits to logical &#8220;<strong>1</strong>&#8221; on the TIMER0 control register (<strong>T0CON</strong>), we activate both the TIMER0 and External peripherals. Selecting the 1:256 prescale value we could calculate the time required to increase the TIMER0 16-bit counter.</p>
<p><strong>TIMER0 Clock period = 4 x Tosc x TMR2 prescale value</strong> second</p>
<p>TIMER0 Clock period = 4 x (1/16.000.000) x 128 = 0.000032 second = <strong>0.032 ms</strong></p>
<p>This mean the TIMER0 counter required <strong>0.032 ms</strong> to increase the <strong>TMR0L</strong> and <strong>TMR0H</strong> registers counter value by one. The following C code shows the PIC18F14K50 microcontroller external interrupt and TIMER0 peripherals initialization:</p>
<pre>// Init TIMER0: Period: 4 x Tosc x Prescale for each counter
// Tosc = 1/16 Mhz = 0.0000000625
// TIMER0 Period: 4 x 0.0000000625 x 128 = 0.000032 Second = 0.032 ms
T0CON = 0b10000110;   // TIMER0 Enable, use 16-bit timer and prescale 1:128
TMR0H = 0;            // Zero the high byte in TMR0H Buffer
TMR0L = 0;            // Clear 16-bit TIMER0 Counter
TMR0IE = 1;           // Enable TIMER0 Overflow Interrupt</pre>
<pre>// Set the External Interrupt on INT0 (RC0) Port
INT0IE = 1;          // Enables the INT0 external interrupt
INTEDG0 = 1;         // Interrupt on rising edge</pre>
<p>By setting the <strong>INTEDG0</strong> to logical &#8220;<strong>1</strong>&#8221; on <strong>INTCON2</strong> (interrupt Control 2) register we choose the Rising Edge detection. The RPM value is being calculated inside the interrupt service routine as shown on this following C code:</p>
<pre>// PIC18 High-priority Interrupt Service
void interrupt high_isr(void){
  static unsigned char pulse_state=0;
  unsigned int rpm_timer;</pre>
<pre>  if (TMR0IF) {                     // Check for TIMER0 Overflow Interrupt
    rpm_value = 0;                  // Reset the RPM Value
    TMR0IF=0;                       // Clear TIMER0 interrupt flag
  }</pre>
<pre>  if (INT0IF){                      // Check for External INT0 Interrupt
    switch(pulse_state) {
      case 0:                       // First Low to High Pulse
        TMR0H = 0;                  // Zero the high byte in TMR0H Buffer
        TMR0L = 0;                  // Clear 16-bit TIMER0 Counter
        pulse_state=1;
        break;
      case 1:                       // Second Low to High Pulse
        rpm_timer=TMR0L;            // Get the first 8-bit TIMER0 Counter
        rpm_timer+=(TMR0H &lt;&lt; 8);    // Get the last 8-bit TIMER0 Counter</pre>
<pre>        // Calculate RPM = 60 x (1/Period)
        // RPM Value = 60000 (1 / (0.032 ms x rpm_timer))
        rpm_value = (int) (60000.0 / (0.032 * rpm_timer));
        pulse_state=0;
    }
    INT0IF = 0;                     // Clear INT0 interrupt flag
  }
}</pre>
<p>By resetting the TIMER0 counter on the first rising edge external interrupt and reading back the TIMER0 counter on the second rising edge external interrupt we could easily calculate the pulse period</p>
<p><strong>Pulse Period = 0.032 x TIMER0 Counter (TMR0H:TMR0L)</strong> millisecond</p>
<p>The RPM value is the frequency of rotation measured in minute (60 second), therefore the DC motor RPM value could be calculated as the following formula:</p>
<p><strong>rpm_value</strong> = (1 / Pulse Period) in second x 60 = 60000.0 / 0.032 x <strong>rpm_timer</strong></p>
<p>The <strong>rpm_timer</strong> variable contains the 16-bit TIMER0 counter value, while the global <strong>rpm_value</strong> contain the RPM value of the DC motor.</p>
<p><strong>The PICKit2 Logic Analyzer</strong></p>
<p>To check the RPM counter accuracy I simply use one of the useful feature of the Microchip PICKit2 programmer, where we could use it as the powerful Logic Analyzer tools to debug serial communication buses such as UART, SPI and I2C. This time we will use it to analyze the RPM pulse produce by the infra red reflective object sensor circuit above and connected the output directly to the PIC18F14K50 microcontroller <strong>RC0</strong> input port and the PICKit2 channel 3 inputs to measure the RPM pulse period.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_14.jpg"><img class="alignnone size-full wp-image-1476" title="pic18_pwm_14" src="http://www.ermicro.com/blog/wp-content/uploads/2009/12/pic18_pwm_14.jpg" alt="" width="578" height="378" /></a></p>
<p>The PICKit2 Logic Analyzer tool could be used by running the PICKit2 programmer Version v2.61 and selecting the Logic Tools from the Tools menu; set the Rising Edge trigger on the channel 3 and 100 ms Sample Rate; next press the RUN button. After the pulse appears check the Cursor checkbox to activate the X and Y horizontal bar to measure the pulse period.</p>
<p>As shown on the above picture, the channel 3 on the PICKit2 logic analyzer tool show that the measured pulse frequency is about 77.52 Hz; this mean the RPM is about <strong>4651</strong> (77.52 x 60) which is close enough to the RPM calculated value <strong>4641</strong> displayed on the LCD at 72% PWM duty cycle.</p>
<p><strong>The 2&#215;16 LCD Display</strong></p>
<p>To display both of the PWM duty cycle and RPM value, I used the Hitachi HD44780U or the equivalent microcontroller 2&#215;16 LCD with back light LED in 4-bit data mode. Most of the LCD function C routine I use in this project is taken from my previous posted blog <a title="VR LCD Thermometer Using ADC and PWM Project" href="http://www.ermicro.com/blog/?p=519" target="_blank">AVR LCD Thermometer Using ADC and PWM Poject</a>; where you could read more information about the principal of how to drive this kind of display. The following is the list of C function for driving the LCD:</p>
<ul>
<li>LCD_putch() function is used to display single character on the LCD</li>
<li>LCD_putcmd() function is used to send LCD command (e.g. clear the LCD, move to second row, etc)</li>
<li>LCD_init() function is used to initialized the 2&#215;16 LCD; this function will initialized the 2&#215;16 LCD into 4-bit data mode</li>
<li>LCD_puts() function is used to display a string on the LCD</li>
<li>num2str() function is used to convert a numeric value to a string, we use this function to display numeric value on the LCD.</li>
</ul>
<p><strong>Inside the C Code</strong></p>
<p>The C program begins by selecting the 16 MHz internal clock and setting all the I/O ports used on this project. After doing the LCD, ADC, TIMER0, External Interrupt and  PWM/TIMER2 peripherals setup. After enabling the high priority interrupt and activating the global interrupt the code enter the <strong>for(;;)</strong> endless loop. Inside this loop; first we read the user&#8217;s switch, this switch is attached to the PIC18F14K50 microcontroller input port RA5 and work as the toggle switch to run or stop the DC motor:</p>
<pre>if (RA5 == 0) {   // Read Switch
  delay_ms(1);
  if (RA5 == 0) { // Read again for Simple Debounce
    motor_stat ^= 0x01;
  }
}</pre>
<p>The PIC18F14K50 microcontroller ADC peripheral than read the analog input on channel 3 (<strong>RA4</strong>) port and assigned the value to the <strong>CCPR1L</strong> register where it used to control the PWM duty cycle.</p>
<pre>if (motor_stat) {
  GODONE=1;
  while (GODONE) continue; // Wait conversion done
  duty_cycle=ADRESH;       // Get the High byte ADC 8-bit result
} else {
 duty_cycle=0;
}</pre>
<pre>// Assign duty cycle to the PWM CCPR1L register
CCPR1L = duty_cycle;
Then next we display the duty cycle and the RPM value on the 2x16 LCD:</pre>
<pre>// Display the Information on the LCD
LCD_putcmd(LCD_HOME,LCD_2CYCLE);      // LCD Home
LCD_puts("Duty Cycle: "); LCD_puts(num2str((int)((duty_cycle/255.0) * 100.0),3));
LCD_puts(" %");
LCD_putcmd(LCD_NEXT_LINE,LCD_2CYCLE); // Goto Second Line
LCD_puts("RPM: "); LCD_puts(num2str(rpm_value,1));</pre>
<p><strong>Downloading and Running the Code</strong></p>
<p>After compiling and simulating your code hook up your PICKit2 programmer to the PICJazz 20PIN development and learning board ICSP port turn power on. From the MPLAB IDE menu select <strong>Programmer -&gt; Select Programmer -&gt; Pickit2</strong> it will automatically configure the connection and display it on the PICkit2 tab Output windows; now you are ready to down load the code from MPLAB IDE menu select <strong>Programmer -&gt; Program</strong>; this will down load the HEX code into the PICJazz 20PIN board with the Microchip PIC18F14K50 microcontroller on it.</p>
<p>Now its time to run your PWM and RPM counter code, where you could enjoy this following video showing all of the process that we&#8217;ve been going through.</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/ic7t_mYDPWM&amp;hl=en_US&amp;fs=1&amp;" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/ic7t_mYDPWM&amp;hl=en_US&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p><strong>The Final Though</strong></p>
<p>Obviously the Microchip PIC18 PWM peripheral feature is almost identical to its 8-bit PIC16 little brother families, therefore upgrading from the PIC16 to PIC18 could be done almost without changing the code. In this project we also take advantage of the infra red reflective object sensor to measure the RPM, where we don&#8217;t have to make special modification on the wheel in order to make this sensor to sense the rotation; all we need is just a piece of white sticker attached to the wheel.</p>
<p>This project also shows us an example of how we use many of the advanced PIC18F14K50 microcontroller peripherals at the same time, which will help to shape our understanding of how we could utilize all of these peripherals in the future project.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ermicro.com/blog/?feed=rss2&amp;p=1461</wfw:commentRss>
		</item>
		<item>
		<title>PIC18 Microcontroller Analog to Digital Converter with Microchip C18 Compiler</title>
		<link>http://www.ermicro.com/blog/?p=1408</link>
		<comments>http://www.ermicro.com/blog/?p=1408#comments</comments>
		<pubDate>Sun, 08 Nov 2009 15:49:13 +0000</pubDate>
		<dc:creator>rwb</dc:creator>
		
		<category><![CDATA[Microcontroller]]></category>

		<category><![CDATA[ADC]]></category>

		<category><![CDATA[C18]]></category>

		<category><![CDATA[PIC18]]></category>

		<category><![CDATA[PIC18LF44K50]]></category>

		<guid isPermaLink="false">http://www.ermicro.com/blog/?p=1408</guid>
		<description><![CDATA[The Microchip PIC18 Microcontroller family is the Microchip highest performance 8-bit class microcontroller. Powered by advanced RISC CPU, this PIC18 microcontroller family could deliver up to 16 MIPS computing power compared to the other Microchip 8-bit microcontroller family such as PIC10, PIC12 and PIC16 which only could deliver up to 5 MIPS. The PIC18 microcontroller [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/ozcumYpedtagtMcL7N_K7n1lT8s/0/da"><img src="http://feedads.g.doubleclick.net/~a/ozcumYpedtagtMcL7N_K7n1lT8s/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/ozcumYpedtagtMcL7N_K7n1lT8s/1/da"><img src="http://feedads.g.doubleclick.net/~a/ozcumYpedtagtMcL7N_K7n1lT8s/1/di" border="0" ismap="true"></img></a></p><p>The Microchip PIC18 Microcontroller family is the Microchip highest performance 8-bit class microcontroller. Powered by advanced RISC CPU, this PIC18 microcontroller family could deliver up to 16 MIPS computing power compared to the other Microchip 8-bit microcontroller family such as PIC10, PIC12 and PIC16 which only could deliver up to 5 MIPS. The PIC18 microcontroller architecture is optimized to be programmed in C language and supporting many advance industrial standard interface peripherals such as I2C, SPI, UART, USB, CAN, Ethernet, LCD and Touch Screen; this make the PIC18 microcontroller family become a popular choice to the new 8-bit embedded system design.<span id="more-1408"></span></p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_00.jpg"><img class="alignnone size-full wp-image-1409" title="pic18_adc_00" src="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_00.jpg" alt="" width="575" height="242" /></a></p>
<p>The Microchip C18 compiler is designed to fully utilize the PIC18 microcontroller capabilities, together with Microchip C18 royalty free advanced library such as USB, TCP/IP stack and ZigBee wireless network protocol make the C18 compiler is the best choice to many embedded system designer and hobbyists.</p>
<p>On this tutorial we will learn of how to program the PIC18LF14K50 microcontroller ADC peripheral using the Microchip C18 compiler. The principal we learn here could be applied to the other Microchip PIC18 microcontroller family as well. On this tutorial we will use this following simplify ermicro <a title="PICJazz 20PIN Learning and Development Board" href="http://www.ermicro.com/blog/?p=15" target="_blank">PICJazz 20PIN board</a> schematic.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_07.jpg"><img class="alignnone size-full wp-image-1410" title="pic18_adc_07" src="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_07.jpg" alt="" width="574" height="356" /></a></p>
<p>After placing the PIC18LF14K50 to the 20 pin socket; using the female to female cable jumper that come with the PICJazz 20PIN board we could easily reconfigure the user trimport and user switch to <strong>RA4</strong> and <strong>RA5</strong> respectively.</p>
<p><a title="PIC18 Microcontroller Analog to Digital Converter with Microchip C18 Compiler by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4086240882/"><img src="http://farm3.static.flickr.com/2557/4086240882_1efdfbe4bd.jpg" alt="PIC18 Microcontroller Analog to Digital Converter with Microchip C18 Compiler" width="547" height="410" /></a></p>
<p>On this tutorial we will use two different approaches to program the PIC18 microcontroller family analog to digital converter (ADC) peripheral. The first one is to use the standard C programming code to manipulate the PIC18LF14K50 ADC peripheral registers and the second one is to use the Microchip C18 ADC wrap-up library. Now take a look on our first code:</p>
<pre>/* ***************************************************************************
**  File Name    : adc.c
**  Version      : 1.0
**  Description  : Analog to Digital Converter
**  Author       : RWB
**  Target       : PICJazz 20PIN Board: PIC18LF14K50
**  Compiler     : Microchip C18 v3.34 C Compiler
**  IDE          : Microchip MPLAB IDE v8.30
**  Programmer   : Microchip PICKit2 Programmer
**  Last Updated : 28 Oct 2009
** ***************************************************************************/
#include &lt;p18cxxx.h&gt;
#include &lt;delays.h&gt;</pre>
<pre>/*
** PIC18LF14K50 Configuration Bit:
**
** FOSC = IRC        - Internal RC Oscillator
** CPUDIV = NOCLKDIV - No CPU System Clock divide
** PLLEN = OFF       - PLL is under software control
** FCMEN = OFF       - Fail-Safe Clock Monitor disabled
** BOREN = OFF       - Brown-out Reset disabled in hardware and software
** WDTEN = OFF       - WDT is controlled by SWDTEN bit of the WDTCON register
** MCLRE = ON        - MCLR pin enabled, RE3 input pin disabled
** LVP = OFF         - Single-Supply ICSP disabled
*/
#pragma config FOSC = IRC, CPUDIV = NOCLKDIV, PLLEN = OFF
#pragma config FCMEN = OFF, BOREN = OFF
#pragma config WDTEN = OFF, MCLRE = ON, LVP = OFF</pre>
<pre>// Delay in 1 ms (approximately) for 16 MHz Internal Clock
void delay_ms(unsigned int ms)
{
  do {
    Delay1KTCYx(4);
  } while(--ms);
}</pre>
<pre>void main(void)
{
  unsigned char chSign,chEye,chType,iCount;
  unsigned int iDelay;
  unsigned char led_patern[] = {0b00000000,0b00000001,
                                0b00000011,0b00000111,
                                0b00001111,0b00001111,
                                0b00001110,0b00001100,
                                0b00001000,0b00000000};</pre>
<pre>  OSCCON=0x70;         // Select 16 MHz internal clock</pre>
<pre>  TRISC = 0x00;        // Set All on PORTC as Output
  TRISA = 0x30;        // Input for RA4 and RA5
  ANSEL = 0b00001000;  // Set PORT AN3 to analog input
  ANSELH = 0;          // Set PORT AN8 to AN11 as Digital I/O</pre>
<pre>  /* Init ADC */
  ADCON0=0b00001101;   // ADC port channel 3 (AN3), Enable ADC
  ADCON1=0b00000000;   // Use Internal Voltage Reference (Vdd and Vss)
  ADCON2=0b10101011;   // Right justify result, 12 TAD, Select the FRC for 16 MHz</pre>
<pre>  chEye=0x01;          // Initial Eye Variables with 0000 0001
  chSign=0;
  iDelay=100;
  chType=0;
  iCount=0;

  for(;;) {
    ADCON0bits.GO=1;
    while (ADCON0bits.GO);   // Wait conversion done</pre>
<pre>    iDelay=ADRESL;           // Get the 8 bit LSB result
    iDelay += (ADRESH &lt;&lt; 8); // Get the 2 bit MSB result</pre>
<pre>    // Display the LED
    if (PORTAbits.RA5 == 0) {
      chType=~chType;
      chSign=0;
    }</pre>
<pre>    if (chType == 0) {
      PORTC=led_patern[iCount++];
      delay_ms(iDelay);          // Call Delay function
      if(iCount == 10) iCount=0;
    } else {
      if (chSign == 0) {
	PORTC=chEye;
	delay_ms(iDelay);        // Call Delay function
	chEye=chEye &lt;&lt; 1;
  	if (chEye &gt; 0x04) chSign=1;
      } else {
        PORTC=chEye;
        delay_ms(iDelay);        // Call Delay function
        chEye=chEye &gt;&gt; 1;
	if (chEye &lt;= 0x01) chSign=0;
      }
    }
  }
}</pre>
<pre>/* EOF: adc.c */</pre>
<p><strong>The Analog to Digital Converter C Code</strong></p>
<p>This program basically will show the running LED pattern attached to <strong>RC0, RC1, RC2</strong> and <strong>RC3</strong> ports on the PIC18LF14K50 microcontroller; the display speed is controlled by the voltage value reads from the user&#8217;s trimport on the port <strong>RA4</strong> (<strong>AN3</strong> - analog input channel 3). This voltage value will be converted by the PIC18LF14K50 ADC peripheral and passing the converted numeric value as the delay argument on the <strong>delay_ms()</strong> function inside the loop. Pressing the user&#8217;s switch will change the running LED pattern.</p>
<p>The user&#8217;s trimport work as the voltage divider circuit and provide voltage input level to the microcontroller analog port (<strong>AN3</strong>); therefore by changing the trimmer means we change the voltage level input and this also will change the running LED speed.</p>
<p>The user&#8217;s switch works as a toggle switch, by pressing it once will switch to the second running LED pattern; pressing once again will switch back to the first running LED pattern.</p>
<p>Just for your reference, you could read of how to program the Microchip PIC16 microcontroller family ADC peripheral using HITEC PICC on my previous posted blog <a title="PIC Analog to Digital Converter C Programming" href="http://www.ermicro.com/blog/?p=660" target="_blank">PIC Analog to Digital Converter C Programming</a>. Ok now let&#8217;s take a look at the PIC18LF14K50 microcontroller ADC peripheral registers (for more information please refers to the datasheet).</p>
<p><strong>1. ADCON0: A/D CONTROL REGISTER 0</strong></p>
<p>The function of this register is to control the microcontroller ADC operation such as power on the ADC circuit, start converting, channel selection, ADC voltage reference selection and ADC result format presentation selection.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_08.jpg"><img class="alignnone size-full wp-image-1412" title="pic18_adc_08" src="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_08.jpg" alt="" width="574" height="261" /></a></p>
<p>The <strong>CHS3, CHS2, CHS1</strong> and <strong>CHS0</strong> bits are used to select the ADC input channel, by setting all these bits to &#8220;<strong>0011</strong>&#8221; means we choose the channel 3 or <strong>AN3</strong> (PIN 3) port which connected to the user&#8217;s trimport.</p>
<pre>ADCON0=0b00001101;   // ADC port channel 3 (AN3), Enable ADC</pre>
<p>Powering the ADC circuit is simply turning on the <strong>ADON</strong> bit by setting it to logical &#8220;<strong>1</strong>&#8221; and to instruct the PIC microcontroller to start the conversion we just turn on the <strong>GO/DONE</strong> bit (logical &#8220;<strong>1</strong>&#8220;) and wait until this bit turn off when the PIC18LF14K50 microcontroller ADC peripheral done with the conversion; we use the C while statement to wait the ADC conversion as this following code:</p>
<pre>ADCON0bits.GO=1;
while (ADCON0bits.GO);   // Wait conversion done</pre>
<p><strong>2. ADCON1: A/D CONTROL REGISTER 1</strong></p>
<p>The PIC18LF14K50 microcontroller ADC peripheral uses the successive approximation method to do the conversion; this method required the reference voltage in order to work. The <strong>ADCON1</strong> register is use to tell the ADC peripheral whether we want to use the internal or external voltage references.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_09.jpg"><img class="alignnone size-full wp-image-1413" title="pic18_adc_09" src="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_09.jpg" alt="" width="580" height="105" /></a></p>
<p>By setting all of these bits to logical &#8220;<strong>0</strong>&#8220;, we simply tell the PIC18LF44K50 microcontroller ADC peripheral to use its own internal voltage reference for the conversion.</p>
<pre>ADCON1=0b00000000;   // Use Internal Voltage Reference (Vdd and Vss)</pre>
<p><strong>3. ADCON2: A/D CONTROL REGISTER 2</strong></p>
<p>The <strong>ADCON2</strong> register is use to set of how the 10-bit ADC result will be presented in two 8-bit registers (<strong>ADRESH</strong> and <strong>ADRESL</strong>) and to select the ADC acquisition time and the ADC conversion clock.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_11.jpg"><img class="alignnone size-full wp-image-1414" title="pic18_adc_11" src="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_11.jpg" alt="" width="574" height="334" /></a></p>
<p>By setting the <strong>ADFM</strong> bit to logical &#8220;<strong>1</strong>&#8221; we use the &#8220;<strong>right justified</strong>&#8221; result. This mean the higher 2 bits value will be place in the <strong>ADRESH</strong> register and the lower 8 bits value are in the <strong>ADRESL</strong> register. By using the C left shifting operation, we could get this 10-bit value as this following code:</p>
<pre>iDelay = ADRESL;           // Get the 8 bit LSB result
iDelay += (ADRESH &lt;&lt; 8);   // Get the 2 bit MSB result</pre>
<p>In order for ADC circuit inside the PIC18LF44K50 microcontroller to meet its specified accuracy than ADC clock period (<strong>ADCS</strong>) and the ADC acquisition time (<strong>ACQT</strong>) should be set accordingly</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_12.jpg"><img class="alignnone size-full wp-image-1416" title="pic18_adc_12" src="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_12.jpg" alt="" width="573" height="438" /></a></p>
<p>The recommended minimum value for the ADC acquisition time before the ADC circuit start to do the conversion is about <strong>7.45 us</strong> and when we use the PIC8LF14K50 microcontroller internal oscillator the time required to convert 1-bit analog data is about <strong>1.7 us</strong>; therefore it would be safe to choose twice the minimum acquisition time:</p>
<pre>TACQT = 2 x 7.45 us = <strong>14.9 us</strong></pre>
<p>The closest ACQT setting for this purpose is to set the ACQT to 12 TAD which give us</p>
<pre>TACQT = 12 TAD = 12 x 1.7 us = <strong>20.4 us</strong></pre>
<p>If you are not using the TACQT (<strong>ACQT2 = 0, ACQT1= 0</strong> and <strong>ACQT0 = 0</strong>) you have to do the delay in program before starting the AD conversion, therefore it always safer to use the PIC18LF14K50 microcontroller ACQT feature. The following is the complete <strong>ADCON2</strong> setup:</p>
<pre>ADCON2=0b10101011;   // Right justify result, 12 TAD, Select the FRC for 16 MHz</pre>
<p>The PIC18LF14K50 microcontroller ADC peripheral is also capable of generating interrupt when it finish the conversion by setting the ADC interrupt flag <strong>ADIF</strong> bit to logical &#8220;<strong>1</strong>&#8221; in <strong>PIR1</strong> register, but for the purpose of this tutorial we will not use this facility.</p>
<p><strong>Initial the Internal Oscillator clock and ADC Port</strong></p>
<p>One of the most important setup before we could use the PIC ADC peripheral is to configure the PIC18LF14K50 microcontroller internal clock and ports.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_19.jpg"><img class="alignnone size-full wp-image-1417" title="pic18_adc_19" src="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_19.jpg" alt="" width="570" height="383" /></a></p>
<p>By setting the <strong>IRCF2, IRCF1</strong> and <strong>IRCF0</strong> bits to logical &#8220;<strong>1</strong>&#8221; on the <strong>OSCCON</strong> register we choose the 16 MHz internal clock from the high frequency internal oscillator (HFINTOSC):</p>
<pre>OSCCON=0x70;         // Select 16 MHz internal clock</pre>
<p>For the analog input this can be done by setting the <strong>TRISA, ANSEL</strong> and <strong>ANSELH</strong> registers as the following code:</p>
<pre>TRISC = 0x00;        // Set All on PORTC as Output
TRISA = 0x30;        // Input for RA4 and RA5
ANSEL = 0b00001000;  // Set PORT AN3 to analog input
ANSELH = 0;          // Set PORT AN8 to AN11 as Digital I/</pre>
<p>Because the we use both <strong>RA4</strong> and <strong>RA5</strong> ports as an input ports (<strong>RA4</strong> for user&#8217;s trimport and <strong>RA5</strong> for user&#8217;s switch) then we turn on the tristate input gate on these ports by setting the <strong>TRISA</strong> to <strong>0&#215;30</strong> and enabling the <strong>AN3</strong> analog input selection by setting the <strong>ANSEL</strong> to <strong>0b00001000</strong> (<strong>0&#215;08</strong> hex value). For the LED&#8217;s, we just enabling all of the PORTC tristate gate ports as an output by setting the <strong>TRISC</strong> register to all zero.</p>
<p><strong>Downloading your C18 ADC project Code</strong></p>
<p>After compiling and simulating your code hook up your PICKit2 programmer to the PICJazz 20PIN board ICSP port turn on the PICJazz 20PIN power. From the MPLAB IDE menu select <strong>Programmer -&gt; Select Programmer -&gt; Pickit2</strong> it will automatically configure the connection and display it on the PICkit2 tab Output windows:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_13.jpg"><img class="alignnone size-full wp-image-1418" title="pic18_adc_13" src="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_13.jpg" alt="" width="574" height="453" /></a></p>
<p>You could get the information about the size of your program by opening the <strong>adc.map</strong> file in your project directory or you could use the MPLAB IDE Memory Usage Gauge facility from the MPLAB IDE menu select <strong>View -&gt; Memory Usage Gauge</strong> as follow:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_14.jpg"><img class="alignnone size-full wp-image-1421" title="pic18_adc_14" src="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_14.jpg" alt="" width="340" height="195" /></a></p>
<p>Now you are ready to down load the code from MPLAB IDE menu select <strong>Programmer -&gt; Program</strong>; this will down load the HEX code into the PICJazz 20PIN board:</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_06.jpg"><img class="alignnone size-full wp-image-1420" title="pic18_adc_06" src="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_06.jpg" alt="" width="567" height="163" /></a></p>
<p>You could see the running code on the video at the end of this tutorial.<br />
<strong></strong></p>
<p><strong>Using the Microchip C18 ADC Peripheral Library</strong></p>
<p>As I mention before that on this tutorial we are going to use the alternative approach to code the PIC18LF14K50 microcontroller ADC peripheral, where we are going to use the Microchip C18 ADC wrap-up library (version 3.34). Using this library supposedly will be much easier than coding the PIC18F14K50 microcontroller ADC peripheral registers directly especially for the beginners; &#8230;hmm let&#8217;s find out how ease this approach comparing to the first one.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_15.jpg"><img class="alignnone size-full wp-image-1424" title="pic18_adc_15" src="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_15.jpg" alt="" width="570" height="416" /></a></p>
<p>Looking at the Microchip C18 library (<strong>MPLAB_C18_Libraries_51297f.pdf</strong>) document the ADC library could be used as the above diagram. First we have to use the <strong>OpenADC()</strong> function to initial the PIC18LF14K50 microcontroller ADC peripheral registers. From the library we know that this function is depend on the microcontroller type we used; therefore to get more detail information about this ADC library we have to read other C18 peripheral library document file found at <strong>&lt;MCC18_DIR&gt;\doc\periph-lib\AD Converter.htm</strong>. From this reference document you will get 12 version of the <strong>OpenADC()</strong> function and for the PIC18LF14K50 microcontroller we have to use the ADC_V10 <strong>OpenADC()</strong> function as follow (taken from the <strong>AD Converter.htm</strong> document):</p>
<pre>Function: Configure the A/D convertor.
Include: adc.h
Prototype:</pre>
<pre>void OpenADC(unsigned char config,
                      unsigned char config2 ,
                      unsigned char config3,
                      unsigned int portconfig);</pre>
<pre>Code Example:</pre>
<pre><strong>With AND mask:</strong>
OpenADC( ADC_FOSC_32      &amp;
         ADC_RIGHT_JUST   &amp;
         ADC_12_TAD,
         ADC_CH0          &amp;
         ADC_REF_VDD_VSS  &amp;
         ADC_INT_ON, ADC_10ANA);</pre>
<pre><strong>With OR mask:</strong>
OpenADC( ADC_FOSC_32 |
         ADC_RIGHT_JUST |
         ADC_12_TAD,
         ADC_CH0 |
         ADC_REF_VDD_VSS |
         ADC_INT_ON, ADC_10ANA);</pre>
<p>This ADC_V10 <strong>OpenADC()</strong> function will accept 4 arguments: <strong>config, config2, config3</strong> and <strong>portconfig</strong>; you should remember here for other PIC18 microcontroller type the <strong>OpenADC()</strong> function version may accept only 2 or 3 arguments instead of 4 arguments therefore its important to know which <strong>OpenADC()</strong> function to be used when you use the other type of PIC18 microcontroller family. There is no general <strong>OpenADC()</strong> function that work to all PIC18 microcontroller type.</p>
<p>The ADC_V10 <strong>OpenADC()</strong> function first argument (<strong>config</strong>) is used to set the A/D Clock Source, A/D result justification and the A/D acquisition time. The second argument (<strong>config2</strong>) is used to set the A/D channel, A/D interrupt and some miscellaneous option. The third argument (<strong>config3</strong>) is used to set the A/D Vref+ and Vref- configuration and last the fourth argument (<strong>portconfig</strong>) is used to set the analog channel port (i.e. <strong>ANSEL</strong> and <strong>ANSELH</strong> registers).</p>
<p>Now we continue to look at the ADC_V10 <strong>OpenADC()</strong> function code example and you will notice that the example only use 3 arguments instead of 4 arguments? Is this mean that the example document give us a wrong information? Now its time to open the ADC_V10 <strong>OpenADC()</strong> function wrap-up library to find out how this thing really work. Opening the <strong>&lt;MCC18_DIR&gt;\src\pmc_common\ADC\adcopen.c</strong> file and here what we get for the ADC_V10 <strong>OpenADC()</strong> function:</p>
<pre>#elif defined (ADC_V10)
void OpenADC( unsigned char config,
              unsigned char config2,
              unsigned char config3,
              unsigned int portconfig)
{
    ADCON0 = 0;
    ADCON1 = 0;
    ADCON2 = 0;</pre>
<pre>    ADCON0 = (config2 &gt;&gt; 1) &amp; 0b00111100;  //channel selection
    ADCON1 = (config3 &amp; 0b00001100) | //Positive Voltage Reference Configuration bits
             (config3 &amp; 0b00000011);  //Negative Voltage Reference Configuration bits
    ADCON2 = (config &amp; 0b10000000) |
             ((config &gt;&gt; 4) &amp; 0b00000111) | //A/D Conversion Clock Select bits
             ((config &lt;&lt; 2) &amp; 0b00111000);  //A/D Acquisition Time Select bits</pre>
<pre>    ANSEL = portconfig;			//Didn't Change
    ANSELH = (portconfig &gt;&gt; 8);		//Didn't Change

	if( config2 &amp; 0b10000000 )		//interrupt enable check
    {
      PIR1bits.ADIF = 0;			//Clear the ADC Interrupt bit
      PIE1bits.ADIE = 1;			//Enable the ADC Interrupt
      INTCONbits.PEIE = 1;			//Peripheral Interrupt Enable
    }
    //A/D Conversion Status bit--A/D converter module is operating
    ADCON0bits.ADON = 1;
}</pre>
<p>Hmm,.. this ADC_V10 <strong>OpenADC()</strong> function use 4 arguments after all (the example document is wrong) and this code seem similar to what we have already done on the first example; by carefully reading the necessary bits definition in the <strong>&lt;MCC18_DIR&gt;\h\adc.h</strong> file and used them with the AND mask as the arguments to the ADC_V10 <strong>OpenADC()</strong> function; do some debugging on the required PIC18LF14K50 registers, finally I came with this following C code:</p>
<pre>/* ***************************************************************************
**  File Name    : adc2.c
**  Version      : 1.1
**  Description  : Analog to Digital Converter
**                 Using C18 ADC Peripheral Wrap-up Library
**  Author       : RWB
**  Target       : PICJazz 20PIN Board: PIC18LF14K50
**  Compiler     : Microchip C18 v3.34 C Compiler
**  IDE          : Microchip MPLAB IDE v8.30
**  Programmer   : PICKit2
**  Last Updated : 28 Oct 2009
** ***************************************************************************/
#include &lt;p18cxxx.h&gt;
#include &lt;delays.h&gt;
#include &lt;adc.h&gt;</pre>
<pre>/*
** PIC18LF14K50 Configuration Bit:
**
** FOSC = IRC        - Internal RC Oscillator
** CPUDIV = NOCLKDIV - No CPU System Clock divide
** PLLEN = OFF       - PLL is under software control
** FCMEN = OFF       - Fail-Safe Clock Monitor disabled
** BOREN = OFF       - Brown-out Reset disabled in hardware and software
** WDTEN = OFF       - WDT is controlled by SWDTEN bit of the WDTCON register
** MCLRE = ON        - MCLR pin enabled, RE3 input pin disabled
** LVP = OFF         - Single-Supply ICSP disabled
*/
#pragma config FOSC = IRC, CPUDIV = NOCLKDIV, PLLEN = OFF
#pragma config FCMEN = OFF, BOREN = OFF
#pragma config WDTEN = OFF, MCLRE = ON, LVP = OFF</pre>
<pre>// Delay in ms (approximately) for 16 MHz Internal Clock
void delay_ms(unsigned int ms)
{
  do {
    Delay1KTCYx(4);
  } while(--ms);
}</pre>
<pre>void main(void)
{
  unsigned char chSign,chEye,chType,iCount;
  unsigned int iDelay;
  unsigned char led_patern[] = {0b00000000,0b00000001,
                                0b00000011,0b00000111,
                                0b00001111,0b00001111,
                                0b00001110,0b00001100,
                                0b00001000,0b00000000};</pre>
<pre>  OSCCON=0x70;         // Select 16 MHz internal clock</pre>
<pre>  TRISC = 0x00;        // Set All on PORTC as Output
  TRISA = 0x30;        // Input for RA4 and RA5  

  /* Init ADC */
  /*
  ** Original:
  ** #define ADC_REF_VDD_VDD 0b11111001 // ADC voltage source VREF+ = AVDD
  **
  ** We redefine this definition to ADC_REF_VDD_VDD_X
  */
  #define ADC_REF_VDD_VDD_X 0b11110011 	// ADC voltage source VREF+ = AVDD</pre>
<pre>  OpenADC(ADC_FOSC_RC &amp; ADC_RIGHT_JUST &amp; ADC_12_TAD,
          ADC_CH3 &amp; ADC_INT_OFF,
          ADC_REF_VDD_VDD_X &amp; ADC_REF_VDD_VSS,
          0b00001000);</pre>
<pre>  chEye=0x01;          // Initial Eye Variables with 0000 0001
  chSign=0;
  iDelay=100;
  chType=0;
  iCount=0;  

  for(;;) {
    ConvertADC();                 // Start conversion
    while(BusyADC());             // Wait for completion
    iDelay = ReadADC();           // Read result

    // Display the LED
    if (PORTAbits.RA5 == 0) {
      chType=~chType;
      chSign=0;
    }</pre>
<pre>    if (chType == 0) {
      PORTC=led_patern[iCount++];
      delay_ms(iDelay);           // Call Delay function
      if(iCount == 10) iCount=0;
    } else {
      if (chSign == 0) {
	PORTC=chEye;
	delay_ms(iDelay);         // Call Delay function
	chEye=chEye &lt;&lt; 1;
  	if (chEye &gt; 0x04) chSign=1;
      } else {
        PORTC=chEye;
        delay_ms(iDelay);         // Call Delay function
        chEye=chEye &gt;&gt; 1;
	if (chEye &lt;= 0x01) chSign=0;
      }
    }
  }
  CloseADC(); // Disable A/D converter
}</pre>
<pre>/* EOF: adc2.c */</pre>
<p>As you&#8217;ve seen from the above code, I had to redefine the third argument definition <strong>ADC_REF_VDD_VDD </strong>(<strong>0b11111001</strong>) to <strong>ADC_REF_VDD_VDD_X</strong> (<strong>0b11110011</strong>) to make the ADC_V10 <strong>OpenADC()</strong> function to set the PIC18LF14K50 microcontroller <strong>ADCON1</strong> register correctly (using the internal voltage reference). I think this is a bug for the ADC_V10 type on the Microchip C18 version 3.34 ADC peripheral libraries.</p>
<p>For starting and reading the ADC channel using this library is just a little bit easier comparing to the first approach as shown on this following code:</p>
<pre>ConvertADC();                 // Start conversion
while(BusyADC());             // Wait for completion
iDelay = ReadADC();           // Read result</pre>
<p>Just for curiosity, I put this following memory usage gauge to compare the memory usage between using the Microchip C18 ADC peripheral library approach and without using it; and it&#8217;s obvious that using the first approach give you a lesser HEX code size.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_17.jpg"><img class="alignnone size-full wp-image-1422" title="pic18_adc_17" src="http://www.ermicro.com/blog/wp-content/uploads/2009/11/pic18_adc_17.jpg" alt="" width="577" height="243" /></a></p>
<p>After compiling and downloading the HEX code to the PICJazz 20PIN board you will have similar result as shown on this following video:</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/LY9p6xRYr8g&amp;hl=en&amp;fs=1&amp;" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/LY9p6xRYr8g&amp;hl=en&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p><strong>The Final Though</strong></p>
<p>Is obviously the Microchip C18 (version 3.34) ADC peripheral library is not ease to use at all and certainly is not intended to be used by the beginners; as you will have to read many documents and must have a good understanding of the PIC18 microcontroller ADC principal in order to make this library work for you. My suggestion is to avoid using the Microchip C18 ADC peripheral library (especially if you are really not understand the PIC18 microcontroller ADC peripheral) and use direct register manipulation as shown on the first approach to handle your PIC18 microcontroller ADC project.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ermicro.com/blog/?feed=rss2&amp;p=1408</wfw:commentRss>
		</item>
		<item>
		<title>Introduction to the Embedded System with PICAXE Microcontroller</title>
		<link>http://www.ermicro.com/blog/?p=1334</link>
		<comments>http://www.ermicro.com/blog/?p=1334#comments</comments>
		<pubDate>Sat, 17 Oct 2009 15:23:17 +0000</pubDate>
		<dc:creator>rwb</dc:creator>
		
		<category><![CDATA[Microcontroller]]></category>

		<category><![CDATA[7SEGMENT]]></category>

		<category><![CDATA[ADC]]></category>

		<category><![CDATA[BASIC]]></category>

		<category><![CDATA[DS18B20]]></category>

		<category><![CDATA[PIC]]></category>

		<category><![CDATA[PICAXE]]></category>

		<guid isPermaLink="false">http://www.ermicro.com/blog/?p=1334</guid>
		<description><![CDATA[These days we are living and surrounding by many tiny computers called embedded products. Unlike the general purpose desktop computer that we use for browsing or typing our email, this tiny computer is designed to do only a limited specific task. We could easily found this embedded product just about anywhere such as home appliance [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/Ogs7FgE7X9PHCCxEvM6C8IEflBQ/0/da"><img src="http://feedads.g.doubleclick.net/~a/Ogs7FgE7X9PHCCxEvM6C8IEflBQ/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/Ogs7FgE7X9PHCCxEvM6C8IEflBQ/1/da"><img src="http://feedads.g.doubleclick.net/~a/Ogs7FgE7X9PHCCxEvM6C8IEflBQ/1/di" border="0" ismap="true"></img></a></p><p>These days we are living and surrounding by many tiny computers called embedded products. Unlike the general purpose desktop computer that we use for browsing or typing our email, this tiny computer is designed to do only a limited specific task. We could easily found this embedded product just about anywhere such as home appliance (e.g. washing machine, refrigerator, microwave oven, TV/DVD), personal gadget (e.g. cell phone, MP3 players, digital camera), car (e.g. anti lock brake system, GPS navigation, alarm) and many more.  The tremendous use of the embedded products in our daily life make the embedded system become one of the most important and interest subject to be learned.<span id="more-1334"></span></p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_05.jpg"><img class="alignnone size-full wp-image-1335" title="picaxe_05" src="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_05.jpg" alt="" width="580" height="296" /></a></p>
<p>Now come to the most asked question &#8220;<strong>How do I start it?</strong>&#8221; Actually when we learn the embedded system; we learn two things at the same time, the first one is the hardware which usually a microcontroller and the second one is the software to program the microcontroller to perform the desire task. Therefore most of the microcontroller manufacture already provides this learning tool all in one package (starter kits) to introduce their product to the microcontroller newcomer. Unfortunately most of this learning tool is aimed for the people that already have an adequate knowledge both in hardware and programming language.</p>
<p>Considering this need, many companies and the open source community start to offer their own embedded system development framework version, where this framework mostly aim for student, hobbyist and even to the professional.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_03.jpg"><img class="alignnone size-full wp-image-1337" title="picaxe_03" src="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_03.jpg" alt="" width="584" height="247" /></a></p>
<p>Among these embedded system learning and development framework I found that the PICAXE from <strong>Revolution Education Ltd</strong> in UK (<strong>http://www.rev-ed.co.uk/picaxe</strong>) is one of the best in terms of price and ease to be used. Equipped with the free sophisticated PICAXE Programming Editor to program, debug, simulate and download the code to the PICAXE microcontroller; therefore this makes the PICAXE framework is a perfect choice for the embedded system newcomer.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_00a.jpg"><img class="alignnone size-full wp-image-1338" title="picaxe_00a" src="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_00a.jpg" alt="" width="580" height="364" /></a></p>
<p>The PICAXE Programming Editor also can automatically generate the BASIC program code base on the flowchart you draw.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_04.jpg"><img class="alignnone size-full wp-image-1339" title="picaxe_04" src="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_04.jpg" alt="" width="583" height="497" /></a></p>
<p>The PICAXE microcontroller is based on various range of Microchip PIC microcontroller, start from 8 pins up to 40 pins PIC microcontroller.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_06.jpg"><img class="alignnone size-full wp-image-1340" title="picaxe_06" src="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_06.jpg" alt="" width="584" height="247" /></a></p>
<p><strong>The PICAXE Microcontroller</strong></p>
<p>Unlike the other BASIC interpreter framework mention above which use the EEPROM to store the program, the PICAXE is a stand alone microcontroller solution, where both of program and the BASIC interpreter firmware is stored in the PIC microcontroller own memory (FLASH and/or EEPROM). Therefore the PICAXE only required a very view external electronic component in order to make it work.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_07.jpg"><img class="alignnone size-full wp-image-1343" title="picaxe_07" src="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_07.jpg" alt="" width="579" height="323" /></a></p>
<p>In fact you could drop the 180 ohm resistor (connect pin 19 directly to TX) shown on the above schematic, which left only two resistors (22K and 10K) to make this PICAXE microcontroller a fully function embedded  learning system. Connecting the RS232 DB-9 female connector to your computer COM port or USB to RS232 converter and you are ready to program this PICAXE microcontroller.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_08.jpg"><img class="alignnone size-full wp-image-1344" title="picaxe_08" src="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_08.jpg" alt="" width="578" height="276" /></a></p>
<p>How ease to program this PICAXE microcontroller? To find it out, I did a small research; First I taught my fourth grade son (9 years old) just an essential PICAXE BASIC (<em>Beginner&#8217;s All-Purpose Symbolic Instruction Code</em>) language commands to program the PICAXE microcontroller output which connected to four LED and let him play with it for a while; later on I asked him to program the PICAXE 20M using the AXEJazz 20PIN learning and development board from <a title="Microcontrollers and Electronics e-Shop" href="http://www.ermicro.com" target="_blank">ermicro</a>, so it could display the back and forth running LED. This is the PICAXE BASIC code that he came with and I called it &#8220;<strong>HelloWorld.bas</strong>&#8221; (I put some comments on the original code to make it clear).</p>
<pre>'--------------------------------------------------------------------
' Program Name : HelloWorld.bas
' Description  : HelloWorld PICAXE Program
' Author       : TRB
' Target       : AXEJazz 20PIN Learning Board
' Interpreter  : PICAXE Firmware version 3.B
' IDE          : PICAXE Programming Editor Version 5.2.6
' Programmer   : PICAXE Programming Editor Version 5.2.6
' Last Update  : 15 Aug 2009
'--------------------------------------------------------------------
#picaxe 20M

symbol led7=7    ' LED7 Attached to PICAXE Output 7
symbol led6=6    ' LED6 Attached to PICAXE Output 6
symbol led5=5    ' LED5 Attached to PICAXE Output 5
symbol led4=4    ' LED5 Attached to PICAXE Output 5

main: high led7  ' Set High Output 7
      low led6   ' Set Low Output 6
      low led5   ' Set Low Output 5
      low led4   ' Set Low Output 4
      pause 100  ' Pause 100ms

      low led7
      high led6
      low led5
      low led4
      pause 100  ' Pause 100ms

      low led7
      low  led6
      high led5
      low led4
      pause 100  ' Pause 100ms  

      low led7
      low led6
      low led5
      high led4
      pause 100  ' Pause 100ms

      low led7
      low led6
      high led5
      low led4
      pause 100  ' Pause 100ms

      low led7
      high led6
      low led5
      low led4
      pause 100  ' Pause 100ms                        

      goto main  ' Back to the main label
      end              

' EOF: HelloWorld.bas</pre>
<p>As you&#8217;ve seen from the code above, this is a simple endless loop program with contain only three PICAXE BASIC <strong>high, low</strong> and <strong>pause</strong> commands. Even though this program looks naïve to the more experience BASIC programmer, but it proves that programming the PICAXE microcontroller is not difficult. To read more about the PICAXE you could visit their website mention above; they have a very good PICAXE user manual and the PICAXE BASIC user reference.<br />
<strong></strong></p>
<p><strong>The PICAXE Analog to Digital Converter</strong></p>
<p>Using the same Input/Output circuit above, I enhanced the <strong>HelloWorld.bas</strong> program so we could control the LED display speed using the 10K trimport connected to the PICAXE <strong>Input 7</strong> (ADC input) and at the same time reading the digital input connected to the PIXAXE <strong>Input 3</strong> to change the LED display behavior</p>
<pre>'***************************************************************************
'  File Name    : blinkled.bas
'  Version      : 1.0
'  Description  : PICAXE Blinking LED
'  Author       : RWB
'  Target       : AXEJazz 20PIN Learning Board
'  Interpreter  : PICAXE Firmware version 3.B
'  IDE          : PICAXE Programming Editor Version 5.2.6
'  Programmer   : PICAXE Programming Editor Version 5.2.6
'  Last Updated : 22 September 2009
'***************************************************************************</pre>
<pre>' Use PICAXE 20M
#picaxe 20M</pre>
<pre>' Assign Variables
symbol adc_val = b0  ' Use b0 for adc_val
symbol high_val = b1
symbol low_val = b2
symbol sign = b3</pre>
<pre>let pins = %00000000 ' Reset all Output Pins
sign = 0             ' Default Running LED</pre>
<pre>main:
  ' Read User Switch on Input IN3
  if pin3 = 0 then
    sign=sign XOR 1
  endif

  ' Read Trimport ADC Value on Input IN7
  readadc 7,adc_val  

  if sign = 0 then
    for high_val = 4 to 7
      low_val=high_val - 1
      low low_val
      high high_val    

      pause adc_val
    next high_val

    for high_val = 6 to 4 step -1
      low_val=high_val + 1
      low low_val
      high high_val    

      pause adc_val
    next high_val
  else
    let pins = %11000000
    pause adc_val
    let pins = %00110000
    pause adc_val
  endif

  goto main      ' Back to the main loop
  end</pre>
<pre>' EOF: blinkled.bas</pre>
<p>The trimport is used as the voltage divider circuit, where adjusting the trimport will varying the voltage on the PICAXE <strong>Input 7</strong>. Using the PICAXE BASIC <strong>readadc</strong> command we could easily capture the voltage changing and represent it in digital form (Analog to Digital Conversion). Then we use this ADC value to set the LED display delay using <strong>pause adc_val</strong> command. For comparison of how to do this ADC stuff in C language you could read my previous posted blog <a title="PIC Analog to Digital Converter C Programming" href="http://www.ermicro.com/blog/?p=660" target="_blank">PIC Analog to Digital Converter C Programming</a>.</p>
<p>By examining the PICAXE <strong>Input3</strong> status (<strong>pin3</strong>), we could determine whether this pin is low (button pressed) or high (button release) and use this status to toggle the <strong>sign</strong> variable for different LED display behavior</p>
<pre>' Read User Switch on Input IN3
if pin3 = 0 then
  sign=sign XOR 1
endif</pre>
<p>The PICAXE BASIC interpreter use a predefined general purpose variables to store temporary data on the microcontroller RAM, these general purpose variables is called &#8220;<strong>bit</strong>&#8221; for 1-bit operation, &#8220;<strong>b</strong>&#8221; for 8-bit operation and &#8220;<strong>w</strong>&#8221; for 16-bit operation. The total variable that you could utilize is depending on the PICAXE type we use.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_13.jpg"><img class="alignnone size-full wp-image-1345" title="picaxe_13" src="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_13.jpg" alt="" width="579" height="228" /></a></p>
<p>Using the PICAXE BASIC <strong>symbol</strong> command, we could redefine the variable name to become more readable instead of just using the <strong>b0</strong>, <strong>b1,b2</strong> and <strong>b3</strong> as shown on this following code:</p>
<pre>' Assigned Variable
symbol adc_val = b0  ' Use b0 for adc_val
symbol high_val = b1
symbol low_val = b2
symbol sign = b3</pre>
<p>The last is the <strong>let pins</strong> command, this special command is used to set or clear the PICAXE output port, therefore by assigning the pins with binary value of <strong>%00000000</strong>, means we clear all the PICAXE-20M output port (Output 0 to Output 7).</p>
<pre>let pins = %00000000 ' Reset all Output Pins</pre>
<p><strong>Driving the Seven Segment Display with PICAXE </strong></p>
<p>Driving the seven segment display is one of the basic knowledge that should be known when we learn the embedded system.  Basically the seven segment display contain 7 LED plus 1 LED for the dot sign, therefore driving this seven segment display use the same principal as we drive an ordinary LED.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_09.jpg"><img class="alignnone size-full wp-image-1347" title="picaxe_09" src="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_09.jpg" alt="" width="574" height="425" /></a></p>
<p>The following program show the counted number from 0 to 9 on the common cathode seven segment display:</p>
<pre>'***************************************************************************
'  File Name    : sevenseg.bas
'  Version      : 1.0
'  Description  : PICAXE Seven Segment
'  Author       : RWB
'  Target       : AXEJazz 20PIN Learning Board
'  Interpreter  : PICAXE Firmware version 3.B
'  IDE          : PICAXE Programming Editor Version 5.2.6
'  Programmer   : PICAXE Programming Editor Version 5.2.6
'  Last Updated : 22 September 2009
'***************************************************************************</pre>
<pre>' Use PICAXE 20M
#picaxe 20M</pre>
<pre>' Assign Variables
symbol cnt = b0
symbol adc_val=b1
symbol seg_dot = 7</pre>
<pre>let pins = %00000000 ' Reset all Output Pins</pre>
<pre>main:
  for cnt=0 to 9
    readadc 7,adc_val  ' Read ADC Value on Input Port 7

    gosub disp_val
    pause adc_val
  next cnt 

  ' Clear Output
  let pins = %00000000 ' Reset all Output Pins
  pause 100

  for cnt=0 to 2
    high seg_dot
    pause adc_val
    low seg_dot
    pause adc_val
  next cnt 

  goto main      ' Back to main loop
  end</pre>
<pre>disp_val:
  select cnt
    case 0
      let pins = %00111111     ' 0 -&gt; A=1,B=1,C=1,D=1,E=1,F=1,G=0,DOT=0
    case 1
      let pins = %00000110     ' 1 -&gt; A=0,B=1,C=1,D=0,E=0,F=0,G=0,DOT=0
    case 2
      let pins = %01011011     ' 2 -&gt; A=1,B=1,C=0,D=1,E=1,F=0,G=1,DOT=0
    case 3
      let pins = %01001111     ' 3 -&gt; A=1,B=1,C=1,D=1,E=0,F=0,G=1,DOT=0
    case 4
      let pins = %01100110     ' 4 -&gt; A=0,B=1,C=1,D=0,E=0,F=1,G=1,DOT=0
    case 5
      let pins = %01101101     ' 5 -&gt; A=1,B=0,C=1,D=1,E=0,F=1,G=1,DOT=0
    case 6
      let pins = %01111101     ' 6 -&gt; A=1,B=0,C=1,D=1,E=1,F=1,G=1,DOT=0
    case 7
      let pins = %00000111     ' 7 -&gt; A=1,B=1,C=1,D=0,E=0,F=0,G=0,DOT=0
    case 8
      let pins = %01111111     ' 8 -&gt; A=1,B=1,C=1,D=1,E=1,F=1,G=1,DOT=0
    case 9
      let pins = %01101111     ' 9 -&gt; A=1,B=1,C=1,D=1,E=0,F=1,G=1,DOT=0
 endselect
 return      

' EOF: sevenseg.bas</pre>
<p><a title="Introduction to the Embedded System with PICAXE Microcontroller (1) by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4018743715/"><img src="http://farm4.static.flickr.com/3155/4018743715_3fe4003908.jpg" alt="Introduction to the Embedded System with PICAXE Microcontroller (1)" width="500" height="375" /></a></p>
<p>As you&#8217;ve seen from the code above, by controlling which of the LED segment to be displayed we could easily display the number on this seven segment display. For example if we want to display the value &#8220;<strong>6</strong>&#8220;, we could simply make the LED on segment <strong>A, C, D, E, F</strong> and <strong>G</strong> to illuminate. Here I used the PICAXE BASIC subroutine <strong>disp_val</strong> to do the seven segment display task, therefore by calling this subroutine with <strong>gosub</strong> command it will display the number on the seven segment display according to the <strong>cnt</strong> variable value. Again in this experiment the ADC (<strong>Input 7</strong>) is being used for controlling the display delay.</p>
<p><strong>Connecting PICAXE with Dallas DS18B20 Digital Thermometer</strong></p>
<p>On this last example, I will show you how ease to use Dallas DS18B20 digital thermometer with PICAXE microcontroller and at the same time we will use the serial (RS232) terminal to display the temperature result.</p>
<p><a href="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_11.jpg"><img class="alignnone size-full wp-image-1348" title="picaxe_11" src="http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_11.jpg" alt="" width="575" height="339" /></a></p>
<p>Dallas DS18B20 is a well known 1-Wire interface centigrade digital thermometer, this TO-92 package chip could measure temperature range from -55 centigrade degree to +125 centigrade degree and it only require 1 data line plus the ground to communicate with the microcontroller. Now take a look of how the PICAXE BASIC communicates with this sophisticated digital temperature sensor:</p>
<pre>'***************************************************************************
'  File Name    : ds18b20.bas
'  Version      : 1.0
'  Description  : Temperature Sensor with Dallas DS18B20
'  Author       : RWB
'  Target       : AXEJazz 20PIN Learning Board
'  Interpreter   : PICAXE Firmware version 3.B
'  IDE          : PICAXE Programming Editor Version 5.2.6
'  Programmer   : PICAXE Programming Editor Version 5.2.6
'  Last Updated : 22 September 2009
'***************************************************************************</pre>
<pre>' Use PICAXE 20M
#picaxe 20M</pre>
<pre>' Assign Variables
symbol temp_val = b0
symbol row_pos = b1
symbol col_pos = b2</pre>
<pre>let pins = %00000000   ' Reset all Output Pins</pre>
<pre>' Initial ANSI Terminal
' Baud: 4800,Data: 8, Parity: None, Stop Bit: 1
gosub ansi_me
gosub ansi_cl</pre>
<pre>let row_pos=1
let col_pos=1
gosub ansi_cm</pre>
<pre>sertxd ("AXEJazz 20PIN - DS18B20 Digital Thermometer")</pre>
<pre>main:
  readtemp 1,temp_val  ' Read the DS18B20 Data Value        

  let row_pos=3
  let col_pos=1
  gosub ansi_cm</pre>
<pre>  if temp_val &lt;= 127 then
    sertxd ("Temperature: ",#temp_val," ",248,"C")
  else
    let temp_val = temp_val - 128
    sertxd ("Temperature: -",#temp_val," ",248,"C")
  endif

  pause 100

  goto main            ' Back to main loop
  end  

ansi_me:
  ' ANSI turn off all attribute: me=\E[0m
  sertxd (27,"[0m")
  return  

ansi_cl:
  ' ANSI clear screen: cl=\E[H\E[J
  sertxd (27,"[H",27,"[J")
  return</pre>
<pre>ansi_cm:
  ' ANSI cursor movement: cl=\E[%row;%colH
  sertxd (27,"[",#row_pos,";",#col_pos,"H")
  return</pre>
<pre>' EOF: ds18b20.bas</pre>
<p><a title="Introduction to the Embedded System with PICAXE Microcontroller (2) by ermicroinfo, on Flickr" href="http://www.flickr.com/photos/33738505@N05/4019507442/"><img src="http://farm3.static.flickr.com/2474/4019507442_896be6b9c1.jpg" alt="Introduction to the Embedded System with PICAXE Microcontroller (2)" width="500" height="373" /></a></p>
<p>On this experiment we just use 8-bit precision (the eighth bit is used as a negative sign bit) temperature measurement using the PICAXE BASIC <strong>readtemp</strong> command which is especially designed function just for the Dallas DS18B20 chip; and when it&#8217;s done (approximately 750ms) then the 8-bit <strong>temp_val</strong> variable will be contained the exact room temperature measured by the DS18B20 chip. When the value reach 128, mean the room temperature is swing to negative (the eighth bit is logical &#8220;<strong>1</strong>&#8220;) then we subtract the value with 128 to get the exact negative value. Next we display this value to the serial terminal using the <strong>sertxd</strong> command.</p>
<p>You could use the Hyperterminal or puTTY program to display the room temperature and set the baud rate to 4800, 8-bit for the data width, none for the parity and 1-bit as the stop bit sign. Remember the PICAXE automatically use this setting when working with serial terminal, for 8 MHz PICAXE clock the baud rate will be automatically set to 9600.</p>
<p>Now you could enjoy seeing the whole PICAXE experiment we&#8217;ve done above on this following video:</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/kWBsp0c7do4&amp;hl=en&amp;fs=1&amp;rel=0" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/kWBsp0c7do4&amp;hl=en&amp;fs=1&amp;rel=0" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p><strong>The Final Though</strong></p>
<p>The PICAXE actually show us how the technology could make our life easier, it just like when you want to copy a file on your desktop computer, you don&#8217;t have to type &#8220;<strong>copy source_directory\file_name target_directory\file_name</strong>&#8220;; instead on the modern computer operating system you just simply click &#8220;<strong>Copy</strong>&#8221; and click &#8220;<strong>Paste</strong>&#8221; the file. I bet many peoples on these days never use this old &#8220;<strong>copy</strong>&#8221; command anymore (they even don&#8217;t recognize that this command in fact is still exist). The same principal is shown on the PICAXE BASIC that we just use a simple command such as <strong>readadc</strong> to read the ADC value, <strong>readtemp</strong> to read the DS18B20 digital thermometer, <strong>servo</strong> to drive the servo motor and <strong>pwmout</strong> to produce the background PWM pulse.</p>
<p>Although the C and Assembler are still the main language choice when programming the embedded system because of their execution speed; but for many systems that only required low response time from the microcontroller; the PICAXE offer rapid and robust development framework to your embedded system application. The other alternative is; you could use the PICAXE microcontroller as your <strong>co-microcontroller</strong> to support your main microcontroller to do many various tasks such as servo controller, keypad reading, LCD or seven segment displayer or as the PWM source for your DC motor.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ermicro.com/blog/?feed=rss2&amp;p=1334</wfw:commentRss>
		</item>
	</channel>
</rss>
