嵌入式STM32F103C8T6使用RTCDS3231实现时间输出(万年历)

 

嵌入式STM32F103C8T6使用RTC DS3231实现时间输出教程(也许是万年历)计划这学期有一门嵌入式的课程,课是没怎么听,但是期末要交大作业,只有这个万年历好写一点,老师给的板子很是便宜,淘宝上就那一家什么Microvue STM32F103C8T6开发版,中文名微视界,怪难搜索到33块钱,的确能跑,还有演示程序,我便开始实现这个万年历。可是我作为一个菜鸡CS学生,这嵌入式太可怕,...

嵌入式STM32F103C8T6使用RTC DS3231实现时间输出教程(也许是万年历)

计划

这学期有一门嵌入式的课程,课是没怎么听,但是期末要交大作业,只有这个万年历好写一点,老师给的板子很是便宜,淘宝上就那一家什么Microvue STM32F103C8T6开发版,中文名微视界,怪难搜索到33块钱,的确能跑,还有演示程序,我便开始实现这个万年历。

可是我作为一个菜鸡CS学生,这嵌入式太可怕,这个便宜的板子是没个什么正经RTC的,意思就是其没有办法存储实时时间,如果要写,就是写个“假”的,用系统内置的delay函数,延时1s之后自己写程序计算,需要考虑闰年等等事情,我觉得很累,就上网搜索了一波,发现可以外接RTC,淘宝买到的RTC大多自带一个小电池,单片机掉电了也可以存储时间,而且贼强,我随手买的这个DS3231据说精度很高,可以存储年,月,日,星期几,时,分,秒,还带着两个闹钟存储,自动闰年等等调整,真的很强,我就决定外接这个来实现年月日等等,而且我还会用到很多模块,我打算把我能跑的代码放出来开源,方便大家测试,最早我也是抄来的吗,嘤。

在此还要感谢这篇文章,靠这篇文章我获取到了时间链接

RTC DS3231

但是我们毕竟是学习,原理是要搞懂的,我们简单来看看这个RTC是如何给我们数据的

DS3231 存储结构

对于我们写程序主要是看这幅图,首先其有8位,本质是两个BCD码,回忆BCD码,每4位表示一个十进制数字,这样就清晰了起来,比如06H这个地址,我们发现低4位(我就这么说了)是单位为1的年份表示,这就是一个数字,也就是0-99的个位部分,之后高4位我们发现单位是10,也就是十位,在我实验之后发现这个模块应该是可以从2000年开始计数,最高数到2100年前一天,如果到99年多一年,系统会认为一个世纪过去了,观察05H地址,我们发现有一个Century位,代表年份的移除标志位,通过这个例子,我们就能看懂这张表,最下面几个应该是模式选择,这里我没有研究,有人有研究可以在底下评论告诉我们。

那么我们知道了对应的地址和代表的意义,那我们就知道要如何设计程序,首先要写两个函数,因为这里是BCD码,这样我们需要实现16toBCD和BCDto16,之后再实现两个函数,一个读一个写,这样就实现了日期的设置和读取,这样整个流程就完了,在此感谢那篇文章,那个程序大部分在我这里是可以跑对的。

实现

这里给出实现,分成三个文件,大家在移植的时候,注意根据自己的系统来修改里面的延时函数,我在这里用的是商家样例程序中的Delay函数,修改LED输出函数等等,有必要的情况下要修改引脚位置保存兼容,修改引入和删除的相关的库,保持工程师的精神,凑乎凑乎,东拼西凑,干就完事,能跑就行:

iic.h

#ifndef __DSIIC_H

#define __DSIIC_H

#include "stm32f10x_i2c.h"
//我这里的延时函数之类的定义的位置,有需要要改
#include "common.h"
//我这里的引脚位置定义文件,有需要要修改成自己的
#include "bitband.h" 

/***************************************IO方向设置**********************************************/

#define DS_SDA_IN()               \
    {                             \
        GPIOA->CRH &= 0XFFFFFF0F; \
        GPIOA->CRH |= 8 << 4;     \
    } //PA9

#define DS_SDA_OUT()              \
    {                             \
        GPIOA->CRH &= 0XFFFFFF0F; \
        GPIOA->CRH |= 2 << 4;     \
    } //1 - 10MHZ 2 - 2MHZ 3- 50MHZ

/***************************************IO操作函数**********************************************/
// 注释给出了IO的绑定
#define DS_IIC_SCL PAout(10) //SCL

#define DS_IIC_SDA PAout(9) //SDA

#define DS_READ_SDA PAin(9) //输入SDA

#define DS_Write_ADD 0xD0 //DS3231地址+写操作

#define DS_Read_ADD 0xD1 //DS3231地址+读操作

extern unsigned char set_rtc_date[7];

extern unsigned char read_rtc_date[7];
		
extern u8 year, month, date, hour, minute, second;

/*************************************IIC所有操作函数*******************************************/

void DS_IIC_Init_Func(void);

void setsda(void);

void clrsda(void);

u8 BCD2HEX(u8 val);

u8 B_BCD(u8 val);

void delay(u16 us);

void Start(void);

void Stop(void);

void SendByte(u8 Dat);

u8 ReceiveByte(u8 b);

void I2cByteWrite(u8 device, u8 addr, u8 bytedata);

u8 I2cByteRead(u8 device, u8 addr);

void Readtime(void);

void ModifyTime(u8 yea, u8 mon, u8 da,u8 we, u8 hou, u8 min, u8 sec);

void get_show_time(void);
u8 get_show_year(void);
u8 get_show_month(void);
u8 get_show_date(void);
u8 get_show_hour(void);
u8 get_show_min(void);
u8 get_show_sec(void);
u8 get_show_week(void);

#define SCL_H PAout(10) = 1

#define SCL_L PAout(10) = 0

#define SDA_H PAout(9) = 1

#define SDA_L PAout(9) = 0

#define SDA (GPIOA->IDR & 1 << 9)

#endif

iic.c

#include "iic.h"
#include "common.h"

void DS_IIC_Init_Func(void)
{
    GPIOA->CRH &= 0xfffff00f;
    GPIOA->CRH |= 0x00000220; //0x22000000; //SCK SDA
    Delay_ms(100);
    // I2cByteWrite(0xD0,0x07,0x80); //为DS3231芯片闹钟1设置,通过SQW输出1HZ信号
    // I2cByteWrite(0xD0,0x08,0x80);
    // I2cByteWrite(0xD0,0x09,0x80);
    // I2cByteWrite(0xD0,0x0a,0x80);
    // I2cByteWrite(0xD0,0x0e,0x05);
    I2cByteWrite(0xD0, 0x0e, 0);
    I2cByteWrite(0xD0, 0x0f, 0);
    //ModifyTime(15,0x03,20,15,32,20);
}

/***********************************************************************************************

** 函数名称:setsda

** 输入参数:无

** 输出参数:无

** 功能描述:SDA输出

***********************************************************************************************/

void setsda(void) //DS_SDA_OUT
{
    GPIOA->CRH &= 0XFFFFFF0F;
    GPIOA->CRH |= 2 << 4;
}

/***********************************************************************************************

** 函数名称:clrsda

** 输入参数:无

** 输出参数:无

** 功能描述:SDA输入

***********************************************************************************************/

void clrsda(void) //DS_SDA_IN
{
    GPIOA->CRH &= 0XFFFFFF0F;
    GPIOA->CRH |= 8 << 4;
}

/***********************************************************************************************

** 函数名称:BCD2HEX

** 输入参数:val:BCD码数据

** 输出参数:十六进制数据

** 功能描述:将BCD码数据转换成十六进制数据

***********************************************************************************************/

u8 BCD2HEX(u8 val)
{
    u8 i;
    i = val & 0x0f;
    val >>= 4;
    val &= 0x0f;
    val *= 10;
    i += val;
    return i;
}
/***********************************************************************************************

** 函数名称:B_BCD

** 输入参数:val:十进制数据

** 输出参数:BCD码数据

** 功能描述:将十进制数据转换成BCD码数据

***********************************************************************************************/

u8 B_BCD(u8 val)
{
    u8 i, j, k;
    i = val / 10;
    j = val % 10;
    k = j + (i << 4);
    return k;
}

/***********************************************************************************************

** 函数名称:delay

** 输入参数:us:延时us时间

** 输出参数:无

** 功能描述:延时设定的时间

***********************************************************************************************/

void delay(u16 us)
{
    Delay_us(us * 7);
}

/***********************************************************************************************

** 函数名称:Start

** 输入参数:无

** 输出参数:无

** 功能描述:IIC开始条件

***********************************************************************************************/

void Start(void)
{
    SDA_H;
    delay(5);
    SCL_H;
    delay(5);
    SDA_L;
    delay(5);
}

/***********************************************************************************************

** 函数名称:Stop

** 输入参数:无

** 输出参数:无

** 功能描述:IIC结束条件

***********************************************************************************************/

void Stop(void)
{
    SDA_L;
    delay(5);
    SCL_H;
    delay(5);
    SDA_H;
    delay(5);
}

/***********************************************************************************************

** 函数名称:SendByte

** 输入参数:Dat:需发送的数据

** 输出参数:无

** 功能描述:发送一字节数据

***********************************************************************************************/

void SendByte(u8 Dat)
{
    u8 i = 0;
    u8 T_Data = 0;
    DS_SDA_OUT();
    SCL_L;
    delay(10);
    T_Data = Dat;
    for (i = 0; i < 8; i++)
    {
        if (T_Data & 0x80)
            SDA_H;
        else
            SDA_L;
        delay(5);
        SCL_L;
        delay(5);
        SCL_H;
        delay(5);
        T_Data = T_Data << 1;
        SCL_L;
        delay(5);
    }
    SDA_H;
    delay(5);
    SCL_L;
    delay(5);
    SCL_H;
    delay(5);
    SCL_L;
}

/***********************************************************************************************

** 函数名称:ReceiveByte

** 输入参数:b:是否产生应答 0-产生应答 1-不产生应答

** 输出参数:无

** 功能描述:接收一字节数据,并根据b决定是否发送应答信息。

***********************************************************************************************/

u8 ReceiveByte(u8 b)
{
    u8 i;
    u8 temp;
    u8 Dat = 0;
    DS_SDA_IN();
    for (i = 0; i < 8; i++)
    {
        SCL_H;
        delay(5);
        Dat = Dat << 1;
        delay(5);
        if (SDA)
        {
            temp = 1;
        }
        else
        {
            temp = 0;
        }
        if (temp)
            Dat |= 0x01;
        else
            Dat |= 0x00;
        delay(5);
        SCL_L;
        delay(5);
    }
    DS_SDA_OUT();
    if (b)
        SDA_H;
    else
        SDA_L;
    delay(5);
    SCL_H;
    delay(5);
    SCL_L;
    delay(5);
    SDA_H;
    delay(100);
    return Dat;
}

/***********************************************************************************************

** 函数名称:I2cByteWrite

** 输入参数:device:设备地址,0xd0; addr:寄存器地址 bytedata:寄存器地址中数据

** 输出参数:无

** 功能描述:向DS3231相关寄存器中写入设定数据

***********************************************************************************************/

void I2cByteWrite(u8 device, u8 addr, u8 bytedata)
{
    Start();
    delay(5);
    SendByte(device);
    delay(5);
    SendByte(addr);
    delay(5);
    SendByte(bytedata);
    delay(5);
    Stop();
}

/***********************************************************************************************

** 函数名称:I2cByteRead

** 输入参数:device:设备地址,0xd1; addr:寄存器地址

** 输出参数:无

** 功能描述:从DS3231相关寄存器中读取数据

***********************************************************************************************/

u8 I2cByteRead(u8 device, u8 addr)
{
    u8 Dat = 0;
    Start();
    SendByte(device);
    delay(5);
    SendByte(addr);
    delay(5);
    Start(); //Restart
    SendByte(0xd1);
    delay(5);
    Dat = ReceiveByte(1);
    Stop();
    return Dat;
}

/***********************************************************************************************

** 函数名称:Readtime

** 输入参数:无

** 输出参数:无

** 功能描述:从DS3231相关寄存器中读取时间数据

***********************************************************************************************/

