⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lin_master.c

📁 基于LPC76X的LIN开发通信源程序调试通过
💻 C
📖 第 1 页 / 共 2 页
字号:
/****************************************************************/
/* 项目名称     : 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 + -