📄 lin_slave.c
字号:
/****************************************************************/
/* 项目名称 : R8C/23 CAN/LIN Demo Board */
/* : 样例程序4 */
/* : LIN从机 */
/* 硬件环境 : R0K521237C000BB */
/* MCU型号 : R5F21237JFP */
/* C编译器 : NC30WA,版本5.30.r02及以上 */
/* 文件名 : lin_slave.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_slave.h"
/*------------------------------------------------------------
声明变量
------------------------------------------------------------*/
lin_slave_buffer_def lin_slave_buffer; // LIN 缓冲区
pid_def rec_pidl; // 接收PID
// 计数器及标志
uchar8 app_event = NO; // 应用层事件发生:YES
uchar8 wakeup_counter = 4; // 唤醒次数计数器(每发出一次唤醒信号+1,直到4,不再发出)
uchar8 cur_processing_frm = 0; // 当前处理帧
uchar8 cur_processing_data = 0; // 当前处理数据
// 状态,命令
uchar8 lin_sts = lin_IDLE; // LIN总状态
uchar8 lin_cmd = CMD_NO_COMMAND; // LIN总命令
uchar8 sleep_status = SLEEP_DUMMY; // 休眠状态
uchar8 idle_status = IDLE_DUMMY; // IDLE状态
uchar8 run_status = RUN_RCV_BREAK; // 运行(帧通信)状态
uchar8 error_code = NO_ERROR; // 错误代码
/*------------------------------------------------------------
函数声明
------------------------------------------------------------*/
void event_processing(uchar8 lin_in_event_processing); // 外部事件处理函数
void sleep_processing(void); // 休眠状态处理函数
void idle_processing(void); // idle状态处理函数
void run_processing(void); // 运行状态处理函数
void uart_init(void); // UART初始化
void uart_error_test(uchar8 uart_h); // UART错误检测
void frame_30ms_timeout_test(void); // 30ms超时检测
void timerRA_snd_wakeup_config(void); // timerRA发送wakeup信号初始化
void timerRA_rcv_wakeup_config(void); // timerRA接收wakeup信号初始化
void timerRA_rcv_break_config(void); // timerRA接收break信号初始化
/*------------------table 定义-----------------*/
/*--------------LIN总状态机--------------*/
uchar8 linM[5][4] =
{ // SLEEP IDLE RUN ERROR
{lin_SLEEP, lin_IDLE, lin_RUN, lin_ERROR}, // CMD_NO_COMMAND
{lin_SLEEP, lin_SLEEP, lin_SLEEP, lin_ERROR}, // CMD_SLEEP
{lin_IDLE, lin_IDLE, lin_IDLE, lin_ERROR}, // CMD_IDLE
{lin_IDLE, lin_RUN, lin_RUN, lin_ERROR}, // CMD_RUN
{lin_ERROR, lin_ERROR, lin_ERROR, lin_ERROR}, // CMD_ERROR
};
/*--------------LIN从机信箱--------------*/
lin_slave_slot_def lin_slave_slot[3] =
{
// 帧ID(frm_id) 31: 主机发送,从机接收
// 非31:从机发送
// (frm_id) (DATA)
{0x31, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
{0x32, {0x03,0x11,0x22,0x33,0x44,0x55,0x66,0x77}},
{0x33, {0x34,0x22,0x44,0x55,0x44,0x33,0x44,0x55}},
};
/*----dlc码与实际发送/接收字节数对照表----*/
uchar8 dlc_convert[4] =
{
1, // 00 即 1个字节
2, // 01 即 2个字节
4, // 10 即 4个字节
8, // 11 即 8个字节
};
/*------------------函数定义---------------------*/
/*------------------------------------------------------
名称 : lin_sts_convert
功能 : LIN状态转换
入口 : uchar8 lin_in_event:外部事件
出口 : 无
调用 : 调用 void event_processing(void)
void sleep_processing(void)
void idle_processing(void)
void run_processing(void)
寄存器 : 无
------------------------------------------------------*/
void lin_sts_convert(uchar8 lin_in_event)
{
event_processing(lin_in_event); // 应用层事件标志置1, 唤醒计数器设为0
switch(lin_sts) // LIN总状态机
{
case lin_SLEEP: // LIN休眠状态
sleep_processing(); // 处理应用层事件为1的情况以及接收到wakeup信号时的情况
break;
case lin_IDLE: // LIN IDLE状态
idle_processing(); // 判断休眠,判断发出的唤醒是否成功唤醒网络
break;
case lin_RUN: // LIN RUN状态
run_processing(); // LIN 帧通信处理函数
break;
case lin_ERROR: // LIN 错误状态
break;
default:
break;
}
}
/*------------------------------------------------------
名称 : event_processing
功能 : 应用层事件处理
入口 : uchar8 lin_in_event_processing: 外部事件
出口 : 无
调用 : 无
寄存器 : 无
------------------------------------------------------*/
void event_processing(uchar8 lin_in_event_processing)
{
if(lin_in_event_processing != NO)
{
app_event = lin_in_event_processing; // 应用层事件代码
wakeup_counter = 0; // 唤醒计数器设0
}
else
{
}
}
/*------------------------------------------------------
名称 : sleep_processing
功能 : 休眠状态处理函数:处理应用层事件为1的情况以及接收到wakeup信号时的情况
入口 : 无
出口 : 无
调用 : void timerRA_snd_wakeup_config(void)
void timerRA_rcv_wakeup_config(void)
void timerRA_rcv_break_config(void)
寄存器 : traic,tracr
------------------------------------------------------*/
void sleep_processing(void)
{
switch(sleep_status) // 休眠状态下的子状态机
{
case SLEEP_DUMMY: // 初次进入休眠
if((app_event != NO )&&(wakeup_counter < 3)) // 应用层事件发生,不到3次发出wakeup唤醒信号
{
timerRA_snd_wakeup_config(); // timerRA发送wakeup信号初始化
wakeup_counter ++; // 发送wakeup信号计数器 +1
sleep_status = SLEEP_WAKEUP; // 进入sleep_wakeup
}
else if((app_event != NO)&&(wakeup_counter == 3)) // 应用层事件发生,已经发出了3次wakeup
{
timerRA_rcv_wakeup_config(); // timerRA接收wakeup初始化
lin_cmd = CMD_SLEEP; // 返回休眠态
sleep_status = SLEEP_DUMMY;
wakeup_counter = 4; // 设为4下次不再进入该状态
}
// tiemrRA发生中断两种可能性,脉冲结束(宽度不够150us),timerRA下溢(宽度大于150us)
else if((ir_traic == YES)&&(tedgf_tracr == YES)) // timerRA发生中断,脉冲不够150us
{
timerRA_rcv_wakeup_config(); // timerRA接收wakeup初始化
}
else if((ir_traic == YES)&&(tundf_tracr == YES)) // timerRA发生中断,脉冲不够150us
{
sleep_status = SLEEP_WAKEUP; // timerRA发生中断,脉冲宽度大于150us
}
else
{
}
break;
case SLEEP_WAKEUP: // 休眠唤醒
if(ir_traic == YES) // timerRA中断查询标志为1,发送完毕/接收到wakeup
{
timerRA_rcv_break_config(); // timerRA接收break初始化
timerRB_10ms_counter = 0U; // 计数器开始计数
lin_cmd = CMD_IDLE; // 进入LIN idle状态
idle_status = IDLE_WAKEUP; // 由于唤醒进入
}
else // 等待
{
}
break;
default:
break;
}
}
/*------------------------------------------------------
名称 : idle_processing
功能 : 休眠状态处理函数:处理应用层事件为1的情况以及接收到wakeup信号时的情况
入口 : 无
出口 : 无
调用 : void timerRA_rcv_wakeup_config(void)
寄存器 : p1_5
------------------------------------------------------*/
void idle_processing(void)
{
switch(idle_status) // idle状态下的字状态
{
case IDLE_DUMMY: // 程序一启动,或帧结束后进入idle状态
if(timerRB_4s_counter >= 400U) // 4s后没有收到break即进入休眠状态
{
timerRA_rcv_wakeup_config(); // timerRA接收wakeup初始化
lin_cmd = CMD_SLEEP; // 进入休眠
sleep_status = SLEEP_DUMMY;
}
else if(sbdct_linst == YES) // 检测到break信号
{
lin_cmd = CMD_RUN; // 进入run状态
run_status = RUN_RCV_BREAK; // 初始化run状态的子状态
}
else
{
}
break;
case IDLE_WAKEUP: // 由wakeup唤醒进入idle状态
if(timerRB_10ms_counter >= 15)
{
timerRA_rcv_wakeup_config(); // 150ms内未收到break信号
lin_cmd = CMD_SLEEP; // 重返休眠状态
sleep_status = SLEEP_DUMMY;
}
else if(sbdct_linst == YES) // 检测(接收)到break信号
{
p1_5 = PPORT_HIGH; // 若该wakeup是本节点发出,将脉冲转为高电平,对于其余情况不影响
wakeup_counter = 0;
lin_cmd = CMD_RUN; // 进入run状态
run_status = RUN_RCV_BREAK; // 初始化run状态的子状态
}
else
{
}
break;
default:
break;
}
}
/*------------------------------------------------------
名称 : run_processing
功能 : 运行(帧通信)状态处理函数,接收header,发送/接收/忽略数据
入口 : 无
出口 : 无
调用 : void uart_init(void)
void timerRA_rcv_break_config(void)
void frame_30ms_timeout_test(void)
寄存器 : linst,trapre,tra,pd1_5,pd1_4,
u0c1,u0brg,s0ric,s0tic,u0rbl,u0rbh,u0tbl
------------------------------------------------------*/
void run_processing(void)
{
uchar8 uarth_error_buf = 0; // 接收uart高位字节
uchar8 local_checksum = 0; // 本地校验和
uchar8 lp_data_comm = 0; // 循环变量for data
uchar8 lp_slot_comm = 0; // 循环变量 for 信箱
uchar8 lp_dlc_comm = 0; // 计数变量 for dlc
uint16 Reset_uart0_bitrate = 0U; // 比特率调整量
switch(run_status) // 运行状态下的子状态机
{
case RUN_RCV_BREAK: // 接收break
run_status = RUN_RCV_SYNC; // 转入接收sync子状态
timerRB_10ms_counter = 0; // 开始计数
break;
case RUN_RCV_SYNC: // 接收sync同步段子状态
if( sfdct_linst == YES) // 确认sync measure完毕
{
run_status = RUN_RX_PID; // 转入接收PID(受保护ID)子状态
uart_init(); // 调用UART初始化函数
// 根据SYNC测量结果,重新计算UART波特率,timerRA的当前值为8个bit所用的时间
Reset_uart0_bitrate = ((TRA_BREAKTEST - tra) * TRAPRE_BREAKTEST + TRAPRE_BREAKTEST - trapre) >> 7;
u0brg = (uchar8)Reset_uart0_bitrate - (uchar8)1;
pd1_5 = PPORT_INPUT; // 设置RXD0端口:输入
pd1_4 = PPORT_INPUT; // 设置TXD0端口:输入
re_u0c1 = HEN; // 允许uart接收
te_u0c1 = LDIS; // 禁止uart发送
}
else
{
}
break;
case RUN_RX_PID: // 接收PID(受保护ID)子状态
if(ir_s0ric == YES) // 接收完毕
{
ir_s0ric = NO; // 清零接收完毕标志
rec_pidl.byte = u0rbl;
uarth_error_buf = u0rbh;
uart_error_test(uarth_error_buf); // 检测uart传输错误
re_u0c1 = LDIS; // 禁止uart截收
te_u0c1 = LDIS; // 禁止uart发送
// 检测PID奇偶校验错误
// PID6:奇偶校验位:P0 = ID0+ID1+ID2+ID4
// PID7:奇偶校验位:P1 = ~(ID1+ID3+ID4+ID5)
if((rec_pidl.bit.P1 != ((~(rec_pidl.bit.ID1 + rec_pidl.bit.ID3 + rec_pidl.bit.ID4 + rec_pidl.bit.ID5))&0x01U))||
(rec_pidl.bit.P0 != ((rec_pidl.bit.ID0 + rec_pidl.bit.ID1 + rec_pidl.bit.ID2 + rec_pidl.bit.ID4)&0x01U)))
{
lin_cmd = CMD_ERROR;
error_code = PID_PARITY_ERROR;
}
else
{
// 以下对指令(帧ID)进行分析:若app_event != NO,
// 且该帧ID对应了该节点应用层某事件,即事件引起的信号变化已经
// 在网络中传输出去,则将app_event清零。
// 由于该样例程序并未对帧ID进行详细定义,因此该清零动作并未在代码
// 中出现,需要用户自行添加。
switch(rec_pidl.pid_str.cmd)
{
case 0x01: // 分析指令cmd = 1,主机发送,从机接收
case 0x0C: // 休眠指令
run_status = RUN_RX_DATA; // 转入数据接收子状态
cur_processing_data = 0; // 当前处理数据为0
re_u0c1 = HEN; // 允许uart接收
te_u0c1 = LDIS; // 禁止uart发送
break;
default: //其余指令,如果id与slot id匹配,则将slot中的对应id的数据发送出去
lp_slot_comm = 0;
while(lp_slot_comm < 3) // 设了3个slot,从机节点应该有32个slot,这里简化了
{
// 检测接收pid中的id是否与信箱中设的id匹配
if(lin_slave_slot[lp_slot_comm].slot_id == rec_pidl.frame_id_and_parity.frame_id)
{
run_status = RUN_TX_DATA; // 进入发送数据子状态
// 将slot中的数据存入buffer
for(lp_data_comm = 0; lp_data_comm < dlc_convert[rec_pidl.pid_str.dlc]; lp_data_comm ++)
{
lin_slave_buffer.data[lp_data_comm] = lin_slave_slot[lp_slot_comm].data[lp_data_comm];
}
lp_slot_comm = 10; //一旦匹配就终止while,这里可设大于等于slot总数的任何数字
/*----------------计算校验和------------------*/
// 将数据字节逐加,一旦大于或等于256时就减去255,将最后结果取反
lp_data_comm = 0;
lp_dlc_comm = dlc_convert[rec_pidl.pid_str.dlc];
lin_slave_buffer.checksum = 0;
while(lp_dlc_comm > 0)
{
if ((uint16)lin_slave_buffer.checksum
+ lin_slave_buffer.data[lp_data_comm] >= 256U)
{
lin_slave_buffer.checksum += lin_slave_buffer.data[lp_data_comm];
lin_slave_buffer.checksum ++;
}
else
{
lin_slave_buffer.checksum += lin_slave_buffer.data[lp_data_comm];
}
lp_data_comm ++;
lp_dlc_comm --;
}
lin_slave_buffer.checksum = ~lin_slave_buffer.checksum;
/*---------------计算校验和结束-----------------*/
te_u0c1 = HEN; // uart发送允许
re_u0c1 = LDIS; // uart接收禁止
ir_s0tic = NO; // 清零发送中断查询标志
cur_processing_data = 0; // 当前处理数据设为0
u0tbl = lin_slave_buffer.data[cur_processing_data]; //发送数据data0
cur_processing_data ++; // 计数器 +1
}
else
{
lp_slot_comm ++; // 检测下一个信箱slot
}
}
if(lp_slot_comm == 3) // 当信箱检测完,都没有发现匹配的ID时
{
timerRA_rcv_break_config(); // timerRA接收break初始化
timerRB_4s_counter = 0U; // 4s计数器启动
lin_cmd = CMD_IDLE; // 程序进入idle状态
idle_status = IDLE_DUMMY;
}
else
{
}
break;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -