Подписаться на получение новых статей на почту:

Шаг №30. DS1307 и AVR. Двоично-десятичный формат BCD

Всем привет. В этой статье мы продолжим работу с часами реального времени DS1307. В прошлой статье, мы разобрались с его настройками, подключением а также написали основные функции приема/передачи и проверили устройство в Proteus используя отладчик I2C. В этот раз выедем информацию с часов на семи сегментный индикатор (Статья №5). Т.е. выведем время. Но для этого нам необходимо провести конвертацию, так как DS1307 это двоично – десятичные часы-календарь.

Разберемся с BCD форматом. BCD формат (Binary-Coded Decimal) это двоично -десятичный формат, который может быть неупакованный и упакованный. При первом варианте десятичная цифра занимает целый байт, недостатком при этом является неэкономичное использование памяти, т.к. старший полубайт всегда равняется 0. Плюсом является то что не возникает разночтений, т.е. цифра в шестнадцатеричном виде равна в десятичном, также нет необходимости выполнять дополнительных операций.

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

!!! Помним, что внутренние представление чисел в регистрах – двоичное. Поэтому упакованный BCD формат дает уникальную возможность не только экономить байты, а еще и наряду с выводимой информацией из байта содержать в себе настроечные биты (рис. ниже), например в предыдущей статье (№29) в таблице регистров 2 –й регистр по выводу часов 6 и 5 бит содержат настроечные биты , а уже 5 (в зависимости от настроек 6) и 4-й содержат 2–й и 1-й десяток часов соответственно, и остальные 4 младших бита – двоичное представление единиц часов, отсюда и BCD формат.

регистр часов Ds1307

!!!Хочется также отметить что некоторые микропроцессорные системы имеют специальную инструкцию для коррекции данного формата, но в AVR, такой инструкции нет. Но это не беда, как Вы увидите ниже, небольшими арифметическими операциями мы легко будем делать конвертацию.

Разберем дополнительные операции для второго варианта на примере. Например необходимо записать десятичное число 37, в BCD формате оно будет иметь вид 11 0111. Например калькулятор истолкует бинарный код, как десятичное 55. Или десятичное 37 как 10 0101. Поэтому необходимо провести следующие операции.:

         — 37 делим на 10, в итоге получаем целое число 3, все дробное откидываем, получаем в бинарном виде 0000 0011;
         — сдвигаем младшую часть полубайта, содержащей 3 на 4 бита влево , уже имеем вид 0011 0000;
         — теперь применяем деление по модулю 10 (%10), т.е. число 37%10. В результате получаем 7, в буфере имеем бинарный код 0000 0111;
         — и последние — производим сложение 0011 0000+ 0000 0111 = 11 0111;
         — все отправляем данный код в часы.

Ниже код по преобразованию числа в двоично десятичный формат:

time = ((time/10)<<4) + time%10;
TWDR = time;

, который вставляем в функцию записи (которую рассмотрели в предыдущей статье №29), перед занесением информации в регистр данных.

void write_DS1307 (uint8_t reg, uint8_t time)
{
             ………………………
           time = ((time/10)<<4) + time%10;
           TWDR = time;
            …………………………….
}

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

uint8_t read_DS1307 (uint8_t reg)
{
         ……………………………………………………………...
         time = TWDR;
         time = (((time & 0xF0) >> 4)*10)+(time & 0x0F);
         …………………………………………………………………………...
}

Сначала выделяем старший полубайт, переносим в младший , умножаем на 10 и получаем десятки. Далее выделяем младшую часть и все это слаживаем и получаем результат.

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

m=read_DS1307 (0×01); // читаем минуты
_delay_ms (100); //задержка необходима для симулятора
h=read_DS1307 (0×02); // читаем часы
_delay_ms (100); // задержка
i = h+(m*0.01); /*передаем индикатору формат времени, для этого минуты умножаем на 0,01 и прибавляем часы, получаем следующий формат чч,мм*/
Display (i); // функция вывода на индикатор

 

