最近准备给恒流源加上电流显示,所以就给板子准备了一个显示接口,我买的小显示屏是80x160像素的分辨率的长条形显示屏,使用的是8Pin的FPC接口。接口的定义如下:
大致的含义如下:
LEDA | LED背光供电(也有说背光调制) |
GND | 接地 |
RESET | 重置pin,初始化时需要重置屏幕,低电平有效复位屏幕 |
RS | DCpin,0=命令寄存器 1=数据寄存器 |
SDA | 即SPI的MOSI,主机的TX引脚 |
SCL | 即SPI的SCK引脚 |
VDD | 主控芯片供电 |
CS | 即SPI的片选 |
涉及的电路图如下:
由于小型化的需要,所以,电阻电容都是0402大小,排阻是0402x2。由于背光有降压的需求(明确要求3V),所以3.3V供电需要串接一个二极管。我找的是肖特基的二极管,大约350mV的压降。
焊好板子,接上显示屏之后,就可以进行软件开发了。最开始的时候,我选择的是Adafruit_Gfx这个包,因为Adafruit这家公司在开源界足够有名,但这个库实在是一言难尽,因为这个库对ST7735的支持并不是特别好,为什么这么说呢,因为这个库对ST7735的支持类连最基本的定制显示屏的参数都做不到,只给了一些有限的显示屏的规格,而我买的这个屏恰恰是在它支持范围之外,最大的问题是颜色的格式,我猜测,Adafruit用的是SPI小端数据传输的方式,但是我这个屏幕支持的格式是大端格式,也就是说,传输到屏幕的数据是BGR格式,而不是RGB。因此需要给颜色换位置,但是Adafruit支持的几个规格没有这种分辨率和颜色格式。我不得不自己想了个办法,直接写寄存器来处理这个问题。
// 初始化屏幕
tft.initR( INITR_MINI160x80 ); // 80x160专用
delay(50);
tft.setFont();
// 设置BGR模式 + 旋转方向(示例为0°方向)
tft.setRotation(3);
uint8_t madctl = ST77XX_MADCTL_MX | ST77XX_MADCTL_MV | ST7735_MADCTL_BGR;
tft.sendCommand(ST77XX_MADCTL, &madctl, 1);
tft.fillScreen(BACK_COLOR);
delay(200);
只有这样,我才能解决颜色反转的问题。非常的烦人。你应该注意到了代码之间的delay函数。因为Adafruit是直接写屏的方式,意思就是有任何操作,直接写到芯片的寄存器内。但是,这个有个问题,就是如果上一条指令还在执行的时候,你就开始发送下一条指令,会导致ST7735出现问题,具体的表现就是屏幕死锁,再也无法更新,而且屏幕显示花屏。
操作的间隔问题非常的麻烦,因为你必须小心的处理每一条操作,避免屏幕的死锁,但是,这又加深了系统的延迟。而且Adafruit有一个致命的缺陷,就是没有双缓冲和dma数据传输,这一点让屏幕在更新字符串的时候延迟特别高,而且有明显的闪烁。
void CPicoFilamental::DrawFrame()
{
if( enable_tft )
{
const int x = 10, y = 34;
tft.setTextSize( 2 );
tft.setCursor( x, y );
tft.setTextColor( BACK_COLOR ); // 底色文字
tft.print( old_string.c_str() );
delay(10);
if( IsFailure() )
{
tft.setCursor( x, y );
tft.print( "Failure!" );
old_string = "Failure!";
}
else
{
double now = 0.0f, exp = 0.0f;
GetCurrent( now, exp );
tft.setCursor( x, y );
tft.setTextColor( FONT_COLOR ); // 白色文字
char msg[32];
sprintf( msg, "%.1f/%.0fmA", now * 1000.0f, exp * 1000.0f );
tft.print( msg );
old_string = msg;
}
}
}
这是一段设置屏幕显示的代码,我是先用上一次写的字符串,用背景色先写一次,然后延迟10毫秒,再写下一次要显示的字符串。因为ST7735依赖延迟来确保操作的稳定行,更新显示内存的时间手册上说是5ms左右,但是我的测试发现5ms完全不够,哪怕我设置为10ms,也会有小几率死屏。
所以,最后我找遍了Arduino的共享库,发现一个非常好的库:LovyanGFX,这是一个日本人写的库,这个库有我需要的一切,双缓冲,详细的配置,足够方便的操作,等等。用这个库也非常方便,只需要先写一个配置文件,他在例子中也提供了ST7735的模板:
#include <LovyanGFX.hpp>
//#define USE_BACKLIGHT
// ST7735S (80x160) の接続設定例
#define TFT_MISO -1
#define TFT_MOSI 19
#define TFT_SCLK 18
#define TFT_CS 17
#define TFT_DC 26
#define TFT_RST 27
#ifdef USE_BACKLIGHT
#define TFT_BLK 28
#endif
#define SPI_PORT 0
class LGFX : public lgfx::LGFX_Device
{
lgfx::Panel_ST7735S _panel_instance;
lgfx::Bus_SPI _bus_instance; // SPIバスのインスタンス
#ifdef USE_BACKLIGHT
lgfx::Light_PWM _light_instance;
#endif
public:
LGFX(void)
{
{ // バス制御の設定を行います。
auto cfg = _bus_instance.config(); // バス設定用の構造体を取得します。
// SPIバスの設定
cfg.spi_host = SPI_PORT; // 使用するSPIポートを選択(0 or 1)
cfg.spi_mode = 0; // SPI通信モードを設定 (0 ~ 3)
cfg.freq_write = 40000000; // 送信時のSPIクロック (最大80MHz, 80MHzを整数で割った値に丸められます)
cfg.freq_read = 20000000; // 受信時のSPIクロック
cfg.pin_sclk = TFT_SCLK; // SPIのSCLKピン番号を設定
cfg.pin_mosi = TFT_MOSI; // SPIのMOSIピン番号を設定
cfg.pin_miso = TFT_MISO; // SPIのMISOピン番号を設定 (-1 = disable)
cfg.pin_dc = TFT_DC; // SPIのD/Cピン番号を設定 (-1 = disable)
_bus_instance.config(cfg); // 設定値をバスに反映します。
_panel_instance.setBus(&_bus_instance); // バスをパネルにセットします。
}
{ // 表示パネル制御の設定を行います。
auto cfg = _panel_instance.config(); // 表示パネル設定用の構造体を取得します。
cfg.pin_cs = TFT_CS; // CSが接続されているピン番号 (-1 = disable)
cfg.pin_rst = TFT_RST; // RSTが接続されているピン番号 (-1 = disable)
cfg.pin_busy = -1; // BUSYが接続されているピン番号 (-1 = disable)
cfg.panel_width = 80; // 実際に表示可能な幅
cfg.panel_height = 160; // 実際に表示可能な高さ
cfg.offset_x = 26; // パネルのX方向オフセット量
cfg.offset_y = 1; // パネルのY方向オフセット量
// cfg.offset_rotation = 4; // 回転方向の値のオフセット 0~7 (4~7は上下反転)
cfg.invert = true; // パネルの明暗が反転してしまう場合 trueに設定
// cfg.rgb_order = true; // パネルの赤と青が入れ替わってしまう場合 trueに設定
_panel_instance.config(cfg);
}
#ifdef USE_BACKLIGHT
{ // バックライト制御の設定を行います。(必要なければ削除)
auto cfg = _light_instance.config(); // バックライト設定用の構造体を取得します。
cfg.pin_bl = TFT_BLK; // バックライトが接続されているピン番号
cfg.invert = false; // バックライトの輝度を反転させる場合 true
cfg.freq = 44100; // バックライトのPWM周波数
cfg.pwm_channel = 6; // 使用するPWMのチャンネル番号
_light_instance.config(cfg);
_panel_instance.setLight(&_light_instance); // バックライトをパネルにセットします。
}
#endif
setPanel(&_panel_instance); // 使用するパネルをセットします。
}
};
主要是调整PIN对应的GPIO值,还有使用的SPI端口、速率,另外,还有还有分辨率,颜色反转,RGB反向等等。这些做好了之后,只需要在 主循环中执行下面的代码就行了:
lcd.init();
lcd.setRotation(1);
lcd.fillScreen(0xFFFFFFU);
lcd.setCursor(10, 20);
lcd.print("print!");
是不是很简单?而且由于有双缓冲和DMA传输,整个屏幕更新终于不再闪烁。
ST7735显示屏