1. Functions of iiC devices
Obviously, the IIC controller provides the ability to transmit data, but the IIC controller does not know what the data means. The meaning of the data is from the external I2C slave device. We need to read the chip manual to know what data the IIC controller should send.
The following figure shows the operation method of AT24cxx:
2.I2C program framework
Obviously our program should be divided into two layers (IIC device layer, IIC controller layer), the framework is shown in the following figure:
The top layer is the i2c_test layer, which is used to test and verify the i2c function.
The second layer is the i2c device layer, which is used to read and write i2c to a specific model of slave device
The third layer is the general i2c controller layer, which is used to provide management operations for a specific model of i2c master control
The bottom layer is the specific model layer of the i2c controller
In the general i2c control layer, we provide a unified interface i2c_transfer. No matter which chip is used, it will eventually call i2c_transfer to select a certain I2C controller to send data or read data from the I2C device.
Each data transmission can be represented by an i2c_msg structure. However, when reading data at a certain address, two i2c_msg structures are needed to describe it, because an i2c_msg structure can only describe one transmission direction (read/write). When we read data at a certain address of ac24ccxx, we must first write the address to be read, and then read the data at the device address.
i2c_test.c file
The contents of this file are as follows:
void i2c_test(void) { /* Initialization: select I2C controller */ /* provide menu for testing */ }
This menu will eventually call the function in at24cxx.c.
at24cxx.c file
The standard interface i2c_transfer will be used to start I2C transmission. The content of the file is as follows:
#define AT24CXX_ADDR 0x50 int at24cxx_write(unsigned int addr, unsigned char *data, int len) { i2c_msg msg; int i; int err; unsigned char buf[2]; for (i = 0; i < len; i++) { buf [0] = addr++; buf[1] = data[i]; /* Construct i2c_msg */ msg.addr = AT24CXX_ADDR; msg.lags = 0; /* write */ msg.len = 2; msg.buf = buf ; msg.err = 0; msg.cnt_transferred = -1; /* Call i2c_transfer */ err = i2c_transfer(&msg, 1); if (err) return err; } return 0; } int at24cxx_read(unsigned int addr, unsigned char *data, int len) { i2c_msg msg[2]; int err; /* Construct i2c_msg */ msg[0].addr = AT24CXX_ADDR; msg[0].lags = 0; /* write */ msg[0].len = 1; msg[0].buf = &addr; msg[0].err = 0; msg[0].cnt_transferred = -1; msg[1].addr = AT24CXX_ADDR; msg[1].lags = 1; /* read */ msg[1].len = len; msg[1].buf = data; msg[1].err = 0; msg[1].cnt_transferred = -1; /* Call i2c_transfer */ err = i2c_transfer(&msg, 2); if (err) return err; return 0; }
View Code
i2c_controller.h file
typedef struct i2c_msg { unsigned int addr; /* 7bits */ int flags; /* 0 - write, 1 - read */ int len; int cnt_transferred; unsigned char *buf; }i2c_msg, *p_i2c_msg; typedef struct i2c_controller { int ( *int)(void); int (*master_xfer)(i2c_msg msgs, int num); char *name; }i2c_controller, *p_i2c_controller;
i2c_controller.c file
The contents of this file are as follows:
#define I2C_CONTROLLER_NUM 10 /* There is an i2c_controller array used to store the operation structures of various chips*/ static p_i2c_controller p_i2c_controllers[I2C_CONTROLLER_NUM]; static p_i2c_controller p_i2c_con_selected; void register_i2c_controller(p_i2c_controller *p) { int i; for (i = 0; i < I2C_CONTROLLER_NUM; i++) { if (!p_i2c_controllers[i]) { p_i2c_controllers[i] = p; return; } } } /* Select an I2C controller by name*/ int select_i2c_controller(char *name) { int i; for (i = 0; i < I2C_CONTROLLER_NUM; i++) { if (p_i2c_controllers[i] && !strcmp(name, p_i2c_controllers[i]->name)) { p_i2c_con_selected = p_i2c_controllers[i]; return 0; } } return -1; } /* Implement the i2c_transfer interface function*/ int i2c_transfer(i2c_msg msgs, int num) { return p_i2c_con_selected->master_xfer(msgs, num); } void i2c_init(void) { /* Register the following I2C controller*/ s3c2440_i2c_con_add(); /* Select a certain I2C controller*/ select_i2c_controller("s3c2440"); /* Call its init function*/ p_i2c_con_selected->init(); }
View Code
If there is an array, there must be a registration function register_i2c_controller, which will put the I2C controller structure i2c_controller implemented below into the i2c_controller array. After the select_i2c_controller function selects a certain I2C controller by name, the selected I2C controller will be used to start the transmission in the future.
s3c2440_i2c_controller.c file
Interrupt service function, when an interrupt occurs, the interrupt service function will be called, the code is as follows (see the next section for details):
void i2c_interrupt_func(int irq) { /* Each data transfer will generate an interrupt */ /* For each transfer, the first interrupt is "the device address has been issued" */ }
The s3c2440_i2c_con_init function is used to initialize I2C. The controller code is as follows:
void s3c2440_i2c_con_init(void) { /* Configure pins for I2C */ GPECON &= ~((3<<28) | (3<<30)); GPECON |= ((2<<28) | (2<<30)); /* Set clock */ /* [7] : IIC-bus acknowledge enable bit, 1-enable in rx mode * [6] : Clock source, 0: IICCLK = fPCLK /16; 1: IICCLK = fPCLK /512 * [5] : 1-enable interrupt * [4] : Reading 1 indicates an interrupt has occurred, writing 0 clears and resumes I2C operation * [3:0] : Tx clock = IICCLK/(IICCON[3:0]+1). * Tx Clock = 100khz = 50Mhz/16/(IICCON[3:0]+1) */ IICCON = (1<<7) | (0<<6) | (1<<5) | (30<<0); /* Register interrupt processing function*/ register_irq(27, i2c_interrupt_func); }
View Code
1).IICCON = (0<<6) | (1<<5) | (30<<0); Set the IICCON control register. Select the transmit clock and enable interrupt. Set ACK response enable, bit[7].
2).register_irq(27, i2c_interrupt_func): Register the interrupt handling function. When an I2C interrupt occurs, the i2c_interrupt_func interrupt handling function will be called.
After initialization is completed, you can call do_master_tx to write to the I2C slave. This function only starts the I2C transmission and then waits until the data is transmitted in the interrupt service routine before returning. The function code is as follows:
int do_master_tx(p_i2c_msg msg) { p_cur_msg = msg; msg->cnt_transferred = -1; msg->err = 0; /* Set registers to start transmission*/ /* 1. Configure as master tx mode */ IICCON |= (1<<7); /* TX mode, release SDA in ACK cycle */ IICSTAT = (1<<4); /*IIC-bus data output enable/disable(1: Enable Rx/Tx)*/ /* 2. Write the slave device address to IICDS */ IICDS = msg->addr<<1;//[slave addr [7:1], addr[0] is trans dir] /* 3. IICSTAT = 0xf0 (start transmission), slave addr data is sent out, when it reaches the 9th clk, whether there is ack or not, it will cause an interrupt*/ IICSTAT = 0xf0; /* Subsequent transmission is driven by interrupt */ /* Loop and wait for interrupt processing to complete */ while (!msg->err && msg->cnt_transferred != msg->len); if (msg->err) return -1; else return 0; }
View Code
1).IICDS = msg->addr<<1: Write the slave address (high 7 bits, so it needs to be shifted right one bit) into the IICDS register.
2).IICSTAT = 0xf0: Set the IICSTAT register, set the s3c2440 as the master transmitter, and send the S signal, followed by the slave address. Subsequent transmission work will be completed in the interrupt service routine.
The implementation of the do_master_rx function is similar to the do_master_tx function. The code is as follows:
int do_master_rx(p_i2c_msg msg) { p_cur_msg = msg; msg->cnt_transferred = -1; msg->err = 0; /* Set registers to start transmission*/ /* 1. Configure as Master Rx mode */ IICCON |= (1<<7); /* RX mode, respond ACK in ACK cycle */ IICSTAT = (1<<4); /*IIC-bus data output enable/disable*/ /* 2. Write slave device address to IICDS */ IICDS = (msg->addr<<1)|(1<<0); /* 3. IICSTAT = 0xb0, the slave device address is sent out, which will cause an interrupt*/ IICSTAT = 0xb0; /* Subsequent transmission is driven by interrupt*/ /* Loop and wait for interrupt processing to complete*/ while (!msg->err && msg->cnt_transferred != msg->len); if (msg->err) return -1; else return 0; }
View Code
1).IICDS = (msg->addr<<1)|(1<<0): Write the slave device address into IICDS. The first 7 bits are the slave address, and the 8th bit indicates the transmission direction (0 indicates a write operation, 1 indicates a read operation).
The s3c2440 transfer function uses the flags bit to indicate whether it is read/write (1: read 0: write). The code is as follows:
int s3c2440_master_xfer(p_i2c_msg msgs, int num) { int i; int err; for (i = 0; i < num; i++) { if (msgs[i].flags == 0)/* write */ err = do_master_tx( &msgs[i]); else err = do_master_rx(&msgs[i]); if (err) return err; } return 0; }
The s3c2440_i2c_con_add function registers the s3c2440_i2c_con structure defined above into the upper layer i2c_controller array.
void s3c2440_i2c_con_add(void) { register_i2c_controller(&s3c2440_i2c_con); }
We define an i2c_controller structure s3c2440_i2c_con. The following code initializes it.
static i2c_controller s3c2440_i2c_con = { .name = "s3c2440", .init = s3c2440_i2c_con_init, .master_xfer = s3c2440_master_xfer, };
The framework is summarized as follows:
Previous article:s3c2440 bare metal - nandflash programming - 1 - nandflash principle and structure introduction
Next article:s3c2440 bare metal - i2c programming - 2 - i2c controller
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- 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!
- 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
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications