March 04, 2012
by RevMoses
|
Circuit
The C code
// FreqGen.c
// for NerdKits with ATmega168
#define F_CPU 14745600
#include <stdio.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"
// PIN DEFINITIONS:
//
// PB2 - freq signal (OC1B)
void pwm_set(uint16_t x)
{
OCR1B = x;
}
void pwm_Freqset(uint16_t y)
{
OCR1A = y;
}
//this is how to define a global variable..i think
#define PWM_START 33
#define PWM_Freq 66
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Start PWM Methods~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void pwm_init()
{
// setup Timer1 for Fast PWM mode, 16-bit
// COM1B1 -- for non-inverting output
// WGM13, WGM12, WGM11, WGM10 -- for Fast PWM with OCR1A as TOP value
// CS11 -- for CLK/8 prescaling
// I want to drive my DC motor at 28kHz
// F=1/T and T=1/F
// this means it will repeat every 35 microseconds (period)
// each count is 8/14745600 = 0.5425us (speed)
pwm_Freqset(PWM_Freq);
pwm_set(PWM_START);
TCCR1A = (1<<COM1B1) | (1<<WGM11) | (1<<WGM10);
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~END PWM Methods~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ADC Methods~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void adc0_init() //void method means your going to have the chip do something
{
// set analog to digital converter
// for external reference (5v), single ended input ADC0
//selects ADC0
ADMUX = 0;
// set analog to digital converter
// to be enabled, with a clock prescale of 1/128
// so that the ADC clock runs at 115.2kHz.
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
// fire a conversion just to get the ADC warmed up
ADCSRA |= (1<<ADSC);
}
uint16_t adc_read()
{
// read from ADC, waiting for conversion to finish
// (assumes someone else asked for a conversion.)
// wait for it to be cleared
while(ADCSRA & (1<<ADSC))
{
// do nothing... just hold your breath.
}
// bit is cleared, so we have a result.
// read from the ADCL/ADCH registers, and combine the result
// Note: ADCL must be read first (datasheet pp. 259)
uint16_t result = ADCL;
uint16_t temp = ADCH;
result = result + (temp<<8);
// set ADSC bit to get the *next* conversion started
ADCSRA |= (1<<ADSC);
return result;
}
double sampleToVolts(uint16_t sample) //changing a 16 bit to a double
{
// conversion ratio in degrees/STEP:
// (5000 mV / 1024 steps) * (1 degree / 1000mV)
// ^^^^^^^^^^^ ^^^^^^^^^^
// from ADC Pot Logic
return sample * (5000.0 / 1024.0 / 1000);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~END ADC Methods~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main() {
// init LCD
//lcd_init();
//FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
//lcd_write_string(PSTR(" NerdKits ServoSquirter "));
// init serial port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;
// set PB2 as output
DDRB |= (1<<PB2);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Start PWM part MAIN~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// init PWM
pwm_init();
uint16_t HighDur = PWM_START;
uint16_t freqNum = PWM_Freq;
double ActualFreq;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~END PWM part MAIN~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ADC part MAIN~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// holder variables for first ADC
uint16_t last_sample0 = 0; //defining last_sample as an unsigned integer that is 16 bits long with a value of 0 (similar to initialization)
double this_temp0; //definig this_temp as a double
double temp_avg0; //defining temp_avg as a doulbe
uint8_t i; //defining i as an unsigned integer that is 16 bits long
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~END ADC part MAIN~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
while(1)
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ State Machine Now Begins~~~~~~~~~~~~~~~~~~~~~
// start up the Analog to Digital Converter
adc0_init();
// take 100 samples and average them!
temp_avg0 = 0.0;
for(i=0; i<100; i++)
{
last_sample0 = adc_read();
this_temp0 = sampleToVolts(last_sample0);
// add this contribution to the average
temp_avg0 = temp_avg0 + this_temp0/100.0;
}
freqNum=temp_avg0*1000;
//for better adjustment use the following multipliers in the above
//10000 for 50Hz to 500Hz
//1000 for 500Hz to 3kHz
//100 for 3kHz to 30kHz
//10 for 30kHz to 368kHz
//5k pot was used
HighDur=freqNum/2; //2 can be changed to give a different duty cycle 2 gives about 50% duty
pwm_Freqset(freqNum);
pwm_set(HighDur);
//we can write Freq=(1/FreqNum)(14745600 / 8.0)
//to see where this came from, refer to pwm_init comments
ActualFreq=(14745600 / 8.0 /(double) freqNum);
//ActualFreq=((14745600/8)*(1/(double) freqNum));
// Print the Frequency, freqNum and Pot Voltage to the serial port.
printf_P(PSTR("Frequency %.2f value of freqNum is %d Pot Voltage %.4f \r\n"), ActualFreq, freqNum, temp_avg0);
}
return 0;
}
python code is same as potentiometer to Control Duty Cycle project
Results...it works :)
|
March 04, 2012
by RevMoses
|
Next step:
Write code to turn ADC1 into a frequency to number converter.
Wire PIN 16 on micro to ADC1
ADC0 will continue to be the control pin for frequency(pot will stay here). |