编辑个目录,直接跳转到总结。
作为计算机专业的,数学不好,对数字相当不敏感。诶,悲催。
每次碰到个啥位操作,总是转不过来,非得仔细地慢慢地看,好吧,写在这里。
在看ucos 源码时,看到TCB 结构里有四个变量,分别为:
INT8U OSTCBX; /* Bit position in group corresponding to task
priority (0..7) */
INT8U OSTCBY; /* Index into ready table corresponding to task priority */
INT8U OSTCBBitX; /* Bit mask to access bit position in ready table */
INT8U OSTCBBitY; /* Bit mask to access bit p osition in ready group*/
下面这段是在TCB 初始化里,根据优先级对这几个变量的给值。
ptcb->OSTCBY = prio >> 3; /* Pre-compute X, Y, BitX and BitY */
ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];
ptcb->OSTCBX = prio & 0x07;
ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
另外有个OSMapTbl ,如下:
/*
*********************************************************************************************************
* MAPPING TABLE TO MAP BIT POSITION TO BIT MASK
*
* Note: Index into table is desired bit position, 0..7
* Indexed value corresponds to bit mask
*********************************************************************************************************
*/
INT8U const OSMapTbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
/*
还有我搜到的一篇关于位掩码的文:位掩码
ucos ii 中任务优先级表是一个8x8 的表(8个UINT8),另设一个 8 位对应每一行,用以标识该行是否有 1 (即优先级是否用了)假设横向是X,纵向是Y 。OSTCBY呢 是对应于Group , 纵向的。OSTCBX 是横向的,根据(OSTCBX ,OSTCBY) 就可以很方便的找到 优先级对应的地方并且判断 其 是 0 还是 1。
创建任务的时候,只是给了一个优先级,那么,怎么样根据优先级来设置这横座标和纵座标呢?
1. OSTCBY 的设置,右移三位,即 除 8,还好理解。尽管对数字不敏感,见了几次还是知道意思的。
2. OSTCBX 的设置, &0x07 ,根据实验结果, 即取后三位,64个优先级 用 6 位可以表示,那么高三位就是表示 Y,低三位就是表示 X。(好吧,到这里已经把前面的自己全部推翻了。标记为红色吧。)
3. 相应位掩码的设置。位掩码是干啥用的?上面文里的大神说:有这么一种技术,叫做位掩码技术,用来处理存在多个布尔值的情况。哦,理解了,记得看飞思卡尔 K60 的寄存器定义经常有这种MASK,一般说来,寄存器的某一位即可以看做一个开关,或者说里面的值就代表将要使开关拨到何种状态,那么这种就是布尔型的。
如这一段:
//开启各个GPIO口的转换时钟
SIM->SCGC5 = SIM_SCGC5_PORTA_MASK | SIM_SCGC5_PORTB_MASK | SIM_SCGC5_PORTC_MASK | SIM_SCGC5_PORTD_MASK | SIM_SCGC5_PORTE_MASK;
/* SCGC5 Bit Fields */ #define SIM_SCGC5_LPTIMER_MASK 0x1u #define SIM_SCGC5_LPTIMER_SHIFT 0 #define SIM_SCGC5_REGFILE_MASK 0x2u #define SIM_SCGC5_REGFILE_SHIFT 1 #define SIM_SCGC5_TSI_MASK 0x20u #define SIM_SCGC5_TSI_SHIFT 5 #define SIM_SCGC5_PORTA_MASK 0x200u #define SIM_SCGC5_PORTA_SHIFT 9 #define SIM_SCGC5_PORTB_MASK 0x400u #define SIM_SCGC5_PORTB_SHIFT 10 #define SIM_SCGC5_PORTC_MASK 0x800u #define SIM_SCGC5_PORTC_SHIFT 11 #define SIM_SCGC5_PORTD_MASK 0x1000u #define SIM_SCGC5_PORTD_SHIFT 12 #define SIM_SCGC5_PORTE_MASK 0x2000u #define SIM_SCGC5_PORTE_SHIFT 13每一个对应一位开关,开了五个开关。(至于SHIFT是做什么用的,就不知道了)
好吧,再回到ucos 中来,为了在写的时候 只需要写一个 | , 设置下位掩码,位掩码指示多个开关中的某一位的开关状态(0/1)。
20130528
那么对这种位掩码的操作我见到了的有三种形式
总结三种常见的位掩码操作形式
1. 设置相应位
操作方法:“ |= ”。 如STM32中有关时钟配置的一个函数 RCC->APB2ENR |= RCC_APB2Periph
2. 清除相应位
操作方法 :“ |= ” “~”
如 :RCC->APB2ENR &= ~RCC_APB2Periph
贴上整个函数:
/**
* @brief Enables or disables the High Speed APB (APB2) peripheral clock.
* @param RCC_APB2Periph: specifies the APB2 peripheral to gates its clock.
* This parameter can be any combination of the following values:
* @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
* RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
* RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
* RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
* RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
* RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
* RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11
* @param NewState: new state of the specified peripheral clock.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
RCC->APB2ENR |= RCC_APB2Periph;
}
else
{
RCC->APB2ENR &= ~RCC_APB2Periph;
}
}
3. 检查 位状态
操作方法:“ & ” 后判断
如: ucos 中挂起任务的一段,用来判断任务就绪表的某一行中是否有其他就绪任务的一段。
if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb ->OSTCBBitX) == 0x00) { // 如果该位的其他位没有
就绪任务,就将 OSRdyGrp 的对应位(指示对应一行中是否有就绪任务)清除
OSRdyGrp &= ~ptcb->OSTCBBitY;
}
ptcb ->OSTCBStat |= OS_STAT_SUSPEND;