PIC мк. Эксперимент №17. Часы-термометр.

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

Получилось что-то такое:

clock

В силу того, что из всех комплектующих первым у меня оказался “расширитель портов” PCA9539. О этой микросхеме упоминалось в эксперименте №13.

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

Схема системы индикации:

ind

Плата под индикацию:

Вид с обратной стороны можно посмотреть на первой фотке.

Далее была собрана главная плата с часами реального времени DS1307 и термометром DS18b20.

Схема:

sch_main_board

и вид платы:

Код проекта:


#include <stdio.h>
#include <htc.h>
#define _XTAL_FREQ 4000000
#include "usart.h"               //функции для работы уарта
#include "softi2c.h"             //функции для работы i2c

#define timeh1 RB7  //3
#define timeh2 RB6  //4
#define timem1 RB4
#define timem2 RB5

#define tempd1 RB3
#define tempd2 RB2
#define tempd3 RB1

#define STATE TRISA2
#define PIN RA2

#define set_min RA3
#define set_hr RA4
#define setup RB0

__CONFIG(LVPDIS & WDTDIS & MCLREN & UNPROTECT & HS);

 unsigned char temphour = 0;         //часы
 unsigned char tempmin = 0;          //минуты

 char temperature;                  //температура
 unsigned char temp_drob = 0;       //дробная часть температуры
 unsigned char sign;                //знак температуры

 volatile unsigned char button = 0;
 volatile unsigned char flag = 0;

const unsigned char tempdigit[11]={    //масив для семисегментника, значения температуры
 0b10110111,   //ноль
 0b10000001,   //один
 0b00111101,   //два
 0b10101101,   //три
 0b10001011,   //четыре
 0b10101110,   //пять
 0b10111110,   //шесть
 0b10000101,   //семь
 0b10111111,   //восемь
 0b10101111,    //девять
 0b00001000
};

const unsigned char tempdigitvspoint[10]={  //значения температуры с точкой
 0b11110111,   //ноль
 0b11000001,   //один
 0b01111101,   //два
 0b11101101,   //три
 0b11001011,   //четыре
 0b11101110,   //пять
 0b11111110,   //шесть
 0b11000101,   //семь
 0b11111111,   //восемь
 0b11101111    //девять
};

const unsigned char timedigit[10]={            //значения времени
 0b11110101,   //ноль
 0b00100100,   //один
 0b10111001,   //два
 0b10101101,   //три
 0b01101100,   //четыре
 0b11001101,   //пять
 0b11011101,  //шесть
 0b10100100,   //семь
 0b11111101,   //восемь
 0b11101101    //девять
};

const unsigned char timedigitvspoint[10]={       //значения времени с точкой
 0b11110111,   //ноль
 0b00100110,   //один
 0b10111011,   //два
 0b10101111,   //три
 0b01101110,   //четыре
 0b11001111,   //пять
 0b11011111,  //шесть
 0b10100110,   //семь
 0b11111111,   //восемь
 0b11101111    //девять
};

/* Объявления функций */
static bit INIT(void);
void TX(unsigned char cmd);
unsigned char RX();
void get_temp();
unsigned char BCDconv (unsigned char source);
unsigned char DCBconv (unsigned char source);
void SetMin(unsigned char minutes);
unsigned char ReadMin();
void SetHour(unsigned char hours);
unsigned char ReadHour();
void SetSeconds(unsigned char seconds);
unsigned char ReadSeconds();
void ShowTime();
void SetupTime();
void InitIO(void);
void Display(unsigned char time, unsigned char temperaturet);
void Displaypoint(unsigned char time, unsigned char temperaturet);
void Displaypointminus(unsigned char time, unsigned char temperaturet);
void DispAll(unsigned char hour, unsigned char min, unsigned char temperaturet);

