📄 candriver.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 + -