Вывод информации на 4 разрядный 7сегментный индикатор при помощи двух 74hc595

CheBuraw
Offline
Зарегистрирован: 10.02.2015

Здравствуйте!
Для одного из проектов мне потребовались часы на 7 сегментном 4-х разрядном индикаторе и возможность подключить еще ряд внешних исполнительных устройств.
Вариант с LCD мне не подходит. То что находил в интернете не подходит, т.к. либо индикатором заняты все "ноги" Arduino, либо, при использовании 74hc595, регистр ставили на каждый разряд, что я считаю перерасходом микросхем.
В итоге решил делать на двух регистрах - один для генерации символов, другой для переключения разрядов.
После долгих часов оптимизации чужих скетчей у меня получился представленный ниже. В принципе код рабочий, но напрягает то что "засвечиваются" соседние разряды. В принципе жить с этим можно, но охота чтобы все разряды показывали четко те символы какие мне необходимы.
В данный момент вместо часов я подключил переменный резистор, для наглядности, а так же может кому и такое решение подойдет.

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

Схема подключения:


Скетч:

/*
********************************************************************
  Name    : Arduino, 4-разрядный 7-сегментный индикатор и 74HC595 сдвиговый регистр
  Author  : CheBuraw
  Date    : 10 Feb, 2015
  Modified:
  Version : 0.9

Это вариативная смесь уроков:
 1. Tutorial: 74HC595 Shift Bit Register using a 7 Segment Display, ...
http://www.youtube.com/watch?v=shcumKPTcTk
shiftOutDisplay
https://gist.github.com/benjjo/6559436
shiftOutAnalogueDisplay
https://gist.github.com/benjjo/6559456
ShiftOut Reference:
http://arduino.ru/Reference/ShiftOut
ShiftOut Tutorial:
http://arduino.ru/Reference/ShiftOut
http://arduino.ru/Tutorial/registr_74HC595

 2. Digital Clock with 7-segments LED and RTC (Realtime Clock) 
 http://arduino-for-beginners.blogspot.ru/2010/12/digital-clock-with-7-se...
********************************************************************

       4-разрядный 7-сегментный индикатор
                1  A  F 2  3  B
 _______________|__|__|__|__|__|_____________
|         |         |    |         |         |
|    A    |    A    |    |    A    |    A    |
| F     B | F     B | dp | F     B | F     B |
|    G    |    G    |    |    G    |    G    |
| E     C | E     C | dp | E     C | E     C |
|    D    |    D    |    |    D    |    D    |
|_________|_________|____|_________|_________|
                |  |  |  |  |  |
                E  D dp  C  G  4

Соответствие отображаемого знака данным порта
общий анод
__________________________________________________
     |   двоичный вид по сегментам   |            |
Цифра|dp | G | F | E | D | C | B | A | Десятичный |
-----|---|---|---|---|---|---|---|---|------------|
  0  | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |    192     |
  1  | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |    249     |
  2  | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |    164     |
  3  | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |    176     |
  4  | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 |    153     |
  5  | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 |    146     |
  6  | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |    130     |
  7  | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |    248     |
  8  | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |    128     |
  9  | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |    144     |
 dp  | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |    127     |
-----*---*---*---*---*---*---*---*---*------------*

74HC595 Map:
     _______
Q1  |1 *  16|  Vcc                  PINS 1-7, 15   Q0 - Q7   Output Pins
Q2  |2    15|  Q0                   PIN 8	   GND	     Ground, Vss
Q3  |3    14|  DS                   PIN 9	   Q7"	     Serial Out
Q4  |4    13|  OE                   PIN 10	   MR	     Master Reclear, active low
Q5  |5    12|  ST_CP                PIN 11	   SH_CP     Shift register clock pin
Q6  |6    11|  SH_CP                PIN 12	   ST_CP     Storage register clock pin (latch pin)
Q7  |7    10|  MR                   PIN 13	   OE	     Output enable, active low
GND |8_____9|  Q7"                  PIN 14	   DS	     Serial data input
                                    PIN 16	   Vcc	     Positive supply voltage
           _______
   LED B -|1 *  16|-5V
   LED C -|2    15|-LED A
   LED D -|3    14|-PIN 11
   LED E -|4    13|-GND
   LED F -|5    12|-PIN 8
   LED G -|6    11|-PIN 12 ; 1uF TO GND
   LED dp-|7    10|-5V
      GND-|8_____9|-NILL
      
*/

int latchPin = 8;     //Пин "защелки" первого регистра подключен к ST_CP входу первого регистра отвечающего за сегменты 
int latchPin2 = 9;    //Пин "защелки" второго регистра подключен к ST_CP входу первого регистра отвечающего за разряды
int clockPin = 12;    //Пин подключен к SH_CP входу 74HC595
int dataPin = 11;     //Пин подключен к DS входу 74HC595

int TimeLight = 5;  //время для разогрева сегментов
int sensorPin=A0;

byte SegDisplay; // переменная для вывода символов на индикаторе
byte RazrDisplay; // переменная для включения разрядов

// Настройка комбинации для отображения каждого номера на дисплее.
// Комбинация выбрана на основе таблицы отображаемого знака данным порта
// Соответственно { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , dp, - }
byte g_digits[12]={192,249,164,176,153,146,130,248,128,144,127,255}; //массив цифр, генерирующий из сегментов цифры
byte g_registerArray[4]={1,2,4,8}; //массив цифр, указывающий разряды


void setup() {
// обозначаем все пины как выходы
  pinMode(latchPin, OUTPUT); 
   pinMode(latchPin2, OUTPUT); 
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  int disp = 0; //создаем переменную для вывода на экран

  int analogueValue = analogRead(sensorPin);  // читаем аналоговый пин A0
  analogueValue = map(analogueValue, 0, 1023, 0, 8888); //преобразуем диапазон с А0 (0-1023) в нужный нам (0-8888)

  disp = analogueValue; // записываем получившуюся после преобразования цифру в переменну для вывода на экран
  
 // Разбиваем цифру по разрядам индикатора
  if (disp < 10) // если наша цифра меньше 10, то
  {
    Razryad(3); Segment(11); // пишем в первый разряд прочерк
    Razryad(0); Segment(11); // пишем во второй разряд прочерк
    Razryad(1); Segment(11); // пишем в третий разряд прочерк
    Razryad(2); Segment(disp); // пишем в четвертый разряд нашу цифру
  }
  else if (disp < 100) // если наша цифра меньше 100, то
  {
    Razryad(3); Segment(11); // пишем в первый разряд прочерк
    Razryad(0); Segment(11); // пишем во второй разряд прочерк
    Razryad(1); Segment(disp / 10); // пишем в третий разряд - цифру делёную на 10 
    Razryad(2); Segment(disp % 10); // пишем в четвертый разряд цифру оставшуюся от деления на 10
/*
 Допустим наша цифра 25. 
  Если мы ее поделим на 10, то у нас получится 2,5. Цифры после запятой, в данном случае,
 у нас не остаются. Таким образом мы имеем в третем разряде цифру 2.
  В чевертый разряд мы, как раз и записываем цифру-остаток полученную в результате деления. 
В нашем случае это и есть та самая 5.
Аналогичным образом разбивается наша цифра и далее.
*/
  }
  else if (disp < 1000)
  {
    Razryad(3); Segment(11);
    Razryad(0); Segment(disp / 100);
    Razryad(1); Segment((disp % 100) / 10);
    Razryad(2); Segment(disp % 10);
  }
  else
  {
    Razryad(3); Segment(disp / 1000);
    Razryad(0); Segment((disp % 1000) / 100);
    Razryad(1); Segment((disp % 100) / 10);
    Razryad(2); Segment(disp % 10); 
  }
 }


void Segment(int x) //функция для записи в 1 регистр информации для генерации символов
{
    digitalWrite(latchPin, LOW); // устанавливаем синхронизацию "защелки" на LOW
SegDisplay=g_digits[x]; // получаем цифру и выводим символ, из массива цифр, соответствующий этой цифре.
      shiftOut(dataPin, clockPin, MSBFIRST, SegDisplay);  // Записываем информацию в регистр
  digitalWrite(latchPin, HIGH); //"защелкиваем" регистр, тем самым устанавливая значения на выходах
}

void Razryad(int r) //функция для записи во 2 регистр номера разряда индикатора
{
    digitalWrite(latchPin2, LOW); // устанавливаем синхронизацию "защелки" на LOW
RazrDisplay=g_registerArray[r];  // получаем цифру и выводим номер регистра, из массива цифр, соответствующий этой цифре.
      shiftOut(dataPin, clockPin, MSBFIRST, RazrDisplay); // Записываем информацию в регистр
  digitalWrite(latchPin2, HIGH); //"защелкиваем" регистр, тем самым устанавливая значения на выходах
 delay(TimeLight); // пауза, чтобы сегменты "разгорелись"
}
Datak
Offline
Зарегистрирован: 09.10.2014

CheBuraw пишет:

напрягает то что "засвечиваются" соседние разряды

Ну, а почему засвечиваются, выяснили?

Предлагаю, например, сэкономить ардуиноноги, и оставить только один выход Latch, на обе 595-е.
Заодно и засвечивание пропадёт, мне кажется.

CheBuraw
Offline
Зарегистрирован: 10.02.2015

Datak пишет:

CheBuraw пишет:

напрягает то что "засвечиваются" соседние разряды

Ну, а почему засвечиваются, выяснили?

Предлагаю, например, сэкономить ардуиноноги, и оставить только один выход Latch, на обе 595-е.
Заодно и засвечивание пропадёт, мне кажется.

Я так думаю, что причина в функции отображения. Как-то не очень мне нравиться как она работает. Видел проекты в которых всё четко отображается, но вот в коде их я так разобраться и не смог :(. Поэтому, собственно и сюда решил написать, может кто подскажет правильный вариант.

Паралельное расположение сдвиговых регистров я оставил умышленно, т.к. мне показалось, что так код проще получается. Возможно заблуждаюсь. Идея в том чтобы в перспективе, при необходимости, можно было расширить еще на 4 разряда. Такая реализация мне проще показалась, да и так всего 4 ноги у Arduino занял.

Не знаю как сюда видео работы устройства выложить, поэтому залил на яндекс

https://yadi.sk/i/DYnnWiuTeb4jD

Datak
Offline
Зарегистрирован: 09.10.2014

Да я же подсказал, осталось попробовать. :)

