BITS_TO_LONGS的解释及相关为操作函数

宏BITS_TO_LONGS

#define BITS_PER_BYTE           8
#define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))

sizeof(long) = 4,所以BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, 32)
BITS_TO_LONGS(nr)就是((nr) + (32 -1) / (32))
就是判断nr这个数是属于几个long类型
nr = 1~32:1
nr = 32~64:2
nr = 65~96:3
nr = 97~128:4
nr = 129~160:5
//…
//…
结合定义unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
就是根据EV_CNT的个数定义一个数组,如果EV_CNT小于32,就定义成evbit[1],如果大于32就装不下了就要使用evbit[2]这么大的数组了。
这么做一方面是为了提高兼容性,万一数量改变,还要记得修改定义的数组大小,定义成宏,定义数组的时候会根据数量自动判断应该创建多大的数组
另一方面也是提供了一种通用的解决办法,为下面的keybit,relbit等都提供了通用好用的解决方案,佩服这种思想!
另外几个函数我们也分析一下,以后也会用到。

static inline void clear_bit(int nr, unsigned long *addr)
{
    addr[nr / BITS_PER_LONG] &= ~(1UL << (nr % BITS_PER_LONG)); 
}

参数@nr,要设置成1的位。
@addr,是一个unsigned long型的数组
这个函数就是实现把addr数组的nr位置为1。为了通用型和健壮性的考虑,unsigned long型数组,一个元素只有32位,如果超过了32位,就要使用
unsigned long addr[2],这种方法,那么在置位操作的时候,还是要转换成单个数组元素的方法,只不过数组的下表变了,如
addr[0]代表位0~31
addr[1]代表位32~63
//依次类推
假如我要把第5位置为1的话我只需要addr[0] |= 1 << 5;就可以了,
但是我要把第35位置为1的话,我就需要addr[35/32] |= 1 << (35 % 32),即addr[1] |= 1 << 3;就可以了
这也就是set_bit这个函数的意义

static inline void clear_bit(int nr, unsigned long *addr)
{
    addr[nr / BITS_PER_LONG] &= ~(1UL << (nr % BITS_PER_LONG));
 }

搞懂了第一个,下面的就都很轻松了,只不过清除某一位的操作使用 &=~来进行

static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)
{
    return ((1UL << (nr % BITS_PER_LONG)) &
        (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
 }

测试某一位是否为1,让那一位和1行&运算,如果为1,那么那一位就是1,如果为0,那一位就为0
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];这个数组代表input设备所支持的事件,使用位图的方法来表示。什么是位图,就是使用某一位来代表一个事件,
如果这一位被为1,那么就代表它支持这类事件。那么我们看看,input设备都支持哪些事件。
在include/linux/input.h中定以了

/*
 * Event types
 */
 
#define EV_SYN            0x00      //位0
#define EV_KEY            0x01      //位1
#define EV_REL            0x02      //位2
#define EV_ABS            0x03      //位3
#define EV_MSC            0x04      //位4
#define EV_SW            0x05      //位5
#define EV_LED            0x11      //位17
#define EV_SND            0x12      //位18
#define EV_REP            0x14      //位20
#define EV_FF            0x15      //位21
#define EV_PWR            0x16      //位22
#define EV_FF_STATUS    0x17      //位23
#define EV_MAX            0x1f      //一共支持31种事件
#define EV_CNT            (EV_MAX+1)

如果我们想让input设备支持按键事件,那么我们只需要让evbit数组的EV_KEY即第1位置为1就可以了。我们可以使用宏set_bit(EV_KEY, input_dev->evbit)就可以了,为什么是input_dev->evbit呢,
因为input_dev下面挂载了一个evbit的数组,表示这个设备支持的事件。分析input_dev这个结构体时会具体说


作者:andrinux
来源:CSDN
原文:https://blog.csdn.net/andrinux/article/details/38925827
版权声明:本文为博主原创文章,转载请附上博文链接!

这段代码使用了 MSP430 微控制器的头文件,并定义了一些宏和变量。主要的代码逻辑是使用 Timer0 来生成一个固定时间间隔的中断,然后在中断服务函数中改变 P1.0 引脚的状态,从而控制 LED1 灯的闪烁。 具体解释如下: 1. `#include <msp430.h>`:包含 MSP430 微控制器的头文件,以便使用相关的寄存器和函数。 2. 宏定义部分: - `#define CPU_F ((double)1000000)`:定义一个名为 `CPU_F` 的宏,表示 CPU 的频率为 1MHz。 - `#define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/100000.0))`:定义一个名为 `delay_us` 的宏,用于延迟指定的微秒数。 - `#define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x1000.0))`:定义一个名为 `delay_ms` 的宏,用于延迟指定的毫秒数。 3. `unsigned char count=0;`:声明一个无符号字符型变量 `count`,用于计数。 4. `int main(void)`:主函数入口。 5. `WDTCTL = WDTPW | WDTHOLD;`:停用看门狗定时器。 6. `P1DIR |= BIT0;`:将 P1.0 引脚设置为输出模式,即控制 LED1。 7. 定时器配置部分: - `TA0CCTL0 = CCIE;`:使能 Timer0 的 CCR0 中断。 - `TA0CCR0 = 50000;`:设置 Timer0 的计数值,即触发中断的时间间隔。 - `TA0CTL = TASSEL_2+MC_1+TACLR;`:配置 Timer0 的时钟源为 SMCLK、增计数模式,并清除 TAR 寄存器的值。 8. `_bis_SR_register(LPM0_bits+GIE);`:将 CPU 进入低功耗模式 0,同时使能全局中断。 9. `#pragma vector=TIMER0_A0_VECTOR`:设置 Timer0 的 CCR0 中断向量。 10. `__interrupt void TIMER0_A0_ISR(void)`:Timer0 CCR0 中断服务函数。 11. `count++;`:每次中断发生时,将计数值加 1。 12. `if(count == 20)`:当计数值达到 20 时执行下面的操作。 13. `count=0;`:将计数值重新设为 0。 14. `P1OUT ^= BIT0;`:通过异或操作改变 P1.0 引脚的状态,实现 LED1 的闪烁效果。 以上就是这段代码的基本解释,它通过定时器中断实现了 LED1 的周期性闪烁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值