蓝桥杯-单片机组基础10——基于2023年官方SPI通信代码讲解(实时时钟DS1302)

蓝桥杯单片机组备赛指南请查看 :本专栏第1篇文章

本文章针对蓝桥杯-单片机组比赛开发板所写,代码可直接在比赛开发板上使用。

型号:国信天长4T开发板(绿板),芯片:IAP15F2K61S2

(使用国信天长蓝板也可以完美兼容,与绿板几乎无差别)


万幸,在蓝桥杯比赛开发板上使用SPI通信的只有DS1302

1. 代码目的

        正确设置官方开发板外设DS1302,并结合数码管实现电子表的效果。

注意:ds1302外设可以得到:年、周几、月、日、时、分、秒

2. 头文件设置

        对于需要进行通信的外设,官方提供了底层ds1302.c文件,我们可以直接采用该文件中已经定义好的函数,来对DS1302进行正确的通信设置。但是,从16年开始为了增加难度,官方提供的代码会故意出现一些错误与遗漏,我们以2023年官方提供的底层文件为参考

下载链接:链接:https://pan.baidu.com/s/1LfixDiinqsOhYbhrAptbMQ      提取码:1111

        下面开始介绍如何正确添加DS1302的头文件到主函数文件中。如果采用直接复制代码到主函数文件,则以下步骤可以直接跳过。

步骤一:创建头文件ds1302.h

在keil5中项目中,左侧工程导航栏中,在source group处点击添加现有文件到工程:

将官方提供的ds1302.c文件添加到项目中:

此时我们的左侧工程栏就会出现一个新的文件,我们双击打开。

然后在左侧工程导航栏中,在source group处右键,点击添加新文件到工程:

接下来我们在新建的ds1302.h文件中,键入以下内容:

#ifndef __DS1302_H__
#define __DS1302_H__

void Write_Ds1302_Byte( unsigned char address,unsigned char dat );
unsigned char Read_Ds1302_Byte ( unsigned char address );

#endif

步骤二:修改函数文件ds1302.c

此时我们去点击编译,可以看到以下报错信息:

他这是提醒我们,在ds1302.c文件中,有4个特殊位变量没有进行定义

因此我们需要打开原理图,找到ds1302这个外设:

先看,P17引脚,图上为我们标注了SCK,正对应了ds1302底层文件中的SCK,时钟信号

再看,P23引脚,图上芯片标注了I/O,表示数据输入与输出端口,在spi通信中SDA表示数据引脚,因此P23正对应了ds1302底层文件中的SDA,数据信号

后看,P13引脚,图上芯片标注了RST#,正对应了ds1302底层文件中的RST,复位信号

因此我们需要打开ds1302.c文件,并在文件首部添加以下语句:

此时编译代码,便不会再出现底层文件的报错问题

步骤三:添加头文件ds1302.h

在我们的主函数程序中,添加我们自己新建的头文件:

至此,调用官方代码的环节结束。可以开始编写源程序

源程序全部代码如下:

/*	# 	DS1302代码片段说明
	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
		中对单片机时钟频率的要求,进行代码调试和修改。
*/								

//
#include <reg52.h>
#include <intrins.h>

sbit SCK = P1^7;
sbit SDA = P2^3;
sbit RST = P1^3;


void Write_Ds1302(unsigned  char temp) 
{
	unsigned char i;
	for (i=0;i<8;i++)     	
	{ 
		SCK = 0;
		SDA = temp&0x01;
		temp>>=1; 
		SCK=1;
	}
}   

//
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )     
{
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1; 	_nop_();  
 	Write_Ds1302(address);	
 	Write_Ds1302(dat);		
 	RST=0; 
}

//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
 	unsigned char i,temp=0x00;
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1;	_nop_();
 	Write_Ds1302(address);
 	for (i=0;i<8;i++) 	
 	{		
		SCK=0;
		temp>>=1;	
 		if(SDA)
 		temp|=0x80;	
 		SCK=1;
	} 
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
	SCK=1;	_nop_();
	SDA=0;	_nop_();
	SDA=1;	_nop_();
	return (temp);			
}

3. 原理图介绍

对于外设原理图,我们重点需要关注的其实就是各通信相关引脚的接线关系,从而正确的设置引脚,添加头文件。

4. SPI通信简介


SPI (Serial Peripheral interface)是串行外围设备接口

        SPI通信对时序的要求,相比ONEWIRE要弱一些。在控制字指令输入后的下一个SCLK时钟信号的上升沿,数据被写入DS1302;在控制字指令输入后的下一个SCLK时钟信号的下降沿,数据从DS1302读出。

        SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
SPI分为主、从两种模式,一个SPI通讯系统需要包含一个(且只能是一个)主设备,一个或多个从设备。提供时钟的为主设备(Master),接收时钟的设备为从设备(Slave),SPI接口的读写操作,都是由主设备发起。当存在多个从设备时,通过各自的片选信号进行管理。

        SPI是全双工且SPI没有定义速度限制,一般的实现通常能达到甚至超过10 Mbps

SPI接口一般使用四条信号线通信:
        SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)

MISO: 主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
MOSI: 主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
SCLK:串行时钟信号,由主设备产生。
CS/SS:从设备片选信号,由主设备控制。它的功能是用来作为“片选引脚”,也就是选择指定的从设备,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。

5. DS1302操作流程

5.1 spi通信操作时序

