LPC2368系统中MUX多路复用器随机GPIO写入异常(约8小时一次)的故障排查求助
LPC2368系统中MUX多路复用器随机GPIO写入异常(约8小时一次)的故障排查求助
我手上有一套老的遗留系统,底层测量板SB用的是跑裸机C代码的LPC2368,上面还叠了一块跑Linux的LPC17xx板卡。
SB的核心逻辑分两部分:一个基础循环处理通用业务逻辑(比如CAN总线读取结果、电压测量结果的后续处理等);还有一个1ms的定时器中断服务程序,专门负责从AD转换器读取数值、通过DIO写控制MUX切换不同的输入电压通道,同时处理其他相关操作。
系统相关细节
- MUX是三状态循环切换:状态按
00→01→10→00循环,有个状态机专门跟踪当前阶段和选中的通道 - 外部AD转换器读取MUX输出,转换时间150ms;SPI选通ADC时,会先输出上一次的转换结果,同时启动新的转换
- 我用
FIO0SET和FIO0CLR来控制GPIO,不过是通过一个结构体封装的方式调用的,结构体定义如下:
typedef struct { volatile u32* pu32Dir; volatile u32* pu32Data; volatile u32* pu32Set; volatile u32* pu32Clr; volatile u32* pu32Mask; volatile u32* pu32FuncSel[2]; volatile u32* pu32PullEna[2]; } port_type; const port_type gaxPortList[5]= { { (volatile u32*)&FIO0DIR, (volatile u32*)&FIO0PIN, (volatile u32*)&FIO0SET, (volatile u32*)&FIO0CLR, (volatile u32*)&FIO0MASK, {(volatile u32*)&PINSEL0, (volatile u32*)&PINSEL1}, {(volatile u32*)&PINMODE0, (volatile u32*)&PINMODE1} }, // 其他端口定义省略 };
- 负责MUX控制的
Io_ReadAdc()是在1ms定时器中断里调用的,相关逻辑如下(只保留出问题的AMUX_VM_SB部分):
// 提前声明的配置项 DECL_AIO_CONFIG(AIO_SYS_VM, 0x40, AMUX_VM_SB, 0, AIO_OS_CNT_A, AIO_AVG_CNT_A, 151), DECL_AIO_CONFIG(AIO_MIDPOINT_VM, 0x40, AMUX_VM_SB, 1, AIO_OS_CNT_A, AIO_AVG_CNT_A, 152), DECL_AIO_CONFIG(AIO_IOB_EFD_VM, 0x40, AMUX_VM_SB, 2, AIO_OS_CNT_A, AIO_AVG_CNT_A, 153), static void Io_ReadAdc(void) { static u8 u8CurrentDevice = 0; static u8 u8NextDevice = 0; static ms_timer_type tSbMuxVmTimer; static aio_meas_state_type eSbMuxVmMeasState = AIO_MEAS_STATE_IDLE; static u8 u8SbMuxVmActiveChanDevNum = SB_MUX_VM_MIDPOINT_INDEX; static u8 u8SbMuxVmOldChanDevNum = SB_MUX_VM_SYS_INDEX; const aio_config_type* pxConfig; aio_state_type* pxState; if (!gbInitialized) return; pxConfig = &(gaxAioConfig[u8CurrentDevice]); pxState = &(gaxAioState[pxConfig->eDevice]); if (pxConfig->eMux == AMUX_VM_SB) // 出问题的就是这个MUX { if (eSbMuxVmMeasState == AIO_MEAS_STATE_IDLE) { if (u8SbMuxVmActiveChanDevNum == SB_MUX_VM_SYS_INDEX) { u8SbMuxVmOldChanDevNum = u8SbMuxVmActiveChanDevNum; u8SbMuxVmActiveChanDevNum = SB_MUX_VM_MIDPOINT_INDEX; } else if (u8SbMuxVmActiveChanDevNum == SB_MUX_VM_MIDPOINT_INDEX) { u8SbMuxVmOldChanDevNum = u8SbMuxVmActiveChanDevNum; u8SbMuxVmActiveChanDevNum = SB_MUX_VM_EFD_INDEX; } else if (u8SbMuxVmActiveChanDevNum == SB_MUX_VM_EFD_INDEX) { u8SbMuxVmOldChanDevNum = u8SbMuxVmActiveChanDevNum; u8SbMuxVmActiveChanDevNum = SB_MUX_VM_SYS_INDEX; } Io_SetDoValue(DIO_VM_SB_MUX_SELECT, gaxAioConfig[u8SbMuxVmActiveChanDevNum].u8MuxChannel, FALSE, FALSE); eSbMuxVmMeasState = AIO_MEAS_STATE_WAIT_FOR_CHANGES; return; // 直接返回,下一次ISR调用再继续,不用等超过1kHz的周期 } else if (eSbMuxVmMeasState == AIO_MEAS_STATE_WAIT_FOR_CHANGES) { pxConfig = &(gaxAioConfig[u8SbMuxVmOldChanDevNum]); pxState = &(gaxAioState[pxConfig->eDevice]); Io_AdcReadAndHandleConversion(pxState, pxConfig, pxConfig->u8AdcChannel, &u8NextDevice); MsTimerReset(&tSbMuxVmTimer); eSbMuxVmMeasState = AIO_MEAS_STATE_MEASURE; } else if (eSbMuxVmMeasState == AIO_MEAS_STATE_MEASURE) { if (u8MsTimerExceeded((&tSbMuxVmTimer), gaxAioConfig[u8SbMuxVmActiveChanDevNum].u16MinWait)) { eSbMuxVmMeasState = AIO_MEAS_STATE_IDLE; } } } else { // 其他MUX的处理逻辑,此处省略 } }
故障现象
随机出现,平均大概每8小时一次。当MUX切换到10状态后,会突然出现一个精准的100.00 us的跳变凹口——看起来像是要切回00状态,但瞬间又弹回10,之后就像完全没发生过异常一样继续正常循环,后续的MUX切换一切正常,直到下一次随机间隔后再出现问题。
我已经做了这些排查:
- 代码走查没找到明显问题
- 检查过SB是否重启:通过UART调试信息确认,异常发生时SB没有重启
- 听说过读-modify-write问题,但不确定是不是这里的情况——毕竟我用的是FIO0SET和FIO0CLR寄存器,不是直接操作Data寄存器
现在想求助大家:这种随机的、极短的GPIO跳变到底可能是什么原因?会不会真的是读-改-写的隐患?或者还有其他硬件/软件层面的可能性我没考虑到?
内容来源于stack exchange
相关产品推荐
相关产品推荐

