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 от идеи к реализации

  • «switch и define/enum вам в помощь», как интерпретировать это понятно, но проблема то не в этом, проблема в том, проблема в том что DS1307 выдает 8 дней в неделе, то есть индексирование дней в неделе начинается не с единиц а с нуля, как с этим быть?

  • Возможно проблема в программе которая симулирует работу DS1307, я не могу этого проверить так как нет самого DS1307… 🙄

  • лень проверять, но микросхемой все впорядке. ну и пускай нумерация с нуля, прибавляйте к результату 1, получите так как хотите

  • admin, а я пошёл ещё дальше при уменьшении времени преобразовани часов минут и секунд!

    Я открыл библиотеку ds1307.lib (благо она открытая), и посмотрел, как оно всё реализованно. Оказывается, полученные из ds1307 значения, через функцию bcd2bin();, преобразовываются в шестнадцатеричный код, и потом отдаются пользователю. Я просто скопировал эту функцию (дабы не ломать фирменную библиотеку), переименовал её в rtc_get_bcd_time();, и выкинул из неё преобразование из BCD в HEX. А дальше я просто при выводе на индикаторы делаю Hour>>4, Hour & 0x0F. Таким образом я беру сначала старшую тетраду (полубайт), потом младшую, и т. д. для минут и секунд. В Вашем случае нужно ещё прибавлять 0х30 к каждому числу.

    В итоге, на ATmega168 при 16МГц, Ваш вариант (с использованием деления) обрабатывается 0,22мкс, а мой 0,12мкс. Практически в два раза быстрее. Скорость работы функции приёма времени падает на 0,1мкс, с 1,4мкс до 1,3мкс.

  • интересная информация, спасибо

  • Подскажите пожалуйста, взял за основу прошивку номер пустую без функционала кнопок. Не запускаеются часы без rtc_set_time(12,0,0);
    rtc_set_date(6,13,10,13);

    А с ними батарейка не пашет , ибо при включении сбрасываются часы, чего не хотелось бы.
    Нарыл в инете функцию void rtc_write(unsigned char address,unsigned char data); — запись данных в микросхему часов, например выбрать 12 или 24 часовой формат времени, выбрать частоту импульсов на выходе OUT и т.д.;
    В ковижне она void rtc_write(unsigned char address, unsigned char data)
    this function stores the byte data in the PCF8563 register at address.
    Непонятно для чего)

  • Тут уже писали про то, что у микросхемы есть бит, который отвечает за ход часов. можно его чекать и запускать при старте без настройки. т.е. нужно открыть даташит на ds1307, найти регистр и записать туда этот бит через i2c.

  • Простите за столь нескромный вопрос,i2c визард сам генерит порты,twi в визарде выбор только мастер или слейв без настройки портов,значит ли это что порты под twi регламентированы под у каждого микроконтроллера?и их нельзя изменить?простите если проглядел что в даташите…

  • А!!! все увидел 27-28 пин у атмеги.. ппц я тупарь) значит ли это что тви аппаратный?)или как-то так. (потому что нельзя поменять порты).

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

  • у меня вопрос lcd_putchar а в Atmel Studio 7 как она называется

  • Иван, никак. В прямом смысле этого слова. As не предоставляет библиотеки для работы с модулями и не должна. Это забота пользователя, где он возьмет эти либы. Codevision в этом плане исключение из правил.

  • подскажите пожалуйста что такое
    _lcd_write_data(0xe); //показать курсор


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

  • А чего не понятного. На низком уровне дисплей умеет принимать команды и данные, тут вы отправляете команду показать курсор. Команда взята прямиком из юзер мануала 4.11 LCD Functions, так что не обманывайте что ее там нет.

  • У меня установлена CodeVisionAVR v.2.04.4a Advanced, и в ней почему-то в разделе I2C нет ds1307.Подскажите пожалуйста, что надо сделать, чтобы DS1307 появилась там? (Сильно не ругайте, я только еще начинающий).

  • искать версию посвежее

  • как сделать меню чтобы уменьшать значение a b c .в протеусе любой вариант работает.А в железе не работает ? Помогите пожайлуста?
    /*
    * Bobr_zashtiya.c
    *
    * Created: 15.03.2017 5:43:50
    * Author : User
    */

    #define F_CPU 8000000UL//настройка мега8а на 1Мгц
    #include //библиотека ввда и вывода информации
    #include //библиотека задержек
    #include
    #include «lcd.h»//библиотека для ЛСД
    #include
    #include

    #define btn_plus (PINC&1<<1)
    #define btn_minus (PINC&1<<2)
    volatile unsigned char menu=0;
    volatile unsigned char menu_x=0;

    char flag1=0;
    uint16_t a;
    uint16_t b;
    uint16_t c;

    int main(void)
    {
    char buf1[10];
    char buf2[10];
    char buf3[10];
    DDRC|=0x00;//настройка порта D на вход "0" и на выход "1"
    PORTC|=0xFF;

    //DDRC|=(0<<0)|(0<<1)|(0<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7);//настройка порта D на вход "0" и на выход "1"
    //PORTC|=(1<<0)|(1<<1)|(1<<2)|(0<<3)|(0<<4)|(0<<5)|(0<<6)|(0<<7);
    DDRB|=0xFF;
    PORTB|=0x00;
    DDRD|=(1<<0)|(0<<1)|(0<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(0<<7);//настройка порта D на вход "0" и на выход "1"
    PORTD|=(0<<0)|(1<<2)|(0<<3)|(0<<4)|(1<<5)|(0<<6)|(1<<7);//уст
    OCR0=200;//когда пдключаем вход OCR2 счёт идёт на этот вход
    TCCR1B=(1<<CS12)|(0<<CS11)|(1<<CS10);
    TCCR1A=0;
    TCNT1H=0;
    TCNT1L=0;
    OCR1A=250;
    TCCR0=(1<<WGM01);
    TCNT0=0;//счетный регистр
    TIMSK|=(1<<OCIE0)|(1<<OCIE1A)|(0<<OCIE1B)|(1<<OCIE2);
    TCCR2|=(1<<CS22)|(0<<CS21)|(1<<CS20);
    OCR2=254;
    TCNT2=0;
    //регистр маски прерываний он уст 1 прерывание по сравнению
    TCCR0=(1<<CS02) | (0<<CS01) | (1<<CS00);//регистр управления 0 и2 биты установлены в лог 1 f/1023 частота работы таймера f-частота работы AVR
    TIFR=(1<<OCF0)|(1<<OCF1A)|(1<<OCF2);//флаг прерывания таймера по сравнению

    //главный в моём случае регистр управления и счётный регистр хотя эту задержку можно написать в прерывании в сравнении

    //настройка АЦП

    lcd_init(LCD_DISP_ON);
    lcd_clrscr();
    /* Replace with your application code */
    //asm("sei");
    while (1)
    {

    if (!(PINC&1<<0))
    {
    flag1=1;
    }
    if ((flag1==1)&&(PINC&1<<0))
    {
    _delay_ms(20);
    menu++;flag1=0;
    menu_x++;
    }
    if (menu==0)
    {
    lcd_clrscr();
    lcd_puts("AVR");
    _delay_ms(50);
    }

    if (menu==1)
    {

    lcd_clrscr();
    lcd_puts("menu;a=");
    lcd_gotoxy(8,0);
    itoa(a,buf1,10);
    lcd_puts(buf1);

    _delay_ms(50);

    }
    if (menu==2)
    {

    lcd_clrscr();
    lcd_puts("menu;b=");
    lcd_gotoxy(8,0);
    itoa(b,buf2,10);
    lcd_puts(buf2);
    _delay_ms(50);

    }

    if (menu==3)
    {

    lcd_clrscr();
    lcd_puts("menu;c=");
    lcd_gotoxy(8,0);
    itoa(c,buf3,10);
    lcd_puts(buf3);
    _delay_ms(50);

    }
    if ((menu==4)&&(menu_x==4))
    {
    menu=0;menu_x=0;
    }
    if (menu==1)
    {

    if (btn_plus==0)
    {
    if (a==1023)
    {
    a=0;
    }
    a++;
    }

    }
    if (menu==2)
    {

    if (btn_plus==0)
    {
    if (b==1023)
    {
    b=0;
    }
    b++;
    }

    }
    if (menu==3)
    {

    if (btn_plus==0)
    {
    if (c==1023)
    {
    c=0;
    }
    c++;
    }

    }
    }

    }

  • Не работает скорее всего из за дребезга контактов, добавьте задержку после проверки нажатия кнопки или проверку что кнопка отжата

  • как мне запомнить значения в память постоянную в железе не получается

  • в AS7 там есть библиотеки вот проект
    /*
    * AVR_MENU.c
    *
    * Created: 18.03.2017 5:30:25
    * Author : User
    */
    #define F_CPU 8000000UL
    #include
    #include
    #include
    #include «lcd.h»
    #include
    #include
    #include
    //#include
    #define btn_minus (PINC&1<<7)
    #define btn_plus (PINC&1<<1)
    volatile unsigned int danie;
    volatile unsigned char menu=0;
    char flag1=0;
    //unsigned int a
    // ;
    uint16_t a EEMEM=0;

    uint16_t b EEMEM=50;
    uint16_t c EEMEM=50;
    //uint16_t m EEMEM;

    //char buf1[10];
    //char buf2[10];
    //char buf3[10];
    //void eeprom_write_word (uint16_t *__p, uint16_t __value);

    int main(void)
    {
    danie= eeprom_read_word(& a);
    danie= eeprom_read_word(& b);
    danie= eeprom_read_word(& c);
    danie++;
    eeprom_write_word (& a,danie);
    eeprom_write_word (& b,danie);
    eeprom_write_word (& c,danie);
    eeprom_write_word (& c,danie);

    char buf1[10];

    char buf2[10];
    char buf3[10];
    DDRB=0xFF;
    PORTB=0x00;
    DDRC=0x00;
    PORTC=0xFF;
    lcd_init(LCD_DISP_ON);
    lcd_clrscr();
    /* Replace with your application code */
    asm("sei");
    while (1)
    {
    if (!(PINC&1<<0))
    {
    flag1=1;
    }
    if ((flag1==1)&&(PINC&1<<0))
    {
    _delay_ms(20);
    menu++;flag1=0;
    }
    if (menu==1)
    {
    asm("cli");

    if (btn_plus==0)
    {
    if (a==1023)
    {
    a=0;
    }
    a++;
    }
    if (btn_minus==0)
    {
    if (a==0)
    {
    a=1023;
    }
    a—;
    }
    //eeprom_write_word (& a,danie);

    asm("sei");
    }
    if (menu==2)
    {
    asm("cli");
    if (btn_plus==0)
    {
    if (b==1023)
    {
    b=0;
    }
    b++;
    }
    if (btn_minus==0)
    {
    if (b==0)
    {
    b=1023;
    }
    b—;
    }

    asm("sei");
    }
    if (menu==3)
    {
    asm("cli");
    //
    if (btn_plus==0)
    {
    if (c==1023)
    {
    c=0;
    }
    c++;
    }
    if (btn_minus==0)
    {
    if (c==0)
    {
    c=1023;
    }
    c—;
    }

    asm("sei");

    }

    if(menu==0)
    {
    // asm("cli");

    lcd_clrscr();
    lcd_puts("AVR");
    _delay_ms(50);
    // asm("sei");

    }
    if (menu==1)
    {
    asm("cli");

    lcd_clrscr();
    //sprintf(buf1,"a=%2d",a);
    //lcd_puts(buf1);

    lcd_puts("menu;a=");
    lcd_gotoxy(8,0);
    itoa(a,buf1,10);
    lcd_puts(buf1);
    _delay_ms(20);

    asm("sei");

    }
    if (menu==2)
    {
    asm("cli");

    lcd_clrscr();
    //sprintf(buf2,"b=%2d",b);

    lcd_puts("menu;b=");
    lcd_gotoxy(8,0);
    itoa(b,buf2,10);
    lcd_puts(buf2);
    _delay_ms(20);
    asm("sei");

    }
    if (menu==3)
    {
    asm("cli");

    lcd_clrscr();
    //sprintf(buf3,"c=%2d",c);

    lcd_puts("menu;c=");
    lcd_gotoxy(8,0);
    itoa(c,buf3,10);
    lcd_puts(buf3);
    _delay_ms(20);
    asm("sei");

    }
    if (menu==4)
    {
    menu=0;
    }
    }
    }

  • вы при объявлении переменных присваиваете им значения, поэтому каждый раз при включении они инициализируются этими значениями

  • всё равно нераотает

  • неработает

  • /* я извеняюсь не та а эта
    * AVR_MENU.c
    *
    * Created: 18.03.2017 5:30:25
    * Author : User
    */
    #define F_CPU 8000000UL
    #include
    #include
    #include
    #include «lcd.h»
    #include
    #include
    #include
    //#include
    #define btn_minus (PINC&1<<7)
    #define btn_plus (PINC&1<<1)
    volatile unsigned int danie;
    volatile unsigned char menu=0;
    volatile unsigned char pauza=0;

    char flag1=0;
    //unsigned int a
    // ;
    uint16_t a EEMEM ;

    uint16_t b EEMEM;
    uint16_t c EEMEM;

    uint16_t m; //EEMEM;

    //char buf1[10];
    //char buf2[10];
    //char buf3[10];
    //void eeprom_write_word (uint16_t *__p, uint16_t __value);
    ISR(TIMER0_COMP_vect)
    {
    TCNT0=0;
    pauza++;
    }

    int main(void)
    {
    danie= eeprom_read_word(& a);
    danie= eeprom_read_word(& b);
    danie= eeprom_read_word(& c);
    danie++;
    eeprom_write_word (& a,danie);
    eeprom_write_word (& b,danie);
    eeprom_write_word (& c,danie);
    //eeprom_write_word (& c,danie);

    char buf1[10];

    char buf2[10];
    char buf3[10];
    DDRB=0xFF;
    PORTB=0x00;
    DDRC=0x00;
    PORTC=0xFF;
    TCCR0=(1<<CS02) | (0<<CS01) | (1<<CS00);
    TIMSK|=(1<<OCIE0)|(1<<OCIE1A)|(0<<OCIE1B)|(1<<OCIE2);
    TIFR=(1<<OCF0)|(1<<OCF1B);
    OCR0=250;
    TCNT0=0;
    lcd_init(LCD_DISP_ON);
    lcd_clrscr();
    /* Replace with your application code */
    asm("sei");
    while (1)
    {
    if (!(PINC&1<<0))
    {
    flag1=1;
    }
    if ((flag1==1)&&(PINC&1<5)
    {
    pauza=0;
    if (a==1023)
    {
    a=0;
    }
    a++;
    }
    }
    if (btn_minus==0)
    {
    if (pauza>5)
    {
    pauza=0;

    if (a==0)
    {
    a=1023;
    }
    a—;
    }
    }
    //eeprom_write_word (& a,danie);

    asm(«sei»);
    }
    if (menu==2)
    {
    asm(«cli»);
    if (btn_plus==0)
    {
    if (pauza>5)
    {
    pauza=0;

    if (b==1023)
    {
    b=0;
    }
    b++;
    }
    }
    if (btn_minus==0)
    {
    if (pauza>5)
    {
    pauza=0;

    if (b==0)
    {
    b=1023;
    }
    b—;
    }
    }

    asm(«sei»);
    }
    if (menu==3)
    {
    asm(«cli»);
    //
    if (btn_plus==0)
    {
    if (pauza>5)
    {
    pauza=0;

    if (c==1023)
    {
    c=0;
    }
    c++;
    }
    }
    if (btn_minus==0)
    {
    if (pauza>5)
    {
    pauza=0;

    if (c==0)
    {
    c=1023;
    }
    c—;
    }
    }

    asm(«sei»);

    }

    if(menu==0)
    {
    // asm(«cli»);

    lcd_clrscr();
    lcd_puts(«AVR»);
    _delay_ms(50);
    // asm(«sei»);

    }
    if (menu==1)
    {
    asm(«cli»);

    lcd_clrscr();
    //sprintf(buf1,»a=%2d»,a);
    //lcd_puts(buf1);

    lcd_puts(«menu;a=»);
    lcd_gotoxy(8,0);
    itoa(a,buf1,10);
    lcd_puts(buf1);
    _delay_ms(50);

    asm(«sei»);

    }
    if (menu==2)
    {
    asm(«cli»);

    lcd_clrscr();
    //sprintf(buf2,»b=%2d»,b);

    lcd_puts(«menu;b=»);
    lcd_gotoxy(8,0);
    itoa(b,buf2,10);
    lcd_puts(buf2);
    _delay_ms(50);
    asm(«sei»);

    }
    if (menu==3)
    {
    asm(«cli»);

    lcd_clrscr();
    //sprintf(buf3,»c=%2d»,c);

    lcd_puts(«menu;c=»);
    lcd_gotoxy(8,0);
    itoa(c,buf3,10);
    lcd_puts(buf3);
    _delay_ms(50);
    asm(«sei»);

    }
    if (menu==4)
    {
    menu=0;
    }
    }
    }

  • Насколько я помню файл eeprom надо шить отдельно, вы прошиваете только флеш или все вместе? Попробуйте создать минимальный проект, где нет прерываний и где вы записываете переменную и читаете, будет ли работать.

  • у меня программатор что то не то выдаёт.Я выбераю еер и выставляю на программирование еепрома-наоборот сначала выставляю потом BLANC>Program>VER>Wr fuse>Red fuse>wr.cal.bit.>READ.Он мне читает BIN EEP как hex .Я может чтото не то делаю.SP200SE хекс файл прошивает.И четает а еер вроде прошивает.Но после отключения читает что там ничего нет да EESAV=0. галочку устанавливал результат отрицательный

  • вы правильно посоветовали.СПАСИБО.Я наверное програматор поменяю

  • да и после закрытия программы WILPRO читает FF FF FF FF .

  • и повторного отрытия

  • Можно вопрос ?eep также прошивается как и хекс? ищется еер файл.У меня програматор не может автоматически найти .4 вида BIN.HEX,SREC,TEC файлы ЕЕР нет.

  • надо в свойствах поставить галку генерить eep

  • в AS7 всё выставленно и еер генерируется .Я пользуюсь програматором SP200SE и программа WILPRO не видит этот файл еер.Сам программатор.Я решил купить AVRISP попроывать с ним прошить еер файл.Да я могу выбрать другие файлы в папке «открыть» он видит но не прошивает.ПРошивает во флеш память?

  • Я шью через avr studio 4, всегда все норм шьется через AVRISP

  • можно вопрос по часам у вас подключены выводы мега8 не на SDA SCL почему.А можно ли использовать эти выводы? аппаратно.

  • И каким образом используются не аппаратные выводы?

  • В CAVR используется софтовая либина чтобы можно было пользоваться любыми ногами, но никто вам не мешает использовать аппаратные ноги

  • дайте напутсвие неполучается вывести на лсд на экран самую простую программу создал
    *
    * EEPROM_atmega 16.c
    *
    * Created: 10.04.2016 6:06:21
    * Author : User
    */

    #define F_CPU 8000000UL
    #include
    //#define save_16 EEMEM
    #include

    #include
    #include «lcd.h»
    #include
    #include
    unsigned int data,i,adres;
    unsigned char masiv[4]={100,150,200,250};
    char buf1[10];

    //uint8_t save8 EEMEM = 29;//размер в 1 байт
    uint16_t save16 EEMEM = 2600;// размер в 2 байта
    // uint32_t save32 EEMEM= 10000;//размер в 3 байта

    int main(void)
    {

    //buf1=eeprom_read_byte(& save8);//чтение 1 байта
    data=eeprom_read_word(& save16);// чтение 2 байт
    //data=eeprom_read_dword(& save32);//чтение 3 байт
    //data++;
    //eeprom_write_byte(& save8, data);//запись в память 1 байта

    //eeprom_write_byte(300,5 );//запись в память 1 байта

    eeprom_write_word(& save16,data);//запись в память2 байт
    //eeprom_write_dword(& save32, data);//запись в память 3 байт
    //char buf1[16];

    DDRB=0xFF;
    PORTB=0x00;
    lcd_init(LCD_DISP_ON);
    //lcd_clrscr();
    while (1)
    {
    lcd_clrscr();
    itoa(save16,buf1,10);
    //sprintf(buf1)
    //sprintf(buf1,»a=%4d»,save16);
    lcd_puts(buf1);
    _delay_ms(50);
    //eeprom_write_word(& save16, data);//запись в память2 байт

    }
    }

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

  • Попробуйте жестко задать адрес переменной
    eeprom_write_byte ((uint8_t*)20, 100)
    eeprom_read_byte((uint8_t*)20);

  • Всем привет .У меня тоже проблема снастройками вывожу в UART часы идут но настраиваться не хотят ???????

    while (1)
    {
    set=0; //Обнуление переменной, необходимой для работы с кнопками
    if(PINB.4==0) // Если нажата кнопка «Set» переходим к настройке часов
    {
    set=1;
    while(set==1)
    {
    pause;
    switch (PINB) { // Отслеживаем нажатие кнопок
    case 0xF7: hour++; if (hour>23){hour=0;}; break; //Если нажата кнопка «>» то увеличиваем колчество часов на 1
    case 0xFB: hour—; if (hour==255){hour=23;}; break; //Если нажата кнопка «59){min=0;}; break; //Если нажата кнопка «>» то увеличиваем колчество минут на 1
    case 0xFB: min—; if (min==255){min=59;}; break; //Если нажата кнопка «<" то уменьшаем колчество минут на 1
    case 0xEF: set=0; rtc_set_time(hour,min,sec);break; //Если нажата кнопка "Set", отправляем в микросхему часов новые значения времени
    };
    };
    rtc_get_time(&hour,&min,&sec); //считать время
    //rtc_get_date(&date,&month,&year);
    //printf("DATA: %02d:%02d:%02d \r",date,month,year);
    printf("CLOCK:%u%u:%u%u:%u%u\r",hour/10,hour%10,min/10,min%10,sec/10,sec%10);
    //printf("CLOCK: %02d:%02d:%02d \r",hour,min,sec);
    pause;
    }}}}

  • И надо ли ход часов останавливать перед настройками???

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

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

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