本帖最后由 lilijin1995 于 2023-1-5 14:22 编辑
今天给大家分享的是移植1.3寸I2C的OLED屏STM32驱动到CH32V203的过程。
一、硬件资料:
1. 1.3寸OLED屏
这里我们用的是中景园1.3寸OLED屏,原理图如下图,其中I2C地址是0x78,因为是默认DC接GND,R11是焊接的,另外我们可以看到这款屏幕用的是SSH1106驱动的,原理图可以打开附件。
2. SH1106
SH1106是一款单片CMOS OLED/PLED驱动器,带控制器,用于有机/聚合物发光二极管点阵图形显示系统。SH1106由132个分段、64个共用点组成,可支持132 x 64的最大显示分辨率。它是专为共阴极型OLED面板而设计的。SH1106内置对比度控制、显示RAM振荡器和高效Dc-Dc转换器,减少外部组件数量和功耗。SH1106适用于各种紧凑便携的应用,如手机的子显示器、计算器、MP3播放器等。规格书可以在附件中找到。
3. CH32V203评估板
评估板是我们自己设计的,主要用于项目前期的评估测试,如下图:
二、软件移植
官方提供的例程是有基于STM32、STM8和51的,我们选择STM32F103C8T6的,如附件OLED.zip
1.首先是OLED_Init,代码如下:
void OLED_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能A端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOD3,6
GPIO_SetBits(GPIOA, GPIO_Pin_5 | GPIO_Pin_7);
Delay_Ms(800);
OLED_WR_Byte(0xAE, OLED_CMD); //--display off
OLED_WR_Byte(0x02, OLED_CMD); //---set low column address
OLED_WR_Byte(0x10, OLED_CMD); //---set high column address
OLED_WR_Byte(0x40, OLED_CMD); //--set start line address
OLED_WR_Byte(0xB0, OLED_CMD); //--set page address
OLED_WR_Byte(0x81, OLED_CMD); // contract control
OLED_WR_Byte(0xFF, OLED_CMD); //--128
OLED_WR_Byte(0xA1, OLED_CMD); //set segment remap
OLED_WR_Byte(0xA6, OLED_CMD); //--normal / reverse
OLED_WR_Byte(0xA8, OLED_CMD); //--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3F, OLED_CMD); //--1/32 duty
OLED_WR_Byte(0xC8, OLED_CMD); //Com scan direction
OLED_WR_Byte(0xD3, OLED_CMD); //-set display offset
OLED_WR_Byte(0x00, OLED_CMD); //
OLED_WR_Byte(0xD5, OLED_CMD); //set osc division
OLED_WR_Byte(0x80, OLED_CMD); //
OLED_WR_Byte(0xD8, OLED_CMD); //set area color mode off
OLED_WR_Byte(0x05, OLED_CMD); //
OLED_WR_Byte(0xD9, OLED_CMD); //Set Pre-Charge Period
OLED_WR_Byte(0xF1, OLED_CMD); //
OLED_WR_Byte(0xDA, OLED_CMD); //set com pin configuartion
OLED_WR_Byte(0x12, OLED_CMD); //
OLED_WR_Byte(0xDB, OLED_CMD); //set Vcomh
OLED_WR_Byte(0x30, OLED_CMD); //
OLED_WR_Byte(0x8D, OLED_CMD); //set charge pump enable
OLED_WR_Byte(0x14, OLED_CMD); //
OLED_WR_Byte(0xAF, OLED_CMD); //--turn on oled panel
}
可以发现,这是通过IO口模拟的方式去实现的I2C,如果是一般情况IO口模拟的话,基本配置为开漏输出模式,但由于这里是驱动OLED,只需要写,不需要读取,所以这里配置推挽输出。命令注释已经很详细了,如果需要学习的话,直接看附件的 SH1106的规格书。
2.清屏
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_Clear(void) {
u8 i, n;
for (i = 0; i < 8; i++) {
OLED_WR_Byte(0xb0 + i, OLED_CMD); //设置页地址(0~7)
OLED_WR_Byte(0x02, OLED_CMD); //设置显示位置—列低地址
OLED_WR_Byte(0x10, OLED_CMD); //设置显示位置—列高地址
for (n = 0; n < 128; n++)
OLED_WR_Byte(0, OLED_DATA);
} //更新显示
}
清屏就是往芯片里面写0。
3.OLED_WR_Byte的实现
/**********************************************
// IIC Write Command
**********************************************/
void Write_IIC_Command(unsigned char IIC_Command) {
IIC_Start();
Write_IIC_Byte(0x78); //Slave address,SA0=0
IIC_Wait_Ack();
Write_IIC_Byte(0x00); //write command
IIC_Wait_Ack();
Write_IIC_Byte(IIC_Command);
IIC_Wait_Ack();
IIC_Stop();
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data) {
IIC_Start();
Write_IIC_Byte(0x78); //D/C#=0; R/W#=0
IIC_Wait_Ack();
Write_IIC_Byte(0x40); //write data
IIC_Wait_Ack();
Write_IIC_Byte(IIC_Data);
IIC_Wait_Ack();
IIC_Stop();
}
void OLED_WR_Byte(unsigned dat, unsigned cmd) {
if (cmd) {
Write_IIC_Data(dat);
} else {
Write_IIC_Command(dat);
}
}
上面是基本的写命令和数据的操作,是I2C的时序,I2C的时序图如下,
时序的要求也如下:
由于STM32和CH32的库函数名字基本一样的,所以我们基本不用修改太多。
4.OLED_ShowCHinese显示中文函数的实现
void OLED_ShowCHinese(u8 x, u8 y, u8 no) {
u8 t, adder = 0;
OLED_Set_Pos(x, y);
for (t = 0; t < 16; t++) {
OLED_WR_Byte(Hzk[2 * no][t], OLED_DATA);
adder += 1;
}
OLED_Set_Pos(x, y + 1);
for (t = 0; t < 16; t++) {
OLED_WR_Byte(Hzk[2 * no + 1][t], OLED_DATA);
adder += 1;
}
}
其中,x和y是屏幕的坐标,而no应该是中文字的索引,Hzk是中文字库,char Hzk[][32]例程没有加const,我们自己减少,因为家里const就是占用Flash空间,否则是SRAM空间。这个字库是通过取模软件生产的。
取模软件可以用PCtoLCD2002,它可以生成图片和中文字库。如果有条件的话,现在市面上也有自带中文库的OLED屏,或者可以自己加字库芯片。
5.OLED_ShowString显示字符串函数的实现
//显示一个字符号串
void OLED_ShowString(u8 x, u8 y, u8 *chr, u8 Char_Size) {
unsigned char j = 0;
while (chr[j]!='\0')
{ OLED_ShowChar(x,y,chr[j],Char_Size);
x+=8;
if(x>120) {x=0;y+=2;}
j++;
}
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(u8 x, u8 y, u8 chr, u8 Char_Size) {
unsigned char c = 0, i = 0;
c = chr - ' '; //得到偏移后的值
if (x > Max_Column - 1) {
x = 0;
y = y + 2;
}
if (Char_Size == 16) {
OLED_Set_Pos(x, y);
for (i = 0; i < 8; i++)
OLED_WR_Byte(F8X16[c * 16 + i], OLED_DATA);
OLED_Set_Pos(x, y + 1);
for (i = 0; i < 8; i++)
OLED_WR_Byte(F8X16[c * 16 + i + 8], OLED_DATA);
} else {
OLED_Set_Pos(x, y);
for (i = 0; i < 6; i++)
OLED_WR_Byte(F6x8[c][i], OLED_DATA);
}
}
可以看到,这个函数提供了F8X16和F6x8两个不同点阵的ASCII码表,F6x8是6*8点阵的,F8X16是8*16点阵的,乘积越大,字符串字体越大。
6.OLED_DrawBMP显示图片函数的实现
void OLED_DrawBMP(unsigned char x0, unsigned char y0, unsigned char x1,
unsigned char y1, unsigned char BMP[]) {
unsigned int j = 0;
unsigned char x, y;
if (y1 % 8 == 0)
y = y1 / 8;
else
y = y1 / 8 + 1;
for (y = y0; y < y1; y++) {
OLED_Set_Pos(x0, y);
for (x = x0; x < x1; x++) {
OLED_WR_Byte(BMP[j++], OLED_DATA);
}
}
}
图片我们的显示关键不是这个函数的实现,关键在于取模,图片库也是取模软件取的,例程是这样调用图片库显示的
OLED_DrawBMP(0,0,128,8,BMP1);其中unsigned char BMP1[] ,BMP1是普通变量,这将会占用非常多的SRAM,SRAM非常小,所以不建议这样,我们可以改一下,将其定义为只读的,const unsigned char BMP1[],调用时候强制转换一下:OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);
最后在main函数中实现的中文、字符串、图片显示如下:
int main(void) {
u8 t;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n", SystemCoreClock);
printf("This is printf example\r\n");
OLED_Init(); //初始化OLED
OLED_Clear();
t=' ';
OLED_ShowCHinese(0, 0, 0); //中
OLED_ShowCHinese(18, 0, 1); //景
OLED_ShowCHinese(36, 0, 2); //园
OLED_ShowCHinese(54, 0, 3); //电
OLED_ShowCHinese(72, 0, 4); //子
OLED_ShowCHinese(90, 0, 5); //科
OLED_ShowCHinese(108, 0, 6); //技
while(1)
{
OLED_Clear();
OLED_ShowCHinese(0,0,0); //中
OLED_ShowCHinese(18,0,1); //景
OLED_ShowCHinese(36,0,2); //园
OLED_ShowCHinese(54,0,3); //电
OLED_ShowCHinese(72,0,4); //子
OLED_ShowCHinese(90,0,5); //科
OLED_ShowCHinese(108,0,6); //技
OLED_ShowString(6,3,"1.30' OLED TEST",16);
//OLED_ShowString(8,2,"ZHONGJINGYUAN");
// OLED_ShowString(20,4,"2014/05/01");
OLED_ShowString(0,6,"ASCII:",16);
OLED_ShowString(63,6,"CODE:",16);
OLED_ShowChar(48,6,t,16);//显示ASCII字符
t++;
if(t>'~')t=' ';
OLED_ShowNum(103,6,t,3,16);//显示ASCII字符的码值
Delay_Ms(1000);
OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//图片显示(图片显示慎用,生成的字表较大,会占用较多空间,FLASH空间8K以下慎用)
Delay_Ms(1000);
}
}
Delay_Ms是CH32中实现的,所以需要修改一下。
最终实现的效果图如下:
|
此文章已获得独家原创/原创奖标签,著作权归21ic所有,未经允许禁止转载。
打赏榜单
21ic小管家 打赏了 30.00 元 2023-02-08 理由:签约作者奖励
|