IQ-DDS mit ATtiny13            

von Gerd Sinning                        
Elektronik-Labor   Projekte   AVR 

So ein Generator erzeugt Signale an zwei Ausgängen, die 90 Grad Phasenverschiebung haben. Das ist dann der VFO für einen IQ Empfänger. Ziel ist es hier, den Generator mit einfachen Mittel und einem ATTiny13 aufzubauen. Die Wahl fällt auf einen DDS Generator, dessen Frequenz einfach mit einem Potentiometer eingestellt wird und der im Frequenzbereich eine lineare Skala hat. Angestrebt ist ein Bereich bis 150 kHz um VLF abzudecken.. Dazu braucht man ein paar Tricks damit das geht. Um die hohe Geschwindigkeit zu erreichen ist das Programm in Assembler, und die CPU-Clock wird im Programm auf 9,6 MHz eingestellt. Der Akkumulator ist 16 bit, das spart einen Zyklus in der Ausgabeschleife, diese Loop hat dann 8 cycles. Normalerweise verwendet man eine 256 byte Tabelle, aber bei den Rechtecksignalen geht auch eine 64 byte Tabelle, die wird eingelesen und an PortB ausgegeben. Damit ist die Ausgabe viermal schneller. Die Frequenz wird mit einem Potentiometer eingestellt, der ADC liest die Spannung in PCINT5 mit 10 bit ein und multipliziert dann mit 2. Dazu muss man eine Taste drücken um die eingestellte Frequenz zu übernehmen, das ist der Enter-Key. Dadurch läuft die Loop mit vollem Tempo, mit 9,6 MHz / 8 cycles also 1,2 MHz. An PB0 und PB1 werden die um 90 Grad phasenverschobenen Pulse abgenommen, siehe Oszillogramm. An PB0 und PB2 liegen um 180 Grad phasenverschobenen Pulse. Die lineare Frequenzskala geht von 150 Hz bis 150 kHz. Die untere Frequenz kann man anheben, wenn das 10 kOhm Poti über 1 kOhm mit Masse verbunden wird, dann geht es von 12,9 bis 150 kHz..

Der Schaltplan des IQ-Generators, einfacher geht es nicht, die Software macht's.

Hier sieht man die um 90 Grad phasenverschobenen Pulse an PB0 (rot) und PB1 (gelb). Berechnet wurde fmax = 149766 Hz, das stimmt gut mit der Messung überein. Die Berechnung ist im Programm erklärt. Zum Test habe ich das Programm auf einen ATtiny2313 angepasst, der mit einem 18.432MHz Quarz läuft. Da hat man dann die Signale mit 0, 90, 180 und 270 Grad Phasenverschiebung an den pins PB0..3. Man erreicht etwas über 1 MHz, damit geht dann schon Mittelwelle.

Das wird dann der VFO für einen IQ-Empfänger.

References:
Atmel datasheet ATtiny13, Atmel.com
Gerd's AVR assembler version 3.3: http://www.avr-asm-tutorial.net/gavrasm/index_en.html

Download: AT13ddI3.zip

;***************************************************************************
; AT13 microdds square wave generator
; generates 90 and 180 degrees phase signals
; Range: 0 - 150 kHz --
;
; Poor-mans DDS Synthesizer Freq Generator
; clock prescaler set by software to 9.6 MHz, 1.042E-07 sec/cycle
;
; changed to ATtiny15 GS 03-05, ATtiny13 06-14 ok
; Modified for the gAVR assembler V3.3
;
;***************************************************************************
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License.
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY;
;
;***************************************************************************
;
; Pinout ATtiny13/ATtiny13V 8-PDIP/SOIC
;
;(PCINT5/RESET/ADC0/dW) PB5 VCC
;(PCINT3/CLKI/ADC3) PB3 PB2 (SCK/ADC1/T0/PCINT2)
;(PCINT4/ADC2) PB4 PB1 (MISO/AIN1/OC0B/INT0/PCINT1)
; GND PB0 (MOSI/AIN0/OC0A/PCINT0)
;
;***************************************************************************
;
; Target = ATtiny13
;
; PB4 input ADC2 0-5 Volt input determines frequency
; PB3 input PCINT5 pull to low to set frequency
; PB2 output
; PB1 output
; PB0 output
; PB0 0 degrees phase pulse output
; PB1 90 degrees phase pulse output
; PB2 180 degrees phase pulse output
;
;***************************************************************************
;* The clock is set to 9.6 MHz
;*
;***************************************************************************
; Output frequency (using 16 bit accumulator) :
;
; f = deltaPhase * fClock/2^16
;
; fClock is the CPU clock divided by the
; number of cycles to output the data ( 8 cycles )
;
; f = Delta1,Delta2 * (clock/8)/65536
;
; f = Delta1,Delta2 * 18.3 * 4 @ 9.6Mhz with this 64 step table
; fmax = 1023 * 73,2 = 74883 Hz
; with *2 multiplication of Delta1,Delta2 in PinInt we get
; fmax = 1023 * 2 * 73,2 = 149766 Hz
;
.DEVICE ATtiny13 ;for gavrasm 33

.equ DDRB_config = 0b00000111
.equ portB_config = 0b00000000 ; set start sequence
.equ ADC_poti = 0b00000010 ; frequency set ADC2 (poti)

;r0 used
.def zero = r14
.def sr = r15
.def temp = r16
.def Delta1 = r25 ; Phase increment of DDS
.def Delta2 = r26 ; from ADC

.def Dds1 = r29 ; 2 byte DDS accumulator
.def Dds2 = r30
.def ZL = r30
.def ZH = r31

.cseg
.org 0
; Reset-vector to adress 0000
rjmp reset
reti
rjmp PinInt

;******************************************************************************
; PinInt on PB3, get ADC value for Delta1,Delta2 frequency setting
;******************************************************************************

PinInt:
in sr,sreg
; get ADC value for frequency setting
sbi ADCSRA, ADIF ; "1" clears ADIF flag !
sbi ADCSRA, ADSC ; start conversion
PIwait1:
sbis ADCSRA, ADIF
rjmp PIwait1 ;
in Delta1, ADCL ; first low byte
in Delta2, ADCH ; then high byte
cp Delta1, zero
cpc Delta2, zero
brne PInonzero
inc Delta1 ; no zeros in Delta
PInonzero:
lsl Delta1 ; multiply
rol Delta2 ; ADC * 2 -> 150 kHZ

out sreg,sr
reti

;******************************************************************************
; reset
;******************************************************************************

reset:

ldi temp, low(RAMEND)
out SPL, temp ; setup stack pointer
; writing calibration byte to the OSCCAL Register.
;ldi temp,0x5D ; test
;out OSCCAL,temp ; set the clock
nop
nop

; Port B setup:

ldi temp,portB_config ; set output
out PORTB, temp ; 1 = pull-up , 0 = float
ldi temp,DDRB_config ;
out DDRB,temp ; to data direction register

ldi temp,0b10000000 ; clock prescaler change enable
out CLKPR,temp
ldi temp,0b00000000 ; clock prescaler set to 0
out CLKPR,temp
nop ; wait a little when switching
nop
nop
nop

; AD-Converter setup:

ldi temp, ADC_poti
out ADMUX, temp
ldi temp,0b11010100 ; ADC init single conversion prescale 16:
out ADCSRA, temp


sbi AcsR, Acd ; Disable Comparator
sbi DidR0, Adc2d ; Disable Adc2 Digital Input

ldi temp, (1<<PCIE) ; Enable pin change IRQ
out GIMSK, temp
ldi temp, (1<<PCINT3) ; pin PB3
out PCMSK, temp

; set phsquare output
ldi ZH,high(2*phsquare) ; setup Z pointer
ldi ZL,low(2*phsquare) ; point to table in flash

ldi Delta1, 0xff ; max frequency / 2
ldi Delta2, 0x03 ; start value

sei ; enable interrupts

;**********************************************************************
; main loop
;
; Dds1,Dds2 is the phase accumulator
; Delta1,Delta2 is the adder value determining frequency
;
; add value to accumulator
; output byte to port
; repeat
;**********************************************************************
;
LOOP:
add Dds1,Delta1 ; 1
adc Dds2,Delta2 ; 1
lpm ; 3
out PORTB, r0 ; 1
rjmp LOOP ; 2 => 8 cycles


;******************************************************************************
; data tables
;******************************************************************************

; force table to begin at 256 byte boundary

.org 0x100

phsquare: ; 64 step phase squarewave table PB0,1 90 degrees PB0,2 180 degrees
; 1. quadrant 1001 = 9
.db 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09
.db 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03
.db 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06
.db 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C
; 2. quadrant 0011 = 3
.db 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09
.db 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03
.db 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06
.db 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C
; 3. quadrant 0110 = 6
.db 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09
.db 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03
.db 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06
.db 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C
; 4. quadrant 1100 = C
.db 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09
.db 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03
.db 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06
.db 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C


;******************************************************************************
; end of file
;******************************************************************************



 Elektronik-Labor   Projekte   AVR