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

ESP32移植Openharmony外设篇(3)OLED屏

模块简介

产品介绍

OLED (Organic Light-Emitting Diode):有机发光二极管又称为有机电激光显示,OLED显示技术具有自发光的特性,采用薄的有机材料涂层和玻璃基板,当有电流通过时,这些有机材料就会发光,而且OLED显示屏幕可视角度大,功耗低。OLED由于同时具备自发光、不需背光源(只上电是不会亮的,驱动程序和接线正确才会点亮)、对比度高、厚度薄、视角广、反应速度快、可用于挠曲面板、使用温度范围广、结构及制程简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。

电气参数

模块尺寸

23.7 *23.8mm

电源电压

3.3-5.5V

驱动芯片

SSD1306

OLED屏幕原理

OLED本身是没有显存的,他的现存是依赖SSD1306提供的,而SSD1306提供一块显存。

SSD1306显存总共为128*64bit大小,SSD1306将这些显存分成了8页。每页包含了128个字节。

单片机内部建立一个缓存(共128*8个字节),每次修改的时候,只是修改单片机上的缓存(实际上就是SRAM),修改完后一次性把STM32上的缓存数据写入到OLED的GRAM。这个方法也有坏处,对于SRAM很小的单片机(51系列)就比较麻烦。

原理图

取字模

因为OLED屏显示的逻辑是根据一个十六进制数 一个点一个点的点亮对应值为1的像素点,网上有很多取字模的网站,这里推荐一个自己使用的取字模的网址:

汉字字模在线: https://www.23bei.com/tool-223.html

参考代码

