7:55:47 pm on 5/17/12

Menu
» Home
» About Scott
» VD Labs
» QRSS VD
» Old Stuff
» Archive
» Publications
» Contact

Categories
» C/C++
» Circuitry
» DIY ECG
» General
» high altitude balloon
» Linux
» Microcontrollers
» Molecular Biology
» My Website
» PHP
» Prime Numbers
» Python
» Radio
» UCF Lab
» Everything
» RF Links

Writings
» MD Labels
» Streamrip
» AIM Thoughts
» WindowsXP?
» Partitioning
» CD/DVD Repair
» Monitor Info
» CRT Deflection
» Venomcrack
» Flash Thing
» Heart/Brain
» Diabetes
» Triops
» Biomed

Friends
» Mike
» Fred
» Kyle W
» Nick
» Louis
» Tom
» Kyle H




Archives
» August 2011
» July 2011
» June 2011
» March 2011
» February 2011
» January 2011
» December 2010
» November 2010
» September 2010
» August 2010
» July 2010
» June 2010
» May 2010
» April 2010
» March 2010
» February 2010
» January 2010
» December 2009
» September 2009
» August 2009
» July 2009
» June 2009
» May 2009
» April 2009
» March 2009
» February 2009
» January 2009
» December 2008
» November 2008
» October 2008
» September 2008
» September 2007
» December 2006
» August 2006
» January 2006
» August 2005
» July 2005
» June 2005
» May 2005
» April 2005
» March 2005
» February 2005
» January 2005
» December 2004
» November 2004
» October 2004
» September 2004
» August 2004
» July 2004
» June 2004
» May 2004
» April 2004
» March 2004
» February 2004
» January 2004
» December 2003
» November 2003
» October 2003
» September 2003
» August 2003
» July 2003
» June 2003
» May 2003
» April 2003
» March 2003
» February 2003
» January 2003
» December 2002
» November 2002
» October 2002
» September 2002
» June 2001

You are currently browsing the archives for the Circuitry category.

Archive for the 'Circuitry' Category

« Previous Entries
Next Entries »


Sound Card Microcontroller/PC Communication
Posted by
Scott July 9th, 2011 | 5,253 words | 13 Comments »


Scott was 25.79 years old when he wrote this!

This page describes a method of sending data from a microchip to a PC using pulses of data. It’s an alternative to more traditional serial or USB methods of connectivity. It’s not intended as a solution for consumer products, but rather an easy hack for hobbyists to employ if they don’t have the equipment for other methods. This method doesn’t require *any* circuitry, just a sound card. The one built in your computer is fine, but I’m using a $1.30 USB sound card for simplicity. It boils down to just a single microcontroller pin connected to a PC sound card microphone jack!

UPDATE: This story was featured on this post of HackADay.com!

This is the finished product ready to send data to a PC:DSCN1532

MY PROBLEM: I want to send data from a simple microcontroller to a PC. While USART and a serial port is the common solution like I’ve done before, it’s not convenient because it requires a level converter (like a MAX232, about $4), crystal (specific values based on bit and error rate, if you’re lucky you might have a right value in your junk box), and an archaic PC which actually has a serial port. A usb serial port adapter sounds clever, but many aren’t supported on Linux, Windows Vista, or Windows 7. Also, many small chips (most of the ATTiny series) don’t have built in serial capabilities, so it has to be bit-banged in software! Yuk! The second choice would be USB. This requires a crystal too, zener diodes, and bit-banging the USB protocol with something like V-USB since most of the AVR series don’t have built in USB (do they even make breadbordable DIP chips with USB?). Even so, it requires drivers, custom software, cross-platform frustrations, etc. I know PIC has some 18f series chips with USB, but I don’t feel like switching architectures just to send a few bytes of data to a PC. FDTI has a FT232R chip which is a USB serial port adapter, but it’s expensive (about $5) and doesn’t come in dip, so no breadboarding! Sure there are adapter boards, but that just adds the cost. I’m not excited about a $5 solution for a $1 microcontroller. I even did a bit of trolling on AVR Freaks to see if anyone could help me out – just more of the same!

