STM32+2.9inch微雪墨水屏(电子纸)实现显示

news2024/12/24 10:09:57

本篇文章从硬件原理以及嵌入式编程等角度完整的介绍了墨水屏驱动过程,本例涉及的墨水屏为2.9inch e-Paper V2,它采用的是“微胶囊电泳显示”技术进行图像显示,其基本原理是悬浮在液体中的带电纳米粒子受到电场作用而产生迁移,从而改变显示屏各像素点的颜色。

墨水屏的原理决定了它具有很好的稳定性,如果电场不改变,粒子就不再运动,也不需要耗电,即使断电,墨水屏的画面也能保留,这个特性使其能作为电子铭牌或价签等重复使用。电子纸显示屏是靠反射环境光来显示图案的,不需要背光,在环境光下,电子纸显示屏清晰可视,可视角度几乎达到了 180°。再加上低功耗的特性,电子纸非常适合作为Kindle等电子书阅读器的显示屏。

目录

概念说明

实现原理

嵌入式程序 

底层硬件接口

中间层墨水屏驱动

上层接口

墨水屏EPD_2IN9_V2驱动Demo


概念说明

我们下面从墨水瓶的产品特点,SPI通信时序,以及像素与字节的关系和注意事项这四个角度来进行墨水瓶驱动前的必要概念说明。

  • 产品特点:工作电压为3.3V/5V;通信接口为SPI;分辨率为296*128;显示颜色为黑、白;灰度等级为4级;刷新功耗为26.4mW(typ.);休眠电流<0.01uA(接近0);局部刷新时间为0.6秒;全局刷新时间为3秒。
  • SPI时序:采用SPImode0,即CPHA=0,时钟线第一个边沿采样;CPOL=0,时钟线空闲时电压为0;

  • 像素与字节的关系:对于黑白图片,黑色我们定义成0,白色就定义成1,一个点在图形上一般称之为像素点(pixel),而颜色不是1就是0,也就是1个位就可以标识颜色:1Pixel = 1bit,那么一个字节里面就包含了8个像素点。以16个像素点为例,我们假设前8个像素点为黑,后8个像素点为白色,那么可以这么认为,像素点1-16,对应这0位到15位,用数据可以表示为0x00ff(数据存储若涉及字节序的问题,stm32为小端存储)。
  • 注意事项:不能一直用局刷对屏幕进行刷新,需要在做几次局刷之后,对屏幕进行一次全刷清屏。否则会造成屏幕显示效果异常;屏幕不能长时间上电,在屏幕不刷新的时候,要将屏幕设置成睡眠模式,或者进行断电处理。否则屏幕长时间保持高电压状态,会损坏膜片,无法修复;使用墨水屏的时候,建议刷新时间间隔至少是180s, 并且至少每24小时做一次刷新,如果长期不使用墨水屏的话,要将墨水屏刷白存放;屏幕的 FPC 排线比较脆弱,请注意:不要沿屏幕垂直方向弯曲排线,避免排线被撕裂;不要反复过度弯曲排线,避免排线断裂;不要往屏幕正面方向弯曲排线,避免排线与面板的连接断开。调试研发时建议固定排线后使用;墨水屏屏幕较为脆弱,注意尽量避免跌落、碰撞、用力按压;

实现原理

墨水屏参考电路如下图,SPI通信以及其他控制引脚与MCU相连,实现驱动。

涉及的引脚定义如下表。

Pin脚序号标号名称功能描述
2GDRN-Channel MOSFET Gate Drive Control
3RESECurrent Sense Input for the Control Loop
8BS1Bus selection pin
9BUSYBusy state output pin
10RES #Reset
11D/C #Data /Command control pin
12CS #Chip Select input pin
13SCLserial clock pin (SPI)
14SDAserial data pin (SPI)

我们常常需要使用墨水屏来显示既有图片,我们首先讲图片根据我们屏幕的像素调整大小,我们的为296*128,然后我们使用Image2Lc对图片进行取模,生成图片数组,在生成的之前我们需要根据屏幕分辨率配置最大宽度和高度,根据微雪示例代码配置扫描模式为垂直扫描,并配置字节序等信息,Image2Lc软件截图如下。


嵌入式程序 

下面介绍一下微雪提供的例程,了解驱动墨水屏的嵌入式程序逻辑与结构。


底层硬件接口

我们进行了底层的封装,如果需要了解内部实现可以去对应的目录中查看。在DEV_Config.c(.h)可以看到很多定义,在目录:Pico_ePaper_Code\c\lib\Config

  • 数据类型定义如下:
    #define UBYTE   uint8_t
    #define UWORD   uint16_t
    #define UDOUBLE uint32_t
  •  模块初始化与退出处理:
    void DEV_Module_Init(void);
    void DEV_Module_Exit(void);
    //注意:
    //1.这里是处理使用墨水屏前与使用完之后一些GPIO的处理。
  •  GPIO读写:
    void DEV_Digital_Write(UWORD Pin, UBYTE Value);
    UBYTE DEV_Digital_Read(UWORD Pin);
  •  SPI写数据:
    void DEV_SPI_WriteByte(UBYTE Value);

