【GD32】从0开始学GD32单片机(8)—— I2C外设详解+主机从机发送和接收例程

简介

I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
I2C总线是一个真正的多主机总线,如果两个或多个主机同时初始化数据传输,可以通过冲突检测和仲裁防止数据破坏,每个连接到总线上的器件都有唯一的地址,任何器件既可以作为主机也可以作为从机,但同一时刻只允许有一个主机。数据传输和地址设定由软件设定,非常灵活。总线上的器件增加和删除不影响其他器件正常工作。
I2C有3种总线速率——标速(100kHz)、快速(400kHz)和高速(3.4MHz),但GD32F103C8T6只支持标速和快速两种速率

在这里插入图片描述

通信流程

开始信号和停止信号

I2C的通信由一个起始信号开始,传输结束后由一个停止信号结束。

在这里插入图片描述

开始信号为SDA线由高电平拉低,接着SCL线开始输出同步时钟信号;传输结束后SCL线停止同步时钟输出,接着SDA线也由低拉高,发出结束信号。

数据有效性

I2C是一个同步传输协议,因此数据的更新需根据同步时钟线来决定。

在这里插入图片描述

I2C规定,时钟信号的高电平期间SDA线上的数据必须稳定。只有在时钟信号SCL变低的时候数据线SDA的电平才能跳变

仲裁

上面说过,I2C是一个多主机总线协议,因此当多个主机同时申请总线的控制权时,则需要对其进行仲裁,以决定哪一个主机获得总线的控制权。
I2C的仲裁比较特别,跟之前介绍的DMA的仲裁不同,DMA的仲裁是通过检查优先级的方式来进行的,而I2C的仲裁是通过比较实际输出电平的方式进行的。
在每一位数据的发送期间,当SCL为高时,每个主机都检查SDA电平是否和自己发送的相同。理论上讲,如果两个主机所传输的内容完全相同,那么他们能够成功传输而不出现错误。如果一个主机发送高电平但检测到SDA电平为低,则认为自己仲裁失败并关闭自己的SDA输出驱动,而另一个主机则继续完成自己的传输。

从设备地址与确认应答

I2C总线上可以连接多个从设备,每个从设备都共享一条SDA和SCL线。

在这里插入图片描述

所以当主机想要给总线上的某一个从设备通信就需要有一个机制来寻找对应的从设备,这个机制就是从设备地址。
每个从设备都有一个自己的地址可以是7位的,也可以是10位的,地址的值的范围具体由厂商来决定。
主机在向总线发送开始信号并成功获得总线控制权后,将会向总线发送对应从机的地址,这个地址所有的从机都会接收,然后从机将收到的地址与自己的地址比较,若地址一致则该从机将会向总线发送ACK信号,也就是确认应答,表示可以开始通信;而其他的从机将会忽略总线上的信号,直到下一个开始信号来临。
主机与从机每传输一组数据,从机接收后都必须发送ACK信号,表示接收成功。

在这里插入图片描述

软件控制流程

I2C具有4种运行状态——主机发送方、主机接收方、从机发送方、从机接收方。对于这4种不同的运行状态都有不同的软件控制流程。
这一部分所有的流程图都是基于10位地址I2C通信的,因此如果各位使用的是7位地址的I2C通信,那么把地址头发送的那一部分忽略即可。

主机发送方

简要流程:

  1. 使能I2C外设时钟,配置时钟相关寄存器来确保正确的I2C时序;
  2. 软件将START位置1,在I2C总线上产生一个START起始位,SBSEND置1,进入主机模式;
  3. 写一个7位地址位或带有地址头的10位地址位到数据寄存器,SBSEND置0,若是10位地址,那么先写入高地址ADD10SEND置1,发送后再写入低地址ADD10SEND置0;
  4. I2C开始发送地址或者地址头到总线上,ADDSEND置1,软件将ADDSEND置0;
  5. I2C进入数据发送状态,软件写第一个字节数据到数据寄存器,此时,写入数据寄存器的字节被立即移入内部移位寄存器,TBE置1;
  6. 移位寄存器非空,I2C开始发送数据到总线;
  7. 在第一个字节的发送过程中,软件可以写第二个字节到数据寄存器,TBE置0;
  8. 任何时候TBE被置1,软件都可以向数据寄存器写入一个字节,只要还有数据待发送。
  9. 在倒数第二个字节发送过程中,软件写入最后一个字节数据到数据寄存器来清除TBE标志
    位;
  10. 最后一个字节发送结束后,I2C主机将BTC位置1,因为移位寄存器和数据寄存器此时都为空;
  11. 软件将STOP位置1来发送一个STOP结束位,此后TBE和BTC状态位都将被置0。

说明:
SBSEND全称Start Bit Send,“起始位发送”标志位。
ADDSEND全称Address Send,“地址发送”标志位,同样ADD10SEND就是“10位地址发送”标志位
TBE全称Transfer Buffer Empty,“发送缓冲区空”标志位。
BTC全称Byte Transfer Complete,“字节传输完成”标志位。

在这里插入图片描述

主机接收方

方案1(需软件对中断快速响应)

简要流程:

  1. 使能I2C外设时钟,配置时钟相关寄存器来确保正确的I2C时序,等待START起始位;
  2. 软件将START位置1,从而在I2C在总线上产生一个START起始位,SBSEND置1,进入主机模式;
  3. 写一个7位地址位或带有地址头的10位地址位到数据寄存器,SBSEND置0,若是10位地址,那么先写入高地址,发送后ADD10SEND置1,再写入低地址ADD10SEND置0;
  4. 7位或10位的地址位发送出去之后,ADDSEND置1,软件应该将ADDSEND置0,如果地址是10位格式,软件应该再次将START位置1来重新产生一个START(Sr);在START产生后,SBSEND位置1,软件将SBSEND置0,然后地址头被发到I2C总线,ADDSEND再次被置1,软件将ADDSEND置0;
  5. 接收到第一个字节,RBNE位置1,软件可以从数据寄存器读取第一个字节,RBNE位置0。
  6. 此后任何时候RBNE被置1,软件就可以从数据寄存器读取一个字节;
  7. 接收完倒数第二个字节(N-1)数据之后,软件应该立即将ACKEN位置0,并将STOP位置1,
    这一过程需要在最后一个字节接收完毕之前完成,以确保NACK发送给最后一个字节;
  8. 最后一个字节接收完毕后,RBNE位置1,软件读取最后一个字节,由于ACKEN已经在前一步骤中被清0,I2C不再为最后一个字节发送ACK,并在最后一个字节发送完毕后产生一个STOP结束位。

说明:
RBNE全称Read Buffer Not Empty,“接收缓冲区非空”标志位。
ACKEN全称Acknowledge Enable,“应答帧使能”标志位。

在这里插入图片描述

方案2

简要流程:

  1. 使能I2C外设时钟,配置时钟相关寄存器来确保正确的I2C时序,等待START起始位;
  2. 软件将START位置1,从而在I2C在总线上产生一个START起始位,SBSEND置1,进入主机模式;
  3. 写一个7位地址位或带有地址头的10位地址位到数据寄存器,SBSEND置0,若是10位地址,那么先写入高地址,发送后ADD10SEND置1,再写入低地址ADD10SEND置0;
  4. 7位或10位的地址位发送出去之后,ADDSEND置1,软件应该将ADDSEND置0,如果地址是10位格式,软件应该再次将START位置1来重新产生一个START(Sr);在START产生后,SBSEND位置1,软件将SBSEND置0,然后地址头被发到I2C总线,ADDSEND再次被置1,软件将ADDSEND置0;
  5. 第一个字节被接收,RBNE位置1,软件从数据寄存器读读取第一个字节,RBNE位被置0;
  6. 此后任何时候,一旦RBNE位被置1,软件就可以从数据寄存器读取一个字节的数据,直到主机接收了N-3个字节;
  7. 软件从数据寄存器读出倒数第三个(N-2)字节,同时也将BTC位置0;
  8. 此后第N-1个字节从移位寄存器被移到数据寄存器,总线得到释放然后开始接收最后一个字节;
  9. 最后一个字节接收完毕后,硬件再次把BTC位和RBNE置1,软件将STOP位置1,主机发出一个STOP结束位;
  10. 软件读取第N-1个字节,BTC置0,此后最后一个字节从移位寄存器被移动到数据寄存器;
  11. 软件读取最后一个字节,RBNE置0;

在这里插入图片描述

从机发送方

简要流程:

  1. 使能I2C外设时钟,配置时钟相关寄存器来确保正确的I2C时序,等待START起始位和地址;
  2. 接收一个START起始位及随后的地址,ADDSEND位置1,软件将ADDSEND置0;如果地址是10位格式,从机在检测到START(Sr)和紧接着的地址头之后会继续将ADDSEND位置1,软件第二次将ADDSEND位置0;
  3. I2C进入数据发送状态,TBE位置1,软件写入第一个字节数据到数据寄存器,写入数据寄存器的字节被立即移入内部移位寄存器;
  4. 移位寄存器非空,I2C开始发送数据到总线上;
  5. 第一个字节的发送期间,软件写第二个字节到数据寄存器,此时TBE位被置0;
  6. 在此之后,任何时候TBE被置1,只要依然有数据待被发送,软件都可以写入一个字节到数据寄存器;
  7. 倒数第二个字节发送期间,软件写最后一个数据到数据寄存器来将TBE置0;
  8. 倒数第二个字节发送完成后,TBE置1;
  9. 检测到STOP结束位,TBE置0。

根据I2C协议,I2C主机将不会对接收到的最后一个字节发送应答,所以在最后一个字节发
送结束后,I2C从机的AERR会置1以通知软件发送结束,软件写0到AERR位可以清除此
位。
AERR全称Acknowledge Error,“应答错误”标志位。

在这里插入图片描述

从机接收方

简要流程:

  1. 使能I2C外设时钟,配置时钟相关寄存器来确保正确的I2C时序,等待START起始位以及地址;
  2. 在接收到START起始条件和匹配的7位或10地址之后,ADDSEND位置1,软件将ADDSEND位置0;
  3. 一旦接收到第一个字节,RBNE位被硬件置1,软件可以读取数据寄存器的第一个字节,此时RBNE位也被清0;
  4. 任何时候RBNE被置1,软件可以从数据寄存器读取一个字节;
  5. 接收到最后一个字节后,RBNE被置1,软件可以读取最后的字节;
  6. 当I2C检测到I2C总线上一个STOP结束位,STPDET位被置1,软件将STPDET位置0。

STPDET全称Stop Detect,“停止位检测”标志位。
在这里插入图片描述

例程

GD32F103C8T6上有2个I2C设备,正好用来测试,它们的对应管脚如下表所示。

设备SCLSDA
I2C0PB6PB7
I2C1PB10PB11

在对管脚初始化时,I2C的两个管脚都必须设置为复用开漏模式
因为GD32F10C8T6的I2C外设没有内部上拉电阻,所以在接线的时候要在SDA和SCL分别加外部上拉电阻,如下图。
VCC为电源电压,一般为3.3V或5V;上拉电阻的阻值推荐为4.7kΩ或10kΩ

在这里插入图片描述

主机发送从机接收

现象:主机每秒向从机发送一串数据
i2c.c文件

#include "i2c.h"

void I2C_MasterInit(void)
{
    /* 初始化GPIO */
    rcu_periph_clock_enable(RCU_GPIOB);
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7);
    
    /* 初始化I2C */
    rcu_periph_clock_enable(RCU_I2C0);
    /* 初始化时钟,频率10kHz,占空比50% */
    i2c_clock_config(I2C0, 100000, I2C_DTCY_2);
    /* 设置地址,7位 */
    i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x82);
    /* 使能I2C0 */
    i2c_enable(I2C0);
    /* 使能应答 */
    i2c_ack_config(I2C0, I2C_ACK_ENABLE);
}

void I2C_SlaveInit(void)
{
    /* 初始化GPIO */
    rcu_periph_clock_enable(RCU_GPIOB);
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_10 | GPIO_PIN_11);
    
    /* 初始化I2C */
    rcu_periph_clock_enable(RCU_I2C1);
    /* 初始化时钟,频率10kHz,占空比50% */
    i2c_clock_config(I2C1, 100000, I2C_DTCY_2);
    /* 设置地址,7位 */
    i2c_mode_addr_config(I2C1, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x72);
    /* 使能I2C1 */
    i2c_enable(I2C1);
    /* 使能应答 */
    i2c_ack_config(I2C1, I2C_ACK_ENABLE);

}

void I2C_MasterSendSlaveReceive(void)
{
    uint8_t i2c_transmitter[16];
    uint8_t i2c_receiver[16];
    
    memset(i2c_transmitter, 0x00, sizeof(i2c_transmitter));
    memset(i2c_receiver, 0x00, sizeof(i2c_receiver));
    
    printf("Master transmit: ");
    for(uint8_t i = 0; i < 16; i++)
    {
        i2c_transmitter[i] = i + 0x80;
        printf("%x ", i2c_transmitter[i]);
    }
    printf("\n");
    
    /* 等待总线空闲 */
    while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY));

    /* 主机发送起始位 */
    i2c_start_on_bus(I2C0);

    /* 主机等待SBSEND置1 */
    while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND));

    /* 主机发送从机地址 */
    i2c_master_addressing(I2C0, 0x72, I2C_TRANSMITTER);

    /* 等待ADDSEND置1 */
    while(!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND));
    while(!i2c_flag_get(I2C1, I2C_FLAG_ADDSEND));
    /* ADDSEND置0 */
    i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
    i2c_flag_clear(I2C1, I2C_FLAG_ADDSEND);
    
    for(uint8_t i = 0; i < 16; i++)
    {
        /* 发送一个字节 */
        i2c_data_transmit(I2C0, i2c_transmitter[i]);
        /* 等待主机发送完成 */
        while(!i2c_flag_get(I2C0, I2C_FLAG_TBE));
        /* 等待从机接收完成 */
        while(!i2c_flag_get(I2C1, I2C_FLAG_RBNE));
        /* 从机读取数据 */
        i2c_receiver[i] = i2c_data_receive(I2C1);
    }
    /* 发送停止位 */
    i2c_stop_on_bus(I2C0);
    /* 等待STPDET位置1 */
    while(I2C_CTL0(I2C0)&0x0200);
    while(!i2c_flag_get(I2C1, I2C_FLAG_STPDET));
    /* STPSET置0 */
    i2c_enable(I2C0);
    
    printf("Slave receive: ");
    for(uint8_t i = 0; i < 16; i++)
    {
        printf("%x ", i2c_receiver[i]);
    }
    printf("\n");
}

main.c文件

#include "gd32f10x.h"
#include "main.h"
#include "systick.h"
#include "usart.h"
#include "i2c.h"
#include <stdio.h>
#include <string.h>

int main(void)
{	
    systick_config();
    USART_Config();
    I2C_MasterInit();
    I2C_SlaveInit();
        
    while(1)
    {
        I2C_MasterSendSlaveReceive();
        delay_ms(1000);
    }
}

在这里插入图片描述

主机接收从机发送

现象:主机每秒接收从机发送的一串数据
i2c.c文件

#include "i2c.h"

void I2C_MasterInit(void)
{
    /* 初始化GPIO */
    rcu_periph_clock_enable(RCU_GPIOB);
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7);
    
    /* 初始化I2C */
    rcu_periph_clock_enable(RCU_I2C0);
    /* 初始化时钟,频率10kHz,占空比50% */
    i2c_clock_config(I2C0, 100000, I2C_DTCY_2);
    /* 设置地址,7位 */
    i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x82);
    /* 使能I2C0 */
    i2c_enable(I2C0);
    /* 使能应答 */
    i2c_ack_config(I2C0, I2C_ACK_ENABLE);
}

void I2C_SlaveInit(void)
{
    /* 初始化GPIO */
    rcu_periph_clock_enable(RCU_GPIOB);
    gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_10 | GPIO_PIN_11);
    
    /* 初始化I2C */
    rcu_periph_clock_enable(RCU_I2C1);
    /* 初始化时钟,频率10kHz,占空比50% */
    i2c_clock_config(I2C1, 100000, I2C_DTCY_2);
    /* 设置地址,7位 */
    i2c_mode_addr_config(I2C1, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x72);
    /* 使能I2C1 */
    i2c_enable(I2C1);
    /* 使能应答 */
    i2c_ack_config(I2C1, I2C_ACK_ENABLE);

}

void I2C_MasterReceiveSlaveSend(void)
{
    uint8_t i2c_transmitter[16];
    uint8_t i2c_receiver[16];
    
    memset(i2c_transmitter, 0x00, sizeof(i2c_transmitter));
    memset(i2c_receiver, 0x00, sizeof(i2c_receiver));
    
    printf("Slave transmit: ");
    for(uint8_t i = 0; i < 16; i++)
    {
        i2c_transmitter[i] = i + 0x80;
        printf("%x ", i2c_transmitter[i]);
    }
    printf("\n");

    /* 等待总线空闲 */
    while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY));
    /* 主机发送起始信号 */
    i2c_start_on_bus(I2C0);
    /* 主机等待SBSEND置1 */
    while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND));
    /* 主机发送从机地址 */
    i2c_master_addressing(I2C0, 0x72, I2C_RECEIVER);
    /* 主机等待ADDSEND置1 */
    while(!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND));
    /* 主机ADDSEND置0 */
    i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
    /* 从机等待ADDSEND置1 */
    while(!i2c_flag_get(I2C1, I2C_FLAG_ADDSEND));
    /* 从机ADDSEND置0 */
    i2c_flag_clear(I2C1, I2C_FLAG_ADDSEND);
    
    /* 从机发送n-1个字节 */
    for(uint8_t i = 0; i < 15; i++)
    {
        /* 发送一个字节 */
        i2c_data_transmit(I2C1, i2c_transmitter[i]);
        /* 从机等待发送完成 */
        while(!i2c_flag_get(I2C1, I2C_FLAG_TBE));
        /* 主机等待接收完成 */
        while(!i2c_flag_get(I2C0, I2C_FLAG_RBNE));
        /* 获取一个字节 */
        i2c_receiver[i] = i2c_data_receive(I2C0);
    }
    /* 关闭ACK */
    i2c_ack_config(I2C0, I2C_ACK_DISABLE);
    /* 发送最后一个字节 */
    i2c_data_transmit(I2C1, i2c_transmitter[15]);
    /* 从机等待发送完成 */
    while(!i2c_flag_get(I2C1, I2C_FLAG_TBE));
    /* 主机等待应答错误位置1 */
    while(!i2c_flag_get(I2C1, I2C_FLAG_AERR));
    /* 主机发送停止信号 */
    i2c_stop_on_bus(I2C0);
    while(I2C_CTL0(I2C0)&0x0200);
    /* 获取最后一个字节 */
    i2c_receiver[15] = i2c_data_receive(I2C0);
    i2c_ack_config(I2C0, I2C_ACK_ENABLE);
    /* 从机AERR置0 */
    i2c_flag_clear(I2C1, I2C_FLAG_AERR);

    printf("Master receive: ");
    for(uint8_t i = 0; i < 16; i++)
    {
        printf("%x ", i2c_receiver[i]);
    }
    printf("\n");

}

main.c文件

#include "gd32f10x.h"
#include "main.h"
#include "systick.h"
#include "usart.h"
#include "i2c.h"
#include <stdio.h>
#include <string.h>

int main(void)
{	
    systick_config();
    USART_Config();
    I2C_MasterInit();
    I2C_SlaveInit();
        
    while(1)
    {
        I2C_MasterReceiveSlaveSend();
        delay_ms(1000);
    }
}

在这里插入图片描述

  • 20
    点赞
  • 121
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
以下是一个示例代码,用于两个STM32单片之间的IIC通信。在该例程中,一个STM32作为从设备(Slave)发送数据给另一个STM32作为主设备(Master)进行接收。 从设备发送数据给主设备: ```c #include "stm32f4xx.h" #include "stm32f4xx_i2c.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_rcc.h" #define I2C_ADDRESS 0x08 // 主设备地址 I2C_InitTypeDef I2C_InitStruct; GPIO_InitTypeDef GPIO_InitStruct; void I2C_Configuration(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_I2C1); GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1); I2C_DeInit(I2C1); I2C_InitStruct.I2C_ClockSpeed = 100000; I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStruct.I2C_OwnAddress1 = 0x07; // 从设备地址 I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_Init(I2C1, &I2C_InitStruct); I2C_Cmd(I2C1, ENABLE); } void I2C_SendData(uint8_t* data, uint8_t dataLength) { while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); I2C_GenerateSTART(I2C1, ENABLE); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, I2C_ADDRESS, I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); for (uint8_t i = 0; i < dataLength; i++) { I2C_SendData(I2C1, data[i]); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); } I2C_GenerateSTOP(I2C1, ENABLE); } int main(void) { uint8_t data[] = "Hello"; I2C_Configuration(); while (1) { I2C_SendData(data, sizeof(data)); delay(500); } } ``` 主设备接收从设备发送的数据: ```c #include "stm32f4xx.h" #include "stm32f4xx_i2c.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_usart.h" #define I2C_ADDRESS 0x07 // 从设备地址 I2C_InitTypeDef I2C_InitStruct; GPIO_InitTypeDef GPIO_InitStruct; USART_InitTypeDef USART_InitStruct; void I2C_Configuration(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_I2C1); GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1); I2C_DeInit(I2C1); I2C_InitStruct.I2C_ClockSpeed = 100000; I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStruct.I2C_OwnAddress1 = 0x08; // 主设备地址 I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_Init(I2C1, &I2C_InitStruct); I2C_Cmd(I2C1, ENABLE); } void USART_Configuration(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); USART_DeInit(USART1); USART_InitStruct.USART_BaudRate = 9600; USART_InitStruct.USART_WordLength = USART_WordLength_8b; USART_InitStruct.USART_StopBits = USART_StopBits_1; USART_InitStruct.USART_Parity = USART_Parity_No; USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStruct); USART_Cmd(USART1, ENABLE); } void I2C_ReceiveData(void) { uint8_t receivedData[10]; uint8_t dataLength = 0; while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); I2C_AcknowledgeConfig(I2C1, ENABLE); I2C_GenerateSTART(I2C1, ENABLE); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, I2C_ADDRESS, I2C_Direction_Receiver); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); while (dataLength < sizeof(receivedData)) { if (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) { receivedData[dataLength++] = I2C_ReceiveData(I2C1); } } I2C_AcknowledgeConfig(I2C1, DISABLE); I2C_GenerateSTOP(I2C1, ENABLE); for (uint8_t i = 0; i < dataLength; i++) { while (!USART_GetFlagStatus(USART1, USART_FLAG_TXE)); USART_SendData(USART1, receivedData[i]); } } int main(void) { I2C_Configuration(); USART_Configuration(); while (1) { I2C_ReceiveData(); delay(500); } } ``` 在从设备中,我们使用`I2C_SendData()`函数发送数据给主设备。首先,我们等待IIC总线空闲,然后生成START信号,接着发送主设备地址和写入位,然后通过循环发送数据字节。最后,我们生成STOP信号结束传输。 在主设备中,我们使用`I2C_ReceiveData()`函数接收从设备发送的数据。首先,我们等待IIC总线空闲,然后生成START信号,接着发送从设备地址和读取位。然后,我们循环接收数据字节,并通过USART发送到串口进行打印。 请注意:以上示例代码是基于STM32F4系列的,使用了STM32标准外设库(Standard Peripherals Library)。如果你使用其他型号的STM32单片机或者使用了不同的开发库,可能需要进行相应的调整。 希望这个例程对你有所帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马浩同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值