声明:本文转载于http://www.cnblogs.com/kingst,版权归黑金动力社区(http://www.heijin.org)所有。
简介 这一节,我将给大家讲解实时时钟部分的内容,我在黑金板上用的实时时钟芯片是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部分的代码,如下表所示,
01 | #define RTC_DATA_NAME "/dev/RTC_DATA" |
02 | #define RTC_DATA_TYPE "altera_avalon_pio" |
03 | #define RTC_DATA_BASE 0x00201030 |
07 | * RTC_SCLK configuration |
11 | #define RTC_SCLK_NAME "/dev/RTC_SCLK" |
12 | #define RTC_SCLK_TYPE "altera_avalon_pio" |
13 | #define RTC_SCLK_BASE 0x00201040 |
16 | * RTC_nRST configuration |
20 | #define RTC_NRST_NAME "/dev/RTC_nRST" |
21 | #define RTC_NRST_TYPE "altera_avalon_pio" |
22 | #define RTC_NRST_BASE 0x00201050 |
在这些代码中,我们需要用到的是以下部分
1 | #define RTC_DATA_BASE 0x00201030 |
2 | #define RTC_SCLK_BASE 0x00201040 |
3 | #define RTC_NRST_BASE 0x00201050 |
好的,接下来,我们就开始写程序吧
第一步,修改sopc.h文件,加入以下代码到sopc.h中
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) |
没什么可说的,接下来我们在inc文件夹下建立ds1302.h,在其中加入以下内容,跟串口程序一样,里面也有个结构体,用这种方式整合所有的函数和变量。
02 | * =============================================================== |
09 | * Compiler: Nios II 9.0 IDE |
12 | * ============================================================== |
17 | #include "../inc/sopc.h" |
19 | //对于双向的IO,操作的过程中要注意改变IO口的方向,置1为输出,置0为输入 |
20 | #define RTC_DATA_OUT RTC_DATA->DIRECTION = 1 |
21 | #define RTC_DATA_IN RTC_DATA->DIRECTION = 0 |
24 | void (* set_time)(unsigned char *ti); |
25 | void (* get_time)(char * ti); |
准备工作都做好以后,接下来我们要做的就是写ds1302的驱动了,根据DS1302的时序图来进行编写,首先我来给看看时序图吧,如下图所示,这个是读数据的时序图,
这个是写数据时序图
还有一个有关寄存器的表格,大家也要注意看一下,如下所示,前面两列是读和写的地址,每次操作时,都先写地址,再传数据。
现在,我们就根据时序图来编写ds1302的驱动,在driver文件夹下建ds1302.c文件,然后添加以下内容,
002 | * ============================================================= |
006 | * Created: 2009-11-23 |
008 | * Compiler: Nios II 9.0 IDE |
012 | * ============================================================= |
015 | #include "../inc/ds1302.h" |
017 | static void delay(unsigned int dly); |
018 | static void write_1byte_to_ds1302(unsigned char da); |
019 | static unsigned char read_1byte_from_ds1302(void); |
020 | static void write_data_to_ds1302(unsigned char addr, unsigned char da); |
021 | static unsigned char read_data_from_ds1302(unsigned char addr); |
022 | void set_time(unsigned char *ti); |
023 | void get_time(char *ti); |
025 | //对DS1302结构体进行初始化,注意结构体中函数指针的初始化方式 |
027 | .set_time = set_time, |
032 | * === FUNCTION ==================================================== |
035 | * ================================================================== |
037 | void delay(unsigned int dly) |
043 | * === FUNCTION ================================================= |
044 | * Name: write_1byte_to_ds1302 |
045 | * Description: 向ds1302写入1 byte数据 |
046 | * =============================================================== |
048 | void write_1byte_to_ds1302(unsigned char da) |
051 | //写数据的时候,RTC_DATA为输出,先设置其为输出 |
053 | //以下步骤是处理串行数据的的典型方法,一个位一个位的来判断 |
060 | //根据芯片手册,适当加些延时,不是精确延时 |
072 | * === FUNCTION ================================================== |
073 | * Name: read_1byte_from_ds1302 |
074 | * Description: 从ds1302读取1 byte数据 |
075 | * ================================================================ |
077 | unsigned char read_1byte_from_ds1302(void) |
080 | unsigned char da = 0; |
081 | //当读数据的时候,我们要将数据IO设置为输入 |
088 | if(RTC_DATA->DATA !=0 ) |
103 | * === FUNCTION ================================================= |
104 | * Name: write_data_to_ds1302 |
105 | * Description: 向ds1302写入数据 |
106 | * =============================================================== |
108 | void write_data_to_ds1302(unsigned char addr, unsigned char da) |
111 | RTC_RST->DATA = 0;//复位,低电平有效 |
117 | write_1byte_to_ds1302(addr); // 地址,命令 |
118 | write_1byte_to_ds1302(da); // 写1Byte数据 |
127 | * === FUNCTION =================================================== |
128 | * Name: read_data_from_ds1302 |
129 | * Description: 从ds1302读取数据 |
130 | * ================================================================= |
132 | unsigned char read_data_from_ds1302(unsigned char addr) |
143 | write_1byte_to_ds1302(addr); |
144 | da = read_1byte_from_ds1302(); |
156 | * === FUNCTION ================================================== |
159 | * ================================================================ |
161 | void set_time(unsigned char *ti) |
164 | unsigned char addr = 0x80; |
166 | write_data_to_ds1302(0x8e,0x00); // 控制命令,WP=0,写操作 |
170 | write_data_to_ds1302(addr,*ti); // 秒 分 时 日 月 星期 年 |
176 | write_data_to_ds1302(0x8e,0x80); // 控制命令,WP=1,写保护 |
179 | * === FUNCTION ================================================== |
181 | * Description: 获取时间 ,读取的时间为BCD码,需要转换成十进制 |
182 | * ================================================================ |
184 | void get_time(char *ti) |
187 | unsigned char addr = 0x81; |
191 | time=read_data_from_ds1302(addr);//读取的时间为BCD码 |
192 | ti[i] = time/16*10+time%16;//格式为: 秒 分 时 日 月 星期 年 |
OK,我们的驱动写好了,现在我们来写一个main函数来验证一下我们的驱动是否好用吧。
02 | #include "../inc/uart.h" |
03 | #include "../inc/ds1302.h" |
07 | unsigned char time[7] = {0x00,0x19,0x14,0x17,0x03,0x17,0x10};//格式为: 秒 分 时 日 月 星期 年 |
11 | unsigned char buffer[50]="\0"; |
13 | ds1302.set_time(time); |
17 | ds1302.get_time(time); |
19 | //将我们要的时间格式化一下,如2010-4-4 15:25:00 |
20 | sprintf(buffer,"20%d-%d-%d %d:%d:%d\n", |
21 | time[6],time[4],time[3],time[2],time[1],time[0]); |
24 | uart.send_string(sizeof(buffer),buffer); |
在上面的程序中,我们获取时间后通过串口发送到上位机,这样也复习了我们上一节讲的串口程序。当然,大家也可以直接通过printf()打印出来。
在操作ds1302的时候有一点需要注意,ds1302的输入和输出都是8421BCD码进行的,所以我们需要对其进行转换。不过,输入的时候我是直接输入16进制,比如,我们设置分钟为10的话,我直接输入十六进制的0x10,这样就不需要转化了。而在输出的时候是必须要转化的,大家在写程序的时候注意这一点。
好了,我们来看看我们的劳动果实,看看串口传出的数据吧。
这一节就讲到这吧,如果有问题请给我留言,或者加入我们的NIOS技术群:100364900,让我们共同讨论解决,谢谢大家!
阅读(1347) | 评论(0) | 转发(0) |