IIC个人感觉驱动的中断步骤还是挺麻烦的,
要按照步骤流程来,程序如下:
/******************************
*文件名:IIC.c *
*功能: AT24C02底层驱动 *
*创建者: 潘星宇 *
*最后修改:2013.03.20 *
*备注: *
* *
*******************************/
#include "IIC.h"
static U8 _iicData[IICBUFSIZE]; //IIC数据缓冲区
static volatile int _iicDataCount; //IIC数据计数器它是保证数据地址和数据值发送完了
static volatile int _iicStatus; //IIC状态标志
static volatile int _iicMode; //IIC模式变量
static int _iicPt; //IIC读数据计数器,用于修改读回的数据缓冲区的地存放在址
/**************************************************************************
***** 函数名: IIC_init(void)
***** 功 能: IIC初始化
***** 参 数: 无
***** 返回值: 无
***** 创建者:
***** 创建时间:
***** 最后更新:
****************************************************************************/
void IIC_init(void)
{
/*第一步:功能选择 GPE15:IICSDA , GPE14:IICSCL。禁止内部上拉*/
//rGPECON = 0xa0000000; // 1010 GPE15:IICSDA , GPE14:IICSCL
rGPECON &= 0x0fffffff;
rGPECON |= 0xa0000000; // 1010 GPE15:IICSDA , GPE14:IICSCL
/*第二步:将中断函数地址送给IIC中断向量地址,pISR_IIC:IIC中断服务程序地址寄存器 IicInt为IIC中断服务函数名*/
pISR_IIC = (unsigned)IicInt;
/*第三步:让IIC中断源有效(IIC中断源使能)*/
rINTMSK &= ~(BIT_IIC); //~(0x1<<27)
/***********************************************************************
***IICCON[7]:IIC总线应答使能位
***IICCON[6]:IIC总线传输时钟预定标器源时钟选择位,
0:IICCLK = fPCLK /16 ; 1: IICCLK = fPCLK/512
***IICCON[5]:IIC总线接收发送中断使能位0:无效即禁止中断 1:有效,允许中断
***IICCON[4]:IIC总线接收发送中断挂起标志
1 中断挂起(读)恢复IIC传输(写),
0无中断挂起(读) N/A(写);
//中断标记,此位用来标识是否有IIC中断发生,读出为0时表示没有中断发生,
//读出为1时表示有中断发生。当此位为1时,SCL线被拉低,此时所有IIC传输停止;
//如果要继续传输,需写入0清除它。
***IICCON[3:0]:IIC总线传输时钟预写器,Tx clock = IICCLK/(IICCON[3:0]+1) 与IICCON[6]有关
*******************************************************************/
/*Enable ACK, Prescaler IICCLK=PCLK/16, Enable interrupt,
Transmit clock value Tx clock=IICCLK/16
If PCLK 50.7MHz, IICCLK = 3.17MHz, Tx Clock = 0.198MHz */
//第四步:设置IICCON寄存器,ACK信号使能,16分频时钟,TX/RX中断允许,继续传输,预分频系数为16。
rIICCON = (1<<7) | (0<<6) | (1<<5) | (0xf);//IICCON[4]上电为0,所以不需要设置
/* 第五步:设置本机当从机时的地址,2440在IIC空闲的情况下通常配置为从机模式*/
rIICADD = 0x10;
/****************************************************************************
***IICSTAT[7:6] 00: 从接收模式 01: 从发送模式
10: 主接收模式 11: 主发送模式
***IICSTAT[5] IIC总线忙状态位 0:(读)不忙,(写)停止信号生成;
1:(读)忙 (写)开始信号输出.在开始信号后,IICDS中的数据自动被传输。
***IICSTAT[4] IIC总线数据输出使能位。
0:无效Rx/Tx 1:有效Rx/Tx
***IICSTAT[3] IIC总线仲裁过程状态标志位
0:总线仲裁成功 1:在串行IO中总线仲裁失败
***IICSTAT[2] IIC总线address-as-slave状态标志位:
当检测到开始或停止条件,该位被清除;
1:收到的从设备地址和IICADD中的地址匹配 。
***IICSTAT[1] IIC总线地址0状态标志位.
0:当检测到开始或停止条件,该位被清除;
1:收到的从地址是00000000b
***IICSTAT[0] IC总线最后收到位状态标志位;
0:最后收到位是0(收到ACK); 1:最后收到位是1(未收到ACK)
****************************************************************************/
//第六步:IIC总线数据输出使能位,允许发送,接收数据
rIICSTAT = 0x10; ///*输出使能*/ 0001 0000
/****************************************************************************
****IICLC[2]:IIC总线滤波器使能位
****IICLC[0:1]:IIC总线SDA线延时长度选择位
00: 0 clocks 01: 5 clocks 10: 10 clocks 11: 15 clocks
****************************************************************************/
//第七步:IICLC IIC数据经过滤波,且延时5个时钟 ???
rIICLC = (1<<2)|(1); /* 一般配置为5或15个时钟就可以了*/
//IICLC[2]表示将1个pclk时钟周期内SDA信号跳变两次就滤除,不处理,
//IICLC[1:0]表示将SDA数据线上的数据保持几个pclk时钟周期
}
/**************************************************************************
***** 函数名: Wr24C080(void)
***** 功 能: 写一个字节到AT24C08
***** 参 数: slvAddr 从机地址 addr内部子地址 data要写的数据
***** 返回值: 无
***** 创建者:
***** 创建时间:
***** 最后更新:
***Wr24C080(0xa0,(U8)i,i);
****************************************************************************/
void Wr24C080(U32 slvAddr,U32 addr,U8 data)
{
U32 i = 0;
/*第一步:设置标志位:设置IIC模式,主发送,仅是一个标志位,(设置两个标志、将要写入数据,写入那个地址暂存)*/
_iicMode = WRDATA; //(1)
/*数据索引设置为0*/
_iicPt = 0;
/*把从机内部地址存放到_iicData[0]*/ //芯片内部地址
_iicData[0] = (U8)addr;
/*把要写入的数据存放到_iicData[1]*/
_iicData[1] = data;
/*写数据模式总共要发送三个字节数据:从机地址,从机内部地址和要
写入的数据。所以在此阶段下要进入三个次中断服务程序,所以设置为2,
因为下面判断是以while (_iicDataCount!=-1);为判断条件的*/
_iicDataCount = 2;
/*第二步:写入芯片地址(即丛机地址),0xa0 把从设备地址写入IICDS*/
//IICDS寄存器的位[7:0],保存的是要发送或已经接收的数据
rIICDS = slvAddr;
//第三步:设置为主机模式及发起发送信号及允许中断
/*设置为主机模式,发送起始信号*/ //IICSTAT[7:6]为10即为主机发送器模式
rIICSTAT = 0xf0; //1111 0000 IICSTAT[5]=1发出起始信号,IICSTAT[4]=1,使能接收/发送功能。
///*第四步:等待发送完成,数据发送完毕,然后就进入中断服务函数
//(进入中断三次才可能等于-1)*/
while (_iicDataCount!=-1)
{
i++;
if (i>80000000)
{
break;
}
}
}
/**************************************************************************
***** 函数名: Rd24C080(void)
***** 功 能: 写一个字节到AT24C08
***** 参 数: slvAddr 从机地址 addr内部子地址
***** 返回值: 读取的数据U8
***** 创建者:
***** 创建时间:
***** 最后更新:
****************************************************************************/
U8 Rd24C080(U32 slvAddr,U32 addr)
{
U32 i = 0;
//第一步:设置相关标志位
_iicMode = SETRDADDR; //4设置IIC模式,写地址,仅是一个标志位
_iicPt = 0; //数据索引设置为0
_iicData[0] = (U8)addr; //把设置字地址存放到_iicData[0]
_iicDataCount = 1; //计数器设置为 1
rIICDS = slvAddr; //0xa0 把从设备地址写入IICDS
/*第二步:设置为主机模式,发送起始信号*/
rIICSTAT = 0xf0;
/* 发送开始信号后IIC就会把前面装入rIICDS中的从机地址发送出去,
当从机地址发送完成后,进入中断服务程序,在中断服务程序执行
_iicDataCount,_iicDataCount值变为0,同时把_iicData[0](这时里面的数据
实际上是24C08的内部的地址)送给rIICDS,恢复IIC传输,退出中断服务程序。
当发送完成后再次进入中断服务程序, 在中断服务程序执行_iicDataCount,
_iicDataCount值变为-1,退出中断服务程序.
*/
/*第三步:等待发送完毕,如果发送完成,进入中断函数,等待把从机地址和从机内部地址发送完成(进入中断2次才可能等于-1)*/
while (_iicDataCount!=-1); //该步:发送芯片地址,发送芯片内部地址
//第四步:将写变成读,并且重新设置相应标志位
/*设置为取数据模式*/
_iicMode = RDDATA; //3
_iicPt = 0;
/*取数据模式上还要再发一次从机地址加读方向位,这会产生一次中断,
读指定单元的数据也会产生一次中断。所以在此阶段下要进入两次中断
服务程序,的以设置为1,因为下面判断是以_iicDataCount!=-1为判断条件的*/
_iicDataCount = 1;
/*把从机地址装入数据寄存器*/
rIICDS = slvAddr;
/*发送起始信号,配置为主接收模式*/
rIICSTAT = 0xb0; //1011
/*恢复IIC传输*/
rIICCON = 0xaf; //Resumes IIC operation.
/*第五步:等待把数据读取回来*/
while (_iicDataCount!=-1)
{
i++;
if (i>80000000)
{
break;
}
}
/*第六步:把数据存放到data指向的单元中*/
return(_iicData[0]);
}
/**************************************************************************
***** 函数名: IicInt(void)
***** 功 能: IIC中断服务程序
***** 参 数: 无
***** 返回值: 无
***** 创建者:
***** 创建时间:
***** 最后更新:
****************************************************************************/
void __irq IicInt(void)
{
U32 iicSt,i;
/*进入中断后第一步:清中断标志*/
rSRCPND = BIT_IIC; //(0x1<<27) 为1清除中断屏蔽,即中断来了进行处理
rINTPND = BIT_IIC; //(0x1<<27) 为1允许中断
//第二步:判定是否正常(IICST=IICSTAT),当出现如下4种情况也可以进入中断,一般用不到。
//根据需要可以添加如下四种设置,该程序没用到
if (iicSt & 0x8){} //当仲裁失败
if (iicSt & 0x4){} //当接收到的从机地址与IICADD中的相同
if (iicSt & 0x2){} //当接收到从机地址是 0000000b
if (iicSt & 0x1){} //当没有接收到应答信号
switch (_iicMode) //判定是处理那一种阶段:读、写、写地址三种
{
/*处于读数据模式*/
case RDDATA: //3
if ((_iicDataCount--)==0) //0,
{
/*读取接收到的数据*/
_iicData[0] = rIICDS;
/*配置为发送停止信号,主接收模式*/
rIICSTAT = 0x90; //1001
/*恢复IIC总线传输,以便把停止信号发送出去*/
rIICCON = 0xaf;
/*延时一会,等待停止信号有效,但不能太长*/
delay(1);
//Too long time...
//The pending bit will not be set after issuing stop condition.
break;
}
/*如果是最后一个数据,重启IIC传输,但禁止发送应答信号*/
if ((_iicDataCount)==0)
{
/*重启支IIC传输,但不再发送应答信号*/
rIICCON = 0x2f; //0010 1111
}
/*如果不是最后一个数据*/
else
{
/*如果不是最后一个数据,重启支IIC传输,同时允许发送应答信号*/
rIICCON = 0xaf;
}
break;
/*处于写数据模式*/
case WRDATA: //1
/*如果已经是最后一个数据*/
if ((_iicDataCount--)==0) //2,1,0
{
/*配置为发送停止信号,主发送模式*/
rIICSTAT = 0xd0; //1101 0000 写IICSTAT[5]此位时0:发出P信号(结束信号)
/*恢复IIC总线传输,以便把停止信号发送出去*/
rIICCON = 0xaf; //1010 1111
/*延时一会,等待停止信号有效,但不能太长*/
for (i=0;i<100;i++); //Wait until stop condtion is in effect.
//Too long time...
//The pending bit will not be set after issuing stop condition.
break;
}
/*把下一个要发送的数据写入到数据寄存器中*/
rIICDS = _iicData[_iicPt++]; //_iicData[0] has dummy.
/*延时,直到IICSCL时钟线上跳,时间可以适当放宽*/
for (i=0;i<10;i++); //for setup time until rising edge of IICSCL
/*恢复IIC传输*/
rIICCON = 0xaf; //resumes IIC operation.
break;
/*处于写地址模式*/
case SETRDADDR:
/*如果已经是最后一个数据*/
if ((_iicDataCount--)==0) //0,-1
{
break; //IIC operation is stopped because of IICCON[4]
}
/*把一个个要发送的数据放入到数据寄存器中*/
rIICDS = _iicData[_iicPt++];
/*延时,直到IICSCL时钟线上跳,时间可以适当放宽*/
for (i=0;i<10;i++);
/*恢复IIC传输*/
rIICCON = 0xaf; //1010 1111
break;
default:
break;
}
}
按照程序中每一步来执行就没有问题了,这个IIC用来控制一个EEPROM的。
头文件如下:
#ifndef IIC_H
#define IIC_H
#include <string.h>
#include "2440addr.h"
#include "def.h"
#include "IIC.h"
#include "UART.h"
#include "common_functions.h"
#define WRDATA (1)
#define POLLACK (2)
#define RDDATA (3)
#define SETRDADDR (4)
#define IICBUFSIZE 0x20
#define EEPROM_ADDRESS 0xa0 //EEPROM地址
#define IIC_DATA_ADDRESS 0xff //用于储存判断是否校准过触摸屏的数据的地址
#define IIC_DATA 0xf0 //用于判断是否校准过触摸屏的数据的值
#define PLUS 0x00 //正数
#define MINUS 0x01 //负数
//以下0~6为校准用的参数储存地址
#define TOUCH_01_ADDRESS 0x00 //最高8位
#define TOUCH_02_ADDRESS 0x01 //次高8位
#define TOUCH_03_ADDRESS 0x02 //次低8位
#define TOUCH_04_ADDRESS 0x03 //最低8位
#define TOUCH_10_ADDRESS 0x10 //参数1
#define TOUCH_11_ADDRESS 0x11
#define TOUCH_12_ADDRESS 0x12
#define TOUCH_13_ADDRESS 0x13
#define TOUCH_20_ADDRESS 0x20 //参数2
#define TOUCH_21_ADDRESS 0x21
#define TOUCH_22_ADDRESS 0x22
#define TOUCH_23_ADDRESS 0x23
#define TOUCH_30_ADDRESS 0x30 //参数3
#define TOUCH_31_ADDRESS 0x31
#define TOUCH_32_ADDRESS 0x32
#define TOUCH_33_ADDRESS 0x33
#define TOUCH_40_ADDRESS 0x40 //参数4
#define TOUCH_41_ADDRESS 0x41
#define TOUCH_42_ADDRESS 0x42
#define TOUCH_43_ADDRESS 0x43
#define TOUCH_50_ADDRESS 0x50 //参数5
#define TOUCH_51_ADDRESS 0x51
#define TOUCH_52_ADDRESS 0x52
#define TOUCH_53_ADDRESS 0x53
#define TOUCH_60_ADDRESS 0x60 //参数6
#define TOUCH_61_ADDRESS 0x61
#define TOUCH_62_ADDRESS 0x62
#define TOUCH_63_ADDRESS 0x63
#define DOOR_PASSWOR1_1 0x70 //用户账号1密码存储区域
#define DOOR_PASSWOR1_2 0x71
#define DOOR_PASSWOR1_3 0x72
#define DOOR_PASSWOR1_4 0x73
#define DOOR_PASSWOR1_5 0x74
#define DOOR_PASSWOR1_6 0x75
#define DOOR_PASSWOR2_1 0x80 //用户账号2密码存储区域
#define DOOR_PASSWOR2_2 0x81
#define DOOR_PASSWOR2_3 0x82
#define DOOR_PASSWOR2_4 0x83
#define DOOR_PASSWOR2_5 0x84
#define DOOR_PASSWOR2_6 0x85
#define DOOR_PASSWOR3_1 0x90 //用户账号3密码存储区域
#define DOOR_PASSWOR3_2 0x91
#define DOOR_PASSWOR3_3 0x92
#define DOOR_PASSWOR3_4 0x93
#define DOOR_PASSWOR3_5 0x94
#define DOOR_PASSWOR3_6 0x95
/**************************************************************************
***** 函数名: IIC_init(void)
***** 功 能: IIC初始化
***** 参 数: 无
***** 返回值: 无
***** 创建者:
***** 创建时间:
***** 最后更新:
****************************************************************************/
extern void IIC_init(void);
/**************************************************************************
***** 函数名: Wr24C080(void)
***** 功 能: 写一个字节到AT24C08
***** 参 数: slvAddr 从机地址 addr内部子地址 data要写的数据
***** 返回值: 无
***** 创建者:
***** 创建时间:
***** 最后更新:
***Wr24C080(0xa0,(U8)i,i);
****************************************************************************/
extern void Wr24C080(U32 slvAddr,U32 addr,U8 data);
/**************************************************************************
***** 函数名: Rd24C080(void)
***** 功 能: 写一个字节到AT24C08
***** 参 数: slvAddr 从机地址 addr内部子地址
***** 返回值: 读取的数据U8
***** 创建者:
***** 创建时间:
***** 最后更新:
****************************************************************************/
extern U8 Rd24C080(U32 slvAddr,U32 addr);
/**************************************************************************
***** 函数名: IicInt(void)
***** 功 能: IIC中断服务程序
***** 参 数: 无
***** 返回值: 无
***** 创建者:
***** 创建时间:
***** 最后更新:
****************************************************************************/
extern void __irq IicInt(void);
#endif