Chinaunix首页 | 论坛 | 博客
  • 博客访问: 132847
  • 博文数量: 55
  • 博客积分: 1870
  • 博客等级: 上尉
  • 技术积分: 540
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-21 20:51
文章分类

全部博文(55)

文章存档

2011年(27)

2009年(3)

2008年(25)

我的朋友

分类: C/C++

2011-05-04 13:22:34

声明:本文转载于http://www.cnblogs.com/kingst,版权归黑金动力社区(http://www.heijin.org)所有。
fpga简介

      这一节,我将给大家讲解实时时钟部分的内容,我在黑金板上用的实时时钟芯片是DS1302,这块芯片很常见,性价比也很高。我们主要来讲如何在NIOS中实现其功能,所以DS1302功能介绍我简单概括一下,有问题的百度一下就都知道了。

      DS1302是DALLAS公司推出的涓流充电实时时钟芯片,内含一个实时时钟/日历和31字节静态RAM,仅需要三根线:RES(复位),I/O(数据线),SCLK(串行时钟)。时钟/RAM 的读/写数据以一个字节或多达 31 个字节的字符组方式通信 DS1302 工作时功耗很低,保持数据和时钟信息时功率小于 1mW。下面看一下电路图吧,下图所示,很简单,三根线就可以搞定了。

clip_image002

硬件开发

      首先,我们需要在软核中构建三个PIO模块,方法跟以前讲的一样。需要注意的是RTC_DATA这个PIO,在构建的过程中,我们将其选择为双向的IO口,因为它是数据线,既要输入也需要输出,如下图所示,红圈处就是我们需要注意的地方,其他两个IO口设置为仅输出。

clip_image004

看看构建好以后的样子吧,如下图是所示

clip_image006

接下来就是自动分配地址,中断,然后开始编译,等待……

回到Quartus后,分配引脚,还是需要注意数据线,也是双向的,分配引脚的时候,要构建双向引脚(bidir),如下图所示。

clip_image008

都设置好以后,我们运行TCL脚本文件,然后开始编译,又是等待……

软件开发

      编译好后,我们打开NIOS II IDE,首先,还是需要编译一下,CTRL+b,编译之后,我们看看system.h有什么变化。观察后可以看出,里面对了,RTC部分的代码,如下表所示,

01#define RTC_DATA_NAME "/dev/RTC_DATA"
02#define RTC_DATA_TYPE "altera_avalon_pio"
03#define RTC_DATA_BASE 0x00201030
04……
05 
06/*
07 * RTC_SCLK configuration
08 *
09 */
10 
11#define RTC_SCLK_NAME "/dev/RTC_SCLK"
12#define RTC_SCLK_TYPE "altera_avalon_pio"
13#define RTC_SCLK_BASE 0x00201040
14……
15/*
16 * RTC_nRST configuration
17 *
18 */
19 
20#define RTC_NRST_NAME "/dev/RTC_nRST"
21#define RTC_NRST_TYPE "altera_avalon_pio"
22#define RTC_NRST_BASE 0x00201050
23……
24/*

在这些代码中,我们需要用到的是以下部分

1#define RTC_DATA_BASE 0x00201030
2#define RTC_SCLK_BASE 0x00201040
3#define RTC_NRST_BASE 0x00201050

好的,接下来,我们就开始写程序吧

第一步,修改sopc.h文件,加入以下代码到sopc.h中

1#define _RTC
2 
3#ifdef      _RTC
4#define     RTC_SCLK                ((PIO_STR *) RTC_SCLK_BASE)
5#define     RTC_DATA                ((PIO_STR *) RTC_DATA_BASE)
6#define     RTC_RST                 ((PIO_STR *) RTC_NRST_BASE)
7#endif /* _RTC */

没什么可说的,接下来我们在inc文件夹下建立ds1302.h,在其中加入以下内容,跟串口程序一样,里面也有个结构体,用这种方式整合所有的函数和变量。

01/*
02 * ===============================================================
03 *
04 *       Filename:  ds1302.h
05 *   Description:
06*        Version:  1.0
07 *        Created: 
08 *       Revision:  none
09 *       Compiler:  Nios II 9.0 IDE
10*         Author:  AVIC
11 *        Company:  金沙滩工作室
12* ==============================================================
13 */
14#ifndef DS1302_H_
15#define DS1302_H_
16 
17#include "../inc/sopc.h"
18 
19//对于双向的IO,操作的过程中要注意改变IO口的方向,置1为输出,置0为输入
20#define RTC_DATA_OUT     RTC_DATA->DIRECTION = 1
21#define RTC_DATA_IN      RTC_DATA->DIRECTION = 0
22 
23typedef struct{
24        void (* set_time)(unsigned char *ti);
25        void (* get_time)(char * ti);
26}DS1302;
27 
28extern DS1302 ds1302;
29 
30#endif /*DS1302_H_*/

准备工作都做好以后,接下来我们要做的就是写ds1302的驱动了,根据DS1302的时序图来进行编写,首先我来给看看时序图吧,如下图所示,这个是读数据的时序图,

clip_image010

这个是写数据时序图

clip_image012

还有一个有关寄存器的表格,大家也要注意看一下,如下所示,前面两列是读和写的地址,每次操作时,都先写地址,再传数据。

clip_image014

现在,我们就根据时序图来编写ds1302的驱动,在driver文件夹下建ds1302.c文件,然后添加以下内容,

001/*
002 * =============================================================
003*       Filename:  ds1302.c
004*   Description:
005*        Version:  1.0
006 *        Created:  2009-11-23
007 *       Revision:  none
008 *       Compiler:  Nios II 9.0 IDE
009*         Author:  AVIC
010 *        Company:  金沙滩工作室
011 *
012 * =============================================================
013 */
014  
015#include "../inc/ds1302.h"
016//函数声明
017static void delay(unsigned int dly);
018static void write_1byte_to_ds1302(unsigned char da);
019static unsigned char read_1byte_from_ds1302(void);
020static void write_data_to_ds1302(unsigned char addr, unsigned char da);
021static unsigned char read_data_from_ds1302(unsigned char addr);
022void set_time(unsigned char *ti);
023void get_time(char *ti);
024 
025//对DS1302结构体进行初始化,注意结构体中函数指针的初始化方式
026DS1302 ds1302={
027        .set_time = set_time,
028        .get_time = get_time
029};
030  
031/*
032 * ===  FUNCTION  ====================================================
033 *         Name:  delay
034 * Description:  延时函数
035 * ==================================================================
036 */
037void delay(unsigned int dly)
038{  
039        for(;dly>0;dly--);
040}
041 
042/*
043 * ===  FUNCTION  =================================================
044 *         Name:  write_1byte_to_ds1302
045 *  Description:  向ds1302写入1 byte数据
046 * ===============================================================
047 */
048void write_1byte_to_ds1302(unsigned char da)
049{
050        unsigned int i;
051        //写数据的时候,RTC_DATA为输出,先设置其为输出
052        RTC_DATA_OUT;
053        //以下步骤是处理串行数据的的典型方法,一个位一个位的来判断
054        for(i=8; i>0; i--)
055        {
056                if((da&0x01)!= 0)
057                        RTC_DATA->DATA = 1;
058                else
059                        RTC_DATA->DATA = 0;
060                //根据芯片手册,适当加些延时,不是精确延时        
061                delay(10);
062                RTC_SCLK->DATA = 1;
063                delay(20);
064                RTC_SCLK->DATA = 0;
065                delay(10);
066 
067                da >>= 1;
068        }
069}
070  
071/*
072 * ===  FUNCTION  ==================================================
073 *         Name:  read_1byte_from_ds1302
074 *  Description:  从ds1302读取1 byte数据
075 * ================================================================
076 */
077unsigned char read_1byte_from_ds1302(void)
078{
079        unsigned char i;
080        unsigned char da = 0;
081        //当读数据的时候,我们要将数据IO设置为输入
082        RTC_DATA_IN;
083        //以下是典型的读串行数据的方法
084        for(i=8; i>0; i--)
085        {
086                delay(10);
087                da >>= 1;
088                if(RTC_DATA->DATA !=0 )
089                        da += 0x80;
090 
091                RTC_SCLK->DATA = 1;
092                delay(20);
093                RTC_SCLK->DATA = 0;
094                delay(10);
095        }
096          
097        RTC_DATA_OUT;
098          
099        return(da);
100}
101 
102/*
103 * ===  FUNCTION  =================================================
104 *         Name:  write_data_to_ds1302
105 *  Description:  向ds1302写入数据
106 * ===============================================================
107 */
108void write_data_to_ds1302(unsigned char addr, unsigned char da)
109{
110        RTC_DATA_OUT;
111        RTC_RST->DATA = 0;//复位,低电平有效
112        RTC_SCLK->DATA = 0;
113        delay(40);
114         
115        RTC_RST->DATA = 1;
116        //先写地址,再写数据,每次写1字节
117        write_1byte_to_ds1302(addr); // 地址,命令
118        write_1byte_to_ds1302(da); // 写1Byte数据
119         
120        RTC_SCLK->DATA = 1;
121        RTC_RST->DATA = 0;
122         
123        delay(40);
124}
125 
126/*
127 * ===  FUNCTION  ===================================================
128 *         Name:  read_data_from_ds1302
129 *  Description:  从ds1302读取数据
130 * =================================================================
131 */
132unsigned char read_data_from_ds1302(unsigned char addr)
133{
134        unsigned char da;
135         
136        RTC_RST->DATA = 0;
137        RTC_SCLK->DATA = 0;
138         
139        delay(40);
140         
141        RTC_RST->DATA = 1;
142        //先写地址,再读数据
143        write_1byte_to_ds1302(addr);
144        da = read_1byte_from_ds1302();
145         
146        RTC_SCLK->DATA = 1;
147         
148        RTC_RST->DATA = 0;
149         
150        delay(40);
151 
152        return(da);
153}
154 
155/*
156 * ===  FUNCTION  ==================================================
157 *         Name:  set_time
158 *  Description:  设置时间 
159 * ================================================================
160 */
161void set_time(unsigned char *ti)
162{
163        unsigned char i;
164        unsigned char addr = 0x80;
165         
166        write_data_to_ds1302(0x8e,0x00); // 控制命令,WP=0,写操作
167         
168        for(i =7;i>0;i--)
169        {
170                write_data_to_ds1302(addr,*ti); // 秒 分 时 日 月 星期 年
171 
172                ti++;
173                addr +=2;
174        }
175         
176        write_data_to_ds1302(0x8e,0x80); // 控制命令,WP=1,写保护
177}
178/*
179 * ===  FUNCTION  ==================================================
180 *         Name:  get_time
181 *  Description:  获取时间 ,读取的时间为BCD码,需要转换成十进制
182 * ================================================================
183 */
184void get_time(char *ti)
185{
186        unsigned char i;
187        unsigned char addr = 0x81;
188        char time;
189         
190        for (i=0;i<7;i++){
191                time=read_data_from_ds1302(addr);//读取的时间为BCD码
192                ti[i] = time/16*10+time%16;//格式为: 秒 分 时 日 月 星期 年
193                addr += 2;
194        }
195}

