NXP JN5169使用EEPROM/片上FLASH/随机数/内部NVM
JN5169 内存映射
一、EEPROM
1、EEPROM特性
JN5169器件包含4kB的EEPROM。 写入周期或耐久性的最大数量保证为100 k,通常为500 k,而数据保留至少保证10年。 该非易失性存储器主要用于保存从诸如网络堆栈软件组件(例如,网络拓扑,路由表)之类的东西生成的持久性数据。 由于EEPROM通过睡眠和复位事件来保持其内容,因此在中断之后可以实现更稳定的操作和更快的恢复。 通过映射到Flash的EEPROM和EEPROM寄存器区域的寄存器访问EEPROM地址图。
客户可以通过与持久数据管理器(PDM)接口来使用EEPROM的一部分来存储自己的数据。 可选地,PDM还可以将数据存储在外部存储器中。
该非易失性存储器用于存储在不给JN516x设备供电或在没有RAM的睡眠期间必须保留的数据。 空白(无数据)的EEPROM完全由二进制0组成。 当向其写入数据时,相关位从0变为1。提供了用于写入,读取和擦除EEPROM的功能。
尽管引用的功能可直接访问EEPROM设备,但出于以下原因,建议不要使用或谨慎使用它们:
- JenNet-IP和ZigBee节点使用JenOS持久数据管理器(PDM),该管理器还访问EEPROM。 PDM在NXPJenNet-IP和ZigBee SDK中提供,并且在《 JenOS用户指南》(JN-UG-3075)中进行了描述。使用PDM的优点包括:
- 通过“磨损平衡”来实现EEPROM的统一使用。
- 记录ID,以避免使用内存地址。
如果要同时使用PDM和EEPROM直接访问功能集,那么避免两者之间的冲突很重要,因此,它们绝不能访问EEPROM的同一部分。 - ZigBee RF4CE本身使用EEPROM直接访问功能,因此,必须避免冲突。
2、实现代码
/**
* EEPROM按每段64字节组成
* JN5169设备包含4kB的EEPROM
* 段从0开始索引,索引范围(0 ~ 255)
* EEPROM的最后一段保留用于生产数据,不能被写入或擦除。
*/
PUBLIC void AppColdStart (void)
{
uint8 write[64], read[64];
uint16 i, len;
uint8 pu8SegmentDatalength;
vAHI_WatchdogStop();
(void)u32AHI_Init();
vUartInit();
vAHI_DelayXms(2000);
for(i = 0; i < 64; i++){
write[i] = i + 64;
}
/**
* 为了从应用程序访问EEPROM,必须首先调用初始化函数u16AHI_InitialiseEEP()。
*/
len = u16AHI_InitialiseEEP(&pu8SegmentDatalength);
vPrintf("每个段的字节数 = %d\n", pu8SegmentDatalength);
vPrintf("设备中可用内存段数 = %d\n", len);//JN5169为255(0-255),256*64=4096=4kb
/*if(!iAHI_EraseEEPROMsegment(0)){ //0-255
vPrintf("擦除EEPROM段0的数据成功!");
}*/
//从段0的位置0开始,写入64个字节数据,不允许写入超出段末尾的数据,否则将返回“失败”状态。
if(!iAHI_WriteDataIntoEEPROMsegment(0, 0, write, 64)){
vPrintf("写入EEPROM中段0的数据为 = ");
for(i = 0; i < 64; i++){
vPrintf(" %x", write[i]);
}
vPrintf("\n");
}
while (1) {
//从段0的位置12开始,读取20个字节数据,不允许读取超出段末尾的数据,否则将返回“失败”状态。
if(!iAHI_ReadDataFromEEPROMsegment(0, 12, read, 20)){
vPrintf("EEPROM中段0的数据为 = ");
for(i = 0; i < 20; i++){
vPrintf(" %x", read[i]);
}
vPrintf("\n");
}
vAHI_DelayXms(2000);
}
}
PUBLIC void AppWarmStart (void)
{
AppColdStart();
}
3、串口格式化输出
#include <stdarg.h>
PRIVATE void vNum2String(uint32 u32Number, uint8 u8Base)
{
char buf[33];
char *p = buf + 33;
uint32 c, n;
*--p = '\0';
do {
n = u32Number / u8Base;
c = u32Number - (n * u8Base);
if (c < 10) {
*--p = '0' + c;
} else {
*--p = 'a' + (c - 10);
}
u32Number /= u8Base;
} while (u32Number != 0);
while (*p){
vPutChar(*p);
p++;
}
return;
}
PUBLIC void vPrintf(const char *fmt, ...)
{
char *bp = (char *)fmt;
va_list ap;
char c;
char *p;
int32 i;
va_start(ap, fmt);
while ((c = *bp++)) {
if (c != '%') {
if (c == '\n'){
vPutChar('\r');
vPutChar('\n');
} else {
vPutChar(c);
}
continue;
}
switch ((c = *bp++)) {
/* %d - show a decimal value */
case 'd':
vNum2String(va_arg(ap, uint32), 10);
break;
/* %x - show a value in hex */
case 'x':
vPutChar('0');
vPutChar('x');
vNum2String(va_arg(ap, uint32), 16);
break;
/* %b - show a value in binary */
case 'b':
vPutChar('0');
vPutChar('b');
vNum2String(va_arg(ap, uint32), 2);
break;
/* %c - show a character */
case 'c':
vPutChar(va_arg(ap, int));
break;
case 'i':
i = va_arg(ap, int32);
if(i < 0){
vPutChar('-');
vNum2String((~i)+1, 10);
} else {
vNum2String(i, 10);
}
break;
/* %s - show a string */
case 's':
p = va_arg(ap, char *);
do {
vPutChar(*p++);
} while (*p);
break;
/* %% - show a % character */
case '%':
vPutChar('%');
break;
/* %something else not handled ! */
default:
vPutChar('?');
break;
}
}
return;
}
串口代码: 串口代码
4、效果图
二、片上FLASH
1、片上FLASH特性
片上Flash由两部分组成:一个用于保存引导代码的8 kB区域和一个用于应用程序代码的512 kB区域。 写入周期或续航力的最大数量保证为10 k,通常为50 k,而数据保留至少保证10年。 引导代码区域由NXP在提供的零件上进行了预编程,并且包含用于处理复位,中断和其他事件的代码。 它还包含一个Flash编程接口,以允许与基于PC的Flash编程实用程序进行交互,该实用程序允许将使用提供的工具链编译的用户代码编程到Application空间中。
JN5169 微控制器具有片上 Flash 存储器。这种非易失性存储器用于存储二进制应用和相关的应用数据。JN5169 器件也可以选择连接到外部 Flash 存储器设备。
集成外设 API 包含了允许应用擦除、编程和读 Flash 存储器扇区的函数。通常,这些函数用于存储和取出应用数据,这可能包含了进入 RAM 不保持供电的睡眠模式之前在非易失性存储器中保存的数据。
Flash 存储器被划分为多个扇区。扇区数取决于 Flash 器件类型,但应用二进制通常存储在第一个扇区(也就是扇区 0)的起始位置,应用数据存储在最后的扇区中。空的(没有数据)Flash 存储器扇区内容全部是二进制 1。当数据被写入扇区时,相应的位从 1 变为 0。
下列表格给出了片上 Flash 存储器的细节,以及所支持的 JN516x 系列微控制器的外部 Flash器件。
2、实现代码
PUBLIC void AppColdStart (void)
{
uint8 i, write[64], read[64];
vAHI_WatchdogStop();
(void)u32AHI_Init();
vUartInit();
vAHI_DelayXms(2000);
for(i = 0; i < 64; i++){
write[i] = i + 64;
}
//调用的第一个 Flash 存储器函数必须是初始化函数 bAHI_FlashInit()。
//在外部 Flash 存储器的情况下,这个函数要求指定附加的 Flash 器件类型。
//使用片上FLASH
if(bAHI_FlashInit(E_FL_CHIP_INTERNAL, NULL)){
vPrintf("片上FLASH初始化成功!\n");
}
else{
vPrintf("片上FLASH初始化失败!\n");
}
/**
* 注意不要删除基本数据,例如应用程序代码。 从片上flash的开头(从扇区0开始)开始存储应用程序。
* JN5169扇区数16(范围0到15)
* JN516x器件中内部闪存的每个扇区均分为16字节的页面字。 不得执行对非空白页面字的写入
* 写入非空白页面字的扇区首先应使用bAHI_FlashEraseSector()擦除,然后再写入该页面字。
* 如果用户省略了扇区擦除操作,则从页字读取时可能会导致后续错误
* 此读取错误将触发中断并执行使用bAHI_FlashEECerrorInterruptSet()注册的回调函数。
* JN516x设备的内部闪存具有大约100ms的扇区擦除时间。
*/
if(bAHI_FlashEraseSector(15)){
vPrintf("擦除扇区15成功!\n");
}
else{
vPrintf("擦除扇区15失败!\n");
}
vAHI_DelayXms(150);
/**
* 该功能通过将1到0的相应位清零来对闪存块进行编程。该功能可用于访问兼容闪存设备的任何扇区。
* 此函数只能用于写入包含16个字节的倍数的数据块,并且该块必须写入16字节的边界。
* 此机制不允许将位设置为0到1。只能通过擦除整个扇区将比特设置为1
* 因此,在使用此功能之前,必须调用函数bAHI_FlashEraseSector()。
* JN516x器件的内部闪存的耐久性极限是每个扇区10000个写/擦除周期。
* 片上Flash绝对地址为0x00080000 ~ 0x00100000(512kB)
* 一个扇区大小为32768(0x8000),所以第15扇区绝对地址为0x000F8000 ~ 0x00100000
* 闪存功能中指定的所有闪存地址都是距闪存起始位置的偏移量,而不是绝对地址。
* 所以扇区偏移地址应该减去0x00080000,所以第15扇区偏移地址为0x00078000 ~ 0x00080000
*/
//向扇区15写入64个字节数据
if(bAHI_FullFlashProgram(0x00078000, 64, write)){
vPrintf("向扇区15写入数据成功!\n");
}
else{
vPrintf("向扇区15写入数据失败!\n");
}
vPrintf("写入扇区15的数据为 = ");
for(i = 0; i < 64; i++){
vPrintf(" %x", write[i]);
}
vPrintf("\n");
while (1) {
//从扇区15读取64个字节数据
bAHI_FullFlashRead(0x00078000, 64, read);
vPrintf("扇区15的数据为 = ");
for(i = 0; i < 64; i++){
vPrintf(" %x", read[i]);
}
vPrintf("\n");
vAHI_DelayXms(2000);
}
}
PUBLIC void AppWarmStart (void)
{
AppColdStart();
}
3、效果图
三、随机数
1、随机数特性
JN5169提供了一个随机数生成器,该生成器在每次调用它时都会创建一个16位随机数。 可以进行连续调用以建立任意长度的随机数。 每个通话大约需要0.25毫秒才能完成。 或者,可以使用连续生成模式,其中大约每0.25 ms生成一个新数字。 在任何一种操作模式下,都可以产生一个中断以指示该数字何时可用,或者可以轮询一个状态位。
随机位是通过每32 kHz系统时钟边沿对32 MHz时钟的状态进行采样而生成的。 由于这些时钟彼此异步,因此每个采样位都是不可预测的,因此是随机的。
JN516x器件具有一个随机数发生器,该发生器可以以两种模式之一产生16位随机数:
- 单发模式:生成器生成一个随机数并停止。
- 连续模式:发生器连续运行,每256µs产生一个新的随机数。
可以使用函数vAHI_StartRandomNumberGenerator()在以上任一模式下启动随机数生成器。 该函数还允许使能一个随机数时产生的中断,该中断由使用函数vAHI_SysCtrlRegisterCallback()注册的回调函数作为系统控制器中断处理。
随后可以使用函数u16AHI_ReadRandomNumber()读取随机生成的值。 可以使用以下两种方法之一确定新随机数的可用性以及因此需要调用“读取”功能:
- 等待随机数生成器中断(如果已启用)。
- 定期调用函数bAHI_RndNumPoll()以轮询是否有新的随机值。
在连续模式下运行时,可以使用函数vAHI_StopRandomNumberGenerator()停止随机数生成器。
注:随机数发生器使用32kHz时钟域,如果使用高精度的外部32kHz时钟源,它将无法正常工作。 因此,如果在应用中生成随机数,建议使用内部RC振荡器或低精度的外部时钟源。 在切换到高精度外部时钟之前,您还可以在应用程序中生成随机数。
2、实现代码
① 轮询模式
PUBLIC void AppColdStart (void)
{
uint16 random_num;
vAHI_WatchdogStop();
(void)u32AHI_Init();
vUartInit();
vAHI_DelayXms(2000);
//单发模式,关闭中断
vAHI_StartRandomNumberGenerator(E_AHI_RND_SINGLE_SHOT, E_AHI_INTS_DISABLED);
while(!bAHI_RndNumPoll());
random_num = u16AHI_ReadRandomNumber();
vPrintf("单发模式新的16位随机数为 = %d\n", random_num);
vAHI_DelayXms(2000);
//连续模式,关闭中断
vAHI_StartRandomNumberGenerator(E_AHI_RND_CONTINUOUS, E_AHI_INTS_DISABLED);
while (1) {
while(!bAHI_RndNumPoll());
random_num = u16AHI_ReadRandomNumber();
vPrintf("连续模式新的16位随机数为 = %d\n", random_num);
vAHI_DelayXms(2000);
}
}
PUBLIC void AppWarmStart (void)
{
AppColdStart();
}
效果图
② 中断模式
uint16 random_num;
PRIVATE void vCbRandomNumber(uint32 u32Device, uint32 u32ItemBitmap)
{
if(E_AHI_DEVICE_SYSCTRL == u32Device){ //系统控制中断
if(E_AHI_SYSCTRL_RNDEM_MASK == u32ItemBitmap){ //生成新的随机数中断
random_num = u16AHI_ReadRandomNumber();
vPrintf("连续模式(中断)新的16位随机数为 = %d\n", random_num);
vAHI_DelayXms(1000);
}
}
}
PUBLIC void AppColdStart (void)
{
vAHI_WatchdogStop();
(void)u32AHI_Init();
vUartInit();
vAHI_DelayXms(2000);
//连续模式,开启中断
vAHI_StartRandomNumberGenerator(E_AHI_RND_CONTINUOUS, E_AHI_INTS_ENABLED);
//注册中断回调函数
vAHI_SysCtrlRegisterCallback(vCbRandomNumber);
while (1) {
}
}
PUBLIC void AppWarmStart (void)
{
AppColdStart();
}
效果图
四、内部NVM
1、内部NVM特性
JN516x 器件包含一小块非易失性存储器(NVM),组成 4 个 32 位的字,编号为 0、1、2 和 3。这个存储器可以在 JN516x RAM 不工作时(例如,RAM 不保持的睡眠期间)用来保存重要数据(例如,计数器值)。
有两个函数用来访问这个存储器:
- vAHI_WriteNVData( ):用来将一个 32 位字的数据写入 4 个存储器单元中的一个;
- u32AHI_ReadNVData():用来从其中一个存储器单元中读出一个 32 位字的数据。
注:微控制器完全断电时这个 JN516x NVM 的内容不保留。但是,器件复位时 NVM 的内容保持不变。
2、实现代码
PUBLIC void AppColdStart (void)
{
uint32 nvm;
vAHI_WatchdogStop();
(void)u32AHI_Init();
vUartInit();
vAHI_DelayXms(2000);
vAHI_WriteNVData(0, 0x5863);
vAHI_WriteNVData(1, 0x425863);
vAHI_WriteNVData(2, 0x56325863);
vAHI_WriteNVData(3, 0x12345863);
nvm = u32AHI_ReadNVData(0);
vPrintf("NVM0的值为 = %x\n", nvm);
nvm = u32AHI_ReadNVData(1);
vPrintf("NVM1的值为 = %x\n", nvm);
nvm = u32AHI_ReadNVData(2);
vPrintf("NVM2的值为 = %x\n", nvm);
nvm = u32AHI_ReadNVData(3);
vPrintf("NVM3的值为 = %x\n", nvm);
while (1) {
}
}
PUBLIC void AppWarmStart (void)
{
AppColdStart();
}
效果图