/*
LIN 总线的主要特性有
单主机 多从机概念
基于普通 UART/SCI 接口的低成本硬件实现 低成本软件或作为纯状态机
从机节点不需要石英或陶瓷谐振器可以实现自同步
保证信号传输的延迟时间
低成本的单线设备
速度高达 20kbit/s
总线的通讯由单个主机控制 每个报文帧都用一个分隔信号起始 ,一个同步场和一个标识符场 .
这些都由主机任务发送 从机任务则是发回数据场和校验场
报文路由 报文的内容由识别符命名 识别符不指出报文的目的地 但解释数据的含义 最大的
标识符数量是 64 其中 4 个保留用于专用的通讯 譬如软件升级或诊断
多播 由于引入了报文滤波的概念 任何数目的节点都可以同时接收报文 并同时对此报文做出反应
位速率
最大的波特率是 20kbit/s 它是由单线传输媒体的 EMI 限制决定 最小的波特率是 1kbit/s 可以避免
和实际设备的超时周期冲突,为使用低成本的 LIN 器件 建议使用下面的位速率
建议的位速率
低速 中速 高速
2400 bit/s 9600 bit/s 19200 bit/s
单主机 无仲裁
只有包含主机任务的控制器节点可以传输报文头,一个从机任务对这个报文头作出响应,由于没有仲
裁过程,如果多于一个从机回应,则将产生错误.这种情况下的错误界定可由用户按照应用要求指定.
安全性
错误检测
监控 发送器比较总线 应当 的值和 现在 的值
数据场的校验和以 256 为模并取反 将 MSB 的进位加到 LSB 上
标识符场的双重奇偶校验保护
连接
LIN 网络节点的最大数量不仅由标识符的数量限制 见上面的信息路由 也由总线的物理特性限制
建议 LIN 网络的节点数量不应超过 16 否则 节点增加将减少网络阻抗 会导致环境条件变差
禁止无错误的通讯 每一个增加的节点都可以减少约 3 的网络阻抗 30k || 1k
网络中总的 电 线 通讯导线 长度应少于或等于 40m
主机节点的总线端电阻典型值是 1k 从机节点是 30k
总线值
总线有两个互补的逻辑值 显性 或 隐性 相应的位值和电压值
表 2.2 逻辑和物理总线值
逻辑值 位值 总线电压
显性 0 地
隐性 1 电池
应答
正确接收报文后的应答过程在 LIN 协议中没有定义 主机控制单元检查由主机任务初始化的报文
和由它自己的从机任务接收的报文的一致性 如果不一致 例如 丢失从机响应 校验和不正确等等 主
机任务可以改变报文的进度表
如果从机检测到不一致 从机控制器将保存这个信息并将它用诊断信息的形式向主机控制单元请求
诊断信息可按普通报文帧的形式进行发送
每个报文帧都由一个同步间隔 SYNCH BREAK 起始 接着是同步场 SYNCH FIRLD 这个同
步场在几倍的位定时长度中包含了 5 个下降沿 即 隐性 到 显性 的转换
一个报文帧 是由一个主机节点发送的报文头和一个主机或从机节点发送的响应组成
报文帧的报文头包括一个同步间隔场 SYNCH BREAK FIELD,一个同步场 SYNCH FIELD,和一个标识符场
报文帧的响应 RESPONSE 则由 3 个到 9 个字节场组成 2 或 4 或 8 字节的数据场 DATA FIELD
和一个校验和场 CHECKSUM FIELD
字节场的格式 通常的 SCI 或 UART 串行数据格式 8N1 编码 每个字节场
的长度是 10 个位定时 BIT TIME 起始位 START BIT 是一个 显性 位 它标志着字节场的开始
接着是 8 个数据位 首先发送最低位 停止位 STOP BIT 是一个 隐性 位 它标志着字节场的结束
报文头场 HEADER fields
同步间隔 SYNCHRONISATION BREAK
为了能清楚识别报文帧的开始 报文帧的第一个场是一个同步间隔 SYNCH BREAK 同步间隔场
SYNCH BREAK FIELD 是由主机任务发送.它使所有的从机任务与总线时钟信号同步
同步间隔场有两个不同的部分,第一个部分是由一个持续 T SYNBRK 或更长时间 即最小是
T SYNBRK 不需要很严格 的显性总线电平 接着的第二部分是最少持续 T SYNDEL 时间的隐性电平
作为同步界定符 第二个场允许用来检测下一个同步场 SYNCH FIELD 的起始位最大的间隔和界定
符时间没有精确的定义 但必须符合整个报文头 T HEADER_MAX 的总体时间预算
同步场 SYNCH FIELD
同步场 SYNCH FIELD 包含了时钟的同步信息 同步场 SYNCH FIELD 的格式是 0x55 表
现在 8 个位定时中有 5 个下降沿 即 隐性 跳变到 显性 的边沿 见图 3.4
标识符场 IDENTIFIER FIELD
标识符场 ID-FIELD 定义了报文的内容和长度 其中 内容是由 6 个标识符 IDENTIFIER 位和
两个 ID 奇偶校验位 ID PARITY bit 表示 见图 3.5 标识符位的第 4 和第 5 位 ID4 和 ID5 定义了
报文的数据场数量 N DATA 见表 3.2 这将把 64 个标识符分成 4 个小组 每组 16 个标识符 这些标识
符分别有 2 4 和 8 个数据场
响应场 RESPONSE field
数据场 DATA FIELD 和 校验和场 CHECKSUM FIELD
数据场通过报文帧传输 由多个 8 位数据的字节场组成 传输由 LSB 开始
校验和场 CHECKSUM FIELD
校验和场是数据场所有字节的和的反码和按 带进位加 ADDC 方式计算 每个进位
都被加到本次结果的最低位 LSB 这就保证了数据字节的可靠性,所有数据字节的和的补码与校验和字节之加的和必须是 0xFF
*/
#include "lin.h"
#include "lin_queue.h"
#include "lin_handle.h"
#include "target.h"
#define LIN_CHANNEL UART4
#define RCC_LIN_APB RCC_APB1PeriphClockCmd
#define RCC_LIN_CLK RCC_APB1Periph_UART4
#define LIN_BOAURATE 19200
#define LIN_IRQ UART4_IRQn
#define LIN_INT_FUNC UART4_IRQHandler
#define LIN_PORT GPIOC
#define LIN_TX_PIN GPIO_Pin_10
#define LIN_RX_PIN GPIO_Pin_11
#define LIN_TX_CONFIG() GPIOConfig(LIN_PORT, LIN_TX_PIN, GPIO_Mode_AF_PP)
#define LIN_RX_CONFIG() GPIOConfig(LIN_PORT, LIN_RX_PIN, GPIO_Mode_IN_FLOATING)
#define LIN_CS_PORT GPIOC
#define LIN_CS_PIN GPIO_Pin_12
#define LIN_CS_CONFIG() GPIOConfig(LIN_CS_PORT, LIN_CS_PIN, GPIO_Mode_Out_PP)
#define LIN_CS_ENABLE() GPIO_SetBits(LIN_CS_PORT, LIN_CS_PIN)
static void lin_gpio_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
LIN_TX_CONFIG();
LIN_RX_CONFIG();
LIN_CS_CONFIG();
LIN_CS_ENABLE();
}
static void lin_nvic_init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = LIN_IRQ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
static void lin_uart_init(void)
{
USART_InitTypeDef USART_InitStructure;
RCC_LIN_APB(RCC_LIN_CLK, ENABLE);
USART_InitStructure.USART_BaudRate = LIN_BOAURATE;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No ;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(LIN_CHANNEL, &USART_InitStructure);
USART_LINBreakDetectLengthConfig(LIN_CHANNEL, USART_LINBreakDetectLength_11b);
USART_LINCmd(LIN_CHANNEL, ENABLE);
USART_Cmd(LIN_CHANNEL, ENABLE);
USART_ITConfig(LIN_CHANNEL, USART_IT_RXNE, ENABLE);
USART_ITConfig(LIN_CHANNEL, USART_IT_TXE, DISABLE);
USART_ITConfig(LIN_CHANNEL, USART_IT_LBD, ENABLE);
}
void LINInit(void)
{
lin_gpio_init();
lin_nvic_init();
lin_uart_init();
}
void LINSendChar(uint8_t ch)
{
USART_SendData(LIN_CHANNEL, ch);
while(!USART_GetFlagStatus(LIN_CHANNEL, USART_FLAG_TXE));
}
void LINSendBreak(void)
{
USART_SendBreak(LIN_CHANNEL);
}
#define BIT(A,B) ((A >> B) & 0x01)
uint8_t LINCalID(uint8_t id)
{
uint8_t 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;
}
uint8_t LINCalChecksum(uint8_t id, uint8_t *data)
{
uint32_t sum = id;
uint8_t i;
for(i = 0; i < 8; i++)
{
sum += data[i];
if(sum & 0xFF00)
{
sum = (sum & 0x00FF) + 1;
}
}
sum ^= 0x00FF;
return (uint8_t)sum;
}
//========================================================================================================
void LIN_INT_FUNC(void)
{
uint8_t ret;
if(USART_GetITStatus(LIN_CHANNEL, USART_IT_RXNE))
{
USART_ClearITPendingBit(LIN_CHANNEL, USART_IT_RXNE);
ret = USART_ReceiveData(LIN_CHANNEL);
//LINQueuePush(&lin_recv, ret);
}
if(USART_GetITStatus(LIN_CHANNEL, USART_IT_LBD))
{
USART_ClearITPendingBit(LIN_CHANNEL, USART_IT_LBD); // 检测到同步间隔场
//LinStatusSet(SYNCH);
}
if(USART_GetFlagStatus(LIN_CHANNEL, USART_FLAG_ORE) == SET) // 溢出
{
USART_ClearFlag(LIN_CHANNEL, USART_FLAG_ORE);
USART_ReceiveData(LIN_CHANNEL);
}
}