欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

STM32单片机IIC驱动OLED灯

程序员文章站 2022-03-11 20:43:53
UART:两点间进行通信IIC:半双工串行同步通信协议(一主多从结构)工作过程:主机给一个起始信号,让从机进入一个准备状态,再发送一个从机地址,然后所有从机开始识别这个地址,当匹配时从机发送给主机一个响应信号,主机接收到这个信号表明从机中有这个设备了。然后再进行 写入数据 或 从从机读出数据操作IIC时序图:......

UART:

两点间进行通信

IIC:

半双工串行同步通信协议(一主多从结构)

STM32单片机IIC驱动OLED灯

工作过程

主机给一个起始信号,让从机进入一个准备状态,再发送一个从机地址,然后所有从机开始识别这个地址,当匹配时从机发送给主机一个响应信号,主机接收到这个信号表明从机中有这个设备了。然后再进行 写入数据从从机读出数据操作 

STM32单片机IIC驱动OLED灯

STM32单片机IIC驱动OLED灯

STM32单片机IIC驱动OLED灯

STM32单片机IIC驱动OLED灯

IIC时序图:

STM32单片机IIC驱动OLED灯

STM32单片机IIC驱动OLED灯

STM32单片机IIC驱动OLED灯

源码下载链接:https://taileliekaishi.lanzous.com/iTdbufi1quf

工程项目结构如下图所示:

其中画红色方框部分为重要函数来进行讲解

STM32单片机IIC驱动OLED灯

程序结构关系图:

STM32单片机IIC驱动OLED灯

OLED.c

#include "stdio.h"
#include "string.h"
#include "OLED/OLED.h"
#include "OLED/FONT.h"  	 
#include "DELAY/Delay.h"
#include "IIC/IIC.h"
#include "OLED/BMP.h"
/**********************************静态功能函数**************************************/
/**
 * 功能:根据指定坐标值生效坐标设置
 * 参数:
 * 		x:x轴坐标
 * 		y:y轴坐标
 * 返回值:None
 */
static void setPos(unsigned char x, unsigned char y) 
{ 
	writeCommand(0xb0+y);
	writeCommand(((x&0xf0)>>4)|0x10);
	writeCommand(x&0x0f); 
} 

/**
 * 功能:查找指定汉字在字库中的位置
 * 参数:
 *      str:待查找汉字字符串,一个汉字也是字符串(占用3字节)
 * 		cnfont_index:待查找中文字库索引数组地址
 * 返回值:None
 */
static u8 findCNIndex(u8* str,u8* cnfont_index)
{
	u16 cnfont_size = strlen(cnfont_index);

	u8 index = 0;
	for(index=0;index<cnfont_size/3;++index)
	{
		if(((str[0]^cnfont_index[index*3+0])||(str[1]^cnfont_index[index*3+1])||(str[2]^cnfont_index[index*3+2]))==0)//匹配到汉字索引
		{
			return index;
		}
	}

	return 0; //没有匹配到直接返回字库第一个索引,这里是“风”
}

/**
 * 功能:写入命令给OLED
 * 参数:
 * 		cmd:命令
 * 返回值:None
 */
static void writeCommand(unsigned char cmd)
{
	startIIC();
	sendIICByte(0x78); //发送从机地址及写指令位('0')       
	waitAck();	
	sendIICByte(0x00); //写入控制字节
	waitAck();	
	sendIICByte(cmd); 
	waitAck();	
	stopIIC();
}

/**
 * 功能:写入数据给OLED
 * 参数:
 * 		data:数据
 * 返回值:None
 */
static void writeData(unsigned char data)
{
	startIIC();
	sendIICByte(0x78);	//发送从机地址及写指令位('0')
	waitAck();	
	sendIICByte(0x40);	//写入控制字节
	waitAck();	
	sendIICByte(data);
	waitAck();	
	stopIIC();
}
/**********************************屏幕设置函数**************************************/
/**
 * 功能:设置屏幕反色  
 * 参数:
 * 		set:设置参数   SCREEN_NORMAL,SCREEN_REVERSE可选
 * 返回值:None
 */
void setScreenReverse(SCREEN_SHOW set)
{
	if(set==SCREEN_REVERSE)		//屏幕反色
	{
		writeCommand(0xA7);
	}else 						//屏幕常色
	{
		writeCommand(0xA6);
	}
}

/**
 * 功能:设置屏幕显示方向,类似于手机屏幕翻转  
 * 参数:
 * 		set:设置参数   SCREEN_UP,SCREEN_DOWN可选
 * 返回值:None
 */
void setScreenDir(SCREEN_DIR set)
{
	if(set==SCREEN_UP)			//屏幕正向
	{
		writeCommand(0xA1);
		writeCommand(0xC8);
	}else 						//屏幕倒向
	{
		writeCommand(0xA0);
		writeCommand(0xC0);
	}
}

/**
 * 功能:设置屏幕是否开启,类似于手机息屏和唤醒
 * 参数:
 * 		set:设置参数   SCREEN_ON,SCREEN_OFF可选
 * 返回值:None
 */
void setScreenSwtich(SCREEN_SWITCH set)
{
	if(set==SCREEN_ON)
	{
		writeCommand(0xAF);
	}else 
	{
		writeCommand(0xAE);
	}
}

/**********************************显示屏驱动函数**************************************/
/**
 * 功能:初始化OLED
 * 参数:None
 * 返回值:None
 */
void initOLED(void)
{ 	 
	writeCommand(0x81); 	//设置亮度
	writeCommand(0xFF); 	//亮度值最大 复位默认0x7F
	writeCommand(0xA1);		//设置段映射方式即设置是否水平翻转 A0表示翻转 通常和C0一起使用
	writeCommand(0xC8);		//设置COM扫描模式即设置是否垂直翻转 C0表示翻转 通常和A0一起使用	
	writeCommand(0x8D);		//电荷泵使能
	writeCommand(0x14);
	
	writeCommand(0xAF);		//开屏幕,默认是关闭的就和没上电一样,所以要手动开启
} 

/**
 * 功能:格式化屏幕,常使用0x00或者0xFF清屏,使用不同数据可以产生不同的条纹
 * 参数:
 * 		format_data:格式化内容,一般清屏会用到0x00或者0xFF
 * 返回值:None
 */
void formatScreen(u8 format_data)  
{  
	u8 x,y;		    
	for(y=0;y<8;++y)  
	{  
		writeCommand(0xb0+y);    //设置页地址(0~7)
		writeCommand(0x00);      //设置显示位置—列低地址
		writeCommand(0x10);      //设置显示位置—列高地址   
		for(x=0;x<128;++x)
		{
			writeData(format_data); 
		}	
	} 
}

/**
 * 功能:显示一个字符到OLED
 * 参数:
 * 		x:x轴坐标 0-127
 * 		y:y轴坐标 0-7
 * 		ch:待显示字符 ASCII字符集
 * 		f_size:字体大小 FONT_8_EN(0608) FONT_16_EN(0816)
 * 返回值:None
 */
void showChar(u8 x,u8 y,u8 ch,FONT_SIZE f_size)
{      	
		u8 index = ch-' ';	
		u8 i;
		
		if(x > 127 || y > 7) 			//参数异常处理
		{
			x = 0;
			y = 0;
		}
		if(f_size == FONT_16_EN)		//如果是16*8点阵
		{
			setPos(x,y);	
			for(i=0;i<8;++i)			//由于是8*16的点阵,因此占用两页,要分成写入,此时写入第一页
			{
				writeData(ANSIC0816[index][i]);
			}
			setPos(x,y+1);				//人为指定下一页地址
			for(i=8;i<16;++i)			//由于是8*16的点阵,因此占用两页,要分成写入,此时写入第二页
			{
				writeData(ANSIC0816[index][i]);
			}
			
		}else if(f_size == FONT_8_EN)	//6*8点阵
		{	
			setPos(x,y);
			for(i=0;i<6;i++)			//6*8点阵,写入一页即可
			{
				writeData(ANSIC0608[index][i]);		
			}	
		}else 
		{
			/*其他字体敬请期待:)*/
		}
}

/**
 * 功能:显示字符串到OLED
 * 参数:
 * 		x:x轴坐标 0-127
 * 		y:y轴坐标 0-7
 * 		str:待显示字符串
 * 		f_size:字体大小 FONT_8_EN(0608) FONT_16_EN(0816)
 * 返回值:None
 */  
void showString(u8 x,u8 y,u8* str,FONT_SIZE f_size)
{
	while(*str)
	{
		showChar(x,y,*str++,f_size);	
		x += f_size;		//增加横坐标,移到下一个汉字位置
	}
}

/**
 * 功能:以八进制/十进制/十六进制显示传入的整形数据
 * 参数:
 * 		x:x轴坐标 0-127
 * 		y:y轴坐标 0-7
 * 		number:待显示整数,支持负数
 * 		radix:选择显示进制,可选OCT/DEC/HEX
 *      ndigit:占用几个字符
 * 		f_size:字体大小 FONT_8_EN(0608) FONT_16_EN(0816)
 * 返回值:None
 * 注意:
 */ 
void showNumber(u8 x,u8 y,s32 number,RADIX radix,u8 ndigit,FONT_SIZE f_size)
{
	u8 i = 0;
	u8 str[25] = {0}; 				//定义数字转字符串的存储buffer

	if(radix==DEC) 					//按十进制存储
	{
		sprintf(str,"%d",number);
	}else if(radix==HEX)			//按十六进制存储
	{
		sprintf(str,"%X",number);
	}else if(radix==OCT)			//按八进制存储
	{
		sprintf(str,"%o",number);
	}else 
	{
		sprintf(str,"%d",number);   //参数错误,按十进制处理
	}

	
	for(i=strlen(str);i<ndigit;++i)
	{
		str[i] = ' ';
	}
	
	i = 0;
	while(str[i])
	{
		showChar(x,y,str[i++],f_size);	
		x += f_size;
	}	
}

/**
 * 功能:显示16*16点阵汉字
 * 参数:
 * 		x:x轴坐标 0-127
 * 		y:y轴坐标 0-7
 * 		str:待显示汉字支持单个汉字和多个汉字
 * 		f_size:字体大小 目前只提供了16*16点阵汉字,如果要用其他大小的汉字添加对应判断即可
 * 			    本函数中该参数无效
 * 返回值:None
 */ 
void showCNString(u8 x,u8 y,u8* str,FONT_SIZE f_size)
{   
	u8 i;
	u8 cn_index;
	u8 count;
	if(x > 127 || y > 7) //参数异常处理
	{
		x = 0;
		y = 0;
	}  

	for(count=0;count<strlen(str)/3;++count)
	{
		cn_index = findCNIndex(str+count*3,CN1616_Index);
		setPos(x+16*count,y);
		for(i=0;i<16;++i)
		{
			writeData(CN1616[cn_index][i]);
		}
		setPos(x+16*count,y+1);
		for(i=16;i<32;++i)
		{
			writeData(CN1616[cn_index][i]);
		}
	} 			    
		
}


/**
 * 功能:在制定区域显示图片
 * 参数:
 * 		x:x轴坐标 0-127
 * 		y:y轴坐标 0-7
 * 		x_len:显示区域横坐标长度 0-128
 *		y_len:显示区域纵坐标长度 0-8
 * 		image_index:图片枚举索引
 * 说明:该函数一般用于显示全屏LOGO,另外灵活运用可以显示PPT切换特效
 * 		如让x_len递增LOGO就会从左到右逐渐显示,其他用法类似
 * 返回值:None
 */ 
void showImage(u8 xpos, u8 ypos,u8 x_len, u8 y_len,IMAGE_INDEX  image_index)
{ 	
	u16 i,j;

	for(i=0;i<y_len;++i)					//页地址控制
	{
		setPos(xpos,ypos++);
		
		for(j=i*128+xpos;j<i*128+x_len;++j) //列地址控制
		{
			switch(image_index)
			{
				case FM_LOGO_ENUM        :writeData(FM_LOGO[j]);        break;
				case BRIGHTNESS_LOGO_ENUM:writeData(BRIGHTNESS_LOGO[j]);break;
				case DIRECT_LOGO_ENUM    :writeData(DIRECT_LOGO[j]);    break;
				case REVERSAL_LOGO_ENUM  :writeData(REVERSAL_LOGO[j]);  break;

				default                  :                              break;
			}
			
		}
	}
} 

IIC.c

#include "IIC/IIC.h"
#include "DELAY/Delay.h"

/**
 * 功能:设置SDA引脚为输入状态,用于检测从机的ACK信号以及接收数据
 * 参数:None
 * 返回值:None
 */
void setSDA_IN(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;                  //定义GPIO初始化结构体

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIO时钟

    GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;		      //SDA引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;         //设置上拉输入

	GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);	      //设置生效
}

/**
 * 功能:设置SDA引脚为输出状态,用于向外发送数据
 * 参数:None
 * 返回值:None
 */
void setSDA_OUT(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;                  //定义GPIO初始化结构体

    RCC_APB2PeriphClockCmd(IIC_SDA_RCC, ENABLE);          //使能GPIO时钟

    GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;            //SDA引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;      //开漏输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);         //设置生效    
    GPIO_SetBits(IIC_SDA_PORT,IIC_SDA_PIN);               //SDA拉高 
}

/**
 * 功能:初始化模拟IIC引脚
 * 参数:None
 * 返回值:None
 */
void initIIC(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;             //定义GPIO初始化结构体

    RCC_APB2PeriphClockCmd(IIC_SDA_RCC, ENABLE);     //使能SDA时钟

    GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;       //设置SDA引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设置输出速度
    GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);    //设置生效    
    GPIO_SetBits(IIC_SDA_PORT,IIC_SDA_PIN);          //空闲状态 拉高SDA  

    RCC_APB2PeriphClockCmd(IIC_SCL_RCC, ENABLE);     //使能SCL时钟
    GPIO_InitStructure.GPIO_Pin = IIC_SCL_PIN;       //设置SCL引脚
    GPIO_Init(IIC_SCL_PORT, &GPIO_InitStructure);    //设置生效    
    GPIO_SetBits(IIC_SCL_PORT,IIC_SCL_PIN);          //空闲状态 拉高SCL   
}

/**
 * 功能:发起IIC开始信号
 * 参数:None
 * 返回值:None
 */
void startIIC(void)
{
    IIC_SDA_OUT();          //设置SDA引脚为开漏输出

    IIC_SCL_H();            //拉高SCL
    IIC_SDA_H();            //拉高SDA
    Delay_us(IIC_SPEED);    //延时一段时间
    IIC_SDA_L();            //拉低SDA,在SCK高电平器件产生下降沿
    Delay_us(IIC_SPEED);    //延时一段时间
    IIC_SCL_L();            //拉低时钟,完成一个时钟周期
}

/**
 * 功能:发起IIC停止信号
 * 参数:None
 * 返回值:None
 */
void stopIIC(void)
{
    IIC_SDA_OUT();

    IIC_SDA_L();
    IIC_SCL_H();
    Delay_us(IIC_SPEED);
    IIC_SDA_H();            //SCL高电平期间,产生SDA下降沿
    Delay_us(IIC_SPEED);
    IIC_SCL_L();
}

/**
 * 功能:发起一个字节数据
 * 参数:byte:待发送数据
 * 返回值:None
 */
void sendIICByte(u8 byte)
{
    u8 i;

    IIC_SDA_OUT(); // 将SDA设置成发送

    for(i=0;i<8;++i) // 每次一循环送一位:高位先发出
    {
				if(byte & 0x80) //和最高位进行比较,如果是非0就是传输数据为1
        {
            IIC_SDA_H();
        }else 
        {
            IIC_SDA_L();
        }
        byte <<= 1; // 将下一位移动到最高位
				
				// 修改完数据之后将数据线拉高:从机就收到了这一位数据
        IIC_SCL_H();
        Delay_us(IIC_SPEED);
				// 时钟线拉低之后可以进行数据的改变
        IIC_SCL_L();
        Delay_us(IIC_SPEED);
    }
}

/**
 * 功能:接收一个字节数据
 * 参数:None
 * 返回值:返回采集到的数据
 * 进行通讯时用到
 */
u8 receiveIICByte(void)
{
    s8 i;
    u8 byte = 0;

    IIC_SDA_IN();
    Delay_us(IIC_SPEED);
    for(i=7;i>=0;--i)
    {
        IIC_SCL_H();
        Delay_us(IIC_SPEED);
        if(GPIO_ReadInputDataBit(IIC_SDA_PORT,IIC_SDA_PIN))
        {
            byte |= 0x01<<i;
        }else 
        {
            byte |= 0x00<<i;
        }

        IIC_SCL_L();
        Delay_us(IIC_SPEED);
    }

    return byte;
}

/**
 * 功能:发送响应信号
 * 参数:None
 * 返回值:None
 */
void sendIICAck(void)
{
    IIC_SDA_OUT();

    IIC_SDA_L();
    IIC_SCL_H();
    Delay_us(IIC_SPEED);
    IIC_SDA_H();
    IIC_SCL_L();
}

/**
 * 功能:发送非响应信号
 * 参数:None
 * 返回值:None
 */
void sendIICNAck(void)
{
    IIC_SDA_OUT();

    IIC_SDA_H();
    IIC_SCL_H();
    Delay_us(IIC_SPEED);
    IIC_SCL_L();
}
/**
 * 功能:等待从机ACK信号
 * 参数:None
 * 返回值:None
 */
IIC_ACK waitAck(void)
{
    u8 i = 0;

    IIC_SDA_IN();
    IIC_SCL_H();
    while(GPIO_ReadInputDataBit(IIC_SDA_PORT,IIC_SDA_PIN))
    {
        if(++i>50)
        {
            IIC_SCL_L();
            return  NACK;
        }
        Delay_us(1);
    }

    IIC_SCL_L();
    return ACK;
}

 

本文地址:https://blog.csdn.net/weixin_39903708/article/details/107906940