电子说
开发者的最大问题是核心域和非核心域不分,大部分时间都在编写不可重用的和非核心域的代码。没有聚焦提升产品竞争力的核心域知识,比如,需求、算法、用户体验和软件工程方法等方面,从而导致代码维护的成本远远大于初期的开发投入。
事实上,那些做出优秀产品的团队,不仅员工队伍非常稳定,而且收入也很高,甚至连精神面貌都不一样。因为他们使用了正确的开发策略和方法,而且短时间内掌握的技术远胜那些所谓的“老程序员”。虽然每个企业都有拿高薪的员工,但为何不是你?别人开发的产品大卖,而你开发的产品却卖不掉?不仅浪费了来之不易的资金,而且导致我们失去了更多的创造更大价值的机会。
十几年前,作者也面临同样的问题,于是毫不犹豫地投身于软硬件标准化平台技术的开发,因为只有方法的突破才能开创未来。AWorks 就是在这样的背景下诞生的,脱胎于Aworks—Nano 子集的AMetal 不仅实现了跨平台,而且还定义了外围器件的软件接口标准,因此“按需定制”为用户提供有价值的服务也就成为了现实。
基于此,ZLG 为用户提供了大量标准的外设驱动与相关的协议组件,意在建立完整的生态系统。无论你选择什么MCU,只要支持AMetal,都可实现“一次编程、终生使用”,其好处是你再也不要重新发明轮子。
6.1 E2PROM 存储器
E2PROM(Electrically Erasable Programable Read-Only Memory,电可擦除可编程只读存储器)是一种掉电后数据不丢失的存储芯片,本节以FM24C02 为例详细介绍在AMetal 中如何使用类似的非易失存储器。
>>> 6.1.1 器件简介
FM24C02 总容量为2K(2048)bits,即256(2048/8)字节。每个字节对应一个存储地址,因此其存储数据的地址范围为0x00 ~ 0xFF。FM24C02 页(page)的大小为8 字节,每次写入数据不能越过页边界,即地址0x08、0x10、0x18……;如果写入数据越过页边界时,则必须分多次写入,其组织结构详见表6.1。
表6.1 FM24C02 存储器组织结构
FM24C02 的通信接口为标准的I2C 接口,仅需SDA 和SCL 两根信号线。这里以8PIN SOIC 封装为例,详见图6.1。其中的WP 为写保护,当该引脚接高电平时,将阻止一切写入操作。一般来说,该引脚直接直接接地,以便芯片正常读写。
图6.1 FM24C02 引脚定义
A2、A1、A0 决定了FM24C02 器件的I2C 从机地址,其7-bit 从机地址为0101 0A2A1A0。如果I2C 总线上仅有一片FM24C02,则将A2、A1、A0 直接接地,其地址为0x50。
在AMetal 中,由于用户无需关心读/写方向位的控制,因此其地址使用7-bit 地址表示。MicroPort-EEPROM 模块通过MicroPort 接口与AM824-Core 相连,详见图6.2,其中的E2PROM 是复旦微半导体提供的256 个字节FM24C02C。
图6.2 E2PROM 电路原理图
>>> 6.1.2 初始化
AMetal 提供了支持FM24C02 、FM24C04 、FM24C08……等系列I2C 接口E2PROM 的驱动函数,下面将以FM24C02 为例予以说明,其函数原型(am_ep24cxx.h)为:
该函数意在获取器件实例句柄fm24c02_handle,其中,p_dev 为指向am_ep24cxx_dev_t类型实例的指针,p_devinfo 为指向am_ep24cxx_devinfo_t 类型实例信息的指针。
1. 实例
单个FM24C02 可以看作EP24Cxx 的一个实例,EP24Cxx 只是抽象了代表一个系列或同种类型的E2PROM 芯片,显然多个FM24C02 是EP24Cxx 的多个实例。如果I2C 总线上只外接了一个FM24C02,定义am_ep24cxx_dev_t 类型(am_ep24cxx.h)实例如下:
其中,g_at24c02_dev 为用户自定义的实例,其地址作为p_dev 的实参传递。如果同一个I2C 总线上外接了2 个FM24C02,需要定义3 个实例。即:
每个实例都要初始化,且每个实例的初始化均会返回一个该实例的handle。便于使用其它接口函数时,传递不同的handle 操作不同的实例。
2. 实例信息
实例信息主要描述了具体器件固有的信息,即I2C 器件的从机地址和具体型号,其类型am_ep24cxx_devinfo_t 的定义(am_ep24cxx.h)如下:
当前已经支持的器件型号均在am_ep24cxx.h 中定义了对应的宏,比如,FM24C02 对应的宏为AM_EP24CXX_FM24C02,实例信息定义如下:
其中,g_24c02_devinfo 为用户自定义的实例信息,其地址作为p_devinfo 的实参传递。
3. I2C 句柄i2c_handle
以I2C1 为例,其实例初始化函数am_lpc82x_i2c1_inst_init()的返回值将作为实参传递给i2c_handle。即:
4. 实例句柄fm24c02_handle
FM24C02 初始化函数am_ep24cxx_init ()的返回值fm24c02_handle,作为实参传递给读写数据函数,其类型am_ep24cxx_handle_t(am_ep24cxx.h)定义如下:
若返回值为NULL,说明初始化失败;若返回值不为NULL,说明返回一个有效的handle。
基于模块化编程思想,将初始化相关的实例和实例信息等的定义存放到对应的配置文件中,通过头文件引出实例初始化函数接口,源文件和头文件的程序范例分别详见程序清单6.1 和程序清单6.2。
程序清单6.1 实例初始化函数范例程序(am_hwconf_ep24cxx.c)
程序清单6.2 实例初始化函数接口(am_hwconf_ep24cxx.h)
后续只需要使用无参数的实例初始化函数,即可获取到FM24C02 的实例句柄。即:
注意,i2c_handle 用于区分I2C0、I2C1、I2C2、I2C3,初始化函数返回值实例句柄用于区分同一系统中连接的多个器件。
>>> 6.1.3 读写函数
读写EP24Cxx 系列存储器的函数原型详见表6.2。
表6.2 ep24cxx 读写函数(am_ep24cxx.h)
各API 的返回值含义都是相同的:AM_OK 表示成功,负值表示失败,失败原因可根据具体的值查看am_errno.h 文件中相对应的宏定义。正值的含义由各API 自行定义,无特殊说明时,表明不会返回正值。
1. 写入数据
从指定的起始地址开始写入一段数据的函数原型为:
如果返回值为AM_OK,则说明写入成功,反之失败。假定从0x20 地址开始,连续写入16 字节,详见程序清单6.3。
程序清单6.3 写入数据范例程序
2. 读取数据
从指定的起始地址开始读取一段数据的函数原型为:
如果返回值为AM_OK,则说明读取成功,反之失败。假定从0x20 地址开始,连续读取16 字节,详见程序清单6.4。
程序清单6.4 读取数据范例程序
如程序清单6.5 所示为写入20 个字节数据再读出来,然后比较是否相同的范例。
程序清单6.5 FM24C02 读写范例程序
由于app_test_ep24cxx()的参数为实例handle,与EP24Cxx 器件具有依赖关系,因此无法实现跨平台调用。
>>> 6.1.4 NVRAM 通用接口函数
由于FM24C02 等E2PROM 是典型的非易失存储器,因此使用NVRAM(非易失存储器)标准接口读写数据就无需关心具体的器件了。使用这些接口函数前,需将工程配置am_prj_config.h 的AM_CFG_NVRAM_ENABLE 宏的值设置为1,相关函数原型详见表6.3。
表6.3 NVRAM 通用接口函数
1. 初始化函数
NVRAM 初始化函数意在初始化FM24C02 的NVRAM 功能,以便使用NVRAM 标准接口读写数据。其函数原型为:
其中,ep24cxx 实例句柄fm24c02_handle 作为实参传递给handle,p_dev 为指向am_nvram_dev_t 类型实例的指针,p_dev_name 为分配给FM24C02 的一个NVRAM 设备名,便于其它模块通过该名字定位到FM24C02 存储器。
(1)实例(NVRAM 存储器)
NVRAM 抽象地代表了所有非易失存储器,FM24C02 可以看作NVRAM 存储器的一个具体实例。定义am_nvram_dev_t 类型(am_nvram.h)实例如下:
其中,g_24c02_nvram_dev 为用户自定义的实例,其地址作为p_dev 的实参传递。
(2)实例信息
实例信息仅包含一个由p_dev_name 指针指定的设备名。设备名为一个字符串,如"fm24c02"。初始化后,该名字就唯一的确定了一个FM24C02 存储器设备,如果有多个FM24C02,则可以命名为"fm24c02_0"、"fm24c02_1"、"fm24c02_2"……
基于模块化编程思想,将初始化FM24C02 为标准的NVRAM 设备的代码存放到对应的配置文件中,通过头文件引出相应的实例初始化函数接口,详见程序清单6.6 和程序清单6.7。
程序清单6.6 新增NVRAM 实例初始化函数(am_hwconf_ep24cxx.c)
程序清单6.7 am_hwconf_ep24cxx.h 文件更新
后续只需要使用无参数的实例初始化函数,即可完成NVRAM 设备初始化,将FM24C02初始化为名为"fm24c02"的NVRAM 存储设备。即:
2. 存储段的定义
NVRAM 定义了存储段的概念,读写函数均对特定的存储段操作。NVRAM 存储器可以被划分为单个或多个存储段。存储段的类型am_nvram_segment_t 定义(am_nvram.h)如下:
存储段的名字p_name 和单元号unit 可以唯一确定一个存储段,当名字相同时,则使用单元号区分不同的存储段。存储段的名字使得每个存储段都被赋予了实际的意义,比如,名为"ip"的存储段表示保存IP 地址的存储段,名为"temp_limit"的存储段表示保存温度上限值的存储段。seg_addr 为该存储段在实际存储器中的起始地址,seg_size 为该存储段的容量大小。p_dev_name 表示该存储段对应的实际存储设备的名字。
如需将存储段分配到FM24C02 上,则需将存储段中的p_dev_name 设定为"fm24c02"。后续针对该存储段的读写操作实际上就是对FM24C02 进行读写操作。为了方便管理,所有存储段统一定义在am_nvram_cfg.c 文件中,默认情况下存储段为空,其定义为:
在具有FM24C02 存储设备后,即可新增一些段的定义,如应用程序需要使用4 个存储段分别存储2 个IP 地址(4 字节×2)、温度上限值(4 字节)和系统参数(50 字节),对应的存储段列表(存储段信息的数组)定义如下:
为了使存储段生效,必须在系统启动时调用am_nvram_inst_init ()函数(am_nvram_cfg.h),其函数原型为:
该函数往往在板级初始化函数中调用,可以通过工程配置文件(am_prj_config.h)中的AM_CFG_NVRAM _ENABLE 宏对其进行裁剪,详见程序清单6.10。
程序清单6.8 在板级初始化中初始化NVRAM
NVRAM 初始化后,根据在am_nvram_cfg.c 文件中定义的存储段可知,共计增加了5个存储段,它们的名字、单元号和大小分别详见表6.4,后续即可使用通用的NVRAM 读写接口对这些存储段进行读写操作。
表6.4 定义的NVRAM 存储段
3. 写入数据
写入数据函数原型为:
其中,p_name 和unit 分别表示存储段的名字和单元号,确定写入数据的存储段,p_buf提供写入存储段的数据,offset 表示从存储段指定的偏移开始写入数据,len 为写入数据的长度。若返回值为AM_OK,则说明写入成功,反之失败。比如,保存一个IP 地址到IP 存储段,详见程序清单6.9。
程序清单6.9 写入数据范例程序
4. 读取数据
读取数据函数原型为:
其中,p_name 和unit 分别为存储段的名字和单元号,确定读取数据的存储段;p_buf保存从存储段读到的数据,offset 表示从存储段指定的偏移开始读取数据,len 为读取数据的长度。若返回值为AM_OK,则说明读取成功,反之失败。比如,从IP 存储段中读取出IP地址,详见程序清单6.10。
程序清单6.10 读取数据范例程序
现在编写NVRAM 通用接口的简单测试程序,测试某个存储段的数据读写是否正常。虽然测试程序是一个简单的应用,但基于模块化编程思想,最好还是将测试相关程序分离出来,程序实现和对应接口的声明详见程序清单6.11 和程序清单6.12。
程序清单6.11 测试程序实现(app_test_nvram.c)
程序清单6.12 接口声明(app_test_nvram.h)
将待测试的存储段(段名和单元号)通过参数传递给测试程序,NVRAM 通用接口对测试段读写数据。若读写数据的结果完全相等,则返回AM_OK,反之返回AM_ERROR。
由此可见,应用程序的实现不包含任何器件相关的语句,仅仅调用NVRAM 通用接口读写指定的存储段,因此该应用程序是跨平台的,在任何AMetal 平台中均可使用,进一步整合NVRAM 通用接口和测试程序的范例详见程序清单6.13。
程序清单6.13 NVRAM 通用接口读写范例程序
显然,NVRAM 通用接口赋予了名字的存储段,使得程序在可读性和可维护性方面都优于使用EP24Cxx 读写接口。而调用NVRAM 通用接口会耗费一定的内存和CPU 资源,特别是在要求效率很高或内存紧缺的场合,建议使用EP24Cxx 读写接口。
全部0条评论
快来发表一下你的评论吧 !