MSP430 LIN总线编程
Lis, 2021-2-3
时隔接近20年,CSDN我回来了...以下为本人原创. 如转载,请注明出处!
LIN总线在汽车领域应用十分的广泛。汽车上的油泵,自动折叠反光镜,大灯,锁等等慢速通信都会有LIN总线的应用身影。但网络上你几乎找不到MSP430在这方面的应用,就算去TI的官方网站上去找,去问客服,也找不到,MSP430似乎没有车规级型号,新能源汽车上几乎清一色的使用TI的DSP车规级芯片,所以也基本没有人在MSP430上使用LIN总线。但是掌握LIN总线的应用,会给我们在一些项目里带来很大的帮助。至于什么是LIN,什么CAN这些的术语,大家自己百度应该可以找到很详细介绍,这里不做过多的说明。
以下是我做的一些笔记,为了避免重复劳动,浪费时间,就直接截图上来了。
[1] LIN总线特点
[二]物理层连接
[三]接口电路
这里我选择了最小封装的TJA1027,8脚芯片,以下为LIN master模式的配置电路。 详细关于TJA1027请自行网上找它的datasheet
[四]LIN-BUS的帧格式
Break长度一般为为13位,我们称之为:间隔场.(参考网上的一张比较不错的图)
示波下的真实waveform:
逻辑分析仪下的 读某品牌油泵的时序:
[五]MSP430F24X下的LIN BUS配置
这里我使用MSP430F249为例,UART模块,需支持SCI功能。才可以支持LIN,所以不是所有的MSP430系列都支持LIN总线,查看Datasheet是最有效的方式。
- 在Slave 模式下,UART需支持自动波特率检测(在一些特定标准下,波特率是特定的)
- UART=8 bit,LSB优先,无奇偶位,1停止位 。 这点和普通的串口单元设置基本一样
- 接口驱动芯片,这里我选用的是TJA1027,实现通信电平上面的转换
- 主模式: 自动生成间隔场(break)和同步码(synch)
- 选择自动波特率检测模式 UCTXBRK=1
- DELIMx指定 break Delimiter的长度,默认为1位周期
- 检测TXBUF是否准备就绪,并向TXBUF写入同步码 0x55h
- 传输13位间隔场后,然后就是break delimiter 和synch 同步场,UCTXBRK在同步载入TX移位寄存器后自动重设,写入TXBUF的数据正常传输
[六]代码部分
1.PID部分:ID只是高6个位,后两个位为奇偶效验码.
//---------------------------------------------------------------------------//
// uchar LIN_GetPID(uchar id) //
//Description: calculate odd and even parity for ID Frame //
// Parity: //
// P0= id0^id1^id2^id4 ==>Even bit6 //
// p1=~(id1^id3^id4^id5) ==>Odd bit7 //
//---------------------------------------------------------------------------//
uchar LIN_GetPID(uchar id)
{
uchar parity=0, p0,p1;
p0 =id;
p0^=id>>1;
p0^=id>>2;
p0^=id>>4;
p0&=0x01;
p1 =id>>1;
p1^=id>>3;
p1^=id>>4;
p1^=id>>5;
p1=~p1;
p1&=0x01;
parity=id|(p0<<6)|(p1<<7);
return parity;
}
2.checksum
LIN总线中的checksum算法为带进位的累加。 在LIN总线里,有两种校验方式,一种称为标准验校,和增强型校验。 代码中我做了相关的说明。
//---------------------------------------------------------------------------//
// calculate checksum //
//paremeters: uchar id: slaver id //
// uchar *data array of data,maximum:8 bytes //
// uchar len length of the data array //
//---------------------------------------------------------------------------//
uchar LIN_Checksum(uchar id,uchar *data,uchar len)
{
uint16 sum = 0;
uchar i;
//if it's not a diagnostic package | id==0x3c using normal checksum,otherwise using enhance checksum
if (id!=0x3c)
{
sum=LIN_GetPID(id);
}
for(i = 0; i < len; i++)
{
sum +=data[i];
if(sum&0xff00)
{
sum&=0x00ff;
sum+=1;
}
}
sum=~sum;
return (uchar)sum;
}
3.添加发送间隔场和同步场
我在代码中加了宏代码,以方便我配置LIN端口使用UART0 或UART1
//----------------------------------------------------------------------------//
// void USART_UCA0_Send_Byte(uchar data)
//----------------------------------------------------------------------------//
void USART_UCA0_Send_Byte(uchar data)
{
UCA0TXBUF = data;
while(!Get_Bit(UC0IFG,UCA0TXIFG));
}
//---------------------------------------------------------------------------//
// LIN_Send_Break_Sync(void) //
//---------------------------------------------------------------------------//
void LIN_Send_Break_Sync(void)
{
UCA0CTL0_bit.UCMODE0=1;
UCA0CTL0_bit.UCMODE1=1;
//4bit stop
UCA0ABCTL_bit.UCDELIM0=1;
UCA0ABCTL_bit.UCDELIM1=1;
UCA0CTL1_bit.UCTXBRK=1;
USART_UCA0_Send_Byte(0x55);
UCA0CTL1_bit.UCTXBRK=0;
}
//---------------------------------------------------------------------------//
// LIN_Sendbuf //
// //
//paremeters: uchar id: slaver id //
// uchar *data array of data,maximum:8 bytes //
// uchar len length of the data array //
//---------------------------------------------------------------------------//
void LIN_Sendbuf(uchar id,uchar *data,uchar len)
{
uchar check_sum, parity_id;
uint16 i;
LIN_Send_Break_Sync();
parity_id=LIN_GetPID(id);
USART_UCA0_Send_Byte(parity_id);
for(i=0;i<len;i++){
USART_UCA0_Send_Byte(data[i]);
}
check_sum=LIN_Checksum(id,data,len);
USART_UCA0_Send_Byte(check_sum);
}
5.发送读操作帧
//---------------------------------------------------------------------------//
// void LIN_RX_Ask(uchar id) //
// //
//definition: Send Syn frame to slaver, Slaver will send back the data //
//parameter: uchar id //
//---------------------------------------------------------------------------//
void LIN_RX_Ask(uchar id)
{
uchar parity_id;
LIN_RX_Idx=0;
LIN_Send_Break_Sync();
parity_id=LIN_GetPID(id);
USART_UCA0_Send_Byte(parity_id);
}
如这里的时序,master端发送Ask(break+sync+PID),然后slave同步并返回数据帧。 这个比较容易理解,类似于I2C,SPI,1 wire 总线。
//----------------------------------------------------------------------------//
// void USART_UCA0_Send_Byte(uchar data)
//----------------------------------------------------------------------------//
void USART_UCA0_Send_Byte(uchar data)
{
UCA0TXBUF = data;
while(!Get_Bit(UC0IFG,UCA0TXIFG));
}
//---------------------------------------------------------------------------//
// void LIN_Readbuf_Pro(uchar data) //
// put this function into RX intterruption //
//---------------------------------------------------------------------------//
void LIN_Readbuf_Pro(uchar data)
{
switch(LIN_RX_Idx)
{
case 0:
if(data==0x55)
{
LIN_RX_Idx=1;
}
break;
case 1:
LIN_RX_Frame.id=data&0x3F;
LIN_RX_Idx=2;
break;
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
LIN_RX_Frame.d[LIN_RX_Idx-2]= data;
LIN_RX_Idx++;
break;
case 10:
LIN_RX_Frame.chksum=data;
LIN_RX_Idx=0;
break;
}
}
串口接收中断:
//----------------------------------------------------------------------------//
// USCI0RX_ISR UCA0 Interruption
//----------------------------------------------------------------------------//
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
uchar data=0;
data = UCA0RXBUF;
// if (UC0IFG&UCA0RXIFG)
// {
LIN_Readbuf_Pro(data);
// }
}
7.串口设置
#define BAUD_UCA0 10417
//----------------------------------------------------------------------------//
uchar Get_UCBRS(float baud)
{
return ((uchar)((baud-(uint16)baud)*8+0.5))<<1;
}
//----------------------------------------------------------------------------//
// void Init_Serial_Ports(void)
//----------------------------------------------------------------------------//
void Init_Serial_Ports(void)
{
float tmp_baud;
SetPinMode(UCA0_TXD_PIN);
SetPinMode(UCA0_RXD_PIN);
tmp_baud=CPU_F/2/BAUD_UCA0;
UCA0CTL1 |= UCSSEL_2 + UCSWRST; //Set the clock of UCA0 =SMCL,UCSWRST Enable,initial all USCI registors
UCA0BR0 =(uchar)(tmp_baud); //Low 8 bits
UCA0BR1 =(uchar)((uint16)tmp_baud>>8); // //High 8bits, NO parity check
UCA0MCTL = Get_UCBRS(tmp_baud);
UCA0CTL1 &= ~UCSWRST; //clear UCSWRST bit
UC0IE |= UCA0RXIE; //Enable RX Interruptor
}
[7]测试板电路设计
[1]电源部分
先设计打算使用于24V电路,所以输入端,和LIN总线电压为24V,但是实际应用调试的油泵为12V,所以主供电也为12V。 这里做个说明.
[2]MSP430部分相对简单,最小系统,然后把端口都引出去
[3]LIN接口电路
[4]调试用TTL串行口电路
增加缓冲电路,能有效加强TTL串口的可靠,稳定性。
[5]电路指示
[6]预留的一个大功率IO驱动口,用于驱动外部电磁阀等器件
[7]Layout
根据schematic,布好PWB后,生成工艺文件以及BOM发给PCB工厂进行打样生产。
[8]实际调试,正常驱动某品牌汽车油泵