📄 lin_master.c
字号:
/****************************************************************/
/* 项目名称 : R8C/23 CAN/LIN Demo Board */
/* : 样例程序3 */
/* : LIN主机 */
/* 硬件环境 : R0K521237C000BB */
/* MCU型号 : R5F21237JFP */
/* C编译器 : NC30WA,版本5.30.r02及以上 */
/* 文件名 : lin_master.c */
/* 版本 : 1.0 */
/* 作者 : wangsy */
/* 修订历史 : */
/* 修订者 时间 版本 说明 */
/* wangsy 2006/9/11 1.0 初版发行 */
/* */
/****************************************************************/
/* Copyright, 2006 RENESAS TECHNOLOGY CORPORATION & */
/* RENESAS SOLUTIONS CORPORATION */
/****************************************************************/
/*------------------------------------------------------------
包含头文件
------------------------------------------------------------*/
#include "../inc/Comdef_r823.h"
#include "../inc/Isr.h"
#include "../inc/lin_master.h"
/*------------------------------------------------------------
声明变量
------------------------------------------------------------*/
lin_buffer_def lin_buffer; // LIN 缓冲区
sch_flg lin_sch; // 进度表状态
frm_flg lin_frm; // 帧状态
uchar8 lin_cmd = CMD_NO_COMMAND; // lin命令
uchar8 lin_sts = lin_SLEEP; // 初始状态休眠态
uchar8 cur_processing_frm = 0; // 当前处理的帧序号
uchar8 cur_processing_data = 0; // 当前处理的数据段的数据序号
uchar8 error_code = NO_ERROR; // 错误代码
uchar8 sleep_status = SLEEP_RCV_WAKEUP; // sleep的初始状态
uchar8 run_status = RUN_IDLE; // 主机帧的初始状态
/*---------------table 定义---------------*/
/*---------------状态转换图---------------*/
uchar8 linM[7][5] =
{ // SLEEP RESET RUN IDLE ERROR
{lin_SLEEP, lin_RESET, lin_RUN, lin_IDLE, lin_SLEEP}, // CMD_NO_COMMAND
{lin_SLEEP, lin_SLEEP, lin_SLEEP, lin_SLEEP, lin_SLEEP}, // CMD_SLEEP
{lin_RESET, lin_RESET, lin_RESET, lin_RESET, lin_RESET}, // CMD_RESET
{lin_RESET, lin_RUN, lin_RUN, lin_RESET, lin_RESET}, // CMD_RUN
{lin_SLEEP, lin_RESET, lin_IDLE, lin_IDLE, lin_IDLE}, // CMD_IDLE
{lin_ERROR, lin_ERROR, lin_ERROR, lin_ERROR, lin_ERROR}, // CMD_ERROR
{lin_RESET, lin_RESET, lin_RUN, lin_RESET, lin_ERROR}, // CMD_WAKEUP
};
/*---------------进度表,每个进度表所包含的帧数---------------*/
uchar8 schedule_include_frm_num[TOTAL_SCH] =
{
3, // 3
1, // 1
1, // 1
1, // 1
};
/*---------------进度表table---------------*/
lin_std_frm_def SCHEDULE_TABLE[6] = // 6 = above table 3+1+1+1
{
// dlc码:11: 8bytes/ 10: 4bytes/ 01: 2bytes/00: 1byte
// cmd 01: 主机节点发送,其余节点接收
// 02: 从机节点发送给主机
// 03: 从机节点发送
// (Cmd) (DLC) (DATA)
// schedule0
{0x01, 0x03, {0x77,0x66,0x55,0x44,0x33,0x22,0x11,0x00}},
{0x02, 0x03, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
{0x03, 0x03, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
// schedule1
{0x0C, 0x03, {0x0,0x55,0x55,0x55,0x55,0x55,0x55,0x0}},
// schedule2
{0x02, 0x03, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
// schedule3
{0x03, 0x03, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
};
/*---------------dlc码与实际发送/接收字节数对照表---------------*/
uchar8 dlc_convert[4] =
{
1, // 00 即 1个字节
2, // 01 即 2个字节
4, // 10 即 4个字节
8, // 11 即 8个字节
};
/*------------------------------------------------------------
声明函数
------------------------------------------------------------*/
void sleep_processing(uchar8 sleep_app_event); // 测试是否收到wakeup命令
void run_processing(void); // LIN 帧处理
void idle_processing(void); // 测试是否进入睡眠
void uart_error_test(uchar8 uart_h); // UART传输错误检测
void frame_30ms_timeout_test(void); // 30ms超时处理
void sch_config(void); // 进度表初始化
void timerRA_snd_break_config(void); // 定时器RA,发送break设置
void timerRA_snd_wakeup_config(void);
/*------------函数定义------------------*/
/*------------------------------------------------------
名称 : lin_sts_convert
功能 : LIN状态转换
入口 : uchar8 lin_in_cur_sch:当前进度表
uchar8 app_event: 外部事件
出口 : 无
调用 : 调用 void sleep_processing(uchar8 sleep_app_event)
void sch_config(void)
void run_processing(void)
void idle_processing(void)
寄存器 : 无
------------------------------------------------------*/
void lin_sts_convert(uchar8 lin_in_cur_sch, uchar8 app_event)
{
switch(lin_sts)
{
case lin_SLEEP: // 主机进入休眠状态
sleep_processing(app_event);
break;
case lin_RESET: // 主机进入初始化状态
if(timerRB_10ms_counter >= 10U)
{
lin_cmd = CMD_RUN;
/*--------lin 主机初始化工作准备开始--------*/
// 进度表标志位初始化
lin_sch.bit.complete = NO;
// 样例程序虽然留出了loop标志位,但并未对进度表循环播放进行处理,
// 用户可根据需要添加相应代码,例如在"检验是否为休眠命令帧"上方
// 对loop进行判断,再重新启动run命令,并启动相应标志位cur_frm, complete和计数器等。
lin_sch.bit.loop = NO;
lin_sch.bit.total_sch = TOTAL_SCH - 1;
lin_sch.bit.cur_sch = lin_in_cur_sch;
// 帧标志位初始化
lin_frm.bit.complete = NO;
lin_frm.bit.total_frm = schedule_include_frm_num[lin_sch.bit.cur_sch] - 1U;
lin_frm.bit.cur_frm = 0;
// 调用进度表初始化,将table中的数据装入lin buffer
sch_config();
/*--------lin 主机初始化工作准备完毕--------*/
}
else
{
}
break;
case lin_RUN: // 主机进入通信状态(有帧的传输)
// 判断是否执行到进度表的结尾
if( lin_frm.bit.cur_frm <= lin_frm.bit.total_frm )
{
run_processing(); // 没有,则继续运行
}
else // 有,则进入idle状态,并将进度表完成标志置1
{
lin_sch.bit.complete = YES; // 进度表完成
lin_cmd = CMD_IDLE; // 转入idle状态
timerRB_4s_counter = 0U; // 清零计数器,重新开始计数
/*------检验是否为休眠命令帧-----*/
if(((lin_buffer.lin_slot[lin_frm.bit.cur_frm - 1].data[0] == 0)&&
(lin_buffer.lin_slot[lin_frm.bit.cur_frm - 1].pid.pid_str.dlc == 3))&&
(lin_buffer.lin_slot[lin_frm.bit.cur_frm - 1].pid.pid_str.cmd == 0x0c))
{
timerRA_rcv_wakeup_config(); // timerRA准备接收wakeup信号初始化
lin_sch.bit.complete = YES; // 进度表完成
lin_cmd = CMD_SLEEP; // 转入idle状态
sleep_status = SLEEP_RCV_WAKEUP;
}
}
break;
case lin_IDLE: // 主机进入idle状态
idle_processing(); // 调用idle处理函数
break;
case lin_ERROR: // 主机进入错误状态,沉默
break;
default:
break;
}
}
/*------------------------------------------------------
名称 : sleep_processing
功能 : sleep状态处理函数
一旦发现总线上出现显性电平,将4s计数器清零
否则,大于4s系统将进入休眠状态
入口 : uchar8 sleep_app_event: 外部事件
出口 : 无
调用 : void timerRA_rcv_wakeup_config(void)
寄存器 : traic,tracr,p1_5,pd1_5
------------------------------------------------------*/
void sleep_processing(uchar8 sleep_app_event)
{
switch(sleep_status)
{
case SLEEP_RCV_WAKEUP:
if(sleep_app_event != NO)
{
timerRA_snd_wakeup_config();
sleep_status = SLEEP_SND_WAKEUP;
}
// 测试是否有其他节点唤醒了网络
// 脉冲宽度测量模式下,引起timerRA中断查询标志为1的两种可能:
// timerRA下溢;脉冲结束
else if((ir_traic == YES)&&(tedgf_tracr == YES))
{
// 脉冲结束,未到达150us
timerRA_rcv_wakeup_config(); // 重新启动timerRA,准备接收wakeup信号
}
// 150us定时器下溢时脉冲尚未结束,脉冲宽度大于150us,确认为wakeup信号
else if((ir_traic == YES)&&(tundf_tracr == YES))
{
lin_cmd = CMD_WAKEUP; // 从sleep状态进入idle状态,并开始计时
timerRB_10ms_counter = 0U;
}
else
{
}
break;
case SLEEP_SND_WAKEUP: // wakeup发送完毕
if(ir_traic == YES)
{
p1_5 = PPORT_HIGH; // 一旦timerRA计数结束,引脚p1_5停止输出低电平
pd1_5 = PPORT_INPUT;
tstop_tracr = HEN; // 强制timerRA终止/LIN 流程
while(tcstf_tracr == HON) // 等待timerRA直到计数停止
{
}
ir_traic = NO;
timerRB_10ms_counter = 0;
lin_cmd = CMD_WAKEUP;
}
else
{
}
break;
default:
break;
}
}
/*------------------------------------------------------
名称 : sch_config
功能 : 进度表初始化,将table中的数据装入lin buffer
入口 : 无
出口 : 无
调用 : 无
寄存器 : 无
------------------------------------------------------*/
void sch_config(void)
{
uchar8 lp_frame;
uchar8 lp_data;
uchar8 lp_dlc;
uchar8 cur_sch_top = 0;
// 首先判断传送的是第几个进度表
for(lp_frame = 0; lp_frame< lin_sch.bit.cur_sch; lp_frame++)
{
cur_sch_top += schedule_include_frm_num[lp_frame];
}
// 从进度表table中装入LIN buffer
for (lp_frame = 0; lp_frame <= lin_frm.bit.total_frm; lp_frame++)
{
// 由于table中只定义了用户指令,需要将其翻译成受保护ID(PID)
// PID构成(LSB):
// PID0: 帧ID0:COMMAND0
// PID1: 帧ID1:COMMAND1
// PID2: 帧ID2:COMMAND2
// PID3: 帧ID3:COMMAND3
// PID4: 帧ID4:DLC0
// PID5: 帧ID5:DLC1
// PID6:奇偶校验位:P0 = ID0+ID1+ID2+ID4
// PID7:奇偶校验位:P1 = ~(ID1+ID3+ID4+ID5)
lin_buffer.lin_slot[lp_frame].pid.bit.ID5 = SCHEDULE_TABLE[cur_sch_top + lp_frame].dlc.bit.dlc1;
lin_buffer.lin_slot[lp_frame].pid.bit.ID4 = SCHEDULE_TABLE[cur_sch_top + lp_frame].dlc.bit.dlc0;
lin_buffer.lin_slot[lp_frame].pid.bit.ID3 = SCHEDULE_TABLE[cur_sch_top + lp_frame].cmd.bit.cmd3;
lin_buffer.lin_slot[lp_frame].pid.bit.ID2 = SCHEDULE_TABLE[cur_sch_top + lp_frame].cmd.bit.cmd2;
lin_buffer.lin_slot[lp_frame].pid.bit.ID1 = SCHEDULE_TABLE[cur_sch_top + lp_frame].cmd.bit.cmd1;
lin_buffer.lin_slot[lp_frame].pid.bit.ID0 = SCHEDULE_TABLE[cur_sch_top + lp_frame].cmd.bit.cmd0;
lin_buffer.lin_slot[lp_frame].pid.bit.P1 =
~(lin_buffer.lin_slot[lp_frame].pid.bit.ID1
+ lin_buffer.lin_slot[lp_frame].pid.bit.ID3
+ lin_buffer.lin_slot[lp_frame].pid.bit.ID4
+ lin_buffer.lin_slot[lp_frame].pid.bit.ID5);
lin_buffer.lin_slot[lp_frame].pid.bit.P0 =
lin_buffer.lin_slot[lp_frame].pid.bit.ID0
+ lin_buffer.lin_slot[lp_frame].pid.bit.ID1
+ lin_buffer.lin_slot[lp_frame].pid.bit.ID2
+ lin_buffer.lin_slot[lp_frame].pid.bit.ID4;
// 将相应数量(由dlc决定)的数据载入到buffer中
lp_dlc = dlc_convert[lin_buffer.lin_slot[lp_frame].pid.pid_str.dlc];
for (lp_data = 0; lp_data < lp_dlc; lp_data ++)
{
lin_buffer.lin_slot[lp_frame].data[lp_data] = SCHEDULE_TABLE[cur_sch_top + lp_frame].data[lp_data];
}
// 计算要发送数据的(标准型)校验和字节:
// 将数据字节逐加,一旦大于或等于256时就减去255,将最后结果取反
lp_data = 0;
lin_buffer.lin_slot[lp_frame].checksum = 0;
while(lp_dlc > 0)
{
if ((uint16)lin_buffer.lin_slot[lp_frame].checksum
+ SCHEDULE_TABLE[cur_sch_top + lp_frame].data[lp_data] >= 256U)
{
lin_buffer.lin_slot[lp_frame].checksum += SCHEDULE_TABLE[cur_sch_top + lp_frame].data[lp_data];
lin_buffer.lin_slot[lp_frame].checksum ++;
}
else
{
lin_buffer.lin_slot[lp_frame].checksum += SCHEDULE_TABLE[cur_sch_top + lp_frame].data[lp_data];
}
lp_data ++;
lp_dlc --;
}
lin_buffer.lin_slot[lp_frame].checksum = ~lin_buffer.lin_slot[lp_frame].checksum;
}
}
/*------------------------------------------------------
名称 : run_processing
功能 : 进度表初始化,将table中的数据装入lin buffer
入口 : 无
出口 : 无
调用 : void timerRA_snd_break_config(void)
void frame_30ms_timeout_test(void)
寄存器 : tracr,linst,u0c1,s0tic,s0ric,u0tbl,u0rbl,u0rbh,pd1_4,pd1_5
------------------------------------------------------*/
void run_processing(void)
{
uchar8 uarth_error_buf = 0; // 接收uart高位字节
uchar8 local_checksum = 0; // 本地校验和
uchar8 lp_data_comm = 0; // 循环变量 for data
uchar8 lp_dlc_comm = 0; // 计数变量 for dlc
switch(run_status)
{
case RUN_IDLE: // 发送break段的准备工作
timerRA_snd_break_config(); // 作好发送break时timerRA初始化工作
// 确认timerRA已经启动,启动后转入发送break状态,并开始对帧的处理时间计时
if(tcstf_tracr == HON)
{
run_status = RUN_SND_BREAK; // 主机节点进入发送break状态,此时break刚刚发出。
timerRB_10ms_counter = 0U;
}
else
{
}
break;
case RUN_SND_BREAK: // 发送break,确认
if(sbdct_linst == YES) // 检测到break结束
{
tstop_tracr = HEN; // 强制timerRA停止计数
while(tcstf_tracr == HON) // 等待直到timerRA停止计数
{
}
run_status = RUN_TX_SYNC; // 进入发送同步段(SYNC)状态
pd1_5 = PPORT_INPUT; // p1_5/RXD0
pd1_4 = PPORT_OUTPUT; // p1_4/TXD0
te_u0c1 = HEN; // 启动UART发送
ir_s0tic = NO; // 清零uart发送中断查询标志
u0tbl = SYNC_TIME; // uart发送缓冲器=0x55h,SYNC字段
}
else // 未检测到break段等待
{
}
break;
case RUN_TX_SYNC: // 发送同步段,确认
if(ir_s0tic == YES) // 判断SYNC是否已发送完毕
{
ir_s0tic = NO; // 发送完毕,将该标志清零
run_status = RUN_TX_PID; // 状态进入发送PID
// 将uart发送缓冲器=PID,protected ID
u0tbl = lin_buffer.lin_slot[lin_frm.bit.cur_frm].pid.byte;
}
else // 等待发送完毕
{
}
break;
case RUN_TX_PID: // 发送PID段,确认,分析PID段
if(ir_s0tic == YES) // 判断SYNC是否已发送完毕
{
ir_s0tic = NO; // 发送完毕,将该标志清零
cur_processing_data = 0; // 当前处理的数据 data0
// 分析pid中帧id的低四位,command
switch(lin_buffer.lin_slot[lin_frm.bit.cur_frm].pid.pid_str.cmd)
{
// command = 1,主机发送,其它从机接收
case 0x01:
case 0x0C:
run_status = RUN_TX_DATA; // 主机转入发送数据段的状态
// 将data0放入uart发送buffer
u0tbl = lin_buffer.lin_slot[lin_frm.bit.cur_frm].data[cur_processing_data];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -