Simulate IIC communication protocol (stm32) (hardware iic will be added later)

1. Summary of basic knowledge of IIC.

1. IIC communication requires only two lines, SCL and SDA.

2. The data transmission rate of IIC is different for different ICs. The IIC data transmission rate is determined according to the delay function time of level maintenance.

3. The delay function of IIC can use the delay function. The delay function generally uses the system tick clock to generate delay, which is also a delay generated based on the Sysclk frequency bus. This delay is the same as the delay generated by the “__NOP();” instruction. “__NOP();” also relies on the Sysclk frequency to generate delay. Usage scenarios: The “__NOP();” instruction is an assembly instruction that generates a delay, which occupies the CPU. Short and precise delays can be used; longer and precise delays still require the use of system ticks. The clock timer implements delay.

4. The standard IIC transmission beat signal consists of 7 types: start signal, stop signal, generate ACK response signal, generate NACK response signal, wait for ACK response signal, receive 1byte byte signal, and send 1byte byte signal.

5. The standard IIC signal is divided into two forms: edge signal, rising edge or falling edge (start signal, stop signal). Level signal, high level or low level (generating ACK response signal, generating NACK response signal, waiting for ACK response signal, receiving 1byte byte signal, sending 1byte byte signal).

6. Among the above two types of signals, that is, among the 7 types of signals, the SDA signal must be valid when SCL is high level.

7. Based on the above 7 basic signals, different chips encapsulate different data sending and receiving functions. The following will briefly introduce the general data sending and receiving protocol forms. Most ic chips are the same.

Sending of ic data:

(1) Send the start bit.

(2) Send the write control byte. The last bit of the write control byte indicates “write”, and the other bits indicate the ID of the IC.

(3) Wait for the ACK response from IC.

(4) Send address bytes.

(5) Wait for the ACK response from IC.

(6) Send the written data bytes.

(7) Wait for the ACK response from IC.

(8) If a single byte is written, it can only be written once. The writing of Pag pages 5 and 6 can be done multiple times.

(9) Finally, send the stop bit to the IC.

Reception of ic data:

(1) Send the start bit.

(2) Send the write control byte. The last bit of the write control byte indicates “write”, and the other bits indicate the ID of the IC.

(3) Wait for the ACK response from IC.

(4) Write the high byte of the address (if it is 16-bit address data).

(5) Wait for the ACK response from IC.

(6) Write address low byte

(7) Wait for the ACK response from IC.

(8) Send the start bit.

(9) Send the read control byte. The last bit of the read control byte indicates “read”, and the other bits indicate the ID of the IC.

(10) Wait for the ACK response from IC.

(11) Receive data.

(12) The data has not been received completely, continue to receive, and send an ACK response signal.

(13) Receive data.

(14) After data reception is completed, a NACK response signal is sent.

(15) Send stop bit.

8. For stop signals, SCL must be kept high at the end; for other signals, SCL must be kept low at the end of the function.

2. IIC uses the configuration of pin configuration levels.

1. SDA GPIO input

static void i2c_sda_in(void)
{
    GPIO_InitTypeDef gpio_cfg;
    __HAL_RCC_GPIOB_CLK_ENABLE();
    gpio_cfg.Pin = bus_i2c->sda_pin;
    gpio_cfg.Mode = GPIO_MODE_INPUT;
// gpio_cfg.Pull = GPIO_PULLUP;
    HAL_GPIO_Init((GPIO_TypeDef*)bus_i2c->sda_port, & amp;gpio_cfg);
}

2. SDA’s GPIO output

static void i2c_sda_out(void)
{
    GPIO_InitTypeDef gpio_cfg;
    __HAL_RCC_GPIOB_CLK_ENABLE();
    gpio_cfg.Pin = bus_i2c->sda_pin;
    gpio_cfg.Mode = GPIO_MODE_OUTPUT_OD;
    gpio_cfg.Pull = GPIO_PULLUP;
    gpio_cfg.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init((GPIO_TypeDef*)bus_i2c->sda_port, & amp;gpio_cfg);
}

3. SCL GPIO output

static void i2c_scl_out(void)
{
    GPIO_InitTypeDef gpio_cfg;
    __HAL_RCC_GPIOB_CLK_ENABLE();
    gpio_cfg.Pin = bus_i2c->scl_pin;
    gpio_cfg.Mode = GPIO_MODE_OUTPUT_OD;
    gpio_cfg.Pull = GPIO_PULLUP;
    gpio_cfg.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init((GPIO_TypeDef*)bus_i2c->scl_port, & amp;gpio_cfg);
}

