打印
[其他]

分享灵动微MM32 MCU的OS移植与应用--基于AMetal平台I2C接口控制

[复制链接]
1774|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
laocuo1142|  楼主 | 2021-6-15 10:26 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

MM32L0系列包含一个硬件I2C接口,支持主从模式,能控制所有符合I2C总线协议的设备。I2C可以工作在标准模式(数据传输速率为0∼100 Kbps),快速模式(数据传输速率最大为400 Kbps)。


I2C 总线在传送数据过程中共有三种类型信号,它们分别是:开始信号、结束信号和应答信号。
当总线处于空闲状态时,SCL和SDA同时被外部上拉电阻拉为高电平。当主机启动数据传输时,必须先产生一个起始条件。在SCL线是高电平时,SDA线从高电平向低电平切换表示起始条件。当主机结束传输时要发送停止条件。在SCL线是高电平时,SDA线由低电平向高电平切换表示停止条件。下图显示了起始和停止条件的时序图。数据传输过程中,当SCL为1时,SDA必须保持稳定。


图1 起始和停止条件

使用特权

评论回复
沙发
laocuo1142|  楼主 | 2021-6-15 10:29 | 只看该作者

eMiniBoard板载一颗24C02,我们接下来一起进入I2C实战操作过程,通过MM32 MCU的硬件I2C接口来与24C02进行双向通信。

板级24C02的SCL和SDA分别连在MM32的PB6和PB7上,如下图所示:



图2 MM32与24C02连接图


使用特权

评论回复
板凳
laocuo1142|  楼主 | 2021-6-15 10:30 | 只看该作者

I2C主机初始化


AMetal 平台提供了MCU的I2C初始化函数,可以直接调用初始化函数。在AMetal 中,由于用户无需关心读/写方向位的控制,因此其地址使用7-bit地址表示。
函数原型为:
am_i2c_handle_t am_mm32l073_i2c1_inst_init (void)

该函数在user_config目录下的am_hwconf_mm32l073_i2c.c文件中定义,在am_mm32l073_inst_init.h中声明。因此使用I2C初始化函数时,需要包含头文件 am_mm32l073_inst_init.h。

初始化I2C,调用该函数时需要定义一个am_i2c_handle_t 类型的变量,用于保存获取的I2C 服务句柄,初始化程序为:
am_i2c_handle_t i2c_handle
i2c_handle = am_mm32l073_i2c1_inst_init()


使用特权

评论回复
地板
laocuo1142|  楼主 | 2021-6-15 10:31 | 只看该作者

获取了I2C服务句柄后,还应该有一个描述I2C从设备的结构体,构造I2C设备函数的原型为: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 为指向 am_i2c_device_t 的结构体指针
handle 为 I2C 服务句柄
dev_addr 为从机设备地址
dev_flags 为传输过程中的控制标识位,其可用的值已在 am_i2c.h 中宏定义
I2C从机初始化
和MM32 MCU作为主机一样,AMetal平台也提供了作为I2C从机初始化函数, 可以直接调用初始化函数。函数原型为:
am_i2c_slv_handle_t    am_mm32l073_i2c1_slv_inst_init (void)

该函数在user_config目录下的am_hwconf_mm32l073_i2c_slv.c文件中定义,在 am_mm32l073_inst_init.h中声明。因此使用定时器初始化函数时,需要包含头文件 am_mm32l073_inst_init.h。

初始化I2C,调用该函数时需要定义一个am_i2c_slv_device_t 类型的变量, 用于保存获取的I2C从机服务句柄,初始化程序为:
am_i2c_slv_handle_t slv_handle = am_mm32l073_i2c1_slv_inst_init ()
获取了I2C服务句柄后,还应该有一个描述I2C从设备的结构体,构造I2C设备函数的原型为:
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 为指向从机设备描述结构体的指针
handle 是与从设备关联的 I2C 标准服务操作句柄
p_cb_funs 是回调函数的函数指针
dev_addr 为从机设备地址
dev_flags 为从机设备特性
p_arg 指向回调函数参数


使用特权

评论回复
5
laocuo1142|  楼主 | 2021-6-15 10:32 | 只看该作者
其中 p_cb_funs 指向的回调函数结构体包括:
从机地址匹配时回调函数指针
获取一个发送字节回调函数指针
提交一个接收到的字节回调函数指针
停止传输回调函数指针
广播回调函数指针

使用特权

评论回复
6
laocuo1142|  楼主 | 2021-6-15 10:35 | 只看该作者
回调函数得用户定义,如果不需要某个回调函数,可以不定义并将其函数指针指向NULL。

在AMetal中提供了实例初始化、读操作和写操作等函数接口,用户不用操作底层,直接按照规范调用相关函数即可,下面我们将结合EEPROM操作来熟悉IIC的函数接口调用。
   

EEPROM操
AMetal 提供I2C接口EEPROM的驱动函数,可以适配不同型号不同容量的产品,下面将以FM24C02为例予以说明,其函数原型(am_ep24cxx.h)为:
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);

该函数意在获取器件实例句柄 24c02_handle,其中p_dev为指向 am_ep24cxx_dev_t类型实例的指针,p_devinfo为指向 am_ep24cxx_devinfo_t 类型实例信息的指针。

使用特权

评论回复
7
laocuo1142|  楼主 | 2021-6-15 10:37 | 只看该作者
01  实例   


单个FM24C02可以看作EP24Cxx的一个实例,EP24Cxx只是抽象了代表一个系列或同种类型的E²PROM芯片,显然多个24C02是EP24Cxx的多个实例。如果I2C总线上只外接一个FM24C02,定义 am_ep24cxx_dev_t 类型(am_ep24cxx.h)实例如下:
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)

其中,g_AT24C02_dev为用户自定义的实例,其地址作为p_dev的实参传递。如果同一个 I2C 总线上外接了2个FM24C02,需要定义 2 个实例。即:am_ep24cxx_dev_t g_24c02_dev0
am_ep24cxx_dev_t g_24c02_dev1

每个实例都要初始化,其每个实例的初始化均会返回一个该实例的handle,便于使用其它接口函数时,传递不同的handle操作不同的实例。

使用特权

评论回复
8
laocuo1142|  楼主 | 2021-6-15 10:38 | 只看该作者
01实例信息
实例信息主要描述了具体器件固有的信息,即 I2C 器件的从机地址和具体型号,其类型am_ep24cxx_devinfo_t 的定义(am_ep24cxx.h)如下:
typedef struct am_ep24cxx_devinfo {
uint8_t     slv_addr;
uint32_t   type;
} am_ep24cxx_devinfo_t;

当前已经支持的器件型号均在 am_ep24cxx.h 中定义了对应的宏, 比如, FM24C02 对应的宏为 AM_EP24CXX_FM24C2,实例信息定义如下:
const am_ep24cxx_devinfo_t _g_24c02_devinfo = {
0x50;
AM_EP24CXX_FM24C02
}
其中,g_24c02_devinfo为用户自定义的实例信息,其地址作为p_devinfo的实参传递。

使用特权

评论回复
9
laocuo1142|  楼主 | 2021-6-15 10:38 | 只看该作者
02  I2C 句柄 I2C_handle
以I2C1为例, 其实例初始化函数 am_mm32l073_i2c1_inst_init()的返回值将作为实参传递给i2c_handle。即:
i2c_handle = am_mm32l073_i2c1_inst_init()

使用特权

评论回复
10
laocuo1142|  楼主 | 2021-6-15 10:39 | 只看该作者
03实例句柄 fm24c02_handle


FM24C02初始化函数 am_ep24cxx_init()的返回值 fm24c02_handle,作为实参传递给读ARM 嵌入式软件工程方法和实践:
写数据函数,其类型 am_ep24cxx_handle_t(am_ep24cxx.h)定义如下:
typedef struct am_ep24cxx_dev *am_ep24cxx_handle_t

若返回值为NULL,说明初始化失败;若返回值不为NULL,说明返回一个有效的 handle。

基于模块化编程思想,将初始化相关的实例信息等的定义存放到对应的配置文件中,通过头文件引出实例初始化函数接口。
实例初始化函数范例程序:
#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; //定义 FM24C02 器件实例
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);
}

使用特权

评论回复
11
laocuo1142|  楼主 | 2021-6-15 10:40 | 只看该作者
实例初始化函数接口:
#pragma once
#include "ametal.h"
#include "am_ep24cxx.h"
am_ep24cxx_handle_t am_fm24c02_inst_init(void);

后续只需要使用无参数的实例初始化函数,即可获取到 FM24C02 的实例句柄。即:
am_ep24cxx_handle_t fm24c02_handle =am_fm24c02_inst_init();

注意, i2c_handle 用于区分 I2C0、 I2C1、 I2C2、 I2C3,初始化函数返回值实例句柄用于区分同一系统中连接多个器件。

使用特权

评论回复
12
laocuo1142|  楼主 | 2021-6-15 10:43 | 只看该作者
本帖最后由 laocuo1142 于 2021-6-15 10:48 编辑

表1 ep24cxx 读写函数

各 API 的返回值含义都是相同的:AM_OK 表示成功,负值表示失败,失败原因可根据具体的值查看 am_errno.h 文件中相对应的宏定义。正值的含义由各 API 自行定义,无特殊说明时,表明不会返回正值。
   


使用特权

评论回复
13
laocuo1142|  楼主 | 2021-6-15 10:45 | 只看该作者
本帖最后由 laocuo1142 于 2021-6-15 10:48 编辑

2
数据读写
01
写入数据
从指定的地址开始写入一段数据的函数原型为:
int am_ep24cxx_write (am_ep24cxx_handle_t  handle,
int                   start_addr,
uint8_t               *p_buf,
int                   len);

如果返回值为AM_OK,则说明写入成功,反之失败。假定从0x20地址开始,连续写入16字节。

写入数据范例程序: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);

使用特权

评论回复
14
laocuo1142|  楼主 | 2021-6-15 10:50 | 只看该作者

02
读取数据
从指定的起始地址开始读取一段数据的函数原型为:
int am_ep24cxx_read (am_ep24cxx_handle_t handle,
int                  start_addr,
uint8_t              *p_buf,
int                  len);

如果返回值为 AM_OK,则说明读取成功,反之失败。假定从 0x20 地址开始,连续读取数据范例程序为:
uint8_t data[16];
am_ep24cxx_read(fm24c02_handle, 0x20, &data[0],16);


使用特权

评论回复
15
laocuo1142|  楼主 | 2021-6-15 10:51 | 只看该作者

03
应用实例
E2PROM 读写测试, 向存储器写入20个字节数据再读出来,然后校验是否读写正常的范例。
#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++) //填充数据
data=i;
am_ep24cxx_write(handle,0,&data[0],20); //从 0 地址开始,连续写入 20 字节数据16 for(i=0;i<20;i++) //清零数据
data=0;
am_ep24cxx_read(handle,0,&data[0],20); //从 0 地址开始,连续读出 20 字节数据19 for(i=0;i<20;i++)
{ //比较数据
IF(data!=i)
return AM_ERROR;
}
return AM_OK;
}

int am_main(void)
{
am_ep24cxx_handle_t fm24c02_handle =am_fm24c02_inst_init();//获取24C02初始化实例句柄
if(app_test_ep24cxx(fm24c02_handle)!=AM_OK)
{
am_led_on(0);
}
while(1)
{
am_led_toggle(0);//翻转 LED
am_mdelay(100);
}
}

app_test_ep24cxx()的参数为实例 handle,与EP24Cxx器件具有依赖关系,因为没办法实现全兼容调用,用户可根据EEPROM信息设置对应的参数。


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1188

主题

5182

帖子

12

粉丝