打印
[STM32F1]

stm32基础篇——IIC实验

[复制链接]
1574|25
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本次实验利用 STM32 的普通 IO 口模拟 IIC 时序,并实现和 24C02 之间的双向通信。将利用 STM32 普通 IO 口模拟 IIC 时序, 来实现24C02 的读写,并将结果通过串口 printf 输出 。 实验目标:
1. 学习 I2C 总线
2. 会操作 AT24C02

沙发
aizaixiyuanqian|  楼主 | 2017-12-16 12:10 | 只看该作者
I2C  简介
I2C 总线是 PHILIPS 公司推出的一种串行总线,具备多主机系统所需的包括总线仲裁和高低速器件同步功能的高性能串行总线。它只需要两跟双向的信号线,一根数据线 SDA,一个是时钟线 SCL。在 I2C 总线上面,每个器件都有自己相应的 I2C 地址,所以在两个器件之间进行通信的时候,都要首先呼叫你想要通信的器件地址,然后等待相应的从器件进行应答之后才开始通信。首先我们来看一下,一个 I2C 信号传输的一个过程,如下图:

可以看出在 I2C 上面一个完整信号的传输过程,一定要有一个始信号,还有一个结束信号,在每个字节传输结束的时候,从机还要提供个应答信号。一个完整的信号传输就是这样子。接下来我们来看一下,I2C 总线上面对起始信号、应答信号、结束信号、还有高低电平的协定是怎么样的呢?这里有两个要注意的要点:
1、在总线空闲的时候,SDA 和 SCL 都是高电平的。
2、在 SCL 为高电平期间,SDA 必须保持稳定。所以 SDA 改变状态最好在 SCL 为低电
平的时候改变,如果在高电平改变的话回被认为是一种有效信号

使用特权

评论回复
板凳
aizaixiyuanqian|  楼主 | 2017-12-16 12:12 | 只看该作者
起始信号
起始信号简介 SCL 线为高电平期间,SDA 线由高电平向低电平的变化表示起始信号,
信号时序如图:


使用特权

评论回复
地板
aizaixiyuanqian|  楼主 | 2017-12-16 12:12 | 只看该作者
IO 口模拟起始信号
//产生起始信号
void I2C_Start(void)
{
I2C_SDA_OUT();
I2C_SDA_H;
I2C_SCL_H;
delay_us(5);
I2C_SDA_L;
delay_us(6);
I2C_SCL_L;
}

使用特权

评论回复
5
643757107| | 2017-12-17 22:44 | 只看该作者
这种时序模拟必须学会的

使用特权

评论回复
6
aizaixiyuanqian|  楼主 | 2017-12-18 12:05 | 只看该作者
643757107 发表于 2017-12-17 22:44
这种时序模拟必须学会的

是的,很基础

使用特权

评论回复
7
aizaixiyuanqian|  楼主 | 2017-12-18 12:06 | 只看该作者
结束信号
结束信号简介 SCL 线为高电平期间,SDA 线由低电平向高电平的变化表示终止信号。信号时序如图:



使用特权

评论回复
8
aizaixiyuanqian|  楼主 | 2017-12-18 12:06 | 只看该作者
IO 口模拟结束信号
//产生停止信号
void I2C_Stop(void)
{
I2C_SDA_OUT();
I2C_SCL_L;
I2C_SDA_L;
I2C_SCL_H;
delay_us(6);
I2C_SDA_H;
delay_us(6);
}

使用特权

评论回复
9
aizaixiyuanqian|  楼主 | 2017-12-18 12:08 | 只看该作者
应答信号
应答,也叫响应。数据的传输必须要带应答。在响应的时钟脉冲期间(也就是 SCL 在高电平的时候) ,发送器释放 SDA 线(释放 SDA 意思就是将 SDA 拉为高电平,这里要注意的是,不能在 SCL 为高电平的时候讲 SDA 从低电平拉到高电平,可以在在 SCL 在低电平的时候将SDA 拉为高电平等待),然后等待应答,在应答时钟脉冲器件,接收器必须将 SDA 拉低,使它在这个时钟脉冲的高电平期间保持稳定的低电平。而一个字节传输完毕之后,接收器没有应答则表示接收完毕。还有一种情况是,当主机作为接收器的时候,接收完最后一个字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机“非应答”来实现的。

使用特权

评论回复
10
aizaixiyuanqian|  楼主 | 2017-12-18 12:09 | 只看该作者
IO 口模拟应答信号
//主机产生应答信号 ACK
void I2C_Ack(void)
{
I2C_SCL_L;
I2C_SDA_OUT();
I2C_SDA_L;
delay_us(2);
I2C_SCL_H;
delay_us(5);
I2C_SCL_L;
}

使用特权

评论回复
11
aizaixiyuanqian|  楼主 | 2017-12-18 12:10 | 只看该作者
//主机不产生应答信号 NACK
void I2C_NAck(void)
{
I2C_SCL_L;
I2C_SDA_OUT();
I2C_SDA_H;
delay_us(2);
I2C_SCL_H;
delay_us(5);
I2C_SCL_L;
}

使用特权

