打印
[MM32软件]

(分享)灵动微课堂:基于MM32 MCU的OS移植与应用——AMetal ...

[复制链接]
1783|51
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
jcky001|  楼主 | 2021-5-19 16:23 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
上一章节与大家一起分享了基于AMetal平台UART配置,相信结合前面几章的学习,大家对AMetal的程序构架和控制流程有了一个大概的了解,接下来我们将MM32 MCU结合其他电子元件来继续熟悉AMetal平台。一般我们平时说到两线的通信端口,支持多个从机、在小数据量场合使用、通常用于与EEPROM或oled屏等进行通信的,相信大家首先会想到I2C接口,今天我们一起来熟悉基于AMetal平台I2C接口控制。

使用特权

评论回复
沙发
jcky001|  楼主 | 2021-5-19 16:23 | 只看该作者
MM32L0系列包含一个硬件I2C接口,支持主从模式,能控制所有符合I2C总线协议的设备。I2C可以工作在标准模式(数据传输速率为0∼100 Kbps),快速模式(数据传输速率最大为400 Kbps)。

欲了解更多详细参数包括其他功能,欢迎大家登录灵动微电子官网(www.mm23mcu.com)下载、获取相关型号的技术手册,在这里我们不做过多的描述。

I2C 总线在传送数据过程中共有三种类型信号,它们分别是:开始信号、结束信号和应答信号。

使用特权

评论回复
板凳
jcky001|  楼主 | 2021-5-19 16:25 | 只看该作者
当总线处于空闲状态时,SCL和SDA同时被外部上拉电阻拉为高电平。当主机启动数据传输时,必须先产生一个起始条件。在SCL线是高电平时,SDA线从高电平向低电平切换表示起始条件。当主机结束传输时要发送停止条件。在SCL线是高电平时,SDA线由低电平向高电平切换表示停止条件。下图显示了起始和停止条件的时序图。数据传输过程中,当SCL为1时,SDA必须保持稳定。

图1 起始和停止条件



使用特权

评论回复
地板
jcky001|  楼主 | 2021-5-19 16:26 | 只看该作者
eMiniBoard板载一颗24C02,我们接下来一起进入I2C实战操作过程,通过MM32 MCU的硬件I2C接口来与24C02进行双向通信。

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



使用特权

评论回复
5
jcky001|  楼主 | 2021-5-19 16:29 | 只看该作者
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()

使用特权

评论回复
6
jcky001|  楼主 | 2021-5-19 16:30 | 只看该作者
获取了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 中宏定义

使用特权

评论回复
7
jcky001|  楼主 | 2021-5-19 16:31 | 只看该作者
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 ()

使用特权

评论回复
8
jcky001|  楼主 | 2021-5-19 16:33 | 只看该作者
获取了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 指向回调函数参数


其中 p_cb_funs 指向的回调函数结构体包括:
从机地址匹配时回调函数指针
获取一个发送字节回调函数指针
提交一个接收到的字节回调函数指针
停止传输回调函数指针
广播回调函数指针


回调函数得用户定义,如果不需要某个回调函数,可以不定义并将其函数指针指向NULL。

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

使用特权

评论回复
9
jcky001|  楼主 | 2021-5-19 16:36 | 只看该作者
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 类型实例信息的指针。

使用特权

评论回复
10
jcky001|  楼主 | 2021-5-19 16:44 | 只看该作者
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操作不同的实例。

使用特权

评论回复
11
jcky001|  楼主 | 2021-5-19 16:48 | 只看该作者
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的实参传递。

使用特权

评论回复
12
jcky001|  楼主 | 2021-5-19 16:49 | 只看该作者
02
I2C 句柄 I2C_handle
以I2C1为例, 其实例初始化函数 am_mm32l073_i2c1_inst_init()的返回值将作为实参传递给i2c_handle。即:
i2c_handle = am_mm32l073_i2c1_inst_init()

使用特权

评论回复
13
jcky001|  楼主 | 2021-5-19 16:50 | 只看该作者
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);
}

实例初始化函数接口:
#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,初始化函数返回值实例句柄用于区分同一系统中连接多个器件。

使用特权

评论回复
14
jcky001|  楼主 | 2021-5-19 16:52 | 只看该作者

表1 ep24cxx 读写函数

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

使用特权

评论回复
15
jcky001|  楼主 | 2021-5-19 16:53 | 只看该作者
02
数据读写
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);

使用特权

评论回复
16
jcky001|  楼主 | 2021-5-19 16:54 | 只看该作者
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);

使用特权

评论回复
17
jcky001|  楼主 | 2021-5-19 16:57 | 只看该作者
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信息设置对应的参数。

使用特权

评论回复
18
yangjiaxu| | 2021-5-23 17:37 | 只看该作者
AMetal平台是什么?

使用特权

评论回复
19
B1lanche| | 2021-5-27 22:46 | 只看该作者
感谢分享,STM32F1系列听说硬件I2C 有BUG ,MM32这方面咋样 没有BUG之类的问题吧

使用特权

评论回复
20
xietingfeng| | 2021-6-13 21:05 | 只看该作者
资料还是相当全面的                                 

使用特权

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

本版积分规则

1443

主题

4019

帖子

6

粉丝