zhy233090130 发表于 2019-12-5 21:16:46

求个STM32F0 模拟I2C的程序例程

各位大牛,求一个STM32F0 模拟I2C的程序例程,求分享,谢谢!

hujjj 发表于 2019-12-6 09:25:15

这个应该不难吧,关键是要调整好SCL的时序。

ldptest 发表于 2019-12-6 11:19:17

直接拷贝stm32f103的例程,改成自己的GPIO端口(注意使用相应库函数)。就行了。

发表于 2019-12-6 11:29:19

HAL库的I2C硬件驱动可以直接使用,建议使用硬件驱动。

zhy233090130 发表于 2019-12-6 20:13:42

安 发表于 2019-12-6 11:29
HAL库的I2C硬件驱动可以直接使用,建议使用硬件驱动。

硬件上没有接I2C复用引脚,只能使用模拟的

zhy233090130 发表于 2019-12-6 20:16:12

ldptest 发表于 2019-12-6 11:19
直接拷贝stm32f103的例程,改成自己的GPIO端口(注意使用相应库函数)。就行了。 ...

能分享一下例程吗,感谢!!

hujjj 发表于 2019-12-8 09:28:08

    下面是我基于G431的软件模拟I2C对AT24C32进行读写操作的代码,应该可以直接使用,最多就是调整一下延时函数,希望能帮到您。

//#include "main.h"
#include "i2c.h"
//#include <stdio.h>

/***************************************************
函数功能:毫秒延时
入口参数:延时毫秒
***************************************************/
void delay_us(uint8_t us)
{
    uint8_t x,y;
    for(x=us;x>0;x--)   
    for(y=20;y>0;y--);
}

/***************************************************
函数功能:软件I2C配置
入口参数:无
***************************************************/
void SI2C_Config(void)            //引脚配置
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = SI2C_SCL | SI2C_SDA;   //SCL,SDA,RST输出模式、弱上拉
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(SI2C_PORT, &GPIO_InitStruct);
       
}


/***************************************************
函数功能:DAT引脚配置
入口参数:x
***************************************************/
void SI2C_PortConfig(uint8_t dir)            //DAT端口配置(根据需要配置成输出或输入模式)
{
        GPIO_InitTypeDef GPIO_InitStruct;
    if(dir == 0){
      GPIO_InitStruct.Pin = SI2C_SDA;      //SDA输出模式、弱上拉
      GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
      GPIO_InitStruct.Pull = GPIO_NOPULL;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
      HAL_GPIO_Init(SI2C_PORT, &GPIO_InitStruct);
        }
        else{
          GPIO_InitStruct.Pin = SI2C_SDA;      //SDA输入模式
      GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
      HAL_GPIO_Init(SI2C_PORT, &GPIO_InitStruct);
        }
        HAL_GPIO_WritePin(SI2C_PORT,SI2C_SDA,GPIO_PIN_SET);
}

/******************************************************************************************************************************************
* 函数名称: I2C_Start()
* 功能说明:        产生I2C传输的Start信号
* 输    入: 无
* 输    出: 无
******************************************************************************************************************************************/
void SI2C_Start(void)
{
    SDA_OUT;         //SDA输出
    SDA_1;
    SCL_1;             //scl = 1;
        delay_us(7);
        SDA_0;             //sda = 0;        scl为高时sda的下降沿表示“起始”
        delay_us(5);
        SCL_0;             //scl = 0;钳住I2C总线,准备发送或接收数据 START:when CLK is high,DATA change form high to low
}

/******************************************************************************************************************************************
* 函数名称:        I2C_Stop()
* 功能说明:        产生I2C传输的Stop信号
* 输    入: 无
* 输    出: 无
******************************************************************************************************************************************/
void SI2C_Stop(void)
{
    SDA_OUT;
    SCL_0;             // scl = 0;
    SDA_0;             // STOP:when CLK is high DATA change form low to high
    delay_us(5);
    SCL_1;             // scl = 1;
    delay_us(5);       
    SDA_1;             // sda = 1;        sclk为高时sdat的上升沿表示“停止”
}


/******************************************************************************************************************************************
* 函数名称: I2C_Send()
* 功能说明:        向IIC总线发送一个字节的数据
* 输    入: byte dat         要发送的数据
* 输    出: 无
******************************************************************************************************************************************/
void SI2C_Send(uint8_t dat)
{
        uint8_t i;
        SDA_OUT;
        SCL_0;               //拉低时钟开始数据传输
        for(i=0;i<8;i++)
        {
//                if(((dat&0x80)>>7) == 1) SDA_1;//准备好SDA数据
      if((dat>>(7-i))&0x01) SDA_1;
                else SDA_0;
//                dat>>=1;
                delay_us(5);
                SCL_1;                         //拉高时钟等待从设备读取数据
                delay_us(9);
                SCL_0;                         //拉低时钟准备下一位数据
                delay_us(5);
        }
}


/******************************************************************************************************************************************
* 函数名称:        I2C_Receive()
* 功能说明:        从IIC总线接收一个字节的数据
* 输    入: 无
* 输    出: byte                从IIC总线上接收到得数据
* 注意事项: 无
******************************************************************************************************************************************/
uint8_t SI2C_Receive(void)
{
        uint8_t i,dat;
        SDA_IN;      //设置为输入       
        for(i=0;i<8;i++)
        {
                SCL_0;
                delay_us(7);
                SCL_1;
                dat<<=1;
      if(1 == SDA_X)
            dat|=0x01;
                delay_us(5);
        }
        return dat;

/*
        ui08 i = 0;
        byte d = 0;
        byte dat = 0;

        for(i=0;i<8;i++)
    {
                scl = 0;
                DELAY();
                sda = 1;                //本语句必须有:于IIC,是释放SDA线;于51单片机,则是由于51的IO不是真双向口,在读之前必须写0
              DELAY();

              scl = 1;
                DELAY();
                d = sda;
              DELAY();

                dat |= (d<<(7-i));
    }
    return dat;
*/
}

/******************************************************************************************************************************************
* 函数名称: I2CDoAck()
* 功能说明:        在应答位位置产生应答,从而继续连续传输
* 输    入: 无
* 输    出: 无
******************************************************************************************************************************************/
void SI2CDoAck(void)
{
    SCL_0;
        SDA_OUT;
    SDA_0;            //sda = 0;        /拉低数据线,即给于应答
    delay_us(7);
    SCL_1;            //scl = 1;
           delay_us(7);
    SCL_0;                    //scl = 0;
}

/******************************************************************************************************************************************
* 函数名称: I2CNoAck()
* 功能说明:        在应答位位置不产生应答,从而终止连续传输
* 输    入: 无
* 输    出: 无
******************************************************************************************************************************************/
void SI2CNoAck(void)
{
    SCL_0;
        SDA_OUT;
    SDA_1;            // sda = 1;        不拉低数据线,即不给于应答
    delay_us(7);
    SCL_1;            // scl = 1;
    delay_us(7);
    SCL_0;            // scl = 0;
}

/******************************************************************************************************************************************
* 函数名称: I2CIsAck()
* 功能说明:        检测从机应答位
* 输    入: 无
* 输    出: uint8_t        0=ACK_OK 从机产生了应答;1=ACK_NO 从机没有产生应答
******************************************************************************************************************************************/
uint8_t SI2CIsAck(void)
{
    uint8_t i;
    SDA_OUT;
    SDA_1;            // sda = 1; 释放数据线
    delay_us(4);
    SDA_IN;
    SCL_1;            // scl = 1;
    delay_us(4);
        while(SDA_X){
                i++;
                if(i>250){
                        SI2C_Stop();//数据线未被拉低,即未收到应答
                        return 1;
                }
        }
    SCL_0;
    return 0;
}





#include "i2c.h"
#include "at24c32.h"

/*************************************************************
* 函数名:at24cxx_Wite_Data(uint8_t addr,uint8_t data)
* 输入参数:addr = 要写入的地址
*         data = 要写入的数据
* 输出参数:无
*************************************************************/
void at24cxx_Write_Data(uint16_t addr,uint8_t data) // 将数据data写入指定地址Addr
{

    SI2C_Start();                //开始I2C通讯
    SI2C_Send(0xA0);             //发送EEPROM器件写入地址
    SI2CIsAck();               //等待应答
    SI2C_Send(addr>>8);          //发送高8位地址
    SI2CIsAck();               //等待应答
    SI2C_Send(addr%256);         //发送低8位地址
    SI2CIsAck();               //等待应答
    SI2C_Send(data);             //发送数据
    SI2CIsAck();               //等待应答
    SI2C_Stop();               //结束I2C通讯
    delay_us(4);
}

/*************************************************************
* 函数名:at24cxx_Read_Data(uint8_t addr)
* 输入参数:addr = 要读取的地址
* 输出参数:data = 读出的数据
*************************************************************/
uint8_t at24cxx_Read_Data(uint16_t addr)            // 读指定地址Addr里的数据Data
{
    uint8_t data=0x00;
   
    SI2C_Start();                //开始I2C通讯
    SI2C_Send(0xA0);             //发送EEPROM器件地址
    SI2CIsAck();               //等待应答
    SI2C_Send(addr>>8);          //发送高8位地址
    SI2CIsAck();               //等待应答
    SI2C_Send(addr%256);         //发送低8位地址
    SI2CIsAck();               //等待应答

    SI2C_Start();
    SI2C_Send(0xA1);             //发送EEPROM器件读取地址
    SI2CIsAck();
   
    data = SI2C_Receive();       //读一字节数据
    SI2CDoAck();
    SI2C_Stop();
    delay_us(4);
    return data;
}

/*************************************************************
* 函数名:at24cxx_Page_Write(uint8_t addr,uint8_t *buff,uint8_t size)
* 输入参数:addr = 要写入的起始地址
*         *buff= 要写入的数组指针
*         size = 数组的长度
* 输出参数:无
*************************************************************/
void at24cxx_Page_Write(uint16_t addr,uint8_t *buff,uint8_t size) // 将数据buff数组写入指定起的始地址Addr
{
    uint8_t i;
       
    SI2C_Start();                //开始I2C通讯
    SI2C_Send(0xA0);             //发送EEPROM器件写入地址
    SI2CIsAck();               //等待应答
    SI2C_Send(addr>>8);          //发送高8位地址
    SI2CIsAck();               //等待应答
    SI2C_Send(addr%256);         //发送低8位地址
    SI2CIsAck();               //等待应答       
        for(i=0; i<size; i++){
      SI2C_Send(buff);      //发送数据
      SI2CIsAck();             //等待应答
    }
        SI2C_Stop();               //结束I2C通讯
    delay_us(4);
}

/*************************************************************
* 函数名:at24cxx_Page_Read(uint8_t addr)
* 输入参数:addr = 要读取的地址
* 输出参数:data = 读出的数据
*************************************************************/
uint8_t at24cxx_Page_Rea(uint16_t addr,uint8_t size)// 读指定起始地址Addr里的数据到buff
{
    uint8_t i,buff;
   
    SI2C_Start();                //开始I2C通讯
    SI2C_Send(0xA0);             //发送EEPROM器件地址
    SI2CIsAck();               //等待应答
    SI2C_Send(addr>>8);          //发送高8位地址
    SI2CIsAck();               //等待应答
    SI2C_Send(addr%256);         //发送低8位地址
    SI2CIsAck();               //等待应答

    SI2C_Start();
    SI2C_Send(0xA1);             //发送EEPROM器件读取地址
    SI2CIsAck();
    for(i=0; i<size; i++){
      buff = SI2C_Receive();//读一字节数据
      SI2CDoAck();
    }
        SI2C_Stop();
    delay_us(4);
    return *buff;
}

hujjj 发表于 2019-12-8 09:30:26

还希望您支持一下:
https://www.stmcu.org.cn/module/forum/thread-622444-1-1.html

zhy233090130 发表于 2019-12-10 09:30:46

hujjj 发表于 2019-12-8 09:30
还希望您支持一下:
https://www.stmcu.org.cn/module/forum/thread-622444-1-1.html

这两天准备调试I2C,谢谢分享

hujjj 发表于 2019-12-10 09:57:55

若调试不成功,则我再抽空在STM32F030开发板上测试,关键就是延时要符合要求,不同主频的单片机,延时会有差异,超出范围则会影响到I2C读写。
页: [1] 2
查看完整版本: 求个STM32F0 模拟I2C的程序例程