前两篇已经讲了如何在Linux下操作AT24C04,本篇来实现裸奔的代码吧
首先因为要操作AT24C04当然就得先了解一下这个EEPROM及相关的知识
我们在第一篇中已经介绍了AT24LC04他的设备地址是0xA0如果是读就是0xA1
我们都知道裸机操作设备至少需要两个部分,一个是初始化,一个是实际的操作(读/写)
初始化都需要干些啥呢?
1.初始化GPIO引脚
2.初始化硬件
先说说初始化GPIO引脚,从原理图上
可以知道使用了这两个脚,对应到GPIO就是
GPD1_0 跟GPD1_1 然后找到210的手册找到GPD1的0跟1脚,也就是需要设定为 0x22
然后从AT24LC04的时序上发现
空闲的时候需要为高电平,但是原理图上发现他已经接了上拉电阻,所以空闲时我们只要让GPIO呈现高阻就可以,所以要设定GPIO的PUD寄存器
这样GPIO就設定好了
然后再来是IIC的初始化,因为我们用210提供的II控制器来驱动,所以看下需要设定什么,查找210的手册I2C的部份
1.是说如果我们需要把设备做为从设备需要设定I2CADD显然我们是主设备不管他
2.设定I2CCON寄存器,主要要做启用中断、设定时钟参数
启用中断,主要是操作两个位一个是清空中断标志(bit4)、一个是启用中断(bit5)
设定时钟,因为之前时钟初始化我将我的PCLK_DSYS设置为667M,(bit0-3)跟(bit6)两个合并可以知道时钟信号的公式为 fPCLK 667M / (16 or 512(bit6) * (1+n(bit0-3))
并且最终的时钟要保持在1MHz左右,所以我们用 512分频系数0(实际我测试过16分频bit6跟15系数bit0-3发现还是太快),然后发现bit7自动应答我们也开启他
3.设定I2CSTAT启用串行输出
这就比较简单了所以初始化代码如下
void i2c_init(void)
{
///*****************
//1.设置引脚-I2C复用
GPD1CON &= ~(0xFF <<0);
GPD1CON |= (0x22 <<0);
//设定引脚上拉,但是原理图上已经加了上拉电阻,所以直接悬空就可以了
GPD1PUD &= ~(0xF);
//2.设置I2CCON寄存器
//2.1使能中断
//清除中断标示
I2CCON0 &= ~(1<<4);
//启用中断
I2CCON0 |= (1<<5);
//2.2设定SCL时钟-PCLK=96M/(16 * (11+1)) 大约在500KHz
//I2CCON0 &= ~(1<<6);
//I2CCON0 |= (15<<0);
//改用512分频 96M/512 = 187.5KHz 就正常了
I2CCON0 |= (1<<6); //设置成512分频
//2.3允许返回ack
I2CCON0 |= (1<<7);
//3.设定I2CSTAT寄存器使能串行输出
//3.1设定工作模式为写
I2CSTAT0 |= (1<<4);
//************************************/
}
这样初始化就设定完成再来考虑读写操作
找到210的I2C控制器的流程,顺带需要说的是这里是不涉及设备的部份,所以我们还要结合AT24LC04的手册的写流程一起看
也就是说发送数据部分得先发送数据地址,再发送写入的数据
当数据全部发送完成才走stop = y 这边的流程,因此写成代码就是
void i2c_24c04_write_byte(unsigned char dataAddr,unsigned char data)
{
///************************
//设置为发送模式
I2CSTAT0 |= (0x3<<6);
//写入从设备地址-I2CDS
I2CDS0 = I2C24C04_WRITE_ADDR;
I2CCON0 &= ~(1<<4);
//写0xF0 到I2CSTAT
I2CSTAT0 = 0xF0;
//等待ACK
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//发送数据地址到I2CDS
I2CDS0 = dataAddr;
I2CCON0 &= ~(1<<4);
//等待ACK
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//写入数据
I2CDS0 = data;
I2CCON0 &= ~(1<<4);
//等待ACK
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//写入0xD0到I2CSTAT
I2CSTAT0 = 0xD0;
//清除PEND
I2CCON0 &= ~(1<<4);
//等待ACK
delay_xz(100);
//********************/
}
再来就是读,看下24C04的随机读部分
需要分成两个部分第一个部分也是写,把数据地址写入之后,再做读取,
因此最后代码就成为了
//随机读
void i2c_24c04_read(unsigned char *buf,unsigned char dataAddr,unsigned int length)
{
//------------以下为第一阶段的写入数据地址-------------
int i =0;
unsigned char unusedData;
//设置为主设备发送模式
I2CSTAT0 |= (0x3<<6);
//写入从设备地址-I2CDS
I2CDS0 = I2C24C04_WRITE_ADDR;
I2CCON0 &= ~(1<<4);
//写0xF0 到I2CSTAT
I2CSTAT0 = 0xF0;
//等待ACK
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//发送数据地址到I2CDS
I2CDS0 = dataAddr;
I2CCON0 &= ~(1<<4);
//等待ACK
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//---------写过程到此为止-------------------
//---------接下来是第二阶段的读-------------
//设定为主机读模式
I2CSTAT0 &= ~(0x3<<6);
I2CSTAT0 |= (0x2<<6);
//写入从设备地址-I2CDS
I2CDS0 = I2C24C04_READ_ADDR;
I2CCON0 &= ~(1<<4);
//写0xB0 到 I2CSTAT
I2CSTAT0 = 0xB0;
//这边设备有个问题第一个字节的数据是无效的,因此需要丢弃
//等待中断--循环读取
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//定义了一个无效变量接收无用数据
unusedData = I2CDS0;
//清除pend --循环结束
I2CCON0 &= ~(1<<4);
for (i=0;i<length;i++)
{
//读取完数据所有需要的数据时,需要回覆NOACK
if (i==(length-1))
{
I2CCON0 &= ~(1<<7);
}
//等待中断--循环读取
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//从I2CDS读取数据
buf[i] = I2CDS0;
//清除pend --循环结束
I2CCON0 &= ~(1<<4);
}
//等待中断--循环读取
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//写入0x90到I2CSTAT
I2CSTAT0 = 0x90;
//清除中断
I2CCON0 &= ~(1<<4);
//为了后续操作应该要恢复ACK
I2CCON0 |= (1<<7);
//*************************/
}
最后完成实现代码
/***********************************************
* 档名:i2c.c
* 作者:yang@wapoop.com
* 日期:2016/10/13
* 描述:i2c 驱动程序读写板载AT24C04
************************************************/
//GPIO寄存器--GPD1共用
#define GPD1CON (*((volatile unsigned int *)0xE02000C0))
#define GPD1PUD (*((volatile unsigned int *)0xE02000C8))
//I2C相关寄存器
#define I2CCON0 (*((volatile unsigned char *)0xE1800000))
#define I2CSTAT0 (*((volatile unsigned char *)0xE1800004))
#define I2CDS0 (*((volatile unsigned char *)0xE180000C))
//仅有在开发板做为Slave情况下需要设定I2CADD寄存器
#define I2CADD0 (*((volatile unsigned char *)0xE1800008))
#define I2CLC0 (*((volatile unsigned char *)0xE1800010))
#define I2C24C04_READ_ADDR 0xA1
#define I2C24C04_WRITE_ADDR 0xA0
static void delay_xz(int i)
{
int j = 0;
while (i--)
{
for (j=0;j<100;j++)
{
;
}
}
}
void i2c_24c04_write_byte(unsigned char dataAddr,unsigned char data)
{
///************************
//设置为发送模式
I2CSTAT0 |= (0x3<<6);
//写入从设备地址-I2CDS
I2CDS0 = I2C24C04_WRITE_ADDR;
I2CCON0 &= ~(1<<4);
//写0xF0 到I2CSTAT
I2CSTAT0 = 0xF0;
//等待ACK
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//发送数据地址到I2CDS
I2CDS0 = dataAddr;
I2CCON0 &= ~(1<<4);
//等待ACK
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//写入数据
I2CDS0 = data;
I2CCON0 &= ~(1<<4);
//等待ACK
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//写入0xD0到I2CSTAT
I2CSTAT0 = 0xD0;
//清除PEND
I2CCON0 &= ~(1<<4);
//等待ACK
delay_xz(100);
//********************/
}
//随机读
void i2c_24c04_read(unsigned char *buf,unsigned char dataAddr,unsigned int length)
{
//------------以下为第一阶段的写入数据地址-------------
int i =0;
unsigned char unusedData;
//设置为主设备发送模式
I2CSTAT0 |= (0x3<<6);
//写入从设备地址-I2CDS
I2CDS0 = I2C24C04_WRITE_ADDR;
I2CCON0 &= ~(1<<4);
//写0xF0 到I2CSTAT
I2CSTAT0 = 0xF0;
//等待ACK
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//发送数据地址到I2CDS
I2CDS0 = dataAddr;
I2CCON0 &= ~(1<<4);
//等待ACK
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//---------写过程到此为止-------------------
//---------接下来是第二阶段的读-------------
//设定为主机读模式
I2CSTAT0 &= ~(0x3<<6);
I2CSTAT0 |= (0x2<<6);
//写入从设备地址-I2CDS
I2CDS0 = I2C24C04_READ_ADDR;
I2CCON0 &= ~(1<<4);
//写0xB0 到 I2CSTAT
I2CSTAT0 = 0xB0;
//这边设备有个问题第一个字节的数据是无效的,因此需要丢弃
//等待中断--循环读取
while((I2CCON0 & 0x10) ==0)
delay_xz(100);
//定义了一个无效变量接收无用数据
unusedData = I2CDS0;
//清除pend --循环结束
I2CCON0 &= ~(1<<4);
for (i=0;i
<<0);
GPD1CON |= (0x22 <<0);
//设定引脚上拉,但是原理图上已经加了上拉电阻,所以直接悬空就可以了
GPD1PUD &= ~(0xF);
//2.设置I2CCON寄存器
//2.1使能中断
//清除中断标示
I2CCON0 &= ~(1<<4);
//启用中断
I2CCON0 |= (1<<5);
//2.2设定SCL时钟-PCLK=96M/(16 * (11+1)) 大约在500KHz
//I2CCON0 &= ~(1<<6);
//I2CCON0 |= (15<<0);
//改用512分频 96M/512 = 187.5KHz 就正常了
I2CCON0 |= (1<<6); //设置成512分频
//2.3允许返回ack
I2CCON0 |= (1<<7);
//3.设定I2CSTAT寄存器使能串行输出
//3.1设定工作模式为写
I2CSTAT0 |= (1<<4);
//************************************/
}
void i2c_test()
{
int i=0;
unsigned char sbuf[10]={0x41,0x32,0x43,0x34,0x45,0x36,0x47,0x38,0x49,0x3A};
unsigned char dbuf[256]={0};
i2c_init();
///********因为读写有次数限制,启动暂且就把写注释但是确实好使-开始******************
printf("start write! \r\n");
for (i=0;i<10;i++)
{
i2c_24c04_write_byte(0x20 + i,sbuf[i]);
}
//********因为读写有次数限制,启动暂且就把写注释但是确实好使-结束******************/
delay_xz(1000);
printf("i2c reading, plese wait!\n\r");
i2c_24c04_read(dbuf,0,256);
printf("dbuf after I2C read:\r\n");
for(i =0; i<256;i++)
{
if(i%16==0)
printf("\r\n");
printf("%0.2x\t",dbuf[i]);
}
}