void main(void){
 unsigned char input;
 unsigned char i=0;

 CMCON = 0x07;

 TRISB = 0x00;
 TRISB0 = 1;
 TRISA3 = 1;
 TRISA4 = 1;
 get_temp();                 //берем значения температуры для первого включения
 /* Служебная установка часов и вывод на комп через уарт */
 /*
 init_comms();
 SetHour(15);
 SetMin(38);
 SetSeconds(23);
 get_temp();
 ShowTime();

 */

 InitIO();              //инициализация расширителя портов

 /* Настраиваем таймер, задающий период обновления температуры и времени */

 T1CKPS1 = 1;
 T1CKPS0 = 1; // предделитель = 8

 T1OSCEN = 0; //выключаем внутренний генератор
 TMR1CS = 0; // Fosc/4
 GIE = 1;   // глобальные прерывания
 PEIE = 1;  // прерывания перефирии
 TMR1IE = 1; // прерывание по переполнению TMR1
 TMR1ON = 1; // включаем таймер

 TMR1H = 0b10110000; TMR1L = 0x00;  //инициализация таймера со значения 65024
 INTE = 0;

 temphour = ReadHour();
 tempmin = ReadMin();
 //get_temp();

 for(;;){

 DispAll(temphour, tempmin , temperature);         //отображаем все на дисплее

 /* Настройка обновления данных */
 if (flag == 20) {
 temphour = ReadHour();
 tempmin = ReadMin();
 i++;
 flag = 0;
 }
 if (i == 100) { TMR1IE = 0;
 get_temp();
 i = 0;
 TMR1IE = 1; }

 /* Обработчик нажатий кнопок */

 if (!set_hr) {
 TMR1IE = 0;
 __delay_ms(100);
 if (!set_hr) {

 if (temphour<24) {
 temphour++;
 SetHour(temphour);
 } else {
 temphour = 1;
 SetHour(temphour);
 }

 }
 TMR1IE = 1;
 }
 if (!set_min) {
 TMR1IE = 0;
 __delay_ms(100);
 if (!set_min) {
 if (tempmin<60) {
 tempmin++;
 SetMin(tempmin);
 } else {
 tempmin = 1;
 SetMin(tempmin);
 }

 }
 TMR1IE = 1;
 }

 if (!setup) {
 TMR1IE = 0;
 __delay_ms(100);
 if (!setup) {
 SetSeconds(0);

 }
 TMR1IE = 1;
 }

/*
 /* Меню для отображения по уарту */

 input = getch();

 switch (input) {
 case 49 : ShowTime();
 break;
 case 50 : SetupTime();
 break;
 case 51 : get_temp();
 ShowTime();
 break;
 }
 if (input == 49) {     SetupTime();}
*/

 }

}

interrupt isr() {               //обработчик прерываний

if (TMR1IF) {
flag++;
TMR1H = 0b10110000; TMR1L = 0x00;
TMR1IF = 0;  //сброс флага
}

}

/* Функции протокола 1-wire */
static bit INIT(void){
static bit b;
STATE = 1;
STATE = 0;
__delay_us(500);
STATE = 1;
__delay_us(65);
b = PIN;
__delay_us(450);

return b;

}

void TX(unsigned char cmd){
unsigned char temp = 0;
unsigned char i = 0;
temp = cmd;
for (i=0;i<8;i++) {
 if (temp&0x01) {
 STATE = 0;
 __delay_us(5);
 STATE = 1;
 __delay_us(70);
 } else {
 STATE = 0;
 __delay_us(70);
 STATE = 1;
 __delay_us(5);

 }
 temp >>= 1;
 }

}

unsigned char RX() {
unsigned char d = 0;
unsigned char i = 0;
for (i=0;i<8;i++){
 STATE = 0;
 __delay_us(6);
 STATE = 1;
 __delay_us(4);
 d>>=1;
 if (PIN == 1) d |= 0x80;
 //printf("\r\n %d", d);
 __delay_us(60);

 }

return d;

}

/* Получаем значение температуры */
void get_temp() {
static bit init;
unsigned char temp1;
unsigned char temp2;
init = INIT();
if (!init) {
TX(0xCC);
TX(0x44);
__delay_ms(150);
__delay_ms(150);
__delay_ms(150);
__delay_ms(150);
__delay_ms(150);
} else printf("bug");
init = INIT();
if (!init) {
TX(0xCC);
TX(0xBE);

temp1 = RX();
temp2 = RX();

}
temp_drob = temp1 & 0b00001111;    //Записываем дробную часть в отдельную переменную
temp_drob = ((temp_drob*6)+2)/10;              //Переводим в нужное дробное число
temp1 >>= 4;
sign = temp2 & 0x80;
temp2 <<= 4;
temp2 &= 0b01110000;
temp2 |= temp1;

if (sign) { temperature = 127-temp2;
 temp_drob = 10 - temp_drob;
 }   else temperature = temp2;

}

/* Функции часов */

unsigned char BCDconv (unsigned char source) {
 unsigned char temp_min=0;
 unsigned char temp_maj=0;
 temp_min = source&15;
 temp_maj = source >> 4;
 //printf("\r\n minor - %x", temp_min);
 //printf("\r\n major - %x", temp_maj);
 temp_maj *= 10;
 //printf("\r\n major - %x", temp_maj);

 return temp_maj+temp_min;
}

unsigned char DCBconv (unsigned char source) {
 unsigned char temp_min=0;
 unsigned char temp_maj=0;
 temp_maj = source/10 ;
 temp_min = source - temp_maj*10;
 temp_maj <<= 4;

 return temp_maj+temp_min;

}

void SetMin(unsigned char minutes) {        //записываем значение в минуты
 i2c_start();
 i2c_tx(0b11010000);
 i2c_tx(0x01);
 i2c_tx(DCBconv(minutes));
 i2c_stop();
}

unsigned char ReadMin() {
 unsigned char temp = 0;
 i2c_start();            //читаем значение минут
 i2c_tx(0b11010000);
 i2c_tx(0x01);
 i2c_start();
 i2c_tx(0b11010001);
 temp = i2c_rx(0);
 i2c_stop();

 return BCDconv(temp);
}

void SetHour(unsigned char hours) {        //записываем значение в часы
 i2c_start();
 i2c_tx(0b11010000);
 i2c_tx(0x02);
 i2c_tx(DCBconv(hours));
 i2c_stop();
}

unsigned char ReadHour() {
 unsigned char temp = 0;
 unsigned char temp2 = 0;
 i2c_start();            //читаем значение часов
 i2c_tx(0b11010000);
 i2c_tx(0x02);
 i2c_start();
 i2c_tx(0b11010001);
 temp = i2c_rx(0);
 i2c_stop();
 /*
 temp2 = temp&0b00001111;
 temp /= 10;
 temp = temp >> 4;
 temp = temp*10 + temp2;*/

 return BCDconv(temp);
}

void SetSeconds(unsigned char seconds) {        //записываем значение в секунды
 i2c_start();
 i2c_tx(0b11010000);
 i2c_tx(0x00);
 i2c_tx(DCBconv(seconds));
 i2c_stop();
}

unsigned char ReadSeconds() {
 unsigned char temp = 0;
 i2c_start();            //читаем значение минут
 i2c_tx(0b11010000);
 i2c_tx(0x00);
 i2c_start();
 i2c_tx(0b11010001);
 temp = i2c_rx(0);
 i2c_stop();

 return BCDconv(temp);
}

/* Функции для работы по протоколу USART */
void ShowTime() {
 printf("\fTime - %d :", ReadHour());
 printf(" %d :", ReadMin());
 printf(" %d ", ReadSeconds());

 printf("\r\ntemperatura -- ");
 if (sign) printf("-"); else printf("+");
 printf("%d", temperature);
 printf(".%d", temp_drob);

 printf("\r\n*********************");

 printf("\r\n Menu");
 printf("\r\n 1 - Reload time");
 printf("\r\n 2 - Setup time");
 printf("\r\n 3 - Reload temp");

}

