commit
1720602fe6
|
@ -1,32 +1,5 @@
|
|||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,827 @@
|
|||
/*
|
||||
OzOLED.cpp - 0.96' I2C 128x64 OLED Driver Library
|
||||
2014 Copyright (c) OscarLiang.net All right reserved.
|
||||
|
||||
Author: Oscar Liang
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "OzOLED.h"
|
||||
#include <Wire.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
|
||||
// 8x8 Font ASCII 32 - 127 Implemented
|
||||
// Users can modify this to support more characters(glyphs)
|
||||
// BasicFont is placed in code memory.
|
||||
|
||||
// This font be freely used without any restriction(It is placed in public domain)
|
||||
const byte BasicFont[][8] PROGMEM = {
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x00,0x5F,0x00,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x00,0x07,0x00,0x07,0x00,0x00,0x00},
|
||||
{0x00,0x14,0x7F,0x14,0x7F,0x14,0x00,0x00},
|
||||
{0x00,0x24,0x2A,0x7F,0x2A,0x12,0x00,0x00},
|
||||
{0x00,0x23,0x13,0x08,0x64,0x62,0x00,0x00},
|
||||
{0x00,0x36,0x49,0x55,0x22,0x50,0x00,0x00},
|
||||
{0x00,0x00,0x05,0x03,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x1C,0x22,0x41,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x41,0x22,0x1C,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x08,0x2A,0x1C,0x2A,0x08,0x00,0x00},
|
||||
{0x00,0x08,0x08,0x3E,0x08,0x08,0x00,0x00},
|
||||
{0x00,0xA0,0x60,0x00,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x00},
|
||||
{0x00,0x60,0x60,0x00,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x20,0x10,0x08,0x04,0x02,0x00,0x00},
|
||||
{0x00,0x3E,0x51,0x49,0x45,0x3E,0x00,0x00},
|
||||
{0x00,0x00,0x42,0x7F,0x40,0x00,0x00,0x00},
|
||||
{0x00,0x62,0x51,0x49,0x49,0x46,0x00,0x00},
|
||||
{0x00,0x22,0x41,0x49,0x49,0x36,0x00,0x00},
|
||||
{0x00,0x18,0x14,0x12,0x7F,0x10,0x00,0x00},
|
||||
{0x00,0x27,0x45,0x45,0x45,0x39,0x00,0x00},
|
||||
{0x00,0x3C,0x4A,0x49,0x49,0x30,0x00,0x00},
|
||||
{0x00,0x01,0x71,0x09,0x05,0x03,0x00,0x00},
|
||||
{0x00,0x36,0x49,0x49,0x49,0x36,0x00,0x00},
|
||||
{0x00,0x06,0x49,0x49,0x29,0x1E,0x00,0x00},
|
||||
{0x00,0x00,0x36,0x36,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x00,0xAC,0x6C,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x00},
|
||||
{0x00,0x14,0x14,0x14,0x14,0x14,0x00,0x00},
|
||||
{0x00,0x41,0x22,0x14,0x08,0x00,0x00,0x00},
|
||||
{0x00,0x02,0x01,0x51,0x09,0x06,0x00,0x00},
|
||||
{0x00,0x32,0x49,0x79,0x41,0x3E,0x00,0x00},
|
||||
{0x00,0x7E,0x09,0x09,0x09,0x7E,0x00,0x00},
|
||||
{0x00,0x7F,0x49,0x49,0x49,0x36,0x00,0x00},
|
||||
{0x00,0x3E,0x41,0x41,0x41,0x22,0x00,0x00},
|
||||
{0x00,0x7F,0x41,0x41,0x22,0x1C,0x00,0x00},
|
||||
{0x00,0x7F,0x49,0x49,0x49,0x41,0x00,0x00},
|
||||
{0x00,0x7F,0x09,0x09,0x09,0x01,0x00,0x00},
|
||||
{0x00,0x3E,0x41,0x41,0x51,0x72,0x00,0x00},
|
||||
{0x00,0x7F,0x08,0x08,0x08,0x7F,0x00,0x00},
|
||||
{0x00,0x41,0x7F,0x41,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x20,0x40,0x41,0x3F,0x01,0x00,0x00},
|
||||
{0x00,0x7F,0x08,0x14,0x22,0x41,0x00,0x00},
|
||||
{0x00,0x7F,0x40,0x40,0x40,0x40,0x00,0x00},
|
||||
{0x00,0x7F,0x02,0x0C,0x02,0x7F,0x00,0x00},
|
||||
{0x00,0x7F,0x04,0x08,0x10,0x7F,0x00,0x00},
|
||||
{0x00,0x3E,0x41,0x41,0x41,0x3E,0x00,0x00},
|
||||
{0x00,0x7F,0x09,0x09,0x09,0x06,0x00,0x00},
|
||||
{0x00,0x3E,0x41,0x51,0x21,0x5E,0x00,0x00},
|
||||
{0x00,0x7F,0x09,0x19,0x29,0x46,0x00,0x00},
|
||||
{0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x00},
|
||||
{0x00,0x01,0x01,0x7F,0x01,0x01,0x00,0x00},
|
||||
{0x00,0x3F,0x40,0x40,0x40,0x3F,0x00,0x00},
|
||||
{0x00,0x1F,0x20,0x40,0x20,0x1F,0x00,0x00},
|
||||
{0x00,0x3F,0x40,0x38,0x40,0x3F,0x00,0x00},
|
||||
{0x00,0x63,0x14,0x08,0x14,0x63,0x00,0x00},
|
||||
{0x00,0x03,0x04,0x78,0x04,0x03,0x00,0x00},
|
||||
{0x00,0x61,0x51,0x49,0x45,0x43,0x00,0x00},
|
||||
{0x00,0x7F,0x41,0x41,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x00},
|
||||
{0x00,0x41,0x41,0x7F,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x04,0x02,0x01,0x02,0x04,0x00,0x00},
|
||||
{0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00},
|
||||
{0x00,0x01,0x02,0x04,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x20,0x54,0x54,0x54,0x78,0x00,0x00},
|
||||
{0x00,0x7F,0x48,0x44,0x44,0x38,0x00,0x00},
|
||||
{0x00,0x38,0x44,0x44,0x28,0x00,0x00,0x00},
|
||||
{0x00,0x38,0x44,0x44,0x48,0x7F,0x00,0x00},
|
||||
{0x00,0x38,0x54,0x54,0x54,0x18,0x00,0x00},
|
||||
{0x00,0x08,0x7E,0x09,0x02,0x00,0x00,0x00},
|
||||
{0x00,0x18,0xA4,0xA4,0xA4,0x7C,0x00,0x00},
|
||||
{0x00,0x7F,0x08,0x04,0x04,0x78,0x00,0x00},
|
||||
{0x00,0x00,0x7D,0x00,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x80,0x84,0x7D,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x7F,0x10,0x28,0x44,0x00,0x00,0x00},
|
||||
{0x00,0x41,0x7F,0x40,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x7C,0x04,0x18,0x04,0x78,0x00,0x00},
|
||||
{0x00,0x7C,0x08,0x04,0x7C,0x00,0x00,0x00},
|
||||
{0x00,0x38,0x44,0x44,0x38,0x00,0x00,0x00},
|
||||
{0x00,0xFC,0x24,0x24,0x18,0x00,0x00,0x00},
|
||||
{0x00,0x18,0x24,0x24,0xFC,0x00,0x00,0x00},
|
||||
{0x00,0x00,0x7C,0x08,0x04,0x00,0x00,0x00},
|
||||
{0x00,0x48,0x54,0x54,0x24,0x00,0x00,0x00},
|
||||
{0x00,0x04,0x7F,0x44,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x3C,0x40,0x40,0x7C,0x00,0x00,0x00},
|
||||
{0x00,0x1C,0x20,0x40,0x20,0x1C,0x00,0x00},
|
||||
{0x00,0x3C,0x40,0x30,0x40,0x3C,0x00,0x00},
|
||||
{0x00,0x44,0x28,0x10,0x28,0x44,0x00,0x00},
|
||||
{0x00,0x1C,0xA0,0xA0,0x7C,0x00,0x00,0x00},
|
||||
{0x00,0x44,0x64,0x54,0x4C,0x44,0x00,0x00},
|
||||
{0x00,0x08,0x36,0x41,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x41,0x36,0x08,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x02,0x01,0x01,0x02,0x01,0x00,0x00},
|
||||
{0x00,0x02,0x05,0x05,0x02,0x00,0x00,0x00}
|
||||
};
|
||||
|
||||
|
||||
// Big numbers font, from 0 to 9 - 96 bytes each.
|
||||
const byte bigNumbers [][96] PROGMEM = {
|
||||
{0x00, 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xF0, 0xF0, 0xF0, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x0F, 0x0F, 0x0F,
|
||||
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x03, 0x00, 0x00, 0x00},
|
||||
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF0,
|
||||
0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
|
||||
{0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xF0, 0xF0, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xC1, 0xC0, 0xC0, 0xC0,
|
||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x83, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x0F, 0x0F, 0x0F,
|
||||
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x00},
|
||||
|
||||
{0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xF0, 0xF0, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xC1, 0xC0, 0xC0, 0xC0,
|
||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE1, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x81, 0x83, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x87,
|
||||
0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x0F, 0x0F, 0x0F,
|
||||
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x00},
|
||||
|
||||
{0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF0, 0xF0, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
|
||||
0xF0, 0xF0, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
|
||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0F, 0x0F, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x00},
|
||||
|
||||
{0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xF0, 0xF0, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1,
|
||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC1, 0x81, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x81, 0x83, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x87,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x0F, 0x0F, 0x0F,
|
||||
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x00},
|
||||
|
||||
{0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xF0, 0xF0, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1,
|
||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC1, 0x81, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x87,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x0F, 0x0F, 0x0F,
|
||||
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x00},
|
||||
|
||||
{0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xF0, 0xF0, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0F, 0x0F, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x00},
|
||||
|
||||
{0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xF0, 0xF0, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xE1,
|
||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE1, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x87, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x87,
|
||||
0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x0F, 0x0F, 0x0F,
|
||||
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x00},
|
||||
|
||||
{0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xF0, 0xF0, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1,
|
||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0F, 0x0F, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x00},
|
||||
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x3C, 0x7E, 0x7E, 0x7E, 0x7E, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8, 0xF0, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||
};
|
||||
|
||||
|
||||
// SSD1306 Commandset
|
||||
// ------------------
|
||||
// Fundamental Commands
|
||||
#define ASA_DISPLAY_ALL_ON_RESUME 0xA4
|
||||
// Addressing Setting Commands
|
||||
#define ASA_MEMORY_ADDR_MODE 0x20
|
||||
// Hardware Configuration Commands
|
||||
#define ASA_SET_START_LINE 0x40
|
||||
#define ASA_SET_SEGMENT_REMAP 0xA0
|
||||
#define ASA_SET_MULTIPLEX_RATIO 0xA8
|
||||
#define ASA_COM_SCAN_DIR_DEC 0xC8
|
||||
#define ASA_SET_DISPLAY_OFFSET 0xD3
|
||||
#define ASA_SET_COM_PINS 0xDA
|
||||
#define ASA_CHARGE_PUMP 0x8D
|
||||
// Timing & Driving Scheme Setting Commands
|
||||
#define ASA_SET_DISPLAY_CLOCK_DIV_RATIO 0xD5
|
||||
#define ASA_SET_PRECHARGE_PERIOD 0xD9
|
||||
#define ASA_SET_VCOM_DESELECT 0xDB
|
||||
|
||||
|
||||
|
||||
// ====================== LOW LEVEL =========================
|
||||
|
||||
void OzOLED::sendCommand(byte command){
|
||||
Wire.beginTransmission(OLED_ADDRESS); // begin transmitting
|
||||
Wire.write(OzOLED_COMMAND_MODE);//data mode
|
||||
Wire.write(command);
|
||||
Wire.endTransmission(); // stop transmitting
|
||||
}
|
||||
|
||||
|
||||
void OzOLED::sendData(byte data){
|
||||
|
||||
Wire.beginTransmission(OLED_ADDRESS); // begin transmitting
|
||||
Wire.write(OzOLED_DATA_MODE);//data mode
|
||||
Wire.write(data);
|
||||
Wire.endTransmission(); // stop transmitting
|
||||
|
||||
}
|
||||
|
||||
void OzOLED::printChar(char C, byte X, byte Y){
|
||||
|
||||
if ( X < 128 )
|
||||
setCursorXY(X, Y);
|
||||
|
||||
//Ignore unused ASCII characters. Modified the range to support multilingual characters.
|
||||
if(C < 32 || C > 127)
|
||||
C='*'; //star - indicate characters that can't be displayed
|
||||
|
||||
|
||||
for(byte i=0; i<8; i++) {
|
||||
|
||||
//read bytes from code memory
|
||||
sendData(pgm_read_byte(&BasicFont[C-32][i])); //font array starts at 0, ASCII starts at 32. Hence the translation
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void OzOLED::printString(const char *String, byte X, byte Y, byte numChar){
|
||||
|
||||
if ( X < 128 )
|
||||
setCursorXY(X, Y);
|
||||
|
||||
|
||||
byte count=0;
|
||||
while(String[count] && count<numChar){
|
||||
printChar(String[count++]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
byte OzOLED::printNumber(long long_num, byte X, byte Y){
|
||||
|
||||
if ( X < 128 )
|
||||
setCursorXY(X, Y);
|
||||
|
||||
|
||||
byte char_buffer[10] = "";
|
||||
byte i = 0;
|
||||
byte f = 0; // number of characters
|
||||
|
||||
if (long_num < 0) {
|
||||
|
||||
f++;
|
||||
printChar('-');
|
||||
long_num = -long_num;
|
||||
|
||||
}
|
||||
else if (long_num == 0) {
|
||||
|
||||
f++;
|
||||
printChar('0');
|
||||
return f;
|
||||
|
||||
}
|
||||
|
||||
while (long_num > 0) {
|
||||
|
||||
char_buffer[i++] = long_num % 10;
|
||||
long_num /= 10;
|
||||
|
||||
}
|
||||
|
||||
f += i;
|
||||
for(; i > 0; i--) {
|
||||
|
||||
printChar('0'+ char_buffer[i - 1]);
|
||||
|
||||
}
|
||||
|
||||
return f;
|
||||
|
||||
}
|
||||
|
||||
byte OzOLED::printNumber16(long long_num, byte X, byte Y){
|
||||
|
||||
if ( X < 128 )
|
||||
setCursorXY(X, Y);
|
||||
|
||||
|
||||
byte char_buffer[10] = "";
|
||||
byte i = 0;
|
||||
byte f = 0; // number of characters
|
||||
|
||||
if (long_num < 0) {
|
||||
|
||||
f++;
|
||||
printChar16('-', X, Y);
|
||||
long_num = -long_num;
|
||||
|
||||
}
|
||||
else if (long_num == 0) {
|
||||
|
||||
f++;
|
||||
printChar16('0', X, Y);
|
||||
return f;
|
||||
|
||||
}
|
||||
|
||||
while (long_num > 0) {
|
||||
|
||||
char_buffer[i++] = long_num % 10;
|
||||
long_num /= 10;
|
||||
|
||||
}
|
||||
|
||||
f += i;
|
||||
for(; i > 0; i--) {
|
||||
|
||||
printChar16('0'+ char_buffer[i - 1], X, Y);
|
||||
X += 2;
|
||||
|
||||
}
|
||||
|
||||
return f;
|
||||
|
||||
}
|
||||
|
||||
|
||||
byte OzOLED::printNumber(float float_num, byte prec, byte X, byte Y){
|
||||
|
||||
if ( X < 128 )
|
||||
setCursorXY(X, Y);
|
||||
|
||||
// prec - 6 maximum
|
||||
|
||||
byte num_int = 0;
|
||||
byte num_frac = 0;
|
||||
byte num_extra = 0;
|
||||
|
||||
long d = float_num; // get the integer part
|
||||
float f = float_num - d; // get the fractional part
|
||||
|
||||
|
||||
if (d == 0 && f < 0.0){
|
||||
|
||||
printChar('-');
|
||||
num_extra++;
|
||||
printChar('0');
|
||||
num_extra++;
|
||||
f *= -1;
|
||||
|
||||
}
|
||||
else if (d < 0 && f < 0.0){
|
||||
|
||||
num_int = printNumber(d); // count how many digits in integer part
|
||||
f *= -1;
|
||||
|
||||
}
|
||||
else{
|
||||
|
||||
num_int = printNumber(d); // count how many digits in integer part
|
||||
|
||||
}
|
||||
|
||||
// only when fractional part > 0, we show decimal point
|
||||
if (f > 0.0){
|
||||
|
||||
printChar('.');
|
||||
num_extra++;
|
||||
|
||||
long f_shift = 1;
|
||||
|
||||
if (num_int + prec > 8)
|
||||
prec = 8 - num_int;
|
||||
|
||||
for (byte j=0; j<prec; j++){
|
||||
f_shift *= 10;
|
||||
}
|
||||
|
||||
num_frac = printNumber((long)(f*f_shift)); // count how many digits in fractional part
|
||||
|
||||
}
|
||||
|
||||
return num_int + num_frac + num_extra;
|
||||
|
||||
}
|
||||
|
||||
unsigned int EnlardeByte2Word(char b)
|
||||
{
|
||||
unsigned int d = 0;
|
||||
for (byte i = 0; i < 8; i++)
|
||||
{
|
||||
unsigned int e = (((unsigned int)b) & (1 << i)) << i;
|
||||
d = d | e | (e << 1);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void OzOLED::printChar16(char C, byte X, byte Y){
|
||||
setCursorXY(X, Y);
|
||||
if(C < 32 || C > 127) //Ignore unused ASCII characters.
|
||||
C='*'; //star - indicate characters that can't be displayed
|
||||
unsigned int m = 0;
|
||||
unsigned char n[8];
|
||||
|
||||
for ( byte i=0; i<8; i++) {
|
||||
m = EnlardeByte2Word(pgm_read_byte(&BasicFont[C-32][i]));
|
||||
sendData(lowByte(m));
|
||||
sendData(lowByte(m));
|
||||
n[i] = highByte(m);
|
||||
}
|
||||
setCursorXY(X, Y+1);
|
||||
|
||||
for(byte i=0; i<8; i++) {
|
||||
sendData(n[i]); //font array starts at 0, ASCII starts at 32. Hence the translation
|
||||
sendData(n[i]); //font array starts at 0, ASCII starts at 32. Hence the translation
|
||||
}
|
||||
}
|
||||
|
||||
void OzOLED::printString16(const char *String, byte X, byte Y, byte numChar){
|
||||
byte count=0;
|
||||
while(String[count] && count<numChar){
|
||||
printChar16(String[count++], X, Y);
|
||||
X += 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OzOLED::printBigNumber(const char *number, byte X, byte Y, byte numChar){
|
||||
// big number pixels: 24 x 32
|
||||
|
||||
// Y - page
|
||||
byte column = 0;
|
||||
byte count = 0;
|
||||
|
||||
while(number[count] && count<numChar){
|
||||
|
||||
|
||||
setCursorXY(X, Y);
|
||||
|
||||
for(byte i=0; i<96; i++) {
|
||||
|
||||
// if character is not "0-9" or ':'
|
||||
if(number[count] < 48 || number[count] > 58)
|
||||
sendData(0);
|
||||
else
|
||||
sendData(pgm_read_byte(&bigNumbers[number[count]-48][i]));
|
||||
|
||||
|
||||
if(column >= 23){
|
||||
column = 0;
|
||||
setCursorXY(X, ++Y);
|
||||
}
|
||||
else
|
||||
column++;
|
||||
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
X = X + 3;
|
||||
Y = Y - 4;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void OzOLED::drawBitmap(const byte *bitmaparray, byte X, byte Y, byte width, byte height){
|
||||
|
||||
// max width = 16
|
||||
// max height = 8
|
||||
|
||||
setCursorXY( X, Y );
|
||||
|
||||
byte column = 0;
|
||||
for(int i=0; i<width*8*height; i++) {
|
||||
|
||||
sendData(pgm_read_byte(&bitmaparray[i]));
|
||||
|
||||
if(++column == width*8) {
|
||||
column = 0;
|
||||
setCursorXY( X, ++Y );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================== High Level ===========================
|
||||
|
||||
|
||||
void OzOLED::init(){
|
||||
Wire.begin();
|
||||
|
||||
// upgrade to 400KHz! (only use when your other i2c device support this speed)
|
||||
if (I2C_400KHZ){
|
||||
// save I2C bitrate (default 100Khz)
|
||||
byte twbrbackup = TWBR;
|
||||
TWBR = 12;
|
||||
//TWBR = twbrbackup;
|
||||
//Serial.println(TWBR, DEC);
|
||||
//Serial.println(TWSR & 0x3, DEC);
|
||||
}
|
||||
|
||||
setPowerOff(); //display off
|
||||
delay(10);
|
||||
setPowerOn(); //display on
|
||||
delay(10);
|
||||
setNormalDisplay(); //default Set Normal Display
|
||||
setPageMode(); // default addressing mode
|
||||
clearDisplay();
|
||||
setCursorXY(0,0);
|
||||
|
||||
// Additional command
|
||||
OzOled.setPowerOff();
|
||||
OzOled.sendCommand(ASA_SET_DISPLAY_CLOCK_DIV_RATIO);
|
||||
OzOled.sendCommand(0x80);
|
||||
OzOled.sendCommand(ASA_SET_MULTIPLEX_RATIO);
|
||||
OzOled.sendCommand(0x3F);
|
||||
OzOled.sendCommand(ASA_SET_DISPLAY_OFFSET);
|
||||
OzOled.sendCommand(0x0);
|
||||
OzOled.sendCommand(ASA_SET_START_LINE | 0x0);
|
||||
OzOled.sendCommand(ASA_CHARGE_PUMP);
|
||||
OzOled.sendCommand(0x14);
|
||||
OzOled.sendCommand(ASA_MEMORY_ADDR_MODE);
|
||||
OzOled.sendCommand(0x00);
|
||||
OzOled.sendCommand(ASA_SET_SEGMENT_REMAP | 0x1);
|
||||
OzOled.sendCommand(ASA_COM_SCAN_DIR_DEC);
|
||||
OzOled.sendCommand(ASA_SET_COM_PINS);
|
||||
OzOled.sendCommand(0x12);
|
||||
OzOled.setBrightness(0xCF);
|
||||
OzOled.sendCommand(ASA_SET_PRECHARGE_PERIOD);
|
||||
OzOled.sendCommand(0xF1);
|
||||
OzOled.sendCommand(ASA_SET_VCOM_DESELECT);
|
||||
OzOled.sendCommand(0x40);
|
||||
OzOled.sendCommand(ASA_DISPLAY_ALL_ON_RESUME);
|
||||
OzOled.setNormalDisplay();
|
||||
OzOled.setPowerOn();
|
||||
|
||||
}
|
||||
|
||||
void OzOLED::setCursorXY(byte X, byte Y){
|
||||
// Y - 1 unit = 1 page (8 pixel rows)
|
||||
// X - 1 unit = 8 pixel columns
|
||||
|
||||
sendCommand(0x00 + (8*X & 0x0F)); //set column lower address
|
||||
sendCommand(0x10 + ((8*X>>4)&0x0F)); //set column higher address
|
||||
sendCommand(0xB0 + Y); //set page address
|
||||
|
||||
}
|
||||
|
||||
|
||||
void OzOLED::clearDisplay() {
|
||||
|
||||
|
||||
for(byte page=0; page<8; page++) {
|
||||
|
||||
setCursorXY(0, page);
|
||||
for(byte column=0; column<128; column++){ //clear all columns
|
||||
sendData(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setCursorXY(0,0);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
void OzOLED::clearPage(byte page) {
|
||||
// clear page and set cursor at beginning of that page
|
||||
|
||||
setCursorXY(0, page);
|
||||
for(byte column=0; column<128; column++){ //clear all columns
|
||||
sendData(0x00);
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
void OzOLED::setInverseDisplay(){
|
||||
|
||||
sendCommand(OzOLED_CMD_INVERSE_DISPLAY);
|
||||
|
||||
}
|
||||
|
||||
void OzOLED::setNormalDisplay(){
|
||||
|
||||
sendCommand(OzOLED_CMD_NORMAL_DISPLAY);
|
||||
|
||||
}
|
||||
|
||||
void OzOLED::setPowerOff(){
|
||||
|
||||
sendCommand(OzOLED_CMD_DISPLAY_OFF);
|
||||
|
||||
}
|
||||
|
||||
void OzOLED::setPowerOn(){
|
||||
|
||||
sendCommand(OzOLED_CMD_DISPLAY_ON);
|
||||
|
||||
}
|
||||
|
||||
void OzOLED::setBrightness(byte Brightness){
|
||||
|
||||
sendCommand(OzOLED_CMD_SET_BRIGHTNESS);
|
||||
sendCommand(Brightness);
|
||||
|
||||
}
|
||||
|
||||
void OzOLED::setPageMode(){
|
||||
addressingMode = PAGE_ADDRESSING;
|
||||
sendCommand(0x20); //set addressing mode
|
||||
sendCommand(PAGE_ADDRESSING); //set page addressing mode
|
||||
}
|
||||
|
||||
void OzOLED::setHorizontalMode(){
|
||||
addressingMode = HORIZONTAL_ADDRESSING;
|
||||
sendCommand(0x20); //set addressing mode
|
||||
sendCommand(HORIZONTAL_ADDRESSING); //set page addressing mode
|
||||
}
|
||||
|
||||
|
||||
// startscrollright
|
||||
// Activate a right handed scroll for rows start through stop
|
||||
// Hint, the display is 16 rows tall. To scroll the whole display, run:
|
||||
// scrollRight(0x00, 0x0F) - start - stop
|
||||
void OzOLED::scrollRight(byte start, byte end, byte speed){
|
||||
|
||||
sendCommand(OzOLED_RIGHT_SCROLL); //Horizontal Scroll Setup
|
||||
sendCommand(0x00); // dummy byte
|
||||
sendCommand(start); // start page address
|
||||
sendCommand(speed); // set time interval between each scroll
|
||||
sendCommand(end); // end page address
|
||||
|
||||
sendCommand(0x01);
|
||||
sendCommand(0xFF);
|
||||
|
||||
sendCommand(0x2f); //active scrolling
|
||||
|
||||
}
|
||||
|
||||
|
||||
// startscrollleft
|
||||
// Activate a right handed scroll for rows start through stop
|
||||
// Hint, the display is 16 rows tall. To scroll the whole display, run:
|
||||
// display.scrollright(0x00, 0x0F) - start - stop
|
||||
void OzOLED::scrollLeft(byte start, byte end, byte speed){
|
||||
|
||||
sendCommand(OzOLED_LEFT_SCROLL); //Horizontal Scroll Setup
|
||||
sendCommand(0x00); // dummy byte
|
||||
sendCommand(start); // start page address
|
||||
sendCommand(speed); // set time interval between each scroll
|
||||
sendCommand(end); // end page address
|
||||
|
||||
sendCommand(0x01);
|
||||
sendCommand(0xFF);
|
||||
|
||||
sendCommand(0x2f); //active scrolling
|
||||
|
||||
}
|
||||
|
||||
// startscrolldiagright
|
||||
// Activate a diagonal scroll for rows start through stop
|
||||
// Hint, the display is 16 rows tall. To scroll the whole display, run:
|
||||
// display.scrollright(0x00, 0x0F)
|
||||
void OzOLED::scrollDiagRight(){
|
||||
|
||||
sendCommand(OzOLED_SET_VERTICAL_SCROLL_AREA);
|
||||
sendCommand(0X00);
|
||||
sendCommand(OzOLED_Max_Y);
|
||||
sendCommand(OzOLED_VERTICAL_RIGHT_SCROLL); //Vertical and Horizontal Scroll Setup
|
||||
sendCommand(0X00); //dummy byte
|
||||
sendCommand(0x00); //define page0 as startpage address
|
||||
sendCommand(0X00); //set time interval between each scroll ste as 6 frames
|
||||
sendCommand(0x07); //define page7 as endpage address
|
||||
sendCommand(0X01); //set vertical scrolling offset as 1 row
|
||||
sendCommand(OzOLED_CMD_ACTIVATE_SCROLL); //active scrolling
|
||||
|
||||
}
|
||||
|
||||
void OzOLED::scrollDiagLeft(){
|
||||
|
||||
sendCommand(OzOLED_SET_VERTICAL_SCROLL_AREA);
|
||||
sendCommand(0X00);
|
||||
sendCommand(OzOLED_Max_Y);
|
||||
sendCommand(OzOLED_VERTICAL_LEFT_SCROLL); //Vertical and Horizontal Scroll Setup
|
||||
sendCommand(0X00); //dummy byte
|
||||
sendCommand(0x00); //define page0 as startpage address
|
||||
sendCommand(0X00); //set time interval between each scroll ste as 6 frames
|
||||
sendCommand(0x07); //define page7 as endpage address
|
||||
sendCommand(0X01); //set vertical scrolling offset as 1 row
|
||||
sendCommand(OzOLED_CMD_ACTIVATE_SCROLL); //active scrolling
|
||||
|
||||
}
|
||||
|
||||
|
||||
void OzOLED::setActivateScroll(byte direction, byte startPage, byte endPage, byte scrollSpeed){
|
||||
|
||||
|
||||
/*
|
||||
This function is still not complete, we need more testing also.
|
||||
Use the following defines for 'direction' :
|
||||
|
||||
Scroll_Left
|
||||
Scroll_Right
|
||||
|
||||
For Scroll_vericle, still need to debug more...
|
||||
|
||||
Use the following defines for 'scrollSpeed' :
|
||||
|
||||
Scroll_2Frames
|
||||
Scroll_3Frames
|
||||
Scroll_4Frames
|
||||
Scroll_5Frames
|
||||
Scroll_25Frames
|
||||
Scroll_64Frames
|
||||
Scroll_128Frames
|
||||
Scroll_256Frames
|
||||
|
||||
*/
|
||||
|
||||
|
||||
if(direction == Scroll_Right) {
|
||||
|
||||
//Scroll Right
|
||||
sendCommand(0x26);
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
//Scroll Left
|
||||
sendCommand(0x27);
|
||||
|
||||
}
|
||||
/*
|
||||
else if (direction == Scroll_Up ){
|
||||
|
||||
//Scroll Up
|
||||
sendCommand(0x29);
|
||||
|
||||
}
|
||||
else{
|
||||
|
||||
//Scroll Down
|
||||
sendCommand(0x2A);
|
||||
|
||||
}
|
||||
*/
|
||||
sendCommand(0x00);//dummy byte
|
||||
sendCommand(startPage);
|
||||
sendCommand(scrollSpeed);
|
||||
sendCommand(endPage); // for verticle scrolling, use 0x29 as command, endPage should = start page = 0
|
||||
|
||||
/*
|
||||
if(direction == Scroll_Up) {
|
||||
|
||||
sendCommand(0x01);
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
sendCommand(OzOLED_CMD_ACTIVATE_SCROLL);
|
||||
|
||||
}
|
||||
|
||||
void OzOLED::setDeactivateScroll(){
|
||||
|
||||
sendCommand(OzOLED_CMD_DEACTIVATE_SCROLL);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
OzOLED OzOled; // Preinstantiate Objects
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
OzOLED.h - 0.96' I2C 128x64 OLED Driver Library
|
||||
2014 Copyright (c) OscarLiang.net All right reserved.
|
||||
|
||||
Author: Oscar Liang
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef OzOLED_data_H
|
||||
#define OzOLED_data_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define OzOLED_Max_X 128 //128 Pixels
|
||||
#define OzOLED_Max_Y 64 //64 Pixels
|
||||
|
||||
#define OLED_ADDRESS 0x3C
|
||||
#define I2C_400KHZ 1 // 0 to use default 100Khz, 1 for 400Khz
|
||||
|
||||
// registers
|
||||
#define OzOLED_COMMAND_MODE 0x80
|
||||
#define OzOLED_DATA_MODE 0x40
|
||||
|
||||
// commands
|
||||
#define OzOLED_CMD_DISPLAY_OFF 0xAE
|
||||
#define OzOLED_CMD_DISPLAY_ON 0xAF
|
||||
#define OzOLED_CMD_NORMAL_DISPLAY 0xA6
|
||||
#define OzOLED_CMD_INVERSE_DISPLAY 0xA7
|
||||
#define OzOLED_CMD_SET_BRIGHTNESS 0x81
|
||||
|
||||
#define OzOLED_RIGHT_SCROLL 0x26
|
||||
#define OzOLED_LEFT_SCROLL 0x27
|
||||
#define OzOLED_SET_VERTICAL_SCROLL_AREA 0xA3
|
||||
#define OzOLED_VERTICAL_RIGHT_SCROLL 0x29
|
||||
#define OzOLED_VERTICAL_LEFT_SCROLL 0x2A
|
||||
#define OzOLED_CMD_ACTIVATE_SCROLL 0x2F
|
||||
#define OzOLED_CMD_DEACTIVATE_SCROLL 0x2E
|
||||
|
||||
#define HORIZONTAL_ADDRESSING 0x00
|
||||
#define PAGE_ADDRESSING 0x02
|
||||
|
||||
#define Scroll_Left 0x00
|
||||
#define Scroll_Right 0x01
|
||||
#define Scroll_Up 0x02
|
||||
#define Scroll_Down 0x03
|
||||
|
||||
#define Scroll_2Frames 0x07
|
||||
#define Scroll_3Frames 0x04
|
||||
#define Scroll_4Frames 0x05
|
||||
#define Scroll_5Frames 0x00
|
||||
#define Scroll_25Frames 0x06
|
||||
#define Scroll_64Frames 0x01
|
||||
#define Scroll_128Frames 0x02
|
||||
#define Scroll_256Frames 0x03
|
||||
|
||||
|
||||
class OzOLED {
|
||||
|
||||
public:
|
||||
|
||||
byte addressingMode;
|
||||
|
||||
void sendCommand(byte command);
|
||||
void sendData(byte Data);
|
||||
|
||||
void printChar(char c, byte X=255, byte Y=255);
|
||||
void printString(const char *String, byte X=255, byte Y=255, byte numChar=255);
|
||||
byte printNumber(long n, byte X=255, byte Y=255);
|
||||
byte printNumber(float float_num, byte prec=6, byte Y=255, byte numChar=255);
|
||||
void printBigNumber(const char *number, byte column=0, byte page=0, byte numChar=255);
|
||||
void drawBitmap(const byte *bitmaparray, byte X, byte Y, byte width, byte height);
|
||||
void printChar16(char c, byte X=255, byte Y=255); // font 16x16
|
||||
void printString16(const char *String, byte X, byte Y, byte numChar=255); // font 16x16
|
||||
byte printNumber16(long n, byte X=255, byte Y=255);
|
||||
void init();
|
||||
|
||||
void setCursorXY(byte Column, byte Row);
|
||||
void clearDisplay();
|
||||
//void clearPage(byte page);
|
||||
|
||||
void setNormalDisplay();
|
||||
void setInverseDisplay();
|
||||
void setPowerOff();
|
||||
void setPowerOn();
|
||||
void setPageMode();
|
||||
void setHorizontalMode();
|
||||
void setBrightness(byte Brightness);
|
||||
|
||||
void scrollRight(byte start, byte end, byte speed);
|
||||
void scrollLeft(byte start, byte end, byte speed);
|
||||
void scrollDiagRight();
|
||||
void scrollDiagLeft();
|
||||
void setActivateScroll(byte direction, byte startPage, byte endPage, byte scrollSpeed);
|
||||
void setDeactivateScroll();
|
||||
|
||||
};
|
||||
|
||||
extern OzOLED OzOled; // OzOLED object
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -0,0 +1,63 @@
|
|||
// source http://qaru.site/questions/186859/how-to-use-date-and-time-predefined-macros-in-as-two-integers-then-stringify
|
||||
|
||||
#ifndef BUILD_DEFS_H
|
||||
#define BUILD_DEFS_H
|
||||
// Example of __DATE__ string: "Jul 27 2012"
|
||||
// 01234567890
|
||||
|
||||
#define BUILD_YEAR_CH0 (__DATE__[7]-'0')
|
||||
#define BUILD_YEAR_CH1 (__DATE__[8]-'0')
|
||||
#define BUILD_YEAR_CH2 (__DATE__[9]-'0')
|
||||
#define BUILD_YEAR_CH3 (__DATE__[10]-'0')
|
||||
#define BUILD_YEAR (BUILD_YEAR_CH0*1000+BUILD_YEAR_CH1*100 + BUILD_YEAR_CH2*10+BUILD_YEAR_CH3)
|
||||
|
||||
#define BUILD_MONTH_IS_JAN (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n')
|
||||
#define BUILD_MONTH_IS_FEB (__DATE__[0] == 'F')
|
||||
#define BUILD_MONTH_IS_MAR (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r')
|
||||
#define BUILD_MONTH_IS_APR (__DATE__[0] == 'A' && __DATE__[1] == 'p')
|
||||
#define BUILD_MONTH_IS_MAY (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y')
|
||||
#define BUILD_MONTH_IS_JUN (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n')
|
||||
#define BUILD_MONTH_IS_JUL (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l')
|
||||
#define BUILD_MONTH_IS_AUG (__DATE__[0] == 'A' && __DATE__[1] == 'u')
|
||||
#define BUILD_MONTH_IS_SEP (__DATE__[0] == 'S')
|
||||
#define BUILD_MONTH_IS_OCT (__DATE__[0] == 'O')
|
||||
#define BUILD_MONTH_IS_NOV (__DATE__[0] == 'N')
|
||||
#define BUILD_MONTH_IS_DEC (__DATE__[0] == 'D')
|
||||
|
||||
#define BUILD_MONTH \
|
||||
( \
|
||||
(BUILD_MONTH_IS_JAN) ? 1 : \
|
||||
(BUILD_MONTH_IS_FEB) ? 2 : \
|
||||
(BUILD_MONTH_IS_MAR) ? 3 : \
|
||||
(BUILD_MONTH_IS_APR) ? 4 : \
|
||||
(BUILD_MONTH_IS_MAY) ? 5 : \
|
||||
(BUILD_MONTH_IS_JUN) ? 6 : \
|
||||
(BUILD_MONTH_IS_JUL) ? 7 : \
|
||||
(BUILD_MONTH_IS_AUG) ? 8 : \
|
||||
(BUILD_MONTH_IS_SEP) ? 9 : \
|
||||
(BUILD_MONTH_IS_OCT) ? 10 : \
|
||||
(BUILD_MONTH_IS_NOV) ? 11 : \
|
||||
(BUILD_MONTH_IS_DEC) ? 12 : \
|
||||
/* error default */ '?' \
|
||||
)
|
||||
|
||||
#define BUILD_DAY_CH0 (((__DATE__[4] >= '0') ? (__DATE__[4]) : '0')-'0')
|
||||
#define BUILD_DAY_CH1 (__DATE__[5]-'0')
|
||||
#define BUILD_DAY (BUILD_DAY_CH0*10+BUILD_DAY_CH1)
|
||||
|
||||
// Example of __TIME__ string: "21:06:19"
|
||||
// 01234567
|
||||
|
||||
#define BUILD_HOUR_CH0 (__TIME__[0]-'0')
|
||||
#define BUILD_HOUR_CH1 (__TIME__[1]-'0')
|
||||
#define BUILD_HOUR (BUILD_HOUR_CH0*10+BUILD_HOUR_CH1)
|
||||
|
||||
#define BUILD_MIN_CH0 (__TIME__[3]-'0')
|
||||
#define BUILD_MIN_CH1 (__TIME__[4]-'0')
|
||||
#define BUILD_MIN (BUILD_MIN_CH0*10+BUILD_MIN_CH1)
|
||||
|
||||
#define BUILD_SEC_CH0 (__TIME__[6]-'0')
|
||||
#define BUILD_SEC_CH1 (__TIME__[7]-'0')
|
||||
#define BUILD_SEC (BUILD_SEC_CH0*10+BUILD_SEC_CH1)
|
||||
|
||||
#endif // BUILD_DEFS_H
|
|
@ -0,0 +1,311 @@
|
|||
//
|
||||
// FILE: dhtnew.cpp
|
||||
// AUTHOR: Rob.Tillaart@gmail.com
|
||||
// VERSION: 0.4.1
|
||||
// PURPOSE: DHT Temperature & Humidity Sensor library for Arduino
|
||||
// URL: https://github.com/RobTillaart/DHTNEW
|
||||
//
|
||||
// HISTORY:
|
||||
// 0.1.0 2017-07-24 initial version based upon DHTStable
|
||||
// 0.1.1 2017-07-29 add begin() to determine type once and for all instead of every call + refactor
|
||||
// 0.1.2 2018-01-08 improved begin() + refactor()
|
||||
// 0.1.3 2018-01-08 removed begin() + moved detection to read() function
|
||||
// 0.1.4 2018-04-03 add get-/setDisableIRQ(bool b)
|
||||
// 0.1.5 2019-01-20 fix negative temperature DHT22 - issue #120
|
||||
// 0.1.6 2020-04-09 #pragma once, readme.md, own repo
|
||||
// 0.1.7 2020-05-01 prevent premature read; add waitForReading flag (Kudo's to Mr-HaleYa),
|
||||
// 0.2.0 2020-05-02 made temperature and humidity private (Kudo's to Mr-HaleYa),
|
||||
// 0.2.1 2020-05-27 Fix #11 - Adjust bit timing threshold
|
||||
// 0.2.2 2020-06-08 added ERROR_SENSOR_NOT_READY and differentiate timeout errors
|
||||
// 0.3.0 2020-06-12 added getReadDelay & setReadDelay to tune reading interval
|
||||
// removed get/setDisableIRQ; adjusted wakeup timing; refactor
|
||||
// 0.3.1 2020-07-08 added powerUp() powerDown();
|
||||
// 0.3.2 2020-07-17 fix #23 added get/setSuppressError(); overrulable DHTLIB_INVALID_VALUE
|
||||
// 0.3.3 2020-08-18 fix #29, create explicit delay between pulling line HIGH and
|
||||
// waiting for LOW in handshake to trigger the sensor.
|
||||
// On fast ESP32 this fails because the capacity / voltage of the long wire
|
||||
// cannot rise fast enough to be read back as HIGH.
|
||||
// 0.3.4 2020-09-23 Added **waitFor(state, timeout)** to follow timing from datasheet.
|
||||
// Restored disableIRQ flag as problems occured on AVR. The default of
|
||||
// this flag on AVR is false so interrupts are allowed.
|
||||
// This need some investigation
|
||||
// Fix wake up timing for DHT11 as it does not behave according datasheet.
|
||||
// fix wakeupDelay bug in setType();
|
||||
// 0.4.0 2020-11-10 added DHTLIB_WAITING_FOR_READ as return value of read (minor break of interface)
|
||||
// 0.4.1 2020-11-11 getType() attempts to detect sensor type
|
||||
|
||||
#include "dhtnew.h"
|
||||
|
||||
// these defines are not for user to adjust
|
||||
#define DHTLIB_DHT11_WAKEUP 18
|
||||
#define DHTLIB_DHT_WAKEUP 1
|
||||
|
||||
// READ_DELAY for blocking read
|
||||
// datasheet: DHT11 = 1000 and DHT22 = 2000
|
||||
// use setReadDelay() to overrule (at own risk)
|
||||
// as individual sensors can be read faster.
|
||||
// see example DHTnew_setReadDelay.ino
|
||||
#define DHTLIB_DHT11_READ_DELAY 1000
|
||||
#define DHTLIB_DHT22_READ_DELAY 2000
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//
|
||||
// PUBLIC
|
||||
//
|
||||
DHTNEW::DHTNEW(uint8_t pin)
|
||||
{
|
||||
_dataPin = pin;
|
||||
// Data-bus's free status is high voltage level.
|
||||
pinMode(_dataPin, OUTPUT);
|
||||
digitalWrite(_dataPin, HIGH);
|
||||
_readDelay = 0;
|
||||
#if defined(__AVR__)
|
||||
_disableIRQ = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
uint8_t DHTNEW::getType()
|
||||
{
|
||||
if (_type == 0) read();
|
||||
return _type;
|
||||
}
|
||||
|
||||
void DHTNEW::setType(uint8_t type)
|
||||
{
|
||||
if ((type == 0) || (type == 11))
|
||||
{
|
||||
_type = type;
|
||||
_wakeupDelay = DHTLIB_DHT11_WAKEUP;
|
||||
}
|
||||
if (type == 22)
|
||||
{
|
||||
_type = type;
|
||||
_wakeupDelay = DHTLIB_DHT_WAKEUP;
|
||||
}
|
||||
}
|
||||
|
||||
// return values:
|
||||
// DHTLIB_OK
|
||||
// DHTLIB_WAITING_FOR_READ
|
||||
// DHTLIB_ERROR_CHECKSUM
|
||||
// DHTLIB_ERROR_BIT_SHIFT
|
||||
// DHTLIB_ERROR_SENSOR_NOT_READY
|
||||
// DHTLIB_ERROR_TIMEOUT_A
|
||||
// DHTLIB_ERROR_TIMEOUT_B
|
||||
// DHTLIB_ERROR_TIMEOUT_C
|
||||
// DHTLIB_ERROR_TIMEOUT_D
|
||||
int DHTNEW::read()
|
||||
{
|
||||
if (_readDelay == 0)
|
||||
{
|
||||
_readDelay = DHTLIB_DHT22_READ_DELAY;
|
||||
if (_type == 11) _readDelay = DHTLIB_DHT11_READ_DELAY;
|
||||
}
|
||||
if (_type != 0)
|
||||
{
|
||||
while (millis() - _lastRead < _readDelay)
|
||||
{
|
||||
if (!_waitForRead) return DHTLIB_WAITING_FOR_READ;
|
||||
yield();
|
||||
}
|
||||
return _read();
|
||||
}
|
||||
|
||||
_type = 22;
|
||||
_wakeupDelay = DHTLIB_DHT_WAKEUP;
|
||||
int rv = _read();
|
||||
if (rv == DHTLIB_OK) return rv;
|
||||
|
||||
_type = 11;
|
||||
_wakeupDelay = DHTLIB_DHT11_WAKEUP;
|
||||
rv = _read();
|
||||
if (rv == DHTLIB_OK) return rv;
|
||||
|
||||
_type = 0; // retry next time
|
||||
return rv;
|
||||
}
|
||||
|
||||
// return values:
|
||||
// DHTLIB_OK
|
||||
// DHTLIB_ERROR_CHECKSUM
|
||||
// DHTLIB_ERROR_BIT_SHIFT
|
||||
// DHTLIB_ERROR_SENSOR_NOT_READY
|
||||
// DHTLIB_ERROR_TIMEOUT_A
|
||||
// DHTLIB_ERROR_TIMEOUT_B
|
||||
// DHTLIB_ERROR_TIMEOUT_C
|
||||
// DHTLIB_ERROR_TIMEOUT_D
|
||||
int DHTNEW::_read()
|
||||
{
|
||||
// READ VALUES
|
||||
int rv = _readSensor();
|
||||
interrupts();
|
||||
|
||||
// Data-bus's free status is high voltage level.
|
||||
pinMode(_dataPin, OUTPUT);
|
||||
digitalWrite(_dataPin, HIGH);
|
||||
_lastRead = millis();
|
||||
|
||||
if (rv != DHTLIB_OK)
|
||||
{
|
||||
if (_suppressError == false)
|
||||
{
|
||||
_humidity = DHTLIB_INVALID_VALUE;
|
||||
_temperature = DHTLIB_INVALID_VALUE;
|
||||
}
|
||||
return rv; // propagate error value
|
||||
}
|
||||
|
||||
if (_type == 22) // DHT22, DHT33, DHT44, compatible
|
||||
{
|
||||
_humidity = (_bits[0] * 256 + _bits[1]) * 0.1;
|
||||
_temperature = ((_bits[2] & 0x7F) * 256 + _bits[3]) * 0.1;
|
||||
}
|
||||
else // if (_type == 11) // DHT11, DH12, compatible
|
||||
{
|
||||
_humidity = _bits[0] + _bits[1] * 0.1;
|
||||
_temperature = _bits[2] + _bits[3] * 0.1;
|
||||
}
|
||||
|
||||
if (_bits[2] & 0x80) // negative temperature
|
||||
{
|
||||
_temperature = -_temperature;
|
||||
}
|
||||
_humidity = constrain(_humidity + _humOffset, 0, 100);
|
||||
_temperature += _tempOffset;
|
||||
|
||||
// TEST CHECKSUM
|
||||
uint8_t sum = _bits[0] + _bits[1] + _bits[2] + _bits[3];
|
||||
if (_bits[4] != sum)
|
||||
{
|
||||
return DHTLIB_ERROR_CHECKSUM;
|
||||
}
|
||||
return DHTLIB_OK;
|
||||
}
|
||||
|
||||
void DHTNEW::powerUp()
|
||||
{
|
||||
digitalWrite(_dataPin, HIGH);
|
||||
// do a dummy read to sync the sensor
|
||||
read();
|
||||
};
|
||||
|
||||
void DHTNEW::powerDown()
|
||||
{
|
||||
digitalWrite(_dataPin, LOW);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//
|
||||
// PRIVATE
|
||||
//
|
||||
|
||||
// return values:
|
||||
// DHTLIB_OK
|
||||
// DHTLIB_ERROR_CHECKSUM
|
||||
// DHTLIB_ERROR_BIT_SHIFT
|
||||
// DHTLIB_ERROR_SENSOR_NOT_READY
|
||||
// DHTLIB_ERROR_TIMEOUT_A
|
||||
// DHTLIB_ERROR_TIMEOUT_B
|
||||
// DHTLIB_ERROR_TIMEOUT_C
|
||||
// DHTLIB_ERROR_TIMEOUT_D
|
||||
int DHTNEW::_readSensor()
|
||||
{
|
||||
// INIT BUFFERVAR TO RECEIVE DATA
|
||||
uint8_t mask = 0x80;
|
||||
uint8_t idx = 0;
|
||||
|
||||
// EMPTY BUFFER
|
||||
for (uint8_t i = 0; i < 5; i++) _bits[i] = 0;
|
||||
|
||||
// HANDLE PENDING IRQ
|
||||
yield();
|
||||
|
||||
// REQUEST SAMPLE - SEND WAKEUP TO SENSOR
|
||||
pinMode(_dataPin, OUTPUT);
|
||||
digitalWrite(_dataPin, LOW);
|
||||
// add 10% extra for timing inaccuracies in sensor.
|
||||
delayMicroseconds(_wakeupDelay * 1100UL);
|
||||
|
||||
// HOST GIVES CONTROL TO SENSOR
|
||||
digitalWrite(_dataPin, HIGH);
|
||||
delayMicroseconds(2);
|
||||
pinMode(_dataPin, INPUT_PULLUP);
|
||||
|
||||
// DISABLE INTERRUPTS when clock in the bits
|
||||
if (_disableIRQ) { noInterrupts(); }
|
||||
|
||||
// DHT22
|
||||
// SENSOR PULLS LOW after 20-40 us => if stays HIGH ==> device not ready
|
||||
// timeout is 20 us less due to delay() above
|
||||
// DHT11
|
||||
// SENSOR PULLS LOW after 6000-10000 us
|
||||
uint32_t WAITFORSENSOR = 50;
|
||||
if (_type == 11) WAITFORSENSOR = 15000UL;
|
||||
if (_waitFor(LOW, WAITFORSENSOR)) return DHTLIB_ERROR_SENSOR_NOT_READY;
|
||||
|
||||
// SENSOR STAYS LOW for ~80 us => or TIMEOUT
|
||||
if (_waitFor(HIGH, 90)) return DHTLIB_ERROR_TIMEOUT_A;
|
||||
|
||||
// SENSOR STAYS HIGH for ~80 us => or TIMEOUT
|
||||
if (_waitFor(LOW, 90)) return DHTLIB_ERROR_TIMEOUT_B;
|
||||
|
||||
// SENSOR HAS NOW SEND ACKNOWLEDGE ON WAKEUP
|
||||
// NOW IT SENDS THE BITS
|
||||
|
||||
// READ THE OUTPUT - 40 BITS => 5 BYTES
|
||||
for (uint8_t i = 40; i != 0; i--)
|
||||
{
|
||||
// EACH BIT START WITH ~50 us LOW
|
||||
if (_waitFor(HIGH, 70)) return DHTLIB_ERROR_TIMEOUT_C;
|
||||
|
||||
// DURATION OF HIGH DETERMINES 0 or 1
|
||||
// 26-28 us ==> 0
|
||||
// 70 us ==> 1
|
||||
uint32_t t = micros();
|
||||
if (_waitFor(LOW, 90)) return DHTLIB_ERROR_TIMEOUT_D;
|
||||
if ((micros() - t) > DHTLIB_BIT_THRESHOLD)
|
||||
{
|
||||
_bits[idx] |= mask;
|
||||
}
|
||||
|
||||
// PREPARE FOR NEXT BIT
|
||||
mask >>= 1;
|
||||
if (mask == 0) // next byte?
|
||||
{
|
||||
mask = 0x80;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
// After 40 bits the sensor pulls the line LOW for 50 us
|
||||
// No functional need to wait for this one
|
||||
// if (_waitFor(HIGH, 60)) return DHTLIB_ERROR_TIMEOUT_E;
|
||||
|
||||
// CATCH RIGHTSHIFT BUG ESP (only 1 single bit shift)
|
||||
// humidity is max 1000 = 0x03E8 for DHT22 and 0x6400 for DHT11
|
||||
// so most significant bit may never be set.
|
||||
if (_bits[0] & 0x80) return DHTLIB_ERROR_BIT_SHIFT;
|
||||
|
||||
return DHTLIB_OK;
|
||||
}
|
||||
|
||||
|
||||
// returns true if timeout has passed.
|
||||
// returns false if timeout is not reached and state is seen.
|
||||
bool DHTNEW::_waitFor(uint8_t state, uint32_t timeout)
|
||||
{
|
||||
uint32_t start = micros();
|
||||
uint8_t count = 2;
|
||||
while ((micros() - start) < timeout)
|
||||
{
|
||||
// delayMicroseconds(1); // less # reads ==> minimizes # glitch reads
|
||||
if (digitalRead(_dataPin) == state)
|
||||
{
|
||||
count--;
|
||||
if (count == 0) return false; // requested state seen count times
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -- END OF FILE --
|
|
@ -0,0 +1,112 @@
|
|||
#pragma once
|
||||
//
|
||||
// FILE: dhtnew.h
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.4.1
|
||||
// PURPOSE: DHT Temperature & Humidity Sensor library for Arduino
|
||||
// URL: https://github.com/RobTillaart/DHTNEW
|
||||
//
|
||||
// HISTORY:
|
||||
// see dhtnew.cpp file
|
||||
|
||||
// DHT PIN layout from left to right
|
||||
// =================================
|
||||
// FRONT : DESCRIPTION
|
||||
// pin 1 : VCC
|
||||
// pin 2 : DATA
|
||||
// pin 3 : Not Connected
|
||||
// pin 4 : GND
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define DHTNEW_LIB_VERSION "0.4.0"
|
||||
|
||||
#define DHTLIB_OK 0
|
||||
#define DHTLIB_ERROR_CHECKSUM -1
|
||||
#define DHTLIB_ERROR_TIMEOUT_A -2
|
||||
#define DHTLIB_ERROR_BIT_SHIFT -3
|
||||
#define DHTLIB_ERROR_SENSOR_NOT_READY -4
|
||||
#define DHTLIB_ERROR_TIMEOUT_C -5
|
||||
#define DHTLIB_ERROR_TIMEOUT_D -6
|
||||
#define DHTLIB_ERROR_TIMEOUT_B -7
|
||||
#define DHTLIB_WAITING_FOR_READ -8
|
||||
|
||||
#ifndef DHTLIB_INVALID_VALUE
|
||||
#define DHTLIB_INVALID_VALUE -999
|
||||
#endif
|
||||
|
||||
|
||||
// bits are timing based (datasheet)
|
||||
// 26-28us ==> 0
|
||||
// 70 us ==> 1
|
||||
// See https://github.com/RobTillaart/DHTNew/issues/11
|
||||
#ifndef DHTLIB_BIT_THRESHOLD
|
||||
#define DHTLIB_BIT_THRESHOLD 50
|
||||
#endif
|
||||
|
||||
|
||||
class DHTNEW
|
||||
{
|
||||
public:
|
||||
|
||||
DHTNEW(uint8_t pin);
|
||||
|
||||
// 0 = unknown, 11 or 22
|
||||
uint8_t getType();
|
||||
void setType(uint8_t type = 0);
|
||||
int read();
|
||||
|
||||
// lastRead is in MilliSeconds since start sketch
|
||||
uint32_t lastRead() { return _lastRead; };
|
||||
|
||||
// preferred interface
|
||||
float getHumidity() { return _humidity; };
|
||||
float getTemperature() { return _temperature; };
|
||||
|
||||
// adding offsets works well in normal range
|
||||
// might introduce under- or overflow at the ends of the sensor range
|
||||
void setHumOffset(float offset) { _humOffset = offset; };
|
||||
void setTempOffset(float offset) { _tempOffset = offset; };
|
||||
float getHumOffset() { return _humOffset; };
|
||||
float getTempOffset() { return _tempOffset; };
|
||||
|
||||
bool getDisableIRQ() { return _disableIRQ; };
|
||||
void setDisableIRQ(bool b ) { _disableIRQ = b; };
|
||||
|
||||
bool getWaitForReading() { return _waitForRead; };
|
||||
void setWaitForReading(bool b ) { _waitForRead = b; };
|
||||
|
||||
// set readDelay to 0 will reset to datasheet values
|
||||
uint16_t getReadDelay() { return _readDelay; };
|
||||
void setReadDelay(uint16_t rd = 0) { _readDelay = rd; };
|
||||
|
||||
// minimal support for low power applications.
|
||||
// after powerUp one must wait up to two seconds.
|
||||
void powerUp();
|
||||
void powerDown();
|
||||
|
||||
// suppress error values of -999 => check return value of read() instead
|
||||
bool getSuppressError() { return _suppressError; };
|
||||
void setSuppressError(bool b) { _suppressError = b; };
|
||||
|
||||
private:
|
||||
uint8_t _dataPin = 0;
|
||||
uint8_t _wakeupDelay = 0;
|
||||
uint8_t _type = 0;
|
||||
float _humOffset = 0.0;
|
||||
float _tempOffset = 0.0;
|
||||
float _humidity;
|
||||
float _temperature;
|
||||
uint32_t _lastRead = 0;
|
||||
bool _disableIRQ = true;
|
||||
bool _waitForRead = false;
|
||||
bool _suppressError = false;
|
||||
uint16_t _readDelay = 0;
|
||||
|
||||
uint8_t _bits[5]; // buffer to receive data
|
||||
int _read();
|
||||
int _readSensor();
|
||||
bool _waitFor(uint8_t state, uint32_t timeout);
|
||||
};
|
||||
|
||||
// -- END OF FILE --
|
|
@ -0,0 +1,101 @@
|
|||
#include "iarduino_NeoPixel.h"
|
||||
iarduino_NeoPixel::iarduino_NeoPixel(uint8_t i, uint16_t j){
|
||||
pinOutput = i; // Сохраняем номер выхода, к которому подключены светодиоды
|
||||
lenLED = j; // Сохраняем количество подключённых светодиодов
|
||||
portOutput = portOutputRegister(digitalPinToPort(pinOutput)); // Получаем указатель на адрес регистра выходных значений порта на котором присутствует выход pinOutput. Функция digitalPinToPort возвращает номер, соответствующий порту на котором находится вывод pinOutput, а функция portOutputRegister возвращает указатель на адрес регистра выходных значений этого порта
|
||||
pinMask = digitalPinToBitMask(pinOutput); // Получаем маску вывода, это байт в котором установлен только один бит. Порядковый номер этого бита, соответствует номеру вывода pinOutput (от 0 до 7) на порту portOutput
|
||||
}
|
||||
|
||||
bool iarduino_NeoPixel::begin(void){
|
||||
pinMode (pinOutput, OUTPUT); // Конфигурируем вывод pinOutput как выход
|
||||
digitalWrite (pinOutput, LOW); // Устанавливаем на выходе pinOutput уровень логического «0»
|
||||
lenRGB = lenLED*3; // Получаем размер массива цветов светодиодов
|
||||
if((arrRGB = (uint8_t *) malloc(lenRGB))){ // Выделяем область памяти, размером lenRGB байт, под массив для хранения цветов светодиодов, адрес первой ячейки памяти массива передаём указателю arrRGB
|
||||
memset (arrRGB, 0, lenRGB); // Сбрасываем в 0 все байты массива по указателю arrRGB
|
||||
}else {lenRGB=0; return false;} // Если память выделить не удалось, то указываем размер массива равным 0 байт и возвращаем false
|
||||
return true; // Возвращаем флаг успешной инициализации
|
||||
}
|
||||
|
||||
void iarduino_NeoPixel::setColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t j){
|
||||
if(lenRGB){ // Если была успешно вызвана функция begin, то ...
|
||||
if(j<127){ r=map(j, 0,126,0, r); g=map(j, 0,126,0, g); b=map(j, 0,126,0, b);}else // Если требуется затемнить цвет, то ...
|
||||
if(j>127){ r=map(j,128,255,r,255); g=map(j,128,255,g,255); b=map(j,128,255,b,255);} // Если требуется осветлить цвет, то ...
|
||||
if(n == NeoPixelAll){ // Если требуется установить цвета для всех светодиодов, то ...
|
||||
for(uint16_t i=0; i<lenLED; i++){ // Проходим по всем светодиодам
|
||||
arrRGB[i*3+0] = g; // Устанавливаем цвет Green (зелёный)
|
||||
arrRGB[i*3+1] = r; // Устанавливаем цвет Red (красный)
|
||||
arrRGB[i*3+2] = b; // Устанавливаем цвет Blue (синий)
|
||||
}
|
||||
}else if(lenLED > n){ // Если требуется установть цвет только одному светодиоду и количество светодиодов больше чем номер нужного светодиода, то ...
|
||||
arrRGB[n*3+0] = g; // Устанавливаем цвет Green (зелёный)
|
||||
arrRGB[n*3+1] = r; // Устанавливаем цвет Red (красный)
|
||||
arrRGB[n*3+2] = b; // Устанавливаем цвет Blue (синий)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void iarduino_NeoPixel::write(){
|
||||
uint8_t bitLow = *portOutput & ~pinMask; // Получаем значение, запись которого в регистр по адресу portOutput, приведёт к установке логического «0» на вывхде pinOutput, не затрагивая уровни других выводов
|
||||
uint8_t bitHigh = *portOutput | pinMask; // Получаем значение, запись которого в регистр по адресу portOutput, приведёт к установке логической «1» на вывхде pinOutput, не затрагивая уровни других выводов
|
||||
uint8_t byte = 0;
|
||||
uint8_t bit = 8;
|
||||
|
||||
if(lenRGB){ // Передаём данные, если количество светодиодов больше 0 и была успешно вызвана функция begin
|
||||
|
||||
// Время передачи одного бита = 1250 нс (20 м.т.)
|
||||
// ______
|
||||
// | 375нс| 875нс (14мт)
|
||||
// | (6мт)|________________ передача логического «0»
|
||||
// _______________
|
||||
// | 875 нс | 375нс
|
||||
// | (14 м.т.) |_______ передача логической «1»
|
||||
//
|
||||
// HHHHHH XXXXXXXX LLLLLL уровни машинный тактов (м.т.) 1 м.т. = 62,5 нс
|
||||
// 123456 78901234 567890
|
||||
|
||||
noInterrupts(); // Запрещаем исполнение прерываний во время вывода данных
|
||||
|
||||
asm volatile(
|
||||
// Команды Assembler
|
||||
"getbyte: \n\t" // Метака getbyte.
|
||||
"ldi %[bit] , 8 \n\t" // 1 м.т. => T=01 bit = 8 Указываем что в передаваемом байте есть 8 не переданных битов
|
||||
"ld %[byte] , %a[arr]+ \n\t" // 2 м.т. => T=03 byte = *arr++ Получаем 1 байт для передачи, копируя его из СОЗУ в регистр byte. Адрес ячейки СОЗУ берётся из указателя arr, значение которого мы увеличиваем после получения байта
|
||||
"sbiw %[len] , 1 \n\t" // 2 м.т. => T=05 len-- Уменьшаем значение регистра len (счётчик количества переданных байт) на 1
|
||||
"brne sendbyte \n\t" // 2 м.т. => T=07 if(len>0){goto sendbyte} Если полученный байт не за пределами счетчика len, то переходим к блоку sendbyte для передачи этого байта
|
||||
"rjmp exitsend \n\t" // 2 м.т. goto exitsend Выходим из Ассемблерной вставки
|
||||
|
||||
"sendbyte: \n\t" // (для 0/1) (для 0/1) Метка sendbyte.
|
||||
"st %a[port], %[high] \n\t" // 2 м.т. => T=02 port = high Устанавливаем на выводе pinOutput уровень логической «1» (записывая в порт port значение регистра high)
|
||||
"nop \n\t" // 1 м.т. => T=03 Пропускаем один машинный такт (м.т.)
|
||||
"nop \n\t" // 1 м.т. => T=04 Пропускаем один машинный такт (м.т.)
|
||||
"nop \n\t" // 1 м.т. => T=05 Пропускаем один машинный такт (м.т.)
|
||||
"sbrs %[byte] , 7 \n\t" // 1/2 м.т. => T=06/07 if(byte & b10000000){jump} Если установлен 7 бит в регистре byte, то следующая строка пропускается. Команда выполняется за 2 м.т. если условие соблюдается, иначе за 1 м.т.
|
||||
"st %a[port], %[low] \n\t" // 2 м.т. => T=08 port = high Устанавливаем на выводе pinOutput уровень логического «0» (записывая в порт port значение регистра low)
|
||||
"rol %[byte] \n\t" // 1 м.т. => T=09/08 byte <<= 1 Сдвигаем влево все биты находящиеся в byte, (т.к. при передаче бита мы сверяемся со значением старшего бита)
|
||||
"nop \n\t" // 1 м.т. => T=10/09 Пропускаем один машинный такт (м.т.)
|
||||
"nop \n\t" // 1 м.т. => T=11/10 Пропускаем один машинный такт (м.т.)
|
||||
"nop \n\t" // 1 м.т. => T=12/11 Пропускаем один машинный такт (м.т.)
|
||||
"nop \n\t" // 1 м.т. => T=13/12 Пропускаем один машинный такт (м.т.)
|
||||
"nop \n\t" // 1 м.т. => T=14/13 Пропускаем один машинный такт (м.т.)
|
||||
"nop \n\t" // 1 м.т. => T=15/14 Пропускаем один машинный такт (м.т.)
|
||||
"st %a[port], %[low] \n\t" // 2 м.т. => T=17/16 port = low Устанавливаем на выводе pinOutput уровень логического «0» (записывая в порт port значение регистра low)
|
||||
"dec %[bit] \n\t" // 1 м.т. => T=01 bit-- Уменьшаем значение регистра bit (счетчик не переданных битов байта) на 1
|
||||
"breq getbyte \n\t" // 1/2 м.т. => T=03 if(bit==0){goto} Если значение bit равно 0 (все биты переданы), то переходим к метке getbyte (для получения следующего байта)
|
||||
"rjmp sendbyte \n\t" // 2 м.т. => T=05 goto sendbyte Переходим к передачи следующего бита текущего байта
|
||||
|
||||
"exitsend: \n"
|
||||
:// Список выходных параметров
|
||||
[port] "+e" (portOutput), // +e один из двухбайтных регистров указателей x,y,z Адрес порта для передачи данных
|
||||
[byte] "+r" (byte), // +r любой однобайтный регистр от r0 до r31 Передаваемый байт данных
|
||||
[bit] "+r" (bit) // +r любой однобайтный регистр от r0 до r31 Количество еще не переданных бит из byte
|
||||
:// Список входных параметров
|
||||
[low] "r" (bitLow), // r любой однобайтный регистр от r0 до r31 Значение которое требуется записать в порт, чтоб на выводе pinOutput установился логический «0»
|
||||
[high] "r" (bitHigh), // r любой однобайтный регистр от r0 до r31 Значение которое требуется записать в порт, чтоб на выводе pinOutput установилась логическая «1»
|
||||
[arr] "e" (arrRGB), // e один из двухбайтных регистров указателей x,y,z Адрес первой ячейки блока памяти в котором находится массив данных
|
||||
[len] "w" (lenRGB+1) // w один из верхних двухбайтных регистров r24,r26,r28,r30 Счетчик количества переданных байтов
|
||||
);
|
||||
interrupts(); // Возобновляем исполнение прерываний (если таковые были)
|
||||
}
|
||||
}
|
||||
|
||||
void iarduino_NeoPixel::setColor(uint16_t n, uint32_t rgb, uint8_t j){setColor(n, (rgb&0xFF0000)>>16, (rgb&0x00FF00)>>8, (rgb&0x0000FF), j);}
|
|
@ -0,0 +1,39 @@
|
|||
// Библиотека для работы с адресными светодиодами WS2812B: http://iarduino.ru/shop/Expansion-payments/neopixel-trema-modul.html
|
||||
// Версия: 1.0.0
|
||||
// Последнюю версию библиотеки Вы можете скачать по ссылке: http://iarduino.ru/file/296.html
|
||||
// Подробное описание функции бибилиотеки доступно по ссылке: http://wiki.iarduino.ru/page/adresnye-svetodiody-moduli-neopixel/
|
||||
// Библиотека является собственностью интернет магазина iarduino.ru и может свободно использоваться и распространяться!
|
||||
// При публикации устройств или скетчей с использованием данной библиотеки, как целиком, так и её частей,
|
||||
// в том числе и в некоммерческих целях, просим Вас опубликовать ссылку: http://iarduino.ru
|
||||
// Автор библиотеки: Панькин Павел
|
||||
// Если у Вас возникли технические вопросы, напишите нам: shop@iarduino.ru
|
||||
|
||||
#ifndef iarduino_NeoPixel_h
|
||||
#define iarduino_NeoPixel_h
|
||||
|
||||
#if defined(ARDUINO) && (ARDUINO >= 100)
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <WProgram.h>
|
||||
#endif
|
||||
|
||||
#define NeoPixelAll 65535L // Константа указывающая, что цвет применяется ко всем светодиодам
|
||||
|
||||
class iarduino_NeoPixel{
|
||||
public: iarduino_NeoPixel (uint8_t, uint16_t=0); // Подключение (№ вывода к которому подключены светодиоды, количество подключённых светодиодов)
|
||||
void setColor (uint16_t, uint8_t, uint8_t, uint8_t, uint8_t=127); // Указание цвета (№ светодиода, R,G,B, яркость)
|
||||
void setColor (uint16_t, uint32_t, uint8_t=127); // Указание цвета (№ светодиода, RGB, яркость)
|
||||
bool begin (void); // Инициализация работы со светодиодами
|
||||
void write (void); // Запись данных в светодиоды
|
||||
uint16_t count (void){return lenRGB? lenLED:0;} // Получение количества светодиодов
|
||||
uint8_t * getPointer (void){return lenRGB? arrRGB:0;} // Получение указателя на массив с цветами для светодиодов
|
||||
private:
|
||||
volatile uint8_t * portOutput; // Объявляем указатель на адрес регистра выходных значений порта. В этом регистре находится один байт, биты которого и являются логическими уровнями на выходе порта
|
||||
uint8_t * arrRGB; // Объявляем указатель на массив с цветами для светодиодов
|
||||
uint8_t pinOutput; // Определяем переменную для хранения номера вывода используемого как выход для подключения светодиодов.
|
||||
uint8_t pinMask; // Объявляем переменную для хранения маски вывода. Она хранит один байт в котором установлен только один бит. Порядковый номер этого бита, соответствует номеру вывода на порту (от 0 до 7)
|
||||
uint16_t lenRGB=0; // Определяем количество байт в массиве с цветами светодиодов (будет равно количеству светодиодов * RGB)
|
||||
uint16_t lenLED; // Объявляем количество свтодиодов
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,134 @@
|
|||
#include "iarduino_RTC.h"
|
||||
|
||||
// Вывод даты и времени
|
||||
char* iarduino_RTC::gettime(String i){char j[i.length()+1]; i.toCharArray(j, i.length()); j[i.length()]=0; return gettime(j);}
|
||||
char* iarduino_RTC::gettime(const char* i){
|
||||
uint8_t j, k, n; bool f; // Объявляем локальные переменные
|
||||
if(valRequest > millis()){valRequest=0;} // Избавляемся от переполнения millis()
|
||||
// Получаем текущее время:
|
||||
if(valPeriod == 0) {funcReadTime();}else // Если минимальный период опроса модуля == 0 минут, то получаем время из регистров модуля, иначе ...
|
||||
if(valRequest == 0 || (valPeriod+valRequest <= millis())) {funcReadTime();}else // Если время последнего опроса модуля превысило минимальный период опроса модуля, то получаем время из регистров модуля, иначе ...
|
||||
{funcCalculationTime();} // Получаем время без обращения к модулю (время рассчитывая исходя из millis и valRequest)
|
||||
funcSetMoreTime(); // Преобразуем переменные не читаемые из модуля
|
||||
// Вычисляем размер блока памяти под строку с ответом:
|
||||
n=strlen(i)+1; // Определяем размер равный введённой строке (i) + 1 символ конца строки
|
||||
for(j=0; j<strlen(i); j++) { // Проходим по символам полученной строки
|
||||
for(k=0; k<strlen(charInput); k++) { // Проходим по служебным символам строки charInput
|
||||
if (i[j]==charInput[k]) { // Если символы обеих строк совпали, то ...
|
||||
if(k>0){n++;} if(k>9){n++;} if(k>11){n++;} // Увеличиваем размер (n) в соостветствии со значениём найденного служебного символа
|
||||
}}}
|
||||
// Выделяем блок памяти под строку с ответом:
|
||||
free(charReturn); // Освобождаем ранее созданный блок памяти
|
||||
charReturn = (char*) malloc(n); // Выделяем новый блок памяти размером n байт
|
||||
// Заполняем выделенный блок памяти:
|
||||
n=0; // Определяем позицию в блоке памяти для следующего подставляемого значения
|
||||
for(j=0; j<strlen(i); j++) { // Проходим по символам полученной строки
|
||||
if(i[j]==charInput[0] /* w */ ) {funcFillChar( weekday ,0,n,7); n+=1;}else // Подставляем день недели от 0 до 6 (один знак: 0-воскресенье, 6-суббота)
|
||||
if(i[j]==charInput[1] /* a */ ) {funcFillChar( midday*2 ,2,n,8); n+=2;}else // Подставляем полдень am или pm (два знака, в нижнем регистре)
|
||||
if(i[j]==charInput[2] /* A */ ) {funcFillChar((midday+2)*2 ,2,n,8); n+=2;}else // Подставляем полдень AM или PM (два знака, в верхнем регистре)
|
||||
if(i[j]==charInput[3] /* d */ ) {funcFillChar( day ,1,n,4); n+=2;}else // Подставляем день месяца от 01 до 31 (два знака)
|
||||
if(i[j]==charInput[4] /* h */ ) {funcFillChar( hours ,1,n,3); n+=2;}else // Подставляем часы от 01 до 12 (два знака)
|
||||
if(i[j]==charInput[5] /* H */ ) {funcFillChar( Hours ,1,n,3); n+=2;}else // Подставляем часы от 00 до 23 (два знака)
|
||||
if(i[j]==charInput[6] /* i */ ) {funcFillChar( minutes ,1,n,2); n+=2;}else // Подставляем минуты от 00 до 59 (два знака)
|
||||
if(i[j]==charInput[7] /* m */ ) {funcFillChar( month ,1,n,5); n+=2;}else // Подставляем месяц от 01 до 12 (два знака)
|
||||
if(i[j]==charInput[8] /* s */ ) {funcFillChar( seconds ,1,n,1); n+=2;}else // Подставляем секунды от 00 до 59 (два знака)
|
||||
if(i[j]==charInput[9] /* y */ ) {funcFillChar( year ,1,n,6); n+=2;}else // Подставляем год от 00 до 99 (два знака)
|
||||
if(i[j]==charInput[10] /* M */ ) {funcFillChar((month+6)*3 ,3,n,5); n+=3;}else // Подставляем имя месяца от Jan до Dec (три знака: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
|
||||
if(i[j]==charInput[11] /* D */ ) {funcFillChar( weekday*3 ,3,n,7); n+=3;}else // Подставляем имя деня недели от Mon до Sun (три знака: Mon Tue Wed Thu Fri Sat Sun)
|
||||
if(i[j]==charInput[12] /* Y */ ) {funcFillChar( year ,4,n,6); n+=4;}else // Подставляем год от 2000 до 2099 (четыре знака)
|
||||
{charReturn[n]=i[j]; n+=1;} // Если символ полученной строки не совпал со служебными, то подставляем его в блок памяти как есть, без изменений
|
||||
} charReturn[n]='\0'; return charReturn; // Устанавливаем символ конца строки и возвращаем указатель на блок памяти с результатом
|
||||
}
|
||||
|
||||
// Заполняем значением определённую позицию блока памяти:
|
||||
void iarduino_RTC::funcFillChar(uint8_t i, uint8_t j, uint8_t n, uint8_t k){ // (данные, тип данных, позиция для вставки, мигание)
|
||||
bool f=valBlink==k; if((millis()%valFrequency)<(valFrequency/2)){f=false;} // Устанавливаем флаг мигания, если значение valBlink равно значению параметра k
|
||||
switch (j){
|
||||
/* 1 знак */ case 0: if(i>6 ){i=6; } charReturn[n]=f?32:i+48; break;
|
||||
/* 2 знака */ case 1: if(i>99){i=99;} charReturn[n]=f?32:i/10+48; charReturn[n+1]=f?32:i%10+48; break;
|
||||
/* AM / PM */ case 2: if(i>6 ){i=6; } charReturn[n]=f?32:charMidday[i]; charReturn[n+1]=f?32:charMidday[i+1]; break;
|
||||
/* дн / мес */ case 3: if(i>54){i=54;} charReturn[n]=f?32:charDayMon[i]; charReturn[n+1]=f?32:charDayMon[i+1]; charReturn[n+2]=f?32:charDayMon[i+2]; break;
|
||||
/* 4 знака */ case 4: if(i>99){i=99;} charReturn[n]=f?32:(valCentury-1)/10+48; charReturn[n+1]=f?32:(valCentury-1)%10+48; charReturn[n+2]=f?32:i/10+48; charReturn[n+3]=f?32:i%10+48; break;
|
||||
}
|
||||
}
|
||||
|
||||
// Установка даты и времени
|
||||
void iarduino_RTC::settime(int i1, int i2, int i3, int i4, int i5, int i6, int i7){ // (сек, мин, час, день, мес, год, день_недели)
|
||||
funcWriteTime(i1, i2, i3, i4, i5, i6, i7); // Записываем дату и время в регистры модуля
|
||||
funcReadTime(); // Читаем дату и время из регистров модуля
|
||||
funcSetMoreTime(); // Корректируем переменные не читаемые из модуля (hours, midday)
|
||||
}
|
||||
|
||||
// Установка даты и времени от начала эпохи Unix
|
||||
void iarduino_RTC::settimeUnix(uint32_t i){ // (сек)
|
||||
uint32_t j; uint8_t k; bool f=true; // Объявляем временные переменные.
|
||||
seconds = i % 60; // Получаем текущее значение секунд. (остаток от деления секунд прошедших с начала эпохи Unix на количество секунд в минуте)
|
||||
i = (i-seconds) / 60; // Получаем количество минут прошедших с начала эпохи Unix.
|
||||
minutes = i % 60; // Получаем текущее значение минут. (остаток от деления минут прошедших с начала эпохи Unix на количество минут в часе)
|
||||
i = (i-minutes) / 60; // Получаем количество часов прошедших с начала эпохи Unix.
|
||||
Hours = i % 24; // Получаем текущее значение часов. (остаток от деления часов прошедших с начала эпохи Unix на количество часов в дне)
|
||||
i = (i-Hours) / 24; // Получаем количество дней прошедших с начала эпохи Unix.
|
||||
j = 0; while((((j+1)*365)+((j+2)/4))<=i){j++;} // Получаем количество лет прошедших с начала эпохи Unix.
|
||||
weekday = (i+4) % 7; // Получаем текущий день недели. (0-воскресенье, 1-понедельник, ... , 6-суббота)
|
||||
i = i - (j*365) - ((j+1)/4); // Получаем количество дней прошедших в текущем году.
|
||||
valCentury = ((1970+j)/100)+1; // Получаем текущий век.
|
||||
year = (1970+j)%100; // Получаем две последние цифры текущего года.
|
||||
k = ((1970+j)%4)==0?29:28; // Получаем количество дней в феврале текущего года.
|
||||
month = 0; while(f){month++; j=month; j=(((j+1)%2)^(j<8))?31:(j==2?k:30); if(i>=j){i-=j;}else{f=false;}}
|
||||
day = i+1; // Получаем текущий день.
|
||||
// Устанавливаем время:
|
||||
settime(seconds, minutes, Hours, day, month, year, weekday);
|
||||
}
|
||||
|
||||
// Чтение даты и времени в переменные из регистров модуля:
|
||||
void iarduino_RTC::funcReadTime(void){ // (без параметров)
|
||||
seconds = arrCalculationTime[0] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(0)); // Получаем секунды
|
||||
minutes = arrCalculationTime[1] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(1)); // Получаем минуты
|
||||
Hours = arrCalculationTime[2] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(2)); // Получаем часы
|
||||
day = arrCalculationTime[3] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(3)); // Получаем день
|
||||
month = arrCalculationTime[4] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(4)); // Получаем месяц
|
||||
year = arrCalculationTime[5] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(5)); // Получаем год
|
||||
weekday = arrCalculationTime[6] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(6))-1; // Получаем день недели (в регистре значение от 1 до 7, а в переменной от 0 до 6)
|
||||
Unix = funcCalculationUnix(); // Получаем количество секунд (прошедших с начала эпохи Unix)
|
||||
valRequest = millis(); // Сохраняем время данного запроса
|
||||
}
|
||||
|
||||
// Запись даты и времени в регистры модуля:
|
||||
void iarduino_RTC::funcWriteTime(int i1, int i2, int i3, int i4, int i5, int i6, int i7){ //
|
||||
if(i1<=60 && i1>=0){objClass -> funcWriteTimeIndex(0, funcConvertNumToCode(i1 ));} // Сохраняем секунды
|
||||
if(i2<=60 && i2>=0){objClass -> funcWriteTimeIndex(1, funcConvertNumToCode(i2 ));} // Сохраняем минуты
|
||||
if(i3<=23 && i3>=0){objClass -> funcWriteTimeIndex(2, funcConvertNumToCode(i3 ));} // Сохраняем часы
|
||||
if(i4<=31 && i4>=1){objClass -> funcWriteTimeIndex(3, funcConvertNumToCode(i4 ));} // Сохраняем день
|
||||
if(i5<=12 && i5>=1){objClass -> funcWriteTimeIndex(4, funcConvertNumToCode(i5 ));} // Сохраняем месяц
|
||||
if(i6<=99 && i6>=0){objClass -> funcWriteTimeIndex(5, funcConvertNumToCode(i6 ));} // Сохраняем год
|
||||
if(i7<= 6 && i7>=0){objClass -> funcWriteTimeIndex(6, funcConvertNumToCode(i7+1));} // Сохраняем день недели (в регистре значение от 1 до 7, а в переменной от 0 до 6)
|
||||
}
|
||||
|
||||
// Расчёт времени без обращения к модулю:
|
||||
void iarduino_RTC::funcCalculationTime(void){ // (без параметров)
|
||||
uint32_t i=(millis()-valRequest)/1000; // Определяем количество секунд (прошедших после последнего обращения к модулю)
|
||||
uint8_t j=30 + ( (arrCalculationTime[4] + (arrCalculationTime[4]>7?1:0)) % 2 ); // Определяем количество дней в месяце (31, 30, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||
if(arrCalculationTime[4]==2){j=28+((((uint16_t)valCentury-1)*100+arrCalculationTime[5])%4?0:1);}// Если текущий месяц - февраль, то меняем на ... (31, 28/29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||
i+=arrCalculationTime[0]; seconds = i%60; i/=60; // Добавляем к прошедним секундам (i) посление прочитанные секунды (arrCalculationTime[0]), результатом будет остаток, а значение i превращаем в минуты
|
||||
i+=arrCalculationTime[1]; minutes = i%60; i/=60; // Добавляем к прошедним минутам (i) посление прочитанные минуты (arrCalculationTime[1]), результатом будет остаток, а значение i превращаем в часы
|
||||
i+=arrCalculationTime[2]; Hours = i%24; i/=24; // Добавляем к прошедним часам (i) посление прочитанные часы (arrCalculationTime[2]), результатом будет остаток, а значение i превращаем в дни
|
||||
weekday = arrCalculationTime[6]+i; if(weekday>6){weekday=0;} // День недели увеличится на (i) от прочитанного дня недели (arrCalculationTime[6])
|
||||
i+=arrCalculationTime[3]; day = i%(j+1); i/=(j+1); day+=i; // Добавляем к прошедним дням (i) посление прочитанные дни (arrCalculationTime[3]), результатом будет остаток, а значение i превращаем в месяцы
|
||||
i+=arrCalculationTime[4]; month = i%13; i/=13; month+=i; // Добавляем к прошедним месяцам (i) посление прочитанные месяцы (arrCalculationTime[4]), результатом будет остаток, а значение i превращаем в годы
|
||||
i+=arrCalculationTime[5]; year = i%100; // Добавляем к прошедним годам (i) посление прочитанные годы (arrCalculationTime[5]), результатом будет остаток, а значение i превращаем в дни
|
||||
Unix = funcCalculationUnix(); // Получаем количество секунд прошедших с начала эпохи Unix
|
||||
}
|
||||
|
||||
// Расчёт количества cекунд прошедших с начала эпохи Unix:
|
||||
uint32_t iarduino_RTC::funcCalculationUnix(void){ // (без параметров)
|
||||
uint32_t i; // Объявляем переменную для хранения результата. (рассчёты производятся из значений переменных: seconds, minutes, Hours, day, month, year и valCentury).
|
||||
uint32_t j = (uint32_t)(valCentury-1) * 100 + year; // Определяем текущий год с учётом века. (valCentury - век, year - последние два знака текущего года).
|
||||
i = j - 1970; // Определяем количество прошедших лет (с 01.01.1970 г.)
|
||||
i = i * 365 + ((i+1)/4); // Определяем количество дней в прошедших годах ((i+1)/4) - количество прошедших високосных лет (без учёта текущего года).
|
||||
i += (month-1)*30 + ( (month + (month<9?0:1) )/2 ); // Добавляем количество дней в прошедших месяцах ((month+(month<9?0:1))/2) - количество прошедших месяцев текущего года, содержащих 31 день.
|
||||
i -= month>2? (j%4==0?1:2) : 0; // Вычитаем 1 или 2 дня из февраля текущего года ((month>2) - если февраль уже прошел, j%4==0 - если текущий год високосный)
|
||||
i += day-1; // Добавляем количество прошедших дней этого месяца
|
||||
i *= 86400; // Получаем количество секунд прошедших дней
|
||||
i += (uint32_t)Hours * 3600 + (uint32_t)minutes * 60 + seconds; // Добавляем количество секунд текущего дня
|
||||
return i; // Возвращаем результат
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
// Библиотека для работы с часами реального времени: (на чипе DS1302) http://iarduino.ru/shop/Expansion-payments/rtc-modul-ds1302.html
|
||||
// (на чипе DS1307) http://iarduino.ru/shop/Expansion-payments/chasy-realnogo-vremeni-rtc-trema-modul.html
|
||||
// (на чипе DS3231) http://iarduino.ru/shop/Expansion-payments/chasy-realnogo-vremeni-ds3231.html
|
||||
// Версия: 1.3.4
|
||||
// Последнюю версию библиотеки Вы можете скачать по ссылке: http://iarduino.ru/file/235.html
|
||||
// Подробное описание функции бибилиотеки доступно по ссылке: http://wiki.iarduino.ru/page/chasy-realnogo-vremeni-rtc-trema-modul/
|
||||
// Библиотека является собственностью интернет магазина iarduino.ru и может свободно использоваться и распространяться!
|
||||
// При публикации устройств или скетчей с использованием данной библиотеки, как целиком, так и её частей,
|
||||
// в том числе и в некоммерческих целях, просим Вас опубликовать ссылку: http://iarduino.ru
|
||||
// Автор библиотеки: Панькин Павел
|
||||
// Если у Вас возникли технические вопросы, напишите нам: shop@iarduino.ru
|
||||
|
||||
#ifndef iarduino_RTC_h //
|
||||
#define iarduino_RTC_h //
|
||||
//
|
||||
#define RTC_UNDEFINED 0 // Модуль часов реального времени не определён
|
||||
//
|
||||
#if defined(ARDUINO) && (ARDUINO >= 100) //
|
||||
#include <Arduino.h> //
|
||||
#else //
|
||||
#include <WProgram.h> //
|
||||
#endif //
|
||||
//
|
||||
#define RTC_ENABLE_DS3231 //
|
||||
|
||||
class iarduino_RTC_BASE{ // Объявляем полиморфный класс
|
||||
public: //
|
||||
virtual void begin (void); // Объявляем функцию инициализации модуля (без параметров)
|
||||
virtual uint8_t funcReadTimeIndex (uint8_t); // Объявляем функцию чтения 1 значения из регистров даты и времени (0-секунды / 1-минуты / 2-часы / 3-день / 4-месяц / 5-год / 6-день недели)
|
||||
virtual void funcWriteTimeIndex (uint8_t, uint8_t); // Объявляем функцию записи 1 значения в регистры даты и времени (0-секунды / 1-минуты / 2-часы / 3-день / 4-месяц / 5-год / 6-день недели, значение)
|
||||
}; //
|
||||
|
||||
#include "iarduino_RTC_DS3231.h" // Подключаем файл iarduino_RTC_DS3231.h
|
||||
//
|
||||
class iarduino_RTC{ //
|
||||
public: //
|
||||
/** Конструктор класса **/ //
|
||||
iarduino_RTC(uint8_t i, uint8_t j=SS, uint8_t k=SCK, uint8_t n=MOSI){ // Конструктор основного класса (название [, вывод SS/RST [, вывод SCK/CLK [, вывод MOSI/DAT]]])
|
||||
switch(i){ // Тип выбранного модуля
|
||||
#ifdef RTC_ENABLE_DS1302 //
|
||||
case RTC_DS1302: objClass = new iarduino_RTC_DS1302(j,k,n); break; // Если используется модуль на базе чипа DS1302, то присваеиваем указателю objClass ссылку на новый объект производного класса iarduino_RTC_DS1302 переопределяя на него все виртуальные функции полиморфного класса iarduino_RTC_BASE
|
||||
#endif //
|
||||
#ifdef RTC_ENABLE_DS1307 //
|
||||
case RTC_DS1307: objClass = new iarduino_RTC_DS1307; break; // Если используется модуль на базе чипа DS1307, то присваеиваем указателю objClass ссылку на новый объект производного класса iarduino_RTC_DS1307 переопределяя на него все виртуальные функции полиморфного класса iarduino_RTC_BASE
|
||||
#endif //
|
||||
#ifdef RTC_ENABLE_DS3231 //
|
||||
case RTC_DS3231: objClass = new iarduino_RTC_DS3231; break; // Если используется модуль на базе чипа DS3231, то присваеиваем указателю objClass ссылку на новый объект производного класса iarduino_RTC_DS3231 переопределяя на него все виртуальные функции полиморфного класса iarduino_RTC_BASE
|
||||
#endif //
|
||||
} //
|
||||
} //
|
||||
/** Пользовательские функции **/ //
|
||||
void begin (void) {objClass -> begin(); gettime();} // Определяем функцию инициализации модуля (без параметров)
|
||||
void period (uint8_t i) {valPeriod=i; valPeriod*=60000;} // Определяем функцию задания минимального периода обращения к модулю (i = период в минутах)
|
||||
void blinktime (uint8_t i, float j=1) {valBlink=i; valFrequency=uint32_t(1000/j);} // Определяем функцию позволяющую мигать одним из параметров времени (i = 0-нет / 1-сек / 2-мин / 3-час / 4-день / 5-мес / 6-год / 7-день_недели / 8-полдень , j = частота мигания в Гц)
|
||||
void gettime (void) {gettime("");} // Определяем функцию получения даты и времени из переменных (без параметров)
|
||||
char* gettime (String); // Объявляем функцию «дублёр» получения даты и времени из переменных (строка с параметрами)
|
||||
char* gettime (const char*); // Объявляем функцию получения даты и времени ввиде строки (строка с параметрами)
|
||||
void settime (int, int=-1, int=-1, int=-1, int=-1, int=-1, int=-1); // Объявляем функцию установки даты и времени (сек, мин, час, день, мес, год, день_недели)
|
||||
uint32_t gettimeUnix (void) {gettime(""); return Unix;} // Определяем функцию получения cекунд прошедших с начала эпохи Unix (без параметров)
|
||||
void settimeUnix (uint32_t); // Объявляем функцию установки cекунд прошедших с начала эпохи Unix (сек)
|
||||
//
|
||||
/** Переменные доступные для пользователя **/ //
|
||||
uint8_t seconds = 0; // Секунды 0-59
|
||||
uint8_t minutes = 0; // Минуты 0-59
|
||||
uint8_t hours = 1; // Часы 1-12
|
||||
uint8_t Hours = 0; // Часы 0-23
|
||||
uint8_t midday = 0; // Полдень 0-1 (0-am, 1-pm)
|
||||
uint8_t day = 1; // День месяца 1-31
|
||||
uint8_t weekday = 0; // День недели 0-6 (0-воскресенье, 1-понедельник, ... , 6-суббота)
|
||||
uint8_t month = 1; // Месяц 1-12
|
||||
uint8_t year = 0; // Год 0-99 (без учёта века)
|
||||
uint32_t Unix = 0; // Секунды прошедшие с начала эпохи Unix (01.01.1970 00:00:00 GMT)
|
||||
//
|
||||
/** Внутренние переменные **/ //
|
||||
iarduino_RTC_BASE* objClass; // Объявляем указатель на объект полиморфного класса (функции данного класса будут переопределены, т.к. указателю будет присвоена ссылка на производный класс)
|
||||
char* charReturn = (char*) malloc(1); // Определяем указатель на символьную область памяти в 1 байт (указатель будет ссылаться на строку вывода времени)
|
||||
const char* charInput = "waAdhHimsyMDY"; // Определяем константу-строку с символами требующими замены (данные символы заменяются функцией gettime на значение времени)
|
||||
const char* charMidday = "ampmAMPM"; // Определяем константу-строку для вывода полудня (am / pm / AM / PM)
|
||||
const char* charDayMon = "SunMonTueWedThuFriSatJanFebMarAprMayJunJulAugSepOctNovDec"; // Определяем константу-строку для вывода дня недели или месяца (Mon ... Sun / Jan ... Dec)
|
||||
uint8_t arrCalculationTime[7]; // Объявляем массив для рассчёта времени без обращения к модулю (для хранения последних, прочитанных из модуля, значений даты и времени)
|
||||
uint8_t valBlink = 0; // Определяем параметр времени, который должен мигать (1-сек / 2-мин / 3-час / 4-день / 5-мес / 6-год / 7-день_недели / 8-полдень)
|
||||
uint32_t valFrequency = 1000; // Определяем частоту мигания параметра времени для функции blinktime (по умолчанию 1 Гц)
|
||||
uint8_t valCentury = 21; // Определяем переменную для хранения текущего века (по умолчанию 21 век)
|
||||
uint16_t valPeriod = 0; // Определяем минимальный период опроса модуля (в минутах, от 00 до 255)
|
||||
uint32_t valRequest = 0; // Определяем время последнего чтения регистров времени
|
||||
private: //
|
||||
/** Внутренние функции **/ //
|
||||
void funcReadTime (void); // Объявляем функцию чтения даты и времени из регистров модуля (без параметров)
|
||||
void funcWriteTime (int, int, int, int, int, int, int); // Объявляем функцию записи даты и времени в регистры модуля (без параметров)
|
||||
uint8_t funcConvertCodeToNum (uint8_t i) {return (i >> 4)*10 + (i & 0x0F);} // Определяем функцию преобразования двоично-десятичного кода в число (код)
|
||||
uint8_t funcConvertNumToCode (uint8_t i) {return ((i/10) << 4) + (i%10);} // Определяем функцию преобразования числа в двоично-десятичный код (число)
|
||||
void funcSetMoreTime (void){hours=(Hours%12)?(Hours%12):12; midday=(Hours<12)?0:1;} // Корректировка переменных не читаемых из модуля (без параметров)
|
||||
void funcCalculationTime (void); // Объявляем функцию расчёта времени без обращения к модулю (без параметров)
|
||||
uint32_t funcCalculationUnix (void); // Объявляем функцию расчёта cекунд прошедших с начала эпохи Unix (без параметров)
|
||||
void funcFillChar (uint8_t, uint8_t, uint8_t, uint8_t); // Объявляем функцию заполнения строки вывода времени (данные, тип данных, позиция для вставки, мигание)
|
||||
}; //
|
||||
//
|
||||
#endif //
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef iarduino_RTC_DS3231_h //
|
||||
#define iarduino_RTC_DS3231_h //
|
||||
#define RTC_DS3231 3 // Модуль часов реального времени с протоколом передачи данных I2C, памятью 019x8, температурной компенсацией, двумя будильниками и встроенным кварцевым резонатором
|
||||
#include "iarduino_RTC_I2C.h" // Подключаем файл iarduino_RTC_I2C.h - для работы с шиной I2C
|
||||
//
|
||||
class iarduino_RTC_DS3231: public iarduino_RTC_BASE{ //
|
||||
public: //
|
||||
/** функции доступные пользователю **/ //
|
||||
// Инициализация модуля: //
|
||||
void begin(void){ // (без параметров)
|
||||
// Инициализация работы с шиной I2C: //
|
||||
objI2C.begin(100); // (скорость шины в кГц)
|
||||
// Установка флагов управления и состояния модуля: //
|
||||
varI=objI2C.readByte(valAddress, 0x02); if( varI & 0b01000000 ){objI2C.writeByte(valAddress, 0x02, (varI&~0b01000000) );} // (если установлен 6 бит в 2 регистре, то сбрасываем его - переводим модуль в 24 часовой режим)
|
||||
varI=objI2C.readByte(valAddress, 0x0E); if( varI & 0b11011111 ){objI2C.writeByte(valAddress, 0x0E, (varI&~0b11011111) );} // (если установлены 7,6,4,3,2,1 и 0 биты в 14 регистре, то сбрасываем их - разрешаем генератору работать от батарейки, запрещаем выводу SQW работать от батарейки, выводим меандр с частотой 1Гц на вывод SQW, переводим вывод INT/SQW в режим SQW, запрещаем прерывания будильников)
|
||||
varI=objI2C.readByte(valAddress, 0x0F); if((varI & 0b10000011) || !(varI & 0b00001000)){objI2C.writeByte(valAddress, 0x0F, (varI&~0b10000011)|0b00001000 );} // (если установлены 7,1 и 0 биты или сброшен 3 бит в 15 регистре, то сбрасываем 7,1 и 0 биты, а 3 устанавливаем - сбрасываем флаг остановки генератора, разрешаем меандр с частотой 32768Гц на выводе 32kHz, сбрасываем флаги будильников)
|
||||
} //
|
||||
//
|
||||
// Чтение одного значения из регистров даты и времени модуля: //
|
||||
uint8_t funcReadTimeIndex(uint8_t i){ // (i = 0-секунды / 1-минуты / 2-часы / 3-день / 4-месяц / 5-год / 6-день недели)
|
||||
delay(1); //
|
||||
return objI2C.readByte(valAddress, arrTimeRegAddr[i]) & arrTimeRegMack[i]; //
|
||||
} //
|
||||
//
|
||||
// Запись одного значения в регистры даты и времени модуля: //
|
||||
void funcWriteTimeIndex(uint8_t i, uint8_t j){ // (i = 0-секунды / 1-минуты / 2-часы / 3-день / 4-месяц / 5-год / 6-день недели, j = значение)
|
||||
varI=funcReadTimeIndex(i); // Читаем данные из регистра i
|
||||
j |= ~arrTimeRegMack[i] & varI; // Устанавливаем биты значения j по маске arrTimeRegMack[i] в прочитанные из регистра i
|
||||
j &= arrTimeRegMack[i] | varI; // Сбрасываем биты значения j по маске arrTimeRegMack[i] в прочитанные из регистра i
|
||||
objI2C.writeByte(valAddress, arrTimeRegAddr[i], j); // Сохраняем значение j в регистр arrTimeRegAddr[i]
|
||||
} //
|
||||
//
|
||||
private: //
|
||||
/** Внутренние переменные **/ //
|
||||
iarduino_I2C objI2C; // Создаём объект для работы с шиной I2C
|
||||
uint8_t valAddress = 0x68; // Адрес модуля на шине I2C
|
||||
uint8_t arrTimeRegAddr[7] = {0x00,0x01,0x02,0x04,0x05,0x06,0x03}; // Определяем массив с адресами регистров даты и времени (сек, мин, час, день, месяц, год, день недели)
|
||||
uint8_t arrTimeRegMack[7] = {0x7F,0x7F,0x3F,0x3F,0x1F,0xFF,0x07}; // Определяем маскировочный массив для регистров даты и времени (при чтении/записи, нужно совершить побитовое «и»)
|
||||
uint8_t varI; //
|
||||
}; //
|
||||
//
|
||||
#endif //
|
|
@ -0,0 +1,461 @@
|
|||
#ifndef iarduino_I2C_h // Разрешаем включить данный код в скетч, только если он не был включён ранее
|
||||
#define iarduino_I2C_h // Запрещаем повторное включение данного кода в скетч
|
||||
//
|
||||
// Определяем тип реализации шины I2C: //
|
||||
#define iarduino_I2C_SW // Объявляем константу iarduino_I2C_SW - Возможна программная реализация шины I2C.
|
||||
#if (!defined(pin_SW_SDA) || !defined(pin_SW_SCL)) // Если выводы не определены пользователем, то ...
|
||||
#undef iarduino_I2C_SW // Отменяем объявление константы iarduino_I2C_SW - Программная реализация шины I2C не возможна (так как выводы не указаны).
|
||||
#if defined(ESP8266) || defined(ESP32) // Если используются указанные платы, то ...
|
||||
#include <Wire.h> // Подключаем библиотеку Wire.
|
||||
#define pin_SW_SDA 255 // № вывода SDA не определён
|
||||
#define pin_SW_SCL 255 // № вывода SCL не определён
|
||||
#define iarduino_I2C_TW // Объявляем константу iarduino_I2C_TW - Будет использована аппаратная реализация шины I2C под управлением библиотеки Wire.
|
||||
#elif defined(TwoWire_h) // Проверяем не подключена ли библиотека Wire.
|
||||
#define pin_SW_SDA 255 // № вывода SDA не определён
|
||||
#define pin_SW_SCL 255 // № вывода SCL не определён
|
||||
#define iarduino_I2C_TW // Объявляем константу iarduino_I2C_TW - Будет использована аппаратная реализация шины I2C под управлением библиотеки Wire.
|
||||
#elif (defined(SDA) && defined(SCL)) // Определяем выводы для шины I2C
|
||||
#define pin_SW_SDA SDA // № вывода SDA = SDA
|
||||
#define pin_SW_SCL SCL // № вывода SCL = SCL
|
||||
#define iarduino_I2C_HW // Объявляем константу iarduino_I2C_HW - Будет использована аппаратная реализация шины I2C.
|
||||
#elif (defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL)) // Определяем выводы для шины I2C
|
||||
#define pin_SW_SDA PIN_WIRE_SDA // № вывода SDA = PIN_WIRE_SDA
|
||||
#define pin_SW_SCL PIN_WIRE_SCL // № вывода SCL = PIN_WIRE_SCL
|
||||
#define iarduino_I2C_HW // Объявляем константу iarduino_I2C_HW - Будет использована аппаратная реализация шины I2C.
|
||||
#elif (defined(SDA1) && defined(SCL1)) // Определяем выводы для шины I2C-1
|
||||
#define pin_SW_SDA SDA1 // № вывода SDA = SDA1
|
||||
#define pin_SW_SCL SCL1 // № вывода SCL = SCL1
|
||||
#define iarduino_I2C_HW_1 // Объявляем константу iarduino_I2C_HW_1 - Будет использована аппаратная реализация шины I2C-1.
|
||||
#else // Если выводы определить не удалось, то ...
|
||||
#define pin_SW_SDA 255 // № вывода SDA не определён - Аппаратная реализация шины I2C не возможна.
|
||||
#define pin_SW_SCL 255 // № вывода SCL не определён - Аппаратная реализация шины I2C не возможна.
|
||||
#endif //
|
||||
#endif //
|
||||
//
|
||||
class iarduino_I2C_BASE{ // Определяем полиморфный класс
|
||||
public: // Доступные методы и функции:
|
||||
// ОСНОВНЫЕ ФУНКЦИИ: (поддерживаются библиотекой Wire) //
|
||||
virtual void begin (uint32_t); // Объявляем функцию указания скорости шины I2C. Аргументы: скорость в кГц.
|
||||
virtual uint8_t readByte (uint8_t, uint8_t ); // Объявляем функцию чтения байта данных из регистра модуля. Аргументы: адрес_модуля, адрес_регистра. (адрес регистра указывает модулю, данные какого регистра требуется отправить мастеру)
|
||||
virtual bool writeByte (uint8_t, uint8_t, uint8_t); // Объявляем функцию записи байта данных в регистр модуля. Аргументы: адрес_модуля, адрес_регистра, байт_данных. (адрес регистра указывает модулю, в какой регистр требуется сохранить данные)
|
||||
virtual uint8_t readByte (uint8_t ); // Объявляем функцию чтения байта данных из модуля. Аргументы: адрес_модуля (функция отличается тем, что она не отправляет модулю адрес регистра)
|
||||
virtual bool writeByte (uint8_t, uint8_t); // Объявляем функцию записи байта данных в модуль. Аргументы: адрес_модуля, байт_данных. (функция отличается тем, что она не отправляет модулю адрес регистра)
|
||||
virtual bool readBytes (uint8_t, uint8_t, uint8_t*, uint8_t); // Объявляем функцию чтения байтов данных из регистров модуля. Аргументы: адрес_модуля, адрес_первого_регистра, указатель_на_массив, количество_байт. (адрес первого регистра указывает модулю, с какого регистра требуется начать передачу данных мастеру)
|
||||
virtual bool writeBytes (uint8_t, uint8_t, uint8_t*, uint8_t); // Объявляем функцию записи байтов данных в регистры модуля. Аргументы: адрес_модуля, адрес_первого_регистра, указатель_на_массив, количество_байт. (адрес первого регистра указывает модулю, начиная с какого регистра требуется сохранять данные)
|
||||
virtual bool readBytes (uint8_t, uint8_t*, uint8_t); // Объявляем функцию чтения байтов данных из модуля. Аргументы: адрес_модуля, указатель_на_массив, количество_байт. (функция отличается тем, что она не отправляет модулю адрес первого регистра, а начинает цикл чтения сразу после отправки адреса модуля.)
|
||||
virtual bool writeBytes (uint8_t, uint8_t*, uint8_t); // Объявляем функцию записи байтов данных в модуль. Аргументы: адрес_модуля, указатель_на_массив, количество_байт. (функция отличается тем, что после отправки адреса модуля она сразу начинает цикл отправки данных, без передачи адреса первого регистра.)
|
||||
// ДОПОЛНИТЕЛЬНЫЕ ФУНКЦИИ: (поддерживаются библиотекой Wire) //
|
||||
virtual uint8_t getType (void); // Объявляем функцию получения типа реализации шины I2C. Аргументы: отсутствуют.
|
||||
virtual bool checkAddress(uint8_t); // Объявляем функцию поиска модуля на шине I2C. Аргументы: адрес_модуля.
|
||||
// ФУНКЦИИ НИЖНЕГО УРОВНЯ: (не поддерживаются библиотекой Wire) //
|
||||
virtual bool start (void); // Объявляем функцию установки состояния START. Аргументы: отсутствуют.
|
||||
virtual bool reStart (void); // Объявляем функцию установки состояния RESTART. Аргументы: отсутствуют.
|
||||
virtual void stop (void); // Объявляем функцию установки состояния STOP. Аргументы: отсутствуют.
|
||||
virtual bool sendID (uint8_t, bool); // Объявляем функцию передачи адреса модуля. Аргументы: ID-адрес модуля, бит RW (0-запись, 1-чтение).
|
||||
virtual bool setByte (uint8_t); // Объявляем функцию передачи байта данных. Аргументы: байт для передачи.
|
||||
virtual uint8_t getByte (bool); // Объявляем функцию получения байта данных. Аргументы: бит подтверждения ACK/NACK
|
||||
private: //
|
||||
virtual bool setSCL (bool); // Объявляем функцию установки уровня на линии SCL. Аргументы: логический уровень.
|
||||
virtual void setSDA (bool); // Объявляем функцию установки уровня на линии SDA. Аргументы: логический уровень.
|
||||
virtual bool getSDA (void); // Объявляем функцию чтения уровня с линии SDA. Аргументы: отсутствуют.
|
||||
}; //
|
||||
//
|
||||
class iarduino_I2C: public iarduino_I2C_BASE{ // Определяем производный класс
|
||||
public: // Доступные методы и функции:
|
||||
//
|
||||
/** ОСНОВНЫЕ ФУНКЦИИ: **/ //
|
||||
//
|
||||
// Функция подготовки шины I2C: // Определяем функцию подготовки шины I2C.
|
||||
void begin(uint32_t speed){ // Аргумент: скорость шины в кГц.
|
||||
// _ _ _ _______ _ _ _ //
|
||||
// SCL: _?_?_/ OUTPUT //
|
||||
// _ _ _ _ _____ _ _ _ //
|
||||
// SDA: _?_?_ _/ OUTPUT //
|
||||
// //
|
||||
#if defined(iarduino_I2C_TW) //
|
||||
// Если используется шина I2C под управлением библиотеки Wire: //
|
||||
Wire.setClock(speed*1000L); // Устанавливаем скорость передачи данных по шине I2C.
|
||||
Wire.begin(); // Инициируем работу на шине I2C в качестве мастера.
|
||||
#elif defined(iarduino_I2C_HW) //
|
||||
// Если используется аппаратная шина I2C: //
|
||||
pinMode(pin_SDA, INPUT ); digitalWrite(pin_SDA, HIGH); // Определяем вывод pin_SDA как вход и подтягиваем его к Vcc.
|
||||
pinMode(pin_SCL, INPUT ); digitalWrite(pin_SCL, HIGH); // Определяем вывод pin_SCL как вход и подтягиваем его к Vcc.
|
||||
TWBR=((F_CPU/(speed*1000L))-16)/2; // Определяем значение регистра скорости связи TWBR: speed = F_CPU / (16 + 2 * TWBR * 4^TWPS). Значит TWBR = (F_CPU/speed - 16) / (2 * 4^TWPS), но так как биты предделителя TWPS мы далее сбросим в 0, то формула упростится до: TWBR = (F_CPU/speed - 16) / 2. Так как speed указана в кГц, а F_CPU в Гц, то умножаем speed на 1000.
|
||||
if(TWBR<10){TWBR=10;} // Если значение регистра скорости TWBR стало меньше 10 (параметр speed > 400 кГ) то оставляем значение этого регистра равным 10 иначе шина будет работать нестабильно.
|
||||
TWSR&=(~(_BV(TWPS1)|_BV(TWPS0))); // Определяем значение регистра статуса TWSR: оставляем все его биты не тронутыми, кроме двух битов предделитея TWPS сбрасывая их в 0.
|
||||
#elif defined(iarduino_I2C_SW) //
|
||||
// Если используется программная шина I2C: //
|
||||
port_SDA = digitalPinToPort (pin_SDA); // Определяем номер порта для вывода pin_SDA.
|
||||
port_SCL = digitalPinToPort (pin_SCL); // Определяем номер порта для вывода pin_SCL.
|
||||
mask_SDA = digitalPinToBitMask (pin_SDA); // Определяем маску для вывода pin_SDA. в переменной mask_SDA будет установлен только тот бит который соответствует позиции бита управления выводом pin_SDA.
|
||||
mask_SCL = digitalPinToBitMask (pin_SCL); // Определяем маску для вывода pin_SCL. в переменной mask_SCL будет установлен только тот бит который соответствует позиции бита управления выводом pin_SCL.
|
||||
mod_SDA = portModeRegister (port_SDA); // Определяем указатель на адрес регистра конфигурации направления работы выводов порта port_SDA.
|
||||
mod_SCL = portModeRegister (port_SCL); // Определяем указатель на адрес регистра конфигурации направления работы выводов порта port_SCL.
|
||||
inp_SDA = portInputRegister (port_SDA); // Определяем указатель на адрес регистра входных значений для управления портом port_SDA.
|
||||
inp_SCL = portInputRegister (port_SCL); // Определяем указатель на адрес регистра входных значений для управления портом port_SCL.
|
||||
out_SDA = portOutputRegister (port_SDA); // Определяем указатель на адрес регистра выходных значений для управления портом port_SDA.
|
||||
out_SCL = portOutputRegister (port_SCL); // Определяем указатель на адрес регистра выходных значений для управления портом port_SCL.
|
||||
setSCL(1); // Переводим вывод SCL в режим входа с подтяжкой к Vcc
|
||||
setSDA(1); // Переводим вывод SDA в режим входа с подтяжкой к Vcc
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
// Функция чтения одного байта из регистра модуля: //
|
||||
uint8_t readByte(uint8_t adr, uint8_t reg){ // Определяем функцию чтения одного байта данных из регистра модуля (аргументы: адрес_модуля, адрес_регистра)
|
||||
uint8_t i=0; readBytes(adr, reg, &i, 1); return i; // Объявляем переменную i и указываем её ссылку для получения 1 байта из регистра reg модуля с адресом adr
|
||||
} //
|
||||
//
|
||||
// Функция чтения одного байта из модуля: //
|
||||
uint8_t readByte(uint8_t adr){ // Определяем функцию чтения одного байта данных из регистра модуля (аргументы: адрес_модуля)
|
||||
uint8_t i=0; readBytes(adr, &i, 1); return i; // Объявляем переменную i и указываем её ссылку для получения 1 байта из модуля с адресом adr
|
||||
} //
|
||||
//
|
||||
// Функция записи одного байта в регистр модуля: //
|
||||
bool writeByte(uint8_t adr, uint8_t reg, uint8_t data){ // Определяем функцию записи одного байта данных в регистр модуля (аргументы: адрес_модуля, адрес_регистра, байт_данных)
|
||||
return writeBytes(adr, reg, &data, 1); // Запысываем 1 байт по ссылке на переменную data в регистр reg модуля с адресом adr
|
||||
} //
|
||||
//
|
||||
// Функция записи одного байта в модуль: //
|
||||
bool writeByte(uint8_t adr, uint8_t data){ // Определяем функцию записи одного байта данных в регистр модуля (аргументы: адрес_модуля, байт_данных)
|
||||
return writeBytes(adr, &data, 1); // Запысываем 1 байт по ссылке на переменную data в модуль с адресом adr
|
||||
} //
|
||||
//
|
||||
// Функция пакетного чтения нескольких байт данных из регистров модуля: //
|
||||
bool readBytes(uint8_t adr, uint8_t reg, uint8_t *data, uint8_t sum){ // Определяем функцию пакетного чтения нескольких байт данных из регистров модуля (аргументы: адрес_модуля, адрес_первого_регистра, указатель_на_массив, количество_байт)
|
||||
#if defined(iarduino_I2C_TW) //
|
||||
// Если используется шина I2C под управлением библиотеки Wire: //
|
||||
uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это ответ по умолчанию.
|
||||
Wire.beginTransmission(adr); // Инициируем передачу данных по шине I2C к устройству с адресом adr и битом RW=0 => запись. При этом сама передача не начнётся.
|
||||
Wire.write(reg); // Определяем значение первого байта (reg - адреса регистра) который будет отправлен после байта адреса. Функция write() поместит указанный байт в буфер для передачи.
|
||||
i=Wire.endTransmission(false); if(i){return 0;} // Выполняем инициированную ранее передачу данных (без установки состояния STOP). Функция endTransmission() возвращает: 0-передача успешна / 1 - переполнен буфер для передачи / 2 - получен NACK при передаче адреса / 3 - получен NACK при передаче данных / 4 - другая ошибка.
|
||||
if(!Wire.requestFrom( adr, sum )) {return i;} // Читаем (запрашиваем) sum байт данных от устройства с адресом adr и битом RW=1 => чтение. Функция requestFrom() возвращает количество реально принятых байтов. Так как предыдущая функция не установила состояние STOP, то состояние START данной функции будет расценено как RESTART.
|
||||
while(Wire.available() && i<sum){data[i]=Wire.read(); i++;} // Читаем sum принятых байт из буфера для полученных данных в массив по указателю data.
|
||||
while(Wire.available()){Wire.read();}return i==sum; // Если в буфере для полученных данных есть еще принятые байты, то чистим буфер. Возвращаем true если удалось прочитать sum байт.
|
||||
#else //
|
||||
// Если шина управляется функциями нижнего уровня данного класса: //
|
||||
uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат записи.
|
||||
if ( start () ) { i=1; // Если на шине I2C установилось состояние START, то ...
|
||||
if ( sendID (adr,0) ) { i=2; // Если модуль ответил ACK на получение адреса устройства adr с битом RW=0 (запись), то ...
|
||||
if ( setByte (reg) ) { i=3; // Если модуль ответил ACK на получение адреса регистра i, то ...
|
||||
if ( reStart () ) { i=4; // Если на шине I2C установилось состояние RESTART, то ...
|
||||
if ( sendID (adr,1) ) { i=5; // Если модуль ответил ACK на получение адреса устройства adr с битом RW=1 (чтение), то ...
|
||||
while(sum>0){ *data=getByte(sum>1); // Получаем по одному байту из очередного регистра за каждый проход цикла while. Прочитанный байт записываются по указателю data. Аргумент функции getByte имеет значение true на всех проходах цикла кроме последнего
|
||||
data++; sum--; // Увеличиваем адрес указателя data, и уменьшаем сумму прочитанных байт sum.
|
||||
#if defined(iarduino_I2C_HW) // Проверить корректность чтения каждого байта можно только на аппаратном уровне
|
||||
if (sum) { if(TWSR&0xF8!=0x50) { i=0;}} // Если после чтения очередного байта пакета значение регистра состояния шины I2C Arduino TWSR с маской 0xF8 не равно 0x50 значит произошла ошибка при чтении
|
||||
else { if(TWSR&0xF8!=0x58) { i=0;}} // Если после чтения последного байта пакета значение регистра состояния шины I2C Arduino TWSR с маской 0xF8 не равно 0x58 значит произошла ошибка при чтении
|
||||
#endif //
|
||||
}}}}}} stop (); return i==5; // Отправляем команду STOP и возвращаем результат успешности чтения
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
// Функция пакетного чтения нескольких байт данных из модуля: //
|
||||
bool readBytes(uint8_t adr, uint8_t *data, uint8_t sum){ // Определяем функцию пакетного чтения нескольких байт данных из модуля (аргументы: адрес_модуля, указатель_на_массив, количество_байт)
|
||||
#if defined(iarduino_I2C_TW) //
|
||||
// Если используется шина I2C под управлением библиотеки Wire: //
|
||||
uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это ответ по умолчанию.
|
||||
if(!Wire.requestFrom( adr, sum )) {return i;} // Читаем (запрашиваем) sum байт данных от устройства с адресом adr и битом RW=1 => чтение. Функция requestFrom() возвращает количество реально принятых байтов.
|
||||
while(Wire.available() && i<sum){data[i]=Wire.read(); i++;} // Читаем sum принятых байт из буфера для полученных данных в массив по указателю data.
|
||||
while(Wire.available()){Wire.read();}return i==sum; // Если в буфере для полученных данных есть еще принятые байты, то чистим буфер. Возвращаем true если удалось прочитать sum байт.
|
||||
#else //
|
||||
// Если шина управляется функциями нижнего уровня данного класса: //
|
||||
uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат записи.
|
||||
if ( start () ) { i=1; // Если на шине I2C установилось состояние START, то ...
|
||||
if ( sendID (adr,1) ) { i=2; // Если модуль ответил ACK на получение адреса устройства adr с битом RW=1 (чтение), то ...
|
||||
while(sum>0){ *data=getByte(sum>1); // Получаем по одному байту из очередного регистра за каждый проход цикла while. Прочитанный байт записываются по указателю data. Аргумент функции getByte имеет значение true на всех проходах цикла кроме последнего
|
||||
data++; sum--; // Увеличиваем адрес указателя data, и уменьшаем сумму прочитанных байт sum.
|
||||
#if defined(iarduino_I2C_HW) // Проверить корректность чтения каждого байта можно только на аппаратном уровне
|
||||
if (sum) { if(TWSR&0xF8!=0x50) { i=0;}} // Если после чтения очередного байта пакета значение регистра состояния шины I2C Arduino TWSR с маской 0xF8 не равно 0x50 значит произошла ошибка при чтении
|
||||
else { if(TWSR&0xF8!=0x58) { i=0;}} // Если после чтения последного байта пакета значение регистра состояния шины I2C Arduino TWSR с маской 0xF8 не равно 0x58 значит произошла ошибка при чтении
|
||||
#endif //
|
||||
}}} stop (); return i==2; // Отправляем команду STOP и возвращаем результат успешности чтения
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
// Функция пакетной записи нескольких байт данных в регистр модуля: //
|
||||
bool writeBytes(uint8_t adr, uint8_t reg, uint8_t *data, uint8_t sum){ // Определяем функцию пакетной записи нескольких байт данных в регистры модуля (аргументы: адрес_модуля, адрес_первого_регистра, указатель_на_массив, количество_байт)
|
||||
#if defined(iarduino_I2C_TW) //
|
||||
// Если используется шина I2C под управлением библиотеки Wire: //
|
||||
uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат записи
|
||||
Wire.beginTransmission(adr); // Инициируем передачу данных по шине I2C к устройству с адресом adr и битом RW=0 => запись. При этом сама передача не начнётся.
|
||||
Wire.write(reg); // Определяем значение первого байта (reg - адреса регистра) который будет отправлен после байта адреса. Функция write() поместит указанный байт в буфер для передачи.
|
||||
Wire.write(data, sum); // Определяем значения следующих байт (data - массив данных) который будет отправлен после байта регистра. Функция write() поместит sum элементов массива data в буфер для передачи.
|
||||
i = Wire.endTransmission(); return i==0; // Выполняем инициированную ранее передачу данных. Функция endTransmission() возвращает: 0-передача успешна / 1 - переполнен буфер для передачи / 2 - получен NACK при передаче адреса / 3 - получен NACK при передаче данных / 4 - другая ошибка. В качестве параметра функция endTransmission() может принимать флаг установки стсояния STOP - по умолчанию true.
|
||||
#else //
|
||||
// Если шина управляется функциями нижнего уровня данного класса: //
|
||||
uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат записи
|
||||
if ( start () ) { i=1; // Если на шине I2C установилось состояние START, то ...
|
||||
if ( sendID (adr,0) ) { i=2; // Если модуль ответил ACK на получение адреса устройства adr с битом RW=0 (запись), то ...
|
||||
if ( setByte (reg) ) { i=3; // Если модуль ответил ACK на получение адреса регистра reg, то ...
|
||||
while(sum>0){if(!setByte(*data )) { i=0;} // Передаём один байт из массива по указателю data в очередной регистр за каждый проход цикла while. Увеличиваем адрес указателя data, и уменьшаем сумму переданных байт sum.
|
||||
data++; sum--; // Увеличиваем адрес указателя data, и уменьшаем сумму переданных байт sum.
|
||||
}}}} stop (); return i==3; // Отправляем команду STOP и возвращаем результат записи.
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
// Функция пакетной записи нескольких байт данных в модуль: //
|
||||
bool writeBytes(uint8_t adr, uint8_t *data, uint8_t sum){ // Определяем функцию пакетной записи нескольких байт данных в модуль (аргументы: адрес_модуля, указатель_на_массив, количество_байт)
|
||||
#if defined(iarduino_I2C_TW) //
|
||||
// Если используется шина I2C под управлением библиотеки Wire: //
|
||||
uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат записи
|
||||
Wire.beginTransmission(adr); // Инициируем передачу данных по шине I2C к устройству с адресом adr и битом RW=0 => запись. При этом сама передача не начнётся.
|
||||
Wire.write(data, sum); // Указываем массив данных который будет отправлен после байта адреса. Функция write() поместит sum элементов массива data в буфер для передачи.
|
||||
i = Wire.endTransmission(); return i==0; // Выполняем инициированную ранее передачу данных. Функция endTransmission() возвращает: 0-передача успешна / 1 - переполнен буфер для передачи / 2 - получен NACK при передаче адреса / 3 - получен NACK при передаче данных / 4 - другая ошибка. В качестве параметра функция endTransmission() может принимать флаг установки стсояния STOP - по умолчанию true.
|
||||
#else //
|
||||
// Если шина управляется функциями нижнего уровня данного класса: //
|
||||
uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат записи
|
||||
if ( start () ) { i=1; // Если на шине I2C установилось состояние START, то ...
|
||||
if ( sendID (adr,0) ) { i=2; // Если модуль ответил ACK на получение адреса устройства adr с битом RW=0 (запись), то ...
|
||||
while(sum>0){if(!setByte(*data )) { i=0;} // Передаём один байт из массива по указателю data в очередной регистр за каждый проход цикла while. Увеличиваем адрес указателя data, и уменьшаем сумму переданных байт sum.
|
||||
data++; sum--; // Увеличиваем адрес указателя data, и уменьшаем сумму переданных байт sum.
|
||||
}}} stop (); return i==2; // Отправляем команду STOP и возвращаем результат записи.
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
/** ДОПОЛНИТЕЛЬНЫЕ ФУНКЦИИ: **/ //
|
||||
//
|
||||
// Функция получения типа реализации шины I2C: // Определяем функцию получения типа шины Ш2С.
|
||||
uint8_t getType(void){ // Аргументы: отсутсвуют.
|
||||
#if defined(iarduino_I2C_TW) //
|
||||
return 4; // Используется аппаратная реализация шины I2C под управлением библиотеки Wire.
|
||||
#elif defined(iarduino_I2C_HW) //
|
||||
return 3; // Используется аппаратная реализация шины I2C.
|
||||
#elif defined(iarduino_I2C_HW_1) //
|
||||
return 2; // Используется аппаратная реализация шины I2C-1.
|
||||
#elif defined(iarduino_I2C_SW) //
|
||||
return 1; // Используется программная реализация шины I2C.
|
||||
#else //
|
||||
return 0; // Тип реализации шины I2C не определён.
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
// Функция проверки наличия ведомого по его адресу: //
|
||||
bool checkAddress(uint8_t adr){ // Определяем функцию записи одного байта данных в регистр модуля (аргументы: адрес_регистра, байт_данных)
|
||||
#if defined(iarduino_I2C_TW) //
|
||||
// Если используется шина I2C под управлением библиотеки Wire: //
|
||||
uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат проверки.
|
||||
Wire.beginTransmission(adr); // Инициируем передачу данных по шине I2C к устройству с адресом adr и битом RW=0 => запись. При этом сама передача не начнётся.
|
||||
i=Wire.endTransmission(); return i==0; // Выполняем инициированную ранее передачу но без данных (отправится только START, байт адреса, STOP). Функция endTransmission() возвращает: 0-передача успешна / 1 - переполнен буфер для передачи / 2 - получен NACK при передаче адреса / 3 - получен NACK при передаче данных / 4 - другая ошибка. В качестве параметра функция endTransmission() может принимать флаг установки стсояния STOP - по умолчанию true.
|
||||
#else //
|
||||
// Если шина управляется функциями нижнего уровня данного класса: //
|
||||
uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат записи.
|
||||
if ( start () ) { i=1; // Если на шине I2C установилось состояние START, то ...
|
||||
if ( sendID (adr,0) ) { i=2; // Если модуль ответил ACK на получение адреса устройства adr с битом RW=0 (запись), то ...
|
||||
}} stop (); return i==2; // Отправляем команду STOP и возвращаем результат записи.
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
/** ФУНКЦИИ НИЖНЕГО УРОВНЯ: **/ //
|
||||
//
|
||||
// Функция нижнего уровня - установка состояния START: // Определяем функцию установки состояния START.
|
||||
bool start(void){ // Аргументы: отсутствуют.
|
||||
// _ _ _ _____:___ _ _ _ //
|
||||
// SCL: : \___ _ _ _ _ _/ //
|
||||
// _ _ _ _____: _ _ _ _ _ _ // Спад логического уровня на линии SDA с «1» в «0», при наличии уровня логической «1» на линии SCL
|
||||
// SDA: :\______ _ _/_ _ _ _ _ _ //
|
||||
// //
|
||||
#if defined(iarduino_I2C_HW) //
|
||||
// Если используется аппаратная шина I2C: //
|
||||
uint16_t i=60000L; // Определяем счётчик указывая максимальное количество циклов для ожидания установки состояния start.
|
||||
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTA); // Определяем значение регистра управления TWCR: устанавливаем биты TWINT (флаг прерывания), TWEN (бит разрешения работы шины), TWSTA (бит условия START).
|
||||
while((!(TWCR & _BV(TWINT))) && i){i--;} // Ждём сброса флага прерывания TWINT в регистре управления TWCR, но не дольше чем i циклов.
|
||||
if((TWSR & 0xF8)==0x08){return true;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0x08, значит состояние START установилось на шине I2C.
|
||||
return false; // Если предыдущая строка не вернула true, значит состояние START не установилось на шине I2C, возвращаем false.
|
||||
#elif defined(iarduino_I2C_SW) //
|
||||
// Если используется программная шина I2C: //
|
||||
bool i= setSCL(1); // Устанавливаем «1» на линии SCL. Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false.
|
||||
setSDA(0); // Устанавливаем «0» на линии SDA
|
||||
setSCL(0); // Устанавливаем «0» на линии SCL
|
||||
return i; // Если счетчик i не дошел до 0 при ожидании «1» на линии SCL, то вернётся true.
|
||||
#else //
|
||||
// Если тип шины I2C не поддерживается или не определён: //
|
||||
return false; // Возвращаем false.
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
// Функция нижнего уровня - установка состояния RESTART: // Определяем функцию установки состояния RESTART.
|
||||
bool reStart(void){ // Аргументы: отсутствуют.
|
||||
// _ _ _ ___:___ _ _ _ //
|
||||
// SCL: \_ _ _ _ / : \___ _ _ _ _ _/ //
|
||||
// _ _ _ _ _ ________: _ _ _ _ _ _ // Спад логического уровня на линии SDA с «1» в «0», при наличии уровня логической «1» на линии SCL
|
||||
// SDA: _ _ _ _ _/ :\______ _ _/_ _ _ _ _ _ // Сигнал рестрат аналогичен сигналу старт и отличается лишь тем, что ему не предшествует сигнал STOP
|
||||
// //
|
||||
#if defined(iarduino_I2C_HW) //
|
||||
// Если используется аппаратная шина I2C: //
|
||||
uint16_t i=60000L; // Определяем счётчик указывая максимальное количество циклов для ожидания установки состояния RESTART.
|
||||
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTA); // Определяем значение регистра управления TWCR: устанавливаем биты TWINT (флаг прерывания), TWEN (бит разрешения работы шины), TWSTA (бит условия START).
|
||||
while((!(TWCR & _BV(TWINT))) && i){i--;} // Ждём сброса флага прерывания TWINT в регистре управления TWCR, но не дольше чем i циклов.
|
||||
if((TWSR & 0xF8)==0x10){return true;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0x10, значит состояние RESTART установилось на шине I2C.
|
||||
return false; // Если предыдущая строка не вернула true, значит состояние RESTART не установилось на шине I2C, возвращаем false.
|
||||
#elif defined(iarduino_I2C_SW) //
|
||||
// Если используется программная шина I2C: //
|
||||
return start(); // Программная реализация состояния RESTART идентична программной реализации состояния START.
|
||||
#else //
|
||||
// Если тип шины I2C не поддерживается или не определён: //
|
||||
return false; // Возвращаем false.
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
// Функция нижнего уровня - установка состояния STOP: // Определяем функцию установки состояния STOP.
|
||||
void stop(void){ // Аргументы: отсутствуют.
|
||||
// _ _ _ ___:____ _ _ _ //
|
||||
// SCL: \_ _ _ _/ : //
|
||||
// _ _ _ _ _ :____ _ _ _ // Подъём логического уровня на линии SDA с «0» в «1», при наличии уровня логической «1» на линии SCL
|
||||
// SDA: _ _ _ _ _\______/: //
|
||||
// //
|
||||
#if defined(iarduino_I2C_HW) //
|
||||
// Если используется аппаратная шина I2C: //
|
||||
uint16_t i=60000L; // Определяем счётчик указывая максимальное количество циклов для ожидания установки состояния STOP
|
||||
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); // Определяем значение регистра управления TWCR: устанавливаем биты TWINT (флаг прерывания), TWEN (бит разрешения работы шины), TWSTO (бит условия STOP).
|
||||
while((!(TWCR & _BV(TWSTO))) && i){i--;} // Ждём сброса бита условия стоп TWSTO в регистре управления TWCR, но не дольше чем i циклов
|
||||
// if((TWSR & 0xF8)==0xA0){return true;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0xA0, значит состояние STOP установилось на шине I2C.
|
||||
delayMicroseconds(20); // Данная функция не возвращает результат, но в любом случае делаем задержку меджу завершением текущего пакета и возможным началом следующего
|
||||
#elif defined(iarduino_I2C_SW) //
|
||||
// Если используется программная шина I2C: //
|
||||
setSDA(0); // Устанавливаем «0» на линии SDA
|
||||
setSCL(1); // Устанавливаем «1» на линии SCL. Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false.
|
||||
setSDA(1); // Устанавливаем «1» на линии SDA
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
// Функция нижнего уровня - передача первого байта АДРЕС+RW: // Определяем функцию передачи первого байта после cигнала START. Это байт адреса модуля с битом RW
|
||||
bool sendID(uint8_t adr, bool rw){ // Аргументы: ID-адрес модуля, бит RW (0-запись, 1-чтение)
|
||||
// 1 2 3 4 5 6 7 8 9 //
|
||||
// SCL: _ _ _ _____/\_/\_/\_/\_/\_/\_/\_/\__/\_____ _ _ _ //
|
||||
// ________________________ // Передача 7-битного адреса и 1 бита RW. На 9 тактирубщем импульсе модуль отвечает ACK («0» - «я сдесь»), или NACK («1» - «нет»)
|
||||
// SDA: _ _ _ ___/________ADDRESS_______RW>----____ _ _ _ //
|
||||
// вход //
|
||||
#if defined(iarduino_I2C_HW) //
|
||||
// Если используется аппаратная шина I2C: //
|
||||
uint16_t i=60000L; // Определяем счётчик указывая максимальное количество циклов для ожидания получения подтверждения от ведомого ввиде бита ACK.
|
||||
TWDR = (adr<<1)+rw; // Определяем значение регистра данных TWDR: записываем в него адрес adr сдвигая его на 1 бит влево, при этом младший бит (освободившийся) займёт бит RW (0-запись / 1-чтение)
|
||||
TWCR = _BV(TWINT) | _BV(TWEN); // Определяем значение регистра управления TWCR: устанавливаем биты TWINT (флаг прерывания) и TWEN (бит разрешения работы шины).
|
||||
while((!(TWCR & _BV(TWINT))) && i){i--;} // Ждём сброса флага прерывания TWINT в регистре управления TWCR, но не дольше чем i циклов
|
||||
if((TWSR & 0xF8)==0x40 && rw){return true;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0x40, значит ведомый опознал свой адрес с битом RW=1 и отправил бит подтверждения ACK (в противном случае значение регистра TWSR будет равно 0x48).
|
||||
if((TWSR & 0xF8)==0x18 && !rw){return true;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0x18, значит ведомый опознал свой адрес с битом RW=0 и отправил бит подтверждения ACK (в противном случае значение регистра TWSR будет равно 0x20).
|
||||
return false; // Если предыдущая строка не вернула true, значит на шине нет ведомых у сказанным адресом, возвращаем false.
|
||||
#elif defined(iarduino_I2C_SW) //
|
||||
// Если используется программная шина I2C: //
|
||||
bool i=true; // Определяем флаг возвращаемый данной функцией.
|
||||
uint8_t j=7; // Определяем счётчик количества переданных бит адреса.
|
||||
while(j){ j--; // Передаём 7 бит адреса adr в цикле
|
||||
setSDA(adr & bit(j)); // Устанавливаем очередной бит адреса на линии SDA
|
||||
if(!setSCL(1) ) {i=false;} // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса). Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false.
|
||||
setSCL(0); // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса)
|
||||
} setSDA(rw); // Устанавливаем бит RW на линии SDA
|
||||
if(!setSCL(1) ) {i=false;} // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса). Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false.
|
||||
setSCL(0); // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса)
|
||||
setSDA(1); // Переводим вывод SDA в режим входа с подтяжкой к Vcc
|
||||
if(!setSCL(1) ) {i=false;} // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса). Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false.
|
||||
if( getSDA( ) ) {i=false;} // Если на линии SDA установлена «1» значит ведомый ответил NACK
|
||||
setSCL(0); // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса)
|
||||
return i; // Если линия SCL была занята или модуль ответил NACK, то вернётся false.
|
||||
#else //
|
||||
// Если тип шины I2C не поддерживается или не определён: //
|
||||
return false; // Возвращаем false
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
// Функция нижнего уровня - передача одного байта данных: // Определяем функцию передачи одного байта (в любом месте между байтом адреса с битом RW=0 и сигналом STOP).
|
||||
bool setByte(uint8_t data){ // Аргумент: байт для передачи.
|
||||
// 1 2 3 4 5 6 7 8 9 //
|
||||
// SCL: _ _ _ _____/\_/\_/\_/\_/\_/\_/\_/\__/\_____ _ _ _ //
|
||||
// ________________________ // Передача 1 байта, каждый бит читается модулем по фронту тактирубщих импульсов. На 9 тактирубщем импульсе модуль отвечает ACK («0» - «принял»), или NACK («1» - «нет»)
|
||||
// SDA: _ _ _ ___/__________DATA__________>----____ _ _ _ //
|
||||
// вход //
|
||||
#if defined(iarduino_I2C_HW) //
|
||||
// Если используется аппаратная шина I2C: //
|
||||
uint16_t i=60000L; // Определяем счётчик указывая максимальное количество циклов для ожидания получения подтверждения от ведомого ввиде бита ACK.
|
||||
TWDR = data; // Определяем значение регистра данных TWDR: записываем в него байт для передачи по шине I2C.
|
||||
TWCR = _BV(TWINT) | _BV(TWEN); // Определяем значение регистра управления TWCR: устанавливаем биты TWINT (флаг прерывания) и TWEN (бит разрешения работы шины).
|
||||
while((!(TWCR & _BV(TWINT))) && i){i--;} // Ждём сброса флага прерывания TWINT в регистре управления TWCR, но не дольше чем i циклов
|
||||
if((TWSR & 0xF8)==0x28){return true;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0x28, значит ведомый отправил бит подтверждения приёма данных ACK (в противном случае значение регистра TWSR будет равно 0x30).
|
||||
return false; // Если предыдущая строка не вернула true, значит ведомый не принял отправленный ему байт, возвращаем false.
|
||||
#elif defined(iarduino_I2C_SW) //
|
||||
// Если используется программная шина I2C: //
|
||||
bool i=true; // Определяем флаг возвращаемый данной функцией.
|
||||
uint8_t j=8; // Определяем счётчик количества переданных байт.
|
||||
while(j){ j--; // Передаём 8 бит байта data в цикле
|
||||
setSDA(data & bit(j)); // Устанавливаем очередной бит байта на линии SDA
|
||||
if(!setSCL(1) ) {i=false;} // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса). Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false.
|
||||
setSCL(0); // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса)
|
||||
} setSDA(1); // Переводим вывод SDA в режим входа с подтяжкой к Vcc
|
||||
if(!setSCL(1) ) {i=false;} // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса). Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false.
|
||||
if( getSDA( ) ) {i=false;} // Если на линии SDA установлена «1» значит ведомый ответил NACK
|
||||
setSCL(0); // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса)
|
||||
return i; // Если счетчик i не дошел до 0 и не был сброшен в 0 при получении NACK, то вернётся true.
|
||||
#else //
|
||||
// Если тип шины I2C не поддерживается или не определён: //
|
||||
return false; // Возвращаем false
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
// Функция нижнего уровня - получение одного байта данных: // Определяем функцию получения одного байта (в любом месте между байтом адреса с битом RW=1 и сигналом STOP).
|
||||
uint8_t getByte(bool ack){ // Аргумент: бит подтверждения ACK/NACK
|
||||
// 1 2 3 4 5 6 7 8 9 //
|
||||
// SCL: _ _ _ _____/\_/\_/\_/\_/\_/\_/\_/\__/\_____ _ _ _ //
|
||||
// __ // Получение 1 байта, каждый бит читается мастером по фронту тактирубщих импульсов. На 9 тактирубщем импульсе мастер отвечает ACK («0» - «давай еще»), или NACK («1» - «хватит»)
|
||||
// SDA: _ _ _ ___/----------DATA-----------<__\____ _ _ _ //
|
||||
// вход выход //
|
||||
#if defined(iarduino_I2C_HW) //
|
||||
// Если используется аппаратная шина I2C: //
|
||||
uint16_t i=60000L; // Определяем счётчик указывая максимальное количество циклов для ожидания.
|
||||
TWCR = _BV(TWINT) | _BV(TWEN) | ack<<TWEA; // Определяем значение регистра управления TWCR: устанавливаем биты TWINT (флаг прерывания) и TWEN (бит разрешения работы шины), а бит TWEA (бит разрешения подтверждения) устанавливаем только если мы собираемся отправить ведомому бит подтверждения получения данных ACK.
|
||||
while((!(TWCR & _BV(TWINT))) && i){i--;} // Ждём сброса флага прерывания TWINT в регистре управления TWCR, но не дольше чем i циклов
|
||||
if((TWSR & 0xF8)==0x50 && ack){return TWDR;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0x50, значит данные приняты и сохранены в регистре TWDR, а ведомому отправлен бит ACK.
|
||||
if((TWSR & 0xF8)==0x58 && !ack){return TWDR;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0x58, значит данные приняты и сохранены в регистре TWDR, а ведомому отправлен бит NACK.
|
||||
return 0; // Если предыдущая строка не вернула принятый байт из регистра данных TWDR, значит мы не приняли байт, возвращаем 0.
|
||||
#elif defined(iarduino_I2C_SW) //
|
||||
// Если используется программная шина I2C: //
|
||||
bool i=true; // Определяем флаг возвращаемый данной функцией.
|
||||
uint8_t j=8; // Определяем счётчик количества полученных битов байта.
|
||||
uint8_t k=0; // Объявляем переменную для хранения прочитанного байта данных.
|
||||
setSDA(1); // Переводим вывод SDA в режим входа с подтяжкой к Vcc
|
||||
while(j){ j--; // Получаем 8 бит байта в цикле
|
||||
if(!setSCL(1) ) {i=false;} // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса). Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false.
|
||||
if( getSDA( ) ) { k |= (1<<j); } // Заполняем биты байта k в соотсетствии с уровнем на линии SDA
|
||||
setSCL(0); // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса)
|
||||
} setSDA(!ack); // Устанавливаем бит «ACK/NACK» на линии SDA (ACK-прижимаем, NACK-отпускаем)
|
||||
if(!setSCL(1) ) {i=false;} // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса). Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false.
|
||||
setSCL(0); // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса)
|
||||
return i?k:0; // Если во время ожидания поднятия логического уровня на линии SCL, cчётчик i не дошел до 0, то функция вернёт байт из переменной k.
|
||||
#else //
|
||||
// Если тип шины I2C не поддерживается или не определён: //
|
||||
return 0; // Возвращаем false
|
||||
#endif //
|
||||
} //
|
||||
//
|
||||
private: //
|
||||
uint8_t pin_SDA = pin_SW_SDA; // Определяем вывод pin_SDA.
|
||||
uint8_t pin_SCL = pin_SW_SCL; // Определяем вывод pin_SCL.
|
||||
uint8_t port_SDA; // Объявляем переменную для хранения номера порта вывода pin_SDA.
|
||||
uint8_t port_SCL; // Объявляем переменную для хранения номера порта вывода pin_SCL.
|
||||
uint8_t mask_SDA; // Объявляем переменную для хранения позиции бита управления выводом pin_SDA.
|
||||
uint8_t mask_SCL; // Объявляем переменную для хранения позиции бита управления выводом pin_SCL.
|
||||
volatile uint8_t *mod_SDA; // Объявляем указатель на регистр направления работы выводов порта port_SDA.
|
||||
volatile uint8_t *mod_SCL; // Объявляем указатель на регистр направления работы выводов порта port_SCL.
|
||||
volatile uint8_t *inp_SDA; // Объявляем указатель на регистр входных значений порта port_SDA.
|
||||
volatile uint8_t *inp_SCL; // Объявляем указатель на регистр входных значений порта port_SCL.
|
||||
volatile uint8_t *out_SDA; // Объявляем указатель на регистр выходных значений порта port_SDA.
|
||||
volatile uint8_t *out_SCL; // Объявляем указатель на регистр выходных значений порта port_SCL.
|
||||
//
|
||||
// функция установки уровня на линии SCL: // Определяем функцию установки уровня на линии SCL.
|
||||
bool setSCL(bool f){ // Аргумент: логический уровень.
|
||||
uint16_t i=60000L; // Определяем счётчик ожидания освобождения линии SCL.
|
||||
if(!f) {*mod_SCL |= mask_SCL; *out_SCL &= ~mask_SCL;} // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса) pinMode(pin_SCL, OUTPUT); digitalWrite(pin_SCL, LOW );
|
||||
else {*mod_SCL &= ~mask_SCL; *out_SCL |= mask_SCL; // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса) pinMode(pin_SCL, INPUT ); digitalWrite(pin_SCL, HIGH);
|
||||
while((*inp_SCL & mask_SCL)==0 && i){i--;} } // Ждём поднятия логического уровня на линии SCL while(digitalRead(pin_SCL)==0 && i){ цикл выполняется пока на линии 0 или пока i не сброситя d 0}
|
||||
return i; //
|
||||
} //
|
||||
//
|
||||
// Функция установки уровня на линии SDA: // Определяем функцию установки уровня на линии SDA.
|
||||
void setSDA(bool f){ // Аргумент: логический уровень.
|
||||
if(!f) {*mod_SDA |= mask_SDA; *out_SDA &= ~mask_SDA;} // Устанавливаем «0» на линии SDA (если бит RW=«0») pinMode(pin_SDA, OUTPUT); digitalWrite(pin_SDA, LOW );
|
||||
else {*mod_SDA &= ~mask_SDA; *out_SDA |= mask_SDA;} // Устанавливаем «1» на линии SDA (если бит RW=«1») pinMode(pin_SDA, INPUT ); digitalWrite(pin_SDA, HIGH );
|
||||
} //
|
||||
//
|
||||
// Функция чтения уровня с линии SDA: // Определяем функцию чтения уровня с линии SDA. Перед чтением необходимо вызвать функцию setSDA(1) которая переведёт вывод SDA в режим входа
|
||||
bool getSDA(void){ return (*inp_SDA & mask_SDA); } // Аргументы: отсутсвуют. digitalRead(pin_SDA);
|
||||
}; //
|
||||
//
|
||||
#endif //
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
|
@ -0,0 +1,14 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:nanoatmega328]
|
||||
platform = atmelavr
|
||||
board = nanoatmega328
|
||||
framework = arduino
|
|
@ -0,0 +1,636 @@
|
|||
#include <Arduino.h>
|
||||
|
||||
// Подключаем библиотеки:
|
||||
// Библиотека для работы I2C
|
||||
#include <Wire.h>
|
||||
//библиотека для работы со внутренней памятью ардуино
|
||||
#include <EEPROM.h>
|
||||
// Библиотека для работы с OLED дисплеем
|
||||
#include "OzOled.h"
|
||||
// Библиотека для работы с RTC
|
||||
#include "iarduino_RTC.h"
|
||||
// Библиотека для работы со светодиодами NeoPixel
|
||||
#include "iarduino_NeoPixel.h"
|
||||
// Библиотека для работы с датчиками DHT22
|
||||
#include "dhtnew.h"
|
||||
|
||||
// Распиновка
|
||||
struct {
|
||||
int pixels = 6;
|
||||
int dht = 7;
|
||||
struct {
|
||||
int up = 2;
|
||||
int down = 3;
|
||||
int right = 4;
|
||||
int left = 5;
|
||||
} button;
|
||||
int humi = 13;
|
||||
int heat = 8;
|
||||
} pin;
|
||||
|
||||
// Инициализируем объекты устройств
|
||||
// Объявляем объект часов реального времени
|
||||
iarduino_RTC rtc(RTC_DS3231);
|
||||
// Объявляем объект LED указывая (№ вывода Arduino к которому подключён модуль NeoPixel, количество используемых светодиодов)
|
||||
iarduino_NeoPixel led(pin.pixels, 1);
|
||||
DHTNEW sensor(pin.dht);
|
||||
|
||||
//#pragma once
|
||||
|
||||
// (Horizontal) byte array of heater icon 16 x 16 px:
|
||||
const unsigned char bmpHeater[] PROGMEM = {
|
||||
0xe0, 0xf8, 0xfe, 0x1c, 0x00, 0xe0, 0xf8, 0xfe, 0x1c, 0x00, 0xe0, 0xf8, 0xfe, 0x1c, 0x00, 0x00,
|
||||
0xe3, 0xff, 0xef, 0xe7, 0xe0, 0xe3, 0xff, 0xef, 0xe7, 0xe0, 0xe3, 0xff, 0xef, 0xe7, 0xe0, 0x00,
|
||||
};
|
||||
// (Horizontal) byte array of humidifier icon 16 x 16 px:
|
||||
const unsigned char bmpHumidifier[] PROGMEM = {
|
||||
0x00, 0x54, 0x00, 0xaa, 0x00, 0x54, 0x00, 0xaa, 0x00, 0x54, 0x00, 0xaa, 0x00, 0x54, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x95, 0xc0, 0xea, 0xc0, 0x95, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
struct menuItem {
|
||||
int Len;
|
||||
char* Text[7];
|
||||
};
|
||||
|
||||
struct Time {
|
||||
byte Hours;
|
||||
byte Minutes;
|
||||
};
|
||||
|
||||
struct Date {
|
||||
byte Day;
|
||||
byte Month;
|
||||
};
|
||||
|
||||
// Глобальные переменные
|
||||
struct {
|
||||
// Состояние кнопок
|
||||
struct {
|
||||
bool Up;
|
||||
bool Down;
|
||||
bool Left;
|
||||
bool Right;
|
||||
} Button;
|
||||
// Данные сенсоров
|
||||
struct {
|
||||
float Temp;
|
||||
float Humi;
|
||||
} Sensor;
|
||||
// Состояние исполнительных устройств
|
||||
struct {
|
||||
bool Heater;
|
||||
bool Humidifier;
|
||||
} Actor;
|
||||
// Пункты меню и положение курсора
|
||||
struct {
|
||||
bool Show = false;
|
||||
int Sect = 0;
|
||||
int Pos = 1;
|
||||
int PosOld = 1;
|
||||
menuItem Item[6] = {
|
||||
{6, {">>>>> MENU <<<<<", "TIMES...", "DROUGHTS...", "MONSOONS...", "PANIC LIMS...", "SYSTEM..."}},
|
||||
{5, {">>>> TIMES <<<<<", "Date/Time", "Sunrize", "Sunset", "Latency (min)"}},
|
||||
{6, {">>> DROUGHTS <<<", "Pick date", "Temp. daytime", "Temp. night", "Humi. daytime", "Humi. night"}},
|
||||
{6, {">>> MONSOONS <<<", "Pick date", "Temp. daytime", "Temp. night", "Humi. daytime", "Humi. night"}},
|
||||
{5, {"> PANIC LIMITS <", "Temp. MAX", "Temp. MIN", "Humi. MAX", "Humi. MIN"}},
|
||||
{2, {">>>> SYSTEM <<<<", "Reset"}},
|
||||
};
|
||||
} Menu;
|
||||
struct {
|
||||
struct {
|
||||
Time Sunrize;
|
||||
Time Sunset;
|
||||
byte Latency;
|
||||
} Times;
|
||||
struct {
|
||||
Date Pick;
|
||||
byte TempDaytime;
|
||||
byte TempNight;
|
||||
byte HumiDaytime;
|
||||
byte HumiNight;
|
||||
} Droughts;
|
||||
struct {
|
||||
Date Pick;
|
||||
byte TempDaytime;
|
||||
byte TempNight;
|
||||
byte HumiDaytime;
|
||||
byte HumiNight;
|
||||
} Monsoons;
|
||||
struct {
|
||||
byte TempMAX;
|
||||
byte TempMIN;
|
||||
byte HumiMAX;
|
||||
byte HumiMIN;
|
||||
} Limits;
|
||||
} Params;
|
||||
} state;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
//вспоминаем данные из EEPROM
|
||||
EEPROM.get(0, state.Params);
|
||||
|
||||
// Объявляем пины кнопок
|
||||
pinMode(pin.button.up, INPUT);
|
||||
pinMode(pin.button.down, INPUT);
|
||||
pinMode(pin.button.right, INPUT);
|
||||
pinMode(pin.button.left, INPUT);
|
||||
// Объявляем пины акторов
|
||||
pinMode(pin.humi, OUTPUT);
|
||||
pinMode(pin.heat, OUTPUT);
|
||||
|
||||
// Объявляем пины исполнительных устройств
|
||||
// Инициализируем адресные светодиоды:
|
||||
led.begin();
|
||||
// Выключаем все светодиоды и ждём завершение инициализации и калибровки (задержка 0,3с):
|
||||
led.setColor(NeoPixelAll, 254, 0, 0); led.write(); delay(300);
|
||||
led.setColor(NeoPixelAll, 0, 254, 0); led.write(); delay(300);
|
||||
led.setColor(NeoPixelAll, 0, 0, 254); led.write(); delay(300);
|
||||
led.setColor(NeoPixelAll, 0, 0, 0); led.write(); delay(300);
|
||||
// Инициализируем дисплей на шине I2C
|
||||
OzOled.init();
|
||||
OzOled.clearDisplay();
|
||||
// Инициализируем часы и устанавливаем время/дату в вформате (сек(0+), мин(0+), час(0+), число(0+), месяц(0+), год(2000+), день недели(0+)).
|
||||
rtc.begin();
|
||||
// Настройка сенсоров
|
||||
sensor.setSuppressError(true);
|
||||
}
|
||||
|
||||
void readButtons();
|
||||
void readSensors();
|
||||
void actorLeds();
|
||||
void screenTime();
|
||||
void screenMenu();
|
||||
void screenSetRTC(byte MenuSect, byte MenuPos);
|
||||
uint16_t screenSetScalar(byte MenuSect, byte MenuPos, uint16_t param, uint16_t limitMIN, uint16_t limitMAX);
|
||||
Date screenSetDate(byte MenuSect, byte MenuPos, Date date);
|
||||
Time screenSetTime(byte MenuSect, byte MenuPos, Time time);
|
||||
void actorLeds();
|
||||
void OLEDPrintDigits16(uint16_t number, uint8_t signs, uint8_t column, uint8_t line);
|
||||
|
||||
|
||||
// Основной цикл
|
||||
void loop() {
|
||||
// Опрос кнопок
|
||||
readButtons();
|
||||
// Опрос датчиков
|
||||
readSensors();
|
||||
// Расчет параметров
|
||||
// Отработка исполнительных
|
||||
actorLeds();
|
||||
|
||||
// Отображение и селектор экранов
|
||||
if (state.Menu.Show) {
|
||||
screenMenu();
|
||||
} else {
|
||||
screenTime();
|
||||
}
|
||||
}
|
||||
|
||||
// Функция опроса сенсорных кнопок.
|
||||
void readButtons() {
|
||||
state.Button.Up = (digitalRead(pin.button.up) == HIGH);
|
||||
state.Button.Down =(digitalRead(pin.button.down) == HIGH);
|
||||
state.Button.Right = (digitalRead(pin.button.right) == HIGH);
|
||||
state.Button.Left = (digitalRead(pin.button.left) == HIGH);
|
||||
}
|
||||
|
||||
// Функция опроса датчиков температуры и влажности.
|
||||
void readSensors() {
|
||||
if (millis() - sensor.lastRead() > 2000) {
|
||||
sensor.setDisableIRQ(false);
|
||||
if (sensor.read() != DHTLIB_WAITING_FOR_READ) {
|
||||
state.Sensor.Temp = sensor.getTemperature();
|
||||
state.Sensor.Humi = sensor.getHumidity();
|
||||
}
|
||||
sensor.setDisableIRQ(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Экран основного режима
|
||||
void screenTime() {
|
||||
// Верхняя строка (текущая температура и влажность)
|
||||
OzOled.printNumber16(state.Sensor.Temp, 0, 0); OzOled.printString16("C", 4, 0);
|
||||
OzOled.printNumber16(state.Sensor.Humi, 10, 0); OzOled.printString16("%", 14, 0);
|
||||
// Мигалка
|
||||
if ((millis() / 1000) % 2 == 0) {
|
||||
OzOled.printBigNumber(rtc.gettime("H:i"), 0, 2);
|
||||
// Состояние нагревателя в верхней строке
|
||||
if (state.Actor.Heater) {
|
||||
OzOled.drawBitmap(bmpHeater, 6, 0, 2, 2);
|
||||
}
|
||||
// Состояние увлажнителя в верхней строке
|
||||
if (state.Actor.Humidifier) {
|
||||
OzOled.drawBitmap(bmpHumidifier, 8, 0, 2, 2);
|
||||
}
|
||||
} else {
|
||||
OzOled.printBigNumber(rtc.gettime("H i"), 0, 2);
|
||||
// Выключаем иконки акторов
|
||||
OzOled.printString16(" ", 6, 0);
|
||||
OzOled.printString16(" ", 8, 0);
|
||||
}
|
||||
// Нижняя строка (текущая дата)
|
||||
OzOled.printString16(rtc.gettime("d-m-y"), 0, 6);
|
||||
// Вызов меню по кнопке RIGHT
|
||||
if (state.Button.Right) {
|
||||
OzOled.clearDisplay();
|
||||
state.Menu.Show = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Экран режима меню
|
||||
void screenMenu() {
|
||||
// Debug section
|
||||
// Serial.print("MenuSection: "); Serial.print(state.Menu.Sect); Serial.print(" MenuPosition: "); Serial.print(state.Menu.Pos); Serial.println(" ");
|
||||
|
||||
// Отображение заголовка меню
|
||||
OzOled.printString(state.Menu.Item[state.Menu.Sect].Text[0], 0, 0);
|
||||
// Отображение пунктов меню
|
||||
for (int i = 1; i < state.Menu.Item[state.Menu.Sect].Len; i++) {
|
||||
OzOled.printString(state.Menu.Item[state.Menu.Sect].Text[i], 2, i);
|
||||
}
|
||||
// Отображение курсора
|
||||
if (state.Menu.PosOld != state.Menu.Pos) {
|
||||
OzOled.printString(" ", 0, state.Menu.PosOld);
|
||||
}
|
||||
OzOled.printString("#>", 0, state.Menu.Pos);
|
||||
// Опрос состояния кнопок
|
||||
// На позицию вверх
|
||||
if (state.Button.Up && state.Menu.Pos > 1 && state.Menu.Pos <= state.Menu.Item[state.Menu.Sect].Len-1) {
|
||||
state.Menu.PosOld = state.Menu.Pos; state.Menu.Pos--;
|
||||
}
|
||||
// На позицию вниз
|
||||
if (state.Button.Down && state.Menu.Pos >= 1 && state.Menu.Pos < state.Menu.Item[state.Menu.Sect].Len-1) {
|
||||
state.Menu.PosOld = state.Menu.Pos; state.Menu.Pos++;
|
||||
}
|
||||
// Переходы вглубь меню и запуск сеттеров
|
||||
if (state.Button.Right) {
|
||||
switch (state.Menu.Sect) {
|
||||
case 0:
|
||||
state.Menu.Sect = state.Menu.Pos;
|
||||
state.Menu.Pos = 1;
|
||||
OzOled.clearDisplay();
|
||||
break;
|
||||
case 1:
|
||||
switch (state.Menu.Pos) {
|
||||
case 1:
|
||||
// Set Date/Time
|
||||
screenSetRTC(state.Menu.Sect, state.Menu.Pos);
|
||||
break;
|
||||
case 2:
|
||||
// Set Sunrize
|
||||
state.Params.Times.Sunrize = screenSetTime(state.Menu.Sect, state.Menu.Pos, state.Params.Times.Sunrize);
|
||||
break;
|
||||
case 3:
|
||||
// Set Sunset
|
||||
state.Params.Times.Sunset = screenSetTime(state.Menu.Sect, state.Menu.Pos, state.Params.Times.Sunset);
|
||||
break;
|
||||
case 4:
|
||||
// Set Latency
|
||||
state.Params.Times.Latency = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Times.Latency, 0, 99);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (state.Menu.Pos) {
|
||||
case 1:
|
||||
// Set Pick date
|
||||
state.Params.Droughts.Pick = screenSetDate(state.Menu.Sect, state.Menu.Pos, state.Params.Droughts.Pick);
|
||||
break;
|
||||
case 2:
|
||||
// Set Temp. daytime
|
||||
state.Params.Droughts.TempDaytime = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Droughts.TempDaytime, state.Params.Limits.TempMIN, state.Params.Limits.TempMAX);
|
||||
break;
|
||||
case 3:
|
||||
// Set Temp. night
|
||||
state.Params.Droughts.TempNight = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Droughts.TempNight, state.Params.Limits.TempMIN, state.Params.Limits.TempMAX);
|
||||
break;
|
||||
case 4:
|
||||
// Set Humi. daytime
|
||||
state.Params.Droughts.HumiDaytime = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Droughts.HumiDaytime, state.Params.Limits.HumiMIN, state.Params.Limits.HumiMAX);
|
||||
break;
|
||||
case 5:
|
||||
// Set Humi. nights
|
||||
state.Params.Droughts.HumiNight = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Droughts.HumiNight, state.Params.Limits.HumiMIN, state.Params.Limits.HumiMAX);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
switch (state.Menu.Pos) {
|
||||
case 1:
|
||||
// Set Pick date
|
||||
state.Params.Monsoons.Pick = screenSetDate(state.Menu.Sect, state.Menu.Pos, state.Params.Monsoons.Pick);
|
||||
break;
|
||||
case 2:
|
||||
// Set Temp. daytime
|
||||
state.Params.Monsoons.TempDaytime = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Monsoons.TempDaytime, state.Params.Limits.TempMIN, state.Params.Limits.TempMAX);
|
||||
break;
|
||||
case 3:
|
||||
// Set Temp. night
|
||||
state.Params.Monsoons.TempNight = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Monsoons.TempNight, state.Params.Limits.TempMIN, state.Params.Limits.TempMAX);
|
||||
break;
|
||||
case 4:
|
||||
// Set Humi. daytime
|
||||
state.Params.Monsoons.HumiDaytime = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Monsoons.HumiDaytime, state.Params.Limits.HumiMIN, state.Params.Limits.HumiMAX);
|
||||
break;
|
||||
case 5:
|
||||
// Set Humi. nights
|
||||
state.Params.Monsoons.HumiNight = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Monsoons.HumiNight, state.Params.Limits.HumiMIN, state.Params.Limits.HumiMAX);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
switch (state.Menu.Pos) {
|
||||
case 1:
|
||||
// Temp. MAX
|
||||
state.Params.Limits.TempMAX = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Limits.TempMAX, state.Params.Limits.TempMIN, 98);
|
||||
break;
|
||||
case 2:
|
||||
// Temp. MIN
|
||||
state.Params.Limits.TempMIN = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Limits.TempMIN, 0, state.Params.Limits.TempMAX);
|
||||
break;
|
||||
case 3:
|
||||
// Humi. MAX
|
||||
state.Params.Limits.HumiMAX = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Limits.HumiMAX, state.Params.Limits.HumiMIN, 98);
|
||||
break;
|
||||
case 4:
|
||||
// Humi. MIN
|
||||
state.Params.Limits.HumiMIN = screenSetScalar(state.Menu.Sect, state.Menu.Pos, state.Params.Limits.HumiMIN, 0, state.Params.Limits.HumiMAX);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
switch (state.Menu.Pos) {
|
||||
case 1:
|
||||
// EEPROM reset
|
||||
for (uint16_t i = 0; i < EEPROM.length(); i++) EEPROM.update(i, 0);
|
||||
EEPROM.get(0, state.Params);
|
||||
OzOled.clearDisplay();
|
||||
for (uint8_t i=0;i<5;i++) {
|
||||
OzOled.printString16("RESETING", 0, 2);
|
||||
OzOled.printString16("EEPROM", 2, 5);
|
||||
delay(500);
|
||||
OzOled.clearDisplay();
|
||||
}
|
||||
state.Menu.Show=false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Переход вверх по меню, запись в EEPROM и выход
|
||||
if (state.Button.Left && state.Menu.Sect>0) {
|
||||
state.Menu.Pos=state.Menu.Sect;
|
||||
state.Menu.Sect=0;
|
||||
OzOled.clearDisplay();
|
||||
} else if (state.Button.Left) {
|
||||
state.Menu.Show=false;
|
||||
EEPROM.put(0, state.Params);
|
||||
OzOled.clearDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
// Экран установки часов реального времени
|
||||
void screenSetRTC(byte MenuSect, byte MenuPos) {
|
||||
OzOled.clearDisplay();
|
||||
rtc.gettime();
|
||||
struct dateTime {
|
||||
uint16_t digit;
|
||||
uint16_t column;
|
||||
uint16_t line;
|
||||
};
|
||||
dateTime t[5] ={
|
||||
{rtc.day, 0, 3},
|
||||
{rtc.month, 6, 3},
|
||||
{rtc.year, 12, 3},
|
||||
{rtc.Hours, 3, 6},
|
||||
{rtc.minutes, 9, 6},
|
||||
};
|
||||
uint8_t pos = 0;
|
||||
// Отображение заголовка пункта меню
|
||||
OzOled.printString(state.Menu.Item[MenuSect].Text[MenuPos], 0, 0);
|
||||
while(true){
|
||||
// Опрос кнопок
|
||||
readButtons();
|
||||
// Отображение значения даты и времени
|
||||
for (int i = 0; i < 5; i++) {
|
||||
OLEDPrintDigits16(t[i].digit, 2, t[i].column, t[i].line);
|
||||
}
|
||||
OzOled.printString16("-", 4, t[0].line);
|
||||
OzOled.printString16("-", 10, t[1].line);
|
||||
OzOled.printString16(":", 7, t[3].line);
|
||||
|
||||
// Отображение курсора
|
||||
OzOled.printString16(" ", t[pos].column, t[pos].line);
|
||||
delay(300);
|
||||
|
||||
// Сдвигаем курсор вправо
|
||||
if (state.Button.Right) {
|
||||
if (pos <4) {
|
||||
pos++;
|
||||
} else {
|
||||
pos=0;
|
||||
}
|
||||
}
|
||||
|
||||
// Увеличиваем
|
||||
if (state.Button.Up && ((pos==0&&t[pos].digit<31)||(pos==1&&t[pos].digit<12)||(pos==2&&t[pos].digit<99)||(pos==3&&t[pos].digit<23)||(pos==4&&t[pos].digit<59))) {
|
||||
t[pos].digit++;
|
||||
}
|
||||
// Уменьшаем
|
||||
if (state.Button.Down && ((pos<=1&&t[pos].digit>1)||(pos>1&&t[pos].digit>0))) {
|
||||
t[pos].digit--;
|
||||
}
|
||||
|
||||
// Сохранить и выйти
|
||||
if (state.Button.Left) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
rtc.settime(rtc.seconds, t[4].digit, t[3].digit, t[0].digit, t[1].digit, t[2].digit, rtc.weekday);
|
||||
}
|
||||
|
||||
// Экран установки скалярных значений
|
||||
uint16_t screenSetScalar(byte MenuSect, byte MenuPos, uint16_t param, uint16_t limitMIN, uint16_t limitMAX) {
|
||||
OzOled.clearDisplay();
|
||||
if (param>limitMAX) {
|
||||
param=limitMAX;
|
||||
}
|
||||
if (param<limitMIN) {
|
||||
param=limitMIN;
|
||||
}
|
||||
// Отображение заголовка пункта меню
|
||||
OzOled.printString(state.Menu.Item[MenuSect].Text[MenuPos], 0, 0);
|
||||
while(true){
|
||||
// Опрос кнопок
|
||||
readButtons();
|
||||
// Отображение параметра
|
||||
OLEDPrintDigits16(param, 2, 6, 3);
|
||||
|
||||
// Увеличиваем
|
||||
if (state.Button.Up && param<limitMAX) {
|
||||
param++;
|
||||
}
|
||||
// Уменьшаем
|
||||
if (state.Button.Down && param>limitMIN) {
|
||||
param--;
|
||||
}
|
||||
|
||||
// Сохранить и выйти
|
||||
if (state.Button.Left) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Экран установки Даты
|
||||
Date screenSetDate(byte MenuSect, byte MenuPos, Date date) {
|
||||
OzOled.clearDisplay();
|
||||
rtc.gettime();
|
||||
struct dateMonth {
|
||||
uint8_t digit;
|
||||
uint8_t column;
|
||||
uint8_t line;
|
||||
};
|
||||
if (date.Day == 0) {
|
||||
date.Day=1;
|
||||
}
|
||||
if (date.Month == 0) {
|
||||
date.Month=1;
|
||||
}
|
||||
dateMonth t[2] ={
|
||||
{date.Day, 2, 4},
|
||||
{date.Month, 8, 4},
|
||||
};
|
||||
uint8_t pos = 0;
|
||||
// Отображение заголовка пункта меню
|
||||
OzOled.printString(state.Menu.Item[MenuSect].Text[MenuPos], 0, 0);
|
||||
while(true){
|
||||
// Опрос кнопок
|
||||
readButtons();
|
||||
// Отображение значения
|
||||
for (int i = 0; i < 2; i++) {
|
||||
OLEDPrintDigits16(t[i].digit, 2, t[i].column, t[i].line);
|
||||
}
|
||||
OzOled.printString16("-", 6, t[0].line);
|
||||
|
||||
// Отображение курсора
|
||||
OzOled.printString16(" ", 0, t[pos].line+2);
|
||||
OzOled.printString16("--", t[pos].column, t[pos].line+2);
|
||||
|
||||
// Сдвигаем курсор вправо
|
||||
if (state.Button.Right) {
|
||||
if (pos==0) {
|
||||
pos++;
|
||||
} else {
|
||||
pos=0;
|
||||
}
|
||||
}
|
||||
|
||||
// Увеличиваем
|
||||
if (state.Button.Up && ((pos==0&&t[pos].digit<28)||(pos==1&&t[pos].digit<12))) {
|
||||
t[pos].digit++;
|
||||
}
|
||||
// Уменьшаем
|
||||
if (state.Button.Down && t[pos].digit>1) {
|
||||
t[pos].digit--;
|
||||
}
|
||||
|
||||
// Сохранить и выйти
|
||||
if (state.Button.Left) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Date{t[0].digit, t[1].digit};
|
||||
}
|
||||
|
||||
|
||||
// Экран установки Времени
|
||||
Time screenSetTime(byte MenuSect, byte MenuPos, Time time) {
|
||||
OzOled.clearDisplay();
|
||||
rtc.gettime();
|
||||
struct timeSlice {
|
||||
uint8_t digit;
|
||||
uint8_t column;
|
||||
uint8_t line;
|
||||
};
|
||||
timeSlice t[2] ={
|
||||
{time.Hours, 2, 4},
|
||||
{time.Minutes, 8, 4},
|
||||
};
|
||||
uint8_t pos = 0;
|
||||
// Отображение заголовка пункта меню
|
||||
OzOled.printString(state.Menu.Item[MenuSect].Text[MenuPos], 0, 0);
|
||||
while(true){
|
||||
// Опрос кнопок
|
||||
readButtons();
|
||||
// Отображение значения
|
||||
for (int i = 0; i < 2; i++) {
|
||||
OLEDPrintDigits16(t[i].digit, 2, t[i].column, t[i].line);
|
||||
}
|
||||
OzOled.printString16(":", 6, t[0].line);
|
||||
|
||||
// Отображение курсора
|
||||
OzOled.printString16(" ", 0, t[pos].line+2);
|
||||
OzOled.printString16("--", t[pos].column, t[pos].line+2);
|
||||
|
||||
// Сдвигаем курсор вправо
|
||||
if (state.Button.Right) {
|
||||
if (pos==0) {
|
||||
pos++;
|
||||
} else {
|
||||
pos=0;
|
||||
}
|
||||
}
|
||||
|
||||
// Увеличиваем
|
||||
if (state.Button.Up && ((pos==0&&t[pos].digit<23)||(pos==1&&t[pos].digit<59))) {
|
||||
t[pos].digit++;
|
||||
}
|
||||
// Уменьшаем
|
||||
if (state.Button.Down && t[pos].digit>0) {
|
||||
t[pos].digit--;
|
||||
}
|
||||
|
||||
// Сохранить и выйти
|
||||
if (state.Button.Left) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Time{t[0].digit, t[1].digit};
|
||||
}
|
||||
|
||||
// Отработка светодиодом в зависимости от влажности
|
||||
void actorLeds() {
|
||||
if (state.Sensor.Humi < state.Params.Limits.HumiMIN) {
|
||||
led.setColor(0, 100, 0, 0); led.write();
|
||||
state.Actor.Humidifier=true;
|
||||
digitalWrite(pin.humi, HIGH);
|
||||
} else if (state.Sensor.Humi < state.Params.Limits.HumiMAX) {
|
||||
led.setColor(0, 0, 3, 0); led.write();
|
||||
state.Actor.Humidifier=false;
|
||||
digitalWrite(pin.humi, LOW);
|
||||
} else {
|
||||
led.setColor(0, 20, 10, 0); led.write();
|
||||
state.Actor.Humidifier=false;
|
||||
digitalWrite(pin.humi, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
// Принтер в форматированные цифры
|
||||
void OLEDPrintDigits16(uint16_t number, uint8_t signs, uint8_t column, uint8_t line) {
|
||||
// Определяем кол-во знаков
|
||||
int n = String(number).length();
|
||||
// Валидируем ввод
|
||||
if (signs>=n) {
|
||||
for (byte i=0;i<signs-n;i++) {
|
||||
OzOled.printNumber16(0, column+i*2, line);
|
||||
}
|
||||
OzOled.printNumber16(number, column+(signs-n)*2, line);
|
||||
} else {
|
||||
for (byte i=0;i<signs;i++) {
|
||||
OzOled.printString16("#", column+i*2, line);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
This directory is intended for PlatformIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
Loading…
Reference in New Issue