void Readtime(void)
{	
    u8 year = I2cByteRead(0xd0, 0x06); //年
    u8 month = I2cByteRead(0xd0, 0x05); //月
    u8 date = I2cByteRead(0xd0, 0x04); //日
    u8 hour = I2cByteRead(0xd0, 0x02); //时
    u8 minute = I2cByteRead(0xd0, 0x01); //分
    u8 second = I2cByteRead(0xd0, 0x00); //秒
}

/***********************************************************************************************

** 函数名称:ModifyTime

** 输入参数:yea,mon,da,hou,min,sec分别代表:年,月,日,时,分,秒

** 输出参数:无

** 功能描述:修改DS3231相关寄存器中时间数据

***********************************************************************************************/

void ModifyTime(u8 yea, u8 mon, u8 da,u8 we, u8 hou, u8 min, u8 sec)
{
    u8 temp = 0;
    temp = B_BCD(yea);
    I2cByteWrite(0xD0, 0x06, temp);
    temp = B_BCD(mon);
    I2cByteWrite(0xD0, 0x05, temp);
    temp = B_BCD(da);
    I2cByteWrite(0xD0, 0x04, temp);
		temp = B_BCD(we);
		I2cByteWrite(0xD0, 0x03, temp);
    temp = B_BCD(hou);
    I2cByteWrite(0xD0, 0x02, temp);
    temp = B_BCD(min);
    I2cByteWrite(0xD0, 0x01, temp);
    temp = B_BCD(sec);
    I2cByteWrite(0xD0, 0x00, temp);
}

/***********************************************************************************************

** 函数名称:get_show_time

** 输入参数:无

** 输出参数:无

** 功能描述:读取DS3231相关寄存器中时间数据

***********************************************************************************************/

void get_show_time(void)
{
		//int d[6];
	  u8 year,month,date,hour,minute,second;
    year = I2cByteRead(0xd0, 0x06);
    year = BCD2HEX(year);
    month = I2cByteRead(0xd0, 0x05);
    month = BCD2HEX(month);
    date = I2cByteRead(0xd0, 0x04);
    date = BCD2HEX(date);
    hour = I2cByteRead(0xd0, 0x02);
    hour &= 0x3f;
    hour = BCD2HEX(hour);
    minute = I2cByteRead(0xd0, 0x01);
    minute = BCD2HEX(minute);
    second = I2cByteRead(0xd0, 0x00);
    second = BCD2HEX(second);
}

u8 get_show_year(void){
	u8 year;
	year = I2cByteRead(0xd0, 0x06);
  year = BCD2HEX(year);
	return year;
}

u8 get_show_month(void){
	u8 month;
	month = I2cByteRead(0xd0, 0x05);
  month = BCD2HEX(month);
	return month;
}

u8 get_show_date(void){
	u8 date;
	date = I2cByteRead(0xd0, 0x04);
  date = BCD2HEX(date);
	return date;
}
//获取星期几
u8 get_show_week(void){
	u8 week;
	week = I2cByteRead(0xd0, 0x03);
	week = BCD2HEX(week);
	return week;
}

u8 get_show_hour(void){
	u8 hour;
	hour = I2cByteRead(0xd0, 0x02);
  hour &= 0x3f;
  hour = BCD2HEX(hour);
	return hour;
}

u8 get_show_min(void){
	u8 minute;
	minute = I2cByteRead(0xd0, 0x01);
  minute = BCD2HEX(minute);
	return minute;
}

u8 get_show_sec(void){
	u8 second;
	second = I2cByteRead(0xd0, 0x00);
  second = BCD2HEX(second);
	return second;
}

main.c

//LED显示相关的应该移植的时候都要改写
#include "iic.h" //这次主要用到的
#include <stdio.h>  
#include <stdlib.h>  

#include "stm32f10x.h"

#include "common.h"
#include "oled.h"
#include "page.h"
#include "key.h"
 
#include "task_manage.h"
#include "AD9834.h"