我们查看ds1302的各个寄存器值:

        我们先看BIT3~BIT0列,可以看出从上到下,依次为:秒、分、时、日、月、周几、年

        第一列为READ读地址列,从0x81到0x8D的奇数地址,为我们需要用到的,在从ds1302里面进行数据读取时,通过这些地址我们便可以将其中的数据读出来。

        第二列为WRITE写地址列,从0x80到0x8C的偶数地址,为我们需要用到的,在从ds1302里面进行初始数据写入,通过这些地址我们便可以将当前的时间写进去。其中,0x8E是写入控制地址,她的BIT7位WP为写保护控制位,当赋值为1时禁止写入,赋值为0时允许写入。其中,0x80地址,她的BIT7位CH为暂停控制位,当赋值为1时时钟震荡开始,赋值为0时时钟震荡暂停。

编程时,可以先建立三个数组,分别存储需要用到的读地址、写地址、初始时间

然后赋值0打开0x8E地址的WP写保护位,利用循环写入初始时间,最后赋值1关闭WP位

最后利用循环将当前时间读取出来,写入时间数组中

5.2 spi通信程序实现

        开发板上的spi通信只有ds1302,因此我们不需要设置ss片选信号。此外开发板上并不需要我们去设置spi通信,我们只需要了解,用于客观题使用。在编程时,我们只需要设置两个环节:

一、ds1302初始化函数

        用于向ds1302中写入当前时间,也就是初始时间

二、ds1302读取函数

        就是不断将当前时间,从ds1302中读取出来

三、数码管的转换关系

        ds1302寄存器中,是将数据按照16进制的关系存储的,因此我们读取出数据后,需要除以16得到十位,取余16得到个位

6. 代码参考

代码效果:将读取ds1302的时间,按照格式 “时时-分分-秒秒” ,在数码管上显示出来

#include <reg52.h>
#include <intrins.h>
#include "ds1302.h"

unsigned char code duanma [18]=
			{ 0xc0 , 0xf9 , 0xa4 , 0xb0 , 0x99 , 0x92 , 0x82 , 0xf8 , 
				0x80 , 0x90 , 0x88 , 0x80 , 0xc6 , 0xc0 , 0x86 , 0x8e ,
				0xbf , 0x7f };

unsigned char code write_ds1302_addr[7] = { 0x80 , 0x82 , 0x84 , 0x86 , 0x88 , 0x8a , 0x8c };
unsigned char code read_ds1302_addr[7] = { 0x81 , 0x83 , 0x85 , 0x87 , 0x89 , 0x8b , 0x8d };
unsigned char ds1302_time[7] = { 0x56 , 0x12 , 0x16 , 0x31 , 0x03 , 0x07 , 0x24 };

				
				
				
void select_HC573 ( unsigned char channal )
{
	switch (channal)
	{
		case 4:
			P2 = ( P2 & 0x1f ) | 0x80;
		break;
		case 5:
			P2 = ( P2 & 0x1f ) | 0xa0;
		break;
		case 6:
			P2 = ( P2 & 0x1f ) | 0xc0;
		break;
		case 7:
			P2 = ( P2 & 0x1f ) | 0xe0;
		break;
	}
}

void state_SMG ( unsigned char pos_SMG , unsigned char value_SMG )
{
	select_HC573 ( 6 );
	P0 = 0x01 << pos_SMG;
	select_HC573 ( 7 );
	P0 = value_SMG;
}

void state_SMG_all ( value_SMG )
{
	select_HC573 ( 6 );
	P0 = 0xff;
	select_HC573 ( 7 );
	P0 = value_SMG;
}

void Delay1ms()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	_nop_();
	_nop_();
	i = 11;
	j = 190;
	do
	{
		while (--j);
	} while (--i);
}

void SMGrunning ()
{
	state_SMG ( 0 , duanma[ds1302_time[2]/16] );
	Delay1ms();
	state_SMG ( 1 , duanma[ds1302_time[2]%16] );
	Delay1ms();
	state_SMG ( 2 , duanma[16] );
	Delay1ms();
	state_SMG ( 3 , duanma[ds1302_time[1]/16] );
	Delay1ms();
	state_SMG ( 4 , duanma[ds1302_time[1]%16] );
	Delay1ms();
	state_SMG ( 5 , duanma[16] );
	Delay1ms();
	state_SMG ( 6 , duanma[ds1302_time[0]/16] );
	Delay1ms();
	state_SMG ( 7 , duanma[ds1302_time[0]%16] );
	Delay1ms();
	
	state_SMG_all ( 0xff );
}

void init_sys ()
{
	select_HC573 ( 4 );
	P0 = 0xff;
	select_HC573 ( 5 );
	P0 = 0x00;
}

void init_ds1302()
{
	unsigned char i;
	Write_Ds1302_Byte( 0x8e , 0x00 );
	for ( i=0 ; i<7 ; i++ )
	{
		Write_Ds1302_Byte( write_ds1302_addr[i] , ds1302_time[i] );
	}
	Write_Ds1302_Byte( 0x8e , 0x80 );
}

void ds1302running ()
{
	unsigned char i;
	for ( i=0 ; i<7 ; i++ )
	{
		ds1302_time[i] = Read_Ds1302_Byte ( read_ds1302_addr[i] );
	}
}
		

void main ()
{
	init_sys ();
	init_ds1302();
	while( 1 )
	{
		ds1302running();
		SMGrunning();
	}
}

  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值