打印
[MM32软件]

IIC协议通信解析

[复制链接]
760|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
rosemoore|  楼主 | 2023-11-18 23:30 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式


// 该代码用于测试没问题

#define I2C_SCL_PORT  GPIOB
#define I2C_SCL_Pin   GPIO_Pin_8

#define I2C_SDA_PORT  GPIOB
#define I2C_SDA_Pin   GPIO_Pin_9

#define OUTPUT 0
#define INPUT 1

static void I2C_Tail(void); //发送结束标志
static void I2C_Head(unsigned short Addrs); // 发送起始标志

static void hal_I2C_Config(void); // 模拟I2C配置函数
static void hal_I2C_SCL_SET(unsigned char i);  // 设置时钟引脚
static void hal_I2C_SDA_SET(unsigned char i);  // 设置数据引脚
static void hal_I2C_Start(void);  // 起始标志
static void hal_I2C_Stop(void);   // 结束标志
static void I2C_Delay(unsigned short t);
static void I2C_ACK(void);   // 有ACK应答
static void I2C_NOACK(void); // 无ACK应答
static void I2C_SendByte(unsigned char data);   // 写一个byte数据时序
static void I2C_WriteByte(unsigned short Addr, unsigned char data); // 发送一个byte
static unsigned char I2C_RecvByte(void); // 读一个字节时序
static unsigned char I2C_WaitAck(void);  // 返回0,无应答数据 。返回1,有应答数据

void hal_I2C_SDA_IO_SET(unsigned char IOMode); // OUTPUT 或者 INPUT
unsigned char hal_IC_SDA_INPUT(void);
unsigned char I2C_ReadByte(unsigned short Addr); // 读一个字节数据


void hal_eeprom_Init(void)
{
  hal_I2C_Config();
}

unsigned char I2C_ReadByte(unsigned short Addr)
{
  unsigned char dat;
        hal_I2C_Start();
        I2C_SendByte(0xA0);
        I2C_WaitAck();
       
        I2C_SendByte((Addr>>8)&0xFF);
        I2C_WaitAck();
       
        I2C_SendByte(Addr&0xFF);
        I2C_WaitAck();
       
        hal_I2C_Start();
        I2C_SendByte(0xA1); // 发送 1010 0001 最低位的1代表从该芯片读取数据
        I2C_WaitAck();
       
        dat = I2C_RecvByte();
        I2C_NOACK();
        hal_I2C_Stop();
        return dat;
}

static unsigned char I2C_RecvByte(void)
{
   unsigned char i = 0;
         unsigned char RecvByte = 0;
       
   hal_I2C_SCL_SET(0);
         I2C_Delay(1);
         hal_I2C_SDA_SET(1);
         hal_I2C_SDA_IO_SET(INPUT);
         
         for(; i < 8; i++)
        {
             RecvByte<<=1;
                  I2C_Delay(1);
                  hal_I2C_SCL_SET(1);
                  I2C_Delay(1);
                  if(hal_IC_SDA_INPUT()) {
                          RecvByte |= 0x01;
                        }
                        hal_I2C_SCL_SET(0);
        }
        hal_I2C_SDA_IO_SET(OUTPUT);
        I2C_Delay(1);
        return RecvByte;
}

void hal_I2C_Read( unsigned short Addrs, unsigned char *pBuf, unsigned short Len)
{
  unsigned short i = 0;
       
        I2C_Head(Addrs);
       
        hal_I2C_Start();
        I2C_SendByte(0xA1); // 发送 1010 0001 最低位的1代表从该芯片读取数据
        I2C_WaitAck();
       
        for( i = 0; i < Len; i++)
        {
          pBuf[i] = I2C_RecvByte();
                if(i == Len - 1) I2C_NOACK();
                else I2C_ACK();
        }
       
        hal_I2C_Stop();
}

static void I2C_WriteByte(unsigned short Addr, unsigned char data)
{
  hal_I2C_Start(); // 发送有效数据起始标志
       
        // 该芯片最低位的一个bit位代表读/写:0 代表写入数据 || 1 代表读出数据
        I2C_SendByte(0xA0); // 发送需要通信的硬件地址(对应的硬件手册上有!!!!!)
        I2C_WaitAck();
       
        I2C_SendByte((Addr>>8)&0xFF); // 发送写入的地址(高字节地址)
        I2C_WaitAck();
       
        I2C_SendByte(Addr&0xFF);    // 发送写入的地址(低字节地址)
        I2C_WaitAck();
       
        I2C_SendByte(data);
        I2C_WaitAck();
       
        hal_I2C_Stop();  // 发送有效数据结束标志
        I2C_Delay(20000);
}


static unsigned char I2C_WaitAck(void)
{
  hal_I2C_SDA_SET(1);
        hal_I2C_SDA_IO_SET(INPUT);  // 设置SDA引脚为输入引脚
        hal_I2C_SCL_SET(1);
        I2C_Delay(1);
        if(hal_IC_SDA_INPUT()) { // 该引脚为高电平,代表无应答
                hal_I2C_SDA_IO_SET(OUTPUT);  // 设置SDA引脚为输出引脚
          return 0;
        }
       
        hal_I2C_SCL_SET(0);
        hal_I2C_SDA_IO_SET(OUTPUT);
        I2C_Delay(1);
        return 1;
}

static void I2C_Head(unsigned short Addrs)
{
    hal_I2C_Start(); // 发送有效数据起始标志
       
          // 该芯片最低位的一个bit位代表读/写:0 代表写入数据 || 1 代表读出数据
          I2C_SendByte(0xA0); // 发送需要通信的硬件地址(对应的硬件手册上有!!!!!)
          I2C_WaitAck();
       
          I2C_SendByte((Addrs>>8)&0xFF); // 发送写入的地址(高字节地址)
          I2C_WaitAck();
       
          I2C_SendByte(Addrs&0xFF);    // 发送写入的地址(低字节地址)
          I2C_WaitAck();
}

static void I2C_Tail(void)
{
  hal_I2C_Stop();  // 发送有效数据结束标志
        I2C_Delay(20000);
}

void hal_I2C_Write(unsigned short Addrs, unsigned char* pBuf, unsigned short Len)
{
        unsigned short tmp = 0;
        unsigned short i = 0, j = 0;
        unsigned short page = 0;
        unsigned short size;
       
        if(Addrs%EEPROM_PAGE_SIZE)
        {
          tmp = EEPROM_PAGE_SIZE-(Addrs%EEPROM_PAGE_SIZE); // 拿到当前页还有多少字节可写入
                if(tmp > Len)
                {
             tmp = Len;        // 后续写入操作,需要tmp来做循环因子
                }
        }
        // 先将数据写入当前页
        if(tmp)
        {
                I2C_Head(Addrs);
                // 正式写入数据
                for(i = 0; i < tmp; i++)
                {
                  I2C_SendByte(pBuf[i]);
                        I2C_WaitAck();
                }
                I2C_Tail();
        }
       
        Len -= tmp;   // 将已经发送过的数据个数减掉
        Addrs += tmp; // 将写入的地址更新
       
        page = Len/EEPROM_PAGE_SIZE; // int整形除法,只会得到整数
        size = Len%EEPROM_PAGE_SIZE; // 不足一页还有几个字节
       
        for(i = 0; i < page; i++)
        {
          I2C_Head(Addrs);
               
                for(j = 0; j < EEPROM_PAGE_SIZE; j++)
                {
                  I2C_SendByte(pBuf[tmp + j]);
                        I2C_WaitAck();
                }
                I2C_Tail();
                Addrs += EEPROM_PAGE_SIZE;
                tmp += EEPROM_PAGE_SIZE;
        }
       
        if(size)
        {
          I2C_Head(Addrs);
               
                for(i = 0; i < size; i++)
                {
                  I2C_SendByte(pBuf[tmp + i]);
                        I2C_WaitAck();
                }
                I2C_Tail();
        }
       
}

static void I2C_SendByte(unsigned char data)
{
  unsigned char i = 0;
        for(; i < 8; i++)
        {
                hal_I2C_SCL_SET(0);
                I2C_Delay(1);
          if(data&0x80){
                  hal_I2C_SDA_SET(1);
                } else {
                  hal_I2C_SDA_SET(0);
                }
                I2C_Delay(1);
                hal_I2C_SCL_SET(1);
                I2C_Delay(1);
                data<<=1;
        }
       
        hal_I2C_SCL_SET(0);
  I2C_Delay(1);
        hal_I2C_SDA_SET(1);
  I2C_Delay(1);
}


