请注意以下代码只适合硬件SPI
硬件SPISTM32cubeMX的配置方法!
其余的时钟等操作与其他一致
重点讲SPI的配置
SPI1/2都行,选一个,因为oled只能接受数据,不能发送,所以选只做主机,(选双向也可以),下面选择硬件输出型号 连接oled的cs 接口,用于控制输出命令还是数据。
其他选项默认
然后博主oled为中景圆的7针SPI接口
所以还要再选两个IO口做res 和 dc
使用user label
就可以在main.h中看见
以下为oled.h头文件内容
#include "main.h"
// 常量定义
#define u8 unsigned char // 将unsigned char 定义简写
#define MAX_LEN 128 // 列最多像素点
#define OLED_CMD 1 // 写命令
#define OLED_DATA 0 // 写数据
// 定义字体枚举,值为字体的高度
enum Font_Size {
SMALL = 6 , MEDIA = 12 , BIG = 16};
// 定义数字枚举,值为数字的高度
enum Num_Size {
SMALL_NUM = 24 , MEDIA_NUM = 32 , BIG_NUM = 40};
// 定义汉字的大小枚举,值为字体的高度
enum Zh_Size {
ZH16 = 16 , ZH32 = 32};
// 滚动方向枚举
enum DIRECTION {
LEFT = 0x27 , RIGHT = 0x26,UP=0x29,DAWN=0x2a};
// 端口高低操纵定义
#define OLED_RST_H HAL_GPIO_WritePin(OLED_RST_GPIO_Port,OLED_RST_Pin,GPIO_PIN_SET) // 复位脚高
#define OLED_RST_L HAL_GPIO_WritePin(OLED_RST_GPIO_Port,OLED_RST_Pin,GPIO_PIN_RESET) // 复位脚低
#define OLED_DC_H HAL_GPIO_WritePin(OLED_DC_GPIO_Port,OLED_DC_Pin,GPIO_PIN_SET) // 控制脚高
#define OLED_DC_L HAL_GPIO_WritePin(OLED_DC_GPIO_Port,OLED_DC_Pin,GPIO_PIN_RESET) // 控制脚低
// 函数定义
// OLED的基础操作函数
void OLED_Write_Byte(u8 data , u8 cmd);
void OLED_Init(void);
void OLED_On(void);
void OLED_Off(void);
void OLCD_Set_Pos(u8 x, u8 y);
void OLED_Ascii_Test(void);
// OLED的显示操作函数
void OLED_Clear(void);
void OLED_Show_Char(u8 x, u8 y , u8 chr , enum Font_Size size);
void OLED_Show_String(u8 x, u8 y , char *chr , enum Font_Size size);
void OLED_Show_Num(u8 x, u8 y , u8 num , enum Num_Size size);
void OLED_Show_Chinese(u8 x, u8 y , u8 index , enum Zh_Size size , u8 reverse);
void OLED_Show_Icon(u8 x, u8 y , u8 index);
void OLED_horizontalroll_Display(u8 start,u8 end,enum DIRECTION dirct);
void OLED_verticalroll_Display(void);
void OLED_obliqueroll_Display(u8 start,u8 end,enum DIRECTION dirct);
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]);
void OLED_DrawDot(u8 x,u8 y,u8 t);
void OLED_Circle(uint8_t x,uint8_t y,uint8_t r);
void OLED_DrawSquare(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2);
void OLED_DrawLine(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2);
void OLED_fill_Circle(u8 x0,u8 y0,u8 r,u8 dot);
#endif
以下为oled.c文件中的内容
写命令或者数据的函数和初始化的函数
void OLED_Write_Byte( u8 data , u8 cmd )
{
// 这里直接使用三目运算符,不使用if/else写法,看起来简洁些
cmd ? OLED_DC_L : OLED_DC_H ; // 写命令DC输出低,写数据DC输出高
// 硬件SPI数据传输
HAL_SPI_Transmit(&hspi2,&data,sizeof(data),1000); // SPI写数据或者命令
OLED_DC_H; // 控制脚拉高,置成写数据状态
}
/*******************
** OLED初始化
********************/
void OLED_Init(void)
{
// 制造一个先低后高的电平变换,达到复位的效果
OLED_RST_L;
HAL_Delay(500);
OLED_RST_H;
OLED_Write_Byte(0xAE,OLED_CMD);//--turn off oled panel
OLED_Write_Byte(0x02,OLED_CMD);//---set low column address
OLED_Write_Byte(0x10,OLED_CMD);//---set high column address
OLED_Write_Byte(0x40,OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
OLED_Write_Byte(0x81,OLED_CMD);//--set contrast control register
OLED_Write_Byte(0xff,OLED_CMD);// Set SEG Output Current Brightness
OLED_Write_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常
OLED_Write_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
OLED_Write_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_Write_Byte(0x3f,OLED_CMD);//--1/64 duty
OLED_Write_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
OLED_Write_Byte(0x00,OLED_CMD);//-not offset
OLED_Write_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
OLED_Write_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
OLED_Write_Byte(0xD9,OLED_CMD);//--set pre-charge period
OLED_Write_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_Write_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
OLED_Write_Byte(0x12,OLED_CMD);
OLED_Write_Byte(0xDB,OLED_CMD);//--set vcomh
OLED_Write_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
OLED_Write_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
OLED_Write_Byte(0x02,OLED_CMD);//
OLED_Write_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
OLED_Write_Byte(0x14,OLED_CMD);//--set(0x10) disable
OLED_Write_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
OLED_Write_Byte(0xA6,OLED_CMD);// 开启正常显示 (0xA6 = 正常 / 0xA7 = 反显)
OLED_Write_Byte(0xAF,OLED_CMD);//--turn on oled panel
OLED_Clear(); // 清屏
}
接下来使一些开启,关闭,清屏,设置光标的位置的函数
void OLED_On(void)
{
OLED_Write_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_Write_Byte(0X14,OLED_CMD); //DCDC ON
OLED_Write_Byte(0XAF,OLED_CMD); //DISPLAY ON
}
/******************
** 关闭OLED显示
*******************/
void OLED_Off(void)
{
OLED_Write_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_Write_Byte(0X10,OLED_CMD); //DCDC OFF
OLED_Write_Byte(0XAE,OLED_CMD); //DISPLAY OFF
}
/****************************
** 设置坐标点
** x: 行坐标 0~127
** y: 页坐标 0~7
*****************************/
void OLCD_Set_Pos(u8 x, u8 y)
{
OLED_Write_Byte(0xb0+y,OLED_CMD);
OLED_Write_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
OLED_Write_Byte((x&0x0f)|0x01,OLED_CMD);
}
/********************
** OLED清屏操作
*********************/
void OLED_Clear(void)
{
u8 i,n;
for(i=0; i<8; i++)
{
OLED_Write_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)
OLED_Write_Byte (0x00,OLED_CMD); //设置显示位置—列低地址
OLED_Write_Byte (0x10,OLED_CMD); //设置显示位置—列高地址
for(n=0; n<128; n++)OLED_Write_Byte(0,OLED_DATA); // 每一列都置零
}
}
下面使一些关于字符,图片,中文,数字,字符串展示的函数
关于图片,汉字的取模可以看我的另外一篇文章
oled汉字和图片取模教程
void OLED_Ascii_Test()
{
u8 i,j;
for(j=0; j<160; j++) {
OLCD_Set_Pos(j*6%120,j/20); // 设置显示位置
for(i=0; i<6; i++)
{
OLED_Write_Byte(F6x8[j%90][i],OLED_DATA);
}
}
}
/**************************************
** 显示一个字符
** x: 列坐标位置
** y: 页坐标位置
** chr: 待显示的字符
***************************************/
void OLED_Show_Char(u8 x, u8 y , u8 chr , enum Font_Size size)
{
u8 i=0 ,c=0;
c = chr - ' '; // 从空字符串算起,得到偏移后的值
switch(size)
{
case SMALL:
OLCD_Set_Pos(x,y); // 设置显示位置
for(i=0; i< size ; i++)
OLED_Write_Byte(F6x8[c][i],OLED_DATA);
break;
case MEDIA:
OLCD_Set_Pos(x,y); // 设置显示位置
for(i=0; i< 6 ; i++)
OLED_Write_Byte(F12X6[c][i],OLED_DATA);
OLCD_Set_Pos(x,y+1); // 设置显示位置
for(i=6; i< 12 ; i++)
OLED_Write_Byte(F12X6[c][i],OLED_DATA);
break;
case BIG:
OLCD_Set_Pos(x,y); // 设置显示位置
for(i=0; i< 8 ; i++)
OLED_Write_Byte(F16x8[c][i],OLED_DATA);
OLCD_Set_Pos(x,y+1); // 设置显示位置
for(i=8; i< 16 ; i++)
OLED_Write_Byte(F16x8[c][i],OLED_DATA);
break;
}
}
/****************************************
** 显示字符串
** x: 起始列坐标
** y: 起始页
** chr: 待显示的字符串
*****************************************/
void OLED_Show_String(u8 x, u8 y , char *chr , enum Font_Size size)
{
u8 i = 0;
u8 page = (size>8 ? size >> 3 : 1 ); // 字体大小决定翻几页
u8 len = (size>8 ? size >>1 : size ) ; // 字体宽度,最后结果对应了字体表中的每行长度索引
u8 limit = MAX_LEN/len - 1 ;
while(chr[i] != '\0') {
OLED_Show_Char(x,y,chr[i],size);
x+=len; // 字体宽高 6*8
if(x>(len*limit-1)) // 如果大于了极限值 128
{
x = 0 ; // 列从零开始
y += page; // 另起一页
}
i ++ ; // 循环继续,直到字符串都写完了为止
}
}
/*********************************************************
** 自定义数字显示函数
** x: 列起始位置 0~(127-len) 取值从0到最后一个字符的起始位置
** y: 页起始位置 0~7
** num: 待显示的数字 0~9
** size: 字体大小枚举值
*********************************************************/
void OLED_Show_Num(u8 x, u8 y , u8 num , enum Num_Size size)
{
u8 i,j ;
u8 len = size >> 1 ; // 字体宽度,这里直接处以2,因为字体宽度正好是高度的一半
u8 page = size >> 3 ; // 这里处以8直接用移位操作,高度大于8就要翻页
for(i = 0 ; i < page ; i ++)
{
OLCD_Set_Pos(x,y+i); // 设置显示位置
switch(size)
{
case SMALL_NUM:
for(j=0; j<len; j++)
OLED_Write_Byte(N24x12[num*page + i][j],OLED_DATA); // 写数据
break;
case MEDIA_NUM:
for(j=0; j<len; j++)
OLED_Write_Byte(N32x16[num*page + i][j],OLED_DATA); // 写数据
break;
case BIG_NUM:
for(j=0; j<len; j++)
OLED_Write_Byte(N40x20[num*page + i][j],OLED_DATA); // 写数据
break;
}
}
}
/*********************************************************
** 自定义汉字显示函数
** x: 列起始位置 0~(127-len) 取值从0到最后一个字符的起始位置
** y: 页起始位置 0~7
** index: 待显示的汉字序号
** size: 字体大小枚举值
** reverse: 是否反显 1=是,0=否
*********************************************************/
void OLED_Show_Chinese(u8 x, u8 y , u8 index , enum Zh_Size size , u8 reverse)
{
u8 i,j ;
u8 len = size ; // 字体宽度,等于字体高度
u8 page = size >> 3 ; // 这里处以8直接用移位操作,高度大于8就要翻页
for(i = 0 ; i < page ; i ++)
{
OLCD_Set_Pos(x,y+i); // 设置显示位置
switch(size)
{
case ZH16:
for(j=0; j<len; j++)
reverse?OLED_Write_Byte(~ZH16x16[index*page + i][j],OLED_DATA):OLED_Write_Byte(ZH16x16[index*page + i][j],OLED_DATA); // 写数据
break;
case ZH32:
for(j=0; j<len; j++)
reverse?OLED_Write_Byte(~ZH32x32[index*page + i][j],OLED_DATA):OLED_Write_Byte(ZH32x32[index*page + i][j],OLED_DATA); // 写数据
break;
}
}
}
/*********************************************************
** 自定义图标显示
** x: 列起始位置 0~(127-len) 取值从0到最后一个字符的起始位置
** y: 页起始位置 0~7
** index: 待显示的图标序号,图标大小为16*16
*********************************************************/
void OLED_Show_Icon(u8 x, u8 y , u8 index)
{
u8 i,j ;
u8 len = 16 ;
u8 page = 2 ;
for(i = 0 ; i < page ; i ++)
{
OLCD_Set_Pos(x,y+i); // 设置显示位置
for(j=0; j<len; j++)
OLED_Write_Byte(ICON16[index*page + i][j],OLED_DATA); // 写数据
}
}
/*展示图片*/
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
{
unsigned int j=0;
unsigned char x,y;
if(y1%8==0) y=y1/8;
else y=y1/8+1;
for(y=y0;y<y1;y++)
{
OLCD_Set_Pos(x0,y);
for(x=x0;x<x1;x++)
{
OLED_Write_Byte(BMP[j++],OLED_DATA);
}
}
}
空心圆,实心圆,点,线,矩形的绘制
/*********************************************************************
**画点
x,y为坐标
t=1为亮,0为灭
pos为页
*********************************************************************/
void OLED_DrawDot(u8 x,u8 y,u8 t)
{
u8 pos,bx,temp=0;
if(x>127||y>63) return;
pos=(y)/8;
bx=y%8;
temp=1<<(bx);
if(t){
OLED_date[x][pos]|=temp;}
else{
OLED_date[x][pos]&=~temp;}
OLCD_Set_Pos(x,pos);
OLED_Write_Byte(OLED_date[x][pos],OLED_DATA);
}
void OLED_DrawLine(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2)
{
uint16_t i,k,k1,k2;
if((x2>128)||(y2>64)||(x1>x2)||(y1>y2))return;
if(x1==x2) //画竖线
{
for(i=0;i<(y2-y1);i++)
{
OLED_DrawDot(x1,y1+i,1);
}
}
else if(y1==y2) //画横线
{
for(i=0;i<(x2-x1);i++)
{
OLED_DrawDot(x1+i,y1,1);
}
}
else //画斜线
{
k1=y2-y1;
k2=x2-x1;
k=(uint16_t)k1*100/k2;
for(i=0;i<(x2-x1);i++)
{
OLED_DrawDot(x1+i,y1+i*k/1000,1);
}
}
}
//画方形;x:0~127 y:0~63
//x1,y1,左上角坐标;x2,y2,右下角坐标
void OLED_DrawSquare(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2)
{
OLED_DrawLine(x1,y1,x2,y1);
OLED_DrawLine(x1,y2,x2,y2);
OLED_DrawLine(x1,y1,x1,y2);
OLED_DrawLine(x2,y1,x2,y2);
}
/**********************************************************
date :1.26
author: zs
优化,使圆能够连在一起
x,y:圆心坐标
r:圆的半径
***************************************************************/
//x,y:圆心坐标
//r:圆的半径
void OLED_Circle(uint8_t x,uint8_t y,uint8_t r)
{
int a, b;
a = 0;
b = r;
while(b>=0)
{
OLED_DrawDot(x + a, y - b,1);
OLED_DrawDot(x - a, y - b,1);
OLED_DrawDot(x - a, y + b,1);
OLED_DrawDot(x + a, y + b,1);
OLED_DrawDot(x - b, y + a,1);
OLED_DrawDot(x - b, y - a,1);
OLED_DrawDot(x + b, y + a,1);
OLED_DrawDot(x + b, y - a,1);
if((a*a+b*b)>r*r)
{
b--;
a--;
}
a++;
}
}
void OLED_fill_Circle(u8 x0,u8 y0,u8 r,u8 dot)//写画实心圆心(x0,y0),半径r
{
u8 x = 0,y = 0,R = 0;
for(x = x0-r;x <= x0+r;x++){
for(y = y0-r; y <= y0+r ;y++ ){
R = sqrt(pow(r,2)-pow(x-x0,2))+y0; //圆方程 x,y反置
if( (y >= y0 && y <= R) || (y < y0 && y >= 2*y0-R )|| dot == 0 ) {
//点限制在 圆方程内
OLED_DrawDot(x,y,dot);
}
}
}
}
最后是是显示界面滚动,通过写命令操作ssd1306驱动芯片
/************************************************************
** 滚动显示某一页
** start: 开始页 0 - 7
** end : 结束页 0 - 7
** dirct:方向枚举,left向左,right向右
**************************************************************/
void OLED_horizontalroll_Display(u8 start,u8 end,enum DIRECTION dirct)
{
if(start > 7 || end > 7) return; // 页码超出
OLED_Write_Byte(0x2E ,OLED_CMD); // 关闭滚动
OLED_Write_Byte(dirct,OLED_CMD); // 向左/右滚动
OLED_Write_Byte(0x00 ,OLED_CMD); // 空制令
OLED_Write_Byte(start,OLED_CMD); // 起始页
OLED_Write_Byte(0x0F ,OLED_CMD); // 滚动间隔,0=不滚动,值越大,滚动越快
OLED_Write_Byte(end ,OLED_CMD); // 结束页
OLED_Write_Byte(0x00 ,OLED_CMD); // 空指令
OLED_Write_Byte(0xFF ,OLED_CMD); // 空指令,加两条空指令,不然不会生效
OLED_Write_Byte(0x2F ,OLED_CMD); // 开启滚动
}
/*倾斜滚动,可以改角度,和速度,当倒数第二个命令改为0x00时,即角度为0,相当于只有左右滚动*/
void OLED_obliqueroll_Display(u8 start,u8 end,enum DIRECTION dirct){
if(start >7 || end > 7) return;
OLED_Write_Byte(0x2e,OLED_CMD); //关闭滚动
OLED_Write_Byte(dirct,OLED_CMD); //水平垂直和水平滚动左右 29/2a
OLED_Write_Byte(0x00,OLED_CMD); //虚拟字节
OLED_Write_Byte(start,OLED_CMD); //起始页 0
OLED_Write_Byte(0x07,OLED_CMD); //滚动间隔,0=不滚动,值越大,滚动越快
OLED_Write_Byte(end,OLED_CMD); //终止页 1
OLED_Write_Byte(0x01,OLED_CMD); //垂直滚动偏移量(可修改角度)
OLED_Write_Byte(0x2F,OLED_CMD); //开启滚动
}
/*垂直滚动,因为没有直接的连续滚动,博主将滚动页设成了9页(超范围了),就可以为单一的垂直滚动*/
void OLED_verticalroll_Display(){
OLED_Write_Byte(0x2e,OLED_CMD); //关闭滚动
OLED_Write_Byte(0x2a,OLED_CMD); //水平垂直和水平滚动左右 29/2a
OLED_Write_Byte(0x00,OLED_CMD); //虚拟字节
OLED_Write_Byte(0x00,OLED_CMD); //起始页 0
OLED_Write_Byte(0x07,OLED_CMD); //滚动时间间隔
OLED_Write_Byte(0x08,OLED_CMD); //终止页 1
OLED_Write_Byte(0x01,OLED_CMD); //滚动间隔,0=不滚动,值越大,滚动越快
OLED_Write_Byte(0x2F,OLED_CMD); //开启滚动
}
以上内容,经博主整理,改写和拓展,属实耗费了一些时间
如果看到这里觉得满意,请点赞,收藏。
一些字库,图库,太多,请下载下方工程
如还有不懂,可直接下载工程,对照
以下是工程链接
oled_spi显示
扫描二维码关注公众号,回复:
12899123 查看本文章
