1307Тема часов на микросхеме DS1307 довольно актуальна — это простое, но в то же время интересное устройство. Кроме того, оно может реально пригодиться. Но описывать отдельно микросхему смысла нет, поэтому я решил собрать себе подобное устройство, заодно рассказать о том, какие шишки набил при этом. Сам процесс разработки и сборки буду описывать, по мере прохождения некоторых этапов готовности девайса.

Update 17.10.2015
Вначале это была серия статей, целью которых было рассказать про создание устройства с нуля до состояния готовности, но внезапно у меня появилась аллергия на все что называется «часы», поэтому я слил все в одну статью. Устройство закончено на 99.9%, (осталось закрутить винты), но сделать это ой как не просто 🙂 Как только аллергия пройдет появится окончательная фотка.

Начнем с того, что пока нам ничего не известно про ds1307 кроме того, что с ее помощью делают часы. Поэтому качаем документацию, на эту микросхему и читаем список «вкусностей», которыми она обладает. Итак, из первого абзаца в целом понятно, что она обладает низким энергопотреблением, информация передается по I2C, можно узнать дату и время, 12 и 24 часовой формат, автоматическая подстройка даты. Но самое интересное это схема (TYPICAL OPERATING CIRCUIT).

sch1307

Курим даташит и пытаемся разобраться что к чему. Идем слева направо, CPU — микроконтроллер ( то есть наша atmega), два резистора, написано pull up — значит подтягивающие (можно взять по 10к), кварц на 32768Гц, сама микросхема и батарейка. Выход SQW/OUT может дрыгаться с частотой 1Hz, 4kHz, 8kHz, 32kHz, пока нам это не интересно. Пожалуй, этой информации пока достаточно, хочется уже чего нибудь накодить 🙂

Создаем проект в CodeVision, в разделе I2C находим ds1307 и включаем его в проект. Хорошо бы еще выводить куда нибудь информацию, например на LCD и пара кнопок не помешает.

i2c_setup

Все что нужно это LCD настроить на порт D и три кнопки с подтяжкой на вход. Далее нужно вывести на LCD время, для этого заглянем в мануал CodeVision и возьмем оттуда пример. Оказывается все просто — есть функция устанавливающая время:
rtc_set_time(3,0,0); //установить 03:00:00

И есть функция, позволяющая считать текущее время:
rtc_get_time(&h,&m,&s);

т.е. после вызова данной функции в переменных h, m, s будут находиться часы(h), минуты(m) и секунды(s). Осталось вывести их на экран. Уж это мы умеем делать)
Итоговый код будет выглядеть так:

#include <mega8.h>
#include <stdio.h>
#include <delay.h>
// I2C Bus functions
#asm
   .equ __i2c_port=0x18 ;PORTB
   .equ __sda_bit=0
   .equ __scl_bit=1
#endasm
#include <i2c.h>
 
// DS1307 Real Time Clock functions
#include <ds1307.h>
 
// Alphanumeric LCD Module functions
#asm
   .equ __lcd_port=0x12 ;PORTD
#endasm
#include <lcd.h>
 
char lcd_buf[33];
 
void main(void)
{   
char hour,min,sek;
 
PORTC=0x07;
DDRC=0x00;
 
// I2C Bus initialization
i2c_init();
 
// DS1307 Real Time Clock initialization
rtc_init(0,0,0);
 
// LCD module initialization
lcd_init(16);
rtc_set_time(3,0,0);
 
while (1)
      { 
          rtc_get_time(&hour,&min,&sek);
          lcd_clear();
          lcd_gotoxy(0,0);
          sprintf(lcd_buf,"%2d:%02d:%02d\n",hour,min,sek); 
          lcd_puts(lcd_buf);
          delay_ms(500);
      };
}

Собираем и тестируем в протеусе:
shc_proteus

Схема и прошивка

