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):有机发光二极管又称为有机电激光显示,OLED显示技术具有自发光的特性,采用薄的有机材料涂层和玻璃基板,当有电流通过时,这些有机材料就会发光,而且…...
人工智能:未来生活与工作的变革力量
人工智能(AI)作为21世纪最具变革性的技术之一,正以前所未有的速度改变着我们的生活和工作方式。从医疗行业的突破性进展到企业运营的智能化,以及日常生活中各种智能产品的普及,人工智能正在成为现代社会不可或缺的一部…...
AI自动生成PPT哪个软件好?智能生成PPT不再熬夜做课件
大概这世上,都是职场牛马对“PPT”这三个字母的头痛反应最大吧! 是的,就连各个年级段的老师也是很头痛——愁着怎样能在排版整齐的情况下,将必考知识点都呈现在PPT每一张幻灯片页面里...... 近期打听到用人工智能生成ppt课件&am…...
C# OOP面试题精选 面向新手/SOLID原则/设计模式++ 长期更新
1.整理目的 相当于0.1版本,旨在学习/提升/复习 关于面向对象模块的知识 目前,记录了一些比较容易混淆或者突然想不起的冷门内容 还有一些个人经过实战后总结的内容,其中还指明了很多实例和分析链接以更加方便地复习 【金山文档 | WPS云文…...
安全见闻(2)——开阔眼界,不做井底之蛙
内容预览 ≧∀≦ゞ 安全见闻二:Web程序构成与潜在漏洞声明导语前端语言及潜在漏洞前端语言前端框架与代码库代码库的概念和用途流行的JavaScript框架常见的代码库 前端潜在漏洞 后端语言及潜在漏洞常见后端语言协议问题后端潜在漏洞 数据库及潜在漏洞数据库分类数据…...
ProtoBuf 的含义和安装
ProtoBuf 是什么 Protocol Buffers 是 Google 的⼀种语⾔⽆关、平台⽆关、可扩展的序列化结构数据的⽅法,它可⽤ 于(数据)通信协议、数据存储等。 Protocol Buffers 类⽐于、 XML,是⼀种灵活,⾼效,⾃动化机…...
C++位操作实战:掩码、提取与组装
在C编程中,位操作是一项基础且强大的技术,它允许程序员在二进制级别上直接操作数据。这种能力对于性能优化、内存节省以及底层硬件控制至关重要。本文将深入探讨C中的掩码操作、字节提取与组装,并通过实例展示这些技术的实际应用。 一、位运算…...
PVE虚拟机强制重启
在Proxmox VE (PVE) 中,强制重启虚拟机的方法有几种,取决于具体场景和虚拟机的状态。以下是常用的两种方法: 1. 使用PVE Web UI强制重启虚拟机 如果虚拟机无法正常关闭或重启,可以使用PVE Web界面中的强制关机/重启选项…...
Adobe Acrobat DC 打印PDF文件,没有打印出注释的解决方法
adobe acrobat在打印的时候,打印不出来注释内容(之前一直可以,突然就不行),升级版本、嵌入字体等等都试过,也在Google找了半天和问了GPT也么找着办法。 无奈之下,自己通过印前检查,…...
主机名学习
1.主机名 定义:主机名是一个人类可读的标识符,通常由字母、数字和连接符组成,用于标识网络中的设备。主机名可以是局部的(例如局域网中的设备名)或者全局的(通过 DNS 解析成 IP 地址)。 解析&…...
SpringBoot循环依赖
在Spring Boot(以及Spring框架)中,循环依赖是指两个或多个Bean互相依赖,导致Spring在创建这些Bean时无法正常进行依赖注入。例如,假设有两个类A和B,A依赖于B,而B又依赖于A。在这种情况下&#x…...
一道面试题:为什么要使用Docker?
先来笼统地看一下 1、环境一致性 众所周知,开发过程中一个常见的问题是环境一致性问题,由于开发环境,测试环境,生产环境不一致,导致有些bug并未在开发过程中被发现,而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文件内容
方法一: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方法来打印,见上图。 gdb调试见上图。 为什么gdb调试无法成功,因为run.sh里面有两行export,如果直接通过.XX运行的话需要配置一下。 npu域也支持调试,可以使用上述的方法。 内存检测工…...
vscode连接keil-5 开发STM32 程序
Vs Code连接 keil-5 ,开发STM32 程序 为了不使用难用的 keil-5 软件 继续使用友好的 vscode开发 步骤: 1 安装Keil Assitant 扩展 2 将你的 Keil-5 这个程序的路径找到复制到这里 3 下载 Mingw 下载点此处 4 下载后只解压 就行了 不安装 下载完 放在…...
数据结构深度优先搜索遍历连通图+非连通图(C语言代码+遍历+终端输入内容)
首先数据结构(C语言版第二版)的关于深度优先搜索遍历连通图的图G4如下: 使用邻接表去创建上面这个无向图,然后再使用书本DFS函数以及DFSTraverse函数实现深度优先搜索遍历 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #…...
信息安全工程师(55)网络安全漏洞概述
一、定义 网络安全漏洞,又称为脆弱性,是网络安全信息系统中与安全策略相冲突的缺陷,这种缺陷也称为安全隐患。漏洞可能导致机密性受损、完整性破坏、可用性降低、抗抵赖性缺失、可控性下降、真实性不保等问题。 二、分类 网络安全漏洞可以根据…...
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蓝图中整理节点的方法 第一种:子图 右键选中的节点,出现一个面板,点击 Collapse Nodes 既可折叠选中的所有节点 注意:子图不可以被复制使用。 双击子图可以查看节点,若不想折叠选中的节点为子图,右键点…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