void SetupTime() {
 unsigned char state = 1;
 unsigned char temp_min = 0;
 unsigned char temp_maj = 0;
 unsigned char temp = 0;

 while(state){
 printf("\fEnter hours - ");
 temp_maj = getch();
 printf(" %c", temp_maj);
 temp_min = getch();
 printf("%c", temp_min);
 temp = getch();
 if (temp == 13) {
 temp_maj -= 48;
 temp_min -= 48;
 temp = temp_maj*10 + temp_min;
 if (temp < 24) {
 SetHour(temp);
 state = 0;
 }

 }
 }
 state = 1;
 while(state){
 printf("\fEnter minutes - ");
 temp_maj = getch();
 printf(" %c", temp_maj);
 temp_min = getch();
 printf("%c", temp_min);
 temp = getch();
 if (temp == 13) {
 temp_maj -= 48;
 temp_min -= 48;
 temp = temp_maj*10 + temp_min;
 if (temp < 61) {
 SetMin(temp);
 state = 0;
 }
 }
 }
 state = 1;
 while(state){
 printf("\fEnter seconds - ");
 temp_maj = getch();
 printf(" %c", temp_maj);
 temp_min = getch();
 printf("%c", temp_min);
 temp = getch();
 if (temp == 13) {
 temp_maj -= 48;
 temp_min -= 48;
 temp = temp_maj*10 + temp_min;
 if (temp < 61) {
 SetSeconds(temp);
 state = 0;
 }
 }
 }

 ShowTime();

}

/* Функции работы с расширителем портов */
void InitIO(void) {
 i2c_start();
 i2c_tx(0b11101000);
 i2c_tx(0b00000110);
 i2c_tx(0x00);
 i2c_tx(0x00);
 i2c_stop();
}

void Display(unsigned char time, unsigned char temperaturet) {
 unsigned char timetemp = 0;
 unsigned char temptemp = 0;
 timetemp = time;
 temptemp = temperaturet;
 i2c_start();
 i2c_tx(0b11101000);
 i2c_tx(0b00000010);
 i2c_tx(timedigit[timetemp]);
 i2c_tx(tempdigit[temptemp]);
 i2c_stop();

}

void Displaypoint(unsigned char time, unsigned char temperaturet) {
 unsigned char timetemp = 0;
 unsigned char temptemp = 0;
 timetemp = time;
 temptemp = temperaturet;
 i2c_start();
 i2c_tx(0b11101000);
 i2c_tx(0b00000010);
 i2c_tx(timedigitvspoint[timetemp]);
 i2c_tx(tempdigitvspoint[temptemp]);
 i2c_stop();

}

void Displaypointminus(unsigned char time, unsigned char temperaturet) {
 unsigned char timetemp = 0;
 unsigned char temptemp = 0;
 timetemp = time;
 temptemp = temperaturet;
 i2c_start();
 i2c_tx(0b11101000);
 i2c_tx(0b00000010);
 i2c_tx(timedigitvspoint[timetemp]);
 i2c_tx(tempdigit[temptemp]);
 i2c_stop();

}

void DispAll(unsigned char hour, unsigned char min, unsigned char temperaturet) {
 unsigned char tim = 0;
 unsigned char tem = 0;
 unsigned char buf = 0;

 if (!sign) {
 timeh1 = 0; timeh2 = 0; timem1 = 0; timem2 = 0; tempd1 = 0; tempd2 = 0; tempd3 = 0;
 tim = hour/10; tem = temperaturet/10;
 Display(tim, tem);
 timeh1 = 1; timeh2 = 0; timem1 = 0; timem2 = 0; tempd1 = 1; tempd2 = 0; tempd3 = 0;
 __delay_us(500);
 timeh1 = 0; timeh2 = 0; timem1 = 0; timem2 = 0; tempd1 = 0; tempd2 = 0; tempd3 = 0;
 buf = tim*10;
 tim = hour - buf;
 tem = temperaturet - tem*10;
 Displaypoint(tim, tem);
 timeh1 = 0; timeh2 = 1; timem1 = 0; timem2 = 0; tempd1 = 0; tempd2 = 1; tempd3 = 0;
 __delay_us(500);

 timeh1 = 0; timeh2 = 0; timem1 = 0; timem2 = 0; tempd1 = 0; tempd2 = 0; tempd3 = 0;
 tim = min/10; tem = temp_drob;
 Display(tim, tem);
 timeh1 = 0; timeh2 = 0; timem1 = 1; timem2 = 0; tempd1 = 0; tempd2 = 0; tempd3 = 1;
 __delay_us(500);
 timeh1 = 0; timeh2 = 0; timem1 = 0; timem2 = 0; tempd1 = 0; tempd2 = 0; tempd3 = 0;
 buf = tim*10;
 tim = min-buf;
 Display(tim, tem);
 timeh1 = 0; timeh2 = 0; timem1 = 0; timem2 = 1; tempd1 = 0; tempd2 = 0; tempd3 = 1;
 __delay_us(500);
 timeh1 = 0; timeh2 = 0; timem1 = 0; timem2 = 0; tempd1 = 0; tempd2 = 0; tempd3 = 0;
 __delay_us(500);
 }  else
 {
 timeh1 = 0; timeh2 = 0; timem1 = 0; timem2 = 0; tempd1 = 0; tempd2 = 0; tempd3 = 0;
 tim = hour/10; tem = 10;
 Display(tim, tem);
 timeh1 = 1; timeh2 = 0; timem1 = 0; timem2 = 0; tempd1 = 1; tempd2 = 0; tempd3 = 0;
 __delay_ms(1);
 timeh1 = 0; timeh2 = 0; timem1 = 0; timem2 = 0; tempd1 = 0; tempd2 = 0; tempd3 = 0;
 buf = tim*10;
 tim = hour - buf;
 tem = temperaturet/10;
 Displaypointminus(tim, tem);
 timeh1 = 0; timeh2 = 1; timem1 = 0; timem2 = 0; tempd1 = 0; tempd2 = 1; tempd3 = 0;
 __delay_ms(1);

 timeh1 = 0; timeh2 = 0; timem1 = 0; timem2 = 0; tempd1 = 0; tempd2 = 0; tempd3 = 0;
 tim = min/10; tem = temperaturet - tem*10;
 Display(tim, tem);
 timeh1 = 0; timeh2 = 0; timem1 = 1; timem2 = 0; tempd1 = 0; tempd2 = 0; tempd3 = 1;
 __delay_ms(1);
 timeh1 = 0; timeh2 = 0; timem1 = 0; timem2 = 0; tempd1 = 0; tempd2 = 0; tempd3 = 0;
 buf = tim*10;
 tim = min-buf;
 Display(tim, tem);
 timeh1 = 0; timeh2 = 0; timem1 = 0; timem2 = 1; tempd1 = 0; tempd2 = 0; tempd3 = 1;
 __delay_ms(1);
 timeh1 = 0; timeh2 = 0; timem1 = 0; timem2 = 0; tempd1 = 0; tempd2 = 0; tempd3 = 0;
 __delay_ms(1);
 }
}

Ну и видео работы часов:

 

Исходники и PCB лежат на репе в разделе проекты.