Проблема в том, что цифра на индикаторе меняется в один момент времени - в функции  Segment(int x),
а номер разряда в другой момент - в функции  Razryad(int r).

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

Можно попробовать изменить программу, чтобы строчки  digitalWrite(latchPin, HIGH);  и  digitalWrite(latchPin2, HIGH);  выполнялись подряд, и пауза между ними была как можно меньше.

Но в идеале, всё-таки советую оставить один  latchPin,  для достижения полной синхронности.
А увеличению разрядности это никак не помешает.

Datak
Offline
Зарегистрирован: 09.10.2014

CheBuraw пишет:

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

Я, кстати, параллельное расположение менять не предлагаю. Оно ничем не мешает - пусть остаётся, если удобно.

Нужно только объединить у 595-ых сдвиговые входы, и подключить к одному выходу ардуины.

-------

Upd: Не сдвиговые конечно, а входы защёлкивания. Ошибся, извините - ночью советовал. :)

2009Shef
Offline
Зарегистрирован: 23.10.2014

Стало интересно сколько и какие внешние устройства предполагается подключить помимо семисегментника.

Может стоит именно эти устройства подключить через сдвиговые регистры или взят I2C расширитель портов например PCF8574 и не мучаться со сдвиговыми регистрами.

А вообще вот мой код для работы с семисегментным индикатором напрямую без сдвиговых регистров но его не сложно модифицировать под регистры.

#include <avr/io.h>
#include <avr/interrupt.h>
/*
!!! Все примеры и описания функций для 4-х разрядного индикатора !!!
Дисплей
Количество разрядов индикатора (NUM_DIGITS)
вычисляется из размера массива COM_PIN но можно задать и в явном виде
*/
#define NUM_DIGITS  sizeof(COM_PIN) 
#define commonAnode 1 //Тип дисплея 0=Общий катод, 1=Общий анод
const byte LED_PIN[8]  = {0,1,2,3,4,5,6,7}; //Массив пинов сегментов (A - G, dp)
const byte COM_PIN[] = {8,9,10,11}; //Массив пинов разрядов
unsigned char buffer[NUM_DIGITS]; //массив символов для вывода (буфер)
volatile unsigned char currentDigit = 0;   //Номер разряда для отображения 
// массив символов для вывода на дисплей
prog_uchar charMap[21] PROGMEM = {
  // биты     |  6 5 4 3 2 1 0  |
  // сегменты |  a b c d e f g  |
  //  Биты        raw
  0b1111110,//0 0x00
  0b0110000,//1 0x01
  0b1101101,//2 0x02
  0b1111001,//3 0x03
  0b0110011,//4 0x04
  0b1011011,//5 0x05
  0b1011111,//6 0x06
  0b1110000,//7 0x07
  0b1111111,//8 0x08
  0b1111011,//9 0x09
  0b1110111,//a 0x0A
  0b0011111,//b 0x0B
  0b1001110,//c 0x0C
  0b0111101,//d 0x0D
  0b1101111,//e 0x0E
  0b1000111,//f 0x0F
  0b1100111,//P 0x10
  0b0001111,//t 0x11
  0b0011100,//u 0x12
  0b0000000,//  0x13 пустой символ (пробел)
  0b0000001 //- 0x14 знак минус
};

void setup() {
  //Serial.begin(9600);
  Display_setup();
  //так можно выводить время
  DisplayTime(12,30,1);
}

void loop() {
    ClearDisplay();
    DisplayNumeric(567, 10, 1, 3);
    delay(1000);
    DisplayNumeric(9876, 10, 0, 4);
    delay(1000);
}
//Функции для работы с диспле
void Display_setup(){
  // Настройка всех пинов сегментов
  for(int i=0;i<8;++i) {
    //int temp = pgm_read_word_near(LED_PIN + i);
    pinMode(LED_PIN[i], OUTPUT);
    digitalWrite(LED_PIN[i], !commonAnode);
  }
  // Настройка всех пинов разрядов
  for(int i=0;i<NUM_DIGITS;++i) {
    //int t = pgm_read_word_near(COM_PIN + i);
    pinMode(COM_PIN[i], OUTPUT);
    digitalWrite(COM_PIN[i], commonAnode);
  }
  // Настройка таймера Т2 на прерывание по переполнению
  noInterrupts();           // Запрещаем прерывания
  //Частота переключения разрядов при 16 MHz ~488 Hz (при 8 MHz ~244 Hz)
  //разделив на количество разрядов получим общую частоту обновления
  TCCR2B = (1<<CS20)|(1<<CS22); //Предделитель 128
  TIMSK2 |= (1<<TOIE2);   // Разрешение прерывания по переполнению
  interrupts();    // Разрешаем все прерывания
}

void DisplayTime(int mm, int ss, boolean dpOn){
  unsigned int num = mm*100+ss;
  for (int i = 3; i>=0; --i){
    int digit = num % 10;
    buffer[i] = pgm_read_byte_near(charMap+digit);
    if(dpOn && i==1) {
      buffer[1] |= 0x80;//BIN 1000 0000
    }
    if(commonAnode) {
      buffer[i] = ~buffer[i];
    }
    num = num / 10;
  }
}
//просто очищаем дисплей записывая в буфер 0
void ClearDisplay(){
  for(int i=NUM_DIGITS-1; i>=0; --i){
    buffer[i] = 0;
    if(commonAnode) {
      buffer[i] = ~buffer[i];
    }
  }
}

/*просто вывод чисел
  входные данные DisplayNumeric(value, base, start, places)
  value -  число можно ввести в базисе BIN B1111 = DEC 15 = HEX 0x0F
  base -   базис выводимого числа если (BIN -2, DEC - 10, HEX - 16)
  Пример 1: value=0x0F,  base=10 на дисплее будет 15
  Пример 2: value=15, а base=16 на дисплее будет 0F
  Пример 2: value=9876, а base=10 на дисплее будет 9876
  start -  первый разряд с которого будет выведенно число нумерация с 0
  places - количество разрядов для числа 
  Пример 1: start=1  places=4 можно вывести 4 значное число 
  Пример 2: start=2  places=4 можно вывести 3 значное число вывод со 2 разряда*/
void DisplayNumeric(unsigned int value, unsigned int base, byte start, byte places){
  for(int i =places+start-1;i>=start;--i) {
    int digit = value % base;
    buffer[i] = pgm_read_byte_near(charMap+digit);
    value = value / base;
    if(commonAnode) {
      buffer[i] = ~buffer[i];
    }
  }
}
/*
Функция задания символов в массиве buffer. Синтаксис:
 setdisplayRaw(position, newValue, dpOn);
 position: номер разряда нумерация с 0
 newValue: номер символа в массиве charMap
 dpOn: горит ли точка 0=не горит 1=горит
 */
void setdisplayRaw(byte position, byte newValue, boolean dpOn) {
  if(position < NUM_DIGITS) {
    buffer[position] = pgm_read_byte_near(charMap+newValue);
    if(dpOn) {
      buffer[position] |= 0x80;//BIN 1000 0000
    }
    if(commonAnode) {
      buffer[position] = ~buffer[position];
    }
  }
}
// Обработчик прерывания по переполнению Т2
ISR(TIMER2_OVF_vect) {
  //Выключаем предыдущий разряд
  if(currentDigit >= (NUM_DIGITS)) {
    digitalWrite(COM_PIN[currentDigit-1], !commonAnode);
    currentDigit = 0;
  }
  else {
    digitalWrite(COM_PIN[currentDigit-1], !commonAnode);
  }
  //Зажигаем сегменты соответственно массиву buffer
  for(int i=0;i<7;i++) {
    digitalWrite(LED_PIN[i], buffer[currentDigit] & (1<<(6-i)));
  }
  //Зажигаем точку
  digitalWrite(LED_PIN[7], buffer[currentDigit] & 0x80);
  //Включаем текущий разряд
  digitalWrite(COM_PIN[currentDigit], commonAnode);
  ++currentDigit;
}

 

axill
Offline
Зарегистрирован: 05.09.2011

2009Shef пишет:

Стало интересно сколько и какие внешние устройства предполагается подключить помимо семисегментника.

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

в данном варианте вместо PCF8574 целесообразней поставить драйвер семисегментников типа max7219

2009Shef
Offline
Зарегистрирован: 23.10.2014

Для семисегиентников есть прекрасный драйвер stled316. 

CheBuraw
Offline
Зарегистрирован: 10.02.2015

axill пишет:

в данном варианте вместо PCF8574 целесообразней поставить драйвер семисегментников типа max7219

   О применении max7219 я думал, но не стал использовать по ряду причин:
1. Работают только с индикаторами с общим катодом, а у меня общий анод на всех имеющихся индикаторах :(
2. У нас в городе max7219 не купить, а заказывать с китая только партиями не менее 10 штук. Ради 3-4 проектов заказывать 10 микросхем не охота.
3. Для своих нужд посчитал кощунством использовать max7219. Если уж их грузить, то по полной :)
4. max7219 в 3 раза дороже 74hc595. 300 рублей (за 10 штук), конечно, не великие деньги, но тем не менее.
5. 74hc595 считаю более распространенным.
...хотя, касательно цены! В данной схеме используются две 74hc595, а значит разница уже не в 3 раза, а с учетом удобства работы с библиотекой для max7219, может оно и действительно вариант правильнее и проще ;). Но не для меня. Если бы не причина №1, то думаю сделалбы на max7219.

   Драйвер stled316 и PCF8574 не стал использовать по тем же причинам - недоступность у нас в городе (думаю не только у меня) да и цена куда выше 74hc595.
   Еще, просто хотелось понять как работать с 74hc595 :)

