1.54寸墨水屏模块 E-Paper电子纸显示屏STM32C8T6驱动代码以及调试过程


我双休实在太累了,然后跟我的hxd上号就没更新,我最近在写墨水屏驱动,我先把 购买链接附上,然后呢我不管咋跑这家店的例程半天跑不出来,我不知道是不是这个店铺的例程有问题嗷,要没问题的小伙伴可以写个文章在评论区嗷,后面呢我领导找了个 微雪的例程让我跑我搞了两个小时也没出来,下班了领导说他加班搞搞,然后呢第二天他搞出来了还不告诉我,让我自己搞,哎,咱能力不太行嗷,墨迹了半天才出来。

对了,昨天现学了SPI,本来昨天要发的,我丢草稿箱了,emm要么晚上发要么明天再发吧。

注意:这个模块虽然是引脚是三线SPI,但实际是四线SPI,模块背面的按键得调成L,也就是四线!

如何点亮屏幕?

由于这个微雪的历程是STM32F103ZET6的,我用的是STM32F103C8T6,需要更换个启动文件
在这里插入图片描述
在这里插入图片描述

点击魔术棒,把芯片换成F103C8的
在这里插入图片描述
因为C6可以和C8兼容,所以我使用J-LINK也使用C6
在这里插入图片描述
keil v5 No Ulink2/Me Device Found 的解决方法
在这里插入图片描述
把这个注释去掉
在这里插入图片描述
编译,这时候会报一个错,堆内存不够,要想快速让屏驱动,把图片的代码注释了,就留显示四个点的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
将堆空间改成2800,除了图片基本都可以显示了。
在这里插入图片描述
在这里插入图片描述
一上电就会闪,我后面找到的话会改,效果如下图所示。
在这里插入图片描述

例程代码拆分

DEV_Module_Init(); 模块引脚初始化

在这里插入图片描述
在这里插入图片描述

EPD_1IN54_V2_Init(); 寄存器初始化

在这里插入图片描述
在这里插入图片描述
到SetRAMX那我现在还并不知道为啥不一样。。

EPD_1IN54_V2_Clear(); 清屏

因为屏幕是200x200,一位代表一个像素点,一个字节即可代表8个像素点
至于两个有什么区别就不知道了

0x24是
在这里插入图片描述

EPD_1IN54_V2_TurnOnDisplay(); 打开显示满

刷新屏幕一闪一闪的就是因为这个函数,但是呢似乎没办法把一闪一闪的关掉
在这里插入图片描述

图片动态开辟空间

因为一个字节8个像素点,取余8,如果取余为0,则可能是200*200的
在这里插入图片描述

void Paint_NewImage(UBYTE *image, UWORD Width, UWORD Height, UWORD Rotate, UWORD Color) 创建图片

image : 图像缓存的名称,实际上是一个指向图像缓存首地址的指针;
Width : 图像缓存的宽度;
Height: 图像缓存的高度;
Rotate:图像的翻转的角度
Color :图像的初始颜色;

注意:旋转和

在这里插入图片描述
因为定义了一个全局结构体,这里是对结构体初始化

显示图片

在这里插入图片描述

void Paint_SelectImage(UBYTE *image) 选择图片

也就是把图片传给那个全局变量结构体
在这里插入图片描述

void Paint_Clear(UWORD Color) 清除图片颜色

#define WHITE 0xFF
#define BLACK 0x00

发现把这个函数注释和更改值,显示的图片都没变化。
没变化是因为图片覆盖了清除的颜色,设置白色就是全屏白色,设置黑色就是全屏黑色,如果把这个清除图片函数去掉,就会默认背景色黑色,会影响下面绘图的效果,如果背景色是黑色,绘图的也是黑色,就什么也看不见了。

在这里插入图片描述

void Paint_DrawBitMap(const unsigned char* image_buffer) 显示单色位图

也就是把图片写入RAM,也可以在此基础上改成清屏函数,把图片改成0x00就是黑色,0xff就是白色
在这里插入图片描述

void EPD_1IN54_V2_Display(UBYTE *Image) 将RAM中的图像缓冲区发送到电子纸和显示器上

在这里插入图片描述

static void EPD_1IN54_V2_SendData(UBYTE Data) 数据写入

在这里插入图片描述

void DEV_SPI_WriteByte(UBYTE value) SPI写字节

hspil 就是我们之前定义的全局结构体
value 就是我们要传输的数据
1 就是发送数据量
1000 是timeout,超时时间

在这里插入图片描述

在图像上绘制

下图对应上面Paint_Clear函数的注释
在这里插入图片描述

void Paint_DrawPoint(UWORD Xpoint, UWORD Ypoint, UWORD Color,DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_Style) 画点函数

Xpoint : 点的X坐标
Ypoint : 点的Y坐标
Color : 填充颜色
Dot_Pixel : 点大小
Dot_Style : 点样式

在这里插入图片描述
下面这段就是判断绘制的坐标是否超出屏幕范围
在这里插入图片描述
画点分为两种类型
一种是DOT_FILL_AROUND ( XDir_Num < 2 * Dot_Pixel - 1 )
另一种是DOT_FILL_RIGHTUP ( XDir_Num < Dot_Pixel )
这两种类型区别也就是一个大一个小
然后就是一个个像素点写入了

在这里插入图片描述

void Paint_SetPixel(UWORD Xpoint, UWORD Ypoint, UWORD Color) 绘制像素

Xpoint : 在X点
Ypoint : 在Y点
Color : 绘制颜色

在这里插入图片描述
在这里插入图片描述
下面这部分还是判断是否超出边界
在这里插入图片描述
判断是否旋转,旋转也会导致x和y改变
在这里插入图片描述
这就判断是否对称了
在这里插入图片描述
再次判断是否超出边界,因为可能旋转或者对称了,画布如果不是200x200的,如果是200x100的就会超出边界,200x200的其实不需要,因为不管怎么对称怎么旋转都不会越界
在这里插入图片描述
不同的Scale(规模)就对应着不同的写入,在创建图片那写的是2,我试着改成了4和7,发现会变大很多,应该是对应着尺寸,因为微雪的例程不仅仅适应2寸显示屏,还有4,5,7寸显示屏

那么为什么黑色是Paint.Image[Addr] = Rdata & ~(0x80 >> (X % 8));
白色是Paint.Image[Addr] = Rdata | (0x80 >> (X % 8));?
因为白色是1黑色是0 ,所以地址或1,就可以把像素点变成白色
黑色的话就取反再与,这样就不会动其他像素,只改变我需要更改的像素点为黑色

因为一个字节对应8个像素点,也就是0x80,也就对应的二进制8位
在这里插入图片描述
这里还是把数据写入缓冲区RAM,还没刷新界面。到最后肯定是调用EPD_1IN54_V2_Display(UBYTE *Image)刷新界面。
在这里插入图片描述

void Paint_DrawLine(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color, DOT_PIXEL Line_width, LINE_STYLE Line_Style) 画线函数(可任意斜率)

Xstart :线段X起点
Ystart :线段Y起点
Xend : 线段X终点
Yend : 线段Y终点
Color :线段的颜色
Line_width : 行宽
Line_Style: 线的类型:实线还是虚线

在这里插入图片描述
在这里插入图片描述
这一部分是判断点是否超出边界
在这里插入图片描述
这一部分是让dx,dy是绝对的正数,如果拿起点作为坐标起点的话,那么终点可以在四个象限的任意一个
在这里插入图片描述
增量方向,1为正,-1为负
在这里插入图片描述
累计误差
在这里插入图片描述
首先这判断了是实线还是虚线,如果是实线那么Dotted_Len就没啥用了,第一个判断,写了3个实线点再写一个虚线点,换句话说就是写了三个1再写了一个0,下面的两个判断,就是保证他任何斜率都可以的关键
在这里插入图片描述

void Paint_DrawRectangle(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend,UWORD Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill) 画矩形

上来判断还是有没有越界嗷,然后判断全写满还是只画框,也就是对画线函数组合了一下
在这里插入图片描述

void Paint_DrawCircle(UWORD X_Center, UWORD Y_Center, UWORD Radius, UWORD Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill) 画圆形

首先还是判断是否越界,然后是画实心圆还是空心圆
在这里插入图片描述

  • 12
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
以下是基于STM32Cube HAL库的0.96屏幕驱动代码,仅供参考: ```c /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "ssd1306.h" #include "fonts.h" /* Private variables ---------------------------------------------------------*/ I2C_HandleTypeDef hi2c1; /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_I2C1_Init(void); /* Private functions ---------------------------------------------------------*/ /** * @brief The application entry point. * @retval int */ int main(void) { /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_I2C1_Init(); /* Initialize the SSD1306 OLED display */ SSD1306_Init(); /* Show some text on the OLED display */ SSD1306_GotoXY(10,10); SSD1306_Puts("Hello, World!", &Font_7x10, 1); SSD1306_UpdateScreen(); /* Infinite loop */ while (1) { } } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /** * @brief I2C1 Initialization Function * @param None * @retval None */ static void MX_I2C1_Init(void) { /* USER CODE BEGIN I2C1_Init 0 */ /* USER CODE END I2C1_Init 0 */ /* USER CODE BEGIN I2C1_Init 1 */ /* USER CODE END I2C1_Init 1 */ hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } /** Configure Analogue filter */ if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK) { Error_Handler(); } /** Configure Digital filter */ if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C1_Init 2 */ /* USER CODE END I2C1_Init 2 */ } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); /*Configure GPIO pin : PB12 */ GPIO_InitStruct.Pin = GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ /* USER CODE END Error_Handler_Debug */ } ``` 除了上述代码,你还需要在工程中添加SSD1306驱动程序,可以从GitHub上下载SSD1306库,或者自己编写驱动程序。注意修改I2C相关的宏定义以适应自己的硬件配置。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小阿大:)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值