STM32F1/F4硬件SPI控制器快速读写DS1302 拒绝模拟IO协议!!

      鉴于网上铺天盖地的DS1302设备驱动都是使用GPIO口进行模拟通讯时序,在这里使用STM32片内SPI控制器进行读写通信,由此代码结构更简洁,通讯速率更快
 
 

DS1302芯片简介

     DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能。
 
 

通讯时序

     DS1302的通信协议与标准SPI协议十分类似,不同的是片选引脚CE的上升沿选中芯片,开始建立通信,下降沿结束通信,这一点与标准SPI正相反,另外需要注意的是,DS1302的数据引脚是半双工模式的,主机发送的命令帧和从机回复的数据帧都经过这根信号线。

     这里我们只实现DS1302的RTC功能,采用单步读,单步写的通讯方式,在单步写时序中,MCU先向DS1302发送控制字节,字节的MSB为0表示写方向,A0-A4决定要写入的寄存器地址,紧接着是发送所要写入的数据,从低位开始传输。通讯过程中,数据随着时间线的上升沿一位一位地锁存至DS1302的内部寄存器。 单步读时序中,MCU先发送控制字,这里与写时序类似,紧接着,在每时钟信号的下降沿,DS1302将RTC数据放入总线,而MCU在每个时钟信号的上升沿对总线进行采样,完成对数据的锁存,流程如下图
Alt
 
 

上代码

       以下是DS1302头文件,我们这里使用SPI1外设,CE_H和CE_L表示片选信号的高低,将DS1302的数据线接入STM32的MOSI引脚,WRITE_MODE()和READ_MODE()是通讯过程中发射和接收的模式切换,先禁用SPI,切换完成后打开SPI.

#ifndef __DS1302_H
#define	__DS1302_H

#include "stm32f4xx.h"

#define DS1302_GPIO                   GPIOA
#define DS1302_RCC_GPIO_CLK           RCC_AHB1Periph_GPIOA
#define DS1302_SPI                    SPI1
#define DS1302_SPI_RCC_APBx_CLK_FUN   RCC_APB2PeriphClockCmd
#define DS1302_SPI_CLK                RCC_APB2Periph_SPI1

#define DS1302_GPIO_AF                     GPIO_AF_SPI1
#define DS1302_GPIO_SPI_SCK_PinSource      GPIO_PinSource5
#define DS1302_GPIO_SPI_MOSI_PinSource     GPIO_PinSource7

#define DS1302_CE_PIN             GPIO_Pin_4
#define DS1302_SCK_PIN            GPIO_Pin_5
#define DS1302_MOSI_PIN           GPIO_Pin_7


#define READ_MODE()   SPI_Cmd(DS1302_SPI,DISABLE); \
                      SPI_BiDirectionalLineConfig(DS1302_SPI,SPI_Direction_Rx); \
                      SPI_Cmd(DS1302_SPI,ENABLE);
                  

#define WRITE_MODE()  SPI_Cmd(DS1302_SPI,DISABLE); \
                      SPI_BiDirectionalLineConfig(DS1302_SPI,SPI_Direction_Tx); \
                      SPI_Cmd(DS1302_SPI,ENABLE);

                               
#define  CE_H    GPIO_SetBits(DS1302_GPIO,DS1302_CE_PIN);
#define  CE_L    GPIO_ResetBits(DS1302_GPIO,DS1302_CE_PIN);


void DS1302_Config(void);
void DS1302_init(void);
void DS1302_getRTC(void);

#endif 

 
 
DS1302.c文件

#include "DS1302.h"
#include "delay.h"

unsigned char time_buf[]={46,59,23,0,0,0,0};
unsigned char ReadBuff[7];



static void DS1302_GPIO_Config(void)
{  
  GPIO_InitTypeDef GPIO_InitStructure;
  
  RCC_AHB1PeriphClockCmd( DS1302_RCC_GPIO_CLK, ENABLE);
		
  GPIO_PinAFConfig(DS1302_GPIO,DS1302_GPIO_SPI_SCK_PinSource,DS1302_GPIO_AF);
  GPIO_PinAFConfig(DS1302_GPIO,DS1302_GPIO_SPI_MOSI_PinSource,DS1302_GPIO_AF);

  GPIO_InitStructure.GPIO_Pin = DS1302_SCK_PIN|DS1302_MOSI_PIN|DS1302_CE_PIN;
  GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_AF;       
  GPIO_InitStructure.GPIO_Speed  = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(DS1302_GPIO, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = DS1302_CE_PIN;
  GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_OUT;       
  GPIO_Init(DS1302_GPIO, &GPIO_InitStructure);

  GPIO_ResetBits(DS1302_GPIO, DS1302_CE_PIN);	 
}






static void DS1302_SPI_Config(void)
{
  SPI_InitTypeDef  SPI_InitStructure;

  DS1302_SPI_RCC_APBx_CLK_FUN(DS1302_SPI_CLK,ENABLE);
  
  SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; 
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;	 				
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;	 		
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		 				
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;					
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		   				
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;  
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB;  				
  SPI_InitStructure.SPI_CRCPolynomial = 7;
  
  SPI_Init(DS1302_SPI, &SPI_InitStructure);

  SPI_Cmd(DS1302_SPI, ENABLE);
}

    

void DS1302_Config(void)
{
    DS1302_GPIO_Config();
    DS1302_SPI_Config();
}




void Write_Ds1302_Byte(u8 byte)
{
  while (SPI_I2S_GetFlagStatus(DS1302_SPI, SPI_I2S_FLAG_TXE) == RESET);
  
  SPI_I2S_SendData(DS1302_SPI, byte);		
  
  while(SPI_I2S_GetFlagStatus(DS1302_SPI,SPI_I2S_FLAG_BSY));
}  






void Write_Ds1302( unsigned char address,unsigned char dat )     
{
  WRITE_MODE();
  
  CE_H;
  
  Write_Ds1302_Byte(address);	
  Write_Ds1302_Byte((dat/10<<4)|(dat%10));
  
  CE_L;  
}





unsigned char Read_Ds1302 ( unsigned char address )
{
 	unsigned char temp=0x00,dat1,dat2;
  
    WRITE_MODE();
  
    CE_H;
  
    Write_Ds1302_Byte(address);
	delay_us(3);
    
    READ_MODE();
  
    while (SPI_I2S_GetFlagStatus(DS1302_SPI, SPI_I2S_FLAG_RXNE) == RESET);
    CE_L;
  
    temp = SPI_I2S_ReceiveData(DS1302_SPI); 
  
    SPI_Cmd(DS1302_SPI,DISABLE);
  

    dat1=temp/16;
	dat2=temp%16;
	temp=dat1*10+dat2;
  
    return temp;			
}







void DS1302_init(void)
{
	unsigned char i,add;
	add=0x80;
	
    Write_Ds1302(0x8e,0x00);
	
    for(i=0;i<7;i++)
	{
		Write_Ds1302(add,time_buf[i]);
		add=add+2;
	}
	
	Write_Ds1302(0x8e,0x80);
}




void DS1302_getRTC(void)
{
	unsigned char i,add = 0x81;
	
    Write_Ds1302(0x8e,0x00);
	
    for(i=0;i<7;i++)
	{
	  ReadBuff[i]=Read_Ds1302(add);
      add = add+2;
    }
	Write_Ds1302(0x8e,0x80);
}

 

 

SPI外设初始化

     我们可以参照DS1302的通讯协议来初始化STM32的SPI外设,这里我们先选用STM32F407为例,在SPI初始化结构体中,先配置为半双工发送模式,时钟相位为空闲时低电平,时钟信号的奇数边沿移出SPI移位寄存器中的数据,或对DS1302方向的数据进行采样,数据低位先行,时钟频率在供电3.3V的条件下配置为约1.3MHz,即对APB2总线时钟进行÷64分频

static void DS1302_SPI_Config(void)
{
   SPI_InitTypeDef  SPI_InitStructure;

   DS1302_SPI_RCC_APBx_CLK_FUN(DS1302_SPI_CLK,ENABLE);

   SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; //先初始化为发送模式
   SPI_InitStructure.SPI_Mode = SPI_Mode_Master;	 				
   SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;	 		
   SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		 				
   SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;					
   SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		   				
   SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;   //3.3V电源下1.3MHz左右         
   SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB;  //低位先行				
   SPI_InitStructure.SPI_CRCPolynomial = 7;

   SPI_Init(DS1302_SPI, &SPI_InitStructure);

   SPI_Cmd(DS1302_SPI, ENABLE);
}

 
 

对底层读写函数的解释

1.void Write_Ds1302_Byte(u8 byte)

void Write_Ds1302_Byte(u8 byte)
{
  while (SPI_I2S_GetFlagStatus(DS1302_SPI, SPI_I2S_FLAG_TXE) == RESET);
  
  SPI_I2S_SendData(DS1302_SPI, byte);		
  
  while(SPI_I2S_GetFlagStatus(DS1302_SPI,SPI_I2S_FLAG_BSY));
}  

       该函数实现对DS1302写一字节数据,首先等待SPI外设的发送寄存器为空,然后对发送寄存器写入将要发送的一字节,最后等待移位寄存器将发送寄存器的数据一位位移走,如果不等待BSY事件则是当数据转移至移位寄存器后立刻跳出函数执行其他操作,会导致通信失败。

 
 
 
2.unsigned char Read_Ds1302 ( unsigned char address )

unsigned char Read_Ds1302 ( unsigned char address )
{
  unsigned char temp=0x00,dat1,dat2;
  
  WRITE_MODE();
  
  CE_H;
  Write_Ds1302_Byte(address);
  delay_us(3);
  
  READ_MODE();
  while (SPI_I2S_GetFlagStatus(DS1302_SPI, SPI_I2S_FLAG_RXNE) == RESET);
  
  CE_L;
  
  temp = SPI_I2S_ReceiveData(DS1302_SPI); 
  
  SPI_Cmd(DS1302_SPI,DISABLE);
  
  dat1=temp/16;
  dat2=temp%16;
  temp=dat1*10+dat2;
  
  return temp;			
}

     这里在给DS1302发送所要读取的寄存器地址,适当延时后,打开SPI的读模式,此时STM32将立刻产生1.3MHz的SPI时钟同步信号,因此紧接着我们需要立刻准备接收DS1302方向的RTC数据,即开始等待RXNE事件(接收寄存器非空),接收完一字节数据后立刻拉低片选线结束当前通信,在获得RTC数据后禁用SPI,掐断时钟信号,对DS1302的BCD码转为十进制数据后返回。

主函数代码


#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "ds1302.h"


extern u8 ReadBuff[8];


int main(void)
{ 
  delay_init(168);		//延时初始化 
	
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2

  uart_init(115200);	//串口初始化波特率为115200
	
  LED_Init();
	
  DS1302_Config();
  DS1302_init();
	

  while (1)
  {
    delay_ms(10);
    DS1302_getRTC();         
    
    for(int8_t i=2;i>=0;i--)
      printf("%d ",ReadBuff[i]);
        
    putchar('\r');  
    putchar('\n');
  }
	
	
	
}
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值