SC32F10TC8拥有2个TWI通信接口,可配置为主模式或从模式,从模式下支持时钟延展,通信速率高达 1Mbps,TWI0 支持 DMA。
TWI(Two-Wire Interface)是I2C协议的另一种名称,二者在电气特性、协议规范和功能实现上完全一致。这是Atmel(现Microchip)早年为避免支付Philips(I2C商标持有者)的专利费而采用的命名方式,后被许多厂商沿用。
本文将使用TWI总线驱动I2C接口的OLED屏。
1、TWI初始化
<pre>void SC_TWI0_Init(void)
{
/*GPIOInit_PC10 Struct*/GPIO_InitTypeDef GPIOInit_PC10_Struct;
GPIOInit_PC10_Struct.GPIO_Pin = GPIO_Pin_10;
GPIOInit_PC10_Struct.GPIO_Mode = GPIO_Mode_IN_PU;
GPIOInit_PC10_Struct.GPIO_DriveLevel = 0;
GPIO_Init(GPIOC,&GPIOInit_PC10_Struct);/*SCL0A端口初始化*/
/*GPIOInit_PC11 Struct*/GPIO_InitTypeDef GPIOInit_PC11_Struct;
GPIOInit_PC11_Struct.GPIO_Pin = GPIO_Pin_11;
GPIOInit_PC11_Struct.GPIO_Mode = GPIO_Mode_IN_PU;
GPIOInit_PC11_Struct.GPIO_DriveLevel = 0;
GPIO_Init(GPIOC,&GPIOInit_PC11_Struct);/*SDA0A端口初始化*/
RCC_APB0PeriphClockCmd(RCC_APB0Periph_TWI0, ENABLE);
/*Init Struct*/TWI_InitTypeDef TWI_InitStruct;
TWI_InitStruct.TWI_Ack = TWI_Ack_Enable;
TWI_InitStruct.TWI_Prescaler = TWI_PRESCALER_1024;
TWI_InitStruct.TWI_Stretch = TWI_Stretch_Enable;
TWI_InitStruct.TWI_SlaveAdress = 0x3C;
TWI_InitStruct.TWI_GeneralCall = TWI_GeneralCall_Disable;
TWI_Init(TWI0, &TWI_InitStruct);
TWI_PinRemapConfig(TWI0,TWI_PinRemap_A);
/*INT*/TWI_ITConfig(TWI0,TWI_IT_INT,ENABLE);
/*DMA_TX*/TWI_DMACmd(TWI0,TWI_DMAReq_TX,ENABLE);
/*DMA_RX*/TWI_DMACmd(TWI0,TWI_DMAReq_RX,ENABLE);
/*Init Struct*/NVIC_SetPriority(TWI0_IRQn,2);/*TWI0*/
NVIC_EnableIRQ(TWI0_IRQn);
TWI_Cmd(TWI0,ENABLE);
}</pre>
复制代码OLED 7位地址为0x3C
PC10:SCL
PC11:SDA
速率:32Mhz/1024=32Khz,应该可以设得更大一些
I2C应答使能,中断使能。
2、I2C写字节
<pre>static void OLED_WR_Byte(uint8_t dat,uint8_t mode)
{
/* 发送起始信号 */
TWI_GenerateSTART(TWI0, ENABLE);
Delay();
while(TWI0_Flag1 == RESET); //等待起始信号发送完成
TWI0_Flag1 = RESET;
/* 发送地址 */
TWI_Send7bitAddress(TWI0, 0x3C, TWI_Command_Write);
while(TWI0_Flag1 == RESET);
TWI0_Flag1 = RESET;
Delay();
/* 等待收到从机的ACK */
while(TWI0_Flag2 != SET);
TWI0_Flag2 = RESET;
if(mode)TWI_SendData(TWI0, 0x40); //写数据
else TWI_SendData(TWI0, 0x00); //写命令
while(TWI0_Flag1 == RESET);//等待信号发送完成
TWI0_Flag1 = RESET;
Delay();
/* 发送数据 */
TWI_SendData(TWI0, dat);
while(TWI0_Flag1 == RESET);
TWI0_Flag1 = RESET;
Delay();
/* 发送停止信号 */
TWI_GenerateSTOP(TWI0, ENABLE);
while(TWI_GetStateMachine(TWI0) != TWI_Master_Idle);
Delay();
}</pre>
复制代码3、OLED初始化
<pre> void OLED_Init(void)
{
OLED_WR_Byte(0xAE,OLED_CMD);//显示关闭 0xAF是开启 0xAE是关闭
OLED_WR_Byte(0x00,OLED_CMD);//设置低列地址
OLED_WR_Byte(0x10,OLED_CMD);//设置高列地址
OLED_WR_Byte(0x40,OLED_CMD);//设置起始行地址(0x00~0x3F)
OLED_WR_Byte(0x81,OLED_CMD);//对比度设置指令
OLED_WR_Byte(0xCF,OLED_CMD);// 设置输出电流亮度
OLED_WR_Byte(0xA1,OLED_CMD);//设置分段/列映射,IIC需要设置为0xA1
OLED_WR_Byte(0xC8,OLED_CMD);//设置COM扫描方向 0xc0上下反置,COM0到COM N-1 左到右 0xc8正常 COM N-1到COM0 右到左
OLED_WR_Byte(0xA6,OLED_CMD);//设置显示方式为正常显示
OLED_WR_Byte(0xA8,OLED_CMD);//设置分辨率(1 to 64)
OLED_WR_Byte(0x3f,OLED_CMD);//分辨率为128*64:0x3f
OLED_WR_Byte(0xD3,OLED_CMD);//设置显示偏移
OLED_WR_Byte(0x00,OLED_CMD);//默认值00 无偏移
OLED_WR_Byte(0xd5,OLED_CMD);//设置显示时钟分频/振荡器频率
OLED_WR_Byte(0x80,OLED_CMD);//将时钟设置为100帧/秒
OLED_WR_Byte(0xD9,OLED_CMD);//设置预充电时期
OLED_WR_Byte(0xF1,OLED_CMD);//预充电设为15时钟,放电设为1时钟
OLED_WR_Byte(0xDA,OLED_CMD);//-设置COM硬件引脚配置,适应分辨率
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD);//设置VCOMH
OLED_WR_Byte(0x40,OLED_CMD);//设置VCOM取消选择级别
OLED_WR_Byte(0x20,OLED_CMD);//页面寻址模式(0x00/0x01/0x02)
OLED_WR_Byte(0x02,OLED_CMD);//页寻址
OLED_WR_Byte(0x8D,OLED_CMD);//充电泵设置可用或者不可用
OLED_WR_Byte(0x14,OLED_CMD);//可用
OLED_WR_Byte(0xA4,OLED_CMD);//0xa4,输出遵循RAM内容 0xa5,输出忽略RAM内容
OLED_WR_Byte(0xA6,OLED_CMD);//设置显示方式,正常显示:0xA6,反相显示:0xA7
OLED_Clear();
OLED_WR_Byte(0xAF,OLED_CMD);//设置完毕,显示开启
OLED_ColorTurn(0);//0正常显示,1 反色显示
OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
}</pre>
复制代码4、其他函数
<pre>//开启OLED显示
void OLED_DisPlay_On(void)
{
OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵
OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕
}
//关闭OLED显示
void OLED_DisPlay_Off(void)
{
OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵
OLED_WR_Byte(0xAF,OLED_CMD);//关闭屏幕
}</pre>
复制代码<pre>uint8_t OLED_GRAM[144][8];
//用于刷新OLED显示内容
void OLED_Refresh(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
OLED_WR_Byte(0x00,OLED_CMD); //设置低列起始地址
OLED_WR_Byte(0x10,OLED_CMD); //设置高列起始地址
for(n=0;n<128;n++)
OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);
}
}</pre>
复制代码<pre>//屏幕旋转180度
void OLED_DisplayTurn(uint8_t i)
{
if(i==0)
{
OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
OLED_WR_Byte(0xA1,OLED_CMD);
}
if(i==1)
{
OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
OLED_WR_Byte(0xA0,OLED_CMD);
}
}
</pre><pre>
</pre>
复制代码<pre>//清屏函数
void OLED_Clear(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
for(n=0;n<128;n++)
{
OLED_GRAM[n][i]=0;//清除所有数据
}
}
OLED_Refresh();//更新显示
}</pre>
复制代码<pre>//清除一个点
//x:0~127
//y:0~63
void OLED_ClearPoint(uint8_t x,uint8_t y)
{
u8 i,m,n;
i=y/8;
m=y%8;
n=1<<m;
OLED_GRAM[x][i]=~OLED_GRAM[x][i];
OLED_GRAM[x][i]|=n;
OLED_GRAM[x][i]=~OLED_GRAM[x][i];
}</pre>
复制代码<pre>//画点
//x:0~127
//y:0~63
void OLED_DrawPoint(uint8_t x,uint8_t y)
{
u8 i,m,n;
i=y/8;
m=y%8;
n=1<<m;
OLED_GRAM[x][i]|=n;
}</pre>
复制代码5、显示字符
<pre>//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//size:选择字体 12/16/24
//取模方式 逐列式
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size1)
{
u8 i,m,temp,size2,chr1;
u8 y0=y;
size2=(size1/8+((size1%8)?1:0))*(size1/2); //得到字体一个字符对应点阵集所占的字节数
chr1=chr-' '; //计算偏移后的值
for(i=0;i<size2;i++)
{
if(size1==12)
{temp=asc2_1206[chr1][i];} //调用1206字体
else if(size1==16)
{temp=asc2_1608[chr1][i];} //调用1608字体
else if(size1==24)
{temp=asc2_2412[chr1][i];} //调用2412字体
else return;
for(m=0;m<8;m++) //写入数据
{
if(temp&0x80)OLED_DrawPoint(x,y);
else OLED_ClearPoint(x,y);
temp<<=1;
y++;
if((y-y0)==size1)
{
y=y0;
x++;
break;
}
}
}
}
//显示字符串
//x,y:起点坐标
//size1:字体大小
//*chr:字符串起始地址
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t size1)
{
while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!
{
OLED_ShowChar(x,y,*chr,size1);
x+=size1/2;
if(x>128-size1) //换行
{
x=0;
y+=2;
}
chr++;
}
}
</pre>
复制代码<pre> OLED_ShowString(0,0,"LB2001",16);
OLED_ShowString(0,17,"SC32F10TC8",16);
OLED_ShowString(0,34,"hello world!",16);
OLED_Refresh();</pre>
复制代码