Due to work needs, use computer to read and write AT24C01A, and use VC language to realize parallel port simulation I2C.
(I) Preparation before the experiment:
I2C bus: The I2C bus is a two-wire serial transmission bus first introduced by Philips. It
consists of a data line (SDA) and a clock line (SDL). The data transmission process of the I2C bus is shown in Figure 3. The basic process is:
1. The host sends a start signal.
2. The host then sends a 1-byte slave address information, of which the lowest bit is the read-write control code (1 for read, 0 for write), and the upper 7 bits are the slave device address
code.
3. The slave sends an acknowledgment signal.
4. The host starts to send information. After each byte is sent, the slave sends an acknowledgment signal to the host.
5. The host sends a stop signal.
Specific description of each signal on the I2C bus:
Start signal: When the clock line (SCL) is at a high level, the data line (SDA) changes from high to low, which will generate a start signal.
Stop signal: When the clock line (SCL) is at a high level, the data line (SDA) changes from low to high, which will generate a stop signal.
Acknowledge signal: It is also called recognition signal. When the host writes to the slave, after writing one byte, if it is correct, the slave will pull the data line (SDA)
low in the next clock cycle to tell the host that the operation is valid. When the host reads a byte correctly from the slave, the host will also pull the data line (SDA
) low in the next clock cycle to send a recognition signal to tell the slave that the data has been received. (Note: When reading from the slave, the host does not send an acknowledgement after receiving the last byte of data
, but directly sends a stop signal).
Note: During the I2C communication process, all data changes must be changed when the clock line SCL is low, and the data SDA signal must be kept stable when the clock line SCL is high. Any level change on the data line when the clock line is high is considered a start or stop signal.
The following takes AT24C01A as an example to explain several main working sequences in detail.
(1) Format of the control word (section) of AT24LC01A: The 4 bits immediately following the start signal when sending are the device selection bits, usually '1010', which together with the following 3-bit device address code (determined by the levels on A0, A1, and A2 of AT24C01) constitute the 7-bit slave address. The slave address is followed by a 1-bit read/write control bit, which is 1 for read and 0 for write. The last bit is the acknowledge bit, which is given by the slave.
(2) AT24LC01A write timing: The host sends a start signal, followed by a slave address and a write control code, the host receives an acknowledgement from the slave, the host sends 1 byte of address information, the host receives an acknowledgement, the host writes 1 byte of data to the slave, the host receives an acknowledgement, and the host sends a stop signal. The write operation is completed, and 1 byte of data is written to the specified address in AT24C01. AT24C01 provides a page write mode, which can continuously write up to 8 bytes of data each time and then send a stop signal. This mode can be used to speed up the writing of a large amount of data.
(3) AT24CO1 random read timing: the host sends a start signal, then sends the slave address and write control code, the host receives a response, the host sends 1 byte of address information, the host receives a response (note: the previous timing is a write operation, the purpose is to write the start address into the AT24C01A buffer to tell the subsequent read operation from which address to start, this step is sometimes called "pseudo write" in the read timing), the host sends a start signal, the host sends the slave address and read control code, the host receives a response, the host reads 1 byte of data, the host does not send a response, and the host sends a stop signal. After completing the above steps, the host has read 1 byte of data in the specified address from AT24CO1.
(4) AT24LC01 read timing: Compared with the random read timing, the host does not write the start address to the slave, so this mode is used to read the data in the current address. In addition, AT24C01 can also use continuous reading mode, so that up to 8 bytes can be read each time. Note: When reading continuously, the host must send a response to the host after reading 1 byte, but the host will not send a response after the last 1 byte (that is, before the stop signal).
Signal on the data line (SDA): When reading, the slave puts the data on SDA at the rising edge of SCL. When writing, when encountering the rising edge of SCL, the slave will receive the data on SDA.
2. Parallel port: It contains a group of input/output ports. On a PC, it is a 25-pin D-type socket, which is generally used to connect a printer, so it
is sometimes called a printer port.
Parallel port signal: Taking a printer as an example, some of the parallel port I/0 signals are specifically used to transmit data to the printer, some are used to
control the transmission process, and some send various working status information of the printer to the CPU. The details are shown in Table 1. All signals in the table use low level
(0V) to represent logic 0 and high level (5V) to represent logic 1 (the voltage is relative to the ground potential on pins 18-25). All signals prefixed with the symbol '-' refer to low level active signals.
It can be seen that the signals on pins 2-9 are signals that transmit actual numbers, while the signals on other lines are used to initialize the printer and synchronize the printer's actions. The following is a brief introduction to the printing process to deepen the understanding of the parallel port. The CPU
selects the printer through the signals on pins 16 and 17 of the parallel port and initializes it. And responds with the signal on pin 13. When the printer is ready to receive data, it
sets pin 11 to a low level (indicating that it can receive), and the CPU puts the data on the parallel port data line (2-9) and selects the printer's data through the selection signal on pin 1. When the printer receives the selection signal, it sets the busy signal (11) to a high level, indicating that it is receiving data. After receiving the data, the printer sends out the active confirmation signal (pin 10 low level) for a short time, and then sets the busy signal (11) to low level (that is, not active) and is ready to receive more data.
Parallel port hardware: The parallel port line is now usually integrated on the system board. The signals on the 25-pin socket
can be programmed and controlled
through three registers (that is, three input/output ports): data latch, print status and printer
control. There are usually multiple parallel ports in the computer system.
Table 2 lists their addresses in the input/output system. It should be noted that these addresses are given by the system BIOS, not the physical
addresses of the hardware, so the current port configuration can be changed by setting the BIOS.
Port register: Table 3 lists the meaning of each bit of the parallel port register. These signals are also the main signals that appear on the external plug. However,
the polarity of some signals in the register is exactly opposite to the polarity of the corresponding signal on the plug. For example, when the select signal is low on the plug, the signal is active
, while it is high in the printer control register and is active.
Through the above preparation knowledge, you should have the following understanding: 1. The 25 pins of the parallel port can be understood as the external mapping of three registers. In addition to
the pins for transmitting 8-bit actual data, there are also pins for controlling the printer and obtaining the current status of the printer. Some of these pins are input and some are
output, so they can be used as flexibly as the I/O of the microcontroller. 2. During the communication process of the I2C bus, the
direction of the signal flow on the data line (SDA) is constantly changing. For example, when the host is writing AT24C01, the direction of SDA is from the host to the slave, and SDA is output. After writing a byte, when receiving the response, the direction of SDA changes from the slave to the host, and SDA is input (for the host). 3. The parallel port simulates the I2C bus, which is actually to use software to control the I/O of the parallel port to input and output the high and low level signals required by the I2C bus, thereby generating various timing sequences of the I2C bus.
(II) Make a test circuit:
Experimental Circuit
相对于并口,P1的13脚接SDA 的输入,P1的3脚接SDA的输出,P1的15脚接SCL的输入,P1的5脚接SCL的输出。试验用的电路,分析如后:P1的5脚接IC1的SCL端,用做I2C总线的串行时钟信号输出。因I2C总线中数据线(SDA)在不同的时间可能是输入也可能是输出,所以接在IC1 SDA端上的信号也有两路,输出时,P13脚输出低电平T1导通,SDA被置为低电平,P13 脚输出高电平T1截止,因 R1的作用SDA被置为高电平。输入时,P1 通过判断 13 脚上的电平高低,来读取SDA上的数据。要注意的是用于输入时T1必须是截止的,以免SDA被箝位。
这个电路具有通用性,AT24C01、AT24CO2、24LC64等24系列的I2C EEPROM 均可按这个电路与并口连接,所以 不妨把它当作实用工具来认真制作。先找一条并口电缆,看电缆插头的形式,找一个与之配套的25针插座,购买一个拨动式的IC插座,将IC插座按图中IC1的连接方法与找来的并口插座相连,然后按图将T1、R1、C1、直接焊在IC插座或并口插座上,要尽量作的紧凑些。最后将电路固定在一个合适的小塑料盒内,好了,现在它是我们的试验器材,等看过后面的内容,你会发现只要为其配上软件,它就是一个用于读写I2C EEPROM 的好工具。
(三)试验程序编写:
和其它高级语言相比,C 更适合于对硬件编程。但是由于要求工作界面要美观,目前实验用到的是VC++。但是在VC条件编制并口程序需要相关的I/O库进行配置。
(IV) Programming: Based on the above analysis, to use the parallel port to simulate the I2C bus to read and write AT24C01, the program needs to have the following parts:
Send I2C start signal: SCL and SDA are both high level, after a delay, write "0XFD" to 378H (other pins remain unchanged, just set bit 1 to low level), so that SDA changes from high level to low level, that is, the start signal of I2C is generated. Finally, write "0XFC" to 378H (that is, other pins remain unchanged, set bit 0 and bit 1 to low level) to make SCL low level to complete a clock and prepare for the subsequent reading and writing.
Send I2C stop signal: The stop signal of I2C is when SCL is high and SDA changes from low to high. The program can be written in the following steps. Use the write port function
to write "0XFC" to 378H, so that SCL and SDA are low level, delay for a period of time, write "0XFD" to 378H, so that SCL becomes high level, SDA is low level, delay, write "0XFF" to 378H, SCL remains unchanged, so that SDA changes from the original low level to high level, that is, a stop signal is generated. Delay for a period of time, and finally write "0XFE" to 378H to make SCL low level to complete a clock.
Send data: first put the data to be sent in a variable, and then send it bit by bit. The method is to obtain the value of the bit to be sent (
1 or 0) through bit operation, and then use the write port function to simulate SCL and SDA, and send one bit of data according to the I2C write timing. The while loop statement can be used in the program to control the number of bits and bytes sent.
Host (parallel port) sends response: I2C bus, host sends response in continuous read sequence. After reading each byte (8 bits), the host keeps SDA
at a low level for one clock cycle. You can use the write port function to first set SDA and SCL to 0, then change SCL to high, SDA remains low, a response signal is sent, and finally SCL is set low to complete a clock.
Receive data: When the parallel port reads data from the I2C bus, T1 must be turned off, and the 13th pin of the parallel port is used to receive data on SDA. You can follow the steps below to
first use the write port function to make SCL low, and at the same time output a high level at the 3rd pin of the parallel port to turn off T1. Then use the write port function to set SCL to 1 alone, and keep other bits unchanged, simulate the rising edge of the clock, IC1 will put one bit of data on the data line SDA, use the read port function to read
the current value of the "printer status" register 379H, assign the result to a variable, and then shift the variable right by 4 bits and then left by 7 bits
(to obtain the level state of pin 13, that is, the value of bit 4 of the printer status register), determine whether the variable is 0, and finally move the judgment result
into another variable used to store the "read data", complete the operation of reading one bit of data, use the write port function to make SCL low level, and
at the next rising edge of SCL, use the above method to add one bit of data to the "read data" variable. The while loop can be used to control the number of bits and bytes to be read
. Note that the above process is performed when T1 is in the cut-off state.
Host (parallel port) receiving response: When receiving response is used to write I2C, after writing one byte of data to the slave, if the operation is successful, the slave
will make SDA low in the next clock. The host query response can enhance the reliability of the operation. The receiving response is roughly the same as the receiving data mentioned above, except that only one bit of data is received and not stored, and its value is directly judged to be 0. If it is not 0 (that is, no response is received), it will turn to the error handling program, and if it is 0, it will continue with
the following operations. In actual programming, this step will be incorporated into the operation of writing I2C.
About delay: I2C devices have regulations on the time that the high and low level signals on SDA and SCL need to be maintained. For example:
how long the high and low levels of the start signal should be maintained, how long the high and low levels of the data signal should be maintained at the minimum, etc. Different devices have different regulations on this time. Looking up
the data sheet of 24LO02, you can know that the time it needs to maintain each signal at different voltages is between hundreds of nanoseconds and several microseconds. This
time also reflects the read and write speed of the I2C device. Because computers have different speeds, it is difficult to use computer parallel ports to simulate I2C with an accuracy of
microseconds. In order to reliably operate the I2C bus on different computers, the test program uses the C language delay function delay(); the
minimum delay that this function can produce is 1 millisecond. Although this reduces the read and write speed of I2C, it can ensure the reliability of the operation.
Program code:
/***************************************/
//The control address is 0x378
//SDA signal: PIN13 simulates the input of the host;
// PIN3 simulates the output of the host;
//SCL signal: PIN15 simulates the input of the host;
// PIN5 simulates the output of the host;
/***************************************/
/*****************************************/
//Function name: IIC start function
//Start signal: While the clock line (SCL) is high,
//the data line (SDA) changes from high to low, which will generate a start signal.
/****************************************/
void port2iicbase::i2c_start()
{
SetPortVal(PORT1,0x07,1);/*scl 0, sda 1*/
delay(1);
SetPortVal(PORT1,0x0f,1);/*scl 1, sda 1*/
delay(1);
SetPortVal(PORT1,0x0d,1);/*scl 1, sda 0*/
delay(1);
SetPortVal(PORT1,0x05,1);/*scl 0, sda 0*/
return;
}
/*****************************************/
//Function name: IIC stop function
//Stop signal: When the clock line (SCL) is high,
//the data line (SDA) changes from low to high, a stop signal will be generated
/*************************************/
void port2iicbase::i2c_stop()
{
SetPortVal(PORT1,0x07,1);/*scl 0, sda 1*/
delay(1);
SetPortVal(PORT1,0x0d,1);/*scl 1, sda 0*/
delay(1);/***/
SetPortVal(PORT1,0x0f,1);/*scl 1, sda 1*/
delay(1);/**/
SetPortVal(PORT1,0x05,1);/*scl 0, sda 0*/
}
/*****************************************/
//Function name: IIC write a byte function
/*************************************/
int port2iicbase::i2c_writebyte(char c)
{
short int count=7;
char temp;
DWORD dwPortVal;
char e;
while(count>=0)
{
temp=c>>count;
temp=temp<<7; //确定传输的字节
if (temp=='x80') //传输"1"
{
SetPortVal(PORT1,0x05,1);/*scl 0, sda 0*/
delay(1);
SetPortVal(PORT1,0x07,1);/*scl 0, sda 1*/
delay(1);
SetPortVal(PORT1,0x0f,1);/*scl 1, sda 1*/
delay(1);
SetPortVal(PORT1,0x07,1);/*scl 0, sda 1*/
delay(1);
}
else //传输"0"
{
SetPortVal(PORT1,0x05,1);/*scl 0, sda 0*/
delay(1);
SetPortVal(PORT1,0x0d,1);/*scl 1, sda 0*/
delay(1);
SetPortVal(PORT1,0x05,1);/*scl 0, sda 0*/
delay(1);
}
count--;
}
/**ask**/
SetPortVal(PORT1,0x07,1);/*scl 0, sda 1*/
delay(1);/***/
SetPortVal(PORT1,0x0f,1);/*scl 1, sda 1*/
delay(1);
// SetPortVal(PORT1,0x05,1);/*scl 0, sda 0*/
// delay(1);/***/
GetPortVal(PORT2, &dwPortVal, 1);
e = (char)dwPortVal;
temp=e>>4;
temp=temp<<7;
if (temp=='x0')
return 0;
else
MessageBox(NULL,"Not Acknowledge!!","Sending error",MB_OKCANCEL);
return 1;
}
/****************************************/
//Function name: IIC reads a character function
/****************************************/
char port2iicbase::i2c_readbyte()
{
unsigned short count=8;
char d,e,f='x0';
DWORD dwPortVal;
while(count>0)
{
SetPortVal(PORT1,0x07,1);/*scl 0, sda 1*/
delay(1);/***/
SetPortVal(PORT1,0x0f,1);/*scl 1, sda 1*/
delay(1);
GetPortVal(PORT2, &dwPortVal, 1);
e = (char)dwPortVal;
d=e>>4;
d=d<<7;
if(d=='x80')
d='x1';
f=f<<1;
f=(f+d); //combine into byte
count--;
}
return f;
}
/****************************************/
//Function name: Host (parallel port) send response
/****************************************/
void port2iicbase::i2c_ask()
{
SetPortVal(PORT1,0x05,1);/*scl 0, sda 0*/
delay(1);/**/
SetPortVal(PORT1,0x0d,1);/*scl 1, sda 0*/
delay(1);
SetPortVal(PORT1,0x05,1);/*scl 0, sda 0*/
delay(1);
}
//////////////////////////////////
//发送数据按钮
/////////////////////////////////
void Cport2iicdlg1::OnWriteok()
{
// TOD Add your control notification handler code here
port2iicbase port2iic;
char s[100];
char temp[3];
int len;
int i;
char m_cValue;
unsigned int WriValue=0;
int time = 0;
unsigned short c;
char d,e;
#ifdef _PORT2IIC
SetDlgItemText(IDC_WRITE_STATUS,"Sending....");
GetDlgItem(IDC_EDIT_WRITE)->GetWindowText(s, 100);
len = strlen(s);
c = len/2;
e='x0';
d='x0';
i = 0;
c = 5;
//Write a page
//Start the start signal;
port2iic.i2c_start();
//Send control information
//"xa0" = 1010(A2)(A1)(A0)(R/W) where (R/w) = 0 Write
//"xa1" = 1010(A2)(A1)(A0)(R/W) where (R/w) = 1 Read
port2iic.i2c_writebyte('xa0');
//Send address
port2iic.i2c_writebyte(e);
while(c>0)
{
temp[0] = s[i];
temp[1] = s[i+1];
temp[2] = 0x0;
sscanf(temp,"%x",&WriValue);
m_cValue = (char)WriValue;
port2iic.i2c_writebyte(m_cValue);
c = c-1;
i+=2;
}
port2iic.i2c_stop();
#if 0
//24c01 writes 128 bytes, debug writes 16 words;
c=20;
e='x0';
d='x0';
while(c>0)
{
//Start the start signal;
port2iic.i2c_start();
//Send control information
//"xa0" = 1010(A2)(A1)(A0)(R/W) where (R/w) = 0 write
//"xa1" = 1010(A2)(A1)(A0)(R/W) where (R/w) = 1 Read
port2iic.i2c_writebyte('xa0');
//Send address
port2iic.i2c_writebyte(e);
port2iic.i2c_writebyte(d);
port2iic.i2c_stop();
d = c/5;
c = c-1;
e = e+1;
}
Sleep(50);
//Read arbitrarily;
//Read and write data
b = 0;
i = 0;
a = 20;
while(a > 0)
{
//Read arbitrarily;
port2iic.i2c_start();
port2iic.i2c_writebyte('xa0');
d = char(b);
port2iic.i2c_writebyte(d);
port2iic.i2c_start();
port2iic.i2c_writebyte('xa1');
d = port2iic.i2c_readbyte();
port2iic.i2c_stop();
buff[i] = d;
i++;
b= b+1;
a = a - 1;
}
#endif
#else
len = strlen(s);
for(i = 0; i < len; i+=2)
{
temp[0] = s[i];
temp[1] = s[i+1];
temp[2] = 0x0;
sscanf(temp,"%x",&WriValue);
m_nValue = (DWORD)WriValue;
SetPortVal( 0x378,m_nValue,1);
}
#endif
SetDlgItemText(IDC_WRITE_STATUS,"Sending Ended");
return;
}
////////////////////////////////////
//Receive data button in timer
////////////////////////////////////
void Cport2iicdlg1::OnTimer(UINT nIDEvent)
{
// TOD Add your message handler code here and/or call default
port2iicbase port2iic;
DWORD m_nValue = 0;
CString temp1,temp2;
CEdit* pReadEdit;
unsigned short buff[100];
unsigned short a,b;
char d;
int i;
pReadEdit = (CEdit*) GetDlgItem(IDC_EDIT_READ);
memset(buff,0x0,100);
#ifdef _PORT2IIC
//任意读;
#if 0
//读写入的数据
b = 0;
a = 5;
i = 0;
while(a > 0)
{
//任意读;
port2iic.i2c_start();
port2iic.i2c_writebyte('xa0');
d = char(b);
port2iic.i2c_writebyte(d);
port2iic.i2c_start();
port2iic.i2c_writebyte('xa1');
d = port2iic.i2c_readbyte();
port2iic.i2c_stop();
b= b+1;
a = a - 1;
buff[i] = (unsigned short)d;
buff[i] = buff [i] & 0x00ff;
temp1 = " ";
temp2.Format(_T("%.2x"),buff[i]);
if(m_read != _T(""))
m_read = m_read + temp1;
m_read = m_read + temp2;
i++;
SetDlgItemText(IDC_EDIT_READ,m_read);
DWORD dwSel = pReadEdit->GetSel();
pReadEdit->SetSel(HIWORD(dwSel), -1);
}
#endif
#if 1
//顺序读
//读写入的数据
b = 0;
a = 5;
i = 0;
port2iic.i2c_start();
port2iic.i2c_writebyte('xa0');
d = char(b);
port2iic.i2c_writebyte(d);
port2iic.i2c_start();
port2iic.i2c_writebyte('xa1');
while(a > 0)
{
d = port2iic.i2c_readbyte();
a = a - 1;
if(a > 0)
port2iic.i2c_ask();
buff[i] = (unsigned short)d;
buff[i] = buff [i] & 0x00ff;
temp1 = " ";
temp2.Format(_T("%.2x"),buff[i]);
if(m_read != _T(""))
m_read = m_read + temp1;
m_read = m_read + temp2;
i++;
SetDlgItemText(IDC_EDIT_READ,m_read);
DWORD dwSel = pReadEdit->GetSel();
pReadEdit->SetSel(HIWORD(dwSel), -1);
}
port2iic.i2c_stop();
#endif
#else
GetPortVal(0x378, &m_nValue,1);
m_nValue = m_nValue & 0x000000ff;
temp1 = " ";
temp2.Format(_T("%.2x"),m_nValue);
if(m_read != _T(""))
m_read = m_read + temp1;
m_read = m_read + temp2;
SetDlgItemText(IDC_EDIT_READ,m_read);
DWORD dwSel = pReadEdit->GetSel();
pReadEdit->SetSel(HIWORD(dwSel), -1);
#endif
CDialog::OnTimer(nIDEvent);
return;
}
The above program has been verified to run correctly.
Previous article:What electronic knowledge should college students majoring in electronics learn?
Next article:Photovoltaic power generation system solar automatic tracker based on single chip microcomputer EM78247
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- Rambus Launches Industry's First HBM 4 Controller IP: What Are the Technical Details Behind It?
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- WIFI6 Explained
- Control battery output and prevent battery reverse connection
- 【NXP Rapid IoT Review】 + Kit Modification-External Lithium Battery (Link)
- How to design a typical analog front-end circuit
- [Fudan Micro FM33LG0 Series Development Board Review] Driving RGB TFT Display
- Thank you for being there + thank you for everyone who appears in my life
- Use NucleiStudio to import GD32VF103_Demo_Suites routine
- [Project source code] Operating FPGA side registers under FPGA-based Linux
- Newbies need help with a few questions about RA2A1
- The virtual machine started to freeze again