MY SOLUTION: Send data through the sound card! USB sound cards are $1.30 (shipped) on eBay! It couldn’t be simpler. Send pulses, measure distance between pulses. Short pulses are a zero, longer ones are a 1, and very long pulses are number separators. A Python solution with PyAudio allows 1 script which will work on Mac, Linux, Windows, etc, and because it calibrates itself, this will work on any chip at any clock rate. Data is initiated with calibration pulses so timing is not critical – the PC figures out how fast the data is coming in. Check it out! (scroll way down for a bidirectional communication solution)

Here is a sound card I used for bidirectional communication:
DSCN1466 DSCN1470

Output graph (python and excel) of temperature when I put a soldering iron near the sensor: pythonexcel

~ UNIDIRECTIONAL SOLUTION ~

The following code is designed to have a chip send data to your PC automatically. This can be run on any micro-controller (PIC or AVR I guess, the concept is the same) at any clock rate. Just make sure the sound card is recording fast enough to differentiate pulses. (keep scrolling down for a bidirectional solution)

A NOTE ABOUT MY CODE: This is just the code I used for my demonstration. It might be more logical for you to write your own since the concept is so simple. I’m a dental student, not a programmer, so I’m sure it’s not coded very elegantly. I didn’t work hard to make this code easy to read or easy to share. With that being said, help yourself!

/*The following code is written in AVR-GCC for an ATTiny44a.
It reads ADC values on 3 pins and reports it each second along
 with a number which increments each time data is sent.
It's designed as a starting point, allowing anyone to
customize it from here!*/

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

// bytes we want to send to the PC
volatile int data1=0;
volatile int data2=0;
volatile int data3=0;
volatile int data4=0;

void solid(){  // dont touch
	_delay_ms(1);
	pulse(1);pulse(1);pulse(1);pulse(3);pulse(3);
	pulse(3);pulse(5);pulse(5);// CALIBRATION PULSES
}
void pulse(char size){ // dont touch
	PORTA|=_BV(PA3);
	_delay_us(100);
	PORTA&=~_BV(PA3);
	while (size){size--;_delay_us(100);}
}
void sendVal(unsigned long tosend){ // dont touch
	pulse(5); // send a space
	while (tosend){
		if (tosend&1){pulse(3);} // send ONE
		else {pulse(1);} // send ZERO
		tosend=tosend>>1;
	}
}

int readADC(char adcNum){
	_delay_ms(1);
	ADMUX=adcNum; // select which ADC to read, VCC as ref.
	ADCSRA=0b11000111; // enable, start, 128 prescale
    while (ADCSRA&( 1<<ADSC)) {}; // wait for measurement
	return ADC;
}

void takeReadings(){
        data1=readADC(0); // ADC0
        data2=readADC(1); // ADC1
        data3=readADC(2); // ADC2
		data4++; // incriment just because we want to
}

void sendStuff(){ // EDIT to send what you want
	solid(); //required
	sendVal(12345); //required
	sendVal(12345); //required
	sendVal(54321); //required

	sendVal(data1);
	sendVal(data2);
	sendVal(data3);
	sendVal(data4);

	pulse(1); //required
}

int main(){
	DDRA|=_BV(PA2)|_BV(PA3);
	for (;;){
		_delay_ms(1000);
		takeReadings();
		sendStuff();
	}
	return 0;
}
"""
file name: listenOnly.py

This is the PC code to listen to the microphone and display
and log the data. It probably does NOT need adjustment!
 Make sure the correct sound card is selected (in the code)
 and make sure microphone input is turned up in volume control.

This code is what was used on my PC for the demonstration
video. This is the listenOnly.py file which will turn any audio
 detected from a sound card into data, optionally logging it
(if the last few lines are uncommented). This also works to
capture data for the bidirectional communication method,
described below on this website.

If this is running but no data is coming through, make sure the
microphone is selected as a recording device, the correct sound
card is selected, and the microphone volume is turned to high.

REQUIRED: To run this, you need to have the following installed:
-- Python 2.6
-- numpy for python 2.6
-- matplotlib for python 2.6
-- pyaudio for python 2.6
(other versions may work, but this is what I'm using)
"""
import numpy
import pyaudio
import matplotlib.pyplot as plt
import wave
import time

