В общем купил мото, и решил сделать на него в дополнение к приборке типа "бортовой комп". По начальной задумке я хотел тупо выводить номер передачи на 7ми сегментник по 1 проводу.
Как работает? Очень просто, в КПП есть вал который поворачивается на определенный угол, соответствующий своей передаче, проградуировав и разметив углы, сделал не хитрую плату с резисторными делителями. В итоге когда переключается передача - переключается резисторный делитель и напряжение выходное соответственно тоже меняется.
Здесь мне на помощь пришло АЦП и гора условий в коде (самый первый мой шаг в изучении СИ и AVR). Проблем особо не возникло, все работало как часы.
Но мне этого МАЛО! Учитывая что контроллер Atmega169 и у него ого-го-го ног и ресурсов, значит можно еще что-то впинать.
Следующим шагом были часы, нашел коды готовых, поизучал, поизучал асинхронный таймер, понял что это не очень сложно и можно в любой момент код готовых часов воткнуть к себе и временно забил.
Во время изучения часов я решил что не плохо было бы еще показывать напряжение бортовой сети, остаток топлива (емкостный датчик), температуру воздуха и температуру двигателя.
Что касается емкостного датчика, решил временно отложить т.к. бОльшая часть схемы там будет на аналоговых компонентах, а возможности разработать и собрать схему пока нет((
Поэтому взялся вплотную за термометры и вольтметр.
Как я хочу это реализовать:
В контроллере есть куча ног куда можно переключать АЦП. На каждую ногу по своему аналоговому датчику (да, датчики температуры будут аналоговым не цифровым, никаких I2C). Всего будет задействовано 4 входа. Вход под КПП, 2 входа термометры и 1 вход под вольтметр.
Переключение должно происходить по нажатию на кнопку.
Для кнопки завел свою переменную, которая считает нажатия и с помощью switch case ставит в соответствие количеству нажатий кнопки вывод откуда цифровать значение. Вот здесь то я и столкнулся с проблемой.
Думаю стоит перейти к коду. по ходу буду пытаться объяснить )
Самое начало я пожалуй пропущу, где объявлены всякие переменные и циклы в которых крутится прога по прервыниям
Это важный кусок прерывания. Суть такая- запустили преобразование, АЦП оцифровал, поднял флаг, ушел в прерывание, в прерывании записал свое значение в переменную temp, и записал 1 в переменную FLAG. Переменная FLAG понадобится дальше, она служит для отслеживания завершилось ли преобразование АЦП.
Код: Выделить всё
// прерывание по окончанию преобразования АЦП
ISR (ADC_vect)
{
temp=ADC;
FLAG=1;
}
Настроил порты под сегменты
Запустил таймер
Настроил АЦП (надеюсь правильно настроил)
В начальный момент АЦП должно ыбть заглушено а потом в основном коде запустаться по необходимости.
Должно так работать- запустили АЦП оно оцифровало, записало в temp, сбросилось на 0 и ждет следующего запуска. Но работает явно не так
Код: Выделить всё
// Главная функция
int main (void)
{
DDRB = 0xFF; // выходы на ОА
PORTB = 0x00; // ноль на выходе
DDRD = 0xFF; // выходы на сегменты
PORTD = 0x00; // ноль на выходе
// настройка таймера
TCCR2A |= (1 << CS21); //| (1<<CS20); // делитель
TIMSK2 |= (1 << TOIE2); // разрешение прерывания по таймеру
// настройка АЦП
ADCSRA |= (1 << ADEN) // разрешение АЦП
|(0 << ADSC) // Запуск преобразования
|(0 << ADATE) // непрерывный режим работы
|(1 << ADPS2)|(1 << ADPS1) // Ïделитель на 64 (частота АЦП 125kHz)
|(1 << ADIE); // разрешение прерываний от АЦП
sei(); //глобально разрешены прерывания
Прошу заметить что порт А я нигде не конфигурировал на вход, но как ни странно кнопка работает, может кто объяснить такой прикол?
Код: Выделить всё
while(1)
{
if ((PINA & 0x1) == 0) // если нажата кнопка на PA0.
{
_delay_ms (50);
// устранение дребезга
if ((PINA & 0x1) == 0) // опять проверяем нажатие
{
while ((PINA & 0x1) == 0) //ждем пока кнопку не отпустят
{}
select++;// переменная выбора откуда цифровать АЦП
}
}
if (select>2) select = 0; //сброс на 0 при переполнении
Здесь как раз и пригодилась та перемененная FLAG в прерывании АЦП. Пока АЦП не оцифровал и не записал 1 в переменную FLAG код дальше не идет т.к. если пойдет дальше то там начнется реальная дичь, прям во время цифрования может произойти переключение выводов оцифровки (изначально так и было и вся моя система выдавала полный несуразный бред вплоть до отдельно зажигающихся сегментов)
После завершения цифрования, и после того как FLAG=1 идем дальше. Тут switch case стоит для выбора с какой ноги брать сигнал для оцифровки согласно значению переменной select.
Именно этот фрагмент меня сильнее всего напрягает, ОНО НЕ ПЕРЕКЛЮЧАЕТ выводы для цифровки при нажатии на кнопку, я не знаю как и что тут надо сделать, чтобы он переключал. При проверке в железе, МК цифрует все с ADC3 и на нажатие кнопки не реагирует (точнее реагирует, но не переключает вывод для оцифровки). Мне с самого начала конструкция кода очень подозрительной казалась, но увы моих знаний не хватает чтобы заставить мультиплексор переключать.
На этом куске кода прошу помощи, как что и куда сделать.
Код: Выделить всё
while (FLAG == 0) //ждем завершения оцифровки
{}
//выбираем с какого вывода цифровать
switch (select)
{
case 0:
ADMUX |= (0 << REFS1)|(1 << REFS0)|(1 << MUX0); //ADC1
ADCSRA |= (1 << ADSC); // запуск цифрования
break; // выход
case 1:
ADMUX |= (0 << REFS1)|(1 << REFS0)|(1 << MUX1); //ADC2
ADCSRA |= (1 << ADSC);
break;
case 2:
ADMUX |= (0 << REFS1)|(1 << REFS0)|(1 << MUX0)|(1<<MUX1); //ADC3
ADCSRA |= (1 << ADSC);
break;
};
_delay_ms (50);
Дальше идет более менее рабочий кусок кода. Здесь опять же согласно переменной select выбирается способ обработки данных (переменной temp). В первом случае (case 0) я уже посчитал и сразу написал формулу для расчета напряжения бортовой сети с помощью заведомого известного делителя. (DP1 - это переменная для вывода точки, она не работает т.к. на авось написал и в подпрограмме вывода на дисплей не используется, только тут висит и на ни что не влияет)
Теперь вернусь к кнопке, выше сказал что мультиплексирование не работает, но кнопка работает. Доказательство работы кнопки дает как раз этот кусок кода.При нажатии кнопки меняется способ обработки значений с АЦП. Сначала выводит 19.96В (типа значение вольтметра но без делителя, поэтому тут все норм, как надо, по расчетам должно быть 20.00В), при нажатии на кнопку выводит 1023, еще раз жму опять 1023, еще жму (select сбрасывается на 0) опять вольтметр. При этом оцифровка идет постоянно с ADC3
Код: Выделить всё
switch (select)
{
case 0:
VOLT=(temp/204.8)*400; // вольтметр
DP1=10; //точка
display=VOLT; // запись в переменную дисплея значения напряжения.
break;
case 1:
display=temp; // будет термометр
break;
case 2:
display=temp; // будет термометр
break;
};
_delay_ms (50);
while (FLAG == 0) //ждем завершения оцифровки
{}
FLAG = 0; сбрасываем переменную FLAG для последующего отслеживания выхода с прерывания АЦП
И завершающий кусок кода, который тоже живет своей жизнью. Т.к. начальная и основная задача ставилась - выводить номер передачи, то НЕЗАВИСИМО от select должен постоянно опрашиваться ADC0 чтобы вывести номер передачи на отдельный индикатор. Как вы, надеюсь, догадываетесь, мультиплексирования тоже не происходит, цифрует с ADC3, при этом обидно, что даже при цифровании с ADC3 он не выводит номер передач на индикатор, рисует там "тире" которое соответствует тому, что значение АЦП вне диапазонов делителей (на практике - типа момент переключения передач, когда первая уже выключена, а вторая еще не включена)
Код: Выделить всё
ADMUX |= (0 << REFS1)|(1 << REFS0)| (0 << MUX0)| (0 << MUX1); // опорник 5В ADC0
ADCSRA |= (1 << ADSC);
_delay_ms (50); // задержка чтоб дать АЦП оцифровать и потом взять от туда temp
if ((temp<176) & (temp>196))
{GEAR=1;}
{if ((temp<126) & (temp>146))
{GEAR=11;} // Н в массиве
{if ((temp>317) & (temp<337))
{GEAR=2;}
{if ((temp>453) & (temp<473))
{GEAR=3;}
{if ((temp>792) & (temp<812))
{GEAR=4;}
{if ((temp>920) & (temp>940))
{GEAR=5;}
{GEAR=12;} // тире в массиве
}
}
}
}
}
_delay_ms (50);
}
}
В общем ХЭЛП, с этим мерзким мультиплексированием, как мне грамотно переключать входы для оцифровки?
Немного про мой "конструктор", софт и программатор.
Плата с контроллером содержит необходимый минимум: кварц на 8МГЦ с обвязкой, фильтры по питанию, выбор опорника (перемычкой выбирается напряжение питания) В комплекете еще 7мисегментный индикатор на 4 цифры, пара кнопок и переменный резистор. Платы делал фоторезистом. Программатор USB на FTDI микросхеме. Не мой, подарили много лет назад))
Софт, в котором пишу код - AVR Studio 4
Шью через SinaProg