> 文章列表 > STM32F103基于标准库+I2C SSD1306仿数码管RTC时钟显示

STM32F103基于标准库+I2C SSD1306仿数码管RTC时钟显示

STM32F103基于标准库+I2C SSD1306仿数码管RTC时钟显示

STM32F103基于标准库+I2C SSD1306仿数码管RTC时钟显示


  • ✨申明:本文章仅发表在CSDN网站,任何其他网站,未注明来源,见此内容均为盗链和爬取。

  • 🍁对于文中所提供的相关资源链接将作不定期更换。

  • 📺显示效果:
    STM32F103基于标准库+I2C SSD1306仿数码管RTC时钟显示

  • 🌿工程基于正点原子标准库3.5版本RTC例程作为模板。

  • 🌿使用IIC SSD1306 OLED 双色屏幕软件IIC驱动显示。

⛳时钟说明

  • ✨采用软件I2C驱动,需要确保单片机外部低速时钟源晶振是正常能工作的,如需保持RTC走时,RTC供电部分需要有外部电池供电才行。

📑接口定义

	STM32F103   -------  I2C SSD1306 OLEDPA5	---------- SCLPA7	---------- SDA

📓OLED驱动代码

  • 🌿OLED.c 驱动代码
#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"  	 
#include "delay.h"u8 OLED_GRAM[144][8];//反显函数
void OLED_ColorTurn(u8 i)
{if(i==0){OLED_WR_Byte(0xA6,OLED_CMD);//正常显示}if(i==1){OLED_WR_Byte(0xA7,OLED_CMD);//反色显示}
}//屏幕旋转180度
void OLED_DisplayTurn(u8 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);}
}//延时
void IIC_delay(void)
{u8 t=3;while(t--);
}//起始信号
void I2C_Start(void)
{OLED_SDA_Set();OLED_SCL_Set();IIC_delay();OLED_SDA_Clr();IIC_delay();OLED_SCL_Clr();IIC_delay();
}//结束信号
void I2C_Stop(void)
{OLED_SDA_Clr();OLED_SCL_Set();IIC_delay();OLED_SDA_Set();
}//等待信号响应
void I2C_WaitAck(void) //测数据信号的电平
{OLED_SDA_Set();IIC_delay();OLED_SCL_Set();IIC_delay();OLED_SCL_Clr();IIC_delay();
}//写入一个字节
void Send_Byte(u8 dat)
{u8 i;for(i=0;i<8;i++){if(dat&0x80)//将dat的8位从最高位依次写入{OLED_SDA_Set();}else{OLED_SDA_Clr();}IIC_delay();OLED_SCL_Set();IIC_delay();OLED_SCL_Clr();//将时钟信号设置为低电平dat<<=1;}
}//发送一个字节
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 mode)
{I2C_Start();Send_Byte(0x78);I2C_WaitAck();if(mode){Send_Byte(0x40);}else{Send_Byte(0x00);}I2C_WaitAck();Send_Byte(dat);I2C_WaitAck();I2C_Stop();
}//开启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(0xAE,OLED_CMD);//关闭屏幕
}//更新显存到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);   //设置高列起始地址I2C_Start();Send_Byte(0x78);I2C_WaitAck();Send_Byte(0x40);I2C_WaitAck();for(n=0;n<128;n++){Send_Byte(OLED_GRAM[n][i]);I2C_WaitAck();}I2C_Stop();}
}
//清屏函数
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();//更新显示
}//画点 
//x:0~127
//y:0~63
//t:1 填充 0,清空	
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{u8 i,m,n;i=y/8;m=y%8;n=1<<m;if(t){OLED_GRAM[x][i]|=n;}else{OLED_GRAM[x][i]=~OLED_GRAM[x][i];OLED_GRAM[x][i]|=n;OLED_GRAM[x][i]=~OLED_GRAM[x][i];}
}//画线
//x1,y1:起点坐标
//x2,y2:结束坐标
void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode)
{u16 t; int xerr=0,yerr=0,delta_x,delta_y,distance;int incx,incy,uRow,uCol;delta_x=x2-x1; //计算坐标增量 delta_y=y2-y1;uRow=x1;//画线起点坐标uCol=y1;if(delta_x>0)incx=1; //设置单步方向 else if (delta_x==0)incx=0;//垂直线 else {incx=-1;delta_x=-delta_x;}if(delta_y>0)incy=1;else if (delta_y==0)incy=0;//水平线 else {incy=-1;delta_y=-delta_x;}if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 else distance=delta_y;for(t=0;t<distance+1;t++){OLED_DrawPoint(uRow,uCol,mode);//画点xerr+=delta_x;yerr+=delta_y;if(xerr>distance){xerr-=distance;uRow+=incx;}if(yerr>distance){yerr-=distance;uCol+=incy;}}
}
//x,y:圆心坐标
//r:圆的半径
void OLED_DrawCircle(u8 x,u8 y,u8 r)
{int a, b,num;a = 0;b = r;while(2 * b * b >= r * r)      {OLED_DrawPoint(x + a, y - b,1);OLED_DrawPoint(x - a, y - b,1);OLED_DrawPoint(x - a, y + b,1);OLED_DrawPoint(x + a, y + b,1);OLED_DrawPoint(x + b, y + a,1);OLED_DrawPoint(x + b, y - a,1);OLED_DrawPoint(x - b, y - a,1);OLED_DrawPoint(x - b, y + a,1);a++;num = (a * a + b * b) - r*r;//计算画的点离圆心的距离if(num > 0){b--;a--;}}
}//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//size1:选择字体 6x8/6x12/8x16/12x24
//mode:0,反色显示;1,正常显示
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode)
{u8 i,m,temp,size2,chr1;u8 x0=x,y0=y;if(size1==8)size2=6;else size2=(size1/8+((size1%8)?1:0))*(size1/2);  //得到字体一个字符对应点阵集所占的字节数chr1=chr-' ';  //计算偏移后的值for(i=0;i<size2;i++){if(size1==8){temp=asc2_0806[chr1][i];} //调用0806字体else 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&0x01)OLED_DrawPoint(x,y,mode);else OLED_DrawPoint(x,y,!mode);temp>>=1;y++;}x++;if((size1!=8)&&((x-x0)==size1/2)){x=x0;y0=y0+8;}y=y0;}
}//显示字符串
//x,y:起点坐标  
//size1:字体大小 
//*chr:字符串起始地址 
//mode:0,反色显示;1,正常显示
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode)
{while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!{OLED_ShowChar(x,y,*chr,size1,mode);if(size1==8)x+=6;else x+=size1/2;chr++;}
}//m^n
u32 OLED_Pow(u8 m,u8 n)
{u32 result=1;while(n--){result*=m;}return result;
}//显示数字
//x,y :起点坐标
//num :要显示的数字
//len :数字的位数
//size:字体大小
//mode:0,反色显示;1,正常显示
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode)
{u8 t,temp,m=0;if(size1==8)m=2;for(t=0;t<len;t++){temp=(num/OLED_Pow(10,len-t-1))%10;if(temp==0){OLED_ShowChar(x+(size1/2+m)*t,y,'0',size1,mode);}else {OLED_ShowChar(x+(size1/2+m)*t,y,temp+'0',size1,mode);}}
}//显示汉字
//x,y:起点坐标
//num:汉字对应的序号
//mode:0,反色显示;1,正常显示
void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode)
{u8 m,temp;u8 x0=x,y0=y;u16 i,size3=(size1/8+((size1%8)?1:0))*size1;  //得到字体一个字符对应点阵集所占的字节数for(i=0;i<size3;i++){if(size1==16){temp=Hzk1[num][i];}//调用16*16字体else if(size1==24){temp=Hzk2[num][i];}//调用24*24字体else if(size1==32)       {temp=Hzk3[num][i];}//调用32*32字体else if(size1==64){temp=Hzk4[num][i];}//调用64*64字体else return;for(m=0;m<8;m++){if(temp&0x01)OLED_DrawPoint(x,y,mode);else OLED_DrawPoint(x,y,!mode);temp>>=1;y++;}x++;if((x-x0)==size1){x=x0;y0=y0+8;}y=y0;}
}//num 显示汉字的个数
//space 每一遍显示的间隔
//mode:0,反色显示;1,正常显示
void OLED_ScrollDisplay(u8 num,u8 space,u8 mode)
{u8 i,n,t=0,m=0,r;while(1){if(m==0){OLED_ShowChinese(128,24,t,16,mode); //写入一个汉字保存在OLED_GRAM[][]数组中t++;}if(t==num){for(r=0;r<16*space;r++)      //显示间隔{for(i=1;i<144;i++){for(n=0;n<8;n++){OLED_GRAM[i-1][n]=OLED_GRAM[i][n];}}OLED_Refresh();}t=0;}m++;if(m==16){m=0;}for(i=1;i<144;i++)   //实现左移{for(n=0;n<8;n++){OLED_GRAM[i-1][n]=OLED_GRAM[i][n];}}OLED_Refresh();}
}//x,y:起点坐标
//sizex,sizey,图片长宽
//BMP[]:要写入的图片数组
//mode:0,反色显示;1,正常显示
void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode)
{u16 j=0;u8 i,n,temp,m;u8 x0=x,y0=y;sizey=sizey/8+((sizey%8)?1:0);for(n=0;n<sizey;n++){for(i=0;i<sizex;i++){temp=BMP[j];j++;for(m=0;m<8;m++){if(temp&0x01)OLED_DrawPoint(x,y,mode);else OLED_DrawPoint(x,y,!mode);temp>>=1;y++;}x++;if((x-x0)==sizex){x=x0;y0=y0+8;}y=y0;}}
}
/*********************OLED 设置坐标************************************/
void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ OLED_WR_Byte(0xb0+y,0);OLED_WR_Byte(((x&0xf0)>>4)|0x10,0);OLED_WR_Byte(x&0x0f,0);//0.96"ssd1306  0.96"sh1106:|0x01
} 
/*****************功能描述:显示32*32点阵  显示的坐标(x,y),y为页范围0~7****************************/
void OLED_P16x32Ch(unsigned char x, unsigned char y, unsigned char N)
{unsigned char wm=0;unsigned char z;unsigned int adder=64*N;OLED_Set_Pos(x , y);for(wm = 0;wm < 16;wm++){OLED_WR_Byte(F16x32[adder],1);adder += 1;}for(z=0;z<3;z++){OLED_Set_Pos(x,z+y+1);for(wm = 0;wm < 16;wm++){OLED_WR_Byte(F16x32[adder],1);adder += 1;} 	  	}  	}
//OLED的初始化
void OLED_Init(void)
{GPIO_InitTypeDef  GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD, ENABLE);	 //使能A端口时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; 		 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);	  GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_7);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(GPIOC, &GPIO_InitStructure);	 GPIO_SetBits(GPIOC,GPIO_Pin_12);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(GPIOD, &GPIO_InitStructure);	 GPIO_SetBits(GPIOD,GPIO_Pin_2);OLED_RES_Clr();delay_ms(200);OLED_RES_Set();OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panelOLED_WR_Byte(0x00,OLED_CMD);//---set low column addressOLED_WR_Byte(0x10,OLED_CMD);//---set high column addressOLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control registerOLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current BrightnessOLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常OLED_WR_Byte(0xA6,OLED_CMD);//--set normal displayOLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 dutyOLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)OLED_WR_Byte(0x00,OLED_CMD);//-not offsetOLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequencyOLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/SecOLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge periodOLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 ClockOLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configurationOLED_WR_Byte(0x12,OLED_CMD);OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomhOLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect LevelOLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)OLED_WR_Byte(0x02,OLED_CMD);//OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disableOLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disableOLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) OLED_Clear();OLED_WR_Byte(0xAF,OLED_CMD);
}
  • 🌿oled.h文件