def listCards(dontAsk=True):
    p=pyaudio.PyAudio()
    print "SOUND CARDS:"
    for i in range(p.get_default_host_api_info()["deviceCount"]):
        if p.get_device_info_by_index(i)["maxInputChannels"]>0:
                cardName = p.get_device_info_by_index(i)["name"]
                cardIndex = p.get_device_info_by_index(i)["index"]
                print "[%d] %s"%(cardIndex,cardName)
    if dontAsk: return
    return int(raw_input("CARD NUMBER TO USE:"))

cardID=1
listCards()
print "USING CARD:",cardID

rate=44100.0
sampleSize=1024

def data2vals(data):
    vals=numpy.array([])
    lastPeak=0
    for i in range(1,len(data)):
        if data[i]==True and data[i-1]==False:
            if lastPeak>0: vals=numpy.append(vals,i-lastPeak)
            lastPeak=i
    return vals

def binary2dec(binary):
    binary=binary[:-1]
    dec=0
    s=""
    for i in range(len(binary)):
        dec=dec*2
        dec+=binary[i]
        s="%d"%binary[i]+s
    #print s,"=",dec #11111100101100000 = 3391
    return dec

def readVals(vals):
    if len(vals)<7: return False
    vals2=[]
    aLow = min(vals[0:3])
    aMed = min(vals[3:6])
    aHigh = vals[6]
    thresh1=sum([aLow,aMed])/2+2
    thresh2=sum([aMed,aHigh])/2+2
    #print "tresholds:",thresh1,thresh2
    #print vals
    vals=vals[8:]
    binary=[]
    for i in range(len(vals)):
        if vals[i]>thresh2:
            vals2.append(binary2dec(binary))
            binary=[]
        if vals[i]>thresh1:binary=[1]+binary
        else:binary=[0]+binary
    vals2.append(binary2dec(binary))
    for i in range(len(vals2)):
        if vals2[i]==54321: return vals2[i+1:]
    return False

def playFile():
    chunk = 1024
    wf = wave.open("short_onenum.wav", 'rb')
    p = pyaudio.PyAudio()
    stream = p.open(format =
                    p.get_format_from_width(wf.getsampwidth()),
                    channels = wf.getnchannels(),
                    rate = wf.getframerate(),
                    output = True)
    data = wf.readframes(chunk)
    while data != '':
        stream.write(data)
        data = wf.readframes(chunk)
    stream.close()

def captureData():
    pyaud = pyaudio.PyAudio()
    stream = pyaud.open(format=pyaudio.paInt16,channels=1,
        rate = 44100,input_device_index=cardID,input=True,output=True)
    sample=numpy.array([])
    while True:
        sampleNew=numpy.fromstring(stream.read(sampleSize),dtype=numpy.int16)
        sampleNew=(sampleNew<-25000)*1
        if True in sampleNew: sample=numpy.append(sample,sampleNew)
        else:
            if len(sample):
                stream.close()
                return sample
    stream.close()

tone_quiet=0

def buildNumber(num=123):

    if num>255: print "NUMBER TOO HIGH!!!"
    #print num,'=',
    num+=1
    for i in [7,6,5,4,3,2,1,0]:
        if num>2**i:one();num=num-2**i;#print"1",
        else: zero();#print"0",
    #print
    space()

def pulse():
    global data
    data+=[-30000]*10

def space():
    global data
    data+=[tone_quiet]*900
    pulse()

def one():
    global data
    data+=[tone_quiet]*600
    pulse()

def zero():
    global data
    data+=[tone_quiet]*300
    pulse()

def silence(msec=1000):
    global data
    data+=[tone_quiet]*int(41.1*msec)

data=[]
def sendAudio(numbers=[11,66,77]):
    global data
    data=[]
    silence(100)
    buildNumber(250)
    print "SENDING",
    for numba in numbers:
        buildNumber(numba)
        print numba,
    buildNumber(250)
    silence(100)
    data=numpy.array(data)
    data=-data
    data=data.tostring()
    print

    p = pyaudio.PyAudio()
    stream = p.open(rate=44100, channels=1, format=pyaudio.paInt16,
                    input_device_index=cardID, output=True)
    stream.write(data)
    stream.close()
    p.terminate()

