реклама на сайте
подробности

 
 
> Первый проект на АТМЕГА
ddwrt
сообщение Apr 18 2016, 09:19
Сообщение #1





Группа: Участник
Сообщений: 6
Регистрация: 18-04-16
Пользователь №: 91 345



Есть задача - в зависимости от скорости,оборотов и температуры двигателя, открывать клапан.
Температура - аналоговый сигнал, примерно от 4 до 1 вольт.
Обороты - импульсы, причем длинна импульса означает время, потраченное на работу одного цилиндра за полный оборот двигателя.
Скорость - 9 импульсов за полный оборот колеса.
Условие срабатывания клапана - температура более 50 гр. (~2.5 вольт), обороты более 1200 либо скорость более 60 км/ч.

Написал код (до этого с программированием не связывался практически совсем):

CODE
#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

Сообщение отредактировал Herz - Apr 20 2016, 08:11
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов (1 - 14)
XVR
сообщение Apr 18 2016, 10:17
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Квалификаторы volatile на переменных rpm_flag и spd_flag излишни
sei(); переставить после настройки портов и прерываний
Сравнения типа spd_flag == 1 переделать на spd_flag != 0

По коду -
При старте переменная temp_flag находится в 0, и если ADC не успеет получить результат до проверки if (temp_flag < 1000) то if сработает и в первой же строке выключит ADC насовсем. Кажется это не совсем желаемое поведение sm.gif

Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Apr 18 2016, 10:22
Сообщение #3


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(ddwrt @ Apr 18 2016, 14:19) *
Код
            ADCSRA &= ~(1<<ADSC)|(1<<ADIE); // Отключаем работу АЦП до следующего холодного старта

Тут нужны скобки. Потому что приоритет операции ~ выше, чем операции |. И у вас получается не то, что задумано.
Цитата(ddwrt @ Apr 18 2016, 14:19) *
Код
        PORTB &= ~(1<<1)||(1<<2)|(1<<3); // Гасим всю индикацию.

Здесь тоже нужны скобки. И в одном месте перепутано | и ||.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
ddwrt
сообщение Apr 18 2016, 13:07
Сообщение #4





Группа: Участник
Сообщений: 6
Регистрация: 18-04-16
Пользователь №: 91 345



Цитата(XVR @ Apr 18 2016, 11:17) *
Сравнения типа spd_flag == 1 переделать на spd_flag != 0

Вот это пока не дошло, почему так лучше.
Цитата(XVR @ Apr 18 2016, 11:17) *
По коду -
При старте переменная temp_flag находится в 0, и если ADC не успеет получить результат до проверки if (temp_flag < 1000) то if сработает и в первой же строке выключит ADC насовсем. Кажется это не совсем желаемое поведение sm.gif

Согласен, может такое случиться. Спасибо!
Цитата(AHTOXA @ Apr 18 2016, 11:22) *
Тут нужны скобки. Потому что приоритет операции ~ выше, чем операции |. И у вас получается не то, что задумано.
Здесь тоже нужны скобки. И в одном месте перепутано | и ||.

А как правильно поставить скобки?
так PORTB &= ~((1<<1)|(1<<2)|(1<<3));
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Apr 18 2016, 13:32
Сообщение #5


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(ddwrt @ Apr 18 2016, 18:07) *
так PORTB &= ~((1<<1)|(1<<2)|(1<<3));

Да, так.
(1<<1) - даёт единичку в первом разряде (00000010),
(1<<2) - даёт единичку во втором разряде (00000100),
(1<<3) - даёт единичку в третьем разряде (00001000),
потом вы их комбинируете при помощи побитовой операции или (|) (00001110),
и после этого инвертируете (~): 11110001.
Таким образом, делая операцию побитового или (&) порта PORTB и значения 11110001 вы сбрасываете в 0 биты 1, 2 и 3.
Как-то так.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
ddwrt
сообщение Apr 18 2016, 13:50
Сообщение #6





Группа: Участник
Сообщений: 6
Регистрация: 18-04-16
Пользователь №: 91 345



спасибо! теперь понятно.
Go to the top of the page
 
+Quote Post
zltigo
сообщение Apr 18 2016, 14:13
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



QUOTE (XVR @ Apr 18 2016, 13:17) *
Сравнения типа spd_flag == 1 переделать на spd_flag != 0

Масло маслянное. Просто
if( spd_flag )

И раздавать каждому биту по байту как то некрасиво. Битовые поля разумнее смотрятся.



--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
XVR
сообщение Apr 18 2016, 14:37
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цитата
Вот это пока не дошло, почему так лучше.
Потому что сравнение с 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

Как говорится - почуствуйте разницу sm.gif
Go to the top of the page
 
+Quote Post
SlavaV
сообщение Apr 19 2016, 13:19
Сообщение #9


Частый гость
**

Группа: Свой
Сообщений: 100
Регистрация: 13-06-06
Из: г.Улан-Удэ
Пользователь №: 18 024



Зачем задействовать прерывание INT0 не проще на счётчик подать (пусть сам считает)
Go to the top of the page
 
+Quote Post
ddwrt
сообщение Apr 20 2016, 04:47
Сообщение #10





Группа: Участник
Сообщений: 6
Регистрация: 18-04-16
Пользователь №: 91 345



Цитата(SlavaV @ Apr 19 2016, 14:19) *
Зачем задействовать прерывание INT0 не проще на счётчик подать (пусть сам считает)

Мне нужно знать время между импульсами, чтобы посмотреть скорость. Если подать на счетчик, он мне просто посчитает импульсы, дальше что с ними делать? Запускать еще один таймер, чтобы узнать за какое время первый таймер насчитал импульсы?

Сообщение отредактировал ddwrt - Apr 20 2016, 04:49
Go to the top of the page
 
+Quote Post
jcxz
сообщение Apr 20 2016, 05:42
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(ddwrt @ Apr 20 2016, 10:47) *
Мне нужно знать время между импульсами, чтобы посмотреть скорость. Если подать на счетчик, он мне просто посчитает импульсы, дальше что с ними делать? Запускать еще один таймер, чтобы узнать за какое время первый таймер насчитал импульсы?

Для измерения интервалов между импульсами гораздо лучше использовать таймер, а не прерывания. В LPC17xx, например, для этого имеется опция CAPTURE в его таймерах - позволяет защёлкивать значение счётчика таймера по внешнему сигналу (с одновременным прерыванием). Я думаю и в других МК есть похожая возможность.
А почему лучше через таймер - подумайте сами.
Go to the top of the page
 
+Quote Post
ddwrt
сообщение Apr 20 2016, 06:06
Сообщение #12





Группа: Участник
Сообщений: 6
Регистрация: 18-04-16
Пользователь №: 91 345



Цитата(jcxz @ Apr 20 2016, 06:42) *
Для измерения интервалов между импульсами гораздо лучше использовать таймер, а не прерывания. В LPC17xx, например, для этого имеется опция CAPTURE в его таймерах - позволяет защёлкивать значение счётчика таймера по внешнему сигналу (с одновременным прерыванием). Я думаю и в других МК есть похожая возможность.
А почему лучше через таймер - подумайте сами.


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

Сообщение отредактировал ddwrt - Apr 20 2016, 06:07
Go to the top of the page
 
+Quote Post
jcxz
сообщение Apr 20 2016, 06:15
Сообщение #13


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(ddwrt @ Apr 20 2016, 12:06) *
Если посмотреть код, то там уже по такому принципу снимается сигнал с тахометра. Если бы был еще один таймер с внешним захватом, думаю скорость также сделал. Поэтому пришлось выкручиваться через прерывание.

Значит неправильно выбрали МК. Выберите другой, в котором больше таймеров. Иначе страдает точность.
Go to the top of the page
 
+Quote Post
ddwrt
сообщение Apr 20 2016, 08:02
Сообщение #14





Группа: Участник
Сообщений: 6
Регистрация: 18-04-16
Пользователь №: 91 345



Цитата(jcxz @ Apr 20 2016, 07:15) *
Значит неправильно выбрали МК. Выберите другой, в котором больше таймеров. Иначе страдает точность.

В задаче точность скорости не нужна, важнее обороты. Просто валялся без дела atmega8, вот его и решил пристроить.
Осталась проблема, если по каким-то причинам сигнал тахометра резко пропадает, то rpm зависнет на последнем значении и клапан будет оставаться открытым до следующего сигнала. Это же может произойти, даже если будет одиночный короткий импульс от помехи, во время включения.

Сообщение отредактировал Herz - Apr 20 2016, 08:12
Go to the top of the page
 
+Quote Post
Herz
сообщение Apr 20 2016, 08:14
Сообщение #15


Гуру
******

Группа: Модераторы
Сообщений: 10 983
Регистрация: 23-11-05
Пользователь №: 11 287



ddwrt, не надо "растягивать" сообщения, разбавляя их пустыми строками. Читабельность от этого не улучшается.
Кроме того, нет слова "проэкт" в русском языке. Исправил заголовок.
Модератор.
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 27th June 2025 - 22:47
Рейтинг@Mail.ru


Страница сгенерированна за 0.01651 секунд с 7
ELECTRONIX ©2004-2016