📄 lcd.c
字号:
/*
* I2C LCD driver.
*
* Copyright (C) 2004 Cambridge Silicon Radio Ltd.
*/
#include "lcd.h"
#include <csrtypes.h>
#include <i2c.h>
#if 0
#include <pio.h>
#else
#define PioSet(x,y)
#endif
#include <stdlib.h>
/* Address octet */
#define LCD_ADDR ((0x3c | LCD_SA) << 1)
/* Control octet */
#define LCD_CONTIN 0x80
#define LCD_LAST 0x00
#define LCD_DATA 0x40
#define LCD_COMMAND 0x00
/* H-independent commands */
#define LCD_NOP 0
#define LCD_FUNCTION_SET 0x20
#define LCD_FUNCTION_SET_MX 0x10 /* MX=1 => mirrored X addressing */
#define LCD_FUNCTION_SET_MY 0x08 /* MY=1 => display vertically mirrored */
#define LCD_FUNCTION_SET_PD 0x04 /* PD=1 => power-down mode */
/* H=[0]0 commands */
#define LCD_SET_V_LCD_RANGE 0x04 /* default: voltage base low */
#define LCD_SET_V_LCD_RANGE_PRS 0x01 /* PRS=1 => voltage base high */
#define LCD_DISPLAY_CONTROL 0x08 /* default: blank */
#define LCD_DISPLAY_CONTROL_BLANK 0x00
#define LCD_DISPLAY_CONTROL_NORMAL 0x04
#define LCD_DISPLAY_CONTROL_BLACK 0x01
#define LCD_DISPLAY_CONTROL_INVERSE 0x05
#define LCD_SET_Y 0x40
#define LCD_SET_X 0x80
/* H=[0]1 commands */
#define LCD_DISPLAY_CONFIGURATION 0x08 /* default: MSB on top, no mirroring */
#define LCD_DISPLAY_CONFIGURATION_DO 0x04 /* DO=1 => LSB on top */
#define LCD_BIAS_SYSTEM 0x10 /* very mysterious */
#define LCD_SET_V_OP 0x80 /* V_LCD = ((PRS ? 6.75 : 2.94) + V_OP * 0.03) *
(1 + (T - 27) * TC) */
/* Controller-dependent stuff */
#if defined LCD_PCF8548_LDA33S481J1D
/* Slave address select */
#define LCD_SA 0
/* H-independent commands */
#define LCD_FUNCTION_SET_V 0x02 /* V=0 => horizontal addressing */
#define LCD_FUNCTION_SET_H0 0x00 /* see below */
#define LCD_FUNCTION_SET_H1 0x01 /* see below */
/* H=0 commands */
#define LCD_SET_HVGEN_STAGES 0x10 /* default: 2 */
#define LCD_SET_HVGEN_STAGES_2 0x00
#define LCD_SET_HVGEN_STAGES_3 0x01
#define LCD_SET_HVGEN_STAGES_4 0x02
#define LCD_SET_HVGEN_STAGES_5 0x03
/* H=1 commands */
#define LCD_TEMPERATURE_CONTROL 0x04 /* default: coefficient 0 */
#define LCD_DISPLAY_CONFIGURATION_TRS 0x02 /* TRS=1 => top rows mirrored */
#define LCD_DISPLAY_CONFIGURATION_BRS 0x01 /* BRS=1 => bottom rows mirrored */
#define LCD_BIAS_SYSTEM_1_64 0x02
#define LCD_BIAS_1_7 0x04
#else
#error Unknown LCD controller/module
#endif
#define LcdTransfer(a) I2cTransfer (LCD_ADDR, (a), sizeof (a), NULL, 0)
#define LcdTransferOK(a) (LcdTransfer (a) == 1 + sizeof (a))
#define I2C_MAX_LEN 64
#if LCD_MAX_LEN > I2C_MAX_LEN
#error LCD_MAX_LEN > I2C_MAX_LEN
#endif
bool LcdClear (void)
{
static const uint8 zeroes[1 + LCD_WIDTH / 3] = { LCD_DATA | LCD_LAST /* , 0, 0, 0... */ };
uint16 u;
/* Need to zap 102 * 9 = 34 * 27 octets */
for (u = 0; u < LCD_WIDTH * (LCD_HEIGHT + 7) / 8 / (LCD_WIDTH / 3); ++u)
{
if (!LcdTransferOK (zeroes)) /* HERE optimise LCD_DATA | LCD_LAST into addr? */
{
return FALSE;
}
}
return TRUE;
}
bool LcdInit (void)
{
static const uint8 init[] = {
LCD_COMMAND | LCD_LAST,
#if defined LCD_PCF8548_LDA33S481J1D
LCD_FUNCTION_SET | LCD_FUNCTION_SET_H1 | LCD_FUNCTION_SET_PD,
LCD_TEMPERATURE_CONTROL | 2, /* as recommended by sample code */
LCD_BIAS_SYSTEM | LCD_BIAS_1_7, /* works much better than LCD_BIAS_SYSTEM_1_64 as recommended by PCF8548 datasheet -- sample code does this too */
LCD_SET_V_OP | 25, /* => 7500 mV (when PRS=1 set below, if TC=0 (or 27C!)) -- sample code uses 24 => 7470 mV for some reason */
LCD_FUNCTION_SET | LCD_FUNCTION_SET_H0 | LCD_FUNCTION_SET_MX, /* set MX otherwise X axis is all screwy -- sample code does this too */
LCD_SET_V_LCD_RANGE | LCD_SET_V_LCD_RANGE_PRS,
LCD_SET_HVGEN_STAGES | LCD_SET_HVGEN_STAGES_3, /* otherwise can't get enough juice out -- sample code does this too */
#endif
LCD_DISPLAY_CONTROL | LCD_DISPLAY_CONTROL_NORMAL
};
/* HERE need to optionally do the PIO nRES thing */
return LcdClear () && LcdTransferOK (init);
}
bool LcdOutput (const uint8 *data, uint16 len, uint16 pos, uint16 line)
{
uint8 set_xy[3];
PioSet (1 << 10, 0 << 10);
PioSet (1 << 10, 1 << 10);
/*
* Validate the input arguments.
*/
if (line >= (LCD_HEIGHT + 7) / 8 || pos >= LCD_WIDTH || len > LCD_WIDTH || pos + len > LCD_WIDTH || data == NULL)
{
return FALSE;
}
PioSet (1 << 10, 0 << 10);
PioSet (1 << 10, 1 << 10);
/*
* Move to the requested position.
*/
set_xy[0] = LCD_COMMAND | LCD_LAST;
set_xy[1] = LCD_SET_X | pos;
set_xy[2] = LCD_SET_Y | line;
if (!LcdTransferOK (set_xy))
{
return FALSE;
}
/*
* Squirt out the data, in chunks to avoid locking out background too much.
* Use a sneaky trick: pretend we're using 10-bit addressing
* so that we can insert the extra control octet (LCD_DATA | LCD_LAST)
* required after the address and before the RAM data.
*/
while (len > LCD_MAX_LEN)
{
if (I2cTransfer (LCD_ADDR << 8 | LCD_DATA | LCD_LAST, data, LCD_MAX_LEN, NULL, 0) != 2 + LCD_MAX_LEN)
{
return FALSE;
}
data += LCD_MAX_LEN;
len -= LCD_MAX_LEN;
}
return I2cTransfer (LCD_ADDR << 8 | LCD_DATA | LCD_LAST, data, len, NULL, 0) == 2 + len;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -