crystal1987的个人空间 https://passport2.21ic.com/?1370010 [收藏] [复制] [RSS]

日志

STM32之精准RTC时间设计

热度 1已有 1788 次阅读2016-2-29 16:45 |系统分类:单片机

      一年前开始做STM32的RTC,到现在才开始整理,战线拖得有点长啦。
       先说说为啥要做RTC吧,公司有个产品要定时启动语音提示,本来是想使用STM32内部RTC的,后来发现STM32内部RTC时间有些快,产品上的时间,一个月快了3分钟多,一年那就是半个小时还多。在网上查了资料说,可以软件校准STM32的RTC,这个兴奋啊!
原文:
实现RTC 校准的核心之一是库文件Stm321f0x_bkp.c中的void BKP_SetRTCCalibrationValue (uint8_t CalibrationValue) 函数。谈到RTC校准的相关参考文档包括AN2604.pdf,AN2821.pdf和AN2821.zip。这三个文档都可以从STM32官方网站下载。按照AN2604.pdf描述的原理,RTC 的校准值应在0-127之间。可实现的校准误差对应为0-121ppm。相当于每30天跑快的秒数为0-314s。
       这个方法,我也不知道有没有人做过,不过我真的把这些资料认真研读,并且做了实验,不过可惜的是,这个方法是让RTC跑慢了下来,但是效果还是不理想,一个月大概快105S的样子(因为这个时间是推算出来的,两天快7,实验做了5天),所以不得不换方法。
       后来,有网友建议使用DS1337,这次我也做了实验,还好这次实验比前面的效果好很多,4天快7S的样子,一个月也有一分钟的误差,这个误差说大不大,说小也不小,可能是有强迫症,然后有重新找方法,看了好多关于DS3231的资料,这次直接买了一个DS3231的模块,实验比较了一下,三天的时间没什么误差,后来买了芯片搞起来。效果真的不错,一个月的误差在2S的范围内,这次果断用了。具体的做法是,使用STM32模拟IIC与DS3231进行通信,读取和设置相关参数;然后再通过模拟IIC与OLED进行通信,将实时时间数据显示在OLED上。以下是实验代码:

OLED.h

#ifndef __OLED_H
#define __OLED_H                                  
#include "sys.h"
#include "stdlib.h"            

#ifdef __cplusplus
extern "C" {
#endif

/*************Pin Define***************/
#define SCL_HIGH  GPIO_SetBits(GPIOB, GPIO_Pin_3)   //LED1点亮//P1OUT|=BIT0           //SCL P1.0
#define SCL_LOW   GPIO_ResetBits(GPIOB, GPIO_Pin_3)     //P1OUT&=~BIT0
#define SDA_HIGH  GPIO_SetBits(GPIOB, GPIO_Pin_4)   //P1OUT|=BIT1           //SDA P1.1
#define SDA_LOW   GPIO_ResetBits(GPIOB, GPIO_Pin_4)     //P1OUT&=~BIT1

/****************************************************/
void OLED_Initial(void);
void IIC_Start(void);
void IIC_Stop(void);
void Write_IIC_Command(unsigned char IIC_Command);
void Write_IIC_Data(unsigned char IIC_Data);
void Write_IIC_Byte(unsigned char IIC_Byte);
void LCD_Set_Pos(unsigned char x, unsigned char y);
void LCD_CLS(void);
void LCD_P8x16Str(unsigned char x,unsigned char  y,unsigned char ch[]);
void LCD_P16x16Ch(unsigned char x,unsigned char  y,unsigned char  N);
void LCD_P6x8Str(unsigned char x,unsigned char  y,unsigned char ch[]);
void Draw_BMP(unsigned char x0,unsigned char  y0,unsigned char x1,unsigned char  y1,unsigned char BMP[]);

#ifdef __cplusplus
}
#endif
#endif

OLED.c

#include "stm32f10x_i2c.h"
#include "oled.h"
#include "delay.h"
#include "sys.h"

#define XLevelL                0x00
#define XLevelH                0x10
#define XLevel            ((XLevelH&0x0F)*16+XLevelL)
#define Max_Column        128
#define Max_Row                64
#define Brightness        0xCF
#define X_WIDTH 128
#define Y_WIDTH 64

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void OLED_Initial()
{
       GPIO_InitTypeDef  GPIO_InitStructure;
       // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

        /* PB8,9 SCL and SDA */
        GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_3 | GPIO_Pin_4;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_Init(GPIOB, &GPIO_InitStructure);
        
        delay_ms(100); //延时
        Write_IIC_Command(0xAE);   //display off
        Write_IIC_Command(0x20); //Set Memory Addressing Mode
        Write_IIC_Command(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
        Write_IIC_Command(0xb0); //Set Page Start Address for Page Addressing Mode,0-7
        Write_IIC_Command(0xc8); //Set COM Output Scan Direction
        Write_IIC_Command(0x00);//---set low column address
        Write_IIC_Command(0x10);//---set high column address
        Write_IIC_Command(0x40);//--set start line address
        Write_IIC_Command(0x81);//--set contrast control register
        Write_IIC_Command(0x7f);
        Write_IIC_Command(0xa1);//--set segment re-map 0 to 127
        Write_IIC_Command(0xa6);//--set normal display
        Write_IIC_Command(0xa8);//--set multiplex ratio(1 to 64)
        Write_IIC_Command(0x3F);//
        Write_IIC_Command(0xa4);//0xa4,Output follows RAM content;0xa5,Output ignores RAM content
        Write_IIC_Command(0xd3);//-set display offset
        Write_IIC_Command(0x00);//-not offset
        Write_IIC_Command(0xd5);//--set display clock divide ratio/oscillator frequency
        Write_IIC_Command(0xf0);//--set divide ratio
        Write_IIC_Command(0xd9);//--set pre-charge period
        Write_IIC_Command(0x22); //
        Write_IIC_Command(0xda);//--set com pins hardware configuration
        Write_IIC_Command(0x12);
        Write_IIC_Command(0xdb);//--set vcomh
        Write_IIC_Command(0x20);//0x20,0.77xVcc
        Write_IIC_Command(0x8d);//--set DC-DC enable
        Write_IIC_Command(0x14);//
        Write_IIC_Command(0xaf);//--turn on oled panel
        LCD_CLS();
        LCD_Set_Pos(0,0);
}

/**********************************************
//IIC Start
**********************************************/
void IIC_Start()
{
         SCL_HIGH;
         SDA_HIGH;
         SDA_LOW;
         SCL_LOW;
}
/**********************************************
//IIC Stop
**********************************************/
void IIC_Stop()
{
     SCL_LOW;
     SDA_LOW;
     SCL_HIGH;
     SDA_HIGH;

}
/**********************************************
// IIC Write byte
**********************************************/
void Write_IIC_Byte(unsigned char IIC_Byte)
{
        unsigned char i;
        for(i=0;i<8;i++)  
        {
                  if(IIC_Byte&0x80)  //1?0?
                      SDA_HIGH;
                  else
                      SDA_LOW;
                  SCL_HIGH;
                  SCL_LOW;
                  IIC_Byte<<=1;   //loop
        }
        SDA_HIGH;
        SCL_HIGH;
        SCL_LOW;
}
/**********************************************
// IIC Write Command
**********************************************/
void Write_IIC_Command(unsigned char IIC_Command)
{
   IIC_Start();
   Write_IIC_Byte(0x78);   //Slave address,SA0=0
   Write_IIC_Byte(0x00);   //write command
   Write_IIC_Byte(IIC_Command);
   IIC_Stop();
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data)
{
    IIC_Start();
    Write_IIC_Byte(0x78);   
    Write_IIC_Byte(0x40);   //write data
    Write_IIC_Byte(IIC_Data);
    IIC_Stop();
}

/*********************LCD 设置坐标************************************/
void LCD_Set_Pos(unsigned char x, unsigned char y)
{
        Write_IIC_Command(0xb0+y);
        Write_IIC_Command(((x&0xf0)>>4)|0x10);
        Write_IIC_Command((x&0x0f)|0x01);
}


/*********************LCD复位************************************/
void LCD_CLS(void)
{
        unsigned char y,x;        
        for(y=0;y<8;y++)
        {
                Write_IIC_Command(0xb0+y);
                Write_IIC_Command(0x01);
                Write_IIC_Command(0x10);
                for(x=0;x<X_WIDTH;x++)
                Write_IIC_Data(0);
        }
}

/***************功能描述:显示6*8一组标准ASCII字符串        显示的坐标(x,y),y为页范围0~7****************/
void LCD_P6x8Str(unsigned char x,unsigned char  y,unsigned char ch[])
{
        unsigned char c=0,i=0,j=0;      
        while (ch[j]!='\0')
        {   
                c =ch[j]-32;

                if(x>126){x=0;y++;}
                        LCD_Set_Pos(x,y);   
                for(i=0;i<6;i++)     
                        Write_IIC_Data(F6x8[c][i]);  
                x+=6;
                j++;
        }
}
/*******************功能描述:显示8*16一组标准ASCII字符串         显示的坐标(x,y),y为页范围0~7****************/
void LCD_P8x16Str(unsigned char x,unsigned char  y,unsigned char ch[])
{
        unsigned char c=0,i=0,j=0;
        while (ch[j]!='\0')
        {   
                c =ch[j]-32;
                if(x>120){x=0;y++;}
                    LCD_Set_Pos(x,y);   
                for(i=0;i<8;i++)     
                    Write_IIC_Data(F8X16[c*16+i]);
                LCD_Set_Pos(x,y+1);   
                for(i=0;i<8;i++)     
                    Write_IIC_Data(F8X16[c*16+i+8]);  
                x+=8;
                j++;
        }
}
/*****************功能描述:显示16*16点阵  显示的坐标(x,y),y为页范围0~7****************************/
void LCD_P16x16Ch(unsigned char x,unsigned char  y,unsigned char  N)
{
        unsigned char wm=0;
        unsigned int adder=32*N;  //          
        LCD_Set_Pos(x , y);
        for(wm = 0;wm < 16;wm++)  //            
        {
                Write_IIC_Data(F16x16[adder]);
                adder += 1;
        }      
        LCD_Set_Pos(x,y + 1);
        for(wm = 0;wm < 16;wm++) //         
        {
                Write_IIC_Data(F16x16[adder]);
                adder += 1;
        }                   
}
/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
void Draw_BMP(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++)
        {
                LCD_Set_Pos(x0,y);                                
            for(x=x0;x<x1;x++)
                  {      
                        Write_IIC_Data(BMP[j++]);                    
                  }
        }
}

IIC.h
#ifndef __DSIIC_H
#define __DSIIC_H

#include <stm32f10x.h>
#include "stm32f10x_i2c.h"
#include "delay.h"
#include "public.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操作函数**********************************************/         
#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];
/*************************************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 hou,u8 min,u8 sec);
void get_show_time(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)
extern u8 year,month,date,hour,minute,second;
#endif

IIC.c

#include "DS_IIC.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)
{
                year=I2cByteRead(0xd0,0x06);  //年
                month=I2cByteRead(0xd0,0x05); //月 
                date=I2cByteRead(0xd0,0x04);  //日
                hour=I2cByteRead(0xd0,0x02);  //时
                minute=I2cByteRead(0xd0,0x01);//分
                second=I2cByteRead(0xd0,0x00);//秒
}
/***********************************************************************************************
** 函数名称:ModifyTime
** 输入参数:yea,mon,da,hou,min,sec分别代表:年,月,日,时,分,秒
** 输出参数:无
** 功能描述:修改DS3231相关寄存器中时间数据
***********************************************************************************************/
void ModifyTime(u8 yea,u8 mon,u8 da,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(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)
{
                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);
}
main.c

int main(void)
{       
        delay_init(24);                                                 //延时初始化函数
        RCC_Configuration();       
        SPI2_GPIO_Config();
         Alarm_GPIO_Config();               
        NVIC_Configuration();
        OLED_Initial();
        DS_IIC_Init_Func();
        KEY_EXTI();
        while(1)
        {                                               
                if(G_un8TestFlag)
                {                                       
                        LED_1 = 1;
                        LED_2 = 1;
                        get_show_time();

                        G_un32WandRNum = date;                       
                        itoafunc(G_un32WandRNum);
                        LCD_P8x16Str(80,3,&NumStr[1]);
                        LCD_P8x16Str(68,3,":");
                                       
                        G_un32WandRNum = month;                       
                         itoafunc(G_un32WandRNum);
                        LCD_P8x16Str(52,3,&NumStr[1]);
                        LCD_P8x16Str(44,3,":");
                                       
                        G_un32WandRNum = year;                       
                        itoafunc(G_un32WandRNum);
                        LCD_P8x16Str(24,3,&NumStr[1]);
       
                        G_un32WandRNum = second;                       
                        itoafunc(G_un32WandRNum);
                        LCD_P8x16Str(80,6,&NumStr[1]);
                        LCD_P8x16Str(68,6,":");
                                       
                        G_un32WandRNum = minute;                       
                        itoafunc(G_un32WandRNum);
                        LCD_P8x16Str(52,6,&NumStr[1]);
                        LCD_P8x16Str(44,6,":");
                                       
                        G_un32WandRNum = hour;                       
                         itoafunc(G_un32WandRNum);
                        LCD_P8x16Str(24,6,&NumStr[1]);
                }
                else
                {
                        LED_1 = 0;
                        LED_2 = 0;
                                       
                        if(G_un32DelayTime == 100)
                                OLED_Initial();
                        G_un32DelayTime++;                               
                        LCD_P8x16Str(4,0,"RTC Testing...");

                }                                       
                sleep(1);               
        }
}


路过

鸡蛋
1

鲜花

握手

雷人

刚表态过的朋友 (1 人)

评论 (0 个评论)