stm32--软件模拟IIC

一、IIC

基于stm32F103 软件模拟IIC。
IIC_SCL和IIC_SDA分别复用PB6和PB7 IO口。

二、具体代码

1.头文件

#ifndef __IIC_SW_H
#define __IIC_SW_H
//GPIOx ODR和IDR寄存器地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+0x0C) //0x4001080C
#define GPIOB_ODR_Addr    (GPIOB_BASE+0x0C) //0x40010C0C
#define GPIOC_ODR_Addr    (GPIOC_BASE+0x0C) //0x4001100C
#define GPIOD_ODR_Addr    (GPIOD_BASE+0x0C) //0x4001140C
#define GPIOE_ODR_Addr    (GPIOE_BASE+0x0C) //0x4001180C
#define GPIOF_ODR_Addr    (GPIOF_BASE+0x0C) //0x40011A0C
#define GPIOG_ODR_Addr    (GPIOG_BASE+0x0C) //0x40011E0C

#define GPIOA_IDR_Addr    (GPIOA_BASE+0x08)  //0x40010808
#define GPIOB_IDR_Addr    (GPIOB_BASE+0x08)  //0x40010C08
#define GPIOC_IDR_Addr    (GPIOC_BASE+0x08)  //0x40011008
#define GPIOD_IDR_Addr    (GPIOD_BASE+0x08)  //0x40011408
#define GPIOE_IDR_Addr    (GPIOE_BASE+0x08)  //0x40011808
#define GPIOF_IDR_Addr    (GPIOF_BASE+0x08)  //0x40011A08
#define GPIOG_IDR_Addr    (GPIOG_BASE+0x08)  //0x40011E08

//位段操作
/*外设位带别名区地址0x42000000 ~ 0x43FFFFFF*/
#define BIT_BAND(addr, pin_num)  ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEE_ADDR(addr) *((volatile unsigned long*)addr)
#define BIT_ADDR(addr, pin_num)  MEE_ADDR(BIT_BAND(addr, pin_num))

#define PBout(pin_num)  BIT_ADDR(GPIOB_ODR_Addr, pin_num)
#define PBin(pin_num)  BIT_ADDR(GPIOB_IDR_Addr, pin_num)

// IO 操作
#define I2C_SCL  PBout(6)  //PB6  SCL
#define I2C_SDA  PBout(7)  //PB7  SDA
#define READ_SDA  PBin(7)  //PB7   输入SDA

#define SDA_IN()  {GPIOB->CRL &= 0x0FFFFFFF; GPIOB->CRL |= 0x08<<28;}  //上下拉输入
#define SDA_OUT()  {GPIOB->CRL &= 0x0FFFFFFF; GPIOB->CRL |= 0x07<<28;}  //开漏输出

#endif

2.源文件

void IIC_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;   //开漏输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    I2C_SCL = 1;  //SCL和SAD均处于空闲状态
    I2C_SDA = 1;
}

//IIC起始信号
void IIC_Start(void)
{
    SDA_OUT();
    I2C_SCL = 1; 
    I2C_SDA = 1;
    delay_us(4); //延时大概4us
    I2C_SDA = 0;
    delay_us(4);
    I2C_SCL = 0; //将SCL拽下来,准备将要发送或接受的数据
}

//IIC结束信号
void IIC_Stop(void)
{
    SDA_OUT();
    I2C_SCL = 0; 
    I2C_SDA = 0;
    delay_us(4);
    I2C_SCL = 1; 
    I2C_SDA = 1;
    delay_us(4);
}

//等待ACK
//返回值:0-接受应答失败;1-接受应答成功
uint8_t IIC_Wait_Ack(void)
{
    uint8_t ErrCnt = 0;
    SDA_IN();  //SDA设置为输入。主机接受从机的应答信号
    I2C_SDA = 1; // 主机释放SDA,从机获得SDA控制权
    I2C_SCL = 1; //SCL高电平期间读数据
    while(READ_SDA)
    {
        ErrCnt ++;
        if(ErrCnt > 200){
            IIC_Stop();
            return 0;
        }
    }
    I2C_SCL = 0;
    return 1;
}

//产生ACK应答
void IIC_ACK(void)
{
    SDA_OUT();
    I2C_SCL = 0;
    I2C_SDA = 0;
    delay_us(2);
    I2C_SCL = 1;
    delay_us(2);
    I2C_SCL = 0;
}

//产生NACK应答
void IIC_NACK(void)
{
    SDA_OUT();
    I2C_SCL = 0;
    I2C_SDA = 1;
    delay_us(2);
    I2C_SCL = 1;
    delay_us(2);
    I2C_SCL = 0;
}

//发送一个字节
void IIC_Send_Byte(uint8_t trd)
{
    SDA_OUT();
    I2C_SCL = 0; //拉低SCL,进行数据准备
    for(uint8_t i = 0; i<8; i++)
    {
        I2C_SDA = (trd & 0x80)>>7; //数据 高位先行
        trd <<= 1;
        delay_us(2); //以下三个延时是必须的,需要给从机读数据的时间。
        I2C_SCL = 1;
        delay_us(2);
        I2C_SCL = 0;
        delay_us(2);
    }
}

//接收一个字节。ack=1时,发送Ack; ack=0时,发送NAck
uint8_t IIC_Read_Byte(bool ack)
{
    uint8_t receive = 0;
    SDA_IN();
    for(uint8_t i=0; i<8; i++)
    {
        I2C_SCL = 0; //从机进行数据准备
        delay_us(2); 
        I2C_SCL = 1;
        receive <<=1;
        if(READ_SDA) 
            receive ++;
        delay_us(2);
    }
    if(ack)
        IIC_ACK();
    else
        IIC_NACK();
    return receive ;
}

void IIC_WriteByte(uint8_8 device_addr, uint8_t addr, uint8_t data)
{
    IIC_Start();
    IIC_Send_Byte(device_addr);
    IIC_Wait_Ack();
    IIC_Send_Byte(addr);
    IIC_Wait_Ack();
    IIC_Send_Byte(data);
    IIC_Wait_Ack();
    IIC_Stop();
    delay_us(2);
}

void IIC_ReadBytes(uint8_8 device_addr, uint8_t addr, uint8_t*data)
{
    IIC_Start();
    IIC_Send_Byte(device_addr);
    IIC_Wait_Ack();
    IIC_Send_Byte(addr);
    IIC_Wait_Ack();

    IIC_Start();  //repeat start
    IIC_Send_Byte(device_addr);
    IIC_Wait_Ack();
    *data = IIC_Read_Byte(0);  // NAck
    IIC_Stop();
    delay_us(2);
}

总结

重点在于IIC的读写时序图

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值