s3c2440 bare metal - i2c programming - 3 - i2c program framework

Publisher:快乐行者Latest update time:2024-07-04 Source: elecfans Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

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:


GIF cover

GIF cover

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:



Reference address:s3c2440 bare metal - i2c programming - 3 - i2c program framework

Previous article:s3c2440 bare metal - nandflash programming - 1 - nandflash principle and structure introduction
Next article:s3c2440 bare metal - i2c programming - 2 - i2c controller

Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components
Guess you like

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号