SineWave with Xmega-A1 Xplained (ATxmega128A1)

Go To Last Post
42 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi everyone,

I am really a newbie on this and I have this project that needs to use this ATxmega128A1 chip to generate sine wave. I am given the AVR Dragon as the debugger, anyone could help me get started with this project in anyways? I've tried googling, all the codes I've found does not seem very useful and is quite complicated for me to understand. I am not a very good programmer. Please help. Thank you.

I have installed avr studio 4 and the toolchain, and everything set-up correctly I supposed. Need some help with the codes, to get the thing started.
Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello there , depending on your electronics backround , the "help" will be different. Generate a sine wave from a AVR (or any processer) is a very wide and general subject. Please provide some info on what you know about programming and electronics, and details on the project. Generally speaking, sine waves can be implemented via PWM (and sometimes filters/ analog integrators ) or DAC .

Alex

There are 10 kinds of people... those who digg binary and those who don't

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Welcome to the Forum.

Quote:
studio 4

?
I don't recall, but if you are using the Xmega series you might have to skip to Studio 6.x. (Don't install Studio 5.anything). AVR Studio 4.whatever might not have your Xmega as part of its capabilities.

Have your own PCB, or a commercial PCB?

Any particular frequency range your micro has to provide?

Do you need to be able to vary the amplitude, or DC offset of the signal, (i.e. make a signal generator?), or just generate a sin?

Start by blinking an LED. This proves your software development platform, programmer, and PCB all work.

The Xmega has a DAC, so it makes sense to use it. You feed it numbers and it outputs a voltage.

I'd start with a table of numbers that generate 1/2 of a sin wave. Then I'd write a loop to read a value, output it to the DAC, delay a bit, get the next sample.

If you use 0 - 1024 as the range, then use 512 as the mid-point. For the first half of the sin wave you add your table value to the 512 to generate a + half cycle. For the second half of the sin wave subtract the value from 512, generating the negative half cycle.

Once that works you can change the program to just use 1/4 of a sin wave table, and again use symmetry to generate a full sin wave.

The above method will tie up the micro, and it will spend all of its time generating the sin wave. That isn't a problem if that meets the (homework ?) assignment.

In the real world one often wants the micro to also run a user interface, (LCD display of the sin frequency, for example), and read a push button switch for increasing or decreasing the frequency, etc.

In this case one would often use a Timer/ Counter to generate an interrupt every so often, and then output a new sample whenever the interrupt occurred. This eliminates the delay routine in the first approach. It frees up most of the micro's time to run the user interface, or other tasks.

The "problem" with the above is that one has to change the interrupt rate to change the rate at which the samples are send to the DAC. Doable, but one has a relatively limited range of interrupt rates to chose from.

The "solution" to the above is to switch to a Direct Digital Synthesis method of generating the sin wave. Here you use a fixed interrupt rate, (generally as fast as possible), and the software selects which sample to read from the look up table. Typically the samples are NOT read in sequential order in this case. Google Jesper's Mini-DDS for an example.

Perhaps a Moderator can move this to the Xmega section?

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hallo rainblag

I have made a synthesizer with Xmega128 chip (see link). The base of sound is a Direct Digital Synthesis (DDS) and a lot of wavetables for sinus, rectrangel and more.

DDS: http://www.electricdruid.net/ind...
Synthesizer Project: http://www.cczwei-forum.de/cc2/t...

Simple code for a sinus in c: http://www.cczwei-forum.de/cc2/t...

Greetings Rolf from Germany

Windows 10 Home 64Bit, ASUS M4A89GTD-PRO/USB3, AMD Phenom II X6 1055T, Ram 2x 4GB, SSD Samsung EVO840 250GB, SATA HD 2.0TB, NVIDIA GeForce GTX 750

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everyone who replied above. I shall reply one by one as a sign of courtesy.

Hi Sagi12313,

Thank you. I am not really sure how to xplain microp works, I am just given that and was asked to research and learn how to program it. I understand c programming, to some extend. I got to generate a fixed 25KHz SineWave, and was also given a diagram of the sallen key low pass filter circuit, I believe that is to help me limit the frequency at 25KHZ that is generating from the microp.
My only concern now is how to generate a sine wave that has frequency of 25kHz and above? Or is there a way to generate a for sure 25Khz sine wave, I am totally new to this so I have a lot of doubts, pardon me if I sound too noob.

Thank you!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everyone who replied above. I shall reply one by one as a sign of courtesy.

Hi DocJC,

Thanks for your sincere reply, it was enlightening! Yes I saw the DAC output! and I was googling around for how to use DAC on the omega. Apparently the AVR studio 4 has my board, the xmega128A1. So I should be fine using the studio 4, I heard bout the 5 it has problems with dragon, 6 has too isn't it?

I wouldn't mind generating a continuos sine wave, in fact that is exactly what I need for my assignment. I will be happy enough if the microp can generate a fixed 25Khz sine wave for me, I only need it for that. And all I need is an on off button to turn the sine wave on and off, I don't think thats a problem for me.

I am still studying on your reply, thank you really, I am learning quite a lot from all these replies you guys posted! but i don't really understand what is needed to program the DAC and all, like what headers to include, etc.. I will be researching on it, but would appreciate if you give me some guidance.

Thank you! I am learning so much from your reply and am still studying on it, appreciate it!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi rolfdegen!

Thanks for your code! I will be trying it out by this week, as I have many other assignment that I am busy with. I see some german language explanation on it, I don't quite understand :| probably I will google translate them! But I am trying to study the code, it seems like its what I will be needing.
Thank you so much! will let all of you know if it works for me!

Thank you all!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

DocJC wrote:
Welcome to the Forum

The Xmega has a DAC, so it makes sense to use it. You feed it numbers and it outputs a voltage.

I'd start with a table of numbers that generate 1/2 of a sin wave. Then I'd write a loop to read a value, output it to the DAC, delay a bit, get the next sample.

If you use 0 - 1024 as the range, then use 512 as the mid-point. For the first half of the sin wave you add your table value to the 512 to generate a + half cycle. For the second half of the sin wave subtract the value from 512, generating the negative half cycle.

DocJC,

Hi again, I don't quite understand bout the part where I feed the DAC numbers. You said I could select a range, 0-1024. mid point 512. What does this range determines? is it the x axis of my sine wave? and each at each point, is it okay to have a +2 increment till 256 then -2 decrement till 512 which at this point hits my 0 of the Y axis? This also means that I am manually plotting the wave isn't it?

Am I getting the right concept of your explanation ? Let me know! Thank you :D

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hallo rainblag

I think the best way for 25KHz Sinus Wave Generation is assembler code programming in Xmega Chip. Its simple. You can mixing code in c and assembler. Programming inits for timer and another in c and calc sinus wave and dac-output in assembler. See my code. Its dds in my synth. I programming a lot of funktion in c and calc Waves in assembler.

How does this work ?
You make a 12Bit Sinus-Table (128 Byte long) in the Flash Memory from Xmega and read in a time interrupt a sample from sinus-table and send this to the dac-out. This is verry fast.

//*************************************************************************
// Timer1 Interrupt Routine (priority high)       
// all 25usec = 40.0 KHz Samplerate
// Soudausgabe auf DACA + DACB
// Version 1.2 vom 03.10.2013  
//*************************************************************************

  #include "avr/io.h"

  .extern Audio_irq					   // Name der Assembler-Funktion
  .global TCC1_OVF_vect           // Timer1 Interrupt-Vektor

//-------------------------------------------------------------------------
// use mcu register and definition
//-------------------------------------------------------------------------
  chanalA_0   = 16		; R16  ChanalA low  Byte
  chanalA_1   = 17		; R17  ChanalA high Byte

  PhaseA0     = 18		; R18  noise generation
  PhaseA1     = 19		; R19
  PhaseA2     = 20      ; R20

  phakku0     = 18      ; R18  Phasen-Akku  Byte 0
  phakku1     = 19      ; R19               Byte 1
  phakku2     = 20      ; R20               Byte 2

  delta0      = 21      ; R21  Phasen-Delta Byte 0
  delta1      = 22      ; R22               Byte 1
  delta2	  = 23      ; R23               Byte 2

  chanalB_0   = 24		; R24  ChanalB low  Byte
  chanalB_1   = 25		; R25  ChanalB high Byte
 
