问题描述
在最新的 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;
}
总结
在多线程变成中,对于操作,与指令实现一一对应最好是使用指令流水号的逻辑,如果由于客观原因无法实现,则在多线程的变量判断和修改的地方做好原子操作的控制。