一、效果展示
启动界面 实时时钟界面 时间修改界面
二、模块介绍
1.GND电源地
2. VCC电源正(3~5.5V)
3. SCL OLED的D0脚,在IIC通信中为时钟管脚
4. SDA OLED的D1脚,在IIC通信中为数据管脚
本屏所用的驱动IIC为SSD1306; OLED本身是没有显存的,它的显存是依赖于SSD1306提供的(如TFTLCD是本身自带显存,利用FSMC来进行控制)。
SSD1306的显存总共为128*64bit大小,SSD1306将这些显存分为了8页。每页包含了128个字节,总共8页,这样刚好是128*64的点阵大小。
四针的oled屏幕是通过IIC总线驱动,因此,其启动、停止、读、写等程序都是通用的,我会将程序放在下面,大家直接移植即可。
三、文字取模
对于大家使用oled显示,最主要的是“文字取模”、“设置文字显示的位置”
文字取模:就是将自己想显示的文字转换成十六进制数组,如此单片机才能识别从而显示。
取模软件:PCtoLCD2002
步骤:
1、选择文字模式
2、配置取模模式
3、输入文字,生成字模
之后将生成的数组,复制到keil5工程相应的文件中,直接在函数中调用编号即可
注意:粘贴过来的一串十六进制,在数组中的位置应该与你函数中调用的编号一致,如此才能通过函数完成相应的显示(如:第二个大红框代表的是“欢”,在数组的编号为1,函数中输入1即可)
调用OLED_ShowCN(u8 x,u8 y,u8 z)函数显示,修改第三个变量,为想要显示的文字在数组中的编号。
四、相关程序
main.c
#include "stm32f10x.h"#include "led.h"#include "delay.h"#include "deep.h"#include "key.h"#include "sys.h"#include "oled_i2c.h"#include "rtc.h"int main(void){u8 Dis_mode;//显示状态标志 0:显示传感器数据界面1:显示日期时间界面delay_init();//延时函数初始化NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组为组2:2位抢占优先级,2位响应优先级LED_Init();KEY_Init();OLED_Init(); //OLED初始化delay_ms(50);OLED_CLS();//清屏OLED_ShowCN(16*2,1,1);//欢OLED_ShowCN(16*3,1,2);//迎OLED_ShowCN(16*4,1,3);//使OLED_ShowCN(16*5,1,4); //用OLED_ShowCN(8,4,5); //耀OLED_ShowCN(16+8,4,6);//鑫OLED_ShowCN(16*2+8,4,7); //电OLED_ShowCN(16*3+8,4,8); //子OLED_ShowCN(16*4+8,4,9);//显OLED_ShowCN(16*5+8,4,10); //示OLED_ShowCN(16*6+8,4,11); //屏RTC_Init();delay_ms(5000);OLED_CLS(); //清屏Dis_mode=1;while(1){Key_set();//按键检测switch(Dis_mode){case 1://在显示时间界面RTC_Display();//显示时钟break;}}}
oled_iic.c
/************************************************************************************* Description:128*64点阵的OLED显示屏驱动文件,仅适用于惠特自动化(heltec.taobao.com)的SD1306驱动IIC通信方式显示屏** Others: none;** Function List:*1. void I2C_Configuration(void) -- 配置CPU的硬件I2C* 2. void I2C_WriteByte(uint8_t addr,uint8_t data) -- 向寄存器地址写一个byte的数据* 3. void WriteCmd(unsigned char I2C_Command) -- 写命令* 4. void WriteDat(unsigned char I2C_Data) -- 写数据* 5. void OLED_Init(void) -- OLED屏初始化* 6. void OLED_SetPos(unsigned char x, unsigned char y) -- 设置起始点坐标* 7. void OLED_Fill(unsigned char fill_Data) -- 全屏填充* 8. void OLED_CLS(void) -- 清屏* 9. void OLED_ON(void) -- 唤醒* 10. void OLED_OFF(void) -- 睡眠* 11. void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize) -- 显示字符串(字体大小有6*8和8*16两种)* 12. void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N) -- 显示中文(中文需要先取模,然后放到codetab.h中)* 13. void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]) -- BMP图片** History: none;**************************************************************************************/#include "oled_i2c.h"#include "delay.h"#include "codetab.h"//externu8 display_1[4]; //当前状态(oled显示的汉字在数组中的索引)extern u8 display_time_flag;//初始化IICvoid IIC_Init2(void){ GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );//使能GPIOB时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); //PB6,PB7 输出高}//产生IIC起始信号void IIC_Start(void){SDA_OUT(); //sda线输出IIC_SDA=1;IIC_SCL=1;//delay_us(4); IIC_SDA=0;//START:when CLK is high,DATA change form high to low //delay_us(4);IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 } //产生IIC停止信号void IIC_Stop(void){SDA_OUT();//sda线输出IIC_SCL=0;IIC_SDA=0;//STOP:when CLK is high DATA change form low to high //delay_us(4);IIC_SCL=1; IIC_SDA=1;//发送I2C总线结束信号//delay_us(4); }//等待应答信号到来//返回值:1,接收应答失败//0,接收应答成功u8 IIC_Wait_Ack(void){u8 ucErrTime=0;SDA_IN();//SDA设置为输入IIC_SDA=1;/*delay_us(1)*/; IIC_SCL=1;/*delay_us(1)*/; while(READ_SDA){ucErrTime++;if(ucErrTime>250){IIC_Stop();return 1;}}IIC_SCL=0;//时钟输出0return 0;} //产生ACK应答void IIC_Ack(void){IIC_SCL=0;SDA_OUT();IIC_SDA=0;//delay_us(2);IIC_SCL=1;//delay_us(2);IIC_SCL=0;}//不产生ACK应答void IIC_NAck(void){IIC_SCL=0;SDA_OUT();IIC_SDA=1;//delay_us(2);IIC_SCL=1;//delay_us(2);IIC_SCL=0;}//IIC发送一个字节//返回从机有无应答//1,有应答//0,无应答void IIC_Send_Byte(u8 txd){u8 t; SDA_OUT(); IIC_SCL=0;//拉低时钟开始数据传输for(t=0;t>7;if((txd&0x80)>>7)IIC_SDA=1;elseIIC_SDA=0;txd<<=1; //delay_us(2); //对TEA5767这三个延时都是必须的IIC_SCL=1;//delay_us(2); IIC_SCL=0;//delay_us(2);} } //读1个字节,ack=1时,发送ACK,ack=0,发送nACK u8 IIC_Read_Byte(unsigned char ack){unsigned char i,receive=0;SDA_IN();//SDA设置为输入for(i=0;i<8;i++ ){//IIC_SCL=0; delay_us(2);//IIC_SCL=1;receive<>4)|0x10);WriteCmd((x&0x0f)|0x01);}void OLED_Fill(unsigned char fill_Data)//全屏填充{unsigned char m,n;for(m=0;m<8;m++){WriteCmd(0xb0+m);//page0-page1WriteCmd(0x00);//low column start addressWriteCmd(0x10);//high column start addressfor(n=0;n 126) //写满一行换行{x = 0;y++;}OLED_SetPos(x,y);for(i=0;i 120){x = 0;y++;}OLED_SetPos(x,y);for(i=0;i<8;i++)WriteDat(F8X16[c*16+i]);//一维数组,c*16定位在第几行OLED_SetPos(x,y+1);//加了一页(按页存储,共8页)for(i=0;i<8;i++)WriteDat(F8X16[c*16+i+8]);x += 8;j++;}}break;}}//--------------------------------------------------------------// Prototype: void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)// Calls: // Parameters : x,y -- 起始点坐标(x:0~127, y:0~7); N:汉字在codetab.h中的索引// Description: 显示codetab.h中的汉字,16*16点阵//--------------------------------------------------------------void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N){unsigned char wm=0;unsigned intadder=32*N;OLED_SetPos(x , y);for(wm = 0;wm < 16;wm++){WriteDat(F16x16[adder]);adder += 1;}OLED_SetPos(x,y + 1);for(wm = 0;wm < 16;wm++){WriteDat(F16x16[adder]);adder += 1;}}//--------------------------------------------------------------// Prototype: void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]);// Calls: // Parameters : x0,y0 -- 起始点坐标(x0:0~127, y0:0~7); x1,y1 -- 起点对角线(结束点)的坐标(x1:1~128,y1:1~8)// Description: 显示BMP位图//--------------------------------------------------------------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;elsey = y1/8 + 1;for(y=y0;y<y1;y++){OLED_SetPos(x0,y);for(x=x0;x<x1;x++){WriteDat(BMP[j++]);}}}/****************************************************************************************函数输入:j→要显示的文字距左侧j个字节num→要显示的汉字个数, display_first[]→“当前状态”所在数组的索引 display_second[]→要显示的汉字所在数组中的索引*函数输出:无*函数功能:oled屏显示控制状态的界面****************************************************************************************/void control_part_display(u8 j,u8 num,u8 display_first[],u8 display_second[])//控制状态显示函数{u8 i;OLED_CLS();//清屏for(i=0;i<num;i++){if(i<4){OLED_ShowCN(16*(i+2),1,display_first[i]);//显示“当前状态”,起始位置距左侧32}OLED_ShowCN(8*j+16*(i),4,display_second[i]);//显示状态信息,比如“已为您打开灯”,起始位置距左侧8*jdelay_ms(50);}//display_time_flag=0; //oled屏返回时间界面标志位TIM_Cmd(TIM2, ENABLE); //到达状态界面后,打开TIM2,2s后触发中断oled返回总体显示界面}void control_all_display(u8 state[]){//OLED_CLS();//清屏OLED_ShowCN(16,0,24);//灯光(函数第三个参数是该汉字在取模数组中的索引)OLED_ShowCN(32,00,25);OLED_ShowCN(16,2,26);//风扇OLED_ShowCN(32,2,27);OLED_ShowCN(16,4,28);//窗帘OLED_ShowCN(32,4,29);OLED_ShowCN(16,6,30);//门switch(state[0]){case 0:OLED_ShowCN(16*4,0,22);//灯光关闭(函数第三个参数是该汉字在取模数组中的索引) OLED_ShowCN(16*5,0,23);break;case 1:OLED_ShowCN(16*4,0,20);//灯光打开 OLED_ShowCN(16*5,0,21);break;}switch(state[1]){case 0:OLED_ShowCN(16*4,2,22);//风扇关闭 OLED_ShowCN(16*5,2,23);break;case 1:OLED_ShowCN(16*4,2,20);//风扇打开 OLED_ShowCN(16*5,2,21);break;}switch(state[2]){case 0:OLED_ShowCN(16*4,4,22);//窗帘关闭 OLED_ShowCN(16*5,4,23);break;case 1:OLED_ShowCN(16*4,4,20);//窗帘打开 OLED_ShowCN(16*5,4,21);break;}switch(state[3]){case 0:OLED_ShowCN(16*4,6,22);//门关闭OLED_ShowCN(16*5,6,23);break;case 1:OLED_ShowCN(16*4,6,20);//门打开 OLED_ShowCN(16*5,6,21);break;}}void wen_shi_display(u8 num,u8 display_wen_shi1[]){u8 i;//unsigned char disp1[5];//OLED_CLS();//清屏for(i=0;i<num;i++){OLED_ShowCN(16*(i+2),1,display_wen_shi1[i]);//显示“当前温湿度”,起始位置距左侧32}OLED_ShowCN(16,4,45);//温OLED_ShowCN(32,4,47);//度OLED_ShowCN(85,4,50); //℃OLED_ShowCN(16,6,46);//湿OLED_ShowCN(32,6,47);//度OLED_ShowStr(85,6,"%",2); //%}
oled_iic.h
#ifndef __OLED_I2C_H#define__OLED_I2C_H#include "stm32f10x.h"#define OLED_ADDRESS0x78 //通过调整0R电阻,屏可以0x78和0x7A两个地址 -- 默认0x78#define SDA_IN(){GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}//IO操作函数 #define IIC_SCLPBout(6) //SCL#define IIC_SDAPBout(7) //SDA #define READ_SDA PBin(7)//输入SDA void I2C_Configuration(void);void I2C_WriteByte(uint8_t addr,uint8_t data);void WriteCmd(unsigned char I2C_Command);void WriteDat(unsigned char I2C_Data);void OLED_Init(void);void OLED_SetPos(unsigned char x, unsigned char y);void OLED_Fill(unsigned char fill_Data);void OLED_CLS(void);void OLED_ON(void);void OLED_OFF(void);void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize);void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N);void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]);void control_part_display(u8 j,u8 num,u8 display_first[],u8 display_second[]);//显示单步控制状态函数void control_all_display(u8 state[]);//显示总控制状态函数void wen_shi_display(u8 num,u8 display_wen_shi1[]);//显示温湿度函数#endif
key.c
#include "key.h"#include "sys.h" #include "delay.h"#include "oled_i2c.h"#include "rtc.h"#include "string.h"#include "stdio.h"/*************************************************************************按键功能:KEY0:屏幕切换按键(用于切换家具状态 和 时钟显示)KEY1:时钟界面调整时间(每次按下可设置不同位置的时间,按满五次后退出设置)KEY2:时间设置时+KEY3:时间设置时-*************************************************************************/u8 Dis_mode=1;//显示状态标志 0:显示传感器数据1:显示日期时间//按键初始化函数void KEY_Init(void) //IO初始化{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能PORTA,PORTE时钟GPIO_InitStructure.GPIO_Pin= GPIO_Pin_4|GPIO_Pin_3;//KEY0-KEY1GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入 GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE4,3//初始化 WK_UP-->GPIOA.0下拉输入GPIO_InitStructure.GPIO_Pin= GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0}void Key_set(void){unsigned char ic = 0;unsigned char disp[20];unsigned int syear = 0;unsigned char smonth,sday,shour,smin;//临时变量syear = calendar.w_year;smonth = calendar.w_month;sday = calendar.w_date;shour = calendar.hour;smin = calendar.min;if((KEY1==0) && (Dis_mode==1))//时间显示界面{while(KEY1==0);//等待释放OLED_CLS();//清屏OLED_ShowCN(16*3,0,24);//设置OLED_ShowCN(16*4,0,25);OLED_ShowCN(0,2,12);//日期OLED_ShowCN(16,2,13);OLED_ShowCN(0,4,14);//时间OLED_ShowCN(16,4,15);while((KEY0==0) || (ic9)ic= 0;}if(ic==0)//年设置{if(KEY2==1){while(KEY2==1);syear++;}//if(KEY3==0)//{//while(KEY3==0);//syear--;//}sprintf((char*)disp,">%d-%02d-%02d",syear,smonth,sday);//将数据以什么样的字符串格式存到disp数组中OLED_ShowStr(32,2,disp,2);sprintf((char*)disp,":%02d:%02d:%02d",shour,smin,0);OLED_ShowStr(32,4,disp,2);}if(ic==1)//月设置{if(KEY2==1){while(KEY2==1);smonth++;if(smonth>12)smonth = 1;}//if(KEY3==0)//{//while(KEY3==0);//if(smonth>1)//smonth--;//}sprintf((char*)disp,":%d>%02d-%02d",syear,smonth,sday);OLED_ShowStr(32,2,disp,2);sprintf((char*)disp,":%02d:%02d:%02d",shour,smin,0);OLED_ShowStr(32,4,disp,2);}if(ic==2)//日设置{if(KEY2==1){while(KEY2==1);sday++;if(sday>31)sday = 1;}//if(KEY3==0)//{//while(KEY3==0);//if(sday>1)//sday--;//}sprintf((char*)disp,":%d-%02d>%02d",syear,smonth,sday);OLED_ShowStr(32,2,disp,2);sprintf((char*)disp,":%02d:%02d:%02d",shour,smin,0);OLED_ShowStr(32,4,(u8 *)disp,2);}if(ic==3)//时设置{if(KEY2==1){while(KEY2==1);shour++;if(shour>23)shour = 0;}//if(KEY3==0)//{//while(KEY3==0);//if(shour>0)//shour--;//}sprintf((char*)disp,":%d-%02d-%02d",syear,smonth,sday);OLED_ShowStr(32,2,(u8 *)disp,2);sprintf((char*)disp,">%02d:%02d:%02d",shour,smin,0);OLED_ShowStr(32,4,(u8 *)disp,2);}if(ic==4)//分设置{if(KEY2==1){while(KEY2==1);smin++;if(smin>59)smin = 0;}//if(KEY3==0)//{//while(KEY3==0);//if(smin>0)//smin--;//}sprintf((char*)disp,":%d-%02d-%02d",syear,smonth,sday);OLED_ShowStr(32,2,(u8 *)disp,2);sprintf((char*)disp,":%02d>%02d:%02d",shour,smin,0);OLED_ShowStr(32,4,(u8 *)disp,2);}if(ic==5)//退出设置返回到时间显示界面{RTC_Set(syear,smonth,sday,shour,smin,0);//设置时间Dis_mode = 1;//显示状态标志 0:显示传感器数据1:显示日期时间OLED_CLS();//清屏ic =0;break;}}}}
key.h
#ifndef __KEY_H#define __KEY_H #include "sys.h"//#define KEY0 PEin(4) //PE4//#define KEY1 PEin(3)//PE3 //#define WK_UP PAin(0)//PA0WK_UP#define KEY0GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)//读取按键0#define KEY1GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//读取按键1#define KEY2GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键3(WK_UP)#define KEY0_PRES 1//KEY0按下#define KEY1_PRES2//KEY1按下#define WKUP_PRES 3//KEY_UP按下(即WK_UP/KEY_UP)void KEY_Init(void);//IO初始化void Key_set(void);#endif