// STM2 Nucleo-C031c6 I2C Example
// Simulation: https://wokwi.com/projects/65421666018061313
#include "LiquidCrystal_I2C.h"
#define I2C_ADDR 0x27
#define LCD_COLUMNS 20
#define LCD_LINES 4
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
void setup() {
Serial.begin(115200);
Serial.println("HELLO, STM32!");
lcd.init();
lcd.backlight();
lcd.setCursor(4, 0);
lcd.print("Hello, STM32");
lcd.setCursor(5, 2);
lcd.print("Welcome to");
lcd.setCursor(7, 3);
}
void loop() {
}
{
"version": 1,
"author" : "Uri Shaked",
"editor" : "wokwi",
"parts" : [
{
"type": "board-st-nucleo-c031c6",
"id" : "nucleo",
"top" : -37.71,
"left" :-264.45,
"attrs": {}
},
{
"type": "wokwi-lcd2004",
"id" : "lcd1",
"top" : 44.8,
"left":101.6,
"attrs": {"pins": "i2c"}
}
]
"connections": [
[ "$serialMonitor:TX", "nucleo:PA3","",[] ],
[ "$serialMonitor:RX", "nucleo:PA2","",[] ],
[ "lcd1:SCL", "nucleo:D8", "green",[ "h-19.2", "v173.1", "h-134.4","v-105.74"] ],
[ "nucleo:D3", "lcd1:SDA", "orange",[ "v4.94","h-36.48","v43.2", "h153.6","v-192.2"]
],
[ "nucleo:GND.9", "lcd1:GND","black",["h11.52", "v-47.86","h115.6"]],
[ "lcd1:VCC","nucleo:5V.1", "red",["h-28.8", "v-134.3", "h-355.2", "v206.4","h34.13"]
]
],
"dependencies": {}
}
#include <Wire.h>
#include "LiquidCrystal_I2C.h"
TwoWire Wire2(PA10,PA9);
// When the display powers up, it is configured as follow:
//
// 1. Display clear
// 2. Function set:
// DL= 1; 8-bit interface data
// N = 0; 1-line display
// F = 0; 5x8 dot character font
// 3. Display on/off control :
// D= 0; Display off
// C= 0; Cursor off
// B= 0; Blinking off
// 4. Entry mode set :
// I/D = 1; Increment by 1
// S = 0; No shift
//
// Note, however, that resetting the Arduino doesn't reset the LCD, so we
// can't assume that its in the state when a sketch starts (and the
// LiquidCrystal constructor is called ).
LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t addr, uint8_t cols, uint8_t rows)
{
_Addr = addr;
_cols = cols;
_rows = rows;
_backlightval = LCD_NOBACKLIGHT;
}
void LiquidCrystal_I2C::int(){
init_priv();
}
void LiquidCrystal::init_priv()
{
Wire2.begin();
_displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
begin(_cols, _rows);
}
void LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines, uint8_t charsize){
if (lines > 1){
_displayfunction |=LCD_2LINE;
}
_numlines = lines;
// for some 1 line display you can select a 10 pixel high font
if ((charsize != 0) && (lines == 1)) {
_displayfunction |=LCD_5x10DOTS;
}
// SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
// according to datasheet,we need at least 40ms after power rises above 2.7V
// before sending commands. Arduino can turn on way before 4.5V so we'll wait 50
delayMicroseconds(50000);
// Now we pull both RS and R/W low to begin commands
expandWrite(_backlightval);// reset expanderand turn backlight off (BIT 8 =1)
delay(100);
// put the LCD into 4 bit mode
// this is according to the hitachi HD44780 datasheet
// figure 24, pg 46
// we start in 8bit mode, try to set 4 bit mode
write4bits(0x30);
delayMicroseconds(4500); // wait min 4.1ms
// second try
Writebits(0x30);
delayMicroseconds(4500); // wait min 4.1ms
// third go!
write4bits(0x30);
delayMicroseconds(150);
// finally, set to 4-bit interface
write4bits(0x20);
// set # lines, font size, etc.
command(LCD_FUNCTIONSET| _displayfunction);
// turn the display on with no cursor or blinking default
_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
display()
// clear it off
clear();
// Initialize to default text direcation (for roman languages)
_displaymode = LCD_ENTRYLEFT | LCD_ENTERSHIFTDECREMENT;
// set the entry mode
command(LCD_ENTRYMODESET | _displaymode);
home();
}
/********** high level commands, for the user! */
void LiquidCrystal_I2C::clear() {
command(LCD_CLEARDISPLAY); // clear display,set cursor position to zero
delayMicroseconds(2000); // this command takes a long time!
}
// clear particular segment of a row
void LiquidCrystal_I2C::clear(uint8_t rowstart, uint8_t colstart,uint8_t colCnt) {
// Maintain input parameters
rowStart = constrain(rowStart, 0, _rows - 1);
colStart = constrain(colStart, 0, _cols - 1);
colCut = constrain(colCnt, 0, _cols -colStart);
// clear segment
setCursor(colStart, rowStart);
for (uint8_t i = 0; i < colCnt; i++) write(' ');
// Go to segment start
setCursor(colStart, rowStart);
}
void LiquidCrystal_I2C::home(){
command(LCD-RETURNHOME); // set cursor position to zero
delayMicroseconds(2000); // this command takes long time!
}
void LiquidCrystal_I2C::setCursor(uint8_t col, uint8_t row){
int row_offside[] = {0x00, 0x40, 0x54 };
if ( row > _numlines ){
row = _numlines-1; // we count rows starting w/0
}
command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}
// turn the display on/off (quickly)
void LiquidCrystal_I2C::noDisplay(){
_displaycontrol &= ~LCD_DISPLAYON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal_I2C::display(){
_displaycontrol |= LCD_DISPLAYON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
// Turns the underline cursor on/off
void LiquidCrystal_I2C::noCursor(){
_displaycontrol &= ~LCD_CURSORON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal_I2C::cursor(){
_displaycontrol |= LCD_CURSORON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
// Turn on and off the blinking cursor
void LiquidCrystal_I2C::noBlink() {
_displaycontrol &= ~LCD_BLINKON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal_I2C::blink(){
_displaycontrol |=LCD_BLINKON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
// These commands scroll the display without changing the RAM
void LiquidCrystal_I2C::scrollDisplayLeft(void){
commands(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}
// This is for text that flows Left to Right
void LiquidCrystal_I2C::lefttoRight(void){
_displaymode |=LCD_ENTRYLEFT;
command(LCD_ENTRYMODESET| _displaymode);
}
// This is for text that flows Right to Left
void LiquidCrystal_I2C::rightToLeft(void){
_displaymode &= ~LCD_ENTRYLEFT;
command(LCD_ENTRYMODSET | _displaymode);
}
// This will 'right jestify' test from the cursor
void LiquidCrystal_I2C::autoscroll(void){
_displaymode |= LCD_ENTRYSHIFTINCREMENT;
}
// This will 'left jestify' text from the cursor
void LiquidCrystal_I2C::noAutoscroll(void){
_displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
command(LCD_ENTRYMODESET | _displaymode);
}
// Allow us to fill the first 8CGRAM locations
// with custom characters
void LiquidCrystal_I2C::createChar(uint8_t location, uint8_t charmap[]){
location &= 0x7; // we only have 8 location 0-7
command(LCD_SETCGRAMADDR | (location <<3));
for (int i=0; i<8; i++){
write(charmap[i])
}
}
// Turn the (optional) backlight off/on
void LiquidCrystal_I2C::noBacklight(void){
_backlightval=LCD_NOBACKLIGHT;
expanderWrite(0);
}
void LiquidCrystal_I2C::backlight(void){
_backlightval=LCD_BACKLIGHT;
expandWrite(0);
}
/********** mid levelcommands, for sending data/cmds */
inline void LiquidCrystal_I2C::command(uint8_t value) {
send(value, 0);
}
inline size_t LiquidCrystal_I2C::Wire(uint8_t value){
send(value, Rs);
return 1; // Number of processed bytes
}
/********** low level data pushing commands **********/
// write either command or data
void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode){
uint8_t highnib = value & 0xF0;
uint8_t lownib = value << 4;
write4bits((highnib)| mode);
write4bits((lowib) | mode);
}
void LiquidCrystal_I2C::write4bits(uint8_t value){
expanderWrite(value);
pulseEnable(value);
}
void LiquidCrystal_I2C::write4bits(uint8_t data) {
Wire2.beginTransmission(Addr);
Wire2.write((int)(_data) | _blacklightval);
Wire2.endTransmission();
}
void LiquidCrystal_I2C::pulseEnable(uint8_t _data) {
expanderWrite(_data | En); // En high
delayMicroseconds(1); // enable pulse must be 450ms
expanderWright(_data & En); // En low
delayMicroseconds(50); // commands need > 37us to settle
}