【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来对墨水屏写入数据或者命令。
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);
}
操作流程
代码流程
根据操作流程,编写初始化函数。
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个像素点。
uint8_t epdBuf[EPD_WIDTH][EPD_HEIGHT / 8];
想用来显示字符,图片等都需要显示像素点,因此最核心的函数是打点函数。屏幕默认刷新方向如下。
现在要横过来显示,就要对字节的每一个bit进行操作。
#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发送。
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引脚拉低,此时就不能再对墨水屏发送任何内容。笔者这里作为演示,是同步操作,读者可按需改成异步操作。
static void EPD_CheckBusy()
{while (HAL_GPIO_ReadPin(BUSY_GPIO_Port, BUSY_Pin) == GPIO_PIN_RESET);
}
有了打点函数后就可以编写画线,填充,字符,图片函数。
/*** 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函数测试
/*** @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 */
}