寒假期间在家调试
不过前面有一部分故障的排查是有计划记录下这些过程之前进行的
背景
硬件平台
外设
- UART1
115200( 8N1, 无流控, 发送) - UART6
100000( 9E1, 无流控, UART, ) - CAN
数字控制转矩电流, - TIMER 1000hz
为,
使用
20210130
故障
在
为了
对照
CubeMX
求助了一波学长
不过无济于事
MR 2021/1/30 21:05:10
一样
MR 2021/1/30 21:05:13
检查贵了
MR 2021/1/30 21:05:15
过了
MR 2021/1/30 21:05:23
复位后接收到的数据也是对的
Lee 2021/1/30 21:05:30
那个 rxbuffer 开小一点, 先用 1 一个字节一个字节接
MR 2021/1/30 21:06:08
好 我试试
MR 2021/1/30 21:09:35
不行
MR 2021/1/30 21:10:09
之前是 18 个收一次 然后复位后能收 3 个
MR 2021/1/30 21:10:48
现在是复位后能连收一长串的 1 字节
MR 2021/1/30 21:10:52
但是总数基本不变
MR 2021/1/30 21:11:13
收完差不多长度的之后 就又不收了
不过轮询能一直收
Lee 2021/1/30 21:25:57
你接收的那些数据是对的吗
Lee 2021/1/30 21:26:22
发送端先用电脑
MR 2021/1/30 21:27:10
绝大部分都对 有一个开关不对 不知道为什么
Lee 2021/1/30 21:27:28
你先用电脑发送试试
MR 2021/1/30 21:27:31
好
Lee 2021/1/30 21:30:10
还有, 把其他外设代码注释掉
关键部分
然后调整定时器的中断优先级后
总结
但是我依然困惑
20210201
故障
虽然现在串口能正常接收了
- A
B; 。
接收到的
但是
故障通道就是
- 此外
这个串口还死活没法开启, 。
虽然说通道变化时
第二个问题相对来说
经过一番排查后
DMA
//位于 HAL 库 stm32f4xx_hal_dma.c 文件中:
void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma)
{
...
/* Transfer Error Interrupt management ***************************************/
if ((tmpisr & (DMA_FLAG_TEIF0_4 << hdma->StreamIndex)) != RESET)
{
if(__HAL_DMA_GET_IT_SOURCE(hdma, DMA_IT_TE) != RESET)
{
/* Disable the transfer error interrupt */
hdma->Instance->CR &= ~(DMA_IT_TE);
/* Clear the transfer error flag */
regs->IFCR = DMA_FLAG_TEIF0_4 << hdma->StreamIndex;
/* Update error code */
hdma->ErrorCode |= HAL_DMA_ERROR_TE;
}
}
...
}
DMA
这里在网上搜到了一种说法
这个说法乍一听很有道理
//位于 HAL 库 stm32f4xx_hal_uart.c 文件中:
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
...
/* Enable the DMA stream */
tmp = (uint32_t *)&pData;
HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t *)tmp, Size);
/* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */
__HAL_UART_CLEAR_OREFLAG(huart);
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Parity Error Interrupt */
SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Enable the DMA transfer for the receiver request by setting the DMAR bit
in the UART CR3 register */
SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
于是我决定不使用
BTW
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if (huart == (&huart6))
{
if(huart->ErrorCode | HAL_UART_ERROR_ORE)
{
errorCount++;
}
}
}
修改后
那么这就奇怪了
也怀疑过是不是串口参数的问题
有一份代码是大疆某个官方车的开源代码
开始百思不得其解
从跟朋友的聊天记录找到一个图
可能是老天看不下去了
https://community.st.com/s/question/0D50X00009XkeWfSAJ/stm32f3-uart-dma-problem
他提到是数据长度的问题
额外收获的是
其实
总结
20210202
故障
新的一天
串口自从改成
外设在裸机条件下基本稳定后
参考了正点原子的
跑马灯任务写好
其实也是预期之中的
在调试状态下设置了一些断点OSStart()
OSStart()
最后得到如下信息
发现
但是, 说明程序应该没有正常调度, 程序一开始
也成功进入最高优先级的内部任务, 并且发现是, 发现程序一直在优先级最高的
每次进入调度器后就退出了, 这里就是不合理的了。 理论上信号量, 调度就应该要到我们的跑马灯程序了, 但是调度程序因为什么原因并没有调度, 而是退出了, 。 继续跟进调度程序
然后发现不执行调度的原因是 中断嵌套计数, 说明, 所以拒绝执行调度, 但是这个时候用, 终于学会使用( ) 。 最后查
直接给, 发现理论上时在进入中断时计数就会++, 退出时–, 从而计算有多少层中断嵌套, 接着查发现原因是在; Systick, 进, 退出时却因为有判断发现, 就没有–直接退出了, 。
图为
图为
所以说要么不让
我认为这是一个很明显的
带着疑问
在移植最新版
移植的文件也有变化
总结
20210205
这个故障从
故障
UCOS
进调试状态搞一搞
这里要先插播介绍一下软件的架构
main()
ChassisTask
此外
此外
RCTask
void DMA2_Stream1_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Stream1_IRQn 0 */
#ifdef APP_UCOS_EN
OSIntEnter();
#endif
/* USER CODE END DMA2_Stream1_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart6_rx);
/* USER CODE BEGIN DMA2_Stream1_IRQn 1 */
#ifdef APP_UCOS_EN
OSIntExit();
#endif
/* USER CODE END DMA2_Stream1_IRQn 1 */
}
void USART6_IRQHandler(void)
{
/* USER CODE BEGIN USART6_IRQn 0 */
#ifdef APP_UCOS_EN
OSIntEnter();
//CPU_SR_ALLOC();
//CPU_CRITICAL_ENTER();
#endif
/* USER CODE END USART6_IRQn 0 */
HAL_UART_IRQHandler(&huart6);
/* USER CODE BEGIN USART6_IRQn 1 */
#ifdef APP_UCOS_EN
//CPU_CRITICAL_EXIT();
OSIntExit();
#endif
/* USER CODE END USART6_IRQn 1 */
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == (&RC_HUART))
{
#ifdef APP_UCOS_EN
OS_ERR err;
//CPU_SR_ALLOC();
//CPU_CRITICAL_ENTER();
#endif
// memcpy(RC_Origin_Buffer, RC_UART_Buffer, 18);
#ifdef APP_UCOS_EN
//CPU_CRITICAL_EXIT();
OSTaskSemPost(&RCTaskTCB, OS_OPT_POST_1, &err);
#else
HAL_UART_Receive_DMA(&RC_HUART, RC_UART_Buffer, 18);
#endif
}
}
void rc_task(void *p_arg)
{
#ifdef APP_UCOS_EN
OS_ERR err;
p_arg = p_arg;
while (1)
{
OSTaskSemPend(0, OS_OPT_PEND_BLOCKING, NULL, &err); // Waiting for RC UART Interrupt
#endif
RC_CtrlData.ch1 = (RC_Origin_Buffer[0] | RC_Origin_Buffer[1] << 8) & 0x07FF;
RC_CtrlData.ch1 -= 1024;
RC_CtrlData.ch2 = (RC_Origin_Buffer[1] >> 3 | RC_Origin_Buffer[2] << 5) & 0x07FF;
RC_CtrlData.ch2 -= 1024;
RC_CtrlData.ch3 = (RC_Origin_Buffer[2] >> 6 | RC_Origin_Buffer[3] << 2 | RC_Origin_Buffer[4] << 10) & 0x07FF;
RC_CtrlData.ch3 -= 1024;
RC_CtrlData.ch4 = (RC_Origin_Buffer[4] >> 1 | RC_Origin_Buffer[5] << 7) & 0x07FF;
RC_CtrlData.ch4 -= 1024;
/* prevent remote control zero deviation */
if (RC_CtrlData.ch1 <= 5 && RC_CtrlData.ch1 >= -5)
{
RC_CtrlData.ch1 = 0;
}
if (RC_CtrlData.ch2 <= 5 && RC_CtrlData.ch2 >= -5)
{
RC_CtrlData.ch2 = 0;
}
if (RC_CtrlData.ch3 <= 5 && RC_CtrlData.ch3 >= -5)
{
RC_CtrlData.ch3 = 0;
}
if (RC_CtrlData.ch4 <= 5 && RC_CtrlData.ch4 >= -5)
{
RC_CtrlData.ch4 = 0;
}
RC_CtrlData.sw1 = ((RC_Origin_Buffer[5] >> 4) & 0x000C) >> 2;
RC_CtrlData.sw2 = (RC_Origin_Buffer[5] >> 4) & 0x0003;
if ((abs(RC_CtrlData.ch1) > 660) || \
(abs(RC_CtrlData.ch2) > 660) || \
(abs(RC_CtrlData.ch3) > 660) || \
(abs(RC_CtrlData.ch4) > 660))
{
memset(&RC_CtrlData, 0, sizeof(struct RC_Ctl_t));
#ifdef APP_UCOS_EN
continue;
#else
return;
#endif
}
RC_CtrlData.x = RC_Origin_Buffer[6] | (RC_Origin_Buffer[7] << 8); // x axis
RC_CtrlData.y = RC_Origin_Buffer[8] | (RC_Origin_Buffer[9] << 8);
RC_CtrlData.z = RC_Origin_Buffer[10] | (RC_Origin_Buffer[11] << 8);
RC_CtrlData.press_l = RC_Origin_Buffer[12];
RC_CtrlData.press_r = RC_Origin_Buffer[13];
RC_CtrlData.kb.key_code = RC_Origin_Buffer[14] | RC_Origin_Buffer[15] << 8; // keyboard code
RC_CtrlData.wheel = (RC_Origin_Buffer[16] | RC_Origin_Buffer[17] << 8) - 1024;
#ifdef APP_UCOS_EN
}
#endif
}
回到故障
经过一段时间的观察
那么
查阅得到
接下来
查看几个关键寄存器的状态
根据
只可惜
接下来可以想办法还原事故现场
查询
根据之前得到的信息
在此图还可以看到
PendSV
接下来
可惜
再看看 PendSV
;********************************************************************************************************
; HANDLE PendSV EXCEPTION
; void OS_CPU_PendSVHandler(void)
;
; Note(s) : 1) PendSV is used to cause a context switch. This is a recommended method for performing
; context switches with Cortex-M. This is because the Cortex-M auto-saves half of the
; processor context on any exception, and restores same on return from exception. So only
; saving of R4-R11 & R14 is required and fixing up the stack pointers. Using the PendSV exception
; this way means that context saving and restoring is identical whether it is initiated from
; a thread or occurs due to an interrupt or exception.
;
; 2) Pseudo-code is:
; a) Get the process SP
; b) Save remaining regs r4-r11 & r14 on process stack;
; c) Save the process SP in its TCB, OSTCBCurPtr->OSTCBStkPtr = SP;
; d) Call OSTaskSwHook();
; e) Get current high priority, OSPrioCur = OSPrioHighRdy;
; f) Get current ready thread TCB, OSTCBCurPtr = OSTCBHighRdyPtr;
; g) Get new process SP from TCB, SP = OSTCBHighRdyPtr->OSTCBStkPtr;
; h) Restore R4-R11 and R14 from new process stack;
; i) Perform exception return which will restore remaining context.
;
; 3) On entry into PendSV handler:
; a) The following have been saved on the process stack (by processor):
; xPSR, PC, LR, R12, R0-R3
; b) Processor mode is switched to Handler mode (from Thread mode)
; c) Stack is Main stack (switched from Process stack)
; d) OSTCBCurPtr points to the OS_TCB of the task to suspend
; OSTCBHighRdyPtr points to the OS_TCB of the task to resume
;
; 4) Since PendSV is set to lowest priority in the system (by OSStartHighRdy() above), we
; know that it will only be run when no other exception or interrupt is active, and
; therefore safe to assume that context being switched out was using the process stack (PSP).
;
; 5) Increasing priority using a write to BASEPRI does not take effect immediately.
; (a) IMPLICATION This erratum means that the instruction after an MSR to boost BASEPRI
; might incorrectly be preempted by an insufficient high priority exception.
;
; (b) WORKAROUND The MSR to boost BASEPRI can be replaced by the following code sequence:
;
; CPSID i
; MSR to BASEPRI
; DSB
; ISB
; CPSIE i
;********************************************************************************************************
OS_CPU_PendSVHandler
CPSID I ; Cortex-M7 errata notice. See Note #5
MOV32 R2, OS_KA_BASEPRI_Boundary ; Set BASEPRI priority level required for exception preemption
LDR R1, [R2]
MSR BASEPRI, R1
DSB
ISB
CPSIE I
MRS R0, PSP ; PSP is process stack pointer
STMFD R0!, {R4-R11, R14} ; Save remaining regs r4-11, R14 on process stack
MOV32 R5, OSTCBCurPtr ; OSTCBCurPtr->StkPtr = SP;
LDR R1, [R5]
STR R0, [R1] ; R0 is SP of process being switched out
; At this point, entire context of process has been saved
MOV R4, LR ; Save LR exc_return value
BL OSTaskSwHook ; Call OSTaskSwHook() for FPU Push & Pop
MOV32 R0, OSPrioCur ; OSPrioCur = OSPrioHighRdy;
MOV32 R1, OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
MOV32 R1, OSTCBHighRdyPtr ; OSTCBCurPtr = OSTCBHighRdyPtr;
LDR R2, [R1]
STR R2, [R5]
ORR LR, R4, #0x04 ; Ensure exception return uses process stack
LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdyPtr->StkPtr;
LDMFD R0!, {R4-R11, R14} ; Restore r4-11, R14 from new process stack
MSR PSP, R0 ; Load PSP with new process SP
MOV32 R2, #0 ; Restore BASEPRI priority level to 0
MSR BASEPRI, R2
BX LR ; Exception return will restore remaining context
ALIGN ; Removes warning[A1581W]: added <no_padbytes> of padding at <address>
END
可以看出
也就是说BX LR
那么理论上
查看任务结构体MOV32 R1, OSTCBHighRdyPtr
LDR R2, [R1]
LDR R0, [R2]
MSR PSP, R0
所以这个
20210221
写了一天
接下来思路
查了一下手册
实际情况是
可以确认
强行看一眼
机智的我又突然想起0x20003910
这条路是有点走不通了
首先说说
PendSVBL OSTaskSwHook
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
/* Check if stack overflowed. */
stk_status = OSTaskStkRedzoneChk((OS_TCB *)0u);
if (stk_status != OS_TRUE) {
OSRedzoneHitHook(OSTCBCurPtr);
}
#endif
如果发现堆栈不对劲
说实话我现在严重怀疑
那么啥叫不对劲
那发现堆栈会溢出后
…… ( 省略 1K 条一样的)
StartTask used/free:119/393 usage:23% SP:0x200041b4 StkBase:0x20003b48 StkTop:0x20004348
LEDTask used/free:112/400 usage:21% SP:0x20001894 StkBase:0x200011f4 StkTop:0x200019f4
RCTask used/free:85/427 usage:16% SP:0x20003a2c StkBase:0x20003334 StkTop:0x20003b34
ChassisTask used/free:73/951 usage:7% SP:0x20000f14 StkBase:0x20000010 StkTop:0x20001010
StartTask used/free:119/393 usage:23% SP:0x200041b4 StkBase:0x20003b48 StkTop:0x20004348
LEDTask used/free:112/400 usage:21% SP:0x20001894 StkBase:0x200011f4 StkTop:0x200019f4
RCTask used/free:84/428 usage:16% SP:0x20003a2c StkBase:0x20003334 StkTop:0x20003b34
ChassisTask used/free:73/951 usage:7% SP:0x20000f14 StkBase:0x20000010 StkTop:0x20001010
StartTask used/free:119/393 usage:23% SP:0x200041b4 StkBase:0x20003b48 StkTop:0x20004348
LEDTask used/free:112/400 usage:21% SP:0x20001894 StkBase:0x200011f4 StkTop:0x200019f4
RCTask used/free:85/427 usage:16% SP:0x20003a2c StkBase:0x20003334 StkTop:0x20003b34
ChassisTask used/free:73/951 usage:7% SP:0x20000f14 StkBase:0x20000010 StkTop:0x20001010
Halt: RCTask Stack Overflow
StkPtr: 0x20000F14
对
另外还有一个数据
StkPtr 0x2000242C
StkLimitPtr 0x200001A8
StkBasePtr 0X20000010
StkSize 1024
StkUsed 73
StkFree 951
StkBasePtr + StkSize*CPU_STK(4) = 0x20001010
这个忘记是啥时候啥情况存的了
这就很疑问
1.
2.
3.
起床了
如图
拿到
按照地址反汇编
不管是出问题的
PRECISERR, 。 这行代码上面刚刚恢复中断
恢复后可能就触发了, 是, 。
再次观察
这还有个更奇怪的样本
按照