NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Use Different External Crystal Oscillator

April 11, 2012
by E2MicroRobots
E2MicroRobots's Avatar

Hi! I am working on the code for delivering a set amount of power for a specific time to a solenoid. I would like to use a 32.768 kHz crystal oscillator because it is slower and therefore will make it easier to run the program for longer periods of time (at least that's what I think will happen). Do I just change the #define F_CPU to be 32768? Or do I need to change anything else? Also, I would like to deliver a set amount of power, but not equal to 5V, is PWM the best method for doing this? Thanks! Sarah

April 11, 2012
by pcbolt
pcbolt's Avatar

Hi Sarah -

You can use the slower crystal by the method you descibed but keep in mind when you wish to program the crystal using the Nerdkit USB cable you will not be able to use the preset baud rate of 115200 bits/sec (this is faster than your clock). 19,200 bit/sec is the highest you can go and you will have to change your bootloader code then re-install it. For that you will need a different method of programming the chip.

You can set time "pre-scalers" programmically which will effectively do what you wish to do but with the 14.7 MHz NK crystal. The pre-scaler simply divides the frequency by even powers of 2 ( 2,4,8,16...1024). This way you can still use the programming cable and current bootloader.

As far as delivering power, I would agree to use PWM. If you are using a solenoid, be careful how you interface with the MCU since it has an EMF "kickback".

April 11, 2012
by E2MicroRobots
E2MicroRobots's Avatar

Thanks!! I am going to build some circuits for powering the solenoid and a motor with the micro controllers I ordered from here. Is there a simple way to get more of the 14.7MHz crystals? And thanks for the warning about the solenoid. We were planning on placing a resistor in between in micro controller and the solenoid, will that be sufficient? Also one more. I need the power to be supplied at a specific voltage for up to 5 minutes. Any advice? Thanks again! Sarah

April 11, 2012
by pcbolt
pcbolt's Avatar

You'll need more than a resistor to connect the MCU to the solenoid. Depending on the size of the solenoid, you will need at least a transistor and a "flyback" diode (which is a diode wired in parallel with the solenoid using correct polarity).

Humberto and Mike (NK creators) made a great video about this Here. Even though it's about motors, it still applies to solenoids.

You may have some circuitry built into the solenoid...post the model number here since others may have used it before.

You can get crystals from Mouser, Digikey and a number of other sources. I'm sure Mike and Humberto will give you a deal on them too! Just e-mail them and find out.

April 11, 2012
by E2MicroRobots
E2MicroRobots's Avatar

The data sheet for the ATmega168 says that an external clock source cannot have prescaling... So does this mean I need to find a way to get the lower frequency crystal to work?

April 11, 2012
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Sarah,

I think we need to take a step back here and figure out what you really need to do. It is unlikely that it is truly necessary to run your chip that much slower to get something to work. What are you trying to slow the system down that much? You can usually achieve a PWM duty cycle that does what you want using the oscillator provided. Can you provide an overview of what you are doing, maybe some diagrams to help us get a better idea?

Humberto

April 11, 2012
by E2MicroRobots
E2MicroRobots's Avatar

Hi Guys, I have three different things I actually need to accomplish, each with their own separate microcontroller.
1 - run a motor at a specified voltage for a specified time (20 seconds) 2 - run a solenoid at 5V for 5 minutes 3 - provide power and receive data from a Passive Infrared Sensor for 5 minutes

I am using the basic things I learned from the kit and will have the necessary voltage regulators and capacitors on the circuit boards I am building. The reason I am attempting to slow down the process is because of the long time (compared to how fast the microcontroller works) I need the last two parts to function. If you can tell me that I don't have to do it this way I will be very excited! Is there a simpler way? And if there is do I need the crystal oscillator on any of the final circuit boards? I hope that makes some more sense (I don't really have any diagrams to show you). Thanks again!! Sarah

April 11, 2012
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Sarah,

I would take a look at the crystal real time clock tutorial. In it, we use the timer/counter modules on the chip to fire an interrupt at a specified time interval, we then use a normal integer on our chip to keep track of how many times that has fired. Does that idea make sense?

I believe you should be able to follow a similar approach to keep track of how long your devices have been "on" in your code, and just control the motor/solenoid from a regular I/O pin (probably using a MOSFET to actually drive the load).

April 11, 2012
by E2MicroRobots
E2MicroRobots's Avatar

Thanks for the tip! So I went through that information and I kind of combined it withe PWM to give the power output necessary. Below is the code I've come up with (it's a compilation of the things I've found) I would really appreciate it if you could let me know if it looks completely crazy :)

I have also seen other forums talking about running the program in simulation mode. Is that possible with WinAVR application?

Thanks again! Sarah

define F_CPU 14745600

include <stdio.h>

include <math.h>

include <avr/io.h>

include <avr/interrupt.h>

include <avr/pgmspace.h>

include <inttypes.h>

include "../libnerdkits/delay.h"

include "../libnerdkits/lcd.h"

include "../libnerdkits/uart.h"

int main(void) { unsigned char elapsedseconds = 0;

//set up pwm info
//timer1 for PWM
DDRD |= (1<<0); //set as output
TCCR1A = (1<<COM1A1) | (1<<COM1B1) | (1<<WGM10); 
//set clear on match
TCCR1B = (1<<CS12) | (1<<CS10);

//set up timer for counter of actualy time
//timer0 for time count
TCCR0B = (1<<CO02) | (1<<CS00);

//for every 100 = 1 sec
//for every 100 add to total count of time 
//start again
//need elapsed seconds = 300
While (elapsedseconds <= 300)
{
    OCR1A = 255;
    PORTD = 1;`
    if (TCNT0 >= 14399)
    {
        TCNT0 = 0;
        elapsedseconds++;
    }

}
OCR1A = 0;

while (1);
return 0;

}

April 11, 2012
by pcbolt
pcbolt's Avatar

Sarah -

The pre-scaler just works on all the timer related functions of the MCU itself (including SPI, TWI, UART, ADC and all the timer functions) it always operates instructions as fast as the crystal frequency (up to a point).

In your code, keep in mind that Timer 1 is 16-bits and Timer 0 is 8-bits. When you test TCNT0 against 14399 it will never get that high since the max an 8-bit value gets is 255 before it resets to 0. Therefore "elapsedseconds" will never increment and you'll be stuck in loop.

I think you might be over complicating the problem at hand. Download Humberto's code from the link he posted. It is easy to understand, well documented and versatile. All it does is increment a variable called "the_time" every 1/100th of a second and displays it. You can use the same variable to control all 3 of your outputs with one micro controller.

For instance, you want to control 3 outputs, "motor", "solenoid", and "sensor". Here is some "pseudocode" to use for your project;

  • define "motor_time", "sol_time", "sensor_time"
  • set motor_time = the_time then start motor
  • set sol_time = the_time then start solenoid
  • set sensor time = the_time then start sensor
  • do a test "if ((the_time - motor_time) > 2000)" then stop motor
  • "if ((the_time - sol_time) > 30000)" then stop solenoid
  • "if ((the_time - sensor_time) > 30000)" then stop sensor
  • check on sensor input
  • loop around to "do a test"

This isn't really complete since it doesn't deal with turning the outputs back on but it is a start. Hope it helps.

April 12, 2012
by E2MicroRobots
E2MicroRobots's Avatar

You are completely right! I was seriously over-complicating the entire process. I took the timer code basically straight from the real time clock example. I would love to do all the processes on one controller, but they are spaced pretty far apart in the system, so they will have to be separate. I do have (hopefully) one last quick question. Below is part of my final code. Am I assigning the values to output through the PWM correctly? It seems awfully simple to just set OCR1A to the value I want...

I would also like to say thank you soooo much!!! To everyone who has helped so much!!! You all are simply amazing! Sarah

void pwm_init() {

//Use PWM Phase Correct to regulate voltage sent to motor
//values from 0-255, choose number according to what percentage of 
//5V we want to go into the motor   
//set up pwm info
//timer1 for PWM
////use PD5 for PWM output 
DDRD |= (1<<0); //set as output
TCCR1A = (1<<COM1A1) | (1<<COM1B1) | (1<<WGM10); 
//set clear on match
TCCR1B = (1<<CS12) | (1<<CS10);

}

int main() { //set PD5 to output that starts at 0 DDRD |=(1<<0); PORTD = 0x00

realtimeclock_setup();
pwm_init();
OCR1A = 255;

//need elapsed seconds = 300
if (the_time >= 30000)
{
    OCR1A = 0;
}
while (1);
return 0;

}

April 12, 2012
by pcbolt
pcbolt's Avatar

A few things to change. To set PD5 as output use:

DDRD |= (1<<PD5);

You can use PORTD = 0x00 to set PD5 to low, but will set PD0,PD1,PD2 etc to low as well. To set just PD5 to low use:

PORTD &= ~(1<<PD5);

The way you have the code set up now, the test "if (the_time >= 30000)" will only execute once. If you want it to keep checking over and over put it inside the "while(1)" loop...

while(1){
  if(the_time >= 30000){
    OCR1A = 0;
  }
}
return 0;

Post a Reply

Please log in to post a reply.

Did you know that you can turn a $20 digital scale into a live weight sensor using our kit with a few extra parts? Learn more...