În exemplul următor pe lângă utilizarea controlerului Atmega644p am adăugat și următoarele funcții:
- semnalizare - folosind un LED;
- comenzi - folosind un buton;
- afișarea de informații - folosind un afișor grafic LCD - 16x2;
- ceas de timp real - RTC - folosind circuitul PCF8583;
- senzor de temperatură - folosind circuitul DS18B20;
- măsurarea semnalelor analoge - folosind capabilitățile ADC.
Schema electrică utilizată în simularea cu Proteus este prezentată în imaginea de mai jos:
Codul sursă (de această dată am folosit comentarii în limba engleză) utilizat este următorul:
/*
Connection LCD to Sanguino
* LCD RS to digital pin 22
* LCD Enable to digital pin 23
* LCD D4 to digital pin 24
* LCD D5 to digital pin 25
* LCD D6 to digital pin 26
* LCD D7 to digital pin 28
* LCD R/W to GND
Connection RED LED to Sanguino
* LED K to digital 13
* LED A to +5V in serie with 1k resistor
Connection DS18B20 - temperature sensor - case TO92 to Sanguino
* DS18B20 DQ to digital 2
* DS18B20 Vdd to +5V
* DS18B20 GND to GND
* DS18B20 use an pull-up resistor with value 4k7 between Vdd and DQ pin
Connection RTC - PCF8583 to Sanguino
* RTC A0 to +V for hardware address 0xA2
* RTC SCL to pin D16/SCL
* RTC SDA to pin D17/SDA
* RTC OSC1 to 32678Hz crystal
* RTC OSC0 to 32678HZ crystal
* RTC GND to GND
* RTC OSC1 to +V using 22pF capacitor
*/
// Library used
#include <Arduino.h>
#include <LiquidCrystalIO.h> // load LCD library
#include <OneWireNg_CurrentPlatform.h> // load 1-Wire communication library (DS18B20)
#include <PCF8583.h> // load library necessary to RTC sensor
#include <stdio.h> // load library necessary to build a string with sprintf function
// variable with constant value
const int ledPin = 13; // digital pin number where LED is connected
const long intervalBlink = 500; // time interval to blink the LED (in milliseconds)
const int runButton = 12; // digital pin number where RUN button is connected
char date[50]; // buffer where we store the date info
char time[50]; // buffer where we store the time info
char times[50]; // buffer where we store the date and time info
char day[5], month[5], year[5]; // buffers used store the date
char hour[5], minute[5], second[6]; // buffers used to store the time
char potbuf1[50]; // buffer where we store the ADC info for P1-P2
char potbuf2[50]; // buffer where we store the ADC info for P3-P4
// variable with value changed
int ledState = HIGH; // ledState used to set LED-ul ON or OFF (HiGH => LED=OFF)
unsigned long previousMillisBlink = 0; // variable used to store the previous time when the LED was upgraded
int runButtonState = HIGH; // variable used to store the state for the button Run
int pot1, pot2, pot3, pot4; // value of ADC
// pins used to connect LCD to Sanguino
const int rs = 22, en = 23, d4 = 24, d5 = 25, d6 = 26, d7 = 27;
// construct object lcd
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// -----------------------Declaration used to set sensor DS18B20---------------
#define OW_PIN 2 // pin where is connected sensor DS18
// #define PARASITE_POWER // delete the comment for parasite support
#ifdef PARASITE_POWER
// # define PWR_CTRL_PIN 9 // delete the comment to power the transistor throw pin 9
#endif
// commands for DS sensors
#define CMD_CONVERT_T 0x44
#define CMD_COPY_SCRATCHPAD 0x48
#define CMD_WRITE_SCRATCHPAD 0x4e
#define CMD_RECALL_EEPROM 0xb8
#define CMD_READ_POW_SUPPLY 0xb4
#define CMD_READ_SCRATCHPAD 0xbe
// DS sensors supported
#define DS18S20 0x10
#define DS1822 0x22
#define DS18B20 0x28
#define DS1825 0x3b
#define DS28EA00 0x42
#define ARRSZ(t) (sizeof(t)/sizeof((t)[0]))
static struct {
uint8_t code;
const char *name;
}
DSTH_CODES[] = {
{ DS18S20, "DS18S20" },
{ DS1822, "DS1822" },
{ DS18B20, "DS18B20" },
{ DS1825, "DS1825" },
{ DS28EA00,"DS28EA00" }
};
static OneWireNg *ow = NULL;
// return NULL if is not supported
//static const char *dsthName(const OneWireNg::Id& id) {
// for (size_t i=0; i < ARRSZ(DSTH_CODES); i++) {
// if (id[0] == DSTH_CODES[i].code)
// return DSTH_CODES[i].name;
// }
// return NULL;
//}
// ---------------------------end declarations for DS18B20-------------------------------
// construct an p object
// PCF8583 p(0xA0); // hardware address for A0 connected to GND
PCF8583 rtc(0xA2); // hardware address for A0 connected to +V
// declare used functions
void blinkLed(); // function used to blink the LED
long getTemp(); // function used to read the temperature
void printLcdTemp(); // function used to display the temperature to LCD
void printLcdTime(); // function used to display the time to LCD
void setDateTime(); // function used to setting the date and the time
void clear_row_1(); // function used to clean the row 1 and to put the cursor at the begging line
void clear_row_2(); // function used to clean the row 2 and to put the cursor at the begging line
void printAllADCtoLCD(); // function used to print all ADC values to LCD
void printLCDTimeADC(); // function used to print all ADC or Date, Time and Temp values at LCD
// this function is run only once when the atmega is power/reset
void setup() {
// ----------------- start to configure DS18B20------------------------------------
#ifdef PWR_CTRL_PIN
ow = new OneWireNg_CurrentPlatform(OW_PIN, PWR_CTRL_PIN, false);
#else
ow = new OneWireNg_CurrentPlatform(OW_PIN, false);
#endif
delay(500);
#if (CONFIG_MAX_SRCH_FILTERS > 0)
// if filtering is enabled - filter to supported devices only;
//CONFIG_MAX_SRCH_FILTERS must be large enough to embrace all code ids
for (size_t i=0; i < ARRSZ(DSTH_CODES); i++)
ow->searchFilterAdd(DSTH_CODES[i].code);
#endif
// ---------------------end to configure DS18B20----------------------
Serial.begin(9600); // begin serial communication
lcd.begin(16, 2); // set the columns number and rows number at used LCD
lcd.print("Load..."); // send message to LCD
Serial.print("Load..."); // send message to serial console
Serial.println(" OK");
delay(1000); // wait to read the message
pinMode(ledPin, OUTPUT); // setting digital pin ledPin to be output
pinMode(runButton, INPUT_PULLUP); // setting digital pin for Run button to be input with pull-up resistor
setDateTime(); // call the function to setting the date and time
lcd.clear(); // clean all lines from LCD
}
//this functions is run in continue loop
void loop() {
blinkLed(); // call the function to blink the LED
printLCDTimeADC();
// printAllADCtoLCD(); // call the function to print the ADC values to LCD
// printLcdTime(); // call the function to print the date and time to LCD
// printLcdTemp(); // call the function to print the temperature to LCD
}
// function what blink the LED
void blinkLed() {
// check if time was gone to change the LED state (ON or OFF)
// if the difference from currentMillis and previosMillis is bigger by intervalBlink
unsigned long currentMillisBlink = millis(); // put current time in variable curentMillis
if (currentMillisBlink - previousMillisBlink >= intervalBlink) {
// check if time wishes was gone
// if yes, then
previousMillisBlink = currentMillisBlink; // save current time
if (ledState == HIGH) {
// if the LED is OFF/ON then I change the state to ON/OFF
ledState = LOW;
}
else {
ledState = HIGH;
}
digitalWrite(ledPin, ledState); // set the pin state with value 0/1 given by ledState
}
}
// function used to read the temperature
long getTemp() {
OneWireNg::Id id;
OneWireNg::ErrorCode ec;
ow->searchReset();
// do {
ec = ow->search(id);
if (!(ec == OneWireNg::EC_MORE || ec == OneWireNg::EC_DONE))
// break;
// start temperature conversion
ow->addressSingle(id);
ow->writeByte(CMD_CONVERT_T);
#ifdef PARASITE_POWER
ow->powerBus(true); // power the bus
#endif
delay(750); // wait for conversion
uint8_t touchScrpd[] = {
CMD_READ_SCRATCHPAD,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
ow->addressSingle(id);
ow->touchBytes(touchScrpd, sizeof(touchScrpd));
uint8_t *scrpd = &touchScrpd[1]; // scratchpad date
if (OneWireNg::crc8(scrpd, 8) != scrpd[8]) {
Serial.println(" Invalid CRC!");
// continue;
}
long temp = ((long)(int8_t)scrpd[1] << 8) | scrpd[0];
if (id[0] != DS18S20) {
unsigned res = (scrpd[4] >> 5) & 3;
temp = (temp >> (3-res)) << (3-res);
temp = (temp*1000)/16;
}
else if (scrpd[7]) {
temp = 1000*(temp >> 1) - 250;
temp += 1000*(scrpd[7] - scrpd[6]) / scrpd[7];
}
else {
temp = (temp*1000)/2;
Serial.println(" Zeroed COUNT_PER_C detected!");
}
return temp;
// } while (ec == OneWireNg::EC_MORE);
}
// function to print temperature information at LCD
void printLcdTemp() {
long temperatura = getTemp();
// print the temperature using cast function from long to float
lcd.setCursor(8,1); // put the cursor to print information at line 2 and column 9
lcd.print(" "); // delete last temperature info
if (temperatura < 0) { // print negative sign if the temperature is below 0
temperatura = -temperatura;
lcd.setCursor(9,1);
lcd.print('-');
}
lcd.setCursor(10,1); // put the cursor to print information at line 2 and column 11
lcd.print((float)temperatura / 1000); // print temperature information
lcd.print('C'); // print C sign for Celsius degree
/*
// print teperature information using long variable
lcd.setCursor(9,1); // put the cursor to print information at line 2 and column 10
lcd.print(" "); // delete last temperature info
if (temperatura < 0) { // print negative sign if the temperature is below 0
temperatura = -temperatura;
lcd.setCursor(9,1);
lcd.print('-');
}
lcd.setCursor(10,1); // put the cursor to print information at line 2 and column 11
lcd.print(temperatura / 1000);
lcd.print('.'); // print comma
lcd.print(temperatura % 1000);
//lcd.print('C');
*/
}
// function to setup the date and time, send: DDMMYYYYhhmmss;
void setDateTime() {
Serial.println("You can set the time using format: DDMMYYYYhhmmss; ");
Serial.println("and then press RUN button.");
while (runButtonState == HIGH) {
sprintf(date, "%02d/%02d/%04d",
rtc.getDay(), rtc.getMonth(), rtc.getYear()); // put the date info from RTC in buffer
sprintf(time, "%02d:%02d:%02d",
rtc.getHour(), rtc.getMinute(), rtc.getSecond()); // put the time info from RTC in buffer
lcd.setCursor(0,0);
lcd.print(date);
lcd.print(" SET");
lcd.setCursor(0,1);
lcd.print(time);
lcd.print(" CLOCK");
if(Serial.available() > 0) { // if exist serial communication, then I read the data send by user
Serial.println("Receiving...");
uint8_t day = (int) ((Serial.read() - 48) *10 + (Serial.read() - 48));
uint8_t month = (int) ((Serial.read() - 48) *10 + (Serial.read() - 48));
uint8_t year = (int) ((Serial.read() - 48) *1000 + (Serial.read() - 48) *100 + (Serial.read() - 48) *10 + (Serial.read() - 48)) ;
uint8_t hour = (int) ((Serial.read() - 48) *10 + (Serial.read() - 48));
uint8_t min = (int) ((Serial.read() - 48) *10 + (Serial.read() - 48));
uint8_t sec = (int) ((Serial.read() - 48) * 10 + (Serial.read() - 48));
if(Serial.read() == ';') { // if the last character is ; this mean the end on data
Serial.println("and the clock was setting..."); // the time was setting
rtc.setDateTime(sec, min, hour, day, month, year); // set the time and date
lcd.clear(); // clear LCD
// read the time
sprintf(times, "%02d/%02d/%04d %02d:%02d:%02d",
rtc.getDay(), rtc.getMonth(), rtc.getYear(), rtc.getHour(), rtc.getMinute(), rtc.getSecond());
Serial.println(times); // print at serial console the setting time
}
}
runButtonState = digitalRead(runButton);
delay(100);
}
}
// function to print date and time information at LCD, DD/MM/YYYY and hh:mm:ss
void printLcdTime() {
// sprintf(date, "%02d/%02d/%04d",
// rtc.getDay(), rtc.getMonth(), rtc.getYear()); // put the date info from RTC in buffer
// sprintf(time, "%02d:%02d:%02d",
// rtc.getHour(), rtc.getMinute(), rtc.getSecond()); // put the time info from RTC in buffer
lcd.setCursor(0,0);
// lcd.print(date);
// put the date info from RTC in buffers
sprintf(day, "%02d", rtc.getDay());
sprintf(month, "%02d", rtc.getMonth());
sprintf(year, "%4d", rtc.getYear());
// put the time info from RTC in buffer
sprintf(hour, "%02d", rtc.getHour());
sprintf(minute, "%02d", rtc.getMinute());
sprintf(second, "%02d", rtc.getSecond());
lcd.print(day);
lcd.print("/");
lcd.print(month);
lcd.print("/");
lcd.print(year);
lcd.print(" ");
lcd.setCursor(0,1);
// lcd.print(time);
lcd.print(hour);
lcd.print(":");
lcd.print(minute);
lcd.print(":");
lcd.print(second);
}
// function clean the line 1 from LCD and put the cursor at the begging line
void clear_row_1() {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
}
// function clean the line 2 from LCD and put the cursor at the begging line
void clear_row_2() {
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
}
//function to read ADC value
void printAllADCtoLCD() {
//read potentiometers
pot1 = analogRead(A0);
pot2 = analogRead(A1);
pot3 = analogRead(A2);
pot4 = analogRead(A3);
sprintf(potbuf1, "P1=%04d P2=%04d", pot1, pot2); // put in buffer info with ADC values
clear_row_1(); // clear line 1 from LCD
lcd.print(potbuf1); // print to LCD the information with ADC values
sprintf(potbuf2, "P3=%04d P4=%04d", pot3, pot4); // put in buffer info with ADC values
clear_row_2(); // clear line 1 from LCD
lcd.print(potbuf2); // print to LCD the information with ADC values
delay(250);
}
// function to print all ADC or Date, Time and Temp values at LCD
void printLCDTimeADC() {
if (runButtonState == HIGH)
{
printAllADCtoLCD();
runButtonState = digitalRead(runButton);
}
else
{
printLcdTime();
printLcdTemp();
runButtonState = digitalRead(runButton);
}
delay(250);
}
Funcționare:
La prima alimentare a montajului sau la resetarea microcontrolerului vom fi întâmpinații pe ecranul LCD-ului de următorul mesaj:
iar mesajul recepționat, prin comunicația serială, la consolă este următorul:
în acest moment putem seta data și ora în ceasul real realizat cu PCF8583.
Secvența necesară a fi transmisă de la consola serială în vederea setării datei și a orei este de forma ZZLLAAAAhhmmss;, de exemplu :
16042020094600;
ca în imaginea de mai jos:
după transmiterea șirului de caractere la consola serială ni se va transmite faptul că mesajul a a fost recepționat împreună cu data și ora curentă, ca în imaginea de mai jos:
După setarea datei și a orei sau dacă suntem mulțumiți de data și ora afișată, de exemplu am setat data și ora anterior iar aceasta fiind menținută de către bateria de 3V, vom apasă butonul RUN ce va face ca montajul să ruleze mai departe în vederea afișării informațiilor primite de la cele 4 potențiometre conectate la intrarile ADC (A0-A3), ca în imaginea de mai jos:
Pentru o obține informații legate de data, oră și temperatură va trebui să apăsăm butonul RUN. Aceste informații vor fi afișate atât timp cât butonul va fi apasat, iar mesajul va fi de forma:
Funcționarea montajului în mediul de simulare Proteus poate fi vizionată în următorul videoclip:
Întreg proiectul (codul sursă și fișierul în Proteus) poate fi descărcat de aici LCD_Blink_LED_DS18B20_PCF8583_ADC.zip
Librăriile noi folosite și adăugate în cadrul codului sursă sunt:
- LiquidCrystalIO;
- OneWireNg;
- PCF8583 - aceasta prezintă mai multe funcționalități ca cea folosită în exemplul anterior.