STM32 I2C驱动0.96寸OLED屏

本文以STM32F103C8T6最小系统板为例               OLED屏为128*64像素  驱动芯片SSD1306

本文讲解标准库  硬件IIC和软件IIC 驱动OLED

硬件IIC

 硬件IIC要了解的第一个东西那便是 IIC结构体:

//设置SCL时钟频率,此值要低于400000

//指定工作模式,可选IIC模式及SMBUS模式

//时钟占空比,可选low/high=2:0或16:9

//自身的IIC设备地址

//使能或者关闭响应,一般是使能

//指定地址长度,可为7或10

从结构体中就会发出一些疑问   为啥SCL时钟频率要小于等于400000

时钟占空比是啥  为啥是 2:0  或者16:9

那么下面便为你们介绍

从STM32F103中文参考手册中我们可以看到:

IIC的CCR寄存器:

初学者看上去 是不是一脸懵逼 下面的算式是干啥  不知道

计算时钟频率:

 标准模式:

         Thigh=CCR * TPCLK1   Tlow=CCR*TPCLK1

快速模式中Tlow/Thigh=2时:

        Thigh=CCR* TPCLK1   Tlow=2*CCR*TPCLK1

快速模式中: Tlow/Thigh=16/9时:

        Thigh=9*CCR*TPCLK1

        Tlow=16*CCR*TPCLK1

例子:以快速模式中Tlow/Thigh=2时:

        PCLK1=36MHZ,想要配置400Kbit/s 方法:

        PCLK1时钟周期:TPCLK1=1/36000000

        目标SCL时钟周期:TSCL=1/400000

        SCL时钟周期内的高电平时间:Thigh=TSCL/3

        SCL时钟周期内的低电平时间: Tlow=2*TSCL/3

        计算CCR的值:CCR=Thigh/TPCLK1=1/400000 *1/3 *36000000=30

        计算出来的值写入到寄存器即可

        这就是时钟占空比的意义。用来确定IIC速度

后面就要考虑硬件IIC的通信过程了

其主要的几个寄存器是:

        主要寄存器CR1 CR2 SR1 SR2寄存器

        SR1 SR2寄存器  主要用到   用于判断通讯状态.

SR1寄存器

主要是以上4个状态寄存器进行状态判断.

硬件IIC写数据图:

 这个是最重要的,这便是硬件IIC和别的芯片之间通讯的时序

 如果这里看不懂  往下看代码  看完就知道了

 

 再来看OLED屏参数:

        驱动芯片:SSD1306

        电源电压:3.3------5.5V

         SSD1306 128*64bit 分8页   每页128字节

以下命令就是操控OLED的  这些命令在下面代码也会有体现

 

命令0x81:设置对比度。包含两个字节,第一个0x81为命令,随后是一个字节设置对比度

命令0xAE/0xAF:关闭显示和打开显示

命令0x8D:包含两个字节,第一个字节是命令,第二个是设置值,电荷泵必须要开,不然看不到屏幕显示

命令0xB0-0xB7设置页地址

命令0x00-0x0F设置列低位4位地址

命令0x10-0x1F设置列高位4位地址

 

下面开始将代码:

硬件IIC我提供以下几个函数

void IICInit(void);        //IIC初始化函数

void IIC_WriteByte(uint8_t addr,uint8_t data);        //IIC写一个字节给其他芯片函数

void WriteCmd(uint8_t cmd);        //IIC写命令函数

void WriteData(uint8_t data);        //IIC写数据函数

void OLEDInit(void);                        //OLED屏初始化函数

void OLED_SetPos(uint8_t x,uint8_t y);        //OLED屏设置坐标x,y

void OLED_Fill(uint8_t Fill_Data);                //填充OLED屏

void OLED_CLS(void);                                //清楚OLED屏

void OLED_ON(void);                                      //打开OLED屏

void OLED_OFF(void);                                        //关闭OLED屏

void OLED_ShowStr(uint8_t x,uint8_t y,uint8_t *q,uint8_t TextSize);

//显示字符串

void OLED_ShowChinese(uint8_t x,uint8_t y,uint8_t *data,uint8_t length);

//显示中文

void OLED_ShowBMP(uint8_t x,uint8_t y,uint8_t *data);

//显示图片

下面将以上函数一一讲解

首先来看void IICInit(void) IIC初始化函数

void IICInit(void)
{
	I2C_InitTypeDef I2C_InitTypeDefStruct;
	GPIO_InitTypeDef GPIO_InitTypeDefStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);

	GPIO_InitTypeDefStruct.GPIO_Mode=GPIO_Mode_AF_OD;
	GPIO_InitTypeDefStruct.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitTypeDefStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitTypeDefStruct);
	
	I2C_DeInit(I2C1);
	I2C_InitTypeDefStruct.I2C_Ack=I2C_Ack_Enable ;
	I2C_InitTypeDefStruct.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
	I2C_InitTypeDefStruct.I2C_ClockSpeed=400000;
	I2C_InitTypeDefStruct.I2C_DutyCycle=I2C_DutyCycle_2;
	I2C_InitTypeDefStruct.I2C_Mode=I2C_Mode_I2C ;
	I2C_InitTypeDefStruct.I2C_OwnAddress1=0x30;
	
	I2C_Init(I2C1,&I2C_InitTypeDefStruct);
	I2C_Cmd(I2C1,ENABLE);
}

此初始化函数先打开GPIOB,IIC1的时钟 

在初始化对应的结构体

设置时钟速度400000HZ  时钟占空比2:1 自己的地址随便写的0x30

使能IIC

为什么要用开漏输出呢                硬件IIC  就是把普通IO复用成功能IO

在中文参考手册8.1.11可以看到:

再来看void IIC_WriteByte(uint8_t addr,uint8_t data)函数        此函数最重要

void IIC_WriteByte(uint8_t addr,uint8_t data)//最重要 硬件IIC通信
{
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
    //首先判断IIC1是否忙碌        用状态判断
	
	I2C_GenerateSTART(I2C1,ENABLE);
    //开始IIC1
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
    //通过事件判断IIC是否开始	SR1寄存器位0 对应EV5

	I2C_Send7bitAddress(I2C1,OLED_ADDR,I2C_Direction_Transmitter);
    //IIC发送7位器件地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
    //通过事件判断IIC地址是否匹配    SR1寄存器位1 对应EV6

	I2C_SendData(I2C1,addr);
    //IIC发送器件的内部地址,内部地址当作数据发送
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING));
	//通过事件判断数据是否发送成功    对应EV8
    
	I2C_SendData(I2C1,data);
    //IIC发送真正的数据
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING));
    //通过事件判断数据是否发送成功    对应EV8
	
	I2C_GenerateSTOP(I2C1,ENABLE);
    //IIC停止
}

 以上便是通过硬件IIC与元器件之间通信的一次全过程

对应那个可能你看不懂的图

再来看void WriteCmd(uint8_t cmd);

void WriteCmd(uint8_t cmd)
{
	IIC_WriteByte(0x00,cmd);	//0.96OLED屏规定的 0x00写命令
}

有了那个重要函数  就可以真正与OLED屏通信了

在OLED屏中规定了  0x00表示写命令

void WriteData(uint8_t data)同理可得写数据

void WriteData(uint8_t data)
{
	IIC_WriteByte(0x40,data);	//0.96OLED屏规定的0x40写数据
}

OLED屏规定了0x40是写地址

接下来是void OLEDInit(void) //厂商提供的初始化代码

void OLEDInit(void)		//厂商提供的初始化代码
{
//	ms_delay(100);
	WriteCmd(0xAE); //display off
	WriteCmd(0x20);	//Set Memory Addressing Mode	
	WriteCmd(0x10);	//00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
	WriteCmd(0xb0);	//Set Page Start Address for Page Addressing Mode,0-7
	WriteCmd(0xc8);	//Set COM Output Scan Direction
	WriteCmd(0x00); //---set low column address
	WriteCmd(0x10); //---set high column address
	WriteCmd(0x40); //--set start line address
	WriteCmd(0x81); //--set contrast control register
	WriteCmd(0xff); //ÁÁ¶Èµ÷½Ú 0x00~0xff
	WriteCmd(0xa1); //--set segment re-map 0 to 127
	WriteCmd(0xa6); //--set normal display
	WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
	WriteCmd(0x3F); //
	WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
	WriteCmd(0xd3); //-set display offset
	WriteCmd(0x00); //-not offset
	WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
	WriteCmd(0xf0); //--set divide ratio
	WriteCmd(0xd9); //--set pre-charge period
	WriteCmd(0x22); //
	WriteCmd(0xda); //--set com pins hardware configuration
	WriteCmd(0x12);
	WriteCmd(0xdb); //--set vcomh
	WriteCmd(0x20); //0x20,0.77xVcc
	WriteCmd(0x8d); //--set DC-DC enable	
	WriteCmd(0x14); //
	WriteCmd(0xaf); //--turn on oled panel
}

这段代码可以不用管  厂商会给  最后三行代码 则对应着OLED屏打开电荷帮代码  后面会讲

又来了这个表  上面就是OLED屏对应的命令