i=0
while True:
    i+=1
    val=readVals(data2vals(captureData()))
    if val == False: continue
    line=""
    for item in val: line+=str(item)+","
    print i,line
    #f=open('log.csv','a')
    #f.write("%s\n"%line)
    #f.close()

~ BIDIRECTIONAL SOLUTION ~

What if we want to send data TO the microcontroller? The solution is a little more complex, but quite doable. Just add an extra wire to the sound card’s speaker output and attach it to PCINT0 (the highest level internal interrupt). This is intended for advanced users, and if you’re doing this you probably are better off with USB or serial anyway! … but heck, why not do it as a proof of concept!

Note that the USB sound card speaker output was not powerful enough to trigger the digital input pin of the AVR, so an inverting buffer was made from a single NPN transistor (2n3904). The hardware interrupt was attacked to the collector, and the collector was attached through +5V through a 220 ohm resistor. The emitter was grounded. The base was attached directly to the sound card output. I also tried running the sound card output through a small series capacitor (0.1uF) and biasing the base to ground through a 1Mohm resistor and it worked the same. Hardware, simple. Chip-side software… a little more complex.

### VIDEO ###

"""
This code is what was used on my PC for the
 demonstration video. The listenonly.py file
 (above on site) was also used without modification.
"""
import pyaudio
from struct import pack
from math import sin, pi
import wave
import random
import numpy
import time

RATE=44100
maxVol=2**15-1.0 #maximum amplitude
p = pyaudio.PyAudio()
stream = p.open(rate=44100, channels=1, format=pyaudio.paInt16,
		input_device_index=1, output=True)

def pulseZero():
    global wvData
    wvData+=pack('h', 0)*30
    wvData+=pack('h', maxVol)

def pulseOne():
    global wvData
    wvData+=pack('h', 0)*40
    wvData+=pack('h', maxVol)

def pulseSpace():
    global wvData
    wvData+=pack('h', 0)*50
    wvData+=pack('h', maxVol)

def buildNumber(num=123):
    if num>255: print "NUMBER TOO HIGH!!!"
    num+=1
    for i in [7,6,5,4,3,2,1,0]:
        if num>2**i:
            pulseOne()
            num=num-2**i
        else:
            pulseZero()

wvData=""
wvData+=pack('h', 0)*2000
pulseOne() #required before sending data

buildNumber(55)
buildNumber(66)
buildNumber(77)
buildNumber(123)

wvData+=pack('h', 0)*2000

while True:
	print "SENDING",
	stream.write(wvData)
	raw_input()
/*
This code is what was used on my AVR
microcontroller for the demonstration video
*/
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>

volatile long commandIncoming=0;
volatile char command1=0;
volatile char command2=0;
volatile char command3=0;
volatile char command4=0;
volatile char bitsGotten=0;

// timing thresholds are critical! Send pulses to the chip
// and have it report the time between them. Use this to
// determine the best threshold value for your application.
// The ones here must be changed if you run at a speed other
// than 1mhz or if you use different timings in PC software
#define thresh_low 100 // between this and the next
#define thresh_high 130 // is the range for a logical 'one'

// ######## OUTGOING AUDIO DATA #########
void solid(){
	_delay_ms(1); //LONG LOW
	pulse(1);pulse(1);pulse(1);pulse(3);pulse(3);
	pulse(3);pulse(5);pulse(5);// CALIBRATION PULSES
}
void pulse(char size){
	PORTA|=_BV(PA3);
	_delay_us(100);
	PORTA&=~_BV(PA3);
	while (size){size--;_delay_us(100);}
}
void sendVal(unsigned long tosend){
	pulse(5); // send a space
	while (tosend){
		if (tosend&1){pulse(3);} // send ONE
		else {pulse(1);} // send ZERO
		tosend=tosend>>1;
	}
}

