当前位置: 首页 > article >正文

STM32 HAL库0.96寸OLED显示液晶屏

本文介绍了使用STM32 HAL库通过I2C协议驱动0.96寸OLED显示屏的方法。首先概述了OLED的基本特性和应用,然后详细讲解了汉字点阵生成的方法,并提供了完整的代码示例,包括初始化、清屏、字符串显示和自定义汉字显示函数。这些代码实现了在STM32F103ZET6开发板上显示特定内容的功能,如英文句子和中文字符“慢慢变好”。

目录

二、0.96 寸 OLED

三、生成汉字点阵

三、代码示例

1.初始化

2清屏函数

3.字符串显示函数

4.自定义汉字字模显示函数

5.完整示例代码,附件是源码

五、运行结果

六、总结


零、屏幕显示的本质

屏幕显示的核心其实非常简单,归根结底就是“点灯”。每一个分辨率单位对应一个像素点,也就是一盏可以单独控制的灯,屏幕放大后就是一个一个的小方块"灯"。

在屏幕上显示内容时,实际上就是在指定坐标(x, y)上点亮或熄灭相应的像素点。

屏幕显示=坐标(x,y)+内容

对于分辨率为128x64的显示屏来说,意味着它有64行、每行128个像素点,总共包含8192个独立可控制的灯(即像素)。要控制这个128x64的显示屏,本质上就是要对这8192个像素进行精确的点亮和熄灭操作。

在实际应用中,我们并不会一个个地去控制这些像素点,因为这样的效率太低了。我们会按照字节(8位)来操作显存,每个字节控制8个连续的像素点。

一、开发环境

硬件:正点原子精英 V2 STM32F103开发板

单片机:STM32F103ZET6

Keil版本:5.32

STM32CubeMX版本:6.9.2

STM32Cube MCU Packges版本:STM32F1xx_DFP.2.4.1
串口:USART1(PA9,PA10)

I2C2:PB10(SCL),PB11(SDA)

屏幕:0.96 寸 OLED 显示液晶屏模128 * 64 SSD1306,屏幕的SCL、SDA接到STM32的PB10(SCL),PB11(SDA)

二、0.96 寸 OLED

0.96 寸 OLED 显示液晶屏模块是一种体积小巧但功能强大的显示设备,在众多小型电子项目中广泛应用。它具有 128 * 64 的分辨率,采用 SSD1306 驱动芯片,支持 SPI 和 I2C 两种通信接口.

三、生成汉字点阵

显示汉字需要自行生成字库调用,运行“PCtoLCD2002”软件,点击菜单“选项”进行设置,如下图所示

比如显示"慢慢变好".输入"慢慢变好".点击生产字模,把字模复制到chinese_font.c

四、代码示例

底层驱动已经实现好,只是调用函数.

1.初始化
************** 7. 初始化函数 **************/
/*
 *  函数名:OLED_Init
 *  功能描述:初始化OLED
 *  输入参数:无
 *  输出参数:无
 *  返回值:无
 */
void OLED_Init(void)
{   
    /*
     * 前提: 已经初始化的I2C通道
     * 本工程里已经: 
     *    使用MX_I2C1_Init初始化I2C通道
     *    使用HAL_I2C_MspInit初始化I2C引脚
     */
    
    OLED_SetMemAddrMode(PAGE_ADDR_MODE);    // 0. 设置地址模式
    OLED_SetMuxRatio(0x3F);                 // 1. 设置多路复用率
    OLED_SetDispOffset(0x00);               // 2. 设置显示的偏移值
    OLED_SetDispStartLine(0x00);            // 3. 设置起始行
    OLED_SEG_REMAP();                       // 4. 行翻转
    OLED_SCAN_REMAP();                      // 5. 正常扫描
    OLED_SetComConfig(COM_PIN_SEQ, COM_NOREMAP);          // 6. COM 引脚设置
    OLED_SetContrastValue(0x7F);            // 7. 设置对比度
    ENTIRE_DISP_OFF();                      // 8. 全屏点亮/熄灭
    DISP_NORMAL();                          // 9. 显示模式
    OLED_SetDCLK_Freq(0x00, 0x08);          // 10. 设置分频系数和频率增值
    OLED_SetChargePump(PUMP_ENABLE);        // 11. 使能电荷碰撞
    
    OLED_SetComConfig(COM_PIN_ALT, COM_NOREMAP);
    
    DISP_ON();
}
2.清屏函数

清屏函数的核心思路是通过循环遍历屏幕的每一页,将每一页的所有像素点都置为 0,从而实现清屏的效果。buf 数组用于存储要写入屏幕的数据,所有元素初始化为 0。OLED_SetPosition 函数用于设置当前操作的页和列位置。OLED_WriteNBytes 函数用于将指定数量的数据写入屏幕。

