溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python實現(xiàn)CAN報文轉換的方法

發(fā)布時間:2020-07-29 09:09:23 來源:億速云 閱讀:450 作者:小豬 欄目:開發(fā)技術

這篇文章主要講解了Python實現(xiàn)CAN報文轉換的方法,內容清晰明了,對此有興趣的小伙伴可以學習一下,相信大家閱讀完之后會有幫助。

一、CAN報文簡介

CAN是控制器局域網(wǎng)絡(Controller Area Network, CAN)的簡稱,是由以研發(fā)和生產(chǎn)汽車電子產(chǎn)品著稱的德國BOSCH公司開發(fā)的,并最終成為國際標準(ISO 11898),是國際上應用最廣泛的現(xiàn)場總線之一。 在北美和西歐,CAN總線協(xié)議已經(jīng)成為汽車計算機控制系統(tǒng)和嵌入式工業(yè)控制局域網(wǎng)的標準總線,并且擁有以CAN為底層協(xié)議專為大型貨車和重工機械車輛設計的J1939協(xié)議。

CAN總線以報文為單位進行數(shù)據(jù)傳送。CAN報文按照幀格式可分為標準幀和擴展幀,標準幀是具有11位標識符的CAN幀,擴展幀是具有29位標識符的CAN幀。按照幀類型可分為:1.從發(fā)送節(jié)點向其它節(jié)點發(fā)送數(shù)據(jù);2.遠程幀:向其它節(jié)點請求發(fā)送具有同一識別符的數(shù)據(jù)幀;3.錯誤幀:指明已檢測到總線錯誤;4.過載幀:過載幀用以在數(shù)據(jù)幀(或遠程幀)之間提供一附加的延時。共有兩種編碼格式:Intel格式和Motorola格式,在編碼優(yōu)缺點上,Motorola格式與Intel格式并沒有孰優(yōu)孰劣之分,只不過根據(jù)設計者的習慣,由用戶自主選擇罷了。當然,對于使用者來講,在進行解析之前,就必須要知道編碼的格式是哪一種,否則,就不能保證正確地解析信號的含義。以下就以8位字節(jié)編碼方式的CAN總線信號為例,詳細分析一下兩者之間的區(qū)別。

Intel編碼格式

當一個信號的數(shù)據(jù)長度不超過1個字節(jié)(8位)并且信號在一個字節(jié)內實現(xiàn)(即該信號沒有跨字節(jié)實現(xiàn)):該信號的高位(S_msb)將被放在該字節(jié)的高位,信號的低位(S_lsb)將被放在該字節(jié)的低位。

當一個信號的數(shù)據(jù)長度超過1個字節(jié)(8位)或者數(shù)據(jù)長度不超過一個字節(jié)但是采用跨字節(jié)方式實現(xiàn)時:該信號的高位(S_msb)將被放在高字節(jié)(MSB)的高位,信號的低位(S_lsb)將被放在低字節(jié)(LSB)的低位。

Motorola編碼格式

當一個信號的數(shù)據(jù)長度不超過1個字節(jié)(8位)并且信號在一個字節(jié)內實現(xiàn)(即該信號沒有跨字節(jié)實現(xiàn)):該信號的高位(S_msb)將被放在該字節(jié)的高位,信號的低位(S_lsb)將被放在該字節(jié)的低位。

當一個信號的數(shù)據(jù)長度超過1個字節(jié)(8位)或者數(shù)據(jù)長度不超過一個字節(jié)但是采用跨字節(jié)方式實現(xiàn)時:該信號的高位(S_msb)將被放在低字節(jié)(MSB)的高位,信號的低位(S_lsb)將被放在高字節(jié)(LSB)的低位。

可以看出,當一個信號的數(shù)據(jù)長度不超過1Byte時,Intel與Motorola兩種格式的編碼結果沒有什么不同,完全一樣。當信號的數(shù)據(jù)長度超過1Byte時,兩者的編碼結果出現(xiàn)了明顯的不同。

二、CAN報文轉換工具需求分析

1、 支持標準幀的CAN報文的轉換,擴展幀暫不支持

2、 CAN報文支持Intel、motorola兩種編碼,先支持motorola格式,后期追加Intel格式

3、 工具具有一定的容錯處理能力、報告生成能力

4、 制定統(tǒng)一格式,方便使用者修改測試腳本

5、增加交互模式,鍵盤輸入,控制臺輸出;例如:

提示語:startBit:length:minValue:maxValue:setValue

輸入:35:1:0:1:1

或:35:1:::1

控制臺輸出:00 00 00 00 08 00 00 00

Intel和Motorola編碼舉例:

Python實現(xiàn)CAN報文轉換的方法

三、交互模式

代碼如下:

import sys
print("----------------歡迎使用CAN報文轉換工具交互模式----------------")
print("請輸入CAN信號,格式為:startBit:length:minValue:maxValue:setValue")
print("例如:32:1:0:1:1")
print("或者省略minValue和maxValue:35:1:::1")
print("信號輸入結束請再按一次回車")
 
#十進制轉換成二進制list
def octToBin(octNum, bit):
 while(octNum != 0):
 bit.append(octNum%2)
 octNum = int(octNum/2)
 for i in range(64-len(bit)):
 bit.append(0)
 
sig = []
startBit = []
length = []
setValue = []
#輸入CAN信號
while True:
 input_str = input()
 if not len(input_str):
 break
 if(input_str.count(":")<4):
 print("輸入格式錯誤,參數(shù)缺少setValue,請重新輸入!")
 continue
 if(input_str.split(":")[4]==""):
 print("setValue參數(shù)不能為空,請重新輸入!")
 continue
 sig.append(input_str)
#解析CAN信號
for i in range(len(sig)):
 startBit.append(int(sig[i].split(":")[0]))
 length.append(int(sig[i].split(":")[1]))
 setValue.append(int(sig[i].split(":")[4]))
#CAN數(shù)組存放CAN報文值 
CAN = []
for i in range(64):
 CAN.append(-1)
