【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 */
}
相关推荐
点灯小铭5 小时前
基于51单片机手机无线蓝牙APP控制风扇调速设计
单片机·mongodb·智能手机·毕业设计·51单片机·课程设计
沐欣工作室_lvyiyi7 小时前
采用AIOT技术的防疫物资监控系统的设计与开发(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·防疫物资
XCOSnTh10 小时前
单片机入门的相关工具XCOSnTh
c语言·单片机·嵌入式硬件·xcosnth·单片机入门
光子物联单片机10 小时前
STM32G474单片机开发入门(四)中断详解及GPIO外部中断输入
stm32·单片机·嵌入式硬件·mcu
逼子格11 小时前
【Proteus仿真】虚拟终端出现乱码问题解决
单片机·嵌入式硬件·proteus·嵌入式·硬件工程·电路仿真·虚拟终端
MAR-Sky13 小时前
单片机学习中的一些简单总结
单片机·嵌入式硬件·学习
点灯小铭13 小时前
基于stm32的物联网OneNet火灾报警系统
stm32·单片机·嵌入式硬件·物联网·毕业设计·课程设计
许商21 小时前
【stm32】【edgetx】解析链接脚本文件(ld)
stm32·单片机·嵌入式硬件
云山工作室1 天前
基于单片机的三相逆变电源设计
单片机·嵌入式硬件·毕业设计·毕设