中间层墨水屏驱动

e-paper驱动代码文件,在目录:Pico_ePaper_Code\c\lib\e-Paper。

  • 墨水屏初始化,再屏幕开始工作时和退出睡眠模式之后调用。其中xxx表示,墨水屏型号。如是是2.13D,全屏初始化那么是EPD_2IN13D_Init(0),局部刷新初始化EPD_2IN13D_Init(1)。
    //2.13inch e-Paper、2.13inch e-Paper  V2、2.13inch e-Paper (D)、2.9inch e-Paper、2.9inch e-Paper (D)
    void EPD_xxx_Init(UBYTE Mode); // Mode = 0 全局刷新初始化、Mode = 1 局部刷新初始化
    //其他型号
    void EPD_xxx_Init(void);
  • 清屏,把墨水屏刷成白色。其中xxx表示,墨水屏型号。如是是2.13D,那么是EPD_2IN9D_Clear()。
    void EPD_xxx_Clear(void);
  • 全局刷新,传入的图像为利用上层接口画图预存的图像。
    //黑白双色墨水屏
    void EPD_xxx_Display(UBYTE *Image);
    //黑白红或黑白黄墨水屏
    void EPD_xxx_Display(const UBYTE *blackimage, const UBYTE *ryimage);
  • 进入睡眠模式。注意进入了睡眠模式,只有两个方式能够重新工作:第一种硬件复位,第二种重新调用初始化函数,其中xxx表示,墨水屏型号。
    void EPD_xxx_Sleep(void);

上层接口

对于屏幕而言,如果需要进行画图、显示中英文字符、显示图片等怎么办,这些都是上层应用做的。这有很多小伙伴有问到一些图形的处理,我们这里提供了一些基本的功能 在如下的目录中可以找到GUI,在目录:RaspberryPi_JetsonNano\c\lib\GUI\GUI_Paint.c(.h)。

  • 新建图像属性:新建一个图像属性,这个属性包括图像缓存的名称、宽度、高度、翻转角度、颜色。
    void Paint_NewImage(UBYTE *image, UWORD Width, UWORD Height, UWORD Rotate, UWORD Color)
    参数:
     	image : 图像缓存的名称,实际上是一个指向图像缓存首地址的指针;
     	Width : 图像缓存的宽度;
     	Height: 图像缓存的高度;
     	Rotate:图像的翻转的角度
     	Color :图像的初始颜色;
  • 选择图像缓存:选择图像缓存,选择的目的是你可以创建多个图像属性,图像缓存可以存在多个,你可以选择你所创建的每一张图像。
    void Paint_SelectImage(UBYTE *image)
    参数:
     	image: 图像缓存的名称,实际上是一个指向图像缓存首地址的指针;
  • 图像旋转:设置选择好的图像的旋转角度,最好使用在Paint_SelectImage()后,可以选择旋转0、90、180、270。不同选择角度下,坐标对应起始像素点不同,这里以1.54B为例,四张图,按顺序为0°, 90°, 180°, 270°。
    void Paint_SetRotate(UWORD Rotate)
    参数:
     	Rotate: 图像选择角度,可以选择ROTATE_0、ROTATE_90、ROTATE_180、ROTATE_270分别对应0、90、180、270度
  • 图像镜像翻转:设置选择好的图像的镜像翻转,可以选择不镜像、关于水平镜像、关于垂直镜像、关于图像中心镜像。
    void Paint_SetMirroring(UBYTE mirror)
    参数:
     	mirror: 图像的镜像方式,可以选择MIRROR_NONE、MIRROR_HORIZONTAL、MIRROR_VERTICAL、MIRROR_ORIGIN分别对应不镜像、关于水平镜像、关于垂直镜像、关于图像中心镜像
  • 设置点在缓存中显示位置和颜色:这里是GUI最核心的一个函数、处理点在缓存中显示位置和颜色。
    void Paint_SetPixel(UWORD Xpoint, UWORD Ypoint, UWORD Color)
    参数:
     	Xpoint: 点在图像缓存中X位置
     	Ypoint: 点在图像缓存中Y位置
     	Color : 点显示的颜色
  • 图像缓存填充颜色:把图像缓存填充为某颜色,一般作为屏幕刷白的作用。
    void Paint_Clear(UWORD Color)
    参数:
     	Color: 填充的颜色
  • 图像缓存部分窗口填充颜色:把图像缓存的某部分窗口填充为某颜色,一般作为窗口刷白的作用,常用于时间的显示,刷白上一秒。
    void Paint_ClearWindows(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color)
    参数:
     	Xstart: 窗口的X起点坐标
     	Ystart: 窗口的Y起点坐标
     	Xend: 窗口的X终点坐标
     	Yend: 窗口的Y终点坐标
     	Color: 填充的颜色
  • 画点:在图像缓存中,在(Xpoint, Ypoint)上画点,可以选择颜色,点的大小,点的风格。
    void Paint_DrawPoint(UWORD Xpoint, UWORD Ypoint, UWORD Color, DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_Style)
    参数:
     	Xpoint: 点的X坐标
     	Ypoint: 点的Y坐标
     	Color: 填充的颜色
     	Dot_Pixel: 点的大小,提供默认的8种大小点
     	 	 typedef enum {
     	 	 	 DOT_PIXEL_1X1  = 1,	// 1 x 1
     	 	 	 DOT_PIXEL_2X2  , 		// 2 X 2
     	 	 	 DOT_PIXEL_3X3  , 	 	// 3 X 3
     	 	 	 DOT_PIXEL_4X4  , 	 	// 4 X 4
     	 	 	 DOT_PIXEL_5X5  , 		// 5 X 5
     	 	 	 DOT_PIXEL_6X6  , 		// 6 X 6
     	 	 	 DOT_PIXEL_7X7  , 		// 7 X 7
     	 	 	 DOT_PIXEL_8X8  , 		// 8 X 8
     	 	} DOT_PIXEL;
     	Dot_Style: 点的风格,大小扩充方式是以点为中心扩大还是以点为左下角往右上扩大
     	 	typedef enum {
     	 	   DOT_FILL_AROUND  = 1,		
     	 	   DOT_FILL_RIGHTUP,
     	 	} DOT_STYLE;
  • 画线:在图像缓存中,从 (Xstart, Ystart) 到 (Xend, Yend) 画线,可以选择颜色,线的宽度,线的风格。
    void Paint_DrawLine(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color, LINE_STYLE Line_Style , LINE_STYLE Line_Style)
    参数:
     	Xstart: 线的X起点坐标
     	Ystart: 线的Y起点坐标
     	Xend: 线的X终点坐标
     	Yend: 线的Y终点坐标
     	Color: 填充的颜色
     	Line_width: 线的宽度,提供默认的8种宽度
     	 	typedef enum {
     	 	 	 DOT_PIXEL_1X1  = 1,	// 1 x 1
     	 	 	 DOT_PIXEL_2X2  , 		// 2 X 2
     	 	 	 DOT_PIXEL_3X3  ,		// 3 X 3
     	 	 	 DOT_PIXEL_4X4  ,		// 4 X 4
     	 	 	 DOT_PIXEL_5X5  , 		// 5 X 5
     	 	 	 DOT_PIXEL_6X6  , 		// 6 X 6
     	 	 	 DOT_PIXEL_7X7  , 		// 7 X 7
     	 	 	 DOT_PIXEL_8X8  , 		// 8 X 8
     	 	} DOT_PIXEL;
     	 Line_Style: 线的风格,选择线是以直线连接还是以虚线的方式连接
     	 	typedef enum {
     	 	 	 LINE_STYLE_SOLID = 0,
     	 	 	 LINE_STYLE_DOTTED,
     	 	} LINE_STYLE;
  • 画矩形:在图像缓存中,从 (Xstart, Ystart) 到 (Xend, Yend) 画一个矩形,可以选择颜色,线的宽度,是否填充矩形内部。
    void Paint_DrawRectangle(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
    参数:
     	Xstart: 矩形的X起点坐标
     	Ystart: 矩形的Y起点坐标
     	Xend: 矩形的X终点坐标
     	Yend: 矩形的Y终点坐标
     	Color: 填充的颜色
     	Line_width: 矩形四边的宽度,提供默认的8种宽度
     	 	typedef enum {
     	 	 	 DOT_PIXEL_1X1  = 1,	// 1 x 1
     	 	 	 DOT_PIXEL_2X2  , 		// 2 X 2
     	 	 	 DOT_PIXEL_3X3  ,		// 3 X 3
     	 	 	 DOT_PIXEL_4X4  ,		// 4 X 4
     	 	 	 DOT_PIXEL_5X5  , 		// 5 X 5
     	 	 	 DOT_PIXEL_6X6  , 		// 6 X 6
     	 	 	 DOT_PIXEL_7X7  , 		// 7 X 7
     	 	 	 DOT_PIXEL_8X8  , 		// 8 X 8
     	 	} DOT_PIXEL;
     	Draw_Fill: 填充,是否填充矩形的内部
     	 	typedef enum {
     	 	 	 DRAW_FILL_EMPTY = 0,
     	 	 	 DRAW_FILL_FULL,
     	 	} DRAW_FILL;
  • 画圆:在图像缓存中,以 (X_Center Y_Center) 为圆心,画一个半径为Radius的圆,可以选择颜色,线的宽度,是否填充圆内部。
    void Paint_DrawCircle(UWORD X_Center, UWORD Y_Center, UWORD Radius, UWORD Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
    参数:
     	X_Center: 圆心的X坐标
     	Y_Center: 圆心的Y坐标
     	Radius:圆的半径
     	Color: 填充的颜色
     	Line_width: 圆弧的宽度,提供默认的8种宽度
     	 	typedef enum {
     	 	 	 DOT_PIXEL_1X1  = 1,	// 1 x 1
     	 	 	 DOT_PIXEL_2X2  , 		// 2 X 2
     	 	 	 DOT_PIXEL_3X3  ,		// 3 X 3
     	 	 	 DOT_PIXEL_4X4  ,		// 4 X 4
     	 	 	 DOT_PIXEL_5X5  , 		// 5 X 5
     	 	 	 DOT_PIXEL_6X6  , 		// 6 X 6
     	 	 	 DOT_PIXEL_7X7  , 		// 7 X 7
     	 	 	 DOT_PIXEL_8X8  , 		// 8 X 8
     	 	} DOT_PIXEL;
     	Draw_Fill: 填充,是否填充圆的内部
     	 	typedef enum {
     	 	 	 DRAW_FILL_EMPTY = 0,
     	 	 	 DRAW_FILL_FULL,
     	 	} DRAW_FILL;
  • 写Ascii字符:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一个Ascii字符,可以选择Ascii码可视字符字库、字体前景色、字体背景色。
    void Paint_DrawChar(UWORD Xstart, UWORD Ystart, const char Ascii_Char, sFONT* Font, UWORD Color_Foreground, UWORD Color_Background)
    参数:
     	Xstart: 字符的左顶点X坐标
     	Ystart: 字体的左顶点Y坐标
     	Ascii_Char:Ascii字符
     	Font: Ascii码可视字符字库,在Fonts文件夹中提供了以下字体:
     	 	font8:5*8的字体
     	 	font12:7*12的字体
     	 	font16:11*16的字体
     	 	font20:14*20的字体
     	 	font24:17*24的字体
     	Color_Foreground: 字体颜色
     	Color_Background: 背景颜色
  • 写英文字符串:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一串英文字符,可以选择Ascii码可视字符字库、字体前景色、字体背景色。
    void Paint_DrawString_EN(UWORD Xstart, UWORD Ystart, const char * pString, sFONT* Font, UWORD Color_Foreground, UWORD Color_Background)
    参数:
     	Xstart: 字符的左顶点X坐标
     	Ystart: 字体的左顶点Y坐标
     	pString:字符串,字符串是一个指针
     	Font: Ascii码可视字符字库,在Fonts文件夹中提供了以下字体:
     	 	font8:5*8的字体
     	 	font12:7*12的字体
     	 	font16:11*16的字体
     	 	font20:14*20的字体
     	 	font24:17*24的字体
     	Color_Foreground: 字体颜色
     	Color_Background: 背景颜色
  • 写中文字符串:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一串中文字符,可以选择GB2312编码字符字库、字体前景色、字体背景色。
    void Paint_DrawString_CN(UWORD Xstart, UWORD Ystart, const char * pString, cFONT* font, UWORD Color_Foreground, UWORD Color_Background)
    参数:
     	Xstart: 字符的左顶点X坐标
     	Ystart: 字体的左顶点Y坐标
     	pString:字符串,字符串是一个指针
     	Font: GB2312编码字符字库,在Fonts文件夹中提供了以下字体:
     	 	font12CN:ascii字符字体11*21,中文字体16*21
     	 	font24CN:ascii字符字体24*41,中文字体32*41
     	Color_Foreground: 字体颜色
     	Color_Background: 背景颜色
  • 写数字:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一串数字,可以选择Ascii码可视字符字库、字体前景色、字体背景色。
    void Paint_DrawNum(UWORD Xpoint, UWORD Ypoint, int32_t Nummber, sFONT* Font, UWORD Color_Foreground, UWORD Color_Background)
    参数:
     	Xstart: 字符的左顶点X坐标
     	Ystart: 字体的左顶点Y坐标
     	Nummber:显示的数字,这里使用的是32位长的int型保存,可以最大显示到2147483647
     	Font: Ascii码可视字符字库,在Fonts文件夹中提供了以下字体:
     	 	font8:5*8的字体
     	 	font12:7*12的字体
     	 	font16:11*16的字体
     	 	font20:14*20的字体
     	 	font24:17*24的字体
     	Color_Foreground: 字体颜色
     	Color_Background: 背景颜色
  • 显示时间:在图像缓存中,在 (Xstart Ystart) 为左顶点,显示一段时间,可以选择Ascii码可视字符字库、字体前景色、字体背景色;这里是方便测试局部刷新而写的,因为局部刷新需要的时间为0.3S,整体显示少于1S加上数据的传输,可以做到1S刷新一次。
    void Paint_DrawTime(UWORD Xstart, UWORD Ystart, PAINT_TIME *pTime, sFONT* Font, UWORD Color_Background, UWORD Color_Foreground)
    参数:
     	Xstart: 字符的左顶点X坐标
     	Ystart: 字体的左顶点Y坐标
     	pTime:显示的时间,这里定义好了一个时间的结构体,只要把时分秒各位数传给参数;
     	Font: Ascii码可视字符字库,在Fonts文件夹中提供了以下字体:
     	 	font8:5*8的字体
     	 	font12:7*12的字体
     	 	font16:11*16的字体
     	 	font20:14*20的字体
     	 	font24:17*24的字体
     	Color_Foreground: 字体颜色
     	Color_Background: 背景颜色

墨水屏EPD_2IN9_V2驱动Demo

下面是使用墨水瓶驱动完成显示预存的图片数组,在图像上画图并显示以及局部刷新(显示当前时间)功能,最后展示了四级的灰度,下面是完成的操作流程。

/*****************************************************************************
* | File      	:   EPD_2IN9_V2_test.c
* | Author      :   Waveshare team
* | Function    :   2.9inch e-paper V2 test demo
* | Info        :
*----------------
* |	This version:   V1.0
* | Date        :   2020-10-20
* | Info        :
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to  whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
******************************************************************************/
#include "EPD_Test.h"
#include "EPD_2in9_V2.h"

int EPD_2in9_V2_test(void)
{
    printf("EPD_2IN9_V2_test Demo\r\n");
    if(DEV_Module_Init()!=0){
        return -1;
    }

    printf("e-Paper Init and Clear...\r\n");
	EPD_2IN9_V2_Init();
    EPD_2IN9_V2_Clear();

    //Create a new image cache
    UBYTE *BlackImage;
    // Additional `*2` on the end to account for four-colour images
    UWORD Imagesize = ((EPD_2IN9_V2_WIDTH % 8 == 0)? (EPD_2IN9_V2_WIDTH / 8 ): (EPD_2IN9_V2_WIDTH / 8 + 1)) * EPD_2IN9_V2_HEIGHT * 2;
    if((BlackImage = (UBYTE *)malloc(Imagesize)) == NULL) {
        printf("Failed to apply for black memory...\r\n");
        return -1;
    }
    printf("Paint_NewImage\r\n");
    Paint_NewImage(BlackImage, EPD_2IN9_V2_WIDTH, EPD_2IN9_V2_HEIGHT, 90, WHITE);
    Paint_SetScale(2); // b&w
	Paint_Clear(WHITE);

#if 1  //show image for array  
    Paint_NewImage(BlackImage, EPD_2IN9_V2_WIDTH, EPD_2IN9_V2_HEIGHT, 90, WHITE);
    Paint_SetScale(2); // b&w
    printf("show image for array\r\n");
    Paint_SelectImage(BlackImage);
    Paint_Clear(WHITE);
    Paint_DrawBitMap(gImage_2in9);

    EPD_2IN9_V2_Display(BlackImage);
    DEV_Delay_ms(3000);
#endif

#if 1  // Drawing on the image
	Paint_NewImage(BlackImage, EPD_2IN9_V2_WIDTH, EPD_2IN9_V2_HEIGHT, 90, WHITE);
    Paint_SetScale(2); // b&w
    printf("Drawing\r\n");
    //1.Select Image
    Paint_SelectImage(BlackImage);
    Paint_Clear(WHITE);
	
    // 2.Drawing on the image
    printf("Drawing:BlackImage\r\n");
    Paint_DrawPoint(10, 80, BLACK, DOT_PIXEL_1X1, DOT_STYLE_DFT);
    Paint_DrawPoint(10, 90, BLACK, DOT_PIXEL_2X2, DOT_STYLE_DFT);
    Paint_DrawPoint(10, 100, BLACK, DOT_PIXEL_3X3, DOT_STYLE_DFT);

    Paint_DrawLine(20, 70, 70, 120, BLACK, DOT_PIXEL_1X1, LINE_STYLE_SOLID);
    Paint_DrawLine(70, 70, 20, 120, BLACK, DOT_PIXEL_1X1, LINE_STYLE_SOLID);

    Paint_DrawRectangle(20, 70, 70, 120, BLACK, DOT_PIXEL_1X1, DRAW_FILL_EMPTY);
    Paint_DrawRectangle(80, 70, 130, 120, BLACK, DOT_PIXEL_1X1, DRAW_FILL_FULL);

    Paint_DrawCircle(45, 95, 20, BLACK, DOT_PIXEL_1X1, DRAW_FILL_EMPTY);
    Paint_DrawCircle(105, 95, 20, WHITE, DOT_PIXEL_1X1, DRAW_FILL_FULL);

    Paint_DrawLine(85, 95, 125, 95, BLACK, DOT_PIXEL_1X1, LINE_STYLE_DOTTED);
    Paint_DrawLine(105, 75, 105, 115, BLACK, DOT_PIXEL_1X1, LINE_STYLE_DOTTED);

    Paint_DrawString_EN(10, 0, "waveshare", &Font16, BLACK, WHITE);
    Paint_DrawString_EN(10, 20, "hello world", &Font12, WHITE, BLACK);

    Paint_DrawNum(10, 33, 123456789, &Font12, BLACK, WHITE);
    Paint_DrawNum(10, 50, 987654321, &Font16, WHITE, BLACK);

    Paint_DrawString_CN(130, 0,"���abc", &Font12CN, BLACK, WHITE);
    Paint_DrawString_CN(130, 20, "΢ѩ����", &Font24CN, WHITE, BLACK);

    EPD_2IN9_V2_Display_Base(BlackImage);
    DEV_Delay_ms(3000);
#endif

#if 1   //Partial refresh, example shows time    		
	Paint_NewImage(BlackImage, EPD_2IN9_V2_WIDTH, EPD_2IN9_V2_HEIGHT, 90, WHITE);  
    printf("Partial refresh\r\n");
    Paint_SelectImage(BlackImage);
	
    PAINT_TIME sPaint_time;
    sPaint_time.Hour = 12;
    sPaint_time.Min = 34;
    sPaint_time.Sec = 56;
    UBYTE num = 10;
    for (;;) {
        sPaint_time.Sec = sPaint_time.Sec + 1;
        if (sPaint_time.Sec == 60) {
            sPaint_time.Min = sPaint_time.Min + 1;
            sPaint_time.Sec = 0;
            if (sPaint_time.Min == 60) {
                sPaint_time.Hour =  sPaint_time.Hour + 1;
                sPaint_time.Min = 0;
                if (sPaint_time.Hour == 24) {
                    sPaint_time.Hour = 0;
                    sPaint_time.Min = 0;
                    sPaint_time.Sec = 0;
                }
            }
        }
        Paint_ClearWindows(150, 80, 150 + Font20.Width * 7, 80 + Font20.Height, WHITE);
        Paint_DrawTime(150, 80, &sPaint_time, &Font20, WHITE, BLACK);

        num = num - 1;
        if(num == 0) {
            break;
        }
		EPD_2IN9_V2_Display_Partial(BlackImage);
        DEV_Delay_ms(500);//Analog clock 1s
    }
#endif

#if 1 //show 4colour image for array
    free(BlackImage);
    printf("show Gray------------------------\r\n");
    Imagesize = ((EPD_2IN9_V2_WIDTH % 4 == 0)? (EPD_2IN9_V2_WIDTH / 4 ): (EPD_2IN9_V2_WIDTH / 4 + 1)) * EPD_2IN9_V2_HEIGHT;
    if((BlackImage = (UBYTE *)malloc(Imagesize)) == NULL) {
        printf("Failed to apply for black memory...\r\n");
        return -1;
    }
    EPD_2IN9_V2_Gray4_Init();
    printf("4 grayscale display\r\n");
    Paint_NewImage(BlackImage, EPD_2IN9_V2_WIDTH, EPD_2IN9_V2_HEIGHT, 90, WHITE);
    Paint_SetScale(4);
    Paint_Clear(0xff);
    
    Paint_DrawPoint(10, 80, GRAY4, DOT_PIXEL_1X1, DOT_STYLE_DFT);
    Paint_DrawPoint(10, 90, GRAY4, DOT_PIXEL_2X2, DOT_STYLE_DFT);
    Paint_DrawPoint(10, 100, GRAY4, DOT_PIXEL_3X3, DOT_STYLE_DFT);
    Paint_DrawLine(20, 70, 70, 120, GRAY4, DOT_PIXEL_1X1, LINE_STYLE_SOLID);
    Paint_DrawLine(70, 70, 20, 120, GRAY4, DOT_PIXEL_1X1, LINE_STYLE_SOLID);
    Paint_DrawRectangle(20, 70, 70, 120, GRAY4, DOT_PIXEL_1X1, DRAW_FILL_EMPTY);
    Paint_DrawRectangle(80, 70, 130, 120, GRAY4, DOT_PIXEL_1X1, DRAW_FILL_FULL);
    Paint_DrawCircle(45, 95, 20, GRAY4, DOT_PIXEL_1X1, DRAW_FILL_EMPTY);
    Paint_DrawCircle(105, 95, 20, GRAY2, DOT_PIXEL_1X1, DRAW_FILL_FULL);
    Paint_DrawLine(85, 95, 125, 95, GRAY4, DOT_PIXEL_1X1, LINE_STYLE_DOTTED);
    Paint_DrawLine(105, 75, 105, 115, GRAY4, DOT_PIXEL_1X1, LINE_STYLE_DOTTED);
    Paint_DrawString_EN(10, 0, "waveshare", &Font16, GRAY4, GRAY1);
    Paint_DrawString_EN(10, 20, "hello world", &Font12, GRAY3, GRAY1);
    Paint_DrawNum(10, 33, 123456789, &Font12, GRAY4, GRAY2);
    Paint_DrawNum(10, 50, 987654321, &Font16, GRAY1, GRAY4);
    Paint_DrawString_CN(150, 0,"���abc", &Font12CN, GRAY4, GRAY1);
    Paint_DrawString_CN(150, 20,"���abc", &Font12CN, GRAY3, GRAY2);
    Paint_DrawString_CN(150, 40,"���abc", &Font12CN, GRAY2, GRAY3);
    Paint_DrawString_CN(150, 60,"���abc", &Font12CN, GRAY1, GRAY4);
    Paint_DrawString_CN(150, 80, "΢ѩ����", &Font24CN, GRAY1, GRAY4);
    EPD_2IN9_V2_4GrayDisplay(BlackImage);
    DEV_Delay_ms(3000);

    Paint_NewImage(BlackImage, EPD_2IN9_V2_WIDTH, EPD_2IN9_V2_HEIGHT, 0, WHITE);
    Paint_SetScale(4);
    Paint_Clear(WHITE);
    Paint_DrawBitMap(gImage_2in9_4gray);
    EPD_2IN9_V2_4GrayDisplay(BlackImage);
    DEV_Delay_ms(3000);

#endif

	printf("Clear...\r\n");
	EPD_2IN9_V2_Init();
    EPD_2IN9_V2_Clear();
	
    printf("Goto Sleep...\r\n");
    EPD_2IN9_V2_Sleep();
    free(BlackImage);
    BlackImage = NULL;
    DEV_Delay_ms(2000);//important, at least 2s
    // close 5V
    printf("close 5V, Module enters 0 power consumption ...\r\n");
    DEV_Module_Exit();
    return 0;
}

十六宿舍 原创作品,转载必须标注原文链接。

©2023 Yang Li. All rights reserved.

欢迎关注 『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1119062.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++初阶】小白入门C++

目录 前言&#xff1a;1、C关键字2、命名空间2.1命名空间是什么2.2为什么要有命名空间2.3命名空间怎么使用2.3.1命名空间的写法2.3.2命名空间是可以嵌套的2.3.3使用命名空间的三种方式 3、C输入和输出3.1初识cout和cin3.2C的输入输出可以自动识别变量类型 4、缺省参数4.1缺省参…

Linux:用户和权限

Linux&#xff1a;用户和权限 1. 认知root用户1.1 root用户&#xff08;超级管理员&#xff09;1.2 su和exit命令1.3 sudo命令1.3.1 为普通用户配置sudo认证 2. 用户、用户组管理2.1 用户组管理2.2 用户管理2.3 getent命令 3. 查看权限控制3.1 认知权限信息3.1.1 案例 4. 修改权…

react 实战- 玩转 react 全家桶(进阶)学习

一个命令是怎么运行起来的? Shell运行一个命令,实际上是运行一个脚本 环境变量 装了node以后,node的路径,就被注册到了环境变量里. 一个js的东西,可以注册? bin Webpack配置 构建 import A from A , const Arequire(A) 为什么可以这么写?为哈都行?本质上,是构建工…

人人自媒体的时候,Ai绘画还值得踏入吗?

前言 先说结论&#xff0c;如果你不打算涉足自媒体&#xff0c;平时也从不上网发什么内容去展示自己的话&#xff0c;其实AI绘画对你来说意义不大。但如果你对自媒体感兴趣&#xff0c;会涉及发作品&#xff0c;发内容&#xff0c;甚至去设计图片&#xff0c;那么AI绘画值得你…

【【萌新的SOC学习之自定义IP核的学习与设计】】

萌新的SOC学习之自定义IP核的学习与设计 本章为了更加深入的了解FPGA的自定义IP和IP封装测试等问题 参考了正点原子 第六讲自定义IP核呼吸灯实验 和 第十九章 IP封装与接口定义实验 为了更好的理解自定义IP核 我们先介绍一个带AXI主从接口的IP核 我们可以展开AXI从接口 下…

Vue--》简易资金管理系统后台项目实战(后端)

今天开始使用 node vue3 ts搭建一个简易资金管理系统的前后端分离项目&#xff0c;因为前后端分离所以会分两个专栏分别讲解前端与后端的实现&#xff0c;后端项目文章讲解可参考&#xff1a;前端链接&#xff0c;我会在前后端的两类专栏的最后一篇文章中会将项目代码开源到我…

转行做程序员,多晚都不晚

大家好啊&#xff0c;我是董董灿。 最近有不少小伙伴加我微信咨询一些问题&#xff0c;有同学想了解AI行业的现状&#xff0c;想着转行的&#xff0c;也有在校生想了解毕业后工作方向的&#xff0c;当然也有想学习编程知识的。 诚惶诚恐&#xff0c;没想到之前写的文章&#…

【CHI】CHI协议,transaction事务汇总

前言 CHI协议最难的是什么&#xff0c;就是那一堆各种各样的事务&#xff0c;你不知道什么场景应该使用什么合适的事务&#xff0c;收到X事务又该回复什么事务。相当于CHI给你制定了很多种&#xff08;尽可能覆盖完全&#xff09;场景及事务&#xff0c;你需要去了解&#xff0…

英语——分享篇——每日200词——2401-2600

2401——moisture——[mɔɪstʃə(r)]——n.潮气&#xff0c;湿气&#xff0c;水分——moisture——moist潮湿的(熟词)ur你的(编码your)e鹅(编码)——潮湿的地方你的鹅一身潮气——Moisture in the atmosphere condensed into dew during the night.——大气中的水分在夜间凝结…

二阶系统时域响应

二阶系统微分方程 二阶系统传递函数 二阶系统单位阶跃响应 过阻尼系统 临界阻尼系统 欠阻尼系统 无阻尼系统 二阶系统阶跃响应仿真 在Matlab中进行仿真&#xff0c;设置不同阻尼比2、1、0.5和0&#xff0c;可以得到结论&#xff1a; 阻尼比越小&#xff0c;系统响应速度越快&…

YOLOV8改进:RefConv(即插即用!重参数化重聚焦卷积替代常规卷积,无额外推理成本下涨点明显!)

1.该文章属于YOLOV5/YOLOV7/YOLOV8改进专栏,包含大量的改进方式,主要以2023年的最新文章和2022年的文章提出改进方式。 2.提供更加详细的改进方法,如将注意力机制添加到网络的不同位置,便于做实验,也可以当做论文的创新点。 3.涨点效果:RefConv,实现有效涨点! 论文地址…

【项目设计】网络对战五子棋(上)

想回家过年… 文章目录 一、项目前置知识1. websocketpp库1.1 http1.0/1.1和websocket协议1.2 websocketpp库接口的前置认识1.3 搭建一个http/websocket服务器 2. jsoncpp库3. mysqlclient库 二、 项目设计1. 项目模块划分2. 实用工具类模块2.1 日志宏封装2.2 mysql_util2.3 j…

蓝桥杯每日一题2023.10.21

后缀表达式 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 30分解法&#xff1a;要求出最大的结果就需要加的数越大&#xff0c;减的数越小&#xff0c;以此为思路简单列举即可 #include<bits/stdc.h> using namespace std; typedef long long ll; const int N 2e5 10…

物联网知识复习

物联网的内涵和体系结构 物联网的基本内涵 物联网的基本内涵在于物联&#xff0c;物物相连或者物和人相连的互联网。 也就是说&#xff0c;它是要由物主动发起的&#xff0c;物物互联的互联网。 它的第一层意思是说物和物相连&#xff1b;第二层意思是说物和人相连。 物联网的…

Gradient conjugate priors and multi-layer neural networks

动机 先验参数 m , α , β , v m,\alpha,\beta,v m,α,β,v和随机变量 τ \tau τ KL散度的形式是&#xff1a; Dynamics of m , α , β , v m,\alpha,\beta,v m,α,β,v Dynamics of m , β , v m,\beta,v m,β,v for a fixed α \alpha α 绿色轨迹连接初始点和目标点…

【linux】Linux 查看内存使用情况的几种方法汇总

文章目录 GUI 查看命令获取命令 free命令 vmstat命令 top命令 htop Linux 查看内存使用情况的几种方法包括使用 free 命令、top 命令、htop 命令、vmstat 命令和/proc/meminfo 文件。这些方法可以帮助用户了解系统内存的使用情况&#xff0c;包括总内存、已用内存、空闲内存、缓…

MapperStruct实现类为空

​ 问题描述&#xff1a; MapperStruct生成的实现了为空 按照在MapperStruct官网Installation – MapStruct中的方法配置后&#xff0c;生成的实现了是空的&#xff0c;如下&#xff1a; Overridepublic DeployHistory toEntity(DeployHistoryDto arg0) {if ( arg0 null ) …

经典题型---旋转数组

经典题型—旋转数组 文章目录 经典题型---旋转数组一、题目二、代码实现 一、题目 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步…

EPLAN_010#STEP格式_箱柜模型的定义、拼柜

一、导入 首先创建一个宏项目——在布局空间中导航器新建一个布局空间 菜单栏——布局空间——导入(3D图形&#xff09;——导入下载下来的STEP 如果导入进来的箱柜是这种模样的&#xff0c;表示可以使用。如果左侧只显示一个逻辑组件&#xff0c;则无法使用。&#xff08;如果…

webgl计算包围盒大小

使用three.js&#xff1b; 代码&#xff1b; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>第一个three.js 示例</title><style>body {margin: 0;overflow: hidden;}</style><…