Доброго времени суток.
Есть задача: управление цветом RGB-ленты посредством трех потенциометров. Сделал на Attiny261A, используя три АЦП и три аппаратных ШИМа. Все, вроде работает, но не совсем так, как рассчитывал.
Программа:
Спойлер
Код: Выделить всё
/*
Используется МК Attiny261A
на частоте 8 МГц
Fuse-биты выставляем следующим оброазом:
(1 - unprogrammed, 0 - programmed)
Bit Low Bits High Bits
7 CKDIV8 1 RSTDISBL 1
6 CKOUT 1 DWEN 1
5 SUT1 1 SPIEN 0
4 SUT0 0 WDTON 1
3 CKSEL3 0 EESAVE 1
2 CKSEL2 0 BODLEVEL2 1
1 CKSEL1 1 BODLEVEL1 0
0 CKSEL0 0 BODLEVEL0 0 SELFPRGEN 1
*/
//Подключаемые файлы
//Файл стандартных функций ввода/вывода
#include <avr/io.h>
//Файл стандартных функций обработки прерываний
#include <avr/interrupt.h>
//Файл стандартных функций таймера WatchDog
#include <avr/wdt.h>
//Макроопределения
#define MAX_COLOR 255
//Прототипы функций
//Процедура инициализации АЦП
void init_ADC(void);
//Процедура инициализации входов/выходов
void init_IO(void);
//Процедура инициализации Т/С1
void init_TC1(void);
//Глобальные переменные
//Значения заданий на каналы ШИМ
volatile unsigned char value_ADC[3] = {1, 1, 1};
//Основная функция
void main(void)
{
//Инициализация АЦП
init_ADC();
//Инициализация Т/С1
init_TC1();
//Инициализация входов/выходов
init_IO();
//Разрешение работы WatchDog с периодом в 1 с
wdt_enable(WDTO_1S);
//Разрешение прерываний
sei();
//Рабочий цикл
while(1)
{
//Получение заданий ШИМ
//Канал 0
OCR1A = value_ADC[0];
//Канал 1
OCR1B = value_ADC[1];
//Канал 2
OCR1D = value_ADC[2];
//Сброс таймера WatchDog
wdt_reset();
}
}
//Функции
//Процедура инициализации АЦП
void init_ADC(void)
{
//Опорное напряжение питания,сдвиг влево вкл., источник ADC0
ADMUX |= (1 << ADLAR);
//Предделитель АЦП 128,
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
//вкл. АЦП,, старт АЦП, прерывание по АЦП вкл.,
ADCSRA |= (1 << ADEN) | (1 << ADSC) | (1 << ADIE);
}
//Процедура инициализации входов/выходов
void init_IO(void)
{
//PB5, PB3, PB1 - выходы ШИМ
DDRB |= (1 << PB5) | (1 << PB3) | (1 << PB1);
}
//Процедура инициализации Т/С1
void init_TC1(void)
{
//В режиме FastPWM "0" при совпадении на OC1A и OC1B, вкл. ШИМ на них
TCCR1A |= (1 << COM1A1) | (1 << COM1B1) | (1 << PWM1A) | (1 << PWM1B);
//Предделитель 1
TCCR1B |= (1 << CS10);
//В режиме FastPWM "0" при совпадении на OC1A и OC1B - теневые биты
TCCR1C |= (1 << COM1A1S) | (1 << COM1B1S);
//В режиме FastPWM "0" при совпадении на OC1D, вкл ШИМ на OC1D
TCCR1C |= (1 << COM1D1) | (1 << PWM1D);
//Режим FastPWM
TCCR1D &= ~((1 << WGM11) | (1 << WGM10));
//Количество дискрет ШИМ
OCR1C = MAX_COLOR;
//Начальные значения регистров сравнения
OCR1A = 0;
OCR1B = 0;
OCR1D = 0;
}
//Вектор прерывания по АЦП
ISR(ADC_vect)
{
//Если не зайдествованы каналы 1 или 2 АЦП
if ((ADMUX & ((1 << MUX0) | (1 << MUX1))) == 0)
{
//Получение задания канала 0
value_ADC[0] = ADCH;
//Переключение канала АЦП на 1
ADMUX |= (1 << MUX0);
}
else
//Если не зайдествован канал 2 АЦП
if ((ADMUX & (1 << MUX1)) == 0)
{
//Получение задания канала 1
value_ADC[1] = ADCH;
//Переключение канала АЦП на 2
ADMUX &= ~(1 << MUX0);
ADMUX |= (1 << MUX1);
}
else
{
//Получение задания канала 2
value_ADC[2] = ADCH;
//Переключение канала АЦП на 0
ADMUX &= ~(1 << MUX1);
};
//Старт АЦП
ADCSRA |= (1 << ADSC);
}
Проблема в том, что если не инициировать значение на D-канале ШИМа единицей (любым ненулевым числом), он не работает до тех пор, пока значение АЦП, которое ему передается, не изменится. Т.е. при volatile unsigned char value_ADC[3] = {0, 0, 0} (значения с АЦП, передающие в OCR1A, OCR1B и OCR1D соответственно) в программе и, например, 127 на выходе соотв. АЦП, на выводе OC1D висит ноль, пока значение с АЦП не поменяется на 126 или 128. Если прописать volatile unsigned char value_ADC[3] = {1, 1, 1} в программе, все работает без проблем. В принципе, мне и так норм, но просто интересно, почему так. Вроде как разницы между A, B и D ШИМами быть не должно, а она есть.