Sharing the OS transplantation and application of Lingdongwei MM32 MCU--Based on I2C interface control of AMetal platform
[Copy link]
This post was last edited by Hot Ximixiu on 2021-6-15 08:52
The MM32L0 series includes a hardware I2C interface that supports master and slave modes and can control all devices that comply with the I2C bus protocol. I2C can operate in standard mode (data transfer rate is 0100 Kbps) and fast mode (data transfer rate is up to 400 Kbps).
The I2C bus has three types of signals in the process of transmitting data, namely: start signal, end signal and response signal.
When the bus is in an idle state, SCL and SDA are pulled high by external pull-up resistors at the same time. When the host starts data transmission, a start condition must be generated first. When the SCL line is high, the SDA line switches from high to low to indicate the start condition. When the host ends the transmission, a stop condition must be sent. When the SCL line is high, the SDA line switches from low to high to indicate the stop condition. The figure below shows the timing diagram of the start and stop conditions. During data transmission, when SCL is 1, SDA must remain stable.
Figure 1: Start and stop conditions
The eMiniBoard has a 24C02 on board. Next, we will enter the actual I2C operation process and conduct two-way communication with 24C02 through the hardware I2C interface of the MM32 MCU.
The SCL and SDA of the board-level 24C02 are connected to PB6 and PB7 of the MM32 respectively, as shown in the following figure:
Figure 2 MM32 and 24C02 connection diagram
I2C Master Initialization
The AMetal platform provides the MCU's I2C initialization function, which can be called directly. In AMetal, since users do not need to care about the control of the read/write direction bit, its address is represented by a 7-bit address. The
function prototype is:
am_i2c_handle_t am_mm32l073_i2c1_inst_init (void)
This function is defined in the am_hwconf_mm32l073_i2c.c file in the user_config directory and declared in am_mm32l073_inst_init.h. Therefore, when using the I2C initialization function, you need to include the header file am_mm32l073_inst_init.h.
Initialize I2C. When calling this function, you need to define a variable of type am_i2c_handle_t to save the obtained I2C service handle. The initialization procedure is:
am_i2c_handle_t i2c_handle
i2c_handle = am_mm32l073_i2c1_inst_init()
After obtaining the I2C service handle, there should also be a structure describing the I2C slave device. The prototype of the I2C device construction function is: void am_i2c_mkdev ( am_i2c_device_t *p_dev,
am_i2c_handle_t handle,
uint16_t dev_addr,
uint16_t dev_flags)
p_dev is a structure pointer pointing to am_i2c_device_t
handle is the I2C service handle
dev_addr is the slave device address
dev_flags is the control flag bit during the transmission process, and its available values have been defined in am_i2c.h macros
I2C slave initialization
Just like the MM32 MCU as a host, the AMetal platform also provides an I2C slave initialization function, which can be called directly. The function prototype is:
am_i2c_slv_handle_t am_mm32l073_i2c1_slv_inst_init (void)
This function is defined in the am_hwconf_mm32l073_i2c_slv.c file in the user_config directory and declared in am_mm32l073_inst_init.h. Therefore, when using the timer initialization function, you need to include the header file am_mm32l073_inst_init.h.
Initialize I2C. When calling this function, you need to define a variable of type am_i2c_slv_device_t to save the obtained I2C slave service handle. The initialization procedure is:
am_i2c_slv_handle_t slv_handle = am_mm32l073_i2c1_slv_inst_init ()
After obtaining the I2C service handle, there should also be a structure describing the I2C slave device. The prototype of the function to construct the I2C device is:
void am_i2c_slv_mkdev (am_i2c_slv_device_t *p_dev,
am_i2c_slv_handle_t handle,
am_i2c_slv_cb_funcs_t *p_cb_funs,
uint16_t dev_addr,
uint16_t dev_flags,
void *p_arg)
p_dev is a pointer to the slave device description structure
handle is the I2C standard service operation handle associated with the slave device
p_cb_funs is the function pointer of the callback function
dev_addr is the slave device address
dev_flags is the slave device characteristics
p_arg points to the callback function parameter
The callback function structure pointed to by p_cb_funs includes:
callback function pointer when slave address matches
get a send byte callback function pointer
submit a received byte callback function pointer
stop transmission callback function pointer
broadcast callback function pointer
The callback function must be defined by the user. If a callback function is not needed, it can be undefined and its function pointer can be pointed to NULL.
AMetal provides function interfaces such as instance initialization, read operation and write operation. Users do not need to operate the underlying layer, but can directly call related functions according to the specifications. Next, we will combine EEPROM operations to familiarize ourselves with the function interface calls of IIC.
EEPROM operation
AMetal provides I2C interface EEPROM driver functions, which can adapt to products of different models and capacities. The following will take FM24C02 as an example to illustrate. Its function prototype (am_ep24cxx.h) is:
am_ep24cxx_handle_t am_ep24cxx_init (am_ep24cxx_dev_t *p_dev,
const am_ep24cxx_devinfo_t *p_devinfo,
am_i2c_handle_t i2c_handle);
This function is intended to obtain the device instance handle 24c02_handle, where p_dev is a pointer to an instance of the am_ep24cxx_dev_t type, and p_devinfo is a pointer to instance information of the am_ep24cxx_devinfo_t type.
01 Example
A single FM24C02 can be considered as an instance of EP24Cxx. EP24Cxx is just an abstract representation of a series or the same type of EPROM chip. Obviously, multiple 24C02s are multiple instances of EP24Cxx. If only one FM24C02 is connected to the I2C bus, define the am_ep24cxx_dev_t type (am_ep24cxx.h) instance as follows:
am_ep24cxx_handle_t am_ep24cxx_init (am_ep24cxx_dev_t *p_dev,
const am_ep24cxx_devinfo_t *p_devinfo,
am_i2c_handle_t i2c_handle)
Among them, g_AT24C02_dev is a user-defined instance, and its address is passed as the actual parameter of p_dev. If two FM24C02s are connected to the same I2C bus, two instances need to be defined. That is: am_ep24cxx_dev_t g_24c02_dev0
am_ep24cxx_dev_t g_24c02_dev1
Each instance must be initialized, and the initialization of each instance will return a handle of the instance, so that when using other interface functions, different handles can be passed to operate different instances.
01 Instance information
Instance information mainly describes the inherent information of a specific device, i.e. the slave address and specific model of the I2C device. The definition of its type am_ep24cxx_devinfo_t (am_ep24cxx.h) is as follows:
typedef struct am_ep24cxx_devinfo {
uint8_t slv_addr;
uint32_t type
; } am_ep24cxx_devinfo_t;
The currently supported device models all have corresponding macros defined in am_ep24cxx.h. For example, the macro corresponding to FM24C02 is AM_EP24CXX_FM24C2, and the instance information is defined as follows:
const am_ep24cxx_devinfo_t _g_24c02_devinfo = {
0x50;
AM_EP24CXX_FM24C02
}
Among them, g_24c02_devinfo is the user-defined instance information, and its address is passed as the actual parameter of p_devinfo.
02 I2C handle I2C_handle
Taking I2C1 as an example, the return value of its instance initialization function am_mm32l073_i2c1_inst_init() will be passed as the actual parameter to i2c_handle. That is:
i2c_handle = am_mm32l073_i2c1_inst_init()
03 Instance handle fm24c02_handle
The return value fm24c02_handle of the FM24C02 initialization function am_ep24cxx_init() is passed as an actual parameter to the read ARM Embedded Software Engineering Methods and Practices:
Write Data function, whose type am_ep24cxx_handle_t (am_ep24cxx.h) is defined as follows:
typedef struct am_ep24cxx_dev *am_ep24cxx_handle_t
If the return value is NULL, it means that the initialization failed; if the return value is not NULL, it means that a valid handle is returned.
Based on the modular programming concept, the definition of initialization-related instance information is stored in the corresponding configuration file, and the instance initialization function interface is introduced through the header file. Example
program of instance initialization function:
#include "ametal.h"
#include "am_ep24cxx.h"
#include "am_mm32l073_inst_init.h"
static const am__ep24cxx_devinfo_t __g_24c02_devinfo = { }
static am_ep24cxx_dev_t __g_24c02_dev; //define FM24C02 device instance
am_ep24cxx_handle_t am_fm24c02_inst_init(void)
{
am_i2c_handle_t i2c_handle =am_mm32l073_i2c1_inst_init();
return am_ep24cxx_init(&__g_24c02_dev, &__g_24c02_devinfo, i2c_handle);
}
Instance initialization function interface:
#pragma once
#include "ametal.h"
#include "am_ep24cxx.h"
am_ep24cxx_handle_t am_fm24c02_inst_init(void);
You only need to use the instance initialization function without parameters to get the instance handle of FM24C02. That is:
am_ep24cxx_handle_t fm24c02_handle =am_fm24c02_inst_init();
Note that i2c_handle is used to distinguish I2C0, I2C1, I2C2, and I2C3, and the instance handle returned by the initialization function is used to distinguish multiple devices connected to the same system.
Table 1 ep24cxx read and write functions
The return value of each API has the same meaning: AM_OK indicates success, negative value indicates failure, and the cause of failure can be found in the corresponding macro definition in the am_errno.h file according to the specific value. The meaning of positive value is defined by each API. If there is no special description, it means that no positive value will be returned.
2
Data Reading and Writing
01
Writing Data
The function prototype for writing a piece of data starting from the specified address is:
int am_ep24cxx_write (am_ep24cxx_handle_t handle,
int start_addr,
uint8_t *p_buf,
int len);
If the return value is AM_OK, it means the write is successful, otherwise it fails. Assume that 16 bytes are written continuously starting from address 0x20.
Example program for writing data: uint8_t data[16] = {0,1,2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15};
am_ep24cxx_write(fm24c02_handle, 0x20, &data[0],16);
02
Read data
The function prototype for reading a section of data starting from the specified starting address is:
int am_ep24cxx_read (am_ep24cxx_handle_t handle,
int start_addr,
uint8_t *p_buf,
int len);
If the return value is AM_OK, it means the read is successful, otherwise it fails. Assuming that the data is read continuously starting from address 0x20, the example program is:
uint8_t data[16];
am_ep24cxx_read(fm24c02_handle, 0x20, &data[0],16);
03
Application Example
E2PROM read and write test, write 20 bytes of data to the memory and then read it out, and then verify whether the read and write are normal.
#include "ametal.h"
#include "am_LED.h"
#include "am_delay.h"
#include "am_mm32l073_inst_init.h"
#include "am_ep24cxx.h"
#include "am_hwconf_ep24cxx.h"
int app_test_ep24cxx(am_ep24cxx_handle_t handle)
{
int i;
uint8_t data[20];
for(i=0;i<20;i++) //Fill data
data=i;
am_ep24cxx_write(handle,0,&data[0],20); //Starting from address 0, write 20 bytes of data continuously 16 for(i=0;i<20;i++) //Clear data
data=0;
am_ep24cxx_read(handle,0,&data[0],20); //Starting from address 0, read 20 bytes of data continuously 19 for(i=0;i<20;i++)
{ //Compare data
IF(data!=i)
return AM_ERROR;
}
return AM_OK;
}
int am_main(void)
{
am_ep24cxx_handle_t fm24c02_handle =am_fm24c02_inst_init(); //Get 24C02 initialization instance handle
if(app_test_ep24cxx(fm24c02_handle)!=AM_OK)
{
am_led_on(0);
}
while(1)
{
am_led_toggle(0); //Flip LED
am_mdelay(100);
}
}
The parameter of app_test_ep24cxx() is the instance handle, which has a dependency on the EP24Cxx device. Since it is not possible to implement full compatibility calls, users can set the corresponding parameters based on the EEPROM information.
|