can通信
TO DO
1
CAN_FilterTypeDef 中的SlaveStartFilterBank 为从过滤器配置,用来选择从过滤器的寄存器号
当选择双CAN模式的时候,这个参数要跟CAN2的filternumber 一致。
//filternumber 是啥
2
can在发送的时候怎么决定是送到FIFO0还是FIFO1?
3
看这个
WHY
发送信息的时候 邮箱是怎么选的。为什么我按照例程重新写句柄和发送函数的时候即使发第一个消息也会跳转到邮箱1,而且发送失败。。
/* Select one empty transmit mailbox */
if ((hcan->Instance->TSR&CAN_TSR_TME0) == CAN_TSR_TME0)
{
transmitmailbox = 0;
}
else if ((hcan->Instance->TSR&CAN_TSR_TME1) == CAN_TSR_TME1)
{
transmitmailbox = 1;
}
CAN 的硬件结构
CAN总线系列学习笔记
左边是高速CAN总线的拓扑结构,右边是低速CAN总线的拓扑结构。所以高速和低速的设备是不能混用的【吧】
CAN 的信息格式
太多了,拿一个典型的给大家康康
下面的描述也是简化版本,具体还有很多细节没提到。
ID 确定顺序用的【canopen里用来划分帧的功能和子设备号】
控制段 确定数据的长度用的 【canopen里的SDO还另外用数据段里的内容又指定一次数据长度,搁这套娃呢】
数据段 8字节,只能少不能多
CRC 校验
ACK 回应
帧结束 7个隐性位
Can总线何时是空闲的
当总线连续表现为11个位的隐形电平(高电平),则总线为空闲状态。但是Can总线不是说5个相同位后就会有一个反转位码,那是发生在发送数据时。总线空闲时没有发送数据,发送数据时不会出现这么多隐性位。
CAN消息的有效长度。uint8_t len
每一帧CAN消息能够传递最多8个无符号整形数据,或者说能够传递8*8的bool类型的数据。这里的len最大值为8,如果该帧CAN消息中有些位没有数据,这里的len就会小于8。
NOTE
1开发板上的两个 CAN 都分别拥有自己的发送邮箱和接收 FIFO,但是他们共用 28 个 滤波器
2CAN有两个接收FIFO ,每个滤波器要设定对应的FIFO // ‘FIFO’ ==先入先出
CAN总线有接收邮箱和发送邮箱:
”发送邮箱“是用于CAN总线数据发送的,总共有3个,并且存在优先级关系。优先级越高表示其里面的数据会被优先发送。数据在发送前都会被送到优先级最高且空闲的发送邮箱,然后依次发送。最后说明一点:“发送邮箱有3个,且每个邮箱只能装一个报文”。
”接收邮箱“是用于CAN总线数据接收用的,在接收数据端会有一个过滤器处于”接收邮箱“的前面,过滤器使用于删选”标识符“的,只有标识符符合的报文才会被放入到”接收邮箱“当中。注意:”接收邮箱不同于发送邮箱,接收邮箱只有2(FIFO0、FIFO1)个,但是每一个有三层,每层都可以存放一个报文,即每一个接收邮箱可以接收三个报文。但读取时只能读到最先收到的报文,等这个读完之后,才能读下一个报文”。
3滤波器初始化里的CAN1_FilerConf.BankNumber=14;用来设置CAN2 起始存储区。
FIFO溢出时的策略
STM32有两种策略来处理当FIFO溢出时的报文:
一:当FIFO溢出时,首先抛弃FIFO内最老的报文,然后再存入新接收到的报文,即滚动接收模式。
二:当FIFO溢出时,抛弃新接收到的报文,即FIFO锁定模式。
如何采用以上何种策略,取决于具体应用需求。
如何设置?CAN主控制器寄存器(CAN_MCR)设置RFLM位为0,则为FIFO滚动接收模式,设为1,则为FIFO锁定模式。
基础设施
指示灯,按键中断以及NVIC中断允许,can的引脚和波特率配置,SYS调试引脚选第一项S… W… 记不清了
文件管理把生成.c .h文件勾选上
一要设置波特率
【重要性类比串口的波特率】
首先知道自己的APB1是45M
后面的参数就是45M/5(1+5+3)=1Mbps 想换频率只要改Prescaler就好 //但是这个频率好像太高了,ioc那边有警报。 现在用45M/9(1+5+4)=0.5Mbps
二是设置过滤器
自己新建个结构体 设定参数,调用句柄。可以像我一样新建个函数来初始化,也可以直接写在can的init函数里。
列表模式:只能接收指定ID的消息。
消息发送时的ID为0x0-0x7FF 【0000 0111 1111 1111】因此只有11个位有效 而过滤器的4个16位数据的存储方式是顶格存储的 所以过滤器设置1000 0000 1000 0000 从前面开始取值是 100 0000 0100 0000 0 对应的是 ID 404 【即过滤器里的后5位是废弃的】。截图里的设置是我的测试方式,大家也试一遍想必印象会更深刻。
当将消息的ID设置为0x0404时,过滤器列表应设为0x8080才能挑出ID为0404的消息。也就是说这个设置不能接收到ID为0201的消息,能接收到306的消息。另外我尝试了反向翻译一下过滤器设为0404的ID,ID是0020.开发板能接收。
【另外,我通信协议学的是canopen 这11个位里的前四位是报文的功能码,剩下的是从机编号(所以能有127个从机)】
屏蔽模式
举个简单的例子,我们设置过滤器组 0 工作在:1 个 32 位过滤器-标识符屏蔽模式,然后设置CAN_F0R1=0XFFFF0000,CAN_F0R2=0XFF00FF00。
其中存放到 CAN_F0R1 的值就是期望收到的ID,即我们希望收到的 ID(STID+EXTID+IDE+RTR)最好是:0XFFFF0000。而 0XFF00FF00就是设置我们需要必须关心的 ID,表示收到的 ID,其位[31:24]和位[15:8]这 16 个 位的必须和 CAN_F0R1 中对应的位一模一样,而另外的 16 个位则不关心,可以一样,也可以 不一样,都认为是正确的 ID,即收到的 ID 必须是0xFFxx00xx,才算是正确的(x 表示不关心)
三 通信中断使能
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
开启中断,开的是CAN_IT_RX_FIFO0_MSG_PENDING 接收中断
四 打开can通信
HAL_CAN_Start(&hcan1);
五 发送
数据帧
数据帧中有两种模式,一种是标准帧,一种是扩展帧。区别就只有ID的长短,但是同样是发送0x601,只有标准帧能被canopen识别,我不清楚这是因为canopen只能识别标准帧还是说标准帧和扩展帧就算低位数相同也会被区分开。
上面是can通信的格式,基本上就只用管ID和实际数据内容,其他部分都给函数整好了【而且学数据帧就够用了,别的很少用的样子】
玩过过滤就会搞ID 其余的设置对应的就是帧的结构
发送扩展帧只是ID变多了。而且发送的时候上面那个Stdid作废,ExtId可以从0写到1fffffff。
CAN发送流程
程序选择 1 个空置的邮箱(TME=1)->设置标识符(ID),数据长度和 发送数据->设置CAN_TIxR的TXRQ位为 1,请求发送->邮箱挂号(等待成为最高优先级)->预定发送(等待总线空闲)->发送->邮箱空置
六 接收
FIFO 空->收到有效报文->挂号_1(存入 FIFO 的一个邮箱,这个由硬件控制,我们不需要理会)->收到有效报文->挂号_2->收到有效报文->挂号_3->收到有效报文-> 溢出。
这个流程里面,我们没有考虑从 FIFO 读出报文的情况,实际情况是:我们必须在 FIFO 溢出之前,读出至少 1 个报文,否则下个报文到来,将导致 FIFO 溢出,从而出现报文丢失。每读出 1 个报文,相应的挂号就减 1,直到 FIFO 空.
FIFO 接收到的报文数,我们可以通过查询 CAN_RFxR 的 FMP 寄存器来得到,只要 FMP 不为 0,我们就可以从 FIFO读出收到的报文
参考文献
微雪课堂
过滤器
理论讲解
运行200秒左右 can就不发数据了 3个发送邮箱都满了?哪的问题,怎么办?
void can_filter_init(CAN_HandleTypeDef *phcan)
{
CAN_FilterTypeDef sFilterConfig;
/* Configure the CAN Filter */
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000;
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.SlaveStartFilterBank = 14;
if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
{
/* Filter configuration Error */
Error_Handler();
}
}
void CAN_Send(CAN_HandleTypeDef* phcan)
{
//HAL_CAN_ActivateNotification(&hcan, CAN_IT_TX_MAILBOX_EMPTY);//开启中断
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailbox=0;
uint8_t TxData[6] = {0x23, 0x81, 0x60, 0x00, 0x55, 0x55};
// HAL_Delay(1000);
TxHeader.StdId = 0x404; //标准帧ID �???�???11位,也就�???0x7FF
TxHeader.RTR = CAN_RTR_DATA; //帧类型是数据�??? 数据类型,由系统定义的,无需理会
TxHeader.IDE = CAN_ID_STD;//ID类型是标准帧 定义信息传输类型,系统内定,不予理会
TxHeader.DLC = 8;//指定即将传输的数据帧的长8字节长度
TxHeader.TransmitGlobalTime=DISABLE;
HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox);
TxData[0]++;
// HAL_UART_Transmit(&huart1,TxData,8,0xffff);//79 0B 00 08 C1 09 00 08
}