Position: Index > Unclassified >

# AVR-DDS signal generator in-line ASM explained

2017-12-23 17:02
Declaration:We aim to transmit more information by carrying articles . We will delete it soon, if we are involved in the problems of article content ,copyright or other problems.

I have got a couple of questions (in fact not a first ones) for AVR DDS generator I have built:

“that’s all the asm code that I don’t understand.

Could you explain it? Is it possible to do it with inline asm (only in C)?”

I decided to explain this part more deeply. Because it took some time for myself to figure this all out.

First of all what wee have to do… it is to implement simple DDS algorithm. Read about it for example here: http://www.hit.bme.hu/~papay/sci/DDS/start.htm

In few words Direct Digital Synthesis(DDS) is known as Numerically Controlled Oscillator. Simple rule: NCO-based DDS is a point(memory location)-skipping technique (and a constant interpolation of the stored signal) and runs at constant update(clock)-rate. As the DDS output frequency is increased, the number of samples per waveform cycles decreases.

Practically speaking lets have some math. We have clock generator connected to MCU. In my case F_CPU=16000000Hz. This is our Clock In.

Then we have sinewave map stored in rom:

const uint8_t sinewave[] __attribute__ ((section (“.MySection1″)))= //256 values

{0×80,0×83,0×86,0×89,0x8c,0x8f,0×92,…

If we have 256 values of sine wave for single period, then at clock rate 16MHz picking each value from table we would have max sine wave frequency Fsine=16000000/256=64kHz; This would be ok. But what if wee need to have sinewave frequency 1kHz or 1MHz. Then wee need to implement some memory location skipping technique – this is what DDS does.

If we would like to have 128kHz instead of 64kKz we would pick every second sample from sinewave rom map (128 samples). And if we would like to have 32kHz we would have to make a delay after each sample and so on. DDS makes this much easier by having Phase accumulator.

In my case phase accumulator is 24bit variable. Phase accumulator is calculating address to sinewave table. Now all matters only in delta phase adder calculation:Firs of all calculate output frequency resolution:fres=(F_CPU/(clocks for one sample output))/2^(accumulator length) = 16000000/9/2^24=0.1059638129340278Hz.

Now ir is simple to calculate phase accumulator adder value. If wee need signal frequency

Fsignal=Acc_adder*fres; For instance if you need 1kHz output, then;

In my code I have used value frequency to calculate phase accumulator adder:

temp=frequency/RESOLUTION;

tfreq1=(uint8_t)(temp);

tfreq2=(uint8_t)(temp>>8);

tfreq3=(uint8_t)(temp>>16);

In pure assembly language this should look like:

```ldi    r31,hi8(sinewave)   ; setup Z pointer hi
ldi    r30,lo8(sinewave)   ; setup Z pointer lo

; clear accumulator

ldi     r29,0x00        ; clear accumulator
ldi     r28,0x00        ; clear accumulator

; setup adder value  to 1 kHz
ldi     r24,tfreq1      ; tfreq1->r24
ldi     r25,tfreq2      ; tfreq2->r25
ldi     r26,tfreq2      ; tfreq2->r26
LOOP1:
adc     r30,r26         ; 1 (Z pointer updated)
lpm                     ; 3 (load for sinewave table)
out     PORTD,r0        ; 1 (out to D port)
rjmp    LOOP1           ; 2 => 9 cycles```

I think ASM code explains why it is hard to do in only C language – it is hard control number of cycles in main loop. Compiler output code may differ from expected and may not be optimal. Its all about performance. You can implement DDS algorithm in C language, but this probably would give lower frequency resolution than in-line ASM.

Lets start from explaining Inline ASM I have used. Inline ASM is a same ASM languge, but ithas to be written with some rules that compiler could understand. There are many things you have to have in mind. Like usingstatic inlinefunction declaration and so on.

{

asm volatile( “eor r18, r18 ;r18<-0″ “\n\t”

“eor r19, r19 ;r19<-0″ “\n\t”

“1:” “\n\t”

“add r18, %0 ;1 cycle” “\n\t”

“adc r19, %1 ;1 cycle” “\n\t”

“adc ?, %2 ;1 cycle” “\n\t”

“lpm ;3 cycles” “\n\t”

“out %4, __tmp_reg__ ;1 cycle” “\n\t”

“rjmp 1b ;2 cycles. Total 9 cycles” “\n\t”

:

:”r18″, “r19″

);

}

In this particular case I will use explain each line of code:

Last notice that declaring simple function withoutstatic inlinegenerates ASM code where pointer signal is passed not to R30:R31 register pair but to R24:R25.

I don’t know if this made things more clear. If not search www before asking. One of starting points would be http://www.myplace.nu/avr/minidds/index.htm.

Good luck.