millwood0 发表于 2013-6-16 07:06:36

A simple AD9850-based signal generator

I got a ad9850 module a while ago. after having played with it, i decided to build a signal generator around it.

some design goals:

1) as simple as possible. a minimalist implementation.
2) controlled by a mcu (12F675, 8pdip).
3) multiple bands. AF + RF.
4) can function as a frequency sweeper as well.

the project was inspired by this page:

http://www.sm7ucz.se/DDS/DDS.htm

millwood0 发表于 2013-6-16 07:09:19

Here is the module I have:



In serial mode, it needs three pins to control: WCLK (for clock), SDATA (for serial data), and FQUD (to update frequency).

millwood0 发表于 2013-6-16 07:31:23

The ad9850 module is controlled via two routines: ad9850_init() resets the gpio pins, and ad9850_freq() sets out a frequency on the two output pins.
//convert frequency / phase defaults to 0
void ad9850_freq(unsigned long freq) {
        freq = ad9850_f2w(freq);                                                //convert 32-bit freq to 32-bit tuning words
        ad9850_sendwords(freq);
}
ad9850_sendwords() sends a 40-bit string to the chip, and then stroke the FQUD pin to execute the command.
//send ad9850 words - lsb first
void ad9850_sendwords(unsigned long words) {
        IO_CLR(AD9850_PORT, AD9850_FQUD);                                //clear the pin
        ad9850_sendbyte(words);                                                //send words
        words = words >> 8; ad9850_sendbyte(words);
        words = words >> 8; ad9850_sendbyte(words);
        words = words >> 8; ad9850_sendbyte(words);
        words = words >> 8; ad9850_sendbyte(words & 0xfc);                                //send the final words -> control1..0 = 0b00
        IO_SET(AD9850_PORT, AD9850_FQUD);
        AD9850_DELAY();                                                                        //wait tFH > 7ns
        IO_CLR(AD9850_PORT, AD9850_FQUD);                                //reset FQUD
}

millwood0 发表于 2013-6-16 07:36:02

The most interesting thing is the ad9850_f2w() routine: it converts a 32-bit number (for the desired frequency) and convert it to a 32-bit tuning word.

AD9850 is a 32-bit DDS, the module itself has a 125Mhz master clock.

So the tuning word corresponding to the desired frequency is:

frequency = tuning word * 125Mhz / 2^32.

Two issues:

1) if you were to use integer math, tuning word * 125Mhz would be very very big. It will take a lot of space + time.
2) if you were to use floating point math, well, it is going to be even worse.

The implementation (original to somebody else) is very efficient in space and time. I will publish it in a couple of days, so you may think how it works, :)

hushaoxin 发表于 2013-6-16 07:52:23

看你敲的英语的口气,感觉你是个真正的老外,哪国家的呢?

RUANJI 发表于 2013-6-16 09:43:25

看精度了。我以前是直接先将频率除一部分。能整除就整除。

比如说125M的晶振,可以先直接缩小为125M/2^6,然后再算。

alias 发表于 2013-6-16 10:09:45

>>tuning word * 125Mhz would be very very big

I would do it in another way, if the multiplication factor is a constant.

I first converted the wanted DDS output frequency into milli-Hertz, represented by a binary value. Then I multiply this value by 0.034359738 to get the tuning word.

The multiplication is done using successive approximation, which involves only shifting, adding or subtracting.

Let says that binary value is X. Now shift right X five times and store in variable M and N. Next shift right N three more times and adds to M (which I will use S3P to denote). Then continue with:

S = shift right, 3 = three times, P = plus(add to M), M=minus(subtract from M)

S2M
S3P
S2M
S3P
S1P
S4M
S2P
S2M
S2P
S2M
S7P
S3P

The final error is just 6.4E-12, close enough. one can easily find out whether to add or subtract using Excel spreadsheet, viewing the difference between existing value and the target value.

millwood0 发表于 2013-6-16 18:32:45

I would do it in another way, if the multiplication factor is a constant.

that's an interesting approach.

put it in a piece of code and let's say how fast it is.

the time to beat is 0.9ms (1000 instructions) on a 1Mhz 12F675 or 0.7ms (700 instructions) on a 1Mhz AVR to convert 1Mhz into a 32-bit tuning word (125Mhz master clock)

mowin 发表于 2013-6-16 18:51:24

标记一下,也许有机会用到。

gzhuli 发表于 2013-6-16 19:46:29

据说9850也有内部PLL,不过不保证能工作,估计是和9851同一个die的筛选品。

alias 发表于 2013-6-16 22:23:55

millwood0 发表于 2013-6-16 18:32 static/image/common/back.gif
that's an interesting approach.

put it in a piece of code and let's say how fast it is.


The packed 11 digits BCD(wanted frequency output in mill-Hz requested by user) to binary conversion consumes 495 cycles, while the multiplication routine takes 861 cycles.

i.e. from 1,000,000.000Hz user input to outputting tuning word 0x020C49BB takes a total of 1366 cycles on AVR.

Looks like your timing of 700 cycles is much faster, but how accurate is the tuning word result?

chensi007 发表于 2013-6-16 22:58:49

hushaoxin 发表于 2013-6-16 07:52 static/image/common/back.gif
看你敲的英语的口气,感觉你是个真正的老外,哪国家的呢?

据矿坛有人透露。这位大师是在米华侨。能看汉字。但不会写。所以他的回贴全是英文。

millwood0 发表于 2013-6-17 01:04:42

how accurate is the tuning word result?

It is exact!
int main(void)
{
    signed long freq=1000000ul;

    //printf("Hello world!\n");
    freq=1000000ul; printf("ad9850_f2w(%10d) = %10x\n", freq, ad9850_f2w(freq));
    freq=1100000ul; printf("ad9850_f2w(%10d) = %10x\n", freq, ad9850_f2w(freq));
    freq=1110000ul; printf("ad9850_f2w(%10d) = %10x\n", freq, ad9850_f2w(freq));
    freq=1111000ul; printf("ad9850_f2w(%10d) = %10x\n", freq, ad9850_f2w(freq));

    freq = - freq;
    printf("ad9850_f2w(%10x) = %10d\n", freq, -ad9850_f2w(-freq) & 0xfffffffful);

    return 0;
}
Generated the following output (under Turbo C):


As of now, it has no rounding.

The code also has the advantage of allowing user to specify the master clock and dds bitwidth. so it can be easily adopted for other applications.

millwood0 发表于 2013-6-17 07:46:38

Basic design:

we will need three input:

1) mode selection: either output a given frequency, or sweep in a band. This is an on/off (latched) switch.
2) band selection: this is a pot that selects through a pre-determined band.
3) frequency selection: another pot that selects the frequency ina given band.

I picked the band with the goal to use them for direct tuning, so they are heavily focused on broadcast bands:
typedef struct {
        unsigned long fstart, fend;                        //starting and ending frequencies
        //unsigned char threshold;                        //band threshold
        unsigned char dly;                                        //delay in each step, in ms
} BAND_T;

//global variable
//frequency bands
const BAND_T freq_bands={
        //Fstart, Fend, DLY, /*THRESHOLD*/.
        {455000ul - 20000ul, 455000ul + 20000ul, 300},                //455K IF scan, +/- 20Khz bandwidth. default output if BAND_ADC is grounded
        {465000ul - 20000ul, 465000ul + 20000ul, 300},                //465K if scan, +/- 20Khz bandwidth
        {10700000ul - 100000ul, 10700000ul + 100000ul, 100},        //FM if scan, +/- 100Khz bandwidth
        {1, 20000, 300},                                        //audio frequencies
        {10000ul, 600000ul, 200},                        //sub RF (LW band)
        {153000ul, 279000ul, 300},                        //LW band
        {500000ul, 1700000ul, 300},                        //am broadcast band
        {1500000ul, 2300000ul, 300},                //1.5Mhz - 2.3Mhz,
        {2100000ul, 3200000ul, 300},                //2.1Mhz - 3.2Mhz. 120m band
        {3100000ul, 4600000ul, 300},                //3.1Mhz - 4.6Mhz. 90m and 75m bands
        {4500000ul, 6700000ul, 300},                //4.5Mhz - 6.7Mhz. 60m and 49m bands
        {6600000ul, 9600000ul, 300},                //6.6Mhz - 9.6Mhz. 41m and 31m (partial) bands
        {9300000ul, 14000000ul, 300},                //9.3Mhz - 14.Mhz. 31m, 25m and 22m bands - may want to split this further
        {1300000ul, 20000000ul, 300},                //13.Mhz - 20.Mhz. 22m, 19m, 16m, 15m bands - may want to split this further
        {500000ul, 20000000ul, 300}                        //rf band, default if band pin is pulled high
};You can adjust them for your liking.

hushaoxin 发表于 2013-6-17 07:50:49

chensi007 发表于 2013-6-16 22:58 static/image/common/back.gif
据矿坛有人透露。这位大师是在米华侨。能看汉字。但不会写。所以他的回贴全是英文。 ...

能看,不会写,那就很奇怪...难道使用google翻译看的?{:lol:}

millwood0 发表于 2013-6-17 07:55:08

here is the hardware design.



Note: RV1 controls the output frequency. Right now, I am using 10-bit adc results so you have 1000+ steps in any given band. You can put a small value pot in sereial with RV1 to provide some fine tuning. However, I think in another implementation, you can use RV1 for major tuning and RV2 for fine tuning - I will publish that later.

The code is mostly there.

millwood0 发表于 2013-6-17 07:57:19

RF buffer:

given that the chip utilizes a current drive, I decided that other than a serial resistor, I will not use any buffer on the output. If you want,you can use a jfet or mmic as an output device, for simplicity.

alias 发表于 2013-6-17 07:58:37

Impressive results.

Does it limit the user choice of frequency to an integer value? How does the software perform if the DDS wanted frequency were say for example, 100.12, 100.23, 100.34 Hz etc...?

This is also the main reason I chose to work in units of milli-Hz instead.

millwood0 发表于 2013-6-17 07:58:41

With all the functions implemented, the code used up about 80% of the flash. Not bad.

millwood0 发表于 2013-6-17 08:02:47

Does it limit the user choice of frequency to an integer value?

Yes.

You can get around that, with minimal efforts. For example, you can specify all frequencies in .1Hz. so 125Mhz master clock becomes 1250000000ul. You just need to remember to shrink the corresponding tuning word by /10. This allows you to tune in steps of .1hz, with minimal efforts.

millwood0 发表于 2013-6-17 08:22:28

For example: assume fmclk=125Mhz on a 32-bit dds. you want to generate a 100,000.2hz signal. You inflate fmclk to 125,000,000Hz * 10, and desired frequency to 100,000.2hz * 10. The corresponding tuning word is 3435980. Divide that by 10, you have a tuning word of 343598, which yields an output frequency of 100,000.18hz on fmclk=125Mhz 32-bit dds.

