Проблемы в AVR C, объединяющие показания АЦП для генерации выхода ШИМ - PullRequest
2 голосов
/ 23 октября 2011

Я пишу программу для ATMega328P, которая будет считывать показания с нескольких каналов АЦП, объединять их в один сигнал и выводить этот сигнал через ШИМ.

Я успешно отключил опросы АЦП на частоту 50 Гц на канал в режиме единого преобразования. Я использую Timer / Counter2 для генерации PWM и Timer / Counter1 для выполнения вычислений, которые мне нужно сделать, чтобы установить значения сравнения для Timer / Counter2. Это ISR для таймера / счетчика 1:

// Interrupt service routine called to generate PWM compare values
ISR(TIMER1_COMPA_vect)
{
    // Grab most recent ADC reading for ADC0
    uint32_t sensor_value_0 = adc_readings[0];

    // Get current value for base waveform from wavetable stored in sinewave_data
    uint32_t sample_value_0 = pgm_read_byte(&sinewave_data[sample_0]);

    // Multiply these two values together
    // In other words, use the ADC reading to modulate the amplitude of base wave
    uint32_t sine_0 = (sample_value_0 * sensor_value_0) >> 10;

    // Do the same thing for ADC2    
    uint32_t sensor_value_1 = adc_readings[1];
    uint32_t sample_value_1 = pgm_read_byte(&sinewave_data[sample_1]);
    uint32_t sine_1 = (sample_value_1 * sensor_value_1) >> 10;

    // Add channels together, divide by two, set compare register for PWM
    OCR2A = (sine_0 + sine_1) >> 1;

    // Move successive ADC base waves through wavetable at integral increments
    // i.e., ADC0 is carried by a 200Hz sine wave, ADC1 at 300Hz, etc.
    sample_0 += 2;
    sample_1 += 3;

    // Wrap back to front of wavetable, if necessary
    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

Моя проблема в том, что у меня нет выхода ШИМ. Если я установлю sensor_value_0 или sensor_value_1 на 1024 и оставлю другой sensor_value_ для чтения с АЦП, я получу одну компонентную волну с полной амплитудой и амплитудно-модулированную компонентную волну. Однако, если я выберу другое значение для жестко запрограммированной амплитуды, мне не повезет (например, 1023). Любые другие значения не дают мне выход ШИМ. Если бы я установил оба sensor_value_ s для просмотра одного и того же канала АЦП, я бы ожидал двухкомпонентные волны, амплитуды которых модулированы одинаково. Вместо этого я не получаю выход ШИМ. Что меня больше всего смущает, так это то, что если я выберу значение для жестко закодированной амплитуды, которое является точной степенью двойки, все будет хорошо.

В целом, из-за степени двойственности мне кажется, что это немного сложная проблема, которую я не вижу. Ты видишь, что я, должно быть, явно упустил? Буду признателен за любые советы!

(я разместил весь мой источник здесь , чтобы держать вещи как можно более аккуратными на SO.)

Ответы [ 2 ]

0 голосов
/ 15 ноября 2011

@ Деврин, я ценю ответ, но просто манипулирование типами не сделало это для меня.Вот что я в итоге сделал:

uint8_t sine_0 = (pgm_read_byte(&sinewave_data[sample_0]) >> 5) * (adc_readings[1] >> 5);
uint8_t sine_1 = (pgm_read_byte(&sinewave_data[sample_1]) >> 5) * (adc_readings[2] >> 5);
OCR2A = (sine_0 >> 1) + (sine_1 >> 1);

По сути, я сделал все свои изменения немедленно, вместо того, чтобы ждать до последней минуты.К сожалению, я теряю много точности, но, по крайней мере, код работает как положено.Теперь я начну проверять, чтобы найти причину моих проблем.

0 голосов
/ 26 октября 2011

Ваша проблема может быть вызвана архитектурой AVR, на которой вы разрабатываете.ATMega328p имеет 8-битные регистры, похожие на большинство других чипов AVR.Это означает, что значения 32b, с которыми вы работаете, должны храниться в памяти компилятором и разбиваться на четыре отдельных регистра каждый раз, когда вы выполняете арифметику с ними.На самом деле, нет никаких арифметических инструкций, которые выполняются более чем на одном регистре одновременно, поэтому я действительно не уверен, что делает компилятор!

Мне было бы интересно узнать, что такое дизассемблирование вашего кодаесть, но я предполагаю, что gcc использует инструкцию MUL для выполнения кода sample_value_0 * sensor_value_0.Эта инструкция работает с двумя значениями 8b и выдает значение 16b, поэтому я не удивлюсь, если причина, по которой вы видите странную зависимость от кратных двух, приводит к результатам.

Я бы сказал, попробуйте переработать этоблок кода путем изменения типов данных переменных.Используйте uint8_t для sensor_value_* и sample_value_* и uint16_t для sine_*.Затем, чтобы убедиться, что все вписывается в регистр 8b OCR2A, измените присвоение на что-то вроде:

OCR2A = (sine_0 + sine_1) & 0xFF;
...