zoukankan      html  css  js  c++  java
  • 【连载】【FPGA黑金开发板】NIOS II那些事儿实时时钟 (七)

    声明:本文为原创作品,版权归本博文作者所有,如需转载,请注明出处http://www.cnblogs.com/kingst/

    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部分的代码,如下表所示,

    #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 = 0
    
    typedef struct{
            void (* set_time)(unsigned char *ti);
            void (* get_time)(char * ti);
    }DS1302;
    
    extern DS1302 ds1302;
    
    #endif /*DS1302_H_*/

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

    clip_image010

    这个是写数据时序图

    clip_image012

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

    clip_image014

    现在,我们就根据时序图来编写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; 
                    else 
                            RTC_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函数来验证一下我们的驱动是否好用吧。

    #include <unistd.h>
    #include "../inc/uart.h"
    #include "../inc/ds1302.h"
    
    #include <stdio.h>
    
    unsigned char time[7] = {0x00,0x19,0x14,0x17,0x03,0x17,0x10};//格式为: 秒 分 时 日 月 星期 年 
    
    int main()
    {
        unsigned char buffer[50]="\0";
    //设置时间
        ds1302.set_time(time);
        
    while(1){   
        //获取时间         
            ds1302.get_time(time);          
            
            //将我们要的时间格式化一下,如2010-4-4 15:25:00
            sprintf(buffer,"20%d-%d-%d %d:%d:%d\n",
    time[6],time[4],time[3],time[2],time[1],time[0]);
    
            //通过串口发送出去
            uart.send_string(sizeof(buffer),buffer);
            
    //延时1秒
            usleep(1000000);
        }
        
        return 0;
    }

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

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

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

    clip_image016

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

  • 相关阅读:
    asp.net后台注册js的四种方法
    前端代码标准最佳实践:CSS
    开始ASP.NET MVC5之旅
    Android应用中如何保护JAVA代码
    C# Winform 拦截关闭按钮触发的事件
    idea没有转换web选项
    Mysql Too many connections
    SpringBoot添加WEB模块
    Maven报错Failed to collect dependencies at com.***:***:jar:1.0.0
    CentOS出现内存溢出
  • 原文地址:https://www.cnblogs.com/kingst/p/1704210.html
Copyright © 2011-2022 走看看