ddwrt 0 18 апреля, 2016 Опубликовано 18 апреля, 2016 (изменено) · Жалоба Есть задача - в зависимости от скорости,оборотов и температуры двигателя, открывать клапан. Температура - аналоговый сигнал, примерно от 4 до 1 вольт. Обороты - импульсы, причем длинна импульса означает время, потраченное на работу одного цилиндра за полный оборот двигателя. Скорость - 9 импульсов за полный оборот колеса. Условие срабатывания клапана - температура более 50 гр. (~2.5 вольт), обороты более 1200 либо скорость более 60 км/ч. Написал код (до этого с программированием не связывался практически совсем): #include <avr/io.h> #include <avr/interrupt.h> volatile unsigned int spd_impulse = 0; volatile unsigned int spd = 0; volatile unsigned int rpm = 65535; volatile unsigned int temp_flag = 0; ISR(INT0_vect) { spd_impulse++; // Считаем импульсы с датчика скорости } ISR(TIMER2_COMP_vect) { spd = spd_impulse; // Передаем переменной spd колличество импульсов за 0.2 сек spd_impulse = 0; // обнуляем счетчик } ISR(TIMER1_CAPT_vect) { if((TCCR1B & (1<<ICES1)) != 0 ) { TCNT1 = 0; ICR1 = 0; TCCR1B &= ~(1<<ICES1); // Прерывание по нисходящему фронту } else { TCCR1B |= (1<<ICES1); // Прерывание по восходящему фронту rpm = (ICR1); // Передаем переменной rpm количество тиков счетчика, поместившихся в длину импульса от тахометра } } ISR(ADC_vect){ temp_flag = ADC; // Присваиваем переменной значение ЦАП ADCSRA |=(1<<ADSC); // Запускаем ЦАП } int main(void) { sei(); // настройка прерываний по импульсам с датчика скорости GICR |= (1 << INT0); MCUCR |= (1 << ISC01)|(1 << ISC00); // Прерывание формируется по переднему фронту // Настройка таймера для подсчета количества импульсов скорости каждые 0.2 сек OCR2 = 0xC4; // Отчитываем 0.2 секунды. TIMSK |= (1<<OCIE2); // Прерывание по совпадению TCCR2 |= 0x07; // делитель на 1024 TCCR2 |= (1<<WGM21); // Сброс при совпадении // Настройка прерываний и таймера тахометр TIMSK |= (1<<TICIE1); // разрешить прерывание на захват TCCR1B |= (1<<ICES1); // Прерывание по восходящему фронту TCCR1B |= (1<<CS10)|(1<<CS11); // тактирование счетчика 64 // Датчик температуры ADCSRA |=(1<<ADEN)|(1<<ADIE)|(1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2); ADMUX |=(1<<REFS0)|(1<<REFS1)|(0<<MUX0)|(0<<MUX1)|(0<<MUX2)|(0<<MUX3); ADCSRA |=(1<<ADSC); // Запускаем ЦАП DDRC &= ~(1<<0); // Порт датчика температуры DDRD |= (1<<0); // Порт включения клапана DDRB |= (1<<1); // Индикация температура DDRB |= (1<<2); // Индикация обороты DDRB |= (1<<3); // Индикация скорость volatile unsigned char rpm_flag = 0; volatile unsigned char spd_flag = 0; while(1) { // Задаем люфт условий открывания клапана if (rpm < 130) { rpm_flag = 1; } else if (rpm > 135) { rpm_flag = 0; } if (spd > 11) { spd_flag = 1; } else if (spd < 10) { spd_flag = 0; } // Проверяем условия if (temp_flag < 1000) { // Ждем пока температура достигнет рабочей ADCSRA &= ~(1<<ADSC)|(1<<ADIE); // Отключаем работу АЦП до следующего холодного старта PORTB |= (1<<1); // Сигнализируем, что температура в норме if (spd_flag == 1) { // Проверка флага скорости PORTD |= (1<<0); // Открываем клапан PORTB |= (1<<3); // Сигнализируем открытие клапана по скорости } else if (rpm_flag == 1 ) { // // Проверка флага оборотов PORTD |= (1<<0); // Открываем клапан PORTB |= (1<<2); // Сигнализируем открытие клапана по оборотам } else { PORTD &= ~(1<<0); // Отключаем клапан } } PORTB &= ~(1<<1)||(1<<2)|(1<<3); // Гасим всю индикацию. } } Вроде работает исправно. Прошу если кому не сложно, указать на ошибки программирования, может можно что-то оптимизировать. Забыл добавить - делаю устройство на Atmega8 Изменено 20 апреля, 2016 пользователем Herz Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xvr 12 18 апреля, 2016 Опубликовано 18 апреля, 2016 · Жалоба Квалификаторы volatile на переменных rpm_flag и spd_flag излишни sei(); переставить после настройки портов и прерываний Сравнения типа spd_flag == 1 переделать на spd_flag != 0 По коду - При старте переменная temp_flag находится в 0, и если ADC не успеет получить результат до проверки if (temp_flag < 1000) то if сработает и в первой же строке выключит ADC насовсем. Кажется это не совсем желаемое поведение :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 14 18 апреля, 2016 Опубликовано 18 апреля, 2016 · Жалоба ADCSRA &= ~(1<<ADSC)|(1<<ADIE); // Отключаем работу АЦП до следующего холодного старта Тут нужны скобки. Потому что приоритет операции ~ выше, чем операции |. И у вас получается не то, что задумано. PORTB &= ~(1<<1)||(1<<2)|(1<<3); // Гасим всю индикацию. Здесь тоже нужны скобки. И в одном месте перепутано | и ||. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ddwrt 0 18 апреля, 2016 Опубликовано 18 апреля, 2016 · Жалоба Сравнения типа spd_flag == 1 переделать на spd_flag != 0 Вот это пока не дошло, почему так лучше. По коду - При старте переменная temp_flag находится в 0, и если ADC не успеет получить результат до проверки if (temp_flag < 1000) то if сработает и в первой же строке выключит ADC насовсем. Кажется это не совсем желаемое поведение :) Согласен, может такое случиться. Спасибо! Тут нужны скобки. Потому что приоритет операции ~ выше, чем операции |. И у вас получается не то, что задумано. Здесь тоже нужны скобки. И в одном месте перепутано | и ||. А как правильно поставить скобки? так PORTB &= ~((1<<1)|(1<<2)|(1<<3)); Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 14 18 апреля, 2016 Опубликовано 18 апреля, 2016 · Жалоба так PORTB &= ~((1<<1)|(1<<2)|(1<<3)); Да, так. (1<<1) - даёт единичку в первом разряде (00000010), (1<<2) - даёт единичку во втором разряде (00000100), (1<<3) - даёт единичку в третьем разряде (00001000), потом вы их комбинируете при помощи побитовой операции или (|) (00001110), и после этого инвертируете (~): 11110001. Таким образом, делая операцию побитового или (&) порта PORTB и значения 11110001 вы сбрасываете в 0 биты 1, 2 и 3. Как-то так. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ddwrt 0 18 апреля, 2016 Опубликовано 18 апреля, 2016 · Жалоба спасибо! теперь понятно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zltigo 0 18 апреля, 2016 Опубликовано 18 апреля, 2016 · Жалоба Сравнения типа spd_flag == 1 переделать на spd_flag != 0 Масло маслянное. Просто if( spd_flag ) И раздавать каждому биту по байту как то некрасиво. Битовые поля разумнее смотрятся. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xvr 12 18 апреля, 2016 Опубликовано 18 апреля, 2016 · Жалоба Вот это пока не дошло, почему так лучше.Потому что сравнение с 1 будет выполняться именно как сравнение с константой 1, в то время как в С для логического значения 0 считается как false , а все остальное (не только 1) как true. У компилятора больше возможностей для более оптимальной генерации кода, если вы его не будете искуственно ограничивать в определении, что есть false а что есть true. Пример (несколько надуманный, но все же): Код с 1 и 0: void call_some(void); void test(unsigned char v) { if (v==1) call_some(); if (v==0) call_some(); } результат - test(unsigned char): cpi r24,lo8(1) breq .L5 cpse r24,__zero_reg__ ret rcall call_some() ret .L5: rcall call_some() ret Код без таких ограничений: void call_some(void); void test(unsigned char v) { if (v) call_some(); if (!v) call_some(); } Результат: test(unsigned char): rcall call_some() ret Как говорится - почуствуйте разницу :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
SlavaV 0 19 апреля, 2016 Опубликовано 19 апреля, 2016 · Жалоба Зачем задействовать прерывание INT0 не проще на счётчик подать (пусть сам считает) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ddwrt 0 20 апреля, 2016 Опубликовано 20 апреля, 2016 (изменено) · Жалоба Зачем задействовать прерывание INT0 не проще на счётчик подать (пусть сам считает) Мне нужно знать время между импульсами, чтобы посмотреть скорость. Если подать на счетчик, он мне просто посчитает импульсы, дальше что с ними делать? Запускать еще один таймер, чтобы узнать за какое время первый таймер насчитал импульсы? Изменено 20 апреля, 2016 пользователем ddwrt Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 20 апреля, 2016 Опубликовано 20 апреля, 2016 · Жалоба Мне нужно знать время между импульсами, чтобы посмотреть скорость. Если подать на счетчик, он мне просто посчитает импульсы, дальше что с ними делать? Запускать еще один таймер, чтобы узнать за какое время первый таймер насчитал импульсы? Для измерения интервалов между импульсами гораздо лучше использовать таймер, а не прерывания. В LPC17xx, например, для этого имеется опция CAPTURE в его таймерах - позволяет защёлкивать значение счётчика таймера по внешнему сигналу (с одновременным прерыванием). Я думаю и в других МК есть похожая возможность. А почему лучше через таймер - подумайте сами. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ddwrt 0 20 апреля, 2016 Опубликовано 20 апреля, 2016 (изменено) · Жалоба Для измерения интервалов между импульсами гораздо лучше использовать таймер, а не прерывания. В LPC17xx, например, для этого имеется опция CAPTURE в его таймерах - позволяет защёлкивать значение счётчика таймера по внешнему сигналу (с одновременным прерыванием). Я думаю и в других МК есть похожая возможность. А почему лучше через таймер - подумайте сами. Если посмотреть код, то там уже по такому принципу снимается сигнал с тахометра. Если бы был еще один таймер с внешним захватом, думаю скорость также сделал. Поэтому пришлось выкручиваться через прерывание. Изменено 20 апреля, 2016 пользователем ddwrt Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 20 апреля, 2016 Опубликовано 20 апреля, 2016 · Жалоба Если посмотреть код, то там уже по такому принципу снимается сигнал с тахометра. Если бы был еще один таймер с внешним захватом, думаю скорость также сделал. Поэтому пришлось выкручиваться через прерывание. Значит неправильно выбрали МК. Выберите другой, в котором больше таймеров. Иначе страдает точность. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ddwrt 0 20 апреля, 2016 Опубликовано 20 апреля, 2016 (изменено) · Жалоба Значит неправильно выбрали МК. Выберите другой, в котором больше таймеров. Иначе страдает точность. В задаче точность скорости не нужна, важнее обороты. Просто валялся без дела atmega8, вот его и решил пристроить. Осталась проблема, если по каким-то причинам сигнал тахометра резко пропадает, то rpm зависнет на последнем значении и клапан будет оставаться открытым до следующего сигнала. Это же может произойти, даже если будет одиночный короткий импульс от помехи, во время включения. Изменено 20 апреля, 2016 пользователем Herz Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Herz 4 20 апреля, 2016 Опубликовано 20 апреля, 2016 · Жалоба ddwrt, не надо "растягивать" сообщения, разбавляя их пустыми строками. Читабельность от этого не улучшается. Кроме того, нет слова "проэкт" в русском языке. Исправил заголовок. Модератор. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться