开发环境:ubuntu arm-linux-gcc4.4.1
开发板: GEC210开发板
原理图
i2c芯片:FM24CL04
与cpu的连接,GPD1的0,1号引脚。0号引脚对应SDA功能,1号引脚对应SCL引脚
实现:
io方式模拟i2c通信,没有使用i2c控制器
需要使用io引脚输出高低电平模拟i2c信号
或者需要改变为输入模式服务数据
//宏定义
#define GPD1DAT (*(volatile unsigned long*)0xe02000c4)
#define GPD1CON (*(volatile unsigned long*)0xe02000c0)
#define UTXH0 (*(volatile unsigned char*)0xe2900020)
#define UTRSTAT0 (*(volatile unsigned long*)0xe2900010)
//函数声明
void sda_set(unsigned char x);
void scl_set(unsigned char x);
void delay(int n);
void i2c_start(void);
void i2c_stop(void);
void sda_input(void);
void sda_output(void);
void scl_output(void);
unsigned char read_sda(void);
unsigned char wait_ack(void);
void send_ack(unsigned char ack);
unsigned char data_read(unsigned char ack_flag);
unsigned char data_write(unsigned char buf);
unsigned char data_write_buf(unsigned short addr,unsigned char*buf,int len);
unsigned char rand_read_buf(unsigned short addr,unsigned char*buf,int len);
void print_hex(int n);
void print_string(char *);
//测试程序
void i2c_main(void)
{
unsigned char buf[32];
int i;
sda_output();
scl_output();
print_string("2017nr");
for(i=0;i<32;i++)
{
buf[i] = i + 0x20;
}
data_write_buf(0x0,buf,30); //向0地址写入30个值
rand_read_buf(10,buf,20); //从10地址读取20个值
for(i=0;i<20;i++)
{
print_hex(buf[i]);
}
print_string("i2c io test overnr");
return;
}
//SDA输出高或低电平
//参数 x非0时输出高电平,x为0时输出低电平
void sda_set(unsigned char x)
{
if(x)
{
GPD1DAT |= 1; //输出高电平
}
else
{
GPD1DAT &= ~1; //输出低电平
}
}
//SCL信号输出高电平或低电平
//参数 x非0时输出高电平,x为0时输出低电平
void scl_set(unsigned char x)
{
if(x)
{
GPD1DAT |= 1<<1; //输出高电平
}
else
{
GPD1DAT &= ~(1<<1); //输出低电平
}
}
//延时函数
void delay(int n)
{
n = 100*n;
while(n--); //直到n减为0
}
//i2c的开始时序
//在SCL为高电平时,SDA由高变为低电平
//尽量保证所有的函数进入时SCL为0,函数返回时SCL也为低
void i2c_start(void)
{
scl_set(0); //SCL = 0
sda_set(1); //SDA = 1
scl_set(1); //SCL = 1
delay(1);
sda_set(0); //SDA = 0
delay(1);
scl_set(0); //SCL = 0
}
//i2c的停止时序
//在SCL为高电平时,SDA由低变为高电平
//最后进入空闲状态,SCL和SDA都保持为高电平
void i2c_stop(void)
{
scl_set(0); //SCL = 0
sda_set(0); //SDA = 0;
scl_set(1); //SCL = 1;
delay(1);
sda_set(1); //SDA = 1
scl_set(1); //SCL = 1
}
//SDA切换为输入模式
//在接收数据或接收应答的时候
//SDA为GPD1 io引脚的0号引脚
void sda_input(void)
{
GPD1CON &= ~0xf; //输入模式
}
//SDA切换为输入模式
//在发送数据的时候
//SDA为GPD1 io引脚的0号引脚
void sda_output(void)
{
GPD1CON &= ~0xf;
GPD1CON |= 1; //输出模式
}
//SCL作为主机模式的时候,只需要设置为输出模式
//SCL为GPD1 io引脚的1号引脚
void scl_output(void)
{
GPD1CON &= ~0xf0;
GPD1CON |= 1<<4; //输出模式
}
//在输入模式时,读取SDA引脚的信号
//只需要最低位,因为最低位才是SDA引脚的对应信号
unsigned char read_sda(void)
{
return GPD1DAT & 1;
}
//主机读取从机发回的应答信号
//
unsigned char wait_ack(void)
{
unsigned char tmp;
scl_set(0); //SCL = 0;
sda_set(1); //SDA = 1 ,释放SDA总线
sda_input(); //SDA切换为输入模式
delay(1);
scl_set(1); //SCL = 1;
delay(1);
tmp = read_sda(); //读取SDA信号
scl_set(0);
delay(1);
sda_output(); //SDA切换为输出模式
return tmp;
}
//主机发送应答信号
//参数ack为0时表示需要应答,为1时不应答
//ack是一个时序过程,就是数据发完的第9位,不管主机要不要发送应答
//这个第9位的时序都要出现在8个数据位之后
void send_ack(unsigned char ack)
{
scl_set(0); //SCL = 0;
delay(1);
sda_set(ack & 1); //SDA = 0;
delay(1);
scl_set(1);
delay(3);
scl_set(0);
}
//写8bits数据时序
unsigned char data_write(unsigned char buf)
{
unsigned char i;
scl_set(0); //SCL = 0;
delay(1);
for(i=0;i<8;i++)
{
sda_set((buf>>(7-i)) & 1);
delay(1);
scl_set(1); //SCL = 1;
delay(5);
scl_set(0); //SCL = 0;
delay(1);
}
return wait_ack(); //应答时序
}
//当前地址读操作
//fm24cl04读取数据的操作,当前地址的话,重新上电后的当前地址为0
//所以调试的时候注意以下,尽量断电重启
unsigned char data_read(unsigned char ack_flag)
{
unsigned char i;
unsigned char buf = 0;
scl_set(0); //SCL = 0;
sda_input(); //sda输入模式
delay(1);
for(i=0;i<8;i++)
{
buf<<=1;
scl_set(1); //SCL = 1;
delay(1);
buf |= read_sda();
// delay(1);
scl_set(0); //SCL = 0;
delay(1);
}
sda_output(); //sda输出模式
send_ack(ack_flag); //发送应答
return buf; //返回收到的数据
}
void i2c_read_buf(unsigned char page,unsigned char*buf,int len)
{
int i;
i2c_start(); //起始信号
while(data_write(0xa1 | (page<<1)) != 0); //设备地址,读操作,页选择
for(i=0;i buf[i] = data_read(i==len-1); //只有i=9才传入1,其他情况为0 } i2c_stop(); return; } //写操作 //fm24cl04写数据的操作 unsigned char data_write_buf(unsigned short addr,unsigned char*buf,int len) { int i; unsigned char page = addr>>8; //第9位为fm24cl04的页选择 i2c_start(); //起始信号 while(data_write(0xa0 | (page << 1)) != 0); //设备地址,写操作 data_write(addr & 0xff); //发送空间地址 if(len == 0) //为随机读创造时序,参看随机读函数 { return; } for(i=0;i data_write(buf[i]); //写入数据 } i2c_stop(); } //随机读函数操作,与当前地址读的差异 //fm24cl04写数据的操作 unsigned char rand_read_buf(unsigned short addr,unsigned char*buf,int len) { data_write_buf(addr,buf,0); i2c_read_buf(addr>>8,buf,len); } /*******************************************************************************************/ /* 分割线 以下为串口打印函数,不完整。但是使用uboot的换是可以使用的。 暂时不做详细介绍 */ void uart0_send_byte(unsigned char buf) { while((UTRSTAT0 & (1<<2)) == 0); //靠靠靠靠靠靠靠靠 UTXH0 = buf; } void print_string(char*str) { while(*str) { uart0_send_byte(*str++); } } void print_hex(int n) { unsigned char arr[8]; unsigned char i; unsigned char tmp; for(i=0;i<8;i++) { tmp = n & 0xf; //靠靠靠靠4靠靠靠?6靠靠靠靠靠靠靠 if(tmp > 9) { arr[i] = tmp + 'a' - 10; //靠靠靠ASCII? } else { arr[i] = tmp + '0'; } n >>= 4; } print_string("0x"); for(i=0;i<8;i++) { uart0_send_byte(arr[7-i]); } print_string("nr"); }
上一篇:GEC210 LED 裸机编程 原理介绍
下一篇:ARM-CP15寄存器组介绍(开启I/D catch,MMU都需要此寄存器)
推荐阅读最新更新时间:2024-11-02 11:20
设计资源 培训 开发板 精华推荐
- NBIoT定位器-基于BC20模组
- Numato Opsis:基于FPGA的开放视频平台
- 使用外部基准的 AD5314 10 位 DAC 的典型应用
- LT8495IFE 450kHz、5V 输出 SEPIC 转换器的典型应用电路
- AM3GW-4812DZ ±12V 3 瓦 DC-DC 转换器的典型应用
- 使用 Analog Devices 的 LT1585CM-3.45 的参考设计
- 【实物已验证】AD584电压基准 V1.1
- 1200W开关电源设计原理图及PCB设计资料
- AD8602DRZ-REEL 高端运算放大器电流监视器的典型应用
- ADP2384 20V、4A、同步降压直流至直流稳压器的典型应用电路具有编程开关频率为500 kHz、VIN = 12 V、VOUT = 5 V、IOUT = 4 A、fSW = 500 kHz