Created

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

ADC -> PWM

View adc_to_pwm.c
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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
 
// Uncomment to enable debugging messages and values
#define DEBUG
 
uint16_t adc_readings[8];
uint8_t digital_output = 11; // (PCINT22/OC0A/AIN0)PD6, Arduino Digital Pin 11
 
uint16_t sample_0;
uint16_t sample_1;
uint16_t sinewave_length = 501;
 
uint32_t sinewave_data[] PROGMEM = { 0x80, 0x81, 0x83, 0x84, 0x86, 0x88, 0x89, 0x8B, 0x8C, 0x8E, 0x8F, 0x91, 0x93, 0x94, 0x96, 0x97, 0x99, 0x9B, 0x9C, 0x9E, 0x9F, 0xA1, 0xA2, 0xA4, 0xA5, 0xA7, 0xA8, 0xAA, 0xAB, 0xAD, 0xAE, 0xB0, 0xB1, 0xB3, 0xB4, 0xB6, 0xB7, 0xB9, 0xBA, 0xBC, 0xBD, 0xBE, 0xC0, 0xC1, 0xC2, 0xC4, 0xC5, 0xC7, 0xC8, 0xC9, 0xCA, 0xCC, 0xCD, 0xCE, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEE, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2, 0xF3, 0xF4, 0xF4, 0xF5, 0xF5, 0xF6, 0xF7, 0xF7, 0xF8, 0xF8, 0xF9, 0xF9, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xFD, 0xFD, 0xFC, 0xFC, 0xFC, 0xFB, 0xFB, 0xFB, 0xFA, 0xFA, 0xF9, 0xF9, 0xF8, 0xF8, 0xF7, 0xF7, 0xF6, 0xF5, 0xF5, 0xF4, 0xF4, 0xF3, 0xF2, 0xF1, 0xF1, 0xF0, 0xEF, 0xEE, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE9, 0xE8, 0xE7, 0xE6, 0xE5, 0xE4, 0xE3, 0xE2, 0xE1, 0xE0, 0xDF, 0xDE, 0xDC, 0xDB, 0xDA, 0xD9, 0xD8, 0xD7, 0xD6, 0xD4, 0xD3, 0xD2, 0xD1, 0xD0, 0xCE, 0xCD, 0xCC, 0xCA, 0xC9, 0xC8, 0xC7, 0xC5, 0xC4, 0xC2, 0xC1, 0xC0, 0xBE, 0xBD, 0xBC, 0xBA, 0xB9, 0xB7, 0xB6, 0xB4, 0xB3, 0xB1, 0xB0, 0xAE, 0xAD, 0xAB, 0xAA, 0xA8, 0xA7, 0xA5, 0xA4, 0xA2, 0xA1, 0x9F, 0x9E, 0x9C, 0x9B, 0x99, 0x97, 0x96, 0x94, 0x93, 0x91, 0x8F, 0x8E, 0x8C, 0x8B, 0x89, 0x88, 0x86, 0x84, 0x83, 0x81, 0x80, 0x7E, 0x7C, 0x7B, 0x79, 0x77, 0x76, 0x74, 0x73, 0x71, 0x70, 0x6E, 0x6C, 0x6B, 0x69, 0x68, 0x66, 0x64, 0x63, 0x61, 0x60, 0x5E, 0x5D, 0x5B, 0x5A, 0x58, 0x57, 0x55, 0x54, 0x52, 0x51, 0x4F, 0x4E, 0x4C, 0x4B, 0x49, 0x48, 0x46, 0x45, 0x43, 0x42, 0x41, 0x3F, 0x3E, 0x3D, 0x3B, 0x3A, 0x38, 0x37, 0x36, 0x35, 0x33, 0x32, 0x31, 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x11, 0x10, 0x0F, 0x0E, 0x0E, 0x0D, 0x0C, 0x0B, 0x0B, 0x0A, 0x0A, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x0A, 0x0A, 0x0B, 0x0B, 0x0C, 0x0D, 0x0E, 0x0E, 0x0F, 0x10, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x31, 0x32, 0x33, 0x35, 0x36, 0x37, 0x38, 0x3A, 0x3B, 0x3D, 0x3E, 0x3F, 0x41, 0x42, 0x43, 0x45, 0x46, 0x48, 0x49, 0x4B, 0x4C, 0x4E, 0x4F, 0x51, 0x52, 0x54, 0x55, 0x57, 0x58, 0x5A, 0x5B, 0x5D, 0x5E, 0x60, 0x61, 0x63, 0x64, 0x66, 0x68, 0x69, 0x6B, 0x6C, 0x6E, 0x70, 0x71, 0x73, 0x74, 0x76, 0x77, 0x79, 0x7B, 0x7C, 0x7E, 0x7F };
 
// Interrupt service routine called to grab ADC readings
ISR(TIMER0_COMPA_vect)
{
// Start ADC conversion
ADCSRA |= (1 << ADSC);
}
 
// Interrupt service routine called to generate PWM compare values
ISR(TIMER1_COMPA_vect)
{
uint32_t sensor_value_0 = adc_readings[0];
uint32_t sample_value_0 = pgm_read_byte(&sinewave_data[sample_0]);
uint32_t sine_0 = (sample_value_0 * sensor_value_0) >> 10;
 
uint32_t sensor_value_1 = 1024;
uint32_t sample_value_1 = pgm_read_byte(&sinewave_data[sample_1]);
uint32_t sine_1 = (sample_value_1 * sensor_value_1) >> 10;
 
OCR2A = (sine_0 + sine_1) >> 1;
 
sample_0 += 2;
sample_1 += 3;
 
if (sample_0 >= sinewave_length) {
sample_0 = 0;
}
 
if (sample_1 >= sinewave_length) {
sample_1 = 0;
}
} // END - Interrupt service routine called to generate PWM compare values
 
// ADC Conversion Complete interrupt service routine
ISR(ADC_vect)
{
static uint8_t firstTime = 1;
static uint8_t low_val, high_val;
uint8_t current_pin;
 
low_val = ADCL;
high_val = ADCH;
if (firstTime == 1) {
firstTime = 0;
 
} else {
// Get current reading from MUX
current_pin = (ADMUX & 0xF);
// Store reading in readings array
adc_readings[current_pin] = (high_val << 8) | low_val;
// Clear MUX bits
ADMUX &= 0xF0;
 
// Unless we're at the top pin, increment the MUX unit, otherwise leave it cleared at 0
if (current_pin < 7) {
ADMUX |= (current_pin + 1);
}
 
#ifdef DEBUG
// Toggle LED pin
PORTD ^= (1 << 7);
#endif
 
}
} // END - ADC Conversion Complete interrupt service routine
 
 
void setup()
{
// Setup digital_output for PWM output
DDRD |= _BV(6);
 
// Clear ADC reading array
memset(adc_readings, 0, sizeof(adc_readings));
 
// Disable global interrupts
cli();
 
#ifdef DEBUG
// Setup serial port for printing debug messages
Serial.begin(9600);
 
// Setup LED pin (PD7, Arduino Digital Pin 7) for testing
DDRD |= _BV(7);
// Set LED pin high
PORTD |= _BV(7);
#endif
 
/***************************************************************************
** Timer/Counter0 Configuration (used for triggering ADC) **
***************************************************************************/
 
// Setup Timer/Counter0 Control Registers (TCCR0) with the following options:
// Timer/Counter Mode of Operation | CTC
// TOP | OCRA
// Update of OCR1x at | Immediate
// TOV1 Flag Set on | MAX
TCCR0A = (TCCR0A & ~_BV(WGM00)) | _BV(WGM01);
TCCR0B &= ~_BV(WGM02);
 
// Setup clock prescaler for TCCR0 as clk_IO/1024
TCCR0B = (TCCR0B & ~_BV(CS01)) | _BV(CS02) | _BV(CS00);
 
// Set compare value (OCR0A) to value equivalent to:
// C = Compare value
// F_CPU = Core clock rate
// F_S = Desired ADC sampling rate (in Hertz)
// PS = Prescaler value
// N = Number of ADC channels
// C = F_CPU / (F_S * N * PS)
OCR0A = F_CPU / (50 * 8 * 1024); // 50Hz x 8 channels x 1024 prescaler
 
// Enable Timer/Counter0 Output Compare A Match Interrupt
TIMSK0 |= _BV(OCIE0A);
 
/***************************************************************************
** END - Timer/Counter0 Configuration **
***************************************************************************/
 
/***************************************************************************
** Timer/Counter1 Configuration (used for setting PWM compare values) **
***************************************************************************/
 
// Setup Timer/Counter1 Control Registers (TCCR1) with the following options:
// Timer/Counter Mode of Operation | CTC
// TOP | OCR1A
// Update of OCR1x at | Immediate
// TOV1 Flag Set on | MAX
TCCR1A &= ~(_BV(WGM11) | _BV(WGM10)); // Clear bits WGM11 and WGM10 in TCCR1A
TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12); // Clear bit WGM13 and set bit WGM12 in TCCR1B
 
// Setup clock prescaler for TCCR1 as clk_IO/8
TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS10))) | _BV(CS11);
 
// Set compare value (OCR1A) to value equivalent to:
// C = Compare value
// PS = Prescaler value
// N = Number of samples in wavetable read by interrupt routine
// B = Base frequency gained by reading one sample from wavetable per call of interrupt routine
/ F_CPU = Core clock frequency
// C = F_CPU / (PS * B * N)
uint8_t base_frequency = 100;
uint16_t tccr1_prescaler = 8;
// Clock Speed / Target Interrupt Frequency (4kHz)
OCR1A = F_CPU / ((uint32_t) tccr1_prescaler * (uint32_t) base_frequency * (uint32_t) sinewave_length);
 
// Enable Timer/Counter1 Output Compare A Match Interrupt
TIMSK1 |= _BV(OCIE1A);
 
sample_0 = 0;
 
/***************************************************************************
** END - Timer/Counter1 Configuration **
***************************************************************************/
 
/***************************************************************************
** Timer/Counter2 Configuration (used for generating PWM) **
***************************************************************************/
 
// Setup Timer/Counter2 Control Registers (TCCR2) with the following options:
// Mode | Fast PWM
// TOP | 0xFF
// Update of compare register | BOTTOM
// TOV Flag Set on | MAX
TCCR2A |= _BV(WGM21) | _BV(WGM20); // Set WGM21 and WGM20 bits in TCCR2A
TCCR2B &= ~_BV(WGM22); // Clear bit WGM22 in TCCR2B
 
// Setup Timer/Counter2 Control Registers (TCCR2) in Compare Output Mode with the following options:
// Clear OC2A on Compare Match, set OC2A at BOTTOM, (non-inverting mode).
TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0); // Set COM2A1 and clear COM2A0 bit in TCCR2A
 
// Setup Timer/Counter2 Control Registers (TCCR2) in Compare Output Mode with the following options:
// Normal port operation, OC2B disconnected.
TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0)); // Clear COM2B0 and COM2B1 bits in TCCR2A
 
// Setup clock prescaler for Timer 2 (TCCR2) as clk_IO/8
// The highest frequency generated by our wavetable (in the future) is planned to be 500Hz.
// Thus, PWM base frequency / PWM resolution (255) must be at least 1kHz.
TCCR2B = (TCCR2B & ~(_BV(CS22) | _BV(CS20))) | _BV(CS21);
 
OCR2A = pgm_read_byte(&sinewave_data[0]);
 
/***************************************************************************
** END - Timer/Counter2 Configuration **
***************************************************************************/
 
/***************************************************************************
** ADC Configuration **
***************************************************************************/
 
// Set ADC reference to AVCC
ADMUX |= _BV(REFS0);
// Initialise ADC pointing to channel 2
ADMUX |= _BV(MUX1);
 
// Enable ADC conversion complete interrupt (ADC_vect)
ADCSRA |= _BV(ADIE);
// Set ADC prescaler to 128 - 125kHz sample rate @ 16MHz, 62.5kHz sample rate @ 8MHz
ADCSRA |= _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
 
// Enable ADC
ADCSRA |= _BV(ADEN);
// Start an initial ADC conversion
ADCSRA |= _BV(ADSC);
 
// Wait for conversion to complete, then clear interrupt flag
while(ADCSRA & _BV(ADSC));
ADCSRA |= _BV(ADIF);
 
/***************************************************************************
** END - ADC Configuration **
***************************************************************************/
 
// Enable global interrupts
sei();
}
 
void loop() {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.