Because of the 4G limit on 32-bit unsigned long, this approach doesn't allow you to go much more beyond .1hz (on a 125Mhz fmclk). you have more flexibility if the master clock is slower.

alias 发表于 2013-6-17 09:37:58

本帖最后由 alias 于 2013-6-17 09:41 编辑

I have moved one of the bit shifting subroutines inline and now the control word conversion routine takes only 1114-495 = 619 cycles. I might even be able to cut it down with the expense of additional flash bytes.

For a fair comparison of performance in the case 0.1Hz DDS output frequency resolution, I think you will need to include instruction timing of a x10 user input conditioning routine and a divide by 10 control word output conditioning routine in your timing test. That would make your total timing higher than 700 cycles. {:smile:}






millwood0 发表于 2013-6-17 18:01:26

no need for x10 as long as you use frequencies in .1hz format. or you can shift your way out of it.

/10 can be approximated by a series of shifting.

for mcus with hardware multipliers / divisors, it may be better to just code them as x10 or /10.

millwood0 发表于 2013-6-18 06:39:19

"/10 can be approximated by a series of shifting."

Scratch that. Assuming that you are willing to take steps in .5hz, .25hz, .125hz, etc., you can simply left shift the master clock and right shifting the resulting tuning word.

It should be very fast as well.

millwood0 发表于 2013-6-18 08:27:41

The implementation (original to somebody else) is very efficient in space and time.

The algorithm is really successive approximation, as demonstrated by the following spreadsheet. It also shows how sub-1hz stepping could be added, in rows 40 and below.




the tuning word for 1,000,000hz on a 125Mhz 32-bit DDS is 0x020c 49ba (cell B6). rows 8 through 39 goes through the calculation. Column E shows the output of the approximation process.

This spreadsheet actually can be implemented via flash for a very fast algorithm if you know the fmclk and bitcount, :)

here is the generic version of the code I used:
#define F_AD9850                125000000ul                //reference clock frequency
#define W_AD9850                32                        //bitcount for the dds

//convert frequency to words
//calculate Freq * 2^BitCount / Divisor
unsigned long ad9850_f2w(unsigned long freq) {

   unsigned long Dividend = freq;
   unsigned long Quotient = 0;
   unsigned long Divisor = F_AD9850/*125000000ul*/;       //F_AD9850, in hz

   unsigned char BitCount;

   for( BitCount=W_AD9850/*32*/; BitCount!=0; BitCount-- ) {        //*2^32
       Dividend <<= 1;
       Quotient <<= 1;

       if( Dividend >= Divisor ) {
         Dividend -= Divisor;

         Quotient |= 1;
       }
   }

   return Quotient /*+ 1*/;
}

Hope it helps.

alias 发表于 2013-6-18 09:12:59

millwood0 发表于 2013-6-16 18:32 static/image/common/back.gif
that's an interesting approach.

put it in a piece of code and let's say how fast it is.


Your open challenge is to beat the 700 instructions conversion time from wanted DDS output frequency to DDS control word.

Well I have done it in 335 instructions. Can you(or anyone) do better?


;====================================================================================
;
;       Subroutine to shift right a 5-byte word(located in TempV) 'X' number of times,
;          depending on entry point to the subroutine.
;       It then adds/subtracts this shifted value in TempV into FinalV
;
;====================================================================================

;--- the entries for adding TempV to FV afterwards

S15P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S14P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S13P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S12P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S11P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S10P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S9P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S8P:                            ; move a whole byte to next byte
      mov   TV0,TV1         ; do first right most byte
      mov   TV1,TV2
      mov   TV2,TV3
      mov   TV3,TV4
      clr   TV4   
      rjmp    AddTV         ; jump to add the result to final value
      ;
S7P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S6P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S5P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S4P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S3P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S2P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S1P:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0                        
      ;                // Now add the result to final value
AddTV:
      add   FV0,TV0
      adc   FV1,TV1
      adc   FV2,TV2
      adc   FV3,TV3
      adc   FV4,TV4      
      ;
      ret            ; Return to calling routine
      
      ;=================================================
      ; Following codes perform the shift and subtract
      ;=================================================
      
S15M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S14M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S13M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S12M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S11M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S10M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S9M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S8M:                            ; move a whole byte to next byte
      mov   TV0,TV1         ; do first right most byte
      mov   TV1,TV2
      mov   TV2,TV3
      mov   TV3,TV4
      clr   TV4   
      rjmp    SubTV         ; jump to subtract result from final value
      ;
S7M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S6M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S5M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S4M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S3M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S2M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0
      ;
S1M:
      lsr   TV4
      ror   TV3
      ror   TV2
      ror   TV1
      ror   TV0                        
      ;                // Now subtract the result from final value
SubTV:
      sub   FV0,TV0
      sbc   FV1,TV1
      sbc   FV2,TV2
      sbc   FV3,TV3
      sbc   FV4,TV4
      ;
      ret      

sco518 发表于 2013-6-18 09:35:26

表示不明觉厉。。我是属于9850玩出波形就over了的那种。   。 。 。

millwood0 发表于 2013-6-18 09:36:37

Can you(or anyone) do better?

if I could use a different chip:



two calls to ad9850_f2w takes 1,430us - 796us = 630us, or 310us per call, on a 1MIPS PIC24F.

in large part thanks to its barrel shift register.

millwood0 发表于 2013-6-18 09:38:08

The difference is that you could optimize for a specific situation in assembler. Mine is done in C and is generic. so you pay for its ease of use and portability.

millwood0 发表于 2013-6-18 10:01:47

Now, that routine can be further enhanced. For example, you really don't need BitCount to control the loop. You can exit once Dividend has reached zero -> it can greatly speed up the execution for frequencies that are 2's multiples, like 32768hz.

alias 发表于 2013-6-18 10:27:25

Well said. Generic C is easier to understand and portable.   

millwood0 发表于 2013-6-21 07:29:41

Here is the first successful run of the code.//12F675 controller for ebay AD9850 module
//connection:
//AN0: analog voltage input, selects output band;
//AN1: analog voltage input, controls output frequency (when not in the scanning mode)
//GPIO3: digital pull-up/down: up: scanning (default). down: user selectable output frequency (per AN1)
//GPIO2: to AD9850 WCLK
//GPIO4: to AD9850 FQ_UD
//GPIO5: to AD9850 SDATA

//history:
//v1.0 / 6/20/2013: first run.

#include <htc.h>                                                //we use picc
#include "gpio.h"
#include "config.h"                                                //configuration words
#include "delay.h"                                                //we use software delay
#include "ad9850.h"                                                //we use ad9850
#include "adc.h"                                                //we use hardware adc

//hardware configuration
//scan switch
#define SCAN_PORT        GPIO
#define SCAN_DDR        TRISIO
#define SCAN_PIN        (1<<3)                                //scan switch on gpio3. 1->output set frequency. 0-> scan
#define IS_SCAN(pin)        IO_GET(SCAN_PORT, pin)        //return 1 if pin is high (in scanning mode), weak pull-up default to on

//freq pot
#define FREQ_ADC        ADC_AN1                                //frequency pot on adc_an1
#define BAND_ADC        ADC_AN0                                //band adc on adc_an0

#define FREQ_STEP        1023                                //how many steps in a band
#define FREQ_DLY        20                                        //delays per step
#define N_BANDS                14                                        //number of bands

//hardware configuration

//global define
typedef struct {
        unsigned long fstart, fend;                        //starting and ending frequencies
        //unsigned char threshold;                        //band threshold
        unsigned char dly;                                        //delay in each step, in ms
} BAND_T;

//global variable
//frequency bands
const BAND_T freq_bands={
        //Fstart, Fend, DLY, /*THRESHOLD*/.
        {10700000ul - 100000ul, 10700000ul + 100000ul, 10},        //FM if scan, +/- 100Khz bandwidth
        {1, 20000, 30},                                        //audio frequencies
        {10000ul, 600000ul, 20},                        //sub RF (LW band)
        {153000ul, 279000ul, 30},                        //LW band
        {500000ul, 1700000ul, 30},                        //am broadcast band
        {1500000ul, 2300000ul, 30},                //1.5Mhz - 2.3Mhz,
        {2100000ul, 3200000ul, 30},                //2.1Mhz - 3.2Mhz. 120m band
        {3100000ul, 4600000ul, 30},                //3.1Mhz - 4.6Mhz. 90m and 75m bands
        {4500000ul, 6700000ul, 30},                //4.5Mhz - 6.7Mhz. 60m and 49m bands
        {6600000ul, 9600000ul, 30},                //6.6Mhz - 9.6Mhz. 41m and 31m (partial) bands
        {9300000ul, 14000000ul, 30},                //9.3Mhz - 14.Mhz. 31m, 25m and 22m bands - may want to split this further
        {1300000ul, 20000000ul, 30},                //13.Mhz - 20.Mhz. 22m, 19m, 16m, 15m bands - may want to split this further
        {500000ul, 20000000ul, 30},                        //rf band, default if band pin is pulled high
        {455000ul - 20000ul, 465000ul + 20000ul, 30}                //455K/465K IF scan, +/- 20Khz bandwidth. default output if BAND_ADC is grounded
        //{465000ul - 20000ul, 465000ul + 20000ul, 300},                //465K if scan, +/- 20Khz bandwidth
};

unsigned char band=0;                                        //band to tune
unsigned char mode=1;                                        //mode. 0=output to a set frequency. 1=scan
unsigned long freq;                                                //output frequency
unsigned long freq_step;                                //frequency step, major
//unsigned long freq_minor;                                //frequency step, minor

//read the band setting
unsigned char adc_band(void) {
        unsigned char i;
        unsigned char tmp;
       
        tmp=adc_read(BAND_ADC)>>2;                        //read band, as unsigned char
        for (i=1; i<N_BANDS; i++)
                if (tmp < /*freq_bands.threshold*/((unsigned short) i<<8) / N_BANDS) return i-1;
        return N_BANDS-1;                                        //default is 0
}

//read the frequency / step from a pot
unsigned short adc_freq(void) {
        return adc_read(FREQ_ADC);
        //return 200;                                                //for debugging purposes only
}

//output a frequency
void out_freq(void) {
        static unsigned char band_prev=0xff;        //previous band
        static unsigned char mode_prev=0xff;        //previous mode.
        static unsigned short step_current=0;        //current step, major
        //static unsigned short step_minor=0;                //current step, minor
        static unsigned long freq_prev=0;        //previous output frequency
       
        if (band ^ band_prev) {                                //has band changed?
                freq_step = (freq_bands.fend - freq_bands.fstart) >> 10;        //calculat the frequency steps
                freq_prev = 0;                                        //reset previous frequency
                band_prev = band;                                //update band_prev
        }
               
        if (!mode) {                                                //output a set frequency
                step_current = adc_freq();                //read the steps
        } else {                                                        //output a scan
                step_current=(step_current == FREQ_STEP)?0:(step_current+1);        //increment steps
        }

        //generate the next frequency
        freq = freq_bands.fstart + freq_step * step_current;
        if (freq ^ freq_prev) {
                ad9850_freq(freq);                                //output the frequency
                freq_prev = freq;                                //update freq_prev
        }
       
        delay_ms(freq_bands.dly);                //waste some time
}

//not used
//tune to a frequency (step in ) in a band
void tune_freq(unsigned char band, unsigned short freq) {
        static unsigned char band_prev=0;        //previous band
        static unsigned short freq_prev=0;        //previous step
        if ((band ^ band_prev) || (freq ^ freq_prev)) {        //execute only if band or step has changed
                ad9850_freq(freq_bands.fstart+((freq_bands.fend-freq_bands.fstart) >> 10) * freq);
                band_prev = band;
                freq_prev = freq;
        }
        delay_ms(freq_bands.dly);                //waste some time
}

//not used
//sweep frequency in a band
void sweep_freq(unsigned char band) {
        //unsigned long freq;
        static unsigned long fdelta;
        static unsigned short step_current=0;
        //static unsigned long fstart, fend;
       
        if (step_current)
                ad9850_freq(freq_bands.fstart + fdelta * step_current);        //set the frequency
        else {
                ad9850_freq(freq_bands.fstart);        //start the frequency
                fdelta = ((freq_bands.fend - freq_bands.fstart) >> 10);        //frequency increment
        }
        //increment step_current
        step_current=(step_current == FREQ_STEP)?0:(step_current+1);
        delay_ms(freq_bands.dly);                //waste some time
}

void mcu_init(void) {                                        //initialize the mcu
        ANSEL = 0x00;                                                //porta are digital io
        //ANSELH = 0x00;                                                //all portB is digital io
        CMCON = 0x07;                                                //analog comparators off
        //IRCF2=1, IRCF1=1, IRCF0=0;                        //running at 4Mhz
       
        //enable weak pull-up
        GPPU = 0;                                                        //enable gppu
        WPU = 0xff;                                                        //enable weak pull-up on all pins

        //scan pin as input
        //not necessary on 12f675 as gpio3 is only input capable
        IO_IN(SCAN_DDR, SCAN_PIN);

        //initialize frequency step
        freq_step = ((freq_bands.fend - freq_bands.fstart) >> 10);        //calculat the frequency steps
}

//main program
int main(void) {
        //unsigned short i;
        //unsigned long freq;
        static unsigned short count=0;
        mcu_init();                                                        //initialize the mcu
        adc_init();                                                        //reset the adc module
        ad9850_init();                                                //reset ad9850
        //ad9850_freq(AD9850_1Khz);                        //2^32 / (125Mhz / 1Mhz) =34359738->1Mhz
        //ad9850_freq(ad9850_f2w(AD9850_100Khz));
        mode = 1;                                                        //defaults to scan
        band = N_BANDS-1;                                        //default band, 455k IF
        while (1) {
                band=adc_band();                                //read the band setting
                mode=IS_SCAN(SCAN_PIN);                        //1 for fixed frequency and 0 for scanning
                out_freq();                                                //output the frequency
        }
}
The controller can act as a scanner or output a user-selectable frequency. Another pot will select the output band.

I can report that it functions perfectly, :)

millwood0 发表于 2013-6-21 07:30:05

The rom is 94.5% used, :)

millwood0 发表于 2013-6-26 06:05:36

The minimalist implementation above requires a lot of real estate to calibrate the dial to control the output frequency. An easier alternative is to use an lcd, :)

so I am porting the code to a 16f674 (pdip14 chip), with 12 gpio pins: 3 for the ad9850 module, 2 for the pots, 1 for the mode switch, and 6 for the lcd (in 4-bit mode).

here is the code, without the lcd module://12F675 controller for ebay AD9850 module
//connection:
//AN0: analog voltage input, selects output band;
//AN1: analog voltage input, controls output frequency (when not in the scanning mode)
//GPIO3: digital pull-up/down: up: scanning (default). down: user selectable output frequency (per AN1)
//GPIO2: to AD9850 WCLK
//GPIO4: to AD9850 FQ_UD
//GPIO5: to AD9850 SDATA

//history:
//v1.0 / 6/20/2013: first run.

#include <htc.h>                                                //we use picc
#include "gpio.h"
#include "config.h"                                                //configuration words
#include "delay.h"                                                //we use software delay
#include "ad9850.h"                                                //we use ad9850
#include "adc.h"                                                //we use hardware adc

//hardware configuration
//scan switch
#define SCAN_PORT        PORTA
#define SCAN_DDR        TRISA
#define SCAN_PIN        (1<<3)                                //scan switch on gpio3. 1->output set frequency. 0-> scan
#define IS_SCAN(pin)        IO_GET(SCAN_PORT, pin)        //return 1 if pin is high (in scanning mode), weak pull-up default to on

//freq pot
#define FREQ_ADC        ADC_AN1                                //frequency pot on adc_an1
#define BAND_ADC        ADC_AN0                                //band adc on adc_an0

#define FREQ_STEP        1023                                //how many steps in a band
#define FREQ_DLY        20                                        //delays per step
#define N_BANDS                14                                        //number of bands

//hardware configuration

//global define
typedef struct {
        unsigned long fstart, fend;                        //starting and ending frequencies
        //unsigned char threshold;                        //band threshold
        unsigned char dly;                                        //delay in each step, in ms
} BAND_T;

//global variable
//frequency bands
const BAND_T freq_bands={
        //Fstart, Fend, DLY, /*THRESHOLD*/.
        {10700000ul - 100000ul, 10700000ul + 100000ul, 10},        //FM if scan, +/- 100Khz bandwidth
        {1, 20000, 30},                                        //audio frequencies
        {10000ul, 600000ul, 20},                        //sub RF (LW band)
        {153000ul, 279000ul, 30},                        //LW band
        {500000ul, 1700000ul, 30},                        //am broadcast band
        {1500000ul, 2300000ul, 30},                //1.5Mhz - 2.3Mhz,
        {2100000ul, 3200000ul, 30},                //2.1Mhz - 3.2Mhz. 120m band
        {3100000ul, 4600000ul, 30},                //3.1Mhz - 4.6Mhz. 90m and 75m bands
        {4500000ul, 6700000ul, 30},                //4.5Mhz - 6.7Mhz. 60m and 49m bands
        {6600000ul, 9600000ul, 30},                //6.6Mhz - 9.6Mhz. 41m and 31m (partial) bands
        {9300000ul, 14000000ul, 30},                //9.3Mhz - 14.Mhz. 31m, 25m and 22m bands - may want to split this further
        {1300000ul, 20000000ul, 30},                //13.Mhz - 20.Mhz. 22m, 19m, 16m, 15m bands - may want to split this further
        {500000ul, 20000000ul, 30},                        //rf band, default if band pin is pulled high
        {455000ul - 20000ul, 465000ul + 20000ul, 30}                //455K/465K IF scan, +/- 20Khz bandwidth. default output if BAND_ADC is grounded
        //{465000ul - 20000ul, 465000ul + 20000ul, 300},                //465K if scan, +/- 20Khz bandwidth
};

unsigned char band=0;                                        //band to tune
unsigned char mode=1;                                        //mode. 0=output to a set frequency. 1=scan
unsigned long freq;                                                //output frequency
unsigned long freq_step;                                //frequency step, major
//unsigned long freq_minor;                                //frequency step, minor

//read the band setting
unsigned char adc_band(void) {
        unsigned char i;
        unsigned char tmp;
       
        tmp=adc_read(BAND_ADC)>>2;                        //read band, as unsigned char
        for (i=1; i<N_BANDS; i++)
                if (tmp < /*freq_bands.threshold*/((unsigned short) i<<8) / N_BANDS) return i-1;
        return N_BANDS-1;                                        //default is 0
}

//read the frequency / step from a pot
unsigned short adc_freq(void) {
        return adc_read(FREQ_ADC);
        //return 200;                                                //for debugging purposes only
}

//output a frequency
void out_freq(void) {
        static unsigned char band_prev=0xff;        //previous band
        static unsigned char mode_prev=0xff;        //previous mode.
        static unsigned short step_current=0;        //current step, major
        //static unsigned short step_minor=0;                //current step, minor
        static unsigned long freq_prev=0;        //previous output frequency
       
        if (band ^ band_prev) {                                //has band changed?
                freq_step = (freq_bands.fend - freq_bands.fstart) >> 10;        //calculat the frequency steps
                freq_prev = 0;                                        //reset previous frequency
                band_prev = band;                                //update band_prev
        }
               
        if (!mode) {                                                //output a set frequency
                step_current = adc_freq();                //read the steps
        } else {                                                        //output a scan
                step_current=(step_current == FREQ_STEP)?0:(step_current+1);        //increment steps
        }

        //generate the next frequency
        freq = freq_bands.fstart + freq_step * step_current;
        if (freq ^ freq_prev) {
                ad9850_freq(freq);                                //output the frequency
                freq_prev = freq;                                //update freq_prev
        }
       
        delay_ms(freq_bands.dly);                //waste some time
}

//not used
//tune to a frequency (step in ) in a band
void tune_freq(unsigned char band, unsigned short freq) {
        static unsigned char band_prev=0;        //previous band
        static unsigned short freq_prev=0;        //previous step
        if ((band ^ band_prev) || (freq ^ freq_prev)) {        //execute only if band or step has changed
                ad9850_freq(freq_bands.fstart+((freq_bands.fend-freq_bands.fstart) >> 10) * freq);
                band_prev = band;
                freq_prev = freq;
        }
        delay_ms(freq_bands.dly);                //waste some time
}

//not used
//sweep frequency in a band
void sweep_freq(unsigned char band) {
        //unsigned long freq;
        static unsigned long fdelta;
        static unsigned short step_current=0;
        //static unsigned long fstart, fend;
       
        if (step_current)
                ad9850_freq(freq_bands.fstart + fdelta * step_current);        //set the frequency
        else {
                ad9850_freq(freq_bands.fstart);        //start the frequency
                fdelta = ((freq_bands.fend - freq_bands.fstart) >> 10);        //frequency increment
        }
        //increment step_current
        step_current=(step_current == FREQ_STEP)?0:(step_current+1);
        delay_ms(freq_bands.dly);                //waste some time
}

void mcu_init(void) {                                        //initialize the mcu
        ANSEL = 0x00;                                                //porta are digital io
        //ANSELH = 0x00;                                                //all portB is digital io
        CMCON0 = 0x07;                                                //analog comparators off
        IRCF2=1, IRCF1=1, IRCF0=0;                        //running at 4Mhz
       
        //enable weak pull-up
        RAPU = 0;                                                        //enable weak pull-up on porta
        WPUA = 0xff;                                                //enable weak pull-up on all pins

        //scan pin as input
        //not necessary on 12f675 as gpio3 is only input capable
        IO_IN(SCAN_DDR, SCAN_PIN);

        //initialize frequency step
        freq_step = ((freq_bands.fend - freq_bands.fstart) >> 10);        //calculat the frequency steps
}

