Перейти к содержанию

Спидометр Для Велосипеда


Dimon007

Рекомендуемые сообщения

Делаю спидометр для велосипеда. Принцип действия тривиальный: магнит на колесе и геркон (или датчик Холла) на вилке. МК - ATmega8515, работает на 1 Mhz от внутреннего генератора (в перспективе кварц поставить). Таймер 0 работает на частоте 125 kHz, отсчитывая количество миллисекунд (в прерывании). Сигнал с геркона приходит на INT0 опять-таки вызывая прерывание(по нарастанию). И в этом прерывании определяем, сколько мс прошло. Далее всё это дело выводим на LCD 16x2. Тестирую от генератора, собранного на таймере 555, по расчёту время между импульсами должно быть ~170 мс. В итоге на дисплее 4-5. Пробовал просто проверить не врёт ли таймер, и делал тест на 10 секунд, приблизительно работало, выводя через 10 сек определённое сообщение на LCD. Дребезг устраняю с помощью конденсатора и резистора, думал проблема в них, убрал - проблема осталась. Вот вопрос, что я делаю не так?) С LCD работаю напрямую. Компилятор - Code Vision AVR.

 
#include <mega8515.h>
#include <delay.h>
#include <stdlib.h>

#define E PORTC.0
#define RW PORTC.1
#define C PORTC.2

	char i;
	volatile unsigned char text[16];
	unsigned char loading[16]=" &Loading&";
	volatile long t;
	float w;
	float R;
	float v;
	float s;
	float ds;

void send(void)
{
  E=0x00;
  delay_us(500);   //команда для дисплея на приём данных
  E=0xFF;
}

void clear(void)
{
	C=0x00;
	PORTA=0b00000001; //очистка дисплея
	send();
	C=0xFF;
}

interrupt [EXT_INT0] void ext_int0_isr(void) //с геркона
{
	//////////////////////////// Физика ////////////////
	w=6.2832/(t*0.001);
	v=w*R;		
	s=s+ds;
	///////////////////////////////////////////////////

	ltoa(t, text);
	clear();
	for (i=0;i<16;i++)
	{ 
	 PORTA=text[i];
	 send();				
	};  

	t=0; //обнуляем время
 }	  


interrupt [TIM0_OVF] void timer0_ovf_isr(void) //прерывания по таймеру
{
	TCNT0=0x83; 
	if(PIND.2!=0xFF)  //если магнит не у геркона
	{
			t=t+1;
	};

}


void main(void)
{
	DDRA=0xFF;
	DDRB=0xFF;
	DDRC=0xFF;
	DDRD=0x00;
	PORTA=0x00;
	PORTC=0x00;
	E=0xFF;
	RW=0x00;
	C=0x00;

//////////////////////////////		
	GICR|=0x40;
	MCUCR=0x03; //прерывание Int0
	GIFR=0x40; 

	TCCR0=0x02;
	TCNT0=0x83;

	TIMSK=0x02;


////////////////////////////// 

	delay_ms(15);
	PORTA=0b00111000;
	send();
	delay_ms(100);
	PORTA=0b00000001;
	send();
	delay_ms(4);				  //инициализация дисплея
	PORTA=0b00000110;
	send();
	delay_ms(1);
	PORTA=0b00000001;
	send();
///////////////////////////////  

	C=0x00; //режим команд

	C=0xFF; //режим данных

	for(i=0;i<16;i++)
	{ 
			PORTA=loading[i];
			send();		
	};
	delay_ms(1000);

	#asm("sei")

while (1)
  {

  };
}

Изменено пользователем wowa
Подрезал длинный бокс.. Wowa.
Ссылка на комментарий
Поделиться на другие сайты

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

Да кто же обработчик прерывания (INT0) таким длинным (в смысле долгим) делает то? Там и умножение флоат, и itoa, и delay_us(500); в вызываемых ф-ях... Пока он у вас до конца досчитает, прерывание уже снова произойдет (даже не один раз). Значит, только выйдя из обработчика, снова туда попадете. Поскольку у прерывания таймера приоритет ниже, в его обработчик программа попадет только в те редкие моменты, когда при выходе из обработчика по инт0, не будет стоять флаг этого прерывания. Счетчик t, разумеется, приращиваться будет очень медленно...

Логика программы, в принципе, неверна совершенно.

Гораздо правильнее в обработчике инт0 сначала запускать таймер (первый вход), потом останавливать (второй вход) считывать TCNT0 в переменную t и обнулять. В основном цикле не спеша считать скорость и выводить. В обработчике переполнения таймера учитывать переходы и считать число переполнений (потом их домножим на 255 и сложим с t и получим число тиков таймера) Взаимодействие легко организовать через систему флагов (когда считать, когда выводить, когда измерять и т.д.)... Никаких флоат, лонг и подобных не надо и в помине. Нужную точность получите и без них. Примерно так...

