The sine and triangle waves are quite clean, the square and sawtooth waves less so. I wonder if the MCP4921 doesn't like big voltage swings. Here are some sample outputs:
Square waves work to about 20kHz, the others to less, depending on how many samples you use.
And here is the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | // Delay-based version of the DAC driver. #include "SPI.h" // Waveform: define only one of the following. #define SQUARE //#define SAWTOOTH //#define TRIANGLE //#define SINE // SS pin for the DAC. #define DAC1SS 10 // Mask to select SS PIN on port b. #define DAC1SSHI B100 #define DAC1SSLO B11111011 // Pin and mask for LDAC pin. #define LDAC 9 #define LDACHI B10 #define LDACLO B11111101 // Range of values. #define MIN_VALUE 0 #define MAX_VALUE 0xfff // The desired frequency in Hz and the period in microseconds. const unsigned long freq = 100UL; const unsigned long period = 1000000 / freq; // How many slices we divide the period into and the period of each. #ifdef SQUARE const unsigned int samples = 2; #else const unsigned int samples = 256; #endif const unsigned long sample_period = period / samples; const double pi = 3.1415926; // We will precompute all the sample values and store them here. // This allows the main loop to run in a fixed time. // Initialized in setup(). unsigned int* sample_values; void SetValue(unsigned long value) { PORTB &= DAC1SSLO; if (value > MAX_VALUE) { value = MAX_VALUE; } byte data = (highByte(value) & 0x0f) | 0x30; SPI.transfer(data); data = lowByte(value); SPI.transfer(data); PORTB |= DAC1SSHI; } // Number of millisecond and microseconds between actions. // Microseconds must be under 16384. // Computed in setup. unsigned int millis_delay; unsigned int micros_delay; void setup() { pinMode(DAC1SS, OUTPUT); pinMode(LDAC, OUTPUT); SPI.begin(); SPI.setBitOrder(MSBFIRST); sample_values = new unsigned int[samples]; #ifdef SQUARE sample_values[0] = MIN_VALUE; sample_values[1] = MAX_VALUE; #endif #ifdef SAWTOOTH float value = (float)MIN_VALUE; float sample_step = ((float)MAX_VALUE - MIN_VALUE)/(samples-1); for (int i = 0; i < samples; ++i) { sample_values[i] = (unsigned int)(value + 0.5); value += sample_step; } #endif #ifdef TRIANGLE unsigned int s2 = samples/2; float value = (float)MIN_VALUE; float sample_step = ((float)MAX_VALUE - MIN_VALUE)/s2; for (int i = 0; i < s2; ++i) { sample_values[i] = (unsigned int)(value + 0.5); value += sample_step; } for (int i = s2; i < samples; ++i) { sample_values[i] = (unsigned int)(value + 0.5); value -= sample_step; } #endif #ifdef SINE float value = (float)MIN_VALUE; double range = ((double)MAX_VALUE - MIN_VALUE)/2; for (int i = 0; i < samples; ++i) { double s = sin((2 * pi * i) / samples); // We have to raise the value from [-x,x] to [0,2x] sample_values[i] = (unsigned int)((s + 1) * range + MIN_VALUE + 0.5); } #endif // Set delays with a little extra for the computation time. unsigned long slopped_period = sample_period - 15; millis_delay = slopped_period / 1000; micros_delay = slopped_period % 1000; } // Current sample index unsigned int sample_number = 0; void loop() { // Toggle LDAC to set the new value. PORTB &= LDACLO; unsigned long toggle_time = micros(); PORTB |= LDACHI; sample_number = ++sample_number % samples; SetValue(sample_values[sample_number]); // Delay until the next action. delay(millis_delay); delayMicroseconds(micros_delay); } |
No comments:
Post a Comment