iic总线实现代码

#include "iic.h"

extern void printf(const char* fmt, ...);

void delay_us(void)
{
    unsigned int i = 2000;
    while(i--);
}

void i2c_init(void)
{
     // 使能GPIOF端口的时钟
    RCC->MP_AHB4ENSETR |= (0x1 << 5);
    // 设置PF14,PF15引脚为通用的输出功能
    GPIOF->MODER &= (~(0xF << 28));
    GPIOF->MODER |= (0x5 << 28);
    // 设置PF14, PF15引脚为推挽输出
    GPIOF->OTYPER &= (~(0x3 << 14));
    // 设置PF14, PF15引脚为高速输出
    GPIOF->OSPEEDR |= (0xF << 28);
    // 设置PF14, PF15引脚的禁止上拉和下拉
    GPIOF->PUPDR &= (~(0xF << 28));
    // 空闲状态SDA和SCL拉高 
    I2C_SCL_H;
    I2C_SDA_H;
}


void i2c_start(void)
{
    //在默认状态下改为输出模式
    SET_SDA_OUT;
    //首先在时钟线拉高时,再将数据线拉高,确保是在高电平状态下
    I2C_SCL_H;
    I2C_SDA_H;
    //加个延时函数确保处于高电平状态,转换成功
    delay_us();
    //确定的高电平状态下,拉低数据线进行发送开始信号
    I2C_SDA_L;//开始信号
    delay_us();//延时过后的此时钟,已经下拉处于写状态

    I2C_SCL_L;//拉低时钟线,确保处于写状态,
    //并且总线被主机占用状态

}

void i2c_stop(void)
{
    
    //确保SDA为输出模式
    SET_SDA_OUT;
    I2C_SCL_L;//时钟拉低,确保处于低电平状态
    //并且可以将一些数据该发送的发送走
    delay_us();
    I2C_SDA_L;//数据线先拉低,确保处于低电平状态
    delay_us();

    //开始终止模式
    I2C_SCL_H;//拉高时钟线
    delay_us();
    I2C_SDA_H;//拉高数据线
    delay_us();


}

void i2c_write_byte(unsigned char dat)
{
    
    //先设置为输出模式写入信号发送
    SET_SDA_OUT;
    unsigned int i;
    for(i=0;i<8;i++){
    I2C_SCL_L;//拉低时钟线占用切换写模式
    delay_us();
    if(dat&0x80)
    {
    I2C_SDA_H;
    }//写入高位数据
    else
    {
    I2C_SDA_L;
    }
    delay_us();
    I2C_SCL_H;//时钟线拉高,禁止写入,等待下一个轮回
    delay_us();
    delay_us();
    dat<<=1;//移位
    }

}


unsigned char i2c_read_byte(unsigned char ack)
{
    
    //设置为输入模式
    SET_SDA_IN;
    unsigned int i;
    unsigned char dat;//返回读取的数据
    for(i=0;i<8;i++)
    {
        I2C_SCL_L;//时钟线拉低
        delay_us();//保证写数据完整
        //这里试试只写一个延时函数
        I2C_SCL_H;//时钟线拉高
        delay_us();
        dat<<=1;//先移位后每一个数据,都左移1位先
        if(I2C_SDA_READ)//读取分机的数据位
        {
        dat|=1;//补上之前的左移后的1
        }
        else
        {
        dat|=0;//这里加不加都行
        }
        delay_us();//延时一下得到结果
    }
    if(!ack)
        i2c_ack();//因为主机作为发送器接收的ACK应答包
    else
        i2c_nack();
    return dat;//返回一个数据dat给主机

}

unsigned char i2c_wait_ack(void)
{
    
    I2C_SCL_L;//拉低时钟线为了输入数据
    I2C_SDA_H;//释放总线
    delay_us();
    SET_SDA_IN;//变换总线方向

    I2C_SCL_H;
    delay_us();
    if(I2C_SDA_READ)
    return 1;//非应答信号
    I2C_SCL_L;
    return 0;//应答信号

void i2c_ack(void)
{
    
    SET_SDA_OUT;//确保总线输出模式
    I2C_SCL_L;//时钟线拉低为写输出
    delay_us();//
    I2C_SDA_L;//数据线先拉低 应答信号
    delay_us();
    I2C_SCL_H;//时钟线拉高,读取数据
    delay_us();
    I2C_SCL_L;//时钟线拉低,总线处于占用状态
}

void i2c_nack(void)
{
    
    SET_SDA_OUT;//确保总线输出模式
    I2C_SCL_L;//拉低时钟为了写数据
    delay_us();
    I2C_SDA_H;//拉高写数据1非应答包
    delay_us();
    I2C_SCL_H;//时钟线拉高,读取数据
    delay_us();
    delay_us();
    I2C_SCL_L;//时钟线拉低,总线处于占用状态
}


头文件

#define SET_SDA_OUT     do{GPIOF->MODER &= (~(0x3 << 30)); \
                            GPIOF->MODER |= (0x1 << 30);}while(0)
#define SET_SDA_IN      do{GPIOF->MODER &= (~(0x3 << 30));}while(0)

#define I2C_SCL_H       do{GPIOF->BSRR |= (0x1 << 14);}while(0)
#define I2C_SCL_L       do{GPIOF->BRR |= (0x1 << 14);}while(0)

#define I2C_SDA_H       do{GPIOF->BSRR |= (0x1 << 15);}while(0)
#define I2C_SDA_L       do{GPIOF->BRR |= (0x1 << 15);}while(0)

#define I2C_SDA_READ    (GPIOF->IDR & (0x1 << 15))

void delay_us(void);
void i2c_init(void);
void i2c_start(void);
void i2c_stop(void);
void i2c_write_byte(unsigned char  dat);
unsigned char i2c_read_byte(unsigned char ack);
unsigned char i2c_wait_ack(void);       
void i2c_ack(void);
void i2c_nack(void);

你可能感兴趣的:(arm开发)