P.S. Можно еще воспользоваться захватом таймера т1 по внешнему прерыванию. Так немного необычно, зато точнее (не тратится время на вход в обработчик прерывания) на несколько тактов процессора. Почитайте про данный режим таймера т1...

Любой, заслуживающий внимания, опыт приобретается себе в убыток...

Ссылка на комментарий
Поделиться на другие сайты

20% скидка на весь каталог электронных компонентов в ТМ Электроникс!

Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!

Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!

Перейти на страницу акции

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

Выбираем схему BMS для корректной работы литий-железофосфатных (LiFePO4) аккумуляторов

 Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ, также как и для других, очень важен контроль процесса заряда и разряда, а специализированных микросхем для этого вида аккумуляторов не так много. Инженеры КОМПЭЛ подготовили список имеющихся микросхем и возможных решений от разных производителей. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

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

Ссылка на комментарий
Поделиться на другие сайты

...В прерывании достаточно считать состояние регистра захвата...

Тут есть небольшая тонкость. Поскольку таймер бежит свободно, значения в регистре ICR могут оказаться по разные стороны перехода таймера через 65535. Это надо учесть, наряду с возможными циклами холостого "пробега" таймера, когда захвата нет...

Любой, заслуживающий внимания, опыт приобретается себе в убыток...

Ссылка на комментарий
Поделиться на другие сайты

Пока попробую сделать по первому варианту, без захвата, ибо не разобрался с ним ещё. А какая погрешность измерения периода приблизительно будет (от внутреннего генератора, от кварца)?

Ссылка на комментарий
Поделиться на другие сайты

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

Любой, заслуживающий внимания, опыт приобретается себе в убыток...

Ссылка на комментарий
Поделиться на другие сайты

Переделал код, но изменений мало) Теперь выводит 6-7. Может быть частоту повысить до 4 Мгц?

		volatile char i;
	volatile bit flag; //флаг
	volatile unsigned int Tov;  //счётчик кол-ва прерываний т0
	volatile unsigned char text[17];
	unsigned char loading[16]=" &Dimon devise&";
	unsigned char vnn[5]="STOP";
	//volatile float R=0.381; 
	volatile unsigned int t;
	//float w;
	//float v;
	//float s;
	//float ds;

void send(void)
{
  E=0x00;
  delay_us(400);   //отсылка данных
  E=0xFF;
}

void clear(void)
{
	C=0x00;
	PORTA=0b00000001;	  //очистка дисплея
	send();
	C=0xFF;
}			 

interrupt [EXT_INT0] void ext_int0_isr(void)
{	 
	flag=1;	 //флаг
}

interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
	Tov=Tov+1; //счётчие прерываний
}

// Declare your global variables here

void main(void)
{

	TCCR0=0x02;
	TCNT0=0x00;
	OCR0=0x00;

	TCCR1A=0x00;
	TCCR1B=0x00;
	TCNT1H=0x00;
	TCNT1L=0x00;
	ICR1H=0x00;
	ICR1L=0x00;
	OCR1AH=0x00;
	OCR1AL=0x00;
	OCR1BH=0x00;
	OCR1BL=0x00;
	GICR|=0x40;
	MCUCR=0x03;
	EMCUCR=0x00;
	GIFR=0x40;
	TIMSK=0x02;
	ACSR=0x80;

	DDRC=0xFF;
	DDRD=0x00;
	DDRA=0xFF;
	PORTA=0x00;
	PORTC=0x00;
	E=0xFF;
	RW=0x00;
	C=0x00;

	delay_ms(150);
	PORTA=0b00111000;
	send();
	delay_ms(100);
	PORTA=0b00000001;
	send();
	delay_ms(100);		   
	PORTA=0b00000110;
	send();
	delay_ms(100);
	PORTA=0b00001111;
	send();
	delay_ms(100);
	PORTA=0b00000001;
	send();
///////////////////////////////  

	C=0x00; //

	C=0xFF; // 
	delay_ms(10);
	for(i=0;i<16;i++)
	{ 
			PORTA=loading[i];
			send();		
	};
	delay_ms(1000);

	#asm("sei")

while (1)
  { 
	if(Tov>=1500)
	{ 
			Tov=0; //от переполнения
	};

	if(flag)
	{	   
			//#asm("cli") //временной запрет прерываний
			t=(Tov*256+TCNT0)/125;  //t в мс
			Tov=0;
			TCNT0=0x00;
			//#asm("sei") //разрешение прерываний

			itoa(t, text);
			clear();
			for (i=0;i<6;i++)  //вывод t
			{
					PORTA=text[i];
					send();
			};
			flag=0;
	};

  };	 

}

Пробовал и вариант с выключением/включением таймера, не помогло.

Изменено пользователем Dimon007
Ссылка на комментарий
Поделиться на другие сайты

Тут есть небольшая тонкость. Поскольку таймер бежит свободно, значения в регистре ICR могут оказаться по разные стороны перехода таймера через 65535. Это надо учесть, наряду с возможными циклами холостого пробега таймера, когда захвата нет...<br />

Как вариант, можно устанавливать флаг переноса по переполнению таймера.

А вот еще мысль, насчет того, что таймер бежит свободно... Регистр захвата ICPn принимает данные по внешнему событию от счетного регистра TCNT. ICPn мы можем только читать, но ведь в TCNT мы можем записывать. А что если сбрасывать его каждый раз после захвата и отсчитывать от 0? Как думаете, пройдет такой вариант?

Изменено пользователем Геннадий
Ссылка на комментарий
Поделиться на другие сайты

Переделал код...

Там снова все неверно. Выложите проект целиком, попробую поправить....

Любой, заслуживающий внимания, опыт приобретается себе в убыток...

Ссылка на комментарий
Поделиться на другие сайты

Кажется заработало. Исправил TIMSK=0x02 на TIMSK=0x01. В протеусе работает, завтра опробую на железе. Хотя, если период больше 524 мс, выдаёт левое число. Думал переполнение, изменил тип t на long - ноль реакции.

Изменено пользователем Dimon007
Ссылка на комментарий
Поделиться на другие сайты

Кстати, диаметр колеса какой?

И сколько раз в секунду должна измеряться скорость? (слишком часто нельзя - моргать будет...) В чем должна выводиться и с точностью до какого знака?

Любой, заслуживающий внимания, опыт приобретается себе в убыток...

Ссылка на комментарий
Поделиться на другие сайты

Попробовал в железе, не работает. МК перезагружается и всё. Диаметр примерно 66 см. Скорость определяется после каждого прохода магнита мимо датчика (т.е. после полного оборота колеса). Точность определения скорости до десятых. Выводится в км/ч.

Изменено пользователем Dimon007
Ссылка на комментарий
Поделиться на другие сайты

В общем, переделывать ваш код я не решился. Слишком много всего сделано "не так". Легче написать с нуля. Что я и сделал. Проект внизу, вместе с симуляцией в Протеусе. Я забил длину колеса 100 см (под реальную длину надо смотреть, изменять или нет интервал между измерениями, а также порог числа переполнений таймера и т.д.) Меряет нормально от 0.6 м/с (это ограничено длиной окружности колеса и временем обновления дисплея - пока каждую секунду, примерно). Код не вылизывал (по понятным причинам :) ), поэтому есть пространство для оптимизации - избавиться от некоторых переменных, оптимизировать расчет скорости и представления чисел (заточен под вывод, с некоторыми артефактами) написать свой цикл вывода на дисплей (я использовал стандартный) и т.д. В реальном образце надо смотреть, какие фронты будет иметь датчик Холла, какая подтяжка нужна на инт0 (у меня прерывание по нарастающему уровню и внутр. подтяжка). Возможно понадобится программный антидребезг. Кроме того, не мешало бы усреднение за некий период (пока тупо мерям, выводим, ждем, снова меряем). Но, как шаблон, работает. Остальное, при желании, доделаете...

...Диаметр примерно 66 см...

Значит длина где-то 207см. Его и использовать...

...Скорость определяется после каждого прохода магнита мимо датчика (т.е. после полного оборота колеса)...

Данные, во-первых, не успеют выводиться, во-вторых вы ничего не увидите (будет беспорядочное мельтешение). Надо выводить не чаще двух-трех раз в секунду...

...Точность определения скорости до десятых. Выводится в км/ч...

Сделал в м/с. Потом переделаете. Тоже до десятых. В протеусе удобно частоту генератора задавать и смотреть - при длине колеса 1м - скорость в м/с совпадают с частотой...

Попробовал в железе, не работает. МК перезагружается и всё...

Вы ватчдог, случаем не включили? Подтяжка по инт0 есть (и какая)?

post-20311-1233830948_thumb.jpg

SpeedMy.rar

Любой, заслуживающий внимания, опыт приобретается себе в убыток...

Ссылка на комментарий
Поделиться на другие сайты

Спасибо, буду разбираться) Между INT0 и землёй резистор на 39кОм. Ватчдог не включал.

Изменено пользователем Dimon007
Ссылка на комментарий
Поделиться на другие сайты

Между INT0 и землёй резистор на 39кОм.

Тогда в моем коде надо отключить подтяжку. Лучше резистор уменьшить где-то до 5кОм... Неплохо было бы импульсы с датчика холла завести в МК через компаратор на операционнике, а так, неизвестно какая там форма и амплитуда...

Любой, заслуживающий внимания, опыт приобретается себе в убыток...

Ссылка на комментарий
Поделиться на другие сайты

C той же точностью? А не могли бы код с симуляцией выложить? Ради интересу...

Любой, заслуживающий внимания, опыт приобретается себе в убыток...

Ссылка на комментарий
Поделиться на другие сайты

Ваша прошивка работает стабильно, но значения выдаёт (не нулевые) только от генератора, если просто геркон поставить и магнитом его замыкать/размыкать, то скорость 0. Моя прошивка реагирует на геркон, работает от генератора, но иногда (если длительное время нет высокого уровня на INT0, а затем подать) просто вешает МК. Насчёт точности я ещё не разобрался, но приблизительно верно.

Чуть поправил код, работает, зависает изредка, пока не понял почему (но уже не всегда при большом периоде).

#include <mega8515.h>
#include <delay.h>
#include <stdlib.h>
#include <stdio.h>

// Alphanumeric LCD Module functions
#asm
  .equ __lcd_port=0x1B ;PORTA
#endasm
#include <lcd.h>

       volatile char i;
       volatile bit flag;
       volatile bit st;
       volatile unsigned long Tov;  //счётчик кол-ва прерываний таймера т0
       volatile unsigned char text[17];
       volatile unsigned long t;
       unsigned char loading[16]=" &Dimon devise&";
       unsigned char vnn[5]="STOP";
       volatile float R=0.66; 
       float w;
       float v;
       float s;
       float ds;


interrupt [EXT_INT0] void ext_int0_isr(void)
{     
       flag=1;     //флаг на обработку данных
}

interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
       Tov=Tov+1; //счётчик переполнений
}

// Declare your global variables here

void main(void)
{       
       DDRC=0x00;
       DDRD=0x00;
       DDRA=0x00;
       PORTA=0x00;

       TCCR0=0x02;
       TCNT0=0x00;
       OCR0=0x00;

       GICR|=0x40;
       MCUCR=0x03;
       GIFR=0x40;
       TIMSK=0x02;
       ACSR=0x80;

       lcd_init(16);
///////////////////////////////  

       lcd_gotoxy(0,0);
       lcd_puts(loading);
       delay_ms(1500);
       ds=6.2832*R; //расстояние за 1 оборот колеса

       #asm("sei")
while (1)
     { 
       if(Tov>=1500)
       { 
               //Tov=0; //от переполнения
       };

       if(flag)
       {       
               t=(Tov*256+TCNT0)/125+1;  //период оборота в мс
               Tov=0;
               TCNT0=0x00;
               //////////////////////////// Физика ////////////////
               w=6.2832/(t*0.001);
               v=w*R;        
               s=s+ds;
               ///////////////////////////////////////////////////

               lcd_gotoxy(0,1);
               ltoa(t,text);
               lcd_puts(text); //выводим t
               for (i=0;i<4;i++)
               {
                       lcd_putchar(32);  //от мусора на дисплее
               };
               ftoa(s,2,text);
               lcd_gotoxy(8,1);
               lcd_puts(text); //выводим s 
               flag=0;
       };     


     };     


}

Project.rar

Изменено пользователем Dimon007
Ссылка на комментарий
Поделиться на другие сайты

Сделайте в моем коде

PORTD=0x00;

вместо

PORTD=0x04;

в коде инициализации, притяните к земле вход внешним резистором и будет и мой код на геркон реагировать...

Я же писал:

Тогда в моем коде надо отключить подтяжку. Лучше резистор уменьшить где-то до 5кОм...

Кроме того, от геркона может быть дребезг. Поэтому можно добавить делай и сброс флага в обработчике:

interrupt [EXT_INT0] void ext_int0_isr(void)
{
...
		else
		{ ...
		  fl_two=1;
		   //здесь добавить
		  delay_ms(1); //антидребезг
		  GIFR=0x40; //сбрасываем флаг прерывания по инт0
		}

Там у вас два бегающих числа. Что из них что?

Изменено пользователем Goodefine

Любой, заслуживающий внимания, опыт приобретается себе в убыток...

Ссылка на комментарий
Поделиться на другие сайты

На мой взгляд, логика программы не совсем правильная: таймер бежит бесконтрольно (TCNT0 считывается с большой погрешностью), прерывания могут возникать когда хотят, не очевидная последовательность действий программы, малый диапазон скоростей с корректной работой (МК может лучше, особенно если использовать захват таймера) , неустойчивые показания, куча переменных флоат и т.д...

Но решать вам, скажу только что в реальных условиях еще парочка другая глюков вылезет обязательно, поскольку надо обеспечить СХОДИМОСТЬ программы - а ее не видно...

Любой, заслуживающий внимания, опыт приобретается себе в убыток...

Ссылка на комментарий
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...