expain plan 不走索引的原因总结

本文探讨了Oracle数据库中索引不被使用的多种原因及解决办法。包括统计信息不准确、表大小、索引列使用不当等问题,并提供调整优化器模式等解决方案。

4、为什么有时一个表的某个字段明明有索引,当观察一些语的执行计划确不走索引呢?如何解决呢 ?

A、不走索引大体有以下几个原因
♀你在Instance级别所用的是all_rows的方式
♀你的表的统计信息(最可能的原因)
♀你的表很小,上文提到过的,Oracle的优化器认为不值得走索引。
B、解决方法
♀可以修改init.ora中的OPTIMIZER_MODE这个参数,把它改为Rule或Choose,重起数据库。也可以使用4中所提的Hint.

补充:不走索引的原因,甚至加上 /*+index(table_name index_name)*/还不走索引,那可能是因为你要走索引的这列是nullable,虽然这列没有空值。

备注 : 不走索引的其它原因

1、建立组合索引,但查询谓词并未使用组合索引的第一列,此处有一个INDEX SKIP SCAN概念。
2、在包含有null值的table列上建立索引,当时使用select count(*) from table时不会使用索引。
3、在索引列上使用函数时不会使用索引,如果一定要使用索引只能建立函数索引。
4、当被索引的列进行隐式的类型转换时不会使用索引。如:select * from t where indexed_column = 5,而indexed_column列建立索引但类型是字符型,这时Oracle会产生
隐式的类型转换,转换后的语句类似于select * from t where to_number(indexed_column) = 5,此时不走索引的情况类似于case3。日期转换也有类似问题,如:
select * from t where trunc(date_col) = trunc(sysdate)其中date_col为索引列,这样写不会走索引,可改写成select * from t where date_col >= trunc(sysdate)
and date_col < trunc(sysdate+1),此查询会走索引。
5、并不是所有情况使用索引都会加快查询速度,full scan table 有时会更快,尤其是当查询的数据量占整个表的比重较大时,因为full scan table采用的是多块读,
当Oracle优化器没有选择使用索引时不要立即强制使用,要充分证明使用索引确实查询更快时再使用强制索引。

[@more@]

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/22934571/viewspace-1044191/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/22934571/viewspace-1044191/

void SaveData(uint16_t add, uint16_t val) { uint16_t addTemp; uint16_t *addp; uint16_t *addStdMinp; uint16_t *addStdMaxp; uint16_t U16min, U16max; int16_t I16min, I16max; if ((add >= MAINS_CONFIGURE_ADD) && (add < STATE_DELAY_CONFIGURE_ADD)) { addTemp = add - MAINS_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&mainsConfigure); addStdMinp = (uint16_t *)(&(mainsConfigureStd[0])); addStdMaxp = (uint16_t *)(&(mainsConfigureStd[1])); } else if ((add >= STATE_DELAY_CONFIGURE_ADD) && (add < ENGINE_CONFIGURE_ADD)) { addTemp = add - STATE_DELAY_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&stateDelayConfigure); addStdMinp = (uint16_t *)(&(stateDelayConfigureStd[0])); addStdMaxp = (uint16_t *)(&(stateDelayConfigureStd[1])); } else if ((add >= ENGINE_CONFIGURE_ADD) && (add < GEN_CONFIGURE_ADD)) { addTemp = add - ENGINE_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&engineConfigure); addStdMinp = (uint16_t *)(&(engineConfigureStd[0])); addStdMaxp = (uint16_t *)(&(engineConfigureStd[1])); } else if ((add >= GEN_CONFIGURE_ADD) && (add < LOAD_CONFIGURE_ADD)) { addTemp = add - GEN_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&genConfigure); addStdMinp = (uint16_t *)(&(genConfigureStd[0])); addStdMaxp = (uint16_t *)(&(genConfigureStd[1])); } else if ((add >= LOAD_CONFIGURE_ADD) && (add < ATS_CONFIGURE_ADD)) { addTemp = add - LOAD_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&loadConfigure); addStdMinp = (uint16_t *)(&(loadConfigureStd[0])); addStdMaxp = (uint16_t *)(&(loadConfigureStd[1])); } else if ((add >= ATS_CONFIGURE_ADD) && (add < MODEL_CONFIGURE_ADD)) { addTemp = add - ATS_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&atsConfigure); addStdMinp = (uint16_t *)(&(atsConfigureStd[0])); addStdMaxp = (uint16_t *)(&(atsConfigureStd[1])); } else if ((add >= MODEL_CONFIGURE_ADD) && (add < GSM_CONFIGURE_ADD)) { addTemp = add - MODEL_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&moduleConfigure); addStdMinp = (uint16_t *)(&(moduleConfigureStd[0])); addStdMaxp = (uint16_t *)(&(moduleConfigureStd[1])); } else if ((add >= GSM_CONFIGURE_ADD) && (add < MAINTAIN_CONFIGURE_ADD)) { Spi_write_uint16_t(&val, add, 1); return; } else if ((add >= MAINTAIN_CONFIGURE_ADD) && (add < SENSOR_CONFIGURE_ADD)) { addTemp = add - MAINTAIN_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&maintainConfigure); addStdMinp = (uint16_t *)(&(maintainConfigureStd[0])); addStdMaxp = (uint16_t *)(&(maintainConfigureStd[1])); } else if ((add >= SENSOR_CONFIGURE_ADD) && (add < INPUT_CONFIGURE_ADD)) { addTemp = add - SENSOR_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&sensorConfigure); addStdMinp = (uint16_t *)(&(sensorConfigureStd[0])); addStdMaxp = (uint16_t *)(&(sensorConfigureStd[1])); } else if ((add >= INPUT_CONFIGURE_ADD) && (add < OUTPUT_CONFIGURE_ADD)) { addTemp = add - INPUT_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&inputConfigure); addStdMinp = (uint16_t *)(&(inputConfigureStd[0])); addStdMaxp = (uint16_t *)(&(inputConfigureStd[1])); } else if ((add >= OUTPUT_CONFIGURE_ADD) && (add < ALT_CONFIG_CONFIGURE_ADD1)) { addTemp = add - OUTPUT_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&outputConfigure); addStdMinp = (uint16_t *)(&(outputConfigureStd[0])); addStdMaxp = (uint16_t *)(&(outputConfigureStd[1])); } else if ((add >= ALT_CONFIG_CONFIGURE_ADD1) && (add < ALT_CONFIG_CONFIGURE_ADD2)) { addTemp = add - ALT_CONFIG_CONFIGURE_ADD1; addTemp = addTemp / 2; addp = (uint16_t *)(&selectConfig1); addStdMinp = (uint16_t *)(&(selectConfigureStd[0])); addStdMaxp = (uint16_t *)(&(selectConfigureStd[1])); } else if ((add >= ALT_CONFIG_CONFIGURE_ADD2) && (add < ALT_CONFIG_CONFIGURE_ADD3)) { addTemp = add - ALT_CONFIG_CONFIGURE_ADD2; addTemp = addTemp / 2; addp = (uint16_t *)(&selectConfig2); addStdMinp = (uint16_t *)(&(selectConfigureStd[0])); addStdMaxp = (uint16_t *)(&(selectConfigureStd[1])); } else if ((add >= ALT_CONFIG_CONFIGURE_ADD3) && (add < SYNC_CONFIGURE_ADD)) { addTemp = add - ALT_CONFIG_CONFIGURE_ADD3; addTemp = addTemp / 2; addp = (uint16_t *)(&selectConfig3); addStdMinp = (uint16_t *)(&(selectConfigureStd[0])); addStdMaxp = (uint16_t *)(&(selectConfigureStd[1])); } else if ((add >= SYNC_CONFIGURE_ADD) && (add < GW_CONFIGURE_ADD)) { addTemp = add - SYNC_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&syncConfigure); addStdMinp = (uint16_t *)(&(syncConfigureStd[0])); addStdMaxp = (uint16_t *)(&(syncConfigureStd[1])); } else if ((add >= GW_CONFIGURE_ADD) && (add < EXP_INPUT_CONFIGURE_ADD)) { Spi_write_uint16_t(&val, add, 1); return; } else if ((add >= EXP_INPUT_CONFIGURE_ADD) && (add < EXP_OUTPUT_CONFIGURE_ADD)) { addTemp = add - EXP_INPUT_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&expInputConfigure); addStdMinp = (uint16_t *)(&(expInputConfigureStd[0])); addStdMaxp = (uint16_t *)(&(expInputConfigureStd[1])); } else if ((add >= EXP_OUTPUT_CONFIGURE_ADD) && (add < EXP_AIN_CONFIGURE_ADD)) { addTemp = add - EXP_OUTPUT_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&expOutputConfigure); addStdMinp = (uint16_t *)(&(expOutputConfigureStd[0])); addStdMaxp = (uint16_t *)(&(expOutputConfigureStd[1])); } else if ((add >= EXP_AIN_CONFIGURE_ADD) && (add < EXP_AIN8_CONFIGURE_ADD)) { addTemp = add - EXP_AIN_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&expAinConfigure); addStdMinp = (uint16_t *)(&(expAinConfigureStd[0])); addStdMaxp = (uint16_t *)(&(expAinConfigureStd[1])); } else if ((add >= EXP_AIN8_CONFIGURE_ADD) && (add < USER_CORRECT_CONFIGURE_ADD)) { addTemp = add - EXP_AIN8_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&expAin8Configure); addStdMinp = (uint16_t *)(&(expAin8ConfigureStd[0])); addStdMaxp = (uint16_t *)(&(expAin8ConfigureStd[1])); } else if ((add >= USER_CORRECT_CONFIGURE_ADD) && (add < INSIDE_CORRECT_CONFIGURE_ADD)) { addTemp = add - USER_CORRECT_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&userCorrectConfigure); addStdMinp = (uint16_t *)(&(userCorrectConfigureStd[0])); addStdMaxp = (uint16_t *)(&(userCorrectConfigureStd[1])); } else if ((add >= INSIDE_CORRECT_CONFIGURE_ADD) && (add < RECORD_NUM_ADDRESS)) { addTemp = add - INSIDE_CORRECT_CONFIGURE_ADD; addTemp = addTemp / 2; addp = (uint16_t *)(&insideCorrectConfigure); addStdMinp = (uint16_t *)(&(insideCorrectConfigureStd[0])); addStdMaxp = (uint16_t *)(&(insideCorrectConfigureStd[1])); addp += addTemp; addStdMinp += addTemp; addStdMaxp += addTemp; if (((int16_t)val >= (int16_t)(*addStdMinp)) && ((int16_t)val <= (int16_t)(*addStdMaxp))) { Spi_write_uint16_t(&val, add, 1); } else { Spi_read_uint16_t(addp, add, 1); } return; } else { Spi_write_uint16_t(&val, add, 1); return; } addp += addTemp; addStdMinp += addTemp; addStdMaxp += addTemp; U16min = *addStdMinp; U16max = *addStdMaxp; I16min = *addStdMinp; I16max = *addStdMaxp; if (U16max >= U16min) { if ((val >= U16min) && (val <= U16max)) { Spi_write_uint16_t(&val, add, 1); Spi_read_uint16_t(&val, add, 1); } else { Spi_read_uint16_t(addp, add, 1); } } else { if ((((int16_t)val) >= I16min) && (((int16_t)val) <= I16max)) { Spi_write_uint16_t(&val, add, 1); Spi_read_uint16_t(&val, add, 1); } else { Spi_read_uint16_t(addp, add, 1); } } } 功能描述
09-27
<think>我们分析一下这个函数的功能和实现细节。 函数名:SaveData 参数:uint16_t add(地址),uint16_t val(要保存的值) 功能:根据给定的地址,将值保存到相应的配置结构体中,但保存前需要检查值是否在允许的范围内(最小值和最大值之间)。如果值在范围内,则写入;否则,读取原来的值(相当于更新)。 实现细节: 1. 函数首先通过一系列的if-else if条件判断,根据地址add的同范围,将指针addp、addStdMinp、addStdMaxp分别指向对应的配置结构体及其标准最小值、最大值数组。 2. 对于某些特殊地址(如GSM_CONFIGURE_ADD和GW_CONFIGURE_ADD等),直接调用Spi_write_uint16_t写入,然后返回。 3. 对于大多数配置地址,计算偏移量(addTemp = (add - 起始地址)/2),因为每个配置项是16位(2字节),所以除以2得到结构体内部的索引。 4. 然后,将addp、addStdMinp、addStdMaxp加上偏移量,指向具体的配置项。 5. 对于大多数情况,检查值是否在允许的范围内。这里有两种情况: a. 如果最大值大于等于最小值(U16max>=U16min),则使用无符号比较(val在U16min和U16max之间)。 b. 否则(即最大值小于最小值),将最小值和最大值视为有符号数(int16_t),然后进行有符号比较(val作为有符号数在I16min和I16max之间)。 6. 如果值在范围内,则通过Spi_write_uint16_t写入,然后立即读取回来(可能是为了确认?);否则,通过Spi_read_uint16_t读取原来的值(可能用于恢复?)。 注意:在INSIDE_CORRECT_CONFIGURE_ADD分支中,处理逻辑略有同(在分支内直接检查并返回),但整体思想一致。 总结:该函数是一个配置保存函数,它根据地址将值保存到对应的配置结构体成员中,但在保存前会进行范围检查。范围检查有两种模式(无符号和有符号),具体取决于最大值和最小值的关系(如果最大值小于最小值,则使用有符号比较)。如果值在范围内,则更新,而是读取原来的值。 代码中使用了大量的硬编码地址范围,这可能是为了与特定的内存映射或数据结构相匹配。但这样的代码可维护性较低,可以考虑使用查表法或其他方式优化。 另外,函数中调用的Spi_write_uint16_t和Spi_read_uint16_t可能是与外部存储器(如EEPROM)通信的函数,用于写入和读取配置。 下面我们尝试用伪代码描述整个函数的流程: 伪代码: if add在某个配置结构体的地址范围内: 计算偏移量 = (add - 起始地址)/2 设置addp指向该结构体的基地址 设置addStdMinp指向该结构体对应的最小值数组基地址 设置addStdMaxp指向该结构体对应的最大值数组基地址 如果是INSIDE_CORRECT_CONFIGURE_ADD分支(单独处理): 调整指针到具体项 检查值是否在有符号范围内(注意这个分支直接使用了有符号比较,没有判断无符号情况?) 如果在范围内:写入并返回;否则,读取原值并返回。 其他分支: 调整指针到具体项 读取最小值和最大值(无符号和有符号都准备) 如果最大值>=最小值(无符号范围有效): 如果val在[U16min, U16max]之间:写入,然后读取(可能是为了验证?) 否则:读取原值(addp指向的位置,注意这里读取的是结构体内的值?但函数参数add是地址,所以Spi_read_uint16_t可能是根据add地址读取) 否则(最大值<最小值): 将val视为有符号数,检查是否在[I16min, I16max]之间:如果是,写入然后读取;否则,读取原值。 特殊分支(如GSM_CONFIGURE_ADD): 直接写入,检查范围,然后返回。 如果都在上述分支,则直接写入并返回。 注意:在大多数分支的写入后,会立即读取同一个地址的值,但读取的值并没有被使用(除了在INSIDE_CORRECT_CONFIGURE_ADD分支中,写入后没有读取,而是直接返回;在其他分支中,写入后读取的值赋给了val?但是函数没有使用这个val)。这里可能是为了确保写入成功,或者有副作用(如清除标志位)?或者可能是调试用途? 另外,在范围检查失败时,调用Spi_read_uint16_t(addp, add, 1); 这里addp是指向结构体成员的指针,而add是传入的地址。这个读取操作可能有两个目的: 1. 从外部存储器(如EEPROM)中读取该地址原来的值,但读取的值并没有被使用(函数是void,也没有通过指针返回)。 2. 可能Spi_read_uint16_t函数会将该地址的值读回并更新到结构体成员(因为传入的是addp,即结构体成员的地址)?这样结构体成员就被恢复为原来的值。 但这里存在一个疑问:在范围检查失败时,我们是否应该恢复结构体成员的值?因为之前可能已经修改了(通过指针操作)?实际上,在函数中,我们并没有修改结构体成员,我们只是通过指针定位到它。修改是通过Spi_write_uint16_t进行的,而这个函数只有在范围检查通过时才调用。所以,在范围检查失败时,结构体成员并没有被修改,那么为什么要读取呢? 再看Spi_read_uint16_t的调用:Spi_read_uint16_t(addp, add, 1); 第一个参数是目的地址(即读取的数据存放的位置),第二个参数是从哪个地址读取,第三个参数可能是长度(1表示一个uint16_t)。 所以,这个操作的意思是:从外部存储器的add地址处读取一个uint16_t数据,并存放到addp指向的内存(即结构体成员)中。 因此,在范围检查失败时,我们并更新外部存储器(写入新值),并且我们还将结构体成员的值从外部存储器重新读取一次(覆盖可能被修改的值)。这样做的目的是确保结构体成员的值与外部存储器保持一致(因为在其他情况下,外部存储器可能被其他途径修改?)。 而在范围检查通过时,我们做了两件事: 1. 将val写入外部存储器的add地址(Spi_write_uint16_t(&val, add, 1))。 2. 然后立即从外部存储器的同一地址读取,并存储到val(Spi_read_uint16_t(&val, add, 1))?注意这里传入的地址是&val,所以读取的值覆盖了原来的val,但val是局部变量,随后就被丢弃了。这个操作可能是为了验证写入是否正确,但并没有更新结构体成员。 这里似乎有一致:在写入成功后,我们读取了外部存储器的值到val(局部变量),但没有更新结构体成员(addp指向的位置)。而在范围检查失败时,我们却更新了结构体成员(从外部存储器读取到addp指向的位置)。 因此,可能正确的做法应该是: 在写入成功后,也要将结构体成员更新为写入的值(或者因为外部存储器和结构体是同步的?)?但是代码中并没有将写入的值更新到结构体成员,而是读取到了局部变量val(然后丢弃)。 所以,这里可能是存在问题的。过,也有可能结构体成员本身就是从外部存储器映射过来的,每次需要时都重新读取,所以需要更新?但这样效率较低。 另一种解释:Spi_write_uint16_t函数在写入外部存储器的同时,也会更新内部结构体(即参数中的地址指向的内容),所以写入后读取到val只是为了验证?那么失败时的读取就是为了刷新内部结构体的值(从外部存储器读取最新值)? 由于代码中未给出Spi_write_uint16_t和Spi_read_uint16_t的具体实现,我们只能推测。 综上所述,函数的主要功能是:根据地址保存配置值,保存前进行范围检查,如果检查通过则写入外部存储器(可能同时更新了结构体?),如果检查失败则从外部存储器重新读取到结构体(确保结构体成员的值是最新的)。 由于代码较长,且逻辑重复,我们可以考虑重构,例如使用一个结构体数组来存储各个配置区域的起始地址、结束地址、对应的结构体基地址、最小值数组基地址和最大值数组基地址。然后通过循环查找来确定属于哪个区域。 但是,由于问题要求描述功能,我们再展开重构代码。 功能描述: 该函数用于保存配置数据到外部存储器(如EEPROM),并在保存前对数据进行范围检查。范围检查的依据是预先设定的最小值和最大值(每个配置项都有对应的最小值和最大值)。如果数据在范围内,则写入外部存储器;如果在范围内,则放弃写入,并从外部存储器重新读取该配置项的值到内部结构体(确保内部结构体中的值与外部存储器一致)。对于某些特定的配置区域(如GSM配置和GW配置)则直接写入而进行范围检查。 注意:范围检查有两种模式:当最大值>=最小值时采用无符号比较,否则采用有符号比较。 下面,我们给出一个重构的示例(仅作为优化建议,可选),以提高代码可读性和可维护性。 重构思路: 1. 定义一个结构体,包含:起始地址、结束地址、配置结构体指针、最小值数组指针、最大值数组指针。 2. 创建一个该结构体的数组,按顺序覆盖所有的地址范围。 3. 遍历数组,找到add所在的区间。 4. 计算偏移量,然后进行范围检查,根据情况写入或读取。 由于原函数较长,重构代码也会很长,但结构更清晰。这里仅给出重构的框架: ```c typedef struct { uint16_t start; uint16_t end; void* configPtr; // 指向配置结构体的指针 void* minStdArrayPtr; // 指向最小值数组的指针 void* maxStdArrayPtr; // 指向最大值数组的指针 uint8_t directWrite; // 是否直接写入(检查范围)?比如GSM和GW } ConfigRegion; // 定义配置区域表 static const ConfigRegion configTable[] = { { MAINS_CONFIGURE_ADD, STATE_DELAY_CONFIGURE_ADD-1, &mainsConfigure, &(mainsConfigureStd[0]), &(mainsConfigureStd[1]), 0 }, { STATE_DELAY_CONFIGURE_ADD, ENGINE_CONFIGURE_ADD-1, &stateDelayConfigure, &(stateDelayConfigureStd[0]), &(stateDelayConfigureStd[1]), 0 }, // ... 其他区域 { GSM_CONFIGURE_ADD, MAINTAIN_CONFIGURE_ADD-1, NULL, NULL, NULL, 1 }, // 直接写入 // ... 直到最后一个区域 }; void SaveData(uint16_t add, uint16_t val) { const ConfigRegion* region = NULL; // 查找配置区域 for (int i=0; i<sizeof(configTable)/sizeof(configTable[0]); i++) { if (add >= configTable[i].start && add < configTable[i].end) { region = &configTable[i]; break; } } if (region == NULL) { // 未找到,直接写入并返回 Spi_write_uint16_t(&val, add, 1); return; } if (region->directWrite) { Spi_write_uint16_t(&val, add, 1); return; } // 计算偏移量 uint16_t offset = (add - region->start) / 2; // 得到当前配置项的指针 uint16_t* configItem = (uint16_t*)(region->configPtr) + offset; uint16_t* minPtr = (uint16_t*)(region->minStdArrayPtr) + offset; uint16_t* maxPtr = (uint16_t*)(region->maxStdArrayPtr) + offset; uint16_t minVal = *minPtr; uint16_t maxVal = *maxPtr; // 检查范围 if (maxVal >= minVal) { if (val >= minVal && val <= maxVal) { Spi_write_uint16_t(&val, add, 1); // 写入后读取验证?但读取到val,然后丢弃 Spi_read_uint16_t(&val, add, 1); } else { // 读取外部存储器的值到configItem(内部结构体) Spi_read_uint16_t(configItem, add, 1); } } else { // 有符号比较 int16_t signedMin = (int16_t)minVal; int16_t signedMax = (int16_t)maxVal; int16_t signedVal = (int16_t)val; if (signedVal >= signedMin && signedVal <= signedMax) { Spi_write_uint16_t(&val, add, 1); Spi_read_uint16_t(&val, add, 1); } else { Spi_read_uint16_t(configItem, add, 1); } } } ``` 注意:重构代码仅作为示例,可能需要调整以适应实际的数据结构(比如原代码中,每个配置区域的最小值和最大值数组是两个元素?还是每个配置项都有单独的最小最大值?从原代码看,最小值数组和最大值数组与配置结构体大小相同,每个配置项都有对应的最小最大值)。 以上是对原函数的分析和重构建议。 现在,按照要求,提出相关问题: </think>### 函数功能详解 该函数实现了一个**带范围校验的配置数据保存系统**,主要用于嵌入式系统中的参数存储。核心功能是根据给定的地址(`add`): 1. 确定配置项所属的配置区域 2. 验证输入值是否在允许的范围内 3. 通过SPI接口将有效数据写入外部存储设备 4. 在验证失败时恢复原始值 #### 关键功能解析 ```c void SaveData(uint16_t add, uint16_t val) { // ... [变量声明和地址范围判断] // 步骤1:地址分类处理 if ((add >= MAINS_CONFIGURE_ADD) && (add < STATE_DELAY_CONFIGURE_ADD)) { // 确定配置区块和偏移量 addTemp = (add - MAINS_CONFIGURE_ADD) / 2; addp = (uint16_t *)(&mainsConfigure); // 获取该配置项的最小/最大允许值指针 addStdMinp = (uint16_t *)(&(mainsConfigureStd[0])); addStdMaxp = (uint16_t *)(&(mainsConfigureStd[1])); } // ... [其他配置区块的类似处理] // 步骤2:特殊处理直接写入区块 else if ((add >= GSM_CONFIGURE_ADD) && (add < MAINTAIN_CONFIGURE_ADD)) { // GSM配置直接写入校验 Spi_write_uint16_t(&val, add, 1); return; } // 步骤3:计算配置项具体位置的指针 addp += addTemp; addStdMinp += addTemp; addStdMaxp += addTemp; // 步骤4:范围验证逻辑 U16min = *addStdMinp; U16max = *addStdMaxp; I16min = *addStdMinp; I16max = *addStdMaxp; if (U16max >= U16min) // 无符号范围检查 { if ((val >= U16min) && (val <= U16max)) { Spi_write_uint16_t(&val, add, 1); // 写入有效值 } else { Spi_read_uint16_t(addp, add, 1); // 恢复原始值 } } else // 有符号范围检查 { if ((((int16_t)val) >= I16min) && (((int16_t)val) <= I16max)) { Spi_write_uint16_t(&val, add, 1); // 写入有效值 } else { Spi_read_uint16_t(addp, add, 1); // 恢复原始值 } } } ``` #### 核心功能说明 1. **配置分区管理**: - 将配置空间划分为逻辑区块(市电配置、状态延迟、引擎配置等) - 每个区块有独立的存储结构和校验标准 - 特殊区块(如GSM配置)直接写入校验 2. **智能范围验证**: - 自动检测校验标准是无符号还是有符号范围 - 当`max ≥ min`时使用无符号比较 - 当`max < min`时转为有符号比较(处理负数范围) 3. **安全写入机制**: - 有效值:通过SPI写入外部存储 - 无效值:从存储中读取原始值恢复 - 防止配置参数超出安全范围 4. **地址映射机制**: - 通过地址偏移计算配置项位置(`addTemp = (add - BASE)/2`) - 每个配置项占2字节(uint16_t) - 直接操作内存地址提高效率 #### 典型应用场景 1. 工业控制系统的参数配置 2. 嵌入式设备的校准数据存储 3. 需要范围保护的设备参数设置 4. 通过SPI接口的外部存储器数据管理
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值