Помогите свести код к функции

wixa
Offline
Зарегистрирован: 05.02.2012
#include <SPI.h>

int led = 0;       // переменная для вывода на led
int val = 0;       // переменная для хранения значения входного напряжения
int potPin = 2;    // потенциометр подключается к 2-му порту
//Порт подключенный к ST_CP 74HC595 SS
int latchPin = 22;
//Порт подключенный к SH_CP  74HC595 SCK
int clockPin = 32;
//Порт подключенный к DS 74HC595 mosi
int dataPin = 33;

void setup() {
  //устанавливаем режим порта выхода
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void STI ()
{  //устанавливаем LOW на latchPin пока не окончена передача байта
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, LSBFIRST, highByte(led)); //ввыводим старший байт
    shiftOut(dataPin, clockPin, LSBFIRST, lowByte(led));   // выводи младший байт
    //устанавливаем HIGH на latchPin, чтобы проинформировать регистр, что передача окончена.
    digitalWrite(latchPin, HIGH);
    
}
  
  
void loop() {
  val = analogRead(potPin);    // считываем значение с потенциометра
  val = val/4;  // конвертируем из 0-1023 к 0-255
 if (val>=0 && val<=16) // сравниваем значение потенциометра с диапазоном загорания первого led ( 16=256(max)/16(кол светодиодов))
 {
led=1; // присваем переменой led значение при котором загорится первый светодиод (0b0000000000000001)
STI(); // ввыводим 2 байта
 } 
 if (val>16 && val<=32) // сравниваем значение потенциометра с диапазоном загорания первого и второго led  (32=(256(max)/16(кол светодиодов))*2)
 {
led=3; // присваем переменой led значение при котором загорится первый и второй светодиод (0b0000000000000011)
STI(); // ввыводим 2 байта
 } 
 if (val>32 && val<=48) // сравниваем значение потенциометра с диапазоном загорания первого и второго led  (48=(256(max)/16(кол светодиодов))*3)
 {
led=7; // присваем переменой led значение при котором загорится первый и второй светодиод (0b0000000000000111)
STI(); // ввыводим 2 байта
 } 
 if (val>48 && val<=64) // и.т.д
 {
led=15;
STI();
 } 
 if (val>64 && val<=80)
 {
led=31;
STI();
 } 
 if (val>80 && val<=96)
 {
led=63;
STI();
 } 
 if (val>96 && val<=112)
 {
led=127;
STI();
 } 
 if (val>112 && val<=128)
 {
led=255;
STI();
 } 
  if (val>128 && val<=144)
 {
led=511;
STI();
 } 
   if (val>144 && val<=160)
 {
led=1023;
STI();
 } 
    if (val>160 && val<=176)
 {
led=2047;
STI();
 }
    if (val>176 && val<=192)
 {
led=4095;
STI();
 }
    if (val>192 && val<=208)
 {
led=8191;
STI();
 }
    if (val>208 && val<=224)
 {
led=16383;
STI();
 }
    if (val>224 && val<=240)
 {
led=32767;
STI();
 }
    if (val>240 && val<=255)
 {
led=65535;
STI();
 }     
}

 Помогите пожалуйста основной цикл укоротить с помощью переменных и стандартных функций. Это часть проекта цифрового тахометра с светодиодной индикацией. Суть основной функции сводится к тому что значение снятое с потенциометра сравнивается с диапазоном загорания 1, 2, 3 - 16 светодиодов вместе ( от led = 0b00000000 00000001 до 0b11111111 11111111 ).

 

Как бы это сделать через "for ( ....) "???

 

Заранее благодарен за помощь 

wixa
Offline
Зарегистрирован: 05.02.2012

Извините не представился :)

 

Я новичок. Дня три как получил из Китая Arduino mega 2560 Crazy Kit.....

 

В планах сделать led подсветку приборной панели Nissan Note с светодиодной индикацией Тахометра, спидометра, топлива и температуры двигателя. Данные планирую снимать с CAN шины автомобиля.

Diemon
Offline
Зарегистрирован: 18.11.2011

 Просмотрел код по диагонали. Первая мысль это значение val делить на 16 и брать только целую часть. В итоге у нас будут значения от 0 до 15. Потом через switch case сравнивать. Должно получиться намного короче

wixa
Offline
Зарегистрирован: 05.02.2012

Спасибо. По поводу /16 понял. А вот про switch case прочитал в разделе программирование, но ничего не понял :(. Если не трудно , киньте простенький пример.  

Diemon
Offline
Зарегистрирован: 18.11.2011

http://www.arduino.ru/Reference/SwitchCase

Я это вижу так:

//тут идет деление на 16 и получение новой переменной val
switch (val) {  //значение от 0 до 15
  case 0:
    led=1;
    break;
  case 1:
    led=3;
    break;
 } //и так далее
// и уже за циклом идет вызов функции STI() 

 

wixa
Offline
Зарегистрирован: 05.02.2012

Спасибо, понял. A как быть со значением Led , может тоже как то можно упростить. Там формула получается led = led*2+1, то-есть если  в предидущий момент led = 3 (0b00000000 00000011) горит 2 светодиода, то в следующий момент led=3*2+1=7 что в двоичном исчислении = 0b00000000 00000111 .... ну и так далее... может как то через for можно упростить....

Diemon
Offline
Зарегистрирован: 18.11.2011

А если значение переменной перескочит так, что led должен поменяться с 3 на 15 сразу. Такое может быть?

wixa
Offline
Зарегистрирован: 05.02.2012

Я думаю, что скорость считывания значения, будет гораздо быстрее чем падение  или повышение оборотов двигателя, тем более что градация на одно деление светодиодом в 500 оборотов.... так что в любом случае светодиоды будут загораться и тухнуть поочереди..... поидеи :)

Diemon
Offline
Зарегистрирован: 18.11.2011

 Вот, сваял тестовый код для проверки моей идеи. Вроде правильно работает.  Заливаете в ардуину и открываете сериал монитор. В него вбиваете число, которое получается от деления на 4 (val = val/4;) тоесть от 0 до 255. а в сериал монитор выходит значение val_new которое от 0 до 16 и led которое от 1 до 65535. На красивость кода внимания не обращай, делался по быстрому. Функция serReadInt добавлена чтоб с сериала нормально читалось и для тебя не нужна

int led = 0;       // переменная для вывода на led
int val_new = 0;       // переменная для хранения значения входного напряжения
int potPin = 2;    // потенциометр подключается к 2-му порту
//Порт подключенный к ST_CP 74HC595 SS
int latchPin = 22;
//Порт подключенный к SH_CP  74HC595 SCK
int clockPin = 32;
//Порт подключенный к DS 74HC595 mosi
int dataPin = 33;
int resultInt;

void setup() {
        Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
}

void STI ()
{  //устанавливаем LOW на latchPin пока не окончена передача байта
    Serial.print("val_new: ");
    Serial.println(val_new);
    Serial.print("led: ");
    Serial.println(led);
}

void loop() {

        // send data only when you receive data:
        if (Serial.available() > 0) {
               resultInt = serReadInt();
               val_new=resultInt/16;
               switch (val_new) {
                 case 0: led=1; break;
                 case 1: led=3; break;
                 case 2: led=7; break;
                 case 3: led=15; break;
                 case 4: led=31; break;
                 case 5: led=63; break;
                 case 6: led=127; break;
                 case 7: led=255; break;
                 case 8: led=511; break;
                 case 9: led=1023; break;
                 case 10: led=2047; break;
                 case 11: led=4095; break;
                 case 12: led=8191; break;
                 case 13: led=16383; break;
                 case 14: led=32767; break;
                 case 15: led=65535; break;
               }
               STI();
        }
        delay(1000);
}

int serReadInt()
{
  int i, serAva;
  char inputBytes [7]; // массив для хранения байтов
  char * inputBytesPtr = &inputBytes[0]; // указатель на первый элемент массива

  if (Serial.available()>0) // проверяем есть ли данные
 {
    delay(5);//чутка ждем, чтобы все данные прошли
    serAva = Serial.available(); // получаем количество доступных байт
    for (i=0; i<serAva; i++) // загружаем байты в массив
      inputBytes[i] = Serial.read();

    inputBytes[i] = '\0'; // дописываем NULL в конец массива
    return atoi(inputBytesPtr); // Вызываем atoi функцию, стандартная функция С
  }
  else
    return -1; // Возвращаем -1, если ничего не получили
}

 А вот и ваша функция уже исправленная. Я думаю должно быть так:

#include <SPI.h>

int led = 0;       // переменная для вывода на led
int val = 0;       // переменная для хранения значения входного напряжения
int potPin = 2;    // потенциометр подключается к 2-му порту
//Порт подключенный к ST_CP 74HC595 SS
int latchPin = 22;
//Порт подключенный к SH_CP  74HC595 SCK
int clockPin = 32;
//Порт подключенный к DS 74HC595 mosi
int dataPin = 33;

void setup() {
  //устанавливаем режим порта выхода
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void STI ()
{  //устанавливаем LOW на latchPin пока не окончена передача байта
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, LSBFIRST, highByte(led)); //ввыводим старший байт
    shiftOut(dataPin, clockPin, LSBFIRST, lowByte(led));   // выводи младший байт
    //устанавливаем HIGH на latchPin, чтобы проинформировать регистр, что передача окончена.
    digitalWrite(latchPin, HIGH);
    
}
  
  
void loop() {
  val = analogRead(potPin);    // считываем значение с потенциометра
  val = val/4;  // конвертируем из 0-1023 к 0-255
  val=val/16;   //конвертируем из 0-255 в 0-16. В принципе можно 2 строчки в одну: val=val/64.
 switch (val) {
    case 0: led=1; break;
    case 1: led=3; break;
    case 2: led=7; break;
    case 3: led=15; break;
    case 4: led=31; break;
    case 5: led=63; break;
    case 6: led=127; break;
    case 7: led=255; break;
    case 8: led=511; break;
    case 9: led=1023; break;
    case 10: led=2047; break;
    case 11: led=4095; break;
    case 12: led=8191; break;
    case 13: led=16383; break;
    case 14: led=32767; break;
    case 15: led=65535; break;
     }
  STI();
}

 

step962
Offline
Зарегистрирован: 23.05.2011

 Попробуйте вот такой вариант:

int pos=val/16;
led=(1<<pos)-1;
STI();
  

 

wixa
Offline
Зарегистрирован: 05.02.2012

Работает, спасибо :) 

wixa
Offline
Зарегистрирован: 05.02.2012

step962 пишет:

 Попробуйте вот такой вариант:

int pos=val/16;
led=(1<<pos)-1;
STI();
  

 

Работает:)  Именно то что я хотел, коротко, и работает так как надо, единственное что вместо того чтоб вводить еще pos я val изначально поделил на 64 и в 2 строке в скобках 1 поменял на 2, так как при "1" на минимуме ничего не горело т.е. led = 0b 00000000 00000000, a последнее значение было led = 0b 01111111 11111111, т.е. последний светодиод не горел, при значении 2 всё исправилось. 

 

Обьясните как работает??? Плз!!! Дело в том что это сейчас я снимаю показания с потенциометра, а потом мне будут приходить значения оборотов двигателя от 0 до 70000, т.е. диапазон уже другой....

 

 

wixa
Offline
Зарегистрирован: 05.02.2012

Diemon пишет:

 Вот, сваял тестовый код для проверки моей идеи. Вроде правильно работает.  Заливаете в ардуину и открываете сериал монитор. В него вбиваете число, которое получается от деления на 4 (val = val/4;) тоесть от 0 до 255. а в сериал монитор выходит значение val_new которое от 0 до 16 и led которое от 1 до 65535. На красивость кода внимания не обращай, делался по быстрому. Функция serReadInt добавлена чтоб с сериала нормально читалось и для тебя не нужна

int led = 0;       // переменная для вывода на led
int val_new = 0;       // переменная для хранения значения входного напряжения
int potPin = 2;    // потенциометр подключается к 2-му порту
//Порт подключенный к ST_CP 74HC595 SS
int latchPin = 22;
//Порт подключенный к SH_CP  74HC595 SCK
int clockPin = 32;
//Порт подключенный к DS 74HC595 mosi
int dataPin = 33;
int resultInt;

void setup() {
        Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
}

void STI ()
{  //устанавливаем LOW на latchPin пока не окончена передача байта
    Serial.print("val_new: ");
    Serial.println(val_new);
    Serial.print("led: ");
    Serial.println(led);
}

void loop() {

        // send data only when you receive data:
        if (Serial.available() > 0) {
               resultInt = serReadInt();
               val_new=resultInt/16;
               switch (val_new) {
                 case 0: led=1; break;
                 case 1: led=3; break;
                 case 2: led=7; break;
                 case 3: led=15; break;
                 case 4: led=31; break;
                 case 5: led=63; break;
                 case 6: led=127; break;
                 case 7: led=255; break;
                 case 8: led=511; break;
                 case 9: led=1023; break;
                 case 10: led=2047; break;
                 case 11: led=4095; break;
                 case 12: led=8191; break;
                 case 13: led=16383; break;
                 case 14: led=32767; break;
                 case 15: led=65535; break;
               }
               STI();
        }
        delay(1000);
}

int serReadInt()
{
  int i, serAva;
  char inputBytes [7]; // массив для хранения байтов
  char * inputBytesPtr = &inputBytes[0]; // указатель на первый элемент массива

  if (Serial.available()>0) // проверяем есть ли данные
 {
    delay(5);//чутка ждем, чтобы все данные прошли
    serAva = Serial.available(); // получаем количество доступных байт
    for (i=0; i<serAva; i++) // загружаем байты в массив
      inputBytes[i] = Serial.read();

    inputBytes[i] = '\0'; // дописываем NULL в конец массива
    return atoi(inputBytesPtr); // Вызываем atoi функцию, стандартная функция С
  }
  else
    return -1; // Возвращаем -1, если ничего не получили
}

 А вот и ваша функция уже исправленная. Я думаю должно быть так:

#include <SPI.h>

int led = 0;       // переменная для вывода на led
int val = 0;       // переменная для хранения значения входного напряжения
int potPin = 2;    // потенциометр подключается к 2-му порту
//Порт подключенный к ST_CP 74HC595 SS
int latchPin = 22;
//Порт подключенный к SH_CP  74HC595 SCK
int clockPin = 32;
//Порт подключенный к DS 74HC595 mosi
int dataPin = 33;

void setup() {
  //устанавливаем режим порта выхода
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void STI ()
{  //устанавливаем LOW на latchPin пока не окончена передача байта
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, LSBFIRST, highByte(led)); //ввыводим старший байт
    shiftOut(dataPin, clockPin, LSBFIRST, lowByte(led));   // выводи младший байт
    //устанавливаем HIGH на latchPin, чтобы проинформировать регистр, что передача окончена.
    digitalWrite(latchPin, HIGH);
    
}
  
  
void loop() {
  val = analogRead(potPin);    // считываем значение с потенциометра
  val = val/4;  // конвертируем из 0-1023 к 0-255
  val=val/16;   //конвертируем из 0-255 в 0-16. В принципе можно 2 строчки в одну: val=val/64.
 switch (val) {
    case 0: led=1; break;
    case 1: led=3; break;
    case 2: led=7; break;
    case 3: led=15; break;
    case 4: led=31; break;
    case 5: led=63; break;
    case 6: led=127; break;
    case 7: led=255; break;
    case 8: led=511; break;
    case 9: led=1023; break;
    case 10: led=2047; break;
    case 11: led=4095; break;
    case 12: led=8191; break;
    case 13: led=16383; break;
    case 14: led=32767; break;
    case 15: led=65535; break;
     }
  STI();
}

 

 

Спасибо большое, работает. 

 

Хороший форум, и люди хорошие, отзывчивые. Спасибо

Diemon
Offline
Зарегистрирован: 18.11.2011

 Операция << это побитовый сдвиг.(На английском arduino.cc/en/Reference/Bitshift). 

Спасибо step962, я не додумался применить сюда. Замените тогда конструкцию switch case на эту одну строчку led=(2<<pos)-1;

Вкратце: когда pos=0 то: led= (побитовый сдвиг числа 2 на 0 бит)-1. Это: 10 сдвинуть на 0 бит влево, получается 10, тоесть 2 в десятичной системе. Отнимаем единицу и получаем 1.

когда pos=1 то: led= (побитовый сдвиг числа 2 на 1 бит)-1. Это: 10 сдвинуть на 0 бит влево, получается 100, тоесть 4 в десятичной системе. Отнимаем единицу и получаем 3.

И так далее......

step962
Offline
Зарегистрирован: 23.05.2011

wixa пишет:

Работает:)  Именно то что я хотел, коротко, и работает так как надо, единственное что вместо того чтоб вводить еще pos я val изначально поделил на 64 и в 2 строке в скобках 1 поменял на 2, так как при "1" на минимуме ничего не горело т.е. led = 0b 00000000 00000000, a последнее значение было led = 0b 01111111 11111111, т.е. последний светодиод не горел, при значении 2 всё исправилось. 

Ну, тогда уж не

led=(2<<pos)-1;

а

led=(1<<(pos+1))-1;

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

Кстати, если в моем исходном варианте pos вычислять как-то так:

pos=(int)(val/16.0); или

pos=(val+8)/16; (что, в общем, то же самое - сдвиг "шкалы" на половину ее шага)

то можно получить не 16, а 17 состояний индикации - к нынешним [горит 1 ... горит 16] добавится еще состояние "не горит ни один диод" (при значениях val в диапазоне [0...7]).

Цитата:

 Обьясните как работает??? Плз!!! Дело в том что это сейчас я снимаю показания с потенциометра, а потом мне будут приходить значения оборотов двигателя от 0 до 70000, т.едиапазон уже другой....

А что там объяснять-то? Первая и третья строчки - как и в предыдущих вариантах. Вторая остается.

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

Если вы приглядитесь к тому варианту кодов, который выложили на высокий суд форумчан, то увидите, что среди бесчисленных ифов занимаетесь присваиванием переменной led чисел, являющихся последовательными степенями двойки минус 1. От бесчисленных ифов (сравнения на попадание в определенный диапазон) к вычислению номера диапазона вы перешли еще до моей реплики. Осталось значение для led не из списка брать (case {...}), а тоже вычислять.

Одна из арифметических операций, поддерживаемых языком Си (а вслед за ним и  Wiring в Arduino IDE) - побитовый сдвиг влево (<<). В справочнике эта операция не упоминается, но тем не менее в языке она присутствует («Ты суслика видишь? — Нет. — И я нет. А он есть!»).  В двоичной системе это по существу умножение на 2 (как и в десятичной системе счисления сдвиг на одну позицию влево означает умножение числа на 10).

 1<<1 = 00000000 00000010
 1<<2 = 00000000 00000100
 1<<3 = 00000000 00001000
 1<<4 = 00000000 00010000
...
1<<15 = 10000000 00000000

 

Теоретические основы разъяснены, закономерность выявлена. Остается воплотить ее в коде:

led=(1<<pos)-1;

1<<pos - вычисляем нужную нам степень двойки (фактически это 2^pos). Каждое из вычисленных таким способом чисел представляет собой единицу со следующими за ней pos-1 нулями. Что получится при вычитании из такого числа одной-единственной единички, вы, наверное, уже догадались.

Фсе.

Ах, да! Как вы, наверное, догадались, есть еще одна операция прямо противоположного назначения - >>.

ЗЫ: прочитал следующие посты - да вам уже, в общем-то, разъяснили политический момент!!! Ну ладно - "Repetitio est mater studiorum".

 

wixa
Offline
Зарегистрирован: 05.02.2012

Уважаемый step962, огромное спасибо за детальное розжовывание :). Именно этого я и хотел, не просто код, а каод который я понимаю и в котором разобрался :). Особено понравилось часть касающаяся математики.... матери всех технических наук :) 

step962
Offline
Зарегистрирован: 23.05.2011

Шафер (мрачно и вскакивая)
Мать! Кто сказал «мать»? Прошу не выражаться при новобрачных.
Шафера оттаскивают.

(с) В.В. (но не Путин)

wixa
Offline
Зарегистрирован: 05.02.2012

Вопрос не втему, но может подскажет, для atmega 328 можно использовать любой кварц на 16 Mhz ? или какого то определённого типа? Допустим можно ли использовать вот этот : http://datasheet.elcodis.com/pdf/5/1/50138/nx3225sa-16.000000mhz-t1.pdf ?

LEVV2006
LEVV2006 аватар
Offline
Зарегистрирован: 15.04.2011

wixa пишет:

Вопрос не втему, но может подскажет, для atmega 328 можно использовать любой кварц на 16 Mhz ? или какого то определённого типа? Допустим можно ли использовать вот этот : http://datasheet.elcodis.com/pdf/5/1/50138/nx3225sa-16.000000mhz-t1.pdf ?

 

http://arduino.ru/forum/obshchii/aruino-pelletnaya-gorelka#comment-5058

wixa
Offline
Зарегистрирован: 05.02.2012

т.е. Это даже хорошо ??? :) А 10 pF хватит ? Просто это у друга в конторе, подешевле, + единственый удовлетворяющий по размерам. 

LEVV2006
LEVV2006 аватар
Offline
Зарегистрирован: 15.04.2011

Я про ваш резонатор ничего не писал. Я просто сказал что вместо кварцевого можно поставить керамический. В керамическом 30пф.
Возможно 10 пФ будет мало.
 

wixa
Offline
Зарегистрирован: 05.02.2012

Извините за глупые вопросы, но наверно можно поставить дополнительный конденсатор?  

LEVV2006
LEVV2006 аватар
Offline
Зарегистрирован: 15.04.2011

Хз. Я не очень хорошо всё знаю. Просто я намучился с кварцевым резонатором и кондерами. И с радостью перешел на керамику.
 

wixa
Offline
Зарегистрирован: 05.02.2012

Спасибо за ответы :) Буду пробовать