使用AVR反转字节中的位

使用AVR反转字节中的位

本文介绍了使用AVR反转字节中的位的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在解决一个问题,希望我建立一个子例程来反转R16中的位.

00000011 => 11000000
or
10101000 => 00010101

对于该类,我们使用AVR子集,并且该子例程需要在norfair中工作.

这是我到目前为止所拥有的,任何帮助将不胜感激!

ldi r16,3 ;00000011
解决方案

天真的解决方案是使用移位运算符循环遍历各个位并进行检查.但是请注意,AVR没有桶形移位器,因此它只能移位1, 任何其他移位计数都需要多于1条指令.这是来自著名的bithacks页面的显而易见的解决方案.

 uint8_t reverse_obvious(uint8_t v)
{
    uint8_t r = v & 1; // r will be reversed bits of v; first get LSB of v
    uint8_t s = sizeof(v) * CHAR_BIT - 1; // extra shift needed at end

    for (v >>= 1; v; v >>= 1)
    {
        r <<= 1;
        r |= v & 1;
        s--;
    }
    r <<= s; // shift when v's highest bits are zero
    return r;
}
 

以上代码段仅使用移位1,最后一个r <<= s除外,后者需要在AVR中循环.您可以通过始终运行8个循环来避免这种情况

 uint8_t reverse(uint8_t x)
{
    uint8_t mask_up = 0x01;
    uint8_t mask_down = 0x80;
    uint8_t result = 0;
    for (; mask_down; mask_down >>= 1, mask_up <<= 1)
    {
        if (x & mask_up)
            result |= mask_down;
    }
    return result;
}
 

另一个偏移量为2的替代方法,但我想这是没有查找表的最佳方法. AVR有足够的可用ROM,因此这种方式应该效率更高

 uint8_t reverse(uint8_t x)
{
    x = (((x & 0xaaU) >> 1) | ((x & 0x55U) << 1));
    x = (((x & 0xccU) >> 2) | ((x & 0x33U) << 2));
    x = (((x & 0xf0U) >> 4) | ((x & 0x0fU) << 4));
    return x;
}
 


某些编译器还具有内置的反转位的功能.例如Clang具有 __builtin_bitreverse8() ,而GCC具有 __builtin_avr_insert_bits() 可用于反转位:

 // reverse the bit order of bits
__builtin_avr_insert_bits (0x01234567, bits, 0)
 

不幸的是它们的输出很糟糕


关于反转位,还有很多关于SO的很好的答案.尝试C代码转换为组装并与

I am currently working on a problem that wants me to build a subroutine that will reverse the bits in R16.

00000011 => 11000000
or
10101000 => 00010101

For the class we are using the AVR subset and the subroutine needs to work in norfair.

This is what I have so far, any help would be appreciated!

ldi r16,3 ;00000011
解决方案

The naive solution is to loop through the bits with the shift operator and check. But be aware that AVR doesn't have a barrel shifter so it can only shift by 1, any other shift counts need more than 1 instruction. Here's one obvious solution from the famous bithacks page

uint8_t reverse_obvious(uint8_t v)
{
    uint8_t r = v & 1; // r will be reversed bits of v; first get LSB of v
    uint8_t s = sizeof(v) * CHAR_BIT - 1; // extra shift needed at end

    for (v >>= 1; v; v >>= 1)
    {
        r <<= 1;
        r |= v & 1;
        s--;
    }
    r <<= s; // shift when v's highest bits are zero
    return r;
}

The above snippet uses only shifts by 1 except the last r <<= s which needs a loop in AVR. You can avoid that by always running 8 loops

uint8_t reverse(uint8_t x)
{
    uint8_t mask_up = 0x01;
    uint8_t mask_down = 0x80;
    uint8_t result = 0;
    for (; mask_down; mask_down >>= 1, mask_up <<= 1)
    {
        if (x & mask_up)
            result |= mask_down;
    }
    return result;
}

Another alternative that has shift by 2, but I guess it's the best way you can do without a lookup table. AVR has plenty of available ROM for the table so it should be a lot more efficient that way

uint8_t reverse(uint8_t x)
{
    x = (((x & 0xaaU) >> 1) | ((x & 0x55U) << 1));
    x = (((x & 0xccU) >> 2) | ((x & 0x33U) << 2));
    x = (((x & 0xf0U) >> 4) | ((x & 0x0fU) << 4));
    return x;
}


Some compilers also have built-ins to reverse bits. For example Clang has __builtin_bitreverse8() and GCC has __builtin_avr_insert_bits() which can be used to reverse bits:

// reverse the bit order of bits
__builtin_avr_insert_bits (0x01234567, bits, 0)

Unfortunately their outputs are terrible


There are also lots of questions with good answers on SO about reversing bits. Try converting the C code to assembly and compare with the result on compiler explorer

这篇关于使用AVR反转字节中的位的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-01 16:33