生活随笔
收集整理的這篇文章主要介紹了
LIN帧头发送
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
**在MC9S12XS128上實現LIN通訊**
為何樹可以長那么高,為什么狗可以單身這么久
之前一直接觸的是CAN通訊、串口之類的,突然有一個LIN通訊的CASE讓我有點措手不及,在習慣性的在CSDN上尋找答案時發現這一塊好像并不是很多,所以在結束了這個CASE之后想著寫點關于做LIN通訊時的所見所感。
1.關于LIN總線
LIN的英文全稱是Local Interconnect Network,它是基于UART/SCI的低成本串行通訊協議,主要用與車上傳感器和執行器之間,例如車窗、后視鏡、大燈、門鎖等,我的那個CASE是用在換擋器手球和手柄之間的LIN通訊。
LIN的傳輸不同于CAN,用的是單線傳輸,單主機多從機(最多16個從機),總線電平12V,最大速率20Kbps。
總線任務:調度線上幀傳輸次序;監測數據處理錯誤;接收喚醒總線命令(這個我沒研究)。
從機任務:發送應答;接收應答;既不接受也不應答(注:從機不能直接向總線發送數據,在收到主節點的幀頭后通過ID判斷具體操作)。
LIN的特點:最大的特點應該就是比CAN便宜吧
2.方向
這次案例是換擋器手柄的LIN通訊檢測,我用的是MC9S12XS128,接下來寫的主要是思路和代碼,硬件部分暫不敘述。
由于我要檢測的是一個LIN從機部分的應答,所以我需要做一個LIN的主機,這個主機的功能就是讓我要檢測的從機發送相應的應答。
從機發送應答的前提是主機提供了幀頭以及幀頭帶有相應的ID,那么接下來我的目標就明確了:做一個循環發送從機對應的幀頭就OK了。理論上這樣從機就可以發送出我需要檢測的應答部分,當然這邊還需要一個接收應答的功能,綜上可以分為兩步:
1.循環發送幀頭
2.接收從機的應答
3.具現
LIN的報文分為兩部分:幀頭和應答。幀頭部分由同步間隔段、同步段、ID組成;應答部分由數據段、校驗和組成。
我需要做的主節點發送幀頭那發送的內容如下圖
我一開始不理解后面的那段平的怎么發或者說怎么用代碼表達,其實不用表達這部分,就只發前面的幀頭部分就可以了。
所有的代碼如下
#include
<hidef
.h
>
#include
"derivative.h"
#define
LEDCPU PORTK_PK4
#define LEDCPU_dir
DDRK_DDRK4
#define
BUS_CLOCK 32000000
#define
OSC_CLOCK 16000000
#define
BAUD 19200
#define
BIT(A,B) ((A>>B)&0x01)
#define
ID 0x20
#define
EN PTM_PTM6
#define EN_dir
DDRM_DDRM6
Bool a
;
struct message
{unsigned char identifier
;unsigned char data
[8];
};struct message msg_send
;
struct message msg_get
;
enum lin_state
{ IDLE, _BREAK
, SYNCH, PROTECTED_IDENTIFIER, DATA0, DATA1, DATA2, DATA3, DATA4, DATA5, DATA6, DATA7, CHECKSUM };
struct frame
{unsigned char protected_id
;unsigned char data
[8];unsigned char check
;enum lin_state state
;unsigned char error
;
};struct frame frame_receive
;
void INIT_PLL(void)
{CLKSEL &= 0x7f; PLLCTL &= 0x8F; CRGINT &= 0xDF;#
if(BUS_CLOCK == 40000000) SYNR = 0x44;#elif(BUS_CLOCK == 32000000)SYNR = 0x43; #elif(BUS_CLOCK == 24000000)SYNR = 0x42;#endif
REFDV = 0x81; PLLCTL =PLLCTL|0x70; asm
NOP;asm
NOP;while(!(CRGFLG&0x08)); CLKSEL |= 0x80;
}
void initialize_TIM(void)
{TSCR1_TFFCA = 1; TSCR1_TEN = 1; TIOS = 0xff; TCTL1 = 0x00; TCTL2 = 0x00; TIE = 0x00; TSCR2 = 0x07; TFLG1 = 0xff; TFLG2 = 0xff;
}
void INIT_LIN(void)
{unsigned char i
;SCI0BD = BUS_CLOCK/16/BAUD; SCI0CR1 = 0x00; SCI0CR2 = 0x2c; SCI0SR2_BRK13 = 1; frame_receive
.protected_id
=0;frame_receive
.state
=IDLE;frame_receive
.error
=0;frame_receive
.check
=0;for (i
=0;i
<8;i
++)frame_receive
.data
[i
]=0;EN_dir
=1;EN=1;
}
unsigned char
LINCalcParity(unsigned char id)
{unsigned char parity
, p0
,p1
;parity
=id
; p0
=(BIT(parity
,0)^BIT(parity
,1)^BIT(parity
,2)^BIT(parity
,4))<<6; p1
=(!(BIT(parity
,1)^BIT(parity
,3)^BIT(parity
,4)^BIT(parity
,5)))<<7; parity
|=(p0
|p1
);return parity
;
}
unsigned char
LINCalcChecksum(unsigned char *data)
{unsigned int sum
= 0;unsigned char i
;for(i
= 0; i
< 8; i
++){sum
+= data
[i
];if(sum
&0xFF00)sum
= (sum
&0x00FF) + 1;}sum
^= 0x00FF; return (unsigned char
)sum
;
}
Bool
LINCheckSend(enum lin_state status, unsigned char val)
{while(frame_receive
.state
< status
)if(frame_receive
.error
)return(FALSE); switch(status
) {case _BREAK
:case SYNCH:break; case PROTECTED_IDENTIFIER:if(frame_receive
.protected_id
!= val
)return(FALSE);break; case DATA0:case DATA1:case DATA2:case DATA3:case DATA4:case DATA5:case DATA6:case DATA7:if(frame_receive
.data
[status
-DATA0] != val
)return(FALSE);break;case CHECKSUM:if(frame_receive
.check
!= val
)return(FALSE);break;}return(TRUE);
}
Bool
LINSendChar(unsigned char ch)
{while(!SCI0SR1_TDRE); SCI0DRL = ch
;return(TRUE);
}
Bool
LINSendbreak(void)
{while(!SCI0SR1_TDRE); SCI0CR2_SBK = 1; SCI0CR2_SBK = 0; return(TRUE);
}
Bool
LINZhuSendMsg(void)
{unsigned char parity_id
;frame_receive
.error
= 0;if(!LINSendbreak())return(FALSE);if(!LINCheckSend(_BREAK
,0x00))return(FALSE); if(!LINSendChar(0x55))return(FALSE);if(!LINCheckSend(SYNCH,0x55))return(FALSE); parity_id
=LINCalcParity(msg_send
.identifier
); if(!LINSendChar(parity_id
)) return(FALSE);if(!LINCheckSend(PROTECTED_IDENTIFIER, parity_id
))return(FALSE);return(TRUE);
}
Bool
LINGetChar(void)
{unsigned volatile char ch
;switch(frame_receive
.state
){case IDLE: if(!(SCI0SR1&0x22))return(FALSE); if(SCI0DRL)return(FALSE);break;case _BREAK
: if(!(SCI0SR1&0x20))return(FALSE); if(SCI0DRL != 0x55)return(FALSE); break; case SYNCH: if(!(SCI0SR1&0x20))return(FALSE); ch
= SCI0DRL;frame_receive
.protected_id
= ch
;break; case PROTECTED_IDENTIFIER: case DATA0: case DATA1: case DATA2: case DATA3: case DATA4: case DATA5: case DATA6: if(!(SCI0SR1&0x20))return(FALSE); ch
= SCI0DRL;frame_receive
.data
[frame_receive
.state
-PROTECTED_IDENTIFIER] = ch
;break;case DATA7: if(!(SCI0SR1&0x20))return(FALSE); ch
= SCI0DRL;frame_receive
.check
= ch
;break;case CHECKSUM: return(FALSE); }frame_receive
.state
+=1;return(TRUE);
}
Bool
LINSlave_Msg_Handle(void)
{if(frame_receive
.state
== CHECKSUM) {if((frame_receive
.protected_id
== LINCalcParity(ID))&&(frame_receive
.check
== LINCalcChecksum(frame_receive
.data
))) {return TRUE;}frame_receive
.state
= IDLE;return TRUE;}return FALSE;
}
void delay1ms(unsigned int n)
{unsigned int i
;for(i
=0;i
<n
;i
++) {TFLG1_C0F = 1; TC0 = TCNT + 250; while(TFLG1_C0F == 0); }
}
#pragma
CODE_SEG __NEAR_SEG
NON_BANKED
interrupt
void LINreceive(void)
{ if(!LINGetChar()) {frame_receive
.error
= 1;frame_receive
.state
= IDLE;}
}
#pragma
CODE_SEG DEFAULT
void main(void)
{DisableInterrupts
;INIT_PLL();initialize_TIM();INIT_LIN();LEDCPU_dir
=1;LEDCPU=0;EnableInterrupts
;msg_send
.identifier
= ID; for(;;) {a
= LINZhuSendMsg();delay1ms(10);if(a
) {delay1ms(10);if(frame_receive
.state
== CHECKSUM) {if((frame_receive
.protected_id
== LINCalcParity(ID))&&(frame_receive
.check
== LINCalcChecksum(frame_receive
.data
))) {LEDCPU=~LEDCPU;}frame_receive
.state
= IDLE;}}delay1ms(500);}
}
簡單講解一下代碼部分:
主函數里面的邏輯是發送幀頭,檢測是否發送成功,發送成功后接收報文。接收部分用的是中斷接收。
enum lin_state
{ IDLE, _BREAK
, SYNCH, PROTECTED_IDENTIFIER, DATA0, DATA1, DATA2, DATA3, DATA4, DATA5, DATA6, DATA7, CHECKSUM };
這里面定義的就是上面圖中描述的LIN完整報文的組成部分。
完整的代碼里主節點發送的只是幀頭;下面是主節點發送完整的報文的代碼,相比之下多了數據和校驗部分。
Bool
LINSendMsg(void)
{unsigned char check_sum
, parity_id
, i
;frame_send
.error
= 0;if(!LINSendbreak())return(FALSE);if(!LINCheckSend(_BREAK
,0x00))return(FALSE); if(!LINSendChar(0x55))return(FALSE);if(!LINCheckSend(SYNCH,0x55))return(FALSE); parity_id
=LINCalcParity(msg_send
.identifier
);if(!LINSendChar(parity_id
))return(FALSE);if(!LINCheckSend(PROTECTED_IDENTIFIER, parity_id
))return(FALSE); for(i
=0; i
< 8; i
++) {if(!LINSendChar(msg_send
.data
[i
]))return(FALSE);if(!LINCheckSend(DATA0+i
, msg_send
.data
[i
]))return(FALSE); }check_sum
= LINCalcChecksum(msg_send
.data
);if(!LINSendChar(check_sum
))return(FALSE);if(!LINCheckSend(CHECKSUM, check_sum
))return(FALSE); frame_send
.state
= IDLE;return(TRUE);
}
最后在廢話一句:我用的是SCI0不是SCI1。
總結
以上是生活随笔為你收集整理的LIN帧头发送的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。