PIC мк. Эксперимент №17. Часы-термометр.: 16 комментариев

  1. У вас довольно часто встречаются команды типа printf
    printf(“\fTime – %d :”, ReadHour());
    printf(” %d :”, ReadMin());
    printf(” %d “, ReadSeconds());
    printf(“\r\ntemperatura — “);
    printf(“\r\n*********************”);
    printf(“\r\n Menu”);
    printf(“\r\n 1 – Reload time”);
    Где можно почитать более подробно про их применение. Конкретно не понятно применение значков “\r\n ” %d “,
    Что они означают

    Ответить

    sarge Ответил:

    Это стандартный синтаксис библиотекии stdio https://ru.wikipedia.org/wiki/Printf

    Конкретно по вашему вопросу:
    %d, отобразит в месте %d децимальное число, например printf(“%d”, 10) отобразит 10.
    \r\n – вообще все просто маркер конца строки (ASCII table)

    Ответить

  2. Здравствуйте!
    Пытаюсь приспособить Ваш проект под 12ф629 в части показания температуры.
    В temperature сохраняется целая часть значения, так?
    Хочу вывести на одноиндикаторный семисегментный диод через сдвиговый регистр его значение по одному символу. Как вывести двухзначное значение temperature?

    Ответить

    yeye77 Ответил:

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

    Ответить

    sarge Ответил:

    Добрый день.

    Не понимаю где вы затык поймали:
    напримерь temperature = 25.
    1. display tempereature/10
    2. display temperature-temperature/10

    Ответить

  3. Все так просто :)
    А второй пункт разве не даст 25-2=23?

    Ответить

    sarge Ответил:

    да, виноват, надо:
    display temperature-(temperature/10)*10

    Ответить

    yeye77 Ответил:

    Не показывает, точнее ноль.
    Выводы определил так:
    #define STATE TRISIO4
    #define PIN GPIO4

    void main(void) 
    {
    TRISIO = 0b111000;
    CMCON = 0x07;
    ...
    }
    

    Ответить

    yeye77 Ответил:

    Точнее в начале показывает 8, далее 2, а затем все время ноль.

         get_temp();
        //temperature=12;
        c=temperature;  c=c/10;
        write_byte(digits[11]);
        write_byte(digits1);
        __delay_ms(2000);
    
        c=temperature-(temperature/10)*10;
        write_byte(digits[11]);
        write_byte(digits1);
        __delay_ms(5000);
    
    

    Ответить

    yeye77 Ответил:

    почему-то вставка неправильно сработала, вот так надо:
    write_byte(digits);

    вместо

    write_byte(digits1);

    Ответить

    yeye77 Ответил:

    Опять не то, съедается квадратная скобка:
    write_byte(digits квад.скобка “с” квад.скобка.закр.)

    Ответить

    sarge Ответил:

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

    Поэтому вы сначала разберитесь, точно ли у вас все правильно отображается.
    digits[11] показывает 8, почему 8 то? У вас 11 элемент массива это 8?

    Ответить

    sarge Ответил:

    Кажется я понял, у вас зачем то подряд идет две команды и нужная это
    digits

    Ок, что будет если вы расскоментируете строку temperature = 12 и не будете использовать функцию get_temp?

    Ответить

    yeye77 Ответил:

    digits (11) очищает индикатор, т.е. гасит светодиоды (не использую линию сброса ради экономии ножек).
    Если расскоментировать, то и будет показывать – 1 и 2, поэтому на нем сначала и проверял, т.е. функция вывода на индикатор работает верно. В остальном код Ваш.

    Ответить

    sarge Ответил:

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

    В протеусе пробовали проверять?

    Ответить

    sarge Ответил:

    Так даже я бы предложил следующее:

    1. Дальше обсуждать http://diymicro.ru/pic-mk-eksperiment-16-one-wire-na-primere-ds18b20.html тут, как в более подходящем месте
    2. В протеусе соберите схемку и проверьте реагирует ли вообще ваш пик на изменение входа на этом пине, этот мк имеет малое количество пинов и там могут быть нюансы, о которых я к сожалению сходу вспомнить не могу

    Ответить

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

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

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.