【STM32】墨水屏驱动开发

目录

一、工程链接

二、简单介绍

三、阅读手册

参考电路

引脚定义

四、开发

cubemx配置

操作流程

代码流程

main函数测试


一、工程链接

STM32墨水屏驱动工程资源-CSDN下载

二、简单介绍

此前笔者购得一块超市货架用的电子价签,拆开后发现排线丝印写着"WFT0290CZ10"型号是GDEW029T5D。

墨水屏使用需注意:

  • 适用于更新周期从几十秒到几分钟的应用场景

  • 非常适合静态图像无需持续供电的应用

  • 超过6个月存储,每3个月刷新一次显示屏

  • 存储期间显示白色图案可防止无法消除的残影并保持最佳性能

墨水屏可以局部刷新也可以全屏刷新:局部刷新耗时短但多次局部刷新后可能有屏幕显示残留,需要全屏刷新一下。刷新完毕后根据需要进行turn off或者进入deep sleep模式。

笔者采用STM32F103CBT6单片机编写驱动,同时也参考网上的资料。

三、阅读手册

参考电路

引脚定义

DC引脚低代表命令,高代表数据。

RES引脚低则复位。

BUSY引脚低代表忙,高代表空闲。

BS1用于选择3线还是4线SPI。

笔者采用4线SPI通信方式,多一条D/C线。

在时钟的上升沿采样,且时钟空闲电平为低。

3线SPI的通信方式下,没有DC线,用SDA的第一个bit的高低电平来表征传输的是命令还是数据。

后面还有寄存器定义,就不赘述了。

四、开发

cubemx配置

开启SPI和相应的GPIO。

通过SPI来对墨水屏写入数据或者命令。

cpp 复制代码
static void EPD_Write(EPD_WriteType type, uint8_t send)
{
   tx[0] = send;
   
   if (type == EPD_CMD)
   {
      HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_RESET);
   }
   else
   {
      HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_SET);
   }

   HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);

   HAL_SPI_Transmit(&hspi1, tx, 1, 10);

   HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
}

操作流程

代码流程

根据操作流程,编写初始化函数。

cpp 复制代码
void EPD_Init()
{
   HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_SET);
   HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
   HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET);

   HAL_Delay(10);
   HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET);
   HAL_Delay(10);
   HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET);
   HAL_Delay(10);

   EPD_BoosterSoftStart();
   EPD_Config();
   EPD_Clear();
   EPD_Refresh();
   EPD_PartMode();
}

定义一个buffer存放屏幕画面信息,一个字节有8个bit,可以表示8个像素点。

cpp 复制代码
uint8_t epdBuf[EPD_WIDTH][EPD_HEIGHT / 8];

想用来显示字符,图片等都需要显示像素点,因此最核心的函数是打点函数。屏幕默认刷新方向如下。

现在要横过来显示,就要对字节的每一个bit进行操作。

cpp 复制代码
#if (ROTATION == 0)
/**
 * x 0 - 295
 * y 0 - 127
 */
void EPD_DrawPixel(uint16_t x, uint8_t y)
{
   epdBuf[x][y / 8] |= (1 << (7 - y % 8)); 
}
#elif (ROTATION == 180)
void EPD_DrawPixel(uint16_t x, uint8_t y)
{
   x = 295 - x;
   y = 127 - y;
   epdBuf[x][y / 8] |= (1 << (7 - y % 8)); 
}
#endif

​​​​​​​把buffer填充好,调用SPI发送。

cpp 复制代码
void EPD_Refresh()
{
   uint16_t i,j = 0;
   
   EPD_Write(EPD_CMD, 0x13);
   
   for (i = 0; i < 296; i++)
   {
      for (j = 0; j < 16; j++)
      {
         EPD_Write(EPD_DATA, ~epdBuf[i][j]);
      }
   }

   EPD_Write(EPD_CMD, 0x12);
   EPD_CheckBusy();
}

​​​​​​​发送后墨水屏进行处理,会处于busy状态,墨水屏将busy引脚拉低,此时就不能再对墨水屏发送任何内容。笔者这里作为演示,是同步操作,读者可按需改成异步操作。

cpp 复制代码
static void EPD_CheckBusy()
{
   while (HAL_GPIO_ReadPin(BUSY_GPIO_Port, BUSY_Pin) == GPIO_PIN_RESET);
}

​​​​​​​有了打点函数后就可以编写画线,填充,字符,图片函数。

cpp 复制代码
/**
 * x 0 - 295
 * y 0 - 127
 */
void EPD_DrawChar(uint16_t x, uint16_t y, char ch, GUI_CHARINFO_EXT* info)
{
   ch -= START_CHAR;
   uint16_t xbyts = ((info + ch)->XSize % 8 == 0) ? (info + ch)->XSize / 8 : (info + ch)->XSize / 8 + 1;

   for (uint16_t k = 0; k < (info + ch)->YSize; k++)
   {
      for (uint16_t i = 0; i < xbyts; i++)
      {
         for (uint16_t j = 0; j < 8; j++)
         {
            if (((info + ch)->pData[i + k * xbyts] >> j) & 0x01 == 1)
            {
               EPD_DrawPixel(x + 8 * i + 7 - j, y + (info + ch)->YSize - 1 - k);
            }
         }
      }
   }
}