//-------------------------------------------------------------------------
// save Stack
//-------------------------------------------------------------------------
TCC1_OVF_vect:
  cli	           
  PUSH  R0                       ; 2   R0  auf Stack schieben
  IN    R0, SREG                 ; 1   Status-Register über bereits gesichertes
  PUSH  R0                       ; 2   R0  auf Stack schieben
  PUSH  R1                       ; 2   R1  auf Stack schieben
  PUSH  R16                      ; 2   R16 auf Stack schieben 
  PUSH  R17                      ; 2   R17 auf Stack schieben
  PUSH  R18                      ; 2   R18 auf Stack schieben
  PUSH  R19                      ; 2   R19 auf Stack schieben
  PUSH  R20                      ; 2   R20 auf Stack schieben
  PUSH  R21                      ; 2   R21 auf Stack schieben
  PUSH  R22                      ; 2   R22 auf Stack schieben
  PUSH  R23                      ; 2   R23 auf Stack schieben
  PUSH  R24                      ; 2   R24 auf Stack schieben
  PUSH  R25                      ; 2   R25 auf Stack schieben
  PUSH  R30                      ; 2   R30 auf Stack schieben (ZL)
  PUSH  R31                      ; 2   R31 auf Stack schieben (ZH)

//-------------------------------------------------------------------------
// Osc1 switch
//-------------------------------------------------------------------------
Osc1_Switch:
LDS   R21, osc_mode					; load osc_mode
SBRC  R21, 3						; if Bit3 = 1 then Osc1 is switch off  
RJMP  Osc1_Off						; jump osc1 set off
SBRC  R21, 2						; if Bit 2 = 1 then Osc1 in FM-Mode
RJMP  Osc1_FM						; jump osc1 fm-mode
RJMP  Osc1_Mix						; jump osc1 mix-mode

//-------------------------------------------------------------------------
// Osc1 Off-Mode
//-------------------------------------------------------------------------
Osc1_Off:
LDI   chanalA_0, 0x00				; osc1 out = 0
LDI   chanalA_1, 0x00				;
JMP  Osc2							; jmp osc2 calculation

//*************************************************************************
//   FM-Synthese
//
//   Oscillator 1
//   * 24-Bit Akku-Breite
//   * 24-Bit Phasen-Delta (2,384185mHz/Unit)
//   * 8-Bit Sample
//
//*************************************************************************

//-------------------------------------------------------------------------
// Osc1 FM-Modulator
// inc phaccu1 for osc1
//-------------------------------------------------------------------------
Osc1_FM:
LDS   delta0, phaccu_stepsize1+0	; 2   Phasen-Delta aus SRAM laden
LDS   delta1, phaccu_stepsize1+1	; 2
LDS   delta2, phaccu_stepsize1+2	; 2
LSR   delta2						; 1   Modulator:Carrier Ratio 0,5:1
ROR   delta1						; 1
ROR   delta0						; 1
LDS   phakku0, phaccu1+0			; 2   Phasen-Akku aus SRAM laden
LDS   phakku1, phaccu1+1			; 2
LDS   phakku2, phaccu1+2			; 2
SUB   phakku0, delta0				; 1   Phasen-Akku + Phasen-Delta
SBC   phakku1, delta1				; 1
SBC   phakku2, delta2				; 1
STS   phaccu1+0, phakku0			; 2   Phasen-Akku in SRAM zurückschreiben
STS   phaccu1+1, phakku1			; 2
STS   phaccu1+2, phakku2			; 2
//-------------------------------------------------------------------------
// load wavetable baseaddress for osc1
//-------------------------------------------------------------------------
LDS   R30, wavetable_dco1+0			; 1   Basis-Adresse Wavetable (Low-Byte)
LDS   R31, wavetable_dco1+1			; 1                           (High-Byte)
clr   R21
ANDI  phakku2, 0b01111111			; shruthi table (128 Byte)
//-------------------------------------------------------------------------
// Load Sample from Wavetable
//-------------------------------------------------------------------------
ADD   R30, phakku2
ADC   R31, R21
LPM   chanalA_0, Z+					; 3   Sample aus Wavetable laden (16-Bit)
LPM   chanalA_1, Z					; 3   => in MixerSumme
clr   chanalA_1
//-------------------------------------------------------------------------
// Osc2 FM-Carrier
// inc phaccu2 for osc2
// ------------------------------------------------------------------------
LDS   delta0, phaccu_stepsize2+0	; 2   Phasen-Delta aus SRAM laden
LDS   delta1, phaccu_stepsize2+1	; 2
LDS   delta2, phaccu_stepsize2+2	; 2

// Stärke 1 (aktuelle Stärke x1)
LSL   chanalA_0
ROL   chanalA_1
ADD   delta1, chanalA_0				; 1   FM-Modulator addieren (16)
ADC   delta2, chanalA_1				; 1
SUBI  delta2, 1 					; 1   FM-Mod-Nullpunkt korregieren

LDS   phakku0, phaccu2+0			; 2   Phasen-Akku aus SRAM laden
LDS   phakku1, phaccu2+1			; 2
LDS   phakku2, phaccu2+2			; 2

SUB   phakku0, delta0				; 1   Phasen-Akku + Phasen-Delta
SBC   phakku1, delta1				; 1
SBC   phakku2, delta2				; 1

STS   phaccu2+0, phakku0			; 2   Phasen-Akku in SRAM zurückschreiben
STS   phaccu2+1, phakku1			; 2
STS   phaccu2+2, phakku2			; 2
//-------------------------------------------------------------------------
// load wavetable baseaddress for osc2
//-------------------------------------------------------------------------
LDS   R30, wavetable_dco2+0			; 1   Basis-Adresse Wavetable (Low-Byte)
LDS   R31, wavetable_dco2+1			; 1                           (High-Byte)
clr   R21 
ANDI  phakku2, 0b01111111			; shruthi table (128 Byte)
//-------------------------------------------------------------------------
// Load Sample from Wavetable
//------------------------------------------------------------------------- 
ADD   R30, phakku2
ADC   R31, R21
LPM   chanalB_0, Z+					; 3   Sample aus Wavetable laden (16-Bit)
LPM   chanalB_1, Z					; 3
clr   chanalB_1
movw  chanalA_0, chanalB_0
JMP  SaveOscValue

//*************************************************************************
// SUB-Synthese
//*************************************************************************

//-------------------------------------------------------------------------
// inc phaccu1 for osc1
// ------------------------------------------------------------------------
Osc1_Mix:
LDS   delta0, phaccu_stepsize1+0	; 2   Phasen-Delta aus SRAM laden
LDS   delta1, phaccu_stepsize1+1	; 2
LDS   delta2, phaccu_stepsize1+2	; 2
LDS   phakku0, phaccu1+0			; 2   Phasen-Akku aus SRAM laden
LDS   phakku1, phaccu1+1			; 2
LDS   phakku2, phaccu1+2			; 2
SUB   phakku0, delta0				; 1   Phasen-Akku + Phasen-Delta
SBC   phakku1, delta1				; 1
SBC   phakku2, delta2				; 1
//-------------------------------------------------------------------------
// Osc1 Sync-Mode  (osc1 synchronize osc2)
//-------------------------------------------------------------------------
SyncOsc1:
BRCC  SavePhaccu1					; Phasenakku 1 läuft über Phase 0
LDS   R21, osc_mode					; Osc2Mode-Switches laden. Bit0 = Sync-Mode
SBRS  R21, 0						; Jump, wenn Bit1=1 "Sync" angewählt ist
RJMP  SavePhaccu1					; Jump, wenn nicht
CLR   r21
STS   phaccu2+0, r21				; phaccu2 (osc2) = 0
STS   phaccu2+1, r21
STS   phaccu2+2, r21 
//-------------------------------------------------------------------------
// save phaccu1
//-------------------------------------------------------------------------
SavePhaccu1:
STS   phaccu1+0, phakku0			; 2   Phasen-Akku in SRAM zurückschreiben
STS   phaccu1+1, phakku1			; 2
STS   phaccu1+2, phakku2			; 2
//-------------------------------------------------------------------------
// load wavetable baseaddress for osc1
//-------------------------------------------------------------------------
Osc1Wave:
LDS   R30, wavetable_dco1+0			; 1   Basis-Adresse Wavetable (Low-Byte)
LDS   R31, wavetable_dco1+1			; 1                           (High-Byte)
clr   R21
ANDI  phakku2, 0b01111111			; shruthi table (128 Byte)
//-------------------------------------------------------------------------
// Osc1 PWM-Mode
//-------------------------------------------------------------------------
Osc1PWM:
LDS   R18,pwm1_level
ldi   R19,128
sub   R19,R18
mov   R18,phakku2
CP    R18,R19
BRLO  Osc1Sample
ldi   phakku2,0x00

//-------------------------------------------------------------------------
// Load Sample from Wavetable
//-------------------------------------------------------------------------
Osc1Sample:
ADD   R30, phakku2
ADC   R31, R21
LPM   chanalA_0, Z+					; 3   Sample aus Wavetable laden (16-Bit)
LPM   chanalA_1, Z					; 3   => in MixerSumme
clr   chanalA_1
//-------------------------------------------------------------------------
//   Oscillator 2
//   * 24-Bit Akku-Breite
//   * 24-Bit Phasen-Delta (2,384185mHz/Unit)
//   * 8-Bit Sample
//-------------------------------------------------------------------------

//-------------------------------------------------------------------------
// Osc2 switch off
//-------------------------------------------------------------------------
Osc2:
LDS   R21, osc_mode
SBRS  R21, 4						; Bit2 = Osc1 off  
RJMP  Osc2Calc						; Sprung, wenn Osc2 on
LDI   chanalB_0, 0x00
LDI   chanalB_1, 0x00
RJMP  SaveOscValue

//-------------------------------------------------------------------------
// inc phaccu2 for osc2
// ------------------------------------------------------------------------
Osc2Calc:
LDS   delta0, phaccu_stepsize2+0	; 2   Phasen-Delta aus SRAM laden
LDS   delta1, phaccu_stepsize2+1	; 2
LDS   delta2, phaccu_stepsize2+2	; 2
LDS   phakku0, phaccu2+0			; 2   Phasen-Akku aus SRAM laden
LDS   phakku1, phaccu2+1			; 2
LDS   phakku2, phaccu2+2			; 2
SUB   phakku0, delta0				; 1   Phasen-Akku + Phasen-Delta
SBC   phakku1, delta1				; 1
SBC   phakku2, delta2				; 1
STS   phaccu2+0, phakku0			; 2   Phasen-Akku in SRAM zurückschreiben
STS   phaccu2+1, phakku1			; 2
STS   phaccu2+2, phakku2			; 2

//-------------------------------------------------------------------------
// load wavetable baseaddress for osc2
//-------------------------------------------------------------------------
OSC2Wave:
LDS   R30, wavetable_dco2+0			; 1   Basis-Adresse Wavetable (Low-Byte)
LDS   R31, wavetable_dco2+1			; 1                           (High-Byte)
clr   R21 
ANDI  phakku2, 0b01111111			; shruthi table (128 Byte)

//-------------------------------------------------------------------------
// Osc2 PWM-Mode
//-------------------------------------------------------------------------
Osc2PWM:
LDS   R18,pwm2_level
ldi   R19,128
sub   R19,R18
mov   R18,phakku2
CP    R18,R19
BRLO  Osc2Sample
ldi   phakku2,0x00

//-------------------------------------------------------------------------
// Load Sample from Wavetable
//-------------------------------------------------------------------------
Osc2Sample:  
ADD   R30, phakku2
ADC   R31, R21
LPM   chanalB_0, Z+					; 3   Sample aus Wavetable laden (16-Bit)
LPM   chanalB_1, Z					; 3
clr   chanalB_1

//-------------------------------------------------------------------------
// Ring Modulation
//-------------------------------------------------------------------------
OscRing:
LDS   R21, osc_mode					; load osc_mode
SBRS  R21, 1						; if Bit1 = 1 then Ring Mode  
RJMP  SaveOscValue					; jump if another mode
SUBI  chanalA_0, 0x80				; make signals signed
SUBI  chanalB_0, 0x80
MULS  chanalA_0, chanalB_0			; mul signed chanalA_1 * chanalB_1
mov   chanalA_0, r1					; result to chanalA_0
subi  chanalA_0, 0x80				; make signal unsigned

mov   chanalA_1, chanalA_0			; amplifying the signal 
lsr   chanalA_1
lsr   chanalA_1
add   chanalA_0, chanalA_1			; result to chanalA_0
mov   chanalB_0, chanalA_0			; result to chanalB_0

//sts    0x06E0, chanalA_0			; Result send to PortH

//-------------------------------------------------------------------------
// save osc1+2 values
//-------------------------------------------------------------------------
SaveOscValue:
osc1_temp = 20
osc2_temp = 21
mov  osc1_temp, chanalA_0
mov  osc2_temp, chanalB_0
    
//-------------------------------------------------------------------------
// osc1 level
//-------------------------------------------------------------------------
Osc1Level:
LDS   R18, osc1_value1 
MUL   R18, osc1_temp				; mul R18*temp_osc1 result in R0+R1
MOVW  R18, R0						; result in r0/r1 mov to r18/r19
// div 127
ANDI R18, 0xF0						; clr Low-Nibble from R18
SWAP R18							; SWAP High-Nibble to Low-Nibble
LSR  R18							; 3x shift right
LSR  R18
LSR  R18
LSL  R19							; R19 1x shift left
ADD  R18, R19						; ADD R18+R19
// set osc1 value to chanal_A
mov chanalA_0,R18
mov chanalA_1,R19

//-------------------------------------------------------------------------
// osc1 balance
//-------------------------------------------------------------------------
Osc1Balance:
LDS   R18, osc1_value2
MUL   R18, osc1_temp
MOVW  R18, R0
// div 127
ANDI R18, 0xF0						; clr Low-Nibble from R18
SWAP R18							; SWAP High-Nibble to Low-Nibble
LSR  R18							; 3x shift right
LSR  R18
LSR  R18
LSL  R19							; R19 1x shift left
ADD  R18, R19						; ADD R18+R19
// set osc1 balance to chanal_B
mov  chanalB_0,R18
mov  chanalB_1,R19

//-------------------------------------------------------------------------
// osc2 level
//-------------------------------------------------------------------------
Osc2Level:
LDS   R18, osc2_value1 
MUL   R18, osc2_temp				; mul R18+R19 result in R0+R1
MOVW  R18, R0						; move result to r18+r19
// div 127
ANDI R18, 0xF0						; clr Low-Nibble from R18
SWAP R18							; SWAP High-Nibble to Low-Nibble
LSR  R18							; 3x shift right
LSR  R18
LSR  R18
LSL  R19							; R19 1x shift left
ADD  R18, R19						; ADD R18+R19
// set osc2 value to chanal_A
ADD chanalA_0,R18
ADC chanalA_1,R19

//-------------------------------------------------------------------------
// osc2 balance
//-------------------------------------------------------------------------
Osc2Balance: 
LDS   R18, osc2_value2
MUL   R18, osc2_temp
MOVW  R18, R0
// div 127
ANDI R18, 0xF0						; clr Low-Nibble from R18
SWAP R18							; SWAP High-Nibble to Low-Nibble
LSR  R18							; 3x shift right
LSR  R18
LSR  R18
LSL  R19							; R19 1x shift left
ADD  R18, R19						; ADD R18+R19
// set oc2 balance to chanal_B
ADD  chanalB_0,R18
ADC  chanalB_1,R19

//-------------------------------------------------------------------------
// 24Bit noise generator
//-------------------------------------------------------------------------
NoiseGen:
LDS   PhaseA0, noise+0				; load noise register
LDS	  PhaseA1, noise+1				;
LDS	  PhaseA2, noise+2				;
BST   PhaseA0, 4					; Bit 4  (0:4) laden
BLD   R30, 0						;                                 
BST   PhaseA2, 7					; Bit 23 (2:7) laden
BLD   R31, 0						;                       
EOR   R30, R31						; XOR der beiden Bits
ROR   R30							; in Carry shiften
ROL   PhaseA0						; Buffer links schieben mit Carry
ROL   PhaseA1						;                                   
ROL   PhaseA2						;                                   
STS	noise+0, PhaseA0				; save noise register
STS	noise+1, PhaseA1				;
STS	noise+2, PhaseA2				;

//-------------------------------------------------------------------------
// set noise level left and balance
//-------------------------------------------------------------------------
NoiseLevLeft:
temp_noise  = 20					; temp-register R20
MOV   temp_noise, PhaseA0			; save noise to R20 
LDS   R18, noise_value1 
MUL   R18, temp_noise				; mul R18+R19 result in R0+R1
MOVW  R18, R0						; move result to r18+r19
// div 127
ANDI R18, 0xF0						; clr Low-Nibble from R18
SWAP R18							; SWAP High-Nibble to Low-Nibble
LSR  R18							; 3x shift right
LSR  R18
LSR  R18
LSL  R19							; R19 1x shift left
ADD  R18, R19						; ADD R18+R19
// add noise chanal_A
ADD  chanalA_0,R18
ADC  chanalA_1,R19