#ifndef __OLED_H
#define __OLED_H #include "sys.h"
#include "stdlib.h"	//-----------------测试LED端口定义---------------- #define LED_ON GPIO_ResetBits(GPIOC,GPIO_Pin_12)
#define LED_OFF GPIO_SetBits(GPIOC,GPIO_Pin_12)//-----------------OLED端口定义---------------- #define OLED_SCL_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_5)//SCL
#define OLED_SCL_Set() GPIO_SetBits(GPIOA,GPIO_Pin_5)#define OLED_SDA_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_7)//DIN
#define OLED_SDA_Set() GPIO_SetBits(GPIOA,GPIO_Pin_7)#define OLED_RES_Clr() GPIO_ResetBits(GPIOD,GPIO_Pin_2)//RES
#define OLED_RES_Set() GPIO_SetBits(GPIOD,GPIO_Pin_2)#define OLED_CMD  0	//写命令
#define OLED_DATA 1	//写数据void OLED_ClearPoint(u8 x,u8 y);
void OLED_ColorTurn(u8 i);
void OLED_DisplayTurn(u8 i);
void I2C_Start(void);
void I2C_Stop(void);
void I2C_WaitAck(void);
void Send_Byte(u8 dat);
void OLED_WR_Byte(u8 dat,u8 mode);
void OLED_DisPlay_On(void);
void OLED_DisPlay_Off(void);
void OLED_Refresh(void);
void OLED_Clear(void);
void OLED_DrawPoint(u8 x,u8 y,u8 t);
void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode);
void OLED_DrawCircle(u8 x,u8 y,u8 r);
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode);
void OLED_ShowChar6x8(u8 x,u8 y,u8 chr,u8 mode);
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode);
void OLED_P16x32Ch(unsigned char x, unsigned char y, unsigned char N);//数码管风格字体
void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode);
void OLED_ScrollDisplay(u8 num,u8 space,u8 mode);
void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode);
void OLED_Init(void);#endif

📝main主程序代码

//
//本程序只供学习使用
//  功能描述   : OLED I2C接口(STM32F103系列)
//              说明:
//              ----------------------------------------------------------------
//              GND  电源地
//              VCC  3.3v电源
//              D0   PA5(SCL)
//              D1   PA7(SDA)
//              RES  PD2(SPI模块改成IIC模块需要接此引脚,IIC模块用户请忽略)
//              ----------------------------------------------------------------
//******************************************************************************/
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "bmp.h"
#include "rtc.h"
#include "led.h"int main(void)
{u8 t = ' ';delay_init();LED_Init();OLED_Init();OLED_ColorTurn(0);//0正常显示,1 反色显示OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示OLED_ShowString(8, 32, (u8*)"Hello World!", 16, 1);OLED_Refresh();delay_ms(500);OLED_Clear();LED_ON;while(RTC_Init())		//RTC初始化	,一定要初始化成功{OLED_ShowString(8, 16, (u8*)"RTC ERROR!", 16, 1);delay_ms(800);OLED_ShowString(8, 32, (u8*)"RTC Trying...", 16, 1);OLED_Refresh();}OLED_ShowString(20, 0, (u8*)"    -  -     ", 16, 1);
//			OLED_ShowString(5, 24,(u8*)"    :  :  ",16, 1);while(1){if(t != calendar.sec){t = calendar.sec;OLED_ShowNum(20, 0, calendar.w_year, 4, 16, 1);OLED_ShowNum(60, 0, calendar.w_month, 2, 16, 1);OLED_ShowNum(85, 0, calendar.w_date, 2, 16, 1);switch(calendar.week){case 0:OLED_ShowString(25, 16, (u8*)"Sunday", 16, 1);break;case 1:OLED_ShowString(25, 48, (u8*)"Monday", 16, 1);break;case 2:OLED_ShowString(25, 48, (u8*)"Tuesday", 16, 1);break;case 3:OLED_ShowString(25, 48, (u8*)"Wednesday", 16, 1);break;case 4:OLED_ShowString(25, 48, (u8*)"Thursday", 16, 1);break;case 5:OLED_ShowString(25, 48, (u8*)"Friday", 16, 1);break;case 6:OLED_ShowString(25, 48, (u8*)"Saturday", 16, 1);break;}OLED_Refresh();//-----------时分秒显示-----------------OLED_P16x32Ch(0, 2, calendar.hour / 10); //时OLED_P16x32Ch(16, 2, calendar.hour % 10);OLED_P16x32Ch(32, 2, 10);OLED_P16x32Ch(48, 2, calendar.min / 10); //分OLED_P16x32Ch(64, 2, calendar.min % 10);OLED_P16x32Ch(80, 2, 10);OLED_P16x32Ch(96, 2, calendar.sec / 10); //秒OLED_P16x32Ch(112, 2, calendar.sec % 10);LED0 = !LED0;}}
}

📚驱动代码

  • ✨申明:本文章仅发表在CSDN网站,任何其他网站,未注明来源,见此内容均为盗链和爬取。
  • 🍁对于文中所提供的相关资源链接将作不定期更换。
链接: https://pan.baidu.com/s/13aD3BPC0Ck1gBuMTtswUtQ
提取码: 3t7u