LIN(Local Interconnect Network)-UART 总线是一种可与外部设备进行同步/异步串行通信的通用接口,它除了支持正常模式下双向通信功能和多处理器模式下的主从通信功能外,还支持LIN 总线的特殊功能,它包含一个主节点,多个从节点,速度最高可达20kbps。
8.3.1 LIN-UART 的结构及功能
LIN-UART 的功能模块如下图8-3 所示,它主要由以下几个模块组成:
- 重载计数器:重载计数器是一个15 位的计数器,在LIN-UART 中作为专用波特率发生器,它由来自外部或内部的时钟产生发送或接受时钟,发送时重载计数器中的计数值由波特率发生器(BGR1和BGR0 中)读出。
- 接受控制电路:这个模块由接受位计数器,开始位检测电路和接受校验位计数器组成。接受位计数器对接受到的数据计数,并在接受完指定长度的数据后产生一个标志位,如果此时接受中断使能,则会产生一个接受中断。开始位检测电路用于检测输入进来的串行数据的开始位,当检测到开始位后,在开始位的下降沿发送一个信号给重载计数器,从而使接受校验位计数器计算接受到的数据的校验位。
- 接受移位寄存器:接受来自SIN 引脚的串行数据,在接受完数据并移位后将数据发送到接受数据寄存器(RDR)中。
- LIN-UART 接受数据寄存器(RDR):保存接受到的数据。
- 发送控制电路:这个模块由一个发送位计数器,发送开始电路和发送校验电路组成。
- 发送移位寄存器:写入到发送数据寄存器(TDR)中的数据送到发送移位寄存器,移位后输出到SOT 引脚。
- LIN-UART 发送数据寄存器(TDR):写入的数据转换为串行数据后输出。
- LIN synch break/Synch Field 检测电路:在LIN-MASTER 模式下,当发送一个信头时检测LIN synch break, 当检测到LIN synch break 后,使LBD 为“1”,并产生一个内部信号送入8/16 位复合定时器来检测Synch Field 的第一和第五个下降沿,从而确定出在MASTER 模式下的同步串行时钟。
- 串行控制寄存器(SCR): 设置校验位,选择停止位和数据的长度,模式1 下的帧数据格式,清除接受错误标志位,使能传输/接受
- 串行模式寄存器(SMR): 设置工作模式,指定波特率时钟,使能串行数据的输出以及时钟的输出
- 串行状态寄存器(SSR):检查传输/接受时的状态和错误,以及使能中断
- 扩展状态控制寄存器(ESCR):使能LIN 同步中止中断,选择同步中止数据长度(13~16bit) , 检测同步中止,直接访问SIN/SOT 引脚, 在同步模式下输出连续时钟,采样时钟边沿(上升还是下降沿)
- 扩展通信控制寄存器(ECCR): 检测总线空闲,使能串行时钟延时(模式2),产生同步中止
- 波特率发生器寄存器0,1(BGR0/1):产生不同的波特率
点击查看图片
8.3.2 LIN-UART 的工作模式
LIN-UART有四种不同的工作模式,工作模式的选择是由串行模式寄存器的MD1和MD0来确定的,模式0和模式2用于双向串行通信,模式1用于主从通信,模式3用于LIN主从通信,如下表8-12所示:


LIN-UART串行模式寄存器中的MD1和MD0用来选择工作模式,如下表8-13所示:

8.3.3 LIN-UART 的应用设计范例
LIN-UART有四种不同的工作模式,在应用上,模式0和模式2与UART相似,下面看一下LIN-Master模式,LIN-Slave模式以及Lin模式。
(1)LIN-Master 模式
在LIN 模式下,Master决定整个LIN总线的波特率,Slave与Master的时钟同步。当写“1”到扩展通信控制寄存器(ECCR)中的LBR位时,就从SOT引脚输出连续的13~16bit的低电平,这些低电平就是LIN Synch Break,它表明了这是一个信头的开始。LIN Synch Break的长度由扩展状态控制寄存器(ESCR)中的LBL0和LBL1来确定,如下表8-14所示:

