Как использовать AVR ADC с умножителем?

Я изучаю программирование микроконтроллера AVR, и в настоящее время у меня есть простая светодиодная мигалка, настроенная на ATtiny26.

Потенциометр на 10 кОм подключен к ADC7 (вывод 7) в соответствии с примером в части 1 этого руководства . Выходные контакты порта B подключены к светодиодам через транзисторы в мультиплексированном массиве. (Схема прекрасно работает без участия АЦП.)

Чтобы проверить/обучиться, я мигаю одним светодиодом и жду количество миллисекунд, равное тому, что считывается с АЦП. У меня проблема, когда я пытаюсь умножить значение для реализации более длительных задержек.

Я думал, что проблема в том, что переменная, в которой я храню ADCH, была, intно должна быть чем-то другим, потому что это 8-битный микроконтроллер и intможет быть только 0-255. Однако замена longне сработала.

Вот код, который работает:

#define F_CPU 1000000UL
#define ADC_VREF_TYPE = 0x40
int SD = 1;
int main()
{
    // Set port A to output (1)
    DDRA = 0b11111111;
    // Set port B, pin 3, to input (0)
    DDRB &= ~(1 << PB3);
    // Set control and status register
    ADCSR = 0b11100111;
    // Set ADC multiplexer selection register
    ADMUX  = 0b00100111;

    while (1)
    {
        // Set SD (step delay) to value from ADC
        SD = ADCH;
        // Turn on LED (it is a multiplexer, so two pins are used)
        PORTA = 0b00010001;
        // Wait for SD milliseconds
        milli_delay(SD);
        // Turn off LED
        PORTA = 0b00000000;
        // Wait for SD milliseconds
        milli_delay(SD);
    }
}

Проблема возникает, когда я пытаюсь использовать такой множитель:

SD = ADCH * 10;

При этом светодиод будет быстро мигать на одном конце поворота потенциометра, но будет гореть постоянно и оставаться таким в какой-то момент при повороте потенциометра на более медленные скорости (большие значения ADCH); по существу «блокируется» и требует сброса, когда потенциометр снова находится в исходном положении. Я предполагаю, что я пытаюсь сохранить значение, превышающее числовой тип, но я не могу преодолеть это, изменив тип переменной. Я пробовал long, uint32_tно я действительно не понимаю, что числовые типы держат в этом микро.

В: Что мне нужно сделать, чтобы реализовать задержки в диапазоне от 0 до, скажем, 2000 миллисекунд?

К вашему сведению, у меня есть следующие библиотеки:

#include <inttypes.h>
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <math.h>

Редактировать:

Я поместил _delay_ms(x)прямо в свой пример, чтобы не публиковать функции, которые я фактически использую, какой вызов _delay_ms(1)внутри цикла. Я думал, что это будет более лаконично, но, как указал один ответ, на самом деле это оказалось ошибкой.

Можете ли вы опубликовать схему вашей настройки транзистора / мультиплексора? Кто знает, может кто-нибудь увидит там пробку.
Я сделал схему сначала на миллиметровой бумаге от руки, а буквально вчера переделал в Eagle. Если вы считаете, что это полезно, я включу это; хотя, когда я игнорирую АЦП и использую жестко закодированное значение, все работает, как и ожидалось. Кроме того, если я использую значение АЦП, не пытаясь его умножить, я получаю хорошие результаты (задержки 0-255 миллисекунд).

Ответы (2)

intможет быть только 0-255

Это неправильно. В Cязыке программирования intвсегда имеет ширину не менее 16 бит, даже на 8-битных микропроцессорах. A charимеет ширину 8 бит.

Но при использовании inttypes.hя настоятельно рекомендую использовать явные типы uint8_t, такие как int16_tили uint32_t.

Ваша настоящая ошибка заключается в использовании _delay_ms()с переменным параметром. Это не разрешено, так как это макрос , включающий вычисления с плавающей запятой - по крайней мере, в avr gcc. Вы должны использовать что-то вроде этого:

void DelayMs(int delay) {
  while(delay--) _delay_ms(1);
}
Хех, забавно то, что я изменил строки _delay_ms() для публикации своего вопроса, так как на самом деле они являются вызовами функций, которые делают именно то, что вы указали для avr gcc. Я изменил их, думая, что будет меньше кода для публикации.
Если значение int равно 16 битам, то, я думаю, ADCH * 10будет работать безупречно. Любое дополнительное понимание того, почему это может быть не так?
Ребята, вы уверены в этом макросе?! util /delay.h , на который я смотрю, определяет _delay_ms() как обычную функцию.
Я думаю, что это как-то связано с использованием языка или что-то в этом роде. См. Stackoverflow.com/q/12095890/161052 .
Решением моей проблемы оказались настройки оптимизации на компиляторе. Тем не менее, ваша информация была очень полезной и помогла мне встать на правильный путь. Спасибо!

Я думаю, проблема в том, что у вас есть результат ADC, выровненный по левому краю в регистре результатов:

введите описание изображения здесь

Код ниже показывает, что вы установили для ADLAR значение 1 (выравнивание по левому краю):

 // Set ADC multiplexer selection register
    ADMUX  = 0b00100111;

Это означает, что когда вы читаете значение АЦП в ADCH, вы полностью пропускаете два младших бита (LSB). Это означает, что вы читаете число, как если бы оно было в 4 раза меньше (сдвиг на два бита вправо).

То, что вы хотите сделать, это установить его на ПРАВИЛЬНОЕ оправдание:

 // Set ADC multiplexer selection register
    ADMUX  = 0b00000111;

и прочитайте значение вашего АЦП из ADCL (нижний регистр результата АЦП):

while (1)
{
    // Set SD (step delay) to value from ADC
    SD = ADCL;
    // Turn on LED (it is a multiplexer, so two pins are used)
    PORTA = 0b00010001;
    // Wait for SD milliseconds
    milli_delay(SD);
    // Turn off LED
    PORTA = 0b00000000;
    // Wait for SD milliseconds
    milli_delay(SD);
}
Это не помогло, извините. Если я использую значение ADCH и выравниваю результат по левому краю, я получаю ожидаемые значения (0-255). Я проверил это, написав небольшой код, который зажигает количество светодиодов, равное 1/16 значения. Поворот потенциометра приводит к ожидаемому свечению светодиодов. Я подозреваю проблему с кодом/языком/использованием, см. stackoverflow.com/q/12095890/161052