Do you know? Use GPIO to simulate I2C communication on C2000
[Copy link]
As a simple digital communication method, I2C only needs two data lines to complete the communication between the host (Master) and the slave (Slave) at a short distance, saving MCU pins and additional logic chips, simplifying the difficulty of PCB layout, and therefore has been widely used. In recent years, TI has also launched more and more chips that support I2C communication functions, which greatly simplifies the communication between the chip and the MCU and facilitates the design of the system.
However, in actual applications, for applications with low performance requirements, low-end master MCUs with simple peripherals are usually selected, which may not have an I2C interface. For such applications, I2C simulation can be performed through the MCU's IO port to establish communication with the controlled device to achieve the purpose of sending control instructions and reading internal registers. Even if the I2C interface is missing, the full functions of the device can be fully utilized.
This article provides a solution based on C2000 to use GPIO to simulate I2C to control the controlled chip, and comes with a complete routine. It has reference significance for most chips that use the standard I2C communication protocol and some chips that use SMBus. Solutions based on other MCUs can also refer to this routine for porting.
1. I2C communication protocol and GPIO simulation
The I2C bus consists of two bidirectional signal lines, the data line (SDA) and the clock line (SCL), which are pulled up by resistors to achieve switching between high and low levels and exchange data between devices. The operating voltage range allowed by I2C is relatively wide, and the typical voltage reference is +3.3V or +5V. Common I2C bus rates are divided into the following modes: standard mode (100Kbit/s), fast mode (400Kbit/s) and high-speed mode (3.4Mbit/s), etc. The figure shows a typical I2C connection diagram:
Figure 1 I2C connection diagram
Figure 2 is a typical I2C communication frame format diagram. A complete frame of data transmission mainly includes the start bit, address bit, read/write bit, ACK/NCK bit, data bit, etc. The following is a brief explanation of each part and how to implement it through C2000.
Figure 2 I2C connection diagram
1.1 Start and end instructions
When a device is configured as a master on the I2C bus, the device can send start and end signals to initiate or end an I2C communication. The bus level diagram is shown in Figure 2.
- Start signal: While SCL is at a high level, SDA changes from a high level to a low level.
- End signal: While SCL is at a high level, SDA changes from a low level to a high level.
Figure 3 I2C communication start and end signals
In C2000, the start signal can be sent through the following code. SCL and SDA represent the SDA and SCL buses simulated by C2000 GPIO respectively. For specific definitions, please refer to the routine section.
- voidI2C_Start(void)
- {
- Delay(I2CDelay);
- SCL_High();//SettheSCL
- SDA_High();//SettheSDA
- Delay(I2CDelay);
- SDA_Low();//CleartheSDAwhileSCLishighindicatesthestartsignal
- Delay(I2CDelay);
- SCL_Low();//CleartheSCLtogetreadytotransmit
- }
You can refer to the following code to send the end signal:
- voidI2C_Finish(void)
- {
- SDA_Low();//CleartheSDA
- SCL_Low();//CleartheSCL
- Delay(I2CDelay);
- SCL_High();//SettheSCL
- Delay(I2CDelay);
- SDA_High();//SettheSDAwhileSCLishighindicatesthefinishsignal
- }
1.2 Data bits and address bits
The data bits of I2C communication are usually composed of 1-8 data. During the period when the host sends and reads data, the SCL bus clock signal is still sent by the host, and each SCL high level period corresponds to one bit of data. During the SCL high level period, the data on SDA should be kept correct, so in actual applications, the high level pulse width of SDA is usually made wider than SCL.
The sending of address bits is similar to that of data bits. In actual operation, the 7-bit address bits + 1-bit read/write bit of the device can be sent as an 8-bit byte. Taking BQ25703A as an example, the default device address is 0x6B (7bit). When performing a read operation, the byte to be sent is 0xD7 (1101011b+1b); when performing a write operation, the overall byte to be sent is 0XD6 (1101011b+0b).
The sending of data bits and address bits can refer to the following implementation method of sending an 8-bit byte:
- voidI2C_Send_Byte(unsignedchartxd)
- {
- intt;
- SDA_Output();//Config SDA GPIO as output
- SCL_Low();//CleartheSCLtogetreadytotransmit
- txd&=0X00FF;// Getthe lower8bits
- for(t=0;t<8;t++)
- {
- SDA_Data_Register=(txd&0x80)>>7;//SendtheLSB
- txd<<=1;
- Delay(I2CDelay/2);
- SCL_High();//SettheSCL
- Delay(I2CDelay);
- SCL_Low();//CleartheSCL
- Delay(I2CDelay/2);
- }
- }
1.3 ACK/NACK Command
The Acknowledge (ACK) and Not Acknowledge (NACK) instructions usually occur after a byte is sent, marking the success or failure of a byte transmission. It is particularly important to note that even during the ACK clock cycle, the SCL bus clock signal is generated by the host.
ACK: When a transmission is completed, the master releases the SDA bus. If the transmission is successful, the slave pulls down the SDA bus in the 9th clock cycle and maintains it during the entire high level period.
NACK: When a transmission is completed, the host releases the SDA bus. If the transmission fails, SDA remains at a high level during the 9th clock cycle.
In communication, the MCU as the host usually only needs to implement the sending of NACK and the waiting of ACK signal. For details, please refer to the following program:
- voidI2C_NAck(void)
- {
- SCL_Low(); //CleartheSCLtogetreadytotransmit
- SDA_Low();//CleartheSDA
- Delay(I2CDelay);
- SCL_High();//SettheSCL
- Delay(I2CDelay);
- SCL_Low(); //CleartheSCL
- Delay(I2CDelay);
- }
- Uint16I2C_Wait_Ack(void)
- {
- intErrTime=0;
- intReadAck=0;
- SDA_Input();//Config SDAGPIO as Input
- Delay(I2CDelay);
- SCL_High();//SettheSCLandwaitforACK
- while(1)
- {
- ReadAck=SDA_Data_Register;//Readtheinput
- if(ReadAck)
- {
- ErrTime++;
- if(ErrTime>ErrLimit)
- {
- //Errorhandler:Seterrorflag,retryorstop.
- //Definebyusers
- return1;
- }
- }
- if(ReadAck==0)//Receive a ACK
- {
- Delay(I2CDelay);
- SCL_Low();//CleartheSCLforNextTransmit
- return0;
- }
- }
- }
Based on the above basic I2C communication operations, you can send a complete I2C data frame, implement basic I2C communication functions, and build the foundation for using GPIO ports to simulate I2C for chip control.
2. Writing and reading I2C analog device registers
After building the basic I2C communication function, you can use I2C communication to control the Slave or read the status, which is essentially reading and writing the internal registers of the Slave. The following takes a typical 8-bit register chip with I2C function as an example to introduce how to use the basic I2C simulation function mentioned above to write and read the internal registers of the chip.
I2C write: To perform an I2C write, the MCU first sends a start bit and an 8-bit hardware write address consisting of a 7-bit slave address bit and a read/write bit (0b), and then releases the SDA bus. If the address is correct, the slave will pull down SDA to send an ACK. After that, the MCU sends the address to be written to the register and waits for the ACK returned by the slave. After the response, the MCU sends 8 bits of data and sends a stop bit after receiving the ACK response.
Figure 4 I2C write register frame format
The specific implementation method can refer to the following code:
- voidI2C_Write_Register(unsignedcharDevice,unsignedcharRegister,unsignedcharValue)
- {
- I2C_Start();
- I2C_Send_Byte(Device);//Sendthedeviceaddress
- I2C_Wait_Ack(); //Waitfortheacksignal
- I2C_Send_Byte(Register);//Sendtheregisteraddress
- I2C_Wait_Ack();//Waitfortheacksignal
- I2C_Send_Byte(Value);//Sendregistervalue
- I2C_Wait_Ack();
- I2C_Finish();
- }
I2C read: To read the internal register of the Slave, the MCU must first communicate with the Slave to inform the Slave of the target register to be read. The process is similar to the write operation. The MCU first sends the start bit, the 8-bit Slave write address, and sends the 8-bit target register address after the ACK signal. After the Slave responds to the address, the MCU resends the start bit and the 8-bit Slave read address (7-bit address + 1b). After the ACK response, the MCU releases the SDA bus and continues to send the SCL clock signal to read the content on SDA. After the reception is completed, the MCU sends the NACK bit and the STOP bit to end a register read operation.
Figure 5 I2C read register frame format
The 8-bit Byte reading method can refer to the following code:
- unsignedcharI2C_Read_Byte(void)
- {
- intt,rxData;
- unsignedcharreceive;
- SDA_Input();
- for(t=0;t<8;t++)
- {
- SCL_Low();//CleartheSCL
- Delay(I2CDelay);
- SCL_High();//SettheSCL
- receive<<=1;
- rxData=SDA_Data_Register;
- if(rxData)
- {
- receive++;
- }
- Delay(I2CDelay);
- }
- returnreceive;
- }
The register reading method can refer to the following code:
- unsignedcharI2C_Read_Register(unsignedcharDevice_Write,unsignedcharDevice_Read,unsignedcharRegister)
- {
- unsignedcharReadData;
- I2C_Start();
- I2C_Send_Byte(Device_Write);//Sendthedeviceaddress
- I2C_Wait_Ack();//Waitfortheacksignal
- I2C_Send_Byte(Register);//Sendtheregisteraddress
- I2C_Wait_Ack();//Waitfortheacksignal
- I2C_Start();
- I2C_Send_Byte(Device_Read);//Sendregistervalue
- I2C_Wait_Ack();
- SDA_High();//SettheSDA
- ReadData=I2C_Read_Byte();
- I2C_NAck();
- Delay(1);
- I2C_Finish();
- returnReadData;
- }
3. Reference routine
The example program included in this article contains the header file and functions for complete GPIO simulation of I2C communication. The main contents of the example program are introduced below to facilitate readers' understanding.
Figure 6 I2C communication program architecture
3.1 Macro Definition
1) Define the hardware communication address and register address:
- #defineDevice_Address_Write0xC0
- #defineDevice_Address_Read0xC1
- #defineREG_1 0x01
- #defineREG_2 0x02
- #defineREG_3 0x03
- #defineREG_4 0x04
Device_Address_Write
|
Hardware write address: default address 0x60(7bit)+0b
|
Device_Address_Read
|
Hardware read address: default address 0x60(7bit)+0b
|
REG_1 - 4
|
Hardware internal register address
|
Table 1 Hardware read and write addresses and register addresses
When calling this code, you only need to modify the hardware address and register address in the .h file according to the actual situation of the device used, and you can easily call the relevant functions.
2) Define the I2C communication rate
- #defineI2CDelay1//DefinetoconfigureI2Crate
I2CDelay
|
I2C communication clock high and low level time
|
Table 2 I2C communication rate
By changing I2CDelay, you can set the duration of the high and low levels of the I2C communication clock, thereby changing the I2C communication rate. In practical applications, this value can be adjusted through actual tests to achieve the ideal communication rate.
3) Define IO port actions
- #defineSDA_High(){GpioDataRegs.GPASET.bit.GPIO7=1;EALLOW;GpioCtrlRegs.GPADIR.bit.GPIO7=1;EDIS;}
- #defineSDA_Low(){GpioDataRegs.GPACLEAR.bit.GPIO7=1;EALLOW;GpioCtrlRegs.GPADIR.bit.GPIO7=1;EDIS;}//TocleartheSDAline.Disableprotectionforwritingregister
- #defineSDA_Input(){EALLOW;GpioCtrlRegs.GPADIR.bit.GPIO7=0;EDIS;}//SDADIR=Input
- #defineSDA_Output(){EALLOW;GpioCtrlRegs.GPADIR.bit.GPIO7=1;EDIS;}//SDADIR=Output
- #defineSDA_Data_RegisterGpioDataRegs.GPADAT.bit.GPIO7
- #defineSCL_High(){GpioDataRegs.GPASET.bit.GPIO6=1;}//SettheSCLline
- #defineSCL_Low(){GpioDataRegs.GPACLEAR.bit.GPIO6=1;}//CleartheSCLline
SDA_High()
|
Set the GPIO corresponding to SDA to 1
|
SDA_Low()
|
Set the GPIO corresponding to SDA to 1
|
SDA_Input
|
Set the GPIO corresponding to SDA to input state
|
SDA_Output
|
Set the GPIO corresponding to SDA to output state
|
SDA_Data_Register
|
SDA corresponds to GPIO data register
|
SCL_High()
|
Set the GPIO corresponding to SCL to 1
|
SCL_Low()
|
Set the GPIO corresponding to SCL to 0
|
Table 3 IO port action macro definition
Define the GPIO port action as SDA and SCL in the form of macro definition to enhance the readability of the code. When porting the program, you only need to replace the code in the macro definition with the code corresponding to the GPIO port action according to the actual situation of the microcontroller, and there is no need to change other parts of the program. The EALLOW\EDIS statement is required to release the corresponding protection when the TI C2000 product changes the GPIO port direction. Please make changes according to the specific situation.
4) Define the Delay function
- #defineDelay(A)DELAY_US(A)
The Delay() function is used to delay the high and low levels of SDA and SCL in the program. It is actually defined as the DELAY_US() function in the routine. During the transplantation process, the macro definition needs to be modified according to the actual situation and changed to a delay function suitable for the user's MCU. There is no need to modify the subsequent program.
3.2 I2C communication function
- voidI2C_Start(void);
- voidI2C_Finish(void);
- Uint16I2C_Wait_Ack(void);
- voidI2C_NAck(void);
- voidI2C_Send_Byte(unsignedcharxtd);
- unsignedcharI2C_Read_Byte(void);
Function Name
|
Functional Description
|
void I2C_Start(void)
|
Send I2C communication start signal
|
void I2C_Finish(void)
|
Send I2C communication end signal
|
Uint16 I2C_Wait_Ack(void)
|
Wait for Ack response signal and return to receiving state
|
void I2C_NAck(void)
|
Send a NAck signal for register read
|
void I2C_Send_Byte(unsigned char xtd)
|
Send a byte
|
unsigned char I2C_Read_Byte(void)
|
Read a byte
|
void Gpio_setup(void)
|
GPIO port layout
|
void I2C_Write_Register(unsigned char Device, unsigned char Register, unsigned char value)
|
I2C write register function
|
void I2C_Read_Register(unsigned char Device_Write, unsigned char Device_Read, unsigned char Register)
|
I2C read register function
|
Table 4 I2C communication functions
IV. Conclusion
In order to solve the problem that MCU cannot directly use I2C to communicate with peripheral chips due to the lack of I2C interface, this article provides a method to use IO to simulate I2C interface. First, starting from the I2C protocol, the logic level of each bit in the data frame is introduced in detail, and a specific implementation method based on C2000 GPIO is given; on this basis, the reading logic of the internal register is introduced by taking the common 8-bit I2C communication Slave as an example, and the implementation method is given. Finally, the content of the attached reference routine is introduced to facilitate readers to refer to the routine, and other MCUs can also be quickly transplanted based on this routine. This article provides an effective solution to the need to use IO to simulate I2C.
|