static void I2C_ACK(void)
{
   hal_I2C_SCL_SET(0);
         I2C_Delay(1);
         hal_I2C_SDA_SET(0); // SDA数据线置低,进行数据位应答。
         I2C_Delay(1);
         hal_I2C_SCL_SET(1);
         I2C_Delay(1);
         hal_I2C_SCL_SET(0);
         I2C_Delay(1);
}


static void I2C_NOACK(void)
{
   hal_I2C_SCL_SET(0);
         I2C_Delay(1);
         hal_I2C_SDA_SET(1); // SDA数据线置高,不进行数据应答。
         I2C_Delay(1);
         hal_I2C_SCL_SET(1);
         I2C_Delay(1);
         hal_I2C_SCL_SET(0);
         I2C_Delay(1);
}

static void hal_I2C_Start(void)
{
  hal_I2C_SDA_SET(1);
        I2C_Delay(1);
        hal_I2C_SCL_SET(1);
        I2C_Delay(1);
        hal_I2C_SDA_SET(0);
        I2C_Delay(1);
}

static void hal_I2C_Stop(void)
{
  hal_I2C_SDA_SET(0);
        I2C_Delay(1);
        hal_I2C_SCL_SET(1);
        I2C_Delay(1);
        hal_I2C_SDA_SET(1);
        I2C_Delay(1);
}


static void hal_I2C_Config(void)
{
   GPIO_InitTypeDef GPIO_Struct;
         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
         
         GPIO_Struct.GPIO_Mode = GPIO_Mode_Out_OD;
         GPIO_Struct.GPIO_Speed = GPIO_Speed_50MHz;
         GPIO_Struct.GPIO_Pin = I2C_SCL_Pin | I2C_SDA_Pin;
         GPIO_Init(GPIOB, &GPIO_Struct);
       
         hal_I2C_SDA_SET(1);
         hal_I2C_SCL_SET(1);
       
}


void hal_I2C_SDA_IO_SET(unsigned char IOMode)
{
  if(0 == IOMode) {
         GPIO_InitTypeDef GPIO_Struct;
         GPIO_Struct.GPIO_Mode = GPIO_Mode_Out_OD;
         GPIO_Struct.GPIO_Speed = GPIO_Speed_50MHz;
         GPIO_Struct.GPIO_Pin =  I2C_SDA_Pin;
         GPIO_Init(GPIOB, &GPIO_Struct);
        }
        if(1 == IOMode) {
         GPIO_InitTypeDef GPIO_Struct;
         GPIO_Struct.GPIO_Mode = GPIO_Mode_IPU;
         GPIO_Struct.GPIO_Speed = GPIO_Speed_50MHz;
         GPIO_Struct.GPIO_Pin =  I2C_SDA_Pin;
         GPIO_Init(GPIOB, &GPIO_Struct);
        }
}


static void hal_I2C_SDA_SET(unsigned char i)
{
  if(i) {
           GPIO_SetBits(I2C_SDA_PORT,I2C_SDA_Pin);
         } else {
           GPIO_ResetBits(I2C_SDA_PORT,I2C_SDA_Pin);
         }
}


static void hal_I2C_SCL_SET(unsigned char i)
{
   if(i) {
           GPIO_SetBits(I2C_SCL_PORT,I2C_SCL_Pin);
         } else {
           GPIO_ResetBits(I2C_SCL_PORT,I2C_SCL_Pin);
         }
}

static void I2C_Delay(unsigned short t)
{
  unsigned short i=50,j,c;
   c = t;
   for(j=0; j<c; j++)
   {
           while(i)
           {
                                i--;
           }
        }
}

unsigned char hal_IC_SDA_INPUT(void)
{
  return GPIO_ReadInputDataBit(I2C_SDA_PORT, I2C_SDA_Pin);
}


使用特权

评论回复
沙发
单片小菜| | 2023-11-24 17:16 | 只看该作者
我还是认为软件的I2C更香,至少通用性更强一些。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

42

主题

1160

帖子

2

粉丝