Мини компьютер на мотоцикл Урал

zenkiller
Offline
Зарегистрирован: 10.03.2013

Делаю микрокомпьютер для урала на дисплее 3310 (Индикатор заряда и напряжения аккумулятора,температура, тахометр цифровой и графическая шкала к нему, индикаторы поворотов и света.)

Сталкнулся с проблемой тахометра. Перечитал много о цифровых тахометрах, но так и разобраться не могу до конца. Кароче не работает, помогите сделать. Сигнал буду брать на прямую с прерывателя (один переход с HIGH на LOW = 1 оборот)

Делал по аналогии с http://cxem.net/arduino/arduino66.php

#include <PCD8544.h>

//для индикатора питания
int AKB = 12;
static const byte sensorPin = 0;
static const byte ledPin = 13;


//для тахометра
volatile float time = 0;
volatile float time_last = 0;
volatile int rpm_array[5] = {0,0,0,0,0};


// Размеры ЖК-дисплея (в пикселях) ...
static const byte LCD_WIDTH = 84;
static const byte LCD_HEIGHT = 48;

// рисунок аккумулятора без зарядки 
static const byte AKB_WIDTH = 10;
static const byte AKB_HEIGHT = 1;
static const byte AKB_nocharge[] = { 0x7C, 0x46, 0x46, 0x44, 0x44, 0x44, 0x46, 0x46, 0x7C, 0x00};

// рисунок аккумулятора с зарядкой 
static const byte AKB_charge[] = { 0x7C, 0x7E, 0x7E, 0x7C, 0x7C, 0x7C, 0x7E, 0x7E, 0x7C, 0x00};

static PCD8544 lcd;

// функция прерывания
void chopper_interrupt()
{
   time = (micros() - time_last); 
   time_last = micros();
}

void setup() {
  Serial.begin(9600); 
  
  lcd.begin(LCD_WIDTH, LCD_HEIGHT);
  
    //Цифровой Pin 2 Установить как прерывание
 attachInterrupt(0, chopper_interrupt, FALLING);
 
  
  pinMode(ledPin, OUTPUT);
  pinMode(AKB, INPUT);
}


void loop() {
  static byte xChart = LCD_WIDTH;
  
  digitalWrite(ledPin, HIGH);
  //=============(Индикатор питания)==================
  
  // Считать напряжение
  float volt = (5 * analogRead(sensorPin) / 1023 / 0.714); // 0.714 переводной коэф делителя напряжения
  
  // Нарисуйте аккумулятор 
  lcd.setCursor(0, 0);
  if ( digitalRead(AKB) == 0 )
  {
    lcd.drawBitmap(AKB_nocharge, AKB_WIDTH, AKB_HEIGHT);
  }
  else
  {
    lcd.drawBitmap(AKB_charge, AKB_WIDTH, AKB_HEIGHT);
  }
  
  // показать напряжение аккумулятора
  lcd.setCursor(10, 0);
  lcd.print(volt, 1);
  lcd.print("B");
  //=================================================
  
  
  
  //===============(Тахометр)========================
  lcd.setCursor(37, 0);
  lcd.print("RPM:");

  int rpm = 0;
      
  
  //delay(400);
  //Цеферблат оборотов
  lcd.setCursor(61, 0);
  lcd.print(rpm);   

  ////lcd.setCursor(4, 1);
  ////lcd.print(time);   

  //Обновление RPM
  if(time > 0)
  {
    //Усреднение(сглаживание)
      rpm_array[0] = rpm_array[1];
      rpm_array[1] = rpm_array[2];
      rpm_array[2] = rpm_array[3];
      rpm_array[3] = rpm_array[4];
      rpm_array[4] = 60*(1000000/(time*1));    
    //Среднее значение по 5 замерам
      rpm = (rpm_array[0] + rpm_array[1] + rpm_array[2] + rpm_array[3] + rpm_array[4]) / 5;
  }
 


  digitalWrite(ledPin, LOW);  
  delay(500);
}

 

maksim
Offline
Зарегистрирован: 12.02.2012

А зажигание у вас электронное или контактное?

zenkiller
Offline
Зарегистрирован: 10.03.2013

контактное

maksim
Offline
Зарегистрирован: 12.02.2012

Тогда вы должны знать о таком явлении как дребезг котактов. И рекомендую все-таки поставить опторазвязку иначе можно сжеч дуину.

zenkiller
Offline
Зарегистрирован: 10.03.2013

http://hardlock.org.ua/viewtopic.php?f=9&t=16 я вот так буду делать, через транзистор, только не с катушки наверное буду брать, а с самого прерывателя.

maksim, вы можете мне подсказать по поводу кода? почему не рабаотает. уже 2 день бьюсь...

zenkiller
Offline
Зарегистрирован: 10.03.2013

естественно я сейчас пробуду не на прерыватете а на кнопке простой. и получается так что сейчес RPM показывает 0, и ещё, я же бал код вот этот 

#include <LiquidCrystal.h>
LiquidCrystal lcd(3, 5, 9, 10, 11, 12);

volatile float time = 0;
volatile float time_last = 0;
volatile int rpm_array[5] = {0,0,0,0,0};

void setup()
{
  //Digital Pin 2 Set As An Interrupt
 attachInterrupt(0, fan_interrupt, FALLING);

  // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("Current RPM:");
}

void loop()
{
  int rpm = 0;
  
  while(1){    

     //Slow Down The LCD Display Updates
  delay(400);
  
  //Clear The Bottom Row
  lcd.setCursor(0, 1);
  lcd.print("                ");   
  
  //Update The Rpm Count
  lcd.setCursor(0, 1);
  lcd.print(rpm);   

  ////lcd.setCursor(4, 1);
  ////lcd.print(time);   

  //Update The RPM
  if(time > 0)
  {
    //5 Sample Moving Average To Smooth Out The Data
      rpm_array[0] = rpm_array[1];
      rpm_array[1] = rpm_array[2];
      rpm_array[2] = rpm_array[3];
      rpm_array[3] = rpm_array[4];
      rpm_array[4] = 60*(1000000/(time*7));    
    //Last 5 Average RPM Counts Eqauls....
      rpm = (rpm_array[0] + rpm_array[1] + rpm_array[2] + rpm_array[3] + rpm_array[4]) / 5;
  }
 
 }
}

void fan_interrupt()
{
   time = (micros() - time_last); 
   time_last = micros();
}

и убал  while(1), так как получется так что зацикливалась ардунка на нем(тахометре) и как кие-то бешаные цифры показывала в RPM, в том числе и отрицательные

sva1509
Offline
Зарегистрирован: 07.12.2012

Доброго времени суток !

Я бы тахометр делал через таймер1.

Настройте таймер 1 в нормальный режим (как это сделать написано в datasheet на Ваш контроллер) частота тактирования таймера задается в зависимости от длинны импульса получаемого на вход (извените не знаю частоты вращения двигателя Урала) Об/Мин_мин / 60 получаем = Об/Сек_мин ; 1 / Об/Сек_мин = Длину одного оборота в секундах. После чего подбираем частоту деления таймера так, что бы в "Длину одного оборота в секундах" вошло 50000 - 60000 тактов. Далее настраиваем маску прерываний от таймера на два прерывания TIMSK = 33 (32(прерывание от ICR) и 1(прерывание от переполнения)) и настраиваем два вектора. Вектор от ICR считывает егистр ICR1 и обнуляет TCNT1. Второе прерывание выставляет флаг двигатель остановлен. Число из ICR быдет длинной входного импульса в тактах, чем меньше тактов - тем выше скорость вращения, зная опорную частоту можно перещитать длинну импульса в секундах, а соответственно и количество оборотов в минуту.

maksim
Offline
Зарегистрирован: 12.02.2012

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

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

#include <LiquidCrystal.h>
LiquidCrystal lcd(3, 5, 9, 10, 11, 12);

volatile uint32_t time = 0;

void setup()
{
  attachInterrupt(0, fan_interrupt, FALLING);
  lcd.begin(16, 2);
  lcd.print("Current RPM:");
}

void loop()
{
  static uint32_t millis_prev;
  if(millis()-millis_prev >= 1000)
  {
    detachInterrupt(0);   
    lcd.setCursor(0, 1);
    lcd.print("                ");   
    lcd.setCursor(0, 1);
    lcd.print(time);
    time = 0;    
    attachInterrupt(0, fan_interrupt, FALLING);
    millis_prev = millis();
  }
}

void fan_interrupt()
{
  time++; 
}

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

zenkiller
Offline
Зарегистрирован: 10.03.2013

как вы и говорили. дребезг дал свое... возможно ли избавиться от бребезга програмно в этом случае? 

maksim
Offline
Зарегистрирован: 12.02.2012

Конечно можно

void fan_interrupt()
{
  static uint32_t millis_prev;
  if(millis()-10 > millis_prev) time++; 
  millis_prev = millis();  
}

число 10 это промежуток времени в милисекундах в течении которого последующие срабатывания будут игнорироваться.

Так же может применяться такая конструкция

void fan_interrupt()
{
  static uint32_t millis_prev;
  if(millis()-10 > millis_prev) 
  {
    time++; 
    millis_prev = millis();  
  }
}

 

zenkiller
Offline
Зарегистрирован: 10.03.2013

Подскажите пожалуйсто вот ещё в чем. Как сделать мигание индикатора поворота не прерывая основной программы.

#include <PCD8544.h>

//для индикаторов
int LEFT = 1;
int RIGHT = 2;
int pa = 1500;

// Размеры ЖК-дисплея (в пикселях) ...
static const byte LCD_WIDTH = 84;
static const byte LCD_HEIGHT = 48;
                                
// рисунок левого поворота
static const byte LEFT_WIDTH = 7;
static const byte LEFT_HEIGHT = 2;
static const byte LEFT_H[] = {0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8,
                              0x02, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF,};

static const byte LEFT_L[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};
                                
static PCD8544 lcd;


// программа для левого поворота
void left_prog()
{
    lcd.setCursor(1, 14);
    lcd.drawBitmap(LEFT_H, LEFT_WIDTH, LEFT_HEIGHT);
    delay(pa);
    lcd.drawBitmap(LEFT_L, LEFT_WIDTH, LEFT_HEIGHT);
    delay(pa);
}

void setup() {
  
  lcd.begin(LCD_WIDTH, LCD_HEIGHT);

}


void loop() {
  static byte xChart = LCD_WIDTH;
  
  digitalWrite(ledPin, HIGH);

// индикатор левого поворота
      
   if ( analogRead(LEFT) == 0 )
  {
    lcd.setCursor(1, 14);
    lcd.drawBitmap(LEFT_L, LEFT_WIDTH, LEFT_HEIGHT);
  }
  else
  {
    left_prog();
  }
  
  digitalWrite(ledPin, LOW);  
  delay(500);
}

 

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