zoukankan      html  css  js  c++  java
  • 【连载】【FPGA黑金开发板】NIOS II那些事儿IIC总线实验(十)

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

    P1011818

    简介 

          这一节,我们来讲一讲有关IIC总线的实验,在硬件中,我们实用了24LC04,一个512字节的EEPROM。在NIOS II中,没有集成IIC接口,为了实现这一功能,我们有两种途径,一种就是自己写IP核或者移植别人的IP核,另一种方法就是通过IO口模拟IIC总线协议。我们这一节采用的方法是后者,通过IO口模拟IIC总线协议,以达到对24LC04控制读写的目的。

          首先,我简单介绍一下IIC总线的原理,大家稍微了解一下。IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,最高传送速率100kbps。它在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。

    开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。

    结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。

    应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。

    这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。下图就为IIC总线的时序图。

    clip_image002

    简单介绍之后,我们就要开始实践一下了,开始吧

    硬件开发

          首先,需要在软核中添加两个IO模块,并将其命名为SCL和SDA,其中,SCL为output建ports only(仅输出),SDA为Bidirection (tristate) port(双向),建好以后,如下图所示

    clip_image004

          接下来,我们自动分配一下地址,编译。

    完成后,我们回到Quartus界面。然后我们来分配引脚,如下图所示

    clip_image006

          分配好管脚以后,我们运行TCL脚本文件,开始编译(Ctrl+L)……

          编译完成后,我们的硬件部分就结束了,接下来,就是我们的软件开发部分了。

    软件开发

          首先,我们打开NIOS II 9.0 IDE,然后进行编译(Ctrl+B)。

          编译好以后,我们看一下system.h文件,看是否多出了SCL和SDA部分代码。跟我们预期的一样,system.h文件中出现了SCL和SDA部分代码,如下表所示

    /*
     * SCL configuration
     *
     */
    #define SCL_NAME "/dev/SCL"
    #define SCL_TYPE "altera_avalon_pio"
    #define SCL_BASE 0x00201060
    ……
    /*
     * SDA configuration
     *
     */
    
    #define SDA_NAME "/dev/SDA"
    #define SDA_TYPE "altera_avalon_pio"
    #define SDA_BASE 0x00201070
    ……

          在跟大家讨论过程中,我了解到很多人都想知道有关NIOS II自带的API的用法,所以,今天我就用这种方式来实现我们的程序。不过我还是推荐大家用我之前的方式编写程序,道理我已经说过了, 在此不再重复。

    下面我们在inc目录下建立一个iic.h文件,如下表所示

    #ifndef IIC_H_
    #define IIC_H_
    
    #define   OUT     1
    #define   IN      0
    
    typedef struct{
        void (* write_byte)(unsigned short addr, unsigned char dat);
        unsigned char (* read_byte)(unsigned short addr);
    }IIC;
    
    extern IIC iic;
    
    #endif /*IIC_H_*/

    接下来,我们需要在driver下建立iic.c文件,如下表所示

    #include <stdio.h>
    #include <sys/unistd.h>
    #include <io.h>
    
    #include "system.h"
    #include "altera_avalon_pio_regs.h"
    #include "alt_types.h"
    #include "../inc/iic.h"
    
    static alt_u8 read_byte(alt_u16 addr);
    static void write_byte(alt_u16 addr, alt_u8 dat);
    
    IIC iic ={
        .write_byte = write_byte,
        .read_byte = read_byte
    };
    
    
    /* 
     * ===  FUNCTION  ===================================================
     *         Name:  start
     *  Description:  IIC启动
     * =================================================================
     */
    static void start(void)    
    {
        IOWR_ALTERA_AVALON_PIO_DIRECTION(SDA_BASE, OUT);
        IOWR_ALTERA_AVALON_PIO_DATA(SDA_BASE, 1);
        IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 1);
        usleep(10);
        IOWR_ALTERA_AVALON_PIO_DATA(SDA_BASE, 0);
        usleep(5);
    }
    /* 
     * ===  FUNCTION  ===================================================
     *         Name:  uart_send_byte
     *  Description:  IIC停止
     * ==================================================================
     */
    static void stop(void)     
    {
        IOWR_ALTERA_AVALON_PIO_DIRECTION(SDA_BASE, OUT);
        IOWR_ALTERA_AVALON_PIO_DATA(SDA_BASE, 0);
        IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 0);
        usleep(10);
        IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 1);
        usleep(5);
        IOWR_ALTERA_AVALON_PIO_DATA(SDA_BASE, 1);
        usleep(10);
    }
    /* 
     * ===  FUNCTION  ===================================================
     *         Name:  ack
     *  Description:  IIC应答
     * =================================================================
     */
    static void ack(void)     
    {
        alt_u8 tmp;
        
        IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 0);
        IOWR_ALTERA_AVALON_PIO_DIRECTION(SDA_BASE, IN);
        usleep(10);
        IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 1);
        usleep(5);
        tmp = IORD_ALTERA_AVALON_PIO_DATA(SDA_BASE);
        usleep(5);
        IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 0);
        usleep(10);
        
        while(tmp);
    }
    /* 
     * ===  FUNCTION  ===================================================
     *         Name:  iic_write
     *  Description:  IIC写一个字节
     * =================================================================
     */
    void iic_write(alt_u8 dat)    
    {
        alt_u8 i, tmp;
        
        IOWR_ALTERA_AVALON_PIO_DIRECTION(SDA_BASE, OUT);
        
        for(i=0; i<8; i++){
            IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 0);
            usleep(5);
            tmp = (dat & 0x80) ? 1 : 0;
            dat <<= 1;
            IOWR_ALTERA_AVALON_PIO_DATA(SDA_BASE, tmp);
            usleep(5);
            IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 1);
            usleep(10);
        }
    }
    /* 
     * ===  FUNCTION  ===================================================
     *         Name:  read
     *  Description:  IIC读一个字节
     * ==================================================================
     */
    static alt_u8 iic_read(void)    
    {
        alt_u8 i, dat = 0;
        
        IOWR_ALTERA_AVALON_PIO_DIRECTION(SDA_BASE, IN);
     
        for(i=0; i<8; i++){
            IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 0);
            usleep(10);
            IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 1);
            usleep(5);
            dat <<= 1;
            dat |= IORD_ALTERA_AVALON_PIO_DATA(SDA_BASE);
            usleep(5);
        }
      
        usleep(5);
        IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 0);
        usleep(10);
        IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 1);
        usleep(10);
        IOWR_ALTERA_AVALON_PIO_DATA(SCL_BASE, 0);
        
        return dat;
    }
    
    /* 
     * ===  FUNCTION  ===================================================
     *         Name:  write_byte
     *  Description:  向EEPROM写一个字节
     * =================================================================
     */
    static void write_byte(alt_u16 addr, alt_u8 dat) 
    {
        alt_u8 cmd;
        cmd = (0xa0 | (addr >> 7)) & 0xfe;
        
        start();
        iic_write(cmd);
        ack();
        iic_write(addr);
        ack();
        iic_write(dat);
        ack();
        stop();   
    }
    /* 
     * ===  FUNCTION  ===================================================
     *         Name:  read_byte
     *  Description:  从EEPROM读一个字节
     * =================================================================
     */
    static alt_u8 read_byte(alt_u16 addr) 
    {
        alt_u8 cmd, dat;
        cmd = (0xa0 | (addr >> 7)) & 0xfe;
        
        start();
        iic_write(cmd);
        ack();
        iic_write(addr);
        ack();
        start();
        cmd |= 0x01;
        start();
        iic_write(cmd);
        ack();
        dat = iic_read();
        stop();   
        
        return dat;
    }

    最后,我们来建立main.c函数

    #include <unistd.h>
    #include "../inc/iic.h"
    #include <stdio.h>
    #include "alt_types.h"
    
    alt_u8 write_buffer[512], read_buffer[512];
    
    int main()
    {
        alt_u16 i, err;
        alt_u8 dat;
    
        printf("\nWriting data to EEPROM!\n");
        
        //写入512btye的数据,前256个数字为0到255,后256个数据为1
        for(i=0; i<512; i++){
            if(i<256)
                dat = i;
            else
                dat = 1;
                
            iic.write_byte(i, dat);
            write_buffer[i] = dat;
            printf("0x%02x ", dat);
            usleep(10000);
        }
      
        printf("\nReading data from EEPROM!\n");
        
        //将512byte数据读出来并打印
        for(i=0; i<512; i++){
            read_buffer[i] = iic.read_byte(i);
            printf("0x%02x ", read_buffer[i]);
            usleep(1000);
        }  
      
        err = 0;
        printf("\nVerifing data!\n");
        
        //对比数据是否相同,如果有不同,说明读写过程有错误
        for(i=0; i<512; i++){
            if(read_buffer[i] != write_buffer[i])
            err ++;
        }  
      
        if(err == 0)
            printf("\nData write and read successfully!\n");
        else
            printf("\nData write and read failed!--%d errors\n", err);
        
        return 0;
    }

          程序很简单,大家只要对IIC总线有一定的了解就会明白的。

          好的,这节的内容就讲到这,谢谢大家对我的支持。如果有问题,可以给我留言或者直接加入我们的NIOS技术高级群:107122106,也可以加我的qq:984597569

  • 相关阅读:
    185. [USACO Oct08] 挖水井
    JavaEE Tutorials (9)
    vjudge A
    HDU 2089 不要62
    国庆 day 2 下午
    国庆 day 2 上午
    国庆 day 1 下午
    P2899 [USACO08JAN]手机网络Cell Phone Network
    洛谷 P1690 贪婪的Copy
    洛谷 P2209 [USACO13OPEN]燃油经济性Fuel Economy
  • 原文地址:https://www.cnblogs.com/kingst/p/1712429.html
Copyright © 2011-2022 走看看