---
# System prepended metadata

title: 答SDO下SpeedRef異常
tags: [sandal]

---

# 狀況
> 協助排查，偶爾使用SDO時，會有此狀況，速度設定值會突然降至低速又回至原本速度。
> 不使用SDO 是正常的


工況:
> ethercat SDO發送間隔：100ms
> 會用SDO 輪訊每一條entry。
> ![](https://hackmd.io/_uploads/HkmE4E9Yh.png)
> 
> Speed picture from drive output. Maybe drive save the result in buffer, then output through enthernet.
> ![](https://hackmd.io/_uploads/S1tAF4qt3.jpg)




# 分析
從速度圖可以看到，應該是有某種機制要求drive的command speed（黃色）為接近0。pulse width =1ms，1ms是記錄的週期，而且有些沒有記錄到。可以推測內部的state快速的轉換，才會有時有記錄到，有時沒有。
從actual speed（綠色） 來看，就算沒被記錄到，命令還是有下達的。
由於program 沒有thread和 timeslicing，全部code都是run to complete。從有黃色沒被記錄到的這種特性，表示output data 可能是一個額外的timer ISR，且使用的 data 沒有 buffer。如果計算的時間長或短了一點，剛好output to PC 的 timer ISR內，就會沒看到pulse。

這種情況一般即為權責不清，e.g. 兩個模組可操作同一 global 變數，且兩個模組內部的state也沒有sync。由於在SDO底下發生，有幾種情況。

1. SDO 有control 相關global 變數，並直接控制
    * 上述的狀況
3. control 有SDO相關的變數，但是在處理時，沒有處理好。比如SDO有error 要停下，control 在callback裡有停下，但是callback 沒有改變control 自身的state。所以下次routine來就蓋掉了。
4. control or SDO的變數同時被其它module存取，且下達錯誤。

## 事實
透過ethernet定閱的項目，只能是database有的項目。透過這個特性我們知道 `速度設定值（濾波前）` 在database中是id=53000，`u_qSpeedRef` 也是此值的原本存放值（會再做些轉換）
此值直接設定的就在`ecatslv`, `database`。看起來沒有其它的setter funciton。


# code
從 code 裡，有出現SDO字眼的.c檔案有 `CANopenComm`, `coeappl`, `database.c`, `ecatslv.c`, `ethercat.c`, `EthernetPCComm`, `mailbox`, `objdef`, `pressurectrl` ，比較有可能的有 `ecatslv`, `coeappl`, `ethercat`（可能性由可到低）。

在 `ecatslv` 確實有發現 include `control`
其中 `Ecat_DMA()` 
```clik=1259
			if (DmaMapping.w60FFOffset != 0xFFFF)
			{
				//lTemp = *((long*)(&u_awPdOutputData[0] + DmaMapping.w60FFOffset));
				memcpy(&lTemp, (u_awPdOutputData + DmaMapping.w60FFOffset),sizeof(DWORD));   // odd adress not get right long value
				if (u_w402Enable)
					lTemp = _IQ(((float)lTemp * 60.0 * Vel402Factor.adwdata[0] / DBVALUE(ENCODER_RESOLUTION).dwData / ENCODER_REV /Vel402Factor.adwdata[1]) / DBVALUE(MOTOR_MAXSPEED).fData);

				if (lTemp > _IQ(1.0))	lTemp = _IQ(1.0);
				else if (lTemp < _IQ(-1.0))	lTemp = _IQ(-1.0);
				if (StatMachine.StopRespFlag == 0)
					u_qSpeedRef = _IQmpy(u_qRealVMax, lTemp);
			}

			if(DmaMapping.w2BB8Offset != 0xFFFF)
			{
				memcpy(&fTemp, (u_awPdOutputData + DmaMapping.w2BB8Offset),sizeof(DWORD));
				lTemp = _IQ(fTemp / DBVALUE(MOTOR_RATEDSPEED).fData);

				if(lTemp > u_qRealVMax) lTemp = u_qRealVMax;
				else if (lTemp < -u_qRealVMax) lTemp = -u_qRealVMax;
				if (StatMachine.StopRespFlag == 0)
					u_qSpeedRef = lTemp;
			}

```
重點在於有動到 `u_qSpeedRef` 這個`contorl` variable，且是速度ref沒錯，~~但不是0。也有可能是在計算過程出了問題，變為0。~~ speed ref只有很接近0，但不等於0。隨應該要有的speed ref等比縮非常小。

`coeappl.c`, `ethercat.c` 裡都沒有 `u_qSpeedRef`。這一段計算裡出問題最有可能。

但是可以看到，用到了一大堆其它地方的數字，要有當下variable value才能知道是知道發生了什麼

比較大的問題是`ControlMode.wOptionMode !=  POSITION_MODE` 才會進入這一區，要看drive 是否有主動切換mode，因為user 這端一直維持在position mode。

但是實在沒有其它地方（除control以外）設定 `u_qSpeedRef`，所以我還是認為問題在這。
由於是接近0，我猜可能是分母突然變很大。