51开发板除了使用AT24C02,还有没有什么方法或者函数可以储存程序里的多个数据?

开发者的最大问题是核心域和非核心域不分,大部分时间都在编写不可重用的和非核心域的代码。没有聚焦提升产品竞争力的核心域知识,比如,需求、算法、用户体验和软件工程方法等方面,从而导致代码维护的成本远远大于初期的开发投入。

事实上,那些做出优秀产品的团队,不仅员工队伍非常稳定,而且收入也很高,甚至连精神面貌都不一样。因为他们使用了正确的开发策略和方法,而且短时间内掌握的技术远胜那些所谓的“老程序员”。虽然每个企业都有拿高薪的员工,但为何不是你?别人开发的产品大卖,而你开发的产品却卖不掉?不仅浪费了来之不易的资金,而且导致我们失去了更多的创造更大价值的机会。

十几年前,作者也面临同样的问题,于是毫不犹豫地投身于软硬件标准化平台技术的开发,因为只有方法的突破才能开创未来。AWorks 就是在这样的背景下诞生的,脱胎于Aworks—Nano 子集的AMetal 不仅实现了跨平台,而且还定义了外围器件的软件接口标准,因此“按需定制”为用户提供有价值的服务也就成为了现实。

基于此,ZLG 为用户提供了大量标准的外设驱动与相关的协议组件,意在建立完整的生态系统。无论你选择什么MCU,只要支持AMetal,都可实现“一次编程、终生使用”,其好处是你再也不要重新发明轮子。

FM24C02 总容量为2K(2048)bits,即256(2048/8)字节。每个字节对应一个存储地址,因此其存储数据的地址范围为0x00 ~ 0xFF。FM24C02 页(page)的大小为8 字节,每次写入数据不能越过页边界,即地址0x08、0x10、0x18……;如果写入数据越过页边界时,则必须分多次写入,其组织结构详见表6.1。

FM24C02 的通信接口为标准的I2C 接口,仅需SDA 和SCL 两根信号线。这里以8PIN SOIC 封装为例,详见图6.1。其中的WP 为写保护,当该引脚接高电平时,将阻止一切写入操作。一般来说,该引脚直接直接接地,以便芯片正常读写。

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

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

实例信息主要描述了具体器件固有的信息,即I2C 器件的从机地址和具体型号,其类型am_ep24cxx_devinfo_t 的定义(am_ep24cxx.h)如下:

当前已经支持的器件型号均在am_ep24cxx.h 中定义了对应的宏,比如,FM24C02 对应的宏为AM_EP24CXX_FM24C02,实例信息定义如下:

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

基于模块化编程思想,将初始化相关的实例和实例信息等的定义存放到对应的配置文件中,通过头文件引出实例初始化函数接口,源文件和头文件的程序范例分别详见程序清单6.1 和程序清单6.2。

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

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

读写EP24Cxx 系列存储器的函数原型详见表6.2。

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

从指定的起始地址开始写入一段数据的函数原型为:

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

程序清单6.3 写入数据范例程序

从指定的起始地址开始读取一段数据的函数原型为:

如果返回值为AM_OK,则说明读取成功,反之失败。假定从0x20 地址开始,连续读取16 字节,详见程序清单6.4。

程序清单6.4 读取数据范例程序

如程序清单6.5 所示为写入20 个字节数据再读出来,然后比较是否相同的范例。

由于FM24C02 等E2PROM 是典型的非易失存储器,因此使用NVRAM(非易失存储器)标准接口读写数据就无需关心具体的器件了。使用这些接口函数前,需将工程配置am_prj_config.h 的AM_CFG_NVRAM_ENABLE 宏的值设置为1,相关函数原型详见表6.3。

NVRAM 初始化函数意在初始化FM24C02 的NVRAM 功能,以便使用NVRAM 标准接口读写数据。其函数原型为:

(1)实例(NVRAM 存储器)

实例信息仅包含一个由p_dev_name 指针指定的设备名。设备名为一个字符串,如"fm24c02"。初始化后,该名字就唯一的确定了一个FM24C02 存储器设备,如果有多个FM24C02,则可以命名为"fm24c02_0"、"fm24c02_1"、"fm24c02_2"……

基于模块化编程思想,将初始化FM24C02 为标准的NVRAM 设备的代码存放到对应的配置文件中,通过头文件引出相应的实例初始化函数接口,详见程序清单6.6 和程序清单6.7。

后续只需要使用无参数的实例初始化函数,即可完成NVRAM 设备初始化,将FM24C02初始化为名为"fm24c02"的NVRAM 存储设备。即:

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_prj_config.h)中的AM_CFG_NVRAM _ENABLE 宏对其进行裁剪,详见程序清单6.10。

程序清单6.8 在板级初始化中初始化NVRAM

NVRAM 初始化后,根据在am_nvram_cfg.c 文件中定义的存储段可知,共计增加了5个存储段,它们的名字、单元号和大小分别详见表6.4,后续即可使用通用的NVRAM 读写接口对这些存储段进行读写操作。

其中,p_name 和unit 分别表示存储段的名字和单元号,确定写入数据的存储段,p_buf提供写入存储段的数据,offset 表示从存储段指定的偏移开始写入数据,len 为写入数据的长度。若返回值为AM_OK,则说明写入成功,反之失败。比如,保存一个IP 地址到IP 存储段,详见程序清单6.9。

程序清单6.9 写入数据范例程序

其中,p_name 和unit 分别为存储段的名字和单元号,确定读取数据的存储段;p_buf保存从存储段读到的数据,offset 表示从存储段指定的偏移开始读取数据,len 为读取数据的长度。若返回值为AM_OK,则说明读取成功,反之失败。比如,从IP 存储段中读取出IP地址,详见程序清单6.10。

程序清单6.10 读取数据范例程序

现在编写NVRAM 通用接口的简单测试程序,测试某个存储段的数据读写是否正常。虽然测试程序是一个简单的应用,但基于模块化编程思想,最好还是将测试相关程序分离出来,程序实现和对应接口的声明详见程序清单6.11 和程序清单6.12。

将待测试的存储段(段名和单元号)通过参数传递给测试程序,NVRAM 通用接口对测试段读写数据。若读写数据的结果完全相等,则返回AM_OK,反之返回AM_ERROR。

由此可见,应用程序的实现不包含任何器件相关的语句,仅仅调用NVRAM 通用接口读写指定的存储段,因此该应用程序是跨平台的,在任何AMetal 平台中均可使用,进一步整合NVRAM 通用接口和测试程序的范例详见程序清单6.13。

程序清单6.13 NVRAM 通用接口读写范例程序

显然,NVRAM 通用接口赋予了名字的存储段,使得程序在可读性和可维护性方面都优于使用EP24Cxx 读写接口。而调用NVRAM 通用接口会耗费一定的内存和CPU 资源,特别是在要求效率很高或内存紧缺的场合,建议使用EP24Cxx 读写接口。

}

本期实验目标:采用硬件I2C模块与OLED进行通信,完成显示控制。

目前发现28335比较缺乏关于I2C模块的实用例程,许多新手在配置使用该模块比较难上手,走弯路。那么通过该例程,大家则可以快速学会使用I2C的基本功能,避免踩坑,让I2C模块运行起来吧!

本节仍然将分为硬件部分、软件部分和实验展示三个方面进行介绍。

我们采用中景园电子的0.96寸OLED模块,它的控制芯片是SSD1306,如图1所示。

由于该OLED模块默认是4线SPI的,为了换成支持I2C方式,从图中我们把电阻R1、R4、R6、R7焊上4.7k的电阻,R3电阻去掉,R8电阻用0欧短接起来即可。


表1给出OLED模块的管脚定义,其中OLED在I2C通信方式中,管脚DC不再是用于数据和命令选择,它是用来作于从机地址。在SSD1306的数据手册中,图2表示了它的7位从机地址与SA0有关,而SA0又是DC管脚的电平状态,在这里我们DC接地,则7位从机地址为0x3C。

注意:在硬件I2C里,R/W#是会自动发送的,我们不能把这一位当做从机地址的一部分,不清楚的朋友可能会认为从机地址是0x78(R/W#=0),但这是错的。因此我们在看数据手册应要注意给出的地址有没有包括R/W#!

不懂I2C的具体工作原理的,百度搜一下非常多,这里不在赘述。接下来是与DSP28335的连线,图3给出硬件连线图。

程序分为I2C的IO口和功能配置、RES输出管脚配置和OLED控制部分。(注意:查看代码时双击点进去看,否则会内容不全)。


 
 




 

oledfont.h(取模软件取模后的数组)


 
 
 
 

程序烧录进去后,大家可以看到这个效果图4,说明该程序可以通过硬件I2C的方式与OLED进行通信。

大家可以参考代码尝试一下, 有疑问的欢迎留言!!

}

      以按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值。这样做的效果是:如果有按键按下了,调用该read()函数的进程,就成功读取到数据,应用程序得到继续执行;倘若没有按键按下,则要一直处于休眠状态,等待这有按键按下这样的事件发生。

这种功能在一些场合是适用的,但是并不能满足我们所有的需要,有时我们需要一个时间节点。倘若没有按键按下,那么超过多少时间之后,也要返回超时错误信息,进程能够继续得到执行,而不是没有按键按下,就永远休眠。这种例子其实还有很多,比方说两人相亲,男方等待女方给个确定相处的信,男方不可能因为女方不给信,就永远等待下去,双方需要一个时间节点。这个时间节点,就是说超过这个时间之后,不能再等了,程序还要继续运行,需要采取其他的行动来解决问题。

      单片机编程,等待IIC设备一个事件的发生,如果在允许的时间内发生了就返回1(SUCCESS),否则返回0(ERROR)。

,如果在限定的时间内(CPU将100000减到0),还没有成功写入,那么就将返回超时错误,页写函数也会返回写入失败的错误信息。之后,任务重新得到了运行。

      对于单片机这样通常单任务运行的状况,必须采取这样的措施。如果没有超时限制,那么程序将陷入死机,不能再继续运行。

     对于类似的场景,linux系统使用poll功能来解决这样的问题。而且,与上述单片机等待方式不同,linux系统再调用poll()函数时候,如果没有发生需要的事件,那么进程进入休眠。如果在限定的时间内得到需要的事件,那么成功返回,如果没有则返回超时错误信息。

     可见,等待期间将进程休眠,利用事件驱动来唤醒进程,将更能提高CPU的效率。下面,以一个应用例程来说明poll的应用程序使用方法:

       例程实现的功能是这样的:用poll()函数监测按键按下的事件,如果按下了就将键值打印出来;如果超过5S,还没有按键按下,就打印出超时信息。

fds         可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回


/* 根据实际情况,标记事件类型 */
/* 如果mask为0,那么证明没有请求事件发生;如果非零说明有时间发生 */

      上述代码展示了一个poll()函数功能,具体对应的底层驱动实现细节。利用这样的框架,我们可以写出类似驱动的poll功能。但是,这个框架很难理解,不知道为什么这样编写?为此,我们需要了解linux系统poll功能实现的机制。

     从应用程序调用poll()函数开始,一直到调用drivers_poll函数,期间的过程很复杂,捡主要的内容列出来:

      由此可见,我们的drivers_poll()函数,是系统在执行sys_poll()过程中的一个调用,调用的目的是“将进程挂接到等待队列下”和“返回事件类型mask”。当已经发生了请求事件,那么通过标记mask非0,if (do_pollfd(pfd, pt))判断为真,令count++,从而可以直接令poll()函数成功返回。如果还没有发生请求的事件,那么mask被标记为0,进程将通过函数poll_schedule_timeout()陷入休眠状态。一旦发生了请求的事件,因为之前已经将进程挂接到等待队列下,所以进程将被唤醒,重新执行drivers_poll(),而显然此时能够成功返回。

}

我要回帖

更多关于 keil5里面没有51单片机型号怎么办 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信