for i in range(len(startBit)):
 #長度超過1Byte的情況,暫不支持
 if(length[i]>16):
 print("CAN信號長度超過2Byte,暫不支持!?。?quot;)
 sys.stdin.readline()
 sys.exit()
 #長度未超過1Byte的情況且未跨字節(jié)的信號
 if((startBit[i]%8 + length[i])<=8):
 for j in range(length[i]):
  bit = []
  #setValue的二進制值按字節(jié)位從低到高填
  octToBin(setValue[i],bit)
  #填滿字節(jié)長度值
  if(CAN[startBit[i]+j]==-1):
  CAN[startBit[i]+j] = bit[j]
  #字節(jié)存在沖突
  else:
  print(sig[i] + "字節(jié)位存在沖突,生成CAN報文失?。。?!")
  sys.stdin.readline()
  sys.exit()
 #跨字節(jié)的信號
 else:
 #高位位數(shù)和低位位數(shù)
 highLen = 8 - startBit[i]%8
 lowLen = length[i] - highLen
 bit = []
 #setValue的二進制值按字節(jié)位從低到高填
 octToBin(setValue[i],bit)
 #先填進信號的高位
 for j1 in range(highLen):
  if(CAN[startBit[i]+j1]==-1):
  CAN[startBit[i]+j1] = bit[j1]
  #字節(jié)存在沖突
  else:
  print(sig[i] + "字節(jié)位存在沖突,生成CAN報文失?。。。?quot;)
  sys.stdin.readline()
  sys.exit()
 #再填進信號的低位
 for j2 in range(lowLen):
  if(CAN[(int(startBit[i]/8)-1)*8+j2]==-1):
  CAN[(int(startBit[i]/8)-1)*8+j2] = bit[highLen+j2]
  #字節(jié)存在沖突
  else:
  print(sig[i] + "字節(jié)位存在沖突,生成CAN報文失?。。。?quot;)
  sys.stdin.readline()
  sys.exit()
#剩余位默認值設為0
for i in range(64):
 if(CAN[i]==-1):
 CAN[i] = 0
#----------------將二進制list每隔8位轉換成十六進制輸出----------------
#其中,map()將list中的數(shù)字轉成字符串,按照Motorola格式每隔8位采用了逆序
# ''.join()將二進制list轉換成二進制字符串,int()將二進制字符串轉換成十進制
#hex()再將十進制轉換成十六進制,upper()轉換成大寫,兩個lstrip()將"0X"刪除,
#zfill()填充兩位,輸出不換行,以空格分隔
print(hex(int(''.join(map(str,CAN[7::-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
print(hex(int(''.join(map(str,CAN[15:7:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
print(hex(int(''.join(map(str,CAN[23:15:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
print(hex(int(''.join(map(str,CAN[31:23:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
print(hex(int(''.join(map(str,CAN[39:31:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
print(hex(int(''.join(map(str,CAN[47:39:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
print(hex(int(''.join(map(str,CAN[55:47:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
print(hex(int(''.join(map(str,CAN[63:55:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2)) 
 

運行截圖:

Python實現(xiàn)CAN報文轉換的方法

錯誤提示:

Python實現(xiàn)CAN報文轉換的方法

四、配置項模式

配置文件如下:

##注釋
::start
#編碼格式:0=Intel;1=Motorola
encodeType=1
#幀格式:0=標準幀;1=擴展幀;
canMode=0
#幀類型:0=數(shù)據(jù)幀;...
canType=0
#默認初始值(0~1)
defaultValue=0
#MSG定義
msgName=BCM_FrP01
msgID=0x2CD
#長度(BYTE)
msgLength=8
#signal定義
#sigName=name:startBit:length:minValue:maxValue:setValue
#sigName=ReverseSw:25:6:0:1:13
#sigName=Trunk_BackDoor_Sts:33:2:0:1:2
#sigName=DRVUnlockState:37:2:0:1:3
#sigName=HeadLampLowBeam:40:8:0:1:60
#sigName=HoodStatus:51:1:0:1:0
#sigName=HeadLampHighBeam:52:1:0:1:0
#sigName=RLDoorStatus:59:1:0:1:0
#sigName=RRDoorStatus:58:1:0:1:0
#sigName=PsgDoorStatus:57:2:0:1:0
sigName=One:0:8:0:255:165
sigName=Two:24:12:0:4095:1701
sigName=Three:54:5:0:31:25
::end
::start
#編碼格式:0=Intel;1=Motorola
encodeType=1
#幀格式:0=標準幀;1=擴展幀;
canMode=0
#幀類型:0=數(shù)據(jù)幀;...
canType=0
#默認初始值(0~1)
defaultValue=0
#MSG定義
msgName=BCM_FrP
msgID=0x2CD
#長度(BYTE)
msgLength=8
#signal定義
#sigName=name:startBit:length:minValue:maxValue:setValue
#sigName=ReverseSw:25:6:0:1:13
#sigName=Trunk_BackDoor_Sts:33:2:0:1:2
#sigName=DRVUnlockState:37:2:0:1:3
#sigName=HeadLampLowBeam:40:8:0:1:60
#sigName=HoodStatus:51:1:0:1:0
#sigName=HeadLampHighBeam:52:1:0:1:0
#sigName=RLDoorStatus:59:1:0:1:0
#sigName=RRDoorStatus:58:1:0:1:0
#sigName=PsgDoorStatus:57:2:0:1:0
sigName=One:35:1:0:1:1
::end

代碼如下:

#!/usr/bin/python
defaultValue = 0
sigName = []
startBit = []
length = []
minValue = []
maxValue = []
setValue = []
#CAN數(shù)組存放CAN報文值
CAN = []
logFile = open("log.txt","w")
def parseConfig():
 config = open("Config.txt","r")
 
 count = 0
 isError = False
 for line in config:
 line = line.strip()
 #注釋
 if(line.find("#")>=0):
  continue
 #開始標記
 elif(line.find("::start")>=0):
  count = count + 1
  isError = False
  if(count>1):
  sigName.clear()
  startBit.clear()
  length.clear()
  setValue.clear()
  continue
  else:
  continue
 elif(isError == True):
  continue
 #編碼格式
 elif(line.find("encodeType")>=0):
  encodeType = line.split("=")[1]
  if(encodeType != "1"):
  isError = True
  print(str(count) + ". CAN報文生成失?。。?!目前僅支持Motorola編碼格式,暫不支持Intel編碼格式!")
  logFile.write("%d. CAN報文生成失?。。?!目前僅支持Motorola編碼格式,暫不支持Intel編碼格式!\n" % count)
  continue
 #幀格式
 elif(line.find("canMode")>=0):
  canMode = line.split("=")[1]
  if(canMode != "0"):
  isError = True
  print(str(count) + ". CAN報文生成失敗?。?!目前僅支持標準幀,暫不支持擴展幀!")
  logFile.write("%d. CAN報文生成失?。。?!目前僅支持標準幀,暫不支持擴展幀!\n" % count)
  continue
 #幀類型
 elif(line.find("canType")>=0):
  canType = line.split("=")[1]
  if(canType != "0"):
  isError = True
  print(str(count) + ". CAN報文生成失?。。?!目前僅支持數(shù)據(jù)幀,暫不支持其他幀!")
  logFile.write("%d. CAN報文生成失?。。。∧壳皟H支持數(shù)據(jù)幀,暫不支持其他幀!\n" % count)
  continue
 #默認初始值
 elif(line.find("defaultValue")>=0):
  global defaultValue
  defaultValue = int(line.split("=")[1])
 #MSG名稱
 elif(line.find("msgName")>=0):
  msgName = line.split("=")[1]
 #MSGID
 elif(line.find("msgID")>=0):
  msgID = line.split("=")[1]
 #MSG長度
 elif(line.find("msgLength")>=0):
  msgLength = line.split("=")[1]
 #signal定義
 elif(line.find("sigName")>=0):
  sigName.append(line.split(":")[0].split("=")[1])
  startBit.append(int(line.split(":")[1]))
  length.append(int(line.split(":")[2]))
  #minValue.append(int(line.split(":")[3]))
  #maxValue.append(int(line.split(":")[4]))
  setValue.append(int(line.split(":")[5]))
 elif(line.find("::end")>=0):
  
  rV,errMsg = getCANMessage()
  if(rV == "-1"):
  isError = True
  print(str(count) + ". CAN報文生成失?。。?!" + errMsg)
  logFile.write("%d. CAN報文生成失敗?。?!%s\n" % (count,errMsg))
  continue
  
  print(str(count) + ". CAN報文生成成功!??!")
  logFile.write("%d. CAN報文生成成功!??!\n" % count)
  #----------------------------輸出標題信息----------------------------
  print("msgName\t\tmsgID\t\tmsgLen\t\tmsgData")
  logFile.write("msgName\t\tmsgID\t\tmsgLen\t\tmsgData\n")
  if(len(msgName)<8):
  print(msgName + "\t\t",end="")
  logFile.write("%s\t\t" % msgName)
  else:
  print(msgName + "\t",end="")
  logFile.write("%s\t" % msgName)
  print(msgID + "\t\t",end="")
  logFile.write("%s\t\t" % msgID)
  print(msgLength + "\t\t",end="")
  logFile.write("%s\t\t" % msgLength)
  #----------------將二進制list每隔8位轉換成十六進制輸出----------------
  #其中,map()將list中的數(shù)字轉成字符串,按照Motorola格式每隔8位采用了逆序
  # ''.join()將二進制list轉換成二進制字符串,int()將二進制字符串轉換成十進制
  #hex()再將十進制轉換成十六進制,upper()轉換成大寫,兩個lstrip()將"0X"刪除,
  #zfill()填充兩位,輸出不換行,以空格分隔
  print(hex(int(''.join(map(str,CAN[7::-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  print(hex(int(''.join(map(str,CAN[15:7:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  print(hex(int(''.join(map(str,CAN[23:15:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  print(hex(int(''.join(map(str,CAN[31:23:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  print(hex(int(''.join(map(str,CAN[39:31:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  print(hex(int(''.join(map(str,CAN[47:39:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  print(hex(int(''.join(map(str,CAN[55:47:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2) + " ",end="")
  print(hex(int(''.join(map(str,CAN[63:55:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  logFile.write("%s " % hex(int(''.join(map(str,CAN[7::-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  logFile.write("%s " % hex(int(''.join(map(str,CAN[15:7:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  logFile.write("%s " % hex(int(''.join(map(str,CAN[23:15:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  logFile.write("%s " % hex(int(''.join(map(str,CAN[31:23:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  logFile.write("%s " % hex(int(''.join(map(str,CAN[39:31:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  logFile.write("%s " % hex(int(''.join(map(str,CAN[47:39:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  logFile.write("%s " % hex(int(''.join(map(str,CAN[55:47:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  logFile.write("%s\n" % hex(int(''.join(map(str,CAN[63:55:-1])),2)).upper().lstrip("0").lstrip("X").zfill(2))
  
 config.close()
 
#十進制轉換成二進制list  
def octToBin(octNum, bit):
 while(octNum != 0):
 bit.append(octNum%2)
 octNum = int(octNum/2)
 for i in range(64-len(bit)):
 bit.append(0)
 
#獲取CAN報文值
def getCANMessage():
 CAN.clear()
 for i in range(64):
 CAN.append(-1)
 for i in range(len(startBit)):
 #長度超過1Byte的情況,暫不支持
 if(length[i]>16):
  errMsg = " CAN信號長度超過2Byte,暫不支持?。。?quot;
  #print(sigName[i] + errMsg)
  return "-1",errMsg
 #長度未超過1Byte的情況且未跨字節(jié)的信號
 if((startBit[i]%8 + length[i])<=8):
  for j in range(length[i]):
  bit = []
  #setValue的二進制值按字節(jié)位從低到高填
  octToBin(setValue[i],bit)
  #填滿字節(jié)長度值
  if(CAN[startBit[i]+j]==-1):
   CAN[startBit[i]+j] = bit[j]
  #字節(jié)存在沖突
  else:
   errMsg = " 字節(jié)位存在沖突,生成CAN報文失敗?。?!"
   #print(sigName[i] + errMsg)
   return "-1",errMsg
 #跨字節(jié)的信號
 else:
  #高位位數(shù)和低位位數(shù)
  highLen = 8 - startBit[i]%8
  lowLen = length[i] - highLen
  bit = []
  #setValue的二進制值按字節(jié)位從低到高填
  octToBin(setValue[i],bit)
  #先填進信號的高位
  for j1 in range(highLen):
  if(CAN[startBit[i]+j1]==-1):
   CAN[startBit[i]+j1] = bit[j1]
  #字節(jié)存在沖突
  else:
   errMsg = " 字節(jié)位存在沖突,生成CAN報文失?。。。?quot;
   #print(sigName[i] + errMsg)
   return "-1",errMsg
  #再填進信號的低位
  for j2 in range(lowLen):
  if(CAN[(int(startBit[i]/8)-1)*8+j2]==-1):
   CAN[(int(startBit[i]/8)-1)*8+j2] = bit[highLen+j2]
  #字節(jié)存在沖突
  else:
   errMsg = " 字節(jié)位存在沖突,生成CAN報文失?。。。?quot;
   #print(sigName[i] + errMsg)
   return "-1",errMsg
 #剩余位設為默認值
 for i in range(64):
 if(CAN[i]==-1):
  CAN[i] = defaultValue
 
 #若無錯誤則返回正確值
 return "0","success!"
 
if __name__ == "__main__":
 #調用parseConfig()函數(shù)開始執(zhí)行程序
 parseConfig()

運行結果:

1. CAN報文生成成功?。?!
msgName		msgID		msgLen		msgData
BCM_FrP01	0x2CD		8		A5 00 06 A5 00 06 40 00
2. CAN報文生成成功!??!
msgName		msgID		msgLen		msgData
BCM_FrP		0x2CD		8		00 00 00 00 08 00 00 00

看完上述內容,是不是對Python實現(xiàn)CAN報文轉換的方法有進一步的了解,如果還想學習更多內容,歡迎關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI