РАДИОСХЕМЫ



СТАРЫЙ ФОРУМ

ФОРУМ НА ЭЛВО

РАДИОФОРУМЫ


СХЕМЫ И СТАТЬИ
  • БЛОГИ
  • НОВОЕ
  • СХЕМЫ
  • РАЗНОЕ
  • ТЕОРИЯ
  • ТЕЛЕВИДЕО
  • СВЕТОДИОД
  • МЕДТЕХНИКА
  • БЫТТЕХНИКА
  • ИЗМЕРИТЕЛИ
  • СПРАВОЧНИК
  • ТЕХНОЛОГИИ
  • РЕМОНТ СХЕМ
  • МОБИЛЬНИКИ
  • КОМПЬЮТЕРЫ
  • НАЧИНАЮЩИМ
  • ЗАРЯДКИ И АКБ
  • СИГНАЛИЗАЦИИ
  • АУДИОТЕХНИКА
  • АВТОВЕЛОМОТО
  • БЛОКИ ПИТАНИЯ
  • РАДИОПРИЁМНИКИ
  • МЕТАЛЛОИСКАТЕЛИ
  • МИКРОКОНТРОЛЛЕР
  • РАДИОПЕРЕДАТЧИКИ


    • Страница 1 из 8
    • 1
    • 2
    • 3
    • 7
    • 8
    • »
    Архив - только для чтения
    Бортовой компьютер на мотоцикл
    Пт, 17.02.2017, 19:26 | Сообщение # 1        
    [)еНиС
    аватар
      Постов: 3074   Друзья 
    Всем привет!
    В общем купил мото, и решил сделать на него в дополнение к приборке типа "бортовой комп". По начальной задумке я хотел тупо выводить номер передачи на 7ми сегментник по 1 проводу.
    Как работает? Очень просто, в КПП есть вал который поворачивается на определенный угол, соответствующий своей передаче, проградуировав и разметив углы, сделал не хитрую плату с резисторными делителями. В итоге когда переключается передача - переключается резисторный делитель и напряжение выходное соответственно тоже меняется.

    Здесь мне на помощь пришло АЦП и гора условий в коде (самый первый мой шаг в изучении СИ и AVR). Проблем особо не возникло, все работало как часы. До поры до врмени

    Но мне этого МАЛО! Учитывая что контроллер Atmega169 и у него ого-го-го ног и ресурсов, значит можно еще что-то впинать.

    Следующим шагом были часы, нашел коды готовых, поизучал, поизучал асинхронный таймер, понял что это не очень сложно и можно в любой момент код готовых часов воткнуть к себе и временно забил.
    Во время изучения часов я решил что не плохо было бы еще показывать напряжение бортовой сети, остаток топлива (емкостный датчик), температуру воздуха и температуру двигателя.
    Что касается емкостного датчика, решил временно отложить т.к. бОльшая часть схемы там будет на аналоговых компонентах, а возможности разработать и собрать схему пока нет((
    Поэтому взялся вплотную за термометры и вольтметр.

    Как я хочу это реализовать:

    В контроллере есть куча ног куда можно переключать АЦП. На каждую ногу по своему аналоговому датчику (да, датчики температуры будут аналоговым не цифровым, никаких I2C). Всего будет задействовано 4 входа. Вход под КПП, 2 входа термометры и 1 вход под вольтметр.

    Переключение измеряемого датчика должно происходить по нажатию на кнопку.
    Для кнопки завел свою переменную, которая считает нажатия и с помощью switch case ставит в соответствие количеству нажатий кнопки вывод откуда цифровать значение. Вот здесь то я и столкнулся первой серьезной проблемой, которую на данный момент уже разрулил и тут все работает biggrin

    Думаю стоит перейти к коду. по ходу буду пытаться объяснить )
    Самое начало я пожалуй пропущу, где объявлены всякие переменные и циклы в которых крутится прога по прервыниям.

    Это важный кусок прерывания. Суть такая- запустили преобразование, АЦП оцифровал, поднял флаг, ушел в прерывание, в прерывании записал свое значение в переменную 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(); //глобально разрешены прерывания



    Тут идет опрос кнопки, вроде бы все коректно работает т.к. делал код, чтоб на дисплей выводилось кол-во нажатий на кнопку, все выводит, работает. После 3 нажатий на 0 сбрасывает select. Переменная select собственно и накапливает в себе нажатия кнопки.

    Прошу заметить что порт А я нигде не конфигурировал на вход, но как ни странно кнопка работает, может кто объяснить такой прикол?

    Цитата

    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 при переполнении


    Едем дальше, вот тут начинался самый хардкор, с которым я долго разбирался в итоге заработало cool

    Здесь как раз и пригодилась та перемененная FLAG в прерывании АЦП. Пока АЦП не завершил преобразование и не записал 1 в переменную FLAG код дальше не идет т.к. если пойдет дальше то там начнется реальная дичь, прям во время цифрования может произойти переключение выводов оцифровки (изначально так и было и вся моя система выдавала бред вплоть до отдельно зажигающихся сегментов)

    После завершения преобразования, и после того как FLAG=1 идем дальше. Тут switch case стоит для выбора с какой ноги брать сигнал для оцифровки согласно значению переменной select.

    Выводы для цифровки переключаются при нажатии на кнопку. Так же переключается в слудующем switch case способ обработки полученного значения с АЦП. Конечно мне кажется не лучшая реализация, но оно работает biggrin

    Цитата


    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 - это переменная для вывода точки, она не работает т.к. на авось написал и в подпрограмме вывода на дисплей не используется временно, только тут висит и на ни что не влияет)

    Цитата


    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 для последующего отслеживания выхода с прерывания АЦП



    И завершающий кусок кода, который на данный момент живет своей жизнью, так сказать то самое "До поры до врмени". Когда первый код писал- все работало, но потом накосячил и не сохранил, сейчас написал заново код с кучей if но оно не работает(( Сейчас опишу подробнее smile

    Т.к. начальная и основная задача ставилась - выводить номер передачи, то НЕЗАВИСИМО от select должен постоянно опрашиваться ADC0 чтобы вывести номер передачи на отдельный индикатор. Но при оцифровке не выводит номер передач на индикатор, рисует там "тире" которое соответствует тому, что значение АЦП вне диапазонов делителей (на практике - типа момент переключения передач, когда первая уже выключена, а вторая еще не включена).

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

    Я одновременно вывожу на дисплей значение АЦП, сравниваю с тем что в коде, по коду должно сработаь условие и вывести номер передачи, но не выводит. Затем задержка и насильно переменной GEAR (которая отвечает за индекс в массиве для вывода на дисплей) присваиваю значения от 1 до 5 и как ни странно на дисплее все выводится, следовательно косяк именно в условии. Удивительно на самом элементарном застрял.

    Цитата

    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);
    }
    }



    В общем теперь нужна помощь чтоб разобраться с этим гадким if biggrin
    Весь код прикреплен в архиве

    Софт в котором пишу - AVR Studio 4
    Софт которым шью - SinaProg
    Программатор USB на FDTI

    Сама плата с МК содержит необходимы минимум - кодеры в питании, кодер в опорнике, кварц с обвязкой на 8МГц, возможность выбора опорника перемычкой - питание\внешнее



    Ну несколько фоток моего "конструктора" для отладки.











    Файлы: 8651824.jpg (327.2 Kb) · 4660382.jpg (269.8 Kb) · 3795422.jpg (263.0 Kb) · 0127845.jpg (277.5 Kb) · 7567075.jpg (225.6 Kb) · 4930187.jpg (242.6 Kb)
    Пт, 17.02.2017, 19:31 | Сообщение # 2        
    [)еНиС
    аватар
      Постов: 3074   Друзья 
    Исходники забыл biggrin
    Файлы: gearDetectorV2.rar (28.1 Kb)
    Пт, 17.02.2017, 19:44 | Сообщение # 3        
    [)еНиС
    аватар
      Постов: 3074   Друзья 
    Ну еще до кучи есть такой актуальный вопрос- как счтитать импульсы контроллером, желательно счетчиком\таймером. Это для емкостного датчика. Думаю как датчик работает объяснять не стоит.
    В общем выходной сигнал с датчика - это изменеие частоты, напрямер бак пустой - частота максимальна, бак полный - частота минимальна. Частота скоре всего будет порядка 30-50кГц. И надо за небольшой промежуток времени посчитать кол-во импульсов для дальнейшей обработки контролелром.

    Есть идея как это сделать, но не могу продумать код.
    В общем заводим таймер\счетчик от внешнего тактирования. Задаем заначение по нулям. В коде программы запускаем счетчик например на 1 мс. Потом тормозим счетчик. Берем значение, сколько импульсов он насчитал. Это значение дальше в код. А сам счетчик сбрасываем на 0 и ждем следующего запуска.

    На словах все просто, на деле... Не думаю что так легко.
    Пт, 17.02.2017, 22:27 | Сообщение # 4        
    [)еНиС
    аватар
      Постов: 3074   Друзья 
    Все, добил. Теперь работает.

    if ((temp<176) && (temp>196))
    {GEAR=1;}

    Надо так писать)

    Еще путались переменные temp в которые значение АЦП заносится и время от времени не туда выводились. Ввел еще одну переменную flagGEAR которая определяет своим состоянием в какую переменную сохранять АЦП в temp или в новую, отдельно заведенную под КПП tempGEAR.

    В выходные может ролик сниму как все работает biggrin
    Пт, 17.02.2017, 23:44 | Сообщение # 5        
    Тёмыч
    аватар
      Постов: 759   Друзья 
    Цитата [)еНиС ()
    Теперь работает.

    if ((temp<176) && (temp>196))
    {GEAR=1;}

    Надо так писать)


    [)еНиС, сразу это заметил, но вижу ты уже и сам догадался... smile

    Цитата [)еНиС ()
    Прошу заметить что порт А я нигде не конфигурировал на вход, но как ни странно кнопка работает, может кто объяснить такой прикол?


    все порты изначально (по умолчанию) сконфигурированны как вход, по этому кнопка и работает smile

    Цитата [)еНиС ()
    Есть идея как это сделать, но не могу продумать код.
    В общем заводим таймер\счетчик от внешнего тактирования.


    я делал не много по другому: использовал внешнее прерывание, в нём увеличиваем переменную, потом в прерывании таймера берём с переменной наше значение и обнуляем (переменную) и пусть тикает дальше до следующего прерывания таймера. Вроде понятно объяснил smile
    Сб, 18.02.2017, 16:17 | Сообщение # 6        
    [)еНиС
    аватар
      Постов: 3074   Друзья 
    Цитата Тёмыч ()
    я делал не много по другому: использовал внешнее прерывание, в нём увеличиваем переменную, потом в прерывании таймера берём с переменной наше значение и обнуляем (переменную) и пусть тикает дальше до следующего прерывания таймера. Вроде понятно объяснил


    в принципе понял, но ведь у меня много чего в прерываниях крутится - индикация, ацп, таймер часов.

    Если пропустить прерывание ацп и индикации - ничего страшного не случится, страшное случится если пропустится прерывание с таймера для часов.

    А еще может пропускаться внешнее прерывание, которое и так не часто будет опрашиваться.

    Тут както надо прям в коде в определнные моменты запрещать прерывания для определенных элементов. И еще нужно задать приоритет прерываниям.

    Т.е. прерывание по часовому таймеру максимальный приоритет.

    В основном коде запрещаем все прерывания кроме внешних и таймера часов, считаем импульсы, разрешаем глобально прерывания.
    Что то типа такого наверно)

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

    Мне бы вот информация по заданию приоритетов бы пригодилась бы, я пока не знаю как это делается biggrin
    Сб, 18.02.2017, 17:15 | Сообщение # 7        
    [)еНиС
    аватар
      Постов: 3074   Друзья 
    Хотя если подумать, то прерывания по таймеру часов будут происходить каждую секунду, за секунду весь код успеет кучу раз пройти и с очень большой вероятностью, всетаки попадет таймер часов в прерывание и прибавит злосчатсную единичку к переменной часов.

    Т.е. если крутимся уже в прерывании, выскакивает флаг таймера, встает в очередь и может наверно ждать целую секунду свою очередь на прерывание.

    Таймер ведь сразу начинает считать с нуля после того как поднял флаг, даже если флаг не опустили, просто второй раз уже не будет поднимать флаг т.к. он уже поднят? Если так, то проблема с асинхронным таймером решена можно сказать.
    Сб, 18.02.2017, 18:23 | Сообщение # 8        
    [)еНиС
    аватар
      Постов: 3074   Друзья 
    Начал впинывать часы, тобишь асинхронный таймер заводить. Стал читать дашик на мой камень и вот что вычитал. Кварц для асинхронного генератора цепляется на те же ножки что и кварц тактирования ядра.



    Как быть? Завести чтоли внутреннее тактирввание на 8МГц фьюзами??
    Файлы: 6454062.jpg (137.6 Kb)
    Сб, 18.02.2017, 22:31 | Сообщение # 9        
    [)еНиС
    аватар
      Постов: 3074   Друзья 
    Написал код часов в 3 строчки. На мой взгляд очень не рационально, пришлось использовать аж long int чтоб влез мой счетчик. Зато всего 3 строчки кода. Все преобразования адаптированы под тот код который уже написан и выводится на дисплей обычным способом как все остальные числа (напряжение, ацп и прочее)

    Долго не мог понять почему после 10.00 начинает сразу 56.хх показывать время, потом предположил что не хватает емкости переменных и они переполняются и кажут дичь. И был прав, нашел табличку, где написано какой тип данных до какого значения, исправил и все заработало.

    В общем вот код, может как то грамотнее подскажите biggrin :D
    Цитата

    // в этот тип данных влазит больше 2х10^9 цифр
    long int HOUR=0; // переменная твечающая за часы
    long int MIN=0; // минуты
    long int CLOCK=0; // счетчик секунд

    ISR (TIMER2_OVF_vect) // в этом прерывании крутится подсчет секунд по асинхронному таймеру
    {

    CLOCK++; // плюсуем секунды
    if (CLOCK==86400) {CLOCK=0;} // в сутках 86400 сек, если счетчик перевалил, то обнуляем на 0

    }

    HOUR = CLOCK / 3600; // таким образом получаем кол-во часов из секунд
    MIN = (CLOCK-(HOUR*3600)) / 60; // тут выделяем из остатка минуты
    display = (HOUR*100)+MIN; // специальное преобразование для адаптации под алгоритм вывода на дисплей т.е. 12.30 будет выглядеть как одно число 1230 для алгоритма дисплея
    DP2=0x80; // вывод точки

    Вс, 19.02.2017, 11:09 | Сообщение # 10        
    Тёмыч
    аватар
      Постов: 759   Друзья 
    Цитата [)еНиС ()
    Кварц для асинхронного генератора цепляется на те же ножки что и кварц тактирования ядра. Как быть? Завести что ли внутреннее тактирввание на 8МГц фьюзами??


    [)еНиС, по другому никак.

    Цитата [)еНиС ()
    В общем вот код, может как то грамотнее подскажите :D


    чтобы не использовать такие большие переменные, код надо записать так (моё мнение):


    таким образом можно использовать все три переменные типа "char"

    Добавлено (19.02.2017, 12:09)
    ---------------------------------------------
    и при возможности надо избегать операции деления, так как она занимает много процессорного времени.
    • Страница 1 из 8
    • 1
    • 2
    • 3
    • 7
    • 8
    • »
    Поиск:

    Внимание! Форум переехал на Tehnodium.ru



    © 2010-2022 "Форум Радиосхемы". All Rights Reserved  Почта  PDA