通过这种方式,可以确保整个屏幕的显示内容被清空,显示为全黑。

/*
 *  函数名:OLED_Clear
 *  功能描述:清屏函数,用于将0.96寸OLED显示液晶屏的显示内容清空,使屏幕显示为全黑。
 *  输入参数:无
 *  输出参数:无
 *  返回值:无
 *  实现原理:
 *      该OLED屏幕采用页寻址模式,整个屏幕被划分为8页,每页有128列。
 *      清屏的过程就是将每一页的128个像素点都置为0(即不发光)。
 *      首先创建一个长度为128的缓冲区,所有元素初始化为0,代表一列的像素数据都为0。
 *      然后通过循环遍历每一页,设置当前操作的页和列位置,将缓冲区的数据写入该页,从而清空该页的显示内容。
*/
void OLED_Clear(void)
{
    // 定义一个循环变量i,用于遍历屏幕的每一页
    uint8_t i = 0;
    // 定义一个长度为128的缓冲区buf,用于存储要写入屏幕的数据
    // 所有元素初始化为0,代表一列的像素数据都为0,即不发光
    uint8_t buf[128] = {0};
    
    // 循环遍历屏幕的8页
    for(i = 0; i < 8; i++)
    {
        // 调用OLED_SetPosition函数,设置当前操作的页和列位置
        // i表示当前页号(0 - 7),0表示列号从第0列开始
        OLED_SetPosition(i, 0);
        // 调用OLED_WriteNBytes函数,将缓冲区buf中的128个字节数据写入当前页
        // 由于buf中的数据都为0,所以写入后该页的所有像素点都不发光,实现了清屏效果
        OLED_WriteNBytes(&buf[0], 128);
    }
}
3.字符串显示函数

此函数的主要逻辑是遍历传入的字符串,使用 OLED_PutChar 函数逐个显示字符。每显示一个字符,x 坐标右移一位。当 x 坐标超出范围(大于 15)时,x 坐标重置为 0,y 坐标下移 2 页。最后返回成功显示的字符数量.

/*
 *  函数名:OLED_PrintString
 *  功能描述:在0.96寸OLED显示液晶屏上显示一个字符串。该函数会将传入的字符串按字符逐个显示在指定的起始坐标位置。
 *  输入参数:
 *            x --> x坐标(0~15),表示字符串起始显示位置的列坐标,取值范围为0到15,每一个单位对应屏幕上一定的列位置。
 *            y --> y坐标(0~7),表示字符串起始显示位置的页坐标,取值范围为0到7,每一页对应屏幕垂直方向上的一部分区域。
 *            str --> 显示的字符串,是一个以'\0'结尾的字符数组。
 *  输出参数:无
 *  返回值:打印了多少个字符,即成功显示在屏幕上的字符数量。
 */
int OLED_PrintString(uint8_t x, uint8_t y, const char *str)
{   
    // 定义一个整型变量i,用于记录当前处理的字符在字符串中的索引位置,同时也用于统计打印的字符数量
    int i = 0;

    // 进入循环,只要字符串还未结束(即当前字符不为字符串结束符'\0'),就继续处理
    while (str[i])
    {
        // 调用OLED_PutChar函数,将当前字符显示在指定的x、y坐标位置
        OLED_PutChar(x, y, str[i]);

        // 显示完一个字符后,将x坐标加1,以便在下一列显示下一个字符
        x++;

        // 判断x坐标是否超出了最大允许值(即是否超过了15)
        if(x > 15)
        {
            // 如果x坐标超出范围,将x坐标重置为0,以便从下一行的起始位置开始显示
            x  = 0;
            // 同时将y坐标增加2,因为通常每显示一行字符后,需要移动到下一页继续显示
            y += 2;
        }
                
        // 索引位置i加1,指向下一个字符
        i++;
    }

    // 循环结束后,返回打印的字符数量
    return i;
}
4.自定义汉字字模显示函数

此函数 OLED_PrintChinese1 用于在 OLED 屏幕指定位置显示预定义的中文字符。它通过外部数组 g_chinese_fonts1 存储中文字符的字模数据,按顺序将每个中文字符的上半部分和下半部分数据依次写入 OLED 屏幕,并更新列位置以显示下一个字符。在写入前会检查输入坐标是否有效,无效则不进行显示操作

/**
 * @brief 在OLED屏幕上显示预定义的中文字符。
 * 
 * 该函数用于在OLED屏幕的指定位置显示一系列预定义的中文字符。中文字符的字模数据存储在外部数组g_chinese_fonts1中。
 * 
 * @param x 中文字符起始显示位置的列索引(范围0 - 15),每个单位对应8列。
 * @param y 中文字符起始显示位置的页索引(范围0 - 7),表示垂直位置。
 * 
 * @return 无
 */
void OLED_PrintChinese1(uint8_t x, uint8_t y)
{   
    // 声明外部数组g_chinese_fonts1,该数组存储了中文字符的字模数据。
    // 数组的每个元素代表一个中文字符,每个中文字符由32字节的数据组成(上半部分16字节,下半部分16字节)。
    extern uint8_t g_chinese_fonts1[4][32];

    // 保存起始页位置,用于后续操作。
    uint8_t page = y;
    // 计算起始列位置,由于每个中文字符宽度为16列,而x是按8列单位计数的,所以乘以8。
    uint8_t col  = x * 8;

    // 检查输入的坐标是否超出OLED屏幕的有效范围。
    // 如果y超过7(页索引范围为0 - 7)或者x超过15(列索引范围为0 - 15),则直接返回,不进行显示操作。
    if (y > 7 || x > 15)
        return;

    // 循环变量,用于遍历中文字符数组。
    int i;
    // 遍历g_chinese_fonts1数组中的每个中文字符。
    // sizeof(g_chinese_fonts1)返回整个数组的字节数,sizeof(g_chinese_fonts1[0])返回一个中文字符数据的字节数,
    // 两者相除得到数组中中文字符的数量。
    for (i = 0; i < sizeof(g_chinese_fonts1) / sizeof(g_chinese_fonts1[0]); i++)
    {
        // 设置OLED屏幕的当前显示位置为当前页和当前列。
        // 这是为了准备写入当前中文字符的上半部分数据。
        OLED_SetPosition(page, col);

        // 向OLED屏幕发送当前中文字符的上半部分数据(前16字节)。
        // &g_chinese_fonts1[i][0]是当前中文字符上半部分数据的起始地址,16表示要发送的字节数。
        OLED_WriteNBytes((uint8_t*)&g_chinese_fonts1[i][0], 16);

        // 设置OLED屏幕的当前显示位置为下一页(page + 1)和当前列。
        // 这是为了准备写入当前中文字符的下半部分数据。
        OLED_SetPosition(page + 1, col);

        // 向OLED屏幕发送当前中文字符的下半部分数据(后16字节)。
        // &g_chinese_fonts1[i][16]是当前中文字符下半部分数据的起始地址,16表示要发送的字节数。
        OLED_WriteNBytes((uint8_t*)&g_chinese_fonts1[i][16], 16);

        // 更新列位置,为显示下一个中文字符做准备。
        // 每个中文字符宽度为16列,所以将列位置增加16。
        col += 16;
    }
}
5.完整示例代码,附件是源码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include "driver_oled.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
extern UART_HandleTypeDef huart1;
extern I2C_HandleTypeDef hi2c2;
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
//char *str= "I2C FUNCTIONS\r\n";
//char c;

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @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_USART1_UART_Init();
  MX_I2C2_Init();
  /* USER CODE BEGIN 2 */

  OLED_Init();
	OLED_Clear();
	OLED_PrintString(0, 0, "I love Aissa");
	OLED_PrintChinese1(3, 3);
	OLED_PrintChinese2(3, 6);
	
  /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */

 
  }
  /* USER CODE END 3 */
}

/**
  * @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();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @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 */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

五、运行结果

屏幕显示如下内容

六、总结

本文介绍了使用STM32 HAL库通过I2C协议驱动0.96寸OLED显示屏的方法,仅供参考,有任何问题,欢迎在评论区留言讨论!


http://www.kler.cn/a/559323.html

相关文章:

  • 虚拟机 VirtualBox7 安装 ubuntu-Linux24.04.1LTS 和常用配置
  • DVWA 靶场
  • WPF框架学习
  • Apache Doris 实现毫秒级查询响应
  • oracle apex post接口
  • vxe-table实现动态列
  • 3分钟idea接入deepseek
  • 4.hive集群搭建
  • 推荐给 Easysearch 新用户的几个 Elasticsearch 可视化工具
  • 【多模态大模型】端侧语音大模型minicpm-o:手机上的 GPT-4o 级多模态大模型
  • 淘系图搜API接入与使用全解析
  • vscode代码补全 main
  • ARM-Linux 基础项目篇——简单的视频监控
  • 路由基本配置
  • 深度学习学习笔记(34周)
  • IDEA中查询Maven项目的依赖树
  • 计算机网络:应用层 —— 域名系统 DNS
  • 【Java并发】CAS原理
  • Spring Boot日志配置与环境切换实战
  • Chrome 浏览器(版本号49之后)‌解决跨域问题