4. SDA output high and low levels

static void i2c_sda_write(unsigned char value)
{
    HAL_GPIO_WritePin((GPIO_TypeDef*)bus_i2c->sda_port, bus_i2c->sda_pin, value?GPIO_PIN_SET:GPIO_PIN_RESET);
}

5. SCL output high and low levels

static void i2c_scl_write(unsigned char value)
{
    HAL_GPIO_WritePin((GPIO_TypeDef*)bus_i2c->scl_port, bus_i2c->scl_pin, value?GPIO_PIN_SET:GPIO_PIN_RESET);
}

6. SDA level reading

static unsigned int i2c_sda_read(void)
{
  return HAL_GPIO_ReadPin((GPIO_TypeDef*)bus_i2c->sda_port, bus_i2c->sda_pin);
}

3. IIC basic signal

1. Start signal (edge signal)

The SDA and SCL pin signals should both be high at the beginning, and SCL should be kept low after the start signal.

When SCL is high, SDA generates a falling edge.

(1) SAD pull high + delay function

(2) SCL pull high + delay function

(3) SDA pull low + delay function

(4) SCL pull low + delay function

static void i2c_start(void)
{
    i2c_sda_out();
    i2c_sda_write(1);
    i2c_delay();
    i2c_scl_write(1);
    i2c_delay();
    i2c_sda_write(0);
    i2c_delay();
    i2c_scl_write(0);
    i2c_delay();
}

2. Stop signal (edge signal)

When it ends, the level of SCL must be low, but the level of SDA is uncertain, so the SDA level should be pulled low first.

When SCL is high, SDA generates a rising edge.

(1) SDA pull low + delay function

(2) SCL pull high + delay function

(3) SDA pull high + delay function

static void i2c_stop(void)
{
    i2c_sda_out();
    i2c_sda_write(0);
    i2c_delay();
    i2c_scl_write(1);
    i2c_delay();
    i2c_sda_write(1);
    i2c_delay();
}

3. Delay function

The delay function uses the assembly instruction “__NOP()”. Please refer to other articles for specific knowledge.

static void i2c_delay(void)
{
    __NOP();
}

4. Send ACK response signal (level signal)

The SCL level must be low, and the SDA level is unknown. When SCL is high level, SDA is low level, which is the ACK response signal. But this signal must be sent after the data is received to be effective.

(1) SDA pull low + delay function

(2) SCL pull high + delay function

(3) SCL pull low + delay function

static void i2c_write_ack(void)
{
    i2c_sda_out();
    i2c_sda_write(0);
    i2c_delay();
    i2c_scl_write(1);
    i2c_delay();
    i2c_scl_write(0);
    i2c_delay();
}

5. Send NACK response signal (level signal)

The SCL level must be low level, and the state of SDA is determined. When SCL is high, SDA is high. This signal can only be sent after reading the data.

(1) SDA pull high + delay function

(2) SCL pull high + delay function

(3) SCL pull low + delay function

static void i2c_write_nack(void)
{
    i2c_sda_out();
    i2c_sda_write(1);
    i2c_delay();
    i2c_scl_write(1);
    i2c_delay();
    i2c_scl_write(0);
    i2c_delay();
}

6. Waiting for ACK response signal (level signal)

SCL must be high level, and the SDA level is uncertain. When SCL is high level, read the SDA level. When SDA is read as low level, it means that the ACK signal is received.

(1) SCL pull high + delay function

(2) Read SDA level + delay function

(3) SCL pull low + delay function

static unsigned char i2c_read_ack(void)
{
    unsigned char level = 0;
    i2c_sda_in();
    i2c_scl_write(1);
    i2c_delay();
    if(i2c_sda_read())
        level = 1;
    i2c_scl_write(0);
    i2c_delay();
    return level;
}

7. Send data (level signal)

The SDA level is valid only when SCL is high. When SCL is high, the SDA level must be kept stable, so before the SCL level changes, SDA should change first.

(1) SDA level change + delay function (to eliminate the write data bit setting level)

(2) SCL pull high + delay function

(3) SCL pull low + delay function

static void i2c_write_byte(unsigned short data)
{
    int i;
    unsigned char temp = (unsigned char)(data & 0xFF);
    i2c_sda_out();
    for(i=0;i<8;i + + )
    {
        if(temp & 0x80)
        i2c_sda_write(1);
        else
        i2c_sda_write(0);
        temp <<= 1;
        i2c_delay();
        i2c_scl_write(1);
        i2c_delay();
        i2c_scl_write(0);
        i2c_delay();
    }
}

