模拟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 ,就可以显示字符了。
图形/字符 取模方式: