stm32f103软件I2C通信

简介

Intern IC BUS, Philips公司开发. 同步, 半双工, 带数据应答机制的通信协议. 支持总线挂载多个设备, 支持多主多从模式, 支持一主多从模式.

同步时序: 软件更加的容易模拟, 不必要主机从机发送接收数据那么严格, 也可以被中断打断.

多主多从: 所有设备都可以跳出来宣布, 我是主机, 在总线冲突时, I2C协议会进行仲裁, 胜利的一方取的总线控制权, 失败的一方变为从机.

一主多从: 主机权利: SCL完全控制权, 空闲状态下, 主机可以主动发起对SDA的控制, 只有从机发送数据, 从机应答时, 主机才会转交SDA的控制权给从机.

硬件电路

stm32f103软件I2C通信_第1张图片

 上拉电阻和开漏输出模式的设计

  1.  软件模拟I2C通信协议GPIO是需要使用开漏输出模式的, 开漏输出模式和上拉电阻的设计, 可以让SDA通信线, 可以让主机占用时输出0和1的数据, 在需要转让SDA占用权的时候, GPIO只需要置1即可, 此时从机将可以控制输出0或者1.
  2. 上拉电阻: SDA引脚, 从机和主机永远是相反的, 一个Input, 一个output. 时机把握不对, 一个输出高电平, 一个输出低电平, 将会造成电源短路, 所以禁止所有设备强上拉的高电平, 使用外部弱上拉来完成电平的转换.

信号时序

空闲状态

 空闲状态下, SCL和SDA都是高电平.

起始信号

stm32f103软件I2C通信_第2张图片

 防止和终止信号冲突, 要先拉高SDA, 再拉高SCL信号进入空闲状态.

终止信号

 stm32f103软件I2C通信_第3张图片

发送一个字节

stm32f103软件I2C通信_第4张图片

 数据发送和接收的时候, 是高位先行, 即: 0b1010 1100, 实际发送顺序是先发送1->0->1->0->1->1->0->0.

在SCL高电平的时候就会读取完成, 所以需要再低电平的时候将SDA设置为数据位, 再拉高SCL.

主机可以慢慢发, 只要大于从机接收速度就行, 一位发一年都可以, 接收也是, 只要控制SCL就有绝对的控制权.

接收一个字节

stm32f103软件I2C通信_第5张图片

 接收, 主机要释放SDA的控制权. 由从机掌控. 在SCL为高电平的时候, 主机读取.

发送应答信号

 stm32f103软件I2C通信_第6张图片

在从机发送数据后, 主机接收到了需要发送应答信号, 如果为0表示应答, 如果为1表示非应答.

应答: 让从机发送继续.

非应答: 让从机停止发送. 

接收应答信号

 stm32f103软件I2C通信_第7张图片

主机发送数据后,  需要读取从机的应答信号. 如果为0表示应答, 如果为1表示非应答.

应答: 从机收到了.

非应答: 从机没收到. 

 I2C完整数据帧

1. 指定地址写

stm32f103软件I2C通信_第8张图片

 起始信号->从机地址+写位(0为写1为读)->接收应答->发送写入地址->接收应答->发送数据->接收应答->停止信号.

2. 当前地址读

 stm32f103软件I2C通信_第9张图片

3. 指定地址读

stm32f103软件I2C通信_第10张图片

起始信号-> 从机地址+写位(0为写1为读)->接收应答->发送读入地址->接收应答->起始信号->从机地址+读位->接收应答->读取数据->发送非应答->停止信号

代码编写

头文件 

 #ifndef __MYI2C_H
#define __MYI2C_H

#include "stm32f10x.h"                  // Device header
#include "delay.h"
#include "bitBand.h"

#define SCL 12
#define SDA 11


void myI2C_Init(void);
void I2C_StartSignal(void);
void I2C_StopSignal(void);  
void I2C_SendByte(uint8_t data);
uint8_t I2C_receiveByte(void);
void I2C_SendAck(uint8_t ack);
uint8_t I2C_ReceiveAck(void);

#endif

源文件 

 #include "myI2C.h"

//配置开漏输出模式, 让PA11作为SDA, PA12作为SCL
void myI2C_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    //初始全是0
}

//起始信号
void I2C_StartSignal(void)
{
    PAout(SDA) = 1; //顺序不能错,要和停止信号区分开
    Delay_us(5);
    PAout(SCL) = 1;
    Delay_us(5);
    PAout(SDA) = 0;
    Delay_us(5);
    PAout(SCL) = 0;
    Delay_us(5);
}

//终止信号
void I2C_StopSignal(void)
{
    PAout(SDA) = 0;
    Delay_us(5);
    PAout(SCL) = 1;
    Delay_us(5);
    PAout(SDA) = 1;
    Delay_us(5);
}

//发送一字节数据,高位先行
void I2C_SendByte(uint8_t data)
{
    PAout(SCL) = 0;
    for(uint8_t i = 0; i < 8; ++i)
    {
        PAout(SDA) = (data >> (7-i)) & 0x01;
        Delay_us(5);
        PAout(SCL) = 1;
        Delay_us(5);
        PAout(SCL) = 0;
        Delay_us(5);
    }
}

//接收一字节数据
uint8_t I2C_receiveByte(void)
{
    uint8_t data = 0;
    PAout(SCL) = 0;
    PAin(SDA) = 1; //释放SDA
    Delay_us(5);//等待从机放数据
    for(uint8_t i = 0; i < 8; ++i)
    {
        PAout(SCL) = 1;
        Delay_us(5);
        data |= PAin(SDA);
        if(i != 7)
        {
            data = (data | PAin(SDA)) << 1;
        }
        Delay_us(5);
        PAout(SCL) = 0;
        Delay_us(5);
    }
    return data;
}    

//发送应答信号,0应答,1非应答
void I2C_SendAck(uint8_t ack)
{
    PAout(SCL) = 0;
    PAout(SDA) = ack;
    Delay_us(5);
    PAout(SCL) = 1;
    Delay_us(5);
    PAout(SCL) = 0;
}
//接收应答信号,0应答,1非应答
uint8_t I2C_ReceiveAck(void)
{
    uint8_t ack = 0;
    PAout(SDA) = 1; //必须释放SDA,开漏等于1就是释放
    PAout(SCL) = 0;
    PAout(SCL) = 1;
    Delay_us(5);
    ack = PAin(SDA);
    Delay_us(5);
    PAout(SCL) = 0;
    return ack;
}

你可能感兴趣的:(stm32,嵌入式硬件,单片机)