2009Shef пишет:

Стало интересно сколько и какие внешние устройства предполагается подключить помимо семисегментника.

   На данный момент хочу повесить релюшки. Согласен, ими куда проще управлять через 74hc595, но я думал сделать некую универсальную схему, чтобы любой другой мог взять данный код и подключить другие устройства, необходимые именно ему.

Datak пишет:

Но в идеале, всё-таки советую оставить один  latchPin,  для достижения полной синхронности.
А увеличению разрядности это никак не помешает.

   Datak, большое Вам спасибо! Подключил как Вы советовали. Немного модифицировал код и УРА - все работает четко, без засветов :).
Я до этого пренебрегал данным вариантом в силу незнания как разделить данные отправляемые в разные регистры. Думал что это как-то сложно. Но после Вашей рекомендации, решил таки более детально изучить данный вариант. Как оказалось что это не сложнее моего варианта ранее. Просто данные необходима передавать, так сказать, наоборот - с начала для последнего регистра, затем для предпоследнего и т.д.

Скорректированная схема:

Итоговый код:

********************************************************************
  Name    : Arduino, 4-разрядный 7-сегментный индикатор, 74HC595 сдвиговый регистр, переменный резистор
  Author  : CheBuraw
  Date    : 11 Feb, 2015
  Version : 0.9
********************************************************************
int latchPin = 8;     //Пин "защелки" первого регистра подключен к ST_CP входу первого регистра отвечающего за сегменты 
int clockPin = 12;    //Пин подключен к SH_CP входу 74HC595
int dataPin = 11;     //Пин подключен к DS входу 74HC595

int TimeLight = 5;  //время для разогрева сегментов
int sensorPin=A0;

byte SegDisplay; // переменная для вывода символов на индикаторе
byte RazrDisplay; // переменная для включения разрядов

// Настройка комбинации для отображения каждого номера на дисплее.
// Комбинация выбрана на основе таблицы отображаемого знака данным порта
// Соответственно { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , dp, - }
byte g_digits[12]={192,249,164,176,153,146,130,248,128,144,127,255}; //массив цифр, генерирующий из сегментов цифры
byte g_registerArray[4]={1,2,4,8}; //массив цифр, указывающий разряды


void setup() {
// обозначаем все пины как выходы
  pinMode(latchPin, OUTPUT); 
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  int disp = 0; //создаем переменную для вывода на экран

  int analogueValue = analogRead(sensorPin);  // читаем аналоговый пин A0
  analogueValue = map(analogueValue, 0, 1023, 0, 8888); //преобразуем диапазон с А0 (0-1023) в нужный нам (0-8888)

  disp = analogueValue; // записываем получившуюся после преобразования цифру в переменну для вывода на экран
  
 // Разбиваем цифру по разрядам индикатора
  if (disp < 10) // если наша цифра меньше 10, то
  {
    Indicate(0, 11); // пишем в первый разряд пусто
    Indicate(1, 11); // пишем во второй разряд пусто
    Indicate(2, 11); // пишем в третий разряд пусто
    Indicate(3, disp); // пишем в четвертый разряд нашу цифру
  }
  else if (disp < 100) // если наша цифра меньше 100, то
  {
    Indicate(0, 11); // пишем в первый разряд пусто
    Indicate(1, 11); // пишем во второй разряд пусто
    Indicate(2, disp / 10); // пишем в третий разряд - цифру делёную на 10
    Indicate(3, disp % 10); // пишем в четвертый разряд цифру оставшуюся от деления на 10
/*
 Допустим наша цифра 25. 
  Если мы ее поделим на 10, то у нас получится 2,5. Цифры после запятой, в данном случае,
 у нас не остаются. Таким образом мы имеем в третем разряде цифру 2.
  В чевертый разряд мы, как раз и записываем цифру-остаток полученную в результате деления. 
В нашем случае это и есть та самая 5.
Аналогичным образом разбивается наша цифра и далее.
*/
  }
  else if (disp < 1000)
  {
    Indicate(0, 11);
    Indicate(1, disp / 100);    
    Indicate(2, (disp % 100) / 10);
    Indicate(3, disp % 10); 
  }
  else
  {
    Indicate(0, disp / 1000);
    Indicate(1, (disp % 1000) / 100);    
    Indicate(2, (disp % 100) / 10);
    Indicate(3, disp % 10);
  }
 }

void Indicate(int r,int x)
{
SegDisplay=g_digits[x]; // получаем цифру и выводим символ, из массива цифр, соответствующий этой цифре.
RazrDisplay=g_registerArray[r];  // получаем цифру и выводим номер регистра, из массива цифр, соответствующий этой цифре.
  digitalWrite(latchPin, LOW);  // устанавливаем синхронизацию "защелки" на LOW
      shiftOut(dataPin, clockPin, MSBFIRST, RazrDisplay); // Записываем информацию для второго регистра (Номер разряда)
      shiftOut(dataPin, clockPin, MSBFIRST, SegDisplay);  // Записываем информацию для первого регистра (Номер символа)
  digitalWrite(latchPin, HIGH);  //"защелкиваем" регистр, тем самым устанавливая значения на выходах

   delay(TimeLight); // пауза, чтобы сегменты "разгорелись"
}

 А еще буду очень благодарен, если мне подскажут как информацию в спойлер прятать.

Datak
Offline
Зарегистрирован: 09.10.2014

CheBuraw, рад что всё получилось.
Схема размытая, не видно, то ли там, о чём я писал, но судя по программе - да, кажется всё так.
Только лишний latchPin2 там почистить бы, чтобы с толку не сбивал. :)

А строки с 41 по 77 можно попробовать заменить на такие:





for( int i = 0; i < 4; i++ )
{
   if( i == 0 || disp != 0 )
      Indicate( i, disp % 10 );
   else
      Indicate( i, 11 );

   disp /= 10;
}

Должно работать, если я нигде не ошибся.

CheBuraw
Offline
Зарегистрирован: 10.02.2015

Datak, код почистил, latchPin2 убрал :) .

По поводу предложенного Вами кода. В моем исходнике фигурируют "Indicate(1, (disp % 1000) / 100);" в Вашем коде я не понял как реализуются такие моменты. Может я просто плохо понял, я еще новичек в програмировании.

Я тут собрал на основе данного скетча Вариант использования.
Arduino, 4-разрядный 7-сегментный индикатор, 74HC595 сдвиговый регистр, DS1307 RTC

/*
********************************************************************
  Name    : Arduino, 4-разрядный 7-сегментный индикатор, 74HC595 сдвиговый регистр, DS1307 RTC
  Author  : CheBuraw
  Date    : 11 Feb, 2015
  Modified:
  Version : 1.0
********************************************************************

4-разрядный 7-сегментный индикатор. общий анод
                1  A  F 2  3  B
 _______________|__|__|__|__|__|_____________
|         |         |    |         |         |
|    A    |    A    |    |    A    |    A    |
| F     B | F     B | dp | F     B | F     B |
|    G    |    G    |    |    G    |    G    |
| E     C | E     C | dp | E     C | E     C |
|    D    |    D    |    |    D    |    D    |
|_________|_________|____|_________|_________|
                |  |  |  |  |  |
                E  D dp  C  G  4

74HC595 Map:
     _______
Q1  |1 *  16|  Vcc                  PINS 1-7, 15   Q0 - Q7   Output Pins
Q2  |2    15|  Q0                   PIN 8	   GND	     Ground, Vss
Q3  |3    14|  DS                   PIN 9	   Q7"	     Serial Out
Q4  |4    13|  OE                   PIN 10	   MR	     Master Reclear, active low
Q5  |5    12|  ST_CP                PIN 11	   SH_CP     Shift register clock pin
Q6  |6    11|  SH_CP                PIN 12	   ST_CP     Storage register clock pin (latch pin)
Q7  |7    10|  MR                   PIN 13	   OE	     Output enable, active low
GND |8_____9|  Q7"                  PIN 14	   DS	     Serial data input
                                    PIN 16	   Vcc	     Positive supply voltage
           _______
   LED B -|1 *  16|-5V
   LED C -|2    15|-LED A
   LED D -|3    14|-PIN 11
   LED E -|4    13|-GND
   LED F -|5    12|-PIN 8
   LED G -|6    11|-PIN 12 ; 1uF TO GND
   LED dp-|7    10|-5V
      GND-|8_____9|-NILL
      
*/
#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 RTC;

int latchPin = 8;     //Пин "защелки" первого регистра подключен к ST_CP входу первого регистра отвечающего за сегменты 
int clockPin = 12;    //Пин подключен к SH_CP входу 74HC595
int dataPin = 11;     //Пин подключен к DS входу 74HC595

int TimeLight = 5;  //время для разогрева сегментов

byte SegDisplay; // переменная для вывода символов на индикаторе
byte RazrDisplay; // переменная для включения разрядов

// Настройка комбинации для отображения каждого номера на дисплее.
byte g_digits[12]={
 B01000000, B01111001,    // 0 1
 B00100100, B00110000,    // 2 3
 B00011001, B00010010,    // 4 5
 B00000010, B01111000,    // 6 7
 B00000000, B00010000,    // 8 9
 B11111111, }; 		  // все сегменты выключены

byte g_registerArray[4]={1,2,4,8}; //массив цифр, указывающий разряды


void setup() {
  Wire.begin();
  RTC.begin();
  
// обозначаем все пины как выходы
  pinMode(latchPin, OUTPUT); 
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  int hour,minute,sec,disp = 0;

  DateTime now = RTC.now();

  hour = now.hour();
  minute = now.minute();
  sec = now.second();
  
  disp = (hour * 100) + minute;
 // Разбиваем цифру по разрядам индикатора
  if (disp < 10) // если наша цифра меньше 10, то
  {
    Indicate(0, 11); // пишем в первый разряд пусто
    Indicate(1, 11); // пишем во второй разряд пусто
    Indicate(2, 11); // пишем в третий разряд пусто
    Indicate(3, disp); // пишем в четвертый разряд нашу цифру
  }
  else if (disp < 100) // если наша цифра меньше 100, то
  {
    Indicate(0, 11); // пишем в первый разряд пусто
    Indicate(1, 11); // пишем во второй разряд пусто
    Indicate(2, disp / 10); // пишем в третий разряд - цифру делёную на 10
    Indicate(3, disp % 10); // пишем в четвертый разряд цифру оставшуюся от деления на 10
/*
 Допустим наша цифра 25. 
  Если мы ее поделим на 10, то у нас получится 2,5. Цифры после запятой, в данном случае,
 у нас не остаются. Таким образом мы имеем в третем разряде цифру 2.
  В чевертый разряд мы, как раз и записываем цифру-остаток полученную в результате деления. 
В нашем случае это и есть та самая 5.
Аналогичным образом разбивается наша цифра и далее.
*/
  }
  else if (disp < 1000)
  {
    Indicate(0, 11);
    Indicate(1, disp / 100);    
    Indicate(2, (disp % 100) / 10);
    Indicate(3, disp % 10); 
  }
  else
  {
    Indicate(0, disp / 1000);
    Indicate(1, (disp % 1000) / 100); 
    Indicate(2, (disp % 100) / 10);
    Indicate(3, disp % 10);
  }

 }

void Indicate(int r,int x)
{
SegDisplay=g_digits[x]; // получаем цифру и выводим символ, из массива цифр, соответствующий этой цифре.
RazrDisplay=g_registerArray[r];  // получаем цифру и выводим номер регистра, из массива цифр, соответствующий этой цифре.
  digitalWrite(latchPin, LOW);  // устанавливаем синхронизацию "защелки" на LOW
      shiftOut(dataPin, clockPin, MSBFIRST, RazrDisplay); // Записываем информацию для второго регистра (Номер разряда)
      shiftOut(dataPin, clockPin, MSBFIRST, SegDisplay);  // Записываем информацию для первого регистра (Номер символа)
  digitalWrite(latchPin, HIGH);  //"защелкиваем" регистр, тем самым устанавливая значения на выходах

   delay(TimeLight); // пауза, чтобы сегменты "разгорелись"
}

 

Datak
Offline
Зарегистрирован: 09.10.2014

CheBuraw пишет:

я не понял как реализуются такие моменты

Реализуются до обидного просто.

Сначала выводится младший разряд числа -  disp % 10,
а потом число делится на десять - disp /= 10 - и значит, младшим становится следующий разряд.
Индикатор 4-х разрядный, поэтому весь этот фокус повторяется в цикле 4 раза.

Дополнительная проверка, if в цикле, нужна для того чтобы не выводить нули перед старшим значащим разрядом. Вот тут я как раз немного сомневаюсь, но надеюсь что не ошибся. :)
 

CheBuraw
Offline
Зарегистрирован: 10.02.2015

Datak пишет:

Реализуются до обидного просто.

Сначала выводится младший разряд числа -  disp % 10,
а потом число делится на десять - disp /= 10 - и значит, младшим становится следующий разряд.
Индикатор 4-х разрядный, поэтому весь этот фокус повторяется в цикле 4 раза.

Дополнительная проверка, if в цикле, нужна для того чтобы не выводить нули перед старшим значащим разрядом. Вот тут я как раз немного сомневаюсь, но надеюсь что не ошибся. :)

На индикаторе все показывается :). Круть!
..но есть один нюанс - разряды отображаются зеркально. Например сейчас 21:27, а показывается 72:12.

В принципе это не смертельно и можно поправить тут:
byte g_registerArray[4]={8,4,2,1}; //массив цифр, указывающий разряды

Крутил, вертел Ваш код, менял значения в цикле, но ничего толкового из этого так и не вышло :(

...но может Вы поправите это в Вашем варианте? (извините за наглость)

Datak пишет:

Индикатор 4-х разрядный, поэтому весь этот фокус повторяется в цикле 4 раза.

Правильно ли я понял, что если я задействую 8 разрядов, то мне необходимо будет заменить 4 на 8 в строке
 

for( int i = 0; i < 4; i++ )

?

Datak
Offline
Зарегистрирован: 09.10.2014

CheBuraw пишет:

..но есть один нюанс - разряды отображаются зеркально. Например сейчас 21:27, а показывается 72:12.

Ну, бывает, да, когда вслепую программы пишешь, без проверки. :)

CheBuraw пишет:

В принципе это не смертельно и можно поправить тут:
byte g_registerArray[4]={8,4,2,1}; //массив цифр, указывающий разряды

Крутил, вертел Ваш код, менял значения в цикле, но ничего толкового из этого так и не вышло :(

...но может Вы поправите это в Вашем варианте? (извините за наглость)

Да кажется должно помочь, если  g_registerArray  написать в обратном порядке. Что, действительно не работает? Так сразу не соображу что-то.
Поправить наверно не трудно, но без проверки боюсь опять обману. Попробуйте лучше там, на месте починить. В конце концов, провода идущие на разряды можно перевоткнуть.

Если уж никак не получится - пишите, придумаем что-нибудь.

CheBuraw пишет:

Правильно ли я понял, что если я задействую 8 разрядов, то мне необходимо будет заменить 4 на 8 в строке
 







for( int i = 0; i < 4; i++ )

?

Абсолютно правильно, задумано именно так. Только размер переменной  disp  придётся взять побольше - unsigned long - т.к. 8 разрядов в  int  не уместятся. И таблицу  g_registerArray  удлиннить, разумеется.

Ну и потом ещё отладить всё, если я опять где-то ошибся. :)

CheBuraw
Offline
Зарегистрирован: 10.02.2015

Нашел у себя ошибку в Arduino, 4-разрядный 7-сегментный индикатор, 74HC595 сдвиговый регистр, DS1307 RTC
 

 // Разбиваем цифру по разрядам индикатора
  if (disp < 10) // если наша цифра меньше 10, то
  {
    Indicate(0, 11); // пишем в первый разряд пусто
    Indicate(1, 11); // пишем во второй разряд пусто
    Indicate(2, 11); // пишем в третий разряд пусто
    Indicate(3, disp); // пишем в четвертый разряд нашу цифру
  }
  else if (disp < 100) // если наша цифра меньше 100, то
  {
    Indicate(0, 11); // пишем в первый разряд пусто
    Indicate(1, 11); // пишем во второй разряд пусто
    Indicate(2, disp / 10); // пишем в третий разряд - цифру делёную на 10
    Indicate(3, disp % 10); // пишем в четвертый разряд цифру оставшуюся от деления на 10
/*
 Допустим наша цифра 25. 
  Если мы ее поделим на 10, то у нас получится 2,5. Цифры после запятой, в данном случае,
 у нас не остаются. Таким образом мы имеем в третем разряде цифру 2.
  В чевертый разряд мы, как раз и записываем цифру-остаток полученную в результате деления. 
В нашем случае это и есть та самая 5.
Аналогичным образом разбивается наша цифра и далее.
*/
  }
  else if (disp < 1000)
  {
    Indicate(0, 11);
    Indicate(1, disp / 100);    
    Indicate(2, (disp % 100) / 10);
    Indicate(3, disp % 10); 
  }

Indicate(0, 11); ссылается на 12 символ (учитывая, что адрес первого символа - 0, а последнего - 11) в массиве данных, а там его нет :( поэтому отображаются все сегменты, а надо чтобы ничего не отображалось :).
Что бы всё отображалось надо поправить на Indicate(0, 10);

Исправленная, оптимизированная Datak`ом и проверенная версия  кода:
Arduino, 4-разрядный 7-сегментный индикатор, 74HC595 сдвиговый регистр, DS1307 RTC

/*
********************************************************************
  Name    : Arduino, 4-разрядный 7-сегментный индикатор, 74HC595 сдвиговый регистр, DS1307 RTC
  Author  : CheBuraw
  Date    : 12 Feb, 2015
 Optimiser: Datak
  Version : 1.1
********************************************************************

4-разрядный 7-сегментный индикатор. общий анод
                1  A  F 2  3  B
 _______________|__|__|__|__|__|_____________
|         |         |    |         |         |
|    A    |    A    |    |    A    |    A    |
| F     B | F     B | dp | F     B | F     B |
|    G    |    G    |    |    G    |    G    |
| E     C | E     C | dp | E     C | E     C |
|    D    |    D    |    |    D    |    D    |
|_________|_________|____|_________|_________|
                |  |  |  |  |  |
                E  D dp  C  G  4

74HC595 Map:
     _______
Q1  |1 *  16|  Vcc                  PINS 1-7, 15   Q0 - Q7   Output Pins
Q2  |2    15|  Q0                   PIN 8	   GND	     Ground, Vss
Q3  |3    14|  DS                   PIN 9	   Q7"	     Serial Out
Q4  |4    13|  OE                   PIN 10	   MR	     Master Reclear, active low
Q5  |5    12|  ST_CP                PIN 11	   SH_CP     Shift register clock pin
Q6  |6    11|  SH_CP                PIN 12	   ST_CP     Storage register clock pin (latch pin)
Q7  |7    10|  MR                   PIN 13	   OE	     Output enable, active low
GND |8_____9|  Q7"                  PIN 14	   DS	     Serial data input
                                    PIN 16	   Vcc	     Positive supply voltage
           _______
   LED B -|1 *  16|-5V
   LED C -|2    15|-LED A
   LED D -|3    14|-PIN 11
   LED E -|4    13|-GND
   LED F -|5    12|-PIN 8
   LED G -|6    11|-PIN 12 ; 1uF TO GND
   LED dp-|7    10|-5V
      GND-|8_____9|-NILL
      
*/
#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 RTC;

int latchPin = 8;     //Пин "защелки" первого регистра подключен к ST_CP входу первого регистра отвечающего за сегменты 
int clockPin = 12;    //Пин подключен к SH_CP входу 74HC595
int dataPin = 11;     //Пин подключен к DS входу 74HC595

int TimeLight = 5;  //время для разогрева сегментов

byte SegDisplay; // переменная для вывода символов на индикаторе
byte RazrDisplay; // переменная для включения разрядов

// Настройка комбинации для отображения каждого номера на дисплее.

byte g_digits[11]={
 B01000000, B01111001,    // 0 1
 B00100100, B00110000,    // 2 3
 B00011001, B00010010,    // 4 5
 B00000010, B01111000,    // 6 7
 B00000000, B00010000,    // 8 9
 B11111111, }; 		  // все сегменты выключены

byte g_registerArray[4]={8,4,2,1}; //массив цифр, указывающий разряды

void setup() {
  Wire.begin();
  RTC.begin();
  
// обозначаем все пины как выходы
  pinMode(latchPin, OUTPUT); 
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  int hour,minute,disp = 0;

  DateTime now = RTC.now(); //Создаём переменную для вывода времени

  hour = now.hour(); // Считываем значение часов
  minute = now.minute(); // Считываем значение минут
  
  disp = (hour * 100) + minute; //"Собираем" значения часов и минут в одну цифру
 
 // Разбиваем цифру по разрядам индикатора
for( int i = 0; i < 4; i++ )
{
   if( i == 0 || disp != 0 )
      Indicate( i, disp % 10 );
   else
      Indicate( i, 10 );

   disp /= 10;
}

 }

void Indicate(int r,int x)
{
SegDisplay=g_digits[x]; // получаем цифру и выводим символ, из массива цифр, соответствующий этой цифре.
RazrDisplay=g_registerArray[r];  // получаем цифру и выводим номер регистра, из массива цифр, соответствующий этой цифре.
  digitalWrite(latchPin, LOW);  // устанавливаем синхронизацию "защелки" на LOW
      shiftOut(dataPin, clockPin, MSBFIRST, RazrDisplay); // Записываем информацию для второго регистра (Номер разряда)
      shiftOut(dataPin, clockPin, MSBFIRST, SegDisplay);  // Записываем информацию для первого регистра (Номер символа)
  digitalWrite(latchPin, HIGH);  //"защелкиваем" регистр, тем самым устанавливая значения на выходах

   delay(TimeLight); // пауза, чтобы сегменты "разгорелись"
}

...но и тут есть небольшая недоработочка -  в 0 часов будут показываться только минуты :).
Корректируется исправлением нашего пресловутого Indicate(0, 10); на Indicate(0, 0); и в этом случае до 10 утра часы будут отображаться в формате 00, 01, 02, ..., 09. Если не напрягает, то скетч готов к использованию. Ну а если кому надо будет, то, думаю, допилят. ...а может и сам как-нибудь попозже доделаю.

Datak
Offline
Зарегистрирован: 09.10.2014

CheBuraw пишет:

Корректируется исправлением нашего пресловутого Indicate(0, 10); на Indicate(0, 0); и в этом случае до 10 утра часы будут отображаться в формате 00, 01, 02, ..., 09.

Не, исправляется это тем, что условие  if -- else  в цикле вообще убирается, оставляется просто



Indicate( i, disp % 10 );

Результат должен получиться тот же.

А если всё же хочется чтобы часы показывались без лишних нулей, можно условие поменять на такое:





if( i < 3 || disp != 0 )

 

CheBuraw
Offline
Зарегистрирован: 10.02.2015

Да! На сколько все просто, как оказывается, решается :).
А я уж пробовал внуть Вашего кода еще один if запихать, но ничего путёвого из этого не получилось.

Sirox
Offline
Зарегистрирован: 24.02.2015

Добрый день, спасибо за код!

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

Щас нет возможности проверить, но если инвертировать эти значения то поможет ?

B01000000, B01111001,    // 0 1
B00100100, B00110000,    // 2 3
B00011001, B00010010,    // 4 5
B00000010, B01111000,    // 6 7
B00000000, B00010000,    // 8 9
B11111111, };        // все сегменты выключены

 

CheBuraw
Offline
Зарегистрирован: 10.02.2015

Sirox, грубо говоря, Вам необходимо все нули поменять на единички, а единички на нули.
Поясню смысл указанного Вами фрагмента кода.
Символ "B" говорит что представленное число в бинарном формате.
Далее следуют команды для зажигания сегментов индикатора - "dpGFEDCBA". Для отображения цифры 5 нам необходимо подать питание или "единички" (для индикаторов с общим катодом) на сегменты A,F,G,C,D или если написать их в алфавитном порядке A,C,D,F,G. Остальные сегменты мы оставляем "обесточенными" то есть пишем в них "нули".

Таким образом заполняем наш шаблон:
B - Говорим что число бинарное
dp - Зажигаем точку, значит ставим 1
G - Зажигаем этот сегмент, значит ставим 1
F - Зажигаем этот сегмент, значит ставим 1
E - Этот сегмент не должен гореть, значит ставим 0
D - Зажигаем этот сегмент, значит ставим 1
C - Зажигаем этот сегмент, значит ставим 1
B - Этот сегмент не должен гореть, значит ставим 0
A - Зажигаем этот сегмент, значит ставим 1
В итоге имеем следующий код для цифры 5 - B11101101 (для индикаторов с общим катодом)
 _________
|         |
|    A    |
| F     B |
|    G    |
| E     C |
|    D    |
|_________|

Остальные цифры давайте сами ;) !

Если не получиться, то пишите.

Sirox
Offline
Зарегистрирован: 24.02.2015

Не выходит, и так и пробовал и эдак, даташит моего индикатора http://zip-2002.ru/z_pdf/kem-5461agr-1.pdf / Предпологаю что порядок сегментов у меня другой, поэтому такая абра кадабра выводится.

 

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

а если просто инвертировать значение? для сегментов и для общего анода или катода

а лучше добавить переменную типа boolean которой задаешь общий катор иди анод

 

Sirox
Offline
Зарегистрирован: 24.02.2015
Получилось только выводить одинаковые числа 1111 или 2222 и т.д. При этом тускло светит первая цифра. Если хоть одна цифра другая то абра кадабра. 
Подставляю для проверки в
hour = 99; // Считываем значение часов minute = 99; // Считываем значение минут
 
#include <Wire.h>

int latchPin = 2;     //Пин "защелки" первого регистра подключен к ST_CP входу первого регистра отвечающего за сегменты 
int clockPin = 3;    //Пин подключен к SH_CP входу 74HC595
int dataPin = 4;     //Пин подключен к DS входу 74HC595

int TimeLight = 5;  //время для разогрева сегментов

byte SegDisplay; // переменная для вывода символов на индикаторе
byte RazrDisplay; // переменная для включения разрядов

// Настройка комбинации для отображения каждого номера на дисплее.

byte g_digits[11]={
B00111111, B00000110,    // 0 1
B01011011, B01001111,    // 2 3
B01100110, B01101101,    // 4 5
B01111101, B00000111,    // 6 7
B01111111, B01101111,    // 8 9
B00000000, }; 		  // все сегменты выключены

byte g_registerArray[4]={8,4,2,1}; //массив цифр, указывающий разряды

void setup() {
  Wire.begin();
  
// обозначаем все пины как выходы
  pinMode(latchPin, OUTPUT); 
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  int hour,minute,disp = 0;

  hour = 99; // Считываем значение часов
  minute = 89; // Считываем значение минут
  
  disp = (hour * 100) + minute; //"Собираем" значения часов и минут в одну цифру
 
 // Разбиваем цифру по разрядам индикатора
for( int i = 0; i < 4; i++ )
{
   if( i == 0 || disp != 0 )
      Indicate( i, disp % 10 );
   else
      Indicate( i, 10 );

   disp /= 10;
}

 }

void Indicate(int r,int x)
{
SegDisplay=g_digits[x]; // получаем цифру и выводим символ, из массива цифр, соответствующий этой цифре.
RazrDisplay=g_registerArray[r];  // получаем цифру и выводим номер регистра, из массива цифр, соответствующий этой цифре.
  digitalWrite(latchPin, LOW);  // устанавливаем синхронизацию "защелки" на LOW
      shiftOut(dataPin, clockPin, MSBFIRST, RazrDisplay); // Записываем информацию для второго регистра (Номер разряда)
      shiftOut(dataPin, clockPin, MSBFIRST, SegDisplay);  // Записываем информацию для первого регистра (Номер символа)
  digitalWrite(latchPin, HIGH);  //"защелкиваем" регистр, тем самым устанавливая значения на выходах

   delay(TimeLight); // пауза, чтобы сегменты "разгорелись"
}

 

 

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

что такое shiftOut. цикл от 0 до 8 (или наоборот) и перебор битов с дерганьем clock.

можно залезть в файлы, и найти что делает конктено shiftOut, переделать, добавить инверсию или доп. параметр boolean через & и радоваться жизни

да и незачем делать такое

disp = (hour * 100) + minute;

если умеешь выводить цифрц в нужном месте, и разбивать число по разрядам

а то получается собираем число сотни и тысячи + десятки и еденицы, а потом снова разделяем

CheBuraw
Offline
Зарегистрирован: 10.02.2015

Sirox, посмотрите повнимательнее, может где соединили не так. У меня такое было :). Долго мучался, потом нашел и все запустилось. Порой даже проще все провода повыдё1ргивать и заново подключить.
Ноги Вашего индикатора, судя по представленному даташиту, полностью совпадают с моим индикатором. Так что тут все должно быть нормально.
Пока еще не разобрал свою схему, залил Ваш скетч. Все нормально работает.

jeka_tm, из всех найденных мною вариантов, только этот мне показался понятным, поэтому я и сделал по этому принципу.
Буду Вам очень признателен, да думаю и не я один, если Вы покажете Ваше решение вывода цифр в заданном разряде. Если Ваш код будет короче и понятнее, то буду использовать его ;) !

Sirox
Offline
Зарегистрирован: 24.02.2015

Спасибо за отзывчивость ! Проверял уже раза 3, буду ковыряться дальше, приносит это дело удовольствие мне )

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013
disp = (hour * 100) + minute;

я этот вариант предлагал так как ТС мог выводить число, но не умел похоже выводить отдельную цифру в нужном разряде

вот посмотри код. может наведет на мысли переделать

переменная anod кстати задает общий анод или катод подключен

//==================================================================================
//                                Зажигаем сегменты
//==================================================================================
void F_Seg(byte n, byte point){
  byte m = Digit[n];
  for(byte i=0;i<7;i++){
    (Anod)? dWrite(Seg[i],!(m & Anod)) : dWrite(Seg[i],(m & !Anod));
    m >>= 1;
  }
  (point)? dWrite(Seg[7], !Anod) : dWrite(Seg[7], Anod);

 

CheBuraw
Offline
Зарегистрирован: 10.02.2015

Sirox, дело это интересное, и представленный мною скетч, для кого-то, сущая фигня, но для меня он такой выстраданный :). Недели две сидел вникал в работу регистров и собирал этот код из других кусочков :).
Вы отключите катоды индикатора от второго регистра и поочерёдно их подключите к минусу. Посмотрите что будет. Должно на тех что подключены отображаться какое-то число.
Если вновь ничего не получиться, то попробуйте для начала с одним регистром 74HC595 например такой скетч:

/*
********************************************************************
  Name    : shiftOutAnalogueDisplay, Test code
  Author  : Benjo Charlie
  Date    : 13 Sept, 2013
  Version : 1.0
  Notes   : This is an adaptation of the ReadAnalogVoltage tutorial.
          : 
          : The idea is to map the analogue input voltage and display that 
          : mapped value on a 7 segment display via a shiftbit register.
  Tutorial: 74HC595 Shift Bit Register using a 7 Segment Display, ...

http://www.youtube.com/watch?v=shcumKPTcTk
Shift Bit Registers can be a little tricky to map out and setup. I designed this simple bit of code to help you do just that. A simple bit shift operation that sends a HIGH signal down each of the Shift Bit Registers pins in turn. i.e. shiftOutDisplay
I've also developed this very simple method that goes hand in hand with the code and should be a good starting point for anyone who is not too familiar with Binary.
Finally, I've added some useful code to display the map that you've created. i.e.
I hope you find it useful, I know I've had a lot of fun with this stuff :)

LINKS:
shiftOutDisplay
https://gist.github.com/benjjo/6559436

shiftOutAnalogueDisplay
https://gist.github.com/benjjo/6559456

ShiftOut Reference:
http://arduino.ru/Reference/ShiftOut

ShiftOut Tutorial:
http://arduino.cc/en/Tutorial/ShiftOut

Music written by Tom Cusack
Composer -- Tom Cusack
Music from www.freemusicforvideos.com
********************************************************************

7 Segment (Common Anode) Display Map: (This can be altered to reflect your HW)

    D   E  5V   F   G
 ___|___|___|___|___|____
|                        |
|        F               |
|    E       G           |
|        D               |
|    A       C           |
|        B       H(Dot)  |
|________________________|
    |   |   |   |   |
    A   B  5V   C   H

74HC595 Map:
     _______
Q1  |1 *  16|  Vcc                  PINS 1-7, 15   Q0 - Q7   Output Pins
Q2  |2    15|  Q0                   PIN 8	   GND	     Ground, Vss
Q3  |3    14|  DS                   PIN 9	   Q7"	     Serial Out
Q4  |4    13|  OE                   PIN 10	   MR	     Master Reclear, active low
Q5  |5    12|  ST_CP                PIN 11	   SH_CP     Shift register clock pin
Q6  |6    11|  SH_CP                PIN 12	   ST_CP     Storage register clock pin (latch pin)
Q7  |7    10|  MR                   PIN 13	   OE	     Output enable, active low
GND |8_____9|  Q7"                  PIN 14	   DS	     Serial data input
                                    PIN 16	   Vcc	     Positive supply voltage
           _______
   LED Q1-|1 *  16|-5V
   LED Q2-|2    15|-LED Q0
   LED Q3-|3    14|-PIN 11
   LED Q4-|4    13|-GND
   LED Q5-|5    12|-PIN 8
   LED Q6-|6    11|-PIN 12 ; 1uF TO GND
   DOT Q7-|7    10|-5V
      GND-|8_____9|-NILL

*/

int latchPin = 8;     //Pin connected to ST_CP of 74HC595
int clockPin = 12;    //Pin connected to SH_CP of 74HC595
int dataPin = 11;     //Pin connected to DS of 74HC595
byte SegDisplay;
int sensorPin=A0;

// Setup the combination to display each number on the display
byte ZERO = 1; // A number value goes here.
byte ONE = 2;
byte TWO = 4;
byte THREE = 8;
byte FOUR = 16;
byte FIVE = 32;
byte SIX = 64;
byte SEVEN = 128;
byte EIGHT = 512;
byte NINE = 144;
byte MAX = 128;

void setup() {     
  pinMode(8, OUTPUT);  
  pinMode(12, OUTPUT);
  pinMode(11, OUTPUT);
  Serial.begin(9600); 
}

void loop() {
  int analogueValue = analogRead(sensorPin);  
  
  analogueValue = map(analogueValue, 0, 1023, 0, 10);
  
  digitalWrite(latchPin, LOW);  
  
  if (analogueValue==0) SegDisplay=ZERO; // Min value
  if (analogueValue==1) SegDisplay=ONE;
  if (analogueValue==2) SegDisplay=TWO;    
  if (analogueValue==3) SegDisplay=THREE;    
  if (analogueValue==4) SegDisplay=FOUR;    
  if (analogueValue==5) SegDisplay=FIVE;    
  if (analogueValue==6) SegDisplay=SIX;    
  if (analogueValue==7) SegDisplay=SEVEN;    
  if (analogueValue==8) SegDisplay=EIGHT;    
  if (analogueValue==9) SegDisplay=NINE;    
  if (analogueValue>9) SegDisplay=MAX; // Max Value: illuminate Dot
  
  shiftOut(dataPin, clockPin, MSBFIRST, SegDisplay);  

  digitalWrite(latchPin, HIGH); //take the latch pin high so the LEDs will light up:

  // Print out the corresponding Shift Bit Register pin with it's value.
  Serial.print("analogueValue = ");
  Serial.println(analogueValue);
  Serial.print("SegDisplay = ");
  Serial.println(SegDisplay, BIN);
  
  delay(100); // Allow to settle
}

Я с него начинал :). Только тут надо еще переменный резистор подключить к A0, выше есть схема подключения. В скетче есть ссылка на ролик ютубовкий, он на английском, но, даже если Вы им и не владеете, то думаю в общих чертах поймете о чем речь идет.

CheBuraw
Offline
Зарегистрирован: 10.02.2015

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

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

да весь смысла нет

void F_Seg(byte n){
  byte m = Digit[n]; //считываем из массива значение по адресу n, 
  //где n это число которое нужно вывести
  for(byte i=0;i<7;i++){//цикл для перебора всех битов в байте
    (Anod)? dWrite(Seg[i],!(m & Anod)) : dWrite(Seg[i],(m & !Anod));// тоже самое можно записать понятнее
    /*
    if(Anod==1){
      dWrite(Seg[i],!(m & Anod));
    }
    else{
     dWrite(Seg[i],(m & !Anod));
    } 
     */
    m >>= 1;//сдвигаем вправо на 1
  }
}

функция dWrite это урезанная digitalWrite и делает тоже самое, ну почти. шим не отрубает на выходе

CheBuraw
Offline
Зарегистрирован: 10.02.2015

jeka_tm, правильно ли я понял, что этот код некая альтернатива shiftOut? Или тут есть и средство разбиения цифр на разряды, т.е. записи каждой цифры в нужный разряд?

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

это не альтернатива. это код для управления индикатором без 74HC595. а точнее 1 цифрой просто ногодрыганьем

короче это долго. если работает оставь как есть

Datak
Offline
Зарегистрирован: 09.10.2014

Sirox, инвертировать нужно не только массив g_digits,  но и  g_registerArray тоже.

В остальном - всё так же, по-моему. Должно работать.

Sirox
Offline
Зарегистрирован: 24.02.2015
byte g_registerArray[4]={8,4,2,1};

А каким образом? 1,2,4,8 делал, не получается. Может цифры другие должны быть?

Datak
Offline
Зарегистрирован: 09.10.2014


byte g_registerArray[4]={~8,~4,~2,~1}; //массив цифр, указывающий разряды

 

Sirox
Offline
Зарегистрирован: 24.02.2015

Одна голова хорошо, а две лучше, спасибо все заработало!

Sirox
Offline
Зарегистрирован: 24.02.2015

Доброго дня, дисплей я планирую приспособить для авто по типу (без верхней шкалы)

Есть проблемы по выводу дробных и отрицательных чисел ,  а также по избавлению от delay

Вот код для вывода temp, цифры моргают, как правильно использовать millis или библиотеку которая это просто сделает?

 

#include <Wire.h> 
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 5

int latchPin = 2;     //Пин "защелки" первого регистра подключен к ST_CP входу первого регистра отвечающего за сегменты 
int clockPin = 3;    //Пин подключен к SH_CP входу 74HC595
int dataPin = 4;     //Пин подключен к DS входу 74HC595

int TimeLight = 5;  //время для разогрева сегментов

byte SegDisplay; // переменная для вывода символов на индикаторе
byte RazrDisplay; // переменная для включения разрядов

// Настройка комбинации для отображения каждого номера на дисплее.

byte g_digits[11]={
B00111111, B00000110,    // 0 1
B01011011, B01001111,    // 2 3
B01100110, B01101101,    // 4 5
B01111101, B00000111,    // 6 7
B01111111, B01101111,    // 8 9
B00000000, }; 		  // все сегменты выключены

byte g_registerArray[4]={~8,~4,~2,~1}; //массив цифр, указывающий разряды

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

float temp = 0;

void setup() {
 
   // start serial port
  Serial.begin(9600);
  // Start up the library
  sensors.begin();
  
// обозначаем все пины как выходы
  pinMode(latchPin, OUTPUT); 
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  int disp = 0;

sensors.requestTemperatures();
  temp=sensors.getTempCByIndex(0);
  Serial.print(temp);
  
  disp = temp;
 // Разбиваем цифру по разрядам индикатора
for( int i = 0; i < 4; i++ )
{
   if( i == 0 || disp != 0 )
      Indicate( i, disp % 10 );
   else
      Indicate( i, 10 );

   disp /= 10;
}

 }

void Indicate(int r,int x)
{
SegDisplay=g_digits[x]; // получаем цифру и выводим символ, из массива цифр, соответствующий этой цифре.
RazrDisplay=g_registerArray[r];  // получаем цифру и выводим номер регистра, из массива цифр, соответствующий этой цифре.
  digitalWrite(latchPin, LOW);  // устанавливаем синхронизацию "защелки" на LOW
      shiftOut(dataPin, clockPin, MSBFIRST, RazrDisplay); // Записываем информацию для второго регистра (Номер разряда)
      shiftOut(dataPin, clockPin, MSBFIRST, SegDisplay);  // Записываем информацию для первого регистра (Номер символа)
  digitalWrite(latchPin, HIGH);  //"защелкиваем" регистр, тем самым устанавливая значения на выходах

   delay(TimeLight); // пауза, чтобы сегменты "разгорелись"
}

 

По дробным и отрицательным нашол код по преобразованию , но там напрямую без регистров подключено, как совместить?

 

void ledDigitDisplay(float num, float time)
{
	unsigned long ltime = millis();
	
	// Настройки
	// 6, 8, 9, 12 - GND
	int pin[] = {6, 7, 8, 9, 10, 2, 11, 3, 4, 12, 13, 5}; // Пины
	int settingsSegments[] = {pin[10], pin[6], pin[3], pin[1], pin[0], pin[9], pin[4], pin[2]}; // Порядок сегментов
	int segments[] = {0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110, 0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111, 0b10000000, 0b01000000}; // 1, 2, 3, 4, 5, 6, 7, 8, 9, '.', '-'
	for(int i = 0; i < 12; ++i) pinMode(pin[i], OUTPUT); // Определяем пины как выход

	int floatingPoint = 0, minus = 4;

	if(num > -1000 && num < 0) // Разбираемся с отрицательными числами
	{
		minus--;
		if(num > -100) minus--;
		if(num > -10) minus--;
		num = -num;
	}

	for(int i = 0; num < 1000 && minus == 4; ++i) // Разбираемся с дробными числами
	{
		if(int(num * 10) != int(num)*10)
		{
			floatingPoint++;
			num *= 10;
		}
		else
			break;
	}
	
	for(int i = 0, temp; millis() - ltime <= time * 1000; i++)
	{
		if(i == 4) i = 0;

		temp = int(num / pow(10, i)) % 10; // Цифра которую передадим индикатору
		if(num >= 10000 || num <= -10000 || minus == i) // Если минус или переполнение, передаем '-'
			temp = 11;

		if(i == 3 && (num >= 1000 || floatingPoint == i || minus == i)) pinMode(pin[11], OUTPUT); else pinMode(pin[11], INPUT); // Работаем с 4 разрядом
		if(i == 2 && (num >= 100 || floatingPoint == i || minus == i)) pinMode(pin[8], OUTPUT); else pinMode(pin[8], INPUT); // Работаем с 3 разрядом
		if(i == 1 && (num >= 10 || floatingPoint == i || minus == i)) pinMode(pin[7], OUTPUT); else pinMode(pin[7], INPUT); // Работаем с 2 разрядом
		if(i == 0) pinMode(pin[5], OUTPUT); else pinMode(pin[5], INPUT); // Работаем с 1 разрядом

		for(int j = 0; j < 8; j++) // Передаем число
			if(segments[temp] & (1 << j))
				digitalWrite(settingsSegments[j], HIGH);

		if(floatingPoint && floatingPoint == i) // Передаем точку
			digitalWrite(settingsSegments[7], HIGH);

		delay(1); // Небольшая пауза, чтобы светодиоды разгорелись

		for(int j = 0; j < 8; j++) digitalWrite(settingsSegments[j], LOW); // Выключаем все светодиоды
	}
}

void setup()
{

}



void loop()
{
	ledDigitDisplay(3.14, 2);
	ledDigitDisplay(123, 2);
	ledDigitDisplay(-5, 2);
}

 

bwn
Offline
Зарегистрирован: 25.08.2014

по millis():

unsigned long a;    Объявили переменную

if (a<=millis()) {Выполнили; a=millis()+5000;} Проверили, произвели действия, обновили значение задержки (5 секунд)

Все.

CheBuraw
Offline
Зарегистрирован: 10.02.2015

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

По поводу предложенного Вами кода. А можете его целиком и под спойлер выложить, или ссылочку на него дать? Думаю я смогу их "объединить", ну или по крайней мере попробую :). Самому интересно себя проверить.

 

Sirox
Offline
Зарегистрирован: 24.02.2015

Для свечения сегментов - нет претензий, у меня там есть еще один который дает задержку при опросе датчика. А код, это был весь, ссылка вот http://nextable.ru/144-4-razryadnyy-7-segmentnyy-indikator-i-arduino.html. было б замечательно если получится.

Sirox
Offline
Зарегистрирован: 24.02.2015

Спасибо! Бубду пробывать

Sirox
Offline
Зарегистрирован: 24.02.2015

CheBuraw, привет!

Есть какие нибудь успехи по числам  ?

CheBuraw
Offline
Зарегистрирован: 10.02.2015

Sirox, Извините, но пока порадовать не чем :(. Все попытки успехом не увенчались. Я, ведь тоже, програмист тот еще :) сам ардуиной и програмерством в целом где-то полгода занимаюсь. Буду продолжать попытки. Я думаю Datak бы Вам помог, он мне вон сколько конструктивных советов давал, да и Вам тоже помочь успел.

Datak
Offline
Зарегистрирован: 09.10.2014

Да я отвлёкся как-то, извините. К тому же, думаю, вдруг там у вас спортивный интерес, или азарт какой-нибудь - а тут я со своими советами. :)

Sirox пишет:
По дробным и отрицательным нашол код по преобразованию , но там напрямую без регистров подключено, как совместить?

Вывод цифр там происходит в строках 41...55. Для начала можно просто заменить их на вызов нашей Indicate( i, temp ), но придётся добавить дополнительные проверки для вывода точки и минуса, и незначащие нули у них гасятся немного по-другому... так что проще получится своё написать. :)

Для начала разбираемся с минусом:




















#define SPACE 10
#define MINUS 11

int  disp;
bool minus;

.......

disp = -123; // получаем какое-то значение, например с датчика

.......

minus = ( disp < 0 );

if( minus ) disp = -disp;

for( int i = 0; i < 4; i++ )
{
   byte Value;

   if( i == 0 || disp != 0 )
   {
      Value = disp % 10
      disp /= 10;
   }
   else
   if( minus ) 
   {
      Value = MINUS;
      minus = FALSE;
   }
   else
   {
      Value = SPACE;
   }

   Indicate( i, Value );
}

В таблицу g_digits, разумеется, надо добавить ещё одно значение - для вывода знака "минус". Попробуйте, должно работать.

Останется добавить "точку" для дробных чисел, и ещё, желательно, проверку на переполнение. Но не сейчас, попозже, ладно?

 
Sirox
Offline
Зарегистрирован: 24.02.2015

Спасибо огромное! Вечером буду пробывать, да конечно подождем 

Nemo
Offline
Зарегистрирован: 07.03.2015

Подскажите пожалуйста, купил вот такой вот шилд. http://www.aliexpress.com/snapshot/6494994863.html?orderId=65798713751283

Не могу понять как правильно подключить на 8 11 и 12 входы андруино что куда.

QH; RCLK; SCLK - на шилде, и

DS; ST_CP; SH_CP - в данной теме.

Что куда чтобы ничего не спалить?

 

CheBuraw
Offline
Зарегистрирован: 10.02.2015

Nemo, я думаю спалить Вам там ничего не удастся, если правильно подключите GND и VCC. А вот остальные пины я бы советовал Вам прозвонить тестером, на какие ноги регистров они разведены. Нам нужны 11, 12 и 14 ножки регистров.

Описание у продавца данного шильдика крайне непонятное :). Если подключить получиться. то решение очень компактное. Мне же вон что нагородить пришлось :)

Вот нашел схему подключения Вашего шилда на каком-то китайском сайте, немного погуглив :).

Судя по ней на данном шилде с одной стороны выходы именуются SCLK, RCLK и DIO , что для нашего подключения значит SH_CP, ST_CP и DS соответственно. Я так думаю что именно на этих контактах и распаены контакты для подключения на данном шилде.

Еще я обратил внимание, что тут к первому регистру подключены разряды, а ко второму сегменты. Таким образом для работы с нашим скетчем Вам потребуется поменять очередность регистров в функции Indicate. Ниже уже подкорретированный для Вас вариант.
 

void Indicate(int r,int x)
{
SegDisplay=g_digits[x]; // получаем цифру и выводим символ, из массива цифр, соответствующий этой цифре.
RazrDisplay=g_registerArray[r];  // получаем цифру и выводим номер регистра, из массива цифр, соответствующий этой цифре.
  digitalWrite(latchPin, LOW);  // устанавливаем синхронизацию "защелки" на LOW
      shiftOut(dataPin, clockPin, MSBFIRST, SegDisplay);  // Записываем информацию для второго регистра (Номер символа)
      shiftOut(dataPin, clockPin, MSBFIRST, RazrDisplay); // Записываем информацию для первого регистра (Номер разряда)
 digitalWrite(latchPin, HIGH);  //"защелкиваем" регистр, тем самым устанавливая значения на выходах

   delay(TimeLight); // пауза, чтобы сегменты "разгорелись"
}

 

Nemo
Offline
Зарегистрирован: 07.03.2015

CheBuraw Большое спасибо. По коду сам допетрил.  Все подключил, индикация работает. Девайсик действительно компактный получился.

Правильный ответ на мой вопрос, который задан постом выше такой:

RCLK - это защелка (SS, STcp); SCLK - тактовый (CLK, SHcp); а QH - остается пин данных (SDI, DS)

(Люди на соседнем ресурсе подсказали.)

Теперь вот ломаю голову как отобразить числа с плавающей точкой. Например "-3.75"

Собираю термометр на ds18b20. По индикации с минусом - получилось. К стати, по поводу минуса: как переделать цыкл for на минус так и не допетрил. Пришлось идти более длинным путем. А если брать модуль числа и после вывода подставлять минус вперед, то мерцение всех цыфр получается не здоровое. Теперь у меня в коде по if проверяется число на минус, затем через if выводится минусовое значение, а ниже в случае положительного значения - выводится через for. Может кому сгодиться.

А вот с точкой - пока ваяю.

Nemo
Offline
Зарегистрирован: 07.03.2015

Ну как то так:

int latchPin = 8;     // Пин "защелки" первого регистра подключен к RCLK (SS, ST_CP) входу 
int clockPin = 12;    // Пин "тактовый" подключен к SCLK (CLK, SH_CP) входу 74HC595
int dataPin = 11;     // Пин "данных" подключен к QH (SDI, DS) входу 74HC595

int TimeLight = 5;    // Время для разогрева сегментов

byte SegDisplay;      // Переменная для вывода символов на индикаторе
byte RazrDisplay;     // Переменная для включения разрядов

// Настройка комбинации для отображения каждого номера на индикаторе
// Массив цифр, генерирующий на сегментах цифры в двоичной системе исчисления
byte g_digits[25]={
 B11000000, B11111001, B10100100, B10110000, B10011001,   // 0 1 2 3 4
 B10010010, B10000010, B11111000, B10000000, B10010000,   // 5 6 7 8 9
 B01000000, B01111001, B00100100, B00110000, B00011001,   // 0. 1. 2. 3. 4.
 B00010010, B00000010, B01111000, B00000000, B00010000,   // 5. 6. 7. 8. 9. 
 B01111111, B10111111, B10011100, B11000110,   // точка, прочерк, градус, цельсия
 B11111111, }; 		                       // все сегменты выключены
byte g_registerArray[4]={1,2,4,8}; //массив цифр, указывающий разряды

void setup()
{ // обозначаем все пины как выходы
  pinMode(latchPin, OUTPUT); 
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop()
{
  float temperature = 1.23;  // Можете подставить любые значения. Следите чтобы стояла именно . (например 5.75 а не 5,75) иначе - ошибка компаиляции вам обеспечена
  int disp;                  // Переменная для целых чисел температуры               
  int disp_sot;              // Переменная для дробной части числа
  
  disp = int (temperature);                 // Избавляемся от десятых и сотых (оставляем челую часть числа)
  temperature = (temperature - disp) * 100; // Переводим десятые и сотые в целую часть числа
  disp_sot = int (abs(temperature));        // Обрубаем значения после запятой (оставляем целую часть числа) модуль - для избавления от минуса
  
  // Разбиваем цифру по разрядам индикатора
  if (disp < 0) // Если значение минусовое, то выполняется следующее условие
    {
      disp = abs(disp); // используем модуль дабы избавиться от минуса. Его мы подставим в процессе
        /* Допустим наша цифра 25. 
           Если мы ее поделим на 10, то у нас получится 2,5. Цифры после запятой, в данном случае,
           у нас не остаются. Таким образом мы имеем в третем разряде цифру 2.
           В чевертый разряд мы, как раз и записываем цифру-остаток полученную в результате деления. 
           В нашем случае это и есть та самая 5.
           Аналогичным образом разбивается наша цифра и далее.
        */
      if (disp < 10) // если наша цифра меньше 10, то
        {
        Indicate(0, disp_sot % 10);      // пишем в первый разряд сотую долю цифры
        Indicate(1, disp_sot / 10);      // пишем во второй разряд десятую долю цифры
        Indicate(2, disp + 10);          // пишем в третий разряд нашу цифру с точкой
        Indicate(3, 21);                 // пишем в четвертый разряд минус
        }
      else if (disp < 100) // если наша цифра меньше 100, то
        {
        Indicate(0, disp_sot / 10);      // пишем в первый разряд десятую долю цифры
        Indicate(1, (disp % 10) + 10);   // пишем во второй разряд - цифру оставшуюся от деления на 10 с точкой
        Indicate(2, disp / 10);          // пишем в третий разряд - цифру делёную на 10
        Indicate(3, 21);                 // пишем в четвертый разряд минус
        }
      else
        {
        Indicate(0, 21);    // Думаю что температура ниже 99 градусов
        Indicate(1, 21);    // вряд ли возможна, поэтому
        Indicate(2, 21);    // выводим прочерки во всех регистрах
        Indicate(3, 21); 
        }     
    }
  else if (disp == 0)    // Значение температуры ровно 0 градусов
    {
        Indicate(0, 23);      // пишем в первый разряд - символ цельсия "С"
        Indicate(1, 22);      // пишем во второй разряд - символ градуса
        Indicate(2, disp);    // пишем в третий разряд - цифру ноль
        Indicate(3, 24);      // пишем в четвертый разряд пусто
    }
  else // Если значение положительное, то выполняется следующий цикл
    {        
      if (disp < 10) // если наша цифра меньше 10, то
        {
        Indicate(0, 22);                 // пишем в первый разряд символ градуса
        Indicate(1, disp_sot % 10);      // пишем во второй разряд сотую долю цифры
        Indicate(2, disp_sot / 10);      // пишем в третий разряд десятую долю цифры
        Indicate(3, disp + 10);          // пишем в четвертый разряд нашу цифру с точкой
        }
      else if (disp < 100) // если наша цифра меньше 100, то
        {
        Indicate(0, disp_sot % 10);      // пишем в первый разряд - сотую долю цифры
        Indicate(1, disp_sot / 10);      // пишем во второй разряд - десятую долю цифры
        Indicate(2, (disp % 10) + 10);   // пишем в третий разряд - цифру оставшуюся от деления на 10 с точкой
        Indicate(3, disp / 10);          // пишем в четвертый разряд - цифру делёную на 10
        }
      else if (disp < 1000) // если наша цифра меньше 1000, то
        {
        Indicate(0, disp_sot / 10);      // пишем в первый разряд - десятую долю цифры
        Indicate(1, (disp % 10) + 10);   // пишем во второй разряд - последнюю целую цифру с точкой
        Indicate(2, (disp % 100) / 10);  // пишем в третий разряд - цифру оставшуюся от деления на 100 
        Indicate(3, (disp / 100));       // пишем в четвертый разряд - цифру делёную на 100
        }
       else // перестраховаться, на случай если вы засунете свой термометр в доменную печь
        {
        Indicate(0, 21);    // Думаю что температура выше 999 градусов
        Indicate(1, 21);    // вряд ли возможна, поэтому
        Indicate(2, 21);    // выводим прочерки во всех регистрах
        Indicate(3, 21); 
        }       

/*  Как реализовать короткий цыкл с плавающей точкой с десятыми и сотыми, пока не придумал
    for( int i = 0; i < 4; i++ )
      {
      if( i == 0 || disp != 0 )
          Indicate( i, disp % 10 );
      else
          Indicate( i, 24 );
      disp /= 10;
      }
*/      
    }
}

void Indicate(int r,int x) // Функция собственно, отображения цыфр на индикаторе.
  {
    SegDisplay=g_digits[x]; // получаем цифру и выводим символ, из массива цифр, соответствующий этой цифре.
    RazrDisplay=g_registerArray[r];  // получаем цифру и выводим номер регистра, из массива цифр, соответствующий этой цифре.
    digitalWrite(latchPin, LOW);   // устанавливаем синхронизацию "защелки" на LOW
      shiftOut(dataPin, clockPin, MSBFIRST, SegDisplay);  // Записываем информацию для первого регистра (Номер символа)
      shiftOut(dataPin, clockPin, MSBFIRST, RazrDisplay); // Записываем информацию для второго регистра (Номер разряда)
    digitalWrite(latchPin, HIGH);  //"защелкиваем" регистр, тем самым устанавливая значения на выходах
  
    delay(TimeLight); // пауза, чтобы сегменты "разгорелись"
  }

в строке №30 можно менять значение на любый цифры, и смотреть что получается на индикаторе. Обрабатывается диапазон значений от -99,99 до +999 градусов цельсия с сотыми долями.

Прикрутить реальный датчик температуры ds18b20 думаю сможете сами. Это уже дело техники.

Осталось все это великолепие записать как библиотеку, так как в дальнейшем планирую успользовать в одном проекте несколько датчиков и несколько индикаторов. (Улица, дом, телица, аквариум, и т.д.)  Не пропадать же выходам Arduino !

Sirox
Offline
Зарегистрирован: 24.02.2015

Отличная работа, благодарствую! У тебя с датчиком получилось? А то при опросе датчика задержка которая мешает (мерцают цифры)

Nemo
Offline
Зарегистрирован: 07.03.2015

Sirox пишет:

Отличная работа, благодарствую! У тебя с датчиком получилось? А то при опросе датчика задержка которая мешает (мерцают цифры)

Спсибо. Сейчас кумекаю как эту красоту в функцию запихнуть. У меня будет датчик не один. С датчиком поке не пробовал. Привинчу - отпишусь.

К стати: переменные TimeLight, SegDisplay, RazrDisplay можно объявлять непосредственно в самой функции Indicate. Я их туда перенес.

И еще: баг какой то. Если поставить температуру 36,6 - показывает 36,59 Где то арифметика с округлениями шалит наверное. Но погрешность в 1 сотую думаю не критично.

p/s/ а как код свернуть, чтобы страницу не раздувать?

Sirox
Offline
Зарегистрирован: 24.02.2015

4 дисплея сразу это сильно) Можно проще - выводить на одном по кнопке или по интервалу, чтобы различать датчики можно докупить одноразрядный индикатор и поставить рядом, на котом будет меняться символ или цифра обозначающая тот или иной датчик.