Как видите ничего сложного. Исходники и проект выложены в конце статьи. Ниже результат в Proteus и в железе. Наверное надо отметить разницу между ними:

— для индикатора в протеусе задержка на выключения транзисторов 50 мс, в железе 30;

— как видите выше между функциями передачи приема информации для протеуса ставим задержку 100мс. Для железа можно и неставить.

DS1307 в Proteus, выводим на индикаторDS1307 в "железе" выводим на индикатор

Исходники и проект

Выводим время на индикатор. ( Скачали: 362 чел. ) 

На этом все. В следующей статье интегрируем написанный код для часов в наш контроллер сбора данных. Подведем итог. Запланируем оставшуюся работу для доведения проекта до конца. Пробуйте, экспериментируйте. Всем пока.

Просмотрено 9920 раз.

Я на Google+

Шаг №30. DS1307 и AVR. Двоично-десятичный формат BCD: 5 комментариев

  1. Здравствуйте, у меня есть программа для DS1302 и с разрешения админа хотел бы выложить ее здесь, может она кому то пригодится. Не буду утверждать, что она идеальна, так как я новичок в программирования, тем не менее может кто то более опытный подскажет как ее оптимизировать. Начну с описания. Порт D подключен к семисегментному индикатору через резисторы 330 Ом. PB6, PB7 — кнопки установки часов и минут, PC1 — кнопка при нажатии на которую (нужно держать постоянно) программа переходит в установку времени. При отжатии начинается считывание времени. Кнопки подтянуты к плюсу через резистор на 10 кОм. Думаю их можно не ставить, если включать подтяжку на соответствующих портах, но у меня они стоят так я думаю надежней. PB0, PB1, PB2, PB3 — порты которые отвечают за включение соответствующей цифры, включены через транзисторы у меня это КП505 . Между портом и затвором транзистора ставятся резисторы на 3 кОм, между затвором и землей на 10 кОм (хотя это может и лишнее). Исток подключен к земле, сток к соответствующей цифре на индикаторе. DS1302 подключена следующим образом 5 — PC5 — rst, 6 — PC4 — I/O — данные, 7 — PC3 — sclk — синхроимпульсы. Подключение питания к микроконтроллеру и ds1302 стандартно.

  2. здесь производим определение портов и назначаем переменные

    #ifndef _indikator_h
    #define _indikator_h
     
    uint8_t digit_0;//младший разряд
    uint8_t digit_1;
    uint8_t digit_2;
    uint8_t digit_3;
    
    #define SEG_A  1
    #define SEG_B  2
    #define SEG_C  4
    #define SEG_D  8
    #define SEG_E  16
    #define SEG_F  32
    #define SEG_G  64
    #define SEG_H  128
    
    //определение цифр 
    #define SYM_0  (SEG_A+SEG_B+SEG_C+SEG_D+SEG_E+SEG_F)
    #define SYM_1  (SEG_B+SEG_C)
    #define SYM_2  (SEG_A+SEG_B+SEG_G+SEG_D+SEG_E)
    #define SYM_3  (SEG_A+SEG_B+SEG_C+SEG_D+SEG_G)
    #define SYM_4  (SEG_B+SEG_C+SEG_G+SEG_F)
    #define SYM_5  (SEG_A+SEG_F+SEG_C+SEG_D+SEG_G)
    #define SYM_6  (SEG_A+SEG_G+SEG_C+SEG_D+SEG_E+SEG_F)
    #define SYM_7  (SEG_A+SEG_B+SEG_C)
    #define SYM_8  (SEG_A+SEG_B+SEG_C+SEG_D+SEG_E+SEG_F+SEG_G)
    #define SYM_9  (SEG_A + SEG_B + SEG_C + SEG_D + SEG_G + SEG_F)
    #define SYM_MINUS  SEG_G
    #define SYM_GRAD  (SEG_D+SEG_E+SEG_G)
    #define SYM_TOCHKA  (SEG_H)
    #define SYM_OFF  0
    #endif

  3. #include 
    #include 
    #include 
    #include 
    #include 
    #include "indikator.h"
    
    #define DIG0 PB0
    #define DIG1 PB1
    #define DIG2 PB2
    #define DIG3 PB3
    
    #define E PC5
    #define IO PC4
    #define SCLK PC3
    
    //посылаем команду или байт данных в часы
    void write(unsigned char cmd)
    {
    	DDRC |= (1<<E) | (1<<SCLK)|(1<<IO);
    	PORTC |= (1<<E);//E=1
    	_delay_us(4);
    	for(unsigned char i=0; i<8; i++)
    	{
    		if((cmd&(1<<i)) == 1<<i)
    		{
    			PORTC |= (1<<IO);
    		}
    		else
    		{
    			PORTC &= ~(1<<IO);
    		}
    		PORTC |= (1<<SCLK);
    		_delay_us(1);
    		PORTC &= ~(1<<IO);
    		PORTC &= ~(1<<SCLK);
    	} 
    }
    
    //вызываем после записи байта данных в часы
    void end_write_data()
    {
    	PORTC &= ~(1<<E);
    }
    
    unsigned char read()
    {
    	unsigned char readbyte=0;
    	DDRC &= ~(1<<IO);
    	for(unsigned char i=0;i<8;i++)
    	{
    		PORTC |= 1<<SCLK;
    		if((PINC & (1<<IO))==0)
    		{
    			readbyte &= ~(1<<i);
    		}
    		else
    		{
    			readbyte |= 1<<i;
    		}
    		_delay_us(10);
    		PORTC &= ~(1<<SCLK);
    		_delay_us(2);
    	}
    	PORTC &= ~(1<<E);
    	_delay_us(4);
    	return readbyte;
    }
    
    ClrWP()
    {
    	write(0x8E);
    	write(0x00);
    	end_write_data();
    }
    
    void StartTime()
    {
    	unsigned char sec;
    	ClrWP();
    //	set seconds and bit CH
    	write(0x80);
    	sec=read();	
    	sec&=~(1<<7);//bit CH is 7- сбрасываем
    	write(0x80);
    	write(0x00);
    	end_write_data();
    }
    
    void SetSeconds(unsigned char sec)
    {
    	unsigned char temp;
    	temp=sec/10;
    	sec%=10;
    	temp<>4) & 0b00000111);
    	sec &= 0x0F;
    	
    	_delay_ms(1);	
    	return 10*sec10+sec;
    }
    
    void SetMinutes(unsigned char min)
    {
    	unsigned char temp;
    	temp=min/10;
    	min%=10;
    	temp<<=4;
    	min |= temp;
    	//set minutes
    	write(0x82);
    	write(min);
    	end_write_data();
    }
    
    void SetHours(unsigned char hour)
    {
        unsigned char temp;
    	temp=hour/10;
    	hour%=10;
    	temp<>4) & 0b00000111);
    	min &= 0x0F;
    	return 10*min10+min;
    }
    
    unsigned char ReceiveHours()
    {
    	unsigned char hour, hour10;	
    	write(0x85);
    	hour = read();
    	
    	hour10 = ((hour>>4) & 0x0F);
    	hour &= 0x0F;
    	return 10*hour10+hour;
    }
    
    uint8_t i, j, min, hour;
    void time_min(void) // установка минут
    {
       if((PINB&0x80)==0x00)
       {
         _delay_ms(60);
         if((PINB&0x80)==0x00)
         {
            j++;
         }
       }
       if (j>59)
       {
         j=0;
       }
       min=j;
       SetMinutes(min);
       digit_1 = min / 10;
       min -= digit_1*10;
       digit_0 = min;  
     }//time_min
    
    void time_hour(void) ///установка часов
    {
      if((PINB&0x40)==0x00)
     {
        _delay_ms(60);
        if((PINB&0x40)==0x00)
       {
          i++;
       }
     }
     if (i>23)
     {
       i=0;    
     }
     hour=i;
     SetHours(hour); 
     digit_3 = hour / 10;
     hour -= digit_3*10; 
     digit_2 = hour;
    }//time_hour
    
    void indikator_init(void)
    {
      //настройка портов ВВ
      DDRD = 0xFF;
      DDRB |= (1<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3);
      PORTB |= (1<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3);
      //Timer0 
      TCCR0 |= (1<<CS01);
      TIMSK |= (1 < код 7 сегментного индикатора
      //функция конвертирования числа в семисегментный код
      uint8_t num_to_8dig(const uint8_t num);
      uint8_t num_to_8dig(const uint8_t num)
      {
         switch (num)
        {
          case 0: 
          return SYM_0;
    	 break;
          case 1:
          return SYM_1;
    	   break;
          case 2:
    	   return SYM_2;
    	   break;
          case 3:
    	   return SYM_3;
    	   break;
          case 4:
    	   return SYM_4;
    	   break;
          case 5:
    	   return SYM_5;
    	   break;
          case 6:
    	   return SYM_6;
    	   break;
          case 7:
    	   return SYM_7;
    	   break;
          case 8:
    	   return SYM_8;
    	   break;
          case 9:
    	   return (SYM_9);
    	   break;
          case 10:
    	   return (SYM_MINUS);
    	   break;
          case 11:
    	   return SYM_GRAD;
    	   break;
          case 12:
    	   return SYM_TOCHKA;
    	   break;
          default:
    	   return SYM_OFF;
    	   break;
      }//switch
    }
    
    //////////////////////////////////////////////////////////////////////
    //uint8_t hour,minut;
    //////////////////////////////////////////////////////////////////////
    ISR(TIMER0_OVF_vect)
    {
     static volatile uint8_t d_cnt;
     //static volatile uint8_t temp;
     switch (d_cnt)
       {
         case 0://отображаем десятки часов
           PORTB &= ~(1<<DIG0);
           PORTB &= ~(1<<DIG1);
           PORTB &= ~(1<<DIG2);
           PORTB &= ~(1<<DIG3);
    	   PORTD = num_to_8dig(digit_3);
    	   PORTB |= (1<<DIG3);
           d_cnt++;
           break;
         case 1: //отображаем единици часов
           PORTB &= ~(1<<DIG0);
           PORTB &= ~(1<<DIG1);
           PORTB &= ~(1<<DIG2);
           PORTB &= ~(1<<DIG3);
    	   PORTD = num_to_8dig(digit_2)+SYM_TOCHKA;
    	   PORTB |= (1<<DIG2);
           d_cnt++;
           break;
         case 2:
           PORTB &= ~(1<<DIG0);
           PORTB &= ~(1<<DIG1);
           PORTB &= ~(1<<DIG2);
           PORTB &= ~(1<<DIG3);
    	   PORTD = num_to_8dig(digit_1);
    	   PORTB |= (1<<DIG1);
           d_cnt++;
           break;
         case 3:
    	   PORTB &= ~(1<<DIG0);
           PORTB &= ~(1<<DIG1);
           PORTB &= ~(1<<DIG2);
           PORTB &= ~(1<<DIG3);
    	   PORTD = num_to_8dig(digit_0);
    	   PORTB |= (1<<DIG0);
           d_cnt = 0;
           break;	  
       }//switch
    }///isr

  4. #include
    #include 
    #include 
    #include 
    #include 
    #include "indikator.h"
    
    uint8_t i,j;
    int main (void)
    { 
      indikator_init();
      sei();
      SetMinutes(37);
      SetHours(10);
      StartTime();
      i=0;
      j=0;
      while(1)
      {
        display();
        _delay_ms(200); 
        while((PINC&0x02)==0x00)
        {
          time_hour();
          time_min();
        }
     }//loop ad infinity
    }//main

  5. Повторюсь что программа не идеальна, но по крайне мере она работает. Я например не знаю как сделать, чтобы точка мигала когда часы идут и горела постоянно, когда идет установка времени. Если кому то нужна более подробная информация можете обратиться ко мне по электронной почте asdfgj@mail.ru

Добавить комментарий

Ваш e-mail не будет опубликован.

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe without commenting