上面函数最后三个就是 对应 显示开 电荷泵开 序号1、2

在介绍设置坐标函数之前 讲一下OLED屏的布局

128*64 指的是有128列 64行 共128*8个字节

OLED屏中 将128*8字节分成了8页 每页有128*8个比特

128*64中 对应128*64个点 每个点就是一个坐标 这个坐标写0就不亮  写1就亮

如果想点亮整个屏幕 那就需要写128*64个1  熄灭就是128*64个0

OLED屏给的命令3、4、5便可以找到对应的坐标,然后将其写0和1就行

接下来就是void OLED_SetPos(uint8_t x,uint8_t y) //设置OLED屏坐标代码

void OLED_SetPos(uint8_t x,uint8_t y)	//设置OLED屏坐标代码	 根据上图来的	
{
	WriteCmd(0xB0+y);
	WriteCmd(((x>>4)&0x0F) |0x10);
	//WriteCmd((x&0x0F)|0x01);
	WriteCmd(x&0x0F);
}

 设置坐标

第一行设置y的值  设置第几页 0xB0+y对应序号3

第二行设置列的高地址  x>>4 x左移4位 然后&0x0F 算出高四位  在| 0x10 对应序号5

第三行和第四行都是序号4 设置列的低地址  但是用第4行更好

这边找到了128*64中的坐标  接下来便是写0还是1的事了

void OLED_Fill(uint8_t Fill_Data) //字节填充函数 在OLED屏启动后 先填充一下 防止有脏数据

void OLED_Fill(uint8_t Fill_Data)	//字节填充函数 在OLED屏启动后 先填充一下 防止有脏数据
{
	uint8_t m,n;
	for(m=0;m<8;m++)    //总共8页
	{
		WriteCmd(0xB0+m);//设置页地址
		WriteCmd(0x00);	   //设置列的低地址
		WriteCmd(0x10);	    //设置列的高地址
		for(n=0;n<128;n++)
		{
			WriteData(Fill_Data);    //写入数据
		}
	}
    按页写的  每一页的每一列都是从上往下写  所以列的小地址在上
}

一次写一页中的一列地址 一页占8位 故一列写8位 一页有128列  故写128次 然后在写下一列

void OLED_CLS(void) //清屏

void OLED_CLS(void)	//清屏
{
	OLED_Fill(0x00);
}

清屏函数 直接全部给0 故熄灭

void OLED_ON(void) //打开电荷泵和显示开  和上面厂家给的最后三行一样

void OLED_ON(void)	//打开电荷泵等		由上图命令得来
{
	WriteCmd(0x8D);
	WriteCmd(0x14);
	WriteCmd(0xAF);
}

void OLED_OFF(void) //关闭电荷泵和显示

void OLED_OFF(void)	//关闭电荷泵等
{
	WriteCmd(0x8D);
	WriteCmd(0x10);
	WriteCmd(0xAE);
}

oid OLED_ShowStr(uint8_t x,uint8_t y,uint8_t *q,uint8_t TextSize)//显示字符串 注意取字模的方式和大小  本文是按列刷 6x8或者8x16

6x8 6指x轴  8指y轴  在y轴上面每8位为一页 一个字符就是从左向右扫

X 0 1 2...6 按列扫 如下图 取模的时候一定要注意低位在前 列行式

扫描6列后一个字符就显示成功 纵坐标为第一页8位

以下几个函数 一定要注意 自己的取模 不然就是错的

void OLED_ShowStr(uint8_t x,uint8_t y,uint8_t *q,uint8_t TextSize)//显示字符串	注意取字模//的方式和大小  本文是按列刷 6x8或者8x16	
//6x8	6指x轴  8指y轴  在y轴上面每8位为一页 一个字符就是从左向右扫 
//X 0 1 2...6 按列扫		如下图			取模的时候一定要注意低位在前	列行式
{
	uint8_t i,c;
	switch(TextSize)
	{
		case 1:
			while(*q!='\0')
			{
				c=*q-32;
				if(x>127)
				{
					x=0;
					y++;
				}
				OLED_SetPos(x,y);
				for(i=0;i<6;i++)
				{
					WriteData(F6x8[c][i]);
				}
				x+=6;
				q++;
			}
			break;
		case 2:
			while(*q!='\0')
			{
				c=*q-32;
				if(x>127)
				{
					x=0;
					y++;
				}
				OLED_SetPos(x,y);
				for(i=0;i<8;i++)
				{
					WriteData(F8X16[c*16+i]);
				}
				OLED_SetPos(x,y+1);
				for(i=0;i<8;i++)
				{
					WriteData(F8X16[c*16+i+8]);
				}
				x+=8;
				q++;
			}
			break;
	}
}
void OLED_ShowChinese(uint8_t x,uint8_t y,uint8_t *data,uint8_t length)//显示中文
{
	int i,j;
	for(i=0;i<length;i++)
	{	
		if(x>127)
		{
			x=0;
			y+=2;
		}
		OLED_SetPos(x,y);
		for(j=0;j<16;j++)
		{
			WriteData(data[i*32+j]);
		}
		OLED_SetPos(x,y+1);
		for(j=0;j<16;j++)
		{
			WriteData(data[i*32+j+16]);
		}
		x+=16;
	}
}

中文的取模方式是16 x16 显示两个中文 每个中文是由2*16个字节组成。

 

void OLED_ShowBMP(uint8_t x,uint8_t y,uint8_t *data)//显示一个BMP图片 只支持黑白图
{
	int i,j;
	for(i=0;i<4;i++)
	{
		OLED_SetPos(x,y+i);
		for(j=0;j<64;j++)
		{
			WriteData(data[i*64+j]);
		}
	}
}
//像素为64*32  占4页  每页写64列

以下是完整代码:

 main.c

#include "main.h"
#include "led/led.h"
#include "exit/exit.h"
#include "uart/uart.h"
#include "tim/tim.h"
#include "systick/systick.h"
#include "iicoled/iicoled.h"

extern unsigned char F16X16_Chinese[];
extern unsigned char F64x32[];
int  main()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	InitLED();
	InitExit();
	InitUart();
	InitTim4General();
	InitTim3GeneralPWM();
	IICInit();
	OLEDInit();
	OLED_Fill(0x00);
	//OLED_ShowStr(0,0,"Hello",1);
	//OLED_ShowStr(0,4,"Hello",2);
	//OLED_ShowChinese(0,2,F16X16_Chinese,2);
	OLED_ShowBMP(0,0,F64x32);
	while(1)
	{
		ms_delay(500);
		GPIO_SetBits(GPIOC,GPIO_Pin_13);
		ms_delay(500);
		GPIO_ResetBits(GPIOC,GPIO_Pin_13);

		
	}
		 
   
}



iicoled.c

#include "iicoled/iicoled.h"
#include "iicoled/codetab.h"

void IICInit(void)
{
	I2C_InitTypeDef I2C_InitTypeDefStruct;
	GPIO_InitTypeDef GPIO_InitTypeDefStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);

	GPIO_InitTypeDefStruct.GPIO_Mode=GPIO_Mode_AF_OD;
	GPIO_InitTypeDefStruct.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitTypeDefStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitTypeDefStruct);
	
	I2C_DeInit(I2C1);
	I2C_InitTypeDefStruct.I2C_Ack=I2C_Ack_Enable ;
	I2C_InitTypeDefStruct.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
	I2C_InitTypeDefStruct.I2C_ClockSpeed=400000;
	I2C_InitTypeDefStruct.I2C_DutyCycle=I2C_DutyCycle_2;
	I2C_InitTypeDefStruct.I2C_Mode=I2C_Mode_I2C ;
	I2C_InitTypeDefStruct.I2C_OwnAddress1=0x30;
	
	I2C_Init(I2C1,&I2C_InitTypeDefStruct);
	I2C_Cmd(I2C1,ENABLE);
}

void IIC_WriteByte(uint8_t addr,uint8_t data)
{
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
	
	I2C_GenerateSTART(I2C1,ENABLE);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
	
	I2C_Send7bitAddress(I2C1,OLED_ADDR,I2C_Direction_Transmitter);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
	
	I2C_SendData(I2C1,addr);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING));
	
	I2C_SendData(I2C1,data);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING));
	
	I2C_GenerateSTOP(I2C1,ENABLE);
}

void WriteCmd(uint8_t cmd)
{
	IIC_WriteByte(0x00,cmd);
}
void WriteData(uint8_t data)
{
	IIC_WriteByte(0x40,data);
}

