can收发程序分析
来源:互联网 发布:比特彗星端口开放 编辑:程序博客网 时间:2024/05/01 09:07
对于发送程序,首先需要按照can协议,根据报文的格式,将can控制器的发送缓冲区填充完毕。然后再启动发送命令,具体的发送程序如下所示:
/*
***********************************************************************************************************
**函数原型 : UINT32 CANSendData(eCANNUM CanNum,UINT32 Cmd,P_stcTxBUF Buf)
**参数说明 : CanNum -->> CAN控制器,值不能大于CAN_MAX_NUM 规定的值
Cmd --> 发送命令字
Buf --> 要发送的数据
**返回值 : 无
**说 明 : 本函数用于将数据发送到CAN总线
************************************************************************************************************/
UINT32 CANSendData(eCANNUM CanNum,UINT32 Cmd,P_stcTxBUF Buf)
{
UINT32 i,status=0;
if(0 != CANSR(CanNum).Bits.TBS1_BIT)//检测第一发送缓冲区是否可以写入数据,是否发送完毕,只有发送完毕
{
i=SEND_TX_BUF1;
}
else if(0 != CANSR(CanNum).Bits.TBS2_BIT)//检测第二发送缓冲区是否可以写入数据,是否发送完毕,只有发送完毕
{
i=SEND_TX_BUF2;
}
else if(0 != CANSR(CanNum).Bits.TBS3_BIT)//检测第三发送缓冲区是否可以写入数据,是否发送完毕,只有发送完毕
{
i=SEND_TX_BUF3;
}
else
{
i=0xFF;
}
status=WriteCanTxBuf(CanNum,i, USE_TPM_CAN[CanNum], Buf);//写入发送缓冲区,对于WriteCanTxBuf函数会在后面有详细分析的。
if(status == 0)//‘0’表示前面的写入缓冲区成功。
{
#if 1
if(CANMOD(CanNum).Bits.SM_BIT != 0)//这里检测canmod寄存器的SM位,来检测can控制器是否进入休眠了。
{
CanQuitSM(CanNum);//函数用于使can控制器退出休眠的,对于CanQuitSM函数的分析请看下面。
}
#endif
CanSendCmd(CanNum,Cmd,i);//启动发送命令。
}
return (status);
}
/*
***********************************************************************************************************
**函数原型 : UINT32 WriteCanTxBuf(eCANNUM CanNum,UINT32 TxBufNum,
UINT32 TPM,P_stcTxBUF Buf) ;
**参数说明 : CanNum -->> CAN控制器,值不能大于CAN_MAX_NUM 规定的值
TxBufNum --> 选择发送缓冲区
TPM --> 发送优先级模式
Buf --> 发送到总线的数据
**返回值 : =0;成功将数据写入发送缓冲区
!=0;写发送缓冲区操作失败
**说 明 : 本函数用于向CAN发送缓冲区TxBUF写入发送数据
***********************************************************************************************************
*/
UINT32 WriteCanTxBuf(eCANNUM CanNum,UINT32 TxBufNum,UINT32 TPM,P_stcTxBUF Buf)
{
UINT32 status=0;
switch(TxBufNum)
{
case SEND_TX_BUF1://这里选择了缓冲区1
if((0== CANSR(CanNum).Bits.TS1_BIT)&&(0 != CANSR(CanNum).Bits.TBS1_BIT))//在往里面写入之前,还是要判断can模块的相应缓冲区内是否有数据正在发送,是否可以往cantfi,cantid,cantda,cantdb寄存器内写入数据。
{
CANTFI1(CanNum)=Buf->TxFrameInfo;
CANTID1(CanNum)=Buf->TxCANID;
CANTDA1(CanNum)=Buf->CANTDA;
CANTDB1(CanNum)=Buf->CANTDB;
}
else
{
status = 0x01;
}
break;
case SEND_TX_BUF2:
if((0== CANSR(CanNum).Bits.TS2_BIT)&&(0 !=CANSR(CanNum).Bits.TBS2_BIT))
{
CANTFI2(CanNum)=Buf->TxFrameInfo;
CANTID2(CanNum)=Buf->TxCANID;
CANTDA3(CanNum)=Buf->CANTDA;//这里好像有点错误????
CANTDB2(CanNum)=Buf->CANTDB;
}
else
{
status = 0x01;
}
break;
case SEND_TX_BUF3:
if((0== CANSR(CanNum).Bits.TS3_BIT)&&(0 != CANSR(CanNum).Bits.TBS3_BIT))
{
CANTFI3(CanNum)=Buf->TxFrameInfo;
CANTID3(CanNum)=Buf->TxCANID;
CANTDA3(CanNum)=Buf->CANTDA;
CANTDB3(CanNum)=Buf->CANTDB;
}
else
{
status = 0x01;
}
break;
default:
status = 0x01;//会发现,如果没有缓冲区写入的话,我们在这里就设定status标志为非‘0’值,表示我们写入缓冲区失败。
break;
}
if(TPM == 1)
{
CANMOD(CanNum).Bits.TPM_BIT =1;//这里是一个优先级根据的问题,如果这里TPM不设置为1的时候,说明优先级是依靠各自的CAN ID决定的,如果为‘1’的话,说明有各个缓冲区tx的优先域决定的。
}
return(status);
}
/*
***********************************************************************************************************
**函数原型 : UINT32 CanQuitSM(eCANNUM CanNum)
**参数说明 : CanNum -->> CAN控制器,值不能大于CAN_MAX_NUM 规定的值
**返回值 : =0;成功退出睡眠状态;
!=0;不能退出睡眠状态;
**说 明 : 本函数用于使CAN控制器退出休眠模式
***********************************************************************************************************
*/
UINT32 CanQuitSM(eCANNUM CanNum)
{
uCANMod i;
i=CANMOD(CanNum);
CANMOD(CanNum).Bits.RM_BIT=1; //有些寄存器的写入前提是canmod寄存器的rm位先置为1.
CANMOD(CanNum).Bits.SM_BIT = 0; //这里置为‘0’后就把can控制器唤醒了.
CANMOD(CanNum).Bits.RM_BIT=i.Bits.RM_BIT; //恢复原来canmod的rm位的初始值.
return(CANMOD(CanNum).Bits.SM_BIT);//这里起到一个检测的功能,如果返回的值为‘0’,说明我们已经成功唤醒了can控制器了。
}
/*
***********************************************************************************************************
**函数原型 : void CanSendCmd(eCANNUM CanNum,UINT32 Cmd,UINT32 TxBuf)
**参数说明 : CanNum -->> CAN控制器,值不能大于CAN_MAX_NUM 规定的值
Cmd --> 发送命令字
TxBuf --> 选择发送缓冲区
**返回值 :
**说 明 : 本函数用于发送命令处理
************************************************************************************************************/
其中,第一发送缓冲区为主要的发送区。发送缓冲区内是否还有数据,是否可以写入新的数据,都是通过寄存器的标志位来查看。
void CanSendCmd(eCANNUM CanNum,UINT32 Cmd,UINT32 TxBuf)
{
uCANCMR Temp;
Temp.Word =0;
Temp.Bits.STB1_BIT = TxBuf&0x00000001;//这里是选择发送第一个缓冲区的内容.
Temp.Bits.STB2_BIT = (TxBuf&0x00000002)>>1;//这里是选择发送第二个缓冲区的内容.
Temp.Bits.STB3_BIT = (TxBuf&0x00000004)>>2;//这里是选择发送第3个缓冲区的内容.
switch(Cmd)
{
case 1://单次发送
Temp.Bits.AT_BIT =1;//表示取消等待发送的帧信息,只是发送此次。
Temp.Bits.TR_BIT =1;//置位后,表示cantfi,cantid,cantda,cantdb寄存器里面的内容等待发送.
break;
case 2://自发自收
case 3://单次自发自收
if(CANMOD(CanNum).Bits.STM_BIT == 0)
{
CANMOD(CanNum).Word =1;
CANMOD(CanNum).Bits.STM_BIT =1;//STM位被设置为1时---自测试模式——控制器认可没有应答的Tx 信息传输。此状态和CANCMR 的SRR 位一起使用,可以达到自发自收的效果。
CANMOD(CanNum).Bits.RM_BIT =0;//恢复正常工作.
}
Temp.Bits.SRR_BIT =1;//如上所说,这里把CANCMR寄存器的SRR位设置为‘1’.
if(Cmd == 3)
{
Temp.Bits.AT_BIT =1;//这里设置使我们是单次的。
}
break;
case 0:
default:
Temp.Bits.TR_BIT =1;//如果为其他值的话,说明只是正常的发送。
break;
}
CANCMR(CanNum) = Temp;//写入寄存器后就开始发送了。
}
由上述程序可知,发送方式有四种:单次发送,自发自收,单次自发自收,正常发送。其中的自发自收,可以很好的用于调试程序。当只有一个can节点时,不需要外接can总线(需要收发器PCA82C250),就可以进行自发自收,以测试发送程序以及硬件电路的正确性。而正常发送,它需要can收发器和120欧姆的电阻,共同组成的can网。
2.can总线接收程序
接收数据有两种方式,一种是查询,一种是中断。查询方式,比较简单,也就是不停的查询接收数据的标志位,当标志位为‘1’时,则表明接收缓冲区内有新的数据可读,然后读出接收到的can数据。不过,这种方法虽然实现起来比较简单,但是cpu要一直处于查询状态,这对cpu资源是很大的浪费。因此,我们采用第二种方式,中断。一定要先初始化好lpc2294(根据你的mcu型号来说)相应的中断设置寄存器以后,当接收缓冲区内有可读数据时,程序就会进入中断,只有我们在中断程序中,调用合适的读缓冲区函数,就达到了接收can数据的目的。
/*
***********************************************************************************************************
**函数原型 : void CANIntPrg(void)
**参数说明 : 无
**返回值 : 无
**说 明 : CAN控制器中断处理函数
************************************************************************************************************/
__irq void CANIntPrg(void)
{
UINT32 j;
uCANICR k;
if(CANLUTerr.Word != 0 ) //验收滤波器遇到表格错误
{
//add or modify code
j=CANLUTerrAd.Word;
}
for(j=0;j<CAN_MAX_NUM;j++)
{
k=CANICR(j);//CANICR是中断捕获寄存器。
if(k.Bits.RI_BIT != 0)//接收中断,表示接收到一条有用的帧信息。
{
//add code
WriteCANRcvCyBuf(j);//这个函数实现的是先把数据从相应的CANRFS寄存器中的数据写到一个数组(CANRcvBufApp.RcvBuf)当中。
ReadCANRcvCyBuf(j,&CRBuf);//再从数组中拿出数据,放在用户空间中。
}
if(k.Bits.TI1_BIT != 0)//第一发送缓冲区发送完成中断。
{
//add code
}
if(k.Bits.TI2_BIT != 0)//第二发送缓冲区发送完成中断。
{
//add code
}
if(k.Bits.TI3_BIT != 0)//第三发送缓冲区发送完成中断。
{
//add code
}
if(k.Bits.BEI_BIT != 0)//1:总线错误中断,CAN 控制器检测到总线错误时,该位置位。
{
//add code
CanBufOffLinePrg(j);
}
if(k.Bits.ALI_BIT != 0)//仲裁丢失中断。
{
//add code
}
if(k.Bits.EPI_BIT != 0)//错误认可中断。
{
//add code
}
if(k.Bits.WUI_BIT != 0)//唤醒中断
{
//add code
}
if(k.Bits.DOI_BIT != 0)//缓冲区溢出中断
{
//add code
ClrCanDataOver(j);//释放接收缓冲区
}
}
VICVectAddr = 0;
}
其中,ReadCanRxBuf就是读取缓冲区数据函数,而其他的一些则是保留扩展添加使用,当发生相应的错误的时候,会进入到相应的函数。另外,需要额外注意的是,在读完数据以后,要把相应的标志位清零,否则整个程序会一直处于中断接收状态。
void WriteCANRcvCyBuf(eCANNUM CanNum)
{
switch(CanNum)
{
case CAN1:
if((0 == CANRcvBufApp.FullFlag1))
{
CANRcvBufApp.RcvBuf[CAN1][CANRcvBufApp.WritePoint1] = RxBUF(CAN1);//这里就是上面所说的写入数组的过程。
if(++CANRcvBufApp.WritePoint1 >= USE_CAN_RCV_BUF_SIZE)
{
CANRcvBufApp.WritePoint1=0;
}
if(CANRcvBufApp.WritePoint1 == CANRcvBufApp.ReadPoint1)
{
CANRcvBufApp.FullFlag1 =1;//这里表明数组已经写满了,为什么可以表示写满,就是因为要符合这里这个IF条件只有writepoint自增到超过数组定义大小后,赋值为‘0’.
}
}
break;
case CAN2:
if((0 == CANRcvBufApp.FullFlag2))
{
CANRcvBufApp.RcvBuf[CAN2][CANRcvBufApp.WritePoint2] = RxBUF(CAN2);
if(++CANRcvBufApp.WritePoint2 >= USE_CAN_RCV_BUF_SIZE)
{
CANRcvBufApp.WritePoint2=0;
}
if(CANRcvBufApp.WritePoint2 == CANRcvBufApp.ReadPoint2)
{
CANRcvBufApp.FullFlag2 =1;
}
}
break;
default:
break;
}
RelCanRecBuf(CanNum);
}
UINT32 ReadCANRcvCyBuf(eCANNUM CanNum,stcRxBUF *Buf)
{
UINT32 status=0;
switch(CanNum)
{
case CAN1:
if((0 != CANRcvBufApp.FullFlag1) ||
(CANRcvBufApp.ReadPoint1 != CANRcvBufApp.WritePoint1))//第一个判断是表示对因的一维数组已经放满了。第二个可能是指针不一致,说明还有数据没有读完。
{
*Buf=CANRcvBufApp.RcvBuf[CAN1][CANRcvBufApp.ReadPoint1];//从数组中读出数据放在用户空间中。
if(++CANRcvBufApp.ReadPoint1 >= USE_CAN_RCV_BUF_SIZE)
{
CANRcvBufApp.ReadPoint1 =0;
}
CANRcvBufApp.FullFlag1=0;
}
else
{
status=1;
}
break;
case CAN2:
if((0 != CANRcvBufApp.FullFlag2) ||
(CANRcvBufApp.ReadPoint2 != CANRcvBufApp.WritePoint2))
{
*Buf=CANRcvBufApp.RcvBuf[CAN2][CANRcvBufApp.ReadPoint2];
if(++CANRcvBufApp.ReadPoint2 >= USE_CAN_RCV_BUF_SIZE)
{
CANRcvBufApp.ReadPoint2 =0;
}
CANRcvBufApp.FullFlag2=0;
}
case CAN3:
if((0 != CANRcvBufApp.FullFlag3) ||
(CANRcvBufApp.ReadPoint3 != CANRcvBufApp.WritePoint3))
{
*Buf=CANRcvBufApp.RcvBuf[CAN3][CANRcvBufApp.ReadPoint3];
if(++CANRcvBufApp.ReadPoint3 >= USE_CAN_RCV_BUF_SIZE)
{
CANRcvBufApp.ReadPoint3 =0;
}
CANRcvBufApp.FullFlag3=0;
}
else
{
status=1;
}
break;
case CAN4:
if((0 != CANRcvBufApp.FullFlag4) ||
(CANRcvBufApp.ReadPoint4 != CANRcvBufApp.WritePoint4))
{
*Buf=CANRcvBufApp.RcvBuf[CAN4][CANRcvBufApp.ReadPoint4];
if(++CANRcvBufApp.ReadPoint4 >= USE_CAN_RCV_BUF_SIZE)
{
CANRcvBufApp.ReadPoint4 =0;
}
CANRcvBufApp.FullFlag4=0;
}
else
{
status=1;
}
break;
break;
default:
status=1;
break;
}
return status;
}
到此为止,我们算是分析完了,对于滤波器的设置,我会在以后的文章中阐明的。
- can收发程序分析
- linux can收发程序
- CAN总线自收发程序
- CAN总线学习笔记二:CAN自收发程序解读
- can收发问题
- CAN收发器TJA1040简介
- linux下组播收发程序
- 自制收发料程序
- STM32+NRF24L01收发程序
- NRF24L01收发测试程序
- linux串口收发程序
- 串口收发程序
- CAN收发器MCP2551的问题
- STM32 CAN 配置、收发结构定义 留存...
- can-bus器件,控制器,收发器
- CAN通信波特率与每秒收发帧数
- CAN程序
- 微信通信协议,用自己的程序收发微信,微信网页web版分析
- 关于cs/bs/n层架构
- 蛇形填数——很白痴的题,我热身用
- 为什么中国企业普遍认为程序员是吃青春饭的
- C++读写复合文档的一点补充
- 总结下C++下的单元测试
- can收发程序分析
- linux内核学习笔记之——list_for_each_entry
- PHP and MySQL
- sd_fat16移植方法小结
- windows服务调试(未测试)
- asp.net多层架构
- 自制N6030便携收音机插头
- 关于贷款买官的可行性研究报告
- 在Java程序中截获控制台输出