//main program
int main(void) {
        //unsigned short i;
        //unsigned long freq;
        static unsigned short count=0;
        mcu_init();                                                        //initialize the mcu
        adc_init();                                                        //reset the adc module
        ad9850_init();                                                //reset ad9850
        //ad9850_freq(AD9850_1Khz);                        //2^32 / (125Mhz / 1Mhz) =34359738->1Mhz
        //ad9850_freq(ad9850_f2w(AD9850_100Khz));
        mode = 1;                                                        //defaults to scan
        band = N_BANDS-1;                                        //default band, 455k IF
        while (1) {
                band=adc_band();                                //read the band setting
                mode=IS_SCAN(SCAN_PIN);                        //1 for fixed frequency and 0 for scanning
                out_freq();                                                //output the frequency
        }
}
As you can see, it is identical to the 12f675 version, other than some defines at the beginning and the mcu_init() module, to reset the mcu and to enable weak pull-up on the PORTA pins.

I will add the lcd module later. Right now, the flash is about 50% used.

millwood0 发表于 2013-6-26 06:15:53

Now, the lcd support is up and running!//16F684 controller for ebay AD9850 module
//connection:
//AN0: analog voltage input, selects output band;
//AN1: analog voltage input, controls output frequency (when not in the scanning mode)
//PORTA3: digital pull-up/down: up: scanning (default). down: user selectable output frequency (per AN1)
//PORTA2: to AD9850 WCLK
//PORTA4: to AD9850 FQ_UD
//PORTA5: to AD9850 SDATA

//history:
//v1.0 / 6/20/2013: first run.

#include <htc.h>                                                //we use picc
#include <string.h>                                                //we use strcpy
#include "gpio.h"
#include "config.h"                                                //configuration words
#include "delay.h"                                                //we use software delay
#include "ad9850.h"                                                //we use ad9850
#include "adc.h"                                                //we use hardware adc
#include "lcd_4bit.h"                                        //we use the lcd module, in 4-bit mode
#include "misc.h"                                                //we use ultoa()

//hardware configuration
//scan switch
#define SCAN_PORT        PORTA
#define SCAN_DDR        TRISA
#define SCAN_PIN        (1<<3)                                //scan switch on gpio3. 1->output set frequency. 0-> scan
#define IS_SCAN(pin)        IO_GET(SCAN_PORT, pin)        //return 1 if pin is high (in scanning mode), weak pull-up default to on

//freq pot
#define FREQ_ADC        ADC_AN1                                //frequency pot on adc_an1
#define BAND_ADC        ADC_AN0                                //band adc on adc_an0

#define FREQ_STEP        1023                                //how many steps in a band
#define FREQ_DLY        20                                        //delays per step
#define N_BANDS                14                                        //number of bands

//hardware configuration

//global define
typedef struct {
        unsigned long fstart, fend;                        //starting and ending frequencies
        //unsigned char threshold;                        //band threshold
        unsigned char dly;                                        //delay in each step, in ms
} BAND_T;

//global variable
//frequency bands
const BAND_T freq_bands={
        //Fstart, Fend, DLY, /*THRESHOLD*/.
        {10700000ul - 100000ul, 10700000ul + 100000ul, 10},        //FM if scan, +/- 100Khz bandwidth
        {1, 20000, 30},                                        //audio frequencies
        {10000ul, 600000ul, 20},                        //sub RF (LW band)
        {153000ul, 279000ul, 30},                        //LW band
        {500000ul, 1700000ul, 30},                        //am broadcast band
        {1500000ul, 2300000ul, 30},                //1.5Mhz - 2.3Mhz,
        {2100000ul, 3200000ul, 30},                //2.1Mhz - 3.2Mhz. 120m band
        {3100000ul, 4600000ul, 30},                //3.1Mhz - 4.6Mhz. 90m and 75m bands
        {4500000ul, 6700000ul, 30},                //4.5Mhz - 6.7Mhz. 60m and 49m bands
        {6600000ul, 9600000ul, 30},                //6.6Mhz - 9.6Mhz. 41m and 31m (partial) bands
        {9300000ul, 14000000ul, 30},                //9.3Mhz - 14.Mhz. 31m, 25m and 22m bands - may want to split this further
        {1300000ul, 20000000ul, 30},                //13.Mhz - 20.Mhz. 22m, 19m, 16m, 15m bands - may want to split this further
        {500000ul, 20000000ul, 30},                        //rf band, default if band pin is pulled high
        {455000ul - 20000ul, 465000ul + 20000ul, 30}                //455K/465K IF scan, +/- 20Khz bandwidth. default output if BAND_ADC is grounded
        //{465000ul - 20000ul, 465000ul + 20000ul, 300},                //465K if scan, +/- 20Khz bandwidth
};

//lcd strings
const unsigned char str0[]="AD9850 SigGenv1";
const unsigned char str1[]="f=         Hz";
unsigned char vRAM;                                        //display buffer
unsigned long freq_prev=0;                                //previous frequency

unsigned char band=0;                                        //band to tune
unsigned char mode=1;                                        //mode. 0=output to a set frequency. 1=scan
unsigned long freq;                                                //output frequency
unsigned long freq_step;                                //frequency step, major
//unsigned long freq_minor;                                //frequency step, minor

//read the band setting
unsigned char adc_band(void) {
        unsigned char i;
        unsigned char tmp;
       
        tmp=adc_read(BAND_ADC)>>2;                        //read band, as unsigned char
        for (i=1; i<N_BANDS; i++)
                if (tmp < /*freq_bands.threshold*/((unsigned short) i<<8) / N_BANDS) return i-1;
        return N_BANDS-1;                                        //default is 0
}

//read the frequency / step from a pot
unsigned short adc_freq(void) {
        return adc_read(FREQ_ADC);
        //return 200;                                                //for debugging purposes only
}

//output a frequency
void out_freq(void) {
        static unsigned char band_prev=0xff;        //previous band
        static unsigned char mode_prev=0xff;        //previous mode.
        static unsigned short step_current=0;        //current step, major
        //static unsigned short step_minor=0;                //current step, minor
        static unsigned long freq_prev=0;        //previous output frequency
       
        if (band ^ band_prev) {                                //has band changed?
                freq_step = (freq_bands.fend - freq_bands.fstart) >> 10;        //calculat the frequency steps
                freq_prev = 0;                                        //reset previous frequency
                band_prev = band;                                //update band_prev
        }
               
        if (!mode) {                                                //output a set frequency
                step_current = adc_freq();                //read the steps
        } else {                                                        //output a scan
                step_current=(step_current == FREQ_STEP)?0:(step_current+1);        //increment steps
        }

        //generate the next frequency
        freq = freq_bands.fstart + freq_step * step_current;
        if (freq ^ freq_prev) {
                ad9850_freq(freq);                                //output the frequency
                freq_prev = freq;                                //update freq_prev
        }
       
        delay_ms(freq_bands.dly);                //waste some time
}

//not used
//tune to a frequency (step in ) in a band
void tune_freq(unsigned char band, unsigned short freq) {
        static unsigned char band_prev=0;        //previous band
        static unsigned short freq_prev=0;        //previous step
        if ((band ^ band_prev) || (freq ^ freq_prev)) {        //execute only if band or step has changed
                ad9850_freq(freq_bands.fstart+((freq_bands.fend-freq_bands.fstart) >> 10) * freq);
                band_prev = band;
                freq_prev = freq;
        }
        delay_ms(freq_bands.dly);                //waste some time
}

//not used
//sweep frequency in a band
void sweep_freq(unsigned char band) {
        //unsigned long freq;
        static unsigned long fdelta;
        static unsigned short step_current=0;
        //static unsigned long fstart, fend;
       
        if (step_current)
                ad9850_freq(freq_bands.fstart + fdelta * step_current);        //set the frequency
        else {
                ad9850_freq(freq_bands.fstart);        //start the frequency
                fdelta = ((freq_bands.fend - freq_bands.fstart) >> 10);        //frequency increment
        }
        //increment step_current
        step_current=(step_current == FREQ_STEP)?0:(step_current+1);
        delay_ms(freq_bands.dly);                //waste some time
}

void mcu_init(void) {                                        //initialize the mcu
        ANSEL = 0x00;                                                //porta are digital io
        //ANSELH = 0x00;                                                //all portB is digital io
        CMCON0 = 0x07;                                                //analog comparators off
        IRCF2=1, IRCF1=1, IRCF0=0;                        //running at 4Mhz
       
        //enable weak pull-up
        RAPU = 0;                                                        //enable weak pull-up on porta
        WPUA = 0xff;                                                //enable weak pull-up on all pins

        //scan pin as input
        //not necessary on 12f675 as gpio3 is only input capable
        IO_IN(SCAN_DDR, SCAN_PIN);

        //initialize frequency step
        freq_step = ((freq_bands.fend - freq_bands.fstart) >> 10);        //calculat the frequency steps
}

//main program
int main(void) {
        //unsigned short i;
        //unsigned long freq;
        static unsigned short count=0;
        mcu_init();                                                        //initialize the mcu
        lcd_init();                                                        //reset the lcd
        adc_init();                                                        //reset the adc module
        ad9850_init();                                                //reset ad9850
        strcpy(vRAM, str0); lcd_display(LCD_Line0, vRAM);        //display the signon message
        strcpy(vRAM, str1);                                        //prepare line1
        ultoa(&vRAM, freq, 10);                        //convert freq
        lcd_display(LCD_Line1, vRAM);                //display line1
        //ad9850_freq(AD9850_1Khz);                        //2^32 / (125Mhz / 1Mhz) =34359738->1Mhz
        //ad9850_freq(ad9850_f2w(AD9850_100Khz));
        mode = 1;                                                        //defaults to scan
        band = N_BANDS-1;                                        //default band, 455k IF
        while (1) {
                band=adc_band();                                //read the band setting
                mode=IS_SCAN(SCAN_PIN);                        //1 for fixed frequency and 0 for scanning
                out_freq();                                                //output the frequency
                if (freq ^ freq_prev) {                        //frequency has changed -> update the display
                        freq_prev=freq;                                //update the frequency
                        strcpy(vRAM, str1);                                        //prepare line1
                        ultoa(&vRAM, freq, 10);                        //convert freq
                        lcd_display(LCD_Line1, vRAM);                //display line1
                }
        }
}
the flash is 72% used -> still lots of space to waste, :)

millwood0 发表于 2013-6-26 06:25:22

slight revision://16F684 controller for ebay AD9850 module
//connection:
//AN0: analog voltage input, selects output band;
//AN1: analog voltage input, controls output frequency (when not in the scanning mode)
//PORTA3: digital pull-up/down: up: scanning (default). down: user selectable output frequency (per AN1)
//PORTA2: to AD9850 WCLK
//PORTA4: to AD9850 FQ_UD
//PORTA5: to AD9850 SDATA

//history:
//v1.0 / 6/25/2013: first run.

