View previous topic :: View next topic |
Author |
Message |
Inventor-George
Joined: 28 Mar 2006 Posts: 74
|
Posted: Fri Feb 10, 2017 1:40 am Post subject: clock cycles per instruction? |
|
|
I must calculate timing for parts of my code, but to do that, I need to know how many clock cycles each BASCOM statement uses.
Is there a list somewhere that gives all statements, and the clock cycles required?
I would most appreciate such a list.
(BASCOM-AVR version : 2.0.7.8 ) |
|
Back to top |
|
|
albertsm
Joined: 09 Apr 2004 Posts: 5913 Location: Holland
|
Posted: Fri Feb 10, 2017 9:29 am Post subject: |
|
|
there is no list. a lot of statements cycles depend on the context.
like : print somevar
it will matter what data type is.
then it also matters what kind of micro is used.
so there is no list, nor will there be one. use the simulator to count the cycles if it is that important.
of toggle some pin and measure with at scope. _________________ Mark |
|
Back to top |
|
|
JC
Joined: 15 Dec 2007 Posts: 585 Location: Cleveland, OH
|
Posted: Sat Feb 11, 2017 1:27 am Post subject: |
|
|
Quote: | I must calculate timing for parts of my code |
It would help if you stated more specifically what you are trying to do.
One can make known delays with the WaitmS and WaituS commands, which are approximates.
Specific timing, either delays or measurements, are usually done using one of the Timer/Counter modules.
Short time delays can be created by inserting an Assembly NOP instruction, which takes 1 clock cycle.
(A 1 MHz clock gives 1 uSec per clock cycle, a 16 MHz clock gives 1/16 uSec per clock cycle...)
JC |
|
Back to top |
|
|
Inventor-George
Joined: 28 Mar 2006 Posts: 74
|
Posted: Sat Feb 11, 2017 8:54 pm Post subject: |
|
|
I guess I was rather vague in my description of what I need.
I am using a DDS approach to generate a sin wave. I need the range of frequencies to vary from 50Hz to about 10KHz.
Using an AXMEGA128A4U, I need to calculate how many samples I can generate per cycle at the top end of the range [10KHz].
I also need to know what clock frequency I will need to run the uP at to give me a reasonable approximation of a sin wave.
I could use a low-pass filter to help smooth out the sin wave at all frequencies, but it is mostly at the higher frequencies that I need to optimize this. |
|
Back to top |
|
|
MWS
Joined: 22 Aug 2009 Posts: 2262
|
Posted: Sat Feb 11, 2017 10:18 pm Post subject: |
|
|
The upper frequency limit obviously is a function of ISR-cycles and sine support points.
To find out, do a simulation, zero cycles by right click at the bottom status line on Cycles -> Clear Cycles, then trigger the appropriate interrupt.
You need a break point somewhere within the ISR, otherwise the simulation will rush through.
After the break point single-step through the rest of the ISR till it returns, the cycles display shows used up cycles.
Different ways of using setting break points surely work too. |
|
Back to top |
|
|
JC
Joined: 15 Dec 2007 Posts: 585 Location: Cleveland, OH
|
Posted: Sun Feb 12, 2017 4:07 am Post subject: |
|
|
Cool project!
Have you written your DDS routine yet?
When I did something similar I learned a few things along the way, which I guess are obvious in retrospect.
The upper output frequency limit is determined by how fast the ISR update rate is, and by how many samples you need in the output waveform.
On AVR Freaks I posted a few photos in the past of a DDS sin wave that looked like stair steps, with only a few levels, (perhaps 8 levels?, I don't recall).
It looked terrible on the O'scope, more like stairs than a sin wave.
I fed it through a LPF to remove the higher freq components of the signal and it looked great!
Even a simple first or second order LPF will clean up the output a lot!
In the DDS ISR I load the next DAC value and then set a flag.
In the Main Loop the flag is spotted, and then the next sample is calculated to be ready for the next DDS ISR.
What wasn't obvious to me at first, and is now, is that as the ISR rate increase, the micro spends more and more time inside the DDS ISR.
This leaves progressively less time within the Main Loop for the Main Loop to calculate the index and get the value of the next sample.
Hard core DDS programmers can optimize their algorithm's implementation significantly.
But that isn't really needed just to get started, especially if one isn't pushing the micro's limits.
I found it easiest to use a two channel O'scope and set/clear a pin within the DDS ISR to see how long it takes to process the ISR.
One can also set/clear a pin surrounding the calculations for the next DAC value.
This is where a lot of optimization can take place, if one needs to do so.
If you run your DDS ISR at 50 KHz, 50,000 interrupts / Sec, as an example, then:
that gives 20 uSec between each ISR,
with a 1 MHz clock one ahs 1,000,000 clock cycles / Sec,
with a 32 MHz clock one has 32 M clock cycles / Sec,
so, with a 32 MHz clock one has 600 clock cycles / 20 uSec, or 600 clock cycles / ISR.
My program, very crude, (I'm not much of a programmer, and I just wanted to see the system work), used about 10 uSec, (~320 clock cycles), / ISR, or about 1/2 of the micro's processing time when running at 32 MHz, and with a 50 KHz ISR rate.
That left 50% of the processing time for the ISR and for switching in and out of the ISR and for running any other Main Loop code, (e.g. user interface to change frequency, or sweep frequency code, etc.).
My 10 uSec's to calculate the updated value could certainly be improved upon!
Remember that if one measures code inside an ISR with an O'scope, by setting and clearing a pin within the ISR, that time does NOT include the time for the ISR to push and pop all of the saved registers for the ISR. As one shortens the ISR code, the ISR overhead can become a significant portion or the ISR time.
Good luck with your project!
JC |
|
Back to top |
|
|
EDC
Joined: 26 Mar 2014 Posts: 971
|
Posted: Sun Feb 12, 2017 11:43 am Post subject: |
|
|
I never build a DDS with sine but after read this topic I have some idea.
Maybe is possible to work in this way.
You want to 50Hz so it is 20ms max. uC can generate pattern/array of "music" for this period.
Then you set up DMA for feeding DAC and uC resources are free as a bird because this will be done automatically.
Its like playing music where 22kHz is not a big deal even in Stereo
So why ISR and calculate in the fly if you can calculate once and play this in a loop. |
|
Back to top |
|
|
JC
Joined: 15 Dec 2007 Posts: 585 Location: Cleveland, OH
|
Posted: Sun Feb 12, 2017 3:32 pm Post subject: |
|
|
Using the DMA to feed the DAC is an interesting idea.
The "Calculations" aren't very extensive.
One calculates the Sin, (or square, or triangle, or ramp, or EKG...), waveform values and stores them in an array at the start of the program.
(For a Sin wave one could use a full Period, or 1/2 a Period with a sign change for the two halves of the waveform, or even a quarter of the waveform...)
The calculations are just to point to the next sample in the look up table, and then read it to have it ready for the DDS ISR.
One of the reasons for the ISR is that one wants to output the samples at precise time intervals, (e.g. every 20 uSec).
The samples need to be evenly spaced.
That is easy when one uses a Timer/Counter to generate the interrupt.
JC |
|
Back to top |
|
|
Inventor-George
Joined: 28 Mar 2006 Posts: 74
|
Posted: Sun Feb 12, 2017 7:36 pm Post subject: |
|
|
GREAT discussion, guys!
Could build a full function generator using these concepts.
Imagine, an expensive sig generator in your watch. |
|
Back to top |
|
|
JC
Joined: 15 Dec 2007 Posts: 585 Location: Cleveland, OH
|
Posted: Sun Feb 12, 2017 8:27 pm Post subject: |
|
|
The following is a simple DDS Sine Wave Generator for the Xmega E5 series.
It is not a Direct Digital Synthesis tutorial, but if you are familiar with the concept it should make sense.
Basically one has a signal lookup table, (Sine, Square, Triangle, etc.), and runs an ISR at a fixed, fast, rate.
One calculates the next sample to output based upon the desired output frequency, table size, and DAC update rate.
This program is set up for a 62.5 kHz DDS ISR, (sample update rate).
The number of samples the output signal has is fixed at 62.5 k samples / Second.
A 1 Hz sine wave would have 62.5 K samples for one Period of the sine wave.
A 1 KHz sine wave would have 62.5 samples per one Period of the sine wave.
A 10 KHz sine wave would have 6.25 samples per one Period of the sine wave.
Know that even a simple first or second order Low Pass Filter will significantly clean up the output signal by removing the higher frequency components of the "stair-step" signal.
There is certainly room for improvement, but this demonstrates a working example of the concept.
JC
Code: |
'File: X32E5 DDS Sinewave V1.bas
'Xmega32E5 DDS Program
'Bascom
'Jay Carter
'April 2014, Revised Feb 2017
'This project is a simple DDS Sinewave generator for audio range signals.
'It outputs a Sine Wave on DAC output PortA.2
'It runs on an XmegaE5 micro that is running at 32 MHz, (i.e. not overclocked).
'It isn't optimized for speed, there is plenty of room for improvement.
'Arrays are Base 0, (Not the default Base 1).
'The Sinewave lookup table holds one full Period of the Sinewave.
'This ignores sinewave symmetry in order to keep it simple.
'Sinewave table has 256 entries, (0-255).
'Therefore can use 1 Byte as the Sinewave Index.
'Table pointer accumulator uses a Word type variable, scaling * 256.
'The upper byte is the Sine Lookup Table Index value!!!
'The full Word type is the DDS accumulator.
'The carry out of the Word is simply ignored.
'Xmega has a 12-Bit DAC, are only using 10 Bits at this time.
'There were several versions prior to this, so the comments may not always be
'up to date!
'This program uses < 10% of the Xmega32E5's memory.
'As structured, the DDS ISR is 62.5 KHz.
'There are 62,500 "Steps" in the output signal.
'For a clean output one MUST feed the signal through a Low Pass Filter.
'How "good" the waveform is, (Amount of Distortion), depends upon the
'output frequency.
'This program calculates the pointer to the next table value in the Main Loop.
'One could do this within the DDS ISR.
'In that case the program would be running mostly within the ISR, and the
'Main Loop could be NULL, or have user interface for setting frequency, etc.
'As long as the pointer to the next value is determined before the next DDS ISR
'interrupt comes along, it really doesn't matter which approach is used.
'Be careful if use Floating Point inside an ISR, it needs special register
'handling.
'This DDS has an update DAC rate of 62.5 KHz.
'During each Intr the new DAC value is loaded into the DAC, and a Flag is set.
'In Main the flag is recognized and the next DAC value is calculated.
'Calculated means determine the next sinewave sample to point to, and output
'on the next interrupt.
'The ISR Frequency determines how fast samples are outputted, and hence how many
'samples will be provided for the Sinewave.
'Output Sine 1 KHz 62.5 Samples / Period
'Output Sine 5 KHz 12.5 Samples / Period
'Output Sine 10 KHz 6.25 Samples / Period
'Note also that the DAC is running at 10 bits, not its full 12 bit resolution.
'Don't forget that with the XmegaE5 and the Timer/Counter C 4 Overflow ISR that
'one has to RESET the Overflow ISR Flag inside the ISR, manually!!!
'Note Well: Increasing the ISR rate ALSO decreases the time the forground has
'to process the data for the next DAC value. The calculation is interrupted
'more frequently, so it takes longer.!! Obvious, once one thinks about it.
'...............................................................................
'Hardware Setup and Notes:
'XMega runs at 2 MHz on startup, default.
'The XMega uses a PDI Programmer, 6-Pin Header is on the board.
'Use the Internal 32 MHz Osc.
'This PCB has a Nokia GLCD installed , which isn't used for this project.
'Hardware:
'Currently set up for my "Blue" Xmega32E5 PCB.
'Has a 4.00 MHz External Xtal installed.
'Available on Edge Connector Pads:
'PortA.1
'PortA.2 = DAC Output, (DAC0), Signal Output, unfiltered
'PortA.3 = DAC Output, (DAC1), Not being used.
'PortC.4 = LED2
'PortC.5 =
'PortC.6 =
'PortC.7 = LED3
'PortD.0 =
'Wired on PCB:
'PortA.4 = PB SW 3 Has External Pull-up resistors
'PortA.6 = PB SW 2 Has External Pull-up resistors
'PortA.7 = PB SW 1 Has External Pull-up resistors
'PortD.1 = LED1 Red LED adjacent the GLCD top right corner, High = On
'PortA.5 BT Module High Side Power Driver control, BT is not installed.
'PortD.4 = GLCD Data Clock, SCK
'PortD.5 = GLCD Data In, MOSI
'PortD.6 = GLCD D/C Mode
'PortD.7 = GLCD CS\
'PortC.0 = GLCD GLCD Backlight LED, High = On
'PortC.1 = GLCD Reset\
'PortR.0 External 4 MHz Xtal
'PortR.1 External 4 MHz Xtal
'-------------------------------------------------------------------------------
$regfile = "xm32E5def.dat" 'Micro in use
$crystal = 32000000 'Micro's Clock: 32 MHz
$hwstack = 192 'Hardware stack
$swstack = 192 'Software stack
$framesize = 192 'Frame space
Config Base = 0 'Arrays start with Index 0
Led1 Alias Portd.1 'Led1 Red High is On
Led2 Alias Portc.4 'Led2 Yellow High is On
Led3 Alias Portc.7 'LED3 Green High is On
' Scope Alias Portc_out.7 'O'Scope Probe
'Now Configure the Port's Pins for Input or Output mode.
'Config LEDs:
Portd_pin1ctrl = &B00000000 'PortD.1 Control Totem Pole Output
Portc_pin4ctrl = &B00000000 'PortC.4 Control Totem Pole Output
Portc_pin7ctrl = &B00000000 'PortC.7 Control Totem Pole Output
Portd_dir.1 = 1 'PortD.1 = Output
Portc_dir.4 = 1 'PortC.4 = Output
Portc_dir.7 = 1 'PortC.7 = Output
'Config PortA.2 for output for the DAC: (Not necessary)
' Porta_pin2ctrl = &B00000000 'PortA.2 Control Totem Pole Output
' Porta_dir.2 = 1 'PortA.2 = Output
'....................................................................................................
Dim J As Byte
Dim D As Byte
Dim Lpcnt As Word 'Loop Counter
Dim Ttcnt2 As Word 'Tick Toc ISR Counter
Dim Rvbit As Bit 'For Register Setup
Dim Regdata As Byte 'For Register Setup
Dim Lpcnt1 As Word 'Loop Counter
Dim Lpcnt2 As Word 'Loop Counter
Dim Isrcnt As Word 'ISR Counter TCD0, 1 intr q mSec
Dim Mainlp As Word 'Loop Counter, Main Loop
'Define several Values for the Xmega DAC
Dim Daccnt As Word 'DAC ISR Counter for rollover
Dim Dacch0val As Word 'Word Type Data to Feed DAC Ch0
Dim Dacch1val As Word 'Word Type Data to Feed DAC Ch1
'Dim DDS Direct Digital Synthesis Variables.
Dim Sintab(512) As Word 'Sin Lookup, Base 1
Dim Ddstmp As Single 'For Sin Table Calcs
Dim Ddsaccum As Single 'DDS Phase Accumulator
Dim Ddsgain As Single 'DDS DAC Gain Factor, <= 1.0
Dim Ddsindex As Word 'Index for SinTab()
Dim Fout As Single 'The desired Output Frequency, in Hz
Dim Dacflag As Byte 'ISR Set Flag for calculating next DAC value in Main
Dim Dacdata As Word 'Data Word for loading DAC with
Dim Dacdur As Word 'Counter for Duration of current tone
Dim Ddstmpw As Word 'Delta Phase * 100
Dim Ddsaccumw As Word 'DDS Phase Accum * 100
Dim Ddstmpdw As Dword 'Delta Phase * 1000, Dword type
Dim Ddsaccumdw As Dword 'DDS Phase Accum * 1000, Dword Type
Dim Tmpdw As Dword 'Calc variable
'Dim Arbitrary Waveform Gen Variables:
Dim Awfgindex As Word 'Sin Table Index BASE 0
Dim Awfgstep As Word 'Sample Table Incr Step Size
'///////////////////////////////////////////////////////////////////////////////
Startup:
'First set the Clock for 32 MHz via Internal RC Osc:
Gosub Clockopt1
'Briefly flash LEDs on start up, show that they work.
Set Led1
Waitms 250
Set Led2
Waitms 250
Set Led3
Waitms 250
Reset Led1
Reset Led2
Reset Led3
'...............................................................................
'Set up Timer/Counter C4 for DDS ISR:
'No hardware pins are controlled by this action.
'The Xmega Interrupt Controller must also be set up for this to function.
'My Xmega E5 Timer/Counter Setup for a desired Interrupt. System Clock = 32 MHz.
'NOTE WELL: One MUST manually Reset the Overflow Interrupt Flag inside the ISR.
'Otherwise it keeps retriggering, and ISR rate is very fast, and foreground crawls
'at one instruction per ISR firing.
'Given the uC's Clock, set the PreScaler divide by.
'Note, This PreScaler applies to ALL of the uC's Timer/Counters!
'Then set the Period for the desired interrupt rate.
'The T/C will roll over when it reaches TOP, or whatever the Period is set to.
'Be sure to reset the OVF Flag inside the ISR, or it will retrigger.
'Test Example:
'Intr at 1000 Hz for now, i.e. q 1 mSec
'Clock = 32 MHz.
'Prescaler is / 64. 32MHz/64 --> 500,000 Hz output of PreScaler.
'500K/500 = 1000.00 Hz as the OverFlow Intr Rate.
'Set TCC4_Per = 499 (Roll Over counts as 1)
'Example: Intr at 62500.0 Hz
'Clock = 32 MHz.
'Prescaler is / 64. 32MHz/64 --> 500,000 Hz output of PreScaler.
'500K/8 = 62500.0 Hz as the OverFlow Intr Rate.
'Set TCC4_Per = 7 for Div by 8, (Roll Over counts as 1)
'Example: Intr at 83333.333 Hz
'Clock = 32 MHz.
'Prescaler is / 64. 32MHz/64 --> 500,000 Hz output of PreScaler.
'500K/6 = 83333.333 Hz as the OverFlow Intr Rate.
'Set TCC4_Per = 5 for Div by 6, (Roll Over counts as 1)
'Example: Intr at 100,000 Hz
'Clock = 32 MHz.
'Prescaler is / 64. 32MHz/64 --> 500,000 Hz output of PreScaler.
'500K/5 = 100,000.0 Hz as the OverFlow Intr Rate.
'Set TCC4_Per = 4 for Div by 5, (Roll Over counts as 1)
'Setup for 62.5 KHz DDS ISR Interrupt Rate:
Config Tcc4 = Normal , Prescale = 64
Tcc4_per = 7 'TOP for TCC4 @ 62.5 KHz ISR
On Tcc4_ovf Ticktock2 , Saveall 'Define ISR Label
'Enable Tcc4_ovf , Hi 'Enable the Overflow ISR
'...............................................................................
'Set up Timer/Counter C5 as a heartbeat ISR.
'As the DAC ISR is High Priority, and this is Low Priority, it should be able
'to run with minimal jitter impact on the DAC output.
'Set this as LOW priority.
'Only one prescaler can be used for all of the T/C's on the E5.
'Prescaler is already set at /64 for TCC4.
'But still need it in the Instruction!
'The following example fires the ISR at 100.00 Hz.
'The ISR counts to 99, plus one for roll over, for a 1 Hz Led Flasher.
'Clock = 32 MHz.
'Prescaler is / 64. 32MHz/64 --> 500 kHz output of PreScaler.
'500 k / 5000 = 100.00 Hz as the OverFlow Intr Rate.
'Set TCC5_Per = 4999 to set Top to 5000. (Roll Over counts as 1)
Config Tcc5 = Normal , Prescale = 64
Tcc5_per = 4999 'Can change this once it works.
On Tcc5_ovf Heartbeat 'Define ISR Label
Enable Tcc5_ovf , Lo 'Enable the Overflow ISR
|
Code: |
'....................................................................................................
Premain:
Gosub Initsintable 'Initialize DDS Sin Table
Gosub Dacsetup 'SetUp DAC Signal Output
Inits:
Ttcnt2 = 0 'Init ISR Counter
Lpcnt = 0 'Init Loop Counter
'Now Enable Global Interrupts and let the fun begin:
Config Priority = Static , Vector = Application , Hi = Enabled , Med = Enabled , Lo = Enabled
Enable Interrupts
'Init Variables:
Isrcnt = 0 'Init ISR Counter to 0
Dacch0val = 0 '4095 'Init Full Scale Mid -range
Dacch1val = 0 '4095 'Init Full Scale Mid-Range
Daccnt = 0 'Init ISR for DAC Counter
Awfgindex = 1 'Init Sin Table pointer
Awfgstep = 1 'Incr through each sample
Ddsaccum = 0 'Init Phase Accumulator
Dacflag = 0 'Init Flag to not set
Dacdur = 0 'Init Tone Duration Counter
Main:
'Generate a Fixed DDS Generated Output Signal:
'DDS ISR fires at 62.5 KHz and updates the DAC with the next value.
'Main Loop watches for flag from DDS ISR, then calculates the next
'DAC output value for when the ISR next fires.
'Prior to starting the loop:
'Pre-calculate the "Step size" for stepping through the Sin Table, based
'upon the output frequency, table size, and DAC update rate.
'*************
'Set the desired output frequency here, in Hz:
Fout = 1000 '1 kHz Output Freq
'*************
Ddsindex = 1 'Init DDS Table Index
Gosub Gensin 'Calc Delta Phase for this Output Frequency
Enable Tcc4_ovf , Hi 'Enable DAC ISR as High Interrupt
'Now let ISR update DAC value, and calculate next value in Main Loop.
'ISR sets a flag when it executes, so Main Loop knows when to calc the
'next value.
'Accumulator is a Word Type, and automatically rolls over at 65K.
'Its upper byte is the Sin Table Index.
Do
'Just watch for DDS ISR to set a flag, indicating that Main should
'calculate the next DAC output value for the ISR to use.
If Dacflag = 1 Then
'Need to Calc the next DAC value before next ISR comes along:
Ddsaccumw = Ddsaccumw + Ddstmpw 'Incr Phase Angle (* 256)
Ddsindex = High(ddsaccumw) 'Grab upper byte as SinTable Index
Dacdata = Sintab(ddsindex) 'Get the DAC Data Word
Dacflag = 0 'Reset the FLAG!
End If
Loop
End
'===============================================================================
'Xmega DACB Ch0 & Ch1 Testing, they feed an Audio Amp Chip.
Dacsetup:
'The XmegaE5 has one 12 Bit DAC, with two output channels.
'PortA.2 is DAC0 which this program uses.
'PortA.3 is DAC1 which is not in use.
'Bascom can set this up, direct Register setup is not required.
Config Daca = Enabled , Io0 = Enabled , Io1 = Enabled , Internal_output = Disabled , Channel = Dual , Trigger_ch0 = Disabled , Trigger_ch1 = Disabled , Reference = Avcc , Left_adjusted = Disabled , Event_channel = 0 , Interval = 64 , Refresh = 64
Return
Ticktock2:
'This is the DDS ISR.
'PortC Timer/Counter TCC4 ISR to update the DAC output value.
'The ISR Rate is set by the PortC Timer/Counter TCC$ setup.
'This routine outputs the next DAC value and sets a flag for the Main Loop.
'The Main Loop has to calculate the next value before the ISR fires again.
'On Entry have: DACData, Word, to load into the DAC.
'Could just use the Sin Table Index for this.
'On Exit have: Set DACFlag for Main to notice, and update DAC Calcs.
'Reset the Overflow Flag inside the ISR!
Tcc4_intflags = 1 'Clr Ovf Intr Flag
Daca0 = Dacdata 'Load DAC with Data Word
Dacflag = 1 'Set Flag, ISR Fired
Return
|
Last edited by JC on Sun Feb 12, 2017 8:40 pm; edited 1 time in total |
|
Back to top |
|
|
JC
Joined: 15 Dec 2007 Posts: 585 Location: Cleveland, OH
|
Posted: Sun Feb 12, 2017 8:40 pm Post subject: |
|
|
I can't seem to post the entire program in one piece.
Here is the rest of it.
Perhaps just posting it as an attachment is better?
JC
Code: |
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'DDS Routines:
'10 Bit DDS for now, (Xmega could do 12-Bit DDS).
'Ignore Sine wave symmetry for now.
'One full Period is stored in 256 entry lookup table:
Initsintable:
'Initialize a Sin Look-Up Table for 1 Period.
'Ignore Symmetry for now.
'N = 9, 2^N = 512 Entries in Table.
'Table has ADC/2 as DC Offset Set to 2047 as Midpoint.
'Sin going -1 to +1 full scale maps to 0 to 4095 or 4096...
'Table is indexed Base 1.
'Actual table goes: 2047 to 4093 to 2047 to 0 to 2047 over 1 period
'Storing Single into Word Type Table saves the Integer part of the
'single type calculation.
'Using a 512 byte array, 2^9 = 512
'360 Deg / 512 Samples --> 0.703125 Deg/sample
'*** Changed to 256 Entry Table, for 1 Byte Index into t=lookup table:
'2^n = 256, n = 8.
'360 / 256 = 1.40625 Degree / Sample
'Note: Changed to Base 0 for arrays with V4.
For Lpcnt1 = 0 To 255
Ddstmp = Lpcnt1 'Index for table 0-255
Ddstmp = Ddstmp * 1.40625 'Deg for this table entry, 0-360
Ddstmp = Deg2rad(ddstmp) 'Rads for this table entry, 0 - 2Pi
Ddstmp = Sin(ddstmp) 'Calc Sin for this table entry 0 to 0, range -1 to +1
'Now Multiply value, ranging -1 to +1, * 2047 so DAC value will range
'from -2047 to + 2047. Then add 2047 DC offset to center the DAC
'so the final DAC range is 0 - 2094 (missing the last value).
Ddstmp = Ddstmp * 2047 'Scale for Full Scale of DAC, (0-4095)
Ddstmp = Ddstmp + 2047 'Add DC offset to put sin midscale of DAC
Sintab(lpcnt1) = Ddstmp 'Store Table Entry
Next
Return
Gensin:
'Calculate the Delta Phase for this Output Frequency
'This calculates the step size for the lookup table for a given desired
'output frequency, number of table entries, and DAC update rate.
'On Entry have: Fout in Hz
'10-Bit Resolution used in Table, but note that Xmega has a 12-Bit DAC available.
'On Entry have: Fout The Output Freq in Hz. Single Type
'On Entry have: SinTab() Sine wave table, Base 0, 1 period's worth of samples.
'On Entry have: DDSGain The Gain Factor for the output, <= 1.0
'Calc the amount of Phase to Add to the Phase Accumulator for the given
'Input Freq, Lookup Table Size, and Output Clock (Sample Rate).
'For now assume Xmega is at 32 MHz clock,
'Sample Rate is 100 kHz, (Interrupt rate, Quite fast). <--- CHANGES with version
'Have about 320 Instr per Interrupt period for ISR and Main to Share!
'On Exit have: DDSTmp which is the Delta Phase, i.e. how many samples to
'skip over to get the next Sine Table sample to play for this Output Freq.
'On Exit have: DDSTmpW DDS Delta Phase as Word Type.
'
'Note: Instead of "rounding up" and back down in the calc loop, which is
'slow, multiple by 100 for two decimal places, or by 1000 to pick up three
'decimal places, but then ignore the rounding of the 4th decimal place.
'Note: CHANGED to 255 Entry Table for 1 byte lookup.
Ddsaccum = 0 'Init Phase Accumulator
Ddsaccumw = 0 'Init Phase Accum
Ddsaccumdw = 0 'Init Phase Accum
'Calc the Delta Phase to Add to the Phase Accumulator
'This gives the entry location in the Sin Table
'M = Step size through the table.
'2^n = Number of entries in 1 period of the sine wave, 1 period.
'512 entries gives n=9 65535 entries gives n=16, etc.
'N = the resolution of the phase accumulator.
'In the language: M = ((fout)(2^n))/Fref
'N=1024 , Fref = 100KHz, the DAC Sample output rate, Fout = output Freq in Hz
'Changed to new, lower ISR Rate of 38461.538 Intr / Sec
'Skip Gain for now, then add. It adds a lot of FP math.
'32MHz / 64 -> 500 kHz, 500 kHz / 5 = 100000 Samples / Sec (Div = 4+1)
'32MHz / 64 -> 500 kHz, 500 kHz / 6 = 83333.333 Samples / Sec (Div = 5+1)
'32MHz / 64 -> 500 kHz, 500 kHz / 13 = 38461.538 Samples / Sec (Div = 12+1)
'32MHz / 64 -> 500 kHz, 500 kHz / 17 = 29411.76 Samples / Sec (Div = 16+1)
'32MHz / 64 -> 500 kHz, 500 kHz / 10 = 50000 Samples / Sec (Div = 9+1)
'32MHz / 64 -> 500 kHz, 500 kHz / 8 = 62500 Samples / Sec (Div = 7+1)
Ddstmp = Fout * 256 'Single Type
'Select the DDS ISR Rate being used at the moment:
'Calculate the Delta Phase Shift per Interrupt:
Ddstmp = Ddstmp / 62500.0
'Now get the Delta Phase as a Word Type, (Scaled * 256)
Ddstmp = Ddstmp * 256.0 'Scale for Integer Math
Ddstmpw = Ddstmp 'Conv Single to Word Type * 256
Return 'Await next intr
Heartbeat:
'Timer/Counter TCC5 ISR for HeartBeat LED1.
'Fires at 100.00 Hz, q 10 mSec
'Low Priority
'Remember to RESET the ISR Flag inside the ISR on the E5's.
'Flash an LED for kicks:
Tcc5_intflags = 1 'Clr Ovf Intr Flag
Ttcnt2 = Ttcnt2 + 1 'Incr
If Ttcnt2 < 20 Then
Set Led1 'LED On
Else
Reset Led1 'Off
End If
If Ttcnt2 > 99 Then
Ttcnt2 = 0 'Rollover
End If
Return
Clockopt1:
'Set up the Xmega clock.
'Run on Internal 32 MHz Osc at 32 MHz
'Xmega runs at 2MHz on power up.
'Leave 2 MHz Osc enabled, and enable the Int 32MHz Clock
Config Osc = Enabled , 32mhzosc = Enabled
'Wait a bit to allow clocks to start up and stabilize.
'Note: Haven't yet switched to 32MHz, are at 2MHz, so Waitms 1 = 16 mSec
Waitms 4 'Wait 64mSec
'Next configure the systemclock:
Config Sysclock = 32mhz , Prescalea = 1 , Prescalebc = 1_1
Waitms 100 'Startup Stabilization Delay
Return
|
|
|
Back to top |
|
|
Per Svensson
Joined: 03 Oct 2004 Posts: 235 Location: Gothenburg, Sweden
|
Posted: Tue Feb 14, 2017 2:24 pm Post subject: |
|
|
Regarding the generation of a sinewawe it could help to remember that the first integral of a constant is a ramp,
and a second integration of that ramp will generate a sinewave.
So all you need are two accumulators for integration. The frequency is controlled by the accumulation rate (IRQ driven?)
This is extremely simple, and you will get sawtooth and sine wave at the same time.
I have code to share somwhere if it is of interest
/Per |
|
Back to top |
|
|
Per Svensson
Joined: 03 Oct 2004 Posts: 235 Location: Gothenburg, Sweden
|
Posted: Tue Feb 14, 2017 2:49 pm Post subject: |
|
|
I wrote a BasCom program some years ago that reports the execution time for some common math operations using various data types.
What I wanted to know was when to use floating Point operations and when to avoid them.
That was also the time I realized that shifting a IEEE single variable will work instead of Dividing or multiplying by 2.
Especially division by two using signed rightshift is enormously fast compared to use of the division operator.
Anyway, here are some results, and if you need other operation benchmarked, other tahn those listed here you will also find the sourcode attached.
For example:
B=B+B: 9 0.56us --> Summing a byte to Another byte and assigning it to a byte will consume nine CPU Clocks (=0.56us at 16MHz)
/Per
---------------------------------------------------------------------------------------
BasCom_AVR 2.0.7.8 Compiled:2015-12-11
Math benchmark at 16MHz clock
Operation: Clocks, Time(us)
---------- No_operation overhead -----------
Overhead: 5 0.31us (Time spent on starting and stopping TIMER1)
---------- BYTE -----------
B=B+B: 9 0.56us
B=B*B: 10 0.63us
B=B xor B: 9 0.56us
---------- WORD -----------
W=W+W: 20 1.25us
W=W*W: 42 2.63us
W=W/W: 260 16.25us
W=W\W: 260 16.25us
W=W xor W: 20 1.25us
Shift(W,Right,: 62 3.88us
---------- INTEGER -----------
I=I+I: 20 1.25us
I=I*I: 42 2.63us
I=I/I: 350 21.88us
I=I\I: 350 21.88us
I=I xor I: 20 1.25us
Shift(I,Right,8,Signed): 82 5.13us
---------- LONG -----------
L=L+L: 44 2.75us
L=L*L: 90 5.63us
L=L/L: 878 54.88us
L=L\L: 878 54.88us
L=SQR(L): 1037 64.81us
Shift(L,Right,12): 127 7.94us
Shift(L,Right,12,Signed): 155 9.69us
---------- SINGLE -----------
S=S+S: 229 14.31us
S=S*S: 260 16.25us
S=S/S: 585 36.56us
Shift(S,12): 81 5.06us
S=Log(S): 81 5.06us
S=Sin(S): 1912 119.50us
---------- DOUBLE -----------
D=D+D: 478 29.88us
D=D*D: 702 43.88us
D=D/D: 1970 123.13us
Shift(S,12): 1 0.06us
S=Log(S): 195 12.19us
D=Din(D): 15269 954.31us
-------- ASSIGNMENT ---------
B <= B: 4 0.25us
I <= I: 12 0.75us
L <= L: 20 1.25us
S <= S: 42 2.63us
---- MIXED TYPE OPERATIONS -----
L=I+I: 47 2.94us
I=B+B: 18 1.13us
S=B+B: 392 24.50us
I=I+S: 281 17.56us
I=I*S: 331 20.69us
------- TYPE CONVERSION --------
I<=B: 11 0.69us
B<=lsb(I): 8 0.50us
I<=S: 207 12.94us
S<=I: 125 7.81us
L<=S: 240 15.00us
S<=L: 133 8.31us
D<=L: 176 11.00us
D<=S: 151 9.44us
L<=D: 194 12.13us
S<=D: 183 11.44us
----- MULT Versus SHIFT LEFT ------
S=S*512=-2781337.00: 336 21.00us
Shift S,left,9=-2781337.00: 82 5.13us
D=D*512=-2781337.00: 571 35.69us
Shift D,left,9=-2781337.00: 1 0.06us
----- DIVIDE Versus SHIFT RIGHT ------
S=S/512=-10.61: 666 41.63us
Shift S,right,9,signed=-10.61: 81 5.06us
D=D/512=-10.61: 2036 127.25us
Shift D,right,9,signed=-10.61: 1 0.06us
L=L/512=-10.61: 852 53.25us
Shift L,right,9,signed = -10.61: 128 8.00us
( Observe that shifting a Single or Double )
( really works as a substitute for MULT/DIV !)
Let us push this test a little bit harder
by dividing 123.45679E6 by 2^0...2^31
MULTIPLICATION OF A SINGLE BY 2^N
(Multiply123.45679E6 by 2^0...2^31)
-------------------------------------
123.45679E6 * 1 = 123.45679E6 24.37500us
Shift 123.45679E6,LEFT,0 = 123.45679E6 5.18750us
123.45679E6 * 16 = 1.97531E9 22.37500us
Shift 123.45679E6,LEFT,4 = 1.97531E9 5.18750us
123.45679E6 * 256 = 31.60494E9 23.81250us
Shift 123.45679E6,LEFT,8 = 31.60494E9 5.18750us
123.45679E6 * 4096 = 505.67903E9 21.81250us
Shift 123.45679E6,LEFT,12 = 505.67903E9 5.18750us
123.45679E6 * 65536 = 8.09086E12 23.25000us
Shift 123.45679E6,LEFT,16 = 8.09086E12 5.18750us
123.45679E6 * 1048576 = 129.45383E12 21.25000us
Shift 123.45679E6,LEFT,20 = 129.45383E12 5.18750us
123.45679E6 * 16777216 = 2.07126E15 22.68750us
Shift 123.45679E6,LEFT,24 = 2.07126E15 5.18750us
123.45679E6 * 268435456 = 33.14018E15 20.68750us
Shift 123.45679E6,LEFT,28 = 33.14018E15 5.18750us
DIVISION OF A SINGLE BY 2^N
(Divide 123.45679E6 by 2^0...2^31)
-------------------------------------
123.45679E6 / 1 = 123.45679E6 45.87500us
Shift 123.45679E6,RIGHT,0,SIGNED = 123.45679E6 5.12500us
123.45679E6 / 16 = 7716049.00000 43.87500us
Shift 123.45679E6,RIGHT,4,SIGNED = 7716049.00000 5.12500us
123.45679E6 / 256 = 482253.00000 45.31250us
Shift 123.45679E6,RIGHT,8,SIGNED = 482253.00000 5.12500us
123.45679E6 / 4096 = 30140.81000 43.31250us
Shift 123.45679E6,RIGHT,12,SIGNED = 30140.81000 5.12500us
123.45679E6 / 65536 = 1883.80100 44.75000us
Shift 123.45679E6,RIGHT,16,SIGNED = 1883.80100 5.12500us
123.45679E6 / 1048576 = 117.73756 42.75000us
Shift 123.45679E6,RIGHT,20,SIGNED = 117.73757 5.12500us
123.45679E6 / 16777216 = 7.35860 44.18750us
Shift 123.45679E6,RIGHT,24,SIGNED = 7.35860 5.12500us
123.45679E6 / 268435456 = 0.45991 42.18750us
Shift 123.45679E6,RIGHT,28,SIGNED = 0.45991 5.12500us |
|
Back to top |
|
|
Duval JP
Joined: 22 Jun 2004 Posts: 1161 Location: France
|
Posted: Tue Feb 14, 2017 6:54 pm Post subject: |
|
|
hi Per,
whooo !
Quote: | I have code to share somwhere if it is of interest
|
Yes please , would you like share it under "Share your working BASCOM-AVR code here" with a specific name : easier to find when you look for a sample.
Many " tak "
JP |
|
Back to top |
|
|
Per Svensson
Joined: 03 Oct 2004 Posts: 235 Location: Gothenburg, Sweden
|
Posted: Wed Feb 15, 2017 10:30 am Post subject: |
|
|
Hi JP
I will put a code snippet for Sine-generation under "share your working AVR code" in a moment.
Take care
Per |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You cannot download files in this forum
|
|