原创 【连载】NIOS那些事儿--IIC总线实验(十)

2010-8-2 00:20 5846 4 4 分类: FPGA/CPLD

黑金动力社区

黑金动力社区:http://www.heijin.org

声明:本文为原创作品,版权归本博文作者所有,如需转载,请注明出处



 


简介 


      这一节,我们来讲一讲有关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总线的时序图。



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


硬件开发


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



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


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



      分配好管脚以后,我们运行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;


}



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


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

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
4
关闭 站长推荐上一条 /3 下一条