| 
				打卡等级:以坛为家III打卡总天数:657最近打卡:2025-10-31 08:27:32   荣誉版主 
 
	积分4469 
 | 
 
 发表于 2023-12-25 16:58:25
|
显示全部楼层 
| 本帖最后由 浦江一水 于 2023-12-27 10:12 编辑 
 有关OLED屏的驱动文件就两个:
 SSD1306.H
 SSD1306.C
 
 尽可能独立成章,减少交叉包含引用,便于移植。在工程中添加挂上就能用。
 
 几点思考与说明:
 1,由于采用内存映射显示缓存的方法,占用内存128*64=1024字节,所有显示函数实际是先对内存映射区操作,然后整体刷新屏幕(一次传输1024字节)完成显示。
 考虑合理减少传输占时,在相关操作函数的调用时,增加了mode形参,当mode=1时, 立即刷新屏幕显示, 当mode=0时,仅完成对缓存的操作, 而不立即刷新屏幕显示。
 使用时可根据需要来选择是否立即刷行, 这样可相对提高显示效率,同时也简化源码书写,避免大量显式地使用OLED_Show()之类的刷新函数。
 2,该12864单色显示屏,采用一字节表达纵向8点(下高位)像素的方法。因此,高度64点分为8页(行)显示。故在函数涉及时,文本和图形采用不同的坐标方式:
 文本显示时,X为水平坐标,0..127点(列)  Y为垂直坐标,0..7页(行)。
 图形显示时,X为水平坐标,0..127点(列)  Y为垂直坐标,0..63点(行)。
 (当然文本显示也是可以做成以点为行,即0..63行的,就是显示效率低些。本文只是Demo,仅实现简单应用。)
 3,显示屏的字库和图片数据为外挂文件。
 ASCII.H 包含 6*8点阵和8*16 点阵西文字符。可根据需要扩展。
 HZK16.H 用户需要扩展制作的16点阵小汉字库,可根据需要扩展。目前只包含“单片机”三字。然而构成方法及书写规则已体现。
 
 SSD1306.H tp 头文件 (工程包里有)
 
 //********************************************************************************
 // 文件名称: SSD1306.H 显示屏头文件
 // 基于电路: STC32G12K128 V9.62 实验箱
 // 实验功能: 驱动7针 OLED 12864 单色显示屏
 // 整理编程: 浦晓明(浦江一水) 2023-12-22
 // 端口定义: 7针排列..
 //   1.GND ------- 地线
 //   2.VCC ------- 电源 3.3-5V
 //   3.SCK -- P2.5 时钟端
 //   4.SDA -- P2.3 数据端 (MOSI)
 //   5.RES -- P2.4 复位端
 //   6.DC  -- P3.4 数据/命令切换
 //   7.CS  -- P1.1 片选端
 //********************************************************************************
 #ifndef __SSD1306_H
 #define __SSD1306_H
 
 
 #include "STC32G.H"
 #include <string.h>
 #include <stdlib.h>
 
 
 #include "STC32G_SYS.H"
 
 
 /*  OLED Pixel */
 #define WIDTH 128
 #define HEIGHT 64
 #define PAGES   8
 
 
 /*  OLED Brightness */
 #define BRIGHTNESS_MIN 1
 #define BRIGHTNESS_MAX 25
 
 
 /*  OLED Driver */
 void OLED_Init(void);                                      //初始化
 void OLED_Show(void);                                    //显示(缓存数据刷屏)
 void OLED_Disp(unsigned char On);                  //开关显示
 void OLED_CLS(void);                                      //清屏
 void OLED_Light(unsigned char Level);              //亮度设置
 void OLED_SetXY(unsigned char X,unsigned char Y);             //设定坐标
 void OLED_Point(unsigned char X,unsigned char Y,u8 mode); //画一个点
 void OLED_LineH(unsigned char X,unsigned char Y,unsigned char W,u8 mode);  //画一条水平线
 void OLED_LineV(unsigned char X,unsigned char Y,unsigned char H,u8 mode);  //画一条垂直线
 void OLED_Line( unsigned char X0, unsigned char Y0, unsigned char X1,unsigned char Y1,u8 mode);
 void OLED_Box(unsigned char X,unsigned char Y,unsigned char W,unsigned char H,u8 mode);  //画一个方框
 void OLED_Circle(unsigned char X, unsigned char Y, unsigned char r,u8 mode);                      //画一个圆
 void OLED_BMP(unsigned char X,unsigned char Y,unsigned char W,unsigned char H,unsigned char BMP[],u8 mode);
 void OLED_A08(unsigned char X,unsigned char Y,char Ch, u8 turn,u8 mode);    //OLED显示一个6*8字符
 void OLED_A16(unsigned char X,unsigned char Y,char Ch, u8 turn,u8 mode);    //OLED显示一个8*16字符
 void OLED_HZ16(unsigned char X,unsigned char Y,char *Hz, u8 turn,u8 mode); //OLED显示一个16*16汉字
 void OLED_Str5x7(unsigned char X,unsigned char Y,char *s,u8 turn,u8 mode);  //OLED显示6*8点阵字符串
 void OLED_String(unsigned char X,unsigned char Y,char *s,u8 turn,u8 mode);   //OLED显示16点阵高字符串
 
 
 #endif
 
 SSD1306.C    各函数的实现文件, 算法参考STC论坛资料及众高手坛友算法, 重新简化整理改编。
 
 //********************************************************************************
 // 文件名称: SSD1306.C
 // 基于电路: STC32G12K128 V9.62 实验箱  /  屠龙刀三 核心板
 // 实验功能: 驱动7针 OLED 12864 单色显示屏 (模拟IO方式)
 // 整理编程: 浦晓明(浦江一水) 2023-12-22
 // 端口定义: 7针排列..
 //   1.GND ------- 地线
 //   2.VCC ------- 电源 3.3-5V        / 屠龙刀三引脚定义
 //   3.SCK -- P2.5 时钟端
 //   4.SDA -- P2.3 数据端 (MOSI)
 //   5.RES -- P2.4 复位端              P2.0 复位端
 //   6.DC  -- P3.4 数据/命令切换       P2.1 数据/命令切换
 //   7.CS  -- P1.1 片选端              P2.2 片选端
 //********************************************************************************
 #include "SSD1306.H"
 #include "ASCII.H"
 #include "HZK16.H"
 
 
 //OLED屏引脚定义
 sbit OLED_SCK = P2^5;   //时钟
 sbit OLED_SDA = P2^3;   //数据
 //sbit OLED_RES = P2^4;   //复位 //for STC32G12K128 实验箱V9.62
 //sbit OLED_DC  = P3^4;   //数令
 //sbit OLED_CS  = P1^1;   //片选
 sbit OLED_RES = P2^0;   //复位   //for 屠龙刀三
 sbit OLED_DC  = P2^1;   //数令
 sbit OLED_CS  = P2^2;   //片选
 
 unsigned char xdata ShowBuf[8][128]; //OLED全局缓存
 
 //========================================================================
 // 函数名称: delayms(unsigned int ms) 毫秒级延时函数
 // 函数功能: OLED驱动用的延时
 //========================================================================
 void delayms(unsigned int ms)
 { unsigned int xdata i;
 do
 { i = MAIN_Fosc / 6000;
 while(--i);   //6T per loop 每循环6指令周期
 } while(--ms);
 }
 //========================================================================
 // 函数名称: OLED_WR_CMD(unsigned char Cmd)
 // 函数功能: OLED_写命令字
 //========================================================================
 void OLED_WR_CMD(unsigned char Cmd)
 { unsigned char i;
 OLED_SCK= 0;    //时钟低;
 OLED_DC = 0;    //写命令
 OLED_CS = 0;    //片选中
 for(i=0;i<8;i++)
 { if(Cmd&0x80)OLED_SDA=1;
 else        OLED_SDA=0;
 Cmd <<= 1;    //左移位
 OLED_SCK = 1;
 OLED_SCK = 0;
 }
 OLED_CS = 1;    //片不选
 }
 //========================================================================
 // 函数名称: OLED_WR_DAT(unsigned char Dat)
 // 函数功能: OLED_写数据字
 //========================================================================
 void OLED_WR_DAT(unsigned char Dat)
 { unsigned char i;
 OLED_SCK= 0;    //时钟低;
 OLED_DC = 1;    //写数据
 OLED_CS = 0;    //片选中
 for(i=0;i<8;i++)
 { if(Dat&0x80)OLED_SDA=1;
 else        OLED_SDA=0;
 Dat <<= 1;    //左移位
 OLED_SCK = 1;
 OLED_SCK = 0;
 }
 OLED_CS = 1;    //片不选
 }
 //========================================================================
 // 函数名称: OLED_Disp(unsigned char OnOff)
 // 函数功能: OLED开关显示 参数: 1:开 0:关
 //========================================================================
 void OLED_Disp(unsigned char On)  //开关显示
 {
 if(On==1)
 { OLED_WR_CMD(0x8D); //电荷泵使能
 OLED_WR_CMD(0x14); //开启电荷泵
 OLED_WR_CMD(0xAF); //点亮屏幕
 } else
 { OLED_WR_CMD(0x8D); //电荷泵使能
 OLED_WR_CMD(0x10); //关闭电荷泵
 OLED_WR_CMD(0xAF); //关闭屏幕
 }
 }
 //========================================================================
 // 函数名称: OLED_Init() 显示屏初始化函数
 //========================================================================
 void OLED_Init(void)
 {
 OLED_SCK = 0;                       //时钟端低
 OLED_SDA = 1; delayms(100); //数据端高
 OLED_RES = 1; delayms(200);
 OLED_RES = 0; delayms(200); //复位
 OLED_RES = 1;
 
 OLED_WR_CMD(0xAE);//--turn off oled panel
 OLED_WR_CMD(0x00);//--set low column address
 OLED_WR_CMD(0x10);//--set high column address
 OLED_WR_CMD(0x40);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
 OLED_WR_CMD(0x81);//--set contrast control register
 OLED_WR_CMD(0xCF);//--Set SEG Output Current Brightness 亮度...
 //OLED_WR_CMD(0xA0);//--Set SEG/Column Mapping     0xA0左右反转 0xA1正常
 //OLED_WR_CMD(0xC0);//--Set COM/Row Scan Direction 0xC0上下反转 0xC8正常
 OLED_WR_CMD(0xA1);//--Set SEG/Column Mapping     0xA0左右反转 0xA1正常
 OLED_WR_CMD(0xC8);//--Set COM/Row Scan Direction 0xC0上下反转 0xC8正常
 OLED_WR_CMD(0xA6);//--set normal display
 OLED_WR_CMD(0xA8);//--set multiplex ratio(1 to 64)
 OLED_WR_CMD(0x3f);//--1/64 duty
 OLED_WR_CMD(0xD3);//--set display offset        Shift Mapping RAM Counter (0x00~0x3F)
 OLED_WR_CMD(0x00);//--not ofset
 OLED_WR_CMD(0xd5);//--set display clock divide ratio/oscillator frequency
 OLED_WR_CMD(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec
 OLED_WR_CMD(0xD9);//--set pre-charge period
 OLED_WR_CMD(0xF1);//--Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
 OLED_WR_CMD(0xDA);//--set com pins hardware configuration
 OLED_WR_CMD(0x12);
 OLED_WR_CMD(0xDB);//--set vcomh
 OLED_WR_CMD(0x40);//--Set VCOM Deselect Level
 OLED_WR_CMD(0x20);//--Set Page Addressing Mode (0x00/0x01/0x02)
 OLED_WR_CMD(0x00);//
 OLED_WR_CMD(0x8D);//--set Charge Pump enable/disable
 OLED_WR_CMD(0x14);//--set(0x10) disable
 OLED_WR_CMD(0xA4);//--Disable Entire Display On (0xa4/0xa5)
 OLED_WR_CMD(0xA6);//--Disable Inverse Display On (0xa6/a7)
 OLED_WR_CMD(0xAF);
 OLED_Disp(1);     //开显示
 }
 //========================================================================
 // 函数名称: OLED_SetXY
 // 函数功能: OLED设置显示位置
 // 入口参数: X-水平坐标 0..127,Y-垂直坐标0..63
 //========================================================================
 void OLED_SetXY(unsigned char X, unsigned char Y)
 {
 OLED_WR_CMD((u8)(0xB0+Y));
 OLED_WR_CMD((u8)(((0xF0&X)>>4)|0x10));
 OLED_WR_CMD((u8)(((0x0f&X)|0x01)));
 }
 //========================================================================
 
 // 函数名称: OLED_Show
 // 函数功能: OLED刷新显示(将显示缓存的数据刷到显示屏显示出来)
 //========================================================================
 void OLED_Show(void)
 { unsigned char xdata i,n;
 for(i=0;i<8;i++)       //8行(页)循环...
 { OLED_WR_CMD((u8)(0xB0+i)); //设置行起始地址
 OLED_WR_CMD(0x00);   //设置低列起始地址
 OLED_WR_CMD(0x10);   //设置高列起始地址
 for(n=0;n<128;n++)   //每行(页)128点(列)
 OLED_WR_DAT(ShowBuf[n]); //写数据...
 }
 }
 //========================================================================
 // 函数名称: OLED_CLS 清屏函数
 // 函数功能: OLED刷新显示(将显示缓存的0数据刷到显示屏显示出来)
 //========================================================================
 void OLED_CLS(void)
 {
 memset(ShowBuf,0,128*8); //清空缓存
 OLED_Show();             //刷新显示
 }
 //========================================================================
 // 函数名称: OLED_Light(unsigned char num)
 // 函数功能: OLED亮度级设置
 //========================================================================
 void OLED_Light(unsigned char Level)
 {
 OLED_WR_CMD(0x81);  //
 OLED_WR_CMD(Level); //
 OLED_WR_CMD(0xDB);  //--set vcomh
 OLED_WR_CMD(0x20);  //Set VCOM Deselect Level
 }
 
 
 //========================================================================
 // 函数名称: OLED_Point
 // 函数功能: OLED显示一个点
 // 入口参数: X:水平点 0..127 Y:垂直点0..63
 //========================================================================
 void OLED_Point(unsigned char X,unsigned char Y,u8 mode)
 {
 ShowBuf[Y/8][X] |= 1<<(Y%8);  //垂直8点,高位在下,低位在上.
 if(mode)OLED_Show();
 }
 //========================================================================
 // 函数名称: OLED_BMP
 // 函数功能: OLED缓存写入BMP格式图片
 // 入口参数: X:水平起点 0..127 Y:垂直起点0..63  W: 宽度 H:高度 *BMP图片数组
 //========================================================================
 void OLED_BMP(unsigned char X, unsigned char Y,unsigned char W, unsigned char H,unsigned char BMP[],u8 mode)
 { unsigned int xdata num=0;
 unsigned char i,l;
 for( i=0;i<H;i++ )
 { for( l=0;l<W;l++)ShowBuf[Y+i][X+l]=BMP[num++]; }
 if(mode)OLED_Show();
 }
 //========================================================================
 
 // 画一条水平线
 //========================================================================
 void OLED_LineH(unsigned char X,unsigned char Y,unsigned char W,u8 mode)
 { unsigned char i;
 for(i=0;i<W;i++)OLED_Point((u8)(X+i),Y,0);
 if(mode)OLED_Show();
 }
 //========================================================================
 // 画一条垂直线
 //========================================================================
 void OLED_LineV(unsigned char X,unsigned char Y,unsigned char H,u8 mode)
 { unsigned char i;
 for(i=0;i<H;i++)OLED_Point(X,(u8)(Y+i),0);
 if(mode)OLED_Show();
 }
 //========================================================================
 // 函数名称: OLED_BuffShowPoint
 // 函数功能: OLED显示一条线
 // 入口参数: X0起点 Y0起点 X1终点 Y1终点
 //========================================================================
 void OLED_Line( unsigned char X0, unsigned char Y0, unsigned char X1,unsigned char Y1,u8 mode)
 { unsigned char x,y;
 if(X0>X1){ x=X0;X0=X1;X1=x; y=Y0;Y0=Y1;Y1=y; }  //为从左到右而交换坐标.
 if(X0!=X1)
 { for(x=0;x<(X1-X0);x++ )
 { if(Y1>Y0)OLED_Point((u8)(X0+x),(u8)(Y0+(u16)(Y1-Y0)*(u16)x/(u16)(X1-X0)),0);
 else     OLED_Point((u8)(X0+x),(u8)(Y0-(u16)(Y0-Y1)*(u16)x/(u16)(X1-X0)),0);
 }
 }
 else
 { if(Y0>Y1){ for(y=Y1; y<=Y0; y++ )OLED_Point(X0,y,0); }
 else     { for(y=Y0; y<=Y1; y++ )OLED_Point(X0,y,0); }
 }
 if(mode)OLED_Show();
 }
 //========================================================================
 // 画一个方框
 //========================================================================
 void OLED_Box(unsigned char X,unsigned char Y,unsigned char W,unsigned char H,u8 mode)
 {
 OLED_LineH(X,Y,W,0);   OLED_LineV(X,Y,H,0);
 OLED_LineH(X,(u8)(Y+H),W,0); OLED_LineV((u8)(X+W),Y,H,0);
 if(mode)OLED_Show();
 }
 //========================================================================
 // 函数名称: OLED_Circle
 // 函数功能: OLED显示一个圆形
 // 入口参数: X点  Y点  r:半径
 //========================================================================
 void OLED_Circle(unsigned char X,unsigned char Y,unsigned char r,u8 mode)
 { int a, b, di;
 a = 0; b = r;
 di = 3 - (r << 1);        //判断下个点位置的标志
 while (a <= b)
 { OLED_Point((u8)(X+a),(u8)(Y-b),0);  //5
 OLED_Point((u8)(X+b),(u8)(Y-a),0);  //0
 OLED_Point((u8)(X+b),(u8)(Y+a),0);  //4
 OLED_Point((u8)(X+a),(u8)(Y+b),0);  //6
 OLED_Point((u8)(X-a),(u8)(Y+b),0);  //1
 OLED_Point((u8)(X-b),(u8)(Y+a),0);  //3
 OLED_Point((u8)(X-a),(u8)(Y-b),0);  //2
 OLED_Point((u8)(X-b),(u8)(Y-a),0);  //7
 a++;
 //使用Bresenham算法画圆
 if (di < 0)di += 4 * a + 6;
 else
 { di += 10 + 4 * (a - b); b--;}
 }
 if(mode)OLED_Show();
 }
 //========================================================================
 // 函数名称: OLED_A08
 // 函数功能: OLED显示一个5*7字符
 // 入口参数: X: 水平坐标列 0..127  Y: 垂直坐标行(页) 0..7
 //     turn: 0:正显示 1:反显示  mode: 1:立即刷新显示 0:仅写缓存
 //========================================================================
 void OLED_A08(unsigned char X,unsigned char Y,char Ch,u8 turn,u8 mode)     //OLED显示一个5*7字符
 { unsigned char i;
 for( i=0;i<6;i++ )
 { if(turn==0)
 ShowBuf[Y][X+i]= ASC8[Ch-' '];
 else
 ShowBuf[Y][X+i]=~ASC8[Ch-' '];
 }
 if(mode)OLED_Show();
 }
 //========================================================================
 // 函数名称: OLED_A16
 // 函数功能: OLED显示一个8*16字符
 // 入口参数: X: 水平坐标列 0..127  Y: 垂直坐标行(页) 0..7
 //     turn: 0:正显示 1:反显示  mode: 1:立即刷新显示 0:仅写缓存
 //========================================================================
 void OLED_A16(unsigned char X,unsigned char Y,char Ch,u8 turn,u8 mode)     //OLED显示一个8*16字符
 { unsigned char i,j;
 for( j=0;j<2;j++ ){
 for( i=0;i<8;i++ )
 { if(turn==0)
 ShowBuf[Y+j][X+i]= ASC16[Ch-' '][i+j*8];
 else
 ShowBuf[Y+j][X+i]=~ASC16[Ch-' '][i+j*8];
 } }
 if(mode)OLED_Show();
 }
 //========================================================================
 // 函数名称: OLED_HZ16
 // 函数功能: OLED显示一个16*16汉字
 // 入口参数: X: 水平坐标列 0..127  Y: 垂直坐标行(页) 0..7
 //     turn: 0:正显示 1:反显示  mode: 1:立即刷新显示 0:仅写缓存
 //========================================================================
 void OLED_HZ16(unsigned char X,unsigned char Y,char *Hz,u8 turn,u8 mode)
 { unsigned char i,j;
 unsigned int m;
 //查找字库汉字表
 for(m=0;m<TotalHZ16;m++)
 { if((Hz[0]==HZ16M[m][0])&&(Hz[1]==HZ16M[m][1]))break; }
 if(m>=TotalHZ16)return; //查无汉字...返回.
 //显示汉字...传输点阵字模32字节
 for( j=0;j<2;j++ ){
 for( i=0;i<16;i++ )
 { if(turn==0)
 ShowBuf[Y+j][X+i]= HZ16[m][i+j*16];
 else
 ShowBuf[Y+j][X+i]=~HZ16[m][i+j*16];
 } }
 if(mode)OLED_Show();
 }
 //========================================================================
 // 函数名称: OLED_String
 // 函数功能: OLED显示5*7点阵字符串
 // 入口参数: X: 水平坐标列 0..127  Y: 垂直坐标行(页) 0..7
 //     turn: 0:正显示 1:反显示  mode: 1:立即刷新显示 0:仅写缓存
 //========================================================================
 void OLED_Str5x7(unsigned char X,unsigned char Y,char *s,u8 turn,u8 mode)
 {
 while(*s != '\0') //字符串不为空,循环
 { OLED_A08(X,Y,*s,turn,0); X+=6; s++; }
 if(mode)OLED_Show();
 }
 //========================================================================
 // 函数名称: OLED_String
 // 函数功能: OLED显示字符串。
 // 入口参数: X: 水平坐标列 0..127  Y: 垂直坐标行(页) 0..7
 //     turn: 0:正显示 1:反显示  mode: 1:立即刷新显示 0:仅写缓存
 //========================================================================
 void OLED_String(unsigned char X,unsigned char Y,char *s,u8 turn,u8 mode)
 { char hz[2];
 while(*s != '\0')       //字符串不为空,循环
 { if ((unsigned char)*s < 0x80)     //根据输入数据的大小判断是字符还是汉字,
 { OLED_A16(X,Y,*s,turn,0);
 X+=8; s++;
 }
 else
 { hz[0] = *s ; hz[1] = *(s+1) ;
 OLED_HZ16(X,Y,hz,turn,0);
 X+=16; s+=2;
 }
 if(X>127){ X=0; Y+=2; }   //行
 }
 if(mode)OLED_Show();
 
 }
 
 //===== SSD1306.C END ===================================================================
 
 
 
 
 
 | 
 |