// ######## INCOMING AUDIO DATA #########
// NOTE THAT INPUTS ARE NORMALLY *HIGH* AND DROP *LOW* FOR SIGNAL
SIGNAL (PCINT0_vect) { // audio input trigger
	TIMSK0|=(1<<TOIE1); //Overflow Interrupt Enable
	if (TCNT0<10){return;} // seem too fast? ignore it!
	// Enable the following line to test custom timings
	//command1=command2;command2=command3;
	//command3=command4;command4=TCNT0;
	bitsGotten++;
	commandIncoming=commandIncoming*2; // shift left
	if (TCNT0>thresh_low){commandIncoming++;} // make 1
	TCNT0=0;
}

ISR(TIM0_OVF_vect){ // TIMER OVERFLOW
	if (bitsGotten){sendStuff();}
}

void fillCommands(){
	command1=(char*)(commandIncoming>>24);
	command2=(char*)(commandIncoming>>16);
	command3=(char*)(commandIncoming>>8);
	command4=(char*)(commandIncoming);
}

void sendStuff(){
	TIMSK0=0; //Overflow Interrupt
	cli(); // disable interrupts!
	fillCommands();
	solid(); // start data transmissions with this
	sendVal(12345);
	sendVal(12345);
	sendVal(54321);
	sendVal(command1);
	sendVal(command2);
	sendVal(command3);
	sendVal(command4);
	sendVal(1234567890);
	pulse(1);
	bitsGotten=0;
	sei(); // enable interrupts again!
	TIMSK0|=(1<<TOIE1); //Overflow Interrupt
}

// ######## MAIN PROGRAM #########
int main(){

	DDRA|=_BV(PA2)|_BV(PA3);

	// SET UP FOR SOUND CARD INTERRUPT
	MCUCR = 0b00000010; // trigger interrupt on falling edge
	GIMSK = 0b00010000; // pin change interrupt enable 0
	GIFR =  0b00010000; // flag register, same as above
	PCMSK0 = (1<<PCINT0); // Set Pin to use (PCINT0)
	sei(); // enable global interrupts

	// SET UP 8-bit COUNTER
	TCCR0B|=0b00000010;
	//TCCR1B|=(1<<CS12)|(1<<CS10); // prescaler 1024
	TIMSK0|=(1<<TOIE1); //Enable Overflow Interrupt Enable
	TCNT0=0;//Initialize our varriable (set for 1/15th second?)

	// MAIN PROGRAM
	for (;;){}
	return 0;

}

In closing, I’m tickled this works so well. It’s funny to me that no one’s really done this before in the hobby field. I’m sure I’m not the only one who wished there were an easy way to do this. I’m sure the process could be greatly improved, but this is a fun start. Wow, it’s late, I should get to bed. I have to treat patients tomorrow morning!

PS: If you replicate this concept, let me know about it! I’d love to see your project!



Using Timers and Counters to Clock Seconds
Posted by
Scott June 19th, 2011 | 5,253 words | No Comments »


Scott was 25.73 years old when he wrote this!

My current secret project involves cramming a bunch of features into a single microcontroller. The chip I chose to use is an ATMega48. The ATMega 48 is $1.40 each in small quantities and comes crammed packed with features. The chip will be quite busy performing many functions, but its main loop will be executed at least every 50ms (required for USB, did I mention I’m bit-banging USB?!). I desire to have a bit of RTC (real time clock) functionality in that I need to precisely measure seconds, although I don’t need to actually know the time or date. I desire to execute a function once per second, consuming a minimum of resources. The solution was quite simple, but I’m choosing to document it because it’s somewhat convoluted in its explanation elsewhere on the net.

In summary, the way I accomplished this is using the built-in 16-bit timer (most AVRs have such a timer, including the ATTiny series). If I’m clocking the microcontroller at a known rate (determined by my selection of crystal, 12 MHz in my case), I can set the chip to continuously increment a register (timer1) and execute a function every time it overflows. Timer1 overflows at 2^16 (65,536). I enabled a prescaler value of 256 so that it takes 256 clock pulses to increment the timer. 12MHz/256 = 46,875 Timer1 increments each second. Since Timer1 overflows at 65,536, if I initiate Timer1 at 18,661 (65,536-46,875), it will take 1 second exactly to overflow. Upon overflowing, I do something (maybe flip a LED on or off), and reset the Timer1 back to its starting value 18,661. Done! Without using an external RTC module or even an external crystal or asynchronous timer, we managed to execute a function every second on the second with minimal overhead, allowing the chip to do everything it wants in the rest of the time!