void OLEDInit(void)
{
//	ms_delay(100);
	WriteCmd(0xAE); //display off
	WriteCmd(0x20);	//Set Memory Addressing Mode	
	WriteCmd(0x10);	//00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
	WriteCmd(0xb0);	//Set Page Start Address for Page Addressing Mode,0-7
	WriteCmd(0xc8);	//Set COM Output Scan Direction
	WriteCmd(0x00); //---set low column address
	WriteCmd(0x10); //---set high column address
	WriteCmd(0x40); //--set start line address
	WriteCmd(0x81); //--set contrast control register
	WriteCmd(0xff); //ÁÁ¶Èµ÷½Ú 0x00~0xff
	WriteCmd(0xa1); //--set segment re-map 0 to 127
	WriteCmd(0xa6); //--set normal display
	WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
	WriteCmd(0x3F); //
	WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
	WriteCmd(0xd3); //-set display offset
	WriteCmd(0x00); //-not offset
	WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
	WriteCmd(0xf0); //--set divide ratio
	WriteCmd(0xd9); //--set pre-charge period
	WriteCmd(0x22); //
	WriteCmd(0xda); //--set com pins hardware configuration
	WriteCmd(0x12);
	WriteCmd(0xdb); //--set vcomh
	WriteCmd(0x20); //0x20,0.77xVcc
	WriteCmd(0x8d); //--set DC-DC enable
	WriteCmd(0x14); //
	WriteCmd(0xaf); //--turn on oled panel
}

void OLED_SetPos(uint8_t x,uint8_t y)
{
	WriteCmd(0xB0+y);
	WriteCmd(((x>>4)&0x0F) |0x10);
	//WriteCmd((x&0x0F)|0x01);
	WriteCmd(x&0x0F);
}

void OLED_Fill(uint8_t Fill_Data)
{
	uint8_t m,n;
	for(m=0;m<8;m++)
	{
		WriteCmd(0xB0+m);
		WriteCmd(0x00);
		WriteCmd(0x10);
		for(n=0;n<128;n++)
		{
			WriteData(Fill_Data);
		}
	}
}
void OLED_CLS(void)
{
	OLED_Fill(0x00);
}
void OLED_ON(void)
{
	WriteCmd(0x8D);
	WriteCmd(0x14);
	WriteCmd(0xAF);
}

void OLED_OFF(void)
{
	WriteCmd(0x8D);
	WriteCmd(0x10);
	WriteCmd(0xAE);
}

void OLED_ShowStr(uint8_t x,uint8_t y,uint8_t *q,uint8_t TextSize)
{
	uint8_t i,c;
	switch(TextSize)
	{
		case 1:
			while(*q!='\0')
			{
				c=*q-32;
				if(x>127)
				{
					x=0;
					y++;
				}
				OLED_SetPos(x,y);
				for(i=0;i<6;i++)
				{
					WriteData(F6x8[c][i]);
				}
				x+=6;
				q++;
			}
			break;
		case 2:
			while(*q!='\0')
			{
				c=*q-32;
				if(x>127)
				{
					x=0;
					y++;
				}
				OLED_SetPos(x,y);
				for(i=0;i<8;i++)
				{
					WriteData(F8X16[c*16+i]);
				}
				OLED_SetPos(x,y+1);
				for(i=0;i<8;i++)
				{
					WriteData(F8X16[c*16+i+8]);
				}
				x+=8;
				q++;
			}
			break;
	}
}

void OLED_ShowChinese(uint8_t x,uint8_t y,uint8_t *data,uint8_t length)
{
	int i,j;
	for(i=0;i<length;i++)
	{	
		if(x>127)
		{
			x=0;
			y+=2;
		}
		OLED_SetPos(x,y);
		for(j=0;j<16;j++)
		{
			WriteData(data[i*32+j]);
		}
		OLED_SetPos(x,y+1);
		for(j=0;j<16;j++)
		{
			WriteData(data[i*32+j+16]);
		}
		x+=16;
	}
}

void OLED_ShowBMP(uint8_t x,uint8_t y,uint8_t *data)
{
	int i,j;
	for(i=0;i<4;i++)
	{
		OLED_SetPos(x,y+i);
		for(j=0;j<64;j++)
		{
			WriteData(data[i*64+j]);
		}
	}
}

iicoled.h

#include "main.h"
#include "systick/systick.h"

#define OLED_ADDR 0x78


void IICInit(void);
void IIC_WriteByte(uint8_t addr,uint8_t data);
void WriteCmd(uint8_t cmd);
void WriteData(uint8_t data);
void OLEDInit(void);
void OLED_SetPos(uint8_t x,uint8_t y);
void OLED_Fill(uint8_t Fill_Data);
void OLED_CLS(void);
void OLED_ON(void);
void OLED_OFF(void);
void OLED_ShowStr(uint8_t x,uint8_t y,uint8_t *q,uint8_t TextSize);
void OLED_ShowChinese(uint8_t x,uint8_t y,uint8_t *data,uint8_t length);
void OLED_ShowBMP(uint8_t x,uint8_t y,uint8_t *data);

codetab.h部分代码  这个是字模代码

运行截图

 

 

软件IIC明天更新 

有问题欢迎讨论 QAQ。

  • 9
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值