使用了STM8S208S6这个8位的单片机来构建一个CAN通讯网络,使用相同的两块板子。 当然最首先的CAN的基本原理我们就不进行赘述了,当时自己在代码中也主要是对于CAN的配置的过程中遇到了问题,特别是对于屏蔽寄存器的配置,这一部分花费了我一段时间, 所以大家一定要看看CAN的寄存器的配置,多去琢磨一下。弄懂之后就会好一些了 首先看看CAN的初始化函数
[mw_shl_code=c,true]void CANBus_Init(void)
{
u8 byCAN_FilterID1;
u8 byCAN_FilterID2;
u8 byCAN_FilterID3;
u8 byCAN_FilterID4;
u8 byCAN_FilterIDMask1;
u8 byCAN_FilterIDMask2;
u8 byCAN_FilterIDMask3;
u8 byCAN_FilterIDMask4;
CAN_FilterNumber_TypeDef sctCAN_FilterNumber;
CAN_FilterMode_TypeDef sctCAN_FilterMode;
CAN_FilterScale_TypeDef sctCAN_FilterScale;
CAN_InitStatus_TypeDef CAN_InitState;
CAN_DeInit();
GPIO_Init(GPIOG,GPIO_PIN_0,GPIO_MODE_OUT_PP_HIGH_FAST);
GPIO_Init(GPIOG,GPIO_PIN_1,GPIO_MODE_IN_FL_NO_IT);
CAN_InitState = CAN_Init(CAN_MasterCtrl_AllDisabled,
CAN_Mode_Normal,/ CAN_SynJumpWidth_1TimeQuantum,
CAN_BitSeg1_8TimeQuantum,
CAN_BitSeg2_7TimeQuantum,
2);
if(CAN_InitState == CAN_InitStatus_Success)
printf("CAN Init done\r\n");
else
printf("CAN Init failed\r\n");
sctCAN_FilterNumber = CAN_FilterNumber_0;
sctCAN_FilterMode = CAN_FilterMode_IdMask;
sctCAN_FilterScale = CAN_FilterScale_32Bit;
byCAN_FilterID1=0x1E;
byCAN_FilterID2=0x20;
byCAN_FilterID3=0;
byCAN_FilterID4=0;
byCAN_FilterIDMask1=0xFF;
byCAN_FilterIDMask2=0xE0;
byCAN_FilterIDMask3=0;
byCAN_FilterIDMask4=0;
CAN_FilterInit(sctCAN_FilterNumber,
ENABLE,
sctCAN_FilterMode,
sctCAN_FilterScale,
byCAN_FilterID1,
byCAN_FilterID2,
byCAN_FilterID3,
byCAN_FilterID4,
byCAN_FilterIDMask1,
byCAN_FilterIDMask2,
byCAN_FilterIDMask3,
byCAN_FilterIDMask4);
CAN_ITConfig(CAN_IT_FMP, ENABLE);
}
[/mw_shl_code]
在这里我们设置CAN工作在正常模式,也就是可以进行正常的数据收发。当然在进行调试的时候可以使用Loopback模式,在这个模式下面发送数据的时候,接收邮箱是可以接收到自己发送出去的数据的。可以用这个模式来测试CAN总线是否有工作。 接下来就是配置CAN总线的波特率了,根据计算公式波特率= 总线频率/(分频系数*(Tq+BS1+BS2));其中Tq, BS1, BS2这些是用来监视CAN总线,设置采样点同步时间什么的,大致了解就行了,在我的工程当中,将会使用16M Hz内部高速时钟(HSI),分频系数设置为2,那么根据计算16M/2/(1+7+8) = 500K Hz,也就是说CAN总线的波特率为500KHz. 接下来就是对于本身的ID以及屏蔽位寄存器的设置了。我们在这里是使用标准帧通信,然后采用屏蔽过滤组0,选择屏蔽位模式,32位的过滤寄存器。下面着重讲一下如何设置这几个寄存器,我准备将CAN的ID设置为0XF1, 全匹配接收。首先我们看看屏蔽寄存器的构成,对于每个过滤组一共有8个寄存器 这里8 个过滤寄存器是分为高位和低位的,而具体要怎么使用这4 个高位和低位寄存器是跟在FMR1 寄存器中的设置有关的,因为FMHx 和FMLx 的设置直接影响到我们怎么使用这8 个寄存器。这里我们是设置均工作在屏蔽位模式也就是FMH0 FML0 均设置为0 。
在这种模式下,低位的4 个byte 的寄存器将会用来存储ID 标识符,后4 个byte 会用来存储屏蔽位的设置。按照上上个图里面的设置,我们要设置的0XF1 = 000 1111 0001 bit( 注意标准帧是11bit 的,所以我们再前面加上三个bit 的0)
转换成十六进制就是Identifier: 0x1E 0x20 0x00 0x00Mask: 0Xff 0xE0 0x00 0x00对应到代码里的设置就如下所示 byCAN_FilterID1=0x1E; byCAN_FilterID2=0x20; byCAN_FilterID3=0; byCAN_FilterID4=0; byCAN_FilterIDMask1=0xFF; byCAN_FilterIDMask2=0xE0; byCAN_FilterIDMask3=0; byCAN_FilterIDMask4=0; 当然在调试过程中,可以先不设屏蔽位,就是把所有的FilterIDMaskx都设置为0,这样CAN总线上的所有的帧都能收到最后再ENABLE接收中断CAN_ITConfig(CAN_IT_FMP, ENABLE); 这样CAN的初始化就完成了。 接下来是CAN的接收了,所有的接收应该都是在中断里面进行的,由于我们设置了屏蔽位,所以只有跟自己的ID相匹配的数据帧才能接收到。[mw_shl_code=c,true]INTERRUPT_HANDLER(CAN_RX_IRQHandler, 8)
{
CAN_Receive();
CAN_Frame_Handler(&CAN_FRAME);
printf("CAN Rx IRQ \r\n");
}
[/mw_shl_code] 在中断函数中,CAN_Receive() 是STM8的库函数,相当于是把所有接收到的数据从FIFO缓冲区转到RAM中,而函数CAN_Frame_Handler(&CAN_FRAME) 是我们自己定义的,用来解析所接收到的数据,具体代码如下[mw_shl_code=c,true]void CAN_Frame_Handler(CAN_FrameType *frame)
{
u8 i=0;
u8 buff[12];
CAN_Page_TypeDef can_page = CAN_GetSelectedPage();
frame->ID = CAN_GetReceivedId(); //get frame ID
frame->IDE = CAN_GetReceivedIDE(); //get IDE
frame->RTR = CAN_GetReceivedRTR(); //get RTR
frame->DLC = CAN_GetReceivedDLC(); //get DLC
frame->FMI = CAN_GetReceivedFMI(); //get FMI
buff[0]=(u8)((frame->ID) >> 24);
buff[1]=(u8)((frame->ID) >> 16);
buff[2]=(u8)((frame->ID) >> 8);
buff[3]=(u8)(frame->ID);
for(i=0;i<8;i++) //get 8 Byte data
{
frame->Data = CAN_GetReceivedData(i);
buff[4+i]=frame->Data;
}
CAN_Rx_Frame_Flag=1; //set flag to indicate received data frame
CAN_FIFORelease();
CAN_SelectPage(can_page);
}
[/mw_shl_code]
这个就是把一个帧里面的数据都解析出来了,在最后面设置一个flag,这样就可以根据收到的数据进行相应的动作了 接下来讲一下发送数据,具体代码如下[mw_shl_code=c,true]CAN_TxStatus_TypeDef CAN_Send_Frame(CAN_FrameType *frame)
{
CAN_TxStatus_TypeDef CAN_TxStatus = CAN_TxStatus_NoMailBox;
CAN_Page_TypeDef can_page = CAN_GetSelectedPage();
if ((CAN->TPR & CAN_TPR_TME0) == CAN_TPR_TME0) //choose a empty mailbox
{
CAN_TxStatus = CAN_TxStatus_MailBox0Ok;
}
else if ((CAN->TPR & CAN_TPR_TME1) == CAN_TPR_TME1)
{
CAN_TxStatus = CAN_TxStatus_MailBox1Ok;
}
else if ((CAN->TPR & CAN_TPR_TME2) == CAN_TPR_TME2)
{
CAN_TxStatus = CAN_TxStatus_MailBox2Ok;
}
else
{
CAN_TxStatus = CAN_TxStatus_NoMailBox;
}
if(CAN_TxStatus != CAN_TxStatus_NoMailBox) //transmit starts
{
CAN->PSR = (u8)CAN_TxStatus;
frame->ID &= (u16)CAN_STDID_SIZE;
CAN->Page.TxMailbox.MIDR1 = (u8)((frame->ID>>6) | (frame->RTR)) ;
CAN->Page.TxMailbox.MIDR2 = (u8)(frame->ID<<2);
CAN->Page.TxMailbox.MDLCR &= (u8)0xF0;
CAN->Page.TxMailbox.MDLCR |= (u8)(frame->DLC);
CAN->Page.TxMailbox.MDAR1 = frame->Data[0];
CAN->Page.TxMailbox.MDAR2 = frame->Data[1];
CAN->Page.TxMailbox.MDAR3 = frame->Data[2];
CAN->Page.TxMailbox.MDAR4 = frame->Data[3];
CAN->Page.TxMailbox.MDAR5 = frame->Data[4];
CAN->Page.TxMailbox.MDAR6 = frame->Data[5];
CAN->Page.TxMailbox.MDAR7 = frame->Data[6];
CAN->Page.TxMailbox.MDAR8 = frame->Data[7];
CAN->Page.TxMailbox.MCSR |= CAN_MCSR_TXRQ;
}
else
printf("no empty mailbox\r\n");
CAN_SelectPage(can_page);
return (CAN_TxStatus_TypeDef)CAN_TxStatus;
}
[/mw_shl_code]
这个函数比较容易看明白,说得直白点,就是按照帧格式去把一帧的数据填满就行了。当然在最开始我们要选择一个空的发送邮箱,然后把帧ID填入,数据填入,最后置上CAN_MCSR寄存器中的TXRQ位,数据帧就发送出去了。
|