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

📄 lin_slave.c

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