本帖最后由 blust5 于 2023-8-14 11:30 编辑
#申请原创# @21小跑堂 @21小跑堂 @21小跑堂
书接上回,之前写了一篇“【技术分享】GD32台阶流水灯项目之硬件问题与改善”,里面介绍了项目背景和硬件相关内容,以及LED驱动芯片的驱动部分代码。 链接: 【技术分享】GD32台阶流水灯项目之硬件问题与改善
https://bbs.21ic.com/icview-3319996-1-1.html?fromuser=blust5 硬件板图: 实物照片图: 下面开始分享软件部分内容。 先来简单介绍一下大致的软件逻辑。 项目有两个人体传感器,分别安装在楼梯底部和顶部。当感应到人走过时,从走过的方向开始逐级点亮台阶灯,同时扶手上的流水灯也同步点亮;当人走过之后,台阶灯和流水灯再逐级熄灭。 板子上有按键和数码管,可以通过按键来设置台阶数量、流水灯数量、台阶亮灯频率以及灯的亮度等信息。同时板子上有存储芯片,可以将设置的信息保存在存储芯片里。不过开发过程中发现单片机内部FLASH空间足够使用,所以后面删除了存储芯片,直接将信息参数存储到FLASH的尾部了。 由此可见,程序除了基本驱动框架之外,主要部分就是按键扫描识别、数码管显示、以及流水灯控制逻辑。 其中按键扫描识别、数码管显示两部分属于很常用的功能,网上例程也有很多,这里就不详细展开介绍了,具体内容可以查看最终程序包里的内容。 这里主要介绍流水灯控制逻辑的实现和完善。 先来说下一开始的流程代码。一开始就是最简单的实现流水灯效果:有人走过,触发人体传感器信号,然后台阶开始按照设定参数逐级点亮,全部点亮之后开始逐级熄灭,直至最终全部熄灭,流程结束。 各个代码功能模块如下: 流程触发代码: void led_step_start(uint8_t mode)
{
step_up_down_flag = mode;
led_on_off_flag = 1;
step_time_cnt = para_data[DATA_STEP_TIME];
}
流水灯流程处理代码: void led_step_process()
{
if(step_up_down_flag != 0)
{
if(led_on_off_flag == 1)
{
step_time_cnt ++;
if(step_time_cnt >= para_data[DATA_STEP_TIME])
{
step_time_cnt = 0;
if(ws_step_led_ctrl(led_on_off_flag, step_up_down_flag) == 1)
{
led_on_off_flag = 2;
}
}
}
else if(led_on_off_flag == 2)
{
step_time_cnt ++;
if(step_time_cnt >= para_data[DATA_STEP_TIME])
{
step_time_cnt = 0;
if(ws_step_led_ctrl(led_on_off_flag, step_up_down_flag) == 1)
{
led_on_off_flag = 0;
step_up_down_flag = 0;
}
}
}
}
}
最终亮灯驱动代码: uint8_t ws_step_led_ctrl(uint8_t on_off, uint8_t up_down)
{
static uint16_t step_now = 0;
uint16_t led_on, i;
uint32_t led_set_a, led_set_b;
if(on_off == 1)
{
led_set_a = step_color_data;
led_set_b = 0;
}
else if(on_off == 2)
{
led_set_b = step_color_data;
led_set_a = 0;
}
else
{
return 1;
}
if(up_down == 1)
{
led_on = step_now + 1;
for(i=0; i<para_data[DATA_STEP_NUM]; i++)
{
if(i < led_on)
{
ws_step_set1(led_set_a);
}
else
{
ws_step_set1(led_set_b);
}
}
ws_step_reset();
}
else if(up_down == 2)
{
led_on = para_data[DATA_STEP_NUM] - step_now - 1;
for(i=0; i<para_data[DATA_STEP_NUM]; i++)
{
if(i < led_on)
{
ws_step_set1(led_set_b);
}
else
{
ws_step_set1(led_set_a);
人触发人体传感器信号时,调用流程触发函数led_step_start(mode),其中mode参数确定触发方向,1为从低往上运行,2为从上往下运行。 然后通过100ms定时中断标志来调用流程处理函数key_press_process(),该函数里标记变量确认是否有待处理流程。有流程需处理时,通过变量累加确定延时间隔,进行亮灯状态改变。然后调用亮灯函数ws_step_led_ctrl()点亮对应的灯,同时确认流程是否完成。流程完成后清除相应标志位。 主函数里对应调用逻辑如下: if(time_100ms_flag == 1)
{
time_100ms_flag = 0;
key_press_process();
if(int_bottom_flag == 1)
{
int_bottom_flag = 0;
led_step_start(1);
}
if(int_top_flag == 1)
{
int_top_flag = 0;
led_step_start(2);
}
led_step_process();
}
后面考虑到复杂情况,比如楼梯两边同时来人、一个流程正在运行时又再次触发流程等情况,于是对流程逻辑进行优化。 主要按照以下几点逻辑进行处理: 1、正常响应:触发后台阶按间隔时间依次亮起,所有灯全部亮起后再依次熄灭。 2、双向判断:一个方向触发后,在所有灯亮起时间的2/3内,另一个方向触发,则认为是另一个人进入,进入双向响应逻辑。 3、同向判断:一个方向触发后,在整个流程结束之前,同一个方向再次触发,则进入同向延时逻辑。 4、双向响应逻辑:两个方向同时按照各自的延时向中间亮起,并按照各自的逻辑熄灭,灯最终的亮灭状态取两个方向的叠加(两个逻辑均为灭才是灭,只要有一个逻辑是亮的,最终就是亮的)。 5、同向延时逻辑:第二次触发时另起一个处理逻辑,与第一次触发的逻辑叠加后再最终处理灯的亮灭状态。 在实现上述逻辑之后,基本可以实现较好的效果。同时也有容错效果,逻辑出错后在运行完毕之后会清除状态,不影响下次触发后的逻辑运行。 基于以上功能逻辑,在代码上进行实现时,考虑到在同一个逻辑流程里判断所有状态叠加后的结果相对比较复杂,于是开启两个流程序列,正常响应时启动一个流程序列,二次触发时根据逻辑关系确定启动第二个流程序列。每个流程序列单独运行其自身逻辑,最终灯的状态取两个序列对应位置灯的状态叠加。 最终启动函数需做如下判断: 1、启动时无正在运行逻辑,则直接启动一个逻辑流程; 2、启动时有一个正在运行的流程,则判断是否满足增加流程条件,满足则启动第二流程; 3、启动时有两个正在运行的流程,则判断忽略新流程(正在运行的两个流程为反向)或追加新流程(正在运行的两个流程为同向)。 详细处理函数如下: void led_step_start(uint8_t mode)
{
if(para_adjust_flag == 1)
{
led_step_stop();
return;
}
if((up_down_flag_1 == STEP_LED_STOP)&&(up_down_flag_2 == STEP_LED_STOP)) // 没有正在进行的流程
{
up_down_flag_1 = mode;
step_now_1 = 0;
color_now_1 = 0;
}
else if(up_down_flag_1 == STEP_LED_STOP) // 有一条正在进行的流程
{
if(up_down_flag_2 == mode) // 新流程与现有流程同方向,直接启动新流程
{
up_down_flag_1 = mode;
step_now_1 = 0;
color_now_1 = 0;
}
else // 新流程与现有流程反向
{
if(step_now_2 < para_data[DATA_STEP_NUM] * 2 / 3) // 现有流程运行时间在全亮所需时间的2/3以内,启动新流程
{
up_down_flag_1 = mode;
step_now_1 = 0;
color_now_1 = 0;
}
}
}
else if(up_down_flag_2 == STEP_LED_STOP) // 有一条正在进行的流程
{
if(up_down_flag_1 == mode) // 新流程与现有流程同方向,直接启动新流程
{
up_down_flag_2 = mode;
step_now_2 = 0;
color_now_2 = 0;
}
else // 新流程与现有流程反向
{
if(step_now_1 < para_data[DATA_STEP_NUM] * 2 / 3) // 现有流程运行时间在全亮所需时间的2/3以内,启动新流程
{
up_down_flag_2 = mode;
step_now_2 = 0;
color_now_2 = 0;
}
}
}
else // 有两条正在进行的流程
{
if((up_down_flag_1 == up_down_flag_2)&&(up_down_flag_1 == mode))
{ // 现有两条流程同向且与新流程同向,则新流程取代现有流程中运行时间更短的流程,同时运行时间长的流程如果运行至灭灯环节,则重启灭灯流程
if(step_now_1 > step_now_2)
{
step_now_2 = 0;
color_now_2 = 0;
if(step_now_1 > para_data[DATA_STEP_NUM])
{
step_now_1 = para_data[DATA_STEP_NUM];
color_now_1 = para_data[DATA_COLOR_NUM];
}
}
else
{
step_now_1 = 0;
color_now_1 = 0;
if(step_now_2 > para_data[DATA_STEP_NUM])
{
step_now_2 = para_data[DATA_STEP_NUM];
color_now_2 = para_data[DATA_COLOR_NUM];
}
}
}
}
step_time_cnt = step_led_time;
color_time_cnt = color_led_time;
}
流程处理函数里先是根据各自的标记位进行各自流程状态的切换,然后将两个流程各自位置灯状态叠加后进行亮灯操作。 由于扶手流水灯数量和台阶数量不一定一致,因此扶手流水灯亮灯间隔是根据台阶亮灯总时间换算得来,流程处理函数里也分别对台阶灯流程和扶手灯流程做了对应处理,并在所有灯全亮时增加了一个3倍台阶灯间隔时间的延时,用以改善运行效果。 void led_step_process()
{
static uint8_t led_all_on_cnt1 = 0;
static uint8_t led_all_on_cnt2 = 0;
uint8_t i;
if(up_down_flag_1 + up_down_flag_2 > 0)
{
// 台阶灯逻辑处理
step_time_cnt ++;
if(step_time_cnt >= step_led_time)
{
step_time_cnt = 0;
switch(up_down_flag_1) // 判断流程1运行方向
{
case STEP_LED_UP: // 流程运行方向为上行
step_now_1 ++;
if(step_now_1 <= para_data[DATA_STEP_NUM]) // 逐级台阶点亮过程
{
for(i=0; i<para_data[DATA_STEP_NUM]; i++)
{
if(i < step_now_1)
{
led_state_1[i] = 1;
}
else
{
led_state_1[i] = 0;
}
}
if(step_now_1 == para_data[DATA_STEP_NUM])
{
led_all_on_cnt1 ++;
if(led_all_on_cnt1 <= 3)
{
step_now_1 --;
}
}
else
{
led_all_on_cnt1 = 0;
}
}
else if(step_now_1 <= para_data[DATA_STEP_NUM]*2) // 逐级台阶熄灭过程
{
for(i=0; i<para_data[DATA_STEP_NUM]; i++)
{
if(i < step_now_1 - para_data[DATA_STEP_NUM])
{
led_state_1[i] = 0;
}
else
{
led_state_1[i] = 1;
}
}
if(step_now_1 == para_data[DATA_STEP_NUM]+1)
{
color_now_1 = para_data[DATA_COLOR_NUM];
color_time_cnt = color_led_time;
}
}
else // 整个流程运行完毕
{
up_down_flag_1 = STEP_LED_STOP;
step_now_1 = 0;
memset(led_state_1,0,sizeof(led_state_1));
}
break;
case STEP_LED_DOWN: // 过程运行方向为下行
step_now_1 ++;
if(step_now_1 <= para_data[DATA_STEP_NUM]) // 逐级台阶点亮过程
{
for(i=0; i<para_data[DATA_STEP_NUM]; i++)
{
if(i < para_data[DATA_STEP_NUM] - step_now_1)
{
led_state_1[i] = 0;
}
else
{
led_state_1[i] = 1;
}
}
if(step_now_1 == para_data[DATA_STEP_NUM])
{
led_all_on_cnt1 ++;
if(led_all_on_cnt1 <= 3)
{
step_now_1 --;
}
}
else
{
led_all_on_cnt1 = 0;
}
}
else if(step_now_1 <= para_data[DATA_STEP_NUM]*2) // 逐级台阶熄灭过程
{
for(i=0; i<para_data[DATA_STEP_NUM]; i++)
{
if(i < para_data[DATA_STEP_NUM]*2 - step_now_1)
{
led_state_1[i] = 1;
}
else
{
led_state_1[i] = 0;
}
}
if(step_now_1 == para_data[DATA_STEP_NUM]+1)
{
color_now_1 = para_data[DATA_COLOR_NUM];
color_time_cnt = color_led_time;
}
}
else // 整个流程运行完毕
{
up_down_flag_1 = STEP_LED_STOP;
step_now_1 = 0;
memset(led_state_1,0,sizeof(led_state_1));
}
break;
default:
up_down_flag_1 = STEP_LED_STOP;
step_now_1 = 0;
memset(led_state_1,0,sizeof(led_state_1));
break;
}
switch(up_down_flag_2) // 判断流程2运行方向
{
case STEP_LED_UP: // 流程运行方向为上行
step_now_2 ++;
if(step_now_2 <= para_data[DATA_STEP_NUM]) // 逐级台阶点亮过程
{
for(i=0; i<para_data[DATA_STEP_NUM]; i++)
{
if(i < step_now_2)
{
led_state_2[i] = 1;
}
else
{
led_state_2[i] = 0;
}
}
if(step_now_2 == para_data[DATA_STEP_NUM])
{
led_all_on_cnt2 ++;
if(led_all_on_cnt2 <= 3)
{
step_now_2 --;
}
}
else
{
led_all_on_cnt2 = 0;
}
}
else if(step_now_2 <= para_data[DATA_STEP_NUM]*2) // 逐级台阶熄灭过程
{
for(i=0; i<para_data[DATA_STEP_NUM]; i++)
{
if(i < step_now_2 - para_data[DATA_STEP_NUM])
{
led_state_2[i] = 0;
}
else
{
led_state_2[i] = 1;
}
}
if(step_now_2 == para_data[DATA_STEP_NUM]+1)
{
color_now_2 = para_data[DATA_COLOR_NUM];
color_time_cnt = color_led_time;
}
}
else // 整个流程运行完毕
{
up_down_flag_2 = STEP_LED_STOP;
step_now_2 = 0;
memset(led_state_2,0,sizeof(led_state_2));
}
break;
case STEP_LED_DOWN: // 过程运行方向为下行
step_now_2 ++;
if(step_now_2 <= para_data[DATA_STEP_NUM]) // 逐级台阶点亮过程
{
for(i=0; i<para_data[DATA_STEP_NUM]; i++)
{
if(i < para_data[DATA_STEP_NUM] - step_now_2)
{
led_state_2[i] = 0;
}
else
{
led_state_2[i] = 1;
}
}
if(step_now_2 == para_data[DATA_STEP_NUM])
{
led_all_on_cnt2 ++;
if(led_all_on_cnt2 <= 3)
{
step_now_2 --;
}
}
else
{
led_all_on_cnt2 = 0;
}
}
else if(step_now_2 <= para_data[DATA_STEP_NUM]*2) // 逐级台阶熄灭过程
{
for(i=0; i<para_data[DATA_STEP_NUM]; i++)
{
if(i < para_data[DATA_STEP_NUM]*2 - step_now_2)
{
led_state_2[i] = 1;
}
else
{
led_state_2[i] = 0;
}
}
if(step_now_2 == para_data[DATA_STEP_NUM]+1)
{
color_now_2 = para_data[DATA_COLOR_NUM];
color_time_cnt = color_led_time;
}
}
else // 整个流程运行完毕
{
up_down_flag_2 = STEP_LED_STOP;
step_now_2 = 0;
memset(led_state_2,0,sizeof(led_state_2));
}
break;
default:
up_down_flag_2 = STEP_LED_STOP;
step_now_2 = 0;
memset(led_state_2,0,sizeof(led_state_2));
break;
}
for(i=0; i<para_data[DATA_STEP_NUM]; i++)
{
if(led_state_1[i] + led_state_2[i] > 0)
{
ws_step_set1(step_light_data);
}
else
{
ws_step_set1(0);
}
}
ws_step_reset();
}
// 流水灯逻辑处理
color_time_cnt ++;
if(color_time_cnt >= color_led_time)
{
color_time_cnt = 0;
switch(up_down_flag_1) // 判断流程1运行方向
{
case STEP_LED_UP: // 流程运行方向为上行
color_now_1 ++;
if(color_now_1 <= para_data[DATA_COLOR_NUM]) // 逐级点亮过程
{
for(i=0; i<para_data[DATA_COLOR_NUM]; i++)
{
if(i < color_now_1)
{
led_color_1[i] = 1;
}
else
{
led_color_1[i] = 0;
}
}
if(color_now_1 == para_data[DATA_COLOR_NUM])
{
color_now_1 --;
}
}
else if(color_now_1 <= para_data[DATA_COLOR_NUM]*2) // 逐级熄灭过程
{
for(i=0; i<para_data[DATA_COLOR_NUM]; i++)
{
if(i < color_now_1 - para_data[DATA_COLOR_NUM])
{
led_color_1[i] = 0;
}
else
{
led_color_1[i] = 1;
}
}
if(color_now_1 == para_data[DATA_COLOR_NUM]*2)
{
color_now_1 --;
}
}
else // 整个流程运行完毕
{
color_now_1 = 0;
memset(led_color_1,0,sizeof(led_color_1));
}
break;
case STEP_LED_DOWN: // 过程运行方向为下行
color_now_1 ++;
if(color_now_1 <= para_data[DATA_COLOR_NUM]) // 逐级点亮过程
{
for(i=0; i<para_data[DATA_COLOR_NUM]; i++)
{
if(i < para_data[DATA_COLOR_NUM] - color_now_1)
{
led_color_1[i] = 0;
}
else
{
led_color_1[i] = 1;
}
}
if(color_now_1 == para_data[DATA_COLOR_NUM])
{
color_now_1 --;
}
}
else if(color_now_1 <= para_data[DATA_COLOR_NUM]*2) // 逐级熄灭过程
{
for(i=0; i<para_data[DATA_COLOR_NUM]; i++)
{
if(i < para_data[DATA_COLOR_NUM]*2 - color_now_1)
{
led_color_1[i] = 1;
}
else
{
led_color_1[i] = 0;
}
}
if(color_now_1 == para_data[DATA_COLOR_NUM]*2)
{
color_now_1 --;
}
}
else // 整个流程运行完毕
{
color_now_1 = 0;
memset(led_color_1,0,sizeof(led_color_1));
}
break;
default:
color_now_1 = 0;
memset(led_color_1,0,sizeof(led_color_1));
break;
}
switch(up_down_flag_2) // 判断流程2运行方向
{
case STEP_LED_UP: // 流程运行方向为上行
color_now_2 ++;
if(color_now_2 <= para_data[DATA_COLOR_NUM]) // 逐级点亮过程
{
for(i=0; i<para_data[DATA_COLOR_NUM]; i++)
{
if(i < color_now_2)
{
led_color_2[i] = 1;
}
else
{
led_color_2[i] = 0;
}
}
if(color_now_2 == para_data[DATA_COLOR_NUM])
{
color_now_2 --;
}
}
else if(color_now_2 <= para_data[DATA_COLOR_NUM]*2) // 逐级熄灭过程
{
for(i=0; i<para_data[DATA_COLOR_NUM]; i++)
{
if(i < color_now_2 - para_data[DATA_COLOR_NUM])
{
led_color_2[i] = 0;
}
else
{
led_color_2[i] = 1;
}
}
if(color_now_2 == para_data[DATA_COLOR_NUM]*2)
{
color_now_2 --;
}
}
else // 整个流程运行完毕
{
color_now_2 = 0;
memset(led_color_2,0,sizeof(led_color_2));
}
break;
case STEP_LED_DOWN: // 过程运行方向为下行
color_now_2 ++;
if(color_now_2 <= para_data[DATA_COLOR_NUM]) // 逐级点亮过程
{
for(i=0; i<para_data[DATA_COLOR_NUM]; i++)
{
if(i < para_data[DATA_COLOR_NUM] - color_now_2)
{
led_color_2[i] = 0;
}
else
{
led_color_2[i] = 1;
}
}
if(color_now_2 == para_data[DATA_COLOR_NUM])
{
color_now_2 --;
}
}
else if(color_now_2 <= para_data[DATA_COLOR_NUM]*2) // 逐级熄灭过程
{
for(i=0; i<para_data[DATA_COLOR_NUM]; i++)
{
if(i < para_data[DATA_COLOR_NUM]*2 - color_now_2)
{
led_color_2[i] = 1;
}
else
{
led_color_2[i] = 0;
}
}
if(color_now_2 == para_data[DATA_COLOR_NUM]*2)
{
color_now_2 --;
}
}
else // 整个流程运行完毕
{
color_now_2 = 0;
memset(led_color_2,0,sizeof(led_color_2));
}
break;
default:
color_now_2 = 0;
memset(led_color_2,0,sizeof(led_color_2));
break;
}
for(i=0; i<para_data[DATA_COLOR_NUM]; i++)
{
if(led_color_1[i] + led_color_2[i] > 0)
{
ws_color1_set1(color_light_data);
}
else
{
ws_color1_set1(0);
}
}
ws_color1_reset();
for(i=0; i<para_data[DATA_COLOR_NUM]; i++)
{
if(led_color_1[i] + led_color_2[i] > 0)
{
ws_color2_set1(color_light_data);
}
else
{
ws_color2_set1(0);
}
}
ws_color2_reset();
}
}
}
由于扶手流水灯的运行时间间隔是计算获取的,因此会有小数部分的丢弃,最终扶手灯全亮时间会短于台阶灯全亮时间。因此在流程处理时,扶手灯全亮之后,会等待台阶灯的信号,最终和台阶灯一起启动灭灯流程(以台阶灯的启动时间为准)。 以上就是台阶流水灯项目关于流水灯处理逻辑部分的代码编写和完善过程。 详细代码在下篇文章解决完bug之后再附上。
|
书接上回,从软件层面实现流水灯的逻辑。