The following example is a little more specific, executing a function exactly 15 times a second, and executing another function (to flash an LED) exactly every 1 second. It should be self explanatory:

// This function is called every second on the second
volatile int count; // this should be global
ISR(TIMER1_OVF_vect){
	TCNT1=62411;//Initialize our varriable (set for 1/15th second)
	count++; //increment 1/15th second counter
	if(count==15){
		statusTOGGLE(); // do your event (flash a LED in my case)
		count=0;//reset global variable
		}
	}
// This is for ATMega48, consult datasheet for variations for different chips
// place this just inside main(), before your primary loop
TCCR1B|=(1<<CS12);// prescaler 256
TIMSK1|=(1<<TOIE1); //Enable Overflow Interrupt Enable
TCNT1=62411;//Initialize our varriable (set for 1/15th second)
count=0; //Initialize a global variable
sei(); // enable interrupts

I’m having a lot of fun spending time going through the datasheet of this chip. It has a lot of features, and some I didn’t really dig deeply into. Without giving away too much of my project, I’ll show some photos I’m excited to share. My project interfaces the PC through USB directly attached to 2 pins using no intermediate chips (wow!). The photos demonstrate various steps in the temperature measurement and calibration tests…
DSCN1367
DSCN1372



Permeability Tuned Oscillator (PTO) Working Nicely
Posted by
Scott June 5th, 2011 | 5,253 words | 4 Comments »


Scott was 25.70 years old when he wrote this!

My last entry described my accidental discovery of the PTO for QRP purposes. I breadboarded it and was amazed at the results! I went ahead and built this carefully in an enclosure and the output is wonderful. It’s strong, it’s stable, and it tunes effortlessly over the same range it did before (about 1MHz). The video describes details of the action, and demonstrates the stability of the oscillator by letting you hear it audibly on a nearby receiver.

The fundamental concept and hardware is straightforward. Two nuts are soldered into an Altoids tin providing much-needed grounding for the screw (reduces shift when it’s touched). Also the wire soldered over the screw is pinched firmly at the base to apply constant pressure to the screw to make it hard to turn and therefore more stable while turning. The inductor is a bunch of turns (no idea how many, about a meter of magnet wire) around a McDonalds straw.
DSCN1350

Alltogether it’s a simple colpitts oscillator with a MPF102 JFET at its heart, using a 74hc240 CMOS buffer as an amplifier. There’s a voltage regulator in there too.DSCN1356

The result? Pretty darn stable (by CW QSO standards).  That’s without any regard to thermal isolation or temperature compensation. I’m quite pleased!  I look forward to MUCH more experimentation now that I’m starting to feel good about designing and building simple, tunable, stable oscillators. It’s always hard to nail all 3 in a single device!
DSCN1357



Screwy Oscillator Idea
Posted by
Scott June 2nd, 2011 | 5,253 words | 3 Comments »


Scott was 25.69 years old when he wrote this!

> > > UPDATE!
>>>CLICK HERE FOR IMPROVED DEVICE

Can you believe it’s been almost 3 months since my last post? A lot’s been going on since then, namely the national board dental exam. I’m happy to report I prepared for it and performed above and beyond my expectations on the exam, and I’m quite satisfied. The last few weeks were quite a strain on my life in my aspects, and during that time I realized that I didn’t appreciate the little things (such as free time) that I would have loved to experience instead of studying. I guess it’s the feeling you have when you’re really sick and think to yourself “remember this moment so that when you’re well again, you can appreciate feeling well”. Now that it’s all behind me, what do I do? I sit at my work station, play some light music, grab an adult beverage, turn on the soldering iron, and make something special.

Update: read the bottom of the post for reflections about the concept discussed below...

I’m resuming work on my simple transmitter/receiver projects, but I’m working at the heart of the device and experimenting with oscillator designs. I built various Colpitts, Hartley, Clapp, and other oscillator designs, and I think I landed on a design I’m most comfortable with replicating. I’m actually creating a voltage controlled oscillator (VCO or VFO), with a frequency that can be adjusted by rotating a dial or two. It’s always a balance between stability and tunability for me. I don’t want to use polyvaricon variable capacitors (expensive!), and LED-based varactor diode configurations only give me a swing of about 20pf. What did I come up with?
DSCN1335

I had tremendous success using a variable inductor for coarse tuning! The inductor is nothing more than a screw entering and exiting the center of an air core inductor. I can’t claim all the credit, because I got the idea from this photo on one of the coolest websites on the planet, Alan Yates’ Lab. It looks like Alan got the idea from this page… This is so useful! Is this common HAM knowledge? Why am I, someone who’s been into RF circuitry for a couple of years now, JUST learning about this? I’m documenting it because I haven’t seen it out there on the web, and I feel it should be represented more! Here’s a video of it in action:

This is the circuit I was using: DSCN1334

This is what it looked like before the glue or screw: DSCN1307

Here’s the variable inductor enveloped in hot glue before it cooled and turned white: DSCN1316

At the end of the day, it looks nice! DSCN1339

Band changes can be accomplished by swapping the capacitor between the inductor and ground. It couldn’t be any easier! I’ll see if I can build this in a more compact manner…

UPDATE (2 days later): Apparently this is called a “Permeability Tuned Oscillator”, or PTO. It’s an early design for radios (earlier than variable capacitors) and I guess therefore not described often on the internet. Knowing it’s official title, searching yielded a few pages describing this action:

Dave, G7UVW did some analytical measurements using a mercury core!
The Tin Ear uses a PTO as its primary tuning method (also McDonalds straw?)
This guy made a PTO out of PVC with a nice screw handle!
This PTO kit seems to be used in many projects.
The Century 21’s VFO is a PTO! I love that rig and had no idea it tuned like that…
This guy used a PTO in his MMR-40 radio.
This guy uses a straw too!
Someone on Hackaday recommended This ARRL Challenge winner with an almost identical design as mine!

I guess this bright idea was so bright, it was thought of by many people long ago…



Frequency Counter Working!
Posted by
Scott February 4th, 2011 | 5,253 words | 3 Comments »


Scott was 25.36 years old when he wrote this!

I’m ecstatic! Finally I built something that worked the first time. Well… on the 3rd attempt! The goal was to develop a minimal-cost, minimal complexity frequency counter suitable for amateur radio. Although I think I can still cut cost by eliminating components and downgrading the microcontroller, I’m happy with my first working prototype.

I haven’t tested it rigorously with anything other than square waves, but I imagine that anything over 1PPV is sufficient (the input is through a bypass capacitor, internally biased right at the trigger threshold). Counting is accomplished by a 74LV8154N (dual 16-bit counter configured as 32-bit) which displays the count as four selectable bytes presented on 8 parallel pins. The heart of the device is an ATMega16 which handles multiplexing of the display and has a continuously-running 16-bit timer which, upon overflowing, triggers a reset of the counter and measurement of the output. Software isn’t perfect (you can see the timing isn’t accurate) but I imagine its inaccuracy can be measured and is a function of frequency such that it can be corrected via software. Here are some photos…IMG_5209IMG_5222IMG_5221

A PCB is DESPERATELY needed. I’ll probably make one soon. Once it’s a PCB, the components are pretty much drop-in and go! No wires! It’ll be a breeze to assemble in 5 minutes. I wonder if it would make a fun kit? It would run on a 9V battery of course, but a calculator-like LCD (rather than LED) display would be ultra-low-current and might make a good counter for field operation (3xAAA batteries would last for months!)

UPDATE: I found out that the ATMega16 donation was from my friend Obulpathi, a fellow Gator Amateur Radio Club member! He also gave me a pair of ATMega32 chips. Thanks Obul!

« Previous EntriesNext Entries »
copyright © 2006 swharden@gmail.com