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

📄 candriver.c

📁 ARM7上的UART驱动。含有周立功CAN232的协议转换
💻 C
字号:

#include "CANdriver.h"

void UART1_RxHandler() ;
void CAN_Handler(void) ;


/* 定义接收缓冲区和当前写指针,由驱动和API公用,操作时应使用信号量 */
static INT8U uart1Buffer[UART1_BUFFER_LEN] ;
static INT16U index = 0 ;

/* 定义用户CAN数据缓冲区,将接收缓冲区的数据拷贝到此缓冲区以供用户处理 */
static INT8U userBuf[UART1_BUFFER_LEN+1] ;
static INT16U userK ;
static INT32U canID ;

//设置CAN232的CAN波特率的指令,任选一个
static INT8U canSetCan250Buf[5] = { 0x12, 0x02, 0x03, 0x04, 0xe5 } ;
static INT8U canSetCan500Buf[5] = { 0x12, 0x02, 0x03, 0x05, 0xe4 } ;

//复位CAN232的CAN的指令
static INT8U canResetCanBuf[4] = { 0x12, 0x01, 0x0f, 0xde } ;

//启动CAN232的CAN收发的指令,没有启动CAN之前的收发都是不成功的。
static INT8U canStartCanBuf[4] = { 0x12, 0x01, 0x0e, 0xdf } ;
// 串口收到CAN232的CAN帧数据后,必须回复 15 02 13 dd f9 共5个字节的应答包
// 否则CAN232会重复发送5次造成冗余数据太多。

static INT8U recvReplyBuf[5] = { 0x15, 0x02, 0x13, 0xdd, 0xf9 } ; 

// 用于封装即将送往UART1的CAN帧,最长16字节=8+canDataLen
static INT8U sendOutBuf[16] ;


/**************************************************************************
名称:INT16U uart1Read(INT8U *buf)
功能:从接收缓冲区读出已经收到的串口数据
参数:INT8U *buf:用户自定义的接收缓冲。不能小于UART1_BUFFER_LEN的值
返回值:INT16U类型,用户接收的数据字节数
**************************************************************************/
INT16U uart1Read(INT8U *buf)
{
    INT16U i ;
	DEFINECRITICAL(cpu_sr);
    OS_ENTER_CRITICAL();

//    INTMSK |= OSISR_URXD1 ;
    for (i=0; i<index; i++)
        buf[i] = uart1Buffer[i] ;
    index = 0 ;
//	INTMSK &= ~OSISR_URXD1 ;
    OS_EXIT_CRITICAL();

    return i ;
}

/**************************************************************************
名称:void UART1_RxHandler() 
功能:调用UART1接收到数据后的处理程序,作为公共入口使用
参数:无
返回值:无
**************************************************************************/
void UART1_RxHandler() 
{
// 这个函数主要用来处理CAN232通过串口发送过来的CAN数据。
/*	
	INT8U buf[UART1_BUFFER_LEN] ;
	INT16U k ;
		k = uart1Read(buf) ;
		buf[k-1] = 0 ; //原先是buf[k] = 0; OSEKPrintString(buf) ;
		// 这个Bug让我和魏城炯调了近2个小时!记录以作纪念。
		OSEKPrintString(buf) ;
*/

	userK = uart1Read(userBuf) ;
		//如果包类型是0x13且实际长度为PDU长度+3,则是一个正常CAN帧
	//那么要先给CAN232一个ACK,然后处理该帧数据
	if ((0x13 == userBuf[0]) && (userK == userBuf[1]+3 ))
	{
		writeUart1(recvReplyBuf, 5) ;	// 给CAN232一个ACK

#if	(HANDLE_CAN_IN_INTERRUPT_EN > 0)
	// 如果允许在中断中处理CAN数据则直接调用CAN处理函数
		memcpy(&canID, &userBuf[3], 4) ; // 从buf里拷贝出4个字节的帧ID
		canID >>= 3 ;	// 右移3位才是29位的扩展帧ID
		ISRRecieveMsg(canID, &userBuf[7], userK-8) ;	// 8 是除了CAN数据之外的协议字节	
#else
	// 否则得通知其他任务处理来处理,通常是发送一个事件给某个任务
		SetEvent(Task1, EventCAN) ;	//这是其中的一个应用
#endif

	}
	//其他格式的串口数据不是CAN帧,不进行处理。
	else
	{
	}
}


/**************************************************************************
名称:void UART1_RECV(void)
功能:UART1中断接收处理函数。清中断并将FIFO中的数据保存到接收缓冲区,然后
      调用数据处理函数
参数:无
返回值:无
**************************************************************************/
/************** FIFO 中断接收方式需要处理的中断 ********************
FIFO Mode:       Each time receive data reaches the trigger
level of receive FIFO, the Rx interrupt will be
generated. When the FIFO is not empty and does not
receive data during 3 word time, the Rx
interrupt will be generated (receive time out).

Non-FIFO Mode:   Each time receive data becomes full, the
receive shift register, generates an interrupt.
********************************************************************/
void UART1_RECV(void)
{
	INT16U bytesGot = 0 ;
	INT16U  tempChar ;
	
    I_ISPC=BIT_URXD1;	// clear INT
    
    // If the UART uses the FIFO, users should check Rx FIFO Count bits in the UFSTAT register instead of this bit.
    // Rx FIFO Count UFSTAT0[3:0] Number of data in Rx FIFO
    while( (UFSTAT1 & 0x010f) >0 )	    //修改了一个重要的bug:FULL status, or FIFO not empty
    {
    	tempChar = URXH1;
    	if (index < UART1_BUFFER_LEN)
    	{
    		bytesGot++ ;	// 累计此次收到的个数
			uart1Buffer[index++] = tempChar;
		}
		else
		{
			//do nothing, skip the char
		}
    }
    //再次判断是否是rx timeout int,如果是,就可以叫上层处理了
    //文档没有给出rx timeout int的标志位,所以以是否有数据收到判断是否是timeout中断
    if (0 != bytesGot)		// 如果bytesGot非零,收到了,可以处理了。
    {
    	UART1_RxHandler() ;
    }
    // 如果bytesGot为零,timeout中断,不管了。

}


/*
********************************************************************************************************
* 函数: INT32S serial_flush_input(void) 和 INT32S serial_flush_output(void).
* 描述: 串口缓冲区清空函数.
********************************************************************************************************
*/
static INT32S serial_flush_input(void)
{
	volatile INT32U tmp;

	/* keep on reading as long as the receiver is not empty */
	while(UTRSTAT1&0x01)
	{
		tmp = (*(volatile INT8U *)(0x01c00000+URXH1));
	}

	return 0;
}

static INT32S serial_flush_output(void)
{
	/* wait until the transmitter is no longer busy */
	while(!(UTRSTAT1 & 0x02))
	{
	}

	return 0;
}

/**************************************************************************
名称:void writeUart1Char(const INT8U c)
功能:往UART1发送一个8位数据
参数:const INT8U c:要发送的8位数据
返回值:无
**************************************************************************/
void writeUart1Char(const INT8U c)
{	
	while(!(UTRSTAT1 & 0x2)); 	//Wait until THR is empty.
	UTXH1=c;

}

/**************************************************************************
名称:void writeUart1Str(INT8U *str)
功能:往UART1发送一串字符
参数:INT8U *str:字符串起始地址
返回值:无
**************************************************************************/
void writeUart1Str(INT8U *str)
{	
	DEFINECRITICAL(cpu_sr);
    OS_ENTER_CRITICAL();
	while (*str) {
		writeUart1Char(*str++);
	}
	OS_EXIT_CRITICAL();
}

/**************************************************************************
名称:void writeUart1(INT8U *buf, INT16U bytes)
功能:往UART1发送从缓冲区buf开始的bytes个8位数据
参数:INT8U *buf:缓冲区起始地址
      INT16U bytes:发送的字节个数
返回值:无
**************************************************************************/
void writeUart1(INT8U *buf, INT16U bytes)
{
	INT16U i ;
	DEFINECRITICAL(cpu_sr);
    
    OS_ENTER_CRITICAL();
    for (i = 0; i < bytes; i++)
    {
		writeUart1Char(*buf++) ;
	}
	OS_EXIT_CRITICAL();

}

/**************************************************************************
名称:void InitUart1(void)
功能:初始化UART1,使用中断接收方式,使用FIFO。
参数:无
返回值:无
**************************************************************************/
void InitUart1(void)
{
	DEFINECRITICAL(cpu_sr);
	
	OS_ENTER_CRITICAL();
	
	serial_flush_output();
	serial_flush_input();
	
	ULCON1 = 0x03;
	UCON1 = 0x285  ;		// by Bavon: tx=level,rx=pulse,enable rx timeout int
	/*	UFCON 数据位含义
	Rx FIFO Trigger level
	UFCON1[5:4] These two bits determine the trigger level of receive FIFO.
	00 = 4-byte 01 = 8-byte
	10 = 12-byte 11 = 16-byte
	3个word时间内没有收到会触发中断
	*/
	UFCON1 = 0x31; //16byte FIFO enabled
	UMCON1 = 0x0;
	UBRDIV1= ((int)(Fclk / 16.0 / UART1BAUDRATE + 0.5) - 1);	// 波特率
	
	OS_EXIT_CRITICAL();
}

/**************************************************************************
名称:void InitCan(INT8U baudrate)
功能:初始化CAN。其实是初始化UART1和CAN232
参数:INT8U baudrate:设置CAN232的CAN波特率
返回值:无
**************************************************************************/
void InitCan(INT8U baudrate)
{
	InitUart1() ;
	// 设置波特率
	if (CAN_250KBPS == baudrate)
	{
		writeUart1(canSetCan250Buf, 5) ;
	}
	else if (CAN_500KBPS == baudrate)
	{
		writeUart1(canSetCan500Buf, 5) ;
	}
	else
	{
		writeUart1(canSetCan250Buf, 5) ;
	}
	Delay(10) ;

	writeUart1(canResetCanBuf, 4) ;	//复位CAN
	Delay(100) ;
	writeUart1(canStartCanBuf, 4) ;	//启动CAN
	Delay(100) ;
	// 初始化结束,但是CAN的使用得等确认回复,这一步暂时不考虑,即不使用canReady标志
}


/**************************************************************************
名称:INT8U can232CheckSum(INT8U *bufstart, INT8U len)
功能:按照CAN232的串口协议计算从bufstart开始共len个字节数据的校验码
参数:INT8U *bufstart:计算的字节起始
      INT8U len:计算的个数
返回值:INT8U类型,所需的校验码
**************************************************************************/
INT8U can232CheckSum(INT8U *bufstart, INT8U len)
{
	INT8U i ;
	INT8U sum = 0 ;	//部分校验和
	
	for (i=0; i<len; i++)
	{
		sum += bufstart[i] ;
	}
	
	return (0x100 - sum) ;	// 总的校验和是0x100
}

