模拟IIC驱动oled——寄存器(SSD1306)

模拟IIC驱动oled——寄存器(SSD1306)

Cortex_m0/m3/m4模拟IIC驱动oled


前言

参考网上现成代码,整理一个寄存器模拟IIC驱动oled

数据手册

https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf

Lcd_port.c 代码如下(示例):

#include "LCD_disp.h"

#include "LCD_port.h"

// ---------------------------------------------------------------------------------------------------------------------
// 定义状态
#define I2C_WRITE   0x00
#define I2C_READ    0x01

// ---------------------------------------------------------------
#define	_IIC_OK			0		// 正常
#define _IIC_NO_ACK 	5		// ACK 出错
#define _IIC_BUS_ERR	6		// IIC总线出错
#define _IIC_NO_SLA		7		// IIC无从器件
#define _IIC_PW_OFF		9		// IIC电源关闭,无响应

// ---------------------------------------------------------------------------------------------------------------------
// SDA IO 切换
#define IIC_SDA_2_IN_IO()  LSDA_2_IIN()
#define IIC_SDA_2_OUT_IO() LSDA_2_OUT()

//IIC IO
#define IIC_SCL         LSCL_O   // SCL OUT
#define IIC_SDA_OUT     LSDA_O   // SDA OUT	 
#define IIC_SDA_IN      LSDA_I   // SDA IN

static void IIC_Delay( u32 time )
{					     
	while( time > 0 )
	{
		time --;
		__NOP();__NOP();__NOP();__NOP();    // 根据主频调整,用示波器看一下间隔,最高400KHz
    
	}	
}

static void I2C_Start( void )
{
	IIC_SDA_OUT = 1;
	IIC_SCL = 1;
	IIC_Delay(4);

	IIC_SDA_OUT = 0;
	IIC_Delay(4);

	IIC_SCL = 0;
	IIC_Delay(4);

	IIC_SDA_OUT = 1;
	IIC_Delay(4);
}	  


static void I2C_Stop( void )
{
	IIC_SDA_OUT = 0;
	IIC_Delay(4);

	IIC_SCL = 1;
	IIC_Delay(4);

	IIC_SDA_OUT = 1;
	IIC_Delay(4);						   	
}


void I2C_Ack( void )
{
	// ACK
	IIC_SDA_2_IN_IO( );					// SDA 切换为输入
	IIC_Delay(2);
	IIC_SCL = 1;
	IIC_Delay(2);
	IIC_SDA_2_OUT_IO();					// SDA 切换为输出
	IIC_SCL = 0;
}


static void I2C_Write( u8 data )
{                        
	U8 i;

	for( i=0; i<8; i++ )				// 向IIC总线发送8位数据
	{	
		if ((data & 0x80) == 0x80)  IIC_SDA_OUT = 1 ;
		else 			  		    IIC_SDA_OUT = 0 ;
		IIC_Delay(2);

		IIC_SCL = 1;
		data = data << 1;
		IIC_Delay(2);

		IIC_SCL = 0;
		IIC_Delay(2);
	}
    
} 	    

 
/*************************************************************************
功 能:从IIC总线上读出一个8位的数
参数表:无
返回值:Byte
说 明:
*************************************************************************/
U8 I2C_Read( void )
{
	U8 i,j=0;

	for( i=0; i<8; i++ )
	{
		j = j << 1;
		IIC_SCL = 1;
		IIC_Delay(2);

		if( IIC_SDA_IN == 1 ) j++;
		IIC_SCL = 0;
		IIC_Delay(2);
	}

	return j ;
}


// ---------------------------------------------------------------------
#define _CHIP_ADDR  0x78
#define _CHIP_CMD   0x00
#define _CHIP_DAT   0x40

void LCD_WR_CMD( u8 cmd )
{
 	U8  Ack;
	U16 TimeOut;							// 超时退出

	Ack = 1;
	TimeOut = 1000 ;

	while( (Ack == 1) && TimeOut )
	{
		IIC_SDA_2_OUT_IO();					// SDA 切换为输出

		I2C_Start( );
		I2C_Write( _CHIP_ADDR | I2C_WRITE );	// 写入硬件地址
	
		IIC_Delay(2);
		IIC_SDA_2_IN_IO();					// SDA 切换为输入	
		IIC_SCL = 1;
					
		IIC_Delay(2);

		if( IIC_SDA_IN == 1 )
			Ack = 1;
		else
			Ack=0;
		
		IIC_SCL = 0;
		TimeOut -- ;
	}

	IIC_SDA_2_OUT_IO();				// SDA 切换为输出

	// 超时退出
	if( TimeOut == 0 )
	{
		return;
	}

	I2C_Write( _CHIP_CMD );			// 写入8位数据地址

	IIC_SCL = 1;
	IIC_Delay(2);
	IIC_SCL = 0;

	I2C_Write( cmd );				// 写入命令

	// ACK
	IIC_SCL = 1;
	IIC_Delay(2);
	IIC_SCL = 0;

	return;

}

void LCD_WR_DAT( u8* buff, u8 len )
{
 	U8  Ack;
	U16 TimeOut;							// 超时退出

	Ack = 1;
	TimeOut = 1000 ;

	while( (Ack == 1) && TimeOut )
	{
		IIC_SDA_2_OUT_IO();					// SDA 切换为输出

		I2C_Start( );
		I2C_Write( _CHIP_ADDR | I2C_WRITE );	// 写入硬件地址
	
		IIC_Delay(2);
		IIC_SDA_2_IN_IO();					// SDA 切换为输入 【这是网上大多忽略的】	
		IIC_SCL = 1;
					
		IIC_Delay(2);

		if( IIC_SDA_IN == 1 )
			Ack = 1;
		else
			Ack=0;
		
		IIC_SCL = 0;
		TimeOut -- ;
	}

	IIC_SDA_2_OUT_IO();						// SDA 切换为输出

	// 超时退出
	if( TimeOut == 0 )
	{
		return;
	}

	I2C_Write( _CHIP_DAT );					// 写入8位数据地址

	IIC_SCL = 1;
	IIC_Delay(2);
	IIC_SCL = 0;

    for ( u8 i=0; i<len; i++ )
    {    
        I2C_Write( buff[i] );				// 写入数据

        
        // ACK
        IIC_SCL = 1;
        IIC_Delay(2);
        IIC_SCL = 0;
    }
    
	I2C_Stop();
    
	return;
    
}

// =====================================================================================================================

Lcd_port.h 代码如下(示例):

#ifndef	__LCD_PORT_H__
#define	__LCD_PORT_H__


// ==================================================================================
// 常量定义
// ==================================================================================


// ==================================================================================
// 变量申明
// ==================================================================================


// ==================================================================================
// 函数申明
// ==================================================================================
void LCD_WR_CMD( u8 cmd );
void LCD_WR_DAT( u8* buff, u8 len );


// ---------------------------------------------------------------------------------------------------------------------


#endif

Lcd_Display.c 代码如下(示例):

// =====================================================================================================================
//
//
// =====================================================================================================================
void Oled_Init( u8 on_off )
{
    
	LCD_WR_CMD( 0xae );	//--turn off oled panel

	LCD_WR_CMD( 0x00 );	//--set low  column address
	LCD_WR_CMD( 0x10 );	//--set high column address

	LCD_WR_CMD( 0x40 );	//--set start line address. Set Mapping RAM Display Start Line (0x00 ~ 0x3F)

	LCD_WR_CMD( 0x81 );	//--set contrast control register ( 2 bytes command )
	LCD_WR_CMD( 0x66 );	//--Set SEG Output Current Brightness

	LCD_WR_CMD( 0xa1 );	//--Set Column x/SEG0 Mapping  (0xa0=Column 0 / 0xa1=Column 127)
	LCD_WR_CMD( 0xc8 );	//--Set COM/Row Scan Direction (0xc0=COM0==>COM7/ 0xc8=COM7==>COM0)
	LCD_WR_CMD( 0xa6 );	//--Inverse Display On/Off (0xa6=normal / 0xa7=Inverse) 

	LCD_WR_CMD( 0xa8 );	//--set multiplex ratio ( 2 bytes command )
	LCD_WR_CMD( 0x3f );	//--1/64 duty (1 to 64)

	LCD_WR_CMD( 0xd3 );	//--set display offset Shift Mapping RAM Counter ( 2 bytes command )
	LCD_WR_CMD( 0x00 );	//--not offset  (0x00 ~ 0x3F)

	LCD_WR_CMD( 0xd5 );	//--set display clock divide ratio/oscillator frequency ( 2 bytes command )
	LCD_WR_CMD( 0x80 );	//--set divide ratio, Set Clock as 100 Frames/Sec

	LCD_WR_CMD( 0xd9 );	//--set pre-charge period  ( 2 bytes command )
	LCD_WR_CMD( 0xf1 );	//--Set Pre-Charge as 15 Clocks & Discharge as 1 Clock

	LCD_WR_CMD( 0xda );	//--set com pins hardware configuration ( 2 bytes command )
	LCD_WR_CMD( 0x12 );
	
	LCD_WR_CMD( 0xdb );	//--set vcomh ( 2 bytes command )
	LCD_WR_CMD( 0x40 );	//--Set VCOM Deselect Level

	LCD_WR_CMD( 0x20 );	//--Set Page Addressing Mode( Double byte command )
	LCD_WR_CMD( 0x02 );	// (0x00/0x01/0x02) 02 = Page Addressing Mode (RESET)

	LCD_WR_CMD( 0x8d );	//--set Charge Pump enable/disable
	LCD_WR_CMD( 0x14 );	//--(0x10=disable / 0x14=during display Pump On) 

	LCD_WR_CMD( 0xa4 );	//--Disable Entire Display On (0xa4=RAM / 0xa5=ALL ON)
	LCD_WR_CMD( 0xa6 );	//--Inverse Display On/Off (0xa6=normal / 0xa7=Inverse) 
        
    if ( on_off > 0 )
		LCD_WR_CMD( 0xAF );	//--turn on oled panel
    else
        LCD_WR_CMD( 0xAE );	//--turn OFF oled panel(sleep mode)

}

// =====================================================================================================================
// 指定X(点)Y(点)清除区域
//
// =====================================================================================================================
void Display_Clr_area( uint8_t X_pos, uint8_t Y_pos, uint8_t X_length, uint8_t Y_length )
{
	uint8_t x,y;
	uint8_t X_end,Y_end;

    u8 pattern = 0;
    
	X_end = X_pos + X_length ;
	if( X_end > _SCR_X_MAX ) X_end = _SCR_X_MAX;

	Y_end = Y_pos + Y_length ;
	if( Y_end > _SCR_Y_MAX ) Y_end = _SCR_Y_MAX;


	for( y=Y_pos; y<Y_end; y++ )
	{
		LCD_WR_CMD( 0xB0 + y );

		LCD_WR_CMD( 0x10 | ((X_pos&0xF0) >> 4) );		// High colum address
		LCD_WR_CMD( 0x00 + ((X_pos&0x0F) >> 0) );		// Low colum asddress

		for( x=X_pos; x<X_end; x++ )
		{
			LCD_WR_DAT( &pattern, 1 );
		}
	}
}

// =====================================================================================================================
// 指定列(点)行(8点一行)显示图像
//
// =====================================================================================================================
void Display_Image( uint8_t X_pos, uint8_t Y_pos, uint8_t const *Buffer, uint8_t PicturNo )
{
	uint8_t  x,y;
	uint16_t j,TotalLength;
	uint8_t  X_end,Y_end;
	uint8_t  ImageData;

	if( X_pos > 127 ) X_pos = 127;
	if( Y_pos >   7 ) Y_pos = 7;


	TotalLength = Buffer[1] * Buffer[2];

	if( PicturNo >= Buffer[0] )
	{
		PicturNo = Buffer[0] - 1;
	}
    
	X_end = Buffer[1] + X_pos;
	Y_end = Buffer[2] + Y_pos;

	Buffer = Buffer + PicturNo*TotalLength + 3;
	
	j = 0;

	for( y=Y_pos; y<Y_end; y++ )
	{

		LCD_WR_CMD( 0xB0 + (y&0x7) );

		LCD_WR_CMD( 0x10 | ((X_pos&0xF0) >> 4) );		// High colum address
		LCD_WR_CMD( 0x00 + ((X_pos&0x0F) >> 0) );		// Low colum asddress

		if( X_end < _SCR_X_MAX )
		{
			for( x=X_pos; x<X_end; x++ )
			{
				if( RevColorDisp > 0 )	ImageData = ~Buffer[ j ];
				else					ImageData =  Buffer[ j ];
                
                LCD_WR_DAT(  &ImageData, 1 );
                
                
				j ++;
			}
		}
		else
		{
			for( x=X_pos; x<_SCR_X_MAX; x++ )
			{			
				if( RevColorDisp > 0 )	ImageData = ~Buffer[ j ];
				else					ImageData =  Buffer[ j ];
                
                LCD_WR_DAT(  &ImageData, 1 );
                
				j ++;
			}

			j = j + X_end - _SCR_X_MAX;
		}
	}
}

// =====================================================================================================================
// 显示字符串
// 输入:位置X(0~127),位置Y(0~7),字符串,字符串长度,字体选择(0-6*8,1-6*16,2--8*16,3--)
// =====================================================================================================================
void Display_String( U8 X_pos, U8 Y_pos, char const* str, U8 num, U8 Font_type )
{
	U8 i;
	U8 ch;
	U8 const *Buff_char;

	for( i=0; i<num; i++ )
	{
		ch = *(str +i);
		// 获得当前字库中位置
		ch = ch - 0x20;
		// 解码地址
		// 字体选择
		switch( Font_type )
		{
			case 1:
				Buff_char = CHAR_TAB8_16;
			break;

			case 2:
				Buff_char = CHAR_TAB12_16;
			break;

			default: // 字体0
				Buff_char = CHAR_TAB6_8;
			break;
		}

		// 显示字符
		Display_Image( X_pos, Y_pos, Buff_char, ch );

		// 下一个字符
		switch( Font_type )
		{
			case 1:
				X_pos = X_pos + 8;
			break;

			case 2:
				X_pos = X_pos + 12;
			break;

			default: // 字体0
				X_pos = X_pos + 6;
			break;
		}
        
		// 下一行转移
		if( X_pos >= _SCR_X_MAX )
		{
			X_pos = 0;
			// 下一行转移
			switch( Font_type )
			{
				case 1:
					Y_pos = Y_pos + 2;
				break;
	
				case 2:
					Y_pos = Y_pos + 2;
				break;
	
				default: // 字体0
					Y_pos = Y_pos + 1;
				break;
			}
		}
	}
}


Lcd_Display.h 代码如下(示例):

#ifndef	__LCD_DISPLAY_H__
#define	__LCD_DISPLAY_H__

#include "fonts.h"
#include "images.h"

// ==================================================================================
// 常量定义
// ==================================================================================
#define X_WIDTH 	128
#define Y_WIDTH 	64

#define	_SCR_X_MAX	128
#define	_SCR_Y_MAX	8

// ==================================================================================
// 变量申明
// ==================================================================================
// 显示状态 
extern uint8_t RevColorDisp;

// ==================================================================================
// 函数申明
// ==================================================================================
void Oled_Init( u8 on_off );

void Display_Clr_area( uint8_t X_pos, uint8_t Y_pos, uint8_t X_length, uint8_t Y_length );
void Display_Image( uint8_t X_pos, uint8_t Y_pos, uint8_t const *Buffer, uint8_t PicturNo );
void Display_String(U8 X_pos,U8 Y_pos,char const* str,U8 num,U8 Font_type );

void disp_demo( void );

// ---------------------------------------------------------------------------------------------------------------------


#endif


这段代码是Lcd_Display.c和Lcd_Display.h文件,主函数初始化 oled 就可以调用图像显示,图像显示OK ,就可以显示字符了。

图形/字符 取模方式:在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值