#include <htc.h>                                                //we use picc
#include <string.h>                                                //we use strcpy
#include "gpio.h"
#include "config.h"                                                //configuration words
#include "delay.h"                                                //we use software delay
#include "ad9850.h"                                                //we use ad9850
#include "adc.h"                                                //we use hardware adc
#include "lcd_4bit.h"                                        //we use the lcd module, in 4-bit mode
#include "misc.h"                                                //we use ultoa()

//hardware configuration
//scan switch
#define SCAN_PORT        PORTA
#define SCAN_DDR        TRISA
#define SCAN_PIN        (1<<3)                                //scan switch on gpio3. 1->output set frequency. 0-> scan
#define IS_SCAN(pin)        IO_GET(SCAN_PORT, pin)        //return 1 if pin is high (in scanning mode), weak pull-up default to on

//freq pot
#define FREQ_ADC        ADC_AN1                                //frequency pot on adc_an1
#define BAND_ADC        ADC_AN0                                //band adc on adc_an0

#define FREQ_STEP        1023                                //how many steps in a band
#define FREQ_DLY        20                                        //delays per step
#define N_BANDS                14                                        //number of bands

//hardware configuration

//global define
typedef struct {
        unsigned long fstart, fend;                        //starting and ending frequencies
        //unsigned char threshold;                        //band threshold
        unsigned char dly;                                        //delay in each step, in ms
} BAND_T;

//global variable
//frequency bands
const BAND_T freq_bands={
        //Fstart, Fend, DLY, /*THRESHOLD*/.
        {10700000ul - 100000ul, 10700000ul + 100000ul, 10},        //FM if scan, +/- 100Khz bandwidth
        {1, 20000, 30},                                        //audio frequencies
        {10000ul, 600000ul, 20},                        //sub RF (LW band)
        {153000ul, 279000ul, 30},                        //LW band
        {500000ul, 1700000ul, 30},                        //am broadcast band
        {1500000ul, 2300000ul, 30},                //1.5Mhz - 2.3Mhz,
        {2100000ul, 3200000ul, 30},                //2.1Mhz - 3.2Mhz. 120m band
        {3100000ul, 4600000ul, 30},                //3.1Mhz - 4.6Mhz. 90m and 75m bands
        {4500000ul, 6700000ul, 30},                //4.5Mhz - 6.7Mhz. 60m and 49m bands
        {6600000ul, 9600000ul, 30},                //6.6Mhz - 9.6Mhz. 41m and 31m (partial) bands
        {9300000ul, 14000000ul, 30},                //9.3Mhz - 14.Mhz. 31m, 25m and 22m bands - may want to split this further
        {1300000ul, 20000000ul, 30},                //13.Mhz - 20.Mhz. 22m, 19m, 16m, 15m bands - may want to split this further
        {500000ul, 20000000ul, 30},                        //rf band, default if band pin is pulled high
        {455000ul - 20000ul, 465000ul + 20000ul, 30}                //455K/465K IF scan, +/- 20Khz bandwidth. default output if BAND_ADC is grounded
        //{465000ul - 20000ul, 465000ul + 20000ul, 300},                //465K if scan, +/- 20Khz bandwidth
};

//lcd strings
const unsigned char str0[]="AD9850 SigGen v1";
const unsigned char str1[]="f=            Hz";
unsigned char vRAM;                                        //display buffer
unsigned long freq_prev=0;                                //previous frequency

unsigned char band=0;                                        //band to tune
unsigned char mode=1;                                        //mode. 0=output to a set frequency. 1=scan
unsigned long freq;                                                //output frequency
unsigned long freq_step;                                //frequency step, major
//unsigned long freq_minor;                                //frequency step, minor

//read the band setting
unsigned char adc_band(void) {
        unsigned char i;
        unsigned char tmp;
       
        tmp=adc_read(BAND_ADC)>>2;                        //read band, as unsigned char
        for (i=1; i<N_BANDS; i++)
                if (tmp < /*freq_bands.threshold*/((unsigned short) i<<8) / N_BANDS) return i-1;
        return N_BANDS-1;                                        //default is 0
}

//read the frequency / step from a pot
unsigned short adc_freq(void) {
        return adc_read(FREQ_ADC);
        //return 200;                                                //for debugging purposes only
}

//output a frequency
void out_freq(void) {
        static unsigned char band_prev=0xff;        //previous band
        static unsigned char mode_prev=0xff;        //previous mode.
        static unsigned short step_current=0;        //current step, major
        //static unsigned short step_minor=0;                //current step, minor
        static unsigned long freq_prev=0;        //previous output frequency
       
        if (band ^ band_prev) {                                //has band changed?
                freq_step = (freq_bands.fend - freq_bands.fstart) >> 10;        //calculat the frequency steps
                freq_prev = 0;                                        //reset previous frequency
                band_prev = band;                                //update band_prev
        }
               
        if (!mode) {                                                //output a set frequency
                step_current = adc_freq();                //read the steps
        } else {                                                        //output a scan
                step_current=(step_current == FREQ_STEP)?0:(step_current+1);        //increment steps
        }

        //generate the next frequency
        freq = freq_bands.fstart + freq_step * step_current;
        if (freq ^ freq_prev) {
                ad9850_freq(freq);                                //output the frequency
                freq_prev = freq;                                //update freq_prev
        }
       
        delay_ms(freq_bands.dly);                //waste some time
}

//not used
//tune to a frequency (step in ) in a band
void tune_freq(unsigned char band, unsigned short freq) {
        static unsigned char band_prev=0;        //previous band
        static unsigned short freq_prev=0;        //previous step
        if ((band ^ band_prev) || (freq ^ freq_prev)) {        //execute only if band or step has changed
                ad9850_freq(freq_bands.fstart+((freq_bands.fend-freq_bands.fstart) >> 10) * freq);
                band_prev = band;
                freq_prev = freq;
        }
        delay_ms(freq_bands.dly);                //waste some time
}

//not used
//sweep frequency in a band
void sweep_freq(unsigned char band) {
        //unsigned long freq;
        static unsigned long fdelta;
        static unsigned short step_current=0;
        //static unsigned long fstart, fend;
       
        if (step_current)
                ad9850_freq(freq_bands.fstart + fdelta * step_current);        //set the frequency
        else {
                ad9850_freq(freq_bands.fstart);        //start the frequency
                fdelta = ((freq_bands.fend - freq_bands.fstart) >> 10);        //frequency increment
        }
        //increment step_current
        step_current=(step_current == FREQ_STEP)?0:(step_current+1);
        delay_ms(freq_bands.dly);                //waste some time
}

void mcu_init(void) {                                        //initialize the mcu
        ANSEL = 0x00;                                                //porta are digital io
        //ANSELH = 0x00;                                                //all portB is digital io
        CMCON0 = 0x07;                                                //analog comparators off
        IRCF2=1, IRCF1=1, IRCF0=0;                        //running at 4Mhz
       
        //enable weak pull-up
        RAPU = 0;                                                        //enable weak pull-up on porta
        WPUA = 0xff;                                                //enable weak pull-up on all pins

        //scan pin as input
        //not necessary on 12f675 as gpio3 is only input capable
        IO_IN(SCAN_DDR, SCAN_PIN);

        //initialize frequency step
        freq_step = ((freq_bands.fend - freq_bands.fstart) >> 10);        //calculat the frequency steps
}

//main program
int main(void) {
        //unsigned short i;
        //unsigned long freq;
        static unsigned short count=0;
        mcu_init();                                                        //initialize the mcu
        lcd_init();                                                        //reset the lcd
        adc_init();                                                        //reset the adc module
        ad9850_init();                                                //reset ad9850
        strcpy(vRAM, str0); lcd_display(LCD_Line0, vRAM);        //display the signon message
        strcpy(vRAM, str1);                                        //prepare line1
        ultoa_s(&vRAM, freq, 10);                //convert freq, with ','
        lcd_display(LCD_Line1, vRAM);                //display line1
        //ad9850_freq(AD9850_1Khz);                        //2^32 / (125Mhz / 1Mhz) =34359738->1Mhz
        //ad9850_freq(ad9850_f2w(AD9850_100Khz));
        mode = 1;                                                        //defaults to scan
        band = N_BANDS-1;                                        //default band, 455k IF
        while (1) {
                band=adc_band();                                //read the band setting
                mode=IS_SCAN(SCAN_PIN);                        //1 for fixed frequency and 0 for scanning
                out_freq();                                                //output the frequency
                if (freq ^ freq_prev) {                        //frequency has changed -> update the display
                        freq_prev=freq;                                //update the frequency
                        strcpy(vRAM, str1);                                        //prepare line1
                        ultoa_s(&vRAM, freq, 12);                //convert freq, with ','
                        lcd_display(LCD_Line1, vRAM);                //display line1
                }
        }
}
Not fully wired up but it shows ad9850 in scan mode:

millwood0 发表于 2013-6-26 06:27:06

another slight revision: re-using freq_prev:
//16F684 controller for ebay AD9850 module
//connection:
//AN0: analog voltage input, selects output band;
//AN1: analog voltage input, controls output frequency (when not in the scanning mode)
//PORTA3: digital pull-up/down: up: scanning (default). down: user selectable output frequency (per AN1)
//PORTA2: to AD9850 WCLK
//PORTA4: to AD9850 FQ_UD
//PORTA5: to AD9850 SDATA

//history:
//v1.0 / 6/25/2013: first run.

#include <htc.h>                                                //we use picc
#include <string.h>                                                //we use strcpy
#include "gpio.h"
#include "config.h"                                                //configuration words
#include "delay.h"                                                //we use software delay
#include "ad9850.h"                                                //we use ad9850
#include "adc.h"                                                //we use hardware adc
#include "lcd_4bit.h"                                        //we use the lcd module, in 4-bit mode
#include "misc.h"                                                //we use ultoa()

//hardware configuration
//scan switch
#define SCAN_PORT        PORTA
#define SCAN_DDR        TRISA
#define SCAN_PIN        (1<<3)                                //scan switch on gpio3. 1->output set frequency. 0-> scan
#define IS_SCAN(pin)        IO_GET(SCAN_PORT, pin)        //return 1 if pin is high (in scanning mode), weak pull-up default to on

//freq pot
#define FREQ_ADC        ADC_AN1                                //frequency pot on adc_an1
#define BAND_ADC        ADC_AN0                                //band adc on adc_an0

#define FREQ_STEP        1023                                //how many steps in a band
#define FREQ_DLY        20                                        //delays per step
#define N_BANDS                14                                        //number of bands

//hardware configuration

//global define
typedef struct {
        unsigned long fstart, fend;                        //starting and ending frequencies
        //unsigned char threshold;                        //band threshold
        unsigned char dly;                                        //delay in each step, in ms
} BAND_T;

