打印
[STM32F4]

基于STM32F407(模拟&硬件)I2C通讯

[复制链接]
1385|35
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

核心板:STM32F407

实验目的:通过I2C来读写EEPROM

目录


EEPROM读写相关数据的协议


I2C基本协议层


I2C基本读写

I2C起始信号与终止信


I2C数有效性


I2C响应 与非响应


模拟I2C代码


模拟I2C代码相关定义


延时代码


起始信号代码


停止信号代码


发送一个字节代码


接收一个字节代码


等待从机应答代码


主机应答代码


主机非应答代码


等待EEPROM写入完成代码


引脚初始化代码


写一页数据代码


写大量数据代码


读大量数据代码


硬件I2C代码


硬件主发送、接收时序图


硬件I2C代码相关定义


I2C相关配置代码


等待EEPROM写入完成代码


写入一个字节代码


读一个字节代码


写一页数据代码


写大量数据代码


读大量数据代码

EEPROM读写相关数据的协议



使用特权

评论回复
沙发
yellow555|  楼主 | 2023-5-26 22:33 | 只看该作者
I2C基本协议层
I2C基本读写


使用特权

评论回复
板凳
yellow555|  楼主 | 2023-5-26 22:33 | 只看该作者
I2C起始信号与终止信号

使用特权

评论回复
地板
yellow555|  楼主 | 2023-5-26 22:33 | 只看该作者
I2C数有效性

使用特权

评论回复
5
yellow555|  楼主 | 2023-5-26 22:34 | 只看该作者
I2C响应 与非响应

使用特权

评论回复
6
yellow555|  楼主 | 2023-5-26 22:35 | 只看该作者
模拟I2C代码
模拟I2C代码相关定义
#define EEPROM_I2C_Write              0xA0        //EEPROM的设备地址(1010 000) + 0(写)
#define EEPROM_I2C_Read               0xA1        //EEPROM的设备地址(1010 000) + 1(读)
#define EEPROM_PAGE_SIZE                           8

/* I2C_SCL——PB8 */       
#define I2C_SCL_PIN                                                                  GPIO_Pin_8
#define I2C_SCL_GPIO_PORT                                                        GPIOB
#define I2C_SCL_GPIO_CLK                                                        RCC_AHB1Periph_GPIOB


/* I2C_SDA——PB9 */
#define I2C_SDA_PIN                                                                  GPIO_Pin_9
#define I2C_SDA_GPIO_PORT                                                        GPIOB
#define I2C_SDA_GPIO_CLK                                                        RCC_AHB1Periph_GPIOB


#define I2C_SCL_1()  GPIO_SetBits(I2C_SCL_GPIO_PORT, I2C_SCL_PIN)                /* SCL = 1 */
#define I2C_SCL_0()  GPIO_ResetBits(I2C_SCL_GPIO_PORT, I2C_SCL_PIN)                /* SCL = 0 */

#define I2C_SDA_1()  GPIO_SetBits(I2C_SDA_GPIO_PORT, I2C_SDA_PIN)                /* SDA = 1 */
#define I2C_SDA_0()  GPIO_ResetBits(I2C_SDA_GPIO_PORT, I2C_SDA_PIN)                /* SDA = 0 */

#define I2C_SDA_READ()  GPIO_ReadInputDataBit(I2C_SDA_GPIO_PORT, I2C_SDA_PIN)        /* 读SDA口线状态 */


void I2C_Buff_Read(uint8_t ADDR , uint8_t *Data,uint16_t size);
void I2C_Buff_Write(uint8_t ADDR , uint8_t *Data,uint16_t size);

void I2C_GPIO_Config(void);

使用特权

评论回复
7
yellow555|  楼主 | 2023-5-26 22:35 | 只看该作者
延时代码
static void I2C_Delay(void)
{
        uint8_t i;

        /* 
                可用逻辑分析仪测量I2C通讯时的频率
    工作条件:CPU主频168MHz ,MDK编译环境,1级优化
  
                经测试,循环次数为20~250时都能通讯正常
        */
        for (i = 0; i < 40; i++);
}

使用特权

评论回复
8
yellow555|  楼主 | 2023-5-26 22:35 | 只看该作者
起始信号代码
void I2C_Start(void)
{
        /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
        I2C_SDA_1();
        I2C_SCL_1();
        I2C_Delay();
        I2C_SDA_0();
        I2C_Delay();
        I2C_SCL_0();//钳住I2C总线(防止数据错误)
        I2C_Delay();
}

使用特权

评论回复
9
yellow555|  楼主 | 2023-5-26 22:35 | 只看该作者
       注意:最后一定要将SCL拉低,否则数据错误。(因为SCL为高电平SDA数有效,则发送从机设备地址时必然出错)

使用特权

评论回复
10
yellow555|  楼主 | 2023-5-26 22:35 | 只看该作者
停止信号代码
void I2C_Stop(void)
{
        /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
        I2C_SDA_0();
        I2C_SCL_1();
        I2C_Delay();
        I2C_SDA_1();
}

使用特权

评论回复
11
yellow555|  楼主 | 2023-5-26 22:36 | 只看该作者
发送一个字节代码
void I2C_SendByte(uint8_t data)
{
        uint8_t i;

        /* I2C先发送字节的高位bit7(SCL为高电平时SDA电平有效) */
        for (i = 0; i < 8; i++)
        {               
                if (data & 0x80)
                {
                        I2C_SDA_1();
                }
                else
                {
                        I2C_SDA_0();
                }
               
                I2C_Delay();
                I2C_SCL_1();
                I2C_Delay();
                I2C_SCL_0();
               
                if (i == 7)
                {
                         I2C_SDA_1(); // 释放总线,交给从机来应答
                }
                data <<= 1;        /* 左移一个bit */
                I2C_Delay();
        }
}

使用特权

评论回复
12
yellow555|  楼主 | 2023-5-26 22:36 | 只看该作者
      注意:发送完最后一次时主机要释放对SDA控制权(将SDA拉高),交给从机来发送应答与非应答信号。

使用特权

评论回复
13
yellow555|  楼主 | 2023-5-26 22:36 | 只看该作者
接收一个字节代码
uint8_t I2C_ReadByte(void)
{
        uint8_t i;
        uint8_t value = 0;

        /* EEPROM先发高位 */

        for (i = 0; i < 8; i++)
        {
                value <<= 1;
                I2C_SCL_1();
                I2C_Delay();
                if (I2C_SDA_READ())//如果发来的数据为1
                {
                        value++;
                }
                I2C_SCL_0();//切换电平准备接收下一个字节
                I2C_Delay();
        }
        return value;
}

使用特权

评论回复
14
yellow555|  楼主 | 2023-5-27 00:01 | 只看该作者
等待从机应答代码
uint8_t I2C_WaitAck(void)
{
        uint8_t re;

        I2C_SDA_1();        /* CPU释放SDA总线,把控制权交给从机 */
        I2C_Delay();
        I2C_SCL_1();        /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
        I2C_Delay();
        if (I2C_SDA_READ())        /* CPU读取SDA口线状态 */
        {
                re = 1;
        }
        else//应答
        {
                re = 0;
        }
        I2C_SCL_0();//拉低,为了下次数据的正确
        I2C_Delay();
        return re;
}

使用特权

评论回复
15
yellow555|  楼主 | 2023-5-27 00:02 | 只看该作者
   注意:最后一定要将SCL拉低,否则数据错误。

使用特权

评论回复
16
yellow555|  楼主 | 2023-5-27 00:02 | 只看该作者
主机应答代码
void I2C_Ack(void)
{
        /* SCL为高电平,SDA为低电平为应答 */
        I2C_SDA_0();        /* CPU驱动SDA = 0 */
        I2C_Delay();
        I2C_SCL_1();        /* CPU产生1个时钟 */
        I2C_Delay();
        I2C_SCL_0();
        I2C_Delay();
        I2C_SDA_1();        /* CPU释放SDA总线 *///不释放总线会导致从机发来的数据均为0
}
   注意:要释放总线 (将SDA拉高)。

使用特权

评论回复
17
yellow555|  楼主 | 2023-5-27 00:02 | 只看该作者
主机非应答代码
void I2C_NAck(void)
{
        /* SCL为高电平,SDA为高电平为非应答 */
        I2C_SDA_1();        /* CPU驱动SDA = 1 */
        I2C_Delay();
        I2C_SCL_1();        /* CPU产生1个时钟 */
        I2C_Delay();
        I2C_SCL_0();
        I2C_Delay();
}

使用特权

评论回复
18
yellow555|  楼主 | 2023-5-27 00:07 | 只看该作者
等待EEPROM写入完成代码
void Wait_For_Writed()
{
        uint32_t TIME_OUT = 0xFFFFF;
       
        while(TIME_OUT--)//一直呼叫从机,只有当从机写完后才会响应我们
        {
                I2C_Start();
               
                I2C_SendByte(EEPROM_I2C_Write);//发送写指令
               
                if(I2C_WaitAck() == 0)//等待从机响应
                {
                        I2C_Stop();
                        return ;
                }
        }
        I2C_Stop();
}


  注意:一定要等写完,否则会卡死或者数据错误。

使用特权

评论回复
19
yellow555|  楼主 | 2023-5-27 00:16 | 只看该作者
        下图摘自AT24C02手册

使用特权

评论回复
20
yellow555|  楼主 | 2023-5-27 00:16 | 只看该作者
引脚初始化代码
void I2C_GPIO_Config(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;

        RCC_AHB1PeriphClockCmd(I2C_SCL_GPIO_CLK | I2C_SDA_GPIO_CLK, ENABLE);        /* 打开GPIO时钟 */

        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;         
        GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;          /* 开漏输出 */
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
       
        GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN;
        GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
       
        GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;
        GPIO_Init(I2C_SDA_GPIO_PORT, &GPIO_InitStructure);

        /* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
        I2C_Stop();
}

使用特权

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

本版积分规则

37

主题

464

帖子

3

粉丝