OK,我们的驱动写好了,现在我们来写一个main函数来验证一下我们的驱动是否好用吧。

01#include
02#include "../inc/uart.h"
03#include "../inc/ds1302.h"
04 
05#include
06 
07unsigned char time[7] = {0x00,0x19,0x14,0x17,0x03,0x17,0x10};//格式为: 秒 分 时 日 月 星期 年
08 
09int main()
10{
11    unsigned char buffer[50]="\0";
12//设置时间
13    ds1302.set_time(time);
14     
15while(1){  
16    //获取时间        
17        ds1302.get_time(time);         
18         
19        //将我们要的时间格式化一下,如2010-4-4 15:25:00
20        sprintf(buffer,"20%d-%d-%d %d:%d:%d\n",
21time[6],time[4],time[3],time[2],time[1],time[0]);
22 
23        //通过串口发送出去
24        uart.send_string(sizeof(buffer),buffer);
25         
26//延时1秒
27        usleep(1000000);
28    }
29     
30    return 0;
31}

在上面的程序中,我们获取时间后通过串口发送到上位机,这样也复习了我们上一节讲的串口程序。当然,大家也可以直接通过printf()打印出来。

在操作ds1302的时候有一点需要注意,ds1302的输入和输出都是8421BCD码进行的,所以我们需要对其进行转换。不过,输入的时候我是直接输入16进制,比如,我们设置分钟为10的话,我直接输入十六进制的0x10,这样就不需要转化了。而在输出的时候是必须要转化的,大家在写程序的时候注意这一点。

好了,我们来看看我们的劳动果实,看看串口传出的数据吧。

clip_image016

这一节就讲到这吧,如果有问题请给我留言,或者加入我们的NIOS技术群:100364900,让我们共同讨论解决,谢谢大家!

阅读(1341) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~