当前位置: 移动技术网 > 网络运营>网络>协议 > STM32单片机IIC驱动OLED灯

STM32单片机IIC驱动OLED灯

2020年08月12日  | 移动技术网网络运营  | 我要评论
UART:两点间进行通信IIC:半双工串行同步通信协议(一主多从结构)工作过程:主机给一个起始信号,让从机进入一个准备状态,再发送一个从机地址,然后所有从机开始识别这个地址,当匹配时从机发送给主机一个响应信号,主机接收到这个信号表明从机中有这个设备了。然后再进行 写入数据 或 从从机读出数据操作IIC时序图:...

UART:

两点间进行通信

IIC:

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

工作过程

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

IIC时序图:

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

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

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

程序结构关系图:

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

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网