int main(void)
{		
	int G_un8TestFlag = 1;
	int G_un32WandRNum;
	char buffer [20];
	int v1=0,v2=0;

	SystemConfiguration();		    //系统初始化
	RCC_Configuration();

	DS_IIC_Init_Func();
  
  Lcd_Init();  
	ad9834_reset();
  T_Adc_Init();

	BACK_COLOR=BLACK; //0xef5d;
	POINT_COLOR=RED;
		
	LCD_Clear(BLACK,320,240); //0x3211);  
  OLED_BLK_Set();
  POINT_COLOR=WHITE;
  Delay_ms(100);
	
	BACK_COLOR=BLACK;
	POINT_COLOR=BLUE;
  
  //!注意这里时设置时间的函数,年,月,日,星期几,时,分,秒
  //!其中年我认为这个RTC时从2000年开始计算的所以19年应该是设置19
	//ModifyTime(19, 12, 10, 2, 13, 12, 0);
	
	while(1)
	{	
		//KeySweep();
 	  //task_inputfre();
		if (HRR == 1){
			LCD_WR_REG(0x28);	
			OLED_BLK_Set();
			LCD_ShowString3216(83,103,(unsigned char const *)"People");
		}else{
			OLED_BLK_Set();
			POINT_COLOR=WHITE;
			BACK_COLOR=BLACK;
			POINT_COLOR=BLUE;
			LCD_ShowString3216(83,103,(unsigned char const *)"No People");
		}
		if (G_un8TestFlag)
    {	
			G_un32WandRNum = get_show_year();
			G_un32WandRNum += 2000;
			sprintf(buffer, "%4d", G_un32WandRNum); 
      LCD_ShowString3216(43,30,(unsigned char const *)buffer);
			//LCD_ShowString3216(86,73,(unsigned char const *)":");
			
			G_un32WandRNum = get_show_month();
			sprintf(buffer, "%2d", G_un32WandRNum); 
			LCD_ShowString3216(43,60,(unsigned char const *)buffer);
			//LCD_ShowString3216(146,93,(unsigned char const *)":");
			
			G_un32WandRNum = get_show_date();
			sprintf(buffer, "%2d", G_un32WandRNum); 
      LCD_ShowString3216(43,90,(unsigned char const *)buffer);
			//LCD_ShowString3216(216,113,(unsigned char const *)":");
			
			G_un32WandRNum = get_show_week();
			if (G_un32WandRNum == 1){
				LCD_ShowString3216(113,30,(unsigned char const *)"MON");
			}else if (G_un32WandRNum == 2){
				LCD_ShowString3216(113,30,(unsigned char const *)"TUE");
			}else if (G_un32WandRNum == 3){
				LCD_ShowString3216(113,30,(unsigned char const *)"WED");
			}else if (G_un32WandRNum == 4){
				LCD_ShowString3216(113,30,(unsigned char const *)"THU");
			}else if (G_un32WandRNum == 5){
				LCD_ShowString3216(113,30,(unsigned char const *)"FRI");
			}else if (G_un32WandRNum == 6){
				LCD_ShowString3216(113,30,(unsigned char const *)"SAT");
			}else if (G_un32WandRNum == 7){
				LCD_ShowString3216(113,30,(unsigned char const *)"SUN");
			}
			
			G_un32WandRNum = get_show_hour();
			sprintf(buffer, "%2d", G_un32WandRNum); 
      LCD_ShowString3216(43,120,(unsigned char const *)buffer);
			//LCD_ShowString3216(266,63,(unsigned char const *)":");
			
			G_un32WandRNum = get_show_min();
			sprintf(buffer, "%2d", G_un32WandRNum); 
			LCD_ShowString3216(43,150,(unsigned char const *)buffer);
			//LCD_ShowString3216(316,63,(unsigned char const *)":");
			
			G_un32WandRNum = get_show_sec();
			sprintf(buffer, "%2d", G_un32WandRNum); 
			LCD_ShowString3216(43,180,(unsigned char const *)buffer);
    }
		Delay_ms(50);
	}
}

修改完之后接好模块,应该就可以运行了,注意按照自己的设备相关的函数。

我认为应该开放这些代码,并且让大家都能方便的使用,即使是在干像我一样的大作业,希望也有更多人能够开放自己的源代码出去。下一篇应该是写加DHT11的代码,我还没有研究,感觉应该不是很难吧(希望)。