发新帖我要提问
12
返回列表
打印
[STM32F4]

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

[复制链接]
楼主: yellow555
手机看帖
扫描二维码
随时随地手机跟帖
21
yellow555|  楼主 | 2023-5-27 00:16 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
写一页数据代码
void I2C_Page_Write(uint8_t ADDR,uint8_t *Data,uint16_t size) 
{
        I2C_Start();
       
        I2C_SendByte(EEPROM_I2C_Write);//发送写指令
       
        while(I2C_WaitAck());//等待从机响应
       
        I2C_SendByte(ADDR);//发送写在哪
       
        while(I2C_WaitAck());//等待从机响应
       
        while(size--)//发送数据
        {
                I2C_SendByte(*Data);
                while(I2C_WaitAck());//等待从机响应
                Data++;
        }
       
        I2C_Stop();
       
        Wait_For_Writed();//等待写入完成
}

使用特权

评论回复
22
yellow555|  楼主 | 2023-5-27 00:19 | 只看该作者
写一页数据代码
void I2C_Page_Write(uint8_t ADDR,uint8_t *Data,uint16_t size) 
{
        I2C_Start();
       
        I2C_SendByte(EEPROM_I2C_Write);//发送写指令
       
        while(I2C_WaitAck());//等待从机响应
       
        I2C_SendByte(ADDR);//发送写在哪
       
        while(I2C_WaitAck());//等待从机响应
       
        while(size--)//发送数据
        {
                I2C_SendByte(*Data);
                while(I2C_WaitAck());//等待从机响应
                Data++;
        }
       
        I2C_Stop();
       
        Wait_For_Writed();//等待写入完成
}
注意:一定要等待一会,要不然数卡死或者数据错误。

使用特权

评论回复
23
yellow555|  楼主 | 2023-5-27 00:20 | 只看该作者
写大量数据代码
void I2C_Buff_Write(uint8_t ADDR , uint8_t *Data,uint16_t size)
{
        uint16_t num_of_page = size / EEPROM_PAGE_SIZE;//算出来有几页
        uint16_t single_byte = size % EEPROM_PAGE_SIZE;//算出来有几个单个的字节数
        uint16_t single_addr = ADDR % EEPROM_PAGE_SIZE;//算出来这一页有几个位置
       
        if(single_addr == 0)//如果地址对齐
        {
                while(num_of_page--)
                {
                        I2C_Page_Write(ADDR,Data,EEPROM_PAGE_SIZE) ;
                       
                        ADDR += EEPROM_PAGE_SIZE;
                        Data += EEPROM_PAGE_SIZE;
                }
                I2C_Page_Write(ADDR,Data,single_byte) ;
        }
        else
        {
                /* 第一次写入的数据字节数(为了让地址对齐) */
                uint16_t first_write_size = EEPROM_PAGE_SIZE - single_addr;//算出来这一页还差几个位置不全一页
               
                I2C_Page_Write(ADDR,Data,first_write_size);//补全
               
                uint16_t surplus = size - first_write_size;//剩余的数据字节数
                Data += first_write_size;
                ADDR += first_write_size;
               
                num_of_page = surplus / EEPROM_PAGE_SIZE;//算出来有几页
                single_byte = surplus % EEPROM_PAGE_SIZE;//算出来有几个单个的字节数
               
                while(num_of_page--)
                {
                        I2C_Page_Write(ADDR,Data,EEPROM_PAGE_SIZE) ;
                       
                        ADDR += EEPROM_PAGE_SIZE;
                        Data += EEPROM_PAGE_SIZE;
                }
                I2C_Page_Write(ADDR,Data,single_byte) ;
        }
}

使用特权

评论回复
24
yellow555|  楼主 | 2023-5-27 00:20 | 只看该作者
读大量数据代码
void I2C_Buff_Read(uint8_t ADDR , uint8_t *Data,uint16_t size)
{
        I2C_Start();
       
        I2C_SendByte(EEPROM_I2C_Write);//发送写指令
       
        while(I2C_WaitAck());//等待从机响应
       
        I2C_SendByte(ADDR);//发送写在哪
       
        while(I2C_WaitAck());//等待从机响应
       
        I2C_Start();
       
        I2C_SendByte(EEPROM_I2C_Read);//发送读指令
       
        while(I2C_WaitAck());//等待从机响应
       
        while(size--)
        {
                *Data = I2C_ReadByte();//读取数据
               
                if(size == 0)//接收了最后一个数据主机发送非应答
                        I2C_NAck();
                else
                        I2C_Ack();
                Data++;
        }
       
        I2C_Stop();
}

使用特权

评论回复
25
yellow555|  楼主 | 2023-5-27 00:20 | 只看该作者
硬件I2C代码
硬件主发送、接收时序图
        (摘自stm32f407中文手册)

使用特权

评论回复
26
yellow555|  楼主 | 2023-5-27 00:21 | 只看该作者
硬件I2C代码相关定义
#define EEPROM_I2C_ADDR                        0xA0       
#define EEPROM_PAGE_SIZE                                 8

#define I2C                                                                I2C1
#define I2C_CLK                                                 RCC_APB1Periph_I2C1
#define I2C_Speed                                                 400000
#define I2C_OWN_ADDR                                        0x78                        //地址只需要不同于总线上的其他设备地址即可

/* I2C_SCL——PB8 */       
#define I2C_SCL_PIN                                          GPIO_Pin_8
#define I2C_SCL_GPIO_PORT                                GPIOB
#define I2C_SCL_GPIO_CLK                                RCC_AHB1Periph_GPIOB
#define I2C_SCL_SOURCE                                        GPIO_PinSource8
#define I2C_SCL_AF                                                GPIO_AF_I2C1

/* 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_SDA_SOURCE                                        GPIO_PinSource9
#define I2C_SDA_AF                                                GPIO_AF_I2C1

void I2C_Config(void);

void I2C_Byte_Write(uint8_t ADDR , uint8_t Data);//向EEPROM中ADDR地址写入一个字节的数据
void I2C_Random_Read(uint8_t ADDR , uint8_t *Data);//随机读取EEPROM中ADDR地址的数据(一个字节)

void I2C_Page_Write(uint8_t ADDR,uint8_t *Data,uint16_t size) ;//写入一页的数据


void I2C_Buffer_Write(uint8_t ADDR , uint8_t *Data,uint16_t size);//批量写入数据
void I2C_Buffer_Read(uint8_t ADDR , uint8_t *Data,uint16_t size);//批量读取数据

使用特权

评论回复
27
yellow555|  楼主 | 2023-5-27 00:21 | 只看该作者
I2C相关配置代码
void I2C_Config(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        I2C_InitTypeDef I2C_InitStructure;
       
        /* 使能 I2C 引脚的 GPIO 时钟 */
        RCC_AHB1PeriphClockCmd(I2C_SCL_GPIO_CLK | I2C_SDA_GPIO_CLK,ENABLE);
        /* 使能 I2C 时钟 */
        RCC_APB1PeriphClockCmd(I2C_CLK,ENABLE);
       
        /* 将对应的IO口连接到外设,开始启动复用功能 */
        GPIO_PinAFConfig(I2C_SCL_GPIO_PORT,I2C_SCL_SOURCE,I2C_SCL_AF);
        GPIO_PinAFConfig(I2C_SDA_GPIO_PORT,I2C_SDA_SOURCE,I2C_SDA_AF);
       
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF ;//复用功能
        GPIO_InitStructure.GPIO_OType = GPIO_OType_OD ;//开漏输出
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;//上拉
        GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed ;//高速
       
        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_InitStructure.I2C_Ack = I2C_Ack_Enable ;
        I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit ;
        I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
        I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2 ;
        I2C_InitStructure.I2C_Mode = I2C_Mode_I2C ;//I2C 模式
        I2C_InitStructure.I2C_OwnAddress1 = I2C_OWN_ADDR;
       
        I2C_Init(I2C, &I2C_InitStructure);
       
        I2C_Cmd(I2C,ENABLE);
}

使用特权

评论回复
28
yellow555|  楼主 | 2023-5-27 00:21 | 只看该作者
等待EEPROM写入完成代码
/* 等待RRPROM内部写入完成 */
static void Wait_For_EEPROM(void)
{
        uint32_t check_count = 0xFFFFF;
        uint32_t count_wait  = 0xFFFF;
        while(check_count--)
        {
                        /* 产生一个起始信号 */
                I2C_GenerateSTART(I2C ,ENABLE);
               
                /* 等待EV5事件,直到成功 */
                while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
               
                /* 发送EEPROM设备的地址,设置为写方向 */
                I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter );
               
                /* 重置TIME_OUT */
                count_wait = 0xFFFF;
                /* 等待EV6事件,直到成功,说明EEPROM写完了,可以相应我们了 */
                while(count_wait --)//只有RRPROM写完了,我们寻址,他才会响应我们,否则是不会响应我们的
                {
                        /* 若检测到响应,说明内部写时序完成,跳出等待函数 */
                        if(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == SUCCESS )
                        {
                                I2C_GenerateSTOP(I2C, ENABLE );//产生一个停止信号
                                return ;
                        }
                }
        }
        I2

使用特权

评论回复
29
yellow555|  楼主 | 2023-5-27 00:21 | 只看该作者
写入一个字节代码
/* 向EEPROM中ADDR地址写入一个字节的数据 */
void I2C_Byte_Write(uint8_t ADDR , uint8_t Data)
{
        I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
       
        I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);//发送地址,为写方向

        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生

        I2C_SendData(I2C,  ADDR);//发送存放数据的地址

        /*(EV8事件是用来检测数据是否正在发送,发送完成会产生一个EV8_2事件,因此我们检测该事件)*/
        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
       
        I2C_SendData(I2C,  Data);//发送存放数据

        while(I2C_CheckEvent(I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生

        I2C_GenerateSTOP(I2C, ENABLE);

        Wait_For_EEPROM();//等待EEPROM写入完成
}

使用特权

评论回复
30
yellow555|  楼主 | 2023-5-27 00:22 | 只看该作者
读一个字节代码
/* 随机读取EEPROM中ADDR地址的数据,用Data返回 */
void I2C_Random_Read(uint8_t ADDR , uint8_t *Data)
{
        /* 第一个起始信号用于写入我们要读取数据的地址 */
        I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
       
        I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);//发送地址,为写方向

        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生
       
        I2C_SendData(I2C,  ADDR);//发送存放数据的地址

        /*(EV8事件是用来检测数据是否正在发送,发送完成会产生一个EV8_2事件,因此我们检测该事件)*/
        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
       
       
        /* 产生第二个信号,用于读取数据 */
        I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
       
        I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Receiver);//发送地址,为写方向
       
        I2C_AcknowledgeConfig(I2C, DISABLE);//非应答信号
       
        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生

        /* 先发非应答信号,然后在等待数据传送到DR寄存器,否则在停止前会接收到错误的数据 */
        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);//等待EV7事件产生
       
        *Data = I2C_ReceiveData(I2C);
       
        I2C_GenerateSTOP(I2C, ENABLE);
}

使用特权

评论回复
31
yellow555|  楼主 | 2023-5-27 00:22 | 只看该作者
   注意:在接收数据完成时要提前发送非应答数据 。

使用特权

评论回复
32
yellow555|  楼主 | 2023-5-27 00:22 | 只看该作者
写一页数据代码
/* 写入一页的数据,size不可大于8,否则数据错误 */
void I2C_Page_Write(uint8_t ADDR,uint8_t *Data,uint16_t size)
{
        I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
       
        I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);//发送地址,为写方向

        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生

        I2C_SendData(I2C,  ADDR);//发送存放数据的地址

        /*(EV8事件是用来检测数据是否正在发送,发送完成会产生一个EV8_2事件,因此我们检测该事件)*/
        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
       
        while(size--)
        {
                I2C_SendData(I2C, *Data);//发送存放数据的地址
               
                while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
               
                Data++;
        }
       
        I2C_GenerateSTOP(I2C, ENABLE);

        Wait_For_EEPROM();//等待EEPROM写入完成
}