评论回复
12
aizaixiyuanqian|  楼主 | 2017-12-18 12:12 | 只看该作者
IO  口模拟发送一个字节数据
//I2C 发送一个字节
void I2C_Send_Byte(u8 txd)
{
u8 i=0;
I2C_SDA_OUT();
I2C_SCL_L;//拉低时钟开始数据传输
for(i=0;i<8;i++)
{
if((txd&0x80)>0) //0x80 1000 0000
I2C_SDA_H;
else
I2C_SDA_L;
txd<<=1;
I2C_SCL_H;
delay_us(2); //发送数据
I2C_SCL_L;
delay_us(2);
}
}

使用特权

评论回复
13
aizaixiyuanqian|  楼主 | 2017-12-18 12:13 | 只看该作者
IO  口模拟接收一个字节数据
//I2C 读取一个字节
u8 I2C_Read_Byte(u8 ack)
{
u8 i=0,receive=0;
I2C_SDA_IN();
for(i=0;i<8;i++)
{
I2C_SCL_L;
delay_us(2);
I2C_SCL_H;
receive<<=1;
if(GPIO_ReadInputDataBit(GPIO_I2C,I2C_SDA))
receive++;
delay_us(1);
}
if(ack==0)
I2C_NAck();
else
I2C_Ack();
return receive;
}

使用特权

评论回复
14
aizaixiyuanqian|  楼主 | 2017-12-18 12:15 | 只看该作者
24C02  简介
1. 24C02 可以提供 2K 位,也就是 256 个 8 位字节的 EEPROM 内存。也就是说它可以保存 256 个字节的数据。所以从这里我们可以了解到,256 个字节也就是有 256 个内存地址,也正好对应一个字节的。当我们向 24C02 读写数据的时候,地址正好跟一个字节一一对应。
2. 24C02 通过 I2C 总线接口进行操作。
3. 24C02 的写操作,可以一个地址一个地址的写,也可以一次写一页。所写的一页,在 24C02这里是 8 个字节,(在有些数据手册上面是 16 个字节,不过开发板上面使用 24C02 是一页 8 个字节,ARM 公司提供的官方例程里面 24C02 设定的也是一页 8 个字节。 )也就是当你写入的数据,在同一页的时候(注意是在同一页的时候) ,你可以只写入一次地址,每写入一个字节,地址自动加 1。
4. 24C02 的读操作,24C02 的读操作就可以连续读,不管连续读的数据是不是在同一页,每次读完一次数据之后,读取地址都会自动加1。

使用特权

评论回复
15
aizaixiyuanqian|  楼主 | 2017-12-18 12:21 | 只看该作者
24C02的原理图


使用特权

评论回复
16
aizaixiyuanqian|  楼主 | 2017-12-18 12:23 | 只看该作者
STM32  的硬件 I2C  简介
STM32 的硬件 I2C 做得很复杂,而且不好用。所以大部分人都不太选择使用 STM32 的硬件 I2C,很难调试。不过 AT24C02 有 ARM 官方的提供的例程,读写还是挺稳定的,我们的例程使用的也是参考 ARM 官方的例程来的。接下来我们看看一下通过库函数使用硬件 I2C 来操作 AT24C02。

使用特权

评论回复
17
aizaixiyuanqian|  楼主 | 2017-12-18 12:24 | 只看该作者
打开时钟使能
要打开 GPIOB 的时钟使能。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

使用特权

评论回复
18
aizaixiyuanqian|  楼主 | 2017-12-18 12:24 | 只看该作者
  配置 IO  的模式
GPIO_InitStructure.GPIO_Pin=I2C_SCL|I2C_SDA;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_Init(GPIOB,&GPIO_InitStructure);
管脚的端口定义如下:

#define I2C_SCL GPIO_Pin_10 //PB10
#define I2C_SDA GPIO_Pin_11 //PB11
#define GPIO_I2C GPIOB

使用特权

评论回复
19
aizaixiyuanqian|  楼主 | 2017-12-18 12:25 | 只看该作者
AT24C02 写 操作
写 AT24C02。一般步骤是:
1) 发送起始信号
2) 发送写器件地址
3) 等待应答
4) 发送要写入的 24C02 的地址
5) 等待应答
6) 发送要写入的数据
7) 等待应答
8) 发送数据结束发送结束信号

使用特权

评论回复
20
aizaixiyuanqian|  楼主 | 2017-12-18 12:26 | 只看该作者
具体代码如下:
void AT24Cxx_WriteOneByte(u16 addr,u8 dt)
{
I2C_Start();
if(EE_TYPE>AT24C16)
{
I2C_Send_Byte(0xA0);
I2C_Wait_Ack();
I2C_Send_Byte(addr>>8); //发送数据地址高位
}
else
{
I2C_Send_Byte(0xA0+((addr/256)<<1));//器件地址+数据地址
}
I2C_Wait_Ack();
I2C_Send_Byte(addr%256);//双字节是数据地址低位
//单字节是数据地址低位
I2C_Wait_Ack();
I2C_Send_Byte(dt);
I2C_Wait_Ack();
I2C_Stop();
delay_ms(10);
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

62

主题

1353

帖子

6

粉丝