当发送完13~16bit的LIN Synch Break后,就要发送LIN Synch Field,它是以数据0x55发送的。为防止产生传输中断,在ECCR中的LBR位置为“1”后,即使TDRE标志位为“0”,也可以将0x55写入到发送数据寄存器(TDR)中。
下面的这个例子为LIN-Master的基本功能,它发送8个字节的数据给LIN-Slave,然后再发送一个信号,Slave必须响应Master发给它的8个字节的数据数据,本实例采用的是CONCERTO-kit,7段LED显示发送的字节数,如果在发送的过程中产生错误,则显示错误号码。在使用LIN-master和LIN-Slave时,需要用到外部的LIN总线驱动芯片TLE6259G,CONCERTO-kit上的跳线设置为:JP1关闭(LIN master),JP2关闭(LIN enable),JP4 :1-2 (LIN TxD),JP5 :1-2 (LIN RxD),Master和Slave的X5分别对应相连,其中引脚1接12V电源,引脚2接地,引脚3为数据线。其源程序如下所示:
#include "mb95100.h"
#define SEG_A 0xFE
#define SEG_B 0xFD
#define SEG_C 0xFB
#define SEG_D 0xF7
#define SEG_E 0xEF
#define SEG_F 0xDF
#define SEG_G 0xBF
#define SEG_DP 0x7F
#define SEG_0 SEG_A & SEG_B & SEG_C & SEG_D & SEG_E & SEG_F
#define SEG_1 SEG_B & SEG_C
#define SEG_2 SEG_A & SEG_B & SEG_D & SEG_E & SEG_G
#define SEG_3 SEG_A & SEG_B & SEG_C & SEG_D & SEG_G
#define SEG_4 SEG_B & SEG_C & SEG_F & SEG_G
#define SEG_5 SEG_A & SEG_C & SEG_D & SEG_F & SEG_G
#define SEG_6 SEG_A & SEG_C & SEG_D & SEG_E & SEG_F & SEG_G
#define SEG_7 SEG_A & SEG_B & SEG_C
#define SEG_8 SEG_A & SEG_B & SEG_C & SEG_D & SEG_E & SEG_F & SEG_G
#define SEG_9 SEG_A & SEG_B & SEG_C & SEG_D & SEG_F & SEG_G
const unsigned char seg_display[12] = { SEG_0, SEG_1, SEG_2, SEG_3, SEG_4, SEG_5, SEG_6, SEG_7, SEG_8, SEG_9, 0xff, 0x00 };
// LIN-State
//
// 0 : Waiting (Ready to send synch break)
// 1 : set synch break
// 2 : synch break read back, set synch field
// 3 : send header
// 4 : LIN_Data Tx/Rx
// 5 : Checksum Tx/Rx
#define DATALENGTH 8
#define SLAVESEND 0x7D
#define MASTERSEND 0xFE
volatile unsigned char LIN_State, Rx_Error, Rx_Data, Master_Send;
volatile unsigned char LIN_Header, LIN_Count, LIN_Checksum;
volatile unsigned char LIN_Data[DATALENGTH];
volatile unsigned char counter;
//------------------------- SUB ROUTINES ---------------------------------
void InitUart3(void)
{
// Initialize UART asynchronous LIN mode
BGR = 1041; // 重载值 = 1041,波特率=10M/1041=9600Baud
SMR = 0xC5; /*11000101,11-模式3,0-波特率发生器,0-重载计数器,0-无需重启重载计数器,1-LIN-UART复位,0-SCKE作为通用IO口或时钟输入引脚,1-使能SOT引脚*/
SSR = 0x02; // 允许接受中断
SCR = 0x01; // 允许发送
ESCR = 0x30; // 设置LIN Synch Break长度为16bit
}
void wait(unsigned long j)
{
while(j--) asm("\tNOP");
}
void Start_LIN_Message(void)
{
ESCR_LBD = 0; // 清除LIN Synch Break检测
ESCR_LBIE = 1; // 使能LIN Break detection
ECCR = 0x40; // 01000000,产生 LIN-Break
LIN_State = 1;
}
/*-----------------------------MAIN PROGRAM -----------------------------*/
void main(void)
{
InitUart3();
LIN_Data[0] = 0x00; // LIN Master发送的数据
LIN_Data[1] = 0xAA;
LIN_Data[2] = 0xED;
LIN_Data[3] = 0x77;
LIN_Data[4] = 0xDD;
LIN_Data[5] = 0x11;
LIN_Data[6] = 0x88;
LIN_Data[7] = 0x00;
PDR0 = 0xFF; // Port 0:
DDR0 = 0x7F; // 七段数码管显示(关闭)
LIN_State = 0; //设置为等待状态
Rx_Error = 0;
counter = 0;
PDR0 = seg_display[counter];
InitIrqLevels(); // 初始化中断电平寄存器和IRQ 向量表
__EI(); // 启用全局中断
__set_il(3); // 将全局中断设置为最低级别,允许其它中断
while (!Rx_Error)
{
wait(10000);
LIN_Header = MASTERSEND; // Master 发送数据到 slave
Master_Send = 1;
Start_LIN_Message();
if (!Rx_Error)
{
wait(10000); LIN_Header = SLAVESEND; // Master 从 slave读回数据
Master_Send = 0;
Start_LIN_Message();
}
if (++counter == 10) counter = 0;
PDR0 = seg_display[counter];
}
//如果发生错误,在七段数码管上显示错误号码
while(1)
{
PDR0 = seg_display[Rx_Error];
wait(30000);
PDR0 = seg_display[10]; wait(10000);
}
}
/*--------------------------中断服务程序-------------------------------*/
__interrupt void IRQ_LIN_RX (void)
{
if (ESCR_LBD) // 检测LIN Break
{
ESCR_LBD = 0; // 检测到LIN Break后,立即将LBD清零
if (LIN_State == 1)
{
SCR_RXE = 1; // 使能接受
TDR = 0x55; // 发送LIN Synch Field 01010101
LIN_State = 2;
LIN_Checksum = 0;
}
else
{
Rx_Error = 1; // LIN Synch Break 接受错误
}
}
else if (SSR_RDRF) // 接受寄存器满
{
Rx_Data = RDR; // 读出接受到的数据
if (SSR_ORE || SSR_FRE) // 如果有溢出或帧错误
{
Rx_Error = 2; //溢出或帧错误
}
else if (LIN_State == 2) // 读Synch field
{
if (Rx_Data != 0x55)
{
Rx_Error = 3; // LIN Synch Field错误
}
else
{
TDR = LIN_Header; // 发送 LIN_Header
LIN_State = 3;
}
}
else if (LIN_State == 3)
{ if (Rx_Data != LIN_Header)
{
Rx_Error = 4; //LIN Header错误
}
else
{
if (Master_Send) // Master发送数据?
{
TDR = LIN_Data[LIN_Count]; // 发送数据 LIN_Checksum = LIN_Data[LIN_Count];
}
LIN_State = 4;
}
}
else if (LIN_State == 4)
{ if (Master_Send) // Master 发送数据?
{
if (Rx_Data != LIN_Data[LIN_Count])
{
Rx_Error = 5; //接受错误
}
LIN_Count++;
if (LIN_Count == DATALENGTH) // 数据发送完?
{ LIN_Count = 0; LIN_State = 5;
LIN_Checksum = LIN_Checksum ^ 0xFF;
TDR = LIN_Checksum;
}
else
{
TDR = LIN_Data[LIN_Count]; // 发送下一个数据
LIN_Checksum = LIN_Checksum + LIN_Data[LIN_Count];
}
}
else // 接受来自Slave的数据
{
LIN_Data[LIN_Count] = Rx_Data;
LIN_Checksum = LIN_Checksum + Rx_Data;
LIN_Count++;
if (LIN_Count == DATALENGTH) // 数据发送完?
{ LIN_Count = 0; LIN_State = 5;
LIN_Checksum = LIN_Checksum ^ 0xFF;
}
}
}
else if (LIN_State == 5)
{ if (Rx_Data != LIN_Checksum)
{
Rx_Error = 6;
}
SCR_RXE = 0; LIN_State = 0;
}
}
else
{
Rx_Error = 7;
SSR_RIE = 0; //不能产生接受中断
}
}
(2)LIN-Slave模式
在LIN-Slave模式,LIN-UART必须和Master的波特率同步,当使能LIN Synch Break中断时,LIN-UART产生一个接受中断,即使接受使能关闭(RXE=0)。同时,扩展状态控制寄存器(ESCR)中的LBD位置“1” ,当产生接受中断后,立即将LBD清零。
当LIN-UART检测到Synch Field的第一个下降沿时,将一个高电平的内部信号送入到8/16 bit复合定时器中,在第五个下降沿时,此信号变为低电平。8/16 bit复合定时器设置为输入捕捉模式,使能中断并设置为可以检测两个边沿(上升沿和下降沿)。因此,输入到8/16 bit复合定时器中的时间为波特率的八倍,波特率的设定置可由如下的表达式计算:
- 8/16 bit复合定时器的计数器没有溢出,BGR=(b-a)/8 -1;
- 8/16 bit复合定时器的计数器发生溢出,BGR=(max+b-a)/8 -1;
其中,a:TII0为第一个中断后数据寄存器的值 b:TII0为第二个中断后数据寄存器的值 max:自由运行定时器的最大值 下面的实例中,LIN-Slave接受LIN-Master发送过来的8 byte的数据,并将接受到的数据存放在数组 LIN-Data[ ]中。源程序如下所示:
#include "mb95100.h"
#define SEG_A 0x01
#define SEG_B 0x02
#define SEG_C 0x04
#define SEG_D 0x08
#define SEG_E 0x10
#define SEG_F 0x20
#define SEG_G 0x40
#define SEG_DP 0x80
#define SEG_0 SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F
#define SEG_1 SEG_B | SEG_C
#define SEG_2 SEG_A | SEG_B | SEG_D | SEG_E | SEG_G
#define SEG_3 SEG_A | SEG_B | SEG_C | SEG_D | SEG_G
#define SEG_4 SEG_B | SEG_C | SEG_F | SEG_G
#define SEG_5 SEG_A | SEG_C | SEG_D | SEG_F | SEG_G
#define SEG_6 SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G
#define SEG_7 SEG_A | SEG_B | SEG_C
#define SEG_8 SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G
#define SEG_9 SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G
const unsigned char seg_display[12] = { SEG_0, SEG_1, SEG_2, SEG_3, SEG_4, SEG_5, SEG_6, SEG_7, SEG_8, SEG_9, 0xff, 0x00 };
// LIN-State
//
// 0 : disabled
// 1 : wait for synch break
// 2 : received synch field
// 3 : received header
// 4 : LIN_Data Tx/Rx
// 5 : Checksum Tx/Rx
#define DATALENGTH 8
#define SLAVESEND 0x7D //bb 0x7D
#define MASTERSEND 0xFE //aa 0xFE
volatile unsigned char LIN_State, Rx_Error, Rx_Data, Master_Send, LIN_Header, LIN_Count, LIN_Checksum;
volatile unsigned char LIN_Data[DATALENGTH];
volatile unsigned char ICU_State;
volatile unsigned char counter;
//------------------------- SUB ROUTINES ---------------------------------
void InitUart(void)
{
// Initialize UART asynchronous LIN mode
BGR = 1041; // 重载值 = 1041,波特率=10MHz/1041= 9600Baud
SMR = 0xC5; /*11000101,11-模式3,0-波特率发生器,0-重载计数器, 0-无需重启重载计数器,1-LIN-UART复位,0-SCKE作为通用IO
口或时钟输入引脚,1-使能SOT引脚*/
SSR = 0x02; // 使能接受中断
SCR = 0x01; // 使能发送
ESCR = 0x80; // 使能LIN Synch Break检测中断t
}
void InitCompTimer(void)
{
// 初始化复合定时器以测量LIN-Synch-Field
TMCR0 = 0x30; // 00110000,选择内部信号TII0作为定时器00的输入
T00CR0 = 0x0F; /*00001111, 0-不启用IF中断标志位,000-1×MCLK(机器时钟),1111-输入捕捉模式,检测两个边沿,计数器清零*/
T00CR1 = 0xA0; /*10100000, 1-允许计时器从00H开始计数,0-当计时器工作时恢复计数,1-允许输出中断请求*/
ICU_State = 0; // State flag
}
void wait(unsigned long j)
{
while(j--) asm("\tNOP");
}
/****************************************************************/ /* Main Routine */ /****************************************************************/
void main(void)
{ InitUart(); InitCompTimer();
PDR0 = 0xFF; // Port 0:
DDR0 = 0x7F; // 7-Segment display (all segments off)
InitIrqLevels(); // 初始化中断电平寄存器和IRQ向量表
__EI(); //允许全局中断
__set_il(3); // 设置全局中断为最低级别
Rx_Error = 0;
LIN_State = 1;
counter = 0;
PDR0 = seg_display[counter];
while (!Rx_Error)
asm("\tNOP");;
while(1) // 在LED上显示错误码
{
PDR0 = seg_display[Rx_Error];
wait(30000);
PDR0 = seg_display[10];
wait(10000);
}
}
/****************************************************************/
/**************************中断服务子程序************************/
/****************************************************************/
__interrupt void IRQ_LIN_RX(void)
{
if (ESCR_LBD) // 检测LIN Synch Break
{
ESCR = 0x80; // 将LBD位清零
if (LIN_State == 1)
{ SCR = 0x03; // 允许接受和中断 LIN_State = 2; LIN_Checksum = 0; LIN_Count = 0;
T00CR1 = 0xA0;
if (++counter == 10)
counter = 0;
PDR0 = seg_display[counter];
}
else { Rx_Error = 1;
}
}
else if (SSR_RDRF) // 接受? { Rx_Data = RDR;
if (SSR_ORE || SSR_FRE) // 发生溢出错误或帧错误?
{
Rx_Error = 2; //溢出错误或帧错误
}
else if (LIN_State == 2) // 接受Synch field ?
{ T00CR1 = 0x00; // 不允许产生捕捉请求中断
if (Rx_Data != 0x55)
{
Rx_Error = 3;
}
else { LIN_State = 3; //准备接受信头r
}
}
else if (LIN_State == 3) // 接受到信头?
{
LIN_Header = Rx_Data;
if (LIN_Header == SLAVESEND)
{
TDR = LIN_Data[LIN_Count]; // 发送 LIN_Data LIN_Count++;
}
LIN_State = 4;
}
else if (LIN_State == 4) //读或写LIN_Data
{
if (LIN_Header == MASTERSEND) // Master 发送数据?
{
LIN_Checksum = LIN_Checksum + Rx_Data;
LIN_Data[LIN_Count] = Rx_Data;
LIN_Count++;
if (LIN_Count == DATALENGTH) // 数据发送完?
{
LIN_Count = 0;
LIN_State = 5;
LIN_Checksum = LIN_Checksum ^ 0xFF;
}
}
else
{
LIN_Checksum = LIN_Checksum + LIN_Data[LIN_Count];
TDR = LIN_Data[LIN_Count]; // 发送下一个LIN Data LIN_Count++;
if (LIN_Count == DATALENGTH)
{
LIN_Count = 0;
LIN_State = 5;
LIN_Checksum = LIN_Checksum ^ 0xFF;
}
}
}
else if (LIN_State == 5)
{ if (LIN_Header == MASTERSEND) // Master发送数据? {
if (Rx_Data != LIN_Checksum)
{
Rx_Error = 4;
}
}
else
{
TDR = LIN_Checksum; // 发送 Checksum
}
SCR_RXE = 0; // 停止接受等待
LIN break LIN_State = 1; }
}
else
{
Rx_Error = 5;
SSR_RIE = 0; // 不允许接受中断
}
}
__interrupt void IRQ_CompTimer(void)
{
if (ICU_State == 0) // 检测到上升沿
{
ICU_State = 1; } else //检测到下降沿(最后一个边沿)
{
ICU_State = 0;
BGR = T0DR >> 3;
}
if (T00CR1_IF)
Rx_Error = 6;
T00CR1 = 0xA0; // 清除标志位
}


