Pregunta

I am trying to generate sine wave using programmable waveform generator AD9833 with ATmega32-A micro controller.(MCLK =8MHz clock frequency). I am using USART communication and so if i change frequency in hyper terminal then the waveform frequency has to change. I wrote small code for this as shown below.

but from the above code I am generating sine wave but if I want to change frequency of signal to 125KHz then I have to enter 499.9KHz at hyperteminal.If i enter 125 KHz then it is showing 31.2KHz. I don't know why it is generating like that and what mistake I did? and also it is changing the waveform until 500KHz frequency suppose if i enter frequency of wave form around 1000KHz but there is no change in the frequency of the signal it is showing only 125KHz.

finally i want to generate waveform with different frequency's. If I change frequency at hyper terminal or putty then what ever frequency I enter it has to generate waveform with that frequency.

This is my first post so if any grammatical mistakes are there then please excuse me.

Thanks in advance.

     void unicom(void){

         switch(Command){


                case(WGF):
            if(Param < 500)
                SetWGFreq(Param);   
                Command = 0;
            break; 

               case....
               case....
               default:
             }

  void main(void){
  SetWGFreq(125);
  -----------
   --------
   }

Hi Once Again,

This time i am trying programming AD9833 with SM470R1B1M-HT microcontroller via SPI. I am fallowing the same principle as explained by the "ross" below. It seems i am not able to change sinewave frequency. Below is the code i am trying, I set the same configuration with clocks as before.

void SetupSPI(void);
unsigned char spi(unsigned char data);
void SetWGFreq(unsigned int);
void setFrequencyA(unsigned long fdata);
void WG_CS_Status(int status);

int main(void)
{
GCR &= ~ZPLL_MULT4; 
GCR &= ~ZPLL_CLK_DIV_PRE1; 
GCR &= ~ZPLL_CLK_DIV_PRE2;
GCR &= ~ZPLL_CLK_DIV_PRE3;   

PCR = CLKDIV_1;                         // ICLK = SYSCLK 
PCR |= PENABLE;                         // Enable peripherals

GIODIRA |= X7;

CLKCNTL |= CLKDIR | CLKSR_ICLK;

SetupSPI();

for(;;)
{
  //SetWGFreq(25);
  setFrequencyA(1045200);
}                             // Wait in endless loop
}

void SetupSPI(void)
{
int data = 0;

 SPI2CTRL1 = CHARLEN_8 + PRESCALE_4;                // 8 bits per xfer
 SPI2CTRL2 |= CLKMOD + MASTER + POLARITY;             // We are the master
 SPI2PC6 |= SOMI_FUN | SIMO_FUN | CLK_FUN;
 // SPI2PC6 |=   0x0E;
 // enable
 SPI2CTRL2 |= SPIEN;

 data = SPI2BUF;
}

unsigned char spi(unsigned char data)
 {
    SPI2DAT1 = data;             
    while(!(SPI2CTRL3 & 0x01)){}        // Wait for RxFlag to get set    
    return (SPI2BUF & 0x000000FF);      // Read SPIBUF 
 } 

void setFrequencyA(unsigned long fdata)
 {
 WG_CS_Status(0);
 while(GIODOUTA&X7);   // Delay
 spi(0x20);      // Initiate loading of frequence register 0 by 28 bits.
 spi(0x00);
 spi(( 0x40 | (0x3F & (fdata >> 8))));   // load bit 8-13 + 0x40.
 spi(fdata);                             // load bit 0-7
 spi(( 0x40 | (0x3F & (fdata >> 22))));  // load bit 22-27 + 0x40.
 spi(fdata >> 14);                       // load bit 14-21
 spi(0);   // dummy write
 WG_CS_Status(1);
}
void WG_CS_Status(int status)
 {    
 if(status == 0)
  {
     // Make Chip Select low
     GIODOUTA &= ~X7;
  }
 else
  {
     // Make Chip select high
     GIODOUTA |= X7; 
   }
 }

I am attaching the SPI guide i fallowed for this controller and AD9833 programming not.SPI guide AD9833

¿Fue útil?

Solución

When you're converting the frequency to the two blocks of 14-bit numbers the AD9833 needs, you're OR'ing in the D14 and D15 of the frequency register. There are two problems there.

The first problem is that you're losing 4 bits (2 bits of a time) of your freg value.

76543210 76543210 76543210 76543210
LL000000 00000000 LL000000 00000000 //The L's are lost because they're overwritten by the addressing.

The second problem is that you need the bits 15 and 14 to be exactly 0x40. Right now with OR'ing in the data the result could be 0xC0 if data had already been there based on your frequency.

As a minor note I see no reason to use masks when assigning to fByte0-3, as you're using as assignment.

So, combining the two fixes and simplifying the masks gives:

fByte0 = (char)freg;
fByte1 = (char)(freg>>8);
fByte1 = (fByte1 & 0x3F) | 0x40; //clears bits 15 and 14, then sets for FREQ0
fByte2 = (char)(freg>>14);  //byte1 only has 6 bits, so move over by 8+6
fByte3 = (char)(freg>>22);  //byte1 only has 6 bits, so move over by 8+8+6
fByte3 = (fByte3 & 0x3F) | 0x40; //clears bits 15 and 14, then sets for FREQ0

I think that'll get you where you want to be.

Otros consejos

The fundamental issue I see with your code is that it is not layered. You are trying to do too much data manipulation in one routine. Your code for reading the ADC values in another post is layered and that is what you should be doing for this device.

The AD9833 waveform generator is fundamentally a 16-bit device. Its three types of registers that you need to program are all 16 bits. It's only the data transfer (using SPI) that is 8 bits. So only the actual output statements (using spi()) need to deal with bytes (just like your ADC input routine immediately converts the bytes read into a short integer). The values calculated for the generator should be 16-bit values. Then you should be able to see how the code implements the data requirements in the generator's data sheet.

void WG_out(unsigned short rval)
{
    /* assume little-endian CPU but AD9833 wants high byte first */
    spi(*((unsigned char *)&rval + 1));
    spi(*(unsigned char *)&rval);
}

void SetWGFreq(int fsel, unsigned int freq)
{
    unsigned short freg_addr;
    unsigned short freq_reghi;
    unsigned short freq_reglo;
    unsigned short cntl_reg;

    SPCR = 0x5A;         /* set SPI to mode 2 and Fosc/64 */
    WG_CS = 0;

    freg_addr = (fsel > 0) ? 0x8000 : 0x4000;
    /* split the f value into two 14-bit values */
    freq *= 33554.432;   /* number based on a MCLK of 8 MHz */
    freq_reghi = freg_addr | ((freq >> 14) & 0x00003fff);
    freq_reglo = freg_addr | (freq & 0x00003fff);

    cntl_reg = (1 << 13) | (fsel > 0) ? (1 << 11) : 0;
    while (WG_CS_PIN)   /* wait for chip select to go low */
        /* DELAY */ ;   
    WG_out(cntl_reg);   /* set B28 in Control */
    WG_out(freq_reglo); /* 14 LSB goes first */
    WG_out(freq_reghi); /* 14 MSB goes next */
    WG_CS = 1;
}

void SetWGPhase(int fsel, int psel, unsigned int phase)
{
    unsigned short cntl_reg;
    unsigned short phase_reg;

    SPCR = 0x5A;
    WG_CS = 0;
    cntl_reg = (1 << 13) | (fsel > 0) ? (1 << 11) : 0 | (psel > 0) ? (1 << 10) : 0;
    phase *= 1303;      /* 4096 / 2pi radians */ 
    phase_reg = 0xc000 | (psel > 0) ? (1 << 13) : 0 | (phase & 0x0fff);
    while (WG_CS_PIN)   /* wait for chip select to go low */
        /* DELAY */ ;   
    WG_out(cntl_reg);   /* set B28 in Control */
    WG_out(phase_reg);
    WG_CS = 1;
}

This code allows specification of either FREQ0 or FREQ1 and either PHASE0 or PHASE1 registers. The source code for *WG_out()* looks complex, but it actually compiles down to very simple machine code.

Code Improvements still needed

  • Magic numbers need to be replaced with #defines.

  • Instead of rewriting the complete Control register, a state variable needs to be maintained so that only Control bits are modified as needed. Then the fsel argument in SetWGPhase() can be eliminated.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top