DSP28377S_CAN通訊
CAN通訊的由來
為適應“減少線束的數量”、“通過多個LAN,進行大量資料的高速通訊”的需要,1986 年德國電氣商博世公司開發出面向汽車的CAN 通訊協議。CAN屬於現場匯流排的範疇,它是一種有效支援分散式控制或實時控制的序列通訊網路。
CAN通訊格式
CAN通訊共有5種,分別為資料幀、遙控幀、錯誤幀、過載幀、幀間隔。資料幀格式由下圖所示,分為標準格式和擴充格式,筆者目前僅使用到標準資料幀,使用其中的64bits資料段進行CAN節點間的資料互動,以ID號區分資料型別。注意ACK!實際波形中彼己電平高度不一
CAN通訊配置
①初始化CAN的對映GPIO,使用TI封裝好的函式初始化CAN,選擇CAN時鐘源,設定波特率,使能CAN中斷觸發源,開啟CAN;
void InitCana(void)
{
InitCanaGpio();
//
// Initialize the CAN controller
//
CANInit(CANA_BASE);
//
// Setup CAN to be clocked off the M3/Master subsystem clock
//
CANClkSourceSelect(CANA_BASE, 0);
//
// Set up the bit rate for the CAN bus. This function sets up the CAN
// bus timing for a nominal configuration. You can achieve more control
// over the CAN bus timing by using the function CANBitTimingSet() instead
// of this one, if needed.
// In this example, the CAN bus is set to 500 kHz. In the function below,
// the call to SysCtlClockGet() is used to determine the clock rate that
// is used for clocking the CAN peripheral. This can be replaced with a
// fixed value if you know the value of the system clock, saving the extra
// function call. For some parts, the CAN peripheral is clocked by a fixed
// 8 MHz regardless of the system clock in which case the call to
// SysCtlClockGet() should be replaced with 8000000. Consult the data
// sheet for more information about CAN peripheral clocking.
//
CANBitRateSet(CANA_BASE, 200000000, 200000); // 波特率200kbps
//
// Enable interrupts on the CAN peripheral. This example uses static
// allocation of interrupt handlers which means the name of the handler
// is in the vector table of startup code. If you want to use dynamic
// allocation of the vector table, then you must also call CANIntRegister()
// here.
//
CANIntEnable(CANA_BASE, CAN_INT_MASTER | CAN_INT_ERROR | CAN_INT_STATUS);
//
// Enable the CAN for operation.
//
CANEnable(CANA_BASE);
//
// Enable CAN Global Interrupt line0
//
CANGlobalIntEnable(CANA_BASE, CAN_GLB_INT_CANINT0);
}
②確定收發快取陣列、ID號;
// CAN通訊
unsigned char ucRXMsgData1[8]; // CAN接受資料
unsigned char ucTXMsgData2[8]; // CAN傳送資料
unsigned char ucTXMsgData3[8]; // CAN傳送資料
unsigned char ucTXMsgData4[8]; // CAN傳送資料
unsigned char ucTXMsgData5[8]; // CAN傳送資料
unsigned char ucTXMsgData6[8]; // CAN傳送資料
tCANMsgObject sRXCANMessage1 = {RX_MSG_OBJ_ID1, 0, 2, 8, ucRXMsgData1}; // CAN接收結構體 MSG_OBJ_RX_INT_ENABLE = 2
tCANMsgObject sTXCANMessage2 = {TX_MSG_OBJ_ID2, 0, 1, 8, ucTXMsgData2}; // CAN傳送結構體 MSG_OBJ_TX_INT_ENABLE = 1
tCANMsgObject sTXCANMessage3 = {TX_MSG_OBJ_ID3, 0, 1, 8, ucTXMsgData3}; // CAN傳送結構體 MSG_OBJ_TX_INT_ENABLE = 1
tCANMsgObject sTXCANMessage4 = {TX_MSG_OBJ_ID4, 0, 1, 8, ucTXMsgData4}; // CAN傳送結構體 MSG_OBJ_TX_INT_ENABLE = 1
tCANMsgObject sTXCANMessage5 = {TX_MSG_OBJ_ID5, 0, 1, 8, ucTXMsgData5}; // CAN傳送結構體 MSG_OBJ_TX_INT_ENABLE = 1
tCANMsgObject sTXCANMessage6 = {TX_MSG_OBJ_ID6, 0, 1, 8, ucTXMsgData6}; // CAN傳送結構體 MSG_OBJ_TX_INT_ENABLE = 1
③根據自己的通訊機制,封裝好CAN通訊收發函式;
void CANA_TX(void)
{
unsigned int T_switch = 0; // 選擇資料傳送
CANA_TX_FRAME_CNT = CANA_TX_FRAME_CNT + 1; // 幀計數自增
// CANA_TX_FRAME_CNT = CANA_TX_FRAME_CNT & 0xFF; // 幀計數達到255後清零,迴圈計數
T_switch = CANA_TX_FRAME_CNT % 5;
switch(T_switch)
{
case 0:
ucTXMsgData2[0] = CAN_CNT_delta & 0xFF; // 幀計數
ucTXMsgData2[1] = 0; // ...此處略去
ucTXMsgData2[2] = 0; // ...此處略去
ucTXMsgData2[3] = 0; // ...此處略去
ucTXMsgData2[4] = 0; // ...此處略去
ucTXMsgData2[5] = 0; // ...此處略去
ucTXMsgData2[6] = 0; // ...此處略去
ucTXMsgData2[7] = 0; // ...此處略去
CanaMessageSet(TX_MSG_OBJ_ID2, &sTXCANMessage2, MSG_OBJ_TYPE_TX);
break;
case 1:
ucTXMsgData3[0] = CAN_CNT_delta & 0xFF; // 幀計數
ucTXMsgData3[1] = 0; // ...此處略去
ucTXMsgData3[2] = 0; // ...此處略去
ucTXMsgData3[3] = 0; // ...此處略去
ucTXMsgData3[4] = 0; // ...此處略去
ucTXMsgData3[5] = 0; // ...此處略去
ucTXMsgData3[6] = // ...此處略去
ucTXMsgData3[7] = // ...此處略去
CanaMessageSet(TX_MSG_OBJ_ID3, &sTXCANMessage3, MSG_OBJ_TYPE_TX);
break;
case 2:
ucTXMsgData4[0] = CAN_CNT_delta & 0xFF; // 幀計數
ucTXMsgData4[1] = // ...此處略去
ucTXMsgData4[2] = // ...此處略去
ucTXMsgData4[3] = // ...此處略去
ucTXMsgData4[4] = // ...此處略去
ucTXMsgData4[5] = // ...此處略去
ucTXMsgData4[6] = // ...此處略去
ucTXMsgData4[7] = // ...此處略去
CanaMessageSet(TX_MSG_OBJ_ID4, &sTXCANMessage4, MSG_OBJ_TYPE_TX);
break;
case 3:
ucTXMsgData5[0] = CAN_CNT_delta & 0xFF; // 幀計數
ucTXMsgData5[1] = // ...此處略去
ucTXMsgData5[2] = // ...此處略去
ucTXMsgData5[3] = // ...此處略去
ucTXMsgData5[4] = // ...此處略去
ucTXMsgData5[5] = // ...此處略去
ucTXMsgData5[6] = // ...此處略去
ucTXMsgData5[7] = // ...此處略去
CanaMessageSet(TX_MSG_OBJ_ID5, &sTXCANMessage5, MSG_OBJ_TYPE_TX);
break;
case 4:
ucTXMsgData6[0] = CAN_CNT_delta & 0xFF; // 幀計數
ucTXMsgData6[1] = // ...此處略去
ucTXMsgData6[2] = // ...此處略去
ucTXMsgData6[3] = // ...此處略去
ucTXMsgData6[4] = // ...此處略去
ucTXMsgData6[5] = // ...此處略去
ucTXMsgData6[6] = // ...此處略去
ucTXMsgData6[7] = // ...此處略去
CanaMessageSet(TX_MSG_OBJ_ID6, &sTXCANMessage6, MSG_OBJ_TYPE_TX);
CANA_TX_Active_Flag = 0;
break;
default:
break;
}
if(CANA_TX_FRAME_CNT >= 254)
{
CANA_TX_FRAME_CNT = 0;
}
}
void CANA_RX(void)
{
if((CANA_errorFlag == 0) && (CANA_RX_Flag == 1))
{
RX_FRAME_CANA.CNT = (Uint16)ucRXMsgData1[0]; // 位元組1
RX_FRAME_CANA.x= (Uint16)(((ucRXMsgData1[1] & 0xF0) >> 4) & 0x0F); // 位元組2H
RX_FRAME_CANA.xx = (Uint16)ucRXMsgData1[1] & 0x0F; // 位元組2L
RX_FRAME_CANA.xxx= (Uint16)(((ucRXMsgData1[2] & 0xF0) >> 4) & 0x0F); // 位元組3H
RX_FRAME_CANA.xxxx= (Uint16)ucRXMsgData1[2] & 0x0F; // 位元組3L
RX_FRAME_CANA.xxxxx= ((Uint16)(ucRXMsgData1[3] & 0xFF)) * 0.2; // 位元組4
RX_FRAME_CANA.xxxxxx= ((Uint16)(ucRXMsgData1[4] & 0xFF)) * 196.08; // 位元組5
RX_FRAME_CANA.xxxxxxx= ((int)(((ucRXMsgData1[5] & 0xFF) << 8 ) | (ucRXMsgData1[6] & 0xFF))) * 0.02; // 位元組6 位元組7
}
}
④使用中斷服務函式,傳輸接收傳送結構體;
interrupt void CANA0_ISR(void)
{
/************************************************************
Description:CANA0中斷服務程式
用於檢測中斷產生原因,傳送接收上位機資料
************************************************************/
uint32_t status;
//
// Read the CAN-A interrupt status to find the cause of the interrupt
//
status = CANIntStatus(CANA_BASE, CAN_INT_STS_CAUSE);
//
// If the cause is a controller status interrupt, then get the status
//
if(status == CAN_INT_INT0ID_STATUS)
{
//
// Read the controller status. This will return a field of status
// error bits that can indicate various errors. Error processing
// is not done in this example for simplicity. Refer to the
// API documentation for details about the error status bits.
// The act of reading this status will clear the interrupt.
//
status = CANStatusGet(CANA_BASE, CAN_STS_CONTROL);
//
// Check to see if an error occurred.
//
if(((status & ~(CAN_ES_TXOK | CAN_ES_RXOK)) != 7) &&
((status & ~(CAN_ES_TXOK | CAN_ES_RXOK)) != 0))
{
//
// Set a flag to indicate some errors may have occurred.
//
CANA_errorFlag = 1;
}
}
//
// Check if the cause is the CAN-A receive message object 1
//
else if(status == RX_MSG_OBJ_ID1)
{
//
// Get the received message
//
CANMessageGet(CANA_BASE, RX_MSG_OBJ_ID1, &sRXCANMessage1, true);
//
// Getting to this point means that the RX interrupt occurred on
// message object 1, and the message RX is complete. Clear the
// message object interrupt.
//
CANIntClear(CANA_BASE, RX_MSG_OBJ_ID1);
//
// Since the message was received, clear any error flags.
//
CANA_errorFlag = 0;
CANA_RX_Flag = 1;
CANA_TX_Active_Flag = 1;
Timer_CANA_TX_1ms = 0;
}
//
// Check if the cause is the CAN-A send message object 1
//
else if(status == TX_MSG_OBJ_ID2)
{
//
// Getting to this point means that the TX interrupt occurred on
// message object 1, and the message TX is complete. Clear the
// message object interrupt.
//
CANIntClear(CANA_BASE, TX_MSG_OBJ_ID2);
//
// Since the message was sent, clear any error flags.
//
CANA_errorFlag = 0;
}
//
// Check if the cause is the CAN-A send message object 1
//
else if(status == TX_MSG_OBJ_ID3)
{
//
// Getting to this point means that the TX interrupt occurred on
// message object 1, and the message TX is complete. Clear the
// message object interrupt.
//
CANIntClear(CANA_BASE, TX_MSG_OBJ_ID3);
//
// Since the message was sent, clear any error flags.
//
CANA_errorFlag = 0;
}
//
// Check if the cause is the CAN-A send message object 1
//
else if(status == TX_MSG_OBJ_ID4)
{
//
// Getting to this point means that the TX interrupt occurred on
// message object 1, and the message TX is complete. Clear the
// message object interrupt.
//
CANIntClear(CANA_BASE, TX_MSG_OBJ_ID4);
//
// Since the message was sent, clear any error flags.
//
CANA_errorFlag = 0;
}
//
// Check if the cause is the CAN-A send message object 1
//
else if(status == TX_MSG_OBJ_ID5)
{
//
// Getting to this point means that the TX interrupt occurred on
// message object 1, and the message TX is complete. Clear the
// message object interrupt.
//
CANIntClear(CANA_BASE, TX_MSG_OBJ_ID5);
//
// Since the message was sent, clear any error flags.
//
CANA_errorFlag = 0;
}
//
// Check if the cause is the CAN-A send message object 1
//
else if(status == TX_MSG_OBJ_ID6)
{
//
// Getting to this point means that the TX interrupt occurred on
// message object 1, and the message TX is complete. Clear the
// message object interrupt.
//
CANIntClear(CANA_BASE, TX_MSG_OBJ_ID6);
//
// Since the message was sent, clear any error flags.
//
CANA_errorFlag = 0;
}
//
// If something unexpected caused the interrupt, this would handle it.
//
else
{
//
// Spurious interrupt handling can go here.
//
}
//
// Clear the global interrupt flag for the CAN interrupt line
//
CANGlobalIntClear(CANA_BASE, CAN_GLB_INT_CANINT0);
//
// Acknowledge this interrupt located in group 9
//
PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;
}
實驗驗證
採用上位機(PC)發1幀,下位機(DSP)應答5幀的方式,實現遙控與遙測,要想實現更為精準的定時,則可以採取其他的方式,比如TT-CAN等。上位機傳送間隔6ms,下位機應答間隔1ms。上位機資料幀ID(00000000001),下位機資料幀ID(00000000010、00000000011、00000000100、00000000101、00000000110)。使用ZLG的示波器,因為他自帶CAN通訊解碼功能,使用起來非常方便。
上位機遙控幀:
下位機遙測幀①
下位機遙測幀⑤
結束語
筆者對於CAN的使用,僅停留在資料幀這一簡單的幀種類上,以後若是有專案需要,則再補充學習其他的幀種類。TI對於CAN的支援比較到位,我們可以直接呼叫相應的函式,即可實現功能。當然規則越明細,開發人員對其標準化程度會越高,但使用靈活度、自由度變差。
參考資料目錄
《TMS320F2837xS Delfino Microcontrollers Datasheet》Memory章節
《TMS320F2837xS Delfino Microcontrollers Technical Reference Manual》CAN章節
RENESAS應用手冊《CAN入門書》
C2000Ware有關CAN的所有例程
相關文章
- 程序間的通訊(訊號通訊)
- 【React】元件通訊 - 跨層通訊React元件
- 程序通訊--訊號
- 程序通訊-訊號
- 網路通訊3:TCP互動通訊TCP
- 網路通訊2:TCP簡單通訊TCP
- Binder通訊機制與IPC通訊.md
- 網路通訊2:TCP通訊實現TCP
- 紅色通訊史(三):延安通訊故事
- socket通訊
- TCP通訊TCP
- 安全通訊
- react通訊React
- 程式通訊
- WebSocket通訊Web
- 序列通訊
- Vue通訊Vue
- netty通訊Netty
- 序列通訊與並行通訊的區別並行
- 簡單區分WiFi通訊和WLAN通訊WiFi
- react元件通訊通識篇React元件
- 漫畫通訊:一圖看懂通訊發展史
- ROS話題通訊和服務通訊的區別ROS
- RabbitMQ 入門(一)同步通訊和非同步通訊MQ非同步
- WebRTC音訊通話升級為視訊通話Web音訊
- Android Socket 通訊Android
- vue 元件通訊Vue元件
- React元件通訊React元件
- 串列埠通訊串列埠
- Qt ModbusTCP通訊QTTCP
- Vue — 元件通訊Vue元件
- 程序通訊方式
- 通訊錄AdressDAO
- 即時通訊
- websocket通訊原理Web
- vue元件通訊Vue元件
- 網路通訊
- (16)USB通訊