easyblink
1.初始化
led1 = easyblink_init_led(GET_PIN(B, 7), PIN_LOW);
功能:对控制led灯的引脚初始化,传入的参数为 初始化哪个引脚,高有效还是低有效
实现方法:1.遍历eb_leds数组(该数组保存了所有led灯的信息,初始的时候led结构体中相关的标志位没有激活),在数组中找到第一个没有激活的位置,该位置即会保存当前所要初始化的那个led灯的所有信息。
eb_leds数组中每个元素是easyblink_data类型的变量
struct easyblink_data
{
rt_base_t led_pin;
rt_base_t active_level;
rt_uint16_t flag;
rt_int16_t nums;
rt_int16_t nums_bak;
rt_uint16_t pulse;
rt_uint16_t pulse_bak;
rt_uint16_t npulse;
rt_uint16_t npulse_bak;
rt_int32_t ticks;
};
2.找到保存led信息的位置后,首先将标志位置为1,((led)->flag |= (0X01)), 防止这个位置被后面初始化的led占了。然后将初始化的关键信息保存下来(哪个引脚,什么电平有效)。同时配置该引脚为输出模式。
__EASYBLINK_SET_FLAG(led, PKG_EASYBLINK_INIT);
led->led_pin = led_pin;
led->active_level = active_level;
eb_led_off(led);
rt_pin_mode(led_pin, PIN_MODE_OUTPUT);
3.如果是第一个配置的led,在配置完之后还需要开一个线程,这个线程的任务就是实现具体的led闪烁的业务。(第一个打开线程后,第二个就不需要打开了,因为这个线程会遍历存放所有led信息的数组)。【只用一个线程就能解决所有led的业务能够节约内存】。
2.调用
easyblink(led1, 5, 500, 1000);
功能:led1 闪烁5次 每次1000ms ,其中点亮的时间为500ms。 note:无限次的参数为-1。
实现方法:判断当前的灯属于什么状态(上一个指令有没有执行完?,上一个指令是什么?)。
上一个指令是无限次循环:保存当前无线循环的信息(周期和占空比)—>关灯—>执行当前的指令
上一个指令是有限循环且未执行完毕:等上一个指令执行完毕–>再执行当前的指令
上一个指令执行完毕了,led当前属于空闲状态:直接执行当前的指令
if(led -> nums == -1)//无限循环?
{...........}
else if (__EASYBLINK_IS_FLAG(led,PKG_EASYBLINK_ACTIVE))//上一个指令没有执行完毕?
{..........}
else
{.......... }
easyblink只操作了led结构体(*easyblink_data)的信息,在这个函数中没有控制MCU相关引脚的电平,具体引脚电平的控制是在初始化时开的线程中实现的
3.线程实现的逻辑
线程入口函数eb_daemon_thread_entry,负责将实现数组eb_leds中每个led结构体包含的闪烁信息
遍历数组中的每个led灯
if(如果当前led应该亮) //如果这次应该亮
{
if(当前还有剩余的闪烁次数 或 闪烁的次数为无限次)
{
操作硬件点灯
清除应该亮的标志位 //下一次就是应该灭了
记录下应该亮多久 //方便到点了过来给关闭
}
else //如果当前没有闪烁的次数
{
if(如果还有下一个任务)
{
把led的下一个任务(bak里存着的)拿出来
tick赋值0 // 0是最小 下次线程一定先处理跟新这个灯的信息
如果任务是永久的(-1)情况,单独处理
}
else //没有下一个任务 (当前任务结束+没有下一个任务==》led任务结束)
{
关闭led灯激活的标志位 // 没有剩余的闪烁次数 也 下面也没有要做的事 这一轮就算结束了
}
}
}
else//如果应该灭
{
操作硬件灭灯
置位点亮的标志位//下一个时间点应该开灯
记录关灯的时间
闪烁的次数-1//成功完成了一次闪烁
}
4.单线程实现多个led灯的闪烁
这个包的一个特点是,使用一个线程解决了多个灯的闪烁问题,好处是节约内存。
实现方法:每次找到最先需要闪烁的灯,并算出需要等待的时间----->等待相应的时间(交出CPU的控制权)----->led灯动作----->再次算出最先需要闪烁的灯的时间…
1.计算需要等待的时间 correct_or_get_min_ticks(0, RT_FALSE)
功能:校正其余led的ticks, 并返回最小的ticks, 当corr为fasle时, 只单纯取最小的ticks
实现: 遍历所有的led灯,选出最小的led->ticks
if(led->ticks < tick_min)
tick_min = led->ticks;
2.等待时间(交出CPU控制权)
- 在等待的时候没有新的led任务(有新调用easyblink)
rt_sem_take等待信号量,如果等待的时间超过wait_tick则返回RT_ETIMEOUT。没有新的led任务时,不会等待信号量,超过时间则break跳出这个循环。
- 在等待的时候添加了新的led任务(没有新调用easyblink)
调用新的led时会释放一个信号量(在led_blink_delay中rt_sem_release)----->收到RT_EOK----->correct_or_get_min_ticks(tick, RT_TRUE)重新计算tick,并取最小的ticks
while(wait_tick)
{
tick = rt_tick_get();
/*在线等待 wait_tick个 os tick*/
if (rt_sem_take(eb_sem, wait_tick) != RT_EOK)
break;
/*接收到信号量,校正其他led的tick数,并取最小的ticks*/
wait_tick = correct_or_get_min_ticks(tick, RT_TRUE);
}
3.重新计算tick
重新计算tick,并取最小时间的方法 correct_or_get_min_ticks(tick, RT_TRUE)
1.已经等了多长时间了tick = rt_tick_get() - tick 现在的tick - 上一次记录的tick
rt_tick_get() // 返回从开机开始的时间戳
2.更新所有led的最新等待时间 最新时间 = 所有led的时间戳 - 已经等待的时间
led->ticks -= tick;
/*校正其余led的ticks, 并返回最小的ticks, 当corr为fasle时, 只单纯取最小的ticks*/
rt_tick_t correct_or_get_min_ticks(rt_tick_t tick, rt_bool_t corr)
{
int i;
ebled_t led = RT_NULL;
rt_int32_t tick_min = PKG_EASYBLINK_WAIT_MAX_TICK;
if(corr)
tick = rt_tick_get() - tick;
for(i = 0; i < PKG_EASYBLINK_MAX_LED_NUMS; i++)
{
led = &eb_leds[i];
if(__EASYBLINK_IS_FLAG(led, PKG_EASYBLINK_ACTIVE))
{
if(corr)
{
if(__EASYBLINK_IS_FLAG(led, PKG_EASYBLINK_CORRECT))
__EASYBLINK_CLEAR_FLAG(led, PKG_EASYBLINK_CORRECT);
else
led->ticks -= tick;
}
if(led->ticks < tick_min)
tick_min = led->ticks;
}
}
return (rt_tick_t)((tick_min < 0) ? 0 : tick_min);//如果比0小就返回0
}