|
|
View previous topic :: View next topic |
Author |
Message |
SteveAustin Guest
|
R2R Ladder SIN wave |
Posted: Sun May 20, 2007 4:00 am |
|
|
Hi all, i have searched all over he site and didn`t find a clear example. I have a 16f917 and a r2r ladder(8 bit resolution) used for DAC.
I must generate a Sin wave, if anybody can give me an example code I`ll be very greatfull.
I`ll wait for you`re posts impatiently.
Best Wishes,
Austin |
|
|
SteveAustin Guest
|
|
Posted: Sun May 20, 2007 4:12 am |
|
|
Sin is at 50Hz |
|
|
libor
Joined: 14 Dec 2004 Posts: 288 Location: Hungary
|
|
Posted: Sun May 20, 2007 5:25 am |
|
|
For a sine wave creation you have many choices:
- a full lookup table
- a quadrant lookup table (with some logic)
- a lookup table with linear interpolation between the elements to increase resolution
- a mix of the above two
- no lookup table, use the built-in sin() function (worst choice I think: to much code, hassle with floating poin, etc.)
- look for the CORDIC algorithm |
|
|
SteveAustin Guest
|
|
Posted: Sun May 20, 2007 5:47 am |
|
|
i would like a full lookup table,......or any method that you have the code |
|
|
libor
Joined: 14 Dec 2004 Posts: 288 Location: Hungary
|
|
Posted: Sun May 20, 2007 7:02 am |
|
|
I found a quarter lookup table I wrote some time:
Code: | const int sintable[] = {0x00,0x03,0x06,0x09,0x0c,0x10,0x13,0x16,
0x19,0x1c,0x1f,0x22,0x25,0x28,0x2b,0x2e,
0x31,0x33,0x36,0x39,0x3c,0x3f,0x41,0x44,
0x47,0x49,0x4c,0x4e,0x51,0x53,0x55,0x58,
0x5a,0x5c,0x5e,0x60,0x62,0x64,0x66,0x68,
0x6a,0x6b,0x6d,0x6f,0x70,0x71,0x73,0x74,
0x75,0x76,0x78,0x79,0x7a,0x7a,0x7b,0x7c,
0x7d,0x7d,0x7e,0x7e,0x7e,0x7f,0x7f,0x7f, 0x7f
};
int my_sin(int deg) {
if (deg<64) return (sintable[deg]+128); //first quarter
if (deg<128) return (sintable[128-deg]+128); //second
if (deg<192) return (128-sintable[deg-128]); //third
return (128-sintable[~deg]); //fourth
} |
I use 1/256th of a circle as a parameter, the function returns 128 as the zero level.
It can be optimized further to use bit_tests and complementing values.
All you have to do is to add a timer interrupt to inc a counter 256values = 1 cycle 50 cycles/sec and to output the result to the resistor ladder. |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Sun May 20, 2007 7:25 am |
|
|
Since you talk about sine waves I assume you need the sines in sequence
Tables and built in functions look up random sines with equal efficiency.
The CORDIC however since it is rotational can be modified to to produce sines in sequence.
Another approach is finite differences. Look at Bresenham's algorithm for a circle. Set the center at 0,0 and the radius at 1 ( the unit circle) the x,y position of a point on the circle is cosx,siny. A few add and subtracts and a multiplication and you move from x,y to x1,y1 where x1-x is your bit resolution. Looking at the y value you went from sin(y) to sin(y1) in sequence
So suppose you resolution was 16 bits then your unit radius is 65535...its like you have a screen 65536 x 65536 pixels and your are drawing a circle that touches the edges using Bresenham's algorithm. Again this is only useful if you can take advantage of the fact you need sines in sequence ...if this is true than you can gain great efficiency.
below is a breshenham adaption for a screen with a rectangular pixel size
aka aspect ration. I have comments to show the math involved.
You would need to go to 16 or 32 bit resolution so modify the code accordingly.
The key to note is the next sine is just
if (delta<0) delta=delta+2*x+3;
else {delta=delta+2*(x-y)+5;y=y-1;}
x=x+1;
the code uses the 8 fold symmetry of a circle and traces through 45 degrees. You sine wave also has this symmetry
Code: | void circle(int x_ctr,int y_ctr,int radius,int pen_size,int set)
{
int x,y,r,x1,y1;
signed long delta;
// y axis is shrunk to reflect the aspect ratio
// this uses bresenham's algorithm
// theory
// 0=x^2+y^2-r^2 is true circle centered at 0,0
// error is x^2+y^2-r^2
// looking at north to north east octant
// pixels are * 0
// 0
// algorithm chooses pixel due east or south east
// * has the coord (x,y) both 0 pixels have an x coord of x+1
// mid pt has coord of (x+1,y+1/2)
// error is (x+1)^2+(y+1/2)^2 -r^2
// error >0 draw south east ( x+1,y-1) pixel else draw east pixel (x+1,y)
// now we can compute the error in advance for the next iteration
// change in error from (x+1,y-1/2) to (x+2,y-1/2) is 2x+3
// (x+1,y-1/2) to (x+2,y-3/2) 2x+3 -2y+2
// Note: delta is the error in the code below
// if delta is <0 then we decrement y
r=70;
if (radius<70) r=radius; // max radius is 70 pixels
for (r=radius-pen_size;r<=radius;r++)
{
x=0;
y=r;
// assume last step was to south east pixel from (0,r) or 2*0-2*r+5
delta=(long)5-(long)2*(long)r;
loop:
y1=(long)y*(long)11/(long)14; /// aspect ratio
x1=(long)x*(long)11/(long)14; /// aspect ratio
plot(x_ctr+x,y_ctr+y1,set);
plot(x_ctr+x,y_ctr-y1,set);
plot(x_ctr-x,y_ctr+y1,set);
plot(x_ctr-x,y_ctr-y1,set);
plot(x_ctr+y,y_ctr+x1,set);
plot(x_ctr+y,y_ctr-x1,set);
plot(x_ctr-y,y_ctr+x1,set);
plot(x_ctr-y,y_ctr-x1,set);
if (delta<0) delta=delta+(long)2*(long)x+(long)3;
else {delta=delta+(long)2*((long)x-(long)y)+(long)5;y=y-1;}
x=x+1;
if (x<=y) goto loop;
}
} |
|
|
|
SteveAustin Guest
|
|
Posted: Sun May 20, 2007 7:27 am |
|
|
I thank you very much for the code libor, but you see i`m a little noob in lookup tables. I have 8 ports which i need to combine to get an analogic result (from 0 to 5V; first are MSB and least are LSB).
Now the big question is how to command them, because the lookup table gives me just some numbers.
I`ll wait you`re answer
Thanx very much |
|
|
libor
Joined: 14 Dec 2004 Posts: 288 Location: Hungary
|
|
Posted: Sun May 20, 2007 7:49 am |
|
|
The easiest if you would use continous ports (bits) of one port register (B port for example) in their original bit order. Then you could use a simple output_b(value) instruction.
If your ports are mixed over various addresses, then you can remap them using #bit
#bit LADDER_0BIT PORTB.5
#bit LADDER_1BIT PORTB.2
..etc
and set them directly
LADDER_0BIT = bit_test(value,0);
LADDER_1BIT = bit_test(value,1);
etc.
Though I would suggest to prepare all output byte(s) first in a temp register and write it to the output at once to avoid the short mismatched values from the sinewave during port update. |
|
|
Guest
|
|
Posted: Sun May 20, 2007 1:44 pm |
|
|
If you only need to generate sine waves with your R-2R ladder, then you could change the resistor weighting from R-2R to give you a sine wave by incrementing/decrementing the output linearly and using the resistor weighting to remove the need for any clever calculations. |
|
|
SteveAustin Guest
|
|
Posted: Mon May 21, 2007 12:19 am |
|
|
Thank you for you`re nice replay libour, i allmost done it but i still have some difficultys.
My testing code is:
Quote: |
#include "16F917.h"
#include <stdio.h>
#include <stdlib.h>
#fuses INTRC_IO,NOPROTECT,NOWDT,PUT
#use delay(clock=4000000)
#define INT_PERIPHERAL 0x0B40 // 2864
#byte T1CON = 0x10 // timer 1
#byte INTCON = 0x0B // Intcon
#byte PIE1 = 0x8C // PIE
#byte PORTE = 0x09
#byte PORTC = 0x07
#bit TMR1ON = T1CON.0 // 0 Bit --> Timer1 - 1/8
#bit PEIE = INTCON.6 // PEIE
#bit GIE = INTCON.7 // GIE
#bit TMR1IE = PIE1.0 // TMR1IE
//===============================
#bit LADDER_0BIT = PORTE.0
#bit LADDER_1BIT = PORTE.1
#bit LADDER_2BIT = PORTE.2
#bit LADDER_3BIT = PORTC.3
#bit LADDER_4BIT = PORTC.4
#bit LADDER_5BIT = PORTC.5
#bit LADDER_6BIT = PORTC.6
#bit LADDER_7BIT = PORTC.7
#define enable PIN_B0 //input line from host
int c=1,place,o,value,i;
const int sintable[] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff,
0x100
};
void main()
{
setup_oscillator(OSC_4MHZ);
TMR1IE = 1;
GIE = 1;
PEIE = 1;
SETUP_TIMER_0(RTCC_INTERNAL | RTCC_8_BIT|RTCC_DIV_32);
ENABLE_INTERRUPTS(GLOBAL); // enable interrupts in general to be active
ENABLE_INTERRUPTS(INT_RTCC); // enable the interrupt for Timer 0
//--------------------------------------------------------------------------------
while(1){ // Prevent PIC from going to sleep.
if(c==1){ //case 1
for(i=0;i<257;i++){
if(i==256){
i=0;
}
sintable[i]=value;
LADDER_0BIT = bit_test(value,0);
LADDER_1BIT = bit_test(value,1);
LADDER_2BIT = bit_test(value,2);
LADDER_3BIT = bit_test(value,3);
LADDER_4BIT = bit_test(value,4);
LADDER_5BIT = bit_test(value,5);
LADDER_6BIT = bit_test(value,6);
LADDER_7BIT = bit_test(value,7);
delay_us(39);
}
LADDER_0BIT = bit_test(value,0);
output_high(PIN_D3);
if(!input(enable)){
c=2;
delay_ms(300);
}
}
if(c==2){ //case 2
output_low(PIN_D3);
if(!input(enable)){
c=1;
delay_ms(300);
}
}
}
/*setup_ccp1(CCP_PWM);
setup_ccp2(CCP_PWM);
setup_timer_2(T2_DIV_BY_1, 128, 1);
pwm_l = 128;
pwm_r = 128;
set_pwm1_duty(pwm_l);
set_pwm2_duty(pwm_r);
*/
}
#INT_RTCC // This function will be called on a Timer0 interrupt
void timer0_interrupt_service()
{
}
|
Well my pins stay to 0 logic mo mater i do.
I tried something easyer like making a port an output and 1 logic:
Quote: |
value = 0xFF;
LADDER_0BIT = bit_test(value,0); //ladder_0bit = pinE0
|
It`s staying to 0 logic too.
Soo what am i doing wrong??????
If you`ll be so kind enough to guide me, I`ll be very happy |
|
|
libor
Joined: 14 Dec 2004 Posts: 288 Location: Hungary
|
|
Posted: Mon May 21, 2007 2:27 am |
|
|
Just a few thoughts now for beginning:
- I see no instruction you set the ports to output, if you use variable mapping to set their level, the compiler does not know about it and does not set the TRIS register automatically as with the output_high, ..low commands. All ports are inputs as default after reset. I suggest to use #USE FAST_IO() -s (manual direction setting mode) and set the ones you use as output with set_c_tris() instruction.
...or as another option, stick to output_low, output_high instructions:
if bit_test(value,0) output_high(PIN_E0);
else output_low(PIN_E0);
...
...so the compiler cares for port directions. (STANDARD_IO setting)
for(i=0;i<257;i++){
if(i==256){
this makes no sense, the i is an int type, its value range is 0-255, however you can take use of this 'feature': just increment it, it will roll over from 255 to 0 automatically. |
|
|
SteveAustin Guest
|
|
Posted: Mon May 21, 2007 9:43 am |
|
|
Thanx very much libor, everything is working fine now :D |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Mon May 21, 2007 2:25 pm |
|
|
I know you have already done this with a lookup table
but I had some spare time and wanted to do some mathematics.
The few lines of code below will generate sines to 8 bit precision for all for 0 to 255
Code: |
#include <18F2320.h>
#fuses NOWDT,NOLVP,INTRC_IO
//Internal RC Osc
#use delay(clock=4000000)
#use rs232(debugger)
/// the sum of the first n numbers=n(n+1)/2
/// sum(n+1)=sum(n)+n+1
/// the sum of every pt on the y axis is y(y+1)/2=sum(y)
/// or y^2=2sum(y)-y
/// the sum of every pt on the x axis is x(x+1)/2=sum(x)
/// or x^2=2sum(x)-x
/// for a circle x^2+y^2=R^2
/// so 2sum(x)-x+2sum(y)-y=r^2 for the true circle
/// if we set x=0 and y=r then 2sum(r)-r=r^2
/// Now consider the increase in x^2 as x moves from pt x to pt x+1
/// (x+1)^2 -x^2
/// substituting sum we get
/// 2(sum(x+1))-(x+1) -( 2sum(x)-x)
/// 2(sum(x)+x+1) -(x+1) -2sum(x)+x
/// x+1+x or 2x+1
/// but since x^2+y^2 =r^2 that means y^2 must decrease by the same amount 2x+1
/// the goal is to find the change in y value that most closely matches
///
/// the initial y^2 value is r^2 so the initial 2sum(r)+r=r^2
/// so first available move from pt y to pt y-1 value
/// generates a decrease in y^2
/// of 2sum(y)-y -( 2sum(y-1)-y-1)
/// or 2(sum(y-1)+y)-y -( 2sum(y-1)-y-1) or 2y+1
/// 2x+1 and 2y+1 will only match when x=y so there are always errors deltax deltay
/// acumulating between the movements of x and y
///
/// on a circle of radius 255 we have x =255*cos(theta) y=255*sin(theta)
/// where theta is arctan(y/x) x is stepped in unit increments
/// theta decreases from 90 to 0
/// y represents the sine 1/4 wave from 90 to zero in 255 increments of dx
///
// for testing
//#include "c:\Program files\PICC\drivers\MATH.H"
void main()
{
//float a,sina;
int8 x,y,r;
signed int16 deltax,deltay;
setup_oscillator(OSC_4MHZ);
r=255;
x=0;
y=r;
deltax=0;
deltay=(long)2*(long)r+(long)1;
loop:
x=x+1;
deltax=deltax+(long)2*(long)x+(long)1;
if ( deltax>deltay )
{
/// we can move y
deltay=deltay-deltax;
deltax=0;
y=y-1;
deltay=deltay+(long)2*(long)y+(long)1;
while (deltay<0)
{
y=y-1;
deltay=deltay+(long)2*(long)y+(long)1;
}
}
// a=atan((float)y/(float)x);
// sina=255L*sin(a);
// printf("\n\r x=%u y=%u sin=%f",x,y,sina);
printf("\n\r x=%u y=%u ",x,y);
if (x<255) goto loop;
while(1);
} |
|
|
|
|
|
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
|
Powered by phpBB © 2001, 2005 phpBB Group
|