Продолжим модернизировать нашу прошивку. Начнем со следующей задумки: у DS1307 есть выход SQW/OUT, который может генерировать несколько частот. Если настроить этот выход на 1Гц, и подать этот сигнал на вход внешнего прерывания, то получится, что раз в секунду 1307 будет дергать «за хвост» нашу atmega8. Для меги это будет сигналом к тому, что пора обновлять время. Это позволит не нагружать микроконтроллер постоянным обновлением времени, информация о текущем времени будет обновляться ровно раз в секунду.

Добавим в проект внешнее прерывание по низкому уровню (low level) на ножке Int1 и включим подтяжку. Выход DS1307 настроим на частоту 1Гц. Кстати, читать мануалы полезно, нашел интересную особенность — подтягивающие резисторы на ножках SCL, SDA должны быть 3,3k — 4,7k. Учтем это.

shc_proteus_v2

Получившийся код будет выглядеть так:

interrupt [EXT_INT1] void ext_int1_isr(void)
{
time_flag=1;
}

В прерывании выставляем флаг, разрешающий вывод времени, в основном цикле, если флаг выставлен, то показываем время, если не выставлен ничего не делаем.

if(time_flag==1)
{
rtc_get_time(&hour,&min,&sek);
lcd_gotoxy(0,0);
sprintf(lcd_buf,"%02d:%02d:%02d\n",hour,min,sek);
lcd_puts(lcd_buf);
}

Теперь перейдем к следующему вопросу, на сколько эффективно использовать sprintf? Чтобы не разводить пустых разговоров, приведу 2 куска кода, которые выполняют одно и тоже — выводят информацию о времени на дисплей.

Первый вариант, уже нам известный:

sprintf(lcd_buf,"%02d:%02d:%02d\n",hour,min,sek);
lcd_puts(lcd_buf);

Согласитесь просто в использовании и наглядно. Теперь вариант номер 2:

lcd_putchar(hour/10+0x30);
lcd_putchar(hour%10+0x30);
lcd_putchar(':');
lcd_putchar(min/10+0x30);
lcd_putchar(min%10+0x30);
lcd_putchar(':');
lcd_putchar(sek/10+0x30);
lcd_putchar(sek%10+0x30);

Не очень наглядно, но разобраться можно. Как мы их будем сравнивать? Делается это очень просто — запускаем отладчик AVR STUDIO и смотрим количество тактов затраченных на их выполнение. Итак, «барабанная дробь», результаты… Первый кусок кода выполнялся 16 466 тактов, что равносильно 2 058,25 мкс, при рабочей частоте в 8МГц, для второго куска кода эта цифра составила 12 278 тактов или 1 534,75 мкс. Согласитесь, снизить время выполнения, а значит и разгрузить микроконтроллер на ~25% достаточно весомая причина, чтобы не использовать sprintf. Выкидываем sprintf из нашего проекта, в след за ним можно выкинуть stdio.h и lcd_buf[].

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

while (1)
{
if(time_flag==1)
{
show_time();              //показать информацию о текущем времени
}
};

Объявление самой функции будет выглядеть так:

void show_time()
{
rtc_get_time(&hour,&min,&sek);
lcd_gotoxy(0,0);
lcd_putchar(hour/10+0x30);
lcd_putchar(hour%10+0x30);
lcd_putchar(':');
lcd_putchar(min/10+0x30);
lcd_putchar(min%10+0x30);
lcd_putchar(':');
lcd_putchar(sek/10+0x30);
lcd_putchar(sek%10+0x30);
 
time_flag=0;
}

Теперь в нашу прошивку, нужно добавить вывод даты. Установка даты, производится следующей функцией:

rtc_set_date(6,13,10,13);  //6- день недели, 13 - день, 10 - месяц, 13 - год

Прочитать текущую дату можно аналогично чтению времени:

rtc_get_date(&week_day,&day,&month,&year); //день недели, день,  месяц, год

Вывод даты можно организовать, пока что в основном цикле, рядом со временем, полный исходный код получился такой:

#include <mega8.h>
 
// I2C Bus functions
#asm
   .equ __i2c_port=0x18 ;PORTB
   .equ __sda_bit=0
   .equ __scl_bit=1
#endasm
#include <i2c.h>
 
// DS1307 Real Time Clock functions
#include <ds1307.h>
 
// Alphanumeric LCD functions
#include <alcd.h>
 
char hour=0,min=0,sek=0,day=0,month=0,year=0,week_day=0;
bit time_flag=0;
char menu=0;
 
// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
 time_flag=1;    
}
void show_time()
{
          rtc_get_time(&hour,&min,&sek);
          rtc_get_date(&week_day,&day,&month,&year);
 
          lcd_gotoxy(0,0);
 
          lcd_putchar(hour/10+0x30);
          lcd_putchar(hour%10+0x30);        
          lcd_putchar(':');
          lcd_putchar(min/10+0x30);
          lcd_putchar(min%10+0x30);
          lcd_putchar(':');
          lcd_putchar(sek/10+0x30);
          lcd_putchar(sek%10+0x30);
 
          lcd_gotoxy(0,1);
          lcd_putchar(day/10+0x30);
          lcd_putchar(day%10+0x30);
          lcd_putchar('/');
          lcd_putchar(month/10+0x30);
          lcd_putchar(month%10+0x30);
          lcd_putchar('/');
          lcd_putchar(year/10+0x30);
          lcd_putchar(year%10+0x30);
 
          time_flag=0;
}
 
void main(void)
{   
PORTC=0x0F;
DDRC=0x00;
 
PORTD=0x08;
DDRD=0x00;
 
// I2C Bus initialization
i2c_init();
 
// DS1307 Real Time Clock initialization
// Square wave output on pin SQW/OUT: On
// Square wave frequency: 1Hz
rtc_init(0,1,0);
 
// External Interrupt(s) initialization
// INT0: Off
// INT1: On
// INT1 Mode: Low level
GICR|=0x80;
MCUCR=0x00;
GIFR=0x80;
 
// LCD module initialization
lcd_init(16);
rtc_set_time(12,0,0);
rtc_set_date(6,13,10,13);
 
#asm("sei")
 
while (1)
      { 
          if(time_flag==1)
          {
              show_time();              
          }                  
      };
}

Результат:
shc_proteus_v3

Схема и прошивка:

Перейдем к организации меню. Самый главный вопрос состоял в том, как оно все должно выглядеть, т.е. нужно было сформулировать техническое задание(т.з.).
Мне хотелось, чтобы это были отдельные экраны:
-главный экран;
-экран настройки времени;
-экран настройки даты;
-экран настройки будильника.

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

То что размер кода будет достаточно большой было понятно изначально. При этом нужно было его разбить на логически связанные части. С частями все понятно — обработка одного экрана одна часть кода. Поэтому начал с того, что основной цикл разбил на четыре части, переключения между которыми производится оператором switch. Внутрь засунул функции — пустышки. Кнопки 0(вверх) и 3(вниз) порта C позволяют изменить переменную menu. Таким образом мы скачем между менюшками. Но пока такая прошивка еще работать не могла, ибо функции еще не определены.

while (1)
{
switch(menu)
{
 case 0:
 show_time();
 break;
 
 case 1:
 set_time();
 break;
 
 case 2:
 set_date();
 break;
 
 case 3:
 set_alarm();
 break;
}
 
};

Следующий шаг определение этих функций, изначально я нарисовал статичные названия, вроде lcd_puts(«Set time»);  функции получились, такими.

void set_alarm()
{
////////просмотр настроек будильника
lcd_gotoxy(0,0);
lcd_puts("Set alarm");
}
void set_time()
{
////////просмотр настроек времени
lcd_gotoxy(0,0);
lcd_puts("Set time");
 
}

Теперь это уже была рабочая прошивка, в которой можно было переключаться между менюшками и смотреть статичные надписи. Настало время оживить эти надписи. С главным экраном проблем не было, вывод времени/даты аналогичен предыдущему уроку.

