简介这一节,我将给大家讲解实时时钟部分的内容,我在黑金板上用的实时时钟芯片是DS1302,这块芯片很常见,性价比也很高。
我们主要来讲如何在NIOS中实现其功能,所以DS1302功能介绍我简单概括一下,有问题的百度一下就都知道了。
DS1302是DALLAS公司推出的涓流充电实时时钟芯片,内含一个实时时钟/日历和31字节静态RAM,仅需要三根线:RES(复位),I/O(数据线),SCLK(串行时钟)。
时钟/RAM 的读/写数据以一个字节或多达31 个字节的字符组方式通信DS1302 工作时功耗很低,保持数据和时钟信息时功率小于1mW。
下面看一下电路图吧,下图所示,很简单,三根线就可以搞定了。
硬件开发首先,我们需要在软核中构建三个PIO模块,方法跟以前讲的一样。
需要注意的是RTC_DATA这个PIO,在构建的过程中,我们将其选择为双向的IO口,因为它是数据线,既要输入也需要输出,如下图所示,红圈处就是我们需要注意的地方,其他两个IO口设置为仅输出。
看看构建好以后的样子吧,如下图是所示接下来就是自动分配地址,中断,然后开始编译,等待……回到Quartus后,分配引脚,还是需要注意数据线,也是双向的,分配引脚的时候,要构建双向引脚(bidir),如下图所示。
都设置好以后,我们运行TCL脚本文件,然后开始编译,又是等待……软件开发编译好后,我们打开NIOS II IDE,首先,还是需要编译一下,CTRL+b,编译之后,我们看看system.h有什么变化。
观察后可以看出,里面对了,RTC部分的代码,如下表所示,#define RTC_DATA_NAME "/dev/RTC_DATA"#define RTC_DATA_TYPE "altera_avalon_pio"#define RTC_DATA_BASE 0x00201030……/** RTC_SCLK configuration**/#define RTC_SCLK_NAME "/dev/RTC_SCLK"#define RTC_SCLK_TYPE "altera_avalon_pio"#define RTC_SCLK_BASE 0x00201040……/** RTC_nRST configuration**/#define RTC_NRST_NAME "/dev/RTC_nRST"#define RTC_NRST_TYPE "altera_avalon_pio"#define RTC_NRST_BASE 0x00201050……/*在这些代码中,我们需要用到的是以下部分#define RTC_DATA_BASE 0x00201030#define RTC_SCLK_BASE 0x00201040#define RTC_NRST_BASE 0x00201050好的,接下来,我们就开始写程序吧第一步,修改sopc.h文件,加入以下代码到sopc.h中#define _RTC#ifdef _RTC#define RTC_SCLK ((PIO_STR *) RTC_SCLK_BASE)#define RTC_DATA ((PIO_STR *) RTC_DATA_BASE)#define RTC_RST ((PIO_STR *) RTC_NRST_BASE)#endif /* _RTC */没什么可说的,接下来我们在inc文件夹下建立ds1302.h,在其中加入以下内容,跟串口程序一样,里面也有个结构体,用这种方式整合所有的函数和变量。
/** =============================================================== ** Filename: ds1302.h* Description:* Version: 1.0* Created:* Revision: none* Compiler: Nios II 9.0 IDE* Author: AVIC* Company: 金沙滩工作室* ==============================================================*/#ifndef DS1302_H_#define DS1302_H_#include "../inc/sopc.h"//对于双向的IO,操作的过程中要注意改变IO口的方向,置1为输出,置0为输入#define RTC_DATA_OUT RTC_DATA->DIRECTION = 1#define RTC_DATA_IN RTC_DATA->DIRECTION = 0typedef struct{void (* set_time)(unsigned char *ti);void (* get_time)(char * ti);}DS1302;extern DS1302 ds1302;#endif /*DS1302_H_*/准备工作都做好以后,接下来我们要做的就是写ds1302的驱动了,根据DS1302的时序图来进行编写,首先我来给看看时序图吧,如下图所示,这个是读数据的时序图,这个是写数据时序图还有一个有关寄存器的表格,大家也要注意看一下,如下所示,前面两列是读和写的地址,每次操作时,都先写地址,再传数据。
现在,我们就根据时序图来编写ds1302的驱动,在driver文件夹下建ds1302.c文件,然后添加以下内容,/** =============================================================* Filename: ds1302.c* Description:* Version: 1.0* Created: 2009-11-23* Revision: none* Compiler: Nios II 9.0 IDE* Author: AVIC* Company: 金沙滩工作室** =============================================================*/#include "../inc/ds1302.h"//函数声明static void delay(unsigned int dly);static void write_1byte_to_ds1302(unsigned char da);static unsigned char read_1byte_from_ds1302(void);static void write_data_to_ds1302(unsigned char addr, unsigned char da); static unsigned char read_data_from_ds1302(unsigned char addr);void set_time(unsigned char *ti);void get_time(char *ti);//对DS1302结构体进行初始化,注意结构体中函数指针的初始化方式DS1302 ds1302={.set_time = set_time,.get_time = get_time};/** === FUNCTION====================================================* Name: delay* Description: 延时函数* ================================================================== */void delay(unsigned int dly){for(;dly>0;dly--);}/** === FUNCTION ================================================= * Name: write_1byte_to_ds1302* Description: 向ds1302写入1 byte数据* ===============================================================*/void write_1byte_to_ds1302(unsigned char da){unsigned int i;//写数据的时候,RTC_DATA为输出,先设置其为输出RTC_DATA_OUT;//以下步骤是处理串行数据的的典型方法,一个位一个位的来判断for(i=8; i>0; i--){if((da&0x01)!= 0)RTC_DATA->DATA = 1;elseRTC_DATA->DATA = 0;//根据芯片手册,适当加些延时,不是精确延时delay(10);RTC_SCLK->DATA = 1;delay(20);RTC_SCLK->DATA = 0;delay(10);da >>= 1;}}/** === FUNCTION ================================================== * Name: read_1byte_from_ds1302* Description: 从ds1302读取1 byte数据* ================================================================ */unsigned char read_1byte_from_ds1302(void){unsigned char i;unsigned char da = 0;//当读数据的时候,我们要将数据IO设置为输入RTC_DATA_IN;//以下是典型的读串行数据的方法for(i=8; i>0; i--){delay(10);da >>= 1;if(RTC_DATA->DATA !=0 )da += 0x80;RTC_SCLK->DATA = 1;delay(20);RTC_SCLK->DATA = 0;delay(10);}RTC_DATA_OUT;return(da);}/** === FUNCTION ================================================= * Name: write_data_to_ds1302* Description: 向ds1302写入数据* ===============================================================*/void write_data_to_ds1302(unsigned char addr, unsigned char da){RTC_DATA_OUT;RTC_RST->DATA = 0;//复位,低电平有效RTC_SCLK->DATA = 0;delay(40);RTC_RST->DATA = 1;//先写地址,再写数据,每次写1字节write_1byte_to_ds1302(addr); // 地址,命令write_1byte_to_ds1302(da); // 写1Byte数据RTC_SCLK->DATA = 1;RTC_RST->DATA = 0;delay(40);}/** === FUNCTION =================================================== * Name: read_data_from_ds1302* Description: 从ds1302读取数据* ================================================================= */unsigned char read_data_from_ds1302(unsigned char addr){unsigned char da;RTC_RST->DATA = 0;RTC_SCLK->DATA = 0;delay(40);RTC_RST->DATA = 1;//先写地址,再读数据write_1byte_to_ds1302(addr);da = read_1byte_from_ds1302();RTC_SCLK->DATA = 1;RTC_RST->DATA = 0;delay(40);return(da);}/** === FUNCTION ================================================== * Name: set_time* Description: 设置时间* ================================================================ */void set_time(unsigned char *ti){unsigned char i;unsigned char addr = 0x80;write_data_to_ds1302(0x8e,0x00); // 控制命令,WP=0,写操作for(i =7;i>0;i--){write_data_to_ds1302(addr,*ti); // 秒分时日月星期年ti++;addr +=2;}write_data_to_ds1302(0x8e,0x80); // 控制命令,WP=1,写保护}/** === FUNCTION ================================================== * Name: get_time* Description: 获取时间 ,读取的时间为BCD码,需要转换成十进制* ================================================================ */void get_time(char *ti){unsigned char i;unsigned char addr = 0x81;char time;for (i=0;i<7;i++){time=read_data_from_ds1302(addr);//读取的时间为BCD码 ti[i] = time/16*10+time%16;//格式为: 秒分时日月星期年addr += 2;}}OK,我们的驱动写好了,现在我们来写一个main函数来验证一下我们的驱动是否好用吧。