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

<channel>
	<title>imsolidstate &#187; ATtiny</title>
	<atom:link href="http://www.imsolidstate.com/archives/tag/attiny/feed" rel="self" type="application/rss+xml" />
	<link>http://www.imsolidstate.com</link>
	<description>Always improving things...</description>
	<lastBuildDate>Wed, 04 Jan 2012 22:32:56 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Digital RPM indicator</title>
		<link>http://www.imsolidstate.com/archives/104</link>
		<comments>http://www.imsolidstate.com/archives/104#comments</comments>
		<pubDate>Wed, 26 Aug 2009 17:40:42 +0000</pubDate>
		<dc:creator>imsolidstate</dc:creator>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[ATtiny]]></category>
		<category><![CDATA[AVR]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[LCD]]></category>
		<category><![CDATA[RPM]]></category>

		<guid isPermaLink="false">http://www.imsolidstate.com/?p=104</guid>
		<description><![CDATA[
I made a circuit for RPM measurement based on an Atmel ATtiny24 to drive a Hitachi 44780 parallel interface character LCD. It uses an external interrupt request and INT0 to calculate the elapsed time between high/low transitions on the INT0 pin. The elapsed time is then converted to RPM and sent to the LCD. The [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignnone size-large wp-image-110" title="LCD RPM indicator" src="http://www.imsolidstate.com/wp-content/uploads/2009/08/IMAG0008-1024x768.jpg" alt="IMAG0008" width="614" height="461" /></p>
<p>I made a circuit for RPM measurement based on an Atmel ATtiny24 to drive a Hitachi 44780 parallel interface character LCD. It uses an external interrupt request and INT0 to calculate the elapsed time between high/low transitions on the INT0 pin. The elapsed time is then converted to RPM and sent to the LCD. The code will work from RPM values up to 999, and a variable timeout is used for a low cutoff to display 0RPM. The RPM range could be extended to 9,999 or higher by modifying the decimal to BCD routine at the end of the program.</p>
<p><img class="alignnone size-large wp-image-111" title="ATtiny24 interface PCB" src="http://www.imsolidstate.com/wp-content/uploads/2009/08/Sony-016-1024x768.jpg" alt="Sony 016" width="614" height="461" /></p>
<p>Here is the source code, it compiles with AVR Studio and AVR-GCC. <span id="more-104"></span><br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<pre>#include &lt;avr/io.h&gt;
#include &lt;avr/interrupt.h&gt;
#include &lt;util/delay.h&gt;

#define F_CPU 	1000000;	// 1MHz

volatile unsigned long RPM=0;	// Global RPM variable

void initLCD(void);
void lcd_write_byte(unsigned int CONTROL, unsigned int DATA);
void updateLCD(unsigned long RPM_OLD);
void check_BF(void);

void main(void)
{
TCCR1B=(1&lt;&lt;CS10)|(1&lt;&lt;CS12);	// Set up prescaler for CLK/1024
GIMSK=(1&lt;&lt;INT0);		// Enable external interrupt INT0
MCUCR=(1&lt;&lt;ISC00)|(1&lt;&lt;ISC01);    // Setup INT0 for rising edge detection

unsigned long RPM_OLD=1;	// Internal loop RPM variable

DDRB=0x00;			// Set PB2 for input

initLCD();			// Initialize the LCD

sei();				// Enable interrupts

while(1)
{
if((RPM != RPM_OLD) &amp; (TCNT1 &lt; 4000))
{
RPM_OLD=RPM;		// If new updated RPM value is not the same
updateLCD(RPM_OLD);	// as the one inside the loop, update the LCD.
}

if(TCNT1 &gt;= 4000)	// If it's been longer than 4 seconds, RPM=0.
{
RPM=0;
if (RPM != RPM_OLD)
{
RPM_OLD=RPM;		// If new updated RPM value is not the same
updateLCD(RPM_OLD);	// as the one inside the loop, update the LCD.
}
}
}
}

ISR(EXT_INT0_vect)	// External interrupt on PB2
{
unsigned long ELAPSED;

if(TCNT1 &lt; 4000)	// If Timer1 value is valid:
{
ELAPSED=TCNT1;		// Get Timer1 value before it changes much
TCNT1=0;		// Reset Timer1
RPM=(58594/ELAPSED);	// Calculate RPM based on Timer1 elapsed time
}

if(TCNT1 &gt;= 4000)	// If Timer1 overflowed, value is no good.
{
TCNT1=0;		// Reset Timer1
}
}

void initLCD()
{
DDRA=0xFF;			// Set PortA for output
_delay_ms(250);			// Wait for HD44780
PORTA=0x04;			// Intialize to 4 Bit, one line
PORTA |= 0x10;
_delay_ms(1);
PORTA &amp;= ~0x10;
_delay_ms(10);			// Intialize to 4 Bit, one line
PORTA |= 0x10;
_delay_ms(1);
PORTA &amp;= ~0x10;
_delay_ms(10);			// Intialize to 4 Bit, one line
PORTA |= 0x10;
_delay_ms(1);
PORTA &amp;= ~0x10;
_delay_ms(10);
lcd_write_byte(0,0x0C);		// Turn on display
lcd_write_byte(0,0x06);		// Increment by one after write

}

void lcd_write_byte(unsigned int CONTROL, unsigned int DATA)
{
// check_BF();

if(CONTROL == 1) PORTA |= 0x20; else PORTA &amp;= ~0x20;
if((DATA &amp; 0x80) == 0x80) PORTA |= 0x01; else PORTA &amp;= ~0x01;
if((DATA &amp; 0x40) == 0x40) PORTA |= 0x02; else PORTA &amp;= ~0x02;
if((DATA &amp; 0x20) == 0x20) PORTA |= 0x04; else PORTA &amp;= ~0x04;
if((DATA &amp; 0x10) == 0x10) PORTA |= 0x08; else PORTA &amp;= ~0x08;
PORTA |= 0x10;
_delay_us(1);
PORTA &amp;= ~0x10;

if((DATA &amp; 0x08) == 0x08) PORTA |= 0x01; else PORTA &amp;= ~0x01;
if((DATA &amp; 0x04) == 0x04) PORTA |= 0x02; else PORTA &amp;= ~0x02;
if((DATA &amp; 0x02) == 0x02) PORTA |= 0x04; else PORTA &amp;= ~0x04;
if((DATA &amp; 0x01) == 0x01) PORTA |= 0x08; else PORTA &amp;= ~0x08;
PORTA |= 0x10;
_delay_us(1);
PORTA &amp;= ~0x10;
_delay_ms(4);
}

void updateLCD(unsigned long RPM_OLD)
{
unsigned long RPM_BCD=((((RPM_OLD/10)+((RPM_OLD/100)*6))*16)+(RPM_OLD%10));

unsigned int ONES = 0x00;
unsigned int TENS = 0x00;
unsigned int HUND = 0x00;

if((RPM_BCD &amp; 0x0800) == 0x0800) HUND |= 0x08; else HUND &amp;= ~0x08;
if((RPM_BCD &amp; 0x0400) == 0x0400) HUND |= 0x04; else HUND &amp;= ~0x04;
if((RPM_BCD &amp; 0x0200) == 0x0200) HUND |= 0x02; else HUND &amp;= ~0x02;
if((RPM_BCD &amp; 0x0100) == 0x0100) HUND |= 0x01; else HUND &amp;= ~0x01;

if((RPM_BCD &amp; 0x0080) == 0x0080) TENS |= 0x08; else TENS &amp;= ~0x08;
if((RPM_BCD &amp; 0x0040) == 0x0040) TENS |= 0x04; else TENS &amp;= ~0x04;
if((RPM_BCD &amp; 0x0020) == 0x0020) TENS |= 0x02; else TENS &amp;= ~0x02;
if((RPM_BCD &amp; 0x0010) == 0x0010) TENS |= 0x01; else TENS &amp;= ~0x01;

if((RPM_BCD &amp; 0x0008) == 0x0008) ONES |= 0x08; else ONES &amp;= ~0x08;
if((RPM_BCD &amp; 0x0004) == 0x0004) ONES |= 0x04; else ONES &amp;= ~0x04;
if((RPM_BCD &amp; 0x0002) == 0x0002) ONES |= 0x02; else ONES &amp;= ~0x02;
if((RPM_BCD &amp; 0x0001) == 0x0001) ONES |= 0x01; else ONES &amp;= ~0x01;

ONES |= 0x30;
TENS |= 0x30;
HUND |= 0x30;

lcd_write_byte(0x00, 0x01);
lcd_write_byte(0x01, 0x20);
lcd_write_byte(0x01, HUND);
lcd_write_byte(0x01, TENS);
lcd_write_byte(0x01, ONES);
lcd_write_byte(0x01, 0x20);
lcd_write_byte(0x01, 0x52);
lcd_write_byte(0x01, 0x50);
lcd_write_byte(0x01, 0x4D);

}

void check_BF(void)
{
DDRA = 0xF0;  // Set PortA 4-7 (R/W, RS, E) to output, 0-3 (DBx) for input
PORTA &amp;= ~0x20;			// Clear register select
PORTA |= 0x40;			// Set read
PORTA |= 0x10;			// Set enable
_delay_us(1);

while((PA0 &amp; 0x01) == 0x01)	// Read DB7 of LCD (busy flag)
{
PORTA &amp;= ~0x10;			// Clear enable
_delay_us(1);
PORTA |= 0x10;			// Set enable
_delay_us(1);
PORTA &amp;= ~0x10;			// Clear enable
_delay_us(1);
PORTA |= 0x10;			// Set enable
_delay_us(1);
}

PORTA &amp;= ~0x10;			// Clear enable
_delay_us(1);
PORTA |= 0x10;			// Set enable
_delay_us(1);
PORTA &amp;= ~0x10;			// Clear enable
PORTA &amp;= ~0x40;			// Clear read
DDRA = 0xFF;			// Set for output

}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.imsolidstate.com/archives/104/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