//global variable
//frequency bands
const BAND_T freq_bands={
        //Fstart, Fend, DLY, /*THRESHOLD*/.
        {10700000ul - 100000ul, 10700000ul + 100000ul, 10},        //FM if scan, +/- 100Khz bandwidth
        {1, 20000, 30},                                        //audio frequencies
        {10000ul, 600000ul, 20},                        //sub RF (LW band)
        {153000ul, 279000ul, 30},                        //LW band
        {500000ul, 1700000ul, 30},                        //am broadcast band
        {1500000ul, 2300000ul, 30},                //1.5Mhz - 2.3Mhz,
        {2100000ul, 3200000ul, 30},                //2.1Mhz - 3.2Mhz. 120m band
        {3100000ul, 4600000ul, 30},                //3.1Mhz - 4.6Mhz. 90m and 75m bands
        {4500000ul, 6700000ul, 30},                //4.5Mhz - 6.7Mhz. 60m and 49m bands
        {6600000ul, 9600000ul, 30},                //6.6Mhz - 9.6Mhz. 41m and 31m (partial) bands
        {9300000ul, 14000000ul, 30},                //9.3Mhz - 14.Mhz. 31m, 25m and 22m bands - may want to split this further
        {1300000ul, 20000000ul, 30},                //13.Mhz - 20.Mhz. 22m, 19m, 16m, 15m bands - may want to split this further
        {500000ul, 20000000ul, 30},                        //rf band, default if band pin is pulled high
        {455000ul - 20000ul, 465000ul + 20000ul, 30}                //455K/465K IF scan, +/- 20Khz bandwidth. default output if BAND_ADC is grounded
        //{465000ul - 20000ul, 465000ul + 20000ul, 300},                //465K if scan, +/- 20Khz bandwidth
};

//lcd strings
const unsigned char str0[]="AD9850 SigGen v1";
const unsigned char str1[]="f=            Hz";
unsigned char vRAM;                                        //display buffer
unsigned long freq_prev=0;                                //previous frequency

unsigned char band=0;                                        //band to tune
unsigned char mode=1;                                        //mode. 0=output to a set frequency. 1=scan
unsigned long freq;                                                //output frequency
unsigned long freq_step;                                //frequency step, major
//unsigned long freq_minor;                                //frequency step, minor

//read the band setting
unsigned char adc_band(void) {
        unsigned char i;
        unsigned char tmp;
       
        tmp=adc_read(BAND_ADC)>>2;                        //read band, as unsigned char
        for (i=1; i<N_BANDS; i++)
                if (tmp < /*freq_bands.threshold*/((unsigned short) i<<8) / N_BANDS) return i-1;
        return N_BANDS-1;                                        //default is 0
}

//read the frequency / step from a pot
unsigned short adc_freq(void) {
        return adc_read(FREQ_ADC);
        //return 200;                                                //for debugging purposes only
}

//output a frequency
void out_freq(void) {
        static unsigned char band_prev=0xff;        //previous band
        static unsigned char mode_prev=0xff;        //previous mode.
        static unsigned short step_current=0;        //current step, major
        //static unsigned short step_minor=0;                //current step, minor
        static unsigned long freq_prev=0;        //previous output frequency
       
        if (band ^ band_prev) {                                //has band changed?
                freq_step = (freq_bands.fend - freq_bands.fstart) >> 10;        //calculat the frequency steps
                freq_prev = 0;                                        //reset previous frequency
                band_prev = band;                                //update band_prev
        }
               
        if (!mode) {                                                //output a set frequency
                step_current = adc_freq();                //read the steps
        } else {                                                        //output a scan
                step_current=(step_current == FREQ_STEP)?0:(step_current+1);        //increment steps
        }

        //generate the next frequency
        freq = freq_bands.fstart + freq_step * step_current;
        if (freq ^ freq_prev) {
                ad9850_freq(freq);                                //output the frequency
                freq_prev = freq;                                //update freq_prev
        }
       
        delay_ms(freq_bands.dly);                //waste some time
}

//not used
//tune to a frequency (step in ) in a band
void tune_freq(unsigned char band, unsigned short freq) {
        static unsigned char band_prev=0;        //previous band
        //static unsigned short freq_prev=0;        //previous step
        if ((band ^ band_prev) || (freq ^ freq_prev)) {        //execute only if band or step has changed
                ad9850_freq(freq_bands.fstart+((freq_bands.fend-freq_bands.fstart) >> 10) * freq);
                band_prev = band;
                freq_prev = freq;
        }
        delay_ms(freq_bands.dly);                //waste some time
}

//not used
//sweep frequency in a band
void sweep_freq(unsigned char band) {
        //unsigned long freq;
        static unsigned long fdelta;
        static unsigned short step_current=0;
        //static unsigned long fstart, fend;
       
        if (step_current)
                ad9850_freq(freq_bands.fstart + fdelta * step_current);        //set the frequency
        else {
                ad9850_freq(freq_bands.fstart);        //start the frequency
                fdelta = ((freq_bands.fend - freq_bands.fstart) >> 10);        //frequency increment
        }
        //increment step_current
        step_current=(step_current == FREQ_STEP)?0:(step_current+1);
        delay_ms(freq_bands.dly);                //waste some time
}

void mcu_init(void) {                                        //initialize the mcu
        ANSEL = 0x00;                                                //porta are digital io
        //ANSELH = 0x00;                                                //all portB is digital io
        CMCON0 = 0x07;                                                //analog comparators off
        IRCF2=1, IRCF1=1, IRCF0=0;                        //running at 4Mhz
       
        //enable weak pull-up
        RAPU = 0;                                                        //enable weak pull-up on porta
        WPUA = 0xff;                                                //enable weak pull-up on all pins

        //scan pin as input
        //not necessary on 12f675 as gpio3 is only input capable
        IO_IN(SCAN_DDR, SCAN_PIN);

        //initialize frequency step
        freq_step = ((freq_bands.fend - freq_bands.fstart) >> 10);        //calculat the frequency steps
}

//main program
int main(void) {
        //unsigned short i;
        //unsigned long freq;
        static unsigned short count=0;
        mcu_init();                                                        //initialize the mcu
        lcd_init();                                                        //reset the lcd
        adc_init();                                                        //reset the adc module
        ad9850_init();                                                //reset ad9850
        strcpy(vRAM, str0); lcd_display(LCD_Line0, vRAM);        //display the signon message
        strcpy(vRAM, str1);                                        //prepare line1
        ultoa_s(&vRAM, freq, 10);                //convert freq, with ','
        lcd_display(LCD_Line1, vRAM);                //display line1
        //ad9850_freq(AD9850_1Khz);                        //2^32 / (125Mhz / 1Mhz) =34359738->1Mhz
        //ad9850_freq(ad9850_f2w(AD9850_100Khz));
        mode = 1;                                                        //defaults to scan
        band = N_BANDS-1;                                        //default band, 455k IF
        while (1) {
                band=adc_band();                                //read the band setting
                mode=IS_SCAN(SCAN_PIN);                        //1 for fixed frequency and 0 for scanning
                out_freq();                                                //output the frequency
                if (freq ^ freq_prev) {                        //frequency has changed -> update the display
                        //freq_prev=freq;                                //update the frequency
                        strcpy(vRAM, str1);                                        //prepare line1
                        ultoa_s(&vRAM, freq, 12);                //convert freq, with ','
                        lcd_display(LCD_Line1, vRAM);                //display line1
                }
        }
}

millwood0 发表于 2013-7-6 19:20:24

a quick update: the circuit is fully functional - I made a couple minor updates to the code posted above. Working on the box today, and waiting for a couple bnc connectors to come in soon.

Power supply: the module consumes 100 - 110ma (depending on the oscillators used) @ 5v and 70 - 80ma @ 3.3v. At 5v, the chip and the oscillator run warm. at 3.3v, they are room temperature. As such, I am running mine at 3.3v, powered from a 5v usb power supply through a TO220 1117 regulator (to minimize noise). I also soldered a L7805 there in case I need to power from a 12v+ source. However, at 12v, that regulator runs fairly warm and requires a heatsink for sure.

millwood0 发表于 2013-7-6 20:26:34

the ad9850 I posted earlier can be used to drive ad9851 as well, with this modification in ad9850_sendwords():        words = words >> 8; ad9851_sendbyte(AD9851_PLL | (words & 0xfc));        //send the final words -> control1..0 = 0b01 -> 6* PLL enabled

wqhzhy5858 发表于 2013-7-6 21:35:30

这个模块价格很便宜,我也淘宝上买了2片,发现生成的波形干扰很大,并且是固定在正弦波某个位置出现尖峰的。放大后发现尖峰是由数个较大幅度较高频率的正弦波组成的。怎么都无法彻底消除这个干扰,PCB布线不合理,后来我是自己LAYOUT画了个小板子,就没有这个干扰了

millwood0 发表于 2013-7-7 02:06:27

发现生成的波形干扰很大,并且是固定在正弦波某个位置出现尖峰的。

that has not been my experience.

I have tried four ad9850 modules. I think the chip is well done - maybe lower power consumption but that's minor for this chip if you run it at 3.3v.

the modules are pretty good - the layout is nice and well thought out - I wish they had routed Rset out. Of the four modules I have tried, the ones with green pcb are well made. the blue pcbs are skinny. they all work out of the box, as expected.

Here are some pictures.

1Mhz fixed frequency:


AM band sweep:


millwood0 发表于 2013-7-7 02:08:12

glitches may happen if you overload the square wave output pins or use too long, unshielded output wires (=antenna).

millwood0 发表于 2013-7-7 02:08:31

glitches may happen if you overload the square wave output pins or use too long, unshielded output wires (=antenna).

millwood0 发表于 2013-7-7 02:20:52

the code, as it stands now:
//16F684 controller for ebay AD9850 module
//connection:
//AN0: analog voltage input, selects output band;
//AN1: analog voltage input, controls output frequency (when not in the scanning mode)
//PORTA3: digital pull-up/down: up: scanning (default). down: user selectable output frequency (per AN1)
//PORTA2: to AD9850 WCLK
//PORTA4: to AD9850 FQ_UD
//PORTA5: to AD9850 SDATA

//history:
//v1.0 / 6/25/2013: first run.
//v1.1 / 6/26/2013: clean up the code, adding banner display
//v1.2 / 6/29/2013: revised the band definitions
//v1.3 / 7/5/2013: fixed lower limit for SW7; swapped BAND_ADC and FREQ_ADC pins for easier lay-out

#include <htc.h>                                                //we use picc
#include <string.h>                                                //we use strcpy
#include "gpio.h"
#include "config.h"                                                //configuration words
#include "delay.h"                                                //we use software delay
#include "ad9850.h"                                                //we use ad9850
#include "adc.h"                                                //we use hardware adc
#include "lcd_4bit.h"                                        //we use the lcd module, in 4-bit mode
#include "misc.h"                                                //we use ultoa()

//hardware configuration
//scan switch
#define SCAN_PORT                PORTA
#define SCAN_DDR                TRISA
#define SCAN_PIN                (1<<3)                                //scan switch on gpio3. 1->output set frequency. 0-> scan
#define IS_SCAN(pin)        IO_GET(SCAN_PORT, pin)        //return 1 if pin is high (in scanning mode), weak pull-up default to on

//freq pot
#define FREQ_ADC                ADC_AN0                                //frequency pot on adc_an0
#define BAND_ADC                ADC_AN3                                //band adc on adc_an3

#define FREQ_STEP                1023                                //how many steps in a band
#define FREQ_INC                20                                        //increments for freq in the sweep / scan mode
//#define FREQ_DLY                20                                        //delays per step, not used
#define N_BANDS                        14                                        //number of bands

//hardware configuration

//global define
typedef struct {
        unsigned char str;                //string to be displayed, for 16x2 lcd
        unsigned long fstart, fend;                        //starting and ending frequencies
        //unsigned char threshold;                        //band threshold
        unsigned char dly;                                        //delay in each step, in ms
} BAND_T;

//global variable
//frequency bands
const BAND_T freq_bands={
        //Fstart, Fend, DLY, /*THRESHOLD*/.
        {"IF0:435 - 485Khz", 455000ul - 20000ul, 465000ul + 20000ul, 0},                //455K/465K IF scan, +/- 20Khz bandwidth. default output if BAND_ADC is grounded
        {"IF1:10.6-10.9Mhz", 10700000ul - 100000ul, 10700000ul + 100000ul, 0},        //FM if scan, +/- 100Khz bandwidth
        {"AF :1 -20Khz", 1, 20000, 10},                                        //audio frequencies
        {"RF-: 10 - 600Khz", 10000ul, 600000ul, 10},                        //sub RF (LW band)
        {"LW :153 - 279Khz", 153000ul, 279000ul, 10},                        //LW band
        {"AM :500 -1700Khz", 500000ul, 1700000ul, 10},                        //am broadcast band
        {"SW1:1500-2300Khz", 1500000ul, 2300000ul, 10},                //1.5Mhz - 2.3Mhz,
        {"SW2:2100-3200Khz", 2100000ul, 3200000ul, 10},                //2.1Mhz - 3.2Mhz. 120m band
        {"SW3:3100-4600Khz", 3100000ul, 4600000ul, 10},                //3.1Mhz - 4.6Mhz. 90m and 75m bands
        {"SW4:4500-6700Khz", 4500000ul, 6700000ul, 10},                //4.5Mhz - 6.7Mhz. 60m and 49m bands
        {"SW5:6600-9600Khz", 6600000ul, 9600000ul, 10},                //6.6Mhz - 9.6Mhz. 41m and 31m (partial) bands
        {"SW6:9.3 -14.0Mhz", 9300000ul, 14000000ul, 10},                //9.3Mhz - 14.Mhz. 31m, 25m and 22m bands - may want to split this further
        {"SW7:13.0-20.0Mhz", 13000000ul, 20000000ul, 10},                //13.Mhz - 20.Mhz. 22m, 19m, 16m, 15m bands - may want to split this further
        {"RF :0.5 -20.0Mhz", 500000ul, 20000000ul, 10}                        //rf band, default if band pin is pulled high
        //{465000ul - 20000ul, 465000ul + 20000ul, 300},                //465K if scan, +/- 20Khz bandwidth
};

//lcd strings
const unsigned char str0[]="AD9850 SigGen1.2";
const unsigned char str1[]="f=            Hz";
unsigned char vRAM;                                        //display buffer
unsigned long freq_prev=0;                                //previous frequency

unsigned char band=0;                                        //band to tune
unsigned char mode=1;                                        //mode. 0=output to a set frequency. 1=scan
unsigned long freq;                                                //output frequency
unsigned long freq_step;                                //frequency step, major
//unsigned long freq_minor;                                //frequency step, minor

//read the band setting
unsigned char adc_band(void) {
        unsigned char i;
        unsigned char tmp;
       
        tmp=adc_read(BAND_ADC)>>2;                        //read band, as unsigned char
        for (i=1; i<N_BANDS; i++)
                if (tmp < /*freq_bands.threshold*/((unsigned short) i<<8) / N_BANDS) return i-1;
        return N_BANDS-1;                                        //default is 0
}

//read the frequency / step from a pot
unsigned short adc_freq(void) {
        return adc_read(FREQ_ADC);
        //return 200;                                                //for debugging purposes only
}

//output a frequency
void out_freq(void) {
        static unsigned char band_prev=0xff;        //previous band
        static unsigned char mode_prev=0xff;        //previous mode.
        static unsigned short step_current=0;        //current step, major
        //static unsigned short step_minor=0;                //current step, minor
        static unsigned long freq_prev=0;        //previous output frequency
       
        if (band ^ band_prev) {                                //has band changed?
                freq_step = (freq_bands.fend - freq_bands.fstart) >> 10;        //calculat the frequency steps
                freq_prev = 0;                                        //reset previous frequency
                band_prev = band;                                //update band_prev
                //update the display
                strcpy(vRAM, freq_bands.str);
                lcd_display(LCD_Line0, vRAM);
        }
               
        if (!mode) {                                                //output a set frequency
                step_current = adc_freq();                //read the steps
        } else {                                                        //output a scan
                step_current=(step_current >= FREQ_STEP)?0:(step_current+FREQ_INC);        //increment steps
        }

        //generate the next frequency
        freq = freq_bands.fstart + freq_step * step_current;
        if (freq ^ freq_prev) {
                ad9850_freq(freq);                                //output the frequency
                freq_prev = freq;                                //update freq_prev
        }
       
        delay_ms(freq_bands.dly);                //waste some time
}

void mcu_init(void) {                                        //initialize the mcu
        ANSEL = 0x00;                                                //porta are digital io
        //ANSELH = 0x00;                                                //all portB is digital io
        CMCON0 = 0x07;                                                //analog comparators off
        IRCF2=1, IRCF1=1, IRCF0=0;                        //running at 4Mhz
       
        //enable weak pull-up
        //RAPU = 0;                                                        //enable weak pull-up on porta
        //WPUA = 0xff;                                                //enable weak pull-up on all pins

        //scan pin as input
        //not necessary on 12f675 as gpio3 is only input capable
        IO_IN(SCAN_DDR, SCAN_PIN);

        //initialize frequency step
        freq_step = ((freq_bands.fend - freq_bands.fstart) >> 10);        //calculat the frequency steps
}

//main program
int main(void) {
        //unsigned short i;
        //unsigned long freq;
        //static unsigned short count=0;
        mcu_init();                                                        //initialize the mcu

        adc_init();                                                        //reset the adc module

        lcd_init();                                                        //reset the lcd
        strcpy(vRAM, str0); lcd_display(LCD_Line0, vRAM);        //display the signon message
        delay_ms(1000);                                                //wait for a short while to display the sign-on message
        //strcpy(vRAM, str1);                                        //prepare line1
        //ultoa_s(&vRAM, freq, 10);                //convert freq, with ','
        //lcd_display(LCD_Line1, vRAM);                //display line1
       
        ad9850_init();                                                //reset ad9850
        //ad9850_freq(AD9850_1Khz); while (1);

        //mode = 1;                                                        //defaults to scan
        //band = N_BANDS-1;                                        //default band, 455k IF
        while (1) {
                band=adc_band();                                //read the band setting
                mode=IS_SCAN(SCAN_PIN);                        //1 for fixed frequency and 0 for scanning
                out_freq();                                                //output the frequency

                if (freq ^ freq_prev) {                        //frequency has changed -> update the display
                        freq_prev=freq;                                //update the frequency
                        strcpy(vRAM, str1);                                        //prepare line1
                        ultoa_s(&vRAM, freq, 12);                //convert freq, with ','
                        lcd_display(LCD_Line1, vRAM);                //display line1
                }

        }
}

millwood0 发表于 2013-7-7 02:26:45

hardware connection:


millwood0 发表于 2013-7-7 02:30:41

ad9850 module connection:

AD9850 <-> mcu
FQ_UD <-> FQUD
WCLK <-> WCLK
SDATA <-> SDATA
RST <-> GND
D0/D1 <-> VCC
D2 <-> GND

values of the resistors / pots / capacitors are NOT critical.

Output pins:
IOUT: unfiltered RF out (DC), to bnc
IOUTB: filtered RF out (AC), to bnc
Vout_P: square wave out (DC), to bnc

Powered supply: outboard 5v (usb), step'd down to 3.3v.

millwood0 发表于 2013-7-7 02:33:46

hex file (it can be burned to a 16F684):
:100000009B27F6009A27F5005730840070300F20A8
:100010008317773084007E300F2083011728640017
:100020008001840A04060319003404061028F22310
:10003000872165237830AC000730AD005F30CC21DC
:100040005F30AC0000305821E830AC000330AD0028
:1000500096216C21FD21F7000508F5000830F50513
:10006000E0245708A0005808A1005908A2005A0827
:10007000A3005B08A0065C08A1065D08A2065E0856
:10008000A306230822042104200403192A285E0859
:10009000DA005D08D9005C08D8005B08D700893019
:1000A000AC000730AD005F30CC210C30A8005E08FA
:1000B000A7005D08A6005C08A5005B08A4006130ED
:0C00C000E3225F30AC00403058212A28B9
:1002B000AD005C212C087829AE00AF01803E232BD5
:1002C000B100B103310F61290800003063242F0809
:1002D000AD002E08AC000800D930850583168505D1
:1002E0008312051500000511851400008510080013
:1002F000AE008129AF01AF0A2E0884000008232335
:10030000AE0A2E0884008008031908007A29831693
:100310001F179F161F1283129F111F119F171F1364
:100320000C1383160C1383121F1408009B29C8306A
:10033000AE00AF01A5210130AC02031CAD032C0AB5
:1003400003192D0A0319080097292E0860212F0888
:10035000AE00AF01AD29FF3060210130AE02031CB9
:10036000AF032E0A03192F0A03190800AB29B400A2
:10037000B501B50AF93085050511340835050319AD
:10038000C3298516C429851205150310B50DB508B6
:10039000031DBC2905110800AE00AF002D08B100F7
:1003A0002C08B000AC0A0319AD0A840031080026FD
:1003B000B2002F088400AF0A32088313800080083F
:1003C00003190800CE292F08B3002E08B2002D080B
:1003D000B1002C08B0009C273308AF003208AE00F3
:1003E0003108AD003008AC002F08B3002E08B20071
:1003F0002D08B1002C08B0004E2A0C3063240310E5
:10040000AF0CAE0C0310AF0CAE0C2E08A400A5016F
:10041000A50A0E30AA00AB012508A900A8012322D5
:100420002408A600A70129082702031D192A280865
:10043000260203181D2A25030800A50A0E302502EE
:1004400003180D34092AAD01AE012B082A04031943
:10045000492AAC01AC0A0310AB1B322AAA0DAB0D22
:100460002A2A0310AD0DAE0D2B082902031D3A2ACE
:100470002A082802031C452A2A08A8022B08031C64
:10048000A903A9020130AD040310AB0CAA0CAC0BFC
:10049000312A2E08A9002D08A80008008510300870
:1004A000B7213108B0003208B1003308B200B301FF
:1004B0003008B7213108B0003208B1003308B2006B
:1004C000B3013008B7213108B0003208B100330859
:1004D000B200B3013008B7213108B0003208B100D2
:1004E0003308B200B3013008FC39B721851400008D
:1004F00085100800B401B501B601B7012C1C942A7F
:100500003008B407310803110318310A031DB50779
:10051000320803110318320A031DB607330803110A
:100520000318330A031DB7070310B00DB10DB20D48
:10053000B30D0310AF0CAE0CAD0CAC0C2F082E0499
:100540002D042C04031D7E2A3708AF003608AE00A8
:100550003508AD003408AC0008003208310430041E
:100560002F0403190800B301B30AB21BBD2A0310FC
:10057000AF0DB00DB10DB20DB42A32082E02031D1D
:10058000CB2A31082D02031DCB2A30082C02031D73
:10059000CB2A2F082B02031CDC2A2F08AB023008C1
:1005A000031C300FAC023108031C310FAD023208BE
:1005B000031C320FAE020310B20CB10CB00CAF0C26
:1005C000B30BBD2A0800A900AA012A08033A031D9B
:1005D000F02AAA01A8032808290784002C308000EB
:1005E000AA0AA8032808290784000A30AF00B0012E
:1005F000B101B2012708AE002608AD002508AC0005
:100600002408AB00AD222B08303E80000A30AF003A
:10061000B001B101B2012708AE002608AD002508DF
:10062000AC002408AB00A8232E08A7002D08A600C4
:100630002C08A5002B08A400270826042504240460
:1006400003190800E52AB000B01F282B0714292B36
:100650000710301F2D2B87142E2B8710B01E322B26
:100660000715332B0711301E372B8715382B8711B1
:1006700000306021AF0803193F2B8716402B8712EB
:100680000716003060210712B01D482B0714492BB4
:100690000710301D4D2B87144E2B8710B01C522B8A
:1006A0000715532B0711301C572B8715582B871113
:1006B00000306021AF0803195F2B8716602B87126B
:1006C00007160030602107120800CF308316870517
:1006D000F03087050F308312AC00AD0196210C304D
:1006E0008704FC3087050030602187120716003030
:1006F000602107120530AC00AD0196210030602169
:1007000087120716003060210712C830AE00AF0113
:10071000A5210030602187120716003060210712E2
:10072000C830AE00AF01A52187120715F430870548
:100730000030602187120716003060210712AF01D8
:100740002B302323AF010C302323AF010630232BA2
:10075000B401B501B601B7013208310430042F04E9
:100760000319E92BB301B30A0310B21BBD2BAF0D64
:10077000B00DB10DB20DB32B0310B40DB50DB60D08
:10078000B70D32082E02031DCF2B31082D02031D99
:10079000CF2B30082C02031DCF2B2F082B02031C5C
:1007A000E32B2F08AB023008031C300FAC023108DA
:1007B000031C310FAD023208031C320FAE020130B0
:1007C000B40400300310B20CB10CB00CAF0CB30B7E
:1007D000BC2B3708AE003608AD003508AC00340835
:1007E000AB0008008316910107308312990083162D
:1007F0000F178F160F1285151A308312AE00AF0136
:100800007708AC00AD01E7271D302C0784000630C7
:10081000031807302D07A7000026A400840A031937
:10082000A70A27080026A500840A0319A70A270893
:100830000026A600840A0319A70A27080026A70095
:100840001A30AE00AF017708AC00AD01E7272130C8
:100850002C0784000630031807302D07AB00002654
:10086000A800840A0319AB0A2B080026A900840AF1
:100870000319AB0A2B080026AA00840A0319AB0A45
:100880002B080026AB002408A8022508031C250F0E
:10089000A9022608031C260FAA022708031C270FFB
:1008A000AB020A300310AB0CAA0CA90CA80CFF3E3B
:1008B000031D522C2B08F3002A08F2002908F1002E
:1008C0002808F0000800B000982CE3309F0583163C
:1008D0009101910ABC2C1F08E33904389F000230B3
:1008E000942C1F08E33908389F000430942C1F080B
:1008F000E3390C389F000830942C1F08E339103876
:100900009F001030942C1F08E33914389F002030CA
:10091000942C1F08E33918389F004030942C1F088E
:10092000E3391C389F00803083169100BC2C972C33
:1009300030081C39B100B201B72C3108003A031954
:10094000652C043A03196B2C0C3A0319712C043AE8
:100950000319772C1C3A03197D2C043A0319832CB4
:100960000C3A0319892C043A03198F2C972C32085E
:10097000003A03199D2C972C000000000000000095
:100980000000000000000000000000000000000067
:100990000000000000000000000000000000000057
:1009A000000000000000000083129F149F18D62C46
:1009B00083121E08AF0083161E088312AE000800C3
:1009C000770876060319682D1A30AE00AF01770854
:1009D000AC00AD01E7271D302C078400063003185A
:1009E00007302D07A7000026A400840A0319A70AD0
:1009F00027080026A500840A0319A70A270800264D
:100A0000A600840A0319A70A27080026A7001A309F
:100A1000AE00AF017708AC00AD01E72721302C070D
:100A200084000630031807302D07AB000026A8000D
:100A3000840A0319AB0A2B080026A900840A0319AB
:100A4000AB0A2B080026AA00840A0319AB0A2B085C
:100A50000026AB002408A8022508031C250FA902C4
:100A60002608031C260FAA022708031C270FAB0227
:100A70000A300310AB0CAA0CA90CA80CFF3E031DF6
:100A8000392D2B08F3002A08F2002908F100280864
:100A9000F000FA01FB01FC01FD017708F6001A30B5
:100AA000AE00AF017708AC00AD01E7270C30A40021
:100AB0000630A5002C082407AC002D0803182D0AC9
:100AC0002507AD005F30CC215F30AC0000305821ED
:100AD000F508031D712D65212D08F9002C08F8007B
:100AE0007F2D03307902FF300319780203187D2D22
:100AF0001430F8070318F90A7F2DF801F9011A30AC
:100B0000AE00AF017708AC00AD01E7271D302C0720
:100B100084000630031807302D07A7000026A40024
:100B2000840A0319A70A27080026A500840A0319C6
:100B3000A70A27080026A600840A0319A70A27087F
:100B40000026A7007308B3007208B2007108B10054
:100B50007008B0007808AC007908AD00AE01AF01B4
:100B60007A222C08A4072D0803182D0FA5072E089C
:100B700003182E0FA6072F0803182F0AA707270808
:100B8000DE002608DD002508DC002408DB007A08EA
:100B9000A4007B08A5007C08A6007D08A7005B08D0
:100BA000A4065C08A5065D08A6065E08A706270839
:100BB0002604250424040319EE2D5E08AF005D0809
:100BC000AE005C08AD005B08AC00E3215E08FD00F0
:100BD0005D08FC005C08FB005B08FA001A30AE0000
:100BE000AF017708AC00AD01E72725302C07840062
:100BF0000630031807302D070026AC00AD019629FA
:100C0000FE00FE1F082E83137E18831700080800BD
:100C10008A000408820000344934463430343A34BF
:100C200034343334353420342D34203434343834AF
:100C300035344B3468347A3400343834A3340634D1
:100C40000034883466340734003400344934463480
:100C500031343A34313430342E3436342D34313466
:100C600030342E3439344D3468347A3400344034DE
:100C7000BE34A13400348034CB34A4340034003486
:100C80004134463420343A34203420343134203452
:100C90002D3420342034323430344B3468347A34B8
:100CA0000034013400340034003420344E34003435
:100CB00000340A34523446342D343A34203431343A
:100CC000303420342D3420343634303430344B3406
:100CD00068347A3400341034273400340034C0349B
:100CE0002734093400340A344C34573420343A342D
:100CF00031343534333420342D34203432343734E5
:100D000039344B3468347A340034A83455340234DE
:100D10000034D8344134043400340A3441344D347E
:100D200020343A3435343034303420342D343134B6
:100D30003734303430344B3468347A34003420342F
:100D4000A13407340034A034F034193400340A34A8
:100D50005334573431343A34313435343034303418
:100D60002D3432343334303430344B3468347A34C4
:100D700000346034E33416340034603418342334DF
:100D800000340A345334573432343A343234313440
:100D9000303430342D3433343234303430344B3416
:100DA00068347A34003420340B3420340034003476
:100DB000D434303400340A345334573433343A346E
:100DC00033343134303430342D34343436343034F8
:100DD00030344B3468347A34003460344D342F343A
:100DE0000034C0343034463400340A345334573479
:100DF00034343A3434343534303430342D343634B9
:100E00003734303430344B3468347A34003420345E
:100E1000AA3444340034E0343B34663400340A34B9
:100E20005334573435343A3436343634303430343D
:100E30002D3439343634303430344B3468347A34E9
:100E400000344034B5346434003400347C3492349B
:100E500000340A345334573436343A3439342E3467
:100E6000333420342D34313434342E3430344D3452
:100E700068347A3400342034E8348D3400348034DB
:100E80009F34D53400340A345334573437343A3429
:100E9000313433342E3430342D34323430342E3433
:100EA00030344D3468347A34003440345D34C634E0
:100EB000003400342D34313401340A345234463491
:100EC00020343A3430342E34353420342D34323416
:100ED00030342E3430344D3468347A340034203495
:100EE000A1340734003400342D34313401340A3451
:100EF0004134443439343834353430342034533484
:100F000069346734473465346E3431342E343234C6
:100F1000003466343D3420342034203420342034EE
:100F200020342034203420342034203420344834F9
:100F30007A3400340134FF343308C0003208BF0073
:100F40003108BE003008BD00B901BA01BB01BC01C7
:100F50000730B8007330B7005930B6004030B500E4
:100F60002030B4000310BD0DBE0DBF0DC00D031029
:100F7000B90DBA0DBB0DBC0D38084002031DCA2FB8
:100F800037083F02031DCA2F36083E02031DCA2F31
:100F900035083D02031CDC2F3508BD023608031C52
:100FA000360FBE023708031C370FBF023808031C78
:100FB000380FC0020130B904B40BB22F3C08B300A3
:100FC0003B08B2003A08B1003908B0000800B0018F
:100FD000B1012C1CF12F2E08B0070318B10A2F08FD
:100FE000B1070310AE0DAF0D0310AD0CAC0C2D0806
:100FF0002C04031DE92F3108AD003008AC000800B7
:02400E00C430BC
:00000001FF
flash usage is about 88%. That means you can add additional bands - about 10 more bands.

tsingyoung 发表于 2013-10-23 16:16:31

非常感谢分享!刚接触DDS,需要慢慢消化。

另,不知DDS用作扫频和调频FM有何异同?
页: [1]
查看完整版本: A simple AD9850-based signal generator