【项目笔记:HT46】rt-thread线程延时导致系统同步异常

问题描述

在最新的 v1.0.8_b1 的测试中,偶现出现开关教师端语音开关,偶现无法播放语音的情况:

问题分析

在设置的地方增加调试信息

@@ -1019,13 +999,12 @@ int8_t spi_rcmd_jsd_data(uint8_t port, spi_pac_t *rpac)
         if (uart_tcb[JSD_PORT].vol_set != s.b.line0_s % 6)
         {
           uart_tcb[JSD_PORT].vol_set = s.b.line0_s % 6;

           uart_tcb[JSD_PORT].vol_flg = 1;
-          jsd_tcb.s.b.line0_s = s.b.line0_s;
-          *(uint16_t *)(jsd_tcb.s_seq) += 1;
           if (uart_tcb[JSD_PORT].vol_set != 0)
             rt_hw_uart_power_on(&rp555_hw_uart_port[JSD_PORT]);
-          log_print("r_PORT", port, "uID[%2d]: %010u line0:%d line1:%d\n",
-            jsd_pac->upos, *(uint32_t *)(jsd_pac->uID), s.b.line0_s, s.b.line1_s);
+          log_print("r_PORT", port, "uID[%2d]: %010u line0:%d line1:%d, flg:%d\n", jsd_pac->upos,
+             *(uint32_t *)(jsd_pac->uID), s.b.line0_s, s.b.line1_s, uart_tcb[JSD_PORT].vol_flg);
         }
         if (uart_tcb[XSD_PORT].vol_set != s.b.line1_s % 6)
         {
@@ -1033,8 +1012,6 @@ int8_t spi_rcmd_jsd_data(uint8_t port, spi_pac_t *rpac)
           seq++;
           uart_tcb[XSD_PORT].vol_set = s.b.line1_s % 6;
           uart_tcb[XSD_PORT].vol_flg = 1;
-          jsd_tcb.s.b.line1_s = s.b.line1_s;
-          *(uint16_t *)(jsd_tcb.s_seq) += 1;
           if (uart_tcb[XSD_PORT].vol_set == 0)
             dtq_answer_start_boardcast(128, (uint8_t *)&seq);
           else
@@ -1042,8 +1019,8 @@ int8_t spi_rcmd_jsd_data(uint8_t port, spi_pac_t *rpac)
             dtq_answer_start_boardcast(8, (uint8_t *)&seq);
             rt_hw_uart_power_on(&rp555_hw_uart_port[XSD_PORT]);
           }
-          log_print("r_PORT", port, "uID[%2d]: %010u line0:%d line1:%d\n",
-            jsd_pac->upos, *(uint32_t *)(jsd_pac->uID), s.b.line0_s, s.b.line1_s);
+          log_print("r_PORT", port, "uID[%2d]: %010u line0:%d line1:%d, flg:%d\n", jsd_pac->upos,
+             *(uint32_t *)(jsd_pac->uID), s.b.line0_s, s.b.line1_s, uart_tcb[XSD_PORT].vol_flg);
         }
       }

发现所有的修改都能发送上来,发现有些设置没被执行:
继续分析代码:

   if (uart_tcb[port].vol_set == (rpac->data[0]/3))
   {
-    rt_thread_mdelay(500);
     uart_tcb[port].vol_flg = 0;
     jsd_vol_sync(port, uart_tcb[port].vol_set);
     if (uart_tcb[port].vol_set == 0)

此处:uart_tcb[port].vol_flg 在SPI的接收线程中设置,但是在UART的接收线程中被赋值,于是分析零界状态原子操作的可能,是否出现问题:

此处的 rt_thread_mdelay(500) 就是问题点,程序进入 { 之后串口线程释放CPU,在500ms内,且临近结束的时候,如果在 SPI接收线程中再次受到一个设置指令,那个设置的 vol_flg 会立刻被清除

  • 于是整体代码修改如下
  /* SPI 接收线程 */
  if ((jsd_tcb.s.b.line0_s != s.b.line0_s) || (jsd_tcb.s.b.line1_s != s.b.line1_s))
  {
	uint8_t is_vol_chg = 0;
	/* 修改音量1临界区间 */
	rt_enter_critical();
	if (uart_tcb[JSD_PORT].vol_set != s.b.line0_s % 6)
	{
	  uart_tcb[JSD_PORT].vol_set = s.b.line0_s % 6;
	  /* 标志:在完成同步之后会在串口接收的地方设置同步 */
	  uart_tcb[JSD_PORT].vol_flg = 1;
	  is_vol_chg = 1;
	}
	rt_exit_critical();
	/* 修改音量1无关变量操作尽量不要放在临界区间 */
	if (is_vol_chg == 1)
	{
	  if (uart_tcb[JSD_PORT].vol_set != 0)
		rt_hw_uart_power_on(&rp555_hw_uart_port[JSD_PORT]);
	  log_print("r_PORT", port, "uID[%2d]: %010u line0:%d line1:%d, flg:%d\n", jsd_pac->upos,
		 *(uint32_t *)(jsd_pac->uID), s.b.line0_s, s.b.line1_s, uart_tcb[JSD_PORT].vol_flg);
	}
	is_vol_chg = 0;
	/* 修改音量2临界区间 */
	rt_enter_critical();
	if (uart_tcb[XSD_PORT].vol_set != s.b.line1_s % 6)
	{
	  uart_tcb[XSD_PORT].vol_set = s.b.line1_s % 6;
	  uart_tcb[XSD_PORT].vol_flg = 1;
	  is_vol_chg = 1;
	}
	rt_exit_critical();
	/* 修改音量2无关变量操作尽量不要放在临界区间 */
	if (is_vol_chg == 1)
	{
	  static uint32_t seq;
	  seq++;
	  if (uart_tcb[XSD_PORT].vol_set == 0)
		dtq_answer_start_boardcast(128, (uint8_t *)&seq);
	  else
	  {
		dtq_answer_start_boardcast(8, (uint8_t *)&seq);
		rt_hw_uart_power_on(&rp555_hw_uart_port[XSD_PORT]);
	  }
	  log_print("r_PORT", port, "uID[%2d]: %010u line0:%d line1:%d, flg:%d\n", jsd_pac->upos,
		 *(uint32_t *)(jsd_pac->uID), s.b.line0_s, s.b.line1_s, uart_tcb[XSD_PORT].vol_flg);
	}
  } 
  
/* UART 接收线程 */
uint8_t uart_rcmd_voc_set(uint8_t port, uart_pac_t *rpac, uart_pac_t *tpac)
{
  log_print("r_uart", port, "dfu_s:%d vol:%d\n", uart_tcb[port].dfu_s, rpac->data[0]);
  /* 修改音量临界区间 */
  rt_enter_critical();
  if (uart_tcb[port].vol_set == (rpac->data[0]/3))
  {
    uart_tcb[port].vol_flg = 0;
    jsd_vol_sync(port, uart_tcb[port].vol_set);
  }
  rt_exit_critical();
  /* 将无关变量操作尽量不要放在临界区间 */
  if (uart_tcb[port].vol_set == 0)
    app_uart_power_off(&rp555_hw_uart_port[port]);
  uart_tcb[port].tm_lock = 0;
  return 0;
}

总结

在多线程变成中,对于操作,与指令实现一一对应最好是使用指令流水号的逻辑,如果由于客观原因无法实现,则在多线程的变量判断和修改的地方做好原子操作的控制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值