[ Log In ]
Image of the Atmega324p

Atmega324P

$8.50
Qty:
Thumbnail: Crystal Oscillator 18.432 MHz for UART

18.432 MHz Crystal Oscillator 18pf 30ppm

$0.94
Qty:
Thumbnail: 22 pF Capacitor

22 pF Multilayer Ceramic Capacitor

$0.43
Qty:
Thumbnail: Quartz crystal oscillator - 16 MHz

16 MHz Crystal Oscillator 20 pF Through Hole

$0.75
Qty:
Microcontroller Kit with DVD

Microcontroller Beginner Kit with Beginning Microcontrollers DVD

$49.95
Qty:
USB AVR programmer

USB AVR Programmer

$9.95
Qty:
3 pin slide switch

SPDT Slide Switch 3 pin 30V

$1.49
Qty:
Microcontroller Beginner Kit

Microcontroller Beginner Kit

$39.95
Qty:
Microcontroller Intermediate Kit

Microcontroller Intermediate Kit

$79.95
Qty:
Microcontroller Intermediate Kit with DVD

Microcontroller Intermediate Kit with DVD

$89.95
Qty:
Skip Navigation Links

Erik Vincent's Project

Check out Erik's project. He demonstrates using an external timer for his ATMega 168 microcontroller to achieve a more accurate timing rather than using the internal timer. Instead of showing his output on an LCD, since he didn't have enough pins available for the LCD's 8-bit interface, he chose to show the results through serial communication on his computer.

I have a larger ATMEGA324P, but I wanted to try doing this with a smaller ATMEGA168-20PU microcontroller. The original goal was just a timer, which shows how to use a crystal and capacitors to make it be more accurate than the internal timer. However, then I had to figure out how to get the fuses to work, so I used a fuse calculator to get the ATMEGA168's fuses for my 10Mhz clock. (http://www.engbedded.com/fusecalc/). I then had to figure out how to change my USBTiny stuff in ATMEL Studio from your ATMEGA324P to the ATMEGA168, which I did, and then add those fuse comments. I added them in the comments of my C code.

Then I was going to output the clock on an LCD, but your demo only works for 8-bit bus, and I didn't have a freed up enough bus to do that. The ATMEGA only has two full 8-bit buses, port B and D, and I need some of port B for the crystal, and some of port D for interrupt for your LCD demo, so I couldn't do it. So I tried my hand at figuring out RS-232.

So, I had a spare MAX232N chip sitting around and some 0.1uF caps and put that all together.

I think this project demonstrates a few cool things:

  • RS232 communication to send and receive stuff
  • How to set fuses
  • How to use a clock on an interrupt
/*
* atmega168_test.c

* Created: 12/1/2014 10:02:10 AM
* Author: Erik Vincent
*/

// Use this command for avrdude to work with 10Mhz crystal fuse on ATMEGA168
// -c usbtiny -p m168 -U flash:w:$(ProjectDir)Release\$(ItemFileName).hex:i -U lfuse:w:0xff:m -U hfuse:w:0xd7:m

// for this code, we are using the external 10 MHz crystal, so we have F_CPU defined as 10 million
#ifndef F_CPU
#define F_CPU 10000000UL
#endif

#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include <avr/interrupt.h>

// define baud-rate of my serial port
#define BAUD 9600
#define MYUBRR F_CPU/16/BAUD-1

// set volatile memory to hold values that interrupt will be collecting
volatile int8_t the_time; // holds the actual "ticks" of the crystal, which will be 40 ticks a second
volatile int16_t the_day; // holds how many days have passed
volatile int8_t the_hour; // holds how many hours have passed
volatile int8_t the_minute; // holds how many minutes have passed
volatile int8_t the_second; // holds how many seconds have passed

// function to initialize my USART port on the ATMEGA168
void USART_Init(unsigned int ubrr)
{
// Set baud rate
UBRR0H = (unsigned char)(ubrr>>8);
UBRR0L = (unsigned char)ubrr;

// Enable receiver and transmitter
UCSR0B = (1<<RXEN0)|(1<<TXEN0);

// Set frame format: 8data, 1 stop bit
UCSR0C = (1<<UCSZ00) | (1 << UCSZ01);
}

// function to transmit/TX stuff out of the ATMEGA168 and into my laptop
void USART_Transmit(unsigned char data )
{
// Wait for empty transmit buffer
while ( !( UCSR0A & (1<<UDRE0)) );

// Put data into buffer, sends the data
UDR0 = data;
}

// function to transmit/write a whole null terminated string to serial port
void serial_write_str(const char* str)
{
int len = strlen(str); // get length of string
int i; // used in for next loop
for (i = 0; i < len; i++)
{
// transmit/write each character in string (character array), one at a time
USART_Transmit(str[i]);
}
}

// function to read a character, one at a time from the serial port
char USARTReadChar()
{
// Wait until a data is available
while(!(UCSR0A & (1<<RXC0)))
{
// Do nothing
}

// Now USART has got data from host and is available is buffer
return UDR0;
}

// function used to display timer info
void show_the_time(void)
{
char buff[16]; // needed a buffer to handle my output correctly
memset(&buff,16,0); // clear my buffer

serial_write_str("\r\n"); // return carriage
sprintf(buff, "Day: %03d", the_day); // display days as a 3 digit integer with trailing 0's
serial_write_str(buff); // write to serial port

memset(&buff,16,0); // clear my buffer
sprintf(buff, " %02d:", the_hour); // display hours as a 2 digit integer with trailing 0's
serial_write_str(buff); // write to serial port

memset(&buff,16,0); // clear my buffer
sprintf(buff, "%02d:", the_minute); // display minutes as a 2 digit integer with trailing 0's
serial_write_str(buff); // write to serial port

memset(&buff,16,0); // clear my buffer
sprintf(buff, "%02d", the_second); // display seconds as a 2 digit integer with trailing 0's
serial_write_str(buff); // write to serial port

serial_write_str("\r\n"); // return carriage
}

// function used to initialize crystal timer for interrupts
void init_crystal_timer(void)
{
TIMSK0 = _BV(OCIE0A); // Enable Interrupt TimerCounter0 Compare Match A (SIG_OUTPUT_COMPARE0A)
TCCR0A = _BV(WGM01); // Set 8 bit timer register counter 0, register a to Clear Timer on Compare (CTC) mode
TCCR0B = _BV(CS02) | _BV(CS00); // Set clock pre-scaler to clock/1024, so 1024 (10-bit) / clock (10,000,000 Hz), 0.0001024 seconds per tick
OCR0A = 244; // 0.0001024 * 244 ~= 0.025 SIG_OUTPUT_COMPARE0A will be triggered every 0.025 seconds, or 40 times per second.

sei(); // Sets up global interrupt flags
}

// function used to clear all the characters out of my buffer
void clear_buffer(char* str)
{
int len = strlen(str); // get length of string/buffer
int i; // used in for next loop
for (i = 0; i < len; i++)
{
// write 0's aka NULL characters to the whole buffer
str[i] = 0;
}
}

// main function
int main (void)
{
int8_t buffer_index = 0; // variable used to hold where I am in the character array/string buffer
char data; // variable used to hold the data character I am getting from the serial port
char charbuff[8]; // variable used to hold the data character in a character array, as to write out to serial port
char strbuff[32]; // variable used to capture 32 characters at a time coming in from my laptop's COM port

// Put an LED on PD4 (pin 6)
DDRD |= (1<<4); // PD4 as output
PORTD |= (1<<4); // Set high

USART_Init(MYUBRR); // initialize USART
init_crystal_timer(); // initialize crystal timer for interrupts

memset(&strbuff,32,0); // initialize string buffer

// main loop
while (1)
{
memset(&charbuff,8,0); // clear character buffer
data = USARTReadChar(); // Read character data from USART port
charbuff[0] = data; // put the character into the character buffer
serial_write_str(charbuff); // echo the character back to the laptop COM port

// If the character I received was a return/Enter character...
if(charbuff[0] == '\r')
{
// Check to see if my 32 character buffer starts with -s, and if so...
if((strbuff[0] == '-') && (strbuff[1] == 's'))
{
// show the timer
show_the_time();
}
else
{
// otherwise, write out what was typed into the buffer, out to the laptop COM port
serial_write_str("\r\n");
serial_write_str("You typed ");
serial_write_str(strbuff);
serial_write_str("\r\n");
}

// reset my buffer index
buffer_index = 0;

// clear my buffer of characters
clear_buffer(strbuff);
}
else
{
// else, if my character received was not a return/Enter character, put the character into string buffer
strbuff[buffer_index] = charbuff[0];

// and increment my buffer index by one for the next character to be put in
buffer_index++;
}

// if my buffer index has grown larger than what I can take in, which is 32...
if(buffer_index >= 32)
{
// let user know that's a bad thing they tried to do
serial_write_str("\r\n");
serial_write_str("Invalid Entry. Please try again.");
serial_write_str("\r\n");

// reset my buffer index
buffer_index = 0;

// clear my buffer of characters
clear_buffer(strbuff);
}
}

return 1;
}

// Interrupt for my crystal clock timer
ISR(TIMER0_COMPA_vect)
{
the_time++; // tick the time, which is 40 times a second, per crystal setup

// if the time has hit 40...
if(the_time >= 40)
{
// reset tick counter
the_time = 0;

// increment the second counter by 1
the_second++;

// blink my LED
PORTD ^= (1<<4);
}

// if the second counter has hit 60...
if(the_second >= 60)
{
// reset the second counter
the_second = 0;

// increment my minute counter by 1
the_minute++;
}

// if the minute counter has hit 60...
if(the_minute >= 60)
{
// reset the minute counter
the_minute = 0;

// increment my hour counter by 1
the_hour++;
}

// if the hour counter has hit 24...
if(the_hour >= 24)
{
// reset the hour counter
the_hour = 0;

// increment my day counter by 1
the_day++;
}
}