Встал следующий вопрос: как организовать сам процесс настройки? Поразмыслив, мне показалась интересной следующая идея: переходим кнопками вверх/вниз в интересующее нас меню, нажимаем вправо, появляется курсор, говорящий нам о том, что идет процесс настройки. Кнопками вверх/вниз мы изменяем величину, влево/вправо скачем курсором между настраиваемыми параметрами. Когда курсор находится под последним параметром, повторное нажатие кнопки вправо позволяет выйти из настройки. Курсор при этом скрывается, что говорит о том что мы вышли из настройки и можем снова переключаться между менюшками.

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

void set_alarm() //Функция обработки будильника
{
//режим отображения меню настроек будильника
if(sub_alarm==0)
{
  if(PINC.0==0)  //кнопка вверх - смена экрана меню
  {
  menu=0;
  .....
  }
}
//подменю настройки будильника
if(sub_alarm==1)
{
  if(PINC.0==0)  //кнопка вверх - увеличить величину
  {
  a_hour++;
  ....
  }
}

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

   if(PINC.0==0)                 //кнопка вверх
   {                             
     if(subProgram==1)           //subProgram=1 - изменяем часы
     {                           
       a_hour++;                 
       ...         
     }                           
     if(subProgram==2)           //subProgram=2 - изменяем минуты
     {                           
       a_min++;                 
      ...         
     }                           
     if(subProgram==3)           //subProgram=3 изменяем флаг будильника
     {                           
      ...
     }                          
   }

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

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

shc_proteus_v4

Пример взят из примеров в папке CodeVision\examples\lcd char.

typedef unsigned char byte; //переопределяем тип
 
flash byte char_table[8]={ //рисуем свой символ
0b10000000,
0b10000100,
0b10001110,
0b10001110,
0b10001110,
0b10011111,
0b10100100,
0b11000000};
 
// function used to define user characters
void define_char(byte flash *pc,byte char_code)
{
byte i,address;
address=(char_code<<3)|0x40;
for (i=0; i<8; i++) lcd_write_byte(address++,*pc++);
}
 
void main(void)
{   
byte i,address;
lcd_init(16);
define_char(char_table,0); //Грузим символ в лсд
 
while(1)
{
lcd_putchar(0); //выводим символ на дисплей
}

Рисовать можно символ 5х7, единичка — пиксел закрашен, ноль — не закрашен. Получился символ колокольчика.

Прошивка

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

Начнем с печатной платы, для этого требуется программа, которая позволяет рисовать печатки. Существует множество подобных программ: P-cad, Altium, Sprint layout… Мне нравится Альтиум, только потому, что для него куча готовых библиотек с элементами, ибо тратить время на набивку собственной библиотеки элементов, на мой взгляд не дело. Общий смысл всех подобных программ одинаков — сначала рисуется электрическая схема.

scha_1307

Далее данные о связях элементов передаются в трассировщик.

exchange_1307

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

pcb_ds1307

Остается только удобно расположить элементы и соединить их проводниками.

pcbready_ds1307

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

print_nout

Я долгое время печатал на бумаге с плотностью 180 г/м2 и меня устраивало, но недавно бумага закончилась и в магазине оказалась только 220 г/м2, думаю ладно попробую. В результате я отодрать бумагу так и не смог, ни от платы ни от утюга :D. Много раз слышал, что можно брать бумагу от обычных глянцевых журналов. Думаю ладно попробую, взял первый попавшийся журнал с рекламой мебели и результат меня впечатлил. Так что рекомендую не тратить деньги, а печатать на халявных журналах.

print_foto

Основой служит фольгированный текстолит, который по сути и есть кусок текстолита, с одной стороны покрытый медью.

fr4-foto

Отрезаем ножницами по металлу чуть больше размера будущей печатки и зашкуриваем, чтоб блестело. Полученную плату протираем ацетоном и ни в коем случае медный слой не лапаем руками.

shkurka

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

jurnalfoto

Теперь потребуется приготовить раствор хлорного железа и бросить туда плату. Процесс травления может быть довольно длительным в зависимости от размеров платы. Существует множество всяких шамаств с раствором, для ускорения этого процесса. Меня впечатлило использование бурбулятора, того самого который называется компрессором и пускает пузырьки в аквариумах, с ним процесс травления небольшой платы занимает около 15мин. Чтобы раствор не брызгал разрезал пластиковую бутылку, горлышко стало крышкой.

ferum_cl

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

cucl

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

last_pp

В общем обленился в край, все сделал, осталось прикрутить винты 🙂

105 комментариев: Часы на DS1307 от идеи к реализации

  • Privet pomagi menya!

    avr studio 6.1 prog

    #include
    #include

    // Функция инициализация шины TWI
    void I2CInit(void)
    {
    // настройка TWI модуля
    TWBR = 2;
    TWSR = (1 << TWPS1)|(1 << TWPS0); // Предделитель на 64
    TWCR |= (1 << TWEN); // Включение модуля TWI
    }

    void I2CStart(void)
    {
    // Передача условия СТАРТ
    TWCR = (1 << TWINT)|(1 << TWEN)|(1 << TWSTA);
    // Ожидание установки флага TWINT
    while(!(TWCR & (1 << TWINT)));
    }

    void I2CStop(void)
    {
    TWCR = (1 << TWINT)|(1 << TWEN)|(1 << TWSTO); // Передача условия СТОП
    while(TWCR & (1 << TWSTO)); // Ожидание завершения передачи условия СТОП
    }

    // Функция записи данных по шине
    uint8_t I2CWriteByte(uint8_t data)
    {
    TWDR = data; // Загрузка данных в TWDR
    TWCR = (1 << TWEN)|(1 << TWINT); // Сброс флага TWINT для начала передачи данных
    while(!(TWCR & (1 << TWINT))); // Ожидание завершения передачи
    // Проверка статуса
    if((TWSR & 0xF8) == 0x18 || (TWSR & 0xF8) == 0x28 || (TWSR & 0xF8) == 0x40)
    {
    // Если адрес DS1307, биты R/W и данные переданы
    // и получено подтверждение
    return 1;
    }
    else
    return 0; // ОШИБКА
    }

    // Функция чтения данных по шине
    uint8_t I2CReadByte(uint8_t *data,uint8_t ack)
    {
    if(ack) // Устанавливаем подтверждение
    {
    // Возвращаем подтверждение после приема
    TWCR |= (1 << TWEA);
    }
    else
    {
    // Возвращаем неподтверждение после приема
    // Ведомое устройство не получает больше данных
    // обычно используется для распознования последнего байта
    TWCR &= ~(1 << TWEA);
    }
    // Разрешение приема данных после сброса TWINT
    TWCR |= (1 << TWINT);
    while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
    // Проверка статуса
    if((TWSR & 0xF8) == 0x58 || (TWSR & 0xF8) == 0x50)
    {
    // Прием данных и возвращение подтверждения
    // или
    // Прием данных и возвращение неподтверждения
    *data = TWDR; // Читаем данные
    return 1;
    }
    else
    return 0; // Ошибка
    }
    // Функция чтения данных из DS1307
    uint8_t DS1307Read(uint8_t address,uint8_t *data)
    {
    uint8_t res; // Результат
    I2CStart(); // СТАРТ
    res = I2CWriteByte(0b11010000); // адрес DS1307 + бит W
    if(!res) return 0; // ОШИБКА
    // Передача адреса необходимого регистра
    res = I2CWriteByte(address);
    if(!res) return 0; // ОШИБКА
    I2CStart(); // Повторный СТАРТ
    res = I2CWriteByte(0b11010001); // адрес DS1307 + бит R
    if(!res) return 0; // ОШИБКА
    // Чтение данных с неподтверждением
    res = I2CReadByte(data,0);
    if(!res) return 0; // ОШИБКА
    I2CStop(); // СТОП
    return 1;
    }
    // Функция записи данных в DS1307
    uint8_t DS1307Write(uint8_t address,uint8_t data)
    {
    uint8_t res; // Результат
    I2CStart(); // СТАРТ
    res = I2CWriteByte(0b11010000); // адрес DS1307 + бит W
    if(!res) return 0; // ОШИБКА
    // Передача адреса необходимого регистра
    res = I2CWriteByte(address);
    if(!res) return 0; // ОШИБКА
    res = I2CWriteByte(data); // Запись данных
    if(!res) return 0; // ОШИБКА
    I2CStop(); // СТОП
    return 1;
    }
    // Функции работы с LCD
    #define RS PD0
    #define EN PD2
    // Функция передачи команды
    void lcd_com(unsigned char p)
    {
    PORTD &= ~(1 << RS); // RS = 0 (запись команд)
    PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
    PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
    _delay_us(100);
    PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
    _delay_us(100);
    PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
    PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
    _delay_us(100);
    PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
    _delay_us(100);
    }
    // Функция передачи данных
    void lcd_data(unsigned char p)
    {
    PORTD |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN — 1 (начало записи команды в LCD)
    PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
    _delay_us(100);
    PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
    _delay_us(100);
    PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
    PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
    _delay_us(100);
    PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
    _delay_us(100);
    }
    // Функция вывода строки на LCD
    void lcd_string(unsigned char command, char *string)
    {
    lcd_com(0x0C);
    lcd_com(command);
    while(*string != '')
    { lcd_data(*string);
    string++;
    }
    }
    // Функция вывода переменной
    void lcd_num_to_str(unsigned int value, unsigned char nDigit)
    {
    switch(nDigit)
    {
    case 4: lcd_data((value/1000)+'0');
    case 3: lcd_data(((value/100)%10)+'0');
    case 2: lcd_data(((value/10)%10)+'0');
    case 1: lcd_data((value%10)+'0');
    }
    }
    // Функция инициализации LCD
    void lcd_init(void)
    {
    PORTD = 0x00;
    DDRD = 0xFF;

    _delay_ms(50); // Ожидание готовности ЖК-модуля

    // Конфигурирование четырехразрядного режима
    PORTD |= (1 << PD5);
    PORTD &= ~(1 << PD4);

    // Активизация четырехразрядного режима
    PORTD |= (1 << EN);
    PORTD &= ~(1 << EN);
    _delay_ms(5);

    lcd_com(0x28); // шина 4 бит, LCD — 2 строки
    lcd_com(0x08); // полное выключение дисплея
    lcd_com(0x01); // очистка дисплея
    _delay_us(100);
    lcd_com(0x06); // сдвиг курсора вправо
    lcd_com(0x0C); // включение дисплея, курсор не видим
    }

    int main(void)
    {
    _delay_ms(100);

    DDRC = 0x00;
    PORTC = 0xFF;

    lcd_init(); // Инициализация LCD
    I2CInit(); // Инициализация шины I2C

    // Запускаем ход часов
    uint8_t temp;
    DS1307Read(0x00,&temp);
    temp &= ~(1 <> 4)*10)+(temp & 0x0F);
    DS1307Read(0x01,&temp); // Чтение регистра минут
    minute = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F);
    DS1307Read(0x02,&temp); // Чтение регистра часов
    hour = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F);

    //lcd_string(0x81, ««acГ Ѕa DS1307″);
    lcd_string(0xC4, » : : «);
    lcd_com(0xC4);
    lcd_num_to_str(hour, 2); // Выводим на экран часы
    lcd_com(0xC7);
    lcd_num_to_str(minute, 2); // Выводим на экран минуты
    lcd_com(0xCA);
    lcd_num_to_str(second, 2); // Выводим на экран секунды
    //*******************************
    while(1)
    {

    switch (kun)
    {
    case dushanba : {lcd_string(0x81,»Dushanba «); break;}
    case seshanba : {lcd_string(0x81,»Seshanba «); break;}
    case chorshanba :{lcd_string(0x81, «Chorshanba»); break;}
    case payshanba : {lcd_string(0x81,»Payshanba «); break;}
    case juma : {lcd_string(0x81,»Juma «); break;}
    case shanba : {lcd_string(0x81,»shanba «); break;}
    case yakshanba : {lcd_string(0x81,»Yakshanba «); break;}
    }
    if (kun==7)
    kun=0;
    if((PINC & (1 << PC2))==0)
    {
    kun++;
    _delay_ms(300);
    }
    }
    //*******************************
    if((PINC & (1 << PC0))==0) // Если нажата кнопка
    {
    while((PINC & (1 < 23) hour = 0;
    // Преобразуем из двоичной системы в BCD и записываем в DS1307
    uint8_t temp;
    temp = ((hour/10) << 4)|(hour%10);
    DS1307Write(0x02, temp);
    _delay_ms(100);
    }

    if((PINC & (1 << PC1))==0) // Если нажата кнопка
    {
    while((PINC & (1 < 59) minute = 0;
    // Преобразуем из двоичной системы в BCD и записываем в DS1307
    uint8_t temp;
    temp = ((minute/10) << 4)|(minute%10);
    DS1307Write(0x01, temp);
    _delay_ms(100);
    }
    }
    }

    eto izminit c ne rabot?
    где ляп я сделал?

  • yo sazdat skolniy zvanol 😛

  • izvinit «shkolniy zvanok avtomatik»

  • hurmat, я чужие исходники не проверяю, если есть конкретные вопросы — спрашивайте.

  • Привет Админ 😉 . Когда мы будем программировать на Atmel studio ??? Реально каша уже с этим КВАВР… Я около года назад, зашел к тебе и по этим урокам начал учиться. Давно уже перешел на Атмелстудию. Советую начинать развиваться… Если ты еще не зарылся с головой в СТМ… 😀

  • уже полгода как перешел на атмел 🙂
    http://avr-start.ru/?p=2230
    http://avr-start.ru/?p=2261
    правда, всеравно пользуюсь цавр как генератором

  • 😀 О извинтиляюсь. Просто не очень шарился по сайту 😳 …

  • admin, приветствую, когда запускаю схему в Proteus после изменения даты с ней происходит какая-то фигня, сбивается сначала год, потом сама дата в странные символы превращается.
    Так же не совсем понял, как здесь срабатывает будильник?
    Вот установили мы его, а куда сигнал будет подаваться в момент достижения времени?

  • Сейчас еще раз потестил, не заметил такого эффекта, когда будильник срабатывает, дрыгается PC4.

  • как можно с Вами связаться? покажу, какой глюк возникает

  • Предлагаю продолжить проект и добавить в него модуль барометра ВМР-180.

  • Я, автоматического планирования вызова может быть сделано через mikrakantroller?

  • Повторил сей девайс в железе, всё хорошо НО, есть одно НО. При каждом перезапуске программы время и дата сбрасываются в первоначальное состояние. В этом, я думаю, виновны вот эти строчки:
    rtc_set_time(3,0,0);
    rtc_set_date(6,13,10,13);
    Попробовал их убрать и скомпеллировать программу, но компилятор начал ругаться на библиотеку LCD.
    Какие тут применены библиотеки? и не мог бы ты выложить их тут?

  • Логично, а как иначе будут переменные инициализироваться? Чтобы сохранять время нужно либо переменные в энергонезависимую память сохранять, либо цеплять батарейку, чтобы ds1307 не вырубалась. Все библиотеки стандартные.

  • Батарейка на DS1307 обязательно установлена, ведь весь интерес в этих часах в том, что после пропадания питания чтобы время не сбивалось. А какой версией студии пользуешься?

  • Батарейка как раз и нужна на случаи пропадания питания.

  • Вот именно, а на этом девайсе при при появлении питания время и дата сбрасываются на исходные: 3.00, 13.10.2013 г.

  • А как вы хотели, пропало питание, а часы идут сами по себе без электричества. Или схема должна работать без энергии? По факту, если пропало напряжение, то даже если вы запомните последние данные, они все равно будут не верными, будут отставать. Варианты: 1. Внешняя синхронизация — комп, gps и т.п. 2. Как я уже несколько раз сказал использовать, отдельный батареечный вход ds1307, не путать с общим питанием.

  • Только сейчас заметил. Куда упразднили батарейку 3V из схемы? Она как раз и позволяет часам ходить как ни в чем не бывало даже без питания 5V. И в программе при старте соответственно нужно убрать установку времени на 3:00 при каждом старте. И еще, когда я осваивал DS1307 обнаружил у нее в регистрах бит, который останавливает/запускает счет времени. Когда он не установлен (или не сброшен, не помню уже) то часы тупо стоят на одном месте, и при этом данные считываются/записываются нормально.

  • Можно использовать батарейку, никто не запрещает, я посчитал ее излишней. По поводу старта не понял, в любом случае когда запускается мк часы будут сброшены, поэтому без разницы 3 часа это будет или нет, не принципиально. Во внутренности не лазил. Хотя возможно залезу, ибо нужно покрутить i2c c stm32

  • Нет, если при старте программы не делать set_time то время будет идти автономно, никуда не сбиваясь. Set_time нужно использовать ТОЛЬКО при необходимости изменить время. При каждом старте программы делать этого не нужно. Сами часы работают от той самой батарейки, не зависимо от того, есть 5 вольт или нет. Проверил все это сам, когда делал часы на PIC16F628A и программу тогда делал на асме без всяких библиотек, Так что разобрался с ней досконально )))

  • Я батарейку не использовал, поэтому и говорю что в моем случае разницы нет. Проект один из первых, много времени прошло с тех пор, сейчас бы многое сделал по другому.

  • Ок, разобрались ))

  • Здраствуйте . Можно добавить немножко на твоём схеме ?Мы хотели открыть , но это не открывается. Это показывает такую error #include header наверное этот fiel нету.
    Если увас есть этот file можно нам дать?

  • Hello . May i add something on that sheme , sheme that you did to me ?
    We wanted to open it , but it doesnt open . I think this has a problem. Its cannot open without #include header this file. Can you send me this file

  • You can open sheme only in Proteus 7.8 or higher. You can add anything what you want. Source code can be opened in CodevisionAVR. Sometimes occured errors with different version CAVR. Use libs from my previous comment and put in CAVR folders «lib» «inc»

  • thank you very much 😀

  • hey i have just done this sheme for real life. Its working perfectly. I used 3voltage battery for RTC. but it didnt save data . how can i decide it? please help me

  • Try to delete this strings
    rtc_set_time(3,0,0); //del
    rtc_set_date(6,13,10,13); //del

  • wow its working thank you man

  • Подскажите где в Altium Designer вы взяли компонент Symbol_LCD ?

  • Сам нарисовал

  • А поделиться нарисованной вами библиотекой на Symbol_LCD можете?

  • Можете взять тот самый проект и выдрать оттуда http://vk.com/doc-61732346_437005487 хотя не знаю поможет ли это, ибо пины просто выведены на пятачки

  • Раскажите подробней о строке lcd_putchar(hour/10+0x30);
    Почему имено такая запись? Допустим в hour хранится 13. Получается 13/10+48=49.3

  • Уважаемы администратор, возникла сложность с установкой соответствия индекса дня недели с его названием.
    Допустим мы установили дату rtc_set_date(7,7,2,16) — седьмой день недели, то есть воскресенье, но по переходу на следующий день, по логике вещей индекс должен стать — 1, но он становится — 0, и если затем считать от нуля то мы получим 8 дней в неделе…Ни как не могу победить этот момент. А вообще задача следующая нужно интерпретировать индекс дня недели (число), в название дня недели: 1- понедельник, 2 — вторник, 3 — среда…
    Помогите, или направьте на путь истинный, буду очень признателен.

  • switch и define/enum вам в помощь

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Последние комментарии
  • Загрузка...
Счетчик
Яндекс.Метрика