引脚说明:
VCC:电源正极,接3.3V
GND:电源负极
SCL:时钟线
SDA:数据线
IIC部分:
这里使用的是P6.5,P.6进行IIC通信
IIC代码如下
#include "./I2C/i2c.h"
void i2c_init(void)
{
GPIO_setAsOutputPin(I2C_SCL_GPIO_PORT,I2C_SCL_GPIO_PIN);//输出模式
GPIO_setAsOutputPin(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN);//输出模式
}
//开始i2c传输
void i2c_star(void)
{
GPIO_setAsOutputPin(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN);//输出模式
I2C_SDA(1);
I2C_SCL(1);
__delay_cycles(2);
I2C_SDA(0);
__delay_cycles(2);
I2C_SCL(0);
__delay_cycles(2);
}
//停止i2c传输
void i2c_stop(void)
{
GPIO_setAsOutputPin(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN);//输出模式
I2C_SDA(0); /* STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号 */
__delay_cycles(2);
I2C_SCL(1);
__delay_cycles(2);
I2C_SDA(1); /* 发送I2C总线结束信号 */
__delay_cycles(2);
}
//等待应答。1,接收应答失败,0,接收应答成功
uint8_t i2c_wait_ack(void)
{
uint8_t waittime = 0;
uint8_t rack = 0;
I2C_SDA(1); /* 主机释放SDA线(此时外部器件可以拉低SDA线) */
__delay_cycles(2);
I2C_SCL(1); /* SCL=1, 此时从机可以返回ACK */
__delay_cycles(2);
GPIO_setAsInputPin(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN);//输入模式
while (GPIO_getInputPinValue(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN)) /* 等待应答 */
{
waittime++;
if (waittime > 250)
{
UART_printf(USCI_A0_BASE, "waitout\r\n");
i2c_stop();
rack = 1;
break;
}
__delay_cycles(2);
}
I2C_SCL(0); /* SCL=0, 结束ACK检查 */
__delay_cycles(2);
return rack;
}
//ack应答
void i2c_ack(void)
{
GPIO_setAsOutputPin(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN);//输出模式
I2C_SDA(0); /* SCL 0 -> 1 时SDA = 0,表示应答 */
__delay_cycles(2);
I2C_SCL(1);
__delay_cycles(2);
I2C_SCL(0);
__delay_cycles(2);
I2C_SDA(1); /* 主机释放SDA线 */
__delay_cycles(2);
}
//ack非应答
void i2c_nack(void)
{
GPIO_setAsOutputPin(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN);//输出模式
I2C_SDA(1); /* SCL 0 -> 1 时 SDA = 1,表示不应答 */
__delay_cycles(2);
I2C_SCL(1);
__delay_cycles(2);
I2C_SCL(0);
__delay_cycles(2);
}
//发送一个字节数据,高位先发
void i2c_write_byte(uint8_t dat)
{
int i;
uint8_t temp=0;
GPIO_setAsOutputPin(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN);//输出模式
for(i=7;i>=0;i--)
{
temp=(dat>>i) & 0x01;
I2C_SDA(temp);
__delay_cycles(2);
I2C_SCL(1);
__delay_cycles(2);
I2C_SCL(0);
}
I2C_SDA(1); /* 发送完成, 主机释放SDA线 */
}
//读一个字节数据,高位先读
uint8_t i2c_read_byte(uint8_t ack)
{
uint8_t i;
uint8_t dat=0;
GPIO_setAsInputPin(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN);//输入模式
for(i=0;i<8;i++)
{
dat<<=1;
I2C_SCL(1);
__delay_cycles(2);
if(GPIO_getInputPinValue(I2C_SDA_GPIO_PORT,I2C_SDA_GPIO_PIN))
dat ++;
I2C_SCL(0);
__delay_cycles(2);
}
if (!ack)
{
i2c_nack(); /* 发送nACK */
}
else
{
i2c_ack(); /* 发送ACK */
}
return dat;
}
Oled代码如下
#include "./OLED/oled.h"
#include "./I2C/i2c.h"
#include "./OLED/oledfont.h"
//OLED的显存, 每个字节表示8个像素, 128,表示有128列, 8表示有64行, 高位表示第行数.
static uint8_t g_oled_gram[128][8];
//写命令
void oled_write_cmd(uint8_t cmd)
{
i2c_star();
i2c_write_byte(0x78);//oled地址
i2c_wait_ack();
i2c_write_byte(0x00);//写命令命令
i2c_wait_ack();
i2c_write_byte(cmd);//写命令
i2c_wait_ack();
i2c_stop();
}
//写数据
void oled_write_data(uint8_t dat)
{
i2c_star();
i2c_write_byte(0x78);//oled地址
i2c_wait_ack();
i2c_write_byte(0x40);//写数据命令
i2c_wait_ack();
i2c_write_byte(dat);//写数据
i2c_wait_ack();
i2c_stop();
}
//全屏更新
void oled_all_refresh_gram(void)
{
uint8_t i,j;
oled_display_off();
for(i=0;i<8;i++)//设置为8页
{
oled_write_cmd(0xb0 |i);//设置页
oled_write_cmd(0x00);//设置列(低4bit)因为128列,至少要7bit
oled_write_cmd(0x10);//设置列(高4bit)
for(j=0;j<128;j++)
{
oled_write_data(g_oled_gram[j][i]);//写入数据
}
}
oled_display_on();
}
//区域更新
void oled_area_refresh_gram(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2)
{
uint8_t pos1,pos2,i,j;
if (x1 > 127 || y1 > 63) return; /* 超出范围了. */
if (x2 > 127 || y2 > 63) return; /* 超出范围了. */
pos1=y1/8;
pos2=y2/8;
for(i=pos1;i<=pos2;i++)//设置为8页
{
oled_write_cmd(0xb0 |i);//设置页
for(j=x1;j<=x2;j++)
{
oled_write_cmd(j&0xf);//设置列(低4bit)因为128列,至少要7bit
oled_write_cmd((j>>4)|0x10);//设置列(高4bit)
oled_write_data(g_oled_gram[j][i]);//写入数据
}
}
}
//全屏清屏
void oled_all_clear(void)
{
uint8_t i, j;
for (i = 0; i < 8; i++)
for (j = 0; j < 128; j++)
g_oled_gram[j][i] = 0X00;
oled_all_refresh_gram(); /* 更新显示 */
}
//区域清屏
void oled_area_clear(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2)
{
uint8_t pos1,pos2,i,j;
if (x1 > 127 || y1 > 63) return; /* 超出范围了. */
if (x2 > 127 || y2 > 63) return; /* 超出范围了. */
pos1=y1/8;
pos2=y2/8;
//printf("x1=%d,y1=%d,x2=%d,y2=%d,pos1=%d,pos2=%d\r\n",x1,y1,x2,y2,pos1,pos2);
for(i=pos1;i<=pos2;i++)//设置为8页
for(j=x1;j<=x2;j++)
{
//printf("i=%d,j=%d\r\n",i,j);
g_oled_gram[j][i] = 0X00;
}
}
//全屏点亮
void oled_fill(void)
{
uint8_t i, j;
for (i = 0; i < 8; i++)
for (j = 0; j < 128; j++)
g_oled_gram[j][i] = 0XFF;
oled_all_refresh_gram(); /* 更新显示 */
}
void oled_rect_fill(uint8_t y,uint8_t mode)
{
uint8_t i;
if(mode==1)
{
for (i = 0; i < 128; i++)
{
g_oled_gram[i][y*2] = 0XFF;
g_oled_gram[i][y*2+1] = 0XFF;
}
}
else
{
for (i = 0; i < 128; i++)
{
g_oled_gram[i][y*2] = 0X00;
g_oled_gram[i][y*2+1] = 0X00;
}
}
}
//打开
void oled_display_on(void)
{
oled_write_cmd(0X8D); /* SET DCDC命令 */
oled_write_cmd(0X14); /* DCDC ON */
oled_write_cmd(0XAF); /* DISPLAY ON */
}
//关闭
void oled_display_off(void)
{
oled_write_cmd(0X8D); /* SET DCDC命令 */
oled_write_cmd(0X10); /* DCDC OFF */
oled_write_cmd(0XAE); /* DISPLAY OFF */
}
//oled初始化
void oled_init(void)
{
i2c_init();//i2c初始化
__delay_cycles(1000);//延时1ms,重要
oled_write_cmd(0xAE); /* 关闭显示 */
oled_write_cmd(0xD5); /* 设置时钟分频因子,震荡频率 */
oled_write_cmd(0xf0); /* [3:0],分频因子;[7:4],震荡频率 */
oled_write_cmd(0xA8); /* 设置驱动路数 */
oled_write_cmd(0X3F); /* 默认0X3F(1/64) */
oled_write_cmd(0xD3); /* 设置显示偏移 */
oled_write_cmd(0X00); /* 设置列(低4bit)*/
oled_write_cmd(0x10);//设置列(高4bit)
oled_write_cmd(0x40); /* 设置显示开始行 [5:0],行数. */
oled_write_cmd(0x8D); /* 电荷泵设置 */
oled_write_cmd(0x14); /* bit2,开启/关闭 */
oled_write_cmd(0x20); /* 设置内存地址模式 */
oled_write_cmd(0x02); /* [1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10; */
oled_write_cmd(0xb0); //开启地址0-7
oled_write_cmd(0xA1); /* 段重定义设置,bit0:0,0->0;1,0->127; */
oled_write_cmd(0xC8); /* 设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数 */
oled_write_cmd(0xDA); /* 设置COM硬件引脚配置 */
oled_write_cmd(0x12); /* 128*64 */
oled_write_cmd(0x81); /* 对比度设置 */
oled_write_cmd(0xEF); /* 1~255;默认0X7F (亮度设置,越大越亮) */
oled_write_cmd(0xD9); /* 设置预充电周期 */
oled_write_cmd(0x22); //充电时间
oled_write_cmd(0xf1); /* [3:0],PHASE 1;[7:4],PHASE 2; */
oled_write_cmd(0xDB); /* 设置VCOMH 电压倍率 */
oled_write_cmd(0x20); //0x20,0.77xVcc
oled_write_cmd(0xA4); /* 全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏) */
oled_write_cmd(0xA6); /* 设置显示方式;bit0:1,反相显示;0,正常显示 */
oled_write_cmd(0xAF); /* 开启显示 */
oled_all_clear();
}
//画点
void oled_draw_point(uint8_t x, uint8_t y, uint8_t dot)
{
uint8_t pos, bx, temp = 0;
if (x > 127 || y > 63) return; /* 超出范围了. */
pos = y / 8; /* 计算GRAM里面的y坐标所在的字节, 每个字节可以存储8个行坐标 */
bx = y % 8; /* 取余数,方便计算y在对应字节里面的位置,及行(y)位置 */
temp = 1 << bx; /* 高位表示低行号, 得到y对应的bit位置,将该bit先置1 */
if (dot) /* 画实心点 */
{
g_oled_gram[x][pos] |= temp;
}
else /* 画空点,即不显示 */
{
g_oled_gram[x][pos] &= ~temp;
}
}
// 显示字符
void oled_show_char(uint8_t x, uint8_t y, uint8_t chr, uint8_t size, uint8_t mode)
{
uint8_t temp, t, t1;
uint8_t y0 = y;
uint8_t *pfont = 0;
uint8_t csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2); /* 得到字体一个字符对应点阵集所占的字节数 */
chr = chr - ' '; /* 得到偏移后的值,因为字库是从空格开始存储的,第一个字符是空格 */
if (size == 12) /* 调用1206字体 */
{
pfont = (uint8_t *)oled_asc2_1206[chr];
}
else if (size == 16) /* 调用1608字体 */
{
pfont = (uint8_t *)oled_asc2_1608[chr];
}
else if (size == 24) /* 调用2412字体 */
{
pfont = (uint8_t *)oled_asc2_2412[chr];
}
else /* 没有的字库 */
{
return;
}
for (t = 0; t < csize; t++)
{
temp = pfont[t];
for (t1 = 0; t1 < 8; t1++)
{
if (temp & 0x80)oled_draw_point(x, y, mode);
else oled_draw_point(x, y, !mode);
temp <<= 1;
y++;
if ((y - y0) == size)
{
y = y0;
x++;
break;
}
}
}
}
//平方函数, m^n
static uint32_t oled_pow(uint8_t m, uint8_t n)
{
uint32_t result = 1;
while (n--)
{
result *= m;
}
return result;
}
//显示len个数字
void oled_show_num(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size,uint8_t mode)
{
uint8_t t, temp;
uint8_t enshow = 0;
for (t = 0; t < len; t++) /* 按总显示位数循环 */
{
temp = (num / oled_pow(10, len - t - 1)) % 10; /* 获取对应位的数字 */
if (enshow == 0 && t < (len - 1)) /* 没有使能显示,且还有位要显示 */
{
if (temp == 0)
{
oled_show_char(x + (size / 2)*t, y, ' ', size, mode); /* 显示空格,站位 */
continue; /* 继续下个一位 */
}
else
{
enshow = 1; /* 使能显示 */
}
}
oled_show_char(x + (size / 2)*t, y, temp + '0', size, mode); /* 显示字符 ,因为是通过两页写的所以size除2*/
}
}
//显示数据
void oled_show_data(uint8_t x, uint8_t y, uint32_t integer,uint32_t decimal, uint8_t integer_len,uint8_t decimal_len, uint8_t size,uint8_t mode)
{
oled_show_num(x,y,integer,integer_len,size,mode);
oled_show_char(x+integer_len*8,y,'.',size,mode);
oled_show_num(x+integer_len*8+8,y,decimal,decimal_len,size,mode);
}
//显示字符串
void oled_show_string(uint8_t x, uint8_t y, const char *p, uint8_t size,uint8_t mode)
{
while ((*p <= '~') && (*p >= ' ')) /* 判断是不是非法字符! */
{
if (x > (128 - (size / 2))) /* 宽度越界 */
{
x = 0;
y += size; /* 换行 */
}
if (y > (64 - size)) /* 高度越界 */
{
y = x = 0;
oled_all_clear();
}
oled_show_char(x, y, *p, size,mode); /* 显示一个字符 */
x += size / 2; /* ASCII字符宽度为汉字宽度的一半 因为是通过两页写的所以size除2*/
p++;
}
}
//显示半个汉字
void oled_show_halfway_character(uint8_t x, uint8_t y,char *p,uint8_t size,uint8_t mode)
{
uint8_t temp, t, t1;
uint8_t y0 = y;
uint8_t *pfont = 0;
uint8_t csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2); /* 得到字体一个字符对应点阵集所占的字节数 */
pfont = (uint8_t *)p;
for (t = 0; t < csize; t++)
{
temp = pfont[t];
for (t1 = 0; t1 < 8; t1++)
{
if(mode==1)
{
if (temp & 0x80)oled_draw_point(x, y,1);
else oled_draw_point(x, y,0);
}
else
{
if (temp & 0x80)oled_draw_point(x, y,0);
else oled_draw_point(x, y,1);
}
temp <<= 1;
y++;
if ((y - y0) == size)
{
y = y0;
x++;
break;
}
}
}
}
//显示多个汉字
void led_show_character(uint8_t x, uint8_t y,char *p,uint8_t size,uint8_t chara_len,uint8_t mode)
{
uint8_t i=0;
for(i=0;i<chara_len*2;i++)
{
oled_show_halfway_character(x+size/2*i,y,p,size,mode);
p+=size;
}
}