8. Receive data (level signal)

Data reception and data transmission are the same. SDA is valid when SCL is high level, so when SCL is high level, read the level status of the SDA pin.

(1) SCL pull high + delay function

(2) Read SDA level + delay function

(3) SCL pull low + delay function

static unsigned char i2c_read_byte(void)
{
    int i;
    unsigned char temp = 0;
    i2c_sda_in();
    for(i=0;i<8;i + + )
    {
i2c_scl_write(1);
        i2c_delay();
        temp <<= 1;
        if(i2c_sda_read())
        temp |= 0x01;
        i2c_scl_write(0);
        i2c_delay();
    }
    return temp;
}

4. Write data command and read data command process for IC

The following is the operation flow of data writing and reading for the general situation of IC.

1. IIC writes data to IC (single byte write)

The figure below shows the basic logic of IIC writing data to the IC chip. Except for the two edge signals (start and stop signals), there is no need to wait for the IC to respond with ACK. Writing data or address requires waiting for the IC’s ACK response to confirm that the IC has received the data.

(1) Write start.

(2) Write “ic write control byte”.

(3) Wait for ACK response.

(4) Write the register address.

(5) Wait for ACK response. (If it does not wait, write the stop bit and return).

(6) Write the data to be written (you can use a loop to write multiple bytes).

(7) Every time you write data, you need to wait for an ACK response.

(8) Write the stop bit.

uint8_t PCT2075DP_Write(uint8_t reg, void* data,uint8_t size)
{
int i;
uint8_t* pData = (uint8_t*)data;
i2c_scl_out();
i2c_start();
i2c_write_byte(0x90);
if(i2c_read_ack)
{
i2c_stop();
return -1;
}
i2c_write_byte(reg);
if(i2c_read_ack)
{
i2c_stop();
return -3;
}
for(i=0; i<size; i + + )
{
i2c_write_byte(pData[i]);
if(i2c_read_ack)
{
i2c_stop();
return i;
}
}
i2c_stop();
return i;
}

2. IIC reads data from IC (single byte readout)

In addition to the two edge signals (start and stop signals), the IIC does not need to give the IC an ACK response signal when reading data from the IC. When reading data, each time a byte is read, an ACK response needs to be sent to the IC, which means that the data has been received and data needs to continue to be received; if the last data is received and the data is no longer being received, Then respond to the NACK signal.

(1) Write the start signal bit.

(2) Write “ic write control byte”.

(3) Wait for the ACK response from IC.

(4) Write the high byte of the address (if the address is 16 bits, write the high byte).

(5) Wait for the ACK response from IC.

(6) Write the low byte of the address.

(7) Wait for the ACK response from IC.

(8) Write the start signal (this time it restarts the IIC bus).

(9) Write “ic read control byte”.

(10) Wait for the ACK response from IC.

(11) Read data bytes.

(12) Write ACK response signal (indicating continued reading).

(13) Read data bytes.

(14) Write NACK response signal (indicating the end of data reading).

(15) Write stop bit.

uint8_t PCT2075DP_Read(uint8_t reg, void* data,uint8_t size, uint8_t poit)
{
int i;
uint8_t* pData = (uint8_t*)data;
i2c_scl_out();
i2c_start();
i2c_write_byte(0x90);
if(i2c_read_ack())
{
i2c_stop();
return -1;
}
i2c_write_byte(0x00);
if(i2c_read_ack())
{
i2c_stop();
return -3;
}
\t
i2c_start();
i2c_write_byte(0x91);
if(i2c_read_ack())
{
i2c_stop();
return -4;
}
for(i=0; i<size; i + + )
{
pData[i] = i2c_read_byte(); //Reply to i2c_write_ack() when you need to continue reading.
if(i==size-1)
{
i2c_write_nack();
}else
{
i2c_write_ack();
}
}
i2c_stop();
return i;
}

Here is the overall code:

#ifndef __MYIIC_H__
#define __MYIIC_H__

#include "stm32l0xx_hal.h"
#include "stdint.h"
#include <stdio.h>
#include "delay.h"
#include "485.h"

typedef struct sIIC_IO {
    unsigned int scl_port;
    unsigned int scl_pin;
    unsigned int sda_port;
    unsigned int sda_pin;
}g_tIIC_IO;


extern void myTest(float *pvalue);
extern int pct7075_read(float *pvalue);
#endif
#include "Myiic.h"
#include 
/* Simulate IIC, 7 functions.
 *(1) When the iic function sends data, pay attention to the compatibility of how many bits are sent.
 *(2) The iic function sends two bytes or one byte address.
 *(3) The data level of SDA is only valid when SCL is high level.
 *(4) iic function: start signal, stop signal, generate ACK response, generate NACK response, wait for ACK response, receive data, send data
 *(5) Using object-oriented thinking, the structure package simulates IIC using ports and pins
 *(6) Pin output initialization, function of pin level change, and structure packaging management.
 */



g_tIIC_IO i2c1 = {
    .scl_port = (unsigned int)GPIOB,
    .scl_pin = (unsigned int)GPIO_PIN_6,
    .sda_port = (unsigned int)GPIOB,
    .sda_pin = (unsigned int)GPIO_PIN_7
};

g_tIIC_IO *bus_i2c = & amp;i2c1;

static void i2c_delay(void)
{
    __NOP();
}

static void i2c_sda_out(void)
{
    GPIO_InitTypeDef gpio_cfg;
    __HAL_RCC_GPIOB_CLK_ENABLE();
    gpio_cfg.Pin = bus_i2c->sda_pin;
    gpio_cfg.Mode = GPIO_MODE_OUTPUT_OD;
    gpio_cfg.Pull = GPIO_PULLUP;
    gpio_cfg.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init((GPIO_TypeDef*)bus_i2c->sda_port, & amp;gpio_cfg);
}

static void i2c_sda_write(unsigned char value)
{
    HAL_GPIO_WritePin((GPIO_TypeDef*)bus_i2c->sda_port, bus_i2c->sda_pin, value?GPIO_PIN_SET:GPIO_PIN_RESET);
}

static void i2c_sda_in(void)
{
    GPIO_InitTypeDef gpio_cfg;
    __HAL_RCC_GPIOB_CLK_ENABLE();
    gpio_cfg.Pin = bus_i2c->sda_pin;
    gpio_cfg.Mode = GPIO_MODE_INPUT;
// gpio_cfg.Pull = GPIO_PULLUP;
    HAL_GPIO_Init((GPIO_TypeDef*)bus_i2c->sda_port, & amp;gpio_cfg);
}

static unsigned int i2c_sda_read(void)
{
  return HAL_GPIO_ReadPin((GPIO_TypeDef*)bus_i2c->sda_port, bus_i2c->sda_pin);
}

static void i2c_scl_out(void)
{
    GPIO_InitTypeDef gpio_cfg;
    __HAL_RCC_GPIOB_CLK_ENABLE();
    gpio_cfg.Pin = bus_i2c->scl_pin;
    gpio_cfg.Mode = GPIO_MODE_OUTPUT_OD;
    gpio_cfg.Pull = GPIO_PULLUP;
    gpio_cfg.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init((GPIO_TypeDef*)bus_i2c->scl_port, & amp;gpio_cfg);
}

static void i2c_scl_write(unsigned char value)
{
    HAL_GPIO_WritePin((GPIO_TypeDef*)bus_i2c->scl_port, bus_i2c->scl_pin, value?GPIO_PIN_SET:GPIO_PIN_RESET);
}

static void i2c_start(void)
{
    i2c_sda_out();
    i2c_sda_write(1);
    i2c_delay();
    i2c_scl_write(1);
    i2c_delay();
    i2c_sda_write(0);
    i2c_delay();
    i2c_scl_write(0);
    i2c_delay();
}

static void i2c_stop(void)
{
    i2c_sda_out();
    i2c_sda_write(0);
    i2c_delay();
    i2c_scl_write(1);
    i2c_delay();
    i2c_sda_write(1);
    i2c_delay();
}

static void i2c_write_nack(void)
{
    i2c_sda_out();
    i2c_sda_write(1);
    i2c_delay();
    i2c_scl_write(1);
    i2c_delay();
    i2c_scl_write(0);
    i2c_delay();
}

static void i2c_write_ack(void)
{
    i2c_sda_out();
    i2c_sda_write(0);
    i2c_delay();
    i2c_scl_write(1);
    i2c_delay();
    i2c_scl_write(0);
    i2c_delay();
}

static unsigned char i2c_read_ack(void)
{
    unsigned char level = 0;
    i2c_sda_in();
    i2c_scl_write(1);
    i2c_delay();
    if(i2c_sda_read())
        level = 1;
    i2c_scl_write(0);
    i2c_delay();
    return level;
}

static void i2c_write_byte(unsigned short data)
{
    int i;
    unsigned char temp = (unsigned char)(data & 0xFF);
    i2c_sda_out();
    for(i=0;i<8;i + + )
    {
        if(temp & 0x80)
        i2c_sda_write(1);
        else
        i2c_sda_write(0);
        temp <<= 1;
        i2c_delay();
        i2c_scl_write(1);
        i2c_delay();
        i2c_scl_write(0);
        i2c_delay();
    }
}

static unsigned char i2c_read_byte(void)
{
    int i;
    unsigned char temp = 0;
    i2c_sda_in();
    for(i=0;i<8;i + + )
    {
i2c_scl_write(1);
        i2c_delay();
        temp <<= 1;
        if(i2c_sda_read())
        temp |= 0x01;
        i2c_scl_write(0);
        i2c_delay();
    }
    return temp;
}

/* The above is the standard operation function of iic */
/****************************************************** ****************/
/*
 *address: address
 *reg: register pointer instruction
 *
 */

uint8_t PCT2075DP_Write(uint8_t reg, void* data,uint8_t size)
{
int i;
uint8_t* pData = (uint8_t*)data;
i2c_scl_out();
i2c_start();
i2c_write_byte(0x90);
if(i2c_read_ack)
{
i2c_stop();
return -1;
}
i2c_write_byte(reg);
if(i2c_read_ack)
{
i2c_stop();
return -3;
}
for(i=0; i<size; i + + )
{
i2c_write_byte(pData[i]);
if(i2c_read_ack)
{
i2c_stop();
return i;
}
}
i2c_stop();
return i;
}



uint8_t PCT2075DP_Read(uint8_t reg, void* data,uint8_t size, uint8_t poit)
{
int i;
uint8_t* pData = (uint8_t*)data;
i2c_scl_out();
i2c_start();
i2c_write_byte(0x90);
if(i2c_read_ack())
{
i2c_stop();
return -1;
}
i2c_write_byte(0x00);
if(i2c_read_ack())
{
i2c_stop();
return -3;
}
\t
i2c_start();
i2c_write_byte(0x91);
if(i2c_read_ack())
{
i2c_stop();
return -4;
}
for(i=0; i<size; i + + )
{
pData[i] = i2c_read_byte(); //Reply to i2c_write_ack() when you need to continue reading.
if(i==size-1)
{
i2c_write_nack();
}else
{
i2c_write_ack();
}
}
i2c_stop();
return i;
}






void myTest(float *pvalue)
{
uint16_t temp,data=0;
int retry = 3;
float value;
/* run in normal mode */
PCT2075DP_Write(0x01, &data, 1);
while(retry --)
{
if (PCT2075DP_Read(0x00, &temp, 2, 0) == 2)
break;
}
temp = ((temp & amp;0xFF00)>>8)|((temp & amp;0x00FF)<<8); // The position of the high 8-bit and low-order 8 bits of the sensor reading data is reversed, and the return value is directly in the short type
value = temp;
temp=0;
value = value / 256;
*pvalue= value;
}



int i2c_write(g_tIIC_IO* bus, unsigned char address, unsigned short reg, void* pbuf, int size, unsigned char reg_16bit)
{
    int i = 0;
    unsigned char* buf_ptr;
    if(pbuf == NULL) return i;
    buf_ptr = (unsigned char*)pbuf;
    bus_i2c = bus;
    i2c_scl_out();
    i2c_start();
    i2c_write_byte(address | 0);
    if(i2c_read_ack()) {
        i2c_stop();
        return -1;
    }
    if(reg_16bit) {
        i2c_write_byte(reg >> 8);
        if(i2c_read_ack()) {
        i2c_stop();
        return -2;
        }
    }
    i2c_write_byte(reg);
    if(i2c_read_ack()) {
        i2c_stop();
        return -3;
    }
    for(i=0;i> 8);
        if(i2c_read_ack()) {
        i2c_stop();
        return -2;
        }
    }
    i2c_write_byte(reg);
    if(i2c_read_ack()) {
        i2c_stop();
        return -3;
    }
    i2c_start();
    i2c_write_byte(address | 1);
    if(i2c_read_ack()) {
        i2c_stop();
        return -4;
    }
    for(i=0;i>8)|((temp & amp;0x00FF)<<8); // The position of the high 8-bit and low-order 8 bits of the sensor reading data is reversed, and the return value is directly in the short type
    value = temp;
    value = value / 256;
    if(pvalue != 0) *pvalue = value;
    return 0;
}

The timing of the communication information of the stm32 hardware IIC is not easy to adjust, so I won’t spend time studying it here.