使用特权

评论回复
33
yellow555|  楼主 | 2023-5-27 00:22 | 只看该作者
写大量数据代码
/* 批量写入数据 */
void I2C_Buffer_Write(uint8_t ADDR , uint8_t *Data,uint16_t size)
{
        uint16_t num_of_page = size / EEPROM_PAGE_SIZE;//算出来有几页
        uint16_t single_byte = size % EEPROM_PAGE_SIZE;//算出来有几个单个的字节数
        uint16_t single_addr = ADDR % EEPROM_PAGE_SIZE;//算出来这一页有几个位置
       
        if(single_addr == 0)//说明地址对齐
        {
                while(num_of_page--)
                {
                        I2C_Page_Write(ADDR,Data,EEPROM_PAGE_SIZE) ;
                       
                        ADDR += EEPROM_PAGE_SIZE;
                        Data += EEPROM_PAGE_SIZE;
                }
                I2C_Page_Write(ADDR,Data,single_byte) ;
        }
        else
        {
                /* 第一次写入的数据字节数(为了让地址对齐) */
                uint16_t first_write_size = EEPROM_PAGE_SIZE - single_addr;//算出来这一页还差几个位置不全一页
               
                I2C_Page_Write(ADDR,Data,first_write_size);//补全
               
                uint16_t surplus = size - first_write_size;//剩余的数据字节数
                Data += first_write_size;
                ADDR += first_write_size;
               
                num_of_page = surplus / EEPROM_PAGE_SIZE;//算出来有几页
                single_byte = surplus % EEPROM_PAGE_SIZE;//算出来有几个单个的字节数
               
                while(num_of_page--)
                {
                        I2C_Page_Write(ADDR,Data,EEPROM_PAGE_SIZE) ;
                       
                        ADDR += EEPROM_PAGE_SIZE;
                        Data += EEPROM_PAGE_SIZE;
                }
                I2C_Page_Write(ADDR,Data,single_byte) ;
        }
}

使用特权

评论回复
34
yellow555|  楼主 | 2023-5-27 00:22 | 只看该作者
读大量数据代码
/* 批量读取数据 */
void I2C_Buffer_Read(uint8_t ADDR , uint8_t *Data,uint16_t size)
{
        I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
       
        I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);//发送地址,为写方向

        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生
       
        I2C_SendData(I2C,  ADDR);//发送存放数据的地址

        /*(EV8事件是用来检测数据是否正在发送,发送完成会产生一个EV8_2事件,因此我们检测该事件)*/
        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
       
       
        /* 产生第二个信号,用于读取数据 */
        I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
       
        I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Receiver);//发送地址,为写方向
       
        while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生
       
        while(size--)
        {
                if(size == 0)
                        I2C_AcknowledgeConfig(I2C, DISABLE);//非应答信号
                else
                        I2C_AcknowledgeConfig(I2C, ENABLE);//应答信号
                /* 读取数据 */
                while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);//等待EV7事件产生
                       
                *Data++ = I2C_ReceiveData(I2C);
        }
       
        I2C_GenerateSTOP(I2C, ENABLE);
}

使用特权

评论回复
35
yellow555|  楼主 | 2023-5-27 00:23 | 只看该作者
主函数中(串口通讯请自行配置)
int main(void)
{
        uint8_t Tx_Buff[256];
        uint8_t Rx_Buff[256];
       
        USART_Config();
       
        I2C_Config();
       

        for(int i = 0;i<TEST_SIZE;i++)//给buff赋值
        {
                Tx_Buff[i] = i;
        }
       
        I2C_Buffer_Write(0x00 ,Tx_Buff,256);
        I2C_Buffer_Read(0x00 , Rx_Buff,256);
       
        for(int i = 0;i<TEST_SIZE;i++)//给buff赋值
        {
                printf("0x%02x ",Rx_Buff[i]);
        }

        printf("\n一切OK\n");
       


        while (1)
        {
        }
}

使用特权

评论回复
36
yellow555|  楼主 | 2023-5-27 00:23 | 只看该作者
实验结果

使用特权

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

本版积分规则