Author Topic: Home Brew Analog Computer System  (Read 23254 times)

0 Members and 1 Guest are viewing this topic.

Offline woodchips

  • Regular Contributor
  • *
  • Posts: 106
  • Country: gb
Re: Home Brew Analog Computer System
« Reply #150 on: July 22, 2013, 08:23:25 AM »
Just seen this post on tekscopes about capacitor probems etc.

http://www.maximintegrated.com/app-notes/index.mvp/id/5663

Offline AlfBaz

  • Super Contributor
  • ***
  • Posts: 1492
  • Country: au
Re: Home Brew Analog Computer System
« Reply #151 on: July 22, 2013, 12:12:41 PM »
Hi GK great thread, I also follow your work with great interest.

On the subject of front panels, have you considered laser cutting services? I was just looking and found quite a few that were reasonable, especially if you can get away with plastic. If shielding is an issue it may still be more viable to simply line the inside of a plastic panel with foil. Given that they can not only cut holes of whatever shape but may also be able to do 2 tone legend engraving

Just a thought

Offline GK

  • Super Contributor
  • ***
  • Posts: 1386
  • Country: au
Re: Home Brew Analog Computer System
« Reply #152 on: July 23, 2013, 12:13:34 AM »
Just seen this post on tekscopes about capacitor probems etc.

http://www.maximintegrated.com/app-notes/index.mvp/id/5663



Dunno if anyone is going to get stroppy over copyright on these old texts, but here is a bit on capacitor leakage and dielectric absorption from Computer Handbook Huskey & Korn, and Electronic Analog and Hybrid Computers, Korn & Korn. Numerous other ancient texts cover the same ground too.

I'm sorry to say it but Bob Pease was a little late to the party on this one.

« Last Edit: July 23, 2013, 12:15:15 AM by GK »

Offline GK

  • Super Contributor
  • ***
  • Posts: 1386
  • Country: au
Re: Home Brew Analog Computer System
« Reply #153 on: July 23, 2013, 12:21:06 AM »
Hi GK great thread, I also follow your work with great interest.

On the subject of front panels, have you considered laser cutting services? I was just looking and found quite a few that were reasonable, especially if you can get away with plastic. If shielding is an issue it may still be more viable to simply line the inside of a plastic panel with foil. Given that they can not only cut holes of whatever shape but may also be able to do 2 tone legend engraving

Just a thought


Thanks, but I'm not sure that plastic would be strong enough; especially so to support the weight of each "modular" chassis. Another thing is that the entire composite rack system front panel serves as a ground plane for my signal ground.

Offline GK

  • Super Contributor
  • ***
  • Posts: 1386
  • Country: au
Re: Home Brew Analog Computer System
« Reply #154 on: July 23, 2013, 01:16:09 AM »
Well, finally, here is the Lorenz Attractor in 3-D projection with variable angles of rotation (the 3-D projection unit in action). I have to say that I am quite happy with the way the synthesized sine/cosine potentiometers worked out with the 8-bit digital pots and the sine/cosine look up table. The 1 degree step resolution and 8-bit accuracy gives a fluid variation in display that is, for all sakes and purposes, totally analog as far as I as the operator can discern. In all honesty the Lorenz Attractor probably isn't the best 3-D "object" to demonstrate the operation of the projection unit, as it is a bit complex and an interpretation of the display isn't intuitively obvious as it is with simpler shapes and objects, such as the assorted springs I posted screen photos of a few posts previously.   

lorenz3Dprojection

Here is a simplified schematic of  the initial, prototype projection unit. It is based on the basic 3-D projection principle outlined in chapter 9 (Multi-Dimensional Displays) of Analog Computing At Ultra High Speed, D M.MacKay, M E. Fischer.





I have made a significant improvement to the above described 3D projection unit. The unit as shown above is only capable of rotating the projection on both the X and Y axes over the range of 0 to 90 degrees. This is quite adequate an adjustable range for graphs and shapes in isometric projection, which are typically shown at a "perspective" of 45 degrees. However I figured that it would be a lot cooler if the projection unit could in fact spin the projected display on either axis the full 360 degrees.

The reason the design detailed above is limited to 0-to-90 degree range is that the digital pots connected as potential dividers (to implement the necessary sine and cosine multiplications) can only effectively multiply in two quadrants (the positive ones) as the range of multiplication of the applied input signal is limited to the range of 0 to 1. Now while 2 quadrants of operation for either a sine or cosine function on its own represents a 180 degree range of rotation, the range is in fact limited 90 degrees when a sine/cosine complement is generated as only a 90 degrees of variation is achievable before one function hits its limit at the point of transition into an off-limits negative quadrant.

However I figured out a simple way to achieve the full 4-quadrant range of operation with each digital pot, thus eliminating the above described restriction and permitting a full 360 degree range of variable rotation on each axis. It's quite simple really. I just followed each of the 8 digital pots implementing the sine and cosine multiplication functions with an individual "amplifier" having a gain of either 1 or -1, depending on the state of a logic control input. The range of multiplication is thus extended from 1 to 0, to 1 to -1, simply by asserting the logic control line to switch the amplifier from a gain of 1 to -1 whenever operation in the negative quadrants are required.

Here is a video of the resultant prototype in action. At the moment the angles of rotation are not manually variable/settable as they were previously as I haven't gotten that far with the code yet. I just wrote a test subroutine to linearly and continuously rotate the projected display on each axis the full 0-to-360 degrees at a fixed and steady rate (one axis rotating twice as fast at the other). Once again the "object" being displayed is the Rössler Attractor: 

Rössler Attractor Analog Simulation MKII



Cool, huh?  ;D

 

 




« Last Edit: July 23, 2013, 01:22:20 AM by GK »

Offline Odysseus

  • Regular Contributor
  • *
  • Posts: 95
  • Country: us
Re: Home Brew Analog Computer System
« Reply #155 on: July 23, 2013, 08:02:24 AM »
Damn right it's cool.

Give us a heads up when you try attacking the N-body problem.  :-+
« Last Edit: July 23, 2013, 08:04:30 AM by Odysseus »

Offline GK

  • Super Contributor
  • ***
  • Posts: 1386
  • Country: au
Re: Home Brew Analog Computer System
« Reply #156 on: July 24, 2013, 07:12:18 PM »
N-body problem.  :-+



I looked it up and the some of the equations looked quite scary!
 

Offline Odysseus

  • Regular Contributor
  • *
  • Posts: 95
  • Country: us
Re: Home Brew Analog Computer System
« Reply #157 on: July 25, 2013, 12:06:38 PM »
Could you simulate a single moving mass orbiting a fixed gravitational center?  Just F=ma and F=m1*m2/r^2.  In vector form for more dimensions than one.

Then try a two body system where you have two moving masses pulling on each other.

Offline GK

  • Super Contributor
  • ***
  • Posts: 1386
  • Country: au
Re: Home Brew Analog Computer System
« Reply #158 on: August 04, 2013, 01:08:34 AM »
  Such a problem may probably best be broken down into something simple and then built upon. Really don't have much time to devote to that now though! Thoroughly stuck into the computers hardware construction right now.


--------------------------------------------------------------------------------------------------------------------


BTW, could anyone suggest to me a more elegant way of converting a long integer (value ranging 0 to 360) into three seperate decimals (0 to 9) (hundreds [xh], tens [xt] and units [xu])? It should probably be obvious but it is getting close to 1am now.

I've added digital readouts (7-segment LED) for the variable angles of rotation to my 3-D projection unit. The code and hardware is done and operational, but I am now tidying it up the firmware and annotating.

Here is how I am currently doing it:

   
xh = abs(angle_of_rotation_X_axis / 100);
xt = angle_of_rotation_X_axis - (xh * 100);
xu = xt - 10 * (abs(xt / 10));
xt = abs(xt / 10);
   

Online baljemmett

  • Supporter
  • ****
  • Posts: 636
  • Country: gb
Re: Home Brew Analog Computer System
« Reply #159 on: August 04, 2013, 03:54:22 AM »
BTW, could anyone suggest to me a more elegant way of converting a long integer (value ranging 0 to 360) into three seperate decimals (0 to 9) (hundreds [xh], tens [xt] and units [xu])? It should probably be obvious but it is getting close to 1am now.

One approach is to convert to BCD, then mask off nibbles - if you happen to have a library function for that, or an app note with efficient code for your uC (I presume this is in the 'pretend pot' firmware?), that might be reasonably elegant.

Another way to avoid the hit of the division and multiplication is to loop over it:

Code: [Select]
unsigned int input = 276;
char hundreds = 0, tens = 0, units = 0;

while (input >= 100)
{
    hundreds++;
    input -= 100;
}

while (input >= 10)
{
    tens++;
    input -= 10;
}

units = input;

/* should now have hundreds = 2, tens = 7, units = 6 */

Crude, but it's been effective for me in the past - faster than doing division with the library functions, at any rate.

Offline Chalky

  • Regular Contributor
  • *
  • Posts: 79
  • Country: nz
Re: Home Brew Analog Computer System
« Reply #160 on: August 04, 2013, 07:16:20 AM »
BTW, could anyone suggest to me a more elegant way of converting a long integer (value ranging 0 to 360) into three seperate decimals (0 to 9) (hundreds [xh], tens [xt] and units [xu])? It should probably be obvious but it is getting close to 1am now.
Hi there, I don't know your language, am assuming some sort of embedded C, but if you have a MOD operator or function available then you could so an equivalent of this VB.NET code (works with either positive or negative angle, just returns absolute digit):
Sub Main()
        Dim angle_of_rotation_X_axis As Long = -359
        Dim xh As Short = GetDigit(angle_of_rotation_X_axis, 100)
        Dim xt As Short = GetDigit(angle_of_rotation_X_axis, 10)
        Dim xu As Short = GetDigit(angle_of_rotation_X_axis, 1)
    End Sub

    Function GetDigit(value As Long, unit As Integer) As Short
        Return CShort(Int((Abs(value) Mod unit * 10) / unit))
    End Function
Cheers.

Offline GK

  • Super Contributor
  • ***
  • Posts: 1386
  • Country: au
Re: Home Brew Analog Computer System
« Reply #161 on: August 05, 2013, 08:01:45 PM »
OK, thanks for the replies. The language is C; code for a PIC uC using "Custom computer services" (CCS) PIC-C compiler.
Yes, it for my "pretend pot" firmware of the 3-D projection unit. I've written a lot of PIC C firmware but I am far from a professional code writer. Here is the complete program. It's functional and I think relatively tidy, but those who program for a living may very well see things that could be done better.
Also attached is the complete schematic of the final unit.

Quote

//   3-D PROJECTION UNIT
//   DATE         AUGUST/2013
//   AUTHOR      Glen Kleinschmidt
// VERSION      1.0
// DEVICE      PIC16F874


// ROM used: 1533 (37%)
// Largest free fragment is 2048
// RAM used: 24 (13%) at main() level
// 131 (69%) worst case


#include <16f874.h>
#device  ADC=10
#use delay(clock=4000000)
#use standard_io(A)
#use standard_io(B)
#use standard_io(C)
#use standard_io(D)
#FUSES HS,NOWDT,PUT,NOPROTECT,NOLVP,NOCPD,NOWRT,BROWNOUT


long int       angle_of_rotation_X_axis;
long int       angle_of_rotation_Y_axis;

int            address;
int            data;
int            shift;

int            xh;
int            xt;
int            xu;

int            yh;
int            yt;
int            yu;

int            sine_X;
int            cosine_X;

int            sine_Y;
int            cosine_Y;

short int      polarity_sine_X;
short int      polarity_cosine_X;

short int      polarity_sine_Y;
short int      polarity_cosine_Y;


read_angle_of_rotation_potentiometers(void)
{
   SET_ADC_CHANNEL(0);                                      // Set ADC MUX to channel 0 (pin RA0)
   delay_us(100);                                           // Wait for ADC-input MUX to settle
   angle_of_rotation_X_axis = (READ_ADC() / 2.8);           // Read X-rotation potentiometer(s) position and scale

   SET_ADC_CHANNEL(1);                                      // Set ADC MUX to channel 1 (pin RA1)
   delay_us(100);                                           // Wait for ADC-input MUX to settle
   angle_of_rotation_Y_axis = (READ_ADC() / 2.8);           // Read Y-rotation potentiometer(s) position and scale

   if (angle_of_rotation_X_axis > 360)                      // Limit X value (0-to-360 degrees)
   angle_of_rotation_X_axis = 360;                          //

   if (angle_of_rotation_Y_axis > 360)                      // Limit Y value (0-to-360 degrees)
   angle_of_rotation_Y_axis = 360;                          //
}


seven_segment_display_decode(void)
{
   int   sevenseg[10] = {2,158,36,12,152,72,192,30,0,24};   // Look-up table for 7-segment LED display decode

   xh = abs(angle_of_rotation_X_axis / 100);                // Decode decimal values (hundreds, tens & units)
   xt = angle_of_rotation_X_axis - (xh * 100);              // for X-axis angle-of-rotation digital display
   xu = xt - 10 * (abs(xt / 10));                           //
   xt = abs(xt / 10);                                       //

   yh = abs(angle_of_rotation_Y_axis / 100);                // Decode decimal values (hundreds, tens & units)
   yt = angle_of_rotation_Y_axis - (yh * 100);              // for Y-axis angle-of-rotation digital display
   yu = yt - 10 * (abs(yt / 10));                           //
   yt = abs(yt / 10);                                       //

   xh = sevenseg[xh];                                       // 3-digit 7-segment decode for X-axis angle-of-rotation display
   xt = sevenseg[xt];                                       //
   xu = sevenseg[xu];                                       //

   yh = sevenseg[yh];                                       // 3-digit 7-segment decode for Y-axis angle-of-rotation display
   yt = sevenseg[yt];                                       //
   yu = sevenseg[yu];                                       //

   output_low(pin_D4);                                      // Digital display digit latch lines low
   output_low(pin_D5);                                      //
   output_low(pin_D6);                                      //
   output_low(pin_D7);                                      //
   output_low(pin_B0);                                      //

   output_b(yu);                                            // Set "units" digit latch for Y-axis angle-of-rotation
   output_high(pin_B0);                                     //
   output_low(pin_B0);                                      //

   output_b(yt);                                            // Set "tens" digit latch for Y-axis angle-of-rotation
   output_high(pin_D7);                                     //
   output_low(pin_D7);                                      //

   output_b(yh);                                            // Set "hundreds" digit latch for Y-axis angle-of-rotation
   output_high(pin_D6);                                     //
   output_low(pin_D6);                                      //

   output_b(xu);                                            // Set "units" digit latch for X-axis angle-of-rotation
   output_high(pin_D5);                                     //
   output_low(pin_D5);                                      //

   output_b(xt);                                            // Set "tens" digit latch for X-axis angle-of-rotation
   output_high(pin_D4);                                     //
   output_low(pin_D4);                                      //

   output_b(xh);                                            // Put "hundreds" digit on 7-segment data bus
}


sincostable(void)
{
   int   sincos[91] = {0,  4,  8,  13, 17, 22, 26, 31, 35, 39,    // Look-up table for sine and cosine trig. function
                       44, 48, 53, 57, 61, 65, 70, 74, 78, 83,
                       87, 91, 95, 99, 103,107,111,115,119,123,
                       127,131,135,138,142,146,149,153,156,160,
                       163,167,170,173,177,180,183,186,189,192,
                       195,198,200,203,206,208,211,213,216,218,
                       220,223,225,227,229,231,232,234,236,238,
                       239,241,242,243,245,246,247,248,249,250,
                       251,251,252,253,253,254,254,254,254,254,
                       255};

   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

   if (angle_of_rotation_X_axis <= 360)                     // Read sine(x) table value for 271-to-360 degree segment
   {                                                        // Inverting for this segment (0 = true)
      sine_X = sincos[360 - angle_of_rotation_X_axis];      //
      polarity_sine_X = 0;                                  //
   }                                                        //

   if (angle_of_rotation_X_axis <= 270)                     // Read sine(x) table value for 181-to-270 degree segment
   {                                                        // Inverting for this segment (0 = true)
      sine_X = sincos[angle_of_rotation_X_axis - 180];      //
      polarity_sine_X = 0;                                  //
   }                                                        //

   if (angle_of_rotation_X_axis <= 180)                     // Read sine(x) table value for 91-to-180 degree segment
   {                                                        // Non-inverting for this segment (1 = false)
      sine_X = sincos[180 - angle_of_rotation_X_axis];      //
      polarity_sine_X = 1;                                  //
   }                                                        //

   if (angle_of_rotation_X_axis <= 90)                      // Read sine(x) table value for 0-to-90 degree segment
   {                                                        // Non-inverting for this segment (1 = false)
      sine_X = sincos[angle_of_rotation_X_axis];            //
      polarity_sine_X = 1;                                  //
   }                                                        //

   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

   if (angle_of_rotation_X_axis <= 360)                     // Read cosine(x) table value for 271-to-360 degree segment
   {                                                        // Non-inverting for this segment (1 = false)
      cosine_X = sincos[angle_of_rotation_X_axis - 270];    //
      polarity_cosine_X = 1;                                //
   }                                                        //

   if (angle_of_rotation_X_axis <= 270)                     // Read cosine(x) table value for 181-to-270 degree segment
   {                                                        // Inverting for this segment (0 = true)
      cosine_X = sincos[270 - angle_of_rotation_X_axis];    //
      polarity_cosine_X = 0;                                //
   }                                                        //

   if (angle_of_rotation_X_axis <= 180)                     // Read cosine(x) table value for 91-to-180 degree segment
   {                                                        // Inverting for this segment (0 = true)
      cosine_X = sincos[angle_of_rotation_X_axis - 90];     //
      polarity_cosine_X = 0;                                //
   }                                                        //

   if (angle_of_rotation_X_axis <= 90)                      // Read cosine(x) table value for 0-to-90 degree segment
   {                                                        // Non-inverting for this segment (1 = false)
      cosine_X = sincos[90 - angle_of_rotation_X_axis];     //
      polarity_cosine_X = 1;                                //
   }                                                        //

   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

   if (angle_of_rotation_Y_axis <= 360)                     // Read sine(y) table value for 271-to-360 degree segment
   {                                                        // Inverting for this segment (0 = true)
      sine_Y = sincos[360 - angle_of_rotation_Y_axis];      //
      polarity_sine_Y = 0;                                  //
   }                                                        //

   if (angle_of_rotation_Y_axis <= 270)                     // Read sine(y) table value for 181-to-270 degree segment
   {                                                        // Inverting for this segment (0 = true)
      sine_Y = sincos[angle_of_rotation_Y_axis - 180];      //
      polarity_sine_Y = 0;                                  //
   }                                                        //

   if (angle_of_rotation_Y_axis <= 180)                     // Read sine(y) table value for 91-to-180 degree segment
   {                                                        // Non-inverting for this segment (1 = false)
      sine_Y = sincos[180 - angle_of_rotation_Y_axis];      //
      polarity_sine_Y = 1;                                  //
   }                                                        //

   if (angle_of_rotation_Y_axis <= 90)                      // Read sine(y) table value for 0-to-90 degree segment
   {                                                        // Non-inverting for this segment (1 = false)
      sine_Y = sincos[angle_of_rotation_Y_axis];            //
      polarity_sine_Y = 1;                                  //
   }                                                        //

   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

   if (angle_of_rotation_Y_axis <= 360)                     // Read cosine(y) table value for 271-to-360 degree segment
   {                                                        // Non-inverting for this segment (1 = false)
      cosine_Y = sincos[angle_of_rotation_Y_axis - 270];    //
      polarity_cosine_Y = 1;                                //
   }                                                        //

   if (angle_of_rotation_Y_axis <= 270)                     // Read cosine(y) table value for 181-to-270 degree segment
   {                                                        // Inverting for this segment (0 = true)
      cosine_Y = sincos[270 - angle_of_rotation_Y_axis];    //
      polarity_cosine_Y = 0;                                //
   }                                                        //

   if (angle_of_rotation_Y_axis <= 180)                     // Read cosine(y) table value for 91-to-180 degree segment
   {                                                        // Inverting for this segment (0 = true)
      cosine_Y = sincos[angle_of_rotation_Y_axis - 90];     //
      polarity_cosine_Y = 0;                                //
   }                                                        //

   if (angle_of_rotation_Y_axis <= 90)                      // Read cosine(y) table value for 0-to-90 degree segment
   {                                                        // Non-inverting for this segment (1 = false)
      cosine_Y = sincos[90 - angle_of_rotation_Y_axis];     //
      polarity_cosine_Y = 1;                                //
   }                                                        //

   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}


RDACclock(void)                           // Strobe digipot clock line
{
   delay_us(100);
   output_high(pin_C1);
   delay_us(100);
   output_low(pin_C1);
   delay_us(100);
}


RDACaddress(void)                         // Shift the 3 address bits to digipot data line
{
   for (shift=0; shift<3; shift++)
   {
      output_bit(pin_C2, bit_test(address, 2 - shift));
      RDACclock();
   }
}


RDACdata(void)                            // Shift the 8 data bits to the digipot data line
{
   for (shift=0; shift<8; shift++)
   {
      output_bit(pin_C2, bit_test(data, 7 - shift));
      RDACclock();
   }
}


set_digital_potentiometers(void)
{
   output_low(pin_C0);                    // !CS low to initiate data transfer
   address = 0;                           // Set address for RDAC1
   RDACaddress();                         // Send the 3 address bits
   data = cosine_Y;                       // Set data value to cosine_Y
   RDACdata();                            // Send the 8 data bits
   RDACaddress();                         // Send the next daisy-chained 3 address bits
   data = cosine_X;                       // Set data value to cosine_X
   RDACdata();                            // Send the next daisy-chained 8 data bits
   output_high(pin_C0);                   // !CS high to complete data transfer

   delay_us(200);

   output_low(pin_C0);                    // !CS low to initiate data transfer
   address = 1;                           // Set address for RDAC2
   RDACaddress();                         // Send the 3 address bits
   data = sine_Y;                         // Set data value to sine_Y
   RDACdata();                            // Send the 8 data bits
   RDACaddress();                         // Send the next daisy-chained 3 address bits
   data = sine_X;                         // Set data value to sine_X
   RDACdata();                            // Send the next daisy-chained 8 data bits
   output_high(pin_C0);                   // !CS high to complete data transfer

   delay_us(200);

   output_low(pin_C0);                    // !CS low to initiate data transfer
   address = 2;                           // Set address for RDAC3
   RDACaddress();                         // Send the 3 address bits
   data = sine_Y;                         // Set data value to sine_Y
   RDACdata();                            // Send the 8 data bits
   RDACaddress();                         // Send the next daisy-chained 3 address bits
   data = sine_X;                         // Set data value to sine_X
   RDACdata();                            // Send the next daisy-chained 8 data bits
   output_high(pin_C0);                   // !CS high to complete data transfer

   delay_us(200);

   output_low(pin_C0);                    // !CS low to initiate data transfer
   address = 3;                           // Set address for RDAC4
   RDACaddress();                         // Send the 3 address bits
   data = cosine_Y;                       // Set data value to cosine_Y
   RDACdata();                            // Send the 8 data bits
   RDACaddress();                         // Send the next daisy-chained 3 address bits
   data = cosine_X;                       // Set data value to cosine_X
   RDACdata();                            // Send the next daisy-chained 8 data bits
   output_high(pin_C0);                   // !CS high to complete data transfer
}


set_sinecos_polarity(void)
{
   output_bit(pin_D1, bit_test(polarity_sine_X,   0));   // Connect polarity status to sine(x) amplifiers
   output_bit(pin_D2, bit_test(polarity_cosine_X, 0));   // Connect polarity status to cosine(x) amplifiers
   output_bit(pin_C3, bit_test(polarity_sine_Y,   0));   // Connect polarity status to sine(y) amplifier
   output_bit(pin_D0, bit_test(polarity_cosine_Y, 0));   // Connect polarity status to cosine(y) amplifier
}


MAIN()
{
   SETUP_ADC_PORTS(RA0_RA1_ANALOG_RA3_RA2_REF);    // Define ADC inputs: RA0 & RA1=inputs, RA2= -Vref, RA3= +Vref)
   SETUP_ADC(ADC_CLOCK_DIV_32);                    // Connect ADC internal clock

   loop:

      read_angle_of_rotation_potentiometers();     // This routine reads the X & Y angle-of-rotation potentiometers
      seven_segment_display_decode();              // This routine sets the digital display of the angles of rotation
      sincostable();                               // This routine sets the sine and cosine values for the digipots
      set_digital_potentiometers();                // This routine programs the digipots (daisy-chained serial)
      set_sinecos_polarity();                      // This routing sets the switchable inverting/non inverting
                                                   // amplifiers as required for 4-quadrant sine/cosine multiplication

   goto loop;                                      // Play it again Sam!
}

Here is the completed unit:


« Last Edit: August 05, 2013, 08:07:45 PM by GK »

Offline GK

  • Super Contributor
  • ***
  • Posts: 1386
  • Country: au
Re: Home Brew Analog Computer System
« Reply #162 on: August 05, 2013, 08:11:05 PM »
Here is a video of the unit in action, with a full 360 degrees of variable rotation of both X and Y axes. Once again I am using my Rössler Attractor signal generator as the source. I'm currently laying out a PCB for that one will post up the circuit details when done.

3D projection unit Rossler Attractor

Offline mamalala

  • Frequent Contributor
  • **
  • Posts: 630
  • Country: de
Re: Home Brew Analog Computer System
« Reply #163 on: August 06, 2013, 01:52:15 AM »
Hi Glen,

there are a few things you can do to improve the code wrt. the table-lookup, avoiding a lot of calculations and stuff. It is a tradeoff between ROM/FLASH usage for the table on one side, and codesize & execution speed on the other side. But since you are using the table a lot with lots of code calculating it, i guess the overall ROM/FLASH used will be less, plus a faster processing speed.

First, make the lookup-table 256 entries large. The native data size of the PIC is 8 bits, using that as table size allows for some really fast methods for reversing the lookup direction and inverting the value.

Then, the PIC has a 10-bit ADC. Use that! This will make things even simpler. To store the result, the PIC uses two registers, ADRESL and ADRESH. Configure the ADC to give you a right-justified result. (register ADCON1 bit ADFM set to 1). This will result in the lower 8 bits of the readout to be stored in ADRESL, and the upper 2 bits into the lower 2 bits of ADRESH.

The effect of this is that in ADRESH you get only the value 0, 1, 2 or 3 over the full range. Now, 4 values, 4 quadrants, a table with 256 entries and a ADRESL of 8 bits... I guess you can already start to see where this goes.

Now about how to access it using simple methods and the ADRESH bits of the result. The lookup table contains the first 90° of a sinewave here. If you invert the index value, you read that table backwards. That way you now have 180°. To decide wether to read forwards or backwwards, you use bit 0 of ADRESH. If it is set, you simply invert the index into the table.

In code this would look something like this:

Code: [Select]
if (ADRESH & 0x01)
{
    result = sintab[~ADRESL];
}
else
{
    result = sintab[ADRESL];
}

Now for the inverting of the read value of the lookup. You use bit 1 of ADRESL to do that. If it set to 1, you simply invert the value you just read from the lookup-table. Again, in code this looks like:

Code: [Select]
if (ADRESH & 0x02)
{
    result = ~result;
}

OK, this was just for sine lookup from the table. The bits of ADRESH for those 4 quadrants in sequence look like this:

Code: [Select]
00000000 - first sine quadrant
00000001 - second sine quadrant
00000010 - third sine quadrant
00000011 - fourth sine quadrant

Which is, of course, also the same sequence you get in ADRESH in 10 bit ADC mode when going from 0 to maximum.

If you want to read a cosine instead, you simply add 1 to ADRESH. The resulting sequence of ADRESH will then be:

Code: [Select]
00000001
00000010
00000011
00000100

This is OK because only the lowest two bits are evaluated anyways. Now, if you want to have the full sine or cosine inverted, you simply add another 2 to ADRESH. Doing all the above allows you to write a single routine that does this. With a little more bit-magic the polarity bits that you use to control the actual output can also be set. You can also combine those bits into a single byte instead of spreading it over four int's. There is the nifty thing of #define, which allows you to define things with a name.

Attached is a textfile with some ad-hoc example code of all that with lots of comments. Of course not tested for bugs, just to show you the overall process. Hope that helps. If you have any questions, let me know.

Greetings,

Chris

Offline mamalala

  • Frequent Contributor
  • **
  • Posts: 630
  • Country: de
Re: Home Brew Analog Computer System
« Reply #164 on: August 06, 2013, 02:56:11 AM »
What i forgot to mention. In my example i do invert the readout value _and_ set the negative-polarity bit. I just assumed that when you toggle the output pin that the DAC output is inverted after the OpAmps, that is 5V input becomes 0V and 0V input becomes -5V output when negative, while for the positive half 5V in becomes 5V out, and 0V in becomes 0V out (just as an example). If that is not the case, simple comment out the "result = ~result" line there.

Also, note that due to the way the lookuptable is read, the first and last value in it are duplicated at each start/end of a quadrant. That is, for example, the zero-crossing at 180° will become 0 at an ADC value of 511 (end of second quadrant) but also 0 at a ADC value of 512 (start of third quadrant). There are two ways to eliminate that error. One is to create the table so there is no 0° and 90°. So the sequence instead of, for example, 8-4-0 0-4-8 becomes 10-6-2 2-6-10. Another way is to limit the maximum value in the table, and then add a fixed value if you go negative. So if the sequence from the table would be 8-4-0 0-4-8 you simply add 4 if negative, and get 8-4-0 4-8-12 instead. Of course this means that the maximum value can not be 255 but must be 251 instead, to avoid an overflow when adding 4.

Personally i would prefer the first method. This ensures that the generated quadrants are symetrical. And at 8 bits for one quadrant you have a resolution of roundabout 0.35°, so the small "error" of not having complete 0° or 90° is just roundabout +/- 0.175°.

Greetings,

Chris


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf