Skip to content

Commit 36f5274

Browse files
Initial code for SeeedStudio monochrome GROVE 16x2 LCD
Based on the common class EBF_AiP31068_I2C_16x2_LCD
1 parent f076aa2 commit 36f5274

10 files changed

+506
-1
lines changed

src/Core/EBF_PlugAndPlayDevice.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ typedef enum : uint32_t {
4343
PNP_ID_2INPUT, // Module with 2 inputs connected directly to interrupt lines
4444
PNP_ID_2BUTTONS_INPUT, // Module with 2 buttons connected directly to interrupt lines
4545
PNP_ID_SPARKFUN_QWIIC_SERLCD, // SparkFun QWIIC SerLCD. Both 2 and 4 rows versions
46+
PNP_ID_SEEED_MONOCHROME_GROVE_16x2_LCD, // SeeedStudio monochrome GROVE 16x2 LCD. Black on Yellow, Black on Red, White on Blue
4647
} PnP_DeviceId;
4748

4849
// 2 bytes

src/EBF_PlugAndPlay.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323
#include "PlugAndPlay/PnP_Module_2Input.h"
2424
#include "PlugAndPlay/PnP_Module_2ButtonsInput.h"
2525
#include "PlugAndPlay/PnP_Module_SparkFun_QWIIC_SerLCD.h"
26+
#include "PlugAndPlay/PnP_Module_Seeed_Monochrome_GROVE_16x2_LCD.h"
2627

2728
#endif

src/EBF_Products.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@
1717
#include "Products/EBF_Servo.h"
1818
#include "Products/EBF_HC-SR04_DistanceSensor.h"
1919
#include "Products/EBF_STTS22H_TemperatureSensor.h"
20-
20+
#include "Products/EBF_SparkFun_QWIIC_SerLCD.h"
21+
#include "Products/EBF_Seeed_Monochrome_GROVE_16x2_LCD.h"
2122
#endif
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include <Arduino.h>
2+
#include "EBF.h"
3+
#include "EBF_PlugAndPlay.h"
4+
#include "EBF_PlugAndPlayManager.h"
5+
6+
// EBF objects creation, should be global
7+
EBF_Core EBF;
8+
PnP_StatusLed led;
9+
10+
void setup()
11+
{
12+
uint8_t rc;
13+
14+
// EBF is the first thing that should be initialized, with the maximum timers to be used
15+
EBF.Init();
16+
17+
// Status LED initialization
18+
led.Init();
19+
20+
// Device EEPROM programming code
21+
PnP_DeviceInfo deviceInfo;
22+
memset(&deviceInfo, 0, sizeof(PnP_DeviceInfo));
23+
24+
deviceInfo.headerId = 0x506E502A; // "PnP*"
25+
deviceInfo.version = 1;
26+
deviceInfo.deviceIDs[0] = PNP_ID_SEEED_MONOCHROME_GROVE_16x2_LCD;
27+
deviceInfo.numberOfEndpoints = 1;
28+
deviceInfo.endpointData[0].endpointId = 1;
29+
deviceInfo.endpointData[0].i2cAddress = 0x3E;
30+
deviceInfo.numberOfInterrupts = 0;
31+
32+
rc = EBF_PlugAndPlayManager::WriteDeviceEEPROM(0x50 + EBF_PlugAndPlayManager::PNP_EEPROM_DEVICE, deviceInfo);
33+
if (rc == EBF_OK) {
34+
led.Blink(100, 900);
35+
} else {
36+
led.Blink(100, 100);
37+
}
38+
}
39+
40+
void loop()
41+
{
42+
// Let EBF to do all the processing
43+
// Your logic should be done in the callback functions
44+
EBF.Process();
45+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include "PnP_Module_Seeed_Monochrome_GROVE_16x2_LCD.h"
2+
3+
PnP_Module_Seeed_Monochrome_GROVE_16x2_LCD::PnP_Module_Seeed_Monochrome_GROVE_16x2_LCD() : EBF_Seeed_Monochrome_GROVE_16x2_LCD(NULL)
4+
{
5+
this->type = HAL_Type::PnP_DEVICE;
6+
this->id = PnP_DeviceId::PNP_ID_SEEED_MONOCHROME_GROVE_16x2_LCD;
7+
}
8+
9+
uint8_t PnP_Module_Seeed_Monochrome_GROVE_16x2_LCD::Init()
10+
{
11+
uint8_t rc = EBF_OK;
12+
PnP_DeviceInfo deviceInfo;
13+
uint8_t endpointIndex;
14+
EBF_PlugAndPlayI2C *pPnPI2C;
15+
EBF_PlugAndPlayHub *pAssignedHub;
16+
17+
EBF_PlugAndPlayManager *pPnpManager = EBF_PlugAndPlayManager::GetInstance();
18+
19+
// Assign the current instance to physical PnP device and get all needed information
20+
rc = pPnpManager->AssignDevice(this, deviceInfo, endpointIndex, &pPnPI2C, &pAssignedHub);
21+
if(rc != EBF_OK) {
22+
return rc;
23+
}
24+
25+
pI2C = pPnPI2C;
26+
27+
// Initialize the device
28+
rc = EBF_Seeed_Monochrome_GROVE_16x2_LCD::Init(deviceInfo.endpointData[endpointIndex].i2cAddress);
29+
if (rc != EBF_OK) {
30+
return rc;
31+
}
32+
33+
// Fix type and ID after the EBF_Instance init
34+
this->type = HAL_Type::PnP_DEVICE;
35+
this->id = PnP_DeviceId::PNP_ID_SEEED_MONOCHROME_GROVE_16x2_LCD;
36+
// PnP is interrupt driven, no polling is needed. And this is an output-only device.
37+
this->pollIntervalMs = EBF_NO_POLLING;
38+
39+
// Attach interrupt lines for that device
40+
rc = pAssignedHub->AssignInterruptLines(pPnPI2C->GetPortNumber(), endpointIndex, deviceInfo);
41+
if (rc != EBF_OK) {
42+
return rc;
43+
}
44+
45+
return rc;
46+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#ifndef __PnP_MODULE_SEEED_MONOCHROME_GROVE_16x2_LCD_H__
2+
#define __PnP_MODULE_SEEED_MONOCHROME_GROVE_16x2_LCD_H__
3+
4+
#include <Arduino.h>
5+
#if __has_include("EBF_Config.h")
6+
#include "EBF_Config.h"
7+
#endif
8+
9+
#include "../Core/EBF_Global.h"
10+
#include "../Core/EBF_PlugAndPlayDevice.h"
11+
#include "../Core/EBF_PlugAndPlayManager.h"
12+
#include "../Core/EBF_PlugAndPlayI2C.h"
13+
#include "../Products/EBF_Seeed_Monochrome_GROVE_16x2_LCD.h"
14+
#include <Wire.h>
15+
16+
class PnP_Module_Seeed_Monochrome_GROVE_16x2_LCD : public EBF_Seeed_Monochrome_GROVE_16x2_LCD {
17+
public:
18+
PnP_Module_Seeed_Monochrome_GROVE_16x2_LCD();
19+
20+
uint8_t Init();
21+
22+
};
23+
24+
#endif
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
#include "EBF_AiP31068_I2C_16x2_LCD.h"
2+
3+
extern void EBF_EmptyCallback();
4+
5+
// Part of the code is based on the Grove_LCD_RGB_Backlight
6+
// https://github.com/Seeed-Studio/Grove_LCD_RGB_Backlight
7+
8+
// Initializing EBF_AiP31068_I2C_16x2_LCD class instance.
9+
// The i2cAddress should specify the device I2C address (0x3E default)
10+
uint8_t EBF_AiP31068_I2C_16x2_LCD::Init(uint8_t i2cAddress)
11+
{
12+
uint8_t rc;
13+
14+
this->i2cAddress = i2cAddress;
15+
16+
rc = EBF_HalInstance::Init(HAL_Type::I2C_INTERFACE, i2cAddress);
17+
if (rc != EBF_OK) {
18+
return rc;
19+
}
20+
21+
// This is output only device, polling is not needed
22+
SetPollingInterval(EBF_NO_POLLING);
23+
24+
displayControl = 0;
25+
displayMode = 0;
26+
27+
// According to datasheet, the LCS needs more than 15mSec after power up. Have to delay
28+
delay(20);
29+
30+
// Initialize the LCD for 2 line move
31+
rc = SendCommand(LCD_FUNCTIONSET | LCD_FUNC_2LINE);
32+
if (rc != EBF_OK) {
33+
return rc;
34+
}
35+
36+
// Turn the display on with no cursor and no blinking by default
37+
displayControl = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
38+
rc = SendCommand(LCD_DISPLAYCONTROL | displayControl);
39+
if (rc != EBF_OK) {
40+
return rc;
41+
}
42+
43+
// Clear the display
44+
rc = Clear();
45+
if (rc != EBF_OK) {
46+
return rc;
47+
}
48+
49+
// LTR direction by default
50+
displayMode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDEC;
51+
rc = SendCommand(LCD_ENTRYMODESET | displayMode);
52+
if (rc != EBF_OK) {
53+
return rc;
54+
}
55+
56+
return EBF_OK;
57+
}
58+
59+
uint8_t EBF_AiP31068_I2C_16x2_LCD::SendCommand(uint8_t command)
60+
{
61+
uint8_t rc;
62+
pI2C->beginTransmission(i2cAddress);
63+
pI2C->write(LCD_COMMAND_PREFIX);
64+
pI2C->write(command);
65+
rc = pI2C->endTransmission();
66+
67+
if (rc != 0) {
68+
return EBF_COMMUNICATION_PROBLEM;
69+
}
70+
71+
return EBF_OK;
72+
}
73+
74+
// Called to process the instance after pollInterval
75+
// Nothing to do for output only device
76+
uint8_t EBF_AiP31068_I2C_16x2_LCD::Process()
77+
{
78+
return EBF_OK;
79+
}
80+
81+
size_t EBF_AiP31068_I2C_16x2_LCD::write(uint8_t b)
82+
{
83+
uint8_t rc;
84+
85+
pI2C->beginTransmission(i2cAddress);
86+
pI2C->write(LCD_DATA_PREFIX);
87+
pI2C->write(b);
88+
rc = pI2C->endTransmission();
89+
90+
if (rc == 0) {
91+
return 1;
92+
} else {
93+
return 0;
94+
}
95+
}
96+
97+
// Clears the display and moves to the first row/col position
98+
uint8_t EBF_AiP31068_I2C_16x2_LCD::Clear()
99+
{
100+
uint8_t rc;
101+
102+
rc = SendCommand(LCD_CLEARDISPLAY);
103+
if (rc != EBF_OK) {
104+
return rc;
105+
}
106+
107+
// TODO: delay() is bad, but looks like there is no other way to do it
108+
delayMicroseconds(1500); // According to datasheet, the HD44780 needs 1.52mSec after clear command
109+
110+
return EBF_OK;
111+
}
112+
113+
// Moves to the first row/col position without clearing the display
114+
uint8_t EBF_AiP31068_I2C_16x2_LCD::Home()
115+
{
116+
uint8_t rc;
117+
118+
rc = SendCommand(LCD_RETURNHOME);
119+
if (rc != EBF_OK) {
120+
return rc;
121+
}
122+
123+
// TODO: delay() is bad, but looks like there is no other way to do it
124+
delayMicroseconds(1500); // According to datasheet, the HD44780 needs 1.52mSec after home command
125+
126+
return EBF_OK;
127+
}
128+
129+
// Moves the cursor to specified row and column
130+
uint8_t EBF_AiP31068_I2C_16x2_LCD::SetCursor(uint8_t col, uint8_t row)
131+
{
132+
if (row == 0) {
133+
col |= 0x80;
134+
} else {
135+
col |= 0xC0;
136+
}
137+
138+
return SendCommand(col);
139+
}
140+
141+
// Turns ON the displayed text
142+
uint8_t EBF_AiP31068_I2C_16x2_LCD::DisplayOn()
143+
{
144+
displayControl |= LCD_DISPLAYON;
145+
return SendCommand(LCD_DISPLAYCONTROL | displayControl);
146+
}
147+
148+
// Turns OFF the displayed text
149+
uint8_t EBF_AiP31068_I2C_16x2_LCD::DisplayOff()
150+
{
151+
displayControl &= ~LCD_DISPLAYON;
152+
return SendCommand(LCD_DISPLAYCONTROL | displayControl);
153+
}
154+
155+
// Turns ON the underline cursor
156+
uint8_t EBF_AiP31068_I2C_16x2_LCD::CursorOn()
157+
{
158+
displayControl |= LCD_CURSORON;
159+
return SendCommand(LCD_DISPLAYCONTROL | displayControl);
160+
}
161+
162+
// Turns OFF the underline cursor
163+
uint8_t EBF_AiP31068_I2C_16x2_LCD::CursorOff()
164+
{
165+
displayControl &= ~LCD_CURSORON;
166+
return SendCommand(LCD_DISPLAYCONTROL | displayControl);
167+
}
168+
169+
// Enable the blinking cursor
170+
uint8_t EBF_AiP31068_I2C_16x2_LCD::BlinkOn()
171+
{
172+
displayControl |= LCD_BLINKON;
173+
return SendCommand(LCD_DISPLAYCONTROL | displayControl);
174+
}
175+
176+
// Disable the blinking cursor
177+
uint8_t EBF_AiP31068_I2C_16x2_LCD::BlinkOff()
178+
{
179+
displayControl &= ~LCD_BLINKON;
180+
return SendCommand(LCD_DISPLAYCONTROL | displayControl);
181+
}
182+
183+
// Scroll the display left by 1 character
184+
uint8_t EBF_AiP31068_I2C_16x2_LCD::ScrollLeft()
185+
{
186+
return SendCommand(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
187+
}
188+
189+
// Scroll the display left by specified number of characters
190+
uint8_t EBF_AiP31068_I2C_16x2_LCD::ScrollLeft(uint8_t count)
191+
{
192+
uint8_t rc;
193+
194+
for (uint8_t i=0; i<count; i++) {
195+
rc = ScrollLeft();
196+
if (rc != EBF_OK) {
197+
return rc;
198+
}
199+
200+
// Have to wait between the commands
201+
delayMicroseconds(50);
202+
}
203+
204+
return EBF_OK;
205+
}
206+
207+
// Scroll the display right by 1 character
208+
uint8_t EBF_AiP31068_I2C_16x2_LCD::ScrollRight()
209+
{
210+
return SendCommand(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
211+
}
212+
213+
// Scroll the display right by specified number of characters
214+
uint8_t EBF_AiP31068_I2C_16x2_LCD::ScrollRight(uint8_t count)
215+
{
216+
uint8_t rc;
217+
218+
for (uint8_t i=0; i<count; i++) {
219+
rc = ScrollRight();
220+
if (rc != EBF_OK) {
221+
return rc;
222+
}
223+
224+
// Have to wait between the commands
225+
delayMicroseconds(50);
226+
}
227+
228+
return EBF_OK;
229+
}
230+
231+
// Changes the direction of text flow to LTR - Left-To-Right
232+
uint8_t EBF_AiP31068_I2C_16x2_LCD::DirectionLTR()
233+
{
234+
displayMode |= LCD_ENTRYLEFT;
235+
return SendCommand(LCD_ENTRYMODESET | displayMode);
236+
}
237+
238+
// Changes the direction of text flow to RTL - Right-To-Left
239+
uint8_t EBF_AiP31068_I2C_16x2_LCD::DirectionRTL()
240+
{
241+
displayMode &= ~LCD_ENTRYLEFT;
242+
return SendCommand(LCD_ENTRYMODESET | displayMode);
243+
}
244+
245+
// Enable the autoscroll
246+
uint8_t EBF_AiP31068_I2C_16x2_LCD::AutoScrollOn()
247+
{
248+
displayMode |= LCD_ENTRYSHIFTINC;
249+
return SendCommand(LCD_ENTRYMODESET | displayMode);
250+
}
251+
252+
// Disable the autoscroll
253+
uint8_t EBF_AiP31068_I2C_16x2_LCD::AutoScrollOff()
254+
{
255+
displayMode &= ~LCD_ENTRYSHIFTINC;
256+
return SendCommand(LCD_ENTRYMODESET | displayMode);
257+
}

0 commit comments

Comments
 (0)