BUILD.gn
# Copyright (c) 2022 Hunan OpenValley Digital Industry Development Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.import("//kernel/liteos_m/liteos.gni")
module_name = get_path_info(rebase_path("."), "name")
kernel_module(module_name){include_dirs = ["//drivers/hdf_core/framework/include/platform/","//drivers/hdf_core/framework/include/utils/","//drivers/hdf_core/framework/support/platform/include/gpio","//drivers/hdf_core/adapter/khdf/liteos_m/osal/include/","//drivers/hdf_core/framework/include/core/","//drivers/hdf_core/framework/include/osal/","//device/soc/esp/esp32/components/driver/include","//device/soc/esp/esp32/components/esp_adc_cal/include","//device/soc/esp/esp32/components/driver/esp32/include",]sources = ["ssd1306.c","ssd1306_fonts.c","ssd1306_demo.c","ssd1306_tests.c",]
}
ssd1306.c
#include "ssd1306.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>  // For memcpy#include <stdio.h>
#include <unistd.h>
#include "cmsis_os2.h"
#include <stdio.h>
#include "cmsis_os2.h"
#include "ohos_run.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/i2c.h"#if defined(SSD1306_USE_I2C)#define SSD1306_I2C_IDX     0#define SSD1306_CTRL_CMD 0x00
#define SSD1306_CTRL_DATA 0x40
#define SSD1306_MASK_CONT (0x1<<7)void ssd1306_Reset(void) {/* for I2C - do nothing */
}void HAL_Delay(uint32_t ms)
{usleep(ms * 1000);
}static uint32_t ssd1306_SendData(uint8_t* data, size_t size)
{int i;i2c_cmd_handle_t cmd_handle = i2c_cmd_link_create();i2c_master_start(cmd_handle);i2c_master_write_byte(cmd_handle, SSD1306_I2C_ADDR, true);for(i = 0; i < size; i ++){i2c_master_write_byte(cmd_handle, data[i], true);}i2c_master_stop(cmd_handle);esp_err_t error = i2c_master_cmd_begin(I2C_NUM_0,cmd_handle,100/portTICK_PERIOD_MS);i2c_cmd_link_delete(cmd_handle);return error;
}static uint32_t ssd1306_WiteByte(uint8_t regAddr, uint8_t byte)
{uint8_t buffer[] = {regAddr, byte};return ssd1306_SendData(buffer, sizeof(buffer));
}// Send a byte to the command register
void ssd1306_WriteCommand(uint8_t byte) {ssd1306_WiteByte(SSD1306_CTRL_CMD, byte);
}// Send data
void ssd1306_WriteData(uint8_t* buffer, size_t buff_size) {uint8_t data[SSD1306_WIDTH * 2] = {0};for (size_t i = 0; i < buff_size; i++) {data[i*2] = SSD1306_CTRL_DATA | SSD1306_MASK_CONT;data[i*2+1] = buffer[i];}data[(buff_size - 1) * 2] = SSD1306_CTRL_DATA;ssd1306_SendData(data, sizeof(data));
}#elif defined(SSD1306_USE_SPI)void ssd1306_Reset(void) {// CS = High (not selected)HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_SET);// Reset the OLEDHAL_GPIO_WritePin(SSD1306_Reset_Port, SSD1306_Reset_Pin, GPIO_PIN_RESET);HAL_Delay(10);HAL_GPIO_WritePin(SSD1306_Reset_Port, SSD1306_Reset_Pin, GPIO_PIN_SET);HAL_Delay(10);
}// Send a byte to the command register
void ssd1306_WriteCommand(uint8_t byte) {HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_RESET); // select OLEDHAL_GPIO_WritePin(SSD1306_DC_Port, SSD1306_DC_Pin, GPIO_PIN_RESET); // commandHAL_SPI_Transmit(&SSD1306_SPI_PORT, (uint8_t *) &byte, 1, HAL_MAX_DELAY);HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_SET); // un-select OLED
}// Send data
void ssd1306_WriteData(uint8_t* buffer, size_t buff_size) {HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_RESET); // select OLEDHAL_GPIO_WritePin(SSD1306_DC_Port, SSD1306_DC_Pin, GPIO_PIN_SET); // dataHAL_SPI_Transmit(&SSD1306_SPI_PORT, buffer, buff_size, HAL_MAX_DELAY);HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_SET); // un-select OLED
}#else
#error "You should define SSD1306_USE_SPI or SSD1306_USE_I2C macro"
#endif// Screenbuffer
static uint8_t SSD1306_Buffer[SSD1306_BUFFER_SIZE];// Screen object
static SSD1306_t SSD1306;/* Fills the Screenbuffer with values from a given buffer of a fixed length */
SSD1306_Error_t ssd1306_FillBuffer(uint8_t* buf, uint32_t len) {SSD1306_Error_t ret = SSD1306_ERR;if (len <= SSD1306_BUFFER_SIZE) {memcpy(SSD1306_Buffer,buf,len);ret = SSD1306_OK;}return ret;
}// Initialize the oled screen
void ssd1306_Init(void) {// Reset OLEDssd1306_Reset();// Wait for the screen to bootHAL_Delay(100);// Init OLEDssd1306_SetDisplayOn(0); //display offssd1306_WriteCommand(0x20); //Set Memory Addressing Modessd1306_WriteCommand(0x00); // 00b,Horizontal Addressing Mode; 01b,Vertical Addressing Mode;// 10b,Page Addressing Mode (RESET); 11b,Invalidssd1306_WriteCommand(0xB0); //Set Page Start Address for Page Addressing Mode,0-7#ifdef SSD1306_MIRROR_VERTssd1306_WriteCommand(0xC0); // Mirror vertically
#elsessd1306_WriteCommand(0xC8); //Set COM Output Scan Direction
#endifssd1306_WriteCommand(0x00); //---set low column addressssd1306_WriteCommand(0x10); //---set high column addressssd1306_WriteCommand(0x40); //--set start line address - CHECKssd1306_SetContrast(0xFF);#ifdef SSD1306_MIRROR_HORIZssd1306_WriteCommand(0xA0); // Mirror horizontally
#elsessd1306_WriteCommand(0xA1); //--set segment re-map 0 to 127 - CHECK
#endif#ifdef SSD1306_INVERSE_COLORssd1306_WriteCommand(0xA7); //--set inverse color
#elsessd1306_WriteCommand(0xA6); //--set normal color
#endif// Set multiplex ratio.
#if (SSD1306_HEIGHT == 128)// Found in the Luma Python lib for SH1106.ssd1306_WriteCommand(0xFF);
#elsessd1306_WriteCommand(0xA8); //--set multiplex ratio(1 to 64) - CHECK
#endif#if (SSD1306_HEIGHT == 32)ssd1306_WriteCommand(0x1F); //
#elif (SSD1306_HEIGHT == 64)ssd1306_WriteCommand(0x3F); //
#elif (SSD1306_HEIGHT == 128)ssd1306_WriteCommand(0x3F); // Seems to work for 128px high displays too.
#else
#error "Only 32, 64, or 128 lines of height are supported!"
#endifssd1306_WriteCommand(0xA4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM contentssd1306_WriteCommand(0xD3); //-set display offset - CHECKssd1306_WriteCommand(0x00); //-not offsetssd1306_WriteCommand(0xD5); //--set display clock divide ratio/oscillator frequencyssd1306_WriteCommand(0xF0); //--set divide ratiossd1306_WriteCommand(0xD9); //--set pre-charge periodssd1306_WriteCommand(0x11); // 0x22 by defaultssd1306_WriteCommand(0xDA); //--set com pins hardware configuration - CHECK
#if (SSD1306_HEIGHT == 32)ssd1306_WriteCommand(0x02);
#elif (SSD1306_HEIGHT == 64)ssd1306_WriteCommand(0x12);
#elif (SSD1306_HEIGHT == 128)ssd1306_WriteCommand(0x12);
#else
#error "Only 32, 64, or 128 lines of height are supported!"
#endifssd1306_WriteCommand(0xDB); //--set vcomhssd1306_WriteCommand(0x30); //0x20,0.77xVcc, 0x30,0.83xVccssd1306_WriteCommand(0x8D); //--set DC-DC enablessd1306_WriteCommand(0x14); //ssd1306_SetDisplayOn(1); //--turn on SSD1306 panel// Clear screenssd1306_Fill(Black);// Flush buffer to screenssd1306_UpdateScreen();// Set default values for screen objectSSD1306.CurrentX = 0;SSD1306.CurrentY = 0;SSD1306.Initialized = 1;
}// Fill the whole screen with the given color
void ssd1306_Fill(SSD1306_COLOR color) {/* Set memory */uint32_t i;for(i = 0; i < sizeof(SSD1306_Buffer); i++) {SSD1306_Buffer[i] = (color == Black) ? 0x00 : 0xFF;}
}// Write the screenbuffer with changed to the screen
void ssd1306_UpdateScreen(void) {// Write data to each page of RAM. Number of pages// depends on the screen height:////  * 32px   ==  4 pages//  * 64px   ==  8 pages//  * 128px  ==  16 pagesuint8_t cmd[] = {0X21,   // 设置列起始和结束地址0X00,   // 列起始地址 00X7F,   // 列终止地址 1270X22,   // 设置页起始和结束地址0X00,   // 页起始地址 00X07,   // 页终止地址 7};uint32_t count = 0;uint8_t data[sizeof(cmd)*2 + SSD1306_BUFFER_SIZE + 1] = {};// copy cmdfor (uint32_t i = 0; i < sizeof(cmd)/sizeof(cmd[0]); i++) {data[count++] = SSD1306_CTRL_CMD | SSD1306_MASK_CONT;data[count++] = cmd[i];}// copy frame datadata[count++] = SSD1306_CTRL_DATA;memcpy(&data[count], SSD1306_Buffer, sizeof(SSD1306_Buffer));count += sizeof(SSD1306_Buffer);// send to i2c busuint32_t retval = ssd1306_SendData(data, count);if (retval != 0) {printf("ssd1306_UpdateScreen send frame data filed: %d!\r\n", retval);}
}//    Draw one pixel in the screenbuffer
//    X => X Coordinate
//    Y => Y Coordinate
//    color => Pixel color
void ssd1306_DrawPixel(uint8_t x, uint8_t y, SSD1306_COLOR color) {if(x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) {// Don't write outside the bufferreturn;}// Check if pixel should be invertedif(SSD1306.Inverted) {color = (SSD1306_COLOR)!color;}// Draw in the right colorif(color == White) {SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8);} else {SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8));}
}// Draw 1 char to the screen buffer
// ch       => char om weg te schrijven
// Font     => Font waarmee we gaan schrijven
// color    => Black or White
char ssd1306_DrawChar(char ch, FontDef Font, SSD1306_COLOR color) {uint32_t i, b, j;// Check if character is validif (ch < 32 || ch > 126)return 0;// Check remaining space on current lineif (SSD1306_WIDTH < (SSD1306.CurrentX + Font.FontWidth) ||SSD1306_HEIGHT < (SSD1306.CurrentY + Font.FontHeight)){// Not enough space on current linereturn 0;}// Use the font to writefor(i = 0; i < Font.FontHeight; i++) {b = Font.data[(ch - 32) * Font.FontHeight + i];for(j = 0; j < Font.FontWidth; j++) {if((b << j) & 0x8000)  {ssd1306_DrawPixel(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR) color);} else {ssd1306_DrawPixel(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR)!color);}}}// The current space is now takenSSD1306.CurrentX += Font.FontWidth;// Return written char for validationreturn ch;
}// Write full string to screenbuffer
char ssd1306_DrawString(char* str, FontDef Font, SSD1306_COLOR color) {// Write until null-bytewhile (*str) {if (ssd1306_DrawChar(*str, Font, color) != *str) {// Char could not be writtenreturn *str;}// Next charstr++;}// Everything okreturn *str;
}// Position the cursor
void ssd1306_SetCursor(uint8_t x, uint8_t y) {SSD1306.CurrentX = x;SSD1306.CurrentY = y;
}// Draw line by Bresenhem's algorithm
void ssd1306_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, SSD1306_COLOR color) {int32_t deltaX = abs(x2 - x1);int32_t deltaY = abs(y2 - y1);int32_t signX = ((x1 < x2) ? 1 : -1);int32_t signY = ((y1 < y2) ? 1 : -1);int32_t error = deltaX - deltaY;int32_t error2;ssd1306_DrawPixel(x2, y2, color);while((x1 != x2) || (y1 != y2)){ssd1306_DrawPixel(x1, y1, color);error2 = error * 2;if(error2 > -deltaY){error -= deltaY;x1 += signX;}else{/*nothing to do*/}if(error2 < deltaX){error += deltaX;y1 += signY;}else{/*nothing to do*/}}return;
}
//Draw polyline
void ssd1306_DrawPolyline(const SSD1306_VERTEX *par_vertex, uint16_t par_size, SSD1306_COLOR color) {uint16_t i;if(par_vertex != 0){for(i = 1; i < par_size; i++){ssd1306_DrawLine(par_vertex[i - 1].x, par_vertex[i - 1].y, par_vertex[i].x, par_vertex[i].y, color);}}else{/*nothing to do*/}return;
}
/*Convert Degrees to Radians*/
static float ssd1306_DegToRad(float par_deg) {return par_deg * 3.14 / 180.0;
}
/*Normalize degree to [0;360]*/
static uint16_t ssd1306_NormalizeTo0_360(uint16_t par_deg) {uint16_t loc_angle;if(par_deg <= 360){loc_angle = par_deg;}else{loc_angle = par_deg % 360;loc_angle = ((par_deg != 0)?par_deg:360);}return loc_angle;
}
/*DrawArc. Draw angle is beginning from 4 quart of trigonometric circle (3pi/2)* start_angle in degree* sweep in degree*/
void ssd1306_DrawArc(uint8_t x, uint8_t y, uint8_t radius, uint16_t start_angle, uint16_t sweep, SSD1306_COLOR color) {#define CIRCLE_APPROXIMATION_SEGMENTS 36float approx_degree;uint32_t approx_segments;uint8_t xp1,xp2;uint8_t yp1,yp2;uint32_t count = 0;uint32_t loc_sweep = 0;float rad;loc_sweep = ssd1306_NormalizeTo0_360(sweep);count = (ssd1306_NormalizeTo0_360(start_angle) * CIRCLE_APPROXIMATION_SEGMENTS) / 360;approx_segments = (loc_sweep * CIRCLE_APPROXIMATION_SEGMENTS) / 360;approx_degree = loc_sweep / (float)approx_segments;while(count < approx_segments){rad = ssd1306_DegToRad(count*approx_degree);xp1 = x + (int8_t)(sin(rad)*radius);yp1 = y + (int8_t)(cos(rad)*radius);count++;if(count != approx_segments){rad = ssd1306_DegToRad(count*approx_degree);}else{rad = ssd1306_DegToRad(loc_sweep);}xp2 = x + (int8_t)(sin(rad)*radius);yp2 = y + (int8_t)(cos(rad)*radius);ssd1306_DrawLine(xp1,yp1,xp2,yp2,color);}return;
}
//Draw circle by Bresenhem's algorithm
void ssd1306_DrawCircle(uint8_t par_x,uint8_t par_y,uint8_t par_r,SSD1306_COLOR par_color) {int32_t x = -par_r;int32_t y = 0;int32_t err = 2 - 2 * par_r;int32_t e2;if (par_x >= SSD1306_WIDTH || par_y >= SSD1306_HEIGHT) {return;}do {ssd1306_DrawPixel(par_x - x, par_y + y, par_color);ssd1306_DrawPixel(par_x + x, par_y + y, par_color);ssd1306_DrawPixel(par_x + x, par_y - y, par_color);ssd1306_DrawPixel(par_x - x, par_y - y, par_color);e2 = err;if (e2 <= y) {y++;err = err + (y * 2 + 1);if(-x == y && e2 <= x) {e2 = 0;}else{/*nothing to do*/}}else{/*nothing to do*/}if(e2 > x) {x++;err = err + (x * 2 + 1);}else{/*nothing to do*/}} while(x <= 0);return;
}//Draw rectangle
void ssd1306_DrawRectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, SSD1306_COLOR color) {ssd1306_DrawLine(x1,y1,x2,y1,color);ssd1306_DrawLine(x2,y1,x2,y2,color);ssd1306_DrawLine(x2,y2,x1,y2,color);ssd1306_DrawLine(x1,y2,x1,y1,color);return;
}void ssd1306_DrawBitmap(const uint8_t* bitmap, uint32_t size)
{uint8_t rows = size * 8 / SSD1306_WIDTH;if (rows > SSD1306_HEIGHT) {rows = SSD1306_HEIGHT;}for (uint8_t y = 0; y < rows; y++) {for (uint8_t x = 0; x < SSD1306_WIDTH; x++) {uint8_t byte = bitmap[(y * SSD1306_WIDTH / 8) + (x / 8)];uint8_t bit = byte & (0x80 >> (x % 8));ssd1306_DrawPixel(x, y, bit ? White : Black);}}
}void ssd1306_DrawRegion(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t* data, uint32_t size, uint32_t stride)
{if (x + w > SSD1306_WIDTH || y + h > SSD1306_HEIGHT || w * h == 0) {printf("%dx%d @ %d,%d out of range or invalid!\r\n", w, h, x, y);return;}w = (w <= SSD1306_WIDTH ? w : SSD1306_WIDTH);h = (h <= SSD1306_HEIGHT ? h : SSD1306_HEIGHT);stride = (stride == 0 ? w : stride);uint8_t rows = size * 8 / stride;for (uint8_t i = 0; i < rows; i++) {uint32_t base = i * stride / 8;for (uint8_t j = 0; j < w; j++) {uint32_t idx = base + (j / 8);uint8_t byte = idx < size ? data[idx] : 0;uint8_t bit  = byte & (0x80 >> (j % 8));ssd1306_DrawPixel(x + j, y + i, bit ? White : Black);}}
}void ssd1306_SetContrast(const uint8_t value) {const uint8_t kSetContrastControlRegister = 0x81;ssd1306_WriteCommand(kSetContrastControlRegister);ssd1306_WriteCommand(value);
}void ssd1306_SetDisplayOn(const uint8_t on) {uint8_t value;if (on) {value = 0xAF;   // Display onSSD1306.DisplayOn = 1;} else {value = 0xAE;   // Display offSSD1306.DisplayOn = 0;}ssd1306_WriteCommand(value);
}uint8_t ssd1306_GetDisplayOn() {return SSD1306.DisplayOn;
}
ssd1306.h
#include "ssd1306.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>  // For memcpy#include <stdio.h>
#include <unistd.h>
#include "cmsis_os2.h"
#include <stdio.h>
#include "cmsis_os2.h"
#include "ohos_run.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/i2c.h"#if defined(SSD1306_USE_I2C)#define SSD1306_I2C_IDX     0#define SSD1306_CTRL_CMD 0x00
#define SSD1306_CTRL_DATA 0x40
#define SSD1306_MASK_CONT (0x1<<7)void ssd1306_Reset(void) {/* for I2C - do nothing */
}void HAL_Delay(uint32_t ms)
{usleep(ms * 1000);
}static uint32_t ssd1306_SendData(uint8_t* data, size_t size)
{int i;i2c_cmd_handle_t cmd_handle = i2c_cmd_link_create();i2c_master_start(cmd_handle);i2c_master_write_byte(cmd_handle, SSD1306_I2C_ADDR, true);for(i = 0; i < size; i ++){i2c_master_write_byte(cmd_handle, data[i], true);}i2c_master_stop(cmd_handle);esp_err_t error = i2c_master_cmd_begin(I2C_NUM_0,cmd_handle,100/portTICK_PERIOD_MS);i2c_cmd_link_delete(cmd_handle);return error;
}static uint32_t ssd1306_WiteByte(uint8_t regAddr, uint8_t byte)
{uint8_t buffer[] = {regAddr, byte};return ssd1306_SendData(buffer, sizeof(buffer));
}// Send a byte to the command register
void ssd1306_WriteCommand(uint8_t byte) {ssd1306_WiteByte(SSD1306_CTRL_CMD, byte);
}// Send data
void ssd1306_WriteData(uint8_t* buffer, size_t buff_size) {uint8_t data[SSD1306_WIDTH * 2] = {0};for (size_t i = 0; i < buff_size; i++) {data[i*2] = SSD1306_CTRL_DATA | SSD1306_MASK_CONT;data[i*2+1] = buffer[i];}data[(buff_size - 1) * 2] = SSD1306_CTRL_DATA;ssd1306_SendData(data, sizeof(data));
}#elif defined(SSD1306_USE_SPI)void ssd1306_Reset(void) {// CS = High (not selected)HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_SET);// Reset the OLEDHAL_GPIO_WritePin(SSD1306_Reset_Port, SSD1306_Reset_Pin, GPIO_PIN_RESET);HAL_Delay(10);HAL_GPIO_WritePin(SSD1306_Reset_Port, SSD1306_Reset_Pin, GPIO_PIN_SET);HAL_Delay(10);
}// Send a byte to the command register
void ssd1306_WriteCommand(uint8_t byte) {HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_RESET); // select OLEDHAL_GPIO_WritePin(SSD1306_DC_Port, SSD1306_DC_Pin, GPIO_PIN_RESET); // commandHAL_SPI_Transmit(&SSD1306_SPI_PORT, (uint8_t *) &byte, 1, HAL_MAX_DELAY);HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_SET); // un-select OLED
}// Send data
void ssd1306_WriteData(uint8_t* buffer, size_t buff_size) {HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_RESET); // select OLEDHAL_GPIO_WritePin(SSD1306_DC_Port, SSD1306_DC_Pin, GPIO_PIN_SET); // dataHAL_SPI_Transmit(&SSD1306_SPI_PORT, buffer, buff_size, HAL_MAX_DELAY);HAL_GPIO_WritePin(SSD1306_CS_Port, SSD1306_CS_Pin, GPIO_PIN_SET); // un-select OLED
}#else
#error "You should define SSD1306_USE_SPI or SSD1306_USE_I2C macro"
#endif// Screenbuffer
static uint8_t SSD1306_Buffer[SSD1306_BUFFER_SIZE];// Screen object
static SSD1306_t SSD1306;/* Fills the Screenbuffer with values from a given buffer of a fixed length */
SSD1306_Error_t ssd1306_FillBuffer(uint8_t* buf, uint32_t len) {SSD1306_Error_t ret = SSD1306_ERR;if (len <= SSD1306_BUFFER_SIZE) {memcpy(SSD1306_Buffer,buf,len);ret = SSD1306_OK;}return ret;
}// Initialize the oled screen
void ssd1306_Init(void) {// Reset OLEDssd1306_Reset();// Wait for the screen to bootHAL_Delay(100);// Init OLEDssd1306_SetDisplayOn(0); //display offssd1306_WriteCommand(0x20); //Set Memory Addressing Modessd1306_WriteCommand(0x00); // 00b,Horizontal Addressing Mode; 01b,Vertical Addressing Mode;// 10b,Page Addressing Mode (RESET); 11b,Invalidssd1306_WriteCommand(0xB0); //Set Page Start Address for Page Addressing Mode,0-7#ifdef SSD1306_MIRROR_VERTssd1306_WriteCommand(0xC0); // Mirror vertically
#elsessd1306_WriteCommand(0xC8); //Set COM Output Scan Direction
#endifssd1306_WriteCommand(0x00); //---set low column addressssd1306_WriteCommand(0x10); //---set high column addressssd1306_WriteCommand(0x40); //--set start line address - CHECKssd1306_SetContrast(0xFF);#ifdef SSD1306_MIRROR_HORIZssd1306_WriteCommand(0xA0); // Mirror horizontally
#elsessd1306_WriteCommand(0xA1); //--set segment re-map 0 to 127 - CHECK
#endif#ifdef SSD1306_INVERSE_COLORssd1306_WriteCommand(0xA7); //--set inverse color
#elsessd1306_WriteCommand(0xA6); //--set normal color
#endif// Set multiplex ratio.
#if (SSD1306_HEIGHT == 128)// Found in the Luma Python lib for SH1106.ssd1306_WriteCommand(0xFF);
#elsessd1306_WriteCommand(0xA8); //--set multiplex ratio(1 to 64) - CHECK
#endif#if (SSD1306_HEIGHT == 32)ssd1306_WriteCommand(0x1F); //
#elif (SSD1306_HEIGHT == 64)ssd1306_WriteCommand(0x3F); //
#elif (SSD1306_HEIGHT == 128)ssd1306_WriteCommand(0x3F); // Seems to work for 128px high displays too.
#else
#error "Only 32, 64, or 128 lines of height are supported!"
#endifssd1306_WriteCommand(0xA4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM contentssd1306_WriteCommand(0xD3); //-set display offset - CHECKssd1306_WriteCommand(0x00); //-not offsetssd1306_WriteCommand(0xD5); //--set display clock divide ratio/oscillator frequencyssd1306_WriteCommand(0xF0); //--set divide ratiossd1306_WriteCommand(0xD9); //--set pre-charge periodssd1306_WriteCommand(0x11); // 0x22 by defaultssd1306_WriteCommand(0xDA); //--set com pins hardware configuration - CHECK
#if (SSD1306_HEIGHT == 32)ssd1306_WriteCommand(0x02);
#elif (SSD1306_HEIGHT == 64)ssd1306_WriteCommand(0x12);
#elif (SSD1306_HEIGHT == 128)ssd1306_WriteCommand(0x12);
#else
#error "Only 32, 64, or 128 lines of height are supported!"
#endifssd1306_WriteCommand(0xDB); //--set vcomhssd1306_WriteCommand(0x30); //0x20,0.77xVcc, 0x30,0.83xVccssd1306_WriteCommand(0x8D); //--set DC-DC enablessd1306_WriteCommand(0x14); //ssd1306_SetDisplayOn(1); //--turn on SSD1306 panel// Clear screenssd1306_Fill(Black);// Flush buffer to screenssd1306_UpdateScreen();// Set default values for screen objectSSD1306.CurrentX = 0;SSD1306.CurrentY = 0;SSD1306.Initialized = 1;
}// Fill the whole screen with the given color
void ssd1306_Fill(SSD1306_COLOR color) {/* Set memory */uint32_t i;for(i = 0; i < sizeof(SSD1306_Buffer); i++) {SSD1306_Buffer[i] = (color == Black) ? 0x00 : 0xFF;}
}// Write the screenbuffer with changed to the screen
void ssd1306_UpdateScreen(void) {// Write data to each page of RAM. Number of pages// depends on the screen height:////  * 32px   ==  4 pages//  * 64px   ==  8 pages//  * 128px  ==  16 pagesuint8_t cmd[] = {0X21,   // 设置列起始和结束地址0X00,   // 列起始地址 00X7F,   // 列终止地址 1270X22,   // 设置页起始和结束地址0X00,   // 页起始地址 00X07,   // 页终止地址 7};uint32_t count = 0;uint8_t data[sizeof(cmd)*2 + SSD1306_BUFFER_SIZE + 1] = {};// copy cmdfor (uint32_t i = 0; i < sizeof(cmd)/sizeof(cmd[0]); i++) {data[count++] = SSD1306_CTRL_CMD | SSD1306_MASK_CONT;data[count++] = cmd[i];}// copy frame datadata[count++] = SSD1306_CTRL_DATA;memcpy(&data[count], SSD1306_Buffer, sizeof(SSD1306_Buffer));count += sizeof(SSD1306_Buffer);// send to i2c busuint32_t retval = ssd1306_SendData(data, count);if (retval != 0) {printf("ssd1306_UpdateScreen send frame data filed: %d!\r\n", retval);}
}//    Draw one pixel in the screenbuffer
//    X => X Coordinate
//    Y => Y Coordinate
//    color => Pixel color
void ssd1306_DrawPixel(uint8_t x, uint8_t y, SSD1306_COLOR color) {if(x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) {// Don't write outside the bufferreturn;}// Check if pixel should be invertedif(SSD1306.Inverted) {color = (SSD1306_COLOR)!color;}// Draw in the right colorif(color == White) {SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8);} else {SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8));}
}// Draw 1 char to the screen buffer
// ch       => char om weg te schrijven
// Font     => Font waarmee we gaan schrijven
// color    => Black or White
char ssd1306_DrawChar(char ch, FontDef Font, SSD1306_COLOR color) {uint32_t i, b, j;// Check if character is validif (ch < 32 || ch > 126)return 0;// Check remaining space on current lineif (SSD1306_WIDTH < (SSD1306.CurrentX + Font.FontWidth) ||SSD1306_HEIGHT < (SSD1306.CurrentY + Font.FontHeight)){// Not enough space on current linereturn 0;}// Use the font to writefor(i = 0; i < Font.FontHeight; i++) {b = Font.data[(ch - 32) * Font.FontHeight + i];for(j = 0; j < Font.FontWidth; j++) {if((b << j) & 0x8000)  {ssd1306_DrawPixel(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR) color);} else {ssd1306_DrawPixel(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR)!color);}}}// The current space is now takenSSD1306.CurrentX += Font.FontWidth;// Return written char for validationreturn ch;
}// Write full string to screenbuffer
char ssd1306_DrawString(char* str, FontDef Font, SSD1306_COLOR color) {// Write until null-bytewhile (*str) {if (ssd1306_DrawChar(*str, Font, color) != *str) {// Char could not be writtenreturn *str;}// Next charstr++;}// Everything okreturn *str;
}// Position the cursor
void ssd1306_SetCursor(uint8_t x, uint8_t y) {SSD1306.CurrentX = x;SSD1306.CurrentY = y;
}// Draw line by Bresenhem's algorithm
void ssd1306_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, SSD1306_COLOR color) {int32_t deltaX = abs(x2 - x1);int32_t deltaY = abs(y2 - y1);int32_t signX = ((x1 < x2) ? 1 : -1);int32_t signY = ((y1 < y2) ? 1 : -1);int32_t error = deltaX - deltaY;int32_t error2;ssd1306_DrawPixel(x2, y2, color);while((x1 != x2) || (y1 != y2)){ssd1306_DrawPixel(x1, y1, color);error2 = error * 2;if(error2 > -deltaY){error -= deltaY;x1 += signX;}else{/*nothing to do*/}if(error2 < deltaX){error += deltaX;y1 += signY;}else{/*nothing to do*/}}return;
}
//Draw polyline
void ssd1306_DrawPolyline(const SSD1306_VERTEX *par_vertex, uint16_t par_size, SSD1306_COLOR color) {uint16_t i;if(par_vertex != 0){for(i = 1; i < par_size; i++){ssd1306_DrawLine(par_vertex[i - 1].x, par_vertex[i - 1].y, par_vertex[i].x, par_vertex[i].y, color);}}else{/*nothing to do*/}return;
}
/*Convert Degrees to Radians*/
static float ssd1306_DegToRad(float par_deg) {return par_deg * 3.14 / 180.0;
}
/*Normalize degree to [0;360]*/
static uint16_t ssd1306_NormalizeTo0_360(uint16_t par_deg) {uint16_t loc_angle;if(par_deg <= 360){loc_angle = par_deg;}else{loc_angle = par_deg % 360;loc_angle = ((par_deg != 0)?par_deg:360);}return loc_angle;
}
/*DrawArc. Draw angle is beginning from 4 quart of trigonometric circle (3pi/2)* start_angle in degree* sweep in degree*/
void ssd1306_DrawArc(uint8_t x, uint8_t y, uint8_t radius, uint16_t start_angle, uint16_t sweep, SSD1306_COLOR color) {#define CIRCLE_APPROXIMATION_SEGMENTS 36float approx_degree;uint32_t approx_segments;uint8_t xp1,xp2;uint8_t yp1,yp2;uint32_t count = 0;uint32_t loc_sweep = 0;float rad;loc_sweep = ssd1306_NormalizeTo0_360(sweep);count = (ssd1306_NormalizeTo0_360(start_angle) * CIRCLE_APPROXIMATION_SEGMENTS) / 360;approx_segments = (loc_sweep * CIRCLE_APPROXIMATION_SEGMENTS) / 360;approx_degree = loc_sweep / (float)approx_segments;while(count < approx_segments){rad = ssd1306_DegToRad(count*approx_degree);xp1 = x + (int8_t)(sin(rad)*radius);yp1 = y + (int8_t)(cos(rad)*radius);count++;if(count != approx_segments){rad = ssd1306_DegToRad(count*approx_degree);}else{rad = ssd1306_DegToRad(loc_sweep);}xp2 = x + (int8_t)(sin(rad)*radius);yp2 = y + (int8_t)(cos(rad)*radius);ssd1306_DrawLine(xp1,yp1,xp2,yp2,color);}return;
}
//Draw circle by Bresenhem's algorithm
void ssd1306_DrawCircle(uint8_t par_x,uint8_t par_y,uint8_t par_r,SSD1306_COLOR par_color) {int32_t x = -par_r;int32_t y = 0;int32_t err = 2 - 2 * par_r;int32_t e2;if (par_x >= SSD1306_WIDTH || par_y >= SSD1306_HEIGHT) {return;}do {ssd1306_DrawPixel(par_x - x, par_y + y, par_color);ssd1306_DrawPixel(par_x + x, par_y + y, par_color);ssd1306_DrawPixel(par_x + x, par_y - y, par_color);ssd1306_DrawPixel(par_x - x, par_y - y, par_color);e2 = err;if (e2 <= y) {y++;err = err + (y * 2 + 1);if(-x == y && e2 <= x) {e2 = 0;}else{/*nothing to do*/}}else{/*nothing to do*/}if(e2 > x) {x++;err = err + (x * 2 + 1);}else{/*nothing to do*/}} while(x <= 0);return;
}//Draw rectangle
void ssd1306_DrawRectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, SSD1306_COLOR color) {ssd1306_DrawLine(x1,y1,x2,y1,color);ssd1306_DrawLine(x2,y1,x2,y2,color);ssd1306_DrawLine(x2,y2,x1,y2,color);ssd1306_DrawLine(x1,y2,x1,y1,color);return;
}void ssd1306_DrawBitmap(const uint8_t* bitmap, uint32_t size)
{uint8_t rows = size * 8 / SSD1306_WIDTH;if (rows > SSD1306_HEIGHT) {rows = SSD1306_HEIGHT;}for (uint8_t y = 0; y < rows; y++) {for (uint8_t x = 0; x < SSD1306_WIDTH; x++) {uint8_t byte = bitmap[(y * SSD1306_WIDTH / 8) + (x / 8)];uint8_t bit = byte & (0x80 >> (x % 8));ssd1306_DrawPixel(x, y, bit ? White : Black);}}
}void ssd1306_DrawRegion(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t* data, uint32_t size, uint32_t stride)
{if (x + w > SSD1306_WIDTH || y + h > SSD1306_HEIGHT || w * h == 0) {printf("%dx%d @ %d,%d out of range or invalid!\r\n", w, h, x, y);return;}w = (w <= SSD1306_WIDTH ? w : SSD1306_WIDTH);h = (h <= SSD1306_HEIGHT ? h : SSD1306_HEIGHT);stride = (stride == 0 ? w : stride);uint8_t rows = size * 8 / stride;for (uint8_t i = 0; i < rows; i++) {uint32_t base = i * stride / 8;for (uint8_t j = 0; j < w; j++) {uint32_t idx = base + (j / 8);uint8_t byte = idx < size ? data[idx] : 0;uint8_t bit  = byte & (0x80 >> (j % 8));ssd1306_DrawPixel(x + j, y + i, bit ? White : Black);}}
}void ssd1306_SetContrast(const uint8_t value) {const uint8_t kSetContrastControlRegister = 0x81;ssd1306_WriteCommand(kSetContrastControlRegister);ssd1306_WriteCommand(value);
}void ssd1306_SetDisplayOn(const uint8_t on) {uint8_t value;if (on) {value = 0xAF;   // Display onSSD1306.DisplayOn = 1;} else {value = 0xAE;   // Display offSSD1306.DisplayOn = 0;}ssd1306_WriteCommand(value);
}uint8_t ssd1306_GetDisplayOn() {return SSD1306.DisplayOn;
}

ssd1306_demo.c
/** Copyright (c) 2020, HiHope Community.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions are met:** 1. Redistributions of source code must retain the above copyright notice, this*    list of conditions and the following disclaimer.** 2. Redistributions in binary form must reproduce the above copyright notice,*    this list of conditions and the following disclaimer in the documentation*    and/or other materials provided with the distribution.** 3. Neither the name of the copyright holder nor the names of its*    contributors may be used to endorse or promote products derived from*    this software without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/#include <stdio.h>
#include <unistd.h>#include "ohos_init.h"
#include "cmsis_os2.h"
#include <stdio.h>
#include "cmsis_os2.h"
#include "ohos_run.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/i2c.h"#include "ssd1306.h"
#include "ssd1306_tests.h"#define OLED_I2C_BAUDRATE 400*1000void TestGetTick(void)
{}/*** 汉字字模在线: https://www.23bei.com/tool-223.html* 数据排列:从左到右从上到下* 取模方式:横向8位左高位
**/
void TestDrawChinese1(void)
{const uint32_t W = 16, H = 16;uint8_t fonts[][32] = {/* [字库]:[HZK1616宋体] [数据排列]:从左到右从上到下 [取模方式]:横向8点左高位 [正负反色]:否 [去掉重复后]共6个字符[总字符库]:" 鸿蒙实验箱"*/{/*-- ID:0,字符:" ",ASCII编码:A1A1,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,},{/*-- ID:1,字符:"鸿",ASCII编码:BAE8,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/0x40,0x20,0x30,0x48,0x10,0xFC,0x02,0x88,0x9F,0xA8,0x64,0x88,0x24,0xA8,0x04,0x90,0x14,0x84,0x14,0xFE,0xE7,0x04,0x3C,0x24,0x29,0xF4,0x20,0x04,0x20,0x14,0x20,0x08,},{/*-- ID:2,字符:"蒙",ASCII编码:C3C9,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/0x04,0x48,0x7F,0xFC,0x04,0x40,0x7F,0xFE,0x40,0x02,0x8F,0xE4,0x00,0x00,0x7F,0xFC,0x06,0x10,0x3B,0x30,0x05,0xC0,0x1A,0xA0,0x64,0x90,0x18,0x8E,0x62,0x84,0x01,0x00,},{/*-- ID:3,字符:"实",ASCII编码:CAB5,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/0x02,0x00,0x01,0x00,0x7F,0xFE,0x48,0x02,0x86,0x84,0x02,0x80,0x10,0x80,0x0C,0x80,0x04,0x84,0xFF,0xFE,0x01,0x00,0x01,0x40,0x02,0x20,0x04,0x10,0x18,0x0C,0x60,0x04,},{/*-- ID:4,字符:"验",ASCII编码:D1E9,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/0x08,0x40,0xFC,0x40,0x08,0xA0,0x48,0xA0,0x49,0x10,0x4A,0x0E,0x4D,0xF4,0x48,0x00,0x7C,0x48,0x06,0x48,0x05,0x48,0x1D,0x50,0xE5,0x10,0x44,0x24,0x17,0xFE,0x08,0x00,},{/*-- ID:5,字符:"箱",ASCII编码:CFE4,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/0x12,0x44,0x1F,0x7E,0x28,0xA0,0x45,0x10,0x08,0x04,0x08,0xFE,0xFE,0x84,0x08,0x84,0x18,0xFC,0x1C,0x84,0x2A,0x84,0x28,0xFC,0x48,0x84,0x88,0x84,0x08,0xFC,0x08,0x84}};ssd1306_Fill(Black);for (size_t i = 0; i < sizeof(fonts)/sizeof(fonts[0]); i++) {ssd1306_DrawRegion(i * W, 18, W, H, fonts[i], sizeof(fonts[0]), W);}ssd1306_UpdateScreen();sleep(1);
}void TestDrawChinese2(void)
{const uint32_t W = 12, H = 12, S = 16;/* [字库]:[HZK1212宋体] [数据排列]:从左到右从上到下 [取模方式]:横向8点左高位 [正负反色]:否 [去掉重复后]共9个字符[总字符库]:" -OLED屏案例"*/uint8_t fonts[][24] = {{/*-- ID:0,字符:" ",ASCII编码:A1A1,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,},{/*-- ID:1,字符:"-",ASCII编码:A3AD,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,},{/*-- ID:2,字符:"O",ASCII编码:A3CF,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/0x00,0x00,0x1C,0x00,0x36,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x36,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,},{/*-- ID:3,字符:"L",ASCII编码:A3CC,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/0x00,0x00,0x78,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x31,0x00,0x33,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,},{/*-- ID:4,字符:"E",ASCII编码:A3C5,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/0x00,0x00,0x7F,0x00,0x33,0x00,0x31,0x00,0x34,0x00,0x3C,0x00,0x34,0x00,0x31,0x00,0x33,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,},{/*-- ID:5,字符:"D",ASCII编码:A3C4,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/0x00,0x00,0x7C,0x00,0x36,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x36,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,},{/*-- ID:6,字符:"屏",ASCII编码:C6C1,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/0x7F,0xE0,0x40,0x20,0x7F,0xE0,0x48,0x80,0x45,0x00,0x5F,0xE0,0x44,0x80,0x7F,0xF0,0x44,0x80,0x44,0x80,0x48,0x80,0x90,0x80,},{/*-- ID:7,字符:"案",ASCII编码:B0B8,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/0x04,0x00,0x7F,0xF0,0xC8,0x20,0x7F,0xF0,0x11,0x00,0x0E,0x00,0x71,0xC0,0x04,0x00,0xFF,0xF0,0x15,0x00,0x24,0xC0,0xC4,0x30,},{/*-- ID:8,字符:"例",ASCII编码:C0FD,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/0x10,0x10,0x1F,0x90,0x24,0x50,0x27,0x50,0x69,0x50,0xA9,0x50,0x35,0x50,0x22,0x50,0x22,0x10,0x24,0x10,0x28,0x50,0x30,0x20}};ssd1306_Fill(Black);for (size_t i = 0; i < sizeof(fonts)/sizeof(fonts[0]); i++) {ssd1306_DrawRegion(i * H, 18, W, H, fonts[i], sizeof(fonts[0]), S);         //y= 18 居中显示}ssd1306_UpdateScreen();sleep(1);
}void Ssd1306TestTask(void* arg)
{(void) arg;i2c_config_t i2c_initer = {.clk_flags = 0,                         // 选择默认时钟源.master.clk_speed = 50000,             // 指定速率为100Kbit,最大可以为400Kbit.mode = I2C_MODE_MASTER,                // 主机模式.scl_io_num = 17,                        // 指定SCL的GPIO口.scl_pullup_en = true,                  // SCL接上拉电阻.sda_io_num = 16,                        // 指定SDA的GPIO口.sda_pullup_en = true,                  // SDA接上拉电阻};if(i2c_param_config(I2C_NUM_0, &i2c_initer) == ESP_OK){printf("i2c parm config success\r\n");}else{printf("config fail\r\n");}if(i2c_driver_install(I2C_NUM_0,I2C_MODE_MASTER,0,0,0) == ESP_OK ){printf("i2c driver install success\r\n");}else{printf("driver fail\r\n");}//WatchDogDisable();usleep(20*1000);ssd1306_Init();ssd1306_Fill(Black);ssd1306_SetCursor(0, 0);while (1) {TestDrawChinese1();ssd1306_UpdateScreen();usleep(10000);TestDrawChinese2();ssd1306_UpdateScreen();usleep(10000);}
}void Ssd1306TestDemo(void)
{osThreadAttr_t attr;attr.name = "Ssd1306Task";attr.attr_bits = 0U;attr.cb_mem = NULL;attr.cb_size = 0U;attr.stack_mem = NULL;attr.stack_size = 10240;attr.priority = osPriorityNormal;if (osThreadNew(Ssd1306TestTask, NULL, &attr) == NULL) {printf("[Ssd1306TestDemo] Falied to create Ssd1306TestTask!\n");}
}
OHOS_APP_RUN(Ssd1306TestDemo);

代码分析:

OLED屏幕分为页寻址模式,水平地址模式,垂直地址模式。

这里用的是页寻址模式,页寻址模式如下图,把整个OLED屏幕分成了八页,说白了就是把OLED的屏幕把宽平均分成了八份。比如想要在第0列第三行的开头显示一个点那就是按位来配置,0000 0100(0x08)。

通过I2C时序与SSD1306显存进行通信,把取好的字模数组中的每一项取出并点亮对应的像素点即可

实验现象

按下ESP32开发板上的EN键,即可观察到实验现象:

OLED屏显示汉字

相关文章:

ESP32移植Openharmony外设篇(3)OLED屏

模块简介 产品介绍 OLED (Organic Light-Emitting Diode)&#xff1a;有机发光二极管又称为有机电激光显示&#xff0c;OLED显示技术具有自发光的特性&#xff0c;采用薄的有机材料涂层和玻璃基板&#xff0c;当有电流通过时&#xff0c;这些有机材料就会发光&#xff0c;而且…...

人工智能:未来生活与工作的变革力量

人工智能&#xff08;AI&#xff09;作为21世纪最具变革性的技术之一&#xff0c;正以前所未有的速度改变着我们的生活和工作方式。从医疗行业的突破性进展到企业运营的智能化&#xff0c;以及日常生活中各种智能产品的普及&#xff0c;人工智能正在成为现代社会不可或缺的一部…...

AI自动生成PPT哪个软件好?智能生成PPT不再熬夜做课件

大概这世上&#xff0c;都是职场牛马对“PPT”这三个字母的头痛反应最大吧&#xff01; 是的&#xff0c;就连各个年级段的老师也是很头痛——愁着怎样能在排版整齐的情况下&#xff0c;将必考知识点都呈现在PPT每一张幻灯片页面里...... 近期打听到用人工智能生成ppt课件&am…...

C# OOP面试题精选 面向新手/SOLID原则/设计模式++ 长期更新

1.整理目的 相当于0.1版本&#xff0c;旨在学习/提升/复习 关于面向对象模块的知识 目前&#xff0c;记录了一些比较容易混淆或者突然想不起的冷门内容 还有一些个人经过实战后总结的内容&#xff0c;其中还指明了很多实例和分析链接以更加方便地复习 【金山文档 | WPS云文…...

安全见闻(2)——开阔眼界,不做井底之蛙

内容预览 ≧∀≦ゞ 安全见闻二&#xff1a;Web程序构成与潜在漏洞声明导语前端语言及潜在漏洞前端语言前端框架与代码库代码库的概念和用途流行的JavaScript框架常见的代码库 前端潜在漏洞 后端语言及潜在漏洞常见后端语言协议问题后端潜在漏洞 数据库及潜在漏洞数据库分类数据…...

ProtoBuf 的含义和安装

ProtoBuf 是什么 Protocol Buffers 是 Google 的⼀种语⾔⽆关、平台⽆关、可扩展的序列化结构数据的⽅法&#xff0c;它可⽤ 于&#xff08;数据&#xff09;通信协议、数据存储等。 Protocol Buffers 类⽐于、 XML&#xff0c;是⼀种灵活&#xff0c;⾼效&#xff0c;⾃动化机…...

C++位操作实战:掩码、提取与组装

在C编程中&#xff0c;位操作是一项基础且强大的技术&#xff0c;它允许程序员在二进制级别上直接操作数据。这种能力对于性能优化、内存节省以及底层硬件控制至关重要。本文将深入探讨C中的掩码操作、字节提取与组装&#xff0c;并通过实例展示这些技术的实际应用。 一、位运算…...

PVE虚拟机强制重启

在Proxmox VE (PVE) 中&#xff0c;强制重启虚拟机的方法有几种&#xff0c;取决于具体场景和虚拟机的状态。以下是常用的两种方法&#xff1a; 1. 使用PVE Web UI强制重启虚拟机 如果虚拟机无法正常关闭或重启&#xff0c;可以使用PVE Web界面中的强制关机/重启选项&#xf…...

Adobe Acrobat DC 打印PDF文件,没有打印出注释的解决方法

adobe acrobat在打印的时候&#xff0c;打印不出来注释内容&#xff08;之前一直可以&#xff0c;突然就不行&#xff09;&#xff0c;升级版本、嵌入字体等等都试过&#xff0c;也在Google找了半天和问了GPT也么找着办法。 无奈之下&#xff0c;自己通过印前检查&#xff0c;…...

主机名学习

1.主机名 定义&#xff1a;主机名是一个人类可读的标识符&#xff0c;通常由字母、数字和连接符组成&#xff0c;用于标识网络中的设备。主机名可以是局部的&#xff08;例如局域网中的设备名&#xff09;或者全局的&#xff08;通过 DNS 解析成 IP 地址&#xff09;。 解析&…...

SpringBoot循环依赖

在Spring Boot&#xff08;以及Spring框架&#xff09;中&#xff0c;循环依赖是指两个或多个Bean互相依赖&#xff0c;导致Spring在创建这些Bean时无法正常进行依赖注入。例如&#xff0c;假设有两个类A和B&#xff0c;A依赖于B&#xff0c;而B又依赖于A。在这种情况下&#x…...

一道面试题:为什么要使用Docker?

先来笼统地看一下 1、环境一致性 众所周知&#xff0c;开发过程中一个常见的问题是环境一致性问题&#xff0c;由于开发环境&#xff0c;测试环境&#xff0c;生产环境不一致&#xff0c;导致有些bug并未在开发过程中被发现&#xff0c;而Docker的镜像提供了除内核外完整的运…...

类的创建、构造器、实例属性、实例方法

Creating Classes # Class: blueprint for creating new Objects # Object: instance of a class # Class: Human # Objects: John, Mary, Jack# 类名定义每个单词的首字母大写 class Point:# 每个方法至少有一个参数def draw(self):print("draw")# 创建Point对象 p…...

js读取.txt文件内容

方法一&#xff1a;FileReader() <input type"file" id"fileInput" /><script>const fileInput document.getElementById(fileInput)fileInput.addEventListener(change, function (e) {const file e.target.files[0]const reader new Fil…...

【AscendC算子开发】笔记2 算子高级开发和调试调优

算子调试 Tensor也可以通过特定的printf方法来打印&#xff0c;见上图。 gdb调试见上图。 为什么gdb调试无法成功&#xff0c;因为run.sh里面有两行export&#xff0c;如果直接通过.XX运行的话需要配置一下。 npu域也支持调试&#xff0c;可以使用上述的方法。 内存检测工…...

vscode连接keil-5 开发STM32 程序

Vs Code连接 keil-5 &#xff0c;开发STM32 程序 为了不使用难用的 keil-5 软件 继续使用友好的 vscode开发 步骤&#xff1a; 1 安装Keil Assitant 扩展 2 将你的 Keil-5 这个程序的路径找到复制到这里 3 下载 Mingw 下载点此处 4 下载后只解压 就行了 不安装 下载完 放在…...

数据结构深度优先搜索遍历连通图+非连通图(C语言代码+遍历+终端输入内容)

首先数据结构(C语言版第二版)的关于深度优先搜索遍历连通图的图G4如下: 使用邻接表去创建上面这个无向图&#xff0c;然后再使用书本DFS函数以及DFSTraverse函数实现深度优先搜索遍历 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #…...

信息安全工程师(55)网络安全漏洞概述

一、定义 网络安全漏洞&#xff0c;又称为脆弱性&#xff0c;是网络安全信息系统中与安全策略相冲突的缺陷&#xff0c;这种缺陷也称为安全隐患。漏洞可能导致机密性受损、完整性破坏、可用性降低、抗抵赖性缺失、可控性下降、真实性不保等问题。 二、分类 网络安全漏洞可以根据…...

member access within null pointer of type ‘ListNode‘

文章目录 前言一、空指针解引用二、访问已释放的内存三、 结构体定义问题四、错误的链表操作五、代码上下文六、示例代码七、调试建议 前言 p -> next p1; p1 p1 -> next; p p->next;runtime error: member access within null pointer of type ListNode如果出现…...

UE5蓝图中整理节点的方法

UE5蓝图中整理节点的方法 第一种&#xff1a;子图 右键选中的节点&#xff0c;出现一个面板&#xff0c;点击 Collapse Nodes 既可折叠选中的所有节点 注意&#xff1a;子图不可以被复制使用。 双击子图可以查看节点&#xff0c;若不想折叠选中的节点为子图&#xff0c;右键点…...

01,http 协议

1 &#xff0c;http 协议 &#xff1a;介绍 1 &#xff0c;http &#xff1a;是什么 Hyper Text Transfer Protocol &#xff1a;超文本传输协议 2 &#xff0c;传输内容 &#xff1a;文本 1 &#xff0c;内容 &#xff1a;      纯文本    2 &#xff0c;特殊 &#xf…...

在 typescript 中,如何封装一个 class 类来接收接口的响应数据

在 TypeScript 中&#xff0c;封装一个类来接收接口的响应数据是一个常见的需求&#xff0c;特别是在处理后端 API 响应时。这通常涉及到定义与后端 API 响应结构相匹配的接口&#xff08;或类型&#xff09;&#xff0c;并在类中创建方法来处理这些数据。以下是一个简单的示例…...

力扣周赛第420场 中等 3325.字符至少出现k次的子字符串 I

文章目录 题目介绍题解 题目介绍 题解 滑动窗口思想&#xff1a;参考 3.无重复字符的最长子串 链接 代码如下&#xff1a; class Solution {public int numberOfSubstrings(String s, int k) {int n s.length(), res 0;for(int left 0; left < n; left){// 记录窗口内…...

【Spring框架】Spring核心思想IoC以及依赖注入DI详解

目录 Spring框架前言 服务端三层开发表现层业务层持久层 Spring框架的概述Spring框架的优点Spring核心——IoC什么是IoC&#xff1f;O.o什么是耦合度&#xff1f; 创建第一个IoC程序导入必要依赖编写接口和实现类编写Spring核心配置文件测试类进行测试 Spring配置文件Bean对象的…...

Java项目-基于springboot框架的智慧外贸系统项目实战(附源码+文档)

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…...

Python程序控制结构 if语句详解

前面我们已经详细介绍了Python编程基础入门&#xff1a;从风格到数据类型再到表达式 在编程中&#xff0c;控制结构决定了代码的执行顺序。Python提供了丰富的控制结构&#xff0c;可以帮助程序根据不同条件做出不同的决策和操作。本文将深入介绍Python中常见的控制结构——包…...

【ppq install】

简介 PPQ 是 Sensetime OpenPPL 团队开源的量化部署工具&#xff0c;经过量化的神经网络往往能够在端侧加速600%~800%&#xff0c;而在目前已经支持OpenPPL, TensorRT, SNPE, NXP, Metax等多个不同平台的量化模拟与网络部署。PPQ 不仅限于提供强大而先进的量化优化算法&#x…...

3DGS相关方法conda环境配置

环境&#xff1a;ubuntu22.04&#xff0c;cuda_11.7 conda create -n 3dgs python3.8 -y conda activate 3dgs python -m pip install --upgrade pip pip uninstall torch torchvision functorch tinycudann pip install torch2.1.2cu118 torchvision0.16.2cu118 torchaudio2…...

python画图|曲线动态输出

【1】引言 前序教程中的曲线动态输出&#xff0c;其实是把曲线按照左右移动的形式输出&#xff08;波的传递形式&#xff09;。 python画图|曲线动态输出基础教程_python 动态曲线-CSDN博客 但有些时候我们更期待的是曲线不移动&#xff0c;随着自变量的增加而输出因变量&am…...

电子商务类型

常见电子商务类型及其代表性的例子&#xff1a; B2B&#xff08;Business to Business&#xff09; 定义&#xff1a;B2B 模式是指企业与企业之间的商业交易。在这种模式下&#xff0c;企业通过电子商务平台相互提供产品或服务。 特点&#xff1a; 大宗交易&#xff1a;通常…...

网站备案真实性核验委托书/腾讯企点app下载安装

这个搞得有点久&#xff0c;不过&#xff0c;还算完美解决。 主要是前端NGINX&#xff0c;后端也是NGINX。 前端的NGINX不好作相关的URL权限限制&#xff0c;因为所有的URL在CI里都要经过INDEX.PHP重定向。 并且&#xff0c;在后端NGINX作限制&#xff0c;更比前端安全。 我大约…...

政府门户网站建设总结/新产品推广方案怎么写

在论坛上经常会有人问&#xff0c;到底是使用Trie算法保存路由表还是用Hash算法。那么我首先要明白&#xff0c;你要保存多大的路由表。简单的答案如下&#xff1a;少量&#xff1a;Hash算法大量&#xff1a;Trie算法但是&#xff0c;仅仅这么回答会显得很业余&#xff0c;真的…...

上海 网站开发 外包/找精准客户的app

参考链接&#xff1a; ubuntu16.04 检测不到扩展屏幕&#xff08;解决方案&#xff09; 系统环境&#xff1a; Ubuntu 18.04.3 LTS 安装过程 使用第三方驱动修改grub引导文件测试 1、使用第三方驱动 这里需要使用第三方驱动。安装好驱动之后&#xff0c;需要重启电脑。 …...

百度怎么免费推广/杭州网站优化公司哪家好

前言 提到IT人员&#xff0c;人们的第一印象就是高薪资&#xff0c;包括转行来学Java的人绝大多数都是冲着高薪以及就业广泛来的。 前段时间跟同学聊天&#xff0c;她说她老公在一家互联网公司做Java后台开发&#xff0c;年薪四十万&#xff0c;最近在准备复习&#xff0c;想着…...

营销型网站建设方案演讲ppt/百度手机助手应用商店下载

我们最常使用的应用&#xff0c;如淘宝、京东、抖音、微信等&#xff0c;全都是使用数据库来进行数据的存储。尤其是在接入互联网网民越来越多的情况下&#xff0c;业务系统经常会面临大量的数据请求&#xff0c;在一些大促场景&#xff0c;更会面临突然间的请求量剧增&#xf…...

一站式做网站费用/市场seo是什么

大家好&#xff0c;我是为人造的智能操碎了心的智能禅师。今天带来的文章&#xff0c;来自一本书&#xff0c;叫做《人工智能简史》。文章比较长&#xff0c;禅师就不多说什么了。提醒大家今天次条由禅师的私人助理条子&#xff0c;给大家送了一份福利&#xff0c;请大家多多支…...