//-------------------------------------------------------------------------
// set noise level right and balance
//-------------------------------------------------------------------------
NoiseLevRight:
LDS   R18, noise_value2
MOV   R19, temp_noise				; load noise
MUL   R18, R19
MOVW  R18, R0
// div 127
ANDI R18, 0xF0						; clr Low-Nibble from R18
SWAP R18							; SWAP High-Nibble to Low-Nibble
LSR  R18							; 3x shift right
LSR  R18
LSR  R18
LSL  R19							; R19 1x shift left
ADD  R18, R19						; ADD R18+R19
// add noise chanal_B
ADD  chanalB_0,R18
ADC  chanalB_1,R19

//-------------------------------------------------------------------------
// 12Bit Dac Output
//-------------------------------------------------------------------------
DacOut:
// convert Chanal_A to 12Bit
LSL chanalA_0
ROL chanalA_1
LSL chanalA_0
ROL chanalA_1
LSL chanalA_0
ROL chanalA_1
// convert Chanal_B to 12Bit
LSL chanalB_0
ROL chanalB_1
LSL chanalB_0
ROL chanalB_1
LSL chanalB_0
ROL chanalB_1

//-------------------------------------------------------------------------
// Bit reduction (Bit crusher) and send signal to audio dac
//-------------------------------------------------------------------------
BitReduction:
Tmp2L = 20
Tmp2H = 21
lds  Tmp2L, Bitcrush+0				;load bitcrush value
lds  Tmp2H, Bitcrush+1
and	 chanalA_0, Tmp2L				;Mask out unused bits
and  chanalA_1, Tmp2H
sts  0x0318, chanalA_0				;send to audio DACA
sts  0x0319, chanalA_1
and	 chanalB_0, Tmp2L				;Mask out unused bits
and  chanalB_1, Tmp2H
sts  0x0338, chanalB_0				;send to audio DACB
sts  0x0339, chanalB_1

//-------------------------------------------------------------------------
// reload MCU-Register
//-------------------------------------------------------------------------
  POP   R31							; 2   R31 von Stack wiederherstellen (ZH)
  POP   R30							; 2   R30 von Stack wiederherstellen (ZL)
  POP   R25							; 2   R25 von Stack wiederherstellen
  POP   R24							; 2   R24 von Stack wiederherstellen
  POP   R23							; 2   R23 von Stack wiederherstellen
  POP   R22							; 2   R22 von Stack wiederherstellen
  POP   R21							; 2   R21 von Stack wiederherstellen
  POP   R20							; 2   R20 von Stack wiederherstellen
  POP   R19							; 2   R19 von Stack wiederherstellen
  POP   R18							; 2   R18 von Stack wiederherstellen
  POP   R17							; 2   R17 von Stack wiederherstellen
  POP   R16							; 2   R16 von Stack wiederherstellen
  POP   R1							; 2   R1  von Stack wiederherstellen
  POP   R0							; 2   Status-Register über R0 wieder herstellen
  OUT   SREG, R0					; 1                       
  POP   R0							; 2   R0  von Stack wiederherstellen
  sei
  RETI								; 4   Return Interrupt und I-Flag quittieren

// ------------------------------------------------------------------------
  .end

Link: DDS-Funktionsgenerators mit dem ATmega128
http://www.alice-dsl.net/sound-i...

Link: AVR-DDS signal generator in-line ASM explained
http://www.scienceprog.com/avr-d...

Assembler programming code for xmega chips:
http://www.avr-roboter.de/contro...

Greetings Rolf

Windows 10 Home 64Bit, ASUS M4A89GTD-PRO/USB3, AMD Phenom II X6 1055T, Ram 2x 4GB, SSD Samsung EVO840 250GB, SATA HD 2.0TB, NVIDIA GeForce GTX 750

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello rainblag,

I suggest you first get familiar with the DAC, how to set it up, put out a fixed voltage. Then you can put out a waveform in a loop and look at the result. If you didn't do any calculations before then the frequency of the output may be lower than you expected.

If we have a look at your budget, I assume you want to run the Xmega in 32MHz.

32e6/25e3 = 1280. You have 1280 clock cycles for each period of a 25kHz signal. If you had 1280 steps in your sine wave period you would need to put out a sample each clock cycle. But that is too fast for the DAC that is limited to 1MS/s (IIRC), so you can not put out more than one sample every 32 clock cycles. That will set a maximum of 40 samples per period of your signal.

Say we choose 20 samples for each period of the sine signal, then you need to put out a sample every 64 clock cycles.

For that you could use a timer with a 64 clock period and put out a sample on every overflow.

If you want to change the frequency with high resolution you could look at DDS technique, but you could do without it if you only need a fixed 25 kHz signal.

If you want to take a step further you could use the DMA to put out the samples for you.

Here's a little teaser ;-)

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello ,
Given the fact that they provided an analig filter circuit as tip I assume the imolementation should use a pwm sine generation. And that makes sense since pwm is a more common and generic solution to this problem. If it is an educational request it is vety likely that they require pwm solution. If that is the case then the theory is more impotant and the code is trivial. Let me know if you need expkanation.

Alex

There are 10 kinds of people... those who digg binary and those who don't

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Snigelen,

What you have on your scope looks interesting, can you guide me more on how you generate that sine wave? Thank you!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Alex(sagi2313),

I would like an explaination bout the pwm sine wave generation! I did some research on it, it seems like it generates pules that acts like a sine function, but my scope wouldnt read it as a sine wave right? what if it goes pass a sallen key low pass filter, will it be shaped into a sine wave? and how much frequency is needed for the pwm to generate a 25Khz sine wave?

Thank you!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Rolfdegen,

I have an error on :

#include "avr/io.h"

.extern Audio_irq

avr/io.h and .extern

- is there a header file that I must download ? Am using AVR studio 4

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Snigelen,

Okay I got what you meant! Researching on DAC now, found out that Amtel has some tutorials on it. Thank you very much, I will try to learn that and let you know how it goes!

Thank you!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To make a sine with a PWM, the PWM repetition frequency needs to be a lot higher than your sine frequency. Otherwise, the filtering won't work in a useful way. 10X may not be enough. That is, if you want a 20KHz sine from a PWM, you would like the PWM to have a repetition frequency (not clock frequency) of 200KHz or more.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When you use either a DAC, as shown below, or PWM, you often need to feed the signal through a LPF to get a "clean" signal. (It really depends upon what you are going to use the signal for.)

This image shows a DAC generated waveform, roughly a sin. (Ignore the pulse on the positive going zero crossing) It is made up of lots of "steps", each a specific voltage. The LPF removes the higher frequency components of the signal, (the steps with sharp edges), and one ends up with the expected clean sin wave.

JC

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you everyone for your help and reply.

#include 
#include 
#include "avr/dac_driver.h"
#include "avr/dma_driver.h"
// The board.h header file defines which IO ports peripherals like
// Switches and LEDs are connected to. The header file is configured
// for use with XMEGA-A1 Xplained.
#include "avr/board.h"

#define SINE_WAVE_LOW_RES  20
#define SINE_WAVE_HIGH_RES 40

#define DMA_CHANNEL_0  &DMA.CH0

#define DMA_REPEAT_FOREVER 0
// 2MHz, 20/40 Samples, dividend of 200 gives 500Hz/250Hz
#define TIMER_C0_PERIOD 200

uint16_t SineWaveLowRes [SINE_WAVE_LOW_RES] =
                        {
                            0x7FF, 0xA78, 0xCB2, 0xE77, 0xF9A,
                            0xFFF, 0xF9A, 0xE77, 0xCB2, 0xA78,
                            0x7FF, 0x586, 0x34C, 0x187, 0x064,
                            0x000, 0x064, 0x187, 0x34C, 0x586
                        };

uint16_t SineWaveHighRes[SINE_WAVE_HIGH_RES] =
                        {
                            0x7FF, 0x93F, 0xA78, 0xBA1, 0xCB2,
                            0xDA7, 0xE77, 0xF1F, 0xF9A, 0xFE5,
                            0xFFF, 0xFE5, 0xF9A, 0xF1F, 0xE77,
                            0xDA7, 0xCB2, 0xBA1, 0xA78, 0x93F,
                            0x7FF, 0x6BF, 0x586, 0x45D, 0x34C,
                            0x257, 0x187, 0x0DF, 0x064, 0x019,
                            0x000, 0x019, 0x064, 0x0DF, 0x187,
                            0x257, 0x34C, 0x45D, 0x586, 0x6BF
                        };

ISR(TCC0_OVF_vect){}

void DMA_Setup( DMA_CH_t * dmaChannel,
                const void * src,
                void * dest,
                int16_t blockSize,
                uint8_t repeatCount )
{
    DMA_Enable();


	DMA_SetupBlock(
                    dmaChannel,
                    src,
                    DMA_CH_SRCRELOAD_BLOCK_gc,
                    DMA_CH_SRCDIR_INC_gc,
                    dest,
                    DMA_CH_DESTRELOAD_BURST_gc,
                    DMA_CH_DESTDIR_INC_gc,
                    blockSize,
                    DMA_CH_BURSTLEN_2BYTE_gc,
                    repeatCount,
                    true
                   );

    // Timer Overflow will trigger DMA
    DMA_SetTriggerSource( dmaChannel, 0x40 );
    DMA_EnableSingleShot( dmaChannel );
}


int main( void )
{
	// First we have to enable the audio amplifier by setting PQ3 high.
	PORTQ.PIN3CTRL = (PORTQ.PIN3CTRL & ~PORT_OPC_gm) | PORT_OPC_PULLUP_gc;

	// Configure switches
	SWITCHPORTL.DIRCLR = 0x3F; // Set port as input

    PORTCFG.MPCMASK = 0x3F;
	SWITCHPORTL.PIN0CTRL = (SWITCHPORTL.PIN0CTRL & ~PORT_OPC_gm) | PORT_OPC_PULLUP_gc; //Enable pull-up to get a defined level on the switches
    PORTCFG.MPCMASK = 0x3F;
    SWITCHPORTL.PIN0CTRL |= PORT_INVEN_bm;	// Inverted keys.. pressed = 1

    // Set direction as output for LEDs
    LEDPORT.DIR = 0xFF;

    // Invert all pins on LED_PORT
    PORTCFG.MPCMASK = 0xFF;
    LEDPORT.PIN0CTRL |= PORT_INVEN_bm;

    // Enable overflow interrupt
    TCC0.INTCTRLA = ( TCC0.INTCTRLA & TC0_OVFINTLVL_gm ) | TC_OVFINTLVL_MED_gc;

	DMA_Setup(DMA_CHANNEL_0, SineWaveHighRes, (void *) &DACB.CH0DATA, SINE_WAVE_HIGH_RES * 2, DMA_REPEAT_FOREVER);


	DAC_SingleChannel_Enable(
                                &DACB,
	                            DAC_REFSEL_AVCC_gc,
	                            false
	                        );

    DMA_EnableChannel( DMA_CHANNEL_0 );

	// Enable medium interrupt level in PMIC and enable global interrupts.
	PMIC.CTRL |= PMIC_MEDLVLEN_bm;
    sei();

	while (1)
    {
        if(SWITCHPORTL.IN == 0x00)
        {
            // No Timer to trigger DMA: No Signal
             TCC0.CTRLA = ( TCC0.CTRLA & ~TC0_CLKSEL_gm ) | TC_CLKSEL_OFF_gc;

        }
        else
        {
            // Enable Timer C0, prescaler div1 means Main Clock (2MHz).
            TCC0.CTRLA = ( TCC0.CTRLA & ~TC0_CLKSEL_gm ) | TC_CLKSEL_DIV1_gc;

    	    if(SWITCHPORTL.IN != 0x00)
            {
                TCC0.PER = TIMER_C0_PERIOD;

                while(SWITCHPORTL.IN != 0x00)
                {
                    LEDPORT.OUTSET = 0xFF;
                }
            }
            LEDPORT.OUT = 0x00;
        }
    }
}

I have got this code up and builded with no errors from amtel's tutorial. But I cant figure out where is port B on the xmega128A1 xplained. It seems that portB doesnt have an output for me to scope. Can anyone help me with this.. thank you.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

PORTB pin 2 is connected to the onboard amplifier and speaker, see the schematic in this file.

You could change to DACA with it's output on PORTA which is available on header J2.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello Snigelen,

Do you also mean that I gotta chagne the DACB below to DACA?

DMA_Setup(DMA_CHANNEL_0, SineWaveHighRes, (void *) &DACB.CH0DATA, SINE_WAVE_HIGH_RES * 2, DMA_REPEAT_FOREVER); 


   DAC_SingleChannel_Enable( 
                                &DACB, 
                               DAC_REFSEL_AVCC_gc, 
                               false 
                           ); 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, if you want to use DACA instead of DACB you have to change all DACB to DACA.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

rolfdegen wrote:
Hallo rainblag

I think the best way for 25KHz Sinus Wave Generation is assembler code programming in Xmega Chip. Its simple. You can mixing code in c and assembler. Programming inits for timer and another in c and calc sinus wave and dac-output in assembler. See my code. Its dds in my synth. I programming a lot of funktion in c and calc Waves in assembler.

Hi,
do you use avr studio 6? I am using studio 4. Is there a way to mix c with assembler in studio 4?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Snigelen,
I still cant get an output after changing it.. :(

anyone kind enough to share with me an example code that I can work on?? thank you guys..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think you should take one step at a time. You need to learn about timers, maybe PWM or DAC. DMA is the very last point in the list and you can get a working solution without using it.

Something like this.

- Setup a timer to overflow at a desired rate. Poll the overflow flag and flash a LED when it happens (and clear the flag). Maybe flash the LED in an ISR.

- Configure the DAC. Put out a fixed value and see that you get what you expect. Change to another value. Put out values from an array in a loop with a delay in it.

- Combine the two things above to put out a DAC value when a timer overflows.

- Try to use PWM instead of the DAC. Investigate PWM by itself first, then combine with the overflow of another timer.

And you can look at application notes and try to run the examples from them. When they run as expected do some modifications.

Then you are almost done. Investigate and use the event system if you like, add DMA if you like, but you can probably do without them.

You asked in a PM for the code that generated the oscilloscope image I posted. Well I can post it. But it is for a XmegaE5. They are a little different from other Xmegas so you will need to change almost every line to port it to a Xmega128a1. My program uses a EDMA ISR that is not needed on the other Xmegas, since they can do unlimited repeats and the E5 can only do one repeat.

So do not look at it now. Work through the steps above first.

/*
  Testing DAC and EDMA on XmegaE5.
 */

#include 
#include 

#define FREQ 25000

#define WAVEFORM sine

const uint8_t sine[20] = {128,167,202,231,249,255,249,231,202,
                          167,128,88,53,24,6,0,6,24,53,88};

// Missing define
#define EDMA_CH_TRNINTLVL_LO_bm EDMA_CH_TRNINTLVL0_bm

// Restart DMA transfer since EDMA only can do ONE repeat.
ISR(EDMA_CH0_vect)
{
    // CLEAR INTERRUPT FLAG
    EDMA.CH0.CTRLB = EDMA_CH_TRNIF_bm | EDMA_CH_TRNINTLVL_LO_bm;
    EDMA.CH0.CTRLA = EDMA_CH_ENABLE_bm | EDMA_CH_REPEAT_bm | EDMA_CH_SINGLE_bm;
}

void clock_32MHz_RC(void)
{
    // Enable 32MHz internal RC oscillator
    OSC.CTRL |= OSC_RC32MEN_bm;
    // Wait until it's stable
    while ((OSC.STATUS & OSC_RC32MRDY_bm) == 0)
        ;
    // Select 32MHz RC as clock source, unlock first
    CCP = CCP_IOREG_gc;
    CLK.CTRL = CLK_SCLKSEL_RC32M_gc;
    // Disable 2MHz RC-oscillator
    OSC.CTRL &= ~OSC_RC2MEN_bm;
}

int main(void)
{
    clock_32MHz_RC();

    // DACA channel 0
    PORTA.DIRSET = PIN2_bm;
    DACA.CTRLA = DAC_CH0EN_bm | DAC_ENABLE_bm;
    DACA.CTRLB = DAC_CHSEL_SINGLE_gc;
    DACA.CTRLC = DAC_REFSEL_AVCC_gc | DAC_LEFTADJ_bm;

    // EDMA transfer waveform to DACA.CH0DATAH
    // One standard, two  peripheral channels, no double buffering,
    // round robin and enable
    EDMA.CTRL = EDMA_CHMODE_STD0_gc | EDMA_DBUFMODE_DISABLE_gc |
        EDMA_CHMODE_PER0123_gc | EDMA_ENABLE_bm;

    // Src address
    EDMA.CH0.ADDRL = ((uint16_t) WAVEFORM) & 0xFF;
    EDMA.CH0.ADDRH = ((uint16_t) WAVEFORM) >> 8;
    EDMA.CH0.TRFCNTL = sizeof(WAVEFORM) & 0xFF;
    EDMA.CH0.TRFCNTH = sizeof(WAVEFORM) >> 8;
    // SRAM addr. ctrl.
    EDMA.CH0.ADDRCTRL = EDMA_CH_RELOAD_BLOCK_gc | EDMA_CH_DIR_INC_gc;
    // Dest
    EDMA.CH0.DESTADDRL = ((uint16_t) &DACA.CH0DATAH) & 0xFF;
    EDMA.CH0.DESTADDRH = ((uint16_t) &DACA.CH0DATAH) >> 8;

    // Trig on event channel 0
    EDMA.CH0.TRIGSRC = EDMA_CH_TRIGSRC_EVSYS_CH0_gc;
    
    // Transfer complete interrupt low level and clear interrupt flag
    EDMA.CH0.CTRLB = EDMA_CH_TRNIF_bm | EDMA_CH_TRNINTLVL_LO_bm;
    // Enable, repeat, single. Default 1 byte burst length.
    EDMA.CH0.CTRLA = EDMA_CH_ENABLE_bm | EDMA_CH_REPEAT_bm | EDMA_CH_SINGLE_bm;


    // Trig-Timer generate overflow event on event channel 0.
    TCD5.PER = F_CPU/sizeof(WAVEFORM)/FREQ - 1;
    TCD5.CTRLA = TC45_CLKSEL_DIV1_gc;
    EVSYS.CH0MUX = EVSYS_CHMUX_TCD5_OVF_gc;

    // Enable low level interrupts
    PMIC_CTRL |= PMIC_LOLVLEN_bm;
    sei();
    
    while(1)
    {
    }
}
===============================================================================
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

rainblag,

I just tested the program you posted.

I cut out everything with switches and LEDs and replaced DACB with DACA, moved the timer setup to before the now empty while loop just to see if I could get a signal.

A nice 250Hz sinewave appeared on PORTA2 :-)

Btw, did you want 250Hz or 25kHz? That's a big difference.

Here's your code with the modifications I made.

#include 
#include 
#include "avr/dac_driver.h"
#include "avr/dma_driver.h"

#define SINE_WAVE_LOW_RES  20
#define SINE_WAVE_HIGH_RES 40

#define DMA_CHANNEL_0  &DMA.CH0

#define DMA_REPEAT_FOREVER 0
// 2MHz, 20/40 Samples, dividend of 200 gives 500Hz/250Hz
#define TIMER_C0_PERIOD 200

uint16_t SineWaveLowRes [SINE_WAVE_LOW_RES] =
{
    0x7FF, 0xA78, 0xCB2, 0xE77, 0xF9A,
    0xFFF, 0xF9A, 0xE77, 0xCB2, 0xA78,
    0x7FF, 0x586, 0x34C, 0x187, 0x064,
    0x000, 0x064, 0x187, 0x34C, 0x586
};

uint16_t SineWaveHighRes[SINE_WAVE_HIGH_RES] =
{
    0x7FF, 0x93F, 0xA78, 0xBA1, 0xCB2,
    0xDA7, 0xE77, 0xF1F, 0xF9A, 0xFE5,
    0xFFF, 0xFE5, 0xF9A, 0xF1F, 0xE77,
    0xDA7, 0xCB2, 0xBA1, 0xA78, 0x93F,
    0x7FF, 0x6BF, 0x586, 0x45D, 0x34C,
    0x257, 0x187, 0x0DF, 0x064, 0x019,
    0x000, 0x019, 0x064, 0x0DF, 0x187,
    0x257, 0x34C, 0x45D, 0x586, 0x6BF
};

ISR(TCC0_OVF_vect){}

void DMA_Setup( DMA_CH_t * dmaChannel,
                const void * src,
                void * dest,
                int16_t blockSize,
                uint8_t repeatCount )
{
    DMA_Enable();


    DMA_SetupBlock(
        dmaChannel,
        src,
        DMA_CH_SRCRELOAD_BLOCK_gc,
        DMA_CH_SRCDIR_INC_gc,
        dest,
        DMA_CH_DESTRELOAD_BURST_gc,
        DMA_CH_DESTDIR_INC_gc,
        blockSize,
        DMA_CH_BURSTLEN_2BYTE_gc,
        repeatCount,
        true
        );

    // Timer Overflow will trigger DMA
    DMA_SetTriggerSource( dmaChannel, 0x40 );
    DMA_EnableSingleShot( dmaChannel );
}


int main( void )
{
    // First we have to enable the audio amplifier by setting PQ3 high.
    PORTQ.PIN3CTRL = (PORTQ.PIN3CTRL & ~PORT_OPC_gm) | PORT_OPC_PULLUP_gc;

    // Enable overflow interrupt
    TCC0.INTCTRLA = ( TCC0.INTCTRLA & TC0_OVFINTLVL_gm ) | TC_OVFINTLVL_MED_gc;

    DMA_Setup(DMA_CHANNEL_0, SineWaveHighRes, (void *) &DACA.CH0DATA, SINE_WAVE_HIGH_RES * 2, DMA_REPEAT_FOREVER);

   
    DAC_SingleChannel_Enable(
        &DACA,
        DAC_REFSEL_AVCC_gc,
        false
        );

    DMA_EnableChannel( DMA_CHANNEL_0 );

    // Enable medium interrupt level in PMIC and enable global interrupts.
    PMIC.CTRL |= PMIC_MEDLVLEN_bm;
    sei();

    TCC0.PER = TIMER_C0_PERIOD;
    // Enable Timer C0, prescaler div1 means Main Clock (2MHz).
    TCC0.CTRLA = ( TCC0.CTRLA & ~TC0_CLKSEL_gm ) | TC_CLKSEL_DIV1_gc;

    while (1)
    {
    }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for trying Snigelen, you were really helpful! just to confirm again, portA2 is the port that is labelled ADC2 on the board right?

I cant seem to get any waveform from it. :(

yes I wanted to get a 25kHz sine wave, I was wondering if I could get this up, editing the codes to get a 25Khz should not be that difficult. Or am I wrong? correct me if I am.. Not really sure bout this. Please help, thank you!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

the output looks almost like a Sinc Snigelen, there seemed to be a lot of noise and the waveform is really small.. or is my waveform frequency too small to be scoped ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's strange. I get this waveform on PORTA2 on a xmega128a1 (not a xplained board).

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

rainblag wrote:
yes I wanted to get a 25kHz sine wave, I was wondering if I could get this up, editing the codes to get a 25Khz should not be that difficult. Or am I wrong?
You can change it to make a 25kHz sine. But then you probably want to run the Xmega in 32MHz. And let your timer generate an event instead of a overflow interrupt that can trigger the DMA.

But first you should get the 250Hz signal working before you change the timer and DMA setup.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mine is an xplained board. And yes the PortA2 on my board should also be the output for DAC, its written as ADC though, or have I mistaken bout it ?

this is exactly how my board looks like.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

this is the dac_driver.c and dma_driver.c that I am using. Please help me check whether is it the same as yours.

dac_driver.c

#include "avr/dac_driver.h"


/*! \brief Enable Singe Channel.
 *
 *  This function configures the DAC to enable channel 0 output only,
 *  in single channel operation. It apply configuration parameters, save old
 *  values, clear affected bit field and set appropriate values.
 *
 *  \param  dac         Pointer to DAC module register section.
 *  \param  convRef     Selects reference voltage for all conversions.
 *  \param  leftAdjust  Set to true to make data registers left adjusted.
 */
void DAC_SingleChannel_Enable( volatile DAC_t * dac,
                              DAC_REFSEL_t convRef,
                              bool leftAdjust )
{
	dac->CTRLB = ( dac->CTRLB & ~DAC_CHSEL_gm ) | DAC_CHSEL_SINGLE_gc;	
	dac->CTRLC = ( dac->CTRLC & ~( DAC_REFSEL_gm | DAC_LEFTADJ_bm ) ) |
	             convRef |
	             ( leftAdjust ? DAC_LEFTADJ_bm : 0x00 );
	dac->CTRLA = ( dac->CTRLA & ~DAC_CH1EN_bm ) |
	             DAC_CH0EN_bm |
	             DAC_ENABLE_bm;
}


/*! \brief Enable Dual Channel.
 *
 *  This function configures the DAC to enable both channel outputs,
 *  in dual channel operation. It apply configuration parameters, save old
 *  values, clear affected bit field and set appropriate values.
 *
 *  \param  dac              Pointer to DAC module register section.
 *  \param  convRef          Selects reference voltage for all conversions.
 *  \param  leftAdjust       Set to true to make data registers left adjusted.
 *  \param  sampleInterval   Interval between refreshing channel A and B.
 *  \param  refreshInterval  Interval between refresh cycles.
 */
void DAC_DualChannel_Enable( volatile DAC_t * dac,
                            DAC_REFSEL_t convRef,
                            bool leftAdjust,
                            DAC_CONINTVAL_t sampleInterval,
                            DAC_REFRESH_t refreshInterval )
{
	dac->CTRLB = ( dac->CTRLB & ~DAC_CHSEL_gm ) | DAC_CHSEL_DUAL_gc;
	dac->CTRLC = ( dac->CTRLC & ~( DAC_REFSEL_gm | DAC_LEFTADJ_bm ) ) |
	             convRef |
	             ( leftAdjust ? DAC_LEFTADJ_bm : 0x00 );
	dac->TIMCTRL = (uint8_t) sampleInterval | refreshInterval;
	dac->CTRLA |= DAC_CH1EN_bm | DAC_CH0EN_bm | DAC_ENABLE_bm;
}


/*! \brief  Write data to selected channel.
 *
 *  This function writes data to the selected channel data register. The program
 *  should wait for the data register to be ready if necessary. This ensures
 *  that no intermediate values are lost with very high update rates.
 *
 *  \note  The data must be in the format currently configured for the DAC,
 *         right or left adjusted.
 *
 *  \param  dac     Pointer to DAC module register section.
 *  \param  data    Data to be converted.
 *  \param  channel Selected channel in the DAC module, either CH0 or CH1.
 */
void DAC_Channel_Write( volatile DAC_t * dac, uint16_t data, DAC_CH_t channel )
{
	if ( channel == CH0 ) {
		dac->CH0DATA = data;
	} else if( channel == CH1 ) {
		dac->CH1DATA = data;
	}
}


/*! \brief Check if channel data register is empty.
 *
 *  This function return the status of the data register empty flag for
 *  the selected channel in the given DAC module. This can be used to get the
 *  status of the register before writing a new value to it.
 *
 *  \param  dac     Pointer to DAC module register section.
 *  \param  channel Selected channel in the DAC module, either CH0 or CH1.
 *
 *  \retval dacStatus True if data register is empty.
 *  \retval dacStatus False if data register is not empty.
 */
bool DAC_Channel_DataEmpty( volatile DAC_t * dac, DAC_CH_t channel )
{
	bool dacStatus = ( dac->STATUS &
	                 ( channel ? DAC_CH1DRE_bm : DAC_CH0DRE_bm ));
	return dacStatus;
}


/*! \brief Configure event actions.
 *
 *  This function configures the event action for the DAC module. It clears
 *  both control bits and set required bits.
 *
 *  \note  There is no checking if the event line number is valid. The value
 *         is simply truncated to fit the bit field in the corresponding register.
 *
 *  \param  dac         Pointer to DAC module register section.
 *  \param  trigChannel The channels to be triggered by events. Values can be
 *                      DAC_TRIG_0_0, DAC_TRIG_0_1, DAC_TRIG_1_0 or DAC_TRIG_1_1.
 *  \param  eventLine   Event line (0..7) to use for triggering conversions.
 */
void DAC_EventAction_Set( volatile DAC_t * dac,
                          DAC_TRIG_t trigChannel,
                          uint8_t eventLine )
{
	dac->CTRLB = ( dac->CTRLB & ~DAC_TRIG_1_1 ) | trigChannel;
	dac->EVCTRL = eventLine & DAC_EVSEL_gm;
}

dma_driver.c

#include "avr/dma_driver.h"


/*! \brief This function configures the double buffering feature of the DMA.
 *
 *  Channel pair 0/1 and/or channel pair 2/3 can
 *  be configured to operation in a chained mode. This means that
 *  once the first channel has completed its transfer, the second
 *  channel takes over automatically. It is important to setup the
 *  channel pair with equal block sizes, repeat modes etc.
 *
 *  Do not change these settings after a transfer has started.
 *
 *  \param  dbufMode  Double buffering mode.
 */
void DMA_ConfigDoubleBuffering( DMA_DBUFMODE_t dbufMode )
{
	DMA.CTRL = ( DMA.CTRL & ~DMA_DBUFMODE_gm ) | dbufMode;
}


/*! \brief This function selects what priority scheme to use for the DMA channels.
 *
 *  It decides what channels to schedule in a round-robin
 *  manner, which means that they take turns in acquiring the data bus
 *  for individual data transfers. Channels not included in the round-robin
 *  scheme will have fixed priorities, with channel 0 having highest priority.
 *
 *  \note  Do not change these settings after a transfer has started.
 *
 *  \param  priMode  An enum selection the priority scheme to use.
 */
void DMA_SetPriority( DMA_PRIMODE_t priMode )
{
	DMA.CTRL = ( DMA.CTRL & ~DMA_PRIMODE_gm ) | priMode;
}


/*! \brief This function checks if the channel has on-going transfers not
 *         finished yet.
 *
 *  \param  channel Channel to check.
 *
 *  \return  Non-zero if the selected channel have on-going transfers,
 *           zero otherwise.
 */
uint8_t DMA_CH_IsOngoing( volatile DMA_CH_t * channel )
{
	uint8_t flagMask;
	flagMask = channel->CTRLB & DMA_CH_CHBUSY_bm;
	return flagMask;
}

/*! \brief This function checks if any channel have on-going transfers are not
 *         finished yet.
 *
 *  \return  Non-zero if any channel have on-going transfers, zero otherwise.
 */
uint8_t DMA_IsOngoing( void )
{
	uint8_t flagMask;
	flagMask = DMA.STATUS & 0xF0;
	return flagMask;
}

/*! \brief This function check if the channel has transfers pending.
 *
 *  This function checks if the channel selected have transfers that are
 *  pending, which means that a transfer has been requested by a trigger source
 *  or by a manual request, but the channel haven't yet started its transfer.
 *
 *  \param  channel Channel to check.
 *
 *  \return  Non-zero if the selected channel have pending transfers,
 *           zero otherwise.
 */
uint8_t DMA_CH_IsPending( volatile DMA_CH_t * channel )
{
	uint8_t flagMask;
	flagMask = channel->CTRLB & DMA_CH_CHPEND_bm;
	return flagMask;
}


/*! \brief This function check if there are any transfers pending.
 *
 *  This function checks if any channel have transfers that are pending,
 *  which means that a transfer has been requested by a trigger source
 *  or by a manual request, but the channels haven't yet started its transfer.
 *
 *  \return  Non-zero if the selected channel have pending transfers,
 *           zero otherwise.
 */
uint8_t DMA_IsPending( void )
{
	uint8_t flagMask;
	flagMask = DMA.STATUS & 0x0F;
	return flagMask;
}

/*! \brief This function return the interrupt flag status of a channel.
 *
 *  This function return the status the channels selected finishes an on-going
 *  transfer or encounters an error and aborts the transfer.
 *
 *  \note  Flags covered by the channel will NOT be cleared when this
 *         function exits.
 *
 *  \param  channel  The channel to check.
 *
 *  \return  Relevant interrupt flags.
 */
uint8_t DMA_ReturnStatus_non_blocking( volatile DMA_CH_t * channel )
{
	uint8_t relevantFlags;
	relevantFlags = channel->CTRLB & (DMA_CH_ERRIF_bm | DMA_CH_TRNIF_bm);
	return relevantFlags;
}


/*! \brief This function return the interrupt flag status of a channel.
 *
 *  This function return the status of the channel selected either finishes
 *  an on-going transfer or encounters an error and aborts the transfer.
 *
 *  \note  Flags covered by the channel will be cleared when this
 *         function exits. However, it will return the flag status. This
 *         is a BLOCKING function, and will go into a dead-lock if the flags
 *         never get set.
 *
 *  \param  channel  The channel to check.
 *
 *  \return  Relevant interrupt flags.
 */
uint8_t DMA_ReturnStatus_blocking( volatile DMA_CH_t * channel )
{
	uint8_t flagMask;
	uint8_t relevantFlags;

	flagMask = DMA_CH_ERRIF_bm | DMA_CH_TRNIF_bm;

	do {
		relevantFlags = channel->CTRLB & flagMask;
	} while (relevantFlags == 0x00);

	channel->CTRLB = flagMask;
	return relevantFlags;
}

/*! \brief This function enables one DMA channel sub module.
 *
 *  \note A DMA channel will be automatically disabled
 *        when a transfer is finished.
 *
 *  \param  channel  The channel to enable.
 */
void DMA_EnableChannel( volatile DMA_CH_t * channel )
{
	channel->CTRLA |= DMA_CH_ENABLE_bm;
}


/*! \brief This function disables one DMA channel sub module.
 *
 *  \note On-going transfers will be aborted and the error flag be set if a
 *        channel is disabled in the middle of a transfer.
 *
 *  \param  channel  The channel to disable.
 */
void DMA_DisableChannel( volatile DMA_CH_t * channel )
{
	channel->CTRLA &= ~DMA_CH_ENABLE_bm;
}


/*! \brief This function forces a software reset of the DMA channel sub module.
 *
 *  All registers will be set to their default values. If the channel
 *  is enabled, it must and will be disabled before being reset.
 *  It will not be enabled afterwards.
 *
 *  \param  channel  The channel to reset.
 */
void DMA_ResetChannel( volatile DMA_CH_t * channel )
{
	channel->CTRLA &= ~DMA_CH_ENABLE_bm;
	channel->CTRLA |= DMA_CH_RESET_bm;
	channel->CTRLA &= ~DMA_CH_RESET_bm;
}


/*! \brief This function configures the interrupt levels for one DMA channel.
 *
 *  \note  The interrupt level parameter use the data type for channel 0,
 *         regardless of which channel is used. This is because we use the
 *         same function for all channels. This method relies upon channel
 *         bit fields to be located this way: CH3:CH2:CH1:CH0.
 *
 *  \param  channel      The channel to configure.
 *  \param  transferInt  Transfer Complete Interrupt Level.
 *  \param  errorInt     Transfer Error Interrupt Level.
 */
void DMA_SetIntLevel( volatile DMA_CH_t * channel,
                      DMA_CH_TRNINTLVL_t transferInt,
                      DMA_CH_ERRINTLVL_t errorInt )
{
	channel->CTRLB = (channel->CTRLB & ~(DMA_CH_ERRINTLVL_gm | DMA_CH_TRNINTLVL_gm)) |
			 transferInt | errorInt;
}


/*! \brief This function configures the necessary registers for a block transfer.
 *
 *  \note The transfer must be manually triggered or a trigger source
 *        selected before the transfer starts. It is possible to reload the
 *        source and/or destination address after each data transfer, block
 *        transfer or only when the entire transfer is complete.
 *        Do not change these settings after a transfer has started.
 *
 *  \param  channel        The channel to configure.
 *  \param  srcAddr        Source memory address.
 *  \param  srcReload      Source address reload mode.
 *  \param  srcDirection   Source address direction (fixed, increment, or decrement).
 *  \param  destAddr       Destination memory address.
 *  \param  destReload     Destination address reload mode.
 *  \param  destDirection  Destination address direction (fixed, increment, or decrement).
 *  \param  blockSize      Block size in number of bytes (0 = 64k).
 *  \param  burstMode      Number of bytes per data transfer (1, 2, 4, or 8 bytes).
 *  \param  repeatCount    Number of blocks, 0x00 if you want to repeat at infinitum.
 *  \param  useRepeat      True if reapeat should be used, false if not.
 */
void DMA_SetupBlock( DMA_CH_t * channel,
                     const void * srcAddr,
                     DMA_CH_SRCRELOAD_t srcReload,
                     DMA_CH_SRCDIR_t srcDirection,
                     void * destAddr,
                     DMA_CH_DESTRELOAD_t destReload,
                     DMA_CH_DESTDIR_t destDirection,
                     uint16_t blockSize,
                     DMA_CH_BURSTLEN_t burstMode,
                     uint8_t repeatCount,
                     bool useRepeat )
{
	channel->SRCADDR0 = (( (uint16_t) srcAddr) >> 0*8 ) & 0xFF;
	channel->SRCADDR1 = (( (uint16_t) srcAddr) >> 1*8 ) & 0xFF;
	channel->SRCADDR2 = 0;

	channel->DESTADDR0 = (( (uint16_t) destAddr) >> 0*8 ) & 0xFF;
	channel->DESTADDR1 = (( (uint16_t) destAddr) >> 1*8 ) & 0xFF;
	channel->DESTADDR2 = 0;

	channel->ADDRCTRL = (uint8_t) srcReload | srcDirection |
	                              destReload | destDirection;
	channel->TRFCNT = blockSize;
	channel->CTRLA = ( channel->CTRLA & ~( DMA_CH_BURSTLEN_gm | DMA_CH_REPEAT_bm ) ) |
	                  burstMode | ( useRepeat ? DMA_CH_REPEAT_bm : 0);

	if ( useRepeat ) {
		channel->REPCNT = repeatCount;
	}
}




/*! \brief This function enables single-shot transfer mode for a channel.
 *
 *  In single-shot mode, one transfer trigger (manual or from a trigger source)
 *  only causes one single data transfer (1, 2, 4, or 8 byte). When not
 *  in single-shot mode, one transfer trigger causes one entire block transfer.
 *  Do not change this setting after a transfer has started.
 *
 *  \param  channel  The channel to configure.
 */
void DMA_EnableSingleShot( volatile DMA_CH_t * channel )
{
	channel->CTRLA |= DMA_CH_SINGLE_bm;
}


/*! \brief This function disables single-shot transfer mode for a channel.
 *
 *  In single-shot mode, one transfer trigger (manual or from a trigger source)
 *  only causes one single data transfer (1, 2, 4, or 8 byte). When not
 *  in single-shot mode, one transfer trigger causes one entire block transfer.
 *
 *  Do not change this setting after a transfer has started.
 *
 *  \param  channel  The channel to configure.
 */
void DMA_DisableSingleShot( volatile DMA_CH_t * channel )
{
	channel->CTRLA &= ~DMA_CH_SINGLE_bm;
}


/*! \brief This function sets the transfer trigger source for a channel.
 *
 *  \note A manual transfer requests can be used even after setting a trigger
 *        source. Do not change this setting after a transfer has started.
 *
 *  \param  channel  The channel to configure.
 *  \param  trigger  The trigger source ID.
 */
void DMA_SetTriggerSource( volatile DMA_CH_t * channel, uint8_t trigger )
{
	channel->TRIGSRC = trigger;
}


/*! \brief This function sends a manual transfer request to the channel.
 *
 *  The bit will automatically clear when transfer starts.
 *
 *  \param  channel  The channel to request a transfer for.
 */
void DMA_StartTransfer( volatile DMA_CH_t * channel )
{
	channel->CTRLA |= DMA_CH_TRFREQ_bm;
}

Thanks Snigelen!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I can't see why it shouldn't work on the A1 XPLAINED. PORTA2 is pin3 on header J2. Are you sure you look at that pin?

I just tried it on a A3BU XPLAINED board. It works fine on that too (after changing back to DACB).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I use dac_driver.[ch] from application note 1301. This is the version in the leading comment block you removed(?).

 * $Revision: 1646 $
 * $Date: 2008-05-14 16:04:55 +0200 (on, 14 mai 2008) $  \n

dma_driver from application note 1502. This version

 * $Revision: 1569 $
 * $Date: 2008-04-22 13:03:43 +0200 (ti, 22 apr 2008) $  \n

They look like the files you use.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yea, i actually scoped all the pins on port A, no output for me. My friend is using a A3BU xplained board. I got him to try the code, there is an error which says that PORTQ is unidentified. :(

So close yet so far..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yea, its the same, except for dac_driver i was using version 1569 too from AVR1520. I am using AVR studio 4 and I include those driver as source file, am I doing it correctly? It should be since build was successful without errors. Or am I wrong.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The PORTQ line can be removed. It's only there to activate the Audio amplifier connected to DACB on the A1 XPLAINED BOARD. And you don't use it.

I guess it should work with Studio4 for your xmega128a1. I think it was the current studio version when those drivers were written.

Your friend with the A3BU XPLAINED may need something newer.

I use Atmel Toolchain 3.4.2 under Linux, it's the same as in one of the latest Atmel Studio 6 versions.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I still cant get anything. Its very depressing.

Is it possible that the frequency is too small to be measured? I see very small waves over here and it is square.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With your program I modified it should be 250Hz with about 3V peak to peak which is easy to measure. If you change back to DACB (and keep the PORTQ line) you should hear the 250Hz sine in the speaker.

Maybe someone else here have a A1 XPLAINED board and could give it a try?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's a .zip with the files I used if someone want to give it a try. It should generate a 250Hz sine on PORTA2. Some changes from the previous version:

- The dma and dac driver files in the same directory (not avr subdir)
- Added a define to choose DACA or DACB
- Let the timer generate overflow events that triggers a DMA block transfer (instead of overflow interrupt).
- The PORTQ line to activate the A1 XPLAINED amplifier removed.

It includes a precompiled ATXmega128a1.hex you can test rainblag.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you so much Snigelen, I am out of the lab, will try soon and let you know how it goes, I will probably try it with another board I have. Mayb this one is fried.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

its indeed fried, now the code is working on my replacement board. Thanks Snigelen!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've managed to generate a 25Khz sinewave already! :D I wanna thank all those who replied and helped with this thread. Special thanks for Snigelen for what he has taught me, with clear explainations and his effort in guiding me with all the replies. :)