Цифровой датчик Холла
- Войдите на сайт для отправки комментариев
Пнд, 02/04/2012 - 17:06
Хочу через матлаб управлять вентилятором (при помощи цапа), который будет дуть на другой вентилятор и разгонять его. Со второго винта снимать частоту. Меняя напряжение на 1 винте добиться требуемых оборотов на 2м.
Вопрос - по какому протоколу работает датчик холла? OneWire? или что то другое, может есть библиотеки? Не хотелось бы мудрить с аналоговым.
Ни по какому. Практически все продаваемые датчики Холла цифровые, имеют 3 провода, два из них питание и сигнальный. В двух словах большинство работает так: когда магнитное поле отсутствует на сигнальном проводе напряжение питания (т.е. логическая 1), когда датчик находится в магнитном поле на сигнальном проводе 0 вольт (т.е. логический 0). Вы какой датчик холла используете? Каким напряжением питаете датчик? Вот этот код проверяли на датчике Холла (5В) и на ИК-датчике:
#define RPMpin 2 // датчик Холла long microsold = 0; int rpm = 0; void setup() { Serial.begin(9600); digitalWrite(RPMpin, 1); attachInterrupt(0, RPM, FALLING); } void loop() { Serial.println(rpm, DEC); // об/мин } void RPM (){ rpm = (1000000.0/(micros() - microsold))*60; microsold = micros(); }Еще даже не купил датчик, пока только решаем все тонкости проекта.
Ну если датчик покупной, то при правильной установке и подключении этот код должен работать.
Кстати, если не ошибаюсь, в куллерах от компа стоят датчики холла, можно разобрать куллер, удалить металлический сердечник с обмотками, только так чтобы ничего не замкнуть (а может и где-то подпаять) и будет вам вентилятор с датчиком холла да и еще на шаракоподшипнике (если куллер с шарикоподшипником).
Спасибо за подсказку!
Делается на лодку прибамбас который будет отображать на семисегментнике(4разряда), температуру, обороты двигателя итд.итп.
Лодочный двигатель Вихрь30.
Собственно сейчас ковыряюсь с тахометром, датчик холла будет стоять под маховиком на котором есть 4 магнита в каком порядке они стоят видно на фото макета .
Решил использовать ваш скетч для тахометра и здесь появились вопросы
1. После запуска моторчика обороты показывает но после полной остановки не становится в ноль (956)
2. оч большой разброс в оборотах при стабильном питании 1.5V моторчика
зы, скетч юзал не изменяя, деление не делал, все как есть
1. Это нормально, потому как нужно дописывать сброс оборотов в 0 при отсутствии импульсов.
2. Питание может и стабильно, а вот обороты двигателя могу и скакать + к этому при таком способе измерения оборотов очень важно равномерное расположение магнитов на валу относительно друг друга и оси вращения, небольшое биение вала приведет к неверным данным. Нужно дописывать сглаживание или применять другой способ подсчета оборотов.
А датчик холла у вас сработывает одинакого что на S что на N ? или только на один полюс? Данный расчет предназначен для одного срабатывания датчика за один оборот вала.
Спасибо за ответ.
Датчик срабатывает так: северный полюс - имеем на выходе лог1, южный лог0 и так далее. В итоге rpm нужно будет поделить на 4 если не ошибаюсь.
Делай прерывания по времени, думаю так проще.
#include <AFMotor.h> #include <MsTimer2.h> //библиотека таймера - гугли ее AF_DCMotor motor(3); char inChar; unsigned int state=0; unsigned int rmp=0; unsigned int time=0; int mspeed=255; unsigned long timpause=5000; void flash() //обработка прерывания таймера { //будет вызыватся по истечении времени указанного в timpause MsTimer2::stop(); //остановить таймер detachInterrupt(1); //остановить прерывания Serial.print("Speed: "); Serial.print(mspeed); Serial.print("\t"); Serial.print("rmp:"); Serial.println(rmp*12); rmp=0; MsTimer2::set(timpause, flash); //установка таймера attachInterrupt(1, blink, RISING ); //вкл прерывания MsTimer2::start();//вкл таймера } void setup() { Serial.begin(9600); attachInterrupt(1, blink, RISING ); //прерывания на 3м порту - с датчика холла motor.setSpeed(mspeed); motor.run(RELEASE); motor.run(FORWARD); delay(500); MsTimer2::set(5000, flash); // 5 сек MsTimer2::start(); //включаем прерывания по таймеру } void loop() { if (Serial.available()){ inChar = (char)Serial.read(); if (inChar=='1') mspeed+=10; else mspeed-=10; motor.setSpeed(mspeed); } } void blink() //прерывания с датчика холла { ++rmp; //увеличиваем счетчик на 1 }Делай прерывания по времени, думаю так проще.
Не проще. Разве если нужна точность до "микросекунд" (но на интервалах в 5 сек, это вряд ли).
Имхо намного проще в loop сделать так:
void loop(){ static unsigned long lastReportTime=0; if(millis()-lastReportTime>=5000){ // прошло 5 sec с прошлого отчета lastReportTime=millis(); Serial.print("Speed: "); Serial.print(mspeed); Serial.print("\trmp:"); Serial.println(rmp*12); rmp=0; } // тут логика чтения serial из предыдущего примера и установка скорости мотора }И еще, желательно не забывать про ключевое слово volatile при объявлении переменных меняющихся в обработчике прерываний ( rpm )
А можно сочинить такую "помогалку дефайн" (разместить где-то в начале скетча):
#define EVERY_var(time,var,action) { static unsigned long var=0; if(millis()-var>=time){var=millis(); action}} #define EVERY(time,action) EVERY_var(time,lastActionTime__LINE__,action) // выполняет действие action каждые time миллисекунд (если регулярно ее вызывать)Тогда в Loop() все будет вообще выглядить "чисто и красиво"
void loop(){ EVERY(5000, // выполняется каждые 5 сек. Serial.print("Speed: "); Serial.print(mspeed); Serial.print("\trmp:"); Serial.println(rmp*12); ); EVERY(10000, // тут еще что-то делаем, каждые 10 sec ); }И в каждом цикле тратим время
Сперва на
millis()Затем вычитание
millis()-lastReportTimeНу и сравнение конечно
millis()-lastReportTime>=5000Только после чего совершается пропуск ненужного фрагмента.
Выполнение этого кода еще все время обрывается внешними прерываниями...
Ну и конечно нельзя забывать про очень быстрый рост размера переменной
lastReportTime- Это же милисекунды как ни как!Запустить на 10 минут или на пару часов - есть разница?
Плюс, за все это время прилетит еще парочку прерываний - точность тоже никчерту.
И в каждом цикле тратим время
Сперва на
millis()Затем вычитание
millis()-lastReportTimeНу и сравнение конечно
millis()-lastReportTime>=5000Только после чего совершается пропуск ненужного фрагмента.
Ой. Не смешите меня. Это микросекунды (даже не мили). 5 sec времени, по сравнению с этим - вечность. Ну какая разница прийдет отчет в ровно через 5 sec, или 5.000005 секунды? Сколько там лишнего успеет накопить RPM за это время? В процентах от его текущего значения это сколько будет? Комаринный писк?
Да по сравнению с тем что занимает вывод в сериал это вообще ничто (ой, а мы его еще постоянно проверяем, нестрашно что на это тоже тратится время?)
Да и вообще, мы что платим деньги за каждый такт процессора? Он же у нас, один черт ничем не занимается больше. Чем крутить пустой loop будет лучше?
Что лучше, экономить такты процессора, которые, вданном случае не нужны, или подрубасить либу, потратить дефецитную память, заюзать дефецитный ресурс "таймер" (который может пригодится для задачь которые действительно требуют "точности")? Ну а как вам нужно будет 5-ть "псевдо-одновременных" действий? Дополнительные контроллеры начнете ставить?
Кстати вход/выход в обработчик таймера - тоже не бесплатная операция. нужно запомнить "текущие состояние проца", потом востановить. Отработать все ваши stop/start. В итоге еще не известно кто больше тиков rpm пропустит.
Выполнение этого кода еще все время обрывается внешними прерываниями...
И что с того? Для того прерывания и предуманы. Негативных эффектов от этого - нет.
Ну и конечно нельзя забывать про очень быстрый рост размера переменной
lastReportTime- Это же милисекунды как ни как!А взять калькулятор и посчитать сколько влезет в unsigned long? На 49-дней хватит. Более того, даже после этого - ничего страшного не случится :) Код нормально отработает и в случае переполнения.
Вообщем опять пришили: и что с того? лично у меня нет страха перед "большими числами".
Запустить на 10 минут или на пару часов - есть разница?
Смотря что запускать. Если сердечника в финскую сауну - есть, если деньги на ветер - то нет.
Плюс, за все это время прилетит еще парочку прерываний - точность тоже никчерту.
И опять "и что с того"? По сравнению с тем что мы насчитали в rpm за 5 sec - это фигня. Между прочим операции с делением это тоже "не точная" штука :)
Кстати, вы почему-то упустили что в вашем коде вы тоже на время отчета делаете detachInterrupt.
Вообщем заявить "точность ни к черту" - это круто. А вот цифрами показать что ваш код на два порядка более точный сможете?
IMHO он нифига не точнее, более ресурсоемкий, менее читабельный.
Если уж очень сильно озаботились точностью, то нужно брать в зубы даташит на ATMEGA (или искать другие библиотеки работы с таймером, где это реализовано) и искать как настроить таймер и компаратор, что-бы он сам считал/увеличивал rpm. Без обработчика прерыаний, не отвлекая процессор даже на это, на железном уровне (ой... а ATMEGA даже такое умеет), так же как он не отвлекает камень на генерацию PWM.
Но обычно, на это заморачиватся имеет смысл только если речь идет о необходимой точности в микросекунды (не милли).
заюзать дефецитный ресурс "таймер" (который может пригодится для задачь которые
Кстати, похоже долго ждать этого не нужно AFMotor тоже использует таймеры. Причем разные, в зависимости от того под какую плату компилим. Так что можно поймать очень классную головную боль. Из серии почему на mega/uno по разному себя ведет или вообще не работает.
Да и подключение любых других Lib - тоже становится "ходьбой по минному полю" (Tone и т.п.) нужно внимательно следить какие таймеры уже заняты.
1. После запуска моторчика обороты показывает но после полной остановки не становится в ноль (956)
Логично. rpm у вас пересчитывается только в момент сработки датчика. А при остановленном движке - сработок нет. Функция RPM() - не вызывается.
Поэтому нежно в loop(), поверять если с последней сработки прошло слишком много времени - насильно обнулять rpm
void loop(){ ... if(micros() - microsold>КАКОГО_ТО_ЗНАЧЕНИЯ)rpm=0; ... }и да... microsold лучше, от греха объявить так:
а не просто long.
2. оч большой разброс в оборотах при стабильном питании 1.5V моторчика
Можете немного "усреднить". Заведите дополнительную переменную counter. И в RPM() - увеличивайте ее. И только когда она накопит, например 20-ть сработок, обнуляем ее и пересчитываем rpm переменную (естественно учтя что у нас теперь замеряно время не между каждой сработкой, а межку каждой 20-той сработкой).
offtop: заметил на фото "полиморфус". Реально круто/удобная штука ;) для подобных игр.
Извиняюсь, что пропал. Неожиданно в больницу загремел.
Всем спасибо за советы, нарыл темку и сварганилскопипастил :) тахометр из нее http://robocraft.ru/blog/electronics/594.html
по тестам работает норм вот только сравнить бы с каким нибудь эталоном.
вот что вышло в предварительном варианте
/* На лодку Функции: вывод данных о температуре "Блок температуры" занят пин МК А0 к пину А1 подключен фоторезистор для динамической регулировки яркости дисплея TO DO: Пин А5 подключен к датчику холла для подсчета оборотов двигателя и отображения на дисплее "Блок тахометра" Пин 2 кнопка переключения режимов, температура или тахометр TODO: пин 13 ардуины использовать для индикации чтения данных 7й сегмент индикатора используется обычными светодиодами для индикации режима работы */ //We always have to include the library #include "LedControl.h" /* Now we need a LedControl to work with. ***** These pin numbers will probably not work with your hardware ***** pin 12 is connected to the DataIn (18 нога МК) pin 11 is connected to the CLK (17 нога МК) pin 10 is connected to LOAD (16 нога МК) We have only a single MAX72XX. */ LedControl lc=LedControl(12,11,10,1); /* we always wait a bit between updates of the display */ const int analogInPin = A1; int sensorValue = 0; int outputValue = 0; // можно убрать // value output to the PWM (analog out) int sw1 = 3; // номер пина ардуины для кнопки int sw1_state = 1; // статус и счетчик нажатий кнопки SW1 double Temp; // Переменная температуры /* Пины, к которым подключен энкодер */ enum { ENC_PIN1 = 2, ENC_PIN2 = A5 }; enum { FORWARD = 1, BACKWARD = -1 }; /* Если что, revolutions здесь и далее - обороты, а не революции (: */ long revolutions = 0, revolutions_at_last_display = 0; int direction = FORWARD; uint8_t previous_code = 0; /* Реакция на событие поворота */ void turned(int new_direction) { if (new_direction != direction) { revolutions = 0; revolutions_at_last_display = 0; } else ++revolutions; direction = new_direction; } /* Объеденил чтение кода Грея с энкодера с его декодированием */ uint8_t readEncoder(uint8_t pin1, uint8_t pin2) { uint8_t gray_code = digitalRead(pin1) | (digitalRead(pin2) << 1), result = 0; for (result = 0; gray_code; gray_code >>= 1) result ^= gray_code; return result; } void setup() { pinMode(sw1, INPUT); // установка пина(3) на чтение (кнопка) digitalWrite(sw1, HIGH); //установка лог 1 пине 3 /* The MAX72XX is in power-saving mode on startup, we have to do a wakeup call Отключение дисплея */ lc.shutdown(0,false); /* Set the brightness to a medium values Яркость дисплея 0-15 */ lc.setIntensity(0,0); /* and clear the display Очистка дисплея */ lc.clearDisplay(0); Serial.begin(9600); // убрать в окончательном варианте pinMode(13, OUTPUT); pinMode(ENC_PIN1, INPUT); pinMode(ENC_PIN2, INPUT); } // Преобразование температуры от датчика в нормальный вид double Thermister(int RawADC) { //double Temp; // See <a data-cke-saved-href="http://en.wikipedia.org/wiki/Thermistor" href="http://en.wikipedia.org/wiki/Thermistor" rel="nofollow">http://en.wikipedia.org/wiki/Thermistor</a> for explanation of formula Temp = log(((10240000/RawADC) - 10000)); Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp)); Temp = Temp - 273.15; // Convert Kelvin to Celcius return Temp; } //============================================================================================ void loop() { /*------------------- Блока обработки нажатия кнопки --------------------*/ if(sw1_state==3) // если счетчик достиг переполнения { sw1_state=0; // сброс счетчика в 0 } if(!digitalRead(sw1)) // чтение состояния кнопки { delay(10); //задержка для устрания дребезга if(!digitalRead(sw1))// продолжаем если все нормально { sw1_state++; //увеличиваем на единицу lc.clearDisplay(0); // очистка дисплея do{ }while(!digitalRead(sw1)); } } /*------------------- Конец блока обработки нажатия кнопки --------------------*/ //digitalWrite(13, HIGH); /*Фоторезистор*/ sensorValue = analogRead(analogInPin); //Читаем показания датчика фоторезистора lc.setIntensity(0,map(sensorValue, 0, 1023, 0, 15)); //Устанавилваем яркость дисплея, чем светлее на улице тем ярче дисплей /*Конец фоторезистора*/ if(sw1_state==0){ // оператор выбора состояния кнопки и запуск соответствующего блока // //----------------------------- // Блок температуры //----------------------------- double temp = Thermister(analogRead(0)); // Читпем данные с термодатчика int temp1 = temp; if (temp1>95) // == Превышение температуры 95 градусов - пишем HELP и мигаем дисплеем // до тех пор пока температура не установится меньше 95 градусов { lc.shutdown(0,true); // отключаем дисплей lc.setRow(0,0,B00110111); // symbol H lc.setRow(0,1,B01001111); // symbol E lc.setRow(0,2,B00001110); // symbol L lc.setRow(0,3,B01100111); // symbol P lc.setRow(0,7,B01110000); // также мигаем светодиодами режима работы delay(500); // задержка полсекунды lc.shutdown(0,false); // Включаем дисплей delay(500); // задержка полсекунды lc.clearDisplay(0); // очистка дисплея }else{ // Температура в номе? Да, отображаем ее на дисплее lc.setDigit(0,0,(temp1/10),false); // В сегмент 1 десятки lc.setDigit(0,1,(temp1-((temp1/10)*10)),true); // В сегмент 2 единицы + зажигаем десятичную точку lc.setDigit(0,2,((temp-temp1)*10),false); // В сегмент 3 деситичную долю lc.setRow(0,3,B01000011); // в сегмент 4 выводим символ температу "с" lc.setRow(0,7,B01000000); // в Сегмент 7 зажечь 3 светодиода ABC (тест) Serial.println(temp1); delay(300); // Задержка в полсекунды //digitalWrite(13, LOW); delay(300); // Задержка в полсекунды } /*--------------------------- Конец блока температуры -----------------------------*/ } if(sw1_state==1){ // если состояние 1 то выполнение блока тахометра /*---------------------------- Блок тахометра -----------------------------*/ //lc.clearDisplay(0); // очистка дисплея lc.setRow(0,7,B00100000); /* Читаем значение с энкодера */ uint8_t code = readEncoder(ENC_PIN1, ENC_PIN2); /* Обрабатываем его */ if (code == 0) { if (previous_code == 3) turned(FORWARD); else if (previous_code == 1) turned(BACKWARD); } previous_code = code; /* Раз в секунду выводим накопленную информацию */ static unsigned long millis_at_last_display = 0; if (millis() - millis_at_last_display >= 1000) { /* ... скорость вращения в оборотах в секунду */ Serial.print(revolutions - revolutions_at_last_display);Serial.print(" <<SEC ---- MINUTE >>> ");Serial.println(((revolutions - revolutions_at_last_display)*60)/2); int RPM = ((revolutions - revolutions_at_last_display)*60)/4; lc.setDigit(0,0,(((RPM-(RPM%1000))%10000)/1000),false); // В сегмент 1, тысячи lc.setDigit(0,1,(((RPM-(RPM%100))%1000)/100),false); // В сегмент 2, сотни lc.setDigit(0,2,(((RPM-(RPM%10))%100)/10),false); // В сегмент 3, десятки lc.setDigit(0,3,(RPM%10),false); // В сегмент 1, тысячи Serial.println(RPM%10); Serial.println (((RPM-(RPM%10))%100)/10); Serial.println (((RPM-(RPM%100))%1000)/100); Serial.println (((RPM-(RPM%1000))%10000)/1000); millis_at_last_display = millis(); revolutions_at_last_display = revolutions; } /*---------------------------- Конец блока тахометра -----------------------------*/ } if(sw1_state==2){ // если состояние 2 то выполнение блока резерва /*---------------------------- Резервный Блок -----------------------------*/ //lc.clearDisplay(0); // очистка дисплея lc.setRow(0,7,B00010000); /*---------------------------- Конец резервного блока -----------------------------*/ } }PS: Да Вы правы полиморфус классная штука не жалею что купил :)
как можно удалять свои сообщения? Дубляж вышел