void EPD_DrawPic(uint16_t x, uint16_t y, GUI_BITMAP* bitmap)
{
   for (uint16_t k = 0; k < bitmap->YSize; k++)
   {
      for (uint16_t i = 0; i < bitmap->BytesPerLine; i++)
      {
         for (uint16_t j = 0; j < 8; j++)
         {
            if ((bitmap->pData[i + k * bitmap->BytesPerLine] >> j) & 0x01 == 1)
            {
               EPD_DrawPixel(x + 8 * i + 7 - j, y + bitmap->YSize - 1 - k);
            }
         }
      }
   }
}

void EPD_DrawStr(uint16_t x, uint16_t y, char* str, GUI_CHARINFO_EXT* info)
{
   uint16_t i = 0;
   while (*(str + i + 1))
   {
      EPD_DrawChar(x, y, *(str + i), info);

      x += ((info + *(str + i) - START_CHAR)->XDist + (info + *(str + i + 1) - START_CHAR)->XPos);

      i++;
   }
   EPD_DrawChar(x, y, *(str + i), info);
}

void EPD_DrawHLine(uint16_t xs, uint16_t xe, uint16_t y)
{
   for (uint16_t i = xs; i < xe; i++)
   {
      EPD_DrawPixel(i, y);
   }
}

void EPD_DrawVLine(uint16_t x, uint16_t ys, uint16_t ye)
{
   for (uint16_t i = ys; i < ye; i++)
   {
      EPD_DrawPixel(x, i);
   }
}

void EPD_DrawRect(uint16_t xs, uint16_t xe, uint16_t ys, uint16_t ye)
{
   for (uint16_t i = ys; i < ye; i++)
   {
      EPD_DrawHLine(xs, xe, i);
   }
}

字符和图片的取模是使用segger的工具,如果安装了HAL固件包,一般在这个路径。

中文字符是由笔者自制的上位机生成。

main函数测试​​​​​​​

cpp 复制代码
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
   EPD_Init();

   for (uint8_t i = 0; i < 10; i++)
   {
      EPD_DrawChar(170 + 12 * i, 110, '0'+ i, GUI_FontMicrosoftYaHeiUI24_CharInfo);
   }

   EPD_DrawStr(170, 20, "hello world", GUI_FontMicrosoftYaHeiUI24_CharInfo);

   sprintf(str, "0123456789");
   EPD_DrawStr(170, 0, str, GUI_FontMicrosoftYaHeiUI24_CharInfo);

   sprintf(str, "3.14");
   EPD_DrawStr(170, 40, str, GUI_FontMicrosoftYaHeiUI24_CharInfo);


   for (uint8_t i = 0; i < 4; i++)
   {
      EPD_DrawChar(170 + 24 * i, 60, i + ' ', GUI_FontMicrosoftYaHeiUI22_CharInfo);
   }

   EPD_DrawRect(170, 190, 85, 105);
   EPD_DrawRect(210, 230, 85, 105);
   EPD_DrawRect(250, 270, 85, 105);

   EPD_DrawChar(110, 0, '1' - '-' + ' ', GUI_FontMicrosoftYaHeiUI200_CharInfo);
   EPD_DrawPic(0, 0, &bmapple_logo_2d_bmp_graphics_graphics_88386);

   EPD_Refresh();

   HAL_Delay(2000);
   EPD_FullMode();
   EPD_Clear();
   EPD_Refresh();
   EPD_PartMode();
   for (uint8_t i = 0; i < 10; i++)
   {
      EPD_BufReset();
      EPD_DrawChar(110, 0, i + '0' - '-' + ' ', GUI_FontMicrosoftYaHeiUI200_CharInfo);
      EPD_Refresh();
      HAL_Delay(1000);
   }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
相关推荐
清风6666661 小时前
基于单片机的多传感器智能云梯逃生控制器设计
单片机·嵌入式硬件·毕业设计·智能家居·课程设计
小何code1 小时前
STM32入门教程,第10课(上),OLED显示屏
stm32·单片机·嵌入式硬件
来自晴朗的明天1 小时前
高速画板-USB模块的PCB设计5-USB2.0/3.0布局布线要求
单片机·嵌入式硬件·硬件工程
早日退休!!!4 小时前
ARM Cortex-M核 【保存上下文&恢复上下文】
arm开发·单片机·嵌入式硬件
来自晴朗的明天4 小时前
差分控多少Ω阻抗
单片机·嵌入式硬件·硬件工程
SystickInt5 小时前
mosbus复习总结(20260110)
stm32
点灯小铭5 小时前
基于单片机的多功能智能婴儿车设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
π同学6 小时前
基于RT-Thread的STM32开发第十一讲——编码器模式
stm32·rt_thread·双相编码器
码农小韩8 小时前
基于Linux的C++学习——动态数组容器vector
linux·c语言·开发语言·数据结构·c++·单片机·学习
匠在江湖9 小时前
裸机单片机任务调度器实现:基于规范分层(COM/APP/SRV/DRV)架构,(附 任务调度器 / 微秒延时函数 / 串口重定向 源码)
单片机·嵌入式硬件·架构