/**************************************************************************
名称:INT16U vCAN_TxMsg(vCANFrameStruct *tx_msg, INT8U NotUsed)
功能:将CAN帧的数据打包成CAN232的串口数据发送出去。发送一次估计耗时23ms
参数:vCANFrameStruct *tx_msg:要发送的CAN数据帧的指针
      INT8U NotUsed:为了保持与12中的CAN驱动API接口统一而设,不使用。
返回值:发送的CAN数据长度
**************************************************************************/
INT16U vCAN_TxMsg(vCANFrameStruct *tx_msg, INT8U NotUsed)
{
	INT32U ID = 0 ;
	INT8U canDataLen = tx_msg->Data_Len ;
	INT8U i = 0;
	
	//不足:需要根据帧类型打包!现在默认以扩展数据帧打包的。memcpy效率可能比较低。fixme
	sendOutBuf[0] = 0x13 ;	// 固定形式,数据包类型
	sendOutBuf[1] = canDataLen + 5 ; //1个字节帧类型+4个字节ID+CAN_PDU长度
	// 接下来的13个字节是CAN2.0B规定的。ID统一用了4个字节,不区分11位和29位,但转换时要正确
	sendOutBuf[2] = 0x80 | tx_msg->Data_Len ; // 0x80=扩展数据帧
	ID = ((tx_msg->ID_High) << 16) | tx_msg->ID_Low ;
	ID <<= 3 ;	//对于扩展数据帧,ID放在高29位
	memcpy(&sendOutBuf[3], &ID, 4) ; // 小端方式存放ID
	memcpy(&sendOutBuf[7], tx_msg->Data, canDataLen) ;
	sendOutBuf[7+canDataLen] = can232CheckSum(sendOutBuf, canDataLen+7) ;
	// 封包到此结束
	
	writeUart1(sendOutBuf, 8+canDataLen) ;//包的长度是
	//通过使用CAN分析仪测量,Delay(2000)可以延时3ms。
	//而CAN232的回复需要5.2ms,故需延时3500次。
	Delay(3500) ;	//在CAN232的串口速率为9600bps的情况下,通过SSCOM32给CAN232发送CAN帧的间隔不低于50ms才不导致丢帧,所以发送完要延时比较长时间
	return canDataLen ;
}

/**************************************************************************
名称:void CAN_Handler(void)
功能:将缓冲区收到的数据进行解析并调用用户的CAN数据处理函数ISRRecieveMsg。
      如果想直接在中断内处理CAN,用此函数直接替代UART1_RxHandler()。
参数:无
返回值:无
**************************************************************************/
void CAN_Handler(void)
{
	INT8U buf[UART1_BUFFER_LEN+1] ;
	INT16U k ;
	INT32U canID ;

	//首先,从串口UART1接收数据
	k = uart1Read(buf) ;
	
	//然后,开始分析包
	//如果包类型是0x13且实际长度为PDU长度+3,则是一个正常CAN帧
	//那么要先给CAN232一个ACK,然后处理该帧数据
	if ((0x13 == buf[0]) && (k == buf[1]+3 ))
	{
		writeUart1(recvReplyBuf, 5) ;	// 给CAN232一个ACK
		memcpy(&canID, &buf[3], 4) ; // 从buf里拷贝出4个字节的帧ID
		canID >>= 3 ;	// 右移3位才是29位的扩展帧ID
		ISRRecieveMsg(canID, &buf[7], k-8) ;	
		//vCANFrameStruct testFrame = {CAN_EXTENDED_DATA_ID, 8, 0x0000, 0x0723, {1,2,3,4,5,6,7,8}} ;
		//vCAN_TxMsg(&testFrame, CAN_250KBPS) ;	//测试的时候使用本句
	}
}

/**************************************************************************
名称:void handleCAN(void)
功能:将缓冲区收到的数据进行解析并调用用户的CAN数据处理函数ISRRecieveMsg。
      由用户主动调用
参数:无
返回值:无
**************************************************************************/
void handleCAN(void)
{
	INT32U canID ;

		memcpy(&canID, &userBuf[3], 4) ; // 从buf里拷贝出4个字节的帧ID
		canID >>= 3 ;	// 右移3位才是29位的扩展帧ID
		ISRRecieveMsg(canID, &userBuf[7], userK-8) ;	
		//vCANFrameStruct testFrame = {CAN_EXTENDED_DATA_ID, 8, 0x0000, 0x0723, {1,2,3,4,5,6,7,8}} ;
		//vCAN_TxMsg(&testFrame, CAN_250KBPS) ;	//测试的时候使用本句

}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -