在位阵列中有效地找到'1'的位置

时间:2012-02-15 15:14:29

标签: c embedded bit-manipulation avr

我正在连接一个测试一组电线的程序,用于开路或短路。该程序在AVR上运行,将测试向量(步进'1')驱动到导线上并接收结果。它将此结果向量与已存储在SD卡或外部EEPROM中的预期数据进行比较。

这是一个例子,假设我们有一组8条线,所有这些线都是直通的,即它们没有连接点。因此,如果我们驱动0b00000010,我们应该收到0b00000010。

假设我们收到0b11000010。这意味着线7,8和线2之间存在短路。我可以通过0b00000010 ^ 0b11000010 = 0b11000000检测我感兴趣的位。这告诉我显然线7和8有故障,但我如何在大位阵列中有效地找到这些'1'的位置。使用位掩码只需8线即可轻松完成此操作,但我正在开发的系统必须能够处理多达300线(位)。在我开始使用如下的宏并测试300 * 300位数组中的每个位之前,我想问一下是否有更优雅的解决方案。

 #define BITMASK(b) (1 << ((b) % 8))
 #define BITSLOT(b) ((b / 8))
 #define BITSET(a, b) ((a)[BITSLOT(b)] |= BITMASK(b))
 #define BITCLEAR(a,b) ((a)[BITSLOT(b)] &= ~BITMASK(b))
 #define BITTEST(a,b) ((a)[BITSLOT(b)] & BITMASK(b))
 #define BITNSLOTS(nb) ((nb + 8 - 1) / 8)

进一步说明如何检测开路。预期数据:0b00000010,接收数据:0b00000000(导线未拉高)。 0b00000010 ^ 0b00000000 = 0b0b00000010 - 电线2已打开。

注意:我知道测试300线不是AVR Mega 1281内部的微小RAM可以处理的,这就是为什么我将它分成组,即测试50线,比较,显示结果然后继续前进。

5 个答案:

答案 0 :(得分:3)

许多体系结构提供了用于定位字中第一个设置位或用于计算设置位数的特定指令。编译器通常为这些操作提供内在函数,因此您不必编写内联汇编。例如,GCC提供__builtin_ffs__builtin_ctz__builtin_popcount等,每个都应映射到目标体系结构上的相应指令,利用位级并行性。

如果目标体系结构不支持这些,则编译器会发出有效的软件实现。在软件中逐位测试矢量的天真方法效率不高。

如果您的编译器没有实现这些,您仍然可以使用de Bruijn sequence编写自己的实现。

答案 1 :(得分:1)

您多久会发生一次故障?如果你不经常发现它们,那么优化“存在故障”的情况似乎毫无意义 - 对速度而言真正重要的唯一部分是“无故障”情况。

要优化无故障情况,只需将实际结果与预期结果进行异或,并进行input ^ expected == 0测试以查看是否设置了任何位。

您可以使用类似的策略来优化“少数故障”情况,如果您进一步预期故障的数量通常会很小 - 当掩盖input ^ expected值以获得前8个故障时位,只是第二个8位,依此类推,并将每个结果与零进行比较。然后,您只需要搜索不等于零的设置位,这应该将搜索空间缩小到可以很快完成的事情。

答案 2 :(得分:0)

您可以使用查找表。例如,255字节的log-base-2查找表可用于查找字节中最重要的1位:

uint8_t bit1 = log2[bit_mask];

其中log2的定义如下:

uint8_t const log2[] = {
   0,               /* not used log2[0] */
   0,               /* log2[0x01] */
   1, 1             /* log2[0x02], log2[0x03] */
   2, 2, 2, 2,      /* log2[0x04],..,log2[0x07] */
   3, 3, 3, 3, 3, 3, 3, 3, /* log2[0x08],..,log2[0x0F */ 
   ... 
} 

在大多数处理器上,这样的查找表将转到ROM。但是AVR是哈佛机器并且将数据放置在代码空间(ROM)中需要特殊的非标准扩展,这取决于编译器。例如,IAR AVR编译器需要使用扩展关键字__flash。在WinAVR(GNU AVR)中,您需要使用PROGMEM属性,但它比这更复杂,因为您还需要使用特殊的宏来从程序空间读取。

答案 3 :(得分:0)

我认为只有一种方法可以做到这一点:

  • 创建一个数组“outdata”。例如,阵列的每个项目对应于8位端口寄存器。
  • 在电线上发送outdata。
  • 将此数据读回“indata”。
  • 将indata存储在与outdata完全相同的数组中。
  • 在一个循环中,将outdata的每个字节与indata的每个字节进行异或。

我强烈建议使用内联函数而不是那些宏。

为什么你的MCU不能处理300线?

300/8 = 37.5字节。圆形到38.它需要存储两次,outdata和indata,38 * 2 = 76字节。

你不能省去76个字节的RAM吗?

答案 4 :(得分:0)

我认为你错过了穿过树林的森林。好像是一张钉床测试。首先测试一些假设:  1)您知道每个测试/通电的引脚应该有哪些引脚。  2)您将第1步的网表翻译成sd

上的文件

如果您在字节级别和位上操作,则可以简化问题。如果激活引脚,则文件中会存储预期的模式。首先找到不匹配的字节;识别字节中不匹配的引脚;最后存储带有错误引脚编号的通电引脚。

您不需要数组进行搜索或结果。总体思路:

numwires=300;

numbytes=numwires/8 + (numwires%8)?1:0;

for(unsigned char currbyte=0; currbyte<numbytes; currbyte++)
{
   unsigned char testbyte=inchar(baseaddr+currbyte)
  unsigned char goodbyte=getgoodbyte(testpin,currbyte/*byte offset*/);
  if( testbyte ^ goodbyte){
  // have a mismatch report the pins
    for(j=0, mask=0x01; mask<0x80;mask<<=1, j++){
       if( (mask & testbyte) != (mask & goodbyte)) // for clarity
          logbadpin(testpin, currbyte*8+j/*pin